前面已经提到过了SPI,在SPI从机的设计中已经讲过SPI的基本原理,这里就不再赘述。对于SPI的主机可以参考百度百科或则笔者前面写的SPI从机介绍的相关知识。
下面是SPI_master的代码
SPI_master.v
//************************************************************************** // *** file name : SPI_master.v // *** version : 1.0 // *** Description : SPI bus timing generation, supports four SPI modes // *** Blogs : https://www.cnblogs.com/WenGalois123/ // *** Author : Galois_V // *** Date : 2022.07.19 // *** Changes : Initial //************************************************************************** `timescale 1ns/1ps module SPI_master #( parameter CLK_EDGE = 47 , parameter RX_WIDTH = 16 ) ( input i_sys_clk , input i_sys_rstn , output reg o_spi_cs , output reg o_spi_sclk , output o_spi_mosi , input i_spi_miso , input i_spi_start , output o_spi_state , input i_spi_cpol , input i_spi_cpha , input [15:0] i_spi_rate , input [1:0] i_spi_ctrl , input i_spi_tx_valid , input [23:0] i_spi_tx_data , output o_spi_tx_req , output o_spi_rx_valid , output [RX_WIDTH-1:0] o_spi_rx_data ); wire w_sclk_egde_over ; reg r_spi_over ; reg r_spi_state_en ; reg r_spi_start ; reg [15:0] r_sclk_egde_cnt ; reg r_spi_get_state ; reg r_spi_over_dly ; reg [15:0] r_spi_sclk_cnt ; reg [3:0] r_spi_sclk_bit_cnt ; reg [23:0] r_spi_tx_buff ; reg [2:0] r_spi_rx_en ; reg [RX_WIDTH-1:0] r_spi_rx_buff ; reg [1:0] r_i_spi_miso ; reg [4:0] r_spi_rx_valid ; wire w_spi_read_en ; wire w_spi_write_en ; wire w_spi_sclk_reverse_en ; wire w_spi_rx_valid ; assign w_spi_read_en = i_spi_ctrl[0]; assign w_spi_write_en = i_spi_ctrl[1]; always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_start <= 'd0; end else begin r_spi_start <= i_spi_start; end end always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_state_en <= 'd0; end else if(r_spi_over) begin r_spi_state_en <= 'd0; end else if(i_spi_start) begin r_spi_state_en <= 1'b1; end end assign o_spi_state = ~r_spi_state_en; /******************************************************************************\ Sample Edge Count \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_sclk_egde_cnt <= 'd0; end else if(r_spi_start) begin r_sclk_egde_cnt <= 'd0; end else if(r_spi_over) begin r_sclk_egde_cnt <= r_sclk_egde_cnt; end else if(w_sclk_egde_over) begin r_sclk_egde_cnt <= r_sclk_egde_cnt + 1'b1; end end always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_over <= 'd0; end else if((r_sclk_egde_cnt == CLK_EDGE) & w_sclk_egde_over) begin r_spi_over <= 1'b1; end else begin r_spi_over <= 'd0; end end always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_get_state <= 'd0; end else if(r_spi_get_state & i_spi_tx_valid | r_spi_over) begin r_spi_get_state <= 'd0; end else if(w_spi_write_en & r_spi_start) begin r_spi_get_state <= 1'b1; end end assign o_spi_tx_req = (~r_spi_over & r_spi_get_state & i_spi_tx_valid) & w_spi_write_en; /******************************************************************************\ Generate SPI CS \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_over_dly <= 'd0; end else begin r_spi_over_dly <= r_spi_over; end end always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin o_spi_cs <= 1'b1; end else if(r_spi_over_dly) begin o_spi_cs <= 1'b1; end else if(i_spi_start) begin o_spi_cs <= 'd0; end end /******************************************************************************\ Generate SPI SCLK \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_sclk_cnt <= 'd0; end else if(r_spi_start | w_spi_sclk_reverse_en) begin r_spi_sclk_cnt <= 'd0; end else begin r_spi_sclk_cnt <= r_spi_sclk_cnt + 1'b1; end end assign w_spi_sclk_reverse_en = (r_spi_sclk_cnt == i_spi_rate); always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_sclk_bit_cnt <= 'd0; end else if(r_spi_start | r_spi_over) begin r_spi_sclk_bit_cnt <= 'd0; end else if(w_spi_sclk_reverse_en) begin r_spi_sclk_bit_cnt <= r_spi_sclk_bit_cnt + 1'b1; end end assign w_sclk_egde_over = w_spi_sclk_reverse_en & r_spi_state_en; /******************************************************************************\ SPI Mode CPOL,CPHA \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin o_spi_sclk <= 'd0; end else if(r_spi_start) begin o_spi_sclk <= i_spi_cpol ^ i_spi_cpha; end else if(~r_spi_state_en |((r_sclk_egde_cnt == CLK_EDGE) & w_sclk_egde_over)) begin o_spi_sclk <= i_spi_cpol; end else if(w_spi_sclk_reverse_en) begin o_spi_sclk <= ~o_spi_sclk; end end /******************************************************************************\ SPI MOSI \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn | r_spi_over) begin r_spi_tx_buff <= 'd0; end else if(o_spi_tx_req) begin r_spi_tx_buff <= i_spi_tx_data; end else if(r_spi_state_en & r_spi_sclk_bit_cnt[0] & w_spi_sclk_reverse_en) begin r_spi_tx_buff <= r_spi_tx_buff << 1; end end /******************************************************************************\ SPI MISO \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_i_spi_miso <= 'd0; end else if(w_spi_read_en) begin r_i_spi_miso <= {r_i_spi_miso[0],i_spi_miso}; end end always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_rx_en <= 'd0; end else if(w_spi_read_en) begin r_spi_rx_en <= {r_spi_rx_en[1:0], (r_spi_state_en & r_spi_sclk_bit_cnt[0] & w_spi_sclk_reverse_en)}; end end /******************************************************************************\ Delayed sampling,Because of external physical delay \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_rx_buff <= 'd0; end else if(r_spi_rx_en[2]) begin r_spi_rx_buff <= {r_spi_rx_buff[14:0],r_i_spi_miso[1]}; end end /******************************************************************************\ Increase effective signal delay to ensure data stability \******************************************************************************/ always@(posedge i_sys_clk) begin if(~i_sys_rstn) begin r_spi_rx_valid <= 'd0; end else begin r_spi_rx_valid <= {r_spi_rx_valid[3:0],w_spi_rx_valid}; end end assign w_spi_rx_valid = w_sclk_egde_over & (r_sclk_egde_cnt == CLK_EDGE) & w_spi_read_en; assign o_spi_rx_valid = r_spi_rx_valid[4]; assign o_spi_rx_data = r_spi_rx_buff; assign o_spi_mosi = r_spi_tx_buff[23]; endmodule
这里提下,笔者写代码比较喜欢写通用点的代码,这里速率是可以控制,不过最大只能是系统时钟的1/4,SPI的模式也支持4种模式,至于代码是否没问题,笔者不能保证,笔者在仿真测试中还未曾发现有太大问题,也仅供参考。这里可以用AXI4_lite总线来对该模块代码进行按需控制,可以把例化的参数CLK_EDGE也用总线配置。
标签:en,SPI,主机,spi,Verilog,output,input,reg From: https://www.cnblogs.com/WenGalois123/p/16924488.html