10_SPI_Flash 连续写实验

news2024/10/5 23:31:07

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     ,   //系统时钟,频率50MHz
    input   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;
    else
    case(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     ,   //系统时钟,频率50MHz
    input   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);


//时钟、复位信号
initial
  begin
    clk     =   1'b1  ;
    rst_n   <=  1'b0  ;
    #200
    rst_n   <=  1'b1  ;
  end

always  #10 clk = ~clk;


initial
  begin
    rx  <=  1'b1;
    #200
    rx_byte();
  end

task  rx_byte();
  integer j;
  for(j=0;j<300;j=j+1)
    rx_bit(data_mem[j]);
endtask

task  rx_bit(input[7:0] data);  //data是data_mem[j]的值。
  integer i;
    for(i=0;i<10;i=i+1)
      begin
        case(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.coloradmin.cn/o/752063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux开发环境的搭建

文章目录 系统安装工具软件安装Xshell远程登录VScode远程登录Linux 下GCC安装 系统安装 &#xff08;虚拟机安装、云服务器&#xff09;Ubuntu18.04 网络类型&#xff1a;桥接模式网络、NAT&#xff08;network access transation)网络地址转换模式、仅主机模式 注意&#xff…

模拟电压与数字脉冲占空比控制的应用与发展前景

摘要&#xff1a;本文将讨论模拟电压控制和数字脉冲占空比控制在嵌入式控制方面的应用场景、共同点和不同点&#xff0c;并探讨它们在未来发展中的前景。 引言&#xff1a; 模拟电压控制和数字脉冲占空比控制都是嵌入式系统中常用的控制方式。模拟电压控制将电压作为控制信号&…

electron 应用优雅的配置 about 信息

使用 electron 的 dialog tray 托盘栏菜单优雅简单的配置 about 关于本应用的信息&#xff0c;效果下图所示。 项目依赖 {"electron": "^24.4.1","electron-builder": "^23.6.0","electron-builder-squirrel-windows": &q…

线程那些事

线程锁 线程锁&#xff08;Thread Lock&#xff09;&#xff0c;也被称为互斥锁&#xff08;Mutex Lock&#xff09;&#xff0c;是一种用于多线程编程中的同步机制。它用于保护共享资源在多个线程之间的访问&#xff0c;以避免出现竞态条件&#xff08;Race Condition&#x…

企业内容建站系统 ModStartCMS v6.8.0 内容页面自定义模板,内容区块功能增强

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…

【状态估计】一维粒子滤波研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

虚函数表的地址

结论 1. c多态的实现是靠虚函数表来实现的&#xff0c;有虚函数的类有虚函数表&#xff0c;没虚函数的类就没有虚函数表 2. 虚函数表是类的所有对象共用&#xff0c;切记是共同所有&#xff0c;不是一个对象所有 3. 每个虚函数成员占据虚函数表的一行&#xff0c;是个指针&a…

机械设计制造及其自动化专业向PLC方向发展的可行性

是的&#xff0c;机械设计制造及其自动化专业往PLC&#xff08;可编程逻辑控制器&#xff09;方向发展是可行的。PLC是一种用于控制和自动化各种机械设备和工业过程的计算机控制系统。它被广泛应用于工业自动化领域&#xff0c;包括制造业、能源行业、交通运输等。 我这里刚好…

001- database - 数据库

1、新的数据库进入默认有四个数据库&#xff0c;一般不要轻易删除&#xff1b; -- 创建数据库 CREATE DATABASE 数据库名 -- 查询所有数据库 SHOW DATABASES -- 使用数据库 -- USE 数据库名 -- 查询当前使用的数据库 SELECT DATABASE() -- 删除数据库 DROP DATABASE 数据库名

Rdkit|分子3D构象生成与优化

github; 地址 文章目录 Rdkit|分子3D构象生成与优化构象生成算法概述基于距离&#xff08;distance-based&#xff09;代码示例 距离几何算法生成3D结构距离几何ETKDG生成3D构象距离几何ETKDG生成多构象将Conformer类转为Mol类手动对齐 距离几何ETKDGMMFF生成3D构象距离几何ETK…

Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析

1. HTTP协议解读 什么是协议&#xff1f; 协议实际上是某些人&#xff0c;或者某些组织提前制定好的一套规范&#xff0c;大家都按照这个规范来&#xff0c;这样可以做到沟通无障碍。 协议就是一套规范&#xff0c;就是一套标准。由其他人或其他组织来负责制定的。 我说的话你…

PyCharm 自动添加作者信息、创建时间等信息

PyCharm 自动添加作者信息、创建时间等信息‘ 第一步 找到settings 第二步&#xff0c;找到下图所示位置输入下面代码&#xff0c;作者改成你自己的缩写&#xff0c;你也可以添加其他的 Project &#xff1a;${PROJECT_NAME} File &#xff1a;${NAME}.py IDE &…

【iOS】编译与链接

前言 计算机语言分为机器语言、汇编语言和高级语言。 可以将高级语言分为两种&#xff1a;编译语言和解释型语言&#xff08;直译式语言&#xff09;。 解释型语言&#xff08;逐步进行解释执行&#xff09; 解释语言编写的程序在每次运行时都需要通过解释器对程序进行动态…

【Leetcode】142.环形链表II

题意&#xff1a; 给定一个链表&#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 为了表示给定链表中的环&#xff0c;使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;索引从 0 开始&#xff09;。 如果 pos 是 -1&#xff0c;则…

多媒体开发之cgo

go语言作为近十年来优秀的现代开发语言的代表&#xff0c;由于继承了c语言的简洁和很多现代语言的表达方式&#xff0c;在广泛的应用场景中得到众多爱好者的喜爱&#xff0c;如何将go和c、c进行联合开发&#xff0c;拓展整个开发生态&#xff0c;不用重复造轮子&#xff0c;掌握…

k8s 持久化存储

我们继续来查看 k8s 的卷&#xff0c;上一次我们分享了将磁盘挂载到容器中&#xff0c;empyDir 和 gitRepo 都是会随着 pod 的启动而创建&#xff0c;随着 pod 的删除而销毁 那么我们或许会有这样的需求&#xff0c;期望在 pod 上面读取节点的文件或者使用节点的文件系统来访问…

Win10 配置ADB安装2023.7.12版本

目录 1. ADB工具介绍2. ADB安装流程 参考 Win10 配置安装ADB教程总结20200514&#xff0c; fastboot刷机 1. ADB工具介绍 ADB ( Android Debug Bridge)&#xff0c;即Android 调试桥。是 Google 为开发人员提供的一种命令行工具&#xff0c;用于与安卓设备进行通信&#xff0…

开源网安加入东莞市大数据协会,共建安全可靠软件产业生态

​近日&#xff0c;开源网安成为东莞市大数据协会会员单位&#xff0c;与协会共同构建安全可靠软件产业生态&#xff0c;在科技创新、共建安全生态等方面发力&#xff0c;推动软件产业引领经济高质量发展&#xff0c;推动大湾区企业加快数字化建设进程。 东莞市大数据协会致力于…

Oracle使用PL/SQL导出表,结果显示insert语句

导出表到sql文件中有两种方法&#xff0c;下面我们列举两种方法的操作 1、选择工具->导出->选中要导出的表->点击sql插入->自定义导出路径以及文件&#xff0c;点击导出即可。当然也可以在where子句中增加条件&#xff0c;以便筛选导出 2、首选查询表数据&#xff…

阿里云无影云电脑使用方法_3分钟上手教程

阿里云无影云电脑即无影云电脑&#xff0c;云电脑如何使用&#xff1f;云电脑购买后没有用户名和密码&#xff0c;先创建用户设置密码&#xff0c;才可以登录连接到云电脑。云桌面想要访问公网还需要开通互联网访问功能。阿里云百科来详细说下阿里云无影云电脑从购买、创建用户…