cruelfox 发表于 2021-7-8 09:29

【Perf-V评测】E203 SOC上程序的构建与CoreMark移植

<p>  在上一篇帖子中,我使用 GCC 编译了一段操作 E203 SOC 的 GPIO 的代码,然后用 openocd 调试工具将二进制直接写入 E203 的 ITCM 内存里面去,再修改 PC 寄存器使 CPU 从指定的函数入口地址开始运行。这样只是调试模式下运行了一个程序片段,还没有搭建一个完整的程序,让 E203 从启动后自己找到程序入口地址,开始运行。<br />
  已经很熟悉的 ARM Cortex-M? 系列 CPU, 复位后是从中断向量表的第一项和第二项获得SP、PC的初值,也就是自动装入 Reset 向量地址,开始执行。但 E203 不是这样的,它是从硬件配置的一个固定的地址开始运行。在我综合过的代码中,地址有两个选项:一个是片内ROM(然后它会跳转到 ITCM 的开头地址),另一个是外部 QSPI flash 映射的地址。<br />
  那么我可以把 ITCM 的首地址 0x80000000 作为整个程序二进制代码的入口地址了。下面要搭建一个完整的程序,按照 MCU 开发的思路,还需要一个启动文件(提供初始化代码,跳转到main函数),和一个给链接器的脚本。现在从零开始,这两个文件怎么获得?<br />
  蜂鸟E203的git上面有一个 sirv-e-sdk 目录,其中的 bsp 子目录下有一些文件,可以参考:</p>

<p><span style="color:#000000;"><span style="font-family:Courier;">bsp<br />
├─drivers<br />
│ &nbsp;├─fe300prci<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;fe300prci_driver.c<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;fe300prci_driver.h<br />
│ &nbsp;│<br />
│ &nbsp;└─plic<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;plic_driver.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;plic_driver.h<br />
│<br />
├─env<br />
│ &nbsp;│ &nbsp;common.mk<br />
│ &nbsp;│ &nbsp;coreplexip-arty.h<br />
│ &nbsp;│ &nbsp;encoding.h<br />
│ &nbsp;│ &nbsp;entry.S<br />
│ &nbsp;│ &nbsp;hifive1.h<br />
│ &nbsp;│ &nbsp;sirv_printf.c<br />
│ &nbsp;│ &nbsp;start.S<br />
│ &nbsp;│<br />
│ &nbsp;├─sirv-e201-arty<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;init.c<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;link.lds<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;openocd.cfg<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;platform.h<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;settings.mk<br />
│ &nbsp;...<br />
│<br />
├─include<br />
│ &nbsp;└─sifive<br />
│ &nbsp; &nbsp; &nbsp;│ &nbsp;bits.h<br />
│ &nbsp; &nbsp; &nbsp;│ &nbsp;const.h<br />
│ &nbsp; &nbsp; &nbsp;│ &nbsp;sections.h<br />
│ &nbsp; &nbsp; &nbsp;│ &nbsp;smp.h<br />
│ &nbsp; &nbsp; &nbsp;│<br />
│ &nbsp; &nbsp; &nbsp;└─devices<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;aon.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;clint.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;gpio.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;i2c.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;otp.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;plic.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;prci.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pwm.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;spi.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uart.h<br />
│<br />
├─libwrap<br />
│ &nbsp;│ &nbsp;libwrap.mk<br />
│ &nbsp;│<br />
│ &nbsp;├─misc<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;write_hex.c<br />
│ &nbsp;│<br />
│ &nbsp;├─stdlib<br />
│ &nbsp;│ &nbsp; &nbsp; &nbsp;malloc.c<br />
│ &nbsp;│<br />
│ &nbsp;└─sys<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;_exit.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;close.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;execve.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fork.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fstat.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;getpid.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isatty.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kill.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;link.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;lseek.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;open.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;openat.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;read.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sbrk.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;stat.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;stub.h<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;times.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;unlink.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;wait.c<br />
│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;write.c<br />
│<br />
└─tools<br />
&nbsp; &nbsp; &nbsp; &nbsp; openocd_upload.sh</span></span></p>

<p>&nbsp;</p>

<p>  注意到 env 目录下有两个汇编文件:start.S 和 entry.S, 其中 start.S 看内容像启动文件了:</p>

<pre>
<code>        .section .init
        .globl _start
        .type _start,@FUNCTION _start:
.option push
.option norelax
        la gp, __global_pointer$
.option pop
        la sp, _sp

        /* Bob: Load code section from flash to ITCM */
        la a0, _itcm_lma
        la a1, _itcm
    beq a0, a1, 2f/*If the ITCM phy-address same as the logic-address, then quit*/
        la a2, _eitcm
        bgeu a1, a2, 2f
1:
        lw t0, (a0)
        sw t0, (a1)
        addi a0, a0, 4
        addi a1, a1, 4
        bltu a1, a2, 1b
2:
        /* Load data section */
        la a0, _data_lma
        la a1, _data
        la a2, _edata
        bgeu a1, a2, 2f
1:
        lw t0, (a0)
        sw t0, (a1)
        addi a0, a0, 4
        addi a1, a1, 4
        bltu a1, a2, 1b
2:
        /* Clear bss section */
        la a0, __bss_start
        la a1, _end
        bgeu a0, a1, 2f
1:
        sw zero, (a0)
        addi a0, a0, 4
        bltu a0, a1, 1b
2:
        /* Call global constructors */
        la a0, __libc_fini_array
        call atexit
        call __libc_init_array
        /* Enable FPU */
        li t0, MSTATUS_FS
        csrs mstatus, t0
        csrr t1, mstatus
        and t1, t1, t0
        beqz t1, 1f
    /*
        fssr x0
    */
1:
        /* argc = argv = 0 */
        li a0, 0
        li a1, 0
        call main
        tail exit
1:
        j 1b
</code></pre>

<p>  这里面引用的 _itcm_lma, _data_lma, __bss_start 等地址是在别处定义,应该是配合链接脚本使用。再留意看,env 目录的子目录里还能找到 link.lds 文件,多半就是链接脚本文件了。打开 sirv-e201-arty/link.lds 得到确认。其中对内存的描述是这样:</p>

<pre>
<code>MEMORY
{
flash (rxai!w) : ORIGIN = 0x20000000, LENGTH = 8M
itcm (rxai!w) : ORIGIN = 0x80000000, LENGTH = 64K
ram (wxa!ri) : ORIGIN = 0x90000000, LENGTH = 64K
}</code></pre>

<p>&nbsp;</p>

<p>  现在试写一个空的 main() 函数,编译链接一下试试。事先将 start.S 里面对 atexit, exit 等C标准库函数的调用去掉。</p>

<p><span style="color:#16a085;"><span style="font-family:Courier;">riscv-nuclei-elf-gcc -c -Wall -march=rv32imc -mabi=ilp32 -Os &nbsp;main.c<br />
riscv-nuclei-elf-gcc -c -Wall -march=rv32imc -mabi=ilp32 ../bsp/env/start.S<br />
riscv-nuclei-elf-gcc -nostdlib -march=rv32imc -mabi=ilp32 --specs=nano.specs main.o start.o -o test.elf -T ../bsp/env/sirv-e201-arty/link.lds</span></span></p>

<p>这样就生成了一个完整的程序。用 nm 看一下里面使用的地址:</p>

<p><span style="color:#16a085;"><span style="font-family:Courier;">&gt;riscv-nuclei-elf-nm test.elf | sort<br />
00000800 A __stack_size<br />
20000000 T _start<br />
200000ac B _itcm_lma<br />
200000b0 ? _data_lma<br />
80000000 B _itcm<br />
80000000 T main<br />
80000004 T _eitcm<br />
90000000 ? _data<br />
90000000 B __bss_start<br />
90000000 B _edata<br />
90000000 B _end<br />
90000800 D __global_pointer$<br />
90010000 B _sp</span></span></p>

<p>0x80000000 是 ITCM, 0x90000000 是 DTCM, 这两处都是 RAM 地址。然而出现了 0x20000000 地址,_start 放在那里。看来这个程序是从 flash 地址启动的。反汇编看一下:</p>

<pre>
<code>Disassembly of section .init:

20000000 &lt;_start&gt;:
20000000:   70001197      auipc gp,0x70001
20000004:   80018193      addigp,gp,-2048 # 90000800 &lt;__global_pointer$&gt;
20000008:   70010117      auipc sp,0x70010
2000000c:   ff810113      addisp,sp,-8 # 90010000 &lt;_sp&gt;
20000010:   00000517      auipc a0,0x0
20000014:   09c50513      addia0,a0,156 # 200000ac &lt;_itcm_lma&gt;
20000018:   60000597      auipc a1,0x60000
2000001c:   fe858593      addia1,a1,-24 # 80000000 &lt;_itcm&gt;
20000020:   02b50063      beq   a0,a1,20000040 &lt;_start+0x40&gt;
20000024:   60000617      auipc a2,0x60000
20000028:   fe060613      addia2,a2,-32 # 80000004 &lt;_eitcm&gt;
2000002c:   00c5fa63      bgeua1,a2,20000040 &lt;_start+0x40&gt;
20000030:   00052283      lw    t0,0(a0)
20000034:   0055a023      sw    t0,0(a1)
20000038:   0511          addia0,a0,4
2000003a:   0591          addia1,a1,4
2000003c:   fec5eae3      bltua1,a2,20000030 &lt;_start+0x30&gt;
20000040:   00000517      auipc a0,0x0
20000044:   07050513      addia0,a0,112 # 200000b0 &lt;_data_lma&gt;
20000048:   70000597      auipc a1,0x70000
2000004c:   fb858593      addia1,a1,-72 # 90000000 &lt;_data&gt;
20000050:   70000617      auipc a2,0x70000
20000054:   fb060613      addia2,a2,-80 # 90000000 &lt;_data&gt;
20000058:   00c5fa63      bgeua1,a2,2000006c &lt;_start+0x6c&gt;
2000005c:   00052283      lw    t0,0(a0)
20000060:   0055a023      sw    t0,0(a1)
20000064:   0511          addia0,a0,4
20000066:   0591          addia1,a1,4
20000068:   fec5eae3      bltua1,a2,2000005c &lt;_start+0x5c&gt;
2000006c:   70000517      auipc a0,0x70000
20000070:   f9450513      addia0,a0,-108 # 90000000 &lt;_data&gt;
20000074:   70000597      auipc a1,0x70000
20000078:   f8c58593      addia1,a1,-116 # 90000000 &lt;_data&gt;
2000007c:   00b57763      bgeua0,a1,2000008a &lt;_start+0x8a&gt;
20000080:   00052023      sw    zero,0(a0)
20000084:   0511          addia0,a0,4
20000086:   feb56de3      bltua0,a1,20000080 &lt;_start+0x80&gt;
2000008a:   6299          lui   t0,0x6
2000008c:   3002a073      csrsmstatus,t0
20000090:   30002373      csrrt1,mstatus
20000094:   00537333      and   t1,t1,t0
20000098:   00030263      beqzt1,2000009c &lt;_start+0x9c&gt;
2000009c:   4501          li    a0,0
2000009e:   4581          li    a1,0
200000a0:   60000097      auipc ra,0x60000
200000a4:   f60080e7      jalr-160(ra) # 80000000 &lt;_itcm&gt;
200000a8:   a001          j   200000a8 &lt;_start+0xa8&gt;

Disassembly of section .text:

80000000 &lt;main&gt;:
80000000:   4501          lia0,0
80000002:   8082          ret</code></pre>

<p>&nbsp;</p>

<p>  这段代码涉及的 RISC-V 指令需要查阅资料熟悉一下。有的指令像运算用的,含义可以猜出来。<br />
  RISC-V RV32 架构有32个通用寄存器:x0~x31, 但是上面反汇编代码中用的是别名 (a0, a1, t0, gp ...)。<br />
  <span style="color:#c0392b;"><strong>auipc</strong></span> 指令在 ARM 指令集里面没有对应,它的效果是把当前 PC 寄存器值和一个立即数向加,结果存入指定寄存器。立即数的高20位从指令中译码出来,低12位全0. 在 _start 入口的代码开头,有几处是 <span style="color:#c0392b;"><strong>auipc</strong></span> 指令后跟一条 <span style="color:#c0392b;"><strong>addi</strong></span> 指令对结果进行调整,综合效果是给一个寄存器赋值。看 start.S 汇编文件里面实际写的是一条 <strong>la</strong> 指令&mdash;&mdash;这是一条伪指令。<br />
  gp, sp 寄存器先被用常量初始化了。<br />
  a0, a1, a2 寄存器分别初始化为 _itcm_lma , _itcm 及 _eitcm 三个地址,然后用了一个循环将 _itcm_lma 开始的内存复制到 _itcm 开始的地方去(通过 <span style="color:#c0392b;"><strong>lw</strong></span>, <span style="color:#c0392b;"><strong>sw</strong></span> 指令,即 load/store word 之意)。当到达 _eitcm 地址时结束。<br />
  <span style="color:#c0392b;"><strong>bgeu</strong></span>, <span style="color:#c0392b;"><strong>bltu</strong></span> 都是条件转移指令,分别表示 &quot;greater than or equal to&quot;, &quot;less than&quot;,后面的 u 表示无符号数比较。与 ARM 指令集有区别的是,RISC-V 的条件转移将比较运算和分支转移合并在一条指令中。<br />
  后面对 _data 数据的初始化也是同样的操作,增加了一个对 _bss 数据段清零的过程。<br />
  <span style="color:#c0392b;"><strong>csrs</strong></span>, <span style="color:#c0392b;"><strong>csrr</strong></span> 这两种指令是 RISC-V 的特点。CSR 意思是 &quot;Control and Status Register&quot;, 即控制和状态寄存器。RISC-V的指令编码空间留出了4096个CSR的位置&mdash;&mdash;太多了。<br />
  <span style="color:#c0392b;"><strong>jalr</strong></span> 指令,是&quot;jump and link register&quot;, 相当于 ARM 的 BLX 指令的增强方式:可带偏移量,还可以指定link的寄存器。<br />
  在 start.S 源文件中,只写了一条 &quot;call main&quot;,翻译成了一条 <span style="color:#c0392b;"><strong>auipc</strong></span> 指令跟一条 <span style="color:#c0392b;"><strong>jalr</strong></span> 指令。如果直接写 &quot;<span style="color:#c0392b;"><strong>jal</strong></span> main&quot; 如何呢?我试了,因为转移地址范围太大,链接失败。因为 _start 是 flash 代码,main 在 ITCM 中(已经复制过去了),这个跳转得用间接方式。<br />
&nbsp;</p>

<p>  程序要有功能,需要使用片上设备跟外部环境打交道。E203 SOC 配置了一些设备比如 UART, SPI, PWM 等,可以从文档中找到寄存器的描述。在 sirv-e-sdk/bsp/include/sifive/devices 目录下有一些头文件,对操作寄存器提供了一点帮助。在别处还能找到 platform.h . 但是这些文件定义的东西只是聊胜于无,和常规MCU SDK里面的设备寄存器定义的完善程度差远了。<br />
  操作 UART 的寄存器,是类似这样的写法:</p>

<pre>
<code class="language-cpp">      while (UART0_REG(UART_REG_TXFIFO) &amp; 0x80000000) ;
      UART0_REG(UART_REG_TXFIFO) = current;</code></pre>

<p>  因为缺少寄存器位的宏,只能直接写数值,要看懂含义就得要注释。</p>

<p>  为了使用 UART 输出字符,除了配置 UART (波特率等)外,还需要在I/O口配置中将 UART 的功能复用开启。否则,I/O口默认功能是GPIO.&nbsp;<br />
  在 e203_subsys_perips.v 中有如下代码:</p>

<pre>
<code>assign uart_pins_0_io_pins_rxd_i_ival = gpio_iof_0_16_i_ival;
assign gpio_iof_0_16_o_oval      = uart_pins_0_io_pins_rxd_o_oval;
assign gpio_iof_0_16_o_oe      = uart_pins_0_io_pins_rxd_o_oe;
assign gpio_iof_0_16_o_ie      = uart_pins_0_io_pins_rxd_o_ie;
assign gpio_iof_0_16_o_valid   = 1'h1;

assign uart_pins_0_io_pins_txd_i_ival = gpio_iof_0_17_i_ival;
assign gpio_iof_0_17_o_oval      = uart_pins_0_io_pins_txd_o_oval;
assign gpio_iof_0_17_o_oe      = uart_pins_0_io_pins_txd_o_oe;
assign gpio_iof_0_17_o_ie      = uart_pins_0_io_pins_txd_o_ie;
assign gpio_iof_0_17_o_valid   = 1'h1;</code></pre>

<p>这说明了 GPIO16,17 的 I/O function 是 UART0 的 RXD/TXD. 于是,需要将 GPIO IOF_EN 寄存器的16和17位设置成1. 至于 IOF_SEL 寄存器,是选择两个复用功能中的哪一个,暂且不管,猜 UART0 功能是默认的。</p>

<p>&nbsp;</p>

<p>  于是 HelloWorld 程序就可以写出来了:</p>

<pre>
<code class="language-cpp">#include&lt;platform.h&gt;

int main()
{
        const char str[]="RISC-V E203 SOC Running\r\n";
        UART0_REG(UART_REG_TXCTRL)=1;        // txen
        UART0_REG(UART_REG_DIV)=138;        // 16000000/115200 = 139

        GPIO_REG(GPIO_IOF_EN) = 1&lt;&lt;16|1&lt;&lt;17;
        for(;;)
        {
                int i;
                for(i=0;i&lt;sizeof(str);i++)
                {
                        while (UART0_REG(UART_REG_TXFIFO) &amp; 0x80000000)
                        {}
                        UART0_REG(UART_REG_TXFIFO) = str;
                }
        }
}</code></pre>

<p>  编译之后将二进制代码写到 ITCM 里面,然后进行硬件复位。E203自动从ROM开始执行,跳转到ITCM我的代码执行。虽然 OpenOCD 可以调试,但不能像 STM32 那样用 reset init 从最前面开始调试。若要跟踪完整执行过程,对这小程序可以使用 RTL 仿真的办法,然后看仿真生成的波形文件。<br />
  UART TXD (GPIO17)连到一个作为串口的FT232R上,从电脑上收到了来自板子的字符。<br />
</p>

cruelfox 发表于 2021-7-8 09:29

本帖最后由 cruelfox 于 2021-7-8 09:33 编辑

<p>  移植 CoreMark 除了要补充输出函数(比如使用UART输出)外,还需要一个定时器,以获取纯计算部分消耗的时钟周期数。<br />
  我查阅了 SiFive-E300 手册,没有找到像STM32中那样类似的通用寄存器。有WDT,但是不合适此处用。但是 SIRV-E200-SOC 介绍中说有 CLINT: 主要实现 RISC-V 架构手册中规定的标准计时器(Timer)和软件中断功能。<br />
  在 SiFive-E3-Coreplex-v1.2.pdf 中稍有介绍:</p>

<p></p>

<p>  在头文件 clint.h 中定义如下:</p>

<pre>
<code class="language-cpp">#define CLINT_MSIP 0x0000
#define CLINT_MSIP_size   0x4
#define CLINT_MTIMECMP 0x4000
#define CLINT_MTIMECMP_size 0x8
#define CLINT_MTIME 0xBFF8
#define CLINT_MTIME_size 0x8</code></pre>

<p>  CLINT 的 MTIME 寄存器就是64-bit real-time couonter 的值。在 OpenOCD 调试 halt 状态下访问此处地址,可以看到内容在变化。<br />
<span style="color:#16a085;"><span style="font-family:Courier;">&gt; mdw 0x0200BFF8 2<br />
0x0200bff8: 072ae195 00000000<br />
&gt; mdw 0x0200BFF8 2<br />
0x0200bff8: 072bac11 00000000</span></span><br />
  但是经过掐表一算,这个计时器的频率应是源于 32768Hz 时钟。所以不太适合 CoreMark 的用途。</p>

<p>&nbsp;</p>

<p>  在 platform.h 末尾发现 get_timer_value, get_instret_value, get_cycle_value 三个函数声明。原来 RISC-V 已经包含了 performance counter, 对应的 CSR 如下:<br />
  sdk 中 get_cycle_value() 是用 read_csr(mcycle) 和 read_csr(mcycleh) 实现的,可获取CPU时钟周期数。对于 CoreMark, 32位计时已经够用,所以在 core_portme.c 里面写获取时钟的函数为:</p>

<pre>
<code class="language-cpp">CORETIMETYPE barebones_clock() {
        return read_csr(mcycle);
}</code></pre>

<p>至于为什么 CSR 用 mcycle, 而不是 cycle 我不解。写 read_csr(cycle) 结果计数一直是0.</p>

<p>&nbsp;</p>

<p>  CoreMark 运行结果:</p>

<p><span style="color:#000000;"><span style="font-family:Courier;">CoreMark run on Perf-V (E203 SOC), ported by cruelfox.<br />
2K performance run parameters for coremark.<br />
CoreMark Size &nbsp; &nbsp;: 666<br />
Total ticks &nbsp; &nbsp; &nbsp;: 509895664<br />
Total time (secs): 31.868479<br />
Iterations/Sec &nbsp; : 31.378969<br />
Iterations &nbsp; &nbsp; &nbsp; : 1000<br />
Compiler version : GCC9.2.0<br />
Compiler flags &nbsp; : -O3<br />
Memory location &nbsp;: STACK<br />
seedcrc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: 0xe9f5<br />
crclist &nbsp; &nbsp; &nbsp; : 0xe714<br />
crcmatrix &nbsp; &nbsp; : 0x1fd7<br />
crcstate &nbsp; &nbsp; &nbsp;: 0x8e3a<br />
crcfinal &nbsp; &nbsp; &nbsp;: 0xd340<br />
Correct operation validated. See readme.txt for run and reporting rules.<br />
CoreMark 1.0 : </span></span><strong><span style="color:#c0392b;"><span style="font-family:Courier;">31.378969</span></span></strong><span style="color:#000000;"><span style="font-family:Courier;"> / GCC9.2.0 -O3 / STACK</span></span><br />
&nbsp;</p>

<p>&nbsp; 附上编译用的 Makefile:</p>

<pre>
<code>default: test.hex

CC =riscv-nuclei-elf-gcc -c
LD =riscv-nuclei-elf-gcc -nostdlib
CFLAGS =-Wall -O2 -march=rv32imc -mabi=ilp32
INC =-I../bsp/env -I../bsp/include -I../bsp/include/sifive/devices
LDFLAGS=-march=rv32imc -mabi=ilp32 --specs=nano.specs
COREMARK = core_main.o core_matrix.o core_state.o core_list_join.o \
                        core_util.o core_portme.o ee_printf.o cvt.o

test.elf : start_itcm.o $(COREMARK) uart_char.o
        $(LD) $(LDFLAGS) $^ -o $@ -lc -T../itcmload.ld -lgcc

%.o : ../bsp/env/%.S
        $(CC) $(CFLAGS) $&lt;

%.o : %.c core_portme.h
        $(CC) $(CFLAGS) -O2 $(INC) $&lt;

core_matrix.o : core_matrix.c core_portme.h
        $(CC) $(CFLAGS) -O3 $(INC) -finline-functions$&lt;

core_list_join.o : core_list_join.c core_portme.h
        $(CC) $(CFLAGS) -O3 $(INC) $&lt;

core_util.o : core_util.c core_portme.h
        $(CC) $(CFLAGS) -O3 $(INC) $&lt;

core_state.o : core_state.c core_portme.h
        $(CC) $(CFLAGS) -O3 $(INC) $&lt;

core_main.o : core_main.c core_portme.h
        $(CC) $(CFLAGS) -O2 $(INC) $&lt; -DFLAGS_STR='"-O3"'

%.hex : %.elf
        riscv-nuclei-elf-objcopy -Oihex $&lt; $@
</code></pre>

<p>&nbsp;</p>

Jacktang 发表于 2021-7-8 11:31

<p>看楼主的介绍,的确是不能像 STM32 那样用 reset init 从最前面开始调试</p>
页: [1]
查看完整版本: 【Perf-V评测】E203 SOC上程序的构建与CoreMark移植