ibex/rtl/ibex_cs_registers.sv
Alex Bradbury 27e68bd76e Convert from Solderpad to standard Apache 2.0 license
This change has been informed by advice from the lowRISC legal
committee.

The Solderpad 0.51 license states "the Licensor permits any Work
licensed under this License, at the option of the Licensee, to be
treated as licensed under the Apache License Version 2.0". We use this
freedom to convert license markings to Apache 2.0. This commit ensures
that we retain all authorship and copyright attribution information.
2019-04-26 15:05:17 +01:00

472 lines
14 KiB
Systemverilog

// Copyright lowRISC contributors.
// Copyright 2018 ETH Zurich and University of Bologna.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
////////////////////////////////////////////////////////////////////////////////
// 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: ibex //
// Language: SystemVerilog //
// //
// Description: Control and Status Registers (CSRs) loosely following the //
// RiscV draft priviledged instruction set spec (v1.9) //
// //
////////////////////////////////////////////////////////////////////////////////
`include "ibex_config.sv"
import ibex_defines::*;
`ifndef PULP_FPGA_EMUL
`ifdef SYNTHESIS
`define ASIC_SYNTHESIS
`endif
`endif
module ibex_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 [23: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 m_irq_enable_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; - unimplemented, hardwired to '0
// logic sie; - unimplemented, hardwired to '0
// logic hie; - unimplemented, hardwired to '0
logic mie;
//logic upie; - unimplemented, hardwired to '0
// 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;
} 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] mepc_q, mepc_n;
logic [ 5:0] mcause_q, mcause_n;
Status_t mstatus_q, mstatus_n;
////////////////////////////////////////////
// ____ ____ ____ ____ //
// / ___/ ___|| _ \ | _ \ ___ __ _ //
// | | \___ \| |_) | | |_) / _ \/ _` | //
// | |___ ___) | _ < | _ < __/ (_| | //
// \____|____/|_| \_\ |_| \_\___|\__, | //
// |___/ //
////////////////////////////////////////////
// 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,
3'h0,
mstatus_q.mie,
3'h0
};
// mtvec: machine trap-handler base address
12'h305: csr_rdata_int = {boot_addr_i, 8'h0};
// 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]};
// 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
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 = '{
mie: csr_wdata_int[`MSTATUS_MIE_BITS],
mpie: csr_wdata_int[`MSTATUS_MPIE_BITS],
mpp: PrivLvl_t'(PRIV_LVL_M)
};
end
// 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.mpie = mstatus_q.mie;
mstatus_n.mie = 1'b0;
mcause_n = csr_cause_i;
end //csr_save_cause_i
csr_restore_mret_i: begin //MRET
mstatus_n.mie = mstatus_q.mpie;
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 mepc_o = mepc_q;
// actual registers
always_ff @(posedge clk, negedge rst_n)
begin
if (rst_n == 1'b0)
begin
mstatus_q <= '{
mie: 1'b0,
mpie: 1'b0,
mpp: PRIV_LVL_M
};
mepc_q <= '0;
mcause_q <= '0;
end
else
begin
// update CSRs
mstatus_q <= '{
mie: mstatus_n.mie,
mpie: mstatus_n.mpie,
mpp: PRIV_LVL_M
};
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'h7A0: begin
is_pcer = 1'b1;
perf_rdata[15:0] = PCER_q;
end
12'h7A1: begin
is_pcmr = 1'b1;
perf_rdata[1:0] = PCMR_q;
end
12'h79F: 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 = csr_addr_i[4:0] < N_PERF_COUNTERS ? PCCR_q[csr_addr_i[4:0]] : '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