timescale 1ns / 1ps //spi module spi#( parameter SYSCLK 50_000_000, parameter SPICLK 20_000_000 )( input sysclk, input rst_n, //用户接口 input spi_req,//spi的开始信号 input spi_valid,//数据有效信号 input [7:0] data_din,//数据通过MOSI 发送给从机的数据 input [7:0] spi_byte_number,//字节数本次通信能够发送的字节数 output reg spi_byte_done,//每发送一个字节的完成信号 output reg spi_done, //spi通信结束发送数据完成的信号 output reg [7:0] spi_dout, //从机给主机的数据采集MISO的 //标准四线spi接口 output reg CS, //片选信号 用来选择从机拉低有效 output reg SCK, //时钟线用于同步数据收发 output reg MOSI , //主机发送 从机接收 input MISO //主机接收从机发送 ); localparam delay SYSCLK / SPICLK; //SCK的分频系数用于对SCK分频 localparam MID delay 1; //二分之一的SCK的位置 ( 代表右移一位 等效于 /2) localparam Q_MID delay 2; //四分之一的SCK的位置 ( 代表右移两位 等效于 /4) localparam TQ_MID MID Q_MID; //四分之三的SCK的位置 reg [31:0] cnt_sck;//用于对SCK计数 reg [31:0] cnt_bit;//对数据进行计数 reg [31:0] cnt_byte;//对发送字节进行计数 reg [7:0] data;//对输入进来的数据打拍目的将数据同步到当前时钟域下 // 对输入进来的数据打拍 always(posedge sysclk) if(!rst_n) data0; else if(spi_valid) datadata_din; else datadata; //cnt_sck对分频计数器赋值 always(posedge sysclk) if(!rst_n) cnt_sck0; else if(CS0)begin if(cnt_sckdelay-1) cnt_sck0; else cnt_sckcnt_sck1; end else cnt_sck0; //cnt_bit always(posedge sysclk) if(!rst_n) cnt_bit0; else if(CS0)begin if(cnt_sckdelay-1)begin if(cnt_bit7) cnt_bit0; else cnt_bitcnt_bit1; end else cnt_bitcnt_bit ; end else cnt_bit0; //----cnt_byte字节计数 always (posedge sysclk ) begin if(!rst_n) cnt_byte 0; else if(CS0)begin if(cnt_bit 7 cnt_sck delay - 1)begin //发生完成一个字节 if(cnt_byte spi_byte_number - 1) //将所有的字节发生完毕 cnt_byte 0; else cnt_byte cnt_byte 1; end else cnt_byte cnt_byte; end else cnt_byte 0; end //CS always(posedge sysclk) if(!rst_n||spi_done) //当复位或者spi通信结束的时候CS拉高 CS1; else if(spi_req) CS0; else CSCS; //spi_byte_done always(posedge sysclk) if(!rst_n) spi_byte_done0; else if(cnt_sckdelay-1cnt_bit7) spi_byte_done1; else spi_byte_done0; //spi_done always(posedge sysclk) if(!rst_n) spi_done0; else if(cnt_sckdelay-1cnt_bit7cnt_bytespi_byte_number-1)//所有的字节发生完毕 spi_done1; else spi_done0; //spi_dout采集从机返回MISO)的数据 always(posedge sysclk) if(!rst_n) spi_dout0; else if(CS0)begin if(cnt_sckMID-1)//数据采样 spi_dout{spi_dout[6:0],MISO};//采集进来的数据是高位 else spi_doutspi_dout; end else spi_dout0; //SCK 0模式 always(posedge sysclk) if(!rst_n) SCK0;//空闲时刻 SCK为低电平 else if(CS0)begin if(cnt_sckQ_MIDcnt_sckTQ_MID-1) //四分之一 到 四分之三的时候拉高 SCK1; else SCK0; end else SCKSCK; //MOSI always(posedge sysclk) if(!rst_n) MOSI0;//空闲时刻 SPI时序没有明确规定是高电平 还是 低电平 else if(CS0)begin if(cnt_sckMID-1)//数据跟新 MOSIdata_din[7-cnt_bit]; else MOSIMOSI; end else MOSI0; endmodule仿真timescale 1ns / 1ps module tb_spi( ); reg sysclk ; reg rst_n ; reg spi_req ; reg [7:0] data_din ; reg [7:0] spi_byte_number; reg MISO; reg spi_valid; wire spi_byte_done ; wire spi_done ; wire [7:0] spi_dout ; wire CS ; wire SCK ; wire MOSI ; initial begin sysclk 0; rst_n 0; spi_req 0; spi_valid 1; MISO 1; data_din 8b1010_1010; spi_byte_number 5; #101 rst_n1; spi_req1; #500 spi_req 1; //开始信号 #20 spi_req 0; end always #10 sysclk~sysclk; spi#( . SYSCLK ( 50_0), . SPICLK ( 20_0) )spi_u( . sysclk (sysclk ) , . rst_n (rst_n ) , . spi_req ( spi_req ) , . spi_valid (spi_valid ) ,//spi的开始信号 . data_din ( data_din ) ,//数据通过MOSI 发送给从机的数据 . spi_byte_number ( spi_byte_number) ,//字节数本次通信能够发送的字节数 . spi_byte_done ( spi_byte_done ) ,//每发送一个字节的完成信号 . spi_done ( spi_done ) , //spi通信结束发送数据完成的信号 . spi_dout ( spi_dout ) , //从机给主机的数据采集MISO的 . CS ( CS ) , //片选信号 用来选择从机拉低有效 . SCK ( SCK ) , //时钟线用于同步数据收发 . MOSI ( MOSI ) , //主机发送 从机接收 . MISO ( MISO ) //主机接收从机发送 ); endmodule