Portieren von MicroPython

Ich dachte plötzlich daran, also versuchte ich es.

Vorbereitung

Bringen Sie, was Sie auf GitHub gegabelt haben, und machen Sie sogar einen Zweig.

git clone [email protected]:ysat0/micropython.git
cd micropython
git checkout -b rx

Verschaffen Sie sich einen groben Überblick. Suchen Sie zunächst den CPU-abhängigen Teil, da dieser vorerst funktionieren sollte.

$ ls
ACKNOWLEDGEMENTS    CONTRIBUTING.md  docs/      extmod/  mpy-cross/  tests/
CODECONVENTIONS.md  LICENSE          drivers/   lib/     ports/      tools/
CODEOFCONDUCT.md    README.md        examples/  logo/    py/

Wilde Intuition sagte mir, ich solle mir Häfen ansehen, also werde ich dort nachsehen.

$ ls ports
bare-arm/  esp8266/     nrf/       qemu-arm/  teensy/   zephyr/
cc3200/    javascript/  pic16bit/  samd/      unix/
esp32/     minimal/     powerpc/   stm32/     windows/          

Es scheint gut, minimal als Vorlage zu verwenden, also kopieren Sie es.

$ mkdir ports/rx
$ cp -R ports/minimal/* ports/rx

Also konnte ich es auf ein Schneidebrett legen.

Aussehen

Nachdem wir eine Vorlage vorbereitet haben, werden wir ernsthaft untersuchen, was mit den CPU-abhängigen Teilen passiert.

$ ls ports/rx 
Makefile   frozentest.mpy  main.c          mphalport.h     stm32f405.ld
README.md  frozentest.py   mpconfigport.h  qstrdefsport.h  uart_core.c

Einige können anhand des Dateinamens erraten werden, aber ich weiß nicht, wo ich das Problem beheben soll. Schauen Sie also hinein und überprüfen Sie den Zweck.

Dateiname Verwenden Fix
Makefil Datei erstellen Machen
README.md Erläuterung Macht nichts
frozentest.mpy Ergebnis der Bytekompilierung von ↓ TU es nicht
frozentest.py Testskript TU es nicht
main.c Initialisieren+main Machen
mpconfigport.h Verschiedene Einstellungen Machen
mphalport.h Definition hardwareabhängiger Funktionen Das musst du nicht
qstrdefsport.h Spezielle Definition des qstr-Typs TU es nicht
stm32f405.ld LD-Skript Machen
uart_core.c UART-Ein- / Ausgang Machen

Sie müssen es nicht so sehr reparieren.

Vorbereitung der Entwicklungsumgebung

Als ich darüber nachdachte, es zu beheben, bemerkte ich ein ernstes Problem, dass ich keine Entwicklungsumgebung vorbereitet habe (weil es normalerweise nicht für Baremetall ist), also werde ich es zuerst vorbereiten. Es verwendet eine normale RX-Elf-Toolchain (binutils + gcc + newlib). Erstellen Sie sie also selbst oder verwenden Sie KPITs.

Berücksichtigung des Ziels

Ich möchte es vorerst mit qemu ausführen, daher werde ich es mit der Hardware abgleichen, die qemu emuliert. Die Spezifikationen von Qemu sind

Dieses Mal werde ich jedoch nicht den externen Speicher verwenden, sondern nur den in die CPU integrierten Speicher.

ich werde es reparieren

Beheben Sie das Problem wie in der obigen Tabelle gezeigt. main.c Der ursprüngliche Code ist auch in main.c enthalten, z. B. beim Zurücksetzen. Da es jedoch einfacher ist, den vom Assembler geschriebenen Teil gehorsam in .s einzufügen, wird er in crt0.s und main.c. zerlegt.

crt0.s


        .global _start

        .section        ".vector","ax"
        .long   _start

        .text
_start: 
        mov     #_estack, r0
        mov     #_sdata,r1
        mov     #_edata,r3
        sub     r1,r3
        mov     #_etext,r2
        smovf
        mov     #_sbss,r1
        mov     #_ebss,r3
        sub     r1,r3
        mov     #0,r2
        sstr
        bsr     _rx62n_init
        sub     r1,r1
        sub     r2,r2
        bsr     _main
        bra     .
        .end

Es ist einfach der Ablauf der DATA / BSS-Initialisierung → Hardware-Initialisierung → main. Es gibt viel mehr Vektortabellen, aber da sie nicht aufgerufen werden sollten, wenn nichts vorhanden ist, wird nur der Rücksetzteil registriert.

main.c


(Abkürzung)
#define SCKCR 0x00080020
#define MSTPCRB 0x00080014
#define SCR0 0x00088242
#define SMR0 0x00088240
#define BRR0 0x00088241

void rx62n_init(void) {
    /* CPG Initialize */
    /* XTAL:12MHz, ICLK:96MHz (x8), PCLK:48MHz (x4) */
    *((volatile unsigned long *)SCKCR) = 0x00010100;
    /* SCI0 enable */
    *((volatile unsigned long *)MSTPCRB) &= ~0x80000000;
    /* SCI0 Initialize */
    *((volatile unsigned char *)SCR0) = 0x00;
    *((volatile unsigned char *)SMR0) = 0x01;
    *((volatile unsigned char *)BRR0) = 39;     /* 9600 bps */
    *((volatile unsigned char *)SCR0) = 0x30;
}

Entspricht der ursprünglichen main.c mit Ausnahme der Hardware-Initialisierung. Dies ist auch die minimale Initialisierung. Aus irgendeinem Grund hat uart_core keine Initialisierung, daher initialisiere ich sie hier. Das Schreiben in C ist übrigens der Grund, warum das Schreiben in einem Assembler problematisch ist.

mpconfigport Geeignet, da ich nicht sicher bin, welche Einstellung wo wirkt. Dies ist auch die minimal notwendige Änderung.

mpconfigport.h


(Abkürzung)
#define MICROPY_HW_BOARD_NAME "minimal"
#define MICROPY_HW_MCU_NAME "Renesas RX"

#ifdef __linux__
#define MICROPY_MIN_USE_STDOUT (1)
#endif

#ifdef __RX__
#define MICROPY_MIN_USE_RX_CPU (1)
#endif

Es gibt ~ THUMB auf der Oberseite, aber es scheint ungültig zu sein, also lasse ich es so wie es ist.

uart_core.c Verantwortlich für die Eingabe / Ausgabe auf niedriger Ebene für den Dialog. Wie ich oben geschrieben habe, gibt es aus irgendeinem Grund keinen Initialisierungsteil, und es gibt nur einen Ort, an dem tatsächlich eingegeben und ausgegeben werden kann.

uart_core.c


#include <unistd.h>
#include "py/mpconfig.h"

/*
 * Core UART functions to implement for a port
 */

#if MICROPY_MIN_USE_RX_CPU
#define TDR0 0x00088243
#define SSR0 0x00088244
#define RDR0 0x00088245
#define SSR_TDRE (1 << 7)
#define SSR_RDRF (1 << 6)
#endif

// Receive single character
int mp_hal_stdin_rx_chr(void) {
    unsigned char c = 0;
#if MICROPY_MIN_USE_STDOUT
    int r = read(0, &c, 1);
    (void)r;
#elif MICROPY_MIN_USE_RX_CPU
    // wait for RDRF
    while ((*(volatile unsigned char *)SSR0 & SSR_RDRF) == 0) {
    }
    c = *(volatile unsigned char *)RDR0;
#endif
    return c;
}

// Send string of given length
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
#if MICROPY_MIN_USE_STDOUT
    int r = write(1, str, len);
    (void)r;
#elif MICROPY_MIN_USE_RX_CPU
    while (len--) {
        // wait for TDRE
        while ((*(volatile unsigned char *)SSR0 & SSR_TDRE) == 0) {
        }
        *(volatile unsigned char *)TDR0 = *str++;
    }
#endif
}

Dies ist hardwareabhängig, daher schreibe ich es neu. Die Verwendung von UART ist jedoch nahezu identisch, sodass sich nicht viel ändert.

ldscript Wie erwartet ist stm32 ~ verwirrend, daher werde ich den Namen ändern. Dies ist auch hardwareabhängig, daher werde ich es beheben. Das heißt, es ist kein allzu großer Aufwand, da nur Text / Daten / BSS dem internen Speicher ordnungsgemäß zugewiesen werden. Wenn Sie es mit R5F562N7 ausführen möchten, das weltweit verfügbar ist, sollten Sie FLASH und RAM entsprechend reparieren.

rx62n.ld


/*
    GNU linker script for RX62N
*/

/* Specify the memory areas */
MEMORY
{
    FLASH (rx)      : ORIGIN = 0xfff80000, LENGTH = 0x00080000 - 0x4 /* 512 KiB 
*/
    VECTOR (r)      : ORIGIN = 0xfffffffc, LENGTH = 0x0000004
    RAM (xrw)       : ORIGIN = 0x00000000, LENGTH = 0x00018000 /* 96 KiB */
}

/* top end of the stack */
_estack = ORIGIN(RAM) + LENGTH(RAM);

/* define output sections */
SECTIONS
{
    /* The program code and other data goes into FLASH */
    .text :
    {
        . = ALIGN(4);
        *(.text.startup)
        *(P)
        *(C)
        *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */

        . = ALIGN(4);
        _etext = .;        /* define a global symbol at end of code */
        _sidata = _etext;  /* This is used by the startup in order to initialize the .data secion */
    } >FLASH

    .vector :
    {
        *(.vector)
    } > VECTOR

    /* This is the initialized data section
    The program executes knowing that the data is in the RAM
    but the loader puts the initial values in the FLASH (inidata).
    It is one task of the startup to copy the initial values from FLASH to RAM. 
*/
    .data : AT ( _sidata )
    {
        . = ALIGN(4);
        _sdata = .;        /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */
        *(D*)          /* .data* sections */

        . = ALIGN(4);
        _edata = .;        /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */
    } >RAM

    /* Uninitialized data section */
    .bss :
    {
        . = ALIGN(4);
        _sbss = .;         /* define a global symbol at bss start; used by startup code */
        *(B*)
        *(COMMON)

        . = ALIGN(4);
        _ebss = .;         /* define a global symbol at bss end; used by startup code */
    } >RAM
}

Der eindeutige Abschnittsname ist übrigens die RX-Elf-Spezifikation. Die Tatsache, dass die Antwort auf halbem Weg ist, ist jedoch problematisch.

Makefile Ändern Sie die Build-Optionen oder fügen Sie crt0 hinzu. Ich dachte, ich würde es tun, aber ich wurde nach setjmp / longjmp gefragt, also habe ich libc einfach verlinkt. Wenn es um setjmp ging, hätte ich es selbst vorbereiten können, aber es war mühsam, die Definition von jmpbuf nachzuschlagen, also geschah dies.

include ../../py/mkenv.mk

CROSS = 0
TARGET = mpython
# qstr definitions (must come before including py.mk)
QSTR_DEFS = qstrdefsport.h

# include py core make definitions
include $(TOP)/py/py.mk

ifeq ($(CROSS), 1)
CROSS_COMPILE ?= rx-elf-
endif

INC += -I.
INC += -I$(TOP)
INC += -I$(BUILD)

ifeq ($(CROSS), 1)
CFLAGS_RX = -fsingle-precision-constant -Wdouble-promotion
CFLAGS = $(INC) -Wall -Werror -std=c99 -nostdlib $(CFLAGS_RX) $(COPT)
LIBC =  $(shell $(CROSS_COMPILE)gcc -print-file-name=libc.a)
LDFLAGS = -T rx62n.ld [email protected] -L $(dir $(LIBC))
else
CFLAGS = -m32 $(INC) -Wall -Werror -std=c99 $(COPT)
LDFLAGS = -m32 -Wl,[email protected],--cref -Wl,--gc-sections
endif

CSUPEROPT = -Os # save some code space

# Tune for Debugging or Optimization
ifeq ($(DEBUG), 1)
CFLAGS += -O0 -ggdb
else
CFLAGS += -Os -DNDEBUG
endif

LIBS = -l c

SRC_C = \
	main.c \
	uart_core.c \
	lib/utils/printf.c \
	lib/utils/stdout_helpers.c \
	lib/utils/pyexec.c \
	lib/libc/string0.c \
	lib/mp-readline/readline.c \
	$(BUILD)/_frozen_mpy.c

SRC_S = crt0.s

OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) $(addprefix $(BUILD)/, $(SRC_S:.s=.o))

ifeq ($(CROSS), 1)
all: $(BUILD)/$(TARGET).bin
else
all: $(BUILD)/$(TARGET).elf
endif

$(BUILD)/_frozen_mpy.c: frozentest.mpy $(BUILD)/genhdr/qstrdefs.generated.h
	$(ECHO) "MISC freezing bytecode"
	$(Q)$(TOP)/tools/mpy-tool.py -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h -mlongint-impl=none $< > $@

$(BUILD)/$(TARGET).elf: $(OBJ)
	$(ECHO) "LINK $@"
	$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
	$(Q)$(SIZE) $@

$(BUILD)/$(TARGET).bin: $(BUILD)/$(TARGET).elf
	$(Q)$(OBJCOPY) -O binary -j .vector -j .text -j .data $^ $@

$(BUILD)/$(TARGET).srec: $(BUILD)/$(TARGET).elf
	$(Q)$(OBJCOPY) -O srec -j .vector -j .text -j .data $^ $@

include $(TOP)/py/mkrules.mk

Danach dachte ich, dass die Bereitstellung übrig bleiben würde, aber ich brauchte sie nicht separat für qemu und löschte sie, weil es kein geeignetes Schreibprogramm von make gab. Ich habe mich daran erinnert, aber es gibt dies. Wenn Sie es also wirklich verwenden möchten, verwenden Sie es bitte (machen Sie Werbung). Behalte es).

Bauen

Gehorsam make cross=1 Du kannst es schaffen mit.

Versuche dich zu bewegen

Jetzt, wo es fertig ist, lass es uns bewegen.

qemu-system-rx -bios build/mpyton.bin


 Beginnen Sie mit und schalten Sie den Bildschirm auf die serielle Schnittstelle![Start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316355/1e891cb0-37a4-f9e6 -639a-5dc4a33328b5.png)
 Etwas funktioniert, und wenn Sie es richtig eingeben, wird es sein![Print.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316355/076e4079-0cd8-8495 -7206-9a2098799ad2.png)
 Ich werde eine richtige Antwort bekommen, also wird es vielleicht so funktionieren, wie es sollte.

# Ende
 Wenn Sie es also vorerst verschieben, ist es ziemlich einfach.
 Vielleicht Zeit, den Code zu reparieren <Zeit, um die Entwicklungsumgebung vorzubereiten.
 Ich denke, ich muss mehr Treiber vorbereiten, damit ich es richtig verwenden kann, aber ich war zufrieden mit dem Ort, an dem es mit qemu funktioniert hat, so dass der zukünftige Zeitplan unentschlossen ist.


Recommended Posts

Portieren von MicroPython
ARM-Architektur »Portierung