blob: 13cef3e4bc89c6a0a8da79f42be756cd70f908a2 [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.
//
package spi_device_pkg;
// Passthrough Inter-module signals
typedef struct packed {
// passthrough_en: switch the mux for downstream SPI pad to host system not
// the internal SPI_HOST IP
logic passthrough_en;
// Passthrough includes SCK also. The sck_en is pad out enable not CG
// enable. The CG is placed in SPI_DEVICE IP.
logic sck;
logic sck_en;
// CSb should be pull-up pad. In passthrough mode, CSb is directly connected
// to the host systems CSb except when SPI_DEVICE decides to drop the
// command.
logic csb;
logic csb_en;
// SPI data from host system to downstream flash device.
logic [3:0] s;
logic [3:0] s_en;
} passthrough_req_t;
typedef struct packed {
// SPI data from downstream flash device to host system.
logic [3:0] s;
} passthrough_rsp_t;
parameter passthrough_req_t PASSTHROUGH_REQ_DEFAULT = '{
passthrough_en: 1'b 0,
sck: 1'b 0,
sck_en: 1'b 0,
csb: 1'b 1,
csb_en: 1'b 0,
s: 4'h 0,
s_en: 4'h 0
};
parameter passthrough_rsp_t PASSTHROUGH_RSP_DEFAULT = '{
s: 4'h 0
};
// Command Type
//
// Passthrough module does not strictly follow every bit on the SPI line but
// loosely tracks the phase of the commands.
//
// The command in SPI Flash can be categorized as follow:
//
// - {Address, PayloadOut}: examples are ReadData
// - {Address, Dummy, PayloadOut}: FastRead / Dual/ Quad commands have dummy
// - {Dummy, PayloadOut}: Release Power-down / Manufacturer ID
// - {PayloadOut}: Right after opcode, the device sends data
// to host
// - {Address, PayloadIn}: Host sends address and payload back-to-back
// - {PayloadIn}: Host sends payload without any address hint
// - None: The commands complete without any address /
// payload(in/out)
//
// If a received command has more than one state, the counter value will be
// set to help the state machine to move to the next state with the exact
// timing.
//
// A `cmd_type_t` struct has information for a command. The actual value for
// commands are compile-time parameters. When the logic receives 8 bits of
// opcode, it latches the parameter into this struct and references this
// through the transaction.
// Address or anything host driving after opcode counter
localparam int unsigned MaxAddrBit = 32;
localparam int unsigned AddrCntW = $clog2(MaxAddrBit);
// Dummy
localparam int unsigned MaxDummyBit = 8;
localparam int unsigned DummyCntW = $clog2(MaxDummyBit);
// TPM
localparam int unsigned AccessRegSize = 8; // times Locality
localparam int unsigned IntEnRegSize = 32;
localparam int unsigned IntVectorRegSize = 8;
localparam int unsigned IntStsRegSize = 32;
localparam int unsigned IntfCapRegSize = 32;
localparam int unsigned StatusRegSize = 32;
localparam int unsigned IdRegSize = 32; // {DID; VID}
localparam int unsigned RidRegSize = 8;
localparam int unsigned ActiveLocalityBitPos = 5; // Access[5
typedef enum logic {
PayloadIn = 1'b 0,
PayloadOut = 1'b 1
} payload_dir_e;
// addr_mode_e: CMD_INFO.addr_mode informs HW if a command has address field,
// CFG.addr_4b_en affected, or address is 3B/4B.
typedef enum logic [1:0] {
AddrDisabled = 2'b 00,
AddrCfg = 2'b 01,
Addr3B = 2'b 10,
Addr4B = 2'b 11
} addr_mode_e;
// cmd_info_t defines the command relevant information. SPI Device IP has
// #NumCmdInfo cmd registers (default 16). A few of them are assigned to a
// specific commands such as Read Status, Read SFDP.
//
// These fields are SW programmable via CSR interface.
typedef struct packed {
// If 1, the cmd_info slot is in active and contains valid config.
logic valid;
// opcode: Each cmd_info type has 8bit opcode. SPI_DEVICE has 16 command
// slots. The logic compares the opcode and uses the command info when
// opcode is matched. If same opcode exists, SPI_DEVICE uses the command
// info slot having the lowest index among the matched ones.
logic [7:0] opcode;
// Look at addr_mode_e description for details
// - 00: Address disabled
// - 01: Address is affected by CFG
// - 10: Address is 3 bytes field
// - 11: Address is 4 bytes field
addr_mode_e addr_mode;
// swap_en is used in the passthrough logic. If this field is set to 1, the
// address in the passthrough command is replaced to the preconfigured
// value.
logic addr_swap_en;
// Mbyte field exist. If set to 1, the command waits 1 byte before moving
// to dummy field. This field data is ignored.
logic mbyte_en;
// set to 1 if the command has a dummy cycle following the address field.
logic dummy_en;
logic [DummyCntW-1:0] dummy_size;
// set to non-zero if the command has payload at the end of the protocol. If
// dummy_en is 1, then the payload follows the dummy cycle. payload_en has
// four bits. Each bit represents the SPI line. If a command is single IO
// command and returns data to the host system, the data is returned on the
// MISO line (IO[1]). In this case, SW sets payload_en to 4'b0010 and
// payload_dir to PayloadOut.
logic [3:0] payload_en;
payload_dir_e payload_dir;
// If payload_swap_en is set, the passthrough logic swaps the first
// 4 bytes of the write payload with PAYLOAD_SWAP_MASK and
// PAYLOAD_SWAP_DATA CSR.
//
// `payload_swap_en` only works with write data and SingleIO mode.
// `payload_en` should be 4'b 0001 && `payload_dir` to be PayloadIn.
logic payload_swap_en;
// upload: If upload field in the command info entry is set, the cmdparse
// activates the upload submodule when the opcode is received. `addr_en`,
// `addr_4B_affected`, and `addr_4b_forced` (TBD) affect the upload
// functionality. The three address related configs defines the command
// address field size.
// The logic assumes the following SPI input stream as payload, which max
// size is 256B. If the command exceeds the maximum payload size 256B, the
// logic wraps the payload and overwrites.
logic upload;
// busy: Set to 1 to set the BUSY bit in the FLASH_STATUS when the command
// is received. This bit is active only when `upload` bit is set.
logic busy;
} cmd_info_t;
// CmdInfoInput parameter is the default value if no opcode in the cmd info
// slot is matched to the received command opcode.
parameter cmd_info_t CmdInfoInput = '{
valid: 1'b 0,
opcode: 8'h 00,
addr_mode: AddrDisabled,
addr_swap_en: 1'b 0,
mbyte_en: 1'b 0,
dummy_en: 1'b 0,
dummy_size: 3'h 0,
payload_en: 4'b 0001, // MOSI active
payload_dir: PayloadIn,
payload_swap_en: 1'b 0,
upload: 1'b 0,
busy: 1'b 0
};
function automatic logic is_cmdinfo_addr_4b(cmd_info_t ci, logic addr_4b_en);
logic result;
result = ci.addr_mode == AddrCfg ? addr_4b_en : (ci.addr_mode == Addr4B);
return result;
endfunction : is_cmdinfo_addr_4b
// get_addr_mode removes AddrCfg.
// It returns {AddrDisabled, Addr3B, Addr4B}
function automatic addr_mode_e get_addr_mode(cmd_info_t ci, logic addr_4b_en);
addr_mode_e result;
result = (ci.addr_mode != AddrCfg) ? ci.addr_mode
: (addr_4b_en) ? Addr4B : Addr3B ;
return result;
endfunction : get_addr_mode
// SPI_DEVICE HWIP has 16 command info slots. A few of them are pre-assigned.
// (defined in the spi_device_pkg)
//
//parameter int unsigned NumCmdInfo = 16;
// cmd_info_index_e assigns some cmd_info slots to specific commands. The
// reason why command slots cannot be fully flexible (unlike NumCmdInfo-way $)
// is that the slots are used in the Flash mode also. The Read Status/ SFDP/
// etc submodules only see the pre-assigned slot and use it.
typedef enum int unsigned {
// Read Status subblock in Flash mode only uses opcode of cmd_info
CmdInfoReadStatus1 = 0,
CmdInfoReadStatus2 = 1,
CmdInfoReadStatus3 = 2,
CmdInfoReadJedecId = 3,
CmdInfoReadSfdp = 4,
// 6 slots are assigned to Read commands in Flash mode.
//
// Read Data / Fast Read / Fast Read Dual / Fast Read Quad
// Fast Read Dual IO / Fast Read Quad IO (IO commands are TBD)
CmdInfoReadCmdStart = 5,
CmdInfoReadCmdEnd = 10,
// other slots are used in the Passthrough and/or upload submodules. These
// free slots may be used for the commands that are not processed in the
// flash mode. Examples are "Release Power-down / ID",
// "Manufacture/Device ID", etc. They are not always Input mode. Some has
// a dummy cycle followed by the output field.
CmdInfoReserveStart = 11,
CmdInfoReserveEnd = spi_device_reg_pkg::NumCmdInfo - 1,
CmdInfoEn4B = CmdInfoReserveEnd + 1,
CmdInfoEx4B = CmdInfoEn4B + 1,
CmdInfoWrEn = CmdInfoEx4B + 1,
CmdInfoWrDi = CmdInfoWrEn + 1,
NumTotalCmdInfo
} cmd_info_index_e;
parameter int unsigned NumReadCmdInfo = CmdInfoReadCmdEnd - CmdInfoReadCmdStart + 1;
// NumCmdInfo adds two more entries (EN4B/ EX4B)
// or, add NumTotalCmdInfo inside cmd_info_index_e and use?
//parameter int unsigned NumFixedCmdInfo = 2;
//parameter int unsigned NumTotalCmdInfo = NumFixedCmdInfo
// + spi_device_reg_pkg::NumCmdInfo;
parameter int unsigned CmdInfoIdxW = $clog2(NumTotalCmdInfo);
// Jedec Configuration Structure
typedef struct packed {
logic [7:0] num_cc; // continuation code repeat
logic [7:0] cc; // the value of CC (default 7Fh)
logic [7:0] jedec_id; // JEDEC Manufacturer ID
logic [15:0] device_id; // Device ID
} jedec_cfg_t ;
// SPI Operation mode
typedef enum logic [1:0] {
FwMode = 'h0,
FlashMode = 'h1,
PassThrough = 'h2
} spi_mode_e;
// SPI IO mode
typedef enum logic [1:0] {
SingleIO = 2'h 0,
DualIO = 2'h 1,
QuadIO = 2'h 2
} io_mode_e;
typedef enum int unsigned {
IoModeFw = 0,
IoModeCmdParse = 1,
IoModeReadCmd = 2,
IoModeStatus = 3,
IoModeJedec = 4,
IoModeUpload = 5,
IoModeEnd = 6 // Indicate of Length
} sub_io_mode_e;
// SPI Line Mode (Mode0 <-> Mode3)
// This HWIP does not support Mode1 and Mode2
typedef enum logic {
// Mode0: CPOL=0, CPHA=0
// Data sampled on rising edge and shifted on falling edge
LineMode0 = 1'b 0,
// Mode3: CPOL=1, CPHA=1
// Data sampled on falling edge and shifted on rising edge
LineMode3 = 1'b 1
} line_mode_e;
// SPI Read mode. QUAD uses additional two pins to read
// Bit 0: Single, Bit 1: Dual Bit 2: Quad
typedef logic [2:0] spi_rdmode_t;
typedef logic [7:0] spi_byte_t;
// eSPI utilizes Alert# signal (from device to host)
typedef enum logic [1:0] {
Spi = 2'h0,
Espi = 2'h1,
Tpm = 2'h2
} spi_type_e;
typedef enum logic [1:0] {
AddrByte = 2'h0, // 1 byte for address
AddrWord = 2'h1, // 2 bytes for address
AddrFull = 2'h2 // 3 bytes for address
} spi_addr_size_e;
localparam int MEM_AW = 12; // Memory Address width (Byte based)
typedef enum logic [9:0] {
DpNone = 'b 0000000000,
DpReadCmd = 'b 0000000001,
DpReadStatus = 'b 0000000010,
DpReadSFDP = 'b 0000000100,
DpReadJEDEC = 'b 0000001000,
// Command + Address only: e.g Block Erase
// Command + Address + Paylod: Program
// Command followed by direct payload
// Write Status could be an example
// Command only: Write Protect Enable / Chip Erase
DpUpload = 'b 0000010000,
DpEn4B = 'b 0000100000,
DpEx4B = 'b 0001000000,
DpWrEn = 'b 0010000000,
DpWrDi = 'b 0100000000,
// Unrecognizable commands: Just handle this as DpPayload
DpUnknown = 'b 1000000000
} sel_datapath_e;
typedef enum spi_byte_t {
CmdNone = 8'h 00,
CmdWriteStatus1 = 8'h 01,
CmdWriteStatus2 = 8'h 31,
CmdWriteStatus3 = 8'h 11,
CmdReadStatus1 = 8'h 05,
CmdReadStatus2 = 8'h 35,
CmdReadStatus3 = 8'h 15,
CmdJedecId = 8'h 9F,
CmdPageProgram = 8'h 02,
CmdQuadPageProgram = 8'h 32, // Not supported
CmdSectorErase = 8'h 20, // 4kB erase
CmdBlockErase32 = 8'h 52, // 32kB
CmdBlockErase64 = 8'h D8, // 64kB
CmdReadData = 8'h 03,
CmdReadFast = 8'h 0B, // with Dummy
CmdReadDual = 8'h 3B,
CmdReadQuad = 8'h 6B,
CmdReadDualIO = 8'h BB,
CmdReadQuadIO = 8'h EB,
CmdWriteDisable = 8'h 04,
CmdWriteEnable = 8'h 06,
CmdEn4B = 8'h B7,
CmdEx4B = 8'h E9,
CmdReadSfdp = 8'h 5A,
CmdChipErase = 8'h C7,
CmdEnableReset = 8'h 66,
CmdResetDevice = 8'h 99
} spi_cmd_e;
// Sram parameters
parameter int unsigned SramDw = 32;
parameter int unsigned SramStrbW = SramDw/8;
parameter int unsigned SramOffsetW = $clog2(SramStrbW);
// Msg region is used for read cmd in Flash and Passthrough region
parameter int unsigned SramMsgDepth = 512; // 2kB
parameter int unsigned SramMsgBytes = SramMsgDepth * SramStrbW;
// Address Width of a Buffer in bytes
parameter int unsigned SramBufferBytes = SramMsgBytes / 2; // Double Buffer
parameter int unsigned SramBufferAw = $clog2(SramBufferBytes);
parameter int unsigned SramMailboxDepth = 256; // 1kB for mailbox
// SPI Flash Discoverable Parameter is for host to get the device
// information. It is a separate Command. The device provides a region in
// DPSRAM for SW. The size is 256B
parameter int unsigned SramSfdpDepth = 64;
parameter int unsigned SramPayloadDepth = 64; // 256B for Program
parameter int unsigned SramCmdFifoDepth = 16; // 16 x 1B for Cmd
parameter int unsigned SramAddrFifoDepth = 16; // 16 x 4B for Addr
parameter int unsigned SramTotalDepth = SramMsgDepth + SramMailboxDepth
+ SramSfdpDepth + SramPayloadDepth
+ SramCmdFifoDepth + SramAddrFifoDepth ;
// Sram Depth is set to 1024 to satisfy DPSRAM parameter even though
// SramTotalDepth above is 928.
//parameter int unsigned SramDepth = 1024;
import spi_device_reg_pkg::SramDepth;
export spi_device_reg_pkg::SramDepth;
parameter int unsigned SramAw = $clog2(spi_device_reg_pkg::SramDepth);
typedef logic [SramAw-1:0] sram_addr_t;
typedef logic [SramDw-1:0] sram_data_t;
typedef logic [SramStrbW-1:0] sram_strb_t;
typedef struct packed {
logic uncorr;
logic corr;
} sram_err_t;
typedef struct packed {
logic req;
logic we;
sram_addr_t addr;
sram_data_t wdata;
sram_strb_t wstrb;
} sram_l2m_t; // logic to Memory
typedef struct packed {
logic rvalid;
sram_data_t rdata;
sram_err_t rerror;
} sram_m2l_t; // Memory to logic
function automatic logic [SramDw-1:0] sram_strb2mask(logic [SramStrbW-1:0] strb);
logic [SramDw-1:0] result;
for (int unsigned i = 0 ; i < SramStrbW ; i++) begin
result[8*i+:8] = strb[i] ? 8'h FF : 8'h 00;
end
return result;
endfunction : sram_strb2mask
function automatic logic [SramStrbW-1:0] sram_mask2strb(
logic [SramDw-1:0] mask
);
logic [SramStrbW-1:0] result;
for (int unsigned i = 0 ; i < SramStrbW ; i++) begin
result[i] = &mask[8*i+:8];
end
return result;
endfunction : sram_mask2strb
// Calculate each space's base and size
parameter sram_addr_t SramReadBufferIdx = sram_addr_t'(0);
parameter sram_addr_t SramReadBufferSize = sram_addr_t'(SramMsgDepth);
parameter sram_addr_t SramMailboxIdx = SramReadBufferIdx + SramReadBufferSize;
parameter sram_addr_t SramMailboxSize = sram_addr_t'(SramMailboxDepth);
parameter sram_addr_t SramSfdpIdx = SramMailboxIdx + SramMailboxSize;
parameter sram_addr_t SramSfdpSize = sram_addr_t'(SramSfdpDepth);
parameter sram_addr_t SramPayloadIdx = SramSfdpIdx + SramSfdpSize;
parameter sram_addr_t SramPayloadSize = sram_addr_t'(SramPayloadDepth);
parameter sram_addr_t SramCmdFifoIdx = SramPayloadIdx + SramPayloadSize ;
parameter sram_addr_t SramCmdFifoSize = sram_addr_t'(SramCmdFifoDepth);
parameter sram_addr_t SramAddrFifoIdx = SramCmdFifoIdx + SramCmdFifoSize ;
parameter sram_addr_t SramAddrFifoSize = sram_addr_t'(SramAddrFifoDepth);
// Max BitCount in a transaction
parameter int unsigned BitLength = SramMsgDepth * 32;
parameter int unsigned BitCntW = $clog2(BitLength + 1);
// spi device scanmode usage
typedef enum logic [3:0] {
ClkInvSel,
CsbRstMuxSel,
TxRstMuxSel,
RxRstMuxSel,
ClkMuxSel,
ClkSramSel,
RstSramSel,
TpmRstSel,
ScanModeUseLast
} scan_mode_e;
// TPM ======================================================================
typedef struct packed {
logic [7:0] rev;
logic locality;
logic [2:0] max_wr_size;
logic [2:0] max_rd_size;
} tpm_cap_t;
// TPM ----------------------------------------------------------------------
endpackage : spi_device_pkg