mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-11 06:05:49 +00:00
comments, more test cases
This commit is contained in:
parent
792ddec064
commit
755c055f74
@ -27,23 +27,16 @@
|
|||||||
|
|
||||||
// Current limitations: Flash read sequencer mode not implemented, dual and quad modes untestable with current test plan.
|
// Current limitations: Flash read sequencer mode not implemented, dual and quad modes untestable with current test plan.
|
||||||
// Hardware interlock change to busy signal
|
// Hardware interlock change to busy signal
|
||||||
// relook at fifo empty full logic; might be that watermark level is low when full
|
// write tests for fifo full and empty watermark edge cases
|
||||||
// ChipSelectInternal boolean logic simplification (Harris suggestion)
|
// HoldModeDeassert verilater lint, relook in general
|
||||||
// document timing on loopback testing
|
// Comment on FIFOs: watermark calculations
|
||||||
// change SCLKenable comparison to equals if possible
|
|
||||||
// Explain how sck divider gets to correct value
|
|
||||||
// HoldModeDeassert verilater lint
|
|
||||||
// Comment on FIFOs: readenable, watermark calculations
|
|
||||||
/* high level explanation of architecture
|
/* 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) (
|
module spi_apb import cvw::*; #(parameter cvw_t P) (
|
||||||
input logic PCLK, PRESETn,
|
input logic PCLK, PRESETn,
|
||||||
input logic PSEL,
|
input logic PSEL,
|
||||||
@ -63,24 +56,23 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
|
|
||||||
//SPI registers
|
//SPI registers
|
||||||
|
|
||||||
logic [11:0] SckDiv, HISckDiv;
|
logic [11:0] SckDiv;
|
||||||
logic [1:0] SckMode, HISckMode;
|
logic [1:0] SckMode;
|
||||||
logic [1:0] ChipSelectID, HIChipSelectID;
|
logic [1:0] ChipSelectID;
|
||||||
logic [3:0] ChipSelectDef, HIChipSelectDef;
|
logic [3:0] ChipSelectDef;
|
||||||
logic [1:0] ChipSelectMode, HIChipSelectMode;
|
logic [1:0] ChipSelectMode;
|
||||||
logic [15:0] Delay0, Delay1, HIDelay0, HIDelay1;
|
logic [15:0] Delay0, Delay1;
|
||||||
logic [7:0] Format, HIFormat;
|
logic [7:0] Format;
|
||||||
logic [8:0] ReceiveData;
|
logic [8:0] ReceiveData;
|
||||||
logic [8:0] ReceiveDataPlaceholder;
|
logic [8:0] ReceiveDataPlaceholder;
|
||||||
logic [2:0] TransmitWatermark, ReceiveWatermark, HITransmitWatermark, HIReceiveWatermark;
|
logic [2:0] TransmitWatermark, ReceiveWatermark;
|
||||||
logic [8:0] TransmitData;
|
logic [8:0] TransmitData;
|
||||||
logic [1:0] InterruptEnable, InterruptPending, HIInterruptEnable;
|
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 busy;
|
|
||||||
|
|
||||||
//FIFO FSM signals
|
//FIFO FSM signals
|
||||||
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
|
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
|
||||||
@ -151,12 +143,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
logic ReceiveShiftFullDelayPCLK;
|
logic ReceiveShiftFullDelayPCLK;
|
||||||
logic [2:0] LeftShiftAmount;
|
logic [2:0] LeftShiftAmount;
|
||||||
logic [7:0] ASR; // AlignedReceiveShiftReg
|
logic [7:0] ASR; // AlignedReceiveShiftReg
|
||||||
|
logic DelayMode;
|
||||||
|
|
||||||
|
|
||||||
|
// apb
|
||||||
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 = 1'b1; // tie high if hardware interlock solution doesn't involve bus
|
//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 = 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;
|
ReceiveWatermark <= #1 3'b0;
|
||||||
InterruptEnable <= #1 2'b0;
|
InterruptEnable <= #1 2'b0;
|
||||||
InterruptPending <= #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
|
end else begin //writes
|
||||||
//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
|
||||||
@ -209,32 +192,19 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
/* verilator lint_off CASEINCOMPLETE */
|
/* verilator lint_off CASEINCOMPLETE */
|
||||||
if (Memwrite)
|
if (Memwrite)
|
||||||
case(Entry) //flop to sample inputs
|
case(Entry) //flop to sample inputs
|
||||||
8'h00: HISckDiv <= Din[11:0];
|
8'h00: SckDiv <= Din[11:0];
|
||||||
8'h04: HISckMode <= Din[1:0];
|
8'h04: SckMode <= Din[1:0];
|
||||||
8'h10: HIChipSelectID <= Din[1:0];
|
8'h10: ChipSelectID <= Din[1:0];
|
||||||
8'h14: HIChipSelectDef <= Din[3:0];
|
8'h14: ChipSelectDef <= Din[3:0];
|
||||||
8'h18: HIChipSelectMode <= Din[1:0];
|
8'h18: ChipSelectMode <= Din[1:0];
|
||||||
8'h28: HIDelay0 <= {Din[23:16], Din[7:0]};
|
8'h28: Delay0 <= {Din[23:16], Din[7:0]};
|
||||||
8'h2C: HIDelay1 <= {Din[23:16], Din[7:0]};
|
8'h2C: Delay1 <= {Din[23:16], Din[7:0]};
|
||||||
8'h40: HIFormat <= {Din[19:16], Din[3:0]};
|
8'h40: Format <= {Din[19:16], Din[3:0]};
|
||||||
8'h48: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0];
|
8'h48: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0];
|
||||||
8'h50: HITransmitWatermark <= Din[2:0];
|
8'h50: TransmitWatermark <= Din[2:0];
|
||||||
8'h54: HIReceiveWatermark <= Din[2:0];
|
8'h54: ReceiveWatermark <= Din[2:0];
|
||||||
8'h70: HIInterruptEnable <= Din[1:0];
|
8'h70: InterruptEnable <= Din[1:0];
|
||||||
endcase
|
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 */
|
/* verilator lint_off CASEINCOMPLETE */
|
||||||
//interrupt clearance
|
//interrupt clearance
|
||||||
InterruptPending[0] <= TransmitReadMark;
|
InterruptPending[0] <= TransmitReadMark;
|
||||||
@ -260,10 +230,10 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1))
|
||||||
assign SCLKenable = (DivCounter >= {1'b0,SckDiv});
|
// generates a high signal at the rising and falling edge of SCLK by counting from 0 to SckDiv
|
||||||
assign SCLKenableEarly = ((DivCounter + 13'b1) >= {1'b0, SckDiv});
|
assign SCLKenable = (DivCounter == {1'b0,SckDiv});
|
||||||
//
|
assign SCLKenableEarly = ((DivCounter + 13'b1) == {1'b0, SckDiv});
|
||||||
always_ff @(posedge PCLK, negedge PRESETn)
|
always_ff @(posedge PCLK, negedge PRESETn)
|
||||||
if (~PRESETn) DivCounter <= #1 0;
|
if (~PRESETn) DivCounter <= #1 0;
|
||||||
else if (SCLKenable) DivCounter <= 0;
|
else if (SCLKenable) DivCounter <= 0;
|
||||||
@ -274,10 +244,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
if (~PRESETn) SCLKenableDelay <= 0;
|
if (~PRESETn) SCLKenableDelay <= 0;
|
||||||
else SCLKenableDelay <= SCLKenable;
|
else SCLKenableDelay <= SCLKenable;
|
||||||
|
|
||||||
|
//Boolean logic that tracks frame progression
|
||||||
//SCK_CONTROL
|
|
||||||
//multiplies frame count by 2 or 4 if in dual or quad mode
|
//multiplies frame count by 2 or 4 if in dual or quad mode
|
||||||
|
|
||||||
always_comb
|
always_comb
|
||||||
case(Format[1:0])
|
case(Format[1:0])
|
||||||
2'b00: FrameCountShifted = FrameCount;
|
2'b00: FrameCountShifted = FrameCount;
|
||||||
@ -287,7 +255,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
endcase
|
endcase
|
||||||
|
|
||||||
//Calculates penultimate frame
|
//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
|
//FrameCompareProtocol further adjusts comparison according to dual or quad mode
|
||||||
|
|
||||||
always_comb
|
always_comb
|
||||||
@ -316,7 +284,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
|
|
||||||
endcase
|
endcase
|
||||||
|
|
||||||
//Signals that track frame count comparisons
|
|
||||||
|
|
||||||
assign FrameCompareBoolean = (FrameCountShifted < FrameCompareProtocol);
|
assign FrameCompareBoolean = (FrameCountShifted < FrameCompareProtocol);
|
||||||
assign ReceivePenultimateFrameCount = FrameCountShifted + ReceivePenultimateFrame;
|
assign ReceivePenultimateFrameCount = FrameCountShifted + ReceivePenultimateFrame;
|
||||||
@ -324,8 +291,6 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
|
|
||||||
// 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 Delay0Compare = SckMode[0] ? (Delay0Count >= ({Delay0[7:0], 1'b0})) : (Delay0Count >= ({Delay0[7:0], 1'b0} + 9'b1));
|
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 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}));
|
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]};
|
assign FrameCompare = (Format[0] | Format[1]) ? ({Format[7:4], 1'b0}) : {1'b0,Format[7:4]};
|
||||||
|
|
||||||
// Transmit and Receive FIFOs
|
// Transmit and Receive FIFOs
|
||||||
|
|
||||||
//calculate when tx/rx shift registers are full/empty
|
//calculate when tx/rx shift registers are full/empty
|
||||||
TransmitShiftFSM TransmitShiftFSM_1 (PCLK, PRESETn, TransmitFIFOReadEmpty, ReceivePenultimateFrameBoolean, Active0, TransmitShiftEmpty);
|
TransmitShiftFSM TransmitShiftFSM_1 (PCLK, PRESETn, TransmitFIFOReadEmpty, ReceivePenultimateFrameBoolean, Active0, TransmitShiftEmpty);
|
||||||
ReceiveShiftFSM ReceiveShiftFSM_1 (PCLK, PRESETn, SCLKenable, ReceivePenultimateFrameBoolean, SampleEdge, SckMode[0], ReceiveShiftFull);
|
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 if (~ReceiveFIFOReadIncrement) ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL);
|
||||||
else ReceiveFIFOReadIncrement <= 0;
|
else ReceiveFIFOReadIncrement <= 0;
|
||||||
|
|
||||||
|
TransmitSynchFIFO #(3,8) txFIFO(PCLK, SCLKenable, PRESETn, TransmitFIFOWriteIncrementDelay, TransmitFIFOReadIncrement, TransmitData[7:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0], TransmitFIFOReadData[7:0], TransmitFIFOWriteFull, TransmitFIFOReadEmpty, TransmitWriteMark, TransmitReadMark);
|
||||||
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);
|
|
||||||
ReceiveSynchFIFO #(3,8) rxFIFO(PCLK, SCLKenable, PRESETn, ReceiveFIFOWriteIncrement, ReceiveFIFOReadIncrement, ReceiveShiftRegEndian, ReceiveWatermark[2:0], ReceiveReadWatermarkLevel, ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark);
|
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)
|
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));
|
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;
|
||||||
|
|
||||||
@ -443,10 +403,9 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
|
|
||||||
/* verilator lint_off CASEINCOMPLETE */
|
/* verilator lint_off CASEINCOMPLETE */
|
||||||
|
|
||||||
|
assign DelayMode = SckMode[0] ? (state == DELAY_1) : (state == ACTIVE_1 & ReceiveShiftFull);
|
||||||
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 ChipSelectInternal = (state == CS_INACTIVE | state == INTER_CS | DelayMode & ~|(Delay0[15:8])) ? ChipSelectDef : ~ChipSelectDef;
|
||||||
assign sck = (state == ACTIVE_0) ? ~SckMode[1] : SckMode[1];
|
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 Active = (state == ACTIVE_0 | state == ACTIVE_1);
|
||||||
assign SampleEdge = SckMode[0] ? (state == ACTIVE_1) : (state == ACTIVE_0);
|
assign SampleEdge = SckMode[0] ? (state == ACTIVE_1) : (state == ACTIVE_0);
|
||||||
assign ZeroDelayHoldMode = ((ChipSelectMode == 2'b10) & (~|(Delay1[7:4])));
|
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 Active0 = (state == ACTIVE_0);
|
||||||
assign Inactive = (state == CS_INACTIVE);
|
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)
|
always_ff @(posedge PCLK, negedge PRESETn)
|
||||||
if (~PRESETn) HoldModeDeassert <= 0;
|
if (~PRESETn) HoldModeDeassert <= 0;
|
||||||
else if (Inactive) 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
|
always_comb
|
||||||
case(SckMode[1:0])
|
case(SckMode[1:0])
|
||||||
2'b00: sckPhaseSelect = ~sck & SCLKenable;
|
2'b00: sckPhaseSelect = ~sck & SCLKenable;
|
||||||
@ -473,12 +434,13 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
default: sckPhaseSelect = sck & SCLKenable;
|
default: sckPhaseSelect = sck & SCLKenable;
|
||||||
endcase
|
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)
|
always_ff @(posedge PCLK, negedge PRESETn)
|
||||||
if(~PRESETn) begin
|
if(~PRESETn) begin
|
||||||
TransmitShiftReg <= 8'b0;
|
TransmitShiftReg <= 8'b0;
|
||||||
end
|
end
|
||||||
else if (TransmitShiftRegLoad) TransmitShiftReg <= TransmitFIFOReadData;
|
else if (TransmitShiftRegLoad) TransmitShiftReg <= TransmitDataEndian;
|
||||||
else if (sckPhaseSelect) begin
|
else if (sckPhaseSelect) begin
|
||||||
//if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty) & TransmitShiftEmpty) TransmitShiftReg <= TransmitFIFOReadData;
|
//if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty) & TransmitShiftEmpty) TransmitShiftReg <= TransmitFIFOReadData;
|
||||||
if (Active) begin
|
if (Active) begin
|
||||||
@ -492,7 +454,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
end
|
end
|
||||||
always_comb
|
always_comb
|
||||||
|
|
||||||
//Transmit shift register
|
//Output pin control based on single, dual, or quad mode
|
||||||
if (Active | Delay0Compare | ~TransmitShiftEmpty) begin
|
if (Active | Delay0Compare | ~TransmitShiftEmpty) begin
|
||||||
case(Format[1:0])
|
case(Format[1:0])
|
||||||
2'b00: SPIOut = {3'b0,TransmitShiftReg[7]};
|
2'b00: SPIOut = {3'b0,TransmitShiftReg[7]};
|
||||||
@ -503,6 +465,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
endcase
|
endcase
|
||||||
end else SPIOut = 4'b0;
|
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;
|
assign shiftin = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn;
|
||||||
|
|
||||||
// Receive shift register
|
// Receive shift register
|
||||||
@ -540,9 +504,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
|
|||||||
|
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module TransmitSynchFIFO #(parameter M =3 , N= 8)(
|
module TransmitSynchFIFO #(parameter M =3 , N= 8)(
|
||||||
input logic PCLK, ren, PRESETn,
|
input logic PCLK, sclken, 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,
|
||||||
@ -562,41 +525,43 @@ module TransmitSynchFIFO #(parameter M =3 , N= 8)(
|
|||||||
always_ff @(posedge PCLK)
|
always_ff @(posedge PCLK)
|
||||||
if (winc & ~wfull) mem[waddr] <= wdata;
|
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)
|
always_ff @(posedge PCLK, negedge PRESETn)
|
||||||
if (~PRESETn) rptr <= 0;
|
if (~PRESETn) begin
|
||||||
else if (ren) rptr <= rptrnext;
|
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 raddr = rptr[M-1:0];
|
||||||
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
|
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
|
||||||
|
assign rempty_val = (wptr == rptrnext);
|
||||||
always_ff @(posedge PCLK, negedge PRESETn)
|
assign rwatermark = ((rptr[M-1:0] - wptr[M-1:0]) < rwatermarklevel);
|
||||||
if (~PRESETn) wptr <= 0;
|
|
||||||
else wptr <= wptrnext;
|
|
||||||
|
|
||||||
assign waddr = wptr[M-1:0];
|
assign waddr = wptr[M-1:0];
|
||||||
assign wwatermark = ((wptr[M-1:0] - rptr[M-1:0]) > wwatermarklevel);
|
assign wwatermark = ((wptr[M-1:0] - rptr[M-1:0]) > wwatermarklevel);
|
||||||
assign wptrnext = wptr + {3'b0, (winc & ~wfull)};
|
assign wptrnext = wptr + {3'b0, (winc & ~wfull)};
|
||||||
|
|
||||||
assign rempty_val = (wptr == rptrnext);
|
|
||||||
assign wfull_val = ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
|
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
|
endmodule
|
||||||
|
|
||||||
|
|
||||||
module ReceiveSynchFIFO #(parameter M =3 , N= 8)(
|
module ReceiveSynchFIFO #(parameter M =3 , N= 8)(
|
||||||
input logic PCLK, ren, PRESETn,
|
input logic PCLK, sclken, 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,
|
||||||
@ -615,31 +580,34 @@ module ReceiveSynchFIFO #(parameter M =3 , N= 8)(
|
|||||||
assign rdata = mem[raddr];
|
assign rdata = mem[raddr];
|
||||||
always_ff @(posedge PCLK)
|
always_ff @(posedge PCLK)
|
||||||
if(winc & ~wfull & PCLK) mem[waddr] <= wdata;
|
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)
|
always_ff @(posedge PCLK, negedge PRESETn)
|
||||||
if (~PRESETn) rptr <= 0;
|
if (~PRESETn) begin
|
||||||
else rptr <= rptrnext;
|
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 rwatermark = ((rptr[M-1:0] - wptr[M-1:0]) < rwatermarklevel);
|
||||||
assign raddr = rptr[M-1:0];
|
assign raddr = rptr[M-1:0];
|
||||||
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
|
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
|
||||||
assign rempty_val = (wptr == rptrnext);
|
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 waddr = wptr[M-1:0];
|
||||||
assign wwatermark = ((wptr[M-1:0] - rptr[M-1:0]) > wwatermarklevel);
|
assign wwatermark = ((wptr[M-1:0] - rptr[M-1:0]) > wwatermarklevel);
|
||||||
assign wptrnext = wptr + {3'b0, (winc & ~wfull)};
|
assign wptrnext = wptr + {3'b0, (winc & ~wfull)};
|
||||||
|
|
||||||
assign wfull_val = ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
|
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
|
endmodule
|
||||||
|
|
||||||
|
@ -306,12 +306,26 @@ test_cases:
|
|||||||
.8byte rx_data, 0x000000BC, read32_test
|
.8byte rx_data, 0x000000BC, read32_test
|
||||||
.8byte rx_data, 0x000000AB, 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 format (fmt) register ===========
|
||||||
|
|
||||||
# Test frame length of 4
|
# 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 sck_mode, 0x00000000, write32_test #reset sckmode register
|
||||||
.8byte cs_mode, 0x00000000, write32_test # set cs_mode to AUTO
|
.8byte cs_mode, 0x00000000, write32_test # set cs_mode to AUTO
|
||||||
.8byte fmt, 0x00040000, write32_test # set frame length to 4
|
.8byte fmt, 0x00040000, write32_test # set frame length to 4
|
||||||
|
Loading…
Reference in New Issue
Block a user