Loading

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 输出端口的值。下面是对该模块的详细分析:

  1. 参数 该模块有三个参数:
  • FORMAT :一个字符串,它用于指定从命令行读取的参数的格式。这个字符串中可以包含 %d 或其他格式占位符,这些占位符会在模块实例化时被替换为实际值。如果没有指定该参数,则默认值为 "borked=%d"。
  • WIDTH :一个整数,它指定了输出端口 out 的位宽。如果没有指定该参数,则默认值为 1。
  • DEFAULT :一个 WIDTH 位的二进制值,用于在没有从命令行中读取到参数时,给 out 赋默认值。如果没有指定该参数,则默认值为 0。
  1. 输入 该模块没有输入端口。

  2. 输出 该模块只有一个输出端口:out。它是一个 WIDTH 位的向量。当从命令行读取到参数时,out 的值被设置为命令行参数的值。如果没有读取到参数,则 out 被设置为默认值 DEFAULT

  3. 实现 该模块的实现包括以下几个部分:

  • 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 为低电平时,该模块会将 entest_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会输出是否通过的信息:

posted @ 2023-04-07 18:01  Haowen_Zhao  阅读(819)  评论(0编辑  收藏  举报