// Copyright 2017 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the “License”); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. //////////////////////////////////////////////////////////////////////////////// // Engineer: Sven Stucki - svstucki@student.ethz.ch // // // // Additional contributions by: // // Andreas Traber - atraber@iis.ee.ethz.ch // // Davide Schiavone - pschiavo@iis.ee.ethz.ch // // // // Design Name: Control and Status Registers // // Project Name: zero-riscy // // Language: SystemVerilog // // // // Description: Control and Status Registers (CSRs) loosely following the // // RiscV draft priviledged instruction set spec (v1.9) // // // //////////////////////////////////////////////////////////////////////////////// `include "zeroriscy_config.sv" import zeroriscy_defines::*; `ifndef PULP_FPGA_EMUL `ifdef SYNTHESIS `define ASIC_SYNTHESIS `endif `endif module zeroriscy_cs_registers #( parameter N_EXT_CNT = 0 ) ( // Clock and Reset input logic clk, input logic rst_n, // Core and Cluster ID input logic [3:0] core_id_i, input logic [5:0] cluster_id_i, // Used for boot address input logic [31:0] boot_addr_i, // Interface to registers (SRAM like) input logic csr_access_i, input logic [11:0] csr_addr_i, input logic [31:0] csr_wdata_i, input logic [1:0] csr_op_i, output logic [31:0] csr_rdata_o, // Interrupts output logic illegal_csr_o, output logic m_irq_enable_o, output PrivLvl_t m_prv_o, output logic [31:0] mtvec_o, output logic [31:0] mepc_o, input logic [31:0] pc_if_i, input logic [31:0] pc_id_i, input logic csr_save_if_i, input logic csr_save_id_i, input logic csr_restore_mret_i, input logic [5:0] csr_cause_i, input logic csr_save_cause_i, // Performance Counters input logic if_valid_i, // IF stage gives a new instruction input logic id_valid_i, // ID stage is done input logic is_compressed_i, // compressed instruction in ID input logic is_decoding_i, // controller is in DECODE state input logic imiss_i, // instruction fetch input logic pc_set_i, // pc was set to a new value input logic jump_i, // jump instruction seen (j, jr, jal, jalr) input logic branch_i, // branch instruction seen (bf, bnf) input logic branch_taken_i, // branch was taken input logic mem_load_i, // load from memory in this cycle input logic mem_store_i, // store to memory in this cycle input logic [N_EXT_CNT-1:0] ext_counters_i ); localparam N_PERF_COUNTERS = 11 + N_EXT_CNT; `ifdef ASIC_SYNTHESIS localparam N_PERF_REGS = 1; `else localparam N_PERF_REGS = N_PERF_COUNTERS; `endif `define MSTATUS_UIE_BITS 0 `define MSTATUS_SIE_BITS 1 `define MSTATUS_MIE_BITS 3 `define MSTATUS_UPIE_BITS 4 `define MSTATUS_SPIE_BITS 5 `define MSTATUS_MPIE_BITS 7 `define MSTATUS_SPP_BITS 8 `define MSTATUS_MPP_BITS 12:11 typedef struct packed { logic uie; // logic sie; - unimplemented, hardwired to '0 // logic hie; - unimplemented, hardwired to '0 logic mie; logic upie; // logic spie; - unimplemented, hardwired to '0 // logic hpie; - unimplemented, hardwired to '0 logic mpie; // logic spp; - unimplemented, hardwired to '0 // logic[1:0] hpp; - unimplemented, hardwired to '0 PrivLvl_t mpp; PrivLvl_t prv; } Status_t; // Performance Counter Signals logic id_valid_q; logic [N_PERF_COUNTERS-1:0] PCCR_in; // input signals for each counter category logic [N_PERF_COUNTERS-1:0] PCCR_inc, PCCR_inc_q; // should the counter be increased? logic [N_PERF_REGS-1:0] [31:0] PCCR_q, PCCR_n; // performance counters counter register logic [1:0] PCMR_n, PCMR_q; // mode register, controls saturation and global enable logic [N_PERF_COUNTERS-1:0] PCER_n, PCER_q; // selected counter input logic [31:0] perf_rdata; logic [4:0] pccr_index; logic pccr_all_sel; logic is_pccr; logic is_pcer; logic is_pcmr; // CSR update logic logic [31:0] csr_wdata_int; logic [31:0] csr_rdata_int; logic csr_we_int; // Interrupt control signals logic [31:0] mtvec_q, mtvec_n; logic [31:0] mscratch_q, mscratch_n; logic [31:0] mepc_q, mepc_n; logic [ 5:0] mcause_q, mcause_n; Status_t mstatus_q, mstatus_n; //////////////////////////////////////////// // ____ ____ ____ ____ // // / ___/ ___|| _ \ | _ \ ___ __ _ // // | | \___ \| |_) | | |_) / _ \/ _` | // // | |___ ___) | _ < | _ < __/ (_| | // // \____|____/|_| \_\ |_| \_\___|\__, | // // |___/ // //////////////////////////////////////////// // illegal logic always_comb begin illegal_csr_o = 1'b0; if(csr_access_i)begin case (csr_addr_i) // mstatus: always M-mode, contains IE bit 12'h300: ; // misa: Machine ISA 12'h301: ; // mtvec: machine trap-handler base address 12'h305: ; // mscratch: Machine Scratch 12'h340: ; // mepc: exception program counter 12'h341: ; // mcause: exception cause 12'h342: ; // tselecr: trigger select 12'h7a0: ; // Machine xx IDs 12'hF11: ; 12'hF12: ; 12'hF13: ; // mhartid: unique hardware thread id 12'hF14: ; default: illegal_csr_o = 1'b1; endcase end end // read logic always_comb begin csr_rdata_int = '0; case (csr_addr_i) // mstatus: always M-mode, contains IE bit 12'h300: csr_rdata_int = { 19'b0, mstatus_q.mpp, 3'b0, mstatus_q.mpie, 2'h0, mstatus_q.upie, mstatus_q.mie, 2'h0, mstatus_q.uie }; // misa: Machine ISA 12'h301: csr_rdata_int = 32'h40001104; // mtvec: machine trap-handler base address 12'h305: csr_rdata_int = mtvec_q; // mscratch: Machine Scratch 12'h340: csr_rdata_int = mscratch_q; // mepc: exception program counter 12'h341: csr_rdata_int = mepc_q; // mcause: exception cause 12'h342: csr_rdata_int = {mcause_q[5], 26'b0, mcause_q[4:0]}; // tselecr: trigger select 12'h7a0: csr_rdata_int = -1; // Machine xx IDs 12'hF11: csr_rdata_int = '0; 12'hF12: csr_rdata_int = '0; 12'hF13: csr_rdata_int = '0; // mhartid: unique hardware thread id 12'hF14: csr_rdata_int = {21'b0, cluster_id_i[5:0], 1'b0, core_id_i[3:0]}; default: ; endcase end // write logic always_comb begin mtvec_n = mtvec_q; mscratch_n = mscratch_q; mepc_n = mepc_q; mstatus_n = mstatus_q; mcause_n = mcause_q; case (csr_addr_i) // mstatus: IE bit 12'h300: if (csr_we_int) begin mstatus_n = '{ uie: csr_wdata_int[`MSTATUS_UIE_BITS], mie: csr_wdata_int[`MSTATUS_MIE_BITS], upie: csr_wdata_int[`MSTATUS_UPIE_BITS], mpie: csr_wdata_int[`MSTATUS_MPIE_BITS], // mpp: csr_wdata_int[`MSTATUS_MPP_BITS], mpp: PrivLvl_t'({2{|csr_wdata_int[`MSTATUS_MPP_BITS]}}), prv: mstatus_q.prv }; end // mtvec: machine trap-handler base address 12'h305: if (csr_we_int) mtvec_n = csr_wdata_int; // mscratch: Machine Scratch 12'h340: if (csr_we_int) mscratch_n = csr_wdata_int; // mepc: exception program counter 12'h341: if (csr_we_int) mepc_n = csr_wdata_int; // mcause 12'h342: if (csr_we_int) mcause_n = {csr_wdata_int[31], csr_wdata_int[4:0]}; default: ; endcase // exception controller gets priority over other writes unique case (1'b1) csr_save_cause_i: begin unique case (1'b1) csr_save_if_i: mepc_n = pc_if_i; csr_save_id_i: mepc_n = pc_id_i; default:; endcase mstatus_n.mpp = mstatus_q.prv; if(mstatus_q.prv == PrivLvl_t'(PRIV_LVL_M))begin mstatus_n.mpie = mstatus_q.mie; end else begin mstatus_n.mpie = mstatus_q.uie; end mstatus_n.mie = 1'b0; mstatus_n.prv = PrivLvl_t'(PRIV_LVL_M); mcause_n = csr_cause_i; end //csr_save_cause_i csr_restore_mret_i: begin //MRET mstatus_n.mpp = PrivLvl_t'(PRIV_LVL_U); mstatus_n.prv = mstatus_q.mpp; if(mstatus_q.prv == PrivLvl_t'(PRIV_LVL_M))begin mstatus_n.mie = mstatus_q.mpie; end else begin mstatus_n.mie = mstatus_q.upie; end mstatus_n.mpie = 1'b1; end //csr_restore_mret_i default:; endcase end // CSR operation logic always_comb begin csr_wdata_int = csr_wdata_i; csr_we_int = 1'b1; unique case (csr_op_i) CSR_OP_WRITE: csr_wdata_int = csr_wdata_i; CSR_OP_SET: csr_wdata_int = csr_wdata_i | csr_rdata_o; CSR_OP_CLEAR: csr_wdata_int = (~csr_wdata_i) & csr_rdata_o; CSR_OP_NONE: begin csr_wdata_int = csr_wdata_i; csr_we_int = 1'b0; end default:; endcase end // output mux always_comb begin csr_rdata_o = csr_rdata_int; // performance counters if (is_pccr || is_pcer || is_pcmr) csr_rdata_o = perf_rdata; end // directly output some registers assign m_irq_enable_o = mstatus_q.mie; assign m_prv_o = mstatus_q.prv; assign mtvec_o = mtvec_q; assign mepc_o = mepc_q; // actual registers always_ff @(posedge clk, negedge rst_n) begin if (rst_n == 1'b0) begin mstatus_q <= '{ uie: 1'b0, mie: 1'b0, upie: 1'b0, mpie: 1'b0, mpp: PRIV_LVL_U, prv: PRIV_LVL_M }; mtvec_q <= boot_addr_i; mscratch_q <= '0; mepc_q <= '0; mcause_q <= '0; end else begin // update CSRs mstatus_q <= '{ uie: mstatus_n.uie, mie: mstatus_n.mie, upie: mstatus_n.upie, mpie: mstatus_n.mpie, mpp: mstatus_n.mpp, prv: mstatus_n.prv }; mtvec_q <= mtvec_n; mscratch_q <= mscratch_n; mepc_q <= mepc_n; mcause_q <= mcause_n; end end ///////////////////////////////////////////////////////////////// // ____ __ ____ _ // // | _ \ ___ _ __ / _| / ___|___ _ _ _ __ | |_ ___ _ __ // // | |_) / _ \ '__| |_ | | / _ \| | | | '_ \| __/ _ \ '__| // // | __/ __/ | | _| | |__| (_) | |_| | | | | || __/ | // // |_| \___|_| |_|(_) \____\___/ \__,_|_| |_|\__\___|_| // // // ///////////////////////////////////////////////////////////////// assign PCCR_in[0] = 1'b1; // cycle counter assign PCCR_in[1] = if_valid_i; // instruction counter assign PCCR_in[2] = 1'b0; // Reserved assign PCCR_in[3] = 1'b0; // Reserved assign PCCR_in[4] = imiss_i & (~pc_set_i); // cycles waiting for instruction fetches, excluding jumps and branches assign PCCR_in[5] = mem_load_i; // nr of loads assign PCCR_in[6] = mem_store_i; // nr of stores assign PCCR_in[7] = jump_i; // nr of jumps (unconditional) assign PCCR_in[8] = branch_i; // nr of branches (conditional) assign PCCR_in[9] = branch_taken_i; // nr of taken branches (conditional) assign PCCR_in[10] = id_valid_i & is_decoding_i & is_compressed_i; // compressed instruction counter // assign external performance counters generate genvar i; for (i = 0; i < N_EXT_CNT; i++) begin : g_extcounters assign PCCR_in[N_PERF_COUNTERS - N_EXT_CNT + i] = ext_counters_i[i]; end endgenerate // address decoder for performance counter registers always_comb begin is_pccr = 1'b0; is_pcmr = 1'b0; is_pcer = 1'b0; pccr_all_sel = 1'b0; pccr_index = '0; perf_rdata = '0; // only perform csr access if we actually care about the read data if (csr_access_i) begin unique case (csr_addr_i) 12'h790: begin is_pcer = 1'b1; perf_rdata[15:0] = PCER_q; end 12'h791: begin is_pcmr = 1'b1; perf_rdata[1:0] = PCMR_q; end 12'h78F: begin is_pccr = 1'b1; pccr_all_sel = 1'b1; end default:; endcase // look for 780 to 79F, Performance Counter Counter Registers if (csr_addr_i[11:5] == 7'b0111100) begin is_pccr = 1'b1; pccr_index = csr_addr_i[4:0]; `ifdef ASIC_SYNTHESIS perf_rdata = PCCR_q[0]; `else perf_rdata = PCCR_q[csr_addr_i[4:0]]; `endif end end end // performance counter counter update logic `ifdef ASIC_SYNTHESIS // for synthesis we just have one performance counter register assign PCCR_inc[0] = (|(PCCR_in & PCER_q)) & PCMR_q[0]; always_comb begin PCCR_n[0] = PCCR_q[0]; if ((PCCR_inc_q[0] == 1'b1) && ((PCCR_q[0] != 32'hFFFFFFFF) || (PCMR_q[1] == 1'b0))) PCCR_n[0] = PCCR_q[0] + 1; if (is_pccr == 1'b1) begin unique case (csr_op_i) CSR_OP_NONE: ; CSR_OP_WRITE: PCCR_n[0] = csr_wdata_i; CSR_OP_SET: PCCR_n[0] = csr_wdata_i | PCCR_q[0]; CSR_OP_CLEAR: PCCR_n[0] = csr_wdata_i & ~(PCCR_q[0]); endcase end end `else always_comb begin for(int i = 0; i < N_PERF_COUNTERS; i++) begin : PERF_CNT_INC PCCR_inc[i] = PCCR_in[i] & PCER_q[i] & PCMR_q[0]; PCCR_n[i] = PCCR_q[i]; if ((PCCR_inc_q[i] == 1'b1) && ((PCCR_q[i] != 32'hFFFFFFFF) || (PCMR_q[1] == 1'b0))) PCCR_n[i] = PCCR_q[i] + 1; if (is_pccr == 1'b1 && (pccr_all_sel == 1'b1 || pccr_index == i)) begin unique case (csr_op_i) CSR_OP_NONE: ; CSR_OP_WRITE: PCCR_n[i] = csr_wdata_i; CSR_OP_SET: PCCR_n[i] = csr_wdata_i | PCCR_q[i]; CSR_OP_CLEAR: PCCR_n[i] = csr_wdata_i & ~(PCCR_q[i]); endcase end end end `endif // update PCMR and PCER always_comb begin PCMR_n = PCMR_q; PCER_n = PCER_q; if (is_pcmr) begin unique case (csr_op_i) CSR_OP_NONE: ; CSR_OP_WRITE: PCMR_n = csr_wdata_i[1:0]; CSR_OP_SET: PCMR_n = csr_wdata_i[1:0] | PCMR_q; CSR_OP_CLEAR: PCMR_n = csr_wdata_i[1:0] & ~(PCMR_q); endcase end if (is_pcer) begin unique case (csr_op_i) CSR_OP_NONE: ; CSR_OP_WRITE: PCER_n = csr_wdata_i[N_PERF_COUNTERS-1:0]; CSR_OP_SET: PCER_n = csr_wdata_i[N_PERF_COUNTERS-1:0] | PCER_q; CSR_OP_CLEAR: PCER_n = csr_wdata_i[N_PERF_COUNTERS-1:0] & ~(PCER_q); endcase end end // Performance Counter Registers always_ff @(posedge clk, negedge rst_n) begin if (rst_n == 1'b0) begin id_valid_q <= 1'b0; PCER_q <= '0; PCMR_q <= 2'h3; for(int i = 0; i < N_PERF_REGS; i++) begin PCCR_q[i] <= '0; PCCR_inc_q[i] <= '0; end end else begin id_valid_q <= id_valid_i; PCER_q <= PCER_n; PCMR_q <= PCMR_n; for(int i = 0; i < N_PERF_REGS; i++) begin PCCR_q[i] <= PCCR_n[i]; PCCR_inc_q[i] <= PCCR_inc[i]; end end end endmodule