` `在使用ZYNQ7021系列的FPGA,若涉及到PL部分读写DDR,可使用过AXI-Lite,AXI4-FULL,AXI-Stream这三个IP来实现,使用的是这三个IP的主机模式。
## AXI 4总线协议解析
* [ ] AXI4:主要面向高性能地址映射通信的需求;
* [ ] AXI4-Lite:是一个简单地吞吐量地址映射性通信总线;
* [ ] AXI4-Stream:面向高速流数据传输;
* [ ] AXI4总线分为主、从两端,两者间可以连续的进行通信;
` `AXI 4总线采用READY,VALID握手通信机制,主设备收到从设备发送的READY,主设备将数据和VALID信号同时发送给从设备。
` `AXI 4-Lite所有的猝发长度为1,数据总线宽度为32位或64位
` `AXI 4-Stream数据总线宽度:8、16、32、64、128、256、512和1024位;
**axi总线分为五个通道:**
* --读地址通道,包含ARVALID,ARADDR, ARREADY信号;
* --写地址通道,包含AWVALID,AWADDR,AWREADY信号;
* --读数据通道,包含RVALID,RDATA, RREADY, RRESP信号;
* --写数据通道,包含WVALID, WDATA,WSTRB,WREADY信号;
* --写应答通道,包含BVALID,BRESP, BREADY信号;
` `AWLEN,猝发长度,是一个猝发中传送的数据个数,传送个数=AWLEN+1;如AWLEN=5,传送6个数据。
` `AWSIZE,猝发大小,猝发中每个传送的数据的大小,字节数为=2^AWSIZE;如AWSIZE=0,每个数据是1个字节,AWSIZE=3,每个数据大小是8个字节
;
**axis信号分为:**
* --TREADY信号:从告诉主做好传输准备;
* --TVALID信号:主告诉从数据传输有效;
* --TLAST信号:主告诉从该次传输为突发传输结尾
* --TDATA信号:数据,可选宽度32,64,128,256bit
* --TSTRB信号:为1的bit为对应tdata有效字节,宽度为tdata/8
* --TUSER信号 :用户定义信号,宽度为128bit
* --ACLK信号:总线时钟,上升沿有效;
* --ARESETn信号:总线复位,低电平有效;
` `读操作
![](https://img.kancloud.cn/a7/69/a769a8fccdd6d97164e8f76c89d1ffc6_583x243.png)
` `在读交易中,主设备先发送ARADDR和ARVALID给从设备,从设备回发ARREADY,通知主设备该地址有效,当ARVALID和ARREADY都为高电平时,主设备发出RREADY,表示主设备准备好接受读数据和相应信号了。从设备发送RVALID、RDATA以及RRESP,当RVALID和RREADY都为高电平时,数据被写入主设备。
` `写操作
![](https://img.kancloud.cn/e9/8a/e98ac9f8d422cc8f910b0a11f002f5f9_600x325.png)
` `在写操作中,主设备往从设备中写入AWADDR和AWVALID,然后主设备并没有等待从设备的AWREADY,而是继续发送WVALID和WDATA,当从设备回应AWREADY有效后,紧接着从设备发送WREADY表示从设备准备好接受数据,当WVALID和WREADY都为高电平是数据写入从设备。主设备发送的AWVALID和WVALID要有重叠区。
## 关于同时对DDR写操作的说明
` `在进行数据存储时,有时候有几类数据需要写入DDR,并且这些数据相互间没有相关性,比如将AD芯片K5394获取的数据存入地址为0x02000000,长度为0xf,将时间信息存入0x020000f0,长度为0XF0的DDR空间,这两者间数据基本无相关性,即获得了数据就将其写入DDR中,在使用这些IP来写数据时不需要考虑数据写入之间的竞争情况,因为AXI的IP模块有自己的WID,不同的WID可以乱序写入,但是同一个WID需要顺序写入,模块将会根据WID来X向DDR写数据。同时AK5394的数据得到为流水线形式,且得到两个数据之间的时间较大,因此这种的数据适合使用AXI-Lite来进行数据传输。
` `验证例程为pl_write_ddr_lite,其中MCU_2_FPGA的IP为修改的axi-lite的slave的IP,用于PS向PL部分发送数据。
![](https://img.kancloud.cn/4f/ab/4fab9ee493820340c482b518bfd7db57_369x291.png)
` `axi_lite_wrddr模块是修改的AXI-Lite的Master的IP,用于实现想DDR的某一地址写入数据。
![](https://img.kancloud.cn/b4/a8/b4a82ff99d50caa1bb27628a1ce3dac9_398x234.png)
` `在使用时,由于对该IP进行了修改,若设置多次传输数据,将会对同一地址写入相同的数据,该模块主要是为了对一个地址写一个数据,不进行突发数据写入,因此需要设置写入次数为1次,如下图所示。同时写入的地址和写入的数据被引出,方便使用。
![](https://img.kancloud.cn/f1/67/f167a16beb2402e8a77d2410fa89af6e_952x669.png)
## HP和GP接口
` `在 ZYNQ 芯片内部用硬件实现了 AXI 总线协议,包括 9 个物理接口,分别为 AXI-GP0~AXI-
GP3,AXI-HP0~AXI-HP3,AXI-ACP 接口。
` `AXI_ACP 接口,是 ARM 多核架构下定义的一种接口,中文翻译为加速器一致性端口,用
来管理 DMA 之类的不带缓存的 AXI 外设,PS 端是 Slave 接口。
` `AXI_HP 接口,是高性能/带宽的 AXI3.0 标准的接口,总共有四个,PL 模块作为主设备连
接。主要用于 PL 访问 PS 上的存储器(DDR 和 On-Chip RAM)
` `AXI_GP 接口,是通用的 AXI 接口,总共有四个,包括两个 32 位主设备接口和两个 32 位
从设备接口。
![](https://img.kancloud.cn/ba/1a/ba1adcec15cd1c81ee537e5ddfd2ce72_986x567.png)
` `可以看到,只有两个 AXI-GP 是 Master Port,即主机接口,其余 7 个口都是 Slave Port(从
机接口)。主机接口具有发起读写的权限,ARM 可以利用两个 AXI-GP 主机接口主动访问 PL 逻
辑,其实就是把 PL 映射到某个地址,读写 PL 寄存器如同在读写自己的存储器。其余从机接口
就属于被动接口,接受来自 PL 的读写,逆来顺受。
` `另外这 9 个 AXI 接口性能也是不同的。GP 接口是 32 位的低性能接口,理论带宽
600MB/s,而 HP 和 ACP 接口为 64 位高性能接口,理论带宽 1200MB/s。有人会问,为什么高
性能接口不做成主机接口呢?这样可以由 ARM 发起高速数据传输。答案是高性能接口根本不
需要 ARM CPU 来负责数据搬移,真正的搬运工是位于 PL 中的 DMA 控制器。
## 几个AXI IP介绍
` `下面为几个常用的 AXI 接口 IP 的功能介绍:
` `AXI-DMA:实现从 PS 内存到 PL 高速传输高速通道 AXI-HP<---->AXI-Stream 的转换
。
` `AXI-FIFO-MM2S:实现从 PS 内存到 PL 通用传输通道 AXI-GP<----->AXI-Stream 的转换
。
` `AXI-Datamover:实现从 PS 内存到 PL 高速传输高速通道 AXI-HP<---->AXI-Stream 的转换,只
不过这次是完全由 PL 控制的,PS 是完全被动的。
` `AXI-VDMA:实现从 PS 内存到 PL 高速传输高速通道 AXI-HP<---->AXI-Stream 的转换,只不
过是专门针对视频、图像等二维数据的。
` `AXI-CDMA:这个是由 PL 完成的将数据从内存的一个位置搬移到另一个位置,无需 CPU 来
插手。
` `AXI 协议严格的讲是一个点对点的主从接口协议,当多个外设需要互相交互数据时,我们
需要加入一个 AXI Interconnect 模块,也就是 AXI 互联矩阵,作用是提供将一个或多个 AXI 主设
备连接到一个或多个 AXI 从设备的一种交换机制(有点类似于交换机里面的交换矩阵)。
` `这个 AXI Interconnect IP 核最多可以支持 16 个主设备、16 个从设备,如果需要更多的接
口,可以多加入几个 IP 核。
## 多个AXI模块并存时的地址分配
` `当使用多个AXI的IP(主机/从机),需要对地址进行映射。
![](https://img.kancloud.cn/2f/36/2f36d1a39d3901182bb2f9849d3c6e98_943x773.png)
` `需要对每一个主机会映射的从机进行地址分配,如硬核PS的AXI主机接口需要连接MCU_2_FPGA的IP,那么就对这哥IP进行地址分配。axi_lite_wrddr需要映射到PS的HP接口,则对HP进行地址分配。
![](https://img.kancloud.cn/56/95/569561c5bcf608f194fd53f580111f4a_873x450.png)
` `但是需要注意的是,两个IP不要对重复地址段进行写数据,因为这样会造成数据混乱。
## 测试程序说明
` `main文件代码如下:
```
/*
* helloworld.c: simple test application
*
* This application configures UART 16550 to baud rate 9600.
* PS7 UART (Zynq) is not initialized by this application, since
* bootrom/bsp configures it to baud rate 115200
*
* ------------------------------------------------
* | UART TYPE BAUD RATE |
* ------------------------------------------------
* uartns550 9600
* uartlite Configurable only in HW design
* ps7_uart 115200 (configured by bootrom/bsp)
*/
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xil_io.h"
#include "xparameters.h"
#include "xparameters_ps.h"
#include "mcu_2_fpga.h"
#include "stdbool.h"
void mcu2fpga_write(int id,u32 data)
{
switch(id){
case(0):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG0_OFFSET,data);break;
case(1):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG1_OFFSET,data);break;
case(2):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG2_OFFSET,data);break;
case(3):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG3_OFFSET,data);break;
default:break;
}
}
void mcu2fpga1_write(int id,u32 data)
{
switch(id){
case(0):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_1_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG0_OFFSET,data);break;
case(1):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_1_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG1_OFFSET,data);break;
case(2):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_1_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG2_OFFSET,data);break;
case(3):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_1_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG3_OFFSET,data);break;
default:break;
}
}
void mcu2fpga2_write(int id,u32 data)
{
switch(id){
case(0):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_2_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG0_OFFSET,data);break;
case(1):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_2_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG1_OFFSET,data);break;
case(2):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_2_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG2_OFFSET,data);break;
case(3):MCU_2_FPGA_mWriteReg(XPAR_MCU_2_FPGA_2_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG3_OFFSET,data);break;
default:break;
}
}
u32 mcu2fpga_read(int id)
{
switch(id){
case(0):return MCU_2_FPGA_mReadReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG0_OFFSET);break;
case(1):return MCU_2_FPGA_mReadReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG1_OFFSET);break;
case(2):return MCU_2_FPGA_mReadReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG2_OFFSET);break;
case(3):return MCU_2_FPGA_mReadReg(XPAR_MCU_2_FPGA_0_S00_AXI_BASEADDR,MCU_2_FPGA_S00_AXI_SLV_REG3_OFFSET);break;
default:break;
}
return 12;
}
//分配DDR中对应AD数据的内存地址给数组
u32 ak5394_m1[60] __attribute__((section(".ak5394M1Section")));//hpy test PS read DDR
u32 ak5394_m2[60] __attribute__((section(".ak5394M2Section")));//hpy test PS read DDR
void pl_update_ddr(u32 addr,int offset,_Bool display)//测试PL写DDR,用于发送数据到FPGA更新DDR中的数据
{
for(int j = 0;j<30;j++){
mcu2fpga_write(3,4*j + offset);
mcu2fpga_write(2,4*j+1 + offset);
mcu2fpga_write(1,4*j+2 + offset);
usleep(10);
mcu2fpga_write(0,addr + j*16);
if(display == true){
printf("data[%d] = %d\n",4*j,4*j + offset);
printf("data[%d] = %d\n",4*j+1,4*j+1 + offset);
printf("data[%d] = %d\n",4*j+2,4*j+2 + offset);
printf("data[%d] = %d\n",4*j+3,4*j+3 + offset);
}
usleep(100);
}
}
//利用MCU_2_FPGA向PL发送数据,然后PL将数据写入DDR
void lite_write(u32 ad,int off)、{
for(int i=0;i<30;i++){
mcu2fpga2_write(1,i+off);
mcu2fpga1_write(1,i+off+1);
mcu2fpga2_write(0,ad+i*4);
mcu2fpga1_write(0,ad+i*4 + 120);
usleep(100);
}
pl_update_ddr(0x020000F0,off,false);
}
void write_ddr_lite_test(){
int i = 0;
int mdata = 5;
u32 ad0 = 0x02000000;
u32 ad = ad0;
int cnt=0;
while(1){
lite_write(ad,cnt);
if(cnt>=100)cnt = 0;
else cnt++;
printf("--------------------------\n");
usleep(1000000);
Xil_DCacheInvalidateRange(ad0,sizeof(ak5394_m1));//将DDR中数据更新到cache中
Xil_DCacheInvalidateRange(0x020000F0,sizeof(ak5394_m2));//将DDR中数据更新到cache中
for(u32 j=0;j<30;j++){// PS读取DDR并显示
printf("a[%d]=%d a[%d]=%d b[%d]=%d\n ",j,ak5394_m1[j],j+30,ak5394_m1[j+30],j,ak5394_m2[j]);
}
}
}
int main()
{
init_platform();
int max = 7;
int i = 1;
u32 addr = 0x02000000;
u32 addr2 =0x02000000;
write_ddr_lite_test();
pl_update_ddr(addr,0,false);//测试函数
cleanup_platform();
return 0;
}
```
## link脚本文件编写
` `为了方便数据读出,将写入DDR的地址段分配给数组,得益于GCC的强大功能,通过``__attribute__``可以实现给数组分配指定地址空间。
` `文件编写如下
```
/*******************************************************************/
/* */
/* This file is automatically generated by linker script generator.*/
/* */
/* Version: 2018.3 */
/* */
/* Copyright (c) 2010-2016 Xilinx, Inc. All rights reserved. */
/* */
/* Description : Cortex-A9 Linker Script */
/* */
/*******************************************************************/
_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000;
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x2000;
_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024;
_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048;
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024;
_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024;
_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024;
/* Define Memories in the system */
MEMORY
{
ps7_ddr_0 : ORIGIN = 0x100000, LENGTH = 0x1f00000 /*0x3FF00000*/
ps7_ram_0 : ORIGIN = 0x0, LENGTH = 0x30000
ps7_ram_1 : ORIGIN = 0xFFFF0000, LENGTH = 0xFE00
/*以下为自己定义,需要分配的地址段*/
AK5394_M1 : ORIGIN = 0x02000000, LENGTH = 0x000000F0
AK5394_M2 : ORIGIN = 0x020000F0, LENGTH = 0x000000F0
}
/* Specify the default entry point to the program */
ENTRY(_vector_table)
/* Define the sections, and where they are mapped in memory */
SECTIONS
{
.ak5394M1Section :{
__ak5394Section_start = .;
*(.ak5394M1Section)
__ak5394Section_end = .;
} > AK5394_M1
.ak5394M2Section :{
__ak5394Section_start = .;
*(.ak5394M2Section)
__ak5394Section_end = .;
} > AK5394_M2
.text : {
KEEP (*(.vectors))
*(.boot)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.plt)
*(.gnu_warning)
*(.gcc_execpt_table)
*(.glue_7)
*(.glue_7t)
*(.vfp11_veneer)
*(.ARM.extab)
*(.gnu.linkonce.armextab.*)
} > ps7_ddr_0
.init : {
KEEP (*(.init))
} > ps7_ddr_0
.fini : {
KEEP (*(.fini))
} > ps7_ddr_0
.rodata : {
__rodata_start = .;
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
__rodata_end = .;
} > ps7_ddr_0
.rodata1 : {
__rodata1_start = .;
*(.rodata1)
*(.rodata1.*)
__rodata1_end = .;
} > ps7_ddr_0
.sdata2 : {
__sdata2_start = .;
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
__sdata2_end = .;
} > ps7_ddr_0
.sbss2 : {
__sbss2_start = .;
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
__sbss2_end = .;
} > ps7_ddr_0
.data : {
__data_start = .;
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.jcr)
*(.got)
*(.got.plt)
__data_end = .;
} > ps7_ddr_0
.data1 : {
__data1_start = .;
*(.data1)
*(.data1.*)
__data1_end = .;
} > ps7_ddr_0
.got : {
*(.got)
} > ps7_ddr_0
.ctors : {
__CTOR_LIST__ = .;
___CTORS_LIST___ = .;
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__CTOR_END__ = .;
___CTORS_END___ = .;
} > ps7_ddr_0
.dtors : {
__DTOR_LIST__ = .;
___DTORS_LIST___ = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
__DTOR_END__ = .;
___DTORS_END___ = .;
} > ps7_ddr_0
.fixup : {
__fixup_start = .;
*(.fixup)
__fixup_end = .;
} > ps7_ddr_0
.eh_frame : {
*(.eh_frame)
} > ps7_ddr_0
.eh_framehdr : {
__eh_framehdr_start = .;
*(.eh_framehdr)
__eh_framehdr_end = .;
} > ps7_ddr_0
.gcc_except_table : {
*(.gcc_except_table)
} > ps7_ddr_0
.mmu_tbl (ALIGN(16384)) : {
__mmu_tbl_start = .;
*(.mmu_tbl)
__mmu_tbl_end = .;
} > ps7_ddr_0
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
*(.gnu.linkonce.armexidix.*.*)
__exidx_end = .;
} > ps7_ddr_0
.preinit_array : {
__preinit_array_start = .;
KEEP (*(SORT(.preinit_array.*)))
KEEP (*(.preinit_array))
__preinit_array_end = .;
} > ps7_ddr_0
.init_array : {
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
} > ps7_ddr_0
.fini_array : {
__fini_array_start = .;
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array))
__fini_array_end = .;
} > ps7_ddr_0
.ARM.attributes : {
__ARM.attributes_start = .;
*(.ARM.attributes)
__ARM.attributes_end = .;
} > ps7_ddr_0
.sdata : {
__sdata_start = .;
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
__sdata_end = .;
} > ps7_ddr_0
.sbss (NOLOAD) : {
__sbss_start = .;
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
__sbss_end = .;
} > ps7_ddr_0
.tdata : {
__tdata_start = .;
*(.tdata)
*(.tdata.*)
*(.gnu.linkonce.td.*)
__tdata_end = .;
} > ps7_ddr_0
.tbss : {
__tbss_start = .;
*(.tbss)
*(.tbss.*)
*(.gnu.linkonce.tb.*)
__tbss_end = .;
} > ps7_ddr_0
.bss (NOLOAD) : {
__bss_start = .;
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
__bss_end = .;
} > ps7_ddr_0
_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );
_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );
/* Generate Stack and Heap definitions */
.heap (NOLOAD) : {
. = ALIGN(16);
_heap = .;
HeapBase = .;
_heap_start = .;
. += _HEAP_SIZE;
_heap_end = .;
HeapLimit = .;
} > ps7_ddr_0
.stack (NOLOAD) : {
. = ALIGN(16);
_stack_end = .;
. += _STACK_SIZE;
. = ALIGN(16);
_stack = .;
__stack = _stack;
. = ALIGN(16);
_irq_stack_end = .;
. += _IRQ_STACK_SIZE;
. = ALIGN(16);
__irq_stack = .;
_supervisor_stack_end = .;
. += _SUPERVISOR_STACK_SIZE;
. = ALIGN(16);
__supervisor_stack = .;
_abort_stack_end = .;
. += _ABORT_STACK_SIZE;
. = ALIGN(16);
__abort_stack = .;
_fiq_stack_end = .;
. += _FIQ_STACK_SIZE;
. = ALIGN(16);
__fiq_stack = .;
_undef_stack_end = .;
. += _UNDEF_STACK_SIZE;
. = ALIGN(16);
__undef_stack = .;
} > ps7_ddr_0
_end = .;
}
```
` `然后利用`__attribute__`进行分配地址。`u32 ak5394_m1[60] __attribute__((section(".ak5394M1Section")));//hpy test PS read DDR`
## 串口显示验证
![](https://img.kancloud.cn/91/2b/912bcb2f11bca154de4ae933c4e6dd5e_1247x704.png)
- 序
- 第1章 Linux下开发FPGA
- 1.1 Linux下安装diamond
- 1.2 使用轻量级linux仿真工具iverilog
- 1.3 使用linux shell来读写串口
- 1.4 嵌入式上的linux
- 设备数教程
- linux C 标准库文档
- linux 网络编程
- 开机启动流程
- 1.5 linux上实现与树莓派,FPGA等通信的串口脚本
- 第2章 Intel FPGA的使用
- 2.1 特别注意
- 2.2 高级应用开发流程
- 2.2.1 生成二进制bit流rbf
- 2.2.2 制作Preloader Image
- 2.2.2.1 生成BSP文件
- 2.2.2.2 编译preloader和uboot
- 2.2.2.3 更新SD的preloader和uboot
- 2.3 HPS使用
- 2.3.1 通过JTAG下载代码
- 2.3.2 HPS软件部分开发
- 2.3 quartus中IP核的使用
- 2.3.1 Intel中RS232串口IP的使用
- 2.4 一些问题的解决方法
- 2.4.1 关于引脚的复用的综合出错
- 第3章 关于C/C++的一些语法
- 3.1 C中数组作为形参不传长度
- 3.2 汇编中JUMP和CALL的区别
- 3.3 c++中map的使用
- 3.4 链表的一些应用
- 3.5 vector的使用
- 3.6 使用C实现一个简单的FIFO
- 3.6.1 循环队列
- 3.7 C语言不定长参数
- 3.8 AD采样计算同频信号的相位差
- 3.9 使用C实现栈
- 3.10 增量式PID
- 第4章 Xilinx的FPGA使用
- 4.1 Alinx使用中的一些问题及解决方法
- 4.1.1 在Genarate Bitstream时提示没有name.tcl
- 4.1.2 利用verilog求位宽
- 4.1.3 vivado中AXI写DDR说明
- 4.1.4 zynq中AXI GPIO中断问题
- 4.1.5 关于时序约束
- 4.1.6 zynq的PS端利用串口接收电脑的数据
- 4.1.7 SDK启动出错的解决方法
- 4.1.8 让工具综合是不优化某一模块的方法
- 4.1.9 固化程序(双核)
- 4.1.10 分配引脚时的问题
- 4.1.11 vivado仿真时相对文件路径的问题
- 4.2 GCC使用Attribute分配空间给变量
- 4.3 关于Zynq的DDR写入byte和word的方法
- 4.4 常用模块
- 4.4.1 I2S接收串转并
- 4.5 时钟约束
- 4.5.1 时钟约束
- 4.6 VIVADO使用
- 4.6.1 使用vivado进行仿真
- 4.7 关于PicoBlaze软核的使用
- 4.8 vivado一些IP的使用
- 4.8.1 float-point浮点单元的使用
- 4.10 zynq的双核中断
- 第5章 FPGA的那些好用的工具
- 5.1 iverilog
- 5.2 Arduino串口绘图器工具
- 5.3 LabVIEW
- 5.4 FPGA开发实用小工具
- 5.5 Linux下绘制时序图软件
- 5.6 verilog和VHDL相互转换工具
- 5.7 linux下搭建轻量易用的verilog仿真环境
- 5.8 VCS仿真verilog并查看波形
- 5.9 Verilog开源的综合工具-Yosys
- 5.10 sublim text3编辑器配置verilog编辑环境
- 5.11 在线工具
- 真值表 -> 逻辑表达式
- 5.12 Modelsim使用命令仿真
- 5.13 使用TCL实现的个人仿真脚本
- 5.14 在cygwin下使用命令行下载arduino代码到开发板
- 5.15 STM32开发
- 5.15.1 安装Atollic TrueSTUDIO for STM32
- 5.15.2 LED闪烁吧
- 5.15.3 模拟U盘
- 第6章 底层实现
- 6.1 硬件实现加法的流程
- 6.2 硬件实现乘法器
- 6.3 UART实现
- 6.3.1 通用串口发送模块
- 6.4 二进制数转BCD码
- 6.5 基本开源资源
- 6.5.1 深度资源
- 6.5.2 FreeCore资源集合
- 第7章 常用模块
- 7.1 温湿度传感器DHT11的verilog驱动
- 7.2 DAC7631驱动(verilog)
- 7.3 按键消抖
- 7.4 小脚丫数码管显示
- 7.5 verilog实现任意人数表决器
- 7.6 基本模块head.v
- 7.7 四相八拍步进电机驱动
- 7.8 单片机部分
- 7.8.1 I2C OLED驱动
- 第8章 verilog 扫盲区
- 8.1 时序电路中数据的读写
- 8.2 从RTL角度来看verilog中=和<=的区别
- 8.3 case和casez的区别
- 8.4 关于参数的传递与读取(paramter)
- 8.5 关于符号优先级
- 第9章 verilog中的一些语法使用
- 9.1 可综合的repeat
- 第10章 system verilog
- 10.1 简介
- 10.2 推荐demo学习网址
- 10.3 VCS在linux上环境的搭建
- 10.4 deepin15.11(linux)下搭建system verilog的vcs仿真环境
- 10.5 linux上使用vcs写的脚本仿真管理
- 10.6 system verilog基本语法
- 10.6.1 数据类型
- 10.6.2 枚举与字符串
- 第11章 tcl/tk的使用
- 11.1 使用Tcl/Tk
- 11.2 tcl基本语法教程
- 11.3 Tk的基本语法
- 11.3.1 建立按钮
- 11.3.2 复选框
- 11.3.3 单选框
- 11.3.4 标签
- 11.3.5 建立信息
- 11.3.6 建立输入框
- 11.3.7 旋转框
- 11.3.8 框架
- 11.3.9 标签框架
- 11.3.10 将窗口小部件分配到框架/标签框架
- 11.3.11 建立新的上层窗口
- 11.3.12 建立菜单
- 11.3.13 上层窗口建立菜单
- 11.3.14 建立滚动条
- 11.4 窗口管理器
- 11.5 一些学习的脚本
- 11.6 一些常用的操作语法实现
- 11.6.1 删除同一后缀的文件
- 11.7 在Lattice的Diamond中使用tcl
- 第12章 FPGA的重要知识
- 12.1 面积与速度的平衡与互换
- 12.2 硬件原则
- 12.3 系统原则
- 12.4 同步设计原则
- 12.5 乒乓操作
- 12.6 串并转换设计技巧
- 12.7 流水线操作设计思想
- 12.8 数据接口的同步方法
- 第13章 小项目
- 13.1 数字滤波器
- 13.2 FIFO
- 13.3 一个精简的CPU( mini-mcu )
- 13.3.1 基本功能实现
- 13.3.2 中断添加
- 13.3.3 使用中断实现流水灯(实际硬件验证)
- 13.3.4 综合一点的应用示例
- 13.4.5 使用flex开发汇编编译器
- 13.4.5 linux--Flex and Bison
- 13.4 有符号数转单精度浮点数
- 13.5 串口调试FPGA模板