【时序协议】-SPI-驱动模块-实验
分析
SPI驱动模块的作用:
SPI驱动模块在SPI通信中起了一个桥梁的作用,
通过指令发送模块将写操作、读操作等指令输入到驱动模块中,作为data_in,
然后去Flash中读到数据后,通过data_out将读取到的数据返回给指令发送模块
状态机
输出信号
SCK、MOSI
SCK使用计数器实验4分频,在计数器cnt_4[0]=1的地方进行SCK翻转,就可以得到SCK输出信号
MOSI信号也需要使用cnt_4实现,分析如下图
MOSI的重点就是确定出什么时候采集信号、什么时候更新数据(即通过模式确定这2个重点)
一、设计文件
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2022/04/29 21:51:51 // Design Name: // Module Name: SPI // Project Name: // Target Devices: // Tool Versions: // Description: // SPI驱动模块,工作在模式0,即SCK空闲状态为低电平,SCK下降沿更新MOSI的数据,MSB // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module SPI_drive_my( input clk , input rst_n , input spi_start , input spi_end , input [7:0] data_in , input W_R ,//判断模块工作模式:读、写 input spi_MISO , output reg spi_SCK , output reg spi_CE , output reg spi_MOSI , output reg out_done , output reg in_done , output reg [7:0] data_out ); // 定义状态机 parameter IDLE = 3'b001, WR = 3'b010, RD = 3'b100; reg [2:0] state ; reg [2:0] next_state ; //定义计数器 reg [1:0] cnt_4 ; reg [3:0] cnt_8 ; parameter MAX_CNT_4 = 3 , MAX_CNT_8 = 7 ; always@(posedge clk or negedge rst_n)begin if(!rst_n) state <= IDLE ; else state <= next_state ; end always @(*) begin case(state) IDLE: if(spi_start == 1'b1 && W_R == 1'b0) next_state <= RD ; else if(spi_start == 1'b1 && W_R == 1'b1) next_state <= WR ; RD: if(spi_end) next_state <= IDLE ; else if(cnt_4 == MAX_CNT_4-2'd1 && cnt_8 == MAX_CNT_8-4'd1) begin if(W_R == 1'b0) next_state <= RD ; else next_state <= WR ; end WR: if(spi_end) next_state <= IDLE ; else if(cnt_4 == MAX_CNT_4-2'd1 && cnt_8 == MAX_CNT_8-4'd1) begin if(W_R == 1'b0) next_state <= RD ; else next_state <= WR ; end default:next_state <= IDLE ; endcase end // 计数器 // SCK时钟计数器 always@(posedge clk or negedge rst_n)begin if(!rst_n) cnt_4 <= 2'd0 ; else if(state != IDLE) cnt_4 <= cnt_4 + 2'd1 ; end always@(posedge clk or negedge rst_n)begin if(!rst_n) cnt_8 <= 8'd0; else if(cnt_4 == MAX_CNT_4)begin cnt_8 <= cnt_8 + 8'd1; if(cnt_8 == MAX_CNT_8) cnt_8 <= 8'd0; end end // 输出信号 // always@(posedge clk or negedge rst_n)begin // if(!rst_n) // spi_SCK <= 1'd0; // else if(spi_end) // spi_SCK <= 1'd0; // else if(cnt_4[0] && spi_end == 1'b0) // spi_SCK <= ~spi_SCK; // end // always@(posedge clk or negedge rst_n)begin // if(!rst_n) // spi_SCK <= 1'd0; // else if(cnt_4[0] && state != IDLE) // spi_SCK <= ~spi_SCK; // else // spi_SCK <= 1'd0; // end always@(posedge clk or negedge rst_n)begin if(!rst_n) spi_SCK <= 1'd0; else if(cnt_4 == 2'd0 && state != IDLE) spi_SCK <= 2'd0; else if(cnt_4 == 2'd2 && state != IDLE) spi_SCK <= 1'd1; end always@(posedge clk or negedge rst_n)begin if(!rst_n) spi_CE <= 1'b1; else if(spi_start == 1'b1)//SPI开始工作 spi_CE <= 1'b0; else if(next_state == IDLE && cnt_8 == MAX_CNT_8 || spi_end)//SPI结束工作 spi_CE <= 1'b1; else spi_CE <= spi_CE; end always@(posedge clk or negedge rst_n)begin if(!rst_n) spi_MOSI <= 1'b0; else if(!spi_CE && cnt_4 == 2'd0)//0模式、BSM spi_MOSI <= data_in[7-cnt_8]; else if(spi_CE) spi_MOSI <= 1'b0; else spi_MOSI <= spi_MOSI; end // 发送数据完成-标志位 always@(posedge clk or negedge rst_n)begin if(!rst_n) out_done <= 1'b0; else if(cnt_4 == 2'd3 && cnt_8 == 3'd7 && state == RD) out_done <= 1'd1; else out_done <= 1'b0; end // 接收数据完成-标志位 always@(posedge clk or negedge rst_n)begin if(!rst_n) in_done <= 1'b0; else if(cnt_4 == 2'd2 && cnt_8 == 3'd7 && state == WR) in_done <= 1'd1; else in_done <= 1'b0; end always@(posedge clk or negedge rst_n)begin if(!rst_n) data_out <= 8'd0; else if(cnt_4 == 2'd0 && state == RD) data_out[7-cnt_8] <= spi_MISO; else data_out <= data_out; end endmodule
二、测试文件
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2022/05/05 22:42:17 // Design Name: // Module Name: tb_SPI_drive // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module tb_SPI_drive(); reg clk,rst_n,spi_start,spi_end,W_R; reg [7:0]data_in; wire spi_SCK,spi_CE,spi_MOSI,out_done,in_done,spi_MISO; wire [7:0]data_out; initial begin clk = 1'b0; //初始时钟为0 rst_n <= 1'b0; //初始复位 spi_start <= 1'b0; data_in <= 8'd0; spi_end <= 1'b0; W_R <= 1'b1; #80 //80个时钟周期后 rst_n <= 1'b1; //拉高复位,系统进入工作状态 #30 //30个时钟周期后拉高SPI开始信号,开始SPI传输 spi_start <= 1'b1; data_in <= 8'h06;//写使能 #20 spi_start <= 1'b0; @(posedge in_done); //一个BYTE发送完成 spi_start <= 1'b1; data_in <= 8'h02;//PP @(posedge in_done); spi_start <= 1'b1; data_in <= 8'b01010101;//地址 @(posedge in_done); spi_start <= 1'b1; data_in <= 8'b01010101;//地址 @(posedge in_done); spi_start <= 1'b1; data_in <= 8'b01010101;//地址 @(posedge in_done); W_R <= 1'b0; spi_start <= 1'b1; @(negedge spi_SCK); force spi_MISO = 1'b0; @(negedge spi_SCK); force spi_MISO = 1'b1; @(negedge spi_SCK); force spi_MISO = 1'b0; @(negedge spi_SCK); force spi_MISO = 1'b1; @(negedge spi_SCK); force spi_MISO = 1'b1; @(negedge spi_SCK); force spi_MISO = 1'b0; @(negedge spi_SCK); force spi_MISO = 1'b0; @(negedge spi_SCK); force spi_MISO = 1'b1; @(negedge out_done); spi_end <= 1'b1; #20 spi_end <= 1'b0; //拉高一个周期结束信号 end //------------<设置时钟>---------------------------------------------- always #10 clk = ~clk; SPI_drive_my SPI_drive_my_inst ( .clk(clk) , .rst_n(rst_n) , .spi_start(spi_start) , .spi_end(spi_end) , .data_in(data_in) , .W_R(W_R) , .spi_MISO(spi_MISO) , .spi_SCK(spi_SCK) , .spi_CE(spi_CE) , .spi_MOSI(spi_MOSI) , .out_done(out_done) , .in_done(in_done) , .data_out(data_out) ); endmodule
三、波形图