本帖最后由 FPGA课程 于 2024-9-27 16:25 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述本文通过axi_quad_spi IP实现,本文不再详细介绍SPI协议本身,如果读者对SPI协议还有不清楚的,可以阅读前面的文章“11SPI环路通信测试”。 本文实验目的: 1:通过阅读pg153-axi-quad-spi.pdf熟悉axi-quad-spi控制器的硬件资源(配套工程的soc_prj/06_doc路径)
2:通过VIVADO搭建axi-quad-spi的SOC工程
3:使用VITIS-SDK编写axi-quad-spi测试程序,实现类似PS-SPI环路测试程序
2系统框图
3AXI-QUAD-SPI IP概述当axi_quad_spi ip可以配置成普通模式axi4-lite或者高性能模式axi4接,IP的框图如下:
3.1特性-配置成axi4-lite接口时,向下兼容IP老版本的1.00版本 -当配置成axi4-full接口时,支持高性能burst模式 -支持的SPI模式包括:标准模式、双SPI模块、四SPI模式 -可编程是SPI时钟和极性 -可配置的FIFO深度(在双/四/标准SPI模式下为16或256)和在XIP模式下固定的FIFO深度为16 -dual和quad模式下可配置的从存储器厂家有:Mixed, Micron, Winbond和Spansion (Beta版) 3.2AXI4接口1:Enhanced Mode这种模式下,AXI4-full只允许访问数据收发寄存器,但是寄存器配置依然采用AXI4-Lite。 2:XIP Mode模式当axi_quad_spi ip配置成XIP模式,通过axis4-lite配置寄存器和状态寄存器,而AXI4仅用于读取数据。在XIP模式下,IP支持以下两种模式: -High Performance Mode:在这种模式下,数据ready信号总是为高,IP支持超过64次的事务。 -Normal Mode:这种模式下,最多支持64次的事务传输
这种模式适合引导程序使用。该模式下,IPCORE将SPI视为只读存储器。在读取SPI闪存时使用的配置模式提供了三个读取命令。通过为axi4 - lite和axis4接口分配相同的频率来验证IP功能。内置在IP中的三个主要的读命令是快速读(0x0Bh)、DIOFR (0xBBh)和QIOFR (0xEBh)。 3.3寄存器概述Axi-quad-spi IP 支持多种工作模式,用户应当根据实际需要选择正确的模式。我们这仅仅介绍最常用的普通模式。非XIP Mode模式下的寄存器介绍如下。 Address Space Offset | Register Name | Access Type | Default Value (hex) | Description | Core Grouping | 40h | SRR
| Write | NA(1) | Software reset register | 60h | SPICR
| R/W | 0x180 | SPI control register | 64h | SPISR
| Read | 0x0a5 | SPI status register | 68h | SPI DTR
| Write | 0x0 | SPI data transmit register. A single register or a FIFO | 6Ch | SPI DRR | Read | N/A(1) | SPI data receive register. A single register or a FIFO | 70h | SPISSR | R/W | No slave is selected 0xFFFF | SPI Slave select register | 74h | SPI Transmit FIFO Occupancy Register(2) | Read | 0x0 | Transmit FIFO occupancy register | 78h | SPI Receive FIFO Occupancy Register(2) | Read | 0x0 | Receive FIFO occupancy register | Interrupt Control Grouping | 1Ch | DGIER | R/W | 0x0 | Device global interrupt enable register | 20h | IPISR | R/TOW(3) | 0x0 | IP interrupt status register | 28h | IPIER | R/W | 0x0 | IP interrupt enable register |
1-SPI DRR上电复位数据未知或全为零。处理步骤这个寄存器不应该考虑在上电复位的情况下使用。 2-仅当“FIFO Depth”为16或256时存在。 3-TOW = Toggle on write。将1写入寄存器内的位位置将导致寄存器中相应的位位置发生切换。 1:SRR软件复位寄存器(offset=0x40)
2:SPICR控制寄存器 (offset=0x60)
1-只有在标准SPI模式下才允许设置该位(LSB优先)。Dual/quad SPI模式仅支持MSB优先模式。在双/四元SPI模式下,如果设置了该位,则在SPISR中设置相应的错误位,并产生中断。 2-在dual和quad SPI模式下,CPHA-CPOL的值允许为00或11。设置其他配置会导致与内存通信时出现故障。如果设置了其他值,则在SPISR中设置相应的错误位,如果相应的位在SPI IPIER寄存器中使能,则会产生中断。 3-只有在标准SPI模式下才支持slave模式。在dual或quad SPI模式下,只支持核心的主模式。如果设置了其他值,则在SPISR中设置相应的错误位,如果相应的位在SPI IPIER寄存器中使能,则会产生中断。 4-在标准SPI模式下允许环回。 3:SPI状态寄存器SPISR(offset=0x64)
4:DTR数据发送寄存器(offset=0x68)
4.1:数据发送寄存器
SPI总线上需要传输的数据写入SPI数据传输寄存器(SPI DTR)。在主模式下SPE位被设为1,或者在从模式下spisel被设为active后,数据从SPI DTR转移到移位寄存器。 SPI DTR在写入之前应该处于复位状态,这样DTR FIFO写指针就指向第0个位置。 4.2:Dual/Quad模式
这种模式下第一个写操作必须总是来自AXI事务的SPI命令,接着是地址(24位或32位),然后写入要传输的数据。当读取内存的状态寄存器时,命令和地址(地址可选)发出后寄存器需要写入虚拟数据,来实现数据的读取。 比如dual mode read命令中,虚拟字节不是在数据线上传输的,而是被内部逻辑用来跟踪要从SPI从内存中读取的数据字节数。 5:DRR数据接收寄存器(offset=0x6C)
SPI DRR (Data Receive Register)用于读取从SPI总线接收到的数据。这是一个双缓冲寄存器。在每次完成传输后,接收到的数据被放入这个寄存器。SPI架构没有为从机提供任何方式来控制总线上的流量;因此,只有当SPI DRR在最后一次SPI传输之前被读取时,SPI DRR才会在每个完成的事务之后更新。 6:SPISSR 从机选择寄存器(offset=0x70)
7:TX_FIFO_OCY发送FIFO占用寄存器(offset=0x74)如果使用了FIFO并且发送的FIFO非空,这个值有效,这个值等于FIFO实际使用的大小减去1。
8:RX_FIFO_OCY接收FIFO占用寄存器(offset=0x78)如果使用了FIFO并且接收的FIFO非空,这个值有效,这个值等于FIFO实际使用的大小减去1。
9:DGIER全局中断寄存器(offset=0x1C)
10:IPISR中断状态寄存器(offset=0x20)
11:IPIER中断使能寄存器(offset=0x28)
4硬件电路分析FEP-BASE-CARD是米联客设计开发用于支持一些基本的FPGA接口学习的模块,可以支持SPI动态驱动数码管实验、RTC实验、EEPROM实验、液晶屏实验、PL串口实验,以及通过CEP扩展到GPIO可以支持LVDS实验、GPIO验证、DVP摄像头连接 FEP-BASE-CARD分3.3V和1.8V版本,由于默认IO采用1.8V因此,这里仅介绍1.8V版本,我们本方案只需要短接MISO和MOSI就可以完成环路测试.
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。
5搭建SOC系统工程详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 5.1SOC系统工程
1:中断设置
2:设置GP Master接口
3:设置复位输出
4:设置PL时钟
5:AXI-SPI IP
6:ILA IP采样深度选择合适 数值,对于BRAM足够的FPGA可以设置大一些
5.2设置AXI外设地址分配只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏
5.3编译并导出平台文件以下步骤简写,有不清楚的看第一篇文章。 1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。 2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。 3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件 4:生成Bit文件。 5:导出到硬件: FileàExport HardwareàInclude bitstream 6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
6搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 6.1创建SDK Platform工程
右击soc_base编译,编译的时间可能会有点长 6.2创建APP工程
7程序分析
7.1polled模式spi_test.c程序- #include "spipl_polled.h"
- #include "sleep.h"
- u8 ReadBuf[MAX_DATA];
- u8 SendBuf[MAX_DATA];
- int main(void)
- {
- int i =0;
- xil_printf("spi loop polled test start!\r\n");
- Spipl_init(SPI_DEVICE_ID);//初始SPI
- for(i=0;i<10;i++)//产生测试数据
- SendBuf[i]=i;
- //启动发送,发送10个字节,和接收10个字节
- Spipl_Transfer(&SpiInstance,SendBuf,ReadBuf,10);
- //打印接收到的数据
- for(i=0;i<10;i++)
- {
- xil_printf("ReadBuf[%d]=%d\r\n",i,ReadBuf[i]);
- }
- xil_printf("spi loop polled test finished!\r\n");
- return 0;
- }
复制代码
通过函数Spipl_Transfer(&SpiInstance,SendBuf,ReadBuf,10)完成数据的发送后接收,如果读者了解SPI协议,应该直到标准SPI数据的发送和接收时同时进行的,如果读数据也时要启动一次发送操作。如果有不清楚的可以阅读“SPI环路测试实验”。 7.2spipl_polled.c程序- /********************MILIANKE**************************
- *Company:Liyang Milian Electronic Technology Co., Ltd
- *Technical forum:www.uisrc.com
- *Taobao: https://milianke.taobao.com
- *Create Date: 2020/12/01
- *Module Name:spi_loop_polled
- *Copyright: Copyright (c) milianke
- *Revision: 1.1
- *Description:
- ****************************************************/
- #include "spipl_polled.h"
- int Spipl_init(u16 SpiDeviceId)
- {
- int Status;
- XSpi_Config *ConfigPtr; /* Pointer to Configuration data */
- /*
- * Initialize the SPI driver so that it is ready to use.
- */
- ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
- if (ConfigPtr == NULL) {return XST_DEVICE_NOT_FOUND;}
- Status = XSpi_CfgInitialize(&SpiInstance, ConfigPtr,ConfigPtr->BaseAddress);
- if (Status != XST_SUCCESS) {return XST_FAILURE;}
- /*
- * Run loopback test only in case of standard SPI mode.
- */
- if (SpiInstance.SpiMode != XSP_STANDARD_MODE) {
- return XST_SUCCESS;
- }
- /*
- * Set the Spi device as a master and in loopback mode.
- */
- //Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION |XSP_LOOPBACK_OPTION);
- Status = XSpi_SetOptions(&SpiInstance, XSP_MASTER_OPTION);//设置SPI的工作模式,使用外部MASTER模式
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- //在主模式下,这个函数一定要执行,设置Slave选择寄存器
- XSpi_SetSlaveSelect(&SpiInstance,1);
- //启动SPI控制器
- XSpi_Start(&SpiInstance);
- //禁用SPI中断
- XSpi_IntrGlobalDisable(&SpiInstance);
- }
- int Spipl_Transfer(XSpi *InstancePtr, u8 *SendBufPtr,u8 *RecvBufPtr, unsigned int ByteCount)
- {
- int Status;
- //在主模式下,SPI启动一次发送的同时也会启动接收,所以要读数据也要启动一次发送,这里正好可以发送的数据直接外部环路环回
- Status = XSpi_Transfer(InstancePtr, SendBufPtr, RecvBufPtr, ByteCount);
- if (Status != XST_SUCCESS) {return XST_FAILURE;}
- }
复制代码
spipl_polled.c程序中我们重点看下以下几个函数。 1:XSpi_SetOptions(&SpiInstance, XSP_MASTER_OPTION)这个函数设置PSI控制器为主模式
2:XSpi_SetSlaveSelect(&SpiInstance,1)在主模式下,这个函数一定要执行,设置Slave选择寄存器,其实就是设置了InstancePtr->SlaveSelectReg,后面的数据传输函数中会设置这个寄存器。
3:XSpi_Start(&SpiInstance)函数启动SPI控制器
4:XSpi_Transfer(InstancePtr,SendBufPtr,RecvBufPtr,ByteCount)数据收发这个函数中有关于主模式下,是否选设置了InstancePtr->SlaveSelectReg参数的判断
之后设置slave select寄存器
最后再while大循环完成数据的发送后接收。
7.3intrrupt模式spi_test.c程序- #include "sys_intr.h"
- #include "spipl_intr.h"
- u8 ReadBuf[100];
- u8 SendBuf[100];
- void init_intr_sys(void)
- {
- Init_Intr_System(&Intc);//初始化系统中断
- SpiIntr_Init(&SpiInstance,SPI_DEVICE_ID); //初始化SPI
- Spipl_Setup_Intr(&Intc, &SpiInstance, SPI_INTR_ID); //初始化SPI PL中断
- Setup_Intr_Exception(&Intc);
- }
- int main(void)
- {
- int i =0;
- xil_printf("spi loop interrupt test start!\r\n");
- init_intr_sys();
- for(i=0;i<10;i++)//产生测试数据
- SendBuf[i]=i;
- TransferInProgress = TRUE;
- Spipl_Transfer(&SpiInstance,SendBuf,ReadBuf,10); //启动SPI数据的发送和接收
- while(TransferInProgress); //在中断状态回调函数中,设置该变量
- //打印接收到的数据
- for(i=0;i<10;i++)
- {
- xil_printf("ReadBuf[%d]=%d\r\n",i,ReadBuf[i]);
- }
- xil_printf("spi loop interrupt test finished!\r\n");
- return 0;
- }
复制代码
7.4spipl_intr.c程序- #include "spipl_intr.h"
- volatile int TransferInProgress;
- int SpiIntr_Init(XSpi *SpiInstancePtr,u16 SpiDeviceId)
- {
- int Status;
- XSpi_Config *ConfigPtr; /* Pointer to Configuration data */
- ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
- if (ConfigPtr == NULL) {return XST_DEVICE_NOT_FOUND;}
- Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr,ConfigPtr->BaseAddress);
- if (Status != XST_SUCCESS) {return XST_FAILURE;}
- /*
- * Run loopback test only in case of standard SPI mode.
- */
- if (SpiInstancePtr->SpiMode != XSP_STANDARD_MODE) {return XST_SUCCESS;}
- //Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION |XSP_LOOPBACK_OPTION);
- //设置SPI模式为MASTER模式,外部模式
- Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION);
- if (Status != XST_SUCCESS) {return XST_FAILURE;}
- //在主模式下,这个函数一定要执行,设置Slave选择寄存器
- XSpi_SetSlaveSelect(SpiInstancePtr,1);
- //启动SPI控制器
- XSpi_Start(SpiInstancePtr);
- //SpiDisableIntrSystem(IntcInstancePtr, SpiIntrId);
- return XST_SUCCESS;
- }
- void Spipl_IntrHandler(void *CallBackRef, u32 StatusEvent, u32 ByteCount)
- {
- TransferInProgress = FALSE;
- if (StatusEvent != XST_SPI_TRANSFER_DONE) {
- Error++;
- }
- }
- //设置PL中断
- int Spipl_Setup_Intr(XScuGic *IntcInstancePtr, XSpi *SpiInstancePtr, u16 SpiIntrId)
- {
- int Status;
- //设置SPI的中断状态回调函数
- XSpi_SetStatusHandler(SpiInstancePtr, SpiInstancePtr,(XSpi_StatusHandler) Spipl_IntrHandler);
- //设置AXI-SPI对应的PL中断
- XScuGic_SetPriorityTriggerType(IntcInstancePtr, SpiIntrId, 0xA0, 0x3);
- //设置中断回调函数,在该函数中,进一步调用Spipl_IntrHandler函数
- Status = XScuGic_Connect(IntcInstancePtr, SpiIntrId,(Xil_InterruptHandler)XSpi_InterruptHandler,SpiInstancePtr);
- if (Status != XST_SUCCESS) {return Status;}
- //使能SPI中断
- XScuGic_Enable(IntcInstancePtr, SpiIntrId);
- return XST_SUCCESS;
- }
- //SPI数据收发
- int Spipl_Transfer(XSpi *InstancePtr, u8 *SendBufPtr,u8 *RecvBufPtr, unsigned int ByteCount)
- {
- int Status;
- //在主模式下,SPI启动一次发送的同时也会启动接收,所以要读数据也要启动一次发送,这里正好可以发送的数据直接外部环路环回
- Status = XSpi_Transfer(InstancePtr, SendBufPtr, RecvBufPtr, ByteCount);
- if (Status != XST_SUCCESS) {return XST_FAILURE;}
- }
- void Spipl_Disable_Intr(XScuGic *IntcInstancePtr, u16 SpiIntrId)
- {
- XScuGic_Disconnect(IntcInstancePtr, SpiIntrId);
-
- }
复制代码
这段程序中,大部分和polled模式一样,增加的中断部分主要增加了中断回调函数的设置。由于采用了中断模式,只有当中断回调函数被调用后,TransferInProgress = FALSE代表一次传输完成。 8方案演示
8.1硬件准备
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON(注意新版本的 MLK_H3_CZ08-7100-MZ7100FC),支持 JTAG 模式,对于老版本的核心板,JTAG 调试的时候 一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
设置短接帽如下图
8.2实验结果
通过在线逻辑分析仪查看波形
|