Add standard compliant performance counters

This commit adds the CSRs `mcycle(h)`, `minstret(h)`, `mcountinhibit` and
mhpmcounter3(h) - mhpmcounter31(h) as well as `mphmevent3 - mhpmevent31`.
The registers `mphmevent3 - mhpmevent31` are hardwired and cannot be
reconfigured.
This commit is contained in:
Pirmin Vogel 2019-05-27 14:08:28 +01:00
parent 7a5c8ef5c6
commit 3ea6744f81
4 changed files with 233 additions and 34 deletions

View file

@ -26,6 +26,8 @@
*/
module ibex_core #(
parameter int unsigned NumExtPerfCounters = 1,
parameter int unsigned MHPMCounterNum = 8,
parameter int unsigned MHPMCounterWidth = 40,
parameter bit RV32E = 0,
parameter bit RV32M = 1,
parameter int unsigned DmHaltAddr = 32'h1A110800,
@ -82,6 +84,7 @@ module ibex_core #(
logic [31:0] instr_rdata_id; // Instruction sampled inside IF stage
logic is_compressed_id;
logic illegal_c_insn_id; // Illegal compressed instruction sent to ID stage
logic illegal_insn_id; // ID stage sees an illegal instruction
logic [31:0] pc_if; // Program counter in IF stage
logic [31:0] pc_id; // Program counter in ID stage
@ -176,7 +179,8 @@ module ibex_core #(
logic debug_single_step;
logic debug_ebreakm;
// Performance Counters
// performance counter related signals
logic insn_ret;
logic perf_imiss;
logic perf_jump;
logic perf_branch;
@ -291,6 +295,7 @@ module ibex_core #(
.ctrl_busy_o ( ctrl_busy ),
.core_ctrl_firstfetch_o ( core_ctrl_firstfetch ),
.is_decoding_o ( is_decoding ),
.illegal_insn_o ( illegal_insn_id ),
// Interface to instruction memory
.instr_valid_i ( instr_valid_id ),
@ -378,7 +383,6 @@ module ibex_core #(
.perf_jump_o ( perf_jump ),
.perf_branch_o ( perf_branch ),
.perf_tbranch_o ( perf_tbranch )
);
@ -470,10 +474,16 @@ module ibex_core #(
assign perf_load = data_req_o & data_gnt_i & (~data_we_o);
assign perf_store = data_req_o & data_gnt_i & data_we_o;
// An instruction has been executed and retired if the ID stage gets a new instruction and
// the previously seen instruction was valid.
assign insn_ret = if_valid & ~illegal_insn_id;
ibex_cs_registers #(
.NumExtCounters ( NumExtPerfCounters ),
.RV32E ( RV32E ),
.RV32M ( RV32M )
.NumExtCounters ( NumExtPerfCounters ),
.MHPMCounterNum ( MHPMCounterNum ),
.MHPMCounterWidth ( MHPMCounterWidth ),
.RV32E ( RV32E ),
.RV32M ( RV32M )
) cs_registers_i (
.clk_i ( clk ),
.rst_ni ( rst_ni ),
@ -513,6 +523,7 @@ module ibex_core #(
// performance counter related signals
.insn_ret_i ( insn_ret ),
.if_valid_i ( if_valid ),
.id_valid_i ( id_valid ),
.is_compressed_i ( is_compressed_id ),
@ -525,6 +536,7 @@ module ibex_core #(
.branch_taken_i ( perf_tbranch ),
.mem_load_i ( perf_load ),
.mem_store_i ( perf_store ),
.lsu_busy_i ( lsu_busy ),
.ext_counters_i ( ext_perf_counters_i )
);

View file

@ -14,21 +14,23 @@
// Project Name: ibex //
// Language: SystemVerilog //
// //
// Description: Control and Status Registers (CSRs) loosely following the //
// RiscV draft priviledged instruction set spec (v1.9) //
// Description: Control and Status Registers (CSRs) following the RISC-V //
// Privileged Specification, draft version 1.11 //
// //
////////////////////////////////////////////////////////////////////////////////
/**
* Control and Status Registers
*
* Control and Status Registers (CSRs) loosely following the RiscV draft
* priviledged instruction set spec (v1.9)
* Control and Status Registers (CSRs) following the RISC-V Privileged
* Specification, draft version 1.11
*/
module ibex_cs_registers #(
parameter int unsigned NumExtCounters = 0,
parameter bit RV32E = 0,
parameter bit RV32M = 0
parameter int unsigned NumExtCounters = 0,
parameter int unsigned MHPMCounterNum = 8,
parameter int unsigned MHPMCounterWidth = 40,
parameter bit RV32E = 0,
parameter bit RV32M = 0
) (
// Clock and Reset
input logic clk_i,
@ -70,18 +72,20 @@ module ibex_cs_registers #(
input logic csr_save_cause_i,
// Performance Counters
input logic if_valid_i, // IF stage gives a new instr
input logic id_valid_i, // ID stage is done
input logic is_compressed_i, // compressed instr in ID
input logic is_decoding_i, // controller is in DECODE state
input logic insn_ret_i, // instr retired in ID/EX stage
input logic if_valid_i, // IF stage gives a new instr
input logic id_valid_i, // ID stage is done
input logic is_compressed_i, // compressed instr in ID
input logic is_decoding_i, // controller is in DECODE state
input logic imiss_i, // instr fetch
input logic pc_set_i, // pc was set to a new value
input logic jump_i, // jump instr seen (j, jr, jal, jalr)
input logic branch_i, // branch instr 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 imiss_i, // instr fetch
input logic pc_set_i, // pc was set to a new value
input logic jump_i, // jump instr seen (j, jr, jal, jalr)
input logic branch_i, // branch instr 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 lsu_busy_i,
input logic [NumExtCounters-1:0] ext_counters_i
);
@ -152,8 +156,20 @@ module ibex_cs_registers #(
priv_lvl_e prv;
} Dcsr_t;
// Hardware performance monitor signals
logic [31:0] mcountinhibit_n, mcountinhibit_q, mcountinhibit;
logic [31:0] mcountinhibit_force;
logic mcountinhibit_we;
logic [63:0] mhpmcounter_mask [32];
logic [63:0] mhpmcounter_n [32];
logic [63:0] mhpmcounter_q [32];
logic [31:0] mhpmcounter_we;
logic [31:0] mhpmcounterh_we;
logic [31:0] mhpmcounter_incr;
logic [31:0] mhpmevent [32];
logic [4:0] mhpmcounter_idx;
// Performance Counter Signals
// Legacy Performance Counter Signals
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?
@ -188,6 +204,10 @@ module ibex_cs_registers #(
// CSR reg //
/////////////
logic [$bits(csr_num_e)-1:0] csr_addr;
assign csr_addr = {csr_addr_i};
assign mhpmcounter_idx = csr_addr[4:0];
// read logic
always_comb begin
csr_rdata_int = '0;
@ -221,11 +241,27 @@ module ibex_cs_registers #(
// misa
CSR_MISA: csr_rdata_int = MISA_VALUE;
CSR_DCSR: csr_rdata_int = dcsr_q;
CSR_DPC: csr_rdata_int = depc_q;
CSR_DCSR: csr_rdata_int = dcsr_q;
CSR_DPC: csr_rdata_int = depc_q;
CSR_DSCRATCH0: csr_rdata_int = dscratch0_q;
CSR_DSCRATCH1: csr_rdata_int = dscratch1_q;
default:;
// Machine Counter/Timers
CSR_MCOUNTINHIBIT: csr_rdata_int = mcountinhibit;
CSR_MCYCLE: csr_rdata_int = mhpmcounter_q[0][31: 0];
CSR_MCYCLEH: csr_rdata_int = mhpmcounter_q[0][63:32];
CSR_MINSTRET: csr_rdata_int = mhpmcounter_q[2][31: 0];
CSR_MINSTRETH: csr_rdata_int = mhpmcounter_q[2][63:32];
default: begin
if (csr_addr_i == CSR_MCOUNTER_SETUP_MASK) begin
csr_rdata_int = mhpmevent[mhpmcounter_idx];
end else if (csr_addr_i == CSR_MCOUNTER_MASK) begin
csr_rdata_int = mhpmcounter_q[mhpmcounter_idx][31: 0];
end else if (csr_addr_i == CSR_MCOUNTERH_MASK) begin
csr_rdata_int = mhpmcounter_q[mhpmcounter_idx][63:32];
end
end
endcase
end
@ -240,6 +276,10 @@ module ibex_cs_registers #(
mcause_n = mcause_q;
exception_pc = pc_id_i;
mcountinhibit_we = 1'b0;
mhpmcounter_we = '0;
mhpmcounterh_we = '0;
unique case (csr_addr_i)
// mstatus: IE bit
CSR_MSTATUS: begin
@ -251,6 +291,7 @@ module ibex_cs_registers #(
};
end
end
// mepc: exception program counter
CSR_MEPC: if (csr_we_int) mepc_n = csr_wdata_int;
// mcause
@ -294,7 +335,46 @@ module ibex_cs_registers #(
end
end
default:;
CSR_MCOUNTINHIBIT: begin
if (csr_we_int) begin
mcountinhibit_we = 1'b1;
end
end
CSR_MCYCLE: begin
if (csr_we_int) begin
mhpmcounter_we[0] = 1'b1;
end
end
CSR_MCYCLEH: begin
if (csr_we_int) begin
mhpmcounterh_we[0] = 1'b1;
end
end
CSR_MINSTRET: begin
if (csr_we_int) begin
mhpmcounter_we[2] = 1'b1;
end
end
CSR_MINSTRETH: begin
if (csr_we_int) begin
mhpmcounterh_we[2] = 1'b1;
end
end
default: begin
if (csr_we_int == 1'b1) begin
// performance counters and event selector
if (csr_addr_i == CSR_MCOUNTER_MASK) begin
mhpmcounter_we[mhpmcounter_idx] = 1'b1;
end else if (csr_addr_i == CSR_MCOUNTERH_MASK) begin
mhpmcounterh_we[mhpmcounter_idx] = 1'b1;
end
end
end
endcase
// exception controller gets priority over other writes
@ -340,7 +420,6 @@ module ibex_cs_registers #(
endcase
end
// CSR operation logic
always_comb begin
csr_we_int = 1'b1;
@ -409,12 +488,107 @@ module ibex_cs_registers #(
end
//////////////////////////
// Performance counters //
// Performance monitor //
//////////////////////////
logic [$bits(csr_num_e)-1:0] csr_addr;
// update enable signals
always_comb begin : mcountinhibit_update
if (mcountinhibit_we == 1'b1) begin
mcountinhibit_n = csr_wdata_int;
end else begin
mcountinhibit_n = mcountinhibit_q;
end
// bit 1 must always be 0
mcountinhibit_n[1] = 1'b0;
end
assign csr_addr = {csr_addr_i};
assign mcountinhibit_force = {{29-MHPMCounterNum{1'b1}}, {MHPMCounterNum{1'b0}}, 3'b000};
assign mcountinhibit = mcountinhibit_q | mcountinhibit_force;
// event selection (hardwired) & control
assign mhpmcounter_incr[0] = 1'b1; // mcycle
assign mhpmcounter_incr[1] = 1'b0; // reserved
assign mhpmcounter_incr[2] = insn_ret_i; // minstret
assign mhpmcounter_incr[3] = lsu_busy_i; // cycles waiting for data memory
assign mhpmcounter_incr[4] = imiss_i & ~pc_set_i; // cycles waiting for instr fetches ex.
// jumps and branches
assign mhpmcounter_incr[5] = mem_load_i; // num of loads
assign mhpmcounter_incr[6] = mem_store_i; // num of stores
assign mhpmcounter_incr[7] = jump_i; // num of jumps (unconditional)
assign mhpmcounter_incr[8] = branch_i; // num of branches (conditional)
assign mhpmcounter_incr[9] = branch_taken_i; // num of taken branches (conditional)
assign mhpmcounter_incr[10] = is_compressed_i // num of compressed instr
& id_valid_i & is_decoding_i;
for (genvar i=3+MHPMCounterNum; i<32; i++) begin : gen_mhpmcounter_incr_inactive
assign mhpmcounter_incr[i] = 1'b0;
end
// event selector (hardwired, 0 means no event)
always_comb begin : gen_mhpmevent
// activate all
for (int i=0; i<32; i++) begin : gen_mhpmevent_active
mhpmevent[i][i] = 1'b1;
end
// deactivate
mhpmevent[1] = '0; // not existing, reserved
for (int i=3+MHPMCounterNum; i<32; i++) begin : gen_mhpmevent_inactive
mhpmevent[i] = '0;
end
end
// mask, controls effective counter width
always_comb begin : gen_mask
for (int i=0; i<3; i++) begin : gen_mask_fixed
// mcycle, mtime, minstret are always 64 bit wide
mhpmcounter_mask[i] = {64{1'b1}};
end
for (int i=3; i<32; i++) begin : gen_mask_configurable
// mhpmcounters have a configurable width
mhpmcounter_mask[i] = {{{64-MHPMCounterWidth}{1'b0}}, {MHPMCounterWidth{1'b1}}};
end
end
// update
always_comb begin : mhpmcounter_update
mhpmcounter_n = mhpmcounter_q;
for (int i=0; i<32; i++) begin : gen_mhpmcounter_update
// increment
if (mhpmcounter_incr[i] & ~mcountinhibit[i]) begin
mhpmcounter_n[i] = mhpmcounter_mask[i] & (mhpmcounter_n[i] + 64'h1);
end
// write
if (mhpmcounter_we[i]) begin
mhpmcounter_n[i][31: 0] = mhpmcounter_mask[i][31: 0] & csr_wdata_int;
end else if (mhpmcounterh_we[i]) begin
mhpmcounter_n[i][63:32] = mhpmcounter_mask[i][63:32] & csr_wdata_int;
end
end
end
// performance monitor registers
always_ff @(posedge clk_i or negedge rst_ni) begin : perf_counter_registers
if (!rst_ni) begin
mcountinhibit_q <= '0;
for (int i=0; i<32; i++) begin
mhpmcounter_q[i] <= '0;
end
end else begin
mhpmcounter_q <= mhpmcounter_n;
mcountinhibit_q <= mcountinhibit_n;
end
end
/////////////////////////////////
// Legacy Performance counters //
/////////////////////////////////
assign PCCR_in[0] = 1'b1; // cycle counter
assign PCCR_in[1] = if_valid_i; // instruction counter

View file

@ -242,7 +242,18 @@ typedef enum logic[11:0] {
// Debug
CSR_DSCRATCH0 = 12'h7b2, // optional
CSR_DSCRATCH1 = 12'h7b3 // optional
CSR_DSCRATCH1 = 12'h7b3, // optional
// Machine Counter/Timers
CSR_MCOUNTINHIBIT = 12'h320,
CSR_MCYCLE = 12'hB00,
CSR_MCYCLEH = 12'hB80,
CSR_MINSTRET = 12'hB02,
CSR_MINSTRETH = 12'hB82,
CSR_MCOUNTER_SETUP_MASK = 12'b0011_001X_XXXX, // actually 12'h323 - 12'h33F
CSR_MCOUNTER_MASK = 12'b1011_000X_XXXX, // actually 12'hB03 - 12'hB1F
CSR_MCOUNTERH_MASK = 12'b1011_100X_XXXX // actually 12'hB83 - 12'hB9F
} csr_num_e;
endpackage

View file

@ -45,6 +45,7 @@ module ibex_id_stage #(
output logic ctrl_busy_o,
output logic core_ctrl_firstfetch_o,
output logic is_decoding_o,
output logic illegal_insn_o,
// Interface to IF stage
input logic instr_valid_i,
@ -406,6 +407,7 @@ module ibex_id_stage #(
////////////////
// Controller //
////////////////
assign illegal_insn_o = illegal_insn_dec | illegal_reg_rv32e;
ibex_controller controller_i (
.clk_i ( clk_i ),
@ -418,7 +420,7 @@ module ibex_id_stage #(
// decoder related signals
.deassert_we_o ( deassert_we ),
.illegal_insn_i ( illegal_insn_dec | illegal_reg_rv32e ),
.illegal_insn_i ( illegal_insn_o ),
.ecall_insn_i ( ecall_insn_dec ),
.mret_insn_i ( mret_insn_dec ),
.dret_insn_i ( dret_insn_dec ),