利用fpga(verilog)实现SPI-flash芯片全擦除实验

最近在学习spi协议,看了看野火的视频,感觉野火的代码是一坨大便,寄存器太多了,看的眼花缭乱。跟着野火的波形图做了一遍,仿真正确但是上板没成功。看了看师兄的代码,然后自己又换了一种方法实现全擦除。最后上板成功,各位大佬有更好的见解可以和我交流,代码如下:

// -----------------------------------------------------------------------------
// Create : 2023-11-29
// Revise : 2023-11-29
// Note   : spi驱动,模式0,时钟默认低电平,上升沿数据有效,主机模式,目前只有单字节发送功能
// 该flash快速命令最高支持50M时钟,常规命令最高支持20M时钟,所以SPI时钟使用主时钟四分频12.5M
// -----------------------------------------------------------------------------

module spi_driver (
    clk,
    reset,
    data,
	spi_miso,
    spi_flag,
    spi_cs,
    spi_sck,
    spi_mosi
);
    input clk;
    input reset;
    input[7:0]data;      //指令数据
    input spi_flag;       //指令数据脉冲
	input spi_miso;	 
    output reg spi_cs;
    output wire spi_sck;
    output wire spi_mosi;


    reg[1:0]cnt ;     //用于四分频
    reg[2:0]cnt_data; //用于计数当前发送的数据位
    reg[7:0]data_r;

//将data向后拉一个时钟周期
    always @(posedge clk or negedge reset) begin
        if(!reset)
            data_r <= 0;
        else if(spi_flag) 
            data_r <= data;
        end
//cnt
    always @(posedge clk or negedge reset) begin
        if(!reset)
            cnt <= 0;
        else if(!spi_cs)
            cnt <= cnt+1'b1;
    end

//产生12.5mhz的sck
    assign spi_sck = cnt[1];  

//8位数据的计数
    always @(posedge clk or negedge reset) begin
        if(!reset)
            cnt_data <= 0;
        else if(cnt == 2'b11)
            cnt_data <= cnt_data+1'b1;
    end

//片选信号,低电平有效
    always @(posedge clk or negedge reset) begin
         if(!reset)
            spi_cs <= 1;
        else if(spi_flag)
            spi_cs <= 0;
        else if(cnt_data == 3'b111&&cnt==2'b11)
            spi_cs <= 1;
    end
 //spi协议规定先发高位,因此将cnt_data取反。
    assign spi_mosi = spi_cs? 0:data_r[~cnt_data];

endmodule
// -----------------------------------------------------------------------------
// Create : 2023-11-29
// Note   : spi flash全擦除实验
// 实验开发板:黑金ax301,flash芯片:M25P16
// 需要用到的命令:
// WREN(Write Enable): 06h
// BE(Bulk Erase): C7h
// 全擦除前要先写入WREN,然后cs拉高需要至少100ns才能开始下一次传输
// Revise : 2023-11-30
// -----------------------------------------------------------------------------

module spi_be (
    clk,
    reset,
    spi_miso,
    spi_mosi,
    spi_cs,
    spi_sck,
);
    input clk;
    input reset;
    input spi_miso;
    output spi_mosi;
    output spi_sck;
    output spi_cs;
	reg [11:0] cnt;
	 
	 //设置全擦除程序运行一次的周期位4095*20ns,当cnt为12'hfff时,cnt将保持不变。
    always @(posedge clk, negedge reset) begin
        if(!reset)
            cnt <= 0;
        else if(cnt < 12'hfff)
            cnt <= cnt + 12'd1;
    end

	 //设置指令的产生和指令标准位脉冲的产生,cnt为2000时,指令为写允许指令“00000110”,产生一个时钟周期的指令标准为脉冲,
	 //cnt为2050时,指令为全擦除指令“11000111”,产生一个时钟周期的指令标准为脉冲。
    wire spi_flag = (cnt == 12'd2000) || (cnt == 12'd2050);
    wire [7:0]data = (cnt == 12'd2000) ? 8'h06 : ((cnt == 12'd2050) ? 8'hc7 : 8'd0);
        

spi_driver spi_driver(
    .clk(clk),
    .reset(reset),
    .data(data),
	.spi_miso(spi_miso),
    .spi_flag(spi_flag),
    .spi_cs(spi_cs),
    .spi_sck(spi_sck),
    .spi_mosi(spi_mosi)
);
endmodule
`timescale 1ns/1ns
module spi_tb;
	reg clk;
	reg reset;
	reg spi_miso;
	wire spi_cs;
	wire spi_sck;
	wire spi_mosi;

 spi_be spi_be(
    .clk(clk),
    .reset(reset),
	 .spi_miso(spi_miso),
    .spi_cs(spi_cs),
    .spi_sck(spi_sck),
    .spi_mosi(spi_mosi)

);

initial clk=0;
always#10 clk=!clk;

initial begin 
	reset=0;
	spi_miso=0;
	#201;
	reset=1;
	#100000
	$stop;
	end
	endmodule
	

仿真波形:

你可能感兴趣的:(fpga开发)