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>
200 lines
6.7 KiB
C
200 lines
6.7 KiB
C
/*
|
|
* 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 */
|