ZedBoard:自定义函数读写AXI外设

摘要:
本文主要讲如何自定义函数读写外AXI外设,摆脱对SDK库函数的依赖。
本文举AXI_GPIO这个IP来讲解如自定义函数实现对AXI_GPIO的控制。编写过STM32程序的人应该都知道控制一个GPIO端口需要控制连个寄存器。一个是输入输出方向控制寄存器,另一个是数据寄存器。通过方向控制寄存器设置GPIO的输入输出方向,然后通过读写数据寄存器可以实现对GPIO的读写。

一、在Vivado中定制硬件
完整的硬件结构如下:
首先添加一个处理器IP:zynq
然后添加一个AXI_GPIO,设置为8位输入。
再然后自动连接即可。
ZedBoard:自定义函数读写AXI外设_第1张图片
接下来生成顶层文件,将顶层文件修改如下,主要是将8位输入设置为8’b10001000。

//Copyright 1986-2017 Xilinx, Inc. All Rights Reserved.
//--------------------------------------------------------------------------------
//Tool Version: Vivado v.2017.2 (win64) Build 1909853 Thu Jun 15 18:39:09 MDT 2017
//Date        : Sat May 25 07:53:09 2019
//Host        : DESKTOP-4K25U7B running 64-bit major release  (build 9200)
//Command     : generate_target design_1_wrapper.bd
//Design      : design_1_wrapper
//Purpose     : IP block netlist
//--------------------------------------------------------------------------------
`timescale 1 ps / 1 ps

module design_1_wrapper
   (DDR_addr,
    DDR_ba,
    DDR_cas_n,
    DDR_ck_n,
    DDR_ck_p,
    DDR_cke,
    DDR_cs_n,
    DDR_dm,
    DDR_dq,
    DDR_dqs_n,
    DDR_dqs_p,
    DDR_odt,
    DDR_ras_n,
    DDR_reset_n,
    DDR_we_n,
    FIXED_IO_ddr_vrn,
    FIXED_IO_ddr_vrp,
    FIXED_IO_mio,
    FIXED_IO_ps_clk,
    FIXED_IO_ps_porb,
    FIXED_IO_ps_srstb
   // sws_8bits_tri_i
   );
  inout [14:0]DDR_addr;
  inout [2:0]DDR_ba;
  inout DDR_cas_n;
  inout DDR_ck_n;
  inout DDR_ck_p;
  inout DDR_cke;
  inout DDR_cs_n;
  inout [3:0]DDR_dm;
  inout [31:0]DDR_dq;
  inout [3:0]DDR_dqs_n;
  inout [3:0]DDR_dqs_p;
  inout DDR_odt;
  inout DDR_ras_n;
  inout DDR_reset_n;
  inout DDR_we_n;
  inout FIXED_IO_ddr_vrn;
  inout FIXED_IO_ddr_vrp;
  inout [53:0]FIXED_IO_mio;
  inout FIXED_IO_ps_clk;
  inout FIXED_IO_ps_porb;
  inout FIXED_IO_ps_srstb;
 // input [7:0]sws_8bits_tri_i;

  wire [14:0]DDR_addr;
  wire [2:0]DDR_ba;
  wire DDR_cas_n;
  wire DDR_ck_n;
  wire DDR_ck_p;
  wire DDR_cke;
  wire DDR_cs_n;
  wire [3:0]DDR_dm;
  wire [31:0]DDR_dq;
  wire [3:0]DDR_dqs_n;
  wire [3:0]DDR_dqs_p;
  wire DDR_odt;
  wire DDR_ras_n;
  wire DDR_reset_n;
  wire DDR_we_n;
  wire FIXED_IO_ddr_vrn;
  wire FIXED_IO_ddr_vrp;
  wire [53:0]FIXED_IO_mio;
  wire FIXED_IO_ps_clk;
  wire FIXED_IO_ps_porb;
  wire FIXED_IO_ps_srstb;
  wire [7:0]sws_8bits_tri_i;

//输入值为0x88
assign	sws_8bits_tri_i = 8'b10001000;

  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),
        .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),
        .sws_8bits_tri_i(sws_8bits_tri_i));
endmodule

再接着生成比特文件,导入到SDK。

二、在SDK中自定义函数实现对GPIO的读取,并打印输出

根据STM32的编程经验,我们首先找到GPIO的数据寄存器和控制寄存器的地址。一般这两个地址都是等于一个基地址+对应的偏移地址。在Block Design中查看GPIO的基地址。

基地址为:0x41200000
ZedBoard:自定义函数读写AXI外设_第2张图片
查看GPIO的手册,查找对应的偏移地址。

ZedBoard:自定义函数读写AXI外设_第3张图片
AXI_GPIO有两个通道,这里我们只用了其中一个通道。查表得到数据和控制寄存器的偏移地址是:
0x0000,0x0004

ZedBoard:自定义函数读写AXI外设_第4张图片
根据手册中说的,当控制寄存器设置为1时当作输入,设置为0时当作输出。
1:输入
0:输出

创建一个HelloWorld工程。
修改helloworld.c如下:

#include 
#include "platform.h"
#include "xil_printf.h"
//基地址
#define	gpio_base_addr 0x41200000
//数据偏移地址
#define data_offset		0x00000000
//控制寄存器偏移地址
#define	con_offset	0x00000004

int main()
{
	//数据地址指针
	u32	*data_addr=NULL;
	//控制地址指针
	u32	*con_addr=NULL;
	
    //设置GPIO为输入,PS:其实在定制硬件的时候就已经设置为输入了,这里不用设置为输入也可以。而且这里设置为输出也不会影响硬件定制的输入,但是控制寄存器里的值确实已经变了。这是我已经实验过的。
	con_addr=gpio_base_addr+con_offset;
    *(con_addr) = 0xFF;
    
   //读取控制寄存器的值
    u8 sw_con;
    sw_con = *(con_addr);
    
    //读取数据寄存器的值
    u8 sw_status;
    data_addr=gpio_base_addr+data_offset;
    sw_status = *(data_addr);

    //分别打印输出数据寄存器和控制寄存器的值
    printf("addres=0x%x,sw_status=0x%08x\n",(data_addr),sw_status);
    printf("addres=0x%x,sw_status=0x%08x\n",(con_addr),sw_con);

}

运行程序:
串口输出:
ZedBoard:自定义函数读写AXI外设_第5张图片

查看内存:
在这里插入图片描述

可以看到内存数据和串口输出的数据一样。

本文通过直接使用物理地址操作GPIO,摆脱了对SDK函数的依赖。而且可以发现,PL端的任何IP都可以看作是PS的外设,由PS分配地址,统一管理。需要注意的就是,不同的IP,操作的寄存器不同,这里需要自己去查手册。

你可能感兴趣的:(ZedBoard)