/ 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  ```