2023-10-12 20:36:57 +00:00
///////////////////////////////////////////
// spi_apb.sv
//
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu 11/16/2022
//
// Purpose: SPI peripheral
2023-11-14 21:44:59 +00:00
//
// SPI module is written to the specifications described in FU540-C000-v1.0. At the top level, it is consists of synchronous 8 byte transmit and recieve FIFOs connected to shift registers.
// The FIFOs are connected to WALLY by an apb control register interface, which includes various control registers for modifying the SPI transmission along with registers for writing
// to the transmit FIFO and reading from the receive FIFO. The transmissions themselves are then controlled by a finite state machine. The SPI module uses 4 tristate pins for SPI input/output,
// along with a 4 bit Chip Select signal, a clock signal, and an interrupt signal to WALLY.
// Current limitations: Flash read sequencer mode not implemented, dual and quad mode not supported
2023-10-12 20:36:57 +00:00
//
// A component of the Wally configurable RISC-V project.
//
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
// may obtain a copy of the License at
//
// https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work distributed under the
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
////////////////////////////////////////////////////////////////////////////////////////////////
module spi_apb import cvw : : * ; # ( parameter cvw_t P ) (
2023-11-01 17:14:15 +00:00
input logic PCLK , PRESETn ,
input logic PSEL ,
input logic [ 7 : 0 ] PADDR ,
input logic [ P . XLEN - 1 : 0 ] PWDATA ,
2023-10-12 20:36:57 +00:00
input logic [ P . XLEN / 8 - 1 : 0 ] PSTRB ,
2023-11-01 17:14:15 +00:00
input logic PWRITE ,
input logic PENABLE ,
output logic PREADY ,
output logic [ P . XLEN - 1 : 0 ] PRDATA ,
output logic SPIOut ,
input logic SPIIn ,
2023-10-12 20:36:57 +00:00
output logic [ 3 : 0 ] SPICS ,
2024-07-22 17:36:39 +00:00
output logic SPIIntr ,
output logic SPICLK
2023-10-12 20:36:57 +00:00
) ;
2024-04-21 14:43:01 +00:00
// register map
localparam SPI_SCKDIV = 8 'h00 ;
localparam SPI_SCKMODE = 8 'h04 ;
localparam SPI_CSID = 8 'h10 ;
localparam SPI_CSDEF = 8 'h14 ;
localparam SPI_CSMODE = 8 'h18 ;
localparam SPI_DELAY0 = 8 'h28 ;
localparam SPI_DELAY1 = 8 'h2C ;
localparam SPI_FMT = 8 'h40 ;
localparam SPI_TXDATA = 8 'h48 ;
localparam SPI_RXDATA = 8 'h4C ;
localparam SPI_TXMARK = 8 'h50 ;
localparam SPI_RXMARK = 8 'h54 ;
localparam SPI_IE = 8 'h70 ;
localparam SPI_IP = 8 'h74 ;
2024-06-18 12:49:12 +00:00
// receive shift register states
typedef enum logic [ 1 : 0 ] { ReceiveShiftFullState , ReceiveShiftNotFullState , ReceiveShiftDelayState } rsrstatetype ;
2023-11-14 21:44:59 +00:00
// SPI control registers. Refer to SiFive FU540-C000 manual
2023-11-01 08:26:34 +00:00
logic [ 11 : 0 ] SckDiv ;
2023-11-14 21:44:59 +00:00
logic [ 1 : 0 ] SckMode ;
logic [ 1 : 0 ] ChipSelectID ;
logic [ 3 : 0 ] ChipSelectDef ;
logic [ 1 : 0 ] ChipSelectMode ;
2023-11-01 08:26:34 +00:00
logic [ 15 : 0 ] Delay0 , Delay1 ;
2023-11-14 21:44:59 +00:00
logic [ 4 : 0 ] Format ;
logic [ 7 : 0 ] ReceiveData ;
logic [ 2 : 0 ] TransmitWatermark , ReceiveWatermark ;
logic [ 8 : 0 ] TransmitData ;
logic [ 1 : 0 ] InterruptEnable , InterruptPending ;
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// Bus interface signals
2023-10-12 20:36:57 +00:00
logic [ 7 : 0 ] Entry ;
logic Memwrite ;
2024-04-21 14:43:01 +00:00
logic [ 31 : 0 ] Din , Dout ;
2023-11-14 21:44:59 +00:00
logic TransmitInactive ; // High when there is no transmission, used as hardware interlock signal
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// FIFO FSM signals
// Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1]
2023-11-10 00:48:11 +00:00
logic TransmitWriteMark , TransmitReadMark , RecieveWriteMark , RecieveReadMark ;
2023-10-12 20:36:57 +00:00
logic TransmitFIFOWriteFull , TransmitFIFOReadEmpty ;
2023-11-01 17:14:15 +00:00
logic TransmitFIFOWriteIncrement ;
logic ReceiveFIFOReadIncrement ;
2023-10-12 20:36:57 +00:00
logic ReceiveFIFOWriteFull , ReceiveFIFOReadEmpty ;
2023-11-21 07:37:56 +00:00
logic [ 7 : 0 ] TransmitFIFOReadData ;
2024-06-18 12:49:12 +00:00
/* verilator lint_off UNDRIVEN */
logic [ 2 : 0 ] TransmitWriteWatermarkLevel , ReceiveReadWatermarkLevel ; // unused generic FIFO outputs
/* verilator lint_off UNDRIVEN */
2023-11-14 21:44:59 +00:00
logic [ 7 : 0 ] ReceiveShiftRegEndian ; // Reverses ReceiveShiftReg if Format[2] set (little endian transmission)
2024-06-18 15:07:14 +00:00
rsrstatetype ReceiveState ;
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// Transmission signals
2024-07-22 17:36:39 +00:00
// logic sck;
2023-11-14 21:44:59 +00:00
logic [ 11 : 0 ] DivCounter ; // Counter for sck
logic SCLKenable ; // Flip flop enable high every sclk edge
// Delay signals
logic [ 8 : 0 ] ImplicitDelay1 ; // Adds implicit delay to cs-sck delay counter based on phase
logic [ 8 : 0 ] ImplicitDelay2 ; // Adds implicit delay to sck-cs delay counter based on phase
logic [ 8 : 0 ] CS_SCKCount ; // Counter for cs-sck delay
logic [ 8 : 0 ] SCK_CSCount ; // Counter for sck-cs delay
logic [ 8 : 0 ] InterCSCount ; // Counter for inter cs delay
logic [ 8 : 0 ] InterXFRCount ; // Counter for inter xfr delay
logic ZeroDelayHoldMode ; // High when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
// Frame counting signals
logic [ 3 : 0 ] FrameCount ; // Counter for number of frames in transmission
logic ReceivePenultimateFrame ; // High when penultimate frame in transmission has been reached
// State fsm signals
logic Active ; // High when state is either Active1 or Active0 (during transmission)
logic Active0 ; // High when state is Active0
// Shift reg signals
logic ShiftEdge ; // Determines which edge of sck to shift from TransmitShiftReg
logic [ 7 : 0 ] TransmitShiftReg ; // Transmit shift register
logic [ 7 : 0 ] ReceiveShiftReg ; // Receive shift register
logic SampleEdge ; // Determines which edge of sck to sample from ReceiveShiftReg
logic [ 7 : 0 ] TransmitDataEndian ; // Reverses TransmitData from txFIFO if littleendian, since TransmitReg always shifts MSB
logic TransmitShiftRegLoad ; // Determines when to load TransmitShiftReg
logic ReceiveShiftFull ; // High when receive shift register is full
logic TransmitShiftEmpty ; // High when transmit shift register is empty
logic ShiftIn ; // Determines whether to shift from SPIIn or SPIOut (if SPI_LOOPBACK_TEST)
logic [ 3 : 0 ] LeftShiftAmount ; // Determines left shift amount to left-align data when little endian
logic [ 7 : 0 ] ASR ; // AlignedReceiveShiftReg
// CS signals
logic [ 3 : 0 ] ChipSelectAuto ; // Assigns ChipSelect value to selected CS signal based on CS ID
logic [ 3 : 0 ] ChipSelectInternal ; // Defines what each ChipSelect signal should be based on transmission status and ChipSelectDef
logic DelayMode ; // Determines where to place implicit half cycle delay based on sck phase for CS assertion
// Miscellaneous signals delayed/early by 1 PCLK cycle
logic ReceiveShiftFullDelay ; // Delays ReceiveShiftFull signal by 1 PCLK cycle
logic ReceiveShiftFullDelayPCLK ; // ReceiveShiftFull delayed by 1 PCLK cycle
2023-11-10 00:48:11 +00:00
logic TransmitFIFOReadEmptyDelay ;
2023-11-14 21:44:59 +00:00
logic SCLKenableEarly ; // SCLKenable 1 PCLK cycle early, needed for on time register changes when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
2023-11-10 00:48:11 +00:00
2023-11-14 21:44:59 +00:00
// APB access
assign Entry = { PADDR [ 7 : 2 ] , 2 'b00 } ; // 32-bit word-aligned accesses
assign Memwrite = PWRITE & PENABLE & PSEL ; // Only write in access phase
2024-08-21 17:39:01 +00:00
assign PREADY = Entry = = SPI_TXDATA | Entry = = SPI_RXDATA | Entry = = SPI_IP | TransmitInactive ; // Tie PREADY to transmission for hardware interlock
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// Account for subword read/write circuitry
2023-11-01 17:14:15 +00:00
// -- Note SPI registers are 32 bits no matter what; access them with LW SW.
assign Din = PWDATA [ 31 : 0 ] ;
2024-04-21 14:43:01 +00:00
if ( P . XLEN = = 64 ) assign PRDATA = { Dout , Dout } ;
else assign PRDATA = Dout ;
2023-11-01 17:14:15 +00:00
2023-11-14 21:44:59 +00:00
// Register access
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2023-10-12 20:36:57 +00:00
if ( ~ PRESETn ) begin
2024-03-08 19:22:04 +00:00
SckDiv < = 12 'd3 ;
SckMode < = 2 'b0 ;
ChipSelectID < = 2 'b0 ;
ChipSelectDef < = 4 'b1111 ;
2024-04-21 14:43:01 +00:00
ChipSelectMode < = 2 'b0 ;
2024-03-08 19:22:04 +00:00
Delay0 < = { 8 'b1 , 8 'b1 } ;
Delay1 < = { 8 'b0 , 8 'b1 } ;
Format < = { 5 'b10000 } ;
TransmitData < = 9 'b0 ;
TransmitWatermark < = 3 'b0 ;
ReceiveWatermark < = 3 'b0 ;
InterruptEnable < = 2 'b0 ;
InterruptPending < = 2 'b0 ;
2023-11-14 21:44:59 +00:00
end else begin // writes
2023-10-12 20:36:57 +00:00
/* verilator lint_off CASEINCOMPLETE */
2023-11-08 23:20:51 +00:00
if ( Memwrite & TransmitInactive )
2023-11-14 21:44:59 +00:00
case ( Entry ) // flop to sample inputs
2024-04-21 14:43:01 +00:00
SPI_SCKDIV: SckDiv < = Din [ 11 : 0 ] ;
SPI_SCKMODE: SckMode < = Din [ 1 : 0 ] ;
SPI_CSID: ChipSelectID < = Din [ 1 : 0 ] ;
SPI_CSDEF: ChipSelectDef < = Din [ 3 : 0 ] ;
SPI_CSMODE: ChipSelectMode < = Din [ 1 : 0 ] ;
SPI_DELAY0: Delay0 < = { Din [ 23 : 16 ] , Din [ 7 : 0 ] } ;
SPI_DELAY1: Delay1 < = { Din [ 23 : 16 ] , Din [ 7 : 0 ] } ;
2024-08-20 19:40:50 +00:00
SPI_FMT: Format < = { Din [ 19 : 16 ] , Din [ 2 ] } ;
2024-04-21 14:43:01 +00:00
SPI_TXMARK: TransmitWatermark < = Din [ 2 : 0 ] ;
SPI_RXMARK: ReceiveWatermark < = Din [ 2 : 0 ] ;
SPI_IE: InterruptEnable < = Din [ 1 : 0 ] ;
2023-10-12 20:36:57 +00:00
endcase
2024-08-20 19:40:50 +00:00
if ( Memwrite )
case ( Entry )
SPI_TXDATA: if ( ~ TransmitFIFOWriteFull ) TransmitData [ 7 : 0 ] < = Din [ 7 : 0 ] ;
endcase
2023-10-12 20:36:57 +00:00
/* verilator lint_off CASEINCOMPLETE */
2023-11-14 21:44:59 +00:00
// According to FU540 spec: Once interrupt is pending, it will remain set until number
// of entries in tx/rx fifo is strictly more/less than tx/rxmark
2023-10-12 20:36:57 +00:00
InterruptPending [ 0 ] < = TransmitReadMark ;
InterruptPending [ 1 ] < = RecieveWriteMark ;
2023-11-14 21:44:59 +00:00
case ( Entry ) // Flop to sample inputs
2024-04-21 14:43:01 +00:00
SPI_SCKDIV: Dout < = { 20 'b0 , SckDiv } ;
SPI_SCKMODE: Dout < = { 30 'b0 , SckMode } ;
SPI_CSID: Dout < = { 30 'b0 , ChipSelectID } ;
SPI_CSDEF: Dout < = { 28 'b0 , ChipSelectDef } ;
SPI_CSMODE: Dout < = { 30 'b0 , ChipSelectMode } ;
SPI_DELAY0: Dout < = { 8 'b0 , Delay0 [ 15 : 8 ] , 8 'b0 , Delay0 [ 7 : 0 ] } ;
SPI_DELAY1: Dout < = { 8 'b0 , Delay1 [ 15 : 8 ] , 8 'b0 , Delay1 [ 7 : 0 ] } ;
SPI_FMT: Dout < = { 12 'b0 , Format [ 4 : 1 ] , 13 'b0 , Format [ 0 ] , 2 'b0 } ;
SPI_TXDATA: Dout < = { 23 'b0 , TransmitFIFOWriteFull , 8 'b0 } ;
SPI_RXDATA: Dout < = { 23 'b0 , ReceiveFIFOReadEmpty , ReceiveData [ 7 : 0 ] } ;
SPI_TXMARK: Dout < = { 29 'b0 , TransmitWatermark } ;
SPI_RXMARK: Dout < = { 29 'b0 , ReceiveWatermark } ;
SPI_IE: Dout < = { 30 'b0 , InterruptEnable } ;
SPI_IP: Dout < = { 30 'b0 , InterruptPending } ;
default : Dout < = 32 'b0 ;
2023-10-12 20:36:57 +00:00
endcase
end
2023-11-01 17:14:15 +00:00
2023-11-14 21:44:59 +00:00
// SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1))
// Asserts SCLKenable at the rising and falling edge of SCLK by counting from 0 to SckDiv
// Active at 2x SCLK frequency to account for implicit half cycle delays and actions on both clock edges depending on phase
2023-11-01 17:14:15 +00:00
assign SCLKenable = ( DivCounter = = SckDiv ) ;
assign SCLKenableEarly = ( ( DivCounter + 12 'b1 ) = = SckDiv ) ;
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2024-04-21 14:43:01 +00:00
if ( ~ PRESETn ) DivCounter < = '0 ;
else if ( SCLKenable ) DivCounter < = 12 'b0 ;
2023-11-01 17:14:15 +00:00
else DivCounter < = DivCounter + 12 'b1 ;
2023-10-31 00:00:20 +00:00
2023-11-14 21:44:59 +00:00
// Asserts when transmission is one frame before complete
assign ReceivePenultimateFrame = ( ( FrameCount + 4 'b0001 ) = = Format [ 4 : 1 ] ) ;
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// Computing delays
2023-10-12 20:36:57 +00:00
// When sckmode.pha = 0, an extra half-period delay is implicit in the cs-sck delay, and vice-versa for sck-cs
2023-11-10 00:48:11 +00:00
assign ImplicitDelay1 = SckMode [ 0 ] ? 9 'b0 : 9 'b1 ;
assign ImplicitDelay2 = SckMode [ 0 ] ? 9 'b1 : 9 'b0 ;
2023-11-14 21:44:59 +00:00
// Calculate when tx/rx shift registers are full/empty
2024-06-18 12:49:12 +00:00
// Transmit Shift FSM
always_ff @ ( posedge PCLK )
if ( ~ PRESETn ) TransmitShiftEmpty < = 1 'b1 ;
else if ( TransmitShiftEmpty ) begin
if ( TransmitFIFOReadEmpty | ( ~ TransmitFIFOReadEmpty & ( ReceivePenultimateFrame & Active0 ) ) ) TransmitShiftEmpty < = 1 'b1 ;
else if ( ~ TransmitFIFOReadEmpty ) TransmitShiftEmpty < = 1 'b0 ;
end else begin
if ( ReceivePenultimateFrame & Active0 ) TransmitShiftEmpty < = 1 'b1 ;
else TransmitShiftEmpty < = 1 'b0 ;
end
// Receive Shift FSM
always_ff @ ( posedge PCLK )
if ( ~ PRESETn ) ReceiveState < = ReceiveShiftNotFullState ;
else if ( SCLKenable ) begin
case ( ReceiveState )
2024-06-18 15:07:14 +00:00
ReceiveShiftFullState: ReceiveState < = ReceiveShiftNotFullState ;
2024-06-18 12:49:12 +00:00
ReceiveShiftNotFullState: if ( ReceivePenultimateFrame & ( SampleEdge ) ) ReceiveState < = ReceiveShiftDelayState ;
else ReceiveState < = ReceiveShiftNotFullState ;
ReceiveShiftDelayState: ReceiveState < = ReceiveShiftFullState ;
endcase
end
assign ReceiveShiftFull = SckMode [ 0 ] ? ( ReceiveState = = ReceiveShiftFullState ) : ( ReceiveState = = ReceiveShiftDelayState ) ;
2023-10-31 00:00:20 +00:00
2023-11-14 21:44:59 +00:00
// Calculate tx/rx fifo write and recieve increment signals
2023-10-12 20:36:57 +00:00
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2024-04-21 14:43:01 +00:00
if ( ~ PRESETn ) TransmitFIFOWriteIncrement < = 1 'b0 ;
2024-08-20 19:40:50 +00:00
else TransmitFIFOWriteIncrement < = ( Memwrite & ( Entry = = SPI_TXDATA ) & ~ TransmitFIFOWriteFull ) ;
2023-10-12 20:36:57 +00:00
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2024-04-21 14:43:01 +00:00
if ( ~ PRESETn ) ReceiveFIFOReadIncrement < = 1 'b0 ;
2024-08-20 19:40:50 +00:00
else ReceiveFIFOReadIncrement < = ( ( Entry = = SPI_RXDATA ) & ~ ReceiveFIFOReadEmpty & PSEL & ~ ReceiveFIFOReadIncrement ) ;
2023-11-01 17:14:15 +00:00
2023-11-14 21:44:59 +00:00
// Tx/Rx FIFOs
2024-06-18 12:49:12 +00:00
spi_fifo # ( 3 , 8 ) txFIFO ( PCLK , 1 'b1 , SCLKenable , PRESETn , TransmitFIFOWriteIncrement , TransmitShiftEmpty , TransmitData [ 7 : 0 ] , TransmitWriteWatermarkLevel , TransmitWatermark [ 2 : 0 ] ,
2023-11-14 21:44:59 +00:00
TransmitFIFOReadData [ 7 : 0 ] , TransmitFIFOWriteFull , TransmitFIFOReadEmpty , TransmitWriteMark , TransmitReadMark ) ;
2024-06-18 12:49:12 +00:00
spi_fifo # ( 3 , 8 ) rxFIFO ( PCLK , SCLKenable , 1 'b1 , PRESETn , ReceiveShiftFullDelay , ReceiveFIFOReadIncrement , ReceiveShiftRegEndian , ReceiveWatermark [ 2 : 0 ] , ReceiveReadWatermarkLevel ,
2023-11-14 21:44:59 +00:00
ReceiveData [ 7 : 0 ] , ReceiveFIFOWriteFull , ReceiveFIFOReadEmpty , RecieveWriteMark , RecieveReadMark ) ;
2023-10-31 00:00:20 +00:00
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2024-04-21 14:43:01 +00:00
if ( ~ PRESETn ) TransmitFIFOReadEmptyDelay < = 1 'b1 ;
2023-10-31 00:00:20 +00:00
else if ( SCLKenable ) TransmitFIFOReadEmptyDelay < = TransmitFIFOReadEmpty ;
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2024-04-21 14:43:01 +00:00
if ( ~ PRESETn ) ReceiveShiftFullDelay < = 1 'b0 ;
2023-10-31 00:00:20 +00:00
else if ( SCLKenable ) ReceiveShiftFullDelay < = ReceiveShiftFull ;
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2024-04-21 14:43:01 +00:00
if ( ~ PRESETn ) ReceiveShiftFullDelayPCLK < = 1 'b0 ;
2023-10-31 00:00:20 +00:00
else if ( SCLKenableEarly ) ReceiveShiftFullDelayPCLK < = ReceiveShiftFull ;
assign TransmitShiftRegLoad = ~ TransmitShiftEmpty & ~ Active | ( ( ( ChipSelectMode = = 2 'b10 ) & ~ | ( Delay1 [ 15 : 8 ] ) ) & ( ( ReceiveShiftFullDelay | ReceiveShiftFull ) & ~ SampleEdge & ~ TransmitFIFOReadEmpty ) ) ;
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// Main FSM which controls SPI transmission
2023-10-12 20:36:57 +00:00
typedef enum logic [ 2 : 0 ] { CS_INACTIVE , DELAY_0 , ACTIVE_0 , ACTIVE_1 , DELAY_1 , INTER_CS , INTER_XFR } statetype ;
statetype state ;
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2023-11-14 21:44:59 +00:00
if ( ~ PRESETn ) begin
state < = CS_INACTIVE ;
2024-08-20 19:40:50 +00:00
FrameCount < = 4 'b0 ;
SPICLK < = SckMode [ 1 ] ;
2023-10-31 00:00:20 +00:00
end else if ( SCLKenable ) begin
2023-11-14 21:44:59 +00:00
/* verilator lint_off CASEINCOMPLETE */
2023-10-12 20:36:57 +00:00
case ( state )
CS_INACTIVE: begin
2023-11-10 00:48:11 +00:00
CS_SCKCount < = 9 'b1 ;
SCK_CSCount < = 9 'b10 ;
FrameCount < = 4 'b0 ;
2023-10-12 20:36:57 +00:00
InterCSCount < = 9 'b10 ;
InterXFRCount < = 9 'b1 ;
if ( ( ~ TransmitFIFOReadEmpty | ~ TransmitShiftEmpty ) & ( ( | ( Delay0 [ 7 : 0 ] ) ) | ~ SckMode [ 0 ] ) ) state < = DELAY_0 ;
2024-08-20 19:40:50 +00:00
else if ( ( ~ TransmitFIFOReadEmpty | ~ TransmitShiftEmpty ) ) begin
state < = ACTIVE_0 ;
SPICLK < = ~ SckMode [ 1 ] ;
2024-08-20 21:24:37 +00:00
end else SPICLK < = SckMode [ 1 ] ;
2023-10-12 20:36:57 +00:00
end
DELAY_0: begin
2023-11-10 00:48:11 +00:00
CS_SCKCount < = CS_SCKCount + 9 'b1 ;
2024-08-20 19:40:50 +00:00
if ( CS_SCKCount > = ( ( { Delay0 [ 7 : 0 ] , 1 'b0 } ) + ImplicitDelay1 ) ) begin
state < = ACTIVE_0 ;
SPICLK < = ~ SckMode [ 1 ] ;
end
2023-10-12 20:36:57 +00:00
end
ACTIVE_0: begin
2023-11-10 00:48:11 +00:00
FrameCount < = FrameCount + 4 'b1 ;
2024-08-20 19:40:50 +00:00
SPICLK < = SckMode [ 1 ] ;
2023-10-12 20:36:57 +00:00
state < = ACTIVE_1 ;
end
ACTIVE_1: begin
InterXFRCount < = 9 'b1 ;
2024-08-20 19:40:50 +00:00
if ( FrameCount < Format [ 4 : 1 ] ) begin
state < = ACTIVE_0 ;
SPICLK < = ~ SckMode [ 1 ] ;
end
2023-10-12 20:36:57 +00:00
else if ( ( ChipSelectMode [ 1 : 0 ] = = 2 'b10 ) & ~ | ( Delay1 [ 15 : 8 ] ) & ( ~ TransmitFIFOReadEmpty ) ) begin
state < = ACTIVE_0 ;
2024-08-20 19:40:50 +00:00
SPICLK < = ~ SckMode [ 1 ] ;
2023-11-10 00:48:11 +00:00
CS_SCKCount < = 9 'b1 ;
SCK_CSCount < = 9 'b10 ;
FrameCount < = 4 'b0 ;
2023-10-12 20:36:57 +00:00
InterCSCount < = 9 'b10 ;
end
else if ( ChipSelectMode [ 1 : 0 ] = = 2 'b10 ) state < = INTER_XFR ;
else if ( ~ | ( Delay0 [ 15 : 8 ] ) & ( ~ SckMode [ 0 ] ) ) state < = INTER_CS ;
else state < = DELAY_1 ;
end
DELAY_1: begin
2023-11-10 00:48:11 +00:00
SCK_CSCount < = SCK_CSCount + 9 'b1 ;
2023-11-14 21:44:59 +00:00
if ( SCK_CSCount > = ( ( { Delay0 [ 15 : 8 ] , 1 'b0 } ) + ImplicitDelay2 ) ) state < = INTER_CS ;
2023-10-12 20:36:57 +00:00
end
INTER_CS: begin
InterCSCount < = InterCSCount + 9 'b1 ;
2024-08-20 21:24:37 +00:00
SPICLK < = SckMode [ 1 ] ;
2023-11-14 21:44:59 +00:00
if ( InterCSCount > = ( { Delay1 [ 7 : 0 ] , 1 'b0 } ) ) state < = CS_INACTIVE ;
2023-10-12 20:36:57 +00:00
end
INTER_XFR: begin
2023-11-10 00:48:11 +00:00
CS_SCKCount < = 9 'b1 ;
SCK_CSCount < = 9 'b10 ;
FrameCount < = 4 'b0 ;
2023-10-12 20:36:57 +00:00
InterCSCount < = 9 'b10 ;
InterXFRCount < = InterXFRCount + 9 'b1 ;
2024-08-20 19:40:50 +00:00
if ( ( InterXFRCount > = ( { Delay1 [ 15 : 8 ] , 1 'b0 } ) ) & ~ TransmitFIFOReadEmptyDelay ) begin
state < = ACTIVE_0 ;
SPICLK < = ~ SckMode [ 1 ] ;
end else if ( ~ | ChipSelectMode [ 1 : 0 ] ) state < = CS_INACTIVE ;
2024-08-20 21:24:37 +00:00
else SPICLK < = SckMode [ 1 ] ;
2023-10-12 20:36:57 +00:00
end
endcase
2023-11-14 21:44:59 +00:00
/* verilator lint_off CASEINCOMPLETE */
2023-10-12 20:36:57 +00:00
end
2023-10-31 00:00:20 +00:00
2023-11-14 21:44:59 +00:00
2023-10-12 20:36:57 +00:00
2023-11-01 08:26:34 +00:00
assign DelayMode = SckMode [ 0 ] ? ( state = = DELAY_1 ) : ( state = = ACTIVE_1 & ReceiveShiftFull ) ;
assign ChipSelectInternal = ( state = = CS_INACTIVE | state = = INTER_CS | DelayMode & ~ | ( Delay0 [ 15 : 8 ] ) ) ? ChipSelectDef : ~ ChipSelectDef ;
2023-10-12 20:36:57 +00:00
assign Active = ( state = = ACTIVE_0 | state = = ACTIVE_1 ) ;
2023-10-31 00:00:20 +00:00
assign SampleEdge = SckMode [ 0 ] ? ( state = = ACTIVE_1 ) : ( state = = ACTIVE_0 ) ;
assign ZeroDelayHoldMode = ( ( ChipSelectMode = = 2 'b10 ) & ( ~ | ( Delay1 [ 7 : 4 ] ) ) ) ;
2024-08-20 21:24:37 +00:00
assign TransmitInactive = ( ( state = = INTER_CS ) | ( state = = CS_INACTIVE ) | ( state = = INTER_XFR ) | ( ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode ) | ( ( state = = ACTIVE_1 ) & ( ( ChipSelectMode [ 1 : 0 ] = = 2 'b10 ) & ~ | ( Delay1 [ 15 : 8 ] ) & ( ~ TransmitFIFOReadEmpty ) & ( FrameCount = = Format [ 4 : 1 ] ) ) ) ) ;
2023-10-12 20:36:57 +00:00
assign Active0 = ( state = = ACTIVE_0 ) ;
2024-08-02 20:33:57 +00:00
// Signal tracks which edge of sck to shift data
2023-10-12 20:36:57 +00:00
always_comb
case ( SckMode [ 1 : 0 ] )
2024-08-02 20:33:57 +00:00
2 'b00 : ShiftEdge = SPICLK & SCLKenable ;
2024-08-20 21:38:15 +00:00
2 'b01 : ShiftEdge = ( ~ SPICLK & ( | ( FrameCount ) | ( CS_SCKCount > = ( ( { Delay0 [ 7 : 0 ] , 1 'b0 } ) + ImplicitDelay1 ) ) ) & SCLKenable & ( FrameCount ! = Format [ 4 : 1 ] ) & ~ TransmitInactive ) ;
2 'b10 : ShiftEdge = ~ SPICLK & SCLKenable ;
2 'b11 : ShiftEdge = ( SPICLK & ( | ( FrameCount ) | ( CS_SCKCount > = ( ( { Delay0 [ 7 : 0 ] , 1 'b0 } ) + ImplicitDelay1 ) ) ) & SCLKenable & ( FrameCount ! = Format [ 4 : 1 ] ) & ~ TransmitInactive ) ;
2024-07-22 17:36:39 +00:00
default : ShiftEdge = SPICLK & SCLKenable ;
2023-10-12 20:36:57 +00:00
endcase
2023-11-14 21:44:59 +00:00
// Transmit shift register
assign TransmitDataEndian = Format [ 0 ] ? { TransmitFIFOReadData [ 0 ] , TransmitFIFOReadData [ 1 ] , TransmitFIFOReadData [ 2 ] , TransmitFIFOReadData [ 3 ] , TransmitFIFOReadData [ 4 ] , TransmitFIFOReadData [ 5 ] , TransmitFIFOReadData [ 6 ] , TransmitFIFOReadData [ 7 ] } : TransmitFIFOReadData [ 7 : 0 ] ;
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2024-08-20 21:38:15 +00:00
if ( ~ PRESETn ) TransmitShiftReg < = 8 'b0 ;
2023-11-01 17:14:15 +00:00
else if ( TransmitShiftRegLoad ) TransmitShiftReg < = TransmitDataEndian ;
2024-08-20 21:38:15 +00:00
else if ( ShiftEdge & Active ) TransmitShiftReg < = { TransmitShiftReg [ 6 : 0 ] , TransmitShiftReg [ 0 ] } ;
2023-10-31 00:00:20 +00:00
2023-11-01 17:14:15 +00:00
assign SPIOut = TransmitShiftReg [ 7 ] ;
2023-11-14 21:44:59 +00:00
// If in loopback mode, receive shift register is connected directly to module's output pins. Else, connected to SPIIn
// There are no setup/hold time issues because transmit shift register and receive shift register always shift/sample on opposite edges
2023-11-10 00:48:11 +00:00
assign ShiftIn = P . SPI_LOOPBACK_TEST ? SPIOut : SPIIn ;
2023-10-31 19:27:41 +00:00
2023-11-14 21:44:59 +00:00
// Receive shift register
2024-06-18 12:49:12 +00:00
always_ff @ ( posedge PCLK )
2023-10-12 20:36:57 +00:00
if ( ~ PRESETn ) ReceiveShiftReg < = 8 'b0 ;
2023-10-31 00:00:20 +00:00
else if ( SampleEdge & SCLKenable ) begin
2023-11-14 21:44:59 +00:00
if ( ~ Active ) ReceiveShiftReg < = 8 'b0 ;
else ReceiveShiftReg < = { ReceiveShiftReg [ 6 : 0 ] , ShiftIn } ;
2023-10-12 20:36:57 +00:00
end
2023-11-14 21:44:59 +00:00
// Aligns received data and reverses if little-endian
2023-11-01 17:14:15 +00:00
assign LeftShiftAmount = 4 'h8 - Format [ 4 : 1 ] ;
assign ASR = ReceiveShiftReg < < LeftShiftAmount [ 2 : 0 ] ;
assign ReceiveShiftRegEndian = Format [ 0 ] ? { ASR [ 0 ] , ASR [ 1 ] , ASR [ 2 ] , ASR [ 3 ] , ASR [ 4 ] , ASR [ 5 ] , ASR [ 6 ] , ASR [ 7 ] } : ASR [ 7 : 0 ] ;
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// Interrupt logic: raise interrupt if any enabled interrupts are pending
2023-10-12 20:36:57 +00:00
assign SPIIntr = | ( InterruptPending & InterruptEnable ) ;
2023-11-14 21:44:59 +00:00
// Chip select logic
2023-10-12 20:36:57 +00:00
always_comb
case ( ChipSelectID [ 1 : 0 ] )
2 'b00 : ChipSelectAuto = { ChipSelectDef [ 3 ] , ChipSelectDef [ 2 ] , ChipSelectDef [ 1 ] , ChipSelectInternal [ 0 ] } ;
2 'b01 : ChipSelectAuto = { ChipSelectDef [ 3 ] , ChipSelectDef [ 2 ] , ChipSelectInternal [ 1 ] , ChipSelectDef [ 0 ] } ;
2 'b10 : ChipSelectAuto = { ChipSelectDef [ 3 ] , ChipSelectInternal [ 2 ] , ChipSelectDef [ 1 ] , ChipSelectDef [ 0 ] } ;
2 'b11 : ChipSelectAuto = { ChipSelectInternal [ 3 ] , ChipSelectDef [ 2 ] , ChipSelectDef [ 1 ] , ChipSelectDef [ 0 ] } ;
endcase
2023-11-01 17:14:15 +00:00
assign SPICS = ChipSelectMode [ 0 ] ? ChipSelectDef : ChipSelectAuto ;
2023-10-12 20:36:57 +00:00
endmodule