作者:付汉杰,hankf@xilinx.com,文章转载自:赛灵思中文社区论坛
1. 概述
MPSoC是Xilinx基于16nm工艺推出的异构计算平台,由于灵活、稳定,在业界得到了广泛的使用。异构计算是一个比较新的领域,需要协调硬件设计、逻辑设计、软件设计,对工程师的要求很高。实际设计过程中,很多工程师对实现PS/PL之间的数据交互感到头疼。
本文将介绍主要的PS/PL之间的数据交互办法。
2. MPSoC PS/PL之间的数据通路
在开始之前,首先简要介绍PS/PL之间的数据通路,请参考 《UG1085 Zynq UltraScale+ MPSoC Technical Reference Manual》的Figure 1-1: AXI Interconnect。
PS/PL之间的数据通路主要是通用的AXI Memory接口,其中PS作为主设备的接口有128-bit位宽的HPM0/HPM1, 64-bit位宽的LPD_PL; PL作为主设备的接口有ACP/ACE/HPC0/HPC1/HP0/HP1/HP2/HP3。所有AXI Memory接口的位宽最高都可以支持到128-bit。
PS/PL之间主要通过PS-DDR交互大块数据。从Figure 1-1可以看到,PS-DDR控制器有六个AXI Slave接口,与PL直接相关的是S3、S4、S5。HPC0/HPC1都连接到了CCI Interconnect。DP和HP0连接到了S3。HP1和HP2连接到了S4。HP3和FP DMA连接到了S5。如果需要提高带宽,要充分利用PS-DDR控制器的AXI Slave接口。如果可能,HP1和HP2最好不要同时用,因为HP1和HP2都连接到了S4,最后会彼此竞争带宽。
UG1085的Figure 1-1提供了最详细的信息, 也有点复杂。为了简单,也可以参考下面来自于《Zynq UltraScale+ MPSoC Technical Overview》的简化示意图。
在Vivado的IP 里,使能相关接口后,接口如下。
3. MPSoC PS/PL之间的简单数据通路和简单外设设计
很多时候,PS/PL之间只需要简单的数据通路。PS只需要下发有限的参数给PL,PL只需要向PS反馈有限的状态数据。串口、SPI设备、IIC等低速接口,就属于这种设备。这种情况下,PL内部只需要实现AXI Slave接口和一些寄存器就可以,PS通过AXI接口去访问寄存器,既向PL提供参数,也可以读回PL的状态。
客户可以自己设计AXI接口和寄存器,也可以使用Vivado里的工具Create and Package IP。
4. 使用Vivado里的工具Create and Package IP创建IP
如果使用Vivado里的工具Create and Package IP创建IP,可以参照下列步骤。
1) 调用Vivado的Create and Package IP 模板
2) Create and Package IP 模板介绍
3)新建IP
这一步选择"Create a new AXI4 peripheral"。
4) IP命名和目录
根据自己需要,指定IP的名称和目录。
5) IP接口选择
可以看到,只为新的IP选择了AXI Lite接口,并实现了16个寄存器。工程师可以根据需要选择寄存器个数,最小4个,最多512个。对于AXI Lite接口,数据位宽是32-bit。
6) 完成IP创建
选择“Add IP to the repository”, 点击"Finish", 完成IP创建。
7) BD框图
在Block Design中,选择对应的IP。示例中是MyIP。添加后,在Block Design中,得到如下IP。可以看到,MyIP有一个AXI Slave接口,及其对应的时钟和复位信号。
8) BD设计
AXI Lite的外设很简单,只有一个AXI Slave接口,及其对应的时钟和复位信号。把AXI Slave接口通过AXI Interconnect连接到某个PS的AXI Master接口,示例是M_AXI_HPM0_FPD,再提供对应的时钟和复位信号就可以。AXI连接两侧的Mater和Slave必须使用同一个时钟和复位信号。
9) 代码分析
创建IP后,在指定的目录下,得到如下的文件夹和和文件。
myip_1.0 │ component.xml │ ├─bd │ bd.tcl │ ├─example_designs │ ├─bfm_design │ │ design.tcl │ │ myip_v1_0_tb.sv │ │ │ └─debug_hw_design │ design.tcl │ myip_v1_0_hw_test.tcl │ ├─hdl │ myip_v1_0.v │ myip_v1_0_S00_AXI.v │ └─xgui myip_v1_0.tcl
myip_v1_0_S00_AXI.v里实现了寄存器及其读写逻辑。
实现寄存器的Verilog HDL代码:
//---------------------------------------------- //-- Signals for user logic register space example //------------------------------------------------ //-- Number of Slave Registers 16 reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg0; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg1; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg2; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg3; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg4; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg5; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg6; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg7; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg8; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg9; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg10; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg11; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg12; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg13; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg14; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg15;
寄存器写操作的Verilog HDL代码的部分片段:
if (slv_reg_wren) begin case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 4'h0: 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 ... ... default : begin slv_reg0 <= slv_reg0; ... ... slv_reg15 <= slv_reg15; end endcase end
寄存器读操作的Verilog HDL代码的部分片段:
// Implement memory mapped register select and read logic generation // Slave register read enable is asserted when valid address is available // and the slave is ready to accept the read address. 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] ) 4'h0 : reg_data_out <= slv_reg0; 4'h1 : reg_data_out <= slv_reg1; 4'h2 : reg_data_out <= slv_reg2; 4'h3 : reg_data_out <= slv_reg3; 4'h4 : reg_data_out <= slv_reg4; 4'h5 : reg_data_out <= slv_reg5; 4'h6 : reg_data_out <= slv_reg6; 4'h7 : reg_data_out <= slv_reg7; 4'h8 : reg_data_out <= slv_reg8; 4'h9 : reg_data_out <= slv_reg9; 4'hA : reg_data_out <= slv_reg10; 4'hB : reg_data_out <= slv_reg11; 4'hC : reg_data_out <= slv_reg12; 4'hD : reg_data_out <= slv_reg13; 4'hE : reg_data_out <= slv_reg14; 4'hF : reg_data_out <= slv_reg15; default : reg_data_out <= 0; endcase end
根据需要,可以将寄存器的某个bit,作为控制信号,连接到应用的逻辑代码。也可以将应用逻辑代码的某些信号,作为状态信号,连接到寄存器的某个bit,做状态信号,供CPU读取。
软件
简单外设只有寄存器,软件靠读写寄存器,就能实现对硬件的控制。
5. DMA外设设计
如果外设的数据量大,速率要求高,建议使用DMA搬移数据,使PS和PL通过DDR共享数据。
Vivado的工具Create and Package IP同样可以创建支持DMA功能的IP。大部分步骤,和前述“简单数据通路”一样。在“AXI Interfaces”窗口,除了“AXI Lite”接口以外,还要增加“AXI Full Master”接口。
1) 新建AXI接口
在“AXI Interfaces”窗口,点击“+”按钮,能创建一个新的接口。
2) 添加AXI Master接口
在弹出的接口中,为“Interface Type”选择"Full",为“Interface Mode”选择"Master"。
后续操作,和简单外设的操作一样。
3)BD框图
在Block Design中,选择对应的IP。示例中是myip_dma。添加后,在Block Design中,得到如下IP。可以看到,myip_dma多了一个AXI Master接口“M00_AXI”和对应的时钟和复位信号。值得注意的是,myip_dma还多了输入信号m00_axi_init_axi_txn,输出信号m00_axi_txn_done和m00_axi_error。m00_axi_init_axi_txn用于发起写/读传输;m00_axi_txn_done和m00_axi_error由于指示传输是否完成。
4) 代码分析
带有DMA的IP的文件如下。
myip_dma_1.0 │ component.xml │ ├─bd │ bd.tcl │ ├─example_designs │ ├─bfm_design │ │ design.tcl │ │ myip_dma_v1_0_tb.sv │ │ │ └─debug_hw_design │ design.tcl │ myip_dma_v1_0_hw_test.tcl │ ├─hdl │ myip_dma_v1_0.v │ myip_dma_v1_0_M00_AXI.v │ myip_dma_v1_0_S00_AXI.v │ └─xgui myip_dma_v1_0.tcl
AXI Master的HDL代码在文件myip_dma_v1_0_M00_AXI.v中,它实现了通过AXI Master端口写数据、读数据的功能。读写的地址由参数C_M_TARGET_SLAVE_BASE_ADDR指定。
工程师需要根据自己的需要,修改相关逻辑。上述AXI Master示例中,只能向固定地址发起成对的写、读操作。在实际工程中,地址通常由软件配置,写、读操作也经常分开。
软件
DMA外设通常需要由软件配置DMA操作的目标内存。如果是standalone(baremetal,裸核)代码,缺省情况下物理地址和软件地址一样,直接向DMA的内存地址寄存器写入软件得到的地址就可以。如果是Linux下,需要先把软件地址转换成硬件地址,再写入DMA的内存地址寄存器。
6. AXI Stream外设设计
在自己IP里集成DMA,可以根据项目需求定制DMA的功能,实现最优的性能,但是也会带来设计和维护的工作量。
为了降低工作量,可以使用现成的DMA,并在IP里增加AXI Stream接口,对接AXI DMA,实现DMA功能。
大部分步骤,仍然和前述“简单数据通路”一样。在“AXI Interfaces”窗口,除了“AXI Lite”接口以外,选择要增加“AXI Stream Slave”接口, 和“AXI Stream MasterMaster”接口。
1) 新建AXI接口
在“AXI Interfaces”窗口,点击“+”按钮,能创建一个新的接口。
2) 添加AXI Streamm口
在弹出的接口中,为“Interface Type”选择"Stream",为“Interface Mode”选择"Master"或者"Slave"。示例中,既添加了AXI Streamm的Master,也添加了AXI Streamm的Master。实际工程中,工程师可以根据需要只添加其中一个,或者添加多个同样的接口。AXI Streamm的Slave,也被称为Sink;AXI Streamm的Master,也被称为Source。
后续操作,和简单外设的操作一样。
3) BD框图
在Block Design中,选择对应的IP。示例中是myip_stream。添加后,在Block Design中,得到如下IP。可以看到,myip_stream多了一个AXI Stream Master接口,也多了一个AXI Streamm Slave,及其对应的时钟和复位信号。
4) AXI-DMA接口
AXI Streamm需要和IP AXI DMA搭配使用。使用AXI DMA的简单配置就可以,比如如下配置。
AXI DMA有一个AXI Lite接口,用于PS配置;有3个 AXI Master接口,M_AXI_SG, M_AXI_MM2S, M_AXI_S2MM,用于访问DDR等存储器;另外还有两个Stream接口,M_AXIS_MM2S, M_AXIS_S2MM,用于通过AXI Streamm接口交换数据。
AXI-DMA连接
如下图所示,AXI DMA的AXI Lite接口,通过AXI Interconnect 连接到PS AXI Master接口,比如HPM0_FPD,供PS访问。
AXI DMA的3个AXI Master接口,M_AXI_SG, M_AXI_MM2S, M_AXI_S2MM,通过AXI Interconnect 连接到PS的AXI Slave接口,比如S_AXI_HP0_FPD。AXI DMA的两个Stream接口,M_AXIS_MM2S连接到用户IP的S00_AXIS, M_AXIS_S2MM连接到用户IP的M00_AXIS。
通过AXI DMA的驱动,软件可以发起MM2S传输,AXI DMA先通过M_AXI_SG从PS-DDR里读取DMA的描述符,得到数据的源地址,再通过M_AXIS_MM2S从PS-DDR里读取数据。软件也可以发起S2MM传输,AXI DMA先通过M_AXI_SG从PS-DDR里读取DMA的描述符,得到数据的目标地址,再通过M_AXIS_S2MM向PS-DDR里写数据。MM2S传输和S2MM传输既可以分开使用,也可以联合使用。如果联合使用,相当于从PS-DDR到PS-DDR的内存搬移工作。
软件
AXI-DMA的相关软件设计,请参考 MPSoC逻辑加速模块数据通道快速设计。
7. PL外设其它接口 7.1. 中断接口
用户外设还经常用到中断,用于向PS产生中断,通知某些事件。Vivado的工具Create and Package IP也可以创建支持中断功能的IP。大部分步骤,和前述“简单数据通路”一样。在“AXI Interfaces”窗口,勾选“Enable Interrupt Support”, 会得到一个S_AXI_INTR接口和一个中断输出信号irq。
通过AXI Interconnect,把S00_AXI和S_AXI_INTR接口连接连接到PS AXI Master接口,比如HPM0_FPD,供PS访问。把中断输出信号irq,通过IP Concat连接到PS的输入pl_ps_irq。
EMIO接口
PS还提供了简单的GPIO接口:EMIO。
在PS的配置界面下的GPIO里,使能EMIO,根据自己的需要选择数量。然后在BD界面下,就能看见EMIO的信号,包括输出、输入、和三态信号。工程师可以把自己的信号连接到EMIO信号上,就能使用。
软件通过访问GGPIO的Bank 3/4/5,可以控制GPIO,包括操作输出状态,和读取输入电平。
软件经常需要检查PL是否正常工作。很多时候,软件靠检查PL加载的done信号来确定。但done只能保证PL已经被加载,不能保证PL已经正常工作。比如,如果PL使用外部时钟,在外部时钟故障时,PL即使已经被加载,也没有工作。这时候,PS去访问PL的寄存器,会导致PS死机。更好的办法是,PL通过EMIO向PS反馈PL是否已经正常工作。比如PL实现计数器,把计数器的输出连接到EMIO的输入。软件读取EMIO的输入值,如果在变化,说明PL的计数器已经正常工作,那么就代表PL已经正常工作。
8. AXI Firewall
在开发过程中,可能有各种异常状况。如果 AXI传输出现异常,可能导致PS死机,影响调试。为了提高系统可靠性,可以使用Xilinx提供的IP -- AXI Firewall,把它插在正常的AXI Master和 AXI Slave接口之间。下图是AXI Firewall的连接示例。
使用软件使能 AXI Firewall后,如果AXI传输出现异常,AXI Firewall会正常结束AXI传输,保证软件继续运行,工程师可以检查AXI传输的错误。AXI Firewall输出的错误信号和中断信号,都可以连接到PS GPIO,或者ILA。工程师可以读出具体的错误信息。
9. 参考文档
Zynq UltraScale+ MPSoC Embedded Design Methodology Guide 2017-03-31
Zynq UltraScale+ MPSoC Software Developers Guide 2019-07-01
Zynq UltraScale+ MPSoC Embedded Design Tutorial 2018-07-31
Zynq UltraScale+ MPSoC Technical Reference Manual 2019-08-21
UltraFast Embedded Design Methodology Guide 2018-04-20
Introducing the UltraFAST Embedded Design Methodology Checklist 2014-06-10