Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions misrc_tools/common/ddd_protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ static bool ddd_read_register(const ddd_control_ops_t *ops,
return ddd_control_in(ops, DDD_REQUEST_REGISTER_READ, address, value, 1);
}

static uint16_t ddd_read_little_endian_word(const uint8_t *block,
size_t offset)
{
return (uint16_t)(block[offset] |
((uint16_t)block[offset + 1u] << 8));
}

bool ddd_is_known_device_id(uint16_t vendor_id, uint16_t product_id)
{
return (vendor_id == DDD_LEGACY_VENDOR_ID &&
Expand Down Expand Up @@ -351,6 +358,143 @@ void ddd_collection_state_init(ddd_collection_state_t *state)
state->profile = DDD_DEVICE_NOT_DDD;
}

void ddd_fifo_telemetry_init(ddd_fifo_telemetry_t *telemetry)
{
if (telemetry) memset(telemetry, 0, sizeof(*telemetry));
}

bool ddd_fifo_telemetry_parse(const uint8_t *block,
size_t block_length,
ddd_fifo_telemetry_t *telemetry)
{
uint8_t status;
uint8_t format;
uint16_t depth;
uint16_t packet;

if (!telemetry) return false;
ddd_fifo_telemetry_init(telemetry);
if (!block || block_length < DDD_FIFO_TELEMETRY_LENGTH ||
block[0] != DDD_FIFO_TELEMETRY_ID) {
return false;
}

status = block[DDD_FIFO_OFFSET_STATUS];
format = status & DDD_FIFO_TELEMETRY_FORMAT_MASK;
if (format != DDD_FIFO_TELEMETRY_FORMAT) return false;

depth = ddd_read_little_endian_word(block, DDD_FIFO_OFFSET_DEPTH);
packet = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_PACKET_WORDS);
if (packet == 0 || depth <= packet) return false;

telemetry->present = true;
telemetry->format = format;
telemetry->overflow_seen =
(status & DDD_FIFO_FLAG_OVERFLOW_SEEN) != 0;
telemetry->saturated = (status & DDD_FIFO_FLAG_SATURATED) != 0;
telemetry->latch_count = block[DDD_FIFO_OFFSET_LATCH_COUNT];
telemetry->used_now = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_USED_NOW);
telemetry->peak = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_PEAK);
telemetry->peak_since_open = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_PEAK_LIFETIME);
telemetry->overflow_events = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_OVERFLOWS);
telemetry->dropped_words = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_DROPPED);
telemetry->packets_read = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_PACKETS);
telemetry->near_full_units = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_NEAR_FULL);
telemetry->depth_words = depth;
telemetry->packet_words = packet;
telemetry->near_full_words = ddd_read_little_endian_word(
block, DDD_FIFO_OFFSET_NEAR_FULL_WORDS);
return true;
}

int ddd_fifo_backpressure_percent(const ddd_fifo_telemetry_t *telemetry)
{
int headroom;
int excursion;
int percent;

if (!telemetry || !telemetry->present) return 0;
if (telemetry->overflow_events > 0) return 100;
if (telemetry->peak <= telemetry->packet_words) return 0;

headroom = (int)telemetry->depth_words - (int)telemetry->packet_words;
if (headroom <= 0) return 0;
excursion = (int)telemetry->peak - (int)telemetry->packet_words;
percent = excursion * 100 / headroom;
if (percent < 0) return 0;
return percent > 100 ? 100 : percent;
}

int ddd_fifo_peak_percent(const ddd_fifo_telemetry_t *telemetry)
{
int percent;
if (!telemetry || !telemetry->present || telemetry->depth_words == 0) {
return 0;
}
percent = (int)telemetry->peak * 100 / (int)telemetry->depth_words;
if (percent < 0) return 0;
return percent > 100 ? 100 : percent;
}

int ddd_fifo_used_percent(const ddd_fifo_telemetry_t *telemetry)
{
int percent;
if (!telemetry || !telemetry->present || telemetry->depth_words == 0) {
return 0;
}
percent = (int)telemetry->used_now * 100 /
(int)telemetry->depth_words;
if (percent < 0) return 0;
return percent > 100 ? 100 : percent;
}

void ddd_fifo_telemetry_totals_init(
ddd_fifo_telemetry_totals_t *totals)
{
if (!totals) return;
memset(totals, 0, sizeof(*totals));
totals->interval_coverage_complete = true;
}

bool ddd_fifo_telemetry_totals_add(
ddd_fifo_telemetry_totals_t *totals,
const ddd_fifo_telemetry_t *telemetry)
{
uint8_t latch_delta;
int backpressure;

if (!totals || !telemetry || !telemetry->present) return false;
if (totals->latch_seen) {
latch_delta = (uint8_t)(telemetry->latch_count -
totals->last_latch_count);
if (latch_delta == 0) return false;
if (latch_delta != 1) totals->interval_coverage_complete = false;
}

totals->latch_seen = true;
totals->last_latch_count = telemetry->latch_count;
totals->saturated = totals->saturated || telemetry->saturated;
totals->overflow_events += telemetry->overflow_events;
totals->dropped_words += telemetry->dropped_words;
totals->near_full_units += telemetry->near_full_units;
if (telemetry->peak > totals->peak_words) {
totals->peak_words = telemetry->peak;
}
backpressure = ddd_fifo_backpressure_percent(telemetry);
if (backpressure > totals->peak_backpressure_percent) {
totals->peak_backpressure_percent = backpressure;
}
return true;
}

static ddd_protocol_result_t ddd_restore_safe_defaults(
const ddd_control_ops_t *ops)
{
Expand Down
67 changes: 67 additions & 0 deletions misrc_tools/common/ddd_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ extern "C" {
#define DDD_REGISTER_TEST_MODE UINT8_C(0x10)
#define DDD_REGISTER_DECIMATION UINT8_C(0x12)

/* Self-described FIFO telemetry block. Reading address 0x40 latches a
* coherent snapshot and clears the interval counters in the gateware. */
#define DDD_REGISTER_FIFO_TELEMETRY UINT8_C(0x40)
#define DDD_FIFO_TELEMETRY_ID UINT8_C(0xBD)
#define DDD_FIFO_TELEMETRY_LENGTH 23u
#define DDD_FIFO_TELEMETRY_FORMAT UINT8_C(1)
#define DDD_FIFO_TELEMETRY_FORMAT_MASK UINT8_C(0x0F)
#define DDD_FIFO_FLAG_OVERFLOW_SEEN UINT8_C(0x10)
#define DDD_FIFO_FLAG_SATURATED UINT8_C(0x20)
#define DDD_FIFO_NEAR_FULL_PRESCALE 256u

#define DDD_FIFO_OFFSET_STATUS 1u
#define DDD_FIFO_OFFSET_LATCH_COUNT 2u
#define DDD_FIFO_OFFSET_USED_NOW 3u
#define DDD_FIFO_OFFSET_PEAK 5u
#define DDD_FIFO_OFFSET_PEAK_LIFETIME 7u
#define DDD_FIFO_OFFSET_OVERFLOWS 9u
#define DDD_FIFO_OFFSET_DROPPED 11u
#define DDD_FIFO_OFFSET_PACKETS 13u
#define DDD_FIFO_OFFSET_NEAR_FULL 15u
#define DDD_FIFO_OFFSET_DEPTH 17u
#define DDD_FIFO_OFFSET_PACKET_WORDS 19u
#define DDD_FIFO_OFFSET_NEAR_FULL_WORDS 21u

#define DDD_DECIMATION_FULL_RATE UINT8_C(1)
#define DDD_DECIMATION_HALF_RATE UINT8_C(2)
#define DDD_CONVERTER_SAMPLE_RATE_HZ UINT32_C(40000000)
Expand Down Expand Up @@ -174,6 +198,36 @@ typedef struct ddd_control_ops {
void *context;
} ddd_control_ops_t;

typedef struct ddd_fifo_telemetry {
bool present;
uint8_t format;
bool overflow_seen;
bool saturated;
uint8_t latch_count;
uint16_t used_now;
uint16_t peak;
uint16_t peak_since_open;
uint16_t overflow_events;
uint16_t dropped_words;
uint16_t packets_read;
uint16_t near_full_units;
uint16_t depth_words;
uint16_t packet_words;
uint16_t near_full_words;
} ddd_fifo_telemetry_t;

typedef struct ddd_fifo_telemetry_totals {
bool latch_seen;
uint8_t last_latch_count;
bool interval_coverage_complete;
bool saturated;
uint64_t overflow_events;
uint64_t dropped_words;
uint64_t near_full_units;
uint16_t peak_words;
int peak_backpressure_percent;
} ddd_fifo_telemetry_totals_t;

typedef enum ddd_protocol_result {
DDD_PROTOCOL_OK = 0,
DDD_PROTOCOL_INVALID_ARGUMENT,
Expand All @@ -199,6 +253,19 @@ typedef struct ddd_collection_state {

void ddd_collection_state_init(ddd_collection_state_t *state);

void ddd_fifo_telemetry_init(ddd_fifo_telemetry_t *telemetry);
bool ddd_fifo_telemetry_parse(const uint8_t *block,
size_t block_length,
ddd_fifo_telemetry_t *telemetry);
int ddd_fifo_backpressure_percent(const ddd_fifo_telemetry_t *telemetry);
int ddd_fifo_peak_percent(const ddd_fifo_telemetry_t *telemetry);
int ddd_fifo_used_percent(const ddd_fifo_telemetry_t *telemetry);
void ddd_fifo_telemetry_totals_init(
ddd_fifo_telemetry_totals_t *totals);
bool ddd_fifo_telemetry_totals_add(
ddd_fifo_telemetry_totals_t *totals,
const ddd_fifo_telemetry_t *telemetry);

/* Protocol-v1 order: B7(identity), B8(test), B8(decimation), B7(test),
* B7(decimation), B5(start). Every partial start is rolled back to B5(stop),
* test=0 and decimation=1 with verified readback. */
Expand Down
13 changes: 12 additions & 1 deletion misrc_tools/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ if raylib_dep.found()
sources_gui += 'misrc_gui/input/gui_ddd_v1.c'
sources_gui += 'misrc_gui/input/gui_ddd_async.c'
sources_gui += 'misrc_gui/input/gui_ddd_clockgen.c'
sources_gui += 'misrc_gui/ui/gui_ddd_fifo_status.c'
endif

# Add RTL-SDR support to GUI if available
Expand Down Expand Up @@ -569,6 +570,15 @@ ddd_protocol_test = executable('ddd_protocol_test',
)
test('ddd_protocol', ddd_protocol_test)

ddd_fifo_status_test = executable('gui_ddd_fifo_status_test',
['test/gui_ddd_fifo_status_test.c',
'misrc_gui/ui/gui_ddd_fifo_status.c',
'common/ddd_protocol.c'],
dependencies: [],
c_args: cflags,
)
test('gui_ddd_fifo_status', ddd_fifo_status_test)

ddd_async_policy_test = executable('gui_ddd_async_policy_test',
'test/gui_ddd_async_policy_test.c',
dependencies: [],
Expand All @@ -579,7 +589,8 @@ test('gui_ddd_async_policy', ddd_async_policy_test)
if ddd_enabled
ddd_async_fault_test = executable('gui_ddd_async_fault_test',
['test/gui_ddd_async_fault_test.c',
'misrc_gui/input/gui_ddd_async.c'],
'misrc_gui/input/gui_ddd_async.c',
'common/ddd_protocol.c'],
dependencies: [libusb_common_dep],
c_args: cflags,
)
Expand Down
Loading