zynq QSPI flash分區(qū)設(shè)置&啟動(dòng)配置
需求:
一款基于zynq架構(gòu)的產(chǎn)品,只有qspi flash,并沒(méi)有其他的存儲(chǔ)設(shè)備,
現(xiàn)在的要求固化某個(gè)應(yīng)用程序app,設(shè)置開(kāi)機(jī)啟動(dòng),
但是根據(jù)廠家提供的sdk,編譯出的鏡像重啟后,文件系統(tǒng)的內(nèi)容都會(huì)還原,
之前的方案是每次都要把程序放到buildroot下,
然后重新編譯,將rootfs、內(nèi)核鏡像、設(shè)備樹(shù)打包到image.ub.bin中,
然后用jtag重新燒錄到flash中。
這很不合理,所以要我們需要對(duì)flash進(jìn)行分區(qū),
然后將需要固化的程序通過(guò)flashcp燒寫(xiě)到flash中,然后在用dd命令導(dǎo)出該文件。
0. MTD基礎(chǔ)
該操作依賴linux的MTD子系統(tǒng)。
MTD(Memory Technology Device)是內(nèi)存技術(shù)設(shè)備,它為原始閃存設(shè)備(例如NAND,OneNAND,NOR 等)提供了一個(gè)抽象層。
這些不同類(lèi)型的Flash都可以使用相同的API。
通常內(nèi)核都默認(rèn)支持MTD驅(qū)動(dòng)。

MTD字符設(shè)備-通常稱(chēng)為/dev/mtd0,/dev/mtd1等。
這些字符設(shè)備提供對(duì)原始閃存的I/O訪問(wèn)。
它們支持許多ioctl調(diào)用,用于擦除擦除塊,將其標(biāo)記為不良或檢查擦除塊是否不良,獲取有關(guān)MTD設(shè)備的信息等。
sysfs接口,它提供有關(guān)系統(tǒng)中每個(gè)MTD設(shè)備的完整信息。 此接口易于擴(kuò)展,并且鼓勵(lì)開(kāi)發(fā)人員盡可能使用sysfs接口,而不是較舊的ioctl或/proc/mtd接口。
mtd子系統(tǒng)的sysfs接口已在內(nèi)核中進(jìn)行了說(shuō)明,當(dāng)前可在Documentation/ABI/ testing/sysfs-class-mtd中找到。
/proc/mtd proc文件系統(tǒng)文件提供常規(guī)的MTD信息。 這是舊版界面,而sysfs界面提供了更多信息。
MTD子系統(tǒng)支持帶有軟件和硬件ECC的 raw NAND閃存,OneNAND閃存,CFI(通用閃存接口)NOR閃存以及其他類(lèi)型的閃存。
1. 查看qspi flash大小
進(jìn)入uboot
fmsh> sf probe 0
SF: Detected n25q256 with page size 256 Bytes, erase size 4 KiB, total 32 MiB
該命令式查看設(shè)備信息。
可以看到qspi flash容量為32MB,即0x1E84800
2. 需要固化鏡像分區(qū)地址設(shè)置
一口君使用的平臺(tái)需要固化2個(gè)文件:cfg(存儲(chǔ)配置信息)、app(可執(zhí)行程序)
加上必須燒錄的boot.bin、image.ub.bin,一共有4個(gè)文件,
所以我們需要配置4個(gè)分區(qū)。
1) boot.bin、image.ub.bin地址
其中boot.bin包含了fpga的ip核和啟動(dòng)必要的文件信息,地址固定為0
image.ub.bin的地址通常廠家也會(huì)給出默認(rèn)地址,
進(jìn)入uboot打印環(huán)境信息:
fmsh> printenv
fit_size=0x153f000
flash_off=0x500000
load_addr=0x2000000
qspiboot=echo Copying FIT from SPI flash to RAM...
&& sf probe && sf read ${load_addr} ${flash_off} ${fit_size} && bootm ${load_addr}
echo Copying FIT from SPI flash to RAM... :
打印提示信息
sf probe:
查看設(shè)備硬件信息
sf read ${load_addr} ${flash_off} ${fit_size},
從flash地址flash_off開(kāi)始讀取fit_size個(gè)字節(jié)到ram地址load_addr
bootm ${load_addr}:
啟動(dòng)內(nèi)核
可以看到flash地址是flash_off:0x500000
2) 分區(qū)劃分
那現(xiàn)在我們就可以給這4個(gè)文件設(shè)置分區(qū)信息了
| 鏡像 | 文件實(shí)際大小(hex) | 起始地址 | offset | 塊數(shù) |
|---|---|---|---|---|
| boot.bin | 3D0900 | 0x00000 | 0x500000 | 61-80 |
| image.ub.bin | D59F80 | 0x500000 | 0x1100000 | 214-272 |
| cfg.bin | 200 | 0x1600000 | 0x10000 | 1 |
| app.bin | 7800 | 0x1610000 | 0x30000 | 3 |
注意:
offset大小必須是 0x10000整數(shù)倍,這個(gè)是擦除的最小單位-塊。
每個(gè)分區(qū)大小結(jié)合要固化的程序,合理分配,既要考慮后面程序升級(jí)需要預(yù)留足夠空間,也不要太大,造成浪費(fèi)
分區(qū)劃分不能超過(guò)flash最大值32M
3. 設(shè)備樹(shù)
flash分區(qū)設(shè)備樹(shù)說(shuō)明如下:
Documentation\devicetree\bindings\mtd\partition.txt
Fixed Partitions
================
Partitions can be represented by sub-nodes of a flash device. This can be used
on platforms which have strong conventions about which portions of a flash are
used for what purposes, but which don't use an on-flash partition table such
as RedBoot.
The partition table should be a subnode of the flash node and should be named
'partitions'. This node should have the following property:
- compatible : (required) must be "fixed-partitions"
Partitions are then defined in subnodes of the partitions node.
For backwards compatibility partitions as direct subnodes of the flash device are
supported. This use is discouraged.
NOTE: also for backwards compatibility, direct subnodes that have a compatible
string are not considered partitions, as they may be used for other bindings.
#address-cells & #size-cells must both be present in the partitions subnode of the
flash device. There are two valid values for both:
<1>: for partitions that require a single 32-bit cell to represent their
size/address (aka the value is below 4 GiB)
<2>: for partitions that require two 32-bit cells to represent their
size/address (aka the value is 4 GiB or greater).
Required properties:
- reg : The partition's offset and size within the flash
Optional properties:
- label : The label / name for this partition. If omitted, the label is taken
from the node name (excluding the unit address).
- read-only : This parameter, if present, is a hint to Linux that this
partition should only be mounted read-only. This is usually used for flash
partitions containing early-boot firmware images or data which should not be
clobbered.
- lock : Do not unlock the partition at initialization time (not supported on
all devices)
我們只需要關(guān)注分區(qū)的子節(jié)點(diǎn)說(shuō)明即可:
-
reg
描述某個(gè)flash分區(qū)的offset和size
-
label(可選)
分區(qū)名字
-
read-only(可選)
該分區(qū)只讀
根據(jù)前面所有分析內(nèi)容,最終我們修改設(shè)備信息如下:
&qspi0 {
status = "okay";
flash0: s25fl256s@0 {
compatible = "spi-flash","spansion,s25fl256s1", "jedec,spi-nor";
reg = <0>; /* chip select */
spi-max-frequency = <50000000>;
m25p,fast-read;
page-size = <256>;
block-size = <16>; /* 2^16, 64KB */
cdns,read-delay = <2>;
cdns,tshsl-ns = <0>;
cdns,tsd2d-ns = <0>;
cdns,tchsh-ns = <0>;
cdns,tslch-ns = <0>;
#address-cells = <1>;
#size-cells = <1>;
partition@boot {
label = "boot";
reg = <0x0000000 0x500000>;
};
partition@uimage.ub {
label = "uimage.ub";
reg = <0x500000 0x1100000>;
};
partition@prm {
label = "cfg";
reg = <0x1600000 0x10000>;
};
partition@kk_ap {
label = "app";
reg = <0x1610000 0x30000>;
};
};
};
重新編譯rootfs打包后重新啟動(dòng)即可。
4. 查看分區(qū)信息
# cat /proc/mtd
dev: size erasesize name
mtd0: 00500000 00010000 "boot"
mtd1: 01100000 00010000 "uimage.ub"
mtd2: 00010000 00010000 "cfg"
mtd3: 00030000 00010000 "app"
# ls /dev/mtd* -l
crw------- 1 root root 90, 0 Jan 1 00:00 /dev/mtd0
crw------- 1 root root 90, 1 Jan 1 00:00 /dev/mtd0ro
crw------- 1 root root 90, 2 Jan 1 00:00 /dev/mtd1
crw------- 1 root root 90, 3 Jan 1 00:00 /dev/mtd1ro
crw------- 1 root root 90, 4 Jan 1 00:00 /dev/mtd2
crw------- 1 root root 90, 5 Jan 1 00:00 /dev/mtd2ro
crw------- 1 root root 90, 6 Jan 1 00:00 /dev/mtd3
crw------- 1 root root 90, 7 Jan 1 00:00 /dev/mtd3ro
brw------- 1 root root 31, 0 Jan 1 00:00 /dev/mtdblock0
brw------- 1 root root 31, 1 Jan 1 00:00 /dev/mtdblock1
brw------- 1 root root 31, 2 Jan 1 00:00 /dev/mtdblock2
brw------- 1 root root 31, 3 Jan 1 00:00 /dev/mtdblock3
/dev/mtd0,/dev/mtd0ro,/dev/mtdblock0代表的是同一個(gè)MTD分區(qū),但是/dev/mtd0,/dev/mtd0ro都是字符設(shè)備,其中/dev/mtd0ro是只讀字符設(shè)備,/dev/mtdblock0是塊設(shè)備。
常見(jiàn)的mtd-utils,nand_write等工具只能操作/dev/mtdX字符設(shè)備,因?yàn)橹挥凶址O(shè)備才支持ioctl操作。
5. 拷貝讀取 MTD 分區(qū)
-
查看 MTD 分區(qū)
cat /proc/mtd -
擦除 MTD 分區(qū)
flash_eraseall /dev/mtdX
擦除/dev/mtd0分區(qū)的第1塊數(shù)據(jù)。
flash_erase /dev/mtd0 0x0 1
-
寫(xiě) MTD 分區(qū) NOR Flash
flashcp /tmp/mtd.bin /dev/mtdX -
寫(xiě) MTD 分區(qū) NAND Flash
nandwrite /tmp/image.bin /dev/mtdX -
讀 MTD 分區(qū)
dd if=/dev/mtdX of=/tmp/mtd.bin
a) 燒寫(xiě)cfg.bin文件到mtd2
首先需要下載文件導(dǎo)開(kāi)發(fā)板,可以用sd卡、網(wǎng)口(tftp)、串口(rz命令),根據(jù)自己的開(kāi)發(fā)板資源。
執(zhí)行下面命令燒錄:
flash_erase /dev/mtd2 0x0 1
flashcp cfg.bin /dev/mtd2
導(dǎo)出分區(qū)文件
dd if=/dev/mtd2 of=/mnt/cfg.bin
b) 燒寫(xiě)app.bin到mtd3
flash_erase /dev/mtd3 0x0 3
flashcp app /dev/mtd3
導(dǎo)出分區(qū)文件
dd if=/dev/mtd3 of=/mnt/app.bin
6. 還原文件
注意導(dǎo)出的文件除了我們燒錄的文件之外,
尾部還有多余FF,所以還需要去掉這些多余的部分,
所以我們必須要還原文件。
如下圖所示:

【文件必須以二進(jìn)制形式打開(kāi)才能看到,彭老師用的Hex Editor Neo】

下載地址:
https://hhdsoftware.com/free-hex-editor
還原文件有很多方法,一口君自己寫(xiě)了個(gè)小程序,
原理:
逐字節(jié)讀取文件,然后判斷是否是0xFF,連續(xù)讀取到16個(gè)0xff(防止文件中也由多個(gè)0XFF出現(xiàn)),
則認(rèn)為讀到了有效文件尾部,記錄有效文件長(zhǎng)度,然后根據(jù)該長(zhǎng)度,復(fù)制成最終文件,該文件就是我們所需要的最終文件。
源碼:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char** argv)
{
int fd_p;
int fdw_p;
unsigned char c;
int count = 0;
int pos = 0;
int i;
if(argc != 3)
{
printf("argument error\n");
for(int i = 0; i < argc ; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
}
fd_p = open(argv[1], O_RDWR);
if(fd_p < 0){
printf("open file %s failed\n", argv[1]);
return -1;
}
fdw_p = open(argv[2], O_RDWR | O_CREAT);
if(fdw_p < 0){
printf("open file %s failed\n", argv[2]);
return -1;
}
while(1){
read(fd_p, &c, 1);
if(c == 0xff){
count++;
if(count >= 16){
break;
}
}
else{
count = 0;
}
pos++;
}
lseek(fd_p, SEEK_SET, 0);
for(i=0; i<pos-15; i++){
read(fd_p, &c, 1);
write(fdw_p, &c, 1);
}
return 0;
}
測(cè)試:

可見(jiàn)寫(xiě)入到分區(qū)的文件和我們從分區(qū)讀取后再還原的文件時(shí)一致的。
重啟后,再驗(yàn)證

從MD5校驗(yàn)碼可知,可執(zhí)行程序還原正確。
7. 開(kāi)機(jī)自動(dòng)還原文件
要想開(kāi)機(jī)后自動(dòng)還原該文件,并啟動(dòng)程序app,步驟如下:
- 將exportimg拷貝文件系統(tǒng)/mnt下,(目錄有執(zhí)行權(quán)限即可)
- 設(shè)置開(kāi)機(jī)子啟動(dòng)腳本
sdk/buildroot-xxxxxx/output/target/etc/init.d/rcS
文件尾部添加:
dd if=/dev/mtd2 of=/mnt/cfg.bin
dd if=/dev/mtd3 of=/mnt/app.bin
touch /mnt/app
touch /mnt/cfg
chmod 777 /mnt/app
chmod 777 /mnt/cfg
/mnt/exportimg /mnt/app.bin /mnt/app
/mnt/exportimg /mnt/cfg.bin /mnt/cfg
rm /mnt/cfg.bin
rm /mnt/app.bin
/mnt/app &
本例在復(fù)旦微fmsh平臺(tái)測(cè)試通過(guò)。
zynq平臺(tái)應(yīng)該也沒(méi)問(wèn)題。

浙公網(wǎng)安備 33010602011771號(hào)