mirror of
https://github.com/Klipper3d/klipper.git
synced 2025-08-06 21:44:13 -06:00
lib: Update rp2040_flash to upstream picotool.git v2.0.0
This is in preparation for adding rp2350 flash support. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
06bb49f135
commit
64ba37c02e
9 changed files with 877 additions and 78 deletions
|
@ -1,5 +1,5 @@
|
|||
CC=gcc
|
||||
CFLAGS=-c -Wall -ggdb
|
||||
CFLAGS=-c -Wall -ggdb -DHAS_LIBUSB
|
||||
LDFALGS=
|
||||
SOURCES=main.c picoboot_connection.c
|
||||
OBJECTS=$(SOURCES:.c=.o)
|
||||
|
|
94
lib/rp2040_flash/addresses.h
Normal file
94
lib/rp2040_flash/addresses.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
#ifndef _ADDRESSES_H
|
||||
#define _ADDRESSES_H
|
||||
|
||||
#define ROM_START 0x00000000 // same as ROM_BASE in addressmap.h
|
||||
#define ROM_END_RP2040 0x00004000
|
||||
#define ROM_END_RP2350 0x00008000
|
||||
// todo amy based on what sort of elf (also this breaks RP2040 builds?)
|
||||
#define FLASH_START 0x10000000 // same as XIP_MAIN_BASE in addressmap.h
|
||||
#define FLASH_END_RP2040 0x11000000 // +32 MiB -- remainder has no external devices mapped
|
||||
#define FLASH_END_RP2350 0x12000000 // +32 MiB -- remainder has no external devices mapped
|
||||
// todo amy based on what sort of elf
|
||||
#define XIP_SRAM_START_RP2040 0x15000000
|
||||
#define XIP_SRAM_END_RP2040 0x15004000
|
||||
#define XIP_SRAM_START_RP2350 0x13ffc000 // same as XIP_SRAM_BASE in addressmap.h
|
||||
#define XIP_SRAM_END_RP2350 0x14000000 // same as XIP_SRAM_END in addressmap.h
|
||||
|
||||
#define SRAM_START 0x20000000 // same as SRAM_BASE in addressmap.h
|
||||
#define SRAM_END_RP2040 0x20042000
|
||||
#define SRAM_END_RP2350 0x20082000
|
||||
// todo amy no more banked alias
|
||||
#define MAIN_RAM_BANKED_START 0x21000000
|
||||
#define MAIN_RAM_BANKED_END 0x21040000
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef IGNORE
|
||||
#endif
|
||||
|
||||
// Address ranges for RP2040/RP2350
|
||||
struct address_range {
|
||||
enum type {
|
||||
CONTENTS, // may have contents
|
||||
NO_CONTENTS, // must be uninitialized
|
||||
IGNORE // will be ignored
|
||||
};
|
||||
address_range(uint32_t from, uint32_t to, type type) : from(from), to(to), type(type) {}
|
||||
address_range() : address_range(0, 0, IGNORE) {}
|
||||
uint32_t from;
|
||||
uint32_t to;
|
||||
type type;
|
||||
};
|
||||
|
||||
typedef std::vector<address_range> address_ranges;
|
||||
|
||||
|
||||
const address_ranges rp2040_address_ranges_flash {
|
||||
address_range(FLASH_START, FLASH_END_RP2040, address_range::type::CONTENTS),
|
||||
address_range(SRAM_START, SRAM_END_RP2040, address_range::type::NO_CONTENTS),
|
||||
address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS)
|
||||
};
|
||||
|
||||
const address_ranges rp2040_address_ranges_ram {
|
||||
address_range(SRAM_START, SRAM_END_RP2040, address_range::type::CONTENTS),
|
||||
address_range(XIP_SRAM_START_RP2040, XIP_SRAM_END_RP2040, address_range::type::CONTENTS),
|
||||
address_range(ROM_START, ROM_END_RP2040, address_range::type::IGNORE) // for now we ignore the bootrom if present
|
||||
};
|
||||
|
||||
const address_ranges rp2350_address_ranges_flash {
|
||||
address_range(FLASH_START, FLASH_END_RP2350, address_range::type::CONTENTS),
|
||||
address_range(SRAM_START, SRAM_END_RP2350, address_range::type::NO_CONTENTS),
|
||||
address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS)
|
||||
};
|
||||
|
||||
const address_ranges rp2350_address_ranges_ram {
|
||||
address_range(SRAM_START, SRAM_END_RP2350, address_range::type::CONTENTS),
|
||||
address_range(XIP_SRAM_START_RP2350, XIP_SRAM_END_RP2350, address_range::type::CONTENTS),
|
||||
address_range(ROM_START, ROM_END_RP2350, address_range::type::IGNORE) // for now we ignore the bootrom if present
|
||||
};
|
||||
|
||||
static bool is_address_valid(const address_ranges& valid_ranges, uint32_t addr) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to > addr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_address_initialized(const address_ranges& valid_ranges, uint32_t addr) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to > addr) {
|
||||
return address_range::type::CONTENTS == range.type;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -12,7 +12,7 @@
|
|||
#include "picoboot_connection.h"
|
||||
#include "boot/uf2.h"
|
||||
|
||||
#define FLASH_MAX_SIZE (FLASH_END - FLASH_START)
|
||||
#define FLASH_MAX_SIZE (FLASH_END_RP2350 - FLASH_START)
|
||||
#define FLASH_NUM_WRITE_BLOCKS (FLASH_MAX_SIZE / PAGE_SIZE)
|
||||
#define FLASH_NUM_ERASE_BLOCKS (FLASH_MAX_SIZE / FLASH_SECTOR_ERASE_SIZE)
|
||||
|
||||
|
@ -62,7 +62,7 @@ int load_flash_data(const char *filename, struct flash_data *target) {
|
|||
|
||||
// Bounds and alignment checking
|
||||
if (block.target_addr != (block.target_addr & ~(PAGE_SIZE-1))) continue;
|
||||
if (block.target_addr > FLASH_END - PAGE_SIZE) continue;
|
||||
if (block.target_addr > FLASH_END_RP2350 - PAGE_SIZE) continue;
|
||||
if (block.target_addr < FLASH_START) continue;
|
||||
|
||||
uint32_t offset = block.target_addr - FLASH_START;
|
||||
|
@ -209,7 +209,8 @@ int main(int argc, char *argv[]) {
|
|||
if (target_bus != libusb_get_bus_number(*dev)) continue;
|
||||
if (target_address != libusb_get_device_address(*dev)) continue;
|
||||
}
|
||||
enum picoboot_device_result res = picoboot_open_device(*dev, &handle);
|
||||
model_t model;
|
||||
enum picoboot_device_result res = picoboot_open_device(*dev, &handle, &model, -1, -1, "");
|
||||
if (res == dr_vidpid_bootrom_ok) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -7,29 +7,32 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "picoboot_connection.h"
|
||||
#include "pico/bootrom_constants.h"
|
||||
|
||||
#if false && !defined(NDEBUG)
|
||||
#define output(format,...) printf(format, __VA_ARGS__)
|
||||
#if ENABLE_DEBUG_LOG
|
||||
#include <stdio.h>
|
||||
#define output(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define output(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
static bool verbose;
|
||||
static bool definitely_exclusive;
|
||||
static enum {
|
||||
XIP_UNKOWN,
|
||||
XIP_ACTIVE,
|
||||
XIP_INACTIVE,
|
||||
} xip_state;
|
||||
|
||||
// todo test sparse binary (well actually two range is this)
|
||||
|
||||
#define VENDOR_ID_RASPBERRY_PI 0x2e8au
|
||||
#define PRODUCT_ID_RP2_USBBOOT 0x0003u
|
||||
#define PRODUCT_ID_PICOPROBE 0x0004u
|
||||
#define PRODUCT_ID_MICROPYTHON 0x0005u
|
||||
#define PRODUCT_ID_STDIO_USB 0x000au
|
||||
|
||||
uint32_t crc32_for_byte(uint32_t remainder) {
|
||||
const uint32_t POLYNOMIAL = 0x4C11DB7;
|
||||
remainder <<= 24u;
|
||||
for (uint bit = 8; bit > 0; bit--) {
|
||||
for (unsigned int bit = 8; bit > 0; bit--) {
|
||||
if (remainder & 0x80000000)
|
||||
remainder = (remainder << 1) ^ POLYNOMIAL;
|
||||
else
|
||||
|
@ -38,47 +41,64 @@ uint32_t crc32_for_byte(uint32_t remainder) {
|
|||
return remainder;
|
||||
}
|
||||
|
||||
uint32_t crc32_sw(const uint8_t *buf, uint count, uint32_t crc) {
|
||||
uint32_t crc32_sw(const uint8_t *buf, unsigned int count, uint32_t crc) {
|
||||
static uint32_t table[0x100];
|
||||
if (!table[1]) {
|
||||
for (uint i = 0; i < count_of(table); i++) {
|
||||
for (unsigned int i = 0; i < count_of(table); i++) {
|
||||
table[i] = crc32_for_byte(i);
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
crc = (crc << 8u) ^ table[(uint8_t) ((crc >> 24u) ^ buf[i])];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint interface;
|
||||
uint out_ep;
|
||||
uint in_ep;
|
||||
unsigned int interface;
|
||||
unsigned int out_ep;
|
||||
unsigned int in_ep;
|
||||
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle) {
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, model_t *model, int vid, int pid, const char* ser) {
|
||||
struct libusb_device_descriptor desc;
|
||||
struct libusb_config_descriptor *config;
|
||||
|
||||
definitely_exclusive = false;
|
||||
*dev_handle = NULL;
|
||||
*model = unknown;
|
||||
int ret = libusb_get_device_descriptor(device, &desc);
|
||||
enum picoboot_device_result res = dr_vidpid_unknown;
|
||||
if (ret && verbose) {
|
||||
output("Failed to read device descriptor");
|
||||
output("Failed to read device descriptor\n");
|
||||
}
|
||||
if (!ret) {
|
||||
if (desc.idVendor != VENDOR_ID_RASPBERRY_PI) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
switch (desc.idProduct) {
|
||||
case PRODUCT_ID_MICROPYTHON:
|
||||
return dr_vidpid_micropython;
|
||||
case PRODUCT_ID_PICOPROBE:
|
||||
return dr_vidpid_picoprobe;
|
||||
case PRODUCT_ID_STDIO_USB:
|
||||
return dr_vidpid_stdio_usb;
|
||||
case PRODUCT_ID_RP2_USBBOOT:
|
||||
break;
|
||||
default:
|
||||
if (pid >= 0) {
|
||||
bool match_vid = (vid < 0 ? VENDOR_ID_RASPBERRY_PI : (unsigned int)vid) == desc.idVendor;
|
||||
bool match_pid = pid == desc.idProduct;
|
||||
if (!(match_vid && match_pid)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
} else if (vid != 0) { // ignore vid/pid filtering if no pid and vid == 0
|
||||
if (desc.idVendor != (vid < 0 ? VENDOR_ID_RASPBERRY_PI : (unsigned int)vid)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
switch (desc.idProduct) {
|
||||
case PRODUCT_ID_MICROPYTHON:
|
||||
return dr_vidpid_micropython;
|
||||
case PRODUCT_ID_PICOPROBE:
|
||||
return dr_vidpid_picoprobe;
|
||||
case PRODUCT_ID_RP2040_STDIO_USB:
|
||||
case PRODUCT_ID_STDIO_USB:
|
||||
res = dr_vidpid_stdio_usb;
|
||||
break;
|
||||
case PRODUCT_ID_RP2040_USBBOOT:
|
||||
*model = rp2040;
|
||||
break;
|
||||
case PRODUCT_ID_RP2350_USBBOOT:
|
||||
*model = rp2350;
|
||||
break;
|
||||
default:
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
}
|
||||
ret = libusb_get_active_config_descriptor(device, &config);
|
||||
if (ret && verbose) {
|
||||
|
@ -92,7 +112,29 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d
|
|||
output("Failed to open device %d\n", ret);
|
||||
}
|
||||
if (ret) {
|
||||
return dr_vidpid_bootrom_cant_connect;
|
||||
if (vid == 0 || strlen(ser) != 0) {
|
||||
// didn't check vid or ser, so treat as unknown
|
||||
return dr_vidpid_unknown;
|
||||
} else if (res != dr_vidpid_unknown) {
|
||||
return res;
|
||||
} else {
|
||||
return dr_vidpid_bootrom_cant_connect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res == dr_vidpid_stdio_usb) {
|
||||
if (strlen(ser) != 0) {
|
||||
// Check USB serial number
|
||||
char ser_str[128];
|
||||
libusb_get_string_descriptor_ascii(*dev_handle, desc.iSerialNumber, (unsigned char*)ser_str, sizeof(ser_str));
|
||||
if (strcmp(ser, ser_str)) {
|
||||
return dr_vidpid_unknown;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,14 +156,48 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d
|
|||
if (verbose) output("Failed to claim interface\n");
|
||||
return dr_vidpid_bootrom_no_interface;
|
||||
}
|
||||
|
||||
return dr_vidpid_bootrom_ok;
|
||||
} else {
|
||||
if (verbose) output("Did not find PICOBOOT interface\n");
|
||||
return dr_vidpid_bootrom_no_interface;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if (*model == unknown) {
|
||||
struct picoboot_get_info_cmd info_cmd;
|
||||
info_cmd.bType = PICOBOOT_GET_INFO_SYS,
|
||||
info_cmd.dParams[0] = (uint32_t) (SYS_INFO_CHIP_INFO);
|
||||
uint32_t word_buf[64];
|
||||
// RP2040 doesn't have this function, so returns non-zero
|
||||
int info_ret = picoboot_get_info(*dev_handle, &info_cmd, (uint8_t*)word_buf, sizeof(word_buf));
|
||||
if (info_ret) {
|
||||
*model = rp2040;
|
||||
} else {
|
||||
*model = rp2350;
|
||||
}
|
||||
}
|
||||
if (strlen(ser) != 0) {
|
||||
if (*model == rp2040) {
|
||||
// Check flash ID, as USB serial number is not unique
|
||||
uint64_t ser_num = strtoull(ser, NULL, 16);
|
||||
uint64_t id = 0;
|
||||
int id_ret = picoboot_flash_id(*dev_handle, &id);
|
||||
if (verbose) output("Flash ID %"PRIX64"\n", id);
|
||||
if (id_ret || (ser_num != id)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
} else {
|
||||
// Check USB serial number
|
||||
char ser_str[128];
|
||||
libusb_get_string_descriptor_ascii(*dev_handle, desc.iSerialNumber, (unsigned char*)ser_str, sizeof(ser_str));
|
||||
if (strcmp(ser, ser_str)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dr_vidpid_bootrom_ok;
|
||||
}
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (*dev_handle) {
|
||||
|
@ -169,6 +245,7 @@ int picoboot_reset(libusb_device_handle *usb_device) {
|
|||
return ret;
|
||||
}
|
||||
if (verbose) output(" ...ok\n");
|
||||
definitely_exclusive = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -198,7 +275,7 @@ int picoboot_cmd_status(libusb_device_handle *usb_device, struct picoboot_cmd_st
|
|||
|
||||
int one_time_bulk_timeout;
|
||||
|
||||
int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uint8_t *buffer, uint buf_size) {
|
||||
int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uint8_t *buffer, unsigned int buf_size) {
|
||||
int sent = 0;
|
||||
int ret;
|
||||
|
||||
|
@ -212,6 +289,10 @@ int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uin
|
|||
return ret;
|
||||
}
|
||||
|
||||
int saved_xip_state = xip_state;
|
||||
bool saved_exclusive = definitely_exclusive;
|
||||
xip_state = XIP_UNKOWN;
|
||||
definitely_exclusive = false;
|
||||
int timeout = 10000;
|
||||
if (one_time_bulk_timeout) {
|
||||
timeout = one_time_bulk_timeout;
|
||||
|
@ -250,6 +331,42 @@ int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uin
|
|||
if (verbose) output("zero length in\n");
|
||||
ret = libusb_bulk_transfer(usb_device, in_ep, spoon, 1, &received, cmd->dTransferLength == 0 ? timeout : 3000);
|
||||
}
|
||||
if (!ret) {
|
||||
// do our defensive best to keep the xip_state up to date
|
||||
switch (cmd->bCmdId) {
|
||||
case PC_EXIT_XIP:
|
||||
xip_state = XIP_INACTIVE;
|
||||
break;
|
||||
case PC_ENTER_CMD_XIP:
|
||||
xip_state = XIP_ACTIVE;
|
||||
break;
|
||||
case PC_READ:
|
||||
case PC_WRITE:
|
||||
// whitelist PC_READ and PC_WRITE as not affecting xip state
|
||||
xip_state = saved_xip_state;
|
||||
break;
|
||||
default:
|
||||
xip_state = XIP_UNKOWN;
|
||||
break;
|
||||
}
|
||||
// do our defensive best to keep the exclusive var up to date
|
||||
switch (cmd->bCmdId) {
|
||||
case PC_EXCLUSIVE_ACCESS:
|
||||
definitely_exclusive = cmd->exclusive_cmd.bExclusive;
|
||||
break;
|
||||
case PC_ENTER_CMD_XIP:
|
||||
case PC_EXIT_XIP:
|
||||
case PC_READ:
|
||||
case PC_WRITE:
|
||||
// whitelist PC_READ and PC_WRITE as not affecting xip state
|
||||
definitely_exclusive = saved_exclusive;
|
||||
break;
|
||||
default:
|
||||
definitely_exclusive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -264,11 +381,16 @@ int picoboot_exclusive_access(libusb_device_handle *usb_device, uint8_t exclusiv
|
|||
}
|
||||
|
||||
int picoboot_exit_xip(libusb_device_handle *usb_device) {
|
||||
if (definitely_exclusive && xip_state == XIP_INACTIVE) {
|
||||
if (verbose) output("Skipping EXIT_XIP");
|
||||
return 0;
|
||||
}
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("EXIT_XIP\n");
|
||||
cmd.bCmdId = PC_EXIT_XIP;
|
||||
cmd.bCmdSize = 0;
|
||||
cmd.dTransferLength = 0;
|
||||
xip_state = XIP_INACTIVE;
|
||||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
|
@ -278,12 +400,13 @@ int picoboot_enter_cmd_xip(libusb_device_handle *usb_device) {
|
|||
cmd.bCmdId = PC_ENTER_CMD_XIP;
|
||||
cmd.bCmdSize = 0;
|
||||
cmd.dTransferLength = 0;
|
||||
xip_state = XIP_ACTIVE;
|
||||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
int picoboot_reboot(libusb_device_handle *usb_device, uint32_t pc, uint32_t sp, uint32_t delay_ms) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("REBOOT %08x %08x %u\n", (uint) pc, (uint) sp, (uint) delay_ms);
|
||||
if (verbose) output("REBOOT %08x %08x %u\n", (unsigned int) pc, (unsigned int) sp, (unsigned int) delay_ms);
|
||||
cmd.bCmdId = PC_REBOOT;
|
||||
cmd.bCmdSize = sizeof(cmd.reboot_cmd);
|
||||
cmd.dTransferLength = 0;
|
||||
|
@ -293,11 +416,21 @@ int picoboot_reboot(libusb_device_handle *usb_device, uint32_t pc, uint32_t sp,
|
|||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
int picoboot_reboot2(libusb_device_handle *usb_device, struct picoboot_reboot2_cmd *reboot_cmd) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("REBOOT %08x %08x %08x %u\n", (unsigned int)reboot_cmd->dFlags, (unsigned int) reboot_cmd->dParam0, (unsigned int) reboot_cmd->dParam1, (unsigned int) reboot_cmd->dDelayMS);
|
||||
cmd.bCmdId = PC_REBOOT2;
|
||||
cmd.bCmdSize = sizeof(cmd.reboot2_cmd);
|
||||
cmd.reboot2_cmd = *reboot_cmd;
|
||||
cmd.dTransferLength = 0;
|
||||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
int picoboot_exec(libusb_device_handle *usb_device, uint32_t addr) {
|
||||
struct picoboot_cmd cmd;
|
||||
// shouldn't be necessary any more
|
||||
// addr |= 1u; // Thumb bit
|
||||
if (verbose) output("EXEC %08x\n", (uint) addr);
|
||||
if (verbose) output("EXEC %08x\n", (unsigned int) addr);
|
||||
cmd.bCmdId = PC_EXEC;
|
||||
cmd.bCmdSize = sizeof(cmd.address_only_cmd);
|
||||
cmd.dTransferLength = 0;
|
||||
|
@ -305,9 +438,22 @@ int picoboot_exec(libusb_device_handle *usb_device, uint32_t addr) {
|
|||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
// int picoboot_exec2(libusb_device_handle *usb_device, struct picoboot_exec2_cmd *exec2_cmd) {
|
||||
// struct picoboot_cmd cmd;
|
||||
// // shouldn't be necessary any more
|
||||
// // addr |= 1u; // Thumb bit
|
||||
// //if (verbose) output("EXEC2 %08x\n", (unsigned int) exec2_cmd->scan_base);
|
||||
// cmd.bCmdId = PC_EXEC2;
|
||||
// cmd.bCmdSize = sizeof(cmd.exec2_cmd);
|
||||
// cmd.dTransferLength = 0;
|
||||
// cmd.exec2_cmd = *exec2_cmd;
|
||||
// return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
// } // currently unused
|
||||
|
||||
|
||||
int picoboot_flash_erase(libusb_device_handle *usb_device, uint32_t addr, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("FLASH_ERASE %08x+%08x\n", (uint) addr, (uint) len);
|
||||
if (verbose) output("FLASH_ERASE %08x+%08x\n", (unsigned int) addr, (unsigned int) len);
|
||||
cmd.bCmdId = PC_FLASH_ERASE;
|
||||
cmd.bCmdSize = sizeof(cmd.range_cmd);
|
||||
cmd.range_cmd.dAddr = addr;
|
||||
|
@ -318,7 +464,7 @@ int picoboot_flash_erase(libusb_device_handle *usb_device, uint32_t addr, uint32
|
|||
|
||||
int picoboot_vector(libusb_device_handle *usb_device, uint32_t addr) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("VECTOR %08x\n", (uint) addr);
|
||||
if (verbose) output("VECTOR %08x\n", (unsigned int) addr);
|
||||
cmd.bCmdId = PC_VECTORIZE_FLASH;
|
||||
cmd.bCmdSize = sizeof(cmd.address_only_cmd);
|
||||
cmd.range_cmd.dAddr = addr;
|
||||
|
@ -328,7 +474,7 @@ int picoboot_vector(libusb_device_handle *usb_device, uint32_t addr) {
|
|||
|
||||
int picoboot_write(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("WRITE %08x+%08x\n", (uint) addr, (uint) len);
|
||||
if (verbose) output("WRITE %08x+%08x\n", (unsigned int) addr, (unsigned int) len);
|
||||
cmd.bCmdId = PC_WRITE;
|
||||
cmd.bCmdSize = sizeof(cmd.range_cmd);
|
||||
cmd.range_cmd.dAddr = addr;
|
||||
|
@ -338,7 +484,7 @@ int picoboot_write(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buf
|
|||
|
||||
int picoboot_read(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len) {
|
||||
memset(buffer, 0xaa, len);
|
||||
if (verbose) output("READ %08x+%08x\n", (uint) addr, (uint) len);
|
||||
if (verbose) output("READ %08x+%08x\n", (unsigned int) addr, (unsigned int) len);
|
||||
struct picoboot_cmd cmd;
|
||||
cmd.bCmdId = PC_READ;
|
||||
cmd.bCmdSize = sizeof(cmd.range_cmd);
|
||||
|
@ -357,8 +503,47 @@ int picoboot_read(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buff
|
|||
return ret;
|
||||
}
|
||||
|
||||
int picoboot_otp_write(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("OTP WRITE %04x+%08x ecc=%d\n", (unsigned int) otp_cmd->wRow, otp_cmd->wRowCount, otp_cmd->bEcc);
|
||||
cmd.bCmdId = PC_OTP_WRITE;
|
||||
#ifdef _MSC_VER
|
||||
cmd.bCmdSize = 5; // for some reason with MSVC, and only with picoboot_otp_cmd, the size is 6 not 5??
|
||||
#else
|
||||
cmd.bCmdSize = sizeof(cmd.otp_cmd);
|
||||
#endif
|
||||
cmd.otp_cmd = *otp_cmd;
|
||||
cmd.dTransferLength = len;
|
||||
one_time_bulk_timeout = 5000 + len * 5;
|
||||
return picoboot_cmd(usb_device, &cmd, buffer, len);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int picoboot_otp_read(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("OTP READ %04x+%08x ecc=%d\n", (unsigned int) otp_cmd->wRow, otp_cmd->wRowCount, otp_cmd->bEcc);
|
||||
cmd.bCmdId = PC_OTP_READ;
|
||||
#ifdef _MSC_VER
|
||||
cmd.bCmdSize = 5; // for some reason with MSVC, and only with picoboot_otp_cmd, the size is 6 not 5??
|
||||
#else
|
||||
cmd.bCmdSize = sizeof(cmd.otp_cmd);
|
||||
#endif
|
||||
cmd.otp_cmd = *otp_cmd;
|
||||
cmd.dTransferLength = len;
|
||||
return picoboot_cmd(usb_device, &cmd, buffer, len);
|
||||
}
|
||||
|
||||
int picoboot_get_info(libusb_device_handle *usb_device, struct picoboot_get_info_cmd *get_info_cmd, uint8_t *buffer, uint32_t len) {
|
||||
if (verbose) output("GET_INFO\n");
|
||||
struct picoboot_cmd cmd;
|
||||
cmd.bCmdId = PC_GET_INFO;
|
||||
cmd.bCmdSize = sizeof(cmd.get_info_cmd);
|
||||
cmd.get_info_cmd = *get_info_cmd;
|
||||
cmd.dTransferLength = len;
|
||||
int ret = picoboot_cmd(usb_device, &cmd, buffer, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 1
|
||||
// Peek/poke via EXEC
|
||||
|
||||
// 00000000 <poke>:
|
||||
|
@ -376,6 +561,7 @@ static const size_t picoboot_poke_cmd_len = 8;
|
|||
static const uint8_t picoboot_poke_cmd[] = {
|
||||
0x01, 0x48, 0x02, 0x49, 0x08, 0x60, 0x70, 0x47
|
||||
};
|
||||
#define PICOBOOT_POKE_CMD_PROG_SIZE (size_t)(8 + 8)
|
||||
|
||||
// 00000000 <peek>:
|
||||
// 0: 4802 ldr r0, [pc, #8] ; (c <inout>)
|
||||
|
@ -391,19 +577,53 @@ static const size_t picoboot_peek_cmd_len = 12;
|
|||
static const uint8_t picoboot_peek_cmd[] = {
|
||||
0x02, 0x48, 0x00, 0x68, 0x79, 0x46, 0x48, 0x60, 0x70, 0x47, 0xc0, 0x46
|
||||
};
|
||||
#define PICOBOOT_PEEK_CMD_PROG_SIZE (size_t)(12 + 4)
|
||||
|
||||
// todo - compile this - currently taken from github PR #86
|
||||
static const size_t picoboot_flash_id_cmd_len = 152;
|
||||
static const uint8_t picoboot_flash_id_cmd[] = {
|
||||
// void flash_get_unique_id(void)
|
||||
0x02, 0xa0, 0x06, 0xa1, 0x00, 0x4a, 0x11, 0xe0,
|
||||
// int buflen
|
||||
0x0d, 0x00, 0x00, 0x00,
|
||||
// char txbuf[13]
|
||||
0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// char rxbuf[13]
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// void flash_do_cmd(txbuf, rxbuf, buflen)
|
||||
0x80, 0x23, 0xf0, 0xb5, 0x17, 0x4e, 0x9b, 0x00,
|
||||
0x34, 0x68, 0x63, 0x40, 0xc0, 0x24, 0xa4, 0x00,
|
||||
0x23, 0x40, 0x15, 0x4c, 0x23, 0x60, 0xc0, 0x24,
|
||||
0x13, 0x00, 0x64, 0x05, 0x17, 0x00, 0x1f, 0x43,
|
||||
0x06, 0xd1, 0xc0, 0x23, 0x32, 0x68, 0x9b, 0x00,
|
||||
0x93, 0x43, 0x0f, 0x4a, 0x13, 0x60, 0xf0, 0xbd,
|
||||
0x08, 0x25, 0xa7, 0x6a, 0x3d, 0x40, 0xac, 0x46,
|
||||
0x02, 0x25, 0x2f, 0x42, 0x08, 0xd0, 0x00, 0x2a,
|
||||
0x06, 0xd0, 0x9f, 0x1a, 0x0d, 0x2f, 0x03, 0xd8,
|
||||
0x07, 0x78, 0x01, 0x3a, 0x27, 0x66, 0x01, 0x30,
|
||||
0x65, 0x46, 0x00, 0x2d, 0xe2, 0xd0, 0x00, 0x2b,
|
||||
0xe0, 0xd0, 0x27, 0x6e, 0x01, 0x3b, 0x0f, 0x70,
|
||||
0x01, 0x31, 0xdb, 0xe7, 0x0c, 0x80, 0x01, 0x40,
|
||||
0x0c, 0x90, 0x01, 0x40,
|
||||
};
|
||||
#define PICOBOOT_FLASH_ID_CMD_PROG_SIZE (size_t)(152)
|
||||
|
||||
// TODO better place for this e.g. the USB DPRAM location the controller has already put it in
|
||||
#define PEEK_POKE_CODE_LOC 0x20000000u
|
||||
|
||||
#define FLASH_ID_CODE_LOC 0x15000000 // XIP_SRAM_BASE on RP2040, as we're not using XIP so probably fine
|
||||
#define FLASH_ID_UID_ADDR (FLASH_ID_CODE_LOC + 28 + 1 + 4)
|
||||
|
||||
int picoboot_poke(libusb_device_handle *usb_device, uint32_t addr, uint32_t data) {
|
||||
const size_t prog_size = picoboot_poke_cmd_len + 8;
|
||||
uint8_t prog[prog_size];
|
||||
uint8_t prog[PICOBOOT_POKE_CMD_PROG_SIZE];
|
||||
output("POKE (D)%08x -> (A)%08x\n", data, addr);
|
||||
memcpy(prog, picoboot_poke_cmd, picoboot_poke_cmd_len);
|
||||
*(uint32_t *) (prog + picoboot_poke_cmd_len) = data;
|
||||
*(uint32_t *) (prog + picoboot_poke_cmd_len + 4) = addr;
|
||||
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, prog_size);
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, PICOBOOT_POKE_CMD_PROG_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
return picoboot_exec(usb_device, PEEK_POKE_CODE_LOC);
|
||||
|
@ -411,13 +631,12 @@ int picoboot_poke(libusb_device_handle *usb_device, uint32_t addr, uint32_t data
|
|||
|
||||
// TODO haven't checked the store goes to the right address :)
|
||||
int picoboot_peek(libusb_device_handle *usb_device, uint32_t addr, uint32_t *data) {
|
||||
const size_t prog_size = picoboot_peek_cmd_len + 4;
|
||||
uint8_t prog[prog_size];
|
||||
uint8_t prog[PICOBOOT_PEEK_CMD_PROG_SIZE];
|
||||
output("PEEK %08x\n", addr);
|
||||
memcpy(prog, picoboot_peek_cmd, picoboot_peek_cmd_len);
|
||||
*(uint32_t *) (prog + picoboot_peek_cmd_len) = addr;
|
||||
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, prog_size);
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, PICOBOOT_PEEK_CMD_PROG_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = picoboot_exec(usb_device, PEEK_POKE_CODE_LOC);
|
||||
|
@ -425,4 +644,36 @@ int picoboot_peek(libusb_device_handle *usb_device, uint32_t addr, uint32_t *dat
|
|||
return ret;
|
||||
return picoboot_read(usb_device, PEEK_POKE_CODE_LOC + picoboot_peek_cmd_len, (uint8_t *) data, sizeof(uint32_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
int picoboot_flash_id(libusb_device_handle *usb_device, uint64_t *data) {
|
||||
picoboot_exclusive_access(usb_device, 1);
|
||||
uint8_t prog[PICOBOOT_FLASH_ID_CMD_PROG_SIZE];
|
||||
uint64_t id;
|
||||
output("GET FLASH ID\n");
|
||||
memcpy(prog, picoboot_flash_id_cmd, picoboot_flash_id_cmd_len);
|
||||
|
||||
// ensure XIP is exited before executing
|
||||
int ret = picoboot_exit_xip(usb_device);
|
||||
if (ret)
|
||||
goto flash_id_return;
|
||||
ret = picoboot_write(usb_device, FLASH_ID_CODE_LOC, prog, PICOBOOT_FLASH_ID_CMD_PROG_SIZE);
|
||||
if (ret)
|
||||
goto flash_id_return;
|
||||
ret = picoboot_exec(usb_device, FLASH_ID_CODE_LOC);
|
||||
if (ret)
|
||||
goto flash_id_return;
|
||||
ret = picoboot_read(usb_device, FLASH_ID_UID_ADDR, (uint8_t *) &id, sizeof(uint64_t));
|
||||
*data = (((id & 0x00000000000000FF) << 56) |
|
||||
((id & 0x000000000000FF00) << 40) |
|
||||
((id & 0x0000000000FF0000) << 24) |
|
||||
((id & 0x00000000FF000000) << 8) |
|
||||
((id & 0x000000FF00000000) >> 8) |
|
||||
((id & 0x0000FF0000000000) >> 24) |
|
||||
((id & 0x00FF000000000000) >> 40) |
|
||||
((id & 0xFF00000000000000) >> 56));
|
||||
|
||||
flash_id_return:
|
||||
picoboot_exclusive_access(usb_device, 0);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
|
@ -10,9 +10,21 @@
|
|||
// todo we should use fully encapsulate libusb
|
||||
|
||||
#include <assert.h>
|
||||
#if HAS_LIBUSB
|
||||
#include <libusb.h>
|
||||
#endif
|
||||
#include "boot/picoboot.h"
|
||||
|
||||
#include "addresses.h"
|
||||
|
||||
#define VENDOR_ID_RASPBERRY_PI 0x2e8au
|
||||
#define PRODUCT_ID_RP2040_USBBOOT 0x0003u
|
||||
#define PRODUCT_ID_PICOPROBE 0x0004u
|
||||
#define PRODUCT_ID_MICROPYTHON 0x0005u
|
||||
#define PRODUCT_ID_STDIO_USB 0x0009u
|
||||
#define PRODUCT_ID_RP2040_STDIO_USB 0x000au
|
||||
#define PRODUCT_ID_RP2350_USBBOOT 0x000fu
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -28,7 +40,20 @@ enum picoboot_device_result {
|
|||
dr_vidpid_stdio_usb,
|
||||
};
|
||||
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle);
|
||||
typedef enum {
|
||||
rp2040,
|
||||
rp2350,
|
||||
unknown
|
||||
} model_t;
|
||||
|
||||
typedef enum {
|
||||
rp2350_a2,
|
||||
rp2350_unknown
|
||||
} rp2350_version_t;
|
||||
|
||||
#if HAS_LIBUSB
|
||||
// note that vid and pid are filters, unless both are specified in which case a device with that VID and PID is allowed for RP2350
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, model_t *model, int vid, int pid, const char* ser);
|
||||
|
||||
int picoboot_reset(libusb_device_handle *usb_device);
|
||||
int picoboot_cmd_status_verbose(libusb_device_handle *usb_device, struct picoboot_cmd_status *status,
|
||||
|
@ -38,26 +63,20 @@ int picoboot_exclusive_access(libusb_device_handle *usb_device, uint8_t exclusiv
|
|||
int picoboot_enter_cmd_xip(libusb_device_handle *usb_device);
|
||||
int picoboot_exit_xip(libusb_device_handle *usb_device);
|
||||
int picoboot_reboot(libusb_device_handle *usb_device, uint32_t pc, uint32_t sp, uint32_t delay_ms);
|
||||
int picoboot_reboot2(libusb_device_handle *usb_device, struct picoboot_reboot2_cmd *reboot_cmd);
|
||||
int picoboot_get_info(libusb_device_handle *usb_device, struct picoboot_get_info_cmd *cmd, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_exec(libusb_device_handle *usb_device, uint32_t addr);
|
||||
// int picoboot_exec2(libusb_device_handle *usb_device, struct picoboot_exec2_cmd *exec2_cmd); // currently unused
|
||||
int picoboot_flash_erase(libusb_device_handle *usb_device, uint32_t addr, uint32_t len);
|
||||
int picoboot_vector(libusb_device_handle *usb_device, uint32_t addr);
|
||||
int picoboot_write(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_read(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_otp_write(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_otp_read(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_poke(libusb_device_handle *usb_device, uint32_t addr, uint32_t data);
|
||||
int picoboot_peek(libusb_device_handle *usb_device, uint32_t addr, uint32_t *data);
|
||||
|
||||
#define ROM_START 0x00000000
|
||||
#define ROM_END 0x00004000
|
||||
#define FLASH_START 0x10000000
|
||||
#define FLASH_END 0x11000000 // this is maximum
|
||||
#define XIP_SRAM_BASE 0x15000000
|
||||
#define XIP_SRAM_END 0x15004000
|
||||
|
||||
#define SRAM_START 0x20000000
|
||||
#define SRAM_END 0x20042000
|
||||
|
||||
#define SRAM_UNSTRIPED_START 0x21000000
|
||||
#define SRAM_UNSTRIPED_END 0x21040000
|
||||
int picoboot_flash_id(libusb_device_handle *usb_device, uint64_t *data);
|
||||
#endif
|
||||
|
||||
// we require 256 (as this is the page size supported by the device)
|
||||
#define LOG2_PAGE_SIZE 8u
|
||||
|
@ -74,27 +93,44 @@ enum memory_type {
|
|||
};
|
||||
|
||||
// inclusive of ends
|
||||
static inline enum memory_type get_memory_type(uint32_t addr) {
|
||||
if (addr >= ROM_START && addr <= ROM_END) {
|
||||
return rom;
|
||||
}
|
||||
if (addr >= FLASH_START && addr <= FLASH_END) {
|
||||
static inline enum memory_type get_memory_type(uint32_t addr, model_t model) {
|
||||
if (addr >= FLASH_START && addr <= FLASH_END_RP2040) {
|
||||
return flash;
|
||||
}
|
||||
if (addr >= SRAM_START && addr <= SRAM_END) {
|
||||
if (addr >= ROM_START && addr <= ROM_END_RP2040) {
|
||||
return rom;
|
||||
}
|
||||
if (addr >= SRAM_START && addr <= SRAM_END_RP2040) {
|
||||
return sram;
|
||||
}
|
||||
if (addr >= SRAM_UNSTRIPED_START && addr <= SRAM_UNSTRIPED_END) {
|
||||
if (model == rp2350) {
|
||||
if (addr >= FLASH_START && addr <= FLASH_END_RP2350) {
|
||||
return flash;
|
||||
}
|
||||
if (addr >= ROM_START && addr <= ROM_END_RP2350) {
|
||||
return rom;
|
||||
}
|
||||
if (addr >= SRAM_START && addr <= SRAM_END_RP2350) {
|
||||
return sram;
|
||||
}
|
||||
}
|
||||
if (addr >= MAIN_RAM_BANKED_START && addr <= MAIN_RAM_BANKED_END) {
|
||||
return sram_unstriped;
|
||||
}
|
||||
if (addr >= XIP_SRAM_BASE && addr <= XIP_SRAM_END) {
|
||||
return xip_sram;
|
||||
if (model == rp2040) {
|
||||
if (addr >= XIP_SRAM_START_RP2040 && addr <= XIP_SRAM_END_RP2040) {
|
||||
return xip_sram;
|
||||
}
|
||||
} else if (model == rp2350) {
|
||||
if (addr >= XIP_SRAM_START_RP2350 && addr <= XIP_SRAM_END_RP2350) {
|
||||
return xip_sram;
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
static inline bool is_transfer_aligned(uint32_t addr) {
|
||||
enum memory_type t = get_memory_type(addr);
|
||||
static inline bool is_transfer_aligned(uint32_t addr, model_t model) {
|
||||
enum memory_type t = get_memory_type(addr, model);
|
||||
return t != invalid && !(t == flash && addr & (PAGE_SIZE-1));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue