[flash_ctrl] ADD ERASE SUSPEND TEST
Test erase suspend while random erase is in progress and also when there is
no erase at all. Added support for backdoor reading of erased bank and page.
Signed-off-by: Nikola Miladinovic <nikola.miladinovic@ensilica.com>
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl_testplan.hjson b/hw/ip/flash_ctrl/data/flash_ctrl_testplan.hjson
index fb170af..31d2d6d 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl_testplan.hjson
+++ b/hw/ip/flash_ctrl/data/flash_ctrl_testplan.hjson
@@ -86,7 +86,7 @@
Read affected bank in order to verify erase suspension feature.
'''
milestone: V2
- tests: []
+ tests: ["flash_ctrl_erase_suspend"]
}
{
name: full_memory_access
diff --git a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env.core b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env.core
index fd5e468..e7394e7 100644
--- a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env.core
+++ b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env.core
@@ -39,6 +39,7 @@
- seq_lib/flash_ctrl_rd_buff_evict_vseq.sv: {is_include_file: true}
- seq_lib/flash_ctrl_phy_arb_vseq.sv: {is_include_file: true}
- seq_lib/flash_ctrl_hw_sec_otp_vseq.sv: {is_include_file: true}
+ - seq_lib/flash_ctrl_erase_suspend_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource
generate:
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 663a3e2..a137fed 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
@@ -94,17 +94,55 @@
// The addr arg need not be word aligned - its the same addr programmed into the `control` CSR.
// TODO: add support for partition.
virtual function void flash_mem_bkdr_read(flash_op_t flash_op, ref data_q_t data);
- flash_mem_addr_attrs addr_attrs = new(flash_op.addr);
+ flash_mem_addr_attrs addr_attrs = new(flash_op.addr);
+ bit [TL_AW-1:0] read_addr;
+
+ if (flash_op.op == flash_ctrl_pkg::FlashOpErase) begin
+ case (flash_op.erase_type)
+ flash_ctrl_pkg::FlashErasePage: begin
+ read_addr = addr_attrs.page_start_addr;
+ flash_op.num_words = FlashNumBusWordsPerPage;
+ end
+ flash_ctrl_pkg::FlashEraseBank: begin
+ read_addr = 0;
+ case (flash_op.partition)
+ FlashPartData: begin
+ flash_op.num_words = FlashNumBusWordsPerBank;
+ end
+ FlashPartInfo: begin
+ flash_op.num_words = InfoTypeBusWords[0];
+ end
+ default: begin
+ `uvm_fatal(`gfn, $sformatf(
+ {
+ "Invalid partition for bank_erase: %0s. ",
+ "Bank erase is only valid in the data partition ",
+ "(FlashPartData) and the first info partition ",
+ "(FlashPartInfo)."
+ },
+ flash_op.partition.name()
+ ))
+ end
+ endcase
+ end
+ default: begin
+ `uvm_fatal(`gfn, $sformatf("Invalid erase_type: %0s", flash_op.erase_type.name()))
+ end
+ endcase
+ end else begin // FlashOpProgram, FlashOpRead
+ read_addr = addr_attrs.bank_addr;
+ end
+
data.delete();
for (int i = 0; i < flash_op.num_words; i++) begin
- data[i] = mem_bkdr_util_h[flash_op.partition][addr_attrs.bank].read32(addr_attrs.bank_addr);
+ data[i] = mem_bkdr_util_h[flash_op.partition][addr_attrs.bank].read32(read_addr);
`uvm_info(`gfn, $sformatf(
"flash_mem_bkdr_read: partition = %s , {%s} = 0x%0h",
flash_op.partition.name(),
addr_attrs.sprint(),
data[i]
), UVM_MEDIUM)
- addr_attrs.incr(TL_DBW);
+ read_addr += TL_DBW;
end
endfunction : flash_mem_bkdr_read
diff --git a/hw/ip/flash_ctrl/dv/env/flash_ctrl_scoreboard.sv b/hw/ip/flash_ctrl/dv/env/flash_ctrl_scoreboard.sv
index 4d37253..71fc573 100644
--- a/hw/ip/flash_ctrl/dv/env/flash_ctrl_scoreboard.sv
+++ b/hw/ip/flash_ctrl/dv/env/flash_ctrl_scoreboard.sv
@@ -147,7 +147,7 @@
"intr_test": begin
end
- "op_status", "status": begin
+ "op_status", "status", "erase_suspend": begin
// TODO: FIXME
do_read_check = 1'b0;
end
diff --git a/hw/ip/flash_ctrl/dv/env/seq_lib/flash_ctrl_erase_suspend_vseq.sv b/hw/ip/flash_ctrl/dv/env/seq_lib/flash_ctrl_erase_suspend_vseq.sv
new file mode 100644
index 0000000..4a14e2f
--- /dev/null
+++ b/hw/ip/flash_ctrl/dv/env/seq_lib/flash_ctrl_erase_suspend_vseq.sv
@@ -0,0 +1,279 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// Erase suspend test. Activate Erase suspend in following cases:
+// 1. Scenario - Erase is not active
+// 2. Scenario - Erase is in progress
+class flash_ctrl_erase_suspend_vseq extends flash_ctrl_base_vseq;
+ `uvm_object_utils(flash_ctrl_erase_suspend_vseq)
+
+ `uvm_object_new
+
+ // Configure sequence knobs to tailor it to smoke seq.
+ virtual function void configure_vseq();
+ // number of transactions
+ cfg.seq_cfg.max_num_trans = 6;
+
+ // no overlap mp regions
+ cfg.seq_cfg.allow_mp_region_overlap = 0;
+
+ // equal chance for bank and page erase
+ cfg.seq_cfg.op_erase_type_bank_pc = 20;
+
+ // enable scramble
+ cfg.seq_cfg.mp_region_scramble_en_pc = 50;
+ cfg.seq_cfg.default_region_scramble_en_pc = 50;
+
+ // enable high endurance
+ cfg.seq_cfg.mp_region_he_en_pc = 50;
+ cfg.seq_cfg.default_region_he_en_pc = 50;
+
+ // randomize memory
+ cfg.seq_cfg.flash_init_set_pc = 0;
+
+ endfunction
+
+ // Randomized flash ctrl operation.
+ rand flash_op_t flash_op;
+
+ rand uint bank;
+
+ // Constraint for banks.
+ constraint bank_c {bank inside {[0 : flash_ctrl_pkg::NumBanks - 1]};}
+
+ // Constraint address to be in relevant range for the selected partition.
+ constraint addr_c {
+ if (flash_op.partition != FlashPartData) {
+ flash_op.addr inside
+ {[0:InfoTypeBytes[flash_op.partition>>1]-1],
+ [BytesPerBank:BytesPerBank+InfoTypeBytes[flash_op.partition>>1]-1]};
+ }
+ }
+
+ constraint flash_op_c {
+ flash_op.addr inside {[0 : FlashSizeBytes - 1]};
+ flash_op.op == flash_ctrl_pkg::FlashOpErase;
+
+ // Bank erase is supported only for data & 1st info partitions
+ flash_op.partition != FlashPartData && flash_op.partition != FlashPartInfo ->
+ flash_op.erase_type == flash_ctrl_pkg::FlashErasePage;
+
+ flash_op.erase_type dist {
+ flash_ctrl_pkg::FlashErasePage :/ (100 - cfg.seq_cfg.op_erase_type_bank_pc),
+ flash_ctrl_pkg::FlashEraseBank :/ cfg.seq_cfg.op_erase_type_bank_pc
+ };
+ }
+
+ // Bit vector representing which of the mp region cfg CSRs to enable.
+ rand bit [flash_ctrl_pkg::MpRegions-1:0] en_mp_regions;
+
+ constraint en_mp_regions_c {$countones(en_mp_regions) == cfg.seq_cfg.num_en_mp_regions;}
+
+ // Memory protection regions settings.
+ rand flash_mp_region_cfg_t mp_regions[flash_ctrl_pkg::MpRegions];
+
+ constraint mp_regions_c {
+ solve en_mp_regions before mp_regions;
+
+ foreach (mp_regions[i]) {
+ mp_regions[i].en == en_mp_regions[i];
+
+ mp_regions[i].read_en == 1;
+
+ mp_regions[i].program_en == 1;
+
+ mp_regions[i].erase_en == 1;
+
+ mp_regions[i].scramble_en dist {
+ 0 :/ (100 - cfg.seq_cfg.mp_region_scramble_en_pc),
+ 1 :/ cfg.seq_cfg.mp_region_scramble_en_pc
+ };
+
+ mp_regions[i].ecc_en == 0;
+
+ mp_regions[i].he_en dist {
+ 0 :/ (100 - cfg.seq_cfg.mp_region_he_en_pc),
+ 1 :/ cfg.seq_cfg.mp_region_he_en_pc
+ };
+
+ mp_regions[i].start_page inside {[0 : FlashNumPages - 1]};
+ mp_regions[i].num_pages inside {[1 : FlashNumPages - mp_regions[i].start_page]};
+ mp_regions[i].num_pages <= cfg.seq_cfg.mp_region_max_pages;
+
+ // If overlap not allowed, then each configured region is uniquified.
+ // This creates an ascending order of mp_regions that are configured, so we shuffle it in
+ // post_randomize.
+ if (!cfg.seq_cfg.allow_mp_region_overlap) {
+ foreach (mp_regions[j]) {
+ if (i != j) {
+ !mp_regions[i].start_page inside {
+ [mp_regions[j].start_page:mp_regions[j].start_page + mp_regions[j].num_pages]
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // Information partitions memory protection pages settings.
+ rand flash_bank_mp_info_page_cfg_t
+ mp_info_pages[flash_ctrl_pkg::NumBanks][flash_ctrl_pkg::InfoTypes][$];
+
+ constraint mp_info_pages_c {
+
+ foreach (mp_info_pages[i, j]) {
+
+ mp_info_pages[i][j].size() == flash_ctrl_pkg::InfoTypeSize[j];
+
+ foreach (mp_info_pages[i][j][k]) {
+
+ mp_info_pages[i][j][k].en == 1;
+
+ mp_info_pages[i][j][k].read_en == 1;
+
+ mp_info_pages[i][j][k].program_en == 1;
+
+ mp_info_pages[i][j][k].erase_en == 1;
+
+ mp_info_pages[i][j][k].ecc_en == 0;
+
+ mp_info_pages[i][j][k].he_en dist {
+ 0 :/ (100 - cfg.seq_cfg.mp_info_page_he_en_pc[i][j]),
+ 1 :/ cfg.seq_cfg.mp_info_page_he_en_pc[i][j]
+ };
+ }
+ }
+ }
+
+ // Default flash ctrl region settings.
+ bit default_region_read_en;
+ bit default_region_program_en;
+ bit default_region_erase_en;
+ rand bit default_region_scramble_en;
+ rand bit default_region_he_en;
+ bit default_region_ecc_en;
+
+ constraint default_scramble_he_en_c {
+ default_region_scramble_en dist {
+ 1 :/ cfg.seq_cfg.default_region_scramble_en_pc,
+ 0 :/ (100 - cfg.seq_cfg.default_region_scramble_en_pc)
+ };
+ }
+
+ constraint default_region_he_en_c {
+ default_region_he_en dist {
+ 1 :/ cfg.seq_cfg.default_region_he_en_pc,
+ 0 :/ (100 - cfg.seq_cfg.default_region_he_en_pc)
+ };
+ }
+
+ // Bank erasability.
+ rand bit [flash_ctrl_pkg::NumBanks-1:0] bank_erase_en;
+
+ constraint bank_erase_en_c {
+ foreach (bank_erase_en[i]) {
+ bank_erase_en[i] == 1;
+ }
+ }
+
+ data_q_t flash_rd_data;
+ uvm_reg_data_t data;
+ time timeout_ns = 1_000;
+ localparam bit [TL_DW-1:0] ALL_ONES = {TL_DW{1'b1}};
+
+ virtual task body();
+ repeat (cfg.seq_cfg.max_num_trans) begin
+ `DV_CHECK_RANDOMIZE_FATAL(this)
+ reset_flash();
+ do_erase();
+ end
+ endtask : body
+
+ virtual task do_erase();
+
+ // Default region settings
+ default_region_read_en = 1;
+ default_region_program_en = 1;
+ default_region_erase_en = 1;
+ default_region_ecc_en = 0;
+
+ // Configure the flash with scramble disable.
+ foreach (mp_regions[k]) begin
+ mp_regions[k].scramble_en = 0;
+ flash_ctrl_mp_region_cfg(k, mp_regions[k]);
+ `uvm_info(`gfn, $sformatf("MP regions values %p", mp_regions[k]), UVM_HIGH)
+ end
+
+ flash_ctrl_default_region_cfg(
+ .read_en(default_region_read_en), .program_en(default_region_program_en),
+ .erase_en(default_region_erase_en), .scramble_en(default_region_scramble_en),
+ .ecc_en(default_region_ecc_en), .he_en(default_region_he_en));
+
+ foreach (mp_info_pages[i, j, k]) begin
+ flash_ctrl_mp_info_page_cfg(i, j, k, mp_info_pages[i][j][k]);
+ `uvm_info(`gfn, $sformatf("MP INFO regions values %p", mp_info_pages[i][j][k]), UVM_HIGH)
+ end
+ //Enable Bank erase
+ flash_ctrl_bank_erase_cfg(.bank_erase_en(bank_erase_en));
+
+ // 1. Scenario - erase is not active
+ `uvm_info(`gfn, $sformatf("Scenario 1: erase is not active"), UVM_HIGH)
+
+ // Read data before writing
+ csr_rd(.ptr(ral.erase_suspend), .value(data));
+ `uvm_info(`gfn, $sformatf("ERASE SUSPEND DATA BEFORE WR: %0p", data), UVM_HIGH)
+
+ // Invoke erase suspend request
+ csr_wr(.ptr(ral.erase_suspend), .value(1));
+ `uvm_info(`gfn, $sformatf("ERASE SUSPEND DATA AFTER WR: %0p", data), UVM_HIGH)
+
+ // value of error suspend request should be imediatelly cleared when
+ // erase is not in progress
+ csr_rd_check(.ptr(ral.erase_suspend.req), .compare_value(0));
+
+ // 2. Scneario - Erase is in progress
+ `uvm_info(`gfn, $sformatf("2. Scenario - Erase is in progress"), UVM_HIGH)
+
+ fork
+ begin : isolation_fork
+ fork
+ begin // erase data
+ `uvm_info(`gfn, $sformatf("FLASH OP ERASE START OP: %0p", flash_op), UVM_HIGH)
+ flash_ctrl_start_op(flash_op);
+ wait_flash_op_done(.timeout_ns(cfg.seq_cfg.erase_timeout_ns));
+ end
+ begin // erase suspend while erase data is in progress
+ `uvm_info(`gfn, $sformatf("START COUNTING BEFORE ES REQ"), UVM_HIGH)
+ cfg.clk_rst_vif.wait_clks($urandom_range(50, 100));
+ csr_wr(.ptr(ral.erase_suspend), .value(1));
+ `uvm_info(`gfn, $sformatf("ERASE SUSPEND REQUESTED"), UVM_HIGH)
+ end
+ join_any;
+ disable fork;
+ end : isolation_fork
+ join
+
+ // WAITING THAT ERASE SUSPEND REQ IS DONE AND REQ RETURNED TO ZERO
+ `DV_SPINWAIT(do begin
+ csr_rd(.ptr(ral.erase_suspend), .value(data));
+ `uvm_info(`gfn, $sformatf("ERASE SUSPEND REQ: %0p", data), UVM_HIGH)
+ end while (data == 1);, "ERASE SUSPEND TIMEOUT OCCURED!", timeout_ns)
+
+ cfg.flash_mem_bkdr_read(flash_op, flash_rd_data);
+
+ foreach (flash_rd_data[i]) begin
+ `uvm_info(`gfn, $sformatf("FLASH RD DATA: %0h", flash_rd_data[i]), UVM_HIGH)
+ // first 30 data are erased
+ if (i <= 30) begin
+ `DV_CHECK_EQ(flash_rd_data[i], ALL_ONES)
+ end
+ // last 50 data are not erased
+ if ((flash_rd_data.size()-i) <= 50) begin
+ `DV_CHECK_NE(flash_rd_data[i], ALL_ONES)
+ end
+ end
+
+ endtask : do_erase
+
+endclass : flash_ctrl_erase_suspend_vseq
diff --git a/hw/ip/flash_ctrl/dv/env/seq_lib/flash_ctrl_vseq_list.sv b/hw/ip/flash_ctrl/dv/env/seq_lib/flash_ctrl_vseq_list.sv
index d2e6f2c..afa44ca 100644
--- a/hw/ip/flash_ctrl/dv/env/seq_lib/flash_ctrl_vseq_list.sv
+++ b/hw/ip/flash_ctrl/dv/env/seq_lib/flash_ctrl_vseq_list.sv
@@ -14,3 +14,4 @@
`include "flash_ctrl_rd_buff_evict_vseq.sv"
`include "flash_ctrl_phy_arb_vseq.sv"
`include "flash_ctrl_hw_sec_otp_vseq.sv"
+`include "flash_ctrl_erase_suspend_vseq.sv"
diff --git a/hw/ip/flash_ctrl/dv/flash_ctrl_sim_cfg.hjson b/hw/ip/flash_ctrl/dv/flash_ctrl_sim_cfg.hjson
index 05c95aa..d488edc 100644
--- a/hw/ip/flash_ctrl/dv/flash_ctrl_sim_cfg.hjson
+++ b/hw/ip/flash_ctrl/dv/flash_ctrl_sim_cfg.hjson
@@ -95,6 +95,11 @@
{
name: flash_ctrl_hw_sec_otp
uvm_test_seq: flash_ctrl_hw_sec_otp_vseq
+ }
+ {
+ name: flash_ctrl_erase_suspend
+ uvm_test_seq: flash_ctrl_erase_suspend_vseq
+ run_opts: ["+zero_delays=1"]
reseed: 5
}