Compare commits

...

9 Commits

Author SHA1 Message Date
Felix c8144787b0 docs(README): 更新标题链接指向 github 仓库
4 months ago
Felix 82fe15aa3e feat(makefile): 为lab9增加benchmarks
8 months ago
Felix 6b950d2ea0 fix(makefile): 修复lab9未删除历史trace的问题
8 months ago
Felix a60237e7fd fix(makefile): 修复测例执行顺序问题
8 months ago
Liphen 6293d1cd89 fix(mstatus.mpp): 写入非法值时,mpp保持原值
8 months ago
Felix 2f2f9e9b54 fix(difftest): 修复只实现M模式的CSR初始值
9 months ago
Liphen 6e3f2de95f perf(difftest/makefile): 增加 help 提示
9 months ago
Liphen ee417a132c docs(README): 增加实验环境说明
10 months ago
Wang Jiadong 98c42ca11b perf(test/lab4): 强化 lab4 的测例
10 months ago

1
.gitignore vendored

@ -1,3 +1,4 @@
.vscode/
.metals/
.VSCodeCounter/
.DS_Store

@ -10,7 +10,7 @@
```
# 🚀 [RISCV-LAB](https://code.educoder.net/ppg69fuwb/riscv-lab)
# 🚀 [RISCV-LAB](https://github.com/Ciliphen/riscv-lab)
从零开始的 RV64IMAZicsr_Zifencei 流水线设计实验
@ -49,6 +49,10 @@
## 🛠️ 环境配置
- 以下配置流程在 Ubuntu22.04 以及 WSL2 的 Ubuntu22.04 版本下得到验证,可顺利执行
- 对于 Mac 用户,可使用 brew 工具直接安装对应的工具(由于 brew 安装的 Mill 的版本较新,需将 chisel/build.sc 的内容替换为 [chisel-playground/build.mill](https://github.com/OSCPU/chisel-playground/blob/master/build.mill) 中的内容以解决环境构建问题)
- 推荐使用 Ubuntu22.04 版本进行实验,对于其他环境出现的问题请先自己尝试解决
### 🍕 安装 Verilator
```bash
@ -157,8 +161,8 @@ git pull # 拉取最新代码
```bash
make trace TESTBIN_DIR=<测例对应的bin文件的路径>
# 如在 difftest 目录下输入以下命令可生成CPU运行 am-tests/add.bin 测例的波形
make trace TESTBIN_DIR=./test/bin/am-tests/add.bin
# 如在 difftest 目录下输入以下命令可生成CPU运行 am-tests/01-add-longlong.bin 测例的波形
make trace TESTBIN_DIR=./test/bin/am-tests/01-add-longlong.bin
```
## 📢 注意事项

@ -23,7 +23,7 @@ test:
clean:
rm -rf obj_dir
rm -rf core/Puacpu.v
rm -rf core/PuaCpu.v
perf: obj_dir/V$(TOP_NAME)
$(call git_commit, "perf test RTL") # DO NOT REMOVE THIS LINE!!!
@ -34,6 +34,17 @@ perf: obj_dir/V$(TOP_NAME)
./obj_dir/V$(TOP_NAME) $$test -rvtest -pc -perf; \
done; \
qbench: obj_dir/V$(TOP_NAME)
@echo "==================================================================="
@echo "=========================quick benchmarks=========================="
@echo "==================================================================="
@count=0; \
for test in $$(find ./test/bin/riscv-test/benchmarks \( -name "*.bin" \) | sort | grep -vE "*dhrystone*|*mt-vvadd*"); do \
count=$$((count + 1)); \
echo "Running bench $$count: $$test"; \
./obj_dir/V$(TOP_NAME) $$test -rvtest -pc -perf; \
done; \
lab1: obj_dir/V$(TOP_NAME)
$(call git_commit, "test lab1") # DO NOT REMOVE THIS LINE!!!
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/lab1.bin -rvtest -initgprs -trace 10000000 -pc
@ -67,7 +78,7 @@ TRACE_TESTS67 := $(addprefix trace_,$(TEST67))
$(TEST67): obj_dir/V$(TOP_NAME)
$(call git_commit, "test $@") # DO NOT REMOVE THIS LINE!!!
count=0; \
for test in ./test/bin/am-tests/*; do \
for test in $$(ls ./test/bin/am-tests/* | sort); do \
count=$$((count + 1)); \
echo "Running test $$count: $$test"; \
./obj_dir/V$(TOP_NAME) $$test -rvtest -pc; \
@ -81,7 +92,7 @@ $(TRACE_TESTS67): obj_dir/V$(TOP_NAME)
$(call git_commit, "trace $*") # DO NOT REMOVE THIS LINE!!!
rm -rf ./trace.txt
count=0; \
for test in ./test/bin/am-tests/*; do \
for test in $$(ls ./test/bin/am-tests/* | sort); do \
count=$$((count + 1)); \
echo "Running test $$count: $$test"; \
./obj_dir/V$(TOP_NAME) $$test -rvtest -cpu_trace -writeappend; \
@ -103,21 +114,32 @@ trace_lab8: obj_dir/V$(TOP_NAME)
lab9: obj_dir/V$(TOP_NAME)
$(call git_commit, "test lab9") # DO NOT REMOVE THIS LINE!!!
count=0; \
for test in $$(find ./test/bin/riscv-test/ \( -name "*rv64ui-p-*" -o -name "*rv64um-p-*" -o -name "*rv64mi-p-*" \) | grep -vE "*rv64ui-p-fence_i|*rv64mi-p-access"); do \
for test in $$(find ./test/bin/riscv-test/ \( -name "*rv64ui-p-*" -o -name "*rv64um-p-*" -o -name "*rv64mi-p-*" \) | sort | grep -vE "*rv64ui-p-fence_i|*rv64mi-p-access"); do \
count=$$((count + 1)); \
echo "Running test $$count: $$test"; \
./obj_dir/V$(TOP_NAME) $$test -rvtest -pc; \
done; \
echo "Total tests run: $$count";
$(MAKE) qbench
trace_lab9: obj_dir/V$(TOP_NAME)
$(call git_commit, "trace lab9") # DO NOT REMOVE THIS LINE!!!
rm -rf ./trace.txt
count=0; \
for test in $$(find ./test/bin/riscv-test/ \( -name "*rv64ui-p-*" -o -name "*rv64um-p-*" -o -name "*rv64mi-p-*" \) | grep -vE "*rv64ui-p-fence_i|*rv64mi-p-access"); do \
for test in $$(find ./test/bin/riscv-test/ \( -name "*rv64ui-p-*" -o -name "*rv64um-p-*" -o -name "*rv64mi-p-*" \) | sort | grep -vE "*rv64ui-p-fence_i|*rv64mi-p-access"); do \
count=$$((count + 1)); \
echo "Running test $$count: $$test"; \
./obj_dir/V$(TOP_NAME) $$test -rvtest -cpu_trace -writeappend; \
done; \
echo "Total tests run: $$count";
help:
@echo "Usage: make [target]"
@echo "Available targets:"
@echo " clean - Remove build artifacts"
@echo " verilog - Generate verilog files"
@echo " perf - Run performance tests"
@echo " lab<number> - Run lab<number>, eg. lab1, lab2, ..."
@echo " trace_lab<number> - Run lab<number> with trace, eg. trace_lab1, trace_lab2, ..."
-include ../Makefile

@ -13,12 +13,12 @@
## 🔨 使用方法
1. 将 CPU 代码放置到相对于本文件夹的 `core` 文件夹中。
2. 在本文件夹下,使用 `make` 命令完成编译编译结果位于 `obj_dir/Vtop`
2. 在本文件夹下,使用 `make` 命令完成编译结果位于 `obj_dir/Vtop`
3. 每次修改 CPU 代码后,需要重新 `make`,如果引入了一些时间早于编译产物的代码,需要先 `make clean``make`
## 🧪 参数说明
`makefile` 中已经预置了常用命令
`makefile` 中已经预置了常用命令,使用 `make help` 查看命令使用方法
在命令 `./obj_dir/Vtop` 后面可以加上不同参数,可以实现不同的功能:
@ -31,3 +31,4 @@
- `-pc` 报错时将额外输出最近的 PC 历史记录
- `-delay` 行为不一致时将继续运行一段时间再停止
- `-cpu_trace` 记录被测试的处理器的程序运行历史信息,生成 trace.txt
- `-perf` 打印处理器运行的IPC数据

@ -39,15 +39,20 @@ public:
status = 0;
csr_mstatus_def *mstatus = (csr_mstatus_def *)&status;
csr_misa_def *isa = (csr_misa_def *)&misa;
if (only_modeM && run_riscv_test)
if (run_riscv_test)
{
isa->ext = rv_ext('i') | rv_ext('m') | rv_ext('u');
mstatus->mpp = M_MODE;
}
else
{
mstatus->uxl = 2;
isa->ext = rv_ext('i') | rv_ext('m') | rv_ext('u');
if (only_modeM)
{
// 只实现M-Mode的CPU
isa->ext = rv_ext('i') | rv_ext('m');
mstatus->mpp = M_MODE;
}
else
{
// 实现M-Mode和U-Mode的CPU
mstatus->uxl = 2;
isa->ext = rv_ext('i') | rv_ext('m') | rv_ext('u');
}
}
isa->mxl = 2; // rv64
isa->blank = 0;
@ -262,7 +267,7 @@ public:
assert(mstatus->spie != 2);
assert(mstatus->mpie != 2);
// mstatus->spp = nstatus->spp;
mstatus->mpp = (nstatus->mpp == 3 || nstatus->mpp == 0) ? nstatus->mpp : 0;
mstatus->mpp = (nstatus->mpp == 3 || nstatus->mpp == 0) ? nstatus->mpp : mstatus->mpp;
mstatus->mprv = nstatus->mprv;
// mstatus->sum = nstatus->sum; // always true
// mstatus->mxr = nstatus->mxr; // always true

File diff suppressed because it is too large Load Diff

@ -4,7 +4,7 @@ import os
random.seed(0)
reg = [f"x{i}" for i in range(32)]
# 定义R型运算类指令
# Define R-type arithmetic instructions
inst_reg = [
"add",
"sub",
@ -62,15 +62,13 @@ file_name = "test"
mem_start = 0x80200000
mem_end = 0x80210000
# 自动生成R型指令测试样例1000个输出到file_name文件中
# 新建build文件夹存放生成的文件
# 初始化寄存器状态0表示可用
# Initialize register status: 0 means available.
reg_status = {r: 0 for r in reg}
def get_available_reg(reg, reg_status):
available_regs = [r for r, status in reg_status.items() if status == 0]
if not available_regs: # 如果没有可用的寄存器则返回x0
if not available_regs: # If no register available, return x0.
return reg[0]
return random.choice(available_regs)
@ -78,7 +76,7 @@ def get_available_reg(reg, reg_status):
def update_reg_status(reg_status):
for r in reg_status:
if reg_status[r] > 0:
reg_status[r] -= 1 # 减少计数,向可用状态靠近
reg_status[r] -= 1 # Countdown to register availability
def test_reg_inst(insts=inst_reg):
@ -87,8 +85,8 @@ def test_reg_inst(insts=inst_reg):
rs1 = get_available_reg(reg, reg_status)
rs2 = get_available_reg(reg, reg_status)
f.write(f"\t{inst} {rd}, {rs1}, {rs2}\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4 # Delay to avoid data hazard
update_reg_status(reg_status)
def test_imm_inst():
@ -102,8 +100,8 @@ def test_imm_inst():
else:
imm = random.randint(-(2**11), 2**11 - 1)
f.write(f"\t{inst} {rd}, {rs1}, {imm}\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
def test_lui_inst():
@ -111,8 +109,8 @@ def test_lui_inst():
rd = get_available_reg(reg, reg_status)
imm = random.randint(0, 2**20 - 1)
f.write(f"\t{inst} {rd}, {imm}\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
def wait_gpr(index):
@ -130,7 +128,7 @@ def insert_nop(n=3):
def test_lb():
f.write("\taddiw x1, x0, 1025\n")
insert_nop()
f.write("\tslli x1, x1, 0x15\n") # x1=0x80200000
f.write("\tslli x1, x1, 0x15\n") # x1 = 0x80200000
insert_nop()
rd = get_available_reg(reg, reg_status)
while rd == "x1":
@ -139,82 +137,183 @@ def test_lb():
imm = random.randint(-(2**11), 2**11 - 1)
f.write(f"\tsb {rd}, {imm}({rs1})\n")
f.write(f"\tlb {rd}, {imm}({rs1})\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
f.write(f"\tlbu {rd}, {imm}({rs1})\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
def test_lh():
f.write("\taddiw x1, x0, 1025\n")
insert_nop()
f.write("\tslli x1, x1, 0x15\n") # x1=0x80200000
f.write("\tslli x1, x1, 0x15\n") # x1 = 0x80200000
insert_nop()
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
rs1 = "x1"
# Align immediate for halfword access (even address)
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFFE
f.write(f"\tsh {rd}, {imm}({rs1})\n")
f.write(f"\tlh {rd}, {imm}({rs1})\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
f.write(f"\tlhu {rd}, {imm}({rs1})\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
def test_lw():
f.write("\taddiw x1, x0, 1025\n")
insert_nop()
f.write("\tslli x1, x1, 0x15\n") # x1=0x80200000
f.write("\tslli x1, x1, 0x15\n") # x1 = 0x80200000
insert_nop()
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
rs1 = "x1"
# Align immediate for word access (multiple of 4)
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFFC
f.write(f"\tsw {rd}, {imm}({rs1})\n")
f.write(f"\tlw {rd}, {imm}({rs1})\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
f.write(f"\tlwu {rd}, {imm}({rs1})\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
def test_ld():
f.write("\taddiw x1, x0, 1025\n")
insert_nop()
f.write("\tslli x1, x1, 0x15\n") # x1=0x80200000
f.write("\tslli x1, x1, 0x15\n") # x1 = 0x80200000
insert_nop()
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
rs1 = "x1"
# Align immediate for doubleword access (multiple of 8)
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFF8
f.write(f"\tsd {rd}, {imm}({rs1})\n")
f.write(f"\tld {rd}, {imm}({rs1})\n")
reg_status[rd] = 4 # 解决数据冲突
update_reg_status(reg_status) # 更新寄存器状态
reg_status[rd] = 4
update_reg_status(reg_status)
def test_store_load_cycle():
"""
This function stores several registers into memory using store instructions
with properly aligned immediates, then overwrites those memory addresses,
and finally loads the values back using the corresponding load instructions.
"""
# Set up x1 as the memory base address (0x80200000)
f.write("\taddiw x1, x0, 1025\n")
insert_nop()
f.write("\tslli x1, x1, 0x15\n")
insert_nop()
cycle_count = 5 # Number of store instructions in each cycle.
store_entries = []
# First, store values using a randomly chosen store instruction.
for _ in range(cycle_count):
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
store_inst = random.choice(inst_store)
if store_inst == "sb":
imm = random.randint(-(2**11), 2**11 - 1)
elif store_inst == "sh":
# Align immediate to an even address.
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFFE
elif store_inst == "sw":
# Align immediate to a multiple of 4.
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFFC
elif store_inst == "sd":
# Align immediate to a multiple of 8.
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFF8
f.write(f"\t{store_inst} {rd}, {imm}(x1)\n")
store_entries.append((store_inst, imm, rd))
reg_status[rd] = 4
update_reg_status(reg_status)
# Overwrite the previously stored addresses with new values.
overwrite_entries = []
for _ in range(cycle_count):
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
store_inst = random.choice(inst_store)
if store_inst == "sb":
imm = random.randint(-(2**11), 2**11 - 1)
elif store_inst == "sh":
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFFE
elif store_inst == "sw":
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFFC
elif store_inst == "sd":
imm = random.randint(-(2**11), 2**11 - 1) & 0xFFFFFFFFFFFFFFF8
f.write(f"\t{store_inst} {rd}, {imm}(x1)\n")
overwrite_entries.append((store_inst, imm, rd))
reg_status[rd] = 4
update_reg_status(reg_status)
# Now, load back the values stored in the first cycle.
for store_inst, imm, reg_written in store_entries:
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
# Choose an appropriate load instruction corresponding to the store type.
if store_inst == "sb":
load_inst = random.choice(["lb", "lbu"])
elif store_inst == "sh":
load_inst = random.choice(["lh", "lhu"])
elif store_inst == "sw":
load_inst = random.choice(["lw", "lwu"])
elif store_inst == "sd":
load_inst = "ld"
f.write(f"\t{load_inst} {rd}, {imm}(x1)\n")
reg_status[rd] = 4
update_reg_status(reg_status)
# Similarly, load back the values from the overwrite cycle.
for store_inst, imm, reg_written in overwrite_entries:
rd = get_available_reg(reg, reg_status)
while rd == "x1":
rd = get_available_reg(reg, reg_status)
if store_inst == "sb":
load_inst = random.choice(["lb", "lbu"])
elif store_inst == "sh":
load_inst = random.choice(["lh", "lhu"])
elif store_inst == "sw":
load_inst = random.choice(["lw", "lwu"])
elif store_inst == "sd":
load_inst = "ld"
f.write(f"\t{load_inst} {rd}, {imm}(x1)\n")
reg_status[rd] = 4
update_reg_status(reg_status)
# Create the build directory if it doesn't exist.
if not os.path.exists("build"):
os.mkdir("build")
with open("./build/{}.s".format(file_name), "w") as f:
f.write(".text\n")
f.write(".global _start\n")
f.write("_start:\n")
# Generate some immediate and LUI instructions.
for i in range(100):
test_imm_inst()
test_lui_inst()
@ -223,7 +322,7 @@ with open("./build/{}.s".format(file_name), "w") as f:
test_reg_inst()
random.choice([test_imm_inst, test_lui_inst, test_reg_inst])()
random.choice(instructions)()
# 结束程序
# End the program.
wait_gpr(3)
f.write("\tli x3, 1\n")
wait_gpr(17)

Loading…
Cancel
Save