Merge pull request #476 from naichewa/main

Final SPI code review
This commit is contained in:
David Harris 2023-11-14 19:10:00 -08:00 committed by GitHub
commit 77338435ce

View File

@ -2,10 +2,14 @@
// spi_apb.sv // spi_apb.sv
// //
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu 11/16/2022 // Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu 11/16/2022
// //
// Purpose: SPI peripheral // Purpose: SPI peripheral
// See FU540-C000-v1.0 for specifications //
// 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
// //
// A component of the Wally configurable RISC-V project. // A component of the Wally configurable RISC-V project.
// //
@ -25,19 +29,6 @@
// and limitations under the License. // and limitations under the License.
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
// Current limitations: Flash read sequencer mode not implemented, dual and quad modes untestable with current test plan.
// Attempt to move from >= comparisons by initializing in FSM differently
// Parameterize SynchFIFO
// look at ReadIncrement/WriteIncrement delay necessity
/*
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.
*/
module spi_apb import cvw::*; #(parameter cvw_t P) ( module spi_apb import cvw::*; #(parameter cvw_t P) (
input logic PCLK, PRESETn, input logic PCLK, PRESETn,
input logic PSEL, input logic PSEL,
@ -54,27 +45,27 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
output logic SPIIntr output logic SPIIntr
); );
//SPI control registers. Refer to SiFive FU540-C000 manual // SPI control registers. Refer to SiFive FU540-C000 manual
logic [11:0] SckDiv; logic [11:0] SckDiv;
logic [1:0] SckMode; logic [1:0] SckMode;
logic [1:0] ChipSelectID; logic [1:0] ChipSelectID;
logic [3:0] ChipSelectDef; logic [3:0] ChipSelectDef;
logic [1:0] ChipSelectMode; logic [1:0] ChipSelectMode;
logic [15:0] Delay0, Delay1; logic [15:0] Delay0, Delay1;
logic [4:0] Format; logic [4:0] Format;
logic [7:0] ReceiveData; logic [7:0] ReceiveData;
logic [2:0] TransmitWatermark, ReceiveWatermark; logic [2:0] TransmitWatermark, ReceiveWatermark;
logic [8:0] TransmitData; logic [8:0] TransmitData;
logic [1:0] InterruptEnable, InterruptPending; logic [1:0] InterruptEnable, InterruptPending;
//Bus interface signals // Bus interface signals
logic [7:0] Entry; logic [7:0] Entry;
logic Memwrite; logic Memwrite;
logic [31:0] Din, Dout; logic [31:0] Din, Dout;
logic TransmitInactive; //High when there is no transmission, used as hardware interlock signal logic TransmitInactive; // High when there is no transmission, used as hardware interlock signal
//FIFO FSM signals // FIFO FSM signals
//Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1] // Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1]
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark; logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty; logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty;
logic TransmitFIFOReadIncrement; logic TransmitFIFOReadIncrement;
@ -83,75 +74,68 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty; logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty;
logic [7:0] TransmitFIFOReadData, ReceiveFIFOWriteData; logic [7:0] TransmitFIFOReadData, ReceiveFIFOWriteData;
logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel; logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel;
logic [7:0] ReceiveShiftRegEndian; //reverses ReceiveShiftReg if Format[2] set (little endian transmission) logic [7:0] ReceiveShiftRegEndian; // Reverses ReceiveShiftReg if Format[2] set (little endian transmission)
//Transmission signals // Transmission signals
logic sck; logic sck;
logic [11:0] DivCounter; //counter for sck logic [11:0] DivCounter; // Counter for sck
logic SCLKenable; //flip flop enable high every sclk edge logic SCLKenable; // Flip flop enable high every sclk edge
//Delay signals // Delay signals
logic [8:0] ImplicitDelay1; //Adds implicit delay to cs-sck delay counter based on phase 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] 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] CS_SCKCount; // Counter for cs-sck delay
logic [8:0] SCK_CSCount; //Counter for sck-cs delay logic [8:0] SCK_CSCount; // Counter for sck-cs delay
logic [8:0] InterCSCount; //Counter for inter cs delay logic [8:0] InterCSCount; // Counter for inter cs delay
logic [8:0] InterXFRCount; //Counter for inter xfr delay logic [8:0] InterXFRCount; // Counter for inter xfr delay
logic CS_SCKCompare; //Boolean comparison signal, high when CS_SCKCount >= cs-sck delay logic ZeroDelayHoldMode; // High when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
logic SCK_CSCompare; //Boolean comparison signal, high when SCK_CSCount >= sck-cs delay
logic InterCSCompare; //Boolean comparison signal, high when InterCSCount >= inter cs delay
logic InterXFRCompare; //Boolean comparison signal, high when InterXFRCount >= inter xfr delay
logic ZeroDelayHoldMode; //High when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
//Frame counting signals // Frame counting signals
logic [3:0] FrameCount; //Counter for number of frames in transmission logic [3:0] FrameCount; // Counter for number of frames in transmission
logic FrameCompare; //Boolean comparison signal, high when FrameCount = Format[7:4] logic [3:0] ReceivePenultimateFrameCount; // Counter
logic [3:0] ReceivePenultimateFrame; //Frame number - 1 logic ReceivePenultimateFrame; // High when penultimate frame in transmission has been reached
logic [3:0] ReceivePenultimateFrameCount; //Counter
logic ReceivePenultimateFrameBoolean; //High when penultimate frame in transmission has been reached
//State fsm signals // State fsm signals
logic Active; //High when state is either Active1 or Active0 (during transmission) logic Active; // High when state is either Active1 or Active0 (during transmission)
logic Active0; //High when state is Active0 logic Active0; // High when state is Active0
//Shift reg signals // Shift reg signals
logic ShiftEdge; //Determines which edge of sck to shift from TransmitShiftReg logic ShiftEdge; // Determines which edge of sck to shift from TransmitShiftReg
logic [7:0] TransmitShiftReg; //Transmit shift register logic [7:0] TransmitShiftReg; // Transmit shift register
logic [7:0] ReceiveShiftReg; //Receive shift register logic [7:0] ReceiveShiftReg; // Receive shift register
logic SampleEdge; //Determines which edge of sck to sample from ReceiveShiftReg 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 [7:0] TransmitDataEndian; // Reverses TransmitData from txFIFO if littleendian, since TransmitReg always shifts MSB
logic TransmitShiftRegLoad; //Determines when to load TransmitShiftReg logic TransmitShiftRegLoad; // Determines when to load TransmitShiftReg
logic ReceiveShiftFull; //High when receive shift register is full logic ReceiveShiftFull; // High when receive shift register is full
logic TransmitShiftEmpty; //High when transmit shift register is empty logic TransmitShiftEmpty; // High when transmit shift register is empty
logic ShiftIn; //Determines whether to shift from SPIIn or SPIOut (if SPI_LOOPBACK_TEST) 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 [3:0] LeftShiftAmount; // Determines left shift amount to left-align data when little endian
logic [7:0] ASR; //AlignedReceiveShiftReg logic [7:0] ASR; // AlignedReceiveShiftReg
//CS signals // CS signals
logic [3:0] ChipSelectAuto; //Assigns ChipSelect value to selected CS signal based on CS ID 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 [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 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 // Miscellaneous signals delayed/early by 1 PCLK cycle
logic ReceiveShiftFullDelay; //Delays ReceiveShiftFull signal by 1 PCLK cycle logic ReceiveShiftFullDelay; // Delays ReceiveShiftFull signal by 1 PCLK cycle
logic TransmitFIFOWriteIncrementDelay; //TransmitFIFOWriteIncrement delayed by 1 PCLK cycle logic ReceiveShiftFullDelayPCLK; // ReceiveShiftFull delayed by 1 PCLK cycle
logic ReceiveShiftFullDelayPCLK; //ReceiveShiftFull delayed by 1 PCLK cycle
logic TransmitFIFOReadEmptyDelay; logic TransmitFIFOReadEmptyDelay;
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 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
//APB access // APB access
assign Entry = {PADDR[7:2],2'b00}; // 32-bit word-aligned accesses assign Entry = {PADDR[7:2],2'b00}; // 32-bit word-aligned accesses
assign Memwrite = PWRITE & PENABLE & PSEL; // only write in access phase assign Memwrite = PWRITE & PENABLE & PSEL; // Only write in access phase
assign PREADY = TransmitInactive; // tie PREADY to transmission for hardware interlock assign PREADY = TransmitInactive; // Tie PREADY to transmission for hardware interlock
//Account for subword read/write circuitry // Account for subword read/write circuitry
// -- Note SPI registers are 32 bits no matter what; access them with LW SW. // -- Note SPI registers are 32 bits no matter what; access them with LW SW.
assign Din = PWDATA[31:0]; assign Din = PWDATA[31:0];
if (P.XLEN == 64) assign PRDATA = {Dout, Dout}; if (P.XLEN == 64) assign PRDATA = {Dout, Dout};
else assign PRDATA = Dout; else assign PRDATA = Dout;
//Register access // Register access
always_ff@(posedge PCLK, negedge PRESETn) always_ff@(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
SckDiv <= #1 12'd3; SckDiv <= #1 12'd3;
@ -167,13 +151,12 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
ReceiveWatermark <= #1 3'b0; ReceiveWatermark <= #1 3'b0;
InterruptEnable <= #1 2'b0; InterruptEnable <= #1 2'b0;
InterruptPending <= #1 2'b0; InterruptPending <= #1 2'b0;
end else begin //writes end else begin // writes
//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
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
if (Memwrite & TransmitInactive) if (Memwrite & TransmitInactive)
case(Entry) //flop to sample inputs case(Entry) // flop to sample inputs
8'h00: SckDiv <= Din[11:0]; 8'h00: SckDiv <= Din[11:0];
8'h04: SckMode <= Din[1:0]; 8'h04: SckMode <= Din[1:0];
8'h10: ChipSelectID <= Din[1:0]; 8'h10: ChipSelectID <= Din[1:0];
@ -188,18 +171,21 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
8'h70: InterruptEnable <= Din[1:0]; 8'h70: InterruptEnable <= Din[1:0];
endcase endcase
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
//interrupt clearance
// 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
InterruptPending[0] <= TransmitReadMark; InterruptPending[0] <= TransmitReadMark;
InterruptPending[1] <= RecieveWriteMark; InterruptPending[1] <= RecieveWriteMark;
case(Entry) // flop to sample inputs
case(Entry) // Flop to sample inputs
8'h00: Dout <= #1 {20'b0, SckDiv}; 8'h00: Dout <= #1 {20'b0, SckDiv};
8'h04: Dout <= #1 {30'b0, SckMode}; 8'h04: Dout <= #1 {30'b0, SckMode};
8'h10: Dout <= #1 {30'b0, ChipSelectID}; 8'h10: Dout <= #1 {30'b0, ChipSelectID};
8'h14: Dout <= #1 {28'b0, ChipSelectDef}; 8'h14: Dout <= #1 {28'b0, ChipSelectDef};
8'h18: Dout <= #1 {30'b0, ChipSelectMode}; 8'h18: Dout <= #1 {30'b0, ChipSelectMode};
8'h28: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]}; 8'h28: Dout <= #1 {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]};
8'h2C: Dout <= {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]}; 8'h2C: Dout <= #1 {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]};
8'h40: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0}; 8'h40: Dout <= #1 {12'b0, Format[4:1], 13'b0, Format[0], 2'b0};
8'h48: Dout <= #1 {23'b0, TransmitFIFOWriteFull, 8'b0}; 8'h48: Dout <= #1 {23'b0, TransmitFIFOWriteFull, 8'b0};
8'h4C: Dout <= #1 {23'b0, ReceiveFIFOReadEmpty, ReceiveData[7:0]}; 8'h4C: Dout <= #1 {23'b0, ReceiveFIFOReadEmpty, ReceiveData[7:0]};
8'h50: Dout <= #1 {29'b0, TransmitWatermark}; 8'h50: Dout <= #1 {29'b0, TransmitWatermark};
@ -210,8 +196,9 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
endcase endcase
end end
//SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1)) // SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1))
//Generates a high signal at the rising and falling edge of SCLK by counting from 0 to SckDiv // 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
assign SCLKenable = (DivCounter == SckDiv); assign SCLKenable = (DivCounter == SckDiv);
assign SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv); assign SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
@ -219,44 +206,38 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
else if (SCLKenable) DivCounter <= 0; else if (SCLKenable) DivCounter <= 0;
else DivCounter <= DivCounter + 12'b1; else DivCounter <= DivCounter + 12'b1;
//Boolean logic that tracks frame progression // Asserts when transmission is one frame before complete
assign FrameCompare = (FrameCount < Format[4:1]); assign ReceivePenultimateFrame = ((FrameCount + 4'b0001) == Format[4:1]);
assign ReceivePenultimateFrameBoolean = ((FrameCount + 4'b0001) == Format[4:1]);
//Computing delays // Computing delays
// When sckmode.pha = 0, an extra half-period delay is implicit in the cs-sck delay, and vice-versa for sck-cs // When sckmode.pha = 0, an extra half-period delay is implicit in the cs-sck delay, and vice-versa for sck-cs
assign ImplicitDelay1 = SckMode[0] ? 9'b0 : 9'b1; assign ImplicitDelay1 = SckMode[0] ? 9'b0 : 9'b1;
assign ImplicitDelay2 = SckMode[0] ? 9'b1 : 9'b0; assign ImplicitDelay2 = SckMode[0] ? 9'b1 : 9'b0;
assign CS_SCKCompare = CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1); // Calculate when tx/rx shift registers are full/empty
assign SCK_CSCompare = SCK_CSCount >= (({Delay0[15:8], 1'b0}) + ImplicitDelay2); TransmitShiftFSM TransmitShiftFSM(PCLK, PRESETn, TransmitFIFOReadEmpty, ReceivePenultimateFrame, Active0, TransmitShiftEmpty);
assign InterCSCompare = (InterCSCount >= ({Delay1[7:0],1'b0})); ReceiveShiftFSM ReceiveShiftFSM(PCLK, PRESETn, SCLKenable, ReceivePenultimateFrame, SampleEdge, SckMode[0], ReceiveShiftFull);
assign InterXFRCompare = (InterXFRCount >= ({Delay1[15:8], 1'b0}));
//Calculate when tx/rx shift registers are full/empty // Calculate tx/rx fifo write and recieve increment signals
TransmitShiftFSM TransmitShiftFSM_1 (PCLK, PRESETn, TransmitFIFOReadEmpty, ReceivePenultimateFrameBoolean, Active0, TransmitShiftEmpty);
ReceiveShiftFSM ReceiveShiftFSM_1 (PCLK, PRESETn, SCLKenable, ReceivePenultimateFrameBoolean, SampleEdge, SckMode[0], ReceiveShiftFull);
//Calculate tx/rx fifo write and recieve increment signals
assign TransmitFIFOWriteIncrement = (Memwrite & (Entry == 8'h48) & ~TransmitFIFOWriteFull & TransmitInactive);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitFIFOWriteIncrementDelay <= 0; if (~PRESETn) TransmitFIFOWriteIncrement <= 0;
else TransmitFIFOWriteIncrementDelay <= TransmitFIFOWriteIncrement; else TransmitFIFOWriteIncrement <= (Memwrite & (Entry == 8'h48) & ~TransmitFIFOWriteFull & TransmitInactive);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveFIFOReadIncrement <= 0; if (~PRESETn) ReceiveFIFOReadIncrement <= 0;
else ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement); else ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement);
//Tx/Rx FIFOs // Tx/Rx FIFOs
SynchFIFO #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn, TransmitFIFOWriteIncrementDelay, TransmitShiftEmpty, TransmitData[7:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0], TransmitFIFOReadData[7:0], TransmitFIFOWriteFull, TransmitFIFOReadEmpty, TransmitWriteMark, TransmitReadMark); SynchFIFO #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn, TransmitFIFOWriteIncrement, TransmitShiftEmpty, TransmitData[7:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0],
SynchFIFO #(3,8) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn, ReceiveShiftFullDelay, ReceiveFIFOReadIncrement, ReceiveShiftRegEndian, ReceiveWatermark[2:0], ReceiveReadWatermarkLevel, ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark); 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);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitFIFOReadEmptyDelay <= 1; if (~PRESETn) TransmitFIFOReadEmptyDelay <= 1;
else if (SCLKenable) TransmitFIFOReadEmptyDelay <= TransmitFIFOReadEmpty; else if (SCLKenable) TransmitFIFOReadEmptyDelay <= TransmitFIFOReadEmpty;
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveShiftFullDelay <= 0; if (~PRESETn) ReceiveShiftFullDelay <= 0;
else if (SCLKenable) ReceiveShiftFullDelay <= ReceiveShiftFull; else if (SCLKenable) ReceiveShiftFullDelay <= ReceiveShiftFull;
@ -266,16 +247,16 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign TransmitShiftRegLoad = ~TransmitShiftEmpty & ~Active | (((ChipSelectMode == 2'b10) & ~|(Delay1[15:8])) & ((ReceiveShiftFullDelay | ReceiveShiftFull) & ~SampleEdge & ~TransmitFIFOReadEmpty)); assign TransmitShiftRegLoad = ~TransmitShiftEmpty & ~Active | (((ChipSelectMode == 2'b10) & ~|(Delay1[15:8])) & ((ReceiveShiftFullDelay | ReceiveShiftFull) & ~SampleEdge & ~TransmitFIFOReadEmpty));
//Main FSM which controls SPI transmission // Main FSM which controls SPI transmission
typedef enum logic [2:0] {CS_INACTIVE, DELAY_0, ACTIVE_0, ACTIVE_1, DELAY_1,INTER_CS, INTER_XFR} statetype; typedef enum logic [2:0] {CS_INACTIVE, DELAY_0, ACTIVE_0, ACTIVE_1, DELAY_1,INTER_CS, INTER_XFR} statetype;
statetype state; statetype state;
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin state <= CS_INACTIVE; if (~PRESETn) begin
state <= CS_INACTIVE;
FrameCount <= 4'b0; FrameCount <= 4'b0;
/* verilator lint_off CASEINCOMPLETE */
end else if (SCLKenable) begin end else if (SCLKenable) begin
/* verilator lint_off CASEINCOMPLETE */
case (state) case (state)
CS_INACTIVE: begin CS_INACTIVE: begin
CS_SCKCount <= 9'b1; CS_SCKCount <= 9'b1;
@ -288,7 +269,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end end
DELAY_0: begin DELAY_0: begin
CS_SCKCount <= CS_SCKCount + 9'b1; CS_SCKCount <= CS_SCKCount + 9'b1;
if (CS_SCKCompare) state <= ACTIVE_0; if (CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1)) state <= ACTIVE_0;
end end
ACTIVE_0: begin ACTIVE_0: begin
FrameCount <= FrameCount + 4'b1; FrameCount <= FrameCount + 4'b1;
@ -296,7 +277,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end end
ACTIVE_1: begin ACTIVE_1: begin
InterXFRCount <= 9'b1; InterXFRCount <= 9'b1;
if (FrameCompare) state <= ACTIVE_0; if (FrameCount < Format[4:1]) state <= ACTIVE_0;
else if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty)) begin else if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty)) begin
state <= ACTIVE_0; state <= ACTIVE_0;
CS_SCKCount <= 9'b1; CS_SCKCount <= 9'b1;
@ -310,11 +291,11 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end end
DELAY_1: begin DELAY_1: begin
SCK_CSCount <= SCK_CSCount + 9'b1; SCK_CSCount <= SCK_CSCount + 9'b1;
if (SCK_CSCompare) state <= INTER_CS; if (SCK_CSCount >= (({Delay0[15:8], 1'b0}) + ImplicitDelay2)) state <= INTER_CS;
end end
INTER_CS: begin INTER_CS: begin
InterCSCount <= InterCSCount + 9'b1; InterCSCount <= InterCSCount + 9'b1;
if (InterCSCompare ) state <= CS_INACTIVE; if (InterCSCount >= ({Delay1[7:0],1'b0})) state <= CS_INACTIVE;
end end
INTER_XFR: begin INTER_XFR: begin
CS_SCKCount <= 9'b1; CS_SCKCount <= 9'b1;
@ -322,13 +303,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
FrameCount <= 4'b0; FrameCount <= 4'b0;
InterCSCount <= 9'b10; InterCSCount <= 9'b10;
InterXFRCount <= InterXFRCount + 9'b1; InterXFRCount <= InterXFRCount + 9'b1;
if (InterXFRCompare & ~TransmitFIFOReadEmptyDelay) state <= ACTIVE_0; if ((InterXFRCount >= ({Delay1[15:8], 1'b0})) & ~TransmitFIFOReadEmptyDelay) state <= ACTIVE_0;
else if (~|ChipSelectMode[1:0]) state <= CS_INACTIVE; else if (~|ChipSelectMode[1:0]) state <= CS_INACTIVE;
end end
endcase endcase
/* verilator lint_off CASEINCOMPLETE */
end end
/* verilator lint_off CASEINCOMPLETE */
assign DelayMode = SckMode[0] ? (state == DELAY_1) : (state == ACTIVE_1 & ReceiveShiftFull); 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; assign ChipSelectInternal = (state == CS_INACTIVE | state == INTER_CS | DelayMode & ~|(Delay0[15:8])) ? ChipSelectDef : ~ChipSelectDef;
@ -339,7 +321,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign TransmitInactive = ((state == INTER_CS) | (state == CS_INACTIVE) | (state == INTER_XFR) | (ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode)); assign TransmitInactive = ((state == INTER_CS) | (state == CS_INACTIVE) | (state == INTER_XFR) | (ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode));
assign Active0 = (state == ACTIVE_0); assign Active0 = (state == ACTIVE_0);
//Signal tracks which edge of sck to shift data // Signal tracks which edge of sck to shift data
always_comb always_comb
case(SckMode[1:0]) case(SckMode[1:0])
2'b00: ShiftEdge = ~sck & SCLKenable; 2'b00: ShiftEdge = ~sck & SCLKenable;
@ -349,36 +331,36 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
default: ShiftEdge = sck & SCLKenable; default: ShiftEdge = sck & SCLKenable;
endcase endcase
//Transmit shift register // 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]; assign TransmitDataEndian = Format[0] ? {TransmitFIFOReadData[0], TransmitFIFOReadData[1], TransmitFIFOReadData[2], TransmitFIFOReadData[3], TransmitFIFOReadData[4], TransmitFIFOReadData[5], TransmitFIFOReadData[6], TransmitFIFOReadData[7]} : TransmitFIFOReadData[7:0];
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if(~PRESETn) TransmitShiftReg <= 8'b0; if(~PRESETn) TransmitShiftReg <= 8'b0;
else if (TransmitShiftRegLoad) TransmitShiftReg <= TransmitDataEndian; else if (TransmitShiftRegLoad) TransmitShiftReg <= TransmitDataEndian;
else if (ShiftEdge & Active) TransmitShiftReg <= {TransmitShiftReg[6:0], 1'b0}; else if (ShiftEdge & Active) TransmitShiftReg <= {TransmitShiftReg[6:0], 1'b0};
assign SPIOut = TransmitShiftReg[7]; assign SPIOut = TransmitShiftReg[7];
//If in loopback mode, receive shift register is connected directly to module's output pins. Else, connected to SPIIn // 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 // There are no setup/hold time issues because transmit shift register and receive shift register always shift/sample on opposite edges
assign ShiftIn = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn; assign ShiftIn = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn;
//Receive shift register // Receive shift register
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if(~PRESETn) ReceiveShiftReg <= 8'b0; if(~PRESETn) ReceiveShiftReg <= 8'b0;
else if (SampleEdge & SCLKenable) begin else if (SampleEdge & SCLKenable) begin
if (~Active) ReceiveShiftReg <= 8'b0; if (~Active) ReceiveShiftReg <= 8'b0;
else ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn}; else ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn};
end end
//Aligns received data and reverses if little-endian // Aligns received data and reverses if little-endian
assign LeftShiftAmount = 4'h8 - Format[4:1]; assign LeftShiftAmount = 4'h8 - Format[4:1];
assign ASR = ReceiveShiftReg << LeftShiftAmount[2:0]; 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]; assign ReceiveShiftRegEndian = Format[0] ? {ASR[0], ASR[1], ASR[2], ASR[3], ASR[4], ASR[5], ASR[6], ASR[7]} : ASR[7:0];
//Interrupt logic: raise interrupt if any enabled interrupts are pending // Interrupt logic: raise interrupt if any enabled interrupts are pending
assign SPIIntr = |(InterruptPending & InterruptEnable); assign SPIIntr = |(InterruptPending & InterruptEnable);
//Chip select logic // Chip select logic
always_comb always_comb
case(ChipSelectID[1:0]) case(ChipSelectID[1:0])
2'b00: ChipSelectAuto = {ChipSelectDef[3], ChipSelectDef[2], ChipSelectDef[1], ChipSelectInternal[0]}; 2'b00: ChipSelectAuto = {ChipSelectDef[3], ChipSelectDef[2], ChipSelectDef[1], ChipSelectInternal[0]};
@ -390,14 +372,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto; assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto;
endmodule endmodule
module SynchFIFO #(parameter M =3 , N= 8)( module SynchFIFO #(parameter M=3, N=8)( // 2^M entries of N bits each
input logic PCLK, wen, ren, PRESETn, input logic PCLK, wen, ren, PRESETn,
input logic winc,rinc, input logic winc, rinc,
input logic [N-1:0] wdata, input logic [N-1:0] wdata,
input logic [M-1:0] wwatermarklevel, rwatermarklevel, input logic [M-1:0] wwatermarklevel, rwatermarklevel,
output logic [N-1:0] rdata, output logic [N-1:0] rdata,
output logic wfull, rempty, output logic wfull, rempty,
output logic wwatermark, rwatermark); output logic wwatermark, rwatermark);
/* Pointer FIFO using design elements from "Simulation and Synthesis Techniques /* 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 for Asynchronous FIFO Design" by Clifford E. Cummings. Namely, M bit read and write pointers
@ -409,8 +391,6 @@ module SynchFIFO #(parameter M =3 , N= 8)(
logic [N-1:0] mem[2**M]; logic [N-1:0] mem[2**M];
logic [M:0] rptr, wptr; logic [M:0] rptr, wptr;
logic [M:0] rptrnext, wptrnext; logic [M:0] rptrnext, wptrnext;
logic rempty_val;
logic wfull_val;
logic [M-1:0] raddr; logic [M-1:0] raddr;
logic [M-1:0] waddr; logic [M-1:0] waddr;
@ -428,53 +408,43 @@ module SynchFIFO #(parameter M =3 , N= 8)(
end end
else begin else begin
if (wen) begin if (wen) begin
wfull <= wfull_val; wfull <= ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
wptr <= wptrnext; wptr <= wptrnext;
end end
if (ren) begin if (ren) begin
rptr <= rptrnext; rptr <= rptrnext;
rempty <= rempty_val; rempty <= (wptr == rptrnext);
end end
end end
assign raddr = rptr[M-1:0]; assign raddr = rptr[M-1:0];
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)}; assign rptrnext = rptr + {{(M){1'b0}}, (rinc & ~rempty)};
assign rempty_val = (wptr == rptrnext);
assign rwatermark = ((waddr - raddr) < rwatermarklevel) & ~wfull; assign rwatermark = ((waddr - raddr) < rwatermarklevel) & ~wfull;
assign waddr = wptr[M-1:0]; assign waddr = wptr[M-1:0];
assign wwatermark = ((waddr - raddr) > wwatermarklevel) | wfull; assign wwatermark = ((waddr - raddr) > wwatermarklevel) | wfull;
assign wptrnext = wptr + {3'b0, (winc & ~wfull)}; assign wptrnext = wptr + {{(M){1'b0}}, (winc & ~wfull)};
assign wfull_val = ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
endmodule endmodule
module TransmitShiftFSM( module TransmitShiftFSM(
input logic PCLK, PRESETn, input logic PCLK, PRESETn,
input logic TransmitFIFOReadEmpty, ReceivePenultimateFrameBoolean, Active0, input logic TransmitFIFOReadEmpty, ReceivePenultimateFrame, Active0,
output logic TransmitShiftEmpty); output logic TransmitShiftEmpty);
typedef enum logic [1:0] {TransmitShiftEmptyState, TransmitShiftHoldState, TransmitShiftNotEmptyState} statetype;
statetype TransmitState, TransmitNextState;
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitState <= TransmitShiftEmptyState; if (~PRESETn) TransmitShiftEmpty <= 1;
else TransmitState <= TransmitNextState; 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
always_comb
case(TransmitState)
TransmitShiftEmptyState: begin
if (TransmitFIFOReadEmpty | (~TransmitFIFOReadEmpty & (ReceivePenultimateFrameBoolean & Active0))) TransmitNextState = TransmitShiftEmptyState;
else if (~TransmitFIFOReadEmpty) TransmitNextState = TransmitShiftNotEmptyState;
end
TransmitShiftNotEmptyState: begin
if (ReceivePenultimateFrameBoolean & Active0) TransmitNextState = TransmitShiftEmptyState;
else TransmitNextState = TransmitShiftNotEmptyState;
end
endcase
assign TransmitShiftEmpty = (TransmitNextState == TransmitShiftEmptyState);
endmodule endmodule
module ReceiveShiftFSM( module ReceiveShiftFSM(
input logic PCLK, PRESETn, SCLKenable, input logic PCLK, PRESETn, SCLKenable,
input logic ReceivePenultimateFrameBoolean, SampleEdge, SckMode, input logic ReceivePenultimateFrame, SampleEdge, SckMode,
output logic ReceiveShiftFull output logic ReceiveShiftFull
); );
typedef enum logic [1:0] {ReceiveShiftFullState, ReceiveShiftNotFullState, ReceiveShiftDelayState} statetype; typedef enum logic [1:0] {ReceiveShiftFullState, ReceiveShiftNotFullState, ReceiveShiftDelayState} statetype;
@ -484,17 +454,12 @@ module ReceiveShiftFSM(
else if (SCLKenable) begin else if (SCLKenable) begin
case (ReceiveState) case (ReceiveState)
ReceiveShiftFullState: ReceiveState <= ReceiveShiftNotFullState; ReceiveShiftFullState: ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftNotFullState: if (ReceivePenultimateFrameBoolean & (SampleEdge)) ReceiveState <= ReceiveShiftDelayState; ReceiveShiftNotFullState: if (ReceivePenultimateFrame & (SampleEdge)) ReceiveState <= ReceiveShiftDelayState;
else ReceiveState <= ReceiveShiftNotFullState; else ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftDelayState: ReceiveState <= ReceiveShiftFullState; ReceiveShiftDelayState: ReceiveState <= ReceiveShiftFullState;
endcase endcase
end end
assign ReceiveShiftFull = SckMode ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState); assign ReceiveShiftFull = SckMode ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState);
endmodule endmodule