]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/asix-sigma/protocol.c
asix-sigma: complete and extend capture mode supervision
[libsigrok.git] / src / hardware / asix-sigma / protocol.c
index 8491d7431af8213616506634cba4f0fed159d2c3..5755e77f821f37197dd8db438fd86a0d9de78798 100644 (file)
@@ -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;
        }
 
@@ -1423,6 +1437,7 @@ static int alloc_sample_buffer(struct dev_context *devc,
         * 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));
@@ -1493,6 +1508,7 @@ 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;
 }
 
 /*
@@ -1585,61 +1601,44 @@ 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)
-{
-       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;
-
-               break;
-       }
-
-       /* If we did not match, return original trigger pos. */
-       return i & 0x7;
-}
-
 static gboolean sample_matches_trigger(struct dev_context *devc, uint16_t sample)
 {
-       /* 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.
+       struct sigma_sample_interp *interp;
+       uint16_t last_sample;
+       struct sigma_trigger *t;
+       gboolean simple_match, rising_match, falling_match;
+       gboolean matched;
+
+       /*
+        * 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;
 
-       (void)sample;
-       (void)get_trigger_offset;
-
-       return FALSE;
+       /*
+        * 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 int send_trigger_marker(struct dev_context *devc)
@@ -1657,21 +1656,16 @@ static int send_trigger_marker(struct dev_context *devc)
 }
 
 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;
 
-       /*
-        * Ignore the condition provided by the "inner loop" logic of
-        * sample memory iteration. Instead use device context status
-        * for the period with software trigger match checks.
-        */
-       check_trigger = devc->interp.trig_chk.armed;
-
-       triggered = check_trigger && sample_matches_trigger(devc, sample);
-       if (triggered)
+       triggered = sample_matches_trigger(devc, sample);
+       if (triggered) {
                send_trigger_marker(devc);
+               devc->interp.trig_chk.matched = TRUE;
+       }
 
        ret = addto_submit_buffer(devc, sample, count);
        if (ret != SR_OK)
@@ -1778,15 +1772,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
@@ -1802,7 +1793,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;
 
@@ -1818,27 +1809,27 @@ 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);
@@ -1857,30 +1848,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];
@@ -1894,7 +1872,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;
@@ -1907,68 +1885,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);
@@ -1981,24 +1986,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++;
                }
+
+               /* 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;
 }
@@ -2011,10 +2032,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;
 }
@@ -2035,12 +2105,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);