Files
2026-10-03 20:34:42 +02:00

85 lines
3.1 KiB
TypeScript

import assert from "assert";
import { describe, it } from "mocha";
import { VCDWriter, vcdId } from "../../src/common/vcd";
function record(signals: { name: string, len: number }[], states: any[]) {
const chunks: string[] = [];
const w = new VCDWriter(signals, c => chunks.push(c));
for (const s of states) w.sample(s);
w.finish();
return { text: chunks.join(''), chunks, w };
}
describe('VCD writer', function () {
it('makes identifier codes from printable characters', function () {
assert.equal(vcdId(0), '!');
assert.equal(vcdId(93), '~');
assert.equal(vcdId(94), '!!');
const ids = new Set(Array.from({ length: 20000 }, (_, i) => vcdId(i)));
assert.equal(ids.size, 20000);
for (const id of ids) assert.ok(/^[!-~]+$/.test(id));
});
it('declares signals in scopes named by their `$` paths', function () {
const { text } = record([
{ name: 'top$clk', len: 1 },
{ name: 'top$cpu$pc', len: 16 },
{ name: 'top$cpu$alu$out', len: 8 },
{ name: 'top$ram$we', len: 1 },
], [{}]);
const defs = text.slice(0, text.indexOf('$enddefinitions'));
assert.deepStrictEqual(defs.split('\n').filter(l => /^\$(scope|upscope|var)/.test(l)), [
'$scope module top $end',
'$var wire 1 ! clk $end',
'$scope module cpu $end',
'$var wire 16 " pc [15:0] $end',
'$scope module alu $end',
'$var wire 8 # out [7:0] $end',
'$upscope $end',
'$upscope $end',
'$scope module ram $end',
'$var wire 1 $ we $end',
'$upscope $end',
'$upscope $end',
]);
});
it('dumps every value first, then only changes', function () {
const { text } = record([{ name: 'm$a', len: 1 }, { name: 'm$b', len: 4 }], [
{ m$a: 0, m$b: 5 },
{ m$a: 0, m$b: 5 }, // nothing changed: no time stamp
{ m$a: 1, m$b: 5 },
{ m$a: 1, m$b: 0 },
]);
const body = text.slice(text.indexOf('$enddefinitions'));
assert.equal(body.split('\n').slice(1).join('\n'), [
'#0', '$dumpvars', '0!', 'b101 "', '$end',
'#2', '1!',
'#3', 'b0 "',
'#4', '',
].join('\n'));
});
it('cuts values to the signal width and reads bigints', function () {
const { text } = record([
{ name: 'm$n', len: 4 }, { name: 'm$s', len: 8 }, { name: 'm$w', len: 40 }, { name: 'm$x', len: 32 },
], [{ m$n: 0x1f, m$s: -1, m$w: BigInt("0x123456789a"), m$x: -1 }]);
assert.ok(text.includes('b1111 !\n'));
assert.ok(text.includes('b11111111 "\n'));
assert.ok(text.includes('b' + BigInt("0x123456789a").toString(2) + ' #\n'));
assert.ok(text.includes('b' + '1'.repeat(32) + ' $\n'));
});
it('writes in chunks, so a long recording is never held whole', function () {
const chunks: string[] = [];
const w = new VCDWriter([{ name: 'm$c', len: 16 }], c => chunks.push(c));
for (let i = 0; i < 100000; i++) w.sample({ m$c: i });
w.finish();
assert.ok(chunks.length > 10, `${chunks.length} chunks`);
assert.equal(w.samples, 100000);
const all = chunks.join('');
assert.ok(all.endsWith('#100000\n'));
assert.equal(all.match(/^#/gm)!.length, 100001);
});
});