移植micropython到jz2440开发板(samsung s3c2440 soc)记录

移植micropython到jz2440开发板(samsung s3c2440 soc)记录

工程全部代码已经上传到了github:https://github.com/Asymptote1994/micropython

1. 概述

1.1 缘由

不知大家有没有了解过K210(一款64位双核带硬件FPU、卷积加速器、FFT、sha256的 RISC-V CPU) 这款AI芯片,如果对嵌入式AI感兴趣的话,非常建议入手。

MaixPy —— sipeed公司将 Micropython 移植到 K210的一个项目,本身python便非常适合AI领域,而将micropython与K210进行结合的MaixPy 项目可以让我们非常方便的在嵌入式设备上面运行AI应用,比如人脸识别、物体检测、目标分类等。

此外除了AI方面,还可以通过micropython控制嵌入式设备上的各种硬件,相比于c语言,micropython足够简洁,非常适合新手入门以及想熟悉python语言的嵌入式工程师。

基于以上,我萌生了将micropython移植到s3c2440的想法,虽然s3c2440没有AI相关的硬件支持,但是可以通过micropython控制各种外围硬件,以此来体验一把micropython语言的各种特性岂不很香吗?

1.2 介绍

本篇文章讲述的是移植micropython到裸机,此外还包括运行在类unix以及windows操作系统之上的版本,这里没有涉及。

下面是对micropython的简单介绍:

MicroPython 是Python 3编程语言的一种精简而高效的实现,它包含Python标准库的一个小子集,并且经过优化,可以在微控制器和受限环境中运行。MicroPython包含了许多高级特性,比如交互式提示符、任意精确整数、闭包、列表理解、生成器、异常处理等等。但是它足够紧凑,可以在256k的代码空间和16k的RAM中运行。MicroPython的目标是尽可能与普通Python兼容,允许轻松地将代码从桌面转移到微控制器或嵌入式系统。

2. 下载源码

micropython的源码可从其官方github下载到:https://github.com/micropython/micropython

执行如下命令即可下载到当前目录下:

git clone git@github.com:micropython/micropython.git

下载到的源码的目录名为micropython

3. 源码目录结构

首先我们看到micropython源码目录的结构为:
在这里插入图片描述关于每个目录的功能,可从顶层目录的README.md得到答案:

Major components in this repository:
- py/ -- the core Python implementation, including compiler, runtime, and
  core library.
- mpy-cross/ -- the MicroPython cross-compiler which is used to turn scripts
  into precompiled bytecode.
- ports/unix/ -- a version of MicroPython that runs on Unix.
- ports/stm32/ -- a version of MicroPython that runs on the PyBoard and similar
  STM32 boards (using ST's Cube HAL drivers).
- ports/minimal/ -- a minimal MicroPython port. Start with this if you want
  to port MicroPython to another microcontroller.
- tests/ -- test framework and test scripts.
- docs/ -- user documentation in Sphinx reStructuredText format. Rendered
  HTML documentation is available at http://docs.micropython.org.

Additional components:
- ports/bare-arm/ -- a bare minimum version of MicroPython for ARM MCUs. Used
  mostly to control code size.
- ports/teensy/ -- a version of MicroPython that runs on the Teensy 3.1
  (preliminary but functional).
- ports/pic16bit/ -- a version of MicroPython for 16-bit PIC microcontrollers.
- ports/cc3200/ -- a version of MicroPython that runs on the CC3200 from TI.
- ports/esp8266/ -- a version of MicroPython that runs on Espressif's ESP8266 SoC.
- ports/esp32/ -- a version of MicroPython that runs on Espressif's ESP32 SoC.
- ports/nrf/ -- a version of MicroPython that runs on Nordic's nRF51 and nRF52 MCUs.
- extmod/ -- additional (non-core) modules implemented in C.
- tools/ -- various tools, including the pyboard.py module.
- examples/ -- a few example Python scripts.

其中ports目录便是具体硬件代码的集合地了,从上文可以看到,stm32以及最近很火的物联网wifi芯片esp8266、esp32都已经被官方主线代码所支持。

而我们今天移植的基础便是ports/minimal目录,从名称也可以看出这是一个最小最基础的版本,所有的芯片移植工作都可以从这个目录做起。

4. 移植

首先,我们将minimal目录复制为s3c2440目录,然后看下该目录下的各个文件,功能如下:

- frozentest.py -- 测试用的micropython源代码文件
- frozentest.mpy -- 利用micropython自带的编译工具mpy-cross针对frozentest.py编译出的字节码文件
- main.c -- c代码入口
- uart_core.c -- 串口驱动文件
- mpconfigport.h -- 主要的配置文件
- mphalport.h -- hal层的配置文件
- qstrdefsport.h -- 用于外部符号的定义
- stm32f405.ld -- 默认的链接脚本
- Makefile -- 不用多说了吧
- README.md -- 用于说明的md文件

其中,需要修改的文件包括:Makefile、stm32f405.ld、uart_core.c、main.c、mpconfigport.h

另外,还需要增加如下文件:

- start.S -- 汇编初始化代码,也即最终编译出的bin文件的入口,用于初始化栈、sram、nand flash以及复制代码到sram等,并最终跳转到main.c文件中的main函数
- nand.c/nand.h -- nand flash驱动文件
- uart.h -- 串口驱动头文件
- libgcc.a -- 从编译工具链获得,用于提供除法相关的汇编符号定义
- mylibc.a -- 增加对printk函数以及string库的支持,这里没有使用工程自带的printf函数,原因在于自带的printf函数打印整形数据会出现错误

4.1 修改Makefile

主要改动如下:

  • CROSS_COMPILE ?= arm-linux-gnueabi-

    关于交叉编译工具链,需要用比较新的,旧的工具链会有一些指令不支持

  • CFLAGS_ARM9 = -march=armv4t -marm -msoft-float -fsingle-precision-constant -Wdouble-promotion -Wfloat-conversion

    指定具体芯片的编译选项,以上是关于s3c2440(arm920t)的选项,具体可以从u-boot编译时的最终链接命令得到

  • 在SRC_C以及SRC_S下增加新文件

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

    这里相较于原文件修改了顺序,目的是让start.o、main.o等硬件相关的代码链接在bin文件的最前面

最终文件全部内容改为如下:

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

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

# MicroPython feature configurations
MICROPY_ROM_TEXT_COMPRESSION ?= 1

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

CROSS_COMPILE ?= arm-linux-gnueabi-

OBJDUMP = $(CROSS_COMPILE)objdump

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

# CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mcpu=cortex-m4 -msoft-float -fsingle-precision-constant -Wdouble-promotion -Wfloat-conversion
CFLAGS_ARM9 = -march=armv4t -marm -msoft-float -fsingle-precision-constant -Wdouble-promotion -Wfloat-conversion
# CFLAGS_CUSTOM = -fno-builtin-printf
CFLAGS = $(INC) -Wall -nostdlib  $(CFLAGS_ARM9) $(CFLAGS_CUSTOM) 

LDFLAGS =  -T s3c2440.ld -Map=$@.map --cref --gc-sections

CSUPEROPT = -Os # save some code space

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

LIBS = mylibc.a libgcc.a

SRC_C = \
	main.c \
	nand.c \
	uart_core.c \
	lib/utils/pyexec.c \
	lib/mp-readline/readline.c \
	$(BUILD)/_frozen_mpy.c \

# SRC_C += lib/libc/string0.c

SRC_S = \
	start.S \

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

all: $(BUILD)/firmware.bin

$(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)/firmware.elf: $(OBJ)
	$(ECHO) "LINK $@"
	$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
	$(Q)$(SIZE) $@

$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
	$(Q)$(OBJCOPY) -S $< -O binary $@
	$(Q)$(OBJDUMP) -D -m arm $< > $(BUILD)/firmware.dis

include $(TOP)/py/mkrules.mk

4.2 修改stm32f405.ld

修改名称为s3c2440.ld,这里没什么好说的,唯一需要注意的是0x30000000这个地址,原因是在start.s中会将最终编译出的firmware.bin文件又复制到sdram的0x30000000地址处,然后继续在sdram中执行。

文件全部内容改为如下:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)

SECTIONS {
    . = 0x30000000;

    . = ALIGN(4);

    .text :
    { 
        *(.text) 
        *(.text*)
    }

	. = ALIGN(4);

    .rodata :
    { 
        *(.rodata) 
        *(.rodata*)
    }
	
	. = ALIGN(4);

    .data :
    { 
        *(.data) 
        *(.data*)
    }
     
	. = ALIGN(4);

	__bss_start = .;
    .bss :
    { 
        *(.bss)
        *(.bss*)
        *(COMMON)
    }
    __bss_end = .;
}

4.3 修改uart_core.c

主要是串口0的初始化、get、put函数。

文件全部内容改为如下:

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

#include "s3c2440_regs.h"
#include "uart.h"

#define PCLK            50000000    // clock_init函数设置PCLK为50MHz
#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define UART_BAUD_RATE  115200      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

void s3c2440_uart0_init(void)
{
    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    GPHUP   = 0x0c;     // GPH2,GPH3内部上拉

    ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    UFCON0  = 0x00;     // 不使用FIFO
    UMCON0  = 0x00;     // 不使用流控
    UBRDIV0 = UART_BRD; // 波特率为115200
}

/* Used by printf in mylibc.a */
void putc(unsigned char c)
{
	while (!(UTRSTAT0 & (1 << 2)));
	UTXH0 = c;
}

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

// Receive single character
int mp_hal_stdin_rx_chr(void)
{
	while (!(UTRSTAT0 & (1)));

    return URXH0;
}

// Send string of given length
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len)
{
    while (len--) {
		while (!(UTRSTAT0 & (1 << 2)));
    	UTXH0 = *str++;
    }
}

4.4 修改main.c

// #include <stdint.h>
// #include <stdio.h>
// #include <string.h>

#include "py/compile.h"
#include "py/runtime.h"
#include "py/repl.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "lib/utils/pyexec.h"

#include "uart.h"

/* fill in __assert_fail for libc */
void __assert_fail(const char *__assertion, const char *__file,
            unsigned int __line, const char *__function)
{
    printk("Assert at %s:%d:%s() \"%s\" failed\n", __file, __line, __function, __assertion);
    for (;;) {;
    }
}

/* Used by libgcc.a */
int raise (int signum)
{
	printk("raise: Signal # %d caught\n", signum);
	return 0;
}

int main(int argc, char **argv)
{
    void *heap_start = (void *)0x31000000;
    unsigned int heap_len = 0x100000;

    s3c2440_uart0_init();
    
    #if MICROPY_ENABLE_GC
    gc_init(heap_start, heap_start + heap_len);
    #endif
 
    mp_init();

    pyexec_friendly_repl();

    mp_deinit();

    while (1);
    return 0;
}

#if 1

#if MICROPY_ENABLE_GC
void gc_collect(void) {
    // WARNING: This gc_collect implementation doesn't try to get root
    // pointers from CPU registers, and thus may function incorrectly.
    volatile void *tmp = (void *)0x32000000;
    volatile void *sp = &tmp;
 
    gc_collect_start();
    gc_collect_root((void**)sp, ((mp_uint_t)0x32000000 - (mp_uint_t)0x31000000) / sizeof(mp_uint_t));
    gc_collect_end();
    // gc_dump_info();
}
#endif

mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
	printk("mp_lexer_new_from_file\n");
    mp_raise_OSError(MP_ENOENT);
}

mp_import_stat_t mp_import_stat(const char *path) {
	printk("mp_import_stat\n");
    return MP_IMPORT_STAT_NO_EXIST;
}

mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
	printk("mp_builtin_open\n");
    return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);

void nlr_jump_fail(void *val) {
    while (1) {
        ;
    }
}

void NORETURN __fatal_error(const char *msg) {
    while (1) {
        ;
    }
}

#ifndef NDEBUG
void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) {
    printk("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
    __fatal_error("Assertion failed");
}
#endif

#include <stdarg.h>
int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args);
int DEBUG_printf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int ret = mp_vprintf(MICROPY_DEBUG_PRINTER, fmt, ap);
    va_end(ap);
    return ret;
}

// Send "cooked" string of given length, where every occurrence of
// LF character is replaced with CR LF.
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
    while (len--) {
        printk("%c", *str++);
    }
}

// Send zero-terminated string
void mp_hal_stdout_tx_str(const char *str) {
    mp_hal_stdout_tx_strn(str, strlen(str));
}
#endif

4.5 修改mpconfigport.h

#include <stdint.h>

// options to control how MicroPython is built

// You can disable the built-in MicroPython compiler by setting the following
// config option to 0.  If you do this then you won't get a REPL prompt, but you
// will still be able to execute pre-compiled scripts, compiled with mpy-cross.
#define MICROPY_ENABLE_COMPILER     (1)

/* Debug */
#define DEBUG_PRINT                 (0)
#define MICROPY_DEBUG_VERBOSE       (0)
#define MICROPY_REPL_INFO           (0)

#define MICROPY_OBJ_REPR            (MICROPY_OBJ_REPR_A)
#define MICROPY_MODULE_BUILTIN_INIT         (1)

#define MICROPY_QSTR_BYTES_IN_HASH  (1)
#define MICROPY_QSTR_EXTRA_POOL     mp_qstr_frozen_const_pool
#define MICROPY_ALLOC_PATH_MAX      (256)
#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
#define MICROPY_COMP_CONST          (1)
#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1)
#define MICROPY_ENABLE_GC           (1)
#define MICROPY_GC_ALLOC_THRESHOLD  (1)
#define MICROPY_HELPER_REPL         (1)
#define MICROPY_ERROR_REPORTING     (MICROPY_ERROR_REPORTING_TERSE)
#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (1)
#define MICROPY_PY_ASYNC_AWAIT      (1)
#define MICROPY_PY_ASSIGN_EXPR      (1)
#define MICROPY_PY_BUILTINS_BYTEARRAY (1)
#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (1)
#define MICROPY_PY_BUILTINS_ENUMERATE (1)
#define MICROPY_PY_BUILTINS_FILTER  (1)
#define MICROPY_PY_BUILTINS_REVERSED (1)
#define MICROPY_PY_BUILTINS_SET     (1)
#define MICROPY_PY_BUILTINS_SLICE   (1)
#define MICROPY_PY_BUILTINS_PROPERTY (1)
#define MICROPY_PY_BUILTINS_MIN_MAX (1)
#define MICROPY_PY_BUILTINS_STR_COUNT (1)
#define MICROPY_PY_BUILTINS_STR_OP_MODULO (1)
#define MICROPY_PY___FILE__         (1)
#define MICROPY_PY_GC               (1)
#define MICROPY_PY_ARRAY            (1)
#define MICROPY_PY_ATTRTUPLE        (1)
#define MICROPY_PY_COLLECTIONS      (1)
#define MICROPY_PY_IO               (1)
#define MICROPY_PY_STRUCT           (1)
#define MICROPY_PY_SYS              (1)
#define MICROPY_MODULE_FROZEN_MPY   (1)
#define MICROPY_CPYTHON_COMPAT      (1)
#define MICROPY_MODULE_GETATTR      (1)
#define BYTES_PER_WORD              (sizeof(mp_uint_t))
#define MICROPY_ENABLE_SCHEDULER            (1)
#define MICROPY_SCHEDULER_DEPTH             (8)

// type definitions for the specific machine
#define UINT_FMT "%u"
#define INT_FMT "%d"
typedef int mp_int_t; // must be pointer size
typedef unsigned int mp_uint_t; // must be pointer size
typedef long mp_off_t;

// use vfs's functions for import stat and builtin open
#define MICROPY_VFS                         (1)
#define mp_import_stat mp_vfs_import_stat
#define mp_builtin_open mp_vfs_open
#define mp_builtin_open_obj mp_vfs_open_obj
#define MICROPY_PY_ATTRTUPLE                (1)

#define MICROPY_PY_FUNCTION_ATTRS           (1)
#define MICROPY_PY_DESCRIPTORS              (1)

// extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
    { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, \

// We need to provide a declaration/definition of alloca()
#include <alloca.h>

#define MICROPY_HW_BOARD_NAME "JZ2440"
#define MICROPY_HW_MCU_NAME "Samsung-s3c2440"

#ifdef __linux__
#define MICROPY_MIN_USE_STDOUT (1)
#endif

#ifdef __thumb__
#define MICROPY_MIN_USE_CORTEX_CPU (1)
#define MICROPY_MIN_USE_STM32_MCU (1)
#endif

#define MP_STATE_PORT MP_STATE_VM

#define MICROPY_PORT_ROOT_POINTERS \
    const char *readline_hist[8];

5. 编译&烧录&运行

5.1 编译

  • 进入micropython/ports/s3c2440目录,然后直接执行make即可,成功的话会在micropython/ports/s3c2440/build目录下生成firmware.bin

  • 执行make clean可清除编译产物micropython/ports/s3c2440/build目录

5.2 烧录

将firmware.bin烧录到开发板nand flash的0地址处

5.3 运行

在这里插入图片描述

6. 遗留的问题

  • 不能打印整型数据

    比如执行print(66),应该打印66,但是现在没有任何输出,如下图:
    在这里插入图片描述
    ​ 又比如执行micropython自带函数min(6,20),应该返回较小的6,但是同样没有任何输出,如下图:
    在这里插入图片描述

  • import问题

    比如执行import sys导入sys模块后,执行sys类下的函数会出现name not defined的问题,如下图:
    在这里插入图片描述 该问题会出现在导入的所有模块中。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值