diff --git a/components/pax-graphics b/components/pax-graphics index 84727c3..8253eb4 160000 --- a/components/pax-graphics +++ b/components/pax-graphics @@ -1 +1 @@ -Subproject commit 84727c3240d07197a4c913105ebcddbdef1743e0 +Subproject commit 8253eb498138dbae22d99d56b645a0ad567295c4 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 4b0f17e..fa53f49 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -10,6 +10,7 @@ idf_component_register( "system_wrapper.c" "wifi_connection.c" "wifi_ota.c" + "fpga_download.c" INCLUDE_DIRS "." "include" EMBED_TXTFILES ${project_dir}/server_certs/isrgrootx1.pem ) diff --git a/main/fpga_download.c b/main/fpga_download.c new file mode 100644 index 0000000..8b47df6 --- /dev/null +++ b/main/fpga_download.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "driver/uart.h" +#include "hardware.h" +#include "managed_i2c.h" +#include "pax_gfx.h" +#include "ice40.h" +#include "system_wrapper.h" +#include "graphics_wrapper.h" +#include "esp32/rom/crc.h" + +void fpga_install_uart() { + fflush(stdout); + ESP_ERROR_CHECK(uart_driver_install(0, 2048, 0, 0, NULL, 0)); + uart_config_t uart_config = { + .baud_rate = 921600, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_APB, + }; + ESP_ERROR_CHECK(uart_param_config(0, &uart_config)); +} + +void fpga_uninstall_uart() { + uart_driver_delete(0); +} + +bool fpga_read_stdin(uint8_t* buffer, uint32_t len, uint32_t timeout) { + int read = uart_read_bytes(0, buffer, len, timeout / portTICK_PERIOD_MS); + return (read == len); +} + +bool fpga_uart_sync(uint32_t* length, uint32_t* crc) { + uint8_t data[256]; + uart_read_bytes(0, data, sizeof(data), 10 / portTICK_PERIOD_MS); + char command[] = "FPGA"; + uart_write_bytes(0, command, 4); + uint8_t rx_buffer[4 * 3]; + fpga_read_stdin(rx_buffer, sizeof(rx_buffer), 1000); + if (memcmp(rx_buffer, "FPGA", 4) != 0) return false; + memcpy((uint8_t*) length, &rx_buffer[4 * 1], 4); + memcpy((uint8_t*) crc, &rx_buffer[4 * 2], 4); + return true; +} + +bool fpga_uart_load(uint8_t* buffer, uint32_t length) { + return fpga_read_stdin(buffer, length, 3000); +} + +void fpga_download(ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341) { + char message[64]; + + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0x325aa8); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Preparing..."); + ili9341_write(ili9341, pax_buffer->buf); + + fpga_install_uart(); + + ice40_disable(ice40); + ili9341_init(ili9341); + + uint8_t counter = 0; + uint32_t length = 0; + uint32_t crc = 0; + while (!fpga_uart_sync(&length, &crc)) { + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0x325aa8); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode"); + snprintf(message, sizeof(message), "Waiting for bitstream%s%s%s", (counter > 0) ? "." : " ", (counter > 1) ? "." : " ", (counter > 2) ? "." : " "); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, message); + ili9341_write(ili9341, pax_buffer->buf); + counter++; + if (counter > 3) counter = 0; + } + + while (true) { + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0x325aa8); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Receiving bitstream..."); + ili9341_write(ili9341, pax_buffer->buf); + + uint8_t* buffer = malloc(length); + if (buffer == NULL) { + free(buffer); + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xa85a32); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Malloc failed"); + ili9341_write(ili9341, pax_buffer->buf); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fpga_uninstall_uart(); + return; + } + if (!fpga_uart_load(buffer, length)) { + free(buffer); + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xa85a32); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "Timeout while loading"); + ili9341_write(ili9341, pax_buffer->buf); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fpga_uninstall_uart(); + return; + } + + uint32_t checkCrc = crc32_le(0, buffer, length); + + if (checkCrc != crc) { + free(buffer); + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xa85a32); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "CRC incorrect"); + snprintf(message, sizeof(message), "Provided CRC: %08X", crc); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*2, message); + snprintf(message, sizeof(message), "Calculated CRC: %08X", checkCrc); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*3, message); + ili9341_write(ili9341, pax_buffer->buf); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fpga_uninstall_uart(); + return; + } + + ili9341_deinit(ili9341); + ili9341_select(ili9341, false); + vTaskDelay(200 / portTICK_PERIOD_MS); + ili9341_select(ili9341, true); + + esp_err_t res = ice40_load_bitstream(ice40, buffer, length); + free(buffer); + + if (res == ESP_OK) { + while (!fpga_uart_sync(&length, &crc)) { + vTaskDelay(2 / portTICK_PERIOD_MS); + } + ice40_disable(ice40); + ili9341_init(ili9341); + } else { + ice40_disable(ice40); + ili9341_init(ili9341); + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xa85a32); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "FPGA download mode"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, "FPGA signals not done"); + ili9341_write(ili9341, pax_buffer->buf); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fpga_uninstall_uart(); + return; + } + } +} diff --git a/main/include/fpga_download.h b/main/include/fpga_download.h new file mode 100644 index 0000000..c313993 --- /dev/null +++ b/main/include/fpga_download.h @@ -0,0 +1,7 @@ +#pragma once + +#include "ice40.h" +#include "pax_gfx.h" +#include "ili9341.h" + +void fpga_download(ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341); diff --git a/main/main.c b/main/main.c index 9690662..475214c 100644 --- a/main/main.c +++ b/main/main.c @@ -40,6 +40,8 @@ #include "esp_vfs.h" #include "esp_vfs_fat.h" +#include "fpga_download.h" + static const char *TAG = "main"; typedef enum action { @@ -49,6 +51,7 @@ typedef enum action { ACTION_SETTINGS, ACTION_OTA, ACTION_FPGA, + ACTION_FPGA_DL, ACTION_RP2040_BL, ACTION_WIFI_CONNECT, ACTION_WIFI_SCAN, @@ -138,6 +141,10 @@ void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili menu_args_t* ota_args = malloc(sizeof(menu_args_t)); ota_args->action = ACTION_OTA; menu_insert_item(menu, "Firmware update", NULL, ota_args, -1); + + menu_args_t* fpga_dl_args = malloc(sizeof(menu_args_t)); + fpga_dl_args->action = ACTION_FPGA_DL; + menu_insert_item(menu, "FPGA download", NULL, fpga_dl_args, -1); menu_args_t* fpga_args = malloc(sizeof(menu_args_t)); fpga_args->action = ACTION_FPGA; @@ -742,6 +749,8 @@ void app_main(void) { } else if (menu_action == ACTION_FPGA) { graphics_task(pax_buffer, ili9341, NULL, "Loading..."); fpga_test(ili9341, ice40, rp2040->queue); + } else if (menu_action == ACTION_FPGA_DL) { + fpga_download(ice40, pax_buffer, ili9341); } else if (menu_action == ACTION_RP2040_BL) { graphics_task(pax_buffer, ili9341, NULL, "RP2040 update..."); rp2040_reboot_to_bootloader(rp2040);