import yaml from "js-yaml"; const PROTOCOLS = ["uart", "spi", "i2c", "axi4lite", "apb", "wishbone", "custom"]; const DEFAULT_YAML = `# UVM Testbench Generator — UART 16550 Example # Production-ready specification for full UVM TB generation design_name: uart protocol: uart version: "1.5" vendor: "Verification IP" description: "16550 Compatible UART Controller" clock_reset: clock: clk reset: rst_n reset_active: 0 interfaces: - name: uart_intf signals: - { name: wb_cyc, direction: input, width: 1, description: "Wishbone cycle" } - { name: wb_stb, direction: input, width: 1, description: "Wishbone strobe" } - { name: wb_we, direction: input, width: 1, description: "Write enable" } - { name: wb_addr, direction: input, width: 3, description: "Address bus" } - { name: wb_data_o, direction: input, width: 8, description: "Write data" } - { name: wb_data_i, direction: output, width: 8, description: "Read data" } - { name: wb_ack, direction: output, width: 1, description: "Acknowledge" } - { name: uart_tx, direction: output, width: 1, description: "Serial transmit" } - { name: uart_rx, direction: input, width: 1, description: "Serial receive" } - { name: cts_n, direction: input, width: 1, description: "Clear to send" } - { name: rts_n, direction: output, width: 1, description: "Request to send" } - { name: uart_intr, direction: output, width: 1, description: "Interrupt output" } registers: - name: RBR_THR address: '0x00' access: rw description: "Receiver Buffer / Transmitter Holding" fields: - { name: data, bits: '7:0', access: rw, description: "Data byte" } - name: IER address: '0x01' access: rw description: "Interrupt Enable Register" fields: - { name: erbfi, bits: '0', description: "Enable RX data available interrupt" } - { name: etbei, bits: '1', description: "Enable TX holding register empty interrupt" } - { name: elsi, bits: '2', description: "Enable RX line status interrupt" } - { name: edssi, bits: '3', description: "Enable modem status interrupt" } - name: IIR address: '0x02' access: ro description: "Interrupt Identification Register" fields: - { name: int_id, bits: '3:0', description: "Interrupt type identifier" } - name: LCR address: '0x03' access: rw description: "Line Control Register" fields: - { name: wls, bits: '1:0', description: "Word length select (5-8 bits)" } - { name: stb, bits: '2', description: "Stop bits (0=1, 1=1.5/2)" } - { name: pen, bits: '3', description: "Parity enable" } - { name: eps, bits: '4', description: "Even parity select" } - { name: sp, bits: '5', description: "Stick parity" } - { name: bc, bits: '6', description: "Break control" } - { name: dlab, bits: '7', description: "Divisor latch access bit" } - name: MCR address: '0x04' access: rw description: "Modem Control Register" fields: - { name: dtr, bits: '0', description: "Data Terminal Ready" } - { name: rts, bits: '1', description: "Request To Send" } - { name: out1, bits: '2', description: "Output 1" } - { name: out2, bits: '3', description: "Output 2" } - { name: loop, bits: '4', description: "Loopback mode enable" } - name: LSR address: '0x05' access: ro description: "Line Status Register" fields: - { name: dr, bits: '0', description: "Data Ready" } - { name: oe, bits: '1', description: "Overrun Error" } - { name: pe, bits: '2', description: "Parity Error" } - { name: fe, bits: '3', description: "Framing Error" } - { name: bi, bits: '4', description: "Break Interrupt" } - { name: thre, bits: '5', description: "TX Holding Register Empty" } - { name: temt, bits: '6', description: "Transmitter Empty" } - { name: err, bits: '7', description: "Error in RX FIFO" } - name: MSR address: '0x06' access: ro description: "Modem Status Register" fields: - { name: dcts, bits: '0', description: "Delta Clear To Send" } - { name: cts, bits: '4', description: "Clear To Send" } - name: SCR address: '0x07' access: rw description: "Scratch Register" fields: - { name: scratch, bits: '7:0', description: "Scratch pad for testing" } `; export function parseYAML(text) { const doc = yaml.load(text); return doc || {}; } export function validateYAML(text) { const errors = []; try { const doc = yaml.load(text); if (!doc || typeof doc !== "object") { errors.push("Root must be a YAML mapping (object)"); return errors; } if (!doc.design_name) errors.push("Missing required field: design_name"); if (!doc.interfaces || !doc.interfaces.length) errors.push("At least one interface required"); if (doc.interfaces) { doc.interfaces.forEach((iface, i) => { if (!iface.name) errors.push(`Interface #${i + 1} missing name`); if (!Array.isArray(iface.signals) || iface.signals.length === 0) errors.push(`Interface "${iface.name || "#" + (i + 1)}" has no signals`); }); } if (doc.registers) { doc.registers.forEach((reg, i) => { if (!reg.name) errors.push(`Register #${i + 1} missing name`); if (!reg.address) errors.push(`Register "${reg.name || "#" + (i + 1)}" missing address`); if (reg.address && !/^0x[0-9a-fA-F]+$/.test(String(reg.address))) errors.push(`Register "${reg.name}" address should be hex (0x...)`); }); } } catch (e) { errors.push(`YAML parse error: ${e.message}`); } return errors; } export function toYAML(obj) { return yaml.dump(obj, { indent: 2, lineWidth: 120, noRefs: true }); } export { PROTOCOLS, DEFAULT_YAML };