Skip to content

Add ARMv5 and NUC980 support#66

Open
MDr164 wants to merge 3 commits intousbarmory:developmentfrom
MDr164:nuc980
Open

Add ARMv5 and NUC980 support#66
MDr164 wants to merge 3 commits intousbarmory:developmentfrom
MDr164:nuc980

Conversation

@MDr164
Copy link
Copy Markdown

@MDr164 MDr164 commented Mar 18, 2026

This adds the Nuvoton NUC980 and one of its reference boards the NuMaker-IIoT-NUC980 which is a low cost ARM9 (ARMv5) based SoC used in some iBMC aka industrial or 'edge' BMC solutions by several manufacturers. The datasheet and software (u-boot + Linux) are publicly available from Nuvoton. I'll add a guide on how to test this on hardware including logs a bit later once everything works as intended. Opening a draft PR for initial discussion.

@abarisani
Copy link
Copy Markdown
Contributor

Please target development branch.

@MDr164 MDr164 changed the base branch from master to development March 18, 2026 16:18
@abarisani
Copy link
Copy Markdown
Contributor

I am not sure I like the overrides and linkhwinit0 flag (the less build flags there are the better), I will ponder a cleaner (at least to me) approach.

Go has the GOARM variable which maybe is exposed by runtime to have better conditionals in both .go and .s files? It should be set differently for this target I think.

I will check this and propose a different layout.

Ideally I would like to keep the current ARMv8 code as identical as possible to the current tested one.

@MDr164 MDr164 mentioned this pull request Mar 18, 2026
@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Mar 18, 2026

Yes, I was also wondering if there is a better approach to split of the ARMv5 quirks and I'm not fully contempt with this solution either. It does compile fine and I do get code execution. It still gets stuck somewhere on my board so I'm currently debugging this with JTAG but the development pipeline of using JTAG with Go code is still a bit tricky. It was easier to get working on my other port than on this one here.

@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Mar 18, 2026

It looks like there are somewhat underdocumented build tags I could use, so instead of adding new tags I would simply rely on GOARM=5 which should trigger //go:build arm.5

@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Mar 18, 2026

Added two commits to experiment with that existing build tag

@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Mar 24, 2026

Good news: I got everything working on real hardware now and got TamaGo talking to me on the UART! I did run into an issue with upstream Go though regarding ARMv5 atomics. Might be worth fixing downstream first I guess. I assume I can open a PR to tamago-go so we can discuss if this should go upstream or not?

Hello from TamaGo on NUC980!

Runtime   : go1.26.1 tamago/arm
Board     : NuMaker-IIoT-NUC980G2
SoC       : NUC980 PDID 0x0
CPU       : ARM926EJ-S MIDR 0x41069265
  impl    : 0x41 (ARM)
  variant : 0x0
  arch    : 0x6
  part    : 0x926
  rev     : 0x5
RAM       : 128 MB @ 0x00000000

tick 0
tick 1
tick 2
tick 3
tick 4

EDIT: This was the needed fix for ARMv5: MDr164/tamago-go@14103fd

@abarisani
Copy link
Copy Markdown
Contributor

Good news: I got everything working on real hardware now and got TamaGo talking to me on the UART! I did run into an issue with upstream Go though regarding ARMv5 atomics. Might be worth fixing downstream first I guess. I assume I can open a PR to tamago-go so we can discuss if this should go upstream or not?

Hello from TamaGo on NUC980!

Runtime   : go1.26.1 tamago/arm
Board     : NuMaker-IIoT-NUC980G2
SoC       : NUC980 PDID 0x0
CPU       : ARM926EJ-S MIDR 0x41069265
  impl    : 0x41 (ARM)
  variant : 0x0
  arch    : 0x6
  part    : 0x926
  rev     : 0x5
RAM       : 128 MB @ 0x00000000

tick 0
tick 1
tick 2
tick 3
tick 4

EDIT: This was the needed fix for ARMv5: MDr164/tamago-go@14103fd

Any tamago-go contribution must be good for potential upstreaming inclusion, to accept it I'd ask:

  • Do not remove the existing commented code (the TODO) from original Go.
  • Patch ·armcas in atomic_arm.s has it alread has a GOARM conditional
  • Explain why ·armcas in atomic_arm.s doesn't work as is? Interrupt handlers should never execute code within the Go runtime, so why is your implementation required?

As it stands your patch is not suitable as it is too specific to hardware and the Go upstream implementation has been designed to be agnostic.

As Go already supports ARM5 I'd like to understand what is the issue that you are trying to solve.

Thanks!

@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Mar 25, 2026

Thanks for the review, I updated the toolchain patch here now: MDr164/tamago-go@5aef39e

Do not remove the existing commented code (the TODO) from original Go.

Done

Patch ·armcas in atomic_arm.s has it alread has a GOARM conditional

Good idea, done

Explain why ·armcas in atomic_arm.s doesn't work as is? Interrupt handlers should never execute code within the Go runtime, so why is your implementation required?

I updated the commit message as well. I ran into some undefined instructions on my target as it's ARMv5 and TamaGo is non-linux so it ran into the TODO item I previously removed. Are you suggesting rewriting the interrupt handlers instead?

As Go already supports ARM5 I'd like to understand what is the issue that you are trying to solve.

I suppose it did not really support the situation of having ARMv5 under a non-linux OS working 100%. There was just this one spot in the atomics assembly which was only working for ARMv6+.

@abarisani
Copy link
Copy Markdown
Contributor

Thanks for the review, I updated the toolchain patch here now: MDr164/tamago-go@5aef39e

Do not remove the existing commented code (the TODO) from original Go.

Done

Patch ·armcas in atomic_arm.s has it alread has a GOARM conditional

Good idea, done

Explain why ·armcas in atomic_arm.s doesn't work as is? Interrupt handlers should never execute code within the Go runtime, so why is your implementation required?

I updated the commit message as well. I ran into some undefined instructions on my target as it's ARMv5 and TamaGo is non-linux so it ran into the TODO item I previously removed. Are you suggesting rewriting the interrupt handlers instead?

As Go already supports ARM5 I'd like to understand what is the issue that you are trying to solve.

I suppose it did not really support the situation of having ARMv5 under a non-linux OS working 100%. There was just this one spot in the atomics assembly which was only working for ARMv6+.

Looks much better thanks.

The interrupt handler should not run Go code which requires the runtime, please see the current pattern used in tamago and tamago-go.

In fact I am working on riscv64 interrupt handling as we speak for other RV64 targets which have just been included, so allow to me finish that and you can leverage on it.

Thanks!

@abarisani
Copy link
Copy Markdown
Contributor

abarisani commented Mar 26, 2026

Please see d7cf6ac, 1ad5e0c for interrupt handling.

An example implementation is at https://github.com/usbarmory/kotama/blob/5ea39b280fc7b3939d6d93132d276c1e043354fc/cmd/sifive_u.go#L64

@MDr164 MDr164 marked this pull request as ready for review March 27, 2026 17:23
@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Mar 27, 2026

I'm not yet 100% certain the newly added buildtags for either arm.5 or arm.6 are the best way to do this. Other than that I suppose the port is working pretty well now.

@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Mar 27, 2026

Btw I created a small CLI tool that is able to create images that can directly be flashed to the onboard flash or an sd-card. This board does require some DDR2 training data to be present. I pulled that data from their GitHub.

@abarisani
Copy link
Copy Markdown
Contributor

Thanks, I'll start my review next week!

MDr164 added 3 commits April 14, 2026 18:21
Add build-tag-gated ARMv5 support to the ARM package:

- Gate exception handlers with arm.6 / !arm.6 build tags:
  exception.s (ARMv6+) uses banked SP MRS; new exception_v5.s
  uses excStack global and SYS-mode round-trip for user SP.

- Gate IRQ/FIQ/WFI assembly similarly: irq.s (arm.6) preserves
  CPSIE/CPSID/WFI; new irq_v5.s provides MRS/MSR equivalents
  and CP15 WFI for cores lacking these instructions.

- Gate Hwinit0: init.go (arm.6) enables VFP; new hwinit0.go
  (!arm.6) is a no-op for soft-float builds.

- Add CPU struct fields for ARMv5 compatibility: NoVBAR skips
  VBAR/MVBAR writes on cores with fixed vectors; override hooks
  allow replacing IRQ/FIQ/WFI assembly for non-standard cores.

- Guard initFeatures behind NoVBAR since CP15 ID registers are
  unavailable on ARMv5.

- Use MRS/MSR in exit() instead of CPSID for portability across
  all ARM architecture levels.

Signed-off-by: Marvin Drees <marvin.drees@9elements.com>
Add peripheral drivers for the Nuvoton NUC980 SoC (ARM926EJ-S):

- AIC: Advanced Interrupt Controller driver with enable/disable/EOI
  for 64 IRQ sources and software interrupt generation.

- UART: UART0 console driver (115200 8N1, 12 MHz XIN).

- Timer: ETimer0 as 1 MHz free-running counter for nanotime with
  software-extended 64-bit wrap tracking; ETimer1 as periodic
  interrupt source for scheduler tick.

- PRNG: CRYPTO engine PRNG driver seeded once, re-triggered per
  GetRandomData call for 256-bit key generation.

- Early init: SoC clock gates (AIC, timers, UART0) in assembly
  via EarlyInit, callable from the board cpuinit.

- ARM926EJ-S assembly helpers: MIDR read, CP15 WFI, IRQ/FIQ
  disable for the exit handler.

The ARM CPU struct sets NoVBAR for fixed exception vectors at
0x00000000.  IRQ/FIQ/WFI use the generic arm/irq_v5.s provided
by the GOARM=5 build tags.

Signed-off-by: Marvin Drees <marvin.drees@9elements.com>
Add board package for the Nuvoton NuMaker-IIoT-NUC980G2 evaluation
board (NUC980DK71YC, 128 MB DDR2).

- cpuinit.s (linkcpuinit): installs early exception vector stubs,
  calls nuc980.EarlyInit for SoC clock gates, configures board
  pin mux (GPF11/GPF12 for UART0), clears BSS, sets up the
  stack, and enters the Go runtime.

- Hwinit1: initializes the NUC980 SoC, configures ETimer1 for
  periodic scheduler interrupts, and enables IRQ_ETMR1.

- Console: UART0 printk with CR insertion for serial terminals.

- Memory: 128 MB DDR2 SDRAM.

Built with GOOS=tamago GOARCH=arm GOARM=5 -tags linkcpuinit.

Signed-off-by: Marvin Drees <marvin.drees@9elements.com>
@abarisani
Copy link
Copy Markdown
Contributor

Haven't forgotten about this, just a little swamped but I promise I'll get to it.

@MDr164
Copy link
Copy Markdown
Author

MDr164 commented Apr 15, 2026

No worries, I just noticed a merge conflict with the development branch so I just updated the code to match the past commits. I can at least report that the code has been working well so far and I'm right now working on more drivers and the other PR so I'm also busy in the meantime :)

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