10_SPI_Flash 连续写实验

10_SPI_Flash 连续写实验

  • 1. 实验目标
  • 2. 连续写方法
  • 3. 操作时序
  • 4. 流程框图
    • 4.1 顶层模块
    • 4.2 连续写模块
  • 5. 波形图
  • 6. RTL
    • 6.1 flash_seq_wr_ctrl
    • 6.2 spi_flash_seq_wr
  • 7. Testbench

1. 实验目标

使用页写指令,将串口发送过来的连续不定量数据写入 Flash。本实验中,我们发送数据为 100 字节,串口波特率位 9600。
注意:在向 Flash 芯片写入数据之前,先要对芯片执行全擦除操作。

2. 连续写方法

在这里插入图片描述
在这里插入图片描述

3. 操作时序

和页写操作的操作时序一样。

4. 流程框图

4.1 顶层模块

在这里插入图片描述

4.2 连续写模块

在这里插入图片描述

在这里插入图片描述

5. 波形图

在这里插入图片描述

6. RTL

6.1 flash_seq_wr_ctrl

`timescale  1ns/1ns
module  flash_seq_wr_ctrl(input   wire            sys_clk     ,   //系统时钟,频率50MHzinput   wire            sys_rst_n   ,   //复位信号,低电平有效input   wire            pi_flag     ,   //数据标志信号input   wire    [7:0]   pi_data     ,   //写入数据output  reg             sck         ,   //串行时钟output  reg             cs_n        ,   //片选信号output  reg             mosi            //主输出从输入数据);//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态WR_EN   =   4'b0010 ,   //写状态DELAY   =   4'b0100 ,   //等待状态PP      =   4'b1000 ;   //扇区擦除状态
parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令PP_INST     =   8'b0000_0010;   //扇区擦除指令
parameter   ADDR        =   24'h00_04_25;   //数据写入地址//reg   define
reg     [23:0]  addr_reg;   //数据写入地址寄存器
reg     [23:0]  addr    ;   //数据写入地址reg     [4:0]   cnt_clk ;   //系统时钟计数器
reg     [3:0]   state   ;   //状态机状态
reg     [3:0]   cnt_byte;   //字节计数器
reg     [1:0]   cnt_sck ;   //串行时钟计数器
reg     [2:0]   cnt_bit ;   //比特计数器//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************////cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_clk  <=  5'd0;else    if(state != IDLE)cnt_clk  <=  cnt_clk + 1'b1;//cnt_byte:记录输出字节个数和等待时间
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_byte    <=  4'd0;else    if((cnt_clk == 5'd31) && (cnt_byte == 4'd10))cnt_byte    <=  4'd0;else    if(cnt_clk == 31)cnt_byte    <=  cnt_byte + 1'b1;//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_sck <=  2'd0;else    if((state == WR_EN) && (cnt_byte == 1'b1))cnt_sck <=  cnt_sck + 1'b1;else    if((state == PP) && (cnt_byte >= 4'd5) && (cnt_byte <= 4'd9))cnt_sck <=  cnt_sck + 1'b1;//addr_reg:数据写入地址寄存器
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)addr_reg    <=  ADDR;else    if(pi_flag == 1'b1)addr_reg    <=  addr_reg + 1'b1 ;//addr:数据写入地址
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)addr    <=  24'd0;else    if(pi_flag == 1'b1)addr    <=  addr_reg;//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cs_n    <=  1'b1;else    if(pi_flag == 1'b1)cs_n    <=  1'b0;else    if((cnt_byte == 4'd2) && (cnt_clk == 5'd31) && (state == WR_EN))cs_n    <=  1'b1;else    if((cnt_byte == 4'd3) && (cnt_clk == 5'd31) && (state == DELAY))cs_n    <=  1'b0;else    if((cnt_byte == 4'd10) && (cnt_clk == 5'd31) && (state == PP))cs_n    <=  1'b1;//sck:输出串行时钟
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;//cnt_bit:高低位对调,控制mosi输出
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 + 1'b1;//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)state   <=  IDLE;elsecase(state)IDLE:   if(pi_flag == 1'b1)state   <=  WR_EN;WR_EN:  if((cnt_byte == 4'd2) && (cnt_clk == 5'd31))state   <=  DELAY;DELAY:  if((cnt_byte == 4'd3) && (cnt_clk == 5'd31))state   <=  PP;PP:     if((cnt_byte == 4'd10) && (cnt_clk == 5'd31))state   <=  IDLE;default:    state   <=  IDLE;endcase//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)mosi    <=  1'b0;else    if((state == WR_EN) && (cnt_byte == 4'd2))mosi    <=  1'b0;else    if((state == PP) && (cnt_byte == 4'd10))mosi    <=  1'b0;else    if((state == WR_EN) && (cnt_byte == 4'd1) && (cnt_sck == 5'd0))mosi    <=  WR_EN_INST[7 - cnt_bit];    //写使能指令else    if((state == PP) && (cnt_byte == 4'd5) && (cnt_sck == 5'd0))mosi    <=  PP_INST[7 - cnt_bit];       //扇区擦除指令else    if((state == PP) && (cnt_byte == 4'd6) && (cnt_sck == 5'd0))mosi    <=  addr[23 - cnt_bit];         //扇区地址else    if((state == PP) && (cnt_byte == 4'd7) && (cnt_sck == 5'd0))mosi    <=  addr[15 - cnt_bit];         //页地址else    if((state == PP) && (cnt_byte == 4'd8) && (cnt_sck == 5'd0))mosi    <=  addr[7 - cnt_bit];          //字节地址else    if((state == PP) && (cnt_byte == 4'd9) && (cnt_sck == 5'd0))mosi    <=  pi_data[7 - cnt_bit];       //写入数据endmodule

6.2 spi_flash_seq_wr

`timescale  1ns/1ns
module  spi_flash_seq_wr(input   wire    sys_clk     ,   //系统时钟,频率50MHzinput   wire    sys_rst_n   ,   //复位信号,低电平有效input   wire    rx          ,   //串口接收数据output  wire    cs_n        ,   //片选信号output  wire    sck         ,   //串行时钟output  wire    mosi        ,   //主输出从输入数据output  wire    tx              //串口发送数据);//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   UART_BPS    =   14'd9600        ,   //比特率CLK_FREQ    =   26'd50_000_000  ;   //时钟频率//wire  define
wire            po_flag ;
wire    [7:0]   po_data ;//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************////-------------uart_rx_inst-------------
uart_rx
#(.UART_BPS    (UART_BPS ),         //串口波特率.CLK_FREQ    (CLK_FREQ )          //时钟频率
)
uart_rx_inst(.sys_clk     (sys_clk  ),   //系统时钟50Mhz.sys_rst_n   (sys_rst_n),   //全局复位.rx          (rx       ),   //串口接收数据.po_data     (po_data  ),   //串转并后的数据.po_flag     (po_flag  )    //串转并后的数据有效标志信号
);//-------------flash_seq_wr_ctrl_inst-------------
flash_seq_wr_ctrl  flash_seq_wr_ctrl_inst(.sys_clk    (sys_clk    ),  //系统时钟,频率50MHz.sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效.pi_flag    (po_flag    ),  //数据标志信号.pi_data    (po_data    ),  //写入数据.sck        (sck        ),  //片选信号.cs_n       (cs_n       ),  //串行时钟.mosi       (mosi       )   //主输出从输入数据);//-------------uart_tx_inst-------------
uart_tx
#(.UART_BPS    (UART_BPS ),         //串口波特率.CLK_FREQ    (CLK_FREQ )          //时钟频率
)
uart_tx_inst
(.sys_clk     (sys_clk  ),   //系统时钟50Mhz.sys_rst_n   (sys_rst_n),   //全局复位.pi_data     (po_data  ),   //并行数据.pi_flag     (po_flag  ),   //并行数据有效标志信号.tx          (tx       )    //串口发送数据
);endmodule

7. Testbench

`timescale  1ns/1ns
module  tb_spi_flash_seq_wr();//wire define
wire    tx  ;
wire    cs_n;
wire    sck ;
wire    mosi;
wire    miso;//reg define
reg           clk   ;
reg           rst_n ;
reg           rx    ;
reg   [7:0]   data_mem [299:0] ;  //data_mem是一个存储器,相当于一个ram//读取sim文件夹下面的data.txt文件,并把读出的数据定义为data_mem
initial$readmemh("E:/base_code/10_spi_flash/spi_flash_write/spi_flash_seq_wr/sim/spi_flash.txt",data_mem);//时钟、复位信号
initialbeginclk     =   1'b1  ;rst_n   <=  1'b0  ;#200rst_n   <=  1'b1  ;endalways  #10 clk = ~clk;initialbeginrx  <=  1'b1;#200rx_byte();endtask  rx_byte();integer j;for(j=0;j<300;j=j+1)rx_bit(data_mem[j]);
endtasktask  rx_bit(input[7:0] data);  //data是data_mem[j]的值。integer i;for(i=0;i<10;i=i+1)begincase(i)0:  rx  <=  1'b0   ;  //起始位1:  rx  <=  data[0];2:  rx  <=  data[1];3:  rx  <=  data[2];4:  rx  <=  data[3];5:  rx  <=  data[4];6:  rx  <=  data[5];7:  rx  <=  data[6];8:  rx  <=  data[7];  //上面8个发送的是数据位9:  rx  <=  1'b1   ;  //停止位endcase#1040;                  //一个波特时间=sclk周期*波特计数器end
endtask//重定义defparam,用于修改参数,缩短仿真时间
defparam spi_flash_seq_wr_inst.uart_rx_inst.CLK_FREQ    = 500000;
defparam spi_flash_seq_wr_inst.uart_tx_inst.CLK_FREQ    = 500000;
defparam memory.mem_access.initfile = "initmemory.txt";//-------------spi_flash_seq_wr_inst-------------
spi_flash_seq_wr  spi_flash_seq_wr_inst(.sys_clk    (clk    ),    //input   sys_clk.sys_rst_n  (rst_n  ),    //input   sys_rst_n.rx         (rx     ),    //input   rx.cs_n       (cs_n   ),    //output  cs_n.sck        (sck    ),    //output  sck.mosi       (mosi   ),    //output  mosi.tx         (tx     )     //output  tx);m25p16  memory (.c          (sck    ), .data_in    (mosi   ), .s          (cs_n   ), .w          (1'b1   ), .hold       (1'b1   ), .data_out   (miso   )
); endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28379.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置uprof环境

AMD uprof 1/从AMD μProf | AMD处下载两个文档 2/解压 tar jxvf filename 3/将.rpm转换成deb Ubuntu的软件包格式是deb&#xff0c;如果要安装rpm的包&#xff0c;则要先用alien把rpm转换成deb。 sudo apt-get install alien #alien默认没有安装&#xff0c;所以首先要安…

SpringBoot整合SpringSecurity+JWT

SpringBoot整合SpringSecurityJWT 整合SpringSecurity步骤 编写拦截链配置类&#xff0c;规定security参数拦截登录请求的参数&#xff0c;对该用户做身份认证。通过登录验证的予以授权&#xff0c;这里根据用户对应的角色作为授权标识。 整合JWT步骤 编写JWTUtils&#xf…

修复git diff正文中文乱码

Linux git diff正文中文乱码 在命令行下输入以下命令&#xff1a; $ git config --global core.quotepath false # 显示 status 编码 $ git config --global gui.encoding utf-8 # 图形界面编码 $ git config --global i18n.commit.encoding utf-8 # …

状态模式:游戏、工作流引擎中常用的状态机是如何实现的?

从今天起&#xff0c;我们开始学习状态模式。在实际的软件开发中&#xff0c;状态模式并不是很常用&#xff0c;但是在能够用到的场景里&#xff0c;它可以发挥很大的作用。从这一点上来看&#xff0c;它有点像我们之前讲到的组合模式。 可以简短的回顾一下组合模式&#xff1a…

本地appserv外挂网址如何让外网访问?快解析端口映射

一、appserv是什么&#xff1f; AppServ 是 PHP 网页架站工具组合包&#xff0c;作者将一些网络上免费的架站资源重新包装成单一的安装程序&#xff0c;以方便初学者快速完成架站&#xff0c;AppServ 所包含的软件有&#xff1a;Apache[、Apache Monitor、PHP、MySQL、phpMyAdm…

mybatis基础

1.搭建环境 2.单参模糊查询 3.parameter语法 4.多参实战 4.1参数为对象 4.2参数为Map 4.3参数为注解标识 5.增删改 5.1增加 5.1-2 map作为参数 5.2删除 5.3修改 5.4拓展 通过非主键删除 返回值是影响的行数 拓展2 通过非主键修改

服务器数据库被360后缀勒索病毒攻击怎么解决,勒索病毒解密

随着网络攻击日益猖獗&#xff0c;数据库遭遇勒索病毒的攻击已成为常见现象。而360后缀勒索病毒是一种恶意软件&#xff0c;它将加密数据库中的文件&#xff0c;并要求受害者支付赎金才能获得解密密钥。近日&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器被360后…

C语言小项目——通讯录高阶(文件管理版)

通讯录初阶: 点这里 通讯录中阶: 点这里 文件管理版本改进之处通讯录初始化退出通讯录并保存 完整代码contact.hcontact.ctest.c 文件管理版本改进之处 通讯录初始化 contact.c 退出通讯录并保存 test.c contact.c contact.h 完整代码 contact.h #pragma once#include&l…

2.8Menubar菜单

2.8Menubar菜单 这一次的效果将会像下面的图片一样. 注意这里的操作系统是苹果的 MacOS, 它的菜单栏位置和 Windows 的不一样. 下面那张图除了 Cut, Copy, Paste 的选项, 后面的都是 Apple 自己生成的选项, Windows 不会有的. menubar 部件 下面是我们制作整个菜单栏的流程…

纯CSS实现的卡片切换效果

纯CSS实现的卡片切换效果 无需JS就可以实现限于纯静态页面产品展示不需要轮播,自动切换 示例代码 <template><div class"example-css-tab"><div class"container dwo"><div class"card"><input type"radio"…

自动驾驶多任务框架 MultiTask V3、HybridNets和YOLOP比较

目标检测和分割是自动驾驶汽车感知系统的两个核心模块。它们应该具有高效率和低延迟,同时降低计算复杂性。目前,最常用的算法是基于深度神经网络的,这保证了高效率,但需要高性能的计算平台。 在自动驾驶汽车的场景下,大多使用的都是计算能力有限的嵌入式平台,这使得难以满…

Acwing.906 区间分组(贪心)

题目 给定N个闭区间[ai,bi]&#xff0c;请你将这些区间分成若千组&#xff0c;使得每组内部的区间两两之间(包括端点)没有交集&#xff0c;并使得组数尽可能小。 输出最小组数。 输入格式 第一行包含整数N&#xff0c;表示区间数。 接下来N行&#xff0c;每行包含两个整数ai…