利用FPGA(verilog)实现SPI-FLASH芯片扇区擦除

一.M25P16flash芯片介绍

本设计使用了M25P16flash芯片,它拥有16Mbit的空间。M25P16flash芯片有32个扇区,每个扇区有256页,每页有256个位空间。32*256*256=2097152=16M。因此它的地址有24位。它的各扇区地址如下表。

  利用FPGA(verilog)实现SPI-FLASH芯片扇区擦除_第1张图片

二.扇区擦除原理

扇区擦除(SE)指令可以按照扇区擦除 Flash。和块擦除不同的是,扇区擦除是要指定扇区地址,扇区擦除前也需要发送写使能指令。时序图如下:

              利用FPGA(verilog)实现SPI-FLASH芯片扇区擦除_第2张图片

同全擦除一样,首先得发送写允许指令,然后发送扇区擦除指令。和全擦除不同的是它的扇区擦除指令后面得加一个24位的地址。例如我们现在要把第一扇区擦除,地址的高八位就得是01h,另外24位可以为0000h-ffffh中的任意值。

三.代码

此代码没有像野火那样使用状态机,具体如下:

// -----------------------------------------------------------------------------
// Create : 2023-12-21
// Note   : spi flash扇区擦除实验
// 实验开发板:黑金ax301,flash芯片:M25P16
// 需要用到的命令:
// WREN(Write Enable): 06h
// SE(Sector Erase): D8h
// 扇区擦除前要先写入WREN,然后cs拉高需要至少100ns才能开始下一次传输,本代码将擦除第0扇区
// Revise : 2023-12-2
// -----------------------------------------------------------------------------

module spi_se (
    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时,指令为扇区擦除指令“d8001213h”,产生一个时钟周期的指令标准为脉冲。
    wire data_flag_1 = cnt == 12'd2000;
	wire data_flag_2 = cnt == 12'd2050;
    wire [31:0]data = (cnt == 12'd2000) ? 32'h00000006 : ((cnt == 12'd2050) ? 32'hd8001213 : 8'd0);
        

    spi_driver spi_driver(
        .clk(clk),
        .reset(reset),
        .data(data),
        .spi_miso(spi_miso),
        .data_flag_1(data_flag_1),
		.data_flag_2(data_flag_2),
        .spi_cs(spi_cs),
        .spi_sck(spi_sck),
        .spi_mosi(spi_mosi)
    );
endmodule
// -----------------------------------------------------------------------------
// Create : 2023-11-31
// Revise : 2023-12-2
// Note   : spi驱动,模式0,时钟默认低电平,上升沿数据有效,主机模式,目前只有单字节发送功能
// 该flash快速命令最高支持50M时钟,常规命令最高支持20M时钟,所以SPI时钟使用主时钟四分频12.5M
// -----------------------------------------------------------------------------

module spi_driver (
    clk,
    reset,
    data,
	spi_miso,
    data_flag_1,//写允许指令标准位
    data_flag_2,//扇区擦除指令标准位
    spi_cs,
    spi_sck,
    spi_mosi
);
    input clk;
    input reset;
    input[31:0]data;      //指令数据
    input data_flag_1;    //写允许指令标准位
    input data_flag_2;    //扇区擦除指令标志位
	input spi_miso;	 
    output reg spi_cs;
    output wire spi_sck;
    output reg spi_mosi;


    reg[1:0]cnt ;     //用于四分频
    reg[4:0]cnt_data; //用于计数当前发送的数据位
    reg[31:0]data_r;
    reg[1:0]data_flag_r;//01表示发写允许指令,10表示发扇区擦除指令

//当写允许指令来的时候,data_flag_r为2‘b01,扇区擦除指令来的时候为2'b10
    always @(posedge clk or negedge reset) begin
        if(!reset)
            data_flag_r <= 0;
        else if(data_flag_1)
            data_flag_r <= 2'b01;
        else if(data_flag_2)
            data_flag_r <= 2'b10;    
    end

//data_r,由于写允许指令只有8位,因此把写允许指令给data_r的低8位,剩下的24位补0
    always @(posedge clk or negedge reset) begin
        if(!reset)
            data_r <= 0;
        else if(data_flag_1) 
            data_r <= {24'h00,data[7:0]};
        else if(data_flag_2)
            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)
			if(data_flag_r == 2'b01 && cnt_data == 5'd7)
				cnt_data <= 0;
			else
				cnt_data <= cnt_data+1'b1;
    end
	 

//片选信号,低电平有效
    always @(posedge clk or negedge reset) begin
        if(!reset)
            spi_cs <= 1;
        else if(data_flag_1 || data_flag_2)
            spi_cs <= 0;
        else if(data_flag_r == 2'b01 && cnt_data == 5'b00111 && cnt == 2'b11)
            spi_cs <= 1;
        else if(data_flag_r == 2'b10 && cnt_data == 5'b11111 && cnt == 2'b11)
            spi_cs <= 1;
    end
 //spi协议规定先发高位,因此将cnt_data取反。
    always @(*) begin
		  if(!reset)
			spi_mosi <= 0;
        else if(data_flag_r == 2'b01)
            spi_mosi <= spi_cs? 0 : data_r[~cnt_data[2:0]];//此时数据有效位为7:0位
        else if(data_flag_r == 2'b10)
            spi_mosi <= spi_cs? 0 : data_r[~cnt_data];
    end
 

endmodule
`timescale 1ns/1ns
module spi_tb;
	reg clk;
	reg reset;
	reg spi_miso;
	wire spi_cs;
	wire spi_sck;
	wire spi_mosi;

 spi_se spi_se(
    .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(verilog)实现SPI-FLASH芯片扇区擦除_第3张图片

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