FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

py,ports,tests,tools: Add AArch64 native emitter and inline assembler. · micropython/micropython@11bc11b · GitHub

py,ports,tests,tools: Add AArch64 native emitter and inline assembler. · micropython/micropython@11bc11b · GitHub
Skip to content

Navigation Menu

Commit 11bc11b

Browse files
committed
py,ports,tests,tools: Add AArch64 native emitter and inline assembler.
Add a complete AArch64 (ARMv8-A) assembler and native/Viper emitter to the MicroPython compiler. This enables runtime native code generation and an inline assembler for 64-bit ARM targets. Key additions include: - Core AArch64 assembler, native/Viper emitters, and instruction encoders in py/. - QEMU port support: VIRT_AARCH64 and VIRT_AARCH64_FLOAT boards, including startup, exception handling, timer, PL011 UART, and semihosting. Hardware-accelerated sqrt/sqrtf in libm. - Unix port support for AArch64 native/inline emitters. - Comprehensive test suite covering 64-bit constants, large stack frames, and large pointer offsets. - CI jobs for QEMU bare-metal and Unix AArch64 emulation. Note that persistent native .mpy loading is deliberately disabled for AArch64 because the current .mpy format has no available architecture IDs. This is deferred to the .mpy v7 toolchain work (see #19386). This change does not increase the code size for any other architecture, as the new emitters are only compiled when targeting AArch64. Signed-off-by: Stride Zhou <stride_anderson@qq.com>
1 parent 0414173 commit 11bc11b

105 files changed

Lines changed: 6194 additions & 36 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ports_qemu.yml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,17 @@ jobs:
7474
- name: Print failures
7575
if: failure()
7676
run: tests/run-tests.py --print-failures
77+
78+
build_and_test_aarch64:
79+
runs-on: ubuntu-latest
80+
steps:
81+
- uses: actions/checkout@v7
82+
- name: Install packages
83+
run: tools/ci.sh qemu_setup_aarch64
84+
- name: Add toolchain to PATH
85+
run: echo "$(tools/ci.sh qemu_aarch64_path)" >> $GITHUB_PATH
86+
- name: Build and run test suite
87+
run: tools/ci.sh qemu_build_aarch64
88+
- name: Print failures
89+
if: failure()
90+
run: tests/run-tests.py --print-failures

‎.github/workflows/ports_unix.yml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,27 @@ jobs:
409409
if: failure()
410410
run: tests/run-tests.py --print-failures
411411

412+
qemu_aarch64:
413+
runs-on: ubuntu-latest
414+
steps:
415+
- uses: actions/checkout@v7
416+
- uses: actions/setup-python@v7
417+
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
418+
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
419+
with:
420+
python-version: '3.11'
421+
- name: Install packages
422+
run: tools/ci.sh unix_qemu_aarch64_setup
423+
- name: Build
424+
run: tools/ci.sh unix_qemu_aarch64_build
425+
- name: Run main test suite
426+
run: tools/ci.sh unix_qemu_aarch64_run_tests
427+
- name: Run gcov coverage analysis
428+
run: tools/ci.sh unix_qemu_aarch64_run_coverage
429+
- name: Print failures
430+
if: failure()
431+
run: tests/run-tests.py --print-failures
432+
412433
sanitize_address:
413434
runs-on: ubuntu-latest
414435
steps:

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ primarily for maintenance, development and testing:
200200
to another microcontroller.
201201

202202
- The [qemu](ports/qemu) port is a QEMU-based emulated target for Cortex-A,
203-
Cortex-M, RISC-V 32-bit, RISC-V 64-bit, and PowerPC 64-bit architectures.
203+
Cortex-M, RISC-V 32-bit, RISC-V 64-bit, PowerPC 64-bit and AArch64 architectures.
204204

205205
The MicroPython cross-compiler, mpy-cross
206206
-----------------------------------------

‎docs/reference/asm_aarch64.rst‎

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
.. _asm_aarch64:
2+
3+
Inline assembler for AArch64
4+
============================
5+
6+
This document assumes some familiarity with assembly language programming
7+
and with the ARM 64-bit (AArch64/ARMv8-A) instruction set. For a detailed
8+
description of the instruction set consult the *Arm Architecture Reference
9+
Manual for A-profile architecture*.
10+
11+
The inline assembler is enabled on AArch64 targets (for example the unix
12+
port running on a 64-bit ARM system, or the ``VIRT_AARCH64`` boards of the
13+
``ports/qemu`` port) and is used via the ``@micropython.asm_aarch64``
14+
decorator::
15+
16+
@micropython.asm_aarch64
17+
def add1(x0) -> int:
18+
add(x0, x0, 1)
19+
20+
print(add1(1)) # prints 2
21+
22+
Instructions are written as Python function calls; operands are passed as
23+
arguments. The syntax tries to be as close as possible to that defined in
24+
the ARM manual, converted to Python function calls. Because ``and`` is a
25+
Python keyword, the AND instruction is spelled ``and_``.
26+
27+
Registers and calling convention
28+
--------------------------------
29+
30+
All 31 general purpose registers are available as ``x0``-``x30``, plus
31+
``sp`` and ``lr`` (an alias for ``x30``). All instructions operate on 64-bit
32+
values (the ``x`` registers); the 32-bit ``w`` registers are not accessible
33+
from the inline assembler.
34+
35+
A function may take up to four parameters, which must be named ``x0``,
36+
``x1``, ``x2``, ``x3`` in sequence; the arguments passed by the caller are
37+
available in those registers. Unless another return type is annotated, the
38+
value in ``x0`` when control reaches the end of the function body is treated
39+
as a signed integer result.
40+
41+
``sp`` is only accepted as an operand of ``push`` and ``pop``; every other
42+
instruction expects ``x0``-``x30`` (or ``lr``). Adjust the stack with those
43+
two operations rather than with ``add``/``sub``.
44+
45+
.. warning::
46+
47+
Do not use ``ret()`` to return from the assembly function itself. Leave
48+
the value in ``x0`` and let control fall off the end of the body, so that
49+
the automatically generated epilogue runs. A ``ret()`` at the top level
50+
skips that epilogue, which leaves the stack pointer 96 bytes too low and
51+
``x19``-``x28`` unrestored, corrupting the caller. ``ret()`` is only for
52+
returning from a subroutine reached with ``bl()``, as in the example
53+
below.
54+
55+
The function wrapper saves and restores all callee-saved registers
56+
(``x19``-``x28``) as well as the frame pointer and link register, so user
57+
assembly code may freely use any register. Note that the compiler may use
58+
``x9``/``x10`` as scratch registers when materialising a large immediate
59+
for ``and_``/``eor``/``orr`` that cannot be encoded directly (see below);
60+
this never affects the operands of the instruction being assembled.
61+
62+
Instructions
63+
------------
64+
65+
The following subset of the AArch64 instruction set is supported.
66+
67+
Arithmetic and logical
68+
~~~~~~~~~~~~~~~~~~~~~~
69+
70+
* ``add(Rd, Rn, Rm)`` / ``add(Rd, Rn, imm)`` — ``Rd = Rn + Rm`` or ``Rd = Rn + imm``
71+
* ``sub(Rd, Rn, Rm)`` / ``sub(Rd, Rn, imm)`` — ``Rd = Rn - Rm`` or ``Rd = Rn - imm``
72+
* ``mul(Rd, Rn, Rm)`` — ``Rd = Rn * Rm`` (signed and unsigned, low 64 bits)
73+
* ``and_(Rd, Rn, Rm)`` / ``and_(Rd, Rn, imm)`` — bitwise AND
74+
* ``orr(Rd, Rn, Rm)`` / ``orr(Rd, Rn, imm)`` — bitwise OR
75+
* ``eor(Rd, Rn, Rm)`` / ``eor(Rd, Rn, imm)`` — bitwise exclusive OR
76+
* ``mvn(Rd, Rm)`` — bitwise NOT
77+
* ``cmp(Rn, Rm)`` / ``cmp(Rn, imm)`` — compare (sets the condition flags)
78+
* ``mov(Rd, Rn)`` / ``mov(Rd, imm)`` — move register or 64-bit immediate
79+
80+
For ``add``/``sub``/``cmp`` the immediate must be a 12-bit value
81+
(``0``-``0xfff``), optionally shifted left by 12 bits (i.e. a multiple of
82+
4096 up to ``0xfff000``).
83+
84+
For ``and_``/``eor``/``orr`` any 64-bit immediate is accepted. Immediates
85+
that match the ARM "bitmask immediate" encoding (a rotated run of one bits
86+
replicated across the register, e.g. ``0xff``, ``0xf0``, ``0xffffffff``,
87+
``~0xf``) are emitted as a single instruction; other values are
88+
materialised in a scratch register first.
89+
90+
For ``mov`` any 64-bit immediate is accepted; the shortest ``MOVZ``/``MOVN``/
91+
``MOVK`` sequence is generated.
92+
93+
Shifts
94+
~~~~~~
95+
96+
* ``lsl(Rd, Rn, Rm)`` / ``lsl(Rd, Rn, imm)`` — logical shift left
97+
* ``lsr(Rd, Rn, Rm)`` / ``lsr(Rd, Rn, imm)`` — logical shift right
98+
* ``asr(Rd, Rn, Rm)`` / ``asr(Rd, Rn, imm)`` — arithmetic shift right
99+
100+
The immediate shift amount must be in the range 0-63.
101+
102+
Load and store
103+
~~~~~~~~~~~~~~
104+
105+
* ``ldr(Rt, [Rn, off])`` — load 64-bit word; ``off`` must be a multiple of 8, in 0-32760
106+
* ``ldrh(Rt, [Rn, off])`` — load 16-bit halfword (zero extend); ``off`` must be a multiple of 2, in 0-8190
107+
* ``ldrb(Rt, [Rn, off])`` — load byte (zero extend); ``off`` in 0-4095
108+
* ``str(Rt, [Rn, off])`` / ``strh(Rt, [Rn, off])`` / ``strb(Rt, [Rn, off])`` — the store equivalents
109+
110+
The offset is a constant in bytes; unaligned offsets for ``ldr``/``str``/
111+
``ldrh``/``strh`` are a compile-time error.
112+
113+
Branches and labels
114+
~~~~~~~~~~~~~~~~~~~
115+
116+
* ``label(NAME)`` — define a label
117+
* ``b(label)`` — unconditional branch
118+
* ``bl(label)`` — branch with link (call); return with ``ret(lr)``
119+
* ``b<cc>(label)`` — conditional branch, where ``<cc>`` is one of:
120+
``eq``, ``ne``, ``cs``, ``cc``, ``mi``, ``pl``, ``vs``, ``vc``, ``hi``,
121+
``ls``, ``ge``, ``lt``, ``gt``, ``le``. ``bhs`` and ``blo`` are accepted as
122+
aliases for ``bcs`` and ``bcc``. The condition code is written directly
123+
after the ``b`` with no separator, because ``b.eq`` is not a valid Python
124+
identifier.
125+
* ``ret(Rn)`` — return to the address in ``Rn`` (usually ``lr``)
126+
* ``nop()``, ``wfi()``, ``bkpt()`` — no operation, wait for interrupt,
127+
breakpoint (``BRK #0``)
128+
129+
``b`` and ``bl`` have a native range of ±128MB and are always a single
130+
instruction. ``b<cc>`` has a native range of only ±1MB, so a conditional
131+
branch that is forwards, or backwards beyond ±1MB, is relaxed automatically to
132+
a two-instruction sequence (an inverted ``b<cc>`` over an unconditional ``b``),
133+
which also gives it a ±128MB range. Because the relaxation is decided from
134+
the branch direction rather than from whether the label is known yet, the
135+
generated code has the same size in every compiler pass.
136+
137+
Stack operations
138+
~~~~~~~~~~~~~~~~
139+
140+
* ``push({Ra, Rb, ...})`` — store the listed registers on the stack
141+
(decrementing ``sp``; an odd number of registers is padded with a dummy
142+
slot to keep ``sp`` 16-byte aligned)
143+
* ``pop({Ra, Rb, ...})`` — the inverse of ``push``
144+
145+
Example::
146+
147+
@micropython.asm_aarch64
148+
def call_helper(x0) -> int:
149+
push({lr})
150+
bl(double)
151+
pop({lr})
152+
ret(lr)
153+
label(double)
154+
add(x0, x0, x0)
155+
ret(lr)
156+
157+
Limitations
158+
-----------
159+
160+
- Register-offset addressing (e.g. ``ldr(x0, [x1, x2])``) is not
161+
supported; only constant offsets. Pre-indexed (``[x1, #8]!``) and
162+
post-indexed (``[x1], #8``) forms cannot be written in Python at all.
163+
- ``push`` and ``pop`` round an odd number of registers up to an even number of
164+
stack slots, to keep ``sp`` 16-byte aligned. The padding slot is written
165+
with, and read back into, the zero register, so it never disturbs ``sp``.
166+
Matching ``push``/``pop`` lists therefore keep the stack balanced as long as
167+
they round to the same number of slots.
168+
- Floating point (SIMD/FP) registers and instructions are not supported.
169+
- There is currently no ``.mpy`` architecture ID for AArch64, so inline
170+
assembler functions cannot be frozen into ``.mpy`` files or loaded from
171+
them; they are compiled at runtime only.

‎docs/reference/index.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ implementation and the best practices to use them.
3030
manifest.rst
3131
packages.rst
3232
asm_thumb2_index.rst
33+
asm_aarch64.rst
3334
filesystem.rst
3435
romfs.rst
3536
unicode_support.rst

‎lib/libm/aarch64_sqrtf.c‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// An implementation of sqrtf for AArch64 using hardware fsqrt instruction.
2+
3+
#include <math.h>
4+
5+
float sqrtf(float x) {
6+
float ret;
7+
__asm__ volatile (
8+
"fsqrt %s0, %s1\n"
9+
: "=w" (ret)
10+
: "w" (x));
11+
return ret;
12+
}

‎lib/libm_dbl/aarch64_sqrt.c‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// An implementation of sqrt for AArch64 using hardware fsqrt instruction.
2+
3+
double sqrt(double x) {
4+
double ret;
5+
__asm__ volatile (
6+
"fsqrt %d0, %d1\n"
7+
: "=w" (ret)
8+
: "w" (x));
9+
return ret;
10+
}

‎ports/qemu/Makefile‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ MICROPY_HEAP_SIZE ?= 204800
4747
MICROPY_STACK_SIZE ?= 20480
4848
FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py',))"
4949
endif
50+
ifeq ($(QEMU_ARCH),aarch64)
51+
MICROPY_HEAP_SIZE ?= 1048576
52+
MICROPY_STACK_SIZE ?= 20480
53+
FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py',))"
54+
endif
5055

5156
MICROPY_FLOAT_IMPL ?= float
5257

@@ -195,6 +200,31 @@ SRC_BOARD_O += mcu/ppc64/head.o
195200

196201
endif
197202

203+
################################################################################
204+
# AArch64 specific settings
205+
206+
ifeq ($(QEMU_ARCH),aarch64)
207+
208+
CROSS_COMPILE ?= aarch64-none-elf-
209+
210+
LDFLAGS += -nostdlib
211+
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
212+
213+
QEMU_ARGS += -cpu max -semihosting
214+
215+
AFLAGS += -march=armv8-a
216+
CFLAGS += $(AFLAGS) -mstrict-align -fno-stack-protector -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0
217+
218+
SRC_C += \
219+
mcu/aarch64/errorhandler.c \
220+
mcu/aarch64/startup.c \
221+
mcu/aarch64/ticks.c \
222+
shared/runtime/semihosting_aarch64.c \
223+
224+
SRC_BOARD_O += mcu/aarch64/entrypoint.o
225+
226+
endif
227+
198228
################################################################################
199229
# Project specific settings and compiler/linker flags
200230

@@ -300,15 +330,23 @@ SRC_C += \
300330
ifeq ($(MICROPY_FLOAT_IMPL),double)
301331
LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C)
302332
ifeq ($(SUPPORTS_HARDWARE_FP_DOUBLE),1)
333+
ifeq ($(QEMU_ARCH),aarch64)
334+
LIBM_SRC_C += lib/libm_dbl/aarch64_sqrt.c
335+
else
303336
LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C)
337+
endif
304338
else
305339
LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_SW_C)
306340
endif
307341
else
308342
ifeq ($(MICROPY_FLOAT_IMPL),float)
309343
LIBM_SRC_C += $(SRC_LIB_LIBM_C)
310344
ifeq ($(SUPPORTS_HARDWARE_FP_SINGLE),1)
345+
ifeq ($(QEMU_ARCH),aarch64)
346+
LIBM_SRC_C += lib/libm/aarch64_sqrtf.c
347+
else
311348
LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C)
349+
endif
312350
else
313351
LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C)
314352
endif

‎ports/qemu/README.md‎

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
MicroPython port to qemu
22
========================
33

4-
This is experimental, community-supported port for Cortex-M and RISC-V
5-
RV32IMC/RV64IMC emulation as provided by QEMU (http://qemu.org).
4+
This is experimental, community-supported port for Cortex-M, RISC-V
5+
RV32IMC/RV64IMC, and AArch64 emulation as provided by QEMU (http://qemu.org).
66

77
The purposes of this port are to enable:
88

@@ -26,6 +26,13 @@ Dependencies
2626
For ARM-based boards the build requires a bare-metal ARM toolchain, such as
2727
`arm-none-eabi-gcc`.
2828

29+
### AArch64
30+
31+
For AArch64-based boards the build requires a bare-metal AArch64 toolchain, such as
32+
`aarch64-none-elf-gcc`. The QEMU `virt` machine is used with `-cpu max`, which
33+
enables all CPU features supported by the version of QEMU being used; the
34+
firmware itself is built for the base ARMv8-A instruction set.
35+
2936
### RISC-V 32
3037

3138
For RV32-based boards the build requires a bare metal RISC-V toolchain with GCC 10
@@ -101,16 +108,18 @@ different board pass the `BOARD` argument to `make`, for example:
101108

102109
Available boards are:
103110

104-
| Name for `BOARD=` | Architecture | Corresponding qemu board |
105-
| ----------------- | ------------ | ------------------------ |
106-
| `MICROBIT` | `arm` | `microbit` |
107-
| `MPS2_AN385` | `arm` | `mps2-an385` |
108-
| `MPS2_AN500` | `arm` | `mps2-an500` |
109-
| `NETDUINO2` | `arm` | `netduino2` |
110-
| `POWERNV9` | `ppc64` | `powernv9` |
111-
| `SABRELITE` | `arm` | `sabrelite` |
112-
| `VIRT_RV32` | `riscv32` | `virt` |
113-
| `VIRT_RV64` | `riscv64` | `virt` |
111+
| Name for `BOARD=` | Architecture | Corresponding qemu board |
112+
| -------------------- | ------------ | ------------------------ |
113+
| `MICROBIT` | `arm` | `microbit` |
114+
| `MPS2_AN385` | `arm` | `mps2-an385` |
115+
| `MPS2_AN500` | `arm` | `mps2-an500` |
116+
| `NETDUINO2` | `arm` | `netduino2` |
117+
| `POWERNV9` | `ppc64` | `powernv9` |
118+
| `SABRELITE` | `arm` | `sabrelite` |
119+
| `VIRT_AARCH64` | `aarch64` | `virt` |
120+
| `VIRT_AARCH64_FLOAT` | `aarch64` | `virt` |
121+
| `VIRT_RV32` | `riscv32` | `virt` |
122+
| `VIRT_RV64` | `riscv64` | `virt` |
114123

115124
Running
116125
-------
@@ -120,7 +129,8 @@ To access the REPL directly use:
120129

121130
$ make repl
122131

123-
This will start `qemu-system-arm` (or `qemu-system-riscv32`) with the UART
132+
This will start `qemu-system-arm` (or `qemu-system-riscv32`, or
133+
`qemu-system-aarch64`) with the UART
124134
redirected to stdio. It's also possible to redirect the UART to a pty device
125135
using:
126136

0 commit comments

Comments
 (0)

Footer

© 2026 GitHub, Inc.

Back | FazBrowse Home | New Git URL