Merge branch 'main' of https://github.com/openhwgroup/cvw into dev

This commit is contained in:
David Harris 2023-11-14 19:14:03 -08:00
commit 3308550409

View File

@ -2,10 +2,14 @@
// spi_apb.sv
//
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu 11/16/2022
//
// 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.
//
@ -25,19 +29,6 @@
// 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) (
input logic PCLK, PRESETn,
input logic PSEL,
@ -83,12 +74,12 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty;
logic [7:0] TransmitFIFOReadData, ReceiveFIFOWriteData;
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
logic sck;
logic [11:0] DivCounter; //counter for sck
logic SCLKenable; //flip flop enable high every sclk edge
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
@ -97,18 +88,12 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
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 CS_SCKCompare; //Boolean comparison signal, high when CS_SCKCount >= cs-sck delay
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
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] ReceivePenultimateFrame; //Frame number - 1
logic [3:0] ReceivePenultimateFrameCount; // Counter
logic ReceivePenultimateFrameBoolean; //High when penultimate frame in transmission has been reached
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)
@ -134,15 +119,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// Miscellaneous signals delayed/early 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 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
// 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
assign Memwrite = PWRITE & PENABLE & PSEL; // Only write in access phase
assign PREADY = TransmitInactive; // Tie PREADY to transmission for hardware interlock
// Account for subword read/write circuitry
// -- Note SPI registers are 32 bits no matter what; access them with LW SW.
@ -168,8 +152,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
InterruptEnable <= #1 2'b0;
InterruptPending <= #1 2'b0;
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 */
if (Memwrite & TransmitInactive)
@ -188,18 +171,21 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
8'h70: InterruptEnable <= Din[1:0];
endcase
/* 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[1] <= RecieveWriteMark;
case(Entry) // flop to sample inputs
case(Entry) // Flop to sample inputs
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};
8'h28: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]};
8'h2C: Dout <= {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'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};
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};
@ -211,7 +197,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end
// 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 SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv);
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 DivCounter <= DivCounter + 12'b1;
//Boolean logic that tracks frame progression
assign FrameCompare = (FrameCount < Format[4:1]);
assign ReceivePenultimateFrameBoolean = ((FrameCount + 4'b0001) == Format[4:1]);
// Asserts when transmission is one frame before complete
assign ReceivePenultimateFrame = ((FrameCount + 4'b0001) == Format[4:1]);
// Computing delays
// 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 ImplicitDelay2 = SckMode[0] ? 9'b1 : 9'b0;
assign CS_SCKCompare = CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1);
assign SCK_CSCompare = SCK_CSCount >= (({Delay0[15:8], 1'b0}) + ImplicitDelay2);
assign InterCSCompare = (InterCSCount >= ({Delay1[7:0],1'b0}));
assign InterXFRCompare = (InterXFRCount >= ({Delay1[15:8], 1'b0}));
// Calculate when tx/rx shift registers are full/empty
TransmitShiftFSM TransmitShiftFSM_1 (PCLK, PRESETn, TransmitFIFOReadEmpty, ReceivePenultimateFrameBoolean, Active0, TransmitShiftEmpty);
ReceiveShiftFSM ReceiveShiftFSM_1 (PCLK, PRESETn, SCLKenable, ReceivePenultimateFrameBoolean, SampleEdge, SckMode[0], ReceiveShiftFull);
TransmitShiftFSM TransmitShiftFSM(PCLK, PRESETn, TransmitFIFOReadEmpty, ReceivePenultimateFrame, Active0, TransmitShiftEmpty);
ReceiveShiftFSM ReceiveShiftFSM(PCLK, PRESETn, SCLKenable, ReceivePenultimateFrame, 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)
if (~PRESETn) TransmitFIFOWriteIncrementDelay <= 0;
else TransmitFIFOWriteIncrementDelay <= TransmitFIFOWriteIncrement;
if (~PRESETn) TransmitFIFOWriteIncrement <= 0;
else TransmitFIFOWriteIncrement <= (Memwrite & (Entry == 8'h48) & ~TransmitFIFOWriteFull & TransmitInactive);
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveFIFOReadIncrement <= 0;
else ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement);
// 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) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn, ReceiveShiftFullDelay, ReceiveFIFOReadIncrement, ReceiveShiftRegEndian, ReceiveWatermark[2:0], ReceiveReadWatermarkLevel, ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark);
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);
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;
@ -271,11 +252,11 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
statetype state;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin state <= CS_INACTIVE;
if (~PRESETn) begin
state <= CS_INACTIVE;
FrameCount <= 4'b0;
/* verilator lint_off CASEINCOMPLETE */
end else if (SCLKenable) begin
/* verilator lint_off CASEINCOMPLETE */
case (state)
CS_INACTIVE: begin
CS_SCKCount <= 9'b1;
@ -288,7 +269,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end
DELAY_0: begin
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
ACTIVE_0: begin
FrameCount <= FrameCount + 4'b1;
@ -296,7 +277,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end
ACTIVE_1: begin
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
state <= ACTIVE_0;
CS_SCKCount <= 9'b1;
@ -310,11 +291,11 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end
DELAY_1: begin
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
INTER_CS: begin
InterCSCount <= InterCSCount + 9'b1;
if (InterCSCompare ) state <= CS_INACTIVE;
if (InterCSCount >= ({Delay1[7:0],1'b0})) state <= CS_INACTIVE;
end
INTER_XFR: begin
CS_SCKCount <= 9'b1;
@ -322,13 +303,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
FrameCount <= 4'b0;
InterCSCount <= 9'b10;
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;
end
endcase
/* verilator lint_off CASEINCOMPLETE */
end
/* verilator lint_off CASEINCOMPLETE */
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;
@ -390,7 +372,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto;
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 winc, rinc,
input logic [N-1:0] wdata,
@ -409,8 +391,6 @@ module SynchFIFO #(parameter M =3 , N= 8)(
logic [N-1:0] mem[2**M];
logic [M:0] rptr, wptr;
logic [M:0] rptrnext, wptrnext;
logic rempty_val;
logic wfull_val;
logic [M-1:0] raddr;
logic [M-1:0] waddr;
@ -428,53 +408,43 @@ module SynchFIFO #(parameter M =3 , N= 8)(
end
else begin
if (wen) begin
wfull <= wfull_val;
wfull <= ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
wptr <= wptrnext;
end
if (ren) begin
rptr <= rptrnext;
rempty <= rempty_val;
rempty <= (wptr == rptrnext);
end
end
assign raddr = rptr[M-1:0];
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
assign rempty_val = (wptr == rptrnext);
assign rptrnext = rptr + {{(M){1'b0}}, (rinc & ~rempty)};
assign rwatermark = ((waddr - raddr) < rwatermarklevel) & ~wfull;
assign waddr = wptr[M-1:0];
assign wwatermark = ((waddr - raddr) > wwatermarklevel) | wfull;
assign wptrnext = wptr + {3'b0, (winc & ~wfull)};
assign wfull_val = ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
assign wptrnext = wptr + {{(M){1'b0}}, (winc & ~wfull)};
endmodule
module TransmitShiftFSM(
input logic PCLK, PRESETn,
input logic TransmitFIFOReadEmpty, ReceivePenultimateFrameBoolean, Active0,
input logic TransmitFIFOReadEmpty, ReceivePenultimateFrame, Active0,
output logic TransmitShiftEmpty);
typedef enum logic [1:0] {TransmitShiftEmptyState, TransmitShiftHoldState, TransmitShiftNotEmptyState} statetype;
statetype TransmitState, TransmitNextState;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitState <= TransmitShiftEmptyState;
else TransmitState <= TransmitNextState;
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
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
module ReceiveShiftFSM(
input logic PCLK, PRESETn, SCLKenable,
input logic ReceivePenultimateFrameBoolean, SampleEdge, SckMode,
input logic ReceivePenultimateFrame, SampleEdge, SckMode,
output logic ReceiveShiftFull
);
typedef enum logic [1:0] {ReceiveShiftFullState, ReceiveShiftNotFullState, ReceiveShiftDelayState} statetype;
@ -484,7 +454,7 @@ module ReceiveShiftFSM(
else if (SCLKenable) begin
case (ReceiveState)
ReceiveShiftFullState: ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftNotFullState: if (ReceivePenultimateFrameBoolean & (SampleEdge)) ReceiveState <= ReceiveShiftDelayState;
ReceiveShiftNotFullState: if (ReceivePenultimateFrame & (SampleEdge)) ReceiveState <= ReceiveShiftDelayState;
else ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftDelayState: ReceiveState <= ReceiveShiftFullState;
endcase
@ -493,8 +463,3 @@ module ReceiveShiftFSM(
assign ReceiveShiftFull = SckMode ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState);
endmodule