problems with Winbond W25Q256JV as QSPI flash
This month I received a ZynqSDR/LibreSDR from aliexpress seller "HamGeek Store". It came with some minor differences from the zynqsdr_rev5.pdf that I found here (this was helpful, thanks!!).
Actually the only real difference I can see is that the AD7291 ADC for voltage monitoring is not populated, plus some related silkscreen changes:
- the pad for the ADC is marked "U28" on my silkscreen vs. "U29" in the PDF
- my +1.8V regulator is marked "U20" instead of "U28"
- my +3.3V regulator is marked "U29" instead of "U20"
This seems to close the circle..
I built the firmware from this repo, put it on an SD card, and this works. Now I'm trying to solve the various problems, and I'm not sure where to put my findings. Let me start with the QSPI flash.
The W25Q256JV seems like a wrong choice: it is not officially supported by AMD/Xilinx and Vivado cannot program it over JTAG.
With this repo the device tree for U-Boot has a compatible statement for the n25q256a type of SPI memory; also micron,m25p80 is listed, not sure how it works. But I guess other LibreSDRs are shipped with non-Winbond SPI flash?
When U-Boot is flashed to the QSPI with this device tree (as part of boot.dfu or boot.frm), U-Boot will fail to load (no output on /dev/ttyUSB2). When the compatible statement of the qspi node in 'u-boot-xlnx/arch/arm/dts/zynq-libre-sdr.dts' is changed to w25q256 (see here), U-Boot works.
But now when I also flash the libre.frm, it is dead again. I think this is because it writes above the first 16 MiB of the chip, where 3 byte addressing is no longer enough and 4 bytes per address are needed. This mode is not supported by the Zynq-7000 SPI controller, so the SPI memory has to be put into a 3 byte addressing mode where the controller writes the 4th address byte into a dedicated "Extended Address Register". Some context here. The Linux kernel driver (spi-nor) handles this, but it runs into a problem (from dmesg):
spi-nor spi1.0: found w25q256, expected n25q256a
spi-nor spi1.0: failed to read ear reg
spi-nor spi1.0: w25q256 (32768 Kbytes)
It can be used, but I suspect that the 3 byte addressing with the EAR is broken here, and when Linux writes to addresses >16MiB, it actually starts at 0 again, overwriting the boot code.
I should do a full write and readback to confirm this, or do a low-level analysis with SPI transactions, but I'm okay with just using the SD card instead.. maybe someone else will run into this and won't waste too much time.
speculation:
- is this a counterfeit W25Q256JV that doesn't care to implement 3-byte addressing?
- did all other designers avoid SPI flash that is not supported by Vivado, so this is some incompatibility that noone ever ran into or bothered to fix?
Maybe someone has the same W25Q256JV, bought it from the same shop, and can confirm or not confirm the problems I'm seeing?
Same Libresdr from hamgeek here. Working mainly with sd card for now. Next step of https://github.com/F5OEO/tezuka_fw is able to boot from flash (did'nt try it). As SPI flash should be blank right now, how to test the issue of 3bytes adressing from linux booted from sd? Maybe dd on mtdblock ?
The first indication would be the kernel message that I see on dmesg where it says "failed to read ear reg". If you also have this then my guess is that you will have the same issues.
Using dd on the mtdblock device should be one way to test, yes! Just mind the partitioning that is described in the device tree (or in /proc/mtd). Up to the 16 MiB boundary (somewhere in mtdblock3) all should be OK, and writing beyond that would probably overwrite parts of the first 16 MiB.
edit: better use flashcp on /dev/mtdX
Or maybe you want to test booting from SPI. Is the MSD Daemon thing working with tezuka_fw? This feature where you connect a PC to the "OTG" USB port and a mass storage device appears on your PC? Then you could just copy a "boot.frm" to it, unmount the USB device, and it should automatically do the flashing and reboot.
Otherwise you could copy "boot.bin" and "u-boot-env.bin" (from the "build" folder, or extracted from "boot.frm"!) to your SD card, boot, mount the SD card, and do manually what update.sh would do in the background:
edit: better use flashcp on /dev/mtdX
dd if=/opt/boot.bin of=/dev/mtdblock0 bs=64k && dd if=/opt/u-boot-env.bin of=/dev/mtdblock1 bs=64k
Now you should get the U-Boot console when you reboot without the SD card. And when you reboot to SD and flash the firmware image (copy firmware.frm to the MSD or dd the FIT image (.itb file) to mtdblock3) it may or may not be broken again :D
See the document about the flash.
I have build the https://github.com/F5OEO/tezuka_fw firmware as well running from SD card ok but updating the flash via copying the boot.frm and pluto.frm did not work, The flash must be erased first to write again!
Hope this give some additional info Johan PA3GSB
Hi Johan, thanks for sharing your findings!
If I interpret your document correctly then your solution was not fitting the QSPI contents into the lower 16 MiB of the chip, but changing something about how the software talks to the chip? In your document you write "The third and last step is modifying the linux driver or updating the linux image".
Do you also get spi-nor spi1.0: failed to read ear reg in your Linux kernel log?
I tried again, following the correct commands from your document with flashcp <file> /dev/mtdX instead of dd if=<file> of=/dev/mtdblockX.
Doing this with a build of the hz12opensource firmware fails:
# uname -a
Linux libre 5.15.0-175882-ge14e351533f9-dirty #1 SMP PREEMPT Wed Jan 8 17:33:33 CET 2025 armv7l GNU/Linux
# flashcp -v libre.fit /dev/mtd3
Erasing blocks: 248/248 (100%)
Writing data: 15818k/15818k (100%)
Verifying data: 10k/15818k (0%)File does not seem to match flash data. First mismatch at 0x00000000-0x00002800
# mtd_debug read /dev/mtd0 0x0 0x100000 /tmp/boot.readback.bin
Copied 1048576 bytes from address 0x00000000 in flash to /tmp/boot.readback.bin
# cmp boot.bin /tmp/boot.readback.bin
boot.bin /tmp/boot.readback.bin differ: char 1, line 1
But when I boot tezuka_fw instead then it works and I can boot from QSPI:
# uname -a
Linux libresdr 6.1.0 #1 SMP PREEMPT Thu Jan 16 17:54:03 UTC 2025 armv7l GNU/Linux
# flashcp -v libre.fit /dev/mtd3
Erasing blocks: 248/248 (100%)
Writing data: 15818k/15818k (100%)
Verifying data: 15818k/15818k (100%)
# mtd_debug read /dev/mtd0 0x0 0x100000 /tmp/boot.readback.bin
Copied 1048576 bytes from address 0x00000000 in flash to /tmp/boot.readback.bin
# cmp boot.bin /tmp/boot.readback.bin
cmp: EOF on boot.bin
All my previous attempts were with the hz12opensource firmware, so I guess tezuka's kernel fixed it for me.
Using dd on the mtdblock device should be one way to test, yes! Just mind the partitioning that is described in the device tree (or in
/proc/mtd). Up to the 16 MiB boundary (somewhere in mtdblock3) all should be OK, and writing beyond that would probably overwrite parts of the first 16 MiB.[..]
Otherwise you could copy "BOOT.bin" and "u-boot-env.bin" to your SD card, boot, mount the SD card, and do manually what
update.shwould do in the background:dd if=/opt/boot.bin of=/dev/mtdblock0 bs=64k && dd if=/opt/u-boot-env.bin of=/dev/mtdblock1 bs=64k
This was wrong, sorry, just using dd on the block device does not erase the flash cells first, like flashcp does.
Also BOOT.bin (for the SD card) doesn't have the same content as boot.frm (for the QSPI partitions 0 and 1). boot.frm has only the FSBL and U-Boot + the default uEnv. BOOT.bin also includes the FPGA bitstream. Or something like that. I hope I didn't confuse anyone.
Indeed using a new linux version does do the right writing to the flash. Indeed with the version 5 linux version the addressing above the 16MB is not working.
In the .gitmodules i changed the linux kernel link: path = linux url = https://github.com/maia-sdr/linux.git branch = maia-sdr
Which is the same as tezuka_fw is using.
Hope this helps.