精华内容
下载资源
问答
  • ZynqPL+PS Linux例程

    2018-08-27 11:42:09
    创龙ZynqPL+PS Linux例程文档,适合入门上手学习。很实用的教程。
  • Zynq 7000 PLPS通信——使用BRAM

    千次阅读 热门讨论 2018-12-02 22:20:22
    Zynq 7000 PLPS通信——使用BRAM介绍1 准备工作2 PL搭建BRAM3 下载PL程序4 编写Linux应用程序5 问题5.1 BRAM的寻址的问题5.2 PS地址映射 介绍 Xilinx公司的Zynq 7000系列SoC除了有功能强大的FPGA,还集成了双PS...

    介绍

    Xilinx公司的Zynq 7000系列SoC除了有功能强大的FPGA,还集成了带有双核ARM Cortex A9的PS。在协同使用PS和PL时,就不免需要在PS和PL之间进行数据交互。本文主要讲述安装了linux操作系统的PS如何与PL进行数据交互。

    1 准备工作

    PC环境:win10
    vivado版本:2015.4
    开发板:ZC706(已安装从SD卡启动的Linux系统)

    系统的总体框架如下图所示。利用AXI接口实现PS和PL的通信,PS作为主机,PL作为从机,通过AXI Interconnect分别和两个BRAM的控制器相连。BRAM部分有两个端口,这两个端口都可对这块BRAM进行读写操作。AXI的通信接口由AXI Interconnect和AXI BRAM Controller IP核完成,在PS端,可以将AXI BRAM Controller当做字符设备进行读写操作,在PL端,可以直接对BRAM进行读写操作。其中AXI接口的具体通信过程由IP核完成,在进行应用开发时可以不用太关心。下图这块BRAM的两个端口都连到了PS,没有端口供PL进行操作了,在实际应用中,可以一个端口连到PS,另一个端口由PL控制,然后商量好PS和PL的读写规则即可使用BRAM实现PL和PS的数据传输。
    在这里插入图片描述

    2 PL搭建BRAM

    打开vivado,新建一个工程,在选择器件时,选择ZC706评估板(如果不是此评估板,应该根据实际芯片型号选择)。
    在这里插入图片描述
    建好工程后,新建一个bd文件。
    在这里插入图片描述
    在Block Design文件中,添加一个ZYNQ7 Processing System IP核。
    在这里插入图片描述
    双击这个IP核对其进行配置,由于本文所用是官方提供的开发板,可以直接点击Presets -> ZC706使用默认的配置。如果是其他型号,应该按照实际情况进行配置。
    在这里插入图片描述
    然后点击PS-PLConfiguration里,把GP0选择,然后点击ok保存配置。
    在这里插入图片描述
    继续添加IP核,选择AXI BRAM Controller。
    在这里插入图片描述
    添加AXI BRAM Controller后,双击并对齐进行配置,将接口选择为1个。配置完成后以同样的方法再添加一个AXI BRAM Controller。
    在这里插入图片描述
    添加BRAM,并对齐进行配置,将其配置成双端口。
    在这里插入图片描述
    在这里插入图片描述
    然后把AXI接口的时钟线连上,并选择自动连线。
    在这里插入图片描述
    最终的Block Design如下图所示。
    在这里插入图片描述
    接下来右击bd文件,选择Create HDL Wrapper,并选择Let Vivado manage wrapper and auto-update。
    在这里插入图片描述
    在这里插入图片描述
    然后点击Address Editor,可以看到两个外设的地址分别为0x40000000和0x42000000。这个地址是给PS使用的,可以按照自己的喜好进行修改(当然地址不可随心所欲的修改,有些地址是不能给它们使用的!)。BRAM的容量大小也可在此处修改,只不过修改之后需要再运行一下Create HDL Wrapper重新进行打包,本文就使用默认的8K大小。
    在这里插入图片描述
    然后直接点击Generate Bitstream,等待其一步到位。
    在这里插入图片描述

    3 下载PL程序

    给开发板上电。前期已经制作了从SD卡启动的linux操作系统,上电后,系统启动,可以从串口进入系统进行命令行操作。连接PL后,点击Program Device下载bitstream文件。
    在这里插入图片描述

    4 编写Linux应用程序

    下载完成后,在linux系统下编写一个测试读写的C语言程序,测试代码如下所示。

    #include<stdio.h>
    #include<unistd.h>
    #include<sys/mman.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    
    #define BRAM_CTRL_0	0x40000000	//AXI_bram_ctrl_0的物理地址
    #define BRAM_CTRL_1	0x42000000	//AXI_bram_ctrl_1的物理地址
    #define DATA_LEN		10			//写入和读取的数据长度
    
    int main(){
        unsigned int * map_base0;
    	unsigned int * map_base1;
     
        int fd = open("/dev/mem", O_RDWR|O_SYNC);
        if (fd == -1){	
    		printf("can't open /dev/mem !\n");
            return (-1);
        }
    	printf("/dev/mem is opened !\n");
    	
        map_base0 = mmap(NULL, DATA_LEN * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, BRAM_CTRL_0);
    	map_base1 = mmap(NULL, DATA_LEN * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, BRAM_CTRL_1);
        if (map_base0 == 0 || map_base1 == 0){
            printf("NULL pointer!\n");
        }
        else{
            printf("mmap successfull!\n");
        }
    	
        unsigned long addr;
        unsigned int content;
        int i = 0;
    	
    	//写数据到BRAM
    	printf("\nWrite data to BRAM via AXI_bram_ctrl_0\n");
        for (i= 0; i < DATA_LEN; i++){
            addr = (unsigned long)(map_base0 + i);
            content = i + 2;
    		map_base0[i] = content;
            printf("%2dth data, address: 0x%lx data_write: 0x%x\t\t\n", i, addr, content);
        }
    	
    	//从BRAM读数据
    	printf("\nRead data from BRAM via AXI_bram_ctrl_1\n");
        for (i= 0; i < DATA_LEN; i++){
            addr = (unsigned long)(map_base1 + i);
    		content = map_base1[i];
            printf("%2dth data, address: 0x%lx data_write: 0x%x\t\t\n", i, addr, content);
        }
        
        close(fd);
        munmap(map_base0, DATA_LEN);
    	munmap(map_base1, DATA_LEN);
     
        return 0;
    }
    

    在linux下使用GCC进行编译,然后运行,运行结果如下图所示。
    在这里插入图片描述
    这里对测试的C语言程序进行简单的分析。首先打开/dev/mem字符设备节点,然后将BRAM的两个控制器AXI_bram_ctrl_0和AXI_bram_ctrl_1的物理地址进行地址映射,这两个控制器在linux看来就相当于两个外设。先向AXI_bram_ctrl_0写入10个数据,然后从AXI_bram_ctrl_1读出10个数据,由于这两个控制器所连接的是同一块BRAM存储空间,所以读出来的数据应该和写入的数据是一致的。如此便实现了PL和PS的数据交互。

    5 问题

    5.1 BRAM的寻址的问题

    如果将BRAM的一端交给PL来操作,如果选择BRAM的容量为8K。如果查看BRAM,会发现其深度竟然只有2048,并非8192,如下图所示。
    在这里插入图片描述
    其实这是因为BRAM的地址是以8位为单位的,BRAM容量设置成8K是指8KB,即8K×8bits。换算成32位宽时为8K×8bits = 2K×32bits,也就是说只有2048个32位地址。PL在对BRAM进行寻址时,也是使用的8位位宽寻址,也就是说地址会从0~1FFF,会有13根地址线,而并非11根地址线32位位宽。这是PL对BRAM进行操作时需要注意的。

    5.2 PS地址映射

    在PL这边搭好工程之后,会分配两个地址0x40000000和0x42000000。这两个地址是真实的物理地址,在Linux系统下不能直接访问,需要进行地址映射,然后才能对其进行操作。关于地址映射可是个麻烦的事情,还需仔细学习和谨慎使用。

    展开全文
  • 上升沿信号, PL 捕获上升沿后立即将 PS 写入的 32 位数据读出,然后加 2,再存入原地址中。存储完成后, PL 通 过 AXI GPIO 向 PS 输入 1 个翻转信号,每翻转 1 次, AXI GPIO 便向 PS 触发 1 次中断。 PS 触发中断...
  • ZYNQ AXI总线PL/PS通信的例程代码,需要的可以下载来看看
  • ZYNQ 、AXI协议、PSPL内部通信 三种AXI总线分别为:AXI4:(For high-performance memory-mapped requirements.)主要面向高性能地址映射通信的需求,是面向地址映射的接口,允许最大256轮的数据突发传输;...
    ZYNQ 、AXI协议、PS与PL内部通信 
    三种AXI总线分别为:
    AXI4:(For high-performance memory-mapped requirements.)主要面向高性能地址映射通信的需求,是面向地址映射的接口,允许最大256轮的数据突发传输;
    AXI4-Lite:(For simple, low-throughput memory-mapped communication )是一个轻量级的地址映射单次传输接口,占用很少的逻辑单元。
    AXI4-Stream:(For high-speed streaming data.)面向高速流数据传输;去掉了地址项,允许无限制的数据突发传输规模。
    AXI4总线和AXI4-Lite总线具有相同的组成部分:
    (1)读地址通道,包含ARVALID, ARADDR, ARREADY信号;
    (2)读数据通道,包含RVALID, RDATA, RREADY, RRESP信号;
    (3)写地址通道,包含AWVALID,AWADDR, AWREADY信号;
    (4)写数据通道,包含WVALID, WDATA,WSTRB, WREADY信号;
    (5)写应答通道,包含BVALID, BRESP, BREADY信号;
    (6)系统通道,包含:ACLK,ARESETN信号。
    AXI4-Stream总线的组成:
    (1)ACLK信号:总线时钟,上升沿有效;
    (2)ARESETN信号:总线复位,低电平有效
    (3)TREADY信号:从机告诉主机做好传输准备;
    (4)TDATA信号:数据,可选宽度32,64,128,256bit
    (5)TSTRB信号:每一bit对应TDATA的一个有效字节,宽度为TDATA/8
    (6)TLAST信号:主机告诉从机该次传输为突发传输的结尾;
    (7)TVALID信号:主机告诉从机数据本次传输有效;
    (8)TUSER信号 :用户定义信号,宽度为128bit。
    三种AXI接口分别是:
    AXI-GP接口(4个):是通用的AXI接口,包括两个32位主设备接口和两个32位从设备接口,用过改接口可以访问PS中的片内外设。
    AXI-HP接口(4个):是高性能/带宽的标准的接口,PL模块作为主设备连接(从下图中箭头可以看出)。主要用于PL访问PS上的存储器(DDR和On-Chip RAM)
    AXI-ACP接口(1个):是ARM多核架构下定义的一种接口,中文翻译为加速器一致性端口,用来管理DMA之类的不带缓存的AXI外设,PS端是Slave接口。
    我们可以双击查看ZYNQ的IP核的内部配置,就能发现上述的三种接口,图中已用红色方框标记出来,我们可以清楚的看出接口连接与总线的走向:
    AXI协议之握手协议
    AXI4所采用的是一种READY,VALID握手通信机制,简单来说主从双方进行数据通信前,有一个握手的过程。传输源产生VLAID信号来指明何时数据或控制信息有效。而目地源产生READY信号来指明已经准备好接受数据或控制信息。传输发生在VALID和READY信号同时为高的时候。VALID和READY信号的出现有三种关系:VALID先变高READY后变高,READY先变高VALID后变高,VALID和READY信号同时变高。无论什么情况信息传输立马发生在VALID和READY信号同时为高且ACLK上升沿,时序图如下:
    需要强调的是,AXI的五个通道,每个通道都有握手机制,接下来我们就来分析一下AXI-Lite的源码来更深入的了解AXI机制。
    突发式读
    当地址出现在地址总线后,传输的数据将出现在读数据通道上。设备保持VALID为低直到读数据有效。为了表明一次突发式读写的完成,设备用RLAST信号来表示最后一个被传输的数据。
    突发式写
    这一过程的开始时,主机发送地址和控制信息到写地址通道中,然后主机发送每一个写数据到写数据通道中。当主机发送最后一个数据时,WLAST信号就变为高。当设备接收完所有数据之后他将一个写响应发送回主机来表明写事务完成。

    PS与PL内部通信(用户自定义IP)
      先要自定义一个AXI-Lite的IP,新建工程之后,选择,菜单栏->Tools->Creat and Package IP:
    选择Next>>选择Create AXI4 Peripheral>>然后Next默认,选择Next>>注意这里接口类型选择Lite,选择Next>>选择Edit IP,点击Finish:
    此后,Vivado会新建一个工程,专门编辑该IP,通过该工程,我们就可以看到Vivado为我们生成的AXI-Lite的操作源码:
    AXI-Lite 源码分析
    当打开顶层文件的时,是一堆AXI的信号。
    1. input wire  s00_axi_aclk,

    2. input wire  s00_axi_aresetn,

    3. input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,

    4. input wire [2 : 0] s00_axi_awprot,

    5. input wire  s00_axi_awvalid,

    6. output wire  s00_axi_awready,

    7. input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,

    8. input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,

    9. input wire  s00_axi_wvalid,

    10. output wire  s00_axi_wready,

    11. output wire [1 : 0] s00_axi_bresp,

    12. output wire  s00_axi_bvalid,

    13. input wire  s00_axi_bready,

    14. input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,

    15. input wire [2 : 0] s00_axi_arprot,

    16. input wire  s00_axi_arvalid,

    17. output wire  s00_axi_arready,

    18. output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,

    19. output wire [1 : 0] s00_axi_rresp,

    20. output wire  s00_axi_rvalid,

    21. input wire  s00_axi_rready</font>
    复制代码
    PS向PL写数据(PS作为Master,PL作为Slave)
    先来看一段WDATA相关的代码:
    1. always @( posedge S_AXI_ACLK )

    2. begin

    3.   if ( S_AXI_ARESETN == 1'b0 )

    4.     begin

    5.       slv_reg0 <= 0;

    6.       slv_reg1 <= 0;

    7.       slv_reg2 <= 0;

    8.       slv_reg3 <= 0;

    9.     end

    10.   else begin

    11.     if (slv_reg_wren)

    12.       begin

    13.         case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )

    14.           2'h0:

    15.             for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

    16.               if ( S_AXI_WSTRB[byte_index] == 1 ) begin

    17.                 // Respective byte enables are asserted as per write strobes

    18.                 // Slave register 0

    19.                 slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

    20.               end  

    21.           2'h1:

    22.             for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

    23.               if ( S_AXI_WSTRB[byte_index] == 1 ) begin

    24.                 // Respective byte enables are asserted as per write strobes

    25.                 // Slave register 1

    26.                 slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

    27.               end  

    28.           2'h2:

    29.             for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

    30.               if ( S_AXI_WSTRB[byte_index] == 1 ) begin

    31.                 // Respective byte enables are asserted as per write strobes

    32.                 // Slave register 2

    33.                 slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

    34.               end  

    35.           2'h3:

    36.             for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

    37.               if ( S_AXI_WSTRB[byte_index] == 1 ) begin

    38.                 // Respective byte enables are asserted as per write strobes

    39.                 // Slave register 3

    40.                 slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

    41.               end  

    42.           default : begin

    43.                       slv_reg0 <= slv_reg0;

    44.                       slv_reg1 <= slv_reg1;

    45.                       slv_reg2 <= slv_reg2;

    46.                       slv_reg3 <= slv_reg3;

    47.                     end

    48.         endcase

    49.       end

    50.   end

    51. end</font>
    复制代码
    这段程序的作用是,当PS那边向AXI4-Lite总线写数据时,PS这边负责将数据接收到寄存器slv_reg。而slv_reg寄存器有0~3共4个。至于赋值给哪一个由axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]决定,根据宏定义其实就是由axi_awaddr[3:2] (写地址中不仅包含地址,而且包含了控制位,这里的[3:2]就是控制位)决定赋值给哪个slv_reg。
    PS读取PL数据(PS作为Master,PL作为Slave)
    我们继续来看有关RADTA读数据代码:
    1. // Output register or memory read data

    2. always @( posedge S_AXI_ACLK )

    3. begin

    4.   if ( S_AXI_ARESETN == 1'b0 )

    5.     begin

    6.       axi_rdata  <= 0;

    7.     end

    8.   else

    9.     begin    

    10.       // When there is a valid read address (S_AXI_ARVALID) with

    11.       // acceptance of read address by the slave (axi_arready),

    12.       // output the read dada

    13.       if (slv_reg_rden)

    14.         begin

    15.           axi_rdata <= reg_data_out;     // register read data

    16.         end   

    17.     end

    18. end

    19. //当PS读取数据时,程序会把reg_data_out复制给axi_rdata(RADTA读数据)。

    20. always @(*)

    21. begin

    22.       // Address decoding for reading registers

    23.       case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )

    24.         2'h0   : reg_data_out <= slv_reg0;

    25.         2'h1   : reg_data_out <= slv_reg1;

    26.         2'h2   : reg_data_out <= slv_reg2;

    27.         2'h3   : reg_data_out <= slv_reg3;

    28.         default : reg_data_out <= 0;

    29.       endcase

    30. end</font>
    复制代码
        ZYNQ嵌入式设计时,用户在PL中自定义的IP相当于PS(ARM的外设),他们之间是通过AXI总线进行数据传输。创建好哦IP后,文件自动的生成,用户只需要做简单的修改,寄存器赋值就可以。
    展开全文
  • Xilinx官方参考文档:ug994-vivado-ip-subsystems.pdf 黑金教程: cource_s1_ALINX_ZYNQ(AX7010_AX7020)开发平台基础教程V...前面介绍了PSPL通过DMA通信的基本测试,但是之前用的都是xilinx的官方的IP核,接口都是...

    Xilinx官方参考文档:ug994-vivado-ip-subsystems.pdf
    黑金教程:
    cource_s1_ALINX_ZYNQ(AX7010_AX7020)开发平台基础教程V1.06(第十一章)
    course_s2_ALINX ZYNQ开发平台SDK应用教程V2.02.pdf(第十章)

    前面介绍了PS与PL通过DMA通信的基本测试,但是之前用的都是xilinx的官方的IP核,接口都是做好的,我们直接例化就好。而在实际应用中,我们更加关心用户逻辑怎么与PS间进行数据通信。下面就介绍在BD中添加用户逻辑的方式,以及在其过程中所遇到的问题

    这里我们用户逻辑以UART接口为代表设计。PL端实现UART功能,在SDK里发送数据通过DMA给PL端的UART发送出去。在外部将UART的收发回环,将发送出去的数据再收回来,通过DMA在给PS。此外我们在PL的uart上封装层slip协议,用来实现在uart上进行帧操作。测试硬件框图如下:
    在这里插入图片描述
    就是在之前的测试环境中将AXIS_DATA_FIFO换成了我们用户逻辑UART部分,其中FIFO_to_AXIS是用来实现FIFO接口与AXIS接口的转换模块。

    建立相应的vivado工程,并添加用户逻辑代码(模块不一一分析了):
    在这里插入图片描述
    在这里插入图片描述
    此时我们发现在用户逻辑部分还缺少FIFO IP核,我们用IP catalog增加相应的IP核,具体IP核的参数根据自己的需要进行定制。
    在这里插入图片描述
    在BD当中可以通过add IP和add module的方式来增加用户逻辑,两者稍微有点区别。
    Add IP需要将用户逻辑提前封装好成IP核,然后通过IP Catalog来添加;而Add module可直接Diagram里添加。不过我在用Add module时遇到一些问题还没解决,可能BD并不认可我们写的fifo_to_axis的AXIS接口(其实在Add ip方式下我们并没有更改fifo_to_axis的逻辑,待定位问题)。

    Add module方式:
    在Diagram里右键,选add module,在弹出的对话框选择你添加模块的顶层文件:
    在这里插入图片描述
    添加后模块上会显示RTL的标志,然后手动连线:
    在这里插入图片描述
    连线完成后,进行validate时报如下错误:
    在这里插入图片描述
    然后尝试定位问题,不过目前还没定位到地方,带我稍微再研究研究看看,我们先采用Add IP的方式。

    Add IP:
    首先我们需要将用户逻辑封装成AXIS4接口IP,步骤可参考相关教程,不细说,只介绍和教程有区别的地方。
    新建一个New IP,取名my_uart_ip。这里我们选2个AXIS接口,一主一从,然后位宽是32位(其实我们需要8位,但这里改不了,我们先不用管)
    在这里插入图片描述
    然后我们Edit in IP Packager,添加所需的用户逻辑代码:
    在这里插入图片描述
    和之前一样增加用户逻辑内部的fifo IP核,不介绍了。

    然后我们修改my_uart_ip_v1_0.v模块,去掉系统生成的两个子模块,加上我们自己的fifo_to_axis模块。
    在这里插入图片描述
    my_uart_ip_v1_0.v具体代码如下(这个地方其实也看出我们并没有修改fifo_to_axis的代码,只是将fifo_to_axis模块用系统封装成IP核了,理论上和add module的效果一样,不知道为什么add module的方式不行):

    `timescale 1 ns / 1 ps
    
    	module my_uart_ip_v1_0
    	(
    		// Users to add ports here
    		input               clk_50M,	   //50M
            input               rst_n,    //low active        
            
            //uart
            output              rs232_tx,
            input               rs232_rx,
            
            //pl_master PL TO PS
            output  [7:0]               m00_axis_tdata,
            output  [0:0]               m00_axis_tkeep,
            output                      m00_axis_tlast,
            input                       m00_axis_tready,
            output                      m00_axis_tvalid,
            input                       m00_axis_aresetn,
            input                       m00_axis_aclk,
            
            // pl_slave PS TO PL
            input  [7:0]                s00_axis_tdata,
            input  [0:0]                s00_axis_tkeep,
            input                       s00_axis_tlast,
            output                      s00_axis_tready,
            input                       s00_axis_tvalid,
            input                       s00_axis_aresetn,
            input                       s00_axis_aclk
    	);
    
    	// Add user logic here
         fifo_to_axi fifo_to_axi_inst(
        .clk_50M(clk_50M),    //50M
        .rst_n(rst_n & s00_axis_aresetn & m00_axis_aresetn),    //low active        
        
        //uart
        .rs232_tx(rs232_tx),
        .rs232_rx(rs232_rx),
        
        //AXI stream slave interface
        .ps2pl_s_clk(s00_axis_aclk),
        .ps2pl_s_data(s00_axis_tdata),
        .ps2pl_s_keep(s00_axis_tkeep),
        .ps2pl_s_valid(s00_axis_tvalid),
        .ps2pl_s_last(s00_axis_tlast),
        .ps2pl_s_ready(s00_axis_tready),
    
        //AXI stream master interface
        .pl2ps_m_clk(m00_axis_aclk),
        .pl2ps_m_data(m00_axis_tdata),
        .pl2ps_m_keep(m00_axis_tkeep),
        .pl2ps_m_valid(m00_axis_tvalid),
        .pl2ps_m_last(m00_axis_tlast),
        .pl2ps_m_ready(m00_axis_tready)
        );
    	// User logic ends
    
    	endmodule
    

    然后修改下IP的接口(可能有一部分信号没有分配为总线方式),这时我们可以右键Auto infer Interface Chooser,选择你需要的接口方式:
    在这里插入图片描述
    在这里插入图片描述
    然后还要关联下相应的时钟:
    在这里插入图片描述
    最后Re-package IP,这样我们的用户IP就定制好了。

    这样我们在BD中通过IP Catalog添加我们定义好的IP,并进行相关的连线:
    在这里插入图片描述
    因为加入用户逻辑了,所以我们需要为用户逻辑分配引脚,在黑金AX_7020增加如下约束内容:
    set_property PACKAGE_PIN F17 [get_ports rs232_rx_1]
    set_property PACKAGE_PIN F16 [get_ports rs232_tx_1]
    set_property IOSTANDARD LVCMOS33 [get_ports rs232_rx_1]
    set_property IOSTANDARD LVCMOS33 [get_ports rs232_tx_1]

    另外我们可以通过netlist方式增加Debug信号(具有层级关系,好定位需要加的信号)
    在这里插入图片描述
    生成bit文件后导出硬件,打开SDK,SDK里的驱动和上一章一样,没有做修改,所以不再介绍了。

    因为开发板上PL没有接UART芯片,没法直接接串口,我们用FPGA的2个IO口模拟UART的收发,在开发板上用跳线将收发端回环(可以用示波器抓取UART的波形):
    在这里插入图片描述
    运行SDK的程序,进行测试,测试结果如下:
    在这里插入图片描述
    我们在debug里分析下fifo_to_axis的相关接口波形,看到PL端通过AXIS接口收过来数据后写到了对应得fifo中(另外一侧一样不细分析):
    在这里插入图片描述
    到此我们完整的实现了在zynq中PS和PL(用户逻辑)数据互通。

    在此附上fifo_to_axis接口转换的代码,因为比较简单就没有采用状态机来实现:

    `timescale 1ns / 1ps
    
    module fifo_to_axi(
        clk_50M,	//50M
        rst_n,    //low active        
        
        //uart
        rs232_tx,
        rs232_rx,
        
        //AXI stream slave interface
        ps2pl_s_clk,
        ps2pl_s_data,
        ps2pl_s_keep,
        ps2pl_s_valid,
        ps2pl_s_last,
        ps2pl_s_ready,
        
        //AXI stream master interface
        pl2ps_m_clk,
        pl2ps_m_data,
        pl2ps_m_keep,
        pl2ps_m_valid,
        pl2ps_m_last,
        pl2ps_m_ready
        );
        
        input               clk_50M;	//50M
        input               rst_n;    //low active        
        
        //uart
        output              rs232_tx;
        input               rs232_rx;
        
        //AXI stream slave interface
        input               ps2pl_s_clk;
        input   [7:0]       ps2pl_s_data;
        input               ps2pl_s_keep;
        input               ps2pl_s_valid;
        input               ps2pl_s_last;
        output              ps2pl_s_ready;
        
        //AXI stream master interface
        input               pl2ps_m_clk;
        output  [7:0]       pl2ps_m_data;
        output              pl2ps_m_keep;
        output              pl2ps_m_valid;
        output              pl2ps_m_last;
        input               pl2ps_m_ready;
        
        reg                 ps2pl_s_ready;
        reg                 pl2ps_m_valid;
        wire                pl2ps_m_last;
        
        wire                ps2pl_fifo_empty;
        wire                ps2pl_fifo_wrclk;
        reg                 ps2pl_fifo_wr;
        reg     [7:0]       ps2pl_fifo_din;
        wire                ps2pl_fifo_full;
        
        wire                pl2ps_fifo_empty;
        wire                pl2ps_fifo_rdclk;
        wire                pl2ps_fifo_rd;
        reg                 pl2ps_fifo_rd_i;
        wire    [7:0]       pl2ps_fifo_dout;
        wire                pl2ps_fifo_rdy;
        reg                 pl2ps_data_valid;
        
        assign ps2pl_fifo_wrclk = ps2pl_s_clk;
        
        always @(posedge ps2pl_s_clk or negedge rst_n)
        begin
            if(!rst_n)
                ps2pl_s_ready <= 1'b0;
            else if(ps2pl_fifo_empty)
                ps2pl_s_ready <= 1'b1;
            else if(ps2pl_s_last)
                ps2pl_s_ready <= 1'b0;
        end
        
        always @(posedge ps2pl_s_clk or negedge rst_n)
        begin
            if(!rst_n)
                ps2pl_fifo_wr <= 1'b0;
            else if(ps2pl_s_valid & ps2pl_s_ready & ~ps2pl_fifo_full)
                ps2pl_fifo_wr <= 1'b1;
            else
                ps2pl_fifo_wr <= 1'b0;
        end
        
        always @(posedge ps2pl_s_clk or negedge rst_n)
        begin
            if(!rst_n)
                ps2pl_fifo_din <= 8'h00;
            else
                ps2pl_fifo_din <= ps2pl_s_data;
        end
        
        assign pl2ps_fifo_rdclk = pl2ps_m_clk;
        
        always @(posedge pl2ps_m_clk or negedge rst_n)
        begin
            if(!rst_n)
                pl2ps_fifo_rd_i <= 1'b0;
            else if(pl2ps_fifo_empty | ~pl2ps_m_ready)
                pl2ps_fifo_rd_i <= 1'b0;
            else if(pl2ps_m_ready & pl2ps_fifo_rdy)
                pl2ps_fifo_rd_i <= 1'b1;
            
        end
        
        assign pl2ps_fifo_rd = pl2ps_fifo_rd_i & pl2ps_m_ready & ~pl2ps_fifo_empty;
        
        always @(posedge pl2ps_m_clk or negedge rst_n)
        begin
            if(!rst_n)
                pl2ps_data_valid <= 1'b0;
            else if(pl2ps_fifo_rd)
                pl2ps_data_valid <= 1'b1;
            else if(pl2ps_m_last) 
                pl2ps_data_valid <= 1'b0;
        end
        
        always @(posedge pl2ps_m_clk or negedge rst_n)
        begin
            if(!rst_n)
                pl2ps_m_valid <= 1'b0;
            else if(pl2ps_fifo_empty)
               pl2ps_m_valid <= 1'b0; 
            else if((pl2ps_fifo_rd) || (pl2ps_data_valid & pl2ps_m_ready))
                pl2ps_m_valid <= 1'b1;
            else
                pl2ps_m_valid <= 1'b0;
        end
      
        assign pl2ps_m_keep = pl2ps_m_valid;
        
        assign pl2ps_m_data = pl2ps_fifo_dout;
        
        assign pl2ps_m_last = pl2ps_fifo_rd_i & pl2ps_fifo_empty;
        
        uart_top pl_uart(
        .clkin(clk_50M),    //50M
        .rst_n(rst_n),        //low active        
        
        .rs232_tx(rs232_tx),
        .rs232_rx(rs232_rx),
        
        .rs232_rate(3'b100),
        
        .ps2uart_fifo_empty(ps2pl_fifo_empty),
        .ps2uart_fifo_wrclk(ps2pl_fifo_wrclk),
        .ps2uart_fifo_wr(ps2pl_fifo_wr),
        .ps2uart_fifo_din(ps2pl_fifo_din),
        .ps2uart_fifo_full(ps2pl_fifo_full),
        
        .uart2ps_fifo_empty(pl2ps_fifo_empty),
        .uart2ps_fifo_rdclk(pl2ps_fifo_rdclk),
        .uart2ps_fifo_rd(pl2ps_fifo_rd),
        .uart2ps_fifo_dout(pl2ps_fifo_dout),
        .uart2ps_fifo_rdy(pl2ps_fifo_rdy)
        );   
    endmodule
    

    总结:
    本文主要介绍了ZYNQ设计中在BD中加入用户逻辑的方法和具体流程以及测试过程中遇到的一些问题,供以后回顾。
    其中通过Add Module的方式加入用户逻辑遇到的错误还需进一步解决,如果有小伙伴解决了该问题也请交流经验。

    展开全文
  • (1)block design, PL:program logic PS:processing system

    一.硬件平台
    (1)添加Zynq硬核。新建工程后打开block design,右键添加IP:Zynq Processing System,这个在选芯片型号时支持Zynq才能添加进去(Tools-Settings可更改),相当于硬核处理器。
    配置硬核如下:
    PS-PL Configuration勾选一个FCLK_RESET0_N 和M AXI GP0 interface;
    外设管脚勾选一个UART并在MIO Configuration里绑定FPGA管脚号;
    时钟勾选一50MHZ时钟即可。其他选项为默认。
    (2)改装AXI-Lite的IP。选择Tools-creat and repackage IP-Creat a new AXI4 peripheral,编辑IP名、版本等信息,并选择IP目录;选择AXI-Lite(用于寄存器的配置),Slave,4个寄存器。完成配置,自动生成接口、驱动、仿真模型;
    打开Edit IP,进入IP编辑面板(区别于工程面板)-在Design Source内打开.v接口文件(主要结构为读写寄存器函数)-在User edit区域添加自己的功能,例如

    output reg LED;
    always@(posedge clk or negedge rst)
    begin
    	if(!rst)
    		LED<=0;
    	else if(slv_reg==32'hffff_ffff)
    		LED<=1;
    end 
    

    -到.v接口文件添加LED端口-回到IP编辑版面,点击Package IP中未打勾的选项,更新IP配置-Repackage IP
    (3)联通PL-PS(PL:program logic;PS:processing system)。添加上一步封装的IP-将LED make external-Run auto-Run Connection auto(自动添加AXI-Lite总线及接口,只有基于协议的模块才有这个功能)-添加ILA(在线逻辑分析仪)检测AXI-Lite协议,一端接总时钟,一端接AXI口;

    (4)生成比特流。右键Sources工程,Generate Output Product-Generate HDL wrapper-添加管脚约束文件Constrains,并设定LED管脚,如

    set_property PACKAGE_PIN G20 [get_ports LED]
    set_property IOSTANDARD LVCMOS33 [get_ports LED]
    

    完成后Generate bit stream,开始综合Synthesis(IP如有误可右键Edit In IP Package)后生成比特流。File-Export Hardware-include bitstream

    二.联合SDK开发
    (5)创建板级支持包。Launch SDK,New一个Application Project,并加入上一步生成的硬件平台-选择Hello world.c例程,完成一个板级支持包
    (6)添加驱动。将硬件平台Source里的驱动文件复制到新建的工程,并在头文件include进去。
    保存后控制台会自动编译,这里遇到一个error,undefine Xilin_In,回去定位到驱动文件,需要再include Xin_io.h;之后又新出现一个error:conflicting types for…,原因是新添加的.h文件重复include了另一个头文件,注释掉一个即可。
    (7)测试例程。右键选择工程-Debugs AS Launch on Hardware(System Debugger)-Run Program FPGA-打开SDK Terminal-设置串口驱动-连接成功后逐步测试函数
    (8)基本读写函数分析。

    AXI_LITE_REGS_mWriteReg(u32 BaseAddress, unsigned RegOffset, u32 Data);
    #define AXI_LITE_REGS_mWriteReg(BaseAddress, RegOffset, Data) \
      	Xil_Out32((BaseAddress) + (RegOffset), (u32)(Data))
    AXI_LITE_REGS_mReadReg(u32 BaseAddress, unsigned RegOffset)
    #define AXI_LITE_REGS_mReadReg(BaseAddress, RegOffset) \
        Xil_In32((BaseAddress) + (RegOffset))
    

    只要有基地址+偏移量+数据就可以使用这两个读写函数。基地址可以在block design页面找
    例如:

    always@(posedge clk)
    begin
    	AXI_LITE_REGS_mWriteReg((u32)0x4300000,0,0xffff_ffff)
    	//对寄存器0写入数据0xffff_ffff
    		if(slave_regs_0==0xffff_ffff)
    			LED=1;
    end
    

    (9)ILA观测。使PS端进入Debug模式,使能ILA的时钟。设置触发条件,逐步调试,观察AXI总线时序。
    在这里插入图片描述

    展开全文
  • ZYNQ DMA实现PSPL间数据传输通信

    千次阅读 2020-06-23 00:55:44
    一,PSPL的数据传输流程: 1,传输过程 (1)、向PL端查询剩余数据存储长度(以byte为单位); (2)、通过写寄存器设置PL端DMA数据传输开始地址; (3)、通过写寄存器设置PL端DMA数据传输长度(以byte为单位...
  • PL透过bram 向PL传递 0-9的数据,PL进行数据处理后( 在此为了简化,就将所取得的数据 + 100 返回) 透过dma 传回PS https://blog.csdn.net/howard789/article/details/111194482
  • zynq ps/pl交互接口

    千次阅读 2017-05-17 09:55:36
    ZynqPSPL部分通过如下接口连接 • 两个32位AXI主端口(PS Master) • 两个32位AXI从端口(PL Master) • 四个32、/64位高性能端口(PL Master) • 一个64位加速器一致性端口(ACP)(PL Master) • 四个来自...
  • ZYNQ学习之plps接口

    千次阅读 2018-09-06 10:06:01
    ZYNQ学习之plps接口
  • zynqPL通过UartliteIP核扩展串口,PS通过中断处理串口数据。开发环境为vivado2018.3,资源里有文档教程,有工程文件。扩展的串口可以发送任意长度的数据,也可以接受任意长度的数据。
  • Zynq-linux PLPS通过DMA数据交互

    千次阅读 多人点赞 2019-10-04 10:39:55
    在米尔科技的z-turn板上,采用AXI DMA 实现zynqPSPL数据交互。 二、分析 ①PS数据传PL 驱动中的测试程序中给出一堆数据,通过DMA传输到AXI4-Stream Data FIFO ,PL端从DATA FIFO中把数据读出来。 ②PL数据传...
  • 添加zynq block 添加一个AXI_GPIO模块 添加点击自动布线 在弹出窗口中打勾 效果如下,我们可以看到系统会自动添加了一个AXI转换器,一端连接到了ARM核,一端连到了AXI的设备上,我们这里用的是
  • ZYNQ_PS读写PL资源

    千次阅读 2019-03-31 17:46:04
    ZYNQ_PS读写PL资源_base_on_pynqZ2前言AXI总线寄存器模块硬件连接软件设计总结 前言 最近比较系统的学习了zynq,内容还是很多的,不过它的架构我还是很熟悉的,所以一些嵌入式知识很快就过了,我的时间主要花在AXI...
  • AXI-EMC IP是一个可以可以支持各种内存型号的控制器,利用这个IP可以非常方便地模拟各种类型的内存或者FLASH接口实现数据的交互和通信。以下是AXI-EMC IP的功能特性: 1、支持AXI4 Slave Memory Map接口,数据宽度...
  • PSPL通信概述 传统的SoPC设计无外乎两种方式:(1)在FPGA上设计一个软核,比如Altera的NIOSII,Xilinx的MicroBlaze等(2)将一个独立的FPGA和处理器芯片(比如ARM等)联合使用。第一种方式的局限在于软核的...
  • 1. ZYNQ 概述 2. AXI 概述 AXI (Advanced eXtensible Interface)是xilinx 从6系列fpga开始引入的一个接口协议,主要描述了主设备和从设备之间的数据传输方式。主设备和从设备之间通过握手信号建立连接。当从设备...
  • 文章目录一、ZYNQ的AXI资源 ALINX 黑金zynq7000开发平台配套教程 ...因此,如何设计高效的PLPS 数据交互通路是 ZYNQ 芯片设计癿重中之重,也是产品设计成败关键之一。 一、ZYNQ的AXI资源 在 ZYNQ.
  • ZYNQ芯片AXI 协议和PLPS接口互联

    千次阅读 2020-03-02 10:41:44
    Zynq可扩展平台的性能不仅在于PSPL的功能强大,最大的优势在于能把二者联合起来使用以形成完整体系。其中,起到至关重要作用的就是通过AXI总线协议实现两部分的紧密联合。 在介绍AXI协议之前先介绍先进的微控制器...
  • 最近在研究zynq 中EMIO的使用,看了许多文章都是裸跑的,没有将zynq ARM的功能发挥出来,此处将介绍如何在linux下操作PL的资源,本文通过PL的emio管脚进行点灯实验作简要说明。 前提:已搭建好7030的linux运行环境 ...
  • 我之前的博客都是基于zynqMP的,与zynq有一些差别 (1) 创建工程 ​ (2) 导入hdf文件 ​ (3) 修改petalinux-config->DTG Settings->MACHINE_NAME为zedboard 其余的配置都可以默认即可,因为默认情况下dma相关的驱动...
  • ZYNQ PSPL共享DDR

    千次阅读 2019-11-07 20:37:32
    平台: 开发板:ZYNQ-7000系列裸板开发 开发环境:vivado hls、vivado、sdk 参考 https://blog.csdn.net/weixin_36474809/article/details/85111550 ...
  • ZYNQ PLPS交互的最大带宽

    千次阅读 2018-12-14 21:04:16
    PLPS的交互使用AXI_HP接口可以达到最大的带宽。 在典型的150MHz的时钟速度...如果数据都存放在DDR中,PL都是通过PS去读取DDR里的数据,这样实际带宽是取决于DDR的通信带宽,HP口的带宽也发挥不出来。 PLPS的D...
  • zynq pl 发数据给 ps

    2021-04-28 15:50:05
    pl端产生的数据信息发送给ps的过程中,踩了不少坑,记录一下;本文几乎没有理论的东西,大部分都是如何在软件上操作,代码是怎么样的,对新手非常友好;因此,大佬可以跳过了;推荐

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 735
精华内容 294
关键字:

pl通信pszynq