From 9d570cb558773fe410228093b3ef4c2313452aa4 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 24 Dec 2021 13:40:00 +0000 Subject: [PATCH] Add macro versions of key parts of M0 code. This commit is separate from the following one which uses the macros, in order to make the diffs easier to read. --- firmware/hackrf_usb/sgpio_m0.s | 113 +++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/firmware/hackrf_usb/sgpio_m0.s b/firmware/hackrf_usb/sgpio_m0.s index 6f4613bb..7291363b 100644 --- a/firmware/hackrf_usb/sgpio_m0.s +++ b/firmware/hackrf_usb/sgpio_m0.s @@ -147,6 +147,119 @@ sgpio_int .req r6 count .req r5 buf_ptr .req r4 +/* Macros */ + +.macro reset_counts + // Reset counts. + // + // Clobbers: + zero .req r0 + + mov zero, #0 // zero = 0 // 1 + str zero, [state, #M0_COUNT] // state.m0_count = zero // 2 + str zero, [state, #M4_COUNT] // state.m4_count = zero // 2 + str zero, [state, #NUM_SHORTFALLS] // state.num_shortfalls = zero // 2 + str zero, [state, #LONGEST_SHORTFALL] // state.longest_shortfall = zero // 2 + mov shortfall_length, zero // shortfall_length = zero // 1 +.endm + +.macro await_sgpio + // Wait for, then clear, SGPIO exchange interrupt flag. + // + // Clobbers: + int_status .req r0 + scratch .req r1 + + // The worst case timing is assumed to occur when reading the interrupt + // status register *just* misses the flag being set - so we include the + // cycles required to check it a second time. + // + // We also assume that we can spend a full 10 cycles doing an ldr from + // SGPIO the first time (2 for ldr, plus 8 for SGPIO-AHB bus latency), + // and still miss a flag that was set at the start of those 10 cycles. + // + // This latter asssumption is probably slightly pessimistic, since the + // sampling of the flag on the SGPIO side must occur some time after + // the ldr instruction begins executing on the M0. However, we avoid + // relying on any assumptions about the timing details of a read over + // the SGPIO to AHB bridge. + +int_wait: + // Spin on the exchange interrupt status, shifting the slice A flag to the carry flag. + ldr int_status, [sgpio_int, #INT_STATUS] // int_status = SGPIO_STATUS_1 // 10, twice + lsr scratch, int_status, #1 // scratch = int_status >> 1 // 1, twice + bcc int_wait // if !carry: goto int_wait // 3, then 1 + + // Clear the interrupt pending bits that were set. + str int_status, [sgpio_int, #INT_CLEAR] // SGPIO_CLR_STATUS_1 = int_status // 8 +.endm + +.macro update_buf_ptr + // Update the address of the buffer segment we want to write to / read from. + ldr count, [state, #M0_COUNT] // count = state.m0_count // 2 + mov buf_ptr, buf_mask // buf_ptr = buf_mask // 1 + and buf_ptr, count // buf_ptr &= count // 1 + add buf_ptr, buf_base // buf_ptr += buf_base // 1 +.endm + +.macro update_counts + // Update counts after successful SGPIO operation. + + // Update the byte count and store the new value. + add count, #32 // count += 32 // 1 + str count, [state, #M0_COUNT] // state.m0_count = count // 2 + + // We didn't have a shortfall, so the current shortfall length is zero. + mov shortfall_length, hi_zero // shortfall_length = hi_zero // 1 +.endm + +.macro handle_shortfall + // Handle a shortfall. + // + // Clobbers: + length .req r0 + num .req r1 + longest .req r1 + limit .req r1 + + // Get current shortfall length from high register. + mov length, shortfall_length // length = shortfall_length // 1 + + // Is this a new shortfall? + cmp length, #0 // if length > 0: // 1 + bgt extend_shortfall // goto extend_shortfall // 1 thru, 3 taken + + // If so, increase the shortfall count. + ldr num, [state, #NUM_SHORTFALLS] // num = state.num_shortfalls // 2 + add num, #1 // num += 1 // 1 + str num, [state, #NUM_SHORTFALLS] // state.num_shortfalls = num // 2 + +extend_shortfall: + + // Extend the length of the current shortfall, and store back in high register. + add length, #32 // length += 32 // 1 + mov shortfall_length, length // shortfall_length = length // 1 + + // Is this now the longest shortfall? + ldr longest, [state, #LONGEST_SHORTFALL] // longest = state.longest_shortfall // 2 + cmp length, longest // if length <= longest: // 1 + blt loop // goto loop // 1 thru, 3 taken + str length, [state, #LONGEST_SHORTFALL] // state.longest_shortfall = length // 2 + + // Is this shortfall long enough to trigger a timeout? + ldr limit, [state, #SHORTFALL_LIMIT] // limit = state.shortfall_limit // 2 + cmp limit, #0 // if limit == 0: // 1 + beq loop // goto loop // 1 thru, 3 taken + cmp length, limit // if length < limit: // 1 + blt loop // goto loop // 1 thru, 3 taken + + // If so, reset mode to idle and return to idle loop. + mode .req r3 + mov mode, #MODE_IDLE // mode = MODE_IDLE // 1 + str mode, [state, #MODE] // state.mode = mode // 2 + b idle // goto idle // 3 +.endm + // Entry point. At this point, the libopencm3 startup code has set things up as // normal; .data and .bss are initialised, the stack is set up, etc. However, // we don't actually use any of that. All the code in this file would work