本文是昨天发的文章《龙芯杯CPU设计竞赛与ZYNQ设计流程介绍》接续部分。重点介绍传统方式的Linux移植和Xilinx的Petalinux的快速移植开发两种。

部分硬件设计中需要CPU完成对电路寄存器的配置,为了完成Zedboard对FPGA上部分寄存器的配置功能,可以在PS单元(处理器系统)上运行裸机程序(无操作系统支持)完成和PL单元(FPGA部分)的数据交互功能,此时PS单元更像单片机开发;另一种方法是PS单元运行Linux操作系统,通过驱动程序和应用程序完成对硬件寄存器的读写操作,并且Linux有着完整的网络协议栈支持,后续可拓展性更强,可以更好的发挥ZYNQ这种异构架构芯片的性能。主要分为两部分,分别阐述Zedboard中FPGA和处理器互联总线与硬件设计和Zedboard处理器系统上嵌入式Linux的移植与通过驱动和应用程序简单配置FPGA寄存器的实现。上次介绍了没有操作系统下的驱动和应用程序开发,本文介绍带操作系统的驱动和应用程序开发。

1、传统方式移植Linux

Zedboard上电后会首先启动BootRom,bootrom中固化了最初启动需要的初始代码,并根据板卡上的跳线决定从flash或者sd卡或者jtag启动。这里选择从SD卡启动,bootrom中的代码会将SD卡中的启动文件拷贝到RAM或者片上共享缓存中去,为下一步启动做准备。

下一阶段的启动文件负责初始化FPGA的比特流文件和初始化ARM处理器的FSBL文件(VIVADO生成),在PL和PS单元完成最基本的初始化操作后,就需要启动BootLoader来引导后面发linux内核,XIlixn的解决方案中可以将二进制比特流文件和fsbl以及uboot打包成BOOT.bin文件,BOOT.bin中的uboot可以加载内核到内存,并从0x00080000位置启动内核。另外,内核启动还需要设备树和根文件系统。

(1)交叉编译链和开发环境搭建

为了得到能够在嵌入式平台上运行的代码,需要在linux主机上交叉编译需要运行的代码,交叉编译工具链就是提供交叉编译的一套工具集。开发主机选择Ubuntu1604LTS系统,安装VIVADO17.4版本,安装完成后 VIVADO SDK

用时已经自动安装了交叉编译链arm-linux-gnueabihf- ,使用命令

source/opt/Xilinx/SDK/2017.4/setting64.sh

添加引用1后即可使用交叉编译链。Xilinx在较早的VIVADO SDK版本中提供了arm-xilinx-linux-gnueabi-编译链,区别在于arm-linux-gnueabihf-使用硬件加速浮点数运算,而arm-xilinx-linux-gnueabi-使用软件计算。通过查询资料,发现17.4版本的SDK中包含arm-xilinx-linux-gnueabi-编译链的引用,但是软件安装时没有成功安装,这应该是17.4版本的一个BUG,我们在另一台安装15.4版本VIVADO SDK的Ubuntu主机下,找到/opt/Xilinx/SDK/2015/gnu/arm文件夹,将其拷贝到17.4版本对应的目录,发现可以成功引用,输入(交叉编译链)gcc-v查看:

gcc版本为4.9.2。需要注意的是,使用两条编译链中的任意一条都可以用于交叉编译,但是两者之前不兼容,因此使用其中一条交叉编译链即可。17.4自带的gcc编译器版本更高,是6.2.1版本。

为了支持32 位工具,需要预先安装 32 位支持工具包。使用sudo命令获取root权限,apt-get install lib32z1 lib32ncurses5lib32bz2-1.0 lib32stdc++6安装上述工具包。(PS,可以修改Ubuntu镜像源为西电开源社区镜像,实测速度在5MB左右)。安装上述包后还需要安装Openssl库来实现网络保密性,在编译u-boot时会用到,使用命令apt-get install libssl-dev安装。

为了提高工作效率,嵌入式开发通常可以在Windows下使用SourceInsight等内核源码阅读工具来开发驱动和应用程序,而交叉编译环境则往往在linux主机上,因此我们可以使用ssh登陆linux服务器,完成命令控制和编译文件,使用ftp文件传输服务在Windows和linux主机之间传递文件,编译完成的驱动可以以NFS挂载的方式直接在嵌入式开发板运行。搭建工作环境不是本文的重点,因此不再这里详细说明。

(2)U-boot编译

Xilinx官方提供了u-boot的源码,位于https://github.com/Xilinx/u-boot-xlnx/releases,我们按照自己需要的版本进行下载和使用。

将下载好的u-boot-xlnx-xilinx-v2017.1.zip文件上传到Ubuntu服务器,使用命令unzip解压缩后进入u-boot-xlnx-xilinx-v2017.1目录,在 u-boot 的文件夹下有很多子文件夹构成,其中每个文件夹都实现一个对应的功能。

1) api:相关的api函数,如输出字符函数。

2) arch: 与特定的 CPU 构架相关。在该目录下,有u-boot 所支持的各种架构的cpu,并且有一个单独的子目录对应。典型的,arch 文件夹下名字为 arm 的子目录就是 Zynq-7000 SOC所对应使用的 CPU 构架目录。

3)board: 和一些已有开发板有关的文件。每一个开发板都有一个子目录出现在当前目录下

4)common: 实现u-boot 命令行下所支持的命令。在该目录下,每条命令对应一个独立的文件夹。

5)disk: 提供对磁盘的支持。

6)doc:文档说明

7)drivers: 在该目录下保存着 u-boot 所支持的设备驱劢程序。典型的如各种网卡、支持的CFI 癿 Flash 存储器、串口和 USB 等。

8)fs:支持的文件系统

9)include:该目录下保存着 u-boot 所使用的头文件,对各种硬件平台支持的汇编文件、系统的配置文件以及对文件系统支持的文件。该目录下configs 目录有开发板相关的配置头文件,如 zynq_common.h 是与 zynq 开发板相关的配置文件。

10)lib: 该目录下保存着体系结构相关的库文件。

11)net: 该目录下保存着网络协议相关的代码。比如BOOTP 协议、 TFTP 协议、RARP 协议和 NFS 文件系统的实现

12)tools: 该目录下保存着用于生成 u-boot 癿工具,包括 mkimage、 crc、 Makefile 和boards.cfg配置文件。

下面开始进行u-boot的编译,编译u-boot需要扁平化设备树的支持,首先输入命令apt-get installdevice-tree-compiler安装设备树编译工具。安装完成dtc工具后就可以进行u-boot的编译了。

在configs文件下保存有各个开发板的默认配置,我们搜索zynq有关的配置文件,发现zynq_zed_defconfig文件,这个就是Zedboard默认的配置选项。而ax70**系列的则是黑金开发板的默认配置文件。

在编译u-boot之前,需要先将配置选项写入.config配置文件中,输入命令make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zynq_zed_defconfig进行配置。注意,你需要先source /opt/Xilinx/SDK/2017/setting64.sh添加相关引用才能使用,当然也可以把上述命令写入/etc/profile这样就可以开机使用。

当出现written to .configs时,表明配置选项写入成功,接下来我们就可以进行编译u-boot了。

使用命令make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-编译u-boot,经过一段时间的编译后,在u-boot根目录下会出现u-boot文件,我们将其下载到Windows下并重命名为u-boot.elf,等待下一步使用。

(3)生成BOOT.bin启动文件

BOOT.bin需要VIVADO SDK生成fsbl,然后将u-boot和VIVADO生成的比特流打包。

启动VIVADO SDK,选择File ->New -> Application Project创建一个新的SDK工程,工程命名为fsbl,其余保持默认不变。

点击next,选择ZYNQ FSBL模板,点击Finish完成工程的创建,SDK会自动创建一个名为fsbl的工程和fsbl_bsp板级支持包。选中fsbl工程,右键选项选择Create Boot Image,在弹出的选项卡中可以发现SDK已经问我们添加了刚才生成的fsbl和有VIVADO导入SDK中的比特流文件,我们只需要再添加编译好的u-boot即可。

点击右侧的Add可以添加新的文件,Delete可以删除选中的文件,Edit可以编辑文件的类型。我们选择Add添加u-boot.elf文件。

在新的选项卡中填入uboot.elf的路径,这里一定要注意类型为Datafile类型,否则无法正常启动。

点击OK确认后退回到上次层选项卡,选择Create Image选项,在SDK目录下就会生成对应的BOOT.bin文件。

将BOOT.bin拷贝到Zedboard的SD卡,连接串口,开机观察串口提示,发现u-boot已经可以正常启动了,并且此时FPGA也已经按照VIVADO的网表文件初始化完成,但是u-boot提示无法读取内核镜像,我们将在下一步中生成。

(4)内核编译

Xilinx官方提供了linux的源码,供开发者下载和使用,我们打开Xilinx官网链接:https://github.com/Xilinx/linux-xlnx/releases;选择17.4版本下载并解压。Linux解压命令为 :

tar zxvf linux-xlnx-xilinx-v2017.4.tar.gz

解压后进入该目录,这里对关键目录进行说明:

1)include/---- 内核头文件,需要提供给外部模块使用

2) kernel/---- Linux 内核癿核心代码,包扩进程调度子系统,以及进程调度相关的模块。

3)arch/---- 体系结构相关的代码,例如 arm, x86 等等,我们使用的ARM A9处理器就在arch/arm/目录下。

arch/mach包含了具体开发板有关的代码

arch/boot/dts 包含了设备树文件

arch/arm/configs目录下包含了arm架构处理器和开发板的一些内核默认配置文件,Zedboard的默认配置文件也在此目录下。

4)driver目录则存放了可用的驱动程序,你可以将自己的驱动放入此目录,在后面选择编译进内核。

5)scripts目录下包含了设备树编译器dtc和解释内核配置选项相关的文件和目录。

其余目录则不是本文介绍的重点,当开发平台启动BootLoader后,需要读取内核镜像,并依赖设备树文件传入的一些启动参数才能启动。当然还需要文件系统的支持。Linux内核有Imange、zImage和uImage等格式,Image就是正常编译出的linux内核,但是鉴于嵌入式资源有限,我们可以将内核和一段自解压程序进行压缩,这样启动时BootLoader先调用zImage的解压接口进行解压缩,而后在调用内核接口启动内核,相比于Image,zImage启动更慢一些。uImage就是在头部加入了一些u-boot相关代码的压缩Linux内核镜像,便于u-boot启动内核镜像。因此,我们最终要生成的就是uImage内核镜像。

上面说过在arch/arm中存放了我们需要的arm A9处理器的代码和文件,进入arch/arm/configs,搜索zynq相关的配置,发现xilinxz_zynq_defconfig配置文件,这就是Zedboard可用的默认配置文件。和u-boot类似,我们也需要先写入默认配置到.config文件才能编译内核。

使用命令make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- xilixn_zynq_defconfig进行内核配置文件的写入。写入完成后提示written to .config。可用使用make menuconfig配置内核选项:

这里保持默认,无需修改,如果需要将自己的驱动编译进内核,可以在这里选中,但是这样不利于调试驱动。

使用命令make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 编译内核,内核编译需要较长的时间。如果配置过程中需要重新修改或者发生错误,可以使用make distclean命令使内核恢复最初的状态,然后重新编译。

我们使用make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImageLOADADDR=0x00008000重新生成内核,指定内核镜像为uImage,内核入口地址为0x00008000。由于内核已经编译过一次,这次可以很快生成。

生成的内核位于arch/arm/boot目录下。拷贝uImage到Zedboard的SD卡准备启动时使用。

(5)生成设备树文件

设备树是一种设备节点的描述,它告诉内核板卡上有哪些外设以及外设占用的资源,比如寄存器映射空间和中断号等信息。另外设备树还可以修改内核启动参数,如串口选择、波特率设置和根文件系统的选择。

通过VIVADO SDK可以生成设备树描述文件,这样便于我们开发,而不需要完全手动创建。VIVIADO安装时并没有安装设备树生成器,所以需要我们手动安装。我们首先下载xilinx提供的device tree generator,并安装到SDK。访问https://github.com/Xilinx/device-tree-xlnx/releases获取对应版本的设备树生成器。下载并放到VIVADO安装目录下的SDK2017.4dataembeddedswdevicetreebsp目录下,重命名为device-tree-xlnx_v2017_4(我的VIVADO版本为17.4)。打开SDK,在SDK中操作点击菜单: Xilinx Tools -> Repositories,然后在LocalRepositories中添加我们刚才下载的SDK2017.4dataembeddedswdevicetreebspdevice-tree-xlnx_v2017_4路径并点击OK。

添加成功后如上图。点击菜单File -> New -> Board Support Package。弹出选项卡New Board Support Packet Project,选择device_tree,如果你上一步配置不成功,则不会出现device_tree选项,此时需要检查上一个步骤的问题。添加成功后点击Finish选项,VIVADO SDK会自动生成设备树描述文件dts。

稍后,VIVADO弹出BoardSupport Packet Setting选项卡,在bootargs中填入console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8Mearlyprintk rootfstype=ext4 rootwait devtmpfs.mount=0。“bootargs”参数用于指定启动时传递给内核的参数。“console device”参数用于指定所使用的串口输出设备。

在SDK目录下的device_tree目录下可以看到很多dts文件,system-top.dts就是我们需要编译的设备树描述文件。它引用了zynq-7000.dtsi等对于zynq芯片通用的部分文件。将SDK目录下的整个device_tree目录上传到Ubuntu服务器,使用dtc编译器编译。编译命令如下:

./scripts/dtc/dtc-I dts -O dtb -o device.dtb ./device_tree/system-top.dts

6)文件系统

根文件系统使用uramdisk.image.gz根文件系统,ramdisk.image.gz根文件系统其格式与uboot不同,启动时uboot会提示ramdisk格式错误,若要让uboot能够识别ramdisk.image.gz根文件系统,需要利用mkimage给ramdisk.image.gz添加一些头部信息,生成uramdisk.image.gz。可以直接使用网络上的uramdisk.image.gz来作为根文件系统,一般来讲,根文件系统不需要做出修改。

另一种广泛应用的根文件系统是LINARO_FS,Linaro文件系统也可从网络上获取,因为我们的设备树中指定了从uramdisk.image.gz文件系统启动,因此这里不再介绍从Linaro文件系统启动。

现在,我们已经得到BOOT.bin文件,设备树device_tree.dtb文件和根文件系统uramdisk.image.gz根文件系统。将这三个文件放入Zedboard的SD卡,上电启动就可以使用Linux操作系统了。

7)驱动程序和应用程序测试

Linux驱动程序有静态编译进内核和动态模块加载两种,这里选择动态模块加载的方式,便于进行调试。在前面的硬件设计中,我们将AXI-Lite Slave的四个寄存器挂载到基地址为0x43c00000的位置,而Zedboard板卡上的8位LED灯连接到了寄存器0的低8位,因此我们写寄存器0的低八位就能很容易的通过LED的状态来判断写入是否成功。

驱动程序的入口和出口分别是init和exit,需要使用宏进行修饰如下:

// 注册初始化Linux驱动的函数module_init( leds_drv_init);// 注册卸载Linux驱动的函数module_exit( leds_drv_exit);

linux操作系统中无法直接读写物理地址,因此入口函数中,我们需要映射物理地址,使用ioremup函数映射物理地址。注意这里物理地址和硬件设计中保持一致。

leds= ioremap(0x43c00000, sizeof(LEDS_T))

Led灯只是一个简单的字符设备,但是这里我们使用该设备来注册设备驱动。

ret= misc_register(&misc);

杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在 Linux 内核的include/linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主编号10,一起归于misc device,其实misc_register就是用主标号10调用register_chrdev()的。也就是说,misc设备其实也就是特殊的字符设备,可自动生成设备节点。LDD3中led设备也是用misc_register函数注册为杂设备,这说明led设备是作为杂项设备出现在内核中的,在内核中,misc杂项设备驱动接口是对一些字符设备的简单封装,他们共享一个主设备号,有不同的次设备号,共享一个open调用,其他的操作函数在打开后运用linux驱动程序的方法重载进行装载。

驱动代码:

#define DEVICE_NAME "leds" #define LEDS_BASE_ADDR (0x43c00000)typedef struct{ volatile unsigned int ADDR0; volatile unsigned int ADDR1; volatile unsigned int ADDR2; volatile unsigned int ADDR3;}LEDS_ADDR; LEDS_ADDR* leds;  static int leds_drv_open(struct inode *Inode, struct file *File){ leds->ADDR0 = 0xffffffff; leds->ADDR1 = 0xffffffff; leds->ADDR2 = 0xffffffff; leds->ADDR3 = 0xffffffff; return 0;}static ssize_t leds_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ return 0;} static ssize_t leds_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ unsigned int ret = 0; unsigned int tmp_val; u32 pos = *offset; ret = copy_from_user(&tmp_val, buf, count);  leds->ADDR0 = tmp_val; //默认写入寄存器0 return ret;} // 描述与设备文件触发的事件对应的回调函数指针static struct file_operations dev_fops ={  .owner = THIS_MODULE,  .open = leds_drv_open, .read = leds_drv_read,  .write = leds_drv_write,}; // 描述设备文件的信息 static struct miscdevice misc ={  .minor = MISC_DYNAMIC_MINOR,  .name = DEVICE_NAME,  .fops = &dev_fops }; // 初始化Linux驱动static int __init leds_drv_init(void){ int ret;  leds = ioremap(LEDS_BASE_ADDR, sizeof(LEDS_T));  // 建立设备文件 ret = misc_register(&misc); // 输出日志信息 if(ret) { printk("leds_drv_init faiitrt!"); } else { printk("leds_drv_init success!"); }  return ret;}// 卸载Linux驱动static void __exit leds_drv_exit(void){ iounmap(leds);  // 删除设备文件  misc_deregister(&misc);  // 输出日志信息 printk("leds_drv_exit success!");} // 注册初始化Linux驱动的函数module_init( leds_drv_init);// 注册卸载Linux驱动的函数module_exit( leds_drv_exit);MODULE_LICENSE("Dual BSD/GPL");

应用程序调用驱动程序接口,从控制台读取一个数字,写入到寄存器0,寄存器0的低八位就可以在led灯上显示出来。

应用程序源码:

#include #include #include #include #include #include int main(int argc, char** argv){ int fd; fd = open("/dev/leds

linux can总线接收数据串口打包上传_使用Zedboard开发板学习Linux的移植及驱动开发...相关推荐

  1. linux can总线接收数据串口打包上传_「干货」手把手教你用Zedboard学习Linux移植和驱动开发...

    本文是昨天发的文章<龙芯杯CPU设计竞赛与ZYNQ设计流程介绍>接续部分.重点介绍传统方式的Linux移植和Xilinx的Petalinux的快速移植开发两种. 部分硬件设计中需要CPU完 ...

  2. linux can总线接收数据串口打包上传_关于串口,你需要知道这些!!

    嵌入式设备在电路中交换信息的时候必须通过共享一个通用的协议.现在嵌入式系统中已经定义了数百种通信协议来实现数据交换,一般来说可以将其分为两类:并行或串行. 并行传输数据是指同时传输多个数据位,它们通常 ...

  3. linux can总线接收数据串口打包上传_【Linux应用】CAN总线编程

    1 CAN总线简介 CAN是控制器局域网络(Controller Area Network,CAN)的简称,由德国BOSCH公司开发,并最终成为国际标准(ISO 11898-1).CAN总线主要应用于 ...

  4. linux can总线接收数据串口打包上传_USART串口通讯

    在计算机科学里,大部分复杂的问题都可以通过分层来简化.如芯片被分为内核层和片上外设:STM32 标准库则是在寄存器与用户代码之间的软件层.对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理 ...

  5. linux can总线接收数据串口打包上传_SPI、I2C、UART、I2S、GPIO、SDIO、CAN,你能分清楚吗?...

    总线,总线,总要陷进里面.这世界上的信号都一样,但是总线却成千上万,让人头疼. 总的来说,总线有三种:内部总线.系统总线和外部总线.内部总线是微机内部各外围芯片与处理器之间的总线,用于芯片一级的互连: ...

  6. linux(6/17)--文件打包上传和下载

    tar命令 命令功能 用来压缩和解压文件  命令格式 tar[必要参数][选择参数][文件] tar打包工具 -f ##指定生成包的名字,建议 -f单独写成一个参数--delete filename ...

  7. ROS节点获取TIA_go机器人传感器数据,并上传到指定服务器做深度学习处理

    Brief 正如标题所言,该小项目包括三部分,如下 ROS节点获取机器人数据 socket和服务器数据传输,包括了数据上传和获取 服务器深度学习处理数据 1 ROS节点获取机器人数据 1.1 数据结构 ...

  8. linux pscp 上传_详解使用pscp命令Linux文件上传与下载

    (一)上传 2.开始运行cmd进入到dos模式输入以下命令以下是代码片段:pscp D:\java\apache-tomcat-5.5.27\webapps\szfdc.rardev@192.168. ...

  9. Linux 中 3 个文件打包上传和下载相关命令详解

    tar 命令 通过 SSH 访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候tar 命令就是必不可少的一个功能强大的工具.Linux 中最流行的tar是麻雀虽小,五脏俱全,功能强大. 使用t ...

最新文章

  1. 九度OJ 1011:最大连续子序列 (DP)
  2. 《OpenGL ES 3.x游戏开发(下卷)》一1.2 顶点数组对象
  3. tcp协议接收方对out of order的分段是如何处理的?_TCP协议与流通信
  4. golang商城_Golang——简单是终极的成熟
  5. HTTP返回代码代表的含义(403,404,500,502,504)
  6. MATLAB plot 画多条线段的问题
  7. VBA中让程序休眠 SLeep的方法
  8. 公众号H5跳转小程序
  9. OAuth2通过token访问资源服务器
  10. 台式计算机大全,电脑品牌大全..3MT产品库
  11. 定时任务管理系统 gocron
  12. 如何将div拼接成html代码,给div拼接html 拼接字符串
  13. springboot前后端分离 前端请求图片问题
  14. Google Earth Engine(GEE)——ASTER全球水体数据库(ASTWBD)第1版
  15. “很抱歉,出现错误,word不能启动”超简单解决方法
  16. ArcGIS教程:“格网和经纬网向导”快速浏览
  17. Stellantis US EDI 214测试流程
  18. 如何更好处理公共关系和个人关系?
  19. 游戏设计与开发_Unity游戏开发——设计模式概述
  20. 工具 | 百度网盘限速解决方案

热门文章

  1. 大型企业网络安全解决方案
  2. code128条码生成与显示
  3. 单文件php目录列表源码,分享个漂亮的php目录列表源码。(很多人问我的)
  4. 地震桌面演练计算机模拟,地震应急处置演练平台环境编辑器及导演组客户端的设计与实现...
  5. 学会思考--菜鸟程序员晋升大神之路
  6. MPU6050-DMP读不出数据
  7. 周杰伦的2000w个故事
  8. 高品质免费字体集锦:25款英文艺术字体下载
  9. Windows 11配置WSL及Linux子系统安装
  10. ERROR:Xst:899--FPGA ERROR