mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-25 15:05:25 +00:00
85 lines
3.1 KiB
TypeScript
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);
|
|
});
|
|
});
|