Enable XIP using stage 2 bootloader (#73)

* flash enable xip calls stage two bootloader using inline assembly

* flash erase/program now works in all modes (Debug, ReleaseSmall, ReleaseSafe, ReleaseFast)

* further docs added
wch-ch32v003
David Sugar 1 year ago committed by GitHub
parent d6b3be320f
commit 1cef56ad9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -62,7 +62,7 @@ pub fn main() !void {
// Note that a whole number of pages (256 bytes) must be written at a time // Note that a whole number of pages (256 bytes) must be written at a time
std.log.info("Programming target region...", .{}); std.log.info("Programming target region...", .{});
flash.range_program(flash_target_offset, &data); flash.range_program(flash_target_offset, data[0..]);
std.log.info("Done. Read back target region:", .{}); std.log.info("Done. Read back target region:", .{});
std.log.info("data: {s}", .{flash_target_contents[0..flash.PAGE_SIZE]}); std.log.info("data: {s}", .{flash_target_contents[0..flash.PAGE_SIZE]});

@ -43,7 +43,7 @@ SECTIONS
.data : .data :
{ {
microzig_data_start = .; microzig_data_start = .;
*(.time_critical*) KEEP(*(.time_critical*))
*(.data*) *(.data*)
microzig_data_end = .; microzig_data_end = .;
} > ram0 AT> flash0 } > ram0 AT> flash0

@ -1,3 +1,4 @@
//! See [rp2040 docs](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf), page 136.
const rom = @import("rom.zig"); const rom = @import("rom.zig");
pub const Command = enum(u8) { pub const Command = enum(u8) {
@ -12,33 +13,51 @@ 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; pub const XIP_BASE = 0x10000000;
/// Flash code related to the second stage boot loader
pub const boot2 = struct { pub const boot2 = struct {
/// Size of the second stage bootloader in bytes /// Size of the second stage bootloader in words
const BOOT2_SIZE_BYTES = 64; const BOOT2_SIZE_WORDS = 64;
/// Buffer for the second stage bootloader /// Buffer for the second stage bootloader
var copyout: [BOOT2_SIZE_BYTES]u32 = undefined; ///
/// 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) performance.
/// Until the SSI is correctly configured for the attached flash device, it's not
/// possible to access flash via the XIP address window, i.e., we have to copy
/// the bootloader into sram before calling `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; var copyout_valid: bool = false;
/// Copy the 2nd stage bootloader into memory /// Copy the 2nd stage bootloader into memory
pub fn flash_init() linksection(".time_critical") void { ///
/// 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(".time_critical") void {
if (copyout_valid) return; if (copyout_valid) return;
const bootloader = @as([*]u32, @ptrFromInt(XIP_BASE)); const bootloader = @as([*]u32, @ptrFromInt(XIP_BASE));
var i: usize = 0; var i: usize = 0;
while (i < BOOT2_SIZE_BYTES) : (i += 1) { while (i < BOOT2_SIZE_WORDS) : (i += 1) {
copyout[i] = bootloader[i]; copyout[i] = bootloader[i];
} }
copyout_valid = true; copyout_valid = true;
} }
pub fn flash_enable_xip() linksection(".time_critical") void { /// Configure the SSI and the external flash for XIP by calling the second stage
// TODO: use the second stage bootloader instead of cmd_xip /// bootloader that was copied out to `copyout`.
//const bootloader: []u32 = copyout[1..]; pub export fn flash_enable_xip() linksection(".time_critical") void {
// The bootloader is in thumb mode
//const f = @ptrCast(*fn () void, bootloader.ptr); asm volatile (
//f(); \\adds r0, #1
\\blx r0
rom.flash_enter_cmd_xip()(); :
: [copyout] "{r0}" (@intFromPtr(&copyout)),
: "r0", "lr"
);
} }
}; };
@ -46,12 +65,17 @@ pub const boot2 = struct {
/// ///
/// The offset must be aligned to a 4096-byte sector, and count must /// The offset must be aligned to a 4096-byte sector, and count must
/// be a multiple of 4096 bytes! /// be a multiple of 4096 bytes!
pub fn range_erase(offset: u32, count: u32) linksection(".time_critical") void { pub inline fn range_erase(offset: u32, count: u32) void {
// Do not inline `_range_erase`!
@call(.never_inline, _range_erase, .{ offset, count });
}
export fn _range_erase(offset: u32, count: u32) linksection(".time_critical") void {
// TODO: add sanity checks, e.g., offset + count < flash size // TODO: add sanity checks, e.g., offset + count < flash size
boot2.flash_init(); asm volatile ("" ::: "memory"); // memory barrier
// TODO: __compiler_memory_barrier boot2.flash_init();
rom.connect_internal_flash()(); rom.connect_internal_flash()();
rom.flash_exit_xip()(); rom.flash_exit_xip()();
@ -65,16 +89,21 @@ pub fn range_erase(offset: u32, count: u32) linksection(".time_critical") void {
/// ///
/// The offset must be aligned to a 256-byte boundary, and the length of data /// The offset must be aligned to a 256-byte boundary, and the length of data
/// must be a multiple of 256! /// must be a multiple of 256!
pub fn range_program(offset: u32, data: []const u8) linksection(".time_critical") void { pub inline fn range_program(offset: u32, data: []const u8) void {
// Do not inline `_range_program`!
@call(.never_inline, _range_program, .{ offset, data.ptr, data.len });
}
export fn _range_program(offset: u32, data: [*]const u8, len: usize) linksection(".time_critical") void {
// TODO: add sanity checks, e.g., offset + count < flash size // TODO: add sanity checks, e.g., offset + count < flash size
boot2.flash_init(); asm volatile ("" ::: "memory"); // memory barrier
// TODO: __compiler_memory_barrier boot2.flash_init();
rom.connect_internal_flash()(); rom.connect_internal_flash()();
rom.flash_exit_xip()(); rom.flash_exit_xip()();
rom.flash_range_program()(offset, data.ptr, data.len); rom.flash_range_program()(offset, data, len);
rom.flash_flush_cache()(); rom.flash_flush_cache()();
boot2.flash_enable_xip(); boot2.flash_enable_xip();

Loading…
Cancel
Save