[spi_device/dv] Align last_read_addr behavior with design
Updated scb to align the behavior. Now last_read_addr shows last address,
rather than the next address
Moved interrupt clear before updating mem, to avoid a new interrupt comes before
it's cleared
Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/ip/spi_device/dv/env/seq_lib/spi_device_pass_base_vseq.sv b/hw/ip/spi_device/dv/env/seq_lib/spi_device_pass_base_vseq.sv
index da188a6..1c6c5a9 100644
--- a/hw/ip/spi_device/dv/env/seq_lib/spi_device_pass_base_vseq.sv
+++ b/hw/ip/spi_device/dv/env/seq_lib/spi_device_pass_base_vseq.sv
@@ -108,7 +108,7 @@
read_addr_size_type inside {ReadAddrWithinMailbox, ReadAddrOutsideMailbox};
// this is read buffer
if (read_addr_size_type == ReadAddrOutsideMailbox && opcode inside {READ_CMD_LIST}) {
- read_start_addr == cfg.read_buffer_addr;
+ read_start_addr == cfg.next_read_buffer_addr;
}
}
}
@@ -517,17 +517,20 @@
end
if (cfg.is_read_buffer_cmd(m_spi_host_seq.rsp)) begin
- cfg.read_buffer_addr = convert_addr_from_byte_queue(m_spi_host_seq.rsp.address_q) +
+ cfg.next_read_buffer_addr = convert_addr_from_byte_queue(m_spi_host_seq.rsp.address_q) +
m_spi_host_seq.rsp.payload_q.size();
- cfg.clk_rst_vif.wait_clks(10);
- `uvm_info(`gfn, $sformatf("Updated read_buffer_addr to 0x%0x", cfg.read_buffer_addr),
+ `uvm_info(`gfn, $sformatf("Updated next_read_buffer_addr: 0x%0x", cfg.next_read_buffer_addr),
UVM_MEDIUM)
end
- // TODO, only read last_read_addr in this mode due to #14586
- if (device_mode == FlashMode && $urandom_range(0, 1)) begin
- csr_rd_check(.ptr(ral.last_read_addr), .compare_value(cfg.read_buffer_addr));
+ // randomly read last_read_addr for scb to check
+ if ($urandom_range(0, 2) == 0) begin
+ bit [TL_DW-1:0] rdata;
+
+ // This is synced from the other clock domain. It takes around 3-4 cycles.
+ cfg.clk_rst_vif.wait_clks(5);
+ csr_rd(.ptr(ral.last_read_addr), .value(rdata));
end
if (wait_on_busy) begin
@@ -680,6 +683,15 @@
is_flip = get_field_val(ral.intr_state.readbuf_flip, intr_state_val);
is_watermark = get_field_val(ral.intr_state.readbuf_watermark, intr_state_val);
+ // clear flip and watermark event before updating mem
+ // if clear too late, scb is hard to handle as the interrupt may happen again
+ intr_state_val = 0;
+ if (is_flip) intr_state_val[ReadbufFlip] = 1;
+ if (is_watermark) intr_state_val[ReadbufWatermark] = 1;
+
+ if (!is_flip && !is_watermark) continue;
+ csr_wr(ral.intr_state, intr_state_val);
+
if (is_flip) begin
start_addr = cfg.read_buffer_ptr % READ_BUFFER_SIZE;
end_addr = (start_addr + READ_BUFFER_HALF_SIZE);
@@ -689,12 +701,6 @@
random_write_spi_mem(start_addr, end_addr - 1, "read buffer", zero_delay_write);
cfg.read_buffer_ptr = end_addr;
end
-
- // clear flip and watermark event
- intr_state_val = 0;
- if (is_flip) intr_state_val[ReadbufFlip] = 1;
- if (is_watermark) intr_state_val[ReadbufWatermark] = 1;
- csr_wr(ral.intr_state, intr_state_val);
end
read_buffer_update_ongoing = 0;
end
diff --git a/hw/ip/spi_device/dv/env/seq_lib/spi_device_read_buffer_direct_vseq.sv b/hw/ip/spi_device/dv/env/seq_lib/spi_device_read_buffer_direct_vseq.sv
index 33cde88..5eb9773 100644
--- a/hw/ip/spi_device/dv/env/seq_lib/spi_device_read_buffer_direct_vseq.sv
+++ b/hw/ip/spi_device/dv/env/seq_lib/spi_device_read_buffer_direct_vseq.sv
@@ -62,6 +62,7 @@
`uvm_info(`gfn, "reading 1 more byte at read_threshold_val - 1", UVM_MEDIUM)
send_read_cmd(.start_addr(start_addr), .payload_size(1));
check_interrupts(.interrupts((1 << ReadbufWatermark)), .check_set(1));
+ check_interrupts(.interrupts((1 << ReadbufFlip)), .check_set(0));
start_addr += 1;
payload_size = READ_BUFFER_SIZE / 2 - 1;
@@ -72,10 +73,10 @@
check_interrupts(.interrupts((1 << ReadbufFlip)), .check_set(1));
start_addr += payload_size;
- start_addr += 1;
`uvm_info(`gfn, "reading 1 more byte at read_threshold_val - 1 in 2nd half", UVM_MEDIUM)
send_read_cmd(.start_addr(start_addr), .payload_size(1));
check_interrupts(.interrupts((1 << ReadbufWatermark)), .check_set(1));
+ check_interrupts(.interrupts((1 << ReadbufFlip)), .check_set(0));
endtask
task send_read_cmd(bit[31:0] start_addr, int payload_size);
@@ -87,6 +88,6 @@
spi_host_xfer_flash_item(opcode, payload_size, start_addr);
cfg.clk_rst_vif.wait_clks(10);
- csr_rd_check(.ptr(ral.last_read_addr), .compare_value(start_addr + payload_size));
+ csr_rd_check(.ptr(ral.last_read_addr), .compare_value(start_addr + payload_size - 1));
endtask
endclass : spi_device_read_buffer_direct_vseq
diff --git a/hw/ip/spi_device/dv/env/spi_device_env_cfg.sv b/hw/ip/spi_device/dv/env/spi_device_env_cfg.sv
index 63566f7..813dc87 100644
--- a/hw/ip/spi_device/dv/env/spi_device_env_cfg.sv
+++ b/hw/ip/spi_device/dv/env/spi_device_env_cfg.sv
@@ -9,7 +9,8 @@
bit [TL_AW-1:0] sram_end_addr;
// read buffer needs to be read with incremental address, otherwise, watermark/fip won't work
- bit [TL_AW-1:0] read_buffer_addr;
+ // this needs to be kept across sequences and cleared when reset occurs. Only seq uses it.
+ bit [TL_AW-1:0] next_read_buffer_addr;
// read_buffer_addr is updated after a transaction is done
// this ptr is updated when a flip event occurs, so that we could know which part is
// read by the SPI host, in order to update the other part
diff --git a/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv b/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv
index d37b52a..f9340cd 100644
--- a/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv
+++ b/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv
@@ -50,7 +50,8 @@
bit [31:0] upload_addr_from_tl_q[$];
// for read buffer
- bit [31:0] read_buffer_addr;
+ // once triggered, it won't be triggered again until it flips
+ bit read_buffer_watermark_triggered;
flash_status_t flash_status_q[$];
flash_status_t flash_status_settle_q[$];
@@ -372,7 +373,8 @@
// this doesn't handle read cmd falling in read buffer
virtual function void check_read_cmd_data_for_non_read_buffer(spi_item up_item,
spi_item dn_item);
- bit [31:0] start_addr, end_addr;
+ bit [31:0] start_addr;
+ bit [TL_DW-1:0] new_last_read_addr = `gmv(ral.last_read_addr);
// TODO, sample this for coverage
read_addr_size_type_e read_addr_size_type;
bit is_passthru = `gmv(ral.control.mode) == PassthroughMode;
@@ -393,6 +395,9 @@
end else begin // out of mbx region
string str;
+ // if last addr is not in mailbox, will be captured in last_read_addr CSR.
+ new_last_read_addr = cur_addr;
+
`DV_CHECK_EQ_FATAL(is_passthru, 1)
if (dn_item != null) begin
str = $sformatf("compare mbx data with downstread item. idx %0d, up: 0x%0x, dn: 0x%0x",
@@ -408,6 +413,10 @@
end
end
+ if (new_last_read_addr != `gmv(ral.last_read_addr)) begin
+ `uvm_info(`gfn, $sformatf("Update last_read_addr to 0x%0x", new_last_read_addr), UVM_MEDIUM)
+ void'(ral.last_read_addr.predict(.value(new_last_read_addr), .kind(UVM_PREDICT_READ)));
+ end
// TODO, sample read_addr_size_type for coverage
endfunction
@@ -540,7 +549,7 @@
forever begin
spi_item item;
uint payload_idx;
- bit [31:0] start_addr, offset;
+ bit [31:0] start_addr, offset, read_buffer_addr;
upstream_spi_req_fifo.get(item);
if (!cfg.is_read_buffer_cmd(item)) continue;
@@ -553,30 +562,35 @@
offset = (start_addr + payload_idx) % READ_BUFFER_SIZE;
compare_mem_byte(READ_BUFFER_START_ADDR, offset, item.payload_q[payload_idx],
payload_idx, "Read buffer");
+ read_buffer_addr = start_addr + payload_idx; // it's kept until reset
payload_idx++; // clear to 0 when transaction is done
- read_buffer_addr++; // it's kept until reset
- predict_read_buffer_intr(read_buffer_addr);
+ predict_read_buffer_intr(read_buffer_addr + 1);
`DV_CHECK_EQ(item.payload_q.size, payload_idx)
end
if (item.mon_item_complete) break;
end
)
-
- `uvm_info(`gfn, $sformatf("Update last_read_addr to 0x%0x", read_buffer_addr), UVM_MEDIUM)
- void'(ral.last_read_addr.predict(.value(read_buffer_addr), .kind(UVM_PREDICT_READ)));
+ // only update when it has payload
+ if (payload_idx > 0) begin
+ `uvm_info(`gfn, $sformatf("Update last_read_addr to 0x%0x", read_buffer_addr), UVM_MEDIUM)
+ void'(ral.last_read_addr.predict(.value(read_buffer_addr), .kind(UVM_PREDICT_READ)));
+ end
end
endtask
virtual function void predict_read_buffer_intr(int addr);
int threshold = `gmv(ral.read_threshold);
-
int offset = addr % READ_BUFFER_HALF_SIZE;
- if (offset == threshold && threshold > 0) begin
+ if (offset >= threshold && threshold > 0 && !read_buffer_watermark_triggered) begin
+ read_buffer_watermark_triggered = 1;
intr_trigger_pending[ReadbufWatermark] = 1;
`uvm_info(`gfn, $sformatf("read buffer watermark is triggered, addr: 0x%0x", addr),
UVM_MEDIUM)
- end else if (addr % READ_BUFFER_HALF_SIZE == 0) begin
+ end
+ if (addr % READ_BUFFER_HALF_SIZE == 0) begin
+ // after flip, WM can be triggered again
+ read_buffer_watermark_triggered = 0;
intr_trigger_pending[ReadbufFlip] = 1;
`uvm_info(`gfn, $sformatf("read buffer flip is triggered, addr: 0x%0x", addr),
UVM_MEDIUM)
@@ -690,6 +704,15 @@
// skip updating predict value to d_data
return;
end // if (!write && channel == DataChannel)
+ else if (write && channel == AddrChannel) begin
+ bit [NumSpiDevIntr-1:0] intr_val = item.a_data;
+ foreach (intr_val[i]) begin
+ spi_device_intr_e intr = i;
+ if (intr_val[i]) begin
+ `uvm_info(`gfn, $sformatf("Clear %s", intr.name), UVM_MEDIUM)
+ end
+ end
+ end
end
"intr_test": begin
if (write && channel == AddrChannel) begin
@@ -875,10 +898,10 @@
flash_status_tl_pre_val_q.delete();
// used in seq
- cfg.read_buffer_addr = 0;
+ cfg.next_read_buffer_addr = 0;
cfg.read_buffer_ptr = 0;
- read_buffer_addr = 0;
+ read_buffer_watermark_triggered = 0;
endfunction
function void check_phase(uvm_phase phase);