学习目的:

(1) 熟悉SPI接口和它的读写时序;

(2) 复习Verilog仿真语句中的$readmemb命令和$display命令;

(3) 掌握SPI接口写时序操作的硬件语言描述流程(本例仅以写时序为例),为以后描述更复杂的时序逻辑电路奠定基础。

学习过程:

SPI的相关知识】

① SPI的速度比串口的快,采用源同步传输的方式,且为串行传输,应用场景不同则时序和接口名称会有不同;

② 串行flash的读写擦除命令可通过SPI接口进行通信,CPU芯片与FPGA可通过SPI接口进行通信,某些芯片的参数寄存器也可通过SPI的方式配置;

③ SPI接口说明

图1 SPI接口

SCLK:主机给从机的系统时钟信号;

SDI:主机输出给从机的数据信号;

SDO:从机输出给主机的数据信号;

CS:片选信号(此处为高电平有效);

SDIO(三线模式):主机与从机之间的双向数据总线。

DAC3283芯片与SPI有关内容】

DAC3283芯片的寄存器映射如图1所示:

图1 DAC3283芯片的寄存器映射图

从图1可知,此DAC芯片共有32个寄存器需要配置(CONFIG0~CONFIG31),且每个寄存器均为8bit。

【关于这些寄存器的配置方法】对于寄存器数量少的情况,可以直接定义几个reg型变量,然后用这些reg变量初始化那些待配置的寄存器;而对于像本例这样有如此多的寄存器需要配置,则需通过FPGA的ROM或RAM完成寄存器的初始化,即:先将配置参数写到FPGA的ROM或者RAM中,然后再通过FPGA把这些参数从ROM或者RAM中读出并写入到外部芯片的寄存器中去。在本例中,是通过SPI接口写入到DAC3283芯片的。

DAC3283芯片的SPI接口时序】

图2 SPI的写时序图

图3 SPI的读时序图

首先,对图2、图3中的几个信号名称作介绍。SCLK是由FPGA送给DAC芯片的时钟信号;SDENB是串行接口的使能信号(相当于片选信号),只有当它为低电平时,SPI的读写才有效;对于“三线模式”的SPI,SDIO是一个双向的数据线,负责数据的读写数据传输;对于“四线模式”的SPI,SDIO也是一个双向的数据线,但ALARM_SDO是一个只读数据线,仅负责DAC芯片输出数据的传输。数据在SCLK的上升沿时刻写入DAC芯片,在SCLK的下降沿时刻从DAC芯片中读出。

然后,介绍一下SPI每次传输的数据内容。本例中,SPI采用的是先传高位后传低位的串行传输方式,每次传输16bit,其中,前8bit构成SPI的指令周期,后8bit构成SPI的数据周期。在前8bit中,rwb(R/W)是读/写控制信号,即:若rwb为高电平时,此条指令为读指令;若rwb为低电平时,此条指令为写指令。N1、N0指明每帧传输的数据字节数(范围为1~4字节,不包含前面的那个指令字节),A3…A0是DAC芯片各寄存器的地址。

SPI接口的状态机】

 
   

图4 DAC芯片寄存器初始化操作状态机(概念模型)

IDLE是起始状态(默认态),work_en是从外部输入的启动使能信号(高电平有效);WAIT是等待状态,在本例中,等待8个时钟周期后(wait_cnt[3] == 1'b1)即进入到READ_MEM状态(读存储器状态),直接读取RAM中的数据;WRITE_REG是写寄存器状态,将32个16bit位宽的数据通过串行移位的方式送给DAC芯片的寄存器,移位完1个16bit位宽的数据后(shift_cnt == 4'd15 && pose_flag == 1'b1 && data_end != 1'b1)就回到WAIT状态,然后重新等待8个时钟周期,重新执行READ_MEM状态和WRITE_REG状态,直到32个数据全都读写完毕后才进入STOP状态(shift_cnt == 4'd15 && pose_flag == 1'b1 && data_end == 1'b1),拉高conf_end信号。

【设计步骤】

(1) 写spi_ctrl.v文件,首先写分频计数器,将50MHz的系统时钟分频为1MHz作为SPI的时钟spi_clk(一般是50~60MHz,但此处仅是演示实验,用1MHz即可),同时,产生clk_p和clk_n两个相位相反、频率为1MHz的reg信号,clk_p是正相时钟信号,用以触发脉冲标志信号pose_flag(标志SPI时钟信号上升沿的到来),clk_n是反相的时钟信号,用作spi_clk,送给SPI当作时钟使用,以上共需写4个always语句块。

(2) 建立一个16bit x 32的单口RAM IP核(仅读不写),并例化到spi_ctrl.v中,完成mif文件的编辑和拷贝。

(3) 声明状态机变量,写状态机(两段式),包括等待状态下的计数器、读RAM的地址产生模块(1个系统时钟周期读出1个数据)、串行移位寄存器(左移,移出最高位)、移位操作的计数器、data_end信号控制模块、数据输出模块、片选信号控制模块、conf_end信号控制模块以及状态跳转模块等部分。

(4) 写testbench文件和run.do文件,运行仿真并分析仿真结果(跑300us)。对testbench文件的要求如下:①能使用$readmemb命令读出dac_ini_16x32.mif文件中的数据;②能在rec_spi任务块中实现对SPI传输数据的回收,并与dac_ini_16x32.mif文件中的数据进行校对,然后用$display命令显示校对结果和数据信息。

【代码实现】

(1)设计一个计数分频模块,用50MHz的系统时钟产生1MHz的时钟信号给SPI读/写操作使用,同时产生clk_p和clk_n两个相位相反、频率均为1MHz的信号:

同时,产生一个标志clk_p上升沿的标志信号pose_flag,用它统一系统的全局时钟:

【注意事项】尽量不要使用分频产生的clk_p或clk_n当作always块的触发条件!原因:时钟信号在很大程度上决定了整个设计的性能和可靠性,应尽量避免使用FPGA内部逻辑产生的时钟,因为它很容易导致功能或时序出现问题,内部逻辑产生的时钟信号容易出现毛刺(布线用线的质量较差),影响信号质量,同时,组合逻辑电路固有的延时也容易导致时序问题。详细的解说请见特权的《深入玩转FPGA》一书的P59。

(2)RAM IP核的创建和例化:

图5 RAM IP核的参数配置

图6 去掉q端寄存器

图7 新建和添加mif文件

图8 勾选例化文件

例化到spi_ctrl.v文件中:

(3)声明状态变量和状态参数,写状态机:

状态跳转控制:

① 写状态机写到WAIT状态时,发现需要产生wait_end信号,而wait_end信号是由计数等待8个时钟周期后产生的,故先需要设计WAIT状态等待计数模块:

然后,用设计wait_end信号产生模块:

② 写状态机写到READ_MEM状态时,发现需要先将RAM中的数据读出来,故先需设计产生读RAM地址的模块:

③ 写状态机写到WRITE_REG状态时,发现先要将从RAM读出来的数据缓存起来(通过shift_buf缓存),然后逐比特地通过sdi线送给DAC芯片(串行移位寄存器):

然后,设计模块产生移位完成的标志信号shift_end:

然后,设计SPI接口的数据输出模块、片选信号控制模块以及时钟信号产生模块:

数据逐比特地送给spi_sdi接口,每帧传输16bit的数据,当传输完32个16bit的数据后,需要产生一个data_end标志信号,标志所有数据已经传输完毕:

当传输完32个16bit的数据时,也即完成了DAC芯片32个寄存器的配置工作,故需要产生一个conf_end标志信号,标志配置操作的完成:

Testbenchrun.do

Testbench文件:

① 用文件控制任务$readmemb读出dac_ini_16x32.mif文件中的数据,并用这些数据初始化存储器mif_data(reg [15:0] mif_data[0:31]):

② 计一个名为spi_check的任务,用它回收spi通信的数据,并将其与dac_ini_16x32.mif文件中的数据进行校对,如果数据一致则输出数据的索引和具体内容,否则提示“SPI write is error!”:

run.do文件:

【仿真结果及分析】

图9 transcript窗口的信息1

图10 transcript窗口的信息2

如图9、图10所示,Testbench从SPI接口回收的数据与mif文件中的数据一致,由此可见SPI数据传输的正确性。

图11 状态机视图

如图12所示,是SPI仿真波形的整体图:

图12 仿真波形图1

如图13所示,spi_clk的上升沿对准spi_sdi数据的中心,满足DAC芯片SPI写时序的要求,即:数据在时钟信号上升沿到来的时候写入DAC芯片。

图13 仿真波形图2

如图14所示,展示了状态机跳转到STOP状态(10000)及data_end上升沿的出现。

图14 仿真波形图3

图15 仿真波形图4

由图15可知,在WRITE_REG状态下,片选信号spi_csn低电平状态的持续时间约为16us,而spi_clk的周期是1us,故由此可知SPI每次移位传输占用了16个SPI时钟周期,这与代码中设计的每帧传输16bit数据的设想是一致的。

转载:https://www.cnblogs.com/huangsanye/p/5256494.html

SPI和RAM IP核的更多相关文章

  1. 第7讲 SPI和RAM IP核

    学习目的: (1) 熟悉SPI接口和它的读写时序: (2) 复习Verilog仿真语句中的$readmemb命令和$display命令: (3) 掌握SPI接口写时序操作的硬件语言描述流程(本例仅以写 ...

  2. 用嵌入式块RAM IP核配置一个双口RAM

    本次设计源码地址:http://download.csdn.net/detail/noticeable/9914173 实验现象:通过串口将数据发送到FPGA 中,通过quartus II 提供的in ...

  3. 自定义AXI总线形式SPI接口IP核,点亮OLED

    一.前言 最近花费很多精力在算法仿真和实现上,外设接口的调试略有生疏.本文以FPGA控制OLED中的SPI接口为例,重新夯实下基础.重点内容为SPI时序的RTL设计以及AXI-Lite总线分析.当然做 ...

  4. IP核——RAM

    一.Quartus 1.打开Quartus ii,点击Tools---MegaWizard Plug-In Manager 2.弹出创建页面,选择Creat a new custom megafunc ...

  5. 7系列高速收发器总结 GTP IP核使用篇

    上一篇7系列收发器博文讲解了GTP IP核的基本配置,本文继续分析如何将它使用起来.生成IP核后打开example design,先看看工程中包含的文件结构. 顶层文件下包含了gtp ip核系统顶层文 ...

  6. Vivado中xilinx_BRAM IP核使用

     Vivado2017.2 中BRAM版本为 Block Memory Generator Specific Features  8.3 BRAM IP核包括有5种类型: Single-port RA ...

  7. EMAC IP 核

    在有线连接的世界里,以太网(Ethernet)无所不在.以太网具有各种速度模式.接口方式.以及灵活的配置方式.现在的以太网卡都是10/100/1000Mbps自适应网卡.以太网的物理层(PHY)通常使 ...

  8. IP核——FIFO

    一.Quartus 1.打开Quartus ii,点击Tools---MegaWizard Plug-In Manager 2.弹出创建页面,选择Creat a new custom megafunc ...

  9. ARM架构授权和IP核授权有什么不一样啊?

    比如,华为分别拿到这2个授权,能做的有什么区别啊? 匿名 | 浏览 2976 次 推荐于2016-06-09 02:43:35   最佳答案   一个公司若想使用ARM的内核来做自己的处理器,比如苹果 ...

随机推荐

  1. 利用Linux文件系统内存cache来提高性能

    https://www.linuxjournal.com/article/6345 利用Linux文件系统内存cache来提高性能 本地磁盘文件->socket发送,4步骤数据流向: hard ...

  2. oracle 拼接字符串的两种方式

      方式一:使用管道符||进行拼接 方式二:使用concat()函数 区别: 方式一可以拼接多个字符串:方式二只能将2个字符串拼接到一起. 写在最后 哪位大佬如若发现文章存在纰漏之处或需要补充更多内容 ...

  3. 〖Linux〗Kubuntu设置打开应用时就只在打开时的工作区显示

    有没有遇到一种情况: 在工作区1打开了应用程序Google Chrome: 这个时间感觉它打开速度比较慢,就快捷键切换到工作区2了: 结果这个时候,Google Chrome就直接在工作区2打开,多不 ...

  4. python之函数用法islower()

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python之函数用法islower() #http://www.runoob.com/python/att ...

  5. java 属性封装

    //封装性 //封装概念: //1.是面向对象思想的三大特征之一 //2.封装细节是隐藏的,仅对外提供访问接口 //3.封装分类: //属性封装 //方法封装 //类的封装 //组件的封装 //模块化 ...

  6. Linux人工清理内存cache

     内存缓存的出现是解决,设备之间的读写速度的差异! 00.linux缓存解释 /proc/sys/vm/drop_caches (since Linux 2.6.16)Writing to this ...

  7. weblogic11g重置账户

    weblogic重置用户名密码 00.备份域下的认证文件 备份%DOMAIN_HOME%/security/DefaultAuthenticatorInit.ldift  #当前的默认密码认证 01. ...

  8. kubelet Pod status的状态分析

    CrashLoopBackOff: 容器退出,kubelet正在将它重启 InvalidImageName: 无法解析镜像名称 ImageInspectError: 无法校验镜像 ErrImageNe ...

  9. ios持久化存储

    前言 iOS中常用的持久化存储方式有好几种: 偏好设置(NSUserDefaults) plist文件存储 归档 SQLite3 Core Data 沙盒 每个iOS应用都有自己的应用沙盒(应用沙盒就 ...

  10. 蛋疼的经历--wireshark不能启动的问题

    事情是这样子的,最近新入职,安装了wireshark,,,在急需要其观察数据包结构,,,,写代码时,,,,卡了,,,我的天!!! 刚开始是提示说,找不到动态链接库api-ms-win-crt-runt ...