cvw/src/uncore/spi_apb.sv

840 lines
34 KiB
Systemverilog
Raw Normal View History

///////////////////////////////////////////
// spi_apb.sv
//
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu 11/16/2022
//
// Purpose: SPI peripheral
// See FU540-C000-v1.0 for specifications
//
// A component of the Wally configurable RISC-V project.
//
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
// may obtain a copy of the License at
//
// https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work distributed under the
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
////////////////////////////////////////////////////////////////////////////////////////////////
// Current limitations: Flash read sequencer mode not implemented, dual and quad modes untestable with current test plan.
2023-10-31 19:27:41 +00:00
// Hardware interlock change to busy signal
2023-11-01 08:26:34 +00:00
// write tests for fifo full and empty watermark edge cases
// HoldModeDeassert verilater lint, relook in general
// Comment on FIFOs: watermark calculations
2023-10-31 19:27:41 +00:00
/* high level explanation of architecture
2023-11-01 08:26:34 +00:00
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.
2023-10-31 19:27:41 +00:00
*/
2023-10-31 00:00:20 +00:00
module spi_apb import cvw::*; #(parameter cvw_t P) (
input logic PCLK, PRESETn,
input logic PSEL,
input logic [7:0] PADDR,
input logic [P.XLEN-1:0] PWDATA,
input logic [P.XLEN/8-1:0] PSTRB,
input logic PWRITE,
input logic PENABLE,
output logic PREADY,
output logic [P.XLEN-1:0] PRDATA,
output logic [3:0] SPIOut,
input logic [3:0] SPIIn,
output logic [3:0] SPICS,
output logic SPIIntr
);
//SPI registers
2023-11-01 08:26:34 +00:00
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;
2023-11-01 08:26:34 +00:00
logic [2:0] TransmitWatermark, ReceiveWatermark;
logic [8:0] TransmitData;
2023-11-01 08:26:34 +00:00
logic [1:0] InterruptEnable, InterruptPending;
//bus interface signals
logic [7:0] Entry;
logic Memwrite;
logic [31:0] Din, Dout;
//FIFO FSM signals
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty;
logic TransmitFIFOWriteIncrement, TransmitFIFOReadIncrement;
logic ReceiveFIFOWriteIncrement, ReceiveFIFOReadIncrement;
logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty;
logic [7:0] TransmitFIFOReadData, ReceiveFIFOWriteData;
logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel;
logic TransmitFIFOReadEmptyDelay;
logic [7:0] ReceiveShiftRegEndian;
//transmission signals
logic sck;
logic [12:0] DivCounter;
2023-10-31 00:00:20 +00:00
logic SCLKenable;
logic [8:0] Delay0Count;
logic [8:0] Delay1Count;
logic Delay0Compare;
logic Delay1Compare;
logic InterCSCompare;
logic [8:0] InterCSCount;
logic InterXFRCompare;
logic [8:0] InterXFRCount;
logic [3:0] ChipSelectInternal;
logic [4:0] FrameCount;
logic [4:0] FrameCompare;
logic FrameCompareBoolean;
logic [4:0] FrameCountShifted;
logic [4:0] ReceivePenultimateFrame;
logic [4:0] ReceivePenultimateFrameCount;
logic ReceivePenultimateFrameBoolean;
logic [4:0] FrameCompareProtocol;
logic ReceiveShiftFull;
logic TransmitShiftEmpty;
logic HoldModeDeassert;
//state fsm signals
logic Active;
logic Active0;
logic Inactive;
//shift reg signals
logic TransmitFIFOWriteIncrementDelay;
logic sckPhaseSelect;
logic [7:0] TransmitShiftReg;
logic [7:0] ReceiveShiftReg;
logic SampleEdge;
logic [7:0] TransmitDataEndian;
logic TransmitShiftRegLoad;
//CS signals
logic [3:0] ChipSelectAuto, ChipSelectHold, CSoff;
logic ChipSelectHoldSingle;
logic ReceiveShiftFullDelay;
2023-10-31 00:00:20 +00:00
logic SCLKenableDelay;
logic [3:0] shiftin;
logic [7:0] ReceiveShiftRegInvert;
logic ZeroDelayHoldMode;
logic TransmitInactive;
logic SCLKenableEarly;
logic ReceiveShiftFullDelayPCLK;
2023-10-31 19:27:41 +00:00
logic [2:0] LeftShiftAmount;
logic [7:0] ASR; // AlignedReceiveShiftReg
2023-11-01 08:26:34 +00:00
logic DelayMode;
2023-11-01 08:26:34 +00:00
// apb
assign Entry = {PADDR[7:2],2'b00}; // 32-bit word-aligned accesses
assign Memwrite = PWRITE & PENABLE & PSEL; // only write in access phase
2023-11-01 08:26:34 +00:00
//assign PREADY = 1'b1; // tie high if hardware interlock solution doesn't involve bus
assign PREADY = TransmitInactive; // tie PREADY to transmission for hardware interlock
2023-10-31 00:00:20 +00:00
// account for subword read/write circuitry
// -- Note GPIO registers are 32 bits no matter what; access them with LW SW.
// (At least that's what I think when FE310 spec says "only naturally aligned 32-bit accesses are supported")
if (P.XLEN == 64) begin
assign Din = Entry[2] ? PWDATA[63:32] : PWDATA[31:0];
assign PRDATA = Entry[2] ? {Dout,32'b0} : {32'b0,Dout};
end else begin // 32-bit
assign Din = PWDATA[31:0];
assign PRDATA = Dout;
end
// register access
always_ff@(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin
SckDiv <= #1 12'd3;
SckMode <= #1 2'b0;
ChipSelectID <= #1 2'b0;
ChipSelectDef <= #1 4'b1111;
ChipSelectMode <= #1 0;
Delay0 <= #1 {8'b1,8'b1};
Delay1 <= #1 {8'b0,8'b1};
Format <= #1 {8'b10000000};
TransmitData <= #1 9'b0;
//ReceiveData <= #1 9'b100000000;
TransmitWatermark <= #1 3'b0;
ReceiveWatermark <= #1 3'b0;
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
//From spec. "Hardware interlocks ensure that the current transfer completes before mode transitions and control register updates take effect"
// Interpreting 'current transfer' as one frame
/* verilator lint_off CASEINCOMPLETE */
if (Memwrite)
case(Entry) //flop to sample inputs
2023-11-01 08:26:34 +00:00
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];
2023-11-01 08:26:34 +00:00
8'h50: TransmitWatermark <= Din[2:0];
8'h54: ReceiveWatermark <= Din[2:0];
8'h70: InterruptEnable <= Din[1:0];
endcase
/* verilator lint_off CASEINCOMPLETE */
//interrupt clearance
InterruptPending[0] <= TransmitReadMark;
InterruptPending[1] <= RecieveWriteMark;
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[7:4], 12'b0, Format[3:0]};
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};
8'h54: Dout <= #1 {29'b0, ReceiveWatermark};
8'h70: Dout <= #1 {30'b0, InterruptEnable};
8'h74: Dout <= #1 {30'b0, InterruptPending};
default: Dout <= #1 32'b0;
endcase
end
2023-10-31 00:00:20 +00:00
2023-11-01 08:26:34 +00:00
// 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});
2023-10-31 00:00:20 +00:00
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) DivCounter <= #1 0;
else if (SCLKenable) DivCounter <= 0;
else DivCounter <= DivCounter + 13'b1;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) SCLKenableDelay <= 0;
else SCLKenableDelay <= SCLKenable;
2023-11-01 08:26:34 +00:00
//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;
2'b01: FrameCountShifted = {FrameCount[3:0], 1'b0};
2'b10: FrameCountShifted = {FrameCount[2:0], 2'b0};
default: FrameCountShifted = FrameCount;
endcase
//Calculates penultimate frame
2023-11-01 08:26:34 +00:00
//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
case(Format[1:0])
2'b00: begin
ReceivePenultimateFrame = 5'b00001;
FrameCompareProtocol = FrameCompare;
end
2'b01: begin
ReceivePenultimateFrame = 5'b00010;
//add 1 to count if # of bits is odd so doubled # will be correct
// for ex. 5 bits needs 3 frames, 5*2 = 10 which will be reached in 5 frames not 3*2.
FrameCompareProtocol = Format[4] ? FrameCompare + 5'b1 : FrameCompare;
end
2'b10: begin
ReceivePenultimateFrame = 5'b00100;
//if frame len =< 4, need 2 frames (one to send 1-4 bits, one to recieve)
//else, 4 < frame len =<8 8, which by same logic needs 4 frames
if (Format[7:4] > 4'b0100) FrameCompareProtocol = 5'b10000;
else FrameCompareProtocol = 5'b01000;
end
default: begin
ReceivePenultimateFrame = 5'b00001;
FrameCompareProtocol = FrameCompare;
end
endcase
assign FrameCompareBoolean = (FrameCountShifted < FrameCompareProtocol);
assign ReceivePenultimateFrameCount = FrameCountShifted + ReceivePenultimateFrame;
assign ReceivePenultimateFrameBoolean = (ReceivePenultimateFrameCount >= FrameCompareProtocol);
// 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}));
assign InterXFRCompare = (InterXFRCount >= ({Delay1[15:8], 1'b0}));
// double number of frames in dual or quad mode because we must wait for peripheral to send back
assign FrameCompare = (Format[0] | Format[1]) ? ({Format[7:4], 1'b0}) : {1'b0,Format[7:4]};
2023-10-31 00:00:20 +00:00
// 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);
//calculate tx/rx fifo write and recieve increment signals
assign TransmitFIFOWriteIncrement = (Memwrite & (Entry == 8'h48) & ~TransmitFIFOWriteFull);
2023-10-31 00:00:20 +00:00
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitFIFOWriteIncrementDelay <= 0;
else TransmitFIFOWriteIncrementDelay <= TransmitFIFOWriteIncrement;
2023-10-31 00:00:20 +00:00
assign TransmitFIFOReadIncrement = TransmitShiftEmpty;
assign ReceiveFIFOWriteIncrement = ReceiveShiftFullDelay;
always_ff @(posedge PCLK, negedge PRESETn)
2023-10-31 00:00:20 +00:00
if (~PRESETn) ReceiveFIFOReadIncrement <= 0;
else if (~ReceiveFIFOReadIncrement) ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL);
else ReceiveFIFOReadIncrement <= 0;
2023-11-01 08:26:34 +00:00
TransmitSynchFIFO #(3,8) txFIFO(PCLK, SCLKenable, PRESETn, TransmitFIFOWriteIncrementDelay, TransmitFIFOReadIncrement, TransmitData[7:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0], TransmitFIFOReadData[7:0], TransmitFIFOWriteFull, TransmitFIFOReadEmpty, TransmitWriteMark, TransmitReadMark);
2023-10-31 00:00:20 +00:00
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)
if (~PRESETn) TransmitFIFOReadEmptyDelay <= 1;
else if (SCLKenable) TransmitFIFOReadEmptyDelay <= TransmitFIFOReadEmpty;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveShiftFullDelay <= 0;
else if (SCLKenable) ReceiveShiftFullDelay <= ReceiveShiftFull;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveShiftFullDelayPCLK <= 0;
else if (SCLKenableEarly) ReceiveShiftFullDelayPCLK <= ReceiveShiftFull;
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;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin state <= CS_INACTIVE;
FrameCount <= 5'b0;
/* verilator lint_off CASEINCOMPLETE */
2023-10-31 00:00:20 +00:00
end else if (SCLKenable) begin
case (state)
CS_INACTIVE: begin
Delay0Count <= 9'b1;
Delay1Count <= 9'b10;
FrameCount <= 5'b0;
InterCSCount <= 9'b10;
InterXFRCount <= 9'b1;
if ((~TransmitFIFOReadEmpty | ~TransmitShiftEmpty) & ((|(Delay0[7:0])) | ~SckMode[0])) state <= DELAY_0;
else if ((~TransmitFIFOReadEmpty | ~TransmitShiftEmpty)) state <= ACTIVE_0;
end
DELAY_0: begin
Delay0Count <= Delay0Count + 9'b1;
if (Delay0Compare) state <= ACTIVE_0;
end
ACTIVE_0: begin
FrameCount <= FrameCount + 5'b1;
state <= ACTIVE_1;
end
ACTIVE_1: begin
InterXFRCount <= 9'b1;
if (FrameCompareBoolean) state <= ACTIVE_0;
else if (HoldModeDeassert) state <= CS_INACTIVE;
else if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty)) begin
state <= ACTIVE_0;
Delay0Count <= 9'b1;
Delay1Count <= 9'b10;
FrameCount <= 5'b0;
InterCSCount <= 9'b10;
end
else if (ChipSelectMode[1:0] == 2'b10) state <= INTER_XFR;
else if (~|(Delay0[15:8]) & (~SckMode[0])) state <= INTER_CS;
else state <= DELAY_1;
end
DELAY_1: begin
Delay1Count <= Delay1Count + 9'b1;
if (Delay1Compare) state <= INTER_CS;
end
INTER_CS: begin
InterCSCount <= InterCSCount + 9'b1;
if (InterCSCompare ) state <= CS_INACTIVE;
end
INTER_XFR: begin
Delay0Count <= 9'b1;
Delay1Count <= 9'b10;
FrameCount <= 5'b0;
InterCSCount <= 9'b10;
InterXFRCount <= InterXFRCount + 9'b1;
if (HoldModeDeassert) state <= CS_INACTIVE;
else if (InterXFRCompare & ~TransmitFIFOReadEmptyDelay) state <= ACTIVE_0;
else if (~|ChipSelectMode[1:0]) state <= CS_INACTIVE;
end
endcase
end
2023-10-31 00:00:20 +00:00
/* verilator lint_off CASEINCOMPLETE */
2023-11-01 08:26:34 +00:00
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 Active = (state == ACTIVE_0 | state == ACTIVE_1);
2023-10-31 00:00:20 +00:00
assign SampleEdge = SckMode[0] ? (state == ACTIVE_1) : (state == ACTIVE_0);
assign ZeroDelayHoldMode = ((ChipSelectMode == 2'b10) & (~|(Delay1[7:4])));
assign TransmitInactive = ((state == INTER_CS) | (state == CS_INACTIVE) | (state == INTER_XFR) | (ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode));
assign Active0 = (state == ACTIVE_0);
assign Inactive = (state == CS_INACTIVE);
2023-11-01 08:26:34 +00:00
// 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;
/* verilator lint_off WIDTH */
else if (((ChipSelectMode[1:0] == 2'b10) & (Entry == (8'h18 | 8'h10) | ((Entry == 8'h14) & ((PWDATA[ChipSelectID]) != ChipSelectDef[ChipSelectID])))) & Memwrite) HoldModeDeassert <= 1;
/* verilator lint_on WIDTH */
2023-11-01 08:26:34 +00:00
// Signal tracks which edge of sck to shift data
always_comb
case(SckMode[1:0])
2023-10-31 00:00:20 +00:00
2'b00: sckPhaseSelect = ~sck & SCLKenable;
2'b01: sckPhaseSelect = (sck & |(FrameCount) & SCLKenable);
2'b10: sckPhaseSelect = sck & SCLKenable;
2'b11: sckPhaseSelect = (~sck & |(FrameCount) & SCLKenable);
default: sckPhaseSelect = sck & SCLKenable;
endcase
2023-11-01 08:26:34 +00:00
//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
2023-11-01 08:26:34 +00:00
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
case (Format[1:0])
2'b00: TransmitShiftReg <= {TransmitShiftReg[6:0], 1'b0};
2'b01: TransmitShiftReg <= {TransmitShiftReg[5:0], 2'b0};
2'b10: TransmitShiftReg <= {TransmitShiftReg[3:0], 4'b0};
default: TransmitShiftReg <= {TransmitShiftReg[6:0], 1'b0};
endcase
end
end
always_comb
2023-10-31 19:27:41 +00:00
2023-11-01 08:26:34 +00:00
//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]};
2'b01: SPIOut = {2'b0,TransmitShiftReg[6], TransmitShiftReg[7]};
// assuming SPIOut[0] is first bit transmitted etc
2'b10: SPIOut = {TransmitShiftReg[3], TransmitShiftReg[2], TransmitShiftReg[1], TransmitShiftReg[0]};
default: SPIOut = {3'b0, TransmitShiftReg[7]};
endcase
end else SPIOut = 4'b0;
2023-10-31 00:00:20 +00:00
2023-11-01 08:26:34 +00:00
//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;
2023-10-31 19:27:41 +00:00
// Receive shift register
always_ff @(posedge PCLK, negedge PRESETn)
if(~PRESETn) ReceiveShiftReg <= 8'b0;
2023-10-31 00:00:20 +00:00
else if (SampleEdge & SCLKenable) begin
if (~Active) ReceiveShiftReg <= 8'b0;
else if (~Format[3]) begin
case(Format[1:0])
2'b00: ReceiveShiftReg <= { ReceiveShiftReg[6:0], shiftin[0]};
2'b01: ReceiveShiftReg <= { ReceiveShiftReg[5:0], shiftin[0],shiftin[1]};
2'b10: ReceiveShiftReg <= { ReceiveShiftReg[3:0], shiftin[0], shiftin[1], shiftin[2], shiftin[3]};
default: ReceiveShiftReg <= { ReceiveShiftReg[6:0], shiftin[0]};
endcase
end
end
2023-10-31 19:27:41 +00:00
// Aligns received data and reverses if little-endian
assign LeftShiftAmount = 8 - Format[7:4];
assign ASR = ReceiveShiftReg << LeftShiftAmount;
assign ReceiveShiftRegEndian = Format[2] ? {ASR[0], ASR[1], ASR[2], ASR[3], ASR[4], ASR[5], ASR[6], ASR[7]} : ASR[7:0];
2023-10-31 19:27:41 +00:00
// Interrupt logic: raise interrupt if any enabled interrupts are pending
assign SPIIntr = |(InterruptPending & InterruptEnable);
2023-10-31 19:27:41 +00:00
// Chip select logic
always_comb
case(ChipSelectID[1:0])
2'b00: ChipSelectAuto = {ChipSelectDef[3], ChipSelectDef[2], ChipSelectDef[1], ChipSelectInternal[0]};
2'b01: ChipSelectAuto = {ChipSelectDef[3],ChipSelectDef[2], ChipSelectInternal[1], ChipSelectDef[0]};
2'b10: ChipSelectAuto = {ChipSelectDef[3],ChipSelectInternal[2], ChipSelectDef[1], ChipSelectDef[0]};
2'b11: ChipSelectAuto = {ChipSelectInternal[3],ChipSelectDef[2], ChipSelectDef[1], ChipSelectDef[0]};
endcase
assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto;
endmodule
2023-10-17 05:57:02 +00:00
module TransmitSynchFIFO #(parameter M =3 , N= 8)(
2023-11-01 08:26:34 +00:00
input logic PCLK, sclken, PRESETn,
input logic winc,rinc,
input logic [N-1:0] wdata,
input logic [M-1:0] wwatermarklevel, rwatermarklevel,
output logic [N-1:0] rdata,
output logic wfull, rempty,
output logic wwatermark, rwatermark);
logic [N-1:0] mem[2**M];
logic [M:0] rptr, wptr;
2023-10-17 05:57:02 +00:00
logic [M:0] rptrnext, wptrnext;
logic rempty_val;
logic wfull_val;
logic [M-1:0] raddr;
logic [M-1:0] waddr;
assign rdata = mem[raddr];
2023-10-17 05:57:02 +00:00
always_ff @(posedge PCLK)
if (winc & ~wfull) mem[waddr] <= wdata;
2023-11-01 08:26:34 +00:00
// read flops are only enabled on sclk edges b/c transmit fifo is read on sclk
2023-10-17 05:57:02 +00:00
always_ff @(posedge PCLK, negedge PRESETn)
2023-11-01 08:26:34 +00:00
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
2023-10-17 05:57:02 +00:00
assign raddr = rptr[M-1:0];
assign rptrnext = rptr + {3'b0, (rinc & ~rempty)};
2023-11-01 08:26:34 +00:00
assign rempty_val = (wptr == rptrnext);
assign rwatermark = ((rptr[M-1:0] - wptr[M-1:0]) < rwatermarklevel);
2023-10-17 05:57:02 +00:00
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);
2023-11-01 08:26:34 +00:00
2023-10-17 05:57:02 +00:00
endmodule
module ReceiveSynchFIFO #(parameter M =3 , N= 8)(
2023-11-01 08:26:34 +00:00
input logic PCLK, sclken, PRESETn,
2023-10-17 05:57:02 +00:00
input logic winc,rinc,
input logic [N-1:0] wdata,
input logic [M-1:0] wwatermarklevel, rwatermarklevel,
output logic [N-1:0] rdata,
output logic wfull, rempty,
output logic wwatermark, rwatermark);
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;
2023-10-17 05:57:02 +00:00
assign rdata = mem[raddr];
always_ff @(posedge PCLK)
if(winc & ~wfull & PCLK) mem[waddr] <= wdata;
2023-11-01 08:26:34 +00:00
//write flops are enabled only on sclk edges b/c receive fifo is written then
2023-10-17 05:57:02 +00:00
always_ff @(posedge PCLK, negedge PRESETn)
2023-11-01 08:26:34 +00:00
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
2023-10-17 05:57:02 +00:00
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);
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);
2023-11-01 08:26:34 +00:00
2023-10-17 05:57:02 +00:00
endmodule
module TransmitFIFO #(parameter M = 3, N = 8)(
input logic wclk, rclk, PRESETn,
input logic winc,rinc,
input logic [N-1:0] wdata,
input logic [M-1:0] wwatermarklevel, rwatermarklevel,
output logic [N-1:0] rdata,
output logic wfull, rempty,
output logic wwatermark, rwatermark);
logic [N-1:0] mem[2**M];
logic [M:0] wq1_rptr, wq2_rptr, rptr;
logic [M:0] rq1_wptr, rq2_wptr, wptr;
logic [M:0] rbin, rgraynext, rbinnext;
logic [M:0] wbin, wgraynext, wbinnext;
logic rempty_val;
logic wfull_val;
logic [M:0] wq2_rptr_bin, rq2_wptr_bin;
logic [M-1:0] raddr;
logic [M-1:0] waddr;
assign rdata = mem[raddr];
always_ff @(posedge wclk)
if(winc & ~wfull) mem[waddr] <= wdata;
always_ff @(posedge wclk, negedge PRESETn)
if (~PRESETn) begin
wq2_rptr <= 0;
wq1_rptr <= 0;
end
else begin
wq2_rptr <= wq1_rptr;
wq1_rptr <= rptr;
end
always_ff @(posedge wclk, negedge PRESETn)
if (~PRESETn) begin
rq2_wptr <= 0;
rq1_wptr <= 0;
end
else if (rclk) begin
rq2_wptr <= rq1_wptr;
rq1_wptr <= wptr;
end
always_ff @(posedge wclk, negedge PRESETn)
if(~PRESETn) begin
rbin <= 0;
rptr <= 0;
end
else if (rclk) begin
rbin <= rbinnext;
rptr <= rgraynext;
end
assign rq2_wptr_bin = {rq2_wptr[3], (rq2_wptr[3]^rq2_wptr[2]),(rq2_wptr[3]^rq2_wptr[2]^rq2_wptr[1]), (rq2_wptr[3]^rq2_wptr[2]^rq2_wptr[1]^rq2_wptr[0]) };
assign rwatermark = ((rbin[M-1:0] - rq2_wptr_bin[M-1:0]) < rwatermarklevel);
assign raddr = rbin[M-1:0];
assign rbinnext = rbin + {3'b0, (rinc & ~rempty)};
assign rgraynext = (rbinnext >> 1) ^ rbinnext;
assign rempty_val = (rgraynext == rq2_wptr);
always_ff @(posedge wclk, negedge PRESETn)
if (~PRESETn) rempty <= 1'b1;
else if (rclk) rempty <= rempty_val;
always_ff @(posedge wclk, negedge PRESETn)
if (~PRESETn) begin
wbin <= 0;
wptr <= 0;
end else begin
wbin <= wbinnext;
wptr <= wgraynext;
end
assign waddr = wbin[M-1:0];
assign wq2_rptr_bin = {wq2_rptr[3], (wq2_rptr[3]^wq2_rptr[2]),(wq2_rptr[3]^wq2_rptr[2]^wq2_rptr[1]), (wq2_rptr[3]^wq2_rptr[2]^wq2_rptr[1]^wq2_rptr[0]) };
assign wwatermark = ((wbin[M-1:0] - wq2_rptr_bin[M-1:0]) > wwatermarklevel);
assign wbinnext = wbin + {3'b0, (winc & ~wfull)};
assign wgraynext = (wbinnext >> 1) ^ wbinnext;
assign wfull_val = (wgraynext == {(~wq2_rptr[M:M-1]),wq2_rptr[M-2:0]});
always_ff @(posedge wclk, negedge PRESETn)
if (~PRESETn) wfull <= 1'b0;
else wfull <= wfull_val;
endmodule
module ReceiveFIFO #(parameter M = 3, N = 8)(
input logic wclk, rclk, PRESETn,
input logic winc,rinc,
input logic [N-1:0] wdata,
input logic [M-1:0] wwatermarklevel, rwatermarklevel,
output logic [N-1:0] rdata,
output logic wfull, rempty,
output logic wwatermark, rwatermark);
logic [N-1:0] mem[2**M];
logic [M:0] wq1_rptr, wq2_rptr, rptr;
logic [M:0] rq1_wptr, rq2_wptr, wptr;
logic [M:0] rbin, rgraynext, rbinnext;
logic [M:0] wbin, wgraynext, wbinnext;
logic rempty_val;
logic wfull_val;
logic [M:0] wq2_rptr_bin, rq2_wptr_bin;
logic [M-1:0] raddr;
logic [M-1:0] waddr;
assign rdata = mem[raddr];
always_ff @(posedge rclk)
if(winc & ~wfull & wclk) mem[waddr] <= wdata;
always_ff @(posedge rclk, negedge PRESETn)
if (~PRESETn) begin
wq2_rptr <= 0;
wq1_rptr <= 0;
end
else if (wclk) begin
wq2_rptr <= wq1_rptr;
wq1_rptr <= rptr;
end
always_ff @(posedge rclk, negedge PRESETn)
if (~PRESETn) begin
rq2_wptr <= 0;
rq1_wptr <= 0;
end
else begin
rq2_wptr <= rq1_wptr;
rq1_wptr <= wptr;
end
always_ff @(posedge rclk, negedge PRESETn)
if(~PRESETn) begin
rbin <= 0;
rptr <= 0;
end
else begin
rbin <= rbinnext;
rptr <= rgraynext;
end
assign rq2_wptr_bin = {rq2_wptr[3], (rq2_wptr[3]^rq2_wptr[2]),(rq2_wptr[3]^rq2_wptr[2]^rq2_wptr[1]), (rq2_wptr[3]^rq2_wptr[2]^rq2_wptr[1]^rq2_wptr[0]) };
assign rwatermark = ((rbin[M-1:0] - rq2_wptr_bin[M-1:0]) < rwatermarklevel);
assign raddr = rbin[M-1:0];
assign rbinnext = rbin + {3'b0, (rinc & ~rempty)};
assign rgraynext = (rbinnext >> 1) ^ rbinnext;
assign rempty_val = (rgraynext == rq2_wptr);
always_ff @(posedge rclk, negedge PRESETn)
if (~PRESETn) rempty <= 1'b1;
else rempty <= rempty_val;
always_ff @(posedge rclk, negedge PRESETn)
if (~PRESETn) begin
wbin <= 0;
wptr <= 0;
end else if (wclk) begin
wbin <= wbinnext;
wptr <= wgraynext;
end
assign waddr = wbin[M-1:0];
assign wq2_rptr_bin = {wq2_rptr[3], (wq2_rptr[3]^wq2_rptr[2]),(wq2_rptr[3]^wq2_rptr[2]^wq2_rptr[1]), (wq2_rptr[3]^wq2_rptr[2]^wq2_rptr[1]^wq2_rptr[0]) };
assign wwatermark = ((wbin[M-1:0] - wq2_rptr_bin[M-1:0]) > wwatermarklevel);
assign wbinnext = wbin + {3'b0, (winc & ~wfull)};
assign wgraynext = (wbinnext >> 1) ^ wbinnext;
assign wfull_val = (wgraynext == {(~wq2_rptr[M:M-1]),wq2_rptr[M-2:0]});
always_ff @(posedge rclk, negedge PRESETn)
if (~PRESETn) wfull <= 1'b0;
else if (wclk) wfull <= wfull_val;
endmodule
module TransmitShiftFSM(
input logic PCLK, PRESETn,
input logic TransmitFIFOReadEmpty, ReceivePenultimateFrameBoolean, 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;
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(
2023-10-31 00:00:20 +00:00
input logic PCLK, PRESETn, SCLKenable,
input logic ReceivePenultimateFrameBoolean, SampleEdge, SckMode,
output logic ReceiveShiftFull
);
typedef enum logic [1:0] {ReceiveShiftFullState, ReceiveShiftNotFullState, ReceiveShiftDelayState} statetype;
statetype ReceiveState, ReceiveNextState;
always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveState <= ReceiveShiftNotFullState;
2023-10-31 00:00:20 +00:00
else if (SCLKenable) begin
case (ReceiveState)
ReceiveShiftFullState: ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftNotFullState: if (ReceivePenultimateFrameBoolean & (SampleEdge)) ReceiveState <= ReceiveShiftDelayState;
else ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftDelayState: ReceiveState <= ReceiveShiftFullState;
endcase
end
assign ReceiveShiftFull = SckMode ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState);
endmodule