240 lines
7.5 KiB
Systemverilog
240 lines
7.5 KiB
Systemverilog
///////////////////////////////////////////////////////////////////////////////
|
|
//Source: https://github.com/nandland/spi-master/tree/master/Verilog/source
|
|
// Description: SPI (Serial Peripheral Interface) Master
|
|
// Creates master based on input configuration.
|
|
// Sends a byte one bit at a time on MOSI
|
|
// Will also receive byte data one bit at a time on MISO.
|
|
// Any data on input byte will be shipped out on MOSI.
|
|
//
|
|
// To kick-off transaction, user must pulse i_TX_DV.
|
|
// This module supports multi-byte transmissions by pulsing
|
|
// i_TX_DV and loading up i_TX_Byte when o_TX_Ready is high.
|
|
//
|
|
// This module is only responsible for controlling Clk, MOSI,
|
|
// and MISO. If the SPI peripheral requires a chip-select,
|
|
// this must be done 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
|
|
// More: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers
|
|
// 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
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
module SPI_Master
|
|
#(parameter SPI_MODE = 0,
|
|
parameter CLKS_PER_HALF_BIT = 2)
|
|
(
|
|
// Control/Data Signals,
|
|
input i_Rst_L, // FPGA Reset
|
|
input i_Clk, // FPGA Clock
|
|
|
|
// TX (MOSI) Signals
|
|
input [7:0] i_TX_Byte, // Byte to transmit on MOSI
|
|
input i_TX_DV, // Data Valid Pulse with i_TX_Byte
|
|
output reg o_TX_Ready, // Transmit Ready for next byte
|
|
|
|
// RX (MISO) Signals
|
|
output reg o_RX_DV, // Data Valid pulse (1 clock cycle)
|
|
output reg [7:0] o_RX_Byte, // Byte received on MISO
|
|
|
|
// SPI Interface
|
|
output reg o_SPI_Clk,
|
|
input i_SPI_MISO,
|
|
output reg o_SPI_MOSI
|
|
);
|
|
|
|
// SPI Interface (All Runs at SPI Clock Domain)
|
|
wire w_CPOL; // Clock polarity
|
|
wire w_CPHA; // Clock phase
|
|
|
|
reg [$clog2(CLKS_PER_HALF_BIT*2)-1:0] r_SPI_Clk_Count;
|
|
reg r_SPI_Clk;
|
|
reg [4:0] r_SPI_Clk_Edges;
|
|
reg r_Leading_Edge;
|
|
reg r_Trailing_Edge;
|
|
reg r_TX_DV;
|
|
reg [7:0] r_TX_Byte;
|
|
|
|
reg [2:0] r_RX_Bit_Count;
|
|
reg [2:0] r_TX_Bit_Count;
|
|
|
|
// CPOL: Clock Polarity
|
|
// CPOL=0 means clock idles at 0, leading edge is rising edge.
|
|
// CPOL=1 means clock idles at 1, leading edge is falling edge.
|
|
assign w_CPOL = (SPI_MODE == 2) | (SPI_MODE == 3);
|
|
|
|
// CPHA: Clock Phase
|
|
// CPHA=0 means the "out" side changes the data on trailing edge of clock
|
|
// the "in" side captures data on leading edge of clock
|
|
// CPHA=1 means the "out" side changes the data on leading edge of clock
|
|
// the "in" side captures data on the trailing edge of clock
|
|
assign w_CPHA = (SPI_MODE == 1) | (SPI_MODE == 3);
|
|
|
|
|
|
|
|
// Purpose: Generate SPI Clock correct number of times when DV pulse comes
|
|
always @(posedge i_Clk or negedge i_Rst_L)
|
|
begin
|
|
if (~i_Rst_L)
|
|
begin
|
|
o_TX_Ready <= 1'b0;
|
|
r_SPI_Clk_Edges <= 0;
|
|
r_Leading_Edge <= 1'b0;
|
|
r_Trailing_Edge <= 1'b0;
|
|
r_SPI_Clk <= w_CPOL; // assign default state to idle state
|
|
r_SPI_Clk_Count <= 0;
|
|
end
|
|
else
|
|
begin
|
|
|
|
// Default assignments
|
|
r_Leading_Edge <= 1'b0;
|
|
r_Trailing_Edge <= 1'b0;
|
|
|
|
if (i_TX_DV)
|
|
begin
|
|
o_TX_Ready <= 1'b0;
|
|
r_SPI_Clk_Edges <= 16; // Total # edges in one byte ALWAYS 16
|
|
end
|
|
else if (r_SPI_Clk_Edges > 0)
|
|
begin
|
|
o_TX_Ready <= 1'b0;
|
|
|
|
if (r_SPI_Clk_Count == CLKS_PER_HALF_BIT*2-1)
|
|
begin
|
|
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
|
|
r_Trailing_Edge <= 1'b1;
|
|
r_SPI_Clk_Count <= 0;
|
|
r_SPI_Clk <= ~r_SPI_Clk;
|
|
end
|
|
else if (r_SPI_Clk_Count == CLKS_PER_HALF_BIT-1)
|
|
begin
|
|
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
|
|
r_Leading_Edge <= 1'b1;
|
|
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
|
|
r_SPI_Clk <= ~r_SPI_Clk;
|
|
end
|
|
else
|
|
begin
|
|
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
|
|
end
|
|
end
|
|
else
|
|
begin
|
|
o_TX_Ready <= 1'b1;
|
|
end
|
|
|
|
|
|
end // else: !if(~i_Rst_L)
|
|
end // always @ (posedge i_Clk or negedge i_Rst_L)
|
|
|
|
|
|
// Purpose: Register i_TX_Byte when Data Valid is pulsed.
|
|
// Keeps local storage of byte in case higher level module changes the data
|
|
always @(posedge i_Clk or negedge i_Rst_L)
|
|
begin
|
|
if (~i_Rst_L)
|
|
begin
|
|
r_TX_Byte <= 8'h00;
|
|
r_TX_DV <= 1'b0;
|
|
end
|
|
else
|
|
begin
|
|
r_TX_DV <= i_TX_DV; // 1 clock cycle delay
|
|
if (i_TX_DV)
|
|
begin
|
|
r_TX_Byte <= i_TX_Byte;
|
|
end
|
|
end // else: !if(~i_Rst_L)
|
|
end // always @ (posedge i_Clk or negedge i_Rst_L)
|
|
|
|
|
|
// Purpose: Generate MOSI data
|
|
// Works with both CPHA=0 and CPHA=1
|
|
always @(posedge i_Clk or negedge i_Rst_L)
|
|
begin
|
|
if (~i_Rst_L)
|
|
begin
|
|
o_SPI_MOSI <= 1'b0;
|
|
r_TX_Bit_Count <= 3'b111; // send MSb first
|
|
end
|
|
else
|
|
begin
|
|
// If ready is high, reset bit counts to default
|
|
if (o_TX_Ready)
|
|
begin
|
|
r_TX_Bit_Count <= 3'b111;
|
|
end
|
|
// Catch the case where we start transaction and CPHA = 0
|
|
else if (r_TX_DV & ~w_CPHA)
|
|
begin
|
|
o_SPI_MOSI <= r_TX_Byte[3'b111];
|
|
r_TX_Bit_Count <= 3'b110;
|
|
end
|
|
else if ((r_Leading_Edge & w_CPHA) | (r_Trailing_Edge & ~w_CPHA))
|
|
begin
|
|
r_TX_Bit_Count <= r_TX_Bit_Count - 1;
|
|
o_SPI_MOSI <= r_TX_Byte[r_TX_Bit_Count];
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Purpose: Read in MISO data.
|
|
always @(posedge i_Clk or negedge i_Rst_L)
|
|
begin
|
|
if (~i_Rst_L)
|
|
begin
|
|
o_RX_Byte <= 8'h00;
|
|
o_RX_DV <= 1'b0;
|
|
r_RX_Bit_Count <= 3'b111;
|
|
end
|
|
else
|
|
begin
|
|
|
|
// Default Assignments
|
|
o_RX_DV <= 1'b0;
|
|
|
|
if (o_TX_Ready) // Check if ready is high, if so reset bit count to default
|
|
begin
|
|
r_RX_Bit_Count <= 3'b111;
|
|
end
|
|
else if ((r_Leading_Edge & ~w_CPHA) | (r_Trailing_Edge & w_CPHA))
|
|
begin
|
|
o_RX_Byte[r_RX_Bit_Count] <= i_SPI_MISO; // Sample data
|
|
r_RX_Bit_Count <= r_RX_Bit_Count - 1;
|
|
if (r_RX_Bit_Count == 3'b000)
|
|
begin
|
|
o_RX_DV <= 1'b1; // Byte done, pulse Data Valid
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Purpose: Add clock delay to signals for alignment.
|
|
always @(posedge i_Clk or negedge i_Rst_L)
|
|
begin
|
|
if (~i_Rst_L)
|
|
begin
|
|
o_SPI_Clk <= w_CPOL;
|
|
end
|
|
else
|
|
begin
|
|
o_SPI_Clk <= r_SPI_Clk;
|
|
end // else: !if(~i_Rst_L)
|
|
end // always @ (posedge i_Clk or negedge i_Rst_L)
|
|
|
|
|
|
endmodule // SPI_Master
|