09_SPI-Flash 页写实验

09_SPI-Flash 页写实验

  • 1. 实验目标
  • 2. 操作时序
  • 3. 模块框图
    • 3.1 顶层模块
    • 3.2 页写模块
  • 4. 波形图
  • 5. RTL
    • 5.1 flash_pp_ctrl
    • 5.2 spi_flash_pp
  • 6. Testbench
    • 6.1 tb_flash_pp_ctrl
    • 6.2 tb_spi_flash_pp

1. 实验目标

使用页写指令,向 Flash 中写入 N 字节数据,N 为整数,且大于 0 小于等于 256。在本 实 验 中 我 们 向 Flash 芯 片 中 写 入 0-99 , 共 100 字 节 数 据 , 数 据 初 始 地 址 为24’h00_04_25。
注意:在向 Flash 芯片写入数据之前,先要对芯片执行全擦除操作。

2. 操作时序

09_SPI-Flash 页写实验_第1张图片
09_SPI-Flash 页写实验_第2张图片
写满不支持跨页写,在这一页刚开始的地方写。
09_SPI-Flash 页写实验_第3张图片
在这里插入图片描述
09_SPI-Flash 页写实验_第4张图片
扇区地址 s 页地址 p 字节地址 b

3. 模块框图

3.1 顶层模块

09_SPI-Flash 页写实验_第5张图片

3.2 页写模块

09_SPI-Flash 页写实验_第6张图片

4. 波形图

09_SPI-Flash 页写实验_第7张图片
09_SPI-Flash 页写实验_第8张图片

5. RTL

5.1 flash_pp_ctrl

`timescale  1ns/1ns




module  flash_pp_ctrl(

    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号

    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi            //主输出从输入数据

);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010 ,   //写状态
            DELAY   =   4'b0100 ,   //等待状态
            PP      =   4'b1000 ;   //页写状态
parameter   WR_EN_INST      =   8'b0000_0110,   //写使能指令
            PP_INST         =   8'b0000_0010;   //页写指令
parameter   SECTOR_ADDR     =   8'b0000_0000,   //扇区地址
            PAGE_ADDR       =   8'b0000_0100,   //页地址
            BYTE_ADDR       =   8'b0010_0101;   //字节地址
parameter   NUM_DATA        =   8'd100      ;   //页写数据个数(0-99)

//reg   define
reg     [7:0]   cnt_byte        ;   //字节计数器
reg     [3:0]   state           ;   //状态机状态
reg     [4:0]   cnt_clk         ;   //系统时钟计数器
reg     [1:0]   cnt_sck         ;   //串行时钟计数器
reg     [2:0]   cnt_bit         ;   //比特计数器
reg     [7:0]   data            ;   //页写入数据

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk  <=  5'd0;
    else    if(state != IDLE)
        cnt_clk  <=  cnt_clk + 1'b1;

//cnt_byte:记录输出字节个数和等待时间
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte    <=  8'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 8'd9))
        cnt_byte    <=  8'd0;
    else    if(cnt_clk == 5'd31)
        cnt_byte    <=  cnt_byte + 1'b1;

//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if((state == WR_EN) && (cnt_byte == 8'd1))
        cnt_sck <=  cnt_sck + 1'b1;
    else    if((state == PP) && (cnt_byte >= 8'd5)
                && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1))
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 8'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 8'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31) && (state == PP))
        cs_n    <=  1'b1;

//sck:输出串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <=  cnt_bit + 1'b1;

//data:页写入数据
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data <=  8'd0;
    else    if((cnt_clk == 5'd31) && ((cnt_byte >= 8'd9)
                && (cnt_byte < NUM_DATA + 8'd9 - 1'b1)))
        data <=  data + 1'b1;

//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(key == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 8'd2) && (cnt_clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 8'd3) && (cnt_clk == 5'd31))
                state   <=  PP;
        PP:     if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31))
                state   <=  IDLE;
        default:    state   <=  IDLE;
    endcase

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte== 8'd2))
        mosi    <=  1'b0;
    else    if((state == PP) && (cnt_byte == NUM_DATA + 8'd9))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 8'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];  //写使能指令
    else    if((state == PP) && (cnt_byte == 8'd5) && (cnt_sck == 5'd0))
        mosi    <=  PP_INST[7 - cnt_bit];    //页写指令
    else    if((state == PP) && (cnt_byte == 8'd6) && (cnt_sck == 5'd0))
        mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
    else    if((state == PP) && (cnt_byte == 8'd7) && (cnt_sck == 5'd0))
        mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
    else    if((state == PP) && (cnt_byte == 8'd8) && (cnt_sck == 5'd0))
        mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址
    else    if((state == PP) && ((cnt_byte >= 8'd9)
                && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1)) && (cnt_sck == 5'd0))
        mosi    <=  data[7 - cnt_bit];  //页写入数据

endmodule

5.2 spi_flash_pp

`timescale  1ns/1ns




module  spi_flash_pp
(
    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    pi_key      ,   //按键输入信号

    output  wire    cs_n        ,   //片选信号
    output  wire    sck         ,   //串行时钟
    output  wire    mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值

//wire  define
wire    po_key  ;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- key_filter_inst -------------
key_filter
#(
    .CNT_MAX    (CNT_MAX    )   //计数器计数最大值
)
key_filter_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key_in     (pi_key     ),  //按键输入信号

    .key_flag   (po_key     )   //消抖后信号
);

//------------- flash_pp_ctrl_inst -------------
flash_pp_ctrl  flash_pp_ctrl_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key        (po_key     ),  //按键输入信号
                                
    .sck        (sck        ),  //片选信号
    .cs_n       (cs_n       ),  //串行时钟
    .mosi       (mosi       )   //主输出从输入数据
);

endmodule

6. Testbench

6.1 tb_flash_pp_ctrl

`timescale  1ns/1ns




module  tb_flash_pp_ctrl();

//wire  define
wire            cs_n;
wire            sck ;
wire            mosi;

//reg   define
reg     sys_clk     ;
reg     sys_rst_n   ;
reg     key         ;

//时钟、复位信号、模拟按键信号
initial
    begin
        sys_clk     =   0;
        sys_rst_n   <=  0;
        key <=  0;
        #100
        sys_rst_n   <=  1;
        #1000
        key <=  1;
        #20
        key <=  0;
    end

always  #10 sys_clk <=  ~sys_clk;

//写入Flash仿真模型初始值(全F)
defparam memory.mem_access.initfile = "initmemory.txt";

//------------- flash_pp_ctrl_inst -------------
flash_pp_ctrl  flash_pp_ctrl_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key        (key        ),  //按键输入信号

    .sck        (sck        ),  //串行时钟
    .cs_n       (cs_n       ),  //片选信号
    .mosi       (mosi       )   //主输出从输入数据
);

//------------- memory -------------
m25p16  memory
(
    .c          (sck    ),  //输入串行时钟,频率12.5Mhz,1bit
    .data_in    (mosi   ),  //输入串行指令或数据,1bit
    .s          (cs_n   ),  //输入片选信号,1bit
    .w          (1'b1   ),  //输入写保护信号,低有效,1bit
    .hold       (1'b1   ),  //输入hold信号,低有效,1bit

    .data_out   (       )   //输出串行数据
);

endmodule

6.2 tb_spi_flash_pp

`timescale  1ns/1ns




module  tb_spi_flash_pp();

//wire  define
wire            cs_n;
wire            sck ;
wire            mosi;
wire    [7:0]   cmd_data;

//reg   define
reg     clk     ;
reg     rst_n   ;
reg     key     ;

//时钟、复位信号、模拟按键信号
initial
    begin
        clk =   0;
        rst_n   <=  0;
        key <=  0;
        #100
        rst_n   <=  1;
        #1000
        key <=  1;
        #20
        key <=  0;
    end

initial
    begin
        $timeformat(-9, 0, "ns", 16);
        $monitor("@time %t: key=%b ", $time, key );
        $monitor("@time %t: cs_n=%b", $time, cs_n);
        $monitor("@time %t: cmd_data=%d", $time, cmd_data);
    end

always  #10 clk <=  ~clk;

defparam memory.mem_access.initfile = "initmemory.txt";

//-------------spi_flash_pp-------------
spi_flash_pp    spi_flash_pp_inst
(
    .sys_clk    (clk        ),  //input     sys_clk
    .sys_rst_n  (rst_n      ),  //input     sys_rst
    .pi_key     (key        ),  //input     key

    .sck        (sck        ),  //output    sck
    .cs_n       (cs_n       ),  //output    cs_n
    .mosi       (mosi       )   //output    mosi
);

m25p16  memory
(
    .c          (sck    ), 
    .data_in    (mosi   ), 
    .s          (cs_n   ), 
    .w          (1'b1   ), 
    .hold       (1'b1   ), 
    .data_out   (       )
); 

endmodule

你可能感兴趣的:(#,Verilog学习强化案例,fpga开发)