ZYNQ_PS读写PL资源_base_on_pynqZ2
- 前言
- AXI总线寄存器模块
- 硬件连接
- 软件设计
- 总结
前言
最近比较系统的学习了zynq,内容还是很多的,不过它的架构我还是很熟悉的,所以一些嵌入式知识很快就过了,我的时间主要花在AXI总线和操作系统;
1、AXI总线:
由于Xilinx是将双核ARM与7系列FPGA集成于一块硅片构成SoC,所以比较重要的一个模块就是硬核处理器(PS)与可编程逻辑(PL)之间的通信,传统的有SPI总线、IFC总线的方式,zynq使用的是ARM和Xilinx共同制定的总线协议AMBA AXI4(Advanced Microcontroller Bus Architec),根据使用场景不同它有三种标准Lite、Stream、Full,Full即总线结构最完整的AXI4,4代表第4代,所以要是想和之前第三代总线模块互联的话,之间必须添加总线转换IP,它的总线结构和时序需要好好看看,《Xilinx AP Z-7000 SoC 设计指南》中有很详细介绍,其中有一段关于交易通道握手信号关系很重要:
2、操作系统:
这里的操作系统主要是指嵌入式linux,需要掌握peta-linux、uboot、内核、设备树、文件系统等,好多,不过幸运的是,在导入硬件文件后Vivado SDK会自动生成一些库,如头文件和BSP,最大的意义在于完成了硬件资源的映射,软件可以通过对地址的调用完成对硬件的操作。
AXI总线寄存器模块
在Tools中选择生成AXI总线接口的模块(IP),然后需求是PS可以读写32个位宽为32的寄存器,所以我选择的深度是32,其实打开代码就会发现,代码中的示例逻辑本身就是一个寄存器模块,PS和PL都可以对这32个寄存器读写操作,它们的W/R属性也是可以修改的
//读操作
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
5'h00 : reg_data_out <= slv_reg0;
5'h01 : reg_data_out <= slv_reg1;
5'h02 : reg_data_out <= slv_reg2;
5'h03 : reg_data_out <= slv_reg3;
5'h04 : reg_data_out <= slv_reg4;
5'h05 : reg_data_out <= slv_reg5;
5'h06 : reg_data_out <= slv_reg6;
5'h07 : reg_data_out <= slv_reg7;
5'h08 : reg_data_out <= slv_reg8;
5'h09 : reg_data_out <= slv_reg9;
5'h0A : reg_data_out <= slv_reg10;
5'h0B : reg_data_out <= slv_reg11;
5'h0C : reg_data_out <= slv_reg12;
5'h0D : reg_data_out <= slv_reg13;
5'h0E : reg_data_out <= slv_reg14;
5'h0F : reg_data_out <= slv_reg15;
5'h10 : reg_data_out <= slv_reg16;
5'h11 : reg_data_out <= slv_reg17;
5'h12 : reg_data_out <= slv_reg18;
5'h13 : reg_data_out <= slv_reg19;
5'h14 : reg_data_out <= slv_reg20;
5'h15 : reg_data_out <= slv_reg21;
5'h16 : reg_data_out <= slv_reg22;
5'h17 : reg_data_out <= slv_reg23;
5'h18 : reg_data_out <= slv_reg24;
5'h19 : reg_data_out <= slv_reg25;
5'h1A : reg_data_out <= slv_reg26;
5'h1B : reg_data_out <= slv_reg27;
5'h1C : reg_data_out <= slv_reg28;
5'h1D : reg_data_out <= slv_reg29;
5'h1E : reg_data_out <= slv_reg30;
5'h1F : reg_data_out <= slv_reg31;
default : reg_data_out <= 0;
endcase
end
//写操作片段
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
5'h00:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
5'h01:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 1
slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
5'h02:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 2
slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
5'h03:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 3
slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
硬件连接
把Zynq PS和上述生成的寄存器IP PL连接起来,其实很简单,两次自动连线就可以了,这里需要强调的是,需要检查DDR的配置、MIO的配置和PL时钟,由于需要串口打印,所以将串口和PS连接起来,具体管脚需要看原理图,可以看到是14 15,点击与之对应的UART0就可以了。
然后是将原理图设计转换成代码,再完成综合、实现和最终的bit文件,烧录进pynq Z2,硬件方面的设计就结束了。
软件设计
在File菜单先Export Hardware,然后 Launch SDK,就进入Xilinx软件开发环境,可以在Hello World模板上直接操作。
源码来源于米联客的教程,我根据自己的工程有改动:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_io.h" //Xil_In32 Xil_Out32函数的头文件
#include "xparameters.h" //硬件映射的地址信息,XPAR_MYIP_REGS_0_S00_AXI_BASEADDR 即PL IP核中寄存器的起始偏移地址
int main()
{
init_platform();
print("Hello World\n\r");
int num;
int rev;
xil_printf("------The test is start...------\n\r");
for( num=0; num<32; num++ )//写入
{
xil_printf("%x",num);
Xil_Out32(XPAR_MYIP_REGS_0_S00_AXI_BASEADDR + num*4, 0x10000000+num); //
}
for( num=0; num<32; num++ )//读出
{
rev = Xil_In32(XPAR_MYIP_REGS_0_S00_AXI_BASEADDR + num*4);
xil_printf( "The data at %x is %x \n\r",XPAR_MYIP_REGS_0_S00_AXI_BASEADDR + num*4,rev);
}
xil_printf("------The test is end!------\n\r");
int test;
test = Xil_In32( XPAR_MYIP_REGS_0_S00_AXI_BASEADDR + 4);
xil_printf("%x",test);
//cleanup_platform();
return 0;
}
总结
调试时遇到一个错误,现象是PL能正常烧录,PS能正常编译但是不能下载RUN,反复一直报错:
AP transaction error, DAP status f0000021
我在网上查找了一下,其他不同厂商的开发板也有遇到类似问题,大致原因是软件通过jtag找不到ARM处理器,我联系了开发板的FAE,他给的解释是可能供电电压不稳,换一种供电方式,但是我并没有适配器,但是我看到开发板上ARM的指示灯(DONE)是亮起的,于是重启了电脑和开发板后就恢复正常了,我觉得这很可能是硬件上的缺陷,也有可能是我笔记本USB接口电压不稳?希望能给有同样遭遇的同学一个思路。