当前位置: 代码网 > 服务器>网络>网络协议 > FPGA使用SPI控制FLASH

FPGA使用SPI控制FLASH

2024年08月01日 网络协议 我要评论
通过控制FLASH芯片进一步熟悉SPI协议。


前言

通过控制flash芯片进一步熟悉spi协议

一、flash与eeprom芯片

flash 存储器: flash 存储器是一种非易失性存储器,它具有 ram 和 rom 的一些特点。与 rom 类似,flash 存储器的内容在断电时不会丢失,但与 ram 类似,它可以通过编程来修改存储的内容。flash 存储器通常用于嵌入式系统中存储程序代码、配置数据等。
eeprom(电可擦除可编程只读存储器): eeprom 是一种可编程的非易失性存储器,可以通过电擦除和编程来修改存储的内容。eeprom 具有 ram 的特点,可以随机读写,但也具有 rom 的特点,存储的内容在断电时不会丢失。eeprom 通常用于存储配置数据、参数设置等。
flash 存储器和 eeprom(electrically erasable programmable read-only memory)之间的主要区别在于它们的擦除和编程方法、速度、寿命和应用范围等方面。

  • 擦除和编程方法:
    flash 存储器通常使用扇区擦除(sector erase)的方式来擦除数据,并使用编程器(programmer)来进行编程。扇区擦除意味着需要一次性擦除一整个存储扇区,然后才能进行写入操作。eeprom 可以通过逐字节擦除和编程来修改数据,因此擦除和编程更加灵活和精细。

  • 速度:由于 flash 存储器通常采用扇区擦除的方式,擦除和编程操作比 eeprom 慢。eeprom 在擦除和编程时的速度相对较快,因为它可以逐字节地擦除和编程。

  • 寿命:flash 存储器和 eeprom 的擦除和编程操作都会导致存储器单元的磨损,从而影响存储器的寿命。由于 flash 存储器通常需要一次性擦除整个扇区,因此其寿命可能受到擦除次数的限制。eeprom 的擦除和编程操作更加精细,因此其寿命可能比 flash 存储器更长。

  • 应用范围:flash 存储器通常用于存储大容量的程序代码、固件更新等。eeprom 通常用于存储配置数据、参数设置、小容量数据等。

  • 总的来说,flash 存储器和 eeprom 都是非易失性存储器,但它们在擦除和编程方法、速度、寿命和应用范围等方面存在一些差异,因此在选择存储器时需要根据具体的应用需求进行选择
    以下内容为补充的更加详细的参考,来自:https://zhuanlan.zhihu.com/p/27621418
    rom最初不能编程,出厂什么内容就永远什么内容,不灵活。后来出现了prom,可以自己写入一次,要是写错了,只能换一片,自认倒霉。人类文明不断进步,终于出现了可多次擦除写入的eprom,每次擦除要把芯片拿到紫外线上照一下,想一下你往单片机上下了一个程序之后发现有个地方需要加一句话,为此你要把单片机放紫外灯下照半小时,然后才能再下一次,这么折腾一天也改不了几次。历史的车轮不断前进,伟大的eeprom出现了,拯救了一大批程序员,终于可以随意的修改rom中的内容了。
    eeprom的全称是“电可擦除可编程只读存储器”,即electrically erasable programmable read-only memory。是相对于紫外擦除的rom来讲的。但是今天已经存在多种eeprom的变种,变成了一类存储器的统称。
    狭义的eeprom:
    这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种eeprom,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的eeprom都是几十千字节到几百千字节的,绝少有超过512k的。
    flash:
    flash属于广义的eeprom,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的eeprom,我们都叫它flash。flash做的改进就是擦除时不再以字节为单位,而是以块为单位,一次简化了电路,数据密度更高,降低了成本。 上m字节的rom一般都是flash。
    flash又分nand flash和nor flash,nor型存储内容以编码为主,其功能多与运算相关;nand型主要功能是存储资料,如数码相机中所用的记忆卡。

  • nor flash:主要用来执行片上程序
      优点:具有很好的读写性能和随机访问性能,因此它先得到广泛的应用;
      缺点:单片容量较小且写入速度较慢,决定了其应用范围较窄。

  • nand flash:主要用在大容量存储场合
      优点:优秀的读写性能、较大的存储容量和性价比,因此在大容量存储领域得到了广泛的应用;
      缺点:不具备随机访问性能。
    nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。
    nand flash同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。(nandflash按块来擦除,按页来读,norflash没有页)
    由于nandflash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的。小容量的2~12m的flash多是nor型的。
    使用寿命上,nand flash的擦除次数是nor的数倍。而且nand flash可以标记坏块,从而使软件跳过坏块。nor flash 一旦损坏便无法再用。因为nor flash可以进行字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。

二、flash(w25q64jv)常见操作

参考fpga奇哥系列网课

2.1、flash常见操作

在这里插入图片描述

2.2、w25q64jv芯片存储大小

在这里插入图片描述

2.3、状态寄存器:

busy:忙信号,写、擦除操作后自动置0,
wel:写使能信号,写数据、擦除、写状态寄存器时需要置1.
一般只用这两,有其他需求需要看芯片手册。在这里插入图片描述

2.4、时序图

该芯片存储较大,因此不支持页擦除,最小为扇区擦除
在这里插入图片描述
写使能:spi采用模式0或3
在这里插入图片描述
写失能:
在这里插入图片描述
读状态寄存器:0h05则返回第一段状态寄存器数值(7-0bit),0h35为第二段,0h15为第三段,第三段时qspi才有的。
在这里插入图片描述
读数据: 读数据时候先传输指令03h,然后地址(24bit分别对应8位扇区地址、8位页地址和8位字节地址),然后flash返回读数据
在这里插入图片描述
页编程: 写数据之前需要先执行擦除,至少写一个byte数据,最大可写246byte,此时字节地址应该为0,若不是从0开始,当写到当前页最后一个地址时,不会跳到下一页,会返回到当前页0地址开始写起。
在这里插入图片描述
扇区擦除 给扇区擦除指令和地址即可,地址当中的页地址、字节地址没关系,只看扇区地址(本人没有验证过,但看到过这样一句话,有兴趣可以自己验证一下,验证好记得戳一戳我hhh)
在这里插入图片描述
块擦除(32kb块)
在这里插入图片描述
块擦除(64kb块)
在这里插入图片描述
全片擦除:
在这里插入图片描述
读厂商id
在这里插入图片描述

三、程序设计框图

完整代码参考github:https://github.com/shun6-6/flash_spi
在这里插入图片描述

3.1、flash_drive模块

flash_drive模块与用户模块和flash芯片连接
用户模块输入读写操作指令,以及相应的读写数据地址和长度,flash_drive模块结果处理后通过spi将数据写入flash或从中读取数据。
以下为接口代码:

module flash_drive#(
    parameter                      p_data_width  = 8  ,
    parameter                      p_spi_cpol    = 0  ,
    parameter                      p_spi_cphl    = 0  ,
    parameter                      p_read_dwidth = 8  ,
    parameter                      p_op_len      = 32  
)(
    input       i_clk                                       ,
    input       i_rst                                       ,
    /*--------user接口--------*/ 
    input  [1 :0]                   i_operation_type        ,
    input  [23:0]                   i_operation_addr        ,
    input  [8 :0]                   i_operation_byte_num    ,
    input                           i_operation_valid       ,
    output                          o_operation_ready       ,
    input  [p_data_width - 1 : 0]   i_write_data            ,
    input                           i_write_sop             ,
    input                           i_write_eop             ,
    input                           i_write_valid           ,
    output [p_data_width - 1 : 0]   o_read_data             ,
    output                          o_read_sop              ,
    output                          o_read_eop              ,
    output                          o_read_valid            ,   
    /*--------spi接口--------*/
    output                          o_spi_cs                ,
    output                          o_spi_clk               ,
    output                          o_spi_mosi              ,
    input                           i_spi_miso              
);

3.2、flash_ctrl模块

flash_ctrl模块将用户操作指令以及数据进行处理,并产生相应指令驱动spi_drive模块。
对于写数据过程:用户模块会将写指令、写地址以及写数据发送至flash_ctrl模块,flash_ctrl模块将数据存至本地fifo,并发送写使能指令和写数据指令,并且根据spi_drive模块的i_user_write_req信号把fifo当中存入的用户写数据一个个取出,这是因为用户输入的的数据都是并行的8bit数据,而spi是穿行传输的,所以需要spi_drive串行传输完一个byte之后,向flash_ctrl模块发起下一个byte请求,也就是i_user_write_req信号。注:在写同一个地址的时候,一定要先擦除,再写,这是因为flash芯片只能把1变为0,不可以把0变为1,需要重新擦除,将芯片变为全1,才可以继续写。
对于读数据过程:用户模块输入读指令、读地址以及要读取的数据长度信息告知flash_ctrl模块,flash_ctrl模块会产生相应的读指令以控制spi_drive模块读flash,并且将读到的数据先存入fifo,最后完整的传递给用户模块。
flash_ctrl模块接口代码:

module flash_ctrl#(
    parameter                       p_data_width  = 8       ,//数据位宽
    parameter                       p_spi_cpol    = 0       ,//spi时钟极性:0/1表示空闲时钟电平为0/1
    parameter                       p_spi_cphl    = 0       ,//spi时钟相位:0/1表示数据采集沿为时钟第1/2跳变沿
    parameter                       p_read_dwidth = 8       ,//读数据位宽
    parameter                       p_op_len      = 32       //操作数据长度
)(      
    input                           i_clk                   ,
    input                           i_rst                   ,
    /*--------用户接口--------*/        
    input  [1 :0]                   i_operation_type        ,//操作类型 1:read 2:write
    input  [23:0]                   i_operation_addr        ,//操作地址
    input  [8 :0]                   i_operation_byte_num    ,//max write 256 byte
    input                           i_operation_valid       ,//操作有效信号
    output                          o_operation_ready       ,//操作准备信号

    input  [p_data_width - 1 : 0]   i_write_data            ,//写数据
    input                           i_write_sop             ,//写数据-开始信号
    input                           i_write_eop             ,//写数据-结束信号
    input                           i_write_valid           ,//写数据有效

    output [p_data_width - 1 : 0]   o_read_data             ,//读数据
    output                          o_read_sop              ,//读数据-开始信号
    output                          o_read_eop              ,//读数据-结束信号
    output                          o_read_valid            ,//读数据有效
    /*--------驱动接口--------*/            
    output [p_op_len - 1 : 0]       o_user_op_data          ,//操作数据(数据8+地址24)
    output [1:0]                    o_user_op_type          ,//操作类型(读写数据,读写指令)
    output [15:0]                   o_user_op_len           ,//操作数据长度(读写数据8+24,指令8)
    output [15:0]                   o_user_clk_len          ,//时钟周期,读写数据时为8+24+8*字节数
    output                          o_user_op_valid         ,//用户数据有效信号
    input                           i_user_op_ready         ,//驱动准备信号

    output [p_data_width - 1 : 0]   o_user_write_data       ,//写数据
    input                           i_user_write_req        ,//写数据请求

    input  [p_read_dwidth - 1 : 0]  i_user_read_data        ,//读数据
    input                           i_user_read_valid        //读数据有效
    );

flash_ctrl模块状态机描述代码:

//fsm 
localparam  p_st_idle      = 11'b00000000001,//空闲状态,握手成功后进入运行状态
            p_st_run       = 11'b00000000010,//运行状态,如果是读指令则进入读数据状态,否则为擦除或者写数据指令,都需要先进入写使能状态
            p_st_w_en      = 11'b00000000100,//写使能状态,若为写数据指令则进入写指令状态,否则进入擦除状态
            p_st_w_ins     = 11'b00000001000,//写数据指令状态
            p_st_w_data    = 11'b00000010000,//写数据状态
            p_st_r_ins     = 11'b00000100000,//读数据指令状态
            p_st_r_data    = 11'b00001000000,//读数据状态
            p_st_clear     = 11'b00010000000,//擦除状态
            p_st_busy      = 11'b00100000000,//读忙状态寄存器
            p_st_busy_chk  = 11'b01000000000,//检查返回的忙状态寄存器状态,若为忙则进入p_st_busy_wait状态,不忙则说明读数据结束,返回空闲状态
            p_st_busy_wait = 11'b10000000000;//读忙等待状态,计数256后再次返回p_st_busy读忙状态

flash_ctrl模块状态机跳转代码:

always @(*)begin
    case (r_st_cur)
        p_st_idle       : r_st_nxt = w_user_op_active   ? p_st_run      : p_st_idle  ;
        p_st_run        : r_st_nxt = ri_operation_type == p_read_type ? p_st_r_ins : p_st_w_en;
        p_st_w_en       : r_st_nxt = w_spi_op_active    ? 
                                     ri_operation_type == p_write_type ? p_st_w_ins : p_st_clear
                                    : p_st_w_en  ;
        p_st_w_ins      : r_st_nxt = w_spi_op_active    ? p_st_w_data   : p_st_w_ins ;
        p_st_w_data     : r_st_nxt = i_user_op_ready    ? p_st_busy     : p_st_w_data;
        p_st_r_ins      : r_st_nxt = w_spi_op_active    ? p_st_r_data   : p_st_r_ins ;
        p_st_r_data     : r_st_nxt = i_user_op_ready    ? p_st_busy     : p_st_r_data;
        p_st_clear      : r_st_nxt = w_spi_op_active    ? p_st_busy     : p_st_clear ;
        p_st_busy       : r_st_nxt = w_spi_op_active    ? p_st_busy_chk : p_st_busy  ;      
        p_st_busy_chk   : r_st_nxt = ri_user_read_valid ? 
                                     ri_user_read_data[0] ? p_st_busy_wait : p_st_idle
                                     : p_st_busy_chk;  
        p_st_busy_wait  : r_st_nxt = r_st_cnt == 255    ? p_st_busy     : p_st_busy_wait;
        default         : r_st_nxt = p_st_idle;      
    endcase
end

3.3、spi_drive模块

该模块则是按照spi协议和flash的相关操作指令,将输入的指令以及数据发送给flash。
spi_drive模块接口代码:

module spi_drive#(
    parameter                      p_data_width  = 8  ,
    parameter                      p_spi_cpol    = 0  ,
    parameter                      p_spi_cphl    = 0  ,
    parameter                      p_read_dwidth = 8  ,
    parameter                      p_op_len      = 32  //操作数据长度
)( 
    input                          i_clk              ,
    input                          i_rst              ,
                               
    output                         o_spi_cs           ,//spi片选信号
    output                         o_spi_clk          ,//spi时钟线
    output                         o_spi_mosi         ,//spi主机输出
    input                          i_spi_miso         ,//spi主机输入
 
    input  [p_op_len - 1 : 0]      i_user_op_data     ,//操作数据(数据8+地址24)
    input  [1:0]                   i_user_op_type     ,//操作类型(读写数据,读写指令)
    input  [15:0]                  i_user_op_len      ,//操作数据长度(读写数据8+24,指令8)
    input  [15:0]                  i_user_clk_len     ,//时钟周期,读写数据时为8+24+8*字节数
    input                          i_user_op_valid    ,//用户数据有效信号
    output                         o_user_op_ready    ,//主机准备信号
 
    input  [p_data_width - 1 : 0]  i_user_write_data  ,//写数据
    output                         o_user_write_req   ,//写数据请求
 
    output [p_read_dwidth - 1 : 0] o_user_read_data   ,//读数据
    output                         o_user_read_valid   //读数据有效

    );

spi协议的实现过程:
该flash芯片支持的spi是模式0和3,波形图当中是模式0。
run运行信号在片选信号cs拉低时同步拉高,同时开启一个1bit计数器r_spi_cnt,该计数器波形与spi_clk一致,因此以此计数器可以表示当前时钟的上升沿或者下降沿,模式0是在时钟的下降沿改变数据,上升沿采样数据。
在这里插入图片描述

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com