diff --git a/fpga/zsbl/boot.c b/fpga/zsbl/boot.c index d9a824a49..7665b2df2 100644 --- a/fpga/zsbl/boot.c +++ b/fpga/zsbl/boot.c @@ -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); diff --git a/linux/sdcard/flash-sd.sh b/linux/sdcard/flash-sd.sh index 7a2c58d42..31e6ee45f 100755 --- a/linux/sdcard/flash-sd.sh +++ b/linux/sdcard/flash-sd.sh @@ -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 diff --git a/linux/sdcard/write-bytes.sh b/linux/sdcard/write-bytes.sh new file mode 100755 index 000000000..b9c312ceb --- /dev/null +++ b/linux/sdcard/write-bytes.sh @@ -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 ] " 1>&2; exit 1; } + +help() { + echo "Usage: $0 [OPTIONS] " + echo " -i Input text file with hex bytes." + echo " -b 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" diff --git a/src/uncore/spi_apb.sv b/src/uncore/spi_apb.sv index 54a072ac9..293c2c2b5 100644 --- a/src/uncore/spi_apb.sv +++ b/src/uncore/spi_apb.sv @@ -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 diff --git a/src/uncore/spi_controller.sv b/src/uncore/spi_controller.sv index c4305047e..19b32cab3 100644 --- a/src/uncore/spi_controller.sv +++ b/src/uncore/spi_controller.sv @@ -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