From c0d0cd2a1d0a667c9d65cf1c0555bdfda2788a47 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 22 Dec 2021 04:48:49 +0000 Subject: [PATCH] Check for sufficient bytes, or space in buffer, before proceeding. In TX, check if there are sufficient bytes in the buffer to write a block to SGPIO. If not, write zeros to SGPIO instead. In RX, check if there is sufficent space in the buffer to store a block read from SGPIO. If not, do nothing, which discards the data. In both of these shortfall cases, the M0 count is not incremented. This ensures that in TX, old data is never repeated. The M0 will not resume writing TX samples to SGPIO until the M4 count advances, indicating new data being ready in the buffer. This fixes bug #180. Similarly, in RX, old data is never overwritten. The M0 will not resume writing RX samples to the buffer until the M4 count advances, indicating new space being available in the buffer. --- firmware/hackrf_usb/sgpio_m0.s | 65 +++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/firmware/hackrf_usb/sgpio_m0.s b/firmware/hackrf_usb/sgpio_m0.s index 983e7781..3056c20b 100644 --- a/firmware/hackrf_usb/sgpio_m0.s +++ b/firmware/hackrf_usb/sgpio_m0.s @@ -64,10 +64,12 @@ These latencies are assumed to apply to all accesses to the SGPIO peripheral's address space, which includes its interrupt control registers as well as the shadow registers. -There are two key code paths, with the following worst-case timings: +There are four key code paths, with the following worst-case timings: -RX: 141 cycles -TX: 126 cycles +RX, normal: 146 cycles +RX, overrun: 52 cycles +TX, normal: 131 cycles +TX, underrun: 118 cycles Design ====== @@ -99,6 +101,7 @@ registers and fixed memory addresses. // Buffer that we're funneling data to/from. .equ TARGET_DATA_BUFFER, 0x20008000 +.equ TARGET_BUFFER_SIZE, 0x8000 .equ TARGET_BUFFER_MASK, 0x7fff // Base address of the state structure. @@ -124,6 +127,7 @@ registers and fixed memory addresses. /* Allocations of single-use registers */ +buf_size_minus_32 .req r14 state .req r13 buf_base .req r12 buf_mask .req r11 @@ -143,6 +147,8 @@ main: value .req r0 ldr sgpio_int, =SGPIO_EXCHANGE_INTERRUPT_BASE // sgpio_int = SGPIO_INT_BASE // 2 ldr sgpio_data, =SGPIO_SHADOW_REGISTERS_BASE // sgpio_data = SGPIO_REG_SS // 2 + ldr value, =(TARGET_BUFFER_SIZE - 32) // value = TARGET_BUFFER_SIZE - 32 // 2 + mov buf_size_minus_32, value // buf_size_minus_32 = value // 1 ldr value, =TARGET_DATA_BUFFER // value = TARGET_DATA_BUFFER // 2 mov buf_base, value // buf_base = value // 1 ldr value, =TARGET_BUFFER_MASK // value = TARGET_DATA_MASK // 2 @@ -205,12 +211,27 @@ loop: direction_tx: + // Check if there is enough data in the buffer. + // + // The number of bytes in the buffer is given by (m4_count - m0_count). + // We need 32 bytes available to proceed. So our margin, which we want + // to be positive or zero, is: + // + // buf_margin = m4_count - m0_count - 32 + // + // If there is insufficient data, transmit zeros instead. + buf_margin .req r0 + ldr buf_margin, [state, #M4_COUNT] // buf_margin = m4_count // 2 + sub buf_margin, count // buf_margin -= count // 1 + sub buf_margin, #32 // buf_margin -= 32 // 1 + bmi tx_zeros // if buf_margin < 0: goto tx_zeros // 1 thru, 3 taken + + // Write data to SGPIO. ldm buf_ptr!, {r0-r3} // r0-r3 = buf_ptr[0:16]; buf_ptr += 16 // 5 str r0, [sgpio_data, #SLICE0] // SGPIO_REG_SS[SLICE0] = r0 // 8 str r1, [sgpio_data, #SLICE1] // SGPIO_REG_SS[SLICE1] = r1 // 8 str r2, [sgpio_data, #SLICE2] // SGPIO_REG_SS[SLICE2] = r2 // 8 str r3, [sgpio_data, #SLICE3] // SGPIO_REG_SS[SLICE3] = r3 // 8 - ldm buf_ptr!, {r0-r3} // r0-r3 = buf_ptr[0:16]; buf_ptr += 16 // 5 str r0, [sgpio_data, #SLICE4] // SGPIO_REG_SS[SLICE4] = r0 // 8 str r1, [sgpio_data, #SLICE5] // SGPIO_REG_SS[SLICE5] = r1 // 8 @@ -219,14 +240,48 @@ direction_tx: b done // goto done // 3 +tx_zeros: + + // Write zeros to SGPIO. + mov zero, #0 // zero = 0 // 1 + str zero, [sgpio_data, #SLICE0] // SGPIO_REG_SS[SLICE0] = zero // 8 + str zero, [sgpio_data, #SLICE1] // SGPIO_REG_SS[SLICE1] = zero // 8 + str zero, [sgpio_data, #SLICE2] // SGPIO_REG_SS[SLICE2] = zero // 8 + str zero, [sgpio_data, #SLICE3] // SGPIO_REG_SS[SLICE3] = zero // 8 + str zero, [sgpio_data, #SLICE4] // SGPIO_REG_SS[SLICE4] = zero // 8 + str zero, [sgpio_data, #SLICE5] // SGPIO_REG_SS[SLICE5] = zero // 8 + str zero, [sgpio_data, #SLICE6] // SGPIO_REG_SS[SLICE6] = zero // 8 + str zero, [sgpio_data, #SLICE7] // SGPIO_REG_SS[SLICE7] = zero // 8 + + b loop // goto loop // 3 + direction_rx: + // Check if there is enough space in the buffer. + // + // The number of bytes in the buffer is given by (m0_count - m4_count). + // We need space for another 32 bytes to proceed. So our margin, which + // we want to be positive or zero, is: + // + // buf_margin = buf_size - (m0_count - state.m4_count) - 32 + // + // which can be rearranged for efficiency as: + // + // buf_margin = m4_count + (buf_size - 32) - m0_count + // + // If there is insufficient space, jump back to the start of the loop. + buf_margin .req r0 + ldr buf_margin, [state, #M4_COUNT] // buf_margin = state.m4_count // 2 + add buf_margin, buf_size_minus_32 // buf_margin += buf_size_minus_32 // 1 + sub buf_margin, count // buf_margin -= count // 1 + bmi loop // if buf_margin < 0: goto loop // 1 thru, 3 taken + + // Read data from SGPIO. ldr r0, [sgpio_data, #SLICE0] // r0 = SGPIO_REG_SS[SLICE0] // 10 ldr r1, [sgpio_data, #SLICE1] // r1 = SGPIO_REG_SS[SLICE1] // 10 ldr r2, [sgpio_data, #SLICE2] // r2 = SGPIO_REG_SS[SLICE2] // 10 ldr r3, [sgpio_data, #SLICE3] // r3 = SGPIO_REG_SS[SLICE3] // 10 stm buf_ptr!, {r0-r3} // buf_ptr[0:16] = r0-r3; buf_ptr += 16 // 5 - ldr r0, [sgpio_data, #SLICE4] // r0 = SGPIO_REG_SS[SLICE4] // 10 ldr r1, [sgpio_data, #SLICE5] // r1 = SGPIO_REG_SS[SLICE5] // 10 ldr r2, [sgpio_data, #SLICE6] // r2 = SGPIO_REG_SS[SLICE6] // 10