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 ,
output logic SPIIntr
) ;
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 ;
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 TransmitFIFOReadIncrement ;
logic TransmitFIFOWriteIncrement ;
logic ReceiveFIFOReadIncrement ;
2023-10-12 20:36:57 +00:00
logic ReceiveFIFOWriteFull , ReceiveFIFOReadEmpty ;
logic [ 7 : 0 ] TransmitFIFOReadData , ReceiveFIFOWriteData ;
logic [ 2 : 0 ] TransmitWriteWatermarkLevel , ReceiveReadWatermarkLevel ;
2023-11-14 21:44:59 +00:00
logic [ 7 : 0 ] ReceiveShiftRegEndian ; // Reverses ReceiveShiftReg if Format[2] set (little endian transmission)
2023-10-12 20:36:57 +00:00
2023-11-14 21:44:59 +00:00
// Transmission signals
2023-10-12 20:36:57 +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 [ 3 : 0 ] ReceivePenultimateFrameCount ; // Counter
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
assign PREADY = 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 ] ;
if ( P . XLEN = = 64 ) assign PRDATA = { Dout , Dout } ;
else assign PRDATA = Dout ;
2023-11-14 21:44:59 +00:00
// Register access
2023-10-12 20:36:57 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) begin
SckDiv < = # 1 12 'd3 ;
SckMode < = # 1 2 'b0 ;
ChipSelectID < = # 1 2 'b0 ;
ChipSelectDef < = # 1 4 'b1111 ;
ChipSelectMode < = # 1 0 ;
Delay0 < = # 1 { 8 'b1 , 8 'b1 } ;
Delay1 < = # 1 { 8 'b0 , 8 'b1 } ;
2023-11-01 17:14:15 +00:00
Format < = # 1 { 5 'b10000 } ;
2023-10-12 20:36:57 +00:00
TransmitData < = # 1 9 'b0 ;
TransmitWatermark < = # 1 3 'b0 ;
ReceiveWatermark < = # 1 3 'b0 ;
InterruptEnable < = # 1 2 'b0 ;
InterruptPending < = # 1 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
2023-11-01 08:26:34 +00:00
8 'h00 : SckDiv < = Din [ 11 : 0 ] ;
8 'h04 : SckMode < = Din [ 1 : 0 ] ;
8 'h10 : ChipSelectID < = Din [ 1 : 0 ] ;
8 'h14 : ChipSelectDef < = Din [ 3 : 0 ] ;
8 'h18 : ChipSelectMode < = Din [ 1 : 0 ] ;
8 'h28 : Delay0 < = { Din [ 23 : 16 ] , Din [ 7 : 0 ] } ;
8 'h2C : Delay1 < = { Din [ 23 : 16 ] , Din [ 7 : 0 ] } ;
2023-11-01 17:14:15 +00:00
8 'h40 : Format < = { Din [ 19 : 16 ] , Din [ 2 ] } ;
2023-10-12 20:36:57 +00:00
8 'h48 : if ( ~ TransmitFIFOWriteFull ) TransmitData [ 7 : 0 ] < = Din [ 7 : 0 ] ;
2023-11-01 08:26:34 +00:00
8 'h50 : TransmitWatermark < = Din [ 2 : 0 ] ;
8 'h54 : ReceiveWatermark < = Din [ 2 : 0 ] ;
8 'h70 : InterruptEnable < = Din [ 1 : 0 ] ;
2023-10-12 20:36:57 +00:00
endcase
/* 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
2023-10-13 21:22:32 +00:00
8 'h00 : Dout < = # 1 { 20 'b0 , SckDiv } ;
8 'h04 : Dout < = # 1 { 30 'b0 , SckMode } ;
8 'h10 : Dout < = # 1 { 30 'b0 , ChipSelectID } ;
8 'h14 : Dout < = # 1 { 28 'b0 , ChipSelectDef } ;
8 'h18 : Dout < = # 1 { 30 'b0 , ChipSelectMode } ;
2023-11-14 21:44:59 +00:00
8 'h28 : Dout < = # 1 { 8 'b0 , Delay0 [ 15 : 8 ] , 8 'b0 , Delay0 [ 7 : 0 ] } ;
8 'h2C : Dout < = # 1 { 8 'b0 , Delay1 [ 15 : 8 ] , 8 'b0 , Delay1 [ 7 : 0 ] } ;
8 'h40 : Dout < = # 1 { 12 'b0 , Format [ 4 : 1 ] , 13 'b0 , Format [ 0 ] , 2 'b0 } ;
2023-10-13 21:22:32 +00:00
8 'h48 : Dout < = # 1 { 23 'b0 , TransmitFIFOWriteFull , 8 'b0 } ;
8 'h4C : Dout < = # 1 { 23 'b0 , ReceiveFIFOReadEmpty , ReceiveData [ 7 : 0 ] } ;
8 'h50 : Dout < = # 1 { 29 'b0 , TransmitWatermark } ;
8 'h54 : Dout < = # 1 { 29 'b0 , ReceiveWatermark } ;
8 'h70 : Dout < = # 1 { 30 'b0 , InterruptEnable } ;
8 'h74 : Dout < = # 1 { 30 'b0 , InterruptPending } ;
2023-10-12 20:36:57 +00:00
default : Dout < = # 1 32 'b0 ;
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 ) ;
2023-10-31 00:00:20 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) DivCounter < = # 1 0 ;
else if ( SCLKenable ) DivCounter < = 0 ;
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
TransmitShiftFSM TransmitShiftFSM ( PCLK , PRESETn , TransmitFIFOReadEmpty , ReceivePenultimateFrame , Active0 , TransmitShiftEmpty ) ;
ReceiveShiftFSM ReceiveShiftFSM ( PCLK , PRESETn , SCLKenable , ReceivePenultimateFrame , SampleEdge , SckMode [ 0 ] , ReceiveShiftFull ) ;
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
2023-10-31 00:00:20 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
2023-11-14 21:44:59 +00:00
if ( ~ PRESETn ) TransmitFIFOWriteIncrement < = 0 ;
else TransmitFIFOWriteIncrement < = ( Memwrite & ( Entry = = 8 'h48 ) & ~ TransmitFIFOWriteFull & TransmitInactive ) ;
2023-10-12 20:36:57 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
2023-10-31 00:00:20 +00:00
if ( ~ PRESETn ) ReceiveFIFOReadIncrement < = 0 ;
2023-11-01 17:14:15 +00:00
else ReceiveFIFOReadIncrement < = ( ( Entry = = 8 'h4C ) & ~ ReceiveFIFOReadEmpty & PSEL & ~ ReceiveFIFOReadIncrement ) ;
2023-11-14 21:44:59 +00:00
// Tx/Rx FIFOs
SynchFIFO # ( 3 , 8 ) txFIFO ( PCLK , 1 'b1 , SCLKenable , PRESETn , TransmitFIFOWriteIncrement , TransmitShiftEmpty , TransmitData [ 7 : 0 ] , TransmitWriteWatermarkLevel , TransmitWatermark [ 2 : 0 ] ,
TransmitFIFOReadData [ 7 : 0 ] , TransmitFIFOWriteFull , TransmitFIFOReadEmpty , TransmitWriteMark , TransmitReadMark ) ;
SynchFIFO # ( 3 , 8 ) rxFIFO ( PCLK , SCLKenable , 1 'b1 , PRESETn , ReceiveShiftFullDelay , ReceiveFIFOReadIncrement , ReceiveShiftRegEndian , ReceiveWatermark [ 2 : 0 ] , ReceiveReadWatermarkLevel ,
ReceiveData [ 7 : 0 ] , ReceiveFIFOWriteFull , ReceiveFIFOReadEmpty , RecieveWriteMark , RecieveReadMark ) ;
2023-10-31 00:00:20 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) TransmitFIFOReadEmptyDelay < = 1 ;
else if ( SCLKenable ) TransmitFIFOReadEmptyDelay < = TransmitFIFOReadEmpty ;
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) ReceiveShiftFullDelay < = 0 ;
else if ( SCLKenable ) ReceiveShiftFullDelay < = ReceiveShiftFull ;
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) ReceiveShiftFullDelayPCLK < = 0 ;
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 ;
always_ff @ ( posedge PCLK , negedge PRESETn )
2023-11-14 21:44:59 +00:00
if ( ~ PRESETn ) begin
state < = CS_INACTIVE ;
2023-11-10 00:52:55 +00:00
FrameCount < = 4 'b0 ;
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 ;
else if ( ( ~ TransmitFIFOReadEmpty | ~ TransmitShiftEmpty ) ) state < = ACTIVE_0 ;
end
DELAY_0: begin
2023-11-10 00:48:11 +00:00
CS_SCKCount < = CS_SCKCount + 9 'b1 ;
2023-11-14 21:44:59 +00:00
if ( CS_SCKCount > = ( ( { Delay0 [ 7 : 0 ] , 1 'b0 } ) + ImplicitDelay1 ) ) state < = ACTIVE_0 ;
2023-10-12 20:36:57 +00:00
end
ACTIVE_0: begin
2023-11-10 00:48:11 +00:00
FrameCount < = FrameCount + 4 'b1 ;
2023-10-12 20:36:57 +00:00
state < = ACTIVE_1 ;
end
ACTIVE_1: begin
InterXFRCount < = 9 'b1 ;
2023-11-14 21:44:59 +00:00
if ( FrameCount < Format [ 4 : 1 ] ) state < = ACTIVE_0 ;
2023-10-12 20:36:57 +00:00
else if ( ( ChipSelectMode [ 1 : 0 ] = = 2 'b10 ) & ~ | ( Delay1 [ 15 : 8 ] ) & ( ~ TransmitFIFOReadEmpty ) ) begin
state < = ACTIVE_0 ;
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 ;
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 ;
2023-11-14 21:44:59 +00:00
if ( ( InterXFRCount > = ( { Delay1 [ 15 : 8 ] , 1 'b0 } ) ) & ~ TransmitFIFOReadEmptyDelay ) state < = ACTIVE_0 ;
2023-10-12 20:36:57 +00:00
else if ( ~ | ChipSelectMode [ 1 : 0 ] ) state < = CS_INACTIVE ;
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 sck = ( state = = ACTIVE_0 ) ? ~ SckMode [ 1 ] : SckMode [ 1 ] ;
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 ] ) ) ) ;
assign TransmitInactive = ( ( state = = INTER_CS ) | ( state = = CS_INACTIVE ) | ( state = = INTER_XFR ) | ( ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode ) ) ;
2023-10-12 20:36:57 +00:00
assign Active0 = ( state = = ACTIVE_0 ) ;
2023-11-14 21:44:59 +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 ] )
2023-11-10 00:48:11 +00:00
2 'b00 : ShiftEdge = ~ sck & SCLKenable ;
2 'b01 : ShiftEdge = ( sck & | ( FrameCount ) & SCLKenable ) ;
2 'b10 : ShiftEdge = sck & SCLKenable ;
2 'b11 : ShiftEdge = ( ~ sck & | ( FrameCount ) & SCLKenable ) ;
default : ShiftEdge = sck & 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 ] ;
2023-10-12 20:36:57 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
2023-11-01 17:14:15 +00:00
if ( ~ PRESETn ) TransmitShiftReg < = 8 'b0 ;
else if ( TransmitShiftRegLoad ) TransmitShiftReg < = TransmitDataEndian ;
2023-11-14 21:44:59 +00:00
else if ( ShiftEdge & Active ) TransmitShiftReg < = { TransmitShiftReg [ 6 : 0 ] , 1 'b0 } ;
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
2023-10-12 20:36:57 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
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
2023-11-01 17:14:15 +00:00
2023-11-14 21:44:59 +00:00
module SynchFIFO # ( parameter M = 3 , N = 8 ) ( // 2^M entries of N bits each
input logic PCLK , wen , ren , PRESETn ,
input logic winc , rinc ,
input logic [ N - 1 : 0 ] wdata ,
input logic [ M - 1 : 0 ] wwatermarklevel , rwatermarklevel ,
2023-10-12 20:36:57 +00:00
output logic [ N - 1 : 0 ] rdata ,
2023-11-14 21:44:59 +00:00
output logic wfull , rempty ,
output logic wwatermark , rwatermark ) ;
2023-10-13 21:22:32 +00:00
2023-11-10 00:48:11 +00:00
/ * Pointer FIFO using design elements from " Simulation and Synthesis Techniques
for Asynchronous FIFO Design " by Clifford E. Cummings. Namely, M bit read and write pointers
are an extra bit larger than address size to determine full / empty conditions .
Watermark comparisons use 2 ' s complement subtraction between the M - 1 bit pointers ,
which are also used to address memory
*/
2023-10-13 21:22:32 +00:00
logic [ N - 1 : 0 ] mem [ 2 * * M ] ;
2023-11-02 22:42:28 +00:00
logic [ M: 0 ] rptr , wptr ;
2023-10-17 05:57:02 +00:00
logic [ M: 0 ] rptrnext , wptrnext ;
2023-10-13 21:22:32 +00:00
logic [ M - 1 : 0 ] raddr ;
logic [ M - 1 : 0 ] waddr ;
2023-11-10 00:48:11 +00:00
2023-10-13 21:22:32 +00:00
assign rdata = mem [ raddr ] ;
2023-10-17 05:57:02 +00:00
always_ff @ ( posedge PCLK )
if ( winc & ~ wfull ) mem [ waddr ] < = wdata ;
2023-10-13 21:22:32 +00:00
2023-11-01 17:14:15 +00:00
// write and read are enabled
2023-10-17 05:57:02 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
2023-11-01 08:26:34 +00:00
if ( ~ PRESETn ) begin
rptr < = 0 ;
wptr < = 0 ;
wfull < = 1 'b0 ;
rempty < = 1 'b1 ;
end
else begin
2023-11-01 17:14:15 +00:00
if ( wen ) begin
2023-11-14 21:44:59 +00:00
wfull < = ( { ~ wptrnext [ M ] , wptrnext [ M - 1 : 0 ] } = = rptr ) ;
2023-11-01 17:14:15 +00:00
wptr < = wptrnext ;
end
if ( ren ) begin
2023-11-01 08:26:34 +00:00
rptr < = rptrnext ;
2023-11-14 21:44:59 +00:00
rempty < = ( wptr = = rptrnext ) ;
2023-11-01 08:26:34 +00:00
end
end
2023-11-14 21:44:59 +00:00
2023-10-17 05:57:02 +00:00
assign raddr = rptr [ M - 1 : 0 ] ;
2023-11-14 21:44:59 +00:00
assign rptrnext = rptr + { { ( M ) { 1 'b0 } } , ( rinc & ~ rempty ) } ;
2023-11-08 01:59:46 +00:00
assign rwatermark = ( ( waddr - raddr ) < rwatermarklevel ) & ~ wfull ;
2023-10-17 05:57:02 +00:00
assign waddr = wptr [ M - 1 : 0 ] ;
2023-11-08 01:59:46 +00:00
assign wwatermark = ( ( waddr - raddr ) > wwatermarklevel ) | wfull ;
2023-11-14 21:44:59 +00:00
assign wptrnext = wptr + { { ( M ) { 1 'b0 } } , ( winc & ~ wfull ) } ;
2023-10-12 20:36:57 +00:00
endmodule
module TransmitShiftFSM (
2023-11-14 21:44:59 +00:00
input logic PCLK , PRESETn ,
input logic TransmitFIFOReadEmpty , ReceivePenultimateFrame , Active0 ,
2023-10-12 20:36:57 +00:00
output logic TransmitShiftEmpty ) ;
always_ff @ ( posedge PCLK , negedge PRESETn )
2023-11-14 21:44:59 +00:00
if ( ~ PRESETn ) TransmitShiftEmpty < = 1 ;
else if ( TransmitShiftEmpty ) begin
if ( TransmitFIFOReadEmpty | ( ~ TransmitFIFOReadEmpty & ( ReceivePenultimateFrame & Active0 ) ) ) TransmitShiftEmpty < = 1 ;
else if ( ~ TransmitFIFOReadEmpty ) TransmitShiftEmpty < = 0 ;
end else begin
if ( ReceivePenultimateFrame & Active0 ) TransmitShiftEmpty < = 1 ;
else TransmitShiftEmpty < = 0 ;
end
2023-10-12 20:36:57 +00:00
endmodule
module ReceiveShiftFSM (
2023-11-14 21:44:59 +00:00
input logic PCLK , PRESETn , SCLKenable ,
input logic ReceivePenultimateFrame , SampleEdge , SckMode ,
2023-10-12 20:36:57 +00:00
output logic ReceiveShiftFull
) ;
typedef enum logic [ 1 : 0 ] { ReceiveShiftFullState , ReceiveShiftNotFullState , ReceiveShiftDelayState } statetype ;
statetype ReceiveState , ReceiveNextState ;
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) ReceiveState < = ReceiveShiftNotFullState ;
2023-10-31 00:00:20 +00:00
else if ( SCLKenable ) begin
2023-10-12 20:36:57 +00:00
case ( ReceiveState )
ReceiveShiftFullState: ReceiveState < = ReceiveShiftNotFullState ;
2023-11-14 21:44:59 +00:00
ReceiveShiftNotFullState: if ( ReceivePenultimateFrame & ( SampleEdge ) ) ReceiveState < = ReceiveShiftDelayState ;
2023-10-12 20:36:57 +00:00
else ReceiveState < = ReceiveShiftNotFullState ;
2023-11-14 21:44:59 +00:00
ReceiveShiftDelayState: ReceiveState < = ReceiveShiftFullState ;
2023-10-12 20:36:57 +00:00
endcase
end
2023-11-14 21:44:59 +00:00
assign ReceiveShiftFull = SckMode ? ( ReceiveState = = ReceiveShiftFullState ) : ( ReceiveState = = ReceiveShiftDelayState ) ;
2023-10-12 20:36:57 +00:00
endmodule