X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=blobdiff_plain;f=src%2Fhardware%2Fasix-sigma%2Fprotocol.c;h=142316514c7ceab0ebda7fc8a157fa66cc495302;hp=ce6974c6ec38a767f6e61bd9766ddbd2ca5837ee;hb=e1a712ca268df9c179377cf5c48ece8b87ed0ed7;hpb=16a5d5ac7d0390b03278d2a65554d395337eaf45 diff --git a/src/hardware/asix-sigma/protocol.c b/src/hardware/asix-sigma/protocol.c index ce6974c6..14231651 100644 --- a/src/hardware/asix-sigma/protocol.c +++ b/src/hardware/asix-sigma/protocol.c @@ -625,7 +625,7 @@ static int sigma_fpga_init_bitbang_once(struct dev_context *devc) if (ret != SR_OK) return ret; g_usleep(10 * 1000); - ftdi_usb_purge_buffers(&devc->ftdi.ctx); + PURGE_FTDI_BOTH(&devc->ftdi.ctx); /* * Wait until the FPGA asserts INIT_B. Check in a maximum number @@ -889,7 +889,7 @@ static int upload_firmware(struct sr_context *ctx, struct dev_context *devc, ftdi_get_error_string(&devc->ftdi.ctx)); return SR_ERR; } - ftdi_usb_purge_buffers(&devc->ftdi.ctx); + PURGE_FTDI_BOTH(&devc->ftdi.ctx); while (sigma_read_raw(devc, &pins, sizeof(pins)) > 0) ; @@ -939,6 +939,7 @@ SR_PRIV int sigma_set_acquire_timeout(struct dev_context *devc) uint64_t count_msecs, acquire_msecs; sr_sw_limits_init(&devc->limit.acquire); + devc->late_trigger_timeout = FALSE; /* Get sample count limit, convert to msecs. */ ret = sr_sw_limits_config_get(&devc->limit.config, @@ -948,6 +949,10 @@ SR_PRIV int sigma_set_acquire_timeout(struct dev_context *devc) user_count = g_variant_get_uint64(data); g_variant_unref(data); count_msecs = 0; + if (devc->use_triggers) { + user_count *= 100 - devc->capture_ratio; + user_count /= 100; + } if (user_count) count_msecs = 1000 * user_count / devc->clock.samplerate + 1; @@ -958,14 +963,18 @@ SR_PRIV int sigma_set_acquire_timeout(struct dev_context *devc) return ret; user_msecs = g_variant_get_uint64(data); g_variant_unref(data); + if (devc->use_triggers) { + user_msecs *= 100 - devc->capture_ratio; + user_msecs /= 100; + } /* Get the lesser of them, with both being optional. */ - acquire_msecs = ~0ull; + acquire_msecs = ~UINT64_C(0); if (user_count && count_msecs < acquire_msecs) acquire_msecs = count_msecs; if (user_msecs && user_msecs < acquire_msecs) acquire_msecs = user_msecs; - if (acquire_msecs == ~0ull) + if (acquire_msecs == ~UINT64_C(0)) return SR_OK; /* Add some slack, and use that timeout for acquisition. */ @@ -978,7 +987,12 @@ SR_PRIV int sigma_set_acquire_timeout(struct dev_context *devc) if (ret != SR_OK) return ret; - sr_sw_limits_acquisition_start(&devc->limit.acquire); + /* Deferred or immediate (trigger-less) timeout period start. */ + if (devc->use_triggers) + devc->late_trigger_timeout = TRUE; + else + sr_sw_limits_acquisition_start(&devc->limit.acquire); + return SR_OK; } @@ -1255,7 +1269,7 @@ static int addto_submit_buffer(struct dev_context *devc, buffer = devc->buffer; limits = &devc->limit.submit; - if (sr_sw_limits_check(limits)) + if (!devc->use_triggers && sr_sw_limits_check(limits)) count = 0; /* @@ -1272,7 +1286,7 @@ static int addto_submit_buffer(struct dev_context *devc, return ret; } sr_sw_limits_update_samples_read(limits, 1); - if (sr_sw_limits_check(limits)) + if (!devc->use_triggers && sr_sw_limits_check(limits)) break; } @@ -1290,6 +1304,92 @@ static void sigma_location_break_down(struct sigma_location *loc) loc->cluster = loc->cluster / EVENTS_PER_CLUSTER; } +static gboolean sigma_location_is_eq(struct sigma_location *loc1, + struct sigma_location *loc2, gboolean with_event) +{ + + if (!loc1 || !loc2) + return FALSE; + + if (loc1->line != loc2->line) + return FALSE; + if (loc1->cluster != loc2->cluster) + return FALSE; + + if (with_event && loc1->event != loc2->event) + return FALSE; + + return TRUE; +} + +/* Decrement the broken-down location fields (leave 'raw' as is). */ +static void sigma_location_decrement(struct sigma_location *loc, + gboolean with_event) +{ + + if (!loc) + return; + + if (with_event) { + if (loc->event--) + return; + loc->event = EVENTS_PER_CLUSTER - 1; + } + + if (loc->cluster--) + return; + loc->cluster = CLUSTERS_PER_ROW - 1; + + if (loc->line--) + return; + loc->line = ROW_COUNT - 1; +} + +static void sigma_location_increment(struct sigma_location *loc) +{ + + if (!loc) + return; + + if (++loc->event < EVENTS_PER_CLUSTER) + return; + loc->event = 0; + if (++loc->cluster < CLUSTERS_PER_ROW) + return; + loc->cluster = 0; + if (++loc->line < ROW_COUNT) + return; + loc->line = 0; +} + +/* + * Determine the position where to open the period of trigger match + * checks. Setup an "impossible" location when triggers are not used. + * Start from the hardware provided 'trig' position otherwise, and + * go back a few clusters, but don't go before the 'start' position. + */ +static void rewind_trig_arm_pos(struct dev_context *devc, size_t count) +{ + struct sigma_sample_interp *interp; + + if (!devc) + return; + interp = &devc->interp; + + if (!devc->use_triggers) { + interp->trig_arm.raw = ~0; + sigma_location_break_down(&interp->trig_arm); + return; + } + + interp->trig_arm = interp->trig; + while (count--) { + if (sigma_location_is_eq(&interp->trig_arm, &interp->start, TRUE)) + break; + sigma_location_decrement(&interp->trig_arm, TRUE); + } +} + static int alloc_sample_buffer(struct dev_context *devc, size_t stop_pos, size_t trig_pos, uint8_t mode) { @@ -1329,6 +1429,16 @@ static int alloc_sample_buffer(struct dev_context *devc, sigma_location_break_down(&interp->trig); sigma_location_break_down(&interp->iter); + /* + * The hardware provided trigger location "is late" because of + * latency in hardware pipelines. It points to after the trigger + * condition match. Arrange for a software check of sample data + * matches starting just a little before the hardware provided + * location. The "4 clusters" distance is an arbitrary choice. + */ + rewind_trig_arm_pos(devc, 4 * EVENTS_PER_CLUSTER); + memset(&interp->trig_chk, 0, sizeof(interp->trig_chk)); + /* Determine which DRAM lines to fetch from the device. */ memset(&interp->fetch, 0, sizeof(interp->fetch)); interp->fetch.lines_total = interp->stop.line + 1; @@ -1398,15 +1508,97 @@ static void free_sample_buffer(struct dev_context *devc) { g_free(devc->interp.fetch.rcvd_lines); devc->interp.fetch.rcvd_lines = NULL; + devc->interp.fetch.lines_per_read = 0; } /* - * In 100 and 200 MHz mode, only a single pin rising/falling can be - * set as trigger. In other modes, two rising/falling triggers can be set, - * in addition to value/mask trigger for any number of channels. + * Parse application provided trigger conditions to the driver's internal + * presentation. Yields a mask of pins of interest, and their expected + * pin levels or edges. + * + * In 100 and 200 MHz mode, only a single pin's rising/falling edge can be + * set as trigger. In 50- MHz modes, two rising/falling edges can be set, + * in addition to value/mask specs for any number of channels. + * + * Hardware implementation detail: When more than one edge is specified, + * then the condition is only considered a match when _all_ transitions + * are seen in the same 20ns check interval, regardless of the user's + * perceived samplerate which can be a fraction of 50MHz. Which reduces + * practical use to edges on a single pin in addition to data patterns. + * Which still covers a lot of users' typical scenarios. Not an issue, + * just something to remain aware of. + * + * The Sigma hardware also supports complex triggers which involve the + * logical combination of several patterns, pulse durations, counts of + * condition matches, A-then-B sequences, etc. But this has not been + * implemented yet here, and applications may lack means to express + * these conditions (present the complex conditions to users for entry + * and review, pass application specs to drivers covering the versatile + * combinations). + * + * Implementor's note: This routine currently exclusively accepts input + * in the form of sr_trigger stages, which results from "01rf-" choices + * on a multitude of individual GUI traces, or the CLI's --trigger spec + * which takes one list of = details. + * + * TODO Consider the addition of SR_CONF_TRIGGER_PATTERN support, which + * accepts a single free form string argument, and could describe a + * multi-bit pattern without the tedious trace name/index selection. + * Fortunately the number of channels is fixed for this device, we need + * not come up with variable length support and counts beyond 64. _When_ + * --trigger as well as SR_CONF_TRIGGER_PATTERN are supported, then the + * implementation needs to come up with priorities for these sources of + * input specs, or enforce exclusive use of either form (at one time, + * per acquisition / invocation). + * + * Text forms that may be worth supporting: + * - Simple forms, mere numbers, optional base specs. These are easiest + * to implement with existing common conversion helpers. + * triggerpattern=[/] + * triggerpattern=255 + * triggerpattern=45054 + * triggerpattern=0xaffe + * triggerpattern=0xa0f0/0xf0f0 + * triggerpattern=0b1010111111111110/0x7ffe + * - Alternative bit pattern form, including wildcards in a single value. + * This cannot use common conversion support, needs special handling. + * triggerpattern=0b1010xxxx1111xxx0 + * This is most similar to SR_CONF_TRIGGER_PATTERN as hameg-hmo uses + * it. Passes the app's spec via SCPI to the device. See section 2.3.5 + * "Pattern trigger" and :TRIG:A:PATT:SOUR in the Hameg document. + * - Prefixed form to tell the above variants apart, and support both of + * them at the same time. Additional optional separator for long digit + * runs, and edge support in the form which lists individual bits (not + * useful for dec/hex formats). + * triggerpattern=value=45054 + * triggerpattern=value=0b1010111111111110 + * triggerpattern=value=0xa0f0,mask=0xf0f0 + * triggerpattern=bits=1010-xxxx-1111-xxxx + * triggerpattern=bits=0010-r100 * - * The Sigma supports complex triggers using boolean expressions, but this - * has not been implemented yet. + * TODO Check this set of processing rules for completeness/correctness. + * - Do implement the prefixed format which covers most use cases, _and_ + * should be usable from CLI and GUI environments. + * - Default to 'bits=' prefix if none was found (and only accept one + * single key/value pair in that case with the default key). + * - Accept dash and space separators in the 'bits=' value. Stick with + * mere unseparated values for value and mask, use common conversion. + * This results in transparent dec/bin/oct/hex support. Underscores? + * - Accept 0/1 binary digits in 'bits=', as well as r/f/e edge specs. + * - Only use --trigger (struct sr_trigger) when SR_CONF_TRIGGER_PATTERN + * is absent? Or always accept --trigger in addition to the data pattern + * spec? Then only accept edge specs from --trigger, since data pattern + * was most importantly motivated by address/data bus inspection? + * - TODO Consider edge= as an optional additional spec in + * the value= and mask= group? Does that help make exclusive support + * for either --trigger or -c triggerpattern acceptable? + * triggerpattern=value=0xa0f0,mask=0xb0f0,edge=15r + * triggerpattern=bits=1r10-xxxx-1111-xxxx + * triggerpattern=1r10-xxxx-1111-xxxx + * - *Any* input spec regardless of format and origin must end up in the + * 'struct sigma_trigger' internal presentation used by this driver. + * It's desirable to have sigma_convert_trigger() do all the parsing, + * and constraint checking in a central location. */ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi) { @@ -1416,21 +1608,18 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi) struct sr_trigger_match *match; const GSList *l, *m; uint16_t channelbit; - size_t trigger_set; + size_t edge_count; devc = sdi->priv; memset(&devc->trigger, 0, sizeof(devc->trigger)); devc->use_triggers = FALSE; + + /* TODO Consider additional SR_CONF_TRIGGER_PATTERN support. */ trigger = sr_session_trigger_get(sdi->session); if (!trigger) return SR_OK; - if (!ASIX_SIGMA_WITH_TRIGGER) { - sr_warn("Trigger support is not implemented. Ignoring the spec."); - return SR_OK; - } - - trigger_set = 0; + edge_count = 0; for (l = trigger->stages; l; l = l->next) { stage = l->data; for (m = stage->matches; m; m = m->next) { @@ -1441,7 +1630,7 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi) channelbit = BIT(match->channel->index); if (devc->clock.samplerate >= SR_MHZ(100)) { /* Fast trigger support. */ - if (trigger_set) { + if (edge_count > 0) { sr_err("100/200MHz modes limited to single trigger pin."); return SR_ERR; } @@ -1454,7 +1643,7 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi) return SR_ERR; } - trigger_set++; + edge_count++; } else { /* Simple trigger support (event). */ if (match->match == SR_TRIGGER_ONE) { @@ -1465,10 +1654,10 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi) devc->trigger.simplemask |= channelbit; } else if (match->match == SR_TRIGGER_FALLING) { devc->trigger.fallingmask |= channelbit; - trigger_set++; + edge_count++; } else if (match->match == SR_TRIGGER_RISING) { devc->trigger.risingmask |= channelbit; - trigger_set++; + edge_count++; } /* @@ -1476,7 +1665,7 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi) * but they are ORed and the current trigger syntax * does not permit ORed triggers. */ - if (trigger_set > 1) { + if (edge_count > 1) { sr_err("Limited to 1 edge trigger."); return SR_ERR; } @@ -1490,77 +1679,70 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi) return SR_OK; } -/* Software trigger to determine exact trigger position. */ -static int get_trigger_offset(uint8_t *samples, uint16_t last_sample, - struct sigma_trigger *t) +static gboolean sample_matches_trigger(struct dev_context *devc, uint16_t sample) { - const uint8_t *rdptr; - size_t i; - uint16_t sample; - - rdptr = samples; - sample = 0; - for (i = 0; i < 8; i++) { - if (i > 0) - last_sample = sample; - sample = read_u16le_inc(&rdptr); - - /* Simple triggers. */ - if ((sample & t->simplemask) != t->simplevalue) - continue; - - /* Rising edge. */ - if (((last_sample & t->risingmask) != 0) || - ((sample & t->risingmask) != t->risingmask)) - continue; - - /* Falling edge. */ - if ((last_sample & t->fallingmask) != t->fallingmask || - (sample & t->fallingmask) != 0) - continue; + struct sigma_sample_interp *interp; + uint16_t last_sample; + struct sigma_trigger *t; + gboolean simple_match, rising_match, falling_match; + gboolean matched; - break; - } + /* + * This logic is about improving the precision of the hardware + * provided trigger match position. Software checks are only + * required for a short range of samples, and only when a user + * specified trigger condition was involved during acquisition. + */ + if (!devc) + return FALSE; + if (!devc->use_triggers) + return FALSE; + interp = &devc->interp; + if (!interp->trig_chk.armed) + return FALSE; - /* If we did not match, return original trigger pos. */ - return i & 0x7; + /* + * Check if the current sample and its most recent transition + * match the initially provided trigger condition. The data + * must not fail either of the individual checks. Unused + * trigger features remain neutral in the summary expression. + */ + last_sample = interp->last.sample; + t = &devc->trigger; + simple_match = (sample & t->simplemask) == t->simplevalue; + rising_match = ((last_sample & t->risingmask) == 0) && + ((sample & t->risingmask) == t->risingmask); + falling_match = ((last_sample & t->fallingmask) == t->fallingmask) && + ((sample & t->fallingmask) == 0); + matched = simple_match && rising_match && falling_match; + + return matched; } -static gboolean sample_matches_trigger(struct dev_context *devc, uint16_t sample) +static int send_trigger_marker(struct dev_context *devc) { - /* TODO - * Check whether the combination of this very sample and the - * previous state match the configured trigger condition. This - * improves the resolution of the trigger marker's position. - * The hardware provided position is coarse, and may point to - * a position before the actual match. - * - * See the previous get_trigger_offset() implementation. This - * code needs to get re-used here. - */ - if (!devc->use_triggers) - return FALSE; + int ret; - (void)sample; - (void)get_trigger_offset; + ret = flush_submit_buffer(devc); + if (ret != SR_OK) + return ret; + ret = std_session_send_df_trigger(devc->buffer->sdi); + if (ret != SR_OK) + return ret; - return FALSE; + return SR_OK; } static int check_and_submit_sample(struct dev_context *devc, - uint16_t sample, size_t count, gboolean check_trigger) + uint16_t sample, size_t count) { gboolean triggered; int ret; - triggered = check_trigger && sample_matches_trigger(devc, sample); + triggered = sample_matches_trigger(devc, sample); if (triggered) { - ret = flush_submit_buffer(devc); - if (ret != SR_OK) - return ret; - ret = std_session_send_df_trigger(devc->buffer->sdi); - if (ret != SR_OK) - return ret; + send_trigger_marker(devc); + devc->interp.trig_chk.matched = TRUE; } ret = addto_submit_buffer(devc, sample, count); @@ -1570,6 +1752,46 @@ static int check_and_submit_sample(struct dev_context *devc, return SR_OK; } +static void sigma_location_check(struct dev_context *devc) +{ + struct sigma_sample_interp *interp; + + if (!devc) + return; + interp = &devc->interp; + + /* + * Manage the period of trigger match checks in software. + * Start supervision somewhere before the hardware provided + * location. Stop supervision after an arbitrary amount of + * event slots, or when a match was found. + */ + if (interp->trig_chk.armed) { + interp->trig_chk.evt_remain--; + if (!interp->trig_chk.evt_remain || interp->trig_chk.matched) + interp->trig_chk.armed = FALSE; + } + if (!interp->trig_chk.armed && !interp->trig_chk.matched) { + if (sigma_location_is_eq(&interp->iter, &interp->trig_arm, TRUE)) { + interp->trig_chk.armed = TRUE; + interp->trig_chk.matched = FALSE; + interp->trig_chk.evt_remain = 8 * EVENTS_PER_CLUSTER; + } + } + + /* + * Force a trigger marker when the software check found no match + * yet while the hardware provided position was reached. This + * very probably is a user initiated button press. + */ + if (interp->trig_chk.armed) { + if (sigma_location_is_eq(&interp->iter, &interp->trig, TRUE)) { + (void)send_trigger_marker(devc); + interp->trig_chk.matched = TRUE; + } + } +} + /* * Return the timestamp of "DRAM cluster". */ @@ -1628,15 +1850,12 @@ static uint16_t sigma_deinterlace_data_4x4(uint16_t indata, int idx) static void sigma_decode_dram_cluster(struct dev_context *devc, struct sigma_dram_cluster *dram_cluster, - size_t events_in_cluster, gboolean triggered) + size_t events_in_cluster) { uint16_t tsdiff, ts, sample, item16; size_t count; size_t evt; - if (!devc->use_triggers || !ASIX_SIGMA_WITH_TRIGGER) - triggered = FALSE; - /* * If this cluster is not adjacent to the previously received * cluster, then send the appropriate number of samples with the @@ -1652,7 +1871,7 @@ static void sigma_decode_dram_cluster(struct dev_context *devc, if (tsdiff > 0) { sample = devc->interp.last.sample; count = tsdiff * devc->interp.samples_per_event; - (void)check_and_submit_sample(devc, sample, count, FALSE); + (void)check_and_submit_sample(devc, sample, count); } devc->interp.last.ts = ts + EVENTS_PER_CLUSTER; @@ -1668,24 +1887,32 @@ static void sigma_decode_dram_cluster(struct dev_context *devc, item16 = sigma_dram_cluster_data(dram_cluster, evt); if (devc->interp.samples_per_event == 4) { sample = sigma_deinterlace_data_4x4(item16, 0); - check_and_submit_sample(devc, sample, 1, triggered); + check_and_submit_sample(devc, sample, 1); + devc->interp.last.sample = sample; sample = sigma_deinterlace_data_4x4(item16, 1); - check_and_submit_sample(devc, sample, 1, triggered); + check_and_submit_sample(devc, sample, 1); + devc->interp.last.sample = sample; sample = sigma_deinterlace_data_4x4(item16, 2); - check_and_submit_sample(devc, sample, 1, triggered); + check_and_submit_sample(devc, sample, 1); + devc->interp.last.sample = sample; sample = sigma_deinterlace_data_4x4(item16, 3); - check_and_submit_sample(devc, sample, 1, triggered); + check_and_submit_sample(devc, sample, 1); + devc->interp.last.sample = sample; } else if (devc->interp.samples_per_event == 2) { sample = sigma_deinterlace_data_2x8(item16, 0); - check_and_submit_sample(devc, sample, 1, triggered); + check_and_submit_sample(devc, sample, 1); + devc->interp.last.sample = sample; sample = sigma_deinterlace_data_2x8(item16, 1); - check_and_submit_sample(devc, sample, 1, triggered); + check_and_submit_sample(devc, sample, 1); + devc->interp.last.sample = sample; } else { sample = item16; - check_and_submit_sample(devc, sample, 1, triggered); + check_and_submit_sample(devc, sample, 1); + devc->interp.last.sample = sample; } + sigma_location_increment(&devc->interp.iter); + sigma_location_check(devc); } - devc->interp.last.sample = sample; } /* @@ -1699,30 +1926,17 @@ static void sigma_decode_dram_cluster(struct dev_context *devc, */ static int decode_chunk_ts(struct dev_context *devc, struct sigma_dram_line *dram_line, - size_t events_in_line, size_t trigger_event) + size_t events_in_line) { struct sigma_dram_cluster *dram_cluster; size_t clusters_in_line; size_t events_in_cluster; size_t cluster; - size_t trigger_cluster; clusters_in_line = events_in_line; clusters_in_line += EVENTS_PER_CLUSTER - 1; clusters_in_line /= EVENTS_PER_CLUSTER; - /* Check if trigger is in this chunk. */ - trigger_cluster = ~UINT64_C(0); - if (trigger_event < EVENTS_PER_ROW) { - if (devc->clock.samplerate <= SR_MHZ(50)) { - trigger_event -= MIN(EVENTS_PER_CLUSTER - 1, - trigger_event); - } - - /* Find in which cluster the trigger occurred. */ - trigger_cluster = trigger_event / EVENTS_PER_CLUSTER; - } - /* For each full DRAM cluster. */ for (cluster = 0; cluster < clusters_in_line; cluster++) { dram_cluster = &dram_line->cluster[cluster]; @@ -1736,7 +1950,7 @@ static int decode_chunk_ts(struct dev_context *devc, } sigma_decode_dram_cluster(devc, dram_cluster, - events_in_cluster, cluster == trigger_cluster); + events_in_cluster); } return SR_OK; @@ -1749,68 +1963,95 @@ static int download_capture(struct sr_dev_inst *sdi) uint32_t stoppos, triggerpos; uint8_t modestatus; int ret; + size_t chunks_per_receive_call; devc = sdi->priv; interp = &devc->interp; - sr_info("Downloading sample data."); - devc->state = SIGMA_DOWNLOAD; - /* + * Check the mode register. Force stop the current acquisition + * if it has not yet terminated before. Will block until the + * acquisition stops, assuming that this won't take long. Should + * execute exactly once, then keep finding its condition met. + * * Ask the hardware to stop data acquisition. Reception of the * FORCESTOP request makes the hardware "disable RLE" (store * clusters to DRAM regardless of whether pin state changes) and * raise the POSTTRIGGERED flag. - * - * Then switch the hardware from DRAM write (data acquisition) - * to DRAM read (sample memory download). */ - modestatus = WMR_FORCESTOP | WMR_SDRAMWRITEEN; - ret = sigma_set_register(devc, WRITE_MODE, modestatus); - if (ret != SR_OK) - return ret; - do { - ret = sigma_get_register(devc, READ_MODE, &modestatus); - if (ret != SR_OK) { - sr_err("Could not poll for post-trigger state."); + ret = sigma_get_register(devc, READ_MODE, &modestatus); + if (ret != SR_OK) { + sr_err("Could not determine current device state."); + return FALSE; + } + if (!(modestatus & RMR_POSTTRIGGERED)) { + sr_info("Downloading sample data."); + devc->state = SIGMA_DOWNLOAD; + + modestatus = WMR_FORCESTOP | WMR_SDRAMWRITEEN; + ret = sigma_set_register(devc, WRITE_MODE, modestatus); + if (ret != SR_OK) return FALSE; - } - } while (!(modestatus & RMR_POSTTRIGGERED)); - ret = sigma_set_register(devc, WRITE_MODE, WMR_SDRAMREADEN); - if (ret != SR_OK) - return ret; + do { + ret = sigma_get_register(devc, READ_MODE, &modestatus); + if (ret != SR_OK) { + sr_err("Could not poll for post-trigger state."); + return FALSE; + } + } while (!(modestatus & RMR_POSTTRIGGERED)); + } /* + * Switch the hardware from DRAM write (data acquisition) to + * DRAM read (sample memory download). Prepare resources for + * sample memory content retrieval. Should execute exactly once, + * then keep finding its condition met. + * * Get the current positions (acquisition write pointer, and * trigger match location). With disabled triggers, use a value * for the location that will never match during interpretation. + * Determine which area of the sample memory to retrieve, + * allocate a receive buffer, and setup counters/pointers. */ - ret = sigma_read_pos(devc, &stoppos, &triggerpos, &modestatus); - if (ret != SR_OK) { - sr_err("Could not query capture positions/state."); - return FALSE; + if (!interp->fetch.lines_per_read) { + ret = sigma_set_register(devc, WRITE_MODE, WMR_SDRAMREADEN); + if (ret != SR_OK) + return FALSE; + + ret = sigma_read_pos(devc, &stoppos, &triggerpos, &modestatus); + if (ret != SR_OK) { + sr_err("Could not query capture positions/state."); + return FALSE; + } + if (!devc->use_triggers) + triggerpos = ~0; + if (!(modestatus & RMR_TRIGGERED)) + triggerpos = ~0; + + ret = alloc_sample_buffer(devc, stoppos, triggerpos, modestatus); + if (ret != SR_OK) + return FALSE; + + ret = alloc_submit_buffer(sdi); + if (ret != SR_OK) + return FALSE; + ret = setup_submit_limit(devc); + if (ret != SR_OK) + return FALSE; } - if (!devc->use_triggers) - triggerpos = ~0; - if (!(modestatus & RMR_TRIGGERED)) - triggerpos = ~0; /* - * Determine which area of the sample memory to retrieve, - * allocate a receive buffer, and setup counters/pointers. + * Get another set of sample memory rows, and interpret its + * content. Will execute as many times as it takes to complete + * the memory region that the recent acquisition spans. + * + * The size of a receive call's workload and the main loop's + * receive call poll period determine the UI responsiveness and + * the overall transfer time for the sample memory content. */ - ret = alloc_sample_buffer(devc, stoppos, triggerpos, modestatus); - if (ret != SR_OK) - return FALSE; - - ret = alloc_submit_buffer(sdi); - if (ret != SR_OK) - return FALSE; - ret = setup_submit_limit(devc); - if (ret != SR_OK) - return FALSE; + chunks_per_receive_call = 50; while (interp->fetch.lines_done < interp->fetch.lines_total) { - size_t dl_events_in_line, trigger_event; + size_t dl_events_in_line; /* Read another chunk of sample memory (several lines). */ ret = fetch_sample_buffer(devc); @@ -1823,26 +2064,40 @@ static int download_capture(struct sr_dev_inst *sdi) if (interp->iter.line == interp->stop.line) { dl_events_in_line = interp->stop.raw & ROW_MASK; } - trigger_event = ~UINT64_C(0); - if (interp->iter.line == interp->trig.line) { - trigger_event = interp->trig.raw & ROW_MASK; - } decode_chunk_ts(devc, interp->fetch.curr_line, - dl_events_in_line, trigger_event); + dl_events_in_line); interp->fetch.curr_line++; interp->fetch.lines_done++; - interp->iter.line++; - interp->iter.line %= ROW_COUNT; + } + + /* Keep returning to application code for large data sets. */ + if (!--chunks_per_receive_call) { + ret = flush_submit_buffer(devc); + if (ret != SR_OK) + return FALSE; + break; } } - flush_submit_buffer(devc); - free_submit_buffer(devc); - free_sample_buffer(devc); - std_session_send_df_end(sdi); + /* + * Release previously allocated resources, and adjust state when + * all of the sample memory was retrieved, and interpretation has + * completed. Should execute exactly once. + */ + if (interp->fetch.lines_done >= interp->fetch.lines_total) { + ret = flush_submit_buffer(devc); + if (ret != SR_OK) + return FALSE; + free_submit_buffer(devc); + free_sample_buffer(devc); - devc->state = SIGMA_IDLE; - sr_dev_acquisition_stop(sdi); + ret = std_session_send_df_end(sdi); + if (ret != SR_OK) + return FALSE; + + devc->state = SIGMA_IDLE; + sr_dev_acquisition_stop(sdi); + } return TRUE; } @@ -1855,10 +2110,59 @@ static int download_capture(struct sr_dev_inst *sdi) static int sigma_capture_mode(struct sr_dev_inst *sdi) { struct dev_context *devc; + int ret; + uint32_t stoppos, triggerpos; + uint8_t mode; + gboolean full, wrapped, triggered, complete; devc = sdi->priv; + + /* + * Get and interpret current acquisition status. Some of these + * thresholds are rather arbitrary. + */ + ret = sigma_read_pos(devc, &stoppos, &triggerpos, &mode); + if (ret != SR_OK) + return FALSE; + stoppos >>= ROW_SHIFT; + full = stoppos >= ROW_COUNT - 2; + wrapped = mode & RMR_ROUND; + triggered = mode & RMR_TRIGGERED; + complete = mode & RMR_POSTTRIGGERED; + + /* + * Acquisition completed in the hardware? Start or continue + * sample memory content download. + * (Can user initiated button presses result in auto stop? + * Will they "trigger", and later result in expired time limit + * of post trigger conditions?) + */ + if (complete) + return download_capture(sdi); + + /* + * Previously configured acquisition period exceeded? Start + * sample download. Start the timeout period late when triggers + * are used (unknown period from acquisition start to trigger + * match). + */ if (sr_sw_limits_check(&devc->limit.acquire)) return download_capture(sdi); + if (devc->late_trigger_timeout && triggered) { + sr_sw_limits_acquisition_start(&devc->limit.acquire); + devc->late_trigger_timeout = FALSE; + } + + /* + * No trigger specified, and sample memory exhausted? Start + * download (may otherwise keep acquiring, even for infinite + * amounts of time without a user specified time/count limit). + * This handles situations when users specify limits which + * exceed the device's capabilities. + */ + (void)full; + if (!devc->use_triggers && wrapped) + return download_capture(sdi); return TRUE; } @@ -1879,12 +2183,15 @@ SR_PRIV int sigma_receive_data(int fd, int revents, void *cb_data) /* * When the application has requested to stop the acquisition, - * then immediately start downloading sample data. Otherwise + * then immediately start downloading sample data. Continue a + * previously initiated download until completion. Otherwise * keep checking configured limits which will terminate the * acquisition and initiate download. */ if (devc->state == SIGMA_STOPPING) return download_capture(sdi); + if (devc->state == SIGMA_DOWNLOAD) + return download_capture(sdi); if (devc->state == SIGMA_CAPTURE) return sigma_capture_mode(sdi);