花屏情况
起因
修改了uboot 的bootargs里的rmem导致花屏,
mem=254M@0x0 rmem=258M@0xFE00000就会绿屏ddr大小不匹配,512MB与256MB芯片的编译选项选择错误。remap_array的值不对
uboot的时候,开机画面是正常的,跑完内核后出现花屏现象
解决
改为
mem=254M@0x0 rmem=258M@0x2FE00000make isvp_a1_x_sfc0nor ,确保生成的ddr_reg_values.h一样
原因
地址映射的问题: console=ttyS1,115200n8 mem=254M@0x0 rmem=258M@0x2FE00000 A1 mips上物理地址的0x10000000~0x20000000(256M)通常给I/O控制器使用 A1 mips架构上地址空间前256M是DDR,0x10000000-0x20000000是寄存器地址空间,需要跳过。0x20000000之后相当于跟0x10000000接上了。
0x0-0x10000000第一段mem,0x10000000-0x20000000 io空间,0x20000000-0x30000000也是映射的第一段mem,0x30000000-0x40000000第二段mem
MIPS 地址空间布局

MIPS 的硬件设计
在芯片出厂时就硬性规定了地址空间的用途。正如图所示:
- 用户空间 (kuseg): 只有 2GB (
0x00000000到0x7FFFFFFF)。- 硬件强制要求最高位(Bit 31)为
0时才是用户模式,导致用户空间被物理地限制在了低 2GB 空间内。
- 硬件强制要求最高位(Bit 31)为
- 内核空间: 占据了剩下的 2GB (
0x80000000到0xFFFFFFFF)。
kseg0/kseg1 的物理地址换算:
物理地址 = 虚拟地址 & 0x1FFFFFFF(去掉最高 3 位)
例:
U-Boot 地址 0x80600000(kseg0)→ 物理 0x00600000 ✅ Flash logo 地址
U-Boot 地址 0xBFC00000(kseg1)→ 物理 0x1FC00000 ✅ vobuf 落在物理 RAM 内
0x00000000 ~ 0x10000000 第一段 mem(256M DDR)
0x10000000 ~ 0x20000000 IO空间(寄存器地址,需跳过)-> 0x90000000 0xF0000000
0x20000000 ~ 0x30000000 映射回第一段 mem
0x30000000 ~ 0x40000000 第二段 mem
vobuf 必须同时满足:
- 物理地址落在真实 DDR(非 IO 空间)
- 落在 rmem 区(Linux 不会动这块内存)
- 距离 rmem 末尾有足够空间存放 NV12 数据
vobuf = 0x80000000 + rmem起始物理地址 + rmem内的偏移
rmem 起始 = 0x2FE00000
rmem 大小 = 258MB = 0x10200000
rmem 末尾 = 0x2FE00000 + 0x10200000 = 0x40000000
vobuf = 0x80000000 + rmem起始 + (rmem大小 - 预留空间)
= 0x80000000 + 0x2FE00000 + (0x10200000 - 0x400000)
= 0x80000000 + 0x2FE00000 + 0xFE00000
= 0x80000000 + 0x3FE00000
= 0xBFC00000
更换物料遇到的问题
更换 flash:GD25Q256 → W25Q256JV(MC6830 NVR 板)
背景:量产料 GD25Q256(32MB)换成 Winbond W25Q256JV(同为 32MB,JEDEC ID ef 40 19)。同容量、同封装、引脚兼容,看似"直接换上就能用",实际连环踩了 5 个坑。
问题 1:U-Boot 不识别新 flash
现象:SF: unrecognized JEDEC id bytes: ef, 40, 19,saveenv 失败。
定位:这套 SDK 用的不是 U-Boot 通用 SPI flash 框架,而是厂商私有 SFC 驱动 mc_flash.c(CONFIG_MC_SFC_FLASH),ID 靠私有表 spi_nor_ids[] 匹配,表里没有 0xef4019。注意 SPL 阶段还有一份独立的 ID 表(board/molchip/fy01/molchip_nor_spl.c),要同步添加。
解决:两处表各加一行 w25q256jv 表项。内核侧 spi-nor.c 的通用表里已有 w25q256,无需添加。
问题 2:读数据损坏——quad 模式下 WP# 引脚故障
现象:内核烧进 flash 后 Bad Linux ARM zImage magic!;saveenv 显示成功,重启后 bad CRC, using default environment。
定位(关键推理):md.b 对比读回数据与预期值:
| 位置 | 预期 | 实际 | 差异 |
|---|---|---|---|
| zImage nop | e1 | a1 | bit6 清零 |
| 跳转指令 | ea | aa | bit6 清零 |
| zImage magic | 6f | 2b | bit6+bit2 清零 |
quad 读每字节分两拍传输:bit7..4 走 IO3..IO0,bit3..0 再走 IO3..IO0——bit6 和 bit2 恰好都由 IO2(即 WP# 引脚)承载。dump 出的 64 字节每一个都满足 byte & 0x44 == 0,即 IO2 恒读 0。同时 IO3 的数据正确(6f 的 bit3=1 能读回),说明芯片确实在执行 quad 读、QE 位已置上——排除驱动配置问题,锁定 WP# 物理链路(换料后虚焊/对地短路)。
一个根因解释所有现象:写入走单线不经过 IO2(数据真写进去了),读回才损坏——所以 saveenv “成功"而 env CRC 恒坏、内核数据完好而 magic 校验失败。
解决:临时方案——表项去掉 SPI_NOR_QUAD_READ 只留 DUAL(IO0/IO1,不经过 WP#),单独出一版应急 uboot;根治靠补焊 WP#(SOIC-8 第 3 脚)。
问题 3:saveenv 假成功——Winbond 没有 5Ch 擦除指令
现象:换应急 uboot 后读正常了,saveenv 仍显示成功、重启参数照丢。
定位:读路径已排除,锁定擦写路径。驱动代码链条是确定性的:
- 表项抄了 gd25q256 的
sector_size = 32K→spi_nor_select_erase()选 52h(32K 块擦除); - 32MB 料带
SPI_NOR_4B_OPCODES→ 3→4 字节转换表把 52h 换成 5Ch; - GD25Q256 支持 5Ch,Winbond W25Q256JV 的 4 字节擦除只有 21h(4KB)和 DCh(64KB),没有 5Ch;
- SPI NOR 对未实现指令静默忽略:不执行也不置 BUSY/WIP →
wait_ready()立即返回"成功”,ERS打印只是循环计数; - NOR 物理特性:编程只能 1→0,擦除才能 0→1。擦除假成功后编程把新数据"与"在旧数据上 → CRC 恒坏。(第一次 saveenv 恰好"成功"是因为全新 flash 本来就是全 FF。)
解决:表项 sector 改 64K → 擦除走 d8h→DCh。验证方法:sf erase 后读回看是否全 FF。
教训:同 JEDEC 容量、同 ID 位宽的不同厂商料,扩展指令集不保证兼容,抄同容量表项的 flag 时必须对照两家 datasheet 的指令表。
问题 4:dtb 之谜——厂商魔改了 bootz
现象:bootcmd 是 bootz 0x80008000 - 0x82000000,但没有任何命令往 0x82000000 加载 dtb,老设备却能一直正常启动。
定位:排除了内核 ARM_APPENDED_DTB(未开启)、U-Boot ATAGS(未编译)后,在 cmd/bootz.c 里发现厂商补丁:
fdt_addr = zImage_addr + *(ulong*)(zImage_addr + 0x2C); // 读 zImage 头的大小字段
sprintf(argv2, "0x%08lx", fdt_addr); // 原地覆盖用户传的第三参
bootz 根本不看第三参:自动定位"内核末尾 = mkdtb.sh 拼接的 dtb",dtb 随 kernel 分区整体 sf read 进内存,由 U-Boot 做 fixup(env bootargs 回填 /chosen、memory 节点、重定位)后传给内核。所以拼接 dtb 的消费者是 U-Boot,不是内核。
教训:魔改行为不改 help 也不留注释,靠读日志里的异常地址(## Flattened Device Tree blob at 80296370 = 加载地址+zImage 大小)反推出来的。日志里每一个"对不上"的数字都是线索。
问题 5:rootfs 挂载失败 panic——jffs2 擦除块不匹配
现象:内核起来了,jffs2 刷屏 Node would run over the end of the erase block / wrong erase size?,最终 No working init found panic。
定位:报错全部卡在 0x1000 边界 → 运行内核的 mtd erasesize 是 4K;而 rootfs jffs2 镜像按 64K 擦除块制作。原因:内核 spi-nor.c 的 w25q256 表项带 SECT_4K 标志,且 CONFIG_MTD_SPI_NOR_USE_4K_SECTORS Kconfig 默认 y;gd25q256 表项恰好没有 SECT_4K,所以老料从来没暴露过。同一个内核二进制,换颗料行为就变了。
解决:w25q256 表项 SECT_4K 改 0(与 gd25q256 同规),erasesize 恒为 64K。
复盘:方法论层面的收获
- 读写路径分离验证:
sf read + md.b对着已知 magic(zImage 的18 28 6f 01)比对,一条命令区分"数据坏了"还是"读坏了"; - 位级分析定位硬件:从损坏 bit 的分布(bit6/bit2)反推到 quad 总线的引脚映射(IO2/WP#),软件日志直接指向焊点;
- 警惕静默失败链:未知指令被忽略 → WIP 不置位 → 驱动无回读校验 → 层层"成功"。凡是关键写操作,验证手段要独立于执行路径(擦完读 FF);
- NOR 的 1→0 特性是判断"擦除没生效"的钥匙:写"成功"但读回是新旧数据按位与,必是没擦;
- 换料 checklist:ID 表(含 SPL 独立表)→ 指令集差异(擦除/4 字节命令)→ 保护位默认值(BP/WPS)→ erasesize 与文件系统镜像匹配 → 硬件焊接(尤其 WP#/HOLD# 这类 quad 复用脚);
- 参考机是最好的对照组:同 SDK、老料的 207 设备提供了"正确行为"基线(flash dump、/proc/device-tree、运行时 fdt),多处推理靠它闭环。