From 137f2481e5413912bb4010b2f164898fda7ee37b Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 9 Feb 2022 02:33:22 +0000 Subject: [PATCH] Make an error code available when a shortfall limit is hit. Previously, finding the M0 in IDLE mode was ambiguous; it could indicate either a normal outcome, or a shortfall limit having being hit. To disambiguate, we add an error field to the M0 state. The errors currently possible are an RX timeout or a TX timeout, both of which can be obtained efficiently from the current operating mode due to the values used. This adds 3 cycles to both shortfall paths, in order to shift down the mode to obtain the error code, and store it to the M0 state. --- firmware/hackrf_usb/m0_state.h | 7 ++++++ firmware/hackrf_usb/sgpio_m0.s | 33 ++++++++++++++++++++++------ host/hackrf-tools/src/hackrf_debug.c | 10 +++++++++ host/libhackrf/src/hackrf.h | 2 ++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/firmware/hackrf_usb/m0_state.h b/firmware/hackrf_usb/m0_state.h index ca6f057d..895a10d0 100644 --- a/firmware/hackrf_usb/m0_state.h +++ b/firmware/hackrf_usb/m0_state.h @@ -34,6 +34,7 @@ struct m0_state { uint32_t shortfall_limit; uint32_t threshold; uint32_t next_mode; + uint32_t error; }; enum m0_mode { @@ -44,6 +45,12 @@ enum m0_mode { M0_MODE_TX_RUN = 4, }; +enum m0_error { + M0_ERROR_NONE = 0, + M0_ERROR_RX_TIMEOUT = 1, + M0_ERROR_TX_TIMEOUT = 2, +}; + /* Address of m0_state is set in ldscripts. If you change the name of this * variable, it won't be where it needs to be in the processor's address space, * unless you also adjust the ldscripts. diff --git a/firmware/hackrf_usb/sgpio_m0.s b/firmware/hackrf_usb/sgpio_m0.s index f12f062d..33ed3e81 100644 --- a/firmware/hackrf_usb/sgpio_m0.s +++ b/firmware/hackrf_usb/sgpio_m0.s @@ -101,9 +101,9 @@ shadow registers. There are four key code paths, with the following worst-case timings: RX, normal: 152 cycles -RX, overrun: 73 cycles +RX, overrun: 76 cycles TX, normal: 142 cycles -TX, underrun: 140 cycles +TX, underrun: 143 cycles Design ====== @@ -222,9 +222,10 @@ The rest of this file is organised as follows: .equ SHORTFALL_LIMIT, 0x14 .equ THRESHOLD, 0x18 .equ NEXT_MODE, 0x1C +.equ ERROR, 0x20 // Private variables stored after state. -.equ PREV_LONGEST_SHORTFALL, 0x20 +.equ PREV_LONGEST_SHORTFALL, 0x24 // Operating modes. .equ MODE_IDLE, 0 @@ -233,6 +234,11 @@ The rest of this file is organised as follows: .equ MODE_TX_START, 3 .equ MODE_TX_RUN, 4 +// Error codes. +.equ ERROR_NONE, 0 +.equ ERROR_RX_TIMEOUT, 1 +.equ ERROR_TX_TIMEOUT, 2 + // Our slice chain is set up as follows (ascending data age; arrows are reversed for flow): // L -> F -> K -> C -> J -> E -> I -> A // Which has equivalent shadow register offsets: @@ -273,6 +279,7 @@ buf_ptr .req r4 str zero, [state, #NUM_SHORTFALLS] // state.num_shortfalls = zero // 2 str zero, [state, #LONGEST_SHORTFALL] // state.longest_shortfall = zero // 2 str zero, [state, #PREV_LONGEST_SHORTFALL] // prev_longest_shortfall = zero // 2 + str zero, [state, #ERROR] // state.error = zero // 2 mov shortfall_length, zero // shortfall_length = zero // 1 mov count, zero // count = zero // 1 .endm @@ -396,8 +403,19 @@ buf_ptr .req r4 cmp length, limit // if length < limit: // 1 blt \name\()_loop // goto loop // 1 thru, 3 taken - // If so, reset mode to idle and return to idle loop. + // If so, reset mode to idle and return to idle loop, logging an error. + // + // Modes are mapped to errors as follows: + // + // MODE_RX (2) -> ERROR_RX_TIMEOUT (1) + // MODE_TX_RUN (4) -> ERROR_TX_TIMEOUT (2) + // + // As such, the error code can be obtained by shifting the mode right by 1 bit. + mode .req r3 + error .req r2 + lsr error, mode, #1 // error = mode >> 1 // 1 + str error, [state, #ERROR] // state.error = error // 2 mov mode, #MODE_IDLE // mode = MODE_IDLE // 1 str mode, [state, #MODE] // state.mode = mode // 2 b idle // goto idle // 3 @@ -466,6 +484,7 @@ main: str zero, [state, #SHORTFALL_LIMIT] // state.shortfall_limit = zero // 2 str zero, [state, #THRESHOLD] // state.threshold = zero // 2 str zero, [state, #NEXT_MODE] // state.next_mode = zero // 2 + str zero, [state, #ERROR] // state.error = zero // 2 idle: // Wait for a mode to be set, then jump to the appropriate loop. @@ -526,7 +545,7 @@ checked_rollback: tx_start: // Reset counts. - reset_counts // reset_counts() // 10 + reset_counts // reset_counts() // 12 tx_loop: @@ -588,7 +607,7 @@ tx_write: wait_start: // Reset counts. - reset_counts // reset_counts() // 10 + reset_counts // reset_counts() // 12 wait_loop: @@ -610,7 +629,7 @@ wait_loop: rx_start: // Reset counts. - reset_counts // reset_counts() // 10 + reset_counts // reset_counts() // 12 rx_loop: diff --git a/host/hackrf-tools/src/hackrf_debug.c b/host/hackrf-tools/src/hackrf_debug.c index 4cb5b41f..a0a13668 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -386,6 +386,15 @@ static const char * mode_name(uint32_t mode) { return "UNKNOWN"; } +static const char * error_name(uint32_t error) { + const char *error_names[] = {"NONE", "RX_TIMEOUT", "TX_TIMEOUT"}; + const uint32_t num_errors = sizeof(error_names) / sizeof(error_names[0]); + if (error < num_errors) + return error_names[error]; + else + return "UNKNOWN"; +} + static void print_state(hackrf_m0_state *state) { printf("M0 state:\n"); printf("Mode: %u (%s)\n", state->mode, mode_name(state->mode)); @@ -396,6 +405,7 @@ static void print_state(hackrf_m0_state *state) { printf("Shortfall limit: %u bytes\n", state->shortfall_limit); printf("Mode change threshold: %u bytes\n", state->threshold); printf("Next mode: %u (%s)\n", state->mode, mode_name(state->mode)); + printf("Error: %u (%s)\n", state->error, error_name(state->error)); } static void usage() { diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 8bd7bd78..0ded0c0b 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -173,6 +173,8 @@ typedef struct { uint32_t threshold; /** Mode which will be switched to when threshold is reached. */ uint32_t next_mode; + /** Error, if any, that caused the M0 to revert to IDLE mode. */ + uint32_t error; } hackrf_m0_state; struct hackrf_device_list {