Skip to content
Draft
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
66 changes: 65 additions & 1 deletion boards.txt
Original file line number Diff line number Diff line change
Expand Up @@ -689,4 +689,68 @@ 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.false.postbuild_debug=
nano_connect.menu.debug.true.postbuild_debug=-debug

nano_connect.build.zephyr_target=arduino_nano_rp2040_connect
nano_connect.build.zephyr_args=
nano_connect.build.zephyr_hals=hal_rpi_pico
nano_connect.build.variant=arduino_nano_rp2040_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.platform.path}/extra/bin2uf2/bin2uf2" "{build.path}/{build.project_name}.elf-zsk.bin" "{build.path}/{build.project_name}.uf2"
nano_connect.build.architecture=cortex-m0plus
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.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

##############################################################################################################
1 change: 1 addition & 0 deletions extra/bin2uf2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin2uf2
3 changes: 3 additions & 0 deletions extra/bin2uf2/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/pennam/bin2uf2

go 1.21
186 changes: 186 additions & 0 deletions extra/bin2uf2/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package main

import (
"encoding/binary"
"flag"
"fmt"
"io"
"os"
"strconv"
"strings"
"unsafe"
)

// UF2 block constants with fixed-size types.
const (
magic1 uint32 = 0x0A324655
magic2 uint32 = 0x9E5D5157
magic3 uint32 = 0x0AB16F30
flags uint32 = 0x00002000 // familyID present
payloadSize uint32 = 256
blockSize uint32 = 512
dataSectionSize uint32 = 476
)

// UF2Block defines the structure of a UF2 block, used as a data container.
// The Payload array is sized to hold the entire data section, so the unused
// portion of the array acts as our padding.
type UF2Block struct {
Magic1 uint32
Magic2 uint32
Flags uint32
TargetAddr uint32
PayloadSize uint32
BlockNo uint32
NumBlocks uint32
FamilyID uint32
Payload [dataSectionSize]byte
Magic3 uint32
}

// Calculate the offset of the NumBlocks field within the block struct.
const numBlocksOffset = unsafe.Offsetof(UF2Block{}.NumBlocks)

func main() {
// Define optional string flags for address and family ID
addrStr := flag.String("addr", "0x100E0000", "The starting memory address in hexadecimal format.")
familyIDStr := flag.String("familyID", "0xe48bff56", "The family ID of the target device in hexadecimal format.")

// Customize the default usage message to be more explicit.
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [options] <source file> <destination file>\n", os.Args[0])
fmt.Fprintln(os.Stderr, "Converts a binary file to the UF2 format.")
fmt.Fprintln(os.Stderr, "\nOptions:")
flag.PrintDefaults()
}

flag.Parse()

// Check for the correct number of positional arguments.
if len(flag.Args()) != 2 {
flag.Usage()
os.Exit(1)
}

// Parse the address string from the flag.
parsedAddr, err := strconv.ParseUint(strings.TrimPrefix(*addrStr, "0x"), 16, 32)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: Invalid address format: %v\n", err)
os.Exit(1)
}
address := uint32(parsedAddr)

// Parse the familyID string from the flag.
parsedFamilyID, err := strconv.ParseUint(strings.TrimPrefix(*familyIDStr, "0x"), 16, 32)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: Invalid familyID format: %v\n", err)
os.Exit(1)
}
familyID := uint32(parsedFamilyID)

srcPath := flag.Arg(0)
dstPath := flag.Arg(1)

// Open source file
src, err := os.Open(srcPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: Could not open source file %s: %v\n", srcPath, err)
os.Exit(1)
}
defer src.Close()

// Create destination file
dst, err := os.Create(dstPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: Could not create destination file %s: %v\n", dstPath, err)
os.Exit(1)
}
defer dst.Close()

var blockNum uint32
var totalBlocks uint32
// This slice is a temporary buffer for reading one payload-worth of data.
readBuffer := make([]byte, payloadSize)

// Main loop to read source and write UF2 blocks
for {
bytesRead, err := io.ReadFull(src, readBuffer)
if err == io.EOF {
break
}
if err != nil && err != io.ErrUnexpectedEOF {
fmt.Fprintf(os.Stderr, "Error: Failed reading from source file %s: %v\n", srcPath, err)
os.Exit(1)
}

// Zero out the unused portion of the buffer for partial reads
for i := bytesRead; i < int(payloadSize); i++ {
readBuffer[i] = 0
}

// Create the block struct and populate its fields.
block := UF2Block{
Magic1: magic1,
Magic2: magic2,
Flags: flags,
TargetAddr: address,
PayloadSize: payloadSize,
BlockNo: blockNum,
NumBlocks: 0, // Placeholder, will be updated later.
FamilyID: familyID,
Magic3: magic3,
}
// Copy the data from our read buffer into the beginning of the
// larger Payload array. The rest of the array remains zero, acting as padding.
copy(block.Payload[:], readBuffer)

// --- Write the block to disk piece-by-piece ---
// 1. Write the header fields
binary.Write(dst, binary.LittleEndian, block.Magic1)
binary.Write(dst, binary.LittleEndian, block.Magic2)
binary.Write(dst, binary.LittleEndian, block.Flags)
binary.Write(dst, binary.LittleEndian, block.TargetAddr)
binary.Write(dst, binary.LittleEndian, block.PayloadSize)
binary.Write(dst, binary.LittleEndian, block.BlockNo)
binary.Write(dst, binary.LittleEndian, block.NumBlocks)
binary.Write(dst, binary.LittleEndian, block.FamilyID)

// 2. Write the entire 476-byte data section (payload + padding) in one go.
if _, err := dst.Write(block.Payload[:]); err != nil {
fmt.Fprintf(os.Stderr, "Error: Failed writing data section to %s: %v\n", dstPath, err)
os.Exit(1)
}

// 3. Write the final magic number
if err := binary.Write(dst, binary.LittleEndian, block.Magic3); err != nil {
fmt.Fprintf(os.Stderr, "Error: Failed writing final magic to %s: %v\n", dstPath, err)
os.Exit(1)
}

address += payloadSize
blockNum++

if err == io.EOF || bytesRead < int(payloadSize) {
break
}
}

totalBlocks = blockNum

// After writing all blocks, seek back and update the totalBlocks field in each header
for i := uint32(0); i < totalBlocks; i++ {
// Calculate the offset using our safe constant instead of a magic number.
offset := int64(i)*int64(blockSize) + int64(numBlocksOffset)
_, err := dst.Seek(offset, io.SeekStart)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: Failed seeking in destination file %s: %v\n", dstPath, err)
os.Exit(1)
}
if err := binary.Write(dst, binary.LittleEndian, totalBlocks); err != nil {
fmt.Fprintf(os.Stderr, "Error: Failed updating total blocks in %s: %v\n", dstPath, err)
os.Exit(1)
}
}

fmt.Printf("Successfully converted %s to %s (%d blocks written).\n", srcPath, dstPath, totalBlocks)
}
2 changes: 1 addition & 1 deletion extra/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,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 Down
46 changes: 46 additions & 0 deletions loader/fixups.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,52 @@ 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_SOC_STM32H747XX_M7)
int enable_bkp_access(void) {
/* Enable access to the backup domain */
Expand Down
4 changes: 2 additions & 2 deletions loader/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ CONFIG_ARM_MPU=n
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y

CONFIG_HEAP_MEM_POOL_SIZE=32768
CONFIG_HEAP_MEM_POOL_SIZE=100000
CONFIG_MAIN_STACK_SIZE=32768

CONFIG_ARDUINO_API=y

CONFIG_LLEXT=y
CONFIG_LLEXT_LOG_LEVEL_ERR=y
CONFIG_LLEXT_HEAP_SIZE=32
CONFIG_LLEXT_HEAP_SIZE=100
CONFIG_LLEXT_STORAGE_WRITABLE=y
CONFIG_LLEXT_EXPORT_DEVICES=y

Expand Down
12 changes: 10 additions & 2 deletions platform.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ compiler.zephyr.common_cxxflags=-fdata-sections -ffunction-sections -fno-unwind-
compiler.zephyr.common_ldflags=-fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-unwind-tables -fno-use-cxa-atexit -lstdc++ -lsupc++ -lnosys -nostdlib

compiler.zephyr.extra_cxxflags=
compiler.zephyr.extra_ldflags=-lstdc++ -lsupc++
compiler.zephyr.extra_ldflags=-lstdc++ -lsupc++ -lgcc

# these can be overriden in boards.txt
build.extra_flags=
Expand Down Expand Up @@ -245,7 +245,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
Loading