FPGA 实现SPI 主机双工通信 CS前后肩可调 操作时钟频率可调 ,SPI模式可调,传输位宽可调(最大32位)

发布时间 2023-11-29 19:10:06作者: xgj_0817
  1 //testbench
  2 `timescale 1ns/1ns
  3 module  lcd_spi_m_tb();
  4  reg rst_n_i;    
  5  reg spi_clkx_i; 
  6  reg [31:0] spi_data_i;
  7  reg spi_start;  
  8  reg spi_miso_r1,spi_miso_r2;   
  9  wire spi_miso_i;
 10  wire [0:0]  spi_done;
 11  wire [0:0]  spi_busy;
 12  wire [0:0]  spi_cs_o;
 13  wire [0:0]  spi_scl_o;
 14  wire [0:0]  spi_mosi_o;
 15  wire [31:0] spi_data_o;
 16  
 17  localparam CPOL=1'B0 ;
 18  localparam CPHA=1'B1 ;
 19 
 20 
 21 
 22 always #50 spi_clkx_i<=~spi_clkx_i;
 23 initial begin
 24          rst_n_i=0;
 25          spi_clkx_i=0;
 26          #200; 
 27          spi_start=0;        
 28          rst_n_i=1;
 29          @(posedge spi_clkx_i)
 30          spi_data_i=16'h8121;
 31          spi_start=1;
 32          @(posedge spi_clkx_i)
 33          spi_start=0;
 34          @(posedge spi_done)
 35          #200;
 36          @(posedge spi_clkx_i)
 37          spi_data_i=16'h0003;
 38          spi_start=1;
 39          @(posedge spi_clkx_i)
 40          spi_start=0;
 41          @(posedge spi_done) 
 42          #200;
 43          @(posedge spi_clkx_i)
 44          spi_data_i=16'hF227;
 45          spi_start=1;
 46          @(posedge spi_clkx_i)
 47          spi_start=0;
 48          @(posedge spi_done) 
 49          #200;
 50          @(posedge spi_clkx_i)
 51          spi_data_i=16'hA31B;
 52          spi_start=1;
 53          @(posedge spi_clkx_i)
 54          spi_start=0;
 55          @(posedge spi_done) 
 56          $stop;
 57         end
 58         
 59 always @(negedge spi_scl_o or negedge rst_n_i )//要根据CPOL CPHA配置 spi_scl_o采样极性
 60      begin
 61          if(rst_n_i==1'b0)
 62              spi_miso_r1<=1'b1;
 63          else if (spi_cs_o==1'b0 )
 64              spi_miso_r1<=~spi_miso_r1;                         
 65          else
 66              spi_miso_r1<=1'b0;    
 67      end  
 68      
 69 always @(posedge spi_scl_o or negedge rst_n_i )//要根据CPOL CPHA配置 spi_scl_o采样极性
 70      begin
 71          if(rst_n_i==1'b0)
 72              spi_miso_r2<=1'b1;
 73          else if (spi_cs_o==1'b0 )
 74              spi_miso_r2<=~spi_miso_r2;                         
 75          else
 76              spi_miso_r2<=1'b0;    
 77      end            
 78 assign spi_miso_i =(CPOL==1'B0 && CPHA==1'B0 || CPOL==1'B1 && CPHA==1'B1)? spi_miso_r1 :spi_miso_r2;
 79 
 80 lcd_spi_m
 81 #(
 82     .SPI_IN_WIDTH(6'd16),//spi  输入位数
 83     .SPI_OUT_WIDTH(6'd16),//SPI  输出位数
 84     .SPI_CPOL(CPOL),
 85     .SPI_CPHA(CPHA),
 86     .CLK_DIV(6'D5),
 87     .CS_F_DELAY(6'd3),
 88     .CS_B_DELAY(6'd3)
 89     
 90 
 91 
 92 )
 93 lcd_spi_m_inst
 94 (
 95      .rst_n_i            (rst_n_i     )       ,    
 96      .spi_clkx_i        (spi_clkx_i )       ,
 97      .spi_data_i         (spi_data_i  )       ,
 98      .spi_start          (spi_start   )       ,     
 99      .spi_miso_i         (spi_miso_i  )       ,
100      .spi_done           (spi_done    )       ,
101      .spi_busy           (spi_busy    )       ,
102      .spi_cs_o           (spi_cs_o    )       ,
103      .spi_scl_o          (spi_scl_o   )       ,
104      .spi_mosi_o         (spi_mosi_o  )       ,
105      .spi_data_o         (spi_data_o  )    
106              
107 );
108 
109 endmodule
  1 //SPI主程序
  2 //功能:完成32位以内SPI接口的数据双向通信
  3 module  lcd_spi_m
  4 #(
  5     parameter [5:0]SPI_IN_WIDTH  =6'd16,//spi  输入位数
  6     parameter [5:0]SPI_OUT_WIDTH =6'd16,//SPI  输出位数
  7     parameter [0:0]SPI_CPOL=1'b0,//空闲状态SCL电平 0:SCL=0  1:SCL=1
  8     parameter [0:0]SPI_CPHA=1'b0, //SPI数据在哪个SCL边沿有效 0:数据在SCL第一个边沿有效,1:数据在SCL第二个边沿有效,
  9     parameter [5:0]CLK_DIV =6'D3, //设定SPI_SCL半个周期所占的spi_clkx_i时钟个数
 10     parameter [5:0]CS_F_DELAY=6'D1, //CS前延时,相对于spi_scl_o时钟个数
 11     parameter [5:0]CS_B_DELAY=6'D1 //CS后延时,相对于spi_scl_o时钟个数
 12 
 13 
 14 )
 15 (
 16      input  wire  [0:0]  rst_n_i, //复位输入,低电平复位
 17      input  wire  [0:0]  spi_clkx_i,//SPI系统时钟 为SCL输出时钟的倍数 spi_clkx_i=spi_scl_o*2(高电平+低电平)*CLK_DIV
 18      input  wire  [31:0] spi_data_i,//输入32位要从MOSI发送出去的数据
 19      input  wire  [0:0]  spi_start, //单次发送开始,把数据送到spi_data_i 并把spi_start维技一个周期的高电平
 20      input  wire  [0:0]  spi_miso_i,//主机接收从机输出引脚
 21      output reg   [0:0]  spi_done,//SPI完成一次传输并从spi_data_o输出读到的数???
 22      output reg   [0:0]  spi_busy,//SPI忙信号输出,在忙状态时不接收外部数据,高表示忙
 23      output reg   [0:0]  spi_cs_o,//SPI片选信号输出低有效
 24      output wire  [0:0]  spi_scl_o,//SPI 时钟信号输出,请结合CPOL CPHA分析有效???
 25      output reg   [0:0]  spi_mosi_o,//SPI主机输出从机输入接口
 26      output reg   [31:0] spi_data_o//从从机读到的数据在SPI_DONE为高时为有效数据
 27 
 28 
 29 );
 30 localparam [5:0]CS_F_CNT=CS_F_DELAY-6'D1; //CS前面延时
 31 localparam [5:0]CS_B_CNT=CS_B_DELAY-6'D1; //CS前面延时
 32 localparam [5:0] WIDTH_MAX=(SPI_IN_WIDTH>SPI_OUT_WIDTH)? SPI_IN_WIDTH :SPI_OUT_WIDTH;//16
 33 localparam [7:0] SPI_MAX =WIDTH_MAX+CS_F_DELAY+CS_B_DELAY-1'D1;//17
 34 
 35 
 36 reg [7:0] spi_scl_cnt;
 37 reg [7:0 ] clk_cnt;
 38 reg [1:0]  pol_cnt;
 39 reg [0:0]  spi_sclk_r;
 40 wire [0:0] spi_flag;
 41 wire [0:0] pol_flag;
 42 //产生时钟计数
 43 always @(posedge spi_clkx_i  or negedge rst_n_i)
 44      begin
 45          if(rst_n_i==1'b0)
 46              clk_cnt<=8'd0;
 47          else if(spi_busy==1'b1 )
 48              begin
 49                    if(clk_cnt>=CLK_DIV-1'd1)
 50                          clk_cnt<=8'd0;
 51                    else
 52                          clk_cnt<=clk_cnt+1'd1;
 53              end
 54          else
 55                 clk_cnt<=8'd0;
 56      end
 57 
 58 
 59    
 60 //产生SPI_SCL极性计数    
 61 always @(posedge spi_clkx_i  or negedge rst_n_i)
 62      begin
 63          if(rst_n_i==1'b0)
 64              pol_cnt<=2'd0;
 65          else if(pol_cnt>=2'd1 && clk_cnt>=CLK_DIV-1'd1)
 66               pol_cnt<=2'd0;  
 67          else if(clk_cnt>=CLK_DIV-1'd1)
 68                  pol_cnt<=pol_cnt+1'd1;
 69          else 
 70                 pol_cnt<=pol_cnt;
 71      end
 72      
 73 assign pol_flag= (clk_cnt>=CLK_DIV-1'd1)? 1'b1:1'b0;  
 74 
 75 //产生spi_scl时钟计数
 76 always @(posedge spi_clkx_i  or negedge rst_n_i)
 77      begin
 78          if(rst_n_i==1'b0)
 79                   spi_scl_cnt<=15'd0;
 80          else if((spi_scl_cnt>=SPI_MAX) &&(spi_flag==1'b1))
 81                   spi_scl_cnt<=8'd0;
 82          else if(spi_flag==1'b1)
 83                   spi_scl_cnt<=spi_scl_cnt+1'd1;
 84          else 
 85      
 86                 spi_scl_cnt<=spi_scl_cnt;
 87 
 88      end
 89 assign spi_flag=(clk_cnt>=CLK_DIV-1'd1  && pol_cnt>=1'd1)? 1'b1:1'b0;       
 90 
 91 //输出SPI_CS信号
 92 always @(posedge spi_clkx_i  or negedge rst_n_i)
 93      begin
 94          if(rst_n_i==1'b0)
 95              spi_cs_o<=1'b1;
 96          else if(spi_start==1'b1 && spi_busy==1'b0)
 97              spi_cs_o<=1'b0;
 98          else if((spi_scl_cnt>=SPI_MAX) &&(spi_flag==1'b1))
 99              spi_cs_o<=1'b1;
100          else
101             spi_cs_o<=spi_cs_o;
102      end
103 
104 //输出SPI_DONE信号
105 always @(posedge spi_clkx_i  or negedge rst_n_i)
106      begin
107          if(rst_n_i==1'b0)
108              spi_done<=1'b0;
109          else if((spi_scl_cnt>=SPI_MAX) &&(spi_flag==1'b1))
110              spi_done<=1'b1;
111          else 
112              spi_done<=1'b0;
113      end
114 
115 
116 //输出spi_scl信号
117 always @(posedge spi_clkx_i  or negedge rst_n_i)
118      begin
119          if(rst_n_i==1'b0)
120              begin
121                  spi_sclk_r<=1'b0;
122              end
123          else if(pol_flag==1'b1)
124                  spi_sclk_r<=~spi_sclk_r;
125          else
126               begin
127                   spi_sclk_r<=spi_sclk_r;               
128               end                           
129      end
130 assign spi_scl_o =((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)))?((SPI_CPOL)? ~spi_sclk_r:spi_sclk_r):((SPI_CPOL==1'B1)? 1'B1:1'B0);
131 
132      
133 //在spi_start???时捕获数???
134 reg [31:0] temp_data_i;
135 //输出SPI_MOSI信号
136 always @(posedge spi_clkx_i  or negedge rst_n_i)
137      begin
138          if(rst_n_i==1'b0)
139              begin
140                  spi_mosi_o<=1'b0;
141                  spi_busy<=1'b0;
142                  temp_data_i<=32'b0;
143              end
144          else if(spi_start==1'b1 && spi_busy==1'b0)  //在spi_start???时捕获数???
145                 begin
146                     temp_data_i<=spi_data_i;
147                     spi_busy<=1'b1;
148                 end
149         else if(spi_done==1'b1)
150                  spi_busy<=1'b0;
151         else if(SPI_CPHA==1'b0)
152                  begin
153                      if((spi_scl_cnt>=CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)) &&(spi_flag==1'b1) && (pol_flag==1'b1)  )
154                          begin
155                              spi_mosi_o<=temp_data_i[SPI_OUT_WIDTH-1'd1];
156                              temp_data_i<={temp_data_i[(SPI_OUT_WIDTH-2'd2):0],temp_data_i[SPI_OUT_WIDTH-1'd1]};
157                          end
158                      else
159                          begin
160                              spi_mosi_o<=spi_mosi_o;
161                          end
162 
163                  end
164         else 
165                  begin
166                      if((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT))&& (spi_flag==1'b0) && (pol_flag==1'b1))//CPOL=0 CPHA=1->NG
167                          begin
168                              spi_mosi_o<=temp_data_i[SPI_OUT_WIDTH-1'd1];
169                              temp_data_i<={temp_data_i[SPI_OUT_WIDTH-2'd2:0],temp_data_i[SPI_OUT_WIDTH-1'd0]};
170                          end
171                      else
172                          begin
173                              spi_mosi_o<=spi_mosi_o;
174                          end
175                  end
176 
177 
178                                  
179      end
180 
181 //接收SPI_MISO信号
182 always @(posedge spi_clkx_i or negedge rst_n_i)
183      begin
184          if(rst_n_i==1'b0)
185              spi_data_o<=32'b0;
186          else if(SPI_CPHA==1'b0)
187                  begin
188                      if((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)) &&(pol_flag==1'b1)&& (spi_flag==1'b0)  )
189                          begin
190                              spi_data_o<={spi_data_o[30:0],spi_miso_i};
191                          end
192                      else
193                          begin
194                              spi_data_o<=spi_data_o;
195                          end
196                  end
197         else  if(SPI_CPHA==1'b1)
198                  begin
199                      if((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)) &&(pol_flag==1'b1)&& (spi_flag==1'b1)  )
200                          begin
201                              spi_data_o<={spi_data_o[30:0],spi_miso_i};
202                          end
203                      else
204                          begin
205                              spi_data_o<=spi_data_o;
206                          end
207                  end
208          else
209                  begin
210                         spi_data_o<=spi_data_o;
211                  end
212 
213      end
214 
215 
216 endmodule