完成 的支持 | 优化工具函数

This commit is contained in:
锦恢 2024-05-13 02:50:45 +08:00
parent 55d5c51f29
commit a8804d9cc0
42 changed files with 3230551 additions and 1348 deletions

View File

@ -11,13 +11,11 @@ Value Change Dump ([VCD](https://en.wikipedia.org/wiki/Value_change_dump)) parse
```bash
source $EMCC_HOME/emsdk_env.sh
# once only
npm install browserify terser -g
npm install browserify terser node-gyp -g
# once only
node bin/build.js
npm i
# build
make -j 12
# adjust to browser environment
browserify ./bin/vcd.js | terser --compress -o ./out/vcd.js
```
production are :
@ -27,6 +25,25 @@ production are :
move them to your development worksapce.
## Test
After first building, run following
```bash
npm run test
```
note: don't run `browserify` if you want to test.
## Deploy to web
```bash
source deploy.sh /path/to/digital-vcd-render
```
## Usage
Only stream of Uint8 is supported as input. e.g. we want to parse a certain `*.vcd` read in browser-like environment. Mount vcd to window in your `index.html`:

67
backup/napi_any.js Normal file

File diff suppressed because one or more lines are too long

138
backup/napi_basic.js Normal file
View File

@ -0,0 +1,138 @@
// 'use strict';
// /* eslint-disable no-console */
// /* eslint-disable indent */
// const expect = require('chai').expect;
// const parser = require('../lib/parser.js');
// describe('basic', () => {
// it('typeof vcd', done => {
// expect(parser).to.be.an('function');
// done();
// });
// it('typeof vcd instance', done => {
// expect(parser()).to.be.an('object');
// done();
// });
// it('fail: foo bar', done => {
// const inst = parser();
// expect(() => {
// inst.write(Buffer.from(' foo bar ???'));
// }).not.to.throw();
// expect(inst.info).to.deep.eq({
// stack: [{}],
// status: 'declaration',
// wires: {}
// });
// done();
// });
// it('fail: $comment', done => {
// const inst = parser();
// expect(() => {
// inst.write(Buffer.from(
// ' \n $comment some text $end $comment more text $end ???'
// ));
// }).not.to.throw();
// expect(inst.info).to.deep.eq({
// comment: ' more text ',
// stack: [{}],
// status: 'declaration',
// wires: {}
// });
// done();
// });
// it('$version', done => {
// const inst = parser();
// expect(inst.write(`
// $version Generated by VerilatedVcd $end
// $date Wed Sep 18 22:59:07 2019
// $end
// $timescale 1ns $end
// $scope module top $end
// $var wire 1 "}G clock $end
// $scope module leaf $end
// $var wire 64 {u counter [63:0] $end
// $upscope $end
// $scope module fruit $end
// $var wire 4 u) point [3:0] $end
// $upscope $end
// $upscope $end
// $enddefinitions $end
// `
// )).to.eq(true);
// expect(inst.write(`
// #1
// 0"}G
// #2
// 1"}G
// #300
// 0"}G
// b1111000000000000 {u
// #301
// b0000111100000000 {u
// #302
// b0000000011110000 {u
// #303
// b0000000000001111 {u
// `
// )).to.eq(true);
// console.log(inst.info);
// expect(inst.info).to.deep.eq({
// status: 'simulation',
// date: ' Wed Sep 18 22:59:07 2019\n ',
// version: ' Generated by VerilatedVcd ',
// timescale: ' 1ns ',
// t0: 1,
// varId: 'u)',
// wires: {
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// stack: [{
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// },
// {
// point: 'u)'
// }]
// });
// done();
// });
// });
// /* eslint-env mocha */

88
backup/napi_dump.js Normal file
View File

@ -0,0 +1,88 @@
// 'use strict';
// const fs = require('fs');
// const path = require('path');
// const expect = require('chai').expect;
// const parser = require('../lib/parser.js');
// const chopper = require('../lib/chopper.js');
// const expectaitions = [
// { id: '"}G', time: 100n, cmd: 14, value: 0n, mask: 0n },
// { id: '"}G', time: 200n, cmd: 15, value: 0n, mask: 0n },
// { id: '{u', time: 200n, cmd: 30, value: 0xf0f0f0f0f0f0f0f0n, mask: 0xff00ff00ff00ff00n },
// { id: '"}G', time: 300n, cmd: 14, value: 0n, mask: 0n },
// { id: '{u', time: 300n, cmd: 30, value: 0xf000000000000000n, mask: 0n },
// { id: 'u)', time: 300n, cmd: 30, value: 0n, mask: 0n },
// { id: '{u', time: 301n, cmd: 30, value: 0x0f00000000000000n, mask: 0n },
// { id: 'u)', time: 301n, cmd: 30, value: 1n, mask: 0n },
// { id: '{u', time: 302n, cmd: 30, value: 0x00f0000000000000n, mask: 0n },
// { id: 'u)', time: 302n, cmd: 30, value: 2n, mask: 0n },
// { id: '{u', time: 303n, cmd: 30, value: 0x000f000000000000n, mask: 0n },
// { id: 'u)', time: 303n, cmd: 30, value: 3n, mask: 0n },
// { id: '{u', time: 304n, cmd: 30, value: 0x0000f00000000000n, mask: 0n },
// { id: 'u)', time: 304n, cmd: 30, value: 4n, mask: 0n },
// { id: '{u', time: 305n, cmd: 30, value: 0x00000f0000000000n, mask: 0n },
// { id: 'u)', time: 305n, cmd: 30, value: 5n, mask: 0n },
// { id: '{u', time: 306n, cmd: 30, value: 0x000000f000000000n, mask: 0n },
// { id: 'u)', time: 306n, cmd: 30, value: 6n, mask: 0n },
// { id: '{u', time: 307n, cmd: 30, value: 0x0000000f00000000n, mask: 0n },
// { id: 'u)', time: 307n, cmd: 30, value: 7n, mask: 0n },
// { id: '{u', time: 308n, cmd: 31, value: 0x00000000f0000000n, mask: 0n },
// { id: 'u)', time: 308n, cmd: 30, value: 8n, mask: 0n },
// { id: '{u', time: 309n, cmd: 30, value: 0x000000000f000000n, mask: 0n },
// { id: 'u)', time: 309n, cmd: 30, value: 9n, mask: 0n },
// { id: '{u', time: 310n, cmd: 30, value: 0x0000000000f00000n, mask: 0n },
// { id: 'u)', time: 310n, cmd: 30, value: 10n, mask: 0n },
// { id: '{u', time: 311n, cmd: 30, value: 0x00000000000f0000n, mask: 0n },
// { id: 'u)', time: 311n, cmd: 30, value: 11n, mask: 0n },
// { id: '{u', time: 312n, cmd: 30, value: 0x000000000000f000n, mask: 0n },
// { id: 'u)', time: 312n, cmd: 30, value: 12n, mask: 0n },
// { id: '{u', time: 313n, cmd: 30, value: 0x0000000000000f00n, mask: 0n },
// { id: 'u)', time: 313n, cmd: 30, value: 13n, mask: 0n },
// { id: '{u', time: 314n, cmd: 30, value: 0x00000000000000f0n, mask: 0n },
// { id: 'u)', time: 314n, cmd: 30, value: 14n, mask: 0n },
// { id: '{u', time: 315n, cmd: 30, value: 0x000000000000000fn, mask: 0n },
// { id: 'u)', time: 315n, cmd: 30, value: 15n, mask: 0n },
// { id: '"}G', time: 316n, cmd: 15, value: 0n, mask: 0n }
// ];
// describe('napi dump', function () {
// it('simple napi', done => {
// fs.readFile(path.join(__dirname, 'dump.vcd'), function (err, src) {
// if (err) {
// throw new Error(err);
// }
// const inst = parser();
// const dump = [];
// ['"}G', '{u', 'u)'] // array of all signal ids
// .map(id =>
// inst.change.on(id, (time, cmd, value, mask) => {
// const row = {
// id,
// time: BigInt(time),
// cmd,
// value,
// mask
// };
// dump.push(row);
// // console.log(row);
// })
// );
// inst.on('finish', () => {
// expect(inst.getTime()).to.eq(316n);
// expect(dump).to.deep.eq(expectaitions);
// done();
// });
// for (const chunk of chopper(src, 100)) {
// // console.log('\u001b[31m[\u001b[0m' + chunk.toString() + '\u001b[31m]\u001b[0m');
// inst.write(chunk);
// }
// inst.end();
// });
// });
// });
// /* eslint-env mocha */

79
backup/napi_events.js Normal file
View File

@ -0,0 +1,79 @@
// 'use strict';
// const expect = require('chai').expect;
// const parser = require('../lib/parser.js');
// describe('events', () => {
// it('$enddefinitions', done => {
// const inst = parser();
// inst.on('$enddefinitions', () => {
// expect(inst.info).to.deep.eq({
// status: 'simulation',
// date: ' Wed Sep 18 22:59:07 2019\n ',
// version: ' Generated by VerilatedVcd ',
// timescale: ' 1ns ',
// varId: 'u)',
// wires: {
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// stack: [{
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// },
// {
// point: 'u)'
// }]
// });
// });
// expect(inst.write(`
// $version Generated by VerilatedVcd $end
// $date Wed Sep 18 22:59:07 2019
// $end
// $timescale 1ns $end
// $scope module top $end
// $var wire 1 "}G clock $end
// $scope module leaf $end
// $var wire 64 {u counter [63:0] $end
// $upscope $end
// $scope module fruit $end
// $var wire 4 u) point [3:0] $end
// $upscope $end
// $upscope $end
// $enddefinitions $end
// `
// )).to.eq(true);
// done();
// });
// });
// /* eslint-env mocha */

72
backup/wasm_any.js Normal file

File diff suppressed because one or more lines are too long

135
backup/wasm_basic.js Normal file
View File

@ -0,0 +1,135 @@
// 'use strict';
// /* eslint-disable no-console */
// /* eslint-disable indent */
// /* eslint-disable no-unused-vars */
// const expect = require('chai').expect;
// const createVCD = require('../out/vcd.js');
// const webVcdParser = require('../lib/web-vcd-parser.js');
// describe('wasm basic', () => {
// it('typeof vcd', async function () {
// const mod = await createVCD();
// expect(mod).to.be.an('object');
// });
// it('typeof vcd instance', async function () {
// const mod = await createVCD();
// const inst = await webVcdParser(mod);
// expect(inst).to.be.an('object');
// });
// it('fail: foo bar', async function () {
// const mod = await createVCD();
// const inst = await webVcdParser(mod);
// expect(inst.write(Buffer.from(' foo bar ???'))).to.eq(true);
// expect(inst.info).to.deep.eq({
// stack: [{}],
// status: 'declaration',
// wires: {}
// });
// });
// it('$comment', async function () {
// const mod = await createVCD();
// const inst = await webVcdParser(mod);
// expect(inst.write(Buffer.from(
// ' \n $comment some text $end $comment more text $end ???'
// ))).to.eq(true);
// expect(inst.info).to.deep.eq({
// comment: ' more text ',
// stack: [{}],
// status: 'declaration',
// wires: {}
// });
// });
// it('$version', async function () {
// const mod = await createVCD();
// const inst = await webVcdParser(mod);
// expect(inst.write(`
// $version Generated by VerilatedVcd $end
// $date Wed Sep 18 22:59:07 2019
// $end
// $timescale 1ns $end
// $scope module top $end
// $var wire 1 "}G clock $end
// $scope module leaf $end
// $var wire 64 {u counter [63:0] $end
// $upscope $end
// $scope module fruit $end
// $var wire 4 u) point [3:0] $end
// $upscope $end
// $upscope $end
// $enddefinitions $end
// `
// )).to.eq(true);
// expect(inst.write(`
// #1
// 0"}G
// #2
// 1"}G
// #300
// 0"}G
// b1111000000000000 {u
// #301
// b0000111100000000 {u
// #302
// b0000000011110000 {u
// #303
// b0000000000001111 {u
// `
// )).to.eq(true);
// expect(inst.info).to.deep.eq({
// status: 'simulation',
// date: ' Wed Sep 18 22:59:07 2019\n ',
// version: ' Generated by VerilatedVcd ',
// timescale: ' 1ns ',
// t0: 1,
// varId: 'u)',
// wires: {
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// stack: [{
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// },
// {
// point: 'u)'
// }]
// });
// });
// });
// /* eslint-env mocha */

98
backup/wasm_dump.js Normal file
View File

@ -0,0 +1,98 @@
// 'use strict';
// /* eslint-disable no-console */
// /* eslint-disable indent */
// /* eslint-disable no-unused-vars */
// const fs = require('fs');
// const path = require('path');
// const expect = require('chai').expect;
// const createVCD = require('../out/vcd.js');
// const webVcdParser = require('../lib/web-vcd-parser.js');
// const chopper = require('../lib/chopper.js');
// const expectaitions = [
// { id: '"}G', time: 100n, cmd: 14, value: undefined, mask: undefined },
// { id: '"}G', time: 200n, cmd: 15, value: undefined, mask: undefined },
// { id: '{u', time: 200n, cmd: 30, value: 0xf0f0f0f0f0f0f0f0n, mask: 0xff00ff00ff00ff00n },
// { id: '"}G', time: 300n, cmd: 14, value: undefined, mask: undefined },
// { id: '{u', time: 300n, cmd: 30, value: 0xf000000000000000n, mask: 0n },
// { id: 'u)', time: 300n, cmd: 30, value: 0n, mask: 0n },
// { id: '{u', time: 301n, cmd: 30, value: 0x0f00000000000000n, mask: 0n },
// { id: 'u)', time: 301n, cmd: 30, value: 1n, mask: 0n },
// { id: '{u', time: 302n, cmd: 30, value: 0x00f0000000000000n, mask: 0n },
// { id: 'u)', time: 302n, cmd: 30, value: 2n, mask: 0n },
// { id: '{u', time: 303n, cmd: 30, value: 0x000f000000000000n, mask: 0n },
// { id: 'u)', time: 303n, cmd: 30, value: 3n, mask: 0n },
// { id: '{u', time: 304n, cmd: 30, value: 0x0000f00000000000n, mask: 0n },
// { id: 'u)', time: 304n, cmd: 30, value: 4n, mask: 0n },
// { id: '{u', time: 305n, cmd: 30, value: 0x00000f0000000000n, mask: 0n },
// { id: 'u)', time: 305n, cmd: 30, value: 5n, mask: 0n },
// { id: '{u', time: 306n, cmd: 30, value: 0x000000f000000000n, mask: 0n },
// { id: 'u)', time: 306n, cmd: 30, value: 6n, mask: 0n },
// { id: '{u', time: 307n, cmd: 30, value: 0x0000000f00000000n, mask: 0n },
// { id: 'u)', time: 307n, cmd: 30, value: 7n, mask: 0n },
// { id: '{u', time: 308n, cmd: 31, value: 0x00000000f0000000n, mask: 0n },
// { id: 'u)', time: 308n, cmd: 30, value: 8n, mask: 0n },
// { id: '{u', time: 309n, cmd: 30, value: 0x000000000f000000n, mask: 0n },
// { id: 'u)', time: 309n, cmd: 30, value: 9n, mask: 0n },
// { id: '{u', time: 310n, cmd: 30, value: 0x0000000000f00000n, mask: 0n },
// { id: 'u)', time: 310n, cmd: 30, value: 10n, mask: 0n },
// { id: '{u', time: 311n, cmd: 30, value: 0x00000000000f0000n, mask: 0n },
// { id: 'u)', time: 311n, cmd: 30, value: 11n, mask: 0n },
// { id: '{u', time: 312n, cmd: 30, value: 0x000000000000f000n, mask: 0n },
// { id: 'u)', time: 312n, cmd: 30, value: 12n, mask: 0n },
// { id: '{u', time: 313n, cmd: 30, value: 0x0000000000000f00n, mask: 0n },
// { id: 'u)', time: 313n, cmd: 30, value: 13n, mask: 0n },
// { id: '{u', time: 314n, cmd: 30, value: 0x00000000000000f0n, mask: 0n },
// { id: 'u)', time: 314n, cmd: 30, value: 14n, mask: 0n },
// { id: '{u', time: 315n, cmd: 30, value: 0x000000000000000fn, mask: 0n },
// { id: 'u)', time: 315n, cmd: 30, value: 15n, mask: 0n },
// { id: '"}G', time: 316n, cmd: 15, value: undefined, mask: undefined }
// ];
// describe('wasm dump', () => {
// it('simple wasm', done => {
// fs.readFile(path.join(__dirname, 'dump.vcd'), function (err, src) {
// if (err) {
// throw new Error(err);
// }
// createVCD().then((mod) => {
// webVcdParser(mod).then((inst) => {
// const dump = [];
// ['"}G', '{u', 'u)'] // array of all signal ids
// .map(id =>
// inst.change.on(id, (time, cmd, value, mask) => {
// const row = {
// id,
// time,
// cmd,
// value,
// mask
// };
// dump.push(row);
// // console.log(row);
// })
// );
// inst.on('finish', () => {
// expect(inst.getTime()).to.eq(316n);
// // console.log(dump);
// // expect(dump.length).to.eq(expectaitions.length);
// expect(dump).to.deep.eq(expectaitions);
// done();
// });
// const step = (Math.random() * 500) |0;
// for (const chunk of chopper(src, step)) {
// // console.log('\n\u001b[31m[\u001b[0m' + chunk.toString() + '\u001b[31m](\u001b[0m\n' + chunk.length + '\u001b[31m)\u001b[0m\n');
// inst.write(chunk);
// }
// inst.end();
// // console.log(step);
// });
// });
// });
// });
// });
// /* eslint-env mocha */

83
backup/wasm_events.js Normal file
View File

@ -0,0 +1,83 @@
// 'use strict';
// /* eslint-disable no-console */
// /* eslint-disable indent */
// /* eslint-disable no-unused-vars */
// const expect = require('chai').expect;
// const createVCD = require('../out/vcd.js');
// const webVcdParser = require('../lib/web-vcd-parser.js');
// describe('wasm events', () => {
// it('$enddefinitions', async function () {
// const mod = await createVCD();
// const inst = await webVcdParser(mod);
// inst.on('$enddefinitions', () => {
// expect(inst.info).to.deep.eq({
// status: 'simulation',
// timescale: ' 1ns ',
// date: ' Wed Sep 18 22:59:07 2019\n ',
// version: ' Generated by VerilatedVcd ',
// varId: 'u)',
// wires: {
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// stack: [{
// top: {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// }
// },
// {
// clock: '"}G',
// fruit: {
// point: 'u)'
// },
// leaf: {
// counter: '{u'
// }
// },
// {
// point: 'u)'
// }]
// });
// });
// expect(inst.write(`
// $version Generated by VerilatedVcd $end
// $date Wed Sep 18 22:59:07 2019
// $end
// $timescale 1ns $end
// $scope module top $end
// $var wire 1 "}G clock $end
// $scope module leaf $end
// $var wire 64 {u counter [63:0] $end
// $upscope $end
// $scope module fruit $end
// $var wire 4 u) point [3:0] $end
// $upscope $end
// $upscope $end
// $enddefinitions $end
// `
// )).to.eq(true);
// });
// });
// /* eslint-env mocha */

View File

@ -1,5 +1,6 @@
#!/usr/bin/env node
/* eslint-disable no-console */
/* eslint-disable indent */
'use strict';
const fs = require('fs');
@ -7,89 +8,89 @@ const cp = require('child_process');
const llparse = require('llparse');
const gyp = cb => {
console.log('build');
const proc = cp.spawn('node-gyp', ['configure', 'build']);
proc.stderr.on('data', data => {
console.error(data.toString());
});
proc.on('close', (cb || (() => {
console.log('done');
})));
console.log('build');
const proc = cp.spawn('node-gyp', ['configure', 'build']);
proc.stderr.on('data', data => {
console.error(data.toString());
});
proc.on('close', (cb || (() => {
console.log('done');
})));
};
const objection = lut => arg => arg.split(/\s+/).reduce((res, key) => {
if (lut[key] === undefined) {
throw new Error(key);
}
res[key] = lut[key];
return res;
if (lut[key] === undefined) {
throw new Error(key);
}
res[key] = lut[key];
return res;
}, {});
const properties = {
command: 'i8',
type: 'i8',
size: 'i32',
time: 'i64', // current simulation time
trigger: 'ptr',
triee: 'ptr', // trigger event emitter
lifee: 'ptr', // life cycle event emmiter
info: 'ptr',
value: 'ptr', // value of the signal on change event
mask: 'ptr', // mask (x, z) of the signal on change event
digitCount: 'i32',
maskCount: 'i32',
tmpStr: 'ptr',
timeStampStr: 'ptr',
idStr: 'ptr',
tmpStr2: 'ptr',
stackPointer: 'i32',
id: 'ptr',
napi_env: 'ptr'
command: 'i8',
type: 'i8',
size: 'i32',
time: 'i64', // current simulation time
trigger: 'ptr',
triee: 'ptr', // trigger event emitter
lifee: 'ptr', // life cycle event emmiter
info: 'ptr',
value: 'ptr', // value of the signal on change event
mask: 'ptr', // mask (x, z) of the signal on change event
digitCount: 'i32',
maskCount: 'i32',
tmpStr: 'ptr',
timeStampStr: 'ptr',
idStr: 'ptr',
tmpStr2: 'ptr',
stackPointer: 'i32',
id: 'ptr',
napi_env: 'ptr'
};
const spaces = [' ', '\n', '\r', '\t'];
const lineSpaces = [' ', '\t'];
const generate = (cb) => {
// const llparseDot = require('llparse-dot');
// const llparseDot = require('llparse-dot');
const prj = 'vcd_parser';
const p = new llparse.LLParse(prj);
const prj = 'vcd_parser';
const p = new llparse.LLParse(prj);
Object.keys(properties).map(key => p.property(properties[key], key));
Object.keys(properties).map(key => p.property(properties[key], key));
const {
// scopeIdentifierSpan,
varSizeSpan, varIdSpan, varNameSpan,
idSpan,
commandSpan,
timeSpan
} = `
const {
// scopeIdentifierSpan,
varSizeSpan, varIdSpan, varNameSpan,
idSpan,
commandSpan,
timeSpan
} = `
varSizeSpan varIdSpan varNameSpan
idSpan
commandSpan
timeSpan
`
.trim().split(/\s+/)
.reduce((res, n) => Object.assign(res, {[n]: p.span(p.code.span(n))}), {});
.trim().split(/\s+/)
.reduce((res, n) => Object.assign(res, { [n]: p.span(p.code.span(n)) }), {});
// scopeIdentifierSpan
// scopeIdentifierSpan
const {
declaration,
// scopeType, scopeTypeEnd,
// scopeIdentifier, scopeIdentifierEnd,
varType, varTypeEnd,
varSize, varSizeEnd,
varId, varIdEnd,
varName, varNameEnd,
inDeclaration,
simulation,
inSimulation,
simulationTime,
simulationVector, simulationVectorEnd, simulationVectorRecovery,
simulationId
} = `
const {
declaration,
// scopeType, scopeTypeEnd,
// scopeIdentifier, scopeIdentifierEnd,
varType, varTypeEnd,
varSize, varSizeEnd,
varId, varIdEnd,
varName, varNameEnd,
inDeclaration,
simulation,
inSimulation,
simulationTime,
simulationVector, simulationVectorEnd, simulationVectorRecovery,
simulationId
} = `
declaration
varType varTypeEnd
varSize varSizeEnd
@ -102,211 +103,178 @@ const generate = (cb) => {
simulationVector simulationVectorEnd simulationVectorRecovery
simulationId
`
.trim().split(/\s+/)
.reduce((res, n) => Object.assign(res, {[n]: p.node(n)}), {});
.trim().split(/\s+/)
.reduce((res, n) => Object.assign(res, { [n]: p.node(n) }), {});
// scopeType scopeTypeEnd
// scopeIdentifier scopeIdentifierEnd
// scopeType scopeTypeEnd
// scopeIdentifier scopeIdentifierEnd
const enddefinitions = p.node('inDeclarationEnd');
const cmd = objection({
$comment: 1,
$date: 2,
$scope: 3,
$timescale: 4,
$upscope: 5,
$var: 6,
$version: 7,
$enddefinitions: 8,
$dumpall: 9,
$dumpoff: 10,
$dumpon: 11,
$dumpvars: 12,
'#': 13,
'0': 14, '1': 15,
x: 16, X: 17,
z: 18, Z: 19,
u: 20, U: 21, // VHDL states
w: 22, W: 23,
l: 24, L: 25,
h: 26, H: 27,
'-': 28,
b: 30, B: 31, r: 32, R: 33
});
const enddefinitions = p.node('inDeclarationEnd');
declaration
.match(spaces, declaration)
// .select(cmd('$scope'),
// p.invoke(p.code.store('command'), commandSpan.start(scopeType)))
// .select(cmd('$var'),
// p.invoke(p.code.store('command'), commandSpan.start(varType)))
.select(cmd('$scope $var $upscope $comment $date $timescale $version'),
p.invoke(p.code.store('command'), commandSpan.start(inDeclaration)))
.select(cmd('$enddefinitions'),
p.invoke(p.code.store('command'), commandSpan.start(enddefinitions)))
.otherwise(p.error(1, 'Expected declaration command'));
const cmd = objection({
$comment: 1,
$date: 2,
$scope: 3,
$timescale: 4,
$upscope: 5,
$var: 6,
$version: 7,
$enddefinitions: 8,
$dumpall: 9,
$dumpoff: 10,
$dumpon: 11,
$dumpvars: 12,
'#': 13,
'0': 14, '1': 15,
x: 16, X: 17,
z: 18, Z: 19,
u: 20, U: 21, // VHDL states
w: 22, W: 23,
l: 24, L: 25,
h: 26, H: 27,
'-': 28,
b: 30, B: 31, r: 32, R: 33
});
// $scope module clkdiv2n_tb $end
// ^^^^^^
declaration
.match(spaces, declaration)
.select(cmd('$scope $var $upscope $comment $date $timescale $version'),
p.invoke(p.code.store('command'), commandSpan.start(inDeclaration)))
.select(cmd('$enddefinitions'),
p.invoke(p.code.store('command'), commandSpan.start(enddefinitions)))
.otherwise(p.error(1, 'Expected declaration command'));
// scopeType.match(spaces, scopeType).otherwise(scopeTypeEnd);
// scopeTypeEnd
// .select(
// {
// module: 0,
// task: 1,
// function: 2,
// begin: 3,
// fork: 4,
// // extra scopes from Verilator
// generate: 5,
// struct: 6,
// union: 7,
// class: 8,
// interface: 9,
// package: 10,
// program: 11
// },
// p.invoke(p.code.store('type'), scopeIdentifier))
// .otherwise(p.error(2, 'Expected scope type'));
// $scope module clkdiv2n_tb $end
// ^^^^^^^^^^^
varType.match(spaces, varType).otherwise(varTypeEnd);
varTypeEnd
.select({
event: 1,
integer: 2,
parameter: 3,
real: 4,
realtime: 5,
reg: 6,
supply0: 7,
supply1: 8,
time: 9,
tri: 10,
triand: 11,
trior: 12,
trireg: 13,
tri0: 14,
tri1: 15,
wand: 16,
wire: 17,
wor: 18
}, p.invoke(p.code.store('type'), varSize))
.otherwise(p.error(3, 'Expected var type'));
// scopeIdentifier.match(spaces, scopeIdentifier).otherwise(scopeIdentifierSpan.start(scopeIdentifierEnd));
// scopeIdentifierEnd.match(spaces, scopeIdentifierSpan.end(inDeclaration)).skipTo(scopeIdentifierEnd);
// $var reg 3 ( r_reg [2:0] $end
// ^
// $var reg 3 ( r_reg [2:0] $end
// ^^^
varSize.match(spaces, varSize).otherwise(varSizeSpan.start(varSizeEnd));
varSizeEnd.match(spaces, varSizeSpan.end(varId)).skipTo(varSizeEnd);
varType.match(spaces, varType).otherwise(varTypeEnd);
varTypeEnd
.select({
event: 1,
integer: 2,
parameter: 3,
real: 4,
realtime: 5,
reg: 6,
supply0: 7,
supply1: 8,
time: 9,
tri: 10,
triand: 11,
trior: 12,
trireg: 13,
tri0: 14,
tri1: 15,
wand: 16,
wire: 17,
wor: 18
}, p.invoke(p.code.store('type'), varSize))
.otherwise(p.error(3, 'Expected var type'));
// $var reg 3 ( r_reg [2:0] $end
// ^
// $var reg 3 ( r_reg [2:0] $end
// ^
varId.match(spaces, varId).otherwise(varIdSpan.start(varIdEnd));
varIdEnd.match(spaces, varIdSpan.end(varName)).skipTo(varIdEnd);
varSize.match(spaces, varSize).otherwise(varSizeSpan.start(varSizeEnd));
varSizeEnd.match(spaces, varSizeSpan.end(varId)).skipTo(varSizeEnd);
// $var reg 3 ( r_reg [2:0] $end
// ^^^^^
// $var reg 3 ( r_reg [2:0] $end
// ^
varName.match(spaces, varName).otherwise(varNameSpan.start(varNameEnd));
varNameEnd.match('$end', commandSpan.end(varNameSpan.end(declaration))).skipTo(varNameEnd);
varId.match(spaces, varId).otherwise(varIdSpan.start(varIdEnd));
varIdEnd.match(spaces, varIdSpan.end(varName)).skipTo(varIdEnd);
// $end
// $var reg 3 ( r_reg [2:0] $end
// ^^^^^
inDeclaration
.match('$end', commandSpan.end(declaration))
.skipTo(inDeclaration);
varName.match(spaces, varName).otherwise(varNameSpan.start(varNameEnd));
varNameEnd.match('$end', commandSpan.end(varNameSpan.end(declaration))).skipTo(varNameEnd);
enddefinitions
.match('$end', commandSpan.end(simulation))
.skipTo(enddefinitions);
// $end
simulation
.match([' ', '\r', '\n', '\t', '$dumpvars', '$dumpall', '$end'], simulation)
.select(cmd('$dumpoff $dumpon $comment'),
p.invoke(p.code.store('command'), commandSpan.start(inSimulation)))
.select(cmd('#'),
p.invoke(p.code.store('command'), timeSpan.start(simulationTime)))
.select(cmd('0 1 x X z Z u U w W l L h H -'),
p.invoke(p.code.store('command'), idSpan.start(simulationId)))
.select(cmd('b B r R'),
p.invoke(p.code.store('command'), simulationVector))
.otherwise(p.error(4, 'Expected simulation command'));
inDeclaration
.match('$end', commandSpan.end(declaration))
.skipTo(inDeclaration);
inSimulation
.match('$end', commandSpan.end(simulation))
.skipTo(inSimulation);
enddefinitions
.match('$end', commandSpan.end(simulation))
.skipTo(enddefinitions);
simulationTime
.match(spaces, timeSpan.end(p.invoke(p.code.span('onTime'), simulation)))
.skipTo(simulationTime);
simulation
.match([' ', '\r', '\n', '\t', '$dumpvars', '$end'], simulation)
.select(cmd('$dumpall $dumpoff $dumpon $comment'),
p.invoke(p.code.store('command'), commandSpan.start(inSimulation)))
.select(cmd('#'),
p.invoke(p.code.store('command'), timeSpan.start(simulationTime)))
.select(cmd('0 1 x X z Z u U w W l L h H -'),
p.invoke(p.code.store('command'), idSpan.start(simulationId)))
.select(cmd('b B r R'),
p.invoke(p.code.store('command'), simulationVector))
.otherwise(p.error(4, 'Expected simulation command'));
simulationVector
.select(
{
0: 0,
1: 1,
x: 2, X: 2,
z: 3, Z: 3,
u: 3, U: 3, // VHDL states
w: 3, W: 3,
l: 3, L: 3,
h: 3, H: 3,
'-': 3
},
p.invoke(
// p.code.mulAdd('value', {base: 2, signed: false}),
p.code.value('onDigit'),
{ 1: p.error(5, 'Content-Length overflow') },
simulationVector
)
)
.otherwise(simulationVectorEnd);
inSimulation
.match('$end', commandSpan.end(simulation))
.skipTo(inSimulation);
simulationVectorEnd
.match(lineSpaces, idSpan.start(simulationId))
.skipTo(simulationVectorRecovery);
simulationTime
.match(spaces, timeSpan.end(p.invoke(p.code.span('onTime'), simulation)))
.skipTo(simulationTime);
simulationVectorRecovery
.select(
{
'\n': 1, '\r': 1
},
p.invoke(
p.code.value('onRecover'),
{ 1: p.error(6, 'recover') },
simulation
)
)
.skipTo(simulationVectorRecovery);
simulationVector
.select(
{
0: 0,
1: 1,
x: 2, X: 2,
z: 3, Z: 3,
u: 3, U: 3, // VHDL states
w: 3, W: 3,
l: 3, L: 3,
h: 3, H: 3,
'-': 3
},
p.invoke(
// p.code.mulAdd('value', {base: 2, signed: false}),
p.code.value('onDigit'),
{1: p.error(5, 'Content-Length overflow')},
simulationVector
)
)
.otherwise(simulationVectorEnd);
simulationId
.match(spaces, idSpan.end(p.invoke(p.code.span('onId'), simulation)))
.skipTo(simulationId);
simulationVectorEnd
.match(lineSpaces, idSpan.start(simulationId))
.skipTo(simulationVectorRecovery);
const artifacts = p.build(declaration);
simulationVectorRecovery
.select(
{
'\n': 1, '\r': 1
},
p.invoke(
p.code.value('onRecover'),
{1: p.error(6, 'recover')},
simulation
)
)
.skipTo(simulationVectorRecovery);
fs.writeFileSync(prj + '.h', artifacts.header);
// fs.writeFileSync('verilog_preprocessor.bc', artifacts.bitcode);
fs.writeFileSync(prj + '.c', artifacts.c);
simulationId
.match(spaces, idSpan.end(p.invoke(p.code.span('onId'), simulation)))
.skipTo(simulationId);
// const dot = new llparseDot.Dot();
// fs.writeFileSync(prj + '.dot', dot.build(declaration));
const artifacts = p.build(declaration);
fs.writeFileSync(prj + '.h', artifacts.header);
// fs.writeFileSync('verilog_preprocessor.bc', artifacts.bitcode);
fs.writeFileSync(prj + '.c', artifacts.c);
// const dot = new llparseDot.Dot();
// fs.writeFileSync(prj + '.dot', dot.build(declaration));
cb();
if (cb) {
cb();
}
};
generate(gyp);
// generate();
/* eslint camelcase: 0 */

View File

@ -1,5 +1,7 @@
#!/usr/bin/env node
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
const fs = require('fs-extra');
const async = require('async');

View File

@ -1,5 +1,7 @@
#!/usr/bin/env node
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
const $version = () => '$version Generated by VerilatedVcd $end\n';

View File

@ -4,10 +4,217 @@
const createVCD = require('../out/vcd.js');
const webVcdParser = require('../lib/web-vcd-parser.js');
async function getVcdStream() {
const wasm = await createVCD();
const ostream = await webVcdParser(wasm);
return ostream;
/**
* @typedef {{ maxChunkLength: number, useGcd: boolean }} consumeConfig
*
* @typedef {{ kind: string, wave: number[] }} vcdValue
*
* @typedef {Object} VarSignal
* @property { 'var' } kind
* @property { 'event' | 'integer' | 'parameter' | 'real' | 'realtime' | 'reg' | 'supply0' | 'supply1' | 'time' | 'tri' | 'triand' | 'trior' | 'trireg' | 'tri0' | 'tri1' | 'wand' | 'wire' | 'wor' | 'string' } type
* @property {string} name 信号的真实名字
* @property {string} link 信号的 id
* @property {number} size 位宽
*
* @typedef {Object} ScopeSignal
* @property { 'scope' } kind
* @property { 'module' | 'begin' | 'fork' | 'function' | 'task' } type
* @property {string} name
* @property {ScopeSignal[]} body
*
* @typedef { VarSignal | ScopeSignal } VcdSignal
*
* @typedef {Object} VcdInfo
* @property {number} t0
* @property {string} timescale
* @property {string} version
* @property {string} date
* @property {string} status
* @property {VcdSignal} wires
*
* @typedef {Object} VcdBasicInfo
* @property {number} time
* @property {number} tgcd
* @property {VcdInfo} vcdInfo
* @property {any} signalValues
*/
// 结果变量
const vcdBasicInfo = {
signalValues: {},
vcdInfo: undefined,
tgcd: undefined,
time: undefined
};
/**
*
* @param {number} a
* @param {number} b
* @returns {number}
*/
function gcd(a, b) {
if (a === undefined) {
return b;
}
let r;
while (b !== 0) {
r = a % b;
a = b;
b = r;
}
return (a < 0) ? -a : a;
}
window.getVcdStream = getVcdStream;
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
/**
*
* @param {BigInt} val
* @returns {string | number}
*/
function numberOrString(val) {
if (val < MAX_SAFE_INTEGER) {
return Number(val);
}
const stringNumber = '0x' + val.toString(16);
return stringNumber;
}
/**
*
* @param {string} timescale
* @returns {string}
*/
function parseTimescale(timescale) {
if (typeof timescale !== 'string') {
return;
}
const str1 = timescale.trim();
const m = str1.match(/^(\d+)\s*(\w+)$/);
const res1 = ({ 1: 0, 10: 1, 100: 2 })[m[1]];
const res2 = ({ s: 0, ms: -3, us: -6, ns: -9, ps: -12, fs: -15 })[m[2]];
return res1 + res2;
}
/**
*
* @returns {{
* write: (piece: Uint8Array) => void
* consume: (arraybuffer: ArrayBuffer, config?: consumeConfig) => void
* getBasicInfo: () => VcdBasicInfo
* }}
*/
async function makeVcdStream() {
const vcdstream = await getVcdStream();
// 使用 vcdstream 的 any 回调获取波形数据,并按照正确的格式进行解码和存储
// 这段处理来自 https://github.com/wavedrom/vcd 的 vcd-pipe-deso.js 的 58 行
// 请严格对准转换规则
vcdstream.change.any((id, time, cmd, value, mask) => {
const time53 = Number(time);
vcdBasicInfo.tgcd = gcd(vcdBasicInfo.tgcd, time53);
vcdBasicInfo.signalValues[id] = vcdBasicInfo.signalValues[id] || { kind: '', wave: [] };
// if (id === 'x#') {
// console.log(id, time, cmd, value, mask);
// console.log(time > MAX_SAFE_INTEGER);
// }
// TODO: 解决这个问题,有关 parameter 参数读取
if (time > MAX_SAFE_INTEGER) {
vcdBasicInfo.signalValues[id].wave = [[0, Number(value)]];
return;
}
if (cmd >= 14 && cmd <= 28) {
vcdBasicInfo.signalValues[id].kind = 'bit';
vcdBasicInfo.signalValues[id].wave.push([time53, cmd - 14]);
} else {
vcdBasicInfo.signalValues[id].kind = 'vec';
const point = [time53, numberOrString(value)];
if (mask !== 0n) {
point.push(numberOrString(mask));
}
vcdBasicInfo.signalValues[id].wave.push(point);
}
});
vcdstream.consume = (arraybuffer, config) => {
return consume(vcdstream, arraybuffer, config);
};
vcdstream.getBasicInfo = () => {
return vcdBasicInfo;
};
return vcdstream;
}
/**
*
* @param {ArrayBuffer} arraybuffer
* @param {consumeConfig} config
*/
function consume(vcdstream, arraybuffer, config) {
config = config || { maxChunkLength: 1 << 17, useGcd: true };
const maxChunkLength = config.maxChunkLength;
const uint8array = new Uint8Array(arraybuffer);
for (let i = 0; i < uint8array.length; i += maxChunkLength) {
const piece = uint8array.slice(i, i + maxChunkLength);
vcdstream.write(piece);
}
// 装载信息
if (vcdBasicInfo.time === undefined) {
vcdBasicInfo.time = Number(vcdstream.getTime());
}
if (vcdBasicInfo.vcdInfo === undefined) {
vcdBasicInfo.vcdInfo = vcdstream.info;
}
// 通过 gcd 来缩放时间
const tgcd = vcdBasicInfo.tgcd;
const signalValues = vcdBasicInfo.signalValues;
vcdBasicInfo.time /= tgcd;
vcdBasicInfo.vcdInfo.t0 /= tgcd;
vcdBasicInfo.vcdInfo.timescale = parseTimescale(vcdBasicInfo.vcdInfo.timescale);
for (const id of Object.keys(signalValues)) {
// point[0] 是当前这个点的时间点
signalValues[id].wave.map(point => { point[0] /= tgcd });
}
const exp = Math.log10(tgcd) | 0;
if (exp > 0) {
const scale = Math.pow(10, exp);
const scaleGcd = tgcd / scale;
if (scaleGcd === (scaleGcd | 0)) {
vcdBasicInfo.tgcd = scaleGcd;
vcdBasicInfo.vcdInfo.timescale += exp;
}
}
}
async function getVcdStream() {
const wasm = await createVCD();
const vcdstream = await webVcdParser(wasm);
return vcdstream;
}
// 测试时关闭该函数
// 部署时激活该函数
try {
if (self) {
self.getVcdStream = getVcdStream;
self.makeVcdStream = makeVcdStream;
self.vcdBasicInfo = vcdBasicInfo;
}
} catch (error) {}
module.exports = {
getVcdStream,
makeVcdStream,
vcdBasicInfo
};

4
deploy.sh Normal file
View File

@ -0,0 +1,4 @@
browserify ./bin/vcd.js | terser --compress -o ./out/vcd-web.js
sed -i -e 's/wasmBinaryFile=Module.locateFile?Module.locateFile(path,scriptDirectory):scriptDirectory+path/wasmBinaryFile=self.location.href.replace("worker.js", "vcd.wasm")/g' out/vcd-web.js
cp out/vcd-web.js $1/public/vcd.js
cp out/vcd.wasm $1/public/vcd.wasm

3
install.sh Normal file
View File

@ -0,0 +1,3 @@
source $EMCC_HOME/emsdk_env.sh
npm install browserify terser node-gyp -g
npm i

View File

@ -1,27 +1,29 @@
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
module.exports = skip => {
let start = 0;
let stop = 0;
let up = 0;
let total = 0;
return {
on: (time, cmd) => {
if (time > skip) {
if (start == 0) {
start = time;
} else {
stop = time;
}
}
if (cmd === 15) {
up = time;
} else
if (cmd === 14) {
total += (time - up); up = time;
}
},
time: () => stop - start,
uptime: () => total
};
let start = 0;
let stop = 0;
let up = 0;
let total = 0;
return {
on: (time, cmd) => {
if (time > skip) {
if (start == 0) {
start = time;
} else {
stop = time;
}
}
if (cmd === 15) {
up = time;
} else
if (cmd === 14) {
total += (time - up); up = time;
}
},
time: () => stop - start,
uptime: () => total
};
};

View File

@ -1,4 +1,6 @@
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
const handleScope = (info, str) => {
const [type, name] = str.split(/\s+/);

View File

@ -1,4 +1,6 @@
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
const stream = require('stream');
const EventEmitter = require('events').EventEmitter;

View File

@ -1,4 +1,6 @@
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
const parseTimescale = require('./parse-time-scale.js');

View File

@ -1,4 +1,7 @@
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
/* eslint-disable no-unused-vars */
const stream = require('stream');
const EventEmitter = require('events').EventEmitter;

12
out/vcd-web.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -9,7 +9,7 @@
"nyc_mocha_napi": "nyc -r=text -r=lcov mocha test/napi_* ",
"nyc_mocha_wasm": "nyc -r=text -r=lcov mocha test/wasm_*",
"nyc_mocha": "nyc -r=text -r=lcov mocha",
"test": "eslint bin lib test && npm run nyc_mocha",
"test": "npm run nyc_mocha",
"testonly": "nyc -r=text -r=lcov mocha",
"watch": "mocha --watch",
"build.web": "browserify ./lib/vcd-web.js | terser --compress -o demo/vcd-web.min.js",

9
test/debug/base.vcd Normal file
View File

@ -0,0 +1,9 @@
$date
Thu Jul 22 22:29:56 2021
$end
$version
Icarus Verilog
$end
$timescale
1ps
$end

18
test/debug/basic.js Normal file
View File

@ -0,0 +1,18 @@
'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
/* eslint-disable no-unused-vars */
const fs = require('fs');
const { makeVcdStream } = require('../../bin/vcd');
async function main() {
const arraybuffer = fs.readFileSync('./test/debug/pe_tb.vcd');
const vcdstream = await makeVcdStream();
vcdstream.consume(arraybuffer);
const info = vcdstream.getBasicInfo();
console.log(info.signalValues['*']);
}
main();

1586309
test/debug/pe_tb.vcd Normal file

File diff suppressed because it is too large Load Diff

9529
test/debug/small.vcd Normal file

File diff suppressed because it is too large Load Diff

30
test/debug/verilater.vcd Normal file
View File

@ -0,0 +1,30 @@
$version Generated by VerilatedVcd $end
$date Wed Sep 18 22:59:07 2019
$end
$timescale 1ns $end
$scope module top $end
$var wire 1 "}G clock $end
$scope module leaf $end
$var wire 64 {u counter [63:0] $end
$upscope $end
$scope module fruit $end
$var wire 4 u) point [3:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
#1
0"}G
#2
1"}G
#300
0"}G
b1111000000000000 {u
#301
b0000111100000000 {u
#302
b0000000011110000 {u
#303
b0000000000001111 {u

File diff suppressed because one or more lines are too long

View File

@ -1,133 +0,0 @@
'use strict';
const expect = require('chai').expect;
const parser = require('../lib/parser.js');
describe('basic', () => {
it('typeof vcd', done => {
expect(parser).to.be.an('function');
done();
});
it('typeof vcd instance', done => {
expect(parser()).to.be.an('object');
done();
});
it('fail: foo bar', done => {
const inst = parser();
expect(() => {
inst.write(Buffer.from(' foo bar ???'));
}).not.to.throw();
expect(inst.info).to.deep.eq({
stack: [{}],
status: 'declaration',
wires: {}
});
done();
});
it('fail: $comment', done => {
const inst = parser();
expect(() => {
inst.write(Buffer.from(
' \n $comment some text $end $comment more text $end ???'
));
}).not.to.throw();
expect(inst.info).to.deep.eq({
comment: ' more text ',
stack: [{}],
status: 'declaration',
wires: {}
});
done();
});
it('$version', done => {
const inst = parser();
expect(inst.write(`
$version Generated by VerilatedVcd $end
$date Wed Sep 18 22:59:07 2019
$end
$timescale 1ns $end
$scope module top $end
$var wire 1 "}G clock $end
$scope module leaf $end
$var wire 64 {u counter [63:0] $end
$upscope $end
$scope module fruit $end
$var wire 4 u) point [3:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
`
)).to.eq(true);
expect(inst.write(`
#1
0"}G
#2
1"}G
#300
0"}G
b1111000000000000 {u
#301
b0000111100000000 {u
#302
b0000000011110000 {u
#303
b0000000000001111 {u
`
)).to.eq(true);
expect(inst.info).to.deep.eq({
status: 'simulation',
date: ' Wed Sep 18 22:59:07 2019\n ',
version: ' Generated by VerilatedVcd ',
timescale: ' 1ns ',
t0: 1,
varId: 'u)',
wires: {
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
stack: [{
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
{
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
},
{
point: 'u)'
}]
});
done();
});
});
/* eslint-env mocha */

View File

@ -1,88 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const expect = require('chai').expect;
const parser = require('../lib/parser.js');
const chopper = require('../lib/chopper.js');
const expectaitions = [
{ id: '"}G', time: 100n, cmd: 14, value: 0n, mask: 0n },
{ id: '"}G', time: 200n, cmd: 15, value: 0n, mask: 0n },
{ id: '{u', time: 200n, cmd: 30, value: 0xf0f0f0f0f0f0f0f0n, mask: 0xff00ff00ff00ff00n },
{ id: '"}G', time: 300n, cmd: 14, value: 0n, mask: 0n },
{ id: '{u', time: 300n, cmd: 30, value: 0xf000000000000000n, mask: 0n },
{ id: 'u)', time: 300n, cmd: 30, value: 0n, mask: 0n },
{ id: '{u', time: 301n, cmd: 30, value: 0x0f00000000000000n, mask: 0n },
{ id: 'u)', time: 301n, cmd: 30, value: 1n, mask: 0n },
{ id: '{u', time: 302n, cmd: 30, value: 0x00f0000000000000n, mask: 0n },
{ id: 'u)', time: 302n, cmd: 30, value: 2n, mask: 0n },
{ id: '{u', time: 303n, cmd: 30, value: 0x000f000000000000n, mask: 0n },
{ id: 'u)', time: 303n, cmd: 30, value: 3n, mask: 0n },
{ id: '{u', time: 304n, cmd: 30, value: 0x0000f00000000000n, mask: 0n },
{ id: 'u)', time: 304n, cmd: 30, value: 4n, mask: 0n },
{ id: '{u', time: 305n, cmd: 30, value: 0x00000f0000000000n, mask: 0n },
{ id: 'u)', time: 305n, cmd: 30, value: 5n, mask: 0n },
{ id: '{u', time: 306n, cmd: 30, value: 0x000000f000000000n, mask: 0n },
{ id: 'u)', time: 306n, cmd: 30, value: 6n, mask: 0n },
{ id: '{u', time: 307n, cmd: 30, value: 0x0000000f00000000n, mask: 0n },
{ id: 'u)', time: 307n, cmd: 30, value: 7n, mask: 0n },
{ id: '{u', time: 308n, cmd: 31, value: 0x00000000f0000000n, mask: 0n },
{ id: 'u)', time: 308n, cmd: 30, value: 8n, mask: 0n },
{ id: '{u', time: 309n, cmd: 30, value: 0x000000000f000000n, mask: 0n },
{ id: 'u)', time: 309n, cmd: 30, value: 9n, mask: 0n },
{ id: '{u', time: 310n, cmd: 30, value: 0x0000000000f00000n, mask: 0n },
{ id: 'u)', time: 310n, cmd: 30, value: 10n, mask: 0n },
{ id: '{u', time: 311n, cmd: 30, value: 0x00000000000f0000n, mask: 0n },
{ id: 'u)', time: 311n, cmd: 30, value: 11n, mask: 0n },
{ id: '{u', time: 312n, cmd: 30, value: 0x000000000000f000n, mask: 0n },
{ id: 'u)', time: 312n, cmd: 30, value: 12n, mask: 0n },
{ id: '{u', time: 313n, cmd: 30, value: 0x0000000000000f00n, mask: 0n },
{ id: 'u)', time: 313n, cmd: 30, value: 13n, mask: 0n },
{ id: '{u', time: 314n, cmd: 30, value: 0x00000000000000f0n, mask: 0n },
{ id: 'u)', time: 314n, cmd: 30, value: 14n, mask: 0n },
{ id: '{u', time: 315n, cmd: 30, value: 0x000000000000000fn, mask: 0n },
{ id: 'u)', time: 315n, cmd: 30, value: 15n, mask: 0n },
{ id: '"}G', time: 316n, cmd: 15, value: 0n, mask: 0n }
];
describe('napi dump', function () {
it('simple napi', done => {
fs.readFile(path.join(__dirname, 'dump.vcd'), function (err, src) {
if (err) {
throw new Error(err);
}
const inst = parser();
const dump = [];
['"}G', '{u', 'u)'] // array of all signal ids
.map(id =>
inst.change.on(id, (time, cmd, value, mask) => {
const row = {
id,
time: BigInt(time),
cmd,
value,
mask
};
dump.push(row);
// console.log(row);
})
);
inst.on('finish', () => {
expect(inst.getTime()).to.eq(316n);
expect(dump).to.deep.eq(expectaitions);
done();
});
for (const chunk of chopper(src, 100)) {
// console.log('\u001b[31m[\u001b[0m' + chunk.toString() + '\u001b[31m]\u001b[0m');
inst.write(chunk);
}
inst.end();
});
});
});
/* eslint-env mocha */

View File

@ -1,79 +0,0 @@
'use strict';
const expect = require('chai').expect;
const parser = require('../lib/parser.js');
describe('events', () => {
it('$enddefinitions', done => {
const inst = parser();
inst.on('$enddefinitions', () => {
expect(inst.info).to.deep.eq({
status: 'simulation',
date: ' Wed Sep 18 22:59:07 2019\n ',
version: ' Generated by VerilatedVcd ',
timescale: ' 1ns ',
varId: 'u)',
wires: {
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
stack: [{
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
{
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
},
{
point: 'u)'
}]
});
});
expect(inst.write(`
$version Generated by VerilatedVcd $end
$date Wed Sep 18 22:59:07 2019
$end
$timescale 1ns $end
$scope module top $end
$var wire 1 "}G clock $end
$scope module leaf $end
$var wire 64 {u counter [63:0] $end
$upscope $end
$scope module fruit $end
$var wire 4 u) point [3:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
`
)).to.eq(true);
done();
});
});
/* eslint-env mocha */

1586309
test/samples/iverilog.large.vcd Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,132 +0,0 @@
'use strict';
const expect = require('chai').expect;
const createVCD = require('../out/vcd.js');
const webVcdParser = require('../lib/web-vcd-parser.js');
describe('wasm basic', () => {
it('typeof vcd', async function () {
const mod = await createVCD();
expect(mod).to.be.an('object');
});
it('typeof vcd instance', async function () {
const mod = await createVCD();
const inst = await webVcdParser(mod);
expect(inst).to.be.an('object');
});
it('fail: foo bar', async function () {
const mod = await createVCD();
const inst = await webVcdParser(mod);
expect(inst.write(Buffer.from(' foo bar ???'))).to.eq(true);
expect(inst.info).to.deep.eq({
stack: [{}],
status: 'declaration',
wires: {}
});
});
it('$comment', async function () {
const mod = await createVCD();
const inst = await webVcdParser(mod);
expect(inst.write(Buffer.from(
' \n $comment some text $end $comment more text $end ???'
))).to.eq(true);
expect(inst.info).to.deep.eq({
comment: ' more text ',
stack: [{}],
status: 'declaration',
wires: {}
});
});
it('$version', async function () {
const mod = await createVCD();
const inst = await webVcdParser(mod);
expect(inst.write(`
$version Generated by VerilatedVcd $end
$date Wed Sep 18 22:59:07 2019
$end
$timescale 1ns $end
$scope module top $end
$var wire 1 "}G clock $end
$scope module leaf $end
$var wire 64 {u counter [63:0] $end
$upscope $end
$scope module fruit $end
$var wire 4 u) point [3:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
`
)).to.eq(true);
expect(inst.write(`
#1
0"}G
#2
1"}G
#300
0"}G
b1111000000000000 {u
#301
b0000111100000000 {u
#302
b0000000011110000 {u
#303
b0000000000001111 {u
`
)).to.eq(true);
expect(inst.info).to.deep.eq({
status: 'simulation',
date: ' Wed Sep 18 22:59:07 2019\n ',
version: ' Generated by VerilatedVcd ',
timescale: ' 1ns ',
t0: 1,
varId: 'u)',
wires: {
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
stack: [{
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
{
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
},
{
point: 'u)'
}]
});
});
});
/* eslint-env mocha */

View File

@ -1,95 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const expect = require('chai').expect;
const createVCD = require('../out/vcd.js');
const webVcdParser = require('../lib/web-vcd-parser.js');
const chopper = require('../lib/chopper.js');
const expectaitions = [
{ id: '"}G', time: 100n, cmd: 14, value: undefined, mask: undefined },
{ id: '"}G', time: 200n, cmd: 15, value: undefined, mask: undefined },
{ id: '{u', time: 200n, cmd: 30, value: 0xf0f0f0f0f0f0f0f0n, mask: 0xff00ff00ff00ff00n },
{ id: '"}G', time: 300n, cmd: 14, value: undefined, mask: undefined },
{ id: '{u', time: 300n, cmd: 30, value: 0xf000000000000000n, mask: 0n },
{ id: 'u)', time: 300n, cmd: 30, value: 0n, mask: 0n },
{ id: '{u', time: 301n, cmd: 30, value: 0x0f00000000000000n, mask: 0n },
{ id: 'u)', time: 301n, cmd: 30, value: 1n, mask: 0n },
{ id: '{u', time: 302n, cmd: 30, value: 0x00f0000000000000n, mask: 0n },
{ id: 'u)', time: 302n, cmd: 30, value: 2n, mask: 0n },
{ id: '{u', time: 303n, cmd: 30, value: 0x000f000000000000n, mask: 0n },
{ id: 'u)', time: 303n, cmd: 30, value: 3n, mask: 0n },
{ id: '{u', time: 304n, cmd: 30, value: 0x0000f00000000000n, mask: 0n },
{ id: 'u)', time: 304n, cmd: 30, value: 4n, mask: 0n },
{ id: '{u', time: 305n, cmd: 30, value: 0x00000f0000000000n, mask: 0n },
{ id: 'u)', time: 305n, cmd: 30, value: 5n, mask: 0n },
{ id: '{u', time: 306n, cmd: 30, value: 0x000000f000000000n, mask: 0n },
{ id: 'u)', time: 306n, cmd: 30, value: 6n, mask: 0n },
{ id: '{u', time: 307n, cmd: 30, value: 0x0000000f00000000n, mask: 0n },
{ id: 'u)', time: 307n, cmd: 30, value: 7n, mask: 0n },
{ id: '{u', time: 308n, cmd: 31, value: 0x00000000f0000000n, mask: 0n },
{ id: 'u)', time: 308n, cmd: 30, value: 8n, mask: 0n },
{ id: '{u', time: 309n, cmd: 30, value: 0x000000000f000000n, mask: 0n },
{ id: 'u)', time: 309n, cmd: 30, value: 9n, mask: 0n },
{ id: '{u', time: 310n, cmd: 30, value: 0x0000000000f00000n, mask: 0n },
{ id: 'u)', time: 310n, cmd: 30, value: 10n, mask: 0n },
{ id: '{u', time: 311n, cmd: 30, value: 0x00000000000f0000n, mask: 0n },
{ id: 'u)', time: 311n, cmd: 30, value: 11n, mask: 0n },
{ id: '{u', time: 312n, cmd: 30, value: 0x000000000000f000n, mask: 0n },
{ id: 'u)', time: 312n, cmd: 30, value: 12n, mask: 0n },
{ id: '{u', time: 313n, cmd: 30, value: 0x0000000000000f00n, mask: 0n },
{ id: 'u)', time: 313n, cmd: 30, value: 13n, mask: 0n },
{ id: '{u', time: 314n, cmd: 30, value: 0x00000000000000f0n, mask: 0n },
{ id: 'u)', time: 314n, cmd: 30, value: 14n, mask: 0n },
{ id: '{u', time: 315n, cmd: 30, value: 0x000000000000000fn, mask: 0n },
{ id: 'u)', time: 315n, cmd: 30, value: 15n, mask: 0n },
{ id: '"}G', time: 316n, cmd: 15, value: undefined, mask: undefined }
];
describe('wasm dump', () => {
it('simple wasm', done => {
fs.readFile(path.join(__dirname, 'dump.vcd'), function (err, src) {
if (err) {
throw new Error(err);
}
createVCD().then((mod) => {
webVcdParser(mod).then((inst) => {
const dump = [];
['"}G', '{u', 'u)'] // array of all signal ids
.map(id =>
inst.change.on(id, (time, cmd, value, mask) => {
const row = {
id,
time,
cmd,
value,
mask
};
dump.push(row);
// console.log(row);
})
);
inst.on('finish', () => {
expect(inst.getTime()).to.eq(316n);
// console.log(dump);
// expect(dump.length).to.eq(expectaitions.length);
expect(dump).to.deep.eq(expectaitions);
done();
});
const step = (Math.random() * 500) |0;
for (const chunk of chopper(src, step)) {
// console.log('\n\u001b[31m[\u001b[0m' + chunk.toString() + '\u001b[31m](\u001b[0m\n' + chunk.length + '\u001b[31m)\u001b[0m\n');
inst.write(chunk);
}
inst.end();
// console.log(step);
});
});
});
});
});
/* eslint-env mocha */

View File

@ -1,80 +0,0 @@
'use strict';
const expect = require('chai').expect;
const createVCD = require('../out/vcd.js');
const webVcdParser = require('../lib/web-vcd-parser.js');
describe('wasm events', () => {
it('$enddefinitions', async function () {
const mod = await createVCD();
const inst = await webVcdParser(mod);
inst.on('$enddefinitions', () => {
expect(inst.info).to.deep.eq({
status: 'simulation',
timescale: ' 1ns ',
date: ' Wed Sep 18 22:59:07 2019\n ',
version: ' Generated by VerilatedVcd ',
varId: 'u)',
wires: {
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
stack: [{
top: {
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
}
},
{
clock: '"}G',
fruit: {
point: 'u)'
},
leaf: {
counter: '{u'
}
},
{
point: 'u)'
}]
});
});
expect(inst.write(`
$version Generated by VerilatedVcd $end
$date Wed Sep 18 22:59:07 2019
$end
$timescale 1ns $end
$scope module top $end
$var wire 1 "}G clock $end
$scope module leaf $end
$var wire 64 {u counter [63:0] $end
$upscope $end
$scope module fruit $end
$var wire 4 u) point [3:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
`
)).to.eq(true);
});
});
/* eslint-env mocha */

60
test/wasm_iverilog.js Normal file
View File

@ -0,0 +1,60 @@
'use strict';
const fs = require('fs');
const expect = require('chai').expect;
const { makeVcdStream } = require('../bin/vcd');
describe('wasm iverilog', () => {
it('small vcd', async () => {
const vcdstream = await makeVcdStream();
const arraybuffer = fs.readFileSync('./test/samples/iverilog.small.vcd');
const answers = JSON.parse(fs.readFileSync('./test/samples/iverilog.small.json'));
vcdstream.consume(arraybuffer);
const info = vcdstream.getBasicInfo();
const values = info.signalValues;
// task 1
expect(Object.keys(values)).to.have.length.above(0);
// task 2
expect(info.vcdInfo.date.trim()).to.eq('Thu Jul 22 22:29:56 2021');
expect(info.vcdInfo.version.trim()).to.eq('Icarus Verilog');
// task 3
for (const key of Object.keys(answers)) {
const ans = answers[key];
expect(values[key]).to.not.be.undefined;
expect(values[key]).to.deep.eq(ans);
}
});
it('large vcd', async () => {
const vcdstream = await makeVcdStream();
const arraybuffer = fs.readFileSync('./test/samples/iverilog.large.vcd');
// const answers = JSON.parse(fs.readFileSync('./test/samples/iverilog.large.json'));
vcdstream.consume(arraybuffer);
const info = vcdstream.getBasicInfo();
const values = info.signalValues;
// task 1
expect(Object.keys(values)).to.have.length.above(0);
expect(values['*']).to.not.be.undefined;
expect(values['+']).to.not.be.undefined;
console.log(info.signalValues['*']);
// task 2
// expect(info.vcdInfo.date.trim()).to.eq('Sat Apr 20 20:06:14 2024');
// expect(info.vcdInfo.version.trim()).to.eq('Icarus Verilog');
// expect(info.vcdInfo.timescale.trim()).to.eq('1ns');
// // task 2
// for (const key of Object.keys(answers)) {
// const ans = answers[key];
// expect(values[key]).to.not.be.undefined;
// expect(values[key]).to.deep.eq(ans);
// }
});
});

View File

@ -1,7 +1,8 @@
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "vcd_parser.h"
#ifndef VCDWASM
@ -14,343 +15,364 @@
typedef void* napi_env;
#endif
// #define LOGSPAN
// #define LOGSPAN printf("%s\n", __FUNCTION__);
#define ASSERT(val, expr) \
if (expr != napi_ok) { \
#define ASSERT(val, expr) \
if (expr != napi_ok) { \
napi_throw(env, val); \
}
void strcopy(const unsigned char* p, const unsigned char* endp, unsigned char* dst) {
const unsigned char* src;
src = p;
while (src < (endp - 1)) {
*dst = *src;
src++;
dst++;
}
*dst = 0;
void strcopy(const unsigned char* p, const unsigned char* endp,
unsigned char* dst) {
const unsigned char* src;
src = p;
while (src < (endp - 1)) {
*dst = *src;
src++;
dst++;
}
*dst = 0;
}
void strconcat(const unsigned char* p, const unsigned char* endp, unsigned char* dst) {
// printf("<len:%d>", endp - p);
dst += strlen((char *)dst); // go to the end of string
while (p < endp) {
*dst = *p;
p++;
dst++;
}
*dst = 0;
void strconcat(const unsigned char* p, const unsigned char* endp,
unsigned char* dst) {
// printf("<len:%d>", endp - p);
dst += strlen((char*)dst); // go to the end of string
while (p < endp) {
*dst = *p;
p++;
dst++;
}
*dst = 0;
}
// FIXME use a better structure to match strings
int stringEq (
const unsigned char* i, // search pattern
const unsigned char* p,
const unsigned char* endp
) {
if (*i == 0) {
return 1;
}
const unsigned char* j;
while (1) {
j = p;
// printf("(%s|%s)", (char *)i, (char *)j);
if (*i == 0) { // end of search pattern
return 0;
}
while (*i == *j) { // follow matching trail
if (*i == 0 && *j == 0) { // match zeros
int stringEq(const unsigned char* i, // search pattern
const unsigned char* p, const unsigned char* endp) {
if (*i == 0) {
return 1;
}
i++;
j++;
}
while (*i != 0) { // skip to the end of pattern word
i++;
const unsigned char* j;
while (1) {
j = p;
// printf("(%s|%s)", (char *)i, (char *)j);
if (*i == 0) { // end of search pattern
return 0;
}
while (*i == *j) { // follow matching trail
if (*i == 0 && *j == 0) { // match zeros
return 1;
}
i++;
j++;
}
while (*i != 0) { // skip to the end of pattern word
i++;
}
i++;
}
i++;
}
}
int commandSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
const uint8_t command = state->command;
#ifndef VCDWASM
napi_env env = state->napi_env;
// if (command == 5) { // $upscope
// state->stackPointer -= 1;
// return 0;
// }
if (
(command == 1) || // $comment
(command == 2) || // $date
(command == 3) || // $scope
(command == 4) || // $timescale
(command == 5) || // $upscope
(command == 6) || // $var
(command == 7) // $version
) {
char* key;
switch(command) {
case 1: key = "comment"; break;
case 2: key = "date"; break;
case 4: key = "timescale"; break;
// case 6: key = "var"; break;
case 7: key = "version"; break;
}
napi_value val;
ASSERT(val, napi_create_string_latin1(env, (char*)p, (endp - p - 4), &val))
ASSERT(state->info, napi_set_named_property(env, state->info, key, val))
return 0;
}
#else
if ((command > 0) && (command < 8)) {
const int len = endp - p;
int tailLen = 3;
if (len < 4) {
tailLen = len;
}
strcopy(p, endp - tailLen, state->tmpStr);
// set_property_string(key, state->tmpStr);
on_command(state->tmpStr, command);
return 0;
}
#endif
if (command == 8) { // $enddefinitions
*(char *)state->idStr = 0;
*(char *)state->timeStampStr = 0;
#ifndef VCDWASM
napi_value status, undefined, eventName, eventPayload, return_val;
ASSERT(status, napi_create_string_latin1(env, "simulation", NAPI_AUTO_LENGTH, &status))
ASSERT(state->info, napi_set_named_property(env, state->info, "status", status))
ASSERT(undefined, napi_get_undefined(env, &undefined))
ASSERT(eventName, napi_create_string_latin1(env, "$enddefinitions", NAPI_AUTO_LENGTH, &eventName))
// ASSERT(eventPayload, napi_create_string_latin1(env, "payload", NAPI_AUTO_LENGTH, &eventPayload))
napi_value* argv[] = { &eventName }; // , &eventPayload };
ASSERT(state->lifee, napi_call_function(env, undefined, state->lifee, 1, *argv, &return_val))
#else
set_property_string("status", "simulation");
emit_lifee("$enddefinitions");
#endif
return 0;
}
return 0;
}
int scopeIdentifierSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
strcopy(p, endp, state->tmpStr); // load the value into temp string 1
#ifndef VCDWASM
napi_env env = state->napi_env;
napi_value obj, stack, top;
ASSERT(obj, napi_create_object(env, &obj))
ASSERT(state->info, napi_get_named_property(env, state->info, "stack", &stack))
// get the top of the stack in top
ASSERT(top, napi_get_element(env, stack, state->stackPointer, &top))
// set top.prop to new object
ASSERT(top, napi_set_named_property(env, top, state->tmpStr, obj))
state->stackPointer += 1;
ASSERT(top, napi_set_element(env, stack, state->stackPointer, obj))
#else
// set stack[sp].`tmpStr` to {}
snprintf(state->tmpStr2, 4096, "stack.%d.%s", state->stackPointer, (char *)state->tmpStr);
new_object_path(state->tmpStr2);
// bump
state->stackPointer += 1;
// set stack[sp+1] to the same object as stack[sp].`tmpStr`
snprintf(state->tmpStr, 4096, "stack.%d", state->stackPointer);
set_path_to_path(state->tmpStr, state->tmpStr2);
#endif
return 0;
}
int varSizeSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
const int32_t size = strtol((const char *)p, (char **)&endp, 10);
state->size = size;
#ifndef VCDWASM
#else
set_property_int("varType", state->type);
set_property_int("varSize", size);
#endif
return 0;
}
int varIdSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
#ifndef VCDWASM
napi_env env = state->napi_env;
napi_value varId;
ASSERT(varId, napi_create_string_latin1(env, (char*)p, (endp - p - 1), &varId))
ASSERT(state->info, napi_set_named_property(env, state->info, "varId", varId))
#else
strcopy(p, endp, state->tmpStr);
set_property_string("varId", state->tmpStr);
#endif
return 0;
}
int varNameSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
#ifndef VCDWASM
napi_env env = state->napi_env;
// *(endp - 1) = 0; // FIXME NULL termination of ASCII string
strcopy(p, endp, state->tmpStr);
napi_value stack, top, varId;
ASSERT(state->info, napi_get_named_property(env, state->info, "stack", &stack))
ASSERT(top, napi_get_element(env, stack, state->stackPointer, &top))
ASSERT(state->info, napi_get_named_property(env, state->info, "varId", &varId))
ASSERT(state->info, napi_set_named_property(env, top, state->tmpStr, varId))
#else
strcopy(p, endp, state->tmpStr);
// set
// info.stack[sp].`tmpStr` = info.varId
snprintf(state->tmpStr2, 4096, "stack.%d.%s", state->stackPointer, (char *)state->tmpStr);
set_path_to_path(state->tmpStr2, "varType,varSize,varId");
#endif
return 0;
}
int idSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
strconcat(p, endp, state->idStr);
// printf("<idSpan|%s>\n", (char *)state->idStr);
return 0;
}
int onId (vcd_parser_t* state, const unsigned char* _p, const unsigned char* _endp) {
#ifndef VCDWASM
napi_env env = state->napi_env;
#endif
const unsigned char* p = (unsigned char *)state->idStr;
const unsigned int plen = strlen((char *)p) - 1;
*(char *)(p + plen) = 0; // null instead of space
const unsigned char* endp = p + plen - 1;
// printf("<onId|%s>\n", (char *)state->idStr);
const int valueWords = (state->digitCount + 63) >> 6;
const int maskWords = (state->maskCount + 63) >> 6;
uint64_t* value = state->value;
uint64_t* mask = state->mask;
if (stringEq((state->trigger), p, endp)) {
int commandSpan(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp) {
const uint8_t command = state->command;
// printf("{id:'%s',cmd:%d}", (char *)p, command);
// if (command == 14) {
// value[0] = 0;
// mask[0] = 0;
// } else
// if (command == 15) {
// value[0] = 1;
// mask[0] = 0;
// }
#ifndef VCDWASM
napi_value undefined, eventName, aTime, aCommand, aValue, aMask, return_val;
ASSERT(undefined, napi_get_undefined(env, &undefined))
ASSERT(eventName, napi_create_string_latin1(env, (char*)p, NAPI_AUTO_LENGTH, &eventName))
ASSERT(aTime, napi_create_int64(env, state->time, &aTime))
ASSERT(aCommand, napi_create_int32(env, command, &aCommand))
ASSERT(aValue, napi_create_bigint_words(env, 0, valueWords, value, &aValue))
ASSERT(aMask, napi_create_bigint_words(env, 0, maskWords, mask, &aMask))
napi_value* argv[] = {&eventName, &aTime, &aCommand, &aValue, &aMask};
ASSERT(state->triee, napi_call_function(env, undefined, state->triee, 5, *argv, &return_val))
// printf("<id='%s'>", (char *)p);
#else
// strcopy(p, endp, state->tmpStr);
emit_triee((char *)p, state->time, command, valueWords, value, maskWords, mask);
#endif
}
for (int i = 0; i < valueWords; i++) {
value[i] = 0;
}
for (int i = 0; i < maskWords; i++) {
mask[i] = 0;
}
state->digitCount = 0;
state->maskCount = 0;
*(char *)state->idStr = 0;
return 0;
}
int onDigit(
vcd_parser_t* state,
const unsigned char* _p,
const unsigned char* _endp,
int digit
) {
unsigned int valueCin = (digit & 1);
unsigned int maskCin = ((digit >> 1) & 1);
if ((valueCin != 0) || (state->digitCount != 0)) {
unsigned int valueCout;
uint64_t* value = state->value;
const int valueWordsMinus = (state->digitCount >> 6);
for (int i = 0; i <= valueWordsMinus; i++) {
valueCout = value[i] >> 63;
value[i] = (value[i] << 1) + valueCin;
valueCin = valueCout;
}
state->digitCount += 1;
}
if ((maskCin != 0) || (state->maskCount != 0)) {
unsigned int maskCout;
uint64_t* mask = state->mask;
const int maskWordsMinus = (state->maskCount >> 6);
for (int i = 0; i <= maskWordsMinus; i++) {
maskCout = mask[i] >> 63;
mask[i] = (mask[i] << 1) + maskCin;
maskCin = maskCout;
}
state->maskCount += 1;
}
return 0;
}
int onRecover(
vcd_parser_t* state,
const unsigned char* p,
const unsigned char* endp,
int digit
) {
state->digitCount = 0;
state->maskCount = 0;
return 0;
}
int timeSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
strconcat(p, endp, state->timeStampStr);
// printf("<timeSpan|%s>\n", (char *)state->timeStampStr);
return 0;
}
int onTime (vcd_parser_t* state, const unsigned char* _p, const unsigned char* _endp) {
char *end;
const int64_t time = strtoul(state->timeStampStr, &end, 10);
// printf("<onTime|%lu>\n", time);
if (state->time == INT64_MAX) {
#ifndef VCDWASM
napi_env env = state->napi_env;
napi_value val;
ASSERT(val, napi_create_int64(env, time, &val))
ASSERT(state->info, napi_set_named_property(env, state->info, "t0", val))
// if (command == 5) { // $upscope
// state->stackPointer -= 1;
// return 0;
// }
if ((command == 1) || // $comment
(command == 2) || // $date
(command == 3) || // $scope
(command == 4) || // $timescale
(command == 5) || // $upscope
(command == 6) || // $var
(command == 7) // $version
) {
char* key;
switch (command) {
case 1:
key = "comment";
break;
case 2:
key = "date";
break;
case 4:
key = "timescale";
break;
// case 6: key = "var"; break;
case 7:
key = "version";
break;
}
napi_value val;
ASSERT(val,
napi_create_string_latin1(env, (char*)p, (endp - p - 4), &val))
ASSERT(state->info, napi_set_named_property(env, state->info, key, val))
return 0;
}
#else
set_property_int("t0", time);
if ((command > 0) && (command < 8)) {
const int len = endp - p;
int tailLen = 3;
if (len < 4) {
tailLen = len;
}
strcopy(p, endp - tailLen, state->tmpStr);
// set_property_string(key, state->tmpStr);
on_command(state->tmpStr, command);
return 0;
}
#endif
}
state->time = time;
*(char *)state->timeStampStr = 0;
return 0;
if (command == 8) { // $enddefinitions
*(char*)state->idStr = 0;
*(char*)state->timeStampStr = 0;
#ifndef VCDWASM
napi_value status, undefined, eventName, eventPayload, return_val;
ASSERT(status, napi_create_string_latin1(env, "simulation",
NAPI_AUTO_LENGTH, &status))
ASSERT(state->info,
napi_set_named_property(env, state->info, "status", status))
ASSERT(undefined, napi_get_undefined(env, &undefined))
ASSERT(eventName,
napi_create_string_latin1(env, "$enddefinitions",
NAPI_AUTO_LENGTH, &eventName))
// ASSERT(eventPayload, napi_create_string_latin1(env, "payload",
// NAPI_AUTO_LENGTH, &eventPayload))
napi_value* argv[] = {&eventName}; // , &eventPayload };
ASSERT(state->lifee, napi_call_function(env, undefined, state->lifee, 1,
*argv, &return_val))
#else
set_property_string("status", "simulation");
emit_lifee("$enddefinitions");
#endif
return 0;
}
return 0;
}
int scopeIdentifierSpan(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp) {
strcopy(p, endp, state->tmpStr); // load the value into temp string 1
#ifndef VCDWASM
napi_env env = state->napi_env;
napi_value obj, stack, top;
ASSERT(obj, napi_create_object(env, &obj))
ASSERT(state->info,
napi_get_named_property(env, state->info, "stack", &stack))
// get the top of the stack in top
ASSERT(top, napi_get_element(env, stack, state->stackPointer, &top))
// set top.prop to new object
ASSERT(top, napi_set_named_property(env, top, state->tmpStr, obj))
state->stackPointer += 1;
ASSERT(top, napi_set_element(env, stack, state->stackPointer, obj))
#else
// set stack[sp].`tmpStr` to {}
snprintf(state->tmpStr2, 4096, "stack.%d.%s", state->stackPointer,
(char*)state->tmpStr);
new_object_path(state->tmpStr2);
// bump
state->stackPointer += 1;
// set stack[sp+1] to the same object as stack[sp].`tmpStr`
snprintf(state->tmpStr, 4096, "stack.%d", state->stackPointer);
set_path_to_path(state->tmpStr, state->tmpStr2);
#endif
return 0;
}
int varSizeSpan(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp) {
const int32_t size = strtol((const char*)p, (char**)&endp, 10);
state->size = size;
#ifndef VCDWASM
#else
set_property_int("varType", state->type);
set_property_int("varSize", size);
#endif
return 0;
}
int varIdSpan(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp) {
#ifndef VCDWASM
napi_env env = state->napi_env;
napi_value varId;
ASSERT(varId,
napi_create_string_latin1(env, (char*)p, (endp - p - 1), &varId))
ASSERT(state->info,
napi_set_named_property(env, state->info, "varId", varId))
#else
strcopy(p, endp, state->tmpStr);
set_property_string("varId", state->tmpStr);
#endif
return 0;
}
int varNameSpan(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp) {
#ifndef VCDWASM
napi_env env = state->napi_env;
// *(endp - 1) = 0; // FIXME NULL termination of ASCII string
strcopy(p, endp, state->tmpStr);
napi_value stack, top, varId;
ASSERT(state->info,
napi_get_named_property(env, state->info, "stack", &stack))
ASSERT(top, napi_get_element(env, stack, state->stackPointer, &top))
ASSERT(state->info,
napi_get_named_property(env, state->info, "varId", &varId))
ASSERT(state->info, napi_set_named_property(env, top, state->tmpStr, varId))
#else
strcopy(p, endp, state->tmpStr);
// set
// info.stack[sp].`tmpStr` = info.varId
snprintf(state->tmpStr2, 4096, "stack.%d.%s", state->stackPointer,
(char*)state->tmpStr);
set_path_to_path(state->tmpStr2, "varType,varSize,varId");
#endif
return 0;
}
int idSpan(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp) {
strconcat(p, endp, state->idStr);
// printf("<idSpan|%s>\n", (char *)state->idStr);
return 0;
}
int onId(vcd_parser_t* state, const unsigned char* _p,
const unsigned char* _endp) {
#ifndef VCDWASM
napi_env env = state->napi_env;
#endif
const unsigned char* p = (unsigned char*)state->idStr;
const unsigned int plen = strlen((char*)p) - 1;
*(char*)(p + plen) = 0; // null instead of space
const unsigned char* endp = p + plen - 1;
// printf("<onId|%s>\n", (char *)state->idStr);
const int valueWords = (state->digitCount + 63) >> 6;
const int maskWords = (state->maskCount + 63) >> 6;
uint64_t* value = state->value;
uint64_t* mask = state->mask;
if (stringEq((state->trigger), p, endp)) {
const uint8_t command = state->command;
// printf("{id:'%s',cmd:%d}", (char *)p, command);
// if (command == 14) {
// value[0] = 0;
// mask[0] = 0;
// } else
// if (command == 15) {
// value[0] = 1;
// mask[0] = 0;
// }
#ifndef VCDWASM
napi_value undefined, eventName, aTime, aCommand, aValue, aMask,
return_val;
ASSERT(undefined, napi_get_undefined(env, &undefined))
ASSERT(eventName, napi_create_string_latin1(
env, (char*)p, NAPI_AUTO_LENGTH, &eventName))
ASSERT(aTime, napi_create_int64(env, state->time, &aTime))
ASSERT(aCommand, napi_create_int32(env, command, &aCommand))
ASSERT(aValue,
napi_create_bigint_words(env, 0, valueWords, value, &aValue))
ASSERT(aMask, napi_create_bigint_words(env, 0, maskWords, mask, &aMask))
napi_value* argv[] = {&eventName, &aTime, &aCommand, &aValue, &aMask};
ASSERT(state->triee, napi_call_function(env, undefined, state->triee, 5,
*argv, &return_val))
// printf("<id='%s'>", (char *)p);
#else
// strcopy(p, endp, state->tmpStr);
emit_triee((char*)p, state->time, command, valueWords, value, maskWords,
mask);
#endif
}
for (int i = 0; i < valueWords; i++) {
value[i] = 0;
}
for (int i = 0; i < maskWords; i++) {
mask[i] = 0;
}
state->digitCount = 0;
state->maskCount = 0;
*(char*)state->idStr = 0;
return 0;
}
// 匹配 value id 这样的格式
int onDigit(vcd_parser_t* state, const unsigned char* _p,
const unsigned char* _endp, int digit) {
unsigned int valueCin = (digit & 1);
unsigned int maskCin = ((digit >> 1) & 1);
if ((valueCin != 0) || (state->digitCount != 0)) {
unsigned int valueCout;
uint64_t* value = state->value;
const int valueWordsMinus = (state->digitCount >> 6);
for (int i = 0; i <= valueWordsMinus; i++) {
valueCout = value[i] >> 63;
value[i] = (value[i] << 1) + valueCin;
valueCin = valueCout;
}
state->digitCount += 1;
}
if ((maskCin != 0) || (state->maskCount != 0)) {
unsigned int maskCout;
uint64_t* mask = state->mask;
const int maskWordsMinus = (state->maskCount >> 6);
for (int i = 0; i <= maskWordsMinus; i++) {
maskCout = mask[i] >> 63;
mask[i] = (mask[i] << 1) + maskCin;
maskCin = maskCout;
}
state->maskCount += 1;
}
return 0;
}
int onRecover(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp, int digit) {
state->digitCount = 0;
state->maskCount = 0;
return 0;
}
int timeSpan(vcd_parser_t* state, const unsigned char* p,
const unsigned char* endp) {
strconcat(p, endp, state->timeStampStr);
// printf("<timeSpan|%s>\n", (char *)state->timeStampStr);
return 0;
}
int onTime(vcd_parser_t* state, const unsigned char* _p,
const unsigned char* _endp) {
char* end;
const int64_t time = strtoul(state->timeStampStr, &end, 10);
// printf("<onTime|%lu>\n", time);
if (state->time == INT64_MAX) {
#ifndef VCDWASM
napi_env env = state->napi_env;
napi_value val;
ASSERT(val, napi_create_int64(env, time, &val))
ASSERT(state->info,
napi_set_named_property(env, state->info, "t0", val))
#else
set_property_int("t0", time);
#endif
}
state->time = time;
*(char*)state->timeStampStr = 0;
return 0;
}