Use stack pointer to hold base address of state structure.
Keeping the base address of this structure in a register allows us to use offsets to load individual fields from it, without needing their individual addresses. However, the ldr instruction can only use immediate offsets relative to the low registers (r0-r7), or the stack pointer (r13). Low registers are in short supply and are needed for other instructions which can only use r0-r7, so we use the stack pointer here. It's safe to do this because we do not use the stack. There are no function calls, interrupt handlers or push/pop instructions in the M0 code. This change saves four cycles by eliminating loads of the addresses for the offset & tx registers, plus a further two by eliminating the need to stash one of these addresses in r8.
This commit is contained in:
@ -66,8 +66,8 @@ shadow registers.
|
|||||||
|
|
||||||
There are two key code paths, with the following worst-case timings:
|
There are two key code paths, with the following worst-case timings:
|
||||||
|
|
||||||
RX: 149 cycles
|
RX: 143 cycles
|
||||||
TX: 134 cycles
|
TX: 128 cycles
|
||||||
|
|
||||||
Design
|
Design
|
||||||
======
|
======
|
||||||
@ -101,10 +101,15 @@ registers and fixed memory addresses.
|
|||||||
|
|
||||||
// Buffer that we're funneling data to/from.
|
// Buffer that we're funneling data to/from.
|
||||||
.equ TARGET_DATA_BUFFER, 0x20008000
|
.equ TARGET_DATA_BUFFER, 0x20008000
|
||||||
.equ TARGET_BUFFER_POSITION, 0x20007000
|
|
||||||
.equ TARGET_BUFFER_TX, 0x20007004
|
|
||||||
.equ TARGET_BUFFER_MASK, 0x7fff
|
.equ TARGET_BUFFER_MASK, 0x7fff
|
||||||
|
|
||||||
|
// Base address of the state structure.
|
||||||
|
.equ STATE_BASE, 0x20007000
|
||||||
|
|
||||||
|
// Offsets into the state structure.
|
||||||
|
.equ OFFSET, 0x00
|
||||||
|
.equ TX, 0x04
|
||||||
|
|
||||||
// Our slice chain is set up as follows (ascending data age; arrows are reversed for flow):
|
// Our slice chain is set up as follows (ascending data age; arrows are reversed for flow):
|
||||||
// L -> F -> K -> C -> J -> E -> I -> A
|
// L -> F -> K -> C -> J -> E -> I -> A
|
||||||
// Which has equivalent shadow register offsets:
|
// Which has equivalent shadow register offsets:
|
||||||
@ -120,6 +125,7 @@ registers and fixed memory addresses.
|
|||||||
|
|
||||||
/* Allocations of single-use registers */
|
/* Allocations of single-use registers */
|
||||||
|
|
||||||
|
state .req r13
|
||||||
buf_base .req r12
|
buf_base .req r12
|
||||||
buf_mask .req r11
|
buf_mask .req r11
|
||||||
sgpio_data .req r7
|
sgpio_data .req r7
|
||||||
@ -140,6 +146,9 @@ main:
|
|||||||
mov buf_base, r0 // 1
|
mov buf_base, r0 // 1
|
||||||
ldr r0, =TARGET_BUFFER_MASK // 2
|
ldr r0, =TARGET_BUFFER_MASK // 2
|
||||||
mov buf_mask, r0 // 1
|
mov buf_mask, r0 // 1
|
||||||
|
ldr r0, =STATE_BASE // 2
|
||||||
|
mov state, r0 // 1
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
// The worst case timing is assumed to occur when reading the interrupt
|
// The worst case timing is assumed to occur when reading the interrupt
|
||||||
// status register *just* misses the flag being set - so we include the
|
// status register *just* misses the flag being set - so we include the
|
||||||
@ -171,15 +180,11 @@ loop:
|
|||||||
|
|
||||||
// ... and grab the address of the buffer segment we want to write to / read from.
|
// ... and grab the address of the buffer segment we want to write to / read from.
|
||||||
mov r0, buf_base // r0 = &buffer // 1
|
mov r0, buf_base // r0 = &buffer // 1
|
||||||
ldr r3, =TARGET_BUFFER_POSITION // r3 = &position_in_buffer // 2
|
ldr r2, [state, #OFFSET] // r2 = position_in_buffer // 2
|
||||||
ldr r2, [r3] // r2 = position_in_buffer // 2
|
|
||||||
add buf_ptr, r0, r2 // buf_ptr = &buffer + position_in_buffer // 1
|
add buf_ptr, r0, r2 // buf_ptr = &buffer + position_in_buffer // 1
|
||||||
|
|
||||||
mov r8, r3 // Store &position_in_buffer. // 1
|
|
||||||
|
|
||||||
// Load direction (TX or RX)
|
// Load direction (TX or RX)
|
||||||
ldr r0, =TARGET_BUFFER_TX // 2
|
ldr r0, [state, #TX] // 2
|
||||||
ldr r0, [r0] // 2
|
|
||||||
|
|
||||||
// TX?
|
// TX?
|
||||||
lsr r0, #1 // 1
|
lsr r0, #1 // 1
|
||||||
@ -221,8 +226,7 @@ done:
|
|||||||
mov r0, buf_mask // 1
|
mov r0, buf_mask // 1
|
||||||
and r0, buf_ptr, r0 // r0 = (pos_in_buffer + size_copied) % buffer_size // 1
|
and r0, buf_ptr, r0 // r0 = (pos_in_buffer + size_copied) % buffer_size // 1
|
||||||
|
|
||||||
// ... restore &position_in_buffer, and store the new position there...
|
// ... and store the new position.
|
||||||
mov r1, r8 // 1
|
str r0, [state, #OFFSET] // pos_in_buffer = (pos_in_buffer + size_copied) % buffer_size // 2
|
||||||
str r0, [r1] // pos_in_buffer = (pos_in_buffer + size_copied) % buffer_size // 2
|
|
||||||
|
|
||||||
b loop // 3
|
b loop // 3
|
||||||
|
Reference in New Issue
Block a user