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 无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在 ...
随机推荐
- C++ int与string的相互转换(含源码实现)
一.int转换成string Ⅰ.to_string函数 c++11标准增加了全局函数std::to_string: string to_string (int val); string to_str ...
- centos无法用password登录,只能用public key的解决办法
如上图只能用public key登录,只是我在17年申请的阿里云免费体验半年的服务器上遇到的, 那么我们用阿里云后台的远程连接按钮进入服务器,进入后 就看/etc/ssh/sshd_config文件配 ...
- 【深入理解JAVA虚拟机】第一部分.走进Java
Java技术体系 如果仅从传统意义上来看,Sun官方所定义的Java技术体系包括以下几个组成部分:Java程序设计语言各种硬件平台上的Java虚拟机Class文件格式Java API类库来自商业机构和 ...
- easyui学习笔记5—panel加载其他的页面
上一篇中我们看到了panel的基本实现,没有什么难度,最重要的是data-options和class两个标签属性的定义.这里我们将看一下在panel中如何加载其他的页面. 1.先看看引用的资源文件和h ...
- ZT 为什么pthread_cond_t要和pthread_mutex_t同时使用 || pthread/Linux多线程编程
为什么线程同步的时候pthread_cond_t要和pthread_mutex_t同时使用 (2009-10-27 11:07:23) 转载▼ 标签: 杂谈 分类: 计算机 举一个例子(http:// ...
- monodevelop 基础用法
1.mono快捷键 CTRL+K 删除光标所在行的该行后面的代码 CTRL + ALT +C 注释/不注释该行 CTRL+ DOWN 像鼠标滚轮一样向下拖 CTRL + UP 像鼠标滚 ...
- EOJ-3300 奇数统计(高维前缀和)
题目链接: https://acm.ecnu.edu.cn/problem/3300/ 题目大意: 给n个数,求在n个数中选两个数(可重复),使得这两个数的组合数是奇数,求总共有多少种取法. 解题思路 ...
- 问题解决:java.sql.SQLException:Value '0000-00-00' can not be represented as java.sql.Date
问题描述: 数据表中有记录的time字段(属性为timestamp)其值为:“0000-00-00 00:00:00” 程序使用select 语句从中取数据时出现以下异常: Java.sql.SQLE ...
- MyBatis框架(6)动态sql
本次全部学习内容:MyBatisLearning 什么是动态sql: mybatis的核心,对sql进行灵活的操作,通过对表达式的判断,对sql灵活的拼接 在之前小案例的基础上我们先进行简 ...
- Selenium应用代码(读取mysql表数据登录)
1. 封装链接数据库的类: import java.sql.ResultSet; import java.sql.Connection; import java.sql.DriverManager; ...