ZYNQ学习之路3. 定制AXI IP核

       ZYNQ最大的优点就是硬核A9处理器与FPGA的结合,处理器可以扩展出任何使用者想要的外设(数字逻辑外设),FPGA与处理器通过AXI高速总线进行连接,提供了处理器到FPGA的高速带宽(ZYNQ7000最高8Gbps)。AXI总线协议相当复杂,好在Xilinx提供了AXI的打包工具,开发人员只需要在指定的位置添加自己的逻辑功能代码即可,极大的简化了开发过程。ZedBoard推出的官方例子中已经介绍了如何将Xilinx做好的AXI总线IP(如AXI_Timer、AXI_GPIO等)添加到工程中,而下面就让我们一起来自己编写一个简单的AXI总线设备——读取板上的4个Swtich状态,并控制3个LED的外设。

一. 建立LED和开关的AXI IP核

首先查看开发板的原理图,确定LED和开关的引脚:

        根据原理图,确定引脚为:

表1. LED和开关引脚
元件LED_RLED_GLED_BSW1SW2SW3SW4
引脚R14Y16Y17R19T19G14J14

1.1 在vivado开发环境里新建一个LED_AXI的工程,并生成一个名为system的Block Diagram文件

  再添加ZYNQ7 Processing System内核系统到这个原理图中,创建好的vivado工程及Bloack Diagram如下:

  双击ZYNQ7 Processing System,配置ZYNQ的DDR为MT41K256M16 RE-125

  配置MIO48,MIO49为uart1的引脚

1.2 下面开始创建自定义的LED和开关IP核

        点击菜单Tools->Create and Package IP...

        点击next, 选择Create a new AXI4 peripheral项,上面三个是将当前工程打包。

        修改IP的名称和存放位置:

        这里显示了AXI总线接口的名字,接口时Slave, 数据位宽为32位,IP寄存器是4个,点击finish完成设计。

        打开IP Catalog界面,我们可以看到LED_IP_v1.0,此时这个IP不具备任何功能。

        右键选中LED_IP_v1.0,然后选择Edit in IP Packager项。点击OK,软件会打开另外一个vivado窗口对这个IP进行编辑

       双击顶层文件LED_IP_V1_0.v打开,在下图的位置添加LED和开关的引脚端口定义:

 

        在顶层文件LED_IP_V1_0.v的下面位置对LED和开关引脚例化:

        打开LED_IP_v1_0_S00_AXI.v文件,在以下位置添加LED和开关的引脚端口定义:

        屏蔽AXI总线对slv_reg0的操作。对于寄存器来说,输入端口不能自行拉低或拉高,所以写操作无效,必须屏蔽。

        在程序的最后注释(//Add user logic here)位置添加代码,实现自己的逻辑功能,以下代码的意思是根据slv_reg0读取开关的状态,slv_reg1设置LED的状态。如果看不懂建议再回顾以下verilog基础吧!

// Add user logic here
always @( posedge S_AXI_ACLK )
begin
    if ( S_AXI_ARESETN == 1'b0 )
    begin
        LED  <= 0;
    end 
    else
    begin    
        LED <= slv_reg0[2:0]; 
    end
end    
always @( posedge S_AXI_ACLK )
begin
    slv_reg0[3:0] <= SWITCH;
end    
// User logic ends
always @( posedge S_AXI_ACLK )
begin
    if ( S_AXI_ARESETN == 1'b0 )
    begin
        LED  <= 0;
    end 
    else
    begin    
        LED <= slv_reg0[2:0]; 
    end
end    
always @( posedge S_AXI_ACLK )
begin
    slv_reg0[3:0] <= SWITCH;
end    
// User logic ends

        编译LED_IP_v1_0这个项目,确保没有错误。

        双击IP-XACT下的component.xml文件,点击ports and interfaces项,点击merge changesfrom ports and Interface wizard。

        在窗口中多出了我们在程序中定义的端口:

        再对其他没有打钩的file groups点击Merge changes from file groups wizard来更新文件和驱动。

        选择Review and Package项,然后点击Re-Package IP按钮技术IP核的设计。

        到此为止,自定义的IP和设计完成了,关闭IP核的vivado工程回到LED_AXI的工程中来。

1.3 回到LED_AXI工程中设计系统原理

        在Diagram窗口中搜索LED会出现刚才自定义的IP核:LED_IP_v1.0.

        双击LED_IP_v1.0添加,点击Run Connection Automation,选中All automation进行总线的自动连接。

        点击Run Block Automation后完成系统的设计,最后原理图如下所示。

 

        此时LED和SWITCH的引脚并没有自动产生为外部的port,这里需要手动设置这些引脚为外部引脚,右键选中LED,选择make external

        在Source窗口中选择system.bd,右键点击并选择Generate output Projects和Create HDL Wrapper选项进行操作,完成之后如下图所示:

        编译后配置引脚约束,然后综合生成Bitstream文件。

        导出硬件:选择菜单File->Export->Export Hardware...导出,勾选include bitstream。

        导出硬件后,选择菜单File->Launch SDK, 启动SDK开发环境,进行裸机驱动编写验证IP的正确性。

 

二. SDK软件编程

2.1 在SDK环境里重新新建一个名为LED_test的工程,软件会自动创建一个LED_test_bsp的工程,项目使用HelloWorld为模板,新建好的工程如下:

        同样的,为了能使用串口输出到终端,需要设置BSP工程的属性,指定终端标准输入输出为串口1,而不是默认的串口0。

2.2 修改LED_test工程的helloworld.c, 代码如下:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "LED_IP.h"
#include "xil_io.h"
#include <stdlib.h>

//look up in address editor
#define LED_BASEADDR	0x43C00000
int reg_led, reg_switch;

int main()
{
    int i = 0;
    init_platform();

    printf("======= AXI IP Test ======\n\r");
    printf("Read Switch register...\n");
    printf("Light on R G B LED...\n");

    while(1)
    {
    	 reg_switch = LED_IP_mReadReg(LED_BASEADDR, 0);//Switch status
    	 printf("switch=0x%0x i =%d\n",reg_switch, i);
    	 LED_IP_mWriteReg(LED_BASEADDR, 4, i);
    	 i++;
    	 if(i>=8) i = 0;
    	 sleep(1);
    }

    cleanup_platform();
    return 0;
}//look up in address editor
#define LED_BASEADDR	0x43C00000
int reg_led, reg_switch;

int main()
{
    int i = 0;
    init_platform();

    printf("======= AXI IP Test ======\n\r");
    printf("Read Switch register...\n");
    printf("Light on R G B LED...\n");

    while(1)
    {
    	 reg_switch = LED_IP_mReadReg(LED_BASEADDR, 0);//Switch status
    	 printf("switch=0x%0x i =%d\n",reg_switch, i);
    	 LED_IP_mWriteReg(LED_BASEADDR, 4, i);
    	 i++;
    	 if(i>=8) i = 0;
    	 sleep(1);
    }

    cleanup_platform();
    return 0;
}

        再这里简要介绍一下驱动代码,LED_BASEADDR的是LED AXI总线挂载在处理上的地址,这个地址在添加AXI外设时软件会自动分配一个地址,默认是0x43c00000,也可以在软件的Address editor中编辑自定义的地址,但必须是AXI外设地址空间范围内。LED_IP_mReadReg函数是读取自定义AXI外设的寄存器数值,相应的LED_IP_mWriteReg是写AXI外设寄存器值,第一个参数是AXI外设的基地址,第二个参数是地址偏移,在设计verilog逻辑代码是,开关使用的是slv_reg0,所以读取开关状态是0偏移地址,而LED是slv_reg1,地址偏移是4。这里的两个读写函数都是由SDK软件自动生成,当然也可以自己实现这两个函数,那么久需要用到系统库函数读写寄存器。

2.3 下载调试

        首先需要下载FPGA的程序,点击菜单Xilinx Tools->Program FPGA:

        再右键选择LED_test, 点击Run as->1 launch on hardware. 在串口终端中显示如下:

        同时RGB三色灯呈现8种颜色循环闪烁。到处位置,FPGA中的LED和开关的IP核创建完成。

 

三. Linux下的IP核驱动及应用

3.1 在Eclipse中配置Linux驱动程序开发环境,配置方法参考前面的《Eclipse开发ZYNQ驱动程序》教程,编写代码如下:

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include <linux/ioctl.h>
#include<linux/types.h>
#include<linux/delay.h>
#include <linux/miscdevice.h>

#include <linux/gpio.h> //gpio 操作函数
#include <asm/io.h> //io读取函数
#include <asm/uaccess.h>

#define DEVICE_NAME "LED"
#define LED_MAJOR 252
#define LED_MINOR 0

#define LED_BASEADDR	0x43C00000
unsigned int* LED_Address = 0;

int led_open(struct inode* inode,struct file* pfile);
int led_release(struct inode* inode,struct file* pfile);
int led_ioctl(struct file* pfile,unsigned int cmd,unsigned long arg);

static const struct file_operations led_fops =
{
    .owner = THIS_MODULE,
    .open  = led_open,
    .release = led_release,
    .unlocked_ioctl = led_ioctl
};

int led_open(struct inode* inode,struct file* pfile)
{
    printk("Open\n");
    LED_Address = ioremap(LED_BASEADDR + 4,4);
    return 0;
}

int led_release(struct inode* inode,struct file* pfile)
{
    iounmap((void*)(LED_BASEADDR + 4));
    printk("LED release\n");
    return 0;
}

int led_ioctl(struct file* pfile,unsigned int cmd,unsigned long arg)
{
    printk("ioctl\n");
    *LED_Address = arg;
    return 0;
}

static struct miscdevice LED_misc =
{
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &led_fops,
};
static int __init led_init(void)
{
    int ret;
    ret = misc_register(&LED_misc);
    if(ret)
    {
        printk("Error:misc_register failed!\n");
        return 0;
    }
    printk("LED module register successfully!\n");
    return 0;
}

static void __exit led_exit(void)
{
    misc_deregister(&LED_misc);
    printk("Exit module\n");
}

MODULE_AUTHOR("Xiong.guo");
MODULE_LICENSE("Dual BSD/GPL");

module_init(led_init);
module_exit(led_exit);

        此处驱动不做过多的解释,因为这是最简单的Linux驱动程序了。编译驱动模块,得到ZYNQ_LED.ko文件。

3.2 在Eclipse中新建C++工程,配置方法见其他教程,编写LED的测试程序,代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include "stdlib.h"

int main(int argc, char *argv[]){
    int fd;

    int i = 0;
    printf("ZYNQ LED driver\n");
    fd = open("/dev/LED",0);
    if(fd < 0)
    {
    	printf("can't open LED\n");
    	return 0;
    }

    while(1)
    {
    	for(i=0;i<8;i++)
    	{
            ioctl(fd,i,i);
	    sleep(1);
	    ioctl(fd,i,i);
	    sleep(1);
    	}
    }

    close(fd);
    printf("exit led\n");

    return 0;
}

        编译程序得到LED_test文件。

3.3 运行程序测试,开发板与Ubuntu系统通过NFS文件传输,将ZYNQ_LED.ko和LED_test两个文件放入NFS的根目录下,在开发板的终端挂载nfs的目录到/mnt目录下,则接着可以加载模块并运行程序:

$ cd /mnt/
$ insmod ZYNQ_LED.ko
$ ./LED_test

        运行程序后,开发板上的三色LED循环闪烁,查看/dev目录,可以看到系统文件中多了一个名为LED的文件,这个便是刚才的驱动程序所建立的文件节点。

 

四. 总结

        本文详细介绍了如何在ZYNQ7000中一步一步的建立自定义的IP核,并在裸机环境使用仿真器进行验证,在裸机运行无误的情况下再在Linux系统中编写驱动程序进行测试,以及Linux应用程序的测试。在以后的教程中大致上都是这样一个调试步骤,FPGA->逻辑仿真->裸机调试->Linux驱动->Linux应用,至此,ZYNQ SOC的开发流程基本都已经熟悉吧!


欢迎关注亦梦云烟的微信公众号: 亦梦智能计算

 

  • 17
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
《基于zynq的FPGA基础入门.pdf》是一本介绍使用Zynq FPGA进行基础入门的教材或指南。Zynq FPGA是一种将ARM处理器和FPGA集成在一起的芯片,结合了处理器的软件优势和FPGA的硬件灵活性。 该教材可能从以下几个方面进行介绍: 首先,教材可能会介绍Zynq FPGA的基本概念和架构。它会解释处理器和FPGA之间的互联关系以及他们各自的功能和优势。读者将了解如何在Zynq FPGA上进行软件和硬件开发,以及如何使用FPGA进行性能加速和工作负载分配。 其次,教材可能会介绍基本的FPGA和硬件描述语言(HDL)知识。读者将学习FPGA的基本概念,如逻辑门、寄存器和时钟等。同时,他们将学习到如何使用HDL(如VHDL或Verilog)来描述和设计硬件电路。这将为读者理解如何使用Zynq FPGA进行自定义硬件设计奠定基础。 接着,教材可能会介绍如何编写和调试FPGA的软件驱动程序。Zynq FPGA具有嵌入式ARM处理器,因此读者将学习如何使用C/C++等编程语言编写驱动程序,以便与外部设备进行通信。他们还将学习调试技巧,以便能够快速解决可能出现的问题。 最后,教材可能会提供一些实际的项目示例,让读者能够应用所学知识。这些示例项目可能包括使用FPGA实现数字信号处理(DSP)、图像处理、嵌入式系统等。通过实践,读者将能够加深对Zynq FPGA的理解,并掌握如何在实际应用中进行开发和调试。 总而言之,《基于zynq的FPGA基础入门.pdf》是一本为初学者打开Zynq FPGA的大门的教材。通过学习该教材,读者将能够了解Zynq FPGA的基本概念、硬件描述语言和软件驱动程序编写,从而为将来的FPGA开发奠定良好的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值