Repository: chipsalliance/caliptra-sw
Pull request #426 (2023-06-26 17:22:41)

Add double-bit ecc hw-model test
- Adds hw-model tests for double bit ecc errors from ICCM and DCCM
- Enabled on Verilator and SW-Emulator
- Added simple fence implementation to SW-Emulator


Patch:
diff --git a/hw-model/src/bus_logger.rs b/hw-model/src/bus_logger.rs
index 94047c21..8cdcd477 100644
--- a/hw-model/src/bus_logger.rs
+++ b/hw-model/src/bus_logger.rs
@@ -47,7 +47,7 @@ impl Bus for NullBus {
 }
 
 pub struct BusLogger<TBus: Bus> {
-    bus: TBus,
+    pub bus: TBus,
     pub log: Option<LogFile>,
 }
 impl<TBus: Bus> BusLogger<TBus> {
diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs
index f9eb08d5..5833fc6c 100644
--- a/hw-model/src/lib.rs
+++ b/hw-model/src/lib.rs
@@ -8,7 +8,9 @@ use std::{
 };
 
 use caliptra_emu_bus::Bus;
-use caliptra_hw_model_types::{EtrngResponse, RandomEtrngResponses, DEFAULT_CPTRA_OBF_KEY};
+use caliptra_hw_model_types::{
+    ErrorInjectionMode, EtrngResponse, RandomEtrngResponses, DEFAULT_CPTRA_OBF_KEY,
+};
 use zerocopy::{AsBytes, LayoutVerified, Unalign};
 
 use caliptra_registers::mbox;
@@ -577,6 +579,8 @@ pub trait HwModel {
 
     fn tracing_hint(&mut self, enable: bool);
 
+    fn ecc_error_injection(&mut self, _mode: ErrorInjectionMode) {}
+
     /// Executes `cmd` with request data `buf`. Returns `Ok(Some(_))` if
     /// the uC responded with data, `Ok(None)` if the uC indicated success
     /// without data, Err(ModelError::MailboxCmdFailed) if the microcontroller
diff --git a/hw-model/src/model_emulated.rs b/hw-model/src/model_emulated.rs
index 72d6a812..33c4c38a 100644
--- a/hw-model/src/model_emulated.rs
+++ b/hw-model/src/model_emulated.rs
@@ -14,6 +14,7 @@ use caliptra_emu_periph::ActionCb;
 use caliptra_emu_periph::ReadyForFwCb;
 use caliptra_emu_periph::{CaliptraRootBus, CaliptraRootBusArgs, SocToCaliptraBus, TbServicesCb};
 use caliptra_emu_types::{RvAddr, RvData, RvSize};
+use caliptra_hw_model_types::ErrorInjectionMode;
 
 use crate::bus_logger::BusLogger;
 use crate::bus_logger::LogFile;
@@ -174,4 +175,19 @@ impl crate::HwModel for ModelEmulated {
             writeln!(log, "pc=0x{pc:x}").unwrap();
         }))
     }
+
+    fn ecc_error_injection(&mut self, mode: ErrorInjectionMode) {
+        match mode {
+            ErrorInjectionMode::None => {
+                self.cpu.bus.bus.iccm.ram().borrow_mut().error_injection = 0;
+                self.cpu.bus.bus.dccm.error_injection = 0;
+            }
+            ErrorInjectionMode::IccmDoubleBitEcc => {
+                self.cpu.bus.bus.iccm.ram().borrow_mut().error_injection = 2;
+            }
+            ErrorInjectionMode::DccmDoubleBitEcc => {
+                self.cpu.bus.bus.dccm.error_injection = 8;
+            }
+        }
+    }
 }
diff --git a/hw-model/src/model_verilated.rs b/hw-model/src/model_verilated.rs
index ba896799..35ef25a2 100644
--- a/hw-model/src/model_verilated.rs
+++ b/hw-model/src/model_verilated.rs
@@ -5,6 +5,7 @@ use crate::EtrngResponse;
 use crate::{HwModel, TrngMode};
 use caliptra_emu_bus::Bus;
 use caliptra_emu_types::{RvAddr, RvData, RvSize};
+use caliptra_hw_model_types::ErrorInjectionMode;
 use caliptra_verilated::{AhbTxnType, CaliptraVerilated};
 use std::cell::RefCell;
 use std::io::Write;
@@ -257,6 +258,20 @@ impl crate::HwModel for ModelVerilated {
             }
         }
     }
+
+    fn ecc_error_injection(&mut self, mode: ErrorInjectionMode) {
+        match mode {
+            ErrorInjectionMode::None => {
+                self.v.input.sram_error_injection_mode = 0x0;
+            }
+            ErrorInjectionMode::IccmDoubleBitEcc => {
+                self.v.input.sram_error_injection_mode = 0x2;
+            }
+            ErrorInjectionMode::DccmDoubleBitEcc => {
+                self.v.input.sram_error_injection_mode = 0x8;
+            }
+        }
+    }
 }
 impl ModelVerilated {
     fn process_trng(&mut self) {
diff --git a/hw-model/types/src/lib.rs b/hw-model/types/src/lib.rs
index 43db3aaa..790ed0c5 100644
--- a/hw-model/types/src/lib.rs
+++ b/hw-model/types/src/lib.rs
@@ -209,6 +209,14 @@ impl<R: RngCore> Iterator for RandomEtrngResponses<R> {
     }
 }
 
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub enum ErrorInjectionMode {
+    #[default]
+    None,
+    IccmDoubleBitEcc,
+    DccmDoubleBitEcc,
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
diff --git a/sw-emulator/lib/bus/src/bus.rs b/sw-emulator/lib/bus/src/bus.rs
index 54b98f19..bbbb6ea2 100644
--- a/sw-emulator/lib/bus/src/bus.rs
+++ b/sw-emulator/lib/bus/src/bus.rs
@@ -16,6 +16,9 @@ use caliptra_emu_types::{RvAddr, RvData, RvSize};
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 pub enum BusError {
+    /// Instruction access exception
+    InstrAccessFault,
+
     /// Load address misaligned exception
     LoadAddrMisaligned,
 
diff --git a/sw-emulator/lib/bus/src/ram.rs b/sw-emulator/lib/bus/src/ram.rs
index 8a3d675d..98b510f2 100644
--- a/sw-emulator/lib/bus/src/ram.rs
+++ b/sw-emulator/lib/bus/src/ram.rs
@@ -17,6 +17,8 @@ use caliptra_emu_types::{RvAddr, RvData, RvSize};
 
 /// Read Only Memory Device
 pub struct Ram {
+    /// Inject double-bit ECC errors on read
+    pub error_injection: u8,
     /// Read Only Data
     data: Mem,
 }
@@ -29,6 +31,7 @@ impl Ram {
     /// * `data` - Data to be stored in the RAM
     pub fn new(data: Vec<u8>) -> Self {
         Self {
+            error_injection: 0,
             data: Mem::new(data),
         }
     }
@@ -63,6 +66,12 @@ impl Bus for Ram {
     /// * `BusException` - Exception with cause `BusExceptionCause::LoadAccessFault`
     ///                   or `BusExceptionCause::LoadAddrMisaligned`
     fn read(&mut self, size: RvSize, addr: RvAddr) -> Result<RvData, BusError> {
+        if 2 == self.error_injection {
+            return Err(BusError::InstrAccessFault);
+        }
+        if 8 == self.error_injection {
+            return Err(BusError::LoadAccessFault);
+        }
         match self.data.read(size, addr) {
             Ok(data) => Ok(data),
             Err(error) => Err(error.into()),
diff --git a/sw-emulator/lib/cpu/src/cpu.rs b/sw-emulator/lib/cpu/src/cpu.rs
index c1caab5a..54263954 100644
--- a/sw-emulator/lib/cpu/src/cpu.rs
+++ b/sw-emulator/lib/cpu/src/cpu.rs
@@ -230,6 +230,7 @@ impl<TBus: Bus> Cpu<TBus> {
         match self.bus.read(size, addr) {
             Ok(val) => Ok(val),
             Err(exception) => match exception {
+                BusError::InstrAccessFault => Err(RvException::instr_access_fault(addr)),
                 BusError::LoadAccessFault => Err(RvException::load_access_fault(addr)),
                 BusError::LoadAddrMisaligned => Err(RvException::load_addr_misaligned(addr)),
                 BusError::StoreAccessFault => Err(RvException::store_access_fault(addr)),
@@ -269,6 +270,7 @@ impl<TBus: Bus> Cpu<TBus> {
         match self.bus.write(size, addr, val) {
             Ok(val) => Ok(val),
             Err(exception) => match exception {
+                BusError::InstrAccessFault => Err(RvException::instr_access_fault(addr)),
                 BusError::LoadAccessFault => Err(RvException::load_access_fault(addr)),
                 BusError::LoadAddrMisaligned => Err(RvException::load_addr_misaligned(addr)),
                 BusError::StoreAccessFault => Err(RvException::store_access_fault(addr)),
@@ -294,6 +296,7 @@ impl<TBus: Bus> Cpu<TBus> {
             _ => match self.bus.read(size, addr) {
                 Ok(val) => Ok(val),
                 Err(exception) => match exception {
+                    BusError::InstrAccessFault => Err(RvException::instr_access_fault(addr)),
                     BusError::LoadAccessFault => Err(RvException::instr_access_fault(addr)),
                     BusError::LoadAddrMisaligned => Err(RvException::instr_addr_misaligned(addr)),
                     BusError::StoreAccessFault => Err(RvException::store_access_fault(addr)),
diff --git a/sw-emulator/lib/cpu/src/instr/fence.rs b/sw-emulator/lib/cpu/src/instr/fence.rs
new file mode 100644
index 00000000..78433d39
--- /dev/null
+++ b/sw-emulator/lib/cpu/src/instr/fence.rs
@@ -0,0 +1,47 @@
+/*++
+
+Licensed under the Apache-2.0 license.
+
+File Name:
+
+    fence.rs
+
+Abstract:
+
+    File contains implementation of system instructions.
+
+--*/
+
+use crate::cpu::Cpu;
+use crate::types::{RvInstr32FenceFunct3, RvInstr32I, RvInstr32Opcode};
+use caliptra_emu_bus::Bus;
+use caliptra_emu_types::RvException;
+
+impl<TBus: Bus> Cpu<TBus> {
+    /// Execute Fence Instructions
+    ///
+    /// # Arguments
+    ///
+    /// * `instr_tracer` - Instruction tracer
+    ///
+    /// # Error
+    ///
+    /// * `RvException` - Exception encountered during instruction execution
+    pub fn exec_fence_instr(&mut self, instr: u32) -> Result<(), RvException> {
+        // Decode the instruction
+        let instr = RvInstr32I(instr);
+        assert_eq!(instr.opcode(), RvInstr32Opcode::Fence);
+
+        match instr.funct3().into() {
+            RvInstr32FenceFunct3::Fence => {
+                // Do nothing
+                Ok(())
+            }
+            RvInstr32FenceFunct3::FenceI => {
+                // Do nothing
+                Ok(())
+            }
+            _ => Err(RvException::illegal_instr(instr.0)),
+        }
+    }
+}
diff --git a/sw-emulator/lib/cpu/src/instr/mod.rs b/sw-emulator/lib/cpu/src/instr/mod.rs
index 1d04c52c..3a386a3a 100644
--- a/sw-emulator/lib/cpu/src/instr/mod.rs
+++ b/sw-emulator/lib/cpu/src/instr/mod.rs
@@ -18,6 +18,7 @@ References:
 mod auipc;
 mod branch;
 mod compression;
+mod fence;
 mod jal;
 mod jalr;
 mod load;
@@ -140,6 +141,7 @@ impl<TBus: Bus> Cpu<TBus> {
             RvInstr32Opcode::Jalr => self.exec_jalr_instr(instr)?,
             RvInstr32Opcode::Jal => self.exec_jal_instr(instr)?,
             RvInstr32Opcode::System => self.exec_system_instr(instr)?,
+            RvInstr32Opcode::Fence => self.exec_fence_instr(instr)?,
             _ => Err(RvException::illegal_instr(instr))?,
         }
         Ok(())
diff --git a/sw-emulator/lib/cpu/src/types.rs b/sw-emulator/lib/cpu/src/types.rs
index eb1ddfd9..9e02fb0b 100644
--- a/sw-emulator/lib/cpu/src/types.rs
+++ b/sw-emulator/lib/cpu/src/types.rs
@@ -53,6 +53,9 @@ emu_enum! {
 
         /// System Opcode
         System = 0b111_0011,
+
+        /// Fence Opcode
+        Fence = 0b000_1111,
     };
     Invalid
 }
@@ -249,6 +252,21 @@ emu_enum! {
     Invalid
 }
 
+emu_enum! {
+    /// RISCV Fence Opcode Functions
+    #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
+    pub RvInstr32FenceFunct3;
+    u32;
+    {
+        /// FENCE
+        Fence =  0b000,
+
+        /// FENCE.I
+        FenceI = 0b001,
+    };
+    Invalid
+}
+
 emu_enum! {
     #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
     pub RvInstr32OpFunct10;
diff --git a/sw-emulator/lib/periph/src/asym_ecc384.rs b/sw-emulator/lib/periph/src/asym_ecc384.rs
index 005988c1..39d66dae 100644
--- a/sw-emulator/lib/periph/src/asym_ecc384.rs
+++ b/sw-emulator/lib/periph/src/asym_ecc384.rs
@@ -478,9 +478,9 @@ impl AsymEcc384 {
 
         let result = self.key_vault.read_key(key_id, key_usage);
         let (key_read_result, key) = match result.err() {
-            Some(BusError::LoadAccessFault) | Some(BusError::LoadAddrMisaligned) => {
-                (KeyReadStatus::ERROR::KV_READ_FAIL.value, None)
-            }
+            Some(BusError::LoadAccessFault)
+            | Some(BusError::LoadAddrMisaligned)
+            | Some(BusError::InstrAccessFault) => (KeyReadStatus::ERROR::KV_READ_FAIL.value, None),
             Some(BusError::StoreAccessFault) | Some(BusError::StoreAddrMisaligned) => {
                 (KeyReadStatus::ERROR::KV_WRITE_FAIL.value, None)
             }
@@ -511,9 +511,9 @@ impl AsymEcc384 {
 
         let result = self.key_vault.read_key(key_id, key_usage);
         let (seed_read_result, seed) = match result.err() {
-            Some(BusError::LoadAccessFault) | Some(BusError::LoadAddrMisaligned) => {
-                (KeyReadStatus::ERROR::KV_READ_FAIL.value, None)
-            }
+            Some(BusError::LoadAccessFault)
+            | Some(BusError::LoadAddrMisaligned)
+            | Some(BusError::InstrAccessFault) => (KeyReadStatus::ERROR::KV_READ_FAIL.value, None),
             Some(BusError::StoreAccessFault) | Some(BusError::StoreAddrMisaligned) => {
                 (KeyReadStatus::ERROR::KV_WRITE_FAIL.value, None)
             }
@@ -549,9 +549,9 @@ impl AsymEcc384 {
             )
             .err()
         {
-            Some(BusError::LoadAccessFault) | Some(BusError::LoadAddrMisaligned) => {
-                KeyWriteStatus::ERROR::KV_READ_FAIL.value
-            }
+            Some(BusError::LoadAccessFault)
+            | Some(BusError::LoadAddrMisaligned)
+            | Some(BusError::InstrAccessFault) => KeyWriteStatus::ERROR::KV_READ_FAIL.value,
             Some(BusError::StoreAccessFault) | Some(BusError::StoreAddrMisaligned) => {
                 KeyWriteStatus::ERROR::KV_WRITE_FAIL.value
             }
diff --git a/sw-emulator/lib/periph/src/hash_sha512.rs b/sw-emulator/lib/periph/src/hash_sha512.rs
index 20000983..b210f282 100644
--- a/sw-emulator/lib/periph/src/hash_sha512.rs
+++ b/sw-emulator/lib/periph/src/hash_sha512.rs
@@ -474,7 +474,9 @@ impl HashSha512 {
             Ok(self.key_vault.read_pcr(key_id))
         };
         let (block_read_result, data) = match result.err() {
-            Some(BusError::LoadAccessFault) | Some(BusError::LoadAddrMisaligned) => {
+            Some(BusError::LoadAccessFault)
+            | Some(BusError::LoadAddrMisaligned)
+            | Some(BusError::InstrAccessFault) => {
                 (BlockReadStatus::ERROR::KV_READ_FAIL.value, None)
             }
             Some(BusError::StoreAccessFault) | Some(BusError::StoreAddrMisaligned) => {
@@ -541,9 +543,9 @@ impl HashSha512 {
             )
             .err()
         {
-            Some(BusError::LoadAccessFault) | Some(BusError::LoadAddrMisaligned) => {
-                HashWriteStatus::ERROR::KV_READ_FAIL.value
-            }
+            Some(BusError::LoadAccessFault)
+            | Some(BusError::LoadAddrMisaligned)
+            | Some(BusError::InstrAccessFault) => HashWriteStatus::ERROR::KV_READ_FAIL.value,
             Some(BusError::StoreAccessFault) | Some(BusError::StoreAddrMisaligned) => {
                 HashWriteStatus::ERROR::KV_WRITE_FAIL.value
             }
diff --git a/sw-emulator/lib/periph/src/hmac_sha384.rs b/sw-emulator/lib/periph/src/hmac_sha384.rs
index 1a339021..c4df16ba 100644
--- a/sw-emulator/lib/periph/src/hmac_sha384.rs
+++ b/sw-emulator/lib/periph/src/hmac_sha384.rs
@@ -463,9 +463,9 @@ impl HmacSha384 {
 
         let result = self.key_vault.read_key(key_id, key_usage);
         let (key_read_result, key) = match result.err() {
-            Some(BusError::LoadAccessFault) | Some(BusError::LoadAddrMisaligned) => {
-                (KeyReadStatus::ERROR::KV_READ_FAIL.value, None)
-            }
+            Some(BusError::LoadAccessFault)
+            | Some(BusError::LoadAddrMisaligned)
+            | Some(BusError::InstrAccessFault) => (KeyReadStatus::ERROR::KV_READ_FAIL.value, None),
             Some(BusError::StoreAccessFault) | Some(BusError::StoreAddrMisaligned) => {
                 (KeyReadStatus::ERROR::KV_WRITE_FAIL.value, None)
             }
@@ -496,9 +496,9 @@ impl HmacSha384 {
         key_usage.set_hmac_data(true);
 
         let error_code = match self.key_vault.read_key_as_data(key_id, key_usage) {
-            Err(BusError::LoadAccessFault) | Err(BusError::LoadAddrMisaligned) => {
-                KeyReadStatus::ERROR::KV_READ_FAIL.value
-            }
+            Err(BusError::LoadAccessFault)
+            | Err(BusError::LoadAddrMisaligned)
+            | Err(BusError::InstrAccessFault) => KeyReadStatus::ERROR::KV_READ_FAIL.value,
             Err(BusError::StoreAccessFault) | Err(BusError::StoreAddrMisaligned) => {
                 KeyReadStatus::ERROR::KV_WRITE_FAIL.value
             }
@@ -556,9 +556,9 @@ impl HmacSha384 {
             )
             .err()
         {
-            Some(BusError::LoadAccessFault) | Some(BusError::LoadAddrMisaligned) => {
-                TagWriteStatus::ERROR::KV_READ_FAIL.value
-            }
+            Some(BusError::LoadAccessFault)
+            | Some(BusError::LoadAddrMisaligned)
+            | Some(BusError::InstrAccessFault) => TagWriteStatus::ERROR::KV_READ_FAIL.value,
             Some(BusError::StoreAccessFault) | Some(BusError::StoreAddrMisaligned) => {
                 TagWriteStatus::ERROR::KV_WRITE_FAIL.value
             }


Test patch:
diff --git a/hw-latest/verilated/caliptra_verilated.cpp b/hw-latest/verilated/caliptra_verilated.cpp
index d84a4b7e..3236f285 100644
--- a/hw-latest/verilated/caliptra_verilated.cpp
+++ b/hw-latest/verilated/caliptra_verilated.cpp
@@ -68,6 +68,8 @@ void caliptra_verilated_eval(struct caliptra_verilated* model,
   v->itrng_data = in->itrng_data;
   v->itrng_valid = in->itrng_valid;
 
+  v->sram_error_injection_mode = in->sram_error_injection_mode;
+
   if (model->tfp.get()) {
     model->tfp->dump(model->sim_time++);
   }
diff --git a/hw-latest/verilated/caliptra_verilated.h b/hw-latest/verilated/caliptra_verilated.h
index 016aef8c..6b0fe914 100644
--- a/hw-latest/verilated/caliptra_verilated.h
+++ b/hw-latest/verilated/caliptra_verilated.h
@@ -29,6 +29,8 @@ struct caliptra_verilated_sig_in {
 
   uint8_t itrng_data;
   bool itrng_valid;
+
+  uint8_t sram_error_injection_mode;
 };
 
 struct caliptra_verilated_sig_out {
diff --git a/hw-latest/verilated/caliptra_verilated.sv b/hw-latest/verilated/caliptra_verilated.sv
index 09a9e1f2..86ddeb14 100644
--- a/hw-latest/verilated/caliptra_verilated.sv
+++ b/hw-latest/verilated/caliptra_verilated.sv
@@ -45,6 +45,8 @@ module caliptra_verilated (
     input  bit [3:0]       itrng_data,
     input  bit             itrng_valid,
 
+    input bit [3:0] sram_error_injection_mode,
+
     output bit ready_for_fuses,
     output bit ready_for_fw_push,
 
@@ -190,7 +192,19 @@ assign uc_hrdata = caliptra_top_dut.rvtop.lsu_hrdata;
 assign uc_hready = caliptra_top_dut.rvtop.lsu_hready;
 assign uc_hresp = caliptra_top_dut.rvtop.lsu_hresp;
 
+// Decode:
+//  [0] - Single bit, ICCM Error Injection
+//  [1] - Double bit, ICCM Error Injection
+//  [2] - Single bit, DCCM Error Injection
+//  [3] - Double bit, DCCM Error Injection
+veer_sram_error_injection_mode_t veer_sram_error_injection_mode;
+assign veer_sram_error_injection_mode.iccm_single_bit_error = sram_error_injection_mode[0];
+assign veer_sram_error_injection_mode.iccm_double_bit_error = sram_error_injection_mode[1];
+assign veer_sram_error_injection_mode.dccm_single_bit_error = sram_error_injection_mode[2];
+assign veer_sram_error_injection_mode.dccm_double_bit_error = sram_error_injection_mode[3];
+
 caliptra_veer_sram_export veer_sram_export_inst (
+    .sram_error_injection_mode(sram_error_injection_mode),
     .el2_mem_export(el2_mem_export.top)
 );
 
diff --git a/hw-latest/verilated/src/bindings/real.rs b/hw-latest/verilated/src/bindings/real.rs
index 92f8f4ad..141455c2 100644
--- a/hw-latest/verilated/src/bindings/real.rs
+++ b/hw-latest/verilated/src/bindings/real.rs
@@ -27,6 +27,7 @@ pub struct caliptra_verilated_sig_in {
     pub imem_wdata: u64,
     pub itrng_data: u8,
     pub itrng_valid: bool,
+    pub sram_error_injection_mode: u8,
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
diff --git a/hw-model/test-fw/Cargo.toml b/hw-model/test-fw/Cargo.toml
index b0905c32..67be0e4e 100644
--- a/hw-model/test-fw/Cargo.toml
+++ b/hw-model/test-fw/Cargo.toml
@@ -49,4 +49,15 @@ required-features = ["riscv"]
 [[bin]]
 name = "test_write_to_rom"
 path = "test_write_to_rom.rs"
-required-features = ["riscv"]
\ No newline at end of file
+required-features = ["riscv"]
+
+[[bin]]
+name = "test_iccm_double_bit_ecc"
+path = "test_iccm_double_bit_ecc.rs"
+required-features = ["riscv"]
+
+[[bin]]
+name = "test_dccm_double_bit_ecc"
+path = "test_dccm_double_bit_ecc.rs"
+required-features = ["riscv"]
+
diff --git a/hw-model/test-fw/test_dccm_double_bit_ecc.rs b/hw-model/test-fw/test_dccm_double_bit_ecc.rs
new file mode 100644
index 00000000..500f0378
--- /dev/null
+++ b/hw-model/test-fw/test_dccm_double_bit_ecc.rs
@@ -0,0 +1,40 @@
+// Licensed under the Apache-2.0 license
+
+#![no_main]
+#![no_std]
+
+use ::core::arch::global_asm;
+
+// Needed to bring in startup code
+#[allow(unused)]
+use caliptra_test_harness;
+
+#[panic_handler]
+pub fn panic(_info: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+global_asm!(
+    r#"
+main:
+    lui t0, 0x50000
+    li  t2, 0x100
+
+writeloop:
+// Write data to DCCM t2/4 times
+    sw t0, 0(t0)
+    addi t0, t0, 4
+    addi t2, t2, -4
+    bgtz t2, writeloop
+
+    lui t0, 0x50000
+    li  t2, 0x100
+readloop:
+// Read data from DCCM t2/4 times
+    lw a0, 0(t0)
+    addi t0, t0, 4
+    addi t2, t2, -4
+    bgtz t2, readloop
+    ret
+"#
+);
diff --git a/hw-model/test-fw/test_iccm_double_bit_ecc.rs b/hw-model/test-fw/test_iccm_double_bit_ecc.rs
new file mode 100644
index 00000000..910b7bcb
--- /dev/null
+++ b/hw-model/test-fw/test_iccm_double_bit_ecc.rs
@@ -0,0 +1,40 @@
+// Licensed under the Apache-2.0 license
+
+#![no_main]
+#![no_std]
+
+use ::core::arch::global_asm;
+
+// Needed to bring in startup code
+#[allow(unused)]
+use caliptra_test_harness;
+
+#[panic_handler]
+pub fn panic(_info: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+global_asm!(
+    r#"
+main:
+    lui t0, 0x40000
+    lui t1, 0x40000
+    li  t2, 0x100
+
+    // Write (nop, nop) to ICCM t2/4 times
+    li a0, 0x00010001
+loop:
+    sw a0, 0(t1)
+    addi t1, t1, 4
+    addi t2, t2, -4
+    bgtz t2, loop
+
+    // Write (ret) to ICCM
+    li a0, 0x00008082
+    sw a0, 0(t1)
+    fence
+
+    // Jump to ICCM
+    jr t0
+"#
+);
diff --git a/hw-model/tests/model_tests.rs b/hw-model/tests/model_tests.rs
index d8973ec7..4a32c4ac 100644
--- a/hw-model/tests/model_tests.rs
+++ b/hw-model/tests/model_tests.rs
@@ -2,6 +2,7 @@
 
 use caliptra_builder::FwId;
 use caliptra_hw_model::{BootParams, DefaultHwModel, HwModel, InitParams};
+use caliptra_hw_model_types::ErrorInjectionMode;
 use caliptra_test_harness_types as harness;
 
 const BASE_FWID: FwId = FwId {
@@ -113,7 +114,7 @@ fn test_invalid_instruction_exception_failure() {
     let ext_info = harness::ExtErrorInfo::from(soc_ifc.cptra_fw_extended_error_info().read());
     assert_eq!(
         ext_info.mcause,
-        harness::NMI_CAUSE_ILLEGAL_INSTRUCTION_ERROR
+        harness::EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION_ERROR
     );
 }
 
@@ -132,3 +133,50 @@ fn test_write_to_rom() {
         harness::ERROR_EXCEPTION
     );
 }
+
+#[test]
+fn test_iccm_double_bit_ecc_nmi_failure() {
+    let elf = caliptra_builder::build_firmware_elf(&FwId {
+        bin_name: "test_iccm_double_bit_ecc",
+        ..BASE_FWID
+    })
+    .unwrap();
+
+    let mut model = run_fw_elf(&elf);
+
+    model.ecc_error_injection(ErrorInjectionMode::IccmDoubleBitEcc);
+
+    model.step_until_exit_success().unwrap_err();
+    let soc_ifc: caliptra_registers::soc_ifc::RegisterBlock<_> = model.soc_ifc();
+    assert_eq!(
+        soc_ifc.cptra_fw_error_non_fatal().read(),
+        harness::ERROR_EXCEPTION
+    );
+    let ext_info = harness::ExtErrorInfo::from(soc_ifc.cptra_fw_extended_error_info().read());
+    assert_eq!(
+        ext_info.mcause,
+        harness::EXCEPTION_CAUSE_INSTRUCTION_ACCESS_FAULT
+    );
+}
+
+#[test]
+fn test_dccm_double_bit_ecc_nmi_failure() {
+    let elf = caliptra_builder::build_firmware_elf(&FwId {
+        bin_name: "test_dccm_double_bit_ecc",
+        ..BASE_FWID
+    })
+    .unwrap();
+
+    let mut model = run_fw_elf(&elf);
+
+    model.ecc_error_injection(ErrorInjectionMode::DccmDoubleBitEcc);
+
+    model.step_until_exit_success().unwrap_err();
+    let soc_ifc: caliptra_registers::soc_ifc::RegisterBlock<_> = model.soc_ifc();
+    assert_eq!(
+        soc_ifc.cptra_fw_error_non_fatal().read(),
+        harness::ERROR_EXCEPTION
+    );
+    let ext_info = harness::ExtErrorInfo::from(soc_ifc.cptra_fw_extended_error_info().read());
+    assert_eq!(ext_info.mcause, harness::EXCEPTION_CAUSE_LOAD_ACCESS_FAULT);
+}
diff --git a/sw-emulator/compliance-test/src/main.rs b/sw-emulator/compliance-test/src/main.rs
index 4124183a..1c84eab0 100644
--- a/sw-emulator/compliance-test/src/main.rs
+++ b/sw-emulator/compliance-test/src/main.rs
@@ -42,7 +42,7 @@ static TESTS_TO_RUN: &[TestInfo] = &[
     TestInfo {extension: "I", name: "blt-01"},
     TestInfo {extension: "I", name: "bltu-01"},
     TestInfo {extension: "I", name: "bne-01"},
-    //TestInfo {extension: "I", name: "fence-01"},
+    TestInfo {extension: "I", name: "fence-01"},
     TestInfo {extension: "I", name: "jal-01"},
     TestInfo {extension: "I", name: "jalr-01"},
     TestInfo {extension: "I", name: "lb-align-01"},
diff --git a/test-harness/types/src/lib.rs b/test-harness/types/src/lib.rs
index 196f3506..b7011f37 100644
--- a/test-harness/types/src/lib.rs
+++ b/test-harness/types/src/lib.rs
@@ -6,8 +6,12 @@ pub const ERROR_EXCEPTION: u32 = 0x0300_0002;
 pub const ERROR_NMI: u32 = 0x0300_0003;
 
 // From RISC-V_VeeR_EL2_PRM.pdf
+// Exception causes
+pub const EXCEPTION_CAUSE_INSTRUCTION_ACCESS_FAULT: u32 = 0x0000_0001;
+pub const EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION_ERROR: u32 = 0x0000_0002;
+pub const EXCEPTION_CAUSE_LOAD_ACCESS_FAULT: u32 = 0x0000_0005;
+// NMI causes
 pub const NMI_CAUSE_PIN_ASSERTION: u32 = 0x0000_0000;
-pub const NMI_CAUSE_ILLEGAL_INSTRUCTION_ERROR: u32 = 0x0000_0002;
 pub const NMI_CAUSE_DBUS_STORE_ERROR: u32 = 0xf000_0000;
 pub const NMI_CAUSE_DBUS_NON_BLOCKING_LOAD_ERROR: u32 = 0xf000_0001;
 pub const NMI_CAUSE_FAST_INTERRUPT_DOUBLE_BIT_ECC_ERROR: u32 = 0xf000_1000;