Merge pull request #1052 from JacobPease/main

Refactored SPI peripheral
This commit is contained in:
Rose Thompson 2024-11-02 14:32:50 -05:00 committed by GitHub
commit 95fc05654b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 608 additions and 558 deletions

View File

@ -135,45 +135,45 @@ int disk_read(BYTE * buf, LBA_t sector, UINT count) {
/* crc = crc16(crc, x); */
/* } while (--n > 0); */
/* n = 512/8; */
/* do { */
/* // Send 8 dummy bytes (fifo should be empty) */
/* for (j = 0; j < 8; j++) { */
/* spi_sendbyte(0xff); */
/* } */
/* // Reset counter. Process bytes AS THEY COME IN. */
/* for (j = 0; j < 8; j++) { */
/* while (!(read_reg(SPI_IP) & 2)) {} */
/* uint8_t x = spi_readbyte(); */
/* *p++ = x; */
/* // crc = crc16(crc, x); */
/* crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff; */
/* } */
/* } while(--n > 0); */
n = 512;
// Initially fill the transmit fifo
for (j = 0; j < 8; j++) {
spi_sendbyte(0xff);
}
while (n > 0) {
// Wait for bytes to be received
while (!(read_reg(SPI_IP) & 2)) {}
// Read byte
uint8_t x = spi_readbyte();
// Send another dummy byte
if (n > 8) {
n = 512/8;
do {
// Send 8 dummy bytes (fifo should be empty)
for (j = 0; j < 8; j++) {
spi_sendbyte(0xff);
}
// Place received byte into memory
*p++ = x;
// Update CRC16 with fast table based method
crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff;
n = n - 1;
}
// Reset counter. Process bytes AS THEY COME IN.
for (j = 0; j < 8; j++) {
while (!(read_reg(SPI_IP) & 2)) {}
uint8_t x = spi_readbyte();
*p++ = x;
// crc = crc16(crc, x);
crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff;
}
} while(--n > 0);
/* n = 512; */
/* // Initially fill the transmit fifo */
/* for (j = 0; j < 8; j++) { */
/* spi_sendbyte(0xff); */
/* } */
/* while (n > 0) { */
/* // Wait for bytes to be received */
/* while (!(read_reg(SPI_IP) & 2)) {} */
/* // Read byte */
/* uint8_t x = spi_readbyte(); */
/* // Send another dummy byte */
/* if (n > 8) { */
/* spi_sendbyte(0xff); */
/* } */
/* // Place received byte into memory */
/* *p++ = x; */
/* // Update CRC16 with fast table based method */
/* crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff; */
/* n = n - 1; */
/* } */
// Read CRC16 and check
crc_exp = ((uint16_t)spi_dummy() << 8);

View File

@ -1,4 +1,30 @@
#!/bin/bash
###########################################
## flash-sd.sh
##
## Written: Jacob Pease jacobpease@protonmail.com
## Created: August 22, 2023
##
## Purpose: A script to flash an sd card with a bootable linux image.
##
## A component of the CORE-V-WALLY configurable RISC-V project.
## https://github.com/openhwgroup/cvw
##
## Copyright (C) 2021-24 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.
################################################################################################
# Exit on any error (return code != 0)
# set -e

80
linux/sdcard/write-bytes.sh Executable file
View File

@ -0,0 +1,80 @@
#!/bin/bash
###########################################
## write-bytes.sh
##
## Written: Jacob Pease jacobpease@protonmail.com
## Created: November 2nd, 2024
## Modified:
##
## Purpose: Write a sequence of bytes from text file to an output file and a flash card.
##
## A component of the CORE-V-WALLY configurable RISC-V project.
## https://github.com/openhwgroup/cvw
##
## Copyright (C) 2021-24 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.
################################################################################################
# This file writes a bunch of bytes to the flash card based on a text
# file input with bytes written in hexadecimal.
usage() { echo "Usage: $0 [-zh] [-b <path/to/buildroot>] <device>" 1>&2; exit 1; }
help() {
echo "Usage: $0 [OPTIONS] <device>"
echo " -i Input text file with hex bytes."
echo " -b <path/to/buildroot> Output binary file."
exit 0;
}
INPUTFILE=""
OUTPUTFILE=""
ARGS=()
while [ $OPTIND -le "$#" ] ; do
if getopts "hi:o:" arg ; then
case "${arg}" in
h) help
;;
i) INPUTFILE=${OPTARG}
;;
o) OUTPUTFILE=${OPTARG}
;;
esac
else
ARGS+=("${!OPTIND}")
((OPTIND++))
fi
done
SDCARD=${ARGS[0]}
if [ ! -e $INPUTFILE ] ; then
echo -e "Error: Input file $INPUTFILE does not exist."
exit 1
fi
if [ -e $OUTPUTFILE ] ; then
echo -e "Error: Output file $OUTPUTFILE already exists."
exit 1
fi
for word in $(cat "$INPUTFILE")
do
echo -en "\x$word" >> $OUTPUTFILE
done
dd if=$OUTPUTFILE of="$SDCARD"

View File

@ -1,7 +1,9 @@
///////////////////////////////////////////
// spi_apb.sv
//
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu 11/16/2022
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu
// Jacob Pease jacobpease@protonmail.com (October 29th, 2024)
// Created: November 16th, 2022
//
// Purpose: SPI peripheral
//
@ -13,7 +15,7 @@
//
// A component of the Wally configurable RISC-V project.
//
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
@ -30,438 +32,337 @@
////////////////////////////////////////////////////////////////////////////////////////////////
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 SPIOut,
input logic SPIIn,
output logic [3:0] SPICS,
output logic SPIIntr,
output logic SPICLK
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 SPIOut,
input logic SPIIn,
output logic [3:0] SPICS,
output logic SPIIntr,
output logic SPICLK
);
// register map
localparam SPI_SCKDIV = 8'h00;
localparam SPI_SCKMODE = 8'h04;
localparam SPI_CSID = 8'h10;
localparam SPI_CSDEF = 8'h14;
localparam SPI_CSMODE = 8'h18;
localparam SPI_DELAY0 = 8'h28;
localparam SPI_DELAY1 = 8'h2C;
localparam SPI_FMT = 8'h40;
localparam SPI_TXDATA = 8'h48;
localparam SPI_RXDATA = 8'h4C;
localparam SPI_TXMARK = 8'h50;
localparam SPI_RXMARK = 8'h54;
localparam SPI_IE = 8'h70;
localparam SPI_IP = 8'h74;
// receive shift register states
typedef enum logic [1:0] {ReceiveShiftFullState, ReceiveShiftNotFullState, ReceiveShiftDelayState} rsrstatetype;
// 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 [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;
// 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
// FIFO FSM signals
// Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1]
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty;
logic TransmitFIFOWriteIncrement;
logic ReceiveFiFoWriteInc;
logic ReceiveFIFOReadIncrement;
logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty;
logic [7:0] TransmitFIFOReadData;
/* verilator lint_off UNDRIVEN */
logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel; // unused generic FIFO outputs
/* verilator lint_off UNDRIVEN */
logic [7:0] ReceiveShiftRegEndian; // Reverses ReceiveShiftReg if Format[2] set (little endian transmission)
rsrstatetype ReceiveState;
logic ReceiveFiFoTakingData;
// Transmission signals
logic ZeroDiv; // High when SckDiv is 0
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 ZeroDelayHoldMode; // High when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
// Frame counting signals
logic FirstFrame;
logic [3:0] FrameCount; // Counter for number of frames in transmission
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
// 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 TransmitShiftRegLoadSingleCycle; // Version of TransmitShiftRegLoad which is only high for a single SCLK cycle to prevent double loads
logic TransmitShiftRegLoadDelay; // TransmitShiftRegLoad delayed by an SCLK cycle, inverted and anded with TransmitShiftRegLoad to create a single cycle signal
logic TransmitFIFOReadIncrement; // Increments Tx FIFO read ptr 1 cycle after Tx FIFO is read
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
logic ShiftEdgeSPICLK; // Changes ShiftEdge when SckDiv is 0
// 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 ReceiveShiftFullDelayPCLK; // ReceiveShiftFull delayed by 1 PCLK cycle
logic TransmitFIFOReadEmptyDelay;
logic SCLKenableEarly; // SCLKenable 1 PCLK cycle early, needed for on time register changes when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
// APB access
assign Entry = {PADDR[7:2],2'b00}; // 32-bit word-aligned accesses
assign Memwrite = PWRITE & PENABLE & PSEL; // Only write in access phase
assign PREADY = Entry == SPI_TXDATA | Entry == SPI_RXDATA | Entry == SPI_IP | TransmitInactive; // Tie PREADY to transmission for hardware interlock
// Account for subword read/write circuitry
// -- Note SPI registers are 32 bits no matter what; access them with LW SW.
assign Din = PWDATA[31:0];
if (P.XLEN == 64) assign PRDATA = { Dout, Dout};
else assign PRDATA = Dout;
// Register access
always_ff@(posedge PCLK)
if (~PRESETn) begin
SckDiv <= 12'd3;
SckMode <= 2'b0;
ChipSelectID <= 2'b0;
ChipSelectDef <= 4'b1111;
ChipSelectMode <= 2'b0;
Delay0 <= {8'b1,8'b1};
Delay1 <= {8'b0,8'b1};
Format <= {5'b10000};
TransmitData <= 9'b0;
TransmitWatermark <= 3'b0;
ReceiveWatermark <= 3'b0;
InterruptEnable <= 2'b0;
InterruptPending <= 2'b0;
end else begin // writes
/* verilator lint_off CASEINCOMPLETE */
if (Memwrite & TransmitInactive)
case(Entry) // flop to sample inputs
SPI_SCKDIV: SckDiv <= Din[11:0];
SPI_SCKMODE: SckMode <= Din[1:0];
SPI_CSID: ChipSelectID <= Din[1:0];
SPI_CSDEF: ChipSelectDef <= Din[3:0];
SPI_CSMODE: ChipSelectMode <= Din[1:0];
SPI_DELAY0: Delay0 <= {Din[23:16], Din[7:0]};
SPI_DELAY1: Delay1 <= {Din[23:16], Din[7:0]};
SPI_FMT: Format <= {Din[19:16], Din[2]};
SPI_TXMARK: TransmitWatermark <= Din[2:0];
SPI_RXMARK: ReceiveWatermark <= Din[2:0];
SPI_IE: InterruptEnable <= Din[1:0];
endcase
if (Memwrite)
case(Entry)
SPI_TXDATA: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0];
endcase
/* verilator lint_off CASEINCOMPLETE */
// 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
SPI_SCKDIV: Dout <= {20'b0, SckDiv};
SPI_SCKMODE: Dout <= {30'b0, SckMode};
SPI_CSID: Dout <= {30'b0, ChipSelectID};
SPI_CSDEF: Dout <= {28'b0, ChipSelectDef};
SPI_CSMODE: Dout <= {30'b0, ChipSelectMode};
SPI_DELAY0: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]};
SPI_DELAY1: Dout <= {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]};
SPI_FMT: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0};
SPI_TXDATA: Dout <= {TransmitFIFOWriteFull, 23'b0, 8'b0};
SPI_RXDATA: Dout <= {ReceiveFIFOReadEmpty, 23'b0, ReceiveData[7:0]};
SPI_TXMARK: Dout <= {29'b0, TransmitWatermark};
SPI_RXMARK: Dout <= {29'b0, ReceiveWatermark};
SPI_IE: Dout <= {30'b0, InterruptEnable};
SPI_IP: Dout <= {30'b0, InterruptPending};
default: Dout <= 32'b0;
endcase
end
// 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
// When SckDiv is 0, count doesn't work and SCLKenable is simply PCLK *** dh 10/26/24: this logic is seriously broken. SCLK is not scaled to PCLK/(2*(SckDiv + 1)). SCLKenableEarly doesn't work right for SckDiv=0
assign ZeroDiv = ~|(SckDiv[10:0]);
assign SCLKenable = ZeroDiv ? 1 : (DivCounter == SckDiv);
assign SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv);
always_ff @(posedge PCLK)
if (~PRESETn) DivCounter <= '0;
else if (SCLKenable) DivCounter <= 12'b0;
else DivCounter <= DivCounter + 12'b1;
// Asserts when transmission is one frame before complete
assign ReceivePenultimateFrame = ((FrameCount + 4'b0001) == Format[4:1]);
assign FirstFrame = (FrameCount == 4'b0);
// 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;
// Calculate when tx/rx shift registers are full/empty
// Transmit Shift FSM
always_ff @(posedge PCLK)
if (~PRESETn) TransmitShiftEmpty <= 1'b1;
else if (TransmitShiftEmpty) begin
if (TransmitFIFOReadEmpty | (~TransmitFIFOReadEmpty & (ReceivePenultimateFrame & Active0))) TransmitShiftEmpty <= 1'b1;
else if (~TransmitFIFOReadEmpty) TransmitShiftEmpty <= 1'b0;
end else begin
if (ReceivePenultimateFrame & Active0) TransmitShiftEmpty <= 1'b1;
else TransmitShiftEmpty <= 1'b0;
end
// Receive Shift FSM
always_ff @(posedge PCLK)
if (~PRESETn) ReceiveState <= ReceiveShiftNotFullState;
else if (SCLKenable) begin
case (ReceiveState)
ReceiveShiftFullState: ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftNotFullState: if (ReceivePenultimateFrame & (SampleEdge)) ReceiveState <= ReceiveShiftDelayState;
else ReceiveState <= ReceiveShiftNotFullState;
ReceiveShiftDelayState: ReceiveState <= ReceiveShiftFullState;
endcase
end
assign ReceiveShiftFull = SckMode[0] ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState);
// Calculate tx/rx fifo write and recieve increment signals
always_ff @(posedge PCLK)
if (~PRESETn) TransmitFIFOWriteIncrement <= 1'b0;
else TransmitFIFOWriteIncrement <= (Memwrite & (Entry == SPI_TXDATA) & ~TransmitFIFOWriteFull);
always_ff @(posedge PCLK)
if (~PRESETn) ReceiveFIFOReadIncrement <= 1'b0;
else ReceiveFIFOReadIncrement <= ((Entry == SPI_RXDATA) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement);
assign TransmitShiftRegLoad = ~TransmitShiftEmpty & ~Active | (((ChipSelectMode == 2'b10) & ~|(Delay1[15:8])) & ((ReceiveShiftFullDelay | ReceiveShiftFull) & ~SampleEdge & ~TransmitFIFOReadEmpty));
always_ff @(posedge PCLK)
if (~PRESETn) TransmitShiftRegLoadDelay <=0;
else if (SCLKenable) TransmitShiftRegLoadDelay <= TransmitShiftRegLoad;
assign TransmitShiftRegLoadSingleCycle = TransmitShiftRegLoad & ~TransmitShiftRegLoadDelay;
always_ff @(posedge PCLK)
if (~PRESETn) TransmitFIFOReadIncrement <= 0;
else if (SCLKenable) TransmitFIFOReadIncrement <= TransmitShiftRegLoadSingleCycle;
// Tx/Rx FIFOs
spi_fifo #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn, TransmitFIFOWriteIncrement, TransmitFIFOReadIncrement, TransmitData[7:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0],
TransmitFIFOReadData[7:0], TransmitFIFOWriteFull, TransmitFIFOReadEmpty, TransmitWriteMark, TransmitReadMark);
spi_fifo #(3,8) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn, ReceiveFiFoWriteInc, ReceiveFIFOReadIncrement, ReceiveShiftRegEndian, ReceiveWatermark[2:0], ReceiveReadWatermarkLevel,
ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark);
always_ff @(posedge PCLK)
if (~PRESETn) TransmitFIFOReadEmptyDelay <= 1'b1;
else if (SCLKenable) TransmitFIFOReadEmptyDelay <= TransmitFIFOReadEmpty;
always_ff @(posedge PCLK)
if (~PRESETn) ReceiveShiftFullDelay <= 1'b0;
else if (SCLKenable) ReceiveShiftFullDelay <= ReceiveShiftFull;
assign ReceiveFiFoTakingData = ReceiveFiFoWriteInc & ~ReceiveFIFOWriteFull;
localparam SPI_SCKDIV = 8'h00;
localparam SPI_SCKMODE = 8'h04;
localparam SPI_CSID = 8'h10;
localparam SPI_CSDEF = 8'h14;
localparam SPI_CSMODE = 8'h18;
localparam SPI_DELAY0 = 8'h28;
localparam SPI_DELAY1 = 8'h2C;
localparam SPI_FMT = 8'h40;
localparam SPI_TXDATA = 8'h48;
localparam SPI_RXDATA = 8'h4C;
localparam SPI_TXMARK = 8'h50;
localparam SPI_RXMARK = 8'h54;
localparam SPI_IE = 8'h70;
localparam SPI_IP = 8'h74;
always_ff @(posedge PCLK)
if (~PRESETn) ReceiveFiFoWriteInc <= 1'b0;
else if (SCLKenable & ReceiveShiftFull) ReceiveFiFoWriteInc <= 1'b1;
else if (SCLKenable & ReceiveFiFoTakingData) ReceiveFiFoWriteInc <= 1'b0;
always_ff @(posedge PCLK)
if (~PRESETn) ReceiveShiftFullDelayPCLK <= 1'b0;
else if (SCLKenableEarly) ReceiveShiftFullDelayPCLK <= ReceiveShiftFull;
// 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 [15:0] Delay0, Delay1;
logic [4:0] Format;
logic [7:0] ReceiveData;
logic [8:0] TransmitData;
logic [2:0] TransmitWatermark, ReceiveWatermark;
logic [1:0] InterruptEnable, InterruptPending;
// Bus interface signals
logic [7:0] Entry;
logic Memwrite;
logic [31:0] Din, Dout;
// SPI Controller signals
logic SCLKenable;
logic EndOfFrame;
logic EndOfFrameDelay;
logic Transmitting;
logic InactiveState;
logic [3:0] FrameLength;
logic ResetSCLKenable;
logic TransmitStart;
logic TransmitStartD;
// 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;
// Transmit Start State Machine Variables
typedef enum logic [1:0] {READY, START, WAIT} txState;
txState CurrState, NextState;
// FIFO FSM signals
// Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1]
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty;
logic TransmitFIFOWriteIncrement;
logic [7:0] TransmitFIFOReadData;
always_ff @(posedge PCLK)
if (~PRESETn) begin
state <= CS_INACTIVE;
FrameCount <= 4'b0;
SPICLK <= SckMode[1];
end else if (SCLKenable) begin
logic ReceiveFIFOWriteInc;
logic ReceiveFIFOReadIncrement;
logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty;
/* verilator lint_off UNDRIVEN */
logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel; // unused generic FIFO outputs
/* verilator lint_off UNDRIVEN */
logic [7:0] ReceiveShiftRegEndian; // Reverses ReceiveShiftReg if Format[2] set (little endian transmission)
// Shift reg signals
logic ShiftEdge; // Determines which edge of sck to shift from TransmitReg
logic SampleEdge; // Determines which edge of sck to sample from ReceiveShiftReg
logic [7:0] TransmitReg; // Transmit shift register
logic [7:0] ReceiveShiftReg; // Receive shift register
logic [7:0] TransmitDataEndian; // Reverses TransmitData from txFIFO if littleendian, since TransmitReg always shifts MSB
logic TransmitLoad; // Determines when to load TransmitReg
logic TransmitFIFOReadIncrement; // Increments Tx FIFO read ptr 1 cycle after Tx FIFO is read
// Shift stuff due to Format register?
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
// 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 = Entry == SPI_TXDATA | Entry == SPI_RXDATA | Entry == SPI_IP;
assign PREADY = 1'b1;
// 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
always_ff@(posedge PCLK)
if (~PRESETn) begin
SckDiv <= 12'd3;
SckMode <= 2'b0;
ChipSelectID <= 2'b0;
ChipSelectDef <= 4'b1111;
ChipSelectMode <= 2'b0;
Delay0 <= {8'b1,8'b1};
Delay1 <= {8'b0,8'b1};
Format <= {5'b10000};
TransmitData <= 9'b0;
TransmitWatermark <= 3'b0;
ReceiveWatermark <= 3'b0;
InterruptEnable <= 2'b0;
InterruptPending <= 2'b0;
end else begin // writes
/* verilator lint_off CASEINCOMPLETE */
case (state)
CS_INACTIVE: begin
CS_SCKCount <= 9'b1;
SCK_CSCount <= 9'b10;
FrameCount <= 4'b0;
InterCSCount <= 9'b10;
InterXFRCount <= 9'b1;
if ((~TransmitFIFOReadEmpty | ~TransmitShiftEmpty) & ((|(Delay0[7:0])) | ~SckMode[0])) state <= DELAY_0;
else if ((~TransmitFIFOReadEmpty | ~TransmitShiftEmpty)) begin
state <= ACTIVE_0;
SPICLK <= ~SckMode[1];
end else SPICLK <= SckMode[1];
end
DELAY_0: begin
CS_SCKCount <= CS_SCKCount + 9'b1;
if (CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1)) begin
state <= ACTIVE_0;
SPICLK <= ~SckMode[1];
end
end
ACTIVE_0: begin
FrameCount <= FrameCount + 4'b1;
SPICLK <= SckMode[1];
state <= ACTIVE_1;
end
ACTIVE_1: begin
InterXFRCount <= 9'b1;
if (FrameCount < Format[4:1]) begin
state <= ACTIVE_0;
SPICLK <= ~SckMode[1];
end
else if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty)) begin
state <= ACTIVE_0;
SPICLK <= ~SckMode[1];
CS_SCKCount <= 9'b1;
SCK_CSCount <= 9'b10;
FrameCount <= 4'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
SCK_CSCount <= SCK_CSCount + 9'b1;
if (SCK_CSCount >= (({Delay0[15:8], 1'b0}) + ImplicitDelay2)) state <= INTER_CS;
end
INTER_CS: begin
InterCSCount <= InterCSCount + 9'b1;
SPICLK <= SckMode[1];
if (InterCSCount >= ({Delay1[7:0],1'b0})) state <= CS_INACTIVE;
end
INTER_XFR: begin
CS_SCKCount <= 9'b1;
SCK_CSCount <= 9'b10;
FrameCount <= 4'b0;
InterCSCount <= 9'b10;
InterXFRCount <= InterXFRCount + 9'b1;
if ((InterXFRCount >= ({Delay1[15:8], 1'b0})) & (~TransmitFIFOReadEmptyDelay | ~TransmitShiftEmpty)) begin
state <= ACTIVE_0;
SPICLK <= ~SckMode[1];
end else if (~|ChipSelectMode[1:0]) state <= CS_INACTIVE;
else SPICLK <= SckMode[1];
end
endcase
/* verilator lint_off CASEINCOMPLETE */
end
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 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])));
assign TransmitInactive = ((state == INTER_CS) | (state == CS_INACTIVE) | (state == INTER_XFR) | (ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode) | ((state == ACTIVE_1) & ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty) & (FrameCount == Format[4:1]))));
assign Active0 = (state == ACTIVE_0);
assign ShiftEdgeSPICLK = ZeroDiv ? ~SPICLK : SPICLK;
// Signal tracks which edge of sck to shift data
always_comb
case(SckMode[1:0])
2'b00: ShiftEdge = ShiftEdgeSPICLK & SCLKenable;
2'b01: ShiftEdge = (~ShiftEdgeSPICLK & ~FirstFrame & (|(FrameCount) | (CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1))) & SCLKenable & (FrameCount != Format[4:1]) & ~TransmitInactive);
2'b10: ShiftEdge = ~ShiftEdgeSPICLK & SCLKenable;
2'b11: ShiftEdge = (ShiftEdgeSPICLK & ~FirstFrame & (|(FrameCount) | (CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1))) & SCLKenable & (FrameCount != Format[4:1]) & ~TransmitInactive);
default: ShiftEdge = ShiftEdgeSPICLK & SCLKenable;
if (Memwrite)
case(Entry) // flop to sample inputs
SPI_SCKDIV: SckDiv <= Din[11:0];
SPI_SCKMODE: SckMode <= Din[1:0];
SPI_CSID: ChipSelectID <= Din[1:0];
SPI_CSDEF: ChipSelectDef <= Din[3:0];
SPI_CSMODE: ChipSelectMode <= Din[1:0];
SPI_DELAY0: Delay0 <= {Din[23:16], Din[7:0]};
SPI_DELAY1: Delay1 <= {Din[23:16], Din[7:0]};
SPI_FMT: Format <= {Din[19:16], Din[2]};
SPI_TXMARK: TransmitWatermark <= Din[2:0];
SPI_RXMARK: ReceiveWatermark <= Din[2:0];
SPI_IE: InterruptEnable <= Din[1:0];
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];
always_ff @(posedge PCLK)
if(~PRESETn) TransmitShiftReg <= 8'b0;
else if (TransmitShiftRegLoadSingleCycle) TransmitShiftReg <= TransmitDataEndian;
else if (ShiftEdge & Active) TransmitShiftReg <= {TransmitShiftReg[6:0], TransmitShiftReg[0]};
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
assign ShiftIn = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn;
// Receive shift register
always_ff @(posedge PCLK)
if(~PRESETn) ReceiveShiftReg <= 8'b0;
else if (SampleEdge & SCLKenable) begin
if (~Active) ReceiveShiftReg <= 8'b0;
else ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn};
end
// 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
assign SPIIntr = |(InterruptPending & InterruptEnable);
// 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]};
if (Memwrite)
case(Entry)
SPI_TXDATA: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0];
endcase
/* verilator lint_off CASEINCOMPLETE */
// 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
SPI_SCKDIV: Dout <= {20'b0, SckDiv};
SPI_SCKMODE: Dout <= {30'b0, SckMode};
SPI_CSID: Dout <= {30'b0, ChipSelectID};
SPI_CSDEF: Dout <= {28'b0, ChipSelectDef};
SPI_CSMODE: Dout <= {30'b0, ChipSelectMode};
SPI_DELAY0: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]};
SPI_DELAY1: Dout <= {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]};
SPI_FMT: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0};
SPI_TXDATA: Dout <= {TransmitFIFOWriteFull, 23'b0, 8'b0};
SPI_RXDATA: Dout <= {ReceiveFIFOReadEmpty, 23'b0, ReceiveData[7:0]};
SPI_TXMARK: Dout <= {29'b0, TransmitWatermark};
SPI_RXMARK: Dout <= {29'b0, ReceiveWatermark};
SPI_IE: Dout <= {30'b0, InterruptEnable};
SPI_IP: Dout <= {30'b0, InterruptPending};
default: Dout <= 32'b0;
endcase
end
assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto;
// 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
// When SckDiv is 0, count doesn't work and SCLKenable is simply PCLK *** dh 10/26/24: this logic is seriously broken. SCLK is not scaled to PCLK/(2*(SckDiv + 1)).
// SPI Controller module -------------------------------------------
// This module controls state and timing signals that drive the rest of this module
assign ResetSCLKenable = Memwrite & (Entry == SPI_SCKDIV);
assign FrameLength = Format[4:1];
spi_controller controller(PCLK, PRESETn,
// Transmit Signals
TransmitStart, TransmitStartD, ResetSCLKenable,
// Register Inputs
SckDiv, SckMode, ChipSelectMode, Delay0, Delay1, FrameLength,
// txFIFO stuff
TransmitFIFOReadEmpty,
// Timing
SCLKenable, ShiftEdge, SampleEdge, EndOfFrame, EndOfFrameDelay,
// State stuff
Transmitting, InactiveState,
// Outputs
SPICLK);
// Transmit FIFO ---------------------------------------------------
always_ff @(posedge PCLK)
if (~PRESETn) begin
TransmitFIFOWriteIncrement <= 1'b0;
end else begin
TransmitFIFOWriteIncrement <= (Memwrite & (Entry == SPI_TXDATA) & ~TransmitFIFOWriteFull);
end
always_ff @(posedge PCLK)
if (~PRESETn) begin
TransmitFIFOReadIncrement <= 1'b0;
end else if (SCLKenable) begin
TransmitFIFOReadIncrement <= TransmitStartD | (EndOfFrameDelay & ~TransmitFIFOReadEmpty) ;
end
// Check whether TransmitReg has been loaded.
// We use this signal to prevent returning to the Ready state for TransmitStart
logic TransmitRegLoaded;
always_ff @(posedge PCLK) begin
if (~PRESETn) begin
TransmitRegLoaded <= 1'b0;
end else if (TransmitLoad) begin
TransmitRegLoaded <= 1'b1;
end else if (ShiftEdge | EndOfFrameDelay) begin
TransmitRegLoaded <= 1'b0;
end
end
// Setup TransmitStart state machine
always_ff @(posedge PCLK) begin
if (~PRESETn) begin
CurrState <= READY;
end else begin
CurrState <= NextState;
end
end
// State machine for starting transmissions
always_comb begin
case (CurrState)
READY: if (~TransmitFIFOReadEmpty & ~Transmitting) NextState = START;
else NextState = READY;
START: NextState = WAIT;
WAIT: if (~Transmitting & ~TransmitRegLoaded) NextState = READY;
else NextState = WAIT;
default: NextState = READY;
endcase
end
assign TransmitStart = (CurrState == START);
always_ff @(posedge PCLK)
if (~PRESETn) TransmitStartD <= 1'b0;
else if (TransmitStart) TransmitStartD <= 1'b1;
else if (SCLKenable) TransmitStartD <= 1'b0;
spi_fifo #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn,
TransmitFIFOWriteIncrement, TransmitFIFOReadIncrement,
TransmitData[7:0],
TransmitWriteWatermarkLevel, TransmitWatermark[2:0],
TransmitFIFOReadData[7:0],
TransmitFIFOWriteFull,
TransmitFIFOReadEmpty,
TransmitWriteMark, TransmitReadMark);
// Receive FIFO ----------------------------------------------------
always_ff @(posedge PCLK)
if (~PRESETn) begin
ReceiveFIFOReadIncrement <= 1'b0;
end else begin
ReceiveFIFOReadIncrement <= ((Entry == SPI_RXDATA) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement);
end
always_ff @(posedge PCLK)
if (~PRESETn) begin
ReceiveFIFOWriteInc <= 1'b0;
end else if (SCLKenable) begin
ReceiveFIFOWriteInc <= EndOfFrameDelay;
end
spi_fifo #(3,8) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn,
ReceiveFIFOWriteInc, ReceiveFIFOReadIncrement,
ReceiveShiftRegEndian, ReceiveWatermark[2:0],
ReceiveReadWatermarkLevel,
ReceiveData[7:0],
ReceiveFIFOWriteFull,
ReceiveFIFOReadEmpty,
RecieveWriteMark, RecieveReadMark);
// Transmit shift register
assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~TransmitFIFOReadEmpty);
assign TransmitDataEndian = Format[0] ? {<<{TransmitFIFOReadData[7:0]}} : TransmitFIFOReadData[7:0];
always_ff @(posedge PCLK)
if(~PRESETn) TransmitReg <= 8'b0;
else if (TransmitLoad) TransmitReg <= TransmitDataEndian;
else if (ShiftEdge) TransmitReg <= {TransmitReg[6:0], TransmitReg[0]};
assign SPIOut = TransmitReg[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
assign ShiftIn = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn;
// Receive shift register
always_ff @(posedge PCLK)
if(~PRESETn) begin
ReceiveShiftReg <= 8'b0;
end else if (SampleEdge) begin
if (~Transmitting) ReceiveShiftReg <= 8'b0;
else ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn};
end
// 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[7:0]}} : ASR[7:0];
// Interrupt logic: raise interrupt if any enabled interrupts are pending
assign SPIIntr = |(InterruptPending & InterruptEnable);
// Chip select logic
assign ChipSelectInternal = InactiveState ? ChipSelectDef : ~ChipSelectDef;
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

View File

@ -1,9 +1,8 @@
///////////////////////////////////////////
// spi_controller.sv
//
// Written: jacobpease@protonmail.com
// Written: Jacob Pease jacobpease@protonmail.com
// Created: October 28th, 2024
// Modified:
//
// Purpose: Controller logic for SPI
//
@ -12,7 +11,7 @@
// A component of the CORE-V-WALLY configurable RISC-V project.
// https://github.com/openhwgroup/cvw
//
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
@ -28,21 +27,35 @@
// and limitations under the License.
////////////////////////////////////////////////////////////////////////////////////////////////
module spi_controller (
module spi_controller (
input logic PCLK,
input logic PRESETn,
// Start Transmission
input logic TransmitStart,
input logic TransmitStartD,
input logic ResetSCLKenable,
// Registers
input logic [11:0] SckDiv,
input logic [1:0] SckMode,
input logic [1:0] CSMode,
input logic [15:0] Delay0,
input logic [15:0] Delay1,
input logic [7:0] txFIFORead,
input logic [15:0] Delay0,
input logic [15:0] Delay1,
input logic [3:0] FrameLength,
// Is the Transmit FIFO Empty?
input logic txFIFOReadEmpty,
output logic SPICLK,
output logic SPIOUT,
output logic CS
// Control signals
output logic SCLKenable,
output logic ShiftEdge,
output logic SampleEdge,
output logic EndOfFrame,
output logic EndOfFrameDelay,
output logic Transmitting,
output logic InactiveState,
output logic SPICLK
);
// CSMode Stuff
@ -55,36 +68,35 @@ module spi_controller (
// SCLKenable stuff
logic [11:0] DivCounter;
logic SCLKenable;
logic SCLKenableEarly;
logic SCLKenableLate;
logic EdgeTiming;
// logic SCLKenable;
// logic SCLKenableEarly;
logic ZeroDiv;
logic Clock0;
logic Clock1;
logic SCK; // SUPER IMPORTANT, THIS CAN'T BE THE SAME AS SPICLK!
// Shift and Sample Edges
logic PreShiftEdge;
logic PreSampleEdge;
logic ShiftEdge;
logic SampleEdge;
// logic ShiftEdge;
// logic SampleEdge;
logic ShiftEdgePulse;
logic SampleEdgePulse;
logic EndOfFramePulse;
// Frame stuff
logic [2:0] BitNum;
logic [3:0] BitNum;
logic LastBit;
logic EndOfFrame;
logic EndOfFrameDelay;
//logic EndOfFrame;
//logic EndOfFrameDelay;
logic PhaseOneOffset;
// Transmit Stuff
logic ContinueTransmit;
// SPIOUT Stuff
logic TransmitLoad;
// logic TransmitLoad;
logic [7:0] TransmitReg;
logic Transmitting;
//logic Transmitting;
logic EndTransmission;
logic HoldMode;
@ -135,15 +147,12 @@ module spi_controller (
// SampleEdge. This makes sure that SPICLK is an output of a register
// and it properly synchronizes signals.
assign SCLKenableLate = DivCounter > SckDiv;
assign SCLKenable = DivCounter == SckDiv;
assign SCLKenableEarly = (DivCounter + 1'b1) == SckDiv;
assign LastBit = BitNum == 3'd7;
assign EdgeTiming = SckDiv > 12'b0 ? SCLKenableEarly : SCLKenable;
// assign SCLKenableEarly = (DivCounter + 1'b1) == SckDiv;
assign LastBit = (BitNum == FrameLength - 4'b1);
//assign SPICLK = Clock0;
assign ContinueTransmit = ~txFIFOReadEmpty & EndOfFrame;
//assign EndOfFrame = SCLKenable & LastBit & Transmitting;
assign ContinueTransmit = ~txFIFOReadEmpty & EndOfFrameDelay;
assign EndTransmission = txFIFOReadEmpty & EndOfFrameDelay;
always_ff @(posedge PCLK) begin
@ -151,10 +160,14 @@ module spi_controller (
DivCounter <= 12'b0;
SPICLK <= SckMode[1];
SCK <= 0;
BitNum <= 3'h0;
BitNum <= 4'h0;
PreShiftEdge <= 0;
PreSampleEdge <= 0;
EndOfFrame <= 0;
CSSCKCounter <= 0;
SCKCSCounter <= 0;
INTERCSCounter <= 0;
INTERXFRCounter <= 0;
end else begin
// TODO: Consolidate into one delay counter since none of the
// delays happen at the same time?
@ -164,27 +177,27 @@ module spi_controller (
SCK <= ~SCK;
end
if ((CurrState == CSSCK) & SCK) begin
if ((CurrState == CSSCK) & SCK & SCLKenable) begin
CSSCKCounter <= CSSCKCounter + 8'd1;
end else begin
end else if (SCLKenable & EndOfCSSCK) begin
CSSCKCounter <= 8'd0;
end
if ((CurrState == SCKCS) & SCK) begin
if ((CurrState == SCKCS) & SCK & SCLKenable) begin
SCKCSCounter <= SCKCSCounter + 8'd1;
end else begin
end else if (SCLKenable & EndOfSCKCS) begin
SCKCSCounter <= 8'd0;
end
if ((CurrState == INTERCS) & SCK) begin
if ((CurrState == INTERCS) & SCK & SCLKenable) begin
INTERCSCounter <= INTERCSCounter + 8'd1;
end else begin
end else if (SCLKenable & EndOfINTERCS) begin
INTERCSCounter <= 8'd0;
end
if ((CurrState == INTERXFR) & SCK) begin
if ((CurrState == INTERXFR) & SCK & SCLKenable) begin
INTERXFRCounter <= INTERXFRCounter + 8'd1;
end else begin
end else if (SCLKenable & EndOfINTERXFR) begin
INTERXFRCounter <= 8'd0;
end
@ -196,10 +209,10 @@ module spi_controller (
end
// Reset divider
if (SCLKenable | TransmitStart) begin
if (SCLKenable | TransmitStart | ResetSCLKenable) begin
DivCounter <= 12'b0;
end else begin
DivCounter = DivCounter + 12'd1;
DivCounter <= DivCounter + 12'd1;
end
// EndOfFrame controller
@ -209,15 +222,16 @@ module spi_controller (
// EndOfFrame <= 1'b0;
// end
// TODO: Rename EndOfFrameDelay to EndOfFrame and remove this logic
if (~TransmitStart) begin
EndOfFrame <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
end
// Increment BitNum
if (ShiftEdge & Transmitting) begin
BitNum <= BitNum + 3'd1;
BitNum <= BitNum + 4'd1;
end else if (EndOfFrameDelay) begin
BitNum <= 3'b0;
BitNum <= 4'b0;
end
end
end
@ -225,26 +239,71 @@ module spi_controller (
// Delay ShiftEdge and SampleEdge by a half PCLK period
// Aligned EXACTLY ON THE MIDDLE of the leading and trailing edges.
// Sweeeeeeeeeet...
assign ShiftEdgePulse = SCLKenable & ~LastBit & Transmitting;
assign SampleEdgePulse = SCLKenable & Transmitting & ~DelayIsNext;
assign EndOfFramePulse = SCLKenable & LastBit & Transmitting;
always_ff @(posedge ~PCLK) begin
if (~PRESETn | TransmitStart) begin
ShiftEdge <= 0;
PhaseOneOffset <= 0;
SampleEdge <= 0;
EndOfFrameDelay <= 0;
end else begin
ShiftEdge <= ((SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & ~LastBit & Transmitting) & PhaseOneOffset;
PhaseOneOffset <= PhaseOneOffset == 0 ? Transmitting & SCLKenable : PhaseOneOffset;
SampleEdge <= (SckMode[1] ^ SckMode[0] ^ ~SPICLK) & SCLKenable & Transmitting;
EndOfFrameDelay <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
end else begin
PhaseOneOffset <= (PhaseOneOffset == 0) ? Transmitting & SCLKenable : ~EndOfFrameDelay;
case(SckMode)
2'b00: begin
ShiftEdge <= SPICLK & ShiftEdgePulse;
SampleEdge <= ~SPICLK & SampleEdgePulse;
EndOfFrameDelay <= SPICLK & EndOfFramePulse;
end
2'b01: begin
ShiftEdge <= ~SPICLK & ShiftEdgePulse & PhaseOneOffset;
SampleEdge <= SPICLK & SampleEdgePulse;
EndOfFrameDelay <= ~SPICLK & EndOfFramePulse;
end
2'b10: begin
ShiftEdge <= ~SPICLK & ShiftEdgePulse;
SampleEdge <= SPICLK & SampleEdgePulse;
EndOfFrameDelay <= ~SPICLK & EndOfFramePulse;
end
2'b11: begin
ShiftEdge <= SPICLK & ShiftEdgePulse & PhaseOneOffset;
SampleEdge <= ~SPICLK & SampleEdgePulse;
EndOfFrameDelay <= SPICLK & EndOfFramePulse;
end
// ShiftEdge <= ((SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & ~LastBit & Transmitting) & PhaseOneOffset;
// PhaseOneOffset <= PhaseOneOffset == 0 ? Transmitting & SCLKenable : ~EndOfFrameDelay;
// SampleEdge <= (SckMode[1] ^ SckMode[0] ^ ~SPICLK) & SCLKenable & Transmitting & ~DelayIsNext;
// EndOfFrameDelay <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
endcase
end
end
// typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
// statetype CurrState, NextState;
assign HoldMode = CSMode == 2'b10;
assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~txFIFOReadEmpty);
assign HoldMode = CSMode == HOLDMODE;
// assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~txFIFOReadEmpty);
logic ContinueTransmitD;
logic NextEndDelay;
logic CurrentEndDelay;
assign NextEndDelay = NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR;
assign CurrentEndDelay = CurrState == SCKCS | CurrState == INTERCS | CurrState == INTERXFR;
always_ff @(posedge PCLK) begin
if (~PRESETn) begin
ContinueTransmitD <= 1'b0;
end else if (NextEndDelay & ~CurrentEndDelay) begin
ContinueTransmitD <= ContinueTransmit;
end else if (EndOfSCKCS & SCLKenable) begin
ContinueTransmitD <= 1'b0;
end
end
always_ff @(posedge PCLK) begin
if (~PRESETn) begin
CurrState <= INACTIVE;
@ -255,71 +314,68 @@ module spi_controller (
always_comb begin
case (CurrState)
INACTIVE: begin // INACTIVE case --------------------------------
if (TransmitStart) begin
if (~HasCSSCK) begin
NextState = TRANSMIT;
end else begin
NextState = CSSCK;
end
end else begin
NextState = INACTIVE;
end
end
CSSCK: begin // DELAY0 case -------------------------------------
if (EndOfCSSCK) begin
NextState = TRANSMIT;
end
end
INACTIVE: if (TransmitStartD) begin
if (~HasCSSCK) NextState = TRANSMIT;
else NextState = CSSCK;
end else begin
NextState = INACTIVE;
end
CSSCK: if (EndOfCSSCK) NextState = TRANSMIT;
else NextState = CSSCK;
TRANSMIT: begin // TRANSMIT case --------------------------------
case(CSMode)
AUTOMODE: begin
if (EndTransmission) begin
NextState = INACTIVE;
end else if (ContinueTransmit) begin
NextState = SCKCS;
end
if (EndTransmission) NextState = INACTIVE;
else if (EndOfFrameDelay) NextState = SCKCS;
else NextState = TRANSMIT;
end
HOLDMODE: begin
if (EndTransmission) begin
NextState = HOLD;
end else if (ContinueTransmit) begin
if (HasINTERXFR) NextState = INTERXFR;
end
if (EndTransmission) NextState = HOLD;
else if (ContinueTransmit & HasINTERXFR) NextState = INTERXFR;
else NextState = TRANSMIT;
end
OFFMODE: begin
if (EndTransmission) NextState = INACTIVE;
else if (ContinueTransmit & HasINTERXFR) NextState = INTERXFR;
else NextState = TRANSMIT;
end
default: NextState = TRANSMIT;
endcase
end
SCKCS: begin // SCKCS case --------------------------------------
if (EndOfSCKCS) begin
if (EndTransmission) begin
if (CSMode == AUTOMODE) NextState = INACTIVE;
else if (CSMode == HOLDMODE) NextState = HOLD;
end else if (ContinueTransmit) begin
if (~ContinueTransmitD) begin
// if (CSMode == AUTOMODE) NextState = INACTIVE;
if (CSMode == HOLDMODE) NextState = HOLD;
else NextState = INACTIVE;
end else begin
if (HasINTERCS) NextState = INTERCS;
else NextState = TRANSMIT;
end
end else begin
NextState = SCKCS;
end
end
HOLD: begin // HOLD mode case -----------------------------------
if (CSMode == AUTOMODE) begin
NextState = INACTIVE;
end else if (TransmitStart) begin // If FIFO is written to, start again.
end else if (TransmitStartD) begin // If FIFO is written to, start again.
NextState = TRANSMIT;
end
end else NextState = HOLD;
end
INTERCS: begin // INTERCS case ----------------------------------
if (EndOfINTERCS) begin
if (HasCSSCK) NextState = CSSCK;
else NextState = TRANSMIT;
end else begin
NextState = INTERCS;
end
end
INTERXFR: begin // INTERXFR case --------------------------------
if (EndOfINTERXFR) begin
NextState = TRANSMIT;
end else begin
NextState = INTERXFR;
end
end
default: begin
@ -330,19 +386,6 @@ module spi_controller (
assign Transmitting = CurrState == TRANSMIT;
assign DelayIsNext = (NextState == CSSCK | NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR);
//
always_ff @(posedge PCLK) begin
if (~PRESETn) begin
TransmitReg <= 8'b0;
end else if (TransmitLoad) begin
TransmitReg <= txFIFORead;
end else if (ShiftEdge) begin
TransmitReg <= {TransmitReg[6:0], TransmitReg[0]};
end
end
assign SPIOUT = TransmitReg[7];
assign CS = CurrState == INACTIVE | CurrState == INTERCS;
assign InactiveState = CurrState == INACTIVE | CurrState == INTERCS;
endmodule