]> sigrok.org Git - libsigrok.git/blobdiff - hardware/sysclk-lwla/protocol.c
sysclk-lwla: Add support for external trigger input.
[libsigrok.git] / hardware / sysclk-lwla / protocol.c
index 65908f99ae7d5723c86f08177146c1e30582dbbd..c4d99d82ab0d10c8c64b866b7cb9f4038373e564 100644 (file)
@@ -20,9 +20,6 @@
 #include "protocol.h"
 #include <string.h>
 
-/* Bit mask covering all 34 channels. */
-#define ALL_CHANNELS_MASK (((uint64_t)1 << NUM_PROBES) - 1)
-
 /* Bit mask for the RLE repeat-count-follows flag. */
 #define RLE_FLAG_LEN_FOLLOWS ((uint64_t)1 << 35)
 
 
 /* The bitstream filenames are indexed by the clock source enumeration.
  */
-static const char *const bitstream_map[] = {
-       FIRMWARE_DIR "/sysclk-lwla1034-off.bitstream",
-       FIRMWARE_DIR "/sysclk-lwla1034-int.bitstream",
-       FIRMWARE_DIR "/sysclk-lwla1034-extpos.bitstream",
-       FIRMWARE_DIR "/sysclk-lwla1034-extneg.bitstream",
+static const char bitstream_map[][32] = {
+       "sysclk-lwla1034-off.rbf",
+       "sysclk-lwla1034-int.rbf",
+       "sysclk-lwla1034-extpos.rbf",
+       "sysclk-lwla1034-extneg.rbf",
 };
 
 /* Submit an already filled-in USB transfer.
@@ -64,11 +61,14 @@ static int submit_transfer(struct dev_context *devc,
 static int capture_setup(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
+       struct acquisition_state *acq;
        uint64_t divider_count;
+       uint64_t trigger_mask;
        uint64_t memory_limit;
        uint16_t command[3 + 10*4];
 
        devc = sdi->priv;
+       acq  = devc->acquisition;
 
        command[0] = LWLA_WORD(CMD_CAP_SETUP);
        command[1] = LWLA_WORD(0); /* address */
@@ -83,7 +83,7 @@ static int capture_setup(const struct sr_dev_inst *sdi)
         * 100 MHz. At the highest samplerate of 125 MHz the clock divider
         * is bypassed.
         */
-       if (devc->samplerate > 0 && devc->samplerate < SR_MHZ(100))
+       if (!acq->bypass_clockdiv && devc->samplerate > 0)
                divider_count = SR_MHZ(100) / devc->samplerate - 1;
        else
                divider_count = 0;
@@ -103,10 +103,18 @@ static int capture_setup(const struct sr_dev_inst *sdi)
        command[17] = LWLA_WORD_2(devc->trigger_edge_mask);
        command[18] = LWLA_WORD_3(devc->trigger_edge_mask);
 
-       command[19] = LWLA_WORD_0(devc->trigger_mask);
-       command[20] = LWLA_WORD_1(devc->trigger_mask);
-       command[21] = LWLA_WORD_2(devc->trigger_mask);
-       command[22] = LWLA_WORD_3(devc->trigger_mask);
+       trigger_mask = devc->trigger_mask;
+       /* Set bits to select external TRG input edge. */
+       if (devc->cfg_trigger_source == TRIGGER_EXT_TRG)
+               switch (devc->cfg_trigger_slope) {
+               case SLOPE_POSITIVE: trigger_mask |= (uint64_t)1 << 35; break; 
+               case SLOPE_NEGATIVE: trigger_mask |= (uint64_t)1 << 34; break; 
+               }
+
+       command[19] = LWLA_WORD_0(trigger_mask);
+       command[20] = LWLA_WORD_1(trigger_mask);
+       command[21] = LWLA_WORD_2(trigger_mask);
+       command[22] = LWLA_WORD_3(trigger_mask);
 
        /* Set the capture memory full threshold. This is slightly less
         * than the actual maximum, most likely in order to compensate for
@@ -229,16 +237,15 @@ static void issue_read_start(const struct sr_dev_inst *sdi)
        acq->sample  = 0;
        acq->run_len = 0;
 
-       acq->captured_samples    = 0;
-       acq->transferred_samples = 0;
+       acq->samples_done = 0;
 
        /* For some reason, the start address is 4 rather than 0. */
        acq->mem_addr_done = 4;
        acq->mem_addr_next = 4;
        acq->mem_addr_stop = acq->mem_addr_fill;
 
-       /* Byte offset into the packet output buffer. */
-       acq->out_offset = 0;
+       /* Sample position in the packet output buffer. */
+       acq->out_index = 0;
 
        regvals = devc->reg_write_seq;
 
@@ -258,6 +265,10 @@ static void issue_read_start(const struct sr_dev_inst *sdi)
                devc->state = STATE_READ_PREPARE;
 }
 
+/* Issue a command as an asynchronous USB transfer which returns the device
+ * to normal state after a read operation.  Sets a new device context state
+ * on success.
+ */
 static void issue_read_end(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -285,10 +296,9 @@ static void process_capture_length(const struct sr_dev_inst *sdi)
                devc->transfer_error = TRUE;
                return;
        }
-       acq->mem_addr_fill = LWLA_READ32(acq->xfer_buf_in);
+       acq->mem_addr_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
 
-       sr_dbg("%lu words in capture buffer.",
-              (unsigned long)acq->mem_addr_fill);
+       sr_dbg("%zu words in capture buffer.", acq->mem_addr_fill);
 
        if (acq->mem_addr_fill > 0 && sdi->status == SR_ST_ACTIVE)
                issue_read_start(sdi);
@@ -342,9 +352,10 @@ static void issue_stop_capture(const struct sr_dev_inst *sdi)
 static void process_capture_status(const struct sr_dev_inst *sdi)
 {
        uint64_t duration;
-       uint64_t timescale;
        struct dev_context *devc;
        struct acquisition_state *acq;
+       unsigned int mem_fill;
+       unsigned int flags;
 
        devc = sdi->priv;
        acq  = devc->acquisition;
@@ -360,22 +371,32 @@ static void process_capture_status(const struct sr_dev_inst *sdi)
         * in the FPGA.  These fields are definitely less than 64 bit wide
         * internally, and the unused bits occasionally even contain garbage.
         */
-       acq->mem_addr_fill = LWLA_READ32(&acq->xfer_buf_in[0]);
-       duration           = LWLA_READ32(&acq->xfer_buf_in[8]);
-       acq->capture_flags = LWLA_READ32(&acq->xfer_buf_in[16])
-                               & STATUS_FLAG_MASK;
-
-       /* The 125 MHz setting is special, and uses the same timebase
-        * for the duration field as the 100 MHz setting.
+       mem_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
+       duration = LWLA_TO_UINT32(acq->xfer_buf_in[4]);
+       flags    = LWLA_TO_UINT32(acq->xfer_buf_in[8]) & STATUS_FLAG_MASK;
+
+       /* The LWLA1034 runs at 125 MHz if the clock divider is bypassed.
+        * However, the time base used for the duration is apparently not
+        * adjusted for this "boost" mode.  Whereas normally the duration
+        * unit is 1 ms, it is 0.8 ms when the clock divider is bypassed.
+        * As 0.8 = 100 MHz / 125 MHz, it seems that the internal cycle
+        * counter period is the same as at the 100 MHz setting.
         */
-       timescale = MIN(devc->samplerate, SR_MHZ(100));
-       acq->captured_samples = (duration * timescale) / 1000;
+       if (acq->bypass_clockdiv)
+               acq->duration_now = duration * 4 / 5;
+       else
+               acq->duration_now = duration;
 
-       sr_spew("Captured %lu words, %" PRIu64 " samples, flags 0x%02X",
-               (unsigned long)acq->mem_addr_fill,
-               acq->captured_samples, acq->capture_flags);
+       sr_spew("Captured %u words, %" PRIu64 " ms, flags 0x%02X.",
+               mem_fill, acq->duration_now, flags);
 
-       if (acq->captured_samples >= devc->limit_samples) {
+       if ((flags & STATUS_TRIGGERED) > (acq->capture_flags & STATUS_TRIGGERED))
+               sr_info("Capture triggered.");
+
+       acq->capture_flags = flags;
+
+       if (acq->duration_now >= acq->duration_max) {
+               sr_dbg("Time limit reached, stopping capture.");
                issue_stop_capture(sdi);
                return;
        }
@@ -425,49 +446,6 @@ static void request_read_mem(const struct sr_dev_inst *sdi)
        }
 }
 
-/* Send a packet of logic samples to the session bus.  The payload is taken
- * from the acquisition state.  The return value indicates whether to stop
- * reading more samples.
- */
-static gboolean send_logic_packet(const struct sr_dev_inst *sdi)
-{
-       uint64_t samples;
-       struct dev_context *devc;
-       struct acquisition_state *acq;
-       struct sr_datafeed_packet packet;
-       struct sr_datafeed_logic  logic;
-       int last;
-
-       devc = sdi->priv;
-       acq  = devc->acquisition;
-
-       if (acq->transferred_samples >= devc->limit_samples)
-               return TRUE;
-
-       packet.type    = SR_DF_LOGIC;
-       packet.payload = &logic;
-       logic.unitsize = UNIT_SIZE;
-       logic.data     = acq->out_packet;
-       logic.length   = acq->out_offset;
-
-       samples = acq->out_offset / UNIT_SIZE;
-       last = FALSE;
-
-       /* Cut the packet short if necessary. */
-       if (acq->transferred_samples + samples >= devc->limit_samples) {
-               samples = devc->limit_samples - acq->transferred_samples;
-               logic.length = samples * UNIT_SIZE;
-               last = TRUE;
-       }
-       acq->transferred_samples += samples;
-       acq->out_offset = 0;
-
-       /* Send off logic datafeed packet. */
-       sr_session_send(sdi, &packet);
-
-       return last;
-}
-
 /* Demangle and decompress incoming sample data from the capture buffer.
  * The data chunk is taken from the acquisition state, and is expected to
  * contain a multiple of 8 device words.
@@ -478,15 +456,19 @@ static gboolean send_logic_packet(const struct sr_dev_inst *sdi)
 static int process_sample_data(const struct sr_dev_inst *sdi)
 {
        uint64_t sample;
-       uint64_t run_len;
        uint64_t high_nibbles;
        uint64_t word;
        struct dev_context *devc;
        struct acquisition_state *acq;
        uint8_t *out_p;
-       uint16_t *slice;
+       uint32_t *slice;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
        size_t expect_len;
        size_t actual_len;
+       size_t out_max_samples;
+       size_t out_run_samples;
+       size_t ri;
        size_t in_words_left;
        size_t si;
 
@@ -494,49 +476,71 @@ static int process_sample_data(const struct sr_dev_inst *sdi)
        acq  = devc->acquisition;
 
        if (acq->mem_addr_done >= acq->mem_addr_stop
-                       || acq->transferred_samples >= devc->limit_samples)
+                       || acq->samples_done >= acq->samples_max)
                return SR_OK;
 
        in_words_left = MIN(acq->mem_addr_stop - acq->mem_addr_done,
                            READ_CHUNK_LEN);
-       expect_len = LWLA1034_MEMBUF_LEN(in_words_left) * sizeof(uint16_t);
+       expect_len = LWLA1034_MEMBUF_LEN(in_words_left) * sizeof(uint32_t);
        actual_len = acq->xfer_in->actual_length;
 
        if (actual_len != expect_len) {
-               sr_err("Received size %lu does not match expected size %lu.",
-                      (unsigned long)actual_len, (unsigned long)expect_len);
+               sr_err("Received size %zu does not match expected size %zu.",
+                      actual_len, expect_len);
                devc->transfer_error = TRUE;
                return SR_ERR;
        }
        acq->mem_addr_done += in_words_left;
+
+       /* Prepare session packet. */
+       packet.type    = SR_DF_LOGIC;
+       packet.payload = &logic;
+       logic.unitsize = UNIT_SIZE;
+       logic.data     = acq->out_packet;
+
        slice = acq->xfer_buf_in;
        si = 0; /* word index within slice */
 
        for (;;) {
-               sample = acq->sample;
+               /* Calculate number of samples to write into packet. */
+               out_max_samples = MIN(acq->samples_max - acq->samples_done,
+                                     PACKET_LENGTH - acq->out_index);
+               out_run_samples = MIN(acq->run_len, out_max_samples);
+
                /* Expand run-length samples into session packet. */
-               for (run_len = acq->run_len; run_len > 0; --run_len) {
-                       out_p = &acq->out_packet[acq->out_offset];
+               sample = acq->sample;
+               out_p = &acq->out_packet[acq->out_index * UNIT_SIZE];
+
+               for (ri = 0; ri < out_run_samples; ++ri) {
                        out_p[0] =  sample        & 0xFF;
                        out_p[1] = (sample >>  8) & 0xFF;
                        out_p[2] = (sample >> 16) & 0xFF;
                        out_p[3] = (sample >> 24) & 0xFF;
                        out_p[4] = (sample >> 32) & 0xFF;
-                       acq->out_offset += UNIT_SIZE;
-
-                       /* Send out packet if it is full. */
-                       if (acq->out_offset > PACKET_SIZE - UNIT_SIZE)
-                               if (send_logic_packet(sdi))
-                                       return SR_OK; /* sample limit reached */
+                       out_p += UNIT_SIZE;
+               }
+               acq->run_len -= out_run_samples;
+               acq->out_index += out_run_samples;
+               acq->samples_done += out_run_samples;
+
+               /* Packet full or sample count limit reached? */
+               if (out_run_samples == out_max_samples) {
+                       logic.length = acq->out_index * UNIT_SIZE;
+                       sr_session_send(sdi, &packet);
+                       acq->out_index = 0;
+
+                       if (acq->samples_done >= acq->samples_max)
+                               return SR_OK; /* sample limit reached */
+                       if (acq->run_len > 0)
+                               continue; /* need another packet */
                }
-               acq->run_len = 0;
 
                if (in_words_left == 0)
                        break; /* done with current chunk */
 
                /* Now work on the current slice. */
-               high_nibbles = LWLA_READ32(&slice[8 * 2]);
-               word = LWLA_READ32(&slice[si * 2]);
+               high_nibbles = LWLA_TO_UINT32(slice[8]);
+               word = LWLA_TO_UINT32(slice[si]);
                word |= (high_nibbles << (4 * si + 4)) & ((uint64_t)0xF << 32);
 
                if (acq->rle == RLE_STATE_DATA) {
@@ -550,17 +554,18 @@ static int process_sample_data(const struct sr_dev_inst *sdi)
                }
 
                /* Move to next word. */
-               if (++si >= 8) {
-                       si = 0;
-                       slice += 9 * 2;
-               }
+               si = (si + 1) % 8;
+               if (si == 0)
+                       slice += 9;
                --in_words_left;
        }
 
-       /* Send out partially filled packet if it is the last one. */
-       if (acq->mem_addr_done >= acq->mem_addr_stop && acq->out_offset > 0)
-               send_logic_packet(sdi);
-
+       /* Send out partially filled packet if this was the last chunk. */
+       if (acq->mem_addr_done >= acq->mem_addr_stop && acq->out_index > 0) {
+               logic.length = acq->out_index * UNIT_SIZE;
+               sr_session_send(sdi, &packet);
+               acq->out_index = 0;
+       }
        return SR_OK;
 }
 
@@ -675,7 +680,7 @@ static void receive_transfer_in(struct libusb_transfer *transfer)
        case STATE_READ_RESPONSE:
                if (process_sample_data(sdi) == SR_OK
                                && acq->mem_addr_next < acq->mem_addr_stop
-                               && acq->transferred_samples < devc->limit_samples)
+                               && acq->samples_done < acq->samples_max)
                        request_read_mem(sdi);
                else
                        issue_read_end(sdi);
@@ -716,21 +721,21 @@ SR_PRIV int lwla_init_device(const struct sr_dev_inst *sdi)
        ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL1, &value);
        if (ret != SR_OK)
                return ret;
-       sr_info("Received test word 0x%08X back.", value);
+       sr_dbg("Received test word 0x%08X back.", value);
        if (value != 0x12345678)
                return SR_ERR;
 
        ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL4, &value);
        if (ret != SR_OK)
                return ret;
-       sr_info("Received test word 0x%08X back.", value);
+       sr_dbg("Received test word 0x%08X back.", value);
        if (value != 0x12345678)
                return SR_ERR;
 
        ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL3, &value);
        if (ret != SR_OK)
                return ret;
-       sr_info("Received test word 0x%08X back.", value);
+       sr_dbg("Received test word 0x%08X back.", value);
        if (value != 0x87654321)
                return SR_ERR;
 
@@ -771,11 +776,57 @@ SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
+       struct acquisition_state *acq;
        struct regval_pair regvals[7];
        int ret;
 
        devc = sdi->priv;
        usb  = sdi->conn;
+       acq  = devc->acquisition;
+
+       if (devc->limit_msec > 0) {
+               acq->duration_max = devc->limit_msec;
+               sr_info("Acquisition time limit %" PRIu64 " ms.",
+                       devc->limit_msec);
+       } else
+               acq->duration_max = MAX_LIMIT_MSEC;
+
+       if (devc->limit_samples > 0) {
+               acq->samples_max = devc->limit_samples;
+               sr_info("Acquisition sample count limit %" PRIu64 ".",
+                       devc->limit_samples);
+       } else
+               acq->samples_max = MAX_LIMIT_SAMPLES;
+
+       switch (devc->cur_clock_source) {
+       case CLOCK_SOURCE_INT:
+               sr_info("Internal clock, samplerate %" PRIu64 ".",
+                       devc->samplerate);
+               if (devc->samplerate == 0)
+                       return SR_ERR_BUG;
+               /* At 125 MHz, the clock divider is bypassed. */
+               acq->bypass_clockdiv = (devc->samplerate > SR_MHZ(100));
+
+               /* If only one of the limits is set, derive the other one. */
+               if (devc->limit_msec == 0 && devc->limit_samples > 0)
+                       acq->duration_max = devc->limit_samples
+                                       * 1000 / devc->samplerate + 1;
+               else if (devc->limit_samples == 0 && devc->limit_msec > 0)
+                       acq->samples_max = devc->limit_msec
+                                       * devc->samplerate / 1000;
+               break;
+       case CLOCK_SOURCE_EXT_FALL:
+               sr_info("External clock, falling edge.");
+               acq->bypass_clockdiv = TRUE;
+               break;
+       case CLOCK_SOURCE_EXT_RISE:
+               sr_info("External clock, rising edge.");
+               acq->bypass_clockdiv = TRUE;
+               break;
+       default:
+               sr_err("No valid clock source has been configured.");
+               return SR_ERR;
+       }
 
        regvals[0].reg = REG_MEM_CTRL2;
        regvals[0].val = 2;
@@ -796,7 +847,7 @@ SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi)
        regvals[5].val = 0;
 
        regvals[6].reg = REG_DIV_BYPASS;
-       regvals[6].val = (devc->samplerate > SR_MHZ(100)) ? 1 : 0;
+       regvals[6].val = acq->bypass_clockdiv;
 
        ret = lwla_write_regs(usb, regvals, G_N_ELEMENTS(regvals));
        if (ret != SR_OK)
@@ -820,6 +871,10 @@ SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi)
        usb  = sdi->conn;
        acq  = devc->acquisition;
 
+       acq->duration_now  = 0;
+       acq->mem_addr_fill = 0;
+       acq->capture_flags = 0;
+
        libusb_fill_bulk_transfer(acq->xfer_out, usb->devhdl, EP_COMMAND,
                                  (unsigned char *)acq->xfer_buf_out, 0,
                                  &receive_transfer_out,