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

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 ```bash
source $EMCC_HOME/emsdk_env.sh source $EMCC_HOME/emsdk_env.sh
# once only # once only
npm install browserify terser -g npm install browserify terser node-gyp -g
# once only # once only
node bin/build.js npm i
# build # build
make -j 12 make -j 12
# adjust to browser environment
browserify ./bin/vcd.js | terser --compress -o ./out/vcd.js
``` ```
production are : production are :
@ -27,6 +25,25 @@ production are :
move them to your development worksapce. 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 ## 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`: 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 #!/usr/bin/env node
/* eslint-disable no-console */ /* eslint-disable no-console */
/* eslint-disable indent */
'use strict'; 'use strict';
const fs = require('fs'); const fs = require('fs');
@ -7,89 +8,89 @@ const cp = require('child_process');
const llparse = require('llparse'); const llparse = require('llparse');
const gyp = cb => { const gyp = cb => {
console.log('build'); console.log('build');
const proc = cp.spawn('node-gyp', ['configure', 'build']); const proc = cp.spawn('node-gyp', ['configure', 'build']);
proc.stderr.on('data', data => { proc.stderr.on('data', data => {
console.error(data.toString()); console.error(data.toString());
}); });
proc.on('close', (cb || (() => { proc.on('close', (cb || (() => {
console.log('done'); console.log('done');
}))); })));
}; };
const objection = lut => arg => arg.split(/\s+/).reduce((res, key) => { const objection = lut => arg => arg.split(/\s+/).reduce((res, key) => {
if (lut[key] === undefined) { if (lut[key] === undefined) {
throw new Error(key); throw new Error(key);
} }
res[key] = lut[key]; res[key] = lut[key];
return res; return res;
}, {}); }, {});
const properties = { const properties = {
command: 'i8', command: 'i8',
type: 'i8', type: 'i8',
size: 'i32', size: 'i32',
time: 'i64', // current simulation time time: 'i64', // current simulation time
trigger: 'ptr', trigger: 'ptr',
triee: 'ptr', // trigger event emitter triee: 'ptr', // trigger event emitter
lifee: 'ptr', // life cycle event emmiter lifee: 'ptr', // life cycle event emmiter
info: 'ptr', info: 'ptr',
value: 'ptr', // value of the signal on change event value: 'ptr', // value of the signal on change event
mask: 'ptr', // mask (x, z) of the signal on change event mask: 'ptr', // mask (x, z) of the signal on change event
digitCount: 'i32', digitCount: 'i32',
maskCount: 'i32', maskCount: 'i32',
tmpStr: 'ptr', tmpStr: 'ptr',
timeStampStr: 'ptr', timeStampStr: 'ptr',
idStr: 'ptr', idStr: 'ptr',
tmpStr2: 'ptr', tmpStr2: 'ptr',
stackPointer: 'i32', stackPointer: 'i32',
id: 'ptr', id: 'ptr',
napi_env: 'ptr' napi_env: 'ptr'
}; };
const spaces = [' ', '\n', '\r', '\t']; const spaces = [' ', '\n', '\r', '\t'];
const lineSpaces = [' ', '\t']; const lineSpaces = [' ', '\t'];
const generate = (cb) => { const generate = (cb) => {
// const llparseDot = require('llparse-dot'); // const llparseDot = require('llparse-dot');
const prj = 'vcd_parser'; const prj = 'vcd_parser';
const p = new llparse.LLParse(prj); 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 { const {
// scopeIdentifierSpan, // scopeIdentifierSpan,
varSizeSpan, varIdSpan, varNameSpan, varSizeSpan, varIdSpan, varNameSpan,
idSpan, idSpan,
commandSpan, commandSpan,
timeSpan timeSpan
} = ` } = `
varSizeSpan varIdSpan varNameSpan varSizeSpan varIdSpan varNameSpan
idSpan idSpan
commandSpan commandSpan
timeSpan timeSpan
` `
.trim().split(/\s+/) .trim().split(/\s+/)
.reduce((res, n) => Object.assign(res, {[n]: p.span(p.code.span(n))}), {}); .reduce((res, n) => Object.assign(res, { [n]: p.span(p.code.span(n)) }), {});
// scopeIdentifierSpan // scopeIdentifierSpan
const { const {
declaration, declaration,
// scopeType, scopeTypeEnd, // scopeType, scopeTypeEnd,
// scopeIdentifier, scopeIdentifierEnd, // scopeIdentifier, scopeIdentifierEnd,
varType, varTypeEnd, varType, varTypeEnd,
varSize, varSizeEnd, varSize, varSizeEnd,
varId, varIdEnd, varId, varIdEnd,
varName, varNameEnd, varName, varNameEnd,
inDeclaration, inDeclaration,
simulation, simulation,
inSimulation, inSimulation,
simulationTime, simulationTime,
simulationVector, simulationVectorEnd, simulationVectorRecovery, simulationVector, simulationVectorEnd, simulationVectorRecovery,
simulationId simulationId
} = ` } = `
declaration declaration
varType varTypeEnd varType varTypeEnd
varSize varSizeEnd varSize varSizeEnd
@ -102,211 +103,178 @@ const generate = (cb) => {
simulationVector simulationVectorEnd simulationVectorRecovery simulationVector simulationVectorEnd simulationVectorRecovery
simulationId simulationId
` `
.trim().split(/\s+/) .trim().split(/\s+/)
.reduce((res, n) => Object.assign(res, {[n]: p.node(n)}), {}); .reduce((res, n) => Object.assign(res, { [n]: p.node(n) }), {});
// scopeType scopeTypeEnd // scopeType scopeTypeEnd
// scopeIdentifier scopeIdentifierEnd // scopeIdentifier scopeIdentifierEnd
const enddefinitions = p.node('inDeclarationEnd'); 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
});
declaration const cmd = objection({
.match(spaces, declaration) $comment: 1,
// .select(cmd('$scope'), $date: 2,
// p.invoke(p.code.store('command'), commandSpan.start(scopeType))) $scope: 3,
// .select(cmd('$var'), $timescale: 4,
// p.invoke(p.code.store('command'), commandSpan.start(varType))) $upscope: 5,
.select(cmd('$scope $var $upscope $comment $date $timescale $version'), $var: 6,
p.invoke(p.code.store('command'), commandSpan.start(inDeclaration))) $version: 7,
.select(cmd('$enddefinitions'), $enddefinitions: 8,
p.invoke(p.code.store('command'), commandSpan.start(enddefinitions))) $dumpall: 9,
.otherwise(p.error(1, 'Expected declaration command')); $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)); // $var reg 3 ( r_reg [2:0] $end
// scopeIdentifierEnd.match(spaces, scopeIdentifierSpan.end(inDeclaration)).skipTo(scopeIdentifierEnd); // ^
// $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); // $var reg 3 ( r_reg [2:0] $end
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 varId.match(spaces, varId).otherwise(varIdSpan.start(varIdEnd));
// ^ varIdEnd.match(spaces, varIdSpan.end(varName)).skipTo(varIdEnd);
varSize.match(spaces, varSize).otherwise(varSizeSpan.start(varSizeEnd)); // $var reg 3 ( r_reg [2:0] $end
varSizeEnd.match(spaces, varSizeSpan.end(varId)).skipTo(varSizeEnd); // ^^^^^
// $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)); // $end
varIdEnd.match(spaces, varIdSpan.end(varName)).skipTo(varIdEnd);
// $var reg 3 ( r_reg [2:0] $end inDeclaration
// ^^^^^ .match('$end', commandSpan.end(declaration))
.skipTo(inDeclaration);
varName.match(spaces, varName).otherwise(varNameSpan.start(varNameEnd)); enddefinitions
varNameEnd.match('$end', commandSpan.end(varNameSpan.end(declaration))).skipTo(varNameEnd); .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 inSimulation
.match('$end', commandSpan.end(declaration)) .match('$end', commandSpan.end(simulation))
.skipTo(inDeclaration); .skipTo(inSimulation);
enddefinitions simulationTime
.match('$end', commandSpan.end(simulation)) .match(spaces, timeSpan.end(p.invoke(p.code.span('onTime'), simulation)))
.skipTo(enddefinitions); .skipTo(simulationTime);
simulation simulationVector
.match([' ', '\r', '\n', '\t', '$dumpvars', '$end'], simulation) .select(
.select(cmd('$dumpall $dumpoff $dumpon $comment'), {
p.invoke(p.code.store('command'), commandSpan.start(inSimulation))) 0: 0,
.select(cmd('#'), 1: 1,
p.invoke(p.code.store('command'), timeSpan.start(simulationTime))) x: 2, X: 2,
.select(cmd('0 1 x X z Z u U w W l L h H -'), z: 3, Z: 3,
p.invoke(p.code.store('command'), idSpan.start(simulationId))) u: 3, U: 3, // VHDL states
.select(cmd('b B r R'), w: 3, W: 3,
p.invoke(p.code.store('command'), simulationVector)) l: 3, L: 3,
.otherwise(p.error(4, 'Expected simulation command')); 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 simulationVectorEnd
.match('$end', commandSpan.end(simulation)) .match(lineSpaces, idSpan.start(simulationId))
.skipTo(inSimulation); .skipTo(simulationVectorRecovery);
simulationTime simulationVectorRecovery
.match(spaces, timeSpan.end(p.invoke(p.code.span('onTime'), simulation))) .select(
.skipTo(simulationTime); {
'\n': 1, '\r': 1
},
p.invoke(
p.code.value('onRecover'),
{ 1: p.error(6, 'recover') },
simulation
)
)
.skipTo(simulationVectorRecovery);
simulationVector simulationId
.select( .match(spaces, idSpan.end(p.invoke(p.code.span('onId'), simulation)))
{ .skipTo(simulationId);
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);
simulationVectorEnd const artifacts = p.build(declaration);
.match(lineSpaces, idSpan.start(simulationId))
.skipTo(simulationVectorRecovery);
simulationVectorRecovery fs.writeFileSync(prj + '.h', artifacts.header);
.select( // fs.writeFileSync('verilog_preprocessor.bc', artifacts.bitcode);
{ fs.writeFileSync(prj + '.c', artifacts.c);
'\n': 1, '\r': 1
},
p.invoke(
p.code.value('onRecover'),
{1: p.error(6, 'recover')},
simulation
)
)
.skipTo(simulationVectorRecovery);
simulationId // const dot = new llparseDot.Dot();
.match(spaces, idSpan.end(p.invoke(p.code.span('onId'), simulation))) // fs.writeFileSync(prj + '.dot', dot.build(declaration));
.skipTo(simulationId);
const artifacts = p.build(declaration); if (cb) {
cb();
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();
}; };
generate(gyp); generate(gyp);
// generate();
/* eslint camelcase: 0 */ /* eslint camelcase: 0 */

View File

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

View File

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

View File

@ -4,10 +4,217 @@
const createVCD = require('../out/vcd.js'); const createVCD = require('../out/vcd.js');
const webVcdParser = require('../lib/web-vcd-parser.js'); const webVcdParser = require('../lib/web-vcd-parser.js');
async function getVcdStream() { /**
const wasm = await createVCD(); * @typedef {{ maxChunkLength: number, useGcd: boolean }} consumeConfig
const ostream = await webVcdParser(wasm); *
return ostream; * @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'; 'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
module.exports = skip => { module.exports = skip => {
let start = 0; let start = 0;
let stop = 0; let stop = 0;
let up = 0; let up = 0;
let total = 0; let total = 0;
return { return {
on: (time, cmd) => { on: (time, cmd) => {
if (time > skip) { if (time > skip) {
if (start == 0) { if (start == 0) {
start = time; start = time;
} else { } else {
stop = time; stop = time;
} }
} }
if (cmd === 15) { if (cmd === 15) {
up = time; up = time;
} else } else
if (cmd === 14) { if (cmd === 14) {
total += (time - up); up = time; total += (time - up); up = time;
} }
}, },
time: () => stop - start, time: () => stop - start,
uptime: () => total uptime: () => total
}; };
}; };

View File

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

View File

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

View File

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

View File

@ -1,4 +1,7 @@
'use strict'; 'use strict';
/* eslint-disable no-console */
/* eslint-disable indent */
/* eslint-disable no-unused-vars */
const stream = require('stream'); const stream = require('stream');
const EventEmitter = require('events').EventEmitter; 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_napi": "nyc -r=text -r=lcov mocha test/napi_* ",
"nyc_mocha_wasm": "nyc -r=text -r=lcov mocha test/wasm_*", "nyc_mocha_wasm": "nyc -r=text -r=lcov mocha test/wasm_*",
"nyc_mocha": "nyc -r=text -r=lcov mocha", "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", "testonly": "nyc -r=text -r=lcov mocha",
"watch": "mocha --watch", "watch": "mocha --watch",
"build.web": "browserify ./lib/vcd-web.js | terser --compress -o demo/vcd-web.min.js", "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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <limits.h>
#include "vcd_parser.h" #include "vcd_parser.h"
#ifndef VCDWASM #ifndef VCDWASM
@ -14,343 +15,364 @@
typedef void* napi_env; typedef void* napi_env;
#endif #endif
// #define LOGSPAN // #define LOGSPAN
// #define LOGSPAN printf("%s\n", __FUNCTION__); // #define LOGSPAN printf("%s\n", __FUNCTION__);
#define ASSERT(val, expr) \
#define ASSERT(val, expr) \ if (expr != napi_ok) { \
if (expr != napi_ok) { \
napi_throw(env, val); \ napi_throw(env, val); \
} }
void strcopy(const unsigned char* p, const unsigned char* endp, unsigned char* dst) { void strcopy(const unsigned char* p, const unsigned char* endp,
const unsigned char* src; unsigned char* dst) {
src = p; const unsigned char* src;
while (src < (endp - 1)) { src = p;
*dst = *src; while (src < (endp - 1)) {
src++; *dst = *src;
dst++; src++;
} dst++;
*dst = 0; }
*dst = 0;
} }
void strconcat(const unsigned char* p, const unsigned char* endp, unsigned char* dst) { void strconcat(const unsigned char* p, const unsigned char* endp,
// printf("<len:%d>", endp - p); unsigned char* dst) {
dst += strlen((char *)dst); // go to the end of string // printf("<len:%d>", endp - p);
while (p < endp) { dst += strlen((char*)dst); // go to the end of string
*dst = *p; while (p < endp) {
p++; *dst = *p;
dst++; p++;
} dst++;
*dst = 0; }
*dst = 0;
} }
// FIXME use a better structure to match strings // FIXME use a better structure to match strings
int stringEq ( int stringEq(const unsigned char* i, // search pattern
const unsigned char* i, // search pattern const unsigned char* p, const unsigned char* endp) {
const unsigned char* p, if (*i == 0) {
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
return 1; return 1;
}
i++;
j++;
} }
while (*i != 0) { // skip to the end of pattern word const unsigned char* j;
i++; 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) { int commandSpan(vcd_parser_t* state, const unsigned char* p,
const uint8_t command = state->command; const unsigned char* endp) {
#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)) {
const uint8_t command = state->command; 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 #ifndef VCDWASM
napi_env env = state->napi_env; napi_env env = state->napi_env;
napi_value val;
ASSERT(val, napi_create_int64(env, time, &val)) // if (command == 5) { // $upscope
ASSERT(state->info, napi_set_named_property(env, state->info, "t0", val)) // 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 #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 #endif
}
state->time = time; if (command == 8) { // $enddefinitions
*(char *)state->timeStampStr = 0; *(char*)state->idStr = 0;
return 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;
} }