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