• 野火FPGA进阶(1):基于SPI协议的Flash驱动控制


    第48讲:基于SPI协议的Flash驱动控制

    0. 理论部分

    SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输

    应用:EEPROM、Flash、RTC、ADC、DSP等

    优缺点:全双工通信,通讯方式较为简单,相对数据传输速率较快;没有应答机制确认数据是否接收,在数据可靠性上有一定缺陷(与I2C相比)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述


    1. Flash全擦除实验

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    key_filter
    `timescale  1ns/1ns
    
    module  key_filter
    #(
        parameter CNT_MAX = 20'd999_999 //计数器计数最大值
    )
    (
        input   wire    sys_clk     ,   //系统时钟50Mhz
        input   wire    sys_rst_n   ,   //全局复位
        input   wire    key_in      ,   //按键输入信号
    
        output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                        //key_flag为0时表示没有检测到按键被按下
    );
    
    //reg   define
    reg     [19:0]  cnt_20ms    ;   //计数器
    
    //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_20ms <= 20'b0;
        else    if(key_in == 1'b1)
            cnt_20ms <= 20'b0;
        else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
            cnt_20ms <= cnt_20ms;
        else
            cnt_20ms <= cnt_20ms + 1'b1;
    
    //key_flag:当计数满20ms后产生按键有效标志位
    //且key_flag在999_999时拉高,维持一个时钟的高电平
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            key_flag <= 1'b0;
        else    if(cnt_20ms == CNT_MAX - 1'b1)
            key_flag <= 1'b1;
        else
            key_flag <= 1'b0;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    flash_be_ctrl
    `timescale  1ns/1ns
    
    module  flash_be_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 define
    parameter   IDLE    =   4'b0001 ,   //初始状态
                WR_EN   =   4'b0010 ,   //写状态
                DELAY   =   4'b0100 ,   //等待状态
                BE      =   4'b1000 ;   //全擦除状态
    parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令
                BE_INST     =   8'b1100_0111;   //全擦除指令
    
    //reg   define
    reg     [2:0]   cnt_byte;   //字节计数器
    reg     [3:0]   state   ;   //状态机状态
    reg     [4:0]   cnt_clk ;   //系统时钟计数器
    reg     [1:0]   cnt_sck ;   //串行时钟计数器
    reg     [2:0]   cnt_bit ;   //比特计数器
    
    //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    <=  3'd0;
        else    if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))
            cnt_byte    <=  3'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 == BE) && (cnt_byte == 3'd5))
            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 == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
            cs_n    <=  1'b1;
        else    if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))
            cs_n    <=  1'b0;
        else    if((cnt_byte == 3'd6) && (cnt_clk == 5'd31) && (state == BE))
            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(key == 1'b1)
                    state   <=  WR_EN;
            WR_EN:  if((cnt_byte == 3'd2) && (cnt_clk == 5'd31))
                    state   <=  DELAY;
            DELAY:  if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))
                    state   <=  BE;
            BE:     if((cnt_byte == 3'd6) && (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 == 3'd2))
            mosi    <=  1'b0;
        else    if((state == BE) && (cnt_byte == 3'd6))
            mosi    <=  1'b0;
        else    if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))
            mosi    <=  WR_EN_INST[7 - cnt_bit];    //写使能指令
        else    if((state == BE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))
            mosi    <=  BE_INST[7 - cnt_bit];       //全擦除指令
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    spi_flash_be
    `timescale  1ns/1ns
    
    module  spi_flash_be
    (
        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 define
    parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值
    
    //wire  define
    wire    po_key  ;
    
    //------------- 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_be_ctrl_inst -------------
    flash_be_ctrl  flash_be_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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    tb_flash_be_ctrl
    `timescale  1ns/1ns
    module  tb_flash_be_ctrl();
    
    //wire  define
    wire            cs_n    ;   //Flash片选信号
    wire            sck     ;   //Flash串行时钟
    wire            mosi    ;   //Flash主输出从输入信号
    
    //reg   define
    reg     sys_clk     ;   //模拟时钟信号
    reg     sys_rst_n   ;   //模拟复位信号
    reg     key         ;   //模拟全擦除触发信号
    
    //时钟、复位信号、模拟按键信号
    initial
        begin
            sys_clk     =   1'b1;
            sys_rst_n   <=  1'b0;
            key <=  1'b0;
            #100
            sys_rst_n   <=  1'b1;
            #1000
            key <=  1'b1;
            #20
            key <=  1'b0;
        end
    
    always  #10 sys_clk <=  ~sys_clk;   //模拟时钟,频率50MHz
    
    //写入Flash仿真模型初始值(全F)
    defparam memory.mem_access.initfile = "initmemory.txt";
    
    //------------- flash_be_ctrl_inst -------------
    flash_be_ctrl  flash_be_ctrl_inst
    (
        .sys_clk    (sys_clk    ),  //输入系统时钟,频率50MHz,1bit
        .sys_rst_n  (sys_rst_n  ),  //输入复位信号,低电平有效,1bit
        .key        (key        ),  //按键输入信号,1bit
    
        .sck        (sck        ),  //输出串行时钟,1bit
        .cs_n       (cs_n       ),  //输出片选信号,1bit
        .mosi       (mosi       )   //输出主输出从输入数据,1bit
    );
    
    //------------- 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    tb_spi_flash_be
    `timescale  1ns/1ns
    module  tb_spi_flash_be();
    
    //wire  define
    wire    cs_n;
    wire    sck ;
    wire    mosi ;
    
    //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
    
    always  #10 clk <=  ~clk;
    
    defparam memory.mem_access.initfile = "initmemory.txt";
    
    //-------------spi_flash_erase-------------
    spi_flash_be    spi_flash_be_inst
    (
        .sys_clk    (clk    ),  //系统时钟,频率50MHz
        .sys_rst_n  (rst_n  ),  //复位信号,低电平有效
        .pi_key     (key    ),  //按键输入信号
    
        .sck        (sck    ),  //串行时钟
        .cs_n       (cs_n   ),  //片选信号
        .mosi       (mosi   )   //主输出从输入数据
    );
    
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    2. Flash扇区擦除实验

    在这里插入图片描述
    在这里插入图片描述

    key_filter
    `timescale  1ns/1ns
    
    module  key_filter
    #(
        parameter CNT_MAX = 20'd999_999 //计数器计数最大值
    )
    (
        input   wire    sys_clk     ,   //系统时钟50Mhz
        input   wire    sys_rst_n   ,   //全局复位
        input   wire    key_in      ,   //按键输入信号
    
        output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                        //key_flag为0时表示没有检测到按键被按下
    );
    
    //reg   define
    reg     [19:0]  cnt_20ms    ;   //计数器
    
    //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_20ms <= 20'b0;
        else    if(key_in == 1'b1)
            cnt_20ms <= 20'b0;
        else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
            cnt_20ms <= cnt_20ms;
        else
            cnt_20ms <= cnt_20ms + 1'b1;
    
    //key_flag:当计数满20ms后产生按键有效标志位
    //且key_flag在999_999时拉高,维持一个时钟的高电平
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            key_flag <= 1'b0;
        else    if(cnt_20ms == CNT_MAX - 1'b1)
            key_flag <= 1'b1;
        else
            key_flag <= 1'b0;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    flash_se_ctrl
    `timescale  1ns/1ns
    
    module  flash_se_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 define
    parameter   IDLE    =   4'b0001 ,   //初始状态
                WR_EN   =   4'b0010 ,   //写状态
                DELAY   =   4'b0100 ,   //等待状态
                SE      =   4'b1000 ;   //扇区擦除状态
    parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令
                SE_INST     =   8'b1101_1000;   //扇区擦除指令
    parameter   SECTOR_ADDR =   8'b0000_0000,   //扇区地址
                PAGE_ADDR   =   8'b0000_0100,   //页地址
                BYTE_ADDR   =   8'b0010_0101;   //字节地址
    
    //reg   define
    reg     [3:0]   cnt_byte;   //字节计数器
    reg     [3:0]   state   ;   //状态机状态
    reg     [4:0]   cnt_clk ;   //系统时钟计数器
    reg     [1:0]   cnt_sck ;   //串行时钟计数器
    reg     [2:0]   cnt_bit ;   //比特计数器
    
    //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'd9))
            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 == SE) && (cnt_byte >= 4'd5) && (cnt_byte <= 4'd8))
            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 == 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'd9) && (cnt_clk == 5'd31) && (state == SE))
            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(key == 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   <=  SE;
            SE:     if((cnt_byte == 4'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 == 4'd2))
            mosi    <=  1'b0;
        else    if((state == SE) && (cnt_byte == 4'd9))
            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 == SE) && (cnt_byte == 4'd5) && (cnt_sck == 5'd0))
            mosi    <=  SE_INST[7 - cnt_bit];    //扇区擦除指令
        else    if((state == SE) && (cnt_byte == 4'd6) && (cnt_sck == 5'd0))
            mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
        else    if((state == SE) && (cnt_byte == 4'd7) && (cnt_sck == 5'd0))
            mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
        else    if((state == SE) && (cnt_byte == 4'd8) && (cnt_sck == 5'd0))
            mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    spi_flash_se
    `timescale  1ns/1ns
    
    module  spi_flash_se
    (
        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 define
    parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值
    
    //wire  define
    wire    po_key  ;
    
    //------------- 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_se_ctrl_inst -------------
    flash_se_ctrl  flash_se_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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46



    3. 数据读操作

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    key_filter
    `timescale  1ns/1ns
    
    module  key_filter
    #(
        parameter CNT_MAX = 20'd999_999 //计数器计数最大值
    )
    (
        input   wire    sys_clk     ,   //系统时钟50Mhz
        input   wire    sys_rst_n   ,   //全局复位
        input   wire    key_in      ,   //按键输入信号
    
        output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                        //key_flag为0时表示没有检测到按键被按下
    );
    
    //reg   define
    reg     [19:0]  cnt_20ms    ;   //计数器
    
    //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_20ms <= 20'b0;
        else    if(key_in == 1'b1)
            cnt_20ms <= 20'b0;
        else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
            cnt_20ms <= cnt_20ms;
        else
            cnt_20ms <= cnt_20ms + 1'b1;
    
    //key_flag:当计数满20ms后产生按键有效标志位
    //且key_flag在999_999时拉高,维持一个时钟的高电平
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            key_flag <= 1'b0;
        else    if(cnt_20ms == CNT_MAX - 1'b1)
            key_flag <= 1'b1;
        else
            key_flag <= 1'b0;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    uart_tx
    `timescale  1ns/1ns
    
    module  uart_tx
    #(
        parameter   UART_BPS    =   'd9600,         //串口波特率
        parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
    )
    (
         input   wire            sys_clk     ,   //系统时钟50MHz
         input   wire            sys_rst_n   ,   //全局复位
         input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据
         input   wire            pi_flag     ,   //并行数据有效标志信号
     
         output  reg             tx              //串转并后的1bit数据
    );
    
    //localparam    define
    localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;
    
    //reg   define
    reg [12:0]  baud_cnt;
    reg         bit_flag;
    reg [3:0]   bit_cnt ;
    reg         work_en ;
    
    //work_en:接收数据工作使能信号
    always@(posedge sys_clk or negedge sys_rst_n)
            if(sys_rst_n == 1'b0)
                work_en <= 1'b0;
            else    if(pi_flag == 1'b1)
                work_en <= 1'b1;
            else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
                work_en <= 1'b0;
    
    //baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
    always@(posedge sys_clk or negedge sys_rst_n)
            if(sys_rst_n == 1'b0)
                baud_cnt <= 13'b0;
            else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
                baud_cnt <= 13'b0;
            else    if(work_en == 1'b1)
                baud_cnt <= baud_cnt + 1'b1;
    
    //bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
    always@(posedge sys_clk or negedge sys_rst_n)
            if(sys_rst_n == 1'b0)
                bit_flag <= 1'b0;
            else    if(baud_cnt == 13'd1)
                bit_flag <= 1'b1;
            else
                bit_flag <= 1'b0;
    
    //bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            bit_cnt <= 4'b0;
        else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
            bit_cnt <= 4'b0;
        else    if((bit_flag == 1'b1) && (work_en == 1'b1))
            bit_cnt <= bit_cnt + 1'b1;
    
    //tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
    always@(posedge sys_clk or negedge sys_rst_n)
            if(sys_rst_n == 1'b0)
                tx <= 1'b1; //空闲状态时为高电平
            else    if(bit_flag == 1'b1)
                case(bit_cnt)
                    0       : tx <= 1'b0;
                    1       : tx <= pi_data[0];
                    2       : tx <= pi_data[1];
                    3       : tx <= pi_data[2];
                    4       : tx <= pi_data[3];
                    5       : tx <= pi_data[4];
                    6       : tx <= pi_data[5];
                    7       : tx <= pi_data[6];
                    8       : tx <= pi_data[7];
                    9       : tx <= 1'b1;
                    default : tx <= 1'b1;
                endcase
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    flash_read_ctrl
    `timescale  1ns/1ns
    
    module  flash_read_ctrl(
        input   wire            sys_clk     ,   //系统时钟,频率50MHz
        input   wire            sys_rst_n   ,   //复位信号,低电平有效
        input   wire            key         ,   //按键输入信号
        input   wire            miso        ,   //读出flash数据
    
        output  reg             sck         ,   //串行时钟
        output  reg             cs_n        ,   //片选信号
        output  reg             mosi        ,   //主输出从输入数据
        output  reg             tx_flag     ,   //输出数据标志信号
        output  wire    [7:0]   tx_data         //输出数据
    );
    
    //parameter define
    parameter   IDLE    =   3'b001  ,   //初始状态
                READ    =   3'b010  ,   //数据读状态
                SEND    =   3'b100  ;   //数据发送状态
    
    parameter   READ_INST   =   8'b0000_0011;   //读指令
    parameter   NUM_DATA    =   16'd100     ;   //读出数据个数
    parameter   SECTOR_ADDR =   8'b0000_0000,   //扇区地址
                PAGE_ADDR   =   8'b0000_0100,   //页地址
                BYTE_ADDR   =   8'b0010_0101;   //字节地址
    parameter   CNT_WAIT_MAX=   16'd6_00_00 ;
    
    //wire  define
    wire    [7:0]   fifo_data_num   ;   //fifo内数据个数
    //reg   define
    reg     [4:0]   cnt_clk         ;   //系统时钟计数器
    reg     [2:0]   state           ;   //状态机状态
    reg     [15:0]  cnt_byte        ;   //字节计数器
    reg     [1:0]   cnt_sck         ;   //串行时钟计数器
    reg     [2:0]   cnt_bit         ;   //比特计数器
    reg             miso_flag       ;   //miso提取标志信号
    reg     [7:0]   data            ;   //拼接数据
    reg             po_flag_reg     ;   //输出数据标志信号
    reg             po_flag         ;   //输出数据
    reg     [7:0]   po_data         ;   //输出数据
    reg             fifo_read_valid ;   //fifo读有效信号
    reg     [15:0]  cnt_wait        ;   //等待计数器
    reg             fifo_read_en    ;   //fifo读使能
    reg     [7:0]   read_data_num   ;   //读出fifo数据个数
    
    //cnt_clk:系统时钟计数器,用以记录单个字节
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_clk  <=  5'd0;
        else    if(state == READ)
            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    <=  16'd0;
        else    if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 16'd3))
            cnt_byte    <=  16'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 == READ)
            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 == NUM_DATA + 16'd3) && (cnt_clk == 5'd31) && (state == READ))
            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(key == 1'b1)
                        state   <=  READ;
            READ:   if((cnt_byte == NUM_DATA + 16'd3) && (cnt_clk == 5'd31))
                        state   <=  SEND;
            SEND:   if((read_data_num == NUM_DATA)
                    && ((cnt_wait == (CNT_WAIT_MAX - 1'b1))))
                        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 == READ) && (cnt_byte>= 16'd4))
            mosi    <=  1'b0;
        else    if((state == READ) && (cnt_byte == 16'd0) && (cnt_sck == 2'd0))
            mosi    <=  READ_INST[7 - cnt_bit];  //读指令
        else    if((state == READ) && (cnt_byte == 16'd1) && (cnt_sck == 2'd0))
            mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
        else    if((state == READ) && (cnt_byte == 16'd2) && (cnt_sck == 2'd0))
            mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
        else    if((state == READ) && (cnt_byte == 16'd3) && (cnt_sck == 2'd0))
            mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址
    
    //miso_flag:miso提取标志信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            miso_flag   <=  1'b0;
        else    if((cnt_byte >= 16'd4) && (cnt_sck == 2'd1))
            miso_flag   <=  1'b1;
        else
            miso_flag   <=  1'b0;
    
    //data:拼接数据
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            data    <=  8'd0;
        else    if(miso_flag == 1'b1)
            data    <=  {data[6:0],miso};
    
    //po_flag_reg:输出数据标志信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            po_flag_reg <=  1'b0;
        else    if((cnt_bit == 3'd7) && (miso_flag == 1'b1))
            po_flag_reg <=  1'b1;
        else
            po_flag_reg <=  1'b0;
    
    //po_flag:输出数据标志信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            po_flag <=  1'b0;
        else
            po_flag <=  po_flag_reg;
    
    //po_data:输出数据
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            po_data <=  8'd0;
        else    if(po_flag_reg == 1'b1)
            po_data <=  data;
        else
            po_data <=  po_data;
    
    //fifo_read_valid:fifo读有效信号
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            fifo_read_valid <=  1'b0;
        else    if((read_data_num == NUM_DATA)
                    && ((cnt_wait == (CNT_WAIT_MAX - 1'b1))))
            fifo_read_valid <=  1'b0;
        else    if(fifo_data_num == NUM_DATA)
            fifo_read_valid <=  1'b1;
    
    //cnt_wait:两数据读取时间间隔
    always@(posedge sys_clk or  negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_wait    <=  16'd0;
        else    if(fifo_read_valid == 1'b0)
            cnt_wait    <=  16'd0;
        else    if(cnt_wait == (CNT_WAIT_MAX - 1'b1))
            cnt_wait    <=  16'd0;
        else    if(fifo_read_valid == 1'b1)
            cnt_wait    <=  cnt_wait + 1'b1;
    
    //fifo_read_en:fifo读使能信号
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            fifo_read_en <=  1'b0;
        else    if((cnt_wait == (CNT_WAIT_MAX - 1'b1))
                    && (read_data_num < NUM_DATA))
            fifo_read_en <=  1'b1;
        else
            fifo_read_en <=  1'b0;
    
    //read_data_num:自fifo中读出数据个数计数
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            read_data_num <=  8'd0;
        else    if(fifo_read_valid == 1'b0)
            read_data_num <=  8'd0;
        else    if(fifo_read_en == 1'b1)
            read_data_num <=  read_data_num + 1'b1;
    
    //tx_flag
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            tx_flag <=  1'b0;
        else
            tx_flag <=  fifo_read_en;
    
    //-------------fifo_data_inst--------------
    fifo_data fifo_data_inst(
        .clock  (sys_clk      ),    //时钟信号
        .data   (po_data      ),    //写数据,8bit
        .wrreq  (po_flag      ),    //写请求
        .rdreq  (fifo_read_en ),    //读请求
    
        .q      (tx_data      ),    //数据读出,8bit
        .usedw  (fifo_data_num)     //fifo内数据个数
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    spi_flash_read
    `timescale  1ns/1ns
    
    module  spi_flash_read(
        input   wire    sys_clk     ,   //系统时钟,频率50MHz
        input   wire    sys_rst_n   ,   //复位信号,低电平有效
        input   wire    pi_key      ,   //按键输入信号
        input   wire    miso        ,   //读出flash数据
    
        output  wire    cs_n        ,   //片选信号
        output  wire    sck         ,   //串行时钟
        output  wire    mosi        ,   //主输出从输入数据
        output  wire    tx              
    );
    
    //parameter define
    parameter   CNT_MAX     =   20'd999_999     ;   //计数器计数最大值
    parameter   UART_BPS    =   14'd9600        ,   //比特率
                CLK_FREQ    =   26'd50_000_000  ;   //时钟频率
    
    
    //wire  define
    wire            po_key  ;   //消抖处理后的按键信号
    wire            tx_flag ;   //输入串口发送模块数据标志信号
    wire    [7:0]   tx_data ;   //输入串口发送模块数据
    
    //------------- 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_read_ctrl_inst-------------
    flash_read_ctrl  flash_read_ctrl_inst(
    
        .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
        .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
        .key        (po_key     ),  //按键输入信号
        .miso       (miso       ),  //读出flash数据
    
        .sck        (sck        ),  //片选信号
        .cs_n       (cs_n       ),  //串行时钟
        .mosi       (mosi       ),  //主输出从输入数据
        .tx_flag    (tx_flag    ),  //输出数据标志信号
        .tx_data    (tx_data    )   //输出数据
    );
    
    //-------------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     (tx_data  ),   //并行数据
        .pi_flag     (tx_flag  ),   //并行数据有效标志信号
                                    
        .tx          (tx       )    //串口发送数据
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    tb_spi_flash_read
    `timescale  1ns/1ns
    module  tb_spi_flash_read();
    
    //wire  define
    wire    cs_n;
    wire    sck ;
    wire    mosi;
    wire    miso;
    wire    tx  ;
    
    //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
    
    always  #10 clk <=  ~clk;
    
    defparam memory.mem_access.initfile = "initM25P16_test.txt";
    defparam spi_flash_read_inst.flash_read_ctrl_inst.CNT_WAIT_MAX = 1000;
    defparam spi_flash_read_inst.uart_tx_inst.CLK_FREQ = 100000;
    
    //------------- spi_flash_read -------------
    spi_flash_read    spi_flash_read_inst(
        .sys_clk    (clk    ),  //input     sys_clk
        .sys_rst_n  (rst_n  ),  //input     sys_rst
        .pi_key     (key    ),  //input     key
        .miso       (miso   ),
    
        .sck        (sck    ),  //output    sck
        .cs_n       (cs_n   ),  //output    cs_n
        .mosi       (mosi   ),  //output    mosi
        .tx         (tx     )
    );
    
    //------------- memory -------------
    m25p16  memory (
        .c          (sck    ), 
        .data_in    (mosi   ), 
        .s          (cs_n   ), 
        .w          (1'b1   ), 
        .hold       (1'b1   ), 
        .data_out   (miso   )
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59



    4. 数据页写操作

    两种写入方式:页写,连续写

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    key_filter
    `timescale  1ns/1ns
    
    module  key_filter
    #(
        parameter CNT_MAX = 20'd999_999 //计数器计数最大值
    )
    (
        input   wire    sys_clk     ,   //系统时钟50Mhz
        input   wire    sys_rst_n   ,   //全局复位
        input   wire    key_in      ,   //按键输入信号
    
        output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                        //key_flag为0时表示没有检测到按键被按下
    );
    
    //reg   define
    reg     [19:0]  cnt_20ms    ;   //计数器
    
    //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_20ms <= 20'b0;
        else    if(key_in == 1'b1)
            cnt_20ms <= 20'b0;
        else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
            cnt_20ms <= cnt_20ms;
        else
            cnt_20ms <= cnt_20ms + 1'b1;
    
    //key_flag:当计数满20ms后产生按键有效标志位
    //且key_flag在999_999时拉高,维持一个时钟的高电平
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            key_flag <= 1'b0;
        else    if(cnt_20ms == CNT_MAX - 1'b1)
            key_flag <= 1'b1;
        else
            key_flag <= 1'b0;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    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 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            ;   //页写入数据
    
    //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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    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 define
    parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值
    
    //wire  define
    wire    po_key  ;
    
    //------------- 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46



    5. 数据连续写操作

    在这里插入图片描述

  • 相关阅读:
    景联文科技:驾驭数据浪潮,赋能AI产业——全球领先的数据标注解决方案供应商
    初见物理引擎库Cannon.js
    linux安装vsftp
    【附源码】计算机毕业设计JAVA助农脱贫系统
    爆冷?黑马?这次用python来给你推测一波.....
    python:OderedDict函数
    HTML中link标签的那些属性
    maven多仓库私库模板配置
    4K壁纸小程序源码 全内容自动采集
    【学术综述】-如何写出一篇好综述-写好综述要注意的问题
  • 原文地址:https://blog.csdn.net/qq_39236499/article/details/128085765