mirror of
https://github.com/lowRISC/ibex.git
synced 2025-06-27 17:00:41 -04:00
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.
472 lines
14 KiB
Systemverilog
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
|