flash读写学习笔记与spi接口及简单测试验证(三)
FPGA中的视频图像资源,以及想要永久存储的程序都是要存储在flash中,flash是FPGA一个不可缺少的部分,flash的种类有很多,根据winbond公司的128Mbit Qual SPI接口的flash,型号为W25Q128BV,作为初学者根据现有的资料去学习,下面的内容主要以这款芯片作参考。前面也提到了三大串行数据传输模式UART,I2C,SPI,顺道就把SPI的内容也做一下总结,每篇一句话,带着自己的思考看问题,尽信书不如无书,fighting!!!
一、flash简单分类
flash分为nor flash和nand flash。nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。nand flash同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。NOR Flash的读取,用户可以直接运行装载在NOR FLASH里面的代码。NAND Flash没有采取内存RAM的随机读取技术,它的读取是以一次读取一块的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。nandflash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的。小容量的2~12M的flash多是nor型的。nor flash可以进行字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。
二、SPI接口
SPI(serial peripheral Interface)串行外设接口总线系统是一种同步串行外设接口,使MCU与各种外围设备以串行方式进行通信以交换信息。SPI接口主要应用在EEPROM、FLASH、实时时钟、AD转换器,还有数字信号处理器和数字信号解码器。在CPU和外围低速器件之间进行同步串行数据传输,数据按位传输,低位在前,高位在后,全双工通信。
三、QSPI FLASH硬件介绍

Flash容量由65536个256-byte的page组成,三种擦除方式。一种为Sector(16个page,共4KB),一种为Block擦除(128个page,共32KB),另一种为Chip擦除(整个擦除)。连接的管脚有QSPI_CS, QSPI_CLK, QSPI_MISO0, QSPI_MISO1, QSPI_MISO2, QSPI_MISO3。下面是一些SPI用到的几个命令。
(1)读 Manufacturer/Device ID(90h)先发送命令字90,再发送24位的地址(全0),然后接收2个byte的数据(第一个数据是 Manufacturor:FEh,第二个是设备的 Device ID:17h),数据在时钟的上升沿采样。

(2) Sector 擦除(20) 先发送命令字 20,再发送 24 位的地址。数据都在时钟的上升沿采样。
(3)先发送命令字 05,然后接收 16 位的寄存器数据。数据都在时钟的上 升沿采样。

(4)先发送命令字 02,再发送 24 位的地址,然后写入 256 个编程的数据(数 据的数量可以自己修改, 但不能超过 256 个)。数据都在时钟的上升沿采样。

(5)先发送命令字 03,再发送 24 位的地址,然后接收数据。数据在时钟的上升 沿采样。

四、程序设计
实现FLASH设备ID的读取,Sector擦除,Page编程,数据的读取。由一个顶层设计模块和一个子模块组成,同样通过程序中加入自己的笔记,红色为所加。SPI 通信程序按照 FLASH 的 SPI 时序把并行的命令,地址或数据转成串行的数据从 SPI 发 送给 FLASH;在读的时候接收 SPI 的串行的数据转化为并行的数据。这里需要注意的是 SPI 发送数据的时候,数据是在时钟的下降沿改变的。所以读取数据时,是要在时钟的上升沿读取。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: flash_spi
//////////////////////////////////////////////////////////////////////////////////
module flash_spi(
output flash_clk,
output reg flash_cs, //使能信号
output reg flash_datain,
input flash_dataout, input clock25M,
input flash_rstn,
input [:] cmd_type,
output reg Done_Sig,
input [:] flash_cmd,
input [:] flash_addr,
output reg [:] mydata_o,
output myvalid_o,
output reg [:] spi_state ); assign myvalid_o=myvalid; assign flash_clk=spi_clk_en?clock25M:; reg myvalid;
reg [:] mydata; reg spi_clk_en='b0;
reg data_come; parameter idle='b000;
parameter cmd_send='b001;
parameter address_send='b010;
parameter read_wait='b011;
parameter write_data='b101;
parameter finish_done='b110; reg [:] cmd_reg; //cmd_reg作为flash_cmd输入命令信号的寄存器,有输入信号时与flash_cmd一样都为寄存器变量,但寄存器内部数值可能发生变化。
reg [:] address_reg; //address_reg同样作为flash_addr信号的内部寄存器。
reg [:] cnta; //自己的理解,cnta是一个8位的寄存器变量,代表存储的是一个数值,这个数值在此时用来表示串转并或者并转串的某一位位数数值,同理下面的cntb。
reg [:] write_cnt;
reg [:] cntb;
reg [:] read_cnt;
reg [:] read_num; reg read_finish; //发送读flash命令
always @(negedge clock25M) //读数据在时钟的下降沿
begin
if(!flash_rstn)
begin
flash_cs<='b1;
spi_state<=idle;
cmd_reg<=;
address_reg<=;
spi_clk_en<='b0; //SPI clock输出不使能
cnta<=;
write_cnt<=;
read_num<=;
address_reg<=;
Done_Sig<='b0;
end
else
begin
case(spi_state)
idle: begin
spi_clk_en<='b0;
flash_cs<='b1;
flash_datain<='b1;
cmd_reg<=flash_cmd;
address_reg<=flash_addr;
Done_Sig<='b0;
if(cmd_type[]=='b1) begin //如果flash操作命令请求
spi_state<=cmd_send;
cnta<=;
write_cnt<=;
read_num<=;
end
end
cmd_send:begin
spi_clk_en<='b1; //flash的SPI clock输出
flash_cs<='b0; //cs拉低
if(cnta>) begin //如果cmd_reg还没有发送完
flash_datain<=cmd_reg[cnta]; //发送bit7~bit1位
cnta<=cnta-'b1;
end
else begin //发送bit0
flash_datain<=cmd_reg[];
if ((cmd_type[:]=='b001) | (cmd_type[2:0]==3'b100)) begin //如果是Write Enable/disable instruction
spi_state<=finish_done;
end
else if (cmd_type[:]=='b011) begin //如果是read register1
spi_state<=read_wait;
cnta<=;
read_num<=; //接收一个数据
end
else begin //如果是sector erase, page program, read data,read device ID
spi_state<=address_send;
cnta<=;
end
end
end
address_send:begin
if(cnta>) begin //如果cmd_reg还没有发送完
flash_datain<=address_reg[cnta]; //发送bit23~bit1位
cnta<=cnta-;
end
else begin //发送bit0
flash_datain<=address_reg[];
if(cmd_type[:]=='b010) begin //如果是 sector erase
spi_state<=finish_done;
end
else if (cmd_type[:]=='b101) begin //如果是page program
spi_state<=write_data;
cnta<=;
end
else if (cmd_type[:]=='b000) begin //如果是读Device ID
spi_state<=read_wait;
read_num<=; //接收2个数据的Device ID
end
else begin
spi_state<=read_wait;
read_num<=; //接收256个数据
end
end
end
read_wait: begin
if(read_finish) begin
spi_state<=finish_done;
data_come<='b0;
end
else
data_come<='b1;
end
write_data: begin
if(write_cnt<) begin // program 256 byte to flash
if(cnta>) begin //如果data还没有发送完
flash_datain<=write_cnt[cnta]; //发送bit7~bit1位
cnta<=cnta-'b1;
end
else begin
flash_datain<=write_cnt[]; //发送bit0
cnta<=;
write_cnt<=write_cnt+'b1;
end
end
else begin
spi_state<=finish_done;
spi_clk_en<='b0;
end end
finish_done:begin
flash_cs<='b1;
flash_datain<='b1;
spi_clk_en<='b0;
Done_Sig<='b1;
spi_state<=idle;
end
default:spi_state<=idle;
endcase;
end
end //接收flash数据,把SPI接收的串行数据转成8位字节
always @(posedge clock25M)
begin
if(!flash_rstn)begin
read_cnt<=;
cntb<=;
read_finish<='b0;
myvalid<='b0;
mydata<=;
mydata_o<=;
end
else
if(data_come) begin
if(read_cnt<read_num) begin //接收read_num个数据
if(cntb<) begin //接收一个byte的bit0~bit6
myvalid<='b0;
mydata<={mydata[:],flash_dataout};
cntb<=cntb+'b1;
end
else begin
myvalid<='b1; //一个byte数据有效
mydata_o<={mydata[:],flash_dataout}; //接收bit7
cntb<=;
read_cnt<=read_cnt+'b1;
end
end
else begin
read_cnt<=;
read_finish<='b1;
myvalid<='b0;
end
end
else begin
read_cnt<=;
cntb<=;
read_finish<='b0;
myvalid<='b0;
mydata<=;
end
end endmodule
顶层控制程序
module flash_test
(
input CLK,
input RSTn, output flash_clk,
output flash_cs,
output flash_datain,
input flash_dataout ); /*******************************/ reg [:] i;
reg [:] flash_cmd;
reg [:] flash_addr; reg clock25M;
reg [:] cmd_type; reg [:] time_delay; wire Done_Sig;
wire [:] mydata_o;
wire myvalid_o;
wire [:] spi_state; /*******************************/
//FLASH 擦除,Page Program,读取程序
/*******************************/
always @ ( posedge clock25M or negedge RSTn )
if( !RSTn ) begin
i <= 'd0;
flash_addr <= 'd0;
flash_cmd <= 'd0;
cmd_type <= 'b0000;
time_delay<=;
end
else
case( i ) 'd0://读Device ID
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1; cmd_type <= 'b0000; end
else begin flash_cmd <= 'h90; flash_addr <= 24'd0; cmd_type <= 'b1000; end 'd1://写Write Enable instruction
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1; cmd_type <= 'b0000; end
else begin flash_cmd <= 'h06; cmd_type <= 4'b1001; end 'd2://Sector擦除
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1; cmd_type<='b0000; end
else begin flash_cmd <= 'h20; flash_addr <= 24'd0; cmd_type <= 'b1010; end 'd3://waitting 100 clock
if( time_delay<'d100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+'b1; cmd_type <= 4'b0000; end
else begin i <= i + 'b1; time_delay<=0; end 'd4://读状态寄存器1, 等待idle
if( Done_Sig ) begin
if (mydata_o[]=='b0) begin flash_cmd <= 8'h00; i <= i + 'b1; cmd_type <= 4'b0000; end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end
end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end 'd5://写Write disable instruction
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1; cmd_type <= 'b0000; end
else begin flash_cmd <= 'h04; cmd_type <= 4'b1100; end 'd6://读状态寄存器1, 等待idle
if( Done_Sig ) begin
if (mydata_o[]=='b0) begin flash_cmd <= 8'h00; i <= i + 'b1; cmd_type <= 4'b0000; end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end
end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end 'd7://写Write Enable instruction
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1; cmd_type <= 'b0000; end
else begin flash_cmd <= 'h06; cmd_type <= 4'b1001; end 'd8://waitting 100 clock
if( time_delay<'d100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+'b1; cmd_type <= 4'b0000; end
else begin i <= i + 'b1; time_delay<=0; end 'd9://page program: write 0~255 to flash
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1;cmd_type <= 'b0000; end
else begin flash_cmd <= 'h02; flash_addr <= 24'd0; cmd_type <= 'b1101; end 'd10://waitting
if( time_delay<'d100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+'b1; cmd_type <= 4'b0000; end
else begin i <= i + 'b1; time_delay<=0; end 'd11://读状态寄存器1, 等待idle
if( Done_Sig ) begin
if (mydata_o[]=='b0) begin flash_cmd <= 8'h00; i <= i + 'b1; cmd_type <= 4'b0000; end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end
end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end 'd12://写Write disable instruction
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1; cmd_type <= 'b0000; end
else begin flash_cmd <= 'h04; cmd_type <= 4'b1100; end 'd13://读状态寄存器1, 等待idle
if( Done_Sig ) begin
if (mydata_o[]=='b0) begin flash_cmd <= 8'h00; i <= i + 'b1; cmd_type <= 4'b0000; end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end
end
else begin flash_cmd <= 'h05; cmd_type <= 4'b1011; end 'd14://read 256byte
if( Done_Sig ) begin flash_cmd <= 'h00; i <= i + 1'b1; cmd_type <= 'b0000; end
else begin flash_cmd <= 'h03; flash_addr <= 24'd0; cmd_type <= 'b1110; end 'd15://idle
i <= 'd15; endcase /*****************************/
always @ ( posedge CLK )
if( !RSTn ) clock25M<='b0;
else clock25M <= ~clock25M; /*****************************/ flash_spi U1
(
.flash_clk(flash_clk ),
.flash_cs( flash_cs ),
.flash_datain( flash_datain ),
.flash_dataout( flash_dataout ), .clock25M( clock25M ), //input clock
.flash_rstn( RSTn ), //input reset
.cmd_type( cmd_type ), // flash command type
.Done_Sig( Done_Sig ), //output done signal
.flash_cmd( flash_cmd ), // input flash command
.flash_addr( flash_addr ), // input flash address
.mydata_o( mydata_o ), // output flash data
.myvalid_o( myvalid_o ), // output flash data valid
.spi_state(spi_state) ); wire [:] CONTROL0;
wire [:] TRIG0;
chipscope_icon icon_debug (
.CONTROL0(CONTROL0) // INOUT BUS [35:0]
); chipscope_ila ila_filter_debug (
.CONTROL(CONTROL0), // INOUT BUS [35:0]
// .CLK(dma_clk), // IN
.CLK(CLK), // IN
.TRIG0(TRIG0) // IN BUS [255:0]
//.TRIG_OUT(TRIG_OUT0)
); assign TRIG0[:]=mydata_o;
assign TRIG0[]=myvalid_o;
assign TRIG0[:]=i;
assign TRIG0[:]=spi_state;
assign TRIG0[]=Done_Sig;
assign TRIG0[]=flash_datain;
assign TRIG0[]=flash_dataout;
assign TRIG0[]=flash_cs;
assign TRIG0[]=flash_clk; endmodule
五、测试验证
flash验证如上面读取器件ID还是需要有实际flash才能较好的获得验证结果,下面的验证程序用来作为验证数据读取,人工给予flash_dataout数据.
`timescale 1ns/10ps
module flash_test();
wire flash_clk;
wire flash_cs;
wire flash_datain;
reg flash_dataout;
reg clock25M;
reg RSTn;
reg [:] cmd_type;
wire Done_Sig;
reg [:] flash_cmd;
reg [:] flash_addr;
wire [:] mydata_o;
wire myvalid_o;
wire [:] spi_state; flash_spi U1
(
.flash_clk(flash_clk ),
.flash_cs( flash_cs ),
.flash_datain( flash_datain ),
.flash_dataout( flash_dataout ), .clock25M( clock25M ), //reg clock
.flash_rstn( RSTn ), //reg reset
.cmd_type( cmd_type ), // flash command type
.Done_Sig( Done_Sig ), //reg done signal
.flash_cmd( flash_cmd ), // reg flash command
.flash_addr( flash_addr ), // reg flash address
.mydata_o( mydata_o ), // reg flash data
.myvalid_o( myvalid_o ), // reg flash data valid
.spi_state(spi_state) );
initial clock25M <= 'b0;
always # clock25M <= ~clock25M; initial
begin
RSTn<= 'b0;
flash_addr <= 'd0;
flash_cmd <= 'd0;
cmd_type <= 'b0000;
# RSTn <= 'b1;
# begin flash_cmd <= 'h03; flash_addr <= 24'd0; cmd_type <= 'b1110; end //此处并没有定义flash_dataout数据,可考虑采用随机数产生函数来获取,程序内数据为256位,可适当减小验证 # RSTn<= 'b0; # $stop;
end endmodule
验证结果如下




flash读写学习笔记与spi接口及简单测试验证(三)的更多相关文章
- python学习笔记(threading接口性能压力测试)
又是新的一周 延续上周的进度 关于多进程的学习 今天实践下 初步设计的接口性能压力测试代码如下: #!/usr/bin/env python # -*- coding: utf_8 -*- impor ...
- Typescript 学习笔记六:接口
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Java学习笔记之---比较接口与抽象类
Java学习笔记之---比较接口与抽象类 抽象类是描述事物的本质,接口是描述事物的功能 接口与抽象类的异同 1.一个类只能继承一个父类,但是可以有多个接口 2.抽象类中的抽象方法没有方法体,但是可以有 ...
- 学习笔记:利用GDI+生成简单的验证码图片
学习笔记:利用GDI+生成简单的验证码图片 /// <summary> /// 单击图片时切换图片 /// </summary> /// <param name=&quo ...
- Ext.Net学习笔记21:Ext.Net FormPanel 字段验证(validation)
Ext.Net学习笔记21:Ext.Net FormPanel 字段验证(validation) 作为表单,字段验证当然是不能少的,今天我们来一起看看Ext.Net FormPanel的字段验证功能. ...
- Directx11学习笔记【一】 最简单的windows程序HelloWin
声明:本系列教程代码有部分来自dx11龙书及dx11游戏编程入门两本书,后面不再说明 首先,在vs2013中创建一个空的解决方案Dx11Demo,以后的工程都会放在这个解决方案下面.然后创建一个win ...
- Flutter学习笔记(14)--StatefulWidget简单使用
如需转载,请注明出处:Flutter学习笔记(14)--StatefulWidget简单使用 今天上班没那么忙,突然想起来我好像没StatefulWidget(有状态组件)的demo,闲来无事,写一个 ...
- STM32学习笔记(八) SPI总线(操作外部flash)
1. SPI总线简介 SPI全称串行外设接口,是一种高速,全双工,同步的外设总线:它工作在主从方式,常规需要至少4根线才能够正常工作.SPI作为基本的外设接口,在FLASH,EPPROM和一些数字通讯 ...
- EEPROM读写学习笔记与I2C总线(转)
reference:https://www.cnblogs.com/uiojhi/p/7565232.html 无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在 ...
随机推荐
- redis外网连接的一些坑
前言 在使用阿里云和腾讯云的redis 可以减少很大的维护量.但是在我们的业务场景中遇到了一个情况,阿里和腾讯的redis均不支持外网访问.因此,正好帮人解决一个问题,就拿出来分享一下. 阿呆的故事 ...
- January 20 2017 Week 3 Friday
I am a slow walker, but I never walk backwards. 我走得很慢,但我从来不会后退. In the past years, I walked very slo ...
- [零基础学JAVA]Java SE基础部分-02.标识符、数据类型
转自:http://redking.blog.51cto.com/27212/114976 1.课程名称:标识符.数据类型 本季介绍了Java中的标识符的命名规则,各种关键字及数据类型的划分,并对各种 ...
- bootstrap table footerFormatter用法 统计列求和 sum、average等
其实上一篇blog里已经贴了代码,简单解释一下吧: 1.showFooter: true,很重要,设置footer显示: $(cur_table).bootstrapTable({ url: '/et ...
- Codeforces Round #434 (Div. 2)【A、B、C、D】
Codeforces Round #434 (Div. 2) codeforces 858A. k-rounding[水] 题意:已知n和k,求n的最小倍数x,要求x后缀至少有k个0. 题解:答案就是 ...
- BZOJ5299:[CQOI2018]解锁屏幕(状压DP)
Description 使用过Android手机的同学一定对手势解锁屏幕不陌生.Android的解锁屏幕由3x3个点组成,手指在屏幕上画一条 线将其中一些点连接起来,即可构成一个解锁图案.如下面三个例 ...
- 汇编试验四:[bx] 和 loop 的使用
预备知识: 段前缀的使用: ffff:0~ffff:b 和 0020:0~0020:b 的数据: 一次循环的复制效果: 但是,这种方式DS的数据得修改: Source Code: assume cs: ...
- Qgis里的查询过滤
查询过虑实现方式 通过给getFeatures()传递 QgsFeatureRequest对象,实现数据的过虑,下边是一个查询的例子: request = QgsFeatureRequest() re ...
- 十、IntelliJ IDEA 中 Project 和 Module 的概念及区别
在 IntelliJ IDEA 中,没有类似于 Eclipse 工作空间(Workspace)的概念,而是提出了Project和Module这两个概念.接下来,就让咱们一起看看 IntelliJ ID ...
- 【题解】洛谷P2679 [NOIP2015TG] 子串(DP+滚动数组)
次元传送门:洛谷P2679 思路 蒟蒻一开始并没有思路而去看了题解 我们发现对于两个字串的位置 我们只需要管他们匹配成功或者匹配失败即可 f[i][j][k] 记录当前 a[i]不论等不等于b[j] ...