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.
This commit is contained in:
Daniel Elstner 2015-12-05 17:47:52 +01:00
parent 940805ce7d
commit 786485772f
6 changed files with 167 additions and 31 deletions

View 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.

View 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,

View 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);

View File

@ -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);

View 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);

View 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 */