mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-02 17:55:19 +00:00
Added a new spi controller design. Designed as a proof of concept to see if timing issues can be fixed. I intend to work it into existing SPI peripheral.
This commit is contained in:
parent
13df786b87
commit
37d2f3220e
317
src/uncore/spi_controller.sv
Normal file
317
src/uncore/spi_controller.sv
Normal file
@ -0,0 +1,317 @@
|
||||
module spi_controller (
|
||||
input logic PCLK,
|
||||
input logic PRESETn,
|
||||
input logic TransmitStart,
|
||||
input logic [11:0] SckDiv,
|
||||
input logic [1:0] SckMode,
|
||||
input logic [1:0] CSMode,
|
||||
input logic [15:0] Delay0,
|
||||
input logic [15:0] Delay1,
|
||||
input logic [7:0] txFIFORead,
|
||||
input logic txFIFOReadEmpty,
|
||||
output logic SPICLK,
|
||||
output logic SPIOUT,
|
||||
output logic CS
|
||||
);
|
||||
|
||||
// CSMode Stuff
|
||||
localparam HOLDMODE = 2'b10;
|
||||
localparam AUTOMODE = 2'b00;
|
||||
localparam OFFMODE = 2'b11;
|
||||
|
||||
typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
|
||||
statetype CurrState, NextState;
|
||||
|
||||
// SCLKenable stuff
|
||||
logic [11:0] DivCounter;
|
||||
logic SCLKenable;
|
||||
logic SCLKenableEarly;
|
||||
logic SCLKenableLate;
|
||||
logic EdgeTiming;
|
||||
logic ZeroDiv;
|
||||
logic Clock0;
|
||||
logic Clock1;
|
||||
logic SCK; // SUPER IMPORTANT, THIS CAN'T BE THE SAME AS SPICLK!
|
||||
|
||||
|
||||
// Shift and Sample Edges
|
||||
logic PreShiftEdge;
|
||||
logic PreSampleEdge;
|
||||
logic ShiftEdge;
|
||||
logic SampleEdge;
|
||||
|
||||
// Frame stuff
|
||||
logic [2:0] BitNum;
|
||||
logic LastBit;
|
||||
logic EndOfFrame;
|
||||
logic EndOfFrameDelay;
|
||||
logic PhaseOneOffset;
|
||||
|
||||
// Transmit Stuff
|
||||
logic ContinueTransmit;
|
||||
|
||||
// SPIOUT Stuff
|
||||
logic TransmitLoad;
|
||||
logic [7:0] TransmitReg;
|
||||
logic Transmitting;
|
||||
logic EndTransmission;
|
||||
|
||||
logic HoldMode;
|
||||
|
||||
// Delay Stuff
|
||||
logic [7:0] cssck;
|
||||
logic [7:0] sckcs;
|
||||
logic [7:0] intercs;
|
||||
logic [7:0] interxfr;
|
||||
|
||||
logic HasCSSCK;
|
||||
logic HasSCKCS;
|
||||
logic HasINTERCS;
|
||||
logic HasINTERXFR;
|
||||
|
||||
logic EndOfCSSCK;
|
||||
logic EndOfSCKCS;
|
||||
logic EndOfINTERCS;
|
||||
logic EndOfINTERXFR;
|
||||
|
||||
logic [7:0] CSSCKCounter;
|
||||
logic [7:0] SCKCSCounter;
|
||||
logic [7:0] INTERCSCounter;
|
||||
logic [7:0] INTERXFRCounter;
|
||||
|
||||
logic DelayIsNext;
|
||||
|
||||
// Convenient Delay Reg Names
|
||||
assign cssck = Delay0[7:0];
|
||||
assign sckcs = Delay0[15:8];
|
||||
assign intercs = Delay1[7:0];
|
||||
assign interxfr = Delay1[15:8];
|
||||
|
||||
// Do we have delay for anything?
|
||||
assign HasCSSCK = cssck > 8'b0;
|
||||
assign HasSCKCS = sckcs > 8'b0;
|
||||
assign HasINTERCS = intercs > 8'b0;
|
||||
assign HasINTERXFR = interxfr > 8'b0;
|
||||
|
||||
// Have we hit full delay for any of the delays?
|
||||
assign EndOfCSSCK = CSSCKCounter == cssck;
|
||||
assign EndOfSCKCS = SCKCSCounter == sckcs;
|
||||
assign EndOfINTERCS = INTERCSCounter == intercs;
|
||||
assign EndOfINTERXFR = INTERXFRCounter == interxfr;
|
||||
|
||||
// Clock Signal Stuff -----------------------------------------------
|
||||
// I'm going to handle all clock stuff here, including ShiftEdge and
|
||||
// SampleEdge. This makes sure that SPICLK is an output of a register
|
||||
// and it properly synchronizes signals.
|
||||
|
||||
assign SCLKenableLate = DivCounter > SckDiv;
|
||||
assign SCLKenable = DivCounter == SckDiv;
|
||||
assign SCLKenableEarly = (DivCounter + 1'b1) == SckDiv;
|
||||
assign LastBit = BitNum == 3'd7;
|
||||
assign EdgeTiming = SckDiv > 12'b0 ? SCLKenableEarly : SCLKenable;
|
||||
|
||||
//assign SPICLK = Clock0;
|
||||
|
||||
assign ContinueTransmit = ~txFIFOReadEmpty & EndOfFrame;
|
||||
assign EndTransmission = txFIFOReadEmpty & EndOfFrameDelay;
|
||||
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
DivCounter <= 12'b0;
|
||||
SPICLK <= SckMode[1];
|
||||
SCK <= 0;
|
||||
BitNum <= 3'h0;
|
||||
PreShiftEdge <= 0;
|
||||
PreSampleEdge <= 0;
|
||||
EndOfFrame <= 0;
|
||||
end else begin
|
||||
// TODO: Consolidate into one delay counter since none of the
|
||||
// delays happen at the same time?
|
||||
if (TransmitStart) begin
|
||||
SCK <= 0;
|
||||
end else if (SCLKenable) begin
|
||||
SCK <= ~SCK;
|
||||
end
|
||||
|
||||
if ((CurrState == CSSCK) & SCK) begin
|
||||
CSSCKCounter <= CSSCKCounter + 8'd1;
|
||||
end else begin
|
||||
CSSCKCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == SCKCS) & SCK) begin
|
||||
SCKCSCounter <= SCKCSCounter + 8'd1;
|
||||
end else begin
|
||||
SCKCSCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == INTERCS) & SCK) begin
|
||||
INTERCSCounter <= INTERCSCounter + 8'd1;
|
||||
end else begin
|
||||
INTERCSCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == INTERXFR) & SCK) begin
|
||||
INTERXFRCounter <= INTERXFRCounter + 8'd1;
|
||||
end else begin
|
||||
INTERXFRCounter <= 8'd0;
|
||||
end
|
||||
|
||||
// SPICLK Logic
|
||||
if (TransmitStart) begin
|
||||
SPICLK <= SckMode[1];
|
||||
end else if (SCLKenable & Transmitting) begin
|
||||
SPICLK <= (~EndTransmission & ~DelayIsNext) ? ~SPICLK : SckMode[1];
|
||||
end
|
||||
|
||||
// Reset divider
|
||||
if (SCLKenable | TransmitStart) begin
|
||||
DivCounter <= 12'b0;
|
||||
end else begin
|
||||
DivCounter = DivCounter + 12'd1;
|
||||
end
|
||||
|
||||
// EndOfFrame controller
|
||||
// if (SckDiv > 0 ? SCLKenableEarly & LastBit & SPICLK : LastBit & ~SPICLK) begin
|
||||
// EndOfFrame <= 1'b1;
|
||||
// end else begin
|
||||
// EndOfFrame <= 1'b0;
|
||||
// end
|
||||
|
||||
if (~TransmitStart) begin
|
||||
EndOfFrame <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
|
||||
end
|
||||
|
||||
// Increment BitNum
|
||||
if (ShiftEdge & Transmitting) begin
|
||||
BitNum <= BitNum + 3'd1;
|
||||
end else if (EndOfFrameDelay) begin
|
||||
BitNum <= 3'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Delay ShiftEdge and SampleEdge by a half PCLK period
|
||||
// Aligned EXACTLY ON THE MIDDLE of the leading and trailing edges.
|
||||
// Sweeeeeeeeeet...
|
||||
always_ff @(posedge ~PCLK) begin
|
||||
if (~PRESETn | TransmitStart) begin
|
||||
ShiftEdge <= 0;
|
||||
PhaseOneOffset <= 0;
|
||||
SampleEdge <= 0;
|
||||
EndOfFrameDelay <= 0;
|
||||
end else begin
|
||||
ShiftEdge <= ((SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & ~LastBit & Transmitting) & PhaseOneOffset;
|
||||
PhaseOneOffset <= PhaseOneOffset == 0 ? Transmitting & SCLKenable : PhaseOneOffset;
|
||||
SampleEdge <= (SckMode[1] ^ SckMode[0] ^ ~SPICLK) & SCLKenable & Transmitting;
|
||||
EndOfFrameDelay <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
|
||||
end
|
||||
end
|
||||
|
||||
// typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
|
||||
// statetype CurrState, NextState;
|
||||
|
||||
assign HoldMode = CSMode == 2'b10;
|
||||
assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~txFIFOReadEmpty);
|
||||
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
CurrState <= INACTIVE;
|
||||
end else if (SCLKenable) begin
|
||||
CurrState <= NextState;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
case (CurrState)
|
||||
INACTIVE: begin // INACTIVE case --------------------------------
|
||||
if (TransmitStart) begin
|
||||
if (~HasCSSCK) begin
|
||||
NextState = TRANSMIT;
|
||||
end else begin
|
||||
NextState = CSSCK;
|
||||
end
|
||||
end else begin
|
||||
NextState = INACTIVE;
|
||||
end
|
||||
end
|
||||
CSSCK: begin // DELAY0 case -------------------------------------
|
||||
if (EndOfCSSCK) begin
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
TRANSMIT: begin // TRANSMIT case --------------------------------
|
||||
case(CSMode)
|
||||
AUTOMODE: begin
|
||||
if (EndTransmission) begin
|
||||
NextState = INACTIVE;
|
||||
end else if (ContinueTransmit) begin
|
||||
NextState = SCKCS;
|
||||
end
|
||||
end
|
||||
HOLDMODE: begin
|
||||
if (EndTransmission) begin
|
||||
NextState = HOLD;
|
||||
end else if (ContinueTransmit) begin
|
||||
if (HasINTERXFR) NextState = INTERXFR;
|
||||
end
|
||||
end
|
||||
OFFMODE: begin
|
||||
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
SCKCS: begin // SCKCS case --------------------------------------
|
||||
if (EndOfSCKCS) begin
|
||||
if (EndTransmission) begin
|
||||
if (CSMode == AUTOMODE) NextState = INACTIVE;
|
||||
else if (CSMode == HOLDMODE) NextState = HOLD;
|
||||
end else if (ContinueTransmit) begin
|
||||
if (HasINTERCS) NextState = INTERCS;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
end
|
||||
HOLD: begin // HOLD mode case -----------------------------------
|
||||
if (CSMode == AUTOMODE) begin
|
||||
NextState = INACTIVE;
|
||||
end else if (TransmitStart) begin // If FIFO is written to, start again.
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
INTERCS: begin // INTERCS case ----------------------------------
|
||||
if (EndOfINTERCS) begin
|
||||
if (HasCSSCK) NextState = CSSCK;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
INTERXFR: begin // INTERXFR case --------------------------------
|
||||
if (EndOfINTERXFR) begin
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
NextState = INACTIVE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
assign Transmitting = CurrState == TRANSMIT;
|
||||
assign DelayIsNext = (NextState == CSSCK | NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR);
|
||||
|
||||
//
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
TransmitReg <= 8'b0;
|
||||
end else if (TransmitLoad) begin
|
||||
TransmitReg <= txFIFORead;
|
||||
end else if (ShiftEdge) begin
|
||||
TransmitReg <= {TransmitReg[6:0], TransmitReg[0]};
|
||||
end
|
||||
end
|
||||
|
||||
assign SPIOUT = TransmitReg[7];
|
||||
assign CS = CurrState == INACTIVE | CurrState == INTERCS;
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user