Skip to content

Commit 9f125b9

Browse files
committed
tools: iio: Add ad9088_cal_dump
A command-line utility for inspecting and validating AD9088 calibration data files. Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
1 parent 311cca0 commit 9f125b9

File tree

3 files changed

+338
-1
lines changed

3 files changed

+338
-1
lines changed

tools/iio/Build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ iio_utils-y += iio_utils.o
22
lsiio-y += lsiio.o iio_utils.o
33
iio_event_monitor-y += iio_event_monitor.o iio_utils.o
44
iio_generic_buffer-y += iio_generic_buffer.o iio_utils.o
5+
ad9088_cal_dump-y += ad9088_cal_dump.o
6+

tools/iio/Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ MAKEFLAGS += -r
1414

1515
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
1616

17-
ALL_TARGETS := iio_event_monitor lsiio iio_generic_buffer
17+
ALL_TARGETS := iio_event_monitor lsiio iio_generic_buffer ad9088_cal_dump
1818
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
1919

2020
all: $(ALL_PROGRAMS)
@@ -55,6 +55,12 @@ $(IIO_GENERIC_BUFFER_IN): prepare FORCE $(OUTPUT)iio_utils-in.o
5555
$(OUTPUT)iio_generic_buffer: $(IIO_GENERIC_BUFFER_IN)
5656
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
5757

58+
AD9088_CAL_DUMP_IN := $(OUTPUT)ad9088_cal_dump-in.o
59+
$(AD9088_CAL_DUMP_IN): prepare FORCE
60+
$(Q)$(MAKE) $(build)=ad9088_cal_dump
61+
$(OUTPUT)ad9088_cal_dump: $(AD9088_CAL_DUMP_IN)
62+
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -lz $< -o $@
63+
5864
clean:
5965
rm -f $(ALL_PROGRAMS)
6066
rm -rf $(OUTPUT)include/linux/iio

tools/iio/ad9088_cal_dump.c

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* AD9088 Calibration Data Dump Tool
4+
*
5+
* Copyright 2025 Analog Devices Inc.
6+
*
7+
* Usage: ad9088_cal_dump <calibration_file>
8+
*
9+
* This tool reads an AD9088 calibration data file and displays:
10+
* - Header information (magic, version, chip ID, configuration)
11+
* - Section offsets and sizes
12+
* - CRC validation
13+
* - Calibration data summary
14+
*/
15+
16+
#include <stdio.h>
17+
#include <stdint.h>
18+
#include <stdlib.h>
19+
#include <string.h>
20+
#include <errno.h>
21+
#include <stdbool.h>
22+
#include <zlib.h> /* For CRC32 calculation */
23+
24+
/* Magic number for AD9088 calibration files */
25+
#define AD9088_CAL_MAGIC 0x41443930 /* "AD90" */
26+
#define AD9088_CAL_VERSION 2
27+
28+
/* Chip IDs */
29+
#define CHIPID_AD9084 0x9084
30+
#define CHIPID_AD9088 0x9088
31+
32+
/* Number of devices */
33+
#define ADI_APOLLO_NUM_ADC_CAL_MODES 2
34+
#define ADI_APOLLO_NUM_JTX_SERDES_12PACKS 4
35+
#define ADI_APOLLO_NUM_SIDES 2
36+
37+
/* Calibration file header structure (must match kernel driver) */
38+
struct ad9088_cal_header {
39+
uint32_t magic; /* Magic number "AD90" */
40+
uint32_t version; /* File format version */
41+
uint32_t chip_id; /* Chip ID (0x9084 or 0x9088) */
42+
uint8_t is_8t8r; /* 1 = 8T8R, 0 = 4T4R */
43+
uint8_t num_adcs; /* Number of ADCs */
44+
uint8_t num_serdes_rx; /* Number of SERDES RX 12-packs */
45+
uint8_t num_clk_cond; /* Number of clock conditioning sides */
46+
uint8_t reserved1[4]; /* Reserved for future use */
47+
48+
/* Section offsets from start of file */
49+
uint32_t adc_cal_offset; /* Offset to ADC cal data */
50+
uint32_t serdes_rx_cal_offset; /* Offset to SERDES RX cal data */
51+
uint32_t clk_cond_cal_offset; /* Offset to clock conditioning cal data */
52+
53+
/* Section sizes */
54+
uint32_t adc_cal_size; /* Total size of all ADC cal data */
55+
uint32_t serdes_rx_cal_size; /* Total size of all SERDES RX cal data */
56+
uint32_t clk_cond_cal_size; /* Total size of all clock conditioning cal data */
57+
58+
uint32_t total_size; /* Total file size including CRC */
59+
uint32_t reserved2[4]; /* Reserved for future use */
60+
} __attribute__((packed));
61+
62+
static const char *chip_id_to_string(uint32_t chip_id)
63+
{
64+
switch (chip_id) {
65+
case CHIPID_AD9084:
66+
return "AD9084";
67+
case CHIPID_AD9088:
68+
return "AD9088";
69+
default:
70+
return "Unknown";
71+
}
72+
}
73+
74+
static void print_header(const struct ad9088_cal_header *hdr)
75+
{
76+
printf("=== AD9088 Calibration Data Header ===\n\n");
77+
78+
printf("Magic Number: 0x%08X ('%c%c%c%c') %s\n",
79+
hdr->magic,
80+
(hdr->magic >> 0) & 0xFF,
81+
(hdr->magic >> 8) & 0xFF,
82+
(hdr->magic >> 16) & 0xFF,
83+
(hdr->magic >> 24) & 0xFF,
84+
hdr->magic == AD9088_CAL_MAGIC ? "[OK]" : "[INVALID]");
85+
86+
printf("Version: %u %s\n",
87+
hdr->version,
88+
hdr->version == AD9088_CAL_VERSION ? "[OK]" : "[UNSUPPORTED]");
89+
90+
printf("Chip ID: 0x%04X (%s)\n",
91+
hdr->chip_id,
92+
chip_id_to_string(hdr->chip_id));
93+
94+
printf("Configuration: %s\n",
95+
hdr->is_8t8r ? "8T8R (8 TX, 8 RX)" : "4T4R (4 TX, 4 RX)");
96+
97+
printf("Number of ADCs: %u\n", hdr->num_adcs);
98+
printf("Number of SERDES RX: %u\n", hdr->num_serdes_rx);
99+
printf("Number of Clk Cond: %u\n", hdr->num_clk_cond);
100+
101+
printf("\n=== Calibration Sections ===\n\n");
102+
103+
printf("ADC Calibration:\n");
104+
printf(" Offset: 0x%08X (%u bytes)\n", hdr->adc_cal_offset, hdr->adc_cal_offset);
105+
printf(" Size: 0x%08X (%u bytes)\n", hdr->adc_cal_size, hdr->adc_cal_size);
106+
if (hdr->num_adcs > 0 && hdr->adc_cal_size > 0) {
107+
uint32_t per_mode = hdr->adc_cal_size / ADI_APOLLO_NUM_ADC_CAL_MODES;
108+
uint32_t per_adc = per_mode / hdr->num_adcs;
109+
printf(" Per Mode: %u bytes\n", per_mode);
110+
printf(" Per ADC: %u bytes\n", per_adc);
111+
}
112+
113+
printf("\nSERDES RX Calibration:\n");
114+
printf(" Offset: 0x%08X (%u bytes)\n", hdr->serdes_rx_cal_offset, hdr->serdes_rx_cal_offset);
115+
printf(" Size: 0x%08X (%u bytes)\n", hdr->serdes_rx_cal_size, hdr->serdes_rx_cal_size);
116+
if (hdr->num_serdes_rx > 0 && hdr->serdes_rx_cal_size > 0) {
117+
uint32_t per_serdes = hdr->serdes_rx_cal_size / hdr->num_serdes_rx;
118+
printf(" Per Pack: %u bytes\n", per_serdes);
119+
}
120+
121+
printf("\nClock Conditioning Calibration:\n");
122+
printf(" Offset: 0x%08X (%u bytes)\n", hdr->clk_cond_cal_offset, hdr->clk_cond_cal_offset);
123+
printf(" Size: 0x%08X (%u bytes)\n", hdr->clk_cond_cal_size, hdr->clk_cond_cal_size);
124+
if (hdr->num_clk_cond > 0 && hdr->clk_cond_cal_size > 0) {
125+
uint32_t per_side = hdr->clk_cond_cal_size / hdr->num_clk_cond;
126+
printf(" Per Side: %u bytes\n", per_side);
127+
}
128+
129+
printf("\nTotal Size: 0x%08X (%u bytes)\n",
130+
hdr->total_size, hdr->total_size);
131+
}
132+
133+
static void print_section_summary(const char *section_name, const uint8_t *data,
134+
uint32_t offset, uint32_t size,
135+
uint32_t num_items, const char *item_name)
136+
{
137+
uint32_t i, per_item;
138+
bool all_zero = true;
139+
bool all_ff = true;
140+
141+
if (size == 0 || num_items == 0) {
142+
printf("\n=== %s: Empty ===\n", section_name);
143+
return;
144+
}
145+
146+
per_item = size / num_items;
147+
148+
printf("\n=== %s ===\n", section_name);
149+
printf("Total size: %u bytes (%u items × %u bytes)\n\n",
150+
size, num_items, per_item);
151+
152+
/* Check for all zeros or all 0xFF (likely uninitialized) */
153+
for (i = 0; i < size && (all_zero || all_ff); i++) {
154+
if (data[i] != 0x00)
155+
all_zero = false;
156+
if (data[i] != 0xFF)
157+
all_ff = false;
158+
}
159+
160+
if (all_zero) {
161+
printf("WARNING: All data is zero (possibly uninitialized)\n");
162+
} else if (all_ff) {
163+
printf("WARNING: All data is 0xFF (possibly uninitialized)\n");
164+
}
165+
166+
/* Print first 16 bytes of each item */
167+
for (i = 0; i < num_items; i++) {
168+
uint32_t item_offset = i * per_item;
169+
uint32_t j, bytes_to_show;
170+
171+
printf("%s %u (offset 0x%08X):\n", item_name, i,
172+
offset + item_offset);
173+
174+
bytes_to_show = per_item < 16 ? per_item : 16;
175+
printf(" ");
176+
for (j = 0; j < bytes_to_show; j++) {
177+
printf("%02X ", data[item_offset + j]);
178+
if ((j + 1) % 16 == 0)
179+
printf("\n ");
180+
}
181+
if (bytes_to_show < per_item)
182+
printf("... (%u more bytes)", per_item - bytes_to_show);
183+
printf("\n");
184+
}
185+
}
186+
187+
static int validate_and_dump(const char *filename)
188+
{
189+
FILE *fp;
190+
uint8_t *data;
191+
size_t file_size, read_size;
192+
struct ad9088_cal_header *hdr;
193+
uint32_t crc_stored = 0, crc_calc = 0;
194+
int ret = 0;
195+
196+
/* Open file */
197+
fp = fopen(filename, "rb");
198+
if (!fp) {
199+
fprintf(stderr, "Error: Cannot open file '%s': %s\n",
200+
filename, strerror(errno));
201+
return 1;
202+
}
203+
204+
/* Get file size */
205+
fseek(fp, 0, SEEK_END);
206+
file_size = ftell(fp);
207+
fseek(fp, 0, SEEK_SET);
208+
209+
printf("File: %s\n", filename);
210+
printf("Size: %zu bytes\n\n", file_size);
211+
212+
/* Check minimum size */
213+
if (file_size < sizeof(struct ad9088_cal_header) + 4) {
214+
fprintf(stderr, "Error: File too small (%zu bytes)\n", file_size);
215+
fclose(fp);
216+
return 1;
217+
}
218+
219+
/* Read entire file */
220+
data = malloc(file_size);
221+
if (!data) {
222+
fprintf(stderr, "Error: Cannot allocate memory\n");
223+
fclose(fp);
224+
return 1;
225+
}
226+
227+
read_size = fread(data, 1, file_size, fp);
228+
fclose(fp);
229+
230+
if (read_size != file_size) {
231+
fprintf(stderr, "Error: Read only %zu of %zu bytes\n",
232+
read_size, file_size);
233+
free(data);
234+
return 1;
235+
}
236+
237+
/* Parse header */
238+
hdr = (struct ad9088_cal_header *)data;
239+
240+
/* Validate magic number */
241+
if (hdr->magic != AD9088_CAL_MAGIC) {
242+
fprintf(stderr, "Error: Invalid magic number 0x%08X (expected 0x%08X)\n",
243+
hdr->magic, AD9088_CAL_MAGIC);
244+
ret = 1;
245+
goto out_print_header;
246+
}
247+
248+
/* Validate version */
249+
if (hdr->version != AD9088_CAL_VERSION) {
250+
fprintf(stderr, "Warning: Unsupported version %u (expected %u)\n",
251+
hdr->version, AD9088_CAL_VERSION);
252+
}
253+
254+
/* Validate total size */
255+
if (hdr->total_size != file_size) {
256+
fprintf(stderr, "Warning: Size mismatch - header says %u, file is %zu\n",
257+
hdr->total_size, file_size);
258+
}
259+
260+
/* Extract and verify CRC */
261+
memcpy(&crc_stored, data + file_size - 4, 4);
262+
crc_calc = crc32(0L, data, file_size - 4);
263+
264+
printf("=== CRC Validation ===\n\n");
265+
printf("Stored CRC: 0x%08X\n", crc_stored);
266+
printf("Calculated CRC: 0x%08X\n", crc_calc);
267+
printf("Status: %s\n\n", crc_stored == crc_calc ? "[OK]" : "[FAILED]");
268+
269+
if (crc_stored != crc_calc) {
270+
fprintf(stderr, "Error: CRC mismatch!\n");
271+
ret = 1;
272+
}
273+
274+
out_print_header:
275+
/* Print header information */
276+
print_header(hdr);
277+
278+
/* Print section summaries if CRC is valid */
279+
if (crc_stored == crc_calc && ret == 0) {
280+
/* ADC calibration */
281+
if (hdr->adc_cal_size > 0 && hdr->adc_cal_offset < file_size) {
282+
uint32_t num_items = hdr->num_adcs * ADI_APOLLO_NUM_ADC_CAL_MODES;
283+
print_section_summary("ADC Calibration Data",
284+
data + hdr->adc_cal_offset,
285+
hdr->adc_cal_offset,
286+
hdr->adc_cal_size,
287+
num_items,
288+
"ADC Chan/Mode");
289+
}
290+
291+
/* SERDES RX calibration */
292+
if (hdr->serdes_rx_cal_size > 0 && hdr->serdes_rx_cal_offset < file_size) {
293+
print_section_summary("SERDES RX Calibration Data",
294+
data + hdr->serdes_rx_cal_offset,
295+
hdr->serdes_rx_cal_offset,
296+
hdr->serdes_rx_cal_size,
297+
hdr->num_serdes_rx,
298+
"SERDES RX Pack");
299+
}
300+
301+
/* Clock conditioning calibration */
302+
if (hdr->clk_cond_cal_size > 0 && hdr->clk_cond_cal_offset < file_size) {
303+
print_section_summary("Clock Conditioning Calibration Data",
304+
data + hdr->clk_cond_cal_offset,
305+
hdr->clk_cond_cal_offset,
306+
hdr->clk_cond_cal_size,
307+
hdr->num_clk_cond,
308+
"Clk Cond Side");
309+
}
310+
}
311+
312+
printf("\n");
313+
free(data);
314+
return ret;
315+
}
316+
317+
int main(int argc, char *argv[])
318+
{
319+
if (argc != 2) {
320+
fprintf(stderr, "Usage: %s <calibration_file>\n", argv[0]);
321+
fprintf(stderr, "\n");
322+
fprintf(stderr, "Example:\n");
323+
fprintf(stderr, " %s /lib/firmware/ad9088_cal.bin\n", argv[0]);
324+
fprintf(stderr, "\n");
325+
return 1;
326+
}
327+
328+
return validate_and_dump(argv[1]);
329+
}

0 commit comments

Comments
 (0)