蜂鸟E203 hbirdv2项目复现与移植到ZYNQ7020及构建FPGA工程【基于Windows】
蜂鸟E203 hbirdv2项目复现与移植到ZYNQ7020及构建FPGA工程【基于Windows】
前言
蜂鸟E203是由芯来科技推出的开源RISC-V处理器核,它遵循RV32IMC指令集架构,专为嵌入式场景设计,具有低功耗、小面积(2级流水线,核心面积仅0.02mm²@28nm)、高可配置性等特点。
蜂鸟v2 E203 RISC-V 处理器内核和 SoC 由中国大陆领先的 RISC-V IP 和解决方案公司 Nuclei System Technology 开发和开源。它是在 SI-RISCV/e200_opensource 中维护的蜂鸟 E203 项目的升级版,因此我们称之为 Hummingbirdv2 E203 (HBirdv2 E203)。Hummingbirdv2 E203 的系统架构如下图所示。
开发环境
-
硬件工具:ZYNQ Z7-020开发板(XC7Z020CLG400-2)、蜂鸟官方JTAG调试器、相关连线
-
软件工具:Vivado 2018.3、Nuclei Studio任意版本
-
操作系统:Windows11
-
工程源码:E203_ZYNQ_FPGA
移植思路
通过对比原Nuclei开发板(Artix-7)与ZYNQ7020的硬件差异,重点解决以下关键问题:
-
时钟架构重构:利用ZYNQ PS端PLL生成系统时钟
-
管脚约束设置:需要根据自己的开发板管脚定义重新设置约束文件
-
JTAG调试:需要自行连接riscv调试器进行调试
1. 准备工作
1.1 E203工程获取
E203官方项目地址:e203_hbirdv2,在Github下载官方项目文件,其中,我们只需要用到的是rtl/e203下的RTL源码,以及tb下的仿真文件。
当热,也可以直接进入E203_ZYNQ_FPGA ,其中已经上传了这个移植项目的所有RTL代码、约束文件、仿真文件以及Vivado工程文件。
然后,安装好该项目需要用到的必要工具,即 Vivado、Nuclei Studio,具体教程如下:
Vivado18.3的安装 安装教程_vivado安装教程2018.3-CSDN博客
NucleiStudio的快速上手 - RISC-V MCU文档中心
1.2 E203_ZYNQ_FPGA项目源码目录结构
├── E203_RTL/ - Core RTL & Top-level Integration / 核心RTL与顶层集成
│ ├── core/ - E203 Processor Core (Pipeline, ALU, CSR) / 处理器核源码(流水线、ALU、CSR等)
│ ├── system.v - Top-level System Module / 系统顶层模块
│ └── clkdivider.v - ZYNQ Clock Divider / ZYNQ时钟分频模块
│
├── E203_SIM/ - Simulation Environment / 仿真环境
│ └── e203_tb_top.v - System Testbench / 系统测试平台
│
├── E203_Zynq_7020/ - Vivado Hardware Project / Vivado硬件工程
│
├── SDK/ - Nuclei Studio SDK Projects / Nuclei Studio软件开发工程
│
└── XDC/ - Physical Constraints / 物理约束
└── e203_zynq_7020.xdc - ZYNQ7020 Pin Assignment / 引脚分配文件
1.3 创建vivado工程并导入E203核的RTL代码
新建vivado项目,根据自己的开发板型号选择部件,然后将E203_RTL目录下的全部RTL代码都导入到源文件(采用 Add Directories 的方式引入整个文件夹)
导入代码后vivado可以自动识别顶层代码system.v,也可以手动设置:
2. 系统顶层文件及相关设置
导入RTL代码后,还需要进行一些设置,具体如下:
2.1 时钟设计
E203 MCU SoC 的两个输入时钟定义如下:
input wire CLK100MHZ,//GCLK-W19
input wire CLK32768KHZ,//RTC_CLK-Y18
而ZYNQ7020开发板是50MHz的时钟源,所以要先用一个锁相环(ZYNQ自带的IP)将50MHz时钟源降频为16MHz ,但是32.768KHz由于频率过低,无法通过此IP实现,所以得自己写分频器实现(分频代码在E203_RTL/clkdivider.v)。
实现方案:
- 通过一个PLL锁相环(ZYNQ自带的IP)将50MHz时钟源降频为 16MHz
- 设计分频模块将16MHz分频生成 32.768KHz
顶层代码中具体的实例化如下:
// 实例化PLL
PLL sys_clk_gen
(
.resetn (ck_rst ), // ck_rst is active low
.CLK_I_50M (CLK50MHZ ),
.CLK_O_16M (clk_16M ), // 16 MHz, this clock we set to 16MHz
// .CLK_O_8M388608 (clk_8M388608 ),
.locked (mmcm_locked)
);
// 实例化clkdivider
clkdivider rtc_clk_gen(
.clk (clk_16M ),//generate 32.768KHz
.resetn (ck_rst),
.clk_out (CLK32768KHZ)
);
手写的分频器是将16MHz分频生成 32.768KHz,具体如下:
module clkdivider
(
input wire clk,
input wire resetn,
output reg clk_out
);
reg [7:0] counter;
always @(posedge clk)
begin
if (!resetn)
begin
counter <= 8'd0;
clk_out <= 1'b0;
end
//else if (counter == 8'hff)
else if (counter == 8'd243)
begin
counter <= 8'd0;
clk_out <= ~clk_out;
end
else
begin
counter <= counter+1;
end
end
endmodule
vivado中需要通过IP Catalog设置相应的IP(Clocking Wizard),修改name以及输入输出端口,设置低电平复位,具体如下:
然后在管脚约束中将 CLK50MHZ 绑定到开发板的PL时钟源管脚 PL_GCLK 即可。
2.2 复位设置
E203 MCU SoC 的复位信号定义如下,即FPGA开发板自身的复位和为MCU设置的复位,这两个复位通过一个与操作生成一个为SoC使用的复位信号,即SoC复位的条件是这两个复位信号至少有一个生效。
input wire fpga_rst,//FPGA_RESET-T6
input wire mcu_rst,//MCU_RESET-P20
由于这两个复位信号都是低电平有效,故分别绑定到开发板自身的 RESET 和 KEY0 按键即可,其中RESET 是FPGA自身的复位键,而KEY0 是为MCU设置的复位键(注意开发板上的按键要满足按下去为0,松开为1,即低电平有效的要求。
system.v中的 reset_sys ip_reset_sys 实例化代码无需修改,同样在vivado中设置IP,注意修改name:
2.3 QSPI Flash接口映射
蜂鸟E203 有三个QSPI接口,分别为QSPI0、QSPI1、QSPI2,其中QSPI0专门是为外部FLASH准备的,而其余两个都是通过GPIO口复用进行使用。所以rtRTL顶层需要绑定的是专为外部FLASH准备的QSPI接口。但是ZYNQ只有一个FLASH,没有外部的接口,所以先注释掉QSPI,不采用FLASH启动而是ILM(ROM)方式。
// Dedicated QSPI interface
// output wire qspi0_cs,
// output wire qspi0_sck,
// inout wire [3:0] qspi0_dq,
相应的,由于蜂鸟 E203 MCU SoC芯片顶层引脚中 io_pads_bootrom_n_i_ival 是用来配置上电地址选择的,即上电复位后处理器核从哪个地址开始上电执行,此信号为1时,处理器核从外部FLASH地址(0x2000_0000)开始执行,这也是默认的上电流程配置;而当此信号为0时,处理器核从内部 ROM 地址(0x0000_1000)开始执行,而 ROM 中存放的代码执行完后会跳转至ITCM(0x8000_0000)中继续执行。由于目前开发板采用ROM方式启动,所以需要将此信号配置为 0.
//==================model select===================
// 0:internal ROM (0x0000_1000)~0x0000_1FFFF (ITCM 0x8000_0000)
// 1:from QSPI_FLASH (0x2000_0000)
assign dut_io_pads_bootrom_n_i_ival = 1'b0; // set 0 to make it rom start either flash start
assign dut_io_pads_dbgmode0_n_i_ival = 1'b1;
assign dut_io_pads_dbgmode1_n_i_ival = 1'b1;
assign dut_io_pads_dbgmode2_n_i_ival = 1'b1;
2.4 PMU管脚即其他
蜂鸟E203的 PMU 用于控制主域电源,可以使主域被置于断电状态以节省功耗,或者重新唤醒。pmu_paden 用于MCU的电源指示,pmu_padrst 指示MCU的复位,这两个可以接开发板上的LED,而mcu_wakeup 用于唤醒,低电平有效,可以接开发板的按键。而32个GPIO和JTAG管脚绑定到开发板上的空闲IO口上即可,具体可以参考项目XDC / e203_zynq_7020.xdc 的引脚分配文件。
//pmu_wakeup
inout wire pmu_paden, //PMU_VDDPADEN M14
inout wire pmu_padrst, //PMU_VADDPARST M15
inout wire mcu_wakeup, //MCU_WAKE N16
// JD (used for JTAG connection)
// inout wire mcu_TDO, //MCU_TDO-W13
output wire mcu_TDO, //MCU_TDO-W13
inout wire mcu_TCK, //MCU_TCK-U13
inout wire mcu_TDI, //MCU_TDI-T11
inout wire mcu_TMS, //MCU_TMS-T12
2.5 设置全局文件
导入项目RTL代码、设置完相应IP后,vivado的Sources目录依旧报错,这时需要设置e203_defines.v为Global Include以及文件类型为Verilog Header:
同时,需要在该文件中加上`define FPGA_SOURCE宏定义:
3. 行为级仿真
在 E203_SIM / e203_tb_top.v 中已经写好了仿真的测试用例,在Sources中点击“+”号导入该仿真文件(注意选择的是 “Add or create simulation sources”):
仿真前需要在Nuclei Studio应用中新建工程,然后编译生成.verilog文件,在tb中进行调用,具体步骤如下:
3.1 Nuclei Studio新建工程
在1.1中已经完成了Nuclei Studio的安装,此时,进入应用中,新建一个helloworld工程:
然后设置编译的指令,在project -> properties -> c/c++ Bulid -> Settings -> Build Steps -> Post-bulid steps -> command 中添加命令:
riscv-nuclei-elf-objcopy -O verilog "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.verilog";sed -i 's/@800/@000/g' "${BuildArtifactFileBaseName}.verilog"; sed -i 's/@00002FB8/@00002000/g' "${BuildArtifactFileBaseName}.verilog";
默认的main主程序比较复杂,为了方便测试,可以修改简单一些:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include "hbird_sdk_soc.h"
int main(void)
{
for (int i = 0; i < 10; i ++) {
//printf("%d: Hello World From My risc-v\r\n",i);
printf("%d: Hello World In zynq 7020!!!\r\n",i);
}
return 0;
}
点击锤子按钮编译生成 .verilog文件:
然后将文件地址复制到e203_tb_top.v指定位置,tb中进行调用的具体代码是这里:
//省略前面的部分......
reg [7:0] itcm_mem [0:(`E203_ITCM_RAM_DP*8)-1];
initial begin
$readmemh({"C:/user/projects/hello_test.verilog"}, itcm_mem);
//$readmemh({""}, itcm_mem);
3.2 vivado仿真
在vivado中点击左侧菜单栏的 Run Simulation 进行行为级仿真,在 Tcl Console 可以观察到刚才的main函数的printf输出内容:
4. 上板验证
4.1 管脚约束
在Sources栏中添加约束文件,导入项目 XDC / e203_zynq_7020.xdc 的约束文件:
约束文件具体内容如下,可以自行参考开发板的说明文档进行设置:
set_property CFGBVS VCCO [current_design];
set_property CONFIG_VOLTAGE 3.3 [current_design];
##### create clock #####
set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { CLK50MHZ }]; # PL_GCLK
create_clock -add -name sys_clk_pin -period 20.00 -waveform {0 5} [get_ports {CLK50MHZ}];
#=========================== CLK32768KHZ ======================================#
#set_property -dict { PACKAGE_PIN Y18 IOSTANDARD LVCMOS33 } [get_ports { CLK32768KHZ }];
#create_clock -add -name sys_clk_pin -period 30517.58 -waveform {0 15258.79} [get_ports {CLK32768KHZ}];
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets dut_io_pads_jtag_TCK_i_ival];
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets IOBUF_jtag_TCK/O];
##### rst define #####
# set_property -dict { PACKAGE_PIN C7 IOSTANDARD LVCMOS33 } [get_ports { fpga_rst }]; # RESET PS_POR_B PS_POR_B_500 C7 (all keys: pull down is 0, low active)
set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { mcu_rst }]; # PL_KEY1 IO_L21P_T3_35 N15
#=========================== QSPI0 Flash ======================================#
##### spi0 define #####
# set_property PACKAGE_PIN A7 [get_ports qspi0_cs ]; # QSPI_CS PS_MIO1_500 A7
# set_property PACKAGE_PIN A5 [get_ports qspi0_sck ]; # QSPI_CLK PS_MIO6_500 A5
# set_property PACKAGE_PIN A6 [get_ports {qspi0_dq[3]}]; # QSPI_D3 PS_MIO5_500 A6
# set_property PACKAGE_PIN B7 [get_ports {qspi0_dq[2]}]; # QSPI_D2 PS_MIO4_500 B7
# set_property PACKAGE_PIN D6 [get_ports {qspi0_dq[1]}]; # QSPI_D1 PS_MIO3_500 D6
# set_property PACKAGE_PIN B8 [get_ports {qspi0_dq[0]}]; # QSPI_D0 PS_MIO2_500 B8
##### PMU define #####
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports pmu_paden ]; # PL_LED1 IO_L23P_T3_35 M14
set_property -dict { PACKAGE_PIN M15 IOSTANDARD LVCMOS33 } [get_ports pmu_padrst ]; # PL_LED2 IO_L23N_T3_35 M15
set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports mcu_wakeup ]; # PL_KEY2 IO_L21N_T3_35 N16
## GPIOA
##### MCU JTAG define #####
set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { mcu_TDO }]; # PIN27 EX_IO1_13N IO_L4N_T0_34 W13
set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { mcu_TCK }]; # PIN26 EX_IO1_12P IO_L3P_T0_34 U13
set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { mcu_TDI }]; # PIN32 EX_IO1_15P IO_L1P_T0_34 T11
set_property -dict { PACKAGE_PIN T12 IOSTANDARD LVCMOS33 } [get_ports { mcu_TMS }]; # PIN30 EX_IO1_14P IO_L2P_T0_34 T12
set_property KEEPER true [get_ports mcu_TMS]
#=============================== UART 0 ======================================
set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { uart0_tx }]; # PIN29 EX_IO1_14N IO_L2N_T0_34 U12
set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { uart0_rx }]; # PIN31 EX_IO1_15N IO_L1N_T0_34 T10
#---------------------------- End of UART 0 --------------------------------
#LEDs
set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { led3 }]; # PL_LED3---> IO_L24P_T3_35-> K16
set_property -dict { PACKAGE_PIN J16 IOSTANDARD LVCMOS33 } [get_ports { led4 }]; # PL_LED4---> IO_L24N_T3_35-> J16
#KEYs
set_property -dict { PACKAGE_PIN T17 IOSTANDARD LVCMOS33 } [get_ports { key3 }]; # PL_KEY3---> IO_L20P_T3_34 T17
set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { key4 }]; # PL_KEY4---> IO_L19N_T3_34 R17
# 40P
# set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports { gpioA_5 }]; # W18
# set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { gpioA_6 }]; # P14
# set_property -dict { PACKAGE_PIN Y16 IOSTANDARD LVCMOS33 } [get_ports { gpioA_7 }]; # Y16
##### gpioA define #####
# ## LED 4
# set_property PACKAGE_PIN M25 [get_ports {gpioA[24]}]
# ## LED 3
# set_property PACKAGE_PIN N23 [get_ports {gpioA[23]}]
# ## LED 2
# set_property PACKAGE_PIN N22 [get_ports {gpioA[22]}]
# ## LED 1
# set_property PACKAGE_PIN V22 [get_ports {gpioA[21]}]
# ## UART TX
# set_property PACKAGE_PIN L25 [get_ports {gpioA[17]}]
# ## UART RX
# set_property PACKAGE_PIN M24 [get_ports {gpioA[16]}]
# ## key_in C
# set_property PACKAGE_PIN AB25 [get_ports {gpioA[7]}]
# ## key_in R
# set_property PACKAGE_PIN W23 [get_ports {gpioA[6]}]
# ## key_in L
# set_property PACKAGE_PIN W24 [get_ports {gpioA[5]}]
# ## key_in D
# set_property PACKAGE_PIN AB26 [get_ports {gpioA[4]}]
# ## key_in U
# set_property PACKAGE_PIN AC26 [get_ports {gpioA[3]}]
##### SPI Configurate Setting #######
#set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
#set_property CONFIG_MODE SPIx4 [current_design]
#set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
4.2 vivado综合
检查源文件没有报错信息后,在vivado中点击左侧菜单的Run Synthesis进行综合,也可以点击Generate Bitstream直接进行 综合——实现——生成比特流的一系列操作,完成后可以查看时序以及资源占用的情况:
4.3 连接开发板并烧写程序
vivxado完成 Generate Bitstream 后,将开发板插上电,使用ZYNQ自带的JTAG下载器连接电脑,点击左侧菜单栏的 Open Hardware Manager,设置vivado自动识别开发板,然后点击 Program Device 烧写程序:
4.4 连接jtag调试器
利用蜂鸟官方的riscv调试器,根据约束文件中的管脚定义进行引脚的连接,注意连接方式为:
TCLK->TCLK,TDI->TDI,TDO->TDO,TMS->TMS,同时,也要连接一个GND。
4.5 使用Nuclei Studio调试
打开刚才的helloworld工程,连接串口,然后点击Run按钮运行程序:
如果出现以下报错内容,那么需要将openocd_hbirdv2.cfg文件中关于FLASH的语句都注释掉,禁止FLASH启动:
修改之后再次运行,程序在串口正常打印输出:
5. 问题处理
5.1 Windows下OpenOCD找不到设备及串口无法识别
通常情况下,Windows系统会自动为其安装正确的串口驱动,如果使用最新版本的Nuclei Studio IDE开发,其内置的OpenOCD是免驱的,也不需要用户手动安装JTAG调试驱动。但有些时候,Windows系统并不能自动安装正确的串口驱动,进而影响到IDE的程序烧写和串口连接功能。经实验,可尝试用下述方法予以解决:
- 先下载蜂鸟调试器驱动和FT2232串口驱动到电脑上
- 蜂鸟调试器驱动下载地址:DEVELOPMENT BOARDS_Nuclei-Best RISC-V Processor IP
- FT2232串口驱动下载地址:D2XX Drivers - FTDI
- 连接开发板,卸载新增的两个串行设备的同时也删除对应的驱动
- 断开开发板,手动安装蜂鸟调试器驱动,即hbird_driver.exe驱动程序
- 连接开发板,打开设备管理器,为另一个未识别的端口手动安装FT2232串口驱动
- 重连开发板,程序可以正常烧写调试,串口也可以正常建立连接
具体图文教程来自于:GD32VF103 MCU_RISC-V论坛讨论_RISC-V MCU中文社区
5.2 Nuclei Studio IDE使用常见问题
相关说明页:Nuclei Studio常见问题_Nuclei Studio_RISC-V MCU中文社区
下载程序的过程中如果出现错误,可能原因经常有以下几个:
- 1. 板子上的处理器没有正常跑起来或者改动了某些地方造成功能错误;
- 2. 蜂鸟的JTAG引脚分配错误或者开发板和调试器的连线错误;
- 3. openocd配置文件即openocd_hbirdv2.cfg有问题(ftdi_device_desc和ftdi_vid_pid与使用的调试器不符);
- 4. 如果是Linux系统则99-openocd.rules文件可能有问题或者没有将用户添加到plugdev group中等。
原创声明:本文为CSDN博主「yuuki_ac」原创文章,遵循CC 4.0 BY-SA版权协议
转载需附原文链接:蜂鸟E203 hbirdv2项目复现与移植到ZYNQ7020及构建FPGA工程【基于Windows】-CSDN博客
更多推荐
所有评论(0)