Skip to content

Rich Memory Layout - #36

Merged
aq1018 merged 84 commits into
mainfrom
feat/rich-memory-layout
Jun 13, 2026
Merged

Rich Memory Layout#36
aq1018 merged 84 commits into
mainfrom
feat/rich-memory-layout

Conversation

@aq1018

@aq1018 aq1018 commented May 12, 2026

Copy link
Copy Markdown
Contributor

Replaces the flat per-chip memory: block (only main flash + SRAM) with a family-shared description of the full on-chip flash address space — user flash (split into zero-wait USR_1 and non-zero-wait USR_2 where the silicon has both), system bootloader, option bytes, vendor block, RAM — including per-region programming modes and access permissions. Generated metapac exposes the full shape for HALs and flash algorithms via METADATA.memory, and renders per-option memory.x linker scripts at the consumer's build time, with layout variants selectable via Cargo features.

Partially addresses #26.

Motivation

Several downstream projects need richer flash-region metadata than the old flat BANK_1 + SRAM block provided:

  • ch32-rs/flash-algorithms is being rewritten on top of metapac so probe-rs can flash every supported chip from a single shared implementation. That requires correct per-region page/erase/write sizes for both fast and standard modes — not just the main bank.
  • ch32-hal's flash driver can use the same region+mode metadata to support option-byte updates and bootloader-area writes through one generic API instead of per-chip constants.
  • OpenServoCore/tinyboot writes to flash at runtime during firmware-update flows and benefits directly from the same metadata.

The old schema couldn't express any of this — it modelled only the main user bank, mixed page_size into write_size on most chips, and had no notion of system/option/vendor regions.

Highlights

  • Family-level memory files shared by every chip in the family. Sub-families with one or two diverging regions reuse a _common.yaml parent via recursive include_memory (cycle-detected, append-only at every layer).
  • Per-region Fast / Standard programming modes pulled straight from the RMs.
  • Per-region Access { read, write, execute }.
  • Two mutually-exclusive ways for a chip YAML to specialise the family layout:
    • memory_sizes: — flat per-region size overrides for fixed-layout parts.
    • memory_ram_code_config: — declares an OB-selectable code/RAM split (SRAM_CODE_MODE); the resolver auto-creates USR_2 at USR_1.address + code with size total_flash - code.
  • User flash splits into USR_1 (silicon zero-wait region) + USR_2 (non-zero-wait); the linker-script renderer auto-merges them into a single USR region when contiguous, or keeps them split when an OB-driven SRAM-backed swap region falls between them.
  • Three Cargo-feature namespaces, two of them orthogonal — see "Selecting a layout" below.
  • Rolls in RM-correctness fixes along the way (V2xx standard erase_size 1024 → 4096, write_size page-sized → 2-byte half-word).
  • ./d dump-memory-x <chip> dumps every (config × split-combo) to disk for spot-checking.

ARM target support: CH32F103

Bundled into this PR is initial support for CH32F1 (C6T6 / C8T6 / R8T6) — WCH's ARM Cortex-M3 line, and the first non-RISC-V family in metapac. A new arch field on chip YAMLs, an arch-aware postprocess_pac_rs, and a thumbv7m-none-eabi build in CI mean metapac is no longer RISC-V-only.

Chip family coverage

Family Status Notes
CH32F1 ✅ Migrated F103C6T6 / F103C8T6 / F103R8T6 — first ARM (Cortex-M3) family
CH32V003 ✅ Migrated
CH32V00X ✅ Migrated CH32V002 / V004 / V005 / V006 / V007
CH32V1xx ✅ Migrated
CH32V20x ✅ Migrated V203 / V205 / V207 / V208
CH32V30x ✅ Migrated V303 / V305 / V307 / V317
CH32X0 ✅ Migrated
CH32L1 ✅ Migrated
CH32H4 ✅ Migrated H415REU6 / H416RDU6 / H417QEU6 / H417MEU6 / H417WEU6 — dual-core V3F+V5F, DBMODE-selectable flash
CH641 ✅ Migrated CH32V003 core + USB PD
CH643 ✅ Migrated CH32X0 core + USB PD
CH56x ⏳ TODO Incomplete peripheral data — filtered out of CI
CH57x ⏳ TODO Incomplete peripheral data — filtered out of CI
CH58x ⏳ TODO Incomplete peripheral data — filtered out of CI
CH59x ⏳ TODO Incomplete peripheral data — filtered out of CI
CH645 ⏳ TODO Incomplete peripheral data — filtered out of CI

Family files

Memory layouts live under data/memory/:

data/memory/
├── CH32L1.yaml
├── CH32V003.yaml
├── CH32V00X.yaml
├── CH32V1.yaml
├── CH32V2_V3.yaml      # shared common: USR_1, SYS_1, OPT, VND, RAM
├── CH32V2_X6.yaml      # include_memory: CH32V2_V3.yaml; + USR_2 @ 0x08008000 / 192K
├── CH32V2_X8.yaml      # include_memory: CH32V2_V3.yaml; + USR_2 @ 0x08010000 / 160K
├── CH32V3_XB.yaml      # include_memory: CH32V2_V3.yaml; + USR_2 @ 0x08028000 /  96K
└── CH32X0.yaml

Common file (data/memory/CH32V2_V3.yaml):

memory:
  - name: USR_1
    kind: flash
    address: 0x08000000
    modes:
      - { type: fast, page_size: 256, load_size: 4 }
      - { type: standard, erase_size: 4K, write_size: 2 }
    access: { read: true, write: true, execute: true }
  - name: SYS_1
    kind: flash
    address: 0x1FFF8000
    size: 28K
    modes: [ ... ]
    access: { read: true, write: true, execute: true }
  - name: OPT
    kind: flash
    address: 0x1FFFF800
    size: 128
    modes:
      - { type: standard, erase_size: 128, write_size: 2 }
    access: { read: true, write: true, execute: false }
  - name: VND
    kind: flash
    address: 0x1FFFF700
    size: 256
    access: { read: true, write: false, execute: false }
  - name: RAM
    kind: ram
    address: 0x20000000
    access: { read: true, write: true, execute: true }

Sub-family (data/memory/CH32V3_XB.yaml):

include_memory: CH32V2_V3.yaml
memory:
  - name: USR_2
    kind: flash
    address: 0x08028000
    modes:
      - { type: fast, page_size: 256, load_size: 4 }
      - { type: standard, erase_size: 4K, write_size: 2 }
    access: { read: true, write: true, execute: true }

Chip file — fixed layout (CH32V305FBP6.yaml)

include_memory: ../memory/CH32V3_XB.yaml
memory_sizes: { USR_1: 128K, RAM: 32K }

Chip file — OB-selectable split (CH32V203RBT6.yaml)

include_memory: ../memory/CH32V2_V3.yaml
# OB SRAM_CODE_MODE: 00x=128K/64K (default), 01x=144K/48K, 1xx=160K/32K
memory_ram_code_config:
  total_flash: 224K
  default: c128_r64
  configs:
    - { name: c128_r64, code: 128K, ram: 64K }
    - { name: c144_r48, code: 144K, ram: 48K }
    - { name: c160_r32, code: 160K, ram: 32K }

The resolver auto-creates USR_2 (address USR_1 + code, size total_flash - code) for the OB-selectable case; chip YAMLs no longer declare USR_2 themselves.

Generated metadata

pub static METADATA: Metadata = Metadata {
    name: "CH32V203K8T6",
    memory: &[
        MemoryRegion {
            name: "USR_1",
            kind: MemoryRegionKind::Flash,
            address: 0x8000000,
            size: 65536,
            modes: &[
                Fast { page_size: 256, load_size: 4 },
                Standard { erase_size: 4096, write_size: 2 },
            ],
            access: Some(Access { read: true, write: true, execute: true }),
        },
        MemoryRegion { name: "SYS_1", /* ... */ },
        MemoryRegion { name: "OPT",   /* ... */ },
        MemoryRegion { name: "VND",   /* ... */ },
        MemoryRegion { name: "RAM",   /* ... */ },
        MemoryRegion {
            name: "USR_2",
            kind: MemoryRegionKind::Flash,
            address: 0x8010000,
            size: 163840,
            /* ... */
        },
    ],
    memory_options: &[
        MemoryOption {
            name: "default",
            region_sizes: &[("RAM", 20480), ("USR_1", 65536)],
        },
    ],
    default_memory_option: "default",
    /* peripherals, interrupts, dma_channels */
};

Selecting a layout

Three Cargo-feature namespaces are emitted. memory-config-* and memory-x are orthogonal — config selection affects METADATA.memory, memory-x controls whether a linker script is rendered.

  • memory-config-<name> — selects which OB code/RAM split populates METADATA.memory (USR_1 / USR_2 / RAM sizes). Affects metadata regardless of whether memory-x is enabled. Enabling more than one is a build-time error.
  • memory-x — enables memory.x linker-script rendering at the consumer's build time, using the chip's default layout or the memory-config-<name> selection if one was made.
  • memory-split-<region> — exposes individual banks (e.g. USR_1 + USR_2) instead of the auto-merged region in the rendered linker script. One feature is emitted per multi-bank prefix; today only memory-split-usr exists. Only meaningful with memory-x, so it implies memory-x.
[dependencies]
# Default OB split (128K code / 64K RAM), linker script enabled:
ch32-metapac = { version = "...", features = ["ch32v203rbt6", "memory-x"] }

# Pin metadata to the 144/48 split (linker script also rendered against this split if memory-x is on):
ch32-metapac = { version = "...", features = ["ch32v203rbt6", "memory-x", "memory-config-c144_r48"] }

# Just want metadata sizes for the 144/48 split, no linker script:
ch32-metapac = { version = "...", features = ["ch32v203rbt6", "memory-config-c144_r48"] }

# Default split, expose USR_1 and USR_2 separately in the linker script:
ch32-metapac = { version = "...", features = ["ch32v203rbt6", "memory-x", "memory-split-usr"] }

Rendered memory.x for ["ch32v203rbt6", "memory-x", "memory-config-c144_r48"]:

MEMORY
{
    CODE   (rx)  : ORIGIN = 0x00000000, LENGTH =  144K /* USR_1 boot alias */
    USR_1  (rwx) : ORIGIN = 0x08000000, LENGTH =  144K
    USR_2  (rwx) : ORIGIN = 0x08028000, LENGTH =   96K
    SYS    (rwx) : ORIGIN = 0x1FFF8000, LENGTH =   28K
    VND    (r)   : ORIGIN = 0x1FFFF700, LENGTH =   256
    OPT    (rw)  : ORIGIN = 0x1FFFF800, LENGTH =   128
    RAM    (rwx) : ORIGIN = 0x20000000, LENGTH =   48K
}

REGION_ALIAS("FLASH", CODE);

REGION_ALIAS("REGION_TEXT", CODE);
REGION_ALIAS("REGION_RODATA", CODE);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);
REGION_ALIAS("REGION_HEAP", RAM);
REGION_ALIAS("REGION_STACK", RAM);

Pipeline cleanup

The legacy serialization shape is gone from the generator and runtime types:

  • chip::memory::Settings (parse-side) and FlashSettings (codegen/runtime) structs are removed entirely. MemoryRegion no longer carries a settings: field; per-region erase/write/page sizes are read from modes only.
  • primary_flash_regions now filters on USR_* exclusively; the BANK_* name fallback is dropped.
  • Stale settings: blocks remaining in the unmigrated CH5xx / CH645 YAMLs are silently ignored by serde at parse time — they don't compile in CI either way.

Deprecated pac-root constants

FLASH_BASE, FLASH_SIZE, and WRITE_SIZE (emitted at the pac root by the codegen) are now #[deprecated(...)] pointing at ch32_metapac::MEMORY_LAYOUT — an always-available const (no feature gate) exposing the chip's full memory geometry with const-fn accessors for page/erase/write sizes, role lookup, address-range checks, and access flags. Values are preserved — this is an advisory-only deprecation, no API breakage. Consumers should use MEMORY_LAYOUT to interpret region addresses, sizes, modes, roles, and access permissions in whatever way fits their use case; the same regions remain reachable as METADATA.memory under the metadata feature.

@aq1018
aq1018 marked this pull request as draft May 12, 2026 06:16
@aq1018

aq1018 commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

@andelf

You might want to take a closer look and see if I'm on the right path. This is a big change.

If you are OK with the direction this PR is going. I will port all the chips over and deprecate the settings, and top level constants, then I will proceed to create PRs for ch32-hal

Comment thread data/chips/CH32V203C6T6.yaml Outdated
@aq1018

aq1018 commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

Inviting @ExplodingWaffle for feedback as well.

@aq1018

aq1018 commented May 15, 2026

Copy link
Copy Markdown
Contributor Author

@andelf

FYI, I merged the CLI friendly OB/ESIG enhancement PR (#37) in.

I think everything is ready. Let me know if I should proceed.

@aq1018 aq1018 closed this May 15, 2026
@aq1018 aq1018 reopened this May 15, 2026
@aq1018 aq1018 mentioned this pull request May 17, 2026
@aq1018
aq1018 force-pushed the feat/rich-memory-layout branch from cc963d5 to 969752f Compare June 13, 2026 06:53
@aq1018
aq1018 merged commit ce44924 into main Jun 13, 2026
1 check passed
@aq1018
aq1018 deleted the feat/rich-memory-layout branch June 13, 2026 09:07
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.

2 participants