]> sigrok.org Git - libsigrok.git/commitdiff
sysclk-lwla: Work around short transfer quirk
authorDaniel Elstner <redacted>
Sat, 5 Dec 2015 16:47:52 +0000 (17:47 +0100)
committerDaniel Elstner <redacted>
Tue, 22 Dec 2015 15:09:39 +0000 (16:09 +0100)
Detect whether the FX2 firmware of the LWLA device exhibits the
short transfer bug. If so, work around the problem by limiting
reads to at most 64 bytes at a time. This slows down the memory
read after acquisition quite noticably, but makes the device
usable even in adverse conditions.

src/hardware/sysclk-lwla/api.c
src/hardware/sysclk-lwla/lwla.c
src/hardware/sysclk-lwla/lwla.h
src/hardware/sysclk-lwla/lwla1016.c
src/hardware/sysclk-lwla/lwla1034.c
src/hardware/sysclk-lwla/protocol.h

index 891062560b0d5a09e86d5c24928a38dec6441ce3..36c9ee608bfc7dc05796437cae9728707ee8e023 100644 (file)
@@ -329,10 +329,13 @@ static int dev_open(struct sr_dev_inst *sdi)
                sr_usb_close(usb);
                return ret;
        }
+       /* This delay appears to be necessary for reliable operation. */
+       g_usleep(30 * 1000);
 
        sdi->status = SR_ST_ACTIVE;
 
        devc->active_fpga_config = FPGA_NOCONF;
+       devc->short_transfer_quirk = FALSE;
        devc->state = STATE_IDLE;
 
        ret = (*devc->model->apply_fpga_config)(sdi);
@@ -343,8 +346,12 @@ static int dev_open(struct sr_dev_inst *sdi)
        if (ret != SR_OK) {
                sdi->status = SR_ST_INACTIVE;
                sr_usb_close(usb);
+               return ret;
        }
-       return ret;
+       if (devc->short_transfer_quirk)
+               sr_warn("Short transfer quirk detected! "
+                       "Memory reads will be slow.");
+       return SR_OK;
 }
 
 /* Shutdown and close device.
index c884885cd918127e416fa84d84856807b2908046..6a0b5606544136ae6e2ebb0981924dcbd6844df0 100644 (file)
@@ -144,33 +144,26 @@ SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
 }
 
 SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
-                              uint32_t *reply, int reply_len, int expect_len)
+                              void *reply, int buf_size, int *xfer_len)
 {
        int ret;
-       int xfer_len;
 
-       if (!usb || !reply || reply_len <= 0)
+       if (!usb || !reply || buf_size <= 0)
                return SR_ERR_BUG;
 
-       xfer_len = 0;
-       ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY,
-                                  (unsigned char *)reply, reply_len * 4,
-                                  &xfer_len, USB_TIMEOUT_MS);
+       ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY, reply, buf_size,
+                                  xfer_len, USB_TIMEOUT_MS);
        if (ret != 0) {
                sr_dbg("Failed to receive reply: %s.", libusb_error_name(ret));
                return SR_ERR;
        }
-       if (xfer_len != expect_len * 4) {
-               sr_dbg("Failed to receive reply: incorrect length %d != %d.",
-                      xfer_len, expect_len * 4);
-               return SR_ERR;
-       }
        return SR_OK;
 }
 
 SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
                          uint16_t reg, uint32_t *value)
 {
+       int xfer_len;
        int ret;
        uint16_t command[2];
        uint32_t reply[128]; /* full EP buffer to avoid overflows */
@@ -179,16 +172,21 @@ SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
        command[1] = LWLA_WORD(reg);
 
        ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
-
        if (ret != SR_OK)
                return ret;
 
-       ret = lwla_receive_reply(usb, reply, ARRAY_SIZE(reply), 1);
+       ret = lwla_receive_reply(usb, reply, sizeof(reply), &xfer_len);
+       if (ret != SR_OK)
+               return ret;
 
-       if (ret == SR_OK)
-               *value = LWLA_TO_UINT32(reply[0]);
+       if (xfer_len != 4) {
+               sr_dbg("Invalid register read response of length %d.",
+                      xfer_len);
+               return SR_ERR;
+       }
+       *value = LWLA_TO_UINT32(reply[0]);
 
-       return ret;
+       return SR_OK;
 }
 
 SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
index 7d95f9c1f447e33f932a18fa3dda36a95c9d8357..903e278dc76cf1c7ccb03c0a263d44a06ef220d5 100644 (file)
@@ -154,7 +154,7 @@ SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
                              const uint16_t *command, int cmd_len);
 
 SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
-                              uint32_t *reply, int reply_len, int expect_len);
+                              void *reply, int buf_size, int *xfer_len);
 
 SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
                          uint16_t reg, uint32_t *value);
index 4c8bbfb5b7049d29451674d227624dba97a5ec33..33fd7e28988fda27f3eff7cd22e48e23d45faa27 100644 (file)
 
 /* Capture memory read start address.
  */
-#define READ_START_ADDR                2
+#define READ_START_ADDR        2
 
 /* Number of device memory units (32 bit) to read at a time.
  */
-#define READ_CHUNK_LEN32       250
+#define READ_CHUNK_LEN 250
 
 /** LWLA1016 register addresses.
  */
@@ -182,6 +182,58 @@ static void read_response_rle(struct acquisition_state *acq)
        acq->mem_addr_done += wi;
 }
 
+/* Check whether we can receive responses of more than 64 bytes.
+ * The FX2 firmware of the LWLA1016 has a bug in the reset logic which
+ * sometimes causes the response endpoint to be limited to transfers of
+ * 64 bytes at a time, instead of the expected 2*512 bytes. The problem
+ * can be worked around by never requesting more than 64 bytes.
+ * This quirk manifests itself only under certain conditions, and some
+ * users seem to see it more frequently than others. Detect it here in
+ * order to avoid paying the penalty unnecessarily.
+ */
+static int test_read_memory(const struct sr_dev_inst *sdi,
+                           unsigned int start, unsigned int count)
+{
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       unsigned int i;
+       int xfer_len;
+       int ret;
+       uint16_t command[5];
+       unsigned char reply[512];
+
+       devc = sdi->priv;
+       usb  = sdi->conn;
+
+       command[0] = LWLA_WORD(CMD_READ_MEM32);
+       command[1] = LWLA_WORD_0(start);
+       command[2] = LWLA_WORD_1(start);
+       command[3] = LWLA_WORD_0(count);
+       command[4] = LWLA_WORD_1(count);
+
+       ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+       if (ret != SR_OK)
+               return ret;
+
+       ret = lwla_receive_reply(usb, reply, sizeof(reply), &xfer_len);
+       if (ret != SR_OK)
+               return ret;
+
+       devc->short_transfer_quirk = (xfer_len == 64);
+
+       for (i = xfer_len; i < 4 * count && xfer_len == 64; i += xfer_len) {
+               ret = lwla_receive_reply(usb, reply, sizeof(reply), &xfer_len);
+               if (ret != SR_OK)
+                       return ret;
+       }
+       if (i != 4 * count) {
+               sr_err("Invalid read response of unexpected length %d.",
+                      xfer_len);
+               return SR_ERR;
+       }
+       return SR_OK;
+}
+
 /* Select and transfer FPGA bitstream for the current configuration.
  */
 static int apply_fpga_config(const struct sr_dev_inst *sdi)
@@ -213,9 +265,15 @@ static int apply_fpga_config(const struct sr_dev_inst *sdi)
  */
 static int device_init_check(const struct sr_dev_inst *sdi)
 {
+       static const struct regval mem_reset[] = {
+               {REG_MEM_CTRL, MEM_CTRL_RESET},
+               {REG_MEM_CTRL, 0},
+       };
        uint32_t value;
        int ret;
 
+       const unsigned int test_count = 24;
+
        ret = lwla_read_reg(sdi->conn, REG_TEST_ID, &value);
        if (ret != SR_OK)
                return ret;
@@ -229,7 +287,19 @@ static int device_init_check(const struct sr_dev_inst *sdi)
                sr_err("Received invalid test word 0x%08X.", value);
                return SR_ERR;
        }
-       return SR_OK;
+
+       ret = lwla_write_regs(sdi->conn, mem_reset, ARRAY_SIZE(mem_reset));
+       if (ret != SR_OK)
+               return ret;
+
+       ret = test_read_memory(sdi, 0, test_count);
+       if (ret != SR_OK)
+               return ret;
+       /*
+        * Issue another read request or the device will stall, for whatever
+        * reason. This happens both with and without the short transfer quirk.
+        */
+       return test_read_memory(sdi, test_count, test_count);
 }
 
 static int setup_acquisition(const struct sr_dev_inst *sdi)
@@ -285,7 +355,7 @@ static int prepare_request(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct acquisition_state *acq;
-       unsigned int count;
+       unsigned int chunk_len, count;
 
        devc = sdi->priv;
        acq  = devc->acquisition;
@@ -319,8 +389,10 @@ static int prepare_request(const struct sr_dev_inst *sdi)
                lwla_queue_regval(acq, REG_CAP_COUNT, 0);
                break;
        case STATE_READ_REQUEST:
-               count = MIN(READ_CHUNK_LEN32,
-                           acq->mem_addr_stop - acq->mem_addr_next);
+               /* Limit reads to 16 device words (64 bytes) at a time if the
+                * device firmware has the short transfer quirk. */
+               chunk_len = (devc->short_transfer_quirk) ? 16 : READ_CHUNK_LEN;
+               count = MIN(chunk_len, acq->mem_addr_stop - acq->mem_addr_next);
 
                acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_MEM32);
                acq->xfer_buf_out[1] = LWLA_WORD_0(acq->mem_addr_next);
index fdb54e620742d061a26fbf2f625585044f85157c..62de549a639b2bd4f2ccec66f14aa7a8fbbc7429 100644 (file)
@@ -39,7 +39,7 @@
 
 /* Capture memory read start address.
  */
-#define READ_START_ADDR                4
+#define READ_START_ADDR        4
 
 /* Number of device memory units (36 bit) to read at a time. Slices of 8
  * consecutive 36-bit words are mapped to 9 32-bit words each, so the chunk
@@ -54,7 +54,7 @@
  * a time. So far, it appears safe to increase this to 224 words (28 slices,
  * 1008 bytes), thus making the most of two 512 byte buffers.
  */
-#define READ_CHUNK_LEN36       (28 * 8)
+#define READ_CHUNK_LEN (28 * 8)
 
 /* Bit mask for the RLE repeat-count-follows flag.
  */
@@ -266,6 +266,60 @@ static void read_response(struct acquisition_state *acq)
        acq->mem_addr_done += wi;
 }
 
+/* Check whether we can receive responses of more than 64 bytes.
+ * The FX2 firmware of the LWLA1034 has a bug in the reset logic which
+ * sometimes causes the response endpoint to be limited to transfers of
+ * 64 bytes at a time, instead of the expected 2*512 bytes. The problem
+ * can be worked around by never requesting more than 64 bytes.
+ * This quirk manifests itself only under certain conditions, and some
+ * users seem to see it more frequently than others. Detect it here in
+ * order to avoid paying the penalty unnecessarily.
+ */
+static int detect_short_transfer_quirk(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       int xfer_len;
+       int ret;
+       uint16_t command[3];
+       unsigned char buf[512];
+
+       const int lreg_count = 10;
+
+       devc = sdi->priv;
+       usb  = sdi->conn;
+
+       command[0] = LWLA_WORD(CMD_READ_LREGS);
+       command[1] = LWLA_WORD(0);
+       command[2] = LWLA_WORD(lreg_count);
+
+       ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+       if (ret != SR_OK)
+               return ret;
+
+       ret = lwla_receive_reply(usb, buf, sizeof(buf), &xfer_len);
+       if (ret != SR_OK)
+               return ret;
+
+       devc->short_transfer_quirk = (xfer_len == 64);
+
+       if (xfer_len == 8 * lreg_count)
+               return SR_OK;
+
+       if (xfer_len == 64) {
+               /* Drain the tailing portion of the split transfer. */
+               ret = lwla_receive_reply(usb, buf, sizeof(buf), &xfer_len);
+               if (ret != SR_OK)
+                       return ret;
+
+               if (xfer_len == 8 * lreg_count - 64)
+                       return SR_OK;
+       }
+       sr_err("Received response of unexpected length %d.", xfer_len);
+
+       return SR_ERR;
+}
+
 /* Select and transfer FPGA bitstream for the current configuration.
  */
 static int apply_fpga_config(const struct sr_dev_inst *sdi)
@@ -317,7 +371,8 @@ static int device_init_check(const struct sr_dev_inst *sdi)
                sr_err("Received invalid test word 0x%016" PRIX64 ".", value);
                return SR_ERR;
        }
-       return SR_OK;
+
+       return detect_short_transfer_quirk(sdi);
 }
 
 /* Set up the device in preparation for an acquisition session.
@@ -404,7 +459,7 @@ static int prepare_request(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct acquisition_state *acq;
-       unsigned int count;
+       unsigned int chunk_len, remaining, count;
 
        devc = sdi->priv;
        acq  = devc->acquisition;
@@ -439,9 +494,12 @@ static int prepare_request(const struct sr_dev_inst *sdi)
                lwla_queue_regval(acq, REG_MEM_FILL, 0);
                break;
        case STATE_READ_REQUEST:
+               /* Limit reads to 8 device words (36 bytes) at a time if the
+                * device firmware has the short transfer quirk. */
+               chunk_len = (devc->short_transfer_quirk) ? 8 : READ_CHUNK_LEN;
                /* Always read a multiple of 8 device words. */
-               count = MIN(READ_CHUNK_LEN36, acq->mem_addr_stop
-                                       - acq->mem_addr_next + 7) / 8 * 8;
+               remaining = (acq->mem_addr_stop - acq->mem_addr_next + 7) / 8 * 8;
+               count = MIN(chunk_len, remaining);
 
                acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_MEM36);
                acq->xfer_buf_out[1] = LWLA_WORD_0(acq->mem_addr_next);
index e8a66cf3e4821ea2396c1920166bf96f13a70d6f..adb08b8e0d46630d3890938175cefa16ba46b4a6 100644 (file)
@@ -129,6 +129,7 @@ struct dev_context {
        const struct model_info *model;         /* device model descriptor */
        struct acquisition_state *acquisition;  /* running capture state */
        int active_fpga_config;                 /* FPGA configuration index */
+       gboolean short_transfer_quirk;          /* 64 bytes response limit */
 
        enum protocol_state state;      /* async protocol state */
        gboolean cancel_requested;      /* stop after current transfer */