Skip to content

feat: parametrize Cheshire for 32-bit (XLEN) cores - #287

Open
Xeratec wants to merge 7 commits into
mainfrom
wiesep/32bit-support
Open

Xeratec wants to merge 7 commits into
mainfrom
wiesep/32bit-support

Conversation

@Xeratec

@Xeratec Xeratec commented Jul 16, 2026 •

Copy link
Copy Markdown
Member

Generic (non-Chimera) changes extracted from the wiesep/chimera-main integration branch,
proposed for upstream. Generalizes Cheshire so it can be instantiated with a 32-bit core
(riscv::XLEN == 32), today hardcoded to 64. No functional change intended for the default
64-bit configuration.

HW

  • Derive RiscvWordWidth from riscv::XLEN instead of hardcoding 64 in every
    axi_riscv_atomics wrapper (reg, LLC, debug, DMA-conf).
  • Size boot_addr_i / hart_id_i to riscv::XLEN.
  • Add config fields TFLenWidth and AxiSlvIdWidth.

Bootrom

  • Guard the 64-bit boot-address handling on the compiler-provided __riscv_xlen.
    The RV64-only lwu is now inside that guard, with lw on RV32.

SW / build

  • crt0.S stays a single file, parametrized over __riscv_xlen via LREG/SREG/REGBYTES,
    with FP init guarded on __riscv_flen. Verified to produce byte-identical RV64 code to
    main, and to assemble for rv32imc/ilp32 and rv32imafc/ilp32f.
  • sw.mk: CHS_XLEN selects CHS_SW_GCC_TRIPLE / CHS_SW_MARCH / CHS_SW_MABI. Set
    CHS_SW_GCC_TRIPLE=riscv64-unknown-elf to build 32-bit SW with a multilib 64-bit toolchain.
  • cheshire.mk: new CHS_CVA6_CONFIG feeds CHS_BENDER_RTL_FLAGS, and CHS_XLEN is derived
    from it, so the RTL target and the SW build cannot disagree.
  • rv32 linker support in common.ldh; widths in dif/uart changed from uint64_t to
    uint32_t (valid on both XLENs).

Sim

  • vip_cheshire_soc: sbaccess and the SBA read stride follow riscv::XLEN, and the
    dm::Command write for repointing execution selects aarsize accordingly.
  • tb_cheshire_pkg: new embedded 32-bit configuration, selectable as SELCFG=4.

Draft: split out of the Chimera 64-bit upgrade work (pulp-platform/chimera#84).
Thanks to @ricted98 for the variable-XLEN prototype (54f4350) that the unified crt0.S,
the __riscv_xlen bootrom guards, and CHS_CVA6_CONFIG are based on.

@Xeratec Xeratec self-assigned this Jul 16, 2026
@ricted98

ricted98 commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Hi @Xeratec, this is an unprompted comment but I did something similar (but much more reduced in scope and quick and dirty) some time ago: 54f4350. I don't know if something can be complementary or be saved from it, for example it could be likely possible to have a unified crt0.S. Feel free to use this info as you like!

@Xeratec
Xeratec force-pushed the wiesep/32bit-support branch from bd4c2db to 6fbe675 Compare July 23, 2026 20:18
- Cleanup
- Add AXI Cut
@Xeratec Xeratec changed the title feat: parametrize Cheshire for 32-bit cores + SerialLink fix feat: parametrize Cheshire for 32-bit cores Aug 25, 2026
@Xeratec

Xeratec commented Aug 25, 2026

Copy link
Copy Markdown
Member Author

Hi @Xeratec, this is an unprompted comment but I did something similar (but much more reduced in scope and quick and dirty) some time ago: 54f4350. I don't know if something can be complementary or be saved from it, for example it could be likely possible to have a unified crt0.S. Feel free to use this info as you like!

Hi @ricted98, thanks for sharing this. I adopted some changes from you, cleaned the PR up a bit, and rebased it to main.

Applies the reviewable parts of ricted98's variable-XLEN prototype
(54f4350) on top of the 32-bit support branch.

- bootrom: guard on the compiler-provided `__riscv_xlen` instead of a
  never-defined `XLEN` macro. `#if XLEN==64` evaluated to false in every
  build, so the RV64 bootrom silently dropped the SCRATCH[5] high-word
  store and the boot address reconstruction. Also move the RV64-only
  `lwu` inside the guard and use `lw` on RV32.
- crt0: reunify `sw/lib/{32,64}/crt0.S` into a single `sw/lib/crt0.S`
  using LREG/SREG/REGBYTES and an `__riscv_flen` guard around FP init.
  Verified byte-identical codegen for RV64. Fixes three bugs that had
  crept into the duplicated 32-bit copy: a `-64`/`+128` stack imbalance
  in the trap epilogue, missing `.align 4` on `_start`, `_exit`, and
  `trap_vector`, and an unconditional RV64 D-extension FP init that
  could not assemble for RV32.
- sw.mk: collapse the duplicated per-XLEN toolchain blocks into a single
  `CHS_SW_GCC_TRIPLE`/`CHS_SW_MARCH`/`CHS_SW_MABI` parametrization, which
  also restores the `CHS_SW_DTC` assignment lost in the split and the
  `?=` override semantics of `CHS_SW_FLAGS`. Add `_zicsr_zifencei` to the
  RV32 march string, required by GCC >= 12.
- cheshire.mk: add `CHS_CVA6_CONFIG` and derive `CHS_XLEN` from it so the
  RTL target and the SW build cannot disagree.
- vip: replace the magic dm::Command constants with a named-field
  concatenation; drop a leftover per-byte UART debug $display.
- cheshire_soc: drop a stray blank line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Xeratec
Xeratec force-pushed the wiesep/32bit-support branch from 6fbe675 to 5f91ea6 Compare August 25, 2026 09:40
@Xeratec Xeratec changed the title feat: parametrize Cheshire for 32-bit cores feat: parametrize Cheshire for 32-bit (XLEN) cores Aug 25, 2026
Xeratec and others added 3 commits August 25, 2026 11:44
TbCheshireConfigs gained a fifth entry but NumCheshireConfigs stayed at 4,
so the concatenation was wider than the [3:0] target and truncated from the
MSB end. That silently dropped gen_cheshire_emb_cfg(), leaving the embedded
32-bit configuration unreachable via SelectedCfg.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Xeratec
Xeratec force-pushed the wiesep/32bit-support branch from 5409441 to 57fe3e2 Compare August 25, 2026 09:53
@Xeratec Xeratec added the enhancement New feature or request label Aug 25, 2026
@Xeratec
Xeratec force-pushed the wiesep/32bit-support branch from 8995afd to 4d5ee80 Compare August 25, 2026 10:22
@Xeratec
Xeratec marked this pull request as ready for review August 25, 2026 13:31
@Xeratec
Xeratec requested a review from creinwar August 25, 2026 13:32

@creinwar creinwar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Looks already quite nice, but I left a few comments/questions :)

Comment thread hw/bootrom/cheshire_bootrom.ld Outdated

SECTIONS {
__stack_pointer$ = ORIGIN(spm) + LENGTH(spm) - 8;
__stack_pointer$ = __stack_start - 8;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're touching this line, we could fix it to follow the actual RISC-V ABI (https://github.com/riscv-non-isa/riscv-elf-psabi-doc) which states:

The stack grows downwards (towards lower addresses) and the stack pointer shall be aligned to a
128-bit boundary upon procedure entry. The first argument passed on the stack is located at offset
zero of the stack pointer on function entry;

So therefore this should probably just become __stack_pointer$ = __stack_start; at which point we could drop either symbol.

Comment thread hw/cheshire_soc.sv
.boot_addr_i ( BootAddr ),
.hart_id_i ( 64'(i) ),
.boot_addr_i ( BootAddr[riscv::XLEN-1:0] ),
.hart_id_i ( riscv::XLEN'(i) ),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to have both RiscvWordWidth and riscv::XLEN in the code? Could we agree on one?

Comment thread sw/lib/dif/uart.c Outdated
}

static inline int __uart_write_ready(void *uart_base) {
static int __uart_write_ready(void *uart_base) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was the inline dropped intentionally? I assume most compilers would do it already automatically, but IMHO it doesn't hurt to explicitly mark it inline.

Comment thread sw/tests/helloworld.c
char str[] = "Hello World!\r\n";
uint32_t rtc_freq = CHS_REGS->rtc_freq.f.ref_freq;
uint64_t reset_freq = clint_get_core_freq(rtc_freq, 2500);
uint64_t reset_freq = clint_get_core_freq(rtc_freq, 2048);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason beyond "power-of-two == better"? 😂

Comment thread .gitignore
vivado*

# Emacs undo-tree files
*undo-tree*

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

Comment thread hw/cheshire_pkg.sv Outdated
AxiUserWidth : 2, // AMO(2)
AxiMstIdWidth : 2,
TFLenWidth : 32,
AxiSlvIdWidth : 3,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is AxiSlvIdWidth used? That should be a computed parameter.

Follows up on @creinwar's review of #287.

- cheshire_pkg: drop the unused `AxiSlvIdWidth` config field; the value is
  already computed in `cheshire_soc` as `AxiMstIdWidth + $clog2(AxiIn.num_in)`
  and every use site reads that localparam. Drop `TFLenWidth` on the same
  grounds: `cheshire_idma_wrap` uses its own local `TfLenWidth`, so the config
  field was never read either. Both restore `cheshire_pkg.sv` to upstream.
- cheshire_soc: settle on `riscv::XLEN` instead of carrying both it and a
  `RiscvWordWidth` localparam that merely aliased it.
- Linker scripts: drop the `- 8` from `__stack_pointer$` in both
  `cheshire_bootrom.ld` and `dram.ld`. The RISC-V psABI requires sp to be
  128-bit aligned on procedure entry; the offset left it 8-mod-16. Now
  0x10010000 and 0x80800000 respectively, both 16-byte aligned.
- crt0: round the entry frame up to a 16-byte multiple for the same reason.
  `4*REGBYTES` yields 32 B on RV64 and 16 B on RV32; the three saved registers
  keep their slots and the fourth is padding. `_exit` needs no change since it
  reloads the caller's sp from the frame rather than deallocating.
- uart: restore `inline` on `__uart_write_ready` and `__uart_write_idle`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants