基于SPI协议的Flash全擦除实验

1、理论知识

        SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输,广泛用于EEPROM、Flash、RTC(实时时钟)、ADC(数模转换器)、DSP (数字信号处理器)以及数字信号解码器上,是常用的也是较为重要的通讯协议之一。

        SPI通讯协议的优点是支持全双工通信,通讯方式较为简单,且相对数据传输速率较快;缺点是没有指定的流控制,没有应答机制确认数据是否接收,与IIC总线通讯协议相比,在数据可靠性上有一定缺陷,IIC总线通讯协议的相关内容会在后面章节进行讲解。

        对于SPI通讯协议的相关内容我们分为物理层、协议层两部分进行讲解,具体内容如下。

1.1 物理层

SPI通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分,根据从机设备的个数,SPI通讯设备之间的连接方式可分为一主一从和一主多从

一主一从
一主一从模式
一主多从模式

 

1.2 协议层

1.2.1 CPOL/CPHA及通讯模式

CPOL:时钟极性(clock polarity)状态 0 1

CPHA:时钟相位(clock phase)状态 0 1

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

模式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时钟的奇数边沿,本模式中,偶数边沿为下降沿。

SPI通讯模式时序图

        对于4种通讯模式中,CPOL比较好理解,就是表示设备未被选中的空闲状态时,串行时钟SCK的电平状态,CPOL = 0,空闲状态时SCK为低电平,CPOL = 1,空闲状态时SCK为高电平;CPHA的不同参数则规定了数据采样是在SCK时钟的奇数边沿还是偶数边沿,CPHA = 0,数据采样是在SCK时钟的奇数边沿,CPHA = 1,数据采样是在SCK时钟的偶数边沿,这里不使用上升沿或下降沿表示,是因为不同模式下,奇数边沿或偶数边沿与上升沿或下降沿的对应不是固定的。 

CPHA=0时的SPI通讯模式

        首先,根据SCK在空闲状态时的电平,分为两种情况。CPOL = 0,SCK信号线在空闲状态为低电平; CPOL = 1,SCK信号线在空闲状态为高电平。

        无论CPOL = 0还是1,我们配置的时钟相位CPHA = 0,在图中可以看到,采样时刻都是在SCK的奇数边沿。注意当CPOL=0的时候,时钟的奇数边沿是上升沿,而CPOL=1的时候,时钟的奇数边沿是下降沿。所以SPI的采样时刻不是由上升/下降沿决定的。MOSI和MISO数据线的有效信号在SCK的奇数边沿保持不变,数据信号将在SCK奇数边沿时被采样,在非采样时刻,MOSI和MISO的有效信号才发生切换。

类似地,当CPHA=1时,不受CPOL的影响,数据信号在SCK的偶数边沿被采样,具体见图 。

CPHA=1时的SPI通讯模式

1.2.2 SPI通讯过程 

上文中,我们详细介绍了SPI通讯协议的4中通讯模式,其中模式0和模式3比较常用,下面我们以模式0为例,为大家讲解一下SPI基本的通讯过程。SPI模式0通讯时序图,即空闲时为低电平,采样点为奇数边沿。具体见图。

SPI模式0通讯时序图

        此图表示的是主机视角的通讯时序。SCK、MOSI、CS_N信号均由主机控制产生SCK是时钟信号,用以同步数据MOSI是主机输出从机输入信号,主机通过此信号线传输数据给从机,CS_N为片选信号用以选定从机设备,低电平有效;而MISO的信号由从机产生,主机通过该信号线读取从机的数据。MOSI与MI SO的信号只在CS_N为低电平的时候才有效,在SCK的每个时钟周期MOSI和MISO传输一位数据。 

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

        SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行的数据传输时,MSB先行或LSB先行并没有作硬性规定,但要保证两个SPI通讯设备之间使用同样的协定,一般都会采图 4中的MSB先行模式。

        观察图中的标号处,MOSI及MISO的数据在SCK的下降沿期间变化输出,在SCK的上升沿时被采样。即在SCK的上升沿时刻,MOSI及MISO的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI及MISO为下一次表示数据做准备。

SPI每次数据传输可以8位或16位为单位,每次传输的单位数不受限制。

2 SPI-Flash全擦除实验

2.1 flash芯片原理图

        W25Q64是华邦公司推出的大容量SPI FLASH产品,其容量为64Mb(8M Bytes)。该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块,每个块大小为64K字节,每个块又分为16个扇区,每个扇区4K个字节。W25Q64的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。所以,这需要给W25Q64开辟一个至少4K的缓存区,这样必须要求芯片有4K以上的SRAM才能有很好的操作。
W25Q64的擦写周期多达10W次,可将数据保存达20年之久,支持2.7~3.6V的电压,支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可达80Mhz。

                                                        ——https://www.cnblogs.com/gongchuangsu/p/4850223.html

指令表

        我们在平时对工程进行上板验证的时候,可以通过两种方式烧录程序:一种是将程序下载到FPGA内部的SRAM之中,这种方式烧录过程耗时较短,但缺点是掉电后程序会丢失,再次上电后要重新烧录程序;另外一种就是将程序固化到FPGA外部挂载的Flash芯片中,Flash芯片是非易失性存储器,程序掉电后不会丢失,重 新上电后会执行掉电前烧录到Flash中的程序,但是烧录程序耗时较长。

        如果我们对程序验证完成后,想要将固化到Flash中的程序删除时,可以通过两种方式,分别是全擦除和扇区擦除。

        本文将带领读者分别编写全擦除工程和扇区擦除工程,使读者对这两种擦除方式有清醒的认识,让读者掌握两种擦除方式的实现方法。

        Flash的全擦除,顾名思义就是将Flash所有的存储空间都进行擦除操作,使各存储空间内存储数据恢复到初始值。FPGA要实现Flash的全擦除也有有两种方式。

        方式一:利用FPGA编译软件,通过Quartus软件的“programmer”窗口,将烧录到Flash的*.jic文件擦除,具体见图 ;

        方式二:编写全擦除程序,实现Flash芯片的全擦除,就是我们下面要进行的实验。

2.2 实验目标

        事先向Flash芯片中烧录流水灯程序,FPGA上电执行流水灯程序,下载Flash芯片全擦除程序到FPGA内部SRAM并执行,擦除Flash芯片中烧录的流水灯程序,FPGA重新上电后,无程序执行。

2.2.1 操作时序

 W25Q64的全擦除(Bulk Erase)操作,简称BE,操作指令为8’b1100_0111(C7h)

        在Flash芯片写入全擦出指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态;随后要拉低片选信号,写入全擦除指令,在指令写入过程中,片选信号始终保持低电平,待指令被芯片锁存后,将片选信号拉高;全擦除指令被锁存并执行后,需要等待一个完整的全擦除周期(tBE),才能完成Flash芯片的全擦除操作。全擦除操作的详细介绍及时序图,具体见图 

全擦除操作详细介绍及操作时序

        上文全擦除操作中我们提到,全擦除(BE)指令写入前必须先对Flash芯片写入写使能(WREN)指令,使芯片处于写使能锁存(WEL)状态。此状态下写入全擦除指令才会被Flash芯片响应,否则,全擦除指令无效。 

写使能(Write Enable)指令,简称WREN,操作指令为8’b0000_0110(06h)

由数据手册中写使能介绍部分可知,写使能指令可将Flash芯片设置为写使能锁存(WEL)状态;在每一次页写操作(PP)、扇区擦除(SE)、全擦除(BE)和写状态寄存器(WRSR)操作之前,都需要先进行写使能指令写入操作。操作时序为先拉低片选信号,写入写使能指令,在指令写入过程中,片选信号始终保持低电平 ,指令写入完成后,将片选信号拉高。写使能指令的详细介绍及时序图,具体见图 :

写使能指令详细介绍及操作时序

写使能指令、全擦除指令以及其它操作指令在写入Flash芯片时要严格遵循芯片的串行输入时序。串行输入时序图,具体见图  

串行输入时序图

        如图所示,相关操作指令在写入芯片之前需要先拉低片选信号,在片选信号保持低电平时将指令写入数据输入端口,指令写入完毕,拉高片选信号,数据输出端口在指令写入过程中始终保持高阻态。图中定义了许多时间参数,其中有三个我们需要格外注意,分别是tSLCH、tCHSH和tSHSL。时间参数参考数值,具体见图:

        片选信号自下降沿始到第一个有效数据写入时止,这一段等待时间定义为片选信号有效建立时间 tSLCH,由图可知,这一时间段必须大于等于5ns片选信号自最后一个有效数据写入时始到片选信号上升沿止,这一段等待时间定义为片选信号有效保持时间tCHSH,由图可知,这一时间段必须大于等于5ns;选信号自上一个上升沿始到下一个下降沿止,这一段等待时间定义为片选信号高电平等待时间tSHSL,由图可知,这一时间段必须大于等于100ns

综上所述,绘制完整全擦除操作时序图如图:

完整全擦除操作时序图

  2.3 程序设计

整个全擦除工程调用3个模块,按键消抖模块(key_filter), Flash全擦除模块(flash_be_ctrl)和顶层模块(spi_flash_be)。模块框图,具体见图 46‑17;模块简介,具体见表格 46‑1。

 全擦除工程整体框图

模块名称

功能描述

spi_flsah_be

全擦除工程顶层模块

key_filter

按键消抖模块

flash_be_ctrl

全擦除模块

        外部按键负责产生全擦除触发信号,信号由外部进入FPGA,经顶层模块(spi_flash_be)进入按键消抖模块(key_filter),触发信号经消抖处理后输出进入Flash全擦除模块(spi_flash_be),触发信号有效,Flash全擦除模块工作, 生成并输出串行时钟信号(sck)、片选信号(cs_n)和主输出从输入信号(mosi),3路信号输入外部挂载的Flash芯片,Flash芯片接收到全擦除指令,实现Flash芯片全擦除。

        Flash全擦除模块是本实验工程的核心模块,其生成并输出时钟、片选和数据信号,向Flash芯片发送全擦除指令,控制Flash芯片实现全擦除,本模块的模块框图,具体见图以及表格。

全擦除模块框图

信号

位宽

类型

功能描述

sys_clk

1Bit

Input

系统时钟50MHz

sys_rst_n

1Bit

Input

复位信号,低有效

key

1Bit

Input

全擦除触发信号

sck

1Bit

Output

Flash串行时钟

cs_n

1Bit

Output

Flash片选信号

mosi

1Bit

Output

Flash主输出从输入信号

  

由前文可知,一个完整的全擦除操作需要对Flash芯片执行两次指令的写入,分别为写使能指令和全擦除指令,而且在片选信号拉低后指令写入前、指令写入完成后片选信号拉高前,以及两指令写入之间都需要做规定时间的等待。

对于这一流程操作,我们可以使用状态机来实现。在模块内部声明状态机状态变量state,定义状态机各状态分别为:初始状态(IDLE)、写使能状态(WR_EN)、两指令间等待状态(DELAY)、全擦除状态(BE)。

状态机状态跳转流程如下:系统上电后,状态机状态变量state一直处于初始状态(IDLE);当传入的全擦除触发信号key有效时,表示实验工程开始执行对Flash芯片的全擦除操作,状态机跳转到写使能状态(WR_EN),同时片选信号拉低,选中要进行全擦除操作的Flash芯片;状态跳转到写使能状态且片选信号拉低后,要进行tSLCH≥5ns的等待时间,等待时间过后对主输出从输入信号写入写使能指令,指令写入完成后需要进行tCHSH≥5ns的等待时间,等待时间过后拉高片选信号,取消对Flash芯片的选择,同时状态机跳转到两指令间等待状态(DELAY);在此状态等待时间tSHSL≥ 100ns后,状态机跳转到全擦除状态(BE),同时片选信号拉低,选中已写入写使能指令的Flash芯片;状态机跳转到全擦除状态且片选信号拉低后,要进行tSLCH≥5ns的等待时间,等待时间过后对主输出从输入信号写入全擦除指令,指令写入完成后需要进行tCHSH≥5ns的等待时间,等待时间过后拉高片选信号,取消对Flash芯片的选择,同时状态机跳回初始状态(IDLE),一次完整的全擦除操作完成。

第一个问题:片选信号的等待时间tSHSL、 tCHSH、tSHSL的参数确定。

        在状态机的状态跳转过程中,片选信号在某些位置需要做规定时间的等待,但在各状态的等待时间参数是不同的,如果声明多个计数器对各等待时间分别计数或者使用一个计数器、多个等待结束标志的话,虽然能够实现,但较为麻烦,且声明信号较多。不如声明一个通用计数器,以最长等待时间为下限进行等待时间的计数,且各等待时间没有上限约束,更容易实现。

        由Flash芯片数据手册可知,Flash芯片数据读操作的时钟频率(SCK)上限为20MHz,除数据读操作之外的其他操作频率上限为50MHz,为了后续数据读操作不再进行时钟的更改,本实验工程的所有实验的时钟均使用12.5MHz,因为晶振传入时钟为50MHz,通过四分频生成12.5MHz较为方便,且满足 Flash芯片时钟要求。

        Flash芯片的指令为串行传输,每个时钟周期只能写入1比特数据,要写入一个完整的单字节指令需要8个完整的SCK时钟周期,即32个完整的系统时钟,系统时钟频率为50MHz,完整指令的写入需要640ns。

        这个时间大于片选信号最长等待时间的下限100ns。所以将片选信号的各等待时间的时间参数统一设置为640ns,即32个系统时钟周期。这样声明的计数器不仅可以用作片选信号等待时间计数,也可以用做指令信号写入时间计数,可节省寄存器资源。

        所以声明计数器cnt_clk,初值为0,在0-31计数范围内循环计数,在状态机处于初始状态时,始终保持为0;在状态机处于初始状态之外的其他状态时,每个系统时钟周期自加1,计到最大值清0,重新计数。

第二个问题:状态机状态跳转约束条件的确定。

        状态机在系统上电之后处于初始状态(IDLE),待输入的全擦除触发信号有效时,状态机由初始状态跳转到写使能状态(WR_EN),但写使能状态后的各状态跳转应该如何进行,跳转条件又是什么?

        可以使用刚刚声明的计数器cnt_clk作为状态跳转的约束条件,但条件并不充分,因为计数器cnt_clk为0-31循环计数,使用其单独作为约束条件的话,状态机在每个cnt_clk的计数周期都会存在满足跳转条件的计数值,所以需要声明一个新的计数器来对计数器cnt_clk的计数周期进行计数,使用两个计数器作为约束条件可以实现状态机的状态跳转。

        声明计数器cnt_byte对计数器cnt_clk的计数周期进行计数。对cnt_byte赋初值为0,当状态机处于初始状态(IDLE)时,计数器cnt_byte始终保持初值0;当状态机处于除初始状态外的其他状态时,计数器cnt_byte开始对计数器cnt_clk的计数周期进行计数,cnt_clk每完成一 个完整的循环计数,即cnt_clk = 31时,计数器cnt_byte自加1,其他时刻保持当前值不变。

        使用这两个计数器作为约束条件就可以实现状态机的状态跳转,当状态机跳转到写使能状态时,同时片选信号拉低,在cnt_byte = 0、计数器cnt_clk的第1个计数周期,是对片选信号等待时间tSLCH = 640ns的计数;在cnt_byte = 1、计数器cnt_clk的第2个计数周期,是对写使能指令写入时间进行计数;在cnt_byte = 2、计数器cnt_clk的第3个计数周期,是对片选信号等待时间tCHSH = 640ns的计数,第3个周期的计数完成后状态机跳转到两指令间等待状态(DELAY),同时片选信号拉高,计数器开始进行第4个计数周期的计数;此时cnt_byte = 3,这一计数周期是对片选信号两指令之间的等待时间tSHSL = 640ns的计数,计数完成后状态机跳转到全擦除状态(BE),片选信号再次拉低;在cnt_byte = 4、计数器cnt_clk的第5个计数周期,是对片选信号等待时间tSLCH = 640ns的计数;在cnt_byte = 5、计数器cnt_clk的第6个计数周期,是对全擦除指令写入时间进行计数;在cnt_byte = 6、计数器cnt_clk的第7个计数周期,是对片选信号等待时间tCHSH = 640ns的计数,第7个周期的计数完成后状态机跳回到初始状态(IDLE),Flash芯片的全擦除操作完成。

本实验使用的Flash芯片使用的是SPI通讯协议的模式0,即CPOL= 0,CPHA=0。空闲状态时SCK串行时钟为低电平;数据采样在SCK时钟的奇数边沿,本模式中,奇数边沿为上升沿;数据更新在SCK时钟的偶数边沿,本模式中,偶数边沿为下降沿。 

2.3 程序部分

flash_be_ctrl.v

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;   

//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; 
       
//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;

//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 == 2'd0))
        mosi <= WR_EN_INST[7 - cnt_bit]; //写使能指令
    else if((state == BE) && (cnt_byte == 3'd5) && (cnt_sck == 2'd0))
        mosi <= BE_INST[7 - cnt_bit]; //全擦除指令

endmodule

tb_flash_be_ctrl.v

`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;
        #30
        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

顶层:

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

m25p16仿真模型文件已置顶,本文程序均通过仿真,上板验证,可放心食用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super_WY_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值