Fixed a Partition boot issue due to XIP Cache

This commit is contained in:
Philip Smart
2026-03-28 18:53:44 +00:00
parent 30466c912c
commit 881364b9fe
20 changed files with 200 additions and 43 deletions

View File

@@ -1,4 +1,3 @@
2
define xac
dont-repeat

View File

@@ -1 +1 @@
2.0
2.03

View File

@@ -814,15 +814,19 @@ bool SDCard::storeRP2350Info(const t_IpcFrameHdr &frame, FSPI &fspi)
{
memcpy(rp2350Header, infoBuf, sizeof(t_FlashPartitionHeader));
// Extended payload includes cpufreq/psramfreq/voltage after the header.
// Extended payload includes cpufreq/psramfreq/voltage/flashSize/psramSize after the header.
if (payloadSize >= sizeof(t_FlashInfoPayload))
{
t_FlashInfoPayload *info = (t_FlashInfoPayload *) infoBuf;
// Store in the 3 int32_t fields immediately after rp2350FlashHeader in wifiCtrl.run
int32_t *extra = (int32_t *) (rp2350Header + 1); // pointer arithmetic: past the header
// Store in the fields immediately after rp2350FlashHeader in wifiCtrl.run.
// Layout must match: int32_t cpufreq, psramfreq, voltage; uint32_t flashSize, psramSize;
int32_t *extra = (int32_t *) (rp2350Header + 1);
extra[0] = info->cpufreq;
extra[1] = info->psramfreq;
extra[2] = info->voltage;
uint32_t *extra32 = (uint32_t *) &extra[3];
extra32[0] = info->flashSize;
extra32[1] = info->psramSize;
}
}
free(infoBuf);

View File

@@ -64,6 +64,8 @@
#include "esp_event.h"
#include "esp_ota_ops.h"
#include "esp_timer.h"
#include "esp_flash.h"
#include "esp_psram.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "driver/uart.h"
@@ -872,6 +874,36 @@ esp_err_t WiFi::expandVarsAndSend(httpd_req_t *req, std::string str)
std::to_string(wifiCtrl.run.rp2350PsramFreq / 1000000) + " MHz" : "N/A";
pairs.push_back(keyValue);
// RP2350 Flash and PSRAM sizes — dynamic from INF payload.
keyValue.name = "%SK_RP2350FLASH%";
keyValue.value = (wifiCtrl.run.rp2350FlashSize > 0) ?
std::to_string(wifiCtrl.run.rp2350FlashSize / (1024 * 1024)) + " MB" : "N/A";
pairs.push_back(keyValue);
keyValue.name = "%SK_RP2350PSRAM%";
keyValue.value = (wifiCtrl.run.rp2350PsramSize > 0) ?
std::to_string(wifiCtrl.run.rp2350PsramSize / (1024 * 1024)) + " MB" : "N/A";
pairs.push_back(keyValue);
// ESP32 info — available directly from ESP-IDF APIs.
keyValue.name = "%SK_ESP32CLOCK%";
keyValue.value = std::to_string(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ) + " MHz";
pairs.push_back(keyValue);
keyValue.name = "%SK_ESP32FLASH%";
{
// Use physical size (actual chip) not image header size (may be smaller).
uint32_t flashSize = 0;
if (esp_flash_get_physical_size(NULL, &flashSize) != ESP_OK)
esp_flash_get_size(NULL, &flashSize); // Fallback to image header size
keyValue.value = (flashSize > 0) ? std::to_string(flashSize / (1024 * 1024)) + " MB" : "N/A";
}
pairs.push_back(keyValue);
keyValue.name = "%SK_ESP32PSRAM%";
keyValue.value = std::to_string(esp_psram_get_size() / (1024 * 1024)) + " MB";
pairs.push_back(keyValue);
keyValue.name = "%SK_SDCARD%";
keyValue.value = (sdcard != NULL) ? "Mounted" : "Not available";
pairs.push_back(keyValue);
@@ -1914,20 +1946,24 @@ esp_err_t WiFi::otaFetchFile(httpd_req_t *req, const std::string &createFileName
// Build the FQFN of the file to create.
std::string fqfn = pThis->wifiCtrl.run.fsPath + fileDir + '/' + createFileName;
ESP_LOGI(WIFITAG, "File to create => %s", fqfn.c_str());
ESP_LOGI(WIFITAG, "FW_FETCH: file=%s content_len=%d heap=%lu", fqfn.c_str(), req->content_len, (unsigned long) esp_get_free_heap_size());
// Open a stream on the SD card temp directory in which to place the received data.
outFile.open(fqfn.c_str());
if (outFile.is_open())
{
ESP_LOGI(WIFITAG, "FW_FETCH: file opened OK");
chunk.reset(new char[MAX_CHUNK_SIZE]);
if (chunk == nullptr)
{
result = ESP_FAIL;
ESP_LOGE(WIFITAG, "FW_FETCH: chunk alloc failed");
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory exhausted in otaFetchFile");
}
remaining = req->content_len;
ESP_LOGI(WIFITAG, "FW_FETCH: starting recv loop, remaining=%d", remaining);
int chunkCount = 0;
while (remaining > 0 && result == ESP_OK)
{
chunkSize = httpd_req_recv(req, chunk.get(), MIN(remaining, MAX_CHUNK_SIZE));
@@ -1937,6 +1973,7 @@ esp_err_t WiFi::otaFetchFile(httpd_req_t *req, const std::string &createFileName
{
continue;
}
ESP_LOGE(WIFITAG, "FW_FETCH: recv error chunkSize=%d remaining=%d", (int) chunkSize, remaining);
result = ESP_FAIL;
int sockFd = httpd_req_to_sockfd(req);
if (sockFd != -1)
@@ -1946,14 +1983,18 @@ esp_err_t WiFi::otaFetchFile(httpd_req_t *req, const std::string &createFileName
{
outFile.write(chunk.get(), chunkSize);
remaining -= chunkSize;
chunkCount++;
if ((chunkCount % 10) == 0)
ESP_LOGI(WIFITAG, "FW_FETCH: chunk %d, remaining=%d", chunkCount, remaining);
}
}
outFile.close();
ESP_LOGI(WIFITAG, "FW_FETCH: done, chunks=%d result=%d", chunkCount, result);
}
else
{
result = ESP_FAIL;
ESP_LOGI(WIFITAG, "Failed to create file:%s", fqfn.c_str());
ESP_LOGE(WIFITAG, "FW_FETCH: failed to create file:%s", fqfn.c_str());
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create local temporary file");
}
@@ -2063,6 +2104,8 @@ esp_err_t WiFi::reqRP2350FWUpdate(char *errMsg,
pageBuffer[bufIdx++] = (uint8_t) (0);
pageBuffer[bufIdx++] = (uint8_t) (0);
pageBuffer[bufIdx++] = (uint8_t) (instance);
ESP_LOGI(WIFITAG, "FW_AUTH: instance=%d addr=%08lx size=%08lx chksum=%08lx",
instance, rp2350Addr, fileSize, fwChkSum);
pageBuffer[bufIdx++] = (uint8_t) (fwChkSum >> 24);
pageBuffer[bufIdx++] = (uint8_t) (fwChkSum >> 16);
pageBuffer[bufIdx++] = (uint8_t) (fwChkSum >> 8);
@@ -2337,12 +2380,14 @@ esp_err_t WiFi::otaRP2350FirmwareUpdatePOSTHandler(httpd_req_t *req)
{
*(errMsg) = 0x00;
ESP_LOGI(WIFITAG, "FW_UPD: fetching file from browser...");
if (pThis->otaFetchFile(req, WIFI_RP2350_FW_FILENAME) != ESP_OK)
{
sprintf((errMsg + strlen(errMsg)), "Failed to fetch firmware file => %s\n", fqfnBIN.c_str());
}
else
{
ESP_LOGI(WIFITAG, "FW_UPD: file saved, opening binary...");
inFile.open(fqfnBIN.c_str(), std::ios::binary);
inFile.seekg(0, inFile.end);
fileSize = inFile.tellg();

View File

@@ -317,6 +317,8 @@ class WiFi
int32_t rp2350CpuFreq; // RP2350 CPU freq in Hz (from INF)
int32_t rp2350PsramFreq; // PSRAM freq in Hz (from INF)
int32_t rp2350Voltage; // Core voltage (from INF)
uint32_t rp2350FlashSize; // RP2350 Flash size in bytes (from INF)
uint32_t rp2350PsramSize; // RP2350 PSRAM size in bytes (from INF)
// Active Floppy Disk images.
std::string floppyDiskImage[WIFI_MAX_FLOPPY_DISK_IMAGES];

View File

@@ -159,6 +159,8 @@ typedef struct
int32_t cpufreq; // RP2350 CPU frequency in Hz (0 = default)
int32_t psramfreq; // PSRAM SPI frequency in Hz (0 = default)
int32_t voltage; // Core voltage setting (VREG enum)
uint32_t flashSize; // RP2350 Flash size in bytes
uint32_t psramSize; // RP2350 PSRAM size in bytes
} t_FlashInfoPayload;
// Structure to describe a single file stored in ROM. Indexed by its filename (matching a filename appearing in JSON including path)

View File

@@ -611,6 +611,23 @@ extern "C"
//
void app_main()
{
// Log the reset reason FIRST — critical for diagnosing unexpected reboots.
esp_reset_reason_t rstReason = esp_reset_reason();
const char *rstName;
switch (rstReason)
{
case ESP_RST_POWERON: rstName = "POWERON"; break;
case ESP_RST_EXT: rstName = "EXT_PIN"; break;
case ESP_RST_SW: rstName = "SW_RESET"; break;
case ESP_RST_PANIC: rstName = "PANIC"; break;
case ESP_RST_INT_WDT: rstName = "INT_WDT"; break;
case ESP_RST_TASK_WDT: rstName = "TASK_WDT"; break;
case ESP_RST_WDT: rstName = "OTHER_WDT"; break;
case ESP_RST_BROWNOUT: rstName = "BROWNOUT"; break;
default: rstName = "UNKNOWN"; break;
}
ESP_LOGW(MAINTAG, "=== ESP32 BOOT === reset reason: %d (%s)", (int) rstReason, rstName);
// Locals.
static NVS nvs;
static SDCard sdcard;

View File

@@ -1 +1 @@
2.0
2.03

View File

@@ -187,22 +187,34 @@
<h3 class="panel-title"><i class="fa fa-dashboard"></i> System Status</h3>
</div>
<div class="panel-body">
<table class="table table-borderless table-sm" style="margin-bottom:0;">
<table class="table table-borderless table-sm" style="margin-bottom:0; width:auto;">
<tbody>
<tr>
<td style="width:160px;">Firmware Version:</td><td><span style="color:#5bf;" id="sys-fwver">%SK_FWVERSION%</span></td>
<td style="width:160px;">ESP32 Version:</td><td><span style="color:#5bf;" id="sys-espver">%SK_ESPVERSION%</span></td>
<td style="width:140px; padding-right:4px;">Firmware Version:</td><td style="width:200px;"><span style="color:#5bf;">%SK_FWVERSION%</span></td>
<td style="width:140px; padding-right:4px;">ESP32 Version:</td><td style="width:200px;"><span style="color:#5bf;">%SK_ESPVERSION%</span></td>
</tr>
<tr>
<td>Active Partition:</td><td><span style="color:#5bf;" id="sys-partition">%SK_ACTIVEPARTITION%</span></td>
<td>Active Persona:</td><td><span style="color:#5bf;" id="sys-persona">%SK_PERSONA%</span></td>
<td>Active Partition:</td><td><span style="color:#5bf;">%SK_ACTIVEPARTITION%</span></td>
<td>Active Persona:</td><td><span style="color:#5bf;">%SK_PERSONA%</span></td>
</tr>
<tr>
<td>RP2350 Clock:</td><td><span style="color:#5bf;" id="sys-cpuclk">%SK_CPUCLOCK%</span></td>
<td>PSRAM Clock:</td><td><span style="color:#5bf;" id="sys-psramclk">%SK_PSRAMCLOCK%</span></td>
<td>RP2350 Clock:</td><td><span style="color:#5bf;">%SK_CPUCLOCK%</span></td>
<td>ESP32 Clock:</td><td><span style="color:#5bf;">%SK_ESP32CLOCK%</span></td>
</tr>
<tr>
<td>SD Card:</td><td><span style="color:#5bf;" id="sys-sdcard">%SK_SDCARD%</span></td>
<td>RP2350 Flash:</td><td><span style="color:#5bf;">%SK_RP2350FLASH%</span></td>
<td>ESP32 Flash:</td><td><span style="color:#5bf;">%SK_ESP32FLASH%</span></td>
</tr>
<tr>
<td>RP2350 PSRAM:</td><td><span style="color:#5bf;">%SK_RP2350PSRAM%</span></td>
<td>ESP32 PSRAM:</td><td><span style="color:#5bf;">%SK_ESP32PSRAM%</span></td>
</tr>
<tr>
<td>PSRAM Clock:</td><td><span style="color:#5bf;">%SK_PSRAMCLOCK%</span></td>
<td>SD Card:</td><td><span style="color:#5bf;">%SK_SDCARD%</span></td>
</tr>
<tr>
<td>FilePack:</td><td><span style="color:#5bf;">%SK_FILEPACK%</span></td>
<td>Uptime:</td><td><span style="color:#5bf;" id="sys-uptime">%SK_UPTIME%</span></td>
</tr>
</tbody>

View File

@@ -1 +1 @@
2.0
2.03

View File

@@ -54,6 +54,9 @@ static t_ESP *esp;
// Sequence counter for binary IPC frames — incremented per command for retry detection.
static uint8_t gIpcSeq = 0;
// PSRAM size from main.c — populated by psram_init() at boot.
extern size_t psramSize;
// Source was originally C++, so this is the destructor.
void ESP_deinit(void)
{
@@ -134,6 +137,10 @@ bool ESP_sendVersionInfo(void)
infPayload.voltage = 0;
}
// Hardware sizes — always available regardless of config.
infPayload.flashSize = PICO_FLASH_SIZE_BYTES;
infPayload.psramSize = psramSize; // Set during psram_init() at boot
// Send INF command — payload is the extended flash info.
// Only attempt if ESP32 HS is already HIGH (CommandProcessor ready).
// INF is non-critical (web interface version display only); the Z80

View File

@@ -72,7 +72,7 @@
// At 50MHz the SPI RX FIFO (4 bytes deep) can overflow when core-1 Z80/PSRAM
// accesses stall the AHB crossbar, causing dropped bytes mid-transfer and CRC mismatches.
// If too many CRC errors are seen in the log, reduce the frequency.
#define FSPI_CLK_FREQ 50 * 1000 * 1000
#define FSPI_CLK_FREQ 25 * 1000 * 1000
#define FSPI_DATABITS_PER_XFER 8
#define FSPI_DATA_POLARITY SPI_CPOL_1
#define FSPI_DATA_PHASE SPI_CPHA_1

View File

@@ -161,6 +161,8 @@ typedef struct
int32_t cpufreq; // RP2350 CPU frequency in Hz (0 = default)
int32_t psramfreq; // PSRAM SPI frequency in Hz (0 = default)
int32_t voltage; // Core voltage setting (VREG enum)
uint32_t flashSize; // RP2350 Flash size in bytes (from PICO_FLASH_SIZE_BYTES)
uint32_t psramSize; // RP2350 PSRAM size in bytes (from psram_init)
} t_FlashInfoPayload;
// Structure to describe a single file stored in ROM. Indexed by its filename (matching a filename appearing in JSON including path)

View File

@@ -186,7 +186,7 @@ static t_FlashAppConfigHeader *flashAppConfigHeader;
//struct semaphore core1StartSem;
static t_Z80CPU *z80CPU = NULL;
static size_t psramSize;
size_t psramSize; // Global — used by ESP.c for INF payload
// Queue definitions (global, shared by ALL devices — referenced via z80CPU->requestQueue/responseQueue)
static queue_t requestQueue __attribute__((unused));
@@ -1270,6 +1270,11 @@ void processInterCoreCommands(void)
// Main entry point
int main(void)
{
// ABSOLUTE FIRST: Enable watchdog for recovery if partition 2 hangs.
// Do NOT write scratch registers here — they hold the previous boot's
// diagnostic data which we need to read below.
watchdog_enable(30000, true);
// Locals.
uint32_t taskCount = 100;
bool clkInit = false;
@@ -1491,6 +1496,13 @@ int main(void)
}
bootStage(BOOTP_CORE1_LAUNCH);
// Ensure Core 1 is in a clean state before launch. After an AIRCR system
// reset (used by partition switch and firmware update), Core 1 may not have
// finished its bootrom parking sequence by the time Core 0 reaches here.
// multicore_reset_core1 forces Core 1 back to the bootrom wait loop.
multicore_reset_core1();
sleep_ms(10);
// Setup core 1 to run the Z80 CPU.
multicore_launch_core1(core1Main);
@@ -1667,6 +1679,18 @@ int main(void)
debugf("*** WATCHDOG RESET — no valid previous stage (magic=0x%08lX) ***\r\n", prevMagic);
}
// Show bootloader's boot decision from scratch[4] (set by bootloader before branchToApp).
// scratch[4] is SPIDIAG — overwritten later at line ~1347, so read it NOW.
{
uint32_t blDecision = watchdog_hw->scratch[BOOTP_SCR_SPIDIAG];
if ((blDecision & 0xFFFF0000) == 0xB0070000)
debugf(" Bootloader: BRANCH to app %d\r\n", blDecision & 0xFF);
else if ((blDecision & 0xFFFF0000) == 0xFA110000)
debugf(" Bootloader: VALIDATION FAILED for app %d\r\n", blDecision & 0xFF);
else if ((blDecision & 0xFFFF0000) == 0xBAD00000)
debugf(" Bootloader: PRE-CHECK FAILED for app %d\r\n", blDecision & 0xFF);
}
// Check PSRAM for fault diagnostic data from a previous boot's crash.
// The fault handler writes to PSRAM_DIAG_ADDR (0x117FFF00) which survives
// watchdog resets. This captures faults that occur before USB is ready.

View File

@@ -23,7 +23,7 @@
MEMORY
{
/* 128K offset as the bootloader occupies the first block. */
/* Partition 2: starts after bootloader (128K) + App1 (5MB) = offset 0x520000. */
FLASH(rx) : ORIGIN = 0x10520000, LENGTH = (16 * 1024 * 1024) - 0x520000
/* FLASH(rx) : ORIGIN = 0x10000000, LENGTH = (16 * 1024 * 1024) */
PSRAM(rwx) : ORIGIN = 0x11000000, LENGTH = (8 * 1024 * 1024)

View File

@@ -1 +1 @@
2.0
2.009

View File

@@ -38,6 +38,7 @@
#include "hardware/clocks.h"
#include "hardware/resets.h"
#include "hardware/watchdog.h"
#include "hardware/xip_cache.h"
#include <pico/printf.h>
#include "rp2350.h"
#include "flash_ram.h"
@@ -289,8 +290,35 @@ int main()
(APP_FLASH_CONTENTS[header->config[header->activeApp].addr - HW_FLASHADDR_START + FLASH_APP_SIG_POS + 2] == FLASH_APP_SIG_BYTE_3) &&
(APP_FLASH_CONTENTS[header->config[header->activeApp].addr - HW_FLASHADDR_START + FLASH_APP_SIG_POS + 3] == FLASH_APP_SIG_BYTE_4)))
{
// Record boot decision in scratch[4] — survives watchdog resets.
watchdog_hw->scratch[4] = 0xB0070000 | header->activeApp;
// Enable watchdog BEFORE branchToApp — if the app crashes during
// CRT startup (before its own watchdog_enable in main()), this ensures
// automatic recovery instead of a permanent hang.
watchdog_enable(30000, true);
// Invalidate XIP cache before branching — the bootloader ran from
// 0x10000000 and the cache holds entries from that range. Without
// invalidation, cache aliasing or stale prefetch state can cause
// the app at a different flash address (e.g. 0x10520000) to hang
// during CRT startup. This was the root cause of partition 2 boot
// failures — GDB's reset cleared the cache, which is why loading
// via GDB "fixed" it.
xip_cache_invalidate_all();
branchToApp(header->activeApp);
}
else
{
// Validation failed.
watchdog_hw->scratch[4] = 0xFA110000 | header->activeApp;
}
}
else
{
// Pre-validation check failed.
watchdog_hw->scratch[4] = 0xBAD00000 | header->activeApp;
}
// Initialise debug output buffers.

View File

@@ -1 +1 @@
2.0
2.004

View File

@@ -1146,8 +1146,9 @@ void updateFlashSectors(uint32_t flashAddr, uint8_t *src, uint32_t sectors)
//
void updateFlashPartitionHeader(t_FlashPartitionHeader *header)
{
// Locals.
uint8_t flashbuf[FW_SECTOR_SIZE];
// Static buffer — 4KB on the stack is dangerous in the bootloader context
// and can overflow into adjacent stack frames, corrupting data silently.
static uint8_t flashbuf[FW_SECTOR_SIZE];
// Clear the flash header. Erase is 1 Sector, ie. 4096 bytes.
flash_range_erase(FLASH_HEADER_OFFSET, FW_SECTOR_SIZE);
@@ -1347,18 +1348,13 @@ void pollUSBtoUART(void)
(flashPartitionInstance.addr % FW_PAGE_SIZE == 0) && (flashPartitionInstance.size < HW_FLASHADDR_SIZE) &&
(flashPartitionInstance.addr + flashPartitionInstance.size < HW_FLASHADDR_END))
{
#if defined(BOOTLOADER_DEBUG)
debugf("First frame ok: addr=%0lx, size=%0lx, flashAddr=%0lx -> %s,%s,%s,%s,%s,%s\r\n",
// Always log first frame details — critical for diagnosing partition issues.
debugf("AUTH OK: inst=%d addr=%0lx size=%0lx flashOfs=%0lx chksum=%0lx\r\n",
updInstance,
flashPartitionInstance.addr,
flashPartitionInstance.size,
flashAddr,
flashPartitionInstance.license,
flashPartitionInstance.author,
flashPartitionInstance.description,
flashPartitionInstance.version,
flashPartitionInstance.versionDate,
flashPartitionInstance.copyright);
#endif
flashPartitionInstance.chksum);
// Ok to send response now, data confirmed.
ud->fwFirstFrame = false;
@@ -1522,26 +1518,37 @@ void pollUSBtoUART(void)
flashPartitionInstance.cfgSize = FLASH_APP_CONFIG_SIZE;
memcpy(&flashPartitionHeader.config[updInstance], &flashPartitionInstance, sizeof(t_FlashPartitionInstance));
// Diagnostic — always print the partition update details.
debugf("FW_UPD: inst=%d addr=%0lx size=%0lx chksum=%0lx activeApp=%d\r\n",
updInstance,
flashPartitionHeader.config[updInstance].addr,
flashPartitionHeader.config[updInstance].size,
flashPartitionHeader.config[updInstance].chksum,
flashPartitionHeader.activeApp);
// Update the flash header with latest config changes.
updateFlashPartitionHeader(&flashPartitionHeader);
// Interrupts MUST be disabled during flash erase/program — the UART ISR
// runs from flash and would hard-fault if it fires during the operation.
{
uint32_t ints = save_and_disable_interrupts();
updateFlashPartitionHeader(&flashPartitionHeader);
restore_interrupts(ints);
}
// Clear configuration flash RAM for this instance?
if (ud->fwCfgClearMode)
{
debugf("Clearing config, partition:%d\r\n", updInstance);
uint32_t ints = save_and_disable_interrupts();
clearFlashConfig(updInstance);
restore_interrupts(ints);
}
// Send success.
putUART(ui->inst, FW_RESP_FW_OK);
#if defined(BOOTLOADER_DEBUG)
// End of line, we are to reboot so make debug tidy.
debugf("\r\nFirmware OK (%0lx, %0lx, %0lx)\r\n",
flashPartitionHeader.config[updInstance].addr,
flashPartitionHeader.config[updInstance].size,
flashPartitionHeader.config[updInstance].chksum);
#endif
// Diagnostic — confirm completion.
debugf("FW_UPD: FW_OK sent, rebooting...\r\n");
// Need to flush out serial debug data if present, so enter loop to service the USB and UART buffers.
for (int idx = 0; idx < 250; idx++)
@@ -1551,8 +1558,13 @@ void pollUSBtoUART(void)
sleep_ms(10);
}
// Finally, reboot using the watchdog.
// Reboot: try both methods to ensure the reset fires.
// The flush loop above keeps Core 0 busy in pollUSBtoUART too (both
// cores call bridgeUSBtoUART). After the loop, trigger reset.
watchdog_reboot(0, 0, 0);
busy_wait_ms(10);
*((volatile uint32_t *)(PPB_BASE + 0x0ED0C)) = 0x05FA0004;
while (1) { tight_loop_contents(); }
}
}
else
@@ -1574,6 +1586,9 @@ void pollUSBtoUART(void)
if (++ud->fwTimeoutCnt > FW_MAX_TIMEOUT_CNT)
{
watchdog_reboot(0, 0, 0);
busy_wait_ms(10);
*((volatile uint32_t *)(PPB_BASE + 0x0ED0C)) = 0x05FA0004;
while (1) { tight_loop_contents(); }
}
else
{

View File

@@ -1 +1 @@
2.0
2.011