Skip to content

Add rp2040 ram image (flashless) target board #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 14, 2025
5 changes: 5 additions & 0 deletions build-internals/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ pub const Target = struct {
/// (optional) Provide a custom linker script for the hardware or define a custom generation.
linker_script: ?LazyPath = null,

/// (Optional) Explicitly set the entry point
entry: ?Build.Step.Compile.Entry = null,

/// (optional) Post processing step that will patch up and modify the elf file if necessary.
patch_elf: ?*const fn (*Build.Dependency, LazyPath) LazyPath = null,

Expand All @@ -71,6 +74,7 @@ pub const Target = struct {
hal: ?HardwareAbstractionLayer = null,
board: ?Board = null,
linker_script: ?LazyPath = null,
entry: ?Build.Step.Compile.Entry = null,
patch_elf: ?*const fn (*Build.Dependency, LazyPath) LazyPath = null,
};

Expand All @@ -88,6 +92,7 @@ pub const Target = struct {
.hal = options.hal orelse from.hal,
.board = options.board orelse from.board,
.linker_script = options.linker_script orelse from.linker_script,
.entry = options.entry orelse from.entry,
.patch_elf = options.patch_elf orelse from.patch_elf,
};
return ret;
Expand Down
4 changes: 4 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ pub fn MicroBuild(port_select: PortSelect) type {
/// If set, overrides the `linker_script` property of the target.
linker_script: ?LazyPath = null,

/// If set, overrides the default `entry` property of the arget.
entry: ?Build.Step.Compile.Entry = null,

/// Strips stack trace info from final executable.
strip: bool = false,

Expand Down Expand Up @@ -538,6 +541,7 @@ pub fn MicroBuild(port_select: PortSelect) type {
fw.artifact.link_gc_sections = options.strip_unused_symbols;
fw.artifact.link_function_sections = options.strip_unused_symbols;
fw.artifact.link_data_sections = options.strip_unused_symbols;
fw.artifact.entry = options.entry orelse target.entry orelse .default;

fw.artifact.root_module.addImport("microzig", core_mod);
fw.artifact.root_module.addImport("app", app_mod);
Expand Down
105 changes: 69 additions & 36 deletions core/src/cpus/cortex_m.zig
Original file line number Diff line number Diff line change
Expand Up @@ -587,49 +587,68 @@ pub const startup_logic = struct {
extern var microzig_bss_end: u8;
extern const microzig_data_load_start: u8;

pub fn ram_image_entrypoint() linksection(".entry") callconv(.naked) void {
asm volatile (
\\
// Set VTOR to point to ram table
\\mov r0, %[_vector_table]
\\mov r1, %[_VTOR_ADDRESS]
\\str r0, [r1]
// Set up stack and jump to _start
\\ldm r0!, {r1, r2}
\\msr msp, r1
\\bx r2
:
: [_vector_table] "r" (&startup_logic._vector_table),
[_VTOR_ADDRESS] "r" (&peripherals.scb.VTOR),
: "memory", "r0", "r1", "r2"
);
}

pub fn _start() callconv(.c) noreturn {
if (comptime !is_ramimage()) {
// fill .bss with zeroes
{
const bss_start: [*]u8 = @ptrCast(&microzig_bss_start);
const bss_end: [*]u8 = @ptrCast(&microzig_bss_end);
const bss_len = @intFromPtr(bss_end) - @intFromPtr(bss_start);

@memset(bss_start[0..bss_len], 0);
}

// fill .bss with zeroes
{
const bss_start: [*]u8 = @ptrCast(&microzig_bss_start);
const bss_end: [*]u8 = @ptrCast(&microzig_bss_end);
const bss_len = @intFromPtr(bss_end) - @intFromPtr(bss_start);
// load .data from flash
{
const data_start: [*]u8 = @ptrCast(&microzig_data_start);
const data_end: [*]u8 = @ptrCast(&microzig_data_end);
const data_len = @intFromPtr(data_end) - @intFromPtr(data_start);
const data_src: [*]const u8 = @ptrCast(&microzig_data_load_start);

@memset(bss_start[0..bss_len], 0);
}
@memcpy(data_start[0..data_len], data_src[0..data_len]);
}

// load .data from flash
{
const data_start: [*]u8 = @ptrCast(&microzig_data_start);
const data_end: [*]u8 = @ptrCast(&microzig_data_end);
const data_len = @intFromPtr(data_end) - @intFromPtr(data_start);
const data_src: [*]const u8 = @ptrCast(&microzig_data_load_start);
// Move vector table to RAM if requested
if (interrupt.has_ram_vectors()) {
// Copy vector table to RAM and set VTOR to point to it

@memcpy(data_start[0..data_len], data_src[0..data_len]);
}
if (comptime interrupt.has_ram_vectors_section()) {
@export(&ram_vectors, .{
.name = "_ram_vectors",
.section = "ram_vectors",
.linkage = .strong,
});
} else {
@export(&ram_vectors, .{
.name = "_ram_vectors",
.linkage = .strong,
});
}

// Move vector table to RAM if requested
if (interrupt.has_ram_vectors()) {
// Copy vector table to RAM and set VTOR to point to it

if (interrupt.has_ram_vectors_section()) {
@export(&ram_vectors, .{
.name = "_ram_vectors",
.section = "ram_vectors",
.linkage = .strong,
});
} else {
@export(&ram_vectors, .{
.name = "_ram_vectors",
.linkage = .strong,
});
}

const flash_vector: [*]const usize = @ptrCast(&_vector_table);
const flash_vector: [*]const usize = @ptrCast(&_vector_table);

@memcpy(ram_vectors[0..vector_count], flash_vector[0..vector_count]);
@memcpy(ram_vectors[0..vector_count], flash_vector[0..vector_count]);

peripherals.scb.VTOR = @intFromPtr(&ram_vectors);
peripherals.scb.VTOR = @intFromPtr(&ram_vectors);
}
}

microzig_main();
Expand All @@ -655,14 +674,28 @@ pub const startup_logic = struct {
};
};

fn is_ramimage() bool {
// HACK
// TODO: Use microzig_options?
if (microzig.config.board_name) |board_name|
return std.mem.containsAtLeast(u8, board_name, 1, "ram image");
return false;
}

pub fn export_startup_logic() void {
if (is_ramimage())
@export(&startup_logic.ram_image_entrypoint, .{
.name = "_entry_point",
.linkage = .strong,
});

@export(&startup_logic._start, .{
.name = "_start",
});

@export(&startup_logic._vector_table, .{
.name = "_vector_table",
.section = "microzig_flash_start",
.section = ".isr_vector",
.linkage = .strong,
});
}
Expand Down
12 changes: 4 additions & 8 deletions examples/raspberrypi/rp2xxx/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub fn build(b: *std.Build) void {
const mz_dep = b.dependency("microzig", .{});
const mb = MicroBuild.init(b, mz_dep) orelse return;

const rp2040_only_examples: []const Example = &.{
const specific_examples: []const Example = &.{
// RaspberryPi Boards:
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-program", .file = "src/rp2040_only/flash_program.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_flash-id", .file = "src/rp2040_only/flash_id.zig" },
Expand All @@ -23,18 +23,15 @@ pub fn build(b: *std.Build) void {
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_hd44780", .file = "src/rp2040_only/hd44780.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pcf8574", .file = "src/rp2040_only/pcf8574.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_i2c_slave", .file = "src/rp2040_only/i2c_slave.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico_flashless, .name = "pico_flashless_blinky", .file = "src/blinky.zig" },

// WaveShare Boards:
.{ .target = mb.ports.rp2xxx.boards.waveshare.rp2040_matrix, .name = "rp2040-matrix_tiles", .file = "src/rp2040_only/tiles.zig" },
.{ .target = mb.ports.rp2xxx.boards.waveshare.rp2040_matrix, .name = "rp2040_matrix_tiles", .file = "src/rp2040_only/tiles.zig" },
// .{ .target = "board:waveshare/rp2040_eth", .name = "rp2040-eth" },
// .{ .target = "board:waveshare/rp2040_plus_4m", .name = "rp2040-plus-4m" },
// .{ .target = "board:waveshare/rp2040_plus_16m", .name = "rp2040-plus-16m" },
};

const rp2350_only_examples: []const Example = &.{
// TODO: No RP2350 feature specific examples to show off yet
};

const chip_agnostic_examples: []const ChipAgnosticExample = &.{
.{ .name = "adc", .file = "src/adc.zig" },
.{ .name = "i2c-bus-scan", .file = "src/i2c_bus_scan.zig" },
Expand All @@ -60,8 +57,7 @@ pub fn build(b: *std.Build) void {
};

var available_examples = std.ArrayList(Example).init(b.allocator);
available_examples.appendSlice(rp2040_only_examples) catch @panic("out of memory");
available_examples.appendSlice(rp2350_only_examples) catch @panic("out of memory");
available_examples.appendSlice(specific_examples) catch @panic("out of memory");
for (chip_agnostic_examples) |example| {
available_examples.append(.{
.target = mb.ports.rp2xxx.boards.raspberrypi.pico,
Expand Down
10 changes: 10 additions & 0 deletions port/raspberrypi/rp2xxx/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ boards: struct {
},
raspberrypi: struct {
pico: *const microzig.Target,
pico_flashless: *const microzig.Target,
pico2_arm: *const microzig.Target,
pico2_riscv: *const microzig.Target,
},
Expand Down Expand Up @@ -163,6 +164,15 @@ pub fn init(dep: *std.Build.Dependency) Self {
.imports = rp2040_bootrom_imports,
},
}),
.pico_flashless = chip_rp2040.derive(.{
.entry = .{ .symbol_name = "_entry_point" },
.linker_script = b.path("rp2040_ram_image.ld"),
.board = .{
.name = "RaspberryPi Pico (ram image)",
.url = "https://www.raspberrypi.com/products/raspberry-pi-pico/",
.root_source_file = b.path("src/boards/raspberry_pi_pico2.zig"),
},
}),
.pico2_arm = chip_rp2350_arm.derive(.{
.board = .{
.name = "RaspberryPi Pico 2",
Expand Down
2 changes: 1 addition & 1 deletion port/raspberrypi/rp2xxx/rp2040.ld
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SECTIONS

.text :
{
KEEP(*(microzig_flash_start))
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
} > flash0
Expand Down
50 changes: 50 additions & 0 deletions port/raspberrypi/rp2xxx/rp2040_ram_image.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Target CPU: ARM Cortex-M0+
* Target Chip: RP2040
*/

MEMORY
{
entry (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00000100
ram0 (rwx) : ORIGIN = 0x20000100, LENGTH = 0x0003ff00
}

SECTIONS
{
.entry :
{
KEEP(*(.entry))
} > entry

.text :
{
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
} > ram0

.ARM.extab : {
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > ram0

.ARM.exidx : {
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > ram0

.data :
{
*(.data*)
} > ram0

.bss :
{
*(.bss*)
} > ram0

/* Unused, but set as extern in startup_logic */
microzig_data_start = .;
microzig_data_end = .;
microzig_bss_start = .;
microzig_bss_end = .;
microzig_data_load_start = .;
}
Loading