chipyard——设计文件解读/verilator asm仿真
chipyard吐出的设计文件包含以下(在vlsi目录下生成的):
打开dve可以查看设计层次和电路图:
一,chiptop功能说明
1,chipyard.TestHarness.RocketConfig.harness.v
harness.v是soc的testbench,其顶层module为Testharness
(1)testbench例化了simJTAG.v
该模块实现了一个用于仿真的JTAG控制器,名为SimJTAG。该模块包含了用于实现JTAG时序的输入/输出信号,包括jtag_TCK、jtag_TMS、jtag_TDI、jtag_TRSTn和jtag_TDO。同时,该模块还包含了一些用于控制仿真时序的逻辑,例如时钟和复位信号。此外,该模块还定义了一个exit输出端口,用于在仿真结束时传递仿真结果。
关于JTAG,你知道的和不知道的都在这里 - 知乎 (zhihu.com)
Verilog——JTAG标准的状态机实现_jtag接口时序_ShareWow丶的博客-CSDN博客
(2)plusarg_reader
module plusarg_reader #( parameter FORMAT="borked=%d", parameter WIDTH=1, parameter [WIDTH-1:0] DEFAULT=0 ) ( output [WIDTH-1:0] out ); `ifdef SYNTHESIS assign out = DEFAULT; `else reg [WIDTH-1:0] myplus; assign out = myplus; initial begin if (!$value$plusargs(FORMAT, myplus)) myplus = DEFAULT; end `endif endmodule
这是一个名为
plusarg_reader
的 Verilog 模块。它通过读取命令行参数来设置out
输出端口的值。下面是对该模块的详细分析:
- 参数 该模块有三个参数:
FORMAT
:一个字符串,它用于指定从命令行读取的参数的格式。这个字符串中可以包含%d
或其他格式占位符,这些占位符会在模块实例化时被替换为实际值。如果没有指定该参数,则默认值为 "borked=%d"。WIDTH
:一个整数,它指定了输出端口out
的位宽。如果没有指定该参数,则默认值为 1。DEFAULT
:一个WIDTH
位的二进制值,用于在没有从命令行中读取到参数时,给out
赋默认值。如果没有指定该参数,则默认值为 0。
输入 该模块没有输入端口。
输出 该模块只有一个输出端口:
out
。它是一个WIDTH
位的向量。当从命令行读取到参数时,out
的值被设置为命令行参数的值。如果没有读取到参数,则out
被设置为默认值DEFAULT
。实现 该模块的实现包括以下几个部分:
myplus
寄存器:这是一个WIDTH
位的寄存器,用于存储从命令行中读取的参数值。out
输出端口的赋值:在ifdef SYNTHESIS
条件编译块内部,out
被赋值为DEFAULT
,这是为了保证在综合时,即使命令行参数没有被指定,也能正常生成电路。在else
块中,将myplus
寄存器的值赋给out
输出端口。initial
代码块:在模块实例化时,initial
块会执行。它使用$value$plusargs
系统任务从命令行读取参数值。如果找不到与FORMAT
参数匹配的参数,则会将myplus
寄存器的值设置为DEFAULT
。总之,
plusarg_reader
模块允许用户通过命令行参数设置 Verilog 设计中的常量值,使得在不同的测试场景中可以很方便地修改常量值而无需重新编译代码。
(3)Chiptop是soc verilog模块
(4)SimDRAM.v
它是一个可配置的双端口存储器,可以与AXI4协议兼容。
(5)SimSerial.v
这个SimSerial.v文件定义了一个Verilog模块SimSerial,该模块用于模拟串行通信。它提供了一个DPI-C函数serial_tick来实现串行数据的收发。此外,它还包含了一些用于模拟的寄存器和信号。在模块中,有一个always块,它会在时钟的上升沿触发。如果reset为1,模块会将所有的寄存器和信号清零。否则,模块会调用serial_tick函数,将输入信号和内部变量作为参数传递给它,然后更新寄存器的值。最后,模块将更新后的信号值分配给相应的输出端口。
(6)module AsyncQueue_inTestHarness (位于*.harness.v内,在testharness module内例化)
这是一个Verilog的模块定义,包含了一个异步队列的测试组件 AsyncQueue_inTestHarness,用于检测异步队列的功能是否正常。
2,chipyard.TestHarness.RocketConfig.top.v
该文件是soc的顶层文件,在verdi中查看电路层次如下,其顶层模块为Chiptop,对应了testbench中的被测DUT。
(1)EICG_wrapper.v
/* verilator lint_off UNOPTFLAT */ module EICG_wrapper( output out, input en, input test_en, input in ); reg en_latched /*verilator clock_enable*/; always @(*) begin if (!in) begin en_latched = en || test_en; end end assign out = en_latched && in; endmodule
包含了EICG_wrapper模块,它将输入信号in与由en和test_en信号控制的时钟使能(en_latched)进行与运算,并将结果分配给输出信号out。当in和en_latched同时为高电平时,out将为高电平。当输入信号in为低电平时,en_latched信号会在时钟上升沿处更新为en和test_en的逻辑或结果。在其他情况下,en_latched将保持其前一个值不变。
这个模块是一个 EICG (Early Input Clock Gating) 的包装器,用于在时钟信号不可用时提供一个软件可控制的时钟使能信号。当输入信号 in
为低电平时,该模块会将 en
和 test_en
两个输入信号的逻辑或值存储到一个寄存器 en_latched
中,并将其作为时钟使能信号输出。当 in
为高电平时,输出信号 out
会被拉低。
这个模块的作用是提供一种软件可控制的时钟使能方式,以便在输入信号无法生成时钟信号的情况下,依然可以通过这个模块产生一个有效的时钟使能信号。
(2)IOcell.v
这是一组 Verilog 模块,用于定义通用的 IO 单元,包括模拟 I/O 和数字 I/O。这些模块可以用于设计 FPGA 或 ASIC 中的 IO 端口。
GenericAnalogIOCell
: 通用模拟 I/O 单元,具有一个输入输出端口pad
和一个内部端口core
,用于模拟模拟信号。core
端口始终为高阻态,pad
端口输出core
端口的值。GenericDigitalGPIOCell
: 通用数字 I/O 单元,具有一个输入输出端口pad
和三个控制端口i
,ie
,o
,oe
,用于控制数字输入输出信号。当oe
为 1 时,pad
端口输出o
端口的值;当ie
为 1 时,i
端口输入pad
端口的值。如果ie
为 0,i
端口始终为 0;如果oe
为 0,pad
端口始终为高阻态。GenericDigitalInIOCell
: 通用数字输入单元,具有一个输入端口pad
和两个控制端口i
,ie
,用于控制数字输入信号。当ie
为 1 时,i
端口输入pad
端口的值。如果ie
为 0,i
端口始终为 0。GenericDigitalOutIOCell
: 通用数字输出单元,具有一个输出端口pad
和两个控制端口o
,oe
,用于控制数字输出信号。当oe
为 1 时,pad
端口输出o
端口的值。如果oe
为 0,pad
端口始终为高阻态。
【设计经验】1、Verilog中如何规范的处理inout信号 - jgliu - 博客园 (cnblogs.com)
Chiptop顶层IO接口如下:
module ChipTop( input jtag_TCK, input jtag_TMS, input jtag_TDI, output jtag_TDO, output serial_tl_clock, output serial_tl_bits_in_ready, input serial_tl_bits_in_valid, input [31:0] serial_tl_bits_in_bits, input serial_tl_bits_out_ready, output serial_tl_bits_out_valid, output [31:0] serial_tl_bits_out_bits, input custom_boot, input clock_clock, input reset, output axi4_mem_0_clock, output axi4_mem_0_reset, input axi4_mem_0_bits_aw_ready, output axi4_mem_0_bits_aw_valid, output [3:0] axi4_mem_0_bits_aw_bits_id, output [31:0] axi4_mem_0_bits_aw_bits_addr, output [7:0] axi4_mem_0_bits_aw_bits_len, output [2:0] axi4_mem_0_bits_aw_bits_size, output [1:0] axi4_mem_0_bits_aw_bits_burst, output axi4_mem_0_bits_aw_bits_lock, output [3:0] axi4_mem_0_bits_aw_bits_cache, output [2:0] axi4_mem_0_bits_aw_bits_prot, output [3:0] axi4_mem_0_bits_aw_bits_qos, input axi4_mem_0_bits_w_ready, output axi4_mem_0_bits_w_valid, output [63:0] axi4_mem_0_bits_w_bits_data, output [7:0] axi4_mem_0_bits_w_bits_strb, output axi4_mem_0_bits_w_bits_last, output axi4_mem_0_bits_b_ready, input axi4_mem_0_bits_b_valid, input [3:0] axi4_mem_0_bits_b_bits_id, input [1:0] axi4_mem_0_bits_b_bits_resp, input axi4_mem_0_bits_ar_ready, output axi4_mem_0_bits_ar_valid, output [3:0] axi4_mem_0_bits_ar_bits_id, output [31:0] axi4_mem_0_bits_ar_bits_addr, output [7:0] axi4_mem_0_bits_ar_bits_len, output [2:0] axi4_mem_0_bits_ar_bits_size, output [1:0] axi4_mem_0_bits_ar_bits_burst, output axi4_mem_0_bits_ar_bits_lock, output [3:0] axi4_mem_0_bits_ar_bits_cache, output [2:0] axi4_mem_0_bits_ar_bits_prot, output [3:0] axi4_mem_0_bits_ar_bits_qos, output axi4_mem_0_bits_r_ready, input axi4_mem_0_bits_r_valid, input [3:0] axi4_mem_0_bits_r_bits_id, input [63:0] axi4_mem_0_bits_r_bits_data, input [1:0] axi4_mem_0_bits_r_bits_resp, input axi4_mem_0_bits_r_bits_last, output uart_0_txd, input uart_0_rxd );
...
二,chipyard前仿、后仿
默认的default config所生成的soc支持的指令集为rv64imafdc,我们需要对其进行仿真验证。主要通过riscv-tests套件进行测试,包括 benchmark 基准测试、debug 测试、isa 指令测试等。
测试程序写在“.S”汇编文件中,程序一开始便调用了 riscv_test.h 头文件,里 面包含了 TVM 所使用的宏,然后指明它所针对的 TVM 名称。测试程序从 RVTEST_CODE_BEGIN 指令处开始执行,至 RVTEST_PASS 或者 RVTEST_CODE _END 结束执行,其间若出现错误,就会跳转至 RVTEST_FAIL。
测试例子:
以 rv64ui 的加法测试程序 riscv-tests/isa/rv64ui/add.S 在单核且禁用虚拟内存的 目标环境下的使用为例,我们想要测试一下之前生成的 SoC 的加法性能。通过以 下命令对加法测试程序进行仿真,且输出详细的命令执行过程:
/emulator-freechips.rocketchip.system-debug +verbose $RISCV/riscv64-unknown -elf/share/riscv-tests/isa/rv64ui-p-add 2>&1 | spike-dasm
加法测试程序 add.S 中包含了很多测试指令,比如:
TEST_RR_OP( 2, add, 0x00000000, 0x00000000, 0x00000000 );
TEST_RR_OP( 3, add, 0x00000002, 0x00000001, 0x00000001 );
TEST_RR_OP( 4, add, 0x0000000a, 0x00000003, 0x00000007 );
我们可以从 add.S 程序开头调用的 riscv-tests/isa/macros/scalar/test_macros.h 头 文件中找到 TEST_RR_OP 的定义,如下所示:
#define TEST_RR_OP( testnum, inst, result, val1, val2 ) \ TEST_CASE( testnum, x14, result, \ li x1, MASK_XLEN(val1); \ li x2, MASK_XLEN(val2); \ inst x14, x1, x2; \ )
TEST_RR_OP 命令是对寄存器-寄存器操作指令进行测试的,TEST_RR_OP 的 第一个参数是测试次数,第二个参数是指令,第三个参数是指令预期得到的正确 结果,第四和第五个参数则是指令的两个操作数。以上述那条 TEST_RR_OP 测试 指令为例,若最后两个数值相加后的结果与第三个参数不符,则会跳转至 RVTEST_FAIL,测试失败。
实践:
在verilator下make可产生相应config的src和c仿真模型可执行文件,Rocket全部config在:chipyard/generators/chipyard/src/main/scala/config/RocketConfigs.scala
这个可执行文件是一个simulator,它是根据构建的设计编译的。然后可以使用此可执行文件运行任何兼容的RV64代码。例如,运行一个riscv工具组件测试。./simulator-chipyard-RocketConfig $RISCV/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-simple
在chipyard/vsim/verilator下运行以下指令进行一组测试(也可以按照测试例子中那样进行单个测试):
make CONFIG=your_soc_config run-asm-tests
make CONFIG=your_soc_config run-bmark-tests
随后在output文件夹下生成xxx_xx.out,其中包含该指令是否passed的信息。
下图是asm-test组正在执行中,并产生输出文件:
产生文件如下:
out
*.out会输出是否通过的信息: