[spi_device] Add payloadptr (last written) and overflow event

Add last written payloadptr and overflow event and synchronization logic
to SYS clock domain.

Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/ip/spi_device/rtl/spi_device.sv b/hw/ip/spi_device/rtl/spi_device.sv
index c4daeb2..af26cf6 100644
--- a/hw/ip/spi_device/rtl/spi_device.sv
+++ b/hw/ip/spi_device/rtl/spi_device.sv
@@ -163,6 +163,7 @@
   logic        addrfifo_notempty;
 
   logic payload_notempty;
+  logic payload_overflow;
 
   localparam int unsigned CmdFifoPtrW = $clog2(SramCmdFifoDepth+1);
   localparam int unsigned AddrFifoPtrW = $clog2(SramAddrFifoDepth+1);
@@ -173,9 +174,14 @@
   logic [CmdFifoPtrW-1:0]    cmdfifo_depth;
   logic [AddrFifoPtrW-1:0]   addrfifo_depth;
   logic [PayloadDepthW-1:0]  payload_depth;
+  logic [PayloadDepthW-1:0]  last_written_payload_idx;
 
   assign payload_notempty = payload_depth != '0;
 
+  // TODO: Implement CSR
+  logic unused_last_written_payload_idx;
+  assign unused_last_written_payload_idx = ^last_written_payload_idx;
+
   /////////////////////
   // Control signals //
   /////////////////////
@@ -311,6 +317,7 @@
 
   // Interrupts in Flash mode
   logic intr_upload_cmdfifo_not_empty, intr_upload_payload_not_empty;
+  logic intr_upload_payload_overflow;
   logic intr_readbuf_watermark, intr_readbuf_flip;
   logic flash_sck_readbuf_watermark, flash_sck_readbuf_flip;
 
@@ -583,16 +590,17 @@
   );
 
   prim_edge_detector #(
-    .Width (2),
+    .Width (3),
     .EnSync(1'b 0)
   ) u_intr_upload_edge (
     .clk_i,
     .rst_ni,
 
-    .d_i               ({cmdfifo_notempty, payload_notempty}),
+    .d_i               ({cmdfifo_notempty, payload_notempty, payload_overflow}),
     .q_sync_o          (),
     .q_posedge_pulse_o ({intr_upload_cmdfifo_not_empty,
-                         intr_upload_payload_not_empty}),
+                         intr_upload_payload_not_empty,
+                         intr_upload_payload_overflow}),
     .q_negedge_pulse_o ()
   );
 
@@ -622,6 +630,22 @@
     .intr_o                 (intr_upload_payload_not_empty_o              )
   );
 
+  logic unused_intr_payload_overflow;
+  assign unused_intr_payload_overflow = intr_upload_payload_overflow;
+  //prim_intr_hw #(.Width(1)) u_intr_payload_overflow (
+  //  .clk_i,
+  //  .rst_ni,
+  //  .event_intr_i           (intr_upload_payload_overflow                ),
+  //  .reg2hw_intr_enable_q_i (reg2hw.intr_enable.upload_payload_overflow.q),
+  //  .reg2hw_intr_test_q_i   (reg2hw.intr_test.upload_payload_overflow.q  ),
+  //  .reg2hw_intr_test_qe_i  (reg2hw.intr_test.upload_payload_overflow.qe ),
+  //  .reg2hw_intr_state_q_i  (reg2hw.intr_state.upload_payload_overflow.q ),
+  //  .hw2reg_intr_state_d_o  (hw2reg.intr_state.upload_payload_overflow.d ),
+  //  .hw2reg_intr_state_de_o (hw2reg.intr_state.upload_payload_overflow.de),
+  //  .intr_o                 (intr_upload_payload_overflow_o              )
+  //);
+
+
   prim_pulse_sync u_flash_readbuf_watermark_pulse_sync (
     .clk_src_i   (clk_spi_in_buf             ),
     .rst_src_ni  (rst_ni                     ),
@@ -1462,10 +1486,12 @@
     .sys_cmdfifo_full_o      (), // not used
     .sys_addrfifo_notempty_o (addrfifo_notempty),
     .sys_addrfifo_full_o     (), // not used
+    .sys_payload_overflow_o  (payload_overflow),
 
-    .sys_cmdfifo_depth_o  (cmdfifo_depth),
-    .sys_addrfifo_depth_o (addrfifo_depth),
-    .sys_payload_depth_o  (payload_depth)
+    .sys_cmdfifo_depth_o            (cmdfifo_depth),
+    .sys_addrfifo_depth_o           (addrfifo_depth),
+    .sys_payload_depth_o            (payload_depth),
+    .sys_last_written_payload_idx_o (last_written_payload_idx)
   );
   // FIFO connect
   assign cmdfifo_rready = reg2hw.upload_cmdfifo.re;
@@ -1924,6 +1950,8 @@
                 intr_upload_cmdfifo_not_empty_o)
   `ASSERT_KNOWN(IntrUploadPayloadNotEmptyOKnown,
                 intr_upload_payload_not_empty_o)
+  //`ASSERT_KNOWN(IntrUploadPayloadOverflowOKnown,
+  //              intr_upload_payload_overflow_o)
   `ASSERT_KNOWN(IntrReadbufWatermarkOKnown,  intr_readbuf_watermark_o)
   `ASSERT_KNOWN(IntrReadbufFlipOKnown,       intr_readbuf_flip_o)
   `ASSERT_KNOWN(IntrTpmHeaderNotEmptyOKnown, intr_tpm_header_not_empty_o)
diff --git a/hw/ip/spi_device/rtl/spid_upload.sv b/hw/ip/spi_device/rtl/spid_upload.sv
index 58ac87a..5fd1692 100644
--- a/hw/ip/spi_device/rtl/spid_upload.sv
+++ b/hw/ip/spi_device/rtl/spid_upload.sv
@@ -107,10 +107,12 @@
   output logic sys_cmdfifo_full_o,
   output logic sys_addrfifo_notempty_o,
   output logic sys_addrfifo_full_o,
+  output logic sys_payload_overflow_o,
 
   output logic [CmdPtrW-1:0]     sys_cmdfifo_depth_o,
   output logic [AddrPtrW-1:0]    sys_addrfifo_depth_o,
-  output logic [PayloadPtrW-1:0] sys_payload_depth_o
+  output logic [PayloadPtrW-1:0] sys_payload_depth_o,
+  output logic [PayloadPtrW-1:0] sys_last_written_payload_idx_o
 );
 
   localparam int unsigned CmdFifoWidth  =  8;
@@ -290,6 +292,58 @@
     .dst_pulse_o (sys_payloadptr_clr_posedge)
   );
 
+  // last_written_payloadptr: in contrast to payloadptr,
+  // `last_written_payloadptr` provides a location that the HW lastly wrote to
+  // the payload buffer.
+  //
+  // This is useful when the host system sends more than 256B of payload. The
+  // HW wraps around the payload buffer when it receives more than 256B. The
+  // SW, with last_written_payloadptr, is able to know the exact offset to
+  // start to read.
+  //
+  // TODO: Handle 257, ... , 511 case (reduce bit by 1?)
+  logic [PayloadPtrW-1:0] last_written_payloadptr;
+  always_ff @(posedge clk_i or negedge sys_rst_ni) begin
+    if (!sys_rst_ni) last_written_payloadptr <= '0;
+    else if (payloadptr_clr) last_written_payloadptr <= '0;
+    else if (payloadptr_inc) begin
+      last_written_payloadptr <= last_written_payloadptr + PayloadPtrW'(1);
+    end
+  end
+
+  // Latch last_written_payloadptr @ CSb events
+  always_ff @(posedge sys_clk_i or negedge sys_rst_ni) begin
+    if (!sys_rst_ni) sys_last_written_payload_idx_o <= '0;
+    else if (sys_payloadptr_clr_posedge) sys_last_written_payload_idx_o <= '0;
+    else if (sys_csb_deasserted_pulse_i) begin
+      sys_last_written_payload_idx_o <= last_written_payloadptr;
+    end
+  end
+
+  // Overflow event
+  // When the SPI host system issues more than 256B payload, HW stores the
+  // overflow event in SCK then notify to SW when CSb is deasserted
+  logic  event_payload_overflow;
+  always_ff @(posedge clk_i or negedge sys_rst_ni) begin
+    if (!sys_rst_ni)         event_payload_overflow <= 1'b 0;
+    else if (payloadptr_clr) event_payload_overflow <= 1'b 0;
+    else if (payloadptr_inc && (payloadptr == PayloadPtrW'(PayloadByte))) begin
+      event_payload_overflow <= 1'b 1;
+    end
+  end
+
+  // Sync to SYSCLK when CSb release. Edge detection on the spi_device top
+  logic sys_event_payload_overflow;
+  always_ff @(posedge sys_clk_i or negedge sys_rst_ni) begin
+    if (!sys_rst_ni)                     sys_event_payload_overflow <= 1'b 0;
+    else if (sys_payloadptr_clr_posedge) sys_event_payload_overflow <= 1'b 0;
+    else if (sys_csb_deasserted_pulse_i) begin
+      sys_event_payload_overflow <= event_payload_overflow;
+    end
+  end
+
+  assign sys_payload_overflow_o = sys_event_payload_overflow;
+
   always_ff @(posedge clk_i or negedge rst_ni) begin
     if (!rst_ni) begin
       st_q <= StIdle;