comments, more test cases

This commit is contained in:
naichewa 2023-11-01 01:26:34 -07:00
parent 792ddec064
commit 755c055f74
2 changed files with 103 additions and 121 deletions

View File

@ -27,23 +27,16 @@
// Current limitations: Flash read sequencer mode not implemented, dual and quad modes untestable with current test plan.
// Hardware interlock change to busy signal
// relook at fifo empty full logic; might be that watermark level is low when full
// ChipSelectInternal boolean logic simplification (Harris suggestion)
// document timing on loopback testing
// change SCLKenable comparison to equals if possible
// Explain how sck divider gets to correct value
// HoldModeDeassert verilater lint
// Comment on FIFOs: readenable, watermark calculations
// write tests for fifo full and empty watermark edge cases
// HoldModeDeassert verilater lint, relook in general
// Comment on FIFOs: watermark calculations
/* high level explanation of architecture
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 bus 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,
@ -63,24 +56,23 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
//SPI registers
logic [11:0] SckDiv, HISckDiv;
logic [1:0] SckMode, HISckMode;
logic [1:0] ChipSelectID, HIChipSelectID;
logic [3:0] ChipSelectDef, HIChipSelectDef;
logic [1:0] ChipSelectMode, HIChipSelectMode;
logic [15:0] Delay0, Delay1, HIDelay0, HIDelay1;
logic [7:0] Format, HIFormat;
logic [11:0] SckDiv;
logic [1:0] SckMode;
logic [1:0] ChipSelectID;
logic [3:0] ChipSelectDef;
logic [1:0] ChipSelectMode;
logic [15:0] Delay0, Delay1;
logic [7:0] Format;
logic [8:0] ReceiveData;
logic [8:0] ReceiveDataPlaceholder;
logic [2:0] TransmitWatermark, ReceiveWatermark, HITransmitWatermark, HIReceiveWatermark;
logic [2:0] TransmitWatermark, ReceiveWatermark;
logic [8:0] TransmitData;
logic [1:0] InterruptEnable, InterruptPending, HIInterruptEnable;
logic [1:0] InterruptEnable, InterruptPending;
//bus interface signals
logic [7:0] Entry;
logic Memwrite;
logic [31:0] Din, Dout;
logic busy;
//FIFO FSM signals
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
@ -151,12 +143,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
logic ReceiveShiftFullDelayPCLK;
logic [2:0] LeftShiftAmount;
logic [7:0] ASR; // AlignedReceiveShiftReg
logic DelayMode;
// apb
assign Entry = {PADDR[7:2],2'b00}; // 32-bit word-aligned accesses
assign Memwrite = PWRITE & PENABLE & PSEL; // only write in access phase
assign PREADY = 1'b1; // tie high if hardware interlock solution doesn't involve bus
//assign PREADY = TransmitInactive; // tie PREADY to transmission for hardware interlock
//assign PREADY = 1'b1; // tie high if hardware interlock solution doesn't involve bus
assign PREADY = TransmitInactive; // tie PREADY to transmission for hardware interlock
@ -189,17 +183,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
ReceiveWatermark <= #1 3'b0;
InterruptEnable <= #1 2'b0;
InterruptPending <= #1 2'b0;
HISckDiv <= #1 12'd3;
HISckMode <= #1 2'b0;
HIChipSelectID <= #1 2'b0;
HIChipSelectDef <= #1 4'b1111;
HIChipSelectMode <= #1 0;
HIDelay0 <= #1 {8'b1,8'b1};
HIDelay1 <= #1 {8'b0,8'b1};
HIFormat <= #1 {8'b10000000};
HITransmitWatermark <= #1 3'b0;
HIReceiveWatermark <= #1 3'b0;
HIInterruptEnable <= #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
@ -209,32 +192,19 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
/* verilator lint_off CASEINCOMPLETE */
if (Memwrite)
case(Entry) //flop to sample inputs
8'h00: HISckDiv <= Din[11:0];
8'h04: HISckMode <= Din[1:0];
8'h10: HIChipSelectID <= Din[1:0];
8'h14: HIChipSelectDef <= Din[3:0];
8'h18: HIChipSelectMode <= Din[1:0];
8'h28: HIDelay0 <= {Din[23:16], Din[7:0]};
8'h2C: HIDelay1 <= {Din[23:16], Din[7:0]};
8'h40: HIFormat <= {Din[19:16], Din[3:0]};
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]};
8'h40: Format <= {Din[19:16], Din[3:0]};
8'h48: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0];
8'h50: HITransmitWatermark <= Din[2:0];
8'h54: HIReceiveWatermark <= Din[2:0];
8'h70: HIInterruptEnable <= Din[1:0];
8'h50: TransmitWatermark <= Din[2:0];
8'h54: ReceiveWatermark <= Din[2:0];
8'h70: InterruptEnable <= Din[1:0];
endcase
if (TransmitInactive) begin
SckDiv <= HISckDiv;
SckMode <= HISckMode;
ChipSelectID <= HIChipSelectID;
ChipSelectDef <= HIChipSelectDef;
ChipSelectMode <= HIChipSelectMode;
Delay0 <= HIDelay0;
Delay1 <= HIDelay1;
Format <= HIFormat;
TransmitWatermark <= HITransmitWatermark;
ReceiveWatermark <= HIReceiveWatermark;
InterruptEnable <= HIInterruptEnable;
end
/* verilator lint_off CASEINCOMPLETE */
//interrupt clearance
InterruptPending[0] <= TransmitReadMark;
@ -260,10 +230,10 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign SCLKenable = (DivCounter >= {1'b0,SckDiv});
assign SCLKenableEarly = ((DivCounter + 13'b1) >= {1'b0, SckDiv});
//
// 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
assign SCLKenable = (DivCounter == {1'b0,SckDiv});
assign SCLKenableEarly = ((DivCounter + 13'b1) == {1'b0, SckDiv});
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) DivCounter <= #1 0;
else if (SCLKenable) DivCounter <= 0;
@ -274,10 +244,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
if (~PRESETn) SCLKenableDelay <= 0;
else SCLKenableDelay <= SCLKenable;
//SCK_CONTROL
//Boolean logic that tracks frame progression
//multiplies frame count by 2 or 4 if in dual or quad mode
always_comb
case(Format[1:0])
2'b00: FrameCountShifted = FrameCount;
@ -287,7 +255,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
endcase
//Calculates penultimate frame
//Frame compare doubles number of frames in dual or qyad mode to account for half-duplex communication
//Frame compare doubles number of frames in dual or quad mode to account for half-duplex communication
//FrameCompareProtocol further adjusts comparison according to dual or quad mode
always_comb
@ -316,7 +284,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
endcase
//Signals that track frame count comparisons
assign FrameCompareBoolean = (FrameCountShifted < FrameCompareProtocol);
assign ReceivePenultimateFrameCount = FrameCountShifted + ReceivePenultimateFrame;
@ -324,8 +291,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// 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 Delay0Compare = SckMode[0] ? (Delay0Count >= ({Delay0[7:0], 1'b0})) : (Delay0Count >= ({Delay0[7:0], 1'b0} + 9'b1));
assign Delay1Compare = SckMode[0] ? (Delay1Count >= (({Delay0[15:8], 1'b0}) + 9'b1)) : (Delay1Count >= ({Delay0[15:8], 1'b0}));
assign InterCSCompare = (InterCSCount >= ({Delay1[7:0],1'b0}));
@ -336,7 +301,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign FrameCompare = (Format[0] | Format[1]) ? ({Format[7:4], 1'b0}) : {1'b0,Format[7:4]};
// Transmit and Receive FIFOs
//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);
@ -356,10 +320,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
else if (~ReceiveFIFOReadIncrement) ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL);
else ReceiveFIFOReadIncrement <= 0;
assign TransmitDataEndian = Format[2] ? {TransmitData[0], TransmitData[1], TransmitData[2], TransmitData[3], TransmitData[4], TransmitData[5], TransmitData[6], TransmitData[7]} : TransmitData[7:0];
TransmitSynchFIFO #(3,8) txFIFO(PCLK, SCLKenable, PRESETn, TransmitFIFOWriteIncrementDelay, TransmitFIFOReadIncrement, TransmitDataEndian, TransmitWriteWatermarkLevel, TransmitWatermark[2:0], TransmitFIFOReadData[7:0], TransmitFIFOWriteFull, TransmitFIFOReadEmpty, TransmitWriteMark, TransmitReadMark);
TransmitSynchFIFO #(3,8) txFIFO(PCLK, SCLKenable, PRESETn, TransmitFIFOWriteIncrementDelay, TransmitFIFOReadIncrement, TransmitData[7:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0], TransmitFIFOReadData[7:0], TransmitFIFOWriteFull, TransmitFIFOReadEmpty, TransmitWriteMark, TransmitReadMark);
ReceiveSynchFIFO #(3,8) rxFIFO(PCLK, SCLKenable, PRESETn, ReceiveFIFOWriteIncrement, ReceiveFIFOReadIncrement, ReceiveShiftRegEndian, ReceiveWatermark[2:0], ReceiveReadWatermarkLevel, ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark);
always_ff @(posedge PCLK, negedge PRESETn)
@ -377,7 +338,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign TransmitShiftRegLoad = ~TransmitShiftEmpty & ~Active | (((ChipSelectMode == 2'b10) & ~|(Delay1[15:8])) & ((ReceiveShiftFullDelay | ReceiveShiftFull) & ~SampleEdge & ~TransmitFIFOReadEmpty));
//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;
statetype state;
@ -443,10 +403,9 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
/* verilator lint_off CASEINCOMPLETE */
assign ChipSelectInternal = SckMode[0] ? ((state == CS_INACTIVE | state == INTER_CS | (state == DELAY_1 & ~|(Delay0[15:8]))) ? ChipSelectDef : ~ChipSelectDef) : ((state == CS_INACTIVE | state == INTER_CS | (state == ACTIVE_1 & ~|(Delay0[15:8]) & ReceiveShiftFull)) ? ChipSelectDef : ~ChipSelectDef);
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 sck = (state == ACTIVE_0) ? ~SckMode[1] : SckMode[1];
assign busy = (state == DELAY_0 | state == ACTIVE_0 | ((state == ACTIVE_1) & ~((|(Delay1[15:8]) & (ChipSelectMode[1:0]) == 2'b10) & ((FrameCount << Format[1:0]) >= FrameCompare))) | state == DELAY_1);
assign Active = (state == ACTIVE_0 | state == ACTIVE_1);
assign SampleEdge = SckMode[0] ? (state == ACTIVE_1) : (state == ACTIVE_0);
assign ZeroDelayHoldMode = ((ChipSelectMode == 2'b10) & (~|(Delay1[7:4])));
@ -454,6 +413,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign Active0 = (state == ACTIVE_0);
assign Inactive = (state == CS_INACTIVE);
// Ensures that when ChipSelectMode = hold, CS pin is deasserted only when a different value is written to csmode or csid or a write to csdeg changes the state
// of the selected pin
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) HoldModeDeassert <= 0;
else if (Inactive) HoldModeDeassert <= 0;
@ -463,7 +424,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// Signal tracks which edge of sck to shift data
always_comb
case(SckMode[1:0])
2'b00: sckPhaseSelect = ~sck & SCLKenable;
@ -473,12 +434,13 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
default: sckPhaseSelect = sck & SCLKenable;
endcase
//Transmit shift register
assign TransmitDataEndian = Format[2] ? {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)
if(~PRESETn) begin
TransmitShiftReg <= 8'b0;
end
else if (TransmitShiftRegLoad) TransmitShiftReg <= TransmitFIFOReadData;
else if (TransmitShiftRegLoad) TransmitShiftReg <= TransmitDataEndian;
else if (sckPhaseSelect) begin
//if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty) & TransmitShiftEmpty) TransmitShiftReg <= TransmitFIFOReadData;
if (Active) begin
@ -492,7 +454,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
end
always_comb
//Transmit shift register
//Output pin control based on single, dual, or quad mode
if (Active | Delay0Compare | ~TransmitShiftEmpty) begin
case(Format[1:0])
2'b00: SPIOut = {3'b0,TransmitShiftReg[7]};
@ -503,6 +465,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
endcase
end else SPIOut = 4'b0;
//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
assign shiftin = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn;
// Receive shift register
@ -540,9 +504,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
endmodule
module TransmitSynchFIFO #(parameter M =3 , N= 8)(
input logic PCLK, ren, PRESETn,
input logic PCLK, sclken, PRESETn,
input logic winc,rinc,
input logic [N-1:0] wdata,
input logic [M-1:0] wwatermarklevel, rwatermarklevel,
@ -562,41 +525,43 @@ module TransmitSynchFIFO #(parameter M =3 , N= 8)(
always_ff @(posedge PCLK)
if (winc & ~wfull) mem[waddr] <= wdata;
// read flops are only enabled on sclk edges b/c transmit fifo is read on sclk
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) rptr <= 0;
else if (ren) rptr <= rptrnext;
if (~PRESETn) begin
rptr <= 0;
wptr <= 0;
wfull <= 1'b0;
rempty <= 1'b1;
end
else begin
wfull <= wfull_val;
wptr <= wptrnext;
if (sclken) begin
rptr <= rptrnext;
rempty <= rempty_val;
end
end
assign raddr = rptr[M-1:0];
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) wptr <= 0;
else wptr <= wptrnext;
assign rempty_val = (wptr == rptrnext);
assign rwatermark = ((rptr[M-1:0] - wptr[M-1:0]) < rwatermarklevel);
assign waddr = wptr[M-1:0];
assign wwatermark = ((wptr[M-1:0] - rptr[M-1:0]) > wwatermarklevel);
assign wptrnext = wptr + {3'b0, (winc & ~wfull)};
assign rempty_val = (wptr == rptrnext);
assign wfull_val = ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
assign rwatermark = ((rptr[M-1:0] - wptr[M-1:0]) < rwatermarklevel);
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) wfull <= 1'b0;
else wfull <= wfull_val;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) rempty <= 1'b1;
else if (ren) rempty <= rempty_val;
endmodule
module ReceiveSynchFIFO #(parameter M =3 , N= 8)(
input logic PCLK, ren, PRESETn,
input logic PCLK, sclken, PRESETn,
input logic winc,rinc,
input logic [N-1:0] wdata,
input logic [M-1:0] wwatermarklevel, rwatermarklevel,
@ -615,31 +580,34 @@ module ReceiveSynchFIFO #(parameter M =3 , N= 8)(
assign rdata = mem[raddr];
always_ff @(posedge PCLK)
if(winc & ~wfull & PCLK) mem[waddr] <= wdata;
//write flops are enabled only on sclk edges b/c receive fifo is written then
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) rptr <= 0;
else rptr <= rptrnext;
if (~PRESETn) begin
rptr <= 0;
wptr <= 0;
wfull <= 0;
rempty <= 0;
end else begin
rptr <= rptrnext;
rempty <= rempty_val;
if (sclken) begin
wptr <= wptrnext;
wfull <= wfull_val;
end
end
assign rwatermark = ((rptr[M-1:0] - wptr[M-1:0]) < rwatermarklevel);
assign raddr = rptr[M-1:0];
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
assign rempty_val = (wptr == rptrnext);
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) rempty <= 1'b1;
else rempty <= rempty_val;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) wptr <= 0;
else if (ren) wptr <= wptrnext;
assign waddr = wptr[M-1:0];
assign wwatermark = ((wptr[M-1:0] - rptr[M-1:0]) > wwatermarklevel);
assign wptrnext = wptr + {3'b0, (winc & ~wfull)};
assign wfull_val = ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) wfull <= 1'b0;
else if (ren) wfull <= wfull_val;
endmodule

View File

@ -306,12 +306,26 @@ test_cases:
.8byte rx_data, 0x000000BC, read32_test
.8byte rx_data, 0x000000AB, read32_test
# Test hold mode deassert conditions
.8byte delay1, 0x00000001, write32_test # reset delay1 register
.8byte delay0, 0x00010001, write32_test # reset delay0 register
.8byte cs_mode, 0x00000002, write32_test # set cs_mode to hold
.8byte tx_data, 0x000000CE, write32_test # place data into tx_data
.8byte cs_id, 0x00000001, write32_test #change selected cs pin. should deassert cs[0] in hold mode
.8byte cs_def, 0x00001101, write32_test # change selected cs pins def value. should deassert cs[1]
.8byte cs_def, 0x00001111, write32_test # reset cs_def
.8byte cs_mode, 0x00000000, write32_test # change cs_mode to auto, should deassert cs[1], have now gone through all deassertion conditions
.8byte rx_data, 0x000000CE, read32_test # clear rx_fifo
# Test transmit and receive fifo full edge cases
# =========== Test frame format (fmt) register ===========
# Test frame length of 4
.8byte delay1, 0x00000001, write32_test # reset delay1 register
.8byte delay0, 0x00010001, write32_test # reset delay0 register
.8byte sck_mode, 0x00000000, write32_test #reset sckmode register
.8byte cs_mode, 0x00000000, write32_test # set cs_mode to AUTO
.8byte fmt, 0x00040000, write32_test # set frame length to 4