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>
This commit is contained in:
248
src/bsdl_json.c
Normal file
248
src/bsdl_json.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user