diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c572b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +.pio diff --git a/examples/Brain/program_esp32_cdc/esp_binaries.h b/examples/Brain/program_esp32_cdc/esp_binaries.h index d689460..b99f880 100644 --- a/examples/Brain/program_esp32_cdc/esp_binaries.h +++ b/examples/Brain/program_esp32_cdc/esp_binaries.h @@ -26,7 +26,7 @@ #define ESP_BINARIES_H_ // Configuration: select which bins to flash -#define BIN_FILES BIN_FEATHER_ESP32_V2_BLINKY +#define BIN_FILES BIN_DEVKIT_S3 //--------------------------------------------------------------------+ // LIST OF BINARIES diff --git a/examples/Brain/program_esp32_cdc/program_esp32_cdc.ino b/examples/Brain/program_esp32_cdc/program_esp32_cdc.ino index e2ad416..d0bf16e 100644 --- a/examples/Brain/program_esp32_cdc/program_esp32_cdc.ino +++ b/examples/Brain/program_esp32_cdc/program_esp32_cdc.ino @@ -21,6 +21,10 @@ // bin_files[] is defined accordingly #include "esp_binaries.h" +// 1 if programming ESP32-S2/S3 via native USB +// 0 if programming ESP32/8266 via USB-to-UART chip such as FTDI/CP210x/CH9102f +#define ESP32_NATIVE_USB 1 + #define ESP32_RESET 27 #define ESP32_IO0 28 @@ -31,14 +35,21 @@ // CDC Host object Adafruit_USBH_CDC SerialHost; +#if ESP32_NATIVE_USB + +// Declare BootROM with IO0 and Reset will use GPIO for bootloader reset +// This is typically for programming ESP32-S2/S3 via native USB +ESP32BootROMClass ESP32BootROM(SerialHost, ESP32_IO0, ESP32_RESET); + +#else + // Defined an boot rom object that use SerialHost // Declare BootROM without IO0 and Reset will use SerialHost.setDtrRts() for bootloader reset // This is for programming ESP32/8266 via USB-to-UART chip such as FTDI/CP210x/CH9102f ESP32BootROMClass ESP32BootROM(SerialHost); -// Declare BootROM with IO0 and Reset will use GPIO for bootloader reset -// This is typically for programming ESP32-S2/S3 via native USB -// ESP32BootROMClass ESP32BootROM(SerialHost, ESP32_IO0, ESP32_RESET); +#endif + //--------------------------------------------------------------------+ // Setup and Loop on Core0 @@ -117,6 +128,19 @@ void setup1() { // core1's loop: process usb host task on core1 void loop1() { + if ( Brain.esp32_s3_inReset() ){ + // Note: S3 has an USB-OTG errata + // https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf + // which is walkarounded by idf/arduino-esp32 to always mux JTAG to USB for + // uploading and/or power on. Afterwards USB-OTG will be set up if selected + // so. However rp2040 USBH is running too fast and can actually retrieve + // device/configuration descriptor of JTAG before the OTG is fully setup. + // We delay a bit here + delay(500); + + Brain.esp32_s3_clearReset(); + } + Brain.USBHost.task(); // periodically flush SerialHost if connected diff --git a/examples/Brain/program_esp32_uart_uncompressed/program_esp32_uart_uncompressed.ino b/examples/Brain/program_esp32_uart_uncompressed/program_esp32_uart_uncompressed.ino deleted file mode 100644 index 985ef02..0000000 --- a/examples/Brain/program_esp32_uart_uncompressed/program_esp32_uart_uncompressed.ino +++ /dev/null @@ -1,128 +0,0 @@ -// This sketch program ESP32 by flashing bin file from SD Card -// Hardware wiring: -// - Brain GPIO28 <-> ESP32 IO0 -// - Brain Reset <-> ESP32 Enable -// - Brain TX/RX <-> ESP32 RX/TX - -// required for Host MSC block device -#include "SdFat.h" - -// required for USB host -#include "pio_usb.h" -#include "Adafruit_TinyUSB.h" - -#include "Adafruit_TestBed_Brains.h" - -#define ESP32_RESET 27 -#define ESP32_IO0 28 - -#define ESP32_BAUDRATE 2000000 -//#define ESP32_BAUDRATE 921600 -//#define ESP32_BAUDRATE 115200 - -// Bin files on SDCard to program -// These files can be generated by any of Arduino sketches -// The example use WiFiAccessPoint sketch, which we can verify its running image -// by simply scan for existence of ssid "YourAP" -struct { - uint32_t addr; - const char* fpath; -} bin_list [] = { - { 0x1000 , "esp32/WiFiAccessPoint/WiFiAccessPoint.ino.bootloader.bin" }, - { 0x8000 , "esp32/WiFiAccessPoint/WiFiAccessPoint.ino.partitions.bin" }, - { 0xe000 , "esp32/WiFiAccessPoint/boot_app0.bin" }, - { 0x10000 , "esp32/WiFiAccessPoint/WiFiAccessPoint.ino.bin" }, -}; - -enum { - BIN_LIST_COUNT = sizeof(bin_list)/sizeof(bin_list[0]) -}; - - -// Defined an boot rom object that use UART Serial1 -ESP32BootROMClass ESP32BootROM(Serial1, ESP32_IO0, ESP32_RESET); - -//--------------------------------------------------------------------+ -// Setup and Loop on Core0 -//--------------------------------------------------------------------+ - -void prepare_sd(void) { - if (!Brain.SD_detected()) { - Brain.LCD_printf(0, "No SD Card"); - while ( !Brain.SD_detected() ) delay(10); - } - - if ( !Brain.SD_begin(SD_SCK_MHZ(16)) ) { - Brain.LCD_printf(0, "SD init failed"); - while(1) delay(10); - } - - Brain.LCD_printf(0, "SD mounted"); - - // Print out file on SD if Serial is connected - if (Serial) { - Serial.println(); - Serial.println("SD Contents:"); - Serial.printf("Card size = %0.1f GB\n", 0.000000512 * Brain.SD.card()->sectorCount()); - Brain.SD.ls(LS_R | LS_DATE | LS_SIZE); - } -} - -void print_speed(size_t count, uint32_t ms) { - Brain.LCD_printf(0, "%.01fKB %.01fs", count/1000.0F, ms / 1000.0F); - - Serial.printf("Completed %u bytes in %.02f seconds.\r\n", count, ms / 1000.0F); - Serial.printf("Speed : %.02f KB/s\r\n", (count / 1000.0F) / (ms / 1000.0F)); -} - -void setup() { - Serial.begin(115200); - while (!Serial) delay(10); - Serial.println("Tester Brains: Programming ESP32 with UART!"); - - Brain.begin(); - - // prepare SD Card - prepare_sd(); - - while ( !Brain.esp32_begin(&ESP32BootROM, ESP32_BAUDRATE) ) { - // retry syncing - delay(100); - } - - // Writing bin files - size_t total_bytes = 0; - uint32_t ms = millis(); - for(size_t i=0; i ESP32 IO0 + * - TB D3 <-> ESP32 Enable + * - TB TX/RX <-> ESP32 RX/TX + * + * How to run this example: + * 0. Define ESP32_RESET, ESP32_IO0, SD_CS, SD_DETECT in this sketch according to your hardware setup + * 1. Generate compressed binary and its metadata (len, md5 etc..) by running: + * python3 tools/esp_compress.py --sd + * For example: python tools/esp_compress.py --sd . + * 2. .bin.gz (e.g NINA_W102-1.7.5.bin.gz) and esp_binaries.h will be generated in the same directory + * 3. Copy esp_binaries.h to this example directory + * 4. Copy .bin.gz files to SD Card within BIN_DIR (defined in this sketch) + * 5. Insert SD Card to board + * 6. Upload this sketch to board + * + * Note: for convenience, this example included generated 'NINA_W102-1.7.5.bin.gz' and 'esp_binaries.h' + * from https://github.com/adafruit/nina-fw/releases/tag/1.7.5 in this example directory. + */ + +#include "SdFat.h" + +// #include "Adafruit_TinyUSB.h" +#include "Adafruit_TestBed.h" + +//--------------------------------------------------------------------+ +// Hardware Configuration +//--------------------------------------------------------------------+ + +// These are defined in airlift-capable board such PyPortal, PyBadge, etc. +#if defined(ESP32_GPIO0) && defined(ESP32_RESETN) + #define ESP32_RESET ESP32_RESETN + #define ESP32_IO0 ESP32_GPIO0 +#else + #define ESP32_RESET 2 + #define ESP32_IO0 3 +#endif + +#if defined(ARDUINO_PYPORTAL_M4) + #define SD_CS 32 + #define SD_DETECT 33 +#else + #define SD_CS 4 + #define SD_DETECT 5 // optional +#endif + +#define ESP32_BAUDRATE 2000000 +//#define ESP32_BAUDRATE 1500000 +//#define ESP32_BAUDRATE 921600 +//#define ESP32_BAUDRATE 115200 + +//--------------------------------------------------------------------+ +// Binaries and Path +//--------------------------------------------------------------------+ +#define BIN_DIR "/" // SD card's directory that contains bin files +#include "esp_binaries.h" + +struct { + uint32_t addr; + esp32_zipfile_t const *zfile; +} bin_files[] = { + {0x00000, &NINA_W102_1_7_5} +}; + +enum { BIN_FILES_COUNT = sizeof(bin_files) / sizeof(bin_files[0]) }; + +//--------------------------------------------------------------------+ +// Defined an boot rom object that use UART Serial1 +ESP32BootROMClass ESP32BootROM(Serial1, ESP32_IO0, ESP32_RESET); + +SdFat SD; +File32 fbin; + +//--------------------------------------------------------------------+ +// Implementation +//--------------------------------------------------------------------+ + +void print_speed(size_t count, uint32_t ms) { + float count_k = count / 1000.0F; + float sec = ms / 1000.0F; + float speed = count_k / sec; + + Serial.print(count_k); Serial.print("KB "); Serial.print(sec); Serial.println("s"); + Serial.print("Spd: "); Serial.print(speed); Serial.println(" KB/s"); +} + +void setup() { + Serial.begin(115200); + while (!Serial) delay(10); + Serial.println("TestBed: Programming ESP32 with UART!"); + +#ifdef SD_DETECT + pinMode(SD_DETECT, INPUT_PULLUP); + if (digitalRead(SD_DETECT) == LOW) { + Serial.println("SD Card not inserted"); + while (1) delay(10); + } +#endif + + if (!SD.begin(SD_CS, SD_SCK_MHZ(12))) { + Serial.println("SD Card init failed"); + while (1) delay(10); + } + Serial.println("SD Card init OK"); + + TB.begin(); + while ( !TB.esp32_begin(&ESP32BootROM, ESP32_BAUDRATE) ) { + // retry syncing + delay(100); + } + + // Writing bin files + size_t total_bytes = 0; + uint32_t ms = millis(); + for(size_t i=0; iname); + + char bin_path[128] = BIN_DIR; + strcat(bin_path, bin_files[i].zfile->name); + + fbin.open(&SD, bin_path); + if (!fbin) { + Serial.printf("Failed to open file %s\r\n", bin_files[i].zfile->name); + continue; + } + + size_t wr_count = TB.esp32_programFlashDefl(bin_files[i].zfile, bin_files[i].addr, &fbin); + total_bytes += wr_count; + if (!wr_count) { + Serial.printf("Failed to flash"); + } + + fbin.close(); + } + print_speed(total_bytes, millis() - ms); + + TB.esp32_end(); + + // reset ESP32 to run new firmware + TB.targetReset(); +} +void loop() { +} diff --git a/library.properties b/library.properties index 4ec23fe..871fc7b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Adafruit TestBed -version=1.10.0 +version=1.11.0 author=Adafruit maintainer=Adafruit sentence=Adafruit's internal test bed code library diff --git a/src/Adafruit_TestBed.cpp b/src/Adafruit_TestBed.cpp index 45567f0..23585e2 100644 --- a/src/Adafruit_TestBed.cpp +++ b/src/Adafruit_TestBed.cpp @@ -1,10 +1,19 @@ #include "Adafruit_TestBed.h" +static inline uint32_t div_ceil(uint32_t v, uint32_t d) { + return (v + d - 1) / d; +} + Adafruit_TestBed::Adafruit_TestBed(void) { #if defined(ADAFRUIT_METRO_M0_EXPRESS) neopixelPin = 40; neopixelNum = 1; #endif + + esp32boot = NULL; + _esp32_flash_defl = false; + _esp32_chip_detect = 0; + _esp32s3_in_reset = false; } /**************************************************************************/ @@ -33,6 +42,11 @@ void Adafruit_TestBed::begin(void) { digitalWrite(ledPin, LOW); } + if (targetResetPin >= 0) { + pinMode(targetResetPin, OUTPUT); + digitalWrite(targetResetPin, HIGH); + } + #if defined(__AVR__) analogRef = 5.0; #elif defined(ARDUINO_ARCH_RP2040) @@ -150,6 +164,23 @@ void Adafruit_TestBed::targetPowerCycle(uint16_t off_time) { targetPower(1); } +void Adafruit_TestBed::targetReset(uint32_t reset_ms) { + digitalWrite(targetResetPin, LOW); + delay(reset_ms); + digitalWrite(targetResetPin, HIGH); + + // Note: S3 has an USB-OTG errata + // https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf + // which is walkarounded by idf/arduino-esp32 to always mux JTAG to USB for + // uploading and/or power on. Afterwards USB-OTG will be set up if selected + // so. However rp2040 USBH is running too fast and can actually retrieve + // device/configuration descriptor of JTAG before the OTG is fully setup. + // Mark this for application usage + if (_esp32_chip_detect == CHIP_DETECT_MAGIC_ESP32S3) { + _esp32s3_in_reset = true; + } +} + /**************************************************************************/ /*! @brief Read the ADC on a pin and convert it to a voltage @@ -414,4 +445,185 @@ void Adafruit_TestBed::beepNblink(void) { #endif } +//--------------------------------------------------------------------+ +// ESP32 Target +//--------------------------------------------------------------------+ + +bool Adafruit_TestBed::esp32_begin(ESP32BootROMClass *bootrom, + uint32_t baudrate) { + esp32boot = bootrom; + + Serial.println("Syncing ESP32"); + _esp32_chip_detect = esp32boot->begin(baudrate); + + if (_esp32_chip_detect) { + setColor(0xFFFFFF); + Serial.println("Synced OK"); + return true; + } else { + Serial.println("Sync failed!"); + return false; + } +} + +void Adafruit_TestBed::esp32_end(bool reset_esp) { + if (esp32boot->isRunningStub()) { + // skip sending flash_finish to ROM loader here, + // as it causes the loader to exit and run user code + esp32boot->beginFlash(0, 0, esp32boot->getFlashWriteSize()); + + if (_esp32_flash_defl) { + esp32boot->endFlashDefl(reset_esp); + } else { + esp32boot->endFlash(reset_esp); + } + } + + esp32boot->end(); +} + +bool Adafruit_TestBed::esp32_s3_inReset(void) { return _esp32s3_in_reset; } + +void Adafruit_TestBed::esp32_s3_clearReset(void) { _esp32s3_in_reset = false; } + +static void print_buf(uint8_t const *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + Serial.print(buf[i], HEX); + } + Serial.println(); +} + +size_t +Adafruit_TestBed::_esp32_programFlashDefl_impl(const esp32_zipfile_t *zfile, + uint32_t addr, File32 *fsrc) { + if (!esp32boot) { + return 0; + } + + // Check if MD5 matches to skip this file + uint8_t esp_md5[16]; + +#if 1 // change to 0 to skip pre-flash md5 check for testing + esp32boot->md5Flash(addr, zfile->uncompressed_len, esp_md5); + Serial.print("Flash MD5: "); + print_buf(esp_md5, 16); + if (0 == memcmp(zfile->md5, esp_md5, 16)) { + Serial.println("MD5 matched"); + return zfile->uncompressed_len; + } +#endif + + // Write Size is different depending on ROM (1K) or Stub (16KB) + uint32_t const block_size = esp32boot->getFlashWriteSize(); + + uint8_t *buf = NULL; + bool const use_sdcard = (fsrc != NULL); + + if (use_sdcard) { + buf = (uint8_t *)malloc(block_size); + if (!buf) { + Serial.print("No memory "); + Serial.println(block_size); + return 0; + } + } + + Serial.print("Compressed "); + Serial.print(zfile->uncompressed_len); + Serial.print(" bytes to "); + Serial.println(zfile->compressed_len); + + if (!esp32boot->beginFlashDefl(addr, zfile->uncompressed_len, + zfile->compressed_len)) { + Serial.println("beginFlash failed!"); + if (buf) { + free(buf); + } + return 0; + } + + _esp32_flash_defl = true; + + //------------- Flashing -------------// + uint32_t written = 0; + + uint32_t const block_num = div_ceil(zfile->compressed_len, block_size); + for (uint32_t i = 0; i < block_num; i++) { + setLED(HIGH); + // Serial.print("Pckt %lu/%lu", i + 1, block_num); + Serial.print("Packet "); + Serial.print(i + 1); + Serial.print("/"); + Serial.println(block_num); + + uint32_t const remain = zfile->compressed_len - written; + uint32_t const wr_count = (remain < block_size) ? remain : block_size; + + uint8_t const *data; + if (!use_sdcard) { + // file contents is stored in internal flash (e.g rp2040) + data = zfile->data + written; + } else { + // file contents is stored in sdcard + memset(buf, 0xff, block_size); // empty it out + if (wr_count != fsrc->read(buf, wr_count)) { + Serial.println("File contents does not matched with compressed_len"); + free(buf); + return 0; + } + + data = buf; + } + + // Note: flash deflat does not need padding + if (!esp32boot->dataFlashDefl(data, wr_count)) { + Serial.println("Failed to flash"); + break; + } + + written += wr_count; + setLED(LOW); + } + Serial.println(); + + // Stub only writes each block to flash after 'ack'ing the receive, + // so do a final dummy operation which will not be 'ack'ed + // until the last block has actually been written out to flash + if (esp32boot->isRunningStub()) { + Serial.println("Dummy read chip detect after final block"); + (void)esp32boot->read_chip_detect(); + } + + //------------- MD5 verification -------------// + Serial.println("Verifying MD5"); + esp32boot->md5Flash(addr, zfile->uncompressed_len, esp_md5); + + if (0 == memcmp(zfile->md5, esp_md5, 16)) { + Serial.println("MD5 matched"); + } else { + Serial.println("MD5 mismatched!!"); + + Serial.print("File: "); + print_buf(zfile->md5, 16); + + Serial.print("ESP : "); + print_buf(esp_md5, 16); + } + + if (buf) { + free(buf); + } + + return zfile->uncompressed_len; +} +size_t Adafruit_TestBed::esp32_programFlashDefl(const esp32_zipfile_t *zfile, + uint32_t addr) { + return _esp32_programFlashDefl_impl(zfile, addr, NULL); +} + +size_t Adafruit_TestBed::esp32_programFlashDefl(const esp32_zipfile_t *zfile, + uint32_t addr, File32 *fsrc) { + return _esp32_programFlashDefl_impl(zfile, addr, fsrc); +} + Adafruit_TestBed TB; diff --git a/src/Adafruit_TestBed.h b/src/Adafruit_TestBed.h index c712d2d..545f98f 100644 --- a/src/Adafruit_TestBed.h +++ b/src/Adafruit_TestBed.h @@ -5,6 +5,9 @@ #include "Arduino.h" #include "Wire.h" +#include "ESP32BootROM.h" +#include "SdFat.h" + #define RED 0xFF0000 #define YELLOW 0xFFFF00 #define GREEN 0x00FF00 @@ -13,6 +16,14 @@ #define PURPLE 0xFF00FF #define WHITE 0xFFFFFF +typedef struct { + const char *name; + const uint8_t *data; + const uint32_t compressed_len; + const uint32_t uncompressed_len; + const uint8_t md5[16]; +} esp32_zipfile_t; + /**************************************************************************/ /*! @brief A helper class for making test beds and functions. Lots of handy lil @@ -33,6 +44,8 @@ class Adafruit_TestBed { void targetPower(bool on); void targetPowerCycle(uint16_t off_time = 10); + void targetReset(uint32_t reset_ms = 20); + float readAnalogVoltage(uint16_t pin, float multiplier = 1); bool testAnalogVoltage(uint16_t pin, const char *name, float multiplier, float value); @@ -50,6 +63,24 @@ class Adafruit_TestBed { uint32_t timestamp(void); void printTimeTaken(bool restamp = false); + //--------------------------------------------------------------------+ + // ESP32 Target + //--------------------------------------------------------------------+ + bool esp32_begin(ESP32BootROMClass *bootrom, uint32_t baudrate); + void esp32_end(bool reset_esp = false); + + // program esp32 target with compressed data stored in internal flash + size_t esp32_programFlashDefl(const esp32_zipfile_t *zfile, uint32_t addr); + + // program esp32 target with compressed file from SDCard + size_t esp32_programFlashDefl(const esp32_zipfile_t *zfile, uint32_t addr, + File32 *fsrc); + + bool esp32_s3_inReset(void); + void esp32_s3_clearReset(void); + + ESP32BootROMClass *esp32boot; // ESP32 ROM + ////////////////// TwoWire *theWire = &Wire; ///< The I2C port used in scanning Stream *theSerial = &Serial; ///< The Serial port used for debugging @@ -60,6 +91,8 @@ class Adafruit_TestBed { int16_t targetPowerPin = -1; ///< Set to a target power pin if used bool targetPowerPolarity = HIGH; ///< What to set the power pin to, for ON + int targetResetPin = -1; ///< Set to target reset pin if used + int16_t neopixelPin = -1; ///< The neopixel connected pin if any uint8_t neopixelNum = 0; ///< How many neopixels are on board, if any Adafruit_NeoPixel *pixels = @@ -70,6 +103,15 @@ class Adafruit_TestBed { private: uint32_t millis_timestamp = 0; ///< A general purpose timestamp + + bool _esp32_flash_defl; + uint32_t _esp32_chip_detect; + bool _esp32s3_in_reset; + + size_t _esp32_programFlashDefl_impl(const esp32_zipfile_t *zfile, + uint32_t addr, File32 *fsrc); }; +extern Adafruit_TestBed TB; + #endif diff --git a/src/Adafruit_TestBed_Brains.cpp b/src/Adafruit_TestBed_Brains.cpp index ca61431..3d09d5a 100644 --- a/src/Adafruit_TestBed_Brains.cpp +++ b/src/Adafruit_TestBed_Brains.cpp @@ -65,10 +65,6 @@ class BrainCRC32 { uint32_t crc; }; -static inline uint32_t div_ceil(uint32_t v, uint32_t d) { - return (v + d - 1) / d; -} - /**************************************************************************/ /*! @brief Initializer, sets up the timestamp, neopixels, piezo, led, @@ -94,16 +90,11 @@ Adafruit_TestBed_Brains::Adafruit_TestBed_Brains() { _usbh_dp_pin = 20; // USB Host D+ _vbus_en_pin = 22; // USB Host VBus enable - _target_rst = 27; + targetResetPin = 27; _target_swdio = 2; _target_swdclk = 3; dap = NULL; - - esp32boot = NULL; - _esp32_flash_defl = false; - _esp32_chip_detect = 0; - _esp32s3_in_reset = false; } void Adafruit_TestBed_Brains::begin(void) { @@ -115,8 +106,8 @@ void Adafruit_TestBed_Brains::begin(void) { neopixelNum = 1; pinMode(neopixelPin, OUTPUT); - pinMode(_target_rst, OUTPUT); - digitalWrite(_target_rst, HIGH); + pinMode(targetResetPin, OUTPUT); + digitalWrite(targetResetPin, HIGH); pinMode(_sd_detect_pin, INPUT_PULLUP); pinMode(_vbus_en_pin, OUTPUT); @@ -138,23 +129,6 @@ void Adafruit_TestBed_Brains::begin(void) { bool Adafruit_TestBed_Brains::inited(void) { return _inited; } -void Adafruit_TestBed_Brains::targetReset(uint32_t reset_ms) { - digitalWrite(_target_rst, LOW); - delay(reset_ms); - digitalWrite(_target_rst, HIGH); - - // Note: S3 has an USB-OTG errata - // https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf - // which is walkarounded by idf/arduino-esp32 to always mux JTAG to USB for - // uploading and/or power on. Afterwards USB-OTG will be set up if selected - // so. However rp2040 USBH is running too fast and can actually retrieve - // device/configuration descriptor of JTAG before the OTG is fully setup. - // Mark this for application usage - if (_esp32_chip_detect == CHIP_DETECT_MAGIC_ESP32S3) { - _esp32s3_in_reset = true; - } -} - //--------------------------------------------------------------------+ // RP2040 Target //--------------------------------------------------------------------+ @@ -162,7 +136,6 @@ void Adafruit_TestBed_Brains::targetReset(uint32_t reset_ms) { void Adafruit_TestBed_Brains::rp2040_targetResetBootRom(int bootsel_pin, uint32_t reset_ms) { pinMode(bootsel_pin, OUTPUT); - digitalWrite(bootsel_pin, LOW); targetReset(reset_ms); @@ -240,7 +213,7 @@ bool Adafruit_TestBed_Brains::dap_begin(Adafruit_DAP *dp) { dap = dp; - return dap->begin(_target_swdclk, _target_swdio, _target_rst, + return dap->begin(_target_swdclk, _target_swdio, targetResetPin, dap_err_hanlder); } @@ -407,239 +380,6 @@ size_t Adafruit_TestBed_Brains::dap_programFlash(const char *fpath, return fsize; } -//--------------------------------------------------------------------+ -// ESP32 Target -//--------------------------------------------------------------------+ - -bool Adafruit_TestBed_Brains::esp32_begin(ESP32BootROMClass *bootrom, - uint32_t baudrate) { - esp32boot = bootrom; - - LCD_printf("Syncing ESP32"); - _esp32_chip_detect = esp32boot->begin(baudrate); - - if (_esp32_chip_detect) { - setColor(0xFFFFFF); - LCD_printf("Synced OK"); - return true; - } else { - LCD_printf_error("Sync failed!"); - return false; - } -} - -void Adafruit_TestBed_Brains::esp32_end(bool reset_esp) { - if (esp32boot->isRunningStub()) { - // skip sending flash_finish to ROM loader here, - // as it causes the loader to exit and run user code - esp32boot->beginFlash(0, 0, esp32boot->getFlashWriteSize()); - - if (_esp32_flash_defl) { - esp32boot->endFlashDefl(reset_esp); - } else { - esp32boot->endFlash(reset_esp); - } - } - - esp32boot->end(); -} - -bool Adafruit_TestBed_Brains::esp32_s3_inReset(void) { - return _esp32s3_in_reset; -} - -void Adafruit_TestBed_Brains::esp32_s3_clearReset(void) { - _esp32s3_in_reset = false; -} - -size_t -Adafruit_TestBed_Brains::esp32_programFlashDefl(const esp32_zipfile_t *zfile, - uint32_t addr) { - if (!esp32boot) { - return 0; - } - - // Check if MD5 matches to skip this file - uint8_t esp_md5[16]; - -#if 1 // change to 0 to skip pre-flash md5 check for testing - esp32boot->md5Flash(addr, zfile->uncompressed_len, esp_md5); - Serial.printf("Flash MD5: "); - for (size_t i = 0; i < 16; i++) { - Serial.printf("%02X ", esp_md5[i]); - } - Serial.println(); - if (0 == memcmp(zfile->md5, esp_md5, 16)) { - LCD_printf(2, "MD5 matched"); - return zfile->uncompressed_len; - } -#endif - - // Write Size is different depending on ROM (1K) or Stub (16KB) - uint32_t const block_size = esp32boot->getFlashWriteSize(); - - Serial.printf("Compressed %lu bytes to %lu\r\n", zfile->uncompressed_len, - zfile->compressed_len); - - if (!esp32boot->beginFlashDefl(addr, zfile->uncompressed_len, - zfile->compressed_len)) { - LCD_printf_error("beginFlash failed!"); - } else { - _esp32_flash_defl = true; - - uint32_t const block_num = div_ceil(zfile->compressed_len, block_size); - - //------------- Flashing -------------// - uint8_t const *data = zfile->data; - uint32_t remain = zfile->compressed_len; - - for (uint32_t i = 0; i < block_num; i++) { - setLED(HIGH); - LCD_printf(1, "Pckt %u/%u", i + 1, block_num); - - uint32_t const wr_count = MIN(block_size, remain); - - // Note: flash deflat does not need padding - if (!esp32boot->dataFlashDefl(data, wr_count)) { - LCD_printf_error("Failed to flash"); - break; - } - - setLED(LOW); - - data += wr_count; - remain -= wr_count; - } - Serial.println(); - - // Stub only writes each block to flash after 'ack'ing the receive, - // so do a final dummy operation which will not be 'ack'ed - // until the last block has actually been written out to flash - if (esp32boot->isRunningStub()) { - Serial.println("Dummy read chip detect after final block"); - (void)esp32boot->read_chip_detect(); - } - - //------------- MD5 verification -------------// - Serial.println("Verifying MD5"); - esp32boot->md5Flash(addr, zfile->uncompressed_len, esp_md5); - - if (0 == memcmp(zfile->md5, esp_md5, 16)) { - LCD_printf(2, "MD5 matched"); - } else { - LCD_error(NULL, "MD5 mismatched!!"); - - Serial.printf("File: "); - for (size_t i = 0; i < 16; i++) { - Serial.printf("%02X ", zfile->md5[i]); - } - Serial.println(); - - Serial.printf("ESP : "); - for (size_t i = 0; i < 16; i++) { - Serial.printf("%02X ", esp_md5[i]); - } - Serial.println(); - } - } - - return zfile->uncompressed_len; -} - -size_t Adafruit_TestBed_Brains::esp32_programFlash(const char *fpath, - uint32_t addr) { - if (!esp32boot) { - return 0; - } - - // Write Size is different depending on ROM (1K) or Stub (16KB) - uint32_t const block_size = esp32boot->getFlashWriteSize(); - uint8_t *buf = (uint8_t *)malloc(block_size); - if (!buf) { - LCD_printf_error("No memory %u\n", block_size); - return 0; - } - - File32 fsrc = SD.open(fpath); - if (!fsrc) { - Serial.printf("SD: cannot open file: %s\r\n", fpath); - return 0; - } - uint32_t fsize = fsrc.fileSize(); - uint32_t total_count = 0; - - Serial.printf("fsize = %lu, block size = %lu\r\n", fsize, block_size); - - if (!esp32boot->beginFlash(addr, fsize, block_size)) { - LCD_printf_error("beginFlash failed!"); - } else { - LCD_printf("#Packets %u", div_ceil(fsize, block_size)); - - MD5Builder md5; - md5.begin(); - - //------------- Flashing -------------// - while (fsrc.available()) { - memset(buf, 0xff, block_size); // empty it out - uint32_t const rd_count = fsrc.read(buf, block_size); - - setLED(HIGH); - Serial.printf("#"); - - if (!esp32boot->dataFlash(buf, block_size)) { - LCD_printf_error("Failed to flash"); - break; - } - - setLED(LOW); - - md5.add(buf, rd_count); - total_count += rd_count; - } - Serial.println(); - - // Stub only writes each block to flash after 'ack'ing the receive, - // so do a final dummy operation which will not be 'ack'ed - // until the last block has actually been written out to flash - if (esp32boot->isRunningStub()) { - (void)esp32boot->read_chip_detect(); - } - - //------------- MD5 verification -------------// - md5.calculate(); - Serial.printf("md5 = %s\r\n", md5.toString().c_str()); - - uint8_t file_md5[16]; - md5.getBytes(file_md5); - - uint8_t esp_md5[16]; - esp32boot->md5Flash(addr, fsize, esp_md5); - - if (0 == memcmp(file_md5, esp_md5, 16)) { - LCD_printf("MD5 matched"); - } else { - LCD_printf_error("MD5 mismatched!!"); - - Serial.printf("File: "); - for (size_t i = 0; i < 16; i++) { - Serial.printf("%02X ", file_md5[i]); - } - Serial.println(); - - Serial.printf("ESP : "); - for (size_t i = 0; i < 16; i++) { - Serial.printf("%02X ", esp_md5[i]); - } - Serial.println(); - } - } - - free(buf); - fsrc.close(); - - return total_count; -} - //--------------------------------------------------------------------+ // SD Card //--------------------------------------------------------------------+ diff --git a/src/Adafruit_TestBed_Brains.h b/src/Adafruit_TestBed_Brains.h index 1bda844..926b139 100644 --- a/src/Adafruit_TestBed_Brains.h +++ b/src/Adafruit_TestBed_Brains.h @@ -3,24 +3,14 @@ #ifdef ARDUINO_RASPBERRY_PI_PICO -#include "Adafruit_TestBed.h" #include "SdFat.h" + +#include "Adafruit_TestBed.h" #include #include "Adafruit_DAP.h" #include "Adafruit_TinyUSB.h" -#include "ESP32BootROM.h" -#include "MD5Builder.h" - -typedef struct { - const char *name; - const uint8_t *data; - const uint32_t compressed_len; - const uint32_t uncompressed_len; - const uint8_t md5[16]; -} esp32_zipfile_t; - /**************************************************************************/ /*! @brief A helper class for making RP2040 "Tester Brains" @@ -60,7 +50,6 @@ class Adafruit_TestBed_Brains : public Adafruit_TestBed { bool usbh_umountFS(uint8_t dev_addr); // Target - void targetReset(uint32_t reset_ms = 20); //--------------------------------------------------------------------+ // RP2040 Target @@ -88,23 +77,6 @@ class Adafruit_TestBed_Brains : public Adafruit_TestBed { // return number of programmed bytes size_t dap_programFlash(const char *fpath, uint32_t addr); - //--------------------------------------------------------------------+ - // ESP32 Target - //--------------------------------------------------------------------+ - - bool esp32_begin(ESP32BootROMClass *bootrom, uint32_t baudrate); - void esp32_end(bool reset_esp = false); - - // program esp32 target with file from SDCard - // return number of programmed bytes - size_t esp32_programFlash(const char *fpath, uint32_t addr); - - // program flash with compressed using zipfile struct - size_t esp32_programFlashDefl(const esp32_zipfile_t *zfile, uint32_t addr); - - bool esp32_s3_inReset(void); - void esp32_s3_clearReset(void); - //--------------------------------------------------------------------+ // Public Variables //--------------------------------------------------------------------+ @@ -123,11 +95,6 @@ class Adafruit_TestBed_Brains : public Adafruit_TestBed { // Dap Adafruit_DAP *dap; - // ESP32 ROM - ESP32BootROMClass *esp32boot; - - int _target_rst; - private: bool _inited; uint8_t _lcd_line; @@ -140,10 +107,6 @@ class Adafruit_TestBed_Brains : public Adafruit_TestBed { int _target_swdio; int _target_swdclk; - bool _esp32_flash_defl; - uint32_t _esp32_chip_detect; - bool _esp32s3_in_reset; - void lcd_write(uint8_t linenum, char buf[17]); }; diff --git a/tools/esp_compress.py b/tools/esp_compress.py index d4304d5..311a407 100644 --- a/tools/esp_compress.py +++ b/tools/esp_compress.py @@ -1,21 +1,7 @@ -import sys import zlib import hashlib from pathlib import Path -import os.path - -output_header = 'esp_binaries.h' - -# Argument is path to folder containing .bin files -# This script will create .bin.gz file with the same name to the folder -# and also print out C array of the gz along with MD5 checksum -dir = '' -if len(sys.argv) == 1: - print("No [DIRS] arguments, use current directory as default") - dir = Path('./') -else: - # only take 1 argument - dir = Path(sys.argv[1]) +import click def print_carray(f, payload): while len(payload) > 0: @@ -25,50 +11,71 @@ def print_carray(f, payload): payload = payload[16:] f.write('\n') -with open(output_header, 'w') as fc: - # print typedef struct - fc.write('// Generated by tools/esp_compress.py\n') - file_list = sorted(dir.glob('*.bin')) - fc.write('#define ESP_BINARIES_COUNT ({})\n\n'.format(len(file_list))) - - # print list of struct first - for fname in file_list: - var = fname.stem - var = var.replace('.', '_') - var = var.replace('-', '_') - fc.write('// const esp32_zipfile_t {}\n'.format(var)) - fc.write('\n'.format(var)) - for fname in file_list: - with open(fname, 'rb') as fi: - image = fi.read() - zimage = zlib.compress(image, 9) - fzname = fname.with_suffix('.bin.gz') - md5 = hashlib.md5(image) +@click.command() +@click.argument('dpath', type=click.Path(exists=True), default='.') +@click.option('--sd', is_flag=True, default=False, help='Generate header for using with SDCard') +def main(dpath, sd): + """ + This script takes a directory path (default is current directory) and + create compressed .bin.gz file along 'esp_binaries.h' containing metadata in esp32_zipfile_t struct. + Which can be used to program ESP32 with TestBed or Brain. + If '--sd' option is specified, it will skip generating C array data for gz file. + """ + output_header = 'esp_binaries.h' + with open(output_header, 'w') as fc: + # print typedef struct + fc.write('// Generated by tools/esp_compress.py\n') + file_list = sorted(Path(dpath).glob('*.bin')) + fc.write(f'#define ESP_BINARIES_COUNT ({len(file_list)})\n\n') - # write .gz file - with open(fzname, 'wb') as fz: - fz.write(zimage) - - # write to c header file + # print list of struct first + for fname in file_list: var = fname.stem var = var.replace('.', '_') var = var.replace('-', '_') + fc.write(f'// const esp32_zipfile_t {var}\n') + fc.write('\n') + + for fname in file_list: + with open(fname, 'rb') as fi: + image = fi.read() + zimage = zlib.compress(image, 9) + fzname = fname.with_suffix('.bin.gz') + md5 = hashlib.md5(image) + + # write .gz file + with open(fzname, 'wb') as fz: + fz.write(zimage) + + # write to c header file + var = fname.stem + var = var.replace('.', '_') + var = var.replace('-', '_') + + # bin gz contents + if not sd: + fc.write(f'const uint8_t _{var}_gz[{len(zimage)}] = {{') + print_carray(fc, zimage) + fc.write('};\n\n') + + fc.write(f'const esp32_zipfile_t {var} = {{\n') + fc.write(f' .name = "{fzname}",\n') + if sd: + fc.write(f' .data = NULL,\n') + else: + fc.write(f' .data = _{var}_gz,\n') + fc.write(f' .compressed_len = {len(zimage)},\n') + fc.write(f' .uncompressed_len = {len(image)},\n') + fc.write(' .md5 = {') + print_carray(fc, md5.digest()) + fc.write(' },\n') + fc.write('};\n') + fc.write('\n') - # bin gz contents - fc.write('const uint8_t _{}_gz[{}] = {{'.format(var, len(zimage))) - print_carray(fc, zimage) - fc.write('};\n\n') + deflation = 100*(1-len(zimage)/len(image)) + print(f"Compressed {var}: {len(image)} to {len(zimage)} bytes, deflation: {deflation:.2f}%") - fc.write('const esp32_zipfile_t {} = {{\n'.format(var)) - fc.write(' .name = "{}",\n'.format(var)) - fc.write(' .data = _{}_gz,\n'.format(var)) - fc.write(' .compressed_len = {},\n'.format(len(zimage))) - fc.write(' .uncompressed_len = {},\n'.format(len(image))) - fc.write(' .md5 = {') - print_carray(fc, md5.digest()) - fc.write(' },\n') - fc.write('};\n') - fc.write('\n') - print("Compressed {}: {} to {} bytes, deflation: {:.2f}%".format(var, len(image), len(zimage), 100*(1-len(zimage)/len(image)))) +if __name__ == '__main__': + main()