Menu Close

ZYNQ 系统中,自建axi-lite 寄存器模式下的gpio模块

在zynq的使用中,我们会经常使用一些自建的IP,本文章向大家介绍如何自建一个axi-lite的基于寄存器模式下的gpio模块

 

硬件: fii_7030

vivado: 2018.3

 

在zynq 系统设计中,我们会创建一些自定义的IP ,以实现用户的某些功能。其中,比较常见的是创建axi-lite 寄存器IP(当然也有其他一些自建的IP)。 寄存器模块可以实现很多功能,可以为用户的IP提供cpu的寄存器接口, 也可以用来控制gpio等。

 

打开vivado 工程,选择create and Package New IP…

%title插图%num

 

选择next

%title插图%num

 

选择Create a new axi4 peripheral

%title插图%num

 

添加用户自建IP的名称:

%title插图%num

 

选择lite,这里我们选择8个寄存器,用户可以根据实际需求更改寄存器个数,每个寄存器都是32bit的,(data width = 32).

%title插图%num

 

选择 edit IP,编辑系统自动生成的带有axi-lite接口的寄存器模块。

%title插图%num

 

%title插图%num

 

双击my_module_v1.0 文件

我们会发现打开的文件没有实质内容, 只是负责将信号进一步的传递,所以我们继续打开my_module_v1.0_s00_axi文件:

%title插图%num

 

打开文件后,我们会发现文件很长,内容也比较多, 如果大家学习过axi总线协议,可以对照协议对每个信号进行理解,如果对axi总线不是很熟悉,那么我们就直接找到我们需要的代码即可。

首先,在模块接口上会发现:

%title插图%num

 

我们可以把自己定义的信号添加到这个位置即可。(注:当前的模块由vivado 自动生成的, 但系统生成的模块不一定满足用户的需求,所以我们有时会添加一些用户自定义的接口)。这里,我们就可以把系统生成的寄存器引出,形成自建的gpio (通过定义某个寄存器为gpio)。

 

这里,我们输出8组寄存器:

%title插图%num

 

这样,这些寄存器可以被zynq cpu 控制, 同时,这些寄存器的输出后,我们就可以写自己的verilog 代码,实现相关的自定义功能了。

 

接下来我们找到cpu写寄存器, 这些寄存器被当前模块输出reg_out_x:

  %title插图%num

通过assign 命令,我们就可以实现cpu 对寄存器的写操作输出。

 

 

接下来找到寄存器的输入,然后被cpu 读走的代码:

原始的代码如下:

 

%title插图%num

 

修改之后的代码如下:

%title插图%num

reg_in_x 由模块顶端输入。

 

接下来,我们将用户修改好的端口,逐层传递到顶层即可:

%title插图%num %title插图%num

接下来完成IP核的设计:

%title插图%num

到此ip核已经设计完毕,这个ip核存放在相应的目录下。

 

%title插图%num

到此,这个ip核就可以正常使用,使用方法,和vivado自带的ip核的使用相同。

 

 

接下来,我们使用刚刚建立的ip(my_module) 组建一个cpu 系统。

%title插图%num %title插图%num

当然zynq cpu的配置,根据相关的硬件进行设置。

修改zynq 设置:

%title插图%num%title插图%num %title插图%num %title插图%num

 

 

选择让系统自动连接:

%title插图%num

 

最终的cpu 连接如下:

%title插图%num

%title插图%num

 

%title插图%num

 

这时,我们看到最终生成的ip 例化接口文件(verilog 文件如下):

 

  design_1 design_1_i

       (.DDR_addr(DDR_addr),

        .DDR_ba(DDR_ba),

        .DDR_cas_n(DDR_cas_n),

        .DDR_ck_n(DDR_ck_n),

        .DDR_ck_p(DDR_ck_p),

        .DDR_cke(DDR_cke),

        .DDR_cs_n(DDR_cs_n),

        .DDR_dm(DDR_dm),

        .DDR_dq(DDR_dq),

        .DDR_dqs_n(DDR_dqs_n),

        .DDR_dqs_p(DDR_dqs_p),

        .DDR_odt(DDR_odt),

        .DDR_ras_n(DDR_ras_n),

        .DDR_reset_n(DDR_reset_n),

        .DDR_we_n(DDR_we_n),

        .FCLK_CLK0(FCLK_CLK0),

        .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),

        .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),

        .FIXED_IO_mio(FIXED_IO_mio),

        .FIXED_IO_ps_clk(FIXED_IO_ps_clk),

        .FIXED_IO_ps_porb(FIXED_IO_ps_porb),

        .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),

        .peri_reset_n_out(peri_reset_n_out),

        .reg_in_0_0(reg_in_0_0),

        .reg_in_1_0(reg_in_1_0),

        .reg_in_2_0(reg_in_2_0),

        .reg_in_3_0(reg_in_3_0),

        .reg_in_4_0(reg_in_4_0),

        .reg_in_5_0(reg_in_5_0),

        .reg_in_6_0(reg_in_6_0),

        .reg_in_7_0(reg_in_7_0),

        .reg_out_0_0(reg_out_0_0),

        .reg_out_1_0(reg_out_1_0),

        .reg_out_2_0(reg_out_2_0),

        .reg_out_3_0(reg_out_3_0),

        .reg_out_4_0(reg_out_4_0),

        .reg_out_5_0(reg_out_5_0),

        .reg_out_6_0(reg_out_6_0),

        .reg_out_7_0(reg_out_7_0));

 

之后,我们可以正常的写相关的verilog代码,把reg_in_x_0 和 reg_out_x_0 连接到用户自定义的逻辑上了。

 

举例:

(* mark_debug = “true” *)wire [31:0]     reg_in_0_0;

wire [31:0]     reg_in_1_0;

wire [31:0]     reg_in_2_0;

wire [31:0]     reg_in_3_0;

wire [31:0]     reg_in_4_0;

wire [31:0]     reg_in_5_0;

wire [31:0]     reg_in_6_0;

wire [31:0]     reg_in_7_0;

 

wire [31:0]     reg_out_0_0;

(* mark_debug = “true” *)wire [31:0]     reg_out_1_0;

wire [31:0]     reg_out_2_0;

wire [31:0]     reg_out_3_0;

wire [31:0]     reg_out_4_0;

wire [31:0]     reg_out_5_0;

wire [31:0]     reg_out_6_0;

wire [31:0]     reg_out_7_0;

 

genvar pin_cnt;

generate for (pin_cnt = 0; pin_cnt < 8; pin_cnt = pin_cnt + 1)

begin: test_reg

    assign led[pin_cnt] = reg_out_1_0[pin_cnt] ? 1’bz : reg_out_0_0[pin_cnt];

    assign reg_in_0_0[pin_cnt] = reg_out_1_0[pin_cnt] ? led[pin_cnt] : reg_out_0_0[pin_cnt];

end

endgenerate

 

assign reg_in_0_0[31:8] = reg_out_0_0[31:8];

assign reg_in_1_0 = reg_out_1_0;

assign reg_in_2_0 = reg_out_2_0;

assign reg_in_3_0 = reg_out_3_0;

assign reg_in_4_0 = reg_out_4_0;

assign reg_in_5_0 = reg_out_5_0;

assign reg_in_6_0 = reg_out_6_0;

assign reg_in_7_0 = reg_out_7_0;

 

led[7:0] 设计为inout的,

寄存器reg_out_1_0 为方向寄存器, 1 input; 0 output

Reg_out_0_0 为cpu 输出值到led gpio 上。

Reg_in_0_0 为 cpu 读取led 上的值。

 

其他的寄存器 直接做输入,输出连接(没有实际意义,可以作为测试). 

 

到此: cpu 就可以通过读写寄存器来控制led 了。

 

Posted in FPGA, SoC, SoC, SoC硬件开发, 教材与教案

发表评论

相关链接