ZYNQ7020--动态加载CPU1程序<1>

本文档详细介绍了如何在ZYNQ7020平台上配置PetaLinux,使得CPU0运行Linux系统,CPU1运行裸机程序。主要内容包括设置硬件配置、PetaLinux端的配置、设备树修改、驱动添加和验证。实验目标是实现CPU0动态加载CPU1的LED控制程序,并提供了驱动代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

配合食用“ZYNQ7020--AMP下裸机程序开发 <2>

1.实验目标

        CPU0运行petalinux,CPU1运行裸机程序。CPU0启动后,可动态加载CPU1程序。

        测试:通过分别加载两个程序来验证是否成功。

        CPU1共有两个程序:一个控制两路LED灯闪烁,另一个控制两路LED按照0-1-2-3二进制显示。

2.硬件配置

  • 串口0
  • 网口
  • SD0
  • AXI-GPIO

详细配置过程参照《Z7-Lite 系列教程 之 Linux 系统篇》第5章和第9章。

工程文件编译后,导出硬件描述文件至SDK。

3.PetaLinux端配置

创建工程

petalinux-create --type project --template zynq --name ampgpio  

 指定工作目录

petalinux-config --get-hw-description=.  

3.1 工程配置

主菜单 ---> DTG Settings ---> Kernel Bootargs,取消 generate boot args automatically,手动填入如下信息:

console=ttyPS0,115200 earlyprintk maxcpus=1

 保存退出

3.2 内核配置

主要用于自动初始化下axi-gpio相关内容,配置内核。

petalinux-config -c kernel                          

等待弹窗,此处无需修改配置,直接保存,退出即可。

 3.3 修改设备树

路径:工程目录 ---> project-spec ---> meta-user ---> recipes-bsp ---> device-tree ---> files ---> system-user.dtsi 添加如下内容:

/include/ "system-conf.dtsi"
/ {    
    reserved-memory {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        reserved: buffer@0x18000000 {       //512-384 = 128M, 长度 120M
            no-map;
            reg = <0x18000000 0x07800000>;
        };
    };
  
    reserved-driver@0 {
        compatible = "xlnx,reserved-memory";
        memory-region = <&reserved>;
    };
   
    dbox_ipc_dev_instance: dbox_ipc_dev@0 { //112M
        compatible = "dbox,dbox-ipc-dev";
        reg = <0x18000000 0x07000000>;
        ipi12 = <12>;
        ipi13 = <13>;   
    };

    usb_phy0:usb_phy0{
        compatible = "ulpi-phy";
        #phy-cells = <0>;
        reg = <0xe0002000 0x1000>;
        view-port = <0x170>;
        drv-vbus;
    };
};

&axi_ethernet_0{
    local-mac-address = [B4 A9 FC 80 0A 54];
    phy-handle = <&phy1>;
    xlnx,has-mdio = <0x1>;
    phy-mode = "mii";

    mdio{
        #address-cells = <1>;
        #size-cells = <0>;
        phy1:phy@1{
            compatible = "realtek,rtl8201","ethernet-phy-id001c.c816";
            device_type = "ethernet-phy";
            reg = <0>;
        };
    };
};

&usb0{
    dr_mode = "host";
    usb-phy = <&usb_phy0>;
};

reserved-memory 和 reserved-driver: 用于为CPU1保留内存,

dbox_ipcb_dev_instance:是基于xilinx官方remotporc AMP程序,网上的一个简化版,网址如下:

zynq7000 AMP双核IPC+SharedMem通信_Donce Jiang的博客-优快云博客_zynq7000 双核通信

需要注意:

dbox_ipcb_dev_instance 里边的地址要和 reserved 地址保持一致,也可能要大于reserved。dbox_ipc_dev_instance中reg后边一个参数,应该是长度,这个值不能超过reserved中reg的第二个参数。

3.4 添加驱动

参考教程:使用petalinux写驱动_沐唐的博客-优快云博客_petalinux编译驱动

3.4.1 创建驱动工程

petalinux-create -t modules --name dbox-ipc-dev

使用如下指令时 无需3.4.3

petalinux-create -t modules --name dbox-ipc-dev --enable

3.4.2 修改.c文件

路径:执行上边指令后,INFO会显示路径

 工程目录 ---> project-spec ---> meta-user ---> recipes-modules ---> dbox-ipc-dev ---> files ---> dbox-ipc-dev.c ,内容如下:

/*  dbox-ipc-dev.c - The simplest kernel module.

* Copyright (C) 2013 - 2016 Xilinx, Inc
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.

*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License along
*   with this program. If not, see <http://www.gnu.org/licenses/>.

*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h> 
#include <linux/cdev.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#include <linux/of_irq.h>
#include <linux/fs.h>
#include <linux/fcntl.h> 
#include <../../arch/arm/mach-zynq/common.h>
#include <linux/irqchip/arm-gic.h> 

//extern int zynq_cpun_stop(int cpu);

/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");
MODULE_DESCRIPTION
    ("dbox-ipc-dev - loadable module template generated by petalinux-create -t modules");

#define DRIVER_NAME "dbox-ipc-dev"
#define DRIVER_NUM 1

/* Simple example of how to receive command line parameters to your module.
   Delete if you don't need them */
unsigned myint = 0xdeadbeef;
char *mystr = "default";

module_param(myint, int, S_IRUGO);
module_param(mystr, charp, S_IRUGO);

#define StarCpu1            0x10000000
#define StopCpu1            0x20000000
#define KickCpu1            0x30000000

struct dbox_ipc_dev_local {
    int irq;
    int cpu0_to_cpu1_ipi;
    int cpu1_to_cpu0_ipi;
    unsigned long mem_start;
    unsigned long mem_end;
    void __iomem *base_addr;
};

struct dbox_ipc_dev 
{ 
    dev_t devid;                   /*  设备号  */ 
    struct cdev chdev;              /* cdev 结构体  */ 
    struct class *class;            /*  类  */ 
    struct device *device;          /*  设备  */ 
    struct dbox_ipc_dev_local *param;
    struct fasync_struct *async_queue; 
};

static struct dbox_ipc_dev dbox_ipc_dev_;

static int dbox_ipc_dev_open(struct inode *inode, struct file *filp) 
{ 
    //cpu_up(1);
    //printk("dbox_ipc_dev Dev: open success  \r\n"); 
                    
    return 0; 
} 

static ssize_t dbox_ipc_dev_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt) 
{
    int ret; 
    
    ret = copy_from_user((unsigned char*)(dbox_ipc_dev_.param->base_addr + (*offt)), buf, cnt);  
    
    if(0 > ret)
    {
        printk(KERN_ERR "dbox_ipc_dev_ Dev: Failed to copy data from user space \r\n"); 
        return -EFAULT;             
    }
    
    //printk("dbox_ipc_dev_ Dev: write add: %08X  cnt: %d\r\n",(unsigned int)(dbox_ipc_dev_.param->base_addr + (*offt)),cnt); 
    
    *offt = *offt + cnt;
    
    return cnt;
}

static ssize_t dbox_ipc_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) 
{ 
    int ret = 0; 
    
    ret = copy_to_user(buf, (unsigned char*)(dbox_ipc_dev_.param->base_addr + (*offt)) , cnt); 
    
    if(ret < 0)
    { 
        printk(KERN_ERR "dbox_ipc_dev_ Dev: Failed to copy data to user space \r\n"); 
    } 
    
    *offt = *offt + cnt;
    
    return cnt;  
}

static long dbox_ipc_dev_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) 
{
    int ret = 0; 
    //printk("cmd %d \n" ,cmd);
    switch(cmd)
    {
    case StarCpu1:
        ret = cpu_down(1);
        if (ret && (ret != -EBUSY)) 
        {
            printk("!! Can't release cpu1 %d\n",ret);
            return ret;
        }
        //zynq_cpun_stop(1);
        zynq_cpun_start((u32)dbox_ipc_dev_.param->mem_start, 1);
        //printk("StarCpu1\n");
        break;      
    case StopCpu1:  
        ret = cpu_up(1);
        if (ret)
        {
            printk("!! Can't power on cpu1 %d\n", ret);
        }
        //printk("StopCpu1\n");
        break;
    case KickCpu1:
        gic_raise_softirq(cpumask_of(1), dbox_ipc_dev_.param->cpu0_to_cpu1_ipi);
        //printk("KickCpu %d  %d \n",1,dbox_ipc_dev_.param->cpu0_to_cpu1_ipi);
        break;      
    }
    return 0;
}
/*********************************************************************
*
*
*
**********************************************************************/
loff_t dbox_ipc_dev_llseek(struct file *filp, loff_t off, int whence)
{
    loff_t newpos;
    switch(whence)
    {
    case 0: /* SEEK_SET */
        newpos = off;    
        break;
    case 1: /* SEEK_CUR */
        newpos = filp->f_pos + off;
        break;
    case 2: /* SEEK_END */
        if(whence>0)
            newpos = (dbox_ipc_dev_.param->mem_end - dbox_ipc_dev_.param->mem_start);
        else
            newpos = (dbox_ipc_dev_.param->mem_end - dbox_ipc_dev_.param->mem_start)+ whence;       
        break;
    default: /* can't happen */
        return -EINVAL;
    }

    if (newpos < 0)
        return -EINVAL;
    
    filp->f_pos = newpos;

    return newpos;
}
/*********************************************************************
*
*
*
**********************************************************************/
static int dbox_ipc_dev_fasync(int fd, struct file *filp, int on) 
{
    return fasync_helper(fd, filp, on, &dbox_ipc_dev_.async_queue); 
}
/*********************************************************************
*
*
*
**********************************************************************/
static int dbox_ipc_dev_fasync_release(struct inode *inode, struct file *filp) 
{
    return dbox_ipc_dev_fasync(-1, filp, 0); 
}
/*********************************************************************
*
*
*
**********************************************************************/
static struct file_operations dbox_ipc_dev_fops = 
{ 
    .owner = THIS_MODULE, 
    .open = dbox_ipc_dev_open, 
    .write = dbox_ipc_dev_write, 
    .read = dbox_ipc_dev_read, 
    .unlocked_ioctl = dbox_ipc_dev_ioctl, 
    .llseek = dbox_ipc_dev_llseek, 
    .fasync = dbox_ipc_dev_fasync, 
    .release = dbox_ipc_dev_fasync_release, 
 }; 
/*********************************************************************
*
*
*
**********************************************************************/
static int dbox_ipc_dev_param_init(struct dbox_ipc_dev_local *nd) 
{ 
    dbox_ipc_dev_.param = nd;   
    cpu_down(1);
    return 0;
}
/*********************************************************************
*
*IPI 中断通知Linux CPU0 有信息要处理
*
**********************************************************************/
static void cpu1_to_cpu0_ipi_kick(void)
{
    //printk("[dbox]-->>ipi %d \n",dbox_ipc_dev_.param->cpu1_to_cpu0_ipi);
    if(dbox_ipc_dev_.async_queue) 
    {
        //printk("ipi %d  kick SIGIO \n",dbox_ipc_dev_.param->cpu1_to_cpu0_ipi);
        kill_fasync(&dbox_ipc_dev_.async_queue, SIGIO, POLL_IN); 
    }
    else
    {
        //printk("ipi %d kick but async_queue is null \n",dbox_ipc_dev_.param->cpu1_to_cpu0_ipi);
    }
}

static irqreturn_t dbox_ipc_dev_irq(int irq, void *lp)
{
    printk("dbox-ipc-dev interrupt\n");
    return IRQ_HANDLED;
}

static int dbox_ipc_dev_probe(struct platform_device *pdev)
{
    struct resource *r_irq; /* Interrupt resources */
    struct resource *r_mem; /* IO mem resources */
    struct device *dev = &pdev->dev;
    struct dbox_ipc_dev_local *lp = NULL;

    int rc = 0;
    dev_info(dev, "Device Tree Probing\n");
    printk("---->>>>>dbox ipc dev Device Tree Probing\n");
    /* Get iospace for the device */
    r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!r_mem) {
        dev_err(dev, "invalid address\n");
        return -ENODEV;
    }
    lp = (struct dbox_ipc_dev_local *) kmalloc(sizeof(struct dbox_ipc_dev_local), GFP_KERNEL);
    if (!lp) {
        dev_err(dev, "Cound not allocate dbox-ipc-dev device\n");
        return -ENOMEM;
    }
    dev_set_drvdata(dev, lp);
    lp->mem_start = r_mem->start;
    lp->mem_end = r_mem->end;

    if (!request_mem_region(lp->mem_start,
                lp->mem_end - lp->mem_start + 1,
                DRIVER_NAME)) {
        dev_err(dev, "Couldn't lock memory region at %p\n",
            (void *)lp->mem_start);
        rc = -EBUSY;
        goto error1;
    }

    lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    if (!lp->base_addr) {
        dev_err(dev, "dbox-ipc-dev: Could not allocate iomem\n");
        rc = -EIO;
        goto error2;
    }

    lp->cpu0_to_cpu1_ipi = 12;
    lp->cpu1_to_cpu0_ipi = 13;//默认值

    /* Read ipi12 ipi number 用于 ucos 通知 linux */
    rc = of_property_read_u32(pdev->dev.of_node, "ipi12",&lp->cpu1_to_cpu0_ipi);
    if (rc < 0) 
    {
        dev_err(&pdev->dev, "unable to read property ipi 12");
        goto error3;
    }
    rc = set_ipi_handler(lp->cpu1_to_cpu0_ipi, cpu1_to_cpu0_ipi_kick,"cpu 1 kick cpu0");
    if (rc) 
    {
        dev_err(&pdev->dev, "IPI 12 handler already registered\n");
        goto error3;
    }
    /* Read ipi13 ipi number 用于 linux 通知 ucos */
    rc = of_property_read_u32(pdev->dev.of_node, "ipi13",&lp->cpu0_to_cpu1_ipi);
    if (rc < 0) 
    {
        dev_err(&pdev->dev, "unable to read property ipi 13");
        goto error3;
    }
    printk("dbox_ipc_dev_device at 0x%08x mapped to 0x%08x len= %08X , ipi= %d\n",
        (unsigned int __force)lp->mem_start,
        (unsigned int __force)lp->base_addr,
        (unsigned int __force)(lp->mem_end - lp->mem_start+1),
        lp->cpu0_to_cpu1_ipi);
    
    dbox_ipc_dev_param_init(lp); 
    
    rc = alloc_chrdev_region(&dbox_ipc_dev_.devid, 0, DRIVER_NUM, DRIVER_NAME); 
    if(rc)
    {
        dev_err(&pdev->dev, "unable to alloc_chrdev_region ");
    }
    
    dbox_ipc_dev_.chdev.owner = THIS_MODULE; 
    cdev_init(&dbox_ipc_dev_.chdev, &dbox_ipc_dev_fops); 
    
    rc = cdev_add(&dbox_ipc_dev_.chdev, dbox_ipc_dev_.devid, 1); 
    if(rc)
    {
        dev_err(&pdev->dev, "unable to cdev_add ");
    }   
    
    dbox_ipc_dev_.class = class_create(THIS_MODULE, DRIVER_NAME); 
    if (IS_ERR(dbox_ipc_dev_.class)) 
    { 
        dev_err(&pdev->dev, "unable to class_create ");
    }   
    
    dbox_ipc_dev_.device = device_create(dbox_ipc_dev_.class, &pdev->dev,dbox_ipc_dev_.devid, NULL, DRIVER_NAME); 
    if (IS_ERR(dbox_ipc_dev_.device)) 
    { 
        dev_err(&pdev->dev, "unable to device_create ");
    }
    
    return 0;
error3:
    clear_ipi_handler(lp->cpu0_to_cpu1_ipi);
error2:
    release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
error1:
    kfree(lp);
    dev_set_drvdata(dev, NULL);
    return rc;
}

static int dbox_ipc_dev_remove(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct dbox_ipc_dev_local *lp = dev_get_drvdata(dev);
    
    device_destroy(dbox_ipc_dev_.class, dbox_ipc_dev_.devid); 
    class_destroy(dbox_ipc_dev_.class);     
    cdev_del(&dbox_ipc_dev_.chdev);     
    unregister_chrdev_region(dbox_ipc_dev_.devid, DRIVER_NUM);  
    clear_ipi_handler(lp->cpu0_to_cpu1_ipi);
    iounmap(lp->base_addr);
    release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    kfree(lp);
    dev_set_drvdata(dev, NULL);
    return 0;
}

static struct of_device_id dbox_ipc_dev_of_match[] = {
    { .compatible = "dbox,dbox-ipc-dev", },
    { /* end of list */ },
};
MODULE_DEVICE_TABLE(of, dbox_ipc_dev_of_match);

static struct platform_driver dbox_ipc_dev_driver = {
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = dbox_ipc_dev_of_match,
    },
    .probe      = dbox_ipc_dev_probe,
    .remove     = dbox_ipc_dev_remove,
};

static int __init dbox_ipc_dev_init(void)
{
    printk("<1>Hello module world.\n");
    printk("<1>Module parameters were (0x%08x) and \"%s\"\n", myint,
           mystr);

    return platform_driver_register(&dbox_ipc_dev_driver);
}

static void __exit dbox_ipc_dev_exit(void)
{
    platform_driver_unregister(&dbox_ipc_dev_driver);
    printk(KERN_ALERT "Goodbye module world.\n");
}

module_init(dbox_ipc_dev_init);
module_exit(dbox_ipc_dev_exit);

3.4.3 将驱动添加到内核

petalinux-config -c rootfs

界面选择 modules ---> ,使能 dbox-ipc-dev,保存。

 3.4.4 编译

petalinux-build

3.4.5 打包

petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga ./images/linux/system.bit --uboot --force

将 images/linux 下的,BOOT.bin 和 image.ub 拷贝至SD卡。

 3.4.6 驱动验证

登录系统,找到 ”/dev/dbox-ipc-dev“ 则表明驱动运行正常

ls /dev/db*				 //看到 dbox-ipc-dev 则表明成功
cat /proc/cpuinfo		 //看到一个cpu则 表明运行成功

文档共60页。主要向初学者提供了Zynq开发的技术方向,针对不同应用给出了基本的参考文档;同时对Zynq双核AMP加载方式做了详细描述,对Zynq的fsbl启动流程做了简单介绍。章节如下: Zynq User Guide 1 介绍 4 2 快速上手指南 4 3 多核开发教程 4 3.1 AMP开发说明 6 3.1.1 快速生成amp工程 6 3.1.2 Generating Boot File 8 3.1.3 烧写程序 9 3.1.4 启动 10 3.1.5 调试 10 3.1.6 总结 11 3.2 SMP开发说明 11 4 ZC706启动代码分析 11 4.1 启动代码 12 4.2 FSBL流程(FOR AMP) 13 4.3 CPU0启动CPU1流程 14 5 程序在线烧写方案及流程 14 5.1 程序烧写需求 14 5.2 提出该需求的原因 14 5.3 程序烧写方案 14 5.3.1 BOOT.BIN组成 14 5.3.2 BOOT.BIN生成方法 15 5.4 FSBL.BIN和APP.BIN等的生成 15 5.5 制作*BIN及烧写的具体步骤 15 5.5.1 制作*bin流程 15 5.5.2 BOOT.bin制作过程 15 5.5.3 FSBL.bin和APP.bin等的生成过程 22 5.6 烧写BOOT.BIN步骤 26 5.6.1 通过SDK工具烧写步骤 26 5.6.2 通过上位机烧写软件的烧写步骤 29 5.6.3 通过串口调试助手烧写步骤 29 6 Zynq Qspi控制器 30 6.1 基本特性 30 6.2 I/O接口 31 6.3 QSPI控制器模式 33 6.3.1 I/O模式 33 6.3.2 线性地址(linear address)模式 33 6.3.3 传统(legacy)SPI模式 34 6.4 QSPI 例程 34 6.5 QSPI控制器支持访问32MB方法 35 6.5.1 Bank地址寄存器(Bank address register) 35 6.5.2 扩展地址模式(Extended address mode) 35 6.5.3 使用新写命令(New commands) 35 6.6 QSPI FLASH选择 35 6.7 作为BOOT器件考虑 35 7 µC/OS系统启动指南 36 7.1 INTRODUCTION 36 7.1.1 Software Requirements 36 7.1.2 Hardware Requirements 36 7.2 HARDWARE DESIGN 37 7.2.1 Step 1. Invoke the Vivado IDE and Create a project 37 7.2.2 Step 2. Create an IP Integrator Design 39 7.2.3 Step 3. Add and setup the Zynq processor system IP block 39 7.2.4 Step 4. Customize the Zynq block for our design 41 7.2.5 Step 5. Add the soft peripherals 45 7.2.6 Step 6. Generate HDL Design Files 47 7.2.7 Step 7. Synthesis, Implement and Generate Bitstream 48 7.3 SOFTWARE DESIGN 49 7.3.1 Step 1. Installation of the µC/OS Repository 49 7.3.2 Step 2. Generate the µC/OS BSP 50 7.3.3 Step 3. Build and Debug the Demonstration Project 54 7.3.4 Step 4. Program the AXI Timer 0 with the ucos_axitimer Driver 55 7.3.5 Step 5. Program the AXI Timer 1 with the Xilinx tmrctr Driver 58 7.4 CONCLUSION 59 8 Linux系统启动指南 59
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值