diff --git a/package.json b/package.json index 0ebdc59e..23273a75 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "build": "make", "test": "npm run test-node", "test-one": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000", - "test-node": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000 test/cli", + "test-node": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000 --reporter mocha-simple-html-reporter --reporter-options output=test/output/cli.html test/cli", "test-profile": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000 --prof test/cli", "test-worker": "NODE_PATH=$(pwd) mocha --timeout 60000 test/cli/testworker.js", "test-platforms": "NODE_PATH=$(pwd) mocha --timeout 60000 test/cli/testplatforms.js", diff --git a/src/common/hdl/hdlwasm.ts b/src/common/hdl/hdlwasm.ts index 2e6123df..a0fed167 100644 --- a/src/common/hdl/hdlwasm.ts +++ b/src/common/hdl/hdlwasm.ts @@ -2,6 +2,7 @@ import binaryen = require('binaryen'); import { hasDataType, HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLDataTypeObject, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, HDLModuleRunner, HDLSourceLocation, HDLSourceObject, 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"; +import { EmuHalt } from '../emu'; const VERILATOR_UNIT_FUNCTIONS = [ "_ctor_var_reset", @@ -71,17 +72,21 @@ function getArrayElementSizeFromExpr(e: HDLExpr) : number { } function getArrayValueSize(e: HDLExpr) : number { + return getDataTypeSize(getArrayValueType(e)); +} + +function getArrayValueType(e: HDLExpr) : HDLDataType { if (isVarRef(e)) { var dt = e.dtype; while (isArrayType(dt)) dt = dt.subtype; - return getDataTypeSize(dt); + return dt; } else if (isBinop(e) && e.op == 'arraysel') { - return getArrayValueSize(e.left); + return getArrayValueType(e.left); } else if (isBinop(e) && e.op == 'wordsel') { - return getArrayValueSize(e.left); + return getArrayValueType(e.left); } - throw new HDLError(e, `cannot figure out array value size`); + throw new HDLError(e, `cannot figure out array value type`); } function getAlignmentForSize(size) { @@ -410,7 +415,6 @@ export class HDLModuleWASM implements HDLModuleRunner { } private async genModule() { - //console.log(this.bmod.emitText()); var wasmData = this.bmod.emitBinary(); var compiled = await WebAssembly.compile(wasmData); this.instance = await WebAssembly.instantiate(compiled, this.getImportObject()); @@ -447,6 +451,7 @@ export class HDLModuleWASM implements HDLModuleRunner { return this.data32[(base + vref.offset) >> 2]; } } + return new Uint32Array(this.databuf, (base>>2) + vref.offset, vref.size >> 2); } return undefined; }, @@ -686,12 +691,12 @@ export class HDLModuleWASM implements HDLModuleRunner { private i3264rel(e: HDLBinop) { if (hasDataType(e.left) && hasDataType(e.right)) { - var leftsize = getDataTypeSize(e.left.dtype); - var rightsize = getDataTypeSize(e.right.dtype); - // TODO: left size should equal right size, both can be > i32 though - return leftsize > rightsize ? this.i3264(e.left.dtype) : this.i3264(e.right.dtype); + var lsize = getDataTypeSize(e.left.dtype); + var rsize = getDataTypeSize(e.right.dtype); + if (lsize > rsize) return this.i3264(e.left.dtype); + else return this.i3264(e.right.dtype); } - return this.i3264(e.dtype); + throw new HDLError(e, `can't ${e.op} arguments`); } private dataptr() : number { @@ -754,14 +759,14 @@ export class HDLModuleWASM implements HDLModuleRunner { const2wasm(e: HDLConstant, opts: Options) : number { var size = getDataTypeSize(e.dtype); if (isLogicType(e.dtype)) { - if (e.bigvalue != null) - return this.i3264(e.dtype).const( - Number(e.bigvalue & BigInt(0xffffffff)), - Number(e.bigvalue >> BigInt(32))); - else if (size <= 4) - return this.bmod.i32.const(e.cvalue); + if (e.bigvalue != null) { + let low = e.bigvalue & BigInt(0xffffffff); + let high = (e.bigvalue >> BigInt(32)) & BigInt(0xffffffff); + return this.i3264(e.dtype).const(Number(low), Number(high)); + } else if (size <= 4) + return this.bmod.i32.const(e.cvalue|0); else if (size <= 8) - return this.bmod.i64.const(e.cvalue, e.cvalue >> 32); // TODO: bigint? + return this.bmod.i64.const(e.cvalue|0, 0); else throw new HDLError(e, `constants > 64 bits not supported`) } else { @@ -861,7 +866,12 @@ export class HDLModuleWASM implements HDLModuleRunner { _arraysel2wasm(e: HDLBinop, opts:Options) : number { var addr = this.address2wasm(e); var elsize = getArrayValueSize(e); - return this.loadmem(e, addr, 0, elsize); + var ret = this.loadmem(e, addr, 0, elsize); + // cast to destination type, if it differs than fetch type + if (elsize != getDataTypeSize(e.dtype)) { + ret = this.castexpr(ret, getArrayValueType(e), e.dtype); + } + return ret; } _wordsel2wasm(e: HDLBinop, opts:Options) : number { @@ -932,11 +942,15 @@ export class HDLModuleWASM implements HDLModuleRunner { return val; } else if (tsrc.left <= 31 && tdst.left <= 31) { return val; + } else if (tsrc.left > 31 && tdst.left > 31) { + return val; + } else if (tsrc.left > 63 || tdst.left > 63) { + throw new HDLError(tdst, `values > 64 bits not supported`); } // TODO: signed? - else if (tsrc.left <= 31 && tdst.left == 63) { // 32 -> 64 + else if (tsrc.left <= 31 && tdst.left > 31) { // 32 -> 64 return this.bmod.i64.extend_u(val); - } else if (tsrc.left == 63 && tdst.left <= 31) { // 64 -> 32 + } else if (tsrc.left > 31 && tdst.left <= 31) { // 64 -> 32 return this.bmod.i32.wrap(val); } } @@ -982,11 +996,8 @@ export class HDLModuleWASM implements HDLModuleRunner { _extend2wasm(e: HDLExtendop, opts:Options) { var value = this.e2w(e.left); - var inst = this.i3264(e.dtype); - if (this.bmod.getFeatures() & binaryen.Features.SignExt) { - if (e.widthminv == 32 && e.width == 64) { - return this.bmod.i64.extend_u(value); - } + if (e.widthminv == 32 && e.width == 64) { + return this.bmod.i64.extend_u(value); } throw new HDLError(e, `cannot extend`); } @@ -1015,82 +1026,84 @@ export class HDLModuleWASM implements HDLModuleRunner { var rtn = inst.and( inst.const(1, 0), inst.popcnt(left)); // (num_set_bits & 1) - return rtn; //this.castexpr(rtn, e.dtype, e.left.dtype); + return this.castexpr(rtn, e.left.dtype, e.dtype); } else throw new HDLError(e, ''); } - binop(e: HDLBinop, f_op: (a:number, b:number) => number, upcastLeft?: boolean, upcastRight?: boolean) { + binop(e: HDLBinop, f_op: (a:number, b:number) => number) { var left = this.e2w(e.left); var right = this.e2w(e.right); + var upcast = null; + // if one argument is 64 bit and one is 32 bit, upcast the latter to 64 bits if (hasDataType(e.left) && hasDataType(e.right)) { var lsize = getDataTypeSize(e.left.dtype); var rsize = getDataTypeSize(e.right.dtype); var ltype = getBinaryenType(lsize); var rtype = getBinaryenType(rsize); - if (ltype != rtype && rsize > lsize && upcastLeft) { - //console.log(e, lsize, rsize); + if (ltype != rtype && rsize > lsize) { left = this.castexpr(left, e.left.dtype, e.right.dtype); - e.left.dtype = e.right.dtype; - } else if (ltype != rtype && lsize > rsize && upcastRight) { - //console.log(e, lsize, rsize); + upcast = e.right.dtype; + } else if (ltype != rtype && lsize > rsize) { right = this.castexpr(right, e.right.dtype, e.left.dtype); - e.right.dtype = e.left.dtype; + upcast = e.left.dtype; } else if (ltype != rtype) - {} // TODO: throw new HDLError(e, `wrong argument sizes`); + throw new HDLError(e, `wrong argument sizes ${lsize} and ${rsize}`); } var rtn = f_op(left, right); + // if we upcasted, and result is 32 bit, downcast to 32 bits + if (upcast) { + rtn = this.castexpr(rtn, upcast, e.dtype); + } return rtn; } + _or2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).or); + } + _and2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).and); + } + _xor2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).xor); + } + _shiftl2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).shl); + } + _shiftr2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).shr_u); + } + _shiftrs2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).shr_s); + } + _add2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).add); + } + _sub2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).sub); + } + _mul2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).mul); + } + _muls2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).mul); // TODO: signed? + } + _moddiv2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).rem_u); + } + _div2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).div_u); + } + _moddivs2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).rem_s); + } + _divs2wasm(e: HDLBinop) { + return this.binop(e, this.i3264rel(e).div_s); + } + relop(e: HDLBinop, f_op : (a:number,b:number)=>number) { return f_op(this.e2w(e.left), this.e2w(e.right)); } - - _or2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).or); - } - _and2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).and); - } - _xor2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).xor); - } - _shiftl2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).shl, false, true); - } - _shiftr2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).shr_u, false, true); - } - _shiftrs2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).shr_s, false, true); - } - _add2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).add); - } - _sub2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).sub); - } - _mul2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).mul); - } - _muls2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).mul); // TODO: signed? - } - _moddiv2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).rem_u); - } - _div2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).div_u); - } - _moddivs2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).rem_s); - } - _divs2wasm(e: HDLBinop) { - return this.binop(e, this.i3264(e.dtype).div_s); - } - - // TODO: i32/i64 _eq2wasm(e: HDLBinop) { return this.relop(e, this.i3264rel(e).eq); } diff --git a/src/platform/verilog.ts b/src/platform/verilog.ts index 43a7948f..ab184d79 100644 --- a/src/platform/verilog.ts +++ b/src/platform/verilog.ts @@ -776,6 +776,11 @@ var VerilogPlatform = function(mainElement, options) { extension:".js", blob: new Blob([top.getJSCode()], {type:"text/plain"}) }; + } else if (top instanceof HDLModuleWASM) { + return { + extension:".wasm", + blob: new Blob([top.bmod.emitText()], {type:"text/plain"}) + }; } } diff --git a/test/cli/testworker.js b/test/cli/testworker.js index 01348ad0..5f45d574 100644 --- a/test/cli/testworker.js +++ b/test/cli/testworker.js @@ -164,60 +164,6 @@ describe('Worker', function() { var csource = ab2str(fs.readFileSync('presets/sms-sg1000-libcv/cursorsmooth.c')); compile('sdcc', csource, 'sms-sg1000-libcv', done, 49152, 80, 0); }); - it('should compile verilog example', function(done) { - var csource = ab2str(fs.readFileSync('presets/verilog/lfsr.v')); - var msgs = [{code:csource, platform:"verilog", tool:"verilator", path:'main.v'}]; - var done2 = function(err, msg) { - var jscode = msg.output.code; - var fn = new Function(jscode); - assert.ok(fn); - done(err, msg); - }; - doBuild(msgs, done2, 2764, 0, 0); - }); - it('should NOT compile verilog example', function(done) { - var csource = "foobar"; - var msgs = [{code:csource, platform:"verilog", tool:"verilator", path:'foomain.v'}]; - doBuild(msgs, done, 0, 0, 1); - }); - it('should compile verilog inline assembler (JSASM)', function(done) { - var dependfiles = ["racing_game_cpu.v", "hvsync_generator.v", "sprite_bitmap.v", "sprite_renderer.v", "cpu8.v", "femto8.json"]; - var depends = []; - for (var dfile of dependfiles) { - var code = ab2str(fs.readFileSync('presets/verilog/' + dfile)); - depends.push({path:dfile, data:code}); - } - var msgs = [{ - updates:depends, - buildsteps:[{platform:"verilog", tool:"verilator", path:'racing_game_cpu.v', files:dependfiles}] - }]; - var done2 = function(err, msg) { - var jscode = msg.output.code; - var fn = new Function(jscode); - assert.ok(fn); - done(err, msg); - }; - doBuild(msgs, done2, 51459, 0, 0); - }); - it('should compile verilog assembler file (JSASM)', function(done) { - var dependfiles = ["test2.asm", "hvsync_generator.v", "font_cp437_8x8.v", "ram.v", "tile_renderer.v", "sprite_scanline_renderer.v", "lfsr.v", "sound_generator.v", "cpu16.v", "cpu_platform.v", "femto16.json"]; - var depends = []; - for (var dfile of dependfiles) { - var code = ab2str(fs.readFileSync('presets/verilog/' + dfile)); - depends.push({path:dfile, data:code}); - } - var msgs = [{ - updates:depends, - buildsteps:[{platform:"verilog", tool:"jsasm", path:'test2.asm', files:dependfiles}] - }]; - var done2 = function(err, msg) { - var jscode = msg.output.code; - var fn = new Function(jscode); - assert.ok(fn); - done(err, msg); - }; - doBuild(msgs, done2, 1997608, 0, 0); - }); it('should NOT preprocess SDCC', function(done) { compile('sdcc', 'int x=0\n#bah\n', 'mw8080bw', done, 0, 0, 1); }); diff --git a/test/cli/verilog/t_var_types.v b/test/cli/verilog/t_var_types.v index 273ad469..81e69d4f 100644 --- a/test/cli/verilog/t_var_types.v +++ b/test/cli/verilog/t_var_types.v @@ -4,6 +4,9 @@ // any use, without warranty, 2009 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 + +/* verilator lint_off UNDRIVEN */ + module t (/*AUTOARG*/); // IEEE: integer_atom_type @@ -90,14 +93,8 @@ module t (/*AUTOARG*/); function chandle f_chandle; chandle lv_chandle; f_chandle = lv_chandle; endfunction // verilator lint_on WIDTH -`ifdef verilator - // For verilator zeroinit detection to work properly, we need to x-rand-reset to all 1s. This is the default! - `define XINIT 1'b1 + `define XINIT 1'b0 `define ALL_TWOSTATE 1'b1 -`else - `define XINIT 1'bx - `define ALL_TWOSTATE 1'b0 -`endif `define CHECK_ALL(name,nbits,issigned,twostate,zeroinit) \ if (zeroinit ? ((name & 1'b1)!==1'b0) : ((name & 1'b1)!==`XINIT)) \