/ README.md
README.md
1 # Ещё одна дешевая плата с Artix 7 2 3 Репозиторий radicle: [rad:z2cFrdTSipBAHzC5NShdbfQiBhS7](https://app.radicle.xyz/nodes/ash.radicle.garden/rad:z2cFrdTSipBAHzC5NShdbfQiBhS7) 4 5 Недавно узнал, что во внешнем мире широко распространён взлом онлайн игр через изменение содержимого оперативной памяти прямо во время игры. Для этого используются разнообразные платы расширения, которые вставляются в слот PCIe и через USB предоставляют прямой доступ к памяти компьютера. 6 7 Оказывается, на большинстве таких плат стоит какая нибудь средних размеров ПЛИС. А если ПЛИС, то такую плату пытливый плисовод может использовать для \<del\>мигания светодиодами\</del\> своих полезных экспериментов. 8 9 Наиболее популярные платы DMA делает некая американская фирма [CaptainDMA](https://captaindma.com/), одну из которых ([Captain DMA 75T](https://captaindma.com/product/captain-dma-75t/)) активно клонируют китайцы и продают в 3-4 раза дешевле оригинала. Например, я свою купил на Озоне за 3200р (курс доллара - 77.88р). 10 11 На плате стоит ПЛИС Xilinx Artix 7 75T (XC7A100TFGG484-2), загрузочная флэш Winbond W25Q64, мост "USB-3.0 to FIFO" FT6010, адаптер USB-JTAG на CH347 и два светодиода (надо же чем-то моргать). Больше ничего нет, ни памяти, ни периферийных раъёмов. 12 13 Схемы на плату нет, но есть пример [прошивки от PCILeech](https://github.com/ufrisk/pcileech-fpga/tree/master/CaptainDMA), откуда можно взять констрейны с распиновкой и рыбу для top-модуля. 14 15 Проект предназначен для сборки в Vivado, но из-за нестандартного адаптера JTAG прошить плату из Vivado не получится. Для этого предлагается использовать [некий китайский тул](https://github.com/WCHSoftGroup/ch347/releases/tag/CH347_OpenOCD_Release) для Windows, исходников для которого нет, и который у меня не заработал на виртуальной машине (а другой винды у меня на компьютере нет). 16 17 Это может показаться печальным, но мы унывать не будем, и попробует использовать для прошивки открытые тулы. Да и не только для прошивки. Сделаем мигалку светодиодами и соберём её совсем без использования Vivado. 18 19 ## Питание 20 21 Плата берёт питание с разъёма PCIe. Для работы ей нужно только 12В, 3.3В она не использует. Если у вас (как у меня) есть лишний разъём PCIe или плата-райзер, то можно подключить питание от внешнего источника. 22 23 ## OpenFPGALoader 24 25 Для начала попробует достучаться до платы с помощью [OpenFPGALoader](https://github.com/trabucayre/openFPGALoader), благо поддержка адаптеров на базе CH347 у него есть. Подключим кабель и питание и попробуем задетектить ПЛИС: 26 27 ``` 28 $ openFPGALoader -c ch347_jtag --detect 29 empty 30 JTAG TCK frequency set to 7.500 MHz 31 32 33 index 0: 34 idcode 0x3632093 35 manufacturer xilinx 36 family artix a7 75t 37 model xc7a75t 38 irlength 6 39 ``` 40 41 *Примечание:* для доступа к порту USB требуются права. Можно запускать openFPGALoader от рута, но правильней добавить запись в udev rules. Обыно это делается добавлением файла в `/etc/udev/rules.d/` с содержимым примерно такого вида: 42 43 ``` 44 SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="55dd", MODE="0666" 45 ``` 46 47 Отлично, ПЛИС видна, можно попробовать определить тип загрузочной флэш-памяти. 48 49 ``` 50 $ openFPGALoader -c ch347_jtag --fpga-part xc7a75tfgg484 --external-flash --detect 51 empty 52 write to flash 53 JTAG TCK frequency set to 7.500 MHz 54 55 56 protect_flash: 57 load program 58 Load SRAM: [===================================================] 100.00% 59 Done 60 Shift IR 35 61 ir: 1 isc_done 1 isc_ena 0 init 1 done 1 62 JEDEC ID: 0xef4017 63 Detected: Winbond W25Q64 128 sectors size: 64Mb 64 RDSR : 0x00 65 WIP : 0 66 WEL : 0 67 BP : 0 68 TB : 0 69 SRWD : 0 70 Done 71 ``` 72 73 Для детекта флэш-памяти в ПЛИС загружается специальная прошивка. Успешное завершение работы программы говорит о том, что доступ к ПЛИС есть и что флэш готова к прошивке. 74 75 ## Проект мигалки 76 77 Возьмём констрейны и топ из [прошивки от PCILeech](https://github.com/ufrisk/pcileech-fpga/tree/master/CaptainDMA), удалим всё лишнее и сделаем счётчик секунд. 78 79 Констрейны `captaindma_75t.xdc`: 80 81 ```tcl 82 set_property PACKAGE_PIN G21 [get_ports user_ld1_n] 83 set_property IOSTANDARD LVCMOS33 [get_ports user_ld1_n] 84 85 set_property PACKAGE_PIN G22 [get_ports user_ld2_n] 86 set_property IOSTANDARD LVCMOS33 [get_ports user_ld2_n] 87 88 set_property PACKAGE_PIN J19 [get_ports clk] 89 set_property IOSTANDARD LVCMOS33 [get_ports clk] 90 91 create_clock -period 10.000 -name clk [get_ports clk] 92 ``` 93 94 Исходный код RTL `captaindma_75t_ledblink.sv`: 95 96 ```verilog 97 module captaindma_75t_ledblink ( 98 input clk, // 100 MHz 99 output user_ld1_n, 100 output user_ld2_n 101 ); 102 localparam COUNT_WIDTH = 26; 103 logic [COUNT_WIDTH-1:0] cnt; 104 105 always_ff @(posedge clk) cnt <= cnt + 1'b1; 106 assign {user_ld1_n, user_ld2_n} = {cnt[COUNT_WIDTH-1], ~cnt[COUNT_WIDTH-1]}; 107 endmodule 108 ``` 109 110 Тут в общем пояснять нечего, всё понятно. 111 112 ## Сборка 113 114 Как я говорил, для сборки будем использовать открытый маршрут [openXC7](https://github.com/openXC7). Есть разные варианты установки, но поскольку я работаю в [NixOS](https://nixos.org/), то и запускать тулчейн буду с помощью Nix. Делается это одной командой: 115 116 ``` 117 $ nix develop github:openxc7/toolchain-nix 118 ``` 119 120 После скачивания нескольких гигабайт откроется консоль со всеми нужными командами. 121 122 *Примечание:* Проект [X-Ray](https://github.com/f4pga/prjxray), в рамках которого делается реверс битстримов ПЛИС Xilinx, пока не поддерживает Artix 7 75T. Есть поддержка 35T, 50T, 100T и 200T. Однако, в пуллреквестах давно висит [патч](https://github.com/f4pga/prjxray/pull/1871), который добавляет поддержку 75T. Патч странный, он добавляет в код 4 строчки, говорящие о том, что для 15T и 75T нужно использовать описание внутреннего устройства (fabric) от 50T и 100T соответственно. В связи с этим, сборка будет выполняться под ПЛИС Artix 7 100T с подменой IDCODE в битстриме, чтобы ПЛИС приняла прошивку как родную. 123 124 Как это работает, я затрудняюсь сказать, но оно работает (по крайней мере на мигании светодиодами). Скорее всего просто повезло, что плейсер положил блоки в область, которая существует в 75T. С другим проектом с другой распиновкой может не заработать. С другой стороны, я несколько минут всматривался в картинки c "картами" ПЛИС в Vivado, и не нашел ни одного отличия между 75T и 100T. Заговор? 125 126 Первый шаг - синтез. Синтез выполняется с помощью синтезатора [Yosys](https://github.com/YosysHQ/yosys). Запускаем yosys и вводим три команды: 127 128 ``` 129 $ yosys 130 ... 131 yosys> read -sv captaindma_75t_ledblink.sv 132 ... 133 yosys> synth_xilinx -flatten -abc9 -family xc7 134 ... 135 yosys> write_json ledblink.json 136 ... 137 yosys> exit 138 ``` 139 140 Если всё прошло гладко, в папке с проектом появится файл `ledblink.json`. Это результат синтеза - нетлист, в котором прописаны все блоки ПЛИС и соединения между ними. 141 142 Теперь нужно сделать размещение блоков на ПЛИС и трассировку соединений. Это делает программа [NextPNR](https://github.com/openXC7/nextpnr-xilinx), а точнее её форк с поддержкой ПЛИС Xilinx. 143 144 Перед самим PnR нужно подготовить базу данных с описанием структуры целевой ПЛИС. Файл генерируется из параметров, определенных в проекте [X-Ray](https://github.com/f4pga/prjxray). 145 146 ``` 147 $ $PYPY3 $NEXTPNR_XILINX_PYTHON_DIR/bbaexport.py --device xc7a100tfgg484-2 --bba xc7a100tfgg484-2.bba 148 $ bbasm -l xc7a100tfgg484-2.bba xc7a100tfgg484-2.bin 149 $ rm xc7a100tfgg484-2.bba 150 ``` 151 152 Теперь PnR: 153 154 ``` 155 $ nextpnr-xilinx --chipdb xc7a100tfgg484-2.bin --xdc captaindma_75t.xdc --json ledblink.json --fasm ledblink.fasm 156 ``` 157 158 В файле `ledblink.fasm` - результат работы плейсера и роутера. Следующий шаг - преобразование нетлиста в конфигурационные данные: 159 160 ``` 161 $ fasm2frames --part xc7a100tfgg484-2 --db-root ${PRJXRAY_DB_DIR}/artix7 ledblink.fasm > ledblink.frames 162 ``` 163 164 Файл `ledblink.frames` - это в общем готовый битстрим, только в текстовом виде и без структуры (заголовков, метаданных и пр.). Для сборки бинарного битстрима, пригодного для заливки в ПЛИС, нужно выполнить последнюю операцию. 165 166 Но перед этим вспомним, что собирали мы для ПЛИС Artix 7 100T, а запускать будем на Artix 7 75T. Чтобы ПЛИС не отторгла прошивку, необходимо в битстрим записать IDCODE от 75T. IDCODE прописан в файле `${PRJXRAY_DB_DIR}/artix7/xc7a100tfgg484-2/part.yaml`, который мы скопируем в папку проекта и сделаем замену: 167 168 ``` 169 $ sed -e 's/0x3631093/0x3632093/' ${PRJXRAY_DB_DIR}/artix7/xc7a100tfgg484-2/part.yaml > part.yaml 170 ``` 171 172 Теперь можно собирать битстрим: 173 174 ``` 175 $ xc7frames2bit --part_file part.yaml --part_name xc7a100tfgg484-2 --frm_file ledblink.frames --output_file ledblink.bit 176 ``` 177 178 Вот и всё, битстрим в файле `ledblink.bit`. 179 180 Все эти команды можно (нужно) собрать в мейкфайл, чтобы запускать одной командой. 181 182 ## Прошивка 183 184 Прошить битстрим можно напрямую в память ПЛИС или в конфигурационную флэш-память. Попробуем записать напрямую в ПЛИС: 185 186 ``` 187 $ openFPGALoader -c ch347_jtag ledblink.bit 188 ``` 189 190 Если всё хорошо, то плата радостно замигает светодиодами. 191 192 Для прошивки во флэш нужно указать точный part number, для того чтобы программа поняла, к каким пинам подключена флэш (на самом деле, чтобы выбрала подходящую прошивку с интерфейсом к загрузочной флэш-памяти), и опцию `--external-flash`, для выбора флэш-памяти в качестве цели прошивки. 193 194 Программа сама вычленит из битстрима голые данные и запишет их на флэш. После прошивки нужно выключить питание и включить снова. К сожалению, открытые тулы пока не умеют делать сжатый битстрим, по этому размер его достаточно большой и грузится он достаточно долго. После включения питания нужно немного подождать. 195 196 ``` 197 $ openFPGALoader -c ch347_jtag --fpga-part xc7a75tfgg484 --external-flash ledblink.bit 198 ``` 199 200 Альтернативный вариант - подготовить файл mcs (образ для записи во флэш), и просто передать его программе без дополнительных параметров: 201 202 ``` 203 $ openFPGALoader -c ch347_jtag --fpga-part xc7a75tfgg484 ledblink.mcs 204 ``` 205 206 Подготовить образ можно в Vivado или простым скриптом `bit2mcs.scm` из корня проекта. Скрипт извлекает из битстрима конфигурационные данные и печатает их на экран в формате Intel HEX. 207 208 ## Поддержка Vivado 209 210 Коллеги подсказали, что есть [Xilinx Virtual Cable с поддержкой CH347](https://github.com/AIOT-CAT/xvcd-ch347). Однако, с этой платой он не заработал, по этому пришлось форкнуть форк и немного пропатчить. Результат тут: https://github.com/punzik/xvcd-ch347 211 212 ## Использование битстрима от 100T в 75T 213 214 Чтобы использовать битстрим от 100T в 75T, нужно заменить в нём IDCODE. Если это битстрим от Vivado, то нужно ещё выключить генерацию CRC (или пересчитать её для нового IDCODE, если знаете как): 215 216 ```tcl 217 set_property BITSTREAM.GENERAL.CRC Disable [current_design] 218 ``` 219 220 Скрипт для патчинга в корне репозитория. Использовать так: 221 222 ``` 223 $ ./patch_idcode.scm 3631093 3632093 ledblink.bit 224 ```