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>
249 lines
7.5 KiB
C
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);
|
|
}
|