From: Daniel Elstner Date: Sun, 26 Jan 2014 19:28:59 +0000 (+0100) Subject: sysclk-lwla: Simplify and optimize word extraction. X-Git-Tag: libsigrok-0.3.0~179 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=e0df15d43600737fe225f5c68dbdbcf84d5fa326;p=libsigrok.git sysclk-lwla: Simplify and optimize word extraction. It turns out that all LWLA protocol responses consist either of 32-bit units or of 32-bit units combined into 64-bit units. Thus it makes sense to double the basic unit size for reading from 16 bit to 32 bit. We cannot do the same for command messages though, as those actually do use 16-bit quantities in some places, and 32-bit arguments are not always aligned to 32-bit boundaries. (acquisition_state.xfer_buf_in): Change unit type to uint32_t, and update related macros and code accordingly. (LWLA_TO_UINT32): New macro to replace LWLA_READ32, operating directly on 32-bit values instead of pointers to 16-bit units. Make use of a compiler-recognized idiom for bitwise rotation to efficiently swap the 16-bit halves of a 32-bit word. (LWLA_TO_UINT16): New macro to replace LWLA_READ16. (LWLA_READ64): Remove unused macro. (LWLA_WORD_[0123]): Slightly simplify 16-bit word extraction. --- diff --git a/hardware/sysclk-lwla/lwla.c b/hardware/sysclk-lwla/lwla.c index 8a6496fb..dcb7af77 100644 --- a/hardware/sysclk-lwla/lwla.c +++ b/hardware/sysclk-lwla/lwla.c @@ -145,20 +145,20 @@ SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb, (unsigned char *)command, cmd_len * 2, &xfer_len, USB_TIMEOUT); if (ret != 0) { - sr_dbg("Failed to send command %u: %s.", - LWLA_READ16(command), libusb_error_name(ret)); + sr_dbg("Failed to send command %d: %s.", + LWLA_TO_UINT16(command[0]), libusb_error_name(ret)); return SR_ERR; } if (xfer_len != cmd_len * 2) { - sr_dbg("Failed to send command %u: incorrect length %d != %d.", - LWLA_READ16(command), xfer_len, cmd_len * 2); + sr_dbg("Failed to send command %d: incorrect length %d != %d.", + LWLA_TO_UINT16(command[0]), xfer_len, cmd_len * 2); return SR_ERR; } return SR_OK; } SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb, - uint16_t *reply, int reply_len, int expect_len) + uint32_t *reply, int reply_len, int expect_len) { int ret; int xfer_len; @@ -168,15 +168,15 @@ SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb, xfer_len = 0; ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY, - (unsigned char *)reply, reply_len * 2, + (unsigned char *)reply, reply_len * 4, &xfer_len, USB_TIMEOUT); if (ret != 0) { sr_dbg("Failed to receive reply: %s.", libusb_error_name(ret)); return SR_ERR; } - if (xfer_len != expect_len * 2) { + if (xfer_len != expect_len * 4) { sr_dbg("Failed to receive reply: incorrect length %d != %d.", - xfer_len, expect_len * 2); + xfer_len, expect_len * 4); return SR_ERR; } return SR_OK; @@ -187,7 +187,7 @@ SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb, { int ret; uint16_t command[2]; - uint16_t reply[256]; /* full EP buffer to avoid overflows */ + uint32_t reply[128]; /* full EP buffer to avoid overflows */ command[0] = LWLA_WORD(CMD_READ_REG); command[1] = LWLA_WORD(reg); @@ -197,10 +197,10 @@ SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb, if (ret != SR_OK) return ret; - ret = lwla_receive_reply(usb, reply, G_N_ELEMENTS(reply), 2); + ret = lwla_receive_reply(usb, reply, G_N_ELEMENTS(reply), 1); if (ret == SR_OK) - *value = LWLA_READ32(reply); + *value = LWLA_TO_UINT32(reply[0]); return ret; } diff --git a/hardware/sysclk-lwla/lwla.h b/hardware/sysclk-lwla/lwla.h index fd5b387a..94db07e4 100644 --- a/hardware/sysclk-lwla/lwla.h +++ b/hardware/sysclk-lwla/lwla.h @@ -27,25 +27,25 @@ struct sr_usb_dev_inst; -/* Read mixed endian words from a buffer of 16-bit units. */ -#define LWLA_READ16(buf) GUINT16_FROM_LE(*(buf)) -#define LWLA_READ32(buf) \ - (((uint32_t)GUINT16_FROM_LE((buf)[0]) << 16) | \ - ((uint32_t)GUINT16_FROM_LE((buf)[1]))) -#define LWLA_READ64(buf) \ - (((uint64_t)LWLA_READ32((buf))) | \ - ((uint64_t)LWLA_READ32((buf) + 2) << 32)) - -/* Convert 16-bit argument to little endian. */ +/* Rotate argument n bits to the left. + * This construct is an idiom recognized by GCC as bit rotation. + */ +#define LROTATE(a, n) (((a) << (n)) | ((a) >> (CHAR_BIT * sizeof(a) - (n)))) + +/* Convert 16-bit little endian LWLA protocol word to machine word order. */ +#define LWLA_TO_UINT16(val) GUINT16_FROM_LE(val) + +/* Convert 32-bit mixed endian LWLA protocol word to machine word order. */ +#define LWLA_TO_UINT32(val) LROTATE(GUINT32_FROM_LE(val), 16) + +/* Convert 16-bit argument to LWLA protocol word. */ #define LWLA_WORD(val) GUINT16_TO_LE(val) -/* Extract 16-bit units from 32/64-bit value in mixed endian order. */ -#define LWLA_WORD_0(val) GUINT16_TO_LE(((val) & 0xFFFF0000u) >> 16) -#define LWLA_WORD_1(val) GUINT16_TO_LE(((val) & 0x0000FFFFu)) -#define LWLA_WORD_2(val) \ - GUINT16_TO_LE(((val) & G_GUINT64_CONSTANT(0xFFFF000000000000)) >> 48) -#define LWLA_WORD_3(val) \ - GUINT16_TO_LE(((val) & G_GUINT64_CONSTANT(0x0000FFFF00000000)) >> 32) +/* Extract 16-bit units in mixed endian order from 32/64-bit value. */ +#define LWLA_WORD_0(val) GUINT16_TO_LE(((val) >> 16) & 0xFFFF) +#define LWLA_WORD_1(val) GUINT16_TO_LE((val) & 0xFFFF) +#define LWLA_WORD_2(val) GUINT16_TO_LE(((val) >> 48) & 0xFFFF) +#define LWLA_WORD_3(val) GUINT16_TO_LE(((val) >> 32) & 0xFFFF) /** USB device end points. */ @@ -108,7 +108,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, - uint16_t *reply, int reply_len, int expect_len); + uint32_t *reply, int reply_len, int expect_len); SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb, uint16_t reg, uint32_t *value); diff --git a/hardware/sysclk-lwla/protocol.c b/hardware/sysclk-lwla/protocol.c index 82c1055c..5b1f2483 100644 --- a/hardware/sysclk-lwla/protocol.c +++ b/hardware/sysclk-lwla/protocol.c @@ -287,7 +287,7 @@ 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("%zu words in capture buffer.", acq->mem_addr_fill); @@ -362,9 +362,9 @@ 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. */ - mem_fill = LWLA_READ32(&acq->xfer_buf_in[0]); - duration = LWLA_READ32(&acq->xfer_buf_in[8]); - flags = LWLA_READ32(&acq->xfer_buf_in[16]) & STATUS_FLAG_MASK; + 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 @@ -452,7 +452,7 @@ static int process_sample_data(const struct sr_dev_inst *sdi) 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; @@ -472,7 +472,7 @@ static int process_sample_data(const struct sr_dev_inst *sdi) 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) { @@ -530,8 +530,8 @@ static int process_sample_data(const struct sr_dev_inst *sdi) 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) { @@ -545,10 +545,9 @@ 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; } diff --git a/hardware/sysclk-lwla/protocol.h b/hardware/sysclk-lwla/protocol.h index 29a191aa..762a17b5 100644 --- a/hardware/sysclk-lwla/protocol.h +++ b/hardware/sysclk-lwla/protocol.h @@ -69,21 +69,21 @@ */ #define READ_CHUNK_LEN (28 * 8) -/** Calculate the required buffer size in 16-bit units for reading a given +/** Calculate the required buffer size in 32-bit units for reading a given * number of device memory words. Rounded to a multiple of 8 device words. */ -#define LWLA1034_MEMBUF_LEN(count) (((count) + 7) / 8 * 18) +#define LWLA1034_MEMBUF_LEN(count) (((count) + 7) / 8 * 9) /** Maximum number of 16-bit words sent at a time during acquisition. * Used for allocating the libusb transfer buffer. */ #define MAX_ACQ_SEND_WORDS 8 /* 5 for memory read request plus stuffing */ -/** Maximum number of 16-bit words received at a time during acquisition. +/** Maximum number of 32-bit words received at a time during acquisition. * Round to the next multiple of the endpoint buffer size to avoid nasty * transfer overflow conditions on hiccups. */ -#define MAX_ACQ_RECV_WORDS ((READ_CHUNK_LEN / 4 * 9 + 255) / 256 * 256) +#define MAX_ACQ_RECV_LEN ((READ_CHUNK_LEN / 8 * 9 + 127) / 128 * 128) /** Maximum length of a register write sequence. */ @@ -174,9 +174,9 @@ struct acquisition_state { /** Whether to bypass the clock divider. */ gboolean bypass_clockdiv; - /* Payload data buffers for outgoing and incoming transfers. */ + /* Payload data buffers for incoming and outgoing transfers. */ + uint32_t xfer_buf_in[MAX_ACQ_RECV_LEN]; uint16_t xfer_buf_out[MAX_ACQ_SEND_WORDS]; - uint16_t xfer_buf_in[MAX_ACQ_RECV_WORDS]; /* Payload buffer for sigrok logic packets. */ uint8_t out_packet[PACKET_LENGTH * UNIT_SIZE];