RISC-V MCU中文社区

【分享】 【分享】HbirdV2跨平台移植教程

发表于 全国大学生集成电路创新创业大赛 2021-04-24 21:51:39
1
226
15

1.队伍介绍

        本参赛队队名为“第一次参赛队”,报名编号:CICC2483。本篇为蜂鸟E203系列分享第一篇。

        本篇着重介绍HbirdV2跨平台移植细节,该内容较为基础,欢迎大家交流。

2.调试器选型

1. 芯来科技官方调试器Humming Debugger

该调试器为官方提供调试器,较为权威,价格较高(199元),如若必需,建议申请板卡使用。

2. sipeed核心板调试器

该调试器价格较低,与官方调试器作用一致,较为推荐该款调试器。

注:官方调试器Uart0_RX与软核Uart0_RX相连即可,Uart0_TX亦然;

第三方调试器串口与正常串口模块一致,即调试器TX与软核Uart0_RX相连。

以上两个调试器相差不大,唯一的几个区别在电脑中只有设备名字的区别而已,在使用的时候需要对openocdcfg文件中将ftdi设备名改为相应的调试器名字即可(后续的帖子中会说到)

3.板子选型

        Xilinx平台:Nexys 4Basys 3

        Intel 平台:第四届集创赛ARM杯官方指定板(安芯教育板)

        国产平台 :安路平台

        从赛题发布到现在,笔者从安路平台→Intel平台→Basys 3 平台→Nexys 4 平台,为来第五次将移植到Xilinx自制板卡,移植期间问题种种,较为心酸。

        总结如上平台代码,推荐各参赛队由于软硬件联调的不便性,建议使用两块板卡分别调试,故跨平台移植软核就显得尤为重要。由于赛题限定使用Xilinx平台,且软核框架平台兼容性较好,故建议使用Xilinx平台。

        Intel平台请参考友晶科技T-core软核移植教程。

        安路平台参考荔枝糖移植教程。

        笔者本文使用Xilinx平台 Basys 3,其系列为Artix-7 35T,资源占用约53%,其IO资源较少,足够前期学习使用。

        注:友晶科技所使用的T-core代码,官方使用时,将一些调试引脚进行硬件上拉,故在此平台移植时,其软件代码需注意该问题。

Verilog代码通用性较好,可将其代码直接移植其他平台,上述教程仅供参考,参照下文E203移植方式,亦可在其他平台顺利移植。

4.开始移植

1.  git HbirdV2


1Hummingbirdv2 E203架构

浅谈HbirdV1HbirdV2区别与联系

FPGA:   1°简化system.v结构,将32GPIO简化为GPIOA[310]

2°添加GPIOB[310]

3°新增外设:Uart2I2C1PWM3

4°新增仿真工具(iVerilog)和Wave ViewerGTKWave

5°新增NICENuclei指令单元扩展)协处理器单元

SDK :     芯来最新版的SDK中支持了HbirdV1HbirdV2两个cpu,且将板级支持包改为芯来官方的HAL库:NMSIS库,同时在sdk中加入了常用的RTOS操作系统如RT-thread等的适配,方便用户使用。

注:虽然V1版本GPIO地址与V2版本GPIOA地址一致,经测试其代码不可通用。

2* vivado新建工程(注意非中文路径)


2:新建工程

 



3* 添加文件

1°在E:\HBirdV2_Basys3下新建文件夹src,即对应路径为E:\HBirdV2_Basys3\src

2°在E:\HBirdV2_Basys3下新建文件夹tb,即对应路径为E:\HBirdV2_Basys3\tb

git的源文件中

3°E:\e203_hbirdv2-master\tb\tb_top.v添加到E:\HBirdV2_Basys3\tb路径下

4°E:\e203_hbirdv2-master\rtl路径整体添加到E:\HBirdV2_Basys3\src路径下

5°E:\e203_hbirdv2-master\fpga\mcu200t\src\system.v添加到E:\HBirdV2_Basys3\src路径下

6°在E:\HBirdV2_Basys3\src下新建文件clkdivider.v

其代码内容如下:


// Divide clock by 256, used to generate 32.768 kHz clock for AON block

module clkdivider
(
  input wire clk,
  input wire reset,
  output reg clk_out
);

  reg [7:0] counter;

  always @(posedge clk)
  begin
    if (reset ==0 )
    begin
      counter <= 8'd0;
      clk_out <= 1'b0;
    end
//  Bob: this original source code is wrong, because it is actually divided clock by 512, so correct it
    //else if (counter == 8'hff)
    else if (counter == 8'h7f
    begin
      counter <= 8'd0;
      clk_out <= ~clk_out;
    end
    else
    begin
      counter <= counter+1;
    end
  end
endmodule


7°对应添加综合文件

 


添加综合文件


添加文件夹


    4* 添加约束文件

 

添加约束文件


选择Create File


新建约束文件(.xdc


完成添加操作

(新建的引脚约束文件在E:\HBirdV2_Basys3\HBirdV2_Basys3.srcs\constrs_1\new文件夹下)

Basys 3.xdc 约束文件中输入下述参考代码

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets IOBUF_jtag_TCK/O]
set_property IOSTANDARD LVCMOS33 [get_ports mcu_TCK]
set_property IOSTANDARD LVCMOS33 [get_ports mcu_TDI]
set_property IOSTANDARD LVCMOS33 [get_ports mcu_TMS]
set_property IOSTANDARD LVCMOS33 [get_ports mcu_wakeup]
set_property IOSTANDARD LVCMOS33 [get_ports ck_rst_low]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[31]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[30]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[29]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[28]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[27]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[26]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[25]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[24]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[23]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[22]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[21]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[20]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[19]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[18]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[17]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[16]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[15]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[14]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[13]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[12]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioA[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[31]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[30]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[29]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[28]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[27]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[26]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[25]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[24]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[23]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[22]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[21]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[20]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[19]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[18]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[17]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[16]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[15]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[14]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[13]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[12]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpioB[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {qspi0_dq[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {qspi0_dq[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {qspi0_dq[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {qspi0_dq[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports CLK100MHZ]
set_property IOSTANDARD LVCMOS33 [get_ports mcu_TDO]
set_property IOSTANDARD LVCMOS33 [get_ports pmu_paden]
set_property IOSTANDARD LVCMOS33 [get_ports pmu_padrst]
set_property IOSTANDARD LVCMOS33 [get_ports qspi0_cs]
set_property IOSTANDARD LVCMOS33 [get_ports qspi0_sck]
set_property PACKAGE_PIN A17 [get_ports qspi0_sck]
set_property PACKAGE_PIN A14 [get_ports qspi0_cs]
set_property PACKAGE_PIN L17 [get_ports mcu_TDO]
set_property PACKAGE_PIN W5 [get_ports CLK100MHZ]
set_property PACKAGE_PIN U18 [get_ports ck_rst_low]
set_property PACKAGE_PIN M18 [get_ports mcu_TMS]
set_property PACKAGE_PIN M19 [get_ports mcu_TDI]
set_property PACKAGE_PIN K17 [get_ports mcu_TCK]
set_property PACKAGE_PIN C15 [get_ports {qspi0_dq[0]}]
set_property PACKAGE_PIN A16 [get_ports {qspi0_dq[1]}]
###                   SW_6-SW15                   ###
set_property PACKAGE_PIN W14 [get_ports {gpioA[0]}]
set_property PACKAGE_PIN W13 [get_ports {gpioA[1]}]
set_property PACKAGE_PIN V2 [get_ports {gpioA[2]}]
set_property PACKAGE_PIN T3 [get_ports {gpioA[3]}]
set_property PACKAGE_PIN T2 [get_ports {gpioA[4]}]
set_property PACKAGE_PIN R3 [get_ports {gpioA[5]}]
set_property PACKAGE_PIN W2 [get_ports {gpioA[6]}]
set_property PACKAGE_PIN U1 [get_ports {gpioA[7]}]
set_property PACKAGE_PIN T1 [get_ports {gpioA[8]}]
set_property PACKAGE_PIN R2 [get_ports {gpioA[9]}]
###                     UART_0                     ###
set_property PACKAGE_PIN N17 [get_ports {gpioA[16]}]
set_property PACKAGE_PIN P17 [get_ports {gpioA[17]}]
###                  JA_1 -  JA_4                  ###
set_property PACKAGE_PIN J1 [get_ports {gpioB[0]}]
set_property PACKAGE_PIN L2 [get_ports {gpioB[1]}]
set_property PACKAGE_PIN J2 [get_ports {gpioB[2]}]
set_property PACKAGE_PIN G2 [get_ports {gpioB[3]}]
###                 JA_7 -  JA_10                  ###
set_property PACKAGE_PIN H1 [get_ports {gpioB[4]}]
set_property PACKAGE_PIN K2 [get_ports {gpioB[5]}]
set_property PACKAGE_PIN H2 [get_ports {gpioB[6]}]
set_property PACKAGE_PIN G3 [get_ports {gpioB[7]}]
###                 JC_4    JC_10                  ###
set_property PACKAGE_PIN P18 [get_ports {gpioB[8]}]
set_property PACKAGE_PIN R18 [get_ports {gpioB[9]}]
###                LED_0    LED_15                 ###
set_property PACKAGE_PIN U16 [get_ports {gpioB[10]}]
set_property PACKAGE_PIN E19 [get_ports {gpioB[11]}]
set_property PACKAGE_PIN U19 [get_ports {gpioB[12]}]
set_property PACKAGE_PIN V19 [get_ports {gpioB[13]}]
set_property PACKAGE_PIN W18 [get_ports {gpioB[14]}]
set_property PACKAGE_PIN U15 [get_ports {gpioB[15]}]
set_property PACKAGE_PIN U14 [get_ports {gpioB[16]}]
set_property PACKAGE_PIN V14 [get_ports {gpioB[17]}]
set_property PACKAGE_PIN V13 [get_ports {gpioB[18]}]
set_property PACKAGE_PIN V3 [get_ports {gpioB[19]}]
set_property PACKAGE_PIN W3 [get_ports {gpioB[20]}]
set_property PACKAGE_PIN U3 [get_ports {gpioB[21]}]
set_property PACKAGE_PIN P3 [get_ports {gpioB[22]}]
set_property PACKAGE_PIN N3 [get_ports {gpioB[23]}]
set_property PACKAGE_PIN P1 [get_ports {gpioB[24]}]
set_property PACKAGE_PIN L1 [get_ports {gpioB[25]}]
###                   SW_0 - SW5                  ###
set_property PACKAGE_PIN V17 [get_ports {gpioB[26]}]
set_property PACKAGE_PIN V16 [get_ports {gpioB[27]}]
set_property PACKAGE_PIN W16 [get_ports {gpioB[28]}]
set_property PACKAGE_PIN W17 [get_ports {gpioB[29]}]
set_property PACKAGE_PIN W15 [get_ports {gpioB[30]}]
set_property PACKAGE_PIN V15 [get_ports {gpioB[31]}]



修改e203_defines.v文件类型


将文件设置为Verilog Header类型

5* 添加ip

    1°配置全局时钟clk_wiz ,在IP Catalog中搜索clocking wizard,并将其名称修改为mmcm。选择类别混合模式时钟MMCM,在clk_in1处输入100MHz,输出8.388MHz16MHz,复位为低复位。




 

    2°配置全局复位reset_sys,在IP Catalog中搜索Processor System Reset,并将其名称修改为reset_sys


6* 修改system.v

    1°  注释语句第6-9

//input wire CLK32768KHZ,//RTC_CLK-Y18
//input wire fpga_rst,   //FPGA_RESET-T6
//input wire mcu_rst,    //MCU_RESET-P20

    2°在原第10行追加

input wire ck_rst_low,              //复位按键翻转

    3° 将原第107

assign ck_rst = fpga_rst & mcu_rst;

修改为

assign ck_rst = ~ck_rst_low;    //FPGA与MCU同时复位

    4°  将原第98-105行中mmcm例化部分

mmcm ip_mmcm
  (
    .resetn(ck_rst),
    .clk_in1(CLK100MHZ),
   
    .clk_out2(clk_16M), // 16 MHz, this clock we set to 16MHz
    .locked(mmcm_locked)
  );

        修改为

  mmcm ip_mmcm
  (
    .resetn(ck_rst),
    .clk_in1(CLK100MHZ),
    .clk_out1(clk_8388),
    .clk_out2(clk_16M), // 16 MHz, this clock we set to 16MHz
    .locked(mmcm_locked)
  );

    5°在原第95行添加

wire CLK32768KHZ;

    6°在原第124行位置新增clkdivider例化代码

clkdivider low_clk
(
  .clk      (clk_8388),
  .reset    (ck_rst),
  .clk_out  (CLK32768KHZ)
);

    7°在E:\HBirdV2_Basys3\src下新建文件bitstream.tcl

其代码为:

set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks RTSTAT-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]

Settings中找到Bitstream bitstream.tcl文件添加至tcl.pre


为方便FPGA代码固化,此处勾选-bin_file选项(勾选后将自动生成.bin固化代码)

7* 注意事项

1°不同平台原语不同,故安路等平台需添加代码iobuf.vXilinx平台无需添加)

其代码如下

module IOBUF(
    input I,
    input T,
    output O,
    inout IO
);
 
  assign IO = ~T ? I:1'bz;
  assign O = IO;
 
endmodule

2°由于其他平台没有对应全局复位代码,移植时可参考如下代码reset_sys.v

module reset_sys (
    input slowest_sync_clk,
    input ext_reset_in,
    input aux_reset_in,//Not used
    input mb_debug_sys_rst,//Not used
    input dcm_locked,
    output mb_reset,// Not used
    output bus_struct_reset,// Not used
    output peripheral_reset,
    output interconnect_aresetn,// Not used
    output peripheral_aresetn// Not used
);

wire clk = slowest_sync_clk;
wire rst_n = ext_reset_in;
reg record_rst_r;

// When the peripheral_reset is really asserted, then we can clear the record rst
wire record_rst_clr = peripheral_reset;
 
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
       record_rst_r <= 1'b1;
    end
    else if (record_rst_clr) begin
       record_rst_r <= 1'b0;
    end
end

reg gen_rst_r;
// When the locked and the record_rst is there, then we assert the gen_rst
wire gen_rst_set = dcm_locked & record_rst_r;
// When the gen_rst asserted with max cycles, then we de-assert it
wire gen_rst_cnt_is_max;
wire gen_rst_clr = gen_rst_r & gen_rst_cnt_is_max;
 
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
       gen_rst_r <= 1'b0;
    end
    else if (gen_rst_set) begin
       gen_rst_r <= 1'b1;
    end
    else if (gen_rst_clr) begin
       gen_rst_r <= 1'b0;
    end
end
 
assign peripheral_reset = gen_rst_r;
 
reg[9:0] gen_rst_cnt_r;
// When the gen_rst is asserted, it need to be clear
wire gen_rst_cnt_clr = gen_rst_set;
// When the gen_rst is asserted, and the counter is not reach the max value
wire gen_rst_cnt_inc = gen_rst_r & (~gen_rst_cnt_is_max);
 
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
       gen_rst_cnt_r <= 10'b0;
    end
    else if (gen_rst_cnt_clr) begin
       gen_rst_cnt_r <= 10'b0;
    end
    else if (gen_rst_cnt_inc) begin
       gen_rst_cnt_r <= gen_rst_cnt_r + 1'b1;
    end
end
 
assign gen_rst_cnt_is_max = (gen_rst_cnt_r == 10'd256);
 
endmodule

3°注意Digilent的按键未按下时为低电平(因此上述修改时将复位进行翻转,若按键状态与之相反,则无需将其复位输入信号进行翻转)

5.代码烧录

1* RAM烧录,直接选中右键烧录即可,其代码掉电时会消失

2*添加相应flash,由于使用Basys 3 平台,故添加其flash芯片型号——S25FL032

下载烧录,选择system.bin即可。

6.软核代码烧录

1* FPGA代码烧录相似,可以在IDE中直接在ITCM指令空间中下载烧录,掉电依旧会消失

2* 代码固化

        1°在ITCM中添加如下代码:(由于FPGA综合时间相较于直接烧录时间较长、效率极低,故未验证,仅提供思路)

E:/HBirdV2_Basys3/src/rtl/e203/general/sirv_sim_ram.v52行添加如下代码

initial
    begin
        (*rom_style=”block”)$readmemh(“code.hex”,mem_r);
    end


$readmemhVerilog HDL提供的系统任务,用于从文本文件中读取数据,并将其加载到指定的存储器中,该系统任务要求以十六进制存放数据文件。其格式为

其中:

1)task_name,用于指定系统任务,为$readmemb$readmemh

2)file_name,为读出数据的文件名

3)memory_name,为要读入数据的存储器名字

4)start_addr,为存储器的起始地址,实际就是建模存储器数组的索引值

5)end_addr,为存储器的结束地址,实际就是建模存储器数组的索引值

注:在$readmemh前面加入(*rom_style=”block”)Verilog HDL中的rom_style属性声明,用于指导Vivado工具使用FPGA内的块存储器实现该存储器,而不是使用FPGA内的分布式存储器实现。

2°在FPGA板卡添加扩展板,可以使用W25Q64W25Q128W25Q256等。


转接板原理图


转接板PCB


QSPI0连接DTCM,使用Single-SPI协议即可固化程序,故引出qspi0[0]QSPI0[1]即可。引出QSPI0[2]QSPI0[3]后,为Quad-SPI协议,可提高传输速度。将QSPI0[2]QSPI0[3]直接拉高,对代码烧录影响较小,而且Basys3IO资源较少,可以节省两个IO,故未引出QSPI0[2]QSPI0[3]

6.小结

由于不同平台差异性较大,需参考其原理图,熟悉其硬件特性后移植,以免因硬件兼容问题使得软核移植失败。

能力有限,难免出现疏漏,海涵。

预祝各参赛队比赛顺利!

 

芯来科技调试器链接:https://item.taobao.com/item.htm?spm=2013.1.w4004-20437802402.5.15dc4563mVuUa5&id=580813056318

第三方调试器链接 :https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-21410578033.19.20cc2db2xBRI5D&id=595953803239

HbirdV2 github链接:https://github.com/riscv-mcu/e203_hbirdv2

Hummingbirdv2 E203内核和SoC使用手册:https://doc.nucleisys.com/hbirdv2/

喜欢15
用户评论 (1)
  • 再胖十斤

    2021-04-27 17:58:40 再胖十斤 1#

    大佬,知道出现这样时序违例是怎么回事吗,我时钟输入配置的是50MHZ

关于作者
youngsea

youngsea 未通过人工认证

懒的都不写签名

问答
粉丝
1
关注
0
  • 早春营|RISC-V处理器嵌入式开发
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
早春营|RISC-V处理器嵌入式开发