blob: f913d0aa3554e62fc9b02d17e7482eb84f18d644 [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 Commands Processing block
//
// This module processes read commands and Mailbox address region.
//
/*
Details:
# Process
Command Parser sends a command in the 8th bit. The datapath is selected at
the same cycle.
Read command process block uses the cmd_info data and determine how the
module should operate. The cmd_info has enough information the module to
operate such as the address mode (4B affected or not), the existence of the
dummy cycles, the number of dummy cycles, the number of output lanes the
command uses, and the number of address lines. Please refer
spi_device_pkg::cmd_info_t for the details of the data structure.
## Address handling
### Address for Output commands
As address comes through IO0 only, the state machine shifts the address
one-by-one and decrement the address counter register by 1.
When it reaches to the 4B address (addr[2]), it triggers DPSRAM state machine
to fetch data from DPSRAM. And when it receives addr[0], at posedge the state
machine moves to appropriate command state.
### Address for I/O commands
Address comes through IO0,1 for the dual I/O command, or IO0:3 for the quad
I/O command. The logic latches 2 or 4bit at a time. When it reaches valid
4B address, it triggers the Dual-Port SRAM(DPSRAM) state machine to fetch
data from the DPSRAM. Then, it moves to M byte state. The value is not used
in current design.
### Mailbox when the mode is enabled
If the address falls into mailbox address range while stacking address into
a register, the state turns on mailbox selection bit. Then all out-going
requests to DPSRAM for read point to the Mailbox section not to the Read
Buffer section.
## Normal command
Normal command sends the read data right after the address phase. Right
after moved to the Normal command state, the state machine pushes proper
byte into synchronous FIFO. The FIFO has the pass parameter enabled to get
the data within a cycle.
## Fast Read command
Moving into Fast Read command state is identical to the Normal read command
state. The address state sends read request when it reaches 4B granularity.
Then when the state moves to this Fast Read command state, the machine waits
preconfigured dummy cycle. The default dummy cycle is 8. Then it follows same
step as Normal read command state.
## Dual/ Quad Output command
The state is similar to the Fast Read command. The dummy cycle for Dual/ Quad
are 4 and 2 by default. But they can be configured by the software. The states
send DPSRAM read request at every 2nd cycle in a byte for Dual command, 1st
cycle in a byte (two beats) for Quad command.
## Dual/ Quad I/O command
I/O commands have M byte prior to the dummy cycles. Read command module does
not support continuous read feature that is described in M byte. So, Dual &
Quad I/O commands state waits M byte then moves to Dual/ Quad output command
states.
## Read SFDP command
If the incoming datapath is not DpReadCmd but DpSfdp, then the request to
the DPSRAM goes to the SFDP data section. The `cmd_info` should have the
expected config for the SFDP command (e.g: 4B affected disabled,
non-existence of the dummy cycle).
*/
`include "prim_assert.sv"
module spi_readcmd
import spi_device_pkg::*;
#(
// Read command configurations
// Base address: Index of DPSRAM
// Buffer size: # of indices assigned to Read Buffer.
// This is used to calculate double buffering and threshold.
parameter sram_addr_t ReadBaseAddr = spi_device_pkg::SramReadBufferIdx,
parameter int unsigned ReadBufferDepth = spi_device_pkg::SramMsgDepth,
// Mailbox Base Addr: Beginning Index of Mailbox in DPSRAM
// Mailbox Size: Mailbox buffer size (# of indices)
parameter sram_addr_t MailboxBaseAddr = spi_device_pkg::SramMailboxIdx,
parameter int unsigned MailboxDepth = spi_device_pkg::SramMailboxDepth,
// SFDP Base Addr: the beginning index of the SFDP region in DPSRAM
// SFDP Depth: The size of the SFDP buffer (64 fixed in the spi_device_pkg)
parameter sram_addr_t SfdpBaseAddr = spi_device_pkg::SramSfdpIdx,
parameter int unsigned SfdpDepth = spi_device_pkg::SramSfdpDepth
) (
input clk_i,
input rst_ni,
input clk_out_i, // Output clock (inverted SPI_CLK)
input sys_rst_ni, // System reset for buffer tracking pointers
// From Command Parser
// DpReadCmd activates readcmd module, and it sees opcode to determine its
// mode inside (Normal/ Fast/ Dual/ Quad/ DualIO/ QuadIO)
input sel_datapath_e sel_dp_i,
// SRAM access
output sram_l2m_t sram_l2m_o,
input sram_m2l_t sram_m2l_i,
// Interface: SPI to Parallel
input s2p_valid_i,
input spi_byte_t s2p_byte_i,
// Interface: Parallel to SPI
// Should be latched to clk_out_i
output logic p2s_valid_o,
output spi_byte_t p2s_byte_o,
input logic p2s_sent_i,
// Configurations:
// All configs come from peripheral clock domain.
// No CDC exists in between.
input spi_mode_e spi_mode_i,
input cmd_info_t cmd_info_i,
input logic [CmdInfoIdxW-1:0] cmd_info_idx_i,
// Double buffering in bytes
input [SramBufferAw-1:0] readbuf_threshold_i,
// The command mode is 4B mode. Every read command receives 4B address
input addr_4b_en_i,
// Features
input mailbox_en_i,
input cfg_intercept_en_mbx_i, // Intercept
// Mailbox Address base address
// Only allow compile-time fixed size (1kB by default)
input [31:0] mailbox_addr_i,
//input [31:0] mailbox_mask_i,
// Indicator to take SPI line in Passthrough mode
output logic mailbox_assumed_o,
// Read buf addr in SCK domain. It is synchronized to bus clock in the top
// module
output logic [31:0] readbuf_address_o,
output io_mode_e io_mode_o,
// Read watermark event occurs when current address exceeds the threshold cfg
// value for the first time after hit the current read buffer (1kB
// granularity).
//
// These signals are pulse signals.
output logic sck_read_watermark_o,
output logic sck_read_flip_o
);
logic unused_p2s_sent ;
assign unused_p2s_sent = p2s_sent_i;
sram_err_t unused_sram_rerr;
assign unused_sram_rerr = sram_m2l_i.rerror;
logic unused_cmd_info_idx;
assign unused_cmd_info_idx = ^cmd_info_idx_i;
logic unused_cmd_info_members;
assign unused_cmd_info_members = ^{
cmd_info_i.valid, // cmdparse checks the valid bit
cmd_info_i.addr_swap_en, // address swap feature is used in Passthrough
cmd_info_i.opcode, // Does not need to check opcode. (fixed slot)
cmd_info_i.payload_dir, // Always output mode
cmd_info_i.payload_swap_en, // Used in passthrough mode only
cmd_info_i.upload,
cmd_info_i.busy
};
`ASSERT(ValidCmdConfig_A,
main_st == MainAddress |-> (cmd_info_i.addr_mode != AddrDisabled)
&& cmd_info_i.payload_dir == PayloadOut
&& cmd_info_i.valid)
/////////////////
// Definitions //
/////////////////
// The expected FSM sequence for each command:
//
// - Read Data: Reset -> Address -> Output
// - Fast Read (Single/ Dual/ Quad): Reset -> Address -> Dummy -> Output
// - Fast Read Dual IO/ Quad IO: Reset -> Address -> MByte -> Dummy -> Output
// - Read SFDP: Reset -> Address -> Output
typedef enum logic [3:0] {
// Reset: Wait until the datapath for Read command is activated by cmdparse
// The cmd_info is valid after the 8th posedge of SCK.
MainReset,
// Address
//
// In this state, the FSM stacks incoming byte from s2p up to 3B or 4B
// based on the config by SW. When FSM moves from Reset to this state,
// it sets the IO mode to Single/ Dual/ Quad depending on the incoming
// SPI command.
//
// In this state, when it stacks addr[2] bit, which is 3rd from last in
// Single IO, 2nd from the last in Dual IO, and the last in Quad IO, it
// triggers Fetch State Machine to fetch data from Dual Port SRAM in
// SPI_DEVICE.
//
// For I/O commands, the state moves to MByte state.
// For Fast commands, the state moves to Dummy state.
// For Normal Read command, the state moves to Output state directly.
MainAddress,
// MByte
//
// IO commands (Dual I/O, Quad I/O) has M byte following Address. It is
// normally used for "continuous read" operation. This logic does not
// support the feature, so ignoring it then moves to Dummy state.
MainMByte,
// Dummy: Wait until it reaches dummy cycles, then moves to Output state
MainDummy,
// Output
//
// After passing the dummy cycle or skipping it for Normal Read command,
// The state drives SPI interface to send data. This state always assumes
// the data is ready in the sync fifo (see instance below). The Fetch
// Machine always prepares the data in advance up to designated level.
// The prefetch does not affect the watermark that SW has configured. The
// event occurs only when the byte is indeed sent through SPI lines.
//
// This state machine watches the sent address and raises an event if it
// exceeds the level. This feature is not turned on for Mailbox address.
MainOutput,
// Error: Wait until CSb deasserted
MainError
} main_st_e;
main_st_e main_st, main_st_d;
/////////////
// Signals //
/////////////
// Address mode in cmd_info
addr_mode_e cmdinfo_addr_mode;
// Address shift & latch
logic addr_ready_in_word, addr_ready_in_halfword;
logic addr_latched;
logic addr_shift_en;
logic addr_latch_en;
logic addr_inc; // increase address by 1 byte. Used to track the current addr
// Address size is latched when the state machine moves to the MainAddress
// state based on the cmd_info.addr_mode and addr_4b_en_i
logic [4:0] addr_cnt_d, addr_cnt_q;
logic addr_cnt_set; // no need to clear the counter
logic addr_latched_d;
logic [31:0] addr_q, addr_d;
// Read buffer access address.
//
// This differs from addr_q. addr_q is to maintain the SRAM access.
// readbuf_addr is to track the Read command address which does not fall
// into SFDP, Mailbox.
logic [31:0] readbuf_addr;
// Read Buffer update: addr_latch_en at the last addr beat & addr_inc
logic readbuf_update;
// When FSM is about to move to Output state, FSM triggers readbuf to operate.
logic readbuf_start;
// Dummy counter
logic dummycnt_eq_zero;
logic load_dummycnt;
logic [2:0] dummycnt;
// IO Mode selection
// SRAM signals
// Compare addr_d[SramAw-1:2] and mailbox_addr_i with mailbox_mask_i. If the
// value falls into mailbox, set this. Even this is set, it only uses when
// sram address is sent.
logic addr_d_in_mailbox, addr_q_in_mailbox;
logic [31:0] mailbox_masked_addr_d, mailbox_masked_addr_q;
// Double buffering signals
logic readbuf_idx; // 0 or 1
logic readbuf_flip; // if this is asserted, readbuf_idx flips.
// bit count within a word
logic bitcnt_update;
logic bitcnt_dec;
logic [2:0] bitcnt; // count down from 7 or partial for first unaligned
// FIFO
logic unused_fifo_rvalid, fifo_pop;
spi_byte_t fifo_rdata;
logic [7:0] p2s_byte;
logic p2s_valid_inclk;
logic sfdp_hit;
assign sfdp_hit = sel_dp_i == DpReadSFDP;
// Events: watermark, flip
logic read_watermark, read_flip;
// SPI Mode
logic spid_in_flashmode;
assign spid_in_flashmode = (spi_mode_i == FlashMode);
//////////////
// Datapath //
//////////////
// Address Shifting.
// the data comes from S2P module, which sends valid signal with spi_byte_t.
// So, if the logic sees `valid` only, it can't latch the exact word address.
// This logic latches in byte based until the 2nd last byte. Then merging
// the incoming byte (premature) to organize 4B address.
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
addr_q <= '0;
end else if (addr_latch_en) begin
addr_q <= addr_d;
end
end
// readbuf_addr is visible to SW after CSb is de-asserted. (last_read_addr)
//
// It indicates the last byte address the host read.
// To precisely represent the last byte:
//
// - the logic does not latch address field (not yet read the byte)
// - the logic latches `addr_q` at the last beat. But compare `addr_q` to
// mailbox address.
always_ff @(posedge clk_i or negedge sys_rst_ni) begin
if (!sys_rst_ni) begin
readbuf_addr <= '0;
end else if ((main_st == MainOutput) && (sel_dp_i == DpReadCmd)
&& addr_latch_en && !(mailbox_en_i && addr_q_in_mailbox)
&& spid_in_flashmode) begin
readbuf_addr <= addr_q;
end
end
assign readbuf_address_o = readbuf_addr;
always_comb begin
addr_d = addr_q; // default value. In 3B mode, upper most byte is 0
addr_latch_en = 1'b0;
// TODO: Handle the case of IO command
if (addr_ready_in_word) begin
// Return word based address, but should not latch
addr_d = {addr_q[23:0], s2p_byte_i[5:0], 2'b00};
end else if (addr_ready_in_halfword) begin
// When addr is a cycle earlier than full addr, sram req sent in
// spid_readsram
addr_d = {addr_q[23:0], s2p_byte_i[6:0], 1'b 0};
end else if (addr_shift_en && s2p_valid_i) begin
// Latch
addr_d = {addr_q[23:0], s2p_byte_i[7:0]};
addr_latch_en = 1'b 1;
end else if (addr_inc) begin
// Increase the address to next
addr_d = addr_q[31:0] + 1'b1;
addr_latch_en = 1'b 1;
end
end
// BEGIN: Address Count =====================================================
// addr_cnt is to track the current shifted bit position in the address field
assign addr_ready_in_word = (addr_cnt_d == 5'd 2);
assign addr_ready_in_halfword = (addr_cnt_d == 5'd 1);
// addr_latched should be a pulse to be used in spid_readsram
assign addr_latched_d = (addr_cnt_d == 5'd 0);
prim_edge_detector #(
.Width (1),
.ResetValue (1'b 0),
.EnSync (1'b 0)
) u_addr_latch_pulse (
.clk_i,
.rst_ni,
.d_i (addr_latched_d),
.q_sync_o ( ),
.q_posedge_pulse_o (addr_latched ),
.q_negedge_pulse_o ( )
);
assign cmdinfo_addr_mode = get_addr_mode(cmd_info_i, addr_4b_en_i);
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
addr_cnt_q <= '0;
end else begin
addr_cnt_q <= addr_cnt_d;
end
end
always_comb begin : addr_cnt
addr_cnt_d = addr_cnt_q;
if (addr_cnt_set) begin
// Set to the addr size based on cmd_info_i
// If addr_mode && addr_4b_en is Addr4B, then 32, if not, 24
// addr_cnt_d starts from the max -1. As addr_cnt_set is asserted when
// FSM moves from Reset to Address. At that time of the transition, the
// datapath should latch the Address[31] or Address[23] too. So, it
// already counts one beat.
addr_cnt_d = (cmdinfo_addr_mode == Addr4B) ? 5'd 31 : 5'd 23;
// TODO: Dual IO/ Quad IO case
end else if (addr_cnt_q == '0) begin
addr_cnt_d = addr_cnt_q;
end else if (addr_shift_en) begin
// Stacking the address, decrease the address counter
addr_cnt_d = addr_cnt_q - 1'b 1;
end
end
// END: Address Count -----------------------------------------------------
// Dummy Counter
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
dummycnt <= '0;
end else if (load_dummycnt) begin
// load quad_io
dummycnt <= cmd_info_i.dummy_size;
end else if (!dummycnt_eq_zero) begin
dummycnt <= dummycnt - 1'b 1;
end
end
assign dummycnt_eq_zero = ~|dummycnt;
// FIFO bit count
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
bitcnt <= '0;
end else if (bitcnt_update) begin
unique case (cmd_info_i.payload_en)
4'b 0010: bitcnt <= 3'h 7;
4'b 0011: bitcnt <= 3'h 6;
4'b 1111: bitcnt <= 3'h 4;
default: bitcnt <= 3'h 7;
endcase
end else if (bitcnt_dec) begin
unique case (cmd_info_i.payload_en)
4'b 0010: bitcnt <= bitcnt - 3'h 1;
4'b 0011: bitcnt <= bitcnt - 3'h 2;
4'b 1111: bitcnt <= bitcnt - 3'h 4;
default: bitcnt <= bitcnt - 3'h 1;
endcase
end
end
//= BEGIN: SRAM Datapath ====================================================
// Main FSM trigger this datapath. This logic calculates the correct address
// Address conversion
// Convert into masked address
localparam int unsigned MailboxAw = $clog2(MailboxDepth);
localparam logic [31:0] MailboxMask = {{30-MailboxAw{1'b1}}, {2+MailboxAw{1'b0}}};
assign mailbox_masked_addr_d = addr_d & MailboxMask;
assign mailbox_masked_addr_q = addr_q & MailboxMask;
// Only valid when logic sends SRAM request
assign addr_d_in_mailbox = (mailbox_masked_addr_d == mailbox_addr_i);
assign addr_q_in_mailbox = (mailbox_masked_addr_q == mailbox_addr_i);
// internal addr is the address that the logic tracks.
// the first address comes from host system and then the internal logic
// manages the address to follow.
logic sram_req;
// Check if mailbox and intercept config, then raises mailbox_assumed.
// The signal shall be registered in SCK in.
// Then, spi_device top will latch to SCK out
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) mailbox_assumed_o <= 1'b 0;
else if (sram_req && mailbox_en_i && cfg_intercept_en_mbx_i
&& addr_d_in_mailbox) begin
mailbox_assumed_o <= 1'b 1;
end else if (mailbox_en_i && cfg_intercept_en_mbx_i
&& addr_d_in_mailbox && (bitcnt == 3'h 0)) begin
// Keep checking if the next byte falls into the mailbox region
mailbox_assumed_o <= 1'b 1;
end else if (!addr_d_in_mailbox && (bitcnt == 3'h 0)) begin
// At every byte, Check the address goes out of mailbox region.
mailbox_assumed_o <= 1'b 0;
end
end
//- END: SRAM Datapath ----------------------------------------------------
//= BEGIN: FIFO to P2S datapath =============================================
assign p2s_byte = fifo_rdata;
// outclk latch
// can't put async fifo. DC constraint should have half clk datapath
always_ff @(posedge clk_out_i or negedge rst_ni) begin
if (!rst_ni) begin
p2s_valid_o <= 1'b 0;
p2s_byte_o <= '0 ;
end else begin
p2s_valid_o <= p2s_valid_inclk;
p2s_byte_o <= p2s_byte;
end
end
//- END: FIFO to P2S datapath ---------------------------------------------
//= BEGIN: Double Buffering =================================================
// Readbuf Index:
//
// this signal is not reset by CSb. The value should be alive throughout the
// CSb event. (last_access_addr too)
always_ff @(posedge clk_i or negedge sys_rst_ni) begin
if (!sys_rst_ni) begin
readbuf_idx <= 1'b 0;
end else if (readbuf_flip) begin
// readbuf_flip happens when the module completes the second to the last
// byte of a buffer through SPI. There will be a chance that will be
// cancelled by de-asserting CSb. This logic does not guarantee to cover
// that corner case. It expects to complete a byte transfer if it sends
// the first beat of the byte.
readbuf_idx <= ~readbuf_idx;
end
end
// readbuf_flip
// TODO: implement. Below is temporary.
assign readbuf_flip = (main_st == MainOutput && addr_q[9:0] == '1);
//- END: Double Buffering -------------------------------------------------
///////////////////
// State Machine //
///////////////////
//= BEGIN: Main state machine ===============================================
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
main_st <= MainReset;
end else begin
main_st <= main_st_d;
end
end
always_comb begin
main_st_d = main_st;
load_dummycnt = 1'b 0;
// address control
addr_cnt_set = 1'b 0;
addr_inc = 1'b 0;
sram_req = 1'b 0;
addr_shift_en = 1'b 0;
p2s_valid_inclk = 1'b 0;
fifo_pop = 1'b 0;
bitcnt_update = 1'b 0;
bitcnt_dec = 1'b 0;
io_mode_o = SingleIO;
readbuf_start = 1'b 0;
readbuf_update = 1'b 0;
unique case (main_st)
MainReset: begin
if (sel_dp_i inside {DpReadCmd, DpReadSFDP}) begin
// Any readcommand goes to MainAddress state to latch address
// 3B, 4B handles inside address
main_st_d = MainAddress;
addr_cnt_set = 1'b 1;
end
end
MainAddress: begin
addr_shift_en = 1'b 1;
// TODO: DualIO/ QuadIO case
if (addr_ready_in_word) begin
sram_req = 1'b 1;
end
if (addr_latched) begin
// update bitcnt. If input address is not word aligned, bitcnt
// could be 23, 15, or 7
bitcnt_update = 1'b 1;
// Next state:
// MByte if mbyte enabled
// Dummy if mbyte = 0 and dummy_en = 1
// Output if mbyte = 0 and dummy_en = 0
unique casez ({cmd_info_i.mbyte_en, cmd_info_i.dummy_en})
2'b 00: begin
// Moves to Output directly
main_st_d = MainOutput;
readbuf_start = 1'b 1;
readbuf_update = 1'b 1;
end
2'b 01: begin
// Dummy Enabled
main_st_d = MainDummy;
load_dummycnt = 1'b 1;
end
2'b 1?: begin
// Regardless of Dummy
main_st_d = MainMByte;
// TODO: Set MByte latency (3 or 1)
end
default: begin
main_st_d = MainError;
end
endcase
end
end
MainMByte: begin
if (s2p_valid_i) begin
main_st_d = MainDummy;
load_dummycnt = 1'b 1;
end
end
MainDummy: begin
if (dummycnt_eq_zero) begin
main_st_d = MainOutput;
readbuf_start = 1'b 1;
readbuf_update = 1'b 1;
end
end
MainOutput: begin
bitcnt_dec = 1'b 1;
// Note: p2s accepts the byte and latch inside at the first beat.
// So, it is safe to change the data at the next cycle.
p2s_valid_inclk = 1'b 1;
// DeadEnd until CSb deasserted
// Change Mode based on the payload_en[3:0]
// Return data from FIFO
unique case (cmd_info_i.payload_en)
4'b 0010: io_mode_o = SingleIO;
4'b 0011: io_mode_o = DualIO;
4'b 1111: io_mode_o = QuadIO;
default: io_mode_o = SingleIO;
endcase
if (bitcnt == 3'h 0) begin
// Increase addr by 1 byte
addr_inc = 1'b 1;
// When address is begin updated, ask readbuf to update the state too
readbuf_update = 1'b 1;
// sent all words
bitcnt_update = 1'b 1;
// TODO: FIFO pop here?
fifo_pop = 1'b 1;
end
end
MainError: begin
main_st_d = MainError;
end
default: begin
main_st_d = MainReset;
end
endcase
end
//- END: Main state machine -----------------------------------------------
// Events: register
// watermark, flip are pulse signals. watermark pulse width is 1 SCK.
// flip pulse width varies inside readbuffer and changed into 1 SCK width.
// They are not registered (output of comb logic).
// As these signals goes into prim_pulse_sync, no need to register here.
assign sck_read_watermark_o = read_watermark;
assign sck_read_flip_o = read_flip;
///////////////
// Instances //
///////////////
spid_readsram #(
.ReadBaseAddr (ReadBaseAddr),
.ReadBufferDepth (ReadBufferDepth),
.MailboxBaseAddr (MailboxBaseAddr),
.MailboxDepth (MailboxDepth),
.SfdpBaseAddr (SfdpBaseAddr),
.SfdpDepth (SfdpDepth)
) u_readsram (
.clk_i,
.rst_ni,
.sram_read_req_i (sram_req),
.addr_latched_i (addr_latched),
.current_address_i (addr_d), // TODO: Change it
.mailbox_en_i,
.mailbox_addr_i,
.sfdp_hit_i (sel_dp_i == DpReadSFDP),
.sram_l2m_o,
.sram_m2l_i,
// FIFO
.fifo_rvalid_o (unused_fifo_rvalid),
.fifo_rready_i (fifo_pop),
.fifo_rdata_o (fifo_rdata)
);
// Double Buffer Management logic
spid_readbuffer u_readbuffer (
.clk_i,
.rst_ni,
.sys_rst_ni,
.spi_mode_i,
.current_address_i (addr_d),
.threshold_i (readbuf_threshold_i),
.sfdp_hit_i (sfdp_hit),
.mailbox_hit_i (addr_d_in_mailbox),
.mailbox_en_i (mailbox_en_i),
.start_i (readbuf_start),
.address_update_i (readbuf_update),
.event_watermark_o (read_watermark),
.event_flip_o (read_flip)
);
////////////////
// Assertions //
////////////////
// `addr_inc` should not be asserted in Address phase
`ASSERT(AddrIncNotAssertInAddressState_A, addr_inc |-> main_st != MainAddress)
// Assume mailbox addr config is mailbox size.
// As depth is # of entries and the entry is word, the size should add 2bits
`ASSUME(MailboxSizeMatch_M, mailbox_addr_i[$clog2(MailboxDepth)+1:0] == '0)
endmodule : spi_readcmd