diff --git a/CMakeLists.txt b/CMakeLists.txt index 10dc03f..4dca877 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register( SRCS "src/rvswd.c" - "src/rvswd_ch32v203.c" + "src/rvswd_ch32v20x.c" INCLUDE_DIRS "include" REQUIRES diff --git a/example/main/main.c b/example/main/main.c index d2f3891..61751a5 100644 --- a/example/main/main.c +++ b/example/main/main.c @@ -6,22 +6,29 @@ #include #include -#include "rvswd_ch32v203.h" #include "esp_err.h" #include "esp_log.h" +#include "rvswd_ch32v20x.h" static const char* TAG = "example"; extern uint8_t const coprocessor_firmware_start[] asm("_binary_coprocessor_bin_start"); extern uint8_t const coprocessor_firmware_end[] asm("_binary_coprocessor_bin_end"); +void callback(char const* msg, uint8_t progress) { + ESP_LOGI(TAG, "%s: %d%%", msg, progress); +} + static void flash_coprocessor(void) { rvswd_handle_t handle = { .swdio = 22, .swclk = 23, }; - bool success = ch32_program(&handle, coprocessor_firmware_start, coprocessor_firmware_end - coprocessor_firmware_start); + ch32v20x_read_option_bytes(&handle); + + bool success = ch32v20x_program(&handle, coprocessor_firmware_start, + coprocessor_firmware_end - coprocessor_firmware_start, callback); if (success) { ESP_LOGI(TAG, "Succesfully flashed the CH32V203 microcontroller"); diff --git a/include/ch32v20x_registers.h b/include/ch32v20x_registers.h new file mode 100644 index 0000000..b7344bb --- /dev/null +++ b/include/ch32v20x_registers.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2025 Nicolai Electronics + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +// Register definitions from the CH32V20x and CH32V30x reference manual + +// Addresses +#define CH32V20X_ADDR_OPTION_BYTES 0x1FFFF800 + +// CH32V20X and CH32V30X flash status register +#define CH32V20X_FLASH_STATR_BSY (1 << 0) // Flash is busy writing or erasing +#define CH32V20X_FLASH_STATR_WRBUSY (1 << 1) // Flash is busy writing +#define CH32V20X_FLASH_STATR_WRPRTERR (1 << 4) // Flash write protection error +#define CH32V20X_FLASH_STATR_EOP (1 << 5) // Flash is finished with the operation +#define CH32V20X_FLASH_STATR_EHMODS (1 << 7) // Flash enhanced read mode start + +// CH32V20X and CH32V30X flash control register +#define CH32V20X_FLASH_CTLR_PG (1 << 0) // Perform standard programming operation +#define CH32V20X_FLASH_CTLR_PER (1 << 1) // Perform 1K sector erase +#define CH32V20X_FLASH_CTLR_MER (1 << 2) // Perform full Flash erase +#define CH32V20X_FLASH_CTLR_OBG (1 << 4) // Perform user-selected word program +#define CH32V20X_FLASH_CTLR_OBER (1 << 5) // Perform user-selected word erasure +#define CH32V20X_FLASH_CTLR_STRT (1 << 6) // Start an erase operation +#define CH32V20X_FLASH_CTLR_LOCK (1 << 7) // Lock the FLASH +#define CH32V20X_FLASH_CTLR_FTPG (1 << 16) // Start a fast page programming operation (256 bytes) +#define CH32V20X_FLASH_CTLR_FTER (1 << 17) // Start a fast page erase operation (256 bytes) +#define CH32V20X_FLASH_CTLR_PGSTRT (1 << 21) // Start a page programming operation (256 bytes) diff --git a/include/ch32x035_registers.h b/include/ch32x035_registers.h new file mode 100644 index 0000000..7dc5a4c --- /dev/null +++ b/include/ch32x035_registers.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2025 Nicolai Electronics + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +// Register definitions from the CH32X035 reference manual + +// CH32X035 flash status register +#define CH32X035_FLASH_STATR_BSY (1 << 0) // Flash is busy writing or erasing +#define CH32X035_FLASH_STATR_WRPRTERR (1 << 4) // Flash write protection error +#define CH32X035_FLASH_STATR_EOP (1 << 5) // Flash is finished with the operation +#define CH32X035_FLASH_STATR_FWAKE_FLAG (1 << 6) // Flash wake-up flag +#define CH32X035_FLASH_STATR_TURBO (1 << 7) // Flash turbo mode enable +#define CH32X035_FLASH_STATR_BOOT_AVA (1 << 12) // Initial configuration word status +#define CH32X035_FLASH_STATR_BOOT_STATUS (1 << 13) // Source of currently executing program +#define CH32X035_FLASH_STATR_BOOT_MODE (1 << 14) // Switch between user area and boot area +#define CH32X035_FLASH_STATR_BOOT_LOCK (1 << 15) // Boot zone lockout + +// CH32X035 flash control register +#define CH32X035_FLASH_CTLR_PER (1 << 1) // Perform 1K sector erase +#define CH32X035_FLASH_CTLR_MER (1 << 2) // Perform full flash erase +#define CH32X035_FLASH_CTLR_OBPG (1 << 4) // Perform user option bytes word program +#define CH32X035_FLASH_CTLR_OBER (1 << 5) // Perform user option bytes word erasure +#define CH32X035_FLASH_CTLR_STRT (1 << 6) // Start an erase or program operation +#define CH32X035_FLASH_CTLR_LOCK (1 << 7) // Lock the flash +#define CH32X035_FLASH_CTLR_OBWRE (1 << 9) // Unlock the user option bytes +#define CH32X035_FLASH_CTLR_ERRIE (1 << 10) // Error status interrupt enable +#define CH32X035_FLASH_CTLR_EOPIE (1 << 12) // Operation completion interrupt enable +#define CH32X035_FLASH_CTLR_FWAKEIE (1 << 13) // Wake-up interrupt enable +#define CH32X035_FLASH_CTLR_FLOCK (1 << 15) // Fast programming lock +#define CH32X035_FLASH_CTLR_FTPG (1 << 16) // Perform a fast page programming operation (256 bytes) +#define CH32X035_FLASH_CTLR_FTER (1 << 17) // Start a fast page erase operation (256 bytes) +#define CH32X035_FLASH_CTLR_BUFLOAD (1 << 18) // Cache data into BUF +#define CH32X035_FLASH_CTLR_BUFRST (1 << 19) // Buffer BUF reset operation +#define CH32X035_FLASH_CTLR_BER32 (1 << 23) // Execute block erasure 32KB diff --git a/include/rvswd_ch32v203.h b/include/rvswd_ch32v203.h deleted file mode 100644 index 574411c..0000000 --- a/include/rvswd_ch32v203.h +++ /dev/null @@ -1,16 +0,0 @@ - -/** - * Copyright (c) 2025 Nicolai Electronics - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include "rvswd.h" - -// Optional user-defined status update callback. -void ch32_status_callback(char const* msg, int progress, int total); - -// Program and restart the CH32V203. -bool ch32_program(rvswd_handle_t* handle, void const* firmware, size_t firmware_len); diff --git a/include/rvswd_ch32v20x.h b/include/rvswd_ch32v20x.h new file mode 100644 index 0000000..4528d57 --- /dev/null +++ b/include/rvswd_ch32v20x.h @@ -0,0 +1,39 @@ + +/** + * Copyright (c) 2025 Nicolai Electronics + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include "rvswd.h" + +typedef void (*ch32v20x_status_callback)(char const* msg, uint8_t progress); + +// Option bytes +bool ch32v20x_read_option_bytes(rvswd_handle_t* handle); + +// Program and restart the CH32V203 +bool ch32v20x_program(rvswd_handle_t* handle, void const* firmware, size_t firmware_len, + ch32v20x_status_callback status_callback); + +rvswd_result_t ch32v20x_halt_microprocessor(rvswd_handle_t* handle); +rvswd_result_t ch32v20x_resume_microprocessor(rvswd_handle_t* handle); +rvswd_result_t ch32v20x_reset_microprocessor_and_run(rvswd_handle_t* handle); +bool ch32v20x_write_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t value); +bool ch32v20x_read_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t* value_out); +bool ch32v20x_run_debug_code(rvswd_handle_t* handle, void const* code, size_t code_size); +bool ch32v20x_read_memory_word(rvswd_handle_t* handle, uint32_t address, uint32_t* value_out); +bool ch32v20x_write_memory_word(rvswd_handle_t* handle, uint32_t address, uint32_t value); +bool ch32v20x_wait_flash(rvswd_handle_t* handle); +void ch32v20x_wait_flash_write(rvswd_handle_t* handle); +bool ch32v20x_unlock_flash(rvswd_handle_t* handle); +bool ch32v20x_lock_flash(rvswd_handle_t* handle); +bool ch32v20x_erase_flash_block(rvswd_handle_t* handle, uint32_t addr); +bool ch32v20x_write_flash_block(rvswd_handle_t* handle, uint32_t addr, void const* data); +bool ch32v20x_write_flash(rvswd_handle_t* handle, uint32_t addr, void const* _data, size_t data_len, + ch32v20x_status_callback status_callback); +bool ch32v20x_clear_running_operations(rvswd_handle_t* handle); +bool ch32v20x_read_option_bytes(rvswd_handle_t* handle); diff --git a/src/rvswd_ch32v203.c b/src/rvswd_ch32v20x.c similarity index 50% rename from src/rvswd_ch32v203.c rename to src/rvswd_ch32v20x.c index 48bc48a..ed6f5f6 100644 --- a/src/rvswd_ch32v203.c +++ b/src/rvswd_ch32v20x.c @@ -5,12 +5,13 @@ * SPDX-License-Identifier: MIT */ -#include "rvswd_ch32v203.h" +#include "rvswd_ch32v20x.h" +#include "ch32v20x_registers.h" #include "esp_log.h" #include "freertos/projdefs.h" #include "string.h" -static char const TAG[] = "RVSWD-CH32V203"; +static char const TAG[] = "CH32V20X"; #define CH32_REG_DEBUG_DATA0 0x04 // Data register 0, can be used for temporary storage of data #define CH32_REG_DEBUG_DATA1 0x05 // Data register 1, can be used for temporary storage of data @@ -39,55 +40,17 @@ static char const TAG[] = "RVSWD-CH32V203"; #define CH32_CFGR_KEY 0x5aa50000 #define CH32_CFGR_OUTEN (1 << 10) -// The start of CH32 CODE FLASH region. -#define CH32_CODE_BEGIN 0x08000000 -// the end of the CH32 CODE FLASH region. -#define CH32_CODE_END 0x08004000 - -// FLASH status register. -#define CH32_FLASH_STATR 0x4002200C -// FLASH configuration register. -#define CH32_FLASH_CTLR 0x40022010 -// FLASH address register. -#define CH32_FLASH_ADDR 0x40022014 - -// FLASH is busy writing or erasing. -#define CH32_FLASH_STATR_BUSY (1 << 0) -// FLASH is busy writing -#define CH32_FLASH_STATR_WRBUSY (1 << 1) -// FLASH write protection error -#define CH32_FLASH_STATR_WRPRTERR (1 << 4) -// FLASH is finished with the operation. -#define CH32_FLASH_STATR_EOP (1 << 5) -// FLASH enhanced read mode start -#define CH32_FLASH_STATR_EHMODS (1 << 7) - -// Perform standard programming operation. -#define CH32_FLASH_CTLR_PG (1 << 0) -// Perform 1K sector erase. -#define CH32_FLASH_CTLR_PER (1 << 1) -// Perform full FLASH erase. -#define CH32_FLASH_CTLR_MER (1 << 2) -// Perform user-selected word program. -#define CH32_FLASH_CTLR_OBG (1 << 4) -// Perform user-selected word erasure. -#define CH32_FLASH_CTLR_OBER (1 << 5) -// Start an erase operation. -#define CH32_FLASH_CTLR_STRT (1 << 6) -// Lock the FLASH. -#define CH32_FLASH_CTLR_LOCK (1 << 7) -// Start a fast page programming operation (256 bytes). -#define CH32_FLASH_CTLR_FTPG (1 << 16) -// Start a fast page erase operation (256 bytes). -#define CH32_FLASH_CTLR_FTER (1 << 17) -// Start a page programming operation (256 bytes). -#define CH32_FLASH_CTLR_PGSTRT (1 << 21) - -uint8_t const ch32_readmem[] = {0x88, 0x41, 0x02, 0x90}; - -uint8_t const ch32_writemem[] = {0x88, 0xc1, 0x02, 0x90}; - -rvswd_result_t ch32_halt_microprocessor(rvswd_handle_t* handle) { +#define CH32_CODE_BEGIN 0x08000000 // The start of CH32 CODE Flash region +#define CH32_CODE_END 0x08004000 // the end of the CH32 CODE Flash region + +#define CH32V20X_FLASH_STATR 0x4002200C // Flash status register +#define CH32V20X_FLASH_CTLR 0x40022010 // Flash configuration register +#define CH32_FLASH_ADDR 0x40022014 // Flash address register + +static uint8_t const ch32v20x_readmem[] = {0x88, 0x41, 0x02, 0x90}; +static uint8_t const ch32v20x_writemem[] = {0x88, 0xc1, 0x02, 0x90}; + +rvswd_result_t ch32v20x_halt_microprocessor(rvswd_handle_t* handle) { rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x80000001); // Make the debug module work properly rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x80000001); // Initiate a halt request @@ -113,7 +76,7 @@ rvswd_result_t ch32_halt_microprocessor(rvswd_handle_t* handle) { return RVSWD_OK; } -rvswd_result_t ch32_resume_microprocessor(rvswd_handle_t* handle) { +rvswd_result_t ch32v20x_resume_microprocessor(rvswd_handle_t* handle) { rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x80000001); // Make the debug module work properly rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x80000001); // Initiate a halt request rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x00000001); // Clear the halt request @@ -138,7 +101,7 @@ rvswd_result_t ch32_resume_microprocessor(rvswd_handle_t* handle) { return RVSWD_OK; } -rvswd_result_t ch32_reset_microprocessor_and_run(rvswd_handle_t* handle) { +rvswd_result_t ch32v20x_reset_microprocessor_and_run(rvswd_handle_t* handle) { rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x80000001); // Make the debug module work properly rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x80000001); // Initiate a halt request rvswd_write(handle, CH32_REG_DEBUG_DMCONTROL, 0x00000001); // Clear the halt request @@ -169,7 +132,7 @@ rvswd_result_t ch32_reset_microprocessor_and_run(rvswd_handle_t* handle) { return RVSWD_OK; } -bool ch32_write_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t value) { +bool ch32v20x_write_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t value) { uint32_t command = regno // Register to access. | (1 << 16) // Write access. | (1 << 17) // Perform transfer. @@ -181,7 +144,7 @@ bool ch32_write_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t value) return true; } -bool ch32_read_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t* value_out) { +bool ch32v20x_read_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t* value_out) { uint32_t command = regno // Register to access. | (0 << 16) // Read access. | (1 << 17) // Perform transfer. @@ -193,7 +156,7 @@ bool ch32_read_cpu_reg(rvswd_handle_t* handle, uint16_t regno, uint32_t* value_o return true; } -bool ch32_run_debug_code(rvswd_handle_t* handle, void const* code, size_t code_size) { +bool ch32v20x_run_debug_code(rvswd_handle_t* handle, void const* code, size_t code_size) { if (code_size > 8 * 4) { ESP_LOGE(TAG, "Debug program is too long (%zd/%zd)", code_size, (size_t)8 * 4); return false; @@ -219,31 +182,31 @@ bool ch32_run_debug_code(rvswd_handle_t* handle, void const* code, size_t code_s return true; } -bool ch32_read_memory_word(rvswd_handle_t* handle, uint32_t address, uint32_t* value_out) { - ch32_write_cpu_reg(handle, CH32_REGS_GPR + 11, address); - ch32_run_debug_code(handle, ch32_readmem, sizeof(ch32_readmem)); - ch32_read_cpu_reg(handle, CH32_REGS_GPR + 10, value_out); +bool ch32v20x_read_memory_word(rvswd_handle_t* handle, uint32_t address, uint32_t* value_out) { + ch32v20x_write_cpu_reg(handle, CH32_REGS_GPR + 11, address); + ch32v20x_run_debug_code(handle, ch32v20x_readmem, sizeof(ch32v20x_readmem)); + ch32v20x_read_cpu_reg(handle, CH32_REGS_GPR + 10, value_out); return true; } -bool ch32_write_memory_word(rvswd_handle_t* handle, uint32_t address, uint32_t value) { - ch32_write_cpu_reg(handle, CH32_REGS_GPR + 10, value); - ch32_write_cpu_reg(handle, CH32_REGS_GPR + 11, address); - ch32_run_debug_code(handle, ch32_writemem, sizeof(ch32_writemem)); +bool ch32v20x_write_memory_word(rvswd_handle_t* handle, uint32_t address, uint32_t value) { + ch32v20x_write_cpu_reg(handle, CH32_REGS_GPR + 10, value); + ch32v20x_write_cpu_reg(handle, CH32_REGS_GPR + 11, address); + ch32v20x_run_debug_code(handle, ch32v20x_writemem, sizeof(ch32v20x_writemem)); return true; } -// Wait for the FLASH chip to finish its current operation. -static bool ch32_wait_flash(rvswd_handle_t* handle) { +// Wait for the Flash chip to finish its current operation. +bool ch32v20x_wait_flash(rvswd_handle_t* handle) { uint32_t value = 0; - ch32_read_memory_word(handle, CH32_FLASH_STATR, &value); + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_STATR, &value); uint32_t timeout = 1000; - while (value & CH32_FLASH_STATR_BUSY) { + while (value & CH32V20X_FLASH_STATR_BSY) { ESP_LOGD(TAG, "Flash busy: FLASH_STATR = 0x%08" PRIx32 "\r\n", value); vTaskDelay(1); - ch32_read_memory_word(handle, CH32_FLASH_STATR, &value); + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_STATR, &value); timeout--; if (timeout == 0) { return false; @@ -252,103 +215,103 @@ static bool ch32_wait_flash(rvswd_handle_t* handle) { return true; } -static void ch32_wait_flash_write(rvswd_handle_t* handle) { +void ch32v20x_wait_flash_write(rvswd_handle_t* handle) { uint32_t value = 0; - ch32_read_memory_word(handle, CH32_FLASH_STATR, &value); - while (value & CH32_FLASH_STATR_WRBUSY) { - ch32_read_memory_word(handle, CH32_FLASH_STATR, &value); + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_STATR, &value); + while (value & CH32V20X_FLASH_STATR_WRBUSY) { + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_STATR, &value); } } -// Unlock the FLASH if not already unlocked. -bool ch32_unlock_flash(rvswd_handle_t* handle) { +// Unlock the Flash if not already unlocked. +bool ch32v20x_unlock_flash(rvswd_handle_t* handle) { uint32_t ctlr; - ch32_read_memory_word(handle, CH32_FLASH_CTLR, &ctlr); + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_CTLR, &ctlr); // Enter the unlock keys. - ch32_write_memory_word(handle, 0x40022004, 0x45670123); - ch32_write_memory_word(handle, 0x40022004, 0xCDEF89AB); - ch32_write_memory_word(handle, 0x40022008, 0x45670123); - ch32_write_memory_word(handle, 0x40022008, 0xCDEF89AB); - ch32_write_memory_word(handle, 0x40022024, 0x45670123); - ch32_write_memory_word(handle, 0x40022024, 0xCDEF89AB); + ch32v20x_write_memory_word(handle, 0x40022004, 0x45670123); + ch32v20x_write_memory_word(handle, 0x40022004, 0xCDEF89AB); + ch32v20x_write_memory_word(handle, 0x40022008, 0x45670123); + ch32v20x_write_memory_word(handle, 0x40022008, 0xCDEF89AB); + ch32v20x_write_memory_word(handle, 0x40022024, 0x45670123); + ch32v20x_write_memory_word(handle, 0x40022024, 0xCDEF89AB); - // Check again if FLASH is unlocked. - ch32_read_memory_word(handle, CH32_FLASH_CTLR, &ctlr); + // Check again if Flash is unlocked. + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_CTLR, &ctlr); - return ((ctlr & CH32_FLASH_CTLR_LOCK) != CH32_FLASH_CTLR_LOCK); + return ((ctlr & CH32V20X_FLASH_CTLR_LOCK) != CH32V20X_FLASH_CTLR_LOCK); } // Lock the FLASH -bool ch32_lock_flash(rvswd_handle_t* handle) { +bool ch32v20x_lock_flash(rvswd_handle_t* handle) { uint32_t ctlr; - // Check if FLASH is locked - ch32_read_memory_word(handle, CH32_FLASH_CTLR, &ctlr); + // Check if Flash is locked + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_CTLR, &ctlr); - if ((ctlr & CH32_FLASH_CTLR_LOCK) == CH32_FLASH_CTLR_LOCK) { - ESP_LOGW(TAG, "Target flash already locked"); + if ((ctlr & CH32V20X_FLASH_CTLR_LOCK) == CH32V20X_FLASH_CTLR_LOCK) { + ESP_LOGW(TAG, "Target Flash already locked"); return true; } // Lock FLASH - ch32_write_memory_word(handle, CH32_FLASH_CTLR, ctlr | CH32_FLASH_CTLR_LOCK); + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_CTLR, ctlr | CH32V20X_FLASH_CTLR_LOCK); - // Check again if FLASH is locked - ch32_read_memory_word(handle, CH32_FLASH_CTLR, &ctlr); + // Check again if Flash is locked + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_CTLR, &ctlr); - return ((ctlr & CH32_FLASH_CTLR_LOCK) == CH32_FLASH_CTLR_LOCK); + return ((ctlr & CH32V20X_FLASH_CTLR_LOCK) == CH32V20X_FLASH_CTLR_LOCK); } // If unlocked: Erase a 256-byte block of FLASH. -bool ch32_erase_flash_block(rvswd_handle_t* handle, uint32_t addr) { +bool ch32v20x_erase_flash_block(rvswd_handle_t* handle, uint32_t addr) { if (addr % 256) return false; - bool wait_res = ch32_wait_flash(handle); + bool wait_res = ch32v20x_wait_flash(handle); if (!wait_res) { return false; } - ch32_write_memory_word(handle, CH32_FLASH_CTLR, CH32_FLASH_CTLR_FTER); - ch32_write_memory_word(handle, CH32_FLASH_ADDR, addr); - ch32_write_memory_word(handle, CH32_FLASH_CTLR, CH32_FLASH_CTLR_FTER | CH32_FLASH_CTLR_STRT); - wait_res = ch32_wait_flash(handle); + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_CTLR, CH32V20X_FLASH_CTLR_FTER); + ch32v20x_write_memory_word(handle, CH32_FLASH_ADDR, addr); + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_CTLR, CH32V20X_FLASH_CTLR_FTER | CH32V20X_FLASH_CTLR_STRT); + wait_res = ch32v20x_wait_flash(handle); if (!wait_res) { return false; } - ch32_write_memory_word(handle, CH32_FLASH_CTLR, 0); + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_CTLR, 0); return true; } // If unlocked: Write a 256-byte block of FLASH. -bool ch32_write_flash_block(rvswd_handle_t* handle, uint32_t addr, void const* data) { +bool ch32v20x_write_flash_block(rvswd_handle_t* handle, uint32_t addr, void const* data) { if (addr % 256) return false; - bool wait_res = ch32_wait_flash(handle); + bool wait_res = ch32v20x_wait_flash(handle); if (!wait_res) { return false; } - ch32_write_memory_word(handle, CH32_FLASH_CTLR, CH32_FLASH_CTLR_FTPG); + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_CTLR, CH32V20X_FLASH_CTLR_FTPG); - ch32_write_memory_word(handle, CH32_FLASH_ADDR, addr); + ch32v20x_write_memory_word(handle, CH32_FLASH_ADDR, addr); uint32_t wdata[64]; memcpy(wdata, data, sizeof(wdata)); for (size_t i = 0; i < 64; i++) { - ch32_write_memory_word(handle, addr + i * 4, wdata[i]); - ch32_wait_flash_write(handle); + ch32v20x_write_memory_word(handle, addr + i * 4, wdata[i]); + ch32v20x_wait_flash_write(handle); } - ch32_write_memory_word(handle, CH32_FLASH_CTLR, CH32_FLASH_CTLR_FTPG | CH32_FLASH_CTLR_PGSTRT); - wait_res = ch32_wait_flash(handle); + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_CTLR, CH32V20X_FLASH_CTLR_FTPG | CH32V20X_FLASH_CTLR_PGSTRT); + wait_res = ch32v20x_wait_flash(handle); if (!wait_res) { return false; } - ch32_write_memory_word(handle, CH32_FLASH_CTLR, 0); + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_CTLR, 0); vTaskDelay(1); uint32_t rdata[64]; for (size_t i = 0; i < 64; i++) { vTaskDelay(0); - ch32_read_memory_word(handle, addr + i * 4, &rdata[i]); + ch32v20x_read_memory_word(handle, addr + i * 4, &rdata[i]); } if (memcmp(wdata, rdata, sizeof(wdata))) { ESP_LOGE(TAG, "Write block mismatch at %08" PRIx32, addr); @@ -366,8 +329,9 @@ bool ch32_write_flash_block(rvswd_handle_t* handle, uint32_t addr, void const* d return true; } -// If unlocked: Erase and write a range of FLASH memory. -bool ch32_write_flash(rvswd_handle_t* handle, uint32_t addr, void const* _data, size_t data_len) { +// If unlocked: Erase and write a range of Flash memory. +bool ch32v20x_write_flash(rvswd_handle_t* handle, uint32_t addr, void const* _data, size_t data_len, + ch32v20x_status_callback status_callback) { if (addr % 64) { return false; } @@ -379,15 +343,17 @@ bool ch32_write_flash(rvswd_handle_t* handle, uint32_t addr, void const* _data, for (size_t i = 0; i < data_len; i += 256) { vTaskDelay(0); snprintf(buffer, sizeof(buffer) - 1, "Writing at 0x%08" PRIx32, addr + i); - ch32_status_callback(buffer, i, data_len); + if (status_callback) { + status_callback(buffer, i * 100 / data_len); + } - if (!ch32_erase_flash_block(handle, addr + i)) { - ESP_LOGE(TAG, "Error: Failed to erase FLASH at %08" PRIx32, addr + i); + if (!ch32v20x_erase_flash_block(handle, addr + i)) { + ESP_LOGE(TAG, "Error: Failed to erase Flash at %08" PRIx32, addr + i); return false; } - if (!ch32_write_flash_block(handle, addr + i, data + i)) { - ESP_LOGE(TAG, "Error: Failed to write FLASH at %08" PRIx32, addr + i); + if (!ch32v20x_write_flash_block(handle, addr + i, data + i)) { + ESP_LOGE(TAG, "Error: Failed to write Flash at %08" PRIx32, addr + i); return false; } } @@ -395,19 +361,19 @@ bool ch32_write_flash(rvswd_handle_t* handle, uint32_t addr, void const* _data, return true; } -bool ch32_clear_running_operations(rvswd_handle_t* handle) { +bool ch32v20x_clear_running_operations(rvswd_handle_t* handle) { uint32_t timeout = 100; while (1) { uint32_t value = 0; - ch32_read_memory_word(handle, CH32_FLASH_STATR, &value); - if (value & CH32_FLASH_STATR_BUSY) { - if (value & CH32_FLASH_STATR_EOP) { + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_STATR, &value); + if (value & CH32V20X_FLASH_STATR_BSY) { + if (value & CH32V20X_FLASH_STATR_EOP) { ESP_LOGD(TAG, "Clearing EOP flag...\r\n"); - ch32_write_memory_word(handle, CH32_FLASH_STATR, value | CH32_FLASH_STATR_EOP); - } else if (value & CH32_FLASH_STATR_WRPRTERR) { + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_STATR, value | CH32V20X_FLASH_STATR_EOP); + } else if (value & CH32V20X_FLASH_STATR_WRPRTERR) { ESP_LOGD(TAG, "Clearing WRPRTERR flag...\r\n"); - ch32_write_memory_word(handle, CH32_FLASH_STATR, value | CH32_FLASH_STATR_WRPRTERR); - } else if (value & CH32_FLASH_STATR_WRBUSY) { + ch32v20x_write_memory_word(handle, CH32V20X_FLASH_STATR, value | CH32V20X_FLASH_STATR_WRPRTERR); + } else if (value & CH32V20X_FLASH_STATR_WRBUSY) { ESP_LOGD(TAG, "Waiting for busy flag to clear...\r\n"); timeout--; if (timeout == 0) { @@ -416,7 +382,7 @@ bool ch32_clear_running_operations(rvswd_handle_t* handle) { } } else { uint32_t ctlr_value = 0; - ch32_read_memory_word(handle, CH32_FLASH_CTLR, &ctlr_value); + ch32v20x_read_memory_word(handle, CH32V20X_FLASH_CTLR, &ctlr_value); ESP_LOGE(TAG, "Target busy for unknown reason (FLASH_STATR: 0x%08" PRIx32 ", FLASH_CTLR: 0x%08" PRIx32 ")!\r\n", @@ -430,8 +396,132 @@ bool ch32_clear_running_operations(rvswd_handle_t* handle) { } } -// Program and restart the CH32V203. -bool ch32_program(rvswd_handle_t* handle, void const* firmware, size_t firmware_len) { +// Configure option bytes of the CH32V20X +bool ch32v20x_read_option_bytes(rvswd_handle_t* handle) { + rvswd_result_t res; + + res = rvswd_init(handle); + + if (res != RVSWD_OK) { + ESP_LOGE(TAG, "RVSWD initialization error %u!", res); + return false; + } + + res = rvswd_reset(handle); + + if (res != RVSWD_OK) { + ESP_LOGE(TAG, "RVSWD reset error %u!", res); + return false; + } + + res = ch32v20x_reset_microprocessor_and_run(handle); + if (res != RVSWD_OK) { + ESP_LOGE(TAG, "Failed to reset target"); + return false; + } + + res = ch32v20x_halt_microprocessor(handle); + if (res != RVSWD_OK) { + ESP_LOGE(TAG, "Failed to halt target"); + return false; + } + + uint32_t option_bytes[4] = {0}; + for (uint8_t i = 0; i < 4; i++) { + ch32v20x_read_memory_word(handle, CH32V20X_ADDR_OPTION_BYTES + (i * 4), &option_bytes[i]); + } + + uint8_t rdpr = ((option_bytes[0] >> 0) & 0xFF); + uint8_t nrdpr = ((option_bytes[0] >> 8) & 0xFF); + + if (((uint8_t)(~nrdpr)) == rdpr) { + if (rdpr == 0xA5) { + printf("Read protection disabled\r\n"); + } else { + printf("Read protection enabled\r\n"); + } + } else { + printf("Invalid read protection config 0x%02x 0x%02x\r\n", rdpr, nrdpr); + } + + uint8_t user = ((option_bytes[0] >> 16) & 0xFF); + uint8_t nuser = ((option_bytes[0] >> 24) & 0xFF); + if (((uint8_t)(~nuser)) == user) { + if (user & (1 << 0)) { + printf("Independent watchdog is disabled by hardware\r\n"); + } else { + printf("Independent watchdog is not disabled by hardware\r\n"); + } + if (user & (1 << 1)) { + printf("System will not reset when entering stop mode\r\n"); + } else { + printf("System will reset when entering stop mode\r\n"); + } + if (user & (1 << 2)) { + printf("System is not reset when entering standby mode\r\n"); + } else { + printf("System is reset when entering standby mode\r\n"); + } + uint8_t ram_code_mod = (user >> 6) & 3; + printf("RAM code mode: %02X\r\n", ram_code_mod); + } else { + printf("Invalid user config 0x%02x 0x%02x\r\n", user, nuser); + } + + uint8_t data0 = ((option_bytes[1] >> 0) & 0xFF); + uint8_t ndata0 = ((option_bytes[1] >> 8) & 0xFF); + if (((uint8_t)(~ndata0)) == data0) { + printf("User data 0: 0x%02x\r\n", data0); + } else { + printf("Invalid user data 0 value 0x%02x 0x%02x\r\n", data0, ndata0); + } + + uint8_t data1 = ((option_bytes[1] >> 16) & 0xFF); + uint8_t ndata1 = ((option_bytes[1] >> 24) & 0xFF); + if (((uint8_t)(~ndata1)) == data1) { + printf("User data 1: 0x%02x\r\n", data1); + } else { + printf("Invalid user data 1 value 0x%02x 0x%02x\r\n", data1, ndata1); + } + + uint8_t wrpr0 = ((option_bytes[2] >> 0) & 0xFF); + uint8_t nwrpr0 = ((option_bytes[2] >> 8) & 0xFF); + if (((uint8_t)(~nwrpr0)) == wrpr0) { + printf("Write protection 0: 0x%02x\r\n", wrpr0); + } else { + printf("Invalid write protection 0 value 0x%02x 0x%02x\r\n", wrpr0, nwrpr0); + } + + uint8_t wrpr1 = ((option_bytes[2] >> 16) & 0xFF); + uint8_t nwrpr1 = ((option_bytes[2] >> 24) & 0xFF); + if (((uint8_t)(~nwrpr1)) == wrpr1) { + printf("Write protection 1: 0x%02x\r\n", wrpr1); + } else { + printf("Invalid write protection 1 value 0x%02x 0x%02x\r\n", wrpr1, nwrpr1); + } + + uint8_t wrpr2 = ((option_bytes[3] >> 0) & 0xFF); + uint8_t nwrpr2 = ((option_bytes[3] >> 8) & 0xFF); + if (((uint8_t)(~nwrpr2)) == wrpr2) { + printf("Write protection 2: 0x%02x\r\n", wrpr2); + } else { + printf("Invalid write protection 2 value 0x%02x 0x%02x\r\n", wrpr2, nwrpr2); + } + + uint8_t wrpr3 = ((option_bytes[3] >> 16) & 0xFF); + uint8_t nwrpr3 = ((option_bytes[3] >> 24) & 0xFF); + if (((uint8_t)(~nwrpr3)) == wrpr3) { + printf("Write protection 3: 0x%02x\r\n", wrpr3); + } else { + printf("Invalid write protection 3 value 0x%02x 0x%02x\r\n", wrpr3, nwrpr3); + } + + return true; +} + +// Program and restart the CH32V20X +bool ch32v20x_program(rvswd_handle_t* handle, void const* firmware, size_t firmware_len, + ch32v20x_status_callback status_callback) { rvswd_result_t res; res = rvswd_init(handle); @@ -448,53 +538,52 @@ bool ch32_program(rvswd_handle_t* handle, void const* firmware, size_t firmware_ return false; } - res = ch32_reset_microprocessor_and_run(handle); + res = ch32v20x_reset_microprocessor_and_run(handle); if (res != RVSWD_OK) { ESP_LOGE(TAG, "Failed to reset target"); return false; } - res = ch32_halt_microprocessor(handle); + res = ch32v20x_halt_microprocessor(handle); if (res != RVSWD_OK) { ESP_LOGE(TAG, "Failed to halt target"); return false; } - bool bool_res = ch32_unlock_flash(handle); + bool bool_res = ch32v20x_unlock_flash(handle); if (!bool_res) { ESP_LOGE(TAG, "Failed to unlock flash"); return false; } - bool_res = ch32_clear_running_operations(handle); + bool_res = ch32v20x_clear_running_operations(handle); if (!bool_res) { ESP_LOGE(TAG, "Failed to clear target running operations"); return false; }; - bool_res = ch32_write_flash(handle, 0x08000000, firmware, firmware_len); + bool_res = ch32v20x_write_flash(handle, 0x08000000, firmware, firmware_len, status_callback); if (!bool_res) { ESP_LOGE(TAG, "Failed to write target flash"); return false; }; - bool_res = ch32_lock_flash(handle); + bool_res = ch32v20x_lock_flash(handle); if (!bool_res) { ESP_LOGE(TAG, "Failed to lock target flash"); return false; }; - res = ch32_reset_microprocessor_and_run(handle); + res = ch32v20x_reset_microprocessor_and_run(handle); if (res != RVSWD_OK) { ESP_LOGE(TAG, "Failed to reset target to run firmware"); return false; } - return true; -} + if (status_callback) { + status_callback("Programming done", 100); + } -// Default status callback implementation. -void __attribute__((weak)) ch32_status_callback(char const* msg, int progress, int total) { - ESP_LOGI(TAG, "%s: %d%% (%d/%d)", msg, progress * 100 / total, progress, total); + return true; }