rp2040: add make flash support

This adds `make flash` support for the rp2040 target. Flashing is
performed using a custom `rp2040_flash` tool that uses the PICOBOOT
protocol. Root is not required.

The user specifies the serial device of the rp2040 they wish to flash as
the device. This device is reset into bootsel mode and `rp2040_flash`
is invoked on the original USB device path.

If the device is already in bootloader mode, the user can specify
'first' as `FLASH_DEVICE` which will simply invoke `rp2040_flash` with
no bus/address options.

Signed-off-by: Lasse Dalegaard <dalegaard@gmail.com>
This commit is contained in:
Lasse Dalegaard
2022-01-03 23:28:54 +01:00
committed by KevinOConnor
parent 8a3727ef74
commit 7c0559c6e6
10 changed files with 1124 additions and 7 deletions

124
lib/rp2040/boot/picoboot.h Normal file
View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _BOOT_PICOBOOT_H
#define _BOOT_PICOBOOT_H
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#ifndef NO_PICO_PLATFORM
#include "pico/platform.h"
#endif
/** \file picoboot.h
* \defgroup boot_picoboot boot_picoboot
*
* Header file for the PICOBOOT USB interface exposed by an RP2040 in BOOTSEL mode.
*/
#define PICOBOOT_MAGIC 0x431fd10bu
// --------------------------------------------
// CONTROL REQUESTS FOR THE PICOBOOT INTERFACE
// --------------------------------------------
// size 0 OUT - unstall EPs and reset
#define PICOBOOT_IF_RESET 0x41
// size 16 IN - return the status of the last command
#define PICOBOOT_IF_CMD_STATUS 0x42
// --------------------------------------------------
// COMMAND REQUESTS SENT TO THE PICOBOOT OUT ENDPOINT
// --------------------------------------------------
//
// picoboot_cmd structure of size 32 is sent to OUT endpoint
// transfer_length bytes are transferred via IN/OUT
// device responds on success with 0 length ACK packet set via OUT/IN
// device may stall the transferring endpoint in case of error
enum picoboot_cmd_id {
PC_EXCLUSIVE_ACCESS = 0x1,
PC_REBOOT = 0x2,
PC_FLASH_ERASE = 0x3,
PC_READ = 0x84, // either RAM or FLASH
PC_WRITE = 5, // either RAM or FLASH (does no erase)
PC_EXIT_XIP = 0x6,
PC_ENTER_CMD_XIP = 0x7,
PC_EXEC = 0x8,
PC_VECTORIZE_FLASH = 0x9
};
enum picoboot_status {
PICOBOOT_OK = 0,
PICOBOOT_UNKNOWN_CMD = 1,
PICOBOOT_INVALID_CMD_LENGTH = 2,
PICOBOOT_INVALID_TRANSFER_LENGTH = 3,
PICOBOOT_INVALID_ADDRESS = 4,
PICOBOOT_BAD_ALIGNMENT = 5,
PICOBOOT_INTERLEAVED_WRITE = 6,
PICOBOOT_REBOOTING = 7,
PICOBOOT_UNKNOWN_ERROR = 8,
};
struct __packed picoboot_reboot_cmd {
uint32_t dPC; // 0 means reset into bootrom
uint32_t dSP;
uint32_t dDelayMS;
};
// used for EXEC, VECTORIZE_FLASH
struct __packed picoboot_address_only_cmd {
uint32_t dAddr;
};
// used for READ, WRITE, FLASH_ERASE
struct __packed picoboot_range_cmd {
uint32_t dAddr;
uint32_t dSize;
};
enum picoboot_exclusive_type {
NOT_EXCLUSIVE = 0,
EXCLUSIVE,
EXCLUSIVE_AND_EJECT
};
struct __packed picoboot_exclusive_cmd {
uint8_t bExclusive;
};
// little endian
struct __packed __aligned(4) picoboot_cmd {
uint32_t dMagic;
uint32_t dToken; // an identifier for this token to correlate with a status response
uint8_t bCmdId; // top bit set for IN
uint8_t bCmdSize; // bytes of actual data in the arg part of this structure
uint16_t _unused;
uint32_t dTransferLength; // length of IN/OUT transfer (or 0) if none
union {
uint8_t args[16];
struct picoboot_reboot_cmd reboot_cmd;
struct picoboot_range_cmd range_cmd;
struct picoboot_address_only_cmd address_only_cmd;
struct picoboot_exclusive_cmd exclusive_cmd;
};
};
static_assert(32 == sizeof(struct picoboot_cmd), "picoboot_cmd must be 32 bytes big");
struct __packed __aligned(4) picoboot_cmd_status {
uint32_t dToken;
uint32_t dStatusCode;
uint8_t bCmdId;
uint8_t bInProgress;
uint8_t _pad[6];
};
static_assert(16 == sizeof(struct picoboot_cmd_status), "picoboot_cmd_status must be 16 bytes big");
#endif

139
lib/rp2040/pico/platform.h Normal file
View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_PLATFORM_H_
#define _PICO_PLATFORM_H_
#include "hardware/platform_defs.h"
#include <stddef.h>
#ifdef __unix__
#include <sys/cdefs.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define __not_in_flash(grup)
#define __not_in_flash_func(func) func
#define __no_inline_not_in_flash_func(func)
#define __in_flash(group)
#define __scratch_x(group)
#define __scratch_y(group)
#define __packed_aligned
#define __packed
#define __time_critical_func(x) x
#define __after_data(group)
//int running_on_fpga() { return false; }
extern void tight_loop_contents();
#ifndef __STRING
#define __STRING(x) #x
#endif
#ifndef _MSC_VER
#ifndef __noreturn
#define __noreturn __attribute((noreturn))
#endif
#ifndef __unused
#define __unused __attribute__((unused))
#endif
#ifndef __noinline
#define __noinline __attribute__((noinline))
#endif
#ifndef __aligned
#define __aligned(x) __attribute__((aligned(x)))
#endif
#define PICO_WEAK_FUNCTION_DEF(x) _Pragma(__STRING(weak x))
#define PICO_WEAK_FUNCTION_IMPL_NAME(x) x
#else
#ifndef __noreturn
#define __noreturn __declspec(noreturn)
#endif
#ifndef __unused
#define __unused
#endif
#ifndef __noinline
#define __noinline __declspec(noinline)
#endif
#ifndef __aligned
#define __aligned(x) __declspec(align(x))
#endif
#ifndef __CONCAT
#define __CONCAT(x,y) x ## y
#endif
#define __thread __declspec( thread )
#define PICO_WEAK_FUNCTION_DEF(x) __pragma(comment(linker, __STRING(/alternatename:_##x=_##x##__weak)));
#define PICO_WEAK_FUNCTION_IMPL_NAME(x) x ## __weak
static __noreturn void __builtin_unreachable() {
}
#include <intrin.h>
#define __builtin_clz __lzcnt
#endif
#ifndef count_of
#define count_of(a) (sizeof(a)/sizeof((a)[0]))
#endif
#ifndef MAX
#define MAX(a, b) ((a)>(b)?(a):(b))
#endif
#ifndef MIN
#define MIN(a, b) ((b)>(a)?(a):(b))
#endif
// abort in our case
void __noreturn __breakpoint();
void __noreturn panic_unsupported();
void __noreturn panic(const char *fmt, ...);
// arggggghhhh there is a weak function called sem_init used by SDL
#define sem_init sem_init_alternative
extern uint32_t host_safe_hw_ptr_impl(uintptr_t x);
// return a 32 bit handle for a raw ptr; DMA chaining for example embeds pointers in 32 bit values
// which of course does not work if we're running the code natively on a 64 bit platforms. Therefore
// we provide this macro which allows that code to provide a 64->32 bit mapping in host mode
#define host_safe_hw_ptr(x) host_safe_hw_ptr_impl((uintptr_t)(x))
void *decode_host_safe_hw_ptr(uint32_t ptr);
#define __fast_mul(a,b) ((a)*(b))
typedef unsigned int uint;
static inline int32_t __mul_instruction(int32_t a,int32_t b)
{
return a*b;
}
static inline void __compiler_memory_barrier(void) {
}
#ifdef __cplusplus
}
#endif
#endif