OSCPU / ysyx

一生一芯的信息发布和内容网站
123 stars 8 forks source link

[分享]Verilog代码接入到Difftest #9

Open YouTheFreedom1999 opened 3 years ago

YouTheFreedom1999 commented 3 years ago

前言

昨天看到有老哥写了Chisel的difftest接入,心血来潮想试试Verilog怎么接入。 一定要参考Chisel的difftest接入DiffTest Usge这两篇,我算是一点点补充 水平有限,请各位多多指教。

TOP文件

首先我们要在difftest的上一层建立一个build文件夹,并新建一个 SimTop.v 并将下列内容写入,这个文件就是整个RTL仿真设计的顶层。 还要将CPU的所有RTL文件放入。(也可以考虑用 make install 自动copy)

module SimTop(
    input         clock,
    input         reset,

    input  [63:0] io_logCtrl_log_begin,
    input  [63:0] io_logCtrl_log_end,
    input  [63:0] io_logCtrl_log_level,
    input         io_perfInfo_clean,
    input         io_perfInfo_dump,

    output        io_uart_out_valid,
    output [7:0]  io_uart_out_ch,
    output        io_uart_in_valid,
    input  [7:0]  io_uart_in_ch
  // ......
);

endmodule

RAM的接入

对于DiffTest框架,提供了一块申请的8GB内存在路径,在difftest/src/test/csrc/common/ram.h中定义了仿真内存的大小,也提供了对应的读写函数,Verilog通过DPI-C的方式访问读写函数完成对仿真内存的读写,代码在difftest/src/test/vsrc/common/ram.v 下面提供的RAMHelper好像只有一读一写,而在没有Cache的情况下,没有办法让取指和访存同时访问这块内存(我在想可不可以上升取指下降访存来过渡),于是我重写了一个有一写两读的RAM模块。 其中指令端口通过一个MUX将64位数据对齐到了32位。inst_addr右移3位是因为CPU内部是以字节编址,而仿真内存是以8字节编址,MUX的选择信号是右移出去三位数据的最高位,来选择64位中的高32位/低32位。 数据的读写也是按照4字节对齐,为了适应仿真内存也要做相应的映射,详情见下述代码

module RAM_1W2R(
    input clk,

    input [`BUS_WIDTH]inst_addr,
    input inst_ena,
    output [31:0]inst,

    // DATA PORT
    input ram_wr_en,
    input ram_rd_en,
    input [`BUS_WIDTH]ram_wmask,
    input [`BUS_WIDTH]ram_addr,
    input [`BUS_WIDTH]ram_wr_data,
    output reg [`BUS_WIDTH]ram_rd_data
);

    // INST PORT

    wire[`BUS_WIDTH] inst_2 = ram_read_helper(inst_ena,{3'b000,(inst_addr-64'h0000_0000_8000_0000)>>3});

    assign inst = inst_addr[2] ? inst_2[63:32] : inst_2[31:0];

    // DATA PORT 
    assign ram_rd_data = ram_read_helper(ram_rd_en, {3'b000,(ram_addr-64'h0000_0000_8000_0000)>>3});

    always @(posedge clk) begin
        ram_write_helper((ram_addr-64'h0000_0000_8000_0000)>>3, ram_wr_data, ram_wmask, ram_wr_en);
    end

endmodule

将此模块实例化到 SimTop.v 中并且将端口接入到自己设计的CPU中,这样就完成了,RAM的接入

指令的提交

Difftest通过,DifftestInstrCommit这个模块来将信号提交到DiffTest中。

module DifftestInstrCommit(InstrCommit)(
  input        clock,  // 时钟
  input [ 7:0] coreid, // 处理器核ID,多处理器有用,默认为0即可
  input [ 7:0] index,  
  input        valid,  // 提交有效信号,只有为高电平时提交才有效
  input [63:0] pc,     // PC指针的值
  input [31:0] instr,  // 对应的指令
  input        skip,   // 是否跳过此条对比
  input        isRVC,  // 是否为压缩指令
  input        scFailed,
  input        wen,    // 写回寄存器使能
  input [ 7:0] wdest,  // 写回寄存器的地址
  input [63:0] wdata   // 写回的数据
);

将需要提交的信号链接,记住手册里的一句话,在指令提交的时刻其产生的影响恰好生效

  reg r_wen;
  reg [7:0]r_wdest;
  reg [`BUS_WIDTH]r_wdata,r_pc;
  reg [31:0]r_inst;
  reg vaild;

  always @(posedge clock) begin
    if(reset)begin
      r_wen     <= 'd0;
      r_wdest   <= 'd0;
      r_wdata   <= 'd0;
      r_pc      <= 'd0;
      r_inst    <= 'd0;
      vaild     <= 'd0;
    end else begin
      r_wen     <= u_riscvcpu.regs_wr_en   ; // 这里面的u_riscvcpu 是cpu核的例化名
      r_wdest   <= {3'd0,u_riscvcpu.regs_wr_addr};
      r_wdata   <= u_riscvcpu.regs_wr_data;
      r_pc      <= {inst_addr[63:32],1'b1,inst_addr[30:0]};
      r_inst    <= inst;
      vaild     <= 1'b1;
    end
  end

DifftestInstrCommit U_inst_commit(
  .clock    ( clock ),
  .coreid   ( 8'd0 ),//8bit
  .index    ( 8'd0 ),//8bit
  .valid    ( vaild ),
  .pc       ( r_pc ),//64bit
  .instr    ( r_inst ),//32bit
  .skip     ( 1'b0 ),
  .isRVC    ( 1'b0 ),
  .scFailed ( 1'b0 ),
  .wen      ( r_wen    ),
  .wdest    ( r_wdest ),//8bit
  .wdata    ( r_wdata ) //64bit
);

REGFILE的提交

对应模块名为DifftestArchIntRegState,类似的方法接入

module DifftestArchIntRegState (
  input         clock,
  input [ 7:0]  coreid,
  input [63:0]  gpr_0,
  input [63:0]  gpr_1,
  input [63:0]  gpr_2,
  input [63:0]  gpr_3,
  input [63:0]  gpr_4,
  input [63:0]  gpr_5,
  input [63:0]  gpr_6,
  input [63:0]  gpr_7,
  input [63:0]  gpr_8,
  input [63:0]  gpr_9,
  input [63:0]  gpr_10,
  input [63:0]  gpr_11,
  input [63:0]  gpr_12,
  input [63:0]  gpr_13,
  input [63:0]  gpr_14,
  input [63:0]  gpr_15,
  input [63:0]  gpr_16,
  input [63:0]  gpr_17,
  input [63:0]  gpr_18,
  input [63:0]  gpr_19,
  input [63:0]  gpr_20,
  input [63:0]  gpr_21,
  input [63:0]  gpr_22,
  input [63:0]  gpr_23,
  input [63:0]  gpr_24,
  input [63:0]  gpr_25,
  input [63:0]  gpr_26,
  input [63:0]  gpr_27,
  input [63:0]  gpr_28,
  input [63:0]  gpr_29,
  input [63:0]  gpr_30,
  input [63:0]  gpr_31
);

编译运行

然后就可以编译emu

make -C difftest emu

运行difftest

./build/emu -i ../am-kernels/tests/cpu-tests/build/add-riscv64-mycpu.bin

根据日志可以看到已经捕获到错误的信息

Emu compiled at Jul 23 2021, 22:43:12
The image is /opt/RISCV/am-kernels/tests/cpu-tests/build/add-riscv64-mycpu.bin
Using simulated 8192MB RAM
[warning] sdcard img not found
--diff is not given, try to use $(NEMU_HOME)/build/riscv64-nemu-interpreter-so by default
Using /opt/RISCV/NEMU/build/riscv64-nemu-interpreter-so for difftest
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
pc:0000000080000000 instr:00000413 wen:1 wdest:  8 wdata:0000000000000000
The first instruction of core 0 has commited. Difftest enabled.
pc:0000000080000004 instr:00009117 wen:1 wdest:  2 wdata:0000000000009004

============== Commit Group Trace (Core 0) ==============
commit group [0]: pc 0080000000 cmtcnt 1
commit group [1]: pc 0080000004 cmtcnt 1 <--
commit group [2]: pc 0000000000 cmtcnt 0
commit group [3]: pc 0000000000 cmtcnt 0
commit group [4]: pc 0000000000 cmtcnt 0
commit group [5]: pc 0000000000 cmtcnt 0
commit group [6]: pc 0000000000 cmtcnt 0
commit group [7]: pc 0000000000 cmtcnt 0
commit group [8]: pc 0000000000 cmtcnt 0
commit group [9]: pc 0000000000 cmtcnt 0
commit group [a]: pc 0000000000 cmtcnt 0
commit group [b]: pc 0000000000 cmtcnt 0
commit group [c]: pc 0000000000 cmtcnt 0
commit group [d]: pc 0000000000 cmtcnt 0
commit group [e]: pc 0000000000 cmtcnt 0
commit group [f]: pc 0000000000 cmtcnt 0

============== Commit Instr Trace ==============
commit inst [0]: pc 0080000000 inst 00000413 wen 1 dst 00000008 data 0000000000000000
commit inst [1]: pc 0080000004 inst 00009117 wen 1 dst 00000002 data 0000000000009004 <--
commit inst [2]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [3]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [4]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [5]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [6]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [7]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [8]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [9]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [a]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [b]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [c]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [d]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [e]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [f]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000

==============  REF Regs  ==============
  $0: 0x0000000000000000   ra: 0x0000000000000000   sp: 0x0000000080009004   gp: 0x0000000000000000
  tp: 0x0000000000000000   t0: 0x0000000000000000   t1: 0x0000000000000000   t2: 0x0000000000000000
  s0: 0x0000000000000000   s1: 0x0000000000000000   a0: 0x0000000000000000   a1: 0x0000000000000000
  a2: 0x0000000000000000   a3: 0x0000000000000000   a4: 0x0000000000000000   a5: 0x0000000000000000
  a6: 0x0000000000000000   a7: 0x0000000000000000   s2: 0x0000000000000000   s3: 0x0000000000000000
  s4: 0x0000000000000000   s5: 0x0000000000000000   s6: 0x0000000000000000   s7: 0x0000000000000000
  s8: 0x0000000000000000   s9: 0x0000000000000000  s10: 0x0000000000000000  s11: 0x0000000000000000
  t3: 0x0000000000000000   t4: 0x0000000000000000   t5: 0x0000000000000000   t6: 0x0000000000000000
 ft0: 0x0000000000000000  ft1: 0x0000000000000000  ft2: 0x0000000000000000  ft3: 0x0000000000000000
 ft4: 0x0000000000000000  ft5: 0x0000000000000000  ft6: 0x0000000000000000  ft7: 0x0000000000000000
 fs0: 0x0000000000000000  fs1: 0x0000000000000000  fa0: 0x0000000000000000  fa1: 0x0000000000000000
 fa2: 0x0000000000000000  fa3: 0x0000000000000000  fa4: 0x0000000000000000  fa5: 0x0000000000000000
 fa6: 0x0000000000000000  fa7: 0x0000000000000000  fs2: 0x0000000000000000  fs3: 0x0000000000000000
 fs4: 0x0000000000000000  fs5: 0x0000000000000000  fs6: 0x0000000000000000  fs7: 0x0000000000000000
 fs8: 0x0000000000000000  fs9: 0x0000000000000000 fs10: 0x0000000000000000 fs11: 0x0000000000000000
 ft8: 0x0000000000000000  ft9: 0x0000000000000000 ft10: 0x0000000000000000 ft11: 0x0000000000000000
pc: 0x0000000080000008 mstatus: 0x0000000000000000 mcause: 0x0000000000000000 mepc: 0x0000000000000000
                       sstatus: 0x0000000000000000 scause: 0x0000000000000000 sepc: 0x0000000000000000
satp: 0x0000000000000000
mip: 0x0000000000000000 mie: 0x0000000000000000 mscratch: 0x0000000000000000 sscratch: 0x0000000000000000
mideleg: 0x0000000000000000 medeleg: 0x0000000000000000
mtval: 0x0000000000000000 stval: 0x0000000000000000mtvec: 0x0000000000000000 stvec: 0x0000000000000000
priviledgeMode: 0
     sp different at pc = 0x0080000004, right= 0x0000000080009004, wrong = 0x0000000000009004
Core 0: ABORT at pc = 0x2913c2304813023
total guest instructions = 0
instrCnt = 0, cycleCnt = 0, IPC = -nan
Seed=0 Guest cycle spent: 3 (this will be different from cycleCnt if emu loads a snapshot)
Host time spent: 10ms

Tips: 将Difftest的Makefile中clean:vcs-clean下面的一行改成

rm -rf $(filter-out $(wildcard $(BUILD_DIR)/*.v), $(wildcard $(BUILD_DIR)/*))

可以在执行make clean时不删除RTL代码

lsyic commented 3 years ago

大佬你好,请问是要在SimTop.v中融合原cpu中顶层.v,还是在SimTop.v中调用原顶层.v呢?

YouTheFreedom1999 commented 3 years ago

大佬你好,请问是要在SimTop.v中融合原cpu中顶层.v,还是在SimTop.v中调用原顶层.v呢?

都可以,只要能把提交信息的端口连到相应的模块就行

lhx6355 commented 3 years ago

你好,请问“在指令提交的时刻其产生的影响恰好生效”,是不是要求需要实现流水线结构?

cxxuser commented 3 years ago

想请问一下这里的CPU的所有RTL文件,指的是什么?是define.v,if_stage.v之类的文件吗?

lsyic commented 3 years ago

想请问一下这里的CPU的所有RTL文件,指的是什么?是define.v,if_stage.v之类的文件吗?

我理解是的。要把自己所有的CPU代码接入进来。

tuuyii commented 3 years ago

你好,请问您这个教程中接入的CPU是单周期的吗?

vx-zzy commented 3 years ago

你好,请问这个地方为什么是写入一半数据呢 wire [BUS_WIDTH]wr_data = {mem_wr_data[31:0],mem_rd_data[63:32]};` 谢谢

YouTheFreedom1999 commented 3 years ago

你好,请问您这个教程中接入的CPU是单周期的吗?

是的,流水线的提交也是类似的

YouTheFreedom1999 commented 3 years ago

你好,请问这个地方为什么是写入一半数据呢 wire [BUS_WIDTH]wr_data = {mem_wr_data[31:0],mem_rd_data[63:32]};` 谢谢

不好意思,可能给你造成了一些误解,这个是我测试sw ld 时为了让地址对齐临时写的,我在上面已经更新代码了,我把掩码的处理和数据的对仿真内存的适应放到了一个单独的模块(只针对difftest中的仿真内存有效)。


module mem_ctrl(
    input rst,

    // CPU端接口
    input [7:0]mem_byte_enble,
    input [`BUS_WIDTH]mem_addr,
    input mem_rd_en,
    input mem_wr_en,
    input [`BUS_WIDTH]mem_wr_data,
    output reg [`BUS_WIDTH]mem_rd_data,

    // 内存接口
    output  [`BUS_WIDTH]ram_addr,
    output  ram_wr_en,
    output reg  [`BUS_WIDTH]ram_wmask,
    output reg  [`BUS_WIDTH]ram_wr_data,
    output  ram_rd_en,
    input    [`BUS_WIDTH]ram_rd_data
);  

    reg [`BUS_WIDTH] sb_ram_wmask   ;
    reg [`BUS_WIDTH] sb_ram_wr_data ;
    reg [`BUS_WIDTH] sh_ram_wmask   ;
    reg [`BUS_WIDTH] sh_ram_wr_data ;
    reg [`BUS_WIDTH] sw_ram_wmask   ;
    reg [`BUS_WIDTH] sw_ram_wr_data ;
    reg [`BUS_WIDTH] sd_ram_wmask   ;
    reg [`BUS_WIDTH] sd_ram_wr_data ;

    reg [`BUS_WIDTH] lb_ram_rd_data ;
    reg [`BUS_WIDTH] lh_ram_rd_data ;
    reg [`BUS_WIDTH] lw_ram_rd_data ;
    reg [`BUS_WIDTH] ld_ram_rd_data ;

    assign ram_addr = mem_addr;
    assign ram_wr_en = rst ? 0 : mem_wr_en;
    assign ram_rd_en = rst ? 0 : mem_rd_en;

    wire sb_signal = mem_wr_en & (mem_byte_enble == 8'b0000_0001);
    wire sh_signal = mem_wr_en & (mem_byte_enble == 8'b0000_0011);
    wire sw_signal = mem_wr_en & (mem_byte_enble == 8'b0000_1111);
    wire sd_signal = mem_wr_en & (mem_byte_enble == 8'b1111_1111);

    wire lb_signal = mem_rd_en & (mem_byte_enble == 8'b0000_0001);
    wire lh_signal = mem_rd_en & (mem_byte_enble == 8'b0000_0011);
    wire lw_signal = mem_rd_en & (mem_byte_enble == 8'b0000_1111);
    wire ld_signal = mem_rd_en & (mem_byte_enble == 8'b1111_1111);

    // 写MUX
    always @(*) begin
        if(rst)begin
            ram_wmask   = `ZERO_WORD;
            ram_wr_data = `ZERO_WORD;
        end 
        else if(sb_signal)begin
            ram_wmask   = sb_ram_wmask  ;
            ram_wr_data = sb_ram_wr_data;
        end 
        else if(sh_signal)begin
            ram_wmask   = sh_ram_wmask  ;
            ram_wr_data = sh_ram_wr_data;
        end
        else if(sw_signal)begin
            ram_wmask   = sw_ram_wmask  ;
            ram_wr_data = sw_ram_wr_data;           
        end
        else if(sd_signal)begin
            ram_wmask   = sd_ram_wmask  ;
            ram_wr_data = sd_ram_wr_data;        
        end
        else begin
            ram_wmask   = `ZERO_WORD;
            ram_wr_data = `ZERO_WORD;
        end
    end

    // 读MUX
    always @(*) begin
        if(rst)
            mem_rd_data = `ZERO_WORD;
        else if(lb_signal)
            mem_rd_data = lb_ram_rd_data;
        else if(lh_signal)
            mem_rd_data = lh_ram_rd_data;
        else if(lw_signal)
            mem_rd_data = lw_ram_rd_data;
        else if(ld_signal)
            mem_rd_data = ld_ram_rd_data;
        else
            mem_rd_data = `ZERO_WORD;
    end

    // 写字节的处理
    always @(sb_signal or mem_addr[2:0] or mem_wr_data[7:0]) begin
        if(sb_signal)begin
            case(mem_addr[2:0])
                3'd0:begin 
                    sb_ram_wmask   = 64'h0000_0000_0000_00FF;
                    sb_ram_wr_data = {56'd0,mem_wr_data[7:0]};
                end
                3'd1:begin 
                    sb_ram_wmask   = 64'h0000_0000_0000_FF00;
                    sb_ram_wr_data = {48'd0,mem_wr_data[7:0],8'd0};
                end
                3'd2:begin 
                    sb_ram_wmask   = 64'h0000_0000_00FF_0000;
                    sb_ram_wr_data = {40'd0,mem_wr_data[7:0],16'd0};
                end
                3'd3:begin 
                    sb_ram_wmask   = 64'h0000_0000_FF00_0000;
                    sb_ram_wr_data = {32'd0,mem_wr_data[7:0],24'd0};
                end
                3'd4:begin 
                    sb_ram_wmask   = 64'h0000_00FF_0000_0000;
                    sb_ram_wr_data = {24'd0,mem_wr_data[7:0],32'd0};
                end
                3'd5:begin 
                    sb_ram_wmask   = 64'h0000_FF00_0000_0000;
                    sb_ram_wr_data = {16'd0,mem_wr_data[7:0],40'd0};
                end
                3'd6:begin 
                    sb_ram_wmask   = 64'h00FF_0000_0000_0000;
                    sb_ram_wr_data = {8'd0,mem_wr_data[7:0],48'd0};
                end
                3'd7:begin 
                    sb_ram_wmask   = 64'hFF00_0000_0000_0000;
                    sb_ram_wr_data = {mem_wr_data[7:0],56'd0};
                end
            endcase
        end
        else begin
            sb_ram_wmask   = `ZERO_WORD;
            sb_ram_wr_data = `ZERO_WORD;
        end
    end

    // 写双字的处理
    always @(sh_signal or mem_addr[2:1] or mem_wr_data[15:0]) begin
        if(sh_signal)begin
            case(mem_addr[2:1])
                2'd0:begin  
                    sh_ram_wmask   = 64'h0000_0000_0000_FFFF;
                    sh_ram_wr_data = {48'd0,mem_wr_data[15:0]};
                end
                2'd1:begin  
                    sh_ram_wmask   = 64'h0000_0000_FFFF_0000;
                    sh_ram_wr_data = {32'd0,mem_wr_data[15:0],16'd0};
                end
                2'd2:begin  
                    sh_ram_wmask   = 64'h0000_FFFF_0000_0000;
                    sh_ram_wr_data = {16'd0,mem_wr_data[15:0],32'd0};
                end
                2'd3:begin  
                    sh_ram_wmask   = 64'hFFFF_0000_0000_0000;
                    sh_ram_wr_data = {mem_wr_data[15:0],48'd0};
                end
            endcase
        end
        else begin
            sh_ram_wmask   = `ZERO_WORD;
            sh_ram_wr_data = `ZERO_WORD;
        end
    end

    // 4字节写
    always @(sw_signal or mem_addr[2] or mem_wr_data[31:0]) begin
        if(sw_signal)begin
            case(mem_addr[2])
                1'b0:begin  
                    sw_ram_wmask   = 64'h0000_0000_FFFF_FFFF;
                    sw_ram_wr_data = {32'd0,mem_wr_data[31:0]};
                end
                1'b1:begin  
                    sw_ram_wmask   = 64'hFFFF_FFFF_0000_0000;
                    sw_ram_wr_data = {mem_wr_data[31:0],32'd0};
                end
            endcase
        end
        else begin
            sw_ram_wmask   = `ZERO_WORD;
            sw_ram_wr_data = `ZERO_WORD;
        end
    end

    // 8字节写
    always @(sd_signal or mem_wr_data) begin
        if(sd_signal) begin
            sd_ram_wmask   = 64'hFFFF_FFFF_FFFF_FFFF;
            sd_ram_wr_data = mem_wr_data;
        end
        else begin
            sd_ram_wmask   = `ZERO_WORD;
            sd_ram_wr_data = `ZERO_WORD;
        end
    end

    // 读字节处理
    always @(lb_signal or mem_addr[2:0] or ram_rd_data) begin
        if(lb_signal)
            case(mem_addr[2:0])
                3'd0:lb_ram_rd_data = {56'd0,ram_rd_data[ 7: 0]};
                3'd1:lb_ram_rd_data = {56'd0,ram_rd_data[15: 8]};
                3'd2:lb_ram_rd_data = {56'd0,ram_rd_data[23:16]};
                3'd3:lb_ram_rd_data = {56'd0,ram_rd_data[31:24]};
                3'd4:lb_ram_rd_data = {56'd0,ram_rd_data[39:32]};
                3'd5:lb_ram_rd_data = {56'd0,ram_rd_data[47:40]};
                3'd6:lb_ram_rd_data = {56'd0,ram_rd_data[55:48]};
                3'd7:lb_ram_rd_data = {56'd0,ram_rd_data[63:56]};
            endcase
        else 
            lb_ram_rd_data = `ZERO_WORD;
    end

    // 读双字节
    always @(lh_signal or mem_addr[2:1] or ram_rd_data) begin
        if(lh_signal)
            case(mem_addr[2:1])
                2'd0:lh_ram_rd_data = {48'd0,ram_rd_data[15: 0]};
                2'd1:lh_ram_rd_data = {48'd0,ram_rd_data[31:16]};
                2'd2:lh_ram_rd_data = {48'd0,ram_rd_data[47:32]};
                2'd3:lh_ram_rd_data = {48'd0,ram_rd_data[63:48]};
            endcase
        else
            lh_ram_rd_data = `ZERO_WORD;
    end
    // 读四字节
    always @(lw_signal or mem_addr[2] or ram_rd_data) begin
        if(lw_signal)
            case(mem_addr[2])
            1'b0:lw_ram_rd_data = {32'd0,ram_rd_data[31: 0]};
            1'b1:lw_ram_rd_data = {32'd0,ram_rd_data[63:32]};
            endcase
        else
            lw_ram_rd_data = `ZERO_WORD;
    end

    // 读八字节
    always @(ld_signal or ram_rd_data) begin
        if(ld_signal)
            ld_ram_rd_data = ram_rd_data;
        else
            ld_ram_rd_data = `ZERO_WORD;
    end

endmodule
codefuturedalao commented 3 years ago

求问如果流水线的话是不是要把wb阶段的pc接入cmt_pc,那么如果发生控制冒险清零PC,difftest报错pc不一致的问题如何处理呢,我的思路是能不能发生冒险的时候不让difftest继续比较pc的值,直到正常指令执行到wb阶段再进行比较(已经试过设置skip信号的方法,但是没有用)

YouTheFreedom1999 commented 3 years ago

求问如果流水线的话是不是要把wb阶段的pc接入cmt_pc,那么如果发生控制冒险清零PC,difftest报错pc不一致的问题如何处理呢,我的思路是能不能发生冒险的时候不让difftest继续比较pc的值,直到正常指令执行到wb阶段再进行比较(已经试过设置skip信号的方法,但是没有用)

写回阶段的pc,你指的是计算出的跳转地址,还是执行到写回阶段跳转指令所在的地址呢

codefuturedalao commented 3 years ago

求问如果流水线的话是不是要把wb阶段的pc接入cmt_pc,那么如果发生控制冒险清零PC,difftest报错pc不一致的问题如何处理呢,我的思路是能不能发生冒险的时候不让difftest继续比较pc的值,直到正常指令执行到wb阶段再进行比较(已经试过设置skip信号的方法,但是没有用)

写回阶段的pc,你指的是计算出的跳转地址,还是执行到写回阶段跳转指令所在的地址呢

我用的是wb_pc和cmt_pc连接,也就是执行到写回阶段指令所在的地址。当出现控制冒险时,我flush了一些寄存器(包括PC,就算不flush,pc也是错误的,所以不如都flush了🤣),之后这些气泡执行到wb阶段时,wb_pc 为0,这时候difftest就会给我报错,表示this_pc出现错误,right_pc为目标指令跳转地址,wrong_pc 为000000000,但其实我跳转正确了,只是指令还没有执行到wb阶段,所以想跳过那些对于气泡的比较,不知道怎么办😂

doubletpy commented 3 years ago

我想请问 1.为什么ram读数据时地址要减去64'h0000_0000_8000_0000 2.ram的代码中读数据时不需要考虑是lb,lh,lw等等的这些吗,我看是直接读64位的

codefuturedalao commented 3 years ago

我想请问 1.为什么ram读数据时地址要减去64'h0000_0000_8000_0000 2.ram的代码中读数据时不需要考虑是lb,lh,lw等等的这些吗,我看是直接读64位的

  1. 因为RAM中的数据是加载的objdump从elf可执行文件中生成的bin文件,可执行文件中从虚拟地址64'h0000_0000_8000_0000开始重定位,分配地址,而bin文件中将指令和数据从0开始放置。举个例子,int a = b[0]假设需要lw指令将b[0]的数据从内存中加载到寄存器里面,当我们生成可执行文件后,在链接的时候会进行重定位,根据规范,编译器会假设这个程序放置在从虚拟地址64'h0000_0000_8000_0000开始的一段空间,然后我们根据b[0]在数据段中的偏移和64‘h8000_0000这两个数可以计算b[0]的虚拟地址,最后填写到lw指令中,当你自己写的CPU运行到该指令时,会很自然地想要读取RAM这个地址(大于0x8000_0000)的数据,本来按照正常情况,这个地址确实应该有你想要的数据,但是我们bin文件是假设数据从0开始放置的,所以就需要你转换一下,减去0x8000_0000。
  2. 所以需要根据lb、lh和lw生成对应的控制信号——mask掩码来选取64位数据中自己想要的数据,当你编写Axi总线的时候,也要提供这样一个信号的。
lsyic commented 3 years ago

请问difftest是只能对比指令和通用寄存器的差异吗?写到RAM中的数据是否正确能否对比出来呢?

codefuturedalao commented 3 years ago

请问difftest是只能对比指令和通用寄存器的差异吗?写到RAM中的数据是否正确能否对比出来呢?

image 是的,如上图所示,左边是difftest进行比较的函数,比较的内容是自己CPU中寄存器和NEMU中寄存器的值,具体寄存器如图右边所示,是32个gpr和一些csr寄存器,所以store指令的正确性是没办法比较出来的,但是可以根据后面的load指令来判断,如果store向一个错误地址或者正确地址写入了错误数据,后面的load取相应的数据到寄存器时自然会被difftest判断出错。QQ群里面好像有个同学给出了验证store指令的正确性的教程,可以翻一下聊天记录。 欢迎指正。

lsyic commented 3 years ago

请问difftest框架内的ram是不能支持既取指又读内存中的数据吗?必须要自己重新改写ram吗?

codefuturedalao commented 3 years ago

请问difftest框架内的ram是不能支持既取指又读内存中的数据吗?必须要自己重新改写ram吗?

应该是,这个issue也给了一写两读的RAM模块供使用

CleanerNo7 commented 3 years ago

同学你好,我参照你的ram模块和mem_ctrl模块接入了simtop模块,也能正常difftest,但是在ld指令显示出错 第64条指令是lh指令能正常通过,应该只有ld指令出错 800001bc: 02813083 ld ra,40(sp) x[rd] = M[x[rs1] + sext(offset)][63:0] 能识别是ld指令,应该是读内存时的问题。但查看了各个接口的数据应该是正确的,但又无法查看内存里的数据。。 查看之前的load指令,有效访问的内存地址都是后四位0xxx的,只有这条ld指令访问的是后四位8FE8, 所以怀疑是地址的问题,但根据指令查看sp,offset也是正确的。所以很奇怪,也不知道是哪里出了问题。。

Capture 2

codefuturedalao commented 3 years ago

同学你好,我参照你的ram模块和mem_ctrl模块接入了simtop模块,也能正常difftest,但是在ld指令显示出错 第64条指令是lh指令能正常通过,应该只有ld指令出错 800001bc: 02813083 ld ra,40(sp) x[rd] = M[x[rs1] + sext(offset)][63:0] 能识别是ld指令,应该是读内存时的问题。但查看了各个接口的数据应该是正确的,但又无法查看内存里的数据。。 查看之前的load指令,有效访问的内存地址都是后四位0xxx的,只有这条ld指令访问的是后四位8FE8, 所以怀疑是地址的问题,但根据指令查看sp,offset也是正确的。所以很奇怪,也不知道是哪里出了问题。。

Capture 2

我刚才看了看load-store-riscv64-mycpu.txt中的汇编指令,同时也跑了一遍看了一下波形,此条指令的地址的后四位的确是8FE8,load指令如果不是自己计算地址和控制信号有错误的话,应该是前面sd的时候存了错误数据或者向错误地址写入了数据,请检查 image 这条指令的写入结果,希望对你有所帮助。

CleanerNo7 commented 3 years ago

同学你好,我参照你的ram模块和mem_ctrl模块接入了simtop模块,也能正常difftest,但是在ld指令显示出错 第64条指令是lh指令能正常通过,应该只有ld指令出错 800001bc: 02813083 ld ra,40(sp) x[rd] = M[x[rs1] + sext(offset)][63:0] 能识别是ld指令,应该是读内存时的问题。但查看了各个接口的数据应该是正确的,但又无法查看内存里的数据。。 查看之前的load指令,有效访问的内存地址都是后四位0xxx的,只有这条ld指令访问的是后四位8FE8, 所以怀疑是地址的问题,但根据指令查看sp,offset也是正确的。所以很奇怪,也不知道是哪里出了问题。。 Capture 2

我刚才看了看load-store-riscv64-mycpu.txt中的汇编指令,同时也跑了一遍看了一下波形,此条指令的地址的后四位的确是8FE8,load指令如果不是自己计算地址和控制信号有错误的话,应该是前面sd的时候存了错误数据或者向错误地址写入了数据,请检查 image 这条指令的写入结果,希望对你有所帮助。

好的,非常感谢大佬🙇‍

lsyic commented 3 years ago

请问如何编译及运行cpu_test下的全部验证程序呢?

codefuturedalao commented 3 years ago

请问如何编译及运行cpu_test下的全部验证程序呢?

image

lsyic commented 3 years ago

请问如何编译及运行cpu_test下的全部验证程序呢?

image

多谢多谢!请问这个链接是在哪里?

codefuturedalao commented 3 years ago

请问如何编译及运行cpu_test下的全部验证程序呢?

image

多谢多谢!请问这个链接是在哪里?

https://github.com/OSCPU/oscpu-framework repo的readme里面

lsyic commented 3 years ago

请问如何编译及运行cpu_test下的全部验证程序呢?

image

多谢多谢!请问这个链接是在哪里?

https://github.com/OSCPU/oscpu-framework repo的readme里面

多谢多谢!