]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/asix-sigma/protocol.c
asix-sigma: re-enable software check for exact trigger position
[libsigrok.git] / src / hardware / asix-sigma / protocol.c
index ce6974c6ec38a767f6e61bd9766ddbd2ca5837ee..2c00d74dd266612e0d4fcf882f7efc4c81af8164 100644 (file)
@@ -1255,7 +1255,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 +1272,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 +1290,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 +1415,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;
@@ -1490,77 +1586,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 +1659,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".
  */
@@ -1652,7 +1781,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 +1797,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;
 }
 
 /*
@@ -1831,8 +1968,6 @@ static int download_capture(struct sr_dev_inst *sdi)
                                dl_events_in_line, trigger_event);
                        interp->fetch.curr_line++;
                        interp->fetch.lines_done++;
-                       interp->iter.line++;
-                       interp->iter.line %= ROW_COUNT;
                }
        }
        flush_submit_buffer(devc);