diff --git a/include/ariane_pkg.svh b/include/ariane_pkg.svh index 8adb613ba..1834225b6 100644 --- a/include/ariane_pkg.svh +++ b/include/ariane_pkg.svh @@ -16,6 +16,15 @@ package ariane_pkg; localparam TRANS_ID_BITS = $clog2(NR_SB_ENTRIES); // depending on the number of scoreboard entries we need that many bits // to uniquely identify the entry in the scoreboard localparam NR_WB_PORTS = 2; + localparam ISA_CODE = (1 << 2) // C - Compressed extension + | (1 << 8) // I - RV32I/64I/128I base ISA + | (1 << 12) // M - Integer Multiply/Divide extension + | (0 << 13) // N - User level interrupts supported + | (1 << 18) // S - Supervisor mode implemented + | (1 << 20) // U - User mode implemented + | (0 << 23) // X - Non-standard extensions present + | (2 << 62); // RV64 + // --------------- // Fetch Stage // --------------- @@ -44,33 +53,40 @@ package ariane_pkg; // EX Stage // --------------- - typedef enum logic [7:0] { ADD, SUB, ADDW, SUBW, // basic ALU op - XORL, ORL, ANDL, // logic operations - SRA, SRL, SLL, SRLW, SLLW, SRAW, // shifts - LTS, LTU, LES, LEU, GTS, GTU, GES, GEU, EQ, NE, // comparisons - SLTS, SLTU, SLETS, SLETU, // set lower than operations - MRET, SRET, URET, ECALL, WRITE, READ, SET, CLEAR, // CSR functions - LD, SD, LW, LWU, SW, LH, LHU, SH, LB, SB, LBU, SBU // LSU functions - } fu_op; + typedef enum logic [7:0] { // basic ALU op + ADD, SUB, ADDW, SUBW, + // logic operations + XORL, ORL, ANDL, + // shifts + SRA, SRL, SLL, SRLW, SLLW, SRAW, + // comparisons + LTS, LTU, LES, LEU, GTS, GTU, GES, GEU, EQ, NE, + // set lower than operations + SLTS, SLTU, SLETS, SLETU, + // CSR functions + MRET, SRET, URET, ECALL, CSR_WRITE, CSR_READ, CSR_SET, CSR_CLEAR, + // LSU functions + LD, SD, LW, LWU, SW, LH, LHU, SH, LB, SB, LBU, SBU + } fu_op; // --------------- // ID/EX/WB Stage // --------------- typedef struct packed { - logic [TRANS_ID_BITS-1:0] trans_id; // this can potentially be simplified, we could index the scoreboard entry - // with the transaction id in any case make the width more generic - fu_t fu; // functional unit to use - fu_op op; // operation to perform in each functional unit - logic [4:0] rs1; // register source address 1 - logic [4:0] rs2; // register source address 2 - logic [4:0] rd; // register destination address - logic [63:0] result; // for unfinished instructions this field also holds the immediate - logic valid; // is the result valid - logic use_imm; // should we use the immediate as operand b? - logic use_pc; // set if we need to use the PC as operand A, PC from exception - exception ex; // exception has occurred - logic is_compressed; // signals a compressed instructions, we need this information at the commit stage if - // we want jump accordingly e.g.: +4, +2 + logic [TRANS_ID_BITS-1:0] trans_id; // this can potentially be simplified, we could index the scoreboard entry + // with the transaction id in any case make the width more generic + fu_t fu; // functional unit to use + fu_op op; // operation to perform in each functional unit + logic [4:0] rs1; // register source address 1 + logic [4:0] rs2; // register source address 2 + logic [4:0] rd; // register destination address + logic [63:0] result; // for unfinished instructions this field also holds the immediate + logic valid; // is the result valid + logic use_imm; // should we use the immediate as operand b? + logic use_pc; // set if we need to use the PC as operand A, PC from exception + exception ex; // exception has occurred + logic is_compressed; // signals a compressed instructions, we need this information at the commit stage if + // we want jump accordingly e.g.: +4, +2 } scoreboard_entry; // -------------------- @@ -144,7 +160,6 @@ package ariane_pkg; // -------------------- typedef enum logic[1:0] { PRIV_LVL_M = 2'b11, - // PRIV_LVL_H = 2'b10, This mode does not longer exist PRIV_LVL_S = 2'b01, PRIV_LVL_U = 2'b00 } priv_lvl_t; @@ -181,4 +196,47 @@ package ariane_pkg; localparam ENV_CALL_UMODE = 64'h8; localparam ENV_CALL_SMODE = 64'h9; localparam ENV_CALL_MMODE = 64'hB; + + typedef enum logic [11:0] { + + CSR_SSTATUS = 12'h100, + CSR_SIE = 12'h104, + CSR_STVEC = 12'h105, + CSR_SSCRATCH = 12'h140, + CSR_SEPC = 12'h141, + CSR_SCAUSE = 12'h142, + CSR_STVAL = 12'h143, + CSR_SIP = 12'h144, + CSR_SATP = 12'h180, + + CSR_MSTATUS = 12'h300, + CSR_MISA = 12'h301, + CSR_MEDELEG = 12'h302, + CSR_MIDELEG = 12'h303, + CSR_MIE = 12'h304, + CSR_MTVEC = 12'h305, + CSR_MSCRATCH = 12'h340, + CSR_MEPC = 12'h341, + CSR_MCAUSE = 12'h342, + CSR_MTVAL = 12'h343, + CSR_MIP = 12'h344, + CSR_MVENDORID = 12'hF11, + CSR_MARCHID = 12'hF12, + CSR_MIMPID = 12'hF13, + CSR_MHARTID = 12'hF14 + } csr_reg_t; + + // decoded csr address + typedef struct packed { + logic [1:0] rw; + priv_lvl_t priv_lvl; + logic [7:0] address; + } csr_addr_t; + + `ifndef VERILATOR + typedef union packed { + csr_reg_t address; + csr_addr_t csr_decode; + } csr_t; + `endif endpackage diff --git a/src/csr_regfile.sv b/src/csr_regfile.sv index 623bb5e07..d091b752f 100644 --- a/src/csr_regfile.sv +++ b/src/csr_regfile.sv @@ -17,10 +17,315 @@ // (http://www.pulp-platform.org), under the copyright of ETH Zurich and the // University of Bologna. // -module csr_regfile ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - output priv_lvl_t priv_lvl_o +import ariane_pkg::*; + +module csr_regfile #( + parameter int ASID_WIDTH = 1 + )( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + + // Core and Cluster ID + input logic [3:0] core_id_i, + input logic [5:0] cluster_id_i, + input logic [63:0] boot_addr_i, + // we are taking an exception + input exception ex_i, + + input fu_op [1:0] csr_op_i, + input logic [11:0] csr_addr_i, + input logic [63:0] csr_wdata_i, + output logic [63:0] csr_rdata_o, + input logic [63:0] pc_i, // PC of instruction accessing the CSR + output exception csr_exception_o, // attempts to access a CSR without appropriate privilege + // level or to write a read-only register also + // raises illegal instruction exceptions. + // Interrupts/Exceptions + output logic [3:0] irq_enable_o, + output logic [31:0] epc_o, + output logic [31:0] trap_vector_base_o, + output priv_lvl_t priv_lvl_o, + input logic [63:0] badaddr_i, + input logic [63:0] exc_cause_i, + input logic save_exc_cause_i, + // MMU + output logic enable_translation_o, + output logic flag_pum_o, + output logic flag_mxr_o, + // input logic flag_mprv_i, + output logic [37:0] pd_ppn_o, + output logic [ASID_WIDTH-1:0] asid_o + // Performance Counter ); + csr_t csr_addr; + assign csr_addr = csr_t'(csr_addr_i); + + // internal signal to keep track of access exceptions + logic read_access_exception, update_access_exception; + logic csr_we; + logic [63:0] csr_wdata, csr_rdata; + // ---------------- + // CSR Registers + // ---------------- + // privilege level register + priv_lvl_t priv_lvl_n, priv_lvl_q, prev_priv_lvl_n, prev_priv_lvl_q; + typedef struct packed { + logic sd; // signal dirty - read-only - hardwired zero + logic [62:36] wpri4; + logic [1:0] sxl; // variable supervisor mode xlen - hardwired to zero + logic [1:0] uxl; // variable user mode xlen - hardwired to zero + logic [8:0] wpri3; + logic tsr; // trap sret + logic tw; // time wait + logic tvm; // trap virtual memory + logic mxr; // make executable readable + logic sum; // permit supervisor user memory access + logic mprv; // modify privilege - privilege level for ld/st + logic [1:0] xs; // extension register - hardwired to zero + logic [1:0] fs; // extension register - hardwired to zero + priv_lvl_t mpp; // holds the previous privilege mode up to machine + logic [1:0] wpri2; // writes preserved reads ignored + logic spp; // holds the previous privilege mode up to supervisor + logic mpie; // machine interrupts enable bit active prior to trap + logic wpri1; // writes preserved reads ignored + logic spie; // supervisor interrupts enable bit active prior to trap + logic upie; // user interrupts enable bit active prior to trap - hardwired to zero + logic mie; // machine interrupts enable + logic wpri0; // writes preserved reads ignored + logic sie; // supervisor interrupts enable + logic uie; // user interrupts enable - hardwired to zero + } status_t; + + status_t mstatus_q, mstatus_n; + + logic [63:0] mtvec_q, mtvec_n; + logic [63:0] medeleg_q, medeleg_n; + logic [63:0] mideleg_q, mideleg_n; + logic [63:0] mip_q, mip_n; + logic [63:0] mie_q, mie_n; + logic [63:0] mscratch_q, mscratch_n; + logic [63:0] mepc_q, mepc_n; + logic [63:0] mcause_q, mcause_n; + logic [63:0] mtval_q, mtval_n; + + logic [63:0] stvec_q, stvec_n; + logic [63:0] sscratch_q, sscratch_n; + logic [63:0] sepc_q, sepc_n; + logic [63:0] scause_q, scause_n; + logic [63:0] stval_q, stval_n; + + typedef struct packed { + logic [3:0] mode; + logic [15:0] asid; + logic [43:0] ppn; + } sapt_t; + + sapt_t satp_q, satp_n; + + + // ---------------- + // CSR Read logic + // ---------------- + always_comb begin : csr_read + // a read access exception can only occur if we attempt to read a CSR which does not exist + read_access_exception = 1'b0; + csr_rdata = 64'b0; + case (csr_addr.address) + + CSR_SSTATUS: csr_rdata = mstatus_q & 64'h3fffe1fee; + CSR_SIE: csr_rdata = mie_q & mideleg_q; + CSR_SIP: csr_rdata = mip_q & mideleg_q; + CSR_STVEC: csr_rdata = stvec_q; + CSR_SSCRATCH: csr_rdata = sscratch_q; + CSR_SEPC: csr_rdata = sepc_q; + CSR_SCAUSE: csr_rdata = scause_q; + CSR_STVAL: csr_rdata = stval_q; + CSR_SATP: csr_rdata = satp_q; + + CSR_MSTATUS: csr_rdata = mstatus_q; + CSR_MISA: csr_rdata = ISA_CODE; + CSR_MEDELEG: csr_rdata = medeleg_q; + CSR_MIDELEG: csr_rdata = mideleg_q; + CSR_MIP: csr_rdata = mip_q; + CSR_MIE: csr_rdata = mie_q; + CSR_MTVEC: csr_rdata = mtvec_q; + CSR_MSCRATCH: csr_rdata = mscratch_q; + CSR_MEPC: csr_rdata = mepc_q; + CSR_MCAUSE: csr_rdata = mcause_q; + CSR_MTVAL: csr_rdata = mtval_q; + CSR_MVENDORID: csr_rdata = 63'b0; // not implemented + CSR_MARCHID: csr_rdata = 63'b0; // PULP, anonymous source (no allocated ID yet) + CSR_MIMPID: csr_rdata = 63'b0; + CSR_MHARTID: csr_rdata = {53'b0, cluster_id_i[5:0], 1'b0, core_id_i[3:0]}; + default: read_access_exception = 1'b1; + endcase + end + // --------------------------- + // CSR Write and update logic + // --------------------------- + always_comb begin : csr_update + update_access_exception = 1'b0; + + mstatus_n = mstatus_q; + mtvec_n = mtvec_q; + medeleg_n = medeleg_q; + mideleg_n = mideleg_q; + mip_n = mip_q; + mie_n = mie_q; + mepc_n = mepc_q; + mcause_n = mcause_q; + mscratch_n = mscratch_q; + mtval_n = mtval_q; + + sepc_n = sepc_q; + scause_n = scause_q; + stvec_n = stvec_q; + sscratch_n = sscratch_q; + stval_n = stval_q; + + // check for correct access rights and that we are writing + if ((priv_lvl_q && csr_addr.csr_decode.priv_lvl) == csr_addr.csr_decode.priv_lvl && csr_we) begin + case (csr_addr.address) + // sstatus is a subset of mstatus - mask it accordingly + CSR_SSTATUS: mstatus_n = csr_wdata & 64'h3fffe1fee; + // even machine mode interrupts can be visible and set-able to supervisor + // if the corresponding bit in mideleg is set + CSR_SIE: mie_n = csr_wdata & (~64'h111) & mideleg_q; + CSR_SIP: mip_n = csr_wdata & (~64'h111) & mideleg_q; + CSR_STVEC: stvec_n = {csr_wdata[63:2], 1'b0, csr_wdata[0]}; + CSR_SSCRATCH: sscratch_n = csr_wdata; + CSR_SEPC: sepc_n = csr_wdata_i[63:1]; + CSR_SCAUSE: scause_n = csr_wdata_i; + CSR_STVAL: stval_n = csr_wdata_i; + // supervisor address translation and protection + CSR_SATP: satp_n = sapt_t'(csr_wdata_i); + + CSR_MSTATUS: begin + mstatus_n = csr_wdata; + // hardwired zero register + mstatus_n.sd = 1'b0; + mstatus.xs = 2'b0; + mstatus.fs = 2'b0; + mstatus.upie = 1'b0; + mstatus.uie = 1'b0; + end + // machine exception delegation register + // 0 - 12 exceptions supported + CSR_MEDELEG: medeleg_n = csr_wdata & (~64'hBFF); + // machine interrupt delegation register + // we do not support user interrupt delegation + CSR_MIDELEG: mideleg_n = csr_wdata & (~64'hAAA); + + // mask the register so that user interrupts can never be set + CSR_MIE: mie_n = csr_wdata & (~64'h111); + CSR_MIP: mip_n = csr_wdata & (~64'h111); + + CSR_MTVEC: mtvec_n = {csr_wdata[63:2], 1'b0, csr_wdata[0]}; + CSR_MSCRATCH: mscratch_n = csr_wdata_i; + CSR_MEPC: mepc_n = csr_wdata_i[63:1]; + CSR_MCAUSE: mcause_n = csr_wdata_i; + CSR_MTVAL: mtval_n = csr_wdata_i; + default: update_access_exception = 1'b1; + endcase + end else begin + update_access_exception = 1'b1; + end + + // we got an exception update cause, pc and stval register + if (ex_i.valid) begin + // TODO + // set cause + + // set epc + + // set mtval or stval + end + end + // --------------------------- + // CSR Op Select Logic + // --------------------------- + always_comb begin : csr_op_logic + csr_wdata = csr_wdata_i; + csr_we = 1'b1; + + unique case (csr_op_i) + CSR_WRITE: csr_wdata = csr_wdata_i; + CSR_SET: csr_wdata = csr_wdata_i | csr_rdata; + CSR_CLEAR: csr_wdata = (~csr_wdata_i) & csr_rdata; + default: csr_we = 1'b0; + endcase + end + // ------------------- + // Exception Control + // ------------------- + always_comb begin : exception_ctrl + csr_exception_o = { + 64'b0, 64'b0, 1'b0 + }; + // we got an exception in one of the processes above + // throw an illegal instruction exception + if (update_access_exception || read_access_exception) begin + csr_exception_o = { + pc_i, ILLEGAL_INSTR, 1'b1 + }; + end + end + // ------------------- + // Output Assignments + // ------------------- + assign csr_rdata_o = csr_rdata; + assign priv_lvl_o = priv_lvl_q; + assign pd_ppn_o = satp_q.ppn; + assign asid_o = satp_q.asid; + // output assignments dependent on privilege mode + always_comb begin : priv_output + trap_vector_base_o = mtvec_q; + + // output user mode stvec + if (priv_lvl_q == PRIV_LVL_S) begin + trap_vector_base_o = stvec_q; + end + end + + // sequential process + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + priv_lvl_q <= PRIV_LVL_M; + prev_priv_lvl_q <= PRIV_LVL_M; + mstatus_q <= 64'b0; + mtvec_q <= {boot_addr_i[63:2], 2'b0}; // set to boot address + direct mode + medeleg_q <= 64'b0; + mideleg_q <= 64'b0; + mepc_q <= 64'b0; + mcause_q <= 64'b0; + mscratch_q <= 64'b0; + mtval_q <= 64'b0; + + sepc_q <= 64'b0; + scause_q <= 64'b0; + stvec_q <= 64'b0; + sscratch_q <= 64'b0; + stval_q <= 64'b0; + end else begin + priv_lvl_q <= priv_lvl_n; + prev_priv_lvl_q <= prev_priv_lvl_n; + mstatus_q <= mstatus_n; + mtvec_q <= mtvec_n; + medeleg_q <= medeleg_n; + mideleg_q <= mideleg_n; + mip_q <= mip_n; + mie_q <= mie_n; + mepc_q <= mepc_n; + mcause_q <= mcause_n; + mscratch_q <= mscratch_n; + mtval_q <= mtval_n; + + sepc_q <= sepc_n; + scause_q <= scause_n; + stvec_q <= stvec_n; + sscratch_q <= sscratch_n; + stval_q <= stval_n; + end + end endmodule \ No newline at end of file