blob: 9d86dc1bf19c9b8fe9f71912f3d1b556e9e33de9 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// SPI Flash Read Command: Read Buffer Manager
/*
The SPI Device IP uses the first half of the DPSRAM as a read buffer. The IP
returns data from the read buffer based on the given address in the received
read command. The read buffer size is 2kB in current design. The IP only uses
lower 11 bits ($clog2(2048)) of the received read command address and uses the
address to issue the read request to the DPSRAM.
It is the SW responsibility to update the read buffer contents. Ths module
provides a SW configurable read watermark CSR and read only
`LAST_READ_ADDRESS` CSR. SW may use those registers to get the time to update
the buffer contents.
The module receives the current sent address and the threshold to determine
the buffer flip and the watermark event. Buffer flip event occurs when the
host system accesses the other side of the buffer. For example, in current
total buffer size 2kB. If the host accesses 0xFFC then 0x1000. The buffer flip
occurs (from Buffer 1 to Buffer 0). In contrast, if the host system crosses
the buffer boundary in opposite direction(0x1000 --> 0xFFC), it is not assumed
as a buffer flip event.
The watermark event happens when the host system accesses the address greater
than the watermark CSR for the first time in a buffer. Let's assume the
threshold (watermark) CSR value is 0x200 (512B). If the host system issues
a SPI read command at the address 0x1FC and read 16B data, the IP keeps
checking the sent address (from 0x1FC then 0x1FD, 0x1FE, 0x1FF) and when it
sends the 0x200 data, it raises a watermark event.
The watermark event is sticky event. It is reported to the SW once and cleared
by the flip event. So even the host system keeps reading 0x1FC and 0x200
multiple times, the event won't be notified to the SW after the first report.
*/
`include "prim_assert.sv"
module spid_readbuffer
import spi_device_pkg::SramBufferAw, spi_device_pkg::FlashMode;
(
input clk_i,
input rst_ni,
input sys_rst_ni, // to keep the addr, bufidx, flip signals
input spi_device_pkg::spi_mode_e spi_mode_i,
input [31:0] current_address_i,
input [SramBufferAw-1:0] threshold_i,
input sfdp_hit_i,
input mailbox_hit_i,
input mailbox_en_i,
// start: data Output phase indicator. Either pulse or level are fine.
input start_i,
// The Buffer logic updates its status only at the last beat of the byte.
// The time is when the address is updated.
// It is expected that the address is updated at the end of beat of a byte.
input address_update_i,
output logic event_watermark_o,
output logic event_flip_o
);
////////////////
// Definition //
////////////////
typedef enum logic {
StIdle,
StActive
} st_e;
st_e st_q, st_d;
////////////
// Signal //
////////////
logic watermark_cross;
logic watermark_crossed; // set by event / clear by flip
logic flip;
// The logic keeps next buffer address. Compare this with the
// current_address and if it hits with mask, then the flip event occurs.
//
// ICEBOX(#10038): If the device goes sleep, the next_buffer_addr should be
// recoverable.
logic [31-SramBufferAw:0] next_buffer_addr;
logic active;
//////////////
// Datapath //
//////////////
// Flip event handling
always_ff @(posedge clk_i or negedge sys_rst_ni) begin
if (!sys_rst_ni) begin
next_buffer_addr <= (32-SramBufferAw)'(1); // pointing to next buffer
end else if (active && flip) begin
next_buffer_addr <= next_buffer_addr + 1'b 1;
end
end
logic [31-SramBufferAw:0] current_buffer_idx;
assign current_buffer_idx = current_address_i[31:SramBufferAw];
assign flip = current_buffer_idx == next_buffer_addr;
// make flip event single cycle pulse signal
// It will be synchronized into the bus clock domain using prim_pulse_sync
logic flip_q;
always_ff @(posedge clk_i or negedge sys_rst_ni) begin
if (!sys_rst_ni) flip_q <= 1'b 0;
else if (active) flip_q <= flip;
end
assign event_flip_o = active && flip && !flip_q;
// ICEBOX(#10037): Consider the case if host jumps the address? (report
// error?)
// ICEBOX(#10038): Consider the case of sleep and recover. Provide a way to
// recover `next_buffer_addr`?
// Watermark event: Threshold should not be 0 to enable the event
assign watermark_cross = (current_address_i[SramBufferAw-1:0] >= threshold_i)
&& |threshold_i;
always_ff @(posedge clk_i or negedge sys_rst_ni) begin
if (!sys_rst_ni) begin
watermark_crossed <= 1'b 0;
end else if (active && watermark_cross) begin
// When `watermark_cross` and `flip` both are 1, the watermark_crossed
// should remain 1. The event will be reported in this case.
watermark_crossed <= 1'b 1;
end else if (active && flip) begin
watermark_crossed <= 1'b 0;
end
end
always_comb begin : watermark_event_logic
event_watermark_o = 1'b 0;
if (active && watermark_cross) begin
if (!watermark_crossed) begin
event_watermark_o = 1'b 1;
end else if (flip) begin
// if flip is set and previous watermark_crossed is set, the event
// should be reported also. This means the host issues the next buffer
// address pointing above the threshold. This scenario, flip event and
// watermark event both should be reported.
event_watermark_o = 1'b 1;
end
end
end : watermark_event_logic
///////////////////
// State Machine //
///////////////////
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) st_q <= StIdle;
else if (spi_mode_i != spi_device_pkg::FlashMode) begin
st_q <= StIdle;
end else begin
st_q <= st_d;
end
end
always_comb begin
st_d = st_q;
active = 1'b 0;
unique case (st_q)
StIdle: begin
if (start_i && (spi_mode_i == spi_device_pkg::FlashMode)
&& !sfdp_hit_i && !(mailbox_en_i && mailbox_hit_i)) begin
st_d = StActive;
active = 1'b 1; // Assume address_update_i is high
end
end
StActive: begin
// Deadend waiting CSb de-assertion
st_d = StActive;
active = address_update_i;
end
default: begin
st_d = StIdle;
end
endcase
end
`ASSERT(StartWithAddressUpdate_A, start_i |-> address_update_i)
endmodule : spid_readbuffer