10_SPI_Flash 连续写实验

10_SPI_Flash 连续写实验

  • 1. 实验目标
  • 2. 连续写方法
  • 3. 操作时序
  • 4. 流程框图
    • 4.1 顶层模块
    • 4.2 连续写模块
  • 5. 波形图
  • 6. RTL
    • 6.1 flash_seq_wr_ctrl
    • 6.2 spi_flash_seq_wr
  • 7. Testbench

1. 实验目标

使用页写指令,将串口发送过来的连续不定量数据写入 Flash。本实验中,我们发送数据为 100 字节,串口波特率位 9600。
注意:在向 Flash 芯片写入数据之前,先要对芯片执行全擦除操作。

2. 连续写方法

10_SPI_Flash 连续写实验_第1张图片
10_SPI_Flash 连续写实验_第2张图片

3. 操作时序

和页写操作的操作时序一样。

4. 流程框图

4.1 顶层模块

10_SPI_Flash 连续写实验_第3张图片

4.2 连续写模块

10_SPI_Flash 连续写实验_第4张图片

10_SPI_Flash 连续写实验_第5张图片

5. 波形图

10_SPI_Flash 连续写实验_第6张图片

6. RTL

6.1 flash_seq_wr_ctrl

`timescale  1ns/1ns




module  flash_seq_wr_ctrl(

    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            pi_flag     ,   //数据标志信号
    input   wire    [7:0]   pi_data     ,   //写入数据

    output  reg             sck         ,   //串行时钟
    output  reg             cs_n        ,   //片选信号
    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   ADDR        =   24'h00_04_25;   //数据写入地址

//reg   define
reg     [23:0]  addr_reg;   //数据写入地址寄存器
reg     [23:0]  addr    ;   //数据写入地址

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

//********************************************************************//
//***************************** 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    <=  4'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == 4'd10))
        cnt_byte    <=  4'd0;
    else    if(cnt_clk == 31)
        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 == 1'b1))
        cnt_sck <=  cnt_sck + 1'b1;
    else    if((state == PP) && (cnt_byte >= 4'd5) && (cnt_byte <= 4'd9))
        cnt_sck <=  cnt_sck + 1'b1;

//addr_reg:数据写入地址寄存器
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        addr_reg    <=  ADDR;
    else    if(pi_flag == 1'b1)
        addr_reg    <=  addr_reg + 1'b1 ;

//addr:数据写入地址
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        addr    <=  24'd0;
    else    if(pi_flag == 1'b1)
        addr    <=  addr_reg;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(pi_flag == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 4'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 4'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == 4'd10) && (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;

//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(pi_flag == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 4'd2) && (cnt_clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 4'd3) && (cnt_clk == 5'd31))
                state   <=  PP;
        PP:     if((cnt_byte == 4'd10) && (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 == 4'd2))
        mosi    <=  1'b0;
    else    if((state == PP) && (cnt_byte == 4'd10))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 4'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];    //写使能指令
    else    if((state == PP) && (cnt_byte == 4'd5) && (cnt_sck == 5'd0))
        mosi    <=  PP_INST[7 - cnt_bit];       //扇区擦除指令
    else    if((state == PP) && (cnt_byte == 4'd6) && (cnt_sck == 5'd0))
        mosi    <=  addr[23 - cnt_bit];         //扇区地址
    else    if((state == PP) && (cnt_byte == 4'd7) && (cnt_sck == 5'd0))
        mosi    <=  addr[15 - cnt_bit];         //页地址
    else    if((state == PP) && (cnt_byte == 4'd8) && (cnt_sck == 5'd0))
        mosi    <=  addr[7 - cnt_bit];          //字节地址
    else    if((state == PP) && (cnt_byte == 4'd9) && (cnt_sck == 5'd0))
        mosi    <=  pi_data[7 - cnt_bit];       //写入数据

endmodule

6.2 spi_flash_seq_wr

`timescale  1ns/1ns




module  spi_flash_seq_wr(

    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    rx          ,   //串口接收数据

    output  wire    cs_n        ,   //片选信号
    output  wire    sck         ,   //串行时钟
    output  wire    mosi        ,   //主输出从输入数据
    output  wire    tx              //串口发送数据

);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率

//wire  define
wire            po_flag ;
wire    [7:0]   po_data ;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//-------------uart_rx_inst-------------
uart_rx
#(
    .UART_BPS    (UART_BPS ),         //串口波特率
    .CLK_FREQ    (CLK_FREQ )          //时钟频率
)
uart_rx_inst(
    .sys_clk     (sys_clk  ),   //系统时钟50Mhz
    .sys_rst_n   (sys_rst_n),   //全局复位
    .rx          (rx       ),   //串口接收数据

    .po_data     (po_data  ),   //串转并后的数据
    .po_flag     (po_flag  )    //串转并后的数据有效标志信号
);

//-------------flash_seq_wr_ctrl_inst-------------
flash_seq_wr_ctrl  flash_seq_wr_ctrl_inst(

    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .pi_flag    (po_flag    ),  //数据标志信号
    .pi_data    (po_data    ),  //写入数据

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

);

//-------------uart_tx_inst-------------
uart_tx
#(
    .UART_BPS    (UART_BPS ),         //串口波特率
    .CLK_FREQ    (CLK_FREQ )          //时钟频率
)
uart_tx_inst
(
    .sys_clk     (sys_clk  ),   //系统时钟50Mhz
    .sys_rst_n   (sys_rst_n),   //全局复位
    .pi_data     (po_data  ),   //并行数据
    .pi_flag     (po_flag  ),   //并行数据有效标志信号

    .tx          (tx       )    //串口发送数据
);

endmodule

7. Testbench

`timescale  1ns/1ns




module  tb_spi_flash_seq_wr();

//wire define
wire    tx  ;
wire    cs_n;
wire    sck ;
wire    mosi;
wire    miso;

//reg define
reg           clk   ;
reg           rst_n ;
reg           rx    ;
reg   [7:0]   data_mem [299:0] ;  //data_mem是一个存储器,相当于一个ram

//读取sim文件夹下面的data.txt文件,并把读出的数据定义为data_mem
initial
  $readmemh("E:/base_code/10_spi_flash/spi_flash_write/spi_flash_seq_wr/sim/spi_flash.txt",data_mem);


//时钟、复位信号
initial
  begin
    clk     =   1'b1  ;
    rst_n   <=  1'b0  ;
    #200
    rst_n   <=  1'b1  ;
  end

always  #10 clk = ~clk;


initial
  begin
    rx  <=  1'b1;
    #200
    rx_byte();
  end

task  rx_byte();
  integer j;
  for(j=0;j<300;j=j+1)
    rx_bit(data_mem[j]);
endtask

task  rx_bit(input[7:0] data);  //data是data_mem[j]的值。
  integer i;
    for(i=0;i<10;i=i+1)
      begin
        case(i)
          0:  rx  <=  1'b0   ;  //起始位
          1:  rx  <=  data[0];
          2:  rx  <=  data[1];
          3:  rx  <=  data[2];
          4:  rx  <=  data[3];
          5:  rx  <=  data[4];
          6:  rx  <=  data[5];
          7:  rx  <=  data[6];
          8:  rx  <=  data[7];  //上面8个发送的是数据位
          9:  rx  <=  1'b1   ;  //停止位
        endcase
        #1040;                  //一个波特时间=sclk周期*波特计数器
      end
endtask

//重定义defparam,用于修改参数,缩短仿真时间
defparam spi_flash_seq_wr_inst.uart_rx_inst.CLK_FREQ    = 500000;
defparam spi_flash_seq_wr_inst.uart_tx_inst.CLK_FREQ    = 500000;
defparam memory.mem_access.initfile = "initmemory.txt";

//-------------spi_flash_seq_wr_inst-------------
spi_flash_seq_wr  spi_flash_seq_wr_inst(
    .sys_clk    (clk    ),    //input   sys_clk
    .sys_rst_n  (rst_n  ),    //input   sys_rst_n
    .rx         (rx     ),    //input   rx

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

);

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

endmodule

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