From: Daniel Elstner Date: Sat, 5 Dec 2015 16:47:52 +0000 (+0100) Subject: sysclk-lwla: Work around short transfer quirk X-Git-Tag: libsigrok-0.4.0~65 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=786485772ffd28dfb1ca7375ca64118ccd7af25c;p=libsigrok.git sysclk-lwla: Work around short transfer quirk 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. --- diff --git a/src/hardware/sysclk-lwla/api.c b/src/hardware/sysclk-lwla/api.c index 89106256..36c9ee60 100644 --- a/src/hardware/sysclk-lwla/api.c +++ b/src/hardware/sysclk-lwla/api.c @@ -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. diff --git a/src/hardware/sysclk-lwla/lwla.c b/src/hardware/sysclk-lwla/lwla.c index c884885c..6a0b5606 100644 --- a/src/hardware/sysclk-lwla/lwla.c +++ b/src/hardware/sysclk-lwla/lwla.c @@ -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, diff --git a/src/hardware/sysclk-lwla/lwla.h b/src/hardware/sysclk-lwla/lwla.h index 7d95f9c1..903e278d 100644 --- a/src/hardware/sysclk-lwla/lwla.h +++ b/src/hardware/sysclk-lwla/lwla.h @@ -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); diff --git a/src/hardware/sysclk-lwla/lwla1016.c b/src/hardware/sysclk-lwla/lwla1016.c index 4c8bbfb5..33fd7e28 100644 --- a/src/hardware/sysclk-lwla/lwla1016.c +++ b/src/hardware/sysclk-lwla/lwla1016.c @@ -35,11 +35,11 @@ /* 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); diff --git a/src/hardware/sysclk-lwla/lwla1034.c b/src/hardware/sysclk-lwla/lwla1034.c index fdb54e62..62de549a 100644 --- a/src/hardware/sysclk-lwla/lwla1034.c +++ b/src/hardware/sysclk-lwla/lwla1034.c @@ -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); diff --git a/src/hardware/sysclk-lwla/protocol.h b/src/hardware/sysclk-lwla/protocol.h index e8a66cf3..adb08b8e 100644 --- a/src/hardware/sysclk-lwla/protocol.h +++ b/src/hardware/sysclk-lwla/protocol.h @@ -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 */