verilog: fuzzhdl

This commit is contained in:
Steven Hugg 2021-07-06 13:50:16 -05:00
parent ac55082863
commit 1ab0d290f8
6 changed files with 151 additions and 77 deletions

View File

@ -63,6 +63,7 @@
"test-web": "nightwatch -e chrome test/web",
"start": "electron .",
"fuzzbasic": "jsfuzz gen/common/basic/fuzz.js ~/basic/corpus/ --versifier false",
"fuzzhdl": "jsfuzz -r binaryen gen/common/hdl/fuzz.js ~/verilator/corpus/ --versifier false",
"machine": "node gen/tools/runmachine.js",
"mkdoc": "typedoc --out web/jsdoc src/common/"
},

35
src/common/hdl/fuzz.ts Normal file
View File

@ -0,0 +1,35 @@
//import binaryen = require('binaryen');
import { HDLModuleJS } from "./hdlruntime";
import { HDLModuleWASM } from "./hdlwasm";
import { CompileError, VerilogXMLParser } from "./vxmlparser";
export function fuzz(buf) {
var parser = new VerilogXMLParser();
var str = buf.toString();
try {
parser.parse(str);
} catch (e) {
if (e instanceof CompileError) return;
throw e;
}
/*
if (0) {
var wmod = new HDLModuleWASM(parser.modules['TOP'], parser.modules['@CONST-POOL@']);
wmod.traceBufferSize = 0x8000;
wmod.maxMemoryMB = 0.25;
wmod.init().then(() => {
wmod.powercycle();
wmod.tick2(10000);
wmod.dispose();
})
}
*/
if (1) {
var jmod = new HDLModuleJS(parser.modules['TOP'], parser.modules['@CONST-POOL@']);
jmod.init();
jmod.powercycle();
jmod.tick2(10000);
jmod.dispose();
}
}

View File

@ -1,6 +1,6 @@
import { byteArrayToString, safe_extend } from "../util";
import { HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, HDLModuleRunner, HDLTriop, HDLUnop, HDLValue, HDLVariableDef, HDLVarRef, isArrayItem, isArrayType, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef, isWhileop } from "./hdltypes";
import { HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, HDLModuleRunner, HDLSourceLocation, HDLTriop, HDLUnop, HDLValue, HDLVariableDef, HDLVarRef, isArrayItem, isArrayType, isBigConstExpr, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef, isWhileop } from "./hdltypes";
interface VerilatorUnit {
_ctor_var_reset(state) : void;
@ -10,6 +10,18 @@ interface VerilatorUnit {
_change_request(state) : boolean;
}
export class HDLError extends Error {
obj: any;
$loc: HDLSourceLocation;
constructor(obj: any, msg: string) {
super(msg);
Object.setPrototypeOf(this, HDLError.prototype);
this.obj = obj;
if (obj && obj.$loc) this.$loc = obj.$loc;
if (obj) console.log(obj);
}
}
export class HDLModuleJS implements HDLModuleRunner {
mod: HDLModuleDef;
@ -114,7 +126,7 @@ export class HDLModuleJS implements HDLModuleRunner {
return;
}
}
throw new Error(`model did not converge on reset()`)
throw new HDLError(null, `model did not converge on reset()`)
}
eval() {
@ -139,7 +151,7 @@ export class HDLModuleJS implements HDLModuleRunner {
return;
}
}
throw new Error(`model did not converge on eval()`)
throw new HDLError(null, `model did not converge on eval()`)
}
tick2(iters: number) {
@ -180,20 +192,20 @@ export class HDLModuleJS implements HDLModuleRunner {
if (isArrayItem(e) && isConstExpr(e.expr)) {
arr[e.index] = e.expr.cvalue;
} else {
throw new Error(`non-const expr in initarray`);
throw new HDLError(dt, `non-const expr in initarray`);
}
}
}
return arr;
}
throw new Error(`no default value for var type: ${vardef.name}`);
throw new HDLError(dt, `no default value for var type: ${vardef.name}`);
}
constValue(expr: HDLExpr) : number {
if (isConstExpr(expr)) {
return expr.cvalue;
} else {
throw new Error(`no const value for expr`);
throw new HDLError(expr, `no const value for expr`);
}
}
@ -220,7 +232,7 @@ export class HDLModuleJS implements HDLModuleRunner {
s += ` = ${this.constValue(e)}`; // TODO?
} else if (e.initValue != null) {
// TODO?
throw new Error(`can't init array here`);
throw new HDLError(e, `can't init array here`);
} else if (isLogicType(e.dtype) && e.dtype.left > 31) {
// TODO: hack for big ints ($readmem)
s += ` = []`;
@ -228,6 +240,8 @@ export class HDLModuleJS implements HDLModuleRunner {
return s;
} else if (isConstExpr(e)) {
return `0x${e.cvalue.toString(16)}`;
} else if (isBigConstExpr(e)) {
return e.bigvalue.toString(); // TODO?
} else if (isTriop(e)) {
switch (e.op) {
case 'if':
@ -239,8 +253,7 @@ export class HDLModuleJS implements HDLModuleRunner {
case 'condbound':
return `(${this.expr2js(e.cond, {cond:true})} ? ${this.expr2js(e.left)} : ${this.expr2js(e.right)})`;
default:
console.log(e);
throw Error(`unknown triop ${e.op}`);
throw new HDLError(e, `unknown triop ${e.op}`);
}
} else if (isBinop(e)) {
switch (e.op) {
@ -260,8 +273,7 @@ export class HDLModuleJS implements HDLModuleRunner {
default:
var jsop = OP2JS[e.op];
if (!jsop) {
console.log(e);
throw Error(`unknown binop ${e.op}`)
throw new HDLError(e, `unknown binop ${e.op}`)
}
if (jsop.startsWith('?')) {
jsop = jsop.substr(1);
@ -287,9 +299,10 @@ export class HDLModuleJS implements HDLModuleRunner {
case 'extends':
let shift = 32 - (e as HDLExtendop).widthminv;
return `((${this.expr2js(e.left)} << ${shift}) >> ${shift})`;
case 'redxor':
return `this.$$${e.op}(${this.expr2js(e.left)})`;
default:
console.log(e);
throw Error(`unknown unop ${e.op}`);
throw new HDLError(e, `unknown unop ${e.op}`);
}
} else if (isBlock(e)) {
// TODO: { e } ?
@ -330,11 +343,11 @@ export class HDLModuleJS implements HDLModuleRunner {
return `${this.expr2js(e)}.forEach((a) => a.fill(0))`
} else {
// TODO: 3d arrays?
throw Error(`unsupported data type for reset: ${JSON.stringify(e.dtype)}`);
throw new HDLError(e, `unsupported data type for reset: ${JSON.stringify(e.dtype)}`);
}
}
} else {
throw Error(`can only reset var refs`);
throw new HDLError(e, `can only reset var refs`);
}
}
@ -379,16 +392,27 @@ export class HDLModuleJS implements HDLModuleRunner {
var strfn = byteArrayToString(barr); // convert to string
// parse hex/binary file
var strdata = this.getFileData(strfn) as string;
if (strdata == null) throw Error("Could not $readmem '" + strfn + "'");
if (strdata == null) throw new HDLError(null, "Could not $readmem '" + strfn + "'");
var data = strdata.split('\n').filter(s => s !== '').map(s => parseInt(s, ishex ? 16 : 2));
console.log('$readmem', ishex, strfn, data.length);
// copy into destination array
if (memp === null) throw Error("No destination array to $readmem " + strfn);
if (memp.length < data.length) throw Error("Destination array too small to $readmem " + strfn);
if (memp === null) throw new HDLError(null, "No destination array to $readmem " + strfn);
if (memp.length < data.length) throw new HDLError(null, "Destination array too small to $readmem " + strfn);
for (i=0; i<data.length; i++)
memp[i] = data[i];
}
$time(o) {
return new Date().getTime();
}
$$redxor(r: number) : number {
r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); r=(r^(r>>16));
return r;
}
//
isStopped() { return this.stopped; }
isFinished() { return this.finished; }

View File

@ -1,6 +1,7 @@
import { hasDataType, HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLDataTypeObject, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, HDLModuleRunner, HDLSourceLocation, HDLTriop, HDLUnop, HDLValue, HDLVariableDef, HDLVarRef, HDLWhileOp, isArrayItem, isArrayType, isBigConstExpr, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef, isWhileop } from "./hdltypes";
import binaryen = require('binaryen');
import { hasDataType, HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLDataTypeObject, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, HDLModuleRunner, HDLSourceLocation, HDLTriop, HDLUnop, HDLValue, HDLVariableDef, HDLVarRef, HDLWhileOp, isArrayItem, isArrayType, isBigConstExpr, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef, isWhileop } from "./hdltypes";
import { HDLError } from "./hdlruntime";
const VERILATOR_UNIT_FUNCTIONS = [
"_ctor_var_reset",
@ -27,18 +28,6 @@ const TRACEBUF = "$$tbuf";
///
export class HDLError extends Error {
obj: any;
$loc: HDLSourceLocation;
constructor(obj: any, msg: string) {
super(msg);
Object.setPrototypeOf(this, HDLError.prototype);
this.obj = obj;
if (obj && obj.$loc) this.$loc = obj.$loc;
if (obj) console.log(obj);
}
}
function getDataTypeSize(dt: HDLDataType) : number {
if (isLogicType(dt)) {
if (dt.left <= 7)
@ -195,15 +184,13 @@ export class HDLModuleWASM implements HDLModuleRunner {
traceEndOffset: number;
trace: any;
getFileData = null;
maxMemoryMB : number;
constructor(moddef: HDLModuleDef, constpool: HDLModuleDef) {
constructor(moddef: HDLModuleDef, constpool: HDLModuleDef, maxMemoryMB?: number) {
this.hdlmod = moddef;
this.constpool = constpool;
this.bmod = new binaryen.Module();
this.genTypes();
var membytes = this.globals.len;
var memblks = Math.ceil(membytes / 65536);
this.bmod.setMemory(memblks, memblks, MEMORY); // memory is in 64k chunks
this.maxMemoryMB = maxMemoryMB || 16;
this.genMemory();
this.genFuncs();
}
@ -300,11 +287,26 @@ export class HDLModuleWASM implements HDLModuleRunner {
if (this.bmod) {
this.bmod.dispose();
this.bmod = null;
this.instance = null;
this.databuf = null;
this.data8 = null;
this.data16 = null;
this.data32 = null;
}
}
//
private genMemory() {
this.bmod = new binaryen.Module();
this.genTypes();
var membytes = this.globals.len;
if (membytes > this.maxMemoryMB*1024*1024)
throw new HDLError(null, `cannot allocate ${membytes} bytes, limit is ${this.maxMemoryMB} MB`);
var memblks = Math.ceil(membytes / 65536);
this.bmod.setMemory(memblks, memblks, MEMORY); // memory is in 64k chunks
}
private genTypes() {
// generate global variables
var state = new Struct();

View File

@ -1,4 +1,5 @@
import { HDLError } from "./hdlruntime";
import { HDLAlwaysBlock, HDLArrayItem, HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLDataTypeObject, HDLExpr, HDLExtendop, HDLFile, HDLFuncCall, HDLHierarchyDef, HDLInstanceDef, HDLLogicType, HDLModuleDef, HDLNativeType, HDLPort, HDLSensItem, HDLSourceLocation, HDLTriop, HDLUnit, HDLUnop, HDLUnpackArray, HDLValue, HDLVariableDef, HDLVarRef, HDLWhileOp, isArrayType, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef } from "./hdltypes";
/**
@ -16,6 +17,13 @@ import { HDLAlwaysBlock, HDLArrayItem, HDLBinop, HDLBlock, HDLConstant, HDLDataT
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
*/
export class CompileError extends Error {
constructor(obj: HDLSourceLocation|XMLNode, msg: string) {
super(msg);
Object.setPrototypeOf(this, CompileError.prototype);
}
}
interface XMLNode {
type: string;
text: string | null;
@ -47,10 +55,11 @@ function parseXMLPoorly(s: string, openfn?: XMLVisitFunction, closefn?: XMLVisit
function closetop() {
top = stack.pop();
if (top.type != ident) throw Error("mismatch close tag: " + ident);
if (top == null || top.type != ident) throw new CompileError(node, "mismatch close tag: " + ident);
if (closefn) {
top.obj = closefn(top);
}
if (stack.length == 0) throw new CompileError(null, "close tag without open: " + ident);
stack[stack.length - 1].children.push(top);
}
function parseattrs(as: string): { [id: string]: string } {
@ -79,24 +88,16 @@ function parseXMLPoorly(s: string, openfn?: XMLVisitFunction, closefn?: XMLVisit
}
if (attrs && attrs.endsWith('/')) closetop();
} else if (content != null) {
if (stack.length == 0) throw new CompileError(null, "content without element");
var txt = escapeXML(content as string).trim();
if (txt.length) stack[stack.length - 1].text = txt;
}
}
if (stack.length != 1) throw Error("tag not closed");
if (stack[0].type != '?xml') throw Error("?xml needs to be first element");
if (stack.length != 1) throw new CompileError(null, "tag not closed");
if (stack[0].type != '?xml') throw new CompileError(null, "?xml needs to be first element");
return top;
}
export class CompileError extends Error {
$loc : HDLSourceLocation;
constructor(msg: string, loc: HDLSourceLocation) {
super(msg);
Object.setPrototypeOf(this, CompileError.prototype);
this.$loc = loc;
}
}
export class VerilogXMLParser implements HDLUnit {
files: { [id: string]: HDLFile } = {};
@ -131,12 +132,13 @@ export class VerilogXMLParser implements HDLUnit {
}
name2js(s: string) {
if (s == null) throw new CompileError(null, `no name`);
return s.replace(/[^a-z0-9_]/gi, '$');
}
findChildren(node: XMLNode, type: string, required: boolean) : XMLNode[] {
var arr = node.children.filter((n) => n.type == type);
if (arr.length == 0 && required) throw Error(`no child of type ${type}`);
if (arr.length == 0 && required) throw new CompileError(node, `no child of type ${type}`);
return arr;
}
@ -165,7 +167,7 @@ export class VerilogXMLParser implements HDLUnit {
instances: [],
vardefs: {},
}
if (this.cur_module) throw new Error(`nested modules not supported`);
if (this.cur_module) throw new CompileError(node, `nested modules not supported`);
this.cur_module = module;
return module;
}
@ -176,8 +178,7 @@ export class VerilogXMLParser implements HDLUnit {
this.defer(() => {
def.dtype = this.dtypes[dtype_id];
if (!def.dtype) {
console.log(node);
throw Error(`Unknown data type ${dtype_id} for ${node.type}`);
throw new CompileError(node, `Unknown data type ${dtype_id} for ${node.type}`);
}
})
}
@ -193,19 +194,19 @@ export class VerilogXMLParser implements HDLUnit {
else
return BigInt('0x' + numstr);
} else {
throw Error(`could not parse constant "${s}"`);
throw new CompileError(null, `could not parse constant "${s}"`);
}
}
resolveVar(s: string, mod: HDLModuleDef) : HDLVariableDef {
var def = mod.vardefs[s];
if (def == null) throw Error(`could not resolve variable "${s}"`);
if (def == null) throw new CompileError(null, `could not resolve variable "${s}"`);
return def;
}
resolveModule(s: string) : HDLModuleDef {
var mod = this.modules[s];
if (mod == null) throw Error(`could not resolve module "${s}"`);
if (mod == null) throw new CompileError(null, `could not resolve module "${s}"`);
return mod;
}
@ -325,7 +326,7 @@ export class VerilogXMLParser implements HDLUnit {
}
visit_inititem(node: XMLNode) : HDLArrayItem {
if (node.children.length != 1) throw Error('expected 1 children');
this.expectChildren(node, 1, 1);
return {
index: parseInt(node.attrs['index']),
expr: node.children[0].obj
@ -334,7 +335,7 @@ export class VerilogXMLParser implements HDLUnit {
visit_cfunc(node: XMLNode) : HDLBlock {
if (this.cur_module == null) { // TODO?
console.log('no module open, skipping', node);
//console.log('no module open, skipping', node);
return;
}
var block = this.visit_begin(node);
@ -363,7 +364,7 @@ export class VerilogXMLParser implements HDLUnit {
}
visit_port(node: XMLNode) : HDLPort {
if (node.children.length != 1) throw Error('expected 1 children');
this.expectChildren(node, 1, 1);
var varref: HDLPort = {
$loc: this.parseSourceLocation(node),
name: node.attrs['name'],
@ -379,7 +380,12 @@ export class VerilogXMLParser implements HDLUnit {
}
visit_module_files(node: XMLNode) {
node.children.forEach((n) => this.files[(n.obj as HDLFile).id].isModule = true);
node.children.forEach((n) => {
if (n.obj) {
var file = this.files[(n.obj as HDLFile).id];
if (file) file.isModule = true;
}
});
}
visit_file(node: XMLNode) {
@ -404,9 +410,12 @@ export class VerilogXMLParser implements HDLUnit {
}
visit_cells(node: XMLNode) {
this.expectChildren(node, 1, 9999);
var hier = node.children[0].obj as HDLHierarchyDef;
var hiername = hier.name;
this.hierarchies[hiername] = hier;
if (hier != null) {
var hiername = hier.name;
this.hierarchies[hiername] = hier;
}
}
visit_cell(node: XMLNode) : HDLHierarchyDef {
@ -449,8 +458,7 @@ export class VerilogXMLParser implements HDLUnit {
default:
dtype = this.dtypes[dtypename];
if (dtype == null) {
console.log(node);
throw Error(`unknown data type ${dtypename}`);
throw new CompileError(node, `unknown data type ${dtypename}`);
}
}
this.dtypes[id] = dtype;
@ -471,18 +479,18 @@ export class VerilogXMLParser implements HDLUnit {
this.dtypes[id] = dtype;
this.defer(() => {
dtype.subtype = this.dtypes[sub_dtype_id];
if (!dtype.subtype) throw Error(`Unknown data type ${sub_dtype_id} for array`);
if (!dtype.subtype) throw new CompileError(node, `Unknown data type ${sub_dtype_id} for array`);
})
return dtype;
} else {
throw Error(`could not parse constant exprs in array`)
throw new CompileError(node, `could not parse constant exprs in array`)
}
}
visit_senitem(node: XMLNode) : HDLSensItem {
var edgeType = node.attrs['edgeType'];
if (edgeType != "POS" && edgeType != "NEG")
throw Error("POS/NEG required")
throw new CompileError(node, "POS/NEG required")
return {
$loc: this.parseSourceLocation(node),
edgeType: edgeType,
@ -505,8 +513,13 @@ export class VerilogXMLParser implements HDLUnit {
visit_constpool(node: XMLNode) {
}
expectChildren(node: XMLNode, low: number, high: number) {
if (node.children.length < low || node.children.length > high)
throw new CompileError(node, `expected between ${low} and ${high} children`);
}
__visit_unop(node: XMLNode) : HDLUnop {
if (node.children.length != 1) throw Error('expected 1 children');
this.expectChildren(node, 1, 1);
var expr: HDLUnop = {
$loc: this.parseSourceLocation(node),
op: node.type,
@ -521,12 +534,12 @@ export class VerilogXMLParser implements HDLUnit {
var unop = this.__visit_unop(node) as HDLExtendop;
unop.width = parseInt(node.attrs['width']);
unop.widthminv = parseInt(node.attrs['widthminv']);
if (unop.width != 32) throw Error(`extends width ${unop.width} != 32`)
if (unop.width != 32) throw new CompileError(node, `extends width ${unop.width} != 32`)
return unop;
}
__visit_binop(node: XMLNode) : HDLBinop {
if (node.children.length != 2) throw Error('expected 2 children');
this.expectChildren(node, 2, 2);
var expr: HDLBinop = {
$loc: this.parseSourceLocation(node),
op: node.type,
@ -539,7 +552,7 @@ export class VerilogXMLParser implements HDLUnit {
}
visit_if(node: XMLNode) : HDLTriop {
if (node.children.length < 2 || node.children.length > 3) throw Error('expected 2 or 3 children');
this.expectChildren(node, 2, 3);
var expr: HDLTriop = {
$loc: this.parseSourceLocation(node),
op: 'if',
@ -553,7 +566,7 @@ export class VerilogXMLParser implements HDLUnit {
// while and for loops
visit_while(node: XMLNode) : HDLWhileOp {
if (node.children.length < 2 || node.children.length > 4) throw Error('expected 2-4 children');
this.expectChildren(node, 2, 4);
var expr: HDLWhileOp = {
$loc: this.parseSourceLocation(node),
op: 'while',
@ -567,7 +580,7 @@ export class VerilogXMLParser implements HDLUnit {
}
__visit_triop(node: XMLNode) : HDLBinop {
if (node.children.length != 3) throw Error('expected 2 children');
this.expectChildren(node, 3, 3);
var expr: HDLTriop = {
$loc: this.parseSourceLocation(node),
op: node.type,
@ -677,8 +690,7 @@ export class VerilogXMLParser implements HDLUnit {
if (method) {
return method.bind(this)(node);
} else {
console.log(node);
throw Error(`no visitor for ${node.type}`)
throw new CompileError(node, `no visitor for ${node.type}`)
}
}

View File

@ -107,6 +107,6 @@ async function testJSvsWASM() {
}
}
testJSvsWASM();
//testWASM().then(testJS);
//testJSvsWASM();
testWASM().then(testJS).then(testJSvsWASM);