Merge pull request #1063 from JacobPease/main

Optimized SPI Logic
This commit is contained in:
Rose Thompson 2024-11-04 17:21:20 -06:00 committed by GitHub
commit 866ad88e97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 132 additions and 202 deletions

View File

@ -48,7 +48,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
output logic SPICLK output logic SPICLK
); );
// register map // register map
localparam SPI_SCKDIV = 8'h00; localparam SPI_SCKDIV = 8'h00;
localparam SPI_SCKMODE = 8'h04; localparam SPI_SCKMODE = 8'h04;
localparam SPI_CSID = 8'h10; localparam SPI_CSID = 8'h10;
@ -85,11 +85,11 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// SPI Controller signals // SPI Controller signals
logic SCLKenable; logic SCLKenable;
logic EndOfFrame; logic EndOfFrame;
logic EndOfFrameDelay;
logic Transmitting; logic Transmitting;
logic InactiveState; logic InactiveState;
logic [3:0] FrameLength; logic [3:0] FrameLength;
// Starting Transmission and restarting SCLKenable
logic ResetSCLKenable; logic ResetSCLKenable;
logic TransmitStart; logic TransmitStart;
logic TransmitStartD; logic TransmitStartD;
@ -98,16 +98,19 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
typedef enum logic [1:0] {READY, START, WAIT} txState; typedef enum logic [1:0] {READY, START, WAIT} txState;
txState CurrState, NextState; txState CurrState, NextState;
// FIFO FSM signals // FIFO Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1]
// Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1] logic TransmitWriteMark, TransmitReadMark, ReceiveWriteMark, ReceiveReadMark;
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty;
logic TransmitFIFOWriteIncrement;
logic [7:0] TransmitFIFOReadData;
// Transmit FIFO Signals
logic TransmitFIFOFull, TransmitFIFOEmpty;
logic TransmitFIFOWriteInc;
logic TransmitFIFOReadInc; // Increments Tx FIFO read ptr 1 cycle after Tx FIFO is read
logic [7:0] TransmitReadData;
// ReceiveFIFO Signals
logic ReceiveFIFOWriteInc; logic ReceiveFIFOWriteInc;
logic ReceiveFIFOReadIncrement; logic ReceiveFIFOReadInc;
logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty; logic ReceiveFIFOFull, ReceiveFIFOEmpty;
/* verilator lint_off UNDRIVEN */ /* verilator lint_off UNDRIVEN */
logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel; // unused generic FIFO outputs logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel; // unused generic FIFO outputs
@ -115,16 +118,16 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
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)
// Shift reg signals // Shift reg signals
logic ShiftEdge; // Determines which edge of sck to shift from TransmitReg logic ShiftEdge; // Determines which edge of sck to shift from TransmitReg
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] TransmitReg; // Transmit shift register logic [7:0] TransmitReg; // Transmit shift register
logic [7:0] ReceiveShiftReg; // Receive shift register logic [7:0] ReceiveShiftReg; // Receive shift register
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 TransmitLoad; // Determines when to load TransmitReg logic TransmitLoad; // Determines when to load TransmitReg
logic TransmitFIFOReadIncrement; // Increments Tx FIFO read ptr 1 cycle after Tx FIFO is read logic TransmitRegLoaded;
// Shift stuff due to Format register? // Shift stuff due to Format register?
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
@ -135,7 +138,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// 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 = Entry == SPI_TXDATA | Entry == SPI_RXDATA | Entry == SPI_IP;
assign PREADY = 1'b1; assign PREADY = 1'b1;
// Account for subword read/write circuitry // Account for subword read/write circuitry
@ -162,7 +164,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
InterruptEnable <= 2'b0; InterruptEnable <= 2'b0;
InterruptPending <= 2'b0; InterruptPending <= 2'b0;
end else begin // writes end else begin // writes
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
if (Memwrite) if (Memwrite)
case(Entry) // flop to sample inputs case(Entry) // flop to sample inputs
SPI_SCKDIV: SckDiv <= Din[11:0]; SPI_SCKDIV: SckDiv <= Din[11:0];
@ -180,14 +182,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
if (Memwrite) if (Memwrite)
case(Entry) case(Entry)
SPI_TXDATA: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0]; SPI_TXDATA: if (~TransmitFIFOFull) TransmitData[7:0] <= Din[7:0];
endcase endcase
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
// According to FU540 spec: Once interrupt is pending, it will remain set until number // 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 // of entries in tx/rx fifo is strictly more/less than tx/rxmark
InterruptPending[0] <= TransmitReadMark; InterruptPending[0] <= TransmitReadMark;
InterruptPending[1] <= RecieveWriteMark; InterruptPending[1] <= ReceiveWriteMark;
case(Entry) // Flop to sample inputs case(Entry) // Flop to sample inputs
SPI_SCKDIV: Dout <= {20'b0, SckDiv}; SPI_SCKDIV: Dout <= {20'b0, SckDiv};
@ -198,8 +200,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
SPI_DELAY0: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]}; 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_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_FMT: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0};
SPI_TXDATA: Dout <= {TransmitFIFOWriteFull, 23'b0, 8'b0}; SPI_TXDATA: Dout <= {TransmitFIFOFull, 23'b0, 8'b0};
SPI_RXDATA: Dout <= {ReceiveFIFOReadEmpty, 23'b0, ReceiveData[7:0]}; SPI_RXDATA: Dout <= {ReceiveFIFOEmpty, 23'b0, ReceiveData[7:0]};
SPI_TXMARK: Dout <= {29'b0, TransmitWatermark}; SPI_TXMARK: Dout <= {29'b0, TransmitWatermark};
SPI_RXMARK: Dout <= {29'b0, ReceiveWatermark}; SPI_RXMARK: Dout <= {29'b0, ReceiveWatermark};
SPI_IE: Dout <= {30'b0, InterruptEnable}; SPI_IE: Dout <= {30'b0, InterruptEnable};
@ -208,11 +210,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
endcase endcase
end end
// 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
// When SckDiv is 0, count doesn't work and SCLKenable is simply PCLK *** dh 10/26/24: this logic is seriously broken. SCLK is not scaled to PCLK/(2*(SckDiv + 1)).
// SPI Controller module ------------------------------------------- // SPI Controller module -------------------------------------------
// This module controls state and timing signals that drive the rest of this module // This module controls state and timing signals that drive the rest of this module
assign ResetSCLKenable = Memwrite & (Entry == SPI_SCKDIV); assign ResetSCLKenable = Memwrite & (Entry == SPI_SCKDIV);
@ -220,59 +217,51 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
spi_controller controller(PCLK, PRESETn, spi_controller controller(PCLK, PRESETn,
// Transmit Signals // Transmit Signals
TransmitStart, TransmitStartD, ResetSCLKenable, TransmitStart, TransmitRegLoaded, ResetSCLKenable,
// Register Inputs // Register Inputs
SckDiv, SckMode, ChipSelectMode, Delay0, Delay1, FrameLength, SckDiv, SckMode, ChipSelectMode, Delay0, Delay1, FrameLength,
// txFIFO stuff // txFIFO stuff
TransmitFIFOReadEmpty, TransmitFIFOEmpty,
// Timing // Timing
SCLKenable, ShiftEdge, SampleEdge, EndOfFrame, EndOfFrameDelay, SCLKenable, ShiftEdge, SampleEdge, EndOfFrame,
// State stuff // State stuff
Transmitting, InactiveState, Transmitting, InactiveState,
// Outputs // Outputs
SPICLK); SPICLK);
// Transmit FIFO --------------------------------------------------- // Transmit FIFO ---------------------------------------------------
always_ff @(posedge PCLK)
if (~PRESETn) begin
TransmitFIFOWriteIncrement <= 1'b0;
end else begin
TransmitFIFOWriteIncrement <= (Memwrite & (Entry == SPI_TXDATA) & ~TransmitFIFOWriteFull);
end
always_ff @(posedge PCLK) // txFIFO write increment logic
if (~PRESETn) begin flopr #(1) txwincreg(PCLK, ~PRESETn,
TransmitFIFOReadIncrement <= 1'b0; (Memwrite & (Entry == SPI_TXDATA) & ~TransmitFIFOFull),
end else if (SCLKenable) begin TransmitFIFOWriteInc);
TransmitFIFOReadIncrement <= TransmitStartD | (EndOfFrameDelay & ~TransmitFIFOReadEmpty) ;
end // txFIFO read increment logic
flopenr #(1) txrincreg(PCLK, ~PRESETn, SCLKenable,
TransmitStartD | (EndOfFrame & ~TransmitFIFOEmpty),
TransmitFIFOReadInc);
// Check whether TransmitReg has been loaded. // Check whether TransmitReg has been loaded.
// We use this signal to prevent returning to the Ready state for TransmitStart // We use this signal to prevent returning to the Ready state for TransmitStart
logic TransmitRegLoaded;
always_ff @(posedge PCLK) begin always_ff @(posedge PCLK) begin
if (~PRESETn) begin if (~PRESETn) begin
TransmitRegLoaded <= 1'b0; TransmitRegLoaded <= 1'b0;
end else if (TransmitLoad) begin end else if (TransmitLoad) begin
TransmitRegLoaded <= 1'b1; TransmitRegLoaded <= 1'b1;
end else if (ShiftEdge | EndOfFrameDelay) begin end else if (ShiftEdge | EndOfFrame) begin
TransmitRegLoaded <= 1'b0; TransmitRegLoaded <= 1'b0;
end end
end end
// Setup TransmitStart state machine // Setup TransmitStart state machine
always_ff @(posedge PCLK) begin always_ff @(posedge PCLK)
if (~PRESETn) begin if (~PRESETn) CurrState <= READY;
CurrState <= READY; else CurrState <= NextState;
end else begin
CurrState <= NextState;
end
end
// State machine for starting transmissions // State machine for starting transmissions
always_comb begin always_comb begin
case (CurrState) case (CurrState)
READY: if (~TransmitFIFOReadEmpty & ~Transmitting) NextState = START; READY: if (~TransmitFIFOEmpty & ~Transmitting) NextState = START;
else NextState = READY; else NextState = READY;
START: NextState = WAIT; START: NextState = WAIT;
WAIT: if (~Transmitting & ~TransmitRegLoaded) NextState = READY; WAIT: if (~Transmitting & ~TransmitRegLoaded) NextState = READY;
@ -281,49 +270,48 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
endcase endcase
end end
// Delayed TransmitStart signal for incrementing tx read point.
assign TransmitStart = (CurrState == START); assign TransmitStart = (CurrState == START);
always_ff @(posedge PCLK) always_ff @(posedge PCLK)
if (~PRESETn) TransmitStartD <= 1'b0; if (~PRESETn) TransmitStartD <= 1'b0;
else if (TransmitStart) TransmitStartD <= 1'b1; else if (TransmitStart) TransmitStartD <= 1'b1;
else if (SCLKenable) TransmitStartD <= 1'b0; else if (SCLKenable) TransmitStartD <= 1'b0;
// Transmit FIFO
spi_fifo #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn, spi_fifo #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn,
TransmitFIFOWriteIncrement, TransmitFIFOReadIncrement, TransmitFIFOWriteInc, TransmitFIFOReadInc,
TransmitData[7:0], TransmitData[7:0],
TransmitWriteWatermarkLevel, TransmitWatermark[2:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0],
TransmitFIFOReadData[7:0], TransmitReadData[7:0],
TransmitFIFOWriteFull, TransmitFIFOFull,
TransmitFIFOReadEmpty, TransmitFIFOEmpty,
TransmitWriteMark, TransmitReadMark); TransmitWriteMark, TransmitReadMark);
// Receive FIFO ---------------------------------------------------- // Receive FIFO ----------------------------------------------------
always_ff @(posedge PCLK)
if (~PRESETn) begin
ReceiveFIFOReadIncrement <= 1'b0;
end else begin
ReceiveFIFOReadIncrement <= ((Entry == SPI_RXDATA) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement);
end
always_ff @(posedge PCLK) // Receive FIFO Read Increment register
if (~PRESETn) begin flopr #(1) rxfiforincreg(PCLK, ~PRESETn,
ReceiveFIFOWriteInc <= 1'b0; ((Entry == SPI_RXDATA) & ~ReceiveFIFOEmpty & PSEL & ~ReceiveFIFOReadInc),
end else if (SCLKenable) begin ReceiveFIFOReadInc);
ReceiveFIFOWriteInc <= EndOfFrameDelay;
end
// Receive FIFO Write Increment register
flopenr #(1) rxfifowincreg(PCLK, ~PRESETn, SCLKenable,
EndOfFrame, ReceiveFIFOWriteInc);
// Receive FIFO
spi_fifo #(3,8) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn, spi_fifo #(3,8) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn,
ReceiveFIFOWriteInc, ReceiveFIFOReadIncrement, ReceiveFIFOWriteInc, ReceiveFIFOReadInc,
ReceiveShiftRegEndian, ReceiveWatermark[2:0], ReceiveShiftRegEndian, ReceiveWatermark[2:0],
ReceiveReadWatermarkLevel, ReceiveReadWatermarkLevel,
ReceiveData[7:0], ReceiveData[7:0],
ReceiveFIFOWriteFull, ReceiveFIFOFull,
ReceiveFIFOReadEmpty, ReceiveFIFOEmpty,
RecieveWriteMark, RecieveReadMark); ReceiveWriteMark, ReceiveReadMark);
// Shift Registers --------------------------------------------------
// Transmit shift register // Transmit shift register
assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~TransmitFIFOReadEmpty); assign TransmitLoad = TransmitStart | (EndOfFrame & ~TransmitFIFOEmpty);
assign TransmitDataEndian = Format[0] ? {<<{TransmitFIFOReadData[7:0]}} : TransmitFIFOReadData[7:0]; assign TransmitDataEndian = Format[0] ? {<<{TransmitReadData[7:0]}} : TransmitReadData[7:0];
always_ff @(posedge PCLK) always_ff @(posedge PCLK)
if(~PRESETn) TransmitReg <= 8'b0; if(~PRESETn) TransmitReg <= 8'b0;
else if (TransmitLoad) TransmitReg <= TransmitDataEndian; else if (TransmitLoad) TransmitReg <= TransmitDataEndian;
@ -342,12 +330,11 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
if(~PRESETn) begin if(~PRESETn) begin
ReceiveShiftReg <= 8'b0; ReceiveShiftReg <= 8'b0;
end else if (SampleEdge) begin end else if (SampleEdge) begin
if (~Transmitting) ReceiveShiftReg <= 8'b0; 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 - FrameLength;
assign ASR = ReceiveShiftReg << LeftShiftAmount[2:0]; assign ASR = ReceiveShiftReg << LeftShiftAmount[2:0];
assign ReceiveShiftRegEndian = Format[0] ? {<<{ASR[7:0]}} : ASR[7:0]; assign ReceiveShiftRegEndian = Format[0] ? {<<{ASR[7:0]}} : ASR[7:0];

View File

@ -33,7 +33,7 @@ module spi_controller (
// Start Transmission // Start Transmission
input logic TransmitStart, input logic TransmitStart,
input logic TransmitStartD, input logic TransmitRegLoaded,
input logic ResetSCLKenable, input logic ResetSCLKenable,
// Registers // Registers
@ -45,14 +45,13 @@ module spi_controller (
input logic [3:0] FrameLength, input logic [3:0] FrameLength,
// Is the Transmit FIFO Empty? // Is the Transmit FIFO Empty?
input logic txFIFOReadEmpty, input logic TransmitFIFOEmpty,
// Control signals // Control signals
output logic SCLKenable, output logic SCLKenable,
output logic ShiftEdge, output logic ShiftEdge,
output logic SampleEdge, output logic SampleEdge,
output logic EndOfFrame, output logic EndOfFrame,
output logic EndOfFrameDelay,
output logic Transmitting, output logic Transmitting,
output logic InactiveState, output logic InactiveState,
output logic SPICLK output logic SPICLK
@ -63,43 +62,31 @@ module spi_controller (
localparam AUTOMODE = 2'b00; localparam AUTOMODE = 2'b00;
localparam OFFMODE = 2'b11; localparam OFFMODE = 2'b11;
// FSM States
typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype; typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
statetype CurrState, NextState; statetype CurrState, NextState;
// SCLKenable stuff // SCLKenable stuff
logic [11:0] DivCounter; logic [11:0] DivCounter;
// logic SCLKenable; logic SCK;
// logic SCLKenableEarly;
logic ZeroDiv;
logic SCK; // SUPER IMPORTANT, THIS CAN'T BE THE SAME AS SPICLK!
// Shift and Sample Edges // Shift and Sample Edges
logic PreShiftEdge; logic EdgePulse;
logic PreSampleEdge;
// logic ShiftEdge;
// logic SampleEdge;
logic ShiftEdgePulse; logic ShiftEdgePulse;
logic SampleEdgePulse; logic SampleEdgePulse;
logic EndOfFramePulse; logic EndOfFramePulse;
logic PhaseOneOffset;
// Frame stuff // Frame stuff
logic [3:0] BitNum; logic [3:0] BitNum;
logic LastBit; logic LastBit;
//logic EndOfFrame;
//logic EndOfFrameDelay;
logic PhaseOneOffset;
// Transmit Stuff // Transmit Stuff
logic ContinueTransmit; logic ContinueTransmit;
// SPIOUT Stuff
// logic TransmitLoad;
logic [7:0] TransmitReg;
//logic Transmitting;
logic EndTransmission; logic EndTransmission;
// logic TransmitRegLoaded; // TODO: Could be replaced by TransmitRegLoaded?
logic HoldMode; logic NextEndDelay;
logic CurrentEndDelay;
// Delay Stuff // Delay Stuff
logic [7:0] cssck; logic [7:0] cssck;
@ -116,13 +103,12 @@ module spi_controller (
logic EndOfSCKCS; logic EndOfSCKCS;
logic EndOfINTERCS; logic EndOfINTERCS;
logic EndOfINTERXFR; logic EndOfINTERXFR;
logic EndOfDelay;
logic [7:0] CSSCKCounter; logic [7:0] DelayCounter;
logic [7:0] SCKCSCounter;
logic [7:0] INTERCSCounter;
logic [7:0] INTERXFRCounter;
logic DelayIsNext; logic DelayIsNext;
logic DelayState;
// Convenient Delay Reg Names // Convenient Delay Reg Names
assign cssck = Delay0[7:0]; assign cssck = Delay0[7:0];
@ -137,23 +123,25 @@ module spi_controller (
assign HasINTERXFR = interxfr > 8'b0; assign HasINTERXFR = interxfr > 8'b0;
// Have we hit full delay for any of the delays? // Have we hit full delay for any of the delays?
assign EndOfCSSCK = CSSCKCounter == cssck; assign EndOfCSSCK = (DelayCounter == cssck) & (CurrState == CSSCK);
assign EndOfSCKCS = SCKCSCounter == sckcs; assign EndOfSCKCS = (DelayCounter == sckcs) & (CurrState == SCKCS);
assign EndOfINTERCS = INTERCSCounter == intercs; assign EndOfINTERCS = (DelayCounter == intercs) & (CurrState == INTERCS);
assign EndOfINTERXFR = INTERXFRCounter == interxfr; assign EndOfINTERXFR = (DelayCounter == interxfr) & (CurrState == INTERXFR);
assign EndOfDelay = EndOfCSSCK | EndOfSCKCS | EndOfINTERCS | EndOfINTERXFR;
// Clock Signal Stuff ----------------------------------------------- // Clock Signal Stuff -----------------------------------------------
// I'm going to handle all clock stuff here, including ShiftEdge and // I'm going to handle all clock stuff here, including ShiftEdge and
// SampleEdge. This makes sure that SPICLK is an output of a register // SampleEdge. This makes sure that SPICLK is an output of a register
// and it properly synchronizes signals. // and it properly synchronizes signals.
// 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
assign SCLKenable = DivCounter == SckDiv; assign SCLKenable = DivCounter == SckDiv;
// assign SCLKenableEarly = (DivCounter + 1'b1) == SckDiv;
assign LastBit = (BitNum == FrameLength - 4'b1);
//assign EndOfFrame = SCLKenable & LastBit & Transmitting; assign ContinueTransmit = ~TransmitFIFOEmpty & EndOfFrame;
assign ContinueTransmit = ~txFIFOReadEmpty & EndOfFrameDelay; assign EndTransmission = TransmitFIFOEmpty & EndOfFrame;
assign EndTransmission = txFIFOReadEmpty & EndOfFrameDelay;
always_ff @(posedge PCLK) begin always_ff @(posedge PCLK) begin
if (~PRESETn) begin if (~PRESETn) begin
@ -161,44 +149,20 @@ module spi_controller (
SPICLK <= SckMode[1]; SPICLK <= SckMode[1];
SCK <= 0; SCK <= 0;
BitNum <= 4'h0; BitNum <= 4'h0;
PreShiftEdge <= 0; DelayCounter <= 0;
PreSampleEdge <= 0;
EndOfFrame <= 0;
CSSCKCounter <= 0;
SCKCSCounter <= 0;
INTERCSCounter <= 0;
INTERXFRCounter <= 0;
end else begin end else begin
// TODO: Consolidate into one delay counter since none of the // SCK logic for delay times
// delays happen at the same time?
if (TransmitStart) begin if (TransmitStart) begin
SCK <= 0; SCK <= 0;
end else if (SCLKenable) begin end else if (SCLKenable) begin
SCK <= ~SCK; SCK <= ~SCK;
end end
if ((CurrState == CSSCK) & SCK & SCLKenable) begin // Counter for all four delay types
CSSCKCounter <= CSSCKCounter + 8'd1; if (DelayState & SCK & SCLKenable) begin
end else if (SCLKenable & EndOfCSSCK) begin DelayCounter <= DelayCounter + 8'd1;
CSSCKCounter <= 8'd0; end else if (SCLKenable & EndOfDelay) begin
end DelayCounter <= 8'd0;
if ((CurrState == SCKCS) & SCK & SCLKenable) begin
SCKCSCounter <= SCKCSCounter + 8'd1;
end else if (SCLKenable & EndOfSCKCS) begin
SCKCSCounter <= 8'd0;
end
if ((CurrState == INTERCS) & SCK & SCLKenable) begin
INTERCSCounter <= INTERCSCounter + 8'd1;
end else if (SCLKenable & EndOfINTERCS) begin
INTERCSCounter <= 8'd0;
end
if ((CurrState == INTERXFR) & SCK & SCLKenable) begin
INTERXFRCounter <= INTERXFRCounter + 8'd1;
end else if (SCLKenable & EndOfINTERXFR) begin
INTERXFRCounter <= 8'd0;
end end
// SPICLK Logic // SPICLK Logic
@ -215,95 +179,69 @@ module spi_controller (
DivCounter <= DivCounter + 12'd1; DivCounter <= DivCounter + 12'd1;
end end
// EndOfFrame controller
// if (SckDiv > 0 ? SCLKenableEarly & LastBit & SPICLK : LastBit & ~SPICLK) begin
// EndOfFrame <= 1'b1;
// end else begin
// EndOfFrame <= 1'b0;
// end
// TODO: Rename EndOfFrameDelay to EndOfFrame and remove this logic
if (~TransmitStart) begin
EndOfFrame <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
end
// Increment BitNum // Increment BitNum
if (ShiftEdge & Transmitting) begin if (ShiftEdge & Transmitting) begin
BitNum <= BitNum + 4'd1; BitNum <= BitNum + 4'd1;
end else if (EndOfFrameDelay) begin end else if (EndOfFrame) begin
BitNum <= 4'b0; BitNum <= 4'b0;
end end
end end
end end
// The very last bit in a frame of any length.
assign LastBit = (BitNum == FrameLength - 4'b1);
// Any SCLKenable pulse aligns with leading or trailing edge during
// Transmission. We can use this signal as the basis for ShiftEdge
// and SampleEdge.
assign EdgePulse = SCLKenable & Transmitting;
// Possible pulses for all edge types. Combined with SPICLK to get
// edges for different phase and polarity modes.
assign ShiftEdgePulse = EdgePulse & ~LastBit;
assign SampleEdgePulse = EdgePulse & ~DelayIsNext;
assign EndOfFramePulse = EdgePulse & LastBit;
// Delay ShiftEdge and SampleEdge by a half PCLK period // Delay ShiftEdge and SampleEdge by a half PCLK period
// Aligned EXACTLY ON THE MIDDLE of the leading and trailing edges. // Aligned EXACTLY ON THE MIDDLE of the leading and trailing edges.
// Sweeeeeeeeeet... // Sweeeeeeeeeet...
assign ShiftEdgePulse = SCLKenable & ~LastBit & Transmitting;
assign SampleEdgePulse = SCLKenable & Transmitting & ~DelayIsNext;
assign EndOfFramePulse = SCLKenable & LastBit & Transmitting;
always_ff @(posedge ~PCLK) begin always_ff @(posedge ~PCLK) begin
if (~PRESETn | TransmitStart) begin if (~PRESETn | TransmitStart) begin
ShiftEdge <= 0; ShiftEdge <= 0;
PhaseOneOffset <= 0; PhaseOneOffset <= 0;
SampleEdge <= 0; SampleEdge <= 0;
EndOfFrameDelay <= 0; EndOfFrame <= 0;
end else begin end else begin
PhaseOneOffset <= (PhaseOneOffset == 0) ? Transmitting & SCLKenable : ~EndOfFrameDelay; PhaseOneOffset <= (PhaseOneOffset == 0) ? Transmitting & SCLKenable : ~EndOfFrame;
case(SckMode) case(SckMode)
2'b00: begin 2'b00: begin
ShiftEdge <= SPICLK & ShiftEdgePulse; ShiftEdge <= SPICLK & ShiftEdgePulse;
SampleEdge <= ~SPICLK & SampleEdgePulse; SampleEdge <= ~SPICLK & SampleEdgePulse;
EndOfFrameDelay <= SPICLK & EndOfFramePulse; EndOfFrame <= SPICLK & EndOfFramePulse;
end end
2'b01: begin 2'b01: begin
ShiftEdge <= ~SPICLK & ShiftEdgePulse & PhaseOneOffset; ShiftEdge <= ~SPICLK & ShiftEdgePulse & PhaseOneOffset;
SampleEdge <= SPICLK & SampleEdgePulse; SampleEdge <= SPICLK & SampleEdgePulse;
EndOfFrameDelay <= ~SPICLK & EndOfFramePulse; EndOfFrame <= ~SPICLK & EndOfFramePulse;
end end
2'b10: begin 2'b10: begin
ShiftEdge <= ~SPICLK & ShiftEdgePulse; ShiftEdge <= ~SPICLK & ShiftEdgePulse;
SampleEdge <= SPICLK & SampleEdgePulse; SampleEdge <= SPICLK & SampleEdgePulse;
EndOfFrameDelay <= ~SPICLK & EndOfFramePulse; EndOfFrame <= ~SPICLK & EndOfFramePulse;
end end
2'b11: begin 2'b11: begin
ShiftEdge <= SPICLK & ShiftEdgePulse & PhaseOneOffset; ShiftEdge <= SPICLK & ShiftEdgePulse & PhaseOneOffset;
SampleEdge <= ~SPICLK & SampleEdgePulse; SampleEdge <= ~SPICLK & SampleEdgePulse;
EndOfFrameDelay <= SPICLK & EndOfFramePulse; EndOfFrame <= SPICLK & EndOfFramePulse;
end end
// ShiftEdge <= ((SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & ~LastBit & Transmitting) & PhaseOneOffset;
// PhaseOneOffset <= PhaseOneOffset == 0 ? Transmitting & SCLKenable : ~EndOfFrameDelay;
// SampleEdge <= (SckMode[1] ^ SckMode[0] ^ ~SPICLK) & SCLKenable & Transmitting & ~DelayIsNext;
// EndOfFrameDelay <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
endcase endcase
end end
end end
// typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype; // Logic for continuing to transmit through Delay states after end of frame
// statetype CurrState, NextState;
assign HoldMode = CSMode == HOLDMODE;
// assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~txFIFOReadEmpty);
logic ContinueTransmitD;
logic NextEndDelay;
logic CurrentEndDelay;
assign NextEndDelay = NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR; assign NextEndDelay = NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR;
assign CurrentEndDelay = CurrState == SCKCS | CurrState == INTERCS | CurrState == INTERXFR; assign CurrentEndDelay = CurrState == SCKCS | CurrState == INTERCS | CurrState == INTERXFR;
always_ff @(posedge PCLK) begin
if (~PRESETn) begin
ContinueTransmitD <= 1'b0;
end else if (NextEndDelay & ~CurrentEndDelay) begin
ContinueTransmitD <= ContinueTransmit;
end else if (EndOfSCKCS & SCLKenable) begin
ContinueTransmitD <= 1'b0;
end
end
always_ff @(posedge PCLK) begin always_ff @(posedge PCLK) begin
if (~PRESETn) begin if (~PRESETn) begin
CurrState <= INACTIVE; CurrState <= INACTIVE;
@ -314,7 +252,7 @@ module spi_controller (
always_comb begin always_comb begin
case (CurrState) case (CurrState)
INACTIVE: if (TransmitStartD) begin INACTIVE: if (TransmitRegLoaded) begin
if (~HasCSSCK) NextState = TRANSMIT; if (~HasCSSCK) NextState = TRANSMIT;
else NextState = CSSCK; else NextState = CSSCK;
end else begin end else begin
@ -326,7 +264,7 @@ module spi_controller (
case(CSMode) case(CSMode)
AUTOMODE: begin AUTOMODE: begin
if (EndTransmission) NextState = INACTIVE; if (EndTransmission) NextState = INACTIVE;
else if (EndOfFrameDelay) NextState = SCKCS; else if (EndOfFrame) NextState = SCKCS;
else NextState = TRANSMIT; else NextState = TRANSMIT;
end end
HOLDMODE: begin HOLDMODE: begin
@ -344,7 +282,7 @@ module spi_controller (
end end
SCKCS: begin // SCKCS case -------------------------------------- SCKCS: begin // SCKCS case --------------------------------------
if (EndOfSCKCS) begin if (EndOfSCKCS) begin
if (~ContinueTransmitD) begin if (~TransmitRegLoaded) begin
// if (CSMode == AUTOMODE) NextState = INACTIVE; // if (CSMode == AUTOMODE) NextState = INACTIVE;
if (CSMode == HOLDMODE) NextState = HOLD; if (CSMode == HOLDMODE) NextState = HOLD;
else NextState = INACTIVE; else NextState = INACTIVE;
@ -359,7 +297,7 @@ module spi_controller (
HOLD: begin // HOLD mode case ----------------------------------- HOLD: begin // HOLD mode case -----------------------------------
if (CSMode == AUTOMODE) begin if (CSMode == AUTOMODE) begin
NextState = INACTIVE; NextState = INACTIVE;
end else if (TransmitStartD) begin // If FIFO is written to, start again. end else if (TransmitRegLoaded) begin // If FIFO is written to, start again.
NextState = TRANSMIT; NextState = TRANSMIT;
end else NextState = HOLD; end else NextState = HOLD;
end end
@ -386,6 +324,7 @@ module spi_controller (
assign Transmitting = CurrState == TRANSMIT; assign Transmitting = CurrState == TRANSMIT;
assign DelayIsNext = (NextState == CSSCK | NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR); assign DelayIsNext = (NextState == CSSCK | NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR);
assign DelayState = (CurrState == CSSCK | CurrState == SCKCS | CurrState == INTERCS | CurrState == INTERXFR);
assign InactiveState = CurrState == INACTIVE | CurrState == INTERCS; assign InactiveState = CurrState == INACTIVE | CurrState == INTERCS;
endmodule endmodule

View File

@ -20,6 +20,10 @@ module spi_fifo #(parameter M=3, N=8)( // 2^M entries of N bits
logic [M-1:0] raddr; logic [M-1:0] raddr;
logic [M-1:0] waddr; logic [M-1:0] waddr;
logic [M-1:0] numVals;
assign numVals = waddr - raddr;
assign rdata = mem[raddr]; assign rdata = mem[raddr];
always_ff @(posedge PCLK) always_ff @(posedge PCLK)
if (winc & ~wfull) mem[waddr] <= wdata; if (winc & ~wfull) mem[waddr] <= wdata;