Merge pull request #22 from esromneb/ben/wasm

Added Wasm, existing NAPI is untouched
This commit is contained in:
Aliaksei Chapyzhenka 2020-09-28 12:38:15 -07:00 committed by GitHub
commit e1c5f08747
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1151 additions and 16 deletions

15
.gitignore vendored
View File

@ -1,8 +1,9 @@
.nyc_output
build
coverage
node_modules
package-lock.json
/.nyc_output
/build
/coverage
/node_modules
/package-lock.json
*.dot
tmp
vcd_parser.*
/tmp
/vcd_parser.*
/out

27
.travis.yml Normal file
View File

@ -0,0 +1,27 @@
notifications:
email: false
language: node_js
node_js:
- 14
sudo: required
services:
- docker
before_install:
- docker run -dit --name emscripten --user "$(id -u):$(id -g)" -v $(pwd):/src trzeci/emscripten:1.39.11-upstream bash
before_script:
- docker exec -it emscripten make wasm
# script:
# - ls -la
# - ls -la out
# - whoami
# - docker run -dit --name dtest -v $(pwd):/src esromneb/simple-clang bash
# - docker exec -it dtest make -C src test

130
Makefile Normal file
View File

@ -0,0 +1,130 @@
.PHONY: wasm all important clean
all: wasm
wasm: out/vcd.wasm
WASM_MAIN = wasm_main.cpp
HPP_FILES = \
vcd_parser.h \
wasm_main.hpp \
CPP_FILES = \
vcd_parser.c \
vcd_spans.c \
# this is a list of all C functions we want to publish to javascript
# In the main cpp file, each of these is wrapped in extern "C" {}
# the version here has a prepended underscore
# all lines must have trailing comma
EXPORT_STRING = \
"_execute", \
"_init", \
"_setTrigger", \
"_getTime", \
# warning and error flags
CLANG_WARN_FLAGS = \
-Wall -Wextra \
-Wno-ignored-qualifiers \
-Wundef \
-Werror=return-type \
-Wshadow \
# -Wconversion
CLANG_OTHER_FLAGS = \
-DVCDWASM \
CLANG_O_FLAG = '-O3'
ifdef NOOPT
CLANG_O_FLAG = ' '
endif
ifdef OPT3
CLANG_O_FLAG = '-O3'
endif
# works however slows down
#-s DISABLE_EXCEPTION_CATCHING=0 \
out/vcd.wasm: $(WASM_MAIN) $(CPP_FILES) $(HPP_FILES) Makefile
mkdir -p out
emcc $(WASM_MAIN) $(CPP_FILES) -s WASM=1 -o out/vcd.html \
-s DISABLE_EXCEPTION_CATCHING=0 \
-s ALLOW_MEMORY_GROWTH=1 \
-s ALLOW_TABLE_GROWTH=1 \
-s EXPORTED_FUNCTIONS='[$(EXPORT_STRING) "_main"]' \
-s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "addOnPostRun", "addFunction", "setValue", "getValue"]' \
$(CLANG_O_FLAG) $(CLANG_WARN_FLAGS) $(CLANG_OTHER_FLAGS)
.PHONY: patchlib patchlib1 patchlib2
# patchlib: patchlib1 patchlib2
# PTSRC1=lib/BehaviorTree.CPP/src/xml_parsing.cpp
# PTPAT1=patch/xml_parsing.patch
# # see https://stackoverflow.com/questions/7394290/how-to-check-return-value-from-the-shell-directive
# PAPPLIED1 := $(shell patch -R -p0 -s -f --dry-run $(PTSRC1) < $(PTPAT1) 1>&2 2> /dev/null > /dev/null; echo $$?)
# # patch is pretty annoying to use here
# # we would like to apply the patch, or skip if already applied exit 0
# # inorder to do this, we first need to run a dry-run in the reverse direction
# # then check the exit code, then run it in the forward direction if actually needed
# # we also have to do the complicated line above to deal with exit codes
# # see https://unix.stackexchange.com/questions/55780/check-if-a-file-or-folder-has-been-patched-already
# patchlib1: $(PTPAT1)
# ifneq ($(PAPPLIED1),0)
# @echo "$(PTSRC1) is unpatched.\n"
# patch --forward --reject-file=- $(PTSRC1) < $(PTPAT1)
# else
# @echo "$(PTSRC1) already patched, skipping..."
# endif
# PTSRC2=lib/BehaviorTree.CPP/src/tree_node.cpp
# PTPAT2=patch/tree_node.patch
# PAPPLIED2 := $(shell patch -R -p0 -s -f --dry-run $(PTSRC2) < $(PTPAT2) 1>&2 2> /dev/null > /dev/null; echo $$?)
# patchlib2: $(PTPAT2)
# ifneq ($(PAPPLIED2),0)
# @echo "$(PTSRC2) is unpatched.\n"
# patch --forward --reject-file=- $(PTSRC2) < $(PTPAT2)
# else
# @echo "$(PTSRC2) already patched, skipping..."
# endif
.PHONY: all build watch dev start test pretest lint jestc copydist cleandist prepare
.PHONY: test testonly
watch:
npm run watch
test:
npm run test
testonly:
npm run testonly
prepare:
npm run prepare
clean:
rm -rf out/*

View File

@ -9,6 +9,7 @@ Install
```
npm i vcd-stream
make wasm
```
Require

View File

@ -37,6 +37,7 @@ const properties = {
mask: 'ptr', // mask (x, z) of the signal on change event
digitCount: 'i32',
tmpStr: 'ptr',
tmpStr2: 'ptr',
stackPointer: 'i32',
id: 'ptr',
napi_env: 'ptr'

29
docs/Emscripten.md Normal file
View File

@ -0,0 +1,29 @@
# Emscripten
This file is a bit of notes on the Emscripten changes to the C code.
# Makefile
I added a `Makefile` to the project. A few key things here.
* warnings flags etc to in `CLANG_WARN_FLAGS`
* compile time `#define` go in `CLANG_OTHER_FLAGS`
* Every .c .cpp file goes in `CPP_FILES`
* Every .h .hpp file goes in `HPP_FILES` however not required
* This is only required if you want make "sensativity" to work correctly
* The emscripten `.js` file is minified by default. to disable this run
* `NOOPT=1 make wasm`
* Doing this will also compile the c code with `O0`
* Any c functions you want to access from javascript must be added to `EXPORT_STRING`
* When you add them, you must add a `_` prefix
# Install
I tested this project with `1.39.11`
```bash
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk/
git pull
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
```
See https://emscripten.org/docs/getting_started/downloads.html

View File

@ -2,12 +2,16 @@
const pkg = require('../package.json');
const parser = require('./parser.js');
const wasmparser = require('./wasmparser.js');
const and = require('./and.js');
const activity = require('./activity.js');
const wrapper = require('./wrapper.js');
module.exports = {
version: pkg.version,
and: and,
activity: activity,
parser: parser
parser: parser,
wasmparser: wasmparser,
wrapper: wrapper
};

View File

@ -12,9 +12,19 @@ module.exports = () => {
const s = new stream.Writable();
// const lifee = new EventEmitter();
// gets called by c with 1 argument, a number
const lifemit = s.emit.bind(s);
const triee = new EventEmitter();
// gets called by c with 5 arguments
// string eventName
// bigint state->time
// int command
// bigint state->value
// bigint state->mask
const triemit = triee.emit.bind(triee);
let triemit2 = triemit;

68
lib/wasmparser.js Normal file
View File

@ -0,0 +1,68 @@
'use strict';
const stream = require('stream');
const EventEmitter = require('events').EventEmitter;
// let lib = require('bindings')('vcd.node');
// const util = require('util');
module.exports = async () => {
const lib = require('../lib/wrapper.js')();
await lib.start();
const wires = {};
const info = {stack: [wires], wires: wires};
const s = new stream.Writable();
// gets called by c with 1 argument, a string
const lifemit = s.emit.bind(s);
const triee = new EventEmitter();
// gets called by c with 5 arguments
// string eventName
// number state->time
// int command
// int state->value
// int state->mask
const triemit = triee.emit.bind(triee);
let triemit2 = triemit;
const cxt = lib.init(lifemit, triemit, info);
s._write = function (chunk, encoding, callback) {
// console.log('about to write', chunk);
lib.execute(cxt, lifemit, triemit2, info, chunk);
// console.log(util.inspect(info, {showHidden: true, depth : null, colorize: true}));
// console.log(info.stack[0].top);
// console.log(info.stack[1]);
// console.log(info.stack[0].top == info.stack[1]);
callback();
};
s.change = {
on: (id, fn) => {
triemit2 = triemit;
// console.log(id, fn);
triee.on(id, fn);
const triggerString = triee.eventNames().join(' ') + ' ';
lib.setTrigger(cxt, triggerString);
},
any: fn => {
triemit2 = fn;
lib.setTrigger(cxt, '\0');
}
};
s.info = info;
s.getTime = () => lib.getTime(cxt);
s.start = lib.start;
return s;
};

179
lib/wrapper.js Normal file
View File

@ -0,0 +1,179 @@
'use strict';
/* global BigInt */
const dotProp = require('dot-prop');
function _waitForStart(mod) {
return new Promise((resolve)=>{
mod.addOnPostRun(resolve);
});
}
function u8ToBn(u8) {
var hex = [];
// let u8 = Uint8Array.from(buf);
u8.forEach(function (i) {
var h = i.toString(16);
if (h.length % 2) { h = '0' + h; }
hex.push(h);
});
hex.reverse();
return BigInt('0x' + hex.join(''));
}
let startCalled = 0;
module.exports = () => {
// let state = 0; // idle
// let total = 0;
// let start = 0;
const c = {};
const wasm = require('../out/vcd.js');
let bindCallback;
const bindCWrap = () => {
const w = wasm.cwrap;
c.execute = w('execute', 'number', ['number', 'number', 'number', 'number', 'number', 'string']);
c.init = w('init', 'number', ['number', 'number', 'number', 'number']);
c.getTime = w('getTime', 'number', ['number']);
c.setTrigger = w('setTrigger', 'number', ['number', 'string']);
};
const start = async() => {
if( !startCalled ) {
await _waitForStart(wasm);
startCalled++;
}
bindCWrap();
bindCallback();
};
// gets a string from a c heap pointer and length
const getString = (name, len) => {
const view = wasm.HEAPU8.subarray(name, name+len);
let string = '';
for (let i = 0; i < len; i++) {
string += String.fromCharCode(view[i]);
}
return string;
};
let boundInfo;
let boundSet;
let boundGet;
let ee = [];
let boundEE0;
let boundEE1;
let context = -1;
// wasm.addFunction can't be called until after
// start finishes
bindCallback = () => {
boundSet = wasm.addFunction(function(name, len, type, v0, v1) {
let prop = getString(name, len);
let tmp;
switch(type) {
// set number
case 0:
boundInfo[prop] = v0;
// console.log(`setting ${prop} to ${boundInfo[prop]}`);
break;
// set string
case 1:
boundInfo[prop] = getString(v0, v1);
// console.log(`setting ${prop} to ${boundInfo[prop]}`);
break;
// set string to path
case 2:
dotProp.set(boundInfo, prop, getString(v0, v1));
// console.log(`setting ${prop} to ${getString(v0, v1)}`);
break;
// path to path (any type)
case 3:
tmp = dotProp.get(boundInfo, getString(v0, v1));
// console.log(`for ${getString(v0, v1)} got ${tmp}, set to ${prop}`);
dotProp.set(boundInfo, prop, tmp);
break;
// create empty object at path
case 4:
// console.log(`${prop} is new {}`);
dotProp.set(boundInfo, prop, {});
break;
default: throw new Error();
}
// viiiii means returns void, accepts int int int int int
}, 'viiiii');
boundGet = wasm.addFunction(function(name, len) {
let prop = getString(name, len);
return prop;
}, 'iii');
boundEE0 = wasm.addFunction(function(name, len) {
ee[0](getString(name, len));
}, 'vii');
// const char* name, const size_t len, const uint64_t time, const uint8_t command, const int valueWords, const uint64_t* aValue, const uint64_t* aMask);
// boundEE1 = wasm.addFunction(function(eventName, l0, time, command, valueWords, value, mask) {
boundEE1 = wasm.addFunction(function(eventName, l0, time, command, valueWords, value, mask) {
const name = getString(eventName, l0);
// console.log(`event name`);
// console.log(`event ${name} time ${time} cmd ${command} wrds ${valueWords}`);
const view0 = wasm.HEAPU8.subarray(value, value+(valueWords*8));
const view1 = wasm.HEAPU8.subarray(mask, mask+(valueWords*8));
let bigValue = u8ToBn(view0);
let bigMask = u8ToBn(view1);
// console.log(bigValue.toString(16));
ee[1](name, time, command, bigValue, bigMask);
}, 'viiiiiii');
};
return {
start,
c,
init: (cb0, cb1, info) => {
boundInfo = info;
ee[0] = cb0;
ee[1] = cb1;
context = c.init(boundEE0, boundEE1, boundSet, boundGet);
return context;
},
execute: (ctx, cb0, cb1, info, chunk) => {
boundInfo = info;
ee[0] = cb0;
ee[1] = cb1;
c.execute(ctx, boundEE0, boundEE1, boundSet, boundGet, chunk.toString());
},
setTrigger: (ctx, triggerString) => {
return c.setTrigger(ctx, triggerString);
},
getTime: (ctx) => {
return BigInt(c.getTime(ctx));
}
};
};

View File

@ -5,6 +5,8 @@
"main": "lib/index.js",
"scripts": {
"test": "eslint bin lib && nyc -r=text -r=lcov mocha",
"testonly": "nyc -r=text -r=lcov mocha",
"watch": "mocha --watch",
"install": "node bin/build.js",
"prepare": "node bin/build.js"
},
@ -31,6 +33,7 @@
"dependencies": {
"async": "^3.1.0",
"bindings": "^1.5.0",
"dot-prop": "^5.3.0",
"fs-extra": "^9.0.1",
"llparse": "^7.0.1"
},

View File

@ -71,11 +71,7 @@ $timescale 1ns $end
0"}G
#200
1"}G
bzzzzxxxx11110000ZZZZXXXX11110000zzzzxxx`);
// break in the middle of the number scan
inst.write( `x11110000zzzzxxxx11110000 {u
bzzzzxxxx11110000ZZZZXXXX11110000zzzzxxxx11110000zzzzxxxx11110000 {u
#300
0"}G
b1111000000000000000000000000000000000000000000000000000000000000 {u

76
test/wasm_any.js Normal file

File diff suppressed because one or more lines are too long

121
test/wasm_basic.js Normal file
View File

@ -0,0 +1,121 @@
'use strict';
const expect = require('chai').expect;
const lib = require('../lib/index.js');
describe('wasm basic', () => {
it('typeof vcd', done => {
expect(lib.wasmparser).to.be.an('function');
done();
});
it('typeof vcd instance', done => {
expect(lib.wasmparser()).to.be.an('promise');
done();
});
it('fail: foo bar', async () => {
const inst = await lib.wasmparser();
expect(inst.write(Buffer.from(' foo bar ???'))).to.eq(true);
expect(inst.info).to.deep.eq({
stack: [{}],
status: 'declaration',
wires: {}
});
});
it('$comment', async () => {
const inst = await lib.wasmparser();
expect(inst.write(Buffer.from(
' \n $comment some text $end $comment more text $end ???'
))).to.eq(true);
expect(inst.info).to.deep.eq({
stack: [{}],
status: 'declaration',
wires: {}
});
});
it('$version', async () => {
const inst = await lib.wasmparser();
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',
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 */

129
test/wasm_dump.js Normal file
View File

@ -0,0 +1,129 @@
'use strict';
const expect = require('chai').expect;
const lib = require('../lib/index.js');
describe('wasm dump', () => {
let inst;
// return a promise, and mocha will wait until it resolves
before(() => {
const fn = async () => {
inst = await lib.wasmparser();
};
return fn();
});
it('simple wasm', done => {
const dump = [];
['"}G', '{u', 'u)'] // array of all signal ids
.map(id =>
inst.change.on(id, (time, cmd, value, mask) => {
dump.push({
id,
time,
cmd,
value,
mask
});
})
);
inst.on('finish', () => {
expect(inst.getTime()).to.eq(316n);
expect(dump).to.deep.eq([
{ id: '"}G', time: 100, cmd: 14, value: 0n, mask: 0n },
{ id: '"}G', time: 200, cmd: 15, value: 1n, mask: 0n },
{ id: '{u', time: 200, cmd: 19, value: 0xf0f0f0f0f0f0f0f0n, mask: 0xff00ff00ff00ff00n },
{ id: '"}G', time: 300, cmd: 14, value: 0n, mask: 0n },
{ id: '{u', time: 300, cmd: 19, value: 0xf000000000000000n, mask: 0n },
{ id: '{u', time: 301, cmd: 19, value: 0x0f00000000000000n, mask: 0n },
{ id: '{u', time: 302, cmd: 19, value: 0x00f0000000000000n, mask: 0n },
{ id: '{u', time: 303, cmd: 19, value: 0x000f000000000000n, mask: 0n },
{ id: '{u', time: 304, cmd: 19, value: 0x0000f00000000000n, mask: 0n },
{ id: '{u', time: 305, cmd: 19, value: 0x00000f0000000000n, mask: 0n },
{ id: '{u', time: 306, cmd: 19, value: 0x000000f000000000n, mask: 0n },
{ id: '{u', time: 307, cmd: 19, value: 0x0000000f00000000n, mask: 0n },
{ id: '{u', time: 308, cmd: 20, value: 0x00000000f0000000n, mask: 0n },
{ id: '{u', time: 309, cmd: 19, value: 0x000000000f000000n, mask: 0n },
{ id: '{u', time: 310, cmd: 19, value: 0x0000000000f00000n, mask: 0n },
{ id: '{u', time: 311, cmd: 19, value: 0x00000000000f0000n, mask: 0n },
{ id: '{u', time: 312, cmd: 19, value: 0x000000000000f000n, mask: 0n },
{ id: '{u', time: 313, cmd: 19, value: 0x0000000000000f00n, mask: 0n },
{ id: '{u', time: 314, cmd: 19, value: 0x00000000000000f0n, mask: 0n },
{ id: '{u', time: 315, cmd: 19, value: 0x000000000000000fn, mask: 0n },
{ id: '"}G', time: 316, cmd: 15, value: 1n, mask: 0n },
])
// console.log(dump);
done();
});
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
#100
0"}G
#200
1"}G
bzzzzxxxx11110000ZZZZXXXX11110000zzzzxxx`);
// break in the middle of the number scan
inst.write( `x11110000zzzzxxxx11110000 {u
#300
0"}G
b1111000000000000000000000000000000000000000000000000000000000000 {u
#301
b0000111100000000000000000000000000000000000000000000000000000000 {u
#302
b0000000011110000000000000000000000000000000000000000000000000000 {u
#303
b0000000000001111000000000000000000000000000000000000000000000000 {u
#304
b0000000000000000111100000000000000000000000000000000000000000000 {u
#305
b0000000000000000000011110000000000000000000000000000000000000000 {u
#306
b0000000000000000000000001111000000000000000000000000000000000000 {u
#307
b0000000000000000000000000000111100000000000000000000000000000000 {u
#308
B0000000000000000000000000000000011110000000000000000000000000000 {u
#309
b0000000000000000000000000000000000001111000000000000000000000000 {u
#310
b0000000000000000000000000000000000000000111100000000000000000000 {u
#311
b0000000000000000000000000000000000000000000011110000000000000000 {u
#312
b0000000000000000000000000000000000000000000000001111000000000000 {u
#313
b0000000000000000000000000000000000000000000000000000111100000000 {u
#314
b0000000000000000000000000000000000000000000000000000000011110000 {u
#315
b0000000000000000000000000000000000000000000000000000000000001111 {u
#316
1"}G
`);
inst.end();
});
});
/* eslint-env mocha */

84
test/wasm_events.js Normal file
View File

@ -0,0 +1,84 @@
'use strict';
const expect = require('chai').expect;
const lib = require('../lib/index.js');
describe('wasm events', () => {
let inst;
// return a promise, and mocha will wait until it resolves
before(() => {
const fn = async () => {
inst = await lib.wasmparser();
};
return fn();
});
it('$enddefinitions', done => {
inst.on('$enddefinitions', () => {
expect(inst.info).to.deep.eq({
status: 'simulation',
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 */

2
vcd.c
View File

@ -124,6 +124,7 @@ METHOD(init) {
static char triggerString [4096] = " ";
static char tmpStr [4096] = " ";
static char tmpStr2 [4096] = " ";
static uint64_t valueBuf [4096] = {};
static uint64_t maskBuf [4096] = {};
@ -131,6 +132,7 @@ METHOD(init) {
state->reason = "NO REASON";
state->napi_env = env;
state->tmpStr = tmpStr;
state->tmpStr2 = tmpStr2;
state->value = valueBuf;
state->mask = maskBuf;
state->digitCount = 0;

View File

@ -1,7 +1,22 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vcd_parser.h"
#ifndef VCDWASM
#include <node_api.h>
#else
#include "wasm_main.hpp"
#endif
#ifdef VCDWASM
typedef void* napi_env;
#endif
// #define LOGSPAN
// #define LOGSPAN printf("%s\n", __FUNCTION__);
#define ASSERT(val, expr) \
if (expr != napi_ok) { \
@ -60,6 +75,7 @@ int commandSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char
}
if (state->command == 8) { // $enddefinitions
#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))
@ -68,6 +84,10 @@ int commandSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char
// 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;
}
@ -75,17 +95,36 @@ int commandSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char
}
int scopeIdentifierSpan(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 name, obj, stack, top;
ASSERT(name, napi_create_string_latin1(env, (char*)p, (endp - p - 1), &name))
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
strcopy(p, endp, state->tmpStr); // load the value into temp string 1
// set stack[sp].`tmpStr` to {}
snprintf(state->tmpStr2, 4096, "stack.%d.%s", state->stackPointer, 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;
}
@ -95,14 +134,20 @@ int varSizeSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char
}
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);
@ -111,11 +156,21 @@ int varNameSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char
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, state->tmpStr);
set_path_to_path(state->tmpStr2, "varId");
#endif
return 0;
}
int idSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) {
#ifndef VCDWASM
napi_env env = state->napi_env;
#endif
const int valueWords = (state->digitCount >> 6) + 1;
uint64_t* value = state->value;
uint64_t* mask = state->mask;
@ -129,6 +184,7 @@ int idSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* end
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, (endp - p - 1), &eventName))
@ -138,6 +194,11 @@ int idSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* end
ASSERT(aMask, napi_create_bigint_words(env, 0, valueWords, mask, &aMask))
napi_value* argv[] = {&eventName, &aTime, &aCommand, &aValue, &aMask};
ASSERT(state->triee, napi_call_function(env, undefined, state->triee, 5, *argv, &return_val))
#else
strcopy(p, endp, state->tmpStr);
emit_triee(state->tmpStr, state->time, command, valueWords, value, mask);
#endif
}
for (int i = 0; i < valueWords; i++) {
value[i] = 0;

202
wasm_main.cpp Normal file
View File

@ -0,0 +1,202 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include "vcd_parser.h"
using namespace std;
/// Typedef used as part of c->js call
typedef void externalJsMethodZero(const char* name, const size_t len);
// typedef void externalJsMethodOne (const char* name, const size_t len, const uint64_t time, const uint8_t command, const int valueWords, const int aValue, const int aMask);
typedef void externalJsMethodOne (const char* name, const size_t len, const int time, const int command, const int valueWords, const int aValue, const int aMask);
typedef int externalJsGetProperty(const char* name, const size_t len);
typedef void externalJsSetProperty(const char* name, const size_t len, const int type, const int v0, const int v1);
/// function pointer for c->js
static externalJsMethodZero* externalZero = 0;
static externalJsMethodOne* externalOne = 0;
static externalJsSetProperty* bound_set_property = 0;
static externalJsGetProperty* bound_get_property = 0;
static struct vcd_parser_s* state;
extern "C" {
void set_property_int(const char* name, const int value) {
bound_set_property(name, strlen(name), 0, value, 0);
}
void set_property_string(const char* name, const char* value) {
bound_set_property(name, strlen(name), 1, (int)value, strlen(value));
}
void set_path_string(const char* name, const char* value) {
bound_set_property(name, strlen(name), 2, (int)value, strlen(value));
}
void set_path_to_path(const char* name, const char* value) {
bound_set_property(name, strlen(name), 3, (int)value, strlen(value));
}
void new_object_path(const char* name) {
bound_set_property(name, strlen(name), 4, 0, 0);
}
int get_property_int(const char* name) {
return bound_get_property(name, strlen(name));
}
void emit_lifee(const char* name) {
externalZero(name, strlen(name));
}
void emit_triee(const char* name, const int64_t time, const int command, const int valueWords, uint64_t* aValue, uint64_t* aMask) {
// return;
// externalOne(
// "hi"
// ,2
// ,time
// ,command
// ,0
// ,0
// ,0
// );
externalOne(
name,
strlen(name),
time,
command,
valueWords,
(int)aValue,
(int)aMask
);
}
// returns context
int init(
externalJsMethodZero* f0,
externalJsMethodOne* f1,
externalJsSetProperty* sfn,
externalJsGetProperty* gfn
) {
state = (struct vcd_parser_s*) malloc(sizeof *state);
const int32_t error = vcd_parser_init(state);
if (error) {
cout << "ERROR: " << error << "\n";
return -1;
}
bound_set_property = sfn;
bound_get_property = gfn;
externalZero = f0;
externalOne = f1;
state->lifee = 0;
state->triee = 0;
static char triggerString [4096] = " ";
static char tmpStr [4096] = " ";
static char tmpStr2 [4096] = " ";
static uint64_t valueBuf [4096] = {};
static uint64_t maskBuf [4096] = {};
state->trigger = triggerString;
state->reason = "NO REASON";
state->napi_env = 0;
state->tmpStr = tmpStr;
state->tmpStr2 = tmpStr2;
state->value = valueBuf;
state->mask = maskBuf;
state->digitCount = 0;
set_property_string("status", "declaration");
static int context = 0;
context++;
return context;
}
int32_t execute(
const int context,
externalJsMethodZero* f0,
externalJsMethodOne* f1,
externalJsSetProperty* sfn,
externalJsGetProperty* gfn,
char* p
) {
// cout << "execute got " << p << "\n";
// cout << "execute " << (int)sfn << " and got " << p << "\n";
bound_set_property = sfn;
bound_get_property = gfn;
externalZero = f0;
externalOne = f1;
const size_t plen = strlen(p);
const int32_t error = vcd_parser_execute(state, p, p + plen);
return error;
}
int setTrigger(const int context, char* triggerString) {
state->trigger = malloc(strlen(triggerString));
strcpy((char*)state->trigger, triggerString);
// cout << "setTrigger() got " << triggerString << "\n";
return 0;
}
uint64_t getTime(const int context) {
return state->time;
}
// void execute(
// const int context,
// externalJsMethodZero* f0,
// externalJsMethodOne* f1,
// externalJsSetProperty* sfn,
// externalJsGetProperty* gfn,
// char* chunk
// ) {
// // cout << "execute got " << p << "\n";
// cout << "execute " << (int)sfn << " and got " << chunk << "\n";
// bound_set_property = sfn;
// bound_get_property = gfn;
// externalZero = f0;
// externalOne = f1;
// set_property_int("foo", 10);
// int got = get_property_int("bar");
// cout << "got " << got << " for bar\n";
// }
int main(void) {
// cout << "main()\n";
return 0;
}
} // extern C

11
wasm_main.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
void set_property_int(const char* name, const int value);
void set_property_string(const char* name, const char* value);
void set_path_string(const char* name, const char* value);
void set_path_to_path(const char* name, const char* value);
void new_object_path(const char* name);
int get_property_int(const char* name);
void emit_lifee(const char* name);
void emit_triee(const char* name, const int64_t time, const int command, const int valueWords, uint64_t* aValue, uint64_t* aMask);