基于 STM32+iCE40 的电赛训练平台移植 Reindeer 软核并实现流水灯功能
在 ICE40UP5K FPGA,移植 Reindeer RSICV 软核,重新映射串口的 Tx 和 Rx 管脚,增加一个寄存器,映射到控制板卡上的 4 个 LED 灯。在软核上运行一个程序,实现打印调试信息到串口输出、执行流水灯等功能。
标签
嵌入式系统
FPGA
2023寒假在家练
topgear
更新2023-03-28
472

2023寒假一起练 - 基于 STM32+iCE40 的电赛训练平台移植 Reindeer 软核并实现流水灯功能

 

一、项目描述

本次活动,硬禾提供的板卡上有 Lattice 的 ICE40UP5K FPGA 和 STM32G031 MCU,板载LPC11U35 下载器,可以通过 USB-C 接口进行 FPGA 的配置,并通过虚拟串口通信配置 STM32G031,支持在 ICE40UP5K 上对 RISC-V 软核的移植以及使用开源的 FPGA 开发工具链。另外硬禾还搭配了电赛扩展板,用于实现信号源、仪器仪表、控制以及信号处理等功能。

在本项目中,我选择移植 PulseRain Reindeer 这个软核,使用到了板卡上的这些硬件 :

  • LPC11U35,虚拟出一个 U 盘,可以用于下载 FPGA rbt 文件
  • ICE40UP5K,运行 PulseRain Reindeer 软核
  • 板载 LED1~LED4 用于实现流水灯演示

所以:

  • 电赛扩展板上的高速 ADC/DAC、LCD、编码器和按键都没有使用到,未来有时间会尝试把这些外设都用起来。
  • 板卡上的 STM32G031 也没有利用上。

二、设计思路

具体实现的思路如下:

  • 安装配置 ICE40UP5K FPGA 的设计工具,在本项目项目我使用的是 Lattice 官方提供的 Radiant 设计软件,官方提供免费的 LICENSE 供工程师使用;
  • 从 github 上克隆 PulseRain Reindeer 项目;
  • 使用 Radiant 打开 Indeer 的工程文件,修改串口 Tx/Rx 的管脚映射;
  • 在工程中的 Reindeer 模块中增加 LED1-LED4 等 4 个输出,并增加一个 gpio_out 寄存器,低四位分别对应 LED1-LED4 这四个管脚,并修改约束文件中的管脚对应核心板上的四个 LED 灯;
  • 修改工程的 Bitstream 导出格式为 Raw Bit File,然后执行综合、布线,将导出的 rbt 文件直接拷贝到名为 step 的 U 盘,实现下载 bitfile 到 核心板上的 spi flash;
  • 下载 RSICV 工具链,编译一个实现依次点亮 LED 灯的程序;
  • 连接一个串口到核心板上 FPGA 映射的 UART Tx/Rx 管脚,使用 Reindeer 工程内提供的 python script,通过 Reindeer onchip-debugger 下载程序 elf 文件到软核上执行;

三、FPGA 管脚连接

  • UART Tx/Rx:

备注:

因为核心板上这两个管脚连接到了扩展板上的ADC,会影响串口工作,所以可以将ADC从扩展板上移除或者将核心板从扩展版上移除,然后再进行串口连接。

核心板接口 ICE40UP5K FPGA
Pin 9 (丝印F0) 26 (TXD)
Pin 10 (丝印F1)  27 (RXD)
  • 核心板上的 LED1-LED4:
核心板 LED ICE40UP5K FPGA
LED1 48 (LED1)
LED2 47 (LED2)
LED3 46 (LED3)
LED4 45 (LED4)

四、开发环境的搭建和移植过程

  • 安装配置 ICE40UP5K FPGA 的设计工具:

从 https://www.latticesemi.com/zh-CN/Products/DesignSoftwareAndIP/FPGAandLDS/Radiant 下载 Windows 版本进行安装,然后申请 License;

  • 克隆 PulseRain Reindeer 项目:
git clone https://github.com/PulseRain/Reindeer.git

备注:

我在移植过程中发现使用 master 最新版本代码时,生成的 bitfile 有些问题,在执行脚本下载仓库里的 hello_world.bin 程序之后无法输出打印串口信息,脚本执行结果如下:

===============================================================================
# Copyright (c) 2018, PulseRain Technology LLC
# Reindeer Configuration Utility, Version 1.0
===============================================================================
baud_rate  =  115200
com_port   =  COM17
toolchain  =  riscv-none-embed-
===============================================================================
Reseting CPU ...
Loading  E:\lattice\Reindeer\bitstream_and_binary\zephyr\hello_world.elf
__start 80000000

//================================================================
//== Section  vector
//================================================================
        addr = 0x80000000, length = 1044 (0x414)

//================================================================
//== Section  reset
//================================================================
        addr = 0x80004000, length = 4 (0x4)

//================================================================
//== Section  exceptions
//================================================================
        addr = 0x80004004, length = 620 (0x26c)

//================================================================
//== Section  text
//================================================================
        addr = 0x80004270, length = 7172 (0x1c04)

//================================================================
//== Section  devconfig
//================================================================
        addr = 0x80005e74, length = 36 (0x24)

//================================================================
//== Section  rodata
//================================================================
        addr = 0x80005e98, length = 1216 (0x4c0)

//================================================================
//== Section  datas
//================================================================
        addr = 0x80006358, length = 28 (0x1c)

//================================================================
//== Section  initlevel
//================================================================
        addr = 0x80006374, length = 36 (0x24)

===================> start the CPU, entry point = 0x80000000

# 然后就停在这里,没有串口信息输出...

通过与群里的同学进行交流之后,Reindeer 可能在某一个版本更新之后出了问题,于是我找到了最初提交 hello_world.bin 时的commit id,将仓库 checkout 到这个commit id的版本,经测试该版本可以使用,下载完 hello_world.bin 之后也可以输出信息到串口。

  • checkout 仓库到 commit-id c23e62b47d104334d3176716ebadf4af878e8ef3
git checkout c23e62b47d104334d3176716ebadf4af878e8ef3
  • 使用 Radiant 打开 Reindeer 的工程文件:
打开 Reindeer/build/par/Lattice/UPDuinoV2/UPDuinoV2.rdf
  • 在工程中的 Reindeer 模块中增加 LED1-LED4 等 4 个输出,并增加一个 gpio_out 寄存器,低四位分别对应 LED1-LED4 这四个管脚,改动 diff 如下:
diff --git a/source/Lattice/UPDuinoV2/Reindeer.v b/source/Lattice/UPDuinoV2/Reindeer.v
index 3adc164..cfc34a7 100644
--- a/source/Lattice/UPDuinoV2/Reindeer.v
+++ b/source/Lattice/UPDuinoV2/Reindeer.v
@@ -38,6 +38,11 @@ module Reindeer (
     //=====================================================================
     // status
     //=====================================================================
+        output  wire                                            LED1,
+        output  wire                                            LED2,
+        output  wire                                            LED3,
+        output  wire                                            LED4,
+
         output  wire                                            REDn,       // Red
         output  wire                                            BLUn,       // Blue
         output  wire                                            GRNn        // Green
@@ -71,6 +76,7 @@ module Reindeer (
        
         wire                                                      uart_tx_cpu;
         wire                                                      uart_tx_ocd;
+        wire  [7 : 0]                                             gpio_out;
        
     //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     // CLOCK
@@ -138,6 +144,7 @@ module Reindeer (
             .start_address (cpu_start_addr),
         
             .processor_paused (processor_paused),
+            .gpio_out(gpio_out),
             
             .peek_pc (),
             .peek_ir (),
@@ -148,6 +155,10 @@ module Reindeer (
             );
      
         assign processor_active = ~processor_paused;
+        assign LED1 = gpio_out[0];
+        assign LED2 = gpio_out[1];
+        assign LED3 = gpio_out[2];
+        assign LED4 = gpio_out[3];
         
     //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     // OCD
diff --git a/submodules/PulseRain_MCU/PulseRain_processor_core/source/PulseRain_RV2T_core.v b/submodules/PulseRain_MCU/PulseRain_processor_core/source/PulseRain_RV2T_core.v
index 6a2e4e6..903fd56 100644
--- a/submodules/PulseRain_MCU/PulseRain_processor_core/source/PulseRain_RV2T_core.v
+++ b/submodules/PulseRain_MCU/PulseRain_processor_core/source/PulseRain_RV2T_core.v
@@ -65,6 +65,7 @@ module PulseRain_RV2T_core (
         
         output  wire  [31 : 0]                                  peek_pc,
         output  wire  [31 : 0]                                  peek_ir,
+        output  wire  [31 : 0]                                  gpio_out,
         
         
         output  wire  [`MEM_ADDR_BITS - 1 : 0]                  mem_addr,
@@ -238,6 +239,7 @@ module PulseRain_RV2T_core (
                 .clk (clk),
                 .reset_n (reset_n),
                 .sync_reset (sync_reset),
+                .gpio_out(gpio_out),
                 
                 .data_read_enable  (mm_reg_re),
                 .data_write_enable (mm_reg_we),
diff --git a/submodules/PulseRain_MCU/PulseRain_processor_core/source/RV2T_mm_reg.v b/submodules/PulseRain_MCU/PulseRain_processor_core/source/RV2T_mm_reg.v
index 1393989..2202fe5 100644
--- a/submodules/PulseRain_MCU/PulseRain_processor_core/source/RV2T_mm_reg.v
+++ b/submodules/PulseRain_MCU/PulseRain_processor_core/source/RV2T_mm_reg.v
@@ -58,6 +58,7 @@ module RV2T_mm_reg (
     //=======================================================================
         output  reg                                                     enable_out,
         output  wire  [`XLEN - 1 : 0]                                   word_out,
+        output  reg   [31 : 0]                                            gpio_out,
         
         output  wire                                                    timer_triggered
 );
@@ -118,8 +119,20 @@ module RV2T_mm_reg (
         assign tx_data = data_write_word [7 : 0];
         
             
-        assign word_out = (data_rw_addr_d1 == `UART_TX_ADDR) ? {tx_active, 31'd0} : machine_timer_data_out;
+        assign word_out = (data_rw_addr_d1 == `UART_TX_ADDR) ? {tx_active, 31'd0} : 
+                          ((data_rw_addr_d1 == 3'b101) ? {gpio_out} : machine_timer_data_out);
         
+    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    // GIIO
+    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+        // assign gpio_write = ((data_rw_addr == 3'b101) & data_write_enable) ? 1'b1 : 1'b0;
+        always @(posedge clk or negedge reset_n)begin
+            if(~reset_n)
+                gpio_out <= 8'h0;
+            // else if(gpio_write)
+            else if(((data_rw_addr == 3'b101) & data_write_enable) ? 1'b1 : 1'b0)
+                gpio_out <= data_write_word[7 : 0];
+        end
         
         
 endmodule
diff --git a/submodules/PulseRain_MCU/source/PulseRain_RV2T_MCU.v b/submodules/PulseRain_MCU/source/PulseRain_RV2T_MCU.v
index 5883cbf..ae0a23c 100644
--- a/submodules/PulseRain_MCU/source/PulseRain_RV2T_MCU.v
+++ b/submodules/PulseRain_MCU/source/PulseRain_RV2T_MCU.v
@@ -60,6 +60,7 @@ module PulseRain_RV2T_MCU (
     //=====================================================================
         input   wire                                            start,
         input   wire  [`PC_BITWIDTH - 1 : 0]                    start_address,
+        output  wire  [31 : 0]                                  gpio_out,
         
         output  wire                                            processor_paused,
 
@@ -113,6 +114,7 @@ module PulseRain_RV2T_MCU (
                 .ocd_reg_we (ocd_reg_we),
                 .ocd_reg_write_addr (ocd_reg_write_addr),
                 .ocd_reg_write_data (ocd_reg_write_data),
+                .gpio_out(gpio_out),
 
                 .start_TX  (start_TX),
                 .tx_data   (tx_data),
  • 修改约束文件 build/par/constraints/Lattice/UPDuinoV2/Reindeer.ldc 中的管脚对应核心板上的四个 LED 灯并修改串口 Tx/Rx 的管脚映射:
#ldc_set_location -site {35} [get_ports osc_in]
#ldc_set_location -site {43} [get_ports reset_button]
ldc_set_location -site {41} [get_ports REDn]
ldc_set_location -site {40} [get_ports BLUn]
ldc_set_location -site {39} [get_ports GRNn]
ldc_set_location -site {26} [get_ports TXD]
ldc_set_location -site {27} [get_ports RXD]
ldc_set_location -site {16} [get_ports spi_ss]
ldc_set_location -site {48} [get_ports LED1]
ldc_set_location -site {47} [get_ports LED2]
ldc_set_location -site {46} [get_ports LED3]
ldc_set_location -site {45} [get_ports LED4]



create_clock -name {osc_i/CLKHF} -period 41.6667 [get_pins {osc_i/CLKHF }]  
  • 修改工程的 Bitstream 导出格式为 Raw Bit File:

FnLF6-zNvzOyeJH7u8AA-3UowhsV

  • 然后执行综合、布线,生成 rbt 文件,FPGA 资源占用报告见下图:

Ft2x18TjsifusYPrpVMAoIy6Vqaq

  • 将导出的 rbt 文件直接拷贝到名为 step 的 U 盘,实现下载 bitfile 到 核心板上的 spi flash;
  • 下载 RSICV 工具链 gnu-mcu-eclipse-riscv-none-gcc-7.2.0-1-20171109-1926-win64-setup.exe,下载地址 https://gnu-mcu-eclipse.github.io/toolchain/riscv/ ,安装后在系统环境变量PATH 中增加相应的工具链的路径:

FibhFcpmFn-7cQlv8ZjbqcO3fAyN

  • 同时,为了实现通过 makefile 编译程序,可以通过安装 mingw 工具,然后调用 mingw-make 程序,相应地,也需要将 mingw 程序执行路径添加到 PATH 环境变量,如上图所示;
  • 修改 Reindeer 仓库中提供的 software/makefile 例程,在例程中添加流水灯控制代码并执行 make 进行编译,生成一个 step.elf 文件:
xxx@xxx MINGW64 /e/lattice/Reindeer-master/software/makefile
$ mingw32-make.exe
===> Building main.o
============> Building Dependency
============> Generating OBJ
----------------------------------------------------------------------------
====> Linking step.elf
===> Dumping sections for all
  • 连接一个串口到核心板上 FPGA 映射的 UART Tx/Rx 管脚(连接见下图),使用 Reindeer 工程内提供的 python script,通过 Reindeer onchip-debugger 下载程序 elf 文件到软核上执行:

备注:

由于未知原因,我发现需要先下载 Reindeer 提供elf文件,然后再下载我编译的step.elf文件,程序才能正常运行,可能是有vector相关的问题导致:

E:\lattice\Reindeer-master\scripts>python reindeer_config.py --port=COM17 --reset --image=E:\lattice\Reindeer-master\software\makefile\step.elf --console_enable --run
===============================================================================
# Copyright (c) 2018, PulseRain Technology LLC
# Reindeer Configuration Utility, Version 1.0
===============================================================================
baud_rate  =  115200
com_port   =  COM17
toolchain  =  riscv-none-embed-
===============================================================================
Reseting CPU ...
Loading  E:\lattice\Reindeer-master\software\makefile\step.elf
_start 80000000

//================================================================
//== Section  .text
//================================================================
        addr = 0x80000000, length = 2936 (0xb78)

//================================================================
//== Section  .rodata
//================================================================
        addr = 0x80000b78, length = 157 (0x9d)

//================================================================
//== Section  .eh_frame
//================================================================
        addr = 0x80000c18, length = 4 (0x4)

//================================================================
//== Section  .init_array
//================================================================
        addr = 0x80001000, length = 12 (0xc)

//================================================================
//== Section  .fini_array
//================================================================
        addr = 0x8000100c, length = 4 (0x4)

//================================================================
//== Section  .data
//================================================================
        addr = 0x80001010, length = 1064 (0x428)

//================================================================
//== Section  .sdata
//================================================================
        addr = 0x80001438, length = 4 (0x4)

===================> start the CPU, entry point = 0x80000000

=== PulseRain Reindeer running on ICE40UP5K ===

...

ALL LEDs -> OFF

...

- LED1 -> ON
- LED2 -> ON
- LED3 -> ON
- LED4 -> ON
- LED4 -> OFF
- LED3 -> OFF
- LED2 -> OFF
- LED1 -> OFF

FniZ0ZCeMeQNPlQJTUzYWzULnK1G

五、代码

  1. RTL的修改见上一章节;
  2. 流水灯程序代码如下:
volatile uint32_t* const REG_GPIO_OUT = (uint32_t*) 0x20000014;

void delayMs(unsigned int ms)
{
    unsigned int _startMillis;

    _startMillis = millis();
    while(millis() - _startMillis < ms);
}

int main()
{
    // int t = 0;
    Serial.println("=== PulseRain Reindeer running on ICE40UP5K ===\n");

    Serial.println("...\n");
    (*REG_GPIO_OUT) &= ~0xF;
    Serial.println("ALL LEDs -> OFF\n");
    Serial.println("...\n");

    while(1) {
        // t = t - cnt;
        // t = t + foo();
        // Serial.println("xxxxxx  ddddd");
      
        // myprint ("ssss    %d    y  iiiiiuk\n", t);

        // myprint("REG_MTIME_LOW: %d\n", (*REG_MTIME_LOW));

        for (int i = 0; i < 4; i++) {
            (*REG_GPIO_OUT) |= (1 << i);

            myprint ("- LED%d -> ON\n", i + 1);

            delayMs(500);
        }

        for (int i = 3; i >= 0; i--) {
            (*REG_GPIO_OUT) &= ~(1 << i);

            myprint ("- LED%d -> OFF\n", i + 1);

            delayMs(500);
        }
    }

    return 0;
}

 

六、功能展示

  • 串口输出信息
=== PulseRain Reindeer running on ICE40UP5K ===

...

ALL LEDs -> OFF

...

- LED1 -> ON
- LED2 -> ON
- LED3 -> ON
- LED4 -> ON
- LED4 -> OFF
- LED3 -> OFF
- LED2 -> OFF
- LED1 -> OFF
- LED1 -> ON
- LED2 -> ON
- LED3 -> ON
- LED4 -> ON
- LED4 -> OFF
- LED3 -> OFF
- LED2 -> OFF
- LED1 -> OFF
- LED1 -> ON
- LED2 -> ON
- LED3 -> ON
- LED4 -> ON
- LED4 -> OFF
- LED3 -> OFF
- LED2 -> OFF
- LED1 -> OFF
- LED1 -> ON
- LED2 -> ON
- LED3 -> ON
- LED4 -> ON
- LED4 -> OFF
- LED3 -> OFF
- LED2 -> OFF
- LED1 -> OFF
- LED1 -> ON
- LED2 -> ON
- LED3 -> ON
- LED4 -> ON
- LED4 -> OFF
- LED3 -> OFF
- LED2 -> OFF
- LED1 -> OFF
- LED1 -> ON
- LED2 -> ON
- LED3 -> ON
  • 流水灯效果见视频

八、未来计划

未来计划能够把扩展板上的ADC/DAC用起来,尝试示波器等功能。

附件下载
ice40_code.zip
Reindeer 代码、bitfile,以及程序源码和elf文件
团队介绍
在职工程师
团队成员
topgear
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号