Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ cxxflags.txt
includes.txt
syms-dynamic.ld
syms-static.ld
tls-syms.S
69 changes: 68 additions & 1 deletion boards.txt
Original file line number Diff line number Diff line change
Expand Up @@ -757,4 +757,71 @@ unoq.bootloader.tool.default=remoteocd
unoq.bootloader.file=zephyr-{build.variant}.elf
unoq.bootloader.target=stm32u585zitxq

##########################################################################################
##############################################################################################################

nano_connect.name=Arduino Nano RP2040 Connect
nano_connect.build.core=arduino
nano_connect.build.crossprefix=arm-zephyr-eabi-
nano_connect.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/

nano_connect.menu.debug.false=Standard
nano_connect.menu.debug.true=Debug
nano_connect.menu.debug.true.build.zsk_args.debug=-debug

nano_connect.menu.link_mode.dynamic=Dynamic
nano_connect.menu.link_mode.static=Static
nano_connect.menu.link_mode.static.build.link_mode=static
nano_connect.menu.link_mode.static.upload.extension=bin-zsk.bin

nano_connect.build.zephyr_target=arduino_nano_connect
nano_connect.build.zephyr_args=
nano_connect.build.zephyr_hals=hal_rpi_pico
nano_connect.build.artifact=zephyr_main
nano_connect.build.variant=arduino_nano_connect_rp2040
nano_connect.build.mcu=cortex-m0plus
nano_connect.build.fpu=
nano_connect.build.architecture=cortex-m0plus
nano_connect.compiler.zephyr.arch.define=

nano_connect.build.float-abi=-mfloat-abi=soft
nano_connect.build.extra_flags=
nano_connect.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit
nano_connect.recipe.hooks.objcopy.postobjcopy.3.pattern="{runtime.tools.bin2uf2.path}/bin2uf2" {upload.address} {upload.familyid} "{build.path}/{build.project_name}.{upload.extension}" "{build.path}/{build.project_name}.uf2"
nano_connect.build.board=NANO_RP2040_CONNECT
nano_connect.compiler.zephyr=
nano_connect.vid.0=0x2341
nano_connect.pid.0=0x005E
nano_connect.upload_port.0.vid=0x2341
nano_connect.upload_port.0.pid=0x005E

nano_connect.upload.tool=picotool
nano_connect.upload.tool.default=picotool
nano_connect.upload.protocol=
nano_connect.upload.transport=
nano_connect.upload.vid=0x2341
nano_connect.upload.pid=0x005E
nano_connect.upload.interface=0
nano_connect.upload.use_1200bps_touch=true
nano_connect.upload.wait_for_upload_port=false
nano_connect.upload.native_usb=true
nano_connect.upload.maximum_size=16777216
nano_connect.upload.maximum_data_size=270336

nano_connect.upload.address=0x100E0000
nano_connect.upload.familyid=0xe48bff56

nano_connect.bootloader.tool=picotool
nano_connect.bootloader.tool.default=picotool
nano_connect.bootloader.vid=0x2341
nano_connect.bootloader.pid=0x005E
nano_connect.bootloader.interface=0
nano_connect.bootloader.file=zephyr-{build.variant}

nano_connect.debug.tool=gdb
nano_connect.debug.server.openocd.scripts.0=interface/{programmer.protocol}.cfg
nano_connect.debug.server.openocd.scripts.1={programmer.transport_script}
nano_connect.debug.server.openocd.scripts.2=target/rp2040-core0.cfg
nano_connect.debug.cortex-debug.custom.request=attach
nano_connect.debug.svd_file={runtime.platform.path}/svd/rp2040.svd

##############################################################################################################
19 changes: 19 additions & 0 deletions cores/arduino/llext_wrappers.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@
__real_##name(a); \
}

#ifdef CONFIG_ARM
/* ARM EABI thread pointer access */
W0(size_t, __aeabi_read_tp)

/*
* Thumb1 switch-dispatch helpers.
*
* These use a non-standard calling convention: BL sets LR to the jump table
* base (not a return address). With -mlong-calls the compiler lowers each
* void-void wrapper to a tail call (ldr r3, =addr; bx r3), leaving LR
* unchanged so the real function sees the original table pointer.
*/
V0(__gnu_thumb1_case_uqi)
V0(__gnu_thumb1_case_sqi)
V0(__gnu_thumb1_case_uhi)
V0(__gnu_thumb1_case_shi)
V0(__gnu_thumb1_case_si)
#endif

/* string.h */
W3(void *, memcpy, void *, const void *, size_t)
W3(void *, memmove, void *, const void *, size_t)
Expand Down
3 changes: 2 additions & 1 deletion extra/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ line_continuation='\\$' # match lines ending with '\'
c_comment='\s*\/\*.*?\*\/' # match C-style comments and any preceding space
perl -i -pe "s/${c_comment}//gs unless /${line_preproc_ok}/ || (/${line_comment_only}/ && !/${line_continuation}/)" $(find ${VARIANT_DIR}/llext-edk/include/ -type f)

for ext in elf bin hex; do
for ext in elf bin hex uf2; do
rm -f firmwares/zephyr-$variant.$ext
if [ -f ${BUILD_DIR}/zephyr/zephyr.$ext ]; then
cp ${BUILD_DIR}/zephyr/zephyr.$ext firmwares/zephyr-$variant.$ext
Expand All @@ -102,6 +102,7 @@ cp ${BUILD_DIR}/zephyr/.config firmwares/zephyr-$variant.config

# Generate the provides.ld file for linked builds
echo "Generating exported symbol scripts"
extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -T > ${VARIANT_DIR}/tls-syms.S
extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -L > ${VARIANT_DIR}/syms-dynamic.ld
extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -LF \
"+kheap_llext_heap" \
Expand Down
70 changes: 57 additions & 13 deletions extra/gen_provides.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,17 +122,29 @@ def main():
argparser.add_argument('-F', '--funcs',
action='store_true',
help='Extract all public functions')
argparser.add_argument('-T', '--tls-defs',
action='store_true',
help='Extract TLS symbol offsets to a .S file')
argparser.add_argument('file',
help='ELF file to parse')
argparser.add_argument('syms', nargs='*',
help='Symbols to export')

args = argparser.parse_intermixed_args()

wants_provides = bool(args.syms or args.funcs or args.llext)
if wants_provides and args.tls_defs:
sys.stderr.write("Cannot generate TLS defs when also generating PROVIDEs.\n")
sys.exit(1)
elif not wants_provides and not args.tls_defs:
sys.stderr.write("Nothing specified for export.\n")
sys.exit(1)

exact_syms = set()
regex_syms = set()
deref_syms = set()
sized_syms = set()
tls_syms = set() # tuple of (name, value, size)
rename_map = {}
for sym in args.syms:
sym_class = None
Expand Down Expand Up @@ -184,6 +196,11 @@ def main():
fail = False

for name, sym in all_syms.items():
if sym['st_info']['type'] == 'STT_TLS':
# Collect TLS symbol information, ignore for PROVIDEs
tls_syms.add((name, sym['st_value'], sym['st_size']))
continue

value = None
comment = []
if name in exact_syms or any(re.match(r, name) for r in regex_syms):
Expand Down Expand Up @@ -220,10 +237,6 @@ def main():
if name in sized_syms:
out_syms[name + "_size"] = (sym['st_size'], [f"size of {name}"])

if not out_syms:
sys.stderr.write("No symbols found matching the criteria.\n")
fail = True

if fail:
sys.exit(1)

Expand All @@ -235,16 +248,47 @@ def main():
* SHA256: {elf_sha}
*/
""")
sym_comment = nul_comment = ""
for name, (value, comments) in sorted(out_syms.items(), key=lambda x: x[0]):
if args.verbose:
comment = ', '.join(sorted(comments))
sym_comment = f"/* {comment} */"
nul_comment = f" ({comment})"
if value:
print(f"PROVIDE({name} = {value:#010x});{sym_comment}")

if wants_provides:
if not out_syms:
print("/* No symbols found matching the criteria */")
sys.stderr.write("warning: no symbols found matching the criteria.\n")
else:
print(f"/* NULL {name}{nul_comment} */")
sym_comment = nul_comment = ""
for name, (value, comments) in sorted(out_syms.items(), key=lambda x: x[0]):
if args.verbose:
comment = ', '.join(sorted(comments))
sym_comment = f"/* {comment} */"
nul_comment = f" ({comment})"
if value:
print(f"PROVIDE({name} = {value:#010x});{sym_comment}")
else:
print(f"/* NULL {name}{nul_comment} */")

if args.tls_defs:
# ARM and AArch64 use '@' as a line-comment character in GAS, so the
# assembler requires '%' as the type prefix there. All other targets use '@'.
prefix = '%' if elf['e_machine'] in ('EM_ARM', 'EM_AARCH64') else '@'

# The TLS Variant 1 layout (used by ARM/AArch64) places a Thread
# Control Block (TCB) before the TLS data. The thread pointer
# returned by __aeabi_read_tp() points to the TCB, so the actual
# runtime address of a TLS variable must be offset by that size.
#
# TCB is 2 pointers: 8 bytes on 32-bit, 16 bytes on 64-bit.
# See zephyr/arch/arm/core/tls.c: arch_tls_stack_setup().
tcb_size = (elf.elfclass // 8) * 2
print(f"/* Offsets include {tcb_size} bytes for TCB data */")

# Sort by offset first, then size
for name, offset, size in sorted(tls_syms, key=lambda x: (x[1], x[2])):
offset += tcb_size
print(
f"\n/* TLS offset {offset:#x}: {name} ({size} bytes) */\n"
f".global {name}\n"
f".type {name}, {prefix}tls_object\n"
f".set {name}, {offset}"
)

#-------------------------------------------------------------------------------
if __name__ == '__main__':
Expand Down
4 changes: 3 additions & 1 deletion libraries/SPI/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ void arduino::ZephyrSPI::detachInterrupt() {
}

void arduino::ZephyrSPI::begin() {
spi_dev->ops.init(spi_dev);
if (!device_is_ready(spi_dev)) {
spi_dev->ops.init(spi_dev);
}
}

void arduino::ZephyrSPI::end() {
Expand Down
47 changes: 47 additions & 0 deletions loader/fixups.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,53 @@ SYS_INIT(disable_bootloader_mpu, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAU
SYS_INIT(disable_mpu_rasr_xn, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
#endif

#if defined(CONFIG_BOARD_ARDUINO_NANO_RP2040_CONNECT)
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <pico/bootrom.h>

/*
* Double-tap reset detection: if the board is reset twice within 500ms,
* enter USB bootloader (BOOTSEL) mode. This mirrors the original
* ArduinoCore-mbed NANO_RP2040_CONNECT behavior.
*
* A magic token is stored in uninitialized RAM (.noinit), which survives
* a warm reset but is lost on power cycle. On boot:
* - If the token is present: a second reset happened quickly, so we
* clear the token and enter USB boot mode (never returns).
* - If not: write the token, wait 500ms, then clear it and boot normally.
*/
static const uint32_t magic_token[] = {
0xf01681de,
0xbd729b29,
0xd359be7a,
};

static uint32_t magic_location[3] __attribute__((section(".noinit.double_tap")));

#define NANO_RP2040_LED_PIN 6

int double_tap_check(void) {
if (magic_location[0] == magic_token[0] && magic_location[1] == magic_token[1] &&
magic_location[2] == magic_token[2]) {
magic_location[0] = 0;
reset_usb_boot(1 << NANO_RP2040_LED_PIN, 0);
/* never returns */
}

for (int i = 0; i < 3; i++) {
magic_location[i] = magic_token[i];
}

k_busy_wait(500000);

magic_location[0] = 0;
return 0;
}

SYS_INIT(double_tap_check, POST_KERNEL, 0);
#endif

#if defined(CONFIG_INPUT)
#include <zephyr/kernel.h>
#include <zephyr/device.h>
Expand Down
16 changes: 16 additions & 0 deletions loader/llext_exports.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,20 @@ EXPORT_SYMBOL(ring_buf_area_finish);
#endif

EXPORT_SYMBOL(sys_clock_cycle_get_32);

#if defined(CONFIG_ARM)
extern uint32_t __aeabi_read_tp(void);
EXPORT_LIBC_SYM(__aeabi_read_tp);
extern void __gnu_thumb1_case_uqi(void);
EXPORT_LIBC_SYM(__gnu_thumb1_case_uqi);
extern void __gnu_thumb1_case_sqi(void);
EXPORT_LIBC_SYM(__gnu_thumb1_case_sqi);
extern void __gnu_thumb1_case_uhi(void);
EXPORT_LIBC_SYM(__gnu_thumb1_case_uhi);
extern void __gnu_thumb1_case_shi(void);
EXPORT_LIBC_SYM(__gnu_thumb1_case_shi);
extern void __gnu_thumb1_case_si(void);
EXPORT_LIBC_SYM(__gnu_thumb1_case_si);
FORCE_EXPORT_SYM(__aeabi_dcmpun);
FORCE_EXPORT_SYM(__aeabi_dcmple);
FORCE_EXPORT_SYM(__aeabi_d2lz);
Expand Down Expand Up @@ -326,6 +340,8 @@ FORCE_EXPORT_SYM(__aeabi_idivmod);
FORCE_EXPORT_SYM(__aeabi_ldivmod);
FORCE_EXPORT_SYM(__aeabi_ul2f);
FORCE_EXPORT_SYM(__aeabi_dcmpge);
FORCE_EXPORT_SYM(__aeabi_lmul);
#endif

#if defined (CONFIG_CPP)
FORCE_EXPORT_SYM(__cxa_pure_virtual);
Expand Down
10 changes: 9 additions & 1 deletion platform.txt
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,15 @@ tools.picotool.path={runtime.tools.rp2040tools.path}
tools.picotool.cmd=rp2040load
tools.picotool.upload.params.verbose=-v
tools.picotool.upload.params.quiet=
tools.picotool.upload.pattern="{path}/{cmd}" {upload.verbose} -D "{build.path}/{build.project_name}.elf"
tools.picotool.upload.pattern="{path}/{cmd}" {upload.verbose} -D "{build.path}/{build.project_name}"

tools.picotool.erase.params.verbose=
tools.picotool.erase.params.quiet=
tools.picotool.erase.pattern=

tools.picotool.bootloader.params.verbose=-v
tools.picotool.bootloader.params.quiet=
tools.picotool.bootloader.pattern="{path}/{cmd}" {upload.verbose} -D "{runtime.platform.path}/firmwares/{bootloader.file}"

#
# IMGTOOL
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) Arduino s.r.l. and/or its affiliated companies
# SPDX-License-Identifier: Apache-2.0

CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Arduino Nano RP2040 Connect"
CONFIG_USB_DEVICE_MANUFACTURER="Arduino"
CONFIG_USB_DEVICE_VID=0x2341
CONFIG_USB_DEVICE_PID=0x005E

CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_UART_LINE_CTRL=y

CONFIG_USB_CDC_ACM=y
CONFIG_USB_CDC_ACM_RINGBUF_SIZE=1024
CONFIG_UART_LINE_CTRL=y
CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT=y

CONFIG_LLEXT_STORAGE_WRITABLE=n

CONFIG_HEAP_MEM_POOL_SIZE=16384
CONFIG_MAIN_STACK_SIZE=32768
CONFIG_LLEXT_HEAP_SIZE=64
CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1

CONFIG_LOG_BACKEND_UART=y
CONFIG_LOG_BACKEND_UART_AUTOSTART=n
CONFIG_LOG_DEFAULT_LEVEL=2

CONFIG_ADC=y
CONFIG_DAC=n
CONFIG_PWM=y

Loading
Loading