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:
2026-06-03 10:27:13 +02:00
parent c78ff82923
commit 6b56ab5c42
16 changed files with 2764 additions and 0 deletions

199
include/bsdl/bsdl.h Normal file
View File

@@ -0,0 +1,199 @@
/*
* libbsdl - Standalone BSDL (IEEE 1149.1) parser with a stable C ABI.
*
* Copyright (c) 2026 François
* Seeded from the Viveris JTAG Core BSDL loader (Jean-François DEL NERO).
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation. See the LICENSE file.
*/
/**
* @file bsdl.h
* @brief Public C API for libbsdl: parse a .bsd file into a struct, or to JSON.
*
* Design notes
* ------------
* - Pure C ABI: callable as-is from C (bs_explorer) and, via `extern "C"`,
* from C++ (essim). Keep this header free of C++/struct-layout churn.
* - `bsdl_t` is a SUPERSET model; consumers read only the fields they need.
* Extend it additively (append fields/enum values) — never reorder/remove —
* so the shared .so stays ABI-stable for both projects.
* - Ownership: every pointer reachable from a `bsdl_t*` is owned by the model
* and released by bsdl_free(). Do not free individual members.
*/
#ifndef LIBBSDL_BSDL_H
#define LIBBSDL_BSDL_H
#include <stddef.h>
/* -------- version -------- */
#define BSDL_VERSION_MAJOR 0
#define BSDL_VERSION_MINOR 1
#define BSDL_VERSION_PATCH 0
#define BSDL_VERSION_STRING "0.1.0"
/* -------- symbol visibility / export -------- */
#if defined(_WIN32)
# if defined(BSDL_BUILD_SHARED)
# define BSDL_API __declspec(dllexport)
# elif !defined(BSDL_STATIC)
# define BSDL_API __declspec(dllimport)
# else
# define BSDL_API
# endif
#else
# if defined(BSDL_BUILD_SHARED)
# define BSDL_API __attribute__((visibility("default")))
# else
# define BSDL_API
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* -------- enumerations (stable values; append-only) -------- */
/** Logical port direction (from the VHDL port clause). */
typedef enum bsdl_dir {
BSDL_DIR_UNKNOWN = 0,
BSDL_DIR_IN,
BSDL_DIR_OUT,
BSDL_DIR_INOUT,
BSDL_DIR_BUFFER,
BSDL_DIR_LINKAGE /**< power / ground / NC — Viveris folds these to UNKNOWN. */
} bsdl_dir_t;
/** TAP signal role, derived from the TAP_SCAN_* attributes. */
typedef enum bsdl_tap_role {
BSDL_TAP_NONE = 0,
BSDL_TAP_TDI, /**< TAP_SCAN_IN */
BSDL_TAP_TDO, /**< TAP_SCAN_OUT */
BSDL_TAP_TMS, /**< TAP_SCAN_MODE */
BSDL_TAP_TCK, /**< TAP_SCAN_CLOCK */
BSDL_TAP_TRST /**< TAP_SCAN_RESET */
} bsdl_tap_role_t;
/** Boundary-register cell type (BC_1 .. BC_7). */
typedef enum bsdl_cell {
BSDL_CELL_UNKNOWN = 0,
BSDL_CELL_BC1, BSDL_CELL_BC2, BSDL_CELL_BC3, BSDL_CELL_BC4,
BSDL_CELL_BC5, BSDL_CELL_BC6, BSDL_CELL_BC7
} bsdl_cell_t;
/** Boundary-register cell function. */
typedef enum bsdl_bittype {
BSDL_BIT_UNKNOWN = 0,
BSDL_BIT_INPUT,
BSDL_BIT_OUTPUT,
BSDL_BIT_TRISTATE,
BSDL_BIT_INOUT,
BSDL_BIT_CONTROL,
BSDL_BIT_INTERNAL
} bsdl_bittype_t;
/** Safe / disable logic state. */
typedef enum bsdl_state {
BSDL_STATE_UNKNOWN = 0,
BSDL_STATE_UNDEF, /**< 'X' */
BSDL_STATE_HIGH, /**< '1' */
BSDL_STATE_LOW, /**< '0' */
BSDL_STATE_HIGHZ /**< 'Z' */
} bsdl_state_t;
/* -------- model structures -------- */
/** A logical port plus its physical mapping and boundary-cell links. */
typedef struct bsdl_pin {
const char* name; /**< logical port name, e.g. "TCK", "IO_A2". */
bsdl_dir_t dir;
const char* physical_pin; /**< package ball/pin from PIN_MAP_STRING, e.g. "W20"; NULL if unmapped. */
bsdl_tap_role_t tap_role; /**< non-NONE if this port is a TAP signal. */
int in_bit; /**< boundary-register input bit index, or -1. */
int out_bit; /**< boundary-register output bit index, or -1. */
int ctrl_bit; /**< controlling bit index, or -1. */
} bsdl_pin_t;
/** One entry of the BOUNDARY_REGISTER. */
typedef struct bsdl_cell_entry {
int index;
bsdl_cell_t cell;
const char* port; /**< associated port name, or "*" when unnamed. */
bsdl_bittype_t type;
bsdl_state_t safe;
int ctrl_index; /**< controlling cell index, or -1. */
bsdl_state_t disable_state;
bsdl_state_t disable_result;
} bsdl_cell_entry_t;
/** One INSTRUCTION_OPCODE entry. */
typedef struct bsdl_instr {
const char* name; /**< e.g. "IDCODE", "EXTEST", "BYPASS", "SAMPLE". */
const char* opcode; /**< bit string, e.g. "001000". */
} bsdl_instr_t;
/** The parsed device model. All pointers owned by the model (see bsdl_free). */
typedef struct bsdl {
const char* entity; /**< entity name (device identifier). */
const char* src_filename; /**< basename of the source file. */
unsigned long idcode; /**< IDCODE value (X bits read as 0). */
unsigned long idcode_mask; /**< 1 where the IDCODE bit is fixed, 0 where 'X'. */
int instruction_length;
bsdl_pin_t* pins; size_t pin_count;
bsdl_cell_entry_t* chain; size_t chain_count;
bsdl_instr_t* instructions; size_t instruction_count;
} bsdl_t;
/* -------- options & diagnostics -------- */
typedef enum bsdl_log_level {
BSDL_LOG_ERROR = 0,
BSDL_LOG_WARN,
BSDL_LOG_INFO,
BSDL_LOG_DEBUG
} bsdl_log_level_t;
/** Optional diagnostic sink. Replaces Viveris' jtag_core logging dependency. */
typedef void (*bsdl_log_fn)(bsdl_log_level_t level, const char* msg, void* user);
typedef struct bsdl_opts {
int sort_pins_by_name; /**< replaces the BSDL_LOADER_SORT_PINS_NAME env var. */
bsdl_log_fn log; /**< may be NULL. */
void* log_user; /**< passed back to `log`. */
} bsdl_opts_t;
/** Fill `opts` with defaults (no sorting, no logging). */
BSDL_API void bsdl_opts_default(bsdl_opts_t* opts);
/* -------- API -------- */
/** Parse a BSDL file. Returns NULL on error. Free with bsdl_free(). */
BSDL_API bsdl_t* bsdl_parse_file(const char* path, const bsdl_opts_t* opts);
/** Parse a BSDL document held in memory. `name` is recorded as src_filename. */
BSDL_API bsdl_t* bsdl_parse_buffer(const char* text, size_t len,
const char* name, const bsdl_opts_t* opts);
/** Release a model returned by a bsdl_parse_* function. NULL-safe. */
BSDL_API void bsdl_free(bsdl_t* model);
/** Serialize a model to a freshly allocated JSON string (compact, UTF-8).
* Returns NULL on allocation failure or NULL input. Free with bsdl_string_free(). */
BSDL_API char* bsdl_to_json(const bsdl_t* model);
/** Free a string returned by bsdl_to_json(). NULL-safe. */
BSDL_API void bsdl_string_free(char* s);
/** Library version, e.g. "0.1.0". */
BSDL_API const char* bsdl_version(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LIBBSDL_BSDL_H */