├── .gitignore ├── vsrc ├── util │ ├── README.md │ ├── IBusToCBus.sv │ ├── CBusMultiplexer.sv │ ├── DBusToCBus.sv │ ├── CBusArbiter.sv │ ├── SimpleArbiter.sv │ └── CBusToAXI.sv ├── add_sources.tcl ├── include │ ├── pipes.sv │ ├── config.sv │ ├── access.svh │ ├── bus_decl │ └── common.sv ├── pipeline │ ├── execute │ │ └── alu.sv │ ├── regfile │ │ └── regfile.sv │ └── core.sv ├── cache │ ├── ICache.sv │ └── DCache.sv ├── mycpu_top.sv ├── mycpu_top_nodelay.sv ├── VTop.sv ├── SimTop.sv ├── ram │ ├── template.sv │ ├── RAM_SimpleDualPort.sv │ ├── LUTRAM_DualPort.sv │ ├── RAM_SinglePort.sv │ └── RAM_TrueDualPort.sv └── VCacheTop.sv ├── vivado ├── test1 │ └── .gitignore ├── test2 │ ├── .gitignore │ └── project │ │ └── project_1.xpr ├── test3 │ ├── .gitignore │ └── project │ │ └── project_1.xpr └── src │ ├── add_sources.tcl │ ├── with_delay │ ├── basys3_top.sv │ ├── simtop.sv │ ├── cbus_crossbar.sv │ ├── soc_top.sv │ └── bram_wrapper.sv │ ├── without_delay │ ├── basys3_top_nodelay.sv │ ├── simtop_nodelay.sv │ ├── dist_ram_wrapper.sv │ └── soc_top_nodelay.sv │ ├── device.svh │ ├── device.sv │ └── Basys-3-Master.xdc ├── verilate ├── vsrc │ ├── VCacheTop │ │ ├── Makefile.deps.mk │ │ ├── defs.h │ │ ├── vmain.cpp │ │ ├── tests.cpp │ │ ├── cache_ref.h │ │ ├── mycache.cpp │ │ ├── mycache.h │ │ └── cache_ref.cpp │ ├── main.cpp │ ├── cbus_device.cpp │ ├── memory.cpp │ ├── model.cpp │ ├── confreg.cpp │ ├── testbench.cpp │ └── common.cpp ├── include │ ├── diff.h │ ├── reference.h │ ├── axi.h │ ├── model.h │ ├── memory.h │ ├── confreg.h │ ├── runner.h │ ├── testbench.h │ ├── cell.h │ └── common.h ├── Makefile.verilate.mk ├── Makefile.include └── Makefile.vsim.mk ├── riscv64-nemu-interpreter-so ├── ready-to-run ├── lab1 │ ├── lab1-test.bin │ └── lab1-test-a.bin ├── lab4 │ ├── all-test-priv.bin │ └── all-test-privfull.bin ├── lab2 │ └── all-test-rv64i.bin ├── lab3 │ └── all-test-rv64im.bin └── challenge │ └── microbench-riscv64-nutshell.bin ├── .gitmodules ├── Makefile.include ├── readme.md └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | /gtkwave 2 | /NEMU 3 | /scripts 4 | **/build/ 5 | -------------------------------------------------------------------------------- /vsrc/util/README.md: -------------------------------------------------------------------------------- 1 | Helper modules, e.g. Arbiter, IBusToCBus. -------------------------------------------------------------------------------- /vivado/test1/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | !src/*.xci 4 | !.gitignore 5 | !*.xpr 6 | -------------------------------------------------------------------------------- /vivado/test2/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | !/src/ip/**/*.xci 4 | !.gitignore 5 | !*.xpr 6 | -------------------------------------------------------------------------------- /vivado/test3/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | !/src/ip/**/*.xci 4 | !.gitignore 5 | !*.xpr 6 | -------------------------------------------------------------------------------- /vsrc/add_sources.tcl: -------------------------------------------------------------------------------- 1 | set COMMON_ROOT [file dirname [info script]] 2 | add_files $COMMON_ROOT/ 3 | -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/Makefile.deps.mk: -------------------------------------------------------------------------------- 1 | $(CXX_BUILD)/$(CXX_ROOT)/tests.o: $(VSOURCE)/VCacheTop/tests.inl 2 | -------------------------------------------------------------------------------- /riscv64-nemu-interpreter-so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/riscv64-nemu-interpreter-so -------------------------------------------------------------------------------- /ready-to-run/lab1/lab1-test.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/ready-to-run/lab1/lab1-test.bin -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "difftest"] 2 | path = difftest 3 | url = https://github.com/FDUCSLG/difftest.git 4 | branch = main 5 | -------------------------------------------------------------------------------- /ready-to-run/lab1/lab1-test-a.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/ready-to-run/lab1/lab1-test-a.bin -------------------------------------------------------------------------------- /ready-to-run/lab4/all-test-priv.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/ready-to-run/lab4/all-test-priv.bin -------------------------------------------------------------------------------- /ready-to-run/lab2/all-test-rv64i.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/ready-to-run/lab2/all-test-rv64i.bin -------------------------------------------------------------------------------- /ready-to-run/lab3/all-test-rv64im.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/ready-to-run/lab3/all-test-rv64im.bin -------------------------------------------------------------------------------- /ready-to-run/lab4/all-test-privfull.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/ready-to-run/lab4/all-test-privfull.bin -------------------------------------------------------------------------------- /vivado/src/add_sources.tcl: -------------------------------------------------------------------------------- 1 | set COMMON_ROOT [file dirname [info script]] 2 | add_files -fileset constrs_1 $COMMON_ROOT/Basys-3-Master.xdc 3 | -------------------------------------------------------------------------------- /ready-to-run/challenge/microbench-riscv64-nutshell.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FDUCSLG/Arch-2022Spring-FDU/HEAD/ready-to-run/challenge/microbench-riscv64-nutshell.bin -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bus.h" 4 | 5 | #include "VModel__Syms.h" 6 | 7 | using VScope = VModel___024unit; 8 | using VModelScope = VModel_VCacheTop; 9 | 10 | using CBusWrapper = CBusWrapperGen; 11 | -------------------------------------------------------------------------------- /verilate/vsrc/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "verilated.h" 4 | 5 | extern int vmain(int argc, char *argv[]); 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | Verilated::commandArgs(argc, argv); 10 | Verilated::traceEverOn(true); 11 | 12 | return vmain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /vivado/src/with_delay/basys3_top.sv: -------------------------------------------------------------------------------- 1 | module basys3_top ( 2 | input logic clk, btnC, 3 | input logic [3:0] sw, 4 | output logic [3:0] led, 5 | output logic RsTx 6 | ); 7 | soc_top soc_top_inst ( 8 | .clk, 9 | .reset(btnC), 10 | .sw(sw), 11 | .led(led), 12 | .tx(RsTx) 13 | ); 14 | endmodule -------------------------------------------------------------------------------- /vivado/src/with_delay/simtop.sv: -------------------------------------------------------------------------------- 1 | module simtop ( 2 | 3 | ); 4 | logic clk; 5 | initial clk = '0; 6 | always #5 clk = ~clk; 7 | 8 | logic reset; 9 | initial begin 10 | reset = 1'b1; 11 | #100 reset = 1'b0; 12 | end 13 | soc_top #(1'b1) soc_top ( 14 | .clk, .reset(reset), 15 | .sw(0) 16 | ); 17 | endmodule -------------------------------------------------------------------------------- /vivado/src/without_delay/basys3_top_nodelay.sv: -------------------------------------------------------------------------------- 1 | module basys3_top_nodelay ( 2 | input logic clk, btnC, 3 | input logic [3:0] sw, 4 | output logic [3:0] led, 5 | output logic RsTx 6 | ); 7 | soc_top_nodelay soc_top_inst ( 8 | .clk, 9 | .reset(btnC), 10 | .sw(sw), 11 | .led(led), 12 | .tx(RsTx) 13 | ); 14 | endmodule -------------------------------------------------------------------------------- /vivado/src/without_delay/simtop_nodelay.sv: -------------------------------------------------------------------------------- 1 | module simtop_nodelay ( 2 | 3 | ); 4 | logic clk; 5 | initial clk = '0; 6 | always #5 clk = ~clk; 7 | 8 | logic reset; 9 | initial begin 10 | reset = 1'b1; 11 | #100 reset = 1'b0; 12 | end 13 | soc_top_nodelay #(1'b1) soc_top ( 14 | .clk, .reset(reset), 15 | .sw(0) 16 | ); 17 | endmodule -------------------------------------------------------------------------------- /vsrc/include/pipes.sv: -------------------------------------------------------------------------------- 1 | `ifndef __PIPES_SV 2 | `define __PIPES_SV 3 | 4 | package pipes; 5 | /* Define instrucion decoding rules here */ 6 | 7 | // parameter F7_RI = 7'bxxxxxxx; 8 | 9 | 10 | /* Define pipeline structures here */ 11 | 12 | typedef enum logic [4:0] { 13 | ALU_ADD 14 | } alufunc_t; 15 | 16 | endpackage 17 | 18 | `endif 19 | -------------------------------------------------------------------------------- /vivado/src/device.svh: -------------------------------------------------------------------------------- 1 | `ifndef __DEVICE_SVH 2 | `define __DEVICE_SVH 3 | 4 | parameter logic [63:0] FINISH_ADDR = 64'h23333000; 5 | parameter logic [63:0] SW_ADDR = 64'h23333008; 6 | 7 | parameter logic [63:0] COUNTER_1 = 64'h3800bff8; 8 | parameter logic [63:0] COUNTER_2 = 64'h20003000; 9 | 10 | parameter logic [63:0] TX_READY = 64'h40600008; 11 | parameter logic [63:0] TX_DATA = 64'h40600004; 12 | 13 | 14 | `endif 15 | -------------------------------------------------------------------------------- /vsrc/include/config.sv: -------------------------------------------------------------------------------- 1 | `ifndef CONFIG_SV 2 | `define CONFIG_SV 3 | 4 | package config_pkg; 5 | // parameters 6 | parameter AREG_READ_PORTS = 1; 7 | parameter AREG_WRITE_PORTS = 1; 8 | parameter USE_CACHE = 1'b1; 9 | parameter USE_ICACHE = USE_CACHE; 10 | parameter USE_DCACHE = USE_CACHE; 11 | parameter ADD_LATENCY = 1'b1; 12 | parameter AXI_BURST_NUM = 16; 13 | parameter ICACHE_BITS = 3; 14 | parameter DCACHE_BITS = 3; 15 | endpackage 16 | 17 | `endif 18 | -------------------------------------------------------------------------------- /vsrc/pipeline/execute/alu.sv: -------------------------------------------------------------------------------- 1 | `ifndef __ALU_SV 2 | `define __ALU_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | `include "include/pipes.sv" 7 | `else 8 | 9 | `endif 10 | 11 | module alu 12 | import common::*; 13 | import pipes::*;( 14 | input u64 a, b, 15 | input alufunc_t alufunc, 16 | output u64 c 17 | ); 18 | always_comb begin 19 | c = '0; 20 | unique case(alufunc) 21 | ALU_ADD: c = a + b; 22 | default: begin 23 | 24 | end 25 | endcase 26 | end 27 | 28 | endmodule 29 | 30 | `endif 31 | -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/vmain.cpp: -------------------------------------------------------------------------------- 1 | #include "runner.h" 2 | #include "testbench.h" 3 | 4 | #include "mycache.h" 5 | 6 | ProgramRunner app; 7 | 8 | void on_error(int) 9 | { 10 | abort(); 11 | } 12 | 13 | void on_abort(int) 14 | { 15 | abort_testbench(); 16 | app.~ProgramRunner(); 17 | } 18 | 19 | int vmain(int argc, char *argv[]) 20 | { 21 | hook_signal(SIGABRT, on_abort); 22 | hook_signal(SIGINT, on_error); 23 | 24 | app.no_init_memory(); 25 | app.no_init_text_trace(); 26 | app.no_init_fst_trace(); 27 | app.no_soc(); 28 | app.with_fst_folder(); 29 | app.with_workers(); 30 | return app.main(argc, argv); 31 | } 32 | -------------------------------------------------------------------------------- /Makefile.include: -------------------------------------------------------------------------------- 1 | # Set the frequency of tested module on board. 2 | DUT_FREQ ?= 100 3 | 4 | BOARD ?= sim 5 | 6 | CORE ?= inorder 7 | 8 | DATAWIDTH ?= 64 9 | 10 | SIMTOP ?= top.TopMain 11 | 12 | TOP ?= TopMain 13 | 14 | FPGATOP ?= NutShellFPGATop 15 | 16 | SIM_TOP ?= SimTop 17 | 18 | SIM_TOP_V ?= $(BUILD_DIR)/$(SIM_TOP).v 19 | 20 | ifeq (BOARD, "sim") 21 | SCALA_OPTS ?= $(SIMTOP) -td $(@D) --output-file $(@F) BOARD=sim CORE=$(CORE) 22 | else 23 | SCALA_OPTS ?= $(SIMTOP) -td $(@D) --output-file $(@F) --infer-rw $(FPGATOP) --repl-seq-mem -c:$(FPGATOP):-o:$(@D)/$(@F).conf BOARD=$(BOARD) CORE=$(CORE) 24 | endif 25 | 26 | DIFFTEST_OPTS ?= 27 | -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/tests.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "testbench.h" 3 | #include "cell.h" 4 | 5 | #include "mycache.h" 6 | 7 | namespace _testbench { 8 | 9 | MyCache *top; 10 | VModelScope *scope; 11 | DBus *dbus; 12 | CacheRefModel *ref; 13 | 14 | PRETEST_HOOK[]{ 15 | // reset before every test. 16 | top->reset(); 17 | }; 18 | 19 | /** 20 | * TODO (Lab3, optional) write your own simple tests :) 21 | */ 22 | // WITH { 23 | // ASSERT(true); 24 | // } AS("my test 1"); 25 | 26 | #define ICS_INLINE_TESTS 27 | #include "tests.inl" 28 | 29 | /** 30 | * TODO (Lab3, optional) write your own algorithm tests :) 31 | */ 32 | 33 | } 34 | -------------------------------------------------------------------------------- /vsrc/util/IBusToCBus.sv: -------------------------------------------------------------------------------- 1 | `ifndef __IBUSTOCBUS_SV 2 | `define __IBUSTOCBUS_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | `else 7 | 8 | `endif 9 | 10 | module IBusToCBus 11 | import common::*;( 12 | input ibus_req_t ireq, 13 | output ibus_resp_t iresp, 14 | output cbus_req_t icreq, 15 | input cbus_resp_t icresp 16 | ); 17 | // since IBus is a subset of DBus, we can reuse DBusToCBus. 18 | dbus_resp_t dresp; 19 | DBusToCBus inst( 20 | .dreq(`IREQ_TO_DREQ(ireq)), 21 | .dresp(dresp), 22 | .dcreq(icreq), 23 | .dcresp(icresp) 24 | ); 25 | assign iresp = `DRESP_TO_IRESP(dresp, ireq); 26 | endmodule 27 | 28 | 29 | 30 | `endif -------------------------------------------------------------------------------- /vsrc/cache/ICache.sv: -------------------------------------------------------------------------------- 1 | `ifndef __ICACHE_SV 2 | `define __ICACHE_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | /* You should not add any additional includes in this file */ 7 | `endif 8 | 9 | module ICache 10 | import common::*; ( 11 | input logic clk, reset, 12 | input ibus_req_t ireq, 13 | output ibus_resp_t iresp, 14 | output cbus_req_t creq, 15 | input cbus_resp_t cresp 16 | ); 17 | 18 | `ifndef REFERENCE_CACHE 19 | 20 | 21 | 22 | `else 23 | 24 | dbus_resp_t dresp; 25 | DCache lazy ( 26 | .clk, .reset, 27 | .dreq(`IREQ_TO_DREQ(ireq)), 28 | .dresp, 29 | .creq, 30 | .cresp 31 | ); 32 | assign iresp = `DRESP_TO_IRESP(dresp, ireq); 33 | `endif 34 | 35 | endmodule 36 | 37 | `endif 38 | -------------------------------------------------------------------------------- /verilate/include/diff.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #include 6 | #include 7 | 8 | class TextDiff { 9 | public: 10 | auto is_open() const -> bool; 11 | void open(const std::string &path); 12 | void close(); 13 | 14 | auto check_line( 15 | const std::string &line, 16 | bool report = true, 17 | bool abort_on_error = true 18 | ) -> bool; 19 | auto check_eof( 20 | bool report = true, 21 | bool abort_on_error = true 22 | ) -> bool; 23 | 24 | auto current_progress() const -> int; 25 | auto current_line() const->size_t; 26 | auto get_error_count() const->size_t; 27 | 28 | private: 29 | size_t line_number; 30 | size_t file_size, byte_read; 31 | size_t error_count; 32 | std::ifstream fs; 33 | 34 | auto get_line()->std::string; 35 | }; 36 | -------------------------------------------------------------------------------- /vsrc/util/CBusMultiplexer.sv: -------------------------------------------------------------------------------- 1 | `ifdef VERILATOR 2 | `include "include/common.sv" 3 | `endif 4 | 5 | module CBusMultiplexer 6 | import common::*;#( 7 | parameter int NUM_INPUTS = 2, // NOTE: NUM_INPUTS >= 1 8 | 9 | localparam int MAX_INDEX = NUM_INPUTS - 1 10 | ) ( 11 | input cbus_req_t [MAX_INDEX:0] ireqs, 12 | output cbus_resp_t [MAX_INDEX:0] iresps, 13 | output cbus_req_t oreq, 14 | input cbus_resp_t oresp 15 | ); 16 | always_comb begin 17 | oreq = '0; 18 | iresps = '0; 19 | 20 | for (int i = 0; i < NUM_INPUTS; i++) begin 21 | if (ireqs[i].valid) begin 22 | oreq = ireqs[i]; 23 | iresps[i] = oresp; 24 | break; 25 | end 26 | end 27 | end 28 | endmodule 29 | -------------------------------------------------------------------------------- /verilate/include/reference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | // "I" stands for interface, not "ICache" 6 | class ICacheRefModel { 7 | public: 8 | virtual ~ICacheRefModel() = default; 9 | 10 | virtual void reset() = 0; 11 | 12 | // load/store is identical to those in DBus. 13 | 14 | virtual auto load(addr_t addr, AXISize size)->word_t = 0; 15 | virtual void store(addr_t addr, AXISize size, word_t strobe, word_t data) = 0; 16 | 17 | // check_* should use assertions. 18 | 19 | // check cache's internal states, memory, etc. 20 | virtual void check_internal() = 0; 21 | 22 | // check memory contents of the reference model and VModel 23 | // NOTE: CBusDevice provides the dump function, which can be used to 24 | // dump the memory content of VModel. 25 | virtual void check_memory() = 0; 26 | }; 27 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Architecture 2022Spring Fudan 2 | 3 | 课程网站:https://fducslg.github.io/Arch-2022Spring-FDU/ 4 | 5 | ### 项目结构 6 | Arch-2022Sping-FDU 7 | │── build:仿真测试时才会生成的目录 8 | │── difftest:仿真测试框架 9 | │── ready-to-run:仿真测试文件目录 10 | │  ├── lab1:包含lab1相关的测试文件,需要关注其中的 .S 汇编文件 11 | │  └── ... 12 | │── vivado 13 | │  └── test1 14 | │     └── project:vivado项目工程目录 15 | │── vsrc:需要写的CPU代码所在目录 16 | │  ├── include:头文件目录 17 | │  ├── pipeline 18 | │     ├── regfile:寄存器文件目录,寄存器组模块已给出 19 | │     ├── execute:流水线执行阶段目录,alu模块已给出 20 | │     └── core.sv:五级流水线主体代码 21 | │  ├── ram:内存控制相关目录 22 | │  ├── util:访存接口相关目录 23 | │  ├── add_sources.tcl 24 | │  ├── mycpu_top_nodelay.sv:以下是项目头文件 25 | │  ├── mycpu_top.sv 26 | │  ├── SimTop.sv 27 | │  └── VTop.sv 28 | │── xpm_memory:Xilinx的内存IP 29 | │── Makefile:仿真测试的命令汇总 30 | │── readme.md: 此文件 31 | -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/cache_ref.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #include "memory.h" 5 | #include "reference.h" 6 | 7 | class MyCache; 8 | 9 | class CacheRefModel final : public ICacheRefModel { 10 | public: 11 | CacheRefModel(MyCache *_top, size_t memory_size); 12 | 13 | void reset(); 14 | auto load(addr_t addr, AXISize size)->word_t; 15 | void store(addr_t addr, AXISize size, word_t strobe, word_t data); 16 | void check_internal(); 17 | void check_memory(); 18 | 19 | private: 20 | MyCache *top; 21 | VModelScope *scope; 22 | #ifdef REFERENCE_CACHE 23 | word_t buffer[16]; 24 | #else 25 | /** 26 | * TODO (Lab3) declare reference model's memory and internal states :) 27 | * 28 | * NOTE: you can use BlockMemory, or replace it with anything you like. 29 | */ 30 | #endif 31 | 32 | // int state; 33 | BlockMemory mem; 34 | }; 35 | -------------------------------------------------------------------------------- /vivado/src/without_delay/dist_ram_wrapper.sv: -------------------------------------------------------------------------------- 1 | module dist_ram_wrapper ( 2 | input logic clk, reset, 3 | 4 | input logic [63:0] inst_addr, 5 | output logic [31:0] inst_data, 6 | 7 | input logic [63:0] data_addr, 8 | input logic wvalid, 9 | input logic [63:0] data_wdata, 10 | output logic [63:0] data_rdata 11 | ); 12 | 13 | logic [31:0] inst_ram [4095:0]; 14 | initial begin 15 | $readmemh("lab1-test.mem", inst_ram); 16 | end 17 | assign inst_data = inst_ram[inst_addr[20:2]]; 18 | 19 | logic [63:0] ram [255:0]; 20 | for (genvar i = 0; i < 256; i++) begin 21 | always_ff @(posedge clk) begin 22 | if (reset) ram[i] <= 64'hdeadbeefdeadbeef; 23 | else if (wvalid && data_addr[7:0] == i && data_addr[31]) begin 24 | ram[i] <= data_wdata; 25 | end 26 | end 27 | 28 | end 29 | assign data_rdata = ram[data_addr[7:0]]; 30 | 31 | 32 | endmodule 33 | -------------------------------------------------------------------------------- /vsrc/mycpu_top.sv: -------------------------------------------------------------------------------- 1 | `ifndef __MYCPU_TOP_SV 2 | `define __MYCPU_TOP_SV 3 | 4 | module mycpu_top 5 | import common::*;( 6 | input logic clk, reset, 7 | 8 | output logic valid, 9 | output logic [63:0] addr, 10 | output logic [63:0] wdata, 11 | input logic [63:0] rdata, 12 | output logic [7:0] wstrobe, 13 | output logic [1:0] burst, 14 | output logic [7:0] len, 15 | output logic [2:0] size, 16 | 17 | input logic ready, 18 | input logic last 19 | ); 20 | 21 | cbus_req_t oreq; 22 | cbus_resp_t oresp; 23 | VTop VTop_inst ( 24 | .clk, .reset, .oreq, .oresp 25 | ); 26 | 27 | assign valid = oreq.valid; 28 | assign addr = oreq.addr; 29 | assign wdata = oreq.data; 30 | assign oresp.data = rdata; 31 | assign wstrobe = oreq.strobe & {8{oreq.is_write}}; 32 | assign burst = oreq.burst; 33 | assign len = oreq.len; 34 | assign oresp.ready = ready; 35 | assign oresp.last = last; 36 | assign size = oreq.size; 37 | 38 | endmodule 39 | 40 | 41 | `endif 42 | -------------------------------------------------------------------------------- /vsrc/mycpu_top_nodelay.sv: -------------------------------------------------------------------------------- 1 | `ifndef __MYCPU_TOP_NODELAY_SV 2 | `define __MYCPU_TOP_NODELAY_SV 3 | 4 | module mycpu_top_nodelay 5 | import common::*;( 6 | input logic clk, reset, 7 | 8 | output logic [63:0] inst_addr, 9 | input logic [31:0] inst_data, 10 | 11 | output logic [63:0] data_addr, 12 | output logic wvalid, 13 | output logic [63:0] data_wdata, 14 | input logic [63:0] data_rdata 15 | ); 16 | 17 | ibus_req_t ireq; 18 | dbus_req_t dreq; 19 | ibus_resp_t iresp; 20 | dbus_resp_t dresp; 21 | core core_inst ( 22 | .clk, .reset, .ireq, .iresp, .dreq, .dresp 23 | ); 24 | 25 | assign inst_addr = ireq.addr; 26 | assign iresp.data = inst_data; 27 | assign iresp.addr_ok = '1; 28 | assign iresp.data_ok = '1; 29 | 30 | assign data_addr = dreq.addr; 31 | assign wvalid = |dreq.strobe; 32 | assign data_wdata = dreq.data; 33 | assign dresp.data = data_rdata; 34 | assign dresp.data_ok = '1; 35 | assign dresp.addr_ok = '1; 36 | 37 | endmodule 38 | 39 | `endif 40 | -------------------------------------------------------------------------------- /vsrc/util/DBusToCBus.sv: -------------------------------------------------------------------------------- 1 | `ifndef __DBUSTOCBUS_SV 2 | `define __DBUSTOCBUS_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | `else 7 | 8 | `endif 9 | /** 10 | * NOTE: CBus does not support byte write enable mask (write_en). 11 | */ 12 | 13 | module DBusToCBus 14 | import common::*;( 15 | input dbus_req_t dreq, 16 | output dbus_resp_t dresp, 17 | output cbus_req_t dcreq, 18 | input cbus_resp_t dcresp 19 | ); 20 | assign dcreq.valid = dreq.valid; 21 | assign dcreq.is_write = |dreq.strobe; 22 | assign dcreq.size = dreq.size; 23 | assign dcreq.addr = dreq.addr; 24 | assign dcreq.strobe = dreq.strobe; 25 | assign dcreq.data = dreq.data; 26 | assign dcreq.len = MLEN1; 27 | assign dcreq.burst = AXI_BURST_FIXED; 28 | 29 | logic okay; 30 | assign okay = dcresp.ready && dcresp.last; 31 | 32 | assign dresp.addr_ok = okay; 33 | assign dresp.data_ok = okay; 34 | assign dresp.data = dcresp.data; 35 | endmodule 36 | 37 | 38 | 39 | `endif -------------------------------------------------------------------------------- /vsrc/include/access.svh: -------------------------------------------------------------------------------- 1 | /** 2 | * some helper macros for VTop. 3 | * 4 | * see 5 | */ 6 | 7 | `ifndef __ACCESS_SVH__ 8 | `define __ACCESS_SVH__ 9 | 10 | `define STRUCT_ACCESSOR(struct_name, member_name, return_type) \ 11 | /* verilator lint_off VARHIDDEN */ \ 12 | function return_type struct_name``_``member_name(struct_name e); \ 13 | /* verilator public */ \ 14 | typedef return_type _return_type; \ 15 | automatic logic _unused_ok = &{1'b0, e, 1'b0}; \ 16 | return _return_type'(e.member_name); \ 17 | endfunction 18 | /* verilator lint_on VARHIDDEN */ 19 | 20 | `define STRUCT_TASK_ACCESSOR(struct_name, member_name, return_type) \ 21 | /* verilator lint_off VARHIDDEN */ \ 22 | task struct_name``_``member_name(input struct_name e, output return_type o); \ 23 | /* verilator public */ \ 24 | typedef return_type _return_type; \ 25 | automatic logic _unused_ok = &{1'b0, e, 1'b0}; \ 26 | assign o = _return_type'(e.r); \ 27 | endtask 28 | /* verilator lint_on VARHIDDEN */ 29 | 30 | `endif 31 | -------------------------------------------------------------------------------- /vsrc/VTop.sv: -------------------------------------------------------------------------------- 1 | `ifndef __VTOP_SV 2 | `define __VTOP_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | `include "pipeline/core.sv" 7 | `include "util/IBusToCBus.sv" 8 | `include "util/DBusToCBus.sv" 9 | `include "util/CBusArbiter.sv" 10 | 11 | `include "cache/ICache.sv" 12 | `include "cache/DCache.sv" 13 | `endif 14 | module VTop 15 | import common::*;( 16 | input logic clk, reset, 17 | 18 | output cbus_req_t oreq, 19 | input cbus_resp_t oresp, 20 | 21 | input logic trint, swint, exint 22 | ); 23 | 24 | ibus_req_t ireq; 25 | ibus_resp_t iresp; 26 | dbus_req_t dreq; 27 | dbus_resp_t dresp; 28 | cbus_req_t icreq, dcreq; 29 | cbus_resp_t icresp, dcresp; 30 | 31 | core core(.*); 32 | if (USE_ICACHE == 0) 33 | IBusToCBus icvt(.*); 34 | else 35 | ICache ICache(.creq(icreq), .cresp(icresp), .*); 36 | if (USE_DCACHE == 0) 37 | DBusToCBus dcvt(.*); 38 | else 39 | DCache DCache(.creq(dcreq), .cresp(dcresp), .*); 40 | 41 | /** 42 | * TODO (Lab2) replace mux with your own arbiter :) 43 | */ 44 | CBusArbiter mux( 45 | .ireqs({icreq, dcreq}), 46 | .iresps({icresp, dcresp}), 47 | .* 48 | ); 49 | 50 | endmodule 51 | 52 | 53 | 54 | `endif -------------------------------------------------------------------------------- /vsrc/pipeline/regfile/regfile.sv: -------------------------------------------------------------------------------- 1 | `ifndef __REGFILE_SV 2 | `define __REGFILE_SV 3 | `ifdef VERILATOR 4 | `include "include/common.sv" 5 | `else 6 | 7 | `endif 8 | module regfile 9 | import common::*; 10 | #( 11 | parameter READ_PORTS = AREG_READ_PORTS, 12 | parameter WRITE_PORTS = AREG_WRITE_PORTS 13 | ) ( 14 | input logic clk, reset, 15 | input creg_addr_t [READ_PORTS-1:0] ra1, ra2, 16 | output u64 [READ_PORTS-1:0] rd1, rd2, 17 | 18 | input creg_addr_t [WRITE_PORTS-1:0] wa, 19 | input u1 [WRITE_PORTS-1:0] wvalid, 20 | input u64 [WRITE_PORTS-1:0] wd 21 | ); 22 | u64 [31:0] regs, regs_nxt; 23 | 24 | for (genvar i = 0; i < READ_PORTS; i++) begin 25 | assign rd1[i] = regs[ra1[i]]; 26 | assign rd2[i] = regs[ra2[i]]; 27 | end 28 | 29 | always_ff @(posedge clk) begin 30 | if (reset) begin 31 | regs <= '0; 32 | end else begin 33 | regs <= regs_nxt; 34 | regs[0] <= '0; 35 | end 36 | end 37 | 38 | for (genvar i = 1; i < 32; i++) begin 39 | always_comb begin 40 | regs_nxt[i] = regs[i]; 41 | for (int j = 0; j < WRITE_PORTS; j++) begin 42 | if (i == wa[j] && wvalid[j]) begin 43 | regs_nxt[i] = wd[j]; 44 | end 45 | end 46 | end 47 | end 48 | 49 | 50 | 51 | endmodule 52 | 53 | 54 | 55 | `endif -------------------------------------------------------------------------------- /vsrc/SimTop.sv: -------------------------------------------------------------------------------- 1 | `ifdef VERILATOR 2 | `include "include/common.sv" 3 | `include "pipeline/core.sv" 4 | 5 | `define USE_VTOP 6 | 7 | module SimTop import common::*;( 8 | input clock, 9 | input reset, 10 | input [63:0] io_logCtrl_log_begin, 11 | input [63:0] io_logCtrl_log_end, 12 | input [63:0] io_logCtrl_log_level, 13 | input io_perfInfo_clean, 14 | input io_perfInfo_dump, 15 | output io_uart_out_valid, 16 | output [7:0] io_uart_out_ch, 17 | output io_uart_in_valid, 18 | input [7:0] io_uart_in_ch 19 | ); 20 | 21 | `ifdef USE_VTOP 22 | cbus_req_t oreq; 23 | cbus_resp_t oresp; 24 | logic trint, swint, exint; 25 | VTop top( 26 | .clk(clock), .reset, .oreq, .oresp, .trint, .swint, .exint 27 | ); 28 | RAMHelper2 ram( 29 | .clk(clock), .reset, .oreq, .oresp, .trint, .swint, .exint 30 | ); 31 | `else 32 | ibus_req_t ireq; 33 | ibus_resp_t iresp; 34 | dbus_req_t dreq; 35 | dbus_resp_t dresp; 36 | core core( 37 | .clk(clock), .reset, .ireq, .iresp, .dreq, .dresp 38 | ); 39 | RAMHelper1 ram( 40 | .clk(clock), .reset, .ireq, .iresp, .dreq, .dresp 41 | ); 42 | `endif 43 | 44 | assign {io_uart_out_valid, io_uart_out_ch, io_uart_in_valid} = '0; 45 | 46 | endmodule 47 | `endif -------------------------------------------------------------------------------- /vivado/src/without_delay/soc_top_nodelay.sv: -------------------------------------------------------------------------------- 1 | module soc_top_nodelay #( 2 | parameter logic SIMULATION = 1'b0 3 | )( 4 | input logic clk, reset, 5 | 6 | output logic [3:0] led, 7 | input logic [3:0] sw, 8 | output logic tx 9 | ); 10 | logic [63:0] inst_addr; 11 | logic [31:0] inst_data; 12 | 13 | logic [63:0] data_addr; 14 | logic wvalid; 15 | logic [63:0] data_wdata; 16 | logic [63:0] data_rdata; 17 | 18 | logic [63:0] ram_rdata; 19 | 20 | logic device_valid; 21 | logic [63:0] device_addr; 22 | logic [63:0] device_wdata; 23 | logic device_wvalid; 24 | logic [63:0] device_rdata; 25 | logic device_ready; 26 | logic device_last; 27 | 28 | logic cpu_clk; 29 | 30 | /* mycpu */ 31 | mycpu_top_nodelay mycpu_top_nodelay(.clk(cpu_clk), .*); 32 | 33 | /* RAM */ 34 | dist_ram_wrapper dist_ram_wrapper_inst( 35 | .clk(cpu_clk), .reset, .data_rdata(ram_rdata), 36 | .* 37 | ); 38 | 39 | /* Device */ 40 | device #(SIMULATION) device_inst ( 41 | .clk, .reset, 42 | .valid(~data_addr[31]), 43 | .addr(data_addr), 44 | .wdata(data_wdata), 45 | .rdata(device_rdata), 46 | .wvalid(wvalid), 47 | .tx, .led(led) 48 | ); 49 | 50 | assign data_rdata = data_addr[31] ? ram_rdata : device_rdata; 51 | 52 | if (SIMULATION) 53 | assign cpu_clk = clk; 54 | else 55 | clk_wiz_0 clk_wiz_0(.sys_clk(clk), .cpu_clk(cpu_clk)); 56 | 57 | 58 | endmodule 59 | -------------------------------------------------------------------------------- /vsrc/ram/template.sv: -------------------------------------------------------------------------------- 1 | `ifdef XXXXX_DFSD 2 | 3 | LUTRAM_DualPort #( 4 | .ADDR_WIDTH(), 5 | .DATA_WIDTH(), 6 | .BYTE_WIDTH(), 7 | .MEM_TYPE(0), 8 | .READ_LATENCY(0) 9 | ) valid_ram ( 10 | .clk, .en_1(1'b1), .en_2(1'b1), 11 | .addr_1(), 12 | .addr_2(), 13 | .strobe(), 14 | .wdata(), 15 | .rdata_1(), 16 | .rdata2() 17 | ); 18 | 19 | RAM_TrueDualPort #( 20 | .ADDR_WIDTH(), 21 | .DATA_WIDTH(), 22 | .BYTE_WIDTH(), 23 | .MEM_TYPE(), 24 | .READ_LATENCY() 25 | ) RAM_TrueDualPort_inst ( 26 | .clk, .en_1(1'b1), .en_2(1'b1), 27 | .addr_1(), 28 | .addr_2(), 29 | .strobe_1(), 30 | .strobe_2(), 31 | .wdata_1(), 32 | .wdata_2(), 33 | .rdata_1(), 34 | .rdata_2() 35 | ); 36 | 37 | RAM_SinglePort #( 38 | .ADDR_WIDTH(), 39 | .DATA_WIDTH(), 40 | .BYTE_WIDTH(), 41 | .MEM_TYPE(), 42 | .READ_LATENCY() 43 | ) data_ram ( 44 | .clk, .en(1'b1), 45 | .addr(), 46 | .strobe(), 47 | .wdata(), 48 | .rdata() 49 | ); 50 | 51 | RAM_SimpleDualPort #( 52 | .ADDR_WIDTH(), 53 | .DATA_WIDTH(), 54 | .BYTE_WIDTH(), 55 | .MEM_TYPE(), 56 | .READ_LATENCY() 57 | ) wb_buffer ( 58 | .clk, .en(1'b1), 59 | .raddr(), 60 | .waddr(), 61 | .strobe(), 62 | .wdata(), 63 | .rdata() 64 | ); 65 | 66 | LUTRAM_DualPort #( 67 | .ADDR_WIDTH(), 68 | .DATA_WIDTH(), 69 | .BYTE_WIDTH(), 70 | .READ_LATENCY() 71 | )( 72 | .clk(), 73 | .en_1(), 74 | .en_2(), 75 | 76 | .addr_1(), 77 | .addr_2(), 78 | .strobe(), 79 | .wdata(), 80 | .rdata_1(), 81 | .rdata_2() 82 | ); 83 | 84 | `endif -------------------------------------------------------------------------------- /vivado/src/with_delay/cbus_crossbar.sv: -------------------------------------------------------------------------------- 1 | module cbus_crossbar ( 2 | input logic clk, reset, 3 | 4 | /* From CPU */ 5 | input logic valid, 6 | input logic [63:0] addr, 7 | input logic [63:0] wdata, 8 | input logic [1:0] burst, 9 | input logic [7:0] len, 10 | input logic [7:0] wstrobe, 11 | output logic [63:0] rdata, 12 | output logic ready, 13 | output logic last, 14 | 15 | /* To RAM */ 16 | output logic ram_valid, 17 | output logic [63:0] ram_addr, 18 | output logic [63:0] ram_wdata, 19 | output logic [1:0] ram_burst, 20 | output logic [7:0] ram_len, 21 | output logic [7:0] ram_wstrobe, 22 | input logic [63:0] ram_rdata, 23 | input logic ram_ready, 24 | input logic ram_last, 25 | 26 | /* To Device */ 27 | output logic device_valid, 28 | output logic [63:0] device_addr, 29 | output logic [63:0] device_wdata, 30 | output logic device_wvalid, 31 | input logic [63:0] device_rdata, 32 | input logic device_ready, 33 | input logic device_last 34 | ); 35 | assign rdata = addr[31] ? ram_rdata : device_rdata; 36 | assign ready = addr[31] ? ram_ready : device_ready; 37 | assign last = addr[31] ? ram_last : device_last; 38 | 39 | assign ram_valid = addr[31] && valid; 40 | assign ram_addr = addr; 41 | assign ram_wdata = wdata; 42 | assign ram_burst = burst; 43 | assign ram_len = len; 44 | assign ram_wstrobe = wstrobe; 45 | 46 | assign device_valid = ~addr[31] && valid; 47 | assign device_addr = addr; 48 | assign device_wdata = wdata; 49 | assign device_wvalid = |wstrobe; 50 | 51 | endmodule -------------------------------------------------------------------------------- /verilate/include/axi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #include 6 | 7 | struct AXITransaction { 8 | bool busy = false; 9 | bool is_write; 10 | 11 | // see "AMBA AXI and ACE Protocol Specification", 12 | // section A3.4.2 "Pseudocode description of the transfers". 13 | // we keep the original naming convention for clarity. 14 | bool aligned; 15 | word_t N; 16 | addr_t addr; 17 | 18 | // the following are intended to be constants during transaction. 19 | addr_t Start_Address; 20 | word_t Number_Bytes; 21 | word_t Burst_Length; 22 | addr_t Aligned_Address; 23 | addr_t Lower_Wrap_Boundry; 24 | addr_t Upper_Wrap_Boundry; 25 | 26 | void reset() 27 | { 28 | memset(this, 0, sizeof(*this)); 29 | } 30 | 31 | // start new transaction. 32 | void init(addr_t AxADDR, word_t AxSIZE, word_t AxLEN) 33 | { 34 | Start_Address = AxADDR; 35 | Number_Bytes = 1u << AxSIZE; // 2^AxSIZE 36 | Burst_Length = AxLEN + 1; 37 | 38 | // dtsize: the number of bytes will be transferred during the transaction. 39 | auto dtsize = Number_Bytes * Burst_Length; 40 | 41 | Aligned_Address = (Start_Address / Number_Bytes) * Number_Bytes; 42 | Lower_Wrap_Boundry = (Start_Address / dtsize) * dtsize; 43 | Upper_Wrap_Boundry = Lower_Wrap_Boundry + dtsize; 44 | 45 | aligned = false; 46 | N = 1; 47 | addr = Start_Address; 48 | } 49 | 50 | // handshake at clock's positive edge. 51 | void sync() 52 | { 53 | N++; 54 | 55 | if (aligned) { 56 | addr = addr + Number_Bytes; 57 | } else { 58 | addr = Aligned_Address + Number_Bytes; 59 | aligned = true; 60 | } 61 | 62 | if (addr >= Upper_Wrap_Boundry) 63 | addr = Lower_Wrap_Boundry; 64 | } 65 | 66 | auto last() const -> bool 67 | { 68 | return N == Burst_Length; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/mycache.cpp: -------------------------------------------------------------------------------- 1 | #include "testbench.h" 2 | 3 | #include "defs.h" 4 | #include "mycache.h" 5 | 6 | MyCache::MyCache() 7 | : ref(this, MEMORY_SIZE) 8 | { 9 | } 10 | 11 | void MyCache::reset() 12 | { 13 | clk = 0; 14 | reset_ = 1; 15 | memset(cresp, 0, sizeof(cresp)); 16 | // cresp = 0; 17 | memset(dreq, 0, sizeof(dreq)); 18 | 19 | for (int i = 0; i < 100000; i++) { 20 | _tick(); 21 | } 22 | 23 | dev->reset(); 24 | clk = 0; 25 | reset_ = 0; 26 | eval(); 27 | } 28 | 29 | void MyCache::tick() 30 | { 31 | _tick(); 32 | } 33 | 34 | void MyCache::enable_statistics(bool enable) 35 | { 36 | stat.enabled = enable; 37 | } 38 | 39 | void MyCache::reset_statistics() 40 | { 41 | /** 42 | * TODO (Lab3, optional) reset statistics information :) 43 | */ 44 | 45 | // memset(stat.count, 0, sizeof(stat.count)); 46 | } 47 | 48 | void MyCache::update_statistics() 49 | { 50 | /** 51 | * TODO (Lab3, optional) track statistics information here :) 52 | */ 53 | 54 | // stat.count[0]++; 55 | } 56 | 57 | void MyCache::print_statistics(const std::string &title) 58 | { 59 | /** 60 | * TODO (Lab3, optional) print statistics with title :) 61 | * 62 | * NOTE: you should use info() to print text. 63 | */ 64 | 65 | info("\"%s\": bingo!\n", title.data()); 66 | } 67 | 68 | auto MyCache::dump() -> MemoryDump 69 | { 70 | return dev->dump(0, MEMORY_SIZE); 71 | } 72 | 73 | void MyCache::run() 74 | { 75 | DBus dbus(this, VCacheTop, DBusPorts{ dreq, dresp }); 76 | 77 | // bind variables to ease testing 78 | _testbench::top = this; 79 | _testbench::scope = VCacheTop; 80 | _testbench::dbus = &dbus; 81 | _testbench::ref = &ref; 82 | 83 | // default to disable FST tracing 84 | enable_fst_trace(false); 85 | 86 | run_testbench(_num_workers); 87 | } 88 | -------------------------------------------------------------------------------- /vivado/src/with_delay/soc_top.sv: -------------------------------------------------------------------------------- 1 | module soc_top #( 2 | parameter logic SIMULATION = 1'b0 3 | )( 4 | input logic clk, reset, 5 | 6 | output logic [3:0] led, 7 | input logic [3:0] sw, 8 | output logic tx 9 | ); 10 | logic valid; 11 | logic [63:0] addr; 12 | logic [63:0] wdata; 13 | logic [1:0] burst; 14 | logic [7:0] len; 15 | logic [7:0] wstrobe; 16 | logic [63:0] rdata; 17 | logic ready; 18 | logic last; 19 | logic [2:0] size; 20 | 21 | logic ram_valid; 22 | logic [63:0] ram_addr; 23 | logic [63:0] ram_wdata; 24 | logic [1:0] ram_burst; 25 | logic [7:0] ram_len; 26 | logic [7:0] ram_wstrobe; 27 | logic [63:0] ram_rdata; 28 | logic ram_ready; 29 | logic ram_last; 30 | 31 | logic device_valid; 32 | logic [63:0] device_addr; 33 | logic [63:0] device_wdata; 34 | logic device_wvalid; 35 | logic [63:0] device_rdata; 36 | logic device_ready; 37 | logic device_last; 38 | 39 | logic cpu_clk; 40 | 41 | /* mycpu */ 42 | mycpu_top mycpu_top_inst(.clk(cpu_clk), .*); 43 | 44 | 45 | /* CBus Crossbar */ 46 | cbus_crossbar cbus_crossbar_inst(.*); 47 | 48 | /* RAM */ 49 | bram_wrapper #(SIMULATION) bram_wrapper_inst( 50 | .clk(cpu_clk), .reset, 51 | .valid(ram_valid), 52 | .addr(ram_addr), 53 | .wdata(ram_wdata), 54 | .rdata(ram_rdata), 55 | .wstrobe(ram_wstrobe), 56 | .burst(ram_burst), 57 | .len(ram_len), 58 | .ready(ram_ready), 59 | .last(ram_last) 60 | ); 61 | 62 | /* Device */ 63 | device #(SIMULATION) device_inst ( 64 | .valid(device_valid), 65 | .addr(device_addr), 66 | .wdata(device_wdata), 67 | .rdata(device_rdata), 68 | .wvalid(device_wvalid), 69 | .ready(device_ready), 70 | .last(device_last), 71 | .* 72 | ); 73 | 74 | if (SIMULATION) 75 | assign cpu_clk = clk; 76 | else 77 | clk_wiz_0 clk_wiz_0(.sys_clk(clk), .cpu_clk(cpu_clk)); 78 | 79 | 80 | endmodule 81 | -------------------------------------------------------------------------------- /vivado/src/with_delay/bram_wrapper.sv: -------------------------------------------------------------------------------- 1 | module bram_wrapper #( 2 | parameter logic SIMULATION = 1'b0 3 | )( 4 | input logic clk, reset, 5 | 6 | input logic valid, 7 | input logic [63:0] addr, 8 | input logic [63:0] wdata, 9 | output logic [63:0] rdata, 10 | input logic [7:0] wstrobe, 11 | input logic [1:0] burst, 12 | input logic [7:0] len, 13 | 14 | output logic ready, 15 | output logic last 16 | 17 | ); 18 | localparam BRAM_DELAY = SIMULATION ? 2 : 32; 19 | logic [15:0] counter; 20 | 21 | logic real_valid; 22 | always_ff @(posedge clk) begin 23 | if (reset || ~valid || last) counter <= '0; 24 | else if (counter != BRAM_DELAY) counter <= counter + 1; 25 | end 26 | 27 | always_ff @(posedge clk) begin 28 | if (reset || counter != BRAM_DELAY || ~valid || last) real_valid <= '0; 29 | else real_valid <= '1; 30 | end 31 | 32 | wire [17:0] base_addr = addr[20:3]; 33 | logic [17:0] burst_addr; 34 | 35 | wire is_incr = burst == 2'b1; 36 | 37 | logic [17:0] burst_counter; 38 | always_ff @(posedge clk) begin 39 | if (real_valid && is_incr && ~reset) begin 40 | burst_counter <= burst_counter + 1; 41 | end else begin 42 | burst_counter <= '0; 43 | end 44 | end 45 | assign burst_addr = base_addr + burst_counter; 46 | 47 | bram_0 bram_0_inst ( 48 | .clka(clk), 49 | .ena(real_valid), 50 | .wea(wstrobe), 51 | .addra(is_incr ? burst_addr : base_addr), 52 | .dina(wdata), 53 | .douta(rdata) 54 | ); 55 | 56 | logic ready_read, ready_write; 57 | logic last_read, last_write; 58 | 59 | always_ff @(posedge clk) begin 60 | ready_read <= ready_write; 61 | last_read <= last_write; 62 | end 63 | 64 | assign ready_write = real_valid; 65 | assign last_write = ~is_incr ? real_valid : real_valid && burst_counter == len; 66 | 67 | assign ready = |wstrobe ? ready_write : ready_read; 68 | assign last = |wstrobe ? last_write : last_read; 69 | endmodule 70 | -------------------------------------------------------------------------------- /vsrc/VCacheTop.sv: -------------------------------------------------------------------------------- 1 | `include "access.svh" 2 | `ifdef VERILATOR 3 | `include "include/common.sv" 4 | /* You should not add any additional includes in this file */ 5 | `endif 6 | 7 | 8 | module VCacheTop 9 | import common::*; 10 | ( 11 | input logic clk, reset_, 12 | 13 | input dbus_req_t dreq, 14 | output dbus_resp_t dresp, 15 | output cbus_req_t creq, 16 | input cbus_resp_t cresp 17 | ); 18 | `include "bus_decl" 19 | 20 | // cbus_req_t dcreq; 21 | // cbus_resp_t dcresp; 22 | 23 | // assign creq = dcreq; 24 | // assign dcresp = cresp; 25 | wire reset = reset_; 26 | DCache top(.*); 27 | `ifdef REFERENCE_CACHE 28 | word_t mem [15:0]/* verilator public_flat_rd */; 29 | for (genvar i = 0; i < 16; i++) 30 | assign mem[i] = top.ram_inst.mem[i]; 31 | `else 32 | /** 33 | * TODO (Lab3, optional) expose internal memory to simulator 34 | * 35 | * NOTE: it will slow down FST tracing significantly, especially 36 | * if your cache is large, you may want to speed up by adding 37 | * "// verilator tracing_off" before the declaration of 38 | * the variable mem. 39 | */ 40 | 41 | /** 42 | * the following is an example. Suppose that you used LUTRAM and 43 | * your cache contains only four cache lines, each of which consists of 44 | * 16 consecutive words in memory. 45 | * 46 | * later you can access the variable mem from C++ via VCacheTop->mem. 47 | * it will possibly become a 1d array of uint32_t. 48 | */ 49 | // typedef word_t [15:0] cache_line_t; 50 | // 51 | // /* verilator tracing_off */ 52 | // cache_line_t [3:0] mem /* verilator public_flat_rd */; 53 | // /* verilator tracing_on */ 54 | // 55 | // for (genvar i = 0; i < 4; i++) begin 56 | // for (genvar j = 0; j < 4; i++) 57 | // assign mem[i][j] = top.xxx.yyy.zzz.lutrams[i].ram_inst.mem[j]; 58 | // end 59 | 60 | 61 | `endif 62 | endmodule 63 | -------------------------------------------------------------------------------- /vsrc/util/CBusArbiter.sv: -------------------------------------------------------------------------------- 1 | `ifndef __CBUSARBITER_SV 2 | `define __CBUSARBITER_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | `else 7 | 8 | `endif 9 | /** 10 | * this implementation is not efficient, since 11 | * it adds one cycle lantency to all requests. 12 | */ 13 | 14 | module CBusArbiter 15 | import common::*;#( 16 | parameter int NUM_INPUTS = 2, // NOTE: NUM_INPUTS >= 1 17 | 18 | localparam int MAX_INDEX = NUM_INPUTS - 1 19 | ) ( 20 | input logic clk, reset, 21 | 22 | input cbus_req_t [MAX_INDEX:0] ireqs, 23 | output cbus_resp_t [MAX_INDEX:0] iresps, 24 | output cbus_req_t oreq, 25 | input cbus_resp_t oresp 26 | ); 27 | logic busy; 28 | int index, select; 29 | cbus_req_t saved_req, selected_req; 30 | 31 | // assign oreq = ireqs[index]; 32 | assign oreq = busy ? ireqs[index] : '0; // prevent early issue 33 | assign selected_req = ireqs[select]; 34 | 35 | // select a preferred request 36 | always_comb begin 37 | select = 0; 38 | 39 | for (int i = 0; i < NUM_INPUTS; i++) begin 40 | if (ireqs[i].valid) begin 41 | select = i; 42 | break; 43 | end 44 | end 45 | end 46 | 47 | // feedback to selected request 48 | always_comb begin 49 | iresps = '0; 50 | 51 | if (busy) begin 52 | for (int i = 0; i < NUM_INPUTS; i++) begin 53 | if (index == i) 54 | iresps[i] = oresp; 55 | end 56 | end 57 | end 58 | 59 | always_ff @(posedge clk) 60 | if (~reset) begin 61 | if (busy) begin 62 | if (oresp.last) 63 | {busy, saved_req} <= '0; 64 | end else begin 65 | // if not valid, busy <= 0 66 | busy <= selected_req.valid; 67 | index <= select; 68 | saved_req <= selected_req; 69 | end 70 | end else begin 71 | {busy, index, saved_req} <= '0; 72 | end 73 | 74 | `UNUSED_OK({saved_req}); 75 | endmodule 76 | 77 | 78 | 79 | `endif -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/mycache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "model.h" 4 | 5 | #include "defs.h" 6 | #include "cache_ref.h" 7 | 8 | class MyCache final : public ModelBase { 9 | public: 10 | MyCache(); 11 | 12 | void reset(); 13 | void tick(); 14 | void run(); 15 | 16 | void enable_statistics(bool enable); 17 | void reset_statistics(); 18 | void print_statistics(const std::string &title); 19 | 20 | // get verilated model's memory. 21 | auto dump()->MemoryDump; 22 | 23 | private: 24 | // your reference model. 25 | CacheRefModel ref; 26 | 27 | struct { 28 | bool enabled = false; 29 | 30 | /** 31 | * TODO (Lab3, optional) store statistics information here :) 32 | */ 33 | 34 | // uint64_t count[4] = {0}; 35 | } stat; 36 | 37 | auto get_creq() const -> CBusWrapper 38 | { 39 | return CBusWrapper(VCacheTop, creq); 40 | } 41 | 42 | void update_statistics(); 43 | 44 | // template is used to reduce the number of unnecessary branches. 45 | // hope compilers optimize those "if"s out. 46 | template 47 | void _tick() 48 | { 49 | // see refcpu/VTop/refcpu.cpp for the descriptions of each stage. 50 | 51 | clk = 0; 52 | 53 | if (Memory) { 54 | auto x = dev->eval_resp(); 55 | // memcpy(cresp, &x, std::min(sizeof(x), sizeof(cresp))); 56 | // cresp = (CBusRespVType) dev->eval_resp(); 57 | cresp[0] = x.data & 0xffffffff; 58 | cresp[1] = (x.data >> 32) & 0xffffffff; 59 | cresp[2] = (x.ready); 60 | cresp[2] = (cresp[2] << 1) | x.last; 61 | } 62 | 63 | eval(); 64 | 65 | if (Trace) 66 | fst_dump(+1); 67 | if (Memory) 68 | dev->eval_req(get_creq()); 69 | if (Stat && stat.enabled) 70 | update_statistics(); 71 | 72 | clk = 1; 73 | 74 | if (Memory) 75 | dev->sync(); 76 | 77 | eval(); 78 | 79 | if (Trace) { 80 | fst_advance(); 81 | fst_dump(+0); 82 | } 83 | } 84 | }; 85 | 86 | using DBus = DBusGen; 87 | using DBusPipeline = DBusPipelineGen; 88 | 89 | namespace _testbench { 90 | extern MyCache *top; 91 | extern VModelScope *scope; 92 | extern DBus *dbus; 93 | extern CacheRefModel *ref; 94 | } 95 | -------------------------------------------------------------------------------- /verilate/Makefile.verilate.mk: -------------------------------------------------------------------------------- 1 | SV_PREFIX = VModel 2 | SV_BUILD = $(BUILD_ROOT)/$(TARGET)/verilated# # build/gcc/refcpu/VTop/verilated 3 | SV_ROOT := $(shell dirname $(TARGET))# # refcpu. NOTE: builtin $(dir ...) will leave the final "/". 4 | SV_NAME := $(notdir $(TARGET))# # VTop 5 | SV_MKFILE = $(SV_BUILD)/$(SV_PREFIX).mk# # build/gcc/refcpu/VTop/verilated/VTop.mk 6 | SV_VTOP = $(SRC)/$(TARGET).sv# # $(SRC)/refcpu/VTop.sv 7 | 8 | SV_EXTERNAL = 9 | 10 | ifeq ($(WITH_XPM),1) 11 | SV_EXTERNAL += $(wildcard $(SRC)/external/xpm_*) 12 | endif 13 | 14 | SV_FOLDERS := $(shell find '$(SRC)/${SV_ROOT}' -type d) 15 | SV_FILES := \ 16 | $(wildcard $(SRC)/util/*) \ 17 | $(wildcard $(SRC)/ram/*) \ 18 | $(wildcard $(SRC)/cache/*) \ 19 | $(wildcard $(SRC)/include/*.svh) \ 20 | $(shell find '$(SRC)/include/$(SV_ROOT)' -type f -name '*.svh') \ 21 | $(shell find '$(SRC)/$(SV_ROOT)' -type f -name '*.sv') \ 22 | $(SRC)/include/bus_decl \ 23 | 24 | SV_INCLUDES := \ 25 | -y $(SRC)/util \ 26 | -y $(SRC)/ram \ 27 | -y $(SRC)/cache \ 28 | -y $(SRC)/include \ 29 | -y $(SRC) \ 30 | $(addprefix -y ,$(SV_$(SRC)_FOLDERS)) 31 | 32 | SV_WARNINGS = \ 33 | -Wall \ 34 | -Wno-IMPORTSTAR\ 35 | -Wno-STMTDLY \ 36 | -Wno-declfilename\ 37 | -Wno-UNUSED \ 38 | # add warnings that you wanna ignore. 39 | 40 | SV_FLAGS = \ 41 | --cc -sv --relative-includes \ 42 | --output-split 6000 \ 43 | --trace-fst --trace-structs \ 44 | --no-trace-params \ 45 | --bbox-unsup \ 46 | --Mdir $(SV_BUILD) \ 47 | --top-module $(SV_NAME) \ 48 | --prefix $(SV_PREFIX) \ 49 | $(SV_INCLUDES) \ 50 | $(SV_WARNINGS) \ 51 | $(SV_EXTRA_FLAGS) 52 | 53 | ifeq ($(USE_CLANG),1) 54 | SV_FLAGS += \ 55 | -CFLAGS -stdlib=libc++ \ 56 | -CFLAGS -Wno-unknown-warning-option 57 | endif 58 | 59 | ifeq ($(VSIM_OPT),1) 60 | SV_FLAGS += -CFLAGS -O3 61 | 62 | # clang 10 has issue with LTO and ar. 63 | ifneq ($(USE_CLANG),1) 64 | SV_FLAGS += -CFLAGS -flto 65 | endif 66 | 67 | endif 68 | 69 | ifeq ($(WITH_XPM),1) 70 | SV_FLAGS += -DICS_WITH_XPM 71 | endif 72 | 73 | ifeq ($(REFERENCE_CACHE), 1) 74 | SV_FLAGS += -DREFERENCE_CACHE 75 | endif 76 | 77 | $(SV_MKFILE): $(SV_FILES) 78 | @mkdir -p $(SV_BUILD) 79 | $(VERILATOR) $(SV_FLAGS) $(SV_EXTERNAL) $(SV_VTOP) 80 | @touch $@ 81 | 82 | .PHONY: verilate 83 | 84 | verilate: $(SV_MKFILE) 85 | -------------------------------------------------------------------------------- /verilate/vsrc/cbus_device.cpp: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | #include 4 | 5 | void CBusDevice::reset() 6 | { 7 | mem->reset(); 8 | tx.reset(); 9 | ntx.reset(); 10 | _strobe = _data = 0; 11 | } 12 | 13 | auto CBusDevice::eval_resp() -> CBusResp 14 | { 15 | if (!enable) 16 | return CBusResp(); 17 | 18 | if (tx.busy) { 19 | // fetch data if needed 20 | word_t data = 0; 21 | if (!tx.is_write) { 22 | auto addr = tx.addr; 23 | data = mem->load(addr); 24 | } 25 | 26 | // return response 27 | return CBusResp(true, tx.last(), data); 28 | } else 29 | return CBusResp(); 30 | } 31 | 32 | void CBusDevice::eval_req(const CBusReq &req) 33 | { 34 | if (!enable) 35 | return; 36 | 37 | if (tx.busy) { 38 | // simple sanity checks 39 | asserts(req.valid(), "req.valid should be 1 during CBus transaction"); 40 | asserts(req.is_write() == tx.is_write, "type of CBus transaction should not change"); 41 | asserts((1 << req.size()) == tx.Number_Bytes, "size of CBus transaction should not change."); 42 | asserts(req.addr() == tx.Start_Address, "address of CBus transaction should not change"); 43 | asserts(req.len() + 1 == tx.Burst_Length, "len of CBus transaction should not change."); 44 | 45 | // pass arguments to commit 46 | _strobe = req.strobe(); 47 | _data = req.data(); 48 | 49 | // evaluate next transaction state 50 | if (tx.last()) 51 | ntx.reset(); 52 | else 53 | ntx.sync(); 54 | } else if (req.valid()) { 55 | // no transaction in progress, so we kick off a new one. 56 | ntx.init( 57 | req.addr(), 58 | static_cast(req.size()), 59 | static_cast(req.len()) 60 | ); 61 | ntx.busy = true; 62 | ntx.is_write = req.is_write(); 63 | } 64 | } 65 | 66 | void CBusDevice::sync() 67 | { 68 | if (enable) { 69 | if (tx.busy && tx.is_write) { 70 | // perform write operation if needed 71 | auto addr = tx.addr; 72 | auto mask1 = STROBE_TO_MASK[_strobe & 0xf]; 73 | auto mask2 = STROBE_TO_MASK[((_strobe) >> 4) & 0xf]; 74 | auto mask = (mask2 << 32) | mask1; 75 | mem->store(addr, _data, mask); 76 | _strobe = _data = 0; 77 | } 78 | 79 | tx = ntx; 80 | } 81 | 82 | enable = p_disable == 0 || randf(0.0, 1.0) >= p_disable; 83 | } 84 | 85 | auto CBusDevice::dump(addr_t addr, size_t size) -> MemoryDump 86 | { 87 | return mem->dump(addr, size); 88 | } 89 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SCALA_FILE = $(shell find ./src/main/scala -name '*.scala' 2>/dev/null) 2 | VERILOG_FILE = $(shell find ./vsrc -name '*.v') 3 | SYSTEMVERILOG_FILE = $(shell find ./vsrc -name '*.sv') 4 | 5 | USE_READY_TO_RUN_NEMU = true 6 | 7 | .DEFAULT_GOAL = verilog 8 | 9 | include Makefile.include 10 | 11 | ifeq ($(wildcard src/*), ) 12 | SCALA_CODE = "false" 13 | else 14 | SCALA_CODE = "true" 15 | endif 16 | 17 | build/SimTop.v: $(SCALA_FILE) 18 | mkdir -p build 19 | ifeq ($(SCALA_CODE), "true") 20 | mill chiselModule.runMain $(SCALA_OPTS) 21 | endif 22 | # cp -r vsrc/* build 23 | 24 | verilog: build/SimTop.v 25 | 26 | sim-verilog: verilog 27 | 28 | emu: verilog 29 | $(MAKE) -C ./difftest emu $(DIFFTEST_OPTS) 30 | 31 | clean: 32 | rm -rf build out 33 | 34 | export NOOP_HOME=$(abspath .) 35 | # export FST_HOME=~ 36 | 37 | sim: 38 | rm -rf build 39 | mkdir -p build 40 | # cp -r vsrc/* build 41 | make EMU_TRACE=1 emu -j12 NOOP_HOME=$(NOOP_HOME) NEMU_HOME=. 42 | 43 | test-lab1a: sim 44 | TEST=$(TEST) ./build/emu --diff ./riscv64-nemu-interpreter-so -i ./ready-to-run/lab1/lab1-test-a.bin $(VOPT) || true 45 | 46 | test-lab1: sim 47 | TEST=$(TEST) ./build/emu --diff ./riscv64-nemu-interpreter-so -i ./ready-to-run/lab1/lab1-test.bin $(VOPT) || true 48 | 49 | test-lab2: sim 50 | TEST=$(TEST) ./build/emu --diff ./riscv64-nemu-interpreter-so -i ./ready-to-run/lab2/all-test-rv64i.bin $(VOPT) || true 51 | 52 | test-lab3: sim 53 | TEST=$(TEST) ./build/emu --diff ./riscv64-nemu-interpreter-so -i ./ready-to-run/lab3/all-test-rv64im.bin $(VOPT) || true 54 | 55 | microbench: 56 | make sim BENCHMARK=1 57 | ./build/emu --diff ./riscv64-nemu-interpreter-so -i ./ready-to-run/challenge/microbench-riscv64-nutshell.bin $(VOPT) || true 58 | 59 | test-lab4: sim 60 | TEST=$(TEST) ./build/emu --diff ./riscv64-nemu-interpreter-so -i ./ready-to-run/lab4/all-test-priv.bin $(VOPT) || true 61 | 62 | test-lab4full: sim 63 | TEST=$(TEST) ./build/emu --diff ./riscv64-nemu-interpreter-so -i ./ready-to-run/lab4/all-test-privfull.bin $(VOPT) || true 64 | 65 | include verilate/Makefile.include 66 | include verilate/Makefile.verilate.mk 67 | include verilate/Makefile.vsim.mk 68 | 69 | test-cache: 70 | @rm -rf build && make vsim -j4 71 | 72 | test-cache-gdb: 73 | @rm -rf build && make vsim-gdb -j4 74 | 75 | test-refcache: 76 | @rm -rf build && make vsim -j4 REFERENCE_CACHE=1 77 | 78 | .PHONY: verilog emu clean sim 79 | -------------------------------------------------------------------------------- /verilate/Makefile.include: -------------------------------------------------------------------------------- 1 | TARGET ?= VCacheTop 2 | SRC ?= vsrc 3 | TEST ?= 4 | FST ?= 5 | USE_CLANG ?= 0 6 | VSIM_ARGS ?= 7 | VSIM_OPT ?= 1 8 | VSIM_SANITIZE ?= 0 9 | SV_EXTRA_FLAGS ?= 10 | CXX_EXTRA_FLAGS ?= 11 | WITH_XPM ?= 0 12 | 13 | #cache test 14 | #command make vsim -j VSIM_OPT=1 15 | 16 | VERILATOR = verilator 17 | GTKWAVE = gtkwave 18 | 19 | help: 20 | @echo 'Available commands:' 21 | @echo ' make verilate: synthesize/compile your RTL code with Verilator.' 22 | @echo ' make vbuild: compile Verilator simulation sources into executable file "vmain".' 23 | @echo ' make vsim: "make vbuild" first and then execute "vmain".' 24 | @echo ' make vsim-gdb: run "vmain" with GDB.' 25 | @echo ' make doc-build: build documents into "doc/book", i.e., run "mdbook build".' 26 | @echo ' make doc-serve: run "mdbook serve".' 27 | @echo ' make doc-sync: upload webpages onto "riteme.site" (requires authentication).' 28 | @echo ' make misc-sync: upload "misc/doc" onto "riteme.site" (requires authentication).' 29 | @echo ' make system-info: print information about installed system packages.' 30 | @echo ' make dump-instructions: dump all instructions during simulation (RefCPU only).' 31 | @echo '' 32 | @echo 'Available parameters:' 33 | @echo ' TARGET: e.g. refcpu/VTop, mycpu/VCacheTop.' 34 | @echo ' TEST: which test under misc/nscscc to simulate. Default to empty string.' 35 | @echo ' FST: where to save FST trace file.' 36 | @echo ' USE_CLANG: use LLVM clang and libc++.' 37 | @echo ' VSIM_ARGS: pass command line arguments to "vmain".' 38 | @echo ' VSIM_OPT: set to 1 to enable compiler optimization. ("-O2 -march=native -flto")' 39 | @echo ' VSIM_SANITIZE: set to 1 to enable address sanitizer and undefined behavior sanitizer.' 40 | @echo ' SV_EXTRA_FLAGS: extra synthesis flags passed to Verilator.' 41 | @echo ' CXX_EXTRA_FLAGS: extra compiler flags passed to C++ compiler.' 42 | @echo ' WITH_XPM: compile with Xilinx XPM modules.' 43 | 44 | # make arguments 45 | 46 | ifeq ($(USE_CLANG),1) 47 | ifeq ($(shell which 'clang++-10' 2> /dev/null),) 48 | CXX=clang++ 49 | else 50 | # for Ubuntu 18.04 51 | CXX=clang++-10 52 | endif 53 | endif 54 | 55 | # determine build root 56 | BUILD_ROOT = build 57 | 58 | ifeq ($(USE_CLANG),1) 59 | BUILD_ROOT := $(BUILD_ROOT)/clang 60 | else 61 | BUILD_ROOT := $(BUILD_ROOT)/gcc 62 | endif 63 | 64 | ifeq ($(VSIM_OPT),1) 65 | BUILD_ROOT := $(BUILD_ROOT)+optimized 66 | endif 67 | 68 | ifeq ($(VSIM_SANITIZE),1) 69 | BUILD_ROOT := $(BUILD_ROOT)+sanitizer 70 | endif 71 | 72 | ifeq ($(WITH_XPM),1) 73 | BUILD_ROOT := $(BUILD_ROOT)+xpm 74 | endif 75 | -------------------------------------------------------------------------------- /verilate/include/model.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "memory.h" 5 | #include "confreg.h" 6 | #include "diff.h" 7 | 8 | #include "verilated_fst_c.h" 9 | 10 | #ifndef ICS_FAKE_MODEL 11 | 12 | #include "VModel.h" 13 | #include "VModel__Syms.h" 14 | // #include "VModel__Dpi.h" 15 | 16 | #else 17 | 18 | /** 19 | * use a fake VModel class in IDE environment. 20 | */ 21 | 22 | class VModel { 23 | public: 24 | void eval(); 25 | void trace(VerilatedFstC *, int); 26 | }; 27 | 28 | #endif 29 | 30 | constexpr int FST_TRACE_MAX_DEPTH = 32; 31 | constexpr size_t FST_TRACE_TIME_SCALE = 10; 32 | constexpr size_t TEXT_TRACE_MAX_LEN = 64; 33 | 34 | class ModelBase : public VModel { 35 | public: 36 | ModelBase() : 37 | force_diff(false), p_disable(0.0f), 38 | _memory_installed(false), _fst_count(0), _current_num(0), 39 | _fst_tfp(nullptr), _text_tfp(nullptr) {} 40 | virtual ~ModelBase(); 41 | 42 | bool force_diff; 43 | float p_disable; 44 | 45 | void install_soc(const std::shared_ptr &mem); 46 | void install_memory(const std::shared_ptr &mem); 47 | void remove_memory(); 48 | 49 | // start/stop: output to some file 50 | // open/close: input to program 51 | void start_fst_trace(const std::string &path); 52 | void enable_fst_trace(bool enable = true); 53 | void stop_fst_trace(); 54 | void start_text_trace(const std::string &path); 55 | void stop_text_trace(); 56 | 57 | // NOTE: CONFREG class does not allow close pty 58 | void open_pty(const std::string &path); 59 | 60 | void set_num_workers(int n_workers); 61 | void set_fst_folder(const std::string &folder); 62 | 63 | virtual void reset() = 0; 64 | virtual void tick() = 0; 65 | virtual void run() = 0; 66 | 67 | void ticks(int count); 68 | 69 | protected: 70 | std::shared_ptr con; 71 | std::shared_ptr dev; 72 | 73 | int _num_workers = 1; 74 | std::string _fst_folder = ""; 75 | 76 | auto fst_time() -> size_t; 77 | void fst_advance(size_t incr = 1); 78 | void fst_dump(size_t offset); 79 | void text_dump(bool enable, addr_t pc, int id, word_t value); 80 | void diff_eof(); 81 | 82 | 83 | virtual void print_num_monitor(int num); 84 | void checkout_confreg(); 85 | 86 | private: 87 | bool _memory_installed; 88 | bool _fst_enabled; 89 | size_t _fst_count; 90 | int _current_num; 91 | 92 | VerilatedFstC *_fst_tfp; 93 | std::string _fst_path; 94 | FILE *_text_tfp; 95 | 96 | auto _fst_avail() -> bool { 97 | return _fst_tfp != nullptr; 98 | } 99 | auto _text_avail() -> bool { 100 | return _text_tfp != nullptr; 101 | } 102 | }; 103 | -------------------------------------------------------------------------------- /vsrc/util/SimpleArbiter.sv: -------------------------------------------------------------------------------- 1 | `ifndef __SIMPLEARBITER_SV 2 | `define __SIMPLEARBITER_SV 3 | 4 | `ifdef VERILATOR 5 | 6 | `else 7 | 8 | `endif 9 | 10 | module SimpleArbiter 11 | import common::*;( 12 | input logic clk, reset, 13 | input dbus_req_t [RMEM_WIDTH-1:0] rreq, 14 | input dbus_req_t [WMEM_WIDTH-1:0] wreq, 15 | input dbus_resp_t [1:0] dresp, 16 | 17 | output dbus_req_t [1:0] dreq, 18 | output dbus_resp_t [RMEM_WIDTH-1:0] rresp, 19 | output dbus_resp_t [WMEM_WIDTH-1:0] wresp 20 | ); 21 | /* 22 | * Priority: R > W 23 | */ 24 | 25 | u1 busy; 26 | u6 [1:0] index; 27 | u6 [1:0] select; 28 | dbus_req_t [1:0] selected_req; 29 | dbus_req_t [1:0] saved_req; 30 | dbus_req_t [3:0] reqs; 31 | assign reqs = {wreq, rreq}; 32 | 33 | assign dreq = busy ? saved_req : selected_req; 34 | for (genvar i = 0; i < 2; i++) begin 35 | assign selected_req[i] = reqs[select[i]]; 36 | end 37 | 38 | /* Select */ 39 | always_comb begin 40 | if (busy) select = index; 41 | else begin 42 | priority case(1'b1) 43 | rreq[0].valid && rreq[1].valid: begin 44 | select[0] = 0; 45 | select[1] = 1; 46 | end 47 | rreq[0].valid: begin 48 | select[0] = 0; 49 | select[1] = (wreq[0].valid & wreq[1].valid) ? 1 : 2; 50 | end 51 | rreq[1].valid: begin 52 | select[0] = (wreq[0].valid & wreq[1].valid) ? 0 : 2; 53 | select[1] = 1; 54 | end 55 | default: begin 56 | select[0] = 2; 57 | select[1] = 3; 58 | end 59 | endcase 60 | end 61 | end 62 | 63 | /* Generate data_ok */ 64 | for (genvar i = 0; i < 4; i++) begin 65 | if (i < 2) always_comb begin // read 66 | rresp[i].data_ok = ~rreq[i].valid | (select[i] == i && (~dreq[0].valid | dresp[0].data_ok) && (~dreq[1].valid | dresp[1].data_ok)); 67 | end else always_comb begin // write 68 | wresp[i-2].data_ok = ~wreq[i-2].valid | (select[i-2] == i && (~dreq[0].valid | dresp[0].data_ok) && (~dreq[1].valid | dresp[1].data_ok)); 69 | end 70 | end 71 | 72 | always_ff @(posedge clk) begin 73 | if (reset) begin 74 | if (busy) begin 75 | if ((~dreq[0].valid | dresp[0].data_ok) && (~dreq[1].valid | dresp[1].data_ok)) begin 76 | busy <= '0; 77 | end 78 | end else begin 79 | busy <= (dreq[0].valid && ~dresp[0].data_ok) | (dreq[1].valid && ~dresp[1].data_ok); 80 | index <= select; 81 | end 82 | end else begin 83 | busy <= '0; 84 | saved_req <= '0; 85 | index <= '0; 86 | end 87 | end 88 | 89 | 90 | for (genvar i = 0; i < RMEM_WIDTH; i++) begin 91 | assign rresp[i].addr_ok = 1'b1; 92 | assign rresp[i].data = dresp[i].data; 93 | end 94 | 95 | for (genvar i = 0; i < WMEM_WIDTH; i++) begin 96 | assign wresp[i].addr_ok = 1'b1; 97 | assign wresp[i].data = 'x; 98 | 99 | end 100 | 101 | endmodule 102 | 103 | 104 | `endif 105 | -------------------------------------------------------------------------------- /verilate/include/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "bus.h" 5 | #include "axi.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using MemoryDump = std::vector; 14 | 15 | class IMemory { 16 | public: 17 | virtual ~IMemory() = default; 18 | 19 | virtual void reset() = 0; 20 | 21 | /** 22 | * addr is indexed by bytes. 23 | * mask is 32 bits, which is different to 4 bit strobe. 24 | */ 25 | 26 | virtual auto load(addr_t addr)->word_t = 0; 27 | virtual void store(addr_t addr, word_t data, word_t mask) = 0; 28 | 29 | virtual auto dump(addr_t addr, size_t size = MEMORY_SIZE)->MemoryDump = 0; 30 | }; 31 | 32 | class MemoryRouter final : public IMemory { 33 | public: 34 | using TranslateFn = std::function; 35 | 36 | struct Entry { 37 | word_t mask; 38 | word_t prefix; 39 | std::shared_ptr mem; 40 | TranslateFn translate; 41 | }; 42 | 43 | MemoryRouter(const std::vector _entries) 44 | : entries(_entries) 45 | { 46 | } 47 | 48 | void reset(); 49 | auto load(addr_t addr)->word_t; 50 | void store(addr_t addr, word_t data, word_t mask); 51 | 52 | auto dump(addr_t addr, size_t size = MEMORY_SIZE)->MemoryDump; 53 | 54 | private: 55 | std::vector entries; 56 | 57 | auto search(addr_t addr)->Entry *; 58 | }; 59 | 60 | class BlockMemory final : public IMemory { 61 | public: 62 | BlockMemory(size_t _size, addr_t _offset = 0); 63 | BlockMemory(const ByteSeq &data, addr_t _offset = 0); 64 | BlockMemory(size_t _size, const ByteSeq &data, addr_t _offset = 0); 65 | 66 | auto size() const->size_t; 67 | auto offset() const->addr_t; 68 | 69 | // set the name to show in DEBUG mode 70 | void set_name(const char *new_name); 71 | 72 | void reset(); 73 | auto load(addr_t addr)->word_t; 74 | void store(addr_t addr, word_t data, word_t mask); 75 | 76 | void map(addr_t addr, const ByteSeq &data); 77 | auto dump(addr_t addr, size_t size = MEMORY_SIZE)->MemoryDump; 78 | 79 | private: 80 | size_t _size; 81 | addr_t _offset; 82 | MemoryDump mem, saved_mem; 83 | const char *name; 84 | }; 85 | 86 | /** 87 | * class CBusDevice should match the behavior of module CBusToAXI. 88 | */ 89 | class CBusDevice { 90 | public: 91 | CBusDevice(const std::shared_ptr &mem, float _p_disable = 0.0f) 92 | : mem(mem), enable(true), p_disable(_p_disable) 93 | { 94 | } 95 | 96 | void reset(); 97 | 98 | /** 99 | * we should guarantee that there's no combinatorial 100 | * logic between the request and the response. 101 | */ 102 | auto eval_resp()->CBusResp; 103 | void eval_req(const CBusReq &req); 104 | void sync(); 105 | 106 | // for model comparing 107 | auto dump(addr_t addr, size_t size = MEMORY_SIZE)->MemoryDump; 108 | 109 | private: 110 | std::shared_ptr mem; 111 | 112 | bool enable; 113 | float p_disable; 114 | word_t _strobe, _data; 115 | AXITransaction tx, ntx; // ntx: new transaction state 116 | }; 117 | -------------------------------------------------------------------------------- /verilate/include/confreg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "memory.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /** 12 | * simulate the "confreg.v" from NSCSCC 13 | */ 14 | 15 | class Confreg final : public IMemory { 16 | public: 17 | static constexpr size_t UART_BUFFER_SIZE = 128; 18 | static constexpr addr_t ADDR_MASK = 0xffff; 19 | 20 | enum Layout : addr_t { 21 | UART_RXD = 0x1000, 22 | UART_TXD = 0x1000, 23 | UART_LSR = 0x1014, 24 | CR0 = 0x8000, 25 | CR1 = 0x8004, 26 | CR2 = 0x8008, 27 | CR3 = 0x800c, 28 | CR4 = 0x8010, 29 | CR5 = 0x8014, 30 | CR6 = 0x8018, 31 | CR7 = 0x801c, 32 | LED = 0xf000, 33 | LED_RG0 = 0xf004, 34 | LED_RG1 = 0xf008, 35 | NUM = 0xf010, 36 | SWITCH = 0xf020, 37 | BTN_KEY = 0xf024, 38 | BTN_STEP = 0xf028, 39 | SW_INTER = 0xf02c, 40 | TIMER = 0xe000, 41 | IO_SIMU = 0xffec, 42 | VIRTUAL_UART = 0xfff0, 43 | SIMU_FLAG = 0xfff4, 44 | OPEN_TRACE = 0xfff8, 45 | NUM_MONITOR = 0xfffc, 46 | }; 47 | 48 | ~Confreg() 49 | { 50 | _uart_close_pty(); 51 | } 52 | 53 | void reset(); 54 | auto load(addr_t addr)->word_t; 55 | void store(addr_t addr, word_t data, word_t mask); 56 | void sync(); 57 | 58 | auto dump(addr_t /*addr*/, size_t /*size*/ = MEMORY_SIZE) -> MemoryDump 59 | { 60 | panic("CONFREG does not support memory dump"); 61 | } 62 | 63 | auto trace_enabled() const -> bool 64 | { 65 | return ctx0.v_open_trace; 66 | } 67 | auto monitor_enabled() const -> bool 68 | { 69 | return ctx0.v_num_monitor; 70 | } 71 | auto has_char() const -> bool 72 | { 73 | return ctx0.vuart_written; 74 | } 75 | auto get_char() const -> uchar 76 | { 77 | return ctx0.vuart_data; 78 | } 79 | auto get_current_num() const -> int 80 | { 81 | return ctx0.v_num >> 24; 82 | } 83 | auto get_acked_num() const -> int 84 | { 85 | return ctx0.v_num & 0xff; 86 | } 87 | 88 | void uart_open_pty(const std::string &path); 89 | 90 | private: 91 | struct Context { 92 | bool uart_avail, uart_written, uart_fetched; 93 | uchar uart_data; 94 | bool vuart_written; 95 | uchar vuart_data; 96 | bool v_open_trace, v_num_monitor; 97 | word_t v_num; 98 | 99 | void reset() 100 | { 101 | uart_avail = false; 102 | uart_written = false; 103 | uart_fetched = false; 104 | uart_data = 0; 105 | vuart_written = false; 106 | vuart_data = 0; 107 | v_open_trace = true; 108 | v_num_monitor = true; 109 | v_num = 0; 110 | } 111 | } ctx, ctx0; 112 | 113 | void _uart_reset(); 114 | auto _uart_has_char() -> bool; 115 | auto _uart_get_char()->uchar; 116 | void _uart_put_char(uchar c); 117 | void _uart_close_pty(); 118 | 119 | struct { 120 | std::mutex lock; 121 | FILE *ipty = nullptr, *opty = nullptr; 122 | std::deque ififo; 123 | ThreadWorker worker; 124 | } uart; 125 | std::unordered_map mem; 126 | 127 | struct change_t { 128 | addr_t addr; 129 | word_t data; 130 | }; 131 | std::vector changes; 132 | }; 133 | -------------------------------------------------------------------------------- /vsrc/ram/RAM_SimpleDualPort.sv: -------------------------------------------------------------------------------- 1 | `ifndef __RAM_SIMPLEDUALPORT_SV 2 | `define __RAM_SIMPLEDUALPORT_SV 3 | 4 | module RAM_SimpleDualPort #( 5 | parameter int ADDR_WIDTH = 10, 6 | parameter int DATA_WIDTH = 64, 7 | parameter int BYTE_WIDTH = 64, 8 | parameter MEM_TYPE = 0, 9 | parameter READ_LATENCY = 0, 10 | 11 | localparam WORD_WIDTH = DATA_WIDTH, 12 | localparam NUM_WORDS = 2 ** ADDR_WIDTH, 13 | localparam NUM_BITS = NUM_WORDS * DATA_WIDTH, 14 | localparam NUM_BYTES = NUM_BITS / BYTE_WIDTH, 15 | localparam BYTES_PER_WORD = DATA_WIDTH / BYTE_WIDTH, 16 | // types 17 | localparam type raddr_t = logic[ADDR_WIDTH-1:0], 18 | localparam type rstrobe_t = logic[BYTES_PER_WORD-1:0], 19 | localparam type rword_t = logic[WORD_WIDTH-1:0], 20 | localparam type rbundle_t = logic [BYTES_PER_WORD-1:0][BYTE_WIDTH-1:0], 21 | localparam type rview_t = union packed { 22 | rword_t word; 23 | rbundle_t lanes; 24 | } 25 | ) ( 26 | input logic clk, en, 27 | 28 | input raddr_t raddr, waddr, 29 | input rstrobe_t strobe, 30 | input rview_t wdata, 31 | output rword_t rdata 32 | ); 33 | 34 | `ifdef VERILATOR 35 | /* verilator tracing_off */ 36 | rview_t mem [NUM_WORDS-1:0]; 37 | initial begin 38 | for (int i = 0; i < NUM_WORDS; i++) begin 39 | mem[i] = '0; 40 | end 41 | 42 | end 43 | if (READ_LATENCY == 0) 44 | assign rdata = mem[raddr]; 45 | else begin 46 | rword_t reads [READ_LATENCY-1:0]; 47 | always_ff @(posedge clk) begin 48 | reads[0] <= mem[raddr]; 49 | end 50 | 51 | for (genvar i = 1; i < READ_LATENCY; i++) begin 52 | always_ff @(posedge clk) begin 53 | reads[i] <= reads[i-1]; 54 | end 55 | end 56 | assign rdata = reads[READ_LATENCY-1]; 57 | 58 | end 59 | 60 | always_ff @(posedge clk) begin 61 | if (en) 62 | for (int i = 0; i < BYTES_PER_WORD; i++) 63 | if (strobe[i]) 64 | mem[waddr].lanes[i] <= wdata.lanes[i]; 65 | end 66 | /* verilator tracing_on */ 67 | `else 68 | xpm_memory_sdpram #( 69 | .ADDR_WIDTH_A(ADDR_WIDTH), 70 | .ADDR_WIDTH_B(ADDR_WIDTH), 71 | .AUTO_SLEEP_TIME(0), 72 | .BYTE_WRITE_WIDTH_A(BYTE_WIDTH), 73 | // .CASCADE_HEIGHT(0), 74 | .ECC_MODE("no_ecc"), 75 | .MEMORY_INIT_FILE("none"), 76 | .MEMORY_INIT_PARAM("0"), 77 | .MEMORY_OPTIMIZATION("true"), 78 | .MEMORY_PRIMITIVE(MEM_TYPE), 79 | .MEMORY_SIZE(NUM_BITS), 80 | .MESSAGE_CONTROL(0), 81 | .READ_DATA_WIDTH_B(WORD_WIDTH), 82 | .READ_LATENCY_B(READ_LATENCY), 83 | .READ_RESET_VALUE_B("0"), 84 | .RST_MODE_A("SYNC"), 85 | .RST_MODE_B("SYNC"), 86 | // .SIM_ASSERT_CHK(1), 87 | .USE_MEM_INIT(0), 88 | .WAKEUP_TIME("disable_sleep"), 89 | .WRITE_DATA_WIDTH_A(WORD_WIDTH), 90 | .WRITE_MODE_B("read_first") 91 | ) xpm_memory_sdpram_inst ( 92 | .clka(clk), .clkb(clk), 93 | .ena(en), 94 | .enb(1'b1), 95 | .addra(waddr), 96 | .addrb(raddr), 97 | .wea(strobe), 98 | .dina(wdata), 99 | .doutb(rdata), 100 | 101 | .regceb(1), 102 | .rstb(0), 103 | .sleep(0), 104 | .injectdbiterra(0), 105 | .injectsbiterra(0) 106 | ); 107 | `endif 108 | 109 | endmodule 110 | 111 | 112 | `endif 113 | -------------------------------------------------------------------------------- /vsrc/include/bus_decl: -------------------------------------------------------------------------------- 1 | /** 2 | * please include this file directly inside module VTop/VCacheTop etc. 3 | * to export struct member accessors of cbus_req_t. for example, 4 | * 5 | * ``` 6 | * module VTop; 7 | * `include "bus_decl" 8 | * // ... 9 | * endmodule 10 | * ``` 11 | */ 12 | 13 | // `include "common.svh" 14 | // `include "access.svh" 15 | 16 | `ifdef STRUCT_ACCESSOR 17 | 18 | `STRUCT_ACCESSOR(cbus_req_t, valid, logic); 19 | `STRUCT_ACCESSOR(cbus_req_t, is_write, logic); 20 | `STRUCT_ACCESSOR(cbus_req_t, size, msize_t); 21 | `STRUCT_ACCESSOR(cbus_req_t, addr, addr_t); 22 | `STRUCT_ACCESSOR(cbus_req_t, strobe, strobe_t); 23 | `STRUCT_ACCESSOR(cbus_req_t, data, word_t); 24 | `STRUCT_ACCESSOR(cbus_req_t, len, mlen_t); 25 | // `STRUCT_ACCESSOR(cbus_req_t, burst, axi_burst_type_t); 26 | 27 | `STRUCT_ACCESSOR(cbus_resp_t, ready, logic); 28 | `STRUCT_ACCESSOR(cbus_resp_t, last, logic); 29 | `STRUCT_ACCESSOR(cbus_resp_t, data, word_t); 30 | 31 | `STRUCT_ACCESSOR(dbus_req_t, valid, logic); 32 | `STRUCT_ACCESSOR(dbus_req_t, size, msize_t); 33 | `STRUCT_ACCESSOR(dbus_req_t, addr, addr_t); 34 | `STRUCT_ACCESSOR(dbus_req_t, strobe, strobe_t); 35 | `STRUCT_ACCESSOR(dbus_req_t, data, word_t); 36 | 37 | `STRUCT_ACCESSOR(dbus_resp_t, addr_ok, logic); 38 | `STRUCT_ACCESSOR(dbus_resp_t, data_ok, logic); 39 | `STRUCT_ACCESSOR(dbus_resp_t, data, word_t); 40 | 41 | `else 42 | 43 | `ifdef VERILATOR 44 | `error "macro \"STRUCT_ACCESSOR\" is not defined." 45 | `endif 46 | 47 | `endif 48 | 49 | /** 50 | * helper tasks to issue/clear DBus requests. 51 | */ 52 | 53 | task dbus_update( 54 | input logic valid, 55 | input addr_t addr, 56 | input msize_t size, 57 | input strobe_t strobe, 58 | input word_t data, 59 | 60 | output dbus_req_t req 61 | ); 62 | /* verilator public */ 63 | req = dbus_req_t'{ 64 | valid : valid, 65 | addr : addr, 66 | size : size, 67 | strobe : strobe, 68 | data : data 69 | }; 70 | endtask 71 | 72 | task dbus_issue_store( 73 | input addr_t addr, 74 | input msize_t size, 75 | input strobe_t strobe, 76 | input word_t data, 77 | 78 | output dbus_req_t req 79 | ); 80 | /* verilator public */ 81 | req = dbus_req_t'{ 82 | valid : 1'b1, 83 | addr : addr, 84 | size : size, 85 | strobe : strobe, 86 | data : data 87 | }; 88 | endtask 89 | 90 | function i2 dbus_handshake(input dbus_resp_t resp, input i2 mask); 91 | /* verilator public */ 92 | automatic logic _unused_ok = &{1'b0, resp}; 93 | return {resp.data_ok, resp.addr_ok} & mask; 94 | endfunction 95 | 96 | /* verilator lint_save */ 97 | /* verilator lint_off UNDRIVEN */ 98 | 99 | task dbus_issue_load( 100 | input addr_t addr, 101 | input msize_t size, 102 | 103 | output dbus_req_t req 104 | ); 105 | /* verilator public */ 106 | {req.valid, req.addr, req.size, req.strobe} = {1'b1, addr, size, 8'b0}; 107 | // req.data left unchanged 108 | endtask 109 | 110 | task dbus_reset_valid( 111 | output dbus_req_t req 112 | ); 113 | /* verilator public */ 114 | req.valid = 1'b0; 115 | endtask 116 | 117 | /* verilator lint_restore */ 118 | -------------------------------------------------------------------------------- /verilate/vsrc/VCacheTop/cache_ref.cpp: -------------------------------------------------------------------------------- 1 | #include "mycache.h" 2 | #include "cache_ref.h" 3 | 4 | CacheRefModel::CacheRefModel(MyCache *_top, size_t memory_size) 5 | : top(_top), scope(top->VCacheTop), mem(memory_size) 6 | { 7 | mem.set_name("ref"); 8 | #ifdef REFERENCE_CACHE 9 | // REFERENCE_CACHE does nothing else 10 | #else 11 | /** 12 | * TODO (Lab3) setup reference model :) 13 | */ 14 | 15 | #endif 16 | } 17 | 18 | void CacheRefModel::reset() 19 | { 20 | log_debug("ref: reset()\n"); 21 | mem.reset(); 22 | #ifdef REFERENCE_CACHE 23 | // REFERENCE_CACHE does nothing else 24 | #else 25 | /** 26 | * TODO (Lab3) reset reference model :) 27 | */ 28 | 29 | #endif 30 | } 31 | 32 | auto CacheRefModel::load(addr_t addr, AXISize size) -> word_t 33 | { 34 | log_debug("ref: load(0x%lx, %d)\n", addr, 1 << size); 35 | #ifdef REFERENCE_CACHE 36 | addr_t start = addr / 128 * 128; 37 | for (int i = 0; i < 16; i++) { 38 | buffer[i] = mem.load(start + 8 * i); 39 | } 40 | 41 | return buffer[addr % 128 / 8]; 42 | #else 43 | /** 44 | * TODO (Lab3) implement load operation for reference model :) 45 | */ 46 | 47 | return mem.load(0x0); 48 | #endif 49 | } 50 | 51 | void CacheRefModel::store(addr_t addr, AXISize size, word_t strobe, word_t data) 52 | { 53 | 54 | log_debug("ref: store(0x%lx, %d, %x, \"%016x\")\n", addr, 1 << size, strobe, data); 55 | #ifdef REFERENCE_CACHE 56 | addr_t start = addr / 128 * 128; 57 | for (int i = 0; i < 16; i++) { 58 | buffer[i] = mem.load(start + 8 * i); 59 | } 60 | 61 | auto mask1 = STROBE_TO_MASK[strobe & 0xf]; 62 | auto mask2 = STROBE_TO_MASK[((strobe) >> 4) & 0xf]; 63 | auto mask = (mask2 << 32) | mask1; 64 | auto &value = buffer[addr % 128 / 8]; 65 | value = (data & mask) | (value & ~mask); 66 | mem.store(addr, data, mask); 67 | return; 68 | #else 69 | /** 70 | * TODO (Lab3) implement store operation for reference model :) 71 | */ 72 | 73 | mem.store(0x0, 0xdeadbeef, 0b1111); 74 | #endif 75 | } 76 | 77 | void CacheRefModel::check_internal() 78 | { 79 | log_debug("ref: check_internal()\n"); 80 | #ifdef REFERENCE_CACHE 81 | /** 82 | * the following comes from StupidBuffer's reference model. 83 | */ 84 | for (int i = 0; i < 16; i++) { 85 | asserts( 86 | buffer[i] == scope->mem[i], 87 | "reference model's internal state is different from RTL model." 88 | " at mem[%x], expected = %016x, got = %016x", 89 | i, buffer[i], scope->mem[i] 90 | ); 91 | } 92 | #else 93 | /** 94 | * TODO (Lab3) compare reference model's internal states to RTL model :) 95 | * 96 | * NOTE: you can use pointer top and scope to access internal signals 97 | * in your RTL model, e.g., top->clk, scope->mem. 98 | */ 99 | 100 | #endif 101 | } 102 | 103 | void CacheRefModel::check_memory() 104 | { 105 | log_debug("ref: check_memory()\n"); 106 | #ifdef REFERENCE_CACHE 107 | /** 108 | * the following comes from StupidBuffer's reference model. 109 | */ 110 | asserts(mem.dump(0, mem.size()) == top->dump(), "reference model's memory content is different from RTL model"); 111 | #else 112 | /** 113 | * TODO (Lab3) compare reference model's memory to RTL model :) 114 | * 115 | * NOTE: you can use pointer top and scope to access internal signals 116 | * in your RTL model, e.g., top->clk, scope->mem. 117 | * you can use mem.dump() and MyCache::dump() to get the full contents 118 | * of both memories. 119 | */ 120 | 121 | #endif 122 | } 123 | -------------------------------------------------------------------------------- /vsrc/cache/DCache.sv: -------------------------------------------------------------------------------- 1 | `ifndef __DCACHE_SV 2 | `define __DCACHE_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | /* You should not add any additional includes in this file */ 7 | `endif 8 | 9 | module DCache 10 | import common::*; #( 11 | /* You can modify this part to support more parameters */ 12 | /* e.g. OFFSET_BITS, INDEX_BITS, TAG_BITS */ 13 | parameter X = 1 14 | )( 15 | input logic clk, reset, 16 | 17 | input dbus_req_t dreq, 18 | output dbus_resp_t dresp, 19 | output cbus_req_t creq, 20 | input cbus_resp_t cresp 21 | ); 22 | 23 | `ifndef REFERENCE_CACHE 24 | 25 | /* TODO: Lab3 Cache */ 26 | 27 | 28 | `else 29 | 30 | typedef enum u2 { 31 | IDLE, 32 | FETCH, 33 | READY, 34 | FLUSH 35 | } state_t /* verilator public */; 36 | 37 | // typedefs 38 | typedef union packed { 39 | word_t data; 40 | u8 [7:0] lanes; 41 | } view_t; 42 | 43 | typedef u4 offset_t; 44 | 45 | // registers 46 | state_t state /* verilator public_flat_rd */; 47 | dbus_req_t req; // dreq is saved once addr_ok is asserted. 48 | offset_t offset; 49 | 50 | // wires 51 | offset_t start; 52 | assign start = dreq.addr[6:3]; 53 | 54 | // the RAM 55 | struct packed { 56 | logic en; 57 | strobe_t strobe; 58 | word_t wdata; 59 | } ram; 60 | word_t ram_rdata; 61 | 62 | always_comb 63 | unique case (state) 64 | FETCH: begin 65 | ram.en = 1; 66 | ram.strobe = 8'b11111111; 67 | ram.wdata = cresp.data; 68 | end 69 | 70 | READY: begin 71 | ram.en = 1; 72 | ram.strobe = req.strobe; 73 | ram.wdata = req.data; 74 | end 75 | 76 | default: ram = '0; 77 | endcase 78 | 79 | RAM_SinglePort #( 80 | .ADDR_WIDTH(4), 81 | .DATA_WIDTH(64), 82 | .BYTE_WIDTH(8), 83 | .READ_LATENCY(0) 84 | ) ram_inst ( 85 | .clk(clk), .en(ram.en), 86 | .addr(offset), 87 | .strobe(ram.strobe), 88 | .wdata(ram.wdata), 89 | .rdata(ram_rdata) 90 | ); 91 | 92 | // DBus driver 93 | assign dresp.addr_ok = state == IDLE; 94 | assign dresp.data_ok = state == READY; 95 | assign dresp.data = ram_rdata; 96 | 97 | // CBus driver 98 | assign creq.valid = state == FETCH || state == FLUSH; 99 | assign creq.is_write = state == FLUSH; 100 | assign creq.size = MSIZE8; 101 | assign creq.addr = req.addr; 102 | assign creq.strobe = 8'b11111111; 103 | assign creq.data = ram_rdata; 104 | assign creq.len = MLEN16; 105 | assign creq.burst = AXI_BURST_INCR; 106 | 107 | // the FSM 108 | always_ff @(posedge clk) 109 | if (~reset) begin 110 | unique case (state) 111 | IDLE: if (dreq.valid) begin 112 | state <= FETCH; 113 | req <= dreq; 114 | offset <= start; 115 | end 116 | 117 | FETCH: if (cresp.ready) begin 118 | state <= cresp.last ? READY : FETCH; 119 | offset <= offset + 1; 120 | end 121 | 122 | READY: begin 123 | state <= (|req.strobe) ? FLUSH : IDLE; 124 | end 125 | 126 | FLUSH: if (cresp.ready) begin 127 | state <= cresp.last ? IDLE : FLUSH; 128 | offset <= offset + 1; 129 | end 130 | 131 | endcase 132 | end else begin 133 | state <= IDLE; 134 | {req, offset} <= '0; 135 | end 136 | 137 | `endif 138 | 139 | endmodule 140 | 141 | `endif 142 | -------------------------------------------------------------------------------- /vsrc/ram/LUTRAM_DualPort.sv: -------------------------------------------------------------------------------- 1 | `ifndef __LUTRAM_DUALPORT_SV 2 | `define __LUTRAM_DUALPORT_SV 3 | 4 | module LUTRAM_DualPort #( 5 | parameter int ADDR_WIDTH = 6, 6 | parameter int DATA_WIDTH = 64, 7 | parameter int BYTE_WIDTH = 64, 8 | parameter READ_LATENCY = 0, 9 | 10 | localparam WORD_WIDTH = DATA_WIDTH, 11 | localparam NUM_WORDS = 2 ** ADDR_WIDTH, 12 | localparam NUM_BITS = NUM_WORDS * DATA_WIDTH, 13 | localparam NUM_BYTES = NUM_BITS / BYTE_WIDTH, 14 | localparam BYTES_PER_WORD = DATA_WIDTH / BYTE_WIDTH, 15 | // types 16 | localparam type raddr_t = logic[ADDR_WIDTH-1:0], 17 | localparam type rstrobe_t = logic[BYTES_PER_WORD-1:0], 18 | localparam type rword_t = logic[WORD_WIDTH-1:0], 19 | localparam type rbundle_t = logic [BYTES_PER_WORD-1:0][BYTE_WIDTH-1:0], 20 | localparam type rview_t = union packed { 21 | rword_t word; 22 | rbundle_t lanes; 23 | } 24 | )( 25 | input logic clk, en_1, en_2, 26 | 27 | input raddr_t addr_1, addr_2, 28 | input rstrobe_t strobe, 29 | input rview_t wdata, 30 | output rword_t rdata_1, rdata_2 31 | ); 32 | 33 | `ifdef VERILATOR 34 | /* verilator tracing_on */ 35 | rview_t mem [NUM_WORDS-1:0]; 36 | initial begin 37 | for (int i = 0; i < NUM_WORDS; i++) begin 38 | mem[i] = '0; 39 | end 40 | 41 | end 42 | if (READ_LATENCY == 0) begin 43 | assign rdata_1 = mem[addr_1]; 44 | assign rdata_2 = mem[addr_2]; 45 | end else begin 46 | rword_t reads_1 [READ_LATENCY-1:0]; 47 | always_ff @(posedge clk) begin 48 | reads_1[0] <= mem[addr_1]; 49 | end 50 | 51 | for (genvar i = 1; i < READ_LATENCY; i++) begin 52 | always_ff @(posedge clk) begin 53 | reads_1[i] <= reads_1[i-1]; 54 | end 55 | end 56 | assign rdata_1 = reads_1[READ_LATENCY-1]; 57 | 58 | rword_t reads_2 [READ_LATENCY-1:0]; 59 | always_ff @(posedge clk) begin 60 | reads_2[0] <= mem[addr_2]; 61 | end 62 | 63 | for (genvar i = 1; i < READ_LATENCY; i++) begin 64 | always_ff @(posedge clk) begin 65 | reads_2[i] <= reads_2[i-1]; 66 | end 67 | end 68 | assign rdata_2 = reads_2[READ_LATENCY-1]; 69 | 70 | end 71 | 72 | always_ff @(posedge clk) begin 73 | if (en_1) 74 | for (int i = 0; i < BYTES_PER_WORD; i++) 75 | if (strobe[i]) 76 | mem[addr_1].lanes[i] <= wdata.lanes[i]; 77 | end 78 | /* verilator tracing_on */ 79 | `else 80 | xpm_memory_dpdistram #( 81 | .ADDR_WIDTH_A(ADDR_WIDTH), 82 | .ADDR_WIDTH_B(ADDR_WIDTH), 83 | .BYTE_WRITE_WIDTH_A(BYTE_WIDTH), // byte-write enable 84 | .CLOCKING_MODE("common_clock"), 85 | .MEMORY_INIT_FILE("none"), 86 | .MEMORY_INIT_PARAM("0"), 87 | .MEMORY_OPTIMIZATION("true"), 88 | .MEMORY_SIZE(NUM_BITS), // in bits 89 | .MESSAGE_CONTROL(0), // disable message reporting 90 | .READ_DATA_WIDTH_A(DATA_WIDTH), 91 | .READ_DATA_WIDTH_B(DATA_WIDTH), 92 | .READ_LATENCY_A(READ_LATENCY), 93 | .READ_LATENCY_B(READ_LATENCY), 94 | .READ_RESET_VALUE_A(0), 95 | .READ_RESET_VALUE_B(0), 96 | .RST_MODE_A("SYNC"), 97 | .RST_MODE_B("SYNC"), 98 | // .SIM_ASSERT_CHK(1), 99 | // .USE_EMBEDDED_CONSTRAINT(0), 100 | .USE_MEM_INIT(1), 101 | .WRITE_DATA_WIDTH_A(DATA_WIDTH) 102 | ) xpm_memory_dpdistram_inst ( 103 | .clka(clk), .clkb(clk), // use the same clock 104 | .ena(en_1), .enb(en_2), 105 | .rsta(0), .rstb(0), 106 | .regcea(1), .regceb(1), 107 | 108 | // port 1/a 109 | .wea(strobe), 110 | .addra(addr_1), 111 | .dina(wdata), 112 | .douta(rdata_1), 113 | 114 | // port 2/b 115 | .addrb(addr_2), 116 | .doutb(rdata_2) 117 | ); 118 | `endif 119 | 120 | endmodule 121 | 122 | `endif 123 | -------------------------------------------------------------------------------- /verilate/vsrc/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "memory.h" 3 | 4 | #include 5 | #include 6 | 7 | auto MemoryRouter::search(addr_t addr) -> Entry * 8 | { 9 | for (auto &e : entries) { 10 | if ((addr & e.mask) == e.prefix) 11 | return &e; 12 | } 13 | return nullptr; 14 | } 15 | 16 | void MemoryRouter::reset() 17 | { 18 | for (auto &e : entries) { 19 | e.mem->reset(); 20 | } 21 | } 22 | 23 | auto MemoryRouter::load(addr_t addr) -> word_t 24 | { 25 | auto e = search(addr); 26 | asserts(e, "no device at 0x%016x", addr); 27 | 28 | auto paddr = e->translate(addr); 29 | return e->mem->load(paddr); 30 | } 31 | 32 | void MemoryRouter::store(addr_t addr, word_t data, word_t mask) 33 | { 34 | auto e = search(addr); 35 | asserts(e, "no device at 0x%016x", addr); 36 | 37 | auto paddr = e->translate(addr); 38 | e->mem->store(paddr, data, mask); 39 | } 40 | 41 | auto MemoryRouter::dump(addr_t addr, size_t size) -> MemoryDump 42 | { 43 | // NOTE: does not support cross module memory dump. 44 | 45 | auto e = search(addr); 46 | asserts(e, "no device at 0x%016x", addr); 47 | 48 | auto paddr = e->translate(addr); 49 | return e->mem->dump(paddr, size); 50 | } 51 | 52 | BlockMemory::BlockMemory(size_t _size, addr_t _offset) 53 | : _size(_size), _offset(_offset), name("mem") 54 | { 55 | asserts(_size % 8 == 0, "size must be multiple of 8"); 56 | mem.resize(_size / 8); 57 | saved_mem = mem; 58 | } 59 | 60 | BlockMemory::BlockMemory(const ByteSeq &data, addr_t _offset) 61 | : BlockMemory(data.size(), _offset) 62 | { 63 | map(_offset, data); 64 | saved_mem = mem; 65 | } 66 | 67 | BlockMemory::BlockMemory(size_t _size, const ByteSeq &data, addr_t _offset) 68 | : BlockMemory(_size, _offset) 69 | { 70 | map(_offset, data); 71 | saved_mem = mem; 72 | } 73 | 74 | auto BlockMemory::size() const -> size_t 75 | { 76 | return _size; 77 | } 78 | 79 | auto BlockMemory::offset() const -> addr_t 80 | { 81 | return _offset; 82 | } 83 | 84 | void BlockMemory::set_name(const char *new_name) 85 | { 86 | name = new_name; 87 | } 88 | 89 | void BlockMemory::reset() 90 | { 91 | mem = saved_mem; 92 | } 93 | 94 | void BlockMemory::map(addr_t addr, const ByteSeq &data) 95 | { 96 | addr -= _offset; 97 | asserts(addr + data.size() <= _size, "memory map out of range"); 98 | 99 | for (size_t i = 0; i < data.size(); i++) { 100 | size_t index = (addr + i) / 8; 101 | word_t shamt = (addr + i) % 8 * 8; 102 | word_t mask = ~(0xffu << shamt); 103 | 104 | word_t &value = mem[index]; 105 | value = (value & mask) | (word_t(data[i]) << shamt); 106 | } 107 | } 108 | 109 | auto BlockMemory::dump(addr_t addr, size_t size) -> MemoryDump 110 | { 111 | asserts((addr & 0x7) == 0, "addr must be aligned on word boundry"); 112 | asserts((size & 0x7) == 0, "size must be multiple of 4"); 113 | addr >>= 3; 114 | size >>= 3; 115 | asserts(addr + size <= mem.size(), "memory dump out of range"); 116 | return MemoryDump(mem.begin() + addr, mem.begin() + addr + size); 117 | } 118 | 119 | auto BlockMemory::load(addr_t addr) -> word_t 120 | { 121 | addr_t caddr = addr; 122 | addr -= _offset; 123 | asserts(addr < _size, "addr=0x%016x out of range", addr); 124 | 125 | size_t index = addr / 8; // align to 4 bytes 126 | word_t value = mem[index]; 127 | 128 | log_debug("%s[%016x] => %016x\n", name, caddr, value); 129 | 130 | return value; 131 | } 132 | 133 | void BlockMemory::store(addr_t addr, word_t data, word_t mask) 134 | { 135 | addr_t caddr = addr; 136 | addr -= _offset; 137 | asserts(addr < _size, "addr=0x%016x out of range", addr); 138 | 139 | size_t index = addr / 8; // align to 8 bytes 140 | word_t &value = mem[index]; 141 | value = (value & ~mask) | (data & mask); 142 | 143 | log_debug("%s[%016x] <- %016x\n", name, caddr, value); 144 | } 145 | -------------------------------------------------------------------------------- /vsrc/ram/RAM_SinglePort.sv: -------------------------------------------------------------------------------- 1 | `ifndef __RAM_SINGLEPORT_SV 2 | `define __RAM_SINGLEPORT_SV 3 | 4 | module RAM_SinglePort #( 5 | parameter int ADDR_WIDTH = 10, 6 | parameter int DATA_WIDTH = 64, 7 | parameter int BYTE_WIDTH = 64, 8 | parameter MEM_TYPE = 0, 9 | parameter READ_LATENCY = 0, 10 | 11 | localparam WORD_WIDTH = DATA_WIDTH, 12 | localparam NUM_WORDS = 2 ** ADDR_WIDTH, 13 | localparam NUM_BITS = NUM_WORDS * DATA_WIDTH, 14 | localparam NUM_BYTES = NUM_BITS / BYTE_WIDTH, 15 | localparam BYTES_PER_WORD = DATA_WIDTH / BYTE_WIDTH, 16 | // types 17 | localparam type raddr_t = logic[ADDR_WIDTH-1:0], 18 | localparam type rstrobe_t = logic[BYTES_PER_WORD-1:0], 19 | localparam type rword_t = logic[WORD_WIDTH-1:0], 20 | localparam type rbundle_t = logic [BYTES_PER_WORD-1:0][BYTE_WIDTH-1:0], 21 | localparam type rview_t = union packed { 22 | rword_t word; 23 | rbundle_t lanes; 24 | } 25 | ) ( 26 | input logic clk, en, 27 | 28 | input raddr_t addr, 29 | input rstrobe_t strobe, 30 | input rview_t wdata, 31 | output rword_t rdata 32 | ); 33 | 34 | `ifdef VERILATOR 35 | /* verilator tracing_off */ 36 | rview_t mem [NUM_WORDS-1:0]; 37 | initial begin 38 | for (int i = 0; i < NUM_WORDS; i++) begin 39 | mem[i] = '0; 40 | end 41 | 42 | end 43 | if (READ_LATENCY == 0) 44 | assign rdata = mem[addr]; 45 | else begin 46 | rword_t reads [READ_LATENCY-1:0]; 47 | always_ff @(posedge clk) begin 48 | reads[0] <= mem[addr]; 49 | end 50 | 51 | for (genvar i = 1; i < READ_LATENCY; i++) begin 52 | always_ff @(posedge clk) begin 53 | reads[i] <= reads[i-1]; 54 | end 55 | end 56 | assign rdata = reads[READ_LATENCY-1]; 57 | 58 | end 59 | 60 | always_ff @(posedge clk) begin 61 | if (en) 62 | for (int i = 0; i < BYTES_PER_WORD; i++) 63 | if (strobe[i]) 64 | mem[addr].lanes[i] <= wdata.lanes[i]; 65 | end 66 | /* verilator tracing_on */ 67 | `else 68 | 69 | localparam logic NEED_EXPAND = BYTE_WIDTH != WORD_WIDTH && BYTE_WIDTH != 8; 70 | localparam REAL_BYTE_WIDTH = NEED_EXPAND ? 8 : BYTE_WIDTH; 71 | localparam REAL_STROBE_BITS = NEED_EXPAND ? WORD_WIDTH/8 : BYTES_PER_WORD; 72 | localparam type real_strobe_t = logic [REAL_STROBE_BITS-1:0]; 73 | real_strobe_t real_strobe; 74 | if (BYTE_WIDTH != WORD_WIDTH && BYTE_WIDTH != 8) begin 75 | initial begin : validation 76 | if ((BYTE_WIDTH % 8) != '0) begin 77 | $error("BYTE_WIDTH should be 8 bit align for byte write."); 78 | end 79 | if ((WORD_WIDTH % BYTE_WIDTH) != '0) begin 80 | $error("WORD_WIDTH % BYTE_WIDTH should be 0."); 81 | end 82 | if (WORD_WIDTH < 8) begin 83 | $error("WORD_WIDTH < 8."); 84 | end 85 | end : validation 86 | localparam EXPAND = BYTE_WIDTH / 8; 87 | for (genvar i = 0; i < WORD_WIDTH/8; i++) 88 | assign real_strobe[i] = strobe[i / EXPAND]; 89 | end else begin; 90 | assign real_strobe = strobe; 91 | end 92 | 93 | xpm_memory_spram #( 94 | .ADDR_WIDTH_A(ADDR_WIDTH), 95 | .AUTO_SLEEP_TIME(0), 96 | .BYTE_WRITE_WIDTH_A(REAL_BYTE_WIDTH), 97 | // .CASCADE_HEIGHT(0), 98 | .ECC_MODE("no_ecc"), 99 | .MEMORY_INIT_FILE("none"), 100 | .MEMORY_INIT_PARAM("0"), 101 | .MEMORY_OPTIMIZATION("true"), 102 | .MEMORY_PRIMITIVE(MEM_TYPE), 103 | .MEMORY_SIZE(NUM_BITS), 104 | .MESSAGE_CONTROL(0), 105 | .READ_DATA_WIDTH_A(WORD_WIDTH), 106 | .READ_LATENCY_A(READ_LATENCY), 107 | .READ_RESET_VALUE_A("0"), 108 | .RST_MODE_A("SYNC"), 109 | // .SIM_ASSERT_CHK(1), 110 | .USE_MEM_INIT(0), 111 | .WAKEUP_TIME("disable_sleep"), 112 | .WRITE_DATA_WIDTH_A(WORD_WIDTH), 113 | .WRITE_MODE_A("read_first") 114 | ) xpm_memory_spram_inst ( 115 | .clka(clk), .ena(en), 116 | .addra(addr), 117 | .wea(real_strobe), 118 | .dina(wdata), 119 | .douta(rdata), 120 | 121 | .regcea(1), 122 | .rsta(0), 123 | .sleep(0), 124 | .injectdbiterra(0), 125 | .injectsbiterra(0) 126 | ); 127 | `endif 128 | 129 | endmodule 130 | 131 | 132 | `endif 133 | -------------------------------------------------------------------------------- /verilate/Makefile.vsim.mk: -------------------------------------------------------------------------------- 1 | VERILATOR_ROOT = /usr/share/verilator/include 2 | 3 | SV_READY = $(SV_MKFILE) 4 | 5 | VMAIN = $(BUILD_ROOT)/$(TARGET)/vmain# # build/gcc/refcpu/VTop/vmain 6 | VROOT = $(TARGET)# # refcpu/VTop 7 | VLIBRARY = $(SV_BUILD)/$(SV_PREFIX)__ALL.a# # build/gcc/verilated/refcpu/VTop/VTop__ALL.a 8 | VINCLUDE = verilate/include 9 | VSOURCE = verilate/$(SRC) 10 | 11 | CXX_BUILD = $(BUILD_ROOT)/$(TARGET)/obj# # build/gcc/refcpu/VTop/obj 12 | CXX_ROOT = $(VSOURCE)/$(VROOT) 13 | 14 | CXX_TARGET_FILES := $(wildcard $(CXX_ROOT)/*.cpp) 15 | CXX_FILES := \ 16 | $(wildcard $(VSOURCE)/*.cpp) \ 17 | $(CXX_TARGET_FILES) \ 18 | $(VERILATOR_ROOT)/verilated.cpp \ 19 | $(VERILATOR_ROOT)/verilated_fst_c.cpp 20 | # $(VERILATOR_ROOT)/verilated_threads.cpp 21 | 22 | CXX_TARGET_HEADERS := \ 23 | $(wildcard $(CXX_ROOT)/*.h) \ 24 | $(wildcard $(CXX_ROOT)/*.inl) 25 | CXX_HEADERS := \ 26 | $(wildcard $(VINCLUDE)/*.h) \ 27 | $(wildcard $(VINCLUDE)/thirdparty/*.h) 28 | 29 | CXX_MODEL_LIB = $(CXX_BUILD)/$(VSOURCE)/model.o 30 | CXX_TARGET_LIBS := $(addprefix $(CXX_BUILD)/,$(CXX_TARGET_FILES:%.cpp=%.o)) 31 | CXX_LIBS := $(addprefix $(CXX_BUILD)/,$(CXX_FILES:%.cpp=%.o)) 32 | 33 | CXX_INCLUDES = \ 34 | -I$(SV_BUILD) \ 35 | -I$(VINCLUDE) \ 36 | -I$(CXX_ROOT) \ 37 | -I$(VERILATOR_ROOT) \ 38 | -I$(VERILATOR_ROOT)/vltstd/ 39 | 40 | CXX_WARNINGS = \ 41 | -Wall -Wextra \ 42 | -Wno-aligned-new \ 43 | -Wno-sign-compare \ 44 | -Wno-unused-const-variable \ 45 | -Wno-implicit-fallthrough\ 46 | 47 | CXX_LINKS = -lz -lpthread 48 | 49 | # link to filesystem library to make legacy compilers happy. 50 | ifeq ($(USE_CLANG),1) 51 | # TODO: auto-detect libc++fs. 52 | # CXX_LINKS += -lc++fs 53 | else 54 | CXX_LINKS += -lstdc++fs 55 | endif 56 | 57 | CXXFLAGS += \ 58 | -std=c++17 -g \ 59 | $(CXX_INCLUDES) \ 60 | $(CXX_WARNINGS) \ 61 | $(CXX_EXTRA_FLAGS) 62 | # -DVL_THREADED 63 | 64 | ifeq ($(USE_CLANG),1) 65 | CXXFLAGS += -stdlib=libc++ -Wno-unknown-warning-option 66 | endif 67 | 68 | ifeq ($(VSIM_OPT),1) 69 | CXXFLAGS += -O2 -march=native -flto 70 | endif 71 | 72 | ifeq ($(VSIM_SANITIZE),1) 73 | CXXFLAGS += -fsanitize=undefined,address 74 | endif 75 | 76 | # model.h/.cpp depends on verilated model. 77 | $(CXX_MODEL_LIB): $(SV_READY) 78 | $(CXX_TARGET_LIBS): $(CXX_TARGET_HEADERS) $(SV_READY) 79 | $(CXX_LIBS): $(CXX_BUILD)/%.o : %.cpp $(CXX_HEADERS) 80 | @mkdir -p $(dir $@) 81 | $(CXX) $(CXXFLAGS) $< -c -o $@ 82 | 83 | # add customed dependencies. 84 | -include $(CXX_ROOT)/Makefile.deps.mk 85 | 86 | $(VLIBRARY): $(SV_READY) $(SV_FILES) 87 | cd $(SV_BUILD); $(MAKE) -f $(notdir $(SV_MKFILE)) CXX=$(CXX) 88 | @touch $@ 89 | 90 | $(VMAIN): $(CXX_LIBS) $(VLIBRARY) 91 | $(CXX) $(CXXFLAGS) $^ $(CXX_LINKS) -o $@ 92 | 93 | # select test file 94 | # for test1 ~ test4, use both COE file and ref text trace 95 | # for NSCSCC performance tests, only COE file for mycpu/*, but also std text trace for refcpu/VTop 96 | 97 | ifneq ($(TEST),) 98 | 99 | TEST_COE_FILE = misc/nscscc/$(TEST).coe 100 | TEST_REF_FILE = misc/nscscc/$(TEST).txt 101 | TEST_STD_FILE = misc/std/$(TEST).txt 102 | 103 | override VSIM_ARGS += -m $(TEST_COE_FILE) 104 | 105 | ifneq ($(wildcard $(TEST_REF_FILE)),) 106 | 107 | override VSIM_ARGS += -r $(TEST_REF_FILE) 108 | 109 | else ifneq ($(wildcard $(TEST_STD_FILE)),) 110 | 111 | # only add std trace for RefCPU 112 | ifeq ($(TARGET),refcpu/VTop) 113 | override VSIM_ARGS += -r $(TEST_STD_FILE) --force-diff 114 | endif # ifeq 115 | 116 | # else 117 | 118 | # override VSIM_ARGS += -r '' 119 | 120 | endif # ifneq 121 | 122 | endif # ifneq ($(TEST),) 123 | 124 | ifneq ($(FST),) 125 | override VSIM_ARGS += -f $(FST) 126 | endif 127 | 128 | ifeq ($(REFERENCE_CACHE), 1) 129 | CXXFLAGS += -DREFERENCE_CACHE 130 | endif 131 | 132 | .PHONY: vpty vbuild vsim vsim-gdb 133 | 134 | vpty: 135 | socat pty,link=build/vpty,raw,echo=0 pty,link=build/pty,raw,echo=0 136 | 137 | vbuild: $(VMAIN) 138 | 139 | vsim: $(VMAIN) 140 | ./$(VMAIN) $(VSIM_ARGS) 141 | 142 | vsim-gdb: $(VMAIN) 143 | @echo Command line arguments: $(VSIM_ARGS) 144 | @gdb -q ./$(VMAIN) 145 | -------------------------------------------------------------------------------- /vsrc/ram/RAM_TrueDualPort.sv: -------------------------------------------------------------------------------- 1 | `ifndef __RAM_TrueDualPort_SV 2 | `define __RAM_TrueDualPort_SV 3 | 4 | module RAM_TrueDualPort #( 5 | parameter int ADDR_WIDTH = 17, 6 | parameter int DATA_WIDTH = 64, 7 | parameter int BYTE_WIDTH = 8, 8 | parameter MEM_TYPE = 0, 9 | parameter READ_LATENCY = 2, 10 | 11 | localparam WORD_WIDTH = DATA_WIDTH, 12 | localparam NUM_WORDS = 2 ** ADDR_WIDTH, 13 | localparam NUM_BITS = NUM_WORDS * DATA_WIDTH, 14 | localparam NUM_BYTES = NUM_BITS / BYTE_WIDTH, 15 | localparam BYTES_PER_WORD = DATA_WIDTH / BYTE_WIDTH, 16 | // types 17 | localparam type raddr_t = logic[ADDR_WIDTH-1:0], 18 | localparam type rstrobe_t = logic[BYTES_PER_WORD-1:0], 19 | localparam type rword_t = logic[WORD_WIDTH-1:0], 20 | localparam type rbundle_t = logic [BYTES_PER_WORD-1:0][BYTE_WIDTH-1:0], 21 | localparam type rview_t = union packed { 22 | rword_t word; 23 | rbundle_t lanes; 24 | } 25 | ) ( 26 | input logic clk, en_1, en_2, 27 | 28 | input raddr_t addr_1, addr_2, 29 | input rstrobe_t strobe_1, strobe_2, 30 | input rview_t wdata_1, wdata_2, 31 | output rword_t rdata_1, rdata_2 32 | ); 33 | 34 | `ifdef VERILATOR 35 | /* verilator tracing_on */ 36 | rview_t mem [NUM_WORDS-1:0]; 37 | initial begin 38 | for (int i = 0; i < NUM_WORDS; i++) begin 39 | mem[i] = '0; 40 | end 41 | 42 | end 43 | if (READ_LATENCY == 0) begin 44 | assign rdata_1 = mem[addr_1]; 45 | assign rdata_2 = mem[addr_2]; 46 | end else begin 47 | rword_t reads_1 [READ_LATENCY-1:0]; 48 | always_ff @(posedge clk) begin 49 | reads_1[0] <= mem[addr_1]; 50 | end 51 | 52 | for (genvar i = 1; i < READ_LATENCY; i++) begin 53 | always_ff @(posedge clk) begin 54 | reads_1[i] <= reads_1[i-1]; 55 | end 56 | end 57 | assign rdata_1 = reads_1[READ_LATENCY-1]; 58 | 59 | rword_t reads_2 [READ_LATENCY-1:0]; 60 | always_ff @(posedge clk) begin 61 | reads_2[0] <= mem[addr_2]; 62 | end 63 | 64 | for (genvar i = 1; i < READ_LATENCY; i++) begin 65 | always_ff @(posedge clk) begin 66 | reads_2[i] <= reads_2[i-1]; 67 | end 68 | end 69 | assign rdata_2 = reads_2[READ_LATENCY-1]; 70 | 71 | end 72 | 73 | always_ff @(posedge clk) begin 74 | if (en_1) 75 | for (int i = 0; i < BYTES_PER_WORD; i++) 76 | if (strobe_1[i]) 77 | mem[addr_1].lanes[i] <= wdata_1.lanes[i]; 78 | if (en_2) 79 | for (int i = 0; i < BYTES_PER_WORD; i++) 80 | if (strobe_2[i]) 81 | mem[addr_2].lanes[i] <= wdata_2.lanes[i]; 82 | end 83 | /* verilator tracing_on */ 84 | `else 85 | xpm_memory_tdpram #( 86 | .ADDR_WIDTH_A(ADDR_WIDTH), 87 | .ADDR_WIDTH_B(ADDR_WIDTH), 88 | .AUTO_SLEEP_TIME(0), 89 | .BYTE_WRITE_WIDTH_A(BYTE_WIDTH), // byte-write enable 90 | .BYTE_WRITE_WIDTH_B(BYTE_WIDTH), 91 | // .CASCADE_HEIGHT(0), 92 | .CLOCKING_MODE("common_clock"), 93 | .ECC_MODE("no_ecc"), 94 | .MEMORY_INIT_FILE("none"), 95 | .MEMORY_INIT_PARAM("0"), 96 | .MEMORY_OPTIMIZATION("true"), 97 | .MEMORY_PRIMITIVE(MEM_TYPE), 98 | .MEMORY_SIZE(NUM_BITS), // in bits 99 | .MESSAGE_CONTROL(0), // disable message reporting 100 | .READ_DATA_WIDTH_A(DATA_WIDTH), 101 | .READ_DATA_WIDTH_B(DATA_WIDTH), 102 | .READ_LATENCY_A(READ_LATENCY), 103 | .READ_LATENCY_B(READ_LATENCY), 104 | .READ_RESET_VALUE_A(0), 105 | .READ_RESET_VALUE_B(0), 106 | .RST_MODE_A("SYNC"), 107 | .RST_MODE_B("SYNC"), 108 | // .SIM_ASSERT_CHK(1), 109 | .USE_EMBEDDED_CONSTRAINT(0), 110 | .USE_MEM_INIT(1), 111 | .WAKEUP_TIME("disable_sleep"), 112 | .WRITE_DATA_WIDTH_A(DATA_WIDTH), 113 | .WRITE_DATA_WIDTH_B(DATA_WIDTH), 114 | .WRITE_MODE_A("no_change"), 115 | .WRITE_MODE_B("no_change") 116 | ) xpm_memory_tdpram_inst ( 117 | .sleep(0), 118 | .clka(clk), .clkb(clk), // use the same clock 119 | .ena(en_1), .enb(en_2), 120 | .rsta(reset), .rstb(reset), 121 | .regcea(1), .regceb(1), 122 | .injectdbiterra(0), 123 | .injectdbiterrb(0), 124 | .injectsbiterra(0), 125 | .injectsbiterrb(0), 126 | 127 | // port 1/a 128 | .wea(strobe_1), 129 | .addra(addr_1), 130 | .dina(wdata_1), 131 | .douta(rdata_1), 132 | 133 | // port 2/b 134 | .web(strobe_2), 135 | .addrb(addr_2), 136 | .dinb(wdata_2), 137 | .doutb(rdata_2) 138 | ); 139 | 140 | `endif 141 | 142 | endmodule 143 | 144 | 145 | `endif 146 | -------------------------------------------------------------------------------- /verilate/include/runner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "memory.h" 4 | 5 | #include 6 | 7 | #include "thirdparty/CLI11.hpp" 8 | 9 | template 10 | class ProgramRunner final { 11 | public: 12 | struct { 13 | std::string default_memfile = "misc/nscscc/func_test.coe"; 14 | std::string default_ref_trace = "misc/nscscc/func_test.txt"; 15 | std::string default_pty = "build/vpty"; 16 | std::string default_fst_folder = "build"; 17 | 18 | std::string fst_trace_path = ""; 19 | std::string text_trace_path = ""; 20 | std::string memfile_path = default_memfile; 21 | std::string ref_trace_path = ""; 22 | std::string pty_path = default_pty; 23 | std::string fst_folder = default_fst_folder; 24 | int num_workers = 1; 25 | bool status_enable = true; 26 | bool debug_enable = false; 27 | float p_disable = 0.0f; 28 | bool force_diff = false; 29 | } args; 30 | 31 | void no_init_memory() 32 | { 33 | _init_memory = false; 34 | } 35 | 36 | void no_init_text_trace() 37 | { 38 | _init_text_trace = false; 39 | } 40 | 41 | void no_init_fst_trace() 42 | { 43 | _init_fst_trace = false; 44 | } 45 | 46 | void no_soc() 47 | { 48 | _init_soc = false; 49 | } 50 | 51 | void no_pty() 52 | { 53 | _init_pty = false; 54 | } 55 | 56 | void with_fst_folder() 57 | { 58 | _set_fst_folder = true; 59 | } 60 | 61 | void with_workers() 62 | { 63 | _set_num_workers = true; 64 | } 65 | 66 | int main(int argc, char *argv[]) 67 | { 68 | auto app = CLI::App(); 69 | 70 | if (_init_memory) 71 | app.add_option("-m,--memfile", args.memfile_path, "File path of memory initialization file."); 72 | if (_init_fst_trace) 73 | app.add_option("-f,--fst-trace", args.fst_trace_path, "File path to save FST trace."); 74 | if (_init_text_trace) { 75 | app.add_option("-t,--text-trace", args.text_trace_path, "File path to save text trace."); 76 | app.add_option("-r,--ref-trace", args.ref_trace_path, "File path of reference text trace."); 77 | } 78 | if (_init_pty) 79 | app.add_option("-y,--pty", args.pty_path, "Path to pesudo-terminal opened by socat."); 80 | if (_set_fst_folder) 81 | app.add_option("-d,--fst-folder", args.fst_folder, "Folder to put FST trace files."); 82 | if (_set_num_workers) 83 | app.add_option("-j,--num-workers", args.num_workers, "Number of workers to spawn."); 84 | 85 | app.add_flag("--status,!--no-status", args.status_enable, "Show status line."); 86 | app.add_flag("--debug,!--no-debug", args.debug_enable, "Show debug messages."); 87 | app.add_option("-p,--p-disable", args.p_disable, "Probability that CBusDevice pauses in a cycle. Set to 0 to disable random delay."); 88 | app.add_flag("--force-diff,!--no-force-diff", args.force_diff, "Ignore OPEN_TRACE bit from Confreg."); 89 | 90 | CLI11_PARSE(app, argc, argv); 91 | if (args.memfile_path == args.default_memfile && args.ref_trace_path.empty()) 92 | args.ref_trace_path = args.default_ref_trace; 93 | 94 | enable_logging(); 95 | enable_status_line(args.status_enable); 96 | enable_debugging(args.debug_enable); 97 | 98 | top = std::make_unique(); 99 | top->p_disable = args.p_disable; 100 | top->force_diff = args.force_diff; 101 | 102 | ByteSeq data; 103 | if (_init_memory) 104 | data = parse_memory_file(args.memfile_path); 105 | auto mem = std::make_shared(MEMORY_SIZE, data); 106 | 107 | if (_init_soc) 108 | top->install_soc(std::move(mem)); 109 | else 110 | top->install_memory(std::move(mem)); 111 | 112 | if (_init_fst_trace && !args.fst_trace_path.empty()) 113 | top->start_fst_trace(args.fst_trace_path); 114 | if (_init_text_trace && !args.text_trace_path.empty()) 115 | top->start_text_trace(args.text_trace_path); 116 | if (_init_pty && !args.pty_path.empty()) 117 | top->open_pty(args.pty_path); 118 | if (_set_fst_folder) 119 | top->set_fst_folder(args.fst_folder); 120 | 121 | #if ICS_ON_LINUX 122 | if (_set_num_workers) 123 | top->set_num_workers(args.num_workers); 124 | #endif 125 | 126 | top->run(); 127 | 128 | // destruct verilated model first to prevent segmentation fault. 129 | delete top.release(); 130 | 131 | return 0; 132 | } 133 | 134 | protected: 135 | std::unique_ptr top = nullptr; 136 | 137 | bool _init_memory = true; 138 | bool _init_text_trace = true; 139 | bool _init_fst_trace = true; 140 | bool _init_soc = true; 141 | bool _init_pty = true; 142 | bool _set_num_workers = false; 143 | bool _set_fst_folder = false; 144 | }; 145 | -------------------------------------------------------------------------------- /verilate/vsrc/model.cpp: -------------------------------------------------------------------------------- 1 | #include "model.h" 2 | 3 | ModelBase::~ModelBase() 4 | { 5 | if (_fst_avail()) 6 | stop_fst_trace(); 7 | if (_text_avail()) 8 | stop_text_trace(); 9 | } 10 | 11 | void ModelBase::install_soc(const std::shared_ptr &mem) 12 | { 13 | asserts(!_memory_installed, "memory has been already installed"); 14 | 15 | con = std::make_shared(); 16 | std::vector layout = { 17 | {0xfff00000, 0x1fc00000, mem, [](addr_t addr) { return addr - 0x1fc00000; }}, 18 | {0xffff0000, 0x1faf0000, con, identity_fn}, 19 | {0x00000000, 0x00000000, mem, identity_fn}, 20 | }; 21 | auto router = std::make_shared(layout); 22 | dev = std::make_shared(router, p_disable); 23 | 24 | _memory_installed = true; 25 | } 26 | 27 | void ModelBase::install_memory(const std::shared_ptr &mem) 28 | { 29 | asserts(!_memory_installed, "memory has been already installed"); 30 | 31 | dev = std::make_shared(mem, p_disable); 32 | 33 | _memory_installed = true; 34 | } 35 | 36 | void ModelBase::remove_memory() 37 | { 38 | asserts(_memory_installed, "no memory installed"); 39 | dev = nullptr; 40 | con = nullptr; 41 | _memory_installed = false; 42 | } 43 | 44 | void ModelBase::enable_fst_trace(bool enable) 45 | { 46 | _fst_enabled = enable; 47 | } 48 | 49 | void ModelBase::start_fst_trace(const std::string &path) 50 | { 51 | asserts(!_fst_avail(), "FST trace has been already opened"); 52 | 53 | if (_fst_folder.empty()) 54 | _fst_path = path; 55 | else 56 | _fst_path = _fst_folder + "/" + path; 57 | 58 | _fst_tfp = new VerilatedFstC; 59 | _fst_count = 0; 60 | trace(_fst_tfp, FST_TRACE_MAX_DEPTH); 61 | _fst_tfp->open(_fst_path.data()); 62 | asserts(_fst_tfp->isOpen(), "failed to open \"%s\"", _fst_path.data()); 63 | 64 | enable_fst_trace(); 65 | fst_dump(+0); 66 | } 67 | 68 | void ModelBase::stop_fst_trace() 69 | { 70 | if (_fst_avail()) { 71 | info("\"%s\": stop @%zu\n", _fst_path.data(), fst_time()); 72 | eval(); 73 | 74 | enable_fst_trace(); 75 | fst_dump(+FST_TRACE_TIME_SCALE); 76 | 77 | _fst_tfp->flush(); 78 | _fst_tfp->close(); 79 | _fst_tfp = nullptr; 80 | } 81 | } 82 | 83 | auto ModelBase::fst_time() -> size_t 84 | { 85 | return _fst_count * FST_TRACE_TIME_SCALE; 86 | } 87 | 88 | void ModelBase::fst_advance(size_t incr) 89 | { 90 | if (_fst_enabled) 91 | _fst_count += incr; 92 | } 93 | 94 | void ModelBase::fst_dump(size_t offset) 95 | { 96 | if (_fst_enabled && _fst_avail()) 97 | _fst_tfp->dump(static_cast(fst_time() + offset)); 98 | } 99 | 100 | void ModelBase::start_text_trace(const std::string &path) 101 | { 102 | asserts(!_text_avail(), "text trace writer has been already opened"); 103 | _text_tfp = fopen(path.data(), "w"); 104 | asserts(_text_avail(), "failed to open \"%s\"", path.data()); 105 | } 106 | 107 | void ModelBase::stop_text_trace() 108 | { 109 | if (_text_avail()) { 110 | fflush(_text_tfp); 111 | fclose(_text_tfp); 112 | _text_tfp = nullptr; 113 | } 114 | } 115 | 116 | void ModelBase::text_dump(bool enable, addr_t pc, int id, word_t value) 117 | { 118 | if (!_text_avail()) 119 | return; 120 | 121 | char buf[TEXT_TRACE_MAX_LEN]; 122 | snprintf(buf, TEXT_TRACE_MAX_LEN, "%01x %08x %02x %08x", enable, pc, id, value); 123 | 124 | if (_text_avail()) 125 | fprintf(_text_tfp, "%s\n", buf); 126 | } 127 | 128 | void ModelBase::ticks(int count) 129 | { 130 | while (count >= 1) { 131 | tick(); 132 | count--; 133 | } 134 | } 135 | 136 | void ModelBase::print_num_monitor(int num) 137 | { 138 | info(BLUE "(info)" RESET " #%u completed.\n", num); 139 | } 140 | 141 | void ModelBase::checkout_confreg() 142 | { 143 | if (con->monitor_enabled()) { 144 | int num = con->get_current_num(); 145 | int ack = con->get_acked_num(); 146 | 147 | if (_current_num != num) { 148 | asserts(_current_num + 1 == num, "#%d not passed. num=%d", _current_num + 1, num); 149 | info(BLUE "(info)" RESET " #%d completed.\n", num); 150 | asserts(ack == num, "#%d not passed. num=%d, ack=%d", _current_num + 1, num, ack); 151 | _current_num = num; 152 | } 153 | } 154 | 155 | if (con->has_char() && con->get_char() <= 0x7f) 156 | notify_char(con->get_char()); 157 | } 158 | 159 | void ModelBase::open_pty(const std::string &path) 160 | { 161 | if (con) 162 | con->uart_open_pty(path); 163 | } 164 | 165 | void ModelBase::set_num_workers(int n_workers) 166 | { 167 | _num_workers = n_workers; 168 | } 169 | 170 | void ModelBase::set_fst_folder(const std::string &folder) 171 | { 172 | _fst_folder = folder; 173 | } 174 | -------------------------------------------------------------------------------- /vsrc/pipeline/core.sv: -------------------------------------------------------------------------------- 1 | `ifndef __CORE_SV 2 | `define __CORE_SV 3 | `ifdef VERILATOR 4 | `include "include/common.sv" 5 | `include "pipeline/regfile/regfile.sv" 6 | 7 | `else 8 | 9 | `endif 10 | 11 | module core 12 | import common::*;( 13 | input logic clk, reset, 14 | output ibus_req_t ireq, 15 | input ibus_resp_t iresp, 16 | output dbus_req_t dreq, 17 | input dbus_resp_t dresp, 18 | input logic trint, swint, exint 19 | ); 20 | /* TODO: Add your pipeline here. */ 21 | 22 | 23 | regfile regfile( 24 | .clk, .reset, 25 | .ra1(), 26 | .ra2(), 27 | .rd1(), 28 | .rd2(), 29 | .wvalid(), 30 | .wa(), 31 | .wd() 32 | ); 33 | 34 | `ifdef VERILATOR 35 | DifftestInstrCommit DifftestInstrCommit( 36 | .clock (clk), 37 | .coreid (0), 38 | .index (0), 39 | .valid (0), 40 | .pc (0), 41 | .instr (0), 42 | .skip (0), 43 | .isRVC (0), 44 | .scFailed (0), 45 | .wen (0), 46 | .wdest (0), 47 | .wdata (0) 48 | ); 49 | 50 | DifftestArchIntRegState DifftestArchIntRegState ( 51 | .clock (clk), 52 | .coreid (0), 53 | .gpr_0 (regfile.regs_nxt[0]), 54 | .gpr_1 (regfile.regs_nxt[1]), 55 | .gpr_2 (regfile.regs_nxt[2]), 56 | .gpr_3 (regfile.regs_nxt[3]), 57 | .gpr_4 (regfile.regs_nxt[4]), 58 | .gpr_5 (regfile.regs_nxt[5]), 59 | .gpr_6 (regfile.regs_nxt[6]), 60 | .gpr_7 (regfile.regs_nxt[7]), 61 | .gpr_8 (regfile.regs_nxt[8]), 62 | .gpr_9 (regfile.regs_nxt[9]), 63 | .gpr_10 (regfile.regs_nxt[10]), 64 | .gpr_11 (regfile.regs_nxt[11]), 65 | .gpr_12 (regfile.regs_nxt[12]), 66 | .gpr_13 (regfile.regs_nxt[13]), 67 | .gpr_14 (regfile.regs_nxt[14]), 68 | .gpr_15 (regfile.regs_nxt[15]), 69 | .gpr_16 (regfile.regs_nxt[16]), 70 | .gpr_17 (regfile.regs_nxt[17]), 71 | .gpr_18 (regfile.regs_nxt[18]), 72 | .gpr_19 (regfile.regs_nxt[19]), 73 | .gpr_20 (regfile.regs_nxt[20]), 74 | .gpr_21 (regfile.regs_nxt[21]), 75 | .gpr_22 (regfile.regs_nxt[22]), 76 | .gpr_23 (regfile.regs_nxt[23]), 77 | .gpr_24 (regfile.regs_nxt[24]), 78 | .gpr_25 (regfile.regs_nxt[25]), 79 | .gpr_26 (regfile.regs_nxt[26]), 80 | .gpr_27 (regfile.regs_nxt[27]), 81 | .gpr_28 (regfile.regs_nxt[28]), 82 | .gpr_29 (regfile.regs_nxt[29]), 83 | .gpr_30 (regfile.regs_nxt[30]), 84 | .gpr_31 (regfile.regs_nxt[31]) 85 | ); 86 | 87 | DifftestTrapEvent DifftestTrapEvent( 88 | .clock (clk), 89 | .coreid (0), 90 | .valid (0), 91 | .code (0), 92 | .pc (0), 93 | .cycleCnt (0), 94 | .instrCnt (0) 95 | ); 96 | 97 | DifftestCSRState DifftestCSRState( 98 | .clock (clk), 99 | .coreid (0), 100 | .priviledgeMode (3), 101 | .mstatus (0), 102 | .sstatus (0 /* mstatus & 64'h800000030001e000 */), 103 | .mepc (0), 104 | .sepc (0), 105 | .mtval (0), 106 | .stval (0), 107 | .mtvec (0), 108 | .stvec (0), 109 | .mcause (0), 110 | .scause (0), 111 | .satp (0), 112 | .mip (0), 113 | .mie (0), 114 | .mscratch (0), 115 | .sscratch (0), 116 | .mideleg (0), 117 | .medeleg (0) 118 | ); 119 | 120 | DifftestArchFpRegState DifftestArchFpRegState( 121 | .clock (clk), 122 | .coreid (0), 123 | .fpr_0 (0), 124 | .fpr_1 (0), 125 | .fpr_2 (0), 126 | .fpr_3 (0), 127 | .fpr_4 (0), 128 | .fpr_5 (0), 129 | .fpr_6 (0), 130 | .fpr_7 (0), 131 | .fpr_8 (0), 132 | .fpr_9 (0), 133 | .fpr_10 (0), 134 | .fpr_11 (0), 135 | .fpr_12 (0), 136 | .fpr_13 (0), 137 | .fpr_14 (0), 138 | .fpr_15 (0), 139 | .fpr_16 (0), 140 | .fpr_17 (0), 141 | .fpr_18 (0), 142 | .fpr_19 (0), 143 | .fpr_20 (0), 144 | .fpr_21 (0), 145 | .fpr_22 (0), 146 | .fpr_23 (0), 147 | .fpr_24 (0), 148 | .fpr_25 (0), 149 | .fpr_26 (0), 150 | .fpr_27 (0), 151 | .fpr_28 (0), 152 | .fpr_29 (0), 153 | .fpr_30 (0), 154 | .fpr_31 (0) 155 | ); 156 | 157 | `endif 158 | endmodule 159 | `endif -------------------------------------------------------------------------------- /verilate/vsrc/confreg.cpp: -------------------------------------------------------------------------------- 1 | #include "confreg.h" 2 | 3 | void Confreg::reset() 4 | { 5 | ctx.reset(); 6 | ctx0.reset(); 7 | _uart_reset(); 8 | 9 | mem.clear(); 10 | mem[CR0] = 0x00000000; 11 | mem[CR1] = 0x00000000; 12 | mem[CR2] = 0x00000000; 13 | mem[CR3] = 0x00000000; 14 | mem[CR4] = 0x00000000; 15 | mem[CR5] = 0x00000000; 16 | mem[CR6] = 0x00000000; 17 | mem[CR7] = 0x00000000; 18 | mem[LED] = 0x0000ffff; 19 | mem[LED_RG0] = 0x00000000; 20 | mem[LED_RG1] = 0x00000000; 21 | mem[NUM] = 0x00000000; 22 | mem[SWITCH] = 0x000000ff; 23 | mem[BTN_KEY] = 0x00000000; 24 | mem[BTN_STEP] = 0x00000000; 25 | mem[SW_INTER] = 0x0000aaaa; 26 | mem[TIMER] = 0x00000000; 27 | mem[IO_SIMU] = 0x00000000; 28 | mem[VIRTUAL_UART] = 0x00000000; 29 | mem[SIMU_FLAG] = 0xffffffff; 30 | mem[OPEN_TRACE] = 0x00000001; 31 | mem[NUM_MONITOR] = 0x00000001; 32 | } 33 | 34 | auto Confreg::load(addr_t addr) -> word_t 35 | { 36 | addr &= ADDR_MASK; 37 | 38 | switch (addr) { 39 | case UART_LSR: 40 | return _uart_has_char(); 41 | case UART_RXD: 42 | return _uart_get_char(); 43 | } 44 | 45 | auto it = mem.find(addr); 46 | if (it == mem.end()) { 47 | warn("CONFREG: load: ignored unknown destination 0x%04x.\n", addr); 48 | return 0; 49 | } else 50 | return it->second; 51 | } 52 | 53 | static auto swap_bytes(word_t data) -> word_t 54 | { 55 | return ((data >> 16) & 0xffff) | ((data << 16) & 0xffff0000); 56 | } 57 | 58 | // NOTE: confreg often ignores mask. 59 | void Confreg::store(addr_t addr, word_t data, word_t mask) 60 | { 61 | addr &= ADDR_MASK; 62 | 63 | switch (addr) { 64 | case UART_LSR: 65 | panic("attempt to write UART's read-only register LSR"); 66 | return; 67 | case UART_TXD: 68 | asserts(mask == 0xff, "UART device only accepts byte writes") 69 | _uart_put_char(data & 0xff); 70 | return; 71 | 72 | case VIRTUAL_UART: 73 | data &= 0xff; 74 | ctx.vuart_written = true; 75 | ctx.vuart_data = data; 76 | break; 77 | case NUM: 78 | ctx.v_num = data; 79 | break; 80 | case SIMU_FLAG: 81 | data = 0xffffffff; 82 | break; 83 | case IO_SIMU: 84 | data = swap_bytes(data); 85 | break; 86 | case OPEN_TRACE: 87 | data = bool(data); 88 | ctx.v_open_trace = data; 89 | break; 90 | case NUM_MONITOR: 91 | data &= 1; 92 | ctx.v_num_monitor = data; 93 | break; 94 | } 95 | 96 | changes.push_back({ addr, data }); 97 | } 98 | 99 | void Confreg::sync() 100 | { 101 | // update UART 102 | if (ctx.uart_fetched) { 103 | internal_assert(!uart.ififo.empty(), "UART input FIFO should not be empty"); 104 | std::lock_guard guard(uart.lock); 105 | uchar c = uart.ififo.front(); 106 | log_debug("CONFREG: uart: get: %u (0x%x)\n", c, c); 107 | uart.ififo.pop_front(); 108 | } 109 | if (ctx.uart_written && uart.opty) { 110 | uchar c = ctx.uart_data; 111 | log_debug("CONFREG: uart: put: %u (0x%x)\n", c, c); 112 | fputc(ctx.uart_data, uart.opty); 113 | fflush(uart.opty); 114 | } 115 | 116 | // reset ctx 117 | ctx0 = ctx; 118 | ctx.uart_avail = !uart.ififo.empty(); 119 | ctx.uart_written = false; 120 | ctx.uart_fetched = false; 121 | ctx.vuart_written = false; 122 | 123 | // commit to memory 124 | for (auto [addr, data] : changes) { 125 | auto it = mem.find(addr); 126 | if (it == mem.end()) 127 | warn("CONFREG: store: ignored unknown destination 0x%04x.\n", addr); 128 | else 129 | it->second = data; 130 | } 131 | changes.clear(); 132 | 133 | // increment timer counter 134 | mem[TIMER]++; 135 | } 136 | 137 | void Confreg::uart_open_pty(const std::string &path) 138 | { 139 | asserts(!uart.ipty, "already connected to UART"); 140 | asserts(!uart.opty, "already connected to UART"); 141 | 142 | uart.ipty = fopen(path.data(), "r"); 143 | if (!uart.ipty) 144 | return; 145 | 146 | // to prevent create a new file during open opty, 147 | // we should open ipty first. 148 | uart.opty = fopen(path.data(), "w"); 149 | if (!uart.opty) 150 | return; 151 | 152 | info("CONFREG: connected to pty \"%s\".\n", path.data()); 153 | 154 | // fetch UART input in the background 155 | uart.worker = ThreadWorker::loop([this] { 156 | auto c = fgetc(uart.ipty); // fgetc will block util there's a new char. 157 | if (c != EOF) { 158 | std::lock_guard guard(uart.lock); 159 | uart.ififo.push_back(c); 160 | } 161 | }, [] {}, [this] { 162 | if (uart.ipty) { 163 | fclose(uart.ipty); 164 | uart.ipty = nullptr; 165 | } 166 | }); 167 | } 168 | 169 | void Confreg::_uart_close_pty() 170 | { 171 | uart.worker.stop(); 172 | 173 | if (uart.opty) { 174 | fclose(uart.opty); 175 | uart.opty = nullptr; 176 | } 177 | 178 | // workers's fgetc will also block fclose... 179 | // we have to let the worker itself to close ipty... 180 | // fclose(uart.ipty); 181 | } 182 | 183 | void Confreg::_uart_reset() 184 | { 185 | std::lock_guard guard(uart.lock); 186 | uart.ififo.clear(); 187 | } 188 | 189 | auto Confreg::_uart_has_char() -> bool 190 | { 191 | return ctx.uart_avail; 192 | } 193 | 194 | auto Confreg::_uart_get_char() -> uchar 195 | { 196 | if (ctx.uart_avail) { 197 | ctx.uart_fetched = true; 198 | return uart.ififo.front(); 199 | } else 200 | return 0; 201 | } 202 | 203 | void Confreg::_uart_put_char(uchar c) 204 | { 205 | ctx.uart_written = true; 206 | ctx.uart_data = c; 207 | } 208 | -------------------------------------------------------------------------------- /vsrc/util/CBusToAXI.sv: -------------------------------------------------------------------------------- 1 | `ifndef __CBUSTOAXI_SV 2 | `define __CBUSTOAXI_SV 3 | 4 | `ifdef VERILATOR 5 | `include "include/common.sv" 6 | `endif 7 | /** 8 | * interconnect with cache bus & AXI. 9 | * 10 | * this implementation is not efficient, since 11 | * it adds one cycle lantency to all requests. 12 | * 13 | * this implementation also serves as a spec for cache bus, 14 | * which shows the corresponding constructs in AXI. 15 | * 16 | * NOTE: assume the widths of cache Bus & AXI are 32 bits. 17 | */ 18 | module CBusToAXI 19 | import common::*;( 20 | input logic aclk, areset, 21 | 22 | output logic [3 :0] arid, 23 | output logic [63:0] araddr, 24 | output logic [7 :0] arlen, 25 | output logic [2 :0] arsize, 26 | output logic [1 :0] arburst, 27 | output logic arlock, 28 | output logic [3 :0] arcache, 29 | output logic [2 :0] arprot, 30 | output logic arvalid, 31 | input logic arready, 32 | input logic [3 :0] rid, 33 | input logic [63:0] rdata, 34 | input logic [1 :0] rresp, 35 | input logic rlast, 36 | input logic rvalid, 37 | output logic rready, 38 | output logic [3 :0] awid, 39 | output logic [63:0] awaddr, 40 | output logic [7 :0] awlen, 41 | output logic [2 :0] awsize, 42 | output logic [1 :0] awburst, 43 | output logic awlock, 44 | output logic [3 :0] awcache, 45 | output logic [2 :0] awprot, 46 | output logic awvalid, 47 | input logic awready, 48 | output logic [63:0] wdata, 49 | output logic [7 :0] wstrb, 50 | output logic wlast, 51 | output logic wvalid, 52 | input logic wready, 53 | input logic [3 :0] bid, 54 | input logic [1 :0] bresp, 55 | input logic bvalid, 56 | output logic bready, 57 | 58 | input cbus_req_t creq, 59 | output cbus_resp_t cresp 60 | ); 61 | localparam int AR = 4; 62 | localparam int R = 3; 63 | localparam int AW = 2; 64 | localparam int W = 1; 65 | localparam int B = 0; 66 | typedef logic [4:0] busy_t; 67 | 68 | // process pending request 69 | busy_t in_issue, next_issue; 70 | assign next_issue = creq.is_write ? 5'b00111 : 5'b11000; 71 | 72 | // check ongoing request 73 | cbus_req_t saved_req; 74 | mlen_t count; 75 | logic is_last; 76 | assign is_last = count == 0; 77 | 78 | // interactions with AXI 79 | logic busy; 80 | busy_t handshake, ended, remain; 81 | 82 | assign busy = |in_issue; 83 | assign handshake = { 84 | in_issue[AR] && arready, 85 | in_issue[R ] && rvalid, 86 | in_issue[AW] && awready, 87 | in_issue[W ] && wready, 88 | in_issue[B ] && bvalid 89 | }; 90 | assign ended = handshake & {1'b1, rlast, 1'b1, wlast, 1'b1}; 91 | assign remain = in_issue ^ ended; 92 | 93 | // cache bus driver 94 | logic ready; 95 | assign ready = handshake[W] || handshake[R]; 96 | assign cresp.ready = ready; 97 | assign cresp.last = ready && (!in_issue[R] || rlast) && is_last; 98 | assign cresp.data = rdata; 99 | 100 | // AXI driver 101 | always_comb begin 102 | { 103 | arid, araddr, arlen, arsize, 104 | arburst, arlock, arcache, arprot, 105 | arvalid, /*arready,*/ 106 | /*rid, rdata, rresp, rlast,*/ 107 | /*rvalid,*/ rready, 108 | awid, awaddr, awlen, awsize, 109 | awburst, awlock, awcache, awprot, 110 | awvalid, /*awready,*/ 111 | wdata, wstrb, wlast, 112 | wvalid, /*wready,*/ 113 | /*bid, bresp, bvalid,*/ bready 114 | } = 0; 115 | 116 | if (in_issue[AR]) begin 117 | arvalid = 1; 118 | araddr = saved_req.addr; 119 | arlen = saved_req.len; 120 | arsize = saved_req.size; 121 | arburst = AXI_BURST_WRAP; 122 | end 123 | 124 | if (in_issue[R]) begin 125 | rready = 1; 126 | end 127 | 128 | if (in_issue[AW]) begin 129 | awvalid = 1; 130 | awaddr = saved_req.addr; 131 | awlen = saved_req.len; 132 | awsize = saved_req.size; 133 | awburst = AXI_BURST_WRAP; 134 | end 135 | 136 | if (in_issue[W]) begin 137 | wvalid = 1; 138 | wdata = creq.data; 139 | wstrb = creq.strobe; 140 | wlast = is_last; 141 | end 142 | 143 | if (in_issue[B]) begin 144 | bready = 1; 145 | end 146 | end 147 | 148 | always_ff @(posedge aclk) 149 | if (~areset) begin 150 | if (busy) begin 151 | if (cresp.ready && !cresp.last) 152 | count <= mlen_t'(count - mlen_t'(1)); 153 | 154 | in_issue <= remain; 155 | end else begin 156 | if (creq.valid) 157 | in_issue <= next_issue; 158 | 159 | saved_req <= creq; 160 | count <= creq.len; 161 | end 162 | end else begin 163 | in_issue <= 0; 164 | end 165 | 166 | `UNUSED_OK({ 167 | saved_req.valid, saved_req.is_write, 168 | saved_req.data, saved_req.strobe, 169 | rid, rresp, bid, bresp 170 | }); 171 | endmodule 172 | 173 | 174 | 175 | `endif 176 | -------------------------------------------------------------------------------- /vivado/src/device.sv: -------------------------------------------------------------------------------- 1 | `include "device.svh" 2 | 3 | module device #( 4 | parameter logic SIMULATION = 1'b0 5 | )( 6 | input logic clk, reset, 7 | input logic cpu_clk, 8 | 9 | /* From Board */ 10 | output logic [3:0] led, 11 | input logic [3:0] sw, 12 | output logic tx, 13 | 14 | /* From CPU */ 15 | input logic valid, 16 | input logic [63:0] addr, 17 | input logic wvalid, 18 | input logic [7:0] size, 19 | input logic [63:0] wdata, 20 | output logic [63:0] rdata, 21 | 22 | output logic ready, 23 | output logic last 24 | ); 25 | /* Counter */ 26 | logic [63:0] cnter, cnter1; 27 | 28 | always_ff @(posedge clk) begin 29 | if (reset) {cnter, cnter1} <= '0; 30 | else begin 31 | cnter1 <= cnter1 + 1; 32 | if (cnter1 == 100) begin 33 | cnter1 <= '0; 34 | cnter <= cnter + 1; 35 | end 36 | end 37 | end 38 | 39 | /* Switch */ 40 | logic [3:0] switch; 41 | always_ff @(posedge clk) begin 42 | switch <= sw; 43 | end 44 | 45 | always_comb begin 46 | rdata = 'x; 47 | unique case(addr) 48 | SW_ADDR: begin 49 | unique case(switch) 50 | 4'd0: begin 51 | rdata = 64'd31; 52 | end 53 | 4'd1: begin 54 | rdata = 64'd1; 55 | end 56 | 4'd2: begin 57 | rdata = 64'd2; 58 | end 59 | 4'd3: begin 60 | rdata = 64'd4; 61 | end 62 | 4'd4: begin 63 | rdata = 64'd8; 64 | end 65 | 4'd5: begin 66 | rdata = 64'd16; 67 | end 68 | default: begin 69 | 70 | end 71 | endcase 72 | end 73 | COUNTER_1, COUNTER_2: begin 74 | rdata = cnter; 75 | end 76 | TX_READY: begin 77 | rdata = '0; 78 | end 79 | default: begin 80 | 81 | end 82 | endcase 83 | end 84 | 85 | 86 | always_ff @(posedge clk) begin 87 | if (reset) led <= '0; 88 | else if (valid && wvalid && (addr == FINISH_ADDR)) led <= '1; 89 | end 90 | 91 | // assign ready = '1; 92 | assign last = ready; 93 | 94 | /* UART */ 95 | parameter BIT_TMR_MAX = 'b10100010110000; 96 | parameter BIT_INDEX_MAX = 10; 97 | 98 | logic finish; 99 | always_ff @(posedge clk) begin 100 | if (reset) finish <= '0; 101 | else if (valid && addr == FINISH_ADDR && wvalid) finish <= '1; 102 | end 103 | 104 | logic [13:0] bitTmr = '0; 105 | 106 | localparam type state_t = enum logic [1:0] { 107 | RDY, LOAD_BIT, SEND_BIT 108 | }; 109 | 110 | logic bitDone; 111 | int bitIndex; 112 | logic txBit = '1; 113 | logic [9:0] txData; 114 | state_t txState = RDY; 115 | 116 | // initial begin 117 | // txState = RDY; 118 | // txBit = '1; 119 | // bitTmr = '0; 120 | // end 121 | 122 | logic send; 123 | logic [7:0] char_data; 124 | logic tx_ready; 125 | 126 | wire [15:0][7:0] str = { 127 | 8'h48,8'h65,8'h6c,8'h6c,8'h6f,8'h20, 128 | 8'h77,8'h6f,8'h72,8'h6c,8'h64,8'h21,8'ha,8'h0 129 | }; 130 | 131 | int idx = 14; 132 | logic putchar; 133 | always_ff @(posedge clk) begin 134 | if (reset) putchar <= '1; 135 | else if (~valid) putchar <= '1; 136 | else if (addr == TX_DATA && valid && wvalid && txState != RDY) putchar <= '0; 137 | end 138 | 139 | 140 | assign send = (idx != 0 && finish) || (addr == TX_DATA && valid && wvalid); 141 | 142 | always_ff @(posedge clk) begin 143 | if (send && finish) begin 144 | if (tx_ready) idx <= idx - 1; 145 | end 146 | end 147 | 148 | assign char_data = finish ? str[idx] : wdata[39:32]; 149 | always_ff @(posedge clk) begin 150 | unique case(txState) 151 | RDY: begin 152 | if (send && putchar) txState <= LOAD_BIT; 153 | end 154 | LOAD_BIT: begin 155 | txState <= SEND_BIT; 156 | end 157 | SEND_BIT: begin 158 | if (bitDone) begin 159 | if (bitIndex == BIT_INDEX_MAX) txState <= RDY; 160 | else txState <= LOAD_BIT; 161 | end 162 | end 163 | default: begin 164 | txState <= RDY; 165 | end 166 | endcase 167 | end 168 | 169 | always_ff @(posedge clk) begin 170 | if (txState == RDY) bitTmr <= '0; 171 | else if (bitDone) bitTmr <= '0; 172 | else bitTmr <= bitTmr + 1; 173 | end 174 | 175 | assign bitDone = bitTmr == BIT_TMR_MAX; 176 | 177 | always_ff @(posedge clk) begin 178 | if (txState == RDY) bitIndex <= '0; 179 | else if (txState == LOAD_BIT) bitIndex <= bitIndex + 1; 180 | end 181 | 182 | always_ff @(posedge clk) begin 183 | if (send) txData <= {1'b1, char_data, 1'b0}; 184 | end 185 | 186 | always_ff @(posedge clk) begin 187 | if (txState == RDY) begin 188 | txBit <= '1; 189 | end else if (txState == LOAD_BIT) begin 190 | txBit <= txData[bitIndex]; 191 | end 192 | end 193 | 194 | assign tx = txBit; 195 | assign tx_ready = txState == RDY;// && ~(send && putchar); 196 | if (SIMULATION) 197 | assign ready = '1; 198 | else 199 | assign ready = tx_ready; 200 | 201 | always_ff @(posedge clk) begin 202 | if (~reset && valid && wvalid) begin 203 | if (addr == TX_DATA) begin 204 | $write("%c", char_data); 205 | end else if (addr == FINISH_ADDR) begin 206 | $write("Hello World!\n"); 207 | end 208 | end 209 | end 210 | 211 | 212 | endmodule 213 | -------------------------------------------------------------------------------- /verilate/include/testbench.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "cell.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | enum TestbenchStatus { 11 | Finished, 12 | Skipped 13 | }; 14 | 15 | using PretestHook = std::function; 16 | using PosttestHook = std::function; 17 | using DeferHook = std::function; 18 | 19 | auto _testbench_pretest_hook()->PretestHook &; 20 | auto _testbench_posttest_hook()->PosttestHook &; 21 | 22 | void run_testbench(int n_workers = 1); 23 | void abort_testbench(); 24 | 25 | class ITestbench { 26 | public: 27 | ITestbench(const char *_name); 28 | 29 | const char *name; 30 | 31 | void run(); 32 | 33 | private: 34 | // returns true if test has been skipped. 35 | virtual auto _run( 36 | const PretestHook &pre_fn, const PosttestHook &post_fn 37 | )->TestbenchStatus = 0; 38 | }; 39 | 40 | // the local defer list 41 | class DeferListProxy { 42 | public: 43 | DeferListProxy(); 44 | ~DeferListProxy(); 45 | 46 | void defer(const DeferHook &fn); 47 | }; 48 | 49 | /** 50 | * helper macros to setup global pre-test and post-test hooks. 51 | * 52 | * usage: 53 | * PRETEST_HOOK/POSTTEST_HOOK ; 54 | * 55 | * examples: 56 | * 57 | * PRETEST_HOOK [] { 58 | * dev->reset(); 59 | * }; 60 | * POSTTEST_HOOK [] { 61 | * // source code here. 62 | * }; 63 | */ 64 | 65 | #define PRETEST_HOOK \ 66 | static struct _TestbenchPretestHookSetter { \ 67 | _TestbenchPretestHookSetter(const PretestHook &fn) { \ 68 | _testbench_pretest_hook() = fn; \ 69 | } \ 70 | } _testbench_pretest_hook_setter_inst \ 71 | INIT_PRIORITY(65534) = (PretestHook) 72 | #define POSTTEST_HOOK \ 73 | static struct _TestbenchPosttestHookSetter { \ 74 | _TestbenchPosttestHookSetter(const PosttestHook &fn) { \ 75 | _testbench_posttest_hook() = fn; \ 76 | } \ 77 | } _testbench_posttest_hook_setter_inst \ 78 | INIT_PRIORITY(65534) = (PosttestHook) 79 | 80 | /** 81 | * unit test declaration macros 82 | * 83 | * usage: 84 | * WITH [plugins...] { } AS(""); 85 | * 86 | * example: 87 | * 88 | * WITH LOG { 89 | * // source code here 90 | * } AS("test name"); 91 | */ 92 | 93 | // unique id magic: https://stackoverflow.com/a/2419720/7434327 94 | #define _TESTBENCH_CAT_IMPL(x, y) x##y 95 | #define _TESTBENCH_CAT(x, y) _TESTBENCH_CAT_IMPL(x, y) 96 | #define _TESTBENCH_UNIQUE_NAME(x) _TESTBENCH_CAT(x, __LINE__) 97 | 98 | #define _TESTBENCH_BEGIN(id) \ 99 | static class id final : public ITestbench { \ 100 | using ITestbench::ITestbench; \ 101 | auto _run( \ 102 | const PretestHook &pre_fn, const PosttestHook &post_fn \ 103 | ) -> TestbenchStatus { \ 104 | pre_fn(); \ 105 | { \ 106 | DeferListProxy _; \ 107 | _.defer(post_fn); \ 108 | { 109 | #define _TESTBENCH_END(id, name) \ 110 | } \ 111 | } \ 112 | return Finished; \ 113 | } \ 114 | } id INIT_PRIORITY(65535) (name); 115 | 116 | #define WITH _TESTBENCH_BEGIN(_TESTBENCH_UNIQUE_NAME(_Testbench_L)) 117 | #define AS(name) _TESTBENCH_END(_TESTBENCH_UNIQUE_NAME(_testbench_L), name) 118 | 119 | /** 120 | * basic plugins 121 | */ 122 | 123 | #define TOP_RESET { top->reset(); } 124 | 125 | #ifdef TESTBENCH_RUN_ALL 126 | #define SKIP /* no effect */ 127 | #else 128 | #define SKIP { return Skipped; } 129 | #endif 130 | 131 | #define ENABLE_WITH_BOTH_FN(controller, pre_fn, post_fn) { \ 132 | controller(true); \ 133 | pre_fn(); \ 134 | _.defer([this] { \ 135 | (void) this; \ 136 | post_fn(); \ 137 | controller(false); \ 138 | }); \ 139 | } 140 | #define ENABLE_WITH_FN(controller, fn) ENABLE_WITH_BOTH_FN(controller, fn, [] {}) 141 | #define ENABLE(controller) ENABLE_WITH_FN(controller, [] {}) 142 | 143 | #define LOG ENABLE(enable_logging) 144 | #define DEBUG ENABLE(enable_debugging) 145 | #define STATUS ENABLE(enable_status_line) 146 | 147 | #define TRACE ENABLE_WITH_BOTH_FN( \ 148 | top->enable_fst_trace, \ 149 | [this] { \ 150 | top->start_fst_trace(escape(name) + ".fst"); \ 151 | top->reset(); \ 152 | }, \ 153 | top->stop_fst_trace \ 154 | ) 155 | 156 | #define STAT ENABLE_WITH_BOTH_FN( \ 157 | top->enable_statistics, \ 158 | top->reset_statistics, \ 159 | [this] { top->print_statistics(name); } \ 160 | ) 161 | 162 | // hacks DBus::load and DBus::store to compare the results with 163 | // reference implementation. 164 | template 165 | class _TestbenchDBusWrapperGen : public DBus { 166 | public: 167 | using RefModel = typename std::remove_pointer::type; 168 | 169 | _TestbenchDBusWrapperGen(DBus *_dbus, RefModel *_ref) 170 | : DBus(*_dbus), ref(_ref) 171 | { 172 | ref->reset(); 173 | } 174 | _TestbenchDBusWrapperGen(DBus *_dbus, RefModel &_ref) 175 | : _TestbenchDBusWrapperGen(_dbus, &_ref) 176 | { 177 | } 178 | 179 | // check_memory seems to be expensive, so we only check at the end of test. 180 | ~_TestbenchDBusWrapperGen() 181 | { 182 | ref->check_memory(); 183 | } 184 | 185 | auto load(addr_t addr, AXISize size) -> word_t 186 | { 187 | auto got = DBus::load(addr, size); 188 | auto expected = ref->load(addr, size); 189 | 190 | asserts( 191 | got == expected, 192 | "different outputs from RTL model and reference model." 193 | " expected = %016x, got = %016x", 194 | expected, got 195 | ); 196 | ref->check_internal(); 197 | 198 | return got; 199 | } 200 | 201 | void store(addr_t addr, AXISize size, word_t strobe, word_t data) 202 | { 203 | DBus::store(addr, size, strobe, data); 204 | ref->store(addr, size, strobe, data); 205 | ref->check_internal(); 206 | } 207 | 208 | private: 209 | RefModel *ref; 210 | }; 211 | 212 | // hook MemoryCell's generic bus interface. 213 | // since the wrapper mimics DBusGen, so the implementation 214 | // can be directly inherited. 215 | template 216 | struct GenericBusInterface<_TestbenchDBusWrapperGen> 217 | : public GenericBusInterfaceDBusGen<_TestbenchDBusWrapperGen> { 218 | }; 219 | 220 | // NOTE: macro CMP_TO will shadow global variable dbus in order to hack in 221 | #define CMP_TO(reference) \ 222 | using _TestbenchDBusWrapper = _TestbenchDBusWrapperGen; \ 223 | _TestbenchDBusWrapper _testbench_dbus_wrapper(dbus, ref); \ 224 | auto dbus = &_testbench_dbus_wrapper; \ 225 | (void) dbus; // to suppress "unused variable" warning 226 | -------------------------------------------------------------------------------- /verilate/include/cell.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "bus.h" 5 | 6 | namespace { 7 | 8 | /** 9 | * static detection of suitable unsigned integer type to hold T 10 | */ 11 | 12 | template 13 | struct IntegerType; 14 | 15 | template <> 16 | struct IntegerType<1> { 17 | using Type = uint8_t; 18 | }; 19 | 20 | template <> 21 | struct IntegerType<2> { 22 | using Type = uint16_t; 23 | }; 24 | 25 | template <> 26 | struct IntegerType<4> { 27 | using Type = uint32_t; 28 | }; 29 | 30 | template <> 31 | struct IntegerType<8> { 32 | using Type = uint64_t; 33 | }; 34 | 35 | /** 36 | * static detection of AXISize 37 | */ 38 | 39 | template 40 | struct MemorySize; 41 | 42 | template <> 43 | struct MemorySize<1> { 44 | static constexpr auto Size = AXISize::MSIZE1; 45 | }; 46 | 47 | template <> 48 | struct MemorySize<2> { 49 | static constexpr auto Size = AXISize::MSIZE2; 50 | }; 51 | 52 | template <> 53 | struct MemorySize<4> { 54 | static constexpr auto Size = AXISize::MSIZE4; 55 | }; 56 | 57 | template <> 58 | struct MemorySize<8> { 59 | static constexpr auto Size = AXISize::MSIZE8; 60 | }; 61 | 62 | } 63 | 64 | /** 65 | * we use GenericBusInterface to enable both DBus and 66 | * DBusPipeline in MemoryCellGen. 67 | */ 68 | template 69 | struct GenericBusInterface { 70 | // in order to generate compiler errors instead of undefined 71 | // references, we should not put declarations here. 72 | 73 | // using BusType = TBus; 74 | 75 | // template 76 | // static auto load(BusType *p, addr_t addr) -> word_t; 77 | 78 | // template 79 | // static void store(BusType *p, addr_t addr, word_t data); 80 | }; 81 | 82 | // implementation for bare DBus. 83 | // this generator is meant to share with testbench.h 84 | template 85 | struct GenericBusInterfaceDBusGen { 86 | template 87 | static auto load(BusType *p, addr_t addr) -> word_t 88 | { 89 | auto op = LoadOp::parse(addr); 90 | auto value = p->load(addr, Size); 91 | return op.apply(value); 92 | } 93 | 94 | template 95 | static void store(BusType *p, addr_t addr, word_t data) 96 | { 97 | auto op = LoadOp::parse(addr); 98 | p->store(addr, Size, op.strobe(), op.place(data)); 99 | } 100 | }; 101 | 102 | template 103 | struct GenericBusInterface> 104 | : public GenericBusInterfaceDBusGen> { 105 | }; 106 | 107 | // implementation for DBusPipeline. 108 | template 109 | struct GenericBusInterface> { 110 | using BusType = DBusPipelineGen; 111 | 112 | template 113 | static auto load(BusType *p, addr_t addr) -> word_t 114 | { 115 | uint32_t value; 116 | p->load(addr, Size, &value, LoadOp::parse(addr)); 117 | p->fence(); 118 | return value; 119 | } 120 | 121 | template 122 | static void store(BusType *p, addr_t addr, word_t data) 123 | { 124 | auto op = LoadOp::parse(addr); 125 | p->store(addr, Size, op.strobe(), op.place(data)); 126 | } 127 | }; 128 | 129 | // a cell of memory stored on your cache. 130 | template 131 | class MemoryCellGen { 132 | public: 133 | /** 134 | * types & constants 135 | */ 136 | 137 | using ValueType = T; 138 | 139 | static constexpr size_t Width = sizeof(T); 140 | static constexpr word_t Strobe = (1 << Width) - 1; 141 | static constexpr auto Size = MemorySize::Size; 142 | 143 | using IntType = typename IntegerType::Type; 144 | 145 | // supported memory widths 146 | static_assert(Width == 1 || Width == 2 || Width == 4 || Width == 8); 147 | 148 | // delete default constructors & assignments 149 | MemoryCellGen() = delete; 150 | MemoryCellGen(const MemoryCellGen &) = delete; 151 | 152 | // allow move constructor & assignments 153 | MemoryCellGen(MemoryCellGen &&rhs) 154 | { 155 | init_cell(rhs.addr, rhs.p); 156 | rhs.addr = 0; 157 | rhs.p = nullptr; 158 | } 159 | 160 | auto operator=(MemoryCellGen &&rhs) 161 | { 162 | init_cell(rhs.addr, rhs.p); 163 | rhs.addr = 0; 164 | rhs.p = nullptr; 165 | } 166 | 167 | // construct a cell pointed to addr. 168 | MemoryCellGen(addr_t _addr, TBus *_p) 169 | { 170 | init_cell(_addr, _p); 171 | asserts(addr % Width == 0, "addr must be aligned to %d bytes", Width); 172 | } 173 | 174 | // assigned by the same type of memory cell 175 | auto operator=(const MemoryCellGen &rhs) 176 | { 177 | set(rhs.get()); 178 | } 179 | 180 | // assigned by other memory cell. 181 | template 182 | void operator=(const MemoryCellGen &rhs) 183 | { 184 | using RHSType = MemoryCellGen; 185 | 186 | set(reinterpret_cast(static_cast( 187 | reinterpret_cast(rhs.get()) 188 | ))); 189 | } 190 | 191 | // assigned by values of type T. 192 | auto operator=(const T &rhs) const 193 | { 194 | set(rhs); 195 | } 196 | 197 | // implicitly convert to type T. 198 | operator T() const 199 | { 200 | return get(); 201 | } 202 | 203 | // provide std::swap. 204 | friend void swap(MemoryCellGen &x, MemoryCellGen &y) noexcept 205 | { 206 | auto u = x.get(); 207 | auto v = y.get(); 208 | x.set(v); 209 | y.set(u); 210 | } 211 | 212 | /** 213 | * manually get/set the value inside the cell. 214 | */ 215 | 216 | auto get() const -> T 217 | { 218 | return bus_impl.template load(p, addr); 219 | } 220 | 221 | void set(const T &value) const 222 | { 223 | bus_impl.template store(p, addr, value); 224 | } 225 | 226 | protected: 227 | template 228 | friend class MemoryCellFactory; 229 | 230 | void init_cell(addr_t _addr, TBus *_p) 231 | { 232 | addr = _addr; 233 | p = _p; 234 | } 235 | 236 | private: 237 | addr_t addr; 238 | TBus *p; 239 | GenericBusInterface bus_impl; 240 | }; 241 | 242 | template 243 | class MemoryCellFactory { 244 | public: 245 | template 246 | using MemoryCell = MemoryCellGen; 247 | 248 | MemoryCellFactory(Pipeline *_p) : offset(0), p(_p) {} 249 | 250 | template 251 | auto take(addr_t addr) -> MemoryCell 252 | { 253 | // thanks to copy elision, we can return temporary object without copying 254 | return MemoryCell(addr, p); 255 | } 256 | 257 | template 258 | auto take(addr_t addr) -> MemoryCell * 259 | { 260 | using Cell = MemoryCell; 261 | 262 | uchar *buffer = new uchar[N * sizeof(Cell)]; 263 | allocated.emplace_back(buffer); 264 | 265 | Cell *cells = reinterpret_cast(buffer); 266 | 267 | for (size_t i = 0; i < N; i++) { 268 | cells[i].init_cell(addr + i * sizeof(T), p); 269 | } 270 | 271 | return cells; 272 | } 273 | 274 | template 275 | auto allocate() -> MemoryCell 276 | { 277 | auto addr = offset; 278 | offset += sizeof(T); 279 | return take(addr); 280 | } 281 | 282 | template 283 | auto allocate_and_init(const T &v) -> MemoryCell 284 | { 285 | auto cell = allocate(); 286 | cell = v; 287 | return cell; 288 | } 289 | 290 | void reset() 291 | { 292 | offset = 0; 293 | } 294 | 295 | private: 296 | size_t offset; 297 | Pipeline *p; 298 | 299 | // use std::unique_ptr instead of std::unique_ptr to 300 | // prevent mismatch of new[] and delete! 301 | std::vector> allocated; 302 | }; 303 | -------------------------------------------------------------------------------- /verilate/vsrc/testbench.cpp: -------------------------------------------------------------------------------- 1 | #include "testbench.h" 2 | 3 | static std::vector test_list INIT_PRIORITY(65532); 4 | static ITestbench *current_test = nullptr; 5 | static PretestHook pretest_hook INIT_PRIORITY(65533) = [] {}; 6 | static PosttestHook posttest_hook INIT_PRIORITY(65533) = [] {}; 7 | static std::vector defer_list INIT_PRIORITY(65532); 8 | static std::vector global_defer_list INIT_PRIORITY(65532); 9 | 10 | auto _testbench_pretest_hook() -> PretestHook & 11 | { 12 | return pretest_hook; 13 | } 14 | 15 | auto _testbench_posttest_hook() -> PosttestHook & 16 | { 17 | return posttest_hook; 18 | } 19 | 20 | template 21 | static void run_defers() 22 | { 23 | // defer_list acts like a stack. 24 | 25 | if (RunLocal) { 26 | for (auto it = defer_list.rbegin(); it != defer_list.rend(); it++) { 27 | (*it)(); 28 | } 29 | } 30 | 31 | if (RunGlobal) { 32 | for (auto it = global_defer_list.rbegin(); it != global_defer_list.rend(); it++) { 33 | (*it)(); 34 | } 35 | } 36 | } 37 | 38 | static void run_test(size_t i, bool report_status = true) 39 | { 40 | auto t = test_list[i]; 41 | 42 | ThreadWorker worker; 43 | if (report_status) { 44 | worker = ThreadWorker::at_interval(1000, [i, t] { 45 | status_line("(%zu/%zu) running \"%s\"...", i + 1, test_list.size(), t->name); 46 | }); 47 | } 48 | 49 | t->run(); 50 | } 51 | 52 | #include 53 | #include 54 | 55 | #if ICS_ON_LINUX 56 | 57 | [[noreturn]] static void run_worker(int id, int req, int resp) 58 | { 59 | // global_defer_list.push_back([req, resp] { 60 | // close(req); 61 | // close(resp); 62 | // }); 63 | 64 | cpu_set_t set; 65 | CPU_ZERO(&set); 66 | CPU_SET(id, &set); 67 | sched_setaffinity(getpid(), sizeof(set), &set); 68 | 69 | while (true) { 70 | // send a dummy char to request a task. 71 | asserts(write(req, "a", 1) == 1, "worker %d failed to send command", id); 72 | 73 | int i, read_count = read(resp, &i, sizeof(int)); 74 | asserts(read_count >= 0, "worker %d failed to get response", id); 75 | if (read_count != sizeof(int)) 76 | break; 77 | if (i < 0) // received -1, exit. 78 | break; 79 | 80 | run_test(i, false); 81 | } 82 | 83 | run_defers(); 84 | exit(EXIT_SUCCESS); 85 | } 86 | 87 | static auto run_parallel(int n_workers) -> int 88 | { 89 | int total = test_list.size(), maxfd = 0; 90 | int dispatched = 0, exited = 0, passed = 0; 91 | std::vector pid, master, worker; 92 | pid.reserve(n_workers); 93 | master.reserve(n_workers); 94 | worker.reserve(n_workers); 95 | 96 | // is the worker the first to request a task? 97 | std::vector first; 98 | first.resize(n_workers); 99 | std::fill(first.begin(), first.end(), true); 100 | 101 | // spawn workers. 102 | for (int i = 0; i < n_workers; i++) { 103 | // pm is master->worker pipe, and pw is worker->master pipe. 104 | int pm[2], pw[2]; 105 | asserts(pipe(pm) >= 0, "failed to create master pipes"); 106 | asserts(pipe(pw) >= 0, "failed to create worker pipes"); 107 | 108 | int p = fork(); 109 | asserts(p >= 0, "failed to spwan workers."); 110 | 111 | if (p == 0) { 112 | close(pm[1]); 113 | close(pw[0]); 114 | run_worker(i, pw[1], pm[0]); 115 | } else { 116 | pid.push_back(p); 117 | close(pm[0]); 118 | close(pw[1]); 119 | master.push_back(pm[1]); 120 | worker.push_back(pw[0]); 121 | maxfd = std::max(maxfd, std::max(pm[1], pw[0])); 122 | } 123 | } 124 | 125 | bool failed = false; 126 | while (!failed && exited < n_workers) { 127 | fd_set set; 128 | FD_ZERO(&set); 129 | for (int fd : worker) { 130 | if (fd >= 0) 131 | FD_SET(fd, &set); 132 | } 133 | 134 | // IO multiplexing 135 | int r = select(maxfd + 1, &set, NULL, NULL, NULL); 136 | asserts(r >= 0, "failed to fetch requests from workers"); 137 | 138 | for (size_t j = 0; j < worker.size(); j++) { 139 | int fd = worker[j]; 140 | if (fd < 0) 141 | continue; 142 | 143 | if (FD_ISSET(fd, &set)) { 144 | // read the dummy char. 145 | char c; 146 | int read_count = read(fd, &c, 1); 147 | asserts(read_count >= 0, "failed to read response from worker"); 148 | 149 | // the child failed and the pipe was closed, 150 | // so we got an EOF. 151 | if (read_count == 0) { 152 | failed = true; 153 | break; 154 | } 155 | 156 | if (first[j]) 157 | first[j] = false; 158 | else 159 | passed++; 160 | 161 | if (dispatched < total) { 162 | // dispatch a new task. 163 | int write_count = write(master[j], &dispatched, sizeof(int)); 164 | asserts(write_count == sizeof(int), "failed to send command to worker"); 165 | dispatched++; 166 | } else { 167 | // or send a -1 to notify the worker to exit. 168 | int v = -1; 169 | int write_count = write(master[j], &v, sizeof(int)); 170 | asserts(write_count == sizeof(int), "failed to send command to worker"); 171 | 172 | close(worker[j]); 173 | worker[j] = -1; 174 | exited++; 175 | } 176 | } 177 | } 178 | } 179 | 180 | // close all master->worker pipes, so all the workers will finally exit. 181 | for (int fd : master) { 182 | close(fd); 183 | } 184 | 185 | for (int i = 0; i < n_workers; i++) { 186 | int p = pid[i]; 187 | 188 | // if some tests failed and the worker is still alive, kill it. 189 | if (failed && waitpid(p, NULL, WNOHANG) == 0) { 190 | info(YELLOW "(warn)" RESET " killing worker %d (pid=%d)...\n", i, p); 191 | kill(p, SIGKILL); 192 | } 193 | 194 | waitpid(p, NULL, 0); 195 | } 196 | 197 | if (failed) 198 | info(RED "FATAL!" RESET " some tests failed.\n"); 199 | 200 | return passed; 201 | } 202 | 203 | #endif 204 | 205 | static auto run_serial() -> int 206 | { 207 | int count = 0, total = test_list.size(); 208 | 209 | for (int i = 0; i < total; i++) { 210 | run_test(i); 211 | count++; 212 | } 213 | 214 | return count; 215 | } 216 | 217 | void run_testbench(int n_workers) 218 | { 219 | #if ICS_ON_LINUX 220 | int count = n_workers == 1 ? 221 | run_serial() : 222 | run_parallel(n_workers); 223 | #else 224 | int count = run_serial(); 225 | (void)n_workers; 226 | #endif 227 | 228 | if (count == 1) 229 | info(BLUE "(info)" RESET " 1 test passed.\n"); 230 | else 231 | info(BLUE "(info)" RESET " %d tests passed.\n", count); 232 | 233 | run_defers(); 234 | } 235 | 236 | void abort_testbench() 237 | { 238 | if (current_test) 239 | info(RED "ERR!" RESET " testbench aborted in \"%s\"\n", current_test->name); 240 | fflush(stdout); 241 | fflush(stderr); 242 | run_defers(); 243 | } 244 | 245 | ITestbench::ITestbench(const char *_name) : name(_name) 246 | { 247 | test_list.push_back(this); 248 | } 249 | 250 | void ITestbench::run() 251 | { 252 | using clock = std::chrono::high_resolution_clock; 253 | 254 | current_test = this; 255 | 256 | auto t_start = clock::now(); 257 | auto result = _run(pretest_hook, posttest_hook); 258 | auto t_end = clock::now(); 259 | 260 | auto span = std::chrono::duration_cast(t_end - t_start); 261 | 262 | if (result == Skipped) 263 | info(YELLOW "[--]" RESET " %s (skipped)\n", name); 264 | else 265 | info(GREEN "[OK]" RESET " %s (%dms)\n", name, span.count()); 266 | 267 | current_test = nullptr; 268 | } 269 | 270 | DeferListProxy::DeferListProxy() 271 | { 272 | internal_assert(defer_list.empty(), "defer list is not empty"); 273 | defer_list.reserve(4); 274 | } 275 | 276 | DeferListProxy::~DeferListProxy() 277 | { 278 | run_defers(); 279 | defer_list.clear(); 280 | } 281 | 282 | void DeferListProxy::defer(const DeferHook &fn) 283 | { 284 | defer_list.push_back(fn); 285 | } 286 | -------------------------------------------------------------------------------- /vsrc/include/common.sv: -------------------------------------------------------------------------------- 1 | `ifndef COMMON_SV 2 | `define COMMON_SV 3 | `ifdef VERILATOR 4 | `include "include/config.sv" 5 | `endif 6 | import config_pkg::*; 7 | package common; 8 | // parameters 9 | import config_pkg::*; 10 | parameter XLEN = 64; 11 | parameter MXLEN = XLEN; 12 | parameter LINK_REG_ID = 1; 13 | parameter logic[63:0] PCINIT = 64'h00000000_80000000; 14 | 15 | // typedefs 16 | typedef logic[127:0] u128; 17 | typedef logic[63:0] u64; 18 | typedef logic[43:0] u44; 19 | typedef logic[31:0] u32; 20 | typedef logic[19:0] u20; 21 | typedef logic[15:0] u16; 22 | typedef logic[14:0] u15; 23 | typedef logic[13:0] u14; 24 | typedef logic[12:0] u13; 25 | typedef logic[11:0] u12; 26 | typedef logic[10:0] u11; 27 | typedef logic[9:0] u10; 28 | typedef logic[8:0] u9; 29 | typedef logic[7:0] u8; 30 | typedef logic[6:0] u7; 31 | typedef logic[5:0] u6; 32 | typedef logic[4:0] u5; 33 | typedef logic[3:0] u4; 34 | typedef logic[2:0] u3; 35 | typedef logic[1:0] u2; 36 | typedef logic u1; 37 | 38 | typedef u5 creg_addr_t; 39 | // typedef u64 word_t; 40 | typedef u8 strobe_t; 41 | typedef u12 csr_addr_t; 42 | 43 | 44 | 45 | 46 | /** 47 | * this file contains basic definitions and typedefs for general designs. 48 | */ 49 | 50 | // Vivado does not support string parameters. 51 | `ifdef VERILATOR 52 | `define STRING string 53 | `else 54 | `define STRING /* f**k vivado */ 55 | `endif 56 | 57 | /** 58 | * Vivado does not support that members of a packed union 59 | * have different sizes. Therefore, we have to use struct 60 | * instead of union in Vivado. 61 | */ 62 | `ifdef VERILATOR 63 | `define PACKED_UNION union packed 64 | `else 65 | `define PACKED_UNION struct packed 66 | `endif 67 | 68 | 69 | // simple compile-time assertion 70 | `define ASSERTS(expr, message) \ 71 | if (!(expr)) $error(message); 72 | `define ASSERT(expr) `ASSERTS(expr, "Assertion failed."); 73 | 74 | // to ignore some signals 75 | `define UNUSED_OK(list) \ 76 | logic _unused_ok = &{1'b0, {list}, 1'b0}; 77 | 78 | // basic data types 79 | `define BITS(x) logic[(x)-1:0] 80 | 81 | typedef int unsigned uint; 82 | 83 | typedef logic i1; 84 | typedef `BITS(2) i2; 85 | typedef `BITS(3) i3; 86 | typedef `BITS(4) i4; 87 | typedef `BITS(5) i5; 88 | typedef `BITS(6) i6; 89 | typedef `BITS(7) i7; 90 | typedef `BITS(8) i8; 91 | typedef `BITS(9) i9; 92 | typedef `BITS(16) i16; 93 | typedef `BITS(19) i19; 94 | typedef `BITS(26) i26; 95 | typedef `BITS(32) i32; 96 | typedef `BITS(33) i33; 97 | typedef `BITS(34) i34; 98 | typedef `BITS(35) i35; 99 | typedef `BITS(36) i36; 100 | typedef `BITS(37) i37; 101 | typedef `BITS(38) i38; 102 | typedef `BITS(39) i39; 103 | typedef `BITS(40) i40; 104 | typedef `BITS(41) i41; 105 | typedef `BITS(42) i42; 106 | typedef `BITS(64) i64; 107 | typedef `BITS(65) i65; 108 | typedef `BITS(66) i66; 109 | typedef `BITS(67) i67; 110 | typedef `BITS(68) i68; 111 | 112 | // for arithmetic overflow detection 113 | typedef i65 arith_t; 114 | 115 | // all addresses and words are 32-bit 116 | typedef i64 addr_t; 117 | typedef i64 word_t; 118 | 119 | // number of bytes transferred in one memory r/w 120 | typedef enum i3 { 121 | MSIZE1 = 3'b000, 122 | MSIZE2 = 3'b001, 123 | MSIZE4 = 3'b010, 124 | MSIZE8 = 3'b011 125 | } msize_t; 126 | 127 | // length of a burst transaction 128 | // NOTE: WRAP mode in AXI3 only supports power-of-2 length. 129 | typedef enum i8 { 130 | MLEN1 = 8'h00, 131 | MLEN2 = 8'h01, 132 | MLEN4 = 8'h03, 133 | MLEN8 = 8'h07, 134 | MLEN16 = 8'h0f, 135 | MLEN32 = 8'h1f, 136 | MLEN64 = 8'h3f, 137 | MLEN128 = 8'h7f, 138 | MLEN256 = 8'hff 139 | 140 | } mlen_t; 141 | 142 | parameter mlen_t AXI_BURST_LEN = AXI_BURST_NUM == 16 ? MLEN16 : 143 | AXI_BURST_NUM == 32 ? MLEN32 : 144 | AXI_BURST_NUM == 64 ? MLEN64 : 145 | AXI_BURST_NUM == 128 ? MLEN128 : 146 | AXI_BURST_NUM == 256 ? MLEN256 : MLEN1; 147 | parameter COMMON_OFFSET_BITS = AXI_BURST_NUM == 16 ? 7 : 148 | AXI_BURST_NUM == 32 ? 8 : 149 | AXI_BURST_NUM == 64 ? 9 : 150 | AXI_BURST_NUM == 128 ? 10 : 151 | AXI_BURST_NUM == 256 ? 11 : 1; 152 | 153 | // a 4-bit mask for memory r/w, namely "write enable" 154 | // typedef i8 strobe_t; 155 | 156 | // general-purpose register index 157 | typedef i5 regidx_t; 158 | 159 | /** 160 | * SOME NOTES ON BUSES 161 | * 162 | * bus naming convention: 163 | * * CPU -> cache: xxx_req_t 164 | * * cache -> CPU: xxx_resp_t 165 | * 166 | * in other words, caches are masters and CPU is the worker, 167 | * and CPU must wait for caches to complete memory transactions. 168 | * handshake signals are synchronized at positive edge of the clock. 169 | * 170 | * we guarantee that IBus is a subset of DBus, so that data cache can 171 | * be used as a instruction cache. 172 | * powerful students are free to design their own bus interfaces to 173 | * enable superscalar pipelines and other advanced techniques. 174 | * 175 | * a request on cache bus can bypass a cache instance if the address 176 | * is in uncached memory regions. 177 | */ 178 | 179 | /** 180 | * NOTE on strobe: 181 | * 182 | * strobe is used to mask out unused bytes in data, and 183 | * data are always assumed be placed at addresses aligned to 184 | * 4 bytes, no matter the lowest 2 bits of addr says. 185 | * for example, if you want to write one byte "0xcd" at 0x1f2, 186 | * the addr is "0x000001f2", but the data should be "0x00cd0000" 187 | * and the strobe should be "0b0100", rather than "0x000000cd" 188 | * and "0b0001". 189 | */ 190 | 191 | /** 192 | * data cache bus 193 | */ 194 | 195 | typedef struct packed { 196 | logic valid; // in request? 197 | addr_t addr; // target address 198 | msize_t size; // number of bytes 199 | strobe_t strobe; // which bytes are enabled? set to zeros for read request 200 | word_t data; // the data to write 201 | } dbus_req_t; 202 | 203 | typedef struct packed { 204 | logic addr_ok; // is the address accepted by cache? 205 | logic data_ok; // is the field "data" valid? 206 | word_t data; // the data read from cache 207 | } dbus_resp_t; 208 | 209 | /** 210 | * instruction cache bus 211 | * addr must be aligned to 4 bytes. 212 | * 213 | * basically, ibus_resp_t is the same as dbus_resp_t. 214 | */ 215 | 216 | typedef struct packed { 217 | logic valid; // in request? 218 | addr_t addr; // target address 219 | } ibus_req_t; 220 | 221 | typedef struct packed { 222 | logic addr_ok; // is the address accepted by cache? 223 | logic data_ok; // is the field "data" valid? 224 | u32 data; // the data read from cache 225 | } ibus_resp_t; 226 | 227 | `define IREQ_TO_DREQ(ireq) \ 228 | {ireq, MSIZE4, 8'b0, 64'b0} 229 | 230 | `define DRESP_TO_IRESP(dresp, ireq) \ 231 | {dresp.addr_ok, dresp.data_ok, ireq.addr[2] ? dresp.data[63:32] : dresp.data[31:0]} 232 | 233 | /** 234 | * cache bus: simplified burst AXI transaction interface 235 | */ 236 | typedef enum i2 { 237 | AXI_BURST_FIXED = '0, 238 | AXI_BURST_INCR, 239 | AXI_BURST_WRAP, 240 | AXI_BURST_RESERVED 241 | } axi_burst_type_t; 242 | typedef struct packed { 243 | logic valid; // in request? 244 | logic is_write; // is it a write transaction? 245 | msize_t size; // number of bytes in one burst 246 | addr_t addr; // start address 247 | strobe_t strobe; // which bytes are enabled? 248 | word_t data; // the data to write 249 | mlen_t len; // number of bursts 250 | axi_burst_type_t burst; 251 | } cbus_req_t; 252 | 253 | typedef struct packed { 254 | logic ready; // is data arrived in this cycle? 255 | logic last; // is it the last word? 256 | word_t data; // the data from AXI bus 257 | } cbus_resp_t; 258 | 259 | /** 260 | * AXI-related typedefs 261 | */ 262 | 263 | 264 | 265 | typedef struct packed { 266 | u1 valid; 267 | u64 addr; 268 | msize_t size; 269 | } mread_req; 270 | 271 | typedef struct packed { 272 | u1 valid; 273 | u64 addr; 274 | msize_t size; 275 | strobe_t strobe; 276 | u64 data; 277 | } mwrite_req; 278 | 279 | endpackage 280 | `endif 281 | -------------------------------------------------------------------------------- /verilate/vsrc/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | void hook_signal(int sig, handler_t *handler) 13 | { 14 | struct sigaction action; 15 | 16 | action.sa_handler = handler; 17 | sigemptyset(&action.sa_mask); 18 | action.sa_flags = SA_RESTART; 19 | 20 | auto result = sigaction(sig, &action, NULL); 21 | internal_assert(result >= 0, "failed to hook signal %d", sig); 22 | } 23 | 24 | /* 25 | static bool startswith(const std::string &text, const std::string &pattern) { 26 | for (size_t i = 0; i < pattern.size(); i++) { 27 | if (i >= text.size() || text[i] != pattern[i]) 28 | return false; 29 | } 30 | return true; 31 | } 32 | */ 33 | 34 | static bool endswith(const std::string &text, const std::string &pattern) 35 | { 36 | for (size_t i = 0; i < pattern.size(); i++) { 37 | if (i >= text.size() || 38 | text[text.size() - i - 1] != pattern[pattern.size() - i - 1]) 39 | return false; 40 | } 41 | return true; 42 | } 43 | 44 | auto trim(const std::string &text) -> std::string 45 | { 46 | size_t i, j; 47 | for (i = 0; i < text.size(); i++) { 48 | if (std::isalnum(text[i])) 49 | break; 50 | } 51 | for (j = text.size(); j > i; j--) { 52 | if (std::isalnum(text[j - 1])) 53 | break; 54 | } 55 | return text.substr(i, j - i); 56 | } 57 | 58 | auto escape(const std::string &text) -> std::string 59 | { 60 | std::string result = text; 61 | 62 | for (char &c : result) { 63 | if (!std::isalnum(c)) 64 | c = '-'; 65 | } 66 | 67 | return result; 68 | } 69 | 70 | static void feed(ByteSeq &seq, std::ifstream &fp, int base) 71 | { 72 | std::string buf; 73 | 74 | while (std::getline(fp, buf)) { 75 | buf = trim(buf); 76 | if (buf.empty()) 77 | continue; 78 | 79 | size_t count = 0; 80 | uint64_t data = std::stoull(buf, &count, base); 81 | asserts(count == buf.size(), "failed to parse data \"%s\" in base %d", buf.data(), base); 82 | asserts((data & 0xffffffff) == data, "\"%s\" cannot fit in a 32-bit integer", buf.data()); 83 | 84 | for (size_t i = 0; i < 4; i++) { 85 | seq.push_back(data & 0xff); 86 | data >>= 8; 87 | } 88 | } 89 | } 90 | 91 | static auto parse_coe(std::ifstream &fp) -> ByteSeq 92 | { 93 | std::string buf; 94 | 95 | // first line 96 | std::getline(fp, buf); 97 | 98 | { 99 | std::istringstream bs(buf); 100 | std::getline(bs, buf, '='); 101 | asserts(trim(buf) == "memory_initialization_radix", "COE file should begin with \"memory_initialization_radix = \""); 102 | std::getline(bs, buf); 103 | } 104 | 105 | int base = std::stoi(buf); 106 | 107 | // second line 108 | std::getline(fp, buf); 109 | 110 | { 111 | std::istringstream bs(buf); 112 | std::getline(bs, buf, '='); 113 | asserts(trim(buf) == "memory_initialization_vector", "the second line of COE file should be \"memory_initialization_vector =\""); 114 | } 115 | 116 | // data 117 | ByteSeq seq; 118 | feed(seq, fp, base); 119 | return seq; 120 | } 121 | 122 | static auto parse_mif(std::ifstream &fp) -> ByteSeq 123 | { 124 | ByteSeq seq; 125 | feed(seq, fp, 2); 126 | return seq; 127 | } 128 | 129 | static auto parse_hex(std::ifstream &fp) -> ByteSeq 130 | { 131 | ByteSeq seq; 132 | feed(seq, fp, 16); 133 | return seq; 134 | } 135 | 136 | static auto parse_bin(std::ifstream &fp) -> ByteSeq 137 | { 138 | ByteSeq seq; 139 | std::ifstream::char_type c; 140 | while (fp.get(c)) { 141 | seq.push_back(static_cast(c)); 142 | } 143 | return seq; 144 | } 145 | 146 | auto parse_memory_file(const std::string &path) -> ByteSeq 147 | { 148 | std::ifstream fp(path); 149 | asserts(fp, "failed to open file \"%s\"", path.data()); 150 | 151 | if (endswith(path, ".coe")) 152 | return parse_coe(fp); 153 | else if (endswith(path, ".mif")) 154 | return parse_mif(fp); 155 | else if (endswith(path, ".hex")) 156 | return parse_hex(fp); 157 | else 158 | return parse_bin(fp); 159 | } 160 | 161 | /** 162 | * simple logging 163 | */ 164 | 165 | _log_ctx_t _ctx; 166 | 167 | static void check_status_line(FILE *fp = stdout) 168 | { 169 | if (_ctx.in_status_line) { 170 | fprintf(fp, MOVE_TO_FRONT CLEAR_TO_RIGHT); 171 | fflush(fp); 172 | _ctx.in_status_line = false; 173 | } 174 | } 175 | 176 | void enable_debugging(bool enable) 177 | { 178 | _ctx.debug_enabled = enable; 179 | } 180 | 181 | void enable_logging(bool enable) 182 | { 183 | _ctx.log_enabled = enable; 184 | } 185 | 186 | void enable_status_line(bool enable) 187 | { 188 | _ctx.status_enabled = enable; 189 | } 190 | 191 | #define VPRINT(fp) { \ 192 | std::lock_guard guard(_ctx.lock); \ 193 | check_status_line(); \ 194 | va_list args; \ 195 | va_start(args, message); \ 196 | vfprintf(fp, message, args); \ 197 | va_end(args); \ 198 | } 199 | 200 | void _log_debug(const char *message, ...) 201 | { 202 | VPRINT(stdout); 203 | } 204 | 205 | void info(const char *message, ...) 206 | { 207 | if (_ctx.log_enabled) 208 | VPRINT(stdout); 209 | } 210 | 211 | void warn(const char *message, ...) 212 | { 213 | if (_ctx.log_enabled) 214 | VPRINT(stderr); 215 | } 216 | 217 | void notify(const char *message, ...) 218 | { 219 | VPRINT(stderr); 220 | fflush(stderr); 221 | } 222 | 223 | void notify_char(char c) 224 | { 225 | std::lock_guard guard(_ctx.lock); 226 | 227 | if (_ctx.status_enabled) { 228 | auto &buf = _ctx.char_buffer; 229 | buf.push_back(c); 230 | if (buf.back() == '\n' || buf.size() >= LOG_MAX_BUFFER_SIZE) { 231 | check_status_line(); 232 | fputs(buf.data(), stderr); 233 | buf.clear(); 234 | } 235 | } else 236 | fputc(c, stderr); 237 | 238 | fflush(stderr); 239 | } 240 | 241 | void status_line(const char *message, ...) 242 | { 243 | if (_ctx.status_enabled) { 244 | std::lock_guard guard(_ctx.lock); 245 | 246 | va_list args; 247 | va_start(args, message); 248 | 249 | _ctx.in_status_line = true; 250 | fprintf(stdout, MOVE_TO_FRONT); 251 | vfprintf(stdout, message, args); 252 | fprintf(stdout, CLEAR_TO_RIGHT " "); 253 | fflush(stdout); 254 | 255 | va_end(args); 256 | } 257 | } 258 | 259 | void log_separator() 260 | { 261 | fputs("\n", stdout); 262 | fputs("\n", stderr); 263 | } 264 | 265 | SimpleTimer::SimpleTimer() 266 | { 267 | t_start = clock::now(); 268 | } 269 | 270 | SimpleTimer::~SimpleTimer() 271 | { 272 | t_end = clock::now(); 273 | auto span = std::chrono::duration(t_end - t_start).count(); 274 | 275 | bool use_mhz = false; 276 | auto rate = _cycles / span / 1e3; 277 | if (rate >= 1e3 - 1) { 278 | use_mhz = true; 279 | rate /= 1e3; 280 | } 281 | 282 | info(BLUE "(info)" RESET " testbench finished in %d cycles (%.3lf %s).\n", 283 | _cycles, rate, use_mhz ? "MHz" : "KHz"); 284 | 285 | } 286 | 287 | void SimpleTimer::update(uint64_t cycles) 288 | { 289 | _cycles = cycles; 290 | } 291 | 292 | ThreadWorker::ThreadWorker() 293 | : stopped(true), flag(nullptr) 294 | { 295 | } 296 | 297 | ThreadWorker::ThreadWorker(ThreadWorker &&rhs) 298 | : stopped(rhs.stopped), flag(rhs.flag), worker(std::move(rhs.worker)) 299 | { 300 | rhs.stopped = true; 301 | rhs.flag = nullptr; 302 | } 303 | 304 | ThreadWorker::ThreadWorker( 305 | uint64_t interval_in_ms, bool repeat, 306 | const WorkerFn &fn, 307 | const WorkerFn &begin_fn, 308 | const WorkerFn &final_fn 309 | ) : stopped(false), 310 | flag(new bool(false)) 311 | { 312 | auto ptr = flag; 313 | worker = std::thread([=] { 314 | begin_fn(); 315 | 316 | while (!(*ptr)) { 317 | fn(); 318 | std::this_thread::sleep_for(std::chrono::milliseconds(interval_in_ms)); 319 | if (!repeat) 320 | break; 321 | } 322 | 323 | final_fn(); 324 | 325 | delete ptr; 326 | }); 327 | worker.detach(); 328 | } 329 | 330 | auto ThreadWorker::operator=(ThreadWorker &&rhs) -> ThreadWorker & 331 | { 332 | stopped = rhs.stopped; 333 | flag = rhs.flag; 334 | worker = std::move(rhs.worker); 335 | 336 | rhs.stopped = true; 337 | rhs.flag = nullptr; 338 | 339 | return *this; 340 | } 341 | 342 | auto ThreadWorker::once( 343 | const WorkerFn &fn, 344 | const WorkerFn &begin_fn, 345 | const WorkerFn &final_fn 346 | ) -> ThreadWorker 347 | { 348 | return ThreadWorker(0, false, fn, begin_fn, final_fn); 349 | } 350 | 351 | auto ThreadWorker::loop( 352 | const WorkerFn &fn, 353 | const WorkerFn &begin_fn, 354 | const WorkerFn &final_fn 355 | ) -> ThreadWorker 356 | { 357 | return ThreadWorker(0, true, fn, begin_fn, final_fn); 358 | } 359 | 360 | auto ThreadWorker::at_interval( 361 | uint64_t interval_in_ms, 362 | const WorkerFn &fn, 363 | const WorkerFn &begin_fn, 364 | const WorkerFn &final_fn 365 | ) -> ThreadWorker 366 | { 367 | return ThreadWorker(interval_in_ms, true, fn, begin_fn, final_fn); 368 | } 369 | 370 | ThreadWorker::~ThreadWorker() 371 | { 372 | stop(); 373 | } 374 | 375 | void ThreadWorker::stop() 376 | { 377 | if (!stopped) { 378 | stopped = true; 379 | internal_assert(flag, "flag should not be nullptr"); 380 | *flag = true; 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /verilate/include/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | // detect Linux 16 | #define ICS_ON_LINUX __linux__ 17 | 18 | // to control initialization order of global variables 19 | #define INIT_PRIORITY(value) \ 20 | __attribute__ ((init_priority(value))) 21 | 22 | /** 23 | * typedefs 24 | */ 25 | 26 | using addr_t = uint64_t; 27 | using word_t = uint64_t; 28 | using handler_t = void(int); 29 | using uchar = unsigned char; 30 | 31 | using ByteSeq = std::vector; 32 | 33 | /** 34 | * basic constexprs 35 | */ 36 | 37 | constexpr size_t MEMORY_SIZE = 1024 * 1024; // 1 MiB 38 | 39 | constexpr word_t STROBE_TO_MASK[] = { 40 | 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, 41 | 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, 42 | 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, 43 | 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff, 44 | }; 45 | 46 | /** 47 | * utility functions 48 | */ 49 | 50 | void hook_signal(int sig, handler_t *handler); 51 | auto trim(const std::string &text)->std::string; 52 | auto escape(const std::string &text)->std::string; 53 | auto parse_memory_file(const std::string &path)->ByteSeq; 54 | 55 | template 56 | auto identity_fn(T x) -> T 57 | { 58 | return x; 59 | } 60 | 61 | /** 62 | * pseudorandom number generators 63 | */ 64 | 65 | #define RANDOM_SEED 0x19260817 66 | 67 | template 68 | auto _rand(T min_value, T max_value) -> T 69 | { 70 | #ifdef RANDOM_SEED 71 | static std::mt19937 gen(RANDOM_SEED); 72 | #else 73 | static std::random_device rd; 74 | static std::mt19937 gen(rd()); 75 | #endif 76 | 77 | TDistribution dist(min_value, max_value); 78 | return dist(gen); 79 | } 80 | 81 | template 82 | auto randf(T min_value, T max_value) -> T 83 | { 84 | return _rand>(min_value, max_value); 85 | } 86 | 87 | template 88 | auto randi(T min_value, T max_value) -> T 89 | { 90 | return _rand>(min_value, max_value); 91 | } 92 | 93 | template 94 | auto randi() -> T 95 | { 96 | return randi(std::numeric_limits::min(), std::numeric_limits::max()); 97 | } 98 | 99 | /** 100 | * basic logging 101 | * 102 | * debug: write to stdout. 103 | * info: write to stdout. 104 | * warn: write to stderr. 105 | * notify: write to stderr, not controlled by the enable flag. 106 | * notify_char: write a single char to stderr. 107 | * status_line: write a line with clear and no '\n' to stdout. 108 | */ 109 | 110 | // ANSI Escape sequences for colors: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors 111 | #define BLACK "\033[30m" 112 | #define RED "\033[31m" 113 | #define GREEN "\033[32m" 114 | #define YELLOW "\033[33m" 115 | #define BLUE "\033[34m" 116 | #define MAGENTA "\033[35m" 117 | #define CYAN "\033[36m" 118 | #define WHITE "\033[37m" 119 | #define RESET "\033[0m" 120 | 121 | #define CLEAR_TO_RIGHT "\033[K" 122 | #define CLEAR_ALL "\033[2K" 123 | #define MOVE_TO_FRONT "\r" 124 | 125 | constexpr size_t LOG_MAX_BUFFER_SIZE = 1024; 126 | 127 | extern struct _log_ctx_t { 128 | std::mutex lock; 129 | bool debug_enabled; 130 | bool log_enabled; 131 | bool status_enabled; 132 | bool in_status_line; 133 | std::string char_buffer; 134 | } _ctx; 135 | 136 | void enable_logging(bool enable = true); 137 | void enable_debugging(bool enable = true); 138 | void enable_status_line(bool enable = true); 139 | 140 | void _log_debug(const char *message, ...); 141 | 142 | #define log_debug(...) { \ 143 | if (_ctx.log_enabled && _ctx.debug_enabled) \ 144 | _log_debug(__VA_ARGS__); \ 145 | } 146 | 147 | void info(const char *message, ...); 148 | void warn(const char *message, ...); 149 | void notify(const char *message, ...); 150 | void notify_char(char c); 151 | void status_line(const char *message, ...); 152 | 153 | void log_separator(); 154 | 155 | /** 156 | * simple timer 157 | * it displays simulation rates in KHz/MHz. 158 | */ 159 | 160 | class SimpleTimer { 161 | public: 162 | SimpleTimer(); 163 | ~SimpleTimer(); 164 | 165 | void update(uint64_t cycles); 166 | 167 | private: 168 | using clock = std::chrono::high_resolution_clock; 169 | 170 | clock::time_point t_start, t_end; 171 | uint64_t _cycles = 0; 172 | }; 173 | 174 | /** 175 | * report status in a separate thread. 176 | */ 177 | 178 | class ThreadWorker { 179 | public: 180 | using WorkerFn = std::function; 181 | 182 | ThreadWorker(); 183 | ThreadWorker(const ThreadWorker &) = delete; 184 | ThreadWorker(ThreadWorker &&); 185 | ThreadWorker( 186 | uint64_t interval_in_ms, bool repeat, 187 | const WorkerFn &fn, 188 | const WorkerFn &begin_fn, 189 | const WorkerFn &final_fn 190 | ); 191 | 192 | auto operator=(const ThreadWorker &) = delete; 193 | auto operator=(ThreadWorker &&)->ThreadWorker &; 194 | 195 | ~ThreadWorker(); 196 | 197 | static auto once( 198 | const WorkerFn &fn, 199 | const WorkerFn &begin_fn = [] {}, 200 | const WorkerFn &final_fn = [] {} 201 | )->ThreadWorker; 202 | static auto loop(const WorkerFn &fn, 203 | const WorkerFn &begin_fn = [] {}, 204 | const WorkerFn &final_fn = [] {} 205 | )->ThreadWorker; 206 | static auto at_interval( 207 | uint64_t interval_in_ms, 208 | const WorkerFn &fn, 209 | const WorkerFn &begin_fn = [] {}, 210 | const WorkerFn &final_fn = [] {} 211 | )->ThreadWorker; 212 | 213 | void stop(); 214 | 215 | private: 216 | bool stopped; 217 | volatile bool *flag; 218 | std::thread worker; 219 | }; 220 | 221 | /** 222 | * common typedefs/definitions 223 | */ 224 | 225 | enum AXISize : uint32_t { 226 | MSIZE1 = 0, 227 | MSIZE2 = 1, 228 | MSIZE4 = 2, 229 | MSIZE8 = 3, 230 | }; 231 | 232 | enum AXILength : uint32_t { 233 | MLEN1 = 0b00000000, 234 | MLEN2 = 0b00000001, 235 | MLEN4 = 0b00000011, 236 | MLEN8 = 0b00000111, 237 | MLEN16 = 0b00001111, 238 | MLEN32 = 0b00011111, 239 | MLEN64 = 0b00111111, 240 | MLEN128 = 0b01111111, 241 | MLEN256 = 0b11111111 242 | }; 243 | 244 | /** 245 | * assertion 246 | */ 247 | 248 | #include 249 | 250 | #ifndef NDEBUG 251 | 252 | #define _print_error_header(text) \ 253 | notify(RED "ERR!" RESET " %s.\n", text); 254 | #define _print_assert_location \ 255 | notify(" location: %s @L%d\n", __FILE__, __LINE__); 256 | #define _print_assert_function \ 257 | notify(" function: %s\n", __ASSERT_FUNCTION); 258 | #define _print_assert_predicate(predicate) \ 259 | notify(" predicate: '%s'\n", #predicate); 260 | #define _print_assert_message(...) { \ 261 | notify(" message: '"); \ 262 | notify(__VA_ARGS__); \ 263 | notify("'\n"); \ 264 | } 265 | #define _print_internal_notes { \ 266 | notify(BLUE "NOTE:" RESET " consider reporting this to TAs with full stack trace.\n"); \ 267 | notify(BLUE "NOTE:" RESET " use \"coredumpctl gdb\" and \"backtrace\" GDB command to dump stack trace.\n"); \ 268 | } 269 | #define _print_assert_notes { \ 270 | notify(BLUE "NOTE:" RESET " please try to understand the message and look into your FST trace.\n"); \ 271 | _print_internal_notes \ 272 | } 273 | 274 | #define ASSERT(predicate) { \ 275 | if (!static_cast(predicate)) { \ 276 | _print_error_header("assertion failed") \ 277 | _print_assert_location \ 278 | _print_assert_function \ 279 | _print_assert_predicate(predicate) \ 280 | _print_assert_notes \ 281 | abort(); \ 282 | } \ 283 | } 284 | 285 | #define asserts(predicate, ...) { \ 286 | if (!static_cast(predicate)) { \ 287 | _print_error_header("assertion failed") \ 288 | _print_assert_location \ 289 | _print_assert_function \ 290 | _print_assert_predicate(predicate) \ 291 | _print_assert_message(__VA_ARGS__) \ 292 | _print_assert_notes \ 293 | abort(); \ 294 | } \ 295 | } 296 | 297 | #define internal_assert(predicate, ...) { \ 298 | if (!static_cast(predicate)) { \ 299 | _print_error_header("internal assertion failed") \ 300 | _print_assert_location \ 301 | _print_assert_function \ 302 | _print_assert_predicate(predicate) \ 303 | _print_assert_message(__VA_ARGS__) \ 304 | _print_internal_notes \ 305 | abort(); \ 306 | } \ 307 | } 308 | 309 | #define panic(...) { \ 310 | _print_error_header("program panicked") \ 311 | _print_assert_location \ 312 | _print_assert_function \ 313 | _print_assert_message(__VA_ARGS__) \ 314 | _print_assert_notes \ 315 | abort(); \ 316 | } 317 | 318 | #define internal_panic(...) { \ 319 | _print_error_header("internal error") \ 320 | _print_assert_location \ 321 | _print_assert_function \ 322 | _print_assert_message(__VA_ARGS__) \ 323 | _print_internal_notes \ 324 | abort(); \ 325 | } 326 | 327 | #else 328 | #define asserts(...) 329 | #define internal_assert(...) 330 | #define panic(...) 331 | #define internal_panic(...) 332 | #endif 333 | -------------------------------------------------------------------------------- /vivado/src/Basys-3-Master.xdc: -------------------------------------------------------------------------------- 1 | ## This file is a general .xdc for the Basys3 rev B board 2 | ## To use it in a project: 3 | ## - uncomment the lines corresponding to used pins 4 | ## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project 5 | 6 | ## Clock signal 7 | set_property -dict {PACKAGE_PIN W5 IOSTANDARD LVCMOS33} [get_ports clk] 8 | create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk] 9 | 10 | 11 | ## Switches 12 | set_property -dict {PACKAGE_PIN V17 IOSTANDARD LVCMOS33} [get_ports {sw[0]}] 13 | set_property -dict {PACKAGE_PIN V16 IOSTANDARD LVCMOS33} [get_ports {sw[1]}] 14 | set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports {sw[2]}] 15 | set_property -dict {PACKAGE_PIN W17 IOSTANDARD LVCMOS33} [get_ports {sw[3]}] 16 | #set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports {sw[5]}] 17 | #set_property -dict { PACKAGE_PIN W14 IOSTANDARD LVCMOS33 } [get_ports {sw[6]}] 18 | #set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports {sw[7]}] 19 | #set_property -dict { PACKAGE_PIN V2 IOSTANDARD LVCMOS33 } [get_ports {sw[8]}] 20 | #set_property -dict { PACKAGE_PIN T3 IOSTANDARD LVCMOS33 } [get_ports {sw[9]}] 21 | #set_property -dict { PACKAGE_PIN T2 IOSTANDARD LVCMOS33 } [get_ports {sw[10]}] 22 | #set_property -dict { PACKAGE_PIN R3 IOSTANDARD LVCMOS33 } [get_ports {sw[11]}] 23 | #set_property -dict { PACKAGE_PIN W2 IOSTANDARD LVCMOS33 } [get_ports {sw[12]}] 24 | #set_property -dict { PACKAGE_PIN U1 IOSTANDARD LVCMOS33 } [get_ports {sw[13]}] 25 | #set_property -dict { PACKAGE_PIN T1 IOSTANDARD LVCMOS33 } [get_ports {sw[14]}] 26 | #set_property -dict { PACKAGE_PIN R2 IOSTANDARD LVCMOS33 } [get_ports {sw[15]}] 27 | 28 | 29 | ## LEDs 30 | set_property -dict {PACKAGE_PIN U16 IOSTANDARD LVCMOS33} [get_ports {led[0]}] 31 | set_property -dict {PACKAGE_PIN E19 IOSTANDARD LVCMOS33} [get_ports {led[1]}] 32 | set_property -dict {PACKAGE_PIN U19 IOSTANDARD LVCMOS33} [get_ports {led[2]}] 33 | set_property -dict {PACKAGE_PIN V19 IOSTANDARD LVCMOS33} [get_ports {led[3]}] 34 | #set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports {led[4]}] 35 | #set_property -dict { PACKAGE_PIN U15 IOSTANDARD LVCMOS33 } [get_ports {led[5]}] 36 | #set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports {led[6]}] 37 | #set_property -dict { PACKAGE_PIN V14 IOSTANDARD LVCMOS33 } [get_ports {led[7]}] 38 | #set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports {led[8]}] 39 | #set_property -dict { PACKAGE_PIN V3 IOSTANDARD LVCMOS33 } [get_ports {led[9]}] 40 | #set_property -dict { PACKAGE_PIN W3 IOSTANDARD LVCMOS33 } [get_ports {led[10]}] 41 | #set_property -dict { PACKAGE_PIN U3 IOSTANDARD LVCMOS33 } [get_ports {led[11]}] 42 | #set_property -dict { PACKAGE_PIN P3 IOSTANDARD LVCMOS33 } [get_ports {led[12]}] 43 | #set_property -dict { PACKAGE_PIN N3 IOSTANDARD LVCMOS33 } [get_ports {led[13]}] 44 | #set_property -dict { PACKAGE_PIN P1 IOSTANDARD LVCMOS33 } [get_ports {led[14]}] 45 | #set_property -dict { PACKAGE_PIN L1 IOSTANDARD LVCMOS33 } [get_ports {led[15]}] 46 | 47 | 48 | ##7 Segment Display 49 | #set_property -dict { PACKAGE_PIN W7 IOSTANDARD LVCMOS33 } [get_ports {seg[0]}] 50 | #set_property -dict { PACKAGE_PIN W6 IOSTANDARD LVCMOS33 } [get_ports {seg[1]}] 51 | #set_property -dict { PACKAGE_PIN U8 IOSTANDARD LVCMOS33 } [get_ports {seg[2]}] 52 | #set_property -dict { PACKAGE_PIN V8 IOSTANDARD LVCMOS33 } [get_ports {seg[3]}] 53 | #set_property -dict { PACKAGE_PIN U5 IOSTANDARD LVCMOS33 } [get_ports {seg[4]}] 54 | #set_property -dict { PACKAGE_PIN V5 IOSTANDARD LVCMOS33 } [get_ports {seg[5]}] 55 | #set_property -dict { PACKAGE_PIN U7 IOSTANDARD LVCMOS33 } [get_ports {seg[6]}] 56 | 57 | #set_property -dict { PACKAGE_PIN V7 IOSTANDARD LVCMOS33 } [get_ports dp] 58 | 59 | #set_property -dict { PACKAGE_PIN U2 IOSTANDARD LVCMOS33 } [get_ports {an[0]}] 60 | #set_property -dict { PACKAGE_PIN U4 IOSTANDARD LVCMOS33 } [get_ports {an[1]}] 61 | #set_property -dict { PACKAGE_PIN V4 IOSTANDARD LVCMOS33 } [get_ports {an[2]}] 62 | #set_property -dict { PACKAGE_PIN W4 IOSTANDARD LVCMOS33 } [get_ports {an[3]}] 63 | 64 | 65 | ##Buttons 66 | set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports btnC] 67 | #set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports btnU] 68 | #set_property -dict { PACKAGE_PIN W19 IOSTANDARD LVCMOS33 } [get_ports btnL] 69 | #set_property -dict { PACKAGE_PIN T17 IOSTANDARD LVCMOS33 } [get_ports btnR] 70 | #set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports btnD] 71 | 72 | 73 | ##Pmod Header JA 74 | #set_property -dict { PACKAGE_PIN J1 IOSTANDARD LVCMOS33 } [get_ports {JA[0]}];#Sch name = JA1 75 | #set_property -dict { PACKAGE_PIN L2 IOSTANDARD LVCMOS33 } [get_ports {JA[1]}];#Sch name = JA2 76 | #set_property -dict { PACKAGE_PIN J2 IOSTANDARD LVCMOS33 } [get_ports {JA[2]}];#Sch name = JA3 77 | #set_property -dict { PACKAGE_PIN G2 IOSTANDARD LVCMOS33 } [get_ports {JA[3]}];#Sch name = JA4 78 | #set_property -dict { PACKAGE_PIN H1 IOSTANDARD LVCMOS33 } [get_ports {JA[4]}];#Sch name = JA7 79 | #set_property -dict { PACKAGE_PIN K2 IOSTANDARD LVCMOS33 } [get_ports {JA[5]}];#Sch name = JA8 80 | #set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 } [get_ports {JA[6]}];#Sch name = JA9 81 | #set_property -dict { PACKAGE_PIN G3 IOSTANDARD LVCMOS33 } [get_ports {JA[7]}];#Sch name = JA10 82 | 83 | ##Pmod Header JB 84 | #set_property -dict { PACKAGE_PIN A14 IOSTANDARD LVCMOS33 } [get_ports {JB[0]}];#Sch name = JB1 85 | #set_property -dict { PACKAGE_PIN A16 IOSTANDARD LVCMOS33 } [get_ports {JB[1]}];#Sch name = JB2 86 | #set_property -dict { PACKAGE_PIN B15 IOSTANDARD LVCMOS33 } [get_ports {JB[2]}];#Sch name = JB3 87 | #set_property -dict { PACKAGE_PIN B16 IOSTANDARD LVCMOS33 } [get_ports {JB[3]}];#Sch name = JB4 88 | #set_property -dict { PACKAGE_PIN A15 IOSTANDARD LVCMOS33 } [get_ports {JB[4]}];#Sch name = JB7 89 | #set_property -dict { PACKAGE_PIN A17 IOSTANDARD LVCMOS33 } [get_ports {JB[5]}];#Sch name = JB8 90 | #set_property -dict { PACKAGE_PIN C15 IOSTANDARD LVCMOS33 } [get_ports {JB[6]}];#Sch name = JB9 91 | #set_property -dict { PACKAGE_PIN C16 IOSTANDARD LVCMOS33 } [get_ports {JB[7]}];#Sch name = JB10 92 | 93 | ##Pmod Header JC 94 | #set_property -dict { PACKAGE_PIN K17 IOSTANDARD LVCMOS33 } [get_ports {JC[0]}];#Sch name = JC1 95 | #set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports {JC[1]}];#Sch name = JC2 96 | #set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports {JC[2]}];#Sch name = JC3 97 | #set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports {JC[3]}];#Sch name = JC4 98 | #set_property -dict { PACKAGE_PIN L17 IOSTANDARD LVCMOS33 } [get_ports {JC[4]}];#Sch name = JC7 99 | #set_property -dict { PACKAGE_PIN M19 IOSTANDARD LVCMOS33 } [get_ports {JC[5]}];#Sch name = JC8 100 | #set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports {JC[6]}];#Sch name = JC9 101 | #set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports {JC[7]}];#Sch name = JC10 102 | 103 | ##Pmod Header JXADC 104 | #set_property -dict { PACKAGE_PIN J3 IOSTANDARD LVCMOS33 } [get_ports {JXADC[0]}];#Sch name = XA1_P 105 | #set_property -dict { PACKAGE_PIN L3 IOSTANDARD LVCMOS33 } [get_ports {JXADC[1]}];#Sch name = XA2_P 106 | #set_property -dict { PACKAGE_PIN M2 IOSTANDARD LVCMOS33 } [get_ports {JXADC[2]}];#Sch name = XA3_P 107 | #set_property -dict { PACKAGE_PIN N2 IOSTANDARD LVCMOS33 } [get_ports {JXADC[3]}];#Sch name = XA4_P 108 | #set_property -dict { PACKAGE_PIN K3 IOSTANDARD LVCMOS33 } [get_ports {JXADC[4]}];#Sch name = XA1_N 109 | #set_property -dict { PACKAGE_PIN M3 IOSTANDARD LVCMOS33 } [get_ports {JXADC[5]}];#Sch name = XA2_N 110 | #set_property -dict { PACKAGE_PIN M1 IOSTANDARD LVCMOS33 } [get_ports {JXADC[6]}];#Sch name = XA3_N 111 | #set_property -dict { PACKAGE_PIN N1 IOSTANDARD LVCMOS33 } [get_ports {JXADC[7]}];#Sch name = XA4_N 112 | 113 | 114 | ##VGA Connector 115 | #set_property -dict { PACKAGE_PIN G19 IOSTANDARD LVCMOS33 } [get_ports {vgaRed[0]}] 116 | #set_property -dict { PACKAGE_PIN H19 IOSTANDARD LVCMOS33 } [get_ports {vgaRed[1]}] 117 | #set_property -dict { PACKAGE_PIN J19 IOSTANDARD LVCMOS33 } [get_ports {vgaRed[2]}] 118 | #set_property -dict { PACKAGE_PIN N19 IOSTANDARD LVCMOS33 } [get_ports {vgaRed[3]}] 119 | #set_property -dict { PACKAGE_PIN N18 IOSTANDARD LVCMOS33 } [get_ports {vgaBlue[0]}] 120 | #set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 } [get_ports {vgaBlue[1]}] 121 | #set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports {vgaBlue[2]}] 122 | #set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports {vgaBlue[3]}] 123 | #set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports {vgaGreen[0]}] 124 | #set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS33 } [get_ports {vgaGreen[1]}] 125 | #set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports {vgaGreen[2]}] 126 | #set_property -dict { PACKAGE_PIN D17 IOSTANDARD LVCMOS33 } [get_ports {vgaGreen[3]}] 127 | #set_property -dict { PACKAGE_PIN P19 IOSTANDARD LVCMOS33 } [get_ports Hsync] 128 | #set_property -dict { PACKAGE_PIN R19 IOSTANDARD LVCMOS33 } [get_ports Vsync] 129 | 130 | 131 | ##USB-RS232 Interface 132 | set_property -dict {PACKAGE_PIN B18 IOSTANDARD LVCMOS33} [get_ports RsRx] 133 | set_property -dict {PACKAGE_PIN A18 IOSTANDARD LVCMOS33} [get_ports RsTx] 134 | 135 | 136 | ##USB HID (PS/2) 137 | #set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 PULLUP true } [get_ports PS2Clk] 138 | #set_property -dict { PACKAGE_PIN B17 IOSTANDARD LVCMOS33 PULLUP true } [get_ports PS2Data] 139 | 140 | 141 | ##Quad SPI Flash 142 | ##Note that CCLK_0 cannot be placed in 7 series devices. You can access it using the 143 | ##STARTUPE2 primitive. 144 | #set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports {QspiDB[0]}] 145 | #set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports {QspiDB[1]}] 146 | #set_property -dict { PACKAGE_PIN G18 IOSTANDARD LVCMOS33 } [get_ports {QspiDB[2]}] 147 | #set_property -dict { PACKAGE_PIN F18 IOSTANDARD LVCMOS33 } [get_ports {QspiDB[3]}] 148 | #set_property -dict { PACKAGE_PIN K19 IOSTANDARD LVCMOS33 } [get_ports QspiCSn] 149 | 150 | 151 | ## Configuration options, can be used for all designs 152 | set_property CONFIG_VOLTAGE 3.3 [current_design] 153 | set_property CFGBVS VCCO [current_design] 154 | 155 | 156 | set_false_path -from [get_clocks sys_clk_pin] -to [get_clocks -of_objects [get_pins soc_top_inst/clk_wiz_0/inst/mmcm_adv_inst/CLKOUT0]] 157 | set_false_path -from [get_clocks -of_objects [get_pins soc_top_inst/clk_wiz_0/inst/mmcm_adv_inst/CLKOUT0]] -to [get_clocks sys_clk_pin] 158 | -------------------------------------------------------------------------------- /vivado/test3/project/project_1.xpr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 157 | 158 | 159 | 160 | 161 | 163 | 164 | 165 | 166 | 167 | 170 | 171 | 173 | 174 | 176 | 177 | 179 | 180 | 182 | 183 | 185 | 186 | 188 | 189 | 190 | 191 | 192 | 193 | Vivado Synthesis Defaults 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | Default settings for Implementation. 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | default_dashboard 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /vivado/test2/project/project_1.xpr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 159 | 160 | 161 | 162 | 163 | 165 | 166 | 167 | 168 | 169 | 172 | 173 | 175 | 176 | 178 | 179 | 181 | 182 | 184 | 185 | 187 | 188 | 190 | 191 | 192 | 193 | 194 | 195 | Vivado Synthesis Defaults 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | Default settings for Implementation. 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | default_dashboard 249 | 250 | 251 | 252 | 253 | 254 | 255 | --------------------------------------------------------------------------------