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.
This commit is contained in:
Martin Ling
2022-02-09 02:33:22 +00:00
parent 8bd3745253
commit 137f2481e5
4 changed files with 45 additions and 7 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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() {

View File

@ -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 {