• SPI协议读取FLASH【FPGA】


    一、SPI协议

    1、SPI简介

    SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是一种同步串行接口技术,
    是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输
    优点:是支持全双工通信,通讯方式较为简单,且相对数据传输速率较快;
    缺点:是没有指定的流控制,没有应答机制确认数据是否接收

    2、SPI物理层

    SPI 通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分(一主一从和一主多从)
    (1) SCK (Serial Clock):时钟信号线,用于同步通讯数据。由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不同,两个设备之间通讯时,通讯速率受限于低速设备。
    (2) MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,数据方向由主机到从机。
    (3) MISO (Master Input,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,数据方向由从机到主机。
    (4) CS(Chip Select):片选信号线,也称为 CS_N,以下用 CS_N 表示。当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCK、MOSI 及 MISO 同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同使用这 3 条总线;而每个从设备都有独立的这一条 CS_N 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI协议中没有设备地址,它使用 CS_N 信号线来寻址,当主机要选择从设备时,把该从设备的 CS_N 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以 SPI 通讯以 CS_N 线置低电平为开始信号,以 CS_N 线被拉高作为结束信号。

    3、SPI协议层
    CPOL/CPHA 及通讯模式

    SPI 通讯协议一共有四种通讯模式,模式 0、模式 1、模式 2 以及模式 3,这 4 种模式(0、3模式比较常用)
    分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义,其中
    CPOL 参数规定了空闲状态(CS_N 为高电平,设备未被选中)时 SCK 时钟信号的电平状态,(0:低 1:高)
    CPHA 规定了数据采样是在 SCK 时钟的奇数边沿还是偶数边沿。(0:奇数边沿 1:偶数边沿)

    模式 0:CPOL= 0,CPHA=0。空闲状态时 SCK 串行时钟为低电平;数据采样在 SCK时钟的奇数边沿,
    本模式中,奇数边沿为上升沿;数据更新在 SCK 时钟的偶数边沿,本模式中,偶数边沿为下降沿。
    模式 1:CPOL= 0,CPHA=1。空闲状态时 SCK 串行时钟为低电平;数据采样在 SCK时钟的偶数边沿,
    本模式中,偶数边沿为下降沿;数据更新在 SCK 时钟的奇数边沿,本模式中,偶数边沿为上升沿。
    模式 2:CPOL= 1,CPHA=0。空闲状态时 SCK 串行时钟为高电平;数据采样在 SCK时钟的奇数边沿,
    本模式中,奇数边沿为下降沿;数据更新在 SCK 时钟的偶数边沿,本模式中,偶数边沿为上升沿。
    模式 3:CPOL= 1,CPHA=1。空闲状态时 SCK 串行时钟为高电平;数据采样在 SCK时钟的偶数边沿,
    本模式中,偶数边沿为上升沿;数据更新在 SCK 时钟的奇数边沿,本模式中,偶数边沿为下降沿。
    在这里插入图片描述
    红线对应的为数据采样点

    4、 SPI 基本通讯过程

    在这里插入图片描述
    MOSI 与 MISO 的信号只在 CS_N 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

    5、 通讯的起始和停止信号

    在图 46-6 中的标号①处,CS_N 信号线由高变低,是 SPI 通讯的起始信号。CS_N 是每个从机各自独占的信号线,当从机在自己的 CS_N 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。
    在图中的标号⑥处,CS_N 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

    6、数据有效性

    数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采图 46-6 中的 MSB 先行模式,先发送数据的最高位。
    观察图中的②③④⑤标号处,MOSI 及 MISO 的数据在 SCK 的下降沿期间变化输出,在 SCK 的上升沿时被采样。即在 SCK 的上升沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数“0”。在其它时刻,数据无效,MOSI 及 MISO为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

    二、Flash

    1、状态寄存器

    在这里插入图片描述

    1、WIP(正在写入)

    高电平“1” 当前Flash还在工作(写状态寄存器、写入数据、擦除数据)
    低电平“0” 当前Flash处于空闲的状态,可以处理指令。

    2、WEL(写使能锁存器)

    高电平“1” 内部启用锁存设置,可以接收输入的指令
    低电平“0” 除了写使能指令,其他指令一律不接收

    3、BP(块保护)

    ①块保护(BP2、BP1、BP0)位是非易失性的。
    ②定义了需要保护数据的区域(只读),防止被指令擦除
    ③只能使用写状态寄存器指令修改
    ④当所有的BP(BP2、BP1、BP0)都为0的时候,才能执行BE(全擦除)指令

    4、SRWD(状态寄存器写保护)

    状态寄存器写保护位与写保护W(芯片的引脚)共同决定状态寄存器BP2、BP1、BP0为的读写属性。
    写保护W引脚为低电平“0”的时候,SRWD才能决定BP的只读属性。
    写保护W引脚为低电平“1”的时候,BP2、BP1、BP0为只读属性。
    高电平“1” BP位只读
    低电平“0” BP位可读可写

    2、Flash运行的模式
    1、Active Power Mode

    处于该状态下,说明flash正处于工作的状态(CS拉低、CS拉高之后一小段时间以内,都会处于该状态)。

    2、Stand-by Power Mode

    处于该模式下,flash能够处理输入的指令,并且芯片的功耗也会降低。

    3、操作指令
    1、Write Enable(WREN-0x06)

    写使能指令用于设置状态寄存器的WEL位,将该位置“1”,后续很多的指令都需要先写入该指令之后,才能被正确的识别。
    在这里插入图片描述

    2、Write Disable(WRDI-0x04)

    写失能指令设置状态寄存器的WEL位,将该位置“0”
    在以下的条件下,也会将WEL位置“0”
    ①上电
    ②执行WRDI指令完成
    ③执行WRSR(写状态寄存器)指令完成
    ④执行PP指令完成
    ⑤执行SE指令完成
    ⑥执行BE指令完成
    在这里插入图片描述

    3、Read Identification(RDID-0x9F)

    可以直接发送该指令获取设备的信息(3个字节),不需要提前发送WREN指令。
    发送完该指令之后,Flash立即进入Stand-by Power Mode,能够立即接收下一次指令。
    在这里插入图片描述

    4、Read State Register(RDSR-0x05)

    状态寄存器可以在任何时候被读取,包括Flash处于Stand-by Power Mode,都可以被读取。
    在发送一个指令之前,最好先读取状态寄存器的WIP(正在写入)位的值。
    发送完该指令之后,Flash会一直发送状态寄存器的值 ,直到将片选信号拉高。
    在这里插入图片描述

    5、Write State Register(WRSR-0x01)

    更新状态寄存器的值,发送该指令需要先发送WREN(写使能)指令。
    在写入状态寄存器值的时候,依然可以读取状态寄存器WIP(正在写入)值,判断写入是否完成。
    该指令写完之后,将WEL位值“0”。
    在这里插入图片描述

    6、Read Data Byte(READ-0x03)

    发送该指令之后,后续紧跟3个字节的地址信息(扇区地址、页地址、字节地址),之后每次回传一次数据,就会在当前的地址基础上加1,直到片选CS信号拉高。
    数据在时钟下降沿更新输出。输出数据的频率最大20MHz,所以时钟设置为20MHz。
    理论上,该指令能够将整个Flash全部读完。
    当Flash处于擦除、写入数据、写周期的状态时,任何的READ指令都会被拒绝。
    在这里插入图片描述

    7、Read Data Bytes at Higher Speed (FAST_READ-0x0B)
    8、Page Program(PP-0x02)

    写入该指令之前,需要先写入WREN指令,使能写的功能。
    写入指令之后,需要写入3字节的地址信息,再传输需要存储的数据。
    如果写入的数据个数大于了256(一页),那么就会覆盖掉最先存入的数据(在当前页循环存入)。
    将内存中的值又由1变为0
    执行完该指令之后,将状态寄存器的WEL置为“0”。
    在这里插入图片描述

    9、Sector Erase(SE-0xD8)

    在写入指令之前,需要先写入WREN指令,使能写锁存器。
    扇区擦除,将指定扇区的全部数据置“1”
    完成该指令之后,状态寄存器WEL的值会置“0”
    执行该指令的过程,可以读取状态寄存器的WIP的值,判断是否擦除完成
    在这里插入图片描述

    10、Bulk Erase(BE-0xC7)

    在执行指令之前,需要先执行WREN指令,将整个Flash的所有数据置为“1”
    完成该指令之后,状态寄存器WEL的值会置“0”
    执行该指令的过程,可以读取状态寄存器的WIP的值,判断是否擦除完成
    在这里插入图片描述

    三、SPI_FLASH

    spi_master
    module spi_master(
        input           clk     ,
        input           rst_n   ,
    
        input           req     ,
        input   [7:0]   din     ,
        output  [7:0]   dout    ,
        output          done    ,
    
        output          cs_n    ,
        output          mosi    ,
        input           miso    ,
        output          sclk        
          
    );
    
    //参数定义
        localparam  SCLK_PERIOD = 16,   
                    SCLK_FALL   = 4 ,
                    SCLK_RISE   = 12;
    //信号定义
        reg [ 3:0]  cnt_sclk        ;
        wire        add_cnt_sclk    ;
        wire        end_cnt_sclk    ; 
    
        reg [ 3:0]  cnt_bit         ;
        wire        add_cnt_bit     ;
        wire        end_cnt_bit     ;
    
        reg         spi_sclk        ;
        reg         spi_mosi        ;
        reg [7:0]   rx_data         ;
    
    //计数器
     
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt_sclk <= 0; 
            end
            else if(add_cnt_sclk) begin
                if(end_cnt_sclk)
                    cnt_sclk <= 0; 
                else
                    cnt_sclk <= cnt_sclk+1 ;
           end
        end
        assign add_cnt_sclk = (req);
        assign end_cnt_sclk = add_cnt_sclk  && cnt_sclk == (SCLK_PERIOD)-1 ;
        
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt_bit <= 0; 
            end
            else if(add_cnt_bit) begin
                if(end_cnt_bit)
                    cnt_bit <= 0; 
                else
                    cnt_bit <= cnt_bit+1 ;
           end
        end
        assign add_cnt_bit = (end_cnt_sclk);
        assign end_cnt_bit = add_cnt_bit  && cnt_bit == (8)-1 ;
    
    //spi_sclk 模式3     
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                spi_sclk <= 1'b1;
            end
            else if(cnt_sclk == SCLK_FALL-1)begin
                spi_sclk <= 1'b0;
            end
            else if(cnt_sclk == SCLK_RISE-1)begin
                spi_sclk <= 1'b1;
            end
        end
    //发送数据
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                spi_mosi <= 1'b0;
            end
            else if(cnt_sclk == SCLK_FALL-1)begin
                spi_mosi <= din[7-cnt_bit];
            end
        end
    //接收数据
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                rx_data <= 0;
            end
            else if(cnt_sclk == SCLK_RISE-1)begin
                rx_data[7-cnt_bit] <= miso;
            end
        end
    
    //输出
        assign sclk = spi_sclk;
        assign mosi = spi_mosi;
        assign cs_n = ~req;
        assign done = end_cnt_bit;
        assign dout = rx_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
    flash_read
    module flash_read(
    
        input               clk         ,
        input               rst_n       ,
        
        //key
        input               rd_id       ,
        input               rd_data     ,
        
        input   [23:0]      rd_addr     ,//flash读地址
    
        //spi_master
        output              trans_req   ,
        output  [7:0]       tx_dout     ,
        input   [7:0]       rx_din      ,
        input               trans_done  ,
    
        //output
        output  [47:0]      dout        ,
        output  [5:0]       dout_mask   ,
        output              dout_vld    
    );
    
    /*********  工程注释        ****************
    
    M25P16 Flash读控制器,实现读数据和读存储器的ID。
    
    **********  注释结束    ****************/
    
    //状态机参数定义
        localparam  IDLE = 4'b0001,
                    RDID = 4'b0010,//读器件ID
                    RDDA = 4'b0100,//读数据字节
                    DONE = 4'b1000;
    //Flash命令参数定义
        localparam  CMD_RDID = 8'h9F,
                    CMD_RDDA = 8'h03;
    
    //信号定义
    
        reg     [3:0]       state_c     ;
        reg     [3:0]       state_n     ;
    
        reg     [3:0]       cnt_byte    ;
        wire                add_cnt_byte;
        wire                end_cnt_byte;
        reg     [3:0]       byte_num    ;
    
        reg     [7:0]       tx_data     ;
        reg                 tx_req      ;
        
        reg                 flag        ;//读数据、读ID标志
    
        wire                idle2rdid   ; 
        wire                rdid2done   ; 
        wire                idle2rdda   ; 
        wire                rdda2done   ; 
        wire                done2idle   ; 
        
        reg     [31:0]      rx_data	/* synthesis keep */;//串并转换寄存器
        reg     [47:0]      data        ;
        reg     [5:0]       data_mask   ;
        reg                 data_vld    ;
    
    //状态机
        
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                state_c <= IDLE ;
            end
            else begin
                state_c <= state_n;
           end
        end
        
        always @(*) begin 
            case(state_c)  
                IDLE :begin
                    if(idle2rdid)
                        state_n = RDID ;
                    else if(idle2rdda)
                        state_n = RDDA ;
                    else 
                        state_n = state_c ;
                end
                RDID :begin
                    if(rdid2done)
                        state_n = DONE ;
                    else 
                        state_n = state_c ;
                end
                RDDA :begin
                    if(rdda2done)
                        state_n = DONE ;
                    else 
                        state_n = state_c ;
                end
                DONE :begin
                    if(done2idle)
                        state_n = IDLE ;
                    else 
                        state_n = state_c ;
                end
                default : state_n = IDLE ;
            endcase
        end
        
        assign idle2rdid = state_c==IDLE && (rd_id  );
        assign rdid2done = state_c==RDID && (end_cnt_byte);
        assign idle2rdda = state_c==IDLE && (rd_data);
        assign rdda2done = state_c==RDDA && (end_cnt_byte);
        assign done2idle = state_c==DONE && (1'b1);
        
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt_byte <= 0; 
            end
            else if(add_cnt_byte) begin
                if(end_cnt_byte)
                    cnt_byte <= 0; 
                else
                    cnt_byte <= cnt_byte+1 ;
           end
        end
        assign add_cnt_byte = (state_c != IDLE && trans_done);
        assign end_cnt_byte = add_cnt_byte  && cnt_byte == (byte_num)-1 ;
            
        always  @(*)begin
            if(state_c == RDID)
                byte_num = 4;
            else  
                byte_num = 8;   //至少5B CMD + 3 ADDR + X DATA
        end
    
    //输出
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                tx_req <= 1'b0;
            end
            else if(idle2rdid | idle2rdda)begin
                tx_req <= 1'b1;
            end
            else if(rdid2done | rdda2done)begin
                tx_req <= 1'b0;
            end
        end
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                tx_data <= 0;
            end
            else if(state_c == RDID)begin
                case(cnt_byte)
                    0:tx_data <= CMD_RDID;  
                    default:tx_data <= 0; 
                endcase 
            end
            else if(state_c == RDDA)begin
                case(cnt_byte)
                    0:tx_data <= CMD_RDDA; 
                    1:tx_data <= rd_addr[23:16];
                    2:tx_data <= rd_addr[15:8];
                    3:tx_data <= rd_addr[7:0];
                    default:tx_data <= 0; 
                endcase 
            end
        end
    
        //rx_data
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                rx_data <= 0;
            end
            else if(state_c == RDID && trans_done)begin
                rx_data <= {rx_data[23:0],rx_din};
            end
            else if(state_c == RDDA && trans_done)begin
                rx_data <= {rx_data[23:0],rx_din};
            end
        end
    
        //data  
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                data <= 0;
            end
            else if(state_c == DONE && ~flag)begin   //读ID
                data <= {4'd0,rx_data[23:20],4'd0,rx_data[19:16],   //2 0
                         4'd0,rx_data[15:12],4'd0,rx_data[11:8],    //2 0
                         4'd0,rx_data[7:4],4'd0,rx_data[3:0]};      //1 5 
            end 
            else if(state_c == DONE && flag)begin //读数据
                data <= {"R","D",16'd0,4'd0,rx_data[7:4],4'd0,rx_data[3:0]};
            end 
        end
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                data_mask <= 0;
            end
            else if(state_c == DONE && ~flag)begin
                data_mask <= 6'b00_0000;
            end
            else if(state_c == DONE && flag)begin
                data_mask <= 6'b00_1100;
            end
        end
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                data_vld <= 0;
            end
            else begin
                data_vld <= state_c == DONE; 
            end
        end
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                flag <= 1'b0;
            end
            else if(idle2rdda)begin
                flag <= 1'b1;
            end
            else if(idle2rdid)begin
                flag <= 1'b0;
            end
        end
    
    //输出
        assign tx_dout =tx_data;
        assign trans_req = tx_req;
    
        assign dout = data;
        assign dout_mask = data_mask;
        assign dout_vld = data_vld;
    
    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
    flash_write
    module flash_write(
    
        input               clk         ,
        input               rst_n       ,
        
        //key
        input               write       ,
        input   [ 7:0]      wr_data     ,//写入的数据
        input   [23:0]      wr_addr     ,//flash写地址
    
        //spi_master
        output              trans_req   ,
        output  [7:0]       tx_dout     ,
        input   [7:0]       rx_din      ,
        input               trans_done  ,
    
        //output
        output  [47:0]      dout        ,
        output  [5:0]       dout_mask   ,
        output              dout_vld    
    );
    
    //参数定义
        //主状态机状态参数
        localparam  M_IDLE = 5'b0_0001,
                    M_WREN = 5'b0_0010,//主机发写使能序列
                    M_SE   = 5'b0_0100,//主机发扇区擦除序列
                    M_RDSR = 5'b0_1000,//主机发读状态寄存器序列
                    M_PP   = 5'b1_0000;//主机发页编程序列
        //从状态机状态参数
        localparam  S_IDLE = 5'b0_0001,
                    S_CMD  = 5'b0_0010,//发送命令
                    S_ADDR = 5'b0_0100,//发送地址
                    S_DATA = 5'b0_1000,//发送数据、接收数据
                    S_DELA = 5'b1_0000;//延时 拉高片选信号
    
        localparam  CMD_WREN = 8'h06,//写使能命令
                    CMD_SE   = 8'hD8,//擦除命令
                    CMD_RDSR = 8'h05,//读状态寄存器命令
                    CMD_PP   = 8'h02;//页编程命令
    
        parameter   TIME_DELAY = 16,//发完WREN、SE、PP序列 拉高片选
                    TIME_SE    = 150_000_000,//扇区擦除3s
                    TIME_PP    = 25_000,//页编程5ms
                    TIME_RDSR  = 2000;//读状态寄存器 最大2000次
    
    //信号定义
    
        reg         [4:0]       m_state_c       ;
        reg         [4:0]       m_state_n       ;
        reg         [4:0]       s_state_c       ;
        reg         [4:0]       s_state_n       ;
    
        reg         [3:0]       cnt0            ;
        wire                    add_cnt0        ;
        wire                    end_cnt0        ;
        reg         [3:0]       xx              ;
    
        reg         [31:0]      cnt1            ;
        wire                    add_cnt1        ;
        wire                    end_cnt1        ;
    	reg			[31:0]      yy				;
    
        reg                     rdsr_wip        ;
        reg                     rdsr_wel        ;
        reg         [2:0]       flag            ;
        
        reg         [7:0]       tx_data         ;
        reg                     tx_req          ;
        
        reg         [47:0]      data            ;
        reg         [5:0]       data_mask       ;
        reg                     data_vld        ;
    
        wire                    m_idle2m_wren   ; 
        wire                    m_wren2m_se     ; 
        wire                    m_se2m_rdsr     ; 
        wire                    m_rdsr2m_wren   ; 
        wire                    m_rdsr2m_pp     ; 
        wire                    m_wren2m_pp     ; 
        wire                    m_rdsr2m_idle   ; 
        wire                    m_pp2m_rdsr     ; 
    
        wire                    s_idle2s_cmd    ; 
        wire                    s_cmd2s_addr    ; 
        wire                    s_cmd2s_data    ; 
        wire                    s_cmd2s_dela    ; 
        wire                    s_addr2s_data   ; 
        wire                    s_addr2s_dela   ; 
        wire                    s_data2s_dela   ; 
        wire                    s_dela2s_idle   ; 
    
    //状态机设计
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                m_state_c <= M_IDLE ;
            end
            else begin
                m_state_c <= m_state_n;
           end
        end
        
        always @(*) begin 
            case(m_state_c)  
                M_IDLE :begin
                    if(m_idle2m_wren)
                        m_state_n = M_WREN ;
                    else 
                        m_state_n = m_state_c ;
                end
                M_WREN :begin
                    if(m_wren2m_se)
                        m_state_n = M_SE ;
                    else if(m_wren2m_pp)
                        m_state_n = M_PP ;
                    else 
                        m_state_n = m_state_c ;
                end
                M_SE :begin
                    if(m_se2m_rdsr)
                        m_state_n = M_RDSR ;
                    else 
                        m_state_n = m_state_c ;
                end
                M_RDSR :begin
                    if(m_rdsr2m_wren)
                        m_state_n = M_WREN ;
                    else if(m_rdsr2m_pp)
                        m_state_n = M_PP ;
                    else if(m_rdsr2m_idle)
                        m_state_n = M_IDLE ;
                    else 
                        m_state_n = m_state_c ;
                end
                M_PP :begin
                    if(m_pp2m_rdsr)
                        m_state_n = M_RDSR ;
                    else 
                        m_state_n = m_state_c ;
                end
                default : m_state_n = M_IDLE ;
            endcase
        end
        
        assign m_idle2m_wren = m_state_c==M_IDLE && (write);
        assign m_wren2m_se   = m_state_c==M_WREN && (s_dela2s_idle && flag[0]);
        assign m_se2m_rdsr   = m_state_c==M_SE   && (s_dela2s_idle);
        assign m_rdsr2m_wren = m_state_c==M_RDSR && (s_dela2s_idle && ~rdsr_wel && ~rdsr_wip && flag[1]);
        assign m_rdsr2m_pp   = m_state_c==M_RDSR && (s_dela2s_idle && rdsr_wel && ~rdsr_wip && flag[1]);
        assign m_wren2m_pp   = m_state_c==M_WREN && (s_dela2s_idle && flag[1]);
        assign m_rdsr2m_idle = m_state_c==M_RDSR && (s_dela2s_idle && flag[2]);
        assign m_pp2m_rdsr   = m_state_c==M_PP   && (s_dela2s_idle);
        
     //从状态机设计
            
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                s_state_c <= S_IDLE ;
            end
            else begin
                s_state_c <= s_state_n;
           end
        end
        
        always @(*) begin 
            case(s_state_c)  
                S_IDLE :begin
                    if(s_idle2s_cmd)
                        s_state_n = S_CMD ;
                    else 
                        s_state_n = s_state_c ;
                end
                S_CMD :begin
                    if(s_cmd2s_addr)
                        s_state_n = S_ADDR ;
                    else if(s_cmd2s_data)
                        s_state_n = S_DATA ;
                    else if(s_cmd2s_dela)
                        s_state_n = S_DELA ;
                    else 
                        s_state_n = s_state_c ;
                end
                S_ADDR :begin
                    if(s_addr2s_data)
                        s_state_n = S_DATA ;
                    else if(s_addr2s_dela)
                        s_state_n = S_DELA ;
                    else 
                        s_state_n = s_state_c ;
                end
                S_DATA :begin
                    if(s_data2s_dela)
                        s_state_n = S_DELA ;
                    else 
                        s_state_n = s_state_c ;
                end
                S_DELA :begin
                    if(s_dela2s_idle)
                        s_state_n = S_IDLE ;
                    else 
                        s_state_n = s_state_c ;
                end
                default : s_state_n = S_IDLE ;
            endcase
        end
        
        assign s_idle2s_cmd  = s_state_c==S_IDLE && (m_state_c != M_IDLE);
        assign s_cmd2s_addr  = s_state_c==S_CMD  && (trans_done && (m_state_c == M_SE || m_state_c == M_PP));
        assign s_cmd2s_data  = s_state_c==S_CMD  && (trans_done && m_state_c == M_RDSR);
        assign s_cmd2s_dela  = s_state_c==S_CMD  && (trans_done && m_state_c == M_WREN);
        assign s_addr2s_data = s_state_c==S_ADDR && (end_cnt0 && (m_state_c == M_RDSR || m_state_c == M_PP));
        assign s_addr2s_dela = s_state_c==S_ADDR && (end_cnt0 && m_state_c == M_SE);
        assign s_data2s_dela = s_state_c==S_DATA && (end_cnt0 && m_state_c == M_PP || m_state_c == M_RDSR && end_cnt1);
        assign s_dela2s_idle = s_state_c==S_DELA && (end_cnt1);
        
        //计数器设计
        
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt0 <= 0; 
            end
            else if(add_cnt0) begin
                if(end_cnt0)
                    cnt0 <= 0; 
                else
                    cnt0 <= cnt0+1 ;
           end
        end
        assign add_cnt0 = (m_state_c != M_RDSR && trans_done);
        assign end_cnt0 = add_cnt0 && cnt0 == (xx)-1 ;
        
        always  @(*)begin
            if(s_state_c == S_CMD)  //发命令1字节
                xx = 1;
            else if(s_state_c == S_ADDR)    //发地址3字节
                xx = 3;
            else /*if(s_state_c == S_DATA)*/ //发数据1字节
                xx = 4;
        end
        
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt1 <= 0; 
            end
            else if(add_cnt1) begin
                if(end_cnt1)
                    cnt1 <= 0; 
                else
                    cnt1 <= cnt1+1 ;
           end
        end
        assign add_cnt1 = (s_state_c == S_DELA || m_state_c == M_RDSR && s_state_c == S_DATA && trans_done);
        assign end_cnt1 = add_cnt1  && (cnt1 == (yy)-1 || trans_done && ~rdsr_wip);
        
        always  @(*)begin
            if((m_state_c == M_WREN || m_state_c == M_RDSR) && s_state_c == S_DELA)  //发完写使能延时
                yy = TIME_DELAY;
            else if(m_state_c == M_SE)//发完擦除延时
                yy = TIME_SE;
            else if(m_state_c == M_PP)//发完页编程延时
                yy = TIME_PP;
            else if(m_state_c == M_RDSR && s_state_c == S_DATA)//读状态寄存器值 yy 次
                yy = TIME_RDSR;
    	     else 
    		    yy = TIME_DELAY;
        end
    
    //rdsr_wel rdsr_wip
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                rdsr_wel <= 1'b0;
                rdsr_wip <= 1'b0;
            end
            else if(m_state_c == M_RDSR && s_state_c == S_DATA && trans_done)begin
                rdsr_wel <= rx_din[1];
                rdsr_wip <= rx_din[0];
            end
        end
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                flag <= 3'b000;
            end
            else if(m_idle2m_wren)begin
                flag <= 3'b001;
            end
            else if(m_se2m_rdsr)begin
                flag <= 3'b010;
            end
            else if(m_pp2m_rdsr)begin
                flag <= 3'b100;
            end
        end
    
    //输出    
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                tx_data <= 0;
            end
            else if(m_state_c == M_WREN)begin
                tx_data <= CMD_WREN;
            end
            else if(m_state_c == M_SE)begin
                case(s_state_c)
                    S_CMD :tx_data <= CMD_SE;
                    S_ADDR:tx_data <= wr_addr[23-cnt0*8 -:8];
                    default:tx_data <= 0;
                endcase 
            end
            else if(m_state_c == M_RDSR)begin   //在读状态寄存器时,可以发一次命令,也可以连续发命令
                tx_data <= CMD_RDSR;
            end 
            else if(m_state_c == M_PP)begin 
                case(s_state_c)
                    S_CMD :tx_data <= CMD_PP;
                    S_ADDR:tx_data <= wr_addr[23-cnt0*8 -:8];
                    S_DATA:tx_data <= wr_data + cnt0;
                    default:tx_data <= 0;
                endcase 
            end 
        end
    
        always  @(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                tx_req <= 1'b0;
            end
            else if(s_idle2s_cmd)begin
                tx_req <= 1'b1;
            end
            else if(s_cmd2s_dela | s_addr2s_dela | s_data2s_dela)begin
                tx_req <= 1'b0;
            end
        end
    //data    data_mask
        always @(posedge clk or negedge rst_n)begin 
            if(!rst_n)begin
                data <= 0;
                data_mask <= 0;
            end 
            else if(m_rdsr2m_idle & ~rdsr_wip)begin //PP completed
                data <= {"P","P",16'd0,4'd0,wr_data[7:4],4'd0,wr_data[3:0]};
                data_mask <= 6'b001100;
            end
            else if(m_rdsr2m_idle & rdsr_wip)begin //PP failed
                data <= {"P","P",8'd0,"ERR"};
                data_mask <= 6'b001000;
            end  
        end
    
    //data_vld   ,
        always @(posedge clk or negedge rst_n)begin 
            if(!rst_n)begin
                data_vld <= 0;
            end 
            else begin 
                data_vld <= m_rdsr2m_idle;
            end 
        end
    
    //输出
        assign tx_dout = tx_data;
        assign trans_req = tx_req;
        assign dout_vld = data_vld;
        assign dout_mask = data_mask;
        assign dout = 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
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    flash_ctrl
    module flash_ctrl(
    
        input               clk         ,
        input               rst_n       ,
        
        input   [2:0]       key         ,
        input   [ 7:0]      wr_din      ,//写入的数据
        input   [23:0]      rw_addr     ,//flash读写地址
        
        output              trans_req   ,
        output  [7:0]       tx_dout     ,
        input   [7:0]       rx_din      ,
        input               trans_done  ,
    
        output  [47:0]      dout        ,
        output  [5:0]       dout_mask   ,
        output              dout_vld     
    
    );
    
    //信号定义
        
        wire                 wr_req         ; 
        wire     [7:0]       wr_dout        ; 
    
        wire     [47:0]      wr_data        ;
        wire     [5:0]       wr_data_mask   ;
        wire                 wr_data_vld    ;
    
        wire                 rd_req         ; 
        wire     [7:0]       rd_dout        ; 
     
        wire     [47:0]      rd_data        ;
        wire     [5:0]       rd_data_mask   ;
        wire                 rd_data_vld    ;
    
    //模块例化
    flash_write u_write(
        /*input               */.clk         (clk           ),
        /*input               */.rst_n       (rst_n         ),
        /*input               */.write       (key[2]        ),
        /*input   [ 7:0]      */.wr_data     (wr_din        ),//写入的数据
        /*input   [23:0]      */.wr_addr     (rw_addr       ),//flash写地址
        /*output              */.trans_req   (wr_req        ),
        /*output  [7:0]       */.tx_dout     (wr_dout       ),
        /*input   [7:0]       */.rx_din      (rx_din        ),
        /*input               */.trans_done  (trans_done    ),
        /*output  [47:0]      */.dout        (wr_data       ),
        /*output  [5:0]       */.dout_mask   (wr_data_mask  ),
        /*output              */.dout_vld    (wr_data_vld   )
    );
     flash_read u_read(
        /*input               */.clk         (clk           ),
        /*input               */.rst_n       (rst_n         ),
        /*input               */.rd_id       (key[0]        ),
        /*input               */.rd_data     (key[1]        ),
        /*input   [23:0]      */.rd_addr     (rw_addr       ),//flash读地址
        /*output              */.trans_req   (rd_req        ),
        /*output  [7:0]       */.tx_dout     (rd_dout       ),
        /*input   [7:0]       */.rx_din      (rx_din        ),
        /*input               */.trans_done  (trans_done    ),
        /*output  [47:0]      */.dout        (rd_data       ),
        /*output  [5:0]       */.dout_mask   (rd_data_mask  ),
        /*output              */.dout_vld    (rd_data_vld   )
    );
    
        assign trans_req = rd_req | wr_req;
        assign tx_dout = {8{wr_req}} & wr_dout | {8{rd_req}} & rd_dout;
        assign dout_vld = wr_data_vld | rd_data_vld;
        assign dout = {48{wr_data_vld}} & wr_data 
                    | {48{rd_data_vld}} & rd_data;
        assign dout_mask = {6{wr_data_vld}} & wr_data_mask
                         | {6{rd_data_vld}} & rd_data_mask;
    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

    四、参考

    【FPGA】FPGA基于spi的flash读写

    五、源码

    https://github.com/IvanXiang/FPGA_SPI_FLASH

  • 相关阅读:
    2024年 为什么不建议新人学习ABAP
    Win:使用 netsh 命令配置 Port Forwarding
    【牛客网刷题系列 之 Verilog快速入门】~ 位拆分与运算
    如何用python做简单的接口压力测试
    【若依框架RuoYi-Vue-Plus 图片回显不显示问题,OSS文件上传或者本地上传】
    压力管道的分类
    LeetCode 619, 58, 24
    ai智能写作软件哪个好-AI智能写作软件的类型标准
    什么是Redis脑裂,如何解决呢
    Vue的使用2
  • 原文地址:https://blog.csdn.net/xyf_fate/article/details/126858652