【协议时序】- SPI-PP写操作-实验
分析
时序图
在下面的时序图中,指令之前还有一段写使能指令,所以这里写入SPI驱动模块的数据有:
写使能指令:8bit
PP指令:8bit
地址:24bit
数据:8bit
注意:
1.在传完写使能指令后,由于2个指令之间需要等待tpp=200ns的时间,因此,在等待时间,SCK时钟不再继续产生,写要将CE拉高
2.W_R=0:读操作;W_R=1:写操作
3.给data_in赋值时用的组合逻辑,用时序逻辑时,data_in会丢失数据(data_in组合逻辑上面的程序注释里就是时序逻辑)
流程框图
状态机
一、设计文件
页写操作模块
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2022/05/10 15:39:07 // Design Name: // Module Name: SPI_PP // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module SPI_PP( input clk , input rst_n , input PP_start , input out_done , input in_done , input [7:0] data_out , output reg spi_start , output reg spi_end , output reg W_R , output reg[7:0] data_in ); // 指令 parameter WREN = 8'b0000_0010, PP = 8'b0000_0110, SECTOR_ADDR = 8'b1000_0010, PAGE_ADDR = 8'b0010_0100, BYTE_ADDR = 8'b0100_0001, DATA = 8'b1011_1100; // 计数器 parameter MAX_BIT = 6'd40; // 状态机参数 parameter IDLE = 6'b000001, WREN_STATE = 6'b000010, DELAY = 6'b000100, PP_STATE_01 = 6'b001000, PP_STATE_02 = 6'b010000, PP_STATE_03 = 6'b100000; // 定义计数器 reg [5:0] cnt_39; reg [7:0] cnt_byte; reg [3:0] cnt_10; // 定义状态机 reg [5:0] state; reg [5:0] next_state; //------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) state <= IDLE; else state <= next_state; end always @(*) begin case(state) IDLE: if(PP_start) next_state <= WREN_STATE; else next_state <= IDLE; WREN_STATE: if(in_done && cnt_byte == 8'd1-1) next_state <= DELAY; else next_state <= WREN_STATE; DELAY: if(cnt_10 == 4'd9) next_state <= PP_STATE_01; else next_state <= DELAY; PP_STATE_01: if(in_done && cnt_byte == 8'd1-1) next_state <= PP_STATE_02; else next_state <= PP_STATE_01; PP_STATE_02: if(in_done && cnt_byte == 8'd3-1) next_state <= PP_STATE_03; else next_state <= PP_STATE_02; PP_STATE_03: if(in_done && cnt_byte == 8'd1-1) next_state <= IDLE; else next_state <= PP_STATE_03; default: next_state <= IDLE; endcase end //------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) spi_start <= 1'b0; else if(state == IDLE && next_state == WREN_STATE) spi_start <= 1'b1; else if(state == DELAY && next_state == PP_STATE_01) spi_start <= 1'b1; else spi_start <= 1'b0; end // spi_end要拉高2次 // 传完WREN拉高一次 // 传完PP指令-地址-数据后拉高一次 always @(posedge clk or negedge rst_n) begin if(!rst_n) spi_end <= 1'b0; else if(state == WREN_STATE && next_state == DELAY) spi_end <= 1'b1; else if(state == PP_STATE_03 && next_state == IDLE)//状态为PP+位数计数器记到最大值 spi_end <= 1'b1; else spi_end <= 1'b0; end always @(posedge clk or negedge rst_n) begin if(!rst_n) W_R <= 1'b0;//默认为读操作 else W_R <= 1'b1; end
//data_in时序逻辑
// always @(posedge clk or negedge rst_n) begin
// if(!rst_n)
// data_in <= 8'd0;
// else if(state == IDLE && next_state == WREN_STATE)
// data_in <= WREN;
// else if(state == DELAY && next_state == PP_STATE_01)
// data_in <= PP;
// else if(state == PP_STATE_02)
// case(cnt_byte)
// 8'd0:data_in <= SECTOR_ADDR;
// 8'd1:data_in <= PAGE_ADDR;
// 8'd2:data_in <= BYTE_ADDR;
// default:data_in<=data_in;
// endcase
// else if(state == PP_STATE_03)
// data_in <= DATA;
// else
// data_in <= data_in;
// end
always @(*) begin case (state) WREN_STATE: data_in <= WREN; PP_STATE_01: data_in <= PP; PP_STATE_02: case (cnt_byte) 8'd0:data_in <= SECTOR_ADDR; 8'd1:data_in <= PAGE_ADDR; 8'd2:data_in <= BYTE_ADDR; default: data_in <= 8'd0; endcase PP_STATE_03: data_in <= DATA; default: data_in <= 8'd0; endcase end // 计数器 always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt_10 <= 4'd0; else if(state == DELAY) begin if(cnt_10 == 4'd9) cnt_10 <= 4'd0; else cnt_10 <= cnt_10 + 4'd1; end else cnt_10 <= 4'd0; end always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt_byte <= 8'd0; else if(in_done) begin case (state) DELAY: cnt_byte <= 8'd0; IDLE: cnt_byte <= 8'd0; PP_STATE_01: cnt_byte <= 8'd0; PP_STATE_02: if(cnt_byte == 8'd2) cnt_byte <= 8'd0; else cnt_byte <= cnt_byte + 8'd1; PP_STATE_03: cnt_byte <= 8'd0; default: cnt_byte <= cnt_byte; endcase end else cnt_byte <= cnt_byte; end endmodule
SPI驱动模块
`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_write , input W_R ,//判断模块工作模式:读、写 input spi_MISO , output reg spi_SCK , output reg spi_CE , output reg spi_MOSI , output reg read_done , output reg write_done , output reg [7:0] data_read ); // 定义状态机 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 == 2'd0 && cnt_8 == 8'd0) 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 == 2'd0 && cnt_8 == 8'd0) 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(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_write[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) read_done <= 1'b0; else if(cnt_4 == 2'd3 && cnt_8 == 3'd7 && state == RD) read_done <= 1'd1; else read_done <= 1'b0; end // 接收数据完成-标志位 always@(posedge clk or negedge rst_n)begin if(!rst_n) write_done <= 1'b0; else if(cnt_4 == 2'd2 && cnt_8 == 3'd7 && state == WR) write_done <= 1'd1; else write_done <= 1'b0; end always@(posedge clk or negedge rst_n)begin if(!rst_n) data_read <= 8'd0; else if(cnt_4 == 2'd2 && state == RD) data_read[7-cnt_8] <= spi_MISO; else data_read <= data_read; end endmodule
顶层模块
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2022/05/10 20:33:57 // Design Name: // Module Name: top_SPI_PP_Dirve // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module top_SPI_PP_Dirve( input clk, input rst_n, input spi_MISO, input PP_start, output spi_SCK, output spi_CE, output spi_MOSI ); wire spi_start; wire spi_end; wire W_R; wire out_done; wire in_done; wire [7:0]data_in; wire data_out; SPI_PP SPI_PP_inst ( .clk(clk) , .rst_n(rst_n) , .PP_start(PP_start), .out_done(out_done) , .in_done(in_done) , .data_out(data_out) , .spi_start(spi_start) , .spi_end(spi_end) , .W_R(W_R) , .data_in(data_in) ); 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
二、测试文件
`timescale 1ns / 1ns ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2022/05/10 20:46:39 // Design Name: // Module Name: tb_top_SPI_PP // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module tb_top_SPI_PP; reg clk,rst_n,spi_MISO,PP_start; wire spi_SCK,spi_CE,spi_MOSI; initial begin clk = 1'b0; rst_n = 1'b1; #15 rst_n = 1'b0; spi_MISO = 1'b0; PP_start = 1'b0; #80 rst_n = 1'b1; PP_start = 1'b1; #20 PP_start = 1'b0; @(negedge spi_SCK); spi_MISO <= 1'b0; @(negedge spi_SCK); spi_MISO <= 1'b0; @(negedge spi_SCK); spi_MISO <= 1'b0; @(negedge spi_SCK); spi_MISO <= 1'b0; @(negedge spi_SCK); spi_MISO <= 1'b0; @(negedge spi_SCK); spi_MISO <= 1'b0; @(negedge spi_SCK); spi_MISO <= 1'b1; @(negedge spi_SCK); spi_MISO <= 1'b0; end always #10 clk = ~clk; top_SPI_PP_Dirve top_SPI_PP_Dirve_inst ( .clk(clk), .rst_n(rst_n), .spi_MISO(spi_MISO), .PP_start(PP_start), .spi_SCK(spi_SCK), .spi_CE(spi_CE), .spi_MOSI(spi_MOSI) ); endmodule
三、波形图