三大通信协议(3)SPI——基于flash的读操作
目录
一、数据手册分析
二、实例
总结
一、数据手册分析
以黑金的开发板EP4CE6F17C8为核心,外部的flash芯片为M25P16,首先给出M25P16官方手册的时序图。
首先使能信号CS_N拉低,时钟信号上升沿读取MOSI中的数据,下降沿进行数据更新,MOSI先发送一个字节的读指令,然后发送三个字节的地址信号(扇区地址、页地址、字节地址),MISO从该地址中输出存储的数据,紧接着输出下一个地址中的数据,依次输出,直到使能信号CS_N拉低,才停止信号的输出。下图给出读指令。
二、实例
目标:将任意一个程序下载到FPGA的外部flash芯片中,编写Verilog程序,通过FPGA读取flash中的内容,并将读取的内容通过串口发送出来。
下边给出整个系统模块的示意图:
下边首先进行spi_read模块程序的编写。sclk时钟频率取12.5M,由于串口数据发送的速率最大约为115200bite/s,远小于FPGA读取flash中数据的速率,因此,FPGA在读取到数据后,需要在内部进行一个数据的缓存,本实验是通过在FPGA内部申请一个fifo模块,当读取的数据全部写入到fifo中后,再根据串口的发送速率将数据读出。下图给出spi_read模块的时序图。
下图给出spi_read模块的Verilog代码:
module spi_read
(input sys_clk,input sys_res,input key ,input miso ,output reg sclk ,output reg cs_n , output reg mosi ,output reg tx_flag,output [7:0] tx_data
);
parameter IDLE = 6'b000_001;
parameter READ = 6'b000_010;
parameter NUM = 100;//从flash中读取的个数
parameter READ_ORDER = 8'b0000_0011; //读指令
parameter ADDR_SECTOR = 8'b0000_0000; //扇区地址
parameter ADDR_PAGE = 8'b0000_0000; //页地址
parameter ADDR_BYTE = 8'b0000_0100; //字节地址
//读等待parameter UART_BPS = 'd9600; //串口波特率
parameter SYS_CLK = 'd50_000_000; //时钟频率
parameter CNT_BPS = SYS_CLK / UART_BPS; //5208
parameter READ_WAIT_CNT = CNT_BPS*11;//57291串口是9600,每个bite104us,50M的时钟需要计数5208次,
reg [4:0] cnt_clk ;//12.5M的SCLK,八个时钟对应一个字节
reg [2:0] cnt_bite ;
reg [9:0] cnt_byte ;
reg [5:0] stata ;
reg [1:0] cnt_sclk ; //12.5M的SCLKreg miso_flag ;//每从miso接收一个比特位的数据标志位拉高一个时钟周期
reg [7:0] data ;//串并转换
reg po_flag ;
reg po_flag_reg ;
reg [7:0] po_data ;
wire [7:0] fifo_data_num;
reg fifo_read_valid;
reg [15:0] cnt_wait ;
reg fifo_read_en ;
reg [9:0] read_data_num;always@(posedge sys_clk or negedge sys_res)if(!sys_res)cnt_clk <= 5'd0;else if(stata == READ&&cnt_clk==5'd31)cnt_clk <= 5'd0;else if(stata == READ)cnt_clk <= cnt_clk + 1'b1;else cnt_clk <= 5'd0;
always@(posedge sys_clk or negedge sys_res)if(!sys_res)cnt_byte <= 10'd0;else if(stata == READ&&cnt_clk==5'd31)cnt_byte <= cnt_byte + 1'b1;else if(stata == READ)cnt_byte <= cnt_byte;else cnt_byte <= 10'd0;
always@(posedge sys_clk or negedge sys_res)if(!sys_res)stata <= IDLE;else begincase(stata)IDLE:if(key == 1'b1)stata <= READ;READ:if(cnt_byte == NUM + 'd3 && cnt_clk == 'd31)stata <= IDLE;default:;endcase end
always@(posedge sys_clk or negedge sys_res)if(!sys_res)cnt_sclk <= 2'd0;else if(stata == READ && cnt_sclk == 2'd3)cnt_sclk <= 2'd0;else if(stata == READ)cnt_sclk <= cnt_sclk + 1'b1;else cnt_sclk <= 2'd0;
always@(posedge sys_clk or negedge sys_res)if(!sys_res)cnt_bite <= 3'd0;else if(cnt_sclk == 'd2 && cnt_bite == 'd7)cnt_bite <= 3'd0;else if(cnt_sclk == 'd2)cnt_bite <= cnt_bite + 1'b1;else cnt_bite <= cnt_bite;
///输出SCLK、CS_N、MOSI
always@(posedge sys_clk or negedge sys_res)if(!sys_res)cs_n <= 1'b1;else if(key == 1'b1)cs_n <= 1'b0;else if(cnt_byte == NUM + 3 && cnt_clk == 'd31)cs_n <= 1'b1;else cs_n <= cs_n;
always@(posedge sys_clk or negedge sys_res)if(!sys_res)sclk <= 1'b0;else if(cs_n == 1'b0 && cnt_sclk == 'd2)sclk <= 1'b1;else if(cs_n == 1'b0 && cnt_sclk == 'd0)sclk <= 1'b0;else if(cs_n == 1'b0)sclk <= sclk;else sclk <= 1'b0;
always@(posedge sys_clk or negedge sys_res)if(!sys_res) mosi <= 1'b0;else if(stata == READ && cnt_byte == 'd0 && cnt_sclk == 'd0)mosi <= READ_ORDER[7-cnt_bite];else if(stata == READ && cnt_byte == 'd1 && cnt_sclk == 'd0)mosi <= ADDR_SECTOR[7-cnt_bite]; else if(stata == READ && cnt_byte == 'd2 && cnt_sclk == 'd0)mosi <= ADDR_PAGE[7-cnt_bite]; else if(stata == READ && cnt_byte == 'd3 && cnt_sclk == 'd0)mosi <= ADDR_BYTE[7-cnt_bite]; else if(stata == READ && cnt_byte > 'd3)mosi <= 1'b0; else if(stata == IDLE)mosi <= 1'b0;else mosi <= mosi;always@(posedge sys_clk or negedge sys_res)if(!sys_res)miso_flag <= 1'b0;else if(stata == READ && cnt_byte >= 'd4 && cnt_sclk == 'd1)miso_flag <= 1'b1;else miso_flag <= 1'b0;always@(posedge sys_clk or negedge sys_res)if(!sys_res)data <= 8'd0;else if(miso_flag == 1'b1)data <= {data[6:0],miso};
always@(posedge sys_clk or negedge sys_res)if(!sys_res)po_flag <= 1'b0;else if(stata == READ && cnt_byte >= 'd4 && (cnt_bite == 'd7&&cnt_sclk == 'd2))po_flag <= 1'b1;else po_flag <= 1'b0;
always@(posedge sys_clk or negedge sys_res)if(!sys_res)po_data <= 8'd0;else if(po_flag == 1'b1)po_data <= data;elsepo_data <= po_data;always@(posedge sys_clk or negedge sys_res)if(!sys_res)po_flag_reg <= 1'b0;else po_flag_reg <= po_flag;always@(posedge sys_clk or negedge sys_res)if(!sys_res)fifo_read_valid <= 1'b0;else if(cnt_wait == READ_WAIT_CNT-1 && read_data_num == NUM)fifo_read_valid <= 1'b0;else if(fifo_data_num == NUM)fifo_read_valid <= 1'b1;else fifo_read_valid <= fifo_read_valid;always@(posedge sys_clk or negedge sys_res)if(!sys_res)cnt_wait <= 16'd0;else if(fifo_read_valid == 1'b1 && cnt_wait == READ_WAIT_CNT-1)cnt_wait <= 16'd0;else if(fifo_read_valid == 1'b1 )cnt_wait <= cnt_wait + 1'b1;else cnt_wait <= 16'd0;always@(posedge sys_clk or negedge sys_res)if(!sys_res)fifo_read_en <= 1'b0;else if(cnt_wait == READ_WAIT_CNT-1)fifo_read_en <= 1'b1;else fifo_read_en <= 1'b0;always@(posedge sys_clk or negedge sys_res)if(!sys_res)read_data_num <= 'd0;else if(fifo_read_valid == 1'b1 && fifo_read_en == 1'b1)read_data_num <= read_data_num + 1'b1;else if(fifo_read_valid == 1'b1)read_data_num <= read_data_num;else read_data_num <= 'd0;always@(posedge sys_clk or negedge sys_res)if(!sys_res) tx_flag <= 1'b0;else tx_flag <= fifo_read_en;//将miso输入的数写进FIFO中
fifo_data fifo_data_inst(.clock (sys_clk ), //时钟信号.data (po_data ), //写数据,8bit.wrreq (po_flag_reg ), //写请求.rdreq (fifo_read_en ), //读请求.q (tx_data ), //数据读出,8bit.usedw (fifo_data_num) //fifo内数据个数
);endmodule
对上述spi_read模块代码进行仿真验证,编写的测试文件如下:
`timescale 1ns/1nsmodule tb_spi_flash_read();
reg clk ;
reg res ;
reg key ;
wire cs_n ;
wire sck ;
wire mosi ;
wire miso ;
wire tx_falg ;
wire [7:0]tx_data ;//时钟、复位信号、模拟按键信号
initialbeginclk <= 0;res <= 0;key <= 0;#100res <= 1;#1000key <= 1;#20key <= 0;endalways #10 clk <= ~clk;defparam memory.mem_access.initfile = "initM25P16_test.txt";
//defparam spi_flash_read_inst.flash_read_ctrl_inst.CNT_WAIT_MAX = 1000;
//defparam spi_flash_read_inst.uart_tx_inst.CLK_FREQ = 100000;//------------- spi_flash_read -------------
spi_read spi_flash_read_inst(.sys_clk (clk ), .sys_res (res ), .key (key ), .miso (miso ),.sclk (sck ), .cs_n (cs_n ), .mosi (mosi ), .tx_flag (tx_flag),.tx_data (tx_data)
);//------------- memory -------------
m25p16 memory (.c (sck ), .data_in (mosi ), .s (cs_n ), .w (1'b1 ), .hold (1'b1 ), .data_out (miso )
);endmodule
上述tb_spi_flash_read测试模块中例化的m25p16模块是M25P16芯片的仿真模型,其中包含M25P16.v、memory_access.v、internal_logic.v、acdc_check.v、parameter.v、M25P16_driver.v等.v文件以及相关的.txt文件。当然,由于spi_read模块调用了FIFOip核,因此在Modelsim仿真时还需要加入fifo_data.v文件和系统库文件altera_mf.v文件。(相关的文件我会打包上传到文章末尾的评论处)
在仿真的过程需要注意,上述所有的.v文件以及相应的.txt文件都应该放在建立工程的路径下,否则会提示找不到相关的模块,导致编译出错。
下列图1、图2、图3给出仿真结果:图1红色框中表示输入flash中的四个字节数,在时钟sclk上升沿读取数据(分别是读指令0000_0011、扇区地址0000_0000、页地址0000_0000、字节地址0000_0100),图2从miso信号中可以看出从flash读出的数据,图4给出了flash中存储的数据,可
以看出,在扇区地址为全0,页地址为全0,字节地址为全0时,对应第一个数“0”,因此,在扇区地址全0,页地址全0,字节地址为0000_0100时,读出的数据应该是04,图2中的po_data,在红色框中是4,与理论读取的数据一致,更加说明了实验的准确性。由于程序中给出的参数,一次只读取100个数据,因此图3中po_data读取到的最后一个数为10。
图1
图2
图3
图4
总结
下一篇文章继续将上述实例进行上板测试,验证程序的准确性。
初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!
三大通信协议(3)SPI——基于flash的读操作相关推荐
- nand flash的读操作详解
这篇文章不是介绍 nandflash的物理结构和关于nandflash的一些基本知识的.你需要至少了解 你手上的 nand flash的物理结构和一些诸如读写命令 操作的大概印象,你至少也需要看过 s ...
- NAND FLASH的读操作及原理
硬件原理 上面是我使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义,下面直接引用韦东山老师的课程中的提问: NAND FLASH是一个存储芯片 那么: 这样的操作很合理&q ...
- NAND FLASH的读写操作(硬件原理图分析)
转载:NAND FLASH的读操作及原理 硬件原理 上面是我使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义,下面直接引用韦东山老师的课程中的提问: NAND FLASH是 ...
- 基于SPI协议的Flash驱动控制-数据普通读操作
目录 Flash数据普通读操作 实现原理 verilog设计代码 verilog测试代码 Flash数据普通读操作 实现原理 将片选信号拉低,写入读操作指令,最少读取一个字节的数据,写入读指令后要写入 ...
- 三大通信协议(3)SPI——寄存器配置
目录 一.SPI通信协议简介 二.SPI通信时序 1.主从通信 2.模式选择 三.实例 总结 一.SPI通信协议简介 SPI是串行外设接口(Serial Peripheral Interface)的缩 ...
- stm32 SPI、FLASH
main.c FLASH:掉电后数据不丢失,U 盘.SD 卡.SSD 固态硬盘.STM32 芯片内部用于存储程序的设备,都是 FLASH 类型的存储器.FLASH芯片(W25Q64)是一种使用 SPI ...
- STM32CUBEIDE(15)----移植兆易创新SPI Nor Flash之GD25Q64Flash
spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的 ...
- STM32CUBEMX开发GD32F303(16)----移植兆易创新SPI Nor Flash之GD25Q64Flash
spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的 ...
- GD32F303固件库开发(16)----移植兆易创新SPI Nor Flash之GD25Q64Flash
spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的 ...
最新文章
- .NET架构与模式探索
- 职校电子计算机专业高考分数线,2015年重庆高考分数线公布:一本文572理573
- c++ 读文件_C语言处理文件基础知识:文件、流和键盘输入
- 用python画四叶草代码-python turtle工具绘制四叶草的实例分享
- eclipse连接Mysql和测试
- SpiderMonkey
- jzoj3461-小麦亩产一千八【斐波那契数列】
- Log4j Bug –减慢您的应用程序
- Sudoku Killer(HDU-1426)
- 使用ROW_NUMBER()查询:列名 'RowNumber' 无效。
- Android 百度地图开发详解
- 神经动力学模型的建立
- 《计算机网络(第七版)谢希仁 编著》部分课后答案
- Python自学教程3-英语不好,变量怎么命名
- 一款简单好用的开源简繁转换类库
- HTML输入密码函数,介绍一个输入密码用的InputBox函数
- GOM传奇文件目录功能说明
- pyaudio:基于pyaudio利用Python编程从电脑端录制音频保存到指定文件夹+将录音读取出来
- kettle 在Linux下执行kjb
- CLIENT_ACKNOWLEDGE机制测试
热门文章
- printk打印级别输出
- mac连接mysql出现 Access denied for user ‘Lampard‘@‘localhost‘ (using password: NO)
- OSGi-Equinox
- 字符串常量断行处理(加号处理)
- 【设计模式】Java设计模式 - 适配器模式
- 请自己写出strcpy函数
- bim常用出图软件的【标注避让】【洞口标注】功能
- 视频、图片一键卡通化的开源工具!
- 第八章shell脚本应用(三)
- MySQL双主模式(2022/11/19)