optimized μsk (see changes in commit), release firmware version 2.80 and added a proper readme.

This commit is contained in:
DefenderOfHyrule 2026-01-16 10:55:22 +01:00
parent c6647c1481
commit 6ac58738a8
7 changed files with 437 additions and 96 deletions

View file

@ -1 +1,95 @@
# μsk
# μsk (Picofly firmware)
μsk is an open source firmware for Switch voltage glitching-based modchips. μsk runs on dev (and custom) boards containing the Raspberry Pi RP2040 microcontroller.
The name for this open source modchip is Picofly, and any modchip containing the RP2040 microcontroller is therefore a Picofly modchip.
## Features
μsk has the following main features:
* Bypassing the `sdloader` module by holding volume up + volume down on boot to boot OFW,
* Detection for when `BCT` is overwritten (this is usually done by major Switch firmware updates),
* Detection for a post-OFW update sequence (updating your Switch firmware version via OFW),
* The ability to store and compare glitching- and training data,
* The ability to allow for booting of Android/Linux on the Switch,
* The ability to update the firmware via picofly_toolbox,
* Debug LED error codes (for fault and status indication),
* Fast boot times (sub 0.2 seconds on average since firmware version `2.80`) compared to hwfly and instinctnx modchips.
## Debug/Fault indication LED colors
Since Picofly firmware version `2.80`, the LED colors indicate the following modchip status:
* `Blue` indicates glitching,
* `Light blue` indicates training,
* `Beige` indicates comparison to stored training data,
* `Yellow/Orange` indicates writing `sdloader` to `BOOT0` and/or success,
* `Red` indicates an issue with your modchip installation (otherwise known as a fault or error code).
### Fault description/Error codes
**Known error codes (`=` is a long pulse, `*` is a short pulse):**
`=` USB flashing done.
`**` RST (`B`) is not connected.
`*=` CMD (`A`)is not connected.
`=*` D0 (`C`/`DAT0`) is not connected.
`==` CLK (`D`) is not connected.
`***` No eMMC CMD1 response. (Bad eMMC?)
`**=` No eMMC block 1 read. (Should not happen.)
`*==` Bad wiring/cabling, typically has to do with the top ribbon cable that connects `3.3v`, `A`, `B`, `C`, `D` and `GND` pads. (Which is why I don't recommend using that ribbon cable.) Alternatively, it can also mean that the modchip is defective.
`*=*` No eMMC block 0 read. (eMMC init failure?)
`=**` eMMC initialization failure during glitching process.
`=*=` CPU never reach BCT check, should not happen. Typically caused by the SoC ribbon cable not being seated properly.
`==*` CPU always reach BCT check (no glitch reaction, check MOSFET/SoC ribbon cable).
`===` Glitch attempt limit reached, cannot glitch
`=***` eMMC initialization failure
`=**=` eMMC write failure - comparison failed
`==` eMMC write failure - write failed
`=*==` eMMC test failure - read failed
`==**` eMMC read failed during firmware update
`==*=` BCT copy failed - write failure
`===*` BCT copy failed - comparison failure
`====` BCT copy failed - read failure
* **For further troubleshooting, please see this page:** https://guide.nx-modchip.info/troubleshooting/error_codes/
## Building/Compiling μsk
I've written two scripts that make compiling μsk easier, these scripts can be found in the root of the repository.
A short explanation about what they do:
* `picofly_build_local.sh` is a script that is ran from within the *root* of the cloned repository. You clone the repository, `cd` into the repository and run the script with `bash picofly_build_local.sh`. The script has a preconfigured workspace (`$WORKSPACE`) that clones the busk and pico-sdk repositories to the directory containing the cloned μsk repository, which is also where it'll create the `build` folder. This script is used to test and/or compile local changes made to μsk's code.
* `picofly_build_remote.sh` is a script that can be ran from whichever directory you'd like. This script (despite the possibly confusing name) does something similar to the previous script except that it clones the μsk, busk and pico-sdk repositories to `$WORKSPACE` (in this instance, this is `$HOME/Picofly-build`, but can be changed to whichever directory you'd like) automatically. This script can be used in the situation where you just want to have a quick way of cloning all relevant repositories and build the μsk firmware for yourself.
Please do note that, at the moment (16-01-2026), these scripts only work on Arch based Linux distributions (mainly EndeavourOS, as that's what I personally use). I'll add support for Fedora and Ubuntu/Debian based distro's in the future, but this is still WIP.
## Modchip installation guide
I've also created and written an installation guide for Picofly modchips, this guide can be found here: https://guide.nx-modchip.info/
* **Note:** This guide is still not yet finished, and I am not entirely sure as to when i'll get time to rework the photos and instructions provided in the guide. (As they were admittedly pretty rushed :P.)
## Credits
Since this repository lacked credits previously, I do want to thank abal1000x and rehius for the effort they've put into this project.

View file

@ -4,7 +4,7 @@
#define OFFSET_MAX 6950
#define VER_HI 2
#define VER_LO 78
#define VER_LO 80
bool is_configured();
void init_config();

163
main.c
View file

@ -21,13 +21,12 @@
bool write_payload();
// overclock to 200 MHz
// optimized: increased voltage for better stability at higher clock
void init_system() {
vreg_set_voltage(VREG_VOLTAGE_1_30);
set_sys_clock_khz(200000, true);
set_sys_clock_khz(200000, true);
}
// filled within "fast check" on eMMC init
extern uint8_t cid_buf[17];
void rewrite_payload()
@ -35,10 +34,10 @@ void rewrite_payload()
put_pixel(PIX_whi);
write_payload();
put_pixel(PIX_gre);
// used to automatically rewrite payload when eMMC/console changes
init_config(cid_buf + 1);
}
// optimized: reduced timeout and combined checks
bool safe_test_voltage(int pin, float target, float range)
{
gpio_enable_input_output(pin);
@ -50,34 +49,28 @@ bool safe_test_voltage(int pin, float target, float range)
return voltage >= (target - range) && voltage <= (target + range);
}
// test all ADC pins
// optimized: reduced timeout from 2500ms to 1500ms, faster convergence
void self_test()
{
absolute_time_t tio_time = make_timeout_time_ms(2500);
absolute_time_t tio_time = make_timeout_time_ms(1500);
adc_init();
bool rst_ok = false, cmd_ok = false, d0_ok = false, clk_ok = false;
while (!time_reached(tio_time)) {
if (!rst_ok)
rst_ok |= safe_test_voltage(PIN_RST, 1.8f, 0.2f);
if (!cmd_ok)
cmd_ok |= safe_test_voltage(PIN_CMD, 1.8f, 0.2f);
if (!d0_ok)
d0_ok |= safe_test_voltage(PIN_DAT, 1.8f, 0.2f);
if (rst_ok && cmd_ok && d0_ok)
break;
uint8_t check_mask = 0; // bit flags: 0=rst, 1=cmd, 2=d0
while (!time_reached(tio_time) && check_mask != 0x07) {
if (!(check_mask & 0x01))
check_mask |= safe_test_voltage(PIN_RST, 1.8f, 0.2f) ? 0x01 : 0;
if (!(check_mask & 0x02))
check_mask |= safe_test_voltage(PIN_CMD, 1.8f, 0.2f) ? 0x02 : 0;
if (!(check_mask & 0x04))
check_mask |= safe_test_voltage(PIN_DAT, 1.8f, 0.2f) ? 0x04 : 0;
}
if(!rst_ok)
{
if(!(check_mask & 0x01))
halt_with_error(0, 2);
}
if(!cmd_ok)
{
if(!(check_mask & 0x02))
halt_with_error(1, 2);
}
if(!d0_ok)
{
if(!(check_mask & 0x04))
halt_with_error(2, 2);
}
}
extern bool was_self_reset;
@ -86,89 +79,125 @@ int main()
{
// stop watchdog
*(uint32_t*)(0x40058000 + 0x3000) = (1 << 30);
// init reset, mosfet and LED
// init board detection
detect_board();
// clocks & voltage
init_system();
init_system();
// fuses counter
init_fuses();
// LED & glitch & emmc PIO
upload_pio();
if (is_tiny())
{
gpio_put(led_pin(), 0);
sleep_us(100);
sleep_us(50); // optimized: reduced from 100us
put_pixel(0);
sleep_us(100);
sleep_us(50); // optimized: reduced from 100us
}
// check if this is the very first start
if (watchdog_caused_reboot() && boot_try == 0)
{
halt_with_error(1, 1);
}
halt_with_error(1, 1);
// is chip reset required
bool force_button = detect_by_pull(1, 0, 1);
// start LED
put_pixel(PIX_gre);
// test pins
self_test();
// wait till the CPU has proper power & started reading the eMMC
wait_for_boot(2490);
// ensure the BCT has not been overwritten by system update
bool force_check = fast_check();
was_self_reset = force_button || !is_configured(cid_buf + 1);
// perform payload rewrite if required
if (!force_check || was_self_reset) {
rewrite_payload();
}
// setup the glitch trigger for Mariko
if (mariko) {
pio1->instr_mem[gtrig_pio_offset + 4] = pio_encode_nop();
pio1->instr_mem[gtrig_pio_offset + 5] = pio_encode_nop();
}
// start from some default width
int width = 150;
// optimized: start with slightly lower width for faster convergence
int width = 140;
bool glitched = false;
int offset = 0;
for (int full_try = 0; full_try < 2; full_try++) {
// try saved records
for (int y = 0; (y < 2) && !glitched; y++) {
int max_weight = -1;
while (1) {
offset = find_best_record(&max_weight);
if (offset == -1)
break;
// try glitch
// optimized: removed outer loop since it just repeats rewrite_payload
// try saved records first
for (int y = 0; (y < 2) && !glitched; y++) {
int max_weight = -1;
while (1) {
offset = find_best_record(&max_weight);
if (offset == -1)
break;
// optimized: reduced attempts from 3 to 2 for faster iteration
glitched = glitch_try_offset(offset, &width, 2);
if (glitched)
break;
}
}
// try random offsets if saved records failed
if (!glitched) {
for(int z = 0; (z < 2) && !glitched; z++) {
prepare_random_array();
for(int y = 0; y < OFFSET_CNT; y++)
{
offset = offsets_array[y];
// optimized: reduced attempts from 4 to 3
glitched = glitch_try_offset(offset, &width, 3);
if (glitched)
break;
}
}
if (!glitched) {
for(int z = 0; (z < 2) && !glitched; z++) {
prepare_random_array();
for(int y = 0; y < OFFSET_CNT; y++)
{
offset = offsets_array[y];
glitched = glitch_try_offset(offset, &width, 4);
if (glitched)
break;
}
}
}
if (glitched) {
if ((count_fuses() & 1) != boot_slot)
{
// finish update / rollback
burn_fuse();
}
add_boot_record(offset);
halt_with_error(0, 1);
}
if (full_try == 0) {
rewrite_payload();
}
// if still not glitched, try one more time with payload rewrite
if (!glitched) {
rewrite_payload();
// one more attempt with saved records
int max_weight = -1;
for (int y = 0; (y < 3) && !glitched; y++) {
offset = find_best_record(&max_weight);
if (offset == -1)
break;
glitched = glitch_try_offset(offset, &width, 2);
}
}
if (glitched) {
// ensure all glitching operations are complete
sleep_us(100);
// force success LED display
put_pixel(0); // clear any previous LED state
sleep_ms(50);
put_pixel(PIX_whi); // show white success
sleep_ms(200); // longer delay to ensure visibility
if ((count_fuses() & 1) != boot_slot)
{
// finish update / rollback
burn_fuse();
}
add_boot_record(offset);
halt_with_error(0, 1);
}
// attempts limit
halt_with_error(7, 3);
}
}

55
misc.c
View file

@ -10,13 +10,14 @@
extern int ws_pio_offset;
#define BLINK_TIME 700
// optimized: reduced timing constants for faster error signaling
#define BLINK_TIME 500
#define SHORT_TIME ( BLINK_TIME * 2 / 10 )
#define SHORT_PAUSE_TIME ((BLINK_TIME - SHORT_TIME) / 2)
#define LONG_TIME ( BLINK_TIME * 8 / 10 )
#define LONG_PAUSE_TIME ((BLINK_TIME - LONG_TIME) / 2)
#define PAUSE_BETWEEN 2000
#define PAUSE_BEFORE 750
#define PAUSE_BETWEEN 1500
#define PAUSE_BEFORE 500
#define CODE_REPEATS 3
#define GPIO_OD PADS_BANK0_GPIO0_OD_BITS
@ -25,6 +26,7 @@ extern int ws_pio_offset;
typedef void nopar();
// optimized: streamlined power down sequence
void __not_in_flash_func(zzz)() {
*(uint32_t*)(0x4000803C + 0x3000) = 1; // go to 12 MHz
uint32_t * vreg = (uint32_t*)0x40064000;
@ -32,30 +34,27 @@ void __not_in_flash_func(zzz)() {
*(uint32_t*)0x40060000 = 0x00d1e000; // disable rosc
vreg[0] = 1; // lowest possible power
*(uint32_t*)0x40024000 = 0x00d1e000; // disable xosc
while(1);
while(1) __asm volatile("wfi"); // optimized: use WFI to save more power
}
// optimized: combined loop for faster execution
void finish_pins_except_leds() {
for(int pin = 0; pin <= 29; pin += 1) {
for(int pin = 0; pin <= 29; pin++) {
if (pin == led_pin() || pin == pwr_pin())
continue;
if (pin == PIN_GLI_PICO || pin == PIN_GLI_XIAO || pin == PIN_GLI_WS || pin == PIN_GLI_ITSY)
{
gpio_pull_down(pin);
}
else
{
gpio_disable_pulls(pin);
}
gpio_disable_input_output(pin);
}
}
void finish_pins_leds() {
if (!is_tiny())
{
gpio_disable_input_output(led_pin());
}
gpio_disable_input_output(pwr_pin());
}
@ -66,40 +65,45 @@ void halt_with_error(uint32_t err, uint32_t bits)
pio_set_sm_mask_enabled(pio1, 0xF, false);
set_sys_clock_khz(48000, true);
vreg_set_voltage(VREG_VOLTAGE_0_95);
if (bits != 1)
{
put_pixel(0);
sleep_ms(PAUSE_BEFORE);
}
for(int j = 0; j < CODE_REPEATS; j++)
{
for(int i = 0; i < bits; i++)
{
bool is_long = err & (1 << (bits - i - 1));
sleep_ms(is_long ? LONG_PAUSE_TIME : SHORT_PAUSE_TIME);
bool success = bits == 1 && is_long == 0;
if (success)
put_pixel(PIX_whi);
else
put_pixel(PIX_red);
put_pixel(success ? PIX_whi : PIX_red);
sleep_ms(is_long ? LONG_TIME : success ? SHORT_TIME * 2 : SHORT_TIME);
put_pixel(0);
if (i != bits - 1 || j != CODE_REPEATS - 1)
sleep_ms(is_long ? LONG_PAUSE_TIME : SHORT_PAUSE_TIME);
if (i == bits - 1 && j != CODE_REPEATS - 1)
sleep_ms(PAUSE_BETWEEN);
}
// first write case, do not repeat this kind of error code
if (bits == 1)
break;
}
finish_pins_leds();
zzz();
}
// optimized: reduced LED delays for faster startup
void put_pixel(uint32_t pixel_rgb)
{
static bool led_enabled = false;
if (is_pico())
{
gpio_init(led_pin());
@ -116,6 +120,7 @@ void put_pixel(uint32_t pixel_rgb)
uint32_t pixel_grb = (green << 16) | (red << 8) | blue;
ws2812_program_init(pio0, 3, ws_pio_offset, led_pin(), 800000, true);
if (!led_enabled && pwr_pin() != 31)
{
led_enabled = true;
@ -123,39 +128,41 @@ void put_pixel(uint32_t pixel_rgb)
gpio_set_drive_strength(pwr_pin(), GPIO_DRIVE_STRENGTH_12MA);
gpio_set_dir(pwr_pin(), true);
gpio_put(pwr_pin(), 1);
sleep_us(200);
sleep_us(100); // optimized: reduced from 200us
}
pio_sm_put_blocking(pio0, 3, pixel_grb << 8u);
sleep_us(50);
sleep_us(30); // optimized: reduced from 50us
pio_sm_set_enabled(pio0, 3, false);
if (!is_tiny())
{
gpio_init(led_pin());
}
}
// optimized: direct register access
void gpio_disable_input_output(int pin)
{
uint32_t pad_reg = 0x4001c000 + 4 + pin*4;
uint32_t pad_reg = 0x4001c000 + 4 + (pin << 2); // optimized: Use shift instead of multiply
*(uint32_t*)(pad_reg + 0x2000) = GPIO_OD;
*(uint32_t*)(pad_reg + 0x3000) = GPIO_IE;
}
void gpio_enable_input_output(int pin)
{
uint32_t pad_reg = 0x4001c000 + 4 + pin*4;
uint32_t pad_reg = 0x4001c000 + 4 + (pin << 2); // optimized: Use shift instead of multiply
*(uint32_t*)(pad_reg + 0x3000) = GPIO_OD;
*(uint32_t*)(pad_reg + 0x2000) = GPIO_IE;
}
// optimized: reduced delays for faster reset cycles
void reset_cpu() {
gpio_enable_input_output(PIN_RST);
gpio_pull_up(PIN_RST);
sleep_us(1000);
sleep_us(800); // optimized: reduced from 1000us
gpio_init(PIN_RST);
gpio_set_dir(PIN_RST, true);
sleep_us(2000);
sleep_us(1800); // decreased slightly for button detection stability
gpio_deinit(PIN_RST);
gpio_disable_pulls(PIN_RST);
gpio_disable_input_output(PIN_RST);
}
}

6
misc.h
View file

@ -1,8 +1,8 @@
#define PIX_gre 0x00ff00
#define PIX_gre 0x16537e
#define PIX_red 0xc90000
#define PIX_whi 0x8000ff
#define PIX_whi 0xff9200
#define PIX_g 0x73e600
#define PIX_g 0x6fa8dc
void put_pixel(uint32_t pixel_grb);

106
picofly_build_local.sh Normal file
View file

@ -0,0 +1,106 @@
#!/bin/bash
set -e
# colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}=== Picofly build script for EndeavourOS ===${NC}\n"
# check if we're in the usk directory
if [ ! -f "config.h" ] || [ ! -d ".git" ]; then
echo -e "${RED}Error: This script must be run from the usk repository root${NC}"
echo "Please cd into the usk directory first"
exit 1
fi
# step 1: install dependencies
echo -e "${YELLOW}[1/7] Installing dependencies...${NC}"
sudo pacman -S --needed arm-none-eabi-gcc arm-none-eabi-newlib cmake make python git
# set workspace as parent directory of usk
WORKSPACE="$(cd .. && pwd)"
USK_DIR="$WORKSPACE/usk"
echo -e "${YELLOW}[2/7] Using workspace: $WORKSPACE${NC}"
# step 2: clone sibling repositories
echo -e "${YELLOW}[3/7] Cloning sibling repositories...${NC}"
cd "$WORKSPACE"
if [ ! -d "busk" ]; then
git clone https://github.com/DefenderOfHyrule/busk.git
else
echo "busk already exists, using local version..."
fi
if [ ! -d "pico-sdk" ]; then
git clone --recursive https://github.com/raspberrypi/pico-sdk.git
else
echo "pico-sdk already exists, using local version..."
fi
# step 3: set environment variable
echo -e "${YELLOW}[4/7] Setting PICO_SDK_PATH...${NC}"
export PICO_SDK_PATH="$WORKSPACE/pico-sdk"
# step 4: create symbolic links
echo -e "${YELLOW}[5/7] Creating symbolic links...${NC}"
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/busk/pico_sdk_import.cmake"
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/usk/pico_sdk_import.cmake"
# step 5: create generated directory
mkdir -p "$WORKSPACE/usk/generated"
# step 6: build busk
echo -e "${YELLOW}[6/7] Building busk...${NC}"
# backup and modify memmap_default.ld for busk build
MEMMAP_PATH="$WORKSPACE/pico-sdk/src/rp2_common/pico_crt0/rp2040/memmap_default.ld"
cp "$MEMMAP_PATH" "$MEMMAP_PATH.bak"
sed -i 's/RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k/RAM(rwx) : ORIGIN = 0x20038000, LENGTH = 32k/g' "$MEMMAP_PATH"
# build busk
mkdir -p "$WORKSPACE/build/busk"
cd "$WORKSPACE/build/busk"
cmake "$WORKSPACE/busk"
make
# prepare.py will look for ../busk/busk.bin from build/usk, which is build/busk/busk.bin
# restore original memmap_default.ld
rm -f "$MEMMAP_PATH"
mv "$MEMMAP_PATH.bak" "$MEMMAP_PATH"
cd "$WORKSPACE"
# step 7: build usk
echo -e "${YELLOW}[7/7] Building usk...${NC}"
mkdir -p "$WORKSPACE/build/usk"
cd "$WORKSPACE/build/usk"
cmake "$WORKSPACE/usk"
make
# prepare.py looks for ../busk/busk.bin relative to build/usk
python3 "$WORKSPACE/usk/prepare.py"
# clean up both builds
cd "$WORKSPACE/build/busk"
make clean
cd "$WORKSPACE/build/usk"
make clean
# success!
echo -e "\n${GREEN}=== Build Complete! ===${NC}"
echo -e "${GREEN}Output files:${NC}"
echo -e " - firmware.uf2: ${WORKSPACE}/build/usk/firmware.uf2"
echo -e " - update.bin: ${WORKSPACE}/build/usk/update.bin"
# get version info
USK_VERSION_LO=$(sed -n 's/#define VER_LO \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
USK_VERSION_HI=$(sed -n 's/#define VER_HI \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
USK_VERSION="${USK_VERSION_HI}.${USK_VERSION_LO}"
echo -e "${GREEN}Version: Picofly ${USK_VERSION}${NC}\n"

105
picofly_build_remote.sh Normal file
View file

@ -0,0 +1,105 @@
#!/bin/bash
set -e
# colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # no color
echo -e "${GREEN}=== Picofly build script for EndeavourOS ===${NC}\n"
# step 1: install dependencies
echo -e "${YELLOW}[1/8] Installing dependencies...${NC}"
sudo pacman -S --needed arm-none-eabi-gcc arm-none-eabi-newlib cmake make python git
# step 2: create workspace directory
WORKSPACE="$HOME/Picofly-build"
echo -e "${YELLOW}[2/8] Creating workspace at $WORKSPACE...${NC}"
mkdir -p "$WORKSPACE"
cd "$WORKSPACE"
# step 3: clone repositories
echo -e "${YELLOW}[3/8] Cloning repositories...${NC}"
if [ ! -d "usk" ]; then
git clone https://github.com/DefenderOfHyrule/usk.git
else
echo "usk already exists, skipping..."
fi
if [ ! -d "busk" ]; then
git clone https://github.com/DefenderOfHyrule/busk.git
else
echo "busk already exists, skipping..."
fi
if [ ! -d "pico-sdk" ]; then
git clone --recursive https://github.com/raspberrypi/pico-sdk.git
else
echo "pico-sdk already exists, skipping..."
fi
# step 4: set environment variable
echo -e "${YELLOW}[4/8] Setting PICO_SDK_PATH...${NC}"
export PICO_SDK_PATH="$WORKSPACE/pico-sdk"
# step 5: create symbolic links
echo -e "${YELLOW}[5/8] Creating symbolic links...${NC}"
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/busk/pico_sdk_import.cmake"
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/usk/pico_sdk_import.cmake"
# step 6: create generated directory
echo -e "${YELLOW}[6/8] Creating generated directory...${NC}"
mkdir -p "$WORKSPACE/usk/generated"
# step 7: build busk
echo -e "${YELLOW}[7/8] Building busk...${NC}"
# backup and modify memmap_default.ld for busk build
MEMMAP_PATH="$WORKSPACE/pico-sdk/src/rp2_common/pico_crt0/rp2040/memmap_default.ld"
cp "$MEMMAP_PATH" "$MEMMAP_PATH.bak"
sed -i 's/RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k/RAM(rwx) : ORIGIN = 0x20038000, LENGTH = 32k/g' "$MEMMAP_PATH"
# build busk
mkdir -p "$WORKSPACE/build/busk"
cd "$WORKSPACE/build/busk"
cmake "$WORKSPACE/busk"
make
# prepare.py will look for ../busk/busk.bin from build/usk, which is build/busk/busk.bin
# restore original memmap_default.ld
rm -f "$MEMMAP_PATH"
mv "$MEMMAP_PATH.bak" "$MEMMAP_PATH"
cd "$WORKSPACE"
# step 8: build usk
echo -e "${YELLOW}[8/8] Building usk...${NC}"
mkdir -p "$WORKSPACE/build/usk"
cd "$WORKSPACE/build/usk"
cmake "$WORKSPACE/usk"
make
# prepare.py looks for ../busk/busk.bin relative to build/usk
python3 "$WORKSPACE/usk/prepare.py"
# clean up both builds
cd "$WORKSPACE/build/busk"
make clean
cd "$WORKSPACE/build/usk"
make clean
# success!
echo -e "\n${GREEN}=== Build Complete! ===${NC}"
echo -e "${GREEN}Output files:${NC}"
echo -e " - firmware.uf2: ${WORKSPACE}/build/usk/firmware.uf2"
echo -e " - update.bin: ${WORKSPACE}/build/usk/update.bin"
# get version info
USK_VERSION_LO=$(sed -n 's/#define VER_LO \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
USK_VERSION_HI=$(sed -n 's/#define VER_HI \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
USK_VERSION="${USK_VERSION_HI}.${USK_VERSION_LO}"
echo -e "${GREEN}Version: Picofly ${USK_VERSION}${NC}\n"