FPGA基础入门【8】开发板外部存储器SPI flash访问 您所在的位置:网站首页 fpga烧写flash读写 FPGA基础入门【8】开发板外部存储器SPI flash访问

FPGA基础入门【8】开发板外部存储器SPI flash访问

2024-01-27 11:24| 来源: 网络整理| 查看: 265

前两篇教程利用数码管project介绍了chipscope和各种烧写开发板的方式,这篇开始继续探索开发板,这次关注外置存储器的控制,外置指的是芯片外部,不是开发板外部。板子上的外置存储器有DDR2和SPI flash,本次先写如何读写SPI flash。

FPGA基础入门【8】开发板外部存储器SPI flash访问 开发板中的外置存储器存储器细节引脚配置指令集控制寄存器重要指令 逻辑设计状态机配置测试读写代码设计 模拟仿真testbench库文件调用仿真脚本仿真结果 编译实现

开发板中的外置存储器

开发板的具体介绍参考此链接:NEXYS 4 DDR Manual PDF

开发板上有两种外置存储器,一种是DDR2,另一种就是上一篇博文中用来保存mcs文件的SPI flash。DDR2已经比较过时了,只因为NEXYS4板本身就比较老,现在新的板子比较多的使用DDR4。SPI flash容量为16M,开发板的配置文件只需要4M以内,因此开发板实际上还可以利用剩下的近12M空间,由于SPI接口不需要调用IP,我们优先介绍SPI flash。

Nexys 4 DDR开发板比起Nexys 4开发板,将原本的Cellular RAM换成了Micron MT47H64M16HR-25:H DDR2存储器,将容量从16M提升到了128M。我们可以使用Vivado的MIG(Memory Interface Generator)来生成和DDR的接口,这一部分下次再写。 DDR2 开发板上另外一个存储设备SPI flash序列号是S25FL128S,使用SPI接口和FPGA连接,接口如下 flash

存储器细节

NEXYS 4 DDR开发板上的SPI flash使用的是S25FL128S芯片,它的文档可以在这里下载:S25FL128S Datasheet

引脚配置

Flash Block Diagram 从图中可以看出有几个引脚是复用的,这是教程第一次用到FPGA以外的芯片,做点详细的分析。

CS#是Chip Select (negative),低电平触发的芯片选择SCK是Serial Clock,串口时钟;SI是Serial Input,串行输入,在双通道或者四通道模式中复用为IO0接口SO是Serial Output,串行输出,在双通道或者四通道模式中复用为IO1接口WP#是Write Protect (negative),低电平触发的写保护,触发时无法配置控制寄存器,在双通道或者四通道模式中复用为IO2接口HOLD#是Hold,低电平触发后可以在不结束CS触发或不停止SCK的情况下暂停串行通信,在双通道或者四通道模式中复用为IO3接口RESET#是低电平触发的复位

这篇教程追求简易实现,暂时不深究如何使用复用接口,只选择初始功能

指令集

不少芯片都用指令访问,具体指令配置要看文档。

指令集有几种不同的形式,不同的时序图,把文档中和指令时序图摘出几个:

独立指令 standalone单字节输入指令 single byte单字节输出指令 single byte out寻址单字节输出指令 Flash wave

观察这几种基本指令的规律,可以把它分为几部分

指令instruction都是8位,必然在最先输出地址address可以是32位或者24位,有些指令不需要地址。理论上来说16MB只需要24位地址,但内部有头地址配置,可以用多枚芯片组成最高4GB,32位地址的存储空间。由于板子上只有一枚,这篇教程只使用24位地址数据data可以是写入flash的数据,也可以是从flash读出的数据,这个数据以字节为单位,可长可短

因此写指令状态机时需要配置的是:指令本身、是否有地址、地址、读还是写数据、读写数据长度(0表示不需要数据)。这在之后的Verilog code中要用

控制寄存器

S25FL128S的寄存器如下 CSR 第一个最重要,其他的看看就好。第一个SR1定义如下 SR1

第7位是状态寄存器写锁定,1表示在WP#引脚拉低时将状态寄存器锁定,无法被修改,0表示不锁定第6位是烧写错误位,1表示烧写有错误第5位是擦除错误位,1表示擦除有错误第4到2位是块保护,1表示将相应存储块锁定,不可擦除第1位是写使能位,只有将这位写到1才能进行烧写擦除操作,否则会被保护,指令写使能WREN和写去能WRDI可以修改这一位第0位是忙碌位,1表示设备正忙,忽略新进来的指令(除了读取状态,和写暂停等少数指令),0表示设备准备好执行下一个指令 重要指令 读取芯片型号READ_ID,指令0x90h,地址24’h000000,读取两个byte,分别是厂商ID和型号ID,S25FL128S的厂商ID为01h,型号ID为17h READ_ID读取芯片信息RDID,指令0x9Fh,不需要地址,读取81个byte RDID读取状态寄存器1 RDSR1,指令0x05h,不需要地址,可以持续读取,不固定读取长度 RDSR1写使能WREN,指令0x06h,无需地址,将写入使能寄存器拉高 WREN擦除一个sector SE,指令0x20h,需要24位地址,在拉高CS后会将该地址所在的sector都设置成高电平,一个sector有64kB,该命令需要写使能WREN后才有效 SE读取数据READ,指令0x03h,需要24位地址,返回第一个byte是相应地址的数据,在拉高停止CS或者停止时钟SCK之前,SO会不停的输出下一个地址的数据,直到最高地址,然后再回到地址0循环往复 READ写入数据Page Program(PP),指令0x02h,需要24位地址,之后跟上一个page的数据,在这里是512byte。如果地址最低9位位非0,则在一个page写完后自动写到下一个page,这个命令只有在写使能WREN之后才有效。如果输入的数据不到512byte,则只从起始地址开始输入相应数据。PP命令只能将相应位置的数据从1写成0,而不能将0拉高成1,因此要写入新数据应该先调用擦除命令,比如SE PP 逻辑设计 状态机配置

状态机,又称作有限状态机finite-state machine (FSM),是FPGA逻辑设计中常见的结构,相比起一般的数字逻辑,它可以比较容易的在FPGA中实现类似软件逻辑。

可以看出SPI flash的配置更偏向软件逻辑,根据不同的instruction依次配置,具体设计如下 FSM

测试读写

伪代码设计大致如下:

READ_ID RDID RDSR1 RDCR READ 0x800000 32 WREN RDSR1 SE 0x800000 RDSR1 READ 0x800000 32 WREN PP 0x800000 0x5a5a5a5a.... while(RDSR1 return busy) {RDSR1} READ 0x800000 32 代码设计 module flash( input clk, input rst, output reg led, // used to debug // Ports for SPI Flash output reg cs_n, input sdi, output reg sdo, output wp_n, output hld_n ); assign wp_n = 1'b1; assign hld_n = 1'b1;

顶层设计中,通用部分依旧是时钟、复位和LED测试输出;其他引脚和SPI flash相连

你可能注意到应该如下所示有6个引脚,但我们只写了5个。原因是SCK引脚所在的E9引脚是系统占用的,因为SPI flash可以作为FPGA初始化配置的来源,具体做法可以参考我的上一篇教程

要使用E9引脚,则需要使用一个Xilinx提供的特殊IP STARTUPE2。它能在FPGA初始化完成之后把这个引脚的控制权交给你,具体用法之后会有 FPGA_flash

parameter IDLE = 4'b0000; parameter START = 4'b0001; parameter INST_OUT = 4'b0010; parameter ADDR1_OUT = 4'b0011; parameter ADDR2_OUT = 4'b0100; parameter ADDR3_OUT = 4'b0101; parameter WRITE_DATA = 4'b0110; parameter READ_DATA = 4'b0111; parameter ENDING = 4'b1000; reg sck; reg [3:0] state; reg [3:0] next_state; (* dont_touch = "true" *)reg [7:0] instruction; (* dont_touch = "true" *)reg [7:0] datain_shift; (* dont_touch = "true" *)reg [7:0] datain; reg [7:0] dataout; reg sck_en; reg [2:0] sck_en_d; reg [2:0] cs_n_d; (* dont_touch = "true" *)reg [7:0] inst_count; reg temp; reg [3:0] sdo_count; reg [15:0] page_count; reg [7:0] wait_count; reg [23:0] addr; reg wrh_rdl; // High indicates write, low indicates read reg addr_req; // Address writing requested reg [15:0] wr_cnt; // Number of bytes to be written reg [15:0] rd_cnt; // Number of bytes to be read

资源定义,parameter是给状态机用的名称。

其中的(* dont_touch = “true” *)是Xilinx定义的资源特质Attribute,在寄存器register定义前加上这个后,编译器就不会把它重新配置来优化布局,这么做是为了方便ChipScope找到相应的寄存器。如果最后逻辑正确,可以将这个Attribute去掉重新编译,对逻辑本身没有影响,仿真也会忽视它。

// State machine always @(posedge clk or posedge rst) begin if(rst) begin state


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有