[flash_ctrl] Add ECC to program / erase datapaths

- New parameters are defined to represent the "metadata" of flas
  - Software only has direct access to data portion
  - Metadata area is used for empty detection as well as ECC
  - There are currently no plans to make metadata directly accessible to
    software, although that can be done.
- ECC and scramble are always disabled / enabled at the same time.
- ECC is calculated on the scrambled data.
- There is no good DV / verilator support yet, To get around the issue,
  prim_generic_flash instantiates a memory for main, and another for metadata.
  - long term there will need to be a better backdoor solution to flash.
- ECC uncorrectable errors are forwarded up to both the host / controller
- When an uncorrectable error occurs, all F's are returned to the reading agent.

Signed-off-by: Timothy Chen <timothytim@google.com>

[flash_ctrl] update for review comments

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/flash_ctrl/flash_ctrl.core b/hw/ip/flash_ctrl/flash_ctrl.core
index cb5b6d0..f0ad240 100644
--- a/hw/ip/flash_ctrl/flash_ctrl.core
+++ b/hw/ip/flash_ctrl/flash_ctrl.core
@@ -10,6 +10,7 @@
     depend:
       - lowrisc:ip:tlul
       - lowrisc:prim:all
+      - lowrisc:prim:secded
       - lowrisc:prim:flash
       - lowrisc:prim:gf_mult
       - lowrisc:ip:flash_ctrl_pkg
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
index d5bbda4..c3162c1 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
@@ -101,11 +101,12 @@
   logic [BusAddrW-1:0] flash_addr;
   logic flash_req;
   logic flash_rd_done, flash_prog_done, flash_erase_done;
-  logic flash_error;
+  logic flash_mp_error;
   logic [BusWidth-1:0] flash_prog_data;
   logic flash_prog_last;
   flash_prog_e flash_prog_type;
   logic [BusWidth-1:0] flash_rd_data;
+  logic flash_rd_err;
   logic flash_phy_busy;
   logic rd_op;
   logic prog_op;
@@ -358,7 +359,7 @@
     .flash_last_o   (flash_prog_last),
     .flash_type_o   (flash_prog_type),
     .flash_done_i   (flash_prog_done),
-    .flash_error_i  (flash_error)
+    .flash_error_i  (flash_mp_error)
   );
 
   // Read FIFO
@@ -433,7 +434,7 @@
     .flash_ovfl_o   (rd_flash_ovfl),
     .flash_data_i   (flash_rd_data),
     .flash_done_i   (flash_rd_done),
-    .flash_error_i  (flash_error)
+    .flash_error_i  (flash_mp_error | flash_rd_err)
   );
 
   // Erase handler does not consume fifo
@@ -450,7 +451,7 @@
     .flash_addr_o   (erase_flash_addr),
     .flash_op_o     (erase_flash_type),
     .flash_done_i   (flash_erase_done),
-    .flash_error_i  (flash_error)
+    .flash_error_i  (flash_mp_error)
   );
 
   // Final muxing to flash macro module
@@ -551,7 +552,7 @@
     .rd_done_o(flash_rd_done),
     .prog_done_o(flash_prog_done),
     .erase_done_o(flash_erase_done),
-    .error_o(flash_error),
+    .error_o(flash_mp_error),
     .err_addr_o(err_addr),
 
     // flash phy interface
@@ -608,6 +609,7 @@
   assign flash_o.scramble_en = reg2hw.scramble_en.q;
   assign flash_o.addr_key = otp_i.addr_key;
   assign flash_o.data_key = otp_i.data_key;
+  assign flash_rd_err = flash_i.rd_err;
   assign flash_rd_data = flash_i.rd_data;
   assign flash_phy_busy = flash_i.init_busy;
 
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
index ffdbc88..0ed379e 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_pkg.sv
@@ -239,6 +239,7 @@
     logic                rd_done;
     logic                prog_done;
     logic                erase_done;
+    logic                rd_err;
     logic [BusWidth-1:0] rd_data;
     logic                init_busy;
   } flash_rsp_t;
@@ -249,6 +250,7 @@
     rd_done:    1'b0,
     prog_done:  1'b0,
     erase_done: 1'b0,
+    rd_err:     '0,
     rd_data:    '0,
     init_busy:  1'b0
   };
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_rd.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_rd.sv
index 583a425..2f9545b 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_rd.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_rd.sv
@@ -14,7 +14,7 @@
   input  [11:0]            op_num_words_i,
   output logic             op_done_o,
   output logic             op_err_o,
-  input [BusAddrW-1:0]        op_addr_i,
+  input [BusAddrW-1:0]     op_addr_i,
 
   // FIFO Interface
   input                    data_rdy_i,
@@ -25,7 +25,7 @@
   output logic             flash_req_o,
   output logic [BusAddrW-1:0] flash_addr_o,
   output logic             flash_ovfl_o,
-  input [BusWidth-1:0]        flash_data_i,
+  input [BusWidth-1:0]     flash_data_i,
   input                    flash_done_i,
   input                    flash_error_i
 );
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy.sv b/hw/ip/flash_ctrl/rtl/flash_phy.sv
index 8f52256..c58ee21 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy.sv
@@ -18,6 +18,7 @@
   output logic host_req_rdy_o,
   output logic host_req_done_o,
   output logic [BusWidth-1:0] host_rdata_o,
+  output logic host_rderr_o,
   input flash_req_t flash_ctrl_i,
   output flash_rsp_t flash_ctrl_o
 );
@@ -47,6 +48,7 @@
   logic [NumBanks-1:0]  host_rsp_vld;
   logic [NumBanks-1:0]  host_rsp_ack;
   logic [BusWidth-1:0]  host_rsp_data [NumBanks];
+  logic [NumBanks-1:0]  host_rsp_err;
   logic                 seq_fifo_rdy;
   logic                 seq_fifo_pending;
 
@@ -61,6 +63,7 @@
 
   // common interface
   logic [BusWidth-1:0] rd_data [NumBanks];
+  logic [NumBanks-1:0] rd_err;
 
   // select which bank each is operating on
   assign host_bank_sel = host_req_i ? host_addr_i[BusAddrW-1 -: BankW] : '0;
@@ -71,6 +74,7 @@
                           seq_fifo_rdy;
 
   assign host_req_done_o = seq_fifo_pending & host_rsp_vld[rsp_bank_sel];
+  assign host_rderr_o = host_rsp_err[rsp_bank_sel];
   assign host_rdata_o = host_rsp_data[rsp_bank_sel];
 
   // all banks are assumed to be the same in terms of prog_type support
@@ -79,6 +83,7 @@
   assign flash_ctrl_o.prog_done = prog_done[ctrl_bank_sel];
   assign flash_ctrl_o.erase_done = erase_done[ctrl_bank_sel];
   assign flash_ctrl_o.rd_data = rd_data[ctrl_bank_sel];
+  assign flash_ctrl_o.rd_err = rd_err[ctrl_bank_sel];
   assign flash_ctrl_o.init_busy = |init_busy;
 
   // This fifo holds the expected return order
@@ -105,7 +110,7 @@
     assign host_rsp_ack[bank] = host_req_done_o & (rsp_bank_sel == bank);
 
     prim_fifo_sync #(
-      .Width   (BusWidth),
+      .Width   (BusWidth + 1),
       .Pass    (1'b1),
       .Depth   (FlashMacroOustanding)
     ) i_host_rsp_fifo (
@@ -114,11 +119,11 @@
       .clr_i   (1'b0),
       .wvalid_i(host_req_done[bank]),
       .wready_o(host_rsp_avail[bank]),
-      .wdata_i (rd_data[bank]),
+      .wdata_i ({rd_err[bank], rd_data[bank]}),
       .depth_o (),
       .rvalid_o(host_rsp_vld[bank]),
       .rready_i(host_rsp_ack[bank]),
-      .rdata_o (host_rsp_data[bank])
+      .rdata_o ({host_rsp_err[bank], host_rsp_data[bank]})
     );
 
     logic host_req;
@@ -161,6 +166,7 @@
       .prog_done_o(prog_done[bank]),
       .erase_done_o(erase_done[bank]),
       .rd_data_o(rd_data[bank]),
+      .rd_err_o(rd_err[bank]),
       .init_busy_o(init_busy[bank])
     );
   end
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_core.sv b/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
index bf57e16..746464f 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_core.sv
@@ -37,6 +37,7 @@
   output logic                       prog_done_o,
   output logic                       erase_done_o,
   output logic [BusWidth-1:0]        rd_data_o,
+  output logic                       rd_err_o,
   output logic                       init_busy_o
 );
 
@@ -81,7 +82,7 @@
   // the read stage is ready to accept a new transaction
   logic rd_stage_rdy;
 
-  // the read stage has valid data return
+  // the read stage has valid response
   logic rd_stage_data_valid;
 
   // arbitration counter
@@ -220,7 +221,7 @@
   ////////////////////////
 
   logic flash_rd_req;
-  logic [DataWidth-1:0] flash_rdata;
+  logic [FullDataWidth-1:0] flash_rdata;
   logic rd_calc_req;
   logic [BankAddrW-1:0] rd_calc_addr;
   logic rd_op_req;
@@ -239,6 +240,7 @@
     .part_i(muxed_part),
     .rdy_o(rd_stage_rdy),
     .data_valid_o(rd_stage_data_valid),
+    .data_err_o(rd_err_o),
     .data_o(rd_data_o),
     .idle_o(rd_stage_idle),
     .req_o(flash_rd_req),
@@ -259,7 +261,9 @@
   // program pipeline
   ////////////////////////
 
-  logic [DataWidth-1:0] prog_data, prog_scrambled_data;
+  logic [FullDataWidth-1:0] prog_full_data;
+  logic [DataWidth-1:0] prog_scrambled_data;
+  logic [DataWidth-1:0] prog_data;
   logic flash_prog_req;
   logic prog_calc_req;
   logic prog_op_req;
@@ -286,7 +290,8 @@
       .scramble_req_o(prog_op_req),
       .req_o(flash_prog_req),
       .ack_o(prog_ack),
-      .data_o(prog_data)
+      .block_data_o(prog_data),
+      .data_o(prog_full_data)
     );
 
   end
@@ -335,7 +340,8 @@
     .InfosPerBank(InfosPerBank),
     .PagesPerBank(PagesPerBank),
     .WordsPerPage(WordsPerPage),
-    .DataWidth(DataWidth),
+    .DataWidth(FullDataWidth),
+    .MetaDataWidth(MetaDataWidth),
     .SkipInit(SkipInit)
   ) i_flash (
     .clk_i,
@@ -347,7 +353,7 @@
     .bk_erase_i(reqs[PhyBkErase]),
     .addr_i(muxed_addr[BusBankAddrW-1:LsbAddrBit]),
     .part_i(muxed_part),
-    .prog_data_i(prog_data),
+    .prog_data_i(prog_full_data),
     .prog_type_avail_o(prog_type_avail_o),
     .ack_o(ack),
     .rd_data_o(flash_rdata),
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv b/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
index 738ff86..949b151 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_pkg.sv
@@ -17,10 +17,14 @@
   parameter int WordW         = flash_ctrl_pkg::WordW;
   parameter int BankAddrW     = flash_ctrl_pkg::BankAddrW;
   parameter int DataWidth     = flash_ctrl_pkg::DataWidth;
+  parameter int EccWidth      = 8;
+  parameter int MetaDataWidth = top_pkg::FLASH_METADATA_WIDTH;
   parameter int WidthMultiple = flash_ctrl_pkg::WidthMultiple;
   parameter int NumBuf        = 4; // number of flash read buffers
   parameter int RspOrderDepth = 2; // this should be DataWidth / BusWidth
                                    // will switch to this after bus widening
+  parameter int ScrDataWidth  = DataWidth + EccWidth;
+  parameter int FullDataWidth = DataWidth + MetaDataWidth;
 
   // flash ctrl / bus parameters
   parameter int BusWidth       = flash_ctrl_pkg::BusWidth;
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_prog.sv b/hw/ip/flash_ctrl/rtl/flash_phy_prog.sv
index d64cf8f..cf273e9 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_prog.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_prog.sv
@@ -39,7 +39,9 @@
   output logic scramble_req_o,
   output logic req_o,
   output logic ack_o,
-  output logic [DataWidth-1:0] data_o
+  // block data does not contain ecc / metadata portion
+  output logic [DataWidth-1:0] block_data_o,
+  output logic [FullDataWidth-1:0] data_o
 );
 
   typedef enum logic [2:0] {
@@ -49,7 +51,8 @@
     StPostPack,
     StWaitFlash,
     StCalcMask,
-    StScrambleData
+    StScrambleData,
+    StCalcEcc
   } prog_state_e;
 
   typedef enum logic [1:0] {
@@ -167,10 +170,14 @@
         scramble_req_o = 1'b1;
 
         if (scramble_ack_i) begin
-          state_d = StWaitFlash;
+          state_d = StCalcEcc;
         end
       end
 
+      StCalcEcc: begin
+        state_d = StWaitFlash;
+      end
+
       StWaitFlash: begin
         req_o = 1'b1;
 
@@ -196,16 +203,24 @@
       packed_data <= packed_data ^ mask_i;
       mask_q <= mask_i;
     end else if (scramble_req_o && scramble_ack_i) begin
-      packed_data <= scrambled_data_i ^ mask_q;
+      packed_data <= scrambled_data_i[DataWidth-1:0] ^ mask_q;
     end else if (pack_valid) begin
       packed_data[idx] <= pack_data;
     end
   end
 
+  assign block_data_o = packed_data;
 
+  // ECC handling
+  logic [ScrDataWidth-1:0] ecc_data;
 
+  prim_secded_72_64_enc u_enc (
+    .in(packed_data),
+    .out(ecc_data)
+  );
 
-  assign data_o = packed_data;
+  // pad the remaining bits to '0', this effectively "programs" them.
+  assign data_o = scramble_i ? FullDataWidth'(ecc_data) : FullDataWidth'(packed_data);
 
 
   /////////////////////////////////
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
index 14855ae..9e00073 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
@@ -40,6 +40,7 @@
   input flash_ctrl_pkg::flash_part_e part_i,
   output logic rdy_o,
   output logic data_valid_o,
+  output logic data_err_o,
   output logic [BusWidth-1:0] data_o,
   output logic idle_o, // the entire read pipeline is idle
 
@@ -56,7 +57,7 @@
   // interface to actual flash primitive
   output logic req_o,
   input ack_i,
-  input [DataWidth-1:0] data_i
+  input [FullDataWidth-1:0] data_i
   );
 
   /////////////////////////////////
@@ -65,6 +66,7 @@
 
   // muxed de-scrambled and plain-data
   logic [DataWidth-1:0] muxed_data;
+  logic muxed_err;
 
   // muxed data valid signal that takes scrambling into consideration
   logic data_valid;
@@ -187,7 +189,7 @@
       .clk_i,
       .rst_ni,
       .alloc_i(rdy_o & alloc[i]),
-      .update_i(update[i]),
+      .update_i(update[i] & ~muxed_err),
       .wipe_i(data_hazard[i]),
       .addr_i(flash_word_addr),
       .part_i(part_i),
@@ -286,10 +288,49 @@
   // issue a transaction to flash
   assign req_o = req_i & rdy_o & no_match;
 
+
+  /////////////////////////////////
+  // Handling ECC
+  /////////////////////////////////
+
+  // only uncorrectable errors are passed on to the fabric
+  logic ecc_single_err;
+  logic ecc_err;
+  logic unused_err;
+  logic data_err;
+
+  // scrambled data must pass through ECC first
+  logic [DataWidth-1:0] data_ecc_chk;
+  logic [DataWidth-1:0] data_int;
+  logic data_erased;
+
+  // When all bits are 1, the data has been erased
+  // This check is only valid when read data returns.
+  assign data_erased = rd_done & (data_i == {FullDataWidth{1'b1}});
+
+  prim_secded_72_64_dec u_dec (
+    .in(data_i[ScrDataWidth-1:0]),
+    .d_o(data_ecc_chk),
+    .syndrome_o(),
+    .err_o({ecc_err, ecc_single_err})
+  );
+  assign unused_err = ecc_single_err;
+
+  // If data needs to be de-scrambled and has not been erased, pass through ecc decoder.
+  // Otherwise, pass the data through untouched.
+  // Likewise, ecc error is only observed if the data needs to be de-scrambled and has not been
+  // erased.
+  // rd_done signal below is duplicated (already in data_erased) to show clear intent of the code.
+  assign data_int = (rd_done && rd_attrs.descramble && !data_erased) ? data_ecc_chk :
+                                                                       data_i[DataWidth-1:0];
+  assign data_err = (rd_done && rd_attrs.descramble && !data_erased) ? ecc_err : 1'b0;
+
   /////////////////////////////////
   // De-scrambling stage
   /////////////////////////////////
 
+  // Even on ECC error, progress through the stage normally
+
   logic fifo_data_ready;
   logic fifo_data_valid;
   logic mask_valid;
@@ -297,9 +338,11 @@
   logic [DataWidth-1:0] mask;
   logic data_fifo_rdy;
   logic mask_fifo_rdy;
+  logic descram;
   logic forward;
   logic hint_forward;
   logic hint_descram;
+  logic data_err_q;
   logic [NumBuf-1:0] alloc_q2;
 
   assign scramble_stage_rdy = data_fifo_rdy & mask_fifo_rdy;
@@ -308,14 +351,15 @@
   // 1. When descrambling completes
   // 2. Immediately consumed when descrambling not required
   // 3. In both cases, when data has not already been forwarded
-  assign fifo_data_ready = hint_descram ? descramble_req_o & descramble_ack_i & ~hint_forward :
-                                          fifo_data_valid & !hint_forward;
+  assign fifo_data_ready = hint_descram ? descramble_req_o & descramble_ack_i :
+                                          fifo_data_valid;
 
-  // data is forwarded whenever it does not require descrambling or if it has been erased
-  // but forwarding is only possible if there are no entries in the FIFO to ensure the current
-  // read cannot run ahead of the descramble.
-  assign forward = rd_done & !fifo_data_valid &
-                   ((data_i == {DataWidth{1'b1}}) | !rd_attrs.descramble);
+  // descramble is only required if the location is scramble enabled AND it is not erased.
+  assign descram = rd_done & rd_attrs.descramble & ~data_erased;
+
+  // data is forwarded whenever it does not require descrambling and there are no entries in the
+  // FIFO to ensure the current read cannot run ahead of the descramble.
+  assign forward = rd_done & ~descram & ~fifo_data_valid;
 
   // storage for read outputs
   // This storage element can be fully merged with the fifo below if the time it takes
@@ -331,28 +375,33 @@
   // All these problems could be resolved if the timings matched exactly, however
   // the user would need to correctly setup constraints on either flash / gf_mult
   // timing change.
+  logic fifo_forward_pop;
+  assign fifo_forward_pop = hint_forward & fifo_data_valid;
+
   prim_fifo_sync #(
-    .Width   (DataWidth + 2 + NumBuf),
+    .Width   (DataWidth + 3 + NumBuf),
     .Pass    (0),
-    .Depth   (2)
+    .Depth   (2),
+    .OutputZeroIfEmpty (1)
   ) u_rd_storage (
     .clk_i,
     .rst_ni,
     .clr_i   (1'b0),
     .wvalid_i(rd_done),
     .wready_o(data_fifo_rdy),
-    .wdata_i ({alloc_q, rd_attrs.descramble,forward,data_i}),
+    .wdata_i ({alloc_q, descram, forward, data_err, data_int}),
     .depth_o (),
     .rvalid_o(fifo_data_valid),
-    .rready_i(fifo_data_ready | hint_forward),
-    .rdata_o ({alloc_q2, hint_descram,hint_forward,fifo_data})
+    .rready_i(fifo_data_ready | fifo_forward_pop),
+    .rdata_o ({alloc_q2, hint_descram, hint_forward, data_err_q, fifo_data})
   );
 
   // storage for mask calculations
   prim_fifo_sync #(
     .Width   (DataWidth),
     .Pass    (0),
-    .Depth   (2)
+    .Depth   (2),
+    .OutputZeroIfEmpty (1)
   ) u_mask_storage (
     .clk_i,
     .rst_ni,
@@ -362,11 +411,12 @@
     .wdata_i (mask_i),
     .depth_o (),
     .rvalid_o(mask_valid),
-    .rready_i(fifo_data_ready | hint_forward),
+    .rready_i(fifo_data_ready | fifo_forward_pop),
     .rdata_o (mask)
   );
 
   // generate the mask calculation request
+  // mask calculation is done in parallel to the read stage
   always_ff @(posedge clk_i or negedge rst_ni) begin
     if (!rst_ni) begin
       calc_req_o <= '0;
@@ -382,13 +432,17 @@
 
   // generate the descramble request whenever both stages are available
   // and there is a need to descramble
-  assign descramble_req_o = fifo_data_valid & mask_valid & !hint_forward;
+  assign descramble_req_o = fifo_data_valid & mask_valid & hint_descram;
 
   // scrambled data to de-scramble
   assign scrambled_data_o = fifo_data ^ mask;
 
-  // muxed data
-  assign muxed_data = hint_descram ? descrambled_data_i ^ mask : data_i;
+  // muxed responses
+  // When "forward" is true, hint_descram is always 0.
+  // This is because forward cannot set unless fifo is empty.  If FIFO is
+  // empty, hint_descram is automatically 0.
+  assign muxed_data = hint_descram ? descrambled_data_i ^ mask : data_int;
+  assign muxed_err  = hint_descram ? data_err_q : data_err;
 
   // muxed data valid
   // if no de-scramble required, return data on read complete
@@ -409,7 +463,6 @@
   // When forwarding, update entry stored in alloc_q
   // When de-scrambling however, the contents of alloc_q may have already updated to the next read,
   // so a different pointer is used.
-  // assign update = data_valid ? alloc_q : '0;
   assign update = forward         ? alloc_q  :
                   fifo_data_ready ? alloc_q2 : '0;
 
@@ -434,18 +487,21 @@
   if (WidthMultiple == 1) begin : gen_width_one_rd
     // When multiple is 1, just pass the read through directly
     logic unused_word_sel;
-    assign data_o = |buf_rsp_match ? buf_rsp_data : muxed_data;
+    assign data_o = data_err_o     ? {BusWidth{1'b1}} :
+                    |buf_rsp_match ? buf_rsp_data : muxed_data;
     assign unused_word_sel = rsp_fifo_rdata.word_sel;
 
   end else begin : gen_rd
     // Re-arrange data into packed array to pick the correct one
     logic [WidthMultiple-1:0][BusWidth-1:0] bus_words_packed;
     assign bus_words_packed = |buf_rsp_match ? buf_rsp_data : muxed_data;
-    assign data_o = bus_words_packed[rsp_fifo_rdata.word_sel];
+    assign data_o = data_err_o ? {BusWidth{1'b1}} : bus_words_packed[rsp_fifo_rdata.word_sel];
 
   end
 
+  // whenever the response is coming from the buffer, the error is never set
   assign data_valid_o = flash_rsp_match | |buf_rsp_match;
+  assign data_err_o   = muxed_err;
 
   // the entire read pipeline is idle when there are no responses to return and no
   assign idle_o = ~rsp_fifo_vld;
@@ -478,5 +534,10 @@
   // unless the pipeline is idle, we should not have non-read trasnactions
   `ASSERT(IdleCheck_A, !idle_o |-> {prog_i,pg_erase_i,bk_erase_i} == '0)
 
+  // Whenever forward is true, hint_descram should always be 0
+  `ASSERT(ForwardCheck_A, forward |-> hint_descram == '0)
+
+  // Whenever response is coming from buffer, ecc error cannot be set
+  `ASSERT(BufferMatchEcc_A, |buf_rsp_match |-> muxed_err == '0)
 
 endmodule // flash_phy_core
diff --git a/hw/ip/prim/rtl/prim_secded_72_64_dec.sv b/hw/ip/prim/rtl/prim_secded_72_64_dec.sv
index 02b1822..eb4ad3e 100644
--- a/hw/ip/prim/rtl/prim_secded_72_64_dec.sv
+++ b/hw/ip/prim/rtl/prim_secded_72_64_dec.sv
@@ -16,36 +16,36 @@
   // Syndrome calculation
   assign syndrome_o[0] = in[64] ^ in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[6] ^ in[7]
                        ^ in[8] ^ in[9] ^ in[10] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[15]
-                       ^ in[16] ^ in[17] ^ in[18] ^ in[19] ^ in[20] ^ in[57] ^ in[58] ^ in[61]
-                       ^ in[62] ^ in[63];
+                       ^ in[16] ^ in[17] ^ in[18] ^ in[19] ^ in[20] ^ in[57] ^ in[59] ^ in[60]
+                       ^ in[61] ^ in[62];
   assign syndrome_o[1] = in[65] ^ in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[21] ^ in[22]
                        ^ in[23] ^ in[24] ^ in[25] ^ in[26] ^ in[27] ^ in[28] ^ in[29] ^ in[30]
-                       ^ in[31] ^ in[32] ^ in[33] ^ in[34] ^ in[35] ^ in[58] ^ in[59] ^ in[60]
+                       ^ in[31] ^ in[32] ^ in[33] ^ in[34] ^ in[35] ^ in[56] ^ in[57] ^ in[60]
                        ^ in[62] ^ in[63];
   assign syndrome_o[2] = in[66] ^ in[0] ^ in[6] ^ in[7] ^ in[8] ^ in[9] ^ in[10] ^ in[21] ^ in[22]
                        ^ in[23] ^ in[24] ^ in[25] ^ in[36] ^ in[37] ^ in[38] ^ in[39] ^ in[40]
-                       ^ in[41] ^ in[42] ^ in[43] ^ in[44] ^ in[45] ^ in[56] ^ in[57] ^ in[59]
-                       ^ in[60] ^ in[63];
+                       ^ in[41] ^ in[42] ^ in[43] ^ in[44] ^ in[45] ^ in[56] ^ in[57] ^ in[58]
+                       ^ in[61] ^ in[62];
   assign syndrome_o[3] = in[67] ^ in[1] ^ in[6] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[21]
                        ^ in[26] ^ in[27] ^ in[28] ^ in[29] ^ in[36] ^ in[37] ^ in[38] ^ in[39]
-                       ^ in[46] ^ in[47] ^ in[48] ^ in[49] ^ in[50] ^ in[51] ^ in[56] ^ in[57]
-                       ^ in[58] ^ in[61] ^ in[63];
+                       ^ in[46] ^ in[47] ^ in[48] ^ in[49] ^ in[50] ^ in[51] ^ in[56] ^ in[58]
+                       ^ in[59] ^ in[62] ^ in[63];
   assign syndrome_o[4] = in[68] ^ in[2] ^ in[7] ^ in[11] ^ in[15] ^ in[16] ^ in[17] ^ in[22]
                        ^ in[26] ^ in[30] ^ in[31] ^ in[32] ^ in[36] ^ in[40] ^ in[41] ^ in[42]
-                       ^ in[46] ^ in[47] ^ in[48] ^ in[52] ^ in[53] ^ in[54] ^ in[56] ^ in[58]
-                       ^ in[59] ^ in[61] ^ in[62];
+                       ^ in[46] ^ in[47] ^ in[48] ^ in[52] ^ in[53] ^ in[54] ^ in[58] ^ in[59]
+                       ^ in[60] ^ in[61] ^ in[63];
   assign syndrome_o[5] = in[69] ^ in[3] ^ in[8] ^ in[12] ^ in[15] ^ in[18] ^ in[19] ^ in[23]
                        ^ in[27] ^ in[30] ^ in[33] ^ in[34] ^ in[37] ^ in[40] ^ in[43] ^ in[44]
                        ^ in[46] ^ in[49] ^ in[50] ^ in[52] ^ in[53] ^ in[55] ^ in[56] ^ in[57]
-                       ^ in[59] ^ in[60] ^ in[61];
+                       ^ in[58] ^ in[60] ^ in[63];
   assign syndrome_o[6] = in[70] ^ in[4] ^ in[9] ^ in[13] ^ in[16] ^ in[18] ^ in[20] ^ in[24]
                        ^ in[28] ^ in[31] ^ in[33] ^ in[35] ^ in[38] ^ in[41] ^ in[43] ^ in[45]
-                       ^ in[47] ^ in[49] ^ in[51] ^ in[52] ^ in[54] ^ in[55] ^ in[56] ^ in[59]
-                       ^ in[60] ^ in[61] ^ in[62];
+                       ^ in[47] ^ in[49] ^ in[51] ^ in[52] ^ in[54] ^ in[55] ^ in[56] ^ in[57]
+                       ^ in[59] ^ in[61] ^ in[63];
   assign syndrome_o[7] = in[71] ^ in[5] ^ in[10] ^ in[14] ^ in[17] ^ in[19] ^ in[20] ^ in[25]
                        ^ in[29] ^ in[32] ^ in[34] ^ in[35] ^ in[39] ^ in[42] ^ in[44] ^ in[45]
-                       ^ in[48] ^ in[50] ^ in[51] ^ in[53] ^ in[54] ^ in[55] ^ in[57] ^ in[58]
-                       ^ in[60] ^ in[62] ^ in[63];
+                       ^ in[48] ^ in[50] ^ in[51] ^ in[53] ^ in[54] ^ in[55] ^ in[58] ^ in[59]
+                       ^ in[60] ^ in[61] ^ in[62];
 
   // Corrected output calculation
   assign d_o[0] = (syndrome_o == 8'h7) ^ in[0];
@@ -104,14 +104,14 @@
   assign d_o[53] = (syndrome_o == 8'hb0) ^ in[53];
   assign d_o[54] = (syndrome_o == 8'hd0) ^ in[54];
   assign d_o[55] = (syndrome_o == 8'he0) ^ in[55];
-  assign d_o[56] = (syndrome_o == 8'h7c) ^ in[56];
-  assign d_o[57] = (syndrome_o == 8'had) ^ in[57];
-  assign d_o[58] = (syndrome_o == 8'h9b) ^ in[58];
-  assign d_o[59] = (syndrome_o == 8'h76) ^ in[59];
-  assign d_o[60] = (syndrome_o == 8'he6) ^ in[60];
-  assign d_o[61] = (syndrome_o == 8'h79) ^ in[61];
-  assign d_o[62] = (syndrome_o == 8'hd3) ^ in[62];
-  assign d_o[63] = (syndrome_o == 8'h8f) ^ in[63];
+  assign d_o[56] = (syndrome_o == 8'h6e) ^ in[56];
+  assign d_o[57] = (syndrome_o == 8'h67) ^ in[57];
+  assign d_o[58] = (syndrome_o == 8'hbc) ^ in[58];
+  assign d_o[59] = (syndrome_o == 8'hd9) ^ in[59];
+  assign d_o[60] = (syndrome_o == 8'hb3) ^ in[60];
+  assign d_o[61] = (syndrome_o == 8'hd5) ^ in[61];
+  assign d_o[62] = (syndrome_o == 8'h8f) ^ in[62];
+  assign d_o[63] = (syndrome_o == 8'h7a) ^ in[63];
 
   // err_o calc. bit0: single error, bit1: double error
   assign single_error = ^syndrome_o;
diff --git a/hw/ip/prim/rtl/prim_secded_72_64_enc.sv b/hw/ip/prim/rtl/prim_secded_72_64_enc.sv
index 207c45c..cf89f37 100644
--- a/hw/ip/prim/rtl/prim_secded_72_64_enc.sv
+++ b/hw/ip/prim/rtl/prim_secded_72_64_enc.sv
@@ -75,27 +75,27 @@
   assign out[63] = in[63] ;
   assign out[64] = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[6] ^ in[7] ^ in[8] ^ in[9]
                  ^ in[10] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[15] ^ in[16] ^ in[17] ^ in[18]
-                 ^ in[19] ^ in[20] ^ in[57] ^ in[58] ^ in[61] ^ in[62] ^ in[63];
+                 ^ in[19] ^ in[20] ^ in[57] ^ in[59] ^ in[60] ^ in[61] ^ in[62];
   assign out[65] = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[21] ^ in[22] ^ in[23] ^ in[24]
                  ^ in[25] ^ in[26] ^ in[27] ^ in[28] ^ in[29] ^ in[30] ^ in[31] ^ in[32] ^ in[33]
-                 ^ in[34] ^ in[35] ^ in[58] ^ in[59] ^ in[60] ^ in[62] ^ in[63];
+                 ^ in[34] ^ in[35] ^ in[56] ^ in[57] ^ in[60] ^ in[62] ^ in[63];
   assign out[66] = in[0] ^ in[6] ^ in[7] ^ in[8] ^ in[9] ^ in[10] ^ in[21] ^ in[22] ^ in[23]
                  ^ in[24] ^ in[25] ^ in[36] ^ in[37] ^ in[38] ^ in[39] ^ in[40] ^ in[41] ^ in[42]
-                 ^ in[43] ^ in[44] ^ in[45] ^ in[56] ^ in[57] ^ in[59] ^ in[60] ^ in[63];
+                 ^ in[43] ^ in[44] ^ in[45] ^ in[56] ^ in[57] ^ in[58] ^ in[61] ^ in[62];
   assign out[67] = in[1] ^ in[6] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[21] ^ in[26] ^ in[27]
                  ^ in[28] ^ in[29] ^ in[36] ^ in[37] ^ in[38] ^ in[39] ^ in[46] ^ in[47] ^ in[48]
-                 ^ in[49] ^ in[50] ^ in[51] ^ in[56] ^ in[57] ^ in[58] ^ in[61] ^ in[63];
+                 ^ in[49] ^ in[50] ^ in[51] ^ in[56] ^ in[58] ^ in[59] ^ in[62] ^ in[63];
   assign out[68] = in[2] ^ in[7] ^ in[11] ^ in[15] ^ in[16] ^ in[17] ^ in[22] ^ in[26] ^ in[30]
                  ^ in[31] ^ in[32] ^ in[36] ^ in[40] ^ in[41] ^ in[42] ^ in[46] ^ in[47] ^ in[48]
-                 ^ in[52] ^ in[53] ^ in[54] ^ in[56] ^ in[58] ^ in[59] ^ in[61] ^ in[62];
+                 ^ in[52] ^ in[53] ^ in[54] ^ in[58] ^ in[59] ^ in[60] ^ in[61] ^ in[63];
   assign out[69] = in[3] ^ in[8] ^ in[12] ^ in[15] ^ in[18] ^ in[19] ^ in[23] ^ in[27] ^ in[30]
                  ^ in[33] ^ in[34] ^ in[37] ^ in[40] ^ in[43] ^ in[44] ^ in[46] ^ in[49] ^ in[50]
-                 ^ in[52] ^ in[53] ^ in[55] ^ in[56] ^ in[57] ^ in[59] ^ in[60] ^ in[61];
+                 ^ in[52] ^ in[53] ^ in[55] ^ in[56] ^ in[57] ^ in[58] ^ in[60] ^ in[63];
   assign out[70] = in[4] ^ in[9] ^ in[13] ^ in[16] ^ in[18] ^ in[20] ^ in[24] ^ in[28] ^ in[31]
                  ^ in[33] ^ in[35] ^ in[38] ^ in[41] ^ in[43] ^ in[45] ^ in[47] ^ in[49] ^ in[51]
-                 ^ in[52] ^ in[54] ^ in[55] ^ in[56] ^ in[59] ^ in[60] ^ in[61] ^ in[62];
+                 ^ in[52] ^ in[54] ^ in[55] ^ in[56] ^ in[57] ^ in[59] ^ in[61] ^ in[63];
   assign out[71] = in[5] ^ in[10] ^ in[14] ^ in[17] ^ in[19] ^ in[20] ^ in[25] ^ in[29] ^ in[32]
                  ^ in[34] ^ in[35] ^ in[39] ^ in[42] ^ in[44] ^ in[45] ^ in[48] ^ in[50] ^ in[51]
-                 ^ in[53] ^ in[54] ^ in[55] ^ in[57] ^ in[58] ^ in[60] ^ in[62] ^ in[63];
+                 ^ in[53] ^ in[54] ^ in[55] ^ in[58] ^ in[59] ^ in[60] ^ in[61] ^ in[62];
 endmodule
 
diff --git a/hw/ip/prim_generic/rtl/prim_generic_flash.sv b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
index b6962d7..718c1b9 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_flash.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
@@ -10,6 +10,7 @@
   parameter int PagesPerBank = 256, // data pages per bank
   parameter int WordsPerPage = 256, // words per page
   parameter int DataWidth   = 32,   // bits per word
+  parameter int MetaDataWidth = 12, // this is a temporary parameter to work around ECC issues
   parameter bit SkipInit = 1,       // this is an option to reset flash to all F's at reset
 
   // Derived parameters
@@ -297,36 +298,70 @@
     endcase // unique case (st_q)
   end // always_comb
 
+  localparam int MemWidth = DataWidth - MetaDataWidth;
+
   logic [DataWidth-1:0] rd_data_main, rd_data_info;
+  logic [MemWidth-1:0] rd_nom_data_main, rd_nom_data_info;
+  logic [MetaDataWidth-1:0] rd_meta_data_main, rd_meta_data_info;
 
   prim_ram_1p #(
-    .Width(DataWidth),
+    .Width(MemWidth),
     .Depth(WordsPerBank),
-    .DataBitsPerMask(DataWidth)
+    .DataBitsPerMask(MemWidth)
   ) u_mem (
     .clk_i,
     .req_i    (mem_req & (mem_part == flash_ctrl_pkg::FlashPartData)),
     .write_i  (mem_wr),
     .addr_i   (mem_addr),
-    .wdata_i  (mem_wdata),
-    .wmask_i  ({DataWidth{1'b1}}),
-    .rdata_o  (rd_data_main)
+    .wdata_i  (mem_wdata[MemWidth-1:0]),
+    .wmask_i  ({MemWidth{1'b1}}),
+    .rdata_o  (rd_nom_data_main)
   );
 
   prim_ram_1p #(
-    .Width(DataWidth),
+    .Width(MetaDataWidth),
+    .Depth(WordsPerBank),
+    .DataBitsPerMask(MetaDataWidth)
+  ) u_mem_meta (
+    .clk_i,
+    .req_i    (mem_req & (mem_part == flash_ctrl_pkg::FlashPartData)),
+    .write_i  (mem_wr),
+    .addr_i   (mem_addr),
+    .wdata_i  (mem_wdata[MemWidth +: MetaDataWidth]),
+    .wmask_i  ({MetaDataWidth{1'b1}}),
+    .rdata_o  (rd_meta_data_main)
+  );
+
+  prim_ram_1p #(
+    .Width(MemWidth),
     .Depth(WordsPerInfoBank),
-    .DataBitsPerMask(DataWidth)
+    .DataBitsPerMask(MemWidth)
   ) u_info_mem (
     .clk_i,
     .req_i    (mem_req & (mem_part == flash_ctrl_pkg::FlashPartInfo)),
     .write_i  (mem_wr),
     .addr_i   (mem_addr[0 +: InfoAddrW]),
-    .wdata_i  (mem_wdata),
-    .wmask_i  ({DataWidth{1'b1}}),
-    .rdata_o  (rd_data_info)
+    .wdata_i  (mem_wdata[MemWidth-1:0]),
+    .wmask_i  ({MemWidth{1'b1}}),
+    .rdata_o  (rd_nom_data_info)
   );
 
+  prim_ram_1p #(
+    .Width(MetaDataWidth),
+    .Depth(WordsPerInfoBank),
+    .DataBitsPerMask(MetaDataWidth)
+  ) u_info_mem_meta (
+    .clk_i,
+    .req_i    (mem_req & (mem_part == flash_ctrl_pkg::FlashPartInfo)),
+    .write_i  (mem_wr),
+    .addr_i   (mem_addr[0 +: InfoAddrW]),
+    .wdata_i  (mem_wdata[MemWidth +: MetaDataWidth]),
+    .wmask_i  ({MetaDataWidth{1'b1}}),
+    .rdata_o  (rd_meta_data_info)
+  );
+
+  assign rd_data_main = {rd_meta_data_main, rd_nom_data_main};
+  assign rd_data_info = {rd_meta_data_info, rd_nom_data_info};
   assign rd_data_o = held_part == flash_ctrl_pkg::FlashPartData ? rd_data_main : rd_data_info;
 
   // hard-wire assignment for now
diff --git a/hw/top_earlgrey/data/top_earlgrey.sv.tpl b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
index 37c6612..b7c98a3 100644
--- a/hw/top_earlgrey/data/top_earlgrey.sv.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
@@ -406,6 +406,7 @@
   logic flash_host_req;
   logic flash_host_req_rdy;
   logic flash_host_req_done;
+  logic flash_host_rderr;
   logic [flash_ctrl_pkg::BusWidth-1:0] flash_host_rdata;
   logic [flash_ctrl_pkg::BusAddrW-1:0] flash_host_addr;
 
@@ -434,7 +435,7 @@
     .wmask_o  (),
     .rdata_i  (flash_host_rdata),
     .rvalid_i (flash_host_req_done),
-    .rerror_i (2'b00)
+    .rerror_i ({flash_host_rderr,1'b0})
   );
 
   flash_phy u_flash_${m["name"]} (
@@ -448,6 +449,7 @@
     .host_addr_i     (flash_host_addr),
     .host_req_rdy_o  (flash_host_req_rdy),
     .host_req_done_o (flash_host_req_done),
+    .host_rderr_o    (flash_host_rderr),
     .host_rdata_o    (flash_host_rdata),
     .flash_ctrl_i    (${m["inter_signal_list"][0]["top_signame"]}_req),
     .flash_ctrl_o    (${m["inter_signal_list"][0]["top_signame"]}_rsp)
diff --git a/hw/top_earlgrey/rtl/top_pkg.sv b/hw/top_earlgrey/rtl/top_pkg.sv
index 50269e2..58a8438 100644
--- a/hw/top_earlgrey/rtl/top_pkg.sv
+++ b/hw/top_earlgrey/rtl/top_pkg.sv
@@ -17,6 +17,7 @@
 localparam int FLASH_WORDS_PER_PAGE=128;
 localparam int FLASH_INFO_PER_BANK=4;
 localparam int FLASH_DATA_WIDTH=64;
+localparam int FLASH_METADATA_WIDTH=12;
 localparam int NUM_AST_ALERTS=7;
 localparam int NUM_IO_RAILS=2;
 localparam int ENTROPY_STREAM=4;