基于ZCU104的PS和PL数据交互例程(三):vivado中创建IP

发布时间 2023-10-24 20:10:21作者: 水流shuiliu

基于ZCU104的PS和PL数据交互例程(三):vivado中创建IP

以创建带有AXI-LITE接口的IP为例子

按照下面步骤创建

image-20231019172307099

image-20231019172349838 image-20231019172332688 image-20231019172557208

这里注意,这里选择的Number of Registers,会在后面的代码里面对应slv_reg0, slv_reg1, ..., slv_reg3

image-20231019172804671 image-20231019172831799

打开IP目录,右键刚才的IP,选择Eidt in IP Packager

image-20231019172920235

image-20231019173003224

controller_v1_0

双击打开controller_v1_0.v文件

添加端口

image-20231019204728025

连接端口

image-20231019204812847

controller_v1_0_S00_AXI_LITE

双击打开controller_v1_0_S00_AXI_LITE.v文件

添加端口

image-20231019204947581

添加必要逻辑

  1. 声明parameter

    image-20231020102811139

  2. 在前面声明变量

image-20231020102843051

  1. 修改slv_reg逻辑,这里的slv_reg就是当时选择的Number of Registers,在代码里面找到原来的代码,添加下面的逻辑

    always @( posedge S_AXI_ACLK )
    	begin
    	  if ( S_AXI_ARESETN == 1'b0 )
    	    begin
    	      slv_reg0 <= 0;
    	      slv_reg1 <= 0;
    	      slv_reg2 <= 0;
    	      slv_reg3 <= 0;
    	    end 
    	  //Add User logic
    	  else if (slv_reg_vld_axi[0] | slv_reg_vld_axi[1]) begin
    	    case (slv_reg_vld_axi)
                SLV0:begin
                    slv_reg0 <= slv_reg0_data;
                    slv_reg1 <= slv_reg1;
                    slv_reg2 <= slv_reg2;
                    slv_reg3 <= slv_reg3;
                end 
                SLV1:begin
                    slv_reg0 <= slv_reg0;
                    slv_reg1 <= slv_reg1_data;
                    slv_reg2 <= slv_reg2;
                    slv_reg3 <= slv_reg3;
                end
                default: begin
                    slv_reg0 <= slv_reg0;
                    slv_reg1 <= slv_reg1;
                    slv_reg2 <= slv_reg2;
                    slv_reg3 <= slv_reg3;
                end
            endcase
    	  end
          //Add User logic end
    	  else begin
    	    if (slv_reg_wren)
    	      begin
    	        case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
    	          2'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  
    	          2'h1:
    	            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  
    	          2'h2:
    	            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  
    	          2'h3:
    	            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  
    	          default : begin
    	                      slv_reg0 <= slv_reg0;
    	                      slv_reg1 <= slv_reg1;
    	                      slv_reg2 <= slv_reg2;
    	                      slv_reg3 <= slv_reg3;
    	                    end
    	        endcase
    	      end
    	  end
    	end    
    

例化模块

先通过添加文件,添加两个新模块:reset_asyn_syn.vuser_ctrl.v

再完成例化

image-20231020102641949

异步复位同步释放

module reset_asyn_syn(
    input  clk      ,
    input  rst_asyn,
    output rstb_syn 
    );
    // maybe key jitter but not matter
    reg [1:0] reset_reg;
    always @(posedge clk or posedge rst_asyn) begin
        if(rst_asyn) begin
            reset_reg <= 2'b0;
        end
        else begin
            reset_reg[0] <= 1'b1;
            reset_reg[1] <= reset_reg[0];
        end
    end

    assign rstb_syn = reset_reg[1];

endmodule

控制逻辑

  1. 时钟:axi有个时钟,PL端有个时钟,可以是同一个,也可以是不同的。这里添加有跨时钟域处理。选择slv_reg0的bit0作为启动信号的判断,如果检测到slv_reg0_bit0的上升沿,则表示PS端要求启动PL端,所以拉高start_DUT信号一个周期。
  2. 状态机逻辑
状态机:

image-20231019211826698

注意事项:

1.读写slv_reg

对于slv_reg1的写入控制,内部模块输出一个数据slv_reg1_data,和对应的有效信号slv_reg1_vld。

然后在slv_reg1接受AXI_lite里面赋值的地方多加一组if-else情况,使得当AXI_lite写入数据时,赋值给slv_reg1;或者slv_reg1_vld拉高时,把slv_reg1_data赋值给slv_reg1;或者保持不变。

slv_reg0同理

2.简单的跨时钟域处理

这里注意写slv_reg的时候,属于跨时钟域操作,对于数据信号可以设置为静态数据,对于vld信号,可以通过打两拍同步到axi时钟域。这里axi的时钟域一般是100M,默认是快时钟域

代码

一般认为ctrl模块跟PL属于同时钟域,跟AXI属于跨时钟域。

module user_ctrl#(
    // Width of S_AXI data bus
	parameter integer C_S_AXI_DATA_WIDTH	= 32,
    // Width of FSM
    parameter FSM_WIDTH = 4,
    parameter IDLE      = 4'b1,
    parameter ENABLE    = 4'b10,
    parameter MYWAIT    = 4'b100,
    parameter FINISH    = 4'b1000
)(
    input                               pl_clk                  ,
    input                               axi_clk                 ,
    input                               pl_rstb                 ,
    input                               axi_rstb                ,
    
    input                               DUT_finish              ,

    input                               slv_reg0_bit0           ,

    output     [C_S_AXI_DATA_WIDTH-1:0]	slv_reg0_data           ,
    output                              slv_reg0_vld_axi        , 
    output     [C_S_AXI_DATA_WIDTH-1:0]	slv_reg1_data           ,
    output                              slv_reg1_vld_axi        ,  
         
    output                              start_DUT                                    
    );

    reg [FSM_WIDTH-1:0] FSM_state ;
    wire slv_reg0_vld,slv_reg1_vld;
    reg [1:0] slv_reg0_vld_reg , slv_reg1_vld_reg;
    
    always @(posedge pl_clk or negedge pl_rstb) begin
        if(!pl_rstb)
            FSM_state <= IDLE;
        else begin
            case (FSM_state)
                IDLE: begin
                    if (slv_reg0_bit0) begin
                        FSM_state <= ENABLE;
                    end
                end
                ENABLE:begin
                    FSM_state <= MYWAIT;
                end
                MYWAIT:begin
                    if (DUT_finish) begin
                        FSM_state <= FINISH;
                    end
                end
                FINISH:begin
                    FSM_state <= IDLE;
                end
                default: FSM_state <= IDLE;
            endcase
        end
    end
    //slv_reg0 output signals
    assign slv_reg0_data = {C_S_AXI_DATA_WIDTH{1'b0}};
    assign slv_reg0_vld  = (FSM_state == ENABLE) ? 1'b1 : 1'b0;

    //slv_reg1 output signals
    assign slv_reg1_data = {31'd0,1'b1};
    assign slv_reg1_vld  = (FSM_state == FINISH) ? 1'b1 : 1'b0;

    //translate to axi clk 
    assign slv_reg0_vld_axi = slv_reg0_vld_reg[1];
    assign slv_reg1_vld_axi = slv_reg1_vld_reg[1];

    always @(posedge axi_clk or negedge axi_rstb) begin
        if(!axi_rstb) begin
            slv_reg0_vld_reg <= 2'b0;
            slv_reg1_vld_reg <= 2'b0;
        end
        else begin
            slv_reg0_vld_reg <= {slv_reg0_vld_reg[0],slv_reg0_vld};
            slv_reg1_vld_reg <= {slv_reg1_vld_reg[0],slv_reg1_vld};
        end
    end
    //enable output 
    assign enable = (FSM_state == ENABLE) ? 1'b1 : 1'b0;
endmodule

封装IP

image-20231020103403476

具体的封装步骤,跟基于ZCU104的PS和PL数据交互例程(二):vivado中封装现有工程成IP类似,也可以参考正点原子2_启明星ZYNQ之嵌入式SDK开发指南_V2.0.pdf------>第六章 自定义IP核-呼吸灯实验

image-20231020103445199