diff --git a/.gitmodules b/.gitmodules index c0ffbe7..1cfe066 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "factory_test/components/sdcard"] path = factory_test/components/sdcard url = git@github.com:Nicolai-Electronics/esp32-component-sdcard.git +[submodule "factory_test/components/pax-graphics"] + path = factory_test/components/pax-graphics + url = https://github.com/robotman2412/pax-graphics.git diff --git a/factory_test/components/appfs/CMakeLists.txt b/factory_test/components/appfs/CMakeLists.txt new file mode 100644 index 0000000..9f72760 --- /dev/null +++ b/factory_test/components/appfs/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + INCLUDE_DIRS "include" + SRCS "appfs.c" + REQUIRES spi_flash +) diff --git a/factory_test/components/appfs/appfs.c b/factory_test/components/appfs/appfs.c new file mode 100644 index 0000000..76926b4 --- /dev/null +++ b/factory_test/components/appfs/appfs.c @@ -0,0 +1,781 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * Jeroen Domburg wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + +/* +Theory of operation: +An appfs filesystem is meant to store executable applications (=ESP32 programs) alongside other +data that is mmap()-able as a contiguous file. + +Appfs does that by making sure the files rigidly adhere to the 64K-page-structure (called a 'sector' +in this description) as predicated by the ESP32s MMU. This way, every file can be mmap()'ed into a +contiguous region or ran as an ESP32 application. (For the future, maybe: Smaller files can be stored +in parts of a 64K page, as long as all are contiguous and none cross any 64K boundaries. +What about fragmentation tho?) + +Because of these reasons, only a few operations are available: +- Creating a file. This needs the filesize to be known beforehand; a file cannot change size afterwards. +- Modifying a file. This follows the same rules as spi_flash_* because it maps directly to the underlying flash. +- Deleting a file +- Mmap()ping a file +This makes the interface to appfs more akin to the partition interface than to a real filesystem. + +At the moment, appfs is not yet tested with encrypted flash; compatibility is unknown. + +Filesystem meta-info is stored using the first sector: there are 2 32K half-sectors there with management +info. Each has a serial and a checksum. The sector with the highest serial and a matching checksum is +taken as current; the data will ping-pong between the sectors. (And yes, this means the pages in these +sectors will be rewritten every time a file is added/removed. Appfs is built with the assumption that +it's a mostly store-only filesystem and apps will only change every now and then. The flash chips +connected to the ESP32 chips usually can do up to 100.000 erases, so for most purposes the lifetime of +the flash with appfs on it exceeds the lifetime of the product.) + +Appfs assumes a partition of 16MiB or less, allowing for 256 128-byte sector descriptors to be stored in +the management half-sectors. The first descriptor is a header used for filesystem meta-info. + +Metainfo is stored per sector; each sector descriptor contains a zero-terminated filename (no +directories are supported, but '/' is an usable character), the size of the file and a pointer to the +next entry for the file if needed. The filename is only set for the first sector; it is all zeroes +(actually: ignored) for other entries. + +Integrity of the meta-info is guaranteed: the file system will never be in a state where sectors are +lost or anything. Integrity of data is *NOT* guaranteed: on power loss, data may be half-written, +contain sectors with only 0xff, and so on. It's up to the user to take care of this. However, files that +are not written to do not run the risk of getting corrupted. + +With regards to this code: it is assumed that an ESP32 will only have one appfs on flash, so everything +is implemented as a singleton. + +*/ + +#include +#include +#include +#include +#include +#include "esp32/rom/crc.h" +#include "esp32/rom/cache.h" +#include "esp_spi_flash.h" +#include "esp_partition.h" +#include "esp_log.h" +#include "esp_err.h" +#include "appfs.h" +#include "sdkconfig.h" + + +#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED +#error "Appfs will not work with SPI flash dangerous regions checking. Please use 'make menuconfig' to enable writing to dangerous regions." +#endif + +static const char *TAG = "appfs"; + + +#define APPFS_SECTOR_SZ SPI_FLASH_MMU_PAGE_SIZE +#define APPFS_META_SZ (APPFS_SECTOR_SZ/2) +#define APPFS_META_CNT 2 +#define APPFS_META_DESC_SZ 128 +#define APPFS_PAGES 255 +#define APPFS_MAGIC "AppFsDsc" + +#define APPFS_USE_FREE 0xff //No file allocated here +#define APPFS_ILLEGAL 0x55 //Sector cannot be used (usually because it's outside the partition) +#define APPFS_USE_DATA 0 //Sector is in use for data + +typedef struct __attribute__ ((__packed__)) { + uint8_t magic[8]; //must be AppFsDsc + uint32_t serial; + uint32_t crc32; + uint8_t reserved[128-16]; +} AppfsHeader; + +typedef struct __attribute__ ((__packed__)) { + char name[112]; //Only set for 1st sector of file. Rest has name set to 0xFF 0xFF ... + uint32_t size; //in bytes + uint8_t next; //next page containing the next 64K of the file; 0 if no next page (Because allocation always starts at 0 and pages can't refer to a lower page, 0 can never occur normally) + uint8_t used; //one of APPFS_USE_* + uint8_t reserved[10]; +} AppfsPageInfo; + +typedef struct __attribute__ ((__packed__)) { + AppfsHeader hdr; + AppfsPageInfo page[APPFS_PAGES]; +} AppfsMeta; + +static int appfsActiveMeta=0; //number of currently active metadata half-sector (0 or 1) +static const AppfsMeta *appfsMeta=NULL; //mmap'ed flash +#ifndef BOOTLOADER_BUILD +static const esp_partition_t *appfsPart=NULL; +static spi_flash_mmap_handle_t appfsMetaMmapHandle; +#else +static uint32_t appfsPartOffset=0; +#endif + + +static int page_in_part(int page) { +#ifndef BOOTLOADER_BUILD + return ((page+1)*APPFS_SECTOR_SZ < appfsPart->size); +#else + return 1; +#endif +} + +//Find active meta half-sector. Updates appfsActiveMeta to the most current one and returns ESP_OK success. +//Returns ESP_ERR_NOT_FOUND when no active metasector is found. +static esp_err_t findActiveMeta() { + int validSec=0; //bitmap of valid sectors + uint32_t serial[APPFS_META_CNT]={0}; + AppfsHeader hdr; + for (int sec=0; secserial[best]) best=sec; + } + } + + ESP_LOGI(TAG, "Meta page 0: %svalid (serial %d)", (validSec&1)?"":"in", serial[0]); + ESP_LOGI(TAG, "Meta page 1: %svalid (serial %d)", (validSec&2)?"":"in", serial[1]); + + //'best' here is either still -1 (no valid sector found) or the sector with the highest valid serial. + if (best==-1) { + ESP_LOGI(TAG, "No valid page found."); + //Eek! Nothing found! + return ESP_ERR_NOT_FOUND; + } else { + ESP_LOGI(TAG, "Using page %d as current.", best); + } + appfsActiveMeta=best; + return ESP_OK; +} + + +static int appfsGetFirstPageFor(const char *filename) { + for (int j=0; j=APPFS_PAGES) return false; + if (appfsMeta[appfsActiveMeta].page[(int)fd].used!=APPFS_USE_DATA) return false; + if (appfsMeta[appfsActiveMeta].page[(int)fd].name[0]==0xff) return false; + return true; +} + +int appfsExists(const char *filename) { + return (appfsGetFirstPageFor(filename)==-1)?0:1; +} + +appfs_handle_t appfsOpen(const char *filename) { + return appfsGetFirstPageFor(filename); +} + +void appfsClose(appfs_handle_t handle) { + //Not needed in this implementation. Added for possible later use (concurrency?) +} + +void appfsEntryInfo(appfs_handle_t fd, const char **name, int *size) { + if (name) *name=appfsMeta[appfsActiveMeta].page[fd].name; + if (size) *size=appfsMeta[appfsActiveMeta].page[fd].size; +} + +appfs_handle_t appfsNextEntry(appfs_handle_t fd) { + if (fd==APPFS_INVALID_FD) { + fd=0; + } else { + fd++; + } + + if (fd>=APPFS_PAGES || fd<0) return APPFS_INVALID_FD; + + while (appfsMeta[appfsActiveMeta].page[fd].used!=APPFS_USE_DATA || appfsMeta[appfsActiveMeta].page[fd].name[0]==0xff) { + fd++; + if (fd>=APPFS_PAGES) return APPFS_INVALID_FD; + } + + return fd; +} + +size_t appfsGetFreeMem() { + size_t ret=0; + for (int i=0; i50) { + ESP_LOGE(TAG, "appfsBlMmap: file too big to mmap"); + return NULL; + } + + //Okay, we have our info. + bootloader_munmap(appfsMeta); + //Bootloader_mmap only allows mapping of one consecutive memory range. We need more than that, so we essentially + //replicate the function here. + + Cache_Read_Disable(0); + Cache_Flush(0); + for (int i=0; icrc32=0; + uint32_t crc=0; + crc=crc32_le(crc, (const uint8_t *)hdr, APPFS_META_DESC_SZ); + for (int j=0; jcrc32=crc; + return esp_partition_write(appfsPart, metaNo*APPFS_META_SZ, hdr, sizeof(AppfsHeader)); +} + +//Kill all existing filesystem metadata and re-initialize the fs. +static esp_err_t initializeFs() { + esp_err_t r; + //Kill management sector + r=esp_partition_erase_range(appfsPart, 0, APPFS_SECTOR_SZ); + if (r!=ESP_OK) return r; + //All the data pages are now set to 'free'. Add a header that makes the entire mess valid. + AppfsHeader hdr; + memset(&hdr, 0xff, sizeof(hdr)); + memcpy(hdr.magic, APPFS_MAGIC, 8); + hdr.serial=0; + //Mark pages outside of partition as invalid. + int lastPage=(appfsPart->size/APPFS_SECTOR_SZ); + for (int j=lastPage; j0) { + //Eek! Can't allocate enough space! + ESP_LOGD(TAG, "Not enough free space!"); + return ESP_ERR_NO_MEM; + } + + //Re-write a new meta page but with file allocated + int newMeta=(appfsActiveMeta+1)%APPFS_META_CNT; + ESP_LOGD(TAG, "Re-writing meta data to meta page %d...", newMeta); + r=esp_partition_erase_range(appfsPart, newMeta*APPFS_META_SZ, APPFS_META_SZ); + if (r!=ESP_OK) return r; + //Prepare header + AppfsHeader hdr; + memcpy(&hdr, &appfsMeta[appfsActiveMeta].hdr, sizeof(hdr)); + hdr.serial++; + hdr.crc32=0; + for (int j=0; jaddress/SPI_FLASH_MMU_PAGE_SIZE)+1; + while (offset >= APPFS_SECTOR_SZ) { + page=appfsMeta[appfsActiveMeta].page[page].next; + offset-=APPFS_SECTOR_SZ; + ESP_LOGD(TAG, "Skipping a page (to page %d), remaining offset 0x%X", page, offset); + } + + int *pages=alloca(sizeof(int)*((len/APPFS_SECTOR_SZ)+1)); + int nopages=0; + size_t mappedlen=0; + while(len>mappedlen) { + pages[nopages++]=page+dataStartPage; + ESP_LOGD(TAG, "Mapping page %d (part offset %d).", page, dataStartPage); + page=appfsMeta[appfsActiveMeta].page[page].next; + mappedlen+=APPFS_SECTOR_SZ; + } + + r=spi_flash_mmap_pages(pages, nopages, memory, out_ptr, out_handle); + if (r!=ESP_OK) { + ESP_LOGD(TAG, "Can't map file: pi_flash_mmap_pages returned %d\n", r); + return r; + } + *out_ptr=((uint8_t*)*out_ptr)+offset; + return ESP_OK; +} + +void appfsMunmap(spi_flash_mmap_handle_t handle) { + spi_flash_munmap(handle); +} + +//Just mmaps and memcpys the data. Maybe not the fastest ever, but hey, if you want that you should mmap +//and read from the flash cache memory area yourself. +esp_err_t appfsRead(appfs_handle_t fd, size_t start, void *buf, size_t len) { + const void *flash; + spi_flash_mmap_handle_t handle; + esp_err_t r=appfsMmap(fd, start, len, &flash, SPI_FLASH_MMAP_DATA, &handle); + if (r!=ESP_OK) return r; + memcpy(buf, flash, len); + spi_flash_munmap(handle); + return ESP_OK; +} + + +esp_err_t appfsErase(appfs_handle_t fd, size_t start, size_t len) { + esp_err_t r; + int page=(int)fd; + if (!appfsFdValid(page)) return ESP_ERR_NOT_FOUND; + //Bail out if trying to erase past the file end. + //Allow erases past the end of the file but still within the page reserved for the file. + int roundedSize=(appfsMeta[appfsActiveMeta].page[page].size+(APPFS_SECTOR_SZ-1))&(~(APPFS_SECTOR_SZ-1)); + if (roundedSize < (start+len)) { + return ESP_ERR_INVALID_SIZE; + } + + //Find initial page + while (start >= APPFS_SECTOR_SZ) { + page=appfsMeta[appfsActiveMeta].page[page].next; + start-=APPFS_SECTOR_SZ; + } + //Page now is the initial page. Start is the offset into the page we need to start at. + + while (len>0) { + size_t size=len; + //Make sure we do not go over a page boundary + if ((size+start)>APPFS_SECTOR_SZ) size=APPFS_SECTOR_SZ-start; + ESP_LOGD(TAG, "Erasing page %d offset 0x%X size 0x%X", page, start, size); + r=esp_partition_erase_range(appfsPart, (page+1)*APPFS_SECTOR_SZ+start, size); + if (r!=ESP_OK) return r; + page=appfsMeta[appfsActiveMeta].page[page].next; + len-=size; + start=0; //offset is not needed anymore + } + return ESP_OK; +} + +esp_err_t appfsWrite(appfs_handle_t fd, size_t start, uint8_t *buf, size_t len) { + esp_err_t r; + int page=(int)fd; + if (!appfsFdValid(page)) return ESP_ERR_NOT_FOUND; + if (appfsMeta[appfsActiveMeta].page[page].size < (start+len)) { + return ESP_ERR_INVALID_SIZE; + } + + while (start > APPFS_SECTOR_SZ) { + page=appfsMeta[appfsActiveMeta].page[page].next; + start-=APPFS_SECTOR_SZ; + } + while (len>0) { + size_t size=len; + if (size+start>APPFS_SECTOR_SZ) size=APPFS_SECTOR_SZ-start; + ESP_LOGD(TAG, "Writing to page %d offset %d size %d", page, start, size); + r=esp_partition_write(appfsPart, (page+1)*APPFS_SECTOR_SZ+start, buf, size); + if (r!=ESP_OK) return r; + page=appfsMeta[appfsActiveMeta].page[page].next; + len-=size; + buf+=size; + start=0; + } + return ESP_OK; +} + +void appfsDump() { + printf("AppFsDump: ..=free XX=illegal no=next page\n"); + for (int i=0; i<16; i++) printf("%02X-", i); + printf("\n"); + for (int i=0; iaddress; + + int page=(phys_offs/APPFS_SECTOR_SZ)-1; + if (page<0 || page>=APPFS_PAGES) { + return ESP_ERR_NOT_FOUND; + } + + //Find first sector for this page. + int tries=APPFS_PAGES; //make sure this loop always exits + while (appfsMeta[appfsActiveMeta].page[page].name[0]==0xff) { + int i; + for (i=0; i=APPFS_PAGES) return ESP_ERR_NOT_FOUND; + tries--; + } + + //Okay, found! + *ret_app=page; + return ESP_OK; +} + + + +#endif diff --git a/factory_test/components/appfs/component.mk b/factory_test/components/appfs/component.mk new file mode 100644 index 0000000..ed40742 --- /dev/null +++ b/factory_test/components/appfs/component.mk @@ -0,0 +1,9 @@ +# +# Component Makefile. By default, +# this will take the sources in this directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# + +COMPONENT_ADD_INCLUDEDIRS := include + diff --git a/factory_test/components/appfs/include/appfs.h b/factory_test/components/appfs/include/appfs.h new file mode 100644 index 0000000..0a79c6e --- /dev/null +++ b/factory_test/components/appfs/include/appfs.h @@ -0,0 +1,289 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * Jeroen Domburg wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + +#pragma once + +#include "esp_err.h" +#include +#include "esp_spi_flash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define APPFS_PART_TYPE 0x43 /*x = rotation.x; - rotation_offset->y = rotation.y; - rotation_offset->z = rotation.z; + /*if (calibrate) { + rotation_offset.x = rotation.x; + rotation_offset.y = rotation.y; + rotation_offset.z = rotation.z; calibrate = false; } - rotation.x -= rotation_offset->x; - rotation.y -= rotation_offset->y; - rotation.z -= rotation_offset->z; + rotation.x -= rotation_offset.x; + rotation.y -= rotation_offset.y; + rotation.z -= rotation_offset.z; + + if (rotation.x < 0) rotation.x = 360.0 - rotation.x; + if (rotation.y < 0) rotation.y = 360.0 - rotation.y; + if (rotation.z < 0) rotation.z = 360.0 - rotation.z;*/ /*printf("\n\n"); printf("Acceleration (m/s²) x = %5.8f y = %5.8f z = %5.8f\n", acceleration.x, acceleration.y, acceleration.z); @@ -182,36 +192,36 @@ void bno055_task(BNO055* bno055, bno055_vector_t* rotation_offset) { } } -void app_main(void) { - esp_err_t res; +esp_err_t graphics_task(pax_buf_t* buffer, ILI9341* ili9341, uint8_t* framebuffer) { + uint64_t millis = esp_timer_get_time() / 1000; + pax_background(buffer, 0x000000); + pax_col_t color0 = pax_col_hsv(millis * 255 / 8000, 255, 255); + pax_col_t color1 = pax_col_hsv(millis * 255 / 8000 + 127, 255, 255); + float a0 = 0;//millis / 3000.0 * M_PI; + printf("a0 = %f (%f)\r\n", a0, rotation.y); + float a1 = rotation.y * (M_PI / 180.0);//fmodf(a0, M_PI * 4) - M_PI * 2; + pax_draw_arc(buffer, color0, 0, 0, 1, a0, a0 + a1); + pax_push_2d(buffer); - res = hardware_init(); - if (res != ESP_OK) { - printf("Failed to initialize hardware!\n"); - restart(); - } + pax_apply_2d(buffer, matrix_2d_rotate(a0)); + pax_push_2d(buffer); + pax_apply_2d(buffer, matrix_2d_translate(1, 0)); + pax_draw_rect(buffer, color1, -0.25, -0.25, 0.5, 0.5); + pax_pop_2d(buffer); - ili9341 = get_ili9341(); - ice40 = get_ice40(); - - // LCD test - /*uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT); - if (framebuffer == NULL) { - ESP_LOGE(TAG, "Failed to allocate framebuffer"); - restart(); - } - - memset(framebuffer, 0, ILI9341_BUFFER_SIZE); // Clear framebuffer - res = ili9341_write(ili9341, framebuffer); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to write framebuffer to LCD"); - restart(); - } + pax_apply_2d(buffer, matrix_2d_rotate(a1)); + pax_push_2d(buffer); + pax_apply_2d(buffer, matrix_2d_translate(1, 0)); + pax_apply_2d(buffer, matrix_2d_rotate(-a0 - a1 + M_PI * 0.5)); + pax_draw_tri(buffer, color1, 0.25, 0, -0.125, 0.2165, -0.125, -0.2165); + pax_pop_2d(buffer); - free(framebuffer); - */ + pax_pop_2d(buffer); - /* Print chip information */ + return ili9341_write(ili9341, framebuffer); +} + +void print_chip_info(void) { esp_chip_info_t chip_info; esp_chip_info(&chip_info); printf("This is %s chip with %d CPU core(s), WiFi%s%s, ", @@ -225,19 +235,203 @@ void app_main(void) { printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); - printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size()); + printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size()); +} - // Initialize the buttons +uint8_t* load_file_to_ram(FILE* fd, uint32_t* fsize) { + fseek(fd, 0, SEEK_END); + *fsize = ftell(fd); + fseek(fd, 0, SEEK_SET); + uint8_t* file = malloc(*fsize); + fread(file, *fsize, 1, fd); + return file; +} + +esp_err_t load_file_into_psram(FILE* fd) { + fseek(fd, 0, SEEK_SET); + const uint8_t write_cmd = 0x02; + uint32_t amount_read; + uint32_t position = 0; + uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (tx_buffer == NULL) return ESP_FAIL; + + while(1) { + tx_buffer[0] = write_cmd; + tx_buffer[1] = (position >> 16); + tx_buffer[2] = (position >> 8) & 0xFF; + tx_buffer[3] = position & 0xFF; + amount_read = fread(&tx_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd); + if (amount_read < 1) break; + ESP_LOGI(TAG, "Writing PSRAM @ %u (%u bytes)", position, amount_read); + esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, NULL, 0); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Write transaction failed @ %u", position); + free(tx_buffer); + return res; + } + position += amount_read; + }; + free(tx_buffer); + return ESP_OK; +} + +esp_err_t verify_file_in_psram(FILE* fd) { + fseek(fd, 0, SEEK_SET); + const uint8_t read_cmd = 0x03; + uint32_t amount_read; + uint32_t position = 0; + uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (tx_buffer == NULL) return ESP_FAIL; + memset(tx_buffer, 0, SPI_MAX_TRANSFER_SIZE); + uint8_t* verify_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (verify_buffer == NULL) return ESP_FAIL; + uint8_t* rx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (rx_buffer == NULL) return ESP_FAIL; + + while(1) { + tx_buffer[0] = read_cmd; + tx_buffer[1] = (position >> 16); + tx_buffer[2] = (position >> 8) & 0xFF; + tx_buffer[3] = position & 0xFF; + amount_read = fread(&verify_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd); + if (amount_read < 1) break; + ESP_LOGI(TAG, "Reading PSRAM @ %u (%u bytes)", position, amount_read); + esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, rx_buffer, amount_read + 4); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Read transaction failed @ %u", position); + free(tx_buffer); + return res; + } + position += amount_read; + ESP_LOGI(TAG, "Verifying PSRAM @ %u (%u bytes)", position, amount_read); + for (uint32_t i = 4; i < amount_read; i++) { + if (rx_buffer[i] != verify_buffer[i]) { + ESP_LOGE(TAG, "Verifying PSRAM @ %u failed: %02X != %02X", position + i, rx_buffer[i], verify_buffer[i]); + free(tx_buffer); + free(rx_buffer); + free(verify_buffer); + return ESP_FAIL; + } + } + }; + free(tx_buffer); + free(rx_buffer); + free(verify_buffer); + ESP_LOGI(TAG, "PSRAM contents verified!"); + return ESP_OK; +} + +void fpga_test(void) { + esp_err_t res = mount_sd(SD_CMD, SD_CLK, SD_D0, SD_PWR, "/sd", false, 5); + if (res == ESP_OK) { + ESP_LOGI(TAG, "SD card mounted"); + FILE* fpga_passthrough = fopen("/sd/passthrough.bin", "rb"); + if (fpga_passthrough == NULL) { + ESP_LOGE(TAG, "Failed to open passthrough.bin (%d)", res); + return; + } + + ESP_LOGI(TAG, "Loading passthrough bitstream into RAM buffer..."); + uint32_t fpga_passthrough_bitstream_length; + uint8_t* fpga_passthrough_bitstream = load_file_to_ram(fpga_passthrough, &fpga_passthrough_bitstream_length); + fclose(fpga_passthrough); + ESP_LOGI(TAG, "Loading passthrough bitstream into FPGA..."); + res = ice40_load_bitstream(ice40, fpga_passthrough_bitstream, fpga_passthrough_bitstream_length); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Failed to load passthrough bitstream into FPGA (%d)", res); + return; + } + free(fpga_passthrough_bitstream); + + FILE* ram_contents = fopen("/sd/ram.bin", "rb"); + if (ram_contents == NULL) { + ESP_LOGE(TAG, "Failed to open ram.bin"); + return; + } + + res = load_file_into_psram(ram_contents); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Failed to load RAM contents into PSRAM (%d)", res); + fclose(ram_contents); + return; + } + + res = verify_file_in_psram(ram_contents); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify PSRAM contents (%d)", res); + fclose(ram_contents); + return; + } + + FILE* fpga_app = fopen("/sd/app.bin", "rb"); + if (fpga_app == NULL) { + ESP_LOGE(TAG, "Failed to open app.bin"); + return; + } + + ESP_LOGI(TAG, "Loading app bitstream into RAM buffer..."); + uint32_t fpga_app_bitstream_length; + uint8_t* fpga_app_bitstream = load_file_to_ram(fpga_app, &fpga_app_bitstream_length); + fclose(fpga_app); + ESP_LOGI(TAG, "Loading app bitstream into FPGA..."); + res = ice40_load_bitstream(ice40, fpga_app_bitstream, fpga_app_bitstream_length); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Failed to load app bitstream into FPGA (%d)", res); + return; + } + free(fpga_app_bitstream); + } else { + ESP_LOGI(TAG, "No SD card, skipping FPGA test"); + } +} + +void appfs_test(void) { + esp_err_t res = appfsInit(APPFS_PART_TYPE, APPFS_PART_SUBTYPE); + if (res != ESP_OK) { + ESP_LOGE(TAG, "AppFS init failed: %d", res); + return; + } + ESP_LOGI(TAG, "AppFS initialized"); +} + +void app_main(void) { + esp_err_t res; + + res = hardware_init(); + if (res != ESP_OK) { + printf("Failed to initialize hardware!\n"); + restart(); + } + + ili9341 = get_ili9341(); + ice40 = get_ice40(); + bno055 = get_bno055(); + + print_chip_info(); + + uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT); + if (framebuffer == NULL) { + ESP_LOGE(TAG, "Failed to allocate framebuffer"); + restart(); + } + memset(framebuffer, 0, ILI9341_BUFFER_SIZE); + + pax_buf_t buffer; + pax_buf_init(&buffer, framebuffer, ILI9341_WIDTH, ILI9341_HEIGHT, PAX_BUF_16_565RGB); + pax_apply_2d(&buffer, matrix_2d_translate(buffer.width / 2.0, buffer.height / 2.0)); + pax_apply_2d(&buffer, matrix_2d_scale(50, 50)); + button_init(); - // Test for connection to RP2040 and to the BNO055 over I2C - /*BNO055* bno055 = get_bno055(); - - bno055_vector_t rotation_offset; - rotation_offset.x = 0; - rotation_offset.y = 0; - rotation_offset.z = 0; + //appfs_test(); + fpga_test(); + + /*while (1) { + bno055_task(bno055); + graphics_task(&buffer, ili9341, framebuffer); + }*/ + /* uint8_t data_out, data_in; enum { @@ -274,9 +468,8 @@ void app_main(void) { } else { printf("GPIO status: %02x\n", data_in); } - bno055_task(bno055, &rotation_offset); vTaskDelay(1000 / portTICK_PERIOD_MS); - }*/ + } // FPGA RAM passthrough test @@ -355,5 +548,7 @@ void app_main(void) { int64_t rx_done_time = esp_timer_get_time(); printf("Read took %lld microseconds\r\n", rx_done_time - rx_start_time); result = (((size_of_ram) / (rx_done_time - rx_start_time))*1000*1000)/1024; - printf("%u bytes in %lld microseconds = %llu kB/s\r\n", size_of_ram, rx_done_time - rx_start_time, result); + printf("%u bytes in %lld microseconds = %llu kB/s\r\n", size_of_ram, rx_done_time - rx_start_time, result);*/ + + free(framebuffer); } diff --git a/factory_test/partitions.csv b/factory_test/partitions.csv index 484da04..775fc73 100644 --- a/factory_test/partitions.csv +++ b/factory_test/partitions.csv @@ -1,5 +1,8 @@ # ESP-IDF Partition Table # Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, +nvs, data, nvs, 0x9000, 0x4000, +otadata, data, ota, 0xd000, 0x2000 phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 1M, \ No newline at end of file +ota_0, app, ota_0, 0x10000, 1536K +ota_1, app, ota_1, , 1536K +appfs, 0x43, 3, , 13248K diff --git a/factory_test/sdkconfig b/factory_test/sdkconfig index 8c26f10..bad2fec 100644 --- a/factory_test/sdkconfig +++ b/factory_test/sdkconfig @@ -231,10 +231,10 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_FLASHFREQ="80m" # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="16MB" CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y CONFIG_ESPTOOLPY_BEFORE_RESET=y # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set