ZYNQ QSPI flash分区设置&启动配置
来源:一口Linux发布时间:2024-07-193438浏览
询问 AI
一款基于zynq架构的产品,只有qspi flash,并没有其他的存储设备,
现在的要求固化某个应用程序app,设置开机启动.
点击查看图片来源
但是根据厂家提供的sdk,编译出的镜像重启后,文件系统的内容都会还原,
之前的方案是每次都要把程序放到buildroot下,
然后重新编译,将rootfs、内核镜像、设备树打包到image.ub.bin中,
然后用jtag重新烧录到flash中。
这很不合理,所以要我们需要对flash进行分区,
然后将需要固化的程序通过flashcp烧写到flash中,然后在用dd命令导出该文件。
该操作依赖linux的MTD子系统。
MTD(Memory Technology Device)是内存技术设备,它为原始闪存设备(例如NAND,OneNAND,NOR 等)提供了一个抽象层。
这些不同类型的Flash都可以使用相同的API。
通常内核都默认支持MTD驱动。MTD字符设备-通常称为**/dev/mtd0,/dev/mtd1**等。
这些字符设备提供对原始闪存的I/O访问。
它们支持许多ioctl调用,用于擦除擦除块,将其标记为不良或检查擦除块是否不良,获取有关MTD设备的信息等。
sysfs接口,它提供有关系统中每个MTD设备的完整信息。 此接口易于扩展,并且鼓励开发人员尽可能使用sysfs接口,而不是较旧的ioctl或/proc/mtd接口。
mtd子系统的sysfs接口已在内核中进行了说明,当前可在Documentation/ABI/ testing/sysfs-class-mtd中找到。
/proc/mtd proc文件系统文件提供常规的MTD信息。 这是旧版界面,而sysfs界面提供了更多信息。
MTD子系统支持带有软件和硬件ECC的 raw NAND闪存,OneNAND闪存,CFI(通用闪存接口)NOR闪存以及其他类型的闪存。
进入uboot
fmsh>sfprobe0
SF:Detectedn25q256withpagesize256Bytes,erasesize4KiB,total32MiB
该命令式查看设备信息。
可以看到qspi flash容量为32MB,即0x1E84800
一口君使用的平台需要固化2个文件:cfg(存储配置信息)、app(可执行程序)
加上必须烧录的boot.bin、image.ub.bin,一共有4个文件,
所以我们需要配置4个分区。
其中boot.bin包含了fpga的ip核和启动必要的文件信息,地址固定为0
image.ub.bin的地址通常厂家也会给出默认地址,
进入uboot打印环境信息:
fmsh>printenv
fit_size=0x153f000
flash_off=0x500000
load_addr=0x2000000
qspiboot=echoCopyingFITfromSPIflashtoRAM...
&&sfprobe&&sfread${load_addr}${flash_off}${fit_size}&&bootm${load_addr}
echoCopyingFITfromSPIflashtoRAM...:
打印提示信息
sfprobe:
查看设备硬件信息
sfread${load_addr}${flash_off}${fit_size},
从flash地址flash_off开始读取fit_size个字节到ram地址load_addr
bootm${load_addr}:
启动内核
可以看到flash地址是flash_off:0x500000
那现在我们就可以给这4个文件设置分区信息了
镜像 文件实际大小(hex) 起始地址 offset 块数
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
flash分区设备树说明如下:
Documentation\devicetree\bindings\mtd\partition.txt
FixedPartitions
================
Partitionscanberepresentedbysub-nodesofaflashdevice.Thiscanbeused
onplatformswhichhavestrongconventionsaboutwhichportionsofaflashare
usedforwhatpurposes,butwhichdon'tuseanon-flashpartitiontablesuch
asRedBoot.
Thepartitiontableshouldbeasubnodeoftheflashnodeandshouldbenamed
'partitions'.Thisnodeshouldhavethefollowingproperty:
-compatible:(required)mustbe"fixed-partitions"
Partitionsarethendefinedinsubnodesofthepartitionsnode.
Forbackwardscompatibilitypartitionsasdirectsubnodesoftheflashdeviceare
supported.Thisuseisdiscouraged.
NOTE:alsoforbackwardscompatibility,directsubnodesthathaveacompatible
stringarenotconsideredpartitions,astheymaybeusedforotherbindings.
#address-cells&#size-cellsmustbothbepresentinthepartitionssubnodeofthe
flashdevice.Therearetwovalidvaluesforboth:
<1>:forpartitionsthatrequireasingle32-bitcelltorepresenttheir
size/address(akathevalueisbelow4GiB)
<2>:forpartitionsthatrequiretwo32-bitcellstorepresenttheir
size/address(akathevalueis4GiBorgreater).
Requiredproperties:
-reg:Thepartition'soffsetandsizewithintheflash
Optionalproperties:
-label:Thelabel/nameforthispartition.Ifomitted,thelabelistaken
fromthenodename(excludingtheunitaddress).
-read-only:Thisparameter,ifpresent,isahinttoLinuxthatthis
partitionshouldonlybemountedread-only.Thisisusuallyusedforflash
partitionscontainingearly-bootfirmwareimagesordatawhichshouldnotbe
clobbered.
-lock:Donotunlockthepartitionatinitializationtime(notsupportedon
alldevices)
我们只需要关注分区的子节点说明即可:
reg
描述某个flash分区的offset和size
label(可选)
分区名字
read-only(可选)
该分区只读
根据前面所有分析内容,最终我们修改设备信息如下:
&qspi0{
status="okay";
flash0:s25fl256s@0{
compatible="spi-flash","spansion,s25fl256s1","jedec,spi-nor";
reg=<0>;/*chipselect*/
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=<0x00000000x500000>;
};
partition@uimage.ub{
label="uimage.ub";
reg=<0x5000000x1100000>;
};
partition@prm{
label="cfg";
reg=<0x16000000x10000>;
};
partition@kk_ap{
label="app";
reg=<0x16100000x30000>;
};
};
};
重新编译rootfs打包后重新启动即可。
#cat/proc/mtd
dev:sizeerasesizename
mtd0:0050000000010000"boot"
mtd1:0110000000010000"uimage.ub"
mtd2:0001000000010000"cfg"
mtd3:0003000000010000"app"
#ls/dev/mtd*-l
crw-------1rootroot90,0Jan100:00/dev/mtd0
crw-------1rootroot90,1Jan100:00/dev/mtd0ro
crw-------1rootroot90,2Jan100:00/dev/mtd1
crw-------1rootroot90,3Jan100:00/dev/mtd1ro
crw-------1rootroot90,4Jan100:00/dev/mtd2
crw-------1rootroot90,5Jan100:00/dev/mtd2ro
crw-------1rootroot90,6Jan100:00/dev/mtd3
crw-------1rootroot90,7Jan100:00/dev/mtd3ro
brw-------1rootroot31,0Jan100:00/dev/mtdblock0
brw-------1rootroot31,1Jan100:00/dev/mtdblock1
brw-------1rootroot31,2Jan100:00/dev/mtdblock2
brw-------1rootroot31,3Jan100:00/dev/mtdblock3
/dev/mtd0,/dev/mtd0ro,/dev/mtdblock0代表的是同一个MTD分区,但是**/dev/mtd0,/dev/mtd0ro都是字符设备,其中/dev/mtd0ro是只读字符设备,/dev/mtdblock0是块设备。常见的mtd-utils,nand_write等工具只能操作/dev/mtdX**字符设备,因为只有字符设备才支持ioctl操作。
查看 MTD 分区
cat/proc/mtd
擦除 MTD 分区
flash_eraseall/dev/mtdX
擦除/dev/mtd0分区的第1块数据。
flash_erase/dev/mtd00x01
写 MTD 分区 NOR Flash
flashcp/tmp/mtd.bin/dev/mtdX
写 MTD 分区 NAND Flash
nandwrite/tmp/image.bin/dev/mtdX
读 MTD 分区
ddif=/dev/mtdXof=/tmp/mtd.bin
首先需要下载文件导开发板,可以用sd卡、网口(tftp)、串口(rz命令),根据自己的开发板资源。
执行下面命令烧录:
flash_erase/dev/mtd20x01
flashcpcfg.bin/dev/mtd2
导出分区文件
ddif=/dev/mtd2of=/mnt/cfg.bin
flash_erase/dev/mtd30x03
flashcpapp/dev/mtd3
导出分区文件
ddif=/dev/mtd3of=/mnt/app.bin
注意导出的文件除了我们烧录的文件之外,
尾部还有多余FF,所以还需要去掉这些多余的部分,
所以我们必须要还原文件。
如下图所示:
【文件必须以二进制形式打开才能看到,彭老师用的Hex Editor Neo】
下载地址:
https://hhdsoftware.com/free-hex-editor
还原文件有很多方法,一口君自己写了个小程序,
原理:
逐字节读取文件,然后判断是否是0xFF,连续读取到16个0xff(防止文件中也由多个0XFF出现),
则认为读到了有效文件尾部,记录有效文件长度,然后根据该长度,复制成最终文件,该文件就是我们所需要的最终文件。
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
intmain(intargc,char**argv)
{
intfd_p;
intfdw_p;
unsignedcharc;
intcount=0;
intpos=0;
inti;
if(argc!=3)
{
printf("argumenterror\n");
for(inti=0;i<argc;i++)
{
printf("argv[%d]=%s\n",i,argv[i]);
}
}
fd_p=open(argv[1],O_RDWR);
if(fd_p<0){
printf("openfile%sfailed\n",argv[1]);
return-1;
}
fdw_p=open(argv[2],O_RDWR|O_CREAT);
if(fdw_p<0){
printf("openfile%sfailed\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);
}
return0;
}
可见写入到分区的文件和我们从分区读取后再还原的文件时一致的。
重启后,再验证
从MD5校验码可知,可执行程序还原正确。
要想开机后自动还原该文件,并启动程序app,步骤如下:
将exportimg拷贝文件系统**/mnt**下,(目录有执行权限即可)
设置开机子启动脚本
sdk/buildroot-xxxxxx/output/target/etc/init.d/rcS
文件尾部添加:
ddif=/dev/mtd2of=/mnt/cfg.bin
ddif=/dev/mtd3of=/mnt/app.bin
touch/mnt/app
touch/mnt/cfg
chmod777/mnt/app
chmod777/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&
本例在复旦微fmsh平台测试通过。zynq平台应该也没问题。
一口Linux
关注,回复【1024】海量Linux资料赠送
新闻来源:一口Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。

