[flash] Improve flash ECC handling based on transasction attribute

Refer to #4812

- When an instruction type transaction sees an uncorrectable error, it is always returned to the source as an error.

- When a data type transaction sees an uncorrectable error, it is returned based on software configuration (this may be further improved to lv_hw_debug_en.  Discussions are ongoing).

- Regardless of the error return type to the source, the error is always collected in the flash_ctrl and alerts generated for consumption.

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl.hjson b/hw/ip/flash_ctrl/data/flash_ctrl.hjson
index 978d252..7cb3559 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl.hjson
+++ b/hw/ip/flash_ctrl/data/flash_ctrl.hjson
@@ -27,6 +27,7 @@
     { name: "rd_full",    desc: "Read FIFO full" },
     { name: "rd_lvl",     desc: "Read FIFO filled to level" },
     { name: "op_done",    desc: "Operation complete" },
+    { name: "err",        desc: "Error encountered"},
   ],
 
   alert_list: [
@@ -1303,8 +1304,8 @@
           This is separate from !!OP_STATUS, which is used to indicate the current state of the software initiated
           flash operation.
         '''
-        swaccess: "rw",
-        hwaccess: "hwo",
+        swaccess: "rw1c",
+        hwaccess: "hrw",
         fields: [
           { bits: "0",
             name: "flash_err",
@@ -1329,14 +1330,14 @@
             name: "ecc_single_err",
             desc: '''
               Flash access has encountered a single bit ECC error.
-              Please see !!ECC_ERR_ADDR for exact address.
+              Please see !!ECC_SINGLE_ERR_ADDR for exact address.
             '''
           },
           { bits: "4",
             name: "ecc_multi_err",
             desc: '''
               Flash access has encountered a multi bit ECC error.
-              Please see !!ECC_ERR_ADDR for exact address.
+              Please see !!ECC_MULTI_ERR_ADDR for exact address.
             '''
           },
         ]
@@ -1353,10 +1354,22 @@
         ]
       },
 
+      { name: "ECC_SINGLE_ERR_CNT",
+        desc: "Total number of single bit ECC error count",
+        swaccess: "rw1c",
+        hwaccess: "hrw",
+        fields: [
+          { bits: "7:0",
+            desc: "This count will not wrap when saturated",
+            resval: 0,
+          },
+        ]
+      },
+
       { multireg: {
-          cname: "ECC_ERR"
-          name: "ECC_ERR_ADDR",
-          desc: "ecc error address",
+          cname: "ECC_SINGLE_ERR"
+          name: "ECC_SINGLE_ERR_ADDR",
+          desc: "Latest single bit error address (correctable)",
           count: "RegNumBanks",
           swaccess: "ro",
           hwaccess: "hwo",
@@ -1368,6 +1381,76 @@
         }
       },
 
+      { name: "ECC_MULTI_ERR_CNT",
+        desc: "Total number of multi bit ECC error count",
+        swaccess: "rw1c",
+        hwaccess: "hrw",
+        fields: [
+          { bits: "7:0",
+            desc: "This count will not wrap when saturated",
+            resval: 0,
+          },
+        ]
+      },
+
+      { multireg: {
+          cname: "ECC_MULTI_ERR"
+          name: "ECC_MULTI_ERR_ADDR",
+          desc: "Latest multi bit error address (uncorrectable)",
+          count: "RegNumBanks",
+          swaccess: "ro",
+          hwaccess: "hwo",
+        }
+      },
+
+      { name: "PHY_ERR_CFG_REGWEN",
+        swaccess: "rw0c",
+        hwaccess: "none",
+        desc: '''
+        Controls the configurability of the !!PHY_ERR_CFG register.
+        ''',
+
+        fields: [
+          { bits: "0",
+            name: "EN",
+            desc: '''
+              Configuration enable.
+            ''',
+            resval: "1",
+          },
+        ]
+      },
+
+      { name: "PHY_ERR_CFG",
+        regwen: "PHY_ERR_CFG_REGWEN",
+        desc: "Phy error configuration",
+        swaccess: "rw",
+        hwaccess: "hro",
+        fields: [
+          { bits: "0",
+            name: "ECC_MULTI_ERR_DATA_EN",
+            desc: '''
+              Transaction error response for ECC multi bit errors in data type transactions.
+              When set to 1, the error response is propagated to the source.
+              When set to 0, the error response is silenced.
+
+              Note this register only controls whether an error response is generated to the source
+              for data transactions.  It does not control whether !!ERR_CODE and its associated error
+              status register collects error information.
+
+              This can be used by software to dictate whether a multi-bit error should be reflected as a
+              bus error to bus hosts or the flash controller.
+
+              The reason this may not be desired is because software may want to triage the actual bit change,
+              and that would not be possible if an error response is always generated.  The hosts may simply
+              react on the error and discard the data.
+            '''
+          },
+        ]
+        tags: [ // alert triggers should be tested by directed tests
+               "excl:CsrAllTests:CsrExclWrite"]
+      },
+
       { name: "PHY_ALERT_CFG",
         desc: "Phy alert configuration",
         swaccess: "rw",
@@ -1415,6 +1498,7 @@
           { bits: "31:0", name: "data",  desc: "Flash ctrl scratch register" },
         ]
       },
+
       { name: "FIFO_LVL",
         desc: "Programmable depth where FIFOs should generate interrupts",
         swaccess: "rw",
@@ -1450,7 +1534,6 @@
             desc: '''
               Active high resets for both program and read FIFOs.  This is especially useful after the controller
               encounters an error of some kind.
-
               This bit will hold the FIFO in reset as long as it is set.
               '''
             resval: "0"
@@ -1490,6 +1573,7 @@
         },
       },
     ],
+
     prim: [
 //      { name: "DUMMY",
 //        desc: "Dummy unused register",
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl.hjson.tpl b/hw/ip/flash_ctrl/data/flash_ctrl.hjson.tpl
index 1056d8c..13bf1ee 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl.hjson.tpl
+++ b/hw/ip/flash_ctrl/data/flash_ctrl.hjson.tpl
@@ -34,6 +34,7 @@
     { name: "rd_full",    desc: "Read FIFO full" },
     { name: "rd_lvl",     desc: "Read FIFO filled to level" },
     { name: "op_done",    desc: "Operation complete" },
+    { name: "err",        desc: "Error encountered"},
   ],
 
   alert_list: [
@@ -812,6 +813,39 @@
         ]
       },
 
+      { name: "ERR_CODE_INTR_EN",
+        desc: '''
+          Interrupt enable mask for error code.
+          Only enabled bits will generate interrupts.
+          Bits that are not enabled will still be reflected in the !!ERR_CODE register, but will not trigger
+          an interrupt
+        '''
+        swaccess: "rw",
+        hwaccess: "hro",
+        fields: [
+          { bits: "0",
+            name: "flash_err_en",
+            desc: "interrupt mask for flash error"
+          },
+          { bits: "1",
+            name: "flash_alert_en",
+            desc: "interrupt mask for flash alert"
+          },
+          { bits: "2",
+            name: "mp_err",
+            desc: "interrupt mask for memory properties error"
+          },
+          { bits: "3",
+            name: "ecc_single_err",
+            desc: "interrupt mask for single bit ecc error"
+          },
+          { bits: "4",
+            name: "ecc_multi_err",
+            desc: "interrupt mask for multiple bits ecc error"
+          },
+        ]
+      },
+
       { name: "ERR_CODE",
         desc: '''
           Flash error code register.
@@ -819,8 +853,8 @@
           This is separate from !!OP_STATUS, which is used to indicate the current state of the software initiated
           flash operation.
         '''
-        swaccess: "rw",
-        hwaccess: "hwo",
+        swaccess: "rw1c",
+        hwaccess: "hrw",
         fields: [
           { bits: "0",
             name: "flash_err",
@@ -845,14 +879,14 @@
             name: "ecc_single_err",
             desc: '''
               Flash access has encountered a single bit ECC error.
-              Please see !!ECC_ERR_ADDR for exact address.
+              Please see !!ECC_SINGLE_ERR_ADDR for exact address.
             '''
           },
           { bits: "4",
             name: "ecc_multi_err",
             desc: '''
               Flash access has encountered a multi bit ECC error.
-              Please see !!ECC_ERR_ADDR for exact address.
+              Please see !!ECC_MULTI_ERR_ADDR for exact address.
             '''
           },
         ]
@@ -869,10 +903,22 @@
         ]
       },
 
+      { name: "ECC_SINGLE_ERR_CNT",
+        desc: "Total number of single bit ECC error count",
+        swaccess: "rw1c",
+        hwaccess: "hrw",
+        fields: [
+          { bits: "7:0",
+            desc: "This count will not wrap when saturated",
+            resval: 0,
+          },
+        ]
+      },
+
       { multireg: {
-          cname: "ECC_ERR"
-          name: "ECC_ERR_ADDR",
-          desc: "ecc error address",
+          cname: "ECC_SINGLE_ERR"
+          name: "ECC_SINGLE_ERR_ADDR",
+          desc: "Latest single bit error address (correctable)",
           count: "RegNumBanks",
           swaccess: "ro",
           hwaccess: "hwo",
@@ -884,6 +930,81 @@
         }
       },
 
+      { name: "ECC_MULTI_ERR_CNT",
+        desc: "Total number of multi bit ECC error count",
+        swaccess: "rw1c",
+        hwaccess: "hrw",
+        fields: [
+          { bits: "7:0",
+            desc: "This count will not wrap when saturated",
+            resval: 0,
+          },
+        ]
+      },
+
+      { multireg: {
+          cname: "ECC_MULTI_ERR"
+          name: "ECC_MULTI_ERR_ADDR",
+          desc: "Latest multi bit error address (uncorrectable)",
+          count: "RegNumBanks",
+          swaccess: "ro",
+          hwaccess: "hwo",
+          fields: [
+            { bits: "${total_byte_width-1}:0",
+              resval: 0,
+            },
+          ]
+        }
+      },
+
+      { name: "PHY_ERR_CFG_REGWEN",
+        swaccess: "rw0c",
+        hwaccess: "none",
+        desc: '''
+        Controls the configurability of the !!PHY_ERR_CFG register.
+        ''',
+
+        fields: [
+          { bits: "0",
+            name: "EN",
+            desc: '''
+              Configuration enable.
+            ''',
+            resval: "1",
+          },
+        ]
+      },
+
+      { name: "PHY_ERR_CFG",
+        regwen: "PHY_ERR_CFG_REGWEN",
+        desc: "Phy error configuration",
+        swaccess: "rw",
+        hwaccess: "hro",
+        fields: [
+          { bits: "0",
+            name: "ECC_MULTI_ERR_DATA_EN",
+            desc: '''
+              Transaction error response for ECC multi bit errors in data type transactions.
+              When set to 1, the error response is propagated to the source.
+              When set to 0, the error response is silenced.
+
+              Note this register only controls whether an error response is generated to the source
+              for data transactions.  It does not control whether !!ERR_CODE and its associated error
+              status register collects error information.
+
+              This can be used by software to dictate whether a multi-bit error should be reflected as a
+              bus error to bus hosts or the flash controller.
+
+              The reason this may not be desired is because software may want to triage the actual bit change,
+              and that would not be possible if an error response is always generated.  The hosts may simply
+              react on the error and discard the data.
+            '''
+          },
+        ]
+        tags: [ // alert triggers should be tested by directed tests
+               "excl:CsrAllTests:CsrExclWrite"]
+      },
+
       { name: "PHY_ALERT_CFG",
         desc: "Phy alert configuration",
         swaccess: "rw",
@@ -931,6 +1052,7 @@
           { bits: "31:0", name: "data",  desc: "Flash ctrl scratch register" },
         ]
       },
+
       { name: "FIFO_LVL",
         desc: "Programmable depth where FIFOs should generate interrupts",
         swaccess: "rw",
@@ -966,7 +1088,6 @@
             desc: '''
               Active high resets for both program and read FIFOs.  This is especially useful after the controller
               encounters an error of some kind.
-
               This bit will hold the FIFO in reset as long as it is set.
               '''
             resval: "0"
@@ -1006,6 +1127,7 @@
         },
       },
     ],
+
     prim: [
 //      { name: "DUMMY",
 //        desc: "Dummy unused register",
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl b/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
index 05dfd16..263832d 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
+++ b/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
@@ -59,6 +59,7 @@
   output logic cio_tdo_o,
 
   // Interrupts
+  output logic intr_err_o,        // ERR_CODE is non-zero
   output logic intr_prog_empty_o, // Program fifo is empty
   output logic intr_prog_lvl_o,   // Program fifo is empty
   output logic intr_rd_full_o,    // Read fifo is full
@@ -446,6 +447,7 @@
     .tl_o        (tl_win_d2h[0]),
     .en_ifetch_i (tlul_pkg::InstrDis),
     .req_o       (sw_wvalid),
+    .req_type_o  (),
     .gnt_i       (sw_wready),
     .we_o        (sw_wen),
     .addr_o      (),
@@ -530,6 +532,7 @@
     .tl_o        (tl_win_d2h[1]),
     .en_ifetch_i (tlul_pkg::InstrDis),
     .req_o       (rd_fifo_ren),
+    .req_type_o  (),
     .gnt_i       (rd_fifo_rvalid),
     .we_o        (),
     .addr_o      (),
@@ -785,6 +788,7 @@
   assign flash_o.jtag_req.tms = cio_tms_i;
   assign flash_o.jtag_req.tdi = cio_tdi_i;
   assign flash_o.jtag_req.trst_n = '0;
+  assign flash_o.ecc_multi_err_en = reg2hw.phy_err_cfg.q;
   assign cio_tdo_o = flash_i.jtag_rsp.tdo;
   assign cio_tdo_en_o = flash_i.jtag_rsp.tdo_oe;
   assign flash_rd_err = flash_i.rd_err;
@@ -792,6 +796,7 @@
   assign flash_phy_busy = flash_i.init_busy;
 
 
+
   // Interface to pwrmgr
   // flash is not idle as long as there is a stateful operation ongoing
   logic flash_idle_d;
@@ -867,39 +872,88 @@
   assign hw2reg.err_addr.d = err_addr;
   assign hw2reg.err_addr.de = flash_mp_error;
 
-  for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_err_cons
-    assign hw2reg.ecc_err_addr[bank].d  = {flash_i.ecc_addr[bank], {BusByteWidth{1'b0}}};
-    assign hw2reg.ecc_err_addr[bank].de = flash_i.ecc_single_err[bank] |
-                                          flash_i.ecc_multi_err[bank];
+  for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_single_err_cons
+    assign hw2reg.ecc_single_err_addr[bank].d  = {flash_i.ecc_addr[bank], {BusByteWidth{1'b0}}};
+    assign hw2reg.ecc_single_err_addr[bank].de = flash_i.ecc_single_err[bank];
   end
 
-  // Generate edge triggered signals for sources that are level
-  logic [3:0] intr_src;
-  logic [3:0] intr_src_q;
-  logic [3:0] intr_assert;
+  for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_multi_err_cons
+    assign hw2reg.ecc_multi_err_addr[bank].d  = {flash_i.ecc_addr[bank], {BusByteWidth{1'b0}}};
+    assign hw2reg.ecc_multi_err_addr[bank].de = flash_i.ecc_multi_err[bank];
+  end
 
-  assign intr_src = { ~prog_fifo_rvalid,
-                      reg2hw.fifo_lvl.prog.q == prog_fifo_depth,
-                      rd_fifo_full,
-                      reg2hw.fifo_lvl.rd.q == rd_fifo_depth
-                    };
+  logic [7:0] single_err_cnt_d, multi_err_cnt_d;
+  always_comb begin
+    single_err_cnt_d = reg2hw.ecc_single_err_cnt.q;
+    multi_err_cnt_d = reg2hw.ecc_multi_err_cnt.q;
+
+    if (|flash_i.ecc_single_err && single_err_cnt_d < '1) begin
+      single_err_cnt_d = single_err_cnt_d + 1'b1;
+    end
+
+    if (|flash_i.ecc_multi_err && multi_err_cnt_d < '1) begin
+      multi_err_cnt_d = multi_err_cnt_d + 1'b1;
+    end
+  end
+
+  // feed back in error count
+  assign hw2reg.ecc_single_err_cnt.de = 1'b1;
+  assign hw2reg.ecc_single_err_cnt.d  = single_err_cnt_d;
+  assign hw2reg.ecc_multi_err_cnt.de  = 1'b1;
+  assign hw2reg.ecc_multi_err_cnt.d   = multi_err_cnt_d;
+
+  // err code interrupt event
+  flash_ctrl_reg2hw_err_code_reg_t err_code_d, err_code_q;
+  logic err_code_intr_event;
+
+  assign err_code_d = reg2hw.err_code & reg2hw.err_code_intr_en;
+
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      err_code_q <= '0;
+    end else begin
+      err_code_q <= err_code_d;
+    end
+  end
+
+  assign err_code_intr_event = err_code_d != err_code_q;
+
+  // general interrupt events
+  logic [3:0] intr_src_d;
+  logic [3:0] intr_src_q;
+
+  assign intr_src_d = { ~prog_fifo_rvalid,
+                        reg2hw.fifo_lvl.prog.q == prog_fifo_depth,
+                        rd_fifo_full,
+                        reg2hw.fifo_lvl.rd.q == rd_fifo_depth
+                      };
 
   always_ff @(posedge clk_i or negedge rst_ni) begin
     if (!rst_ni) begin
       intr_src_q <= 4'h8; //prog_fifo is empty by default
-    end else if (sw_sel) begin
-      intr_src_q <= intr_src;
+    end else begin
+      if (sw_sel) begin
+        intr_src_q[3:0] <= intr_src_d[3:0];
+      end
     end
   end
 
-  assign intr_assert = ~intr_src_q & intr_src;
-
+  // interrupt events
+  logic [4:0] intr_assert;
+  assign intr_assert[4] = err_code_intr_event;
+  assign intr_assert[3:0] = ~intr_src_q & intr_src_d;
 
   assign intr_prog_empty_o = reg2hw.intr_enable.prog_empty.q & reg2hw.intr_state.prog_empty.q;
   assign intr_prog_lvl_o = reg2hw.intr_enable.prog_lvl.q & reg2hw.intr_state.prog_lvl.q;
   assign intr_rd_full_o = reg2hw.intr_enable.rd_full.q & reg2hw.intr_state.rd_full.q;
   assign intr_rd_lvl_o = reg2hw.intr_enable.rd_lvl.q & reg2hw.intr_state.rd_lvl.q;
   assign intr_op_done_o = reg2hw.intr_enable.op_done.q & reg2hw.intr_state.op_done.q;
+  assign intr_err_o = reg2hw.intr_enable.err.q & reg2hw.intr_state.err.q;
+
+  assign hw2reg.intr_state.err.d  = 1'b1;
+  assign hw2reg.intr_state.err.de = intr_assert[4] |
+                                    (reg2hw.intr_test.err.qe  &
+                                    reg2hw.intr_test.err.q);
 
   assign hw2reg.intr_state.prog_empty.d  = 1'b1;
   assign hw2reg.intr_state.prog_empty.de = intr_assert[3]  |
@@ -921,13 +975,13 @@
                                        (reg2hw.intr_test.rd_lvl.qe  &
                                        reg2hw.intr_test.rd_lvl.q);
 
-
   assign hw2reg.intr_state.op_done.d  = 1'b1;
   assign hw2reg.intr_state.op_done.de = sw_ctrl_done  |
                                         (reg2hw.intr_test.op_done.qe  &
                                         reg2hw.intr_test.op_done.q);
 
 
+
   // Unused bits
   logic [BusByteWidth-1:0] unused_byte_sel;
   logic [top_pkg::TL_AW-1-BusAddrW:0] unused_higher_addr_bits;
@@ -953,5 +1007,6 @@
   `ASSERT_KNOWN(IntrProgRdFullKnownO_A, intr_rd_full_o   )
   `ASSERT_KNOWN(IntrRdLvlKnownO_A,      intr_rd_lvl_o    )
   `ASSERT_KNOWN(IntrOpDoneKnownO_A,     intr_op_done_o   )
+  `ASSERT_KNOWN(IntrErrO_A,             intr_err_o       )
 
 endmodule
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl_pkg.sv.tpl b/hw/ip/flash_ctrl/data/flash_ctrl_pkg.sv.tpl
index 92a1359..5447f22 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl_pkg.sv.tpl
+++ b/hw/ip/flash_ctrl/data/flash_ctrl_pkg.sv.tpl
@@ -283,6 +283,8 @@
     logic                 scramble_en;
     logic                 ecc_en;
     logic                 he_en;
+    logic                 rd_buf_en;
+    logic                 ecc_multi_err_en;
     logic                 rd;
     logic                 prog;
     logic                 pg_erase;
@@ -297,7 +299,6 @@
     mp_region_cfg_t [MpRegions:0] region_cfgs;
     logic [KeyWidth-1:0]  addr_key;
     logic [KeyWidth-1:0]  data_key;
-    logic                 rd_buf_en;
     tlul_pkg::tl_h2d_t    tl_flash_c2p;
     logic                 alert_trig;
     logic                 alert_ack;
@@ -310,6 +311,8 @@
     scramble_en:   '0,
     ecc_en:        '0,
     he_en:         '0,
+    rd_buf_en:     1'b0,
+    ecc_multi_err_en: '0,
     rd:            '0,
     prog:          '0,
     pg_erase:      '0,
@@ -324,7 +327,6 @@
     region_cfgs:   '0,
     addr_key:      RndCnstAddrKeyDefault,
     data_key:      RndCnstDataKeyDefault,
-    rd_buf_en:     1'b0,
     tl_flash_c2p:  '0,
     alert_trig:    1'b0,
     alert_ack:     1'b0,
diff --git a/hw/ip/flash_ctrl/dv/tb/flash_ctrl_wrapper.sv b/hw/ip/flash_ctrl/dv/tb/flash_ctrl_wrapper.sv
index 3ab2fc4..846fad0 100644
--- a/hw/ip/flash_ctrl/dv/tb/flash_ctrl_wrapper.sv
+++ b/hw/ip/flash_ctrl/dv/tb/flash_ctrl_wrapper.sv
@@ -100,6 +100,7 @@
 
   // host to flash communication
   logic flash_host_req;
+  tlul_pkg::tl_type_e flash_host_req_type;
   logic flash_host_req_rdy;
   logic flash_host_req_done;
   logic [flash_ctrl_pkg::BusWidth-1:0] flash_host_rdata;
@@ -112,27 +113,29 @@
     .ByteAccess(0),
     .ErrOnWrite(1)
   ) u_tl_adapter_eflash (
-    .clk_i    (clk_i),
-    .rst_ni   (rst_ni),
+    .clk_i      (clk_i),
+    .rst_ni     (rst_ni),
 
-    .tl_i     (eflash_tl_i),
-    .tl_o     (eflash_tl_o),
+    .tl_i       (eflash_tl_i),
+    .tl_o       (eflash_tl_o),
 
-    .req_o    (flash_host_req),
-    .gnt_i    (flash_host_req_rdy),
-    .we_o     (),
-    .addr_o   (flash_host_addr),
-    .wdata_o  (),
-    .wmask_o  (),
-    .rdata_i  (flash_host_rdata),
-    .rvalid_i (flash_host_req_done),
-    .rerror_i (2'b00)
+    .req_o      (flash_host_req),
+    .req_type_o (flash_host_req_type),
+    .gnt_i      (flash_host_req_rdy),
+    .we_o       (),
+    .addr_o     (flash_host_addr),
+    .wdata_o    (),
+    .wmask_o    (),
+    .rdata_i    (flash_host_rdata),
+    .rvalid_i   (flash_host_req_done),
+    .rerror_i   (2'b00)
   );
 
   flash_phy u_flash_eflash (
     .clk_i,
     .rst_ni,
     .host_req_i             (flash_host_req     ),
+    .host_req_type_i        (flash_host_req_type),
     .host_addr_i            (flash_host_addr    ),
     .host_req_rdy_o         (flash_host_req_rdy ),
     .host_req_done_o        (flash_host_req_done),
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
index 892c7c7..36d6c7c 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
@@ -59,6 +59,7 @@
   output logic cio_tdo_o,
 
   // Interrupts
+  output logic intr_err_o,        // ERR_CODE is non-zero
   output logic intr_prog_empty_o, // Program fifo is empty
   output logic intr_prog_lvl_o,   // Program fifo is empty
   output logic intr_rd_full_o,    // Read fifo is full
@@ -446,6 +447,7 @@
     .tl_o        (tl_win_d2h[0]),
     .en_ifetch_i (tlul_pkg::InstrDis),
     .req_o       (sw_wvalid),
+    .req_type_o  (),
     .gnt_i       (sw_wready),
     .we_o        (sw_wen),
     .addr_o      (),
@@ -530,6 +532,7 @@
     .tl_o        (tl_win_d2h[1]),
     .en_ifetch_i (tlul_pkg::InstrDis),
     .req_o       (rd_fifo_ren),
+    .req_type_o  (),
     .gnt_i       (rd_fifo_rvalid),
     .we_o        (),
     .addr_o      (),
@@ -786,6 +789,7 @@
   assign flash_o.jtag_req.tms = cio_tms_i;
   assign flash_o.jtag_req.tdi = cio_tdi_i;
   assign flash_o.jtag_req.trst_n = '0;
+  assign flash_o.ecc_multi_err_en = reg2hw.phy_err_cfg.q;
   assign cio_tdo_o = flash_i.jtag_rsp.tdo;
   assign cio_tdo_en_o = flash_i.jtag_rsp.tdo_oe;
   assign flash_rd_err = flash_i.rd_err;
@@ -793,6 +797,7 @@
   assign flash_phy_busy = flash_i.init_busy;
 
 
+
   // Interface to pwrmgr
   // flash is not idle as long as there is a stateful operation ongoing
   logic flash_idle_d;
@@ -868,39 +873,72 @@
   assign hw2reg.err_addr.d = err_addr;
   assign hw2reg.err_addr.de = flash_mp_error;
 
-  for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_err_cons
-    assign hw2reg.ecc_err_addr[bank].d  = {flash_i.ecc_addr[bank], {BusByteWidth{1'b0}}};
-    assign hw2reg.ecc_err_addr[bank].de = flash_i.ecc_single_err[bank] |
-                                          flash_i.ecc_multi_err[bank];
+  for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_single_err_cons
+    assign hw2reg.ecc_single_err_addr[bank].d  = {flash_i.ecc_addr[bank], {BusByteWidth{1'b0}}};
+    assign hw2reg.ecc_single_err_addr[bank].de = flash_i.ecc_single_err[bank];
   end
 
-  // Generate edge triggered signals for sources that are level
-  logic [3:0] intr_src;
-  logic [3:0] intr_src_q;
-  logic [3:0] intr_assert;
+  for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_multi_err_cons
+    assign hw2reg.ecc_multi_err_addr[bank].d  = {flash_i.ecc_addr[bank], {BusByteWidth{1'b0}}};
+    assign hw2reg.ecc_multi_err_addr[bank].de = flash_i.ecc_multi_err[bank];
+  end
 
-  assign intr_src = { ~prog_fifo_rvalid,
-                      reg2hw.fifo_lvl.prog.q == prog_fifo_depth,
-                      rd_fifo_full,
-                      reg2hw.fifo_lvl.rd.q == rd_fifo_depth
-                    };
+  logic [7:0] single_err_cnt_d, multi_err_cnt_d;
+  always_comb begin
+    single_err_cnt_d = reg2hw.ecc_single_err_cnt.q;
+    multi_err_cnt_d = reg2hw.ecc_multi_err_cnt.q;
 
-  always_ff @(posedge clk_i or negedge rst_ni) begin
-    if (!rst_ni) begin
-      intr_src_q <= 4'h8; //prog_fifo is empty by default
-    end else if (sw_sel) begin
-      intr_src_q <= intr_src;
+    if (|flash_i.ecc_single_err && single_err_cnt_d < '1) begin
+      single_err_cnt_d = single_err_cnt_d + 1'b1;
+    end
+
+    if (|flash_i.ecc_multi_err && multi_err_cnt_d < '1) begin
+      multi_err_cnt_d = multi_err_cnt_d + 1'b1;
     end
   end
 
-  assign intr_assert = ~intr_src_q & intr_src;
+  // feed back in error count
+  assign hw2reg.ecc_single_err_cnt.de = 1'b1;
+  assign hw2reg.ecc_single_err_cnt.d  = single_err_cnt_d;
+  assign hw2reg.ecc_multi_err_cnt.de  = 1'b1;
+  assign hw2reg.ecc_multi_err_cnt.d   = multi_err_cnt_d;
 
+  // Generate edge triggered signals for sources that are level
+  logic [4:0] intr_src_d;
+  logic [4:0] intr_src_q;
+  logic [4:0] intr_assert;
+
+  assign intr_src_d = { reg2hw.err_code != '0,
+                        ~prog_fifo_rvalid,
+                        reg2hw.fifo_lvl.prog.q == prog_fifo_depth,
+                        rd_fifo_full,
+                        reg2hw.fifo_lvl.rd.q == rd_fifo_depth
+                      };
+
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      intr_src_q <= 5'h8; //prog_fifo is empty by default
+    end else begin
+      intr_src_q[4] <= intr_src_d[4];
+      if (sw_sel) begin
+        intr_src_q[3:0] <= intr_src_d[3:0];
+      end
+    end
+  end
+
+  assign intr_assert = ~intr_src_q & intr_src_d;
 
   assign intr_prog_empty_o = reg2hw.intr_enable.prog_empty.q & reg2hw.intr_state.prog_empty.q;
   assign intr_prog_lvl_o = reg2hw.intr_enable.prog_lvl.q & reg2hw.intr_state.prog_lvl.q;
   assign intr_rd_full_o = reg2hw.intr_enable.rd_full.q & reg2hw.intr_state.rd_full.q;
   assign intr_rd_lvl_o = reg2hw.intr_enable.rd_lvl.q & reg2hw.intr_state.rd_lvl.q;
   assign intr_op_done_o = reg2hw.intr_enable.op_done.q & reg2hw.intr_state.op_done.q;
+  assign intr_err_o = reg2hw.intr_enable.err.q & reg2hw.intr_state.err.q;
+
+  assign hw2reg.intr_state.err.d  = 1'b1;
+  assign hw2reg.intr_state.err.de = intr_assert[4] |
+                                    (reg2hw.intr_test.err.qe  &
+                                    reg2hw.intr_test.err.q);
 
   assign hw2reg.intr_state.prog_empty.d  = 1'b1;
   assign hw2reg.intr_state.prog_empty.de = intr_assert[3]  |
@@ -922,13 +960,13 @@
                                        (reg2hw.intr_test.rd_lvl.qe  &
                                        reg2hw.intr_test.rd_lvl.q);
 
-
   assign hw2reg.intr_state.op_done.d  = 1'b1;
   assign hw2reg.intr_state.op_done.de = sw_ctrl_done  |
                                         (reg2hw.intr_test.op_done.qe  &
                                         reg2hw.intr_test.op_done.q);
 
 
+
   // Unused bits
   logic [BusByteWidth-1:0] unused_byte_sel;
   logic [top_pkg::TL_AW-1-BusAddrW:0] unused_higher_addr_bits;
@@ -954,5 +992,6 @@
   `ASSERT_KNOWN(IntrProgRdFullKnownO_A, intr_rd_full_o   )
   `ASSERT_KNOWN(IntrRdLvlKnownO_A,      intr_rd_lvl_o    )
   `ASSERT_KNOWN(IntrOpDoneKnownO_A,     intr_op_done_o   )
+  `ASSERT_KNOWN(IntrErrO_A,             intr_err_o       )
 
 endmodule
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
index bbbdc62..7670752 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
@@ -283,6 +283,8 @@
     logic                 scramble_en;
     logic                 ecc_en;
     logic                 he_en;
+    logic                 rd_buf_en;
+    logic                 ecc_multi_err_en;
     logic                 rd;
     logic                 prog;
     logic                 pg_erase;
@@ -297,7 +299,6 @@
     mp_region_cfg_t [MpRegions:0] region_cfgs;
     logic [KeyWidth-1:0]  addr_key;
     logic [KeyWidth-1:0]  data_key;
-    logic                 rd_buf_en;
     tlul_pkg::tl_h2d_t    tl_flash_c2p;
     logic                 alert_trig;
     logic                 alert_ack;
@@ -310,6 +311,8 @@
     scramble_en:   '0,
     ecc_en:        '0,
     he_en:         '0,
+    rd_buf_en:     1'b0,
+    ecc_multi_err_en: '0,
     rd:            '0,
     prog:          '0,
     pg_erase:      '0,
@@ -324,7 +327,6 @@
     region_cfgs:   '0,
     addr_key:      RndCnstAddrKeyDefault,
     data_key:      RndCnstDataKeyDefault,
-    rd_buf_en:     1'b0,
     tl_flash_c2p:  '0,
     alert_trig:    1'b0,
     alert_ack:     1'b0,
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy.sv b/hw/ip/flash_ctrl/rtl/flash_phy.sv
index 2272048..f878dd5 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy.sv
@@ -14,6 +14,7 @@
   input clk_i,
   input rst_ni,
   input host_req_i,
+  input tlul_pkg::tl_type_e host_req_type_i,
   input [BusAddrW-1:0] host_addr_i,
   output logic host_req_rdy_o,
   output logic host_req_done_o,
@@ -189,9 +190,11 @@
       .scramble_en_i(flash_ctrl_i.scramble_en),
       .ecc_en_i(flash_ctrl_i.ecc_en),
       .he_en_i(flash_ctrl_i.he_en),
+      .ecc_multi_err_en_i(flash_ctrl_i.ecc_multi_err_en),
       // host request must be suppressed if response fifo cannot hold more
       // otherwise the flash_phy_core and flash_phy will get out of sync
       .host_req_i(host_req),
+      .host_req_type_i,
       .host_scramble_en_i(host_scramble_en),
       .host_ecc_en_i(host_ecc_en),
       .host_addr_i(host_addr_i[0 +: BusBankAddrW]),
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_core.sv b/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
index e75756e..148ef86 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
@@ -15,6 +15,7 @@
   input                              clk_i,
   input                              rst_ni,
   input                              host_req_i,   // host request - read only
+  input tlul_pkg::tl_type_e          host_req_type_i,
   input                              host_scramble_en_i,
   input                              host_ecc_en_i,
   input [BusBankAddrW-1:0]           host_addr_i,
@@ -22,6 +23,7 @@
   input                              scramble_en_i,
   input                              ecc_en_i,
   input                              he_en_i,
+  input                              ecc_multi_err_en_i,
   input                              rd_i,
   input                              prog_i,
   input                              pg_erase_i,
@@ -66,6 +68,9 @@
   // request signals to flash macro
   logic [PhyOps-1:0] reqs;
 
+  // the type of transaction to flash macro
+  tlul_pkg::tl_type_e muxed_req_type;
+
   // host select for address
   logic host_sel;
 
@@ -225,6 +230,8 @@
     endcase // unique case (state_q)
   end // always_comb
 
+  // transactions coming from flash controller are always data type
+  assign muxed_req_type = host_sel ? host_req_type_i : tlul_pkg::DataType;
   assign muxed_addr = host_sel ? host_addr_i : addr_i;
   assign muxed_part = host_sel ? flash_ctrl_pkg::FlashPartData : part_i;
   assign muxed_scramble_en = host_sel ? host_scramble_en_i : scramble_en_i;
@@ -249,7 +256,9 @@
     .clk_i,
     .rst_ni,
     .buf_en_i(rd_buf_en_i),
+    .ecc_multi_err_en_i,
     .req_i(reqs[PhyRead]),
+    .req_type_i(muxed_req_type),
     .descramble_i(muxed_scramble_en),
     .ecc_i(muxed_ecc_en),
     .prog_i(reqs[PhyProg]),
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv b/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
index 905cf7f..1849edd 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
@@ -76,6 +76,7 @@
     logic [BankAddrW-1:0] addr;
     logic descramble;
     logic ecc;
+    tlul_pkg::tl_type_e req_type;
   } rd_attr_t;
 
   // Flash Operations Supported
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
index f1277e3..5024eb8 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
@@ -32,9 +32,11 @@
 
   // configuration interface from flash controller
   input buf_en_i,
+  input ecc_multi_err_en_i,
 
   // interface with arbitration unit
   input req_i,
+  input tlul_pkg::tl_type_e req_type_i,
   input descramble_i,
   input ecc_i,
   input prog_i,
@@ -307,6 +309,7 @@
       rd_attrs.addr <= addr_i[BusBankAddrW-1:LsbAddrBit];
       rd_attrs.descramble <= descramble_i;
       rd_attrs.ecc <= ecc_i;
+      rd_attrs.req_type <= req_type_i;
     end else if (rd_done) begin
       rd_busy <= 1'b0;
     end
@@ -343,7 +346,7 @@
   logic [DataWidth-1:0] data_int;
   logic data_erased;
 
-  assign valid_ecc = rd_done && rd_attrs.ecc && !data_erased;
+  assign valid_ecc = rd_done && rd_attrs.ecc;
 
   // When all bits are 1, the data has been erased
   // This check is only valid when read data returns.
@@ -363,9 +366,18 @@
   // rd_done signal below is duplicated (already in data_erased) to show clear intent of the code.
   assign data_int = valid_ecc ? data_ecc_chk :
                                 data_i[DataWidth-1:0];
-  assign data_err = valid_ecc & ecc_multi_err;
-  assign ecc_multi_err_o = data_err;
+
+  // For instruction type accesses, always return the transaction error
+  // For data type accesses, allow the error return to be configurable, as the actual data may
+  // need to be debugged
+  assign data_err = (rd_attrs.req_type == tlul_pkg::InstrType) ?
+                    ecc_multi_err_o :
+                    ecc_multi_err_o & ecc_multi_err_en_i;
+
+  // always send out sideband indication on error
+  assign ecc_multi_err_o = valid_ecc & ecc_multi_err;
   assign ecc_single_err_o = valid_ecc & ecc_single_err;
+
   // ecc address return is always the full flash word
   assign ecc_addr_o = {rd_attrs.addr, {LsbAddrBit{1'b0}}};
 
diff --git a/hw/ip/prim_generic/rtl/prim_generic_flash.sv b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
index 5cde253..97c6032 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_flash.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
@@ -148,6 +148,7 @@
     .tl_o,
     .en_ifetch_i(tlul_pkg::InstrDis),
     .req_o(cfg_req),
+    .req_type_o(),
     .gnt_i(1'b1),
     .we_o(cfg_we),
     .addr_o(cfg_addr),
diff --git a/hw/ip/tlul/rtl/tlul_adapter_sram.sv b/hw/ip/tlul/rtl/tlul_adapter_sram.sv
index 87c03eb..caba9e0 100644
--- a/hw/ip/tlul/rtl/tlul_adapter_sram.sv
+++ b/hw/ip/tlul/rtl/tlul_adapter_sram.sv
@@ -44,6 +44,7 @@
 
   // SRAM interface
   output logic                req_o,
+  output tl_type_e            req_type_o,
   input                       gnt_i,
   output logic                we_o,
   output logic [SramAw-1:0]   addr_o,
@@ -184,9 +185,10 @@
   //    Generate request only when no internal error occurs. If error occurs, the request should be
   //    dropped and returned error response to the host. So, error to be pushed to reqfifo.
   //    In this case, it is assumed the request is granted (may cause ordering issue later?)
-  assign req_o    = tl_i.a_valid & reqfifo_wready & ~error_internal;
-  assign we_o     = tl_i.a_valid & logic'(tl_i.a_opcode inside {PutFullData, PutPartialData});
-  assign addr_o   = (tl_i.a_valid) ? tl_i.a_address[DataBitWidth+:SramAw] : '0;
+  assign req_o      = tl_i.a_valid & reqfifo_wready & ~error_internal;
+  assign req_type_o = tl_i.a_user.tl_type;
+  assign we_o       = tl_i.a_valid & logic'(tl_i.a_opcode inside {PutFullData, PutPartialData});
+  assign addr_o     = (tl_i.a_valid) ? tl_i.a_address[DataBitWidth+:SramAw] : '0;
 
   // Support SRAMs wider than the TL-UL word width by mapping the parts of the
   // TL-UL address which are more fine-granular than the SRAM width to the
diff --git a/sw/device/lib/flash_ctrl.c b/sw/device/lib/flash_ctrl.c
index 5538f7e..7b34865 100644
--- a/sw/device/lib/flash_ctrl.c
+++ b/sw/device/lib/flash_ctrl.c
@@ -43,7 +43,7 @@
 static int get_clr_err(void) {
   uint32_t err_status =
       REG32(FLASH_CTRL0_BASE_ADDR + FLASH_CTRL_ERR_CODE_REG_OFFSET);
-  REG32(FLASH_CTRL0_BASE_ADDR + FLASH_CTRL_ERR_CODE_REG_OFFSET) = 0;
+  REG32(FLASH_CTRL0_BASE_ADDR + FLASH_CTRL_ERR_CODE_REG_OFFSET) = -1u;
   return err_status;
 }
 
diff --git a/sw/device/tests/dif/dif_plic_unittest.cc b/sw/device/tests/dif/dif_plic_unittest.cc
index 4598e04..cd8e4ad 100644
--- a/sw/device/tests/dif/dif_plic_unittest.cc
+++ b/sw/device/tests/dif/dif_plic_unittest.cc
@@ -20,7 +20,7 @@
 
 // If either of these static assertions fail, then the unit-tests for related
 // API should be revisited.
-static_assert(RV_PLIC_PARAM_NUMSRC == 177,
+static_assert(RV_PLIC_PARAM_NUMSRC == 178,
               "PLIC instantiation parameters have changed.");
 static_assert(RV_PLIC_PARAM_NUMTARGET == 1,
               "PLIC instantiation parameters have changed.");
@@ -112,7 +112,7 @@
           {RV_PLIC_IE0_2_REG_OFFSET, RV_PLIC_IE0_2_E_95_BIT},
           {RV_PLIC_IE0_3_REG_OFFSET, RV_PLIC_IE0_3_E_127_BIT},
           {RV_PLIC_IE0_4_REG_OFFSET, RV_PLIC_IE0_4_E_159_BIT},
-          {RV_PLIC_IE0_5_REG_OFFSET, RV_PLIC_IE0_5_E_176_BIT},
+          {RV_PLIC_IE0_5_REG_OFFSET, RV_PLIC_IE0_5_E_177_BIT},
       }};
   static constexpr std::array<Register, RV_PLIC_LE_MULTIREG_COUNT>
       kTriggerRegisters{{
@@ -121,7 +121,7 @@
           {RV_PLIC_LE_2_REG_OFFSET, RV_PLIC_LE_2_LE_95_BIT},
           {RV_PLIC_LE_3_REG_OFFSET, RV_PLIC_LE_3_LE_127_BIT},
           {RV_PLIC_LE_4_REG_OFFSET, RV_PLIC_LE_4_LE_159_BIT},
-          {RV_PLIC_LE_5_REG_OFFSET, RV_PLIC_LE_5_LE_176_BIT},
+          {RV_PLIC_LE_5_REG_OFFSET, RV_PLIC_LE_5_LE_177_BIT},
       }};
   static constexpr std::array<Register, RV_PLIC_IP_MULTIREG_COUNT>
       kPendingRegisters{{
@@ -130,7 +130,7 @@
           {RV_PLIC_IP_2_REG_OFFSET, RV_PLIC_IP_2_P_95_BIT},
           {RV_PLIC_IP_3_REG_OFFSET, RV_PLIC_IP_3_P_127_BIT},
           {RV_PLIC_IP_4_REG_OFFSET, RV_PLIC_IP_4_P_159_BIT},
-          {RV_PLIC_IP_5_REG_OFFSET, RV_PLIC_IP_5_P_176_BIT},
+          {RV_PLIC_IP_5_REG_OFFSET, RV_PLIC_IP_5_P_177_BIT},
       }};
 
   // Set enable/disable multireg expectations, one bit per call.
diff --git a/util/topgen/templates/toplevel.sv.tpl b/util/topgen/templates/toplevel.sv.tpl
index ddfecad..5786302 100644
--- a/util/topgen/templates/toplevel.sv.tpl
+++ b/util/topgen/templates/toplevel.sv.tpl
@@ -384,6 +384,7 @@
     .tl_o        (${m["name"]}_tl_rsp),
     .en_ifetch_i (${m["inter_signal_list"][3]["top_signame"]}),
     .req_o       (${m["name"]}_req),
+    .req_type_o  (),
     .gnt_i       (${m["name"]}_gnt),
     .we_o        (${m["name"]}_we),
     .addr_o      (${m["name"]}_addr),
@@ -474,6 +475,7 @@
     .tl_o        (${m["name"]}_tl_rsp),
     .en_ifetch_i (tlul_pkg::InstrEn),
     .req_o       (${m["name"]}_req),
+    .req_type_o  (),
     .gnt_i       (1'b1), // Always grant as only one requester exists
     .we_o        (),
     .addr_o      (${m["name"]}_addr),
@@ -507,6 +509,7 @@
 
   // host to flash communication
   logic flash_host_req;
+  tlul_pkg::tl_type_e flash_host_req_type;
   logic flash_host_req_rdy;
   logic flash_host_req_done;
   logic flash_host_rderr;
@@ -534,6 +537,7 @@
     .tl_o        (${m["name"]}_tl_rsp),
     .en_ifetch_i (tlul_pkg::InstrEn), // tie this to secure boot somehow
     .req_o       (flash_host_req),
+    .req_type_o  (flash_host_req_type),
     .gnt_i       (flash_host_req_rdy),
     .we_o        (),
     .addr_o      (flash_host_addr),
@@ -553,6 +557,7 @@
     .${key}   (${value}),
     % endfor
     .host_req_i        (flash_host_req),
+    .host_req_type_i   (flash_host_req_type),
     .host_addr_i       (flash_host_addr),
     .host_req_rdy_o    (flash_host_req_rdy),
     .host_req_done_o   (flash_host_req_done),