AI Box on the BC-250

A Sony PS5 APU as a local AI server: 16 GB GDDR6, 40 compute units, Ollama over Vulkan — build, setup and measurements for you to reproduce.

ASRock BC-250Fedora ServerOllama · Vulkanqwen3:14b · 36 tok/sno BIOS flash

Field report and guide · as of 3 November 2026 · reproduce at your own risk

   ████    ████         ████   █████   ███         ███   █████         ████    ███   █   █
   █   █  █                 █  █      █  ██       █   █    █           █   █  █   █   █ █ 
   ████   █      █████   ███   ████   █ █ █       █████    █    █████  ████   █   █    █  
   █   █  █             █          █  ██  █       █   █    █           █   █  █   █   █ █ 
   ████    ████         █████  ████    ███        █   █  █████         ████    ███   █   █

                 PS5 APU  ·  16 GB GDDR6  ·  40 CUs  ·  Ollama via Vulkan

As of 3 November 2026. A build guide for a local LLM box on the ASRock BC-250 (AMD “Cyan Skillfish”, the Sony PS5 APU) with Fedora Server and Ollama over Vulkan. Every number was measured on our own board; the sources are listed at the end. Our use case: a nightly security digest (classifying Wazuh events with an LLM) and a private chat AI.

0. Summary

What Result on our board
Board ASRock BC-250, BIOS P3.00 (stock, untouched), 6C/12T, 16 GB GDDR6
Usable GPU memory 13.5 GiB (512 MB split + 13.3 GB GTT, no BIOS flash)
qwen3:14b Q4 (9.3 GB) 27.8 tok/s with 24 CUs → 36.1 tok/s with 40 CUs
Prompt processing (2,426 tokens) 197 → 253 tok/s
Power draw ~50 W idle / ~120 W load (at the PSU, community measurement)
Cost board €260 used (eBay, DE) + PSU ~€45 + NVMe + fan + printed case

What the board is not: a replacement for a 24 GB card. 357 GB/s memory bandwidth (community measurement) versus 1,008 GB/s on an RTX 4090. Tokens per second are bandwidth-bound, and 27b dense models do not fit into 13 GiB. For the 14b class and MoE models it is very good.

The route in ten steps

 [1] PSU wiring       [2] Function test     [3] Fedora Server     [4] Split + GTT
   measure 12.0 V ───▶  live USB, lspci  ───▶  NVMe, grow LV   ───▶  memcfg 512 MB
                        BIOS: IOMMU off       dnf upgrade            ttm.pages_limit
        │                                                                 │
        ▼                                                                 ▼
 [10] Operation      [9] Measure           [8] 40 CUs            [5-7] Governor,
   cron/WoL, digest ◀── tok/s, temps,   ◀──  umr + boot     ◀──   sensors, Ollama
   monitoring           stress test          service               (Vulkan, unit)

1. Hardware

1.1 Board

1.2 Power supply

   8-pin PCIe plug, viewed from the cable side, latch on TOP
   ┌─────┬─────┬─────┬─────┐
   │ GND │ GND │ GND │ GND │   ◀── row with the latch: 4× ground  → V−
   ├─────┼─────┼─────┼─────┤
   │ 12V │ 12V │ 12V │ GND │   ◀── lower row: 3× +12 V → V+ , 1× ground → V−
   └─────┴─────┴─────┴─────┘
           ▲ the two detachable "+2" pins are BOTH ground

   LRS-350-12 terminal block:   L   N   ⏚  │  V−  V−  V+  V+
                               (230 V)      │  5 wires  3 wires

1.3 Cooling

            ↓↓↓  P12 Pro blows from above ONTO the heatsink  ↓↓↓
          ┌──────────────────────────────────────────┐
          │ ░░░░░░░░░░░░░░ 120 mm fan ░░░░░░░░░░░░░░░ │
          └──────────────────────────────────────────┘
      ←── ║║║║║║║║║║║║║║  heatsink fins  ║║║║║║║║║║║║║ ──→   exhaust sideways
          ╔══════════════════════════════════════════╗
          ║   APU  ·  GDDR6  ·  BC-250 board          ║
          ╚══════════════════════════════════════════╝
makeshift fan (USB, unregulated) ARCTIC P12 Pro on CPU_FAN1
Idle GPU / CPU 47–55 °C / 50–55 °C 45 °C / 51 °C
Load GPU / CPU 71 °C / 83–85 °C 63 °C / 77 °C
GPU clock at the end of the load run ~1,420 MHz (throttled) 1,850 MHz
Fan – 1,145 → 2,013 rpm, board-controlled

With the controlled fan the throttling is gone; the value inside the closed case is still open.

1.4 Case

1.5 Storage

1.6 Parts list (Amazon.de numbers checked on 27 Oct 2026)

Part Product ASIN / source
Board ASRock BC-250, used (unused leftover stock, stock BIOS) eBay, German seller, €260
PSU Mean Well LRS-350-12 (see the lesson in section 8: fanless next time) B07F2939RR
Fan ARCTIC P12 Pro PST LN, 120 mm, 4-pin PWM B0FVXTP7P6
Mains inlet IEC C14 inlet with switch and fuse holder B0H7X6NK2Z
Board power cable PCIe 8-pin extension (cut at the PSU end and wired to the terminals) B07RW7FQZN
Ferrules assortment with crimp tool B09P8B19VB
Mains cord C13, 1.8 m B07Y1V1HB2
Case screws self-tapping screw assortment for the 3D print B0CG673MS3
Board screws PC screw set #6-32 / M3 B0DFMG3F8K
Lid magnets neodymium 8×1 mm (for the case lid) B0BBMY4QNX
NVMe 2280, 250 GB is enough (ours: a Samsung 980 Pro 1 TB from stock) e.g. B09F4CT8LV
Tool long PH2 screwdriver (PSU terminals sit behind the board) B000ZEFON4
Optional CH341A programmer with SOIC8 clip, only for BIOS backup/flash. NOT needed for LLM use (split via memcfg). Caution: black CH341A boards put 5 V on the 3.3 V flash B0H9SKVSBW
From the workshop 5×20 mm 4 A slow-blow fuse, 2× M4×8 for the PSU, cable ties –

Case: the MakerWorld model (section 1.4) on your own or a friend’s 3D printer. No affiliate links.

2. Function test before anything else (return window!)

  1. Fan on the heatsink (any 12 V fan), CPU_FAN1, monitor on DP, keyboard, live USB (Fedora Workstation Live, a release, not a beta).
  2. Power on, Del → BIOS: read the version (P3.00), under Advanced set IOMMU → Disabled (docs: MUST be disabled), SVM does not matter (leave it off). There is no “Chipset” menu in the stock BIOS; that is normal.
  3. F11 → boot from the stick. In the live terminal:
lspci -nn | grep -iE 'vga|display'          # expected: Cyan Skillfish [BC-250] [1002:13fe]
free -g                                     # ~7 GiB with the stock 8/8 split
cat /sys/class/drm/card*/device/mem_info_vram_total   # 8589934592 with 8/8
journalctl -k | grep -iE 'amdgpu.*(SMU|VRAM|Display Core)'

Expected: SMU is initialized successfully, Display Core … initialized, 8192M of VRAM. Two WARNING … dal_irq_service lines during display init and one ACPI FADT warning are known and harmless.

3. Installation

sudo lvextend -l +100%FREE /dev/fedora_localhost-live/root
sudo xfs_growfs /
sudo dnf -y upgrade --refresh
sudo dnf -y install git gcc gcc-c++ make vulkan-tools mesa-vulkan-drivers lm_sensors nvme-cli pciutils zstd umr

4. Memory split and GTT (the part that matters)

   16 GB GDDR6, ONE memory for CPU and GPU (unified)

   Stock (UMA 8192):   ├──────── 8 GB VRAM fixed ───────┼──── 8 GB RAM ────┤
                                 GPU sees 8 GB             OS gets 7.5 GB

   memcfg UMA 512      ├─┬────────────── GTT 13.3 GB ──────────────┬────────┤
   + ttm.pages_limit:   │512 MB      GPU may allocate up to here    │ OS rest │
                        VRAM        (Ollama reports 13.5 GiB)       ~1.5 GB

4.1 Split WITHOUT a BIOS flash: bc250_memcfg

The stock BIOS hides the UMA setting (fixed 8/8 or 4/12). People used to flash a modified BIOS for this; that is obsolete: bc250_memcfg (fanoush) writes the value into battery-backed CMOS, no flash, rollback = CMOS clear (jumper/battery) or UMA_SIZE 8192. Source reviewed: 259 lines of C, only I/O ports 0x72/0x73, offsets 0x90–0xAB.

git clone https://github.com/fanoush/bc250_memcfg && cd bc250_memcfg && make
sudo ./bc250memcfg                # read only: UMA_SIZE=8192 on stock
sudo ./bc250memcfg UMA_SIZE 512   # "512 MB" = dynamic, the GPU can use almost everything
sudo reboot

After the reboot: free -h ≈ 14 GiB, mem_info_vram_total = 536870912.

4.2 Raise the GTT cap (otherwise the GPU only sees ~7.5 GB)

sudo grubby --update-kernel=ALL --args="ttm.pages_limit=3407872 ttm.page_pool_size=3407872"
sudo reboot
sudo dmesg | grep "GTT memory"    # expected: 13312M of GTT memory ready

(3407872 pages × 4 KiB = 13 GiB.) Community trap: systemd-tmpfiles can overwrite /sys/module/ttm/..., hence the kernel argument rather than sysfs.

5. OS tuning

5.1 Sensors (nct6683, read only)

echo nct6683 | sudo tee /etc/modules-load.d/99-sensors.conf
echo "options nct6683 force=true" | sudo tee /etc/modprobe.d/options-sensors.conf
sudo modprobe nct6683 && sensors

Gives you the thermistors, the AMD TSI temperature and the fan tachos (fan1–5).

5.2 IOMMU

Disabled in the BIOS (section 2). Proof in the kernel: dmesg | grep -c AMD-Vi = 0.

5.3 GPU governor (mandatory)

Without a governor the GPU is pinned at 1,500 MHz. Recommended (community since 03/2026): cyan-skillfish-governor-smu, which controls clocks through the SMU firmware, no kernel patch. Source reviewed (Rust, unsafe_code = deny, SMU mailbox via the host bridge PCI config space, no network).

sudo dnf copr enable filippor/bazzite
sudo dnf install cyan-skillfish-governor-smu
sudo systemctl enable --now cyan-skillfish-governor-smu
cat /sys/class/drm/card1/device/pp_dpm_sclk    # idle: 1000Mhz *

Config: /etc/cyan-skillfish-governor-smu/config.toml (voltage curve; only touch after measuring, see 8).

5.4 Wake-on-LAN

The NIC supports magic packets; enable it in the OS and save in the BIOS (in our case WoL only worked after a BIOS save):

sudo nmcli con modify "<connection name>" 802-3-ethernet.wake-on-lan magic
sudo ethtool enp4s0 | grep Wake-on     # expected: g

In our setup the firewall (OPNsense, os-wol plugin) wakes the box every morning by cron; at night it is powered off.

6. Ollama over Vulkan

Not curl | sh but the tarball with checksum:

V=v0.40.13; B=https://github.com/ollama/ollama/releases/download/$V
curl -sSL -o ollama-linux-amd64.tar.zst $B/ollama-linux-amd64.tar.zst
curl -sSL -o sha256sum.txt $B/sha256sum.txt
grep ollama-linux-amd64.tar.zst sha256sum.txt   # compare with: sha256sum ollama-linux-amd64.tar.zst
sudo tar --zstd -xf ollama-linux-amd64.tar.zst -C /usr/local
sudo useradd -r -s /sbin/nologin -U -m -d /usr/share/ollama ollama
sudo usermod -a -G render,video ollama

Unit /etc/systemd/system/ollama.service; the three lines that make the difference are commented:

[Unit]
Description=Ollama Service
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/ollama serve
User=ollama
Group=ollama
Restart=always
RestartSec=3
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
Environment="OLLAMA_HOST=0.0.0.0:11434"
# without this the iGPU is discarded ("dropping integrated GPU")
Environment="OLLAMA_IGPU_ENABLE=1"
# prompt cache OFF: on the BC-250, RAM *is* GPU memory (GTT); llama-server's
# 8 GiB default fills the RAM and the runner dies with an OOM kill
Environment="LLAMA_ARG_CACHE_RAM=0"
# flash attention + KV cache q8_0: same generation speed, less memory per context
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
# Vulkan needs CAP_PERFMON to query VRAM (Ollama docs)
AmbientCapabilities=CAP_PERFMON
CapabilityBoundingSet=CAP_PERFMON

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload && sudo systemctl enable --now ollama
sudo firewall-cmd --permanent --add-port=11434/tcp && sudo firewall-cmd --reload
journalctl -u ollama | grep "inference compute"
# expected: library=Vulkan … description="AMD BC-250 (RADV GFX1013)" … total="13.5 GiB"
ollama pull qwen3:14b
ollama run qwen3:14b --verbose "Answer in one sentence: what is a SIEM?"

OOM trap (hit us after 28 digest requests): journalctl -u ollama shows Failed with result 'oom-kill', dmesg names llama-server. The cause is the prompt cache (srv load_model: use --cache-ram 0 to disable the prompt cache). The fix is the LLAMA_ARG_CACHE_RAM=0 line above.

7. Enabling all 40 compute units (no flash, no kernel patch)

The chip has 40 CUs, the driver enables 24. Two routes: a patched amdgpu module (duggasco, must be rebuilt after every kernel update) or at runtime via umr (WinnieLV/bc250-cu-live-manager). We use the latter: reversible by reboot, no kernel surgery.

   Shader engines of Cyan Skillfish — 5 WGPs of 2 CUs per row

              WGP0  WGP1  WGP2  WGP3  WGP4
   SE0.SH0  [ ██ ] [ ██ ] [ ██ ] [ ░░ ] [ ░░ ]
   SE0.SH1  [ ██ ] [ ██ ] [ ██ ] [ ░░ ] [ ░░ ]      ██ = active from the factory (24 CUs)
   SE1.SH0  [ ██ ] [ ██ ] [ ██ ] [ ░░ ] [ ░░ ]      ░░ = routed via umr (+16)
   SE1.SH1  [ ██ ] [ ██ ] [ ██ ] [ ░░ ] [ ░░ ]
                                                     mask per row: 0x07 → 0x1f
sudo dnf install umr        # AMD's register tool, shipped by Fedora
sudo install -D -m 0755 bc250-cu-live-manager.sh /opt/bc250-cu-live-manager/bc250-cu-live-manager.sh
cd /opt/bc250-cu-live-manager
sudo ./bc250-cu-live-manager.sh status          # CUs active & routed : 24/40
sudo ./bc250-cu-live-manager.sh enable all --yes
sudo ./bc250-cu-live-manager.sh status          # 40/40
# persistent across boots (systemd oneshot, config /etc/bc250-cu-live-manager.conf):
sudo ./bc250-cu-live-manager.sh write-service-table --yes
sudo ./bc250-cu-live-manager.sh install-service --yes
# rollback: sudo ./bc250-cu-live-manager.sh uninstall-service --yes  (or stock-dispatch)

Notes:

Our measurement (qwen3:14b Q4, Ollama, identical prompts):

24 CU 40 CU
Generation 27.8 tok/s 36.1 tok/s
Prompt, 2,426 tokens 197 tok/s 253 tok/s
80 digest requests (~1,000 prompt tokens each, one-line answer) 610 s 550 s

7.2 Comparison with a PS5 (same chip, same model)

A colleague runs the same model on a PS5 with llama.cpp (b11327, flash attention, KV cache q8_0, 36 CUs, 2.23 GHz). Re-measured on our board with Qwen3.5-9B Q4_K_M, Ollama, 40 CUs, still with the makeshift fan:

Qwen3.5-9B Q4_K_M PS5 (llama.cpp) BC-250 (Ollama)
Generation, short prompt 45–50 tok/s 50.0 tok/s
Generation, ~4.8k-token prompt ~40–50 tok/s 47.5 tok/s
Prompt processing, clean context 340–370 tok/s 507 tok/s

Tokens per second depend almost entirely on model size (bandwidth): the 14B model runs at 36 tok/s, the 9B at 50. The PS5 is not ahead of the board.

7.1 The two disabled CPU cores (not done)

They can be enabled via an SMU message without a BIOS flash (rw-r-r-0644/bc250-core-unlock, also available as cpu-unlock in the cu-live-manager): core mask 0x77 → 0xFF. Volatile until the next cold boot; permanent only via a BIOS DXE mod (boot-block flash, the highest-risk option). The cores may be disabled for a reason (several hours of stress-ng, check dmesg for MCEs). Known side effect: pp_dpm_sclk reports wrong values afterwards. For GPU inference they add nothing, so we deliberately left them off.

8. What is still open on our side (in this order)

  1. Thermals inside the closed case (open; with the P12 Pro the throttling is gone).
  2. Voltage curve in the governor (docs: 1,500 MHz @ ~900 mV), only with measurements.
  3. Benchmark llama.cpp directly against Ollama (community: up to ~2× on Vulkan).
  4. Not planned: memory timings/clock (silent errors), mitigations=off.
  5. Lesson for the next build: a different PSU. The LRS-350 is technically fine, but its fan runs continuously under load and is loud. Next time fanless (Mean Well UHP-350-12) or a semi-passive SFX unit with a native PCIe plug, and design the case around it from the start.

9. Sources

Contact

Questions, corrections, your own numbers?

Happy to talk shop with fellow admins — get in touch.

Get in touch