Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/01_overview/compliance.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ In addition, the following instruction set extensions are available.
- 2.0
- always enabled

* - **"Zicntr" and "Zihpm": Extensions for Counters and Performance Monitoring**
- 2.0
- always enabled

* - **Zifencei**: Instruction-Fetch Fence
- 2.0
- always enabled
Expand Down
20 changes: 18 additions & 2 deletions doc/03_reference/performance_counters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
Performance Counters
====================

Ibex implements performance counters according to the RISC-V Privileged Specification, version 1.11 (see Hardware Performance Monitor, Section 3.1.11).
Ibex implements performance counters according to the RISC-V Privileged Specification, version 1.11 (see Hardware Performance Monitor, Section 3.1.11) and supports the **Zihpm** (Hardware Performance Counters) extension.
The performance counters are placed inside the Control and Status Registers (CSRs) and can be accessed with the ``CSRRW(I)`` and ``CSRRS/C(I)`` instructions.

Ibex implements the clock cycle counter ``mcycle(h)``, the retired instruction counter ``minstret(h)``, as well as the 29 event counters ``mhpmcounter3(h)`` - ``mhpmcounter31(h)`` and the corresponding event selector CSRs ``mhpmevent3`` - ``mhpmevent31``, and the ``mcountinhibit`` CSR to individually enable/disable the counters.
Ibex implements the machine-mode clock cycle counter ``mcycle(h)``, the retired instruction counter ``minstret(h)``, as well as the 29 event counters ``mhpmcounter3(h)`` - ``mhpmcounter31(h)`` and the corresponding event selector CSRs ``mhpmevent3`` - ``mhpmevent31``, and the ``mcountinhibit`` CSR to individually enable/disable the counters.

Additionally, Ibex implements the Zicntr and Zihpm extensions which provide User-mode (U-mode) aliases for these performance counters: ``cycle(h)``, ``instret(h)``, and ``hpmcounter3(h)`` - ``hpmcounter31(h)``. These aliases provide read-only access to the exact same underlying hardware counters configured in M-mode.

``mcycle(h)`` and ``minstret(h)`` are always available and 64 bit wide.
The ``mhpmcounter`` performance counters are optional (unavailable by default) and parametrizable in width.

Expand Down Expand Up @@ -60,6 +63,19 @@ In particular, to enable/disable ``mcycle(h)``, bit 0 must be written. For ``min
The lower 32 bits of all counters can be accessed through the base register, whereas the upper 32 bits are accessed through the ``h``-register.
Reads to all these registers are non-destructive.

User-Mode Counter Access (mcounteren)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Access to the U-mode counter aliases (``cycle(h)``, ``instret(h)``, and ``hpmcounterX(h)``) is controlled via the Machine Counter-Enable CSR (``mcounteren``). This register can gate access to the counters from less privileged modes to prevent benchmarking the core if desired.

* **Bit 0** controls access to ``cycle(h)``.
* **Bit 2** controls access to ``instret(h)``.
* **Bit X** controls access to ``hpmcounterX(h)``.

When a bit in ``mcounteren`` is clear (0), any attempt to read the corresponding counter alias from U-mode will trigger an illegal instruction exception.

To secure this mechanism, the ``mcounteren`` register can be locked against software modifications using a MUBI input signal called ``mcounteren_writeable``. When this signal disables writes, any attempt by software to modify the contents of ``mcounteren`` is ignored.

Parametrization at synthesis time
---------------------------------

Expand Down
11 changes: 11 additions & 0 deletions dv/cosim/cosim.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ class Cosim {
// A full 64-bit value is provided setting both the mcycle and mcycleh CSRs.
virtual void set_mcycle(uint64_t mcycle) = 0;

// Set the value of minstret.
//
// The co-simulation model doesn't alter the value of minstret itself (other
// than instructions that do a direct CSR write). minstret should be set to
// the correct value before any `step` call that may execute an instruction
// that observes the value of minstret.
//
// A full 64-bit value is provided setting both the minstret and minstreth
// CSRs.
virtual void set_minstret(uint64_t minstret) = 0;

// Set the value of a CSR. This is used when it is needed to have direct
// communication between DUT and Spike (e.g. Performance counters).
virtual void set_csr(const int csr_num, const uint32_t new_val) = 0;
Expand Down
7 changes: 7 additions & 0 deletions dv/cosim/cosim_dpi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ void riscv_cosim_set_mcycle(Cosim *cosim, svBitVecVal *mcycle) {
cosim->set_mcycle(mcycle_full);
}

void riscv_cosim_set_minstret(Cosim *cosim, svBitVecVal *minstret) {
assert(cosim);

uint64_t minstret_full = minstret[0] | (uint64_t)minstret[1] << 32;
cosim->set_minstret(minstret_full);
}

void riscv_cosim_set_csr(Cosim *cosim, const int csr_id,
const svBitVecVal *csr_val) {
assert(cosim);
Expand Down
1 change: 1 addition & 0 deletions dv/cosim/cosim_dpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi);
void riscv_cosim_set_nmi_int(Cosim *cosim, svBit nmi_int);
void riscv_cosim_set_debug_req(Cosim *cosim, svBit debug_req);
void riscv_cosim_set_mcycle(Cosim *cosim, svBitVecVal *mcycle);
void riscv_cosim_set_minstret(Cosim *cosim, svBitVecVal *minstret);
void riscv_cosim_set_csr(Cosim *cosim, const int csr_id,
const svBitVecVal *csr_val);
void riscv_cosim_set_ic_scr_key_valid(Cosim *cosim, svBit valid);
Expand Down
1 change: 1 addition & 0 deletions dv/cosim/cosim_dpi.svh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import "DPI-C" function void riscv_cosim_set_nmi(chandle cosim_handle, bit nmi);
import "DPI-C" function void riscv_cosim_set_nmi_int(chandle cosim_handle, bit nmi_int);
import "DPI-C" function void riscv_cosim_set_debug_req(chandle cosim_handle, bit debug_req);
import "DPI-C" function void riscv_cosim_set_mcycle(chandle cosim_handle, bit [63:0] mcycle);
import "DPI-C" function void riscv_cosim_set_minstret(chandle cosim_handle, bit [63:0] minstret);
import "DPI-C" function void riscv_cosim_set_csr(chandle cosim_handle, int csr_id,
bit [31:0] csr_val);
import "DPI-C" function void riscv_cosim_set_ic_scr_key_valid(chandle cosim_handle, bit valid);
Expand Down
39 changes: 38 additions & 1 deletion dv/cosim/spike_cosim.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ SpikeCosim::SpikeCosim(const std::string &isa_string, uint32_t start_pc,
uint32_t pmp_num_regions, uint32_t pmp_granularity,
uint32_t mhpm_counter_num, uint32_t dm_start_addr,
uint32_t dm_end_addr)
: nmi_mode(false), pending_iside_error(false), insn_cnt(0) {
: nmi_mode(false),
pending_iside_error(false),
insn_cnt(0),
mhpm_counter_num(mhpm_counter_num) {
FILE *log_file = nullptr;
if (trace_log_path.length() != 0) {
log = std::make_unique<log_file_t>(trace_log_path.c_str());
Expand Down Expand Up @@ -747,6 +750,27 @@ void SpikeCosim::set_mcycle(uint64_t mcycle) {
// to write all 64 bits at once at least.
}

void SpikeCosim::set_minstret(uint64_t minstret) {
uint32_t upper_minstret = minstret >> 32;
uint32_t lower_minstret = minstret & 0xffffffff;

// Spike decrements the MINSTRET CSR when you write to it. This is the same
// issue as with MCYCLE. See `set_mcycle` for more details.

// Write the lower half first, incremented twice due to the double decrement
processor->get_state()->csrmap[CSR_MINSTRET]->write(lower_minstret + 2);

if ((processor->get_state()->csrmap[CSR_MINSTRET]->read() & 0xffffffff) ==
0) {
// If the lower half is 0 at this point then the upper half will get
// decremented, so increment it first.
upper_minstret++;
}

// Set the upper half
processor->get_state()->csrmap[CSR_MINSTRETH]->write(upper_minstret);
}

void SpikeCosim::set_csr(const int csr_num, const uint32_t new_val) {
// Note that this is tested with ibex-cosim-v0.3 version of Spike. 'set_csr'
// method might have a hardwired zero for mhpmcounterX registers.
Expand Down Expand Up @@ -832,6 +856,19 @@ void SpikeCosim::fixup_csr(int csr_num, uint32_t csr_val) {
processor->set_csr(csr_num, new_val);
#else
processor->put_csr(csr_num, new_val);
#endif
break;
}
case CSR_MCOUNTEREN: {
// Bits 3..3+mhpm_counter_num-1 correspond to implemented HPM counters
reg_t hpm_mask = ((1 << mhpm_counter_num) - 1) << 3;
// Bit 0 and 2 are for mcycle and minstret which are always implemented
// Bit 1 is for time which is not implemented, hence the mask 0x5
reg_t new_val = csr_val & (0x5 | hpm_mask);
#ifdef OLD_SPIKE
processor->set_csr(csr_num, new_val);
#else
processor->put_csr(csr_num, new_val);
#endif
break;
}
Expand Down
2 changes: 2 additions & 0 deletions dv/cosim/spike_cosim.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class SpikeCosim : public simif_t, public Cosim {
void misaligned_pmp_fixup();

unsigned int insn_cnt;
uint32_t mhpm_counter_num;

public:
SpikeCosim(const std::string &isa_string, uint32_t start_pc,
Expand Down Expand Up @@ -131,6 +132,7 @@ class SpikeCosim : public simif_t, public Cosim {
void set_nmi_int(bool nmi_int) override;
void set_debug_req(bool debug_req) override;
void set_mcycle(uint64_t mcycle) override;
void set_minstret(uint64_t minstret) override;
void set_csr(const int csr_num, const uint32_t new_val) override;
void set_ic_scr_key_valid(bool valid) override;
void notify_dside_access(const DSideAccessInfo &access_info) override;
Expand Down
10 changes: 9 additions & 1 deletion dv/formal/check/top.sv
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ module top import ibex_pkg::*; #(

// CPU Control Signals
input ibex_mubi_t fetch_enable_i,
input ibex_mubi_t mcounteren_writable_i,
output logic core_sleep_o,
output logic alert_minor_o,
output logic alert_major_internal_o,
Expand Down Expand Up @@ -162,7 +163,9 @@ NotDebug: assume property (!ibex_top_i.u_ibex_core.debug_mode & !debug_req_i);
ConstantBoot: assume property (boot_addr_i == $past(boot_addr_i));
// 3. Always fetch enable
FetchEnable: assume property (fetch_enable_i == IbexMuBiOn);
// 4. Never try to sleep if we couldn't ever wake up
// 4. Always have mcounteren writable
McounterenWritable: assume property (mcounteren_writable_i == IbexMuBiOn);
// 5. Never try to sleep if we couldn't ever wake up
WFIStart: assume property (`IDC.ctrl_fsm_cs == SLEEP |-> (
`CSR.mie_q.irq_software |
`CSR.mie_q.irq_timer |
Expand Down Expand Up @@ -200,6 +203,7 @@ WFIStart: assume property (`IDC.ctrl_fsm_cs == SLEEP |-> (
logic [31:0] `X(mscratch); \
logic [31:0] `X(mepc); \
logic [63:0] `X(mcycle); \
logic [63:0] `X(cycle); \
logic [31:0] `X(mshwmb); \
logic [31:0] `X(mshwm); \
logic [31:0] `X(mcounteren); \
Expand Down Expand Up @@ -441,13 +445,17 @@ logic ex_is_checkable_csr;
assign ex_is_checkable_csr = ~(
((CSR_MHPMCOUNTER3H <= `CSR_ADDR) && (`CSR_ADDR <= CSR_MHPMCOUNTER31H)) |
((CSR_MHPMCOUNTER3 <= `CSR_ADDR) && (`CSR_ADDR <= CSR_MHPMCOUNTER31)) |
((CSR_HPMCOUNTER3H <= `CSR_ADDR) && (`CSR_ADDR <= CSR_HPMCOUNTER31H)) |
((CSR_HPMCOUNTER3 <= `CSR_ADDR) && (`CSR_ADDR <= CSR_HPMCOUNTER31)) |
((CSR_MHPMEVENT3 <= `CSR_ADDR) && (`CSR_ADDR <= CSR_MHPMEVENT31)) |
(`CSR_ADDR == CSR_CPUCTRLSTS) | (`CSR_ADDR == CSR_SECURESEED) |
(`CSR_ADDR == CSR_MIE) |
(`CSR_ADDR == CSR_MCYCLE) | (`CSR_ADDR == CSR_MCYCLEH) |
(`CSR_ADDR == CSR_CYCLE) | (`CSR_ADDR == CSR_CYCLEH) |

// TODO:
(`CSR_ADDR == CSR_MINSTRET) | (`CSR_ADDR == CSR_MINSTRETH) |
(`CSR_ADDR == CSR_INSTRET) | (`CSR_ADDR == CSR_INSTRETH) |
(`CSR_ADDR == CSR_MCOUNTINHIBIT)
);

Expand Down
1 change: 1 addition & 0 deletions dv/riscv_compliance/rtl/ibex_riscv_compliance.sv
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ module ibex_riscv_compliance (
.double_fault_seen_o ( ),

.fetch_enable_i (ibex_pkg::IbexMuBiOn ),
.mcounteren_writable_i (ibex_pkg::IbexMuBiOn ),
.alert_minor_o ( ),
.alert_major_internal_o ( ),
.alert_major_bus_o ( ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
riscv_cosim_set_nmi_int(cosim_handle, rvfi_instr.nmi_int);
riscv_cosim_set_mip(cosim_handle, rvfi_instr.pre_mip, rvfi_instr.post_mip);
riscv_cosim_set_mcycle(cosim_handle, rvfi_instr.mcycle);
riscv_cosim_set_minstret(cosim_handle, rvfi_instr.minstret);

// Set performance counters through a pseudo-backdoor write
for (int i=0; i < 10; i++) begin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ibex_rvfi_monitor extends uvm_monitor;
trans_collected.debug_req = vif.monitor_cb.ext_debug_req;
trans_collected.rf_wr_suppress = vif.monitor_cb.ext_rf_wr_suppress;
trans_collected.mcycle = vif.monitor_cb.ext_mcycle;
trans_collected.minstret = vif.monitor_cb.ext_minstret;
trans_collected.ic_scr_key_valid = vif.monitor_cb.ext_ic_scr_key_valid;

for (int i=0; i < 10; i++) begin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ibex_rvfi_seq_item extends uvm_sequence_item;
bit debug_req;
bit rf_wr_suppress;
bit [63:0] mcycle;
bit [63:0] minstret;

bit [31:0] mhpmcounters [10];
bit [31:0] mhpmcountersh [10];
Expand All @@ -34,6 +35,7 @@ class ibex_rvfi_seq_item extends uvm_sequence_item;
`uvm_field_int (debug_req, UVM_DEFAULT)
`uvm_field_int (rf_wr_suppress, UVM_DEFAULT)
`uvm_field_int (mcycle, UVM_DEFAULT)
`uvm_field_int (minstret, UVM_DEFAULT)
`uvm_field_sarray_int (mhpmcounters, UVM_DEFAULT)
`uvm_field_sarray_int (mhpmcountersh, UVM_DEFAULT)
`uvm_field_int (ic_scr_key_valid, UVM_DEFAULT)
Expand Down
Loading
Loading