[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK入门篇连载-21PL AXI-SPI 实验

文档创建者:FPGA课程
浏览次数:160
最后更新:2024-09-27
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 1-SDK基础入门方案
本帖最后由 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系统框图
f36f5b736e0b44cebbea5c7e7b8fd9a7.jpg
3AXI-QUAD-SPI IP概述
当axi_quad_spi ip可以配置成普通模式axi4-lite或者高性能模式axi4接,IP的框图如下:
0cb07be65a574852bf738d18410ec6ae.jpg
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次的事务传输
01689605027c4d04a091e3a9dbac1fbe.jpg
这种模式适合引导程序使用。该模式下,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)
26add172c90d493599a5f7c2aeef4886.jpg
2:SPICR控制寄存器 (offset=0x60)
e4e73efadd37411a9e4f987879b857ae.jpg
1a7f21ef37b24c9fa51e3a0e324a5184.jpg
80bc83b820fb4d36aac0a9e728e8bea7.jpg
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)
97f51ac20cbb4efa964d3f78d29cfcf1.jpg
cba531947b1543359791223c269e6763.jpg
c6b15efd946d4e8aa3db1ae8984ed2f0.jpg
4:DTR数据发送寄存器(offset=0x68)
d0288a544f9e445dbd6c20bed8e2bebf.jpg
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)
796e8e166664452c8c7d43ce2de83289.jpg
SPI DRR (Data Receive Register)用于读取从SPI总线接收到的数据。这是一个双缓冲寄存器。在每次完成传输后,接收到的数据被放入这个寄存器。SPI架构没有为从机提供任何方式来控制总线上的流量;因此,只有当SPI DRR在最后一次SPI传输之前被读取时,SPI DRR才会在每个完成的事务之后更新。
6:SPISSR 从机选择寄存器(offset=0x70)
9c0f2583cfa74402b181a6944d26857f.jpg
9f0e37974b7c46fab93d13fc467d915a.jpg
7:TX_FIFO_OCY发送FIFO占用寄存器(offset=0x74)
如果使用了FIFO并且发送的FIFO非空,这个值有效,这个值等于FIFO实际使用的大小减去1。
e0ede9938100465d899493234f16a256.jpg
8:RX_FIFO_OCY接收FIFO占用寄存器(offset=0x78)
如果使用了FIFO并且接收的FIFO非空,这个值有效,这个值等于FIFO实际使用的大小减去1。
7ece7ec1a9974250b42e0f29cf1d387e.jpg
9:DGIER全局中断寄存器(offset=0x1C)
925430db69574e049de461868f66d8c2.jpg
10:IPISR中断状态寄存器(offset=0x20)
544e1de3228b486bb218db4e97bd1752.jpg
fce1f4ed1170410bab7818ee3c79d563.jpg
ab1eb4faf8904a9d8e337559b11e181d.jpg
30b8ba41711646afb2eafdbb9c53cb47.jpg
5be31d0b514f4be4889aadde52ec3107.jpg
11:IPIER中断使能寄存器(offset=0x28)
c81f79c7c4ab4309a2a63173c6559c8d.jpg
af7b6ccc5bc94ee7b56b7ea61af06a1a.jpg
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就可以完成环路测试.
445e6102cc174b2b99b530f63a59b4b9.jpg
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。
d7a684a394cd435683114bedcd15b03f.jpg
5搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。
5.1SOC系统工程
d8842c33969b441d978bdf9007e512e3.jpg
1:中断设置
fe289fbd2acd4e7793fcd7a599bed9be.jpg
2:设置GP Master接口
9378dd271df446faab5f2dfa594429cf.jpg
3:设置复位输出
bac0a701e3374e8b99c57b7bf8745f86.jpg
4:设置PL时钟
d6e2f0ccfe8b40f6b2546c94b4fe9efd.jpg
5:AXI-SPI IP
51452d74ed4a4963a81d5005f1b6752c.jpg
6:ILA IP
采样深度选择合适 数值,对于BRAM足够的FPGA可以设置大一些
cbb6d082df2442b499094d7b1cd81742.jpg
5.2设置AXI外设地址分配
只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏
5409b2f394da455abbec1aec57d232a9.jpg
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平台。
b99b337881324817acc2eecdc5f67e67.jpg
6搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
6.1创建SDK Platform工程
8950f2cdf20840d1b782caea189d1806.jpg
右击soc_base编译,编译的时间可能会有点长
6.2创建APP工程
613157622b23422f9733ba9e392126f3.jpg
7程序分析
7.1polled模式spi_test.c程序
  1. #include "spipl_polled.h"
  2. #include "sleep.h"

  3. u8 ReadBuf[MAX_DATA];
  4. u8 SendBuf[MAX_DATA];

  5. int main(void)
  6. {
  7.         int i =0;

  8.         xil_printf("spi loop polled test start!\r\n");

  9.         Spipl_init(SPI_DEVICE_ID);//初始SPI

  10.         for(i=0;i<10;i++)//产生测试数据
  11.                 SendBuf[i]=i;

  12. //启动发送,发送10个字节,和接收10个字节
  13.         Spipl_Transfer(&SpiInstance,SendBuf,ReadBuf,10);

  14. //打印接收到的数据
  15.         for(i=0;i<10;i++)
  16.         {
  17.                 xil_printf("ReadBuf[%d]=%d\r\n",i,ReadBuf[i]);
  18.         }

  19.         xil_printf("spi loop polled test finished!\r\n");
  20.         return 0;
  21. }
复制代码

通过函数Spipl_Transfer(&SpiInstance,SendBuf,ReadBuf,10)完成数据的发送后接收,如果读者了解SPI协议,应该直到标准SPI数据的发送和接收时同时进行的,如果读数据也时要启动一次发送操作。如果有不清楚的可以阅读“SPI环路测试实验”。
7.2spipl_polled.c程序
  1. /********************MILIANKE**************************

  2. *Company:Liyang Milian Electronic Technology Co., Ltd
  3. *Technical forum:www.uisrc.com
  4. *Taobao: https://milianke.taobao.com
  5. *Create Date: 2020/12/01
  6. *Module Name:spi_loop_polled
  7. *Copyright: Copyright (c) milianke
  8. *Revision: 1.1
  9. *Description:

  10. ****************************************************/

  11. #include "spipl_polled.h"

  12. int Spipl_init(u16 SpiDeviceId)
  13. {
  14.         int Status;
  15.         XSpi_Config *ConfigPtr;        /* Pointer to Configuration data */

  16.         /*
  17.          * Initialize the SPI driver so that it is  ready to use.
  18.          */
  19.         ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
  20.         if (ConfigPtr == NULL) {return XST_DEVICE_NOT_FOUND;}

  21.         Status = XSpi_CfgInitialize(&SpiInstance, ConfigPtr,ConfigPtr->BaseAddress);
  22.         if (Status != XST_SUCCESS) {return XST_FAILURE;}

  23.         /*
  24.          * Run loopback test only in case of standard SPI mode.
  25.          */
  26.         if (SpiInstance.SpiMode != XSP_STANDARD_MODE) {
  27.                 return XST_SUCCESS;
  28.         }

  29.         /*
  30.          * Set the Spi device as a master and in loopback mode.
  31.          */
  32.         //Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION |XSP_LOOPBACK_OPTION);
  33.         Status = XSpi_SetOptions(&SpiInstance, XSP_MASTER_OPTION);//设置SPI的工作模式,使用外部MASTER模式
  34.         if (Status != XST_SUCCESS) {
  35.                 return XST_FAILURE;
  36.         }
  37. //在主模式下,这个函数一定要执行,设置Slave选择寄存器
  38.         XSpi_SetSlaveSelect(&SpiInstance,1);
  39. //启动SPI控制器
  40.         XSpi_Start(&SpiInstance);
  41. //禁用SPI中断
  42.         XSpi_IntrGlobalDisable(&SpiInstance);
  43. }


  44. int Spipl_Transfer(XSpi *InstancePtr, u8 *SendBufPtr,u8 *RecvBufPtr, unsigned int ByteCount)
  45. {
  46.         int Status;
  47. //在主模式下,SPI启动一次发送的同时也会启动接收,所以要读数据也要启动一次发送,这里正好可以发送的数据直接外部环路环回
  48.         Status = XSpi_Transfer(InstancePtr, SendBufPtr, RecvBufPtr, ByteCount);
  49.         if (Status != XST_SUCCESS) {return XST_FAILURE;}
  50. }
复制代码

spipl_polled.c程序中我们重点看下以下几个函数。
1:XSpi_SetOptions(&SpiInstance, XSP_MASTER_OPTION)
这个函数设置PSI控制器为主模式
97c10641ca0044c0872312d49012c3cb.jpg
2:XSpi_SetSlaveSelect(&SpiInstance,1)
在主模式下,这个函数一定要执行,设置Slave选择寄存器,其实就是设置了InstancePtr->SlaveSelectReg,后面的数据传输函数中会设置这个寄存器。
cfddcf3c8a724da8a7360a8101a5c5d0.jpg
3:XSpi_Start(&SpiInstance)函数启动SPI控制器

53bf1fc1402947d6932ac45ce0a55009.jpg
4:XSpi_Transfer(InstancePtr,SendBufPtr,RecvBufPtr,ByteCount)数据收发
这个函数中有关于主模式下,是否选设置了InstancePtr->SlaveSelectReg参数的判断
b77a3ccad1a54666a161d366cfce3f83.jpg
之后设置slave select寄存器
074bf061ae7e4f8689bda620a4c4ab92.jpg
最后再while大循环完成数据的发送后接收。
9e3ec8f25a70490188988f7c5da74179.jpg

15c528174c7f47be81c78118622ad335.jpg
7.3intrrupt模式spi_test.c程序
  1. #include "sys_intr.h"
  2. #include "spipl_intr.h"

  3. u8 ReadBuf[100];
  4. u8 SendBuf[100];

  5. void init_intr_sys(void)
  6. {
  7.         Init_Intr_System(&Intc);//初始化系统中断

  8.         SpiIntr_Init(&SpiInstance,SPI_DEVICE_ID); //初始化SPI

  9.         Spipl_Setup_Intr(&Intc, &SpiInstance, SPI_INTR_ID); //初始化SPI PL中断

  10.         Setup_Intr_Exception(&Intc);
  11. }

  12. int main(void)
  13. {
  14.         int i =0;

  15.         xil_printf("spi loop interrupt test start!\r\n");

  16.         init_intr_sys();

  17.         for(i=0;i<10;i++)//产生测试数据
  18.                 SendBuf[i]=i;
  19.         TransferInProgress = TRUE;
  20.         Spipl_Transfer(&SpiInstance,SendBuf,ReadBuf,10); //启动SPI数据的发送和接收
  21.         while(TransferInProgress); //在中断状态回调函数中,设置该变量

  22. //打印接收到的数据
  23.         for(i=0;i<10;i++)
  24.         {
  25.                 xil_printf("ReadBuf[%d]=%d\r\n",i,ReadBuf[i]);
  26.         }

  27.         xil_printf("spi loop interrupt test finished!\r\n");
  28.         return 0;
  29. }
复制代码

7.4spipl_intr.c程序
  1. #include "spipl_intr.h"

  2. volatile int TransferInProgress;

  3. int SpiIntr_Init(XSpi *SpiInstancePtr,u16 SpiDeviceId)
  4. {
  5.         int Status;
  6.         XSpi_Config *ConfigPtr;        /* Pointer to Configuration data */

  7.         ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
  8.         if (ConfigPtr == NULL) {return XST_DEVICE_NOT_FOUND;}

  9.         Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr,ConfigPtr->BaseAddress);
  10.         if (Status != XST_SUCCESS) {return XST_FAILURE;}
  11.         /*
  12.          * Run loopback test only in case of standard SPI mode.
  13.          */
  14.         if (SpiInstancePtr->SpiMode != XSP_STANDARD_MODE) {return XST_SUCCESS;}

  15. //Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION |XSP_LOOPBACK_OPTION);
  16. //设置SPI模式为MASTER模式,外部模式
  17.         Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION);
  18.         if (Status != XST_SUCCESS) {return XST_FAILURE;}

  19. //在主模式下,这个函数一定要执行,设置Slave选择寄存器
  20.         XSpi_SetSlaveSelect(SpiInstancePtr,1);
  21. //启动SPI控制器
  22.         XSpi_Start(SpiInstancePtr);

  23.         //SpiDisableIntrSystem(IntcInstancePtr, SpiIntrId);

  24.         return XST_SUCCESS;
  25. }

  26. void Spipl_IntrHandler(void *CallBackRef, u32 StatusEvent, u32 ByteCount)
  27. {

  28.         TransferInProgress = FALSE;

  29.         if (StatusEvent != XST_SPI_TRANSFER_DONE) {
  30.                 Error++;
  31.         }
  32. }

  33. //设置PL中断
  34. int Spipl_Setup_Intr(XScuGic *IntcInstancePtr, XSpi *SpiInstancePtr, u16 SpiIntrId)
  35. {
  36.         int Status;

  37. //设置SPI的中断状态回调函数
  38.         XSpi_SetStatusHandler(SpiInstancePtr, SpiInstancePtr,(XSpi_StatusHandler) Spipl_IntrHandler);

  39. //设置AXI-SPI对应的PL中断
  40.         XScuGic_SetPriorityTriggerType(IntcInstancePtr, SpiIntrId, 0xA0, 0x3);
  41. //设置中断回调函数,在该函数中,进一步调用Spipl_IntrHandler函数
  42.         Status = XScuGic_Connect(IntcInstancePtr, SpiIntrId,(Xil_InterruptHandler)XSpi_InterruptHandler,SpiInstancePtr);
  43.         if (Status != XST_SUCCESS) {return Status;}

  44. //使能SPI中断
  45.         XScuGic_Enable(IntcInstancePtr, SpiIntrId);

  46.         return XST_SUCCESS;
  47. }

  48. //SPI数据收发
  49. int Spipl_Transfer(XSpi *InstancePtr, u8 *SendBufPtr,u8 *RecvBufPtr, unsigned int ByteCount)
  50. {
  51.         int Status;
  52. //在主模式下,SPI启动一次发送的同时也会启动接收,所以要读数据也要启动一次发送,这里正好可以发送的数据直接外部环路环回
  53.         Status = XSpi_Transfer(InstancePtr, SendBufPtr, RecvBufPtr, ByteCount);
  54.         if (Status != XST_SUCCESS) {return XST_FAILURE;}
  55. }

  56. void Spipl_Disable_Intr(XScuGic *IntcInstancePtr, u16 SpiIntrId)
  57. {

  58.         XScuGic_Disconnect(IntcInstancePtr, SpiIntrId);
  59.         
  60. }
复制代码

这段程序中,大部分和polled模式一样,增加的中断部分主要增加了中断回调函数的设置。由于采用了中断模式,只有当中断回调函数被调用后,TransferInProgress = FALSE代表一次传输完成。
8方案演示

8.1硬件准备
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON(注意新版本的 MLK_H3_CZ08-7100-MZ7100FC),支持 JTAG 模式,对于老版本的核心板,JTAG 调试的时候 一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
设置短接帽如下图
1f44d4fcb09e468298e5cae613e676ad.jpg
8.2实验结果

590d157fbba645b3ae135d7bce1169e4.jpg
abcfff0e171247538eedd2365d171aa5.jpg
通过在线逻辑分析仪查看波形
50a6007f005c4cfc85e751c8c3f417a8.jpg

























您需要登录后才可以回帖 登录 | 立即注册

本版积分规则