spi从机的Verilog实现2.0
前面已经提过了SPI协议的主从机,并用代码实现了。不过之前的版本是用系统时钟实现的,现在是直接通过SPI的时钟敏感进行边沿采样。参考了下github上一位大神的代码如下链接https://github.com/nandland/spi-slave:
1 /////////////////////////////////////////////////////////////////////////////// 2 // Description: SPI (Serial Peripheral Interface) Slave 3 // Creates slave based on input configuration. 4 // Receives a byte one bit at a time on MOSI 5 // Will also push out byte data one bit at a time on MISO. 6 // Any data on input byte will be shipped out on MISO. 7 // Supports multiple bytes per transaction when CS_n is kept 8 // low during the transaction. 9 // 10 // Note: i_Clk must be at least 4x faster than i_SPI_Clk 11 // MISO is tri-stated when not communicating. Allows for multiple 12 // SPI Slaves on the same interface. 13 // 14 // Parameters: SPI_MODE, can be 0, 1, 2, or 3. See above. 15 // Can be configured in one of 4 modes: 16 // Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA) 17 // 0 | 0 | 0 18 // 1 | 0 | 1 19 // 2 | 1 | 0 20 // 3 | 1 | 1 21 // More info: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers 22 /////////////////////////////////////////////////////////////////////////////// 23 24 module SPI_Slave 25 #(parameter SPI_MODE = 0) 26 ( 27 // Control/Data Signals, 28 input i_Rst_L, // FPGA Reset, active low 29 input i_Clk, // FPGA Clock 30 output reg o_RX_DV, // Data Valid pulse (1 clock cycle) 31 output reg [7:0] o_RX_Byte, // Byte received on MOSI 32 input i_TX_DV, // Data Valid pulse to register i_TX_Byte 33 input [7:0] i_TX_Byte, // Byte to serialize to MISO. 34 35 // SPI Interface 36 input i_SPI_Clk, 37 output o_SPI_MISO, 38 input i_SPI_MOSI, 39 input i_SPI_CS_n // active low 40 ); 41 42 43 // SPI Interface (All Runs at SPI Clock Domain) 44 wire w_CPOL; // Clock polarity 45 wire w_CPHA; // Clock phase 46 wire w_SPI_Clk; // Inverted/non-inverted depending on settings 47 wire w_SPI_MISO_Mux; 48 49 reg [2:0] r_RX_Bit_Count; 50 reg [2:0] r_TX_Bit_Count; 51 reg [7:0] r_Temp_RX_Byte; 52 reg [7:0] r_RX_Byte; 53 reg r_RX_Done, r2_RX_Done, r3_RX_Done; 54 reg [7:0] r_TX_Byte; 55 reg r_SPI_MISO_Bit, r_Preload_MISO; 56 57 // CPOL: Clock Polarity 58 // CPOL=0 means clock idles at 0, leading edge is rising edge. 59 // CPOL=1 means clock idles at 1, leading edge is falling edge. 60 assign w_CPOL = (SPI_MODE == 2) | (SPI_MODE == 3); 61 62 // CPHA: Clock Phase 63 // CPHA=0 means the "out" side changes the data on trailing edge of clock 64 // the "in" side captures data on leading edge of clock 65 // CPHA=1 means the "out" side changes the data on leading edge of clock 66 // the "in" side captures data on the trailing edge of clock 67 assign w_CPHA = (SPI_MODE == 1) | (SPI_MODE == 3); 68 69 assign w_SPI_Clk = w_CPHA ? ~i_SPI_Clk : i_SPI_Clk; 70 71 72 73 // Purpose: Recover SPI Byte in SPI Clock Domain 74 // Samples line on correct edge of SPI Clock 75 always @(posedge w_SPI_Clk or posedge i_SPI_CS_n) 76 begin 77 if (i_SPI_CS_n) 78 begin 79 r_RX_Bit_Count <= 0; 80 r_RX_Done <= 1'b0; 81 end 82 else 83 begin 84 r_RX_Bit_Count <= r_RX_Bit_Count + 1; 85 86 // Receive in LSB, shift up to MSB 87 r_Temp_RX_Byte <= {r_Temp_RX_Byte[6:0], i_SPI_MOSI}; 88 89 if (r_RX_Bit_Count == 3'b111) 90 begin 91 r_RX_Done <= 1'b1; 92 r_RX_Byte <= {r_Temp_RX_Byte[6:0], i_SPI_MOSI}; 93 end 94 else if (r_RX_Bit_Count == 3'b010) 95 begin 96 r_RX_Done <= 1'b0; 97 end 98 99 end // else: !if(i_SPI_CS_n) 100 end // always @ (posedge w_SPI_Clk or posedge i_SPI_CS_n) 101 102 103 104 // Purpose: Cross from SPI Clock Domain to main FPGA clock domain 105 // Assert o_RX_DV for 1 clock cycle when o_RX_Byte has valid data. 106 always @(posedge i_Clk or negedge i_Rst_L) 107 begin 108 if (~i_Rst_L) 109 begin 110 r2_RX_Done <= 1'b0; 111 r3_RX_Done <= 1'b0; 112 o_RX_DV <= 1'b0; 113 o_RX_Byte <= 8'h00; 114 end 115 else 116 begin 117 // Here is where clock domains are crossed. 118 // This will require timing constraint created, can set up long path. 119 r2_RX_Done <= r_RX_Done; 120 121 r3_RX_Done <= r2_RX_Done; 122 123 if (r3_RX_Done == 1'b0 && r2_RX_Done == 1'b1) // rising edge 124 begin 125 o_RX_DV <= 1'b1; // Pulse Data Valid 1 clock cycle 126 o_RX_Byte <= r_RX_Byte; 127 end 128 else 129 begin 130 o_RX_DV <= 1'b0; 131 end 132 end // else: !if(~i_Rst_L) 133 end // always @ (posedge i_Bus_Clk) 134 135 136 // Control preload signal. Should be 1 when CS is high, but as soon as 137 // first clock edge is seen it goes low. 138 always @(posedge w_SPI_Clk or posedge i_SPI_CS_n) 139 begin 140 if (i_SPI_CS_n) 141 begin 142 r_Preload_MISO <= 1'b1; 143 end 144 else 145 begin 146 r_Preload_MISO <= 1'b0; 147 end 148 end 149 150 151 // Purpose: Transmits 1 SPI Byte whenever SPI clock is toggling 152 // Will transmit read data back to SW over MISO line. 153 // Want to put data on the line immediately when CS goes low. 154 always @(posedge w_SPI_Clk or posedge i_SPI_CS_n) 155 begin 156 if (i_SPI_CS_n) 157 begin 158 r_TX_Bit_Count <= 3'b111; // Send MSb first 159 r_SPI_MISO_Bit <= r_TX_Byte[3'b111]; // Reset to MSb 160 end 161 else 162 begin 163 r_TX_Bit_Count <= r_TX_Bit_Count - 1; 164 165 // Here is where data crosses clock domains from i_Clk to w_SPI_Clk 166 // Can set up a timing constraint with wide margin for data path. 167 r_SPI_MISO_Bit <= r_TX_Byte[r_TX_Bit_Count]; 168 169 end // else: !if(i_SPI_CS_n) 170 end // always @ (negedge w_SPI_Clk or posedge i_SPI_CS_n_SW) 171 172 173 // Purpose: Register TX Byte when DV pulse comes. Keeps registed byte in 174 // this module to get serialized and sent back to master. 175 always @(posedge i_Clk or negedge i_Rst_L) 176 begin 177 if (~i_Rst_L) 178 begin 179 r_TX_Byte <= 8'h00; 180 end 181 else 182 begin 183 if (i_TX_DV) 184 begin 185 r_TX_Byte <= i_TX_Byte; 186 end 187 end // else: !if(~i_Rst_L) 188 end // always @ (posedge i_Clk or negedge i_Rst_L) 189 190 // Preload MISO with top bit of send data when preload selector is high. 191 // Otherwise just send the normal MISO data 192 assign w_SPI_MISO_Mux = r_Preload_MISO ? r_TX_Byte[3'b111] : r_SPI_MISO_Bit; 193 194 // Tri-state MISO when CS is high. Allows for multiple slaves to talk. 195 assign o_SPI_MISO = i_SPI_CS_n ? 1'bZ : w_SPI_MISO_Mux; 196 197 endmodule // SPI_Slave
笔者写的代码如下:
1 //************************************************************************** 2 // *** file name : SPI_slave_if.v 3 // *** version : 1.0 4 // *** Description : SPI_slave_if 5 // *** Blogs : https://www.cnblogs.com/WenGalois123/ 6 // *** Author : Galois_V 7 // *** Date : 2023.7.9 8 // *** Changes : Initial 9 //************************************************************************** 10 `timescale 1ns/10ps 11 module SPI_slave_if 12 #( 13 parameter DATA_WIDTH = 8 14 ) 15 ( 16 input i_sys_clk , 17 input i_sys_rstn , 18 input i_spi_cs , 19 input i_spi_sclk , 20 input i_spi_mosi , 21 output reg o_spi_miso , 22 input i_spi_cpha , 23 input i_spi_cpol , 24 input [DATA_WIDTH-1:0] i_tx_data , 25 input i_tx_valid , 26 output reg [DATA_WIDTH-1:0] o_rx_data , 27 output reg o_rx_valid 28 ); 29 wire w_spi_sclk ; 30 reg [DATA_WIDTH-1:0] r_tx_data ; 31 reg [7:0] r_tx_cnt ; 32 reg r_spi_miso ; 33 reg [DATA_WIDTH-1:0] r_rx_data ; 34 reg [7:0] r_rx_cnt ; 35 reg r_rx_done ; 36 reg [2:0] r_rx_valid ; 37 /******************************************************************************\ 38 SPI slave mode select and cs control sclk 39 \******************************************************************************/ 40 assign w_spi_sclk = i_spi_cs ? 1'b0 : ((i_spi_cpha ^ i_spi_cpol) ? ~i_spi_sclk : i_spi_sclk); 41 /******************************************************************************\ 42 SPI slave miso 43 \******************************************************************************/ 44 always@(posedge i_sys_clk) 45 begin 46 if(~i_sys_rstn) 47 r_tx_data <= 'd0; 48 else if(i_tx_valid) 49 r_tx_data <= i_tx_data; 50 end 51 always@(negedge w_spi_sclk) 52 begin 53 if(~i_sys_rstn) 54 begin 55 o_spi_miso <= i_spi_cpha ? 'd0 : r_tx_data[DATA_WIDTH-1]; //cpha = 1,1st edge tx 1st bit data,cpha = 0 2sec edge tx second bit data 56 r_tx_cnt <= i_spi_cpha ? DATA_WIDTH-1 : DATA_WIDTH-2; 57 end 58 else 59 begin 60 if(r_rx_cnt == 0) 61 begin 62 r_rx_cnt <= DATA_WIDTH - 1; 63 end 64 else 65 begin 66 r_rx_cnt <= r_rx_cnt -1'b1; 67 end 68 o_spi_miso <= r_tx_data[r_tx_cnt]; 69 end 70 end 71 /******************************************************************************\ 72 SPI slave mosi 73 \******************************************************************************/ 74 always@(posedge w_spi_sclk) 75 begin 76 if(~i_sys_rstn) 77 begin 78 r_rx_cnt <= 'd0; 79 r_rx_data <= 'd0; 80 r_rx_done <= 'd0; 81 end 82 else if(r_rx_cnt == DATA_WIDTH-1) 83 begin 84 r_rx_done <= 1'b1; 85 r_rx_cnt <= r_rx_cnt + 1'b1; 86 r_rx_data <= {r_rx_data[DATA_WIDTH-2:0],i_spi_mosi}; 87 end 88 else 89 begin 90 r_rx_cnt <= r_rx_cnt + 1'b1; 91 r_rx_done <= 'd0; 92 r_rx_data <= {r_rx_data[DATA_WIDTH-2:0],i_spi_mosi}; 93 end 94 end 95 always@(posedge w_spi_sclk) 96 begin 97 if(~i_sys_rstn) 98 r_rx_valid <= 'd0; 99 else 100 r_rx_valid <= {r_rx_valid[1:0],r_rx_done}; 101 end 102 always@(posedge i_sys_clk) 103 begin 104 if(~i_sys_rstn) 105 begin 106 o_rx_data <= 'd0; 107 o_rx_valid <= 'd0; 108 end 109 else if(r_rx_valid[1]&(~r_rx_valid[2])) 110 begin 111 o_rx_valid <= 1'b1; 112 o_rx_data <= r_rx_data; 113 end 114 else 115 begin 116 o_rx_valid <= 1'b0; 117 o_rx_data <= 'd0; 118 end 119 end 120 121 endmodule