Skip to content

Commit e80b3cb

Browse files
committed
MicroPython: Removed Newlib dependency from headless builds. Streamline build process for readability.
1 parent 2126bac commit e80b3cb

7 files changed

Lines changed: 135 additions & 42 deletions

File tree

advanced/micropython/README.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ git submodule update --init lib/micropython-lib
1515

1616
The port supports three build modes:
1717

18-
### 1. REPL_SYSCALL (default)
18+
### 1. REPL_NEWLIB (default)
1919
Interactive REPL on host's stdio:
2020
```bash
2121
make
2222
# or explicitly
23-
make MODE=REPL_SYSCALL
23+
make MODE=REPL_NEWLIB
2424
```
2525

2626
### 2. HEADLESS
@@ -29,28 +29,40 @@ Executes a frozen Python script with no stdio (requires `FROZEN_SCRIPT`, default
2929
make MODE=HEADLESS FROZEN_SCRIPT=startup.py
3030
```
3131

32-
### 3. UART
32+
### 3. REPL_UART
3333
REPL over emulated UART, with an optional frozen initialization script (requires `FROZEN_SCRIPT`, defaults to `startup.py`):
3434
```bash
35-
make MODE=UART FROZEN_SCRIPT=startup.py
35+
make MODE=REPL_UART FROZEN_SCRIPT=startup.py
3636
```
3737

3838
## Build Options
3939

40-
- `MODE` - Build mode: `REPL_SYSCALL` (default), `HEADLESS`, or `UART`
41-
- `FROZEN_SCRIPT` - Python script to freeze into firmware (required for HEADLESS, optional for UART, defaults to `startup.py`)
40+
- `MODE` - Build mode: `REPL_NEWLIB` (default), `HEADLESS`, or `REPL_UART`
41+
- `FROZEN_SCRIPT` - Python script to freeze into firmware (required for HEADLESS, optional for REPL_UART, defaults to `startup.py`)
4242
- `RVM=1` - Enable RISC-V M extension (multiply/divide, default: enabled)
4343
- `RVA=0` - Enable RISC-V A extension (atomics, default: disabled)
4444
- `RVC=0` - Enable RISC-V C extension (compressed instructions, default: disabled)
4545

46+
## Build Characteristics
47+
48+
| Mode | Binary Size | Newlib | Float Support | Long Int Support |
49+
|------|-------------|--------|---------------|------------------|
50+
| REPL_NEWLIB | ~246 KB | Yes | Yes | Yes |
51+
| HEADLESS | ~194 KB | No | No | No |
52+
| REPL_UART | ~194 KB | No | No | No |
53+
54+
**HEADLESS and REPL_UART modes** are minimal builds with no Newlib dependencies (only libgcc). They use integer-only arithmetic and have no syscall overhead, resulting in smaller binaries.
55+
56+
**REPL_NEWLIB mode** uses full Newlib for syscall-based I/O and supports floating-point operations.
57+
4658
## Running
4759

4860
In the emulator's root directory. Prebuilt binary:
4961
```bash
5062
./riscv-emu.py --raw-tty --ram-size=4096 prebuilt/micropython.elf
5163
```
5264

53-
Compiled binary, REPL over stdio (REPL_SYSCALL default build mode):
65+
Compiled binary, REPL over stdio (REPL_NEWLIB default build mode):
5466
```bash
5567
./riscv-emu.py --raw-tty --ram-size=4096 advanced/micropython/port-riscv-emu.py/build/firmware.elf
5668
```
@@ -60,7 +72,7 @@ Compiled binary, headless (HEADLESS build mode):
6072
./riscv-emu.py ---ram-size=4096 advanced/micropython/port-riscv-emu.py/build/firmware.elf
6173
```
6274

63-
Compiled binary, REPL over UART (UART build mode):
75+
Compiled binary, REPL over UART (REPL_UART build mode):
6476
```bash
6577
./riscv-emu.py ---ram-size=4096 --uart advanced/micropython/port-riscv-emu.py/build/firmware.elf
6678
```

advanced/micropython/port-riscv-emu.py/Makefile

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ QSTR_DEFS = qstrdefsport.h
88
# MicroPython feature configurations
99
MICROPY_ROM_TEXT_COMPRESSION ?= 1
1010

11-
# Mode selection (default to REPL_SYSCALL for backwards compatibility)
12-
# Valid values: REPL_SYSCALL, HEADLESS, UART
13-
MODE ?= REPL_SYSCALL
11+
# Mode selection (default to REPL_NEWLIB for backwards compatibility)
12+
# Valid values: REPL_NEWLIB, HEADLESS, REPL_UART
13+
MODE ?= REPL_NEWLIB
1414

15-
# Frozen module support for HEADLESS and UART modes
15+
# Frozen module support for HEADLESS and REPL_UART modes
1616
# The frozen script is always named startup.py
1717
ifeq ($(MODE),HEADLESS)
1818
FROZEN_ENABLED = 1
19-
else ifeq ($(MODE),UART)
19+
else ifeq ($(MODE),REPL_UART)
2020
FROZEN_ENABLED = 1
21-
else ifeq ($(MODE),REPL_SYSCALL)
21+
else ifeq ($(MODE),REPL_NEWLIB)
2222
FROZEN_ENABLED = 0
2323
else
24-
$(error Invalid MODE=$(MODE). Valid values: REPL_SYSCALL, HEADLESS, UART)
24+
$(error Invalid MODE=$(MODE). Valid values: REPL_NEWLIB, HEADLESS, REPL_UART)
2525
endif
2626

2727
ifeq ($(FROZEN_ENABLED),1)
@@ -45,7 +45,12 @@ MPY_CROSS ?= $(TOP)/mpy-cross/build/mpy-cross
4545
MPY_TOOL ?= $(PYTHON) $(TOP)/tools/mpy-tool.py
4646
# Flags for mpy-cross and mpy-tool to match port configuration
4747
MPY_CROSS_FLAGS += -msmall-int-bits=31
48-
MPY_TOOL_FLAGS += -mlongint-impl=longlong
48+
ifeq ($(MODE),REPL_NEWLIB)
49+
MPY_TOOL_FLAGS += -mlongint-impl=longlong
50+
else
51+
# HEADLESS and REPL_UART use minimal integer implementation
52+
MPY_TOOL_FLAGS += -mlongint-impl=none
53+
endif
4954

5055
# Extension options - set to 1 to enable, 0 to disable
5156
# Note: the toolchain might not support all combinations
@@ -65,17 +70,45 @@ INC += -I$(BUILD)
6570
ifeq ($(CROSS), 1)
6671
DFU = $(TOP)/tools/dfu.py
6772
PYDFU = $(TOP)/tools/pydfu.py
68-
CFLAGS_RISCV = -march=$(MARCH) -mabi=ilp32 -D_REENT_SMALL
73+
74+
# Base RISC-V flags (bare-metal)
75+
CFLAGS_RISCV = -march=$(MARCH) -mabi=ilp32
76+
77+
# Define MODE constants for use in C and assembly
78+
CFLAGS_RISCV += -DMODE_REPL_NEWLIB=1 -DMODE_HEADLESS=2 -DMODE_REPL_UART=3
79+
80+
# Add Newlib-specific flags for REPL_NEWLIB mode
81+
ifeq ($(MODE),REPL_NEWLIB)
82+
CFLAGS_RISCV += -D_REENT_SMALL
83+
endif
84+
6985
CFLAGS += $(INC) -Wall -Werror -std=c99 $(CFLAGS_RISCV) $(COPT) -DMICROPY_PORT_MODE=MODE_$(MODE)
70-
LDFLAGS += -nostartfiles -static -Tlinker_newlib.ld --specs=nosys.specs
86+
87+
# Linker flags
88+
LDFLAGS += -nostartfiles -static -Tmicropython.ld
89+
ifeq ($(MODE),REPL_NEWLIB)
90+
LDFLAGS += --specs=nosys.specs
91+
else
92+
LDFLAGS += -nostdlib
93+
endif
7194
else
7295
UNAME_S := $(shell uname -s)
7396
LD = $(CC)
7497
CFLAGS += $(INC) -Wall -Werror -Wdouble-promotion -Wfloat-conversion -std=c99 $(COPT) -DMICROPY_PORT_MODE=MODE_$(MODE)
7598
ifeq ($(UNAME_S),Linux)
76-
LDFLAGS += -nostartfiles -Wl,--gc-sections -static -Tlinker_newlib.ld --specs=nano.specs -Wl,-map,$@.map -Wl,-dead_strip
99+
LDFLAGS += -nostartfiles -Wl,--gc-sections -static -Tmicropython.ld -Wl,-map,$@.map -Wl,-dead_strip
100+
ifeq ($(MODE),REPL_NEWLIB)
101+
LDFLAGS += --specs=nano.specs
102+
else
103+
LDFLAGS += -nostdlib
104+
endif
77105
else ifeq ($(UNAME_S),Darwin)
78-
LDFLAGS += -nostartfiles -Wl,--gc-sections -static -Tlinker_newlib.ld --specs=nano.specs -Wl,-map,$@.map -Wl,-dead_strip
106+
LDFLAGS += -nostartfiles -Wl,--gc-sections -static -Tmicropython.ld -Wl,-map,$@.map -Wl,-dead_strip
107+
ifeq ($(MODE),REPL_NEWLIB)
108+
LDFLAGS += --specs=nano.specs
109+
else
110+
LDFLAGS += -nostdlib
111+
endif
79112
endif
80113
endif
81114

@@ -99,7 +132,14 @@ endif
99132
# Flags for optional C++ source code
100133
CXXFLAGS += $(filter-out -std=c99,$(CFLAGS))
101134

102-
LIBS = -lm
135+
# Libraries based on mode
136+
ifeq ($(MODE),REPL_NEWLIB)
137+
# REPL_NEWLIB: Full Newlib with float support
138+
LIBS = -lm
139+
else
140+
# HEADLESS and REPL_UART: Minimal config (no floats, no long ints) - only libgcc
141+
LIBS = -lgcc
142+
endif
103143

104144
# Common source files for all build modes
105145
SRC_C = \
@@ -115,19 +155,24 @@ SRC_C = \
115155
extmod/machine_mem.c \
116156
extmod/moductypes.c
117157

158+
# Base assembly files (bare-metal)
118159
SRC_S = \
119-
start_newlib.S \
120-
syscalls_newlib.S \
160+
start.S \
121161
gchelper_rv32i.S
122162

163+
# Add syscall stubs for REPL_NEWLIB mode
164+
ifeq ($(MODE),REPL_NEWLIB)
165+
SRC_S += syscalls_newlib.S
166+
endif
167+
123168
# Select HAL based on build mode
124-
ifeq ($(MODE),REPL_SYSCALL)
169+
ifeq ($(MODE),REPL_NEWLIB)
125170
# Mode 1: syscall-based I/O
126171
SRC_C += mphalport.c
127172
else ifeq ($(MODE),HEADLESS)
128173
# Mode 2: headless (no stdio)
129174
SRC_C += mphalport_headless.c
130-
else ifeq ($(MODE),UART)
175+
else ifeq ($(MODE),REPL_UART)
131176
# Mode 3: UART MMIO I/O
132177
SRC_C += mphalport_uart.c
133178
endif

advanced/micropython/port-riscv-emu.py/main.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ int main(int argc, char *argv[]) {
2020
mp_obj_list_append(mp_sys_argv, mp_obj_new_str(argv[i], strlen(argv[i])));
2121
}
2222

23-
#if (MICROPY_PORT_MODE == MODE_REPL_SYSCALL)
24-
// Welcome message for syscall REPL mode
23+
#if (MICROPY_PORT_MODE == MODE_REPL_NEWLIB)
24+
// Welcome message for REPL with Newlib mode
2525
mp_printf(&mp_plat_print, "Welcome to MicroPython on RISC-V!\n");
2626
#endif
2727

@@ -30,8 +30,8 @@ int main(int argc, char *argv[]) {
3030
pyexec_frozen_module(FROZEN_MODULE_NAME, false);
3131
#endif
3232

33-
#if (MICROPY_PORT_MODE == MODE_REPL_SYSCALL) || \
34-
(MICROPY_PORT_MODE == MODE_UART)
33+
#if (MICROPY_PORT_MODE == MODE_REPL_NEWLIB) || \
34+
(MICROPY_PORT_MODE == MODE_REPL_UART)
3535
// Start REPL
3636
pyexec_friendly_repl();
3737
#endif

advanced/micropython/port-riscv-emu.py/linker_newlib.ld renamed to advanced/micropython/port-riscv-emu.py/micropython.ld

File renamed without changes.

advanced/micropython/port-riscv-emu.py/minimal_stubs.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1-
#include <stdio.h>
21
#include "py/obj.h"
32
#include "py/lexer.h"
43
#include "py/stream.h"
54
#include "py/runtime.h"
65
#include "py/builtin.h"
76
#include "py/mpprint.h"
7+
#include "py/mphal.h"
8+
#include "mpconfigport.h"
9+
#include <string.h>
10+
11+
#if (MICROPY_PORT_MODE == MODE_REPL_NEWLIB)
12+
#include <stdio.h>
13+
#define FATAL_ERROR(msg) do { printf("FATAL: %s\n", msg); while(1); } while(0)
14+
#else
15+
// Bare-metal: no stdio.h (HEADLESS and REPL_UART modes)
16+
static void fatal_error(const char *msg) __attribute__((noreturn));
17+
static void fatal_error(const char *msg) {
18+
mp_hal_stdout_tx_strn("FATAL: ", 7);
19+
mp_hal_stdout_tx_strn(msg, strlen(msg));
20+
mp_hal_stdout_tx_strn("\n", 1);
21+
while (1);
22+
}
23+
#define FATAL_ERROR(msg) fatal_error(msg)
24+
#endif
825

926
// Stub: If nlr fails, just halt.
1027
void nlr_jump_fail(void *val) {
11-
printf("FATAL: nlr_jump_fail\n");
12-
while (1);
28+
FATAL_ERROR("nlr_jump_fail");
1329
}
1430

1531
// Stub: We have no filesystem
@@ -19,7 +35,7 @@ mp_import_stat_t mp_import_stat(const char *path) {
1935

2036
// Stub: no file-based imports
2137
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
22-
printf("FATAL: mp_lexer_new_from_file() not supported\n");
38+
FATAL_ERROR("mp_lexer_new_from_file() not supported");
2339
return NULL;
2440
}
2541

advanced/micropython/port-riscv-emu.py/mpconfigport.h

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,29 @@
77
// options to control how MicroPython is built
88

99
// Mode definitions (set via Makefile)
10-
#define MODE_REPL_SYSCALL 1 // Interactive REPL with syscalls
10+
#define MODE_REPL_NEWLIB 1 // Interactive REPL with Newlib syscalls
1111
#define MODE_HEADLESS 2 // Frozen script execution, no stdio
12-
#define MODE_UART 3 // Frozen init script + UART REPL
12+
#define MODE_REPL_UART 3 // Frozen init script + UART REPL
1313

1414
#ifndef MICROPY_PORT_MODE
15-
#define MICROPY_PORT_MODE MODE_REPL_SYSCALL
15+
#define MICROPY_PORT_MODE MODE_REPL_NEWLIB
1616
#endif
1717

1818
// Use CORE_FEATURES ROM level as base, then explicitly enable what we need
1919
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES)
2020

21-
// Float support enabled for all modes
22-
#define MICROPY_PY_BUILTINS_FLOAT (1)
23-
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
24-
#define MICROPY_PY_MATH (1)
25-
#define MICROPY_PY_CMATH (0)
21+
// Float support - disabled for HEADLESS and REPL_UART to eliminate libc dependencies
22+
#if (MICROPY_PORT_MODE == MODE_REPL_NEWLIB)
23+
#define MICROPY_PY_BUILTINS_FLOAT (1)
24+
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
25+
#define MICROPY_PY_MATH (1)
26+
#define MICROPY_PY_CMATH (0)
27+
#else
28+
#define MICROPY_PY_BUILTINS_FLOAT (0)
29+
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE)
30+
#define MICROPY_PY_MATH (0)
31+
#define MICROPY_PY_CMATH (0)
32+
#endif
2633

2734
#define MICROPY_ENABLE_COMPILER (1)
2835
#define MICROPY_ENAVLE_REPL (1)
@@ -36,6 +43,12 @@
3643
#define MICROPY_ENABLE_EXTERNAL_IMPORT (0)
3744
#define MICROPY_KBD_EXCEPTION (1)
3845

46+
// Use MicroPython's internal printf for HEADLESS and REPL_UART (non-Newlib builds)
47+
#if (MICROPY_PORT_MODE != MODE_REPL_NEWLIB)
48+
#define MICROPY_USE_INTERNAL_PRINTF (1)
49+
#define MICROPY_INTERNAL_PRINTF_PRINTER (&mp_plat_print)
50+
#endif
51+
3952
// MICROPY_MODULE_FROZEN_MPY is automatically defined by the manifest system, when needed
4053

4154
// Enable core modules
@@ -45,7 +58,12 @@
4558
#define MICROPY_PY_GC (1)
4659
#define MICROPY_PY_BUILTINS_STR_UNICODE (1)
4760

48-
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG)
61+
// Integer implementation - minimal for HEADLESS and REPL_UART to reduce dependencies
62+
#if (MICROPY_PORT_MODE == MODE_REPL_NEWLIB)
63+
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG)
64+
#else
65+
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE)
66+
#endif
4967
#define MICROPY_PY_BUILTINS_COMPLEX (0)
5068
#define MICROPY_PY_IO (0) // no file system or streams
5169

advanced/micropython/port-riscv-emu.py/start_newlib.S renamed to advanced/micropython/port-riscv-emu.py/start.S

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ z_sbss:
2828
addi a0, a0, 4
2929
blt a0, a1, z_sbss
3030

31+
#if (MICROPY_PORT_MODE == MODE_REPL_NEWLIB)
3132
# initialize Newlib
3233
la a0, _impure_ptr
3334
lw a0, 0(a0)
3435
call __sinit
36+
#endif
3537

3638
# restore a0 and a1
3739
mv a0, s0

0 commit comments

Comments
 (0)