FPGA实现基于SPI协议的Flash驱动控制(全擦除、页擦除、读数据、页写、连续写—地址写)

这篇具有很好参考价值的文章主要介绍了FPGA实现基于SPI协议的Flash驱动控制(全擦除、页擦除、读数据、页写、连续写—地址写)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要

本论文使用Verilog HDL硬件描述语言,结合野火可以FPGA征途Pro开发板,实现了SPI通信协议的全擦除,扇区擦除,读数据,页写,连续写的驱动设计。在 Altera Cyclone Ⅳ 芯片上采用“自顶向下”的模块化设计思想及 Verilog HDL 硬件描述语言,设计并实现串行外设接口(SPI)。在 Quartus II 13.0 软件开发平台上编译、仿真后下载到 FPGA 芯片上,进行在线编程调试,实现了 SPI 总线通信功能。基于 FPGA 的系统设计调试维护方便、可靠性高,而且设计具有灵活性,可以方便地进行扩展和移植。
关键词:SPI;串口通信;FPGA;Verilog HDL

1 绪论

1.1研究背景

串行外设接口 (Serial peripheral interface, SPI) 是由Motorola 公司推出的一种同步串行外围设备接口。SPI总线是一种高速、同步、全双工的串行通信总线。SPI 总线接口只有四根外部接口线,结构简单、速度快、可靠性强。典型的 SPI 接口通信由四根信号线实现,分别为:
SCS:从片选信号,逻辑 “0” 为有效状态,表示被选中与主设进行数据传输。
SCLK:串行时钟线,由主设输出,从设按照此时钟进行数据的同步传输。
MOSI:数据传输线,由主设到从设。
MISO:数据传输线,由从设到主设。[1]

1.2 研究目的和意义

SPI 接口是一种全双工、三线通信的系统,是常用的工业标准同步串行接口,它允许主机处理器与各种外围设备之间的通信方式是串行通信。在 SPI 接口中,主/从机之间数据的传输需要 1 个时钟信号和 2 条数据线,所以 SPI 总线区分主机(Master)和从机(Slave)2 部分,结构框图如图 1 所示。
主机和从机之间 SPI 总线由 4 根线构成:①SCK。串行同步时钟信号,用来同步主机和从机的数据传输,
由主机控制输出,从机在 SCK 的边沿接收或发送数据。②MOSI。主机输出/从机输入线,主机在上升沿(或下
降沿)通过该信号线发送数据给从机,从机在下降沿(或上升沿)通过该信号线接收该数据。③MISO。主
机输入/从机输出线,从机在上升沿(或下降沿)通过该信号线发送数据给主机,主机在下降沿(或上升沿)
通过该信号线接收该数据。④SS。从机片选信号线,它同样是由主机控制输出。[2]

1.2.1 SPI通信协议物理层简介

fpga spiflash,fpga开发
(图1-2-1-1:一主一从SPI通讯设备连接图)
(Figure 1-2-1: One master and one slave SPI communication device connection diagram)
fpga spiflash,fpga开发
(图1-2-1:一主多从SPI通讯设备连接图)
(Figure 1-2-1-2: One master and multiple slave SPI communication device connection diagram)
SPI通信协议采用的是主从通信模式,通信双方有主从之分,根据从机的设备个数,SPI通信设备之间的连接方式可以分为一主一从和一主多从。

SPI通信协议包含1条时钟信号线、2条数据总线和1条片选信号线,时钟信号线为SCK、2条数据总线为MOSI和MISO,片选信号线为CS。它们的作用介绍如下:
1.SCK(Serial Clock) : 时钟信号线,用于同步通信数据。由通信主机产生,决定了通信的速率,不同的设备支持的最高时钟频率不同,两个设备通信时通信速率受限于低速设备。
2.MOSI(MasterOutput ,Slave Input) : 主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机从这条信号线读入主机发送的数据,数据方向由主机到从机。
3.MISO(Master Input , Slave Output) : 主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输入到主机,数据方向由从机到主机。
。CS(Chip Select) : 片选信号线,也称为CS_N。当有多个从设备与主机相连时,设备的其他数据信号线SCK、MOSI、MISO同时并联到相同的SPI总线上,即无论有多少从设备都使用这三条总线,每个从设备都有一条独立的CS_N信号线,本数据线独占主机的一个引脚,既有多少个从设备就有多少个片选信号线。I2C协议中通过设备地址来寻址,选中总线上的某个设备并与其进行通信;而SPI协议中没有设备地址,它使用CS_N信号线来寻址,当主机要选择从设备时,把该设备的CS_N信号线设置为低电平,该设备即被选中,即片选信号有效,接着主机开始与被选中的从设备进行SPI通信。所以SPI通信以CS_N信号线置为低电平为开始信号,以CS_N信号线拉高为结束信号。

1.2.2 SPI 协议层

SPI通信协议一共有4种通信模式:模式0、模式1、模式2、模式3.这四种模式分由时钟极性(CPLD,Clock Polarity)和时钟相位(CPHA,ClockPhase)来定义,其中CPOL参数规定了空闲状态(CS_N为高电平,设备未被选中时SCK时钟信号的电平状态,CPHA规定了数据采样是在SCK时钟奇数边沿还是偶数边沿。

1.2.3 设计SPI总线接口要求

设计的 SPI 总线接口完成工作有:①将主机收到的 16 位并行数据转换为串行数据,并发送给从机;
②接收来自从机的串行数据,将其转换为并行数据,通过并行端口输出;③输出从机所需要的输入信号、
时钟信号 SCK 和片选信号 SS
fpga spiflash,fpga开发
fpga spiflash,fpga开发

1.3 国内外研究现状(SPI接口的发展)

SPI 接口最早是由美国的Motorola 公司所定义的,它的中文名称叫做串行外设接口,用于 Motorola 公司自己研发的产品之中。SPI 接口可以作为通信的桥梁,连接 CPU 等控制设备和外围设备。SPI 接口能够以串行、同步、高速的特点来传输通信数据,具有简单易用、节省面积等优点,因此具有越来越广泛的用途,并且逐渐应用到其他场景,比如 SD 卡、液晶显示屏、射频通信卡等。传统的 SPI 接口可以连接控制设备和外围设备进行全双工通信,随着通信场景的需求,会引入设计半双工、单工等通信方式,会根据功能设计更多的寄存器进行控制。在规模一般的芯片设计中,可能仅简单设计 SPI 接口、满足基本通信需求即可,但是在芯片性能不断发展的今天,在设计 SPI 接口时需要考虑到 SPI 能否与其他各 IP 进行高效的通信与交互,能否在芯片 IP 复杂化的同时可以尽量复用之前的设计,能否适应芯片设计中新的功能点和需求点。SPI 接口设计好后,会进行功能点的验证。如果以传统的 Verilog 语言验证SPI,需要编写大量激励文件,以众多定向验证覆盖到功能点。如果以单纯的System Verilog 语言验证SPI,可以实现随机化的激励输入,达到高效的验证,但是验证平台环境不容易得到复用,在项目迭代中具有一定的麻烦。所以需要寻求既可以高效验证 SPI 功能点,又可以高效搭建实现验证平台环境的方式。[3]

1.4 研究内容

1.5 研究方法及技术路线

2 设计SPI-FLASH全擦除实验模块

2.1 设计SPI总线接口要求

2.2 设计SPI接口模块结构

fpga spiflash,fpga开发

2.3 SPI接口的子模块设计

2.3.1 通讯模块

2.3.2 控制模块

fpga spiflash,fpga开发

2.3.3 FIFO模块

2.3.4 数据收发模块

2.4 设计过程波形图绘制及各信号产生原因解析

fpga spiflash,fpga开发
fpga spiflash,fpga开发
fpga spiflash,fpga开发

fpga spiflash,fpga开发

2.5 设计的仿真、综合与实现

module  key_filter
#(
    parameter   CNT_20MS_MAX    =   20'd999_999
)
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire            key_in      ,
    
    output  reg             key_flag
);
reg [19:0]  cnt_20ms;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'd0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'd0;
    else    if(cnt_20ms == CNT_20MS_MAX && key_in == 1'b0)
        cnt_20ms <= CNT_20MS_MAX;
    else    if(key_in == 1'b0)
        cnt_20ms <= cnt_20ms + 20'd1;
    else
        cnt_20ms <= cnt_20ms;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_20MS_MAX - 20'd1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

fpga spiflash,fpga开发
fpga spiflash,fpga开发

module  flash_be_ctrl
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire            key_flag    ,
    
    output  reg             cs_n        ,
    output  reg             sck         ,
    output  reg             mosi
);

parameter   CNT_CLK_MAX     =   5'd31;
parameter   CNT_BYTE_MAX    =   3'd6;
parameter   CNT_SCK_MAX     =   2'd3;
parameter   CNT_BIT_MAX     =   3'd7;

parameter   IDLE    =   4'b0001,
            WREN    =   4'b0010,
            DELAY   =   4'b0100,
            BE      =   4'b1000;
            
parameter   WREN_IN =   8'b0000_0110;
parameter   BE_IN   =   8'b1100_0111;

reg     [3:0]   state;
reg     [4:0]   cnt_clk;
reg     [2:0]   cnt_byte;
reg     [1:0]   cnt_sck;
reg     [2:0]   cnt_bit;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE; 
    else    
        case(state)
            IDLE:
                if(key_flag == 1'b1)
                    state <= WREN;
                else
                    state <= IDLE;
            WREN:
                if(cnt_clk == CNT_CLK_MAX && cnt_byte == 3'd2)
                    state <= DELAY;
                else
                    state <=WREN;
            DELAY:
                if(cnt_clk == CNT_CLK_MAX && cnt_byte == 3'd3)
                    state <= BE;
                else
                    state <=DELAY;
            BE  :
                if(cnt_clk == CNT_CLK_MAX && cnt_byte == CNT_BYTE_MAX)
                    state <= IDLE;
                else
                    state <=BE;
            default:state <= IDLE;
        endcase
            
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <= 5'd0;
    else    
        case(state)
            IDLE :cnt_clk <= 5'd0;
            WREN :cnt_clk <= cnt_clk + 5'd1;//溢出清零
            DELAY:cnt_clk <= cnt_clk + 5'd1;//溢出清零
            BE   :cnt_clk <= cnt_clk + 5'd1;//溢出清零
            default:cnt_clk <= cnt_clk;
        endcase

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte <= 3'd0;
    else    if(cnt_clk == CNT_CLK_MAX && cnt_byte == CNT_BYTE_MAX)
        cnt_byte <= 3'd0;
    else    if(cnt_clk == CNT_CLK_MAX)
        cnt_byte <= cnt_byte + 3'd1;
    else
        cnt_byte <= cnt_byte;
        
always@(posedge sys_clk or negedge sys_rst_n)//不同
    if(sys_rst_n == 1'b0)
        cnt_sck <= 2'd0;
    else    if((cnt_byte == 3'd1 || cnt_byte == 3'd5) && (cnt_sck == CNT_SCK_MAX))
        cnt_sck <= 2'd0;
    else    if(cnt_byte == 3'd1 || cnt_byte == 3'd5)
        cnt_sck <= cnt_sck + 2'd1;
    else
        cnt_sck <= 2'd0;
        
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <= 3'd0;
    else    if(cnt_sck == 2'd2 && cnt_bit == CNT_BIT_MAX)
        cnt_bit <= 3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <= cnt_bit + 3'd1;
    else
        cnt_bit <= cnt_bit;
        
/* always@(*)  
    case(state)
        IDLE :cs_n <= 1'b1;
        WREN :cs_n <= 1'b0;
        DELAY:cs_n <= 1'b1;
        BE   :cs_n <= 1'b0;
        default:cs_n <= 1'b1;
    endcase */
    
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n <= 1'b1;
    else    if(key_flag == 1'b1)
        cs_n <= 1'b0;
    else    if(state == WREN && cnt_clk == CNT_CLK_MAX && cnt_byte == 3'd2)
        cs_n <= 1'b1;
    else    if(state == DELAY && cnt_clk == CNT_CLK_MAX && cnt_byte == 3'd3)
        cs_n <= 1'b0;
    else    if(state == BE    && cnt_clk == CNT_CLK_MAX && cnt_byte == 3'd6)
        cs_n <= 1'b1;
    else
        cs_n <= cs_n;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <= 1'b0;
    else    if(cnt_sck == 2'd0)
        sck <= 1'b0;
    else    if(cnt_sck == 2'd2)
        sck <= 1'b1;
    else    
        sck <= sck;
        
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi <= 1'b0;
    else    if(state == WREN && cnt_byte == 3'd2)
        mosi <= 1'b0;
    else    if(state == BE && cnt_byte == 3'd6)
        mosi <= 1'b0;
    else    if(state == WREN && cnt_byte == 3'd1 && cnt_sck == 2'd0)
        mosi <= WREN_IN[7 - cnt_bit];
    else    if(state == BE && cnt_byte == 3'd5 && cnt_sck == 2'd0)
        mosi <= BE_IN[7 - cnt_bit];
    else
        mosi <= mosi;
        
endmodule
module  spi_flash_be
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire            key_in      ,
    
    output  wire            cs_n        ,
    output  wire            sck         ,
    output  wire            mosi        
);

wire            key_flag;

key_filter
#(
    .CNT_20MS_MAX(20'd999_999)
)
key_filter_inst
(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .key_in      (key_in   ),

    .key_flag    (key_flag )
);

flash_be_ctrl   flash_be_ctrl_inst
(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .key_flag    (key_flag ),

    .cs_n        (cs_n     ),
    .sck         (sck      ),
    .mosi        (mosi     )
);

endmodule
`timescale  1ns/1ns 
module  tb_flash_be_ctrl();

reg             sys_clk     ;
reg             sys_rst_n   ;
reg             key_flag    ;
                            
wire            cs_n        ;
wire            sck         ;
wire            mosi        ;

initial
    begin
        sys_clk <= 1'b1;
        sys_rst_n <= 1'b0;
        key_flag <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
        #200
        key_flag <= 1'b1;
        #20
        key_flag <= 1'b0;
    end
    
always  #10 sys_clk <= ~sys_clk;

defparam memory.mem_access.initfile = "initmemory.txt";

flash_be_ctrl   flash_be_ctrl_inst
(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .key_flag    (key_flag ),

    .cs_n        (cs_n     ),
    .sck         (sck      ),
    .mosi        (mosi     )
);

m25p16 memory 
(
    .c          (sck  ), 
    .data_in    (mosi ), 
    .s          (cs_n ), 
    .w          (1'b1 ), 
    .hold       (1'b1 ), 
    .data_out   (     )
); 

endmodule

2.5 模块小结

参考文献

[1]蒋国庆,顾军.基于FPGA的LPC总线转多路SPI总线设计[J].电子质量,2022(10):39-45.
[2]杨梓鹤,彭秋雨,李湛艺,程晓迪.SPI接口仿真设计与实现[J].科技与创新,2022(19):121-123+126.DOI:10.15913/j.cnki.kjycx.2022.19.038.
[3]王大为. 基于UVM的SPI接口IP核的设计与验证[D].北方工业学,2022.DOI:10.26926/d.cnki.gbfgu.2022.000608.文章来源地址https://www.toymoban.com/news/detail-659271.html

到了这里,关于FPGA实现基于SPI协议的Flash驱动控制(全擦除、页擦除、读数据、页写、连续写—地址写)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【接口协议】FPGA实现SPI协议基于ADC128S022进行模拟信号采集

    使用vivado联合modelsim实现SPI协议基于ADC128S022进行模拟信号连续采集。 SPI是串行外设接口,是一种同步/全双工/主从式接口。通常由四根信号线构成: CS_N :片选信号,主从式接口,可以有多个从机,用片选信号进行从机选择; SCLK :串行时钟线,由主机提供给从机; MISO :主机

    2024年02月07日
    浏览(53)
  • FPGA实现SPI协议基于ADC128S022进行模拟信号采集

    使用vivado联合modelsim实现SPI协议基于ADC128S022进行模拟信号连续采集。 SPI是串行外设接口,是一种同步/全双工/主从式接口。通常由四根信号线构成: CS_N :片选信号,主从式接口,可以有多个从机,用片选信号进行从机选择; SCLK :串行时钟线,由主机提供给从机; MISO :主机

    2024年02月14日
    浏览(46)
  • stm32基于HAL库驱动外部SPI flash制作虚拟U盘

    📌参考文章: https://xiaozhuanlan.com/topic/6058234791 🎞实现效果演示: 🔖上图中的读到的 FLASH_ID 所指的是针对不同容量,所对应的ID。 🔖在电脑端,支持对虚拟出来的存储器进行读写操作。 ✨如果设计成一块PCB,可以制作成一个微小容量的移动U盘。 🌿基于STM32F103,HAL库生成

    2024年02月11日
    浏览(48)
  • 【最通用版FPGA 实现 SPI 驱动】

    最近研究了一下SPI协议的FPGA实现,发现网上很多大佬分享的方法都是针对某一特定的flash芯片或者某一传感器芯片来设计电路结构的。所以想根据SPI(Serial Peripheral Interface)的基本通讯协议实现一个通用版的SPI Master驱动。SPI在嵌入式领域是一个很成熟且应用非常广泛的通信协

    2024年02月05日
    浏览(46)
  • 【FPGA】SPI读写flash

    SPI是同步全双工通信,通信原理以主从方式工作,通常有一个主设备和一个或多个从设备,需要4根线连接:MISO(主设备数据输入)、MOSI(主设备输出)、SCLK(时钟)、CS(片选)。通常拉低对应从机的片选来收发数据。 MISO:主设备输入,从设备输出 MOSI:主设备输出,从设备输入 SCLK:时

    2024年02月05日
    浏览(43)
  • 【FPGA】SPI读写FLASH闪存

    通信原理 SPI也是以主从方式工作,通常需要四根线来完成数据的传输,分别是MISO MOSI CS SCLK。以下是这四根线代表的含义: MISO:主设备输入,从设备输出 MOSI:主设备输出,从设备输入 CS :片选信号,选择进行通信的从设备 SCLK:时钟线,由主设备产生给到从设备 SPI通信的

    2024年02月16日
    浏览(40)
  • 【LabVIEW FPGA入门】LabVIEW FPGA 实现SPI通信协议

            该实现由两个组件组成:在 LabVIEW FPGA 中实现的 SPI 协议以及用于从主机 PC 或实时控制器与 FPGA 进行通信的 LabVIEW 主机接口。该架构允许从单个主机程序控制多个 SPI 端口,同时仍然允许定制 FPGA VI 以进行其他数据采集和处理。该实现不使用任何DMA(直接内存访问

    2024年01月17日
    浏览(58)
  • STM32F103单片机通过SPI全双工通信协议与W25Q64(FLASH)进行通信【串行同步通信(数据线与时钟线配合),(一主多从模式)】附相关驱动代码详解

    1.W25Qxx系列是一种低成本、小型化、使用简单的 非易失性存储器 ,常应用于数据存储、字库存储、固件程序存储等场景 2.存储介质: Nor Flash(闪存) 3.时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI) 4.存储容量(24位地址): W25Q40: 4Mbit / 512KByte W25Q80: 8Mbit / 1MByte W25Q16: 16

    2024年04月13日
    浏览(60)
  • 【FPGA】SPI-FLASH-M25P16手册解读

    M25P16概述: M25P16是一款带有先进写保护机制和高速SPI总线访问的串行Flash存储器。M25P16特点如下: 存储结构:16M Bit(2M Byte)的存储空间,一共32个扇区(sector),每个扇区256页,每页256字节,每个字节的的存储地址由扇区地址(8bit)+页地址(8bit)+字节地址(8bit)构成。 SP

    2024年02月04日
    浏览(40)
  • 【全志V3s】SPI NAND Flash 驱动开发

    芯片:W25N01GVZEIG datasheet上的描述: SLC工艺 2KB*65536页 10万次擦写次数 焊好以后用CH341A读了一下 上面这张图描述了soc启动的四个顺序,分别是usb启动、sd卡启动、spi norflash启动、spi nandflash启动四种方式。前面的测试中一直走的是sd卡启动,但是从成本和稳定性上说,spi nandfla

    2024年02月07日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包