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