From: Vitaliy Vorobyov <> Date: Mon, 22 Apr 2019 10:24:47 +0000 (+0200) Subject: Add initial Sysclk SLA5032 driver. X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=8da8c8265fabe437db254d3b659785ea2352b7dc;p=libsigrok.git Add initial Sysclk SLA5032 driver. --- diff --git a/Makefile.am b/Makefile.am index 500d6620..cae48d47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -554,6 +554,14 @@ src_libdrivers_la_SOURCES += \ src/hardware/sysclk-lwla/protocol.c \ src/hardware/sysclk-lwla/api.c endif +if HW_SYSCLK_SLA5032 +src_libdrivers_la_SOURCES += \ + src/hardware/sysclk-sla5032/sla5032.h \ + src/hardware/sysclk-sla5032/sla5032.c \ + src/hardware/sysclk-sla5032/protocol.h \ + src/hardware/sysclk-sla5032/protocol.c \ + src/hardware/sysclk-sla5032/api.c +endif if HW_TELEINFO src_libdrivers_la_SOURCES += \ src/hardware/teleinfo/protocol.h \ diff --git a/configure.ac b/configure.ac index ea3e574b..50787cde 100644 --- a/configure.ac +++ b/configure.ac @@ -277,6 +277,7 @@ SR_DRIVER([serial DMM], [serial-dmm], [libserialport]) SR_DRIVER([serial LCR], [serial-lcr], [libserialport]) SR_DRIVER([Siglent SDS], [siglent-sds]) SR_DRIVER([Sysclk LWLA], [sysclk-lwla], [libusb]) +SR_DRIVER([Sysclk SLA5032], [sysclk-sla5032], [libusb]) SR_DRIVER([Teleinfo], [teleinfo], [libserialport]) SR_DRIVER([Testo], [testo], [libusb]) SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [libserialport]) diff --git a/src/hardware/sysclk-sla5032/api.c b/src/hardware/sysclk-sla5032/api.c new file mode 100644 index 00000000..d3fca3dd --- /dev/null +++ b/src/hardware/sysclk-sla5032/api.c @@ -0,0 +1,546 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2014 Daniel Elstner + * Copyright (C) 2019 Vitaliy Vorobyov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "protocol.h" +#include "sla5032.h" + + /* Number of logic channels. */ +#define NUM_CHANNELS 32 + +static const uint32_t scanopts[] = { + SR_CONF_CONN, +}; + +static const uint32_t drvopts[] = { + SR_CONF_LOGIC_ANALYZER, +}; + +static const int32_t trigger_matches[] = { + SR_TRIGGER_ZERO, + SR_TRIGGER_ONE, + SR_TRIGGER_RISING, + SR_TRIGGER_FALLING, +}; + +static const uint64_t capture_ratios[] = { + 0, 10, 20, 30, 50, 70, 90, 100, +}; + +static const uint32_t devopts[] = { + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, + SR_CONF_RLE | SR_CONF_GET, +}; + +static const uint64_t samplerates[] = { + SR_MHZ(500), SR_MHZ(400), SR_MHZ(250), SR_MHZ(200), SR_MHZ(100), + SR_MHZ(50), SR_MHZ(25), SR_MHZ(20), SR_MHZ(10), SR_MHZ(5), SR_MHZ(2), + SR_MHZ(1), SR_KHZ(500), SR_KHZ(200), SR_KHZ(100), SR_KHZ(50), + SR_KHZ(20), SR_KHZ(10), SR_KHZ(5), SR_KHZ(2), +}; + +static struct sr_dev_inst *dev_inst_new(void) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + int i; + char name[8]; + + devc = g_malloc0(sizeof(struct dev_context)); + devc->active_fpga_config = FPGA_NOCONF; + devc->samplerate = samplerates[0]; + devc->limit_samples = MAX_LIMIT_SAMPLES; + devc->capture_ratio = capture_ratios[4]; + devc->channel_mask = (UINT64_C(1) << NUM_CHANNELS) - 1; + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("Sysclk"); + sdi->model = g_strdup("SLA5032"); + sdi->priv = devc; + + for (i = 0; i < NUM_CHANNELS; i++) { + g_snprintf(name, sizeof(name), "CH%d", i); + sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, name); + } + + return sdi; +} + +/* Create a new device instance for a libusb device if it is a Sysclk SLA5032 + * device and also matches the connection specification. + */ +static struct sr_dev_inst *dev_inst_new_matching(GSList *conn_matches, + libusb_device *dev) +{ + GSList *node; + struct sr_usb_dev_inst *usb; + struct sr_dev_inst *sdi; + struct libusb_device_descriptor des; + int bus, address, ret; + unsigned int vid, pid; + + bus = libusb_get_bus_number(dev); + address = libusb_get_device_address(dev); + + for (node = conn_matches; node != NULL; node = node->next) { + usb = node->data; + if (usb && usb->bus == bus && usb->address == address) + break; /* found */ + } + if (conn_matches && !node) + return NULL; /* no match */ + + ret = libusb_get_device_descriptor(dev, &des); + if (ret != 0) { + sr_err("Failed to get USB device descriptor: %s.", + libusb_error_name(ret)); + return NULL; + } + vid = des.idVendor; + pid = des.idProduct; + + /* Create sigrok device instance. */ + if (vid == USB_VID_SYSCLK && pid == USB_PID_SLA5032) { + } else { + if (conn_matches) + sr_warn("USB device %d.%d (%04x:%04x) is not a" + " Sysclk SLA5032.", bus, address, vid, pid); + return NULL; + } + sdi = dev_inst_new(); + + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(bus, address, NULL); + + return sdi; +} + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + GSList *conn_devices, *devices, *node; + struct drv_context *drvc; + struct sr_dev_inst *sdi; + struct sr_config *src; + const char *conn; + libusb_device **devlist; + ssize_t num_devs, i; + + drvc = di->context; + conn = NULL; + conn_devices = NULL; + devices = NULL; + + for (node = options; node != NULL; node = node->next) { + src = node->data; + if (src->key == SR_CONF_CONN) { + conn = g_variant_get_string(src->data, NULL); + break; + } + } + if (conn) { + /* Find devices matching the connection specification. */ + conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); + } + + /* List all libusb devices. */ + num_devs = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + if (num_devs < 0) { + sr_err("Failed to list USB devices: %s.", + libusb_error_name(num_devs)); + g_slist_free_full(conn_devices, + (GDestroyNotify)&sr_usb_dev_inst_free); + return NULL; + } + + /* Scan the USB device list for matching devices. */ + for (i = 0; i < num_devs; i++) { + sdi = dev_inst_new_matching(conn_devices, devlist[i]); + if (!sdi) + continue; /* no match */ + + /* Register device instance with driver. */ + devices = g_slist_append(devices, sdi); + } + + libusb_free_device_list(devlist, 1); + g_slist_free_full(conn_devices, (GDestroyNotify)&sr_usb_dev_inst_free); + + return std_scan_complete(di, devices); +} + +static int dev_open(struct sr_dev_inst *sdi) +{ + struct drv_context *drvc; + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + int ret; + + drvc = sdi->driver->context; + devc = sdi->priv; + usb = sdi->conn; + + ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb); + if (ret != SR_OK) + return ret; + + ret = libusb_set_configuration(usb->devhdl, USB_CONFIG); + if (ret != LIBUSB_SUCCESS) { + sr_err("Failed to set USB configuration: %s.", + libusb_error_name(ret)); + sr_usb_close(usb); + return SR_ERR; + } + + ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); + if (ret != LIBUSB_SUCCESS) { + sr_err("Failed to claim interface: %s.", + libusb_error_name(ret)); + sr_usb_close(usb); + return SR_ERR; + } + + sdi->status = SR_ST_ACTIVE; + + devc->active_fpga_config = FPGA_NOCONF; + devc->state = STATE_IDLE; + + ret = sla5032_apply_fpga_config(sdi); + + return ret; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + + usb = sdi->conn; + + if (usb->devhdl) + libusb_release_interface(usb->devhdl, USB_INTERFACE); + + sr_usb_close(usb); + + return SR_OK; +} + +/* Check whether the device options contain a specific key. + * Also match against get/set/list bits if specified. + */ +static int has_devopt(uint32_t key) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(devopts); i++) { + if ((devopts[i] & (SR_CONF_MASK | key)) == key) + return TRUE; + } + + return FALSE; +} + +static int config_get(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + if (!has_devopt(key | SR_CONF_GET)) + return SR_ERR_NA; + + switch (key) { + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(devc->samplerate); + break; + case SR_CONF_LIMIT_SAMPLES: + *data = g_variant_new_uint64(devc->limit_samples); + break; + case SR_CONF_CAPTURE_RATIO: + *data = g_variant_new_uint64(devc->capture_ratio); + break; + case SR_CONF_RLE: + *data = g_variant_new_boolean(TRUE); + break; + default: + /* Must not happen for a key listed in devopts. */ + return SR_ERR_BUG; + } + + return SR_OK; +} + +static int config_set(uint32_t key, GVariant *data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + uint64_t value; + struct dev_context *devc; + int idx; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + if (!has_devopt(key | SR_CONF_SET)) + return SR_ERR_NA; + + switch (key) { + case SR_CONF_SAMPLERATE: + value = g_variant_get_uint64(data); + if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(samplerates))) < 0) + return SR_ERR_ARG; + devc->samplerate = samplerates[idx]; + break; + case SR_CONF_LIMIT_SAMPLES: + value = g_variant_get_uint64(data); + if (value > MAX_LIMIT_SAMPLES || value < MIN_LIMIT_SAMPLES) + return SR_ERR_ARG; + devc->limit_samples = value; + break; + case SR_CONF_CAPTURE_RATIO: + value = g_variant_get_uint64(data); + devc->capture_ratio = value; + break; + default: + /* Must not happen for a key listed in devopts. */ + return SR_ERR_BUG; + } + + return SR_OK; +} + +static int config_channel_set(const struct sr_dev_inst *sdi, + struct sr_channel *ch, unsigned int changes) +{ + uint64_t channel_bit; + struct dev_context *devc; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + if (ch->index < 0 || ch->index >= NUM_CHANNELS) { + sr_err("Channel index %d out of range.", ch->index); + return SR_ERR_BUG; + } + + if ((changes & SR_CHANNEL_SET_ENABLED) != 0) { + channel_bit = UINT64_C(1) << ch->index; + + /* Enable or disable logic input for this channel. */ + if (ch->enabled) + devc->channel_mask |= channel_bit; + else + devc->channel_mask &= ~channel_bit; + } + + return SR_OK; +} + +/* Derive trigger masks from the session's trigger configuration. */ +static int prepare_trigger_masks(const struct sr_dev_inst* sdi) +{ + uint32_t trigger_mask, trigger_values, trigger_edge_mask; + uint32_t level_bit, type_bit; + struct dev_context* devc; + struct sr_trigger* trigger; + struct sr_trigger_stage* stage; + struct sr_trigger_match* match; + const GSList* node; + int idx; + enum sr_trigger_matches trg; + + devc = sdi->priv; + + trigger_mask = 0; + trigger_values = 0; + trigger_edge_mask = 0; + + trigger = sr_session_trigger_get(sdi->session); + if (!trigger || !trigger->stages) { + goto no_triggers; + } + + if (trigger->stages->next) { + sr_err("This device only supports 1 trigger stage."); + return SR_ERR_ARG; + } + stage = trigger->stages->data; + + for (node = stage->matches; node; node = node->next) { + match = node->data; + + if (!match->channel->enabled) + continue; /* Ignore disabled channel. */ + + idx = match->channel->index; + trg = match->match; + + if (idx < 0 || idx >= NUM_CHANNELS) { + sr_err("Channel index %d out of range.", idx); + return SR_ERR_BUG; /* Should not happen. */ + } + if (trg != SR_TRIGGER_ZERO + && trg != SR_TRIGGER_ONE + && trg != SR_TRIGGER_RISING + && trg != SR_TRIGGER_FALLING) { + sr_err("Unsupported trigger match for CH%d.", idx); + return SR_ERR_ARG; + } + level_bit = (trg == SR_TRIGGER_ONE + || trg == SR_TRIGGER_RISING) ? 1 : 0; + type_bit = (trg == SR_TRIGGER_RISING + || trg == SR_TRIGGER_FALLING) ? 1 : 0; /* 1 if edge triggered, 0 if level triggered */ + + trigger_mask |= UINT32_C(1) << idx; + trigger_values |= level_bit << idx; + trigger_edge_mask |= type_bit << idx; + } + +no_triggers: + devc->trigger_mask = trigger_mask; + devc->trigger_values = trigger_values; + devc->trigger_edge_mask = trigger_edge_mask; + + return SR_OK; +} + +static int config_commit(const struct sr_dev_inst *sdi) +{ + int ret; + + ret = prepare_trigger_masks(sdi); + if (ret != SR_OK) + return ret; + + ret = sla5032_apply_fpga_config(sdi); + if (ret != SR_OK) { + sr_err("Failed to apply FPGA configuration."); + return ret; + } + + return SR_OK; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + devc = (sdi) ? sdi->priv : NULL; + + switch (key) { + case SR_CONF_SCAN_OPTIONS: + case SR_CONF_DEVICE_OPTIONS: + return std_opts_config_list(key, data, sdi, cg, + ARRAY_AND_SIZE(scanopts), ARRAY_AND_SIZE(drvopts), + (devc) ? devopts : NULL, + (devc) ? ARRAY_SIZE(devopts) : 0); + } + + if (!devc) + return SR_ERR_ARG; + if (!has_devopt(key | SR_CONF_LIST)) + return SR_ERR_NA; + + switch (key) { + case SR_CONF_SAMPLERATE: + *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates)); + break; + case SR_CONF_LIMIT_SAMPLES: + *data = std_gvar_tuple_u64(MIN_LIMIT_SAMPLES, MAX_LIMIT_SAMPLES); + break; + case SR_CONF_CAPTURE_RATIO: + *data = std_gvar_array_u64(ARRAY_AND_SIZE(capture_ratios)); + break; + case SR_CONF_TRIGGER_MATCH: + *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); + break; + default: + /* Must not happen for a key listed in devopts. */ + return SR_ERR_BUG; + } + + return SR_OK; +} + +/* Set up the device hardware to begin capturing samples as soon as the + * configured trigger conditions are met, or immediately if no triggers + * are configured. + */ +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + return la_start_acquisition(sdi); +} + +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + devc = sdi->priv; + + sr_session_source_remove(sdi->session, -1); + + std_session_send_df_end(sdi); + + devc->state = STATE_IDLE; + + return SR_OK; +} + +static struct sr_dev_driver sysclk_sla5032_driver_info = { + .name = "sysclk-sla5032", + .longname = "Sysclk SLA5032", + .api_version = 1, + .init = std_init, + .cleanup = std_cleanup, + .scan = scan, + .dev_list = std_dev_list, + .dev_clear = std_dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_channel_set = config_channel_set, + .config_commit = config_commit, + .config_list = config_list, + .dev_open = dev_open, + .dev_close = dev_close, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .context = NULL, +}; +SR_REGISTER_DEV_DRIVER(sysclk_sla5032_driver_info); diff --git a/src/hardware/sysclk-sla5032/protocol.c b/src/hardware/sysclk-sla5032/protocol.c new file mode 100644 index 00000000..24683f9e --- /dev/null +++ b/src/hardware/sysclk-sla5032/protocol.c @@ -0,0 +1,292 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2019 Vitaliy Vorobyov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "protocol.h" +#include "sla5032.h" + +/* Callback handling data */ +static int la_prepare_data(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + int i, j, ret, xfer_len; + uint8_t *rle_buf, *samples; + const uint8_t *p, *q; + uint16_t rle_count; + int samples_count, rle_samples_count; + uint32_t status[3]; + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + uint32_t value; + int trigger_offset; + + enum { + RLE_SAMPLE_SIZE = sizeof(uint32_t) + sizeof(uint16_t), + RLE_SAMPLES_COUNT = 0x100000, + RLE_BUF_SIZE = RLE_SAMPLES_COUNT * RLE_SAMPLE_SIZE, + RLE_END_MARKER = 0xFFFF, + }; + + (void)fd; + (void)revents; + + sdi = cb_data; + devc = sdi->priv; + usb = sdi->conn; + + memset(status, 0, sizeof(status)); + ret = sla5032_get_status(usb, status); + if (ret != SR_OK) { + sla5032_write_reg14_zero(usb); + sr_dev_acquisition_stop(sdi); + return G_SOURCE_CONTINUE; + } + + /* data not ready (acquision in progress) */ + if (status[1] != 3) + return G_SOURCE_CONTINUE; + + sr_dbg("acquision done, status: %u.", (unsigned int)status[2]); + + /* data ready (download, decode and send to sigrok) */ + ret = sla5032_set_read_back(usb); + if (ret != SR_OK) { + sla5032_write_reg14_zero(usb); + sr_dev_acquisition_stop(sdi); + return G_SOURCE_CONTINUE; + } + + rle_buf = g_try_malloc(RLE_BUF_SIZE); + if (rle_buf == NULL) { + sla5032_write_reg14_zero(usb); + sr_dev_acquisition_stop(sdi); + return G_SOURCE_CONTINUE; + } + + do { + xfer_len = 0; + ret = sla5032_read_data_chunk(usb, rle_buf, RLE_BUF_SIZE, &xfer_len); + if (ret != SR_OK) { + sla5032_write_reg14_zero(usb); + g_free(rle_buf); + sr_dev_acquisition_stop(sdi); + + sr_dbg("acquision done, ret: %d.", ret); + return G_SOURCE_CONTINUE; + } + + sr_dbg("acquision done, xfer_len: %d.", xfer_len); + + if (xfer_len == 0) { + sla5032_write_reg14_zero(usb); + g_free(rle_buf); + sr_dev_acquisition_stop(sdi); + return G_SOURCE_CONTINUE; + } + + p = rle_buf; + samples_count = 0; + rle_samples_count = xfer_len / RLE_SAMPLE_SIZE; + + sr_dbg("acquision done, rle_samples_count: %d.", rle_samples_count); + + for (i = 0; i < rle_samples_count; i++) { + p += sizeof(uint32_t); /* skip sample value */ + + rle_count = RL16(p); /* read RLE counter */ + p += sizeof(uint16_t); + if (rle_count == RLE_END_MARKER) { + rle_samples_count = i; + break; + } + samples_count += rle_count + 1; + } + sr_dbg("acquision done, samples_count: %d.", samples_count); + + if (samples_count == 0) { + sr_dbg("acquision done, no samples."); + sla5032_write_reg14_zero(usb); + g_free(rle_buf); + sr_dev_acquisition_stop(sdi); + return G_SOURCE_CONTINUE; + } + + /* Decode RLE */ + samples = g_try_malloc(samples_count * sizeof(uint32_t)); + if (!samples) { + sr_dbg("memory allocation error."); + sla5032_write_reg14_zero(usb); + g_free(rle_buf); + sr_dev_acquisition_stop(sdi); + return G_SOURCE_CONTINUE; + } + + p = rle_buf; + q = samples; + for (i = 0; i < rle_samples_count; i++) { + value = RL32(p); + p += sizeof(uint32_t); /* read sample value */ + + rle_count = RL16(p); /* read RLE counter */ + p += sizeof(uint16_t); + + if (rle_count == RLE_END_MARKER) { + sr_dbg("RLE end marker found."); + break; + } + + for (j = 0; j <= rle_count; j++) { + WL32(q, value); + q += sizeof(uint32_t); + } + } + + if (devc->trigger_fired) { + /* Send the incoming transfer to the session bus. */ + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + + logic.length = samples_count * sizeof(uint32_t); + logic.unitsize = sizeof(uint32_t); + logic.data = samples; + sr_session_send(sdi, &packet); + } else { + trigger_offset = soft_trigger_logic_check(devc->stl, + samples, samples_count * sizeof(uint32_t), NULL); + if (trigger_offset > -1) { + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + int num_samples = samples_count - trigger_offset; + + logic.length = num_samples * sizeof(uint32_t); + logic.unitsize = sizeof(uint32_t); + logic.data = samples + trigger_offset * sizeof(uint32_t); + sr_session_send(sdi, &packet); + + devc->trigger_fired = TRUE; + } + } + + g_free(samples); + } while (rle_samples_count == RLE_SAMPLES_COUNT); + + sr_dbg("acquision stop, rle_samples_count < RLE_SAMPLES_COUNT."); + + sla5032_write_reg14_zero(usb); + + sr_dev_acquisition_stop(sdi); /* if all data transfered */ + + g_free(rle_buf); + + if (devc->stl) { + soft_trigger_logic_free(devc->stl); + devc->stl = NULL; + } + + return G_SOURCE_CONTINUE; +} + +SR_PRIV int la_start_acquisition(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + struct sr_trigger* trigger; + int ret; + enum { poll_interval_ms = 100 }; + uint64_t pre, post; + + devc = sdi->priv; + usb = sdi->conn; + + if (devc->state != STATE_IDLE) { + sr_err("Not in idle state, cannot start acquisition."); + return SR_ERR; + } + + pre = (devc->limit_samples * devc->capture_ratio) / 100; + post = devc->limit_samples - pre; + + if ((trigger = sr_session_trigger_get(sdi->session))) { + devc->stl = soft_trigger_logic_new(sdi, trigger, pre); + if (!devc->stl) { + sr_err("stl alloc error."); + return SR_ERR_MALLOC; + } + devc->trigger_fired = FALSE; + } + else + devc->trigger_fired = TRUE; + + sr_dbg("start acquision, smp lim: %" PRIu64 ", cap ratio: %" PRIu64 + ".", devc->limit_samples, devc->capture_ratio); + + sr_dbg("start acquision, pre: %" PRIu64 ", post: %" PRIu64 ".", pre, post); + pre /= 256; + pre = max(pre, 2); + pre--; + + post /= 256; + post = max(post, 2); + post--; + + sr_dbg("start acquision, pre: %" PRIx64 ", post: %" PRIx64 ".", pre, post); + + /* (x + 1) * 256 (samples) pre, post */ + ret = sla5032_set_depth(usb, pre, post); + if (ret != SR_OK) + return ret; + + ret = sla5032_set_triggers(usb, devc->trigger_values, devc->trigger_edge_mask, devc->trigger_mask); + if (ret != SR_OK) + return ret; + + ret = sla5032_set_samplerate(usb, devc->samplerate); + if (ret != SR_OK) + return ret; + + /* TODO: make PWM generator as separate configurable subdevice */ + enum { + pwm1_hi = 20000000 - 1, + pwm1_lo = 200000 - 1, + pwm2_hi = 15 - 1, + pwm2_lo = 5 - 1, + }; + + ret = sla5032_set_pwm1(usb, pwm1_hi, pwm1_lo); + if (ret != SR_OK) + return ret; + + ret = sla5032_set_pwm2(usb, pwm2_hi, pwm2_lo); + if (ret != SR_OK) + return ret; + + ret = sla5032_start_sample(usb); + if (ret != SR_OK) + return ret; + + sr_session_source_add(sdi->session, -1, 0, poll_interval_ms, + la_prepare_data, (struct sr_dev_inst *)sdi); + + std_session_send_df_header(sdi); + + return ret; +} diff --git a/src/hardware/sysclk-sla5032/protocol.h b/src/hardware/sysclk-sla5032/protocol.h new file mode 100644 index 00000000..205c907b --- /dev/null +++ b/src/hardware/sysclk-sla5032/protocol.h @@ -0,0 +1,101 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2019 Vitaliy Vorobyov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_HARDWARE_SYSCLK_SLA5032_PROTOCOL_H +#define LIBSIGROK_HARDWARE_SYSCLK_SLA5032_PROTOCOL_H + +#include +#include +#include +#include +#include + +#define LOG_PREFIX "sysclk-sla5032" + +/* Maximum configurable sample count limit. */ +#define MAX_LIMIT_SAMPLES (64 * 1024 * 1024) +#define MIN_LIMIT_SAMPLES 512 + +/* USB vendor and product IDs. */ +enum { + USB_VID_SYSCLK = 0x2961, + USB_PID_SLA5032 = 0x66B0, +}; + +/* USB device characteristics. */ +enum { + USB_CONFIG = 1, + USB_INTERFACE = 0, + USB_CMD_TIMEOUT_MS = 5000, + USB_REPLY_TIMEOUT_MS = 500000, + USB_DATA_TIMEOUT_MS = 2000, +}; + +/* USB device end points. */ +enum usb_endpoint { + EP_COMMAND = 4 | LIBUSB_ENDPOINT_OUT, + EP_REPLY = 8 | LIBUSB_ENDPOINT_IN, + EP_DATA = 6 | LIBUSB_ENDPOINT_IN, +}; + + +/* Common indicator for no or unknown FPGA config. */ +enum { + FPGA_NOCONF = -1, + FPGA_CONF, +}; + +/** Acquisition protocol states. */ +enum protocol_state { + /* idle states */ + STATE_IDLE = 0, + STATE_STATUS_WAIT, + /* device command states */ + STATE_START_CAPTURE, + STATE_STOP_CAPTURE, + STATE_READ_PREPARE, + STATE_READ_FINISH, + /* command followed by response */ + STATE_EXPECT_RESPONSE = 1 << 3, + STATE_STATUS_REQUEST = STATE_EXPECT_RESPONSE, + STATE_LENGTH_REQUEST, + STATE_READ_REQUEST, +}; + +struct dev_context { + uint64_t samplerate; /* requested samplerate */ + uint64_t limit_samples; /* requested capture length (samples) */ + uint64_t capture_ratio; + + uint64_t channel_mask; /* bit mask of enabled channels */ + uint64_t trigger_mask; /* trigger enable mask */ + uint64_t trigger_edge_mask; /* trigger type mask */ + uint64_t trigger_values; /* trigger level/slope bits */ + + struct soft_trigger_logic *stl; + gboolean trigger_fired; + + int active_fpga_config; /* FPGA configuration index */ + + enum protocol_state state; /* async protocol state */ +}; + +SR_PRIV int la_start_acquisition(const struct sr_dev_inst *sdi); + +#endif diff --git a/src/hardware/sysclk-sla5032/sla5032.c b/src/hardware/sysclk-sla5032/sla5032.c new file mode 100644 index 00000000..620c83ec --- /dev/null +++ b/src/hardware/sysclk-sla5032/sla5032.c @@ -0,0 +1,742 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2019 Vitaliy Vorobyov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "sla5032.h" +#include "protocol.h" + +/* + * Register description (all registers are 32bit): + * + * Rx - means register with index x (register address is x*4) + * + * R0(wr): trigger sel0 (low/high) + * R0(rd): n*256 samples (post trigger) captured + * + * R1(wr): trigger sel1 (level/edge) + * R1(rd): current sampled value + * + * R2(wr): trigger enable mask + * + * R2(rd): (status register) + * b0: 1 - keys entered + * b1: 1 - triggered + * b3: 1 - capture done + * + * not configured: B6FF9C97, 12FF9C97, 92FF9C97, 16FF9C97, ... + * configured: A5A5A5A0, after enter keys A5A5A5A1 + * + * sel1 (one bit per channel): + * 0 - level triggered + * 1 - edge triggered + * + * sel0 (one bit per channel): + * 0 - (low level trigger, sel1=0), (falling edge, sel1=1) + * 1 - (high level trigger, sel1=0), (raising edge, sel1=1) + * + * mask (one bit per channel): + * 0 - disable trigger on channel n + * 1 - enable trigger on channel n + * + * R3: upload base address or num samples (0x300000) + * + * R4: pll divisor - 1 + * 0 - div 1 (no division) + * 1 - div 2 + * 2 - div 3 + * ... + * n-1 - div n + * + * R5(rd/wr): + * b0: 1 - enable pll mul 2, 0 - disable pll mul 2 + * b1: ?? + * b2: ?? + * b3: ?? + * b4: + * b5: ->0->1 upload next data chunk (to pc) + * b6: ?? + * b7: 0 - enable pll mul 1.25, 1 - disable pll mul 1.25 + * b8: ?? + * + * R6: post trigger depth, value x means (x+1)*256 (samples), min value is 1 + * R7: pre trigger depth, value y means (y+1)*256 (samples), min value is 1 + * (x+1)*256 + (y+1)*256 <= 64M + * + * R9: PWM1 HI (1 width-1) + * R10: PWM1 LO (0 width-1) + * + * R11: PWM2 HI (1 width-1) + * R12: PWM2 LO (0 width-1) + * + * R14: + * 1 - start sample? + * 0 - upload done? + * + * R16: rom key 0 + * R17: rom key 1 + * + * key0 is F6 81 13 64 + * key1 is 00 00 00 00 + * + * start sample: + * r5 <= b2 <= 0 + * r5 <= b3 <= 0 + * r5 <= b5 <= 0 + * + * r5 <= b6 <= 1 + * r5 <= b1 <= 1 + * r5 <= b1 <= 0 + * + * r5 <= b8 <= 1 + * r5 <= b8 <= 0 + * + * r5 <= b6 <= 1 + * r5 <= b2 <= 1 + * + * read back: + * r5 <= 0x08 (b3) + * r5 <= 0x28 (b5,b3) + */ + +#define BITSTREAM_NAME "sysclk-sla5032.bit" +#define BITSTREAM_MAX_SIZE (512 * 1024) /* Bitstream size limit for safety */ +#define BITSTREAM_HEADER_SIZE 0x69 +#define FW_CHUNK_SIZE 250 +#define XILINX_SYNC_WORD 0xAA995566 + +static int la_write_cmd_buf(const struct sr_usb_dev_inst *usb, uint8_t cmd, + unsigned int addr, unsigned int len, const void *data) +{ + uint8_t *cmd_pkt; + int ret, xfer_len; + int cmd_len; + + cmd_pkt = g_try_malloc(len + 10); + if (!cmd_pkt) { + ret = SR_ERR_MALLOC; + goto exit; + } + + cmd_pkt[0] = cmd; + cmd_len = 1; + xfer_len = 0; + + switch(cmd) { + case CMD_INIT_FW_UPLOAD: /* init firmware upload */ + break; + case CMD_UPLOAD_FW_CHUNK: + cmd_pkt[1] = len; + cmd_len += 1 + len; + memcpy(&cmd_pkt[2], data, len); + break; + case CMD_READ_REG: /* read register */ + cmd_pkt[1] = addr; + cmd_pkt[2] = len; + cmd_len += 2; + break; + case CMD_WRITE_REG: /* write register */ + cmd_pkt[1] = addr; + cmd_pkt[2] = len; + cmd_len += 2 + len; + memcpy(&cmd_pkt[3], data, len); + break; + case CMD_READ_MEM: /* read mem */ + cmd_pkt[1] = (addr >> 8) & 0xFF; + cmd_pkt[2] = addr & 0xFF; + cmd_pkt[3] = len; + cmd_len += 3; + break; + case CMD_READ_DATA: /* read samples */ + cmd_pkt[1] = addr; + cmd_len += 1; + break; + } + + ret = libusb_bulk_transfer(usb->devhdl, EP_COMMAND, cmd_pkt, cmd_len, + &xfer_len, USB_CMD_TIMEOUT_MS); + if (ret != 0) { + sr_dbg("Failed to send command %d: %s.", + cmd, libusb_error_name(ret)); + return SR_ERR; + } + + if (xfer_len != cmd_len) { + sr_dbg("Invalid send command response of length %d.", xfer_len); + return SR_ERR; + } + +exit: + g_free(cmd_pkt); + return ret; +} + +static int la_read_reg(const struct sr_usb_dev_inst *usb, unsigned int reg, uint32_t *val) +{ + int ret, xfer_len; + uint32_t reply; + + ret = la_write_cmd_buf(usb, CMD_READ_REG, reg * sizeof(uint32_t), + sizeof(reply), NULL); /* rd reg */ + if (ret != SR_OK) + return ret; + + ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY, (uint8_t *)&reply, + sizeof(reply), &xfer_len, USB_REPLY_TIMEOUT_MS); + if (ret != SR_OK) + return ret; + + if (xfer_len != sizeof(uint32_t)) { + sr_dbg("Invalid register read response of length %d.", xfer_len); + return SR_ERR; + } + + *val = GUINT32_FROM_BE(reply); + + return ret; +} + +static int la_write_reg(const struct sr_usb_dev_inst *usb, unsigned int reg, uint32_t val) +{ + int ret; + uint32_t val_be; + + val_be = GUINT32_TO_BE(val); + + ret = la_write_cmd_buf(usb, CMD_WRITE_REG, reg * sizeof(uint32_t), + sizeof(val_be), &val_be); /* wr reg */ + + return ret; +} + +static int la_read_mem(const struct sr_usb_dev_inst *usb, unsigned int addr, unsigned int len, void *data) +{ + int ret, xfer_len; + + ret = la_write_cmd_buf(usb, CMD_READ_MEM, addr, len, NULL); /* rd mem */ + if (ret != SR_OK) + return ret; + + xfer_len = 0; + ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY, (uint8_t *)data, + len, &xfer_len, USB_REPLY_TIMEOUT_MS); + if (xfer_len != (int)len) { + sr_dbg("Invalid memory read response of length %d.", xfer_len); + return SR_ERR; + } + + return ret; +} + +static int la_read_samples(const struct sr_usb_dev_inst *usb, unsigned int addr) +{ + int ret; + + ret = la_write_cmd_buf(usb, CMD_READ_DATA, addr, 0, NULL); /* rd samples */ + return ret; +} + +SR_PRIV int sla5032_set_depth(const struct sr_usb_dev_inst *usb, uint32_t pre, uint32_t post) +{ + int ret; + + /* (pre + 1)*256 + (post + 1)*256 <= 64*1024*1024 */ + ret = la_write_reg(usb, 7, pre); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 6, post); + + return ret; +} + +SR_PRIV int sla5032_set_triggers(const struct sr_usb_dev_inst *usb, + uint32_t trg_value, uint32_t trg_edge_mask, uint32_t trg_mask) +{ + int ret; + + sr_dbg("set trigger: val: %08X, e_mask: %08X, mask: %08X.", trg_value, + trg_edge_mask, trg_mask); + + ret = la_write_reg(usb, 0, trg_value); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 1, trg_edge_mask); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 2, trg_mask); + + return ret; +} + +static int la_set_res_reg_bit(const struct sr_usb_dev_inst *usb, + unsigned int reg, unsigned int bit, unsigned int set_bit) +{ + int ret; + uint32_t v; + + v = 0; + ret = la_read_reg(usb, reg, &v); + if (ret != SR_OK) + return ret; + + if (set_bit) + v |= (1u << bit); + else + v &= ~(1u << bit); + + ret = la_write_reg(usb, reg, v); + + return ret; +} + +struct pll_tbl_entry_t +{ + unsigned int sr; + uint32_t pll_div_minus_1; + unsigned int pll_mul_flags; +}; + +enum { + PLL_MUL2 = 1, /* x2 */ + PLL_MUL1_25 = 2, /* x1.25 */ +}; + +static const struct pll_tbl_entry_t pll_tbl[] = { + { 500000000, 0, PLL_MUL2 | PLL_MUL1_25 }, /* 500M = f*2*1.25/1 */ + { 400000000, 0, PLL_MUL2 }, /* 400M = f*2/1 */ + { 250000000, 0, PLL_MUL1_25 }, /* 250M = f*1.25/1 */ + { 200000000, 0, 0 }, /* 200M = f/1 */ + { 100000000, 1, 0 }, /* 100M = f/2 */ + { 50000000, 3, 0 }, /* 50M = f/4 */ + { 25000000, 7, 0 }, /* 25M = f/8 */ + { 20000000, 9, 0 }, /* 20M = f/10 */ + { 10000000, 19, 0 }, /* 10M = f/20 */ + { 5000000, 39, 0 }, /* 5M = f/40 */ + { 2000000, 99, 0 }, /* 2M = f/100 */ + { 1000000, 199, 0 }, /* 1M = f/200 */ + { 500000, 399, 0 }, /* 500k = f/400 */ + { 200000, 999, 0 }, /* 200k = f/1000 */ + { 100000, 1999, 0 }, /* 100k = f/2000 */ + { 50000, 3999, 0 }, /* 50k = f/4000 */ + { 20000, 9999, 0 }, /* 20k = f/10000 */ + { 10000, 19999, 0 }, /* 10k = f/20000 */ + { 5000, 39999, 0 }, /* 5k = f/40000 */ + { 2000, 99999, 0 }, /* 2k = f/100000 */ +}; + +SR_PRIV int sla5032_set_samplerate(const struct sr_usb_dev_inst *usb, unsigned int sr) +{ + int i, ret; + const struct pll_tbl_entry_t *e; + + e = NULL; + for (i = 0; i < (int)ARRAY_SIZE(pll_tbl); i++) { + if (sr == pll_tbl[i].sr) { + e = &pll_tbl[i]; + break; + } + } + + if (!e) + return SR_ERR_SAMPLERATE; + + sr_dbg("set sample rate: %u.", e->sr); + + ret = la_write_reg(usb, 4, e->pll_div_minus_1); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 0, + (e->pll_mul_flags & PLL_MUL2) ? 1 : 0); /* bit0 (1=en_mul2) */ + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 7, + (e->pll_mul_flags & PLL_MUL1_25) ? 0 : 1); /* bit7 (0=en_mul_1.25) */ + + return ret; +} + +SR_PRIV int sla5032_start_sample(const struct sr_usb_dev_inst *usb) +{ + int ret; + + ret = la_write_reg(usb, 14, 1); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 2, 0); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 3, 0); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 5, 0); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 6, 1); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 1, 1); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 1, 0); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 8, 1); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 8, 0); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 6, 1); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 2, 1); + + return ret; +} + +SR_PRIV int sla5032_get_status(const struct sr_usb_dev_inst *usb, uint32_t status[3]) +{ + int ret; + uint32_t v; + + ret = la_read_reg(usb, 1, &status[0]); + if (ret != SR_OK) + return ret; + + status[1] = 1; /* wait trigger */ + + ret = la_read_reg(usb, 0, &status[2]); + if (ret != SR_OK) + return ret; + + v = 0; + ret = la_read_reg(usb, 2, &v); + if (ret != SR_OK) + return ret; + + if (v & 8) { + status[1] = 3; /* sample done */ + sr_dbg("get status, reg2: %08X.", v); + } else if (v & 2) { + status[1] = 2; /* triggered */ + } + + return ret; +} + +static int la_read_samples_data(const struct sr_usb_dev_inst *usb, void *buf, + unsigned int len, int *xfer_len) +{ + int ret; + + ret = libusb_bulk_transfer(usb->devhdl, EP_DATA, (uint8_t *)buf, len, + xfer_len, USB_DATA_TIMEOUT_MS); + + return ret; +} + +SR_PRIV int sla5032_read_data_chunk(const struct sr_usb_dev_inst *usb, + void *buf, unsigned int len, int *xfer_len) +{ + int ret; + + ret = la_read_samples(usb, 3); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 3, 0x300000); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 4, 0); + if (ret != SR_OK) + return ret; + + ret = la_set_res_reg_bit(usb, 5, 4, 1); + if (ret != SR_OK) + return ret; + + ret = la_read_samples_data(usb, buf, len, xfer_len); + + return ret; +} + +SR_PRIV int sla5032_set_read_back(const struct sr_usb_dev_inst *usb) +{ + int ret; + + ret = la_write_reg(usb, 5, 0x08); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 5, 0x28); + + return ret; +} + +SR_PRIV int sla5032_set_pwm1(const struct sr_usb_dev_inst* usb, uint32_t hi, uint32_t lo) +{ + int ret; + + ret = la_write_reg(usb, 9, hi); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 10, lo); + + return ret; +} + +SR_PRIV int sla5032_set_pwm2(const struct sr_usb_dev_inst* usb, uint32_t hi, uint32_t lo) +{ + int ret; + + ret = la_write_reg(usb, 11, hi); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 12, lo); + + return ret; +} + +SR_PRIV int sla5032_write_reg14_zero(const struct sr_usb_dev_inst* usb) +{ + int ret; + + ret = la_write_reg(usb, 14, 0); + + return ret; +} + +static int la_cfg_fpga_done(const struct sr_usb_dev_inst *usb, unsigned int addr) +{ + uint8_t done_key[8]; + uint32_t k0, k1; + unsigned int reg2; + int ret; + + memset(done_key, 0, sizeof(done_key)); + + ret = la_read_mem(usb, addr, sizeof(done_key), done_key); /* read key from eeprom */ + if (ret != SR_OK) + return ret; + + k0 = RL32(done_key); /* 0x641381F6 */ + k1 = RL32(done_key + 4); /* 0x00000000 */ + + sr_dbg("cfg fpga done, k0: %08X, k1: %08X.", k0, k1); + + ret = la_write_reg(usb, 16, k0); + if (ret != SR_OK) + return ret; + + ret = la_write_reg(usb, 17, k1); + if (ret != SR_OK) + return ret; + + reg2 = 0; + ret = la_read_reg(usb, 2, ®2); + + sr_dbg("cfg fpga done, reg2: %08X.", reg2); + + return ret; +} + +/* + * Load a bitstream file into memory. Returns a newly allocated array + * consisting of a 32-bit length field followed by the bitstream data. + */ +static unsigned char *load_bitstream(struct sr_context *ctx, + const char *name, int *length_p) +{ + struct sr_resource fw; + unsigned char *stream, *fw_data; + ssize_t length, count; + + if (sr_resource_open(ctx, &fw, SR_RESOURCE_FIRMWARE, name) != SR_OK) + return NULL; + + if (fw.size <= BITSTREAM_HEADER_SIZE || fw.size > BITSTREAM_MAX_SIZE) { + sr_err("Refusing to load bitstream of unreasonable size " + "(%" PRIu64 " bytes).", fw.size); + sr_resource_close(ctx, &fw); + return NULL; + } + + stream = g_try_malloc(fw.size); + if (!stream) { + sr_err("Failed to allocate bitstream buffer."); + sr_resource_close(ctx, &fw); + return NULL; + } + + count = sr_resource_read(ctx, &fw, stream, fw.size); + sr_resource_close(ctx, &fw); + + if (count != (ssize_t)fw.size) { + sr_err("Failed to read bitstream '%s'.", name); + g_free(stream); + return NULL; + } + + if (RB32(stream + BITSTREAM_HEADER_SIZE) != XILINX_SYNC_WORD) { + sr_err("Invalid bitstream signature."); + g_free(stream); + return NULL; + } + + length = fw.size - BITSTREAM_HEADER_SIZE + 0x100; + fw_data = g_try_malloc(length); + if (!fw_data) { + sr_err("Failed to allocate bitstream aligned buffer."); + return NULL; + } + + memset(fw_data, 0xFF, 0x100); + memcpy(fw_data + 0x100, stream + BITSTREAM_HEADER_SIZE, + fw.size - BITSTREAM_HEADER_SIZE); + g_free(stream); + + *length_p = length; + + return fw_data; +} + +static int sla5032_is_configured(const struct sr_usb_dev_inst* usb, gboolean *is_configured) +{ + int ret; + uint32_t reg2; + + reg2 = 0; + ret = la_read_reg(usb, 2, ®2); + if (ret == SR_OK) + *is_configured = (reg2 & 0xFFFFFFF1) == 0xA5A5A5A1 ? TRUE : FALSE; + + return ret; +} + +/* Load a Binary File from the firmware directory, transfer it to the device. */ +static int sla5032_send_bitstream(struct sr_context *ctx, + const struct sr_usb_dev_inst *usb, const char *name) +{ + unsigned char *stream; + int ret, length, i, n, m; + uint32_t reg2; + + if (!ctx || !usb || !name) + return SR_ERR_BUG; + + stream = load_bitstream(ctx, name, &length); + if (!stream) + return SR_ERR; + + sr_dbg("Downloading FPGA bitstream '%s'.", name); + + reg2 = 0; + ret = la_read_reg(usb, 2, ®2); + sr_dbg("send bitstream, reg2: %08X.", reg2); + + /* Transfer the entire bitstream in one URB. */ + ret = la_write_cmd_buf(usb, CMD_INIT_FW_UPLOAD, 0, 0, NULL); /* init firmware upload */ + if (ret != SR_OK) { + g_free(stream); + return ret; + } + + n = length / FW_CHUNK_SIZE; + m = length % FW_CHUNK_SIZE; + + for (i = 0; i < n; i++) { + /* upload firmware chunk */ + ret = la_write_cmd_buf(usb, CMD_UPLOAD_FW_CHUNK, 0, + FW_CHUNK_SIZE, &stream[i * FW_CHUNK_SIZE]); + + if (ret != SR_OK) { + g_free(stream); + return ret; + } + } + + if (m != 0) { + /* upload firmware last chunk */ + ret = la_write_cmd_buf(usb, CMD_UPLOAD_FW_CHUNK, 0, m, + &stream[n * FW_CHUNK_SIZE]); + + if (ret != SR_OK) { + g_free(stream); + return ret; + } + } + + g_free(stream); + + la_cfg_fpga_done(usb, 4000); + + sla5032_write_reg14_zero(usb); + + sr_dbg("FPGA bitstream download of %d bytes done.", length); + + return SR_OK; +} + +/* Select and transfer FPGA bitstream for the current configuration. */ +SR_PRIV int sla5032_apply_fpga_config(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct drv_context *drvc; + int ret; + gboolean is_configured; + + devc = sdi->priv; + drvc = sdi->driver->context; + + if (FPGA_NOCONF != devc->active_fpga_config) + return SR_OK; /* No change. */ + + is_configured = FALSE; + ret = sla5032_is_configured(sdi->conn, &is_configured); + if (ret != SR_OK) + return ret; + + if (is_configured) { + devc->active_fpga_config = FPGA_CONF; + return ret; + } + + sr_dbg("FPGA not configured, send bitstream."); + ret = sla5032_send_bitstream(drvc->sr_ctx, sdi->conn, BITSTREAM_NAME); + devc->active_fpga_config = (ret == SR_OK) ? FPGA_CONF : FPGA_NOCONF; + + return ret; +} diff --git a/src/hardware/sysclk-sla5032/sla5032.h b/src/hardware/sysclk-sla5032/sla5032.h new file mode 100644 index 00000000..22d12797 --- /dev/null +++ b/src/hardware/sysclk-sla5032/sla5032.h @@ -0,0 +1,52 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2019 Vitaliy Vorobyov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_HARDWARE_SYSCLK_SLA5032_SLA5032_H +#define LIBSIGROK_HARDWARE_SYSCLK_SLA5032_SLA5032_H + +#include +#include +#include +#include + +/** SLA5032 protocol command ID codes. */ +enum command_id { + CMD_INIT_FW_UPLOAD = 1, + CMD_UPLOAD_FW_CHUNK = 2, + CMD_READ_REG = 3, + CMD_WRITE_REG = 4, + CMD_READ_MEM = 5, + CMD_READ_DATA = 7, +}; + +struct sr_usb_dev_inst; + +SR_PRIV int sla5032_apply_fpga_config(const struct sr_dev_inst* sdi); +SR_PRIV int sla5032_start_sample(const struct sr_usb_dev_inst *usb); +SR_PRIV int sla5032_get_status(const struct sr_usb_dev_inst *usb, uint32_t status[3]); +SR_PRIV int sla5032_set_read_back(const struct sr_usb_dev_inst *usb); +SR_PRIV int sla5032_read_data_chunk(const struct sr_usb_dev_inst *usb, void *buf, unsigned int len, int *xfer_len); +SR_PRIV int sla5032_set_depth(const struct sr_usb_dev_inst *usb, uint32_t pre, uint32_t post); +SR_PRIV int sla5032_set_triggers(const struct sr_usb_dev_inst *usb, uint32_t trg_value, uint32_t trg_edge_mask, uint32_t trg_mask); +SR_PRIV int sla5032_set_samplerate(const struct sr_usb_dev_inst *usb, unsigned int sr); +SR_PRIV int sla5032_set_pwm1(const struct sr_usb_dev_inst *usb, uint32_t hi, uint32_t lo); +SR_PRIV int sla5032_set_pwm2(const struct sr_usb_dev_inst* usb, uint32_t hi, uint32_t lo); +SR_PRIV int sla5032_write_reg14_zero(const struct sr_usb_dev_inst* usb); + +#endif