[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),