目录

一、数据手册分析

二、实例

总结


一、数据手册分析 

       以黑金的开发板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的读操作相关推荐

  1. nand flash的读操作详解

    这篇文章不是介绍 nandflash的物理结构和关于nandflash的一些基本知识的.你需要至少了解 你手上的 nand flash的物理结构和一些诸如读写命令 操作的大概印象,你至少也需要看过 s ...

  2. NAND FLASH的读操作及原理

    硬件原理 上面是我使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义,下面直接引用韦东山老师的课程中的提问: NAND FLASH是一个存储芯片 那么: 这样的操作很合理&q ...

  3. NAND FLASH的读写操作(硬件原理图分析)

    转载:NAND FLASH的读操作及原理 硬件原理 上面是我使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义,下面直接引用韦东山老师的课程中的提问: NAND FLASH是 ...

  4. 基于SPI协议的Flash驱动控制-数据普通读操作

    目录 Flash数据普通读操作 实现原理 verilog设计代码 verilog测试代码 Flash数据普通读操作 实现原理 将片选信号拉低,写入读操作指令,最少读取一个字节的数据,写入读指令后要写入 ...

  5. 三大通信协议(3)SPI——寄存器配置

    目录 一.SPI通信协议简介 二.SPI通信时序 1.主从通信 2.模式选择 三.实例 总结 一.SPI通信协议简介 SPI是串行外设接口(Serial Peripheral Interface)的缩 ...

  6. stm32 SPI、FLASH

    main.c FLASH:掉电后数据不丢失,U 盘.SD 卡.SSD 固态硬盘.STM32 芯片内部用于存储程序的设备,都是 FLASH 类型的存储器.FLASH芯片(W25Q64)是一种使用 SPI ...

  7. STM32CUBEIDE(15)----移植兆易创新SPI Nor Flash之GD25Q64Flash

    spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的 ...

  8. STM32CUBEMX开发GD32F303(16)----移植兆易创新SPI Nor Flash之GD25Q64Flash

    spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的 ...

  9. GD32F303固件库开发(16)----移植兆易创新SPI Nor Flash之GD25Q64Flash

    spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的 ...

最新文章

  1. .NET架构与模式探索
  2. 职校电子计算机专业高考分数线,2015年重庆高考分数线公布:一本文572理573
  3. c++ 读文件_C语言处理文件基础知识:文件、流和键盘输入
  4. 用python画四叶草代码-python turtle工具绘制四叶草的实例分享
  5. eclipse连接Mysql和测试
  6. SpiderMonkey
  7. jzoj3461-小麦亩产一千八【斐波那契数列】
  8. Log4j Bug –减慢您的应用程序
  9. Sudoku Killer(HDU-1426)
  10. 使用ROW_NUMBER()查询:列名 'RowNumber' 无效。
  11. Android 百度地图开发详解
  12. 神经动力学模型的建立
  13. 《计算机网络(第七版)谢希仁 编著》部分课后答案
  14. Python自学教程3-英语不好,变量怎么命名
  15. 一款简单好用的开源简繁转换类库
  16. HTML输入密码函数,介绍一个输入密码用的InputBox函数
  17. GOM传奇文件目录功能说明
  18. pyaudio:基于pyaudio利用Python编程从电脑端录制音频保存到指定文件夹+将录音读取出来
  19. kettle 在Linux下执行kjb
  20. CLIENT_ACKNOWLEDGE机制测试

热门文章

  1. printk打印级别输出
  2. mac连接mysql出现 Access denied for user ‘Lampard‘@‘localhost‘ (using password: NO)
  3. OSGi-Equinox
  4. 字符串常量断行处理(加号处理)
  5. 【设计模式】Java设计模式 - 适配器模式
  6. 请自己写出strcpy函数
  7. bim常用出图软件的【标注避让】【洞口标注】功能
  8. 视频、图片一键卡通化的开源工具!
  9. 第八章shell脚本应用(三)
  10. MySQL双主模式(2022/11/19)