]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/kingst-la2016/protocol.c
kingst-la2016: rephrase FPGA bitstream content zero padding
[libsigrok.git] / src / hardware / kingst-la2016 / protocol.c
index efd16fa9fddb1f1745ed5329ac67466e18abfaea..a0de3be70c0982ea5bc7525a49069dfdc13bab39 100644 (file)
  * CMD_FPGA_SPI requests. The FX2 MCU transparently handles the detail
  * of SPI transfers encoding the read (1) or write (0) direction in the
  * MSB of the address field. There are some 60 byte-wide FPGA registers.
+ *
+ * Unfortunately the FPGA registers change their meaning between the
+ * read and write directions of access, or exclusively provide one of
+ * these directions and not the other. This is an arbitrary vendor's
+ * choice, there is nothing which the sigrok driver could do about it.
+ * Values written to registers typically cannot get read back, neither
+ * verified after writing a configuration, nor queried upon startup for
+ * automatic detection of the current configuration. Neither appear to
+ * be there echo registers for presence and communication checks, nor
+ * version identifying registers, as far as we know.
  */
 #define REG_RUN                0x00    /* Read capture status, write start capture. */
 #define REG_PWM_EN     0x02    /* User PWM channels on/off. */
@@ -67,8 +77,8 @@
 #define REG_PWM2       0x78    /* Write config for user PWM2. */
 
 static int ctrl_in(const struct sr_dev_inst *sdi,
-                  uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
-                  void *data, uint16_t wLength)
+       uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+       void *data, uint16_t wLength)
 {
        struct sr_usb_dev_inst *usb;
        int ret;
@@ -91,8 +101,8 @@ static int ctrl_in(const struct sr_dev_inst *sdi,
 }
 
 static int ctrl_out(const struct sr_dev_inst *sdi,
-                   uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
-                   void *data, uint16_t wLength)
+       uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+       void *data, uint16_t wLength)
 {
        struct sr_usb_dev_inst *usb;
        int ret;
@@ -114,22 +124,92 @@ static int ctrl_out(const struct sr_dev_inst *sdi,
        return SR_OK;
 }
 
-static int upload_fpga_bitstream(const struct sr_dev_inst *sdi, const char *bitstream_fname)
+/*
+ * Check the necessity for FPGA bitstream upload, because another upload
+ * would take some 600ms which is undesirable after program startup. Try
+ * to access some FPGA registers and check the values' plausibility. The
+ * check should fail on the safe side, request another upload when in
+ * doubt. A positive response (the request to continue operation with the
+ * currently active bitstream) should be conservative. Accessing multiple
+ * registers is considered cheap compared to the cost of bitstream upload.
+ *
+ * It helps though that both the vendor software and the sigrok driver
+ * use the same bundle of MCU firmware and FPGA bitstream for any of the
+ * supported models. We don't expect to successfully communicate to the
+ * device yet disagree on its protocol. Ideally we would access version
+ * identifying registers for improved robustness, but are not aware of
+ * any. A bitstream reload can always be forced by a power cycle.
+ */
+static int check_fpga_bitstream(const struct sr_dev_inst *sdi)
+{
+       uint8_t init_rsp;
+       int ret;
+       uint16_t run_state;
+       uint8_t pwm_en;
+       size_t read_len;
+       uint8_t buff[sizeof(run_state)];
+       const uint8_t *rdptr;
+
+       sr_dbg("Checking operation of the FPGA bitstream.");
+
+       init_rsp = 0xff;
+       ret = ctrl_in(sdi, CMD_FPGA_INIT, 0x00, 0, &init_rsp, sizeof(init_rsp));
+       if (ret != SR_OK || init_rsp != 0) {
+               sr_dbg("FPGA init query failed, or unexpected response.");
+               return SR_ERR_IO;
+       }
+
+       read_len = sizeof(run_state);
+       ret = ctrl_in(sdi, CMD_FPGA_SPI, REG_RUN, 0, buff, read_len);
+       if (ret != SR_OK) {
+               sr_dbg("FPGA register access failed (run state).");
+               return SR_ERR_IO;
+       }
+       rdptr = buff;
+       run_state = read_u16le_inc(&rdptr);
+       sr_spew("FPGA register: run state 0x%04x.", run_state);
+       if (run_state && (run_state & 0x3) != 0x1) {
+               sr_dbg("Unexpected FPGA register content (run state).");
+               return SR_ERR_DATA;
+       }
+       if (run_state && (run_state & ~0xf) != 0x85e0) {
+               sr_dbg("Unexpected FPGA register content (run state).");
+               return SR_ERR_DATA;
+       }
+
+       read_len = sizeof(pwm_en);
+       ret = ctrl_in(sdi, CMD_FPGA_SPI, REG_PWM_EN, 0, buff, read_len);
+       if (ret != SR_OK) {
+               sr_dbg("FPGA register access failed (PWM enable).");
+               return SR_ERR_IO;
+       }
+       rdptr = buff;
+       pwm_en = read_u8_inc(&rdptr);
+       sr_spew("FPGA register: PWM enable 0x%02x.", pwm_en);
+       if ((pwm_en & 0x3) != 0x0) {
+               sr_dbg("Unexpected FPGA register content (PWM enable).");
+               return SR_ERR_DATA;
+       }
+
+       sr_info("Could re-use current FPGA bitstream. No upload required.");
+       return SR_OK;
+}
+
+static int upload_fpga_bitstream(const struct sr_dev_inst *sdi,
+       const char *bitstream_fname)
 {
-       struct dev_context *devc;
        struct drv_context *drvc;
        struct sr_usb_dev_inst *usb;
        struct sr_resource bitstream;
+       uint32_t bitstream_size;
        uint8_t buffer[sizeof(uint32_t)];
        uint8_t *wrptr;
-       uint8_t cmd_resp;
        uint8_t block[4096];
        int len, act_len;
        unsigned int pos;
        int ret;
-       unsigned int zero_pad_to = 0x2c000;
+       unsigned int zero_pad_to;
 
-       devc = sdi->priv;
        drvc = sdi->driver->context;
        usb = sdi->conn;
 
@@ -141,14 +221,18 @@ static int upload_fpga_bitstream(const struct sr_dev_inst *sdi, const char *bits
                return ret;
        }
 
-       devc->bitstream_size = (uint32_t)bitstream.size;
+       bitstream_size = (uint32_t)bitstream.size;
        wrptr = buffer;
-       write_u32le_inc(&wrptr, devc->bitstream_size);
+       write_u32le_inc(&wrptr, bitstream_size);
        if ((ret = ctrl_out(sdi, CMD_FPGA_INIT, 0x00, 0, buffer, wrptr - buffer)) != SR_OK) {
                sr_err("Cannot initiate FPGA bitstream upload.");
                sr_resource_close(drvc->sr_ctx, &bitstream);
                return ret;
        }
+       zero_pad_to = bitstream_size;
+       zero_pad_to += LA2016_EP2_PADDING - 1;
+       zero_pad_to /= LA2016_EP2_PADDING;
+       zero_pad_to *= LA2016_EP2_PADDING;
 
        pos = 0;
        while (1) {
@@ -169,7 +253,8 @@ static int upload_fpga_bitstream(const struct sr_dev_inst *sdi, const char *bits
                if (len == 0)
                        break;
 
-               ret = libusb_bulk_transfer(usb->devhdl, 2, (unsigned char*)&block[0], len, &act_len, DEFAULT_TIMEOUT_MS);
+               ret = libusb_bulk_transfer(usb->devhdl, 2,
+                       &block[0], len, &act_len, DEFAULT_TIMEOUT_MS);
                if (ret != 0) {
                        sr_dbg("Cannot write FPGA bitstream, block %#x len %d: %s.",
                                pos, (int)len, libusb_error_name(ret));
@@ -190,6 +275,14 @@ static int upload_fpga_bitstream(const struct sr_dev_inst *sdi, const char *bits
        sr_info("FPGA bitstream upload (%" PRIu64 " bytes) done.",
                bitstream.size);
 
+       return SR_OK;
+}
+
+static int enable_fpga_bitstream(const struct sr_dev_inst *sdi)
+{
+       int ret;
+       uint8_t cmd_resp;
+
        if ((ret = ctrl_in(sdi, CMD_FPGA_INIT, 0x00, 0, &cmd_resp, sizeof(cmd_resp))) != SR_OK) {
                sr_err("Cannot read response after FPGA bitstream upload.");
                return ret;
@@ -199,15 +292,14 @@ static int upload_fpga_bitstream(const struct sr_dev_inst *sdi, const char *bits
                        cmd_resp);
                return SR_ERR;
        }
-
        g_usleep(30000);
 
        if ((ret = ctrl_out(sdi, CMD_FPGA_ENABLE, 0x01, 0, NULL, 0)) != SR_OK) {
                sr_err("Cannot enable FPGA after bitstream upload.");
                return ret;
        }
-
        g_usleep(40000);
+
        return SR_OK;
 }
 
@@ -218,15 +310,14 @@ static int set_threshold_voltage(const struct sr_dev_inst *sdi, float voltage)
 
        devc = sdi->priv;
 
-       uint16_t duty_R79,duty_R56;
+       uint16_t duty_R79, duty_R56;
        uint8_t buf[2 * sizeof(uint16_t)];
        uint8_t *wrptr;
 
        /* Clamp threshold setting to valid range for LA2016. */
        if (voltage > 4.0) {
                voltage = 4.0;
-       }
-       else if (voltage < -4.0) {
+       } else if (voltage < -4.0) {
                voltage = -4.0;
        }
 
@@ -244,12 +335,10 @@ static int set_threshold_voltage(const struct sr_dev_inst *sdi, float voltage)
        if (voltage >= 2.9) {
                duty_R79 = 0;           /* PWM off (0V). */
                duty_R56 = (uint16_t)(302 * voltage - 363);
-       }
-       else if (voltage <= -0.4) {
+       } else if (voltage <= -0.4) {
                duty_R79 = 0x02d7;      /* 72% duty cycle. */
                duty_R56 = (uint16_t)(302 * voltage + 1090);
-       }
-       else {
+       } else {
                duty_R79 = 0x00f2;      /* 25% duty cycle. */
                duty_R56 = (uint16_t)(302 * voltage + 121);
        }
@@ -257,8 +346,7 @@ static int set_threshold_voltage(const struct sr_dev_inst *sdi, float voltage)
        /* Clamp duty register values to sensible limits. */
        if (duty_R56 < 10) {
                duty_R56 = 10;
-       }
-       else if (duty_R56 > 1100) {
+       } else if (duty_R56 > 1100) {
                duty_R56 = 1100;
        }
 
@@ -303,7 +391,8 @@ static int enable_pwm(const struct sr_dev_inst *sdi, uint8_t p1, uint8_t p2)
        return SR_OK;
 }
 
-static int set_pwm(const struct sr_dev_inst *sdi, uint8_t which, float freq, float duty)
+static int set_pwm(const struct sr_dev_inst *sdi, uint8_t which,
+       float freq, float duty)
 {
        int CTRL_PWM[] = { REG_PWM1, REG_PWM2 };
        struct dev_context *devc;
@@ -561,20 +650,16 @@ static uint16_t run_state(const struct sr_dev_inst *sdi)
                previous_state = state;
                if ((state & 0x0003) == 0x01) {
                        sr_dbg("Run state: 0x%04x (%s).", state, "idle");
-               }
-               else if ((state & 0x000f) == 0x02) {
+               } else if ((state & 0x000f) == 0x02) {
                        sr_dbg("Run state: 0x%04x (%s).", state,
                                "pre-trigger sampling");
-               }
-               else if ((state & 0x000f) == 0x0a) {
+               } else if ((state & 0x000f) == 0x0a) {
                        sr_dbg("Run state: 0x%04x (%s).", state,
                                "sampling, waiting for trigger");
-               }
-               else if ((state & 0x000f) == 0x0e) {
+               } else if ((state & 0x000f) == 0x0e) {
                        sr_dbg("Run state: 0x%04x (%s).", state,
                                "post-trigger sampling");
-               }
-               else {
+               } else {
                        sr_dbg("Run state: 0x%04x.", state);
                }
        }
@@ -614,10 +699,10 @@ static int get_capture_info(const struct sr_dev_inst *sdi)
        devc->info.write_pos = read_u32le_inc(&rdptr);
 
        sr_dbg("Capture info: n_rep_packets: 0x%08x/%d, before_trigger: 0x%08x/%d, write_pos: 0x%08x%d.",
-              devc->info.n_rep_packets, devc->info.n_rep_packets,
-              devc->info.n_rep_packets_before_trigger,
-              devc->info.n_rep_packets_before_trigger,
-              devc->info.write_pos, devc->info.write_pos);
+               devc->info.n_rep_packets, devc->info.n_rep_packets,
+               devc->info.n_rep_packets_before_trigger,
+               devc->info.n_rep_packets_before_trigger,
+               devc->info.write_pos, devc->info.write_pos);
 
        if (devc->info.n_rep_packets % 5) {
                sr_warn("Unexpected packets count %lu, not a multiple of 5.",
@@ -627,7 +712,8 @@ static int get_capture_info(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-SR_PRIV int la2016_upload_firmware(struct sr_context *sr_ctx, libusb_device *dev, uint16_t product_id)
+SR_PRIV int la2016_upload_firmware(struct sr_context *sr_ctx,
+       libusb_device *dev, uint16_t product_id)
 {
        char fw_file[1024];
        snprintf(fw_file, sizeof(fw_file) - 1, UC_FIRMWARE, product_id);
@@ -710,7 +796,8 @@ static int la2016_has_triggered(const struct sr_dev_inst *sdi)
        return (state & 0x3) == 1;
 }
 
-static int la2016_start_retrieval(const struct sr_dev_inst *sdi, libusb_transfer_cb_fn cb)
+static int la2016_start_retrieval(const struct sr_dev_inst *sdi,
+       libusb_transfer_cb_fn cb)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
@@ -732,7 +819,7 @@ static int la2016_start_retrieval(const struct sr_dev_inst *sdi, libusb_transfer
        devc->n_reps_until_trigger = devc->info.n_rep_packets_before_trigger;
 
        sr_dbg("Want to read %u xfer-packets starting from pos %" PRIu32 ".",
-              devc->n_transfer_packets_to_read, devc->read_pos);
+               devc->n_transfer_packets_to_read, devc->read_pos);
 
        if ((ret = ctrl_out(sdi, CMD_BULK_RESET, 0x00, 0, NULL, 0)) != SR_OK) {
                sr_err("Cannot reset USB bulk state.");
@@ -848,8 +935,8 @@ static void send_chunk(struct sr_dev_inst *sdi,
                                        devc->reading_behind_trigger = 1;
                                        do_signal_trigger = 1;
                                        sr_dbg("Trigger position after %" PRIu64 " samples, %.6fms.",
-                                              devc->total_samples,
-                                              (double)devc->total_samples / devc->cur_samplerate * 1e3);
+                                               devc->total_samples,
+                                               (double)devc->total_samples / devc->cur_samplerate * 1e3);
                                }
                        }
                }
@@ -877,7 +964,7 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
        usb = sdi->conn;
 
        sr_dbg("receive_transfer(): status %s received %d bytes.",
-              libusb_error_name(transfer->status), transfer->actual_length);
+               libusb_error_name(transfer->status), transfer->actual_length);
 
        if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
                sr_err("USB bulk transfer timeout.");
@@ -977,6 +1064,7 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
        uint8_t buf[8];
        int16_t purchase_date_bcd[2];
        uint8_t magic;
+       const char *bitstream_fn;
        int ret;
 
        devc = sdi->priv;
@@ -989,8 +1077,7 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
         */
        if ((ret = ctrl_in(sdi, CMD_EEPROM, 0x20, 0, purchase_date_bcd, sizeof(purchase_date_bcd))) != SR_OK) {
                sr_err("Cannot read purchase date in EEPROM.");
-       }
-       else {
+       } else {
                sr_dbg("Purchase date: 20%02hx-%02hx.",
                        (purchase_date_bcd[0]) & 0xff,
                        (purchase_date_bcd[0] >> 8) & 0xff);
@@ -1046,8 +1133,7 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
        if (buf[0] == (0xff & ~buf[1])) {
                /* Primary copy of magic passes complement check. */
                magic = buf[0];
-       }
-       else if (buf[4] == (0x0ff & ~buf[5])) {
+       } else if (buf[4] == (0xff & ~buf[5])) {
                /* Backup copy of magic passes complement check. */
                sr_dbg("Using backup copy of device type magic number.");
                magic = buf[4];
@@ -1058,28 +1144,40 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
        /* Select the FPGA bitstream depending on the model. */
        switch (magic) {
        case 2:
-               ret = upload_fpga_bitstream(sdi, FPGA_FW_LA2016);
+               bitstream_fn = FPGA_FW_LA2016;
                devc->max_samplerate = MAX_SAMPLE_RATE_LA2016;
                break;
        case 3:
-               ret = upload_fpga_bitstream(sdi, FPGA_FW_LA1016);
+               bitstream_fn = FPGA_FW_LA1016;
                devc->max_samplerate = MAX_SAMPLE_RATE_LA1016;
                break;
        case 8:
-               ret = upload_fpga_bitstream(sdi, FPGA_FW_LA2016A);
+               bitstream_fn = FPGA_FW_LA2016A;
                devc->max_samplerate = MAX_SAMPLE_RATE_LA2016;
                break;
        case 9:
-               ret = upload_fpga_bitstream(sdi, FPGA_FW_LA1016A);
+               bitstream_fn = FPGA_FW_LA1016A;
                devc->max_samplerate = MAX_SAMPLE_RATE_LA1016;
                break;
        default:
+               bitstream_fn = NULL;
+               break;
+       }
+       if (!bitstream_fn || !*bitstream_fn) {
                sr_err("Cannot identify as one of the supported models.");
                return SR_ERR;
        }
 
+       if (check_fpga_bitstream(sdi) != SR_OK) {
+               ret = upload_fpga_bitstream(sdi, bitstream_fn);
+               if (ret != SR_OK) {
+                       sr_err("Cannot upload FPGA bitstream.");
+                       return ret;
+               }
+       }
+       ret = enable_fpga_bitstream(sdi);
        if (ret != SR_OK) {
-               sr_err("Cannot upload FPGA bitstream.");
+               sr_err("Cannot enable FPGA bitstream after upload.");
                return ret;
        }