Не нужна NVIDIA! Часть 2: 8 нейросетей на FPGA за $50 — и всё упёрлось в память DDR3
Это я, пока 512 умножителей простаивают в ожидании весов от DDR3.
В первой части я рассказывал, как запустил Qwen на списанной FPGA-плате за $50 с открытым TPU (openTPU). Читатели справедливо отметили, что получился скорее анонс, чем полноценная статья. Где сборка проекта, где разбор кристалла, где замеры, откуда взялись 133 МГц и при чём тут КЕДР?
Сегодня мы вместе соберём прошивку в Vivado, раскрасим кристалл по модулям, распределим 9,7 ватта по блокам и разберём 165 запусков восьми языковых моделей. Главный вывод оказался совсем не тем, которого я ждал: матрица из 512 умножителей выдаёт 121 GOPS из теоретических 136,5, но при генерации текста ей достаётся меньше четверти этой мощности. Причина в том, что память DDR3 выжата на 94 % от своего предела и просто не успевает доставлять веса быстрее.
Коротко
-
Железо: Inspur YPCB-00338, Kintex-7 XC7K480T-2, 2 × 2 ГиБ DDR3-1066, PCIe Gen2 x8.
-
Моя сборка в Vivado: тактовая частота ядра 133,33 МГц, запас по таймингу 19 пикосекунд (я сторонник положительного
ШСЛАКА, мало ли кто привык жить с отрицательным). Задействовано 59 % LUT, 63 % BRAM, 37 % DSP и 76 % слайсов. -
Проверка на плате: 138 аппаратных тестов, три прогона подряд. Все 165 запусков LLM завершились успешно.
-
Скорость: Qwen3.5-2B выдаёт 12,5 ток/с, Qwen3-0.6B — 35, LFM2.5-230M — 95 (веса в 4 битах).
-
Узкое место: при генерации DDR3 загружена на 92–96 %. Поэтому переход с PCIe x8 на x1 почти ничего не меняет (разве что замедляет загрузку модели в память с компьютера), а 4-битные веса вместо 8-битных ускоряют работу в 1,4–1,8 раза.
Спойлер

По итогам опроса больше всего читателей интересовала сборка с нуля (59 %) и устройство TPU (51 %), затем шли подводные камни (37 %), стоимость (35 %) и сравнение моделей (29 %). Всё это здесь есть, причём в порядке от железа к цифрам.
Пссс…⚡ Если вы быстры, как Flash, вот куда прыгать:
Ну а тем, кто устроился надолго и готов погрузиться с головой, — поехали) ↓
1. Железо и сколько стоит эксперимент с КЕДРом
Плата

Inspur YPCB-00338 — это списанная серверная карта, которую распродают на нашем любимом AliExpress и на eBay. Для наших задач в ней важны три вещи:
-
Kintex-7 XC7K480T-2 (алмазик): 299 тыс. LUT, 1920 умножителей DSP48E1 и 955 блоков памяти по 36 Кбит (около 34 Мбит на весь кристалл).
-
Два канала DDR3-1066 (теперь и ты алмазик) по 2 ГБ, у каждого 64 бита данных плюс 8 бит ECC. ECC защищает от ошибок, но скорости не добавляет: полезных бит всё равно 64.
-
PCIe x8, по которому плата общается с компьютером.
34 Мбит на кристалле для LLM — капля в море: даже Qwen3-0.6B в 4 битах весит примерно в сто раз больше. Поэтому веса лежат в DDR3, и, забегая вперёд, именно DDR3 задаёт потолок скорости. Вы и сами уже догадываетесь, так что умничать не стану.
Стенды
Плата побывала в нескольких компьютерах, что позволило сравнить разные варианты подключения PCIe.
|
Стенд |
Описание |
PCIe |
|---|---|---|
|
ПК |
Windows, Intel Core i9-14900KF, 64GB DDR, 4T SSD, Vivado 2024.2.2 |
— |
|
A |
Ноутбук (Notebook), Ubuntu 24.04, плата снаружи через USB4 с туннелем PCIe (TH3P4G3), 16GB DDR, 2T SSD, i7-1185G7, блок питания (METALFISH Black Diamond 550M), кабель USB4 Type-C (идёт в комплекте с TH3P4G3) |
Gen2x1(Gen2x4) |
|
B |
MINISFORUM MS-A2, Ubuntu 22.04, плата в слоте, AMD Ryzen 9 9955HX, 64GB DDR, 2T SSD |
Gen2x8 |
Все замеры в статье сделаны на стенде B, где PCIe работает на полной скорости.
Сколько это стоит
$50 из заголовка — цена самой платы (сам чип, как справедливо заметили в комментариях к прошлой статье, стоит гораздо дороже). Указываю цены, по которым купил сам.
На AliExpress лучше искать по XC7K480T и ориентироваться на фотографии и отзывы: вместо платы вполне могут прислать болт, у меня такое однажды случилось :xD.
Полный стенд обойдётся дороже:
|
Позиция |
Цена |
|---|---|
|
Плата YPCB-00338 |
~$50 |
|
Доставка |
от 0 до бесконечности (у меня вышло ~$10) |
|
JTAG-программатор: |
~$40 |
|
USB4 с туннелем PCIe (для конфигурации А) |
~$140 |
|
Блок питания (для конфигурации А) |
~$85 |
|
ПК |
уже был |
В сумме выходит около $100, если плата стоит в слоте (плата 50 + доставка 10 + JTAG 40). Для стенда A с USB4 понадобится ещё примерно $225 на адаптер и блок питания.
Отдельная засада — TH3P4G3: во время прошивки платы устройство PCIe пропадает, и адаптер отключает ей питание, из-за чего прошить плату не получается. Для этого проекта его не рекомендую (возможно, это поведение можно отключить, но я не копал).
2. Собираем прошивку для кристалла с нуля
Что внутри прошивки
В Vivado собирается не один только TPU. Прошивка состоит из четырёх подсистем на одном кристалле:
-
Ядро openTPU на Verilog: матрица, векторный блок, рабочая память, DMA, квантизатор и секвенсор.
-
Мост PCIe — IP-блок Xilinx XDMA.
-
Два контроллера DDR3 из проекта LiteDRAM вместо привычного Xilinx MIG. Внутри работает маленький процессор RISC-V (VexRiscv), который при старте сам подбирает задержки для DDR3. Получается, что рядом с TPU крутится ещё один процессор, единственная задача которого — договориться с памятью.
-
Обвязка: мост от ядра к двум каналам памяти, регистры управления и аппаратный журнал событий.
Сборка
git clone https://github.com/FeSens/openTPU.git
cd openTPU
git checkout 8eaa480
Ревизию я зафиксировал намеренно: репозиторий активно развивается, и на свежем main цифры могут отличаться.
Перед стартом нужно установить Vivado 2024.2 или новее (новее я не проверял, возможны нюансы).
На Windows всё делает один скрипт PowerShell: он создаёт проект, генерирует IP, выполняет синтез и разводку и записывает bitstream:
run_vivado.ps1
param(
[int]$DDR = 1066,
[int]$JOBS = 8, # как нагрузить процессор
[int]$MCOLS = 4,
[int]$VPU_CL = 2,
[int]$LANES = 8,
[int]$ACT_ROWS = 0,
[int]$DSTEP = 1,
[double]$CORE_MHZ = 0,
[int]$PCIE_GEN = 2,
[switch]$FAST,
[switch]$ImplOnly,
[string]$IMPL_STRATEGY = "",
[string]$BUILD_ID = "00000000",
# Измени этот путь под свою установку
[string]$Vivado = "C:\Xilinx\Vivado\2024.2\bin\vivado.bat"
)
$ErrorActionPreference = "Stop"
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$root = (Resolve-Path (Join-Path $here "..\..")).Path
$out = Join-Path $root "build\vivado"
if ($DDR -ne 1066) {
throw "DDR3-$DDR is unsupported. LiteDRAM is generated for DDR3-1066."
}
if ($ACT_ROWS -eq 0) {
$ACT_ROWS = $MCOLS
}
if ($CORE_MHZ -eq 0) {
if ($FAST) {
$CORE_MHZ = 100
} else {
$CORE_MHZ = 133.33
}
}
if (!(Test-Path $Vivado)) {
throw "Vivado not found: $Vivado"
}
New-Item -ItemType Directory -Force -Path $out | Out-Null
# Environment read by create_project.tcl / bd_native.tcl / build.tcl
$env:PCIE_GEN = "$PCIE_GEN"
if ($FAST) {
$env:OTPU_FAST = "1"
} else {
$env:OTPU_FAST = "0"
}
# Optional IP cache
$cache = Join-Path $env:LOCALAPPDATA "openTPU\vivado-ip-cache"
New-Item -ItemType Directory -Force -Path $cache | Out-Null
$env:OTPU_IP_CACHE = $cache
$createTcl = Join-Path $here "vivado\create_project.tcl"
$buildTcl = Join-Path $here "vivado\build.tcl"
Write-Host ""
Write-Host "openTPU Vivado build"
Write-Host "-------------------"
Write-Host "Root : $root"
Write-Host "Output : $out"
Write-Host "DDR : $DDR"
Write-Host "CORE_MHZ : $CORE_MHZ"
Write-Host "MCOLS : $MCOLS"
Write-Host "VPU_CL : $VPU_CL"
Write-Host "LANES : $LANES"
Write-Host "ACT_ROWS : $ACT_ROWS"
Write-Host "DSTEP : $DSTEP"
Write-Host "PCIe Gen : $PCIE_GEN"
Write-Host "FAST : $FAST"
Write-Host ""
if (!$ImplOnly) {
Write-Host "=== CREATE PROJECT ==="
& $Vivado `
-mode batch `
-nojournal `
-log (Join-Path $out "create_project.log") `
-source $createTcl `
-tclargs `
$DDR `
$out `
$MCOLS `
$CORE_MHZ `
$BUILD_ID `
$VPU_CL `
$LANES `
$ACT_ROWS `
$DSTEP
if ($LASTEXITCODE -ne 0) {
throw "Vivado create_project failed: $LASTEXITCODE"
}
Write-Host ""
Write-Host "=== FULL BUILD ==="
& $Vivado `
-mode batch `
-nojournal `
-log (Join-Path $out "build.log") `
-source $buildTcl `
-tclargs `
$out `
$JOBS `
"full" `
$IMPL_STRATEGY
} else {
Write-Host "=== IMPLEMENTATION ONLY ==="
& $Vivado `
-mode batch `
-nojournal `
-log (Join-Path $out "build_impl.log") `
-source $buildTcl `
-tclargs `
$out `
$JOBS `
"impl" `
$IMPL_STRATEGY
}
if ($LASTEXITCODE -ne 0) {
throw "Vivado build failed: $LASTEXITCODE"
}
Write-Host ""
Write-Host "================================"
Write-Host "DONE"
Write-Host "Bitstream: $out\otpu.bit"
Write-Host "MCS : $out\otpu.mcs"
Write-Host "Reports : $out\reports"
Write-Host "================================"
Положите run_vivado.ps1 (его текст приведён под спойлером выше) в папку boards\ypcb-00338.
В 17-й строке нужно заменить путь на тот, где у вас установлен Vivado ( [string]$Vivado = “C:\Xilinx\Vivado\2024.2\bin\vivado.bat”).
Запускаем всё сразу (создание проекта, синтез, имплементация) и терпеливо ждём; время зависит от мощности вашей машины.
cd boards\ypcb-00338
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Unblock-File .\run_vivado.ps1
.\run_vivado.ps1
Параметр Bypass разрешает запуск скачанного скрипта без подписи, причём только в текущем окне.
Параметры конфигурации: DDR = 1066, MCOLS = 4 (четыре колонки матрицы по 128 ячеек), LANES = 8 (дорожки векторного блока), CORE_MHZ = 133.333, PCIE_GEN = 2.
|
Этап |
Что происходит |
|---|---|
|
Создание проекта Vivado и IP |
блок-дизайн, XDMA, генерация IP |
|
Синтез |
RTL → нетлист |
|
Размещение и оптимизация |
place, phys_opt |
|
Трассировка |
route |
Проект появится в папке build\vivado.
Когда скрипт закончит работу, откройте в этой папке файл проекта с расширением *.xpr.
У меня это выглядит так:

После открытия переходим в Project Summary:

Вот так выглядит окно с победой: разводка завершена, тайминг сошёлся.


Запас составляет 19 пикосекунд (0.019ns), то есть 0,25 % такта. Почему получилось так впритык, разберу в разделе про кристалл.
Теперь нажимаем Gener
Лучший день в жизни: невеста вручила жениху GeForce RTX 5090, и он не сдержал слёз
Владельцы смартфонов Xiaomi в России берут микрозаймы на бóльшие суммы, чем пользователи Samsung и iPhone
Стальной сентябрь: от 122-терабайтных накопителей Unigen до архитектуры Arm Ranger и планов развития Huawei Ascend
«Самый быстрый GPU в истории»: он опережает GeForce RTX 5090 и Nvidia B200, а Bolt закончила отладку тестового чипа Zeus
SpaceX провела закрытый запуск: ракета Falcon 9 отправилась в космос в 28-й раз
«Крупнейшие перемены в истории»: источник из индустрии рассказал, что Samsung готовит для One UI 10
Nvidia без разрешения дала ИИ-агентам контроль над видеокартой и играми — из-за этого карты начали перегреваться
«Билайн» показал в московском метро лучший результат по 5G: покрытие 99,48% при скорости 185 Мбит/с