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>
155 lines
5.0 KiB
C
155 lines
5.0 KiB
C
/*
|
|
* libbsdl - parser regression test over a synthetic in-memory BSDL.
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*
|
|
* Exercises: entity, IDCODE, port directions, scalar + vector PIN_MAP,
|
|
* TAP_SCAN roles, boundary register, instruction opcodes.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "bsdl/bsdl.h"
|
|
|
|
static int failures = 0;
|
|
|
|
#define CHECK(cond) do { \
|
|
if (!(cond)) { \
|
|
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
|
|
failures++; \
|
|
} \
|
|
} while (0)
|
|
|
|
static const char DEMO[] =
|
|
"entity DEMO is\n"
|
|
" generic (PHYSICAL_PIN_MAP : string := \"PKG\");\n"
|
|
" port (\n"
|
|
" TCK:in bit;\n"
|
|
" TDI:in bit;\n"
|
|
" TDO:out bit;\n"
|
|
" TMS:in bit;\n"
|
|
" Q:out bit_vector(0 to 1);\n"
|
|
" VCC:linkage bit;\n"
|
|
" GND:linkage bit_vector(0 to 1)\n"
|
|
" );\n"
|
|
" use STD_1149_1_2001.all;\n"
|
|
" attribute PIN_MAP of DEMO : entity is PHYSICAL_PIN_MAP;\n"
|
|
" constant PKG : PIN_MAP_STRING :=\n"
|
|
" \"TCK:1,\" &\n"
|
|
" \"TDI:2,\" &\n"
|
|
" \"TDO:3,\" &\n"
|
|
" \"TMS:4,\" &\n"
|
|
" \"Q:(5,6),\" &\n"
|
|
" \"VCC:7,\" &\n"
|
|
" \"GND:(8,9)\";\n"
|
|
" attribute TAP_SCAN_IN of TDI : signal is true;\n"
|
|
" attribute TAP_SCAN_MODE of TMS : signal is true;\n"
|
|
" attribute TAP_SCAN_OUT of TDO : signal is true;\n"
|
|
" attribute TAP_SCAN_CLOCK of TCK : signal is (10.0e6, BOTH);\n"
|
|
" attribute INSTRUCTION_LENGTH of DEMO : entity is 4;\n"
|
|
" attribute INSTRUCTION_OPCODE of DEMO : entity is\n"
|
|
" \"BYPASS (1111),\" &\n"
|
|
" \"EXTEST (0000),\" &\n"
|
|
" \"SAMPLE (0001),\" &\n"
|
|
" \"IDCODE (1110)\";\n"
|
|
" attribute IDCODE_REGISTER of DEMO : entity is\n"
|
|
" \"0001\" &\n"
|
|
" \"0010001101000101\" &\n"
|
|
" \"00000000111\" &\n"
|
|
" \"1\";\n"
|
|
" attribute BOUNDARY_LENGTH of DEMO : entity is 3;\n"
|
|
" attribute BOUNDARY_REGISTER of DEMO : entity is\n"
|
|
" \"0 (BC_1, Q(0), output3, X, 2, 1, Z),\" &\n"
|
|
" \"1 (BC_1, Q(1), output3, X, 2, 1, Z),\" &\n"
|
|
" \"2 (BC_1, *, control, 1)\";\n"
|
|
"end DEMO;\n";
|
|
|
|
static const bsdl_pin_t *pin(const bsdl_t *m, const char *name)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < m->pin_count; i++)
|
|
if (!strcmp(m->pins[i].name, name))
|
|
return &m->pins[i];
|
|
return NULL;
|
|
}
|
|
|
|
static int has_instr(const bsdl_t *m, const char *name, const char *opcode)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < m->instruction_count; i++)
|
|
if (!strcmp(m->instructions[i].name, name) &&
|
|
!strcmp(m->instructions[i].opcode, opcode))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
bsdl_opts_t opts;
|
|
bsdl_t *m;
|
|
const bsdl_pin_t *p;
|
|
|
|
bsdl_opts_default(&opts);
|
|
m = bsdl_parse_buffer(DEMO, sizeof(DEMO) - 1, "demo.bsd", &opts);
|
|
CHECK(m != NULL);
|
|
if (!m) {
|
|
fprintf(stderr, "parse returned NULL\n");
|
|
return 1;
|
|
}
|
|
|
|
/* header */
|
|
CHECK(strcmp(m->entity, "DEMO") == 0);
|
|
CHECK(m->idcode == 0x1234500FUL);
|
|
CHECK(m->idcode_mask == 0xFFFFFFFFUL);
|
|
CHECK(m->instruction_length == 4);
|
|
|
|
/* 4 scalars + Q(0..1) + VCC + GND(0..1) = 9 */
|
|
CHECK(m->pin_count == 9);
|
|
|
|
/* scalar pin + TAP role + physical pin */
|
|
p = pin(m, "TCK");
|
|
CHECK(p && p->dir == BSDL_DIR_IN && p->tap_role == BSDL_TAP_TCK &&
|
|
p->physical_pin && !strcmp(p->physical_pin, "1"));
|
|
p = pin(m, "TDI");
|
|
CHECK(p && p->tap_role == BSDL_TAP_TDI && !strcmp(p->physical_pin, "2"));
|
|
p = pin(m, "TDO");
|
|
CHECK(p && p->dir == BSDL_DIR_OUT && p->tap_role == BSDL_TAP_TDO);
|
|
p = pin(m, "TMS");
|
|
CHECK(p && p->tap_role == BSDL_TAP_TMS);
|
|
|
|
/* vector PIN_MAP, positional: Q(0)->5, Q(1)->6 */
|
|
p = pin(m, "Q(0)");
|
|
CHECK(p && p->dir == BSDL_DIR_OUT && p->physical_pin && !strcmp(p->physical_pin, "5"));
|
|
p = pin(m, "Q(1)");
|
|
CHECK(p && p->physical_pin && !strcmp(p->physical_pin, "6"));
|
|
|
|
/* linkage scalar + vector */
|
|
p = pin(m, "VCC");
|
|
CHECK(p && p->dir == BSDL_DIR_LINKAGE && !strcmp(p->physical_pin, "7"));
|
|
p = pin(m, "GND(0)");
|
|
CHECK(p && p->dir == BSDL_DIR_LINKAGE && !strcmp(p->physical_pin, "8"));
|
|
p = pin(m, "GND(1)");
|
|
CHECK(p && !strcmp(p->physical_pin, "9"));
|
|
|
|
/* boundary register linkage to pins (tristate output cell) */
|
|
CHECK(m->chain_count == 3);
|
|
p = pin(m, "Q(0)");
|
|
CHECK(p && p->out_bit == 0 && p->ctrl_bit == 2);
|
|
p = pin(m, "Q(1)");
|
|
CHECK(p && p->out_bit == 1 && p->ctrl_bit == 2);
|
|
|
|
/* instructions */
|
|
CHECK(has_instr(m, "IDCODE", "1110"));
|
|
CHECK(has_instr(m, "EXTEST", "0000"));
|
|
CHECK(has_instr(m, "BYPASS", "1111"));
|
|
CHECK(has_instr(m, "SAMPLE", "0001"));
|
|
|
|
bsdl_free(m);
|
|
|
|
if (failures) {
|
|
fprintf(stderr, "%d check(s) failed\n", failures);
|
|
return 1;
|
|
}
|
|
printf("all parse checks passed\n");
|
|
return 0;
|
|
}
|