[flash_ctrl] design and dv updates for alerts

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl b/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
index 7592c78..2807c24 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
+++ b/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
@@ -815,8 +815,10 @@
     ) u_alert_sender (
       .clk_i,
       .rst_ni,
-      .alert_req_i(alert_srcs[i] | alert_tests[i]),
+      .alert_req_i(alert_srcs[i]),
+      .alert_test_i(alert_tests[i]),
       .alert_ack_o(),
+      .alert_state_o(),
       .alert_rx_i(alert_rx_i[i]),
       .alert_tx_o(alert_tx_o[i])
     );
diff --git a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv
index 4461f69..c923b27 100644
--- a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv
+++ b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv
@@ -25,6 +25,7 @@
   `uvm_object_new
 
   virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1);
+    list_of_alerts = flash_ctrl_env_pkg::LIST_OF_ALERTS;
     super.initialize(csr_base_addr);
     // create tl agent config obj
     m_eflash_tl_agent_cfg = tl_agent_cfg::type_id::create("m_eflash_tl_agent_cfg");
diff --git a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_pkg.sv b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_pkg.sv
index f62ac45..c80b91a 100644
--- a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_pkg.sv
+++ b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_pkg.sv
@@ -20,7 +20,8 @@
   `include "dv_macros.svh"
 
   // parameters
-
+  parameter string LIST_OF_ALERTS[] = {"fatal_err", "recov_mp_err", "recov_ecc_err"};
+  parameter uint NUM_ALERTS = 3;
   parameter uint FlashNumPages            = flash_ctrl_pkg::NumBanks * flash_ctrl_pkg::PagesPerBank;
   parameter uint FlashSizeBytes           = FlashNumPages * flash_ctrl_pkg::WordsPerPage *
                                             flash_ctrl_pkg::DataWidth / 8;
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 fd4df3a..e116448 100644
--- a/hw/ip/flash_ctrl/dv/tb/flash_ctrl_wrapper.sv
+++ b/hw/ip/flash_ctrl/dv/tb/flash_ctrl_wrapper.sv
@@ -43,7 +43,10 @@
   output logic intr_prog_lvl_o,   // Program fifo is empty
   output logic intr_rd_full_o,    // Read fifo is full
   output logic intr_rd_lvl_o,     // Read fifo is full
-  output logic intr_op_done_o     // Requested flash operation (wr/erase) done
+  output logic intr_op_done_o,    // Requested flash operation (wr/erase) done
+
+  input  prim_alert_pkg::alert_rx_t [flash_ctrl_reg_pkg::NumAlerts-1:0] alert_rx_i,
+  output prim_alert_pkg::alert_tx_t [flash_ctrl_reg_pkg::NumAlerts-1:0] alert_tx_o
 );
 
   // define inter-module signals
@@ -61,6 +64,10 @@
     .intr_rd_lvl_o    (intr_rd_lvl_o),
     .intr_op_done_o   (intr_op_done_o),
 
+    // Alerts
+    .alert_rx_i,
+    .alert_tx_o,
+
     // Inter-module signals
     .flash_o           (flash_ctrl_flash_req),
     .flash_i           (flash_ctrl_flash_rsp),
diff --git a/hw/ip/flash_ctrl/dv/tb/tb.sv b/hw/ip/flash_ctrl/dv/tb/tb.sv
index 2de60ec..287f2cf 100644
--- a/hw/ip/flash_ctrl/dv/tb/tb.sv
+++ b/hw/ip/flash_ctrl/dv/tb/tb.sv
@@ -31,6 +31,8 @@
   tl_if tl_if(.clk(clk), .rst_n(rst_n));
   tl_if eflash_tl_if(.clk(clk), .rst_n(rst_n));
 
+  `DV_ALERT_IF_CONNECT
+
   // dut
   flash_ctrl_wrapper dut (
     .clk_i              (clk      ),
@@ -67,7 +69,9 @@
     .intr_prog_lvl_o    (intr_prog_lvl  ),
     .intr_rd_full_o     (intr_rd_full   ),
     .intr_rd_lvl_o      (intr_rd_lvl    ),
-    .intr_op_done_o     (intr_op_done   )
+    .intr_op_done_o     (intr_op_done   ),
+    .alert_rx_i         (alert_rx       ),
+    .alert_tx_o         (alert_tx       )
   );
 
   // bind mem_bkdr_if
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
index fca06d5..9661d91 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
@@ -335,10 +335,15 @@
   logic data_err;
 
   // scrambled data must pass through ECC first
+  logic valid_ecc;
+  logic ecc_multi_err;
+  logic ecc_single_err;
   logic [DataWidth-1:0] data_ecc_chk;
   logic [DataWidth-1:0] data_int;
   logic data_erased;
 
+  assign valid_ecc = rd_done && rd_attrs.ecc && !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}});
@@ -347,20 +352,21 @@
     .in(data_i[ScrDataWidth-1:0]),
     .d_o(data_ecc_chk),
     .syndrome_o(),
-    .err_o({ecc_multi_err_o, ecc_single_err_o})
+    .err_o({ecc_multi_err, ecc_single_err})
   );
 
-  // ecc address return is always the full flash word
-  assign ecc_addr_o = {rd_attrs.addr, {LsbAddrBit{1'b0}}};
-
   // 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.ecc && !data_erased) ? data_ecc_chk :
-                                                                data_i[DataWidth-1:0];
-  assign data_err = (rd_done && rd_attrs.ecc && !data_erased) ? ecc_multi_err_o : 1'b0;
+  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;
+  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}}};
 
   /////////////////////////////////
   // De-scrambling stage