2 * This file is part of the libsigrok project.
4 * Copyright (C) 2015 Soeren Apel <soeren@apelpie.net>
5 * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * This input module reads .ad files created using the
24 * following practice commands:
26 * I.SAVE <file> /NoCompress
27 * IPROBE.SAVE <file> /NoCompress
29 * It currently cannot make use of files that have been
30 * saved using /QuickCompress, /Compress or /ZIP.
31 * As a workaround you may load the file in PowerView
32 * using I.LOAD / IPROBE.LOAD and re-save using /NoCompress.
38 #include <sys/types.h>
43 #include <libsigrok/libsigrok.h>
44 #include "libsigrok-internal.h"
46 #define LOG_PREFIX "input/trace32_ad"
48 #define CHUNK_SIZE (4 * 1024 * 1024)
49 #define MAX_POD_COUNT 12
50 #define HEADER_SIZE 80
52 #define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
55 * The resolution equals a sampling freq of 12.8 GHz. That's a bit high
56 * for inter-record sample generation, so we scale it down to 200 MHz
57 * for now. That way, the scaling factor becomes 32.
59 #define DEFAULT_SAMPLERATE 200
62 AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
63 AD_FORMAT_TXTHDR /* Textual header, binary data */
67 AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
68 /* LA-394x PowerIntegrator II. */
69 AD_DEVICE_IPROBE /* Data recorded by LA-769x PowerTrace II IProbe. */
70 /* Missing file format info for LA-793x ICD PowerProbe */
71 /* Missing file format info for LA-4530 uTrace analog probe */
80 AD_COMPR_NONE = 0, /* File created with /NOCOMPRESS */
81 AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
86 gboolean header_read, records_read, trigger_sent;
87 char format, device, record_mode, compression;
88 char pod_status[MAX_POD_COUNT];
89 struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
90 uint64_t trigger_timestamp;
91 uint32_t record_size, record_count, cur_record;
94 double timestamp_scale;
98 static int process_header(GString *buf, struct context *inc);
99 static void create_channels(struct sr_input *in);
101 static char get_pod_name_from_id(int id)
117 sr_err("get_pod_name_from_id() called with invalid ID %d!", id);
122 static int init(struct sr_input *in, GHashTable *options)
128 in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
129 in->priv = g_malloc0(sizeof(struct context));
133 /* Calculate the desired timestamp scaling factor. */
134 inc->samplerate = 1000000 *
135 g_variant_get_uint32(g_hash_table_lookup(options, "samplerate"));
137 inc->timestamp_scale = ((1 / TIMESTAMP_RESOLUTION) / (double)inc->samplerate);
139 /* Enable the pods the user chose to see. */
140 for (pod = 0; pod < MAX_POD_COUNT; pod++) {
141 g_snprintf(id, sizeof(id), "pod%c", get_pod_name_from_id(pod));
142 if (g_variant_get_boolean(g_hash_table_lookup(options, id)))
143 inc->pod_status[pod] = 1;
147 if (g_slist_length(in->sdi->channels) == 0) {
148 sr_err("No pods were selected and thus no channels created, aborting.");
154 inc->out_buf = g_string_sized_new(CHUNK_SIZE);
159 static int format_match(GHashTable *metadata, unsigned int *confidence)
164 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
165 rc = process_header(buf, NULL);
174 static int process_header(GString *buf, struct context *inc)
176 char *format_name, *format_name_sig;
177 int i, record_size, device_id;
180 * 00-31 (0x00-1F) file format name
181 * 32-39 (0x20-27) trigger timestamp u64
182 * 40-47 (0x28-2F) unused
183 * 48 (0x30) compression
185 * 50 (0x32) 0x00 (PI), 0x01 (iprobe)
186 * 54 (0x36) 0x08 (PI 250/500), 0x0A (iprobe 250)
187 * 55 (0x37) 0x00 (250), 0x01 (500)
188 * 56 (0x38) record size u8
189 * 57-59 (0x39-3B) const 0x00
190 * 60-63 (0x3C-3F) number of records u32
191 * 64-67 (0x40-43) id of last record s32
193 * 71 (0x47) const 0x80=128
194 * 72 (0x48) const 0x01
198 /* Note: inc is off-limits until we check whether it's a valid pointer. */
200 format_name = g_strndup(buf->str, 32);
202 /* File format name ends on 0x20/0x1A, let's remove both. */
203 for (i = 1; i < 31; i++) {
204 if (format_name[i] == 0x1A) {
205 format_name[i - 1] = 0;
209 g_strchomp(format_name); /* This is for additional padding spaces. */
211 format_name_sig = g_strndup(format_name, 5);
213 /* Desired file formats either start with digit+space or "trace32". */
214 if (g_strcmp0(format_name_sig, "trace32")) {
216 inc->format = AD_FORMAT_BINHDR;
217 } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == 0x20)) {
219 inc->format = AD_FORMAT_TXTHDR;
220 g_free(format_name_sig);
223 sr_err("This format isn't implemented yet, aborting.");
225 sr_dbg("Not a supported trace32 input file.");
228 g_free(format_name_sig);
231 sr_err("Don't know this file format, aborting.");
233 sr_dbg("Not a trace32 input file.");
237 sr_dbg("File says it's \"%s\"", format_name);
239 record_size = R8(buf->str + 56);
242 if (g_strcmp0(format_name, "trace32 power integrator data") == 0) {
243 if (record_size == 28 || record_size == 45)
244 device_id = AD_DEVICE_PI;
245 } else if (g_strcmp0(format_name, "trace32 iprobe data") == 0) {
246 if (record_size == 11)
247 device_id = AD_DEVICE_IPROBE;
251 g_free(format_name_sig);
253 sr_err("Don't know how to handle this file with record size %d.",
258 g_free(format_name_sig);
261 /* Stop processing the header if we just want to identify the file. */
265 inc->device = device_id;
266 inc->trigger_timestamp = RL64(buf->str + 32);
267 inc->compression = R8(buf->str + 48); /* Maps to the enum. */
268 inc->record_mode = R8(buf->str + 55); /* Maps to the enum. */
269 inc->record_size = record_size;
270 inc->record_count = RL32(buf->str + 60);
271 inc->last_record = RL32S(buf->str + 64);
273 sr_dbg("Trigger occured at %lf s.",
274 inc->trigger_timestamp * TIMESTAMP_RESOLUTION);
275 sr_dbg("File contains %d records: first one is %d, last one is %d.",
276 inc->record_count, (inc->last_record - inc->record_count + 1),
279 /* Check if we can work with this compression. */
280 if (inc->compression != AD_COMPR_NONE) {
281 sr_err("File uses unsupported compression (0x%02X), can't continue.",
286 inc->header_read = TRUE;
291 static void create_channels(struct sr_input *in)
294 int pod, channel, chan_id;
300 for (pod = 0; pod < MAX_POD_COUNT; pod++) {
301 if (!inc->pod_status[pod])
304 for (channel = 0; channel < 16; channel++) {
305 snprintf(name, sizeof(name), "%c%d", get_pod_name_from_id(pod), channel);
306 inc->channels[pod][channel] =
307 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
311 snprintf(name, sizeof(name), "CLK%c", get_pod_name_from_id(pod));
312 inc->channels[pod][16] =
313 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
318 static void send_metadata(struct sr_input *in)
320 struct sr_datafeed_packet packet;
321 struct sr_datafeed_meta meta;
322 struct sr_config *src;
327 packet.type = SR_DF_META;
328 packet.payload = &meta;
329 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
330 meta.config = g_slist_append(NULL, src);
331 sr_session_send(in->sdi, &packet);
332 g_slist_free(meta.config);
335 inc->meta_sent = TRUE;
338 static void flush_output_buffer(struct sr_input *in)
341 struct sr_datafeed_packet packet;
342 struct sr_datafeed_logic logic;
346 if (inc->out_buf->len) {
347 packet.type = SR_DF_LOGIC;
348 packet.payload = &logic;
349 logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
350 logic.data = inc->out_buf->str;
351 logic.length = inc->out_buf->len;
352 sr_session_send(in->sdi, &packet);
354 g_string_truncate(inc->out_buf, 0);
358 static void process_record_pi(struct sr_input *in, gsize start)
360 struct sr_datafeed_packet packet;
362 uint64_t timestamp, next_timestamp;
364 char single_payload[12 * 3];
366 int i, pod_count, clk_offset, packet_count, pod;
367 int payload_bit, payload_len, value;
381 * 24-25 J15..0 Not present in 500MHz mode
382 * 26-27 K15..0 Not present in 500MHz mode
383 * 28-29 L15..0 Not present in 500MHz mode
384 * 30-31 M15..0 Not present in 500MHz mode
385 * 32-33 N15..0 Not present in 500MHz mode
386 * 34-35 O15..0 Not present in 500MHz mode
387 * 36-39 ?? Not present in 500MHz mode
388 * 40/24 CLKF..A (32=CLKF, .., 1=CLKA)
389 * 41 CLKO..J (32=CLKO, .., 1=CLKJ) Not present in 500MHz mode
395 timestamp = RL64(buf->str + start);
397 if (inc->record_mode == AD_MODE_500MHZ) {
407 single_payload[0] = 0;
409 for (pod = 0; pod < pod_count; pod++) {
410 if (!inc->pod_status[pod])
415 pod_data = RL16(buf->str + start + 8);
416 pod_data |= (RL16(buf->str + start + clk_offset) & 1) << 16;
419 pod_data = RL16(buf->str + start + 10);
420 pod_data |= (RL16(buf->str + start + clk_offset) & 2) << 15;
423 pod_data = RL16(buf->str + start + 12);
424 pod_data |= (RL16(buf->str + start + clk_offset) & 4) << 14;
427 pod_data = RL16(buf->str + start + 14);
428 pod_data |= (RL16(buf->str + start + clk_offset) & 8) << 13;
431 pod_data = RL16(buf->str + start + 16);
432 pod_data |= (RL16(buf->str + start + clk_offset) & 16) << 12;
435 pod_data = RL16(buf->str + start + 18);
436 pod_data |= (RL16(buf->str + start + clk_offset) & 32) << 11;
439 pod_data = RL16(buf->str + start + 24);
440 pod_data |= (RL16(buf->str + start + 41) & 1) << 16;
443 pod_data = RL16(buf->str + start + 26);
444 pod_data |= (RL16(buf->str + start + 41) & 2) << 15;
447 pod_data = RL16(buf->str + start + 28);
448 pod_data |= (RL16(buf->str + start + 41) & 4) << 14;
451 pod_data = RL16(buf->str + start + 30);
452 pod_data |= (RL16(buf->str + start + 41) & 8) << 13;
455 pod_data = RL16(buf->str + start + 32);
456 pod_data |= (RL16(buf->str + start + 41) & 16) << 12;
459 pod_data = RL16(buf->str + start + 34);
460 pod_data |= (RL16(buf->str + start + 41) & 32) << 11;
463 sr_err("Don't know how to obtain data for pod %d.", pod);
466 for (i = 0; i < 17; i++) {
467 value = (pod_data >> i) & 1;
468 single_payload[payload_len] |= value << payload_bit;
471 if (payload_bit > 7) {
474 single_payload[payload_len] = 0;
479 /* Make sure that payload_len accounts for any incomplete bytes used. */
483 i = (g_slist_length(in->sdi->channels) + 7) / 8;
484 if (payload_len != i) {
485 sr_err("Payload unit size is %d but should be %d!", payload_len, i);
489 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
490 sr_dbg("Trigger @%lf s, record #%d.",
491 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
493 packet.type = SR_DF_TRIGGER;
494 packet.payload = NULL;
495 sr_session_send(in->sdi, &packet);
496 inc->trigger_sent = TRUE;
499 /* Is this the last record in the file? */
500 if (inc->cur_record == inc->record_count - 1) {
501 /* It is, so send the last sample data only once. */
502 g_string_append_len(inc->out_buf, single_payload, payload_len);
504 /* It's not, so fill the time gap by sending lots of data. */
505 next_timestamp = RL64(buf->str + start + inc->record_size);
506 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
508 /* Make sure we send at least one data set. */
509 if (packet_count == 0)
512 for (i = 0; i < packet_count; i++)
513 g_string_append_len(inc->out_buf, single_payload, payload_len);
516 if (inc->out_buf->len >= CHUNK_SIZE)
517 flush_output_buffer(in);
520 static void process_record_iprobe(struct sr_input *in, gsize start)
522 struct sr_datafeed_packet packet;
524 uint64_t timestamp, next_timestamp;
525 char single_payload[3];
526 int i, payload_len, packet_count;
536 timestamp = RL64(in->buf->str + start);
537 single_payload[0] = R8(in->buf->str + start + 8);
538 single_payload[1] = R8(in->buf->str + start + 9);
539 single_payload[2] = R8(in->buf->str + start + 10) & 1;
542 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
543 sr_dbg("Trigger @%lf s, record #%d.",
544 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
546 packet.type = SR_DF_TRIGGER;
547 packet.payload = NULL;
548 sr_session_send(in->sdi, &packet);
549 inc->trigger_sent = TRUE;
552 /* Is this the last record in the file? */
553 if (inc->cur_record == inc->record_count - 1) {
554 /* It is, so send the last sample data only once. */
555 g_string_append_len(inc->out_buf, single_payload, payload_len);
557 /* It's not, so fill the time gap by sending lots of data. */
558 next_timestamp = RL64(in->buf->str + start + inc->record_size);
559 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
561 /* Make sure we send at least one data set. */
562 if (packet_count == 0)
565 for (i = 0; i < packet_count; i++)
566 g_string_append_len(inc->out_buf, single_payload, payload_len);
569 if (inc->out_buf->len >= CHUNK_SIZE)
570 flush_output_buffer(in);
573 static void process_practice_token(struct sr_input *in, char *cmd_token)
577 char chan_suffix[2], chan_name[33];
580 struct sr_channel *channel;
585 * Commands of interest (I may also be IPROBE):
590 * I.TYSNC.SELECT I.A0 HIGH
591 * NAME.SET <port.chan> <name> <+/-> ...
597 if (cmd_token[0] == 0)
600 tokens = g_strsplit(cmd_token, " ", 0);
605 if (g_strcmp0(tokens[0], "NAME.SET") == 0) {
606 /* Let the user know when the channel has been inverted. */
607 /* This *should* be token #3 but there's an additonal space, making it #4. */
611 if (tokens[4][0] == '-')
612 chan_suffix[0] = '-'; /* This is the way PowerView shows it. */
616 * Command is using structure "NAME.SET I.A00 I.XYZ" or
617 * "NAME.SET IP.00 IP.XYZ", depending on the device used.
618 * Let's get strings with the I./IP. from both tokens removed.
620 s1 = g_strstr_len(tokens[1], -1, ".") + 1;
621 s2 = g_strstr_len(tokens[2], -1, ".") + 1;
623 if (g_strcmp0(s1, "CLK") == 0) {
627 } else if ((strlen(s1) == 4) && g_ascii_isupper(s1[3])) {
628 /* CLKA/B/J/K for PowerIntegrator */
629 pod = s1[3] - (char)'A';
631 } else if (g_ascii_isupper(s1[0])) {
632 /* A00 for PowerIntegrator */
633 pod = s1[0] - (char)'A';
641 channel = inc->channels[pod][ch];
642 g_snprintf(chan_name, sizeof(chan_name), "%s%s", s2, chan_suffix);
644 sr_dbg("Changing channel name for %s to %s.", s1, chan_name);
645 sr_dev_channel_name_set(channel, chan_name);
651 static void process_practice(struct sr_input *in)
654 char **tokens, *token;
657 /* Gather all input data until we see the end marker. */
658 if (in->buf->str[in->buf->len - 1] != 0x29)
665 tokens = g_strsplit(in->buf->str, delimiter, 0);
667 /* Special case: first token contains the start marker, too. Skip it. */
669 for (i = 0; token[i]; i++) {
671 process_practice_token(in, token + i + 1);
674 for (i = 1; tokens[i]; i++)
675 process_practice_token(in, tokens[i]);
679 g_string_erase(in->buf, 0, in->buf->len);
682 static int process_buffer(struct sr_input *in)
685 int i, chunk_size, res;
689 if (!inc->header_read) {
690 res = process_header(in->buf, inc);
691 g_string_erase(in->buf, 0, HEADER_SIZE);
696 if (!inc->meta_sent) {
697 std_session_send_df_header(in->sdi);
701 if (!inc->records_read) {
702 /* Cut off at a multiple of the record size. */
703 chunk_size = ((in->buf->len) / inc->record_size) * inc->record_size;
705 /* There needs to be at least one more record process_record() can peek into. */
706 chunk_size -= inc->record_size;
708 for (i = 0; (i < chunk_size) && (!inc->records_read); i += inc->record_size) {
709 switch (inc->device) {
711 process_record_pi(in, i);
713 case AD_DEVICE_IPROBE:
714 process_record_iprobe(in, i);
717 sr_err("Trying to process records for unknown device!");
722 if (inc->cur_record == inc->record_count)
723 inc->records_read = TRUE;
726 g_string_erase(in->buf, 0, i);
729 if (inc->records_read) {
730 /* Read practice commands that configure the setup. */
731 process_practice(in);
737 static int receive(struct sr_input *in, GString *buf)
739 g_string_append_len(in->buf, buf->str, buf->len);
741 if (!in->sdi_ready) {
742 /* sdi is ready, notify frontend. */
743 in->sdi_ready = TRUE;
747 return process_buffer(in);
750 static int end(struct sr_input *in)
758 ret = process_buffer(in);
762 flush_output_buffer(in);
765 std_session_send_df_end(in->sdi);
770 static int reset(struct sr_input *in)
772 struct context *inc = in->priv;
774 inc->meta_sent = FALSE;
775 inc->header_read = FALSE;
776 inc->records_read = FALSE;
777 inc->trigger_sent = FALSE;
780 g_string_truncate(in->buf, 0);
785 static struct sr_option options[] = {
786 { "podA", "Import pod A / iprobe",
787 "Create channels and data for pod A / iprobe", NULL, NULL },
789 { "podB", "Import pod B", "Create channels and data for pod B", NULL, NULL },
790 { "podC", "Import pod C", "Create channels and data for pod C", NULL, NULL },
791 { "podD", "Import pod D", "Create channels and data for pod D", NULL, NULL },
792 { "podE", "Import pod E", "Create channels and data for pod E", NULL, NULL },
793 { "podF", "Import pod F", "Create channels and data for pod F", NULL, NULL },
794 { "podJ", "Import pod J", "Create channels and data for pod J", NULL, NULL },
795 { "podK", "Import pod K", "Create channels and data for pod K", NULL, NULL },
796 { "podL", "Import pod L", "Create channels and data for pod L", NULL, NULL },
797 { "podM", "Import pod M", "Create channels and data for pod M", NULL, NULL },
798 { "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
799 { "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
801 { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
806 static const struct sr_option *get_options(void)
808 if (!options[0].def) {
809 options[0].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
810 options[1].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
811 options[2].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
812 options[3].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
813 options[4].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
814 options[5].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
815 options[6].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
816 options[7].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
817 options[8].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
818 options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
819 options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
820 options[11].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
821 options[12].def = g_variant_ref_sink(g_variant_new_uint32(DEFAULT_SAMPLERATE));
827 SR_PRIV struct sr_input_module input_trace32_ad = {
829 .name = "Trace32_ad",
830 .desc = "Lauterbach Trace32 logic analyzer data",
831 .exts = (const char*[]){"ad", NULL},
832 .options = get_options,
833 .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
834 .format_match = format_match,