diff --git a/src/uncore/spi_apb.sv b/src/uncore/spi_apb.sv index 4db435be6..b0649bf93 100644 --- a/src/uncore/spi_apb.sv +++ b/src/uncore/spi_apb.sv @@ -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, @@ -54,27 +45,27 @@ module spi_apb import cvw::*; #(parameter cvw_t P) ( output logic SPIIntr ); - //SPI control registers. Refer to SiFive FU540-C000 manual + // SPI control registers. Refer to SiFive FU540-C000 manual logic [11:0] SckDiv; - logic [1:0] SckMode; - logic [1:0] ChipSelectID; - logic [3:0] ChipSelectDef; - logic [1:0] ChipSelectMode; + logic [1:0] SckMode; + logic [1:0] ChipSelectID; + logic [3:0] ChipSelectDef; + logic [1:0] ChipSelectMode; logic [15:0] Delay0, Delay1; - logic [4:0] Format; - logic [7:0] ReceiveData; - logic [2:0] TransmitWatermark, ReceiveWatermark; - logic [8:0] TransmitData; - logic [1:0] InterruptEnable, InterruptPending; + logic [4:0] Format; + logic [7:0] ReceiveData; + logic [2:0] TransmitWatermark, ReceiveWatermark; + logic [8:0] TransmitData; + logic [1:0] InterruptEnable, InterruptPending; - //Bus interface signals + // Bus interface signals logic [7:0] Entry; logic Memwrite; logic [31:0] Din, Dout; - logic TransmitInactive; //High when there is no transmission, used as hardware interlock signal + logic TransmitInactive; // High when there is no transmission, used as hardware interlock signal - //FIFO FSM signals - //Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1] + // FIFO FSM signals + // Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1] logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark; logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty; logic TransmitFIFOReadIncrement; @@ -83,75 +74,68 @@ 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 + // 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 - logic [8:0] ImplicitDelay2; //Adds implicit delay to sck-cs delay counter based on phase - logic [8:0] CS_SCKCount; //Counter for cs-sck delay - 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 + // Delay signals + logic [8:0] ImplicitDelay1; // Adds implicit delay to cs-sck delay counter based on phase + logic [8:0] ImplicitDelay2; // Adds implicit delay to sck-cs delay counter based on phase + logic [8:0] CS_SCKCount; // Counter for cs-sck delay + 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 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 + // Frame counting signals + logic [3:0] FrameCount; // Counter for number of frames in transmission + logic [3:0] ReceivePenultimateFrameCount; // Counter + 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) - logic Active0; //High when state is Active0 + // State fsm signals + logic Active; // High when state is either Active1 or Active0 (during transmission) + logic Active0; // High when state is Active0 - //Shift reg signals - logic ShiftEdge; //Determines which edge of sck to shift from TransmitShiftReg - logic [7:0] TransmitShiftReg; //Transmit shift register - logic [7:0] ReceiveShiftReg; //Receive shift register - logic SampleEdge; //Determines which edge of sck to sample from ReceiveShiftReg - logic [7:0] TransmitDataEndian; //Reverses TransmitData from txFIFO if littleendian, since TransmitReg always shifts MSB - logic TransmitShiftRegLoad; //Determines when to load TransmitShiftReg - logic ReceiveShiftFull; //High when receive shift register is full - logic TransmitShiftEmpty; //High when transmit shift register is empty - logic ShiftIn; //Determines whether to shift from SPIIn or SPIOut (if SPI_LOOPBACK_TEST) - logic [3:0] LeftShiftAmount; //Determines left shift amount to left-align data when little endian - logic [7:0] ASR; //AlignedReceiveShiftReg + // Shift reg signals + logic ShiftEdge; // Determines which edge of sck to shift from TransmitShiftReg + logic [7:0] TransmitShiftReg; // Transmit shift register + logic [7:0] ReceiveShiftReg; // Receive shift register + logic SampleEdge; // Determines which edge of sck to sample from ReceiveShiftReg + logic [7:0] TransmitDataEndian; // Reverses TransmitData from txFIFO if littleendian, since TransmitReg always shifts MSB + logic TransmitShiftRegLoad; // Determines when to load TransmitShiftReg + logic ReceiveShiftFull; // High when receive shift register is full + logic TransmitShiftEmpty; // High when transmit shift register is empty + logic ShiftIn; // Determines whether to shift from SPIIn or SPIOut (if SPI_LOOPBACK_TEST) + logic [3:0] LeftShiftAmount; // Determines left shift amount to left-align data when little endian + logic [7:0] ASR; // AlignedReceiveShiftReg - //CS signals - logic [3:0] ChipSelectAuto; //Assigns ChipSelect value to selected CS signal based on CS ID - logic [3:0] ChipSelectInternal; //Defines what each ChipSelect signal should be based on transmission status and ChipSelectDef - logic DelayMode; //Determines where to place implicit half cycle delay based on sck phase for CS assertion + // CS signals + logic [3:0] ChipSelectAuto; // Assigns ChipSelect value to selected CS signal based on CS ID + logic [3:0] ChipSelectInternal; // Defines what each ChipSelect signal should be based on transmission status and ChipSelectDef + logic DelayMode; // Determines where to place implicit half cycle delay based on sck phase for CS assertion - //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 + // Miscellaneous signals delayed/early by 1 PCLK cycle + logic ReceiveShiftFullDelay; // Delays ReceiveShiftFull signal 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 + 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 + // 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 - //Account for subword read/write circuitry + // Account for subword read/write circuitry // -- Note SPI registers are 32 bits no matter what; access them with LW SW. assign Din = PWDATA[31:0]; if (P.XLEN == 64) assign PRDATA = {Dout, Dout}; else assign PRDATA = Dout; - //Register access + // Register access always_ff@(posedge PCLK, negedge PRESETn) if (~PRESETn) begin SckDiv <= #1 12'd3; @@ -167,13 +151,12 @@ module spi_apb import cvw::*; #(parameter cvw_t P) ( 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 + end else begin // writes + /* verilator lint_off CASEINCOMPLETE */ if (Memwrite & TransmitInactive) - case(Entry) //flop to sample inputs + case(Entry) // flop to sample inputs 8'h00: SckDiv <= Din[11:0]; 8'h04: SckMode <= Din[1:0]; 8'h10: ChipSelectID <= Din[1:0]; @@ -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}; @@ -210,8 +196,9 @@ module spi_apb import cvw::*; #(parameter cvw_t P) ( endcase 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 + // SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1)) + // 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 + // 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(PCLK, PRESETn, TransmitFIFOReadEmpty, ReceivePenultimateFrame, Active0, TransmitShiftEmpty); + ReceiveShiftFSM ReceiveShiftFSM(PCLK, PRESETn, SCLKenable, ReceivePenultimateFrame, SampleEdge, SckMode[0], ReceiveShiftFull); - //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 & TransmitInactive); + // Calculate tx/rx fifo write and recieve increment signals 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); + // Tx/Rx FIFOs + 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; @@ -266,16 +247,16 @@ 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 + // 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; + 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; @@ -339,7 +321,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) ( assign TransmitInactive = ((state == INTER_CS) | (state == CS_INACTIVE) | (state == INTER_XFR) | (ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode)); assign Active0 = (state == ACTIVE_0); - //Signal tracks which edge of sck to shift data + // Signal tracks which edge of sck to shift data always_comb case(SckMode[1:0]) 2'b00: ShiftEdge = ~sck & SCLKenable; @@ -349,36 +331,36 @@ module spi_apb import cvw::*; #(parameter cvw_t P) ( default: ShiftEdge = sck & SCLKenable; endcase - //Transmit shift register - assign TransmitDataEndian = Format[0] ? {TransmitFIFOReadData[0], TransmitFIFOReadData[1], TransmitFIFOReadData[2], TransmitFIFOReadData[3], TransmitFIFOReadData[4], TransmitFIFOReadData[5], TransmitFIFOReadData[6], TransmitFIFOReadData[7]} : TransmitFIFOReadData[7:0]; + // Transmit shift register + assign TransmitDataEndian = Format[0] ? {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) TransmitShiftReg <= 8'b0; else if (TransmitShiftRegLoad) TransmitShiftReg <= TransmitDataEndian; - else if (ShiftEdge & Active) TransmitShiftReg <= {TransmitShiftReg[6:0], 1'b0}; + else if (ShiftEdge & Active) TransmitShiftReg <= {TransmitShiftReg[6:0], 1'b0}; assign SPIOut = TransmitShiftReg[7]; - //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 + // 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 + // Receive shift register always_ff @(posedge PCLK, negedge PRESETn) if(~PRESETn) ReceiveShiftReg <= 8'b0; else if (SampleEdge & SCLKenable) begin - if (~Active) ReceiveShiftReg <= 8'b0; - else ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn}; + if (~Active) ReceiveShiftReg <= 8'b0; + else ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn}; end - //Aligns received data and reverses if little-endian + // Aligns received data and reverses if little-endian assign LeftShiftAmount = 4'h8 - Format[4:1]; assign ASR = ReceiveShiftReg << LeftShiftAmount[2:0]; assign ReceiveShiftRegEndian = Format[0] ? {ASR[0], ASR[1], ASR[2], ASR[3], ASR[4], ASR[5], ASR[6], ASR[7]} : ASR[7:0]; - //Interrupt logic: raise interrupt if any enabled interrupts are pending + // Interrupt logic: raise interrupt if any enabled interrupts are pending assign SPIIntr = |(InterruptPending & InterruptEnable); - //Chip select logic + // Chip select logic always_comb case(ChipSelectID[1:0]) 2'b00: ChipSelectAuto = {ChipSelectDef[3], ChipSelectDef[2], ChipSelectDef[1], ChipSelectInternal[0]}; @@ -390,14 +372,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) ( assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto; endmodule -module SynchFIFO #(parameter M =3 , N= 8)( - input logic PCLK, wen, ren, PRESETn, - input logic winc,rinc, - input logic [N-1:0] wdata, - input logic [M-1:0] wwatermarklevel, rwatermarklevel, +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, + input logic [M-1:0] wwatermarklevel, rwatermarklevel, output logic [N-1:0] rdata, - output logic wfull, rempty, - output logic wwatermark, rwatermark); + output logic wfull, rempty, + output logic wwatermark, rwatermark); /* Pointer FIFO using design elements from "Simulation and Synthesis Techniques for Asynchronous FIFO Design" by Clifford E. Cummings. Namely, M bit read and write pointers @@ -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 PCLK, PRESETn, + 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 PCLK, PRESETn, SCLKenable, + input logic ReceivePenultimateFrame, SampleEdge, SckMode, output logic ReceiveShiftFull ); typedef enum logic [1:0] {ReceiveShiftFullState, ReceiveShiftNotFullState, ReceiveShiftDelayState} statetype; @@ -484,17 +454,12 @@ 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; + ReceiveShiftDelayState: ReceiveState <= ReceiveShiftFullState; endcase end - assign ReceiveShiftFull = SckMode ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState); + assign ReceiveShiftFull = SckMode ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState); endmodule - - - - -