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
3 changes: 1 addition & 2 deletions examples/raspberrypi/rp2xxx/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ pub fn build(b: *std.Build) void {
const specific_examples: []const Example = &.{
// RaspberryPi Boards:
.{ .target = raspberrypi.pico, .name = "pico_board_blinky", .file = "src/board_blinky.zig" },
.{ .target = raspberrypi.pico, .name = "pico_flash-program", .file = "src/rp2040_only/flash_program.zig" },
.{ .target = raspberrypi.pico, .name = "pico_random", .file = "src/rp2040_only/random.zig" },
.{ .target = raspberrypi.pico, .name = "pico_rtc", .file = "src/rp2040_only/rtc.zig" },
.{ .target = raspberrypi.pico, .name = "pico_multicore", .file = "src/blinky_core1.zig" },
Expand All @@ -35,7 +34,6 @@ pub fn build(b: *std.Build) void {
.{ .target = raspberrypi.pico2_arm, .name = "pico2_arm_freertos-multitask-demo", .file = "src/freertos/multitask_demo.zig" },

.{ .target = raspberrypi.pico_flashless, .name = "pico_flashless_blinky", .file = "src/blinky.zig" },
.{ .target = raspberrypi.pico_flashless, .name = "pico_flashless_flash-program", .file = "src/rp2040_only/flash_program.zig" },

.{ .target = raspberrypi.pico2_arm_flashless, .name = "pico2_arm_flashless_blinky", .file = "src/blinky.zig" },
.{ .target = raspberrypi.pico2_riscv_flashless, .name = "pico2_riscv_flashless_blinky", .file = "src/blinky.zig" },
Expand Down Expand Up @@ -108,6 +106,7 @@ pub fn build(b: *std.Build) void {
.{ .name = "net-tcp_client", .file = "src/net/tcp_client.zig" },
.{ .name = "net-tcp_server", .file = "src/net/tcp_server.zig" },
.{ .name = "board-id", .file = "src/board_id.zig" },
.{ .name = "flash-program", .file = "src/flash_program.zig" },
};

var available_examples: std.array_list.Managed(Example) = .init(b.allocator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ const microzig = @import("microzig");

const rp2xxx = microzig.hal;
const flash = rp2xxx.flash;
const time = rp2xxx.time;
const gpio = rp2xxx.gpio;
const clocks = rp2xxx.clocks;

const led = gpio.num(25);
const uart = rp2xxx.uart.instance.num(0);
Expand Down
2 changes: 0 additions & 2 deletions port/raspberrypi/rp2xxx/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,6 @@ pub fn init(dep: *std.Build.Dependency) Self {
.name = "RaspberryPi Pico (ram image)",
.url = "https://www.raspberrypi.com/products/raspberry-pi-pico/",
.root_source_file = b.path("src/boards/raspberry_pi_pico.zig"),
// needed by the flash hal
.imports = rp2040_bootrom_imports,
},
}),
.pico2_arm = chip_rp2350_arm.derive(.{
Expand Down
10 changes: 4 additions & 6 deletions port/raspberrypi/rp2xxx/src/boards/raspberry_pi_pico.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ const microzig = @import("microzig");
const hal = microzig.hal;
const pins = hal.pins;

pub const bootrom = @import("shared/rp2040_bootrom.zig");

comptime {
_ = bootrom;
}

pub const pin_config = pins.GlobalConfiguration{
.GPIO25 = .{
.name = "led",
.function = .SIO,
},
};

comptime {
_ = @import("shared/rp2040_bootrom.zig");
}
57 changes: 29 additions & 28 deletions port/raspberrypi/rp2xxx/src/boards/shared/rp2040_bootrom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,41 @@ const chip = microzig.hal.compatibility.chip;

comptime {
if (chip == .RP2040 and !microzig.config.ram_image) {
_ = bootloader_data;
_ = BootromData.bootloader_data;
}
}

/// Raw stage2 bootrom
pub const stage2_rom: []const u8 = @embedFile("bootloader");
const BootromData = struct {
fn prepare_boot_sector(comptime stage2_rom: []const u8) [256]u8 {
@setEvalBranchQuota(10_000);

pub export const bootloader_data: [256]u8 linksection(".boot2") = blk: {
@setEvalBranchQuota(10_000);
var bootrom: [256]u8 = @splat(0xFF);
@memcpy(bootrom[0..stage2_rom.len], stage2_rom);

var bootrom: [256]u8 = @splat(0xFF);
@memcpy(bootrom[0..stage2_rom.len], stage2_rom);
// 2.8.1.3.1. Checksum
// The last four bytes of the image loaded from flash (which we hope is a valid flash second stage) are a CRC32 checksum
// of the first 252 bytes. The parameters of the checksum are:
// • Polynomial: 0x04c11db7
// • Input reflection: no
// • Output reflection: no
// • Initial value: 0xffffffff
// • Final XOR: 0x00000000
// • Checksum value appears as little-endian integer at end of image
// The Bootrom makes 128 attempts of approximately 4ms each for a total of approximately 0.5 seconds before giving up
// and dropping into USB code to load and checksum the second stage with varying SPI parameters. If it sees a checksum
// pass it will immediately jump into the 252-byte payload which contains the flash second stage.
const Hash = std.hash.crc.Crc(u32, .{
.polynomial = 0x04c11db7,
.initial = 0xffffffff,
.reflect_input = false,
.reflect_output = false,
.xor_output = 0x00000000,
});

// 2.8.1.3.1. Checksum
// The last four bytes of the image loaded from flash (which we hope is a valid flash second stage) are a CRC32 checksum
// of the first 252 bytes. The parameters of the checksum are:
// • Polynomial: 0x04c11db7
// • Input reflection: no
// • Output reflection: no
// • Initial value: 0xffffffff
// • Final XOR: 0x00000000
// • Checksum value appears as little-endian integer at end of image
// The Bootrom makes 128 attempts of approximately 4ms each for a total of approximately 0.5 seconds before giving up
// and dropping into USB code to load and checksum the second stage with varying SPI parameters. If it sees a checksum
// pass it will immediately jump into the 252-byte payload which contains the flash second stage.
const Hash = std.hash.crc.Crc(u32, .{
.polynomial = 0x04c11db7,
.initial = 0xffffffff,
.reflect_input = false,
.reflect_output = false,
.xor_output = 0x00000000,
});
std.mem.writeInt(u32, bootrom[252..256], Hash.hash(bootrom[0..252]), .little);

std.mem.writeInt(u32, bootrom[252..256], Hash.hash(bootrom[0..252]), .little);
return bootrom;
}

break :blk bootrom;
export const bootloader_data: [256]u8 linksection(".boot2") = prepare_boot_sector(@embedFile("bootloader"));
};
83 changes: 46 additions & 37 deletions port/raspberrypi/rp2xxx/src/hal/flash.zig
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! See [rp2040 docs](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf), page 136.
const rom = @import("rom.zig");

/// Based on https://github.com/raspberrypi/pico-sdk/blob/a1438dff1d38bd9c65dbd693f0e5db4b9ae91779/src/rp2_common/hardware_flash/flash.c#L41
const microzig = @import("microzig");
const peripherals = microzig.chip.peripherals;

const IO_QSPI = peripherals.IO_QSPI;
const XIP_SSI = peripherals.SSI;

const rom = @import("rom.zig");
const compatibility = @import("compatibility.zig");

pub const Command = enum(u8) {
block_erase = 0xd8,
ruid_cmd = 0x4b,
Expand All @@ -16,15 +16,24 @@ pub const PAGE_SIZE = 256;
pub const SECTOR_SIZE = 4096;
pub const BLOCK_SIZE = 65536;

/// Bus reads to a 16MB memory window start at this address
/// Bus reads to a 16MB memory window start at this address.
pub const XIP_BASE = 0x10000000;

/// Flash code related to the second stage boot loader
/// After the bootrom enters the user application, a copy of the flash XIP
/// setup function is found here on RP2350.
pub const BOOTRAM_BASE = 0x400e0000;

/// Infrastructure for reentering XIP mode after exiting for programming.
///
pub const boot2 = if (!microzig.config.ram_image) struct {
/// Size of the second stage bootloader in words
const BOOT2_SIZE_WORDS = 64;

/// Buffer for the second stage bootloader
var copyout: [BOOT2_SIZE_WORDS]u32 = undefined;
var copyout_valid: bool = false;

/// Copies the XIP setup function into RAM:
///
/// - On RP2040 this *is* the second stage bootloader
///
/// The only job of the second stage bootloader is to configure the SSI and
/// the external flash for the best possible execute-in-place (XIP)
Expand All @@ -34,59 +43,59 @@ pub const boot2 = if (!microzig.config.ram_image) struct {
/// `rom.flash_exit_xip`. This is required if we want to erase and/or write
/// to flash.
///
/// At the end we can then just make a subroutine call to copyout, to
/// configure the SSI and flash. The second stage bootloader will return to
/// the calling function if a return address is provided in `lr`.
var copyout: [BOOT2_SIZE_WORDS]u32 = undefined;
var copyout_valid: bool = false;

/// Copy the 2nd stage bootloader into memory
/// - On RP2350 it is found at BOOTRAM_BASE.
///
/// ** From RP2350 datasheet section 4.3 **
/// It is physically impossible to execute code from boot RAM, regardless
/// of MPU configuration, as it is on the APB peripheral bus segment, which
/// is not wired to the processor instruction fetch ports.
///
/// Therefore, we must make a copy of it first.
///
/// This is required by `_range_erase` and `_range_program` so we can later
/// setup XIP via the second stage bootloader.
pub export fn flash_init() linksection(".ram_text") void {
if (copyout_valid) return;
const bootloader = @as([*]u32, @ptrFromInt(XIP_BASE));
const bootloader = @as([*]u32, @ptrFromInt(switch (compatibility.chip) {
.RP2040 => XIP_BASE,
.RP2350 => BOOTRAM_BASE,
}));
var i: usize = 0;
while (i < BOOT2_SIZE_WORDS) : (i += 1) {
copyout[i] = bootloader[i];
}
asm volatile ("" ::: .{ .memory = true }); // memory barrier
copyout_valid = true;
}

/// Configure the SSI and the external flash for XIP by calling the second
/// stage bootloader that was copied out to `copyout`.
/// Configure the SSI and the external flash for XIP using the XIP setup
/// function that was copied out to `copyout`.
pub export fn flash_enable_xip() linksection(".ram_text") void {
// The bootloader is in thumb mode
asm volatile (
\\adds r0, #1
\\blx r0
:
: [copyout] "{r0}" (@intFromPtr(&copyout)),
: .{ .r0 = true, .r14 = true });

// Calling boot2 as a function works because it accepts a return vector in
// LR (and doesn't trash r4-r7). Bootrom passes NULL in LR, instructing
// boot2 to enter flash vector table's reset handler.

const thumb_bit = switch (compatibility.arch) {
.arm => 1,
.riscv => 0,
};
@as(*const fn () callconv(.c) void, @ptrFromInt(@intFromPtr(&copyout) + thumb_bit))();
}
} else struct {
// no op
pub inline fn flash_init() linksection(".ram_text") void {}

/// Configure the SSI and the external flash for XIP by calling the second
/// stage bootloader embedded into RAM.
// Fallback. This is a very slow XIP configuration, but is very widely
// supported.
pub inline fn flash_enable_xip() linksection(".ram_text") void {
// The bootloader is in thumb mode
asm volatile (
\\adds r0, #1
\\blx r0
:
: [copyout] "{r0}" (@intFromPtr(microzig.board.bootrom.stage2_rom.ptr)),
: .{ .r0 = true, .r14 = true });
rom.flash_enter_cmd_xip();
}
};

/// Erase count bytes starting at offset (offset from start of flash)
///
/// The offset must be aligned to a 4096-byte sector, and count must be a
/// multiple of 4096 bytes!
pub inline fn range_erase(offset: u32, count: u32) void {
pub inline fn range_erase(offset: u33, count: u32) void {
// Do not inline `_range_erase`!
@call(.never_inline, _range_erase, .{ offset, count });
}
Expand Down
Loading