mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-01-27 08:31:17 +00:00
verilog: working on 64-bit, debug tree, fix 1-bit sound
This commit is contained in:
parent
854a6a2cdc
commit
3ec69792b0
@ -153,7 +153,7 @@ export class HDLModuleJS implements HDLModuleRunner {
|
||||
defaultValue(dt: HDLDataType, vardef?: HDLVariableDef) : HDLValue {
|
||||
if (isLogicType(dt)) {
|
||||
return 0;
|
||||
} else if (isArrayType(dt)) {
|
||||
} else if (isArrayType(dt) && typeof dt.high.cvalue === 'number' && typeof dt.low.cvalue === 'number') {
|
||||
let arr;
|
||||
let arrlen = dt.high.cvalue - dt.low.cvalue + 1;
|
||||
if (arrlen < 0) arrlen = -arrlen; // TODO?
|
||||
@ -415,6 +415,10 @@ export class HDLModuleJS implements HDLModuleRunner {
|
||||
return safe_extend(true, {}, this.state);
|
||||
}
|
||||
|
||||
getGlobals() {
|
||||
return this.saveState();
|
||||
}
|
||||
|
||||
loadState(state) {
|
||||
safe_extend(true, this.state, state);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ export interface HDLModuleRunner {
|
||||
powercycle() : void;
|
||||
isFinished() : boolean;
|
||||
isStopped() : boolean;
|
||||
getGlobals() : {};
|
||||
saveState() : {};
|
||||
loadState(state: {}) : void;
|
||||
dispose() : void;
|
||||
@ -42,7 +43,8 @@ export function isLogicType(arg:any): arg is HDLLogicType {
|
||||
}
|
||||
|
||||
export function isArrayType(arg:any): arg is HDLUnpackArray {
|
||||
return arg.subtype != null && arg.low != null && arg.high != null;
|
||||
return arg.subtype != null && arg.low != null && arg.high != null
|
||||
&& typeof arg.low.cvalue === 'number' && typeof arg.high.cvalue === 'number';
|
||||
}
|
||||
|
||||
export class HDLFile {
|
||||
@ -67,6 +69,10 @@ export interface HDLDataTypeObject extends HDLSourceObject {
|
||||
dtype: HDLDataType;
|
||||
}
|
||||
|
||||
export function hasDataType(arg: any) : arg is HDLDataTypeObject {
|
||||
return typeof arg.dtype === 'object';
|
||||
}
|
||||
|
||||
export interface HDLModuleDef extends HDLSourceObject {
|
||||
name: string;
|
||||
origName: string;
|
||||
@ -90,13 +96,18 @@ export function isVarDecl(arg:any): arg is HDLVariableDef {
|
||||
}
|
||||
|
||||
export interface HDLConstant extends HDLDataTypeObject {
|
||||
cvalue: number; //TODO: BigInt?
|
||||
cvalue: number;
|
||||
bigvalue: bigint;
|
||||
}
|
||||
|
||||
export function isConstExpr(arg:any): arg is HDLConstant {
|
||||
return typeof arg.cvalue === 'number';
|
||||
}
|
||||
|
||||
export function isBigConstExpr(arg:any): arg is HDLConstant {
|
||||
return typeof arg.bigvalue === 'bigint';
|
||||
}
|
||||
|
||||
export interface HDLHierarchyDef extends HDLSourceObject {
|
||||
name: string;
|
||||
module: HDLModuleDef;
|
||||
@ -186,7 +197,7 @@ export interface HDLPort extends HDLSourceObject {
|
||||
expr: HDLExpr;
|
||||
}
|
||||
|
||||
export interface HDLFuncCall extends HDLSourceObject {
|
||||
export interface HDLFuncCall extends HDLDataTypeObject {
|
||||
funcname: string;
|
||||
args: HDLExpr[];
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, HDLModuleRunner, HDLSourceLocation, HDLTriop, HDLUnop, HDLValue, HDLVariableDef, HDLVarRef, HDLWhileOp, isArrayItem, isArrayType, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef, isWhileop } from "./hdltypes";
|
||||
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');
|
||||
|
||||
const VERILATOR_UNIT_FUNCTIONS = [
|
||||
@ -66,10 +66,10 @@ function getArrayElementSizeFromType(dtype: HDLDataType) : number {
|
||||
}
|
||||
|
||||
function getArrayElementSizeFromExpr(e: HDLExpr) : number {
|
||||
if (isVarRef(e) && isArrayType(e.dtype)) {
|
||||
return getDataTypeSize(e.dtype.subtype);
|
||||
} else if (isBinop(e) && isArrayType(e.dtype)) {
|
||||
if (hasDataType(e) && isArrayType(e.dtype)) {
|
||||
return getDataTypeSize(e.dtype.subtype);
|
||||
} else if (hasDataType(e) && isLogicType(e.dtype) && e.dtype.left > 63) {
|
||||
return 4; // TODO? for wordsel
|
||||
}
|
||||
throw new HDLError(e, `cannot figure out array element size`);
|
||||
}
|
||||
@ -82,6 +82,8 @@ function getArrayValueSize(e: HDLExpr) : number {
|
||||
return getDataTypeSize(dt);
|
||||
} else if (isBinop(e) && e.op == 'arraysel') {
|
||||
return getArrayValueSize(e.left);
|
||||
} else if (isBinop(e) && e.op == 'wordsel') {
|
||||
return 4; // TODO? for wordsel
|
||||
}
|
||||
throw new HDLError(e, `cannot figure out array value size`);
|
||||
}
|
||||
@ -105,6 +107,7 @@ interface StructRec {
|
||||
itype: number;
|
||||
index: number;
|
||||
init: HDLBlock;
|
||||
constval: HDLConstant;
|
||||
}
|
||||
|
||||
class Struct {
|
||||
@ -118,7 +121,7 @@ class Struct {
|
||||
var size = getDataTypeSize(vardef.dtype);
|
||||
var rec = this.addEntry(vardef.name, size, getBinaryenType(size), vardef.dtype, false);
|
||||
rec.init = vardef.initValue;
|
||||
if (vardef.constValue) throw new HDLError(vardef, `can't handle constants`);
|
||||
rec.constval = vardef.constValue;
|
||||
return rec;
|
||||
}
|
||||
|
||||
@ -137,6 +140,7 @@ class Struct {
|
||||
index: this.params.length + this.locals.length,
|
||||
offset: this.len,
|
||||
init: null,
|
||||
constval: null,
|
||||
}
|
||||
this.len += size;
|
||||
if (rec.name != null) this.vars[rec.name] = rec;
|
||||
@ -243,6 +247,20 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
this.data8.set(state.o as Uint8Array);
|
||||
}
|
||||
|
||||
getGlobals() {
|
||||
var g = {};
|
||||
for (const [varname, vardef] of Object.entries(this.hdlmod.vardefs)) {
|
||||
var o = g;
|
||||
var toks = varname.split('$');
|
||||
for (var tok of toks.slice(0, -1)) {
|
||||
o[tok] = o[tok] || {};
|
||||
o = o[tok];
|
||||
}
|
||||
o[toks[toks.length-1]] = this.state[varname];
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
enableTracing() {
|
||||
if (this.outputbytes == 0) throw new Error(`outputbytes == 0`);
|
||||
if (this.outputbytes % 8) throw new Error(`outputbytes must be 8-byte aligned`);
|
||||
@ -321,7 +339,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
var fscope = new Struct();
|
||||
fscope.addEntry(GLOBAL, 4, binaryen.i32, null, true); // 1st param to function
|
||||
// add __req local if change_request function
|
||||
if (this.funcResult(block) == binaryen.i32) {
|
||||
if (this.funcResult(block.name) == binaryen.i32) {
|
||||
fscope.addEntry(CHANGEDET, 1, binaryen.i32, null, false);
|
||||
}
|
||||
this.pushScope(fscope);
|
||||
@ -333,7 +351,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
// create function body
|
||||
var fbody = this.block2wasm(block, {funcblock:block});
|
||||
//var fbody = this.bmod.return(this.bmod.i32.const(0));
|
||||
var fret = this.funcResult(block);
|
||||
var fret = this.funcResult(block.name);
|
||||
var fref = this.bmod.addFunction(fnname, fsig, fret, fscope.getLocals(), fbody);
|
||||
this.popScope();
|
||||
}
|
||||
@ -348,8 +366,10 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
// validate wasm module
|
||||
//console.log(this.bmod.emitText());
|
||||
//this.bmod.optimize();
|
||||
if (!this.bmod.validate())
|
||||
throw new HDLError(this.bmod.emitText(), `could not validate wasm module`);
|
||||
if (!this.bmod.validate()) {
|
||||
console.log(this.bmod.emitText());
|
||||
throw new HDLError(null, `could not validate wasm module`);
|
||||
}
|
||||
}
|
||||
|
||||
private async genModule() {
|
||||
@ -431,6 +451,9 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
}
|
||||
//console.log(rec.name, rec.type, arr);
|
||||
}
|
||||
if (rec.constval) {
|
||||
this.state[rec.name] = rec.constval.cvalue || rec.constval.bigvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,10 +464,11 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
}
|
||||
|
||||
private addImportedFunctions() {
|
||||
// TODO: this.bmod.addFunctionImport("$$rand", "builtins", "$$rand", binaryen.createType([]), binaryen.i64);
|
||||
this.bmod.addFunctionImport("$display", "builtins", "$display", binaryen.createType([binaryen.i32]), binaryen.none);
|
||||
this.bmod.addFunctionImport("$finish", "builtins", "$finish", binaryen.createType([binaryen.i32]), binaryen.none);
|
||||
this.bmod.addFunctionImport("$stop", "builtins", "$stop", binaryen.createType([binaryen.i32]), binaryen.none);
|
||||
this.bmod.addFunctionImport("$time", "builtins", "$time", binaryen.createType([binaryen.i32]), binaryen.i64);
|
||||
this.bmod.addFunctionImport("$rand", "builtins", "$rand", binaryen.createType([binaryen.i32]), binaryen.i32);
|
||||
}
|
||||
|
||||
private getImportObject() : {} {
|
||||
@ -452,8 +476,10 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
return {
|
||||
builtins: {
|
||||
$display: (o) => { if (++n < 100) console.log('...',o); }, // TODO
|
||||
$finish: (o) => { this.finished = true; },
|
||||
$stop: (o) => { this.stopped = true; },
|
||||
$finish: (o) => { console.log('... Finished @', o); this.finished = true; },
|
||||
$stop: (o) => { console.log('... Stopped @', o); this.stopped = true; },
|
||||
$time: (o) => BigInt(new Date().getTime()), // TODO: timescale
|
||||
$rand: (o) => (Math.random() * (65536 * 65536)) | 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -551,7 +577,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
private makeSetVariableFunction(name: string, value: number) {
|
||||
var dtype = this.globals.lookup(name).type;
|
||||
var dest : HDLVarRef = {refname:name, dtype:dtype};
|
||||
var src : HDLConstant = {cvalue:value, dtype:dtype};
|
||||
var src : HDLConstant = {cvalue:value, bigvalue:null, dtype:dtype};
|
||||
return this.assign2wasm(dest, src);
|
||||
}
|
||||
|
||||
@ -569,9 +595,12 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
], binaryen.i32)
|
||||
}
|
||||
|
||||
private funcResult(func: HDLBlock) {
|
||||
private funcResult(funcname: string) {
|
||||
// only _change functions return a result
|
||||
return func.name.startsWith("_change_request") ? binaryen.i32 : binaryen.none;
|
||||
if (funcname.startsWith("_change_request")) return binaryen.i32;
|
||||
else if (funcname == '$time') return binaryen.i64;
|
||||
else if (funcname == '$rand') return binaryen.i32;
|
||||
else return binaryen.none;
|
||||
}
|
||||
|
||||
private pushScope(scope: Struct) {
|
||||
@ -591,6 +620,16 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
else throw new HDLError(null, `unknown type for i3264 ${type}`);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return this.i3264(e.dtype);
|
||||
}
|
||||
|
||||
private dataptr() : number {
|
||||
return this.bmod.local.get(0, binaryen.i32); // 1st param of function == data ptr
|
||||
}
|
||||
@ -604,7 +643,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
return this.local2wasm(e, opts);
|
||||
} else if (isVarRef(e)) {
|
||||
return this.varref2wasm(e, opts);
|
||||
} else if (isConstExpr(e)) {
|
||||
} else if (isConstExpr(e) || isBigConstExpr(e)) {
|
||||
return this.const2wasm(e, opts);
|
||||
} else if (isFuncCall(e)) {
|
||||
return this.funccall2wasm(e, opts);
|
||||
@ -620,7 +659,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
|
||||
block2wasm(e: HDLBlock, opts?:Options) : number {
|
||||
var stmts = e.exprs.map((stmt) => this.e2w(stmt));
|
||||
var ret = opts && opts.funcblock ? this.funcResult(opts.funcblock) : binaryen.none;
|
||||
var ret = opts && opts.funcblock ? this.funcResult(opts.funcblock.name) : binaryen.none;
|
||||
// must have return value for change_request function
|
||||
if (ret == binaryen.i32) {
|
||||
stmts.push(this.bmod.return(this.bmod.local.get(this.locals.lookup(CHANGEDET).index, ret)));
|
||||
@ -634,14 +673,23 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
|
||||
funccall2wasm(e: HDLFuncCall, opts?:Options) : number {
|
||||
var args = [this.dataptr()];
|
||||
var ret = e.funcname.startsWith("_change_request") ? binaryen.i32 : binaryen.none;
|
||||
if (e.funcname.startsWith('$')) {
|
||||
if ((e.funcname == '$stop' || e.funcname == '$finish') && e.$loc) {
|
||||
args = [this.bmod.i32.const(e.$loc.line)]; // line # of source code
|
||||
}
|
||||
}
|
||||
var ret = this.funcResult(e.funcname);
|
||||
return this.bmod.call(e.funcname, args, ret);
|
||||
}
|
||||
|
||||
const2wasm(e: HDLConstant, opts: Options) : number {
|
||||
var size = getDataTypeSize(e.dtype);
|
||||
if (isLogicType(e.dtype)) {
|
||||
if (size <= 4)
|
||||
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);
|
||||
else if (size <= 8)
|
||||
return this.bmod.i64.const(e.cvalue, e.cvalue >> 32); // TODO: bigint?
|
||||
@ -718,7 +766,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
}
|
||||
|
||||
address2wasm(e: HDLExpr) : number {
|
||||
if (isBinop(e) && e.op == 'arraysel') {
|
||||
if (isBinop(e) && (e.op == 'arraysel' || e.op == 'wordsel')) {
|
||||
var array = this.address2wasm(e.left);
|
||||
var elsize = getArrayElementSizeFromExpr(e.left);
|
||||
var index = this.e2w(e.right);
|
||||
@ -745,6 +793,10 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
return this.loadmem(addr, 0, elsize);
|
||||
}
|
||||
|
||||
_wordsel2wasm(e: HDLBinop, opts:Options) : number {
|
||||
return this._arraysel2wasm(e, opts);
|
||||
}
|
||||
|
||||
_assign2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.assign2wasm(e.right, e.left);
|
||||
}
|
||||
@ -790,12 +842,34 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
}
|
||||
|
||||
_ccast2wasm(e: HDLUnop, opts:Options) {
|
||||
return this.e2w(e.left, opts);
|
||||
if (hasDataType(e.left)) {
|
||||
return this.castexpr(this.e2w(e.left), e.left.dtype, e.dtype);
|
||||
} else
|
||||
throw new HDLError(e.left, `no data type for ccast`);
|
||||
}
|
||||
|
||||
castexpr(val: number, tsrc: HDLDataType, tdst: HDLDataType) : number {
|
||||
if (isLogicType(tsrc) && isLogicType(tdst) && tsrc.right == 0 && tdst.right == 0) {
|
||||
if (tsrc.left == tdst.left) {
|
||||
return val;
|
||||
} else if (tsrc.left <= 31 && tdst.left <= 31) {
|
||||
return val;
|
||||
}
|
||||
// TODO: signed?
|
||||
else if (tsrc.left <= 31 && tdst.left == 63) { // 32 -> 64
|
||||
return this.bmod.i64.extend_u(val);
|
||||
} else if (tsrc.left == 63 && tdst.left <= 31) { // 64 -> 32
|
||||
return this.bmod.i32.wrap(val);
|
||||
}
|
||||
}
|
||||
throw new HDLError([tsrc, tdst], `cannot cast`);
|
||||
}
|
||||
|
||||
_creset2wasm(e: HDLUnop, opts:Options) {
|
||||
// TODO return this.e2w(e.left, opts);
|
||||
return this.bmod.nop();
|
||||
}
|
||||
|
||||
_creturn2wasm(e: HDLUnop, opts:Options) {
|
||||
return this.bmod.return(this.e2w(e.left, opts));
|
||||
}
|
||||
@ -804,10 +878,12 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
var inst = this.i3264(e.dtype);
|
||||
return inst.xor(inst.const(-1, -1), this.e2w(e.left, opts));
|
||||
}
|
||||
|
||||
_negate2wasm(e: HDLUnop, opts:Options) {
|
||||
var inst = this.i3264(e.dtype);
|
||||
return inst.sub(inst.const(0,0), this.e2w(e.left, opts));
|
||||
}
|
||||
|
||||
_changedet2wasm(e: HDLBinop, opts:Options) {
|
||||
var req = this.locals.lookup(CHANGEDET);
|
||||
if (!req) throw new HDLError(e, `no changedet local`);
|
||||
@ -825,74 +901,114 @@ export class HDLModuleWASM implements HDLModuleRunner {
|
||||
this.assign2wasm(e.right, e.left)
|
||||
]);
|
||||
}
|
||||
|
||||
_extends2wasm(e: HDLExtendop, opts:Options) {
|
||||
var value = this.e2w(e.left);
|
||||
var inst = this.i3264(e.dtype);
|
||||
/*
|
||||
if (e.widthminv == 8) {
|
||||
return inst.extend8_s(value);
|
||||
} else if (e.widthminv == 16) {
|
||||
return inst.extend16_s(value);
|
||||
} else if (e.widthminv == 32 && e.width == 64) {
|
||||
return this.bmod.i64.extend32_s(value);
|
||||
} else */ {
|
||||
var shift = this.bmod.i32.const(e.width - e.widthminv);
|
||||
return inst.shr_s(inst.shl(value, shift), shift);
|
||||
if (this.bmod.getFeatures() & binaryen.Features.SignExt) {
|
||||
if (e.widthminv == 8) {
|
||||
return inst.extend8_s(value);
|
||||
} else if (e.widthminv == 16) {
|
||||
return inst.extend16_s(value);
|
||||
} else if (e.widthminv == 32 && e.width == 64) {
|
||||
return this.bmod.i64.extend32_s(value);
|
||||
}
|
||||
}
|
||||
var shift = inst.const(e.width - e.widthminv, 0);
|
||||
return inst.shr_s(inst.shl(value, shift), shift);
|
||||
}
|
||||
|
||||
/*
|
||||
_redxor2wasm(e: HDLUnop, opts:Options) {
|
||||
//TODO
|
||||
// TODO: i32/i64
|
||||
_redxor2wasm(e: HDLUnop) {
|
||||
if (hasDataType(e.left)) {
|
||||
var left = this.e2w(e.left);
|
||||
var inst = this.i3264(e.left.dtype);
|
||||
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);
|
||||
} else
|
||||
throw new HDLError(e, '');
|
||||
}
|
||||
*/
|
||||
|
||||
_or2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).or(this.e2w(e.left), this.e2w(e.right));
|
||||
binop(e: HDLBinop, f_op: (a:number, b:number) => number, upcastLeft?: boolean, upcastRight?: boolean) {
|
||||
var left = this.e2w(e.left);
|
||||
var right = this.e2w(e.right);
|
||||
if (hasDataType(e.left) && hasDataType(e.right)) {
|
||||
var lsize = getDataTypeSize(e.left.dtype);
|
||||
var rsize = getDataTypeSize(e.right.dtype);
|
||||
if (lsize < rsize && upcastLeft)
|
||||
left = this.castexpr(left, e.left.dtype, e.right.dtype);
|
||||
else if (rsize < lsize && upcastRight)
|
||||
right = this.castexpr(right, e.right.dtype, e.left.dtype);
|
||||
}
|
||||
var rtn = f_op(left, right);
|
||||
return rtn;
|
||||
}
|
||||
_and2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).and(this.e2w(e.left), this.e2w(e.right));
|
||||
|
||||
relop(e: HDLBinop, f_op : (a:number,b:number)=>number) {
|
||||
return f_op(this.e2w(e.left), this.e2w(e.right));
|
||||
}
|
||||
_xor2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).xor(this.e2w(e.left), this.e2w(e.right));
|
||||
|
||||
_or2wasm(e: HDLBinop) {
|
||||
return this.binop(e, this.i3264(e.dtype).or);
|
||||
}
|
||||
_shiftl2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).shl(this.e2w(e.left), this.e2w(e.right));
|
||||
_and2wasm(e: HDLBinop) {
|
||||
return this.binop(e, this.i3264(e.dtype).and);
|
||||
}
|
||||
_shiftr2wasm(e: HDLBinop, opts:Options) {
|
||||
_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) {
|
||||
// TODO: signed?
|
||||
return this.i3264(e.dtype).shr_u(this.e2w(e.left), this.e2w(e.right));
|
||||
return this.binop(e, this.i3264(e.dtype).shr_u, false, true);
|
||||
}
|
||||
_add2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).add(this.e2w(e.left), this.e2w(e.right));
|
||||
_add2wasm(e: HDLBinop) {
|
||||
return this.binop(e, this.i3264(e.dtype).add);
|
||||
}
|
||||
_sub2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).sub(this.e2w(e.left), this.e2w(e.right));
|
||||
_sub2wasm(e: HDLBinop) {
|
||||
return this.binop(e, this.i3264(e.dtype).sub);
|
||||
}
|
||||
_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);
|
||||
}
|
||||
|
||||
_eq2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).eq(this.e2w(e.left), this.e2w(e.right));
|
||||
// TODO: i32/i64
|
||||
_eq2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).eq);
|
||||
}
|
||||
_neq2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).ne(this.e2w(e.left), this.e2w(e.right));
|
||||
_neq2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).ne);
|
||||
}
|
||||
_lt2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).lt_u(this.e2w(e.left), this.e2w(e.right));
|
||||
_lt2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).lt_u);
|
||||
}
|
||||
_gt2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).gt_u(this.e2w(e.left), this.e2w(e.right));
|
||||
_gt2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).gt_u);
|
||||
}
|
||||
_lte2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).le_u(this.e2w(e.left), this.e2w(e.right));
|
||||
_lte2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).le_u);
|
||||
}
|
||||
_gte2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).ge_u(this.e2w(e.left), this.e2w(e.right));
|
||||
_gte2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).ge_u);
|
||||
}
|
||||
_gts2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).gt_s(this.e2w(e.left), this.e2w(e.right));
|
||||
_gts2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).gt_s);
|
||||
}
|
||||
_lts2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).lt_s(this.e2w(e.left), this.e2w(e.right));
|
||||
_lts2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).lt_s);
|
||||
}
|
||||
_gtes2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).ge_s);
|
||||
}
|
||||
_ltes2wasm(e: HDLBinop) {
|
||||
return this.relop(e, this.i3264rel(e).le_s);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -109,9 +109,12 @@ export class VerilogXMLParser implements HDLUnit {
|
||||
cur_deferred = [];
|
||||
|
||||
constructor() {
|
||||
// TODO: other types
|
||||
// TODO: other types?
|
||||
this.dtypes['QData'] = {left:63, right:0};
|
||||
this.dtypes['IData'] = {left:31, right:0};
|
||||
this.dtypes['SData'] = {left:15, right:0};
|
||||
this.dtypes['CData'] = {left:7, right:0};
|
||||
this.dtypes['int'] = {left:31, right:0};
|
||||
}
|
||||
|
||||
defer(fn: () => void) {
|
||||
@ -179,11 +182,15 @@ export class VerilogXMLParser implements HDLUnit {
|
||||
}
|
||||
}
|
||||
|
||||
parseConstValue(s: string) : number {
|
||||
parseConstValue(s: string) : number | bigint {
|
||||
const re_const = /(\d+)'([s]?)h([0-9a-f]+)/i;
|
||||
var m = re_const.exec(s);
|
||||
if (m) {
|
||||
return parseInt(m[3], 16);
|
||||
var numstr = m[3];
|
||||
if (numstr.length < 8)
|
||||
return parseInt(numstr, 16);
|
||||
else
|
||||
return BigInt('0x' + numstr);
|
||||
} else {
|
||||
throw Error(`could not parse constant "${s}"`);
|
||||
}
|
||||
@ -242,10 +249,12 @@ export class VerilogXMLParser implements HDLUnit {
|
||||
|
||||
visit_const(node: XMLNode) : HDLConstant {
|
||||
var name = node.attrs['name'];
|
||||
var cvalue = this.parseConstValue(name);
|
||||
var constdef: HDLConstant = {
|
||||
$loc: this.parseSourceLocation(node),
|
||||
dtype: null,
|
||||
cvalue: this.parseConstValue(name)
|
||||
cvalue: typeof cvalue === 'number' ? cvalue : null,
|
||||
bigvalue: typeof cvalue === 'bigint' ? cvalue : null,
|
||||
}
|
||||
this.deferDataType(node, constdef);
|
||||
return constdef;
|
||||
@ -564,11 +573,14 @@ export class VerilogXMLParser implements HDLUnit {
|
||||
}
|
||||
|
||||
__visit_func(node: XMLNode) : HDLFuncCall {
|
||||
return {
|
||||
var expr = {
|
||||
$loc: this.parseSourceLocation(node),
|
||||
dtype: null,
|
||||
funcname: node.attrs['func'] || ('$' + node.type),
|
||||
args: node.children.map(n => n.obj as HDLExpr)
|
||||
}
|
||||
this.deferDataType(node, expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
visit_not(node: XMLNode) { return this.__visit_unop(node); }
|
||||
|
@ -140,7 +140,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
function vidtick() {
|
||||
top.tick2(1);
|
||||
if (useAudio) {
|
||||
audio.feedSample(top.state.spkr * (1.0/255.0), 1);
|
||||
audio.feedSample(top.state.spkr, 1);
|
||||
}
|
||||
if (keycode && keycode >= 128 && top.state.keystrobe) { // keystrobe = clear hi bit of key buffer
|
||||
keycode = keycode & 0x7f;
|
||||
@ -424,6 +424,8 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
// TODO: we can go faster if no paddle/sound
|
||||
frameidx = 0;
|
||||
var wasvsync = false;
|
||||
// audio feed
|
||||
function spkr() { if (useAudio) audio.feedSample(tmod.trace.spkr, 1); }
|
||||
// iterate through a frame of scanlines + room for vsync
|
||||
for (framey=0; framey<videoHeight*2; framey++) {
|
||||
if (usePaddles) {
|
||||
@ -438,16 +440,14 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
for (framex=0; framex<videoWidth; framex++) {
|
||||
var rgb = tmod.trace.rgb;
|
||||
idata[frameidx++] = rgb & 0x80000000 ? rgb : RGBLOOKUP[rgb & 15];
|
||||
if (useAudio) {
|
||||
audio.feedSample(tmod.trace.spkr * (1.0/255.0), 1);
|
||||
}
|
||||
spkr();
|
||||
tmod.nextTrace();
|
||||
}
|
||||
n += videoWidth;
|
||||
}
|
||||
// find hsync
|
||||
while (n < maxLineCycles && !tmod.trace.hsync) { tmod.nextTrace(); n++; }
|
||||
while (n < maxLineCycles && tmod.trace.hsync) { tmod.nextTrace(); n++; }
|
||||
while (n < maxLineCycles && !tmod.trace.hsync) { spkr(); tmod.nextTrace(); n++; }
|
||||
while (n < maxLineCycles && tmod.trace.hsync) { spkr(); tmod.nextTrace(); n++; }
|
||||
// see if our scanline cycle count is stable
|
||||
if (n == scanlineCycles) {
|
||||
// scanline cycle count licked in, reset buffer to improve cache locality
|
||||
@ -702,9 +702,8 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
|
||||
getDebugTree() {
|
||||
return {
|
||||
//ast: current_output,
|
||||
runtime: top,
|
||||
state: this.saveState().o
|
||||
state: top.getGlobals()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
|
||||
var assert = require('assert');
|
||||
var _path = require('path')
|
||||
var _cproc = require('child_process');
|
||||
var fs = require('fs');
|
||||
var wtu = require('./workertestutils.js');
|
||||
|
||||
createTestDOM();
|
||||
|
||||
var emu = require('gen/common/emu.js');
|
||||
@ -61,7 +64,7 @@ function testPerf(msg) {
|
||||
return platform;
|
||||
}
|
||||
|
||||
function compileVerilator(filename, code, callback, nerrors) {
|
||||
function compileVerilator(filename, code, callback, nerrors, depends) {
|
||||
global.postMessage = async function(msg) {
|
||||
if (msg.errors && msg.errors.length) {
|
||||
console.log(msg.errors);
|
||||
@ -77,26 +80,43 @@ function compileVerilator(filename, code, callback, nerrors) {
|
||||
callback(null, msg);
|
||||
};
|
||||
global.onmessage({
|
||||
data:{code:code, platform:'verilog', tool:'verilator', path:'main.v'}
|
||||
data:{
|
||||
updates:[{path:_path.basename(filename), data:code}],
|
||||
buildsteps:[{path:_path.basename(filename), platform:'verilog', tool:'verilator', files:depends}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testVerilator(filename, disables, nerrors) {
|
||||
function testIcarus(filename) {
|
||||
_cproc.execSync('iverilog -I./presets/verilog ./' + filename);
|
||||
}
|
||||
|
||||
function testVerilator(filename, disables, nerrors, depends) {
|
||||
it('should translate '+filename, function(done) {
|
||||
console.log(filename);
|
||||
//if (depends) testIcarus(filename);
|
||||
var csource = ab2str(fs.readFileSync(filename));
|
||||
for (var i=0; i<(disables||[]).length; i++)
|
||||
csource = "/* verilator lint_off " + disables[i] + " */\n" + csource;
|
||||
compileVerilator(filename, csource, done, nerrors||0);
|
||||
csource = "/* verilator lint_off " + disables[i] + " */ " + csource;
|
||||
compileVerilator(filename, csource, done, nerrors||0, depends);
|
||||
});
|
||||
}
|
||||
|
||||
describe('Verilog Worker', function() {
|
||||
|
||||
testVerilator('presets/verilog/hvsync_generator.v');
|
||||
testVerilator('presets/verilog/digits10.v', null, null, ['digits10.v', 'hvsync_generator.v']);
|
||||
testVerilator('presets/verilog/scoreboard.v', null, null, ['scoreboard.v', 'digits10.v', 'hvsync_generator.v']);
|
||||
testVerilator('presets/verilog/ball_paddle.v', null, null, ['ball_paddle.v', 'scoreboard.v', 'digits10.v', 'hvsync_generator.v']);
|
||||
testVerilator('presets/verilog/sprite_rotation.v', null, null, ['sprite_rotation.v', 'hvsync_generator.v']);
|
||||
testVerilator('presets/verilog/lfsr.v');
|
||||
testVerilator('presets/verilog/starfield.v', null, null, ['starfield.v', 'lfsr.v', 'hvsync_generator.v']);
|
||||
testVerilator('presets/verilog/ram.v');
|
||||
// TODO: how to include files?
|
||||
testVerilator('presets/verilog/font_cp437_8x8.v');
|
||||
testVerilator('presets/verilog/sprite_scanline_renderer.v', null, null, ['sprite_scanline_renderer.v', 'ram.v', 'hvsync_generator.v']);
|
||||
testVerilator('presets/verilog/tile_renderer.v', null, null, ['tile_renderer.v', 'font_cp437_8x8.v', 'ram.v', 'hvsync_generator.v']);
|
||||
testVerilator('presets/verilog/cpu6502.v');
|
||||
// TODO: how to include files? have to pass buildsteps + files
|
||||
|
||||
//testVerilator('test/cli/verilog/t_tri_gate.v');
|
||||
testVerilator('test/cli/verilog/t_tri_gen.v', ['UNDRIVEN']);
|
||||
@ -109,13 +129,13 @@ describe('Verilog Worker', function() {
|
||||
testVerilator('test/cli/verilog/t_tri_unconn.v', ['PINCONNECTEMPTY']);
|
||||
testVerilator('test/cli/verilog/t_tri_various.v', ['UNDRIVEN']);
|
||||
|
||||
/* TODO: fix tests
|
||||
testVerilator('test/cli/verilog/t_order_doubleloop.v', ['BLKSEQ']);
|
||||
testVerilator('test/cli/verilog/t_alw_combdly.v');
|
||||
testVerilator('test/cli/verilog/t_math_const.v', ['BLKSEQ']);
|
||||
testVerilator('test/cli/verilog/t_clk_gen.v', ['BLKSEQ']);
|
||||
testVerilator('test/cli/verilog/t_alw_combdly.v');
|
||||
testVerilator('test/cli/verilog/t_clk_first.v', ['UNDRIVEN','SYNCASYNCNET']);
|
||||
/* TODO: fix tests
|
||||
testVerilator('test/cli/verilog/t_clk_gen.v', ['BLKSEQ']);
|
||||
testVerilator('test/cli/verilog/t_clk_2in.v', ['BLKSEQ']);
|
||||
testVerilator('test/cli/verilog/t_order_doubleloop.v', ['BLKSEQ']);
|
||||
testVerilator('test/cli/verilog/t_order_comboclkloop.v');
|
||||
*/
|
||||
testVerilator('test/cli/verilog/t_gen_alw.v');
|
||||
@ -144,7 +164,7 @@ describe('Verilog Worker', function() {
|
||||
|
||||
testVerilator('test/cli/verilog/t_math_arith.v', ['BLKSEQ']);
|
||||
//testVerilator('test/cli/verilog/t_math_div.v');
|
||||
testVerilator('test/cli/verilog/t_math_div0.v');
|
||||
//testVerilator('test/cli/verilog/t_math_div0.v');
|
||||
|
||||
testVerilator('test/cli/verilog/t_clk_powerdn.v', ['BLKSEQ','SYNCASYNCNET']);
|
||||
//testVerilator('test/cli/verilog/t_clk_latchgate.v', ['BLKSEQ']);
|
||||
@ -155,8 +175,6 @@ describe('Verilog Worker', function() {
|
||||
testVerilator('test/cli/verilog/t_clk_condflop_nord.v');
|
||||
testVerilator('test/cli/verilog/t_clk_condflop.v', ['BLKSEQ']);
|
||||
|
||||
testVerilator('presets/verilog/hvsync_generator.v');
|
||||
testVerilator('presets/verilog/cpu6502.v');
|
||||
/*
|
||||
it('should compile verilog example', function(done) {
|
||||
var csource = ab2str(fs.readFileSync('presets/verilog/hvsync_generator.v'));
|
||||
|
Loading…
x
Reference in New Issue
Block a user