├── .gitignore ├── sim ├── .gitattributes ├── .gitignore ├── Makefile └── tb_core.do ├── doc ├── core-arch.png ├── riscv-privileged-isa.pdf └── riscv-unprivileged-isa.pdf ├── .svls.toml ├── .gitmodules ├── Makefile ├── tools ├── Makefile └── tracedasm │ └── tracedasm.py ├── rtl ├── core │ ├── regfile.v │ ├── dba.v │ ├── bpu.v │ ├── pc.v │ ├── br_alu.v │ ├── alu.v │ ├── exc.v │ ├── pmp.v │ ├── imem.v │ ├── cu.v │ ├── dmem.v │ ├── pd.v │ ├── cmem.v │ ├── core.v │ └── csr.v └── config.vh ├── tb ├── mem.sv └── tb_core.sv ├── .svlint.toml ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # backup files 2 | *.bak 3 | -------------------------------------------------------------------------------- /sim/.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-generated 2 | -------------------------------------------------------------------------------- /sim/.gitignore: -------------------------------------------------------------------------------- 1 | dromajo/ 2 | trace/ 3 | *.hex 4 | *.lst 5 | -------------------------------------------------------------------------------- /doc/core-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiclu/rv6/HEAD/doc/core-arch.png -------------------------------------------------------------------------------- /.svls.toml: -------------------------------------------------------------------------------- 1 | [verilog] 2 | include_paths = ["hdl"] 3 | 4 | [option] 5 | linter = true 6 | -------------------------------------------------------------------------------- /doc/riscv-privileged-isa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiclu/rv6/HEAD/doc/riscv-privileged-isa.pdf -------------------------------------------------------------------------------- /doc/riscv-unprivileged-isa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiclu/rv6/HEAD/doc/riscv-unprivileged-isa.pdf -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/dromajo"] 2 | path = tools/dromajo 3 | url = https://github.com/kiclu/dromajo 4 | [submodule "tools/riscv-tests"] 5 | path = tools/riscv-tests 6 | url = https://github.com/riscv-software-src/riscv-tests 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export RISCV_TESTS = ${PWD}/tools/riscv-tests/target/share/riscv-tests/isa/ 2 | export DROMAJO = ${PWD}/tools/dromajo/build/dromajo 3 | export DROMAJO_COSIM_TEST = ${PWD}/tools/dromajo/build/dromajo_cosim_test 4 | 5 | export ROOT_DIR = ${PWD} 6 | 7 | # Simulation 8 | 9 | .PHONY: rtl vsim_tb_% vsim_tb_%_c 10 | 11 | rtl: 12 | @${MAKE} -C sim ${@} 13 | 14 | vsim_tb_%: 15 | @${MAKE} -C sim ${@} 16 | 17 | vsim_tb_%_c: 18 | @${MAKE} -C sim ${@} 19 | 20 | # Tools 21 | 22 | .PHONY: tools 23 | 24 | tools: 25 | @${MAKE} -C tools all 26 | 27 | # Clean 28 | 29 | .PHONY: clean 30 | 31 | clean: 32 | @${MAKE} -C sim clean 33 | -------------------------------------------------------------------------------- /tools/Makefile: -------------------------------------------------------------------------------- 1 | ifndef ROOT_DIR 2 | ROOT_DIR = ${PWD}/.. 3 | endif 4 | 5 | .PHONY: all 6 | 7 | all: riscv-tests/target dromajo/build 8 | 9 | riscv-tests/target: 10 | @cd riscv-tests/ && git submodule update --init --recursive 11 | @cd riscv-tests/ && autoconf && ./configure --prefix=${ROOT_DIR}/tools/riscv-tests/target && make && make install 12 | 13 | dromajo/build: 14 | @cd ${ROOT_DIR}/tools/dromajo/ && mkdir build/ && cd build/ && cmake -DCMAKE_BUILD_TYPE=Release .. && make 15 | 16 | .PHONY: clean clean_riscv_tests clean_dromajo 17 | 18 | clean: clean_riscv_tests clean_dromajo 19 | 20 | clean_riscv_tests: 21 | @rm -rf riscv-tests/target 22 | 23 | clean_dromajo: 24 | @rm -rf dromajo/build 25 | -------------------------------------------------------------------------------- /sim/Makefile: -------------------------------------------------------------------------------- 1 | DIR_WORK = work 2 | 3 | DIR_RTL = rtl 4 | DIR_TB = tb 5 | 6 | DIR_TRACE = trace 7 | DIR_DROMAJO = dromajo 8 | 9 | OPTS_RTL = -incr 10 | OPTS_TB = -incr -sv 11 | OPTS_TB += +define+RISCV_TESTS=\"${RISCV_TESTS}\" 12 | OPTS_TB += +define+DROMAJO_COSIM_TEST=\"${DROMAJO_COSIM_TEST}\" 13 | OPTS_TB += +define+DROMAJO_VERBOSE 14 | OPTS_TB += +define+ANSI_COLORS 15 | 16 | ifdef ELF 17 | OPTS_TB += +define+ELF=\"${ELF}\" 18 | else 19 | 20 | endif 21 | 22 | SOURCES_RTL = $(shell find ../${DIR_RTL} -name "*.v" -printf "%p ") 23 | SOURCES_TB = $(shell find ../${DIR_TB} -name "*.sv" -printf "%p ") 24 | 25 | .PHONY: rtl tb tb_% vsim_tb_% vsim_tb_%_c clean 26 | 27 | ${DIR_WORK}: 28 | @vlib ${DIR_WORK} 29 | 30 | rtl: | ${DIR_WORK} 31 | @vlog ${OPTS_RTL} ${SOURCES_RTL} 32 | 33 | tb: | ${DIR_WORK} 34 | @vlog ${OPTS_TB} ${SOURCES_TB} 35 | 36 | tb_%: rtl 37 | @vlog ${OPTS_TB} $(shell find ../${DIR_TB} -name "${@}.sv" -printf "%p ") 38 | 39 | vsim_tb_%: tb_% | tb ${DIR_TRACE} ${DIR_DROMAJO} 40 | @vsim ${<} -do '${<}.do' -do 'run -all; view wave;' 41 | 42 | vsim_tb_%_c: tb_% | tb ${DIR_TRACE} ${DIR_DROMAJO} 43 | @vsim -c ${<} -do 'run -all; quit -f;' 44 | 45 | ${DIR_TRACE}: 46 | @mkdir -p ${DIR_TRACE} 47 | 48 | ${DIR_DROMAJO}: 49 | @mkdir -p ${DIR_DROMAJO} 50 | 51 | clean: 52 | @rm -rf ${DIR_WORK} 53 | @rm -rf ${DIR_TRACE} 54 | @rm -rf ${DIR_DROMAJO} 55 | @rm -f transcript 56 | @rm -f vsim.wlf 57 | -------------------------------------------------------------------------------- /tools/tracedasm/tracedasm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | def find_asm(log): 5 | return "/opt/riscv/target/share/riscv-tests/isa/" + log.split('.')[0] + ".dump" 6 | 7 | def dasm(trace, m, f): 8 | for ln in trace: 9 | regex_res = re.search("(^\d \d 0{8}([\da-f]{8}) [\da-f]{8}.*)DASM.*", ln) 10 | if regex_res: 11 | if regex_res.group(2) in m: 12 | print(regex_res.group(1), m[regex_res.group(2)], file=f) 13 | else: 14 | print(regex_res.group(1).strip(), file=f) 15 | else: 16 | print(ln, end='', file=f) 17 | 18 | def get_insn_map(asm): 19 | m = {} 20 | for ln in asm: 21 | regex_res = re.search("\s*([\da-f]{8}):\s*[\da-f]{4,8}\s*(.*)\n", ln) 22 | if regex_res != None: 23 | m[regex_res.group(1)] = regex_res.group(2) 24 | return m 25 | 26 | 27 | if not os.path.exists("../../sim/dromajo/"): 28 | os.mkdir("../../sim/dromajo/") 29 | 30 | if not os.path.exists("dasm/"): 31 | os.mkdir("dasm/") 32 | 33 | dromajo_logs = os.listdir("../../sim/dromajo/") 34 | for filename in dromajo_logs: 35 | if not os.path.isfile("../../sim/dromajo/" + filename): 36 | continue 37 | try: 38 | f_trace = open("../../sim/dromajo/" + filename, "r") 39 | f_asm = open(find_asm(filename), "r") 40 | f = open("dasm/" + filename, "w") 41 | m = get_insn_map(f_asm) 42 | dasm(f_trace, m, f) 43 | except: 44 | print("Couldn't open " + filename) 45 | -------------------------------------------------------------------------------- /rtl/core/regfile.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | module regfile ( 18 | output [63:0] rs1_data, 19 | input [ 4:0] rs1, 20 | 21 | output [63:0] rs2_data, 22 | input [ 4:0] rs2, 23 | 24 | input [63:0] rd_data, 25 | input [ 4:0] rd, 26 | input we, 27 | 28 | input rst_n, 29 | input clk 30 | ); 31 | 32 | (* ram_style = "registers" *) 33 | reg [63:0] reg_data [1:31]; 34 | 35 | /* REGISTER OUTPUT */ 36 | 37 | wire [63:0] reg_data_out [0:31]; 38 | genvar i; 39 | generate 40 | for(i = 1; i < 32; i = i + 1) begin 41 | assign reg_data_out[i] = reg_data[i]; 42 | end 43 | endgenerate 44 | assign reg_data_out[0] = 64'b0; 45 | 46 | /* OUTPUT MUX */ 47 | 48 | assign rs1_data = reg_data_out[rs1]; 49 | assign rs2_data = reg_data_out[rs2]; 50 | 51 | /* REGISTER WRITE */ 52 | 53 | always @(posedge clk, negedge rst_n) begin 54 | if(!rst_n) begin : regfile_reset 55 | integer i; 56 | for(i = 1; i < 32; i = i + 1) reg_data[i] <= 64'b0; 57 | end 58 | else if(we) reg_data[rd] <= rd_data; 59 | end 60 | 61 | endmodule 62 | -------------------------------------------------------------------------------- /rtl/core/dba.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module dba ( 20 | // write bus 21 | input [63:0] b_addr_w, 22 | input [63:0] b_wdata_w, 23 | input [ 1:0] b_len_w, 24 | input b_wr_w, 25 | 26 | // read bus 27 | input [`CMEM_BLK_LEN-1:0] b_addr_c, 28 | output [ `CMEM_LINE-1:0] b_rdata_c, 29 | input b_rd_c, 30 | output b_dv_c, 31 | 32 | // external bus 33 | output [63:0] c_addr, 34 | output c_ext, 35 | 36 | input [`CMEM_LINE-1:0] c_rdata, 37 | output c_rd, 38 | input c_dv, 39 | 40 | output [63:0] c_wdata, 41 | output [ 1:0] c_len, 42 | output c_wr 43 | ); 44 | 45 | assign b_rdata_c = c_rdata; 46 | assign b_dv_c = c_dv; 47 | 48 | assign c_addr = !c_wr ? {b_addr_c, {`CMEM_OFFS_LEN{1'b0}}} : b_addr_w; 49 | assign c_ext = (c_addr < `EXT_MMAP_RANGE) && (c_rd || c_wr); 50 | assign c_rd = b_rd_c; 51 | 52 | assign c_wdata = b_wdata_w; 53 | assign c_len = b_len_w; 54 | assign c_wr = b_wr_w && !c_rd; 55 | 56 | endmodule 57 | -------------------------------------------------------------------------------- /rtl/core/bpu.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module bpu ( 20 | input [63:0] pc, 21 | input [31:0] ir, 22 | 23 | output jal_taken, 24 | output [63:0] jal_addr, 25 | 26 | output pr_taken, 27 | output [12:0] pr_offs, 28 | 29 | input rst_n 30 | ); 31 | 32 | `ifdef BPU_STATIC_TAKEN 33 | // static branch prediction, all jumps taken 34 | assign pr_taken = ir[6:0] == 7'b1100011; 35 | `endif 36 | 37 | `ifdef BPU_STATIC_NTAKEN 38 | // static branch prediction, all jumps not taken 39 | assign pr_taken = 0; 40 | `endif 41 | 42 | `ifdef BPU_STATIC_BTAKEN 43 | // static branch prediction, backward jumps taken, forward jumps not taken 44 | assign pr_taken = ir[6:0] == 7'b1100011 && ir[31]; 45 | `endif 46 | 47 | assign pr_offs = {ir[31], ir[7], ir[30:25], ir[11:8], 1'b0}; 48 | 49 | wire j_taken = ir[6:0] == 7'b1101111; 50 | wire j_taken_c = ir[15:13] == 3'b101 && ir[1:0] == 2'b01; 51 | 52 | wire [63:0] j_addr = pc + {{43{ir[31]}}, ir[31], ir[19:12], ir[20], ir[30:21], 1'b0}; 53 | wire [63:0] j_addr_c = pc + {{52{ir[12]}}, ir[12], ir[8], ir[10:9], ir[6], ir[7], ir[2], ir[11], ir[5:3], 1'b0}; 54 | 55 | assign jal_taken = j_taken | j_taken_c; 56 | assign jal_addr = j_taken ? j_addr : j_addr_c; 57 | 58 | endmodule 59 | -------------------------------------------------------------------------------- /rtl/core/pc.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module pc ( 20 | output reg [63:0] pc, 21 | 22 | input t_taken, 23 | input [63:0] t_addr, 24 | 25 | input jalr_taken, 26 | input [63:0] jalr_addr, 27 | 28 | input pr_miss, 29 | input [63:0] br_addr, 30 | 31 | input jal_taken, 32 | input [63:0] jal_addr, 33 | 34 | input pr_taken, 35 | input [12:0] pr_offs, 36 | 37 | input c_ins, 38 | input fence_i, 39 | input stall_if, 40 | input rst_n, 41 | input clk 42 | ); 43 | 44 | wire [63:0] pr_addr = pc + {{51{pr_offs[12]}}, pr_offs}; 45 | wire [63:0] n_pc = pc + (c_ins ? 64'h2 : 64'h4); 46 | 47 | always @(posedge clk, negedge rst_n) begin 48 | if(!rst_n) pc <= `RESET_VECTOR; 49 | else if(t_taken) pc <= t_addr; 50 | else if(jalr_taken) pc <= jalr_addr; 51 | else if(fence_i && pr_miss) pc <= br_addr; 52 | else if(!stall_if) begin 53 | if(pr_miss) pc <= br_addr; 54 | else if(jal_taken) pc <= jal_addr; 55 | else if(pr_taken) pc <= pr_addr; 56 | else pc <= n_pc; 57 | end 58 | end 59 | 60 | endmodule 61 | -------------------------------------------------------------------------------- /rtl/core/br_alu.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module br_alu ( 20 | input [63:0] pc, 21 | input [31:0] ir, 22 | 23 | input [63:0] rs1_data, 24 | input [63:0] rs2_data, 25 | 26 | output jalr_taken, 27 | output [63:0] jalr_addr, 28 | 29 | output pr_miss, 30 | output [63:0] br_addr, 31 | 32 | input pr_taken, 33 | 34 | input stall_id, 35 | input rst_n 36 | ); 37 | 38 | // JALR 39 | assign jalr_taken = !stall_id && ir[6:0] == `OP_JALR; 40 | assign jalr_addr = rs1_data + {{52{ir[31]}}, ir[31:21], 1'b0}; 41 | 42 | // branch offset calculation 43 | wire [63:0] br_offs = {{51{ir[31]}}, ir[31], ir[7], ir[30:25], ir[11:8], 1'b0}; 44 | 45 | // branch instruction check 46 | wire branch = ir[6:0] == `OP_BRANCH; 47 | 48 | // branch prediction miss check 49 | reg brc; 50 | assign pr_miss = !stall_id && pr_taken != brc && branch; 51 | assign br_addr = brc ? pc + br_offs : pc + 4; 52 | 53 | // branch condition evaluation 54 | wire signed [63:0] rs1_data_s = rs1_data; 55 | wire signed [63:0] rs2_data_s = rs2_data; 56 | always @(*) begin 57 | case(ir[14:12]) 58 | 3'b000: brc = (rs1_data == rs2_data); 59 | 3'b001: brc = (rs1_data != rs2_data); 60 | 3'b100: brc = (rs1_data_s < rs2_data_s); 61 | 3'b101: brc = (rs1_data_s >= rs2_data_s); 62 | 3'b110: brc = (rs1_data < rs2_data); 63 | 3'b111: brc = (rs1_data >= rs2_data); 64 | default: brc = 0; 65 | endcase 66 | end 67 | 68 | endmodule 69 | -------------------------------------------------------------------------------- /tb/mem.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../rtl/config.vh" 18 | 19 | `define TB_MEM_START_ADDR 64'h8000_0000 20 | `define TB_MEM_SIZE 64'h10_0000 21 | `define TB_MEM_DELAY 8 22 | 23 | module mem ( 24 | input [63:0] addr, 25 | input [63:0] wdata, 26 | output [`CMEM_LINE-1:0] rdata, 27 | input [ 1:0] len, 28 | input rd, 29 | input wr, 30 | output dv, 31 | input rst_n, 32 | input clk 33 | ); 34 | 35 | /* READ RISING EDGE */ 36 | 37 | reg rd_d; 38 | always @(posedge clk) rd_d <= rd; 39 | assign rd_re = rd && !rd_d; 40 | 41 | /* WRITE RISING EDGE */ 42 | 43 | reg wr_d; 44 | always @(posedge clk) wr_d <= wr; 45 | assign wr_re = wr && !wr_d; 46 | 47 | /* MEMORY MODEL */ 48 | 49 | reg [7:0] mem [`TB_MEM_START_ADDR:`TB_MEM_START_ADDR+`TB_MEM_SIZE]; 50 | 51 | reg [`CMEM_LINE-1:0] data_out_buffer [0:`TB_MEM_DELAY-1]; 52 | reg data_valid [0:`TB_MEM_DELAY-1]; 53 | 54 | always @(posedge clk) begin 55 | if(!rst_n) begin 56 | for(integer i = 0; i < `TB_MEM_DELAY; ++i) begin 57 | data_out_buffer[i] <= 'bZ; 58 | data_valid[i] <= 0; 59 | end 60 | end 61 | else begin 62 | if(wr_re) begin 63 | //$display( 64 | // "WRITE: %-16h @ %-16h; len=%3b", 65 | // wdata, 66 | // addr, 67 | // len 68 | //); 69 | for(integer i = 0; i < (2**len); ++i) begin 70 | mem[addr + i] <= wdata[8*i +: 8]; 71 | end 72 | end 73 | 74 | if(rd_re) begin 75 | //$display( 76 | // "READ: @ %-16h", 77 | // addr 78 | //); 79 | for(integer i = 0; i < `CMEM_LINE/8; ++i) begin 80 | data_out_buffer[`TB_MEM_DELAY-1][8*i +: 8] <= mem[addr + i]; 81 | end 82 | data_valid[`TB_MEM_DELAY-1] <= 1; 83 | end 84 | else begin 85 | data_out_buffer[`TB_MEM_DELAY-1] <= 'bZ; 86 | data_valid [`TB_MEM_DELAY-1] <= 0; 87 | end 88 | 89 | for(integer i = 0; i < `TB_MEM_DELAY - 1; ++i) begin 90 | data_out_buffer[i] <= data_out_buffer[i+1]; 91 | data_valid[i] <= data_valid[i+1]; 92 | end 93 | end 94 | end 95 | 96 | assign rdata = data_out_buffer[0]; 97 | assign dv = data_valid[0]; 98 | 99 | task read_hex(string filename); 100 | $readmemh(filename, mem, `TB_MEM_START_ADDR); 101 | for(integer i = 0; i < `TB_MEM_SIZE; ++i) begin 102 | if(mem[i] === 8'hX) mem[i] <= 8'h00; 103 | end 104 | endtask 105 | 106 | endmodule 107 | -------------------------------------------------------------------------------- /rtl/core/alu.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module alu ( 20 | input [63:0] a, 21 | input [63:0] b, 22 | output reg [63:0] alu_out, 23 | input [ 6:0] opcode, 24 | input [ 2:0] op, 25 | input mod 26 | ); 27 | 28 | wire w = opcode[3]; 29 | wire mod_sub = mod && opcode[5]; 30 | 31 | wire [63:0] op1 = w ? {{32{a[31]}}, a[31:0]} : a; 32 | wire [63:0] op2 = w ? {{32{b[31]}}, b[31:0]} : b; 33 | 34 | wire [5:0] shamt = {op2[5] & ~w, op2[4:0]}; 35 | 36 | /* ADD / SUB */ 37 | 38 | wire [63:0] d_add = mod_sub ? op1 - op2 : op1 + op2; 39 | wire [31:0] add_ext = w ? {32{d_add[31]}} : d_add[63:32]; 40 | 41 | /* SLL */ 42 | 43 | wire [63:0] d_sll = op1 << shamt; 44 | wire [31:0] sll_ext = w ? {32{d_sll[31]}} : d_sll[63:32]; 45 | 46 | /* SLT */ 47 | 48 | wire signed [63:0] op1_s = op1; 49 | wire signed [63:0] op2_s = op2; 50 | 51 | wire slt = op1_s < op2_s; 52 | wire [63:0] d_slt = {63'b0, slt}; 53 | wire [31:0] slt_ext = 32'b0; 54 | 55 | /* SLTU */ 56 | 57 | wire [63:0] d_sltu = {63'b0, op1 < op2}; 58 | wire [31:0] sltu_ext = 32'b0; 59 | 60 | /* XOR */ 61 | 62 | wire [63:0] d_xor = op1 ^ op2; 63 | wire [31:0] xor_ext = d_xor[63:32]; 64 | 65 | /* SRL / SRA */ 66 | 67 | wire signed [63:0] op1_sd = a; 68 | wire signed [63:0] op2_sd = b; 69 | wire signed [31:0] op1_sw = a[31:0]; 70 | wire signed [31:0] op2_sw = b[31:0]; 71 | 72 | wire [63:0] srl = op1_sd >> shamt; 73 | wire [63:0] sra = op1_sd >>> shamt; 74 | wire [31:0] srlw = op1_sw >> shamt; 75 | wire [31:0] sraw = op1_sw >>> shamt; 76 | wire [63:0] shr_d = mod ? sra : srl; 77 | wire [31:0] shr_w = mod ? sraw : srlw; 78 | wire [63:0] d_shr = w ? shr_w : shr_d; 79 | wire [31:0] shr_ext = w ? {32{mod ? sraw[31] : srlw[31]}} : d_shr[63:32]; 80 | 81 | /* OR */ 82 | 83 | wire [63:0] d_or = op1 | op2; 84 | wire [31:0] or_ext = d_or[63:32]; 85 | 86 | /* AND */ 87 | 88 | wire [63:0] d_and = op1 & op2; 89 | wire [31:0] and_ext = d_and[63:32]; 90 | 91 | /* OP MUX */ 92 | 93 | wire [63:0] alu_out_mux [0:7]; 94 | assign alu_out_mux[0] = d_add; 95 | assign alu_out_mux[1] = d_sll; 96 | assign alu_out_mux[2] = d_slt; 97 | assign alu_out_mux[3] = d_sltu; 98 | assign alu_out_mux[4] = d_xor; 99 | assign alu_out_mux[5] = d_shr; 100 | assign alu_out_mux[6] = d_or; 101 | assign alu_out_mux[7] = d_and; 102 | 103 | /* EXT MUX */ 104 | 105 | wire [31:0] alu_ext_mux [0:7]; 106 | assign alu_ext_mux[0] = add_ext; 107 | assign alu_ext_mux[1] = sll_ext; 108 | assign alu_ext_mux[2] = slt_ext; 109 | assign alu_ext_mux[3] = sltu_ext; 110 | assign alu_ext_mux[4] = xor_ext; 111 | assign alu_ext_mux[5] = shr_ext; 112 | assign alu_ext_mux[6] = or_ext; 113 | assign alu_ext_mux[7] = and_ext; 114 | 115 | always @(*) begin 116 | case(opcode) 117 | `OP_ALRI, `OP_ALRR, `OP_ALRIW, `OP_ALRRW: alu_out = {alu_ext_mux[op], alu_out_mux[op][31:0]}; 118 | `OP_LUI: alu_out = b; 119 | default: alu_out = a + b; 120 | endcase 121 | end 122 | 123 | endmodule 124 | -------------------------------------------------------------------------------- /rtl/core/exc.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `define OP_ECALL 32'h00000073 18 | `define OP_EBREAK 32'h00100073 19 | 20 | module exc ( 21 | input [ 1:0] priv, 22 | input [31:0] ir, 23 | 24 | output reg exc, 25 | output reg [63:0] exc_cause, 26 | output reg [63:0] exc_val, 27 | 28 | input exc_ii_if, 29 | input exc_ii_csr, 30 | input exc_pmp_iaf, 31 | input exc_pmp_laf, 32 | input exc_pmp_saf, 33 | input exc_dmem_lma, 34 | input exc_dmem_sma, 35 | 36 | input [63:0] dmem_addr, 37 | 38 | input flush_pd, 39 | input flush_id, 40 | input t_flush, 41 | 42 | input stall_if, 43 | input stall_pd, 44 | input stall_id, 45 | input stall_ex, 46 | input stall_mem, 47 | input stall_wb, 48 | 49 | input rst_n, 50 | input clk 51 | ); 52 | 53 | wire ecall = ir == `OP_ECALL; 54 | wire ebreak = ir == `OP_EBREAK; 55 | 56 | reg pd_exc; 57 | reg [ 5:0] pd_exc_cause; 58 | reg [63:0] pd_exc_val; 59 | 60 | always @(posedge clk) begin 61 | if(!rst_n) pd_exc <= 0; 62 | else if(flush_pd || t_flush) begin 63 | pd_exc <= 0; 64 | end 65 | else if(exc_pmp_iaf) begin 66 | pd_exc <= 1; 67 | pd_exc_cause <= 6'd1; 68 | pd_exc_val <= 64'h0; 69 | end 70 | else if(exc_ii_if) begin 71 | pd_exc <= 1; 72 | pd_exc_cause <= 6'd2; 73 | pd_exc_val <= 64'h0; 74 | end 75 | else if(!stall_pd) begin 76 | pd_exc <= 0; 77 | end 78 | end 79 | 80 | reg id_exc; 81 | reg [ 5:0] id_exc_cause; 82 | reg [63:0] id_exc_val; 83 | 84 | always @(posedge clk) begin 85 | if(!rst_n) id_exc <= 0; 86 | else if(flush_id || t_flush) begin 87 | id_exc <= 0; 88 | end 89 | else if(!stall_id) begin 90 | id_exc <= pd_exc; 91 | id_exc_cause <= pd_exc_cause; 92 | id_exc_val <= pd_exc_val; 93 | end 94 | end 95 | 96 | reg ex_exc; 97 | reg [ 5:0] ex_exc_cause; 98 | reg [63:0] ex_exc_val; 99 | 100 | always @(posedge clk) begin 101 | if(!rst_n) ex_exc <= 0; 102 | else if(t_flush) begin 103 | ex_exc <= 0; 104 | end 105 | else if(!stall_ex) begin 106 | ex_exc <= id_exc; 107 | ex_exc_cause <= id_exc_cause; 108 | ex_exc_val <= id_exc_val; 109 | end 110 | end 111 | 112 | reg mem_exc; 113 | reg [ 5:0] mem_exc_cause; 114 | reg [63:0] mem_exc_val; 115 | 116 | always @(posedge clk) begin 117 | if(!rst_n) mem_exc <= 0; 118 | else if(t_flush) begin 119 | mem_exc <= 0; 120 | end 121 | else if(!stall_mem) begin 122 | mem_exc <= ex_exc; 123 | mem_exc_cause <= ex_exc_cause; 124 | mem_exc_val <= ex_exc_val; 125 | end 126 | end 127 | 128 | always @(*) begin 129 | exc = 1; 130 | 131 | exc_cause = 64'h0; 132 | exc_val = 64'h0; 133 | 134 | if(mem_exc) begin 135 | exc_cause = mem_exc_cause; 136 | exc_val = mem_exc_val; 137 | end 138 | else if(exc_ii_csr) begin 139 | exc_cause = 64'd2; 140 | end 141 | else if(ecall) begin 142 | case(priv) 143 | 2'b00: exc_cause = 64'd8; 144 | 2'b01: exc_cause = 64'd9; 145 | 2'b11: exc_cause = 64'd11; 146 | endcase 147 | end 148 | else if(ebreak) begin 149 | exc_cause = 64'd3; 150 | end 151 | else if(exc_pmp_laf) begin 152 | exc_cause = 64'd5; 153 | end 154 | else if(exc_pmp_saf) begin 155 | exc_cause = 64'd7; 156 | end 157 | else if(exc_dmem_lma) begin 158 | exc_cause = 64'd4; 159 | exc_val = dmem_addr; 160 | end 161 | else if(exc_dmem_sma) begin 162 | exc_cause = 64'd6; 163 | exc_val = dmem_addr; 164 | end 165 | else exc = 0; 166 | end 167 | 168 | endmodule 169 | -------------------------------------------------------------------------------- /.svlint.toml: -------------------------------------------------------------------------------- 1 | [option] 2 | indent = 4 3 | [textrules] 4 | header_copyright = false 5 | style_directives = false 6 | style_semicolon = true 7 | style_textwidth = false 8 | [syntaxrules] 9 | action_block_with_side_effect = true 10 | blocking_assignment_in_always_at_edge = false 11 | blocking_assignment_in_always_ff = false 12 | blocking_assignment_in_always_latch = false 13 | case_default = true 14 | default_nettype_none = false 15 | enum_with_type = true 16 | eventlist_comma_always_ff = false 17 | eventlist_or = true 18 | explicit_case_default = true 19 | explicit_if_else = false 20 | function_same_as_system_function = true 21 | function_with_automatic = true 22 | general_always_level_sensitive = true 23 | general_always_no_edge = false 24 | genvar_declaration_in_loop = false 25 | genvar_declaration_out_loop = true 26 | inout_with_tri = true 27 | input_with_var = false 28 | interface_port_with_modport = false 29 | keyword_forbidden_always = false 30 | keyword_forbidden_always_comb = true 31 | keyword_forbidden_always_ff = true 32 | keyword_forbidden_always_latch = true 33 | keyword_forbidden_generate = false 34 | keyword_forbidden_logic = true 35 | keyword_forbidden_priority = true 36 | keyword_forbidden_unique = true 37 | keyword_forbidden_unique0 = true 38 | keyword_forbidden_wire_reg = false 39 | keyword_required_generate = true 40 | localparam_explicit_type = false 41 | localparam_type_twostate = false 42 | loop_statement_in_always_comb = false 43 | loop_statement_in_always_ff = false 44 | loop_statement_in_always_latch = false 45 | loop_variable_declaration = false 46 | module_ansi_forbidden = false 47 | module_nonansi_forbidden = true 48 | multiline_for_begin = true 49 | multiline_if_begin = true 50 | non_blocking_assignment_in_always_comb = false 51 | non_blocking_assignment_in_always_no_edge = false 52 | operator_case_equality = true 53 | operator_incdec = true 54 | operator_self_assignment = true 55 | output_with_var = false 56 | package_item_not_in_package = true 57 | parameter_default_value = true 58 | parameter_explicit_type = false 59 | parameter_in_generate = true 60 | parameter_in_package = true 61 | parameter_type_twostate = false 62 | sequential_block_in_always_comb = false 63 | sequential_block_in_always_ff = false 64 | sequential_block_in_always_latch = false 65 | generate_case_with_label = false 66 | generate_for_with_label = false 67 | generate_if_with_label = false 68 | lowercamelcase_interface = false 69 | lowercamelcase_module = false 70 | lowercamelcase_package = false 71 | prefix_inout = false 72 | prefix_input = false 73 | prefix_instance = false 74 | prefix_interface = false 75 | prefix_module = false 76 | prefix_output = false 77 | prefix_package = false 78 | re_forbidden_assert = false 79 | re_forbidden_assert_property = false 80 | re_forbidden_checker = false 81 | re_forbidden_class = false 82 | re_forbidden_function = false 83 | re_forbidden_generateblock = false 84 | re_forbidden_genvar = false 85 | re_forbidden_instance = false 86 | re_forbidden_interface = false 87 | re_forbidden_localparam = false 88 | re_forbidden_modport = false 89 | re_forbidden_module_ansi = false 90 | re_forbidden_module_nonansi = false 91 | re_forbidden_package = false 92 | re_forbidden_parameter = false 93 | re_forbidden_port_inout = false 94 | re_forbidden_port_input = false 95 | re_forbidden_port_interface = false 96 | re_forbidden_port_output = false 97 | re_forbidden_port_ref = false 98 | re_forbidden_program = false 99 | re_forbidden_property = false 100 | re_forbidden_sequence = false 101 | re_forbidden_task = false 102 | re_forbidden_var_class = false 103 | re_forbidden_var_classmethod = false 104 | re_required_assert = false 105 | re_required_assert_property = false 106 | re_required_checker = false 107 | re_required_class = false 108 | re_required_function = false 109 | re_required_generateblock = false 110 | re_required_genvar = false 111 | re_required_instance = false 112 | re_required_interface = false 113 | re_required_localparam = false 114 | re_required_modport = false 115 | re_required_module_ansi = false 116 | re_required_module_nonansi = false 117 | re_required_package = false 118 | re_required_parameter = false 119 | re_required_port_inout = false 120 | re_required_port_input = false 121 | re_required_port_interface = false 122 | re_required_port_output = false 123 | re_required_port_ref = false 124 | re_required_program = false 125 | re_required_property = false 126 | re_required_sequence = false 127 | re_required_task = false 128 | re_required_var_class = false 129 | re_required_var_classmethod = false 130 | uppercamelcase_interface = false 131 | uppercamelcase_module = false 132 | uppercamelcase_package = false 133 | style_commaleading = false 134 | style_indent = false 135 | style_keyword_0or1space = false 136 | style_keyword_0space = false 137 | style_keyword_1or2space = false 138 | style_keyword_1space = false 139 | style_keyword_construct = false 140 | style_keyword_datatype = false 141 | style_keyword_end = true 142 | style_keyword_maybelabel = false 143 | style_keyword_new = false 144 | style_keyword_newline = false 145 | style_operator_arithmetic = false 146 | style_operator_arithmetic_leading_space = false 147 | style_operator_boolean = false 148 | style_operator_boolean_leading_space = false 149 | style_operator_integer = false 150 | style_operator_integer_leading_space = false 151 | style_operator_unary = true 152 | style_trailingwhitespace = true 153 | tab_character = true 154 | -------------------------------------------------------------------------------- /rtl/config.vh: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | /*----------------------------------------------------------------------------*/ 18 | 19 | `define RESET_VECTOR 64'h8000_0000 20 | 21 | /*----------------------------------------------------------------------------*/ 22 | 23 | /* CACHE PARAMETERS */ 24 | 25 | `define EXT_MMAP_RANGE 64'h8000_0000 26 | 27 | // L1i cache 28 | `define IMEM_SET_ASSOC 29 | `define IMEM_LINE 128 30 | `define IMEM_SETS 2 31 | `define IMEM_WAYS 2 32 | `define IMEM_READ_VALID_DELAY 2 33 | 34 | // L1d cache 35 | `define DMEM_SET_ASSOC 36 | `define DMEM_LINE 128 37 | `define DMEM_SETS 2 38 | `define DMEM_WAYS 2 39 | `define DMEM_READ_VALID_DELAY 2 40 | 41 | // L2 cache 42 | `define CMEM_SET_ASSOC 43 | `define CMEM_LINE 256 44 | `define CMEM_SETS 2 45 | `define CMEM_WAYS 2 46 | `define CMEM_READ_VALID_DELAY 2 47 | 48 | /*----------------------------------------------------------------------------*/ 49 | 50 | /* CACHE MISALIGNED DATA */ 51 | 52 | `define DMEM_MA_NATURAL 53 | 54 | /*----------------------------------------------------------------------------*/ 55 | 56 | /* BRANCH PREDICTION */ 57 | 58 | `define BPU_STATIC_BTAKEN 59 | 60 | /*----------------------------------------------------------------------------*/ 61 | 62 | /* OPCODES */ 63 | 64 | `define OP_LUI 7'b0110111 65 | `define OP_AUIPC 7'b0010111 66 | `define OP_JAL 7'b1101111 67 | `define OP_JALR 7'b1100111 68 | `define OP_BRANCH 7'b1100011 69 | `define OP_LOAD 7'b0000011 70 | `define OP_STORE 7'b0100011 71 | `define OP_ALRI 7'b0010011 72 | `define OP_ALRR 7'b0110011 73 | `define OP_ALRIW 7'b0011011 74 | `define OP_ALRRW 7'b0111011 75 | `define OP_AMO 7'b0101111 76 | `define OP_FENCE 7'b0001111 77 | `define OP_SYSTEM 7'b1110011 78 | 79 | `define NOP 32'h13 80 | 81 | /*----------------------------------------------------------------------------*/ 82 | 83 | `define XLEN 64 84 | 85 | `ifdef IMEM_SET_ASSOC 86 | `define IMEM_LINES `IMEM_SETS * `IMEM_WAYS 87 | `define IMEM_SET_LEN $clog2(`IMEM_SETS) 88 | `define IMEM_WAY_LEN $clog2(`IMEM_WAYS) 89 | `define IMEM_OFFS_LEN $clog2(`IMEM_LINE/8) 90 | `define IMEM_BLK_LEN (`XLEN - `IMEM_OFFS_LEN ) 91 | `define IMEM_TAG_LEN (`XLEN - `IMEM_OFFS_LEN - `IMEM_SET_LEN) 92 | `define IMEM_ADDR_TAG_RANGE `XLEN - 1 -: `IMEM_TAG_LEN 93 | `define IMEM_ADDR_SET_RANGE `XLEN - `IMEM_TAG_LEN - 1 -: `IMEM_SET_LEN 94 | `define IMEM_ADDR_OFFS_RANGE `XLEN - `IMEM_BLK_LEN - 1 -: `IMEM_OFFS_LEN 95 | `endif//IMEM_SET_ASSOC 96 | 97 | /*----------------------------------------------------------------------------*/ 98 | 99 | `ifdef DMEM_SET_ASSOC 100 | `define DMEM_LINES `DMEM_SETS * `DMEM_WAYS 101 | `define DMEM_SET_LEN $clog2(`DMEM_SETS) 102 | `define DMEM_WAY_LEN $clog2(`DMEM_WAYS) 103 | `define DMEM_OFFS_LEN $clog2(`DMEM_LINE/8) 104 | `define DMEM_BLK_LEN (`XLEN - `DMEM_OFFS_LEN ) 105 | `define DMEM_TAG_LEN (`XLEN - `DMEM_OFFS_LEN - `DMEM_SET_LEN) 106 | `define DMEM_ADDR_TAG_RANGE `XLEN - 1 -: `DMEM_TAG_LEN 107 | `define DMEM_ADDR_SET_RANGE `XLEN - `DMEM_TAG_LEN - 1 -: `DMEM_SET_LEN 108 | `define DMEM_ADDR_OFFS_RANGE `XLEN - `DMEM_BLK_LEN - 1 -: `DMEM_OFFS_LEN 109 | `endif//DMEM_SET_ASSOC 110 | 111 | /*----------------------------------------------------------------------------*/ 112 | 113 | `ifdef CMEM_SET_ASSOC 114 | `define CMEM_LINES `CMEM_SETS * `CMEM_WAYS 115 | `define CMEM_SET_LEN $clog2(`CMEM_SETS) 116 | `define CMEM_WAY_LEN $clog2(`CMEM_WAYS) 117 | `define CMEM_OFFS_LEN $clog2(`CMEM_LINE/8) 118 | `define CMEM_TAG_LEN (`XLEN - `CMEM_OFFS_LEN - `CMEM_SET_LEN) 119 | `define CMEM_BLK_LEN `CMEM_TAG_LEN + `CMEM_SET_LEN 120 | 121 | `define CMEM_I_OFFS_LEN `CMEM_OFFS_LEN - `IMEM_OFFS_LEN 122 | `define CMEM_I_ADDR_TAG_RANGE `IMEM_BLK_LEN - 1 -: `CMEM_TAG_LEN 123 | `define CMEM_I_ADDR_SET_RANGE `IMEM_BLK_LEN - `CMEM_TAG_LEN - 1 -: `CMEM_SET_LEN 124 | `define CMEM_I_ADDR_OFFS_RANGE `CMEM_I_OFFS_LEN - 1 : 0 125 | 126 | `define CMEM_D_OFFS_LEN `CMEM_OFFS_LEN - `DMEM_OFFS_LEN 127 | `define CMEM_D_ADDR_TAG_RANGE `DMEM_BLK_LEN - 1 -: `CMEM_TAG_LEN 128 | `define CMEM_D_ADDR_SET_RANGE `DMEM_BLK_LEN - `CMEM_TAG_LEN - 1 -: `CMEM_SET_LEN 129 | `define CMEM_D_ADDR_OFFS_RANGE `CMEM_D_OFFS_LEN - 1 : 0 130 | 131 | `define CMEM_W_OFFS_LEN `CMEM_OFFS_LEN 132 | `define CMEM_W_ADDR_TAG_RANGE `XLEN - 1 -: `CMEM_TAG_LEN 133 | `define CMEM_W_ADDR_SET_RANGE `XLEN - `CMEM_TAG_LEN - 1 -: `CMEM_SET_LEN 134 | `define CMEM_W_ADDR_OFFS_RANGE `CMEM_W_OFFS_LEN - 1 : 0 135 | 136 | `endif//CMEM_SET_ASSOC 137 | 138 | /*----------------------------------------------------------------------------*/ 139 | -------------------------------------------------------------------------------- /rtl/core/pmp.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `define PHY_ADDR 64 18 | 19 | `include "../config.vh" 20 | 21 | module pmp ( 22 | input [`PHY_ADDR-1:0] b_addr_w_p, 23 | input b_wr_w_p, 24 | input [`IMEM_BLK_LEN-1:0] b_addr_i_p, 25 | input b_rd_i_p, 26 | input [`DMEM_BLK_LEN-1:0] b_addr_d_p, 27 | input b_rd_d_p, 28 | 29 | input [ 1:0] priv, 30 | 31 | input [63:0] pmpcfg0, 32 | input [63:0] pmpcfg2, 33 | 34 | input [63:0] pmpaddr0, 35 | input [63:0] pmpaddr1, 36 | input [63:0] pmpaddr2, 37 | input [63:0] pmpaddr3, 38 | input [63:0] pmpaddr4, 39 | input [63:0] pmpaddr5, 40 | input [63:0] pmpaddr6, 41 | input [63:0] pmpaddr7, 42 | input [63:0] pmpaddr8, 43 | input [63:0] pmpaddr9, 44 | input [63:0] pmpaddr10, 45 | input [63:0] pmpaddr11, 46 | input [63:0] pmpaddr12, 47 | input [63:0] pmpaddr13, 48 | input [63:0] pmpaddr14, 49 | input [63:0] pmpaddr15, 50 | 51 | output exc_pmp_iaf, 52 | output exc_pmp_laf, 53 | output exc_pmp_saf, 54 | 55 | input rst_n, 56 | input clk 57 | ); 58 | 59 | wire [ 7:0] pmpcfg [0:15]; 60 | wire [63:0] pmpaddr [0:15]; 61 | 62 | reg [15:0] access_i; 63 | reg [15:0] access_d; 64 | reg [15:0] access_w; 65 | 66 | wire [63:0] napot_mask [0:15]; 67 | 68 | wire pmp_iaf_oob = |b_addr_i_p[63-`IMEM_OFFS_LEN -: 8] && b_rd_i_p; 69 | wire pmp_laf_oob = |b_addr_d_p[63-`DMEM_OFFS_LEN -: 8] && b_rd_d_p; 70 | wire pmp_saf_oob = |b_addr_w_p[63:56] && b_wr_w_p; 71 | 72 | assign exc_pmp_iaf = (~|access_i && b_rd_i_p && priv != 2'b11) || pmp_iaf_oob; 73 | assign exc_pmp_laf = (~|access_d && b_rd_d_p && priv != 2'b11) || pmp_laf_oob; 74 | assign exc_pmp_saf = (~|access_w && b_wr_w_p && priv != 2'b11) || pmp_saf_oob; 75 | 76 | wire [63:0] b_addr_w = b_addr_w_p; 77 | wire [63:0] b_addr_i = {b_addr_i_p, {`IMEM_OFFS_LEN{1'b0}}}; 78 | wire [63:0] b_addr_d = {b_addr_d_p, {`DMEM_OFFS_LEN{1'b0}}}; 79 | 80 | genvar i; 81 | generate 82 | for(i = 0; i < 16; i = i + 1) begin 83 | always @(*) begin 84 | case(pmpcfg[i][4:3]) 85 | // OFF 86 | 2'b00: begin 87 | access_w[i] = 0; 88 | access_i[i] = 0; 89 | access_d[i] = 0; 90 | end 91 | // TOR 92 | 2'b01: begin 93 | access_w[i] = 0; 94 | access_i[i] = 0; 95 | access_d[i] = 0; 96 | end 97 | // NA4 98 | 2'b10: begin 99 | access_w[i] = 0; 100 | access_i[i] = 0; 101 | access_d[i] = 0; 102 | end 103 | // NAPOT 104 | 2'b11: begin 105 | access_w[i] = &(~(b_addr_w ^ {pmpaddr[i][61:0], 2'b00}) | napot_mask[i]) && pmpcfg[i][1]; 106 | access_i[i] = &(~(b_addr_i ^ {pmpaddr[i][61:0], 2'b00}) | napot_mask[i]) && pmpcfg[i][2]; 107 | access_d[i] = &(~(b_addr_d ^ {pmpaddr[i][61:0], 2'b00}) | napot_mask[i]) && pmpcfg[i][0]; 108 | end 109 | endcase 110 | end 111 | end 112 | endgenerate 113 | 114 | /* NAPOT */ 115 | 116 | genvar j; 117 | generate 118 | for(i = 0; i < 16; i = i + 1) begin 119 | assign napot_mask[i][2:0] = 3'b111; 120 | for(j = 4; j < 64; j = j + 1) begin 121 | assign napot_mask[i][j] = napot_mask[i][j-1] && pmpaddr[i][j-2]; 122 | end 123 | end 124 | endgenerate 125 | 126 | /* INPUT SIGNALS */ 127 | 128 | generate 129 | for(i = 0; i < 8; i = i + 1) begin 130 | assign pmpcfg[i] = pmpcfg0[i<<3 +: 8]; 131 | assign pmpcfg[i+8] = pmpcfg2[i<<3 +: 8]; 132 | end 133 | endgenerate 134 | 135 | assign pmpaddr[0] = pmpaddr0; 136 | assign pmpaddr[1] = pmpaddr1; 137 | assign pmpaddr[2] = pmpaddr2; 138 | assign pmpaddr[3] = pmpaddr3; 139 | assign pmpaddr[4] = pmpaddr4; 140 | assign pmpaddr[5] = pmpaddr5; 141 | assign pmpaddr[6] = pmpaddr6; 142 | assign pmpaddr[7] = pmpaddr7; 143 | assign pmpaddr[8] = pmpaddr8; 144 | assign pmpaddr[9] = pmpaddr9; 145 | assign pmpaddr[10] = pmpaddr10; 146 | assign pmpaddr[11] = pmpaddr11; 147 | assign pmpaddr[12] = pmpaddr12; 148 | assign pmpaddr[13] = pmpaddr13; 149 | assign pmpaddr[14] = pmpaddr14; 150 | assign pmpaddr[15] = pmpaddr15; 151 | 152 | endmodule 153 | -------------------------------------------------------------------------------- /rtl/core/imem.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | /* 18 | * Configurable L1 instruction cache 19 | */ 20 | 21 | `include "../config.vh" 22 | 23 | module imem ( 24 | input [63:0] pc, 25 | output reg [31:0] ir, 26 | 27 | output reg [`IMEM_BLK_LEN-1:0] b_addr_i, 28 | input [`IMEM_LINE-1:0] b_data_i, 29 | output reg b_rd_i, 30 | input b_dv_i, 31 | 32 | input fence_i, 33 | output stall_imem, 34 | input rst_n, 35 | input clk 36 | ); 37 | 38 | `ifdef IMEM_SET_ASSOC 39 | 40 | wire [63:0] addr; 41 | 42 | wire [ `IMEM_TAG_LEN-1:0] addr_tag = addr[`IMEM_ADDR_TAG_RANGE ]; 43 | wire [ `IMEM_SET_LEN-1:0] addr_set = addr[`IMEM_ADDR_SET_RANGE ]; 44 | wire [`IMEM_OFFS_LEN-1:0] addr_offs = addr[`IMEM_ADDR_OFFS_RANGE]; 45 | 46 | (* ram_style = "block" *) 47 | reg [ `IMEM_LINE-1:0] data [0:`IMEM_LINES-1]; 48 | reg [`IMEM_TAG_LEN-1:0] tag [0:`IMEM_LINES-1]; 49 | reg v [0:`IMEM_LINES-1]; 50 | 51 | reg [`IMEM_WAY_LEN-1:0] re; 52 | reg hit; 53 | 54 | /* BRAM WRITE */ 55 | 56 | reg [`IMEM_TAG_LEN-1:0] tag_d; 57 | reg [`IMEM_SET_LEN-1:0] set_d; 58 | reg [`IMEM_WAY_LEN-1:0] way_d; 59 | reg [ `IMEM_LINE-1:0] d; 60 | reg wre; 61 | always @(posedge clk) if(wre) data[`IMEM_WAYS * set_d + way_d] <= d; 62 | 63 | /* BRAM READ */ 64 | 65 | reg [`IMEM_TAG_LEN-1:0] tag_q; 66 | reg [`IMEM_SET_LEN-1:0] set_q; 67 | reg [`IMEM_WAY_LEN-1:0] way_q; 68 | reg [ `IMEM_LINE-1:0] q; 69 | reg rde; 70 | always @(posedge clk) if(rde) q <= data[`IMEM_WAYS * set_q + way_q]; 71 | 72 | /* READ BUFFER */ 73 | 74 | reg [`IMEM_TAG_LEN-1:0] rb_tag; 75 | reg [`IMEM_SET_LEN-1:0] rb_set; 76 | reg rb_v; 77 | 78 | wire rb_hit = rb_tag == addr_tag && rb_set == addr_set && rb_v; 79 | 80 | /* MISALIGNED ACCESS */ 81 | 82 | wire [63:0] ma_pc = pc + 2; 83 | reg [15:0] ma_reg; 84 | reg ma_pend; 85 | reg ma_acc; 86 | wire ma_re; 87 | wire ma; 88 | 89 | /* FSM */ 90 | 91 | reg [3:0] ld_cnt; 92 | 93 | reg [1:0] imem_fsm_state; 94 | reg [1:0] imem_fsm_state_next; 95 | 96 | `define IMEM_S_READY 2'd0 97 | `define IMEM_S_FETCH 2'd1 98 | `define IMEM_S_LOAD 2'd2 99 | `define IMEM_S_MA 2'd3 100 | 101 | /* FSM */ 102 | 103 | always @(*) begin 104 | b_rd_i = 0; 105 | wre = 0; 106 | rde = 0; 107 | way_d = 0; 108 | 109 | imem_fsm_state_next = imem_fsm_state; 110 | case(imem_fsm_state) 111 | `IMEM_S_READY: begin 112 | if(!hit) imem_fsm_state_next = `IMEM_S_FETCH; 113 | if( hit) imem_fsm_state_next = `IMEM_S_LOAD; 114 | if(rb_hit && !ma_re) imem_fsm_state_next = `IMEM_S_READY; 115 | if(rb_hit && ma_re) imem_fsm_state_next = `IMEM_S_MA; 116 | end 117 | `IMEM_S_FETCH: begin 118 | b_rd_i = 1; 119 | way_d = re; 120 | wre = b_dv_i; 121 | 122 | if(b_dv_i) imem_fsm_state_next = ma_pend ? `IMEM_S_MA : `IMEM_S_LOAD; 123 | end 124 | `IMEM_S_LOAD: begin 125 | rde = ld_cnt == `IMEM_READ_VALID_DELAY; 126 | if(!hit) imem_fsm_state_next = `IMEM_S_FETCH; 127 | 128 | if(!ld_cnt) imem_fsm_state_next = ma_pend ? `IMEM_S_MA : `IMEM_S_READY; 129 | end 130 | `IMEM_S_MA: begin 131 | imem_fsm_state_next = hit ? `IMEM_S_LOAD : `IMEM_S_FETCH; 132 | end 133 | endcase 134 | end 135 | 136 | /* FSM UPDATE */ 137 | 138 | always @(posedge clk) begin 139 | if(!rst_n) imem_fsm_state <= `IMEM_S_READY; 140 | else imem_fsm_state <= imem_fsm_state_next; 141 | end 142 | 143 | /* READ BUFFER */ 144 | 145 | always @(posedge clk) begin 146 | if(!rst_n) begin 147 | rb_v <= 0; 148 | end 149 | else if(ld_cnt == 0 && imem_fsm_state == `IMEM_S_LOAD) begin 150 | rb_tag <= addr_tag; 151 | rb_set <= addr_set; 152 | rb_v <= 1; 153 | end 154 | ld_cnt <= imem_fsm_state == `IMEM_S_LOAD ? ld_cnt - 1 : `IMEM_READ_VALID_DELAY; 155 | end 156 | 157 | /* REQUEST ADDRESS */ 158 | 159 | always @(*) begin 160 | b_addr_i = {addr_tag, addr_set}; 161 | end 162 | 163 | /* METADATA UPDATE */ 164 | 165 | always @(posedge clk) begin 166 | if(!rst_n || fence_i) begin : imem_reset 167 | integer i; 168 | for(i = 0; i < `IMEM_LINES; i = i + 1) begin 169 | tag[i] <= 0; 170 | v [i] <= 0; 171 | end 172 | end 173 | else begin 174 | if(wre) begin 175 | tag[`IMEM_WAYS * set_d + way_d] <= tag_d; 176 | v [`IMEM_WAYS * set_d + way_d] <= 1; 177 | end 178 | end 179 | end 180 | 181 | /* INPUT MUX */ 182 | 183 | always @(*) d = b_data_i; 184 | 185 | /* OUTPUT MUX */ 186 | 187 | always @(*) begin 188 | if(ma) ir = {q[15:0], ma_reg}; 189 | else if(rb_hit) ir = q[8*addr_offs +: 32]; 190 | else ir = 32'h00000013; 191 | end 192 | 193 | /* HIT DETECTION */ 194 | 195 | always @(*) begin : imem_cache_hit 196 | integer w; 197 | way_q = 'bZ; hit = 0; 198 | for(w = 0; w < `IMEM_WAYS; w = w + 1) begin 199 | if(tag[`IMEM_WAYS * set_q + w] == tag_q && v[`IMEM_WAYS * set_q + w]) begin 200 | way_q = w; hit = 1; 201 | end 202 | end 203 | end 204 | 205 | /* BRAM READ */ 206 | 207 | always @(*) begin 208 | tag_q = addr_tag; 209 | set_q = addr_set; 210 | end 211 | 212 | /* BRAM WRITE */ 213 | 214 | always @(*) begin 215 | tag_d = addr_tag; 216 | set_d = addr_set; 217 | end 218 | 219 | /* MISALIGNED ACCESS */ 220 | 221 | always @(posedge clk) begin 222 | if(imem_fsm_state == `IMEM_S_READY) begin 223 | ma_pend <= ma_re; 224 | end 225 | else if(imem_fsm_state == `IMEM_S_MA) begin 226 | ma_reg <= q[`IMEM_LINE-1 -: 16]; 227 | ma_pend <= 0; 228 | ma_acc <= 1; 229 | end 230 | else if(imem_fsm_state_next == `IMEM_S_READY) begin 231 | ma_pend <= 0; 232 | ma_acc <= 0; 233 | end 234 | end 235 | 236 | reg ma_d; 237 | always @(posedge clk) ma_d <= ma; 238 | 239 | assign ma = ma_pc[63 -: `IMEM_BLK_LEN] != pc[63 -: `IMEM_BLK_LEN]; 240 | assign ma_re = ma && !ma_d; 241 | 242 | assign addr = ma_acc ? ma_pc : pc; 243 | 244 | `endif//IMEM_SET_ASSOC 245 | 246 | assign stall_imem = imem_fsm_state != `IMEM_S_READY || imem_fsm_state_next != `IMEM_S_READY || !rb_hit; 247 | 248 | // TODO: 249 | /* REPLACEMENT ENTRY */ 250 | 251 | always @(posedge clk) re <= $random() % `DMEM_WAYS; 252 | 253 | endmodule 254 | -------------------------------------------------------------------------------- /rtl/core/cu.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module cu ( 20 | input [31:0] ir_if, 21 | input [31:0] ir_id, 22 | input [31:0] ir_ex, 23 | input [31:0] ir_mem, 24 | input [31:0] ir_wb, 25 | 26 | // stage stall signals 27 | output stall_if, 28 | output stall_pd, 29 | output stall_id, 30 | output stall_ex, 31 | output stall_mem, 32 | output stall_wb, 33 | 34 | // extern core stall 35 | input c_stall, 36 | 37 | // cache stall signals 38 | input stall_imem, 39 | input stall_dmem, 40 | 41 | // instruction fence signal 42 | output fence_i, 43 | 44 | // atomic instruction signals 45 | input amo_req, 46 | input amo_ack, 47 | 48 | // forwarding signals 49 | output reg [ 1:0] s_mx_a_fw, 50 | output reg a_fw, 51 | output reg [ 1:0] s_mx_b_fw, 52 | output reg b_fw, 53 | 54 | // control signals 55 | input rst_n, 56 | input clk 57 | ); 58 | 59 | wire stall_all = !rst_n || stall_imem || stall_dmem || (amo_req && !amo_ack) || c_stall; 60 | 61 | /* PIPELINE DATA HAZARD */ 62 | 63 | wire rs1_pc = ir_id[6:0] == `OP_LUI || ir_id[6:0] == `OP_AUIPC || ir_id[6:0] == `OP_JAL; 64 | wire rs2_imm = ir_id[6:0] != `OP_ALRR && ir_id[6:0] != `OP_ALRRW; 65 | 66 | wire [4:0] rs1 = ir_id[19:15]; 67 | wire [4:0] rs2 = ir_id[24:20]; 68 | 69 | wire [4:0] rd_ex = ir_ex [11:7]; 70 | wire [4:0] rd_mem = ir_mem[11:7]; 71 | wire [4:0] rd_wb = ir_wb [11:7]; 72 | 73 | wire wr_ex = ir_ex [6:0] != `OP_BRANCH && ir_ex [6:0] != `OP_STORE; 74 | wire wr_mem = ir_mem[6:0] != `OP_BRANCH && ir_mem[6:0] != `OP_STORE; 75 | wire wr_wb = ir_wb [6:0] != `OP_BRANCH && ir_wb [6:0] != `OP_STORE; 76 | 77 | /* DATA HAZARD DETECTION */ 78 | 79 | // in order for a data hazard to occur, following needs to be true: 80 | // 1. instruction in ID stage depends on register result of instructions in either EX, MEM or WB 81 | // 2. EX, MEM or WB register result must not be zero register (result discarded false dependency) 82 | // 3. EX, MEM or WB instruction must update register (RAR false dependency) 83 | 84 | wire dh_ex = ((rd_ex == rs1 && !rs1_pc) || (rd_ex == rs2 /*&& !rs2_imm*/)) && rd_ex && wr_ex && !stall_ex; 85 | wire dh_mem = ((rd_mem == rs1 && !rs1_pc) || (rd_mem == rs2 /*&& !rs2_imm*/)) && rd_mem && wr_mem && !stall_mem; 86 | wire dh_wb = ((rd_wb == rs1 && !rs1_pc) || (rd_wb == rs2 /*&& !rs2_imm*/)) && rd_wb && wr_wb && !stall_wb; 87 | 88 | /* FORWARDING */ 89 | 90 | wire a_fw_ex = rd_ex == rs1 && !rs1_pc && rd_ex && wr_ex; 91 | wire a_fw_mem = rd_mem == rs1 && !rs1_pc && rd_mem && wr_mem; 92 | wire a_fw_wb = rd_wb == rs1 && !rs1_pc && rd_wb && wr_wb; 93 | 94 | always @(posedge clk) begin 95 | if(!stall_all) begin 96 | if(a_fw_ex) begin 97 | a_fw <= ir_ex[6:0] != `OP_LOAD && ir_ex[6:0] != `OP_SYSTEM; 98 | s_mx_a_fw <= 0; 99 | end 100 | else if(a_fw_mem) begin 101 | a_fw <= ir_mem[6:0] != `OP_LOAD && ir_mem[6:0] != `OP_SYSTEM; 102 | s_mx_a_fw <= 1; 103 | end 104 | else if(a_fw_wb) begin 105 | a_fw <= 1; 106 | s_mx_a_fw <= 2; 107 | end 108 | else a_fw <= 0; 109 | end 110 | end 111 | 112 | wire b_fw_ex = rd_ex == rs2 && !rs2_imm && rd_ex && wr_ex; 113 | wire b_fw_mem = rd_mem == rs2 && !rs2_imm && rd_mem && wr_mem; 114 | wire b_fw_wb = rd_wb == rs2 && !rs2_imm && rd_wb && wr_wb; 115 | 116 | always @(posedge clk) begin 117 | if(!stall_all) begin 118 | if(b_fw_ex) begin 119 | b_fw <= ir_ex[6:0] != `OP_LOAD && ir_ex[6:0] != `OP_SYSTEM; 120 | s_mx_b_fw <= 0; 121 | end 122 | else if(b_fw_mem) begin 123 | b_fw <= ir_ex[6:0] != `OP_LOAD && ir_mem[6:0] != `OP_SYSTEM; 124 | s_mx_b_fw <= 1; 125 | end 126 | else if(b_fw_wb) begin 127 | b_fw <= 1; 128 | s_mx_b_fw <= 2; 129 | end 130 | else b_fw <= 0; 131 | end 132 | end 133 | 134 | reg fw; 135 | always @(*) begin 136 | fw <= 0; 137 | if(a_fw_ex || b_fw_ex) fw <= ir_ex [6:0] != `OP_LOAD && ir_ex [6:0] != `OP_SYSTEM; 138 | else if(a_fw_mem || b_fw_mem) fw <= ir_mem[6:0] != `OP_LOAD && ir_mem[6:0] != `OP_SYSTEM; 139 | else if(a_fw_wb || b_fw_wb) fw <= 1; 140 | end 141 | 142 | /* STALL */ 143 | 144 | // front end stall counter 145 | reg [1:0] stall_c; 146 | 147 | // back end stall counter 148 | reg [4:0] stall_d; 149 | 150 | wire dh = (dh_ex || dh_mem || dh_wb) && !stall_c && 151 | (!fw || ir_id[6:0] == `OP_BRANCH || ir_id[6:0] == `OP_JALR || ir_id[6:0] == `OP_STORE || ir_id[6:0] == `OP_SYSTEM); 152 | 153 | // disable forwarding 154 | //wire dh = (dh_ex || dh_mem || dh_wb) && !stall_c; 155 | 156 | // front end stall signals 157 | assign stall_if = stall_all || stall_c || dh || amo_req || fence_i; 158 | assign stall_pd = stall_all || stall_c || dh; 159 | assign stall_id = stall_all || stall_c || dh; 160 | 161 | // back end stall signals 162 | assign stall_ex = stall_all || stall_d[2]; 163 | assign stall_mem = stall_all || stall_d[3]; 164 | assign stall_wb = stall_all || stall_d[4]; 165 | 166 | always @(posedge clk) begin 167 | if(!rst_n) begin 168 | stall_c <= 0; 169 | stall_d <= 5'b11111; 170 | end 171 | else if(dh) begin 172 | if(dh_ex) begin 173 | stall_c <= 2; 174 | stall_d <= (stall_d << 1) | 5'b00111; 175 | end 176 | else if(dh_mem) begin 177 | stall_c <= 1; 178 | stall_d <= (stall_d << 1) | 5'b00110; 179 | end 180 | else if(dh_wb) begin 181 | stall_c <= 0; 182 | stall_d <= (stall_d << 1) | 5'b00100; 183 | end 184 | end 185 | else if(!stall_all) begin 186 | if(stall_c) stall_c <= stall_c - 1; 187 | stall_d <= stall_d << 1; 188 | end 189 | end 190 | 191 | /* FENCE_I */ 192 | 193 | wire i_fence = ir_if[6:0] == `OP_FENCE; 194 | reg i_fence_d; 195 | always @(posedge clk, negedge rst_n) begin 196 | if(!rst_n) i_fence_d <= 0; 197 | else i_fence_d <= i_fence; 198 | end 199 | wire i_fence_re = i_fence && !i_fence_d; 200 | 201 | reg [2:0] fence_cnt; 202 | reg fence_cnt_ena; 203 | 204 | always @(posedge clk, negedge rst_n) begin 205 | if(!rst_n) fence_cnt_ena <= 0; 206 | else if(i_fence_re) fence_cnt_ena <= 1; 207 | else if(fence_cnt == 3'd0) fence_cnt_ena <= 0; 208 | end 209 | 210 | always @(posedge clk, negedge rst_n) begin 211 | if(!rst_n) fence_cnt <= 3'd5; 212 | else begin 213 | if(!fence_cnt_ena) fence_cnt <= 3'd5; 214 | if(!stall_wb && fence_cnt_ena) fence_cnt <= fence_cnt - 1; 215 | end 216 | end 217 | 218 | assign fence_i = !stall_imem && (i_fence_re || (fence_cnt_ena && fence_cnt != 3'd0)); 219 | 220 | endmodule 221 | -------------------------------------------------------------------------------- /sim/tb_core.do: -------------------------------------------------------------------------------- 1 | onerror {resume} 2 | quietly WaveActivateNextPane {} 0 3 | add wave -noupdate -radix hexadecimal /tb_core/c_addr 4 | add wave -noupdate -radix hexadecimal /tb_core/c_ext 5 | add wave -noupdate -radix hexadecimal /tb_core/c_rdata 6 | add wave -noupdate -radix hexadecimal /tb_core/c_rd 7 | add wave -noupdate -radix hexadecimal /tb_core/c_dv 8 | add wave -noupdate -radix hexadecimal /tb_core/c_wdata 9 | add wave -noupdate -radix hexadecimal /tb_core/c_len 10 | add wave -noupdate -radix hexadecimal /tb_core/c_wr 11 | add wave -noupdate -radix hexadecimal /tb_core/c_irq_me 12 | add wave -noupdate -radix hexadecimal /tb_core/c_irq_mt 13 | add wave -noupdate -radix hexadecimal /tb_core/c_irq_ms 14 | add wave -noupdate -radix hexadecimal /tb_core/c_irq_se 15 | add wave -noupdate -radix hexadecimal /tb_core/c_irq_st 16 | add wave -noupdate -radix hexadecimal /tb_core/c_irq_ss 17 | add wave -noupdate -radix hexadecimal /tb_core/c_inv_addr 18 | add wave -noupdate -radix hexadecimal /tb_core/c_inv 19 | add wave -noupdate -radix hexadecimal /tb_core/c_amo_req 20 | add wave -noupdate -radix hexadecimal /tb_core/c_amo_ack 21 | add wave -noupdate -radix hexadecimal /tb_core/c_stall 22 | add wave -noupdate -radix hexadecimal /tb_core/c_rst_n 23 | add wave -noupdate -radix hexadecimal /tb_core/c_clk 24 | add wave -noupdate -color Salmon -radix hexadecimal /tb_core/dut/pc 25 | add wave -noupdate -color Salmon -radix hexadecimal /tb_core/dut/ir 26 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_addr_w_p 27 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_wr_w_p 28 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_addr_i_p 29 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_rd_i_p 30 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_addr_d_p 31 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_rd_d_p 32 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/priv 33 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpcfg0 34 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpcfg2 35 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr0 36 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr1 37 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr2 38 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr3 39 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr4 40 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr5 41 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr6 42 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr7 43 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr8 44 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr9 45 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr10 46 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr11 47 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr12 48 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr13 49 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr14 50 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr15 51 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/exc_pmp_iaf 52 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/exc_pmp_laf 53 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/exc_pmp_saf 54 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpcfg 55 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmpaddr 56 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/access_i 57 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/access_d 58 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/access_w 59 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/napot_mask 60 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmp_iaf_oob 61 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmp_laf_oob 62 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/pmp_saf_oob 63 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_addr_w 64 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_addr_i 65 | add wave -noupdate -group pmp -radix hexadecimal /tb_core/dut/u_pmp/b_addr_d 66 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/priv 67 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/ir 68 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc 69 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_cause 70 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_val 71 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_ii_if 72 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_ii_csr 73 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_pmp_iaf 74 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_pmp_laf 75 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_pmp_saf 76 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_dmem_lma 77 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/exc_dmem_sma 78 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/dmem_addr 79 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/flush_pd 80 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/flush_id 81 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/t_flush 82 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/stall_if 83 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/stall_pd 84 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/stall_id 85 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/stall_ex 86 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/stall_mem 87 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/stall_wb 88 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/ecall 89 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/ebreak 90 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/if_exc 91 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/if_exc_cause 92 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/if_exc_val 93 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/pd_exc 94 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/pd_exc_cause 95 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/pd_exc_val 96 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/id_exc 97 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/id_exc_cause 98 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/id_exc_val 99 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/ex_exc 100 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/ex_exc_cause 101 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/ex_exc_val 102 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/mem_exc 103 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/mem_exc_cause 104 | add wave -noupdate -group exc -radix hexadecimal /tb_core/dut/u_exc/mem_exc_val 105 | add wave -noupdate -expand -group pipeline -color Gold -label pd -radix hexadecimal {/tb_core/env.t.pipeline[1]} 106 | add wave -noupdate -expand -group pipeline -color Gold -label id -radix hexadecimal {/tb_core/env.t.pipeline[2]} 107 | add wave -noupdate -expand -group pipeline -color Gold -label ex -radix hexadecimal {/tb_core/env.t.pipeline[3]} 108 | add wave -noupdate -expand -group pipeline -color Gold -label mem -radix hexadecimal {/tb_core/env.t.pipeline[4]} 109 | add wave -noupdate -expand -group pipeline -color Gold -label wb -radix hexadecimal {/tb_core/env.t.pipeline[5]} 110 | add wave -noupdate -color {Slate Blue} /tb_core/env 111 | TreeUpdate [SetDefaultTree] 112 | WaveRestoreCursors {{Cursor 1} {8800000 ps} 0} 113 | quietly wave cursor active 1 114 | configure wave -namecolwidth 150 115 | configure wave -valuecolwidth 127 116 | configure wave -justifyvalue left 117 | configure wave -signalnamewidth 1 118 | configure wave -snapdistance 10 119 | configure wave -datasetprefix 0 120 | configure wave -rowmargin 4 121 | configure wave -childrowmargin 2 122 | configure wave -gridoffset 0 123 | configure wave -gridperiod 1 124 | configure wave -griddelta 40 125 | configure wave -timeline 0 126 | configure wave -timelineunits ps 127 | update 128 | WaveRestoreZoom {5855569 ps} {7386997 ps} 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RV6 2 | **RV6** is configurable, 64-bit, multi-core application processor implemented in Verilog HDL, based on [RISC-V instruction set](https://riscv.org). 3 | 4 | ## Table of Contents 5 | - [Features](https://github.com/kiclu/rv6#features) 6 | - [Compatibility](https://github.com/kiclu/rv6#compatibility) 7 | - [Core Architecture](https://github.com/kiclu/rv6#core-architecture) 8 | - [Project Structure](https://github.com/kiclu/rv6#project-structure) 9 | - [Parameters](https://github.com/kiclu/rv6#parameters) 10 | - [Prerequisites](https://github.com/kiclu/rv6#prerequisites) 11 | - [License](https://github.com/kiclu/rv6#license) 12 | 13 | ## Features 14 | - RV64IMAC_Zicsr ISA extenstions 15 | - Optimized 6-stage, single-issue, in-order pipeline 16 | - Machine, Supervisor & User privilege modes 17 | - Three level cache hierarchy 18 | - Branch prediction 19 | - Instruction pre-decoding for compressed & atomic instruction support 20 | - Parametrized branch prediction, cache size and associativity 21 | 22 | ## Compatibility 23 | RV6 core is compatible with following RISC-V Foundation specifications: 24 | - [RISC-V Instruction Set Manual - Volume I: Unprivileged ISA](https://github.com/kiclu/rv6/blob/master/doc/riscv-unprivileged-isa.pdf), version 20191213 25 | - [RISC-V Instruction Set Manual - Volume II: Privileged Architecture](https://github.com/kiclu/rv6/blob/master/doc/riscv-privileged-isa.pdf), version 20211203 26 | 27 | ## Core Architecture 28 | Pipeline is split into following 6 stages: 29 | - Instruction Fetch (IF) 30 | - Instruction Pre-Decode (PD) 31 | - Instruction Decode (ID) 32 | - Execute (EX) 33 | - Memory (MEM) 34 | - Write Back (WB) 35 | 36 | 37 | 38 | ### Instruction Fetch (IF) 39 | During the Instruction Fetch stage one instruction is fetched from instruction memory and the program counter is updated.
40 | If fetched instruction is JAL, next PC is immediately calculated and updated.
41 | If fetched instruction is a branch instruction, Branch Prediction Unit (BPU) predicts if branch is taken, updates PC if prediction is positive and passes the prediction to following stages. 42 | 43 | ### Instruction Pre-Decode (PD) 44 | In the Instruction Pre-Decode stage multi-cycle, atomic and compressed instructions are pre-decoded.
45 | Multi-cycle instructions are broken down into multiple single-cycle instructions.
46 | Atomic instructions are, after receiving a handshake from Central Control Unit (CCU), broken-down into multiple single-cycle instructions. 47 | Handshake is mandatory to ensure atomicty, i.e. ensure that only one core is accessing and modifying data.
48 | Compressed instructions are expanded into their non-compressed form. 49 | 50 | ### Instruction Decode (ID) 51 | During the Instruction Decode stage registers are accessed and/or immediate is multiplexed and sign-extended.
52 | After registers are decoded, Branch ALU checks if branch prediction from IF is valid and generates branch miss signal if it isn't.
53 | Also, during this stage, JALR control transfer instruction updates the Program Counter. 54 | 55 | ### Execute (EX) 56 | During the Execute stage, result is calculated for ALU instructions, memory address is calculated for Load / Store instructions and 57 | return address is calculated for control transfer instructions. 58 | 59 | ### Memory (MEM) 60 | During the Memory stage, Data Memory is accessed for Load and Store instructions, as well as Control & Status Registers for CSR instructions. 61 | 62 | ### Write Back (WB) 63 | During the Write Back stage, the result from previous stages is written back into the Register File. 64 | 65 | ## Project Structure 66 | ``` 67 | . 68 | ├─ doc/ # Documentation files 69 | ├─ rtl/ # Synthesis source files 70 | │ ├─ core/ # Core top-level & submodules 71 | │ │ ├─ alu.v # Integer ALU 72 | │ │ ├─ bpu.v # Branch Prediction Unit 73 | │ │ ├─ br_alu.v # Branch ALU 74 | │ │ ├─ cmem.v # L2 cache 75 | │ │ ├─ core.v # Core top-level module 76 | │ │ ├─ csr.v # Control & Status Registers 77 | │ │ ├─ cu.v # Control Unit 78 | │ │ ├─ dba.v # Data Bus Arbiter 79 | │ │ ├─ dmem.v # L1d cache 80 | │ │ ├─ exc.v # Exception handler 81 | │ │ ├─ imem.v # L1i cache 82 | │ │ ├─ pc.v # Program Counter 83 | │ │ ├─ pd.v # Instruction Pre-Decoder 84 | │ │ ├─ pmp.v # Physical Memory Protection 85 | │ │ └─ regfile.v # Register File 86 | │ └─ config.vh # Configuration include file 87 | ├─ sim/ # ModelSim project files 88 | ├─ tb/ # Testbench source files 89 | ├─ tools/ 90 | ├─ LICENSE 91 | ├─ Makefile 92 | └─ README.md 93 | ``` 94 | 95 | ## Parameters 96 | | Parameter | Type | Description | 97 | |---------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------| 98 | | **L1i cache** | | | 99 | | `IMEM_SET_ASSOC` `IMEM_DIRECT` `IMEM_FULL_ASSOC` | ifdef flag | L1i cache associativity | 100 | | `IMEM_LINE` | integer | L1i cache line size in bits | 101 | | `IMEM_SETS` | integer | L1i cache set count | 102 | | `IMEM_WAYS` | integer | L1i cache ways per set | 103 | | **L1d cache** | | | 104 | | `DMEM_SET_ASSOC` `DMEM_DIRECT` `DMEM_FULL_ASSOC` | ifdef flag | L1d cache associativity | 105 | | `DMEM_LINE` | integer | L1d cache line size in bits | 106 | | `DMEM_SETS` | integer | L1d cache set count | 107 | | `DMEM_WAYS` | integer | L1d cache ways per set | 108 | | **L2 cache** | | | 109 | | `CMEM_SET_ASSOC` `CMEM_DIRECT` `CMEM_FULL_ASSOC` | ifdef flag | L2 cache associativity | 110 | | `CMEM_LINE` | integer | L2 cache line size in bits | 111 | | `CMEM_SETS` | integer | L2 cache set count | 112 | | `CMEM_WAYS` | integer | L2 cache ways per set | 113 | | **Branch Prediction** | | | 114 | | `BPU_STATIC_TAKEN` | ifdef flag | Branch predict static taken | 115 | | `BPU_STATIC_NTAKEN` | ifdef flag | Branch predict static not taken | 116 | | `BPU_STATIC_BTAKEN` | ifdef flag | Branch predict static backward taken | 117 | | **Misaligned Access** | | | 118 | | `DMEM_MA_CACHE_LINE` | ifdef flag | L1d misaligned access exception on cache line boundary violation | 119 | | `DMEM_MA_NATURAL` | ifdef flag | L1d misaligned access exception on natural alignment violation | 120 | 121 | *Parameters labeled with `ifdef flag` type are mutually exclusive 122 | 123 | ## Prerequisites 124 | - [riscv-collab/riscv-gnu-toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain) 125 | - [riscv-software-src/riscv-tests](https://github.com/riscv-software-src/riscv-tests) 126 | - [chipsalliance/dromajo](https://github.com/chipsalliance/dromajo) 127 | 128 | ## License 129 | The hardware is licensed under the [CERN Open Hardware Licence Version 2 - Strongly Reciprocal](https://ohwr.org/cern_ohl_s_v2.txt). 130 | -------------------------------------------------------------------------------- /rtl/core/dmem.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module dmem ( 20 | input [63:0] addr, 21 | input [ 2:0] len, 22 | 23 | output reg [63:0] rdata, 24 | input rd, 25 | 26 | input [63:0] wdata, 27 | input wr, 28 | 29 | // misaligned access 30 | output exc_lma, 31 | output exc_sma, 32 | 33 | // external bus signals 34 | output reg [`DMEM_BLK_LEN-1:0] b_addr_d, 35 | 36 | input [`DMEM_LINE-1:0] b_rdata_d, 37 | output reg b_rd_d, 38 | input b_dv_d, 39 | 40 | // cache invalidaton 41 | input [`DMEM_BLK_LEN-1:0] b_inv_addr_d, 42 | input inv, 43 | 44 | // control signals 45 | output stall_dmem, 46 | input stall_mem, 47 | input rst_n, 48 | input clk 49 | ); 50 | 51 | `ifdef DMEM_SET_ASSOC 52 | 53 | wire [ `DMEM_TAG_LEN-1:0] addr_tag = addr[`DMEM_ADDR_TAG_RANGE ]; 54 | wire [ `DMEM_SET_LEN-1:0] addr_set = addr[`DMEM_ADDR_SET_RANGE ]; 55 | wire [`DMEM_OFFS_LEN-1:0] addr_offs = addr[`DMEM_ADDR_OFFS_RANGE]; 56 | 57 | (* ram_style = "block" *) 58 | reg [ `DMEM_LINE-1:0] data [0:`DMEM_LINES-1]; 59 | reg [`DMEM_TAG_LEN-1:0] tag [0:`DMEM_LINES-1]; 60 | reg v [0:`DMEM_LINES-1]; 61 | 62 | reg [`DMEM_WAY_LEN-1:0] re; 63 | reg hit; 64 | 65 | /* BRAM WRITE */ 66 | 67 | reg [`DMEM_TAG_LEN-1:0] tag_d; 68 | reg [`DMEM_SET_LEN-1:0] set_d; 69 | reg [`DMEM_WAY_LEN-1:0] way_d; 70 | reg [ `DMEM_LINE-1:0] d; 71 | reg wre; 72 | always @(posedge clk) if(wre) data[`DMEM_WAYS * set_d + way_d] <= d; 73 | 74 | /* BRAM READ */ 75 | 76 | reg [`DMEM_TAG_LEN-1:0] tag_q; 77 | reg [`DMEM_SET_LEN-1:0] set_q; 78 | reg [`DMEM_WAY_LEN-1:0] way_q; 79 | reg [ `DMEM_LINE-1:0] q; 80 | reg rde; 81 | always @(posedge clk) if(rde) q <= data[`DMEM_WAYS * set_q + way_q]; 82 | 83 | /* WRITE BUFFER */ 84 | 85 | reg [ `DMEM_TAG_LEN-1:0] wb_tag; 86 | reg [ `DMEM_SET_LEN-1:0] wb_set; 87 | reg [`DMEM_OFFS_LEN-1:0] wb_offs; 88 | reg [ 63:0] wb_data; 89 | reg [ 1:0] wb_len; 90 | reg wr_pend; 91 | 92 | /* READ BUFFER */ 93 | 94 | reg [`DMEM_TAG_LEN-1:0] rb_tag; 95 | reg [`DMEM_SET_LEN-1:0] rb_set; 96 | reg rb_v; 97 | 98 | wire rb_hit = rb_tag == addr_tag && rb_set == addr_set && rb_v; 99 | 100 | /* FSM */ 101 | 102 | reg [3:0] ld_cnt; 103 | 104 | reg [1:0] dmem_fsm_state; 105 | reg [1:0] dmem_fsm_state_next; 106 | 107 | `define DMEM_S_READY 2'd0 108 | `define DMEM_S_FETCH 2'd1 109 | `define DMEM_S_LOAD 2'd2 110 | `define DMEM_S_WRITE 2'd3 111 | 112 | always @(*) begin 113 | wre = 0; 114 | rde = 0; 115 | b_rd_d = 0; 116 | way_d = way_q; 117 | dmem_fsm_state_next = dmem_fsm_state; 118 | case(dmem_fsm_state) 119 | `DMEM_S_READY: begin 120 | if(rd && !hit) dmem_fsm_state_next = `DMEM_S_FETCH; 121 | if(wr && !hit) dmem_fsm_state_next = `DMEM_S_FETCH; 122 | 123 | if(rd && hit) dmem_fsm_state_next = `DMEM_S_LOAD; 124 | if(wr && hit) dmem_fsm_state_next = `DMEM_S_LOAD; 125 | 126 | if(rd && rb_hit) dmem_fsm_state_next = `DMEM_S_READY; 127 | if(wr && rb_hit) dmem_fsm_state_next = `DMEM_S_WRITE; 128 | 129 | if(exc_lma || exc_sma) dmem_fsm_state_next = `DMEM_S_READY; 130 | end 131 | `DMEM_S_FETCH: begin 132 | b_rd_d = 1; 133 | way_d = re; 134 | wre = b_dv_d; 135 | 136 | if(b_dv_d) dmem_fsm_state_next = `DMEM_S_LOAD; 137 | end 138 | `DMEM_S_LOAD: begin 139 | rde = ld_cnt == `DMEM_READ_VALID_DELAY; 140 | 141 | if(!ld_cnt) dmem_fsm_state_next = wr_pend ? `DMEM_S_WRITE : `DMEM_S_READY; 142 | end 143 | `DMEM_S_WRITE: begin 144 | wre = 1; 145 | 146 | dmem_fsm_state_next = `DMEM_S_READY; 147 | end 148 | endcase 149 | end 150 | 151 | /* FSM UPDATE */ 152 | 153 | always @(posedge clk) begin 154 | if(!rst_n) dmem_fsm_state <= `DMEM_S_READY; 155 | else dmem_fsm_state <= dmem_fsm_state_next; 156 | end 157 | 158 | /* READ BUFFER */ 159 | 160 | always @(posedge clk, negedge rst_n) begin 161 | if(!rst_n) begin 162 | rb_tag <= 0; 163 | rb_set <= 0; 164 | rb_v <= 0; 165 | end 166 | else begin 167 | if(dmem_fsm_state == `DMEM_S_WRITE) rb_v <= 0; 168 | if(dmem_fsm_state == `DMEM_S_LOAD) begin 169 | rb_tag <= wr_pend ? wb_tag : addr_tag; 170 | rb_set <= wr_pend ? wb_set : addr_set; 171 | rb_v <= 1; 172 | end 173 | ld_cnt <= dmem_fsm_state == `DMEM_S_LOAD ? ld_cnt - 1 : `DMEM_READ_VALID_DELAY; 174 | end 175 | end 176 | 177 | /* WRITE BUFFER */ 178 | 179 | wire wr_nstall = (wr && !exc_sma) && !stall_mem; 180 | reg wr_nstall_d; 181 | always @(posedge clk) wr_nstall_d <= wr_nstall; 182 | wire wr_nstall_re = wr_nstall && !wr_nstall_d; 183 | 184 | always @(posedge clk) begin 185 | if(!rst_n || dmem_fsm_state == `DMEM_S_WRITE) wr_pend <= 0; 186 | else if(wr_nstall_re && !rb_hit) begin 187 | wb_tag <= addr_tag; 188 | wb_set <= addr_set; 189 | wb_offs <= addr_offs; 190 | wb_data <= wdata; 191 | wb_len <= len; 192 | wr_pend <= 1; 193 | end 194 | end 195 | 196 | /* REQUEST ADDRESS */ 197 | 198 | //always @(posedge clk) begin 199 | // if(dmem_fsm_state == `DMEM_S_FETCH) begin 200 | // b_addr_d <= wr_pend ? {wb_tag, wb_set} : {addr_tag, addr_set}; 201 | // end 202 | //end 203 | 204 | always @(*) begin 205 | b_addr_d = wr_pend ? {wb_tag, wb_set} : {addr_tag, addr_set}; 206 | end 207 | 208 | /* METADATA UPDATE */ 209 | 210 | always @(posedge clk) begin 211 | if(!rst_n) begin : dmem_reset 212 | integer i; 213 | for(i = 0; i < `DMEM_LINES; i = i + 1) begin 214 | tag[i] <= 0; 215 | v [i] <= 0; 216 | end 217 | end 218 | else begin 219 | if(wre && dmem_fsm_state == `DMEM_S_FETCH) begin 220 | tag[`DMEM_WAYS * set_d + way_d] <= tag_d; 221 | v [`DMEM_WAYS * set_d + way_d] <= 1; 222 | end 223 | end 224 | end 225 | 226 | /* INPUT MUX */ 227 | 228 | always @(*) begin 229 | if(dmem_fsm_state == `DMEM_S_FETCH) d = b_rdata_d; 230 | else if(wr_pend) begin 231 | d = q; 232 | case(wb_len) 233 | 2'b00: d[8*wb_offs +: 8] = wb_data[ 7:0]; 234 | 2'b01: d[8*wb_offs +: 16] = wb_data[15:0]; 235 | 2'b10: d[8*wb_offs +: 32] = wb_data[31:0]; 236 | 2'b11: d[8*wb_offs +: 64] = wb_data[63:0]; 237 | endcase 238 | end 239 | else begin 240 | d = q; 241 | case(len) 242 | 2'b00: d[8*addr_offs +: 8] = wdata[ 7:0]; 243 | 2'b01: d[8*addr_offs +: 16] = wdata[15:0]; 244 | 2'b10: d[8*addr_offs +: 32] = wdata[31:0]; 245 | 2'b11: d[8*addr_offs +: 64] = wdata[63:0]; 246 | endcase 247 | end 248 | end 249 | 250 | /* OUTPUT MUX */ 251 | 252 | always @(*) begin 253 | case(len) 254 | 3'b000: rdata = $signed(q[8*addr_offs +: 8]); 255 | 3'b001: rdata = $signed(q[8*addr_offs +: 16]); 256 | 3'b010: rdata = $signed(q[8*addr_offs +: 32]); 257 | 3'b011: rdata = $signed(q[8*addr_offs +: 64]); 258 | 3'b100: rdata = $unsigned(q[8*addr_offs +: 8]); 259 | 3'b101: rdata = $unsigned(q[8*addr_offs +: 16]); 260 | 3'b110: rdata = $unsigned(q[8*addr_offs +: 32]); 261 | 3'b111: rdata = $unsigned(q[8*addr_offs +: 64]); 262 | endcase 263 | end 264 | 265 | /* HIT DETECTION */ 266 | 267 | always @(*) begin : dmem_cache_hit 268 | integer w; 269 | way_q = 'bZ; hit = 0; 270 | for(w = 0; w < `DMEM_WAYS; w = w + 1) begin 271 | if(tag[`DMEM_WAYS * set_q + w] == tag_q && v[`DMEM_WAYS * set_q + w]) begin 272 | way_q = w; hit = 1; 273 | end 274 | end 275 | end 276 | 277 | /* BRAM READ */ 278 | 279 | always @(*) begin 280 | tag_q = wr_pend ? wb_tag : addr_tag; 281 | set_q = wr_pend ? wb_set : addr_set; 282 | end 283 | 284 | /* BRAM WRITE */ 285 | 286 | always @(*) begin 287 | tag_d = wr_pend ? wb_tag : addr_tag; 288 | set_d = wr_pend ? wb_set : addr_set; 289 | end 290 | 291 | assign stall_dmem = (rd && dmem_fsm_state_next) || ((rd ||wr) && wr_pend) || (dmem_fsm_state == `DMEM_S_READY && dmem_fsm_state_next == `DMEM_S_WRITE); 292 | 293 | `endif//DMEM_SET_ASSOC 294 | 295 | // TODO: 296 | /* REPLACEMENT POLICY */ 297 | 298 | always @(posedge clk) re <= $random() % `DMEM_WAYS; 299 | 300 | /* MISALIGNED ACCESS DETECTION */ 301 | 302 | `ifdef DMEM_MA_NONE 303 | // DEBUG PURPOSES ONLY 304 | wire ma = 0; 305 | `endif//DMEM_MA_NONE 306 | 307 | `ifdef DMEM_MA_CACHE_LINE 308 | reg [63:0] ma_addr; 309 | wire [`DMEM_TAG_LEN-1:0] ma_addr_tag = ma_addr[`DMEM_ADDR_TAG_RANGE]; 310 | wire [`DMEM_SET_LEN-1:0] ma_addr_set = ma_addr[`DMEM_ADDR_SET_RANGE]; 311 | wire ma = ma_addr_tag != addr_tag || ma_addr_set != addr_set; 312 | 313 | always @(*) begin 314 | case(len[1:0]) 315 | 2'b00: ma_addr = addr + 0; 316 | 2'b01: ma_addr = addr + 1; 317 | 2'b10: ma_addr = addr + 3; 318 | 2'b11: ma_addr = addr + 7; 319 | endcase 320 | end 321 | `endif//DMEM_MA_CACHE_LINE 322 | 323 | `ifdef DMEM_MA_NATURAL 324 | reg ma; 325 | always @(*) begin 326 | case(len[1:0]) 327 | 2'b00: ma = 0; 328 | 2'b01: ma = |addr[0:0]; 329 | 2'b10: ma = |addr[1:0]; 330 | 2'b11: ma = |addr[2:0]; 331 | endcase 332 | end 333 | `endif//DMEM_MA_NATURAL 334 | 335 | assign exc_lma = ma && rd; 336 | assign exc_sma = ma && wr; 337 | 338 | endmodule 339 | -------------------------------------------------------------------------------- /rtl/core/pd.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module pd ( 20 | input [63:0] pc_in, 21 | input [31:0] ir_in, 22 | 23 | output reg [31:0] ir_out, 24 | 25 | output reg amo_req, 26 | input amo_ack, 27 | 28 | input stall, 29 | input rst_n, 30 | input clk 31 | ); 32 | 33 | // AMO sequence counter 34 | reg [1:0] sc; 35 | 36 | // compressed instruction ir 37 | wire [4:0] rvc_ir = {ir_in[15:13], ir_in[1:0]}; 38 | // compressed instruction register decode 39 | wire [4:0] rvc1 = {2'b0, ir_in[9:7]} + 5'd8; 40 | wire [4:0] rvc2 = {2'b0, ir_in[4:2]} + 5'd8; 41 | 42 | always @(*) begin 43 | ir_out = ir_in; 44 | amo_req = 0; 45 | 46 | /* AMO INSTRUCTIONS */ 47 | case({ir_in[31:27], ir_in[6:0]}) 48 | // lr.w 49 | 12'b00010_0101111: begin 50 | // TODO 51 | end 52 | 53 | // sc.w 54 | 12'b00011_0101111: begin 55 | // TODO 56 | end 57 | 58 | // amoswap.w 59 | 12'b00001_0101111: begin 60 | case(sc) 61 | 2'b00: begin 62 | amo_req = 1; 63 | ir_out = {12'b0, ir_in[19:7], 7'b0000011}; 64 | end 65 | 2'b01: begin 66 | amo_req = 1; 67 | ir_out = {7'b0, ir_in[24:12], 5'b0, 7'b0100011}; 68 | end 69 | endcase 70 | end 71 | 72 | // amoadd.w 73 | 12'b00000_0101111: begin 74 | case(sc) 75 | 2'b00: begin 76 | amo_req = 1; 77 | ir_out = {12'b0, ir_in[19:7], 7'b0000011}; 78 | end 79 | 2'b01: begin 80 | amo_req = 1; 81 | ir_out = {7'b0, ir_in[24:20], ir_in[11:7], 3'b000, ir_in[11:7], 7'b0110011}; 82 | end 83 | 2'b10: begin 84 | amo_req = 1; 85 | ir_out = {7'b0, ir_in[24:12], 5'b0, 7'b0100011}; 86 | end 87 | endcase 88 | end 89 | 90 | // amoxor.w 91 | 12'b00100_0101111: begin 92 | case(sc) 93 | 2'b00: begin 94 | amo_req = 1; 95 | ir_out = {12'b0, ir_in[19:7], 7'b0000011}; 96 | end 97 | 2'b01: begin 98 | amo_req = 1; 99 | ir_out = {7'b0, ir_in[24:20], ir_in[11:7], 3'b100, ir_in[11:7], 7'b0110011}; 100 | end 101 | 2'b10: begin 102 | amo_req = 1; 103 | ir_out = {7'b0, ir_in[24:12], 5'b0, 7'b0100011}; 104 | end 105 | endcase 106 | end 107 | 108 | // amoand.w 109 | 12'b01100_0101111: begin 110 | case(sc) 111 | 2'b00: begin 112 | amo_req = 1; 113 | ir_out = {12'b0, ir_in[19:7], 7'b0000011}; 114 | end 115 | 2'b01: begin 116 | amo_req = 1; 117 | ir_out = {7'b0, ir_in[24:20], ir_in[11:7], 3'b111, ir_in[11:7], 7'b0110011}; 118 | end 119 | 2'b10: begin 120 | amo_req = 1; 121 | ir_out = {7'b0, ir_in[24:12], 5'b0, 7'b0100011}; 122 | end 123 | endcase 124 | end 125 | 126 | // amoor.w 127 | 12'b01000_0101111: begin 128 | case(sc) 129 | 2'b00: begin 130 | amo_req = 1; 131 | ir_out = {12'b0, ir_in[19:7], 7'b0000011}; 132 | end 133 | 2'b01: begin 134 | amo_req = 1; 135 | ir_out = {7'b0, ir_in[24:20], ir_in[11:7], 3'b110, ir_in[11:7], 7'b0110011}; 136 | end 137 | 2'b10: begin 138 | amo_req = 1; 139 | ir_out = {7'b0, ir_in[24:12], 5'b0, 7'b0100011}; 140 | end 141 | endcase 142 | end 143 | 144 | // amomin.w 145 | 12'b10000_0101111: begin 146 | // TODO 147 | end 148 | 149 | // amomax.w 150 | 12'b10100_0101111: begin 151 | // TODO 152 | end 153 | 154 | // amominu.w 155 | 12'b11000_0101111: begin 156 | // TODO 157 | end 158 | 159 | // amomaxu.w 160 | 12'b11100_0101111: begin 161 | // TODO 162 | end 163 | 164 | endcase 165 | 166 | /* COMPRESSED INSTRUCTIONS */ 167 | case(rvc_ir) 168 | // c.addi4spn 169 | 16'b00000: begin 170 | ir_out = {2'b0, ir_in[10:7], ir_in[12:11], ir_in[5], ir_in[6], 2'b00, 5'b00010, 3'b000, rvc2, 7'b0010011}; 171 | if(~|ir_in[12:5]) ir_out = 32'h0; 172 | end 173 | 174 | // c.lw 175 | 16'b01000: begin 176 | ir_out = {6'b000000, ir_in[5], ir_in[12:10], ir_in[6], 2'b00, rvc1, 3'b010, rvc2, 7'b0000011}; 177 | end 178 | 179 | // c.ld 180 | 16'b01100: begin 181 | ir_out = {5'b00000, ir_in[6:5], ir_in[12:10], 3'b000, rvc1, 3'b011, rvc2, 7'b0000011}; 182 | end 183 | 184 | // c.sw 185 | 16'b11000: begin 186 | ir_out = {6'b000000, ir_in[5], ir_in[12], rvc2, rvc1, 3'b010, ir_in[11:10], ir_in[6], 2'b00, 7'b0100011}; 187 | end 188 | 189 | // c.sd 190 | 16'b11100: begin 191 | ir_out = {4'b0000, ir_in[6:5], ir_in[12], rvc2, rvc1, 3'b011, ir_in[11:10], 3'b000, 7'b0100011}; 192 | end 193 | 194 | // c.addi 195 | 16'b00001: begin 196 | ir_out = {{6{ir_in[12]}}, ir_in[12], ir_in[6:2], ir_in[11:7], 3'b000, ir_in[11:7], 7'b0010011}; 197 | end 198 | 199 | // c.addiw 200 | 16'b00101: begin 201 | ir_out = {{6{ir_in[12]}}, ir_in[12], ir_in[6:2], ir_in[11:7], 3'b000, ir_in[11:7], 7'b0011011}; 202 | end 203 | 204 | // c.li 205 | 16'b01001: begin 206 | ir_out = {{6{ir_in[12]}}, ir_in[12], ir_in[6:2], 5'b00000, 3'b000, ir_in[11:7], 7'b0010011}; 207 | end 208 | 209 | // c.lui/addi16sp 210 | 16'b01101: begin 211 | // addi16sp 212 | if(ir_in[11:7] == 5'b00010) begin 213 | ir_out = {{6{ir_in[12]}}, ir_in[12], ir_in[4:3], ir_in[5], ir_in[2], ir_in[6], 4'b0000, 5'b00010, 3'b000, 5'b00010, 7'b0010011}; 214 | end 215 | // lui 216 | else begin 217 | ir_out = {{14{ir_in[12]}}, ir_in[12], ir_in[6:2], ir_in[11:7], 7'b0110111}; 218 | end 219 | end 220 | 221 | // misc-alu 222 | 16'b10001: begin 223 | case(ir_in[11:10]) 224 | // c.srli 225 | 2'b00: begin 226 | ir_out = {7'b0000000, ir_in[12], ir_in[6:2], rvc1, 3'b101, rvc1, 7'b0010011}; 227 | end 228 | 229 | // c.srai 230 | 2'b01: begin 231 | ir_out = {7'b0100000, ir_in[12], ir_in[6:2], rvc1, 3'b101, rvc1, 7'b0010011}; 232 | end 233 | 234 | // c.andi 235 | 2'b10: begin 236 | ir_out = {{6{ir_in[12]}}, ir_in[12], ir_in[6:2], rvc1, 3'b111, rvc1, 7'b0010011}; 237 | end 238 | 239 | 2'b11: begin 240 | case({ir_in[12], ir_in[6:5]}) 241 | // c.sub 242 | 5'b000: begin 243 | ir_out = {7'b0100000, rvc2, rvc1, 3'b000, rvc1, 7'b0110011}; 244 | end 245 | // c.xor 246 | 5'b001: begin 247 | ir_out = {7'b0000000, rvc2, rvc1, 3'b100, rvc1, 7'b0110011}; 248 | end 249 | // c.or 250 | 5'b010: begin 251 | ir_out = {7'b0000000, rvc2, rvc1, 3'b110, rvc1, 7'b0110011}; 252 | end 253 | // c.and 254 | 5'b011: begin 255 | ir_out = {7'b0000000, rvc2, rvc1, 3'b111, rvc1, 7'b0110011}; 256 | end 257 | // c.subw 258 | 5'b100: begin 259 | ir_out = {7'b0100000, rvc2, rvc1, 3'b000, rvc1, 7'b0111011}; 260 | end 261 | // c.addw 262 | 5'b101: begin 263 | ir_out = {7'b0000000, rvc2, rvc1, 3'b000, rvc1, 7'b0111011}; 264 | end 265 | 266 | endcase 267 | end 268 | 269 | endcase 270 | end 271 | 272 | // c.j 273 | 16'b10101: begin 274 | ir_out = {1'b0, ir_in[8], ir_in[10:9], ir_in[6], ir_in[7], ir_in[2], ir_in[11], ir_in[5:3], ir_in[12], 5'b00000, 7'b1101111}; 275 | end 276 | 277 | // c.beqz 278 | 16'b11001: begin 279 | ir_out = {3'b000, ir_in[12], ir_in[6:5], ir_in[2], 5'b00000, rvc1, 3'b000, ir_in[11:10], ir_in[4:3], 1'b0, 7'b1100011}; 280 | end 281 | 282 | // c.bnez 283 | 16'b11101: begin 284 | ir_out = {3'b000, ir_in[12], ir_in[6:5], ir_in[2], 5'b00000, rvc1, 3'b001, ir_in[11:10], ir_in[4:3], 1'b0, 7'b1100011}; 285 | end 286 | 287 | // c.slli 288 | 16'b00010: begin 289 | ir_out = {7'b0000000, ir_in[12], ir_in[6:2], ir_in[11:7], 3'b001, ir_in[11:7], 7'b0010011}; 290 | end 291 | 292 | // c.lwsp 293 | 16'b01010: begin 294 | ir_out = {4'b0000, ir_in[3:2], ir_in[12], ir_in[6:4], 2'b00, 5'b00010, 3'b010, ir_in[11:7], 7'b0000011}; 295 | end 296 | 297 | // c.ldsp 298 | 16'b01110: begin 299 | ir_out = {6'b0, ir_in[4:2], ir_in[12], ir_in[6:5], 3'b000, 5'b00010, 3'b011, ir_in[11:7], 7'b0000011}; 300 | end 301 | 302 | // c.j[al]r/mv/add 303 | 16'b10010: begin 304 | if(!ir_in[12]) begin 305 | // c.jr 306 | if(ir_in[6:2] == 5'b00000) begin 307 | ir_out = {12'b000000000000, ir_in[11:7], 3'b000, 5'b00000, 7'b1100111}; 308 | end 309 | // c.mv 310 | else begin 311 | ir_out = {7'b0000000, ir_in[6:2], 5'b00000, 3'b000, ir_in[11:7], 7'b0110011}; 312 | end 313 | end 314 | else begin 315 | // c.ebreak 316 | if(ir_in[11:7] == 5'b00000 && ir_in[6:2] == 5'b00000) begin 317 | ir_out = 32'h00100073; 318 | end 319 | // c.jalr 320 | else if(ir_in[6:2] == 5'b00000) begin 321 | ir_out = {12'b000000000000, ir_in[11:7], 3'b000, 5'b00001, 7'b1100111}; 322 | end 323 | // c.add 324 | else begin 325 | ir_out = {7'b0000000, ir_in[6:2], ir_in[11:7], 3'b000, ir_in[11:7], 7'b0110011}; 326 | end 327 | end 328 | end 329 | 330 | // c.swsp 331 | 16'b11010: begin 332 | ir_out = {4'b0000, ir_in[8:7], ir_in[12], ir_in[6:2], 5'b00010, 3'b010, ir_in[12:10], 2'b00, 7'b0100011}; 333 | end 334 | 335 | // c.sdsp 336 | 16'b11110: begin 337 | ir_out = {3'b000, ir_in[9:7], ir_in[12], ir_in[6:2], 5'b00010, 3'b011, ir_in[12:11], 3'b000, 7'b0100011}; 338 | end 339 | 340 | endcase 341 | end 342 | 343 | always @(posedge clk) begin 344 | if(!rst_n) sc <= 0; 345 | else if(!stall) begin 346 | if( amo_ack) sc <= sc + 1; 347 | if(!amo_req) sc <= 0; 348 | end 349 | end 350 | 351 | endmodule 352 | -------------------------------------------------------------------------------- /rtl/core/cmem.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module cmem ( 20 | // write bus 21 | input [63:0] b_addr_w, 22 | input [63:0] b_wdata_w, 23 | input [ 1:0] b_len_w, 24 | input b_wr_w, 25 | 26 | // imem read bus 27 | input [`IMEM_BLK_LEN-1:0] b_addr_i, 28 | output reg [ `IMEM_LINE-1:0] b_data_i, 29 | input b_rd_i, 30 | output reg b_dv_i, 31 | 32 | // dmem read bus 33 | input [`DMEM_BLK_LEN-1:0] b_addr_d, 34 | output reg [ `DMEM_LINE-1:0] b_rdata_d, 35 | input b_rd_d, 36 | output reg b_dv_d, 37 | 38 | // external bus 39 | output reg [`CMEM_BLK_LEN-1:0] b_addr_c, 40 | input [ `CMEM_LINE-1:0] b_rdata_c, 41 | output reg b_rd_c, 42 | input b_dv_c, 43 | 44 | // cache invalidation 45 | input [`CMEM_BLK_LEN-1:0] b_inv_addr_c, 46 | input inv, 47 | 48 | // control signals 49 | input rst_n, 50 | input clk 51 | ); 52 | 53 | `ifdef CMEM_SET_ASSOC 54 | 55 | wire [ `CMEM_TAG_LEN-1:0] addr_i_tag = b_addr_i[`CMEM_I_ADDR_TAG_RANGE ]; 56 | wire [ `CMEM_SET_LEN-1:0] addr_i_set = b_addr_i[`CMEM_I_ADDR_SET_RANGE ]; 57 | wire [`CMEM_I_OFFS_LEN-1:0] addr_i_offs = b_addr_i[`CMEM_I_ADDR_OFFS_RANGE]; 58 | 59 | wire [ `CMEM_TAG_LEN-1:0] addr_d_tag = b_addr_d[`CMEM_D_ADDR_TAG_RANGE ]; 60 | wire [ `CMEM_SET_LEN-1:0] addr_d_set = b_addr_d[`CMEM_D_ADDR_SET_RANGE ]; 61 | wire [`CMEM_D_OFFS_LEN-1:0] addr_d_offs = b_addr_d[`CMEM_D_ADDR_OFFS_RANGE]; 62 | 63 | wire [ `CMEM_TAG_LEN-1:0] addr_w_tag = b_addr_w[`CMEM_W_ADDR_TAG_RANGE ]; 64 | wire [ `CMEM_SET_LEN-1:0] addr_w_set = b_addr_w[`CMEM_W_ADDR_SET_RANGE ]; 65 | wire [`CMEM_W_OFFS_LEN-1:0] addr_w_offs = b_addr_w[`CMEM_W_ADDR_OFFS_RANGE]; 66 | 67 | (* ram_style = "block" *) 68 | reg [ `CMEM_LINE-1:0] data [0:`CMEM_LINES-1]; 69 | reg [`CMEM_TAG_LEN-1:0] tag [0:`CMEM_LINES-1]; 70 | reg v [0:`CMEM_LINES-1]; 71 | 72 | reg [`CMEM_WAY_LEN-1:0] re; 73 | reg hit_i; 74 | reg hit_d; 75 | reg hit_w; 76 | 77 | /* BRAM WRITE */ 78 | 79 | reg [`CMEM_TAG_LEN-1:0] tag_d; 80 | reg [`CMEM_SET_LEN-1:0] set_d; 81 | reg [`CMEM_WAY_LEN-1:0] way_d; 82 | reg [ `CMEM_LINE-1:0] d; 83 | reg wre; 84 | always @(posedge clk) if(wre) data[`CMEM_WAYS * set_d + way_d] <= d; 85 | 86 | /* BRAM READ I */ 87 | 88 | reg [`CMEM_TAG_LEN-1:0] tag_qi; 89 | reg [`CMEM_SET_LEN-1:0] set_qi; 90 | reg [`CMEM_WAY_LEN-1:0] way_qi; 91 | reg [ `CMEM_LINE-1:0] qi; 92 | reg rde_i; 93 | always @(posedge clk) if(rde_i) qi <= data[`CMEM_WAYS * set_qi + way_qi]; 94 | 95 | /* BRAM READ D */ 96 | 97 | reg [`CMEM_TAG_LEN-1:0] tag_qd; 98 | reg [`CMEM_SET_LEN-1:0] set_qd; 99 | reg [`CMEM_WAY_LEN-1:0] way_qd; 100 | reg [ `CMEM_LINE-1:0] qd; 101 | reg rde_d; 102 | always @(posedge clk) if(rde_d) qd <= data[`CMEM_WAYS * set_qd + way_qd]; 103 | 104 | /* READ BUFFER */ 105 | 106 | reg [`CMEM_TAG_LEN-1:0] rb_tag_i; 107 | reg [`CMEM_SET_LEN-1:0] rb_set_i; 108 | reg rb_v_i; 109 | 110 | wire rb_hit_i = rb_v_i && rb_tag_i == addr_i_tag && rb_set_i == addr_i_set; 111 | 112 | reg [`CMEM_TAG_LEN-1:0] rb_tag_d; 113 | reg [`CMEM_SET_LEN-1:0] rb_set_d; 114 | reg rb_v_d; 115 | 116 | wire rb_hit_d = rb_v_d && rb_tag_d == addr_d_tag && rb_set_d == addr_d_set; 117 | wire rb_hit_w = rb_v_d && rb_tag_d == addr_w_tag && rb_set_d == addr_w_set; 118 | 119 | /* WRITE BUFFER */ 120 | 121 | reg [ `CMEM_TAG_LEN-1:0] wb_tag; 122 | reg [ `CMEM_SET_LEN-1:0] wb_set; 123 | reg [`CMEM_OFFS_LEN-1:0] wb_offs; 124 | reg [ 63:0] wb_data; 125 | reg [ 1:0] wb_len; 126 | reg wr_pend; 127 | 128 | /* FSM */ 129 | 130 | reg [3:0] ld_cnt; 131 | 132 | reg [1:0] cmem_fsm_state; 133 | reg [1:0] cmem_fsm_state_next; 134 | 135 | `define CMEM_S_READY 2'd0 136 | `define CMEM_S_FETCH 2'd1 137 | `define CMEM_S_LOAD 2'd2 138 | `define CMEM_S_WRITE 2'd3 139 | 140 | reg [1:0] pend; 141 | reg [1:0] pend_next; 142 | 143 | `define PEND_NOP 2'd0 144 | `define PEND_RD_I 2'd1 145 | `define PEND_RD_D 2'd2 146 | `define PEND_WR_D 2'd3 147 | 148 | always @(*) begin 149 | // TODO: latch inference 150 | b_rd_c = 0; 151 | rde_i = 0; 152 | rde_d = 0; 153 | wre = 0; 154 | 155 | pend_next = pend; 156 | cmem_fsm_state_next = cmem_fsm_state; 157 | case(cmem_fsm_state) 158 | `CMEM_S_READY: begin 159 | if(b_rd_i && !hit_i && !pend_next) cmem_fsm_state_next = `CMEM_S_FETCH; 160 | if(b_rd_i && hit_i && !pend_next) cmem_fsm_state_next = `CMEM_S_LOAD; 161 | if(b_rd_i && rb_hit_i && !pend_next) cmem_fsm_state_next = `CMEM_S_READY; 162 | if(cmem_fsm_state_next && !pend_next) pend_next = `PEND_RD_I; 163 | 164 | if(b_rd_d && !hit_d && !pend_next) cmem_fsm_state_next = `CMEM_S_FETCH; 165 | if(b_rd_d && hit_d && !pend_next) cmem_fsm_state_next = `CMEM_S_LOAD; 166 | if(b_rd_d && rb_hit_d && !pend_next) cmem_fsm_state_next = `CMEM_S_READY; 167 | if(cmem_fsm_state_next && !pend_next) pend_next = `PEND_RD_D; 168 | 169 | if(b_wr_w && !hit_d && !pend_next) cmem_fsm_state_next = `CMEM_S_FETCH; 170 | if(b_wr_w && hit_d && !pend_next) cmem_fsm_state_next = `CMEM_S_LOAD; 171 | if(b_wr_w && rb_hit_w && !pend_next) cmem_fsm_state_next = `CMEM_S_WRITE; 172 | if(cmem_fsm_state_next && !pend_next) pend_next = `PEND_WR_D; 173 | end 174 | `CMEM_S_FETCH: begin 175 | b_rd_c = 1; 176 | wre = b_dv_c; 177 | 178 | if(b_dv_c) cmem_fsm_state_next = `CMEM_S_LOAD; 179 | end 180 | `CMEM_S_LOAD: begin 181 | if(pend == `PEND_RD_I) rde_i = ld_cnt == `CMEM_READ_VALID_DELAY; 182 | else rde_d = ld_cnt == `CMEM_READ_VALID_DELAY; 183 | 184 | pend_next = pend == `PEND_WR_D ? `PEND_WR_D : `PEND_NOP; 185 | if(!ld_cnt) cmem_fsm_state_next = pend == `PEND_WR_D ? `CMEM_S_WRITE : `CMEM_S_READY; 186 | end 187 | `CMEM_S_WRITE: begin 188 | wre = 1; 189 | pend_next = `PEND_NOP; 190 | cmem_fsm_state_next = `CMEM_S_READY; 191 | end 192 | endcase 193 | end 194 | 195 | /* FSM UPDATE */ 196 | 197 | always @(posedge clk) begin 198 | if(!rst_n) cmem_fsm_state <= `CMEM_S_READY; 199 | else cmem_fsm_state <= cmem_fsm_state_next; 200 | end 201 | 202 | /* PENDING OP UPDATE */ 203 | 204 | always @(posedge clk) begin 205 | if(!rst_n) pend <= `PEND_NOP; 206 | else pend <= pend_next; 207 | end 208 | 209 | /* READ BUFFER */ 210 | 211 | always @(posedge clk) begin 212 | if(!rst_n) begin 213 | rb_tag_i <= 0; 214 | rb_set_i <= 0; 215 | rb_v_i <= 0; 216 | end 217 | else if(cmem_fsm_state == `CMEM_S_LOAD && pend == `PEND_RD_I) begin 218 | rb_tag_i <= addr_i_tag; 219 | rb_set_i <= addr_i_set; 220 | rb_v_i <= 1; 221 | end 222 | end 223 | 224 | always @(posedge clk) begin 225 | if(!rst_n) begin 226 | rb_tag_d <= 0; 227 | rb_set_d <= 0; 228 | rb_v_d <= 0; 229 | end 230 | else if(cmem_fsm_state == `CMEM_S_WRITE) rb_v_d <= 0; 231 | else if(cmem_fsm_state == `CMEM_S_LOAD) begin 232 | if(pend == `PEND_RD_D) begin 233 | rb_tag_d <= addr_d_tag; 234 | rb_set_d <= addr_d_set; 235 | rb_v_d <= 1; 236 | end 237 | if(pend == `PEND_WR_D) begin 238 | rb_tag_d <= wb_tag; 239 | rb_set_d <= wb_set; 240 | rb_v_d <= 1; 241 | end 242 | end 243 | ld_cnt <= cmem_fsm_state == `CMEM_S_LOAD ? ld_cnt - 1 : `CMEM_READ_VALID_DELAY; 244 | end 245 | 246 | /* WRITE BUFFER */ 247 | 248 | always @(posedge clk) begin 249 | if(!rst_n || cmem_fsm_state == `CMEM_S_WRITE) begin 250 | wb_tag <= 0; 251 | wb_set <= 0; 252 | wb_offs <= 0; 253 | wb_data <= 0; 254 | wb_len <= 0; 255 | wr_pend <= 0; 256 | end 257 | else if(b_wr_w && cmem_fsm_state == `CMEM_S_READY) begin 258 | wb_tag <= addr_w_tag; 259 | wb_set <= addr_w_set; 260 | wb_offs <= addr_w_offs; 261 | wb_data <= b_wdata_w; 262 | wb_len <= b_len_w; 263 | wr_pend <= 1; 264 | end 265 | end 266 | 267 | /* REQUEST ADDRESS */ 268 | 269 | always @(*) begin 270 | b_addr_c = 0; 271 | case(pend_next) 272 | `PEND_RD_I: b_addr_c = {addr_i_tag, addr_i_set}; 273 | `PEND_RD_D: b_addr_c = {addr_d_tag, addr_d_set}; 274 | `PEND_WR_D: b_addr_c = {addr_d_tag, addr_d_set}; 275 | endcase 276 | end 277 | 278 | /* METADATA UPDATE */ 279 | 280 | always @(posedge clk) begin 281 | if(!rst_n) begin : cmem_reset 282 | integer i; 283 | for(i = 0; i < `CMEM_LINES; i = i + 1) begin 284 | tag[i] <= 0; 285 | v [i] <= 0; 286 | end 287 | end 288 | else begin 289 | if(wre && cmem_fsm_state == `CMEM_S_FETCH) begin 290 | tag[`CMEM_WAYS * set_d + way_d] <= tag_d; 291 | v [`CMEM_WAYS * set_d + way_d] <= 1; 292 | end 293 | end 294 | end 295 | 296 | /* INPUT MUX */ 297 | 298 | always @(*) begin 299 | if(cmem_fsm_state == `CMEM_S_WRITE) begin 300 | d = qd; 301 | case(wb_len) 302 | 2'b00: d[8*wb_offs +: 8] = wb_data[ 7:0]; 303 | 2'b01: d[8*wb_offs +: 16] = wb_data[15:0]; 304 | 2'b10: d[8*wb_offs +: 32] = wb_data[31:0]; 305 | 2'b11: d[8*wb_offs +: 64] = wb_data[63:0]; 306 | endcase 307 | end 308 | else d = b_rdata_c; 309 | end 310 | 311 | /* OUTPUT MUX */ 312 | 313 | always @(*) begin 314 | b_data_i = qi[`IMEM_LINE*addr_i_offs +: `IMEM_LINE]; 315 | b_dv_i = rb_hit_i; 316 | end 317 | 318 | always @(*) begin 319 | b_rdata_d = qd[`DMEM_LINE*addr_d_offs +: `DMEM_LINE]; 320 | b_dv_d = rb_hit_d; 321 | end 322 | 323 | /* HIT DETECTION */ 324 | 325 | always @(*) begin : cmem_cache_hit 326 | integer w; 327 | 328 | way_qi = 'bZ; hit_i = 0; 329 | for(w = 0; w < `CMEM_WAYS; w = w + 1) begin 330 | if(tag[`CMEM_WAYS * set_qi + w] == tag_qi && v[`CMEM_WAYS * set_qi + w]) begin 331 | way_qi = w; hit_i = 1; 332 | end 333 | end 334 | 335 | way_qd = 'bZ; hit_d = 0; 336 | for(w = 0; w < `CMEM_WAYS; w = w + 1) begin 337 | if(tag[`CMEM_WAYS * set_qd + w] == tag_qd && v[`CMEM_WAYS * set_qd + w]) begin 338 | way_qd = w; hit_d = 1; 339 | end 340 | end 341 | 342 | hit_w = 0; 343 | for(w = 0; w < `CMEM_WAYS; w = w + 1) begin 344 | if(tag[`CMEM_WAYS * wb_set + w] == wb_tag && v[`CMEM_WAYS * wb_set + w]) begin 345 | if(pend_next == `PEND_WR_D) way_qd = w; 346 | hit_w = 1; 347 | end 348 | end 349 | end 350 | 351 | /* BRAM READ */ 352 | 353 | always @(*) begin 354 | tag_qi = addr_i_tag; 355 | set_qi = addr_i_set; 356 | end 357 | 358 | always @(*) begin 359 | tag_qd = pend == `PEND_WR_D ? wb_tag : addr_d_tag; 360 | set_qd = pend == `PEND_WR_D ? wb_set : addr_d_set; 361 | end 362 | 363 | /* BRAM WRITE */ 364 | 365 | always @(*) begin 366 | tag_d = 'bZ; set_d = 'bZ; 367 | case(pend) 368 | `PEND_RD_I: begin 369 | tag_d = addr_i_tag; 370 | set_d = addr_i_set; 371 | end 372 | `PEND_RD_D: begin 373 | tag_d = addr_d_tag; 374 | set_d = addr_d_set; 375 | end 376 | `PEND_WR_D: begin 377 | tag_d = wb_tag; 378 | set_d = wb_set; 379 | end 380 | endcase 381 | end 382 | 383 | always @(*) begin 384 | way_d = cmem_fsm_state == `CMEM_S_WRITE ? way_qd : re; 385 | end 386 | 387 | `endif//CMEM_SET_ASSOC 388 | 389 | // TODO: 390 | /* REPLACEMENT POLICY */ 391 | 392 | always @(posedge clk) re <= $random() % `CMEM_WAYS; 393 | 394 | endmodule 395 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CERN Open Hardware Licence Version 2 - Strongly Reciprocal 2 | 3 | 4 | Preamble 5 | 6 | CERN has developed this licence to promote collaboration among hardware 7 | designers and to provide a legal tool which supports the freedom to use, 8 | study, modify, share and distribute hardware designs and products based on 9 | those designs. Version 2 of the CERN Open Hardware Licence comes in three 10 | variants: CERN-OHL-P (permissive); and two reciprocal licences: CERN-OHL-W 11 | (weakly reciprocal) and this licence, CERN-OHL-S (strongly reciprocal). 12 | 13 | The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in 14 | unmodified form only. 15 | 16 | Use of this Licence does not imply any endorsement by CERN of any Licensor or 17 | their designs nor does it imply any involvement by CERN in their development. 18 | 19 | 20 | 1 Definitions 21 | 22 | 1.1 'Licence' means this CERN-OHL-S. 23 | 24 | 1.2 'Compatible Licence' means 25 | 26 | a) any earlier version of the CERN Open Hardware licence, or 27 | 28 | b) any version of the CERN-OHL-S, or 29 | 30 | c) any licence which permits You to treat the Source to which it 31 | applies as licensed under CERN-OHL-S provided that on Conveyance of 32 | any such Source, or any associated Product You treat the Source in 33 | question as being licensed under CERN-OHL-S. 34 | 35 | 1.3 'Source' means information such as design materials or digital code 36 | which can be applied to Make or test a Product or to prepare a Product 37 | for use, Conveyance or sale, regardless of its medium or how it is 38 | expressed. It may include Notices. 39 | 40 | 1.4 'Covered Source' means Source that is explicitly made available under 41 | this Licence. 42 | 43 | 1.5 'Product' means any device, component, work or physical object, whether 44 | in finished or intermediate form, arising from the use, application or 45 | processing of Covered Source. 46 | 47 | 1.6 'Make' means to create or configure something, whether by manufacture, 48 | assembly, compiling, loading or applying Covered Source or another 49 | Product or otherwise. 50 | 51 | 1.7 'Available Component' means any part, sub-assembly, library or code 52 | which: 53 | 54 | a) is licensed to You as Complete Source under a Compatible Licence; or 55 | 56 | b) is available, at the time a Product or the Source containing it is 57 | first Conveyed, to You and any other prospective licensees 58 | 59 | i) as a physical part with sufficient rights and information 60 | (including any configuration and programming files and 61 | information about its characteristics and interfaces) to enable 62 | it either to be Made itself, or to be sourced and used to Make 63 | the Product; or 64 | ii) as part of the normal distribution of a tool used to design or 65 | Make the Product. 66 | 67 | 1.8 'Complete Source' means the set of all Source necessary to Make a 68 | Product, in the preferred form for making modifications, including 69 | necessary installation and interfacing information both for the Product, 70 | and for any included Available Components. If the format is 71 | proprietary, it must also be made available in a format (if the 72 | proprietary tool can create it) which is viewable with a tool available 73 | to potential licensees and licensed under a licence approved by the Free 74 | Software Foundation or the Open Source Initiative. Complete Source need 75 | not include the Source of any Available Component, provided that You 76 | include in the Complete Source sufficient information to enable a 77 | recipient to Make or source and use the Available Component to Make the 78 | Product. 79 | 80 | 1.9 'Source Location' means a location where a Licensor has placed Covered 81 | Source, and which that Licensor reasonably believes will remain easily 82 | accessible for at least three years for anyone to obtain a digital copy. 83 | 84 | 1.10 'Notice' means copyright, acknowledgement and trademark notices, Source 85 | Location references, modification notices (subsection 3.3(b)) and all 86 | notices that refer to this Licence and to the disclaimer of warranties 87 | that are included in the Covered Source. 88 | 89 | 1.11 'Licensee' or 'You' means any person exercising rights under this 90 | Licence. 91 | 92 | 1.12 'Licensor' means a natural or legal person who creates or modifies 93 | Covered Source. A person may be a Licensee and a Licensor at the same 94 | time. 95 | 96 | 1.13 'Convey' means to communicate to the public or distribute. 97 | 98 | 99 | 2 Applicability 100 | 101 | 2.1 This Licence governs the use, copying, modification, Conveying of 102 | Covered Source and Products, and the Making of Products. By exercising 103 | any right granted under this Licence, You irrevocably accept these terms 104 | and conditions. 105 | 106 | 2.2 This Licence is granted by the Licensor directly to You, and shall apply 107 | worldwide and without limitation in time. 108 | 109 | 2.3 You shall not attempt to restrict by contract or otherwise the rights 110 | granted under this Licence to other Licensees. 111 | 112 | 2.4 This Licence is not intended to restrict fair use, fair dealing, or any 113 | other similar right. 114 | 115 | 116 | 3 Copying, Modifying and Conveying Covered Source 117 | 118 | 3.1 You may copy and Convey verbatim copies of Covered Source, in any 119 | medium, provided You retain all Notices. 120 | 121 | 3.2 You may modify Covered Source, other than Notices, provided that You 122 | irrevocably undertake to make that modified Covered Source available 123 | from a Source Location should You Convey a Product in circumstances 124 | where the recipient does not otherwise receive a copy of the modified 125 | Covered Source. In each case subsection 3.3 shall apply. 126 | 127 | You may only delete Notices if they are no longer applicable to the 128 | corresponding Covered Source as modified by You and You may add 129 | additional Notices applicable to Your modifications. Including Covered 130 | Source in a larger work is modifying the Covered Source, and the larger 131 | work becomes modified Covered Source. 132 | 133 | 3.3 You may Convey modified Covered Source (with the effect that You shall 134 | also become a Licensor) provided that You: 135 | 136 | a) retain Notices as required in subsection 3.2; 137 | 138 | b) add a Notice to the modified Covered Source stating that You have 139 | modified it, with the date and brief description of how You have 140 | modified it; 141 | 142 | c) add a Source Location Notice for the modified Covered Source if You 143 | Convey in circumstances where the recipient does not otherwise 144 | receive a copy of the modified Covered Source; and 145 | 146 | d) license the modified Covered Source under the terms and conditions 147 | of this Licence (or, as set out in subsection 8.3, a later version, 148 | if permitted by the licence of the original Covered Source). Such 149 | modified Covered Source must be licensed as a whole, but excluding 150 | Available Components contained in it, which remain licensed under 151 | their own applicable licences. 152 | 153 | 154 | 4 Making and Conveying Products 155 | 156 | You may Make Products, and/or Convey them, provided that You either provide 157 | each recipient with a copy of the Complete Source or ensure that each 158 | recipient is notified of the Source Location of the Complete Source. That 159 | Complete Source is Covered Source, and You must accordingly satisfy Your 160 | obligations set out in subsection 3.3. If specified in a Notice, the Product 161 | must visibly and securely display the Source Location on it or its packaging 162 | or documentation in the manner specified in that Notice. 163 | 164 | 165 | 5 Research and Development 166 | 167 | You may Convey Covered Source, modified Covered Source or Products to a legal 168 | entity carrying out development, testing or quality assurance work on Your 169 | behalf provided that the work is performed on terms which prevent the entity 170 | from both using the Source or Products for its own internal purposes and 171 | Conveying the Source or Products or any modifications to them to any person 172 | other than You. Any modifications made by the entity shall be deemed to be 173 | made by You pursuant to subsection 3.2. 174 | 175 | 176 | 6 DISCLAIMER AND LIABILITY 177 | 178 | 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products are 179 | provided 'as is' and any express or implied warranties, including, but 180 | not limited to, implied warranties of merchantability, of satisfactory 181 | quality, non-infringement of third party rights, and fitness for a 182 | particular purpose or use are disclaimed in respect of any Source or 183 | Product to the maximum extent permitted by law. The Licensor makes no 184 | representation that any Source or Product does not or will not infringe 185 | any patent, copyright, trade secret or other proprietary right. The 186 | entire risk as to the use, quality, and performance of any Source or 187 | Product shall be with You and not the Licensor. This disclaimer of 188 | warranty is an essential part of this Licence and a condition for the 189 | grant of any rights granted under this Licence. 190 | 191 | 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to the 192 | maximum extent permitted by law, have no liability for direct, indirect, 193 | special, incidental, consequential, exemplary, punitive or other damages 194 | of any character including, without limitation, procurement of 195 | substitute goods or services, loss of use, data or profits, or business 196 | interruption, however caused and on any theory of contract, warranty, 197 | tort (including negligence), product liability or otherwise, arising in 198 | any way in relation to the Covered Source, modified Covered Source 199 | and/or the Making or Conveyance of a Product, even if advised of the 200 | possibility of such damages, and You shall hold the Licensor(s) free and 201 | harmless from any liability, costs, damages, fees and expenses, 202 | including claims by third parties, in relation to such use. 203 | 204 | 205 | 7 Patents 206 | 207 | 7.1 Subject to the terms and conditions of this Licence, each Licensor 208 | hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, 209 | royalty-free, irrevocable (except as stated in subsections 7.2 and 8.4) 210 | patent licence to Make, have Made, use, offer to sell, sell, import, and 211 | otherwise transfer the Covered Source and Products, where such licence 212 | applies only to those patent claims licensable by such Licensor that are 213 | necessarily infringed by exercising rights under the Covered Source as 214 | Conveyed by that Licensor. 215 | 216 | 7.2 If You institute patent litigation against any entity (including a 217 | cross-claim or counterclaim in a lawsuit) alleging that the Covered 218 | Source or a Product constitutes direct or contributory patent 219 | infringement, or You seek any declaration that a patent licensed to You 220 | under this Licence is invalid or unenforceable then any rights granted 221 | to You under this Licence shall terminate as of the date such process is 222 | initiated. 223 | 224 | 225 | 8 General 226 | 227 | 8.1 If any provisions of this Licence are or subsequently become invalid or 228 | unenforceable for any reason, the remaining provisions shall remain 229 | effective. 230 | 231 | 8.2 You shall not use any of the name (including acronyms and 232 | abbreviations), image, or logo by which the Licensor or CERN is known, 233 | except where needed to comply with section 3, or where the use is 234 | otherwise allowed by law. Any such permitted use shall be factual and 235 | shall not be made so as to suggest any kind of endorsement or 236 | implication of involvement by the Licensor or its personnel. 237 | 238 | 8.3 CERN may publish updated versions and variants of this Licence which it 239 | considers to be in the spirit of this version, but may differ in detail 240 | to address new problems or concerns. New versions will be published with 241 | a unique version number and a variant identifier specifying the variant. 242 | If the Licensor has specified that a given variant applies to the 243 | Covered Source without specifying a version, You may treat that Covered 244 | Source as being released under any version of the CERN-OHL with that 245 | variant. If no variant is specified, the Covered Source shall be treated 246 | as being released under CERN-OHL-S. The Licensor may also specify that 247 | the Covered Source is subject to a specific version of the CERN-OHL or 248 | any later version in which case You may apply this or any later version 249 | of CERN-OHL with the same variant identifier published by CERN. 250 | 251 | 8.4 This Licence shall terminate with immediate effect if You fail to comply 252 | with any of its terms and conditions. 253 | 254 | 8.5 However, if You cease all breaches of this Licence, then Your Licence 255 | from any Licensor is reinstated unless such Licensor has terminated this 256 | Licence by giving You, while You remain in breach, a notice specifying 257 | the breach and requiring You to cure it within 30 days, and You have 258 | failed to come into compliance in all material respects by the end of 259 | the 30 day period. Should You repeat the breach after receipt of a cure 260 | notice and subsequent reinstatement, this Licence will terminate 261 | immediately and permanently. Section 6 shall continue to apply after any 262 | termination. 263 | 264 | 8.6 This Licence shall not be enforceable except by a Licensor acting as 265 | such, and third party beneficiary rights are specifically excluded. 266 | -------------------------------------------------------------------------------- /tb/tb_core.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../rtl/config.vh" 18 | 19 | `define OBJCOPY "/opt/riscv/bin/riscv64-unknown-elf-objcopy" 20 | 21 | `define DROMAJO_BOOTROM_TRACE "\ 22 | 0 3 0x0000000000010000 (0xf1402573) x10 0x0000000000000000\n\ 23 | 0 3 0x0000000000010004 (0x00050663)\n\ 24 | 0 3 0x0000000000010010 (0x00000597) x11 0x0000000000010010\n\ 25 | 0 3 0x0000000000010014 (0x0f058593) x11 0x0000000000010100\n\ 26 | 0 3 0x0000000000010018 (0x60300413) x 8 0x0000000000000603\n\ 27 | 0 3 0x000000000001001c (0x7b041073)\n\ 28 | 0 3 0x0000000000010020 (0x0010041b) x 8 0x0000000000000001\n\ 29 | 0 3 0x0000000000010024 (0x01f41413) x 8 0x0000000080000000\n\ 30 | 0 3 0x0000000000010028 (0x7b141073)\n\ 31 | 0 3 0x000000000001002c (0x7b200073)" 32 | 33 | `ifdef ANSI_COLORS 34 | `define TEST_PASSED "\x1b[1;32mpassed\x1b[0m" 35 | `define TEST_FAILED "\x1b[1;31mfailed\x1b[0m" 36 | `define TEST_REPORT_FMT "riscv-tests finished: \x1b[1;32m%-d\x1b[0m passed, \x1b[1;31m%-d\x1b[0m failed!" 37 | `else 38 | `define TEST_PASSED "passed" 39 | `define TEST_FAILED "failed" 40 | `define TEST_REPORT_FMT "riscv-tests finished: %-d passed, %-d failed!" 41 | `endif 42 | 43 | `ifdef DROMAJO_VERBOSE 44 | `define DROMAJO_OUTPUT " 2> dromajo/", this.name, ".dromajo.log > dromajo/", this.name, ".dromajo.log" 45 | `else 46 | `define DROMAJO_OUTPUT " > /dev/null 2> /dev/null" 47 | `endif 48 | 49 | `timescale 1ns/1ps 50 | module tb_core; 51 | 52 | wire [63:0] c_addr; 53 | wire c_ext; 54 | reg [`CMEM_LINE-1:0] c_rdata; 55 | wire c_rd; 56 | reg c_dv; 57 | wire [63:0] c_wdata; 58 | wire [ 1:0] c_len; 59 | wire c_wr; 60 | reg c_irq_me; 61 | reg c_irq_mt; 62 | reg c_irq_ms; 63 | reg c_irq_se; 64 | reg c_irq_st; 65 | reg c_irq_ss; 66 | reg [63:0] c_inv_addr; 67 | reg c_inv; 68 | wire c_amo_req; 69 | reg c_amo_ack; 70 | reg c_stall; 71 | reg c_rst_n; 72 | wire c_clk; 73 | 74 | enum integer {IF, PD, ID, EX, MEM, WB} phase; 75 | 76 | rv6_core #(.HART_ID(0)) dut ( 77 | .c_addr (c_addr ), 78 | .c_ext (c_ext ), 79 | .c_rdata (c_rdata ), 80 | .c_rd (c_rd ), 81 | .c_dv (c_dv ), 82 | .c_wdata (c_wdata ), 83 | .c_len (c_len ), 84 | .c_wr (c_wr ), 85 | .c_irq_me (c_irq_me ), 86 | .c_irq_mt (c_irq_mt ), 87 | .c_irq_ms (c_irq_ms ), 88 | .c_irq_se (c_irq_se ), 89 | .c_irq_st (c_irq_st ), 90 | .c_irq_ss (c_irq_ss ), 91 | .c_inv_addr (c_inv_addr ), 92 | .c_inv (c_inv ), 93 | .c_amo_req (c_amo_req ), 94 | .c_amo_ack (c_amo_ack ), 95 | .c_stall (c_stall ), 96 | .c_rst_n (c_rst_n ), 97 | .c_clk (c_clk ) 98 | ); 99 | 100 | // initial signal values 101 | initial begin 102 | c_rdata = 64'bZ; 103 | c_dv = 0; 104 | c_irq_me = 0; 105 | c_irq_mt = 0; 106 | c_irq_ms = 0; 107 | c_irq_se = 0; 108 | c_irq_st = 0; 109 | c_irq_ss = 0; 110 | c_inv_addr = 64'bZ; 111 | c_inv = 0; 112 | c_amo_ack = 0; 113 | c_stall = 0; 114 | c_rst_n = 1; 115 | end 116 | 117 | // clock generator 118 | reg clk; 119 | initial begin 120 | clk = 1; 121 | forever #10 clk = ~clk; 122 | end 123 | assign c_clk = clk; 124 | 125 | /* MEMORY MODEL */ 126 | 127 | mem tb_mem ( 128 | .addr (c_addr ), 129 | .wdata (c_wdata ), 130 | .rdata (c_rdata ), 131 | .len (c_len ), 132 | .rd (c_rd ), 133 | .wr (c_wr ), 134 | .dv (c_dv ), 135 | .rst_n (c_rst_n ), 136 | .clk (c_clk ) 137 | ); 138 | 139 | /*--------------------------------------------------------------------------------*/ 140 | /* EXCEPTION */ 141 | /*--------------------------------------------------------------------------------*/ 142 | 143 | class Exception; 144 | bit [63:0] cause; 145 | bit [63:0] tval; 146 | 147 | function new(input bit [63:0] cause, input bit [63:0] tval); 148 | this.cause = cause; 149 | this.tval = tval; 150 | endfunction 151 | 152 | virtual function string what(); 153 | return $sformatf("exception %-d, tval %16h", this.cause, this.tval); 154 | endfunction 155 | 156 | virtual function string err_msg(); 157 | return ""; 158 | endfunction 159 | endclass 160 | 161 | class EcallException extends Exception; 162 | function new(input bit [63:0] cause); 163 | super.new(cause, 0); 164 | endfunction 165 | endclass 166 | 167 | class BreakpointException extends Exception; 168 | function new(); 169 | super.new(3, 0); 170 | endfunction 171 | endclass 172 | 173 | class InvalidCSRException extends Exception; 174 | bit [11:0] csr_addr; 175 | 176 | function new(input bit [11:0] csr_addr); 177 | super.new(2, 0); 178 | this.csr_addr = csr_addr; 179 | endfunction 180 | 181 | function string err_msg(); 182 | return $sformatf( 183 | "csr_read: invalid CSR=0x%-h\n", 184 | this.csr_addr 185 | ); 186 | endfunction 187 | endclass 188 | 189 | class PrivilegeCSRException extends Exception; 190 | function new(); 191 | super.new(2, 0); 192 | endfunction 193 | endclass 194 | 195 | class WriteInvalidCSRException extends Exception; 196 | function new(); 197 | super.new(2, 0); 198 | endfunction 199 | endclass 200 | 201 | class MisalignedLoadAddressException extends Exception; 202 | function new(input bit [63:0] tval); 203 | super.new(4, tval); 204 | endfunction 205 | endclass 206 | 207 | class MisalignedStoreAddressException extends Exception; 208 | function new(input bit [63:0] tval); 209 | super.new(6, tval); 210 | endfunction 211 | endclass 212 | 213 | class IllegalInstructionException extends Exception; 214 | function new(); 215 | super.new(2, 0); 216 | endfunction 217 | endclass 218 | 219 | class InstructionAccessFaultException extends Exception; 220 | function new(); 221 | super.new(1, 0); 222 | endfunction 223 | 224 | function string what(); 225 | return ""; 226 | endfunction 227 | endclass 228 | 229 | /*--------------------------------------------------------------------------------*/ 230 | /* INSTRUCTION */ 231 | /*--------------------------------------------------------------------------------*/ 232 | 233 | class Instruction; 234 | 235 | bit [31:0] ir; 236 | bit [63:0] pc; 237 | bit [63:0] hart_id; 238 | bit [ 1:0] priv_lvl; 239 | Exception e; 240 | bit trap_ret; 241 | 242 | function new( 243 | input bit [63:0] hart_id, 244 | input bit [ 1:0] priv_lvl, 245 | input bit [31:0] ir, 246 | input bit [63:0] pc 247 | ); 248 | this.hart_id = hart_id; 249 | this.priv_lvl = priv_lvl; 250 | 251 | this.ir = ir; 252 | this.pc = pc; 253 | 254 | this.e = null; 255 | this.trap_ret = 0; 256 | endfunction 257 | 258 | function string retire(); 259 | if(this.e) begin 260 | string trace = $sformatf( 261 | "%-d %-d 0x%16h (0x%8h) %s", 262 | this.hart_id, 263 | this.priv_lvl, 264 | this.pc, 265 | this.ir, 266 | this.e.what() 267 | ); 268 | 269 | return {this.e.err_msg(), trace}; 270 | end 271 | else if(dut.we && dut.rd) begin 272 | return $sformatf( 273 | "%-d %-d 0x%16h (0x%8h) x%2d 0x%16h", 274 | this.hart_id, 275 | this.priv_lvl, 276 | this.pc, 277 | this.ir, 278 | dut.rd, 279 | dut.rd_data 280 | ); 281 | end 282 | else begin 283 | return $sformatf( 284 | "%-d %-d 0x%16h (0x%8h)", 285 | this.hart_id, 286 | this.priv_lvl, 287 | this.pc, 288 | this.ir 289 | ); 290 | end 291 | endfunction 292 | endclass 293 | 294 | /*--------------------------------------------------------------------------------*/ 295 | /* TEST */ 296 | /*--------------------------------------------------------------------------------*/ 297 | 298 | class Test; 299 | string name; 300 | bit passed; 301 | local string elf; 302 | local Instruction retired; 303 | local integer fd; 304 | Instruction pipeline [1:5]; 305 | 306 | function new(input string elf); 307 | integer k; 308 | this.elf = elf; 309 | for(integer i = 0; i < elf.len(); ++i) begin 310 | if(elf.getc(i) == 8'h2F) k = i+1; 311 | end 312 | this.name = elf.substr(k, elf.len()-1); 313 | endfunction 314 | 315 | // dromajo cosim startup 316 | local task dromajo_cosim(); 317 | $system({`OBJCOPY, " -O verilog ", this.elf, " temp.hex"}); 318 | tb_mem.read_hex("temp.hex"); 319 | $system("rm -f temp.hex"); 320 | 321 | // dromajo runs debug mode bootrom at 0x10000 322 | // there's no debug mode implemented on this core so this section 323 | // is just skipped, but trace still has to be printed for trace comparison 324 | $fdisplay(this.fd, `DROMAJO_BOOTROM_TRACE); 325 | endtask 326 | 327 | // synchronizes simulation pipeline with DUT pipeline 328 | local task pipeline_sync(); 329 | forever begin 330 | @(posedge clk) begin 331 | if(!this.fd) break; 332 | this.retire_handler(); 333 | 334 | if(!dut.stall_mem) this.pipeline[WB] = this.pipeline[MEM]; 335 | if(!dut.stall_ex) this.pipeline[MEM] = this.pipeline[EX]; 336 | if(!dut.stall_id) this.pipeline[EX] = this.pipeline[ID]; 337 | if(!dut.stall_pd) this.pipeline[ID] = this.pipeline[PD]; 338 | if(!dut.stall_if && !dut.fence_i) begin 339 | this.pipeline[PD] = new(0, dut.u_csr.priv, (dut.c_ins ? {16'b0, dut.ir[15:0]} : dut.ir), dut.pc); 340 | end 341 | 342 | if(this.pipeline[MEM]) begin 343 | if(dut.t_flush && !this.pipeline[MEM].e && !this.pipeline[MEM].trap_ret) this.pipeline[MEM] = null; 344 | end 345 | 346 | if(dut.t_flush) this.pipeline[EX] = null; 347 | 348 | if(dut.t_flush || dut.flush_id) begin 349 | this.pipeline[ID] = null; 350 | end 351 | 352 | if(dut.t_flush || dut.flush_pd) begin 353 | this.pipeline[PD] = null; 354 | end 355 | end 356 | end 357 | endtask 358 | 359 | // retire functions and write them to trace file 360 | local task retire_handler(); 361 | if(!dut.stall_wb && this.pipeline[WB] != null) begin 362 | if(this.retired != this.pipeline[WB]) begin 363 | if(this.pipeline[WB].ir != dut.bmw_ir && !this.pipeline[WB].e && !this.pipeline[WB].trap_ret) begin 364 | this.pipeline[WB].ir = dut.bmw_ir; 365 | end 366 | this.retired = this.pipeline[WB]; 367 | $fdisplay( 368 | this.fd, 369 | "%s", 370 | this.pipeline[WB].retire() 371 | ); 372 | end 373 | end 374 | endtask 375 | 376 | // snoop on core traps and update sim pipeline 377 | local task exception_handler(); 378 | forever begin 379 | @(negedge clk) begin 380 | if(!this.fd) break; 381 | if(dut.u_csr.tret && this.pipeline[MEM]) begin 382 | this.pipeline[MEM].trap_ret = 1; 383 | end 384 | else if(dut.u_exc.exc && this.pipeline[MEM]) begin 385 | case(dut.u_exc.exc_cause) 386 | 6'd1: begin 387 | automatic InstructionAccessFaultException ex = new(); 388 | this.pipeline[MEM].e = ex; 389 | end 390 | 6'd2: begin 391 | if(dut.u_csr.csr_addr_invalid) begin 392 | automatic InvalidCSRException ex = new(dut.u_csr.csr_addr); 393 | this.pipeline[MEM].e = ex; 394 | end 395 | else if(dut.u_csr.csr_wr_invalid) begin 396 | automatic WriteInvalidCSRException ex = new(); 397 | this.pipeline[MEM].e = ex; 398 | end 399 | else if(dut.u_csr.csr_pr_invalid) begin 400 | automatic PrivilegeCSRException ex = new(); 401 | this.pipeline[MEM].e = ex; 402 | end 403 | else begin 404 | automatic IllegalInstructionException ex = new(); 405 | this.pipeline[MEM].e = ex; 406 | end 407 | end 408 | 6'd3: begin 409 | automatic BreakpointException ex = new(); 410 | this.pipeline[MEM].e = ex; 411 | end 412 | 6'd4: begin 413 | automatic MisalignedLoadAddressException ex = new(dut.u_csr.tval); 414 | this.pipeline[MEM].e = ex; 415 | end 416 | 6'd6: begin 417 | automatic MisalignedStoreAddressException ex = new(dut.u_csr.tval); 418 | this.pipeline[MEM].e = ex; 419 | end 420 | 6'd8, 6'd9, 6'd11: begin 421 | automatic EcallException ex = new(dut.u_csr.tcause); 422 | this.pipeline[MEM].e = ex; 423 | end 424 | endcase 425 | end 426 | end 427 | end 428 | endtask 429 | 430 | // sim termination monitor 431 | local task tohost_monitor(); 432 | forever begin 433 | @(negedge clk) begin 434 | if(this.retired != null && (this.retired.ir == 32'hfc3f2223 || this.retired.ir == 32'hfc3f2023)) begin 435 | $fclose(this.fd); 436 | this.fd = 0; 437 | this.passed = $system({`DROMAJO_COSIM_TEST, " cosim trace/", this.name, ".trace ", this.elf, `DROMAJO_OUTPUT}) == 0; 438 | break; 439 | end 440 | end 441 | end 442 | endtask 443 | 444 | local task timeout(); 445 | `ifdef ELF 446 | #300_000; 447 | `else 448 | #2_000_000; 449 | `endif 450 | endtask 451 | 452 | task run(); 453 | // core reset signal 454 | #80 455 | c_rst_n = 0; 456 | #80; 457 | c_rst_n = 1; 458 | 459 | // trace file handle init 460 | this.fd = $fopen({"trace/", this.name, ".trace"}, "w"); 461 | 462 | // run co-sim 463 | this.dromajo_cosim(); 464 | fork 465 | this.pipeline_sync(); 466 | this.exception_handler(); 467 | this.tohost_monitor(); 468 | this.timeout(); 469 | join_any 470 | disable fork; 471 | endtask 472 | endclass 473 | 474 | /*--------------------------------------------------------------------------------*/ 475 | /* RISC-V TEST ENVIRONMENT */ 476 | /*--------------------------------------------------------------------------------*/ 477 | 478 | class RiscvTestEnv; 479 | Test t; 480 | string path; 481 | integer passed; 482 | integer failed; 483 | 484 | function new(input string path); 485 | this.path = path; 486 | this.passed = 0; 487 | this.failed = 0; 488 | $system("rm tb_core.lst"); 489 | endfunction 490 | 491 | // generate list of tests based on template 492 | task gen_file_list(input string template); 493 | $system({"find ", this.path, " -name '", template, "' -not -name '*.dump' >> tb_core.lst"}); 494 | endtask 495 | 496 | // run tests and report 497 | task run(); 498 | integer fd; 499 | string filename; 500 | 501 | $display("Running riscv-tests..."); 502 | fd = $fopen("tb_core.lst", "r"); 503 | while(!$feof(fd)) begin 504 | $fgets(filename, fd); 505 | filename = filename.substr(0, filename.len()-2); 506 | if(filename == "") break; 507 | t = new(filename); 508 | t.run(); 509 | $display( 510 | "%-25s: %s", 511 | t.name, 512 | t.passed ? `TEST_PASSED : `TEST_FAILED 513 | ); 514 | 515 | this.passed += t.passed; 516 | this.failed += !t.passed; 517 | end 518 | 519 | $display(`TEST_REPORT_FMT, this.passed, this.failed); 520 | $fclose(fd); 521 | $system("rm tb_core.lst"); 522 | endtask 523 | endclass 524 | 525 | RiscvTestEnv env; 526 | initial begin 527 | env = new(`RISCV_TESTS); 528 | 529 | `ifndef ELF 530 | env.gen_file_list("rv64mi-p-*"); 531 | env.gen_file_list("rv64si-p-*"); 532 | env.gen_file_list("rv64ui-p-*"); 533 | env.gen_file_list("rv64uc-p-*"); 534 | `else 535 | env.gen_file_list(`ELF); 536 | `endif 537 | 538 | env.run(); 539 | $stop(); 540 | end 541 | 542 | endmodule 543 | -------------------------------------------------------------------------------- /rtl/core/core.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | module rv6_core #(parameter HART_ID = 0) ( 20 | // data bus signals 21 | output [63:0] c_addr, 22 | output c_ext, 23 | 24 | input [`CMEM_LINE-1:0] c_rdata, 25 | output c_rd, 26 | input c_dv, 27 | 28 | output [63:0] c_wdata, 29 | output [ 1:0] c_len, 30 | output c_wr, 31 | 32 | // interrupt signals 33 | input c_irq_me, 34 | input c_irq_mt, 35 | input c_irq_ms, 36 | 37 | input c_irq_se, 38 | input c_irq_st, 39 | input c_irq_ss, 40 | 41 | // cache invalidation signals 42 | input [63:0] c_inv_addr, 43 | input c_inv, 44 | 45 | // atomic operation signals 46 | output c_amo_req, 47 | input c_amo_ack, 48 | 49 | // control signals 50 | input c_stall, 51 | input c_rst_n, 52 | input c_clk 53 | ); 54 | 55 | // privilege level 56 | wire [1:0] priv; 57 | 58 | // pipeline flush signals 59 | wire flush_n; 60 | wire t_flush; 61 | 62 | // pipeline stall signals 63 | wire stall_if; 64 | wire stall_pd; 65 | wire stall_id; 66 | wire stall_ex; 67 | wire stall_mem; 68 | wire stall_wb; 69 | 70 | // exception signals 71 | wire exc_ii_if; 72 | wire exc_ii_csr; 73 | wire exc_pmp_iaf; 74 | wire exc_pmp_laf; 75 | wire exc_pmp_saf; 76 | wire exc_dmem_lma; 77 | wire exc_dmem_sma; 78 | 79 | /* IF */ 80 | 81 | wire [63:0] pc; 82 | wire [31:0] ir; 83 | 84 | wire [63:0] t_addr; 85 | wire [63:0] jalr_addr; 86 | wire [63:0] br_addr; 87 | wire [63:0] jal_addr; 88 | wire [12:0] pr_offs; 89 | 90 | wire t_taken; 91 | wire jalr_taken; 92 | wire jal_taken; 93 | wire pr_taken; 94 | wire pr_miss; 95 | 96 | wire c_ins = ir[1:0] != 2'b11; 97 | 98 | wire fence_i; 99 | 100 | // program counter 101 | pc u_pc ( 102 | .pc (pc ), 103 | .t_taken (t_taken ), 104 | .t_addr (t_addr ), 105 | .jalr_taken (jalr_taken ), 106 | .jalr_addr (jalr_addr ), 107 | .pr_miss (pr_miss ), 108 | .br_addr (br_addr ), 109 | .jal_taken (jal_taken ), 110 | .jal_addr (jal_addr ), 111 | .pr_taken (pr_taken ), 112 | .pr_offs (pr_offs ), 113 | .c_ins (c_ins ), 114 | .fence_i (fence_i ), 115 | .stall_if (stall_if ), 116 | .rst_n (c_rst_n ), 117 | .clk (c_clk ) 118 | ); 119 | 120 | // branch prediction unit 121 | bpu u_bpu ( 122 | .pc (pc ), 123 | .ir (ir ), 124 | .jal_taken (jal_taken ), 125 | .jal_addr (jal_addr ), 126 | .pr_taken (pr_taken ), 127 | .pr_offs (pr_offs ), 128 | .rst_n (c_rst_n ) 129 | ); 130 | 131 | // instruction memory / L1i cache 132 | 133 | wire [`IMEM_BLK_LEN-1:0] b_addr_i; 134 | wire [ `IMEM_LINE-1:0] b_data_i; 135 | wire b_rd_i; 136 | wire b_dv_i; 137 | wire stall_imem; 138 | 139 | imem u_imem ( 140 | .pc (pc ), 141 | .ir (ir ), 142 | .b_addr_i (b_addr_i ), 143 | .b_data_i (b_data_i ), 144 | .b_rd_i (b_rd_i ), 145 | .b_dv_i (b_dv_i ), 146 | .fence_i (fence_i ), 147 | .stall_imem (stall_imem ), 148 | .rst_n (c_rst_n ), 149 | .clk (c_clk ) 150 | ); 151 | 152 | reg [31:0] bfp_ir; 153 | reg [63:0] bfp_pc; 154 | reg bfp_pr_taken; 155 | reg bfp_c_ins; 156 | 157 | wire flush_ena = !stall_if || ((jalr_taken || pr_miss) && fence_i); 158 | wire flush_pd = !flush_n && flush_ena; 159 | 160 | always @(posedge c_clk, negedge c_rst_n) begin 161 | if(!c_rst_n) begin 162 | bfp_ir <= `NOP; 163 | bfp_pc <= 64'b0; 164 | bfp_pr_taken <= 1'b0; 165 | bfp_c_ins <= 1'b0; 166 | end 167 | else begin 168 | if(flush_pd || t_flush) begin 169 | bfp_ir <= `NOP; 170 | bfp_pc <= 64'b0; 171 | bfp_pr_taken <= 1'b0; 172 | bfp_c_ins <= 1'b0; 173 | end 174 | else if(!stall_if) begin 175 | bfp_pc <= pc; 176 | bfp_ir <= ir; 177 | bfp_pr_taken <= pr_taken; 178 | bfp_c_ins <= c_ins; 179 | end 180 | end 181 | end 182 | 183 | /* PD */ 184 | 185 | wire [31:0] pd_ir; 186 | 187 | // instruction predecoder 188 | pd u_pd ( 189 | .pc_in (bfp_pc ), 190 | .ir_in (bfp_ir ), 191 | .ir_out (pd_ir ), 192 | .amo_req (c_amo_req ), 193 | .amo_ack (c_amo_ack ), 194 | .stall (stall_pd ), 195 | .rst_n (c_rst_n ), 196 | .clk (c_clk ) 197 | ); 198 | 199 | reg [31:0] bpd_ir; 200 | reg [63:0] bpd_pc; 201 | reg bpd_pr_taken; 202 | reg bpd_c_ins; 203 | 204 | wire flush_id = !flush_n && flush_ena; 205 | 206 | always @(posedge c_clk, negedge c_rst_n) begin 207 | if(!c_rst_n) begin 208 | bpd_ir <= `NOP; 209 | bpd_pc <= 64'b0; 210 | bpd_pr_taken <= 1'b0; 211 | bpd_c_ins <= 1'b0; 212 | end 213 | else begin 214 | if(flush_id || t_flush) begin 215 | bpd_ir <= `NOP; 216 | bpd_pc <= 64'b0; 217 | bpd_pr_taken <= 1'b0; 218 | bpd_c_ins <= 1'b0; 219 | end 220 | else if(!stall_pd) begin 221 | bpd_ir <= pd_ir; 222 | bpd_pc <= bfp_pc; 223 | bpd_pr_taken <= bfp_pr_taken; 224 | bpd_c_ins <= bfp_c_ins; 225 | end 226 | end 227 | end 228 | 229 | /* ID */ 230 | 231 | wire [63:0] rs1_data; 232 | wire [ 4:0] rs1 = bpd_ir[19:15]; 233 | 234 | wire [63:0] rs2_data; 235 | wire [ 4:0] rs2 = bpd_ir[24:20]; 236 | 237 | wire [63:0] rd_data; 238 | wire [ 4:0] rd; 239 | wire we; 240 | 241 | // register file 242 | regfile u_regfile ( 243 | .rs1_data (rs1_data ), 244 | .rs1 (rs1 ), 245 | .rs2_data (rs2_data ), 246 | .rs2 (rs2 ), 247 | .rd_data (rd_data ), 248 | .rd (rd ), 249 | .we (we ), 250 | .rst_n (c_rst_n ), 251 | .clk (c_clk ) 252 | ); 253 | 254 | // branch alu 255 | br_alu u_br_alu ( 256 | .pc (bpd_pc ), 257 | .ir (bpd_ir ), 258 | .rs1_data (rs1_data ), 259 | .rs2_data (rs2_data ), 260 | .jalr_taken (jalr_taken ), 261 | .jalr_addr (jalr_addr ), 262 | .pr_miss (pr_miss ), 263 | .br_addr (br_addr ), 264 | .pr_taken (bpd_pr_taken ), 265 | .stall_id (stall_id ), 266 | .rst_n (c_rst_n ) 267 | ); 268 | 269 | assign flush_n = c_rst_n && (!pr_miss && !jalr_taken); 270 | 271 | // immediate format mux 272 | reg [63:0] mux_imm; 273 | always @(*) begin 274 | case(bpd_ir[6:0]) 275 | // I-type 276 | `OP_LOAD, `OP_ALRI, `OP_ALRIW: mux_imm = {{52{bpd_ir[31]}}, bpd_ir[31:20]}; 277 | // S-type 278 | `OP_STORE: mux_imm = {{52{bpd_ir[31]}}, bpd_ir[31:25], bpd_ir[11:7]}; 279 | // U-type 280 | `OP_LUI, `OP_AUIPC: mux_imm = {{32{bpd_ir[31]}}, bpd_ir[31:12], 12'b0}; 281 | // J-type 282 | `OP_JAL, `OP_JALR: mux_imm = bpd_c_ins ? 64'h2 : 64'h4; 283 | 284 | default: mux_imm = 64'b?; 285 | endcase 286 | end 287 | 288 | reg [31:0] bdx_ir; 289 | reg [63:0] bdx_pc; 290 | reg [63:0] bdx_rs1_data; 291 | reg [63:0] bdx_rs2_data; 292 | reg [63:0] bdx_imm; 293 | 294 | always @(posedge c_clk) begin 295 | if(!c_rst_n || t_flush) begin 296 | bdx_ir <= `NOP; 297 | bdx_pc <= 64'b0; 298 | bdx_rs1_data <= 64'b0; 299 | bdx_rs2_data <= 64'b0; 300 | bdx_imm <= 64'b0; 301 | end 302 | else if(!stall_id) begin 303 | bdx_ir <= bpd_ir; 304 | bdx_pc <= bpd_pc; 305 | bdx_rs1_data <= rs1_data; 306 | bdx_rs2_data <= rs2_data; 307 | bdx_imm <= mux_imm; 308 | end 309 | end 310 | 311 | /* EX */ 312 | 313 | wire [63:0] mx_a_fw [0:2]; 314 | wire [ 1:0] s_mx_a_fw; 315 | wire a_fw; 316 | 317 | wire [63:0] mx_b_fw [0:2]; 318 | wire [ 1:0] s_mx_b_fw; 319 | wire b_fw; 320 | 321 | wire [63:0] alu_mx_a [0:3]; 322 | assign alu_mx_a[0] = bdx_rs1_data; 323 | assign alu_mx_a[1] = bdx_pc; 324 | assign alu_mx_a[2] = mx_a_fw[s_mx_a_fw]; 325 | assign alu_mx_a[3] = bdx_pc; 326 | wire [1:0] s_alu_mx_a; 327 | 328 | assign s_alu_mx_a[1] = a_fw; 329 | assign s_alu_mx_a[0] = bdx_ir[6:0] == `OP_JAL || bdx_ir[6:0] == `OP_JALR || bdx_ir[6:0] == `OP_AUIPC; 330 | 331 | wire [63:0] alu_mx_b [0:3]; 332 | assign alu_mx_b[0] = bdx_imm; 333 | assign alu_mx_b[1] = bdx_rs2_data; 334 | assign alu_mx_b[2] = bdx_imm; 335 | assign alu_mx_b[3] = mx_b_fw[s_mx_b_fw]; 336 | wire [1:0] s_alu_mx_b; 337 | 338 | assign s_alu_mx_b[1] = b_fw; 339 | assign s_alu_mx_b[0] = bdx_ir[6:0] == `OP_ALRR || bdx_ir[6:0] == `OP_ALRRW; 340 | 341 | wire [63:0] alu_out; 342 | 343 | wire [63:0] alu_a = alu_mx_a[s_alu_mx_a]; 344 | wire [63:0] alu_b = alu_mx_b[s_alu_mx_b]; 345 | wire [ 6:0] opcode = bdx_ir[6:0]; 346 | wire [ 2:0] op = bdx_ir[14:12]; 347 | wire mod = bdx_ir[30]; 348 | 349 | alu u_alu ( 350 | .a (alu_a ), 351 | .b (alu_b ), 352 | .alu_out (alu_out ), 353 | .opcode (opcode ), 354 | .op (op ), 355 | .mod (mod ) 356 | ); 357 | 358 | reg [31:0] bxm_ir; 359 | reg [63:0] bxm_pc; 360 | reg [63:0] bxm_alu_out; 361 | reg [63:0] bxm_csr_in; 362 | reg [63:0] bxm_rs2_data; 363 | 364 | always @(posedge c_clk) begin 365 | if(!c_rst_n || t_flush) begin 366 | bxm_ir <= `NOP; 367 | bxm_pc <= 64'b0; 368 | bxm_alu_out <= 64'b0; 369 | bxm_csr_in <= 64'b0; 370 | bxm_rs2_data <= 64'b0; 371 | end 372 | else if(!stall_ex) begin 373 | bxm_ir <= bdx_ir; 374 | bxm_pc <= bdx_pc; 375 | bxm_alu_out <= alu_out; 376 | bxm_csr_in <= bdx_ir[14] ? alu_b : alu_a; 377 | bxm_rs2_data <= bdx_rs2_data; 378 | end 379 | end 380 | 381 | assign exc_ii_if = ir == 32'h0; 382 | 383 | /* MEM */ 384 | 385 | wire [63:0] dmem_out; 386 | 387 | // data memory / L1d cache 388 | wire op_load = bxm_ir[6:0] == `OP_LOAD; 389 | wire op_store = bxm_ir[6:0] == `OP_STORE; 390 | 391 | wire [`DMEM_BLK_LEN-1:0] b_addr_d; 392 | wire [ `DMEM_LINE-1:0] b_rdata_d; 393 | wire b_rd_d; 394 | wire b_dv_d; 395 | wire [`DMEM_BLK_LEN-1:0] b_inv_addr_d; 396 | wire stall_dmem; 397 | 398 | wire [2:0] len = bxm_ir[14:12]; 399 | 400 | dmem u_dmem ( 401 | .addr (bxm_alu_out ), 402 | .len (len ), 403 | .rdata (dmem_out ), 404 | .rd (op_load ), 405 | .wdata (bxm_rs2_data ), 406 | .wr (op_store ), 407 | .exc_lma (exc_dmem_lma ), 408 | .exc_sma (exc_dmem_sma ), 409 | .b_addr_d (b_addr_d ), 410 | .b_rdata_d (b_rdata_d ), 411 | .b_rd_d (b_rd_d ), 412 | .b_dv_d (b_dv_d ), 413 | .b_inv_addr_d (b_inv_addr_d ), 414 | .inv (c_inv ), 415 | .stall_dmem (stall_dmem ), 416 | .stall_mem (stall_mem ), 417 | .rst_n (c_rst_n ), 418 | .clk (c_clk ) 419 | ); 420 | 421 | wire [63:0] pmpcfg0; 422 | wire [63:0] pmpcfg2; 423 | 424 | wire [63:0] pmpaddr0; 425 | wire [63:0] pmpaddr1; 426 | wire [63:0] pmpaddr2; 427 | wire [63:0] pmpaddr3; 428 | wire [63:0] pmpaddr4; 429 | wire [63:0] pmpaddr5; 430 | wire [63:0] pmpaddr6; 431 | wire [63:0] pmpaddr7; 432 | wire [63:0] pmpaddr8; 433 | wire [63:0] pmpaddr9; 434 | wire [63:0] pmpaddr10; 435 | wire [63:0] pmpaddr11; 436 | wire [63:0] pmpaddr12; 437 | wire [63:0] pmpaddr13; 438 | wire [63:0] pmpaddr14; 439 | wire [63:0] pmpaddr15; 440 | 441 | wire [63:0] csr_out; 442 | wire csr_rd; 443 | 444 | wire instret; 445 | 446 | wire exc; 447 | wire [63:0] exc_cause; 448 | wire [63:0] exc_val; 449 | 450 | exc u_exc ( 451 | .priv (priv ), 452 | .ir (bxm_ir ), 453 | .exc (exc ), 454 | .exc_cause (exc_cause ), 455 | .exc_val (exc_val ), 456 | .exc_ii_if (exc_ii_if ), 457 | .exc_ii_csr (exc_ii_csr ), 458 | .exc_pmp_iaf (exc_pmp_iaf ), 459 | .exc_pmp_laf (exc_pmp_laf ), 460 | .exc_pmp_saf (exc_pmp_saf ), 461 | .exc_dmem_lma (exc_dmem_lma ), 462 | .exc_dmem_sma (exc_dmem_sma ), 463 | .dmem_addr (bxm_alu_out ), 464 | .flush_pd (flush_pd ), 465 | .flush_id (flush_id ), 466 | .t_flush (t_flush ), 467 | .stall_if (stall_if ), 468 | .stall_pd (stall_pd ), 469 | .stall_id (stall_id ), 470 | .stall_ex (stall_ex ), 471 | .stall_mem (stall_mem ), 472 | .stall_wb (stall_wb ), 473 | .rst_n (c_rst_n ), 474 | .clk (c_clk ) 475 | ); 476 | 477 | csr #(.HART_ID(HART_ID)) u_csr ( 478 | .priv (priv ), 479 | .ir (bxm_ir ), 480 | .pc (bxm_pc ), 481 | .csr_in (bxm_csr_in ), 482 | .csr_out (csr_out ), 483 | .csr_rd (csr_rd ), 484 | .csr_ii (csr_ii ), 485 | .t_taken (t_taken ), 486 | .t_addr (t_addr ), 487 | .t_flush (t_flush ), 488 | .irq_me (c_irq_me ), 489 | .irq_mt (c_irq_mt ), 490 | .irq_ms (c_irq_ms ), 491 | .irq_se (c_irq_se ), 492 | .irq_st (c_irq_st ), 493 | .irq_ss (c_irq_ss ), 494 | .exc (exc ), 495 | .exc_cause (exc_cause ), 496 | .exc_val (exc_val ), 497 | .pmpcfg0 (pmpcfg0 ), 498 | .pmpcfg2 (pmpcfg2 ), 499 | .pmpaddr0 (pmpaddr0 ), 500 | .pmpaddr1 (pmpaddr1 ), 501 | .pmpaddr2 (pmpaddr2 ), 502 | .pmpaddr3 (pmpaddr3 ), 503 | .pmpaddr4 (pmpaddr4 ), 504 | .pmpaddr5 (pmpaddr5 ), 505 | .pmpaddr6 (pmpaddr6 ), 506 | .pmpaddr7 (pmpaddr7 ), 507 | .pmpaddr8 (pmpaddr8 ), 508 | .pmpaddr9 (pmpaddr9 ), 509 | .pmpaddr10 (pmpaddr10 ), 510 | .pmpaddr11 (pmpaddr11 ), 511 | .pmpaddr12 (pmpaddr12 ), 512 | .pmpaddr13 (pmpaddr13 ), 513 | .pmpaddr14 (pmpaddr14 ), 514 | .pmpaddr15 (pmpaddr15 ), 515 | .instret (instret ), 516 | .stall (stall_mem ), 517 | .rst_n (c_rst_n ), 518 | .clk (c_clk ) 519 | ); 520 | 521 | assign exc_ii_csr = csr_ii; 522 | 523 | reg [31:0] bmw_ir; 524 | reg [63:0] bmw_pc; 525 | reg [63:0] bmw_alu_out; 526 | reg [63:0] bmw_dmem_out; 527 | reg [63:0] bmw_csr_out; 528 | reg bmw_csr_rd; 529 | 530 | always @(posedge c_clk, negedge c_rst_n) begin 531 | if(!c_rst_n) begin 532 | bmw_ir <= `NOP; 533 | bmw_pc <= 64'b0; 534 | bmw_alu_out <= 64'b0; 535 | bmw_dmem_out <= 64'b0; 536 | bmw_csr_out <= 64'b0; 537 | bmw_csr_rd <= 1'b0; 538 | end 539 | else if(!stall_mem && !t_flush) begin 540 | bmw_ir <= bxm_ir; 541 | bmw_pc <= bxm_pc; 542 | bmw_alu_out <= bxm_alu_out; 543 | bmw_dmem_out <= dmem_out; 544 | bmw_csr_out <= csr_out; 545 | bmw_csr_rd <= csr_rd; 546 | end 547 | end 548 | 549 | /* WB */ 550 | 551 | reg [63:0] wb_mux; 552 | 553 | always @(*) begin 554 | case(bmw_ir[6:0]) 555 | `OP_LOAD: wb_mux <= bmw_dmem_out; 556 | `OP_SYSTEM: wb_mux <= bmw_csr_out; 557 | default: wb_mux <= bmw_alu_out; 558 | endcase 559 | end 560 | 561 | assign rd = bmw_ir[11:7]; 562 | assign rd_data = wb_mux; 563 | 564 | assign we = (bmw_ir[6:0] != `OP_BRANCH) && (bmw_ir[6:0] != `OP_STORE) && !(bmw_ir[6:0] == `OP_SYSTEM && !bmw_csr_rd) && !stall_wb; 565 | 566 | assign instret = bmw_ir != `NOP && !stall_wb; 567 | 568 | /* WB FORWARD REGISTER */ 569 | 570 | reg [63:0] wb_fw; 571 | always @(posedge c_clk) if(!stall_wb) wb_fw <= wb_mux; 572 | 573 | /* FORWARDING MUX */ 574 | 575 | assign mx_a_fw[0] = bxm_alu_out; 576 | assign mx_a_fw[1] = bmw_alu_out; 577 | assign mx_a_fw[2] = wb_fw; 578 | 579 | assign mx_b_fw[0] = bxm_alu_out; 580 | assign mx_b_fw[1] = bmw_alu_out; 581 | assign mx_b_fw[2] = wb_fw; 582 | 583 | /* L2 CACHE */ 584 | 585 | wire [63:0] b_addr_w = bxm_alu_out; 586 | wire [63:0] b_wdata_w = bxm_rs2_data; 587 | wire [ 1:0] b_len_w = len; 588 | wire b_wr_w = op_store && !exc_dmem_sma; 589 | 590 | wire [`CMEM_BLK_LEN-1:0] b_addr_c; 591 | wire [ `CMEM_LINE-1:0] b_rdata_c; 592 | wire b_rd_c; 593 | wire b_dv_c; 594 | 595 | wire [`CMEM_BLK_LEN-1:0] b_inv_addr_c; 596 | 597 | cmem u_cmem ( 598 | .b_addr_w (b_addr_w ), 599 | .b_wdata_w (b_wdata_w ), 600 | .b_len_w (b_len_w ), 601 | .b_wr_w (b_wr_w ), 602 | .b_addr_i (b_addr_i ), 603 | .b_data_i (b_data_i ), 604 | .b_rd_i (b_rd_i ), 605 | .b_dv_i (b_dv_i ), 606 | .b_addr_d (b_addr_d ), 607 | .b_rdata_d (b_rdata_d ), 608 | .b_rd_d (b_rd_d ), 609 | .b_dv_d (b_dv_d ), 610 | .b_addr_c (b_addr_c ), 611 | .b_rdata_c (b_rdata_c ), 612 | .b_rd_c (b_rd_c ), 613 | .b_dv_c (b_dv_c ), 614 | .b_inv_addr_c (b_inv_addr_c ), 615 | .inv (c_inv ), 616 | .rst_n (c_rst_n ), 617 | .clk (c_clk ) 618 | ); 619 | 620 | /* CACHE INVALIDATION */ 621 | 622 | assign b_inv_addr_d = c_inv_addr[63:`DMEM_OFFS_LEN]; 623 | assign b_inv_addr_c = c_inv_addr[63:`CMEM_OFFS_LEN]; 624 | 625 | /* DATA BUS ARBITER */ 626 | 627 | dba u_dba ( 628 | .b_addr_w (b_addr_w ), 629 | .b_wdata_w (b_wdata_w ), 630 | .b_len_w (b_len_w ), 631 | .b_wr_w (b_wr_w ), 632 | .b_addr_c (b_addr_c ), 633 | .b_rdata_c (b_rdata_c ), 634 | .b_rd_c (b_rd_c ), 635 | .b_dv_c (b_dv_c ), 636 | .c_addr (c_addr ), 637 | .c_ext (c_ext ), 638 | .c_rdata (c_rdata ), 639 | .c_rd (c_rd ), 640 | .c_dv (c_dv ), 641 | .c_wdata (c_wdata ), 642 | .c_len (c_len ), 643 | .c_wr (c_wr ) 644 | ); 645 | 646 | /* MEMORY MANAGEMENT UNIT */ 647 | 648 | // TODO: 649 | // mmu u_mmu (); 650 | 651 | /* PHYSICAL MEMORY PROTECTION */ 652 | 653 | pmp u_pmp ( 654 | .b_addr_w_p (b_addr_w ), 655 | .b_wr_w_p (b_wr_w ), 656 | .b_addr_i_p (b_addr_i ), 657 | .b_rd_i_p (b_rd_i ), 658 | .b_addr_d_p (b_addr_d ), 659 | .b_rd_d_p (b_rd_d ), 660 | .priv (priv ), 661 | .pmpcfg0 (pmpcfg0 ), 662 | .pmpcfg2 (pmpcfg2 ), 663 | .pmpaddr0 (pmpaddr0 ), 664 | .pmpaddr1 (pmpaddr1 ), 665 | .pmpaddr2 (pmpaddr2 ), 666 | .pmpaddr3 (pmpaddr3 ), 667 | .pmpaddr4 (pmpaddr4 ), 668 | .pmpaddr5 (pmpaddr5 ), 669 | .pmpaddr6 (pmpaddr6 ), 670 | .pmpaddr7 (pmpaddr7 ), 671 | .pmpaddr8 (pmpaddr8 ), 672 | .pmpaddr9 (pmpaddr9 ), 673 | .pmpaddr10 (pmpaddr10 ), 674 | .pmpaddr11 (pmpaddr11 ), 675 | .pmpaddr12 (pmpaddr12 ), 676 | .pmpaddr13 (pmpaddr13 ), 677 | .pmpaddr14 (pmpaddr14 ), 678 | .pmpaddr15 (pmpaddr15 ), 679 | .exc_pmp_iaf (exc_pmp_iaf ), 680 | .exc_pmp_laf (exc_pmp_laf ), 681 | .exc_pmp_saf (exc_pmp_saf ), 682 | .rst_n (c_rst_n ), 683 | .clk (c_clk ) 684 | ); 685 | 686 | /* CONTROL UNIT */ 687 | 688 | cu u_cu ( 689 | .ir_if (ir ), 690 | .ir_id (bpd_ir ), 691 | .ir_ex (bdx_ir ), 692 | .ir_mem (bxm_ir ), 693 | .ir_wb (bmw_ir ), 694 | .stall_if (stall_if ), 695 | .stall_pd (stall_pd ), 696 | .stall_id (stall_id ), 697 | .stall_ex (stall_ex ), 698 | .stall_mem (stall_mem ), 699 | .stall_wb (stall_wb ), 700 | .c_stall (c_stall ), 701 | .stall_imem (stall_imem ), 702 | .stall_dmem (stall_dmem ), 703 | .fence_i (fence_i ), 704 | .amo_req (c_amo_req ), 705 | .amo_ack (c_amo_ack ), 706 | .s_mx_a_fw (s_mx_a_fw ), 707 | .a_fw (a_fw ), 708 | .s_mx_b_fw (s_mx_b_fw ), 709 | .b_fw (b_fw ), 710 | .rst_n (c_rst_n ), 711 | .clk (c_clk ) 712 | ); 713 | 714 | endmodule 715 | -------------------------------------------------------------------------------- /rtl/core/csr.v: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Nikola Lukić 2 | * This source describes Open Hardware and is licensed under the CERN-OHL-S v2 3 | * 4 | * You may redistribute and modify this documentation and make products 5 | * using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). 6 | * This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED 7 | * WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY 8 | * AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 9 | * for applicable conditions. 10 | * 11 | * Source location: https://www.github.com/kiclu/rv6 12 | * 13 | * As per CERN-OHL-S v2 section 4.1, should You produce hardware based on 14 | * these sources, You must maintain the Source Location visible on the 15 | * external case of any product you make using this documentation. */ 16 | 17 | `include "../config.vh" 18 | 19 | // Machine Information Registers 20 | `define MVENDORID 12'hf11 21 | `define MARCHID 12'hf12 22 | `define MIMPID 12'hf13 23 | `define MHARTID 12'hf14 24 | `define MCONFIGPTR 12'hf15 25 | 26 | // Machine Trap Setup 27 | `define MSTATUS 12'h300 28 | `define MISA 12'h301 29 | `define MEDELEG 12'h302 30 | `define MIDELEG 12'h303 31 | `define MIE 12'h304 32 | `define MTVEC 12'h305 33 | `define MCOUNTEREN 12'h306 34 | 35 | // Machine Trap Handling 36 | `define MSCRATCH 12'h340 37 | `define MEPC 12'h341 38 | `define MCAUSE 12'h342 39 | `define MTVAL 12'h343 40 | `define MIP 12'h344 41 | 42 | // Machine Configuration 43 | `define MENVCFG 12'h30a 44 | `define MSECCFG 12'h747 45 | 46 | // Machine Memory Protection 47 | `define PMPCFG0 12'h3a0 48 | `define PMPCFG2 12'h3a2 49 | 50 | `define PMPADDR0 12'h3b0 51 | `define PMPADDR1 12'h3b1 52 | `define PMPADDR2 12'h3b2 53 | `define PMPADDR3 12'h3b3 54 | `define PMPADDR4 12'h3b4 55 | `define PMPADDR5 12'h3b5 56 | `define PMPADDR6 12'h3b6 57 | `define PMPADDR7 12'h3b7 58 | `define PMPADDR8 12'h3b8 59 | `define PMPADDR9 12'h3b9 60 | `define PMPADDR10 12'h3ba 61 | `define PMPADDR11 12'h3bb 62 | `define PMPADDR12 12'h3bc 63 | `define PMPADDR13 12'h3bd 64 | `define PMPADDR14 12'h3be 65 | `define PMPADDR15 12'h3bf 66 | 67 | // Machine Counter/Timers 68 | `define MCYCLE 12'hb00 69 | `define MINSTRET 12'hb02 70 | `define MHPMCOUNTER3 12'hb03 71 | 72 | // Machine Counter Setup 73 | `define MCOUNTINHIBIT 12'h320 74 | `define MHPMEVENT3 12'h323 75 | 76 | // Debug/Trace Registers 77 | `define TSELECT 12'h7a0 78 | `define TDATA1 12'h7a1 79 | `define TDATA2 12'h7a2 80 | `define TDATA3 12'h7a3 81 | `define MCONTEXT 12'h7a8 82 | 83 | // Supervisor Trap Setup 84 | `define SSTATUS 12'h100 85 | `define SIE 12'h104 86 | `define STVEC 12'h105 87 | `define SCOUNTEREN 12'h106 88 | 89 | // Supervisor Trap Handling 90 | `define SSCRATCH 12'h140 91 | `define SEPC 12'h141 92 | `define SCAUSE 12'h142 93 | `define STVAL 12'h143 94 | `define SIP 12'h144 95 | 96 | // Supervisor Protection and Translation 97 | `define SATP 12'h180 98 | 99 | // Unprivileged Floating-Point CSRs 100 | `define FFLAGS 12'h001 101 | `define FRM 12'h002 102 | `define FCSR 12'h003 103 | 104 | // Unprivileged Counter/Timers 105 | `define CYCLE 12'hc00 106 | `define TIME 12'hc01 107 | `define INSTRET 12'hc02 108 | 109 | // MSTATUS Register Fields 110 | `define MSTATUS_SD csr_mstatus[63] 111 | `define MSTATUS_MBE csr_mstatus[37] 112 | `define MSTATUS_SBE csr_mstatus[36] 113 | `define MSTATUS_SXL csr_mstatus[35:34] 114 | `define MSTATUS_UXL csr_mstatus[33:32] 115 | `define MSTATUS_TSR csr_mstatus[22] 116 | `define MSTATUS_TW csr_mstatus[21] 117 | `define MSTATUS_TVM csr_mstatus[20] 118 | `define MSTATUS_MXR csr_mstatus[19] 119 | `define MSTATUS_SUM csr_mstatus[18] 120 | `define MSTATUS_MPRV csr_mstatus[17] 121 | `define MSTATUS_XS csr_mstatus[16:15] 122 | `define MSTATUS_FS csr_mstatus[14:13] 123 | `define MSTATUS_MPP csr_mstatus[12:11] 124 | `define MSTATUS_VS csr_mstatus[10:9] 125 | `define MSTATUS_SPP csr_mstatus[8] 126 | `define MSTATUS_MPIE csr_mstatus[7] 127 | `define MSTATUS_UBE csr_mstatus[6] 128 | `define MSTATUS_SPIE csr_mstatus[5] 129 | `define MSTATUS_MIE csr_mstatus[3] 130 | `define MSTATUS_SIE csr_mstatus[1] 131 | 132 | // MIDELEG Register Fields 133 | `define MIDELEG_MEID csr_mideleg[11] 134 | `define MIDELEG_SEID csr_mideleg[9] 135 | `define MIDELEG_MTID csr_mideleg[7] 136 | `define MIDELEG_STID csr_mideleg[5] 137 | `define MIDELEG_MSID csr_mideleg[3] 138 | `define MIDELEG_SSID csr_mideleg[1] 139 | 140 | // MIE Register Fields 141 | `define MIE_MEIE csr_mie[11] 142 | `define MIE_SEIE csr_mie[9] 143 | `define MIE_MTIE csr_mie[7] 144 | `define MIE_STIE csr_mie[5] 145 | `define MIE_MSIE csr_mie[3] 146 | `define MIE_SSIE csr_mie[1] 147 | 148 | // MIP Register Fields 149 | `define MIP_MEIP csr_mip[11] 150 | `define MIP_SEIP csr_mip[9] 151 | `define MIP_MTIP csr_mip[7] 152 | `define MIP_STIP csr_mip[5] 153 | `define MIP_MSIP csr_mip[3] 154 | `define MIP_SSIP csr_mip[1] 155 | 156 | // MCOUNTEREN Register Fields 157 | `define MCOUNTEREN_HPM3 csr_mcounteren[3] 158 | `define MCOUNTEREN_IR csr_mcounteren[2] 159 | `define MCOUNTEREN_TM csr_mcounteren[1] 160 | `define MCOUNTEREN_CY csr_mcounteren[0] 161 | 162 | // MCOUNTINHIBIT Register Fields 163 | `define MCOUNTINHIBIT_HPM3 csr_mcountinhibit[3] 164 | `define MCOUNTINHIBIT_IR csr_mcountinhibit[2] 165 | `define MCOUNTINHIBIT_CY csr_mcountinhibit[0] 166 | 167 | // SSTATUS Register Fields 168 | `define SSTATUS_SD csr_sstatus[63] 169 | `define SSTATUS_UXL csr_sstatus[33:32] 170 | `define SSTATUS_MXR csr_sstatus[19] 171 | `define SSTATUS_SUM csr_sstatus[18] 172 | `define SSTATUS_XS csr_sstatus[16:15] 173 | `define SSTATUS_FS csr_sstatus[14:13] 174 | `define SSTATUS_VS csr_sstatus[10:9] 175 | `define SSTATUS_SPP csr_sstatus[8] 176 | `define SSTATUS_UBE csr_sstatus[6] 177 | `define SSTATUS_SPIE csr_sstatus[5] 178 | `define SSTATUS_SIE csr_sstatus[1] 179 | 180 | // SIE Register Fields 181 | `define SIE_SEIE csr_sie[9] 182 | `define SIE_STIE csr_sie[5] 183 | `define SIE_SSIE csr_sie[1] 184 | 185 | // SIP Register Fields 186 | `define SIP_SEIP csr_sip[9] 187 | `define SIP_STIP csr_sip[5] 188 | `define SIP_SSIP csr_sip[1] 189 | 190 | // SCOUNTEREN Register Fields 191 | `define SCOUNTEREN_HPM3 csr_scounteren[3] 192 | `define SCOUNTEREN_IR csr_scounteren[2] 193 | `define SCOUNTEREN_TM csr_scounteren[1] 194 | `define SCOUNTEREN_CY csr_scounteren[0] 195 | 196 | // Zicsr instructions 197 | `define CSR_RW 3'b001 198 | `define CSR_RS 3'b010 199 | `define CSR_RC 3'b011 200 | `define CSR_RWI 3'b101 201 | `define CSR_RSI 3'b110 202 | `define CSR_RCI 3'b111 203 | 204 | `define OP_SRET 32'h10200073 205 | `define OP_MRET 32'h30200073 206 | 207 | `define FLUSH_PD 4'b1000 208 | `define FLUSH_ID 4'b1100 209 | `define FLUSH_EX 4'b1110 210 | `define FLUSH_MEM 4'b1111 211 | 212 | module csr #(parameter HART_ID = 0) ( 213 | output reg [ 1:0] priv, 214 | input [31:0] ir, 215 | input [63:0] pc, 216 | 217 | // CSR signals 218 | input [63:0] csr_in, 219 | output reg [63:0] csr_out, 220 | output csr_rd, 221 | output csr_ii, 222 | 223 | // trap signals 224 | output reg [63:0] t_addr, 225 | output t_taken, 226 | output t_flush, 227 | 228 | // interrupt signals 229 | input irq_me, 230 | input irq_mt, 231 | input irq_ms, 232 | input irq_se, 233 | input irq_st, 234 | input irq_ss, 235 | 236 | // exception signals 237 | input exc, 238 | input [63:0] exc_cause, 239 | input [63:0] exc_val, 240 | 241 | // PMP signals 242 | output [63:0] pmpcfg0, 243 | output [63:0] pmpcfg2, 244 | 245 | output [63:0] pmpaddr0, 246 | output [63:0] pmpaddr1, 247 | output [63:0] pmpaddr2, 248 | output [63:0] pmpaddr3, 249 | output [63:0] pmpaddr4, 250 | output [63:0] pmpaddr5, 251 | output [63:0] pmpaddr6, 252 | output [63:0] pmpaddr7, 253 | output [63:0] pmpaddr8, 254 | output [63:0] pmpaddr9, 255 | output [63:0] pmpaddr10, 256 | output [63:0] pmpaddr11, 257 | output [63:0] pmpaddr12, 258 | output [63:0] pmpaddr13, 259 | output [63:0] pmpaddr14, 260 | output [63:0] pmpaddr15, 261 | 262 | input instret, 263 | input stall, 264 | input rst_n, 265 | input clk 266 | ); 267 | 268 | wire [11:0] csr_addr = ir[31:20]; 269 | 270 | /* ZICSR */ 271 | 272 | wire csr_wr; 273 | 274 | wire csr_rw = ir[14:12] == `CSR_RW && ir[6:0] == `OP_SYSTEM; 275 | wire csr_rwi = ir[14:12] == `CSR_RWI && ir[6:0] == `OP_SYSTEM; 276 | wire csr_rs = ir[14:12] == `CSR_RS && ir[6:0] == `OP_SYSTEM; 277 | wire csr_rsi = ir[14:12] == `CSR_RSI && ir[6:0] == `OP_SYSTEM; 278 | wire csr_rc = ir[14:12] == `CSR_RC && ir[6:0] == `OP_SYSTEM; 279 | wire csr_rci = ir[14:12] == `CSR_RCI && ir[6:0] == `OP_SYSTEM; 280 | wire csr_op = csr_rw || csr_rwi || csr_rs || csr_rsi || csr_rc || csr_rci; 281 | 282 | wire rd = csr_op && !((csr_rw || csr_rwi ) && ir[11:7] == 5'b0); 283 | wire wr = csr_op && !((csr_rs || csr_rsi || csr_rc || csr_rci) && ir[19:15] == 5'b0); 284 | 285 | reg csr_addr_invalid; 286 | wire csr_wr_invalid = &csr_addr[11:10] && wr; 287 | wire csr_pr_invalid = priv < csr_addr[9:8] && (rd || wr); 288 | 289 | wire rd_valid = rd && !csr_addr_invalid && !csr_pr_invalid; 290 | wire wr_valid = wr && !csr_addr_invalid && !csr_pr_invalid && !csr_wr_invalid; 291 | 292 | assign csr_rd = rd_valid; 293 | assign csr_wr = wr_valid && !stall; 294 | 295 | /* TRAP CONTROL SIGNALS */ 296 | 297 | reg [63:0] tcause; 298 | reg [63:0] tcause_pc; 299 | reg [63:0] tval; 300 | 301 | reg m_trap; 302 | reg s_trap; 303 | wire trap = m_trap || s_trap; 304 | 305 | wire mret = ir == `OP_MRET; 306 | wire sret = ir == `OP_SRET; 307 | wire tret = mret || sret; 308 | 309 | /* CSR REGISTERS */ 310 | 311 | reg [63:0] ncsr; 312 | 313 | /* MISA */ 314 | 315 | reg [63:0] csr_misa; 316 | always @(posedge clk, negedge rst_n) begin 317 | if(!rst_n) csr_misa <= 64'h8000000000141105; 318 | else if(csr_wr && csr_addr == `MISA) csr_misa <= ncsr; 319 | end 320 | 321 | /* MVENDORID */ 322 | 323 | wire [63:0] csr_mvendorid = 64'h0; 324 | 325 | /* MARCHID */ 326 | 327 | wire [63:0] csr_marchid = 64'h27; 328 | 329 | /* MIMPID */ 330 | 331 | wire [63:0] csr_mimpid = 64'h0; 332 | 333 | /* MHARTID */ 334 | 335 | wire [63:0] csr_mhartid = HART_ID; 336 | 337 | /* MSTATUS */ 338 | 339 | `define SSTATUS_MASK 64'h7ffffffcfff2189d 340 | 341 | reg [63:0] csr_mstatus; 342 | always @(posedge clk, negedge rst_n) begin 343 | if(!rst_n) csr_mstatus <= 64'h0000000a00001800; 344 | else if(trap) begin 345 | if(m_trap) begin 346 | `MSTATUS_MIE <= 1'b0; 347 | `MSTATUS_MPIE <= `MSTATUS_MIE; 348 | `MSTATUS_MPP <= priv; 349 | end 350 | else begin 351 | `MSTATUS_SIE <= 1'b0; 352 | `MSTATUS_SPIE <= `MSTATUS_SIE; 353 | `MSTATUS_SPP <= priv[0]; 354 | end 355 | end 356 | else if(tret) begin 357 | if(mret) begin 358 | `MSTATUS_MIE <= `MSTATUS_MPIE; 359 | `MSTATUS_MPIE <= 1'b1; 360 | //`MSTATUS_MPP <= 2'b00; ??? 361 | end 362 | else begin 363 | `MSTATUS_SIE <= `MSTATUS_SPIE; 364 | `MSTATUS_SPIE <= 1'b1; 365 | `MSTATUS_SPP <= 1'b0; 366 | end 367 | end 368 | else if(csr_wr && csr_addr == `MSTATUS) csr_mstatus <= ncsr; 369 | else if(csr_wr && csr_addr == `SSTATUS) csr_mstatus <= (ncsr & `SSTATUS_MASK) | (csr_mstatus & ~`SSTATUS_MASK); 370 | end 371 | 372 | /* MTVEC */ 373 | 374 | reg [63:0] csr_mtvec; 375 | always @(posedge clk, negedge rst_n) begin 376 | if(!rst_n) csr_mtvec <= 64'h0; 377 | else if(csr_wr && csr_addr == `MTVEC) csr_mtvec <= ncsr; 378 | end 379 | 380 | /* MEDELEG */ 381 | 382 | reg [63:0] csr_medeleg; 383 | always @(posedge clk, negedge rst_n) begin 384 | if(!rst_n) csr_medeleg <= 64'h0; 385 | else if(csr_wr && csr_addr == `MEDELEG) csr_medeleg <= ncsr; 386 | end 387 | 388 | /* MIDELEG */ 389 | 390 | reg [63:0] csr_mideleg; 391 | always @(posedge clk, negedge rst_n) begin 392 | if(!rst_n) csr_mideleg <= 64'h0; 393 | else if(csr_wr && csr_addr == `MIDELEG) csr_mideleg <= ncsr; 394 | end 395 | 396 | /* MIE */ 397 | 398 | reg [63:0] csr_mie; 399 | always @(posedge clk, negedge rst_n) begin 400 | if(!rst_n) csr_mie <= 64'h0; 401 | else if(csr_wr && csr_addr == `MIE) csr_mie <= ncsr; 402 | end 403 | 404 | /* MIP */ 405 | 406 | reg [63:0] csr_mip; 407 | always @(posedge clk, negedge rst_n) begin 408 | if(!rst_n) csr_mip <= 64'h0; 409 | else if(csr_wr && csr_addr == `MIP) csr_mip <= ncsr; 410 | end 411 | 412 | /* MCYCLE */ 413 | 414 | reg [63:0] csr_mcycle; 415 | wire mcountinhibit_cy; 416 | always @(posedge clk, negedge rst_n) begin 417 | if(!rst_n) csr_mcycle <= 64'h0; 418 | else if(csr_wr && csr_addr == `MCYCLE) csr_mcycle <= ncsr; 419 | else if(!mcountinhibit_cy) csr_mcycle <= csr_mcycle + 1; 420 | end 421 | 422 | /* MINSTRET */ 423 | 424 | reg [63:0] csr_minstret; 425 | wire mcountinhibit_ir; 426 | always @(posedge clk, negedge rst_n) begin 427 | if(!rst_n) csr_minstret <= 64'h0; 428 | else if(csr_wr && csr_addr == `MINSTRET) csr_minstret <= ncsr; 429 | else if(!mcountinhibit_ir && instret) csr_minstret <= csr_minstret + 1; 430 | end 431 | 432 | /* MHPMCOUNTER */ 433 | 434 | // TODO 435 | 436 | /* MCOUNTEREN */ 437 | 438 | reg [63:0] csr_mcounteren; 439 | always @(posedge clk, negedge rst_n) begin 440 | if(!rst_n) csr_mcounteren <= 64'h0; 441 | else if(csr_wr && csr_addr == `MCOUNTEREN) csr_mcounteren <= ncsr; 442 | end 443 | 444 | /* MCOUNTINHIBIT */ 445 | 446 | reg [63:0] csr_mcountinhibit; 447 | always @(posedge clk, negedge rst_n) begin 448 | if(!rst_n) csr_mcountinhibit <= 64'h0; 449 | else if(csr_wr && csr_addr == `MCOUNTINHIBIT) csr_mcountinhibit <= ncsr; 450 | end 451 | 452 | assign mcountinhibit_cy = `MCOUNTINHIBIT_CY; 453 | assign mcountinhibit_ir = `MCOUNTINHIBIT_IR; 454 | 455 | /* MSCRATCH */ 456 | 457 | reg [63:0] csr_mscratch; 458 | always @(posedge clk, negedge rst_n) begin 459 | if(!rst_n) csr_mscratch <= 64'h0; 460 | else if(csr_wr && csr_addr == `MSCRATCH) csr_mscratch <= ncsr; 461 | end 462 | 463 | /* MEPC */ 464 | 465 | reg [63:0] csr_mepc; 466 | always @(posedge clk, negedge rst_n) begin 467 | if(!rst_n) csr_mepc <= 64'h0; 468 | else if(m_trap) csr_mepc <= pc; 469 | else if(csr_wr && csr_addr == `MEPC) csr_mepc <= ncsr; 470 | end 471 | 472 | /* MCAUSE */ 473 | 474 | reg [63:0] csr_mcause; 475 | always @(posedge clk, negedge rst_n) begin 476 | if(!rst_n) csr_mcause <= 64'h0; 477 | else if(m_trap) csr_mcause <= tcause; 478 | else if(csr_wr && csr_addr == `MCAUSE) csr_mcause <= ncsr; 479 | end 480 | 481 | /* MTVAL */ 482 | 483 | reg [63:0] csr_mtval; 484 | always @(posedge clk, negedge rst_n) begin 485 | if(!rst_n) csr_mtval <= 64'h0; 486 | else if(m_trap) csr_mtval <= tval; 487 | else if(csr_wr && csr_addr == `MTVAL) csr_mtval <= ncsr; 488 | end 489 | 490 | /* MCONFIGPTR */ 491 | 492 | reg [63:0] csr_mconfigptr; 493 | always @(posedge clk, negedge rst_n) begin 494 | if(!rst_n) csr_mconfigptr <= 64'h0; 495 | else if(csr_wr && csr_addr == `MCONFIGPTR) csr_mconfigptr <= ncsr; 496 | end 497 | 498 | /* MENVCFG */ 499 | 500 | reg [63:0] csr_menvcfg; 501 | always @(posedge clk, negedge rst_n) begin 502 | if(!rst_n) csr_menvcfg <= 64'h0; 503 | else if(csr_wr && csr_addr == `MENVCFG) csr_menvcfg <= ncsr; 504 | end 505 | 506 | /* MSECCFG */ 507 | 508 | reg [63:0] csr_mseccfg; 509 | always @(posedge clk, negedge rst_n) begin 510 | if(!rst_n) csr_mseccfg <= 64'h0; 511 | else if(csr_wr && csr_addr == `MSECCFG) csr_mseccfg <= ncsr; 512 | end 513 | 514 | /* PMPCFG */ 515 | 516 | reg [63:0] csr_pmpcfg0; 517 | always @(posedge clk, negedge rst_n) begin 518 | if(!rst_n) csr_pmpcfg0 <= 64'h0; 519 | else if(csr_wr && csr_addr == `PMPCFG0) csr_pmpcfg0 <= ncsr; 520 | end 521 | 522 | reg [63:0] csr_pmpcfg2; 523 | always @(posedge clk, negedge rst_n) begin 524 | if(!rst_n) csr_pmpcfg2 <= 64'h0; 525 | else if(csr_wr && csr_addr == `PMPCFG2) csr_pmpcfg2 <= ncsr; 526 | end 527 | 528 | /* PMPADDR */ 529 | 530 | reg [63:0] csr_pmpaddr0; 531 | always @(posedge clk, negedge rst_n) begin 532 | if(!rst_n) csr_pmpaddr0 <= 64'h0; 533 | else if(csr_wr && csr_addr == `PMPADDR0) csr_pmpaddr0 <= ncsr; 534 | end 535 | 536 | reg [63:0] csr_pmpaddr1; 537 | always @(posedge clk, negedge rst_n) begin 538 | if(!rst_n) csr_pmpaddr1 <= 64'h0; 539 | else if(csr_wr && csr_addr == `PMPADDR1) csr_pmpaddr1 <= ncsr; 540 | end 541 | 542 | reg [63:0] csr_pmpaddr2; 543 | always @(posedge clk, negedge rst_n) begin 544 | if(!rst_n) csr_pmpaddr2 <= 64'h0; 545 | else if(csr_wr && csr_addr == `PMPADDR2) csr_pmpaddr2 <= ncsr; 546 | end 547 | 548 | reg [63:0] csr_pmpaddr3; 549 | always @(posedge clk, negedge rst_n) begin 550 | if(!rst_n) csr_pmpaddr3 <= 64'h0; 551 | else if(csr_wr && csr_addr == `PMPADDR3) csr_pmpaddr3 <= ncsr; 552 | end 553 | 554 | reg [63:0] csr_pmpaddr4; 555 | always @(posedge clk, negedge rst_n) begin 556 | if(!rst_n) csr_pmpaddr4 <= 64'h0; 557 | else if(csr_wr && csr_addr == `PMPADDR4) csr_pmpaddr4 <= ncsr; 558 | end 559 | 560 | reg [63:0] csr_pmpaddr5; 561 | always @(posedge clk, negedge rst_n) begin 562 | if(!rst_n) csr_pmpaddr5 <= 64'h0; 563 | else if(csr_wr && csr_addr == `PMPADDR5) csr_pmpaddr5 <= ncsr; 564 | end 565 | 566 | reg [63:0] csr_pmpaddr6; 567 | always @(posedge clk, negedge rst_n) begin 568 | if(!rst_n) csr_pmpaddr6 <= 64'h0; 569 | else if(csr_wr && csr_addr == `PMPADDR6) csr_pmpaddr6 <= ncsr; 570 | end 571 | 572 | reg [63:0] csr_pmpaddr7; 573 | always @(posedge clk, negedge rst_n) begin 574 | if(!rst_n) csr_pmpaddr7 <= 64'h0; 575 | else if(csr_wr && csr_addr == `PMPADDR7) csr_pmpaddr7 <= ncsr; 576 | end 577 | 578 | reg [63:0] csr_pmpaddr8; 579 | always @(posedge clk, negedge rst_n) begin 580 | if(!rst_n) csr_pmpaddr8 <= 64'h0; 581 | else if(csr_wr && csr_addr == `PMPADDR8) csr_pmpaddr8 <= ncsr; 582 | end 583 | 584 | reg [63:0] csr_pmpaddr9; 585 | always @(posedge clk, negedge rst_n) begin 586 | if(!rst_n) csr_pmpaddr9 <= 64'h0; 587 | else if(csr_wr && csr_addr == `PMPADDR9) csr_pmpaddr9 <= ncsr; 588 | end 589 | 590 | reg [63:0] csr_pmpaddr10; 591 | always @(posedge clk, negedge rst_n) begin 592 | if(!rst_n) csr_pmpaddr10 <= 64'h0; 593 | else if(csr_wr && csr_addr == `PMPADDR10) csr_pmpaddr10 <= ncsr; 594 | end 595 | 596 | reg [63:0] csr_pmpaddr11; 597 | always @(posedge clk, negedge rst_n) begin 598 | if(!rst_n) csr_pmpaddr11 <= 64'h0; 599 | else if(csr_wr && csr_addr == `PMPADDR11) csr_pmpaddr11 <= ncsr; 600 | end 601 | 602 | reg [63:0] csr_pmpaddr12; 603 | always @(posedge clk, negedge rst_n) begin 604 | if(!rst_n) csr_pmpaddr12 <= 64'h0; 605 | else if(csr_wr && csr_addr == `PMPADDR12) csr_pmpaddr12 <= ncsr; 606 | end 607 | 608 | reg [63:0] csr_pmpaddr13; 609 | always @(posedge clk, negedge rst_n) begin 610 | if(!rst_n) csr_pmpaddr13 <= 64'h0; 611 | else if(csr_wr && csr_addr == `PMPADDR13) csr_pmpaddr13 <= ncsr; 612 | end 613 | 614 | reg [63:0] csr_pmpaddr14; 615 | always @(posedge clk, negedge rst_n) begin 616 | if(!rst_n) csr_pmpaddr14 <= 64'h0; 617 | else if(csr_wr && csr_addr == `PMPADDR14) csr_pmpaddr14 <= ncsr; 618 | end 619 | 620 | reg [63:0] csr_pmpaddr15; 621 | always @(posedge clk, negedge rst_n) begin 622 | if(!rst_n) csr_pmpaddr15 <= 64'h0; 623 | else if(csr_wr && csr_addr == `PMPADDR15) csr_pmpaddr15 <= ncsr; 624 | end 625 | 626 | /* TSELECT */ 627 | 628 | reg [63:0] csr_tselect; 629 | always @(posedge clk, negedge rst_n) begin 630 | if(!rst_n) csr_tselect <= 64'h0; 631 | else if(csr_wr && csr_addr == `TSELECT) csr_tselect <= ncsr; 632 | end 633 | 634 | /* TDATA1 */ 635 | 636 | reg [63:0] csr_tdata1; 637 | always @(posedge clk, negedge rst_n) begin 638 | if(!rst_n) csr_tdata1 <= 64'h0; 639 | else if(csr_wr && csr_addr == `TDATA1) csr_tdata1 <= ncsr; 640 | end 641 | 642 | /* TDATA2 */ 643 | 644 | reg [63:0] csr_tdata2; 645 | always @(posedge clk, negedge rst_n) begin 646 | if(!rst_n) csr_tdata2 <= 64'h0; 647 | else if(csr_wr && csr_addr == `TDATA2) csr_tdata2 <= ncsr; 648 | end 649 | 650 | /* TDATA3 */ 651 | 652 | reg [63:0] csr_tdata3; 653 | always @(posedge clk, negedge rst_n) begin 654 | if(!rst_n) csr_tdata3 <= 64'h0; 655 | else if(csr_wr && csr_addr == `TDATA3) csr_tdata3 <= ncsr; 656 | end 657 | 658 | /* MCONTEXT */ 659 | 660 | reg [63:0] csr_mcontext; 661 | always @(posedge clk, negedge rst_n) begin 662 | if(!rst_n) csr_mcontext <= 64'h0; 663 | else if(csr_wr && csr_addr == `MCONTEXT) csr_mcontext <= ncsr; 664 | end 665 | 666 | /* SSTATUS */ 667 | 668 | wire [63:0] csr_sstatus = csr_mstatus; 669 | 670 | /* STVEC */ 671 | 672 | reg [63:0] csr_stvec; 673 | always @(posedge clk, negedge rst_n) begin 674 | if(!rst_n) csr_stvec <= 64'h0; 675 | else if(csr_wr && csr_addr == `STVEC) csr_stvec <= ncsr; 676 | end 677 | 678 | /* SIE */ 679 | 680 | reg [63:0] csr_sie; 681 | always @(posedge clk, negedge rst_n) begin 682 | if(!rst_n) csr_sie <= 64'h0; 683 | else if(csr_wr && csr_addr == `SIE) csr_sie <= ncsr; 684 | end 685 | 686 | /* SIP */ 687 | 688 | reg [63:0] csr_sip; 689 | always @(posedge clk, negedge rst_n) begin 690 | if(!rst_n) csr_sip <= 64'h0; 691 | else if(csr_wr && csr_addr == `SIP) csr_sip <= ncsr; 692 | end 693 | 694 | /* SCOUNTEREN */ 695 | 696 | reg [63:0] csr_scounteren; 697 | always @(posedge clk, negedge rst_n) begin 698 | if(!rst_n) csr_scounteren <= 64'h0; 699 | else if(csr_wr && csr_addr == `SCOUNTEREN) csr_scounteren <= ncsr; 700 | end 701 | 702 | /* SSCRATCH */ 703 | 704 | reg [63:0] csr_sscratch; 705 | always @(posedge clk, negedge rst_n) begin 706 | if(!rst_n) csr_sscratch <= 64'h0; 707 | else if(csr_wr && csr_addr == `SSCRATCH) csr_sscratch <= ncsr; 708 | end 709 | 710 | /* SEPC */ 711 | 712 | reg [63:0] csr_sepc; 713 | always @(posedge clk, negedge rst_n) begin 714 | if(!rst_n) csr_sepc <= 64'h0; 715 | else if(s_trap) csr_sepc <= pc; 716 | else if(csr_wr && csr_addr == `SEPC) csr_sepc <= ncsr; 717 | end 718 | 719 | /* SCAUSE */ 720 | 721 | reg [63:0] csr_scause; 722 | always @(posedge clk, negedge rst_n) begin 723 | if(!rst_n) csr_scause <= 64'h0; 724 | else if(s_trap) csr_scause <= tcause; 725 | else if(csr_wr && csr_addr == `SCAUSE) csr_scause <= ncsr; 726 | end 727 | 728 | /* STVAL */ 729 | 730 | reg [63:0] csr_stval; 731 | always @(posedge clk, negedge rst_n) begin 732 | if(!rst_n) csr_stval <= 64'h0; 733 | else if(s_trap) csr_stval <= tval; 734 | else if(csr_wr && csr_addr == `STVAL) csr_stval <= ncsr; 735 | end 736 | 737 | /* SENVCFG */ 738 | 739 | // TODO 740 | 741 | /* SATP */ 742 | 743 | reg [63:0] csr_satp; 744 | always @(posedge clk, negedge rst_n) begin 745 | if(!rst_n) csr_satp <= 64'h0; 746 | else if(csr_wr && csr_addr == `SATP) csr_satp <= ncsr; 747 | end 748 | 749 | /* CYCLE */ 750 | 751 | wire [63:0] csr_cycle = csr_mcycle; 752 | reg csr_cycle_access_exc; 753 | 754 | always @(*) begin 755 | csr_cycle_access_exc = rd_valid && wr_valid && csr_addr == `CYCLE; 756 | case(priv) 757 | 2'b11: csr_cycle_access_exc = 0; 758 | 2'b01: csr_cycle_access_exc = csr_cycle_access_exc && !`MCOUNTEREN_CY; 759 | 2'b00: csr_cycle_access_exc = csr_cycle_access_exc && (!`SCOUNTEREN_CY || !`MCOUNTEREN_CY); 760 | endcase 761 | end 762 | 763 | /* TIME */ 764 | 765 | // wire [63:0] csr_time = mtime; 766 | // TODO: implement mtime in plic 767 | 768 | wire [63:0] csr_time = 0; 769 | reg csr_time_access_exc; 770 | 771 | always @(*) begin 772 | csr_time_access_exc = rd_valid && wr_valid && csr_addr == `TIME; 773 | case(priv) 774 | 2'b11: csr_time_access_exc = 0; 775 | 2'b01: csr_time_access_exc = csr_time_access_exc && !`MCOUNTEREN_TM; 776 | 2'b00: csr_time_access_exc = csr_time_access_exc && (!`SCOUNTEREN_TM || !`MCOUNTEREN_TM); 777 | endcase 778 | end 779 | 780 | /* INSTRET */ 781 | 782 | wire [63:0] csr_instret = csr_minstret; 783 | reg csr_instret_access_exc; 784 | 785 | always @(*) begin 786 | csr_instret_access_exc = rd_valid && wr_valid && csr_addr == `INSTRET; 787 | case(priv) 788 | 2'b11: csr_instret_access_exc = 0; 789 | 2'b01: csr_instret_access_exc = csr_instret_access_exc && !`MCOUNTEREN_IR; 790 | 2'b00: csr_instret_access_exc = csr_instret_access_exc && (!`SCOUNTEREN_IR || !`MCOUNTEREN_IR); 791 | endcase 792 | end 793 | 794 | /* PRIVILEGE LEVEL */ 795 | 796 | always @(posedge clk, negedge rst_n) begin 797 | if(!rst_n) priv <= 2'b11; 798 | else begin 799 | if(trap) priv <= m_trap ? 2'b11 : 2'b01; 800 | if(tret) priv <= mret ? `MSTATUS_MPP : {1'b0, `SSTATUS_SPP}; 801 | end 802 | end 803 | 804 | /* TRAP ENTRY / RETURN */ 805 | 806 | assign csr_ii = csr_addr_invalid || csr_wr_invalid || csr_pr_invalid || csr_cycle_access_exc || csr_time_access_exc || csr_instret_access_exc; 807 | 808 | wire [63:0] stvec_offs = (csr_stvec[1:0] == 2'b01 && !tcause[63]) ? (tcause << 2) : 64'b0; 809 | wire [63:0] mtvec_offs = (csr_mtvec[1:0] == 2'b01 && !tcause[63]) ? (tcause << 2) : 64'b0; 810 | 811 | always @(*) begin 812 | if(trap) t_addr = m_trap ? {csr_mtvec[63:2], 2'b00} + mtvec_offs : {csr_stvec[63:2], 2'b00} + stvec_offs; 813 | else t_addr = mret ? csr_mepc : csr_sepc; 814 | end 815 | 816 | assign t_taken = trap || tret; 817 | 818 | /* TRAP CAUSE */ 819 | 820 | always @(*) begin 821 | tcause = exc_cause; 822 | end 823 | 824 | /* TRAP VAL */ 825 | 826 | always @(*) begin 827 | tval = exc_val; 828 | end 829 | 830 | /* TRAP DELEGATION */ 831 | 832 | always @(*) begin 833 | s_trap = 0; 834 | m_trap = 0; 835 | 836 | if(exc) begin 837 | m_trap = !csr_medeleg[tcause] || priv == 2'b11; 838 | s_trap = csr_medeleg[tcause] && priv != 2'b11; 839 | end 840 | end 841 | 842 | /* PIPELINE FLUSH */ 843 | 844 | assign t_flush = t_taken; 845 | 846 | /* WPRI MASK */ 847 | 848 | reg [63:0] wp_mask; 849 | always @(*) begin 850 | case(csr_addr) 851 | `MSTATUS: wp_mask = 64'h7fffffcfff800015; 852 | `SSTATUS: wp_mask = 64'h7ffffffffff2189d; 853 | default: wp_mask = 64'h0000000000000000; 854 | endcase 855 | if(!rd_valid && !wr_valid) wp_mask = 64'hffffffffffffffff; 856 | end 857 | 858 | reg [63:0] ri_mask; 859 | always @(*) begin 860 | case(csr_addr) 861 | `MSTATUS: ri_mask = 64'h7fffffc0ff800015; 862 | `SSTATUS: ri_mask = 64'h7ffffffcfff2189d; 863 | default: ri_mask = 64'h0000000000000000; 864 | endcase 865 | if(!rd_valid && !wr_valid) ri_mask = 64'hffffffffffffffff; 866 | end 867 | 868 | /* NCSR */ 869 | 870 | always @(*) begin 871 | case(ir[14:12]) 872 | `CSR_RW: ncsr = csr_in; 873 | `CSR_RS: ncsr = csr_in | csr_out; 874 | `CSR_RC: ncsr = ~csr_in & csr_out; 875 | `CSR_RWI: ncsr = {59'b0, ir[19:15]}; 876 | `CSR_RSI: ncsr = {59'b0, ir[19:15]} | csr_out; 877 | `CSR_RCI: ncsr = {59'b0, ~ir[19:15]} & csr_out; 878 | default: ncsr = 64'h0; 879 | endcase 880 | ncsr = ncsr & ~wp_mask | csr_out & wp_mask; 881 | end 882 | 883 | /* OUTPUT MUX */ 884 | 885 | always @(*) begin 886 | csr_addr_invalid = 0; 887 | csr_out = 64'h0; 888 | case(csr_addr) 889 | `MISA: csr_out = csr_misa; 890 | `MVENDORID: csr_out = csr_mvendorid; 891 | `MARCHID: csr_out = csr_marchid; 892 | `MIMPID: csr_out = csr_mimpid; 893 | `MHARTID: csr_out = csr_mhartid; 894 | `MSTATUS: csr_out = csr_mstatus; 895 | `MTVEC: csr_out = csr_mtvec; 896 | `MEDELEG: csr_out = csr_medeleg; 897 | `MIDELEG: csr_out = csr_mideleg; 898 | `MIE: csr_out = csr_mie; 899 | `MIP: csr_out = csr_mip; 900 | `MCYCLE: csr_out = csr_mcycle; 901 | `MINSTRET: csr_out = csr_minstret; 902 | // `MHPMCOUNTER: 903 | `MCOUNTEREN: csr_out = csr_mcounteren; 904 | `MCOUNTINHIBIT: csr_out = csr_mcountinhibit; 905 | `MSCRATCH: csr_out = csr_mscratch; 906 | `MEPC: csr_out = csr_mepc; 907 | `MCAUSE: csr_out = csr_mcause; 908 | `MTVAL: csr_out = csr_mtval; 909 | `MCONFIGPTR: csr_out = csr_mconfigptr; 910 | `MENVCFG: csr_out = csr_menvcfg; 911 | `MSECCFG: csr_out = csr_mseccfg; 912 | `PMPCFG0: csr_out = csr_pmpcfg0; 913 | `PMPCFG2: csr_out = csr_pmpcfg2; 914 | `PMPADDR0: csr_out = csr_pmpaddr0; 915 | `PMPADDR1: csr_out = csr_pmpaddr1; 916 | `PMPADDR2: csr_out = csr_pmpaddr2; 917 | `PMPADDR3: csr_out = csr_pmpaddr3; 918 | `PMPADDR4: csr_out = csr_pmpaddr4; 919 | `PMPADDR5: csr_out = csr_pmpaddr5; 920 | `PMPADDR6: csr_out = csr_pmpaddr6; 921 | `PMPADDR7: csr_out = csr_pmpaddr7; 922 | `PMPADDR8: csr_out = csr_pmpaddr8; 923 | `PMPADDR9: csr_out = csr_pmpaddr9; 924 | `PMPADDR10: csr_out = csr_pmpaddr10; 925 | `PMPADDR11: csr_out = csr_pmpaddr11; 926 | `PMPADDR12: csr_out = csr_pmpaddr12; 927 | `PMPADDR13: csr_out = csr_pmpaddr13; 928 | `PMPADDR14: csr_out = csr_pmpaddr14; 929 | `PMPADDR15: csr_out = csr_pmpaddr15; 930 | 931 | `TSELECT: csr_out = csr_tselect; 932 | `TDATA1: csr_out = csr_tdata1; 933 | `TDATA2: csr_out = csr_tdata2; 934 | `TDATA2: csr_out = csr_tdata3; 935 | `MCONTEXT: csr_out = csr_mcontext; 936 | 937 | `SSTATUS: csr_out = csr_sstatus; 938 | `STVEC: csr_out = csr_stvec; 939 | `SIE: csr_out = csr_sie; 940 | `SIP: csr_out = csr_sip; 941 | `SCOUNTEREN: csr_out = csr_scounteren; 942 | `SSCRATCH: csr_out = csr_sscratch; 943 | `SEPC: csr_out = csr_sepc; 944 | `SCAUSE: csr_out = csr_scause; 945 | `STVAL: csr_out = csr_stval; 946 | // `SENVCFG: 947 | `SATP: csr_out = csr_satp; 948 | 949 | `CYCLE: csr_out = csr_cycle; 950 | `TIME: csr_out = csr_time; 951 | `INSTRET: csr_out = csr_instret; 952 | 953 | default: csr_addr_invalid = rd || wr; 954 | endcase 955 | csr_out = csr_out & ~ri_mask; 956 | end 957 | 958 | assign pmpcfg0 = csr_pmpcfg0; 959 | assign pmpcfg2 = csr_pmpcfg2; 960 | 961 | assign pmpaddr0 = csr_pmpaddr0; 962 | assign pmpaddr1 = csr_pmpaddr1; 963 | assign pmpaddr2 = csr_pmpaddr2; 964 | assign pmpaddr3 = csr_pmpaddr3; 965 | assign pmpaddr4 = csr_pmpaddr4; 966 | assign pmpaddr5 = csr_pmpaddr5; 967 | assign pmpaddr6 = csr_pmpaddr6; 968 | assign pmpaddr7 = csr_pmpaddr7; 969 | assign pmpaddr8 = csr_pmpaddr8; 970 | assign pmpaddr9 = csr_pmpaddr9; 971 | assign pmpaddr10 = csr_pmpaddr10; 972 | assign pmpaddr11 = csr_pmpaddr11; 973 | assign pmpaddr12 = csr_pmpaddr12; 974 | assign pmpaddr13 = csr_pmpaddr13; 975 | assign pmpaddr14 = csr_pmpaddr14; 976 | assign pmpaddr15 = csr_pmpaddr15; 977 | 978 | endmodule 979 | --------------------------------------------------------------------------------