Update lowrisc_ibex to lowRISC/ibex@f025236
Update code from upstream repository
https://github.com/lowRISC/ibex.git to revision
f025236a22e4d2290acf856de60449f24d79bc6f
* [I-side] - Fix issues found in tracing example (Tom Roberts)
* Run clang-format on all source files (Philipp Wagner)
* Add lowRISC standard clang-format file (Philipp Wagner)
* Controller: fix `nmi_mode` default assignment (Pirmin Vogel)
* Update slave_driver grant timing to pass Ibex assertion checks
(lowRISC/ibex#295) (udinator)
* Controller: Fix exception cause ID of fast interrupts (Pirmin Vogel)
* [I-side] - Fix assertion error (Tom Roberts)
* Instruction set extensions M and C may be swapped (pbing)
* Fix syntax error (Udi)
* Adding Compressed Instruction support in tracer (Rahul Behl)
* CSRs: reset `dcsr.XDEBUGVER` to `XDEBUGVER_STD` instead of 0 (Pirmin
Vogel)
* [RTL] Fix I-side timing loop (Tom Roberts)
* Add core_sleep_o to ibex interface (Greg Chadwick)
* [RTL] - Remove timing loop in LSU (Tom Roberts)
* Added dret and ebreak tests (lowRISC/ibex#281) (udinator)
diff --git a/hw/vendor/lowrisc_ibex.lock.hjson b/hw/vendor/lowrisc_ibex.lock.hjson
index 37908b0..acd95e5 100644
--- a/hw/vendor/lowrisc_ibex.lock.hjson
+++ b/hw/vendor/lowrisc_ibex.lock.hjson
@@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/ibex.git
- rev: d14312c3cc47a7d4b34943e4febb96312353b2cc
+ rev: f025236a22e4d2290acf856de60449f24d79bc6f
}
}
diff --git a/hw/vendor/lowrisc_ibex/.clang-format b/hw/vendor/lowrisc_ibex/.clang-format
new file mode 100644
index 0000000..a2c542c
--- /dev/null
+++ b/hw/vendor/lowrisc_ibex/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+DerivePointerAlignment: false
+PointerAlignment: Right
diff --git a/hw/vendor/lowrisc_ibex/doc/integration.rst b/hw/vendor/lowrisc_ibex/doc/integration.rst
index b737da3..309083b 100644
--- a/hw/vendor/lowrisc_ibex/doc/integration.rst
+++ b/hw/vendor/lowrisc_ibex/doc/integration.rst
@@ -60,8 +60,9 @@
// Debug interface
.debug_req_i (),
- // Special control signal
- .fetch_enable_i ()
+ // Special control signals
+ .fetch_enable_i (),
+ .core_sleep_o ()
);
Parameters
@@ -118,3 +119,8 @@
+-------------------------+-------------------------+-----+----------------------------------------+
| ``fetch_enable_i`` | 1 | in | Enable the core, won't fetch when 0 |
+-------------------------+-------------------------+-----+----------------------------------------+
+| ``core_sleep_o`` | 1 | out | Core in WFI with no outstanding data |
+| | | | or instruction accesses. Deasserts |
+| | | | if an external event (interrupt or |
+| | | | debug req) wakes the core up |
++-------------------------+-------------------------+-----+----------------------------------------+
diff --git a/hw/vendor/lowrisc_ibex/doc/introduction.rst b/hw/vendor/lowrisc_ibex/doc/introduction.rst
index 3744422..b21235d 100644
--- a/hw/vendor/lowrisc_ibex/doc/introduction.rst
+++ b/hw/vendor/lowrisc_ibex/doc/introduction.rst
@@ -38,11 +38,11 @@
- Version
- Configurability
- * - **M**: Standard Extension for Compressed Instructions
+ * - **C**: Standard Extension for Compressed Instructions
- 2.0
- always enabled
- * - **C**: Standard Extension for Integer Multiplication and Division
+ * - **M**: Standard Extension for Integer Multiplication and Division
- 2.0
- optional
diff --git a/hw/vendor/lowrisc_ibex/doc/load_store_unit.rst b/hw/vendor/lowrisc_ibex/doc/load_store_unit.rst
index 8a7d59f..cedfe61 100644
--- a/hw/vendor/lowrisc_ibex/doc/load_store_unit.rst
+++ b/hw/vendor/lowrisc_ibex/doc/load_store_unit.rst
@@ -48,6 +48,10 @@
However, it does so by performing two separate word-aligned accesses.
This means that at least two cycles are needed for misaligned loads and stores.
+If an error response is received for the first transaction, the second transaction will still be issued.
+The second transaction will then follow the normal bus protocol, but its response/data will be ignored.
+If a new load/store request is received while waiting for an abandoned second part to complete, it will not be serviced until the state machine returns to IDLE.
+
.. _lsu-protocol:
Protocol
@@ -61,6 +65,8 @@
3. The memory answers with a ``data_rvalid_i`` set high for exactly one cycle to signal the response from the bus or the memory using ``data_err_i`` and ``data_rdata_i`` (during the very same cycle). This may happen one or more cycles after the grant has been received. If ``data_err_i`` is low, the request could successfully be handled at the destination and in the case of a load, ``data_rdata_i`` contains valid data. If ``data_err_i`` is high, an error occurred in the memory system and the core will raise an exception.
+4. When multiple granted requests are outstanding, it is assumed that the memory requests will be kept in-order and one ``data_rvalid_i`` will be signalled for each of them, in the order they were issued.
+
:numref:`timing1`, :numref:`timing2` and :numref:`timing3` show example-timing diagrams of the protocol.
.. wavedrom::
diff --git a/hw/vendor/lowrisc_ibex/dv/riscv_compliance/ibex_riscv_compliance.cc b/hw/vendor/lowrisc_ibex/dv/riscv_compliance/ibex_riscv_compliance.cc
index c695715..7a02524 100644
--- a/hw/vendor/lowrisc_ibex/dv/riscv_compliance/ibex_riscv_compliance.cc
+++ b/hw/vendor/lowrisc_ibex/dv/riscv_compliance/ibex_riscv_compliance.cc
@@ -12,8 +12,8 @@
VERILATED_TOPLEVEL(ibex_riscv_compliance)
-ibex_riscv_compliance* top;
-VerilatorSimCtrl* simctrl;
+ibex_riscv_compliance *top;
+VerilatorSimCtrl *simctrl;
static void SignalHandler(int sig) {
if (!simctrl) {
@@ -52,7 +52,7 @@
*/
double sc_time_stamp() { return simctrl->GetTime(); }
-int main(int argc, char** argv) {
+int main(int argc, char **argv) {
int retcode;
top = new ibex_riscv_compliance;
simctrl = new VerilatorSimCtrl(top, top->IO_CLK, top->IO_RST_N,
diff --git a/hw/vendor/lowrisc_ibex/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv b/hw/vendor/lowrisc_ibex/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv
index 58d1ec8..c55ec67 100644
--- a/hw/vendor/lowrisc_ibex/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv
+++ b/hw/vendor/lowrisc_ibex/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv
@@ -139,7 +139,8 @@
.debug_req_i ('b0),
- .fetch_enable_i ('b1)
+ .fetch_enable_i ('b1),
+ .core_sleep_o ()
);
// SRAM block for instruction and data storage
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv b/hw/vendor/lowrisc_ibex/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv
index e57803d..2a48b9d 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv
@@ -10,9 +10,6 @@
protected virtual ibex_mem_intf vif;
- int unsigned min_grant_delay = 0;
- int unsigned max_grant_delay = 10;
-
`uvm_component_utils(ibex_mem_intf_slave_driver)
`uvm_component_new
@@ -58,20 +55,15 @@
join_none
endtask : get_and_drive
+ // TODO(udinator) - this direct send_grant logic is temporary until instruction fetch protocol
+ // issue is clarified (https://github.com/lowRISC/ibex/pull/293). After resolution, will re-add
+ // random delays insertion before driving grant to the ibex core.
virtual protected task send_grant();
int gnt_delay;
forever begin
while(vif.request !== 1'b1) begin
@(negedge vif.clock);
end
- std::randomize(gnt_delay) with {
- gnt_delay dist {
- min_grant_delay :/ 1,
- [min_grant_delay+1 : max_grant_delay-1] :/ 1,
- max_grant_delay :/ 1
- };
- };
- repeat(gnt_delay) @(negedge vif.clock);
if(~vif.reset) begin
vif.grant = 1'b1;
@(negedge vif.clock);
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py b/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py
index 5377300..d6c7fac 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py
@@ -81,9 +81,9 @@
fd = sys.stdout
fd.write("%s uvm log : %s\n" % (core_name, uvm_log))
if pass_cnt == 1:
- fd.write("%s : PASSED\n" % test_name)
+ fd.write("%s : [PASSED]\n\n" % test_name)
elif fail_cnt == 1:
- fd.write("%s : FAILED\n" % test_name)
+ fd.write("%s : [FAILED]\n\n" % test_name)
if report:
fd.close()
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/testlist.yaml b/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/testlist.yaml
index 7d9c2a1..d21b79e 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/testlist.yaml
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/riscv_dv_extension/testlist.yaml
@@ -116,7 +116,7 @@
+no_csr_instr=1
+no_fence=1
+num_of_sub_program=0
- rtl_test: core_ibex_debug_intr_test
+ rtl_test: core_ibex_debug_intr_basic_test
sim_opts: >
+require_signature_addr=1
+max_interval=1000
@@ -135,14 +135,13 @@
+instr_cnt=6000
+no_csr_instr=1
+no_fence=1
- rtl_test: core_ibex_debug_intr_test
+ rtl_test: core_ibex_debug_intr_basic_test
iterations: 5
sim_opts: >
+max_interval=250
+enable_debug_stress_seq=1
+require_signature_addr=1
compare_opts:
- compare_final_value_only: 1
verbose: 1
- test: riscv_debug_branch_jump_test
@@ -158,11 +157,12 @@
+instr_cnt=6000
+no_csr_instr=1
+no_fence=1
- +num_of_sub_program=5
- +num_debug_sub_program=5
- rtl_test: core_ibex_debug_intr_test
+ +num_of_sub_program=0
+ +num_debug_sub_program=3
+ rtl_test: core_ibex_debug_intr_basic_test
sim_opts: >
+require_signature_addr=1
+ +max_interval=2000
+enable_debug_stress_seq=1
compare_opts:
compare_final_value_only: 1
@@ -189,6 +189,75 @@
compare_final_value_only: 1
verbose: 1
+- test: riscv_dret_test
+ description: >
+ Dret instructions will be inserted into M-mode code, ibex should treat these
+ like illegal instructions.
+ iterations: 5
+ gen_test: riscv_rand_instr_test
+ gen_opts: >
+ +require_signature_addr=1
+ +no_dret=0
+ +instr_cnt=6000
+ rtl_test: core_ibex_dret_test
+ sim_opts: >
+ +require_signature_addr=1
+
+- test: riscv_ebreak_test
+ description: >
+ Ebreak instructions will be inserted into the M mode code, ibex should handle them normally.
+ iterations: 5
+ gen_test: riscv_rand_instr_test
+ gen_opts: >
+ +no_ebreak=0
+ +instr_cnt=6000
+ rtl_test: core_ibex_base_test
+
+- test: riscv_debug_ebreak_test
+ description: >
+ A directed ebreak sequence will be inserted into the debug rom, upon encountering it,
+ ibex should jump back to the beginning of debug mode. The sequence is designed to avoid an
+ infinite loop.
+ iterations: 5
+ gen_test: riscv_rand_instr_test
+ gen_opts: >
+ +require_signature_addr=1
+ +gen_debug_section=1
+ +enable_ebreak_in_debug_rom=1
+ +no_csr_instr=1
+ +no_fence=1
+ +no_wfi=1
+ +no_ebreak=0
+ +instr_cnt=6000
+ rtl_test: core_ibex_debug_ebreak_test
+ sim_opts: >
+ +require_signature_addr=1
+ +enable_debug_single_seq=1
+ compare_opts:
+ compare_final_value_only: 1
+
+- test: riscv_debug_ebreakm_test
+ description: >
+ dcsr.ebreakm will be set at the beginning of the test upon the first entry into the debug rom.
+ From then on, every ebreak instruction should cause debug mode to be entered.
+ iterations: 5
+ gen_test: riscv_rand_instr_test
+ gen_opts: >
+ +require_signature_addr=1
+ +gen_debug_section=1
+ +set_dcsr_ebreak=1
+ +no_ebreak=0
+ +no_csr_instr=1
+ +no_fence=1
+ +no_wfi=1
+ +instr_cnt=6000
+ rtl_test: core_ibex_debug_ebreakm_test
+ sim_opts: >
+ +require_signature_addr=1
+ +enable_debug_single_seq=1
+ compare_opts:
+ compare_final_value_only: 1
+
- test: riscv_interrupt_test
description: >
Random instruction test with complete interrupt handling
@@ -196,7 +265,7 @@
gen_test: riscv_rand_instr_test
gen_opts: >
+require_signature_addr=1
- rtl_test: core_ibex_debug_intr_test
+ rtl_test: core_ibex_debug_intr_basic_test
sim_opts: >
+require_signature_addr=1
+enable_irq_stress_seq=1
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/sim.py b/hw/vendor/lowrisc_ibex/dv/uvm/sim.py
index 44558c5..02c501b 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/sim.py
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/sim.py
@@ -166,8 +166,8 @@
print("Comparing %s/DUT sim result : %s" % (iss, elf))
run_cmd(("echo 'Test binary: %s' >> %s" % (elf, report)))
uvm_log = ("%s/rtl_sim/%s.%d/sim.log" % (output_dir, test['test'], i))
- rtl_log = ("%s/rtl_sim/%s.%d/trace_core_00_0.log" % (output_dir, test['test'], i))
- rtl_csv = ("%s/rtl_sim/%s.%d/trace_core_00_0.csv" % (output_dir, test['test'], i))
+ rtl_log = ("%s/rtl_sim/%s.%d/trace_core_00000000.log" % (output_dir, test['test'], i))
+ rtl_csv = ("%s/rtl_sim/%s.%d/trace_core_00000000.csv" % (output_dir, test['test'], i))
test_name = "%s.%d" % (test['test'], i)
if 'no_post_compare' in test and test['no_post_compare'] == 1:
check_ibex_uvm_log(uvm_log, "ibex", test_name, report)
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_base_test.sv b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_base_test.sv
index 75d5d58..54cd279 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_base_test.sv
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_base_test.sv
@@ -12,12 +12,12 @@
core_ibex_vseq vseq;
irq_seq irq_seq_h;
int unsigned timeout_in_cycles = 3000000;
- // If no signature_addr handshake functionality is desired between the
- // testbench and the generated code, the test will wait for the specified
- // number of cycles before starting stimulus sequences (irq and debug)
+ // If no signature_addr handshake functionality is desired between the testbench and the generated
+ // code, the test will wait for the specifield number of cycles before starting stimulus
+ // sequences (irq and debug)
int unsigned stimulus_delay = 800;
- bit[ibex_mem_intf_agent_pkg::DATA_WIDTH] signature_data_q[$];
- bit[ibex_mem_intf_agent_pkg::DATA_WIDTH] signature_data;
+ bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data_q[$];
+ bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data;
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) item_collected_port;
`uvm_component_utils(core_ibex_base_test)
@@ -120,23 +120,20 @@
input signature_type_t ref_type);
ibex_mem_intf_seq_item mem_txn;
forever begin
- // The first write to this address is guaranteed to contain the
- // signature type in bits [7:0]
+ // The first write to this address is guaranteed to contain the signature type in bits [7:0]
item_collected_port.get(mem_txn);
if (mem_txn.addr == ref_addr && mem_txn.data[7:0] === ref_type && mem_txn.read_write == WRITE) begin
signature_data = mem_txn.data;
case (ref_type)
- // The very first write to the signature address in every test is
- // guaranteed to be a write of CORE_STATUS, indicating the
- // INITIALIZED state
+ // The very first write to the signature address in every test is guaranteed to be a write
+ // of CORE_STATUS, indicating the INITIALIZED state
CORE_STATUS: begin
signature_data_q.push_back(signature_data >> 8);
end
TEST_RESULT: begin
signature_data_q.push_back(signature_data >> 8);
end
- // The next 32 writes to the address are guaranteed to be a dump of
- // all GPRs
+ // The next 32 writes to the address are guaranteed to be a dump of all GPRs
WRITE_GPR: begin
for(int i = 0; i < 32; i++) begin
do begin
@@ -145,8 +142,7 @@
signature_data_q.push_back(mem_txn.data);
end
end
- // The next write to this address is guaranteed to be the data held
- // in the CSR
+ // The next write to this address is guaranteed to be the data held in the CSR
WRITE_CSR: begin
signature_data_q.push_back(signature_data >> 8);
do begin
@@ -163,10 +159,12 @@
end
endtask
- // API of various tasks wrapping wait_for_mem_txn, for various common
- // functionalities that might be needed for verification purposes.
+ // API of various tasks wrapping wait_for_mem_txn, for various common functionalities that
+ // might be needed for verification purposes.
// Will be expanded as needed.
+ // Gets the next CORE_STATUS signature write and compares it against the provided core_status
+ // type, throws uvm_error on mismatch
virtual task check_next_core_status(core_status_t core_status, error_msg="");
wait_for_mem_txn(cfg.signature_addr, CORE_STATUS);
signature_data = signature_data_q.pop_front();
@@ -175,6 +173,17 @@
end
endtask
+ // Waits for a write to the address of the specified CSR and retrieves the csr data
+ virtual task wait_for_csr_write(csr_num_e csr);
+ bit [11:0] csr_addr;
+ do begin
+ wait_for_mem_txn(cfg.signature_addr, WRITE_CSR);
+ csr_addr = signature_data_q.pop_front();
+ signature_data = signature_data_q.pop_front();
+ end while (csr_addr != csr);
+ endtask
+
+ // Waits until the next time the given core_status is written to the signature address
virtual task wait_for_core_status(core_status_t core_status);
do begin
wait_for_mem_txn(cfg.signature_addr, CORE_STATUS);
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_lib.sv b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_lib.sv
index 34bd049..c306e5b 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_lib.sv
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_lib.sv
@@ -32,9 +32,9 @@
endclass
// Debug test class
-class core_ibex_debug_intr_test extends core_ibex_base_test;
+class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
- `uvm_component_utils(core_ibex_debug_intr_test)
+ `uvm_component_utils(core_ibex_debug_intr_basic_test)
`uvm_component_new
virtual task send_stimulus();
@@ -46,8 +46,8 @@
if (cfg.require_signature_addr) begin
wait_for_core_status(INITIALIZED);
end else begin
- // If no signature_addr functionality is desired, then the test will
- // simply wait for an adequate number of cycles
+ // If no signature_addr functionality is desired, then the test will simply wait for an
+ // adequate number of cycles
clk_vif.wait_clks(stimulus_delay);
end
fork
@@ -68,10 +68,10 @@
endclass
-// Debug WFI test class
-class core_ibex_debug_wfi_test extends core_ibex_base_test;
+// Base class for directed debug and irq test scenarios
+class core_ibex_directed_test extends core_ibex_base_test;
- `uvm_component_utils(core_ibex_debug_wfi_test)
+ `uvm_component_utils(core_ibex_directed_test)
`uvm_component_new
virtual task send_stimulus();
@@ -95,30 +95,163 @@
end
join_none
end else begin
- // Wait for core initialization before starting the wfi stimulus
- // loop - first write to signature address is guaranteed to be core
- // initialization info
+ // Wait for core initialization before starting the stimulus check loop - first write
+ // to signature address is guaranteed to be core initialization info
check_next_core_status(INITIALIZED, "Core initialization handshake failure");
- // TODO(udi) - need to check that no other instruction fetches occur
- // after the WFI is detected, and before any stimulus is sent to the
- // core
- forever begin
- wait (dut_vif.wfi === 1'b1);
- clk_vif.wait_clks($urandom_range(100));
- vseq.start_debug_single_seq();
- // After assserting this signal, core should wake up and jump into
- // debug mode from WFI state - next handshake should
- // be a notification that the core is now in debug mode
- check_next_core_status(IN_DEBUG_MODE, "Core did not jump into debug mode from WFI state");
- // We don't want to trigger debug stimulus for any WFI
- // instructions encountered inside the debug rom - those should
- // act as NOP instructions - so we wait until hitting the end of
- // the debug rom
- wait (dut_vif.dret === 1'b1);
- end
+ // Should be extended by derived classes.
+ // DO NOT use this test class directly.
+ check_stimulus();
end
end
join_none
endtask
+ virtual task check_stimulus();
+ `uvm_fatal(`gfn, "Base class task should not be used")
+ endtask
+
+ //------------------------------------------------------
+ // Checker functions/tasks that might be commonly used
+ //------------------------------------------------------
+
+ // compares dcsr.ebreak against the privilege mode encoded in dcsr.prv
+ virtual function check_dcsr_ebreak();
+ // dcsr.prv is the bottom two bits.
+ case (signature_data[1:0])
+ 2'b11: begin
+ `DV_CHECK_EQ_FATAL(signature_data[15], 1'b1, "dcsr.ebreakm is not set")
+ end
+ 2'b01: begin
+ `DV_CHECK_EQ_FATAL(signature_data[13], 1'b1, "dcsr.ebreaks is not set")
+ end
+ 2'b00: begin
+ `DV_CHECK_EQ_FATAL(signature_data[12], 1'b1, "dcsr.ebreaku is not set")
+ end
+ default: begin
+ `uvm_fatal(`gfn, "dcsr.prv is an unsupported privilege mode")
+ end
+ endcase
+ endfunction
+
+ virtual function check_dcsr_cause(dbg_cause_e cause);
+ `DV_CHECK_EQ_FATAL(cause, signature_data[8:6], "dcsr.cause has been incorrectly updated")
+ endfunction
+
+endclass
+
+// Debug WFI test class
+class core_ibex_debug_wfi_test extends core_ibex_directed_test;
+
+ `uvm_component_utils(core_ibex_debug_wfi_test)
+ `uvm_component_new
+
+ virtual task check_stimulus();
+ // TODO(udi) - need to check that no other instruction fetches occur after after the WFI
+ // is detected, and before any stimulus is sent to the core
+ forever begin
+ wait (dut_vif.wfi === 1'b1);
+ clk_vif.wait_clks($urandom_range(100));
+ vseq.start_debug_single_seq();
+ // After assserting this signal, core should wake up and jump into debug mode from WFI state
+ // - next handshake should be a notification that the core is now in debug mode
+ check_next_core_status(IN_DEBUG_MODE, "Core did not jump into debug mode from WFI state");
+ // We don't want to trigger debug stimulus for any WFI instructions encountered inside the
+ // debug rom - those should act as NOP instructions - so we wait until hitting the end of the
+ // debug rom.
+ // We also want to check that dcsr.cause has been set correctly
+ wait_for_csr_write(CSR_DCSR);
+ check_dcsr_cause(DBG_CAUSE_HALTREQ);
+ wait (dut_vif.dret === 1'b1);
+ end
+ endtask
+
+endclass
+
+// DRET test class
+class core_ibex_dret_test extends core_ibex_directed_test;
+
+ `uvm_component_utils(core_ibex_dret_test)
+ `uvm_component_new
+
+ virtual task check_stimulus();
+ forever begin
+ wait (dut_vif.dret === 1'b1);
+ // After hitting a dret, the core will jump to the vectored trap handler, which sends a
+ // handshake write to the bench
+ check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to vectored exception handler");
+ // The core will receive an illegal instruction handshake after jumping from the vectored trap
+ // handler to the illegal instruction exception handler
+ check_next_core_status(ILLEGAL_INSTR_EXCEPTION, "Core did not treat dret like illegal instruction");
+ end
+ endtask
+
+endclass
+
+// Normal debug ebreak test class
+class core_ibex_debug_ebreak_test extends core_ibex_directed_test;
+
+ `uvm_component_utils(core_ibex_debug_ebreak_test)
+ `uvm_component_new
+
+ bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] dpc;
+ bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] dcsr;
+
+ virtual task check_stimulus();
+ forever begin
+ wait (dut_vif.ebreak === 1'b1);
+ check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to exception handler");
+ check_next_core_status(EBREAK_EXCEPTION, "Core did not jump from exception handler to ebreak handler");
+ wait (dut_vif.mret === 1'b1);
+ // Want to wait until after the ebreak handler has finished to send debug stimulus, to avoid
+ // nested trap scenarios
+ clk_vif.wait_clks($urandom_range(5, 11));
+ vseq.start_debug_single_seq();
+ // capture the first write of dcsr
+ wait_for_csr_write(CSR_DCSR);
+ dcsr = signature_data;
+ // We also want to check that dcsr.cause has been set correctly
+ check_dcsr_cause(DBG_CAUSE_HALTREQ);
+ // capture the first write of dpc
+ wait_for_csr_write(CSR_DPC);
+ dpc = signature_data;
+ check_next_core_status(IN_DEBUG_MODE, "Core did not properly jump into debug mode");
+ wait (dut_vif.ebreak === 1'b1);
+ // compare the second writes of dcsr and dpc against the captured values
+ wait_for_csr_write(CSR_DCSR);
+ `DV_CHECK_EQ_FATAL(dcsr, signature_data, "ebreak inside the debug rom has changed the value of DCSR")
+ wait_for_csr_write(CSR_DPC);
+ `DV_CHECK_EQ_FATAL(dpc, signature_data, "ebreak inside the debug rom has changed the value of DPC")
+ wait (dut_vif.dret === 1'b1);
+ end
+ endtask
+
+endclass
+
+// Debug ebreak test with dcsr.ebreak(m/s/u) set
+class core_ibex_debug_ebreakm_test extends core_ibex_directed_test;
+
+ `uvm_component_utils(core_ibex_debug_ebreakm_test)
+ `uvm_component_new
+
+ virtual task check_stimulus();
+ // send a single debug request after core initialization to configure dcsr
+ vseq.start_debug_single_seq();
+ check_next_core_status(IN_DEBUG_MODE, "Core did not enter debug mode after debug_req stimulus");
+ // Read dcsr and verify the appropriate ebreak(m/s/u) bit has been set based on the prv field,
+ // as well as the cause field
+ wait_for_csr_write(CSR_DCSR);
+ check_dcsr_ebreak();
+ check_dcsr_cause(DBG_CAUSE_HALTREQ);
+ wait (dut_vif.dret === 1'b1);
+ forever begin
+ wait (dut_vif.ebreak === 1'b1);
+ check_next_core_status(IN_DEBUG_MODE, "Core did not enter debug mode after execution of ebreak");
+ // Read dcsr and verify the appropriate ebreak(m/s/u) bit has been set based on the prv field
+ wait_for_csr_write(CSR_DCSR);
+ check_dcsr_ebreak();
+ check_dcsr_cause(DBG_CAUSE_EBREAK);
+ wait (dut_vif.dret === 1'b1);
+ end
+ endtask
+
endclass
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_pkg.sv b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_pkg.sv
index b6a8d4f..a7b9803 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_pkg.sv
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_test_pkg.sv
@@ -12,6 +12,7 @@
import ibex_mem_intf_agent_pkg::*;
import irq_agent_pkg::*;
import riscv_signature_pkg::*;
+ import ibex_pkg::*;
`include "core_ibex_report_server.sv"
`include "core_ibex_seq_lib.sv"
diff --git a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_vseq.sv b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_vseq.sv
index 9d22c70..53ccc75 100644
--- a/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_vseq.sv
+++ b/hw/vendor/lowrisc_ibex/dv/uvm/tests/core_ibex_vseq.sv
@@ -72,8 +72,8 @@
end
endtask
- // Helper tasks to allow the test fine grained control to start sequences
- // through the vseq - necessary for testing directed stimulus scenarios
+ // Helper tasks to allow the test fine grained control to start sequences through the vseq
+ // - necessary for testing directed stimulus scenarios
virtual task start_debug_stress_seq();
debug_seq_stress_h.start(null);
endtask
diff --git a/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilated_toplevel.h b/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilated_toplevel.h
index e40d23b..8b13671 100644
--- a/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilated_toplevel.h
+++ b/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilated_toplevel.h
@@ -31,35 +31,25 @@
*/
class VerilatedTracer {
public:
- VerilatedTracer() : impl_(nullptr) {
- impl_ = new VM_TRACE_CLASS_NAME();
- };
+ VerilatedTracer() : impl_(nullptr) { impl_ = new VM_TRACE_CLASS_NAME(); };
- ~VerilatedTracer() {
- delete impl_;
+ ~VerilatedTracer() { delete impl_; }
+
+ bool isOpen() const { return impl_->isOpen(); };
+
+ void open(const char *filename) { impl_->open(filename); };
+
+ void close() { impl_->close(); };
+
+ void dump(vluint64_t timeui) { impl_->dump(timeui); }
+
+ operator VM_TRACE_CLASS_NAME *() const {
+ assert(impl_);
+ return impl_;
}
-
- bool isOpen() const {
- return impl_->isOpen();
- };
-
- void open(const char* filename) {
- impl_->open(filename);
- };
-
- void close() {
- impl_->close();
- };
-
- void dump(vluint64_t timeui) {
- impl_->dump(timeui);
- }
-
- operator VM_TRACE_CLASS_NAME*() const { assert(impl_); return impl_; }
-
private:
- VM_TRACE_CLASS_NAME* impl_;
+ VM_TRACE_CLASS_NAME *impl_;
};
#else
/**
@@ -67,14 +57,14 @@
*/
class VerilatedTracer {
public:
- VerilatedTracer() {};
+ VerilatedTracer(){};
~VerilatedTracer() {}
bool isOpen() const { return false; };
- void open(const char* filename) {};
- void close() {};
+ void open(const char *filename){};
+ void close(){};
void dump(vluint64_t timeui) {}
};
-#endif // VM_TRACE == 1
+#endif // VM_TRACE == 1
/**
* Pure abstract class (interface) for verilated toplevel modules
@@ -97,37 +87,37 @@
* of the tracer-specific class.
*/
class VerilatedToplevel {
-public:
- VerilatedToplevel() {};
- virtual ~VerilatedToplevel() {};
+ public:
+ VerilatedToplevel(){};
+ virtual ~VerilatedToplevel(){};
virtual void eval() = 0;
virtual void final() = 0;
- virtual const char* name() const = 0;
- virtual void trace(VerilatedTracer& tfp, int levels, int options) = 0;
+ virtual const char *name() const = 0;
+ virtual void trace(VerilatedTracer &tfp, int levels, int options) = 0;
};
#define STR(s) #s
#if VM_TRACE == 1
-#define VERILATED_TOPLEVEL_TRACE_CALL(topname) \
- V##topname::trace(static_cast<VM_TRACE_CLASS_NAME*>(tfp), levels, options);
+# define VERILATED_TOPLEVEL_TRACE_CALL(topname) \
+ V##topname::trace(static_cast<VM_TRACE_CLASS_NAME *>(tfp), levels, options);
#else
-#define VERILATED_TOPLEVEL_TRACE_CALL(topname) \
- assert(0 && "Tracing not enabled.");
+# define VERILATED_TOPLEVEL_TRACE_CALL(topname) \
+ assert(0 && "Tracing not enabled.");
#endif
-
-#define VERILATED_TOPLEVEL(topname) \
- class topname : public V##topname, public VerilatedToplevel { \
- public: \
- topname(const char* name="TOP") : V##topname(name), VerilatedToplevel() {} \
- const char* name() const { return STR(topname); } \
- void eval() { V##topname::eval(); } \
- void final() { V##topname::final(); } \
- void trace(VerilatedTracer& tfp, int levels, int options=0) { \
- VERILATED_TOPLEVEL_TRACE_CALL(topname) \
- } \
+#define VERILATED_TOPLEVEL(topname) \
+ class topname : public V##topname, public VerilatedToplevel { \
+ public: \
+ topname(const char *name = "TOP") \
+ : V##topname(name), VerilatedToplevel() {} \
+ const char *name() const { return STR(topname); } \
+ void eval() { V##topname::eval(); } \
+ void final() { V##topname::final(); } \
+ void trace(VerilatedTracer &tfp, int levels, int options = 0) { \
+ VERILATED_TOPLEVEL_TRACE_CALL(topname) \
+ } \
};
-#endif // VERILATED_TOPLEVEL_H_
+#endif // VERILATED_TOPLEVEL_H_
diff --git a/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc b/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc
index 95fa841..d9aae69 100644
--- a/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc
+++ b/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc
@@ -18,11 +18,11 @@
// DPI Exports
extern "C" {
-extern void simutil_verilator_memload(const char* file);
+extern void simutil_verilator_memload(const char *file);
}
-VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel* top, CData& sig_clk,
- CData& sig_rst, VerilatorSimCtrlFlags flags)
+VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel *top, CData &sig_clk,
+ CData &sig_rst, VerilatorSimCtrlFlags flags)
: top_(top),
sig_clk_(sig_clk),
sig_rst_(sig_rst),
@@ -143,15 +143,15 @@
<< std::endl;
}
-bool VerilatorSimCtrl::ParseCommandArgs(int argc, char** argv, int& retcode) {
+bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, int &retcode) {
const struct option long_options[] = {
- {"rominit", required_argument, nullptr, 'r'},
- {"raminit", required_argument, nullptr, 'm'},
+ {"rominit", required_argument, nullptr, 'r'},
+ {"raminit", required_argument, nullptr, 'm'},
{"flashinit", required_argument, nullptr, 'f'},
{"term-after-cycles", required_argument, nullptr, 'c'},
- {"trace", no_argument, nullptr, 't'},
- {"help", no_argument, nullptr, 'h'},
- {nullptr, no_argument, nullptr, 0}};
+ {"trace", no_argument, nullptr, 't'},
+ {"help", no_argument, nullptr, 'h'},
+ {nullptr, no_argument, nullptr, 0}};
while (1) {
int c = getopt_long(argc, argv, ":r:m:f:th", long_options, nullptr);
@@ -250,7 +250,7 @@
tracer_.dump(GetTime());
}
-const char* VerilatorSimCtrl::GetSimulationFileName() const {
+const char *VerilatorSimCtrl::GetSimulationFileName() const {
#ifdef VM_TRACE_FMT_FST
return "sim.fst";
#else
@@ -301,8 +301,7 @@
}
if (term_after_cycles_ && time_ > term_after_cycles_) {
std::cout << "Simulation timeout of " << term_after_cycles_
- << " cycles reached, shutting down simulation."
- << std::endl;
+ << " cycles reached, shutting down simulation." << std::endl;
break;
}
}
@@ -344,7 +343,7 @@
return stat(filepath.data(), &statbuf) == 0;
}
-bool VerilatorSimCtrl::FileSize(std::string filepath, int& size_byte) {
+bool VerilatorSimCtrl::FileSize(std::string filepath, int &size_byte) {
struct stat statbuf;
if (stat(filepath.data(), &statbuf) != 0) {
size_byte = 0;
diff --git a/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h b/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h
index ef629c8..183939f 100644
--- a/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h
+++ b/hw/vendor/lowrisc_ibex/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h
@@ -19,7 +19,7 @@
class VerilatorSimCtrl {
public:
- VerilatorSimCtrl(VerilatedToplevel* top, CData& clk, CData& rst_n,
+ VerilatorSimCtrl(VerilatedToplevel *top, CData &clk, CData &rst_n,
VerilatorSimCtrlFlags flags = Defaults);
/**
@@ -36,7 +36,7 @@
* retcode: if this method returns true, do *not* exit; if it returns *false*,
* do exit.
*/
- bool ParseCommandArgs(int argc, char** argv, int& retcode);
+ bool ParseCommandArgs(int argc, char **argv, int &retcode);
/**
* Run the main loop of the simulation
@@ -120,12 +120,12 @@
*/
void PrintStatistics();
- const char* GetSimulationFileName() const;
+ const char *GetSimulationFileName() const;
private:
- VerilatedToplevel* top_;
- CData& sig_clk_;
- CData& sig_rst_;
+ VerilatedToplevel *top_;
+ CData &sig_clk_;
+ CData &sig_rst_;
VerilatorSimCtrlFlags flags_;
unsigned long time_;
bool init_rom_;
@@ -150,7 +150,7 @@
void SetReset();
void UnsetReset();
bool IsFileReadable(std::string filepath);
- bool FileSize(std::string filepath, int& size_byte);
+ bool FileSize(std::string filepath, int &size_byte);
void Trace();
};
diff --git a/hw/vendor/lowrisc_ibex/examples/fpga/artya7-100/rtl/top_artya7_100.sv b/hw/vendor/lowrisc_ibex/examples/fpga/artya7-100/rtl/top_artya7_100.sv
index 8391f68..056a41a 100644
--- a/hw/vendor/lowrisc_ibex/examples/fpga/artya7-100/rtl/top_artya7_100.sv
+++ b/hw/vendor/lowrisc_ibex/examples/fpga/artya7-100/rtl/top_artya7_100.sv
@@ -78,7 +78,8 @@
.debug_req_i ('b0),
- .fetch_enable_i ('b1)
+ .fetch_enable_i ('b1),
+ .core_sleep_o ()
);
// Connect Ibex to SRAM
diff --git a/hw/vendor/lowrisc_ibex/examples/sim/tb/ibex_tracing_tb.sv b/hw/vendor/lowrisc_ibex/examples/sim/tb/ibex_tracing_tb.sv
index 42b2b7b..59d4d9a 100644
--- a/hw/vendor/lowrisc_ibex/examples/sim/tb/ibex_tracing_tb.sv
+++ b/hw/vendor/lowrisc_ibex/examples/sim/tb/ibex_tracing_tb.sv
@@ -110,7 +110,8 @@
.debug_req_i (1'b0),
// CPU Control Signals
- .fetch_enable_i (1'b1)
+ .fetch_enable_i (1'b1),
+ .core_sleep_o ()
);
endmodule
diff --git a/hw/vendor/lowrisc_ibex/lint/verilator_waiver.vlt b/hw/vendor/lowrisc_ibex/lint/verilator_waiver.vlt
index 4df10e5..22c77bd 100644
--- a/hw/vendor/lowrisc_ibex/lint/verilator_waiver.vlt
+++ b/hw/vendor/lowrisc_ibex/lint/verilator_waiver.vlt
@@ -56,15 +56,15 @@
// Signal is not used: csr_pmp_addr
// Signal not connected when PMP is not configured
-lint_off -msg UNUSED -file "*/rtl/ibex_core.sv" -lines 186
+lint_off -msg UNUSED -file "*/rtl/ibex_core.sv" -lines 185
// Signal is not used: csr_pmp_cfg
// Signal not connected when PMP is not configured
-lint_off -msg UNUSED -file "*/rtl/ibex_core.sv" -lines 187
+lint_off -msg UNUSED -file "*/rtl/ibex_core.sv" -lines 186
// Signal is not used: priv_mode
// Signal not connected when PMP is not configured
-lint_off -msg UNUSED -file "*/rtl/ibex_core.sv" -lines 199
+lint_off -msg UNUSED -file "*/rtl/ibex_core.sv" -lines 198
// Signal unoptimizable: Feedback to clock or circular logic:
// ibex_core.id_stage_i.controller_i.ctrl_fsm_cs
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_controller.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_controller.sv
index d61c3fd..5a8fc54 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_controller.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_controller.sv
@@ -113,7 +113,7 @@
logic enter_debug_mode;
logic handle_irq;
- logic [4:0] mfip_id;
+ logic [3:0] mfip_id;
logic unused_csr_mtip;
`ifndef SYNTHESIS
@@ -164,22 +164,22 @@
// generate ID of fast interrupts, highest priority to highest ID
always_comb begin : gen_mfip_id
- if (csr_mfip_i[14]) mfip_id = 5'd14;
- else if (csr_mfip_i[13]) mfip_id = 5'd13;
- else if (csr_mfip_i[12]) mfip_id = 5'd12;
- else if (csr_mfip_i[11]) mfip_id = 5'd11;
- else if (csr_mfip_i[10]) mfip_id = 5'd10;
- else if (csr_mfip_i[ 9]) mfip_id = 5'd9;
- else if (csr_mfip_i[ 8]) mfip_id = 5'd8;
- else if (csr_mfip_i[ 7]) mfip_id = 5'd7;
- else if (csr_mfip_i[ 6]) mfip_id = 5'd6;
- else if (csr_mfip_i[ 5]) mfip_id = 5'd5;
- else if (csr_mfip_i[ 5]) mfip_id = 5'd5;
- else if (csr_mfip_i[ 4]) mfip_id = 5'd4;
- else if (csr_mfip_i[ 3]) mfip_id = 5'd3;
- else if (csr_mfip_i[ 2]) mfip_id = 5'd2;
- else if (csr_mfip_i[ 1]) mfip_id = 5'd1;
- else mfip_id = 5'd0;
+ if (csr_mfip_i[14]) mfip_id = 4'd14;
+ else if (csr_mfip_i[13]) mfip_id = 4'd13;
+ else if (csr_mfip_i[12]) mfip_id = 4'd12;
+ else if (csr_mfip_i[11]) mfip_id = 4'd11;
+ else if (csr_mfip_i[10]) mfip_id = 4'd10;
+ else if (csr_mfip_i[ 9]) mfip_id = 4'd9;
+ else if (csr_mfip_i[ 8]) mfip_id = 4'd8;
+ else if (csr_mfip_i[ 7]) mfip_id = 4'd7;
+ else if (csr_mfip_i[ 6]) mfip_id = 4'd6;
+ else if (csr_mfip_i[ 5]) mfip_id = 4'd5;
+ else if (csr_mfip_i[ 5]) mfip_id = 4'd5;
+ else if (csr_mfip_i[ 4]) mfip_id = 4'd4;
+ else if (csr_mfip_i[ 3]) mfip_id = 4'd3;
+ else if (csr_mfip_i[ 2]) mfip_id = 4'd2;
+ else if (csr_mfip_i[ 1]) mfip_id = 4'd1;
+ else mfip_id = 4'd0;
end
assign unused_csr_mtip = csr_mtip_i;
@@ -215,7 +215,7 @@
debug_csr_save_o = 1'b0;
debug_cause_o = DBG_CAUSE_EBREAK;
debug_mode_d = debug_mode_q;
- nmi_mode_d = 1'b0;
+ nmi_mode_d = nmi_mode_q;
perf_tbranch_o = 1'b0;
perf_jump_o = 1'b0;
@@ -360,7 +360,11 @@
exc_cause_o = EXC_CAUSE_IRQ_NM;
nmi_mode_d = 1'b1; // enter NMI mode
end else if (csr_mfip_i != 15'b0) begin
- exc_cause_o = exc_cause_e'({1'b1, mfip_id});
+ // generate exception cause ID from fast interrupt ID:
+ // - first bit distinguishes interrupts from exceptions,
+ // - second bit adds 16 to fast interrupt ID
+ // for example EXC_CAUSE_IRQ_FAST_0 = {1'b1, 5'd16}
+ exc_cause_o = exc_cause_e'({2'b11, mfip_id});
end else if (csr_meip_i) begin
exc_cause_o = EXC_CAUSE_IRQ_EXTERNAL_M;
end else if (csr_msip_i) begin
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_core.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_core.sv
index be613b9..4f5bafd 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_core.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_core.sv
@@ -66,7 +66,6 @@
output logic rvfi_valid,
output logic [63:0] rvfi_order,
output logic [31:0] rvfi_insn,
- output logic [31:0] rvfi_insn_uncompressed,
output logic rvfi_trap,
output logic rvfi_halt,
output logic rvfi_intr,
@@ -87,8 +86,8 @@
`endif
// CPU Control Signals
- input logic fetch_enable_i
-
+ input logic fetch_enable_i,
+ output logic core_sleep_o
);
import ibex_pkg::*;
@@ -272,6 +271,8 @@
assign core_busy = core_ctrl_firstfetch ? 1'b1 : core_busy_q;
+ assign core_sleep_o = ~clock_en;
+
assign clock_en = core_busy | debug_req_i | irq_pending | irq_nm_i;
// main clock gate of the core
@@ -669,7 +670,6 @@
rvfi_intr <= '0;
rvfi_order <= '0;
rvfi_insn <= '0;
- rvfi_insn_uncompressed <= '0;
rvfi_mode <= '0;
rvfi_rs1_addr <= '0;
rvfi_rs2_addr <= '0;
@@ -691,7 +691,6 @@
rvfi_intr <= rvfi_intr_d;
rvfi_order <= rvfi_order + rvfi_valid;
rvfi_insn <= rvfi_insn_id;
- rvfi_insn_uncompressed <= instr_rdata_id;
rvfi_mode <= PRIV_LVL_M; // TODO: Update for user mode support
rvfi_rs1_addr <= rvfi_rs1_addr_id;
rvfi_rs2_addr <= rvfi_rs2_addr_id;
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_core_tracing.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_core_tracing.sv
index 63c100f..7331923 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_core_tracing.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_core_tracing.sv
@@ -53,7 +53,8 @@
input logic debug_req_i,
// CPU Control Signals
- input logic fetch_enable_i
+ input logic fetch_enable_i,
+ output logic core_sleep_o
);
@@ -67,7 +68,6 @@
logic rvfi_valid;
logic [63:0] rvfi_order;
logic [31:0] rvfi_insn;
- logic [31:0] rvfi_insn_uncompressed;
logic rvfi_trap;
logic rvfi_halt;
logic rvfi_intr;
@@ -130,7 +130,6 @@
.rvfi_valid,
.rvfi_order,
.rvfi_insn,
- .rvfi_insn_uncompressed,
.rvfi_trap,
.rvfi_halt,
.rvfi_intr,
@@ -149,7 +148,8 @@
.rvfi_mem_rdata,
.rvfi_mem_wdata,
- .fetch_enable_i
+ .fetch_enable_i,
+ .core_sleep_o
);
@@ -163,7 +163,7 @@
.valid_i ( rvfi_valid ),
.pc_i ( rvfi_pc_rdata ),
- .instr_i ( rvfi_insn_uncompressed ),
+ .instr_i ( rvfi_insn ),
.rs1_value_i ( rvfi_rs1_rdata ),
.rs2_value_i ( rvfi_rs2_rdata ),
.ex_reg_addr_i ( rvfi_rd_addr ),
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_cs_registers.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_cs_registers.sv
index f65ab51..d7763df 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_cs_registers.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_cs_registers.sv
@@ -596,7 +596,7 @@
mtval_q <= '0;
mtvec_q <= 32'b01;
dcsr_q <= '{
- xdebugver: XDEBUGVER_NO, // 4'h0
+ xdebugver: XDEBUGVER_STD,
cause: DBG_CAUSE_NONE, // 3'h0
prv: PRIV_LVL_M,
default: '0
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_fetch_fifo.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_fetch_fifo.sv
index 1a2d5d1..6d4fba9 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_fetch_fifo.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_fetch_fifo.sv
@@ -6,64 +6,90 @@
/**
* Fetch Fifo for 32 bit memory interface
*
- * input port: send address one cycle before the data
- * clear_i clears the FIFO for the following cycle. in_addr_i can be sent in
- * this cycle already.
+ * input port: send address and data to the FIFO
+ * clear_i clears the FIFO for the following cycle, including any new request
*/
module ibex_fetch_fifo (
input logic clk_i,
input logic rst_ni,
// control signals
- input logic clear_i, // clears the contents of the fifo
+ input logic clear_i, // clears the contents of the FIFO
// input port
+ input logic in_valid_i,
+ output logic in_ready_o,
input logic [31:0] in_addr_i,
input logic [31:0] in_rdata_i,
input logic in_err_i,
- input logic in_valid_i,
- output logic in_ready_o,
-
// output port
output logic out_valid_o,
input logic out_ready_i,
- output logic [31:0] out_rdata_o,
output logic [31:0] out_addr_o,
- output logic out_err_o,
-
- output logic out_valid_stored_o // same as out_valid_o, except that if something is
- // incoming now it is not included. This signal is
- // available immediately as it comes directly out of FFs
+ output logic [31:0] out_rdata_o,
+ output logic out_err_o
);
localparam int unsigned DEPTH = 3; // must be 3 or greater
// index 0 is used for output
- logic [DEPTH-1:0] [31:0] addr_n, addr_int, addr_q;
- logic [DEPTH-1:0] [31:0] rdata_n, rdata_int, rdata_q;
- logic [DEPTH-1:0] err_n, err_int, err_q;
- logic [DEPTH-1:0] valid_n, valid_int, valid_q;
+ logic [DEPTH-1:0] [31:2] addr_d, addr_q;
+ logic [DEPTH-1:0] [31:0] rdata_d, rdata_q;
+ logic [DEPTH-1:0] err_d, err_q;
+ logic [DEPTH-1:0] valid_d, valid_q;
+ logic [DEPTH-1:0] lowest_free_entry;
+ logic [DEPTH-1:0] valid_pushed, valid_popped;
+ logic [DEPTH-1:0] entry_en;
- logic [31:2] addr_next;
+ logic pop_fifo;
logic [31:0] rdata, rdata_unaligned;
logic err, err_unaligned;
logic valid, valid_unaligned;
+ logic entry0_unaligned_d, entry0_unaligned_q;
logic aligned_is_compressed, unaligned_is_compressed;
- logic unaligned_is_compressed_st;
+
+ logic unused_addr_in;
/////////////////
// Output port //
/////////////////
-
assign rdata = valid_q[0] ? rdata_q[0] : in_rdata_i;
assign err = valid_q[0] ? err_q[0] : in_err_i;
assign valid = valid_q[0] | in_valid_i;
+ // The FIFO contains word aligned memory fetches, but the instructions contained in each entry
+ // might be half-word aligned (due to compressed instructions)
+ // e.g.
+ // | 31 16 | 15 0 |
+ // FIFO entry 0 | Instr 1 [15:0] | Instr 0 [15:0] |
+ // FIFO entry 1 | Instr 2 [15:0] | Instr 1 [31:16] |
+ //
+ // The FIFO also has a direct bypass path, so a complete instruction might be made up of data
+ // from the FIFO and new incoming data.
+ //
+ // Additionally, branches can cause a fetch from an unaligned address. The full data word will be
+ // fetched, but the FIFO must output the unaligned instruction as the first valid data.
+
+ // Alignment is tracked with a flag, this records whether entry[0] of the FIFO has become unaligned.
+ // The flag is set once any compressed instruction enters the FIFO and is only cleared once a
+ // a compressed instruction realigns the FIFO, or the FIFO is cleared.
+
+ // New incoming unaligned request (must be a branch) or already unaligned
+ assign entry0_unaligned_d = ((((in_valid_i & in_addr_i[1]) | entry0_unaligned_q) &
+ // cleared by a compressed unaligned instruction
+ ~(out_ready_i & unaligned_is_compressed)) |
+ // Also set when a new aligned compressed instruction is driven
+ (valid & out_ready_i & ~out_addr_o[1] & aligned_is_compressed)) &
+ // reset by a FIFO clear
+ ~clear_i;
+
+ // Construct the output data for an unaligned instruction
assign rdata_unaligned = valid_q[1] ? {rdata_q[1][15:0], rdata[31:16]} :
{in_rdata_i[15:0], rdata[31:16]};
+
// If entry[1] is valid, an error can come from entry[0] or entry[1], unless the
// instruction in entry[0] is compressed (entry[1] is a new instruction)
// If entry[1] is not valid, and entry[0] is, an error can come from entry[0] or the incoming
@@ -72,22 +98,19 @@
assign err_unaligned = valid_q[1] ? ((err_q[1] & ~unaligned_is_compressed) | err_q[0]) :
((valid_q[0] & err_q[0]) |
(in_err_i & (~valid_q[0] | ~unaligned_is_compressed)));
+
// An uncompressed unaligned instruction is only valid if both parts are available
assign valid_unaligned = valid_q[1] ? 1'b1 :
(valid_q[0] & in_valid_i);
assign unaligned_is_compressed = rdata[17:16] != 2'b11;
assign aligned_is_compressed = rdata[ 1: 0] != 2'b11;
- assign unaligned_is_compressed_st = rdata_q[0][17:16] != 2'b11;
////////////////////////////////////////
// Instruction aligner (if unaligned) //
////////////////////////////////////////
always_comb begin
- // serve the aligned case even though the output address is unaligned when
- // the next instruction will be from a hardware loop target
- // in this case the current instruction is already prealigned in element 0
if (out_addr_o[1]) begin
// unaligned case
out_rdata_o = rdata_unaligned;
@@ -106,29 +129,18 @@
end
end
- assign out_addr_o = valid_q[0] ? addr_q[0] : in_addr_i;
+ assign out_addr_o[31:2] = valid_q[0] ? addr_q[0] : in_addr_i[31:2];
+ assign out_addr_o[1] = valid_q[0] ? entry0_unaligned_q : in_addr_i[1];
+ assign out_addr_o[0] = 1'b0;
- // this valid signal must not depend on signals from outside!
- always_comb begin
- out_valid_stored_o = 1'b1;
-
- if (out_addr_o[1]) begin
- if (unaligned_is_compressed_st) begin
- out_valid_stored_o = 1'b1;
- end else begin
- out_valid_stored_o = valid_q[1];
- end
- end else begin
- out_valid_stored_o = valid_q[0];
- end
- end
-
+ // The LSB of the address is unused, since all addresses are halfword aligned
+ assign unused_addr_in = in_addr_i[0];
////////////////
// input port //
////////////////
- // we accept data as long as our fifo is not full
+ // we accept data as long as our FIFO is not full
// we don't care about clear here as the data will be received one cycle
// later anyway
assign in_ready_o = ~valid_q[DEPTH-2];
@@ -137,78 +149,65 @@
// FIFO management //
/////////////////////
- always_comb begin
- addr_int = addr_q;
- rdata_int = rdata_q;
- err_int = err_q;
- valid_int = valid_q;
- if (in_valid_i) begin
- for (int j = 0; j < DEPTH; j++) begin
- if (!valid_q[j]) begin
- addr_int[j] = in_addr_i;
- rdata_int[j] = in_rdata_i;
- err_int[j] = in_err_i;
- valid_int[j] = 1'b1;
- break;
- end
- end
+ // Since an entry can contain unaligned instructions, popping an entry can leave the entry valid
+ assign pop_fifo = out_ready_i & out_valid_o & (~aligned_is_compressed | out_addr_o[1]);
+
+ for (genvar i = 0; i < (DEPTH - 1); i++) begin : g_fifo_next
+ // Calculate lowest free entry (write pointer)
+ if (i == 0) begin : g_ent0
+ assign lowest_free_entry[i] = ~valid_q[i];
+ end else begin : g_ent_others
+ assign lowest_free_entry[i] = ~valid_q[i] & (&valid_q[i-1:0]);
end
+
+ // An entry is set when an incoming request chooses the lowest available entry
+ assign valid_pushed[i] = (in_valid_i & lowest_free_entry[i]) |
+ valid_q[i];
+ // Popping the FIFO shifts all entries down
+ assign valid_popped[i] = pop_fifo ? valid_pushed[i+1] : valid_pushed[i];
+ // All entries are wiped out on a clear
+ assign valid_d[i] = valid_popped[i] & ~clear_i;
+
+ // data flops are enabled if there is new data to shift into it, or
+ assign entry_en[i] = (valid_pushed[i+1] & pop_fifo) |
+ // a new request is incoming and this is the lowest free entry
+ (in_valid_i & lowest_free_entry[i] & ~pop_fifo);
+
+ // take the next entry or the incoming data
+ assign addr_d [i] = valid_q[i+1] ? addr_q [i+1] : in_addr_i[31:2];
+ assign rdata_d[i] = valid_q[i+1] ? rdata_q[i+1] : in_rdata_i;
+ assign err_d [i] = valid_q[i+1] ? err_q [i+1] : in_err_i;
end
+ // The top entry is similar but with simpler muxing
+ assign lowest_free_entry[DEPTH-1] = ~valid_q[DEPTH-1] & (&valid_q[DEPTH-2:0]);
+ assign valid_pushed [DEPTH-1] = valid_q[DEPTH-1] | (in_valid_i & lowest_free_entry[DEPTH-1]);
+ assign valid_popped [DEPTH-1] = pop_fifo ? 1'b0 : valid_pushed[DEPTH-1];
+ assign valid_d [DEPTH-1] = valid_popped[DEPTH-1] & ~clear_i;
+ assign entry_en[DEPTH-1] = in_valid_i & lowest_free_entry[DEPTH-1];
+ assign addr_d [DEPTH-1] = in_addr_i[31:2];
+ assign rdata_d [DEPTH-1] = in_rdata_i;
+ assign err_d [DEPTH-1] = in_err_i;
- assign addr_next[31:2] = addr_int[0][31:2] + 30'h1;
-
- // move everything by one step
- always_comb begin
- addr_n = addr_int;
- rdata_n = rdata_int;
- err_n = err_int;
- valid_n = valid_int;
-
- if (out_ready_i && out_valid_o) begin
- if (addr_int[0][1]) begin
- // unaligned case
- if (unaligned_is_compressed) begin
- addr_n[0] = {addr_next[31:2], 2'b00};
- end else begin
- addr_n[0] = {addr_next[31:2], 2'b10};
- end
-
- rdata_n = {32'b0, rdata_int[DEPTH-1:1]};
- err_n = {1'b0, err_int[DEPTH-1:1]};
- valid_n = {1'b0, valid_int[DEPTH-1:1]};
- end else if (aligned_is_compressed) begin
- // just increase address, do not move to next entry in FIFO
- addr_n[0] = {addr_int[0][31:2], 2'b10};
- end else begin
- // move to next entry in FIFO
- addr_n[0] = {addr_next[31:2], 2'b00};
- rdata_n = {32'b0, rdata_int[DEPTH-1:1]};
- err_n = {1'b0, err_int[DEPTH-1:1]};
- valid_n = {1'b0, valid_int[DEPTH-1:1]};
- end
- end
- end
-
- ///////////////
- // registers //
- ///////////////
+ ////////////////////
+ // FIFO registers //
+ ////////////////////
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
- addr_q <= '{default: '0};
- rdata_q <= '{default: '0};
- err_q <= '0;
- valid_q <= '0;
+ valid_q <= '0;
+ entry0_unaligned_q <= '0;
end else begin
- // on a clear signal from outside we invalidate the content of the FIFO
- // completely and start from an empty state
- if (clear_i) begin
- valid_q <= '0;
- end else begin
- addr_q <= addr_n;
- rdata_q <= rdata_n;
- err_q <= err_n;
- valid_q <= valid_n;
+ valid_q <= valid_d;
+ entry0_unaligned_q <= entry0_unaligned_d;
+ end
+ end
+
+ for (genvar i = 0; i < DEPTH; i++) begin : g_fifo_regs
+ always_ff @(posedge clk_i) begin
+ if (entry_en[i]) begin
+ addr_q[i] <= addr_d[i];
+ rdata_q[i] <= rdata_d[i];
+ err_q[i] <= err_d[i];
end
end
end
@@ -218,6 +217,7 @@
////////////////
`ifndef VERILATOR
assert property (
- @(posedge clk_i) (in_valid_i) |-> ((valid_q[DEPTH-1] == 1'b0) || (clear_i == 1'b1)) );
+ @(posedge clk_i) disable iff (!rst_ni)
+ (in_valid_i) |-> ((valid_q[DEPTH-1] == 1'b0) || (clear_i == 1'b1)) );
`endif
endmodule
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_if_stage.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_if_stage.sv
index a1fa6d4..f3a58f7 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_if_stage.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_if_stage.sv
@@ -261,11 +261,6 @@
@(posedge clk_i) (boot_addr_i[7:0] == 8'h00) ) else
$error("The provided boot address is not aligned to 256 bytes");
- // there should never be a grant when there is no request
- assert property (
- @(posedge clk_i) (instr_gnt_i) |-> (instr_req_o) ) else
- $warning("There was a grant without a request");
-
// assert that the address is word aligned when request is sent
assert property (
@(posedge clk_i) (instr_req_o) |-> (instr_addr_o[1:0] == 2'b00) ) else
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_load_store_unit.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_load_store_unit.sv
index 8abcd95..c959ce2 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_load_store_unit.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_load_store_unit.sv
@@ -76,11 +76,13 @@
logic split_misaligned_access;
logic handle_misaligned_q, handle_misaligned_d; // high after receiving grant for first
// part of a misaligned access
+ logic pmp_err_d;
logic pmp_err_q;
logic data_or_pmp_err;
typedef enum logic [2:0] {
- IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT, WAIT_RVALID
+ IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT, WAIT_RVALID,
+ WAIT_GNT_ERR, WAIT_RVALID_ERR, WAIT_RVALID_DONE
} ls_fsm_e;
ls_fsm_e ls_fsm_cs, ls_fsm_ns;
@@ -321,12 +323,14 @@
addr_incr_req_o = 1'b0;
handle_misaligned_d = handle_misaligned_q;
data_or_pmp_err = 1'b0;
+ pmp_err_d = pmp_err_q;
unique case (ls_fsm_cs)
IDLE: begin
if (data_req_ex_i) begin
data_req_o = 1'b1;
+ pmp_err_d = data_pmp_err_i;
if (data_gnt_i) begin
handle_misaligned_d = split_misaligned_access;
ls_fsm_ns = split_misaligned_access ? WAIT_RVALID_MIS : WAIT_RVALID;
@@ -338,6 +342,10 @@
WAIT_GNT_MIS: begin
data_req_o = 1'b1;
+ // data_pmp_err_i is valid during the address phase of a request. An error will block the
+ // external request and so a data_gnt_i might never be signalled. The registered version
+ // pmp_err_q is only updated for new address phases and so can be used in WAIT_GNT* and
+ // WAIT_RVALID* states
if (data_gnt_i || pmp_err_q) begin
handle_misaligned_d = 1'b1;
ls_fsm_ns = WAIT_RVALID_MIS;
@@ -345,32 +353,34 @@
end
WAIT_RVALID_MIS: begin
+ // push out second request
+ data_req_o = 1'b1;
// tell ID/EX stage to update the address
addr_incr_req_o = 1'b1;
- // first part rvalid is received, or gets a pmp error
- // pmp_err_i will hold stable until the address is updated, and
- // therefore pmp_err_q is valid in both WAIT_GNT_MIS and WAIT_RVALID_MIS states
+
+ // first part rvalid is received, or gets a PMP error
if (data_rvalid_i || pmp_err_q) begin
+ // Update the PMP error for the second part
+ pmp_err_d = data_pmp_err_i;
if (pmp_err_q || data_err_i) begin
// first part created an error, abort transaction
data_valid_o = 1'b1;
data_or_pmp_err = 1'b1;
handle_misaligned_d = 1'b0;
- ls_fsm_ns = IDLE;
+ // If already granted, wait for second rvalid
+ ls_fsm_ns = data_gnt_i ? WAIT_RVALID_ERR : WAIT_GNT_ERR;
+
end else begin
- // push out second request
- data_req_o = 1'b1;
- if (data_gnt_i) begin
- // second grant is received
- ls_fsm_ns = WAIT_RVALID;
- end else begin
- // second grant is NOT received, but first rvalid
- ls_fsm_ns = WAIT_GNT;
- end
+ // No error in first part, proceed with second part
+ ls_fsm_ns = data_gnt_i ? WAIT_RVALID : WAIT_GNT;
end
+
end else begin
// first part rvalid is NOT received
- ls_fsm_ns = WAIT_RVALID_MIS;
+ if (data_gnt_i) begin
+ // second grant is received
+ ls_fsm_ns = WAIT_RVALID_DONE;
+ end
end
end
@@ -385,8 +395,6 @@
WAIT_RVALID: begin
data_req_o = 1'b0;
- // pmp_err_i will hold stable until the address is updated, and
- // therefore pmp_err_q is valid in both WAIT_GNT and WAIT_RVALID states
if (data_rvalid_i || pmp_err_q) begin
data_valid_o = 1'b1;
data_or_pmp_err = data_err_i | pmp_err_q;
@@ -397,6 +405,44 @@
end
end
+ WAIT_GNT_ERR: begin
+ // Wait for the grant of the abandoned second access
+ data_req_o = 1'b1;
+ // tell ID/EX stage to update the address
+ addr_incr_req_o = 1'b1;
+ if (pmp_err_q) begin
+ // The second part was suppressed by a PMP error
+ ls_fsm_ns = IDLE;
+ end else if (data_gnt_i) begin
+ ls_fsm_ns = WAIT_RVALID_ERR;
+ end
+ end
+
+ WAIT_RVALID_ERR: begin
+ // Wait for the rvalid, but do nothing with it
+ if (data_rvalid_i || pmp_err_q) begin
+ ls_fsm_ns = IDLE;
+ end
+ end
+
+ WAIT_RVALID_DONE: begin
+ // Wait for the first rvalid, second request is already granted
+ if (data_rvalid_i) begin
+ // Update the pmp error for the second part
+ pmp_err_d = data_pmp_err_i;
+ // The first part cannot see a PMP error in this state
+ if (data_err_i) begin
+ // first part created an error, abort transaction and wait for second rvalid
+ data_valid_o = 1'b1;
+ data_or_pmp_err = 1'b1;
+ handle_misaligned_d = 1'b0;
+ ls_fsm_ns = WAIT_RVALID_ERR;
+ end else begin
+ ls_fsm_ns = WAIT_RVALID;
+ end
+ end
+ end
+
default: begin
ls_fsm_ns = ls_fsm_e'(1'bX);
end
@@ -424,7 +470,7 @@
ls_fsm_cs <= ls_fsm_ns;
addr_last_q <= addr_last_d;
handle_misaligned_q <= handle_misaligned_d;
- pmp_err_q <= data_pmp_err_i;
+ pmp_err_q <= pmp_err_d;
end
end
@@ -451,7 +497,7 @@
assign load_err_o = data_or_pmp_err & ~data_we_q;
assign store_err_o = data_or_pmp_err & data_we_q;
- assign busy_o = (ls_fsm_cs == WAIT_RVALID) | (data_req_o == 1'b1);
+ assign busy_o = (ls_fsm_cs != IDLE);
////////////////
// Assertions //
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_prefetch_buffer.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_prefetch_buffer.sv
index cda0bb7..6446661 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_prefetch_buffer.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_prefetch_buffer.sv
@@ -39,27 +39,31 @@
output logic busy_o
);
- typedef enum logic [1:0] {
- IDLE, WAIT_GNT, WAIT_RVALID, WAIT_ABORTED
- } pf_fsm_e;
+ // Changes to the address flops would be required for > 2 outstanding requests
+ localparam int unsigned NUM_REQS = 2;
- pf_fsm_e pf_fsm_cs, pf_fsm_ns;
+ logic valid_req;
+ logic valid_req_d, valid_req_q;
+ logic hold_addr_d, hold_addr_q;
+ logic gnt_or_pmp_err, rvalid_or_pmp_err;
+ logic [NUM_REQS-1:0] rdata_outstanding_n, rdata_outstanding_s, rdata_outstanding_q;
+ logic [NUM_REQS-1:0] branch_abort_n, branch_abort_s, branch_abort_q;
- logic [31:0] instr_addr_q, fetch_addr;
- logic [31:0] instr_addr, instr_addr_w_aligned;
- logic addr_valid;
- logic pmp_err_q;
- logic instr_or_pmp_err;
+ logic [31:0] instr_addr_q, fetch_addr;
+ logic [31:0] instr_addr, instr_addr_w_aligned;
+ logic addr_valid;
+ logic pmp_err_q;
+ logic instr_or_pmp_err;
- logic fifo_valid;
- logic fifo_ready;
- logic fifo_clear;
+ logic fifo_valid;
+ logic fifo_ready;
+ logic fifo_clear;
////////////////////////////
// Prefetch buffer status //
////////////////////////////
- assign busy_o = (pf_fsm_cs != IDLE) | instr_req_o;
+ assign busy_o = (|rdata_outstanding_q) | instr_req_o;
//////////////////////////////////////////////
// Fetch fifo - consumes addresses and data //
@@ -69,16 +73,19 @@
// PMP errors are generated in the address phase, and registered into a fake data phase
assign instr_or_pmp_err = instr_err_i | pmp_err_q;
+ // A branch will invalidate any previously fetched instructions
+ assign fifo_clear = branch_i;
+
ibex_fetch_fifo fifo_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.clear_i ( fifo_clear ),
+ .in_valid_i ( fifo_valid ),
.in_addr_i ( instr_addr_q ),
.in_rdata_i ( instr_rdata_i ),
.in_err_i ( instr_or_pmp_err ),
- .in_valid_i ( fifo_valid ),
.in_ready_o ( fifo_ready ),
@@ -86,154 +93,142 @@
.out_ready_i ( ready_i ),
.out_rdata_o ( rdata_o ),
.out_addr_o ( addr_o ),
- .out_err_o ( err_o ),
-
- .out_valid_stored_o ( )
+ .out_err_o ( err_o )
);
+ //////////////
+ // Requests //
+ //////////////
+
+ // Make a new request any time there is space in the FIFO, and space in the request queue
+ assign valid_req = valid_req_q | (req_i & (fifo_ready | branch_i) & (~&rdata_outstanding_q));
+
+ // If a request address triggers a PMP error, the external bus request is suppressed. We might
+ // therefore never receive a grant for such a request. The grant is faked in this case to make
+ // sure the request proceeds and the error is pushed to the FIFO.
+ // We always use the registered version of the signal since it will be held stable throughout
+ // the request, and the penalty of waiting for an extra cycle to consume the error is irrelevant.
+ // A branch could update the address (and therefore data_pmp_err_i) on the cycle a request is
+ // issued, in which case we must ignore the registered version.
+ assign gnt_or_pmp_err = instr_gnt_i | (pmp_err_q & ~branch_i);
+
+ // As with the grant, the rvalid must be faked for a PMP error, since the request was suppressed.
+ // Since the pmp_err_q flop is only updated when the address updates, it will always point to the
+ // PMP error status of the oldest outstanding request
+ assign rvalid_or_pmp_err = rdata_outstanding_q[0] & (instr_rvalid_i | pmp_err_q);
+
+ // Hold the request stable for requests that didn't get granted
+ assign valid_req_d = valid_req & ~instr_gnt_i;
+
+ // Hold the address stable for requests that couldn't be issued, or didn't get granted.
+ // This is different to valid_req_q since there are cases where we must use addr+4 for
+ // an ungranted request rather than addr_q (where addr_q has not been updated).
+ assign hold_addr_d = (branch_i | hold_addr_q) & ~(valid_req & instr_gnt_i);
////////////////
// Fetch addr //
////////////////
+ // The address flop is used to hold the address steady for ungranted requests and also to
+ // push the address to the FIFO for completed requests. For this reason, the address is only
+ // updated once a request is the oldest outstanding to ensure that newer requests do not
+ // overwrite the addresses of older ones. Branches are an exception to this, since all older
+ // addresses will be discarded due to the branch.
+
+ // Update the addr_q flop on any branch, or
+ assign addr_valid = branch_i |
+ // A new request which will be the oldest, or
+ (valid_req & instr_gnt_i & ~rdata_outstanding_q[0]) |
+ // each time a valid request becomes the oldest
+ (rvalid_or_pmp_err & ~branch_abort_q[0] &
+ ((valid_req & instr_gnt_i) | rdata_outstanding_q[1]));
+
+ // Fetch the next word-aligned instruction address
assign fetch_addr = {instr_addr_q[31:2], 2'b00} + 32'd4;
- assign fifo_clear = branch_i;
- //////////////////////////////////////////////////////////////////////////////
- // Instruction fetch FSM -deals with instruction memory / instruction cache //
- //////////////////////////////////////////////////////////////////////////////
+ // Address mux
+ assign instr_addr = branch_i ? addr_i :
+ hold_addr_q ? instr_addr_q :
+ fetch_addr;
- always_comb begin
- instr_req_o = 1'b0;
- instr_addr = fetch_addr;
- fifo_valid = 1'b0;
- addr_valid = 1'b0;
- pf_fsm_ns = pf_fsm_cs;
+ assign instr_addr_w_aligned = {instr_addr[31:2], 2'b00};
- unique case(pf_fsm_cs)
- // default state, not waiting for requested data
- IDLE: begin
- instr_addr = fetch_addr;
- instr_req_o = 1'b0;
+ ///////////////////////////////
+ // Request outstanding queue //
+ ///////////////////////////////
- if (branch_i) begin
- instr_addr = addr_i;
- end
+ for (genvar i = 0; i < NUM_REQS; i++) begin : g_outstanding_reqs
+ // Request 0 (always the oldest outstanding request)
+ if (i == 0) begin : g_req0
+ // A request becomes outstanding once granted, and is cleared once the rvalid is received.
+ // Outstanding requests shift down the queue towards entry 0. Entry 0 considers the PMP
+ // error cases while newer entries do not (pmp_err_q is only valid for entry 0)
+ assign rdata_outstanding_n[i] = (valid_req & gnt_or_pmp_err) |
+ rdata_outstanding_q[i];
+ // If a branch is received at any point while a request is outstanding, it must be tracked
+ // to ensure we discard the data once received
+ assign branch_abort_n[i] = (branch_i & rdata_outstanding_q[i]) | branch_abort_q[i];
- if (req_i && (fifo_ready || branch_i )) begin
- instr_req_o = 1'b1;
- addr_valid = 1'b1;
+ end else begin : g_reqtop
-
- //~> granted request or not
- pf_fsm_ns = instr_gnt_i ? WAIT_RVALID : WAIT_GNT;
- end
- end // case: IDLE
-
- // we sent a request but did not yet get a grant
- WAIT_GNT: begin
- instr_addr = instr_addr_q;
- instr_req_o = 1'b1;
-
- if (branch_i) begin
- instr_addr = addr_i;
- addr_valid = 1'b1;
- end
-
- //~> granted request or not
- // If the instruction generated a PMP error, we may or may not
- // get granted (the external valid is suppressed by the error)
- // but we proceed to WAIT_RVALID to push the error to the fifo
- pf_fsm_ns = (instr_gnt_i || pmp_err_q) ? WAIT_RVALID : WAIT_GNT;
- end // case: WAIT_GNT
-
- // we wait for rvalid, after that we are ready to serve a new request
- WAIT_RVALID: begin
- instr_addr = fetch_addr;
-
- if (branch_i) begin
- instr_addr = addr_i;
- end
-
- if (req_i && (fifo_ready || branch_i)) begin
- // prepare for next request
-
- // Fake the rvalid for PMP errors to push the error to the fifo
- if (instr_rvalid_i || pmp_err_q) begin
- instr_req_o = 1'b1;
- fifo_valid = 1'b1;
- addr_valid = 1'b1;
-
- //~> granted request or not
- pf_fsm_ns = instr_gnt_i ? WAIT_RVALID : WAIT_GNT;
- end else begin
- // we are requested to abort our current request
- // we didn't get an rvalid yet, so wait for it
- if (branch_i) begin
- addr_valid = 1'b1;
- pf_fsm_ns = WAIT_ABORTED;
- end
- end
- end else begin
- // just wait for rvalid and go back to IDLE, no new request
-
- // Fake the rvalid for PMP errors to push the error to the fifo
- if (instr_rvalid_i || pmp_err_q) begin
- fifo_valid = 1'b1;
- pf_fsm_ns = IDLE;
- end
- end
- end // case: WAIT_RVALID
-
- // our last request was aborted, but we didn't yet get a rvalid and
- // there was no new request sent yet
- // we assume that req_i is set to high
- WAIT_ABORTED: begin
- instr_addr = instr_addr_q;
-
- if (branch_i) begin
- instr_addr = addr_i;
- addr_valid = 1'b1;
- end
-
- if (instr_rvalid_i) begin
- instr_req_o = 1'b1;
- // no need to send address, already done in WAIT_RVALID
-
- //~> granted request or not
- pf_fsm_ns = instr_gnt_i ? WAIT_RVALID : WAIT_GNT;
- end
- end
-
- default: begin
- pf_fsm_ns = pf_fsm_e'(1'bX);
- end
- endcase
+ assign rdata_outstanding_n[i] = (valid_req & instr_gnt_i &
+ (&rdata_outstanding_q[i-1:0])) |
+ rdata_outstanding_q[i];
+ assign branch_abort_n[i] = (branch_i & rdata_outstanding_q[i]) | branch_abort_q[i];
+ end
end
+ // Shift the entries down on each instr_rvalid_i
+ assign rdata_outstanding_s = rvalid_or_pmp_err ? {1'b0,rdata_outstanding_n[NUM_REQS-1:1]} :
+ rdata_outstanding_n;
+ assign branch_abort_s = rvalid_or_pmp_err ? {1'b0,branch_abort_n[NUM_REQS-1:1]} :
+ branch_abort_n;
+
+ // Push a new entry to the FIFO once complete (and not aborted by a branch)
+ assign fifo_valid = rvalid_or_pmp_err & ~branch_abort_q[0];
+
///////////////
// Registers //
///////////////
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
- pf_fsm_cs <= IDLE;
- instr_addr_q <= '0;
- pmp_err_q <= '0;
+ valid_req_q <= 1'b0;
+ hold_addr_q <= 1'b0;
+ rdata_outstanding_q <= 'b0;
+ branch_abort_q <= 'b0;
end else begin
- pf_fsm_cs <= pf_fsm_ns;
-
- if (addr_valid) begin
- instr_addr_q <= instr_addr;
- pmp_err_q <= instr_pmp_err_i;
- end
+ valid_req_q <= valid_req_d;
+ hold_addr_q <= hold_addr_d;
+ rdata_outstanding_q <= rdata_outstanding_s;
+ branch_abort_q <= branch_abort_s;
end
end
- /////////////////
- // Output Addr //
- /////////////////
- assign instr_addr_w_aligned = {instr_addr[31:2], 2'b00};
- assign instr_addr_o = instr_addr_w_aligned;
+ // CPU resets with a branch, so no need to reset these
+ always_ff @(posedge clk_i) begin
+ if (addr_valid) begin
+ instr_addr_q <= instr_addr;
+ pmp_err_q <= instr_pmp_err_i;
+ end
+ end
+
+ /////////////
+ // Outputs //
+ /////////////
+
+ assign instr_req_o = valid_req;
+ assign instr_addr_o = instr_addr_w_aligned;
+
+ ////////////////
+ // Assertions //
+ ////////////////
+
+`ifndef VERILATOR
+ // Code changes required to support > 2 outstanding requests
+ assert property (
+ @(posedge clk_i) disable iff (!rst_ni)
+ (NUM_REQS <= 2) );
+`endif
endmodule
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_tracer.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_tracer.sv
index 2048d46..69c096c 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_tracer.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_tracer.sv
@@ -191,6 +191,131 @@
end
endfunction // printCSRInstr
+ function void printCRInstr(input string mnemonic);
+ logic [4:0] rs1;
+ logic [4:0] rs2;
+ begin
+ rs1 = instr_i[11:7];
+ rs2 = instr_i[6:2];
+
+ if (rs2 == 5'b0) begin
+ regs_read.push_back('{rs1, rs1_value_i});
+ str = $sformatf("%-16s x%0d", mnemonic, rs1);
+ end else begin
+ regs_write.push_back('{rs1, 'x});
+ regs_read.push_back('{rs2, rs2_value_i});
+ str = $sformatf("%-16s x%0d, x%0d", mnemonic, rs1, rs2);
+ end
+ end
+ endfunction // printCRInstr
+
+ function void printCIInstr(input string mnemonic);
+ begin
+ regs_write.push_back('{rd, 'x});
+ str = $sformatf("%-16s x%0d, 0x%h", mnemonic, rd, {instr_i[12], instr_i[4:0]});
+ end
+ endfunction // printCIInstr
+
+ function void printCIWInstr(input string mnemonic);
+ logic [4:0] rd;
+ begin
+ rd = {2'b01, instr_i[4:2]};
+ regs_write.push_back('{rd, 'x});
+ str = $sformatf("%-16s x%0d, 0x%h", mnemonic, rd, {instr_i[10:7], instr_i[12:11], instr_i[5], instr_i[6]});
+ end
+ endfunction // printCIWInstr
+
+ function void printCBInstr(input string mnemonic);
+ logic [4:0] rs1;
+ logic [8:1] imm;
+ begin
+ rs1 = {2'b01, instr_i[9:7]};
+ if ((instr_i[15:13] == 3'b110) || (instr_i[15:13] == 3'b111)) begin
+ imm = {instr_i[12], instr_i[6:5], instr_i[2], instr_i[11:10], instr_i[4:3]};
+ regs_read.push_back('{rs1, rs1_value_i});
+ end else begin
+ imm = {instr_i[12], instr_i[6:2], 2'b00};
+ regs_write.push_back('{rs1, 'x});
+ end
+ str = $sformatf("%-16s x%0d, 0x%h", mnemonic, rs1, imm);
+ end
+ endfunction // printCBInstr
+
+ function void printCSInstr(input string mnemonic);
+ logic [4:0] rd;
+ logic [4:0] rs2;
+ begin
+ rd = {2'b01, instr_i[9:7]};
+ rs2 = {2'b01, instr_i[4:2]};
+
+ regs_write.push_back('{rd, 'x});
+ regs_read.push_back('{rs2, rs2_value_i});
+ str = $sformatf("%-16s x%0d, x%0d", mnemonic, rd, rs2);
+ end
+ endfunction // printCSInstr
+
+ function void printCJInstr(input string mnemonic);
+ logic [11:1] imm;
+ imm = {instr_i[12], instr_i[8], instr_i[10:9], instr_i[6],
+ instr_i[7], instr[2], instr[11], instr_i[5:3]};
+ begin
+ str = $sformatf("%-16s 0x%h", mnemonic, imm);
+ end
+ endfunction // printCJInstr
+
+ function void printCompressedLoadInstr(input string mnemonic);
+ logic [4:0] rd;
+ logic [4:0] rs1;
+ logic [7:0] imm;
+ mem_acc_t mem_acc;
+ begin
+ // Detect C.LW intruction
+ if (instr_i[1:0] == OPCODE_C0) begin
+ rd = {2'b01, instr_i[4:2]};
+ rs1 = {2'b01, instr_i[9:7]};
+ imm = {1'b0, instr[5], instr[12:10], instr[6], 2'b00};
+ end else begin
+ // LWSP instruction
+ rd = instr_i[11:7];
+ rs1 = 5'h2;
+ imm = {instr[3:2], instr[12], instr[6:4], 2'b00};
+ end
+ regs_write.push_back('{rd, 'x});
+ regs_read.push_back('{rs1, rs1_value_i});
+ str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rd, rs1, imm);
+ mem_acc.addr = ex_data_addr_i;
+ mem_acc.rdata = ex_data_rdata_i;
+ mem_access.push_back(mem_acc);
+ end
+ endfunction // printCompressedLoadInstr()
+
+ function void printCompressedStoreInstr(input string mnemonic);
+ logic [4:0] rs1;
+ logic [4:0] rs2;
+ logic [7:0] imm;
+ mem_acc_t mem_acc;
+ begin
+ // Detect C.SW instruction
+ if (instr_i[1:0] == OPCODE_C0) begin
+ rs1 = {2'b01, instr_i[9:7]};
+ rs2 = {2'b01, instr_i[4:2]};
+ imm = {1'b0, instr[5], instr[12:10], instr[6], 2'b0};
+ end else begin
+ // SWSP instruction
+ rs1 = 5'h2;
+ rs2 = instr_i[11:7];
+ imm = {instr[8:7], instr[12:9], 2'b00};
+ end
+ str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rs2, rs1, imm);
+ regs_read.push_back('{rs1, rs1_value_i});
+ regs_read.push_back('{rs2, rs2_value_i});
+ mem_acc.addr = ex_data_addr_i;
+ mem_acc.we = 1'b1;
+ mem_acc.wdata = ex_data_wdata_i;
+ mem_access.push_back(mem_acc);
+ end
+ endfunction // printCompressedStoreInstr
+
function void printLoadInstr();
string mnemonic;
logic [2:0] size;
@@ -317,8 +442,56 @@
trace.pc = pc_i;
trace.instr = instr_i;
- // separate case for 'nop' instruction to avoid overlapping with 'addi'
- if (instr_i == 32'h00_00_00_13) begin
+ // Check for compressed instructions
+ if (instr_i[1:0] != 2'b11) begin
+ // Separate case to avoid overlapping decoding
+ if ((instr_i[15:13] == 3'b100) && (instr_i[1:0] == 2'b10)) begin
+ if (instr_i[12]) begin
+ if (instr_i[11:2] == 10'h0) begin
+ trace.printMnemonic("c.ebreak");
+ end else if (instr_i[6:2] == 5'b0) begin
+ trace.printCRInstr("c.jalr");
+ end else begin
+ trace.printCRInstr("c.add");
+ end
+ end else begin
+ if (instr_i[6:2] == 5'h0) begin
+ trace.printCRInstr("c.jr");
+ end else begin
+ trace.printCRInstr("c.mv");
+ end
+ end
+ end else begin
+ // use casex instead of case inside due to ModelSim bug
+ unique casex (instr_i)
+ // C0 Opcodes
+ INSTR_CADDI4SPN: trace.printCIWInstr("c.addi4spn");
+ INSTR_CLW: trace.printCompressedLoadInstr("c.lw");
+ INSTR_CSW: trace.printCompressedStoreInstr("c.sw");
+ // C1 Opcodes
+ INSTR_CADDI: trace.printCIInstr("c.addi");
+ INSTR_CJAL: trace.printCJInstr("c.jal");
+ INSTR_CJ: trace.printCJInstr("c.j");
+ INSTR_CLI: trace.printCIInstr("c.li");
+ INSTR_CLUI: trace.printCIInstr("c.lui");
+ INSTR_CSRLI: trace.printCBInstr("c.srli");
+ INSTR_CSRAI: trace.printCBInstr("c.srai");
+ INSTR_CANDI: trace.printCBInstr("c.andi");
+ INSTR_CSUB: trace.printCSInstr("c.sub");
+ INSTR_CXOR: trace.printCSInstr("c.xor");
+ INSTR_COR: trace.printCSInstr("c.or");
+ INSTR_CAND: trace.printCSInstr("c.and");
+ INSTR_CBEQZ: trace.printCBInstr("c.beqz");
+ INSTR_CBNEZ: trace.printCBInstr("c.bnez");
+ // C2 Opcodes
+ INSTR_CSLLI: trace.printCIInstr("c.slli");
+ INSTR_CLWSP: trace.printCompressedLoadInstr("c.lwsp");
+ INSTR_SWSP: trace.printCompressedStoreInstr("c.swsp");
+ default: trace.printMnemonic("INVALID");
+ endcase // unique casex (instr_i)
+ end
+ end else if (instr_i == 32'h00_00_00_13) begin
+ // separate case for 'nop' instruction to avoid overlapping with 'addi'
trace.printMnemonic("nop");
end else begin
// use casex instead of case inside due to ModelSim bug
diff --git a/hw/vendor/lowrisc_ibex/rtl/ibex_tracer_pkg.sv b/hw/vendor/lowrisc_ibex/rtl/ibex_tracer_pkg.sv
index 88a159d..d834d2f 100644
--- a/hw/vendor/lowrisc_ibex/rtl/ibex_tracer_pkg.sv
+++ b/hw/vendor/lowrisc_ibex/rtl/ibex_tracer_pkg.sv
@@ -6,6 +6,10 @@
package ibex_tracer_pkg;
import ibex_pkg::*;
+parameter logic [1:0] OPCODE_C0 = 2'b00;
+parameter logic [1:0] OPCODE_C1 = 2'b01;
+parameter logic [1:0] OPCODE_C2 = 2'b10;
+
// instruction masks (for tracer)
parameter logic [31:0] INSTR_LUI = { 25'b?, {OPCODE_LUI } };
parameter logic [31:0] INSTR_AUIPC = { 25'b?, {OPCODE_AUIPC} };
@@ -71,4 +75,36 @@
// MISC-MEM
parameter logic [31:0] INSTR_FENCE = { 17'b?, 3'b000, 5'b?, {OPCODE_MISC_MEM} };
+// Compressed Instructions
+// C0
+parameter logic [15:0] INSTR_CADDI4SPN = { 3'b000, 11'b?, {OPCODE_C0} };
+parameter logic [15:0] INSTR_CLW = { 3'b010, 11'b?, {OPCODE_C0} };
+parameter logic [15:0] INSTR_CSW = { 3'b110, 11'b?, {OPCODE_C0} };
+
+// C1
+parameter logic [15:0] INSTR_CADDI = { 3'b000, 11'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CJAL = { 3'b001, 11'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CJ = { 3'b101, 11'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CLI = { 3'b010, 11'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CLUI = { 3'b011, 11'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CBEQZ = { 3'b110, 11'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CBNEZ = { 3'b111, 11'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CSRLI = { 3'b100, 1'b?, 2'b00, 8'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CSRAI = { 3'b100, 1'b?, 2'b01, 8'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CANDI = { 3'b100, 1'b?, 2'b10, 8'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CSUB = { 3'b100, 1'b0, 2'b11, 3'b?, 2'b00, 3'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CXOR = { 3'b100, 1'b0, 2'b11, 3'b?, 2'b01, 3'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_COR = { 3'b100, 1'b0, 2'b11, 3'b?, 2'b10, 3'b?, {OPCODE_C1} };
+parameter logic [15:0] INSTR_CAND = { 3'b100, 1'b0, 2'b11, 3'b?, 2'b11, 3'b?, {OPCODE_C1} };
+
+// C2
+parameter logic [15:0] INSTR_CSLLI = { 3'b000, 11'b?, {OPCODE_C2} };
+parameter logic [15:0] INSTR_CLWSP = { 3'b010, 11'b?, {OPCODE_C2} };
+parameter logic [15:0] INSTR_SWSP = { 3'b110, 11'b?, {OPCODE_C2} };
+parameter logic [15:0] INSTR_CMV = { 3'b100, 1'b0, 10'b?, {OPCODE_C2} };
+parameter logic [15:0] INSTR_CADD = { 3'b100, 1'b1, 10'b?, {OPCODE_C2} };
+parameter logic [15:0] INSTR_CEBREAK = { 3'b100, 1'b1, 5'b0, 5'b0, {OPCODE_C2} };
+parameter logic [15:0] INSTR_CJR = { 3'b100, 1'b0, 5'b?, 5'b0, {OPCODE_C2} };
+parameter logic [15:0] INSTR_CJALR = { 3'b100, 1'b1, 5'b?, 5'b0, {OPCODE_C2} };
+
endpackage