verilog: fixed 64-bit casting, constant issues

This commit is contained in:
Steven Hugg 2021-07-08 10:06:47 -05:00
parent 6ac29d78dc
commit 5ab0e397d3
5 changed files with 102 additions and 141 deletions

View File

@ -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",

View File

@ -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);
}

View File

@ -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"})
};
}
}

View File

@ -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);
});

View File

@ -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)) \