switch all .S files to unified asm, and use a macro to setup compiler and some other misc changes (#1295)

* switch all .S files to unified asm, and use a new macro `pico_default_asm_setup` to setup compiler to help porting to other compilers. Also some minor tweaks:
* switch some code to use more recent helper methods (e.g. busy_wait_at_least_n_cycles)
* add documentation to host divider header (I had this ages ago and never promoted)
* fixup erroneous docs about 32p32 values in all divider headers
* fix some compiler warnings
* rename recently added `unified_asm` macro to `pico_default_asm`
This commit is contained in:
Graham Sanderson 2023-03-09 17:40:20 -06:00 committed by GitHub
parent dca773f36b
commit 7f7232541a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 379 additions and 217 deletions

View File

@ -7,7 +7,11 @@
#ifndef _HARDWARE_DIVIDER_H
#define _HARDWARE_DIVIDER_H
#include "pico/types.h"
#include "pico.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef uint64_t divmod_result_t;
@ -15,108 +19,313 @@ static inline int __sign_of(int32_t v) {
return v > 0 ? 1 : (v < 0 ? -1 : 0);
}
// divides unsigned values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
static inline uint64_t hw_divider_divmod_u32(uint32_t a, uint32_t b) {
/*! \brief Do an unsigned HW divide and wait for result
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return result as a pair of 32-bit quotient/remainder values.
*
* \param a The dividend
* \param b The divisor
* \return Results of divide as a pair of 32-bit quotient/remainder values.
*/
static inline divmod_result_t hw_divider_divmod_u32(uint32_t a, uint32_t b) {
if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-1); // todo check this
return (((uint64_t)(a%b))<<32u) | (a/b);
}
// divides signed values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
static inline uint64_t hw_divider_divmod_s32(int32_t a, int32_t b) {
/*! \brief Do a signed HW divide and wait for result
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return result as a pair of 32-bit quotient/remainder values.
*
* \param a The dividend
* \param b The divisor
* \return Results of divide as a pair of 32-bit quotient/remainder values.
*/
static inline divmod_result_t hw_divider_divmod_s32(int32_t a, int32_t b) {
if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-__sign_of(a));
return (((uint64_t)(a%b))<<32u) | (uint32_t)(a/b);
}
extern __thread divmod_result_t hw_divider_result_threadlocal;
/*! \brief Start a signed asynchronous divide
* \ingroup hardware_divider
*
* Start a divide of the specified signed parameters. You should wait for 8 cycles (__div_pause()) or wait for the ready bit to be set
* (hw_divider_wait_ready()) prior to reading the results.
*
* \param a The dividend
* \param b The divisor
*/
static inline void hw_divider_divmod_s32_start(int32_t a, int32_t b) {
hw_divider_result_threadlocal = hw_divider_divmod_s32(a, b);
}
/*! \brief Start an unsigned asynchronous divide
* \ingroup hardware_divider
*
* Start a divide of the specified unsigned parameters. You should wait for 8 cycles (__div_pause()) or wait for the ready bit to be set
* (hw_divider_wait_ready()) prior to reading the results.
*
* \param a The dividend
* \param b The divisor
*/
static inline void hw_divider_divmod_u32_start(uint32_t a, uint32_t b) {
hw_divider_result_threadlocal = hw_divider_divmod_u32(a, b);
}
static inline divmod_result_t hw_divider_result_wait() {
/*! \brief Return result of last asynchronous HW divide
* \ingroup hardware_divider
*
* This function waits for the result to be ready by calling hw_divider_wait_ready().
*
* \return Current result. Most significant 32 bits are the remainder, lower 32 bits are the quotient.
*/
static inline divmod_result_t hw_divider_result_wait(void) {
return hw_divider_result_threadlocal;
}
static inline uint64_t hw_divider_result_nowait() {
/*! \brief Return result of HW divide, nowait
* \ingroup hardware_divider
*
* \note This is UNSAFE in that the calculation may not have been completed.
*
* \return Current result. Most significant 32 bits are the remainder, lower 32 bits are the quotient.
*/
static inline divmod_result_t hw_divider_result_nowait(void) {
return hw_divider_result_threadlocal;
}
inline static uint32_t to_quotient_u32(unsigned long long int r) {
/*! \brief Wait for a divide to complete
* \ingroup hardware_divider
*
* Wait for a divide to complete
*/
static inline void hw_divider_wait_ready(void) {}
/*! \brief Efficient extraction of unsigned quotient from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r A pair of 32-bit quotient/remainder values.
* \return Unsigned quotient
*/
inline static uint32_t to_quotient_u32(divmod_result_t r) {
return (uint32_t) r;
}
inline static int32_t to_quotient_s32(unsigned long long int r) {
/*! \brief Efficient extraction of signed quotient from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r A pair of 32-bit quotient/remainder values.
* \return Unsigned quotient
*/
inline static int32_t to_quotient_s32(divmod_result_t r) {
return (int32_t)(uint32_t)r;
}
inline static uint32_t to_remainder_u32(unsigned long long int r) {
/*! \brief Efficient extraction of unsigned remainder from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r A pair of 32-bit quotient/remainder values.
* \return Unsigned remainder
*
* \note On Arm this is just a 32 bit register move or a nop
*/
inline static uint32_t to_remainder_u32(divmod_result_t r) {
return (uint32_t)(r >> 32u);
}
inline static int32_t to_remainder_s32(unsigned long long int r) {
/*! \brief Efficient extraction of signed remainder from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r A pair of 32-bit quotient/remainder values.
* \return Signed remainder
*
* \note On arm this is just a 32 bit register move or a nop
*/
inline static int32_t to_remainder_s32(divmod_result_t r) {
return (int32_t)(r >> 32u);
}
static inline uint32_t hw_divider_u32_quotient_wait() {
return to_quotient_u32(hw_divider_result_wait());
}
static inline uint32_t hw_divider_u32_remainder_wait() {
return to_remainder_u32(hw_divider_result_wait());
}
static inline int32_t hw_divider_s32_quotient_wait() {
return to_quotient_s32(hw_divider_result_wait());
}
static inline int32_t hw_divider_s32_remainder_wait() {
return to_remainder_s32(hw_divider_result_wait());
}
static inline void hw_divider_pause(void) {}
/*! \brief Do an unsigned HW divide, wait for result, return quotient
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return quotient.
*
* \param a The dividend
* \param b The divisor
* \return Quotient results of the divide
*/
static inline uint32_t hw_divider_u32_quotient(uint32_t a, uint32_t b) {
return b ? (a / b) : (uint32_t)(-1);
}
/*! \brief Do an unsigned HW divide, wait for result, return remainder
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return remainder.
*
* \param a The dividend
* \param b The divisor
* \return Remainder results of the divide
*/
static inline uint32_t hw_divider_u32_remainder(uint32_t a, uint32_t b) {
return b ? (a % b) : a;
}
static inline int32_t hw_divider_s32_quotient(int32_t a, int32_t b) {
return b ? (a / b) : -__sign_of(a);
/*! \brief Do a signed HW divide, wait for result, return quotient
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return quotient.
*
* \param a The dividend
* \param b The divisor
* \return Quotient results of the divide
*/
static inline int32_t hw_divider_quotient_s32(int32_t a, int32_t b) {
return to_quotient_s32(hw_divider_divmod_s32(a, b));
}
static inline int32_t hw_divider_s32_remainder(int32_t a, int32_t b) {
/*! \brief Do a signed HW divide, wait for result, return remainder
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return remainder.
*
* \param a The dividend
* \param b The divisor
* \return Remainder results of the divide
*/
static inline int32_t hw_divider_remainder_s32(int32_t a, int32_t b) {
return b ? (a % b) : a;
}
/*! \brief Return result of last asynchronous HW divide, unsigned quotient only
* \ingroup hardware_divider
*
* This function waits for the result to be ready by calling hw_divider_wait_ready().
*
* \return Current unsigned quotient result.
*/
static inline uint32_t hw_divider_u32_quotient_wait(void) {
return to_quotient_u32(hw_divider_result_wait());
}
/*! \brief Return result of last asynchronous HW divide, signed quotient only
* \ingroup hardware_divider
*
* This function waits for the result to be ready by calling hw_divider_wait_ready().
*
* \return Current signed quotient result.
*/
static inline int32_t hw_divider_s32_quotient_wait(void) {
return to_remainder_u32(hw_divider_result_wait());
}
/*! \brief Return result of last asynchronous HW divide, unsigned remainder only
* \ingroup hardware_divider
*
* This function waits for the result to be ready by calling hw_divider_wait_ready().
*
* \return Current unsigned remainder result.
*/
static inline uint32_t hw_divider_u32_remainder_wait(void) {
return to_quotient_s32(hw_divider_result_wait());
}
/*! \brief Return result of last asynchronous HW divide, signed remainder only
* \ingroup hardware_divider
*
* This function waits for the result to be ready by calling hw_divider_wait_ready().
*
* \return Current remainder results.
*/
static inline int32_t hw_divider_s32_remainder_wait(void) {
return to_remainder_s32(hw_divider_result_wait());
}
/*! \brief Do a hardware unsigned HW divide, wait for result, return quotient
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return quotient.
*
* \param a The dividend
* \param b The divisor
* \return Quotient result of the divide
*/
static inline uint32_t hw_divider_u32_quotient_inlined(uint32_t a, uint32_t b) {
return hw_divider_u32_quotient(a,b);
}
/*! \brief Do a hardware unsigned HW divide, wait for result, return remainder
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return remainder.
*
* \param a The dividend
* \param b The divisor
* \return Remainder result of the divide
*/
static inline uint32_t hw_divider_u32_remainder_inlined(uint32_t a, uint32_t b) {
return hw_divider_u32_remainder(a,b);
}
/*! \brief Do a hardware signed HW divide, wait for result, return quotient
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return quotient.
*
* \param a The dividend
* \param b The divisor
* \return Quotient result of the divide
*/
static inline int32_t hw_divider_s32_quotient_inlined(int32_t a, int32_t b) {
return hw_divider_s32_quotient(a,b);
return hw_divider_quotient_s32(a,b);
}
/*! \brief Do a hardware signed HW divide, wait for result, return remainder
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return remainder.
*
* \param a The dividend
* \param b The divisor
* \return Remainder result of the divide
*/
static inline int32_t hw_divider_s32_remainder_inlined(int32_t a, int32_t b) {
return hw_divider_s32_remainder(a,b);
return hw_divider_remainder_s32(a,b);
}
typedef uint64_t hw_divider_state_t;
static inline void hw_divider_save_state(hw_divider_state_t *dest) {
/*! \brief Save the calling cores hardware divider state
* \ingroup hardware_divider
*
* Copy the current core's hardware divider state into the provided structure. This method
* waits for the divider results to be stable, then copies them to memory.
* They can be restored via hw_divider_restore_state()
*
* \param dest the location to store the divider state
*/
static inline void hw_divider_save_state(hw_divider_state_t *dest) {
*dest = hw_divider_result_threadlocal;
}
static inline void hw_divider_restore_state(hw_divider_state_t *src) {
/*! \brief Load a saved hardware divider state into the current core's hardware divider
* \ingroup hardware_divider
*
* Copy the passed hardware divider state into the hardware divider.
*
* \param src the location to load the divider state from
*/
static inline void hw_divider_restore_state(hw_divider_state_t *src) {
hw_divider_result_threadlocal = *src;
}
#ifdef __cplusplus
}
#endif
#endif // _HARDWARE_DIVIDER_H

View File

@ -8,7 +8,7 @@
// These functions save/restore divider state, so are safe to call from interrupts
int32_t div_s32s32(int32_t a, int32_t b) {
return hw_divider_s32_quotient(a, b);
return hw_divider_quotient_s32(a, b);
}
divmod_result_t divmod_s32s32(int32_t a, int32_t b) {

View File

@ -86,9 +86,7 @@
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
.section .text
@ -97,10 +95,7 @@
// Otherwise it will be a return address -- second stage being called as a
// function by user code, after copying out of XIP region. r3 holds SSI base,
// r0...2 used as temporaries. Other GPRs not used.
.global _stage2_boot
.type _stage2_boot,%function
.thumb_func
_stage2_boot:
regular_func _stage2_boot
push {lr}
// Set pad configuration:

View File

@ -20,6 +20,8 @@
#include "hardware/regs/addressmap.h"
#include "hardware/regs/ssi.h"
pico_default_asm_setup
// ----------------------------------------------------------------------------
// Config section
// ----------------------------------------------------------------------------
@ -53,25 +55,19 @@
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------
.cpu cortex-m0
.thumb
.section .text
.global _stage2_boot
.type _stage2_boot,%function
.thumb_func
_stage2_boot:
regular_func _stage2_boot
push {lr}
ldr r3, =XIP_SSI_BASE // Use as base address where possible
// Disable SSI to allow further config
mov r1, #0
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET]
// Set baud rate
mov r1, #PICO_FLASH_SPI_CLKDIV
movs r1, #PICO_FLASH_SPI_CLKDIV
str r1, [r3, #SSI_BAUDR_OFFSET]
ldr r1, =(CTRLR0_XIP)
@ -82,11 +78,11 @@ _stage2_boot:
str r1, [r0]
// NDF=0 (single 32b read)
mov r1, #0x0
movs r1, #0x0
str r1, [r3, #SSI_CTRLR1_OFFSET]
// Re-enable SSI
mov r1, #1
movs r1, #1
str r1, [r3, #SSI_SSIENR_OFFSET]
// We are now in XIP mode. Any bus accesses to the XIP address window will be

View File

@ -80,25 +80,20 @@
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------
.cpu cortex-m0
.thumb
pico_default_asm_setup
.section .text
.global _stage2_boot
.type _stage2_boot,%function
.thumb_func
_stage2_boot:
.section text
regular_func _stage2_boot
push {lr}
ldr r3, =XIP_SSI_BASE // Use as base address where possible
// Disable SSI to allow further config
mov r1, #0
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET]
// Set baud rate
mov r1, #PICO_FLASH_SPI_CLKDIV
movs r1, #PICO_FLASH_SPI_CLKDIV
str r1, [r3, #SSI_BAUDR_OFFSET]
// On QSPI parts we usually need a 01h SR-write command to enable QSPI mode
@ -113,7 +108,7 @@ program_sregs:
str r1, [r3, #SSI_CTRLR0_OFFSET]
// Enable SSI and select slave 0
mov r1, #1
movs r1, #1
str r1, [r3, #SSI_SSIENR_OFFSET]
// Check whether SR needs updating
@ -124,7 +119,7 @@ program_sregs:
beq skip_sreg_programming
// Send write enable command
mov r1, #CMD_WRITE_ENABLE
movs r1, #CMD_WRITE_ENABLE
str r1, [r3, #SSI_DR0_OFFSET]
// Poll for completion and discard RX
@ -132,9 +127,9 @@ program_sregs:
ldr r1, [r3, #SSI_DR0_OFFSET]
// Send status write command followed by data bytes
mov r1, #CMD_WRITE_STATUS
movs r1, #CMD_WRITE_STATUS
str r1, [r3, #SSI_DR0_OFFSET]
mov r0, #0
movs r0, #0
str r2, [r3, #SSI_DR0_OFFSET]
bl wait_ssi_ready
@ -145,7 +140,7 @@ program_sregs:
1:
ldr r0, =CMD_READ_STATUS
bl read_flash_sreg
mov r1, #1
movs r1, #1
tst r0, r1
bne 1b
@ -157,7 +152,7 @@ skip_sreg_programming:
// bl wait_ssi_ready
// Disable SSI again so that it can be reconfigured
mov r1, #0
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET]
#endif
@ -182,7 +177,7 @@ dummy_read:
ldr r1, =(CTRLR0_ENTER_XIP)
str r1, [r3, #SSI_CTRLR0_OFFSET]
mov r1, #0x0 // NDF=0 (single 32b read)
movs r1, #0x0 // NDF=0 (single 32b read)
str r1, [r3, #SSI_CTRLR1_OFFSET]
#define SPI_CTRLR0_ENTER_XIP \
@ -197,12 +192,12 @@ dummy_read:
ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET) // SPI_CTRL0 Register
str r1, [r0]
mov r1, #1 // Re-enable SSI
movs r1, #1 // Re-enable SSI
str r1, [r3, #SSI_SSIENR_OFFSET]
mov r1, #CMD_READ
movs r1, #CMD_READ
str r1, [r3, #SSI_DR0_OFFSET] // Push SPI command into TX FIFO
mov r1, #MODE_CONTINUOUS_READ // 32-bit: 24 address bits (we don't care, so 0) and M[7:4]=1010
movs r1, #MODE_CONTINUOUS_READ // 32-bit: 24 address bits (we don't care, so 0) and M[7:4]=1010
str r1, [r3, #SSI_DR0_OFFSET] // Push Address into TX FIFO - this will trigger the transaction
// Poll for completion
@ -218,7 +213,7 @@ dummy_read:
// to APM mode and generate a 28-bit address phase with the extra nibble set
// to 4'b0000).
mov r1, #0
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI (and clear FIFO) to allow further config
// Note that the INST_L field is used to select what XIP data gets pushed into
@ -240,7 +235,7 @@ configure_ssi:
ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET)
str r1, [r0]
mov r1, #1
movs r1, #1
str r1, [r3, #SSI_SSIENR_OFFSET] // Re-enable SSI
// We are now in XIP mode, with all transactions using Dual I/O and only

View File

@ -4,6 +4,8 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/asm_helper.S"
// Stub second stage which calls into USB bootcode, with parameters.
// USB boot takes two parameters:
// - A GPIO mask for activity LED -- if mask is 0, don't touch GPIOs at all
@ -19,17 +21,12 @@
#define ACTIVITY_LED 0
#define BOOT_MODE USB_BOOT_MSD_AND_PICOBOOT
.cpu cortex-m0
.thumb
pico_default_asm_setup
.section .text
.global _stage2_boot
.type _stage2_boot,%function
.thumb_func
_stage2_boot:
mov r7, #0x14 // Pointer to _well_known pointer table in ROM
regular_func _stage2_boot
movs r7, #0x14 // Pointer to _well_known pointer table in ROM
ldrh r0, [r7, #0] // Offset 0 is 16 bit pointer to function table
ldrh r7, [r7, #4] // Offset 4 is 16 bit pointer to table lookup routine
ldr r1, =('U' | ('B' << 8)) // Symbol for USB Boot
@ -39,7 +36,7 @@ _stage2_boot:
mov r7, r0
ldr r0, =(1u << ACTIVITY_LED) // Mask of which GPIO (or GPIOs) to use
mov r1, #BOOT_MODE
movs r1, #BOOT_MODE
blx r7
dead:

View File

@ -86,9 +86,7 @@
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
.section .text
@ -97,10 +95,7 @@
// Otherwise it will be a return address -- second stage being called as a
// function by user code, after copying out of XIP region. r3 holds SSI base,
// r0...2 used as temporaries. Other GPRs not used.
.global _stage2_boot
.type _stage2_boot,%function
.thumb_func
_stage2_boot:
regular_func _stage2_boot
push {lr}
// Set pad configuration:

View File

@ -40,6 +40,8 @@
#define PICO_FLASH_SPI_CLKDIV 4
#endif
pico_default_asm_setup
// ----------------------------------------------------------------------------
// The "System Control Block" is a set of internal Cortex-M0+ control registers
// that are memory mapped and accessed like any other H/W register. They have
@ -69,31 +71,25 @@
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------
.cpu cortex-m0
.thumb
.org 0
.section .text
// This code will get copied to 0x20000000 and then executed
.global _stage2_boot
.type _stage2_boot,%function
.thumb_func
_stage2_boot:
regular_func _stage2_boot
push {lr}
ldr r3, =XIP_SSI_BASE // Use as base address where possible
// We are primarily interested in setting up Flash for DSPI XIP w/ continuous read
mov r1, #0
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI to allow further config
// The Boot ROM sets a very conservative SPI clock frequency to be sure it can
// read the initial 256 bytes from any device. Here we can be more aggressive.
mov r1, #PICO_FLASH_SPI_CLKDIV
movs r1, #PICO_FLASH_SPI_CLKDIV
str r1, [r3, #SSI_BAUDR_OFFSET] // Set SSI Clock
// First we need to send the initial command to get us in to Fast Read Dual I/O
@ -116,7 +112,7 @@ _stage2_boot:
ldr r1, =(CTRLR0_ENTER_XIP)
str r1, [r3, #SSI_CTRLR0_OFFSET]
mov r1, #0x0 // NDF=0 (single 32b read)
movs r1, #0x0 // NDF=0 (single 32b read)
str r1, [r3, #SSI_CTRLR1_OFFSET]
#define SPI_CTRLR0_ENTER_XIP \
@ -131,18 +127,18 @@ _stage2_boot:
ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET) // SPI_CTRL0 Register
str r1, [r0]
mov r1, #1 // Re-enable SSI
movs r1, #1 // Re-enable SSI
str r1, [r3, #SSI_SSIENR_OFFSET]
mov r1, #W25X10CL_CMD_READ_DATA_FAST_DUAL_IO // 8b command = 0xBB
movs r1, #W25X10CL_CMD_READ_DATA_FAST_DUAL_IO // 8b command = 0xBB
str r1, [r3, #SSI_DR0_OFFSET] // Push SPI command into TX FIFO
mov r1, #0x0000002 // 28-bit Address for dummy read = 0x000000 + 0x2 Mode bits to set M[5:4]=10
movs r1, #0x0000002 // 28-bit Address for dummy read = 0x000000 + 0x2 Mode bits to set M[5:4]=10
str r1, [r3, #SSI_DR0_OFFSET] // Push Address into TX FIFO - this will trigger the transaction
// Now we wait for the read transaction to complete by monitoring the SSI
// status register and checking for the "RX FIFO Not Empty" flag to assert.
mov r1, #SSI_SR_RFNE_BITS
movs r1, #SSI_SR_RFNE_BITS
00:
ldr r0, [r3, #SSI_SR_OFFSET] // Read status register
tst r0, r1 // RFNE status flag set?
@ -158,7 +154,7 @@ _stage2_boot:
// to APM mode and generate a 28-bit address phase with the extra nibble set
// to 4'b0000).
mov r1, #0
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI (and clear FIFO) to allow further config
// Note that the INST_L field is used to select what XIP data gets pushed into
@ -180,7 +176,7 @@ _stage2_boot:
ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET)
str r1, [r0]
mov r1, #1
movs r1, #1
str r1, [r3, #SSI_SSIENR_OFFSET] // Re-enable SSI
// We are now in XIP mode, with all transactions using Dual I/O and only

View File

@ -48,7 +48,7 @@ bool clock_configure(enum clock_index clk_index, uint32_t src, uint32_t auxsrc,
return false;
// Div register is 24.8 int.frac divider so multiply by 2^8 (left shift by 8)
div = (uint32_t) (((uint64_t) src_freq << 8) / freq);
div = (uint32_t) (((uint64_t) src_freq << CLOCKS_CLK_GPOUT0_DIV_INT_LSB) / freq);
clock_hw_t *clock = &clocks_hw->clk[clk_index];
@ -76,14 +76,9 @@ bool clock_configure(enum clock_index clk_index, uint32_t src, uint32_t auxsrc,
if (configured_freq[clk_index] > 0) {
// Delay for 3 cycles of the target clock, for ENABLE propagation.
// Note XOSC_COUNT is not helpful here because XOSC is not
// necessarily running, nor is timer... so, 3 cycles per loop:
// necessarily running, nor is timer...:
uint delay_cyc = configured_freq[clk_sys] / configured_freq[clk_index] + 1;
unified_asm (
"1: \n\t"
"subs %0, #1 \n\t"
"bne 1b"
: "+r" (delay_cyc)
);
busy_wait_at_least_cycles(delay_cyc * 3);
}
}

View File

@ -2,9 +2,7 @@
#include "hardware/regs/addressmap.h"
#include "hardware/regs/sio.h"
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
// tag::hw_div_s32[]
regular_func_with_section hw_divider_divmod_s32

View File

@ -85,7 +85,7 @@ static inline void hw_divider_wait_ready(void) {
// we use one less register and instruction than gcc which uses a TST instruction
uint32_t tmp; // allow compiler to pick scratch register
unified_asm (
pico_default_asm (
"hw_divider_result_loop_%=:"
"ldr %0, [%1, %2]\n\t"
"lsrs %0, %0, #1\n\t"
@ -177,29 +177,29 @@ static inline int32_t hw_divider_s32_remainder_wait(void) {
/*! \brief Do a signed HW divide and wait for result
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return result as a fixed point 32p32 value.
* Divide \p a by \p b, wait for calculation to complete, return result as a pair of 32-bit quotient/remainder values.
*
* \param a The dividend
* \param b The divisor
* \return Results of divide as a 32p32 fixed point value.
* \return Results of divide as a pair of 32-bit quotient/remainder values.
*/
divmod_result_t hw_divider_divmod_s32(int32_t a, int32_t b);
/*! \brief Do an unsigned HW divide and wait for result
* \ingroup hardware_divider
*
* Divide \p a by \p b, wait for calculation to complete, return result as a fixed point 32p32 value.
* Divide \p a by \p b, wait for calculation to complete, return result as a pair of 32-bit quotient/remainder values.
*
* \param a The dividend
* \param b The divisor
* \return Results of divide as a 32p32 fixed point value.
* \return Results of divide as a pair of 32-bit quotient/remainder values.
*/
divmod_result_t hw_divider_divmod_u32(uint32_t a, uint32_t b);
/*! \brief Efficient extraction of unsigned quotient from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r 32p32 fixed point value.
* \param r A pair of 32-bit quotient/remainder values.
* \return Unsigned quotient
*/
inline static uint32_t to_quotient_u32(divmod_result_t r) {
@ -209,7 +209,7 @@ inline static uint32_t to_quotient_u32(divmod_result_t r) {
/*! \brief Efficient extraction of signed quotient from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r 32p32 fixed point value.
* \param r A pair of 32-bit quotient/remainder values.
* \return Unsigned quotient
*/
inline static int32_t to_quotient_s32(divmod_result_t r) {
@ -219,7 +219,7 @@ inline static int32_t to_quotient_s32(divmod_result_t r) {
/*! \brief Efficient extraction of unsigned remainder from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r 32p32 fixed point value.
* \param r A pair of 32-bit quotient/remainder values.
* \return Unsigned remainder
*
* \note On Arm this is just a 32 bit register move or a nop
@ -231,7 +231,7 @@ inline static uint32_t to_remainder_u32(divmod_result_t r) {
/*! \brief Efficient extraction of signed remainder from 32p32 fixed point
* \ingroup hardware_divider
*
* \param r 32p32 fixed point value.
* \param r A pair of 32-bit quotient/remainder values.
* \return Signed remainder
*
* \note On arm this is just a 32 bit register move or a nop
@ -296,7 +296,7 @@ static inline int32_t hw_divider_remainder_s32(int32_t a, int32_t b) {
* \ingroup hardware_divider
*/
static inline void hw_divider_pause(void) {
unified_asm (
pico_default_asm (
"b _1_%=\n"
"_1_%=:\n"
"b _2_%=\n"

View File

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
// Note this file is always included by another, so does not do pico_default_asm_setup
#include "hardware/regs/addressmap.h"
#include "hardware/regs/sio.h"

View File

@ -5,7 +5,7 @@
*/
#include "hardware/exception.h"
#include "hardware/regs/m0plus.h"
#include "hardware/platform_defs.h"
#include "hardware/structs/scb.h"
#include "hardware/sync.h"
#include "pico/assert.h"

View File

@ -9,7 +9,6 @@
#include "pico.h"
#include "hardware/address_mapped.h"
#include "hardware/regs/m0plus.h"
/** \file exception.h
* \defgroup hardware_exception hardware_exception

View File

@ -244,8 +244,8 @@ void gpio_set_input_enabled(uint gpio, bool enabled) {
}
void gpio_init(uint gpio) {
sio_hw->gpio_oe_clr = 1ul << gpio;
sio_hw->gpio_clr = 1ul << gpio;
gpio_set_dir(gpio, GPIO_IN);
gpio_put(gpio, 0);
gpio_set_function(gpio, GPIO_FUNC_SIO);
}

View File

@ -186,7 +186,7 @@ static inline int8_t slot_diff(struct irq_handler_chain_slot *to, struct irq_han
int32_t result = 0xaaaa;
// return (to - from);
// note this implementation has limited range, but is fine for plenty more than -128->127 result
unified_asm (
pico_default_asm (
"subs %1, %2\n"
"adcs %1, %1\n" // * 2 (and + 1 if negative for rounding)
"muls %0, %1\n"

View File

@ -6,11 +6,11 @@
#include "pico.h"
#include "hardware/irq.h"
#include "pico/asm_helper.S"
pico_default_asm_setup
#if !PICO_DISABLE_SHARED_IRQ_HANDLERS
.syntax unified
.cpu cortex-m0plus
.thumb
.data
.align 2

View File

@ -116,7 +116,7 @@ typedef volatile uint32_t spin_lock_t;
*/
#if !__has_builtin(__sev)
__force_inline static void __sev(void) {
unified_asm ("sev");
pico_default_asm ("sev");
}
#endif
@ -128,7 +128,7 @@ __force_inline static void __sev(void) {
*/
#if !__has_builtin(__wfe)
__force_inline static void __wfe(void) {
unified_asm ("wfe");
pico_default_asm ("wfe");
}
#endif
@ -139,7 +139,7 @@ __force_inline static void __wfe(void) {
*/
#if !__has_builtin(__wfi)
__force_inline static void __wfi(void) {
unified_asm ("wfi");
pico_default_asm ("wfi");
}
#endif
@ -150,7 +150,7 @@ __force_inline static void __wfi(void) {
* instruction will be observed before any explicit access after the instruction.
*/
__force_inline static void __dmb(void) {
unified_asm ("dmb" : : : "memory");
pico_default_asm ("dmb" : : : "memory");
}
/*! \brief Insert a DSB instruction in to the code path.
@ -161,7 +161,7 @@ __force_inline static void __dmb(void) {
* accesses before this instruction complete.
*/
__force_inline static void __dsb(void) {
unified_asm ("dsb" : : : "memory");
pico_default_asm ("dsb" : : : "memory");
}
/*! \brief Insert a ISB instruction in to the code path.
@ -172,7 +172,7 @@ __force_inline static void __dsb(void) {
* the ISB instruction has been completed.
*/
__force_inline static void __isb(void) {
unified_asm ("isb");
pico_default_asm ("isb");
}
/*! \brief Acquire a memory fence
@ -213,8 +213,8 @@ __force_inline static void __mem_fence_release(void) {
*/
__force_inline static uint32_t save_and_disable_interrupts(void) {
uint32_t status;
unified_asm ("mrs %0, PRIMASK" : "=r" (status)::);
unified_asm ("cpsid i");
pico_default_asm ("mrs %0, PRIMASK" : "=r" (status)::);
pico_default_asm ("cpsid i");
return status;
}
@ -224,7 +224,7 @@ __force_inline static uint32_t save_and_disable_interrupts(void) {
* \param status Previous interrupt status from save_and_disable_interrupts()
*/
__force_inline static void restore_interrupts(uint32_t status) {
unified_asm ("msr PRIMASK,%0"::"r" (status) : );
pico_default_asm ("msr PRIMASK,%0"::"r" (status) : );
}
/*! \brief Get HW Spinlock instance from number

View File

@ -4,14 +4,10 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
.syntax unified
.cpu cortex-m0plus
.thumb
#include "pico/asm_helper.S"
#include "pico/bootrom.h"
__pre_init __aeabi_bits_init, 00010
pico_default_asm_setup
.macro bits_section name
#if PICO_BITS_IN_RAM
@ -21,6 +17,8 @@ __pre_init __aeabi_bits_init, 00010
#endif
.endm
__pre_init __aeabi_bits_init, 00010
.section .data.aeabi_bits_funcs
.global aeabi_bits_funcs, aeabi_bits_funcs_end
.equ BITS_FUNC_COUNT, 4

View File

@ -63,7 +63,7 @@ static uint32_t counter = 0;
//#define SWAP32(A) ((((A) & 0xff000000U) >> 8) | (((A) & 0xff0000U) << 8) | (((A) & 0xff00U) >> 8) | (((A) & 0xffU) << 8))
__force_inline static uint32_t __swap16x2(uint32_t a) {
unified_asm ("rev16 %0, %0" : "+l" (a) : : );
pico_default_asm ("rev16 %0, %0" : "+l" (a) : : );
return a;
}
#define SWAP32(a) __swap16x2(a)

View File

@ -4,17 +4,12 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/asm_helper.S"
#include "hardware/regs/addressmap.h"
#include "hardware/divider_helper.S"
.syntax unified
.cpu cortex-m0plus
.thumb
// PICO_CONFIG: PICO_DIVIDER_DISABLE_INTERRUPTS, Disable interrupts around division such that divider state need not be saved/restored in exception handlers, default=0, group=pico_divider
#include "pico/asm_helper.S"
// PICO_CONFIG: PICO_DIVIDER_CALL_IDIV0, Whether 32 bit division by zero should call __aeabi_idiv0, default=1, group=pico_divider
#ifndef PICO_DIVIDER_CALL_IDIV0
#define PICO_DIVIDER_CALL_IDIV0 1
@ -25,6 +20,8 @@
#define PICO_DIVIDER_CALL_LDIV0 1
#endif
pico_default_asm_setup
// PICO_CONFIG: PICO_DIVIDER_IN_RAM, Whether divider functions should be placed in RAM, default=0, group=pico_divider
.macro div_section name
#if PICO_DIVIDER_IN_RAM
@ -37,11 +34,11 @@
@ wait 8-n cycles for the hardware divider
.macro wait_div n
.rept (8-\n) / 2
b 9f
b 9f
9:
.endr
.if (8-\n) % 2
nop
nop
.endif
.endm

View File

@ -8,11 +8,9 @@
#include "pico/bootrom/sf_table.h"
#include "hardware/divider_helper.S"
__pre_init __aeabi_double_init, 00020
pico_default_asm_setup
.syntax unified
.cpu cortex-m0plus
.thumb
__pre_init __aeabi_double_init, 00020
.macro double_section name
#if PICO_DOUBLE_IN_RAM

View File

@ -7,9 +7,7 @@
#include "pico/asm_helper.S"
#include "pico/bootrom/sf_table.h"
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
wrapper_func __aeabi_dadd
wrapper_func __aeabi_ddiv

View File

@ -17,9 +17,7 @@
#include "pico/asm_helper.S"
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
.macro double_section name
// todo separate flag for shims?

View File

@ -10,9 +10,7 @@
__pre_init __aeabi_float_init, 00020
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
.macro float_section name
#if PICO_FLOAT_IN_RAM

View File

@ -7,9 +7,7 @@
#include "pico/asm_helper.S"
#include "pico/bootrom/sf_table.h"
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
wrapper_func __aeabi_fadd
wrapper_func __aeabi_fdiv

View File

@ -7,14 +7,13 @@
#include "pico/asm_helper.S"
#if PICO_FLOAT_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
.syntax unified
.cpu cortex-m0plus
.thumb
#ifndef PICO_FLOAT_IN_RAM
#define PICO_FLOAT_IN_RAM 0
#endif
pico_default_asm_setup
.macro float_section name
// todo separate flag for shims?
#if PICO_FLOAT_IN_RAM

View File

@ -4,12 +4,10 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
.syntax unified
.cpu cortex-m0plus
.thumb
#include "pico/asm_helper.S"
pico_default_asm_setup
.macro int64_section name
#if PICO_INT64_OPS_IN_RAM
.section RAM_SECTION_NAME(\name), "ax"

View File

@ -4,13 +4,11 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
.syntax unified
.cpu cortex-m0plus
.thumb
#include "pico/asm_helper.S"
#include "pico/bootrom.h"
pico_default_asm_setup
__pre_init __aeabi_mem_init, 00001
.macro mem_section name

View File

@ -75,7 +75,7 @@ bool multicore_fifo_pop_timeout_us(uint64_t timeout_us, uint32_t *out) {
static uint32_t __attribute__((section(".stack1"))) core1_stack[PICO_CORE1_STACK_SIZE / sizeof(uint32_t)];
static void __attribute__ ((naked)) core1_trampoline(void) {
unified_asm ("pop {r0, r1, pc}");
pico_default_asm ("pop {r0, r1, pc}");
}
int core1_wrapper(int (*entry)(void), void *stack_base) {

View File

@ -6,6 +6,15 @@
#include "pico.h"
# note we don't do this by default in this file for backwards comaptibility with user code
# that may include this file, but not use unified syntax. Note that this macro does equivalent
# setup to the pico_default_asm macro for inline assembly in C code.
.macro pico_default_asm_setup
.syntax unified
.cpu cortex-m0plus
.thumb
.endm
// do not put align in here as it is used mid function sometimes
.macro regular_func x
.global \x

View File

@ -335,13 +335,13 @@ extern "C" {
#define MIN(a, b) ((b)>(a)?(a):(b))
#endif
#define unified_asm(...) __asm volatile (".syntax unified\n" __VA_ARGS__)
#define pico_default_asm(...) __asm volatile (".syntax unified\n" __VA_ARGS__)
/*! \brief Execute a breakpoint instruction
* \ingroup pico_platform
*/
static inline void __breakpoint(void) {
unified_asm ("bkpt #0");
pico_default_asm ("bkpt #0");
}
/*! \brief Ensure that the compiler does not move memory access across this method call
@ -357,7 +357,7 @@ static inline void __breakpoint(void) {
* might - even above the memory store!)
*/
__force_inline static void __compiler_memory_barrier(void) {
unified_asm ("" : : : "memory");
pico_default_asm ("" : : : "memory");
}
/*! \brief Macro for converting memory addresses to 32 bit addresses suitable for DMA
@ -443,7 +443,7 @@ static __force_inline void tight_loop_contents(void) {}
* \return a * b
*/
__force_inline static int32_t __mul_instruction(int32_t a, int32_t b) {
unified_asm ("muls %0, %1" : "+l" (a) : "l" (b) : );
pico_default_asm ("muls %0, %1" : "+l" (a) : "l" (b) : );
return a;
}
@ -477,19 +477,15 @@ __force_inline static int32_t __mul_instruction(int32_t a, int32_t b) {
*
* \return the exception number if the CPU is handling an exception, or 0 otherwise
*/
static inline uint __get_current_exception(void) {
static __force_inline uint __get_current_exception(void) {
uint exception;
unified_asm ("mrs %0, ipsr" : "=l" (exception));
pico_default_asm ("mrs %0, ipsr" : "=l" (exception));
return exception;
}
#define WRAPPER_FUNC(x) __wrap_ ## x
#define REAL_FUNC(x) __real_ ## x
#ifdef __cplusplus
}
#endif
/*! \brief Helper method to busy-wait for at least the given number of cycles
* \ingroup pico_platform
*
@ -505,7 +501,7 @@ static inline uint __get_current_exception(void) {
* \param minimum_cycles the minimum number of system clock cycles to delay for
*/
static inline void busy_wait_at_least_cycles(uint32_t minimum_cycles) {
unified_asm (
pico_default_asm (
"1: subs %0, #3\n"
"bcs 1b\n"
: "+r" (minimum_cycles) : : "memory"
@ -521,6 +517,10 @@ __force_inline static uint get_core_num(void) {
return (*(uint32_t *) (SIO_BASE + SIO_CPUID_OFFSET));
}
#ifdef __cplusplus
}
#endif
#else // __ASSEMBLER__
#define WRAPPER_FUNC_NAME(x) __wrap_##x

View File

@ -7,9 +7,7 @@
#include "pico/asm_helper.S"
#include "pico/bootrom/sf_table.h"
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
wrapper_func sprintf
wrapper_func snprintf

View File

@ -151,7 +151,7 @@ void runtime_init(void) {
#ifndef NDEBUG
if (__get_current_exception()) {
// crap; started in exception handler
unified_asm ("bkpt #0");
__breakpoint();
}
#endif
@ -286,7 +286,7 @@ extern void __attribute__((noreturn)) __printflike(1, 0) PICO_PANIC_FUNCTION(__u
// Use a forwarding method here as it is a little simpler than renaming the symbol as it is used from assembler
void __attribute__((naked, noreturn)) __printflike(1, 0) panic(__unused const char *fmt, ...) {
// if you get an undefined reference here, you didn't define your PICO_PANIC_FUNCTION!
unified_asm (
pico_default_asm (
"push {lr}\n"
#if !PICO_PANIC_FUNCTION_EMPTY
"bl " __XSTRING(PICO_PANIC_FUNCTION) "\n"

View File

@ -5,10 +5,11 @@
*/
#include "pico.h"
#include "pico/asm_helper.S"
#include "hardware/regs/m0plus.h"
#include "hardware/regs/addressmap.h"
#include "hardware/regs/sio.h"
#include "pico/asm_helper.S"
#include "pico/binary_info/defs.h"
#ifdef NDEBUG
@ -17,9 +18,7 @@
#endif
#endif
.syntax unified
.cpu cortex-m0plus
.thumb
pico_default_asm_setup
.section .vectors, "ax"
.align 2

View File

@ -25,7 +25,7 @@ static void stdio_semihosting_out_chars(const char *buf, int length) {
args.buf = buf;
args.len = length;
unified_asm (
pico_default_asm (
// r1 must contain a pointer to the arguments
"movs r1, %[args]\n"
// semihosting call number 0x05 = SYS_WRITE

View File

@ -1,9 +1,9 @@
# PICO_CMAKE_CONFIG: PICO_STDIO_UART, OPTION: Globally enable stdio UART, default=1, group=pico_stdlib
option(PICO_STDIO_UART "Globablly enable stdio UART" 1)
option(PICO_STDIO_UART "Globally enable stdio UART" 1)
# PICO_CMAKE_CONFIG: PICO_STDIO_USB, OPTION: Globally enable stdio USB, default=0, group=pico_stdlib
option(PICO_STDIO_USB "Globablly enable stdio USB" 0)
option(PICO_STDIO_USB "Globally enable stdio USB" 0)
# PICO_CMAKE_CONFIG: PICO_STDIO_SEMIHOSTING, OPTION: Globally enable stdio semihosting, default=0, group=pico_stdlib
option(PICO_STDIO_SEMIHOSTING "Globablly enable stdio semihosting" 0)
option(PICO_STDIO_SEMIHOSTING "Globally enable stdio semi-hosting" 0)
if (NOT TARGET pico_stdlib)
pico_add_impl_library(pico_stdlib)

View File

@ -138,6 +138,6 @@ int main(void) {
hard_assert(recursive_mutex_try_enter(&recursive_mutex, NULL));
hard_assert(recursive_mutex_try_enter(&recursive_mutex, NULL));
// this should compile as we are Cortex M0+
unified_asm ("SVC #3");
pico_default_asm ("SVC #3");
}

View File

@ -193,7 +193,7 @@ void test_random() {
#endif
uint32_t __attribute__((naked)) time_32(uint32_t a, uint32_t b, uint32_t (*func)(uint32_t a, uint32_t b)) {
unified_asm (
pico_default_asm (
"push {r4, r5, lr}\n"
"ldr r4, =0xe000e018\n"
"ldr r5, [r4]\n"
@ -207,7 +207,7 @@ uint32_t __attribute__((naked)) time_32(uint32_t a, uint32_t b, uint32_t (*func)
}
uint32_t __attribute__((naked)) time_64(uint64_t a, uint64_t b, uint64_t (*func64)(uint64_t a, uint64_t b)) {
unified_asm (
pico_default_asm (
"push {r4-r6, lr}\n"
"ldr r6, [sp, #16]\n"
"ldr r4, =0xe000e018\n"

View File

@ -12,9 +12,9 @@
//
//===-
.syntax unified
.cpu cortex-m0plus
.thumb
#include "pico/asm_helper.S"
pico_default_asm_setup
.align 2

View File

@ -518,7 +518,7 @@ int main() {
}
for(float x = 4294967296.f * 4294967296.f * 2.f; x>=0.5f; x/=2.f) {
printf("f2i64 %f->%lld\n", x, (int64_t)x);
if (x >= INT64_MAX) {
if ((double)x >= (double)INT64_MAX) {
// seems like there is a bug in the gcc version!
assert(__aeabi_f2lz(x) == INT64_MAX);
} else {