1.队伍介绍
本参赛队队名为“第一次参赛队”,报名编号:CICC2483。本篇为蜂鸟E203系列分享第一篇。
本篇着重介绍HbirdV2跨平台移植细节,该内容较为基础,欢迎大家交流。
2.调试器选型
1. 芯来科技官方调试器Humming Debugger
该调试器为官方提供调试器,较为权威,价格较高(199元),如若必需,建议申请板卡使用。
2. sipeed核心板调试器
该调试器价格较低,与官方调试器作用一致,较为推荐该款调试器。
注:官方调试器Uart0_RX与软核Uart0_RX相连即可,Uart0_TX亦然;
第三方调试器串口与正常串口模块一致,即调试器TX与软核Uart0_RX相连。
以上两个调试器相差不大,唯一的几个区别在电脑中只有设备名字的区别而已,在使用的时候需要对openocd的cfg文件中将ftdi设备名改为相应的调试器名字即可(后续的帖子中会说到)
3.板子选型
Xilinx平台:Nexys 4与Basys 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
图1:Hummingbirdv2 E203架构
浅谈HbirdV1与HbirdV2区别与联系
FPGA: 1°简化system.v结构,将32组GPIO简化为GPIOA[31:0]
2°添加GPIOB[31:0]
3°新增外设:Uart2、I2C1、PWM3等
4°新增仿真工具(iVerilog)和Wave Viewer(GTKWave)
5°新增NICE(Nuclei指令单元扩展)协处理器单元
SDK
: 芯来最新版的SDK中支持了HbirdV1和HbirdV2两个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.388MHz和16MHz,复位为低复位。
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.v(Xilinx平台无需添加)
其代码如下
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.v第52行添加如下代码
initial
begin
(*rom_style=”block”)$readmemh(“code.hex”,mem_r);
end
$readmemh是Verilog 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板卡添加扩展板,可以使用W25Q64、W25Q128、W25Q256等。
转接板原理图
转接板PCB图
QSPI0连接DTCM,使用Single-SPI协议即可固化程序,故引出qspi0[0],QSPI0[1]即可。引出QSPI0[2],QSPI0[3]后,为Quad-SPI协议,可提高传输速度。将QSPI0[2],QSPI0[3]直接拉高,对代码烧录影响较小,而且Basys3的IO资源较少,可以节省两个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/