mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-25 18:33:11 +00:00
verilog: wasm, HDLModuleRunner interface
This commit is contained in:
parent
9bb79c318f
commit
4e97cd2eef
@ -1,6 +1,6 @@
|
||||
|
||||
import { byteArrayToString } from "../util";
|
||||
import { HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, HDLTriop, HDLUnop, HDLValue, HDLVariableDef, HDLVarRef, isArrayItem, isArrayType, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef, isWhileop } from "./hdltypes";
|
||||
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";
|
||||
|
||||
interface VerilatorUnit {
|
||||
_ctor_var_reset(state) : void;
|
||||
@ -10,7 +10,7 @@ interface VerilatorUnit {
|
||||
_change_request(state) : boolean;
|
||||
}
|
||||
|
||||
export class HDLModuleJS {
|
||||
export class HDLModuleJS implements HDLModuleRunner {
|
||||
|
||||
mod: HDLModuleDef;
|
||||
constpool: HDLModuleDef;
|
||||
@ -22,14 +22,12 @@ export class HDLModuleJS {
|
||||
stopped: boolean = false;
|
||||
settleTime: number = 0;
|
||||
curconsts: {};
|
||||
constused: number;
|
||||
specfuncs: VerilatorUnit[] = [];
|
||||
|
||||
constructor(mod: HDLModuleDef, constpool: HDLModuleDef) {
|
||||
this.mod = mod;
|
||||
this.constpool = constpool;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.basefuncs = {} as any;
|
||||
this.state = {}; //new Object(this.funcs) as any;
|
||||
// set built-in functions
|
||||
@ -39,10 +37,12 @@ export class HDLModuleJS {
|
||||
// generate functions
|
||||
this.basefuncs = this.genFuncs({});
|
||||
this.curfuncs = this.basefuncs;
|
||||
for (var i=0; i<2; i++) {
|
||||
for (var i=0; i<16; i++) {
|
||||
this.specfuncs[i] = this.genFuncs({
|
||||
reset:(i&1),
|
||||
//reset:(i&1),
|
||||
//clk:(i&2),
|
||||
//CPU16$state:i
|
||||
test_CPU16_top$cpu$state:i
|
||||
});
|
||||
}
|
||||
// set initial state
|
||||
@ -57,22 +57,30 @@ export class HDLModuleJS {
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
}
|
||||
|
||||
genFuncs(constants: {}) : VerilatorUnit {
|
||||
var funcs = Object.create(this.basefuncs);
|
||||
this.curconsts = constants;
|
||||
for (var block of this.mod.blocks) {
|
||||
this.locals = {};
|
||||
// if we have at least 1 constant value, check for it (set counter to zero)
|
||||
this.constused = (Object.keys(this.curconsts).length == 0) ? 99999 : 0;
|
||||
var s = this.block2js(block);
|
||||
try {
|
||||
var funcname = block.name||'__anon';
|
||||
var funcbody = `'use strict'; function ${funcname}(o) { ${s} }; return ${funcname};`;
|
||||
var func = new Function('', funcbody)();
|
||||
funcs[block.name] = func;
|
||||
//console.log(funcbody);
|
||||
} catch (e) {
|
||||
console.log(funcbody);
|
||||
throw e;
|
||||
if (this.constused) {
|
||||
try {
|
||||
var funcname = block.name||'__anon';
|
||||
var funcbody = `'use strict'; function ${funcname}(o) { ${s} }; return ${funcname};`;
|
||||
var func = new Function('', funcbody)();
|
||||
funcs[block.name] = func;
|
||||
//console.log(funcbody);
|
||||
} catch (e) {
|
||||
console.log(funcbody);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
//if (this.constused) console.log('FUNC',constants,funcname,this.constused);
|
||||
}
|
||||
return funcs;
|
||||
}
|
||||
@ -88,14 +96,14 @@ export class HDLModuleJS {
|
||||
return s;
|
||||
}
|
||||
|
||||
reset() {
|
||||
powercycle() {
|
||||
this.finished = false;
|
||||
this.stopped = false;
|
||||
this.basefuncs._ctor_var_reset(this.state);
|
||||
this.basefuncs._eval_initial(this.state);
|
||||
for (var i=0; i<100; i++) {
|
||||
this.basefuncs._eval_settle(this.state);
|
||||
this.eval();
|
||||
this.basefuncs._eval(this.state);
|
||||
var Vchange = this.basefuncs._change_request(this.state);
|
||||
if (!Vchange) {
|
||||
this.settleTime = i;
|
||||
@ -106,9 +114,14 @@ export class HDLModuleJS {
|
||||
}
|
||||
|
||||
eval() {
|
||||
//var clk = this.state.clk as number;
|
||||
var clk = this.state.clk as number;
|
||||
var reset = this.state.reset as number;
|
||||
this.curfuncs = this.specfuncs[reset];
|
||||
var state = this.state.test_CPU16_top$cpu$state as number;
|
||||
var opcode = this.state.CPU$opcode as number;
|
||||
var aluop = this.state.CPU$aluop as number;
|
||||
var fi = state;
|
||||
//this.curfuncs = this.specfuncs[fi & 0xff];
|
||||
this.curfuncs = this.basefuncs;
|
||||
for (var i=0; i<100; i++) {
|
||||
this.curfuncs._eval(this.state);
|
||||
var Vchange = this.curfuncs._change_request(this.state);
|
||||
@ -125,15 +138,13 @@ export class HDLModuleJS {
|
||||
throw new Error(`model did not converge on eval()`)
|
||||
}
|
||||
|
||||
tick2() {
|
||||
//var k1 = Object.keys(this.state).length;
|
||||
// TODO
|
||||
this.state.clk = 0;
|
||||
this.eval();
|
||||
this.state.clk = 1;
|
||||
this.eval();
|
||||
//var k2 = Object.keys(this.state).length;
|
||||
//if (k2 != k1) console.log(k1, k2);
|
||||
tick2(iters: number) {
|
||||
while (iters-- > 0) {
|
||||
this.state.clk = 0;
|
||||
this.eval();
|
||||
this.state.clk = 1;
|
||||
this.eval();
|
||||
}
|
||||
}
|
||||
|
||||
defaultValue(dt: HDLDataType, vardef?: HDLVariableDef) : HDLValue {
|
||||
@ -186,14 +197,15 @@ export class HDLModuleJS {
|
||||
return this.expr2js(block);
|
||||
}
|
||||
|
||||
expr2js(e: HDLExpr, options?:{cond?:boolean}) : string {
|
||||
expr2js(e: HDLExpr, options?:{store?:boolean,cond?:boolean}) : string {
|
||||
if (e == null) {
|
||||
return "/*null*/"; // TODO
|
||||
}
|
||||
if (isVarRef(e)) {
|
||||
if (this.curconsts[e.refname] != null)
|
||||
if (this.curconsts[e.refname] != null && !(options||{}).store) {
|
||||
this.constused++;
|
||||
return `${this.curconsts[e.refname]}`;
|
||||
else if (this.locals[e.refname])
|
||||
} else if (this.locals[e.refname])
|
||||
return `${e.refname}`;
|
||||
else
|
||||
return `o.${e.refname}`;
|
||||
@ -233,7 +245,7 @@ export class HDLModuleJS {
|
||||
case 'assignpre':
|
||||
case 'assigndly':
|
||||
case 'assignpost':
|
||||
return `${this.expr2js(e.right)} = ${this.expr2js(e.left)}`;
|
||||
return `${this.expr2js(e.right, {store:true})} = ${this.expr2js(e.left)}`;
|
||||
case 'arraysel':
|
||||
case 'wordsel':
|
||||
return `${this.expr2js(e.left)}[${this.expr2js(e.right)}]`;
|
||||
@ -303,11 +315,9 @@ export class HDLModuleJS {
|
||||
|
||||
expr2reset(e: HDLExpr) {
|
||||
if (isVarRef(e)) {
|
||||
// don't reset constant values
|
||||
if (this.curconsts[e.refname] != null)
|
||||
return `/* ${e.refname} */`;
|
||||
// TODO: random values?
|
||||
if (isLogicType(e.dtype)) {
|
||||
if (this.curconsts[e.refname] != null) {
|
||||
return `${e.refname}`;
|
||||
} else if (isLogicType(e.dtype)) {
|
||||
return `${this.expr2js(e)} = 0`;
|
||||
} else if (isArrayType(e.dtype)) {
|
||||
if (isLogicType(e.dtype.subtype)) {
|
||||
@ -380,6 +390,32 @@ export class HDLModuleJS {
|
||||
return null;
|
||||
}
|
||||
|
||||
isStopped() { return this.stopped; }
|
||||
isFinished() { return this.finished; }
|
||||
|
||||
tick() {
|
||||
(this.state as any).clk ^= 1;
|
||||
this.eval();
|
||||
}
|
||||
|
||||
get(varname: string) {
|
||||
return this.state[varname];
|
||||
}
|
||||
|
||||
set(varname: string, value) {
|
||||
if (varname in this.state) {
|
||||
this.state[varname] = value;
|
||||
}
|
||||
}
|
||||
|
||||
saveState() {
|
||||
return safe_extend(true, {}, this.state);
|
||||
}
|
||||
|
||||
loadState(state) {
|
||||
safe_extend(true, this.state, state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const OP2JS : {[key:string] : string} = {
|
||||
|
@ -1,4 +1,18 @@
|
||||
|
||||
export interface HDLModuleRunner {
|
||||
state: any; // live state or proxy object
|
||||
eval() : void;
|
||||
tick() : void;
|
||||
tick2(iters: number) : void;
|
||||
powercycle() : void;
|
||||
isFinished() : boolean;
|
||||
isStopped() : boolean;
|
||||
saveState() : {};
|
||||
loadState(state: {}) : void;
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
export interface HDLLogicType extends HDLSourceObject {
|
||||
left: number;
|
||||
right: number;
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLExpr, HDLExtendop, HDLFuncCall, HDLModuleDef, 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, HDLWhileOp, isArrayItem, isArrayType, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef, isWhileop } from "./hdltypes";
|
||||
import binaryen = require('binaryen');
|
||||
|
||||
interface VerilatorUnit {
|
||||
@ -21,6 +21,7 @@ const VERILATOR_UNIT_FUNCTIONS = [
|
||||
interface Options {
|
||||
store?: boolean;
|
||||
funcblock?: HDLBlock;
|
||||
resulttype?: number;
|
||||
}
|
||||
|
||||
const GLOBAL = "$$GLOBAL";
|
||||
@ -29,6 +30,16 @@ const MEMORY = "0";
|
||||
|
||||
///
|
||||
|
||||
export class HDLError extends Error {
|
||||
obj: any;
|
||||
constructor(obj: any, msg: string) {
|
||||
super(msg);
|
||||
Object.setPrototypeOf(this, HDLError.prototype);
|
||||
this.obj = obj;
|
||||
if (obj) console.log(obj);
|
||||
}
|
||||
}
|
||||
|
||||
function getDataTypeSize(dt: HDLDataType) : number {
|
||||
if (isLogicType(dt)) {
|
||||
if (dt.left <= 7)
|
||||
@ -42,14 +53,41 @@ function getDataTypeSize(dt: HDLDataType) : number {
|
||||
else
|
||||
return (dt.left >> 6) * 8 + 8; // 64-bit words
|
||||
} else if (isArrayType(dt)) {
|
||||
return (dt.high.cvalue - dt.low.cvalue + 1) * getDataTypeSize(dt.subtype);
|
||||
//return (asValue(dt.high) - asValue(dt.low) + 1) * dt.
|
||||
// TODO: additional padding for array?
|
||||
return (Math.abs(dt.high.cvalue - dt.low.cvalue) + 1) * getDataTypeSize(dt.subtype);
|
||||
} else {
|
||||
console.log(dt);
|
||||
throw Error(`don't know data type`);
|
||||
throw new HDLError(dt, `don't know data type`);
|
||||
}
|
||||
}
|
||||
|
||||
function getArrayElementSize(e: HDLExpr) : number {
|
||||
if (isVarRef(e) && isArrayType(e.dtype)) {
|
||||
return getDataTypeSize(e.dtype.subtype);
|
||||
} else if (isBinop(e) && isArrayType(e.dtype)) {
|
||||
return getDataTypeSize(e.dtype.subtype);
|
||||
}
|
||||
throw new HDLError(e, `cannot figure out array element size`);
|
||||
}
|
||||
|
||||
function getArrayValueSize(e: HDLExpr) : number {
|
||||
if (isVarRef(e)) {
|
||||
var dt = e.dtype;
|
||||
while (isArrayType(dt))
|
||||
dt = dt.subtype;
|
||||
return getDataTypeSize(dt);
|
||||
} else if (isBinop(e) && e.op == 'arraysel') {
|
||||
return getArrayValueSize(e.left);
|
||||
}
|
||||
throw new HDLError(e, `cannot figure out array value size`);
|
||||
}
|
||||
|
||||
function getAlignmentForSize(size) {
|
||||
if (size <= 1) return 1;
|
||||
else if (size <= 2) return 2;
|
||||
else if (size <= 4) return 4;
|
||||
else return 8;
|
||||
}
|
||||
|
||||
function getBinaryenType(size: number) {
|
||||
return size <= 4 || size > 8 ? binaryen.i32 : binaryen.i64
|
||||
}
|
||||
@ -61,6 +99,7 @@ interface StructRec {
|
||||
size: number;
|
||||
itype: number;
|
||||
index: number;
|
||||
init: HDLBlock;
|
||||
}
|
||||
|
||||
class Struct {
|
||||
@ -72,10 +111,15 @@ class Struct {
|
||||
|
||||
addVar(vardef: HDLVariableDef) {
|
||||
var size = getDataTypeSize(vardef.dtype);
|
||||
return this.addEntry(vardef.name, getBinaryenType(size), size, vardef.dtype, false);
|
||||
var rec = this.addEntry(vardef.name, getBinaryenType(size), size, vardef.dtype, false);
|
||||
rec.init = vardef.initValue;
|
||||
if (vardef.constValue) throw new HDLError(vardef, `can't handle constants`);
|
||||
return rec;
|
||||
}
|
||||
|
||||
addEntry(name: string, itype: number, size: number, hdltype: HDLDataType, isParam: boolean) : StructRec {
|
||||
var align = getAlignmentForSize(size);
|
||||
while (this.len % align) this.len++;
|
||||
// pointers are 32 bits, so if size > 8 it's a pointer
|
||||
var rec : StructRec = {
|
||||
name: name,
|
||||
@ -84,8 +128,9 @@ class Struct {
|
||||
itype: itype,
|
||||
index: this.params.length + this.locals.length,
|
||||
offset: this.len,
|
||||
init: null,
|
||||
}
|
||||
this.len += 8; //TODO: rec.size, alignment
|
||||
this.len += size;
|
||||
if (rec.name != null) this.vars[rec.name] = rec;
|
||||
if (isParam) this.params.push(rec);
|
||||
else this.locals.push(rec);
|
||||
@ -105,117 +150,228 @@ class Struct {
|
||||
}
|
||||
}
|
||||
|
||||
export class HDLModuleWASM {
|
||||
///
|
||||
|
||||
export class HDLModuleWASM implements HDLModuleRunner {
|
||||
|
||||
bmod: binaryen.Module;
|
||||
instance: WebAssembly.Instance;
|
||||
|
||||
hdlmod: HDLModuleDef;
|
||||
constpool: HDLModuleDef;
|
||||
globals: Struct;
|
||||
locals: Struct;
|
||||
finished: boolean;
|
||||
stopped: boolean;
|
||||
databuf: Buffer;
|
||||
data8: Uint8Array;
|
||||
state: any;
|
||||
statebytes: number;
|
||||
outputbytes: number;
|
||||
|
||||
constructor(moddef: HDLModuleDef, constpool: HDLModuleDef) {
|
||||
this.hdlmod = moddef;
|
||||
this.constpool = constpool;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bmod = new binaryen.Module();
|
||||
this.genTypes();
|
||||
this.bmod.setMemory(this.globals.len, this.globals.len, MEMORY); // TODO?
|
||||
var memsize = Math.ceil(this.globals.len / 65536) + 1;
|
||||
this.bmod.setMemory(memsize, memsize, MEMORY); // memory is in 64k chunks
|
||||
this.genFuncs();
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.genModule();
|
||||
this.genInitData();
|
||||
}
|
||||
|
||||
genTypes() {
|
||||
// generate global variables
|
||||
var state = new Struct();
|
||||
// outputs are first
|
||||
for (const [varname, vardef] of Object.entries(this.hdlmod.vardefs)) {
|
||||
if (vardef.isOutput) state.addVar(vardef);
|
||||
}
|
||||
this.outputbytes = state.len;
|
||||
// followed by inputs and internal vars
|
||||
for (const [varname, vardef] of Object.entries(this.hdlmod.vardefs)) {
|
||||
if (!vardef.isOutput) state.addVar(vardef);
|
||||
}
|
||||
this.statebytes = state.len;
|
||||
// followed by constant pool
|
||||
for (const [varname, vardef] of Object.entries(this.constpool.vardefs)) {
|
||||
state.addVar(vardef);
|
||||
}
|
||||
this.globals = state;
|
||||
}
|
||||
|
||||
genFuncs() {
|
||||
// function type (dsegptr)
|
||||
var fsig = binaryen.createType([binaryen.i32])
|
||||
for (var block of this.hdlmod.blocks) {
|
||||
// TODO: cfuncs only
|
||||
var fnname = block.name;
|
||||
// find locals of function
|
||||
var fscope = new Struct();
|
||||
fscope.addEntry(GLOBAL, binaryen.i32, 4, null, true); // 1st param to function
|
||||
// add __req local if change_request function
|
||||
if (this.funcResult(block) == binaryen.i32) {
|
||||
fscope.addEntry(CHANGEDET, binaryen.i32, 1, null, false);
|
||||
}
|
||||
this.pushScope(fscope);
|
||||
block.exprs.forEach((e) => {
|
||||
if (e && isVarDecl(e)) {
|
||||
fscope.addVar(e);
|
||||
}
|
||||
})
|
||||
// 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 fref = this.bmod.addFunction(fnname, fsig, fret, fscope.getLocals(), fbody);
|
||||
this.popScope();
|
||||
}
|
||||
// export functions
|
||||
for (var fname of VERILATOR_UNIT_FUNCTIONS) {
|
||||
this.bmod.addFunctionExport(fname, fname);
|
||||
}
|
||||
// create helper functions
|
||||
this.addHelperFunctions();
|
||||
// create wasm module
|
||||
console.log(this.bmod.emitText());
|
||||
//this.bmod.optimize();
|
||||
if (!this.bmod.validate())
|
||||
throw Error(`could not validate wasm module`);
|
||||
// function type (dsegptr)
|
||||
var fsig = binaryen.createType([binaryen.i32])
|
||||
for (var block of this.hdlmod.blocks) {
|
||||
// TODO: cfuncs only
|
||||
var fnname = block.name;
|
||||
// find locals of function
|
||||
var fscope = new Struct();
|
||||
fscope.addEntry(GLOBAL, binaryen.i32, 4, null, true); // 1st param to function
|
||||
// add __req local if change_request function
|
||||
if (this.funcResult(block) == binaryen.i32) {
|
||||
fscope.addEntry(CHANGEDET, binaryen.i32, 1, null, false);
|
||||
}
|
||||
this.pushScope(fscope);
|
||||
block.exprs.forEach((e) => {
|
||||
if (e && isVarDecl(e)) {
|
||||
fscope.addVar(e);
|
||||
}
|
||||
})
|
||||
// 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 fref = this.bmod.addFunction(fnname, fsig, fret, fscope.getLocals(), fbody);
|
||||
this.popScope();
|
||||
}
|
||||
// export functions
|
||||
for (var fname of VERILATOR_UNIT_FUNCTIONS) {
|
||||
this.bmod.addFunctionExport(fname, fname);
|
||||
}
|
||||
// create helper functions
|
||||
this.addHelperFunctions();
|
||||
// create 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`);
|
||||
}
|
||||
|
||||
async genModule() {
|
||||
//console.log(this.bmod.emitText());
|
||||
this.test();
|
||||
var wasmData = this.bmod.emitBinary();
|
||||
var compiled = await WebAssembly.compile(wasmData);
|
||||
this.instance = await WebAssembly.instantiate(compiled, {});
|
||||
this.databuf = (this.instance.exports[MEMORY] as any).buffer;
|
||||
this.data8 = new Uint8Array(this.databuf);
|
||||
this.state = new Proxy({}, {
|
||||
// TODO
|
||||
get: (target, prop, receiver) => {
|
||||
var vref = this.globals.lookup(prop.toString());
|
||||
if (vref !== undefined) {
|
||||
if (isLogicType(vref.type)) {
|
||||
return this.data8[vref.offset]; // TODO: other types
|
||||
} else if (isArrayType(vref.type)) {
|
||||
return new Uint8Array(this.databuf, vref.offset, vref.size); // TODO: other types
|
||||
} else
|
||||
throw new HDLError(vref, `can't get property ${prop.toString()}`);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
set: (obj, prop, value) => {
|
||||
var vref = this.globals.lookup(prop.toString());
|
||||
if (vref !== undefined) {
|
||||
if (isLogicType(vref.type)) {
|
||||
this.data8[vref.offset] = value; // TODO: other types
|
||||
return true;
|
||||
} else if (isArrayType(vref.type)) {
|
||||
throw new HDLError(vref, `can't set property ${prop.toString()}`);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true; // silently fail
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
genInitData() {
|
||||
for (var rec of this.globals.locals) {
|
||||
if (rec.init) {
|
||||
var arr = this.state[rec.name];
|
||||
if (!arr) throw new HDLError(rec, `no array to init`);
|
||||
for (let i=0; i<rec.init.exprs.length; i++) {
|
||||
let e = rec.init.exprs[i];
|
||||
if (isArrayItem(e) && isConstExpr(e.expr)) {
|
||||
arr[e.index] = e.expr.cvalue;
|
||||
} else {
|
||||
throw new HDLError(e, `non-const expr in initarray`);
|
||||
}
|
||||
}
|
||||
//console.log(rec.name, rec.type, arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
powercycle() {
|
||||
this.finished = false;
|
||||
this.stopped = false;
|
||||
(this.instance.exports as any)._ctor_var_reset(0);
|
||||
(this.instance.exports as any)._eval_initial(0);
|
||||
for (var i=0; i<100; i++) {
|
||||
(this.instance.exports as any)._eval_settle(0);
|
||||
(this.instance.exports as any)._eval(0);
|
||||
var Vchange = (this.instance.exports as any)._change_request(0);
|
||||
if (!Vchange) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new HDLError(null, `model did not converge on reset()`)
|
||||
}
|
||||
|
||||
eval() {
|
||||
(this.instance.exports as any).eval(0);
|
||||
}
|
||||
|
||||
tick() {
|
||||
// TODO: faster
|
||||
this.state.clk ^= 1;
|
||||
(this.instance.exports as any).eval(0);
|
||||
}
|
||||
|
||||
tick2(iters: number) {
|
||||
(this.instance.exports as any).tick2(0, iters);
|
||||
}
|
||||
|
||||
isFinished() { return this.finished; }
|
||||
|
||||
isStopped() { return this.stopped; }
|
||||
|
||||
saveState() {
|
||||
return { o: this.data8.slice(0, this.statebytes) };
|
||||
}
|
||||
|
||||
loadState(state) {
|
||||
this.data8.set(state.o as Uint8Array);
|
||||
}
|
||||
|
||||
test() {
|
||||
var wasmData = this.bmod.emitBinary();
|
||||
var compiled = new WebAssembly.Module(wasmData);
|
||||
var instance = new WebAssembly.Instance(compiled, {});
|
||||
var mem = (instance.exports[MEMORY] as any).buffer;
|
||||
(instance.exports as any)._ctor_var_reset(0);
|
||||
(instance.exports as any)._eval_initial(0);
|
||||
(instance.exports as any)._eval_settle(0);
|
||||
var data = new Uint8Array(mem);
|
||||
var o_clk = this.globals.lookup('clk').offset;
|
||||
var o_reset = this.globals.lookup('reset').offset
|
||||
data[o_reset] = 1;
|
||||
//new Uint8Array(mem)[this.globals.lookup('reset').offset] = 0;
|
||||
//new Uint8Array(mem)[this.globals.lookup('enable').offset] = 1;
|
||||
for (var i=0; i<20; i++) {
|
||||
data[o_clk] = 0;
|
||||
(instance.exports as any).tick(0);
|
||||
data[o_clk] = 1;
|
||||
(instance.exports as any).tick(0);
|
||||
if (i==5) new Uint8Array(mem)[this.globals.lookup('reset').offset] = 0;
|
||||
//console.log(this.globals);
|
||||
this.state.reset = 1;
|
||||
for (var i=0; i<50; i++) {
|
||||
this.tick();
|
||||
this.tick();
|
||||
if (i==5) this.state.reset = 0;
|
||||
}
|
||||
console.log(mem);
|
||||
console.log(this.databuf);
|
||||
var t1 = new Date().getTime();
|
||||
var tickiters = 10000;
|
||||
var looplen = Math.round(100000000/tickiters);
|
||||
for (var i=0; i<looplen; i++) {
|
||||
(instance.exports as any).tick2(0,tickiters);
|
||||
this.tick2(tickiters);
|
||||
}
|
||||
var t2 = new Date().getTime();
|
||||
console.log('wasm:',t2-t1,'msec',i*tickiters,'iterations');
|
||||
console.log(mem);
|
||||
console.log(this.databuf);
|
||||
}
|
||||
|
||||
addHelperFunctions() {
|
||||
this.bmod.addFunction("tick",
|
||||
this.bmod.addFunction("eval",
|
||||
binaryen.createType([binaryen.i32]), // (dataptr)
|
||||
binaryen.i32, // return # of iterations
|
||||
[], // no locals
|
||||
this.makeTickFunction(0)
|
||||
);
|
||||
this.bmod.addFunctionExport("tick", "tick");
|
||||
this.bmod.addFunctionExport("eval", "eval");
|
||||
|
||||
// TODO: what if there is no clk? (e.g. ALU)
|
||||
var l_dseg = this.bmod.local.get(0, binaryen.i32);
|
||||
var l_count = this.bmod.local.get(1, binaryen.i32);
|
||||
this.bmod.addFunction("tick2",
|
||||
@ -224,9 +380,9 @@ export class HDLModuleWASM {
|
||||
[], // no locals
|
||||
this.bmod.loop("@loop", this.bmod.block(null, [
|
||||
this.makeSetVariableFunction("clk", 0),
|
||||
this.bmod.drop(this.bmod.call("tick", [l_dseg], binaryen.i32)),
|
||||
this.bmod.drop(this.bmod.call("eval", [l_dseg], binaryen.i32)),
|
||||
this.makeSetVariableFunction("clk", 1),
|
||||
this.bmod.drop(this.bmod.call("tick", [l_dseg], binaryen.i32)),
|
||||
this.bmod.drop(this.bmod.call("eval", [l_dseg], binaryen.i32)),
|
||||
// dec $1
|
||||
this.bmod.local.set(1, this.bmod.i32.sub(l_count, this.bmod.i32.const(1))),
|
||||
// goto @loop if $1
|
||||
@ -236,6 +392,12 @@ export class HDLModuleWASM {
|
||||
this.bmod.addFunctionExport("tick2", "tick2");
|
||||
}
|
||||
|
||||
makeGetVariableFunction(name: string, value: number) {
|
||||
var dtype = this.globals.lookup(name).type;
|
||||
var src : HDLVarRef = {refname:name, dtype:dtype};
|
||||
return this.e2w(src);
|
||||
}
|
||||
|
||||
makeSetVariableFunction(name: string, value: number) {
|
||||
var dtype = this.globals.lookup(name).type;
|
||||
var dest : HDLVarRef = {refname:name, dtype:dtype};
|
||||
@ -276,7 +438,7 @@ export class HDLModuleWASM {
|
||||
var type = getBinaryenType(size);
|
||||
if (type == binaryen.i32) return this.bmod.i32;
|
||||
else if (type == binaryen.i64) return this.bmod.i64;
|
||||
else throw Error();
|
||||
else throw new HDLError(null, `unknown type for i3264 ${type}`);
|
||||
}
|
||||
|
||||
dataptr() : number {
|
||||
@ -299,20 +461,24 @@ export class HDLModuleWASM {
|
||||
} else if (isUnop(e) || isBinop(e) || isTriop(e) || isWhileop(e)) {
|
||||
var n = `_${e.op}2wasm`;
|
||||
var fn = this[n];
|
||||
if (fn == null) { console.log(e); throw Error(`no such method ${n}`) }
|
||||
if (fn == null) { throw new HDLError(e, `no such method ${n}`) }
|
||||
return this[n](e, opts);
|
||||
} else {
|
||||
console.log('expr', e);
|
||||
throw Error(`could not translate expr`)
|
||||
throw new HDLError(e, `could not translate expr`)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (ret == binaryen.i32) { // must have return value
|
||||
// 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)));
|
||||
}
|
||||
// return block value for loop condition
|
||||
if (opts && opts.resulttype) {
|
||||
ret = binaryen.i32;
|
||||
}
|
||||
return this.bmod.block(e.name, stmts, ret);
|
||||
}
|
||||
|
||||
@ -328,10 +494,9 @@ export class HDLModuleWASM {
|
||||
if (size <= 4)
|
||||
return this.bmod.i32.const(e.cvalue);
|
||||
else
|
||||
throw new Error(`constants > 32 bits not supported`)
|
||||
throw new HDLError(e, `constants > 32 bits not supported`)
|
||||
} else {
|
||||
console.log(e);
|
||||
throw new Error(`non-logic constants not supported`)
|
||||
throw new HDLError(e, `non-logic constants not supported`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,17 +507,9 @@ export class HDLModuleWASM {
|
||||
if (local != null) {
|
||||
return this.bmod.local.get(local.index, local.itype);
|
||||
} else if (global != null) {
|
||||
if (global.size == 1) {
|
||||
return this.bmod.i32.load8_u(global.offset, 1, this.dataptr());
|
||||
} else if (global.size == 2) {
|
||||
return this.bmod.i32.load16_u(global.offset, 2, this.dataptr());
|
||||
} else if (global.size == 4) {
|
||||
return this.bmod.i32.load(global.offset, 4, this.dataptr());
|
||||
} else if (global.size == 8) {
|
||||
return this.bmod.i64.load(global.offset, 8, this.dataptr());
|
||||
}
|
||||
return this.loadmem(this.dataptr(), global.offset, global.size);
|
||||
}
|
||||
throw new Error(`cannot lookup variable ${e.refname}`)
|
||||
throw new HDLError(e, `cannot lookup variable ${e.refname}`)
|
||||
}
|
||||
|
||||
local2wasm(e: HDLVariableDef, opts:Options) : number {
|
||||
@ -361,7 +518,7 @@ export class HDLModuleWASM {
|
||||
return this.bmod.nop(); // TODO
|
||||
}
|
||||
|
||||
assign2wasm(dest: HDLExpr, src: HDLExpr) {
|
||||
assign2wasm(dest: HDLExpr, src: HDLExpr) : number {
|
||||
var value = this.e2w(src);
|
||||
if (isVarRef(dest)) {
|
||||
var local = this.locals && this.locals.lookup(dest.refname);
|
||||
@ -369,19 +526,70 @@ export class HDLModuleWASM {
|
||||
if (local != null) {
|
||||
return this.bmod.local.set(local.index, value);
|
||||
} else if (global != null) {
|
||||
if (global.size == 1) {
|
||||
return this.bmod.i32.store8(global.offset, 1, this.dataptr(), value);
|
||||
} else if (global.size == 2) {
|
||||
return this.bmod.i32.store16(global.offset, 2, this.dataptr(), value);
|
||||
} else if (global.size == 4) {
|
||||
return this.bmod.i32.store(global.offset, 4, this.dataptr(), value);
|
||||
} else if (global.size == 8) {
|
||||
return this.bmod.i64.store(global.offset, 8, this.dataptr(), value);
|
||||
}
|
||||
return this.storemem(this.dataptr(), global.offset, global.size, value);
|
||||
}
|
||||
} else if (isBinop(dest)) {
|
||||
// TODO: array bounds
|
||||
var addr = this.address2wasm(dest);
|
||||
return this.storemem(addr, 0, getDataTypeSize(dest.dtype), value);
|
||||
}
|
||||
throw new HDLError([dest, src], `cannot complete assignment`);
|
||||
}
|
||||
|
||||
loadmem(ptr, offset:number, size:number) : number {
|
||||
if (size == 1) {
|
||||
return this.bmod.i32.load8_u(offset, 1, ptr);
|
||||
} else if (size == 2) {
|
||||
return this.bmod.i32.load16_u(offset, 2, ptr);
|
||||
} else if (size == 4) {
|
||||
return this.bmod.i32.load(offset, 4, ptr);
|
||||
} else if (size == 8) {
|
||||
return this.bmod.i64.load(offset, 8, ptr);
|
||||
} else {
|
||||
throw new HDLError(null, `bad size ${size}`)
|
||||
}
|
||||
}
|
||||
|
||||
storemem(ptr, offset:number, size:number, value) : number {
|
||||
if (size == 1) {
|
||||
return this.bmod.i32.store8(offset, 1, ptr, value);
|
||||
} else if (size == 2) {
|
||||
return this.bmod.i32.store16(offset, 2, ptr, value);
|
||||
} else if (size == 4) {
|
||||
return this.bmod.i32.store(offset, 4, ptr, value);
|
||||
} else if (size == 8) {
|
||||
return this.bmod.i64.store(offset, 8, ptr, value);
|
||||
} else {
|
||||
throw new HDLError(null, `bad size ${size}`)
|
||||
}
|
||||
}
|
||||
|
||||
address2wasm(e: HDLExpr) : number {
|
||||
if (isBinop(e) && e.op == 'arraysel') {
|
||||
var array = this.address2wasm(e.left);
|
||||
var elsize = getArrayElementSize(e.left);
|
||||
var index = this.e2w(e.right);
|
||||
return this.bmod.i32.add(
|
||||
array,
|
||||
this.bmod.i32.mul(this.bmod.i32.const(elsize), index)
|
||||
);
|
||||
} else if (isVarRef(e)) {
|
||||
var local = this.locals && this.locals.lookup(e.refname);
|
||||
var global = this.globals.lookup(e.refname);
|
||||
if (local != null) {
|
||||
throw new HDLError(e, `can't get array local address yet`);
|
||||
} else if (global != null) {
|
||||
return this.bmod.i32.const(global.offset);
|
||||
}
|
||||
}
|
||||
console.log(dest, src);
|
||||
throw new Error(`cannot complete assignment`);
|
||||
throw new HDLError(e, `cannot get address`);
|
||||
}
|
||||
|
||||
// TODO: array bounds
|
||||
_arraysel2wasm(e: HDLBinop, opts:Options) : number {
|
||||
var addr = this.address2wasm(e);
|
||||
var elsize = getArrayValueSize(e);
|
||||
return this.loadmem(addr, 0, elsize);
|
||||
}
|
||||
|
||||
_assign2wasm(e: HDLBinop, opts:Options) {
|
||||
@ -406,6 +614,27 @@ export class HDLModuleWASM {
|
||||
_cond2wasm(e: HDLTriop, opts:Options) {
|
||||
return this.bmod.select(this.e2w(e.cond), this.e2w(e.left), this.e2w(e.right));
|
||||
}
|
||||
_condbound2wasm(e: HDLTriop, opts:Options) {
|
||||
return this.bmod.select(this.e2w(e.cond), this.e2w(e.left), this.e2w(e.right));
|
||||
}
|
||||
|
||||
_while2wasm(e: HDLWhileOp, opts:Options) {
|
||||
var block = [];
|
||||
if (e.precond) {
|
||||
block.push(this.e2w(e.precond));
|
||||
}
|
||||
if (e.loopcond) {
|
||||
block.push(this.bmod.br_if("@block", this.e2w(e.loopcond, {resulttype:binaryen.i32})));
|
||||
}
|
||||
if (e.body) {
|
||||
block.push(this.e2w(e.body));
|
||||
}
|
||||
if (e.inc) {
|
||||
block.push(this.e2w(e.inc));
|
||||
}
|
||||
block.push(this.bmod.br("@loop"));
|
||||
return this.bmod.loop("@loop", this.bmod.block("@block", block, binaryen.none));
|
||||
}
|
||||
|
||||
_ccast2wasm(e: HDLUnop, opts:Options) {
|
||||
return this.e2w(e.left, opts);
|
||||
@ -422,9 +651,13 @@ export class HDLModuleWASM {
|
||||
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 Error(`no changedet local`);
|
||||
if (!req) throw new HDLError(e, `no changedet local`);
|
||||
var left = this.e2w(e.left);
|
||||
var right = this.e2w(e.right);
|
||||
return this.bmod.block(null, [
|
||||
@ -439,6 +672,21 @@ export class HDLModuleWASM {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
_or2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).or(this.e2w(e.left), this.e2w(e.right));
|
||||
@ -481,5 +729,11 @@ export class HDLModuleWASM {
|
||||
_gte2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).ge_u(this.e2w(e.left), this.e2w(e.right));
|
||||
}
|
||||
_gts2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).gt_s(this.e2w(e.left), this.e2w(e.right));
|
||||
}
|
||||
_lts2wasm(e: HDLBinop, opts:Options) {
|
||||
return this.i3264(e.dtype).lt_s(this.e2w(e.left), this.e2w(e.right));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,26 +14,33 @@ try {
|
||||
throw e;
|
||||
}
|
||||
console.log(parser);
|
||||
var modname = process.argv[3];
|
||||
if (1 && modname) {
|
||||
var modname = 'TOP'; //process.argv[3];
|
||||
|
||||
async function testWASM() {
|
||||
var bmod = new HDLModuleWASM(parser.modules[modname], parser.modules['@CONST-POOL@']);
|
||||
bmod.init();
|
||||
await bmod.init();
|
||||
bmod.powercycle();
|
||||
bmod.test();
|
||||
}
|
||||
if (1 && modname) {
|
||||
|
||||
async function testJS() {
|
||||
var mod = new HDLModuleJS(parser.modules[modname], parser.modules['@CONST-POOL@']);
|
||||
mod.init();
|
||||
console.log(mod.getJSCode());
|
||||
mod.reset();
|
||||
mod.powercycle();
|
||||
var t1 = new Date().getTime();
|
||||
for (var i=0; i<100000000; i++) {
|
||||
mod.tick2();
|
||||
mod.tick2(1);
|
||||
}
|
||||
mod.state.reset = 1;
|
||||
for (var j=0; j<10000000; j++) {
|
||||
mod.tick2();
|
||||
mod.tick2(1);
|
||||
}
|
||||
var t2 = new Date().getTime();
|
||||
console.log(mod.state);
|
||||
console.log('js:',t2-t1, 'msec', i, 'iterations', i/1000/(t2-t1), 'MHz')
|
||||
//console.log(emitter);
|
||||
}
|
||||
|
||||
testWASM().then(testJS);
|
||||
|
||||
|
@ -5,8 +5,9 @@ import { SampleAudio } from "../common/audio";
|
||||
import { safe_extend } from "../common/util";
|
||||
import { WaveformView, WaveformProvider, WaveformMeta } from "../ide/waveform";
|
||||
import { setFrameRateUI, loadScript } from "../ide/ui";
|
||||
import { HDLUnit, isLogicType } from "../common/hdl/hdltypes";
|
||||
import { HDLModuleRunner, HDLUnit, isLogicType } from "../common/hdl/hdltypes";
|
||||
import { HDLModuleJS } from "../common/hdl/hdlruntime";
|
||||
import { HDLModuleWASM } from "../common/hdl/hdlwasm";
|
||||
|
||||
declare var Split;
|
||||
|
||||
@ -84,8 +85,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
var videoHeight = 256;
|
||||
var maxVideoLines = 262+40; // vertical hold
|
||||
var idata, timer, timerCallback;
|
||||
var top : HDLModuleJS;
|
||||
var gen;
|
||||
var top : HDLModuleRunner;
|
||||
var cyclesPerFrame = (256+23+7+23)*262; // 4857480/60 Hz
|
||||
|
||||
// control inputs
|
||||
@ -133,23 +133,25 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
var frameRate = 0;
|
||||
|
||||
function vidtick() {
|
||||
top.tick2();
|
||||
if (useAudio)
|
||||
audio.feedSample(gen.spkr*(1.0/255.0), 1);
|
||||
if (keycode && keycode >= 128 && gen.keystrobe) // keystrobe = clear hi bit of key buffer
|
||||
keycode = gen.keycode = keycode & 0x7f;
|
||||
if (debugCond && debugCond())
|
||||
top.tick2(1);
|
||||
if (useAudio) {
|
||||
audio.feedSample(top.state.spkr * (1.0/255.0), 1);
|
||||
}
|
||||
if (keycode && keycode >= 128 && top.state.keystrobe) { // keystrobe = clear hi bit of key buffer
|
||||
keycode = keycode & 0x7f;
|
||||
top.state.keycode = keycode;
|
||||
}
|
||||
if (debugCond && debugCond()) {
|
||||
debugCond = null;
|
||||
}
|
||||
}
|
||||
|
||||
function doreset() {
|
||||
gen.reset = 1;
|
||||
top.state.reset = 1;
|
||||
}
|
||||
|
||||
function unreset() {
|
||||
if (gen.reset !== undefined) {
|
||||
gen.reset = 0;
|
||||
}
|
||||
top.state.reset = 0;
|
||||
}
|
||||
|
||||
// inner Platform class
|
||||
@ -174,6 +176,8 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
async start() {
|
||||
await loadScript('./gen/common/hdl/hdltypes.js');
|
||||
await loadScript('./gen/common/hdl/hdlruntime.js');
|
||||
await loadScript('./gen/common/hdl/hdlwasm.js');
|
||||
await loadScript('./node_modules/binaryen/index.js'); // TODO: path?
|
||||
video = new RasterVideo(mainElement,videoWidth,videoHeight,{overscan:true});
|
||||
video.create();
|
||||
poller = setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP, (o,key,code,flags) => {
|
||||
@ -186,7 +190,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
timerCallback = () => {
|
||||
if (!this.isRunning())
|
||||
return;
|
||||
if (gen && gen.switches != null) gen.switches = switches[0];
|
||||
if (top) top.state.switches = switches[0];
|
||||
this.updateFrame();
|
||||
};
|
||||
this.setFrameRate(60);
|
||||
@ -212,7 +216,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
video.setupMouseEvents();
|
||||
// setup mouse click
|
||||
video.vcanvas.click( (e) => {
|
||||
if (!gen) return; // must have created emulator
|
||||
if (!top) return; // must have created emulator
|
||||
if (!e.ctrlKey) {
|
||||
//setFrameRateUI(60);
|
||||
return; // ctrl key must be down
|
||||
@ -238,10 +242,10 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
setGenInputs() {
|
||||
useAudio = (audio != null);
|
||||
//TODO debugCond = this.getDebugCallback();
|
||||
if (gen.switches_p1 != null) gen.switches_p1 = switches[0];
|
||||
if (gen.switches_p2 != null) gen.switches_p2 = switches[1];
|
||||
if (gen.switches_gen != null) gen.switches_gen = switches[2];
|
||||
if (gen.keycode != null) gen.keycode = keycode;
|
||||
top.state.switches_p1 = switches[0];
|
||||
top.state.switches_p2 = switches[1];
|
||||
top.state.switches_gen = switches[2];
|
||||
top.state.keycode = keycode;
|
||||
}
|
||||
|
||||
updateVideoFrame() {
|
||||
@ -316,7 +320,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
}
|
||||
|
||||
updateFrame() {
|
||||
if (!gen) return;
|
||||
if (!top) return;
|
||||
if (this.hasvideo)
|
||||
this.updateVideoFrame();
|
||||
else
|
||||
@ -365,26 +369,26 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
if (inspect) {
|
||||
inspect_data[frameidx] = inspect_obj[inspect_sym];
|
||||
}
|
||||
let rgb = gen.rgb;
|
||||
let rgb = top.state.rgb;
|
||||
idata[frameidx] = rgb & 0x80000000 ? rgb : RGBLOOKUP[rgb & 15];
|
||||
frameidx++;
|
||||
}
|
||||
} else if (!framehsync && gen.hsync) {
|
||||
} else if (!framehsync && top.state.hsync) {
|
||||
framehsync = true;
|
||||
} else if ((framehsync && !gen.hsync) || framex > videoWidth*2) {
|
||||
} else if ((framehsync && !top.state.hsync) || framex > videoWidth*2) {
|
||||
framehsync = false;
|
||||
framex = 0;
|
||||
framey++;
|
||||
if (gen.hpaddle != null) gen.hpaddle = framey > video.paddle_x ? 1 : 0;
|
||||
if (gen.vpaddle != null) gen.vpaddle = framey > video.paddle_y ? 1 : 0;
|
||||
top.state.hpaddle = framey > video.paddle_x ? 1 : 0;
|
||||
top.state.vpaddle = framey > video.paddle_y ? 1 : 0;
|
||||
}
|
||||
if (framey > maxVideoLines || gen.vsync) {
|
||||
if (framey > maxVideoLines || top.state.vsync) {
|
||||
framevsync = true;
|
||||
framey = 0;
|
||||
framex = 0;
|
||||
frameidx = 0;
|
||||
if (gen.hpaddle != null) gen.hpaddle = 0;
|
||||
if (gen.vpaddle != null) gen.vpaddle = 0;
|
||||
top.state.hpaddle = 0;
|
||||
top.state.vpaddle = 0;
|
||||
} else {
|
||||
var wasvsync = framevsync;
|
||||
framevsync = false;
|
||||
@ -400,7 +404,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
var arr = trace_signals;
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
var v = arr[i];
|
||||
var z = gen[v.name];
|
||||
var z = top.state[v.name];
|
||||
trace_buffer[trace_index] = z+0;
|
||||
trace_index++;
|
||||
}
|
||||
@ -411,8 +415,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
fillTraceBuffer(count:number) : boolean {
|
||||
var max_index = Math.min(trace_buffer.length - trace_signals.length, trace_index + count);
|
||||
while (trace_index < max_index) {
|
||||
gen.clk ^= 1;
|
||||
top.eval();
|
||||
top.tick();
|
||||
this.snapshotTrace();
|
||||
if (trace_index == 0)
|
||||
break;
|
||||
@ -443,7 +446,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
|
||||
setSignalValue(index:number, value:number) {
|
||||
var meta = this.getSignalMetadata()[index];
|
||||
gen[meta.label] = value;
|
||||
top.state[meta.label] = value;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
@ -460,16 +463,17 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
}
|
||||
}
|
||||
|
||||
loadROM(title:string, output:any) {
|
||||
async loadROM(title:string, output:any) {
|
||||
var unit = output as HDLUnit;
|
||||
var topmod = unit.modules['TOP'];
|
||||
if (unit.modules && topmod) {
|
||||
{
|
||||
// initialize top module and constant pool
|
||||
top = new HDLModuleJS(topmod, unit.modules['@CONST-POOL@']);
|
||||
top.init();
|
||||
top.reset();
|
||||
gen = top.state;
|
||||
var useWASM = true;
|
||||
var _top = new (useWASM ? HDLModuleWASM : HDLModuleJS)(topmod, unit.modules['@CONST-POOL@']);
|
||||
await _top.init();
|
||||
_top.powercycle();
|
||||
top = _top;
|
||||
// create signal array
|
||||
var signals : WaveformSignal[] = [];
|
||||
for (var key in topmod.vardefs) {
|
||||
@ -490,7 +494,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
// reset
|
||||
this.poweron();
|
||||
// query output signals -- video or not?
|
||||
this.hasvideo = gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined;
|
||||
this.hasvideo = top.state.vsync != null && top.state.hsync != null && top.state.rgb != null;
|
||||
if (this.hasvideo) {
|
||||
const IGNORE_SIGNALS = ['clk','reset'];
|
||||
trace_signals = trace_signals.filter((v) => { return IGNORE_SIGNALS.indexOf(v.name)<0; }); // remove clk, reset
|
||||
@ -507,11 +511,11 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
// replace program ROM, if using the assembler
|
||||
this.reset();
|
||||
if (output.program_rom && output.program_rom_variable) {
|
||||
if (gen[output.program_rom_variable]) {
|
||||
if (gen[output.program_rom_variable].length != output.program_rom.length)
|
||||
alert("ROM size mismatch -- expected " + gen[output.program_rom_variable].length + " got " + output.program_rom.length);
|
||||
if (top.state[output.program_rom_variable]) {
|
||||
if (top.state[output.program_rom_variable].length != output.program_rom.length)
|
||||
alert("ROM size mismatch -- expected " + top.state.program_rom_variable.length + " got " + output.program_rom.length);
|
||||
else
|
||||
gen[output.program_rom_variable].set(output.program_rom);
|
||||
top.state[output.program_rom_variable].set(output.program_rom);
|
||||
} else {
|
||||
alert("No program_rom variable found (" + output.program_rom_variable + ")");
|
||||
}
|
||||
@ -525,7 +529,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
|
||||
restartAudio() {
|
||||
// stop/start audio
|
||||
var hasAudio = gen && gen.spkr !== undefined && frameRate > 1;
|
||||
var hasAudio = top && top.state.spkr != null && frameRate > 1;
|
||||
if (audio && !hasAudio) {
|
||||
audio.stop();
|
||||
audio = null;
|
||||
@ -549,10 +553,10 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
}
|
||||
|
||||
isBlocked() {
|
||||
return top && top.finished;
|
||||
return top && top.isFinished();
|
||||
}
|
||||
isStopped() {
|
||||
return top && top.stopped;
|
||||
return top && top.isStopped();
|
||||
}
|
||||
|
||||
setFrameRate(rateHz) {
|
||||
@ -573,21 +577,21 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
getFrameRate() { return frameRate; }
|
||||
|
||||
poweron() {
|
||||
top.reset();
|
||||
top.powercycle();
|
||||
this.reset();
|
||||
}
|
||||
reset() {
|
||||
if (!gen) return;
|
||||
if (!top) return;
|
||||
//top.reset(); // to avoid clobbering user inputs
|
||||
doreset();
|
||||
trace_index = 0;
|
||||
if (trace_buffer) trace_buffer.fill(0);
|
||||
if (video) video.setRotate(gen.rotate ? -90 : 0);
|
||||
if (video) video.setRotate(top.state.rotate ? -90 : 0);
|
||||
$("#verilog_bar").hide();
|
||||
if (!this.hasvideo) this.resume(); // TODO?
|
||||
}
|
||||
tick() {
|
||||
top.tick2();
|
||||
top.tick2(1);
|
||||
}
|
||||
getToolForFilename(fn) {
|
||||
if (fn.endsWith(".asm")) return "jsasm";
|
||||
@ -597,13 +601,13 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
getDefaultExtension() { return ".v"; };
|
||||
|
||||
inspect(name:string) : string {
|
||||
if (!gen) return;
|
||||
if (!top) return;
|
||||
if (name) name = name.replace('.','_');
|
||||
if (!name || !name.match(/^\w+$/)) {
|
||||
inspect_obj = inspect_sym = null;
|
||||
return;
|
||||
}
|
||||
var val = gen[name];
|
||||
var val = top.state[name];
|
||||
/* TODO
|
||||
if (val === undefined && current_output.code) {
|
||||
var re = new RegExp("(\\w+__DOT__(?:_[dcw]_)" + name + ")\\b", "gm");
|
||||
@ -615,7 +619,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
}
|
||||
*/
|
||||
if (typeof(val) === 'number') {
|
||||
inspect_obj = gen;
|
||||
inspect_obj = top.state;
|
||||
inspect_sym = name;
|
||||
} else {
|
||||
inspect_obj = inspect_sym = null;
|
||||
@ -634,17 +638,13 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
|
||||
// TODO: bind() a function to avoid depot?
|
||||
saveState() {
|
||||
var state = {
|
||||
// TODO: T:gen.ticks(),
|
||||
o:safe_extend(true, {}, gen)
|
||||
};
|
||||
return state;
|
||||
return {o: top.saveState()};
|
||||
}
|
||||
|
||||
loadState(state) {
|
||||
gen = safe_extend(true, gen, state.o);
|
||||
// TODO: gen.setTicks(state.T);
|
||||
//console.log(gen, state.o);
|
||||
top.loadState(state.o);
|
||||
}
|
||||
|
||||
saveControlsState() {
|
||||
return {
|
||||
p1x: video.paddle_x,
|
||||
@ -664,10 +664,13 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
keycode = state.keycode;
|
||||
}
|
||||
getDownloadFile() {
|
||||
return {
|
||||
extension:".js",
|
||||
blob: new Blob([top.getJSCode()], {type:"text/plain"})
|
||||
};
|
||||
// TODO: WASM code too?
|
||||
if (top instanceof HDLModuleJS) {
|
||||
return {
|
||||
extension:".js",
|
||||
blob: new Blob([top.getJSCode()], {type:"text/plain"})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // end of inner class
|
||||
|
Loading…
Reference in New Issue
Block a user