Samet Demir

Kria KV260 Notes, Part 1: Unlocking the KV260’s microSD Performance

I recently got interested in FPGAs and bought an AMD Kria KV260. I expected the FPGA part to be difficult.

Instead, the first surprise was something else: the official Ubuntu desktop felt slow. The KV260 is not a desktop PC, and I did not expect it to feel like one. But small things like opening a terminal, installing a package, or clicking around the desktop had a delay that felt too big for a quad-core A53 with 4 GB of RAM. The whole operating system runs from a microSD card, so I started there.

This post is about what I found, and the one-line change that fixed it.

My setup

Everything below comes from this exact setup. Your board may be different; I will come back to that at the end.

Board Kria KV260 Vision AI Starter Kit, carrier Rev B (/proc/device-tree/model says ZynqMP KV260 revB)
OS Official Ubuntu 22.04 image for Kria, updated to 22.04.5 LTS
Kernel 5.15.0-1078-xilinx-zynqmp (after apt upgrade)
Card SanDisk Extreme 256 GB microSDXC (UHS-I, U3, V30, A2 — the card’s own status register confirms this)
Root filesystem ext4 on mmcblk1p2; boot files on a 1 GiB FAT partition
Display GNOME desktop over DisplayPort, kept running the whole time

Why the card matters so much

The KV260 starter kit has no eMMC. The microSD card *is* the disk: root filesystem, package database, Snap images, browser profile, logs, everything. A desktop session does many small reads and writes all the time. So the speed of the card is directly the speed of the desktop.

That is all the theory I need here. The interesting part is what the board was actually doing with the card.

What I checked

The kernel shows the negotiated SD mode in debugfs. This was the first thing I looked at:

sudo cat /sys/kernel/debug/mmc1/ios
clock:          50000000 Hz
actual clock:   50000000 Hz
bus width:      2 (4 bits)
timing spec:    2 (sd high-speed)
signal voltage: 0 (3.30 V)

dmesg said the same:

mmc1: new high speed SDXC card at address d555

So the card was running in plain SD High Speed mode: 50 MHz, 3.3 V. This is the old mode from before UHS existed. A 4-bit bus at 50 MHz can do about 25 MB/s in theory, and less in practice.

I had a UHS-I card. AMD’s own KV260 getting-started guide tells you to use a UHS-I card. But the board was not using any UHS mode.

I also checked that the card itself was fine. In my PC’s card reader, reading it with dd | gzip ran at about 68 MiB/s, and gzip was probably the limit. So the card was fast. Something on the board side was holding it back.

The real problem

UHS-I modes need the SD interface to switch from 3.3 V to 1.8 V. Whether Linux is allowed to try this switch is written in the device tree. So I dumped the running device tree and looked at the SD controller:

sudo apt install device-tree-compiler
sudo dtc -I fs -O dts /proc/device-tree > /tmp/running.dts
grep -A30 'mmc@ff170000' /tmp/running.dts

Inside mmc@ff170000 (the SD1 controller, which is connected to the microSD slot) I found this:

no-1-8-v;

This one property tells the kernel: never try the 1.8 V switch on this host. With it, every UHS-I mode is blocked, and the driver falls back to High Speed. The node also has no sd-uhs-sdr50 or sd-uhs-sdr104 properties. So this looks like a deliberately careful configuration.

I want to be honest about what this means. The ZynqMP SD controller does support UHS-I. AMD tells you to buy a UHS-I card. And the shipped device tree blocks it. I do not know the reason. Maybe it is caution across different carrier revisions, maybe it is history from an older kernel. It is a software setting, not something I could find documented as a hardware limit for Rev B. So it was worth testing, carefully.

The fix

The Ubuntu image boots from a FIT image (/boot/firmware/image.fit). It contains the kernel, the initrd, and several device trees. The U-Boot script picks the right one for your carrier revision. I did not want to rebuild that 100 MB file. But the boot script has a built-in override. You can see it with strings /boot/firmware/boot.scr.uimg:

if test -e ${devtype} ${devnum}:${distro_bootpart} /user-override.dtb; then
    echo "Loading user-override.dtb"
    ...

If a file called user-override.dtb exists on the boot partition, U-Boot uses it instead of the one inside the FIT. This gives a clean and reversible way: take the device tree my board really uses, remove the one property, and save the result as the override.

Before changing anything, I made a backup of the card on my PC (partclone.ext4 for the root partition, dd for the boot partition). I recommend the same. If the board does not boot, the fix is to delete one file from the FAT partition — but a full backup is still good to have.

All commands below run as root on the board.

1. Find which device tree your board boots. dumpimage -l /boot/firmware/image.fit lists them all. My Rev B carrier uses fdt-smk-k26-revA-sck-kv-g-revB.dtb, which is image index 6. (Rev A/Z carriers use index 4. Check yours, do not copy mine.)

2. Extract it and convert it to text:

mkdir -p /root/kv260-uhs
dumpimage -T flat_dt -p 6 -o /root/kv260-uhs/original.dtb /boot/firmware/image.fit
dtc -I dtb -O dts -o /root/kv260-uhs/original.dts /root/kv260-uhs/original.dtb
grep -n 'no-1-8-v' /root/kv260-uhs/original.dts

dtc prints many warnings. They are normal for vendor device trees and can be ignored.

3. Remove the property and compile again:

cp /root/kv260-uhs/original.dts /root/kv260-uhs/uhs.dts
sed -i '/^[[:space:]]*no-1-8-v;[[:space:]]*$/d' /root/kv260-uhs/uhs.dts
grep -c 'no-1-8-v' /root/kv260-uhs/uhs.dts    # must print 0
dtc -I dts -O dtb -o /root/kv260-uhs/uhs.dtb /root/kv260-uhs/uhs.dts

4. Install it as the override and reboot:

install -m 0644 /root/kv260-uhs/uhs.dtb /boot/firmware/user-override.dtb
sync
reboot

To undo it:

sudo rm /boot/firmware/user-override.dtb && sudo reboot

If the board does not boot at all, put the card in a PC and delete that file from the first partition. image.fit and the boot script are never touched.

Did it work?

After the reboot:

clock:          200000000 Hz
actual clock:   200000000 Hz
bus width:      2 (4 bits)
timing spec:    6 (sd uhs SDR104)
signal voltage: 1 (1.80 V)
mmc1: new ultra high speed SDR104 SDXC card at address d555

SDR104 at 200 MHz and 1.8 V. This is the fastest UHS-I mode. The kernel log had no CRC, timeout, or tuning errors, and it has stayed clean over several reboots since then.

Numbers

I ran the same fio test before and after. Only the device-tree change was different between the two runs. Same card, same board, same kernel, about fifty minutes apart:

fio --name=smallfiles --directory=$HOME/fio-sd-test \
    --rw=randrw --rwmixread=70 --bs=4k --size=256M \
    --iodepth=1 --numjobs=1 --direct=1 \
    --runtime=45 --time_based --group_reporting

This is a hard test on purpose: 4 KiB random reads and writes, one at a time, no caching. It is close to what a package manager or a desktop session really does to a disk.

High Speed (50 MHz) SDR104 (200 MHz)
4 KiB random read 980 IOPS, 0.60 ms avg 1272 IOPS, 0.44 ms avg
4 KiB random write 418 IOPS, 0.97 ms avg 544 IOPS, 0.79 ms avg
Device busy 99.9 % 99.9 %

About 30 % more small-I/O throughput, and lower latency to match. This is not a miracle. The card’s own flash latency still dominates 4 KiB operations, and the card is still at 100 % busy. But it is a real, repeatable improvement on exactly the kind of work that makes a desktop feel slow.

Sequential reads show the change much more clearly. An 8 GiB direct read from the block device:

sudo dd if=/dev/mmcblk1 of=/dev/null bs=16M iflag=direct count=512 status=progress
8589934592 bytes (8.6 GB, 8.0 GiB) copied, 140.602 s, 61.1 MB/s

I did not measure sequential speed before the change, so I can only compare with the ~25 MB/s theoretical limit of the old mode. 61 MB/s is close to what the same card did in my PC’s reader.

One more small thing: fstab

While looking around, I saw that the stock /etc/fstab mounts the root filesystem with discard:

LABEL=writable  /  ext4  discard,errors=remount-ro  0 1

With discard, ext4 sends a TRIM command every time it frees blocks. On a microSD card this adds latency exactly where you delete a lot of files: package upgrades, Snap refreshes, build cleanups. I changed it to noatime and let the weekly fstrim.timer do the trimming in one batch instead:

LABEL=writable  /  ext4  noatime,errors=remount-ro  0 1

Back up fstab first, reboot, and check with findmnt -no OPTIONS /. I made this change before the fio baseline above, so it is not part of those numbers. I do not have a separate measurement for it. I would still recommend it.

How it feels

In daily use: package installs and Snap refreshes no longer cause long pauses, GNOME feels less hesitant, and big copies to and from the card are clearly faster. It does not turn the KV260 into a workstation. It just makes it feel like the hardware it is.

Scope

Everything here was measured on one Rev B carrier, one SanDisk Extreme card, Ubuntu 22.04.5, kernel 5.15.0-1078. I do not know if every KV260 carrier revision runs SDR104 reliably, and I cannot tell you why AMD ships the device tree with no-1-8-v. Test it with a backup ready, watch dmesg for errors, and if you see CRC or tuning failures, delete the override and you are back to stock.

Storage was the first limit I found, but not the only one. In the next post, I’ll show how I configure my sytem to keep the graphical desktop and trim the background services that a focused KV260 setup does not need.

$ ls comments/ | wc -l

Zero comments, which makes the first one the best one by default.

$ make comment

Compile your thoughts

first comment is held until I read it. after that, you're trusted. no cookies, no third parties.