一、前言
在从0-1探索RISCV指令集的路上,我们用百元不到的NANO 9K开发板一步步的实现:
1)最小的内核架构
2)取值,译码和执行的过程(电路实现ISA指令集)
3)存储空间的实现(寄存器,RAM, ROM的寻址)和数据搬移
4) 外设的电路实现
至此,我们有了一个自己用数字电路实现的CPU精简内核,而且实现了RISCV的ISA指令集的一部分,虽然是很小的一部分!
看看我们之前的软件编写方式,
1)用什么语言?首先要用机器码二进制来实现,汇编都不行。
2)软件怎么装载和运行?只能是写死到ROM里,没法更新,下载?更不用想了。
所以这个简洁到简陋的"MCU"和我们平常用的8051,STM32相比,缺什么?
1)编译器和反编译器。 编译器用来把C或者汇编编译成机器语言(二进制)然后可以加载执行;
2)支持交互编程的IDE集成开发环境,最好能一键下载,最好不需要专用的烧录器,比如串口下载。
MCU是工具芯片,能否用起来,用的好,依赖于开发者生态, 很多性能优异的MCU设计出来推广的不好都是因为开发环境“反人类”, 工程师/开发者“不喜欢”,从而制约了发展,越没人用越无法迭代, 越迭代慢越没人用,从而陷入僵局。
反观市场上的“当红炸子鸡”,除了芯片本身的性价比外,简单易用,稳定可靠的IDE环境,便宜易获得的下载工具都为人津津乐道,前有AVR/MCS51, 后有ESP32, STM32,所以设计MCU一定要有关注开发环境和融入生态的思路和提前规划, 这些就是本次内容要解决的问题!
二、解决方案
导入简洁而不简单的IDE , ARDUINO开发环境, 实现开发环境的闭环。
常见的生态:
8位机: 8051/STM8+IAR, STM32+KEIL......还有像STC以及其他厂商推出的各类IDE,或者基于eclipse\codeblock\vscode扩展的各种插件。
如果我们平时玩过电子制作,对arduino不会模式,经常用来做电子积木,逻辑简单而功能强大,生态极为丰富,只要是成熟的外设,不管是温湿度传感器,陀螺仪,气压计,还是OLED屏,SPI屏,甚至蓝牙,wifi都不在话下,不但有硬件生态,还有丰富的软件生态,各种库几乎能满足平常电子制作甚至专业快速开发的一切需求。
先看看我们的成果:
在arduino里导入了我们的精简内核(用nano 9k的板子实现的软核),并且支持直接一键编译,串口下载,是不是完美解决了我们上面的困扰。 当然,由于内核实现的指令集过于简单,这个软核的能编写的应用软件很局限,但至少也可以把流水灯点起来,bink,blink.......
分步骤实现:
1)修改内核,加载到板子上;形成我们自己的"MCU开发板"
硬件设计的增量----内核在前面项目基础上,增加:
- 模块--HWLoader实现
- 模块--双口RAM实现
- A口-HWLoader
- B口-core
- core修改TOP层引用,增加了端口调用,MUX端口选择
- core修改地址译码电路
综合,下载,这里注意要设置下systemverilog语言支持,不然综合会报错。
报错信息:
设置位置: project->configuration
2)配置arduino开发环境;
包括必要的三件套: 硬件支持包+环境配置文件,编译器,下载工具软件
环境配置:
获取压缩包arduino_riscvgcc_tools ” riscv.rar下载,解压到Arduino安装目录的hardware子目录下(如“C:\Arduino\hardware\”)
工具链配置(编译+下载):
tools下的gcc.rar解压。得到两个工具链的两个要素: 编译器gcc+下载器
查看配置文件: C:\arduino-1.8.19\hardware\riscv\tangnano9k\boards.txt
```
TANG.name=TANG Nano9k Board (Gaoyun GW1NR)
TANG.platform=riscv-gcc
TANG.build.board=_BOARD_TANG_NANO9K_
TANG.build.extra_flags = -march=rv32i -mabi=ilp32
TANG.compiler.c.extra_flags=
TANG.compiler.cpp.extra_flags=
TANG.ld.extra_flags =
TANG.upload.maximum_size=4096
TANG.upload.speed=115200
TANG.upload.tool=Reindeer_upload
TANG.build.mcu=tiny
TANG.build.core=riscv
TANG.build.variant=generic
```
platform.txt
```
name=Lanzhoo RISC-V (TANG Nano9k)
compiler.path = {runtime.platform.path}/tools/gcc/bin/
compiler.define= -DIDE=Arduino "-I{compiler.path}../riscv-none-elf/include"
build.extra_flags =
build.flags =
compiler.c.cmd = riscv-none-elf-gcc
compiler.c.flags = -W -c --specs=nosys.specs -O1 -fdata-sections -ffunction-sections -fno-exceptions -fno-unwind-tables
compiler.c.extra_flags =
compiler.elf.flags =
compiler.elf.cmd = riscv-none-elf-gcc
compiler.ld.cmd = riscv-none-elf-gcc
compiler.ld.flags= -Wl,--gc-sections -static -lm --specs=nosys.specs
compiler.ld.extra_flags=
compiler.ldscript=Reindeer.ld
compiler.ar.cmd=riscv-none-elf-ar
compiler.ar.flags=rcs
compiler.objcopy.cmd=riscv-none-elf-objcopy
compiler.elf2hex.cmd=riscv-none-elf-objcopy
compiler.size.path = {runtime.platform.path}/tools/gcc/bin/
compiler.size.cmd = riscv-none-elf-size
build.variant=generic
build.ldscript.path={build.variant.path}
core.header=Arduino.h
compiler.c.elf.extra_flags= -I{build.core.path}
compiler.S.extra_flags=
compiler.cpp.extra_flags=
compiler.ar.extra_flags=
compiler.objcopy.eep.flags = -O binary
compiler.objcopy.eep.extra_flags=
compiler.elf2hex.flags = -O ihex
compiler.elf2hex.extra_flags=
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {build.flags} {compiler.c.flags} {compiler.define} {compiler.c.extra_flags} {build.extra_flags} -I{build.path}/sketch {includes} "{source_file}" -o "{object_file}"
recipe.cpp.o.pattern="{compiler.path}{compiler.c.cmd}" {build.flags} {compiler.c.flags} {compiler.define} {compiler.c.extra_flags} {build.extra_flags} -I{build.path}/sketch {includes} "{source_file}" -o "{object_file}"
recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}"
recipe.c.combine.pattern="{compiler.path}{compiler.ld.cmd}" -T {build.ldscript.path}/{compiler.ldscript} {build.flags} {compiler.ld.flags} {build.extra_flags} -o "{build.path}/{build.project_name}.elf" {object_files} "{build.path}/{archive_file}"
recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
recipe.output.tmp_file={build.project_name}.hex
recipe.output.save_file={build.project_name}.{build.variant}.hex
recipe.size.pattern="{compiler.size.path}/{compiler.size.cmd}" "{build.path}/{build.project_name}.hex"
recipe.size.regex=Total\s+([0-9]+).*
# up loader
tools.Reindeer_upload.cmd=reindeer_config
tools.Reindeer_upload.path={runtime.platform.path}/tools
tools.Reindeer_upload.upload.params.verbose=
tools.Reindeer_upload.upload.params.quiet=
tools.Reindeer_upload.upload.protocol=UART
tools.Reindeer_upload.upload.pattern="{path}/{cmd}" --reset --run --port={serial.port} --baud=115200 "--image={build.path}/{build.project_name}.hex"
```
链接link配置脚本, 路径: C:\arduino-1.8.19\hardware\riscv\tangnano9k\variants\generic\Reindeer.ld
内容:
/* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014-2018 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
"elf32-littleriscv")
OUTPUT_ARCH(riscv)
ENTRY(_start)
/*
SEARCH_DIR("=/Host/Work/riscv-none-gcc-8.1.0-2/install/win64/riscv-none-gcc/riscv-none-embed/lib");
SEARCH_DIR("=/usr/local/lib");
SEARCH_DIR("=/lib");
SEARCH_DIR("=/usr/lib");
*/
SECTIONS
{
/* Read-only sections, merged into text segment: */
/* PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x80000000)); . = SEGMENT_START("text-segment", 0x80000000);
*/
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x80002000)); . = SEGMENT_START("text-segment", 0x80002000);
/* PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x00002000)); . = SEGMENT_START("text-segment", 0x00002000);
*/
/* hzheng. 2022.11.19 */
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rela.dyn :
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
*(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
*(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
*(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
PROVIDE_HIDDEN (__rela_iplt_start = .);
*(.rela.iplt)
PROVIDE_HIDDEN (__rela_iplt_end = .);
}
.rela.plt :
{
*(.rela.plt)
}
.init :
{
KEEP (*(SORT_NONE(.init)))
}
.plt : { *(.plt) }
.iplt : { *(.iplt) }
.text :
{
*(.text .stub .text.* .gnu.linkonce.t.*)
*(.text.startup .text.startup.*)
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.hot .text.hot.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
}
.fini :
{
KEEP (*(SORT_NONE(.fini)))
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.sdata2 :
{
*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
}
.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
.gcc_except_table.*) }
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
/* These sections are generated by the Sun/Oracle C++ compiler. */
.exception_ranges : ONLY_IF_RO { *(.exception_ranges
.exception_ranges*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
/* . = SEGMENT_START("data-segment", 0x80002000);
*/
/* . = SEGMENT_START("data-segment", 0x80001000);
*/
. = SEGMENT_START("data-segment", 0x00000400);
/* . = SEGMENT_START("data-segment", 0x00000400);
*/
/* hzheng. 2022.11.19 */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data .rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
. = DATA_SEGMENT_RELRO_END (0, .);
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
.got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata :
{
__global_pointer$ = . + 0x800;
*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
*(.sdata .sdata.* .gnu.linkonce.s.*)
}
_edata = .; PROVIDE (edata = .);
. = .;
__bss_start = .;
.sbss :
{
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
}
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 32 / 8 : 1);
}
. = ALIGN(32 / 8);
. = SEGMENT_START("ldata-segment", .);
. = ALIGN(32 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}
3)编写主程序代码和头文件
main.cpp
```
#include "Arduino.h"
int main(){
setup();
while (1) {
loop();
}
return 0;
}
```
头文件arduino.h:
```
#ifndef _ARDUINO_H
#define _ARDUINO_H
#ifdef __cplusplus
extern "C" {
#endif
extern void setup();
extern void loop();
#ifdef __cplusplus
}
#endif
#endif
```
(注:这里涉及.c和.cpp中函数相互调用的链接问题。如果Arduino.h中不加“#ifdef __cplusplus”相关语句,则main.c中的main()在链接时找不到.ino.cpp中实现的setup()和loop()。(把main.c改名为main.cpp就可以))
4)编写arduino 应用,查看编译下载日志, 熟悉工作流程
C:\arduino-1.8.19\arduino-builder -dump-prefs -logger=machine -hardware C:\arduino-1.8.19\hardware -hardware C:\Users\hy\AppData\Local\Arduino15\packages -tools C:\arduino-1.8.19\tools-builder -tools C:\arduino-1.8.19\hardware\tools\avr -tools C:\Users\hy\AppData\Local\Arduino15\packages -built-in-libraries C:\arduino-1.8.19\libraries -libraries C:\Users\hy\Documents\Arduino\libraries -fqbn=riscv:tangnano9k:TANG -vid-pid=0403_6010 -ide-version=10819 -build-path C:\Users\hy\AppData\Local\Temp\arduino_build_815823 -warnings=none -build-cache C:\Users\hy\AppData\Local\Temp\arduino_cache_966892 -prefs=build.warn_data_percentage=75 -verbose C:\Gowin\TangNano9k_Tutorial\04_arduino_step01\arduino01\arduino01.ino
C:\arduino-1.8.19\arduino-builder -compile -logger=machine -hardware C:\arduino-1.8.19\hardware -hardware C:\Users\hy\AppData\Local\Arduino15\packages -tools C:\arduino-1.8.19\tools-builder -tools C:\arduino-1.8.19\hardware\tools\avr -tools C:\Users\hy\AppData\Local\Arduino15\packages -built-in-libraries C:\arduino-1.8.19\libraries -libraries C:\Users\hy\Documents\Arduino\libraries -fqbn=riscv:tangnano9k:TANG -vid-pid=0403_6010 -ide-version=10819 -build-path C:\Users\hy\AppData\Local\Temp\arduino_build_815823 -warnings=none -build-cache C:\Users\hy\AppData\Local\Temp\arduino_cache_966892 -prefs=build.warn_data_percentage=75 -verbose C:\Gowin\TangNano9k_Tutorial\04_arduino_step01\arduino01\arduino01.ino
---------------------------------------------
Using board 'TANG' from platform in folder: C:\arduino-1.8.19\hardware\riscv\tangnano9k
Using core 'riscv' from platform in folder: C:\arduino-1.8.19\hardware\riscv\tangnano9k
-------------------------------------------------
Detecting libraries used...
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/riscv-none-elf-gcc" -W -c --specs=nosys.specs -O1 -fdata-sections -ffunction-sections -fno-exceptions -fno-unwind-tables -DIDE=Arduino "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/../riscv-none-elf/include" -march=rv32i -mabi=ilp32 "-IC:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/sketch" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\cores\\riscv" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\variants\\generic" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\sketch\\arduino01.ino.cpp" -o nul
----------------------------------------------------
Generating function prototypes...
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/riscv-none-elf-gcc" -W -c --specs=nosys.specs -O1 -fdata-sections -ffunction-sections -fno-exceptions -fno-unwind-tables -DIDE=Arduino "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/../riscv-none-elf/include" -march=rv32i -mabi=ilp32 "-IC:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/sketch" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\cores\\riscv" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\variants\\generic" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\sketch\\arduino01.ino.cpp" -o "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\preproc\\ctags_target_for_gcc_minus_e.cpp"
"C:\\arduino-1.8.19\\tools-builder\\ctags\\5.8-arduino11/ctags" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\preproc\\ctags_target_for_gcc_minus_e.cpp"
----------------------------------------------------
正在编译项目...
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/riscv-none-elf-gcc" -W -c --specs=nosys.specs -O1 -fdata-sections -ffunction-sections -fno-exceptions -fno-unwind-tables -DIDE=Arduino "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/../riscv-none-elf/include" -march=rv32i -mabi=ilp32 "-IC:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/sketch" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\cores\\riscv" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\variants\\generic" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\sketch\\arduino01.ino.cpp" -o "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\sketch\\arduino01.ino.cpp.o"
Compiling libraries...
Compiling core...
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/riscv-none-elf-gcc" -W -c --specs=nosys.specs -O1 -fdata-sections -ffunction-sections -fno-exceptions -fno-unwind-tables -DIDE=Arduino "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/../riscv-none-elf/include" -march=rv32i -mabi=ilp32 "-IC:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/sketch" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\cores\\riscv" "-IC:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\variants\\generic" "C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\cores\\riscv\\main.c" -o "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\core\\main.c.o"
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/riscv-none-elf-ar" rcs "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\core\\core.a" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\core\\main.c.o"
Archiving built core (caching) in: C:\Users\hy\AppData\Local\Temp\arduino_cache_966892\core\core_riscv_tangnano9k_TANG_e2890981838dae9eb3ff54af2f115652.a
--------------------------------------------
Linking everything together...
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/riscv-none-elf-gcc" -T "C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k\\variants\\generic/Reindeer.ld" -Wl,--gc-sections -static -lm --specs=nosys.specs -march=rv32i -mabi=ilp32 -o "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/arduino01.ino.elf" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823\\sketch\\arduino01.ino.cpp.o" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/core\\core.a"
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin/riscv-none-elf-objcopy" -O ihex "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/arduino01.ino.elf" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/arduino01.ino.hex"
"C:\\arduino-1.8.19\\hardware\\riscv\\tangnano9k/tools/gcc/bin//riscv-none-elf-size" "C:\\Users\\hy\\AppData\\Local\\Temp\\arduino_build_815823/arduino01.ino.hex"
项目使用了 0 字节,占用了 (0%) 程序存储空间。最大为 4096 字节。
------------------------------------------------------------------
C:\arduino-1.8.19\hardware\riscv\tangnano9k/tools/reindeer_config --reset --run --port=COM53 --baud=115200 --image=C:\Users\hy\AppData\Local\Temp\arduino_build_815823/arduino01.ino.hex
===============================================================================
# Copyright (c) 2019, PulseRain Technology LLC
# Reindeer Configuration Utility, Version 2.1
===============================================================================
baud_rate = 115200
com_port = COM53
toolchain = riscv-none-embed-
===============================================================================
Reseting CPU ...
Loading C:\Users\hy\AppData\Local\Temp\arduino_build_815823/arduino01.ino.hex
===================> start the CPU, entry point = 0x80002000
生成的.hex文件将用于烧写。
生成的.elf文件可以通过riscv-none-elf-objdump.exe反编译为汇编。
5)通过反编译,深入探索
文件路径: C:\Users\hy\AppData\Local\Temp\arduino_build_815823
反编译命令:
-D arduino01.ino.elf > code.txt
得到反编译的汇编代码:
-D arduino01.ino.elf > code.txt
arduino01.ino.elf: file format elf32-littleriscv
Disassembly of section .text:
80002000 <_start>:
80002000: 00001137 lui sp,0x1
80002004: 0240006f j 80002028 <main>
_start函数先设置堆栈指针gp也就是sp的值,然后调用若干函数初始化变量,然后跳转main函数。
80002008 <_fini>:
80002008: 0000006f j 80002008 <_fini>
8000200c <setup>:
8000200c: 00008067 ret
80002010 <loop>:
80002010: 00180813 addi a6,a6,1
80002014: 00a00793 li a5,10
80002018: 00000013 nop
8000201c: fff78793 addi a5,a5,-1
80002020: fe079ce3 bnez a5,80002018 <loop+0x8>
80002024: 00008067 ret
80002028 <main>:
80002028: ff010113 addi sp,sp,-16 # ff0 <__global_pointer$+0x3ec>
8000202c: 00112623 sw ra,12(sp)
80002030: fddff0ef jal ra,8000200c <setup>
80002034: fddff0ef jal ra,80002010 <loop>
80002038: ffdff06f j 80002034 <main+0xc>
Disassembly of section .eh_frame:
00000400 <__FRAME_END__>:
400: 0000 .2byte 0x0
...
Disassembly of section .comment:
00000000 <.comment>:
0: 3a434347 .4byte 0x3a434347
4: 2820 .2byte 0x2820
6: 5078 .2byte 0x5078
8: 6361 .2byte 0x6361
a: 4e47206b .4byte 0x4e47206b
e: 2055 .2byte 0x2055
10: 4952 .2byte 0x4952
12: 562d4353 .4byte 0x562d4353
16: 4520 .2byte 0x4520
18: 626d .2byte 0x626d
1a: 6465 .2byte 0x6465
1c: 6564 .2byte 0x6564
1e: 2064 .2byte 0x2064
20: 20434347 .4byte 0x20434347
24: 3878 .2byte 0x3878
26: 5f36 .2byte 0x5f36
28: 3436 .2byte 0x3436
2a: 2029 .2byte 0x2029
2c: 3231 .2byte 0x3231
2e: 312e .2byte 0x312e
30: 302e .2byte 0x302e
...
Disassembly of section .riscv.attributes:
00000000 <.riscv.attributes>:
0: 1b41 .2byte 0x1b41
2: 0000 .2byte 0x0
4: 7200 .2byte 0x7200
6: 7369 .2byte 0x7369
8: 01007663 bgeu zero,a6,14 <__FRAME_END__-0x3ec>
c: 0011 .2byte 0x11
e: 0000 .2byte 0x0
10: 1004 .2byte 0x1004
12: 7205 .2byte 0x7205
14: 3376 .2byte 0x3376
16: 6932 .2byte 0x6932
18: 7032 .2byte 0x7032
1a: 0031 .2byte 0x31
- 其它函数还包括:deregister_tm_clones、register_tm_clones、__do_global_dtors_aux、frame_dummy、atexit、exit、__libc_fini_array、__libc_init_array、memset、__register_exitproc、__call_exitprocs、_exit。
总共代码量是0x052c,约2.4kB。
另外还占用了约2点多kB的数据区。
三、运行结果观察
下载程序前:
Arduino未下载程序前,core运行的程序是rom_data.mi中的指令。
观察led的值:
```
00, 0000
||: (循环)
01, 0100
10, 1000
11, 1000
00, 1011
:||
```
和以下指令(addr[3:2], code[5:2])是一一对应的:
rom_data.mi
#File_format=Hex
#Address_depth=1024
#Data_width=32
00002783
00178813
01002023
81002823
ff5ff06f
00000000
00000000
00000000
#### 下载程序后的现象
观察led的值:
```
00, 1101
01, 1011
10, 0100
11, 1000
00, 1011
||: (循环)
11, 1001
00, 0100
01, 0100
10, 0100
11, 0100
00, 1000
01, 1001
10, 0100
11, 1000
00, 1011
:||
```
观察反编译的代码
对照反汇编指令,是一致的:
```
80002000 <_start>:
80002000: 00001137 lui sp,0x1
80002004: 0240006f j 80002028 <main>
80002028 <main>:
80002028: ff010113 addi sp,sp,-16 # ff0 <__global_pointer$+0x3ec>
8000202c: 00112623 sw ra,12(sp)
80002030: fddff0ef jal ra,8000200c <_Z5setupv>
||: (循环)
8000200c <_Z5setupv>:
8000200c: 00008067 ret
80002010 <_Z4loopv>:
80002010: 00180813 addi a6,a6,1
80002014: 00a00793 li a5,10
80002018: 00000013 nop
8000201c: fff78793 addi a5,a5,-1
80002020: fe079ce3 bnez a5,80002018 <_Z4loopv+0x8>
80002024: 00008067 ret
80002028 <main>:
80002028: ff010113 addi sp,sp,-16 # ff0 <__global_pointer$+0x3ec>
8000202c: 00112623 sw ra,12(sp)
80002030: fddff0ef jal ra,8000200c <_Z5setupv>
:||
```
由于没有实现ret指令和bnez指令,所以只有jal和j进行了跳转,其它都是顺序执行。
四、小结
通过这样的IDE+TOOLCHAIN组合,我们建立了自研CPU核的小生态闭环
内核实现,外设,指令集,开发工具,编译环境。
没错,商用的MCU也是这些,只是规模和复杂度增加了几个数量级,但是原理基本一致。