123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- ///////////////////////////////////////////////////////////////////////////////
- //Source: https://github.com/nandland/spi-master/tree/master/Verilog/source
- //Description: SPI (Serial Peripheral Interface) Master
- // With single chip-select (AKA Slave Select) capability
- //
- // Supports arbitrary length byte transfers.
- //
- // Instantiates a SPI Master and adds single CS.
- // If multiple CS signals are needed, will need to use different
- // module, OR multiplex the CS from this at a higher level.
- //
- // Note: i_Clk must be at least 2x faster than i_SPI_Clk
- //
- // Parameters: SPI_MODE, can be 0, 1, 2, or 3. See above.
- // Can be configured in one of 4 modes:
- // Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
- // 0 | 0 | 0
- // 1 | 0 | 1
- // 2 | 1 | 0
- // 3 | 1 | 1
- //
- // CLKS_PER_HALF_BIT - Sets frequency of o_SPI_Clk. o_SPI_Clk is
- // derived from i_Clk. Set to integer number of clocks for each
- // half-bit of SPI data. E.g. 100 MHz i_Clk, CLKS_PER_HALF_BIT = 2
- // would create o_SPI_CLK of 25 MHz. Must be >= 2
- //
- // MAX_BYTES_PER_CS - Set to the maximum number of bytes that
- // will be sent during a single CS-low pulse.
- //
- // CS_INACTIVE_CLKS - Sets the amount of time in clock cycles to
- // hold the state of Chip-Selct high (inactive) before next
- // command is allowed on the line. Useful if chip requires some
- // time when CS is high between trasnfers.
- ///////////////////////////////////////////////////////////////////////////////
- `include "iSPI.sv"
-
-
- module SPI_Master_With_Single_CS
- #(parameter SPI_MODE = 0,
- parameter CLKS_PER_HALF_BIT = 2,
- parameter MAX_BYTES_PER_CS = 1,
- parameter CS_INACTIVE_CLKS = 1)
- (
- // Control/Data Signals,
- input i_Rst_L, // FPGA Reset
- input i_Clk, // FPGA Clock
-
- // TX (MOSI) Signals
- input [$clog2(MAX_BYTES_PER_CS+1)-1:0] i_TX_Count, // # bytes per CS low
- input [7:0] i_TX_Byte, // Byte to transmit on MOSI
- input i_TX_DV, // Data Valid Pulse with i_TX_Byte
- output o_TX_Ready, // Transmit Ready for next byte
-
- // RX (MISO) Signals
- output reg [$clog2(MAX_BYTES_PER_CS+1)-1:0] o_RX_Count, // Index RX byte
- output o_RX_DV, // Data Valid pulse (1 clock cycle)
- output [7:0] o_RX_Byte, // Byte received on MISO
-
- // SPI Interface
- output o_SPI_Clk,
- input i_SPI_MISO,
- output o_SPI_MOSI,
- output o_SPI_CS_n
- );
-
- localparam IDLE = 2'b00;
- localparam TRANSFER = 2'b01;
- localparam CS_INACTIVE = 2'b10;
-
- reg [1:0] r_SM_CS;
- reg r_CS_n;
- reg [$clog2(CS_INACTIVE_CLKS)-1:0] r_CS_Inactive_Count;
- reg [$clog2(MAX_BYTES_PER_CS+1)-1:0] r_TX_Count;
- wire w_Master_Ready;
-
- // Instantiate Master
- SPI_Master
- #(.SPI_MODE(SPI_MODE),
- .CLKS_PER_HALF_BIT(CLKS_PER_HALF_BIT)
- ) SPI_Master_Inst
- (
- // Control/Data Signals,
- .i_Rst_L(i_Rst_L), // FPGA Reset
- .i_Clk(i_Clk), // FPGA Clock
-
- // TX (MOSI) Signals
- .i_TX_Byte(i_TX_Byte), // Byte to transmit
- .i_TX_DV(i_TX_DV), // Data Valid Pulse
- .o_TX_Ready(w_Master_Ready), // Transmit Ready for Byte
-
- // RX (MISO) Signals
- .o_RX_DV(o_RX_DV), // Data Valid pulse (1 clock cycle)
- .o_RX_Byte(o_RX_Byte), // Byte received on MISO
-
- // SPI Interface
- .o_SPI_Clk(o_SPI_Clk),
- .i_SPI_MISO(i_SPI_MISO),
- .o_SPI_MOSI(o_SPI_MOSI)
- );
-
-
- // Purpose: Control CS line using State Machine
- always @(posedge i_Clk or negedge i_Rst_L)
- begin
- if (~i_Rst_L)
- begin
- r_SM_CS <= IDLE;
- r_CS_n <= 1'b1; // Resets to high
- r_TX_Count <= 0;
- r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
- end
- else
- begin
-
- case (r_SM_CS)
- IDLE:
- begin
- if (r_CS_n & i_TX_DV) // Start of transmission
- begin
- r_TX_Count <= i_TX_Count - 1; // Register TX Count
- r_CS_n <= 1'b0; // Drive CS low
- r_SM_CS <= TRANSFER; // Transfer bytes
- end
- end
-
- TRANSFER:
- begin
- // Wait until SPI is done transferring do next thing
- if (w_Master_Ready)
- begin
- if (r_TX_Count > 0)
- begin
- if (i_TX_DV)
- begin
- r_TX_Count <= r_TX_Count - 1;
- end
- end
- else
- begin
- r_CS_n <= 1'b1; // we done, so set CS high
- r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
- r_SM_CS <= CS_INACTIVE;
- end // else: !if(r_TX_Count > 0)
- end // if (w_Master_Ready)
- end // case: TRANSFER
-
- CS_INACTIVE:
- begin
- if (r_CS_Inactive_Count > 0)
- begin
- r_CS_Inactive_Count <= r_CS_Inactive_Count - 1'b1;
- end
- else
- begin
- r_SM_CS <= IDLE;
- end
- end
-
- default:
- begin
- r_CS_n <= 1'b1; // we done, so set CS high
- r_SM_CS <= IDLE;
- end
- endcase // case (r_SM_CS)
- end
- end // always @ (posedge i_Clk or negedge i_Rst_L)
-
-
- // Purpose: Keep track of RX_Count
- always @(posedge i_Clk)
- begin
- begin
- if (r_CS_n)
- begin
- o_RX_Count <= 0;
- end
- else if (o_RX_DV)
- begin
- o_RX_Count <= o_RX_Count + 1'b1;
- end
- end
- end
-
- assign o_SPI_CS_n = r_CS_n;
-
- assign o_TX_Ready = ((r_SM_CS == IDLE) | (r_SM_CS == TRANSFER && w_Master_Ready == 1'b1 && r_TX_Count > 0)) & ~i_TX_DV;
-
- endmodule // SPI_Master_With_Single_CS
|