Keep count of number of shortfalls.

To enable this, we keep a count of the current shortfall length. Each
time an SGPIO read/write cannot be completed due to a shortfall, we
increase this length. Each time an SGPIO read/write is completed
successfully, we reset the shortfall length to zero.

When a shortfall occurs and the existing shortfall length is zero, this
indicates a new shortfall, and the shortfall count is incremented.

This change adds one cycle to the normal RX & TX paths, to zero the
shortfall count. To enable this to be done in a single cycle, we keep a
zero handy in a high register.

The extra accounting adds 10 cycles to the TX and RX shortfall paths,
plus an additional 3 cycles to the RX shortfall path since there are
now two branches involved: one to the shortfall handler, and another
back to the main loop.
This commit is contained in:
Martin Ling
2021-12-24 09:09:16 +00:00
parent 0f3069ee5e
commit a7bd1e3ede
5 changed files with 54 additions and 12 deletions

View File

@ -29,6 +29,7 @@ struct m0_state {
uint32_t mode; uint32_t mode;
uint32_t m0_count; uint32_t m0_count;
uint32_t m4_count; uint32_t m4_count;
uint32_t num_shortfalls;
}; };
enum m0_mode { enum m0_mode {

View File

@ -66,10 +66,10 @@ shadow registers.
There are four key code paths, with the following worst-case timings: There are four key code paths, with the following worst-case timings:
RX, normal: 146 cycles RX, normal: 147 cycles
RX, overrun: 52 cycles RX, overrun: 65 cycles
TX, normal: 132 cycles TX, normal: 133 cycles
TX, underrun: 119 cycles TX, underrun: 129 cycles
Design Design
====== ======
@ -111,6 +111,7 @@ registers and fixed memory addresses.
.equ MODE, 0x00 .equ MODE, 0x00
.equ M0_COUNT, 0x04 .equ M0_COUNT, 0x04
.equ M4_COUNT, 0x08 .equ M4_COUNT, 0x08
.equ NUM_SHORTFALLS, 0x0C
// Operating modes. // Operating modes.
.equ MODE_IDLE, 0 .equ MODE_IDLE, 0
@ -136,6 +137,8 @@ buf_size_minus_32 .req r14
state .req r13 state .req r13
buf_base .req r12 buf_base .req r12
buf_mask .req r11 buf_mask .req r11
shortfall_length .req r10
hi_zero .req r9
sgpio_data .req r7 sgpio_data .req r7
sgpio_int .req r6 sgpio_int .req r6
count .req r5 count .req r5
@ -160,13 +163,15 @@ main:
mov buf_mask, value // buf_mask = value // 1 mov buf_mask, value // buf_mask = value // 1
ldr value, =STATE_BASE // value = STATE_BASE // 2 ldr value, =STATE_BASE // value = STATE_BASE // 2
mov state, value // state = value // 1 mov state, value // state = value // 1
// Initialise state.
zero .req r0 zero .req r0
mov zero, #0 // zero = 0 // 1 mov zero, #0 // zero = 0 // 1
mov hi_zero, zero // hi_zero = zero // 1
// Initialise state.
str zero, [state, #MODE] // state.mode = zero // 2 str zero, [state, #MODE] // state.mode = zero // 2
str zero, [state, #M0_COUNT] // state.m0_count = zero // 2 str zero, [state, #M0_COUNT] // state.m0_count = zero // 2
str zero, [state, #M4_COUNT] // state.m4_count = zero // 2 str zero, [state, #M4_COUNT] // state.m4_count = zero // 2
str zero, [state, #NUM_SHORTFALLS] // state.num_shortfalls = zero // 2
idle: idle:
// Wait for RX or TX mode to be set. // Wait for RX or TX mode to be set.
@ -179,6 +184,8 @@ idle:
mov zero, #0 // zero = 0 // 1 mov zero, #0 // zero = 0 // 1
str zero, [state, #M0_COUNT] // state.m0_count = zero // 2 str zero, [state, #M0_COUNT] // state.m0_count = zero // 2
str zero, [state, #M4_COUNT] // state.m4_count = zero // 2 str zero, [state, #M4_COUNT] // state.m4_count = zero // 2
str zero, [state, #NUM_SHORTFALLS] // state.num_shortfalls = zero // 2
mov shortfall_length, zero // shortfall_length = zero // 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
@ -270,6 +277,29 @@ tx_zeros:
str zero, [sgpio_data, #SLICE6] // SGPIO_REG_SS[SLICE6] = zero // 8 str zero, [sgpio_data, #SLICE6] // SGPIO_REG_SS[SLICE6] = zero // 8
str zero, [sgpio_data, #SLICE7] // SGPIO_REG_SS[SLICE7] = zero // 8 str zero, [sgpio_data, #SLICE7] // SGPIO_REG_SS[SLICE7] = zero // 8
shortfall:
// Get current shortfall length from high register.
length .req r0
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.
num .req r1
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
// Return to main loop.
b loop // goto loop // 3 b loop // goto loop // 3
direction_rx: direction_rx:
@ -286,12 +316,12 @@ direction_rx:
// //
// buf_margin = m4_count + (buf_size - 32) - m0_count // buf_margin = m4_count + (buf_size - 32) - m0_count
// //
// If there is insufficient space, jump back to the start of the loop. // If there is insufficient space, jump to shortfall handling.
buf_margin .req r0 buf_margin .req r0
ldr buf_margin, [state, #M4_COUNT] // buf_margin = state.m4_count // 2 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 add buf_margin, buf_size_minus_32 // buf_margin += buf_size_minus_32 // 1
sub buf_margin, count // buf_margin -= count // 1 sub buf_margin, count // buf_margin -= count // 1
bmi loop // if buf_margin < 0: goto loop // 1 thru, 3 taken bmi shortfall // if buf_margin < 0: goto shortfall // 1 thru, 3 taken
// Read data from SGPIO. // Read data from SGPIO.
ldr r0, [sgpio_data, #SLICE0] // r0 = SGPIO_REG_SS[SLICE0] // 10 ldr r0, [sgpio_data, #SLICE0] // r0 = SGPIO_REG_SS[SLICE0] // 10
@ -312,6 +342,9 @@ done:
// ... and store the new count. // ... and store the new count.
str count, [state, #M0_COUNT] // state.m0_count = count // 2 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
b loop // goto loop // 3 b loop // goto loop // 3
// The linker will put a literal pool here, so add a label for clearer objdump output: // The linker will put a literal pool here, so add a label for clearer objdump output:

View File

@ -388,6 +388,7 @@ static void print_state(hackrf_m0_state *state) {
"UNKNOWN"); "UNKNOWN");
printf("M0 count: %u bytes\n", state->m0_count); printf("M0 count: %u bytes\n", state->m0_count);
printf("M4 count: %u bytes\n", state->m4_count); printf("M4 count: %u bytes\n", state->m4_count);
printf("Number of shortfalls: %u\n", state->num_shortfalls);
} }
static void usage() { static void usage() {

View File

@ -1159,9 +1159,11 @@ int main(int argc, char** argv) {
if (result != HACKRF_SUCCESS) if (result != HACKRF_SUCCESS)
fprintf(stderr, "\nhackrf_get_m0_state() failed: %s (%d)\n", hackrf_error_name(result), result); fprintf(stderr, "\nhackrf_get_m0_state() failed: %s (%d)\n", hackrf_error_name(result), result);
else else
fprintf(stderr, ", %d bytes %s in buffer\n", fprintf(stderr, ", %d bytes %s in buffer, %u %s\n",
tx ? state.m4_count - state.m0_count : state.m0_count - state.m4_count, tx ? state.m4_count - state.m0_count : state.m0_count - state.m4_count,
tx ? "filled" : "free"); tx ? "filled" : "free",
state.num_shortfalls,
tx ? "underruns" : "overruns");
} else { } else {
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
@ -1216,9 +1218,12 @@ int main(int argc, char** argv) {
fprintf(stderr, fprintf(stderr,
"Transfer statistics:\n" "Transfer statistics:\n"
"%lu bytes transferred by M0\n" "%lu bytes transferred by M0\n"
"%lu bytes transferred by M4\n", "%lu bytes transferred by M4\n"
"%u %s\n",
stats.m0_total, stats.m0_total,
stats.m4_total); stats.m4_total,
state.num_shortfalls,
(transmit || signalsource) ? "underruns" : "overruns");
} }
} }

View File

@ -163,6 +163,8 @@ typedef struct {
uint32_t m0_count; uint32_t m0_count;
/** Number of bytes transferred by the M4. */ /** Number of bytes transferred by the M4. */
uint32_t m4_count; uint32_t m4_count;
/** Number of shortfalls. */
uint32_t num_shortfalls;
} hackrf_m0_state; } hackrf_m0_state;
struct hackrf_device_list { struct hackrf_device_list {