Compare commits

...

5 Commits
main ... axi

Author SHA1 Message Date
Felix e4d2cc9902 docs(README): 更新标题链接指向 github 仓库
4 months ago
Felix da699f81b3 fix(makefile): 修复clean命令中的文件名错误
8 months ago
Felix 431ad06315 fix(makefile): 修复测例执行顺序问题
8 months ago
Liphen 229a927621 fix(mstatus.mpp): 写入非法值时,mpp保持原值
8 months ago
Liphen b36ac7e394 feat: 增加 AXI 接口下的差分测试框架
9 months ago

@ -10,7 +10,7 @@
```
# 🚀 [RISCV-LAB](https://code.educoder.net/ppg69fuwb/riscv-lab)
# 🚀 [RISCV-LAB](https://github.com/Ciliphen/riscv-lab)
从零开始的 RV64IMAZicsr_Zifencei 流水线设计实验

@ -0,0 +1,99 @@
module top(
input clock,
input reset,
// Interrupts
input mei, // to PLIC
input msi, // to CLINT
input mti, // to CLINT
input sei, // to PLIC
// aw
output [3:0]axi_awid,
output[31:0]axi_awaddr,
output [7:0]axi_awlen,
output [2:0]axi_awsize,
output [1:0]axi_awburst,
output axi_awvalid,
input axi_awready,
// w
output[63:0]axi_wdata,
output [7:0]axi_wstrb,
output axi_wlast,
output axi_wvalid,
input axi_wready,
// b
input [3:0]axi_bid,
input [1:0]axi_bresp,
input axi_bvalid,
output axi_bready,
// ar
output [3:0]axi_arid,
output[31:0]axi_araddr,
output [7:0]axi_arlen,
output [2:0]axi_arsize,
output [1:0]axi_arburst,
output axi_arvalid,
input axi_arready,
// r
input [3:0]axi_rid,
input [63:0]axi_rdata,
input [1:0]axi_rresp,
input axi_rlast,
input axi_rvalid,
output axi_rready,
// debug
output debug_commit,
output[63:0]debug_pc,
output[4:0] debug_rf_wnum,
output[63:0]debug_rf_wdata
);
PuaCpu core(
.clock (clock),
.reset (reset),
// Interrupts
.io_ext_int_mei (mei), // to PLIC
.io_ext_int_msi (msi), // to CLINT
.io_ext_int_mti (mti), // to CLINT
.io_ext_int_sei (sei), // to PLIC
// aw
.io_axi_aw_bits_id (axi_awid),
.io_axi_aw_bits_addr (axi_awaddr),
.io_axi_aw_bits_len (axi_awlen),
.io_axi_aw_bits_size (axi_awsize),
.io_axi_aw_bits_burst (axi_awburst),
.io_axi_aw_valid (axi_awvalid),
.io_axi_aw_ready (axi_awready),
// w
.io_axi_w_bits_data (axi_wdata),
.io_axi_w_bits_strb (axi_wstrb),
.io_axi_w_bits_last (axi_wlast),
.io_axi_w_valid (axi_wvalid),
.io_axi_w_ready (axi_wready),
// b
.io_axi_b_bits_id (axi_bid),
.io_axi_b_bits_resp (axi_bresp),
.io_axi_b_valid (axi_bvalid),
.io_axi_b_ready (axi_bready),
// ar
.io_axi_ar_bits_id (axi_arid),
.io_axi_ar_bits_addr (axi_araddr),
.io_axi_ar_bits_len (axi_arlen),
.io_axi_ar_bits_size (axi_arsize),
.io_axi_ar_bits_burst (axi_arburst),
.io_axi_ar_valid (axi_arvalid),
.io_axi_ar_ready (axi_arready),
// r
.io_axi_r_bits_id (axi_rid),
.io_axi_r_bits_data (axi_rdata),
.io_axi_r_bits_resp (axi_rresp),
.io_axi_r_bits_last (axi_rlast),
.io_axi_r_valid (axi_rvalid),
.io_axi_r_ready (axi_rready),
// debug
.io_debug_pc (debug_pc),
.io_debug_commit (debug_commit),
.io_debug_rf_wnum (debug_rf_wnum),
.io_debug_rf_wdata (debug_rf_wdata)
);
endmodule

@ -1,54 +0,0 @@
module top(
input clock,
input reset,
// Interrupts
input mei, // to PLIC
input msi, // to CLINT
input mti, // to CLINT
input sei, // to PLIC
// inst sram interface
output inst_sram_en,
output [ 3:0] inst_sram_wen,
output [31:0] inst_sram_addr,
output [31:0] inst_sram_wdata,
input [31:0] inst_sram_rdata,
// data sram interface
output data_sram_en,
output [ 7:0] data_sram_wen,
output [31:0] data_sram_addr,
output [63:0] data_sram_wdata,
input [63:0] data_sram_rdata,
// trace debug interface
output debug_commit,
output [63:0] debug_pc,
output [4:0 ] debug_rf_wnum,
output [63:0] debug_rf_wdata
);
PuaCpu core(
.clock (clock),
.reset (reset),
// interrupts
.io_ext_int_mei (mei),
.io_ext_int_mti (mti),
.io_ext_int_msi (msi),
// inst sram interface
.io_inst_sram_en (inst_sram_en),
.io_inst_sram_wen (inst_sram_wen),
.io_inst_sram_addr (inst_sram_addr),
.io_inst_sram_wdata (inst_sram_wdata),
.io_inst_sram_rdata (inst_sram_rdata),
// data sram interface
.io_data_sram_en (data_sram_en),
.io_data_sram_wen (data_sram_wen),
.io_data_sram_addr (data_sram_addr),
.io_data_sram_wdata (data_sram_wdata),
.io_data_sram_rdata (data_sram_rdata),
// debug
.io_debug_pc (debug_pc),
.io_debug_commit (debug_commit),
.io_debug_rf_wnum (debug_rf_wnum),
.io_debug_rf_wdata (debug_rf_wdata)
);
endmodule

@ -23,51 +23,24 @@ 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!!!
count=0; \
for test in ./test/bin/riscv-test/benchmarks/*; do \
for test in $$(ls ./test/bin/riscv-test/benchmarks/* | sort); do \
count=$$((count + 1)); \
echo "Running test $$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
trace_lab1: obj_dir/V$(TOP_NAME)
$(call git_commit, "trace lab1") # DO NOT REMOVE THIS LINE!!!
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/lab1.bin -rvtest -initgprs -cpu_trace
TESTS234 := lab2 lab3 lab4
TRACE_TESTS234 := $(addprefix trace_,$(TESTS234))
$(TESTS234): %: obj_dir/V$(TOP_NAME)
$(call git_commit, "test $@") # DO NOT REMOVE THIS LINE!!!
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/$@.bin -rvtest -trace 10000000 -pc
$(TRACE_TESTS234): trace_%: obj_dir/V$(TOP_NAME)
$(call git_commit, "trace $*") # DO NOT REMOVE THIS LINE!!!
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/$*.bin -rvtest -cpu_trace
lab5: obj_dir/V$(TOP_NAME)
$(call git_commit, "test lab5") # DO NOT REMOVE THIS LINE!!!
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/lab5.bin -rvtest -trace 10000000 -pc -hasdelayslot
trace_lab5: obj_dir/V$(TOP_NAME)
$(call git_commit, "trace lab5") # DO NOT REMOVE THIS LINE!!!
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/lab5.bin -rvtest -cpu_trace -hasdelayslot
TEST67 := lab6 lab7
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 +54,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; \
@ -91,42 +64,97 @@ $(TRACE_TESTS67): obj_dir/V$(TOP_NAME)
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/lab6.bin -rvtest -cpu_trace -writeappend; \
echo "Total tests run: $$count";
lab8: obj_dir/V$(TOP_NAME)
$(call git_commit, "test lab8") # DO NOT REMOVE THIS LINE!!!
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/lab8.bin -rvtest -trace 10000000 -pc -onlymodem
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; \
trace_lab8: obj_dir/V$(TOP_NAME)
$(call git_commit, "trace lab8") # DO NOT REMOVE THIS LINE!!!
rm -rf ./trace.txt
./obj_dir/V$(TOP_NAME) ./test/bin/lab-test/lab8.bin -rvtest -cpu_trace -onlymodem
sbench: obj_dir/V$(TOP_NAME)
@echo "====================================================================="
@echo "====================quick superscalar 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 -dual_issue; \
done; \
lab9: obj_dir/V$(TOP_NAME)
$(call git_commit, "test lab9") # DO NOT REMOVE THIS LINE!!!
TEST9_10 := lab9 lab10
TRACE_TEST9_10 := $(addprefix trace_,$(TEST9_10))
$(TEST9_10): obj_dir/V$(TOP_NAME)
$(call git_commit, "test $@") # 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!!!
$(TRACE_TEST9_10): obj_dir/V$(TOP_NAME)
$(call git_commit, "trace $*") # 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";
TEST11_13 := lab11 lab12 lab13
TRACE_TEST11_13 := $(addprefix trace_,$(TEST11_13))
$(TEST11_13): obj_dir/V$(TOP_NAME)
$(call git_commit, "test $@") # 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-*" \) | sort | grep -vE "*rv64mi-p-access"); do \
count=$$((count + 1)); \
echo "Running test $$count: $$test"; \
if [ "$@" = "lab13" ]; then \
./obj_dir/V$(TOP_NAME) $$test -rvtest -pc -dual_issue; \
else \
./obj_dir/V$(TOP_NAME) $$test -rvtest -pc; \
fi; \
done; \
echo "Total tests run: $$count"; \
if [ "$@" = "lab13" ]; then \
$(MAKE) sbench; \
else \
$(MAKE) qbench; \
fi;
$(TRACE_TEST11_13): obj_dir/V$(TOP_NAME)
$(call git_commit, "trace $*") # 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-*" \) | sort | grep -vE "*rv64mi-p-access"); do \
count=$$((count + 1)); \
echo "Running test $$count: $$test"; \
if [ "$@" = "trace_lab13" ]; then \
./obj_dir/V$(TOP_NAME) $$test -rvtest -cpu_trace -writeappend -dual_issue; \
else \
./obj_dir/V$(TOP_NAME) $$test -rvtest -cpu_trace -writeappend; \
fi; \
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, ..."
@echo " lab<number> - Run lab<number>, eg. lab6, lab7, ..."
@echo " trace_lab<number> - Run lab<number> with trace, eg. trace_lab6, trace_lab7, ..."
-include ../Makefile

@ -1,12 +1,12 @@
# 🧬 RISCV-DIFFTEST
SRAM 结构的 [RISCV-LAB](https://code.educoder.net/ppg69fuwb/riscv-lab) 提供差分测试支持
AXI 结构的 [RISCV-LAB](https://code.educoder.net/ppg69fuwb/riscv-lab) 提供差分测试支持
差分测试框架修改自 [soc-simulator](https://github.com/cyyself/soc-simulator)
## 📑 目录说明
- `core` **存放待测试处理器的代码**,其顶层信号应与 `top_sram_wrapper` 中信号定义一致
- `core` **存放待测试处理器的代码**,其顶层信号应与 `top_axi_wrapper` 中信号定义一致
- `test` 存放测试代码
- `src` 存放模拟器代码
@ -32,3 +32,4 @@
- `-delay` 行为不一致时将继续运行一段时间再停止
- `-cpu_trace` 记录被测试的处理器的程序运行历史信息,生成 trace.txt
- `-perf` 打印处理器运行的IPC数据
- `-dual_issue` 对双发射处理器进行测试

@ -0,0 +1,99 @@
module top(
input clock,
input reset,
// Interrupts
input mei, // to PLIC
input msi, // to CLINT
input mti, // to CLINT
input sei, // to PLIC
// aw
output [3:0]axi_awid,
output[31:0]axi_awaddr,
output [7:0]axi_awlen,
output [2:0]axi_awsize,
output [1:0]axi_awburst,
output axi_awvalid,
input axi_awready,
// w
output[63:0]axi_wdata,
output [7:0]axi_wstrb,
output axi_wlast,
output axi_wvalid,
input axi_wready,
// b
input [3:0]axi_bid,
input [1:0]axi_bresp,
input axi_bvalid,
output axi_bready,
// ar
output [3:0]axi_arid,
output[31:0]axi_araddr,
output [7:0]axi_arlen,
output [2:0]axi_arsize,
output [1:0]axi_arburst,
output axi_arvalid,
input axi_arready,
// r
input [3:0]axi_rid,
input [63:0]axi_rdata,
input [1:0]axi_rresp,
input axi_rlast,
input axi_rvalid,
output axi_rready,
// debug
output debug_commit,
output[63:0]debug_pc,
output[4:0] debug_rf_wnum,
output[63:0]debug_rf_wdata
);
PuaCpu core(
.clock (clock),
.reset (reset),
// Interrupts
.io_ext_int_mei (mei), // to PLIC
.io_ext_int_msi (msi), // to CLINT
.io_ext_int_mti (mti), // to CLINT
.io_ext_int_sei (sei), // to PLIC
// aw
.io_axi_aw_bits_id (axi_awid),
.io_axi_aw_bits_addr (axi_awaddr),
.io_axi_aw_bits_len (axi_awlen),
.io_axi_aw_bits_size (axi_awsize),
.io_axi_aw_bits_burst (axi_awburst),
.io_axi_aw_valid (axi_awvalid),
.io_axi_aw_ready (axi_awready),
// w
.io_axi_w_bits_data (axi_wdata),
.io_axi_w_bits_strb (axi_wstrb),
.io_axi_w_bits_last (axi_wlast),
.io_axi_w_valid (axi_wvalid),
.io_axi_w_ready (axi_wready),
// b
.io_axi_b_bits_id (axi_bid),
.io_axi_b_bits_resp (axi_bresp),
.io_axi_b_valid (axi_bvalid),
.io_axi_b_ready (axi_bready),
// ar
.io_axi_ar_bits_id (axi_arid),
.io_axi_ar_bits_addr (axi_araddr),
.io_axi_ar_bits_len (axi_arlen),
.io_axi_ar_bits_size (axi_arsize),
.io_axi_ar_bits_burst (axi_arburst),
.io_axi_ar_valid (axi_arvalid),
.io_axi_ar_ready (axi_arready),
// r
.io_axi_r_bits_id (axi_rid),
.io_axi_r_bits_data (axi_rdata),
.io_axi_r_bits_resp (axi_rresp),
.io_axi_r_bits_last (axi_rlast),
.io_axi_r_valid (axi_rvalid),
.io_axi_r_ready (axi_rready),
// debug
.io_debug_pc (debug_pc),
.io_debug_commit (debug_commit),
.io_debug_rf_wnum (debug_rf_wnum),
.io_debug_rf_wdata (debug_rf_wdata)
);
endmodule

@ -1,54 +0,0 @@
module top(
input clock,
input reset,
// Interrupts
input mei, // to PLIC
input msi, // to CLINT
input mti, // to CLINT
input sei, // to PLIC
// inst sram interface
output inst_sram_en,
output [ 3:0] inst_sram_wen,
output [31:0] inst_sram_addr,
output [31:0] inst_sram_wdata,
input [31:0] inst_sram_rdata,
// data sram interface
output data_sram_en,
output [ 7:0] data_sram_wen,
output [31:0] data_sram_addr,
output [63:0] data_sram_wdata,
input [63:0] data_sram_rdata,
// trace debug interface
output debug_commit,
output [63:0] debug_pc,
output [4:0 ] debug_rf_wnum,
output [63:0] debug_rf_wdata
);
PuaCpu core(
.clock (clock),
.reset (reset),
// interrupts
.io_ext_int_mei (mei),
.io_ext_int_mti (mti),
.io_ext_int_msi (msi),
// inst sram interface
.io_inst_sram_en (inst_sram_en),
.io_inst_sram_wen (inst_sram_wen),
.io_inst_sram_addr (inst_sram_addr),
.io_inst_sram_wdata (inst_sram_wdata),
.io_inst_sram_rdata (inst_sram_rdata),
// data sram interface
.io_data_sram_en (data_sram_en),
.io_data_sram_wen (data_sram_wen),
.io_data_sram_addr (data_sram_addr),
.io_data_sram_wdata (data_sram_wdata),
.io_data_sram_rdata (data_sram_rdata),
// debug
.io_debug_pc (debug_pc),
.io_debug_commit (debug_commit),
.io_debug_rf_wnum (debug_rf_wnum),
.io_debug_rf_wdata (debug_rf_wdata)
);
endmodule

@ -0,0 +1,312 @@
#ifndef AXI4_HPP
#define AXI4_HPP
#include <verilated.h>
#include <condition_variable>
#include <cstdint>
#include <set>
#include <cstring>
#define AUTO_SIG(name, msb, lsb) \
typename std::conditional<(msb - lsb + 1) <= 8, CData, \
typename std::conditional<(msb - lsb + 1) <= 16, SData, \
typename std::conditional<(msb - lsb + 1) <= 32, IData, QData>::type>::type>::type name
#define AUTO_IN(name, msb, lsb) AUTO_SIG(name, msb, lsb)
#define AUTO_OUT(name, msb, lsb) AUTO_SIG(name, msb, lsb)
/*
We have defined 3 types of AXI signals for a different purposes: axi4, axi4_ptr, axi4_ref.
Since verilator exposes signals as a value itself, we use axi4_ptr to get signal to connect.
Then axi4_ptr can be converted to axi4_ref to reach the value in a better way.
*/
template <unsigned int A_WIDTH, unsigned int D_WIDTH, unsigned int ID_WIDTH>
struct axi4;
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
struct axi4_ptr
{
static_assert(__builtin_popcount(D_WIDTH) == 1, "D_WIDTH should be the power of 2.");
static_assert(D_WIDTH >= 8, "D_WIDTH should be larger or equal to 8.");
// aw
AUTO_IN(*awid, ID_WIDTH - 1, 0) = NULL;
AUTO_IN(*awaddr, A_WIDTH - 1, 0) = NULL;
AUTO_IN(*awlen, 7, 0) = NULL;
AUTO_IN(*awsize, 2, 0) = NULL;
AUTO_IN(*awburst, 1, 0) = NULL;
AUTO_IN(*awvalid, 0, 0) = NULL;
AUTO_OUT(*awready, 0, 0) = NULL;
// w
AUTO_IN(*wdata, D_WIDTH - 1, 0) = NULL;
AUTO_IN(*wstrb, (D_WIDTH / 8) - 1, 0) = NULL;
AUTO_IN(*wlast, 0, 0) = NULL;
AUTO_IN(*wvalid, 0, 0) = NULL;
AUTO_OUT(*wready, 0, 0) = NULL;
// b
AUTO_OUT(*bid, ID_WIDTH - 1, 0) = NULL;
AUTO_OUT(*bresp, 1, 0) = NULL;
AUTO_OUT(*bvalid, 0, 0) = NULL;
AUTO_IN(*bready, 0, 0) = NULL;
// ar
AUTO_IN(*arid, ID_WIDTH - 1, 0) = NULL;
AUTO_IN(*araddr, A_WIDTH - 1, 0) = NULL;
AUTO_IN(*arlen, 7, 0) = NULL;
AUTO_IN(*arsize, 2, 0) = NULL;
AUTO_IN(*arburst, 1, 0) = NULL;
AUTO_IN(*arvalid, 0, 0) = NULL;
AUTO_OUT(*arready, 0, 0) = NULL;
// r
AUTO_OUT(*rid, ID_WIDTH - 1, 0) = NULL;
AUTO_OUT(*rdata, D_WIDTH - 1, 0) = NULL;
AUTO_OUT(*rresp, 1, 0) = NULL;
AUTO_OUT(*rlast, 0, 0) = NULL;
AUTO_OUT(*rvalid, 0, 0) = NULL;
AUTO_IN(*rready, 0, 0) = NULL;
bool check()
{
std::set<void *> s;
// aw
s.insert((void *)awid);
s.insert((void *)awaddr);
s.insert((void *)awlen);
s.insert((void *)awsize);
s.insert((void *)awburst);
s.insert((void *)awvalid);
s.insert((void *)awready);
// w
s.insert((void *)wdata);
s.insert((void *)wstrb);
s.insert((void *)wlast);
s.insert((void *)wvalid);
s.insert((void *)wready);
// b
s.insert((void *)bid);
s.insert((void *)bresp);
s.insert((void *)bvalid);
s.insert((void *)bready);
// ar
s.insert((void *)arid);
s.insert((void *)araddr);
s.insert((void *)arlen);
s.insert((void *)arsize);
s.insert((void *)arburst);
s.insert((void *)arvalid);
s.insert((void *)arready);
// r
s.insert((void *)rid);
s.insert((void *)rdata);
s.insert((void *)rresp);
s.insert((void *)rlast);
s.insert((void *)rvalid);
s.insert((void *)rready);
return s.size() == 29 && s.count(NULL) == 0;
}
};
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
struct axi4_ref
{
AUTO_IN(&awid, ID_WIDTH - 1, 0);
AUTO_IN(&awaddr, A_WIDTH - 1, 0);
AUTO_IN(&awlen, 7, 0);
AUTO_IN(&awsize, 2, 0);
AUTO_IN(&awburst, 1, 0);
AUTO_IN(&awvalid, 0, 0);
AUTO_OUT(&awready, 0, 0);
// w
AUTO_IN(&wdata, D_WIDTH - 1, 0);
AUTO_IN(&wstrb, (D_WIDTH / 8) - 1, 0);
AUTO_IN(&wlast, 0, 0);
AUTO_IN(&wvalid, 0, 0);
AUTO_OUT(&wready, 0, 0);
// b
AUTO_OUT(&bid, ID_WIDTH - 1, 0);
AUTO_OUT(&bresp, 1, 0);
AUTO_OUT(&bvalid, 0, 0);
AUTO_IN(&bready, 0, 0);
// ar
AUTO_IN(&arid, ID_WIDTH - 1, 0);
AUTO_IN(&araddr, A_WIDTH - 1, 0);
AUTO_IN(&arlen, 7, 0);
AUTO_IN(&arsize, 2, 0);
AUTO_IN(&arburst, 1, 0);
AUTO_IN(&arvalid, 0, 0);
AUTO_OUT(&arready, 0, 0);
// r
AUTO_OUT(&rid, ID_WIDTH - 1, 0);
AUTO_OUT(&rdata, D_WIDTH - 1, 0);
AUTO_OUT(&rresp, 1, 0);
AUTO_OUT(&rlast, 0, 0);
AUTO_OUT(&rvalid, 0, 0);
AUTO_IN(&rready, 0, 0);
axi4_ref(axi4_ptr<A_WIDTH, D_WIDTH, ID_WIDTH> &ptr) : awid(*(ptr.awid)),
awaddr(*(ptr.awaddr)),
awlen(*(ptr.awlen)),
awsize(*(ptr.awsize)),
awburst(*(ptr.awburst)),
awvalid(*(ptr.awvalid)),
awready(*(ptr.awready)),
wdata(*(ptr.wdata)),
wstrb(*(ptr.wstrb)),
wlast(*(ptr.wlast)),
wvalid(*(ptr.wvalid)),
wready(*(ptr.wready)),
bid(*(ptr.bid)),
bresp(*(ptr.bresp)),
bvalid(*(ptr.bvalid)),
bready(*(ptr.bready)),
arid(*(ptr.arid)),
araddr(*(ptr.araddr)),
arlen(*(ptr.arlen)),
arsize(*(ptr.arsize)),
arburst(*(ptr.arburst)),
arvalid(*(ptr.arvalid)),
arready(*(ptr.arready)),
rid(*(ptr.rid)),
rdata(*(ptr.rdata)),
rresp(*(ptr.rresp)),
rlast(*(ptr.rlast)),
rvalid(*(ptr.rvalid)),
rready(*(ptr.rready))
{
}
axi4_ref(axi4<A_WIDTH, D_WIDTH, ID_WIDTH> &axi4);
};
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
struct axi4
{
AUTO_IN(awid, ID_WIDTH - 1, 0);
AUTO_IN(awaddr, A_WIDTH - 1, 0);
AUTO_IN(awlen, 7, 0);
AUTO_IN(awsize, 2, 0);
AUTO_IN(awburst, 1, 0);
AUTO_IN(awvalid, 0, 0);
AUTO_OUT(awready, 0, 0);
// w
AUTO_IN(wdata, D_WIDTH - 1, 0);
AUTO_IN(wstrb, (D_WIDTH / 8) - 1, 0);
AUTO_IN(wlast, 0, 0);
AUTO_IN(wvalid, 0, 0);
AUTO_OUT(wready, 0, 0);
// b
AUTO_OUT(bid, ID_WIDTH - 1, 0);
AUTO_OUT(bresp, 1, 0);
AUTO_OUT(bvalid, 0, 0);
AUTO_IN(bready, 0, 0);
// ar
AUTO_IN(arid, ID_WIDTH - 1, 0);
AUTO_IN(araddr, A_WIDTH - 1, 0);
AUTO_IN(arlen, 7, 0);
AUTO_IN(arsize, 2, 0);
AUTO_IN(arburst, 1, 0);
AUTO_IN(arvalid, 0, 0);
AUTO_OUT(arready, 0, 0);
// r
AUTO_OUT(rid, ID_WIDTH - 1, 0);
AUTO_OUT(rdata, D_WIDTH - 1, 0);
AUTO_OUT(rresp, 1, 0);
AUTO_OUT(rlast, 0, 0);
AUTO_OUT(rvalid, 0, 0);
AUTO_IN(rready, 0, 0);
axi4()
{
// reset all pointer to zero
memset(this, 0, sizeof(*this));
}
void update_input(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &ref)
{
// aw
awid = ref.awid;
awaddr = ref.awaddr;
awlen = ref.awlen;
awsize = ref.awsize;
awburst = ref.awburst;
awvalid = ref.awvalid;
// w
wdata = ref.wdata;
wstrb = ref.wstrb;
wlast = ref.wlast;
wvalid = ref.wvalid;
// b
bready = ref.bready;
// arid
arid = ref.arid;
araddr = ref.araddr;
arlen = ref.arlen;
arsize = ref.arsize;
arburst = ref.arburst;
arvalid = ref.arvalid;
// r
rready = ref.rready;
}
void update_output(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &ref)
{
ref.awready = awready;
ref.wready = wready;
ref.bid = bid;
ref.bresp = bresp;
ref.bvalid = bvalid;
ref.arready = arready;
ref.rid = rid;
ref.rdata = rdata;
ref.rresp = rresp;
ref.rlast = rlast;
ref.rvalid = rvalid;
}
};
template <unsigned int A_WIDTH, unsigned int D_WIDTH, unsigned int ID_WIDTH>
axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH>::axi4_ref(axi4<A_WIDTH, D_WIDTH, ID_WIDTH> &axi4) : awid(axi4.awid),
awaddr(axi4.awaddr),
awlen(axi4.awlen),
awsize(axi4.awsize),
awburst(axi4.awburst),
awvalid(axi4.awvalid),
awready(axi4.awready),
wdata(axi4.wdata),
wstrb(axi4.wstrb),
wlast(axi4.wlast),
wvalid(axi4.wvalid),
wready(axi4.wready),
bid(axi4.bid),
bresp(axi4.bresp),
bvalid(axi4.bvalid),
bready(axi4.bready),
arid(axi4.arid),
araddr(axi4.araddr),
arlen(axi4.arlen),
arsize(axi4.arsize),
arburst(axi4.arburst),
arvalid(axi4.arvalid),
arready(axi4.arready),
rid(axi4.rid),
rdata(axi4.rdata),
rresp(axi4.rresp),
rlast(axi4.rlast),
rvalid(axi4.rvalid),
rready(axi4.rready)
{
}
enum axi_resp
{
RESP_OKEY = 0,
RESP_EXOKEY = 1,
RESP_SLVERR = 2,
RESP_DECERR = 3
};
enum axi_burst_type
{
BURST_FIXED = 0,
BURST_INCR = 1,
BURST_WRAP = 2,
BURST_RESERVED = 3
};
#endif

@ -0,0 +1,88 @@
#ifndef AXI4_MEM
#define AXI4_MEM
#include "axi4_slave.hpp"
#include <fstream>
#include <iostream>
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
class axi4_mem : public axi4_slave<A_WIDTH, D_WIDTH, ID_WIDTH>
{
public:
axi4_mem(size_t size_bytes)
{
if (size_bytes % (D_WIDTH / 8))
size_bytes += 8 - (size_bytes % (D_WIDTH / 8));
mem = new unsigned char[size_bytes];
mem_size = size_bytes;
}
axi4_mem(size_t size_bytes, const uint8_t *init_binary, size_t init_binary_len) : axi4_mem(size_bytes)
{
assert(init_binary_len <= size_bytes, "init_binary_len is larger than memory size.");
memcpy(mem, init_binary, init_binary_len);
}
~axi4_mem()
{
delete[] mem;
}
bool read(off_t start_addr, size_t size, uint8_t *buffer)
{
if (start_addr + size <= mem_size)
{
memcpy(buffer, &mem[start_addr], size);
return true;
}
else
return false;
}
bool write(off_t start_addr, size_t size, const uint8_t *buffer)
{
if (start_addr + size <= mem_size)
{
memcpy(&mem[start_addr], buffer, size);
return true;
}
else
return false;
}
void load_binary(const char *init_file, uint64_t start_addr = 0)
{
std::ifstream file(init_file, std::ios::in | std::ios::binary | std::ios::ate);
size_t file_size = file.tellg();
file.seekg(std::ios_base::beg);
if (start_addr >= mem_size || file_size > mem_size - start_addr)
{
std::cerr << "memory size is not big enough for init file." << std::endl;
file_size = mem_size;
}
file.read((char *)mem + start_addr, file_size);
}
protected:
axi_resp do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer)
{
if (start_addr + size <= mem_size)
{
memcpy(buffer, &mem[start_addr], size);
return RESP_OKEY;
}
else
return RESP_DECERR;
}
axi_resp do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer)
{
if (start_addr + size <= mem_size)
{
memcpy(&mem[start_addr], buffer, size);
return RESP_OKEY;
}
else
return RESP_DECERR;
}
private:
uint8_t *mem;
size_t mem_size;
};
#endif

@ -0,0 +1,340 @@
#ifndef AXI4_SLAVE
#define AXI4_SLAVE
#include "axi4.hpp"
#include <queue>
#include <algorithm>
#include <utility>
#include <vector>
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
class axi4_slave
{
static_assert(D_WIDTH <= 64, "D_WIDTH should be <= 64.");
static_assert(A_WIDTH <= 64, "A_WIDTH should be <= 64.");
public:
axi4_slave(int delay = 0) : delay(delay)
{
}
void beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
read_channel(pin);
write_channel(pin);
}
void reset()
{
read_busy = false;
read_last = false;
read_wait = false;
read_delay = 0;
write_busy = false;
b_busy = false;
write_delay = 0;
}
protected:
virtual axi_resp do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer) = 0;
virtual axi_resp do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer) = 0;
private:
unsigned int D_bytes = D_WIDTH / 8;
int delay;
private:
bool read_busy = false; // during trascation except last
bool read_last = false; // wait rready and free
bool read_wait = false; // ar ready, but waiting the last read to ready
int read_delay = 0; // delay
uint64_t r_start_addr; // lower bound of transaction address
uint64_t r_current_addr; // current burst address in r_data buffer (physical address % 4096)
AUTO_SIG(arid, ID_WIDTH - 1, 0);
axi_burst_type r_burst_type;
unsigned int r_each_len;
unsigned int r_nr_trans;
unsigned int r_cur_trans;
unsigned int r_tot_len;
bool r_out_ready;
bool r_early_err;
axi_resp r_resp;
uint8_t r_data[4096];
bool read_check()
{
if (r_burst_type == BURST_RESERVED)
return false;
if (r_burst_type == BURST_WRAP && (r_current_addr % r_each_len))
return false;
if (r_burst_type == BURST_WRAP)
{
if (r_nr_trans != 2 || r_nr_trans != 4 || r_nr_trans != 8 || r_nr_trans != 16)
{
return false;
}
}
uint64_t rem_addr = 4096 - (r_start_addr % 4096);
if (r_tot_len > rem_addr)
{
printf("r_tot_len: %d, rem_addr: %ld\n", r_tot_len, rem_addr);
return false;
}
if (r_each_len > D_bytes)
{
printf("r_each_len: %d, D_bytes: %d\n", r_each_len, D_bytes);
return false;
}
return true;
}
void read_beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
pin.rid = arid;
pin.rvalid = 1;
bool update = false;
if (pin.rready || r_cur_trans == 0)
{
r_cur_trans += 1;
update = true;
if (r_cur_trans == r_nr_trans)
{
read_last = true;
read_busy = false;
}
}
pin.rlast = read_last;
if (update)
{
if (r_early_err)
{
pin.rresp = RESP_DECERR;
pin.rdata = 0;
}
else if (r_burst_type == BURST_FIXED)
{
pin.rresp = do_read(static_cast<uint64_t>(r_start_addr), static_cast<uint64_t>(r_tot_len), &r_data[r_start_addr % 4096]);
pin.rdata = *(AUTO_SIG(*, D_WIDTH - 1, 0))(&r_data[(r_start_addr % 4096) - (r_start_addr % D_bytes)]);
}
else
{ // INCR, WRAP
pin.rresp = r_resp;
pin.rdata = *(AUTO_SIG(*, D_WIDTH - 1, 0))(&r_data[r_current_addr - (r_current_addr % D_bytes)]);
r_current_addr += r_each_len - (r_current_addr % r_each_len);
if (r_burst_type == BURST_WRAP && r_current_addr == ((r_start_addr % 4096) + r_each_len * r_nr_trans))
{
r_current_addr = r_start_addr % 4096;
}
}
}
}
void read_init(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
arid = static_cast<unsigned int>(pin.arid);
r_burst_type = static_cast<axi_burst_type>(pin.arburst);
r_each_len = 1 << pin.arsize;
r_nr_trans = pin.arlen + 1;
r_current_addr = (r_burst_type == BURST_WRAP) ? (pin.araddr % 4096) : ((pin.araddr % 4096) - (pin.araddr % r_each_len));
r_start_addr = (r_burst_type == BURST_WRAP) ? (pin.araddr - (pin.araddr % (r_each_len * r_nr_trans))) : pin.araddr;
r_cur_trans = 0;
r_tot_len = ((r_burst_type == BURST_FIXED) ? r_each_len : r_each_len * r_nr_trans) - (r_start_addr % r_each_len); // first beat can be unaligned
r_early_err = !read_check();
assert(!r_early_err, "r early error");
// clear unused bits.
if (r_start_addr % D_bytes)
{
uint64_t clear_addr = r_start_addr % 4096;
clear_addr -= clear_addr % D_bytes;
memset(&r_data[clear_addr], 0x00, D_bytes);
}
if ((r_start_addr + r_tot_len) % D_bytes)
{
uint64_t clear_addr = (r_start_addr + r_tot_len) % 4096;
clear_addr -= (clear_addr % D_bytes);
memset(&r_data[clear_addr], 0x00, D_bytes);
}
// For BURST_FIXED, we call do_read every read burst
if (!r_early_err && r_burst_type != BURST_FIXED)
r_resp = do_read(static_cast<uint64_t>(r_start_addr), static_cast<uint64_t>(r_tot_len), &r_data[r_start_addr % 4096]);
}
void read_channel(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
// Read step 1. release old transaction
if (read_last && pin.rready)
{
read_last = false;
pin.rvalid = 0; // maybe change in the following code
pin.rlast = 0;
if (read_wait)
{
read_wait = false;
read_busy = true;
read_delay = delay;
}
}
// Read step 2. check new address come
if (pin.arready && pin.arvalid)
{
read_init(pin);
if (read_last)
read_wait = true;
else
{
read_busy = true;
read_delay = delay;
}
}
// Read step 3. do read trascation
if (read_busy)
{
if (read_delay)
read_delay--;
else
read_beat(pin);
}
// Read step 4. set arready before new address come, it will change read_busy and read_wait status
pin.arready = !read_busy && !read_wait;
}
private:
bool write_busy = false;
bool b_busy = false;
int write_delay = 0;
uint64_t w_start_addr;
uint64_t w_current_addr;
AUTO_SIG(awid, ID_WIDTH - 1, 0);
axi_burst_type w_burst_type;
unsigned int w_each_len;
int w_nr_trans;
int w_cur_trans;
unsigned int w_tot_len;
bool w_out_ready;
bool w_early_err;
axi_resp w_resp;
uint8_t w_buffer[D_WIDTH / 8];
bool write_check()
{
if (w_burst_type == BURST_RESERVED)
return false; // TODO:去掉了或,不知道对不对
// if (w_burst_type == BURST_RESERVED || w_burst_type) return false;
if (w_burst_type == BURST_WRAP && (w_current_addr % w_each_len))
return false;
if (w_burst_type == BURST_WRAP)
{
if (w_nr_trans != 2 || w_nr_trans != 4 || w_nr_trans != 8 || w_nr_trans != 16)
return false;
}
uint64_t rem_addr = 4096 - (w_start_addr % 4096);
if (w_tot_len > rem_addr)
return false;
if (w_each_len > D_bytes)
return false;
return true;
}
void write_init(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
awid = pin.awid;
w_burst_type = static_cast<axi_burst_type>(pin.awburst);
w_each_len = 1 << pin.awsize;
w_nr_trans = pin.awlen + 1;
w_current_addr = (w_burst_type == BURST_WRAP) ? pin.awaddr : (pin.awaddr - (pin.awaddr % w_each_len));
w_start_addr = (w_burst_type == BURST_WRAP) ? (pin.awaddr - (pin.awaddr % (w_each_len * w_nr_trans))) : pin.awaddr;
w_cur_trans = 0;
w_tot_len = w_each_len * w_nr_trans - (w_start_addr % w_each_len);
w_early_err = !write_check();
assert(!w_early_err, "w early error");
w_resp = RESP_OKEY;
}
// pair<start,len>
std::vector<std::pair<int, int>> strb_to_range(AUTO_IN(wstrb, (D_WIDTH / 8) - 1, 0), int st_pos, int ed_pos)
{
std::vector<std::pair<int, int>> res;
int l = st_pos;
while (l < ed_pos)
{
if ((wstrb >> l) & 1)
{
int r = l;
while ((wstrb >> r) & 1 && r < ed_pos)
r++;
res.emplace_back(l, r - l);
l = r + 1;
}
else
l++;
}
return res;
}
void write_beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
if (pin.wvalid && pin.wready)
{
w_cur_trans += 1;
if (w_cur_trans == w_nr_trans)
{
write_busy = false;
b_busy = true;
if (!pin.wlast)
{
w_early_err = true;
assert(false, "w last error");
}
}
uint64_t addr_base = w_current_addr;
if (w_burst_type != BURST_FIXED)
{
w_current_addr += w_each_len - (addr_base % w_each_len);
if (w_current_addr == (w_start_addr + w_each_len * w_nr_trans))
w_cur_trans = w_start_addr; // warp support
}
uint64_t in_data_pos = addr_base % D_bytes;
addr_base -= addr_base % D_bytes;
uint64_t rem_data_pos = w_each_len - (in_data_pos % w_each_len); // unaligned support
// split discontinuous wstrb bits to small requests
std::vector<std::pair<int, int>> range = strb_to_range(pin.wstrb, in_data_pos, in_data_pos + rem_data_pos);
for (std::pair<int, int> sub_range : range)
{
int &addr = sub_range.first;
int &len = sub_range.second;
memcpy(w_buffer, &(pin.wdata), sizeof(pin.wdata));
w_resp = static_cast<axi_resp>(static_cast<int>(w_resp) | static_cast<int>(do_write(addr_base + addr, len, w_buffer + addr)));
}
}
}
void b_beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
pin.bid = awid;
pin.bresp = w_early_err ? RESP_DECERR : w_resp;
if (pin.bvalid && pin.bready)
b_busy = false;
}
void write_channel(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
{
if (pin.awready && pin.awvalid)
{
write_init(pin);
write_busy = true;
write_delay = delay;
}
if (write_busy)
{
if (write_delay)
write_delay--;
else
write_beat(pin);
}
if (b_busy)
{
b_beat(pin);
}
pin.bvalid = b_busy;
pin.awready = !write_busy && !b_busy;
if (delay)
pin.wready = write_busy && !write_delay;
else
pin.wready = !b_busy;
}
};
#endif

@ -0,0 +1,88 @@
#ifndef AXI4_XBAR_H
#define AXI4_XBAR_H
#include "axi4.hpp"
#include "axi4_slave.hpp"
#include "mmio_dev.hpp"
#include <map>
#include <utility>
#include <algorithm>
#include <climits>
extern long long current_pc;
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
class axi4_xbar : public axi4_slave<A_WIDTH, D_WIDTH, ID_WIDTH>
{
public:
axi4_xbar(int delay = 0) : axi4_slave<A_WIDTH, D_WIDTH, ID_WIDTH>(delay)
{
}
bool add_dev(uint64_t start_addr, uint64_t length, mmio_dev *dev)
{
std::pair<uint64_t, uint64_t> addr_range = std::make_pair(start_addr, start_addr + length);
if (start_addr % length)
{
printf("mmio device start address must be aligned to its length\n");
return false;
}
// check range
auto it = devices.upper_bound(addr_range);
if (it != devices.end())
{
uint64_t l_max = std::max(it->first.first, addr_range.first);
uint64_t r_min = std::min(it->first.second, addr_range.second);
if (l_max < r_min)
return false; // overleap
}
if (it != devices.begin())
{
it = std::prev(it);
uint64_t l_max = std::max(it->first.first, addr_range.first);
uint64_t r_min = std::min(it->first.second, addr_range.second);
if (l_max < r_min)
return false; // overleap
}
// overleap check pass
devices[addr_range] = dev;
return true;
}
protected:
axi_resp do_read(uint64_t start_addr, uint64_t size, unsigned char *buffer)
{
auto it = devices.upper_bound(std::make_pair(start_addr, ULONG_MAX));
if (it == devices.begin())
return RESP_DECERR;
it = std::prev(it);
uint64_t end_addr = start_addr + size;
if (it->first.first <= start_addr && end_addr <= it->first.second)
{
uint64_t dev_size = it->first.second - it->first.first;
return it->second->do_read(start_addr % dev_size, size, buffer) ? RESP_OKEY : RESP_SLVERR;
}
else
return RESP_DECERR;
}
axi_resp do_write(uint64_t start_addr, uint64_t size, const unsigned char *buffer)
{
auto it = devices.upper_bound(std::make_pair(start_addr, ULONG_MAX));
if (it == devices.begin())
return RESP_DECERR;
it = std::prev(it);
uint64_t end_addr = start_addr + size;
if (it->first.first <= start_addr && end_addr <= it->first.second)
{
uint64_t dev_size = it->first.second - it->first.first;
return it->second->do_write(start_addr % dev_size, size, buffer) ? RESP_OKEY : RESP_SLVERR;
}
else
return RESP_DECERR;
}
private:
std::map<std::pair<uint64_t, uint64_t>, mmio_dev *> devices;
};
#endif

@ -1,10 +1,11 @@
#ifndef MMIO_DEV_H
#define MMIO_DEV_H
class mmio_dev {
class mmio_dev
{
public:
virtual bool do_read (uint64_t start_addr, uint64_t size, uint8_t* buffer) = 0;
virtual bool do_write(uint64_t start_addr, uint64_t size, const uint8_t* buffer) = 0;
virtual bool do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer) = 0;
virtual bool do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer) = 0;
};
#endif

@ -1,123 +0,0 @@
#ifndef NSCSCC_SRAM_HPP
#define NSCSCC_SRAM_HPP
#include <verilated.h>
#include <condition_variable>
#include <cstdint>
#include <set>
#define AUTO_SIG(name, msb, lsb) \
typename std::conditional <(msb-lsb+1) <= 8, CData, \
typename std::conditional <(msb-lsb+1) <= 16, SData, \
typename std::conditional <(msb-lsb+1) <= 32, IData, QData >::type >::type >::type name
#define AUTO_IN(name, msb, lsb) AUTO_SIG(name, msb, lsb)
#define AUTO_OUT(name, msb, lsb) AUTO_SIG(name, msb, lsb)
struct nscscc_sram;
struct nscscc_sram_ptr {
// inst channel
AUTO_IN (*inst_sram_en, 0, 0) = NULL;
AUTO_IN (*inst_sram_wen, 3, 0) = NULL;
AUTO_IN (*inst_sram_addr, 31, 0) = NULL;
AUTO_IN (*inst_sram_wdata, 31, 0) = NULL;
AUTO_OUT(*inst_sram_rdata, 31, 0) = NULL;
// data channel
AUTO_IN (*data_sram_en, 0, 0) = NULL;
AUTO_IN (*data_sram_wen, 7, 0) = NULL;
AUTO_IN (*data_sram_addr, 31, 0) = NULL;
AUTO_IN (*data_sram_wdata, 63, 0) = NULL;
AUTO_OUT(*data_sram_rdata, 63, 0) = NULL;
bool check() {
std::set <void*> s;
s.insert((void*)inst_sram_en);
s.insert((void*)inst_sram_wen);
s.insert((void*)inst_sram_addr);
s.insert((void*)inst_sram_wdata);
s.insert((void*)inst_sram_rdata);
s.insert((void*)data_sram_en);
s.insert((void*)data_sram_wen);
s.insert((void*)data_sram_addr);
s.insert((void*)data_sram_wdata);
s.insert((void*)data_sram_rdata);
return s.size() == 10 && s.count(NULL) == 0;
}
};
struct nscscc_sram_ref {
// inst channel
AUTO_IN (&inst_sram_en, 0, 0);
AUTO_IN (&inst_sram_wen, 3, 0);
AUTO_IN (&inst_sram_addr, 31, 0);
AUTO_IN (&inst_sram_wdata, 31, 0);
AUTO_OUT(&inst_sram_rdata, 31, 0);
// data channel
AUTO_IN (&data_sram_en, 0, 0);
AUTO_IN (&data_sram_wen, 7, 0);
AUTO_IN (&data_sram_addr, 31, 0);
AUTO_IN (&data_sram_wdata, 63, 0);
AUTO_OUT(&data_sram_rdata, 63, 0);
nscscc_sram_ref(nscscc_sram_ptr &ptr):
inst_sram_en (*(ptr.inst_sram_en)),
inst_sram_wen (*(ptr.inst_sram_wen)),
inst_sram_addr (*(ptr.inst_sram_addr)),
inst_sram_wdata (*(ptr.inst_sram_wdata)),
inst_sram_rdata (*(ptr.inst_sram_rdata)),
data_sram_en (*(ptr.data_sram_en)),
data_sram_wen (*(ptr.data_sram_wen)),
data_sram_addr (*(ptr.data_sram_addr)),
data_sram_wdata (*(ptr.data_sram_wdata)),
data_sram_rdata (*(ptr.data_sram_rdata))
{}
nscscc_sram_ref(nscscc_sram &sram);
};
struct nscscc_sram {
// inst channel
AUTO_IN (inst_sram_en, 0, 0);
AUTO_IN (inst_sram_wen, 3, 0);
AUTO_IN (inst_sram_addr, 31, 0);
AUTO_IN (inst_sram_wdata, 31, 0);
AUTO_OUT(inst_sram_rdata, 31, 0);
// data channel
AUTO_IN (data_sram_en, 0, 0);
AUTO_IN (data_sram_wen, 7, 0);
AUTO_IN (data_sram_addr, 31, 0);
AUTO_IN (data_sram_wdata, 63, 0);
AUTO_OUT(data_sram_rdata, 63, 0);
nscscc_sram() {
// reset all pointer to zero
memset(this,0,sizeof(*this));
}
void update_input(nscscc_sram_ref &ref) {
inst_sram_en = ref.inst_sram_en;
inst_sram_wen = ref.inst_sram_wen;
inst_sram_addr = ref.inst_sram_addr;
inst_sram_wdata = ref.inst_sram_wdata;
data_sram_en = ref.data_sram_en;
data_sram_wen = ref.data_sram_wen;
data_sram_addr = ref.data_sram_addr;
data_sram_wdata = ref.data_sram_wdata;
}
void update_output(nscscc_sram_ref &ref) {
ref.inst_sram_rdata = inst_sram_rdata;
ref.data_sram_rdata = data_sram_rdata;
}
};
nscscc_sram_ref::nscscc_sram_ref(nscscc_sram &sram):
inst_sram_en (sram.inst_sram_en),
inst_sram_wen (sram.inst_sram_wen),
inst_sram_addr (sram.inst_sram_addr),
inst_sram_wdata (sram.inst_sram_wdata),
inst_sram_rdata (sram.inst_sram_rdata),
data_sram_en (sram.data_sram_en),
data_sram_wen (sram.data_sram_wen),
data_sram_addr (sram.data_sram_addr),
data_sram_wdata (sram.data_sram_wdata),
data_sram_rdata (sram.data_sram_rdata)
{}
#endif

@ -1,76 +0,0 @@
#ifndef NSCSCC_SRAM_SLAVE
#define NSCSCC_SRAM_SLAVE
#include "nscscc_sram.hpp"
#include <utility>
#include <vector>
class nscscc_sram_slave
{
public:
nscscc_sram_slave()
{
}
void beat(nscscc_sram_ref &pin)
{
inst_channel(pin);
data_channel(pin);
}
void inst_channel(nscscc_sram_ref &pin)
{
if (pin.inst_sram_en)
{
assert(!pin.inst_sram_wen);
do_read(pin.inst_sram_addr & address_mask, 4, (uint8_t *)&(pin.inst_sram_rdata));
}
}
void data_channel(nscscc_sram_ref &pin)
{
if (pin.data_sram_en)
{
if (pin.data_sram_wen)
{
std::vector<std::pair<int, int>> write_range = strb_to_range(pin.data_sram_wen);
for (std::pair<int, int> sub_range : write_range)
{
int &addr = sub_range.first;
int &len = sub_range.second;
do_write(((pin.data_sram_addr & address_mask) & 0xfffffff8) + addr, len, ((uint8_t *)&(pin.data_sram_wdata)) + addr);
}
}
do_read(pin.data_sram_addr & address_mask, 8, (uint8_t *)&(pin.data_sram_rdata));
}
}
void set_address_mask(uint32_t new_mask)
{
address_mask = new_mask;
}
protected:
virtual void do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer) = 0;
virtual void do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer) = 0;
private:
uint32_t address_mask = 0xffffffff;
std::vector<std::pair<int, int>> strb_to_range(AUTO_IN(wen, 7, 0))
{
std::vector<std::pair<int, int>> res;
int l = 0;
while (l < 8)
{
if ((wen >> l) & 1)
{
int r = l;
while ((wen >> r) & 1 && r < 8)
r++;
res.emplace_back(l, r - l);
l = r + 1;
}
else
l++;
}
return res;
}
};
#endif

@ -1,94 +0,0 @@
#ifndef NSCSCC_SRAM_XBAR
#define NSCSCC_SRAM_XBAR
#include "nscscc_sram_slave.hpp"
#include "mmio_dev.hpp"
#include <map>
#include <utility>
#include <algorithm>
#include <climits>
void debug()
{
}
class nscscc_sram_xbar : public nscscc_sram_slave
{
public:
nscscc_sram_xbar()
{
}
bool add_dev(uint64_t start_addr, uint64_t length, mmio_dev *dev, bool byte_mode = false)
{
std::tuple<uint64_t, uint64_t, bool> addr_range = std::make_tuple(start_addr, start_addr + length, byte_mode);
if (start_addr % length)
return false;
// check range
auto it = devices.upper_bound(addr_range);
if (it != devices.end())
{
uint64_t l_max = std::max(std::get<0>(it->first), std::get<0>(addr_range));
uint64_t r_min = std::min(std::get<1>(it->first), std::get<1>(addr_range));
if (l_max < r_min)
return false; // overleap
}
if (it != devices.begin())
{
it = std::prev(it);
uint64_t l_max = std::max(std::get<0>(it->first), std::get<0>(addr_range));
uint64_t r_min = std::min(std::get<1>(it->first), std::get<1>(addr_range));
if (l_max < r_min)
return false; // overleap
}
// overleap check pass
devices[addr_range] = dev;
return true;
}
protected:
void do_read(uint64_t start_addr, uint64_t size, unsigned char *buffer)
{
auto it = devices.upper_bound(std::make_tuple(start_addr, ULONG_MAX, true));
if (it == devices.begin())
{
*((uint32_t *)buffer) = 0xdec0dee3;
return;
}
it = std::prev(it);
if (std::get<0>(it->first) <= start_addr && start_addr + 1 <= std::get<1>(it->first))
{
uint64_t dev_size = std::get<1>(it->first) - std::get<0>(it->first);
if (std::get<2>(it->first))
size = 1; // treat as byte device
else
start_addr -= start_addr & (size - 1);
it->second->do_read(start_addr % dev_size, size, buffer + (start_addr % size));
}
else
*((uint32_t *)buffer) = 0xdec0dee3;
}
void do_write(uint64_t start_addr, uint64_t size, const unsigned char *buffer)
{
auto it = devices.upper_bound(std::make_tuple(start_addr, ULONG_MAX, true));
if (it == devices.begin())
{
*((uint32_t *)buffer) = 0xdec0dee3;
return;
}
it = std::prev(it);
uint64_t end_addr = start_addr + size;
if (std::get<0>(it->first) <= start_addr && end_addr <= std::get<1>(it->first))
{
uint64_t dev_size = std::get<1>(it->first) - std::get<0>(it->first);
it->second->do_write(start_addr % dev_size, size, buffer);
}
else
*((uint32_t *)buffer) = 0xdec0dee3;
}
private:
std::map<std::tuple<uint64_t, uint64_t, bool>, mmio_dev *> devices;
};
#endif

@ -48,7 +48,8 @@ public:
uint64_t debug_reg_num;
uint64_t debug_reg_wdata;
uint32_t debug_inst;
bool debug_is_branch;
bool debug_is_mcycle;
bool debug_is_minstret;
bool int_allow;
bool difftest_mode = false;
rv_core(rv_systembus &systembus, uint8_t hart_id = 0) : systembus(systembus), priv(hart_id, pc, systembus)
@ -110,9 +111,10 @@ private:
void exec(bool meip, bool msip, bool mtip, bool seip)
{
debug_pc = pc;
debug_is_branch = false;
debug_reg_num = 0;
debug_reg_wdata = 0;
debug_is_mcycle = false;
debug_is_minstret = false;
if (run_riscv_test && priv.get_cycle() >= 2e6) // 默认是1e6
{
printf("\033[31mTest timeout! at pc 0x%lx\n\033[0m", pc);
@ -186,7 +188,6 @@ private:
priv.raise_trap(csr_cause_def(exc_instr_misalign), npc);
else
{
debug_is_branch = true;
set_GPR(inst->j_type.rd, pc + 4);
pc = npc;
new_pc = true;
@ -202,7 +203,6 @@ private:
priv.raise_trap(csr_cause_def(exc_instr_misalign), npc);
else
{
debug_is_branch = true;
set_GPR(inst->j_type.rd, pc + 4);
pc = npc;
new_pc = true;
@ -218,7 +218,6 @@ private:
case FUNCT3_BEQ:
if (GPR[inst->b_type.rs1] == GPR[inst->b_type.rs2])
{
debug_is_branch = true;
npc = pc + offset;
new_pc = true;
}
@ -226,7 +225,6 @@ private:
case FUNCT3_BNE:
if (GPR[inst->b_type.rs1] != GPR[inst->b_type.rs2])
{
debug_is_branch = true;
npc = pc + offset;
new_pc = true;
}
@ -234,7 +232,6 @@ private:
case FUNCT3_BLT:
if (GPR[inst->b_type.rs1] < GPR[inst->b_type.rs2])
{
debug_is_branch = true;
npc = pc + offset;
new_pc = true;
}
@ -242,7 +239,6 @@ private:
case FUNCT3_BGE:
if (GPR[inst->b_type.rs1] >= GPR[inst->b_type.rs2])
{
debug_is_branch = true;
npc = pc + offset;
new_pc = true;
}
@ -250,7 +246,6 @@ private:
case FUNCT3_BLTU:
if ((uint64_t)GPR[inst->b_type.rs1] < (uint64_t)GPR[inst->b_type.rs2])
{
debug_is_branch = true;
npc = pc + offset;
new_pc = true;
}
@ -258,7 +253,6 @@ private:
case FUNCT3_BGEU:
if ((uint64_t)GPR[inst->b_type.rs1] >= (uint64_t)GPR[inst->b_type.rs2])
{
debug_is_branch = true;
npc = pc + offset;
new_pc = true;
}
@ -727,7 +721,11 @@ private:
else
{
printf("Failed with value 0x%lx\n", GPR[10]);
dump_pc_history();
while (!trace.empty())
{
printf("%lx\n", trace.front());
trace.pop();
}
exit(1);
}
}
@ -807,7 +805,14 @@ private:
if (!ri)
ri = !priv.csr_write(csr_index, GPR[inst->i_type.rs1]);
if (!ri && inst->i_type.rd)
{
if (csr_index == csr_mcycle || csr_index == csr_minstret)
{
debug_is_mcycle = csr_index == csr_mcycle;
debug_is_minstret = csr_index == csr_minstret;
}
set_GPR(inst->i_type.rd, csr_result);
}
break;
}
case FUNCT3_CSRRS:
@ -820,7 +825,14 @@ private:
if (!ri && inst->i_type.rs1)
ri = !priv.csr_write(csr_index, csr_result | GPR[inst->i_type.rs1]);
if (!ri && inst->i_type.rd)
{
if (csr_index == csr_mcycle || csr_index == csr_minstret)
{
debug_is_mcycle = csr_index == csr_mcycle;
debug_is_minstret = csr_index == csr_minstret;
}
set_GPR(inst->i_type.rd, csr_result);
}
break;
}
case FUNCT3_CSRRC:
@ -833,7 +845,14 @@ private:
if (!ri && inst->i_type.rs1)
ri = !priv.csr_write(csr_index, csr_result & (~GPR[inst->i_type.rs1]));
if (!ri && inst->i_type.rd)
{
if (csr_index == csr_mcycle || csr_index == csr_minstret)
{
debug_is_mcycle = csr_index == csr_mcycle;
debug_is_minstret = csr_index == csr_minstret;
}
set_GPR(inst->i_type.rd, csr_result);
}
break;
}
case FUNCT3_CSRRWI:
@ -846,7 +865,14 @@ private:
if (!ri)
ri = !priv.csr_write(csr_index, inst->i_type.rs1);
if (!ri && inst->i_type.rd)
{
if (csr_index == csr_mcycle || csr_index == csr_minstret)
{
debug_is_mcycle = csr_index == csr_mcycle;
debug_is_minstret = csr_index == csr_minstret;
}
set_GPR(inst->i_type.rd, csr_result);
}
break;
}
case FUNCT3_CSRRSI:
@ -859,7 +885,14 @@ private:
if (!ri && inst->i_type.rs1)
ri = !priv.csr_write(csr_index, csr_result | inst->i_type.rs1);
if (!ri && inst->i_type.rd)
{
if (csr_index == csr_mcycle || csr_index == csr_minstret)
{
debug_is_mcycle = csr_index == csr_mcycle;
debug_is_minstret = csr_index == csr_minstret;
}
set_GPR(inst->i_type.rd, csr_result);
}
break;
}
case FUNCT3_CSRRCI:
@ -872,7 +905,14 @@ private:
if (!ri && inst->i_type.rs1)
ri = !priv.csr_write(csr_index, csr_result & (~(inst->i_type.rs1)));
if (!ri && inst->i_type.rd)
{
if (csr_index == csr_mcycle || csr_index == csr_minstret)
{
debug_is_mcycle = csr_index == csr_mcycle;
debug_is_minstret = csr_index == csr_minstret;
}
set_GPR(inst->i_type.rd, csr_result);
}
break;
}
default:

@ -8,129 +8,173 @@
// We need 2 context corresponding to meip and seip per hart.
template <int nr_source = 1, int nr_context = 2>
class rv_plic : public mmio_dev {
class rv_plic : public mmio_dev
{
public:
rv_plic() {
for (int i=0;i<(nr_source+1);i++) {
rv_plic()
{
for (int i = 0; i < (nr_source + 1); i++)
{
priority[i] = 0;
}
for (int i=0;i<((nr_source+1+31)/32);i++) {
for (int i = 0; i < ((nr_source + 1 + 31) / 32); i++)
{
pending[i] = 0;
claimed[i] = 0;
}
for (int i=0;i<nr_context;i++) {
for (int j=0;j<((nr_source+1+31)/32);j++) enable[i][j] = 0;
for (int i = 0; i < nr_context; i++)
{
for (int j = 0; j < ((nr_source + 1 + 31) / 32); j++)
enable[i][j] = 0;
threshold[i] = 0;
claim[i] = 0;
}
}
void update_ext(int source_id, bool fired) {
pending[source_id/32] &= ~(1u << (source_id % 32));
if (fired) pending[source_id/32] |= 1u << (source_id % 32);
void update_ext(int source_id, bool fired)
{
pending[source_id / 32] &= ~(1u << (source_id % 32));
if (fired)
pending[source_id / 32] |= 1u << (source_id % 32);
}
bool get_int(int context_id) {
bool get_int(int context_id)
{
uint64_t max_priority = 0;
uint64_t max_priority_int = 0;
for (int i=1;i<=nr_source;i++) {
if (priority[i] >= threshold[context_id] && (pending[i/32]>>(i%32)) && (enable[context_id][i/32]>>(i%32)) && !(claimed[i/32]>>(i%32))) {
if (priority[i] > max_priority) {
for (int i = 1; i <= nr_source; i++)
{
if (priority[i] >= threshold[context_id] && (pending[i / 32] >> (i % 32)) && (enable[context_id][i / 32] >> (i % 32)) && !(claimed[i / 32] >> (i % 32)))
{
if (priority[i] > max_priority)
{
max_priority = priority[i];
max_priority_int = i;
}
}
}
if (max_priority_int) claim[context_id] = max_priority_int;
else claim[context_id] = 0;
if (max_priority_int)
claim[context_id] = max_priority_int;
else
claim[context_id] = 0;
return claim[context_id] != 0;
}
bool do_read(uint64_t start_addr, uint64_t size, unsigned char* buffer) {
bool do_read(uint64_t start_addr, uint64_t size, unsigned char *buffer)
{
// printf("PLIC read: 0x%lx, size: 0x%lx\n", start_addr, size);
// assert(size == 4);
if (start_addr + size <= 0x1000) { // [0x4,0x1000] interrupt source priority
if (start_addr == 0) return false;
if (start_addr > 4 * nr_source || start_addr + size > 4 * (nr_source + 1)) return false;
*((uint32_t*)buffer) = priority[start_addr/4];
if (start_addr + size <= 0x1000)
{ // [0x4,0x1000] interrupt source priority
if (start_addr == 0)
return false;
if (start_addr > 4 * nr_source || start_addr + size > 4 * (nr_source + 1))
return false;
*((uint32_t *)buffer) = priority[start_addr / 4];
return true;
}
else if (start_addr + size <= 0x1080) { // [0x1000,0x1080] interrupt pending bits
else if (start_addr + size <= 0x1080)
{ // [0x1000,0x1080] interrupt pending bits
uint64_t idx = (start_addr - 0x1000) / 4;
if (idx > nr_source) return false;
*((uint32_t*)buffer) = pending[idx];
if (idx > nr_source)
return false;
*((uint32_t *)buffer) = pending[idx];
return true;
}
else if (start_addr + size <= 0x2000) {
return false; // error
else if (start_addr + size <= 0x2000)
{
return false; // error
}
else if (start_addr + size <= 0x200000) { // enable bits for sources on context
else if (start_addr + size <= 0x200000)
{ // enable bits for sources on context
uint64_t context_id = (start_addr - 0x2000) / 0x80;
uint64_t pos = start_addr % 0x80;
if (context_id >= nr_context) return false;
if (pos > nr_source) return false;
*((uint32_t*)buffer) = enable[context_id][pos];
if (context_id >= nr_context)
return false;
if (pos > nr_source)
return false;
*((uint32_t *)buffer) = enable[context_id][pos];
return true;
}
else { // priority threshold and claim/complete
else
{ // priority threshold and claim/complete
uint64_t context_id = (start_addr - 0x200000) / 0x1000;
if (context_id > nr_context) return false;
if (context_id > nr_context)
return false;
uint64_t offset = start_addr % 0x1000;
if (offset == 0) { // priority threshold
*((uint32_t*)buffer) = threshold[context_id];
if (offset == 0)
{ // priority threshold
*((uint32_t *)buffer) = threshold[context_id];
return true;
}
else if (offset == 4) { // claim/complete
(*((uint32_t*)buffer)) = claim[context_id];
claimed[claim[context_id]/32] |= 1u << (claim[context_id]%32);
else if (offset == 4)
{ // claim/complete
(*((uint32_t *)buffer)) = claim[context_id];
claimed[claim[context_id] / 32] |= 1u << (claim[context_id] % 32);
return true;
}
else return false;
else
return false;
}
return true;
}
bool do_write(uint64_t start_addr, uint64_t size, const unsigned char* buffer) {
if (start_addr + size <= 0x1000) { // [0x4,0x1000] interrupt source priority
if (start_addr == 0) return false;
if (start_addr > 4 * nr_source || start_addr + size > 4 * (nr_source + 1)) return false;
priority[start_addr/4] = *((uint32_t*)buffer);
bool do_write(uint64_t start_addr, uint64_t size, const unsigned char *buffer)
{
if (start_addr + size <= 0x1000)
{ // [0x4,0x1000] interrupt source priority
if (start_addr == 0)
return false;
if (start_addr > 4 * nr_source || start_addr + size > 4 * (nr_source + 1))
return false;
priority[start_addr / 4] = *((uint32_t *)buffer);
return true;
}
else if (start_addr + size <= 0x1080) { // [0x1000,0x1080] interrupt pending bits
else if (start_addr + size <= 0x1080)
{ // [0x1000,0x1080] interrupt pending bits
return true;
}
else if (start_addr + size <= 0x2000) {
return false; // error
else if (start_addr + size <= 0x2000)
{
return false; // error
}
else if (start_addr + size <= 0x200000) { // enable bits for sources on context
else if (start_addr + size <= 0x200000)
{ // enable bits for sources on context
uint64_t context_id = (start_addr - 0x2000) / 0x80;
uint64_t pos = start_addr % 0x80;
if (context_id >= nr_context) return false;
if (pos > nr_source) return false;
enable[context_id][pos] = *((uint32_t*)buffer);
if (context_id >= nr_context)
return false;
if (pos > nr_source)
return false;
enable[context_id][pos] = *((uint32_t *)buffer);
return true;
}
else { // priority threshold and claim/complete
else
{ // priority threshold and claim/complete
uint64_t context_id = (start_addr - 0x200000) / 0x1000;
if (context_id > nr_context) return false;
if (context_id > nr_context)
return false;
uint64_t offset = start_addr % 0x1000;
if (offset == 0) { // priority threshold
threshold[context_id] = *((uint32_t*)buffer);
if (offset == 0)
{ // priority threshold
threshold[context_id] = *((uint32_t *)buffer);
return true;
}
else if (offset == 4) { // claim/complete
claimed[(*((uint32_t*)buffer))/32] &= ~(1u << ((*((uint32_t*)buffer)%32)));
else if (offset == 4)
{ // claim/complete
claimed[(*((uint32_t *)buffer)) / 32] &= ~(1u << ((*((uint32_t *)buffer) % 32)));
return true;
}
else return false;
else
return false;
}
return true;
}
private:
// source 0 is reserved.
uint32_t priority[nr_source+1];
uint32_t pending[(nr_source+1+31)/32];
uint32_t enable[nr_context][(nr_source+1+31)/32];
uint32_t priority[nr_source + 1];
uint32_t pending[(nr_source + 1 + 31) / 32];
uint32_t enable[nr_context][(nr_source + 1 + 31) / 32];
uint32_t threshold[nr_context];
uint32_t claim[nr_context];
uint32_t claimed[(nr_source+1+31)/32];
uint32_t claimed[(nr_source + 1 + 31) / 32];
};
#endif

@ -14,7 +14,6 @@ extern bool run_riscv_test;
extern bool perf_counter;
extern long long total_instr;
extern long long total_cycle;
extern bool only_modeM;
class rv_priv
{
public:
@ -38,17 +37,9 @@ public:
next_priv = M_MODE;
status = 0;
csr_mstatus_def *mstatus = (csr_mstatus_def *)&status;
mstatus->uxl = 2;
csr_misa_def *isa = (csr_misa_def *)&misa;
if (only_modeM && 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');
}
isa->ext = rv_ext('i') | rv_ext('m') | rv_ext('u');
isa->mxl = 2; // rv64
isa->blank = 0;
medeleg = 0;
@ -248,28 +239,20 @@ public:
{
csr_mstatus_def *nstatus = (csr_mstatus_def *)&csr_data;
csr_mstatus_def *mstatus = (csr_mstatus_def *)&status;
if (only_modeM && run_riscv_test)
{
mstatus->mie = nstatus->mie;
mstatus->mpie = nstatus->mpie;
}
else
{
// mstatus->sie = nstatus->sie;
mstatus->mie = nstatus->mie;
// mstatus->spie = nstatus->spie;
mstatus->mpie = nstatus->mpie;
assert(mstatus->spie != 2);
assert(mstatus->mpie != 2);
// mstatus->spp = nstatus->spp;
mstatus->mpp = (nstatus->mpp == 3 || nstatus->mpp == 0) ? nstatus->mpp : 0;
mstatus->mprv = nstatus->mprv;
// mstatus->sum = nstatus->sum; // always true
// mstatus->mxr = nstatus->mxr; // always true
// mstatus->tvm = nstatus->tvm;
// mstatus->tw = nstatus->tw; // not supported but wfi impl as nop
// mstatus->tsr = nstatus->tsr;
}
// mstatus->sie = nstatus->sie;
mstatus->mie = nstatus->mie;
// mstatus->spie = nstatus->spie;
mstatus->mpie = nstatus->mpie;
assert(mstatus->spie != 2);
assert(mstatus->mpie != 2);
// mstatus->spp = nstatus->spp;
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
// mstatus->tvm = nstatus->tvm;
// mstatus->tw = nstatus->tw; // not supported but wfi impl as nop
// mstatus->tsr = nstatus->tsr;
break;
}
case csr_mie:

@ -17,8 +17,6 @@ bool dual_issue = false;
bool perf_counter = false;
bool init_gprs = false;
bool write_append = false;
bool has_delayslot = false;
bool only_modeM = false;
const uint64_t commit_timeout = 3000;
const uint64_t print_pc_cycle = 5e5;
long trace_start_time = 0; // -starttrace [time]
@ -47,9 +45,9 @@ void assert(bool expr, const char *msg = "")
}
}
#include "nscscc_sram.hpp"
#include "nscscc_sram_slave.hpp"
#include "nscscc_sram_xbar.hpp"
#include "axi4.hpp"
#include "axi4_mem.hpp"
#include "axi4_xbar.hpp"
#include "mmio_mem.hpp"
#include <iostream>
@ -59,21 +57,47 @@ void assert(bool expr, const char *msg = "")
#include <csignal>
#include <sstream>
void connect_wire(nscscc_sram_ptr &sram_ptr, Vtop *top)
void connect_wire(axi4_ptr<32, 64, 4> &mmio_ptr, Vtop *top)
{
sram_ptr.inst_sram_en = &(top->inst_sram_en);
sram_ptr.inst_sram_addr = &(top->inst_sram_addr);
sram_ptr.inst_sram_wen = &(top->inst_sram_wen);
sram_ptr.inst_sram_rdata = &(top->inst_sram_rdata);
sram_ptr.inst_sram_wdata = &(top->inst_sram_wdata);
sram_ptr.data_sram_en = &(top->data_sram_en);
sram_ptr.data_sram_addr = &(top->data_sram_addr);
sram_ptr.data_sram_wen = &(top->data_sram_wen);
sram_ptr.data_sram_rdata = &(top->data_sram_rdata);
sram_ptr.data_sram_wdata = &(top->data_sram_wdata);
// connect
// mmio
// aw
mmio_ptr.awaddr = &(top->axi_awaddr);
mmio_ptr.awburst = &(top->axi_awburst);
mmio_ptr.awid = &(top->axi_awid);
mmio_ptr.awlen = &(top->axi_awlen);
mmio_ptr.awready = &(top->axi_awready);
mmio_ptr.awsize = &(top->axi_awsize);
mmio_ptr.awvalid = &(top->axi_awvalid);
// w
mmio_ptr.wdata = &(top->axi_wdata);
mmio_ptr.wlast = &(top->axi_wlast);
mmio_ptr.wready = &(top->axi_wready);
mmio_ptr.wstrb = &(top->axi_wstrb);
mmio_ptr.wvalid = &(top->axi_wvalid);
// b
mmio_ptr.bid = &(top->axi_bid);
mmio_ptr.bready = &(top->axi_bready);
mmio_ptr.bresp = &(top->axi_bresp);
mmio_ptr.bvalid = &(top->axi_bvalid);
// ar
mmio_ptr.araddr = &(top->axi_araddr);
mmio_ptr.arburst = &(top->axi_arburst);
mmio_ptr.arid = &(top->axi_arid);
mmio_ptr.arlen = &(top->axi_arlen);
mmio_ptr.arready = &(top->axi_arready);
mmio_ptr.arsize = &(top->axi_arsize);
mmio_ptr.arvalid = &(top->axi_arvalid);
// r
mmio_ptr.rdata = &(top->axi_rdata);
mmio_ptr.rid = &(top->axi_rid);
mmio_ptr.rlast = &(top->axi_rlast);
mmio_ptr.rready = &(top->axi_rready);
mmio_ptr.rresp = &(top->axi_rresp);
mmio_ptr.rvalid = &(top->axi_rvalid);
}
void riscv_test_run(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test_path)
void riscv_test_run(Vtop *top, axi4_ref<32, 64, 4> &mmio_ref, const char *riscv_test_path)
{
// setup cemu {
@ -95,9 +119,9 @@ void riscv_test_run(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
// setup cemu }
// setup rtl {
nscscc_sram mmio_sigs;
nscscc_sram_ref mmio_sigs_ref(mmio_sigs);
nscscc_sram_xbar mmio;
axi4<32, 64, 4> mmio_sigs;
axi4_ref<32, 64, 4> mmio_sigs_ref(mmio_sigs);
axi4_xbar<32, 64, 4> mmio;
mmio_mem rtl_mem(128 * 1024 * 1024, riscv_test_path);
assert(mmio.add_dev(0x80000000, 0x80000000, &rtl_mem));
@ -113,8 +137,6 @@ void riscv_test_run(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
uint64_t ticks = 0;
uint64_t last_commit = ticks;
uint64_t pc_cnt = print_pc_cycle;
int delayslot_cnt = 0;
bool delayslot_flag = true;
int delay = 1500;
while (!Verilated::gotFinish() && sim_time > 0 && running)
{
@ -144,18 +166,7 @@ void riscv_test_run(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
if (((top->clock && !dual_issue) || dual_issue) && top->debug_commit)
{ // instr retire
// cemu_rvcore.import_diff_test_info(top->debug_csr_mcycle, top->debug_csr_minstret, top->debug_csr_mip, top->debug_csr_interrupt);
if (has_delayslot)
{
if (!delayslot_cnt)
{
cemu_rvcore.step(0, 0, 0, 0);
delayslot_flag = true;
}
}
else
{
cemu_rvcore.step(0, 0, 0, 0);
}
cemu_rvcore.step(0, 0, 0, 0);
last_commit = ticks;
if (pc_cnt++ >= print_pc_cycle && print_pc)
{
@ -165,8 +176,7 @@ void riscv_test_run(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
if ((top->debug_pc != cemu_rvcore.debug_pc ||
cemu_rvcore.debug_reg_num != 0 &&
(top->debug_rf_wnum != cemu_rvcore.debug_reg_num ||
top->debug_rf_wdata != cemu_rvcore.debug_reg_wdata)) &&
!delayslot_cnt)
top->debug_rf_wdata != cemu_rvcore.debug_reg_wdata && !(cemu_rvcore.debug_is_mcycle || cemu_rvcore.debug_is_minstret))))
{
printf("\033[1;31mError!\033[0m\n");
printf("reference: PC = 0x%016lx, wb_rf_wnum = 0x%02lx, wb_rf_wdata = 0x%016lx\n", cemu_rvcore.debug_pc, cemu_rvcore.debug_reg_num, cemu_rvcore.debug_reg_wdata);
@ -182,18 +192,13 @@ void riscv_test_run(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
else if (delay-- == 0)
running = false;
}
// ==========================
if (has_delayslot)
else
{
if (delayslot_cnt > 0)
delayslot_cnt--;
if (cemu_rvcore.debug_is_branch && delayslot_flag)
if (cemu_rvcore.debug_is_mcycle || cemu_rvcore.debug_is_minstret)
{
delayslot_cnt = 2;
delayslot_flag = false;
cemu_rvcore.set_GPR(cemu_rvcore.debug_reg_num, top->debug_rf_wdata);
}
}
// ==========================
}
if (trace_on)
{
@ -214,7 +219,7 @@ void riscv_test_run(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
printf("total_ticks: %lu\n", ticks);
}
void make_cpu_trace(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test_path)
void make_cpu_trace(Vtop *top, axi4_ref<32, 64, 4> &mmio_ref, const char *riscv_test_path)
{
// setup cemu {
@ -235,9 +240,9 @@ void make_cpu_trace(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
// setup cemu }
// setup rtl {
nscscc_sram mmio_sigs;
nscscc_sram_ref mmio_sigs_ref(mmio_sigs);
nscscc_sram_xbar mmio;
axi4<32, 64, 4> mmio_sigs;
axi4_ref<32, 64, 4> mmio_sigs_ref(mmio_sigs);
axi4_xbar<32, 64, 4> mmio;
mmio_mem rtl_mem(128 * 1024 * 1024, riscv_test_path);
@ -269,8 +274,6 @@ void make_cpu_trace(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
uint64_t ticks = 0;
uint64_t last_commit = ticks;
int delay = 10;
int delayslot_cnt = 0;
bool delayslot_flag = true;
while (!Verilated::gotFinish() && sim_time > 0 && running)
{
if (rst_ticks > 0)
@ -292,24 +295,13 @@ void make_cpu_trace(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
}
if (((top->clock && !dual_issue) || dual_issue) && top->debug_commit)
{ // instr retire
if (has_delayslot)
{
if (!delayslot_cnt)
{
cemu_rvcore.step(0, 0, 0, 0);
delayslot_flag = true;
}
}
else
{
cemu_rvcore.step(0, 0, 0, 0);
}
cemu_rvcore.step(0, 0, 0, 0);
last_commit = ticks;
if ((top->debug_pc != cemu_rvcore.debug_pc ||
cemu_rvcore.debug_reg_num != 0 &&
(top->debug_rf_wnum != cemu_rvcore.debug_reg_num ||
top->debug_rf_wdata != cemu_rvcore.debug_reg_wdata)) &&
!delayslot_cnt)
running)
{
printf("\033[1;31mError!\033[0m\n");
printf("reference: PC = 0x%016lx, wb_rf_wnum = 0x%02lx, wb_rf_wdata = 0x%016lx\n", cemu_rvcore.debug_pc, cemu_rvcore.debug_reg_num, cemu_rvcore.debug_reg_wdata);
@ -325,18 +317,6 @@ void make_cpu_trace(Vtop *top, nscscc_sram_ref &mmio_ref, const char *riscv_test
else if (delay-- == 0)
running = false;
}
// ==========================
if (has_delayslot)
{
if (delayslot_cnt > 0)
delayslot_cnt--;
if (cemu_rvcore.debug_is_branch && delayslot_flag)
{
delayslot_cnt = 2;
delayslot_flag = false;
}
}
// ==========================
fprintf(trace_file, "1 %016lx %02lx %016lx\n", cemu_rvcore.debug_pc, cemu_rvcore.debug_reg_num, cemu_rvcore.debug_reg_wdata);
}
if (trace_on)
@ -424,13 +404,9 @@ int main(int argc, char **argv, char **env)
{
write_append = true;
}
else if (strcmp(argv[i], "-hasdelayslot") == 0) // 是否有延迟槽
{
has_delayslot = true;
}
else if (strcmp(argv[i], "-onlymodem") == 0) // 只有modeM
else if (strcmp(argv[i], "-dual_issue") == 0) // 追加写入
{
only_modeM = true;
dual_issue = true;
}
else
{
@ -442,12 +418,12 @@ int main(int argc, char **argv, char **env)
// setup soc
Vtop *top = new Vtop;
nscscc_sram_ptr mmio_ptr;
axi4_ptr<32, 64, 4> mmio_ptr;
connect_wire(mmio_ptr, top);
assert(mmio_ptr.check());
nscscc_sram_ref mmio_ref(mmio_ptr);
axi4_ref<32, 64, 4> mmio_ref(mmio_ptr);
switch (run_mode)
{

Loading…
Cancel
Save