Petalinux 下使用 UIO 实现 AXI GPIO & AXI Stream FIFO 驱动

目录

  • 前言
  • Vivado 工程
    • 功能定义
    • 创建Vivado工程
  • Petalinux 配置
  • UIO GPIO 测试
  • AXI Stream FIFO IP UIO 驱动
    • 关于AXI Stream FIFO读取数据的补充
  • 结论

前言

瑟如电子TDC的很多用户在standalone环境下使用TDC,但近来也有客户在问,能否在linux下使用。在绝大多数的中低速应用下,一般都推荐用户使用axi stream fifo IP 来获取TDC IP产生的时间戳数据。当然也可以使用AXI DMA,虽然有官方的linux驱动,但是AXI DMA占用面积较大,资源较多,中低速环境下太浪费了。对于Linux驱动,我也才是菜鸟级别,照着示例写过一点字符型驱动。不过一套流程走下来,略显繁琐,而且不便于日后维护。一番比较研究之后,发现使用UIO的框架来实现更为简洁,如果不用中断功能的话,几乎不用在kernel里写程序。因此定下方向:使用UIO实现axi stream fifo 的数据读取。

本文主要的两个参考来源如下:

  1. 何晔: 当ZYNQ遇到Linux Userspace I/O

  2. Vacajk: ZYNQ中的UIO驱动和中断程序学习【Xilinx-Petalinux学习】

Vivado 工程

功能定义

  1. 使用UIO驱动通过AXI GPIO控制PL端的LED。

  2. 使用UIO驱动读取AXI Stream FIFO(AXI-Lite)的缓存内容

  3. 此外为了便于调试还需要Zynq具有以下端口:Ethernet, SD,
    UART。其中,Ethernet用于NFS,便于程序的远程运行与调试。

创建Vivado工程

工程基于黑金的AX7020开发板,FPGA型号设置如下图:

首先添加Zynq PS, 设置DDR、PS/PL MIO,
并使能PL到PS的中断端口。本示例外设的MIO设置如下图:

第二步,添加AXI GPIO IP,用于控制PL侧的LED。GPIO
IP位宽设为8位,低4位连接LED,高4位连接按键。并勾选Enable
Interrupt,添加中断输出端口。本示例只演示控制GPIO低4位的输出,以控制LED。按键输入及中断功能,如有兴趣可自行测试。

第三步,添加AXI Stream FIFO, 配置如下:

因为只用到接收功能,所以没有配置数据发送端口。数据接口格式设置为AXI4-Lite。

第四步,添加自定义IP,产生AXI Stream 数据,输出到AXI Stream
FIFO。在Vivado中点击Tools->Create and package New IP,选择创建AXI4外设:

为自定义IP选取数据端口:AXI Stream
Master。创建完成后,IP中已经包含了示例。该示例的功能为系统初始化后,等待一段时间然后对外发送32字节,也就是8个Word的数据,数据内容为0x01-0x08。可初步满足测试需求。

第五步,连接中断,并自动连接AXI端口。AXI GPIO 以及AXI Stream
FIFO的中断输出通过concate IP 连接到PS的中断接口。

最后,编写XDC文件,将GPIO的端口定义到对应的Led及Key上,并进行综合和实现,最终生成Bitstream。通过File->Export->
Export
Hardware,将硬件定义导出到指定的目录。记住这个目录,之后配置Petalinux的时候要用到。下图是Vivado
工程的Block Diagram。

Petalinux 配置

在这一节中,我们首先基于之前生成的Vivado工程,配置petalinux,使能UIO platform driver, 修改device tree,将AXI GPIO及AXI Stream FIFO的驱动改为generic-uio,最后生成用于从SD启动的镜像文件。

第一步,创建工程以及配置硬件描述文件:

source /opt/petalinux/settings.sh
petalinux-create -t project –-template zynq -n uio_test
cd uio_test
petalinux-config --get-hw-description ../xxxxxxx/

(…/xxxx是之前导出hardware的目录相对路径)

这一步没什么要修改的,保存,退出就可以。

第二步,配置内核:

petalinux-config -c kernel

Device Drivers —>Userspace I/O drivers —>

如上图勾选两个选项。

第三步,编译设备树:

petalinux-build -c device-tree

编译完成后,到petalinux 工程目录下.:

/components/plnx_workspace/device-tree-generation/pl.dtsi中可以看到系统自动识别出的PL侧的设备树信息。可以看到识别出来两个设备:分别为axi_fifo_mm_s_0 以及 axi_gpio_0。

/ {amba_pl: amba_pl {\#address-cells = \<1\>;\#size-cells = \<1\>;compatible = "simple-bus";ranges ;axi_fifo_mm_s_0: axi_fifo_mm_s\@43c00000 {compatible = "xlnx,axi-fifo-mm-s-4.1";interrupt-parent = \<\&intc\>;interrupts = \<0 30 4\>;reg = \<0x43c00000 0x10000\>;xlnx,axi-str-rxd-protocol = "XIL_AXI_STREAM_ETH_DATA";xlnx,axi-str-rxd-tdata-width = \<0x20\>;xlnx,axi-str-txc-protocol = "XIL_AXI_STREAM_ETH_CTRL";xlnx,axi-str-txc-tdata-width = \<0x20\>;xlnx,axi-str-txd-protocol = "XIL_AXI_STREAM_ETH_DATA";xlnx,axi-str-txd-tdata-width = \<0x20\>;xlnx,axis-tdest-width = \<0x4\>;xlnx,axis-tid-width = \<0x4\>;xlnx,axis-tuser-width = \<0x4\>;xlnx,data-interface-type = \<0x0\>;xlnx,has-axis-tdest = \<0x0\>;xlnx,has-axis-tid = \<0x0\>;xlnx,has-axis-tkeep = \<0x0\>;xlnx,has-axis-tstrb = \<0x0\>;xlnx,has-axis-tuser = \<0x0\>;xlnx,rx-fifo-depth = \<0x200\>;xlnx,rx-fifo-pe-threshold = \<0x2\>;xlnx,rx-fifo-pf-threshold = \<0x1fb\>;xlnx,s-axi-id-width = \<0x4\>;xlnx,s-axi4-data-width = \<0x20\>;xlnx,select-xpm = \<0x0\>;xlnx,tx-fifo-depth = \<0x200\>;xlnx,tx-fifo-pe-threshold = \<0x2\>;xlnx,tx-fifo-pf-threshold = \<0x1fb\>;xlnx,use-rx-cut-through = \<0x0\>;xlnx,use-rx-data = \<0x1\>;xlnx,use-tx-ctrl = \<0x0\>;xlnx,use-tx-cut-through = \<0x0\>;xlnx,use-tx-data = \<0x0\>;};axi_gpio_0: gpio\@41200000 {\#gpio-cells = \<2\>;\#interrupt-cells = \<2\>;compatible = "xlnx,xps-gpio-1.00.a";gpio-controller ;interrupt-controller ;interrupt-parent = \<\&intc\>;interrupts = \<0 29 4\>;reg = \<0x41200000 0x10000\>;xlnx,all-inputs = \<0x0\>;xlnx,all-inputs-2 = \<0x0\>;xlnx,all-outputs = \<0x0\>;xlnx,all-outputs-2 = \<0x0\>;xlnx,dout-default = \<0x00000000\>;xlnx,dout-default-2 = \<0x00000000\>;xlnx,gpio-width = \<0x8\>;xlnx,gpio2-width = \<0x20\>;xlnx,interrupt-present = \<0x1\>;xlnx,is-dual = \<0x0\>;xlnx,tri-default = \<0xFFFFFFFF\>;xlnx,tri-default-2 = \<0xFFFFFFFF\>;};};};

打开文件./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi,
将pl.dtsi中的内容复制进去,并将两个设备的compatible都改为compatible =
“generic-uio”;并在两个设备后添加:

chosen {bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";stdout-path = "serial0:115200n8";};

由于本示例没有用到中断,所以设备树里也没有添加中断的描述,如果需要用到,请参考参考资源(1).

第四步就可以编译petalinux工程了。

petalinux-build

编译完成后,生成镜像文件:

petalinux-package --boot --fsbl=./images/linux/zynq_fsbl.elf --fpga --u-boot --force

之后将petalinux工程目录下/images/linux/中的boot.bin和image.ub拷贝到SD卡上,插入AX7020开发板,设置好从SD卡启动的跳线。

为了实现NFS,将开发板接入与上位主机位于同一网段的局域网,启动板子。

来到登录界面后,输入用户名root, 密码root,进入系统。

UIO GPIO 测试

首先查看UIO设备

ls /dev/uio*:

根据设备树中的顺序,uio0 为axi stream fifo, uio1 为axi gpio。

编写GPIO测试程序

从vacajk的博文中复制过来的gpio-uio-test.c代码:

/** This application reads/writes GPIO devices with UIO.**/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>#define IN 0
#define OUT 1#define GPIO_MAP_SIZE 0x10000#define GPIO_DATA_OFFSET 0x00
#define GPIO_TRI_OFFSET 0x04
#define GPIO2_DATA_OFFSET 0x08
#define GPIO2_TRI_OFFSET 0x0C#define GIER 0x011C
#define IP_IER 0x0128
#define IP_ISR 0x0120void usage(void)
{printf("*argv[0] -d <UIO_DEV_FILE> -i|-o <VALUE>\n");printf("    -d               UIO device file. e.g. /dev/uio0");printf("    -i               Input from GPIO\n");printf("    -o <VALUE>       Output to GPIO\n");return;
}int main(int argc, char *argv[])
{int c;int fd;int direction=IN;char *uiod;int value = 0;int valued = 0;int irq_on = 1;void *ptr;printf("GPIO UIO test.\n");while((c = getopt(argc, argv, "d:io:h")) != -1) {switch(c) {case 'd':uiod=optarg;break;case 'i':direction=IN;break;case 'o':direction=OUT;valued=atoi(optarg);break;case 'h':usage();return 0;default:printf("invalid option: %c\n", (char)c);usage();return -1;}}/* Open the UIO device file */fd = open(uiod, O_RDWR);if (fd < 1) {perror(argv[0]);printf("Invalid UIO device file:%s.\n", uiod);usage();return -1;}/* mmap the UIO device */ptr = mmap(NULL, GPIO_MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);/* Print Interrupt Registers */value = *((unsigned *) (ptr + GIER));printf("%s: GIER: %08x\n",argv[0], value);value = *((unsigned *) (ptr + IP_IER));printf("%s: IP_IER: %08x\n",argv[0], value);value = *((unsigned *) (ptr + IP_ISR));printf("%s: IP_ISR: %08x\n",argv[0], value);/* Enable All Interrupts */printf("%s: Enable All Interrupts in Regs\n", argv[0]);*((unsigned *)(ptr + GIER)) = 0x80000000;*((unsigned *)(ptr + IP_IER)) = 0x3;*((unsigned *)(ptr + IP_ISR)) = 0x3;/* Enable UIO interrupt */write(fd, &irq_on, sizeof(irq_on));if (direction == IN) {/* Read from GPIO */*((unsigned *)(ptr + GPIO_TRI_OFFSET)) = 255;value = *((unsigned *) (ptr + GPIO_DATA_OFFSET));printf("%s: input: %08x\n",argv[0], value);} else {/* Write to GPIO */*((unsigned *)(ptr + GPIO_TRI_OFFSET)) = 0;value = valued;*((unsigned *)(ptr + GPIO_DATA_OFFSET)) = value;}/* Print Interrupt Registers */value = *((unsigned *) (ptr + GIER));printf("%s: GIER: %08x\n",argv[0], value);value = *((unsigned *) (ptr + IP_IER));printf("%s: IP_IER: %08x\n",argv[0], value);value = *((unsigned *) (ptr + IP_ISR));printf("%s: IP_ISR: %08x\n",argv[0], value);munmap(ptr, GPIO_MAP_SIZE);return 0;
}

在编译之前,我们先简单分析一下源代码:

首先,对输入的参数进行解析:
参数d
定义了UIO设备的文件路径,之后所有对UIO设备的操作,都是以这个文件为接口的。在本示例中,/dev/uio1代表了AXI GPIO, /dev/uio0代表了axi stream fifo。

参数i/o
指定了对GPIO是输入还是输出操作。

之后打开设备文件,通过

ptr = mmap(NULL, GPIO_MAP_SIZE, PROT_READ\|PROT_WRITE, MAP_SHARED, fd,0);

将设备寄存器地址映射到用户空间的虚拟地址上。对该虚拟地址进行读写操作,就能直接对设备寄存器进行读写操作。

忽略中断的内容直接看这一段:

 if (direction == IN) {/* Read from GPIO */*((unsigned *)(ptr + GPIO_TRI_OFFSET)) = 255;value = *((unsigned *) (ptr + GPIO_DATA_OFFSET));printf("%s: input: %08x\n",argv[0], value);} else {/* Write to GPIO */*((unsigned *)(ptr + GPIO_TRI_OFFSET)) = 0;value = valued;*((unsigned *)(ptr + GPIO_DATA_OFFSET)) = value;}

可见,当对GPIO执行输出操作时,先将对应的三态寄存器写入0,将GPIO设置为输出状态,然后再写入对应的数据寄存器。

接下来在Linux上位机中进行编译:

source /xxxxx/settings.sh

(/xxxx是petalinux所在的绝对路径)

arm-linux-gnueabihf-gcc uio_test.c -o uio_test2

生成了针对目标器件在linux下可执行文件uio_test2

下面要把uio_test2复制到Linux主机下的NFS目录,并在AX7020上挂载目录,并运行程序。

挂载NFS文件夹:

mount -t nfs -o nolock 192.168.1.12:/xxxx/Work_NFS /mnt

(/xxxx/Work_NFS 是用户自己设置的NFS绝对路径。NFS的设置不在本文范围之内,需要注意的是,如果像我一样采用虚拟机运行Linux host的,需要将虚拟机的网卡设为桥接模式。)

将可执行程序uio_test2 复制到/xxxx /Work_NFS。在串口终端中进入/mnt,如果NFS没有问题,可以看到刚刚复制的uio_test2。

接着我们就要运行程序,控制LED。之前提到AXI GPIO的低四位接到了PL侧的LED。而查看AX7020的电路图可以发现,LED对应的pin为低时点亮,高时熄灭。

先熄灭第一个LED,点亮其余;(从左侧第3个LED起为PL LED)

在串口终端中运行如下命令

uio_test2 -d /dev/uio1 -o 1

熄灭第2、3个LED,点亮其余:

uio_test2 -d /dev/uio1 -o 6

AXI Stream FIFO IP UIO 驱动

有了用UIO 驱动GPIO的经验,写读取AXI Stream FIFO的UIO驱动也就更直观了。在用户空间程序中,我们只要打开axi stream fifo对应的设备文件,映射内存,然后操作对应的寄存器即可。非常有参考价值的两份资料:一是SDK自带的axi stream fifo bare-metal 示例;二是IP的说明文档AXI4-Streaem FIFO IP Product Guide (PG080)。SDK的示例可从bsp的system.mss找到。

点击Import Example:

闲话少说,直接上代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>#define GPIO_MAP_SIZE 0x10000#define XLLF_ISR_OFFSET  0x00000000  /**< Interrupt Status */
#define XLLF_IER_OFFSET  0x00000004  /**< Interrupt Enable */#define XLLF_TDFR_OFFSET 0x00000008  /**< Transmit Reset */
#define XLLF_TDFV_OFFSET 0x0000000c  /**< Transmit Vacancy */
#define XLLF_TDFD_OFFSET 0x00000010  /**< Transmit Data */
#define XLLF_AXI4_TDFD_OFFSET   0x00000000  /**< Axi4 Transmit Data */
#define XLLF_TLF_OFFSET  0x00000014  /**< Transmit Length */#define XLLF_RDFR_OFFSET 0x00000018  /**< Receive Reset */
#define XLLF_RDFO_OFFSET 0x0000001c  /**< Receive Occupancy */
#define XLLF_RDFD_OFFSET 0x00000020  /**< Receive Data */
#define XLLF_AXI4_RDFD_OFFSET   0x00001000  /**< Axi4 Receive Data */
#define XLLF_RLF_OFFSET  0x00000024  /**< Receive Length */
#define XLLF_LLR_OFFSET  0x00000028  /**< Local Link Reset */
#define XLLF_TDR_OFFSET  0x0000002C  /**< Transmit Destination  */
#define XLLF_RDR_OFFSET  0x00000030  /**< Receive Destination  */#define XLLF_INT_RPURE_MASK       0x80000000 /**< Receive under-read */
#define XLLF_INT_RPORE_MASK       0x40000000 /**< Receive over-read */
#define XLLF_INT_RPUE_MASK        0x20000000 /**< Receive underrun (empty) */
#define XLLF_INT_TPOE_MASK        0x10000000 /**< Transmit overrun */
#define XLLF_INT_TC_MASK          0x08000000 /**< Transmit complete */
#define XLLF_INT_RC_MASK          0x04000000 /**< Receive complete */
#define XLLF_INT_TSE_MASK         0x02000000 /**< Transmit length mismatch */
#define XLLF_INT_TRC_MASK         0x01000000 /**< Transmit reset complete */
#define XLLF_INT_RRC_MASK         0x00800000 /**< Receive reset complete */
#define XLLF_INT_TFPF_MASK        0x00400000 /**< Tx FIFO Programmable Full,* AXI FIFO MM2S Only */
#define XLLF_INT_TFPE_MASK        0x00200000 /**< Tx FIFO Programmable Empty* AXI FIFO MM2S Only */
#define XLLF_INT_RFPF_MASK        0x00100000 /**< Rx FIFO Programmable Full* AXI FIFO MM2S Only */
#define XLLF_INT_RFPE_MASK        0x00080000 /**< Rx FIFO Programmable Empty* AXI FIFO MM2S Only */
#define XLLF_INT_ALL_MASK         0xfff80000 /**< All the ints */
#define XLLF_INT_ERROR_MASK       0xf2000000 /**< Error status ints */
#define XLLF_INT_RXERROR_MASK     0xe0000000 /**< Receive Error status ints */
#define XLLF_INT_TXERROR_MASK     0x12000000 /**< Transmit Error status ints */
/*@}*//** @name Reset register values*  These bits are associated with the XLLF_TDFR_OFFSET and XLLF_RDFR_OFFSET*  reset registers.* @{*/
#define XLLF_RDFR_RESET_MASK        0x000000a5 /**< receive reset value */
#define XLLF_TDFR_RESET_MASK        0x000000a5 /**< Transmit reset value */
#define XLLF_LLR_RESET_MASK         0x000000a5 /**< Local Link reset value */
/*@}*/void XLlFifo_IntClear(void* baseaddr, unsigned Mask)
{*(unsigned*)(baseaddr + XLLF_ISR_OFFSET) = ((Mask) & XLLF_INT_ALL_MASK);
}int RxReceive (void* baseaddr, unsigned* DestinationAddr)
{int i;int Status;unsigned RxWord;static unsigned ReceiveLength;printf(" Receiving data ....\n\r");/* Read Recieve Length */int occp = *(unsigned*)(baseaddr + XLLF_RDFO_OFFSET);printf("occp is %d \n\r", occp);if( occp){ReceiveLength = (*((unsigned*)(baseaddr + XLLF_RLF_OFFSET)) )/4;printf("Data Length: is %u \n\r", ReceiveLength);/* Start Receiving */for ( i=0; i < ReceiveLength; i++){RxWord = 0;RxWord = *(unsigned*)(baseaddr + XLLF_RDFD_OFFSET);*(DestinationAddr+i) = RxWord;printf("Data %u is %u \n\r", i, RxWord);}}return 0;
}void XLlFifo_RxReset(void* baseaddr)
{*(unsigned*)(baseaddr + XLLF_RDFR_OFFSET) = XLLF_RDFR_RESET_MASK;
}static unsigned dest_buffer[200];int main(int argc, char *argv[])
{int c;int fd;char *uiod;int value = 0;int valued = 0;void *ptr;printf("AXI Stream UIO test.\n");while((c = getopt(argc, argv, "d:")) != -1) {switch(c) {case 'd':uiod=optarg;printf("uiod %s \n\r", uiod);break;case 'i':printf("option i \n\r");break;default:printf("invalid option: %c\n", (char)c);return -1;}}/* Open the UIO device file */fd = open(uiod, O_RDWR);if (fd < 1) {perror(argv[0]);printf("Invalid UIO device file:%s.\n", uiod);return -1;}/* mmap the UIO device */ptr = mmap(NULL, GPIO_MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);close(fd);printf("mmaped address is %x \n\r",ptr);//XLlFifo_RxReset(ptr);//*(unsigned*)(ptr + XLLF_LLR_OFFSET) = XLLF_RDFR_RESET_MASK;//usleep(100);XLlFifo_IntClear(ptr, 0xffffffff);printf("xllfifo is intcleared\n\r");RxReceive(ptr,dest_buffer);munmap(ptr, GPIO_MAP_SIZE);}

数据读取逻辑在函数 int RxReceive中实现,实现逻辑如下:

  1. 读取RDFO,检查 receive FIFO occupancy,如果非0,继续

  2. 读取RLR(RLF),得到receive length in bytes(待接收的字节数,除以4,得到32bit
    word的个数)

  3. 循环读取RDFD, 每次读取一个32bit word, 直到读完receive length in
    bytes/4个word。

编译源文件:

arm-linux-gnueabihf-gcc axisfifo_test.c -o axisfifo_test

复制到NFS文件夹,在串口终端上运行,以下是串口终端的输出结果:

关于AXI Stream FIFO读取数据的补充

后续更加深入的测试表明,上述代码只能正常读取没有tlast信号的数据,当axis fifo 使能tlast信号后,每一次读取XLLF_RLF_OFFSET寄存器得到的数值表示一个数据包(用tlast信号区分)的字节数,而不是fifo中所有数据的字节长度。因此,需要将int RxReceive (void* baseaddr, unsigned* DestinationAddr)修改一下,见如下代码:

int RxReceive (void* baseaddr, u32* DestinationAddr)
{int i;int Status;unsigned RxWord;static u32 ReceiveLength;u32 totalLength = 0;//  printf(" Receiving data ....\n\r");/* Read Recieve Length */int occp = *(u32*)(baseaddr + XLLF_RDFO_OFFSET);while( occp){ReceiveLength = (*((u32*)(baseaddr + XLLF_RLF_OFFSET)) )/4;/* Start Receiving */for ( i=0; i < ReceiveLength; i++){RxWord = 0;RxWord = *(u32*)(baseaddr + XLLF_RDFD_OFFSET);*(DestinationAddr+totalLength) = RxWord;totalLength = totalLength + 1;}occp = *(u32*)(baseaddr + XLLF_RDFO_OFFSET);}return totalLength;
}

结论

简单的UIO驱动测试就结束了,接下来我会将TDC IP也放到设计中,实现通过UIO控制TDC IP以及时间戳数据的读取,敬请期待!

Petalinux 下使用 UIO 实现 AXI GPIO AXI Stream FIFO 驱动相关推荐

  1. zynq开发系列5:通过AXI GPIO的中断实现PL端按键控制PS端LED(SDK开发详解)

    axi_gpio是PL端gpio(FPGA资源搭建的软核),ps7_gpio是ps端gpio(硬核).打开Documentation的示例Examples,可知第二个是关于中断的示例.导入示例impo ...

  2. zynq开发系列5:通过AXI GPIO的中断实现PL端按键控制PS端LED

    在pg144-axi-gpio(LogiCORE IP Product Guide)中可以看见AXI GPIO提供通用输入输出接口到AXI接口,32位软核,设计与AXI4-Lite接口进行连接.IOP ...

  3. zynq文档阅读pg144-axi-gpio之AXI GPIO IP核

    在pg144-axi-gpio(LogiCORE IP Product Guide)中可以看见AXI GPIO提供通用输入输出接口到AXI接口,32位软核,设计与AXI4-Lite接口进行连接.IOP ...

  4. Xilinx AXI GPIO学习笔记以及问题点

    Xilinx AXI GPIO学习笔记以及问题点 1.问题总结 如上图,在Vivado中设置的GPIO IP只有一个,但是使用的两个Channel,此时在SDK中初始化和设置就容易出现问题. 1.1 ...

  5. xilinx ZYNQ 7000 AXI GPIO

    .0AXI GPIO 第一部分 PS 和 PL之间的通讯有一个接口称为AXI.AXI总线具体的内容这边不去深究,可以理解为一种特殊协议的通讯方式. AXI GPIO是什么意思? PL是FPGA它可以做 ...

  6. ZYNQ AXI GPIO中断实验——FPGA Vitis篇

    文章目录 1. 前言 2. Vivado工程的编写 2.1 Block Design工程设计 2.2 创建XDC管脚约束 3. Vitis工程的编写 4. 实验小结 5. 工程源码下载 1. 前言 使 ...

  7. Microblaze添加自定义IP核,挂AXI总线实现SSD1306 OELD驱动

    Microblaze添加自定义IP核,挂AXI总线,SSD1306 OELD驱动 前言 本着好好学习,认真负责的态度,我计划在空闲时间把自己用到的一些模块的使用方法与心得总结下与大家分享下,技术交流的 ...

  8. STM32单片机在Keil5下仿真的问题解决及GPIO口初始化、使用

    STM32单片机在Keil5下仿真的问题解决及GPIO口初始化.使用 参考文章: (1)STM32单片机在Keil5下仿真的问题解决及GPIO口初始化.使用 (2)https://www.cnblog ...

  9. Petalinux下SATA接口设计

    Zynq UltraScale+ MPSOC在PS侧扩展了PS-GTR接口,可以灵活的支持PCIe.SATA和USB3.0连接.我们在自己开发的板卡上支持了SATA数据接口. Step1:SATA数据 ...

最新文章

  1. Kubernetes理论基础
  2. 深度学习100例-卷积神经网络(CNN)彩色图片分类 | 第2天
  3. Java多线程设计模式(1)
  4. Java中的Serialization
  5. CG CTF WEB 密码重置2
  6. SpringBoot_数据访问-整合MyBatis(一)-基础环境搭建
  7. SAP CRM系统UI checkbox的设计与实现
  8. gradle 转 maven
  9. Graphviz的安装及纠错
  10. 视觉SLAM笔记(36) 3D-2D: PnP
  11. java的实现内部类实现链表
  12. 代码重构之旅(一) 项目结构
  13. 读取nacos_使用nacos配置多环境切换
  14. 多元函数法曲率和主曲率的几何解释
  15. mac安装win7之后鼠标失灵_mac安装win7鼠标失灵怎么办
  16. 理解“万事万物皆对象“
  17. Bazinga(HDU5510+KMP)
  18. 怎么给旧版本ios装旧版本软件
  19. 小白学 Python 爬虫(26):为啥上海二手房你都买不起
  20. ubuntu18.04返回桌面快捷键

热门文章

  1. MAC:外接其他接盘设置f1——f12功能键位
  2. 软件测试系列之单元测试 (转载)
  3. GHOST装机出现A:\GHOSTERR.TXT 问题分析和详解
  4. elasticsearch 索引创建脚本
  5. C/C++:变长参数技巧汇总
  6. 工业元宇宙讲座PPT讲义
  7. 将时间转换为16进制字符串或16进制小端模式byte数据
  8. ADM2483的原理图
  9. ASP.NET MVC 登录验证码
  10. 浅谈会话劫持原理及实践