一 引言
公司正在进行小型低功耗SOC的内核选型,Ibex作为一款优秀的RISC-V core自然成为首选目标。Ibex最早从PULP平台下的zero-riscy发展而来,目前由非盈利组织LowRISC发展和维护,Ibex有官方文档,并且有simple_system与demo_system两个系统,简单来说,simple_system系统除了挂载ram之外,还挂载有simulator_ctrl和timer两个外设,并且有一个tracing模块用于记录ibex core读取总线数据的记录,是一个主要用于simulation的system;而demo_system主要针对 FPGA 实现,包含一些额外的外设如uart、gpio、pwm等以及 RISC-V 调试模块的集成。
二 环境准备
ibex_demo_system Github链接:GitHub - lowRISC/ibex-demo-system: A demo system for Ibex including debug support and some peripherals
想要在windoibex_demo_system移植至FPGA开发板上前提是在linux环境下跑通ibex_simple_system,因为我在win下搭建FPGA环境时发现有几个设计文件是在linux下用官方给的脚本自动生成的,在如下文件夹中,如果不在linux下跑通simple_system,在vivado中添加设计文件时,将出现找不到的情况。在linux下跑通simple_system请参考Ibex环境建立及CoreMark测试_riscvibex-CSDN博客。后续内容默认在此基础上进行。
三 FPGA环境搭建
本人使用的vivado版本为2018.3,使用的FPGA型号为XILINX-SP701,芯片型号为XC7S100系列,本实验对FPGA型号应该没有要求,在vivado中选择自己FPGA的芯片型号即可。
(1)add design sources
在vivado中添加设计文件不要一股脑添加全部文件夹,建议先添加顶层ibex_demo_system,在一层一层往下添加,在文件夹中一个一个找,比较费时间,耐心添加完即可。注意有几个design source不在文件夹中,这时候就要在ibex\build文件夹中找,如上图。
(2)编写FPGA top层
根据自己的需要编写顶层代码,我的代码如下:
// This is the top level SystemVerilog file that connects the IO on the board to the Ibex Demo System on Nexys-A7 board.
module top_sp701 (
// These inputs are defined in data/pins_nexysa7.xdc
input clk_in1_p,//clk200mhz
input clk_in1_n,//clk200mhz
input IO_RST_N,//cpu_resetn
input [ 3:0] SW,//SW
output [ 3:0] LED,//LED
//output [ 3:0] RGB_LED,//[0:5] = led16_b/led16_g/led16_r/led17_b/led17_g/led17_r
input UART_RX,//uart_rxd_out
output UART_TX,//uart_txd_in
output logic led_bootok,
// input SPI_RX,
// output SPI_TX,
// output SPI_SCK
input logic tck_i, // JTAG test clock pad
input logic tms_i, // JTAG test mode select pad
input logic td_i, // JTAG test data input pad
output logic td_o // JTAG test data output pad
);
parameter SRAMInitFile = "";
logic clk_sys, rst_sys_n;
//wire clk_25m;
//logic mainclk_buf;
logic locked;
logic rst_btn;
assign led_bootok = rst_sys_n;
logic clk_rst;
clk_wiz_0 instance_name0
(
// Clock out ports
.clk_out1(clk_sys), // output clk_out1
.clk_out2(clk_rst), // output clk_out2
// Status and control signals
.reset(IO_RST_N), // input reset
.locked(locked), // output locked
// Clock in ports
.clk_in1_p(clk_in1_p), // input clk_in1_p
.clk_in1_n(clk_in1_n) // input clk_in1_n
);
// Instantiating the Ibex Demo System.
ibex_demo_system #(
.GpiWidth(4),
.GpoWidth(4),
.PwmWidth(4),
.SRAMInitFile(SRAMInitFile)
) u_ibex_demo_system (
//input
.clk_sys_i(clk_sys),
.rst_sys_ni(rst_sys_n),
.gp_i(SW),
.uart_rx_i(UART_RX),
//output
.gp_o(LED),
.pwm_o(),
.uart_tx_o(UART_TX),
.spi_rx_i(1'b0),
.spi_tx_o(),
.spi_sck_o(),
.tck_i(tck_i),
.tms_i(tms_i),
.trst_ni(rst_sys_n),
.td_i(td_i),
.td_o(td_o)
);
// Generating the system clock and reset for the FPGA.
assign rst_btn = IO_RST_N;
rst_ctrl u_rst_ctrl (
.clk_i (clk_rst),
.pll_locked_i(locked),
.rst_btn_i (rst_btn),
.rst_no (rst_sys_n)
);
endmodule
由于跑helloworld_test只需要用到uart和jtag接口,所以其他模块我没有用到,如spi输入引脚直接给0,输出引脚直接悬空,大家可以根据自己的需要编写顶层模块。需要注意的是,我使用的SP701开发板板载时钟是频率200M的差分时钟,而ibex所需要的时钟频率为50M,所以在vivado中需要添加pll ipcore,如下图,这属于基本FPGA开发,这里就不多赘述了。需要注意的是,当时钟为差分时钟时,如下图右下角所示,时钟源需选择“Differential clock capable pin”。
另外,我用了一个ledboot_ok信号接到板载的led灯上,目的是监测rst_ctrl模块是否正常工作并提供rst_sys_n信号,如果ledboot_ok信号接的led灯没有亮,就不要进行后续任何操作,进行了也会在jtag调试时显示no jtag device found,先检查前面的操作是否有问题。
(3)add constraints
编写完top层后需要添加xdc文件,完整的约束文件如下:
## This file is a general .xdc for the SP701
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project
## Clock signal
#set_property -dict { PACKAGE_PIN AE8 IOSTANDARD DIFF_SSTL15 } [get_ports {clk_in1_p}];
create_clock -period 5.00 [get_ports {clk_in1_p}];
set_property IOSTANDARD DIFF_SSTL15 [get_ports clk_in1_p]
set_property PACKAGE_PIN AE8 [get_ports clk_in1_p]
#set_property -dict { PACKAGE_PIN AE8 IOSTANDARD LVDS } [get_ports {clk_in1_n}];
#create_clock -add -name sys_clk_pin -period 5.00 [get_ports {clk_in1_n}];
set_property IOSTANDARD DIFF_SSTL15 [get_ports clk_in1_n]
set_property PACKAGE_PIN AE7 [get_ports clk_in1_n]
#sw5
set_property IOSTANDARD LVCMOS33 [get_ports IO_RST_N]
set_property PACKAGE_PIN AF23 [get_ports IO_RST_N]
##Switches SW6/SW7/SW4/SW9
set_property -dict { PACKAGE_PIN AA20 IOSTANDARD LVCMOS33 } [get_ports { SW[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
set_property -dict { PACKAGE_PIN AB20 IOSTANDARD LVCMOS33 } [get_ports { SW[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
set_property -dict { PACKAGE_PIN AB22 IOSTANDARD LVCMOS33 } [get_ports { SW[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=sw[2]
set_property -dict { PACKAGE_PIN AC22 IOSTANDARD LVCMOS33 } [get_ports { SW[3] }]; #IO_L13N_T2_MRCC_14 Sch=sw[3]
## LEDs GPIO_LED 0-3
set_property -dict { PACKAGE_PIN J25 IOSTANDARD LVCMOS33 } [get_ports { LED[0] }]; #IO_L18P_T2_A24_15 Sch=led[0]
set_property -dict { PACKAGE_PIN M24 IOSTANDARD LVCMOS33 } [get_ports { LED[1] }]; #IO_L24P_T3_RS1_15 Sch=led[1]
set_property -dict { PACKAGE_PIN L24 IOSTANDARD LVCMOS33 } [get_ports { LED[2] }]; #IO_L17N_T2_A25_15 Sch=led[2]
set_property -dict { PACKAGE_PIN K25 IOSTANDARD LVCMOS33 } [get_ports { LED[3] }]; #IO_L8P_T1_D11_14 Sch=led[3]
set_property -dict { PACKAGE_PIN K26 IOSTANDARD LVCMOS33 } [get_ports { led_bootok }]; #IO_L8P_T1_D11_14 Sch=led[4]
##USB-RS232 Interface PMOD1 6/8
set_property -dict { PACKAGE_PIN A14 IOSTANDARD LVCMOS33 } [get_ports { UART_RX }]; #IO_L7P_T1_AD6P_35 Sch=uart_txd_in
set_property -dict { PACKAGE_PIN A15 IOSTANDARD LVCMOS33 } [get_ports { UART_TX }]; #IO_L11N_T1_SRCC_35 Sch=uart_rxd_out
#pin1
set_property IOSTANDARD LVCMOS33 [get_ports tck_i]
set_property PACKAGE_PIN C13 [get_ports tck_i]
#create_clock -name jtag_clk_pin -period 300 [get_ports {jtag_TCK}];
#pin4
set_property IOSTANDARD LVCMOS33 [get_ports tms_i]
set_property PACKAGE_PIN B15 [get_ports tms_i]
#pin2
set_property IOSTANDARD LVCMOS33 [get_ports td_i]
set_property PACKAGE_PIN D14 [get_ports td_i]
#pin3
set_property IOSTANDARD LVCMOS33 [get_ports td_o]
set_property PACKAGE_PIN B14 [get_ports td_o]
#pin7
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets tck_i]
本人是将uart、jtag全部接到FPGA的普通IO口上,使用外部的uart和jtag调试模块进行调试,约束文件需要注意两个点:1.注意差分时钟管脚的约束;2.由于jtag使用外部的调试器,所以引脚都直接接到开发板上的普通IO口上,所以要添加“set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets tck_i]”,不然生成bitstream时会报错。
至此,生成bitstream前期准备工作全部完成,接下来就是等待bitstream的生成,生成结束后烧写至开发板上,烧写后,检查ledboot_ok信号接的led灯有没有亮,如果亮了,恭喜你,已经成功将demo_system移植至FPGA开发板上,并且demo_system已经在开发板上正常运行了!
四 FPGA连线
请正确连线,jtag中tck、tms、tdi、tdo引脚需正确连接FPGA上所绑定的对应IO口,uart调试模块中rx引脚需接FPGA上绑定的tx信号IO口,如下图所示。
连线完毕后,我们要运行的驱动程序是ibex-demo-system\sw\c\demo\hello_world下的main.c文件,使用的是openocd.exe进行调试,这里需要编写openocd的配置文件(.cfg文件):
# script for ibex family
#
# riscv devices support jtag transports only.
#
adapter driver jlink
adapter speed 1000
transport select jtag
#source [find target/swj-dp.tcl]
#source [find mem_helper.tcl]
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x11001cdf
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
#riscv set_reset_timeout_sec 1
riscv set_reset_timeout_sec 2000
riscv set_command_timeout_sec 2000
#gdb_report_data_abort enable
#gdb_report_register_access_error enable
#gdb_breakpoint_override hard
#reset_config none
# prefer to use sba for system bus access
riscv set_prefer_sba on
# dump jtag chain
scan_chain
init
halt
编写完配置文件后,接入openocd,成功识别jtag会打印以下信息:
做到这儿就已经成功了,接下来的就是打开串口调试助手并运行驱动程序,可以看到,串口成功打印Hello World!
五 结语
至此,windows下使用vivado将ibex_demo_system移植至XILINX-SP701开发板并使用串口调试助手成功打印helloworld的实验就全部完成了,在ibex-demo-system\sw\c\demo下还有别的驱动程序,大家有兴趣可以试一试。
本人是刚踏入ic行业的小白,第一次编写文档,可能有错误或是不足之处,还望大家批评指正。