blob: 19ffefd55731d4616e7c51dfa9240fde75131614 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Serial Peripheral Interface (SPI) Device module.
//
`include "prim_assert.sv"
module spi_device
import spi_device_reg_pkg::*;
#(
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}
) (
input clk_i,
input rst_ni,
// Register interface
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,
// Alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
// SPI Interface
input cio_sck_i,
input cio_csb_i,
output logic [3:0] cio_sd_o,
output logic [3:0] cio_sd_en_o,
input [3:0] cio_sd_i,
// Passthrough interface
output spi_device_pkg::passthrough_req_t passthrough_o,
input spi_device_pkg::passthrough_rsp_t passthrough_i,
// Interrupts
output logic intr_rxf_o, // RX FIFO Full
output logic intr_rxlvl_o, // RX FIFO above level
output logic intr_txlvl_o, // TX FIFO below level
output logic intr_rxerr_o, // RX Frame error
output logic intr_rxoverflow_o, // RX Async FIFO Overflow
output logic intr_txunderflow_o, // TX Async FIFO Underflow
// Memory configuration
input prim_ram_2p_pkg::ram_2p_cfg_t ram_cfg_i,
// DFT related controls
input mbist_en_i,
input scan_clk_i,
input scan_rst_ni,
input lc_ctrl_pkg::lc_tx_t scanmode_i
);
import spi_device_pkg::*;
localparam int FifoWidth = $bits(spi_byte_t);
localparam int FifoDepth = 8; // 2 DWords
localparam int SDW = $clog2(SramDw/FifoWidth);
localparam int PtrW = SramAw + 1 + SDW;
localparam int AsFifoDepthW = $clog2(FifoDepth+1);
logic clk_spi_in, clk_spi_in_muxed, clk_spi_in_buf; // clock for latch SDI
logic clk_spi_out, clk_spi_out_muxed, clk_spi_out_buf; // clock for driving SDO
spi_device_reg2hw_t reg2hw;
spi_device_hw2reg_t hw2reg;
tlul_pkg::tl_h2d_t tl_sram_h2d;
tlul_pkg::tl_d2h_t tl_sram_d2h;
// Dual-port SRAM Interface: Refer prim_ram_2p_wrapper.sv
logic sram_clk;
logic sram_clk_en;
logic sram_clk_ungated;
logic sram_clk_muxed;
logic sram_rst_n;
logic sram_rst_n_noscan;
logic mem_a_req;
logic mem_a_write;
logic [SramAw-1:0] mem_a_addr;
logic [SramDw-1:0] mem_a_wdata;
logic mem_a_rvalid;
logic [SramDw-1:0] mem_a_rdata;
logic [1:0] mem_a_rerror;
logic mem_b_req;
logic mem_b_write;
logic [SramAw-1:0] mem_b_addr;
logic [SramDw-1:0] mem_b_wdata;
logic mem_b_rvalid;
logic [SramDw-1:0] mem_b_rdata;
logic [1:0] mem_b_rerror;
// Submoule SRAM Requests
logic sub_sram_req [IoModeEnd];
logic sub_sram_write [IoModeEnd];
logic [SramAw-1:0] sub_sram_addr [IoModeEnd];
logic [SramDw-1:0] sub_sram_wdata [IoModeEnd];
logic sub_sram_rvalid [IoModeEnd];
logic [SramDw-1:0] sub_sram_rdata [IoModeEnd];
logic [1:0] sub_sram_rerror [IoModeEnd];
// Host return path mux
logic [3:0] internal_sd, internal_sd_en;
logic [3:0] passthrough_sd, passthrough_sd_en;
/////////////////////
// Control signals //
/////////////////////
logic cpol; // Clock polarity
logic cpha; // Phase : Not complete
logic txorder; // TX bitstream order: 0(bit 7 to 0), 1(bit 0 to 7)
logic rxorder; // RX bitstream order: 0(bit 7 to 0), 1(bit 0 to 7)
logic abort; // Abort current operations (txf only at this time)
// Think how FW knows abort is done.
//logic abort_done; // TODO: Not implemented yet
logic csb_syncd;
logic rst_txfifo_n, rst_rxfifo_n;
logic rst_txfifo_reg, rst_rxfifo_reg;
//spi_addr_size_e addr_size; // Not used in fwmode
spi_mode_e spi_mode;
//spi_byte_t fw_dummy_byte;
logic [255:0] cfg_upload_mask;
logic cfg_addr_4b_en;
logic intr_sram_rxf_full, intr_fwm_rxerr;
logic intr_fwm_rxlvl, rxlvl, rxlvl_d, intr_fwm_txlvl, txlvl, txlvl_d;
logic intr_fwm_rxoverflow, intr_fwm_txunderflow;
logic rxf_overflow, txf_underflow;
logic [7:0] timer_v; // Wait timer inside rxf control
logic [PtrW-1:0] sram_rxf_rptr, sram_rxf_wptr;
logic [PtrW-1:0] sram_txf_rptr, sram_txf_wptr;
logic [PtrW-1:0] sram_rxf_depth, sram_txf_depth;
logic [SramAw-1:0] sram_rxf_bindex, sram_txf_bindex;
logic [SramAw-1:0] sram_rxf_lindex, sram_txf_lindex;
logic [AsFifoDepthW-1:0] as_txfifo_depth, as_rxfifo_depth;
logic rxf_empty, rxf_full, txf_empty, txf_full;
logic rxf_full_syncd, txf_empty_syncd; // sync signals
// SPI S2P signals
// io_mode: Determine s2p/p2s behavior. As of now, only fwmode exists.
// TODO: Add FlashMode IO, passthrough IO
// based on the SPI protocol, the mode should be changed at the negedge of
// SPI_CLK. The sub_iomode value is changed based on the input of SPI,
// it is latched by clk_spi_out.
// TODO: Add this path to DC constraint
io_mode_e io_mode, io_mode_outclk;
io_mode_e sub_iomode[IoModeEnd];
logic s2p_data_valid;
spi_byte_t s2p_data;
logic [BitCntW-1:0] s2p_bitcnt;
logic p2s_valid;
spi_byte_t p2s_data;
logic p2s_sent;
logic sub_p2s_valid[IoModeEnd];
spi_byte_t sub_p2s_data[IoModeEnd];
logic sub_p2s_sent[IoModeEnd];
// CMD interface
sel_datapath_e cmd_dp_sel, cmd_dp_sel_outclk;
spi_byte_t cmd_opcode;
// Mailbox in Passthrough needs to take SPI if readcmd hits mailbox address
logic mailbox_assumed, passthrough_assumed_by_internal;
// Passthrouth config signals
logic [255:0] cmd_filter;
logic [31:0] addr_swap_mask;
logic [31:0] addr_swap_data;
// Command Info structure
cmd_info_t [spi_device_reg_pkg::NumCmdInfo-1:0] cmd_info;
//////////////////////////////////////////////////////////////////////
// Connect phase (between control signals above and register module //
//////////////////////////////////////////////////////////////////////
assign cpol = reg2hw.cfg.cpol.q;
assign cpha = reg2hw.cfg.cpha.q;
assign txorder = reg2hw.cfg.tx_order.q;
assign rxorder = reg2hw.cfg.rx_order.q;
assign rst_txfifo_reg = reg2hw.control.rst_txfifo.q;
assign rst_rxfifo_reg = reg2hw.control.rst_rxfifo.q;
assign sram_clk_en = reg2hw.control.sram_clk_en.q;
assign timer_v = reg2hw.cfg.timer_v.q;
assign cfg_addr_4b_en = reg2hw.cfg.addr_4b_en.q;
assign sram_rxf_bindex = reg2hw.rxf_addr.base.q[SDW+:SramAw];
assign sram_rxf_lindex = reg2hw.rxf_addr.limit.q[SDW+:SramAw];
assign sram_txf_bindex = reg2hw.txf_addr.base.q[SDW+:SramAw];
assign sram_txf_lindex = reg2hw.txf_addr.limit.q[SDW+:SramAw];
assign sram_rxf_rptr = reg2hw.rxf_ptr.rptr.q[PtrW-1:0];
assign hw2reg.rxf_ptr.wptr.d = {{(16-PtrW){1'b0}}, sram_rxf_wptr};
assign hw2reg.rxf_ptr.wptr.de = 1'b1;
assign sram_txf_wptr = reg2hw.txf_ptr.wptr.q[PtrW-1:0];
assign hw2reg.txf_ptr.rptr.d = {{(16-PtrW){1'b0}}, sram_txf_rptr};
assign hw2reg.txf_ptr.rptr.de = 1'b1;
assign abort = reg2hw.control.abort.q;
assign hw2reg.status.abort_done.d = 1'b1;
assign hw2reg.status.rxf_empty.d = rxf_empty;
assign hw2reg.status.txf_full.d = txf_full;
// SYNC logic required
assign hw2reg.status.rxf_full.d = rxf_full_syncd;
assign hw2reg.status.txf_empty.d = txf_empty_syncd;
// CSb : after 2stage synchronizer
assign hw2reg.status.csb.d = csb_syncd;
prim_flop_2sync #(.Width(1)) u_sync_csb (
.clk_i,
.rst_ni,
.d_i(cio_csb_i),
.q_o(csb_syncd)
);
logic rxf_full_q, txf_empty_q;
always_ff @(posedge clk_spi_in_buf or negedge rst_ni) begin
if (!rst_ni) rxf_full_q <= 1'b0;
else rxf_full_q <= rxf_full;
end
always_ff @(posedge clk_spi_out_buf or negedge rst_ni) begin
if (!rst_ni) txf_empty_q <= 1'b1;
else txf_empty_q <= txf_empty;
end
prim_flop_2sync #(.Width(1)) u_sync_rxf (
.clk_i,
.rst_ni,
.d_i(rxf_full_q),
.q_o(rxf_full_syncd)
);
prim_flop_2sync #(.Width(1), .ResetValue(1'b1)) u_sync_txe (
.clk_i,
.rst_ni,
.d_i(txf_empty_q),
.q_o(txf_empty_syncd)
);
assign spi_mode = spi_mode_e'(reg2hw.control.mode.q);
// TODO: Define and connect masks.
assign cfg_upload_mask = '0;
// Async FIFO level
// rx rdepth, tx wdepth to be in main clock domain
assign hw2reg.async_fifo_level.txlvl.d = {{(8-AsFifoDepthW){1'b0}}, as_txfifo_depth};
assign hw2reg.async_fifo_level.rxlvl.d = {{(8-AsFifoDepthW){1'b0}}, as_rxfifo_depth};
// Interrupt
// Edge
logic sram_rxf_full_q, fwm_rxerr_q;
logic sram_rxf_full , fwm_rxerr ;
// TODO: Check if CE# deasserted in the middle of bit transfer
assign fwm_rxerr = 1'b0;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
sram_rxf_full_q <= 1'b0;
fwm_rxerr_q <= 1'b0;
end else begin
sram_rxf_full_q <= sram_rxf_full;
fwm_rxerr_q <= fwm_rxerr;
end
end
// Interrupt
assign intr_sram_rxf_full = ~sram_rxf_full_q & sram_rxf_full;
assign intr_fwm_rxerr = ~fwm_rxerr_q & fwm_rxerr;
assign rxlvl_d = (sram_rxf_depth >= reg2hw.fifo_level.rxlvl.q[PtrW-1:0]) ;
assign txlvl_d = (sram_txf_depth < reg2hw.fifo_level.txlvl.q[PtrW-1:0]) ;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rxlvl <= 1'b0;
txlvl <= 1'b0;
end else begin
rxlvl <= rxlvl_d;
txlvl <= txlvl_d;
end
end
assign intr_fwm_rxlvl = ~rxlvl && rxlvl_d;
assign intr_fwm_txlvl = ~txlvl && txlvl_d;
// rxf_overflow
// Could trigger lint error for input clock.
// It's unavoidable due to the characteristics of SPI intf
prim_pulse_sync u_rxf_overflow (
.clk_src_i (clk_spi_in_buf ),
.rst_src_ni (rst_ni ),
.src_pulse_i (rxf_overflow ),
.clk_dst_i (clk_i ),
.rst_dst_ni (rst_ni ),
.dst_pulse_o (intr_fwm_rxoverflow)
);
// txf_underflow
// Could trigger lint error for input clock.
// It's unavoidable due to the characteristics of SPI intf
prim_pulse_sync u_txf_underflow (
.clk_src_i (clk_spi_out_buf ),
.rst_src_ni (rst_ni ),
.src_pulse_i (txf_underflow ),
.clk_dst_i (clk_i ),
.rst_dst_ni (rst_ni ),
.dst_pulse_o (intr_fwm_txunderflow)
);
assign intr_rxlvl_o = reg2hw.intr_enable.rxlvl.q & reg2hw.intr_state.rxlvl.q;
assign intr_txlvl_o = reg2hw.intr_enable.txlvl.q & reg2hw.intr_state.txlvl.q;
assign intr_rxf_o = reg2hw.intr_enable.rxf.q & reg2hw.intr_state.rxf.q;
assign intr_rxerr_o = reg2hw.intr_enable.rxerr.q & reg2hw.intr_state.rxerr.q;
assign intr_rxoverflow_o = reg2hw.intr_enable.rxoverflow.q & reg2hw.intr_state.rxoverflow.q;
assign intr_txunderflow_o = reg2hw.intr_enable.txunderflow.q & reg2hw.intr_state.txunderflow.q;
assign hw2reg.intr_state.rxf.d = 1'b1;
assign hw2reg.intr_state.rxf.de = intr_sram_rxf_full |
(reg2hw.intr_test.rxf.qe & reg2hw.intr_test.rxf.q);
assign hw2reg.intr_state.rxerr.d = 1'b1;
assign hw2reg.intr_state.rxerr.de = intr_fwm_rxerr |
(reg2hw.intr_test.rxerr.qe & reg2hw.intr_test.rxerr.q);
assign hw2reg.intr_state.rxlvl.d = 1'b1;
assign hw2reg.intr_state.rxlvl.de = intr_fwm_rxlvl |
(reg2hw.intr_test.rxlvl.qe & reg2hw.intr_test.rxlvl.q);
assign hw2reg.intr_state.txlvl.d = 1'b1;
assign hw2reg.intr_state.txlvl.de = intr_fwm_txlvl |
(reg2hw.intr_test.txlvl.qe & reg2hw.intr_test.txlvl.q);
assign hw2reg.intr_state.rxoverflow.d = 1'b1;
assign hw2reg.intr_state.rxoverflow.de = intr_fwm_rxoverflow |
(reg2hw.intr_test.rxoverflow.qe & reg2hw.intr_test.rxoverflow.q);
assign hw2reg.intr_state.txunderflow.d = 1'b1;
assign hw2reg.intr_state.txunderflow.de = intr_fwm_txunderflow |
(reg2hw.intr_test.txunderflow.qe & reg2hw.intr_test.txunderflow.q);
// Passthrough config: value shall be stable while SPI transaction is active
//assign cmd_filter = reg2hw.cmd_filter.q;
always_comb begin
for (int unsigned i = 0 ; i < 256 ; i++) begin
cmd_filter[i] = reg2hw.cmd_filter[i].q;
end
end
assign addr_swap_mask = reg2hw.addr_swap_mask.q;
assign addr_swap_data = reg2hw.addr_swap_data.q;
// Connect command info
always_comb begin
for (int unsigned i = 0 ; i < spi_device_reg_pkg::NumCmdInfo ; i++) begin
cmd_info[i] = '{
opcode: reg2hw.cmd_info[i].opcode.q,
addr_en: reg2hw.cmd_info[i].addr_en.q,
addr_swap_en: reg2hw.cmd_info[i].addr_swap_en.q,
addr_4b_affected: reg2hw.cmd_info[i].addr_4b_affected.q,
dummy_en: reg2hw.cmd_info[i].dummy_en.q,
dummy_size: reg2hw.cmd_info[i].dummy_size.q,
payload_en: reg2hw.cmd_info[i].payload_en.q,
payload_dir: payload_dir_e'(reg2hw.cmd_info[i].payload_dir.q)
};
end
end
//////////////////////////////
// // Clock & reset control //
//////////////////////////////
// clk_spi cannot use glitch-free clock mux as clock switching in glitch-free
// requires two clocks to propagate clock selection and enable but SPI clock
// doesn't exist until it transmits data through SDI
logic sck_n;
logic rst_spi_n;
lc_ctrl_pkg::lc_tx_t [ScanModeUseLast-1:0] scanmode;
prim_lc_sync #(
.NumCopies(int'(ScanModeUseLast)),
.AsyncOn(0)
) u_scanmode_sync (
.clk_i(1'b0), //unused
.rst_ni(1'b1), //unused
.lc_en_i(scanmode_i),
.lc_en_o(scanmode)
);
prim_clock_inv u_clk_spi (
.clk_i(cio_sck_i),
.clk_no(sck_n),
.scanmode_i(scanmode[ClkInvSel] == lc_ctrl_pkg::On)
);
assign clk_spi_in = (cpha ^ cpol) ? sck_n : cio_sck_i ;
assign clk_spi_out = (cpha ^ cpol) ? cio_sck_i : sck_n ;
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_clk_spi_in_mux (
.clk0_i(clk_spi_in),
.clk1_i(scan_clk_i),
.sel_i(scanmode[ClkMuxSel] == lc_ctrl_pkg::On),
.clk_o(clk_spi_in_muxed)
);
prim_clock_buf u_clk_spi_in_buf(
.clk_i (clk_spi_in_muxed),
.clk_o (clk_spi_in_buf)
);
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_clk_spi_out_mux (
.clk0_i(clk_spi_out),
.clk1_i(scan_clk_i),
.sel_i(scanmode[ClkMuxSel] == lc_ctrl_pkg::On),
.clk_o(clk_spi_out_muxed)
);
prim_clock_buf u_clk_spi_out_buf(
.clk_i (clk_spi_out_muxed),
.clk_o (clk_spi_out_buf)
);
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_csb_rst_scan_mux (
.clk0_i(rst_ni & ~cio_csb_i),
.clk1_i(scan_rst_ni),
.sel_i(scanmode[CsbRstMuxSel] == lc_ctrl_pkg::On),
.clk_o(rst_spi_n)
);
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_tx_rst_scan_mux (
.clk0_i(rst_ni & ~rst_txfifo_reg),
.clk1_i(scan_rst_ni),
.sel_i(scanmode[TxRstMuxSel] == lc_ctrl_pkg::On),
.clk_o(rst_txfifo_n)
);
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_rx_rst_scan_mux (
.clk0_i(rst_ni & ~rst_rxfifo_reg),
.clk1_i(scan_rst_ni),
.sel_i(scanmode[RxRstMuxSel] == lc_ctrl_pkg::On),
.clk_o(rst_rxfifo_n)
);
// SRAM clock
// If FwMode, SRAM clock for B port uses peripheral clock (clk_i)
// If FlashMode or PassThrough, SRAM clock for B port uses SPI_CLK
// To remove glitch, CG cell is put after clock mux
// The enable signal is not synchronized to SRAM_CLK when clock is
// switched into SPI_CLK. So, change the clock only when SPI_CLK is
// not toggle.
//
// Programming sequence:
// Change to SPI_CLK
// 1. Check if SPI line is idle.
// 2. Clear sram_clk_en to 0.
// 3. Change mode to FlashMode or PassThrough
// 4. Set sram_clk_en to 1.
// Change to peripheral clk
// 1. Check if SPI_CLK is idle
// 2. Clear sram_clk_en to 0.
// 3. Change mode to FwMode
// 4. Set sram_clk_en to 1.
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_sram_clk_sel (
.clk0_i (clk_spi_in_muxed),
.clk1_i (clk_i),
.sel_i (spi_mode == FwMode),
.clk_o (sram_clk_ungated)
);
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_sram_clk_scan (
.clk0_i (sram_clk_ungated),
.clk1_i (scan_clk_i),
.sel_i ((scanmode[ClkSramSel] == lc_ctrl_pkg::On) | mbist_en_i),
.clk_o (sram_clk_muxed)
);
prim_clock_gating u_sram_clk_cg (
.clk_i (sram_clk_muxed),
.en_i (sram_clk_en),
.test_en_i ((scanmode[ClkSramSel] == lc_ctrl_pkg::On) | mbist_en_i),
.clk_o (sram_clk)
);
prim_clock_mux2 #(
.NoFpgaBufG (1'b1)
) u_sram_rst_sel (
.clk0_i (rst_spi_n),
.clk1_i (rst_ni),
.sel_i (spi_mode == FwMode),
.clk_o (sram_rst_n_noscan)
);
prim_clock_mux2 #(
.NoFpgaBufG (1'b1)
) u_sram_rst_scanmux (
.clk0_i (sram_rst_n_noscan),
.clk1_i (scan_rst_ni),
.sel_i (scanmode[RstSramSel] == lc_ctrl_pkg::On),
.clk_o (sram_rst_n)
);
//////////////////////////////
// SPI_DEVICE mode selector //
//////////////////////////////
// This logic chooses appropriate signals based on input SPI_DEVICE mode.
// e.g) If FwMode is selected. all data connected to spi_fwmode logic
// Assume spi_mode does not change dynamically
// io_mode to spi_s2p io_mode should be affected at the negedge of SPI_CLK
// based on SPI protocol. the internal io_mode signal is generated by SPI
// input signals. So, the io_mode should be latched at clk_spi_out to not
// introduce the timing loop.
//
// example: cmdparse triggers sel_dp at 8th beat of CMD bit.
// -> readcmd activates, it also changes IoMode if opcode is DualIO
// or QuadIO commands
// -> changed io_mode affects spi_s2p module, which again affects
// cmdparse module.
always_ff @(posedge clk_spi_out_buf or negedge rst_spi_n) begin
if (!rst_spi_n) io_mode_outclk <= SingleIO;
else io_mode_outclk <= io_mode;
end
always_ff @(posedge clk_spi_out_buf or negedge rst_spi_n) begin
if (!rst_spi_n) cmd_dp_sel_outclk <= DpNone;
else cmd_dp_sel_outclk <= cmd_dp_sel;
end
always_comb begin
io_mode = SingleIO;
p2s_valid = 1'b 0;
p2s_data = 8'h 0;
sub_p2s_sent = '{default: 1'b 0};
mem_b_req = 1'b 0;
mem_b_write = 1'b 0;
mem_b_addr = '0;
mem_b_wdata = '0;
sub_sram_rvalid = '{default: 1'b 0};
sub_sram_rdata = '{default: '0};
sub_sram_rerror = '{default: 2'b 00};
unique case (spi_mode)
FwMode: begin
io_mode = sub_iomode[IoModeFw];
p2s_valid = sub_p2s_valid[IoModeFw];
p2s_data = sub_p2s_data[IoModeFw];
sub_p2s_sent[IoModeFw] = p2s_sent;
// SRAM:: Remember this has glitch
// switch should happen only when clock gate is disabled.
mem_b_req = sub_sram_req [IoModeFw];
mem_b_write = sub_sram_write [IoModeFw];
mem_b_addr = sub_sram_addr [IoModeFw];
mem_b_wdata = sub_sram_wdata [IoModeFw];
sub_sram_rvalid [IoModeFw] = mem_b_rvalid;
sub_sram_rdata [IoModeFw] = mem_b_rdata;
sub_sram_rerror [IoModeFw] = mem_b_rerror;
end
FlashMode, PassThrough: begin
unique case (cmd_dp_sel_outclk)
DpNone: begin
io_mode = sub_iomode[IoModeCmdParse];
sub_p2s_sent[IoModeCmdParse] = p2s_sent;
// Leave SRAM default;
end
DpReadCmd: begin
io_mode = sub_iomode[IoModeReadCmd];
p2s_valid = sub_p2s_valid[IoModeReadCmd];
p2s_data = sub_p2s_data[IoModeReadCmd];
sub_p2s_sent[IoModeReadCmd] = p2s_sent;
// SRAM:: Remember this has glitch
// switch should happen only when clock gate is disabled.
mem_b_req = sub_sram_req [IoModeReadCmd];
mem_b_write = sub_sram_write [IoModeReadCmd];
mem_b_addr = sub_sram_addr [IoModeReadCmd];
mem_b_wdata = sub_sram_wdata [IoModeReadCmd];
sub_sram_rvalid [IoModeReadCmd] = mem_b_rvalid;
sub_sram_rdata [IoModeReadCmd] = mem_b_rdata;
sub_sram_rerror [IoModeReadCmd] = mem_b_rerror;
end
// DpReadStatus:
// DpReadSFDP:
// DpReadJEDEC:
// DpUpload:
// DpUnknown:
default: begin
io_mode = sub_iomode[IoModeCmdParse];
sub_p2s_sent[IoModeCmdParse] = p2s_sent;
end
endcase
end
default: begin
io_mode = SingleIO;
end
endcase
end
`ASSERT_KNOWN(SpiModeKnown_A, spi_mode)
always_comb begin
cio_sd_o = internal_sd;
cio_sd_en_o = internal_sd_en;
unique case (spi_mode)
FwMode, FlashMode: begin
cio_sd_o = internal_sd;
cio_sd_en_o = internal_sd_en;
end
PassThrough: begin
if (passthrough_assumed_by_internal) begin
cio_sd_o = internal_sd;
cio_sd_en_o = internal_sd_en;
end else begin
cio_sd_o = passthrough_sd;
cio_sd_en_o = passthrough_sd_en;
end
end
default: begin
cio_sd_o = internal_sd;
cio_sd_en_o = internal_sd_en;
end
endcase
end
assign passthrough_assumed_by_internal = mailbox_assumed
// TOGO: Uncomment below when those submodules are implemented.
// | readstatus_assumed | readsfdp_assumed | readjedec_assumed
;
////////////////////////////
// SPI Serial to Parallel //
////////////////////////////
spi_s2p u_s2p (
.clk_i (clk_spi_in_buf),
.rst_ni (rst_spi_n),
// SPI interface
.s_i (cio_sd_i),
.data_valid_o (s2p_data_valid),
.data_o (s2p_data ),
.bitcnt_o (s2p_bitcnt ),
// Config (changed dynamically)
.order_i (rxorder),
.io_mode_i (io_mode_outclk)
);
spi_p2s u_p2s (
.clk_i (clk_spi_out_buf),
.rst_ni (rst_spi_n),
.data_valid_i (p2s_valid),
.data_i (p2s_data),
.data_sent_o (p2s_sent),
.csb_i (cio_csb_i),
.s_en_o (internal_sd_en),
.s_o (internal_sd),
.cpha_i (cpha),
.order_i (txorder),
.io_mode_i (io_mode_outclk)
);
/////////////
// FW Mode //
/////////////
spi_fwmode #(
.FifoWidth (FifoWidth),
.FifoDepth (FifoDepth)
) u_fwmode (
.clk_i (sram_clk),
.rst_ni (sram_rst_n),
.clk_spi_in_i (clk_spi_in_buf),
.rst_rxfifo_ni (rst_rxfifo_n),
.clk_spi_out_i (clk_spi_out_buf),
.rst_txfifo_ni (rst_txfifo_n),
.rxf_overflow_o (rxf_overflow),
.txf_underflow_o (txf_underflow),
// SRAM interface
.fwm_req_o (sub_sram_req [IoModeFw]),
.fwm_write_o (sub_sram_write [IoModeFw]),
.fwm_addr_o (sub_sram_addr [IoModeFw]),
.fwm_wdata_o (sub_sram_wdata [IoModeFw]),
.fwm_rvalid_i (sub_sram_rvalid [IoModeFw]),
.fwm_rdata_i (sub_sram_rdata [IoModeFw]),
.fwm_rerror_i (sub_sram_rerror [IoModeFw]),
// Input from S2P
.rx_data_valid_i (s2p_data_valid),
.rx_data_i (s2p_data),
// Output to S2P (mode select)
.io_mode_o (sub_iomode[IoModeFw]),
// P2S
.tx_wvalid_o (sub_p2s_valid [IoModeFw]),
.tx_data_o (sub_p2s_data [IoModeFw]),
.tx_wready_i (sub_p2s_sent [IoModeFw]),
// CSRs
.timer_v_i (timer_v),
.sram_rxf_bindex_i (sram_rxf_bindex),
.sram_txf_bindex_i (sram_txf_bindex),
.sram_rxf_lindex_i (sram_rxf_lindex),
.sram_txf_lindex_i (sram_txf_lindex),
.abort_i (abort),
.sram_rxf_rptr_i (sram_rxf_rptr ),
.sram_rxf_wptr_o (sram_rxf_wptr ),
.sram_txf_rptr_o (sram_txf_rptr ),
.sram_txf_wptr_i (sram_txf_wptr ),
.sram_rxf_depth_o (sram_rxf_depth),
.sram_txf_depth_o (sram_txf_depth),
.sram_rxf_full_o (sram_rxf_full ),
.as_txfifo_depth_o (as_txfifo_depth),
.as_rxfifo_depth_o (as_rxfifo_depth),
.rxf_empty_o (rxf_empty),
.rxf_full_o (rxf_full),
.txf_empty_o (txf_empty),
.txf_full_o (txf_full)
);
////////////////////
// SPI Flash Mode //
////////////////////
spi_cmdparse u_cmdparse (
.clk_i (clk_spi_in_buf),
.rst_ni (rst_spi_n),
.data_valid_i (s2p_data_valid),
.data_i (s2p_data),
.spi_mode_i (spi_mode),
.upload_mask_i (cfg_upload_mask),
.cmd_info_i (cmd_info),
.io_mode_o (sub_iomode[IoModeCmdParse]),
.sel_dp_o (cmd_dp_sel),
.opcode_o (cmd_opcode),
// Not used for now
.cmd_config_req_o (),
.cmd_config_idx_o ()
);
spi_readcmd u_readcmd (
.clk_i (clk_spi_in_buf),
.rst_ni (rst_spi_n),
.clk_out_i (clk_spi_out_buf),
.sys_rst_ni (rst_ni),
.sel_dp_i (cmd_dp_sel),
.opcode_i (cmd_opcode),
// SRAM interface
.sram_req_o (sub_sram_req [IoModeReadCmd]),
.sram_we_o (sub_sram_write [IoModeReadCmd]),
.sram_addr_o (sub_sram_addr [IoModeReadCmd]),
.sram_wdata_o (sub_sram_wdata [IoModeReadCmd]),
.sram_rvalid_i (sub_sram_rvalid [IoModeReadCmd]),
.sram_rdata_i (sub_sram_rdata [IoModeReadCmd]),
.sram_rerror_i (sub_sram_rerror [IoModeReadCmd]),
// S2P
.s2p_valid_i (s2p_data_valid),
.s2p_byte_i (s2p_data),
.s2p_bitcnt_i (s2p_bitcnt),
// P2S
.p2s_valid_o (sub_p2s_valid [IoModeReadCmd]),
.p2s_byte_o (sub_p2s_data [IoModeReadCmd]),
.p2s_sent_i (sub_p2s_sent [IoModeReadCmd]),
.spi_mode_i (spi_mode),
// TODO: connect to reg intf
.fastread_dummy_i (3'h 7),
.dualread_dummy_i (3'h 3),
.quadread_dummy_i (3'h 1),
.readbuf_threshold_i ('0), //$clog2(ReadBufferDepth)-1
.addr_4b_en_i (cfg_addr_4b_en),
.mailbox_en_i (1'b 0),
.mailbox_addr_i ('0), // 32
.mailbox_assumed_o (mailbox_assumed),
.io_mode_o (sub_iomode [IoModeReadCmd]),
.read_watermark_o ()
);
/////////////////////
// SPI Passthrough //
/////////////////////
spi_passthrough #(
.NumCmdInfo(spi_device_reg_pkg::NumCmdInfo)
) u_passthrough (
.clk_i (clk_spi_in_buf),
.rst_ni (rst_spi_n),
.clk_out_i (clk_spi_out_buf),
// Configurations
.cfg_cmd_filter_i (cmd_filter), //TODO
.cfg_addr_mask_i (addr_swap_mask), // TODO
.cfg_addr_value_i (addr_swap_data), // TODO
.cfg_addr_4b_en_i (cfg_addr_4b_en),
.cmd_info_i (cmd_info),
.spi_mode_i (spi_mode),
// Host SPI
.host_sck_i (cio_sck_i),
.host_csb_i (cio_csb_i),
.host_s_i (cio_sd_i),
.host_s_o (passthrough_sd),
.host_s_en_o (passthrough_sd_en),
// Passthrough to SPI_HOST HWIP
.passthrough_o,
.passthrough_i,
.mailbox_hit_i (1'b 0),
.event_cmd_filtered_o ()
);
////////////////////
// Common modules //
////////////////////
tlul_adapter_sram #(
.SramAw (SramAw),
.SramDw (SramDw),
.Outstanding (1),
.ByteAccess (0)
) u_tlul2sram (
.clk_i,
.rst_ni,
.tl_i (tl_sram_h2d),
.tl_o (tl_sram_d2h),
.en_ifetch_i (tlul_pkg::InstrDis),
.req_o (mem_a_req),
.req_type_o (),
.gnt_i (mem_a_req), //Always grant when request
.we_o (mem_a_write),
.addr_o (mem_a_addr),
.wdata_o (mem_a_wdata),
.wmask_o (), // Not used
.intg_error_o(),
.rdata_i (mem_a_rdata),
.rvalid_i (mem_a_rvalid),
.rerror_i (mem_a_rerror)
);
// SRAM Wrapper
prim_ram_2p_async_adv #(
.Depth (SramDepth),
.Width (SramDw), // 32 x 512 --> 2kB
.DataBitsPerMask (8),
.EnableECC (0),
.EnableParity (1),
.EnableInputPipeline (0),
.EnableOutputPipeline(0)
) u_memory_2p (
.clk_a_i (clk_i),
.rst_a_ni (rst_ni),
.clk_b_i (sram_clk),
.rst_b_ni (sram_rst_n),
.a_req_i (mem_a_req),
.a_write_i (mem_a_write),
.a_addr_i (mem_a_addr),
.a_wdata_i (mem_a_wdata),
.a_wmask_i ({SramDw{1'b1}}),
.a_rvalid_o (mem_a_rvalid),
.a_rdata_o (mem_a_rdata),
.a_rerror_o (mem_a_rerror),
.b_req_i (mem_b_req),
.b_write_i (mem_b_write),
.b_addr_i (mem_b_addr),
.b_wdata_i (mem_b_wdata),
.b_wmask_i ({SramDw{1'b1}}),
.b_rvalid_o (mem_b_rvalid),
.b_rdata_o (mem_b_rdata),
.b_rerror_o (mem_b_rerror),
.cfg_i (ram_cfg_i)
);
// Register module
logic [NumAlerts-1:0] alert_test, alerts;
spi_device_reg_top u_reg (
.clk_i,
.rst_ni,
.tl_i (tl_i),
.tl_o (tl_o),
.tl_win_o (tl_sram_h2d),
.tl_win_i (tl_sram_d2h),
.reg2hw,
.hw2reg,
.intg_err_o (alerts[0]),
.devmode_i (1'b1)
);
// Alerts
assign alert_test = {
reg2hw.alert_test.q &
reg2hw.alert_test.qe
};
for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[i]),
.IsFatal(1'b1)
) u_prim_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test[i] ),
.alert_req_i ( alerts[0] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_o[i] )
);
end
// make sure scanmode_i is never X (including during reset)
`ASSERT_KNOWN(scanmodeKnown, scanmode_i, clk_i, 0)
`ASSERT_KNOWN(CioSdoEnOKnown, cio_sd_en_o)
`ASSERT_KNOWN(IntrRxfOKnown, intr_rxf_o )
`ASSERT_KNOWN(IntrRxlvlOKnown, intr_rxlvl_o )
`ASSERT_KNOWN(IntrTxlvlOKnown, intr_txlvl_o )
`ASSERT_KNOWN(IntrRxerrOKnown, intr_rxerr_o )
`ASSERT_KNOWN(IntrRxoverflowOKnown, intr_rxoverflow_o )
`ASSERT_KNOWN(IntrTxunderflowOKnown, intr_txunderflow_o)
`ASSERT_KNOWN(AlertKnownO_A, alert_tx_o)
endmodule