oscpu / ysyx Goto Github PK
View Code? Open in Web Editor NEW一生一芯的信息发布和内容网站
一生一芯的信息发布和内容网站
按照教程编译OSCPU/rt-thread之后,把elf文件dump了一下,发现有如下指令。
80008f74: 9e850513 addi a0,a0,-1560 # 80016958 <rt_data_queue_deinit+0x12e8>
80008f78: e3dfd0ef jal ra,80006db4 <rt_kprintf>
80008f7c: 180027f3 csrr a5,satp
80008f80: fef43023 sd a5,-32(s0)
80008f84: fe043783 ld a5,-32(s0)
查阅指令集文档得知satp是supervisor特权级的CSR,是否意味着需要对rt-thread代码做进一步修改?
讨论区目前还在试验阶段,后续会对提问 --> 解答的流程进行优化。
每个问题请开一个新的issue,issue 的标题和内容可以使用中文。
提交 issue 的界面右侧可以为 issue 添加标签(Label),请从预先定义好的标签中选择匹配的项目对您的 issue 进行标记。例如,本篇 issue 的类型为 知识分享
,话题是 Policy&Schedule
。
您还可以为您的问题指定(Assign)助教以获得快速解答,我们稍后会列出助教团队成员的 GitHub ID 以及擅长方向以供参考。
无论是交流类 issue 还是求助类 issue,所有人均可参与讨论和解答。我们的助教团队也会尽可能对您的 issue 作出回应。
讨论区的目的是最大化知识分享的效率,减少重复问题。因此,在提问前请先搜索已有的 issue ,确定该问题没有被提出过。
对于 需要帮助
类型的问题,请尽可能包含详细的代码信息、运行环境和报错信息,以便试图提供帮助的同学复现你的问题。
请勿在本仓库提交与 “一生一芯” 项目无关的 issue !
昨天看到有老哥写了Chisel的difftest接入,心血来潮想试试Verilog怎么接入。
一定要参考Chisel的difftest接入、DiffTest Usge这两篇,我算是一点点补充
水平有限,请各位多多指教。
首先我们要在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
对于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
);
对应模块名为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代码
由于chisel导出的verilog没有数组,所以chisel导出的verilog代码可能无法直接用于difftest仿真。
例如,直接用verilog描述的信号为:
Output [7:0] test [3:0];
使用chisel的描述语句为
val test = Vec(4, UInt(8.W))
将该代码生成verilog,声明的端口自动会转换为
...
output [7:0] test_0,
output [7:0] test_1,
output [7:0] test_2,
output [7:0] test_3,
...
最简单的解决方法是直接在生成的Verilog中对不符合接口规范的语句进行替换。
这是我用Python创建的修改脚本replace.py
。
其中的几个变量str_tobe_replace_1 str_tobe_replace_2 str_tobe_replace_3
需要根据你的实际代码进行一定的修改。
# 源文件路径(默认导出的文件覆盖原文件)
file_path = "./generated/oscpu2/SimTop.v"
str_tobe_replace_1 = ''' output [63:0] io_memAXI_0_w_bits_data_0,
output [63:0] io_memAXI_0_w_bits_data_1,
output [63:0] io_memAXI_0_w_bits_data_2,
output [63:0] io_memAXI_0_w_bits_data_3,'''
str_new_1 = ''' output [63:0] io_memAXI_0_w_bits_data [3:0], // !!! generated by python'''
str_tobe_replace_2 = ''' input [63:0] io_memAXI_0_r_bits_data_0,
input [63:0] io_memAXI_0_r_bits_data_1,
input [63:0] io_memAXI_0_r_bits_data_2,
input [63:0] io_memAXI_0_r_bits_data_3,'''
str_new_2 = ''' input [63:0] io_memAXI_0_r_bits_data [3:0], // !!! generated by python'''
str_tobe_replace_3 = ''' wire [63:0] axi_mem_io_mem_data_read; '''
str_new_3 = ''' wire [63:0] axi_mem_io_mem_data_read;
wire [63:0] io_memAXI_0_w_bits_data_0;
wire [63:0] io_memAXI_0_w_bits_data_1;
wire [63:0] io_memAXI_0_w_bits_data_2;
wire [63:0] io_memAXI_0_w_bits_data_3;
assign io_memAXI_0_w_bits_data[0] = io_memAXI_0_w_bits_data_0 ;
assign io_memAXI_0_w_bits_data[1] = io_memAXI_0_w_bits_data_1 ;
assign io_memAXI_0_w_bits_data[2] = io_memAXI_0_w_bits_data_2 ;
assign io_memAXI_0_w_bits_data[3] = io_memAXI_0_w_bits_data_3 ;
wire [63:0] io_memAXI_0_r_bits_data_0;
wire [63:0] io_memAXI_0_r_bits_data_1;
wire [63:0] io_memAXI_0_r_bits_data_2;
wire [63:0] io_memAXI_0_r_bits_data_3;
assign io_memAXI_0_r_bits_data_0 = io_memAXI_0_r_bits_data[0];
assign io_memAXI_0_r_bits_data_1 = io_memAXI_0_r_bits_data[1];
assign io_memAXI_0_r_bits_data_2 = io_memAXI_0_r_bits_data[2];
assign io_memAXI_0_r_bits_data_3 = io_memAXI_0_r_bits_data[3];
'''
file_content = ""
with open(file_path, "r") as f:
file_content = f.read()
file_content = file_content.replace(str_tobe_replace_1, str_new_1)
file_content = file_content.replace(str_tobe_replace_2, str_new_2)
file_content = file_content.replace(str_tobe_replace_3, str_new_3)
with open(file_path, "w+") as f:
f.write(file_content)
同时为了方便将处理好的verilog源文件复制到进行difftest的仿真路径,可以使用下面的shell脚本,一键编译verilog和修改源码(需要根据实际目录修改一些路径)
。
sbt "test:runMain oscpu2.verilog_TopMain_oscpu2"
python replace.py
sed -i '1i\`timescale 1ns / 10ps' ./generated/oscpu2/SimTop.v
cp ./generated/oscpu2/SimTop.v ../projects/czcpu_axi_diff/vsrc/SimTop.v
描述:在调试过程中,遇到信号值不正确,单从emu程序中看不出问题的时候,可以考虑启用波形仿真。
verilator.mk
:定位到以下代码段(关键字:trace
)"你的项目名字"/difftest/verilator.mk
# Verilator trace support
EMU_TRACE ?= 1 #==============>>这一行<<==============#
ifeq ($(EMU_TRACE),1)
VEXTRA_FLAGS += --trace
endif
EMU_TRACE ?=
修改为 EMU_TRACE ?= 1
,并保存
emu.h
"你的项目名字"/difftest/src/test/csrc/verilator/emu.h
#define VM_TRACE 1
"你的项目名字"/difftest
目录执行以下命令,生成新的emu
程序make emu
"你的项目名字"/build
目录,执行以下命令(将emu
和*.bin
文件拷贝到其他地方执行也可以,只要你知道这两个文件的路径)./emu -i add-riscv64-mycpu.bin
Guest cycle spent: 22
("22"是例子具体要看你自己的程序)
SimTop.v
中接入 “DifftestTrapEvent ”模块./emu -h
Guest cycle
填入0和22,表示全程记录./emu -b 0 -e 22 --dump-wave -i add-riscv64-mycpu.bin
"你的项目名字"/build
目录下会生成以时间命名的*.vcd
文件*.vcd
文件,添加一些信号,即可查看仿真波形我看第一个add的demo编译出来就包含了很多不同指令,不只有addi指令还有跳转、加载等指令。是不是使用AM进行验证需要实现所有基础指令?同样是验证,AM和difftest有关系吗?请多多指教!!!
mstatus different at pc = 0x00800005f4, right= 0x0000000000000088, wrong = 0x0000000000001888
执行mret指令时,在我们的实现里始终保持在machine mode,但是difftest给出的默认实现是返回到user mode(见mstatus中的mpp field),需要在difftest中修改源码吗?具体是在哪里修改?
(QQ群中有人提出类似问题但是聊天记录中没有具体回答)
在 difftest 讲座中提供的 difftest 和配套的 NEMU 来自于香山处理器配套的开发环境. 近期, 香山主线所使用的 difftest 和 NEMU 经历了一次更新, NEMU 提供的 difftest API 发生了变化. 需要同时更新 NEMU 和 difftest 的版本才能让 difftest 正确运行.
请注意: NEMU 和 difftest 的版本需要匹配, 否则 difftest 无法正确运行. 我们提供以下两个可用的版本:
这些分支上的代码会比较稳定. 这也是 difftest 讲座 时给大家提供的版本.
https://github.com/OpenXiangShan/difftest/tree/ysyx
https://github.com/OpenXiangShan/NEMU/tree/ysyx
想尝鲜的同学可以更新到 master 分支的 difftest / NEMU. 这一分支上的代码是目前香山处理器开发所使用的版本.
https://github.com/OpenXiangShan/NEMU
https://github.com/OpenXiangShan/difftest
这一版本的 NEMU 编译命令相比之前的版本发生了变化: 参见 新NEMU临时使用指南
这一仓库下的 readme 介绍了如何使用香山的开发环境:
https://github.com/OpenXiangShan/xs-env
https://ysyx.oscc.cc/docs/ics-pa/1.4.html#%E6%89%AB%E6%8F%8F%E5%86%85%E5%AD%98
ICS PA1.4 最后一个红色警告段落“ 我怕代码写错了啊, 怎么办?”的第一句
2014年图灵奖得主Michael Stonebraker在一次访谈中提到
“访谈”所指的链接 http://idke.ruc.edu.cn/resources/Sigmod%20Record_cn/Michael%20Stonebraker_cn.pdf 已404
我个人使用 chisel
编写项目,并且使用 mill
作为构建工具。
使用 verilog
编写或者使用 sbt
构建工具大同小异。
如果在操作过程有出现本文章未提及的问题,欢迎在讨论区提出。你提出的问题很可能能帮助到别人。
difftest
项目和所需依赖主要为 difftest
项目 和 NEMU
模拟器。
其中,difftest
项目必须在 chisel
项目根目录下,NEMU
项目不限制位置,但是必须设置 NEMU_HOME
环境变量为 NEMU
所在目录。
此外,还需要设置 NOOP_HOME
为项目根目录(参考 verilog.mk
,虽然不会使用这个功能,但是这个变量必须要有)。
我个人的项目树大致如下,不要求完全一致,符合上述要求即可。
/home/xxx/
chisel-template/
difftest/
src/
build.sc
NEMU/
因此 NEMU_HOME
环境变量应该设置为 /home/xxx/NEMU
,NOOP_HOME
环境变量应该设置为 /home/xxx/chisel-template
。
(正式开始前建议执行 git commit -am --allow-empty "before difftest"
保证接入失败后可以随时回退回原来的状态)
SimTop.v
由于 difftest
框架本身由 verilog
写成,仅使用 chisel
编写的项目需要此步骤。使用 verilog
编写的项目可以直接跳过此步骤,但是必须保证 build/SimTop.v
是 CPU 的顶级文件且具有 规定的端口。
我的 SimTop.v
实现如下:
package sim
import chisel3._
import rvcore._
import difftest._
class SimTop extends Module {
// 规定的端口格式,不多不少,但可以暂时不使用。
val io = IO(new Bundle {
val logCtrl = new LogCtrlIO
val perfInfo = new PerfInfoIO
val uart = new UARTIO
})
val rvcore = Module(new RvCore)
// rvcore 访问内存的端口,下个步骤会说明,此处暂不连接
rvcore.io.ram.rdata := 0.U
// 暂不使用 difftest 给出的端口,但是还是需要初始化合法值
io.uart.in.valid := false.B
io.uart.out.valid := false.B
io.uart.out.ch := 0.U
}
由于需要使用 difftest
模块下的文件,需要指示 mill
difftest
模块的位置。修改 build.sc
如下(sbt
需修改 build.sbt
,两个文件不通用)
// import Mill dependency
import mill._
import mill.scalalib._
import mill.scalalib.scalafmt.ScalafmtModule
import mill.scalalib.TestModule.ScalaTest
// support BSP
import mill.bsp._
object ysyx extends SbtModule { m =>
override def millSourcePath = os.pwd
override def scalaVersion = "2.12.13"
override def scalacOptions = Seq(
"-Xsource:2.11",
"-language:reflectiveCalls",
"-deprecation",
"-feature",
"-Xcheckinit",
// Enables autoclonetype2 in 3.4.x (on by default in 3.5)
"-P:chiselplugin:useBundlePlugin"
)
override def ivyDeps = Agg(
ivy"edu.berkeley.cs::chisel3:3.4.3"
)
override def scalacPluginIvyDeps = Agg(
ivy"edu.berkeley.cs:::chisel3-plugin:3.4.3",
ivy"org.scalamacros:::paradise:2.1.1"
)
object test extends Tests with ScalaTest {
override def ivyDeps = m.ivyDeps() ++ Agg(
ivy"edu.berkeley.cs::chiseltest:0.3.3"
)
}
override def moduleDeps = super.moduleDeps ++ Seq(difftest)
}
object difftest extends ScalaModule {
override def scalaVersion = "2.12.13"
override def millSourcePath = os.pwd / "difftest"
override def ivyDeps = Agg(
ivy"edu.berkeley.cs::chisel3:3.4.3"
)
}
此时,mill
可以执行以下命令生成 build/SimTop.v
mill -i __.test.runMain top.TopMain -td ./build
其中,top.TopMain
是本 chisel
项目的顶层文件,形如
package top
import chisel3.stage._
import sim._
object TopMain extends App {
(new ChiselStage).execute(
args,
Seq(
ChiselGeneratorAnnotation(() => new SimTop())
)
)
}
而 SimTop
是编写的 CPU
模块。
运行上述命令后,期望能找到 build/SimTop.v
。
difftest
程序 build/emu
在项目根目录中执行以下命令即可构建 build/emu
make -C difftest emu
在准备好上述文件后,理论上就算不做任何修改,在没有接入 difftest
的情况下期望能生成 build/emu
文件(可能会有大量输出,成功与否以 build/emu
文件是否存在为准)。如果无法生成,请检查是否做好上述准备,如是否已配置正确的 NEMU_HOME
环境变量、build/SimTop.v
是否是 CPU 模块的顶层文件等。
此时,运行 build/emu --help
应该能够看到帮助文档。
$ build/emu --help
Emu compiled at Jul 21 2021, 22:18:14
Usage: build/emu [OPTION...]
-s, --seed=NUM use this seed
-C, --max-cycles=NUM execute at most NUM cycles
-I, --max-instr=NUM execute at most NUM instructions
-W, --warmup-instr=NUM the number of warmup instructions
-D, --stat-cycles=NUM the interval cycles of dumping statistics
-i, --image=FILE run with this image file
-b, --log-begin=NUM display log from NUM th cycle
-e, --log-end=NUM stop display log at NUM th cycle
--force-dump-result force dump performance counter result in the end
--load-snapshot=PATH load snapshot from PATH
--no-snapshot disable saving snapshots
--dump-wave dump waveform when log is enabled
--no-diff disable differential testing
--diff=PATH set the path of REF for differential testing
-h, --help print program help info
任意镜像文件均可,可以使用 AbstractMachine
编译 cpu-test
作为镜像(需要实现几乎全部 rv64i
指令才能正常运行),也可以自己编写并生成 stripped
的纯二进制文件。该文件会作为 difftest
过程中执行的程序。
也可以使用框架里的 inst.bin 替代。
以下使用 $IMG
代指该镜像文件。
此时执行 build/emu -i $IMG
,期望看到以下输出。
$ build/emu -i ../oscpu-framework/cpu/inst.bin
Emu compiled at Jul 21 2021, 22:18:14
The image is ../oscpu-framework/cpu/inst.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 /home/lh/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
No instruction commits for 5000 cycles of core 0. Please check the first instruction.
Note: The first instruction may lie in 0x10000000 which may executes and commits after 500 cycles.
Or the first instruction may lie in 0x80000000 which may executes and commits after 2000 cycles.
============== Commit Group Trace (Core 0) ==============
commit group [0]: pc 0000000000 cmtcnt 0
commit group [1]: pc 0000000000 cmtcnt 0
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 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
commit inst [1]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
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: 0x0000000000000000 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: 0x0000000080000000 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
Core 0: ABORT at pc = 0x0
total guest instructions = 0
instrCnt = 0, cycleCnt = 0, IPC = -nan
Seed=0 Guest cycle spent: 5001 (this will be different from cycleCnt if emu loads a snapshot)
Host time spent: 6ms
在前几行输出可以看出,由于我们还没有接入 difftest
,框架无法获取我们 CPU 的执行状态。因此接下来需要修改 CPU
模块来接入 difftest
框架。
我们的 CPU 需要获取执行的指令以及运行 load/store
指令。原先这些数据由 verilator
中的测试框架给出,现在需要将其转接到 difftest
框架中。
difftest
框架通过 RAMHelper
向 CPU 提供读写信息。其定义在 ram.v
中。我们需要做的就是在 CPU 中实例化这个 RAMHelper
模块并且按照其定义连接对应信号。
需要注意的是,RAMHelper
读写都仅以 8字节 为一个单位,也即,当rIdx
为 1
时,实际读取了 0x80000008
开始的八个字节。写入同理。
正确接入后,期望在 CPU 运行过程中可以正确读取对应地址的信息。正确性可以通过在代码中打印日志人工判断。
我个人在 SimTop.v
中的实现如下:
class RAMHelper extends BlackBox {
val io = IO(new Bundle {
val clk = Input(Clock())
val en = Input(Bool())
val rIdx = Input(UInt(64.W))
val rdata = Output(UInt(64.W))
val wIdx = Input(UInt(64.W))
val wdata = Input(UInt(64.W))
val wmask = Input(UInt(64.W))
val wen = Input(Bool())
})
}
val ram = Module(new RAMHelper)
val rvcore = Module(new RvCore)
ram.io.clk := clock
ram.io.en := rvcore.io.ram.en
ram.io.rIdx := rvcore.io.ram.raddr - (BigInt("80000000", 16) >> 3).U
ram.io.wIdx := rvcore.io.ram.waddr - (BigInt("80000000", 16) >> 3).U
ram.io.wdata := rvcore.io.ram.wdata
ram.io.wmask := rvcore.io.ram.wmask
ram.io.wen := rvcore.io.ram.wen
rvcore.io.ram.rdata := ram.io.rdata
我们需要通过一个方式告诉 difftest
框架已经完成了某条指令的执行。该功能通过 DifftestInstrCommit 实现。verilog
中对应的模块是 difftest.v
。
具体而言,需要在写回阶段,实例化 DifftestInstrCommit
并且连接对应的信号。
我个人的实现大概如下:
val commit = Module(new difftest.DifftestInstrCommit)
commit.io.clock := clock
commit.io.coreid := 0.U
commit.io.index := 0.U
commit.io.valid := RegNext(io.in_valid && !io.stall)
commit.io.pc := RegNext(io.in.pc)
commit.io.instr := DontCare
commit.io.skip := false.B
commit.io.isRVC := false.B
commit.io.scFailed := false.B
commit.io.wen :=
RegNext(io.in_valid && io.in.wb.rd =/= 0.U)
commit.io.wdata := RegNext(io.in.wb.data)
commit.io.wdest := RegNext(io.in.wb.rd)
其中,我没有使用 RegNext 的信号是暂时不需要管的,和我一致即可。其余使用了 RegNext 的信号需要替换成你们对应的实现。(端口含义通过命名可以猜出来很多了)
此时,执行 mill -i __.test.runMain top.TopMain -td ./build
、make -C difftest emu
和 build/emu -i $IMG
,期望 difftest
能够正确识别出第一条指令的执行。但是由于其余数据我们还未提供给 difftest
框架,所以极可能第一条指令就会被判错,这是正常现象。
该阶段正确性的判定可以通过是否输出了 The first instruction of core 0 has commited. Difftest enabled.
判断。
我个人在该阶段的输出如下:
$ build/emu -b 0 -i ../oscpu-framework/cpu/inst.bin
Emu compiled at Jul 21 2021, 23:00:47
The image is ../oscpu-framework/cpu/inst.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 /home/lh/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
Received commit pc=80000000
The first instruction of core 0 has commited. Difftest enabled.
============== Commit Group Trace (Core 0) ==============
commit group [0]: pc 0080000000 cmtcnt 1 <--
commit group [1]: pc 0000000000 cmtcnt 0
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 00000000 wen 1 dst 00000001 data 0000000000000001 <--
commit inst [1]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000
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: 0x0000000000000001 sp: 0x0000000000000000 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: 0x0000000080000004 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
ra different at pc = 0x0080000000, right= 0x0000000000000001, wrong = 0x0000000000000000
Core 0: ABORT at pc = 0x0
total guest instructions = 0
instrCnt = 0, cycleCnt = 0, IPC = -nan
Seed=0 Guest cycle spent: 5 (this will be different from cycleCnt if emu loads a snapshot)
Host time spent: 4ms
从错误信息中,可以看到期望的 Difftest enabled
,说明 InstrCommit
正确接入了。并且可以看到在 ==Commit Instr Trace==
中我的CPU正确提交了第一条指令,但是由于 ra
寄存器的错误终止了 difftest
。这个问题在下个阶段会修复。
difftest
框架主要比对我们 CPU 的寄存器值和 NEMU 模拟器的寄存器值来判定实现的正确性。该功能通过 DifftestArchIntRegState
实现。verilog
也有对应的模块。
具体而言,需要在 寄存器组 模块中,实例化 DifftestArchIntRegState
并连接对应的信号。
我个人的实现大概如下:
// 这是我原先的寄存器实现,不需要和我一致
val reg = RegInit(VecInit(Seq.fill(32)(0.U(c.XLEN.W))))
// 以下为接入 difftest 新增内容
val mod = Module(new difftest.DifftestArchIntRegState)
mod.io.clock := clock
mod.io.coreid := 0.U
mod.io.gpr := reg
此外,difftest
在运行时还会对比 CSR
寄存器的值。由于我们此时并未实现,直接硬编码即可。(但是不能留空,否则会出现非合法值导致 NEMU
异常)
val csr = Module(new difftest.DifftestCSRState)
csr.io.clock := clock
csr.io.coreid := 0.U
csr.io.mstatus := 0.U
csr.io.mcause := 0.U
csr.io.mepc := 0.U
csr.io.sstatus := 0.U
csr.io.scause := 0.U
csr.io.sepc := 0.U
csr.io.satp := 0.U
csr.io.mip := 0.U
csr.io.mie := 0.U
csr.io.mscratch := 0.U
csr.io.sscratch := 0.U
csr.io.mideleg := 0.U
csr.io.medeleg := 0.U
csr.io.mtval:= 0.U
csr.io.stval:= 0.U
csr.io.mtvec := 0.U
csr.io.stvec := 0.U
csr.io.priviledgeMode := 0.U
此时,再次执行 mill -i __.test.runMain top.TopMain -td ./build
、make -C difftest emu
和 build/emu -i $IMG
,此时期望能够正确执行 difftest
了。(此时的表现为 difftest
没有任何输出)
但是由于我们还未实现 Trap
通知 difftest
框架我们的程序已经终止,difftest
将会一直执行下去。(因为在 AM
中 halt
的实现暂时是一个死循环)
在 NJU-ProjectN
中告知框架 “程序已运行结束” 的方式是发送一个 Trap
事件。在 difftest
框架中也是如此。在没有修改的情况下,如果使用 NEMU
运行 AM
编译的程序,会出现程序运行结束之后仍然无法退出的情况。该行为在 trap.c
中定义。
$NEMU_HOME/build/riscv64-nemu-interpreter -b am-kernels/tests/cpu-tests/build/add-riscv64-mycpu.bin
可以看到,无论等待多久,NEMU
的运行都不会停止。
要修改这个行为,让程序能够自动退出,只需要在 trap.c
的 halt
前加入一条特殊的指令即可。
void halt(int code) {
asm volatile("mv a0, %0; .word 0x0000006b" : :"r"(code));
while (1);
}
asm
语句在 halt
函数中添加了一条特殊指令 0x0000006b
。该指令在 NEMU
中的行为是终止执行,并将 a0
寄存器的值作为程序是否正常退出的标志。 a0
为 0 时表示时正常终止,否则代表异常终止。该行为定义在 exec/special.c
中。
此时重新编译 cpu-test
(可以使用 make clean
再 make ARCH=riscv64-mycpu
),并且再次使用 NEMU
运行该测试,可以发现 NEMU
可以正常终止,并输出 HIT GOOD TRAP
。
$ $NEMU_HOME/build/riscv64-nemu-interpreter -b ../am-kernels/tests/cpu-tests/build/recursion-riscv64-mycpu.bin
[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
[src/monitor/monitor.c,46,load_img] The image is ../am-kernels/tests/cpu-tests/build/recursion-riscv64-mycpu.bin
[src/monitor/monitor.c,29,welcome] Debug: OFF
[src/monitor/monitor.c,32,welcome] Build time: 15:45:21, Jul 23 2021
Welcome to riscv64-NEMU!
For help, type "help"
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'screen' at [0xa1000100, 0xa1000107]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'vmem' at [0xa0000000, 0xa00752ff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'serial' at [0xa10003f8, 0xa10003ff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'rtc' at [0xa1000048, 0xa1000057]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'keyboard' at [0xa1000060, 0xa1000063]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'audio' at [0xa1000200, 0xa1000217]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'audio-sbuf' at [0xa0800000, 0xa080ffff]
[src/monitor/cpu-exec.c,97,cpu_exec] nemu: HIT GOOD TRAP at pc = 0x0000000080000290
[src/monitor/cpu-exec.c,35,monitor_statistic] total guest instructions = 6552
接入 difftest
的 Trap
的流程与之前类似,只需要实例化对应的接口并连接信号即可。对应本次是 DifftestTrapEvent
模块。
暂时不需要区分 GoodTrap
和 BadTrap
,因此大部分内容都可以硬编码完成。此外,只需要保证你的 CPU
在识别到 0x0000006b
这个非标准的指令的时候不会出现异常行为即可。
我的实现如下:
val trap = Module(new DifftestTrapEvent)
trap.io.clock := clock
trap.io.coreid := c.CoreId.U
trap.io.valid := io.in.commit.instr === BigInt("0000006b", 16).U
trap.io.code := 0.U // GoodTrap
trap.io.pc := io.in.commit.pc
trap.io.cycleCnt := 0.U
trap.io.instrCnt := 0.U
此时再次执行 difftest
相关流程,可以看到 difftest
也最终输出了 Hit Good Trap
指示程序正确结束了。
Emu compiled at Jul 23 2021, 16:49:20
The image is ../am-kernels/tests/cpu-tests/build/fact-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 /home/lh/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
The first instruction of core 0 has commited. Difftest enabled.
Core 0: HIT GOOD TRAP at pc = 0x8000011c
total guest instructions = 0
instrCnt = 0, cycleCnt = 0, IPC = -nan
Seed=0 Guest cycle spent: 5529 (this will be different from cycleCnt if emu loads a snapshot)
Host time spent: 9ms
最后,附带一个能够一件测试 cpu-test
中所有指令的命令:
find ../am-kernels/tests/cpu-tests/build/ -name *.bin | xargs -i build/emu -i {}
如果一次性输出太多,可以考虑重定向到某个文件中然后查找 ABORT
关键词查看错误的样例。
遇到分支预测错误的情况,需要flush掉流水线,如何暂停difftest的指令提交,等到新的流水指令到来的时候再继续提交指令,以及difftest调用NEMU继续执行
我在对lw和sw指令进行测试,按照学长提供的测试代码自己写了一段汇编并翻译成2进制进行测试。
我在ram.cpp里面加入了printf信号打印出存储器,看到写入和读出的数据是正确的。
但是difftest报错显示,这个读出的数据和理想的寄存器数据不同?
通过分析可以发现理想的寄存器状态是0x0000000400000003,而读出的数据是0x0000000000000003。该pc对应的指令ld,即读取64位数据。那么基本可以判断是在对存储器进行读写时出现的问题。
RISCV的数据均为小端模式,即高位数据存储在存储器的大地址,低位数据存储在存储器的小地址。以下图为例,最高位字节是0x0A,最低位字节是0x0D。
打开difftest/src/test/csrc/common/ram.cpp
文件,找到ram_write_helper和ram_inst_helper函数。在这里,我添加了两行printf(已注释)来打印写入或者读出数据,来辅助调试。
extern "C" uint64_t ram_inst_helper(uint8_t en, uint64_t rIdx) {
if (!ram)
return 0;
if (en && rIdx >= EMU_RAM_SIZE / sizeof(uint64_t)) {
rIdx %= EMU_RAM_SIZE / sizeof(uint64_t);
}
pthread_mutex_lock(&ram_mutex);
uint64_t rdata = (en) ? ram[rIdx] : 0;
pthread_mutex_unlock(&ram_mutex);
// printf("Read\t rIdx: 0x%lx \t rdata: 0x%lx \n", rIdx, rdata );
return rdata;
}
extern "C" void ram_write_helper(uint64_t wIdx, uint64_t wdata, uint64_t wmask, uint8_t wen) {
if (wen && ram) {
if (wIdx >= EMU_RAM_SIZE / sizeof(uint64_t)) {
printf("ERROR: ram wIdx = 0x%lx out of bound!\n", wIdx);
assert(wIdx < EMU_RAM_SIZE / sizeof(uint64_t));
}
pthread_mutex_lock(&ram_mutex);
ram[wIdx] = (ram[wIdx] & ~wmask) | (wdata & wmask);
pthread_mutex_unlock(&ram_mutex);
// printf("Write\t wIdx: 0x%lx \t wdata: 0x%lx \t wmask: 0x%lx \n", wIdx, wdata, wmask);
}
}
分析代码可以发现,代码的读写都是以uint64_t进行的!!!所以在写入过程加入了一个64位的写入掩码wmask!!!
即,该存储器是按照64位的存储空间设置的,所以对应的rtl代码要做出相同的适配。
由于项目给出的例子中没有涉及到回写部分的操作,所以在这里我也是纠结了很久才修正了bug。
我使用的是chisel,给出我的访存部分的代码;对于verilog选手,我也会给出chisel生成的verilog代码。
大体上和chisel接入difftest经验贴的接入部分一致。需要特别注意的是( rvcore.io.mem_r_addr - "h80000000".U(64.W) ) >> 3.U
这里计算出来的地址要右移3位。主要原因在于存储器的每个存储地址对应的存储空间是64位的!!!最低的3位的地址是该存储地址内存储空间的索引。
假设低4位的地址为the_addr[3:0]
,存储器存储的64位数据为the_data[63:0]
。
那么对于lw
来说,the_addr[2]
就决定读取the_data
的高32位the_data[63:32]
还是低32位the_data[31:0]
。
同样对于lh
来说,the_addr[2:1]
就决定读取the_data
的高16位the_data[63:48]
或者是中间的the_data[47:32]
或the_data[31:16]
还是低16位the_data[15:0]
。
同样对于lb
来说,the_addr[2:0]
就决定读取the_data
的哪8位数据。
对于写入,也是相同的情况。
代码写的比较烂,没有进行优化。核心的**就是,写入或者读出时,按照读写指令的类型和具体的地址,对数据进行移位,配合读写掩码得到正确的结果。
package oscpu
import chisel3._
import chisel3.util._
import chisel3.experimental._
class Mem_Stage extends Module {
val io = IO(new Bundle{
val inst_type = Input(UInt( 5.W))
val rd_data = Input(UInt(64.W))
val save_data = Input(UInt(64.W))
val mem_r_data = Input(UInt(64.W))
val mem_ext = Input(UInt(1.W))
val mem_sel = Input(UInt(2.W))
val mem_r_ena = Output(UInt( 1.W))
val mem_r_addr = Output(UInt(64.W))
val mem_w_ena = Output(UInt( 1.W))
val mem_w_addr = Output(UInt(64.W))
val mem_w_data = Output(UInt(64.W))
val mem_w_mask = Output(UInt(64.W))
val rd_w_data = Output(UInt(64.W))
})
val the_mask = Wire(UInt(64.W))
val the_data_mask = Wire(UInt(64.W)) // should be removed
val the_data_ext = Wire(UInt(64.W))
when(reset.asBool) {
io.mem_r_ena := 0.U
io.mem_r_addr := 0.U
io.mem_w_ena := 0.U
io.mem_w_addr := 0.U
io.mem_w_data := 0.U
io.mem_w_mask := 0.U
io.rd_w_data := 0.U
the_mask := 0.U
the_data_mask := 0.U
the_data_ext := 0.U
}.otherwise{
// THE MASK
// 64-bits
when ( io.mem_sel === 3.U ) {
the_mask := Cat( VecInit(Seq.fill(64)( 1.U )).asUInt )
}
// 32-bits
.elsewhen( io.mem_sel === 2.U ) {
when(io.rd_data(2).asBool){
the_mask := Cat( VecInit(Seq.fill(32)( 1.U )).asUInt, VecInit(Seq.fill(32)( 0.U )).asUInt )
}.otherwise{
the_mask := Cat( VecInit(Seq.fill(32)( 0.U )).asUInt, VecInit(Seq.fill(32)( 1.U )).asUInt )
}
}
// 16-bits
.elsewhen( io.mem_sel === 1.U ) {
when(io.rd_data(2,1) === 1.U){
the_mask := Cat( VecInit(Seq.fill(32)( 0.U )).asUInt, VecInit(Seq.fill(16)( 1.U )).asUInt, VecInit(Seq.fill(16)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,1) === 2.U){
the_mask := Cat( VecInit(Seq.fill(16)( 0.U )).asUInt, VecInit(Seq.fill(16)( 1.U )).asUInt, VecInit(Seq.fill(32)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,1) === 3.U){
the_mask := Cat( VecInit(Seq.fill(16)( 1.U )).asUInt, VecInit(Seq.fill(48)( 0.U )).asUInt )
}
.otherwise{
the_mask := Cat( VecInit(Seq.fill(48)( 0.U )).asUInt, VecInit(Seq.fill(16)( 1.U )).asUInt )
}
}
// 8-bits
.otherwise {
when(io.rd_data(2,0) === 1.U){
the_mask := Cat( VecInit(Seq.fill(48)( 0.U )).asUInt, VecInit(Seq.fill( 8)( 1.U )).asUInt, VecInit(Seq.fill( 8)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,0) === 2.U){
the_mask := Cat( VecInit(Seq.fill(40)( 0.U )).asUInt, VecInit(Seq.fill( 8)( 1.U )).asUInt, VecInit(Seq.fill( 16)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,0) === 3.U){
the_mask := Cat( VecInit(Seq.fill(32)( 0.U )).asUInt, VecInit(Seq.fill( 8)( 1.U )).asUInt, VecInit(Seq.fill(24)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,0) === 4.U){
the_mask := Cat( VecInit(Seq.fill(24)( 0.U )).asUInt, VecInit(Seq.fill( 8)( 1.U )).asUInt, VecInit(Seq.fill(32)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,0) === 5.U){
the_mask := Cat( VecInit(Seq.fill(16)( 0.U )).asUInt, VecInit(Seq.fill( 8)( 1.U )).asUInt, VecInit(Seq.fill(40)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,0) === 6.U){
the_mask := Cat( VecInit(Seq.fill( 8)( 0.U )).asUInt, VecInit(Seq.fill( 8)( 1.U )).asUInt, VecInit(Seq.fill(48)( 0.U )).asUInt )
}
.elsewhen(io.rd_data(2,0) === 7.U){
the_mask := Cat( VecInit(Seq.fill( 8)( 1.U )).asUInt, VecInit(Seq.fill(56)( 0.U )).asUInt )
}
.otherwise{
the_mask := Cat( VecInit(Seq.fill(56)( 0.U )).asUInt, VecInit(Seq.fill( 8)( 1.U )).asUInt )
}
}
// load
when( io.inst_type(1) === 1.U ){
io.mem_r_ena := 1.U
io.mem_r_addr := io.rd_data
the_data_mask := io.mem_r_data & the_mask
// 64-bits
when ( io.mem_sel === 3.U ) {
the_data_ext := the_data_mask
io.rd_w_data := the_data_ext
}
// 32-bits
.elsewhen( io.mem_sel === 2.U ) {
when(io.rd_data(2).asBool){
the_data_ext := the_data_mask >> 32.U
}.otherwise{
the_data_ext := the_data_mask
}
when( io.mem_ext.asBool ) { io.rd_w_data := Cat( VecInit(Seq.fill(32)( the_data_ext(31) )).asUInt, the_data_ext(31,0) ) }
.otherwise { io.rd_w_data := Cat( VecInit(Seq.fill(32)( 0.U )).asUInt, the_data_ext(31,0) ) }
}
// 16-bits
.elsewhen( io.mem_sel === 1.U ) {
when(io.rd_data(2,1) === 1.U){
the_data_ext := the_data_mask >> 16.U
}
.elsewhen(io.rd_data(2,1) === 2.U){
the_data_ext := the_data_mask >> 32.U
}
.elsewhen(io.rd_data(2,1) === 3.U){
the_data_ext := the_data_mask >> 48.U
}
.otherwise{
the_data_ext := the_data_mask
}
when( io.mem_ext.asBool ) { io.rd_w_data := Cat( VecInit(Seq.fill(48)( the_data_ext(15) )).asUInt, the_data_ext(15,0) ) }
.otherwise { io.rd_w_data := Cat( VecInit(Seq.fill(48)( 0.U )).asUInt, the_data_ext(15,0) ) }
}
// 8-bits
.otherwise {
when(io.rd_data(2,0) === 1.U){
the_data_ext := the_data_mask >> 8.U
}
.elsewhen(io.rd_data(2,0) === 2.U){
the_data_ext := the_data_mask >> 16.U
}
.elsewhen(io.rd_data(2,0) === 3.U){
the_data_ext := the_data_mask >> 24.U
}
.elsewhen(io.rd_data(2,0) === 4.U){
the_data_ext := the_data_mask >> 32.U
}
.elsewhen(io.rd_data(2,0) === 5.U){
the_data_ext := the_data_mask >> 40.U
}
.elsewhen(io.rd_data(2,0) === 6.U){
the_data_ext := the_data_mask >> 48.U
}
.elsewhen(io.rd_data(2,0) === 7.U){
the_data_ext := the_data_mask >> 56.U
}
.otherwise{
the_data_ext := the_data_mask
}
when( io.mem_ext.asBool ) { io.rd_w_data := Cat( VecInit(Seq.fill(56)( the_data_ext(7) )).asUInt, the_data_ext(7,0) ) }
.otherwise { io.rd_w_data := Cat( VecInit(Seq.fill(56)( 0.U )).asUInt, the_data_ext(7,0) ) }
}
}.otherwise{
io.mem_r_ena := 0.U
io.mem_r_addr := 0.U
io.rd_w_data := io.rd_data
the_data_mask := 0.U
the_data_ext := 0.U
}
// save
when( io.inst_type(0) === 1.U ){
io.mem_w_ena := 1.U
io.mem_w_addr := io.rd_data
io.mem_w_mask := the_mask
// 64-bits
when ( io.mem_sel === 3.U ) {
io.mem_w_data := io.save_data
}
// 32-bits
.elsewhen( io.mem_sel === 2.U ) {
when(io.rd_data(2).asBool){
io.mem_w_data := io.save_data << 32.U
}.otherwise{
io.mem_w_data := io.save_data
}
}
// 16-bits
.elsewhen( io.mem_sel === 1.U ) {
when(io.rd_data(2,1) === 1.U){
io.mem_w_data := io.save_data << 16.U
}
.elsewhen(io.rd_data(2,1) === 2.U){
io.mem_w_data := io.save_data << 32.U
}
.elsewhen(io.rd_data(2,1) === 3.U){
io.mem_w_data := io.save_data << 48.U
}
.otherwise{
io.mem_w_data := io.save_data
}
}
// 8-bits
.otherwise {
when(io.rd_data(2,0) === 1.U){
io.mem_w_data := io.save_data << 8.U
}
.elsewhen(io.rd_data(2,0) === 2.U){
io.mem_w_data := io.save_data << 16.U
}
.elsewhen(io.rd_data(2,0) === 3.U){
io.mem_w_data := io.save_data << 24.U
}
.elsewhen(io.rd_data(2,0) === 4.U){
io.mem_w_data := io.save_data << 32.U
}
.elsewhen(io.rd_data(2,0) === 5.U){
io.mem_w_data := io.save_data << 40.U
}
.elsewhen(io.rd_data(2,0) === 6.U){
io.mem_w_data := io.save_data << 48.U
}
.elsewhen(io.rd_data(2,0) === 7.U){
io.mem_w_data := io.save_data << 56.U
}
.otherwise{
io.mem_w_data := io.save_data
}
}
}.otherwise{
io.mem_w_ena := 0.U
io.mem_w_addr := 0.U
io.mem_w_data := 0.U
io.mem_w_mask := 0.U
}
}
}
chisel自动生成的代码,可读性比较差
module Mem_Stage(
input clock,
input reset,
input [4:0] io_inst_type,
input [63:0] io_rd_data,
input [63:0] io_save_data,
input [63:0] io_mem_r_data,
input io_mem_ext,
input [1:0] io_mem_sel,
output io_mem_r_ena,
output [63:0] io_mem_r_addr,
output io_mem_w_ena,
output [63:0] io_mem_w_addr,
output [63:0] io_mem_w_data,
output [63:0] io_mem_w_mask,
output [63:0] io_rd_w_data
);
wire _T_1 = io_mem_sel == 2'h3; // @[Mem_Stage.scala 48:31]
wire _T_2 = io_mem_sel == 2'h2; // @[Mem_Stage.scala 52:31]
wire [63:0] _GEN_0 = io_rd_data[2] ? 64'hffffffff00000000 : 64'hffffffff; // @[Mem_Stage.scala 53:39 Mem_Stage.scala 54:26 Mem_Stage.scala 56:26]
wire _T_5 = io_mem_sel == 2'h1; // @[Mem_Stage.scala 60:31]
wire _T_7 = io_rd_data[2:1] == 2'h1; // @[Mem_Stage.scala 62:34]
wire _T_9 = io_rd_data[2:1] == 2'h2; // @[Mem_Stage.scala 65:39]
wire _T_11 = io_rd_data[2:1] == 2'h3; // @[Mem_Stage.scala 68:39]
wire [63:0] _GEN_1 = io_rd_data[2:1] == 2'h3 ? 64'hffff000000000000 : 64'hffff; // @[Mem_Stage.scala 68:47 Mem_Stage.scala 69:26 Mem_Stage.scala 72:26]
wire [63:0] _GEN_2 = io_rd_data[2:1] == 2'h2 ? 64'hffff00000000 : _GEN_1; // @[Mem_Stage.scala 65:47 Mem_Stage.scala 66:26]
wire [63:0] _GEN_3 = io_rd_data[2:1] == 2'h1 ? 64'hffff0000 : _GEN_2; // @[Mem_Stage.scala 62:42 Mem_Stage.scala 63:26]
wire _T_13 = io_rd_data[2:0] == 3'h1; // @[Mem_Stage.scala 77:34]
wire _T_15 = io_rd_data[2:0] == 3'h2; // @[Mem_Stage.scala 80:39]
wire _T_17 = io_rd_data[2:0] == 3'h3; // @[Mem_Stage.scala 83:39]
wire _T_19 = io_rd_data[2:0] == 3'h4; // @[Mem_Stage.scala 86:39]
wire _T_21 = io_rd_data[2:0] == 3'h5; // @[Mem_Stage.scala 89:39]
wire _T_23 = io_rd_data[2:0] == 3'h6; // @[Mem_Stage.scala 92:39]
wire _T_25 = io_rd_data[2:0] == 3'h7; // @[Mem_Stage.scala 95:39]
wire [63:0] _GEN_4 = io_rd_data[2:0] == 3'h7 ? 64'hff00000000000000 : 64'hff; // @[Mem_Stage.scala 95:47 Mem_Stage.scala 96:26 Mem_Stage.scala 99:27]
wire [63:0] _GEN_5 = io_rd_data[2:0] == 3'h6 ? 64'hff000000000000 : _GEN_4; // @[Mem_Stage.scala 92:47 Mem_Stage.scala 93:26]
wire [63:0] _GEN_6 = io_rd_data[2:0] == 3'h5 ? 64'hff0000000000 : _GEN_5; // @[Mem_Stage.scala 89:47 Mem_Stage.scala 90:26]
wire [63:0] _GEN_7 = io_rd_data[2:0] == 3'h4 ? 64'hff00000000 : _GEN_6; // @[Mem_Stage.scala 86:47 Mem_Stage.scala 87:26]
wire [63:0] _GEN_8 = io_rd_data[2:0] == 3'h3 ? 64'hff000000 : _GEN_7; // @[Mem_Stage.scala 83:47 Mem_Stage.scala 84:26]
wire [63:0] _GEN_9 = io_rd_data[2:0] == 3'h2 ? 64'hff0000 : _GEN_8; // @[Mem_Stage.scala 80:47 Mem_Stage.scala 81:26]
wire [63:0] _GEN_10 = io_rd_data[2:0] == 3'h1 ? 64'hff00 : _GEN_9; // @[Mem_Stage.scala 77:42 Mem_Stage.scala 78:26]
wire [63:0] _GEN_11 = io_mem_sel == 2'h1 ? _GEN_3 : _GEN_10; // @[Mem_Stage.scala 60:41]
wire [63:0] _GEN_12 = io_mem_sel == 2'h2 ? _GEN_0 : _GEN_11; // @[Mem_Stage.scala 52:41]
wire [63:0] _GEN_13 = io_mem_sel == 2'h3 ? 64'hffffffffffffffff : _GEN_12; // @[Mem_Stage.scala 48:41 Mem_Stage.scala 49:22]
wire [63:0] the_mask = reset ? 64'h0 : _GEN_13; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 41:25]
wire [63:0] _the_data_mask_T = io_mem_r_data & the_mask; // @[Mem_Stage.scala 110:46]
wire [63:0] _GEN_36 = io_inst_type[1] ? _the_data_mask_T : 64'h0; // @[Mem_Stage.scala 107:40 Mem_Stage.scala 110:29 Mem_Stage.scala 182:29]
wire [63:0] the_data_mask = reset ? 64'h0 : _GEN_36; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 42:25]
wire [63:0] _the_data_ext_T = {{32'd0}, the_data_mask[63:32]}; // @[Mem_Stage.scala 120:54]
wire [63:0] _GEN_14 = io_rd_data[2] ? _the_data_ext_T : the_data_mask; // @[Mem_Stage.scala 119:43 Mem_Stage.scala 120:37 Mem_Stage.scala 122:37]
wire [63:0] _the_data_ext_T_1 = {{16'd0}, the_data_mask[63:16]}; // @[Mem_Stage.scala 132:54]
wire [63:0] _the_data_ext_T_3 = {{48'd0}, the_data_mask[63:48]}; // @[Mem_Stage.scala 138:54]
wire [63:0] _GEN_16 = _T_11 ? _the_data_ext_T_3 : the_data_mask; // @[Mem_Stage.scala 137:51 Mem_Stage.scala 138:37 Mem_Stage.scala 141:37]
wire [63:0] _GEN_17 = _T_9 ? _the_data_ext_T : _GEN_16; // @[Mem_Stage.scala 134:51 Mem_Stage.scala 135:37]
wire [63:0] _GEN_18 = _T_7 ? _the_data_ext_T_1 : _GEN_17; // @[Mem_Stage.scala 131:46 Mem_Stage.scala 132:37]
wire [63:0] _the_data_ext_T_4 = {{8'd0}, the_data_mask[63:8]}; // @[Mem_Stage.scala 150:54]
wire [63:0] _the_data_ext_T_6 = {{24'd0}, the_data_mask[63:24]}; // @[Mem_Stage.scala 156:54]
wire [63:0] _the_data_ext_T_8 = {{40'd0}, the_data_mask[63:40]}; // @[Mem_Stage.scala 162:54]
wire [63:0] _the_data_ext_T_10 = {{56'd0}, the_data_mask[63:56]}; // @[Mem_Stage.scala 168:54]
wire [63:0] _GEN_20 = _T_25 ? _the_data_ext_T_10 : the_data_mask; // @[Mem_Stage.scala 167:51 Mem_Stage.scala 168:37 Mem_Stage.scala 171:37]
wire [63:0] _GEN_21 = _T_23 ? _the_data_ext_T_3 : _GEN_20; // @[Mem_Stage.scala 164:51 Mem_Stage.scala 165:37]
wire [63:0] _GEN_22 = _T_21 ? _the_data_ext_T_8 : _GEN_21; // @[Mem_Stage.scala 161:51 Mem_Stage.scala 162:37]
wire [63:0] _GEN_23 = _T_19 ? _the_data_ext_T : _GEN_22; // @[Mem_Stage.scala 158:51 Mem_Stage.scala 159:37]
wire [63:0] _GEN_24 = _T_17 ? _the_data_ext_T_6 : _GEN_23; // @[Mem_Stage.scala 155:51 Mem_Stage.scala 156:37]
wire [63:0] _GEN_25 = _T_15 ? _the_data_ext_T_1 : _GEN_24; // @[Mem_Stage.scala 152:51 Mem_Stage.scala 153:37]
wire [63:0] _GEN_26 = _T_13 ? _the_data_ext_T_4 : _GEN_25; // @[Mem_Stage.scala 149:46 Mem_Stage.scala 150:37]
wire [63:0] _GEN_28 = _T_5 ? _GEN_18 : _GEN_26; // @[Mem_Stage.scala 129:45]
wire [63:0] _GEN_30 = _T_2 ? _GEN_14 : _GEN_28; // @[Mem_Stage.scala 118:45]
wire [63:0] _GEN_32 = _T_1 ? the_data_mask : _GEN_30; // @[Mem_Stage.scala 113:45 Mem_Stage.scala 114:33]
wire [63:0] _GEN_37 = io_inst_type[1] ? _GEN_32 : 64'h0; // @[Mem_Stage.scala 107:40 Mem_Stage.scala 183:29]
wire [63:0] the_data_ext = reset ? 64'h0 : _GEN_37; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 43:25]
wire [7:0] io_rd_w_data_lo_lo = {the_data_ext[31],the_data_ext[31],the_data_ext[31],the_data_ext[31],the_data_ext[31],
the_data_ext[31],the_data_ext[31],the_data_ext[31]}; // @[Mem_Stage.scala 125:110]
wire [15:0] io_rd_w_data_lo = {the_data_ext[31],the_data_ext[31],the_data_ext[31],the_data_ext[31],the_data_ext[31],
the_data_ext[31],the_data_ext[31],the_data_ext[31],io_rd_w_data_lo_lo}; // @[Mem_Stage.scala 125:110]
wire [31:0] io_rd_w_data_hi_1 = {the_data_ext[31],the_data_ext[31],the_data_ext[31],the_data_ext[31],the_data_ext[31],
the_data_ext[31],the_data_ext[31],the_data_ext[31],io_rd_w_data_lo_lo,io_rd_w_data_lo}; // @[Mem_Stage.scala 125:110]
wire [31:0] io_rd_w_data_lo_1 = the_data_ext[31:0]; // @[Mem_Stage.scala 125:132]
wire [63:0] _io_rd_w_data_T_32 = {io_rd_w_data_hi_1,io_rd_w_data_lo_1}; // @[Cat.scala 30:58]
wire [63:0] _io_rd_w_data_T_33 = {32'h0,io_rd_w_data_lo_1}; // @[Cat.scala 30:58]
wire [63:0] _GEN_15 = io_mem_ext ? _io_rd_w_data_T_32 : _io_rd_w_data_T_33; // @[Mem_Stage.scala 125:45 Mem_Stage.scala 125:60 Mem_Stage.scala 126:60]
wire [5:0] io_rd_w_data_lo_lo_lo_2 = {the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext
[15],the_data_ext[15]}; // @[Mem_Stage.scala 144:110]
wire [11:0] io_rd_w_data_lo_lo_2 = {the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext[
15],the_data_ext[15],io_rd_w_data_lo_lo_lo_2}; // @[Mem_Stage.scala 144:110]
wire [23:0] io_rd_w_data_lo_4 = {the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext[15],
the_data_ext[15],io_rd_w_data_lo_lo_lo_2,io_rd_w_data_lo_lo_2}; // @[Mem_Stage.scala 144:110]
wire [15:0] io_rd_w_data_lo_5 = the_data_ext[15:0]; // @[Mem_Stage.scala 144:132]
wire [63:0] _io_rd_w_data_T_82 = {the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext[15],the_data_ext[15]
,the_data_ext[15],io_rd_w_data_lo_lo_lo_2,io_rd_w_data_lo_lo_2,io_rd_w_data_lo_4,io_rd_w_data_lo_5}; // @[Cat.scala 30:58]
wire [63:0] _io_rd_w_data_T_83 = {48'h0,io_rd_w_data_lo_5}; // @[Cat.scala 30:58]
wire [63:0] _GEN_19 = io_mem_ext ? _io_rd_w_data_T_82 : _io_rd_w_data_T_83; // @[Mem_Stage.scala 144:45 Mem_Stage.scala 144:60 Mem_Stage.scala 145:60]
wire [6:0] io_rd_w_data_lo_lo_lo_4 = {the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],
the_data_ext[7],the_data_ext[7]}; // @[Mem_Stage.scala 174:109]
wire [13:0] io_rd_w_data_lo_lo_4 = {the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],
the_data_ext[7],the_data_ext[7],io_rd_w_data_lo_lo_lo_4}; // @[Mem_Stage.scala 174:109]
wire [27:0] io_rd_w_data_lo_8 = {the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],
the_data_ext[7],the_data_ext[7],io_rd_w_data_lo_lo_lo_4,io_rd_w_data_lo_lo_4}; // @[Mem_Stage.scala 174:109]
wire [55:0] io_rd_w_data_hi_9 = {the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],the_data_ext[7],
the_data_ext[7],the_data_ext[7],io_rd_w_data_lo_lo_lo_4,io_rd_w_data_lo_lo_4,io_rd_w_data_lo_8}; // @[Mem_Stage.scala 174:109]
wire [7:0] io_rd_w_data_lo_9 = the_data_ext[7:0]; // @[Mem_Stage.scala 174:132]
wire [63:0] _io_rd_w_data_T_140 = {io_rd_w_data_hi_9,io_rd_w_data_lo_9}; // @[Cat.scala 30:58]
wire [63:0] _io_rd_w_data_T_141 = {56'h0,io_rd_w_data_lo_9}; // @[Cat.scala 30:58]
wire [63:0] _GEN_27 = io_mem_ext ? _io_rd_w_data_T_140 : _io_rd_w_data_T_141; // @[Mem_Stage.scala 174:45 Mem_Stage.scala 174:60 Mem_Stage.scala 175:60]
wire [63:0] _GEN_29 = _T_5 ? _GEN_19 : _GEN_27; // @[Mem_Stage.scala 129:45]
wire [63:0] _GEN_31 = _T_2 ? _GEN_15 : _GEN_29; // @[Mem_Stage.scala 118:45]
wire [63:0] _GEN_33 = _T_1 ? the_data_ext : _GEN_31; // @[Mem_Stage.scala 113:45 Mem_Stage.scala 115:33]
wire [63:0] _GEN_35 = io_inst_type[1] ? io_rd_data : 64'h0; // @[Mem_Stage.scala 107:40 Mem_Stage.scala 109:29 Mem_Stage.scala 180:29]
wire [63:0] _GEN_38 = io_inst_type[1] ? _GEN_33 : io_rd_data; // @[Mem_Stage.scala 107:40 Mem_Stage.scala 181:29]
wire [95:0] _GEN_78 = {io_save_data, 32'h0}; // @[Mem_Stage.scala 201:53]
wire [126:0] _io_mem_w_data_T = {{31'd0}, _GEN_78}; // @[Mem_Stage.scala 201:53]
wire [126:0] _GEN_39 = io_rd_data[2] ? _io_mem_w_data_T : {{63'd0}, io_save_data}; // @[Mem_Stage.scala 200:43 Mem_Stage.scala 201:37 Mem_Stage.scala 203:37]
wire [79:0] _GEN_79 = {io_save_data, 16'h0}; // @[Mem_Stage.scala 211:53]
wire [94:0] _io_mem_w_data_T_1 = {{15'd0}, _GEN_79}; // @[Mem_Stage.scala 211:53]
wire [111:0] _GEN_81 = {io_save_data, 48'h0}; // @[Mem_Stage.scala 217:53]
wire [126:0] _io_mem_w_data_T_3 = {{15'd0}, _GEN_81}; // @[Mem_Stage.scala 217:53]
wire [126:0] _GEN_40 = _T_11 ? _io_mem_w_data_T_3 : {{63'd0}, io_save_data}; // @[Mem_Stage.scala 216:51 Mem_Stage.scala 217:37 Mem_Stage.scala 220:37]
wire [126:0] _GEN_41 = _T_9 ? _io_mem_w_data_T : _GEN_40; // @[Mem_Stage.scala 213:51 Mem_Stage.scala 214:37]
wire [126:0] _GEN_42 = _T_7 ? {{32'd0}, _io_mem_w_data_T_1} : _GEN_41; // @[Mem_Stage.scala 210:46 Mem_Stage.scala 211:37]
wire [71:0] _GEN_82 = {io_save_data, 8'h0}; // @[Mem_Stage.scala 226:53]
wire [78:0] _io_mem_w_data_T_4 = {{7'd0}, _GEN_82}; // @[Mem_Stage.scala 226:53]
wire [87:0] _GEN_84 = {io_save_data, 24'h0}; // @[Mem_Stage.scala 232:53]
wire [94:0] _io_mem_w_data_T_6 = {{7'd0}, _GEN_84}; // @[Mem_Stage.scala 232:53]
wire [103:0] _GEN_86 = {io_save_data, 40'h0}; // @[Mem_Stage.scala 238:53]
wire [126:0] _io_mem_w_data_T_8 = {{23'd0}, _GEN_86}; // @[Mem_Stage.scala 238:53]
wire [119:0] _GEN_88 = {io_save_data, 56'h0}; // @[Mem_Stage.scala 244:53]
wire [126:0] _io_mem_w_data_T_10 = {{7'd0}, _GEN_88}; // @[Mem_Stage.scala 244:53]
wire [126:0] _GEN_43 = _T_25 ? _io_mem_w_data_T_10 : {{63'd0}, io_save_data}; // @[Mem_Stage.scala 243:51 Mem_Stage.scala 244:37 Mem_Stage.scala 247:37]
wire [126:0] _GEN_44 = _T_23 ? _io_mem_w_data_T_3 : _GEN_43; // @[Mem_Stage.scala 240:51 Mem_Stage.scala 241:37]
wire [126:0] _GEN_45 = _T_21 ? _io_mem_w_data_T_8 : _GEN_44; // @[Mem_Stage.scala 237:51 Mem_Stage.scala 238:37]
wire [126:0] _GEN_46 = _T_19 ? _io_mem_w_data_T : _GEN_45; // @[Mem_Stage.scala 234:51 Mem_Stage.scala 235:37]
wire [126:0] _GEN_47 = _T_17 ? {{32'd0}, _io_mem_w_data_T_6} : _GEN_46; // @[Mem_Stage.scala 231:51 Mem_Stage.scala 232:37]
wire [126:0] _GEN_48 = _T_15 ? {{32'd0}, _io_mem_w_data_T_1} : _GEN_47; // @[Mem_Stage.scala 228:51 Mem_Stage.scala 229:37]
wire [126:0] _GEN_49 = _T_13 ? {{48'd0}, _io_mem_w_data_T_4} : _GEN_48; // @[Mem_Stage.scala 225:46 Mem_Stage.scala 226:37]
wire [126:0] _GEN_50 = _T_5 ? _GEN_42 : _GEN_49; // @[Mem_Stage.scala 208:45]
wire [126:0] _GEN_51 = _T_2 ? _GEN_39 : _GEN_50; // @[Mem_Stage.scala 199:45]
wire [126:0] _GEN_52 = _T_1 ? {{63'd0}, io_save_data} : _GEN_51; // @[Mem_Stage.scala 195:45 Mem_Stage.scala 196:33]
wire [63:0] _GEN_54 = io_inst_type[0] ? io_rd_data : 64'h0; // @[Mem_Stage.scala 189:40 Mem_Stage.scala 191:29 Mem_Stage.scala 252:29]
wire [63:0] _GEN_55 = io_inst_type[0] ? the_mask : 64'h0; // @[Mem_Stage.scala 189:40 Mem_Stage.scala 192:29 Mem_Stage.scala 254:29]
wire [126:0] _GEN_56 = io_inst_type[0] ? _GEN_52 : 127'h0; // @[Mem_Stage.scala 189:40 Mem_Stage.scala 253:29]
wire [126:0] _GEN_61 = reset ? 127'h0 : _GEN_56; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 38:25]
assign io_mem_r_ena = reset ? 1'h0 : io_inst_type[1]; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 34:25]
assign io_mem_r_addr = reset ? 64'h0 : _GEN_35; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 35:25]
assign io_mem_w_ena = reset ? 1'h0 : io_inst_type[0]; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 36:25]
assign io_mem_w_addr = reset ? 64'h0 : _GEN_54; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 37:25]
assign io_mem_w_data = _GEN_61[63:0];
assign io_mem_w_mask = reset ? 64'h0 : _GEN_55; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 39:25]
assign io_rd_w_data = reset ? 64'h0 : _GEN_38; // @[Mem_Stage.scala 33:24 Mem_Stage.scala 40:25]
endmodule
我想请问
1.为什么ram读数据时地址要减去64'h0000_0000_8000_0000
2.ram的代码中读数据时不需要考虑是lb,lh,lw等等的这些吗,我看是直接读64位的
Originally posted by @codefuturedalao in #9 (comment)
我看了下 《riscv-spec.pdf》 RV64I 的指令,里面的 opcode 有 15 种,但是每种 opcode 下,又有可能同时存在 I 指令 或者 R 指令。
RISCV 指令为什么不是一种 opcode 对应一个指令呢?
Fail to compile from source code in GitHub Repositary. Use brew install verilator
to install 4.200 version, slightly behind the latest 4.204 version, but also works well.
Add --no-quarantine
to avoid security protection on MacOS. Use brew install gtkwave --no-quarantine
to install GTKWave.
/usr/local/bin/gtkwave: line 122: test: too many arguments
/usr/local/bin/gtkwave: line 149: test: ==: unary operator expected
find: /share/locale: No such file or directory
/usr/local/bin/gtkwave: line 215: /usr/local/bin/../../../Contents/Resources/bin/: No such file or directory
/usr/local/bin/gtkwave: line 215: exec: /usr/local/bin/../../../Contents/Resources/bin/: cannot execute: No such file or directory
GTKWave on macOS gives a good solution.
TL;DR
Use Perl’s package manager to install Switch and run binary with full path.
cpan install Switch
perl -V:'installsitelib'
/Applications/gtkwave.app/Contents/Resources/bin/gtkwave
In build.sh
, some commands only works in Linux.
There is no -f
option with readlink
in MacOS. Use SHELL_PATH=$(pwd)
to show current directory.
There is no hostnamectl
command in MacOX. Use hostname
instead.
Substitute gtkwave
with full path /Applications/gtkwave.app/Contents/Resources/bin/gtkwave
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.