11_SPI_Flash 读数据实验

news2025/1/11 12:46:19

11_SPI_Flash 读数据实验

  • 1. 实验目标
  • 2. 操作时序
    • 2.1 数据读操作指令
    • 2.2 数据读操作时序
  • 3. 流程框图
    • 3.1 顶层模块
    • 3.2 数据读模块
  • 4. 波形图绘制
  • 5. RTL
    • 5.1 flash_read_ctrl
    • 5.2 spi_flash_read
  • 6. testbench

1. 实验目标

使用页写或连续写操作向 Flash 芯片写入数据,再使用数据读操作读取之前写入数据,将读取的数据使用串口传回 PC 机,使用串口助手传回数据并与之前写入数据比较,判断正误。

2. 操作时序

2.1 数据读操作指令

在这里插入图片描述

2.2 数据读操作时序

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

3. 流程框图

3.1 顶层模块

在这里插入图片描述

3.2 数据读模块

在这里插入图片描述

在这里插入图片描述

4. 波形图绘制

读取数据阶段
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

读出了数据,下面是把串行的数据 转换为并行的数据,传入到接口数据的读取上升沿。
在这里插入图片描述
在这里插入图片描述

5. RTL

5.1 flash_read_ctrl

`timescale  1ns/1ns




module  flash_read_ctrl(

    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号
    input   wire            miso        ,   //读出flash数据

    output  reg             sck         ,   //串行时钟
    output  reg             cs_n        ,   //片选信号
    output  reg             mosi        ,   //主输出从输入数据
    output  reg             tx_flag     ,   //输出数据标志信号
    output  wire    [7:0]   tx_data         //输出数据

);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   3'b001  ,   //初始状态
            READ    =   3'b010  ,   //数据读状态
            SEND    =   3'b100  ;   //数据发送状态

parameter   READ_INST   =   8'b0000_0011;   //读指令
parameter   NUM_DATA    =   16'd100     ;   //读出数据个数
parameter   SECTOR_ADDR =   8'b0000_0000,   //扇区地址
            PAGE_ADDR   =   8'b0000_0100,   //页地址
            BYTE_ADDR   =   8'b0010_0101;   //字节地址
parameter   CNT_WAIT_MAX=   16'd6_00_00 ;

//wire  define
wire    [7:0]   fifo_data_num   ;   //fifo内数据个数
//reg   define
reg     [4:0]   cnt_clk         ;   //系统时钟计数器
reg     [2:0]   state           ;   //状态机状态
reg     [15:0]  cnt_byte        ;   //字节计数器
reg     [1:0]   cnt_sck         ;   //串行时钟计数器
reg     [2:0]   cnt_bit         ;   //比特计数器
reg             miso_flag       ;   //miso提取标志信号
reg     [7:0]   data            ;   //拼接数据
reg             po_flag_reg     ;   //输出数据标志信号
reg             po_flag         ;   //输出数据
reg     [7:0]   po_data         ;   //输出数据
reg             fifo_read_valid ;   //fifo读有效信号
reg     [15:0]  cnt_wait        ;   //等待计数器
reg             fifo_read_en    ;   //fifo读使能
reg     [7:0]   read_data_num   ;   //读出fifo数据个数

//********************************************************************//
//***************************** 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 == READ)
        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    <=  16'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 16'd3))
        cnt_byte    <=  16'd0;
    else    if(cnt_clk == 5'd31)
        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 == READ)
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == NUM_DATA + 16'd3) && (cnt_clk == 5'd31) && (state == READ))
        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(key == 1'b1)
                    state   <=  READ;
        READ:   if((cnt_byte == NUM_DATA + 16'd3) && (cnt_clk == 5'd31))
                    state   <=  SEND;
        SEND:   if((read_data_num == NUM_DATA)
                && ((cnt_wait == (CNT_WAIT_MAX - 1'b1))))
                    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 == READ) && (cnt_byte>= 16'd4))
        mosi    <=  1'b0;
    else    if((state == READ) && (cnt_byte == 16'd0) && (cnt_sck == 2'd0))
        mosi    <=  READ_INST[7 - cnt_bit];  //读指令
    else    if((state == READ) && (cnt_byte == 16'd1) && (cnt_sck == 2'd0))
        mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
    else    if((state == READ) && (cnt_byte == 16'd2) && (cnt_sck == 2'd0))
        mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
    else    if((state == READ) && (cnt_byte == 16'd3) && (cnt_sck == 2'd0))
        mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址

//miso_flag:miso提取标志信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        miso_flag   <=  1'b0;
    else    if((cnt_byte >= 16'd4) && (cnt_sck == 2'd1))
        miso_flag   <=  1'b1;
    else
        miso_flag   <=  1'b0;

//data:拼接数据
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data    <=  8'd0;
    else    if(miso_flag == 1'b1)
        data    <=  {data[6:0],miso};

//po_flag_reg:输出数据标志信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag_reg <=  1'b0;
    else    if((cnt_bit == 3'd7) && (miso_flag == 1'b1))
        po_flag_reg <=  1'b1;
    else
        po_flag_reg <=  1'b0;

//po_flag:输出数据标志信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <=  1'b0;
    else
        po_flag <=  po_flag_reg;

//po_data:输出数据
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <=  8'd0;
    else    if(po_flag_reg == 1'b1)
        po_data <=  data;
    else
        po_data <=  po_data;

//fifo_read_valid:fifo读有效信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        fifo_read_valid <=  1'b0;
    else    if((read_data_num == NUM_DATA)
                && ((cnt_wait == (CNT_WAIT_MAX - 1'b1))))
        fifo_read_valid <=  1'b0;
    else    if(fifo_data_num == NUM_DATA)
        fifo_read_valid <=  1'b1;

//cnt_wait:两数据读取时间间隔
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_wait    <=  16'd0;
    else    if(fifo_read_valid == 1'b0)
        cnt_wait    <=  16'd0;
    else    if(cnt_wait == (CNT_WAIT_MAX - 1'b1))
        cnt_wait    <=  16'd0;
    else    if(fifo_read_valid == 1'b1)
        cnt_wait    <=  cnt_wait + 1'b1;

//fifo_read_en:fifo读使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        fifo_read_en <=  1'b0;
    else    if((cnt_wait == (CNT_WAIT_MAX - 1'b1))
                && (read_data_num < NUM_DATA))
        fifo_read_en <=  1'b1;
    else
        fifo_read_en <=  1'b0;

//read_data_num:自fifo中读出数据个数计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        read_data_num <=  8'd0;
    else    if(fifo_read_valid == 1'b0)
        read_data_num <=  8'd0;
    else    if(fifo_read_en == 1'b1)
        read_data_num <=  read_data_num + 1'b1;

//tx_flag
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        tx_flag <=  1'b0;
    else
        tx_flag <=  fifo_read_en;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//-------------fifo_data_inst--------------
fifo_data fifo_data_inst(
    .clock  (sys_clk      ),    //时钟信号
    .data   (po_data      ),    //写数据,8bit
    .wrreq  (po_flag      ),    //写请求
    .rdreq  (fifo_read_en ),    //读请求

    .q      (tx_data      ),    //数据读出,8bit
    .usedw  (fifo_data_num)     //fifo内数据个数
);

endmodule

5.2 spi_flash_read

`timescale  1ns/1ns




module  spi_flash_read(

    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    pi_key      ,   //按键输入信号
    input   wire    miso        ,   //读出flash数据

    output  wire    cs_n        ,   //片选信号
    output  wire    sck         ,   //串行时钟
    output  wire    mosi        ,   //主输出从输入数据
    output  wire    tx              

);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   CNT_MAX     =   20'd999_999     ;   //计数器计数最大值
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率


//wire  define
wire            po_key  ;   //消抖处理后的按键信号
wire            tx_flag ;   //输入串口发送模块数据标志信号
wire    [7:0]   tx_data ;   //输入串口发送模块数据

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- key_filter_inst -------------
key_filter
#(
    .CNT_MAX    (CNT_MAX    )   //计数器计数最大值
)
key_filter_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key_in     (pi_key     ),  //按键输入信号

    .key_flag   (po_key     )   //消抖后信号
);

//-------------flash_read_ctrl_inst-------------
flash_read_ctrl  flash_read_ctrl_inst(

    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key        (po_key     ),  //按键输入信号
    .miso       (miso       ),  //读出flash数据

    .sck        (sck        ),  //片选信号
    .cs_n       (cs_n       ),  //串行时钟
    .mosi       (mosi       ),  //主输出从输入数据
    .tx_flag    (tx_flag    ),  //输出数据标志信号
    .tx_data    (tx_data    )   //输出数据

);

//-------------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     (tx_data  ),   //并行数据
    .pi_flag     (tx_flag  ),   //并行数据有效标志信号
                                
    .tx          (tx       )    //串口发送数据
);

endmodule

6. testbench

`timescale  1ns/1ns




module  tb_spi_flash_read();

//wire  define
wire    cs_n;
wire    sck ;
wire    mosi;
wire    miso;
wire    tx  ;

//reg   define
reg     clk     ;
reg     rst_n   ;
reg     key     ;

//时钟、复位信号、模拟按键信号
initial
    begin
        clk =   0;
        rst_n   <=  0;
        key <=  0;
        #100
        rst_n   <=  1;
        #1000
        key <=  1;
        #20
        key <=  0;
    end

always  #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_flash_read    spi_flash_read_inst(
    .sys_clk    (clk    ),  //input     sys_clk
    .sys_rst_n  (rst_n  ),  //input     sys_rst
    .pi_key     (key    ),  //input     key
    .miso       (miso   ),

    .sck        (sck    ),  //output    sck
    .cs_n       (cs_n   ),  //output    cs_n
    .mosi       (mosi   ),  //output    mosi
    .tx         (tx     )

);

//------------- memory -------------
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/751808.html

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

相关文章

火山引擎 DataLeap 构建Data Catalog系统的实践(三):关键技术与总结

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 关键技术 构建一个好的Data Catalog系统&#xff0c;需要考虑的核心产品设计和技术设计有很多。篇幅所限&#xff0c;本文只概要介绍技术设计中最核心重要的部分&a…

工作日志2 input 的事件优先级 字符串.trim() this.$set()的应用 获取jq的自定义属性

input 的事件优先级 1.input输入框的事件 字符串.trim() 除去前后空格的方法 undefind不可以使用 this.$set()的应用

苹果Mac动态壁纸软件Dynamic Wallpaper

Dynamic Wallpaper 是一款桌面壁纸管理软件&#xff0c;它提供了动态壁纸的功能。动态壁纸是指可以在一段时间内自动更改外观的壁纸&#xff0c;比如根据时间或其他条件进行变化。这种壁纸可以为用户提供更加生动有趣的桌面体验。 Dynamic Wallpaper 软件具有以下特点和功能&am…

基于Java+SpringBoot+Vue的中小企业财务管理系统设计与实现

博主介绍&#xff1a;✌擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案…

Word之解决中文和英文混写导致字间距增大的问题(六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【2023裸辞失业后之初学Docker1】

目录 Docker简介docker下载安装常用命令帮助启动类命令镜像命令容器命令 镜像分层概念commit命令本地镜像发布到腾讯云 docker容器数据卷宿主机和容器内添加容器卷案例读写规则容器卷的继承 学习视频来自 https://www.bilibili.com/video/BV1gr4y1U7CY Docker简介 Docker出现…

DNS协议解析原理

0. 前言 为了保证网址的正常访问&#xff0c;域名解析协议&#xff08;DNS&#xff09;其实在背后做出了很多努力&#xff0c;本文将透彻讲解 DNS 协议的原理&#xff0c;了解我们每天都在接触的网址到底是怎么工作的。 1. 什么是 DNS 协议 在学习 DNS 协议之前&#xff0c;我…

你真的不想知道ai绘画工具有哪些吗?

近期我发现了一个超酷的玩意儿&#xff0c;叫做ai绘画工具。没错&#xff0c;它就是那个能让你在不懂任何绘画技巧的情况下&#xff0c;也能创作出令人惊叹的艺术作品的神奇东西&#xff01;简直就像是给你一支魔法画笔&#xff0c;让你成为真正的艺术大师。但是&#xff0c;市…

Java设计模式之行为型-备忘录模式(UML类图+案例分析)

目录 一、基础概念 二、UML类图 三、角色设计 四、案例分析 五、总结 一、基础概念 备忘录模式是一种行为型设计模式&#xff0c;它允许保存一个对象的内部状态到一个备忘录对象中&#xff0c;这样就可以在需要的时候恢复这个对象的状态了&#xff0c;同时又不违反封装…

1.5 纹理

这次笔记时间有点久&#xff0c;主要是这节课讲的东西需要很多基础来铺垫&#xff0c;看完了后感觉缺失信息很多&#xff0c;又去补了GAMES 101 3~10节内容。 强烈建议看不懂的先去学习GMAES101 网址Lecture 08 Shading 2 (Shading, Pipeline and Texture Mapping)_哔哩哔哩_bi…

你知道哪些Linux 发行版采用 了KDE Plasma 吗

Linux操作系统以其高度的灵活性和可定制性而闻名&#xff0c;有许多不同的发行版适用于各种场合和需求。其中一种备受欢迎的桌面环境是KDE Plasma&#xff0c;它提供了现代化的外观、丰富的功能和高度自定义的选项。那么&#xff0c;你知道哪些Linux发行版采用了KDE Plasma呢&a…

python遍历整个网站寻找所有输入框并提交表单

文章目录 一、遍历查找网站所有输入框二、对找到的输入框实现自动表单提交三、实现留言板和其他输入框的表单提交 一、遍历查找网站所有输入框 # 查找所有表单 import requests from bs4 import BeautifulSoup import sys# 定义起始页面 url sys.argv[1]# 通过requests库获取…

Linux下如何部署Nuxt项目(二)

Linux下如何部署Nuxt项目(一)_小鸟哗啦啦的博客-CSDN博客&#xff0c;书接上回&#xff0c;以实际场景开始。 请认真看完这篇文章&#xff0c;还不会部署Nuxt&#xff0c;我直接拿弹弓打你们家玻璃&#xff01; 一、nuxt的配置检查 服务端渲染的应用&#xff0c;应该是先编译构…

智能手表学习笔记

一、相关知识模块 1、RTOS & FreeRTOS &#xff08;1&#xff09; RTOS 实时操作系统&#xff08;Real Time Operating System&#xff09; 是指当外界事件或数据产生时&#xff0c;能够接受并以足够快的速度予以处理&#xff0c;其处理的结果又能在规定的时间之内来控制…

信号完整性分析基础知识之有损传输线、上升时间衰减和材料特性(二):损耗的来源

导体电阻和趋肤深度 信号沿信号路径和返回路径传播的串联电阻与导体的体电阻率和电流传播的横截面有关。直流时&#xff0c;信号导体中的电流分布均匀&#xff0c;电阻为&#xff1a; ρ表示电阻的体电阻率&#xff0c;w表示线宽&#xff0c;t表示导体厚度&#xff0c;Len表示走…

7.10蓝桥杯刷题

public class _求阶乘和 {public static void main(String[] args) {// 根据已有的知识 可以知道的是&#xff0c;现在要求s的末尾九位数字&#xff0c;已知的是39之后的阶乘他的后九位都是0;//所以不需要计算到2023的阶乘//一个数求出来的阶乘想要末尾有0//数中必须要有2和5&a…

C语言每日一练(3)

C 练习实例6 题目&#xff1a;用*号输出字母C的图案。 程序分析&#xff1a;可先用*号在纸上写出字母C&#xff0c;再分行输出。 程序源代码&#xff1a; #include "stdio.h" int main() {printf("用 * 号输出字母 C!\n");printf(" ****\n")…

小红书数据分析工具|年轻人入玄门?小红书热门笔记大赏

当代年轻人血脉觉醒&#xff0c;逐渐出现寺庙打卡、电子木鱼、发疯语录“在上进和上班之间&#xff0c;选择上香”&#xff0c;小编观察近期彩票成为年轻人信仰的新玄学&#xff0c;兴起了“送礼就送刮刮乐”的小风潮。卷不动躺不平的年轻人&#xff0c;严重精神内耗下&#xf…

Pycharm中设置动态模板(自定义自动补齐)

Settings——Editor——Live Template——点击号新增动态模板 Abbreviation缩略词&#xff1a;输入的简写内容 Template text: 简写词对应的模板内容 默认输入简写内容按Tab键&#xff0c;生成模板内容 Define: 选择在哪个语言环境下生效

【WebSocket】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 3.1 介绍3.2 入门案例3.2.1 案例分析3.2.2 代码开发3.2.3 功能测试 4. 来单提醒4.1 需求分析和设计4.2 代码开发4.3 功能测试 5. 客户催单5.1 需求分析和设计5.2 代…