• FPGA基于spi的flash读写


    一、SPI
    SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。

    (1)MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;
    (2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;
    (3)SCLK – Serial Clock,时钟信号,由主设备产生;
    (4)CS – Chip Select,从设备使能信号,由主设备控制。

    在这里插入图片描述
    二、看spi–flash手册找关键
    1.描述

    在这里插入图片描述
    16Mbit的存储空间

    单扇区擦除或者整块擦除

    用spi协议与flash读写
    2.flash接口信号
    在这里插入图片描述
    在这里插入图片描述
    C是串行时钟

    D是数据

    S是片选信号

    3.SPI模式选择
    flash只支持mode0和mode3两种模式
    在这里插入图片描述
    在这里插入图片描述
    CPOL时钟相位

    时钟的极性(CPOL)用来决定在总线空闲时,同步时钟(SCK)信号线上的电位是高电平还是低电平。当时钟极性为0时(CPOL=0),SCK信号线在空闲时为低电平;当时钟极性为1时(CPOL=1),SCK信号线在空闲时为高电平;

    CPHA时钟极性

    当时钟相位为1时(CPHA=1),在SCK信号线的第二个跳变沿进行采样;这里的跳变沿究竟是上升沿还是下降沿?取决于时钟的极性。当时钟极性为0时,取下降沿;当时钟极性为1时,取上升沿

    CPOL=0,CPHA=0
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    4.高字节MSB
    在这里插入图片描述
    MSB先,就是高字节先
    5.指令
    在这里插入图片描述
    在这里插入图片描述
    6. 写使能时序
    在这里插入图片描述
    7.读ID时序
    在这里插入图片描述
    8.读寄存器时序(我没用到)
    在这里插入图片描述
    判断WIP BIT是否为0才能进行下一步(我的代码里没有用到)
    在这里插入图片描述
    9.读数据时序
    在这里插入图片描述
    10.页编程
    在这里插入图片描述
    11.扇区擦除
    在这里插入图片描述
    12.重要的时间
    在这里插入图片描述
    三、状态机设计
    1.spi接口状态机

    在这里插入图片描述
    2.flash读状态机
    在这里插入图片描述
    3.flash写状态机
    在这里插入图片描述
    四、代码部分
    1.spi_interface.v

    module spi_interface(
        input           clk,
        input           rst_n,
        // 接口与主机
        input   [7:0]   din,
        input           req,
        output  [7:0]   dout,
        output          done,
        // 接口与flash
        input           miso,// 主机采样从机发送
        output          mosi,// 主机发送从机
        output          sclk,// 串行时钟
        output          cs_n // 片选信号
    );
    
    parameter   CPHA = 1,// 空闲状态高电平
                CPOL = 1;// 下降沿发送,上升沿采样
    
    // 16分频或8分频或4分频 不能2分频
    parameter   SCLK = 16,
                SCLK_BEFORE = SCLK/4,
                SCLK_AFTER  = SCLK*3/4;
    
    // 状态机
    localparam  IDLE  = 4'b0001,
                WAIT  = 4'b0010,
                DATA  = 4'b0100,
                DONE  = 4'b1000;
    
    reg     [3:0]       state_c;
    reg     [3:0]       state_n;
    
    wire                idle2wait;
    wire                wait2data;
    wire                data2done;
    wire                done2idle;
    
    // bit计数器
    reg     [2:0]       cnt_bit;
    wire                add_cnt_bit;
    wire                end_cnt_bit;
    
    // 分频串行时钟计数器
    reg     [4:0]       cnt_sclk;
    wire                add_cnt_sclk;
    wire                end_cnt_sclk;
    
    // 寄存要发送的数据
    reg                 spi_sclk;
    reg     [7:0]       rx_data;
    reg     [7:0]       tx_data;
    reg                 spi_cn_n;
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE; 
        end 
        else begin 
            state_c <= state_n;
        end 
    end
        
    always @(*)begin 
        case (state_c)
            IDLE      :begin 
                        if(idle2wait)begin
                            state_n = WAIT;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            WAIT      :begin 
                        if(wait2data)begin
                            state_n = DATA;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            DATA      :begin 
                        if(data2done)begin
                            state_n = DONE;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            DONE      :begin 
                        if(done2idle)begin
                            state_n = IDLE;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            default: state_n = IDLE;
        endcase
    end
        
    assign idle2wait = state_c == IDLE && (req);
    assign wait2data = state_c == WAIT && (1'b1);
    assign data2done = state_c == DATA && (end_cnt_bit);
    assign done2idle = state_c == DONE && (1'b1);
    
    // bit计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 0;
        end 
        else if(add_cnt_bit)begin 
                if(end_cnt_bit)begin 
                    cnt_bit <= 0;
                end
                else begin 
                    cnt_bit <= cnt_bit + 1;
                end 
        end
       else  begin
           cnt_bit <= cnt_bit;
        end
    end 
    
    assign add_cnt_bit = end_cnt_sclk;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == 8 - 1;
    
    // sclk计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_sclk <= 0;
        end 
        else if(add_cnt_sclk)begin 
                if(end_cnt_sclk)begin 
                    cnt_sclk <= 0;
                end
                else begin 
                    cnt_sclk <= cnt_sclk + 1;
                end 
        end
       else  begin
           cnt_sclk <= cnt_sclk;
        end
    end 
    
    assign add_cnt_sclk = (state_c == DATA);
    assign end_cnt_sclk = add_cnt_sclk && cnt_sclk == SCLK - 1;
    
    // 16分频串行时钟 CPHA=1,CPOL=1
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            if(CPHA == 0)begin
                spi_sclk <= 1'b0;
            end
            else if(CPHA == 1)begin
                spi_sclk <= 1'b1;
            end
        end 
        else if(add_cnt_sclk && cnt_sclk == SCLK_BEFORE - 1)begin 
            if(CPHA == 0)begin
                spi_sclk <= 1'b1;
            end
            else if(CPHA == 1)begin
                spi_sclk <= 1'b0;
            end
        end 
        else if(add_cnt_sclk && cnt_sclk == SCLK_AFTER - 1)begin 
            if(CPHA == 0)begin
                spi_sclk <= 1'b0;
            end
            else if(CPHA == 1)begin
                spi_sclk <= 1'b1;
            end
        end 
    end
    
    // 发送的数据mosi 高位MSB先 CPHA=1,CPOL=1
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_data <= 0;
        end 
        else if(CPOL == 0)begin
            if(add_cnt_sclk && cnt_sclk == SCLK_AFTER - 1)begin
                tx_data <= din;
            end
        end
        else if(CPOL == 1)begin
            if(add_cnt_sclk && cnt_sclk == SCLK_BEFORE - 1)begin 
                tx_data <= din;
            end 
        end
    end
    
    // 接收的数据miso 高位MSB先 CPHA=1,CPOL=1
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_data <= 0;
        end 
        else if(CPOL == 0)begin
            if(add_cnt_sclk && cnt_sclk == SCLK_BEFORE - 1)begin 
                rx_data[7-cnt_bit] <= miso;
            end 
        end
        else if(CPOL == 1)begin
            if(add_cnt_sclk && cnt_sclk == SCLK_AFTER - 1)begin 
                rx_data[7-cnt_bit] <= miso;
            end 
        end
    end
    
    assign mosi = tx_data[7-cnt_bit];
    assign sclk = spi_sclk;
    assign cs_n = ~req;
    assign dout = rx_data;
    assign done = (state_c == DONE);
    
    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

    2.spi_read_ctrl.v

    module spi_read_ctrl(
        input               clk,
        input               rst_n,
        input       [2:0]   key_out,
        input       [7:0]   din,
        input               done,
        output reg          req,
        output      [7:0]   dout,
        output reg  [23:0]  seg_data
    );
    
    localparam  RDID_CMD = 8'h9F,// 读ID指令
                RDDA_CMD = 8'h03,// 读数据指令
                RDDA_ADD = 24'h0;// 读数据地址
    
    localparam  IDLE    = 7'b000_0001,
                RDIDCMD = 7'b000_0010,  
                RDID    = 7'b000_0100,   
                RDDACMD = 7'b000_1000,
                RDDAADD = 7'b001_0000, 
                RDDATA  = 7'b010_0000,  
                DONE    = 7'b100_0000;
    
    reg     [6:0]       state_c;
    reg     [6:0]       state_n;
    
    wire                idle2rdidcmd   ;
    wire                idle2rddacmd   ;
    wire                rdidcmd2rdid   ;
    wire                rdid2done      ;
    wire                rddacmd2rddaadd;
    wire                rddaadd2rddata ;
    wire                rddata2done    ;
    wire                done2idle      ;
    
    // 字节计数器
    reg     [2:0]       cnt_byte;
    wire                add_cnt_byte;
    wire                end_cnt_byte;
    
    // 读id和读数据请求
    reg                 rdid_req;
    reg                 rdda_req;
    
    reg     [7:0]       tx_data;
    // 状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE; 
        end 
        else begin 
            state_c <= state_n;
        end 
    end
        
    always @(*)begin 
        case (state_c)
            IDLE      :begin 
                        if(idle2rdidcmd)begin
                            state_n = RDIDCMD;
                        end
                        else if(idle2rddacmd)begin
                            state_n = RDDACMD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            RDIDCMD      :begin 
                        if(rdidcmd2rdid)begin
                            state_n = RDID;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            RDID      :begin 
                        if(rdid2done)begin
                            state_n = DONE;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            RDDACMD      :begin 
                        if(rddacmd2rddaadd)begin
                            state_n = RDDAADD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            RDDAADD      :begin 
                        if(rddaadd2rddata)begin
                            state_n = RDDATA;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            RDDATA      :begin 
                        if(rddata2done)begin
                            state_n = DONE;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            DONE      :begin 
                        if(done2idle)begin
                            state_n = IDLE;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            default: state_n = IDLE;
        endcase
    end
        
    assign idle2rdidcmd     = state_c == IDLE       && (rdid_req);
    assign idle2rddacmd     = state_c == IDLE       && (rdda_req);
    assign rdidcmd2rdid     = state_c == RDIDCMD    && (end_cnt_byte);
    assign rdid2done        = state_c == RDID       && (end_cnt_byte);
    assign rddacmd2rddaadd  = state_c == RDDACMD    && (end_cnt_byte);
    assign rddaadd2rddata   = state_c == RDDAADD    && (end_cnt_byte);
    assign rddata2done      = state_c == RDDATA     && (end_cnt_byte);
    assign done2idle        = state_c == DONE       && (1'b1);
    
    // 字节计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 0;
        end 
        else if(add_cnt_byte)begin 
                if(end_cnt_byte)begin 
                    cnt_byte <= 0;
                end
                else begin 
                    cnt_byte <= cnt_byte + 1;
                end 
        end
       else  begin
           cnt_byte <= cnt_byte;
        end
    end 
    
    assign add_cnt_byte = (state_c != IDLE) && done;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == (((state_c == RDIDCMD) || (state_c == RDDACMD) || (state_c == RDDATA))?(1-1):(3-1));
    
    
    // 读id和读数据请求
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rdid_req <= 0;
            rdda_req <= 0;
            req <= 0;
        end 
        else if(key_out[0])begin 
            rdid_req <= 1'b1;
            req <= 1'b1;
        end 
        else if(key_out[1])begin 
            rdda_req <= 1'b1;
            req <= 1'b1;
        end 
        else if(state_c == DONE)begin
            req <= 1'b0;
            rdid_req <= 1'b0;
            rdda_req <= 1'b0;
        end
    end
    
    // 指令
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_data <= 0;
        end 
        else if(idle2rdidcmd)begin 
            tx_data <= RDID_CMD;
        end 
        else if(idle2rddacmd)begin 
            tx_data <= RDDA_CMD;
        end 
        else if(rddacmd2rddaadd)begin
            tx_data <= RDDA_ADD;
        end
    end
    
    // seg_data
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            seg_data <= 0;
        end 
        else if(state_c == RDID && add_cnt_byte)begin
            case(cnt_byte)
            0   :   seg_data[23:16] <= din;
            1   :   seg_data[15:8] <= din;
            2   :   seg_data[7:0] <= din;
            default: seg_data <= seg_data;
            endcase
        end
        else if(state_c == RDDATA && add_cnt_byte)begin 
            case(cnt_byte)
            0   :   seg_data[23:16] <= din;
            default: seg_data <= seg_data;
            endcase
        end 
        else begin
            seg_data <= seg_data;
        end
    end
    
    // assign req = rdid_req || rdda_req;
    assign dout = tx_data;
    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

    3.spi_write_ctrl.v

    module spi_write_ctrl(
        input           clk,
        input           rst_n,
        input   [2:0]   key_out,
        input   [7:0]   din,
        input           done,
        output          req,
        output  [7:0]   dout
    );
    
    parameter   CMD_TIME = 10,// 第一个指令到下一个指令200ns等待时间
                PP_TIME  = 250_000,// PP可编程时间5ms
                SE_TIME  = 150_000_000;// SE擦除时间3s
    
    parameter   WREN_CMD = 8'h06,
                SE_CMD   = 8'hD8,
                SE_ADD   = 24'h000000,
                RDSR_CMD = 8'h05,
                PP_CMD   = 8'h02,
                PP_ADD   = 24'h000000,
                DATA     = 8'h78;
    
    // 状态机
    localparam  IDLE        =10'b00000_00001,
                FIRWRENCMD  =10'b00000_00010,
                SECMD       =10'b00000_00100,
                SEADD       =10'b00000_01000,
                RDSRCMD     =10'b00000_10000,
                SECWRENCMD  =10'b00001_00000,
                PPCMD       =10'b00010_00000,
                PPADD       =10'b00100_00000,
                PPDATA      =10'b01000_00000,
                DONE        =10'b10000_00000;
    
    reg     [9:0]       state_c;
    reg     [9:0]       state_n;
    
    wire                idle2firwrencmd     ;
    wire                firwrencmd2secmd    ;
    wire                secmd2seadd         ;
    wire                seadd2rdsrcmd       ;
    wire                rdsrcmd2secwrencmd  ;
    wire                secwrencmd2ppcmd    ;
    wire                ppcmd2ppadd         ;
    wire                ppadd2ppdata        ;
    wire                ppdata2done         ;
    wire                done2idle           ;
    
    // 字节计数器
    reg     [1:0]       cnt_byte;
    wire                add_cnt_byte;
    wire                end_cnt_byte;
    
    // 100ms一个命令到下一个命令的等待时间
    reg     [3:0]       cnt_200ns;
    wire                add_cnt_200ns;
    wire                end_cnt_200ns;
    
    // se擦除等待时间
    reg     [27:0]      cnt_3s;
    wire                add_cnt_3s;
    wire                end_cnt_3s; 
    
    // pp页编程等待时间
    // reg                 cnt_5ms;
    // wire                add_cnt_5ms;
    // wire                end_cnt_5ms;
    
    // 一个命令到下一个命令的等待标志
    reg                 delay_flag;
    
    // 寄存req
    reg                 req_r;
    
    // 寄存要发送的数据
    reg     [7:0]       tx_data;
    
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE; 
        end 
        else begin 
            state_c <= state_n;
        end 
    end
        
    always @(*)begin 
        case (state_c)
            IDLE      :begin 
                        if(idle2firwrencmd)begin
                            state_n = FIRWRENCMD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            FIRWRENCMD      :begin 
                        if(firwrencmd2secmd)begin
                            state_n = SECMD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            SECMD      :begin 
                        if(secmd2seadd)begin
                            state_n = SEADD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            SEADD      :begin 
                        if(seadd2rdsrcmd)begin
                            state_n = RDSRCMD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            RDSRCMD      :begin 
                        if(rdsrcmd2secwrencmd)begin
                            state_n = SECWRENCMD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            SECWRENCMD      :begin 
                        if(secwrencmd2ppcmd)begin
                            state_n = PPCMD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            PPCMD      :begin 
                        if(ppcmd2ppadd)begin
                            state_n = PPADD;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            PPADD      :begin 
                        if(ppadd2ppdata)begin
                            state_n = PPDATA;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            PPDATA      :begin 
                        if(ppdata2done)begin
                            state_n = DONE;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            DONE      :begin 
                        if(done2idle)begin
                            state_n = IDLE;
                        end
                        else begin
                            state_n = state_c;
                        end 
                       end
            default: state_n = IDLE;
        endcase
    end
        
    assign idle2firwrencmd    = state_c == IDLE         && (key_out[2]);
    assign firwrencmd2secmd   = state_c == FIRWRENCMD   && (end_cnt_200ns);
    assign secmd2seadd        = state_c == SECMD        && (end_cnt_byte);
    assign seadd2rdsrcmd      = state_c == SEADD        && (end_cnt_3s);
    assign rdsrcmd2secwrencmd = state_c == RDSRCMD      && (end_cnt_200ns);
    assign secwrencmd2ppcmd   = state_c == SECWRENCMD   && (end_cnt_200ns);
    assign ppcmd2ppadd        = state_c == PPCMD        && (end_cnt_byte);
    assign ppadd2ppdata       = state_c == PPADD        && (end_cnt_byte);
    assign ppdata2done        = state_c == PPDATA       && (end_cnt_byte);
    assign done2idle          = state_c == DONE         && (1'b1);
    
    // 字节计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 0;
        end 
        else if(add_cnt_byte)begin 
                if(end_cnt_byte)begin 
                    cnt_byte <= 0;
                end
                else begin 
                    cnt_byte <= cnt_byte + 1;
                end 
        end
       else  begin
           cnt_byte <= cnt_byte;
        end
    end 
    
    assign add_cnt_byte = ((state_c != IDLE) && done);
    assign end_cnt_byte = add_cnt_byte && cnt_byte == (((state_c == FIRWRENCMD) || (state_c == SECMD) || (state_c == RDSRCMD) || (state_c == SECWRENCMD) || (state_c == PPCMD) || (state_c == PPDATA))?(1-1):(3-1));
    
    // 100ms一个命令到下一个命令的等待时间计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_200ns <= 0;
        end 
        else if(add_cnt_200ns)begin 
                if(end_cnt_200ns)begin 
                    cnt_200ns <= 0;
                end
                else begin 
                    cnt_200ns <= cnt_200ns + 1;
                end 
        end
       else  begin
           cnt_200ns <= cnt_200ns;
        end
    end 
    
    assign add_cnt_200ns = (((state_c == FIRWRENCMD) || (state_c == RDSRCMD) || (state_c == SECWRENCMD)) && delay_flag);
    assign end_cnt_200ns = add_cnt_200ns && cnt_200ns == CMD_TIME - 1;
    
    // SE擦除时间2s
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_3s <= 0;
        end 
        else if(add_cnt_3s)begin 
                if(end_cnt_3s)begin 
                    cnt_3s <= 0;
                end
                else begin 
                    cnt_3s <= cnt_3s + 1;
                end 
        end
       else  begin
           cnt_3s <= cnt_3s;
        end
    end 
    
    assign add_cnt_3s = ((state_c == SEADD) && delay_flag);
    assign end_cnt_3s = add_cnt_3s && cnt_3s == SE_TIME - 1;
    
    // 一个命令到下一个命令的等待延长标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            delay_flag <= 0;
        end 
        else if(end_cnt_byte)begin 
            delay_flag <= 1'b1;
        end 
        else if(end_cnt_200ns || end_cnt_3s)begin 
            delay_flag <= 1'b0;
        end 
        else begin
            delay_flag <= delay_flag;
        end
    end
    
    // req信号
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            req_r <= 1'b0;
        end 
        else if(idle2firwrencmd)begin 
            req_r <= 1'b1;
        end
        else if((state_c == FIRWRENCMD) && end_cnt_byte)begin 
            req_r <= 1'b0;
        end 
        else if(firwrencmd2secmd)begin
            req_r <= 1'b1;
        end
        else if(secmd2seadd)begin
            req_r <= 1'b1;
        end
        else if((state_c == SEADD) && end_cnt_byte)begin
            req_r <= 1'b0;
        end
        else if(seadd2rdsrcmd)begin
            req_r <= 1'b1;
        end
        else if((state_c == RDSRCMD) && end_cnt_byte)begin
            req_r <= 1'b0;
        end
        else if(rdsrcmd2secwrencmd)begin
            req_r <= 1'b1;
        end
        else if((state_c == SECWRENCMD) && end_cnt_byte)begin
            req_r <= 1'b0;
        end
        else if(secwrencmd2ppcmd)begin
            req_r <= 1'b1;
        end
        else if(ppcmd2ppadd)begin
            req_r <= 1'b1;
        end
        else if(ppadd2ppdata)begin
            req_r <= 1'b1;
        end
        else if(ppdata2done)begin
            req_r <= 1'b0;
        end 
    end
    
    // dout传输的数据
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_data <= 0;
        end 
        else if(state_c == FIRWRENCMD)begin 
            tx_data <= WREN_CMD;
        end 
        else if(state_c == SECMD)begin 
            tx_data <= SE_CMD;
        end 
        else if(state_c == SEADD)begin
            tx_data <= SE_ADD;
        end
        else if(state_c == RDSRCMD)begin
            tx_data <= RDSR_CMD;
        end
        else if(state_c == SECWRENCMD)begin
            tx_data <= WREN_CMD;
        end
        else if(state_c == PPCMD)begin
            tx_data <= PP_CMD;
        end
        else if(state_c == PPADD)begin
            tx_data <= PP_ADD;
        end
        else if(state_c == PPDATA)begin
            tx_data <= DATA;
        end
    end
    
    assign req = req_r;
    assign dout = tx_data;
    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
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342

    4.spi_control.v

    module spi_control(
        input           clk,
        input           rst_n,
        input   [2:0]   key_out,
        input   [7:0]   din,
        input           done,
        output  [7:0]   dout,
        output          req,
        output  [23:0]  seg_data
    );
    
    wire            rd_req;
    wire            wr_req;
    wire    [7:0]   rd_tx_data;
    wire    [7:0]   wr_tx_data;
    
    assign req = rd_req | wr_req;
    assign dout = ({8{rd_req}} & rd_tx_data) | ({8{wr_req}} & wr_tx_data); 
    
    // 读控制模块
    spi_read_ctrl u_spi_read_ctrl(
        /* input            */.clk      (clk      ),
        /* input            */.rst_n    (rst_n    ),
        /* input   [2:0]    */.key_out  (key_out ),
        /* input   [7:0]    */.din      (din      ),
        /* input            */.done     (done     ),
        /* output           */.req      (rd_req   ),
        /* output  [7:0]    */.dout     (rd_tx_data),
        /* output reg  [23:0]*/.seg_data(seg_data)
    );
    
    // 写控制模块
    spi_write_ctrl u_spi_write_ctrl(
        /* input            */.clk      (clk     ),
        /* input            */.rst_n    (rst_n   ),
        /* input   [2:0]    */.key_out  (key_out),
        /* input   [7:0]    */.din      (din     ),
        /* input            */.done     (done    ),
        /* output           */.req      (wr_req   ),
        /* output  [7:0]    */.dout     (wr_tx_data)
    );
    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

    5.top.v

    module top(
        input           clk,
        input           rst_n,
        input   [2:0]   key_in, 
        output  [7:0]   seg_dig,
        output  [5:0]   seg_sel,
        input           miso,// 主机采样从机发送
        output          mosi,// 主机发送从机
        output          sclk,// 串行时钟
        output          cs_n // 片选信号
    );
    
    wire    [2:0]       key_out;
    wire                req;
    wire                done;
    wire    [7:0]       rx_data;
    wire    [7:0]       tx_data;
    wire    [23:0]      seg_data;                     
    
    // 按键消抖模块
    key_filter u_key_filter(
        /* input                        */.clk      (clk    ),
        /* input                        */.rst_n    (rst_n  ),
        /* input         [KEY_W-1:0]    */.key_in   (key_in ),
        /* output  reg   [KEY_W-1:0]    */.key_out  (key_out)
    );
    
    // 数码管驱动
    seg_driver u_seg_driver(
        /* input                        */.clk      (clk    ),
        /* input                        */.rst_n    (rst_n  ),
        /* input           [23:0]       */.data     (seg_data),
        /* output   reg    [7:0]        */.seg_dig  (seg_dig),
        /* output   reg    [5:0]        */.seg_sel  (seg_sel)
    );
    
    spi_control u_spi_control(
        /* input            */.clk      (clk     ),
        /* input            */.rst_n    (rst_n   ),
        /* input   [2:0]    */.key_out  (key_out ),
        /* input   [7:0]    */.din      (rx_data ),
        /* input            */.done     (done    ),
        /* output  [7:0]    */.dout     (tx_data ),
        /* output           */.req      (req     ),
        /* output  [23:0]   */.seg_data (seg_data)
    );
    
    
    spi_interface u_spi_interface(
        /* input            */.clk      (clk  ),
        /* input            */.rst_n    (rst_n),
        /* // 接口与主机 */
        /* input   [7:0]    */.din      (tx_data),
        /* input            */.req      (req  ),
        /* output  [7:0]    */.dout     (rx_data ),
        /* output           */.done     (done ),
        /* // 接口与flash */
        /* input            */.miso     (miso ),// 主机采样从机发送
        /* output           */.mosi     (mosi ),// 主机发送从机
        /* output           */.sclk     (sclk ),// 串行时钟
        /* output           */.cs_n     (cs_n ) // 片选信号
    );
    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

    6.其他模块
    按键消抖模块
    数码管驱动模块

  • 相关阅读:
    养发神器:重复代码一键生成,从此远离加班,告别秃头!
    华为OD机试 - 玩牌高手 - 动态规划(Java 2023 B卷 100分)
    15 软专
    数据可视化项目(二)
    找实习之从0开始的后端学习日记【9.8】
    宝塔的安装与查看
    常用运维命令
    linux中35个find案例
    如何理解Spring的IOC和AOP
    【开源】JAVA+Vue.js实现天沐瑜伽馆管理系统
  • 原文地址:https://blog.csdn.net/qq_44985628/article/details/125936262