From 0c84522e4f07097796cb2e3d8012ba6a9e087f67 Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 10 Apr 2026 11:07:31 +0100 Subject: [PATCH 1/3] [flash] Implement 4bytes address commands --- lib/flash/flash.hh | 51 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/lib/flash/flash.hh b/lib/flash/flash.hh index f23f2a6..08f464e 100644 --- a/lib/flash/flash.hh +++ b/lib/flash/flash.hh @@ -199,14 +199,24 @@ class Generic { return true; } - Option erase(uint32_t address, Opcode op = Opcode::SectorErase) { + template + Option erase(uint32_t address) { + static_assert(ADDR_SIZE == 3 || ADDR_SIZE == 4, "Only 3 or 4 byte addresses supported"); + static_assert( + (ADDR_SIZE == 3 && (OP == Opcode::SectorErase || OP == Opcode::BlockErase32k || + OP == Opcode::BlockErase64k)) || + (ADDR_SIZE == 4 && (OP == Opcode::SectorErase4b || OP == Opcode::BlockErase32k4b || + OP == Opcode::BlockErase64k4b)), + "Opcode should match address size"); + write_enable(); - std::array cmd = { - op, - static_cast(address >> 16), - static_cast(address >> 8), - static_cast(address >> 0), - }; + + std::array cmd = {OP}; + for (size_t i = 0; i < ADDR_SIZE; ++i) { + // This calculates the correct shift (24, 16, 8, 0 for 4-byte; 16, 8, 0 for 3-byte) + cmd[1 + i] = static_cast(address >> (8 * (ADDR_SIZE - 1 - i))); + } + auto ret = spih.transfer(cmd, std::span()); if (embeddedpp::is_error(ret)) { return std::nullopt; @@ -214,6 +224,12 @@ class Generic { return wait_not_busy(); } + Option enter_4b_addr() { + std::array cmd = {Opcode::Enter4bAddr}; + TRY_OPT(spih.transfer(cmd, std::span())); + return true; + } + Option reset() { std::array cmd = {Opcode::Reset}; TRY_OPT(spih.transfer(cmd, std::span())); @@ -233,14 +249,23 @@ class Generic { return ret[0]; } + template Option single_page_program(uint32_t address, std::span data) { + static_assert(ADDR_SIZE == 3 || ADDR_SIZE == 4, "Only 3 or 4 byte addresses supported"); + + Opcode op; + if constexpr (ADDR_SIZE == 3) { + op = Opcode::PageProgram; + } else { + op = Opcode::PageProgram4b; + } write_enable(); - std::array cmd = { - Opcode::PageProgram, - static_cast(address >> 16), - static_cast(address >> 8), - static_cast(address >> 0), - }; + + std::array cmd = {op}; + for (size_t i = 0; i < ADDR_SIZE; ++i) { + // This calculates the correct shift (24, 16, 8, 0 for 4-byte; 16, 8, 0 for 3-byte) + cmd[1 + i] = static_cast(address >> (8 * (ADDR_SIZE - 1 - i))); + } auto slice = std::span(cmd).last<256>(); From 24a9f4fafa07b6da922c939ddd629e2080ce008e Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 10 Apr 2026 11:09:34 +0100 Subject: [PATCH 2/3] [app] Support the flash 4bytes address --- app/commands.hh | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app/commands.hh b/app/commands.hh index 5d7d3fa..c61d47d 100644 --- a/app/commands.hh +++ b/app/commands.hh @@ -218,9 +218,15 @@ struct LoadFile : public Commands { return 0; } + bool addr4b = this->start_addr > 0xFFFFFF; + if (!bootstrap) { this->flash.reset(); } + if (addr4b && !this->flash.enter_4b_addr()) { + std::println("Enter 4-byte address mode failed"); + return 0; + } if (quad && !this->flash.enable_quad(true)) { std::println("enable quad failed"); return 0; @@ -230,13 +236,19 @@ struct LoadFile : public Commands { auto progress_bar = ProgressBar(buffer.size(), 50, "Loading").with_throughput(); size_t addr = start_addr; while (data.size() > 0) { - if ((addr % flash::SectorSize) == 0 && !this->flash.erase(addr)) { - std::println("Failed to erase block {:#x}", addr); - return 0; + if ((addr % flash::SectorSize) == 0) { + auto erased = addr4b ? this->flash.template erase<4, flash::Opcode::SectorErase4b>(addr) + : this->flash.erase(addr); + if (!erased) { + std::println("Failed to erase block {:#x}", addr); + return 0; + } } std::optional res; - if (quad) { + if (addr4b) { + res = this->flash.template single_page_program<4>(addr, data.first()); + } else if (quad) { res = this->flash.quad_page_program(addr, data.first()); } else { res = this->flash.single_page_program(addr, data.first()); From ec489d1434ef386b8a03f3fcd4257c43f34fc31c Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 10 Apr 2026 11:12:01 +0100 Subject: [PATCH 3/3] [lint] Run clang-format --- app/main.cc | 3 ++- lib/ftdi/spi_host.hh | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/main.cc b/app/main.cc index 2237fce..e0ff356 100644 --- a/app/main.cc +++ b/app/main.cc @@ -141,7 +141,8 @@ int main(int argc, char* argv[]) { return 0; }; - auto test_page_cmd = new_flash_command("test-page", "Write a pattern to a page and read it back."); + auto test_page_cmd = + new_flash_command("test-page", "Write a pattern to a page and read it back."); test_page_cmd->add_argument("--addr") .help("The address to be loaded") .default_value(std::size_t{0}) diff --git a/lib/ftdi/spi_host.hh b/lib/ftdi/spi_host.hh index b1f071c..4cab2c6 100644 --- a/lib/ftdi/spi_host.hh +++ b/lib/ftdi/spi_host.hh @@ -28,8 +28,7 @@ class SpiHost { public: explicit SpiHost(FT_HANDLE handle, bool mpsse = false) noexcept : handle(handle), mpsse(mpsse), traces(false) {} - ~SpiHost() { - } + ~SpiHost() {} void close() { if (mpsse) {