Files
libbsdl/src/bsdl_json.c
François 6b56ab5c42 libbsdl: scaffold + working BSDL parser (struct + JSON, C ABI)
Standalone LGPL-2.1 parser for BSDL (IEEE 1149.1), shared by essim and
bs_explorer. The parser is ported from the Viveris JTAG Core loader,
decoupled from jtag_core, and extended with two extractions the original
lacks:
  - PIN_MAP_STRING -> per-port physical package pin, scalar and vector;
  - TAP_SCAN_*     -> TAP signal roles (TDI/TDO/TMS/TCK/TRST).

Exposes a stable C ABI (bsdl_parse_file/buffer -> bsdl_t, bsdl_to_json)
with a dependency-free JSON serializer and a bsdl2json CLI. CMake builds a
versioned shared library with install/export rules for find_package(bsdl).

Verified against m2gl010t, xcku040 and xcku15p (100% pin mapping, correct
IDCODEs and TAP roles); api and parse regression tests pass, clean build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 10:27:13 +02:00

249 lines
7.5 KiB
C

/*
* libbsdl - JSON serialization (the optional output layer essim consumes).
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* Self-contained: a tiny growable string buffer, no external JSON dependency.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsdl/bsdl.h"
/* ---- growable string buffer ---- */
typedef struct {
char* buf;
size_t len;
size_t cap;
int oom; /* sticky out-of-memory flag */
} sb_t;
static void sb_reserve(sb_t* sb, size_t extra)
{
size_t need;
char* p;
if (sb->oom)
return;
need = sb->len + extra + 1;
if (need <= sb->cap)
return;
while (sb->cap < need)
sb->cap = sb->cap ? sb->cap * 2 : 256;
p = (char*)realloc(sb->buf, sb->cap);
if (!p) { sb->oom = 1; return; }
sb->buf = p;
}
static void sb_putc(sb_t* sb, char c)
{
sb_reserve(sb, 1);
if (sb->oom) return;
sb->buf[sb->len++] = c;
}
static void sb_puts(sb_t* sb, const char* s)
{
size_t n;
if (!s) s = "";
n = strlen(s);
sb_reserve(sb, n);
if (sb->oom) return;
memcpy(sb->buf + sb->len, s, n);
sb->len += n;
}
/* Append a JSON string literal (with quotes) for `s`, or `null` if s == NULL. */
static void sb_json_str(sb_t* sb, const char* s)
{
if (!s) { sb_puts(sb, "null"); return; }
sb_putc(sb, '"');
for (; *s; s++) {
unsigned char c = (unsigned char)*s;
switch (c) {
case '"': sb_puts(sb, "\\\""); break;
case '\\': sb_puts(sb, "\\\\"); break;
case '\b': sb_puts(sb, "\\b"); break;
case '\f': sb_puts(sb, "\\f"); break;
case '\n': sb_puts(sb, "\\n"); break;
case '\r': sb_puts(sb, "\\r"); break;
case '\t': sb_puts(sb, "\\t"); break;
default:
if (c < 0x20) {
char esc[7];
snprintf(esc, sizeof(esc), "\\u%04x", c);
sb_puts(sb, esc);
} else {
sb_putc(sb, (char)c);
}
}
}
sb_putc(sb, '"');
}
static void sb_json_int(sb_t* sb, long v)
{
char tmp[32];
snprintf(tmp, sizeof(tmp), "%ld", v);
sb_puts(sb, tmp);
}
static void sb_json_hex32(sb_t* sb, unsigned long v)
{
char tmp[16];
snprintf(tmp, sizeof(tmp), "\"0x%08lX\"", v & 0xFFFFFFFFUL);
sb_puts(sb, tmp);
}
/* Append `"key":` (object member key). */
static void sb_key(sb_t* sb, const char* k)
{
sb_json_str(sb, k);
sb_putc(sb, ':');
}
/* ---- enum -> token ---- */
static const char* dir_str(bsdl_dir_t d)
{
switch (d) {
case BSDL_DIR_IN: return "in";
case BSDL_DIR_OUT: return "out";
case BSDL_DIR_INOUT: return "inout";
case BSDL_DIR_BUFFER: return "buffer";
case BSDL_DIR_LINKAGE: return "linkage";
default: return "unknown";
}
}
static const char* tap_str(bsdl_tap_role_t t)
{
switch (t) {
case BSDL_TAP_TDI: return "tdi";
case BSDL_TAP_TDO: return "tdo";
case BSDL_TAP_TMS: return "tms";
case BSDL_TAP_TCK: return "tck";
case BSDL_TAP_TRST: return "trst";
default: return "none";
}
}
static const char* cell_str(bsdl_cell_t c)
{
switch (c) {
case BSDL_CELL_BC1: return "BC_1";
case BSDL_CELL_BC2: return "BC_2";
case BSDL_CELL_BC3: return "BC_3";
case BSDL_CELL_BC4: return "BC_4";
case BSDL_CELL_BC5: return "BC_5";
case BSDL_CELL_BC6: return "BC_6";
case BSDL_CELL_BC7: return "BC_7";
default: return "unknown";
}
}
static const char* bittype_str(bsdl_bittype_t b)
{
switch (b) {
case BSDL_BIT_INPUT: return "input";
case BSDL_BIT_OUTPUT: return "output";
case BSDL_BIT_TRISTATE: return "tristate";
case BSDL_BIT_INOUT: return "inout";
case BSDL_BIT_CONTROL: return "control";
case BSDL_BIT_INTERNAL: return "internal";
default: return "unknown";
}
}
static const char* state_str(bsdl_state_t s)
{
switch (s) {
case BSDL_STATE_UNDEF: return "X";
case BSDL_STATE_HIGH: return "1";
case BSDL_STATE_LOW: return "0";
case BSDL_STATE_HIGHZ: return "Z";
default: return "unknown";
}
}
char* bsdl_to_json(const bsdl_t* m)
{
sb_t sb = { NULL, 0, 0, 0 };
size_t i;
if (!m)
return NULL;
sb_putc(&sb, '{');
sb_key(&sb, "entity"); sb_json_str(&sb, m->entity); sb_putc(&sb, ',');
sb_key(&sb, "src_filename"); sb_json_str(&sb, m->src_filename); sb_putc(&sb, ',');
sb_key(&sb, "idcode"); sb_json_hex32(&sb, m->idcode); sb_putc(&sb, ',');
sb_key(&sb, "idcode_mask"); sb_json_hex32(&sb, m->idcode_mask); sb_putc(&sb, ',');
sb_key(&sb, "instruction_length"); sb_json_int(&sb, m->instruction_length); sb_putc(&sb, ',');
/* pins */
sb_key(&sb, "pins");
sb_putc(&sb, '[');
for (i = 0; i < m->pin_count; i++) {
const bsdl_pin_t* p = &m->pins[i];
if (i) sb_putc(&sb, ',');
sb_putc(&sb, '{');
sb_key(&sb, "name"); sb_json_str(&sb, p->name); sb_putc(&sb, ',');
sb_key(&sb, "dir"); sb_json_str(&sb, dir_str(p->dir)); sb_putc(&sb, ',');
sb_key(&sb, "physical_pin"); sb_json_str(&sb, p->physical_pin); sb_putc(&sb, ',');
sb_key(&sb, "tap_role"); sb_json_str(&sb, tap_str(p->tap_role)); sb_putc(&sb, ',');
sb_key(&sb, "in_bit"); sb_json_int(&sb, p->in_bit); sb_putc(&sb, ',');
sb_key(&sb, "out_bit"); sb_json_int(&sb, p->out_bit); sb_putc(&sb, ',');
sb_key(&sb, "ctrl_bit"); sb_json_int(&sb, p->ctrl_bit);
sb_putc(&sb, '}');
}
sb_putc(&sb, ']');
sb_putc(&sb, ',');
/* boundary chain */
sb_key(&sb, "chain");
sb_putc(&sb, '[');
for (i = 0; i < m->chain_count; i++) {
const bsdl_cell_entry_t* c = &m->chain[i];
if (i) sb_putc(&sb, ',');
sb_putc(&sb, '{');
sb_key(&sb, "index"); sb_json_int(&sb, c->index); sb_putc(&sb, ',');
sb_key(&sb, "cell"); sb_json_str(&sb, cell_str(c->cell)); sb_putc(&sb, ',');
sb_key(&sb, "port"); sb_json_str(&sb, c->port); sb_putc(&sb, ',');
sb_key(&sb, "type"); sb_json_str(&sb, bittype_str(c->type)); sb_putc(&sb, ',');
sb_key(&sb, "safe"); sb_json_str(&sb, state_str(c->safe)); sb_putc(&sb, ',');
sb_key(&sb, "ctrl_index"); sb_json_int(&sb, c->ctrl_index); sb_putc(&sb, ',');
sb_key(&sb, "disable_state"); sb_json_str(&sb, state_str(c->disable_state)); sb_putc(&sb, ',');
sb_key(&sb, "disable_result"); sb_json_str(&sb, state_str(c->disable_result));
sb_putc(&sb, '}');
}
sb_putc(&sb, ']');
sb_putc(&sb, ',');
/* instructions */
sb_key(&sb, "instructions");
sb_putc(&sb, '[');
for (i = 0; i < m->instruction_count; i++) {
const bsdl_instr_t* in = &m->instructions[i];
if (i) sb_putc(&sb, ',');
sb_putc(&sb, '{');
sb_key(&sb, "name"); sb_json_str(&sb, in->name); sb_putc(&sb, ',');
sb_key(&sb, "opcode"); sb_json_str(&sb, in->opcode);
sb_putc(&sb, '}');
}
sb_putc(&sb, ']');
sb_putc(&sb, '}');
if (sb.oom) {
free(sb.buf);
return NULL;
}
sb.buf[sb.len] = '\0';
return sb.buf;
}
void bsdl_string_free(char* s)
{
free(s);
}