Skip to content

Conversation

chmousset
Copy link
Contributor

Adds sync API for most CH32 parts.

Tested on 208 and 307 (see examples)

@khionu
Copy link

khionu commented Aug 15, 2025

Is there any particular blocker to merging this? I need this for embassy-boot support

@Codetector1374
Copy link
Member

Codetector1374 commented Aug 15, 2025 via email

@chmousset
Copy link
Contributor Author

@Codetector1374 all done on my side.
FYI the the ch32v305 examples fail to build on nightly on my machine too, but builds successfully on nightly-2025-06-01

Maybe pinning the rust version in the CI would be a good idea

@Codetector1374
Copy link
Member

Yeah let me work on fixing that soon, then we can merge

@Codetector1374
Copy link
Member

I pushed a fix onto main, I believe you need to do a rebase then it should pass

@ExplodingWaffle
Copy link
Contributor

Is there any particular blocker to merging this? I need this for embassy-boot support

FYI: these chips have a Weird flash erase value of 0xe339 which isn't quite compatible with embedded-storage's trait and therefore embassy-boot. see rust-embedded-community/embedded-storage#35 and also my embassy fork

(only just saw this, busy moving)

@khionu
Copy link

khionu commented Aug 29, 2025

Changing that const in embassy-boot is actually a decent use case for cargo-patch. That would unblock people at least

@jsprog
Copy link

jsprog commented Aug 31, 2025

@chmousset
The available program flash for CH32V203 is 224KB, covering both zero-wait (cached) and the non-zero-wait flash

/* CH32V203C8T6 */
MEMORY
{
	FLASH : ORIGIN = 0x00000000, LENGTH = 64k
	RAM : ORIGIN = 0x20000000, LENGTH = 20k
	/* Non Zero Wait Flash, 224K - 64K = 160K */
	FLASH1 : ORIGIN = 0x00010000, LENGTH = 160K
}

At least, we should be able to read/write/erase beyond the zero-wait area instead on depending on pub const FLASH_SIZE: usize = 65536; // from pac.rs

https://github.com/chmousset/rs-ch32-hal/blob/add_flash/src/flash/common.rs

impl<'d, MODE> Flash<'d, MODE> {
    /// Blocking read.
    ///
    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
    /// For example, to read address `0x0800_1234` you have to use offset `0x1234`.
    pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
        blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes)
    }

    /// Blocking write.
    ///
    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
    /// For example, to write address `0x0800_1234` you have to use offset `0x1234`.
    pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
        unsafe {
            blocking_write(
                FLASH_BASE as u32,
                FLASH_SIZE as u32,
                offset,
                bytes,
                write_chunk_unlocked,
            )
        }
    }

    /// Blocking erase.
    ///
    /// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.
    /// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.
    pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
        unsafe { blocking_erase(FLASH_BASE as u32, from, to, erase_sector_unlocked) }
    }
}

@chmousset
Copy link
Contributor Author

@jsprog TBH I didn't want to open that can of worms.

  • if ch32-data is wrong, or lack chip-specific information, we should create a PR in ch32-data
  • I'm all for giving the dev access to all the hardware available, but not to bless them with the opportunity to loose hours (if not days) trying to figure out why a certain portions of the code suddenly loose 50 or 75% of its performance

So really the only approach I think makes sense is the one taken with STM32, where the Flash sectors are exposed in ch32-data and handled in ch32-hal.
I would also add an optional feature to generate a linker script that places code in the entire Flash, which will force the dev to read the doc and be exposed to the (big) limitations that go with full access.

As plenty of WCH chips have non-zero wait Flash sectors, a generic approach would benefit tons of parts at once.

It's a bit more than I can chew on right now, hence the working-but-not-ideal solution I PRd here.

@jsprog
Copy link

jsprog commented Sep 1, 2025

@chmousset I understand that going beyond the non-zero-wait area is more involved and not just providing a different value instead of FLASH_SIZE.

According to a note in the reference manual:

  • The Fast programming related functions can only be placed in the zero-wait area FLASH. (256 page write/erase size)
  • Fast programming: This method uses page operation (recommended). After unlocking through a specific
    sequence, a single 256-byte program, 256-byte erase, 32K-byte erase, and full chip erase are performed.

Beyond the non-zero wait area there is the standard programming mode

  • Standard programming: This method is the default programming method (Compatible method). In this
    mode, the CPU executes programming in a single 2-byte manner, and executes erasure and entire chip erasure
    operations in a single 4K byte

Likely I'll bear with the current limits for storing serialized configuration and taking benefits from other embedded-storage wrappers for wear-leveling, kv-storage, and less erase cycles, and use the non-zero-wait area for logs keeping (custom code).

Finally, I'll appreciate if you could answer this question:

  • As they're using in-package separate flash. What's making half-word erase value 0xe339 instead of 0xFFFF?

@jsprog
Copy link

jsprog commented Sep 6, 2025

@chmousset
Copy link
Contributor Author

@jsprog

As they're using in-package separate flash. What's making half-word erase value 0xe339 instead of 0xFFFF?

Unfortunately I didn't find any clue in the documentation or online... like most people I found out when trying to read a blank device.
At least it's consistent across zero-wait and non-zero-wait FLASH (and probably system flash as well, although this one isn't writable).

Anyways, I gave more thoughts on the current API and its limitations.
I think we can start with this current PR, which obviously doesn't allow to access the non-zero-wait flash, and upgrade it later with access to both FLASH and maybe async aswell.
If we use a similar API as for the STM32 HAL the user code should remain the same when moving to the new API.

@Codetector1374 is there anything blocking this PR on your side?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants