「正点原子FPGA连载」第十四章U-Boot移植
csdh11 2024-11-30 14:16 4 浏览
1)摘自【正点原子】领航者 ZYNQ 之linux驱动开发指南
2)实验平台:正点原子领航者ZYNQ开发板
3)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
4)全套实验源码+手册+视频下载:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
5)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
6)关注正点原子公众号,获取最新资料
第十四章U-Boot移植
上一章节我们详细的分析了uboot的启动流程,对uboot有了一个初步的了解。前两章我们都是使用的正点原子提供的uboot,本章我们就来学习如何将Xilinx官方的uboot移植到正点原子的ZYNQ开发板上,学习如何在uboot中添加我们自己的板子。
1.1Xilinx官方开发板uboot编译测试
1.1.1查找Xilinx官方的开发板默认配置文件
uboot的移植并不是说我们完完全全的从零开始将uboot移植到我们现在所使用的开发板或者开发平台上。这个对于我们来说基本是不可能的,这个工作一般是半导体厂商做的,半导体厂商负责将uboot移植到他们的芯片上,因此半导体厂商都会自己做一个开发板,这个开发板就叫做原厂开发板,比如大家学习STM32的时候听说过的discover开发板就是ST自己做的。半导体厂商会将uboot移植到他们自己的原厂开发板上,测试好以后就会将这个uboot发布出去,这就是大家常说的原厂BSP包。我们一般做产品的时候就会参考原厂的开发板做硬件,然后在原厂提供的BSP包上做修改,将uboot或者linux kernel移植到我们的硬件上。这个就是uboot移植的一般流程:
① 在uboot中找到参考的开发平台,一般是原厂的开发板。
② 参考原厂开发板移植uboot到我们所使用的开发板上。
正点原子的ZYNQ开发板参考的是Xilinx官方的ZYNQ ZC702开发板做的硬件,因此我们在移植uboot的时候就可以以Xilinx官方的ZYNQ ZC702开发板为蓝本。
本章我们是将Xilinx官方的uboot移植到正点原子的ZYNQ开发板上,Xilinx官方的uboot放到了开发板光盘中,路径为:领航者ZYNQ开发板资料盘(A盘)\4_SourceCode\ZYNQ_7020\3_Embedded_Linux\资源文件\uboot\u-boot-xlnx-xilinx-v2018.3.tar.gz。将u-boot-xlnx-xilinx-v2018.3.tar.gz发送到Ubuntu中并解压。
在移植之前,我们先编译一下Xilinx官方ZYNQ ZC702开发板对应的uboot。首先是配置uboot,解压Xilinx官方的uboot后得到的configs目录下有很多跟zynq有关的配置,如下图所示,
图 25.1.1 Xilinx官方zynq默认配置文件
从上图可以看出有很多的默认配置文件,其中以zynq开头的是ZYNQ相关开发板的配置文件。zynq_zc702_defconfig是ZYNQ ZC702开发板的配置文件。
1.1.2编译Xilinx官方开发板对应的uboot
找到Xilinx官方ZYNQ ZC702开发板对应的默认配置文件以后可以编译一下。编译uboot命令如下:
- make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zynq_zc702_defconfig
- make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
复制代码
编译完成以后结果如下图所示:
图 25.1.2编译结果
从上图可以看出,编译成功。我们在编译的时候需要输入ARCH和CORSS_COMPILE这两个变量的值,有点麻烦,如果使用Petalinux就没有这个问题了,现在我们没有使用Petalinux,不过也可以通过以下两种方式解决:
方式一:(不推荐)可以直接在顶层Makefile中直接给ARCH和CORSS_COMPILE赋值,如下图所示的那样:
图 25.1.3添加ARCH和CROSS_COMPILE值
上图中的250、251行就是直接给ARCH和CROSS_COMPILE赋值,这样我们就可以使用如下简短的命令来编译uboot了:
- make zynq_zc702_defconfig
- make V=1 -j8
复制代码
方式二:(推荐)创建shell脚本。在uboot根目录(对应笔者的就是alientek_uboot目录)下创建一个名为zynq_zc702.sh的shell脚本,然后在shell脚本里面输入如下内容:
示例代码 zynq_zc702.sh文件
- 1 #!/bin/bash
- 2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
- 3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zynq_zc702_defconfig
- 4 make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
复制代码
记得给zynq_zc702.sh这个文件可执行权限,使用zynq_zc702.sh脚本编译uboot的时候每次都会清理一下工程,然后全部重新编译,编译的时候直接执行这个脚本就行了,命令如下:
- ./zynq_zc702.sh
复制代码
编译完成以后会生成u-boot.bin、u-boot等文件,但是这些文件是Xilinx官方ZYNQ ZC702开发板的。在第十七章Petalinux设计流程实战章节,我们没有单独配置uboot,使用的是Petalinux工具默认的ZC702的配置,使用上是没有问题的。不过那时我们使用了Petalinux工具,Petalinux会根据硬件描述文件自动配置uboot,现在我们没有使用Petalinux工具,能不能用到正点原子的ZYNQ开发板上呢?下面我们拭目以待。
1.1.3验证与驱动测试
如果使用Petalinux工具,测试还是比较方便的,可以直接生成BOOT.bin文件,现在我们没有使用Petalinux工具,也不在Petalinux工程目录下编译,所以测试起来还是比较麻烦的。当然了,也可以借助XSDK软件,这种方法是可行的,不过还是较麻烦,有没有一种更简便的方法呢?
答案是有的,不过首先说明一点,这种方法笔者测试是可行的,各人可能因为软硬件的问题不一定能够顺利运行,这时可以使用XSDK软件来下载。下面我们介绍下这种方法。
这种方法是使用JTAG下载,跟使用XSDK软件一样的,不过这种方式更便捷,因为是脚本化。需要提醒的是Linux需要手动安装JTAG驱动,安装方式见16.6小节Linux系统安装JTAG cable驱动。
首先我们进入第十七章Petalinux设计流程实战章节建立的Petalinux工程目录中,在工程的project-spec目录下有一个hw-description文件夹,如下图所示:
图 25.1.4 hw-description文件夹
该文件夹的内容和我们从Vivado软件打开SDK时创建的system_wrapper_hw_platform_0文件夹的内容基本相同,我们需要的是里面的PS初始化文件。我们将该文件夹复制到uboot根目录下,如下图所示:
图 25.1.5 uboot根目录下的hw-description文件夹
现在我们在uboot根目录下新建一个名为linux.tcl的文件,用来需要下载fgpa的bitstream文件和uboot的elf文件以启动linux内核,文件内容如下:
- 1 connect
- 2 source hw-description/ps7_init.tcl
- 3 targets -set -filter {name =~"APU*" && jtag_cable_name =~ "Digilent*"} -index 0
- 4 rst -system
- 5 after 3000
- 6 targets -set -filter {jtag_cable_name =~ "Digilent*" && level==0} -index 1
- 7 fpga -file hw-description/system_wrapper.bit
- 8 targets -set -filter {name =~"APU*" && jtag_cable_name =~ "Digilent*"} -index 0
- 9 loadhw -hw hw-description/system.hdf -mem-ranges [list {0x40000000 0xbfffffff}]
- 10 configparams force-mem-access 1
- 11 targets -set -filter {name =~"APU*" && jtag_cable_name =~ "Digilent*"} -index 0
- 12 ps7_init
- 13 ps7_post_config
- 14 targets -set -nocase -filter {name =~ "ARM*#0"}
- 15 dow u-boot
- 16 configparams force-mem-access 0
- 17 targets -set -nocase -filter {name =~ "ARM*#0"}
- 18 con
复制代码
然后新建一个名为uboot.tcl的文件。当我们只是调试uboot,不启动内核的时候或没有使用fpga部分时启动内核的时候,可使用该文件,文件内容如下(该文件剩去了下载bitstream文件的步骤,更节省时间):
- 1 connect
- 2 source hw-description/ps7_init.tcl
- 3 targets -set -filter {name =~"APU"}
- 4 loadhw hw-description/system.hdf
- 5 stop
- 6 ps7_init
- 7 targets -set -nocase -filter {name =~ "ARM*#0"}
- 8 rst -processor
- 9 dow u-boot
- 10 con
复制代码
至此,我们可以开始下载uboot。
将领航者开发板的启动模式设置为“JTAG”启动,连接JTAG、串口和电源,然后开发板上电。打开串口软件如SecureCRT或Putty,设置好领航者开发板所使用的串口并打开。
在Vmware软件的菜单栏点击“虚拟机(M)”菜单,在弹出的子菜单中移动到“可移动设备(D)”:会弹出相应的移动设备,里面带有“Digilent USB”的是JTAG的USB接口,连接该USB接口,如下图所示:
图 25.1.6 在Vmware中连接JTAG的USB接口到虚拟机内
现在我们在终端中输入如下命令配置Petalinux的环境变量:
- source /opt/pkg/petalinux/2018.3/settings.sh //或使用spl别名
复制代码
因为这种方法使用的工具是Petalinux自带的,XSDK软件也有,这里我们使用Petalinux中的,所以需要配置Petalinux的环境变量。
在uboot根目录下,输入如下命令下载u-boot文件,也就是u-boot.elf文件:
- xsct linux.tcl
复制代码
串口软件接收到的情况如下图所示:
图 25.1.8 串口软件接收情况
从上图可以看到,串口软件没有接收到任何启动信息,有两种可能,一是下载有问题,二是这种命令行编译确实不适用于我们自己的开发板。笔者经过多次测试之后排除了第一种可能,不信可以看后面我们下载移植好的,就能正常打印启动信息。那么就是第二种可能了。
虽然我们可以直接在官方的开发板上配置文件中直接修改,使uboot可以完整的运行在我们的板子上。但是从学习的角度来讲,这样我们就不能了解到uboot是如何添加新平台的。接下来我们就参考Xilinx官方的ZYNQ ZC702开发板,学习如何在uboot中添加我们自己的开发板或开发平台。
1.2在U-Boot中添加自己的开发板
1.2.1添加开发板默认配置文件
先在configs目录下创建领航者开发板的默认配置文件。复制zynq_zc702_defconfig,然后重命名为zynq_altk_defconfig,命令如下:
- cd configs
- cp zynq_zc702_defconfig zynq_altk_defconfig
复制代码
然后修改zynq_altk_defconfig文件,修改后的文件内容如下(因文件较长,故部分未改动内容以“……”代替):
示例代码 zynq_altk_defconfig文件
- 1 CONFIG_ARM=y
- 2 CONFIG_SYS_CONFIG_NAME="zynq_altk"
- 3 CONFIG_ARCH_ZYNQ=y
- 4 CONFIG_SYS_TEXT_BASE=0x400000
- 5 CONFIG_SYS_MALLOC_F_LEN=0x800
- 6 CONFIG_IDENT_STRING=" Xilinx Zynq ALTK"
- 7 CONFIG_SPL_STACK_R_ADDR=0x200000
- 8 CONFIG_DEFAULT_DEVICE_TREE="zynq-altk"
- 9 CONFIG_DEBUG_UART=y
- 10 CONFIG_DISTRO_DEFAULTS=y
- 11 CONFIG_FIT=y
- 12 CONFIG_FIT_SIGNATURE=y
- 13 CONFIG_FIT_VERBOSE=y
- 14 CONFIG_BOOTCOMMAND="run default_bootcmd"
- 15 # CONFIG_DISPLAY_CPUINFO is not set
- 16 CONFIG_SYS_L2CACHE_OFF=y
- 17 CONFIG_SPL_DM_SERIAL=y
- 18 CONFIG_SYS_PROMPT="Zynq> "
- ……
- 54 #CONFIG_PHY_XILINX=y
- 55 CONFIG_ZYNQ_GEM=y
- 56 CONFIG_BOOTDELAY=3
- 57 CONFIG_PHY_GIGE=y
- 58 CONFIG_DEBUG_UART_ZYNQ=y
- 59 CONFIG_DEBUG_UART_BASE=0xE0000000
- 60 CONFIG_DEBUG_UART_CLOCK=100000000
- 61 CONFIG_DEBUG_UART_ANNOUNCE=y
- 62 CONFIG_ZYNQ_SERIAL=y
- 63 CONFIG_ZYNQ_QSPI=y
- 64 CONFIG_USB=y
- 65 CONFIG_USB_EHCI_HCD=y
- 66 CONFIG_USB_ULPI_VIEWPORT=y
- 67 CONFIG_USB_ULPI=y
- 68 CONFIG_USB_STORAGE=y
- 69 CONFIG_USB_GADGET=y
- 70 CONFIG_USB_GADGET_MANUFACTURER="Xilinx"
- 71 CONFIG_USB_GADGET_VENDOR_NUM=0x03fd
- 72 CONFIG_USB_GADGET_PRODUCT_NUM=0x0300
- 73 CONFIG_CI_UDC=y
- 74 CONFIG_USB_GADGET_DOWNLOAD=y
复制代码
可以看出,zynq_altk_defconfig文件基本和zynq_zc702_defconfig文件中的内容一样。主要修改了第8行,指定领航者开发板的设备树文件,对应的是我们在25.2.4节创建的领航者开发板的设备树文件名,第14行的CONFIG_BOOTCOMMAND,第56行的CONFIG_BOOTDELAY改为3。第59行指定串口地址,宏CONFIG_DEBUG_UART_BASE表示调试串口寄存器基地址,领航者开发板使用的是PS的串口0作为PS的调试串口。查阅ug585《Zynq-7000 SoC Technical Reference Manual》参考手册,UART0的寄存器基地址是0xe0000000,如下图所示:
图 25.2.1 UART0寄存器地址
因而我们需要将宏CONFIG_DEBUG_UART_BASE的值设为0xe0000000。
1.2.2添加开发板对应的头文件
在目录include/configs下添加领航者开发板对应的头文件。使用zc702开发板的头文件zynq_zc70x.h进行修改是可以的。不过此处我们就不复制zc702开发板的头文件了,而是使用Petalinux创建的头文件,该头文件是我们在22.2节使用Petalinux编译uboot时Petalinux工具自动生成的。这样做的目的是对使用Petalinux工具搭建的工程有更好的了解。需要说明的是配置Petalinux工程时选择不同的启动方式产生的头文件不一样,此处我们使用的是从SD卡启动的头文件。在终端输入如下命令:
- cd ../include/configs/
- ll platform*
复制代码
执行结果如下图所示:
可以看到有两个以“platform”开头的文件。我们将platform-top.h重命名为zynq_altk.h,platform-auto.h重命名为platform-altk.h,命令如下:
- mv platform-top.h zynq_altk.h
- mv platform-auto.h platform-altk.h
复制代码
重命名完成以后将zynq_altk.h文件的内容修改成如下所示:
示例代码 zynq_altk.h文件
- 1 #ifndef __CONFIG_ZYNQ_ALTK_H
- 2 #define __CONFIG_ZYNQ_ALTK_H
- 3
- 4 #include <configs/platform-altk.h>
- 5 #define CONFIG_SYS_BOOTM_LEN 0xF000000
- 6 #define DFU_ALT_INFO_RAM \
- 7 "dfu_ram_info=" \
- 8 "setenv dfu_alt_info " \
- 9 "image.ub ram $netstart 0x1e00000\0" \
- 10 "dfu_ram=run dfu_ram_info && dfu 0 ram 0\0" \
- 11 "thor_ram=run dfu_ram_info && thordown 0 ram 0\0"
- 12
- 13 #define DFU_ALT_INFO_MMC \
- 14 "dfu_mmc_info=" \
- 15 "set dfu_alt_info " \
- 16 "${kernel_image} fat 0 1\\\\;" \
- 17 "dfu_mmc=run dfu_mmc_info && dfu 0 mmc 0\0" \
- 18 "thor_mmc=run dfu_mmc_info && thordown 0 mmc 0\0"
- 19
- 20 #define CONFIG_ZYNQ_I2C0
- 21 #define CONFIG_ZYNQ_EEPROM
- 22
- 23 /* EEPROM */
- 24 #ifdef CONFIG_ZYNQ_EEPROM
- 25 #define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1
- 26 #define CONFIG_SYS_I2C_EEPROM_ADDR 0x54
- 27 #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 4
- 28 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
- 29 #define CONFIG_SYS_EEPROM_SIZE 1024 /* Bytes */
- 30 #define CONFIG_SYS_I2C_MUX_ADDR 0x74
- 31 #define CONFIG_SYS_I2C_MUX_EEPROM_SEL 0x4
- 32 #endif
- 33
- 34 #endif /* __CONFIG_ZYNQ_ALTK_H */
复制代码
可以看到zynq_altk.h里面有一些以“CONFIG_”开头的宏定义,这些宏定义可以用于配置或者裁剪uboot。如果我们要想使能或者禁止uboot的某些功能,可以在该文件中添加这个功能对应的CONFIG_XXX宏即可,如果不需要某个功能的话就删除掉对应的宏。zynq_altk.h文件内容不是很多,主要是第4行的include语句,将configs/platform-altk.h文件包含进来了,该文件里面是领航者开发板平台的配置,具体的我们就不看了,毕竟是由Petalinux自动生成的。
1.2.3添加开发板对应的板级文件夹
uboot中每个板子都有一个对应的文件夹来存放板级文件,比如开发板上外设驱动文件等。Xilinx的ZYNQ系列芯片的所有板级文件夹都存放在board/xilinx/zynq目录下,在这个目录下有个名为zynq-zc702的文件夹,这个文件夹就是Xilinx官方ZC702开发板的板级文件夹。复制zynq-zc702,将其重命名为zynq-altk,命令如下:
- cd board/xilinx/zynq/
- cp -r zynq-zc702 zynq-altk
复制代码
进入zynq-altk目录中,可以看到只有一个名为“ps7_init_gpl.c”的文件,该文件是PS的初始化文件。可以用于我们的领航者开发板。
1.2.4添加开发板对应的设备树
uboot支持设备树,每个开发板都有一个对应的设备树文件。Xilinx的ZYNQ系列芯片的所有设备树文件夹都存放在arch/arm/dts目录下,在这个目录下有个名为zynq-zc702.dts的文件,该文件是ZC702开发板的设备树文件。这里我们就不参照zynq-zc702.dts文件,而是参照zynq-zed.dts文件,这是因为zynq-zed.dts是在zynq-zc702.dts文件基础上修改而来,能极大的方便我们的移植。我们将zynq-zed.dts重命名为zynq-altk.dts,命令如下:
- cd arch/arm/dts
- cp zynq-zed.dts zynq-altk.dts
复制代码
拷贝完成以后将zynq_altk.dts文件的内容修改成如下所示:
示例代码 zynq-altk.dts文件
- 6 /dts-v1/;
- 7 #include "zynq-7000.dtsi"
- 8
- 9 / {
- 10 model = "Zynq Alientek Development Board";
- 11 compatible = "xlnx,zynq-altk", "xlnx,zynq-7000";
- 12
- 13 aliases {
- 14 ethernet0 = &gem0;
- 15 serial0 = &uart0;
- 16 spi0 = &qspi;
- 17 mmc0 = &sdhci0;
- 18 usb0 = &usb0;
- 19 };
- 20
- 21 memory@0 {
- 22 device_type = "memory";
- 23 reg = <0x0 0x40000000>;
- 24 };
- 25
- 26 chosen {
- 27 bootargs = "";
- 28 stdout-path = "serial0:115200n8";
- 29 };
- 30
- 31 usb_phy0: phy0@e0002000 {
- 32 compatible = "ulpi-phy";
- 33 #phy-cells = <0>;
- 34 reg = <0xe0002000 0x1000>;
- 35 view-port = <0x0170>;
- 36 drv-vbus;
- 37 };
- 38 };
- 39
- 40 &clkc {
- 41 ps-clk-frequency = <33333333>;
- 42 };
- 43
- 44 &gem0 {
- 45 status = "okay";
- 46 phy-mode = "rgmii-id";
- 47 phy-handle = <eernet_phy>;
- 48
- 49 ethernet_phy: ethernet-phy@0 {
- 50 reg = <0>;
- 51 device_type = "ethernet-phy";
- 52 };
- 53 };
- 54
- 55 &qspi {
- 56 u-boot,dm-pre-reloc;
- 57 status = "okay";
- 58 is-dual = <0>;
- 59 num-cs = <1>;
- 60 flash@0 {
- 61 compatible = "n25q128a11";
- 62 reg = <0x0>;
- 63 spi-tx-bus-width = <1>;
- 64 spi-rx-bus-width = <4>;
- 65 spi-max-frequency = <50000000>;
- 66 #address-cells = <1>;
- 67 #size-cells = <1>;
- 68 partition@qspi-fsbl-uboot {
- 69 label = "qspi-fsbl-uboot";
- 70 reg = <0x0 0x100000>;
- 71 };
- 72 partition@qspi-linux {
- 73 label = "qspi-linux";
- 74 reg = <0x100000 0x500000>;
- 75 };
- 76 partition@qspi-device-tree {
- 77 label = "qspi-device-tree";
- 78 reg = <0x600000 0x20000>;
- 79 };
- 80 partition@qspi-rootfs {
- 81 label = "qspi-rootfs";
- 82 reg = <0x620000 0x5E0000>;
- 83 };
- 84 partition@qspi-bitstream {
- 85 label = "qspi-bitstream";
- 86 reg = <0xC00000 0x400000>;
- 87 };
- 88 };
- 89 };
- 90
- 91 &sdhci0 {
- 92 u-boot,dm-pre-reloc;
- 93 status = "okay";
- 94 };
- 95
- 96 &uart0 {
- 97 u-boot,dm-pre-reloc;
- 98 status = "okay";
- 99 };
- 100
- 101 &usb0 {
- 102 status = "okay";
- 103 dr_mode = "host";
- 104 usb-phy = <&usb_phy0>;
- 105 };
复制代码
关于设备树的内容后面在讲解linux驱动的时候会详细讲解,这里需要提醒的是我们使用的是PS的UART0作为通信接口,而不是UART1,另外memory的大小这里设置为7020的1GB,也就是“reg = <0x0 0x40000000>”,如果是7010的核心板,就是“reg = <0x0 0x20000000>”,也就是512MB。在qspi节点可以看到有5个分区,分别是fsbl-uboot分区、linux分区、设备树分区、根文件系统分区和bitstream分区,不过在实际使用的时候并不是如此分布,特别是在使用Petalinux工具的时候,一般都是使能“Advanced Flash Auto Configuration”功能,自动对qspi进行分区。
1.2.5修改U-Boot图形界面配置文件
uboot支持图形界面配置,关于uboot的图形界面配置下一章会详细的讲解。修改文件arch/arm/mach-zynq/Kconfig,将第48行的SYS_CONFIG_NAME配置项的内容修改如下:
示例代码 Kconfig文件
- 48 config SYS_CONFIG_NAME
- 49 string "Board configuration name"
- 50 default "zynq_altk"
- 51 help
- 52 This option contains information about board configuration name.
- 53 Based on this option include/configs/<CONFIG_SYS_CONFIG_NAME>.h header
- 54 will be used for board configuration.
复制代码
修改完成以后的Kconfig文件如下图所示:
图 25.2.2修改后的Kconfig文件
到此为止,领航者开发板就已经添加到uboot中了,接下来就是编译这个新添加的开发板。
1.2.6使用新添加的板子配置编译uboot
在uboot源码根目录下新建一个名为zynq.sh的shell脚本,在这个shell脚本里面输入如下内容:
示例代码 zynq.sh脚本文件
- 1 #!/bin/bash
- 2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
- 3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zynq_altk_defconfig
- 4 make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
复制代码
第3行我们使用的默认配置文件就是25.2.1节中新建的zynq_altk_defconfig配置文件。给予zynq.sh可执行权限,然后运行脚本来完成编译,命令如下:
- chmod 777 zynq.sh //给予可执行权限
- ./zynq.sh //运行脚本编译uboot
复制代码
等待编译完成,编译完成以后输入如下命令,查看一下33.2.2小节中添加的zynq_altk.h这个头文件有没有被引用。
grep -nR "zynq_altk.h"
如果有很多文件都引用了zynq_altk.h这个头文件,那就说明新板子添加成功,如下图所示:
图 25.2.3查找结果
编译完成以后就可以使用“xsct jtag.tcl”进行下载测试。串口软件输出结果如下图所示:
图 25.2.4 uboot启动过程
从上图可以看到,我们基本移植成功了,也验证了这种下载方式是没有问题的。uboot的最终目的就是启动Linux内核,所以还是需要通过启动Linux内核来判断uboot移植是否真的成功。在启动Linux内核之前我们先来学习两个重要的环境变量bootcmd和bootargs。
1.3bootcmd和bootargs环境变量
uboot中有两个非常重要的环境变量bootcmd和bootargs,接下来看一下这两个环境变量。bootcmd和bootagrs是采用类似shell脚本语言编写的,里面有很多的变量引用,这些变量其实都是环境变量,有很多是Xilinx自己定义的。文件include/configs/platform-altk.h中的宏CONFIG_EXTRA_ENV_SETTINGS保存着这些环境变量的默认值,内容如下:
示例代码 宏CONFIG_EXTRA_ENV_SETTINGS默认值
- 155 /* Extra U-Boot Env settings */
- 156 #define CONFIG_EXTRA_ENV_SETTINGS \
- 157 SERIAL_MULTI \
- 158 CONSOLE_ARG \
- 159 DFU_ALT_INFO_RAM \
- 160 DFU_ALT_INFO_MMC \
- 161 PSSERIAL0 \
- 162 "nc=setenv stdout nc;setenv stdin nc;\0" \
- 163 "ethaddr=00:0a:35:00:1e:53\0" \
- 164 "bootenv=uEnv.txt\0" \
- 165 "importbootenv=echo "Importing environment from SD ..."; " \
- 166 "env import -t ${loadbootenv_addr} $filesize\0" \
- 167 "loadbootenv=load mmc $sdbootdev:$partid ${loadbootenv_addr} ${bootenv}\0" \
- 168 "sd_uEnvtxt_existence_test=test -e mmc $sdbootdev:$partid /uEnv.txt\0" \
- 169 "uenvboot=" \
- 170 "if run sd_uEnvtxt_existence_test; then " \
- 171 "run loadbootenv; " \
- 172 "echo Loaded environment from ${bootenv}; " \
- 173 "run importbootenv; " \
- 174 "fi; " \
- 175 "if test -n $uenvcmd; then " \
- 176 "echo Running uenvcmd ...; " \
- 177 "run uenvcmd; " \
- 178 "fi\0" \
- 179 "autoload=no\0" \
- 180 "sdbootdev=0\0" \
- 181 "clobstart=0x10000000\0" \
- 182 "netstart=0x10000000\0" \
- 183 "dtbnetstart=0x23fff000\0" \
- 184 "loadaddr=0x10000000\0" \
- 185 "bootsize=0x500000\0" \
- 186 "bootstart=0x0\0" \
- 187 "boot_img=BOOT.BIN\0" \
- 188 "load_boot=tftpboot ${clobstart} ${boot_img}\0" \
- 189 "update_boot=setenv img boot; setenv psize ${bootsize}; setenv installcmd "install_boot"; run load_boot test_img; setenv img; setenv psize; setenv installcmd\0" \
- 190 "sd_update_boot=echo Updating boot from SD; mmcinfo && fatload mmc ${sdbootdev}:1 ${clobstart} ${boot_img} && run install_boot\0" \
- 191 "install_boot=sf probe 0 && sf erase ${bootstart} ${bootsize} && " \
- 192 "sf write ${clobstart} ${bootstart} ${filesize}\0" \
- 193 "bootenvsize=0x20000\0" \
- 194 "bootenvstart=0x500000\0" \
- 195 "eraseenv=sf probe 0 && sf erase ${bootenvstart} ${bootenvsize}\0" \
- 196 "jffs2_img=rootfs.jffs2\0" \
- 197 "load_jffs2=tftpboot ${clobstart} ${jffs2_img}\0" \
- 198 "update_jffs2=setenv img jffs2; setenv psize ${jffs2size}; setenv installcmd "install_jffs2"; run load_jffs2 test_img; setenv img; setenv psize; setenv installcmd\0" \
- 199 "sd_update_jffs2=echo Updating jffs2 from SD; mmcinfo && fatload mmc ${sdbootdev}:1 ${clobstart} ${jffs2_img} && run install_jffs2\0" \
- 200 "install_jffs2=sf probe 0 && sf erase ${jffs2start} ${jffs2size} && " \
- 201 "sf write ${clobstart} ${jffs2start} ${filesize}\0" \
- 202 "kernelsize=0xa80000\0" \
- 203 "kernelstart=0x520000\0" \
- 204 "kernel_img=image.ub\0" \
- 205 "load_kernel=tftpboot ${clobstart} ${kernel_img}\0" \
- 206 "update_kernel=setenv img kernel; setenv psize ${kernelsize}; setenv installcmd "install_kernel"; run load_kernel test_crc; setenv img; setenv psize; setenv installcmd\0" \
- 207 "sd_update_kernel=echo Updating kernel from SD; mmcinfo && fatload mmc ${sdbootdev}:1 ${clobstart} ${kernel_img} && run install_kernel\0" \
- 208 "install_kernel=sf probe 0 && sf erase ${kernelstart} ${kernelsize} && " \
- 209 "sf write ${clobstart} ${kernelstart} ${filesize}\0" \
- 210 "cp_kernel2ram=sf probe 0 && sf read ${netstart} ${kernelstart} ${kernelsize}\0" \
- 211 "dtb_img=system.dtb\0" \
- 212 "load_dtb=tftpboot ${clobstart} ${dtb_img}\0" \
- 213 "update_dtb=setenv img dtb; setenv psize ${dtbsize}; setenv installcmd "install_dtb"; run load_dtb test_img; setenv img; setenv psize; setenv installcmd\0" \
- 214 "sd_update_dtb=echo Updating dtb from SD; mmcinfo && fatload mmc ${sdbootdev}:1 ${clobstart} ${dtb_img} && run install_dtb\0" \
- 215 "loadbootenv_addr=0x00100000\0" \
- 216 "fault=echo ${img} image size is greater than allocated place - partition ${img} is NOT UPDATED\0" \
- 217 "test_crc=if imi ${clobstart}; then run test_img; else echo ${img} Bad CRC - ${img} is NOT UPDATED; fi\0" \
- 218 "test_img=setenv var "if test ${filesize} -gt ${psize}\\; then run fault\\; else run ${installcmd}\\; fi"; run var; setenv var\0" \
- 219 "netboot=tftpboot ${netstart} ${kernel_img} && bootm\0" \
- 220 "default_bootcmd=run cp_kernel2ram && bootm ${netstart}\0" \
- 221 ""
复制代码
宏CONFIG_EXTRA_ENV_SETTINGS中有很多有价值的信息,比如第219行的netboot变量,就可以让我们从网络启动linux。
1.3.1环境变量bootcmd
bootcmd保存着uboot默认命令,uboot倒计时结束以后就会执行bootcmd中的命令。这些命令一般都是用来启动Linux内核的,比如读取SD或者EMMC中的Linux内核镜像文件和设备树文件到DRAM中,然后启动Linux内核。可以在uboot启动以后进入命令行设置bootcmd环境变量的值。如果QSPI中没有保存bootcmd的值,那么uboot就会使用默认的值,开发板第一次运行uboot的时候都会使用默认值来设置bootcmd环境变量,默认环境变量在文件include/env_default.h中定义。打开文件include/env_default.h,在此文件中有如下所示内容:
示例代码 默认环境变量
- 14 env_t environment __UBOOT_ENV_SECTION__ = {
- 15 ENV_CRC, /* CRC Sum */
- 16 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
- 17 1, /* Flags: valid */
- 18 #endif
- 19 {
- 20 #elif defined(DEFAULT_ENV_INSTANCE_STATIC)
- 21 static char default_environment[] = {
- 22 #else
- 23 const uchar default_environment[] = {
- 24 #endif
- ……
- 31 #ifdef CONFIG_USE_BOOTARGS
- 32 "bootargs=" CONFIG_BOOTARGS "\0"
- 33 #endif
- 34 #ifdef CONFIG_BOOTCOMMAND
- 35 "bootcmd=" CONFIG_BOOTCOMMAND "\0"
- 36 #endif
- 37 #ifdef CONFIG_RAMBOOTCOMMAND
- 38 "ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
- 39 #endif
- 40 #ifdef CONFIG_NFSBOOTCOMMAND
- 41 "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
- 42 #endif
- 43 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
- 44 "bootdelay=" __stringify(CONFIG_BOOTDELAY) "\0"
- 45 #endif
- ……
- 91 #ifdef CONFIG_ENV_VARS_UBOOT_CONFIG
- 92 "arch=" CONFIG_SYS_ARCH "\0"
- 93 #ifdef CONFIG_SYS_CPU
- 94 "cpu=" CONFIG_SYS_CPU "\0"
- 95 #endif
- 96 #ifdef CONFIG_SYS_BOARD
- 97 "board=" CONFIG_SYS_BOARD "\0"
- 98 "board_name=" CONFIG_SYS_BOARD "\0"
- 99 #endif
- 100 #ifdef CONFIG_SYS_VENDOR
- 101 "vendor=" CONFIG_SYS_VENDOR "\0"
- 102 #endif
- 103 #ifdef CONFIG_SYS_SOC
- 104 "soc=" CONFIG_SYS_SOC "\0"
- 105 #endif
- 106 #endif
- 107 #ifdef CONFIG_EXTRA_ENV_SETTINGS
- 108 CONFIG_EXTRA_ENV_SETTINGS
- 109 #endif
- 110 "\0"
- 111 #ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
- 112 }
- 113 #endif
- 114 };
复制代码
从上述代码中的第14行可以看出environment是个env_t类型的变量,env_t类型如下:
示例代码 env_t结构体
- 148 typedef struct environment_s {
- 149 uint32_t crc; /* CRC32 over data bytes */
- 150 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
- 151 unsigned char flags; /* active/obsolete flags */
- 152 #endif
- 153 unsigned char data[ENV_SIZE]; /* Environment data */
- 154 } env_t;
复制代码
env_t结构体中的crc为CRC值,flags是标志位,data数组就是环境变量值。因此,environment就是用来保存默认环境变量的,在示例代码默认环境变量中指定了很多环境变量的默认值,比如bootcmd的默认值就是CONFIG_BOOTCOMMAND,bootargs的默认值就是CONFIG_BOOTARGS。我们可以在platform-altk.h文件中通过设置宏CONFIG_BOOTCOMMAND来设置bootcmd的默认值,Petalinux工具设置的CONFIG_BOOTCOMMAND值如下:
示例代码 CONFIG_BOOTCOMMAND默认值
- 215 /* BOOTCOMMAND */
- 216 #define CONFIG_BOOTCOMMAND "run default_bootcmd"
复制代码
看起来很简单,我们来分析下。以下分析的内容都来自include/configs/platform-altk.h中的宏CONFIG_EXTRA_ENV_SETTINGS定义处。
第216行,“run default_bootcmd”使用的是uboot的run命令来运行default_bootcmd,default_bootcmd是Petalinux工具生成的。default_bootcmd内容如下:
- 212 "default_bootcmd=run uenvboot; run cp_kernel2ram && bootm ${netstart}\0" \
- default_bootcmd里面用到的变量有uenvboot、cp_kernel2ram和netstart。uenvboot变量的内容如下:
- 164 "bootenv=uEnv.txt\0" \
- 165 "importbootenv=echo "Importing environment from SD ..."; " \
- 166 "env import -t ${loadbootenv_addr} $filesize\0" \
- 167 "loadbootenv=load mmc $sdbootdev:$partid ${loadbootenv_addr} ${bootenv}\0" \
- 168 "sd_uEnvtxt_existence_test=test -e mmc $sdbootdev:$partid /uEnv.txt\0" \
- 169 "uenvboot=" \
- 170 "if run sd_uEnvtxt_existence_test; then " \
- 171 "run loadbootenv; " \
- 172 "echo Loaded environment from ${bootenv}; " \
- 173 "run importbootenv; " \
- 174 "fi; " \
- 175 "if test -n $uenvcmd; then " \
- 176 "echo Running uenvcmd ...; " \
- 177 "run uenvcmd; " \
- 178 "fi\0" \
复制代码
uboot使用了类似shell脚本语言的方式来编写变量。uenvboot做的事情是首先判断SD卡是否存在环境变量文件uEnv.txt,显然SD卡中没有uEnv.txt文件,然后判断uenvcmd变量值的长度是否为零,由于uenvcmd变量未定义,所以领航者开发板启动时不打印uenvboot中的信息。
cp_kernel2ram和netstart变量的内容如下:
- 180 "sdbootdev=0\0" \
- 182 "netstart=0x10000000\0" \
- 198 "kernel_img=image.ub\0" \
- 202 "cp_kernel2ram=mmcinfo && fatload mmc ${sdbootdev} ${netstart} ${kernel_img}\0" \
复制代码
可以看到netstart变量值为0x10000000,是文件加载到内存的地址。使用netstart变量名是因为使用tftpboot命令从网络下载内核镜像文件时,存放在内存中的位置由netstart指定。
cp_kernel2ram先执行mmcinfo命令,打印mmc信息,执行成功后从mmc0中加载内核镜像文件image.ub到内存DRAM的0x10000000处。
当我们明确知道我们所使用的板子的时候就可以大幅简化宏CONFIG_BOOTCOMMAND的设置,比如我们要从SD启动,那么宏CONFIG_BOOTCOMMAND就可简化为:
- #define CONFIG_BOOTCOMMAND \
- "mmcinfo && fatload mmc 0 0x10000000 image.ub;" \
- "bootm 0x10000000;"
复制代码
或者可以直接在uboot中设置bootcmd的值,命令如下:
- setenv bootcmd ' mmcinfo && fatload mmc 0 0x10000000 image.ub; bootm 0x10000000;'
复制代码
1.3.2环境变量bootargs
bootargs保存着uboot传递给Linux内核的参数。zynq的bootargs由设备树指定,在25.2.4节我们可以看到bootargs的值为空,也就是说zynq一般不用向linux内核传递参数。不过此处我们还是简单地讲解下bootargs。以下面的命令做介绍:
- setenv bootargs ‘console=${console},${baudrate} root=${mmcroot} rootfstype=ext4’
复制代码
假设console=ttyPS0,baudrate=115200,mmcroot=/dev/mmcblk0p2 rootwait rw,因此将其展开后就是:
- setenv bootargs ‘console=ttyPS0,115200 root=/dev/mmcblk0p2 rootwait rw rootfstype=ext4’
复制代码
可以看出bootargs的值为“console= ttyPS0,115200 root= /dev/mmcblk1p2 rootwait rw rootfstype=ext4。bootargs设置了很多的参数的值,这些参数Linux内核会使用到,常用的参数有:
1、console
console用来设置linux终端(或者叫控制台),也就是通过什么设备来和Linux进行交互,是串口还是LCD屏幕。如果是串口的话应该是串口几等等。一般设置串口作为Linux终端,这样我们就可以在电脑上通过串口工具来和linux交互了。这里设置console为ttyPS0,因为linux启动以后ZYNQ的串口0在linux下的设备文件就是/dev/ttyPS0,在Linux下,一切皆文件。
ttyPS0后面有个“,115200”,这是设置串口的波特率,console= ttyPS0,115200综合起来就是设置ttyPS0(也就是串口0)作为Linux的终端,并且串口波特率设置为115200。
2、root
root用来设置根文件系统的位置,root=/dev/mmcblk0p2用于指明根文件系统存放在mmcblk0设备的分区2中。领航者开发板启动linux以后会存在/dev/mmcblk0、/dev/mmcblk1、/dev/mmcblk0p1、/dev/mmcblk0p2、/dev/mmcblk1p1和/dev/mmcblk1p2这样的文件,其中/dev/mmcblkx(x=0~n)表示mmc设备,而/dev/mmcblkxpy(x=0~n,y=1~n)表示mmc设备x的分区y。在领航者开发板中/dev/mmcblk0表示SD卡,而/dev/mmcblk0p2表示SD卡的分区2。
root后面有“rootwait rw”,rootwait表示等待mmc设备初始化完成以后再挂载,否则的话mmc设备还没初始化完成就挂载根文件系统会出错的。rw表示根文件系统是可以读写的,不加rw的话可能无法在根文件系统中进行写操作,只能进行读操作。
3、rootfstype
此选项一般配置root一起使用,rootfstype用于指定根文件系统类型,如果根文件系统为ext格式的话此选项无所谓。如果根文件系统是yaffs、jffs或ubifs的话就需要设置此选项,指定根文件系统的类型。
bootargs常设置的选项就这三个,后面遇到其他选项的话再讲解。
1.4uboot启动Linux测试
uboot已经移植好了,bootcmd和bootargs这两个重要的环境变量也讲解了,接下来就要测试一下uboot能不能完成它的工作:启动Linux内核。我们测试两种启动Linux内核的方法,一种是直接从SD卡启动,一种是从网络启动。
1.4.1从SD卡启动Linux系统
从SD卡启动也就是将编译出来的Linux镜像文件image.ub保存在SD卡中,uboot从SD卡中读取该文件并启动。由于目前我们还没有讲解如何移植linux,所以这里我们就以第六章生成的image.ub为例,该文件已经烧写到了SD卡中,我们可以直接读取来测试。先检查一下SD卡的分区1中有没有image.ub文件,输入命令“ls mmc 0:1”,结果如下图所示:
图 25.4.1 SD卡分区1文件
从上图中可以看出,此时SD卡分区1中存在BOOT.BIN和image.ub这两个文件,所以我们可以测试新移植的uboot能不能启动linux内核。
直接输入boot,或者run bootcmd即可启动Linux内核,如果Linux内核启动成功的话就会输出如图下图所示的启动信息:
图 25.4.2 linux内核启动成功
1.4.2从网络启动Linux系统
从网络启动linux系统的唯一目的就是为了调试。不管是为了调试linux系统还是linux下的驱动。每次修改linux系统文件或者linux下的某个驱动以后都要将其烧写到SD中去测试,这样太麻烦了。我们可以设置linux从网络启动,也就是将linux镜像文件和根文件系统都放到Ubuntu下某个指定的文件夹中,这样每次重新编译linux内核或者某个linux驱动以后只需要使用cp命令将其拷贝到这个指定的文件夹中即可,这样就不用需要频繁的烧写SD,这样就加快了开发速度。我们可以通过nfs或者tftp从Ubuntu中下载image.ub文件或者zImage和设备树文件,根文件系统的话也可以通过nfs挂载,不过本小节我们不讲解如何通过nfs挂载根文件系统,这个在讲解根文件系统移植的时候再讲解。这里我们使用tftp从Ubuntu中下载image.ub文件或者zImage和设备树文件,前提是要将image.ub文件或者zImage和设备树文件放到Ubuntu下的tftpboot目录中,这些文件我们在第六章编译Petalinux工程的时候Petalinux工具已经帮我们复制到/tftpboot目录中了。
我们先运行uboot默认的netboot环境变量来从网络下载image.ub文件以启动linux。
使用“run netboot”命令即可,如下图所示:
图 25.4.3 使用“run netboot”命令启动linux
现在我们通过tftp下载zImage和system.dtb这两个文件来启动linux。首先设置bootcmd环境变量,设置如下:
setenv bootcmd 'tftpboot 8000 zImage; tftpboot 103cdda8 system.dtb; bootz 8000 - 103cdda8'
一开始是下载zImage和system.dtb这两个文件,过程如下图所示:
图 25.4.4下载过程
下载完成以后就是启动Linux内核,启动过程如下图所示:
图 25.4.5 Linux启动过程
uboot移植到此结束,简单总结一下uboot移植的过程:
①不管是购买的开发板还是自己做的开发板,基本都是参考半导体厂商的dmeo板,而半导体厂商会在他们自己的开发板上移植好uboot、linux kernel和systemfs等,最终制作好BSP包提供给用户。我们可以在官方提供的BSP包的基础上添加我们的板子,也就是俗称的移植。
②我们购买的开发板或者自己做的板子一般都不会原封不动的照抄半导体厂商的demo板,都会根据实际的情况来做修改,既然有修改就必然涉及到uboot下驱动的移植。
③一般uboot中需要解决串口、QSPI、EMMC或SD卡、网络和LCD驱动,因为uboot的主要目的就是启动Linux内核,所以不需要考虑太多的外设驱动。
④ 在uboot中添加自己的板子信息,根据自己板子的实际情况来修改uboot中的驱动。
相关推荐
- Micheal Nielsen's神经网络学习之二
-
依然是跟着MichaelNielsen的神经网络学习,基于前一篇的学习,已经大概明白了神经网络的基本结构和BP算法,也能通过神经网络训练数字识别功能,之后我试验了一下使用神经网络训练之前的文本分类,...
- CocoaPods + XCTest进行单元测试 c单元测试工具
-
在使用XCTest进行单元测试时,我们经常会遇到一些CocoaPods中的开源框架的调用,比如“Realm”或“Alamofire”在测试的时候,如果配置不当,会导致“frameworknotfo...
- Java基础知识回顾第四篇 java基础讲解
-
1、&和&&的区别作为逻辑运算符:&(不管左边是什么,右边都参与运算),&&(如果左边为false,右边则不参与运算,短路)另外&可作为位运算符...
- 项目中的流程及类似业务的设计模式总结
-
说到业务流程,可能是我做过的项目中涉及业务最多的一个方面了。除了在流程设计之外,在一些考核系统、产业审批、还有很多地方,都用到相似的设计思路,在此一并总结一下。再说到模式,并不是因为流行才用这个词,而...
- 联想三款显示器首批获得 Eyesafe Certified 2.0 认证
-
IT之家7月31日消息,据外媒报道,三款全新联想显示器是全球首批满足EyesafeCertified2.0的设备。据报道,联想获得EyesafeCertified2.0认证的显...
- maven的生命周期,插件介绍(二) 一个典型的maven构建生命周期
-
1.maven生命周期一个完整的项目构建过程通常包括清理、编译、测试、打包、集成测试、验证、部署等步骤,Maven从中抽取了一套完善的、易扩展的生命周期。Maven的生命周期是抽象的,其中的具体任务都...
- 多线程(3)-基于Object的线程等待与唤醒
-
概述在使用synchronized进行线程同步中介绍了依赖对象锁定线程,本篇文章介绍如何依赖对象协调线程。同synchronized悲观锁一样,线程本身不能等待与唤醒,也是需要对象才能完成等待与唤醒的...
- jquery mobile + 百度地图 + phonegap 写的一个"校园助手"的app
-
1jquerymobile+百度地图+phonegap写的一个"校园助手"的app,使用的是基于Flat-UI的jQueryMobile,请参考:https://github.com/...
- Apache 服务启动不了 apache系统服务启动不了
-
{我是新手,从未遇到此问题,请各位大大勿喷}事由:今天早上上班突然发现公司网站出现问题。经过排查,发现是Apache出现问题。首先检查配置文件没有出问题后,启动服务发现Apache服务能启动,但是没法...
- 健康债和技术债都不能欠 公众号: 我是攻城师(woshigcs)
-
在Solr4.4之后,Solr提供了SolrCloud分布式集群的模式,它带来的主要好处是:(1)大数据量下更高的性能(2)更好扩展性(3)更高的可靠性(4)更简单易用什么时候应该使用Sol...
- Eye Experience怎么用?HTC告诉你 eyebeam怎么用
-
IT之家(www.ithome.com):EyeExperience怎么用?HTC告诉你HTC上周除了发布HTCDesireEYE自拍机和HTCRE管状运动相机之外,还发布了一系列新的智能手机...
- Android系统应用隐藏和应用禁止卸载
-
1、应用隐藏与禁用Android设置中的应用管理器提供了一个功能,就是【应用停用】功能,这是针对某些系统应用的。当应用停用之后,应用的图标会被隐藏,但apk还是存在,不会删除,核心接口就是Packag...
- 计算机软件技术分享--赠人玫瑰,手遗余香
-
一、Netty介绍Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty...
- Gecco爬虫框架的线程和队列模型 爬虫通用框架
-
简述爬虫在抓取一个页面后一般有两个任务,一个是解析页面内容,一个是将需要继续抓取的url放入队列继续抓取。因此,当爬取的网页很多的情况下,待抓取url的管理也是爬虫框架需要解决的问题。本文主要说的是g...
- 一点感悟(一) 初识 初读感知的意思
-
时间过得很快,在IT业已从业了两年多。人这一辈子到底需要什么,在路边看着人来人往,大部分人脸上都是很匆忙。上海真是一个魔都,它有魅力,有底蕴,但是一个外地人在这里扎根置业,真的是举全家之力,还贷3...
- 一周热门
-
-
Boston Dynamics Founder to Attend the 2024 T-EDGE Conference
-
IDC机房服务器托管可提供的服务
-
详解PostgreSQL 如何获取当前日期时间
-
新版腾讯QQ更新Windows 9.9.7、Mac 6.9.25、Linux 3.2.5版本
-
一文看懂mysql时间函数now()、current_timestamp() 和sysdate()
-
流星蝴蝶剑:76邵氏精华版,强化了流星,消失了蝴蝶
-
PhotoShop通道
-
查看 CAD文件,电脑上又没装AutoCAD?这款CAD快速看图工具能帮你
-
WildBit Viewer 6.13 快速的图像查看器,具有幻灯片播放和编辑功能
-
光与灯具的专业术语 你知多少?
-
- 最近发表
-
- Micheal Nielsen's神经网络学习之二
- CocoaPods + XCTest进行单元测试 c单元测试工具
- Java基础知识回顾第四篇 java基础讲解
- 项目中的流程及类似业务的设计模式总结
- 联想三款显示器首批获得 Eyesafe Certified 2.0 认证
- maven的生命周期,插件介绍(二) 一个典型的maven构建生命周期
- 多线程(3)-基于Object的线程等待与唤醒
- jquery mobile + 百度地图 + phonegap 写的一个"校园助手"的app
- Apache 服务启动不了 apache系统服务启动不了
- 健康债和技术债都不能欠 公众号: 我是攻城师(woshigcs)
- 标签列表
-
- serv-u 破解版 (19)
- huaweiupdateextractor (27)
- thinkphp6下载 (25)
- mysql 时间索引 (31)
- mydisktest_v298 (34)
- sql 日期比较 (26)
- document.appendchild (35)
- 头像打包下载 (61)
- oppoa5专用解锁工具包 (23)
- acmecadconverter_8.52绿色版 (39)
- oracle timestamp比较大小 (28)
- f12019破解 (20)
- np++ (18)
- 魔兽模型 (18)
- java面试宝典2019pdf (17)
- beamoff下载 (17)
- unity shader入门精要pdf (22)
- word文档批量处理大师破解版 (36)
- pk10牛牛 (22)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- 加密与解密第四版pdf (30)
- pcm文件下载 (23)
- jemeter官网 (31)
- iteye (18)