11//! Access to functions and data in the RP2040 bootrom
22//!
3- //! The Bootrom contains a number of public functions that provide useful RP2040 functionality that might be needed in
4- //! the absence of any other code on the device, as well as highly optimized versions of certain key functionality that would
5- //! otherwise have to take up space in most user binaries.
3+ //! The Bootrom contains a number of public functions that provide useful RP2040 functionality
4+ //! that might be needed in the absence of any other code on the device,
5+ //! as well as highly optimized versions of certain key functionality
6+ //! that would otherwise have to take up space in most user binaries.
67//!
78//! The functions include:
89//! 1. Fast Bit Counting / Manipulation Functions
910//! 2. Fast Bulk Memory Fill / Copy Functions
1011//! 3. Flash Access Functions
11- //! 4. Debugging Support Functions (TODO)
12- //! 5. Miscellaneous Functions (TODO)
12+ //! 4. Debugging Support Functions
13+ //! 5. Miscellaneous Functions
1314
1415const std = @import ("std" );
1516const microzig = @import ("microzig" );
@@ -31,52 +32,94 @@ pub const Code = enum(u32) {
3132 flash_range_program = rom_table_code ('R' , 'P' ),
3233 flash_flush_cache = rom_table_code ('F' , 'C' ),
3334 flash_enter_cmd_xip = rom_table_code ('C' , 'X' ),
35+ debug_trampoline = rom_table_code ('D' , 'T' ),
36+ debug_trampoline_end = rom_table_code ('D' , 'E' ),
37+ reset_to_usb_boot = rom_table_code ('U' , 'B' ),
38+ wait_for_vector = rom_table_code ('W' , 'V' ),
3439};
3540
3641/// Signatures of all public bootrom functions
3742pub const signatures = struct {
3843 /// Returns the 32 bit pointer into the ROM if found or NULL otherwise
39- const rom_table_lookup = fn (table : * u16 , code : u32 ) * anyopaque ;
44+ const rom_table_lookup = fn (table : [ * ] const u16 , code : u32 ) callconv ( .C ) * anyopaque ;
4045 /// Signature for popcount32: Return a count of the number of 1 bits in value
41- const popcount32 = fn (value : u32 ) u32 ;
46+ const popcount32 = fn (value : u32 ) callconv ( .C ) u32 ;
4247 /// Signature for reverse32: Return the bits of value in the reverse order
43- const reverse32 = fn (value : u32 ) u32 ;
48+ const reverse32 = fn (value : u32 ) callconv ( .C ) u32 ;
4449 /// Signature for clz32: Return the number of consecutive high order 0 bits of value
45- const clz32 = fn (value : u32 ) u32 ;
50+ const clz32 = fn (value : u32 ) callconv ( .C ) u32 ;
4651 /// Signature for ctz32: Return the number of consecutive low order 0 bits of value
47- const ctz32 = fn (value : u32 ) u32 ;
52+ const ctz32 = fn (value : u32 ) callconv ( .C ) u32 ;
4853 /// Signature of memset: Sets n bytes start at ptr to the value c and returns ptr
49- const memset = fn (ptr : [* ]u8 , c : u8 , n : u32 ) [* ]u8 ;
50- /// Signature of memset4: Sets n bytes start at ptr to the value c and returns ptr; must be word (32-bit) aligned!
51- const memset4 = fn (ptr : [* ]u32 , c : u8 , n : u32 ) [* ]u32 ;
52- /// Signature of memcpy: Copies n bytes starting at src to dest and returns dest. The results are undefined if the regions overlap.
53- const memcpy = fn (dest : [* ]u8 , src : [* ]const u8 , n : u32 ) [* ]u8 ;
54- /// Signature of memcpy44: Copies n bytes starting at src to dest and returns dest; must be word (32-bit) aligned!
55- const memcpy44 = fn (dest : [* ]u32 , src : [* ]const u32 , n : u32 ) [* ]u8 ;
56- /// Signature of connect_internal_flash: Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads
57- const connect_internal_flash = fn () void ;
58- /// Signature of flash_exit_xip: First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence described in
59- /// Section 2.8.1.2. Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be
60- /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This function
61- /// configures the SSI with a fixed SCK clock divisor of /6.
62- const flash_exit_xip = fn () void ;
63- /// Signature of flash_range_erase: Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a block erase command
64- /// e.g. D8h block erase, and the size of the block erased by this command — this function will use the larger
65- /// block erase where possible, for much higher erase speed. addr must be aligned to a 4096-byte sector, and
66- /// count must be a multiple of 4096 bytes.
67- const flash_range_erase = fn (addr : u32 , count : usize , block_size : u32 , block_cmd : u8 ) void ;
68- /// Signature of flash_range_program: Program data to a range of flash addresses starting at addr (offset from the start of flash) and count bytes
69- /// in size. addr must be aligned to a 256-byte boundary, and count must be a multiple of 256.
70- const flash_range_program = fn (addr : u32 , data : [* ]const u8 , count : usize ) void ;
71- /// Signature of flash_flush_cache: Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can drive the
72- /// flash chip select as normal.
73- const flash_flush_cache = fn () void ;
74- /// Signature of flash_enter_cmd_xip: Configure the SSI to generate a standard 03h serial read command, with 24 address bits, upon each XIP
75- /// access. This is a very slow XIP configuration, but is very widely supported. The debugger calls this
76- /// function after performing a flash erase/programming operation, so that the freshly-programmed code
77- /// and data is visible to the debug host, without having to know exactly what kind of flash device is
78- /// connected.
79- const flash_enter_cmd_xip = fn () void ;
54+ const memset = fn (ptr : [* ]u8 , c : u8 , n : u32 ) callconv (.C ) [* ]u8 ;
55+ /// Signature of memset4: Sets n bytes start at ptr to the value c and returns ptr;
56+ /// must be word (32-bit) aligned!
57+ const memset4 = fn (ptr : [* ]u32 , c : u8 , n : u32 ) callconv (.C ) [* ]u32 ;
58+ /// Signature of memcpy: Copies n bytes starting at src to dest and returns dest.
59+ /// The results are undefined if the regions overlap.
60+ const memcpy = fn (dest : [* ]u8 , src : [* ]const u8 , n : u32 ) callconv (.C ) [* ]u8 ;
61+ /// Signature of memcpy44: Copies n bytes starting at src to dest and returns dest;
62+ /// must be word (32-bit) aligned!
63+ const memcpy44 = fn (dest : [* ]u32 , src : [* ]const u32 , n : u32 ) callconv (.C ) [* ]u8 ;
64+ /// Signature of connect_internal_flash: Restore all QSPI pad controls to their
65+ /// default state, and connect the SSI to the QSPI pads
66+ const connect_internal_flash = fn () callconv (.C ) void ;
67+ /// Signature of flash_exit_xip: First set up the SSI for serial-mode operations,
68+ /// then issue the fixed XIP exit sequence described in Section 2.8.1.2. Note that
69+ /// the bootrom code uses the IO forcing logic to drive the CS pin, which must be
70+ /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache).
71+ /// This function configures the SSI with a fixed SCK clock divisor of /6.
72+ const flash_exit_xip = fn () callconv (.C ) void ;
73+ /// Signature of flash_range_erase: Erase a count bytes, starting at addr (offset from
74+ /// start of flash). Optionally, pass a block erase command e.g. D8h block erase,
75+ /// and the size of the block erased by this command — this function will use the
76+ /// larger block erase where possible, for much higher erase speed. addr must be aligned
77+ /// to a 4096-byte sector, and count must be a multiple of 4096 bytes.
78+ const flash_range_erase = fn (
79+ addr : u32 ,
80+ count : usize ,
81+ block_size : u32 ,
82+ block_cmd : u8 ,
83+ ) callconv (.C ) void ;
84+ /// Signature of flash_range_program: Program data to a range of flash addresses
85+ /// starting at addr (offset from the start of flash) and count bytes in size.
86+ /// addr must be aligned to a 256-byte boundary, and count must be a multiple of 256.
87+ const flash_range_program = fn (addr : u32 , data : [* ]const u8 , count : usize ) callconv (.C ) void ;
88+ /// Signature of flash_flush_cache: Flush and enable the XIP cache. Also clears the IO
89+ /// forcing on QSPI CSn, so that the SSI can drive the flash chip select as normal.
90+ const flash_flush_cache = fn () callconv (.C ) void ;
91+ /// Signature of flash_enter_cmd_xip: Configure the SSI to generate
92+ /// a standard 03h serial read command, with 24 address bits, upon each XIP access.
93+ /// This is a very slow XIP configuration, but is very widely supported.
94+ /// The debugger calls this function after performing a flash erase/programming operation,
95+ /// so that the freshly-programmed code and data is visible to the debug host,
96+ /// without having to know exactly what kind of flash device is connected.
97+ const flash_enter_cmd_xip = fn () callconv (.C ) void ;
98+ /// Signature of debug_trampoline: Simple debugger trampoline for break-on-return.
99+ /// This methods helps the debugger call ROM routines without setting hardware breakpoints.
100+ /// The function address is passed in r7 and args are passed through r0 … r3 as per ABI.
101+ /// This method does not return but executes a BKPT #0 at the end
102+ const debug_trampoline = fn () callconv (.C ) void ;
103+ /// Signature of debug_trampoline_end: This is the address of the final BKPT #0 instruction
104+ /// of debug_trampoline. This can be compared with the program counter to detect
105+ /// completion of the debug_trampoline call.
106+ const debug_trampoline_end = fn () callconv (.C ) void ;
107+ /// Signature of reset_to_usb_boot: Resets the RP2040 and uses the watchdog facility
108+ /// to re-start in BOOTSEL mode:
109+ /// - gpio_activity_pin_mask is provided to enable an "activity light" via
110+ /// GPIO attached LED for the USB Mass Storage Device:
111+ /// - 0 No pins are used as per a cold boot.
112+ /// - Otherwise a single bit set indicating which GPIO pin should be set to output and raised
113+ /// whenever there is mass storage activity from the host.
114+ /// - disable_interface_mask may be used to control the exposed USB interfaces:
115+ /// - 0 To enable both interfaces (as per a cold boot)
116+ /// - 1 To disable the USB Mass Storage Interface (see Section 2.8.4)
117+ /// - 2 To disable the USB PICOBOOT Interface (see Section 2.8.5)
118+ const reset_to_usb_boot = fn (gpio_activity_mask : u32 , disable_interface_mask : u32 ) callconv (.C ) noreturn ;
119+ /// Signature of wait_for_vector: This is the method that is entered by core 1 on reset to wait
120+ /// to be launched by core 0. There are few cases where you should call this method (resetting
121+ /// core 1 is much better). This method does not return and should only ever be called on core 1.
122+ const wait_for_vector = fn () callconv (.C ) noreturn ;
80123};
81124
82125/// Return a bootrom lookup code based on two ASCII characters
@@ -91,7 +134,7 @@ pub const signatures = struct {
91134///
92135/// A 32 bit address pointing into bootrom
93136pub fn rom_table_code (c1 : u8 , c2 : u8 ) u32 {
94- return @as (u32 , @intCast ( c1 )) | (@as (u32 , @intCast ( c2 ) ) << 8 );
137+ return @as (u32 , c1 ) | (@as (u32 , c2 ) << 8 );
95138}
96139
97140/// Convert a 16 bit pointer stored at the given rom address into a pointer
@@ -103,8 +146,8 @@ pub fn rom_table_code(c1: u8, c2: u8) u32 {
103146///
104147/// The converted pointer
105148pub inline fn rom_hword_as_ptr (rom_addr : u32 ) * anyopaque {
106- const ptr_to_ptr = @as ( * u16 , @ptrFromInt (rom_addr ) );
107- return @as ( * anyopaque , @ ptrFromInt (@as ( usize , @intCast ( ptr_to_ptr .* ))) );
149+ const ptr_to_ptr : * const u16 = @ptrFromInt (rom_addr );
150+ return @ptrFromInt (ptr_to_ptr .* );
108151}
109152
110153/// Lookup a bootrom function by code (inline)
@@ -115,9 +158,9 @@ pub inline fn rom_hword_as_ptr(rom_addr: u32) *anyopaque {
115158/// # Returns
116159///
117160/// A anyopaque pointer to the function; must be cast by the caller
118- pub inline fn _rom_func_lookup (code : Code ) * anyopaque {
119- const rom_table_lookup = @as ( * signatures .rom_table_lookup , @alignCast (@ptrCast (rom_hword_as_ptr (0x18 ) )));
120- const func_table = @as ( * u16 , @ptrCast (@alignCast (rom_hword_as_ptr (0x14 ) )));
161+ pub inline fn _rom_func_lookup (code : Code ) * const anyopaque {
162+ const rom_table_lookup : * const signatures.rom_table_lookup = @alignCast (@ptrCast (rom_hword_as_ptr (0x18 )));
163+ const func_table : [ * ] const u16 = @ptrCast (@alignCast (rom_hword_as_ptr (0x14 )));
121164 return rom_table_lookup (func_table , @intFromEnum (code ));
122165}
123166
@@ -129,7 +172,7 @@ pub inline fn _rom_func_lookup(code: Code) *anyopaque {
129172/// # Returns
130173///
131174/// A anyopaque pointer to the function; must be cast by the caller
132- pub fn rom_func_lookup (code : Code ) * anyopaque {
175+ pub fn rom_func_lookup (code : Code ) * const anyopaque {
133176 return _rom_func_lookup (code );
134177}
135178
@@ -145,10 +188,10 @@ pub fn popcount32(value: u32) u32 {
145188 }
146189
147190 const S = struct {
148- var f : ? * signatures.popcount32 = null ;
191+ var f : ? * const signatures.popcount32 = null ;
149192 };
150193
151- if (S .f == null ) S .f = @as ( * signatures . popcount32 , @ ptrCast (_rom_func_lookup (Code .popcount32 ) ));
194+ if (S .f == null ) S .f = @ptrCast (_rom_func_lookup (Code .popcount32 ));
152195 return S .f .? (value );
153196}
154197
@@ -160,10 +203,10 @@ pub fn reverse32(value: u32) u32 {
160203 }
161204
162205 const S = struct {
163- var f : ? * signatures.reverse32 = null ;
206+ var f : ? * const signatures.reverse32 = null ;
164207 };
165208
166- if (S .f == null ) S .f = @as ( * signatures . reverse32 , @ ptrCast (_rom_func_lookup (Code .reverse32 ) ));
209+ if (S .f == null ) S .f = @ptrCast (_rom_func_lookup (Code .reverse32 ));
167210 return S .f .? (value );
168211}
169212
@@ -175,10 +218,10 @@ pub fn clz32(value: u32) u32 {
175218 }
176219
177220 const S = struct {
178- var f : ? * signatures.clz32 = null ;
221+ var f : ? * const signatures.clz32 = null ;
179222 };
180223
181- if (S .f == null ) S .f = @as ( * signatures . clz32 , @ ptrCast (_rom_func_lookup (Code .clz32 ) ));
224+ if (S .f == null ) S .f = @ptrCast (_rom_func_lookup (Code .clz32 ));
182225 return S .f .? (value );
183226}
184227
@@ -190,10 +233,10 @@ pub fn ctz32(value: u32) u32 {
190233 }
191234
192235 const S = struct {
193- var f : ? * signatures.ctz32 = null ;
236+ var f : ? * const signatures.ctz32 = null ;
194237 };
195238
196- if (S .f == null ) S .f = @as ( * signatures . ctz32 , @ ptrCast (_rom_func_lookup (Code .ctz32 ) ));
239+ if (S .f == null ) S .f = @ptrCast (_rom_func_lookup (Code .ctz32 ));
197240 return S .f .? (value );
198241}
199242
@@ -209,10 +252,10 @@ pub fn memset(dest: []u8, c: u8) []u8 {
209252 }
210253
211254 const S = struct {
212- var f : ? * signatures.memset = null ;
255+ var f : ? * const signatures.memset = null ;
213256 };
214257
215- if (S .f == null ) S .f = @as ( * signatures . memset , @ ptrCast (_rom_func_lookup (Code .memset ) ));
258+ if (S .f == null ) S .f = @ptrCast (_rom_func_lookup (Code .memset ));
216259 return S .f .? (dest .ptr , c , dest .len )[0.. dest .len ];
217260}
218261
@@ -225,12 +268,12 @@ pub fn memcpy(dest: []u8, src: []const u8) []u8 {
225268 }
226269
227270 const S = struct {
228- var f : ? * signatures.memcpy = null ;
271+ var f : ? * const signatures.memcpy = null ;
229272 };
230273
231274 const n = if (dest .len <= src .len ) dest .len else src .len ;
232275
233- if (S .f == null ) S .f = @as ( * signatures . memcpy , @ alignCast (@ptrCast (_rom_func_lookup (Code .memcpy ) )));
276+ if (S .f == null ) S .f = @alignCast (@ptrCast (_rom_func_lookup (Code .memcpy )));
234277 return S .f .? (dest .ptr , src .ptr , n )[0.. n ];
235278}
236279
@@ -239,54 +282,39 @@ pub fn memcpy(dest: []u8, src: []const u8) []u8 {
239282// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
240283
241284/// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads
242- pub inline fn connect_internal_flash () * signatures.connect_internal_flash {
243- return @as (
244- * signatures .connect_internal_flash ,
245- @alignCast (@ptrCast (_rom_func_lookup (Code .connect_internal_flash ))),
246- );
285+ pub inline fn connect_internal_flash () * const signatures.connect_internal_flash {
286+ return @alignCast (@ptrCast (_rom_func_lookup (Code .connect_internal_flash )));
247287}
248288
249289/// First set up the SSI for serial-mode operations, then issue the fixed XIP exit
250290/// sequence described in Section 2.8.1.2. Note that the bootrom code uses the IO
251291/// forcing logic to drive the CS pin, which must be cleared before returning the
252292/// SSI to XIP mode (e.g. by a call to _flash_flush_cache). This function configures
253293/// the SSI with a fixed SCK clock divisor of /6.
254- pub inline fn flash_exit_xip () * signatures.flash_exit_xip {
255- return @as (
256- * signatures .flash_exit_xip ,
257- @alignCast (@ptrCast (_rom_func_lookup (Code .flash_exit_xip ))),
258- );
294+ pub inline fn flash_exit_xip () * const signatures.flash_exit_xip {
295+ return @alignCast (@ptrCast (_rom_func_lookup (Code .flash_exit_xip )));
259296}
260297
261298/// Erase a count bytes, starting at addr (offset from start of flash). Optionally,
262299/// pass a block erase command e.g. D8h block erase, and the size of the block
263300/// erased by this command — this function will use the larger block erase where
264301/// possible, for much higher erase speed. addr must be aligned to a 4096-byte sector,
265302/// and count must be a multiple of 4096 bytes.
266- pub inline fn flash_range_erase () * signatures.flash_range_erase {
267- return @as (
268- * signatures .flash_range_erase ,
269- @alignCast (@ptrCast (_rom_func_lookup (Code .flash_range_erase ))),
270- );
303+ pub inline fn flash_range_erase () * const signatures.flash_range_erase {
304+ return @alignCast (@ptrCast (_rom_func_lookup (Code .flash_range_erase )));
271305}
272306
273307/// Program data to a range of flash addresses starting at addr (offset from the
274308/// start of flash) and count bytes in size. addr must be aligned to a 256-byte
275309/// boundary, and the length of data must be a multiple of 256.
276- pub inline fn flash_range_program () * signatures.flash_range_program {
277- return @as (
278- * signatures .flash_range_program ,
279- @alignCast (@ptrCast (_rom_func_lookup (Code .flash_range_program ))),
280- );
310+ pub inline fn flash_range_program () * const signatures.flash_range_program {
311+ return @alignCast (@ptrCast (_rom_func_lookup (Code .flash_range_program )));
281312}
282313
283314/// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that
284315/// the SSI can drive the flash chip select as normal.
285- pub inline fn flash_flush_cache () * signatures.flash_flush_cache {
286- return @as (
287- * signatures .flash_flush_cache ,
288- @alignCast (@ptrCast (_rom_func_lookup (Code .flash_flush_cache ))),
289- );
316+ pub inline fn flash_flush_cache () * const signatures.flash_flush_cache {
317+ return @alignCast (@ptrCast (_rom_func_lookup (Code .flash_flush_cache )));
290318}
291319
292320/// Configure the SSI to generate a standard 03h serial read command, with 24 address
@@ -295,9 +323,31 @@ pub inline fn flash_flush_cache() *signatures.flash_flush_cache {
295323/// erase/programming operation, so that the freshly-programmed code and data is
296324/// visible to the debug host, without having to know exactly what kind of flash
297325/// device is connected.
298- pub inline fn flash_enter_cmd_xip () * signatures.flash_enter_cmd_xip {
299- return @as (
300- * signatures .flash_enter_cmd_xip ,
301- @alignCast (@ptrCast (_rom_func_lookup (Code .flash_enter_cmd_xip ))),
302- );
326+ pub inline fn flash_enter_cmd_xip () * const signatures.flash_enter_cmd_xip {
327+ return @alignCast (@ptrCast (_rom_func_lookup (Code .flash_enter_cmd_xip )));
328+ }
329+
330+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
331+ // Miscellaneous Functions (Datasheet p. 137)
332+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
333+
334+ /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:
335+ /// - gpio_activity_pin_mask is provided to enable an "activity light" via
336+ /// GPIO attached LED for the USB Mass Storage Device:
337+ /// - 0 No pins are used as per a cold boot.
338+ /// - Otherwise a single bit set indicating which GPIO pin should be set to output
339+ /// and raised whenever there is mass storage activity from the host.
340+ /// - disable_interface_mask may be used to control the exposed USB interfaces:
341+ /// - 0 To enable both interfaces (as per a cold boot)
342+ /// - 1 To disable the USB Mass Storage Interface (see Section 2.8.4)
343+ /// - 2 To disable the USB PICOBOOT Interface (see Section 2.8.5)
344+ pub inline fn reset_to_usb_boot () * const signatures.reset_to_usb_boot {
345+ return @alignCast (@ptrCast (_rom_func_lookup (Code .reset_to_usb_boot )));
346+ }
347+
348+ /// This is the method that is entered by core 1 on reset to wait to be launched by core 0.
349+ /// There are few cases where you should call this method (resetting core 1 is much better).
350+ /// This method does not return and should only ever be called on core 1.
351+ pub inline fn wait_for_vector () * const signatures.wait_for_vector {
352+ return @alignCast (@ptrCast (_rom_func_lookup (Code .wait_for_vector )));
303353}
0 commit comments