Иногда лучший способ чему-то научиться, будь то ПЛИС, модель ИИ или простейшая логическая задача — это позволить технологии научить саму себя.
Однажды мне захотелось запустить нейронную сеть на ПЛИС, и это стало моим первым опытом работы с тем и другим. К счастью, у меня был доступ к Anthropic API, что позволило воспользоваться помощью LLM-сервиса в создании нейронной сети XOR для ПЛИС.

На картинке выше приведена макетка с Arduino MKR Vidor 4000 и мигающими светодиодами. Два левых светодиода отображают входные значения, а правый крайний светодиод показывает результат операции XOR.
Первые шаги
До этого у меня не было абсолютно никакого опыта работы с ПЛИС. Запуск базового примера «Hello World» с мигающим светодиодом стал хорошим началом и придал мне уверенности для решения более сложной задачи: использования языковой модели при написании кода Verilog для нейронной сети XOR.
Почему XOR?
Реализация вентиля XOR с помощью нейронной сети может показаться излишеством. Для этой базовой функции цифровой логики достаточно нескольких логических элементов. Тем не менее, в изучении ИИ эта задача может послужить идеальной отправной точкой: небольшая, контролируемая и с легко предсказуемыми результатами.
Моделируем на Verilator
Компиляция кода для ПЛИС представляет из себя нечто вроде работы с чёрным ящиком. Здесь может помочь инструментарий Verilator. Он преобразует код Verilog в C++, что позволяет выполнять его симуляцию на компьютере перед развёртыванием на ПЛИС. Этот шаг оказался особенно кстати при работе с сигмоидальной функцией. Вместо того чтобы вычислять результат операции XOR напрямую, LLM сгенерировала его методом кусочно-линейной аппроксимации с использованием LUT (таблиц поиска).
XOR Neural Network Test Results ================================ Input: (0.000, 0.000) -> Output: 1.000 (Expected: 0.000) - FAIL [13 cycles] Input: (0.000, 1.000) -> Output: 1.000 (Expected: 1.000) - PASS [13 cycles] Input: (1.000, 0.000) -> Output: 1.000 (Expected: 1.000) - PASS [13 cycles] Input: (1.000, 1.000) -> Output: 1.000 (Expected: 0.000) - FAIL [13 cycles]
Обучение модели
С помощью языка Python и пакета NumPy я обучил простую модель, чтобы получить значения вес—смещение для нейронной сети XOR. Затем они были помещены в код Verilog, чтобы нейросеть могла правильно подбирать значения XOR.
Epoch 0, Error: 0.4808 Epoch 1000, Error: 0.0653 ... Epoch 9000, Error: 0.0158 Trained weights in fixed-point format: // Hidden weights: w_hidden[0] = 16'h00DE; // 0.870 w_hidden[1] = 16'hFFAB; // -0.335 ... w_hidden[15] = 16'hFE8D; // -1.451 // Hidden biases: b_hidden[0] = 16'hFFFC; // -0.018 b_hidden[1] = 16'h0058; // 0.344 ... b_hidden[7] = 16'h009D; // 0.615 // Output weights: w_output[0] = 16'hFF97; // -0.412 w_output[1] = 16'h015B; // 1.358 ... w_output[7] = 16'hFDCF; // -2.194 // Output bias: b_output = 16'h01EE; // 1.933 Testing: Input: [0 0] -> Output: 0.011 (Expected: 0) Input: [0 1] -> Output: 0.984 (Expected: 1) Input: [1 0] -> Output: 0.985 (Expected: 1) Input: [1 1] -> Output: 0.017 (Expected: 0)
Quartus Prime
Quartus Prime — это ПО разработки и программирования ПЛИС. В нем имеются инструменты для логического синтеза, компоновки, маршрутизации и моделирования. Бесплатная версия Lite хорошо подходит для этого небольшого проекта.

На скриншоте показана сигмоидальная функция. При работе с ПЛИС числа часто отображаются в формате вида 16’h0400, который представляет собой 16-разрядное значение, где «h» означает «шестнадцатеричное».
Конечный автомат
Приведенный ниже конечный автомат координирует операции в ПЛИС, определяя последовательность вычислений для получения результата XOR. Он переходит из одного состояния в другое, например из состояния «ожидание» в состояние «вычисление», «применение сигмоиды» и «вывод результата», обеспечивая выполнение вычислений в правильном порядке.
// State machine always @(posedge clk or posedge rst) begin if (rst) begin state <= IDLE; done <= 0; y_out <= 0; acc_idx <= 0; mult_result <= 0; // Initialize this too end else begin case (state) IDLE: begin done <= 0; acc_idx <= 0; if (start) begin state <= COMPUTE_HIDDEN; end end COMPUTE_HIDDEN: begin // ... computation logic ... if (acc_idx == HIDDEN_LAYER_SIZE) begin state <= APPLY_SIGMOID; end end APPLY_SIGMOID: begin // ... apply sigmoid function ... state <= OUTPUT_RESULT; end OUTPUT_RESULT: begin // ... output result ... state <= IDLE; end endcase end end
Совместная работа MCU и FPGA
На последнем этапе микроконтроллер SAMD21 платы Arduino должен был взаимодействовать с ПЛИС. Обратите внимание, что сигнал OUT_Y_PIN по отношению к микроконтроллеру действует как входное значение, которое задается соответствующим выходом ПЛИС:
// ... setup() function ... pinMode(INPUT_X1_PIN, OUTPUT); pinMode(INPUT_X2_PIN, OUTPUT); pinMode(START_PIN, OUTPUT); pinMode(OUT_Y_PIN, INPUT); // Start the computation on the FPGA digitalWrite(START_PIN, HIGH); delay(100); digitalWrite(START_PIN, LOW); // ... loop() function ... digitalWrite(INPUT_X1_PIN, inputPattern & 0x01); digitalWrite(INPUT_X2_PIN, (inputPattern & 0x02) >> 1); ... int xorResult = digitalRead(OUT_Y_PIN);```
Резюме
В этом пусть небольшом проекте мне удалось реализовать свое первое решение на ПЛИС. Сервис LLM Anthropic помог мне создать весь код для нейронной сети XOR, моделирования, обучения и интеграции с Arduino. Поначалу код оказался неидеален, но послужил хорошей отправной точкой. Благодаря большему количеству взаимодействий «мы» (ИИ Anthropic и я) нашли решение.
Моделирование проекта с помощью Verilator перед развертыванием помогает выявить и устранить потенциальные проблемы на ранних этапах. Хотя вентиль XOR в виде нейросети может показаться излишеством, это послужит отличным введением в ИИ и программирование ПЛИС и может открыть дверь для более сложных приложений.
Полный код, включая реализацию на Verilog и интеграцию с Arduino, доступен в моем репозитории GitHub: FPGA_XOR_NN.
ссылка на оригинал статьи https://habr.com/ru/articles/925382/
Добавить комментарий