前言
在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145547974 中,我们利用官方提供的BSP(FSL Yocto Project Community BSP)构建了写到SD卡中的完整镜像,然后启动后发现存在不少问题,首要的问题就是u-boot不能识别网卡,在这篇博文中,我们就找到FSL Yocto Project Community BSP用到的u-boot源码,然后进行修改适配。
配置gcc交叉编译器
由于后面我们要自己去对自己修改后的u-boot进行编译,所以需要有gcc交叉编译器。
Buildroot在构建完之后能生成gcc交叉编译器,详情见 https://blog.youkuaiyun.com/wenhao_ir/article/details/145470042 【搜索“生成交叉编译工具链”】。
Ycoto在构建时也能生成,但是由于这里在配置时没有配置生成gcc交叉编译器,所以这里就只有自己去手动操作了。
为了减少这篇博文的长度,所以具体的gcc交叉编译器的下载和配置见下面博文:https://blog.youkuaiyun.com/wenhao_ir/article/details/145649698
找到u-boot源码
从博文https://blog.youkuaiyun.com/wenhao_ir/article/details/145547974构建的完整的SD卡镜像的运行情况来看,公板MCIMX6ULL-EVK(imx6ull14x14evk)的u-boot是不适应百问网的板子,主要就是网卡有问题,我们需要进行修改…
我们需要在FSL Yocto Project Community BSP中找到u-boot源码…
我们需要在在FSL Yocto Project Community BSP中找到对应的仓库地址,然后拉取到本地。
具体方法如下:
由于我们之前的镜像选择的是imx-image-multimedia,Yocto对应的层如下面这个列表:

所以我们需要在目录/imx-yocto-bsp/sources/meta-imx/中去找其对应的仓库地址信息。打开下面这个文件:
/imx-yocto-bsp/sources/meta-imx/meta-bsp/recipes-kernel/linux/linux-imx_5.4.bb
我们能找到meta-imx层对应的liunx的源码仓库地址:

git://source.codeaurora.org/external/imx/linux-imx.git
据此,我们可以推断出其u-boot源码的仓库地址为:
https://source.codeaurora.org/external/imx/uboot-imx
如果这个源还有效的话,运行下面的命令就能将u-boot的源码下载下来了:
git clone https://source.codeaurora.org/external/imx/uboot-imx
但很可惜这个源已经无效了,证据如下:
curl https://source.codeaurora.org/

上面的截图是美国的服务器上运行的,说明是真失效了,那就只有用百问网提供的之前通过仓库地址下载好的u-boot源码了。百度网盘下载链接:https://pan.baidu.com/s/1YgJPge5JMOf2HkKm_gQB7Q?pwd=ytut
下载好之后,先放在那里备用。

编译找到的u-boot源码
将上一步得到的u-boot的源码复制到Ubuntu中:

然后解压出来:


终端中进入目录/home/book/mybuild/uboot-imx
cd /home/book/mybuild/uboot-imx
不妨先执行一次清除命令:
make distclean

然后给文件check-config.sh添加执行权限
chmod +x ./scripts/check-config.sh
接着我们需要去查看下有哪些配置文件,配置文件在目录/uboot-imx/configs下:

可以看到文件非常多,不妨搜索一下关键词mx6ull_14x14_evk*

上面截图中的几个配置文件介绍如下:
- mx6ull_14x14_evk_defconfig:imx6ullevk公板默认的配置文件,默认只支持SD卡启动。
- mx6ull_14x14_evk_emmc_defconfig:支持eMMC启动方式启动的配置文件。
- mx6ull_14x14_evk_nand_defconfig:支持NAND启动方式启动的配置文件。
- mx6ull_14x14_evk_optee_defconfig:支持 optee 系统的配置文件。
- mx6ull_14x14_evk_plugin_defconfig:支持扩展功能的配置文件,主要是烧写功能。
- mx6ull_14x14_evk_qspi1_defconfig:支持 qspi 启动方式启动的配置文件。
我们这里选择支持eMMC启动方式的配置文件: mx6ull_14x14_evk_emmc_defconfig,运行下面的命令选择这个配置文件:
make mx6ull_14x14_evk_emmc_defconfig

然后执行下面的命令开始构建编译:
make -j4
命令中的4代表使用4个线程进行编译,当然,如果你的CPU资源够多,可以调大,比如调到6或8。

编译很快就结束了,u-boot毕竟只是很短的代码。

从上面的运行结果来看,已经生成了我们想要的u-boot镜像文件u-boot-dtb.imx,现在我们把它写入到eMMC中,然后通eMMC方式来运行u-boot。
镜像文件u-boot-dtb.imx的位置就是在u-boot的根目录下:

上面这个u-boot镜像的百度网盘下载地址:
https://pan.baidu.com/s/1rmyO7zWG4oDP8neWJtvWjQ?pwd=9d28
u-boot镜像文件烧写到eMMC中
参考下面这篇博文把生成的u-boot镜像文件u-boot-dtb.imx烧写到eMMC中:
https://blog.youkuaiyun.com/wenhao_ir/article/details/145653414
此时我准备烧写的u-boot镜像的百度网盘下载地址:
https://pan.baidu.com/s/1rmyO7zWG4oDP8neWJtvWjQ?pwd=9d28
运行烧写到eMMC中的u-boot并分析运行结果
烧写完成后关闭开发板电源,
烧写完成后关闭开发板电源,
烧写完成后关闭开发板电源,
然后设置开发板为eMMC启动方式,打开串口即可看到终端有u-boot的运行信息了。

u-boot的运行信息如下:
U-Boot 2020.04-dirty (Feb 15 2025 - 16:43:33 +0800)
CPU: i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 31C
Reset cause: POR
Model: i.MX6 ULL 14x14 EVK Board
Board: MX6ULL 14x14 EVK
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Loading Environment from MMC... *** Warning - bad CRC, using default environment
[*]-Video Link 0 (480 x 272)
[0] lcdif@21c8000, video
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc1(part 0) is current device
flash target is MMC:1
Net: Could not get PHY for FEC1: addr 1
Could not get PHY for FEC1: addr 1
Get shared mii bus on ethernet@2188000
Could not get PHY for FEC1: addr 2
Get shared mii bus on ethernet@2188000
undefined instruction
pc : [<00000084>] lr : [<9ef9bdaf>]
reloc pc : [<e8888084>] lr : [<87823daf>]
sp : 9df6d6b8 ip : 00000020 fp : 87800020
r10: 9df955c0 r9 : 9df75ed0 r8 : 9df774b8
r7 : 9df77458 r6 : 00000006 r5 : 00000004 r4 : 9df78748
r3 : 00000048 r2 : 00000006 r1 : 00000004 r0 : 9df78748
Flags: nzCv IRQs off FIQs off Mode SVC_32
Code: 00000000 00000000 00000000 00000011 (79706f43)
Resetting CPU ...
resetting ...
U-Boot 2020.04-dirty (Feb 15 2025 - 16:43:33 +0800)
CPU: i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 32C
Reset cause: POR
Model: i.MX6 ULL 14x14 EVK Board
Board: MX6ULL 14x14 EVK
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Loading Environment from MMC... *** Warning - bad CRC, using default environment
[*]-Video Link 0 (480 x 272)
[0] lcdif@21c8000, video
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc1(part 0) is current device
flash target is MMC:1
Net:
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
FEC: can't find phy-handle
Error: ethernet@20b4000 address not set.
Could not get PHY for FEC0: addr 2
Error: ethernet@20b4000 address not set.
FEC: can't find phy-handle
Error: ethernet@20b4000 address not set.
Could not get PHY for FEC0: addr 2
No ethernet found.
Fastboot: Normal
Normal Boot
Hit any key to stop autoboot: 0
switch to partitions #0, OK
mmc1(part 0) is current device
switch to partitions #0, OK
mmc1(part 0) is current device
** Unrecognized filesystem type **
** Unrecognized filesystem type **
Booting from net ...
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
FEC: can't find phy-handle
Error: ethernet@20b4000 address not set.
Could not get PHY for FEC0: addr 2
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
No ethernet found.
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
FEC: can't find phy-handle
Error: ethernet@20b4000 address not set.
Could not get PHY for FEC0: addr 2
Error: ethernet@20b4000 address not set.
Error: ethernet@20b4000 address not set.
No ethernet found.
Error: ethernet@20b4000 address not set.
zimage: Bad magic!
=>
通过串口终端打印信息可以看出:
uboot 在第一次运行时发生未定义指令异常错误导致uboot自动复位,截图如下:

结合第二次运行到同样位置时输出Booting from net ...:

我的推测如下:
第一次启动时,U-Boot尝试从MMC或SD卡加载内核镜像,但由于找不到有效的内核镜像,导致了undefined instruction错误。第二次启动时,U-Boot通过网络方式启动内核(Booting from net …),所以没有了第一次的错误。
整个过程中,可以很明显的看出还是网卡的问题,这和我们之前在SD卡中烧写的完整镜像中的u-boot是一样的,所以问题是一样的,所以我们当务之急是要解决网卡的问题。
当网卡的问题解决后,我们还可以使用tftp运行内核,并使用网络文件系统NFS挂载文件系统。
所以,现在就去解决网卡的问题吧。
对比公板和开发板的原理图
公板的以太网实现的原理图及相关引脚分析
关于什么叫以太网,详情见 https://blog.youkuaiyun.com/wenhao_ir/article/details/145691255
从上面这篇博文我们可以看出,以太网的主体是MAC层和物理层,而我们这篇博文涉及到的就是MAC(以太网控制器)与物理层芯片的连接,所以本篇博文我们称我们实现的东西为以太网。
这部分内容很多,所以专门写了一篇博文,链接如下:
https://blog.youkuaiyun.com/wenhao_ir/article/details/145663029
开发板的以太网实现的原理图及相关引脚分析
开发板的原理图的百度网盘下载链接:
https://pan.baidu.com/s/1YUfqZyXACwuNiYB_i5uzSw?pwd=thxh
这部分内容也很多,所以专门写了一篇博文,链接如下:
https://blog.youkuaiyun.com/wenhao_ir/article/details/145699423
开发板与公板不一样而导致要改代码地方
经过在上面两篇博文中仔细分析:发丙两个不同的地方是:
①复位信号来源不一样。
②两个以太网对应的物理层芯片的PHY地址不一样。
具体怎么个不一样法,详情见博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145699423 【搜索“与公板不一样而导致要改代码地方”】
知道了不同的地方后就可以开始改了。
在VScode中打开u-boot的源码


修改u-boot使其能实始化成功开发板上的以太网2
修改配置文件configs\mx6ul_14x14_evk_defconfig
文件configs\mx6ul_14x14_evk_defconfig是配置SD卡启动方式的配置文件。
打开文件configs\mx6ul_14x14_evk_defconfig,跳转到第49行:

把第49行和第50行:
CONFIG_PHY_MICREL=y
CONFIG_PHY_MICREL_KSZ8XXX=y
替换为下面的内容:
CONFIG_PHY_SMSC=y
改好之后的截图如下:

对CONFIG_PHY_SMSC=y这句话的解释如下:
详细解析:
-
CONFIG_PHY_SMSC=y:这条配置项表明,U-Boot 在编译时会启用对 SMSC PHY 驱动的支持。PHY(Physical Layer)是以太网硬件层的一部分,主要负责在物理媒介(如电缆)和高层协议之间传输数据。-
SMSC PHY 指的是由 SMSC(现在是 Microchip 公司的一个子公司)生产的 PHY 芯片。SMSC 提供了一系列以太网控制器和 PHY 芯片,通常用于嵌入式系统中,以实现网络通信功能。
-
启用此选项后,U-Boot 会在启动过程中初始化和配置这个 PHY,以便正确地与以太网设备进行通信。
-
具体作用:
- 启用 SMSC PHY 驱动:当 U-Boot 启动时,它会初始化 PHY 驱动程序,以确保以太网硬件的物理层能够与上层能正确配合。
- 配置网络功能:启用此配置后,U-Boot 可以通过网络接口(如以太网)进行更多的操作,比如网络引导(TFTP)或加载文件。
如果没有这个配置,U-Boot 可能无法识别或配置 SMSC PHY 芯片,导致无法正常使用网络接口。
适用场景:
- 如果你的硬件板子使用了 SMSC PHY 芯片,那么就需要启用这个选项,确保网络功能能够正常工作。
百问网的开发板的物理层芯片LAN8720A的确是 Microchip 公司的,所以咱们这里通过这个设置项启用对 SMSC PHY 驱动的支持。
该芯片旧版的Date sheet的截图如下:

该芯片新版的Date sheet如下:

可见,的确是Microchip收购买了SMSC这个公司。
u-boot自身是支持很多处理器芯片以及很多物理层芯片的
从这个修改我们还可以看出,u-boot自身是支持很多处理器芯片以及很多物理层芯片的。
修改配置文件configs\mx6ul_14x14_evk_emmc_defconfig
文件configs\mx6ul_14x14_evk_emmc_defconfig`是配置eMMC启动方式的配置文件。
和文件configs\mx6ul_14x14_evk_defconfig的修改一模一样:
把第49行和第50行:
CONFIG_PHY_MICREL=y
CONFIG_PHY_MICREL_KSZ8XXX=y
替换为下面的内容:
CONFIG_PHY_SMSC=y
改好之后的截图如下:

两个配置文件的区别
文件configs\mx6ul_14x14_evk_defconfig和文件configs\mx6ul_14x14_evk_emmc_defconfig有何区别?
答案如下:
文件configs\mx6ul_14x14_evk_defconfig是配置SD卡启动方式的配置文件。
文件configs\mx6ul_14x14_evk_emmc_defconfig`是配置eMMC启动方式的配置文件。
修改设备树文件arch\arm\dts\imx6ul-14x14-evk.dtsi
这里只需要以太网2,所以禁用以太网1
我们让u-boot支持网络是为了利用u-boot支持的TFTP将内核下载到我们的eMMC中,所以在u-boot运行的阶段,我们只需要有一个网口能用就是了,这里我们用以太网2,而不用以太网1。
u-boot也有类似于Linux系统的设备树文件,所以我们修改u-boot的设备树文件实现这一点。
以下是修改设备树文件,实现禁用以太网1。
打开设备树文件arch\arm\dts\imx6ul-14x14-evk.dtsi,转到86行:

把86行的代码由:
status = "okay";
改为:
status = "disabled";
改好之后的截图如下:

这样就把以太网1给禁用了。
设置复位信号的产生引脚,设置复位过程的持续时间
然后之前我们已经在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145699423 中分析出了,复位信号的产生不一样,在百问网的开发板中,物理层的芯片的复位信号接收引脚是连接到处理器的 SNVS_TAMPER6引脚的,的,我们可以让IMX6ULL的信号GPIO5_IO6路由到引脚 SNVS_TAMPER6,进而产生复位信号。
在设备树对设备的描述中,还有一个问题我们需要知道,那就是复位过程需要多久的时间?这就需要查阅物理层芯片LAN8720的Data sheet了。
物理层芯片LAN8720的Data sheet的百度网盘下载地址如下:
https://pan.baidu.com/s/1ZCVbzF2QH1SYCKOtnvSUWg?pwd=7i7a
关于复位过程需要多久的时间,我在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145749489 中进行了详细分析,实际上就是时间tpurstd的最小值加1,我在博文中分析出了,即26毫秒。
根据上面这两点,我们就可以继续修改u-boot的设备树文件了。
打开设备树文件arch\arm\dts\imx6ul-14x14-evk.dtsi
94行左右加入下面的语句:
phy-reset-gpios = <&gpio5 6 GPIO_ACTIVE_LOW>;
phy-reset-duration = <26>;
加好之后的截图如下:

设置两个以太网的物理层芯片的地址
因为前面已经禁用了u-boot初始化以太网1,所以其实这里可以只设置以太网2的物理层芯片地址,但是我还是把两个都设置了吧,也就是几行代码的事。
在博文https://blog.youkuaiyun.com/wenhao_ir/article/details/145699423中我们已经分析了以太网1对应的物理层芯片的PHY地址为0,以太网1对应的物理层芯片的PHY地址为2。而公板中以太网1对应的物理层芯片的PHY地址为2,以太网2对应的物理层芯片的PHY地址为1。
另外还要知道,在u-boot中,把以太网1称为叫ethphy0、把以太网2称为叫ethphy1。
据此我们对u-boot修改如下。
打开u-boot的源码文件arch\arm\dts\imx6ul-14x14-evk.dtsi
找到103行左右的代码:
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@2 {
reg = <2>;
micrel,led-mode = <1>;
clocks = <&clks IMX6UL_CLK_ENET_REF>;
clock-names = "rmii-ref";
};
ethphy1: ethernet-phy@1 {
reg = <1>;
micrel,led-mode = <1>;
clocks = <&clks IMX6UL_CLK_ENET2_REF>;
clock-names = "rmii-ref";
};
};

从上面这段代码中,我们可以看到,公板中的以太网1(ethphy0)使用的PHY地址为2,而以太网2(ethphy1)使用的PHY地址为1,这与我们之前的分析是吻合的。
现在我们百问网的开发板中,以太网1(ethphy0)使用的PHY地址为0,以太网2(ethphy1)使用的PHY地址为1,所以改为下面这样。
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@2 {
reg = <0>;
//micrel,led-mode = <1>;
clocks = <&clks IMX6UL_CLK_ENET_REF>;
clock-names = "rmii-ref";
};
ethphy1: ethernet-phy@1 {
reg = <1>;
//micrel,led-mode = <1>;
clocks = <&clks IMX6UL_CLK_ENET2_REF>;
clock-names = "rmii-ref";
};
};

这样,就把两块物理层芯片的PHY地址设置好了,虽然这里没有使用以太网1(ethphy0),但我还是顺便给它改好了。
设置GPIO5_IO06的属性
打开u-boot的源码文件arch\arm\dts\imx6ul-14x14-evk.dtsi,翻到第406行代码左右:
pinctrl_lcdif_ctrl: lcdifctrlgrp {
fsl,pins = <
MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79
MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79
MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79
MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79
/* used for lcd reset */
MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x79
>;
};

在这里添加下面这行代码:
MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x79
添加之后的截图如下:

在这里,为什么配置值为0x79,我猜想这是u-boot的代码定义的,这个配置值会被分解成各个寄存器需要的配置值,然后去配置相关的寄存器。
以下是一种分解猜想(注意:只是一种分解猜想,并不代表真实情况):
0x79=0111 1001(二进制)
每一位可以对应以下含义:
- 功能选择(前 3 位)
011(前 3位)表示该引脚的复用功能。通常这些位指定该引脚的工作功能,比如是否作为GPIO、I2C、UART等。011表示该引脚配置为 GPIO功能(具体取决于映射表,通常是GPIO模式)。
- 输入输出类型(接下来的 1 位)
1(第 4 位)表示 推挽输出。- 推挽输出意味着该引脚的电流方向可以在高电平和低电平之间切换。
- 电平驱动能力(接下来的 1 位)
1(第 5 位)表示该引脚的驱动能力为 高驱动能力。- 高驱动能力可以确保该引脚能够提供较高的输出电流。
- 上拉/下拉电阻(接下来的 2 位)
00(第 6 和第 7 位)表示没有配置上拉/下拉电阻。00通常表示 无上拉/下拉电阻(即浮空输入)。
- 输出电平(接下来的 1 位)
1(第 8 位)表示该引脚是 输出。- 这个配置保证该引脚在硬件上是一个输出引脚。
注意:以上只是一种分解猜想,并不代表真实情况。
另外,还要注意为什么是在对lcd的引脚控制中去初始化这个GPIO口,你要这样想,干嘛去认那个死理呢,在lcd的引脚控制中去初始化这个GPIO口不还是能达到我们初始化它的目的吗?
对软复位函数phy_reset()的完善
在百问网提供的教程和代码中,还对u-boot的文件drivers\net\phy\phy.c中的软复位函数phy_reset进行了修改完善。
在进行完善前,我们需要先了解下面两个知识点:
①首先我们要了解下物理层芯片LAN8720A的软复位操作的相关知识,这一点我写在另外的博文中了,链接为 https://blog.youkuaiyun.com/wenhao_ir/article/details/145749489 【搜索关键词“物理层芯片LAN8720A的软复位操作”】
②了解了物理层芯片LAN8720A的软复位操作的相关知识后,你还需要知道物理层芯片LAN8720A的软复位操作可能存在的异常是什么?解决方法是什么?关于这些,仍然请看我的另一篇博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145749489 【搜索关键词“软复位操作中存在的异常情况及解决方法”】
了解了上面的两个知识点后:我们再来看百问网教程中提供的对软复位函数phy_reset()的完善是怎么操作的。
百问网教程中提供的对软复位函数phy_reset()的完善方法如下:
打开文件 \uboot-imx\drivers\net\phy\phy.c
然后搜索genphy_config_aneg,跳转到函数phy_reset()所在的大约863行或874处:

在while循环中的代码:
reg = phy_read(phydev, devad, MII_BMCR);
的前面加上下面这两行代码:
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
udelay(1000);
修改之后的代码如下:

所以while循环就变成了下面的代码:
while ((reg & BMCR_RESET) && timeout--) {
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
udelay(1000);
reg = phy_read(phydev, devad, MII_BMCR);
if (reg < 0) {
debug("PHY status read failed\n");
return -1;
}
udelay(1000);
}
这段代码的解释如下:
这段代码是 PHY 复位操作 中的一个循环部分,主要用于监控 基本控制寄存器(BMCR) 中的 复位位(BMCR_RESET),直到复位完成或者超时。下面是对这段代码的详细解释:
- while 循环条件
while ((reg & BMCR_RESET) && timeout--) {
reg & BMCR_RESET:这是在检查 BMCR_RESET 位 是否仍然为 1,表示 PHY 是否仍在复位过程中。如果该位为 1,表示 PHY 还在复位中,循环会继续。timeout--:这是一个超时机制,timeout初始值为 500,每次循环时会递减。如果timeout减少到 0,表示复位操作超时,循环结束。
因此,while 循环的目的是:每次检查复位位是否仍然为 1(表示复位未完成),并通过 timeout 机制确保不会无限等待复位完成。
- 重新写入复位位
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
- 如果复位位 (
BMCR_RESET) 仍然为 1,代码会再次写入BMCR_RESET位,强制 PHY 继续保持复位状态。 - 这种操作是为了确保 PHY 继续执行复位操作,某些 PHY 芯片可能会出现复位位一直为1的异常,此时就需要反复写入复位命令才能保证复位完成。详情请查看我的另一篇博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145749489 【搜索关键词“软复位操作中存在的异常情况及解决方法”】
- 延时等待
udelay(1000);
udelay(1000)会引入 1 毫秒的延时。- 这个延时的作用是为了给 PHY 足够的时间完成复位操作,避免在复位过程中频繁读取寄存器而导致问题。复位操作需要一定的时间,延时确保复位过程中的状态能被正确读取。
- 读取 PHY 状态
reg = phy_read(phydev, devad, MII_BMCR);
phy_read(phydev, devad, MII_BMCR)会读取 BMCR 寄存器 的值,并将其存储在reg变量中。这个值包含了当前 PHY 的控制状态,包括复位位。- 通过读取
BMCR寄存器,程序检查 PHY 复位是否完成。
- 错误检查
if (reg < 0) {
debug("PHY status read failed\n");
return -1;
}
- 如果读取寄存器时出现错误(
reg < 0),则打印调试信息"PHY status read failed",并返回错误代码-1,表示读取失败。
- 再次延时
udelay(1000);
- 在每次读取和判断后,再次引入 1 毫秒的延时,以确保操作的稳定性。
- 退出条件:超时检查
if (reg & BMCR_RESET) {
puts("PHY reset timed out\n");
return -1;
}
- 如果在
timeout次循环后,BMCR_RESET 位 仍然为 1,表示复位操作没有在规定时间内完成,程序会打印"PHY reset timed out",并返回错误代码-1。
小结:
这段代码的目的是确保 PHY 在复位操作过程中按预期完成复位。它会反复检查 BMCR_RESET 位,并在超时前重新写入复位命令,直到复位位被清除(表示复位完成),或者达到超时次数。通过这个机制,函数能够可靠地等待 PHY 复位完成,或者在复位失败时报告超时错误。
完善物理层芯片在软复位后的自动协商配置
在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145699423 中,我们知道了百问网的开发板中在物理层芯片LAN8720A硬复位后,是启用了自动协商模式(Auto-negotiation)的【通过MODE[2:0] 配置带实现的】,也就是说我们是希望使用自动协商模式(Auto-negotiation)的。但是自动协商模式(Auto-negotiation)不是光通过MODE[2:0] 配置带就能实现的,它还需要进行进一步配置(比如广告自己能支持的双工模式和速率信息)。
在u-boot的驱动文件drivers\net\phy\phy.c中,提供了对自动协商模式(Auto-negotiation)的进一步配置。这个函数在公板提供的u-boot中的源代码如下:
/**
* genphy_config_aneg - restart auto-negotiation or write BMCR
* @phydev: target phy_device struct
*
* Description: If auto-negotiation is enabled, we configure the
* advertising, and then restart auto-negotiation. If it is not
* enabled, then we write the BMCR.
*/
int genphy_config_aneg(struct phy_device *phydev)
{
int result;
if (phydev->autoneg != AUTONEG_ENABLE)
return genphy_setup_forced(phydev);
result = genphy_config_advert(phydev);
if (result < 0) /* error */
return result;
if (result == 0) {
/*
* Advertisment hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
int ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
if (ctl < 0)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
result = 1; /* do restart aneg */
}
/*
* Only restart aneg if we are advertising something different
* than we were before.
*/
if (result > 0)
result = genphy_restart_aneg(phydev);
return result;
}
其注释摘取如下:
/**
* genphy_config_aneg - restart auto-negotiation or write BMCR
* @phydev: target phy_device struct
*
* Description: If auto-negotiation is enabled, we configure the
* advertising, and then restart auto-negotiation. If it is not
* enabled, then we write the BMCR.
*/
这段注释的解释如下:
这段注释是对 genphy_config_aneg 函数的简要说明,描述了该函数的功能以及根据是否启用了自动协商(auto-negotiation)来执行不同操作的逻辑。下面是详细解释:
-
函数功能说明
genphy_config_aneg - restart auto-negotiation or write BMCR @phydev: target phy_device struct- 函数名称:
genphy_config_aneg - 参数:
@phydev是一个指向phy_device结构体的指针,表示目标 PHY 设备。 - 简要描述:该函数的功能是根据当前的自动协商配置,执行两种操作之一:
- 如果启用了自动协商,则配置广告信息并重新启动自动协商。
- 如果未启用自动协商,则直接写入 基本控制寄存器(BMCR)。
- 函数名称:
-
详细描述
Description: If auto-negotiation is enabled, we configure the
advertising, and then restart auto-negotiation. If it is not
enabled, then we write the BMCR.
- 如果启用了自动协商:
- 首先,配置 PHY 广告信息。广告信息通常包括 PHY 支持的速率、双工模式等,这些信息将用于自动协商过程。
- 配置完广告信息后,重新启动自动协商。这意味着 PHY 会开始与对端设备协商,决定最佳的速率和双工模式。
- 如果未启用自动协商:
- 如果自动协商被禁用,函数会直接写入 BMCR(基本控制寄存器)。
BMCR寄存器控制PHY的一些基本功能,如速率、双工模式、复位等。
- 如果自动协商被禁用,函数会直接写入 BMCR(基本控制寄存器)。
总结:
genphy_config_aneg函数的目的是根据是否启用自动协商,选择适当的操作:- 启用自动协商时,配置广告信息并重启自动协商。
- 未启用自动协商时,直接写入控制寄存器
BMCR来配置固定的速率和模式。
该注释简要地概括了该函数的两个主要功能路径,并清晰地指出了自动协商启用和禁用时的不同处理方式。
但是这个函数还需要完善一下,才能适配物理层芯片LAN8720A的自动协商设置,具体来说,就是经实践证明,在运行函数 genphy_config_aneg 前,还需先执行一次软复位操作才能确保配置没问题。注意,在执行软复位之前,之前的配置信息早已保存在函数的参数struct phy_device *phydev中了,之前的某些配置信息应该是通过由引脚MDIO 和引脚MDC构成的“SMI(Serial Management Interface)”接口来读取的。
了解了我们要干什么和为什么要这么干后,我们我们就容易理解下面对u-boot的代码的修改和添加了。
打开文件 \uboot-imx\drivers\net\phy\phy.c
然后搜索genphy_config_aneg,跳转到大约176行:

在函数genphy_config_aneg的开头添加下面的代码:
int rc;
do{
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
udelay(1000);
rc=phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
udelay(1000);
}while(rc&BMCR_RESET);
加上上面的代码之后的截图如下:

这段代码的解释我实际上已经在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145749489 中解释得很清楚了,详情请搜索关键词“软复位操作中存在的异常情况及解决方法”。
设置以太网2的MAC地址
如果这里不设置MAC地址,那么在u-boot启动后,会出现下面的提示:

当然你可以在u-boot启动后,运行下面的命令手动设置以太网的MAC地址,并保存到eMMC中,
setenv eth1addr 00:01:3f:2d:3e:4d
saveenv
reset
这里要说明下,为什么是eth1addr,而不是eth2addr,原因是在u-boot中eth0对应IMX6ULL的以太网1,eth1对应IMX6ULL的以太网2。
但是我们不希望每次烧写了u-boot后都这样去设置,所以不妨把MAC写到u-boot中。
修改方法如下:
打开文件 include\configs\mx6ullevk.h,定位到115行左右:

在代码
"ip_dyn=yes\0" \
的后面加上代码:
"eth1addr=00:01:3f:2d:3e:4d\0" \
加上之后的代码截图如下:

重新编译u-boot
把修改后的u-boot源码打包复制到Ubuntu中:

然后解压出来:

然后打开终端,按上面第一次编译时的步骤,执行下面的命令进行编译:
make distclean

chmod +x ./scripts/check-config.sh

make mx6ull_14x14_evk_emmc_defconfig

make -j4

此时生成的u-boot镜像文件的百度网盘下载地址如下:
https://pan.baidu.com/s/1_Wyk36d3dnABZMK4R-MaFg?pwd=ik1k
u-boot镜像文件再次烧写到eMMC中
烧写工具的原理和详细使用见下面这篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/145653414
由于之前已经把驱动这些安装好了,所以可以像下面这样操作。
把目录100ask_imx6ull_pro开发板系统烧写工具\files下的文件u-boot-dtb.imx替换为编译u-boot时生成的镜像文件u-boot-dtb.imx。
此时准备烧写的u-boot镜像文件的百度网盘下载地址如下:
https://pan.baidu.com/s/1_Wyk36d3dnABZMK4R-MaFg?pwd=ik1k


把板子设置为USB启动的方式如下:

打开板子的电源…
打开板子的烧写工具…


切换到专业版界面上,然后点击运行按钮:

运行之后由于之前已经完成了相关设备驱动的安装,所以马上显示为“固件已运行”。

切换到“基础版”界面,因为是对百问网的IMX6ULL_PRO开发板作操作嘛,然后点击下图中的“更新 Uboot”:

烧写成功后的界面如下:

烧写完成后请按下面的步骤顺序操作:
①关闭Winows上百问网提供的烧写工具;
②关闭开发板的电源;
③开发板设为eMMC启动方式;
④打开串口终端;
⑤启动开发板,观察u-boot的运行情况。
烧写完成后首先关闭烧写工具,然后关闭开发板电源,烧写完成后关闭开发板电源,烧写完成后关闭开发板电源,,然后关闭烧写工具,接着设置开发板为eMMC启动方式,打开串口即可看到终端有u-boot的运行信息了。
把开发板的网线插好
我们需要找到我们该把网线插到哪个网口上,我们用的是以太网2,我们查看原理图,发现以太网2的插座是J7,相关原理图如下:

然后我们通丝印图查看J7在板子上的哪里:

可见如果人面向两个网口,那么右边那个网口是以太网2,即下面图中的13:

把网线插到编号为13的这个网口上。

运行第二次烧写到烧写到eMMC中的u-boot,并观察分析运行情况
在上上一部中烧写u-boot到eMMC成功后,请按下面的步骤顺序操作:
1、关闭Winows上百问网提供的烧写工具;
2、关闭开发板的电源;
3、开发板设为eMMC启动方式;

4、确认USB网口按下图进行了设置:

5、确认网线的一端插到USB网口上了:

6、确认网线的另一端插到开发板的网口2上了;

7、打开串口终端;
8、启动开发板,观察u-boot的运行情况。
运行情况如下:
U-Boot 2020.04-dirty (Feb 21 2025 - 15:50:48 +0800)
CPU: i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 29C
Reset cause: POR
Model: i.MX6 ULL 14x14 EVK Board
Board: MX6ULL 14x14 EVK
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Loading Environment from MMC... *** Warning - bad CRC, using default environment
[*]-Video Link 0 (480 x 272)
[0] lcdif@21c8000, video
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc1(part 0) is current device
flash target is MMC:1
Net: eth1: ethernet@20b4000 [PRIME]
Fastboot: Normal
Normal Boot
Hit any key to stop autoboot: 0
switch to partitions #0, OK
mmc1(part 0) is current device
switch to partitions #0, OK
mmc1(part 0) is current device
** Unrecognized filesystem type **
** Unrecognized filesystem type **
Booting from net ...
BOOTP broadcast 1
BOOTP broadcast 2
BOOTP broadcast 3
BOOTP broadcast 4
BOOTP broadcast 5
BOOTP broadcast 6
BOOTP broadcast 7
BOOTP broadcast 8
BOOTP broadcast 9
BOOTP broadcast 10
BOOTP broadcast 11
BOOTP broadcast 12
BOOTP broadcast 13
BOOTP broadcast 14
BOOTP broadcast 15
BOOTP broadcast 16
BOOTP broadcast 17
Retry time exceeded; starting again
BOOTP broadcast 1
BOOTP broadcast 2
BOOTP broadcast 3
BOOTP broadcast 4
BOOTP broadcast 5
BOOTP broadcast 6
BOOTP broadcast 7
BOOTP broadcast 8
BOOTP broadcast 9
BOOTP broadcast 10
BOOTP broadcast 11
BOOTP broadcast 12
BOOTP broadcast 13
BOOTP broadcast 14
BOOTP broadcast 15
BOOTP broadcast 16
BOOTP broadcast 17
Retry time exceeded; starting again
zimage: Bad magic!
=>
异常情况如下图中红框所示:

出现这个原因是此时咱们的USB网卡与开发板之间的网络连接并不支持用BOOTP协议(即后来的DHCP协议)来动态获取IP、网关等网络配置信息。
如果你把开发板连接到路由器的LAN口,路由器里是有DHCP服务的,所以是可以通过BOOTP协议(即后来的DHCP协议)来动态获取IP、子网掩码、网关等网络配置信息。把开发板连接到路由器的LAN口的u-boot启动截图如下:

我们这里不用管这个异常,仍然保持我们的开发板直接与USB网卡相连即可,因为后面我们可以通过u-boot的命令行手动设置IP地址、子网掩码、网关地址、TFTP服务器地址等,接下来的内容我们就来进行手动设置。
手动设置u-boot的以太网的IP地址、子网掩码、网关信息、TFTP服务器地址
请按照博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145814363 记录的方法进行设置和测试。
当最终测试TFTP能成功下载文件到指定内存位置时,便证明测试成功了。
测试成功的截图如下:

这样我们的u-boot便移植完成了。
附整个过程中各阶段的u-boot压缩文件
从FSL Yocto Project Community BSP中提取出的u-boot源码
百度网盘下载链接:
https://pan.baidu.com/s/1YgJPge5JMOf2HkKm_gQB7Q?pwd=ytut
将从FSL Yocto Project Community BSP中提取出的u-boot源码在未修改前进行了编译构建
百度网盘下载链接:
https://pan.baidu.com/s/1yqTSAfMVJ8VwQy1jFz0JHA?pwd=b2es
自己修改好的u-boot源码
百度网盘下载链接:
https://pan.baidu.com/s/1lvOsiapS0lx_Z42nHbsmVg?pwd=57qe
将自己修改好的u-boot源码进行了编译构建
百度网盘下载链接:
https://pan.baidu.com/s/1LT5NJlbUy6MKAhDqmeYBTg?pwd=ar4y
百问网提供的修改好的u-boot源码
百度网盘下载链接:
https://pan.baidu.com/s/1EXF8OxPOBDGE0UnPqQr-Qg?pwd=p9a1
两个阶段的u-boot镜像文件
FSL Yocto Project Community BSP中的u-boot代码生成的镜像的百度网盘下载地址:
https://pan.baidu.com/s/1rmyO7zWG4oDP8neWJtvWjQ?pwd=9d28
自己对u-boot进行修改后生成的u-boot镜像的百度网盘下载地址:
https://pan.baidu.com/s/1_Wyk36d3dnABZMK4R-MaFg?pwd=ik1k
1232

被折叠的 条评论
为什么被折叠?



