1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-09 15:29:29 +00:00

verilog: refactor, trace buffer, fast video update

This commit is contained in:
Steven Hugg 2021-07-02 09:06:13 -05:00
parent 4e97cd2eef
commit c0d60edbad
6 changed files with 362 additions and 335 deletions

View File

@ -60,6 +60,9 @@ export class HDLModuleJS implements HDLModuleRunner {
init() {
}
dispose() {
}
genFuncs(constants: {}) : VerilatorUnit {
var funcs = Object.create(this.basefuncs);
this.curconsts = constants;

View File

@ -9,6 +9,13 @@ export interface HDLModuleRunner {
isStopped() : boolean;
saveState() : {};
loadState(state: {}) : void;
dispose() : void;
}
export interface HDLModuleTrace {
trace: any;
resetTrace() : void;
nextTrace() : void;
}
///

View File

@ -2,14 +2,6 @@
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 {
_ctor_var_reset(state) : void;
_eval_initial(state) : void;
_eval_settle(state) : void;
_eval(state) : void;
_change_request(state) : boolean;
}
const VERILATOR_UNIT_FUNCTIONS = [
"_ctor_var_reset",
"_eval_initial",
@ -24,9 +16,14 @@ interface Options {
resulttype?: number;
}
const GLOBALOFS = 0;
const MEMORY = "0";
const GLOBAL = "$$GLOBAL";
const CHANGEDET = "$$CHANGE";
const MEMORY = "0";
const TRACERECLEN = "$$treclen";
const TRACEOFS = "$$tofs";
const TRACEEND = "$$tend";
const TRACEBUF = "$$tbuf";
///
@ -60,7 +57,15 @@ function getDataTypeSize(dt: HDLDataType) : number {
}
}
function getArrayElementSize(e: HDLExpr) : number {
function getArrayElementSizeFromType(dtype: HDLDataType) : number {
if (isArrayType(dtype)) {
return getArrayElementSizeFromType(dtype.subtype);
} else {
return getDataTypeSize(dtype);
}
}
function getArrayElementSizeFromExpr(e: HDLExpr) : number {
if (isVarRef(e) && isArrayType(e.dtype)) {
return getDataTypeSize(e.dtype.subtype);
} else if (isBinop(e) && isArrayType(e.dtype)) {
@ -111,15 +116,18 @@ class Struct {
addVar(vardef: HDLVariableDef) {
var size = getDataTypeSize(vardef.dtype);
var rec = this.addEntry(vardef.name, getBinaryenType(size), size, vardef.dtype, false);
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`);
return rec;
}
addEntry(name: string, itype: number, size: number, hdltype: HDLDataType, isParam: boolean) : StructRec {
var align = getAlignmentForSize(size);
alignTo(align: number) : void {
while (this.len % align) this.len++;
}
addEntry(name: string, size: number, itype?: number, hdltype?: HDLDataType, isParam?: boolean) : StructRec {
this.alignTo(getAlignmentForSize(size));
// pointers are 32 bits, so if size > 8 it's a pointer
var rec : StructRec = {
name: name,
@ -165,46 +173,142 @@ export class HDLModuleWASM implements HDLModuleRunner {
stopped: boolean;
databuf: Buffer;
data8: Uint8Array;
data16: Uint16Array;
data32: Uint32Array;
state: any;
statebytes: number;
outputbytes: number;
traceBufferSize: number = 0xff000;
traceRecordSize: number;
traceReadOffset: number;
traceStartOffset: number;
traceEndOffset: number;
trace: any;
constructor(moddef: HDLModuleDef, constpool: HDLModuleDef) {
this.hdlmod = moddef;
this.constpool = constpool;
this.bmod = new binaryen.Module();
this.genTypes();
var memsize = Math.ceil(this.globals.len / 65536) + 1;
this.bmod.setMemory(memsize, memsize, MEMORY); // memory is in 64k chunks
var membytes = this.globals.len;
var memblks = Math.ceil(membytes / 65536) + 1;
this.bmod.setMemory(memblks, memblks, MEMORY); // memory is in 64k chunks
this.genFuncs();
}
async init() {
await this.genModule();
this.genInitData();
this.enableTracing();
}
genTypes() {
powercycle() {
this.finished = false;
this.stopped = false;
(this.instance.exports as any)._ctor_var_reset(GLOBALOFS);
(this.instance.exports as any)._eval_initial(GLOBALOFS);
for (var i=0; i<100; i++) {
(this.instance.exports as any)._eval_settle(GLOBALOFS);
(this.instance.exports as any)._eval(GLOBALOFS);
var Vchange = (this.instance.exports as any)._change_request(GLOBALOFS);
if (!Vchange) {
return;
}
}
throw new HDLError(null, `model did not converge on reset()`)
}
eval() {
(this.instance.exports as any).eval(GLOBALOFS);
}
tick() {
// TODO: faster, save state
this.state.clk ^= 1;
(this.instance.exports as any).eval(GLOBALOFS);
}
tick2(iters: number) {
(this.instance.exports as any).tick2(GLOBALOFS, 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);
}
enableTracing() {
this.traceStartOffset = this.globals.lookup(TRACEBUF).offset;
this.traceEndOffset = this.traceStartOffset + this.traceBufferSize - this.outputbytes;
this.state[TRACEEND] = this.traceEndOffset;
this.state[TRACERECLEN] = this.outputbytes;
this.resetTrace();
//console.log(this.state[TRACEOFS], this.state[TRACERECLEN], this.state[TRACEEND]);
this.trace = new Proxy({}, this.makeScopeProxy(() => { return this.traceReadOffset }));
}
resetTrace() {
this.traceReadOffset = this.traceStartOffset;
this.state[TRACEOFS] = this.traceStartOffset;
}
nextTrace() {
this.traceReadOffset += this.outputbytes;
if (this.traceReadOffset >= this.traceEndOffset)
this.traceReadOffset = this.traceStartOffset;
}
getTraceRecordSize() {
return this.traceRecordSize;
}
dispose() {
if (this.bmod) {
this.bmod.dispose();
this.bmod = null;
}
}
//
private genTypes() {
// generate global variables
var state = new Struct();
this.globals = state;
// TODO: sort globals by size
// outputs are first
for (const [varname, vardef] of Object.entries(this.hdlmod.vardefs)) {
if (vardef.isOutput) state.addVar(vardef);
}
state.alignTo(8);
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);
}
state.alignTo(8);
this.statebytes = state.len;
// followed by constant pool
for (const [varname, vardef] of Object.entries(this.constpool.vardefs)) {
state.addVar(vardef);
}
this.globals = state;
state.alignTo(8);
// and now the trace buffer
state.addEntry(TRACERECLEN, 4, binaryen.i32);
state.addEntry(TRACEOFS, 4, binaryen.i32);
state.addEntry(TRACEEND, 4, binaryen.i32);
state.addEntry(TRACEBUF, this.traceBufferSize);
this.traceRecordSize = this.outputbytes;
}
genFuncs() {
private genFuncs() {
// function type (dsegptr)
var fsig = binaryen.createType([binaryen.i32])
for (var block of this.hdlmod.blocks) {
@ -212,10 +316,10 @@ export class HDLModuleWASM implements HDLModuleRunner {
var fnname = block.name;
// find locals of function
var fscope = new Struct();
fscope.addEntry(GLOBAL, binaryen.i32, 4, null, true); // 1st param to function
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) {
fscope.addEntry(CHANGEDET, binaryen.i32, 1, null, false);
fscope.addEntry(CHANGEDET, 1, binaryen.i32, null, false);
}
this.pushScope(fscope);
block.exprs.forEach((e) => {
@ -243,46 +347,71 @@ export class HDLModuleWASM implements HDLModuleRunner {
throw new HDLError(this.bmod.emitText(), `could not validate wasm module`);
}
async genModule() {
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.databuf = (this.instance.exports[MEMORY] as any).buffer;
this.data8 = new Uint8Array(this.databuf);
this.state = new Proxy({}, {
// TODO
this.data16 = new Uint16Array(this.databuf);
this.data32 = new Uint32Array(this.databuf);
// proxy object to access globals (starting from 0)
this.state = new Proxy({}, this.makeScopeProxy(() => 0));
}
private makeScopeProxy(basefn: () => number) {
return {
// TODO: more types, signed/unsigned
get: (target, prop, receiver) => {
var vref = this.globals.lookup(prop.toString());
var base = basefn();
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()}`);
if (vref.type && isArrayType(vref.type)) {
var elsize = getArrayElementSizeFromType(vref.type);
if (elsize == 1) {
return new Uint8Array(this.databuf, base + vref.offset, vref.size);
} else if (elsize == 2) {
return new Uint16Array(this.databuf, (base + vref.offset) >> 1, vref.size >> 1);
} else if (elsize == 4) {
return new Uint32Array(this.databuf, (base + vref.offset) >> 2, vref.size >> 2);
}
} else {
if (vref.size == 1) {
return this.data8[base + vref.offset];
} else if (vref.size == 2) {
return this.data16[(base + vref.offset) >> 1];
} else if (vref.size == 4) {
return this.data32[(base + vref.offset) >> 2];
}
}
}
return undefined;
},
set: (obj, prop, value) => {
var vref = this.globals.lookup(prop.toString());
var base = basefn();
if (vref !== undefined) {
if (isLogicType(vref.type)) {
this.data8[vref.offset] = value; // TODO: other types
if (vref.size == 1) {
this.data8[(base + vref.offset)] = value;
return true;
} else if (vref.size == 2) {
this.data16[(base + vref.offset) >> 1] = value;
return true;
} else if (vref.size == 4) {
this.data32[(base + vref.offset) >> 2] = value;
return true;
} else if (isArrayType(vref.type)) {
throw new HDLError(vref, `can't set property ${prop.toString()}`);
} else {
return true;
throw new HDLError(vref, `can't set property ${prop.toString()}`);
}
} else {
return true; // silently fail
}
}
});
}
}
genInitData() {
private genInitData() {
for (var rec of this.globals.locals) {
if (rec.init) {
var arr = this.state[rec.name];
@ -300,112 +429,111 @@ export class HDLModuleWASM implements HDLModuleRunner {
}
}
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;
}
private addHelperFunctions() {
this.addCopyTraceRecFunction();
this.addEvalFunction();
this.addTick2Function();
// TODO: this.bmod.addFunctionImport("$$rand", "builtins", "$$rand", binaryen.createType([]), binaryen.i64);
}
private addCopyTraceRecFunction() {
const m = this.bmod;
const o_TRACERECLEN = this.globals.lookup(TRACERECLEN).offset;
const o_TRACEOFS = this.globals.lookup(TRACEOFS).offset;
const o_TRACEEND = this.globals.lookup(TRACEEND).offset;
const o_TRACEBUF = this.globals.lookup(TRACEBUF).offset;
var i32 = binaryen.i32;
var none = binaryen.none;
m.addFunction("copyTraceRec",
binaryen.createType([]),
none,
[i32, i32, i32], // src, len, dest
m.block("@block", [
// $0 = 0 (start of globals)
m.local.set(0, m.i32.const(GLOBALOFS)),
// don't use $0 as data seg offset, assume trace buffer offsets start @ 0
// $1 = TRACERECLEN
m.local.set(1, m.i32.load(0, 4, m.i32.const(o_TRACERECLEN))),
// $2 = TRACEOFS
m.local.set(2, m.i32.load(0, 4, m.i32.const(o_TRACEOFS))),
// while ($1--) [$0]++ = [$2]++
m.loop("@loop", m.block(null, [
m.i64.store(0, 8, m.local.get(2, i32), m.i64.load(0, 8, m.local.get(0, i32))),
m.local.set(0, m.i32.add(m.local.get(0, i32), m.i32.const(8))),
m.local.set(2, m.i32.add(m.local.get(2, i32), m.i32.const(8))),
m.local.set(1, m.i32.sub(m.local.get(1, i32), m.i32.const(8))),
this.bmod.br_if("@loop", m.local.get(1, i32))
])),
// TRACEOFS += TRACERECLEN
m.i32.store(0, 4, m.i32.const(o_TRACEOFS),
m.i32.add(
m.i32.load(0, 4, m.i32.const(o_TRACEOFS)),
m.i32.load(0, 4, m.i32.const(o_TRACERECLEN))
)
),
// break if TRACEOFS < TRACEEND
m.br_if("@block", m.i32.lt_u(
m.i32.load(0, 4, m.i32.const(o_TRACEOFS)),
m.i32.load(0, 4, m.i32.const(o_TRACEEND))
)),
// TRACEOFS = @TRACEBUF
m.i32.store(0, 4, m.i32.const(o_TRACEOFS), m.i32.const(o_TRACEBUF))
])
);
}
private addTick2Function() {
const m = this.bmod;
if (this.globals.lookup('clk')) {
var l_dseg = m.local.get(0, binaryen.i32);
var l_count = m.local.get(1, binaryen.i32);
m.addFunction("tick2",
binaryen.createType([binaryen.i32, binaryen.i32]),
binaryen.none,
[],
m.loop("@loop", m.block(null, [
this.makeSetVariableFunction("clk", 0),
m.drop(m.call("eval", [l_dseg], binaryen.i32)),
this.makeSetVariableFunction("clk", 1),
m.drop(m.call("eval", [l_dseg], binaryen.i32)),
// call copyTraceRec
m.call("copyTraceRec", [], binaryen.none),
// dec $1
m.local.set(1, m.i32.sub(l_count, m.i32.const(1))),
// goto @loop if $1
m.br_if("@loop", l_count)
]))
);
m.addFunctionExport("tick2", "tick2");
} else {
m.addFunctionExport("eval", "tick2");
}
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() {
//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(this.databuf);
var t1 = new Date().getTime();
var tickiters = 10000;
var looplen = Math.round(100000000/tickiters);
for (var i=0; i<looplen; i++) {
this.tick2(tickiters);
}
var t2 = new Date().getTime();
console.log('wasm:',t2-t1,'msec',i*tickiters,'iterations');
console.log(this.databuf);
}
addHelperFunctions() {
private addEvalFunction() {
this.bmod.addFunction("eval",
binaryen.createType([binaryen.i32]), // (dataptr)
binaryen.i32, // return # of iterations
[], // no locals
this.makeTickFunction(0)
binaryen.createType([binaryen.i32]),
binaryen.i32,
[],
this.makeTickFuncBody(0)
);
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",
binaryen.createType([binaryen.i32, binaryen.i32]), // (dataptr, iters)
binaryen.none, // return nothing
[], // no locals
this.bmod.loop("@loop", this.bmod.block(null, [
this.makeSetVariableFunction("clk", 0),
this.bmod.drop(this.bmod.call("eval", [l_dseg], binaryen.i32)),
this.makeSetVariableFunction("clk", 1),
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
this.bmod.br_if("@loop", l_count)
]))
);
this.bmod.addFunctionExport("tick2", "tick2");
}
makeGetVariableFunction(name: string, value: number) {
private 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) {
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};
return this.assign2wasm(dest, src);
}
makeTickFunction(count: number) {
private makeTickFuncBody(count: number) {
var dseg = this.bmod.local.get(0, binaryen.i32);
if (count > 4)
return this.bmod.i32.const(count);
@ -413,27 +541,27 @@ export class HDLModuleWASM implements HDLModuleRunner {
this.bmod.call("_eval", [dseg], binaryen.none),
this.bmod.if(
this.bmod.call("_change_request", [dseg], binaryen.i32),
this.makeTickFunction(count+1),
this.makeTickFuncBody(count+1),
this.bmod.return(this.bmod.local.get(0, binaryen.i32))
)
], binaryen.i32)
}
funcResult(func: HDLBlock) {
private funcResult(func: HDLBlock) {
// only _change functions return a result
return func.name.startsWith("_change_request") ? binaryen.i32 : binaryen.none;
}
pushScope(scope: Struct) {
private pushScope(scope: Struct) {
scope.parent = this.locals;
this.locals = scope;
}
popScope() {
private popScope() {
this.locals = this.locals.parent;
}
i3264(dt: HDLDataType) {
private i3264(dt: HDLDataType) {
var size = getDataTypeSize(dt);
var type = getBinaryenType(size);
if (type == binaryen.i32) return this.bmod.i32;
@ -441,11 +569,11 @@ export class HDLModuleWASM implements HDLModuleRunner {
else throw new HDLError(null, `unknown type for i3264 ${type}`);
}
dataptr() : number {
private dataptr() : number {
return this.bmod.local.get(0, binaryen.i32); // 1st param of function == data ptr
}
e2w(e: HDLExpr, opts?:Options) : number {
private e2w(e: HDLExpr, opts?:Options) : number {
if (e == null) {
return this.bmod.nop();
} else if (isBlock(e)) {
@ -531,7 +659,8 @@ export class HDLModuleWASM implements HDLModuleRunner {
} else if (isBinop(dest)) {
// TODO: array bounds
var addr = this.address2wasm(dest);
return this.storemem(addr, 0, getDataTypeSize(dest.dtype), value);
var elsize = getArrayElementSizeFromExpr(dest.left);
return this.storemem(addr, 0, elsize, value);
}
throw new HDLError([dest, src], `cannot complete assignment`);
}
@ -567,7 +696,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
address2wasm(e: HDLExpr) : number {
if (isBinop(e) && e.op == 'arraysel') {
var array = this.address2wasm(e.left);
var elsize = getArrayElementSize(e.left);
var elsize = getArrayElementSizeFromExpr(e.left);
var index = this.e2w(e.right);
return this.bmod.i32.add(
array,

View File

@ -20,7 +20,23 @@ async function testWASM() {
var bmod = new HDLModuleWASM(parser.modules[modname], parser.modules['@CONST-POOL@']);
await bmod.init();
bmod.powercycle();
bmod.test();
//console.log(this.globals);
bmod.state.reset = 1;
for (var i=0; i<10; i++) {
bmod.tick2(1);
if (i==5) bmod.state.reset = 0;
bmod.nextTrace();
}
console.log(bmod.databuf);
var t1 = new Date().getTime();
var tickiters = 10000;
var looplen = Math.round(100000000/tickiters);
for (var i=0; i<looplen; i++) {
bmod.tick2(tickiters);
}
var t2 = new Date().getTime();
console.log('wasm:',t2-t1,'msec',i*tickiters,'iterations');
console.log(bmod.databuf);
}
async function testJS() {

View File

@ -5,7 +5,7 @@ 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 { HDLModuleRunner, HDLUnit, isLogicType } from "../common/hdl/hdltypes";
import { HDLModuleRunner, HDLModuleTrace, HDLUnit, isLogicType } from "../common/hdl/hdltypes";
import { HDLModuleJS } from "../common/hdl/hdlruntime";
import { HDLModuleWASM } from "../common/hdl/hdlwasm";
@ -78,13 +78,17 @@ const CYCLES_PER_FILL = 20;
var VerilogPlatform = function(mainElement, options) {
this.__proto__ = new (BasePlatform as any)();
var video, audio;
var video : RasterVideo;
var audio;
var poller;
var useAudio = false;
var usePaddles = false;
var videoWidth = 292;
var videoHeight = 256;
var maxVideoLines = 262+40; // vertical hold
var idata, timer, timerCallback;
var idata : Uint32Array;
var timer : AnimationTimer;
var timerCallback;
var top : HDLModuleRunner;
var cyclesPerFrame = (256+23+7+23)*262; // 4857480/60 Hz
@ -109,6 +113,7 @@ var VerilogPlatform = function(mainElement, options) {
var frameidx=0;
var framehsync=false;
var framevsync=false;
var scanlineCycles = 0;
var RGBLOOKUP = [
0xff222222,
@ -240,7 +245,8 @@ var VerilogPlatform = function(mainElement, options) {
}
setGenInputs() {
useAudio = (audio != null);
useAudio = audio != null && top.state.spkr != null;
usePaddles = top.state.hpaddle != null || top.state.vpaddle != null;
//TODO debugCond = this.getDebugCallback();
top.state.switches_p1 = switches[0];
top.state.switches_p2 = switches[1];
@ -354,9 +360,16 @@ var VerilogPlatform = function(mainElement, options) {
}
}
updateVideoFrameCycles(ncycles:number, sync:boolean, trace:boolean) {
updateVideoFrameCycles(ncycles:number, sync:boolean, trace:boolean) : void {
ncycles |= 0;
var inspect = inspect_obj && inspect_sym;
// use fast trace buffer-based update?
if (sync && !trace && top['trace'] != null) {
this.updateVideoFrameFast((top as any) as HDLModuleTrace);
this.updateRecorder();
return;
}
// use slow update method
var trace0 = trace_index;
while (ncycles--) {
if (trace) {
@ -400,6 +413,59 @@ var VerilogPlatform = function(mainElement, options) {
}
}
// use trace buffer to update video
updateVideoFrameFast(tmod: HDLModuleTrace) {
var maxLineCycles = videoWidth < 300 ? 521 : 1009; // prime number so we eventually sync up
if (!scanlineCycles) scanlineCycles = maxLineCycles;
var nextlineCycles = scanlineCycles;
// TODO: we can go faster if no paddle/sound
frameidx = 0;
var wasvsync = false;
// iterate through a frame of scanlines + room for vsync
for (framey=0; framey<videoHeight*2; framey++) {
if (usePaddles) {
top.state.hpaddle = framey > video.paddle_x ? 1 : 0;
top.state.vpaddle = framey > video.paddle_y ? 1 : 0;
}
// generate frames in trace buffer
top.tick2(nextlineCycles);
// convert trace buffer to video/audio
var n = 0;
if (framey < videoHeight) {
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);
}
tmod.nextTrace();
}
n += videoWidth;
}
// find hsync
while (n < maxLineCycles && !tmod.trace.hsync) { tmod.nextTrace(); n++; }
while (n < maxLineCycles && tmod.trace.hsync) { 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
nextlineCycles = n;
tmod.resetTrace();
} else {
// not in sync, set to prime # and we'll eventually sync
nextlineCycles = maxLineCycles;
scanlineCycles = n;
}
// exit when vsync starts and then stops
if (tmod.trace.vsync) {
wasvsync = true;
} else if (wasvsync) {
top.state.hpaddle = 0;
top.state.vpaddle = 0;
break;
}
}
}
snapshotTrace() {
var arr = trace_signals;
for (var i=0; i<arr.length; i++) {
@ -473,6 +539,7 @@ var VerilogPlatform = function(mainElement, options) {
var _top = new (useWASM ? HDLModuleWASM : HDLModuleJS)(topmod, unit.modules['@CONST-POOL@']);
await _top.init();
_top.powercycle();
if (top) top.dispose();
top = _top;
// create signal array
var signals : WaveformSignal[] = [];
@ -510,10 +577,11 @@ var VerilogPlatform = function(mainElement, options) {
}
// replace program ROM, if using the assembler
this.reset();
// TODO: fix this, it ain't good
if (output.program_rom && output.program_rom_variable) {
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);
alert("ROM size mismatch -- expected " + top.state[output.program_rom_variable].length + " got " + output.program_rom.length);
else
top.state[output.program_rom_variable].set(output.program_rom);
} else {

View File

@ -1,196 +0,0 @@
type V2JS_Var = {
wordlen:number,
name:string,
len:number,
ofs:number,
arrdim?:number[],
input:boolean,
output:boolean,
}
type V2JS_Code = {
name:string,
ports:V2JS_Var[],
signals:V2JS_Var[],
funcs:string[],
}
type V2JS_Output = {
output:{
code:V2JS_Code,
name:string,
ports:V2JS_Var[],
signals:V2JS_Var[],
}
}
function translateVerilatorOutputToJS(htext:string, cpptext:string) {
var moduleName : string;
var symsName : string;
function parseDecls(text:string, arr:V2JS_Var[], name:string, bin?:boolean, bout?:boolean) {
var re = new RegExp(name + "(\\d*)[(](\\w+),(\\d+),(\\d+)[)]", 'gm');
var m;
while ((m = re.exec(text))) {
arr.push({
wordlen:parseInt(m[1]),
name:m[2],
len:parseInt(m[3])+1,
ofs:parseInt(m[4]),
input:bin,
output:bout,
});
}
re = new RegExp(name + "(\\d*)[(](\\w+)\\[(\\d+)\\],(\\d+),(\\d+)[)]", 'gm');
var m;
while ((m = re.exec(text))) {
arr.push({
wordlen:parseInt(m[1]),
name:m[2],
arrdim:[parseInt(m[3])],
len:parseInt(m[4])+1,
ofs:parseInt(m[5]),
input:bin,
output:bout,
});
}
re = new RegExp(name + "(\\d*)[(](\\w+)\\[(\\d+)\\]\\[(\\d+)\\],(\\d+),(\\d+)[)]", 'gm');
var m;
while ((m = re.exec(text))) {
arr.push({
wordlen:parseInt(m[1]),
name:m[2],
arrdim:[parseInt(m[3]), parseInt(m[4])],
len:parseInt(m[5])+1,
ofs:parseInt(m[6]),
input:bin,
output:bout,
});
}
}
function buildModule(o : V2JS_Code) : string {
var m = '"use strict";\n';
for (var i=0; i<o.ports.length; i++) {
m += "\tthis." + o.ports[i].name + ";\n";
}
for (var i=0; i<o.signals.length; i++) {
var sig = o.signals[i];
if (sig.arrdim) {
if (sig.arrdim.length == 1) {
m += "\tvar " + sig.name + " = this." + sig.name + " = [];\n";
} else if (sig.arrdim.length == 2) {
m += "\tvar " + sig.name + " = this." + sig.name + " = [];\n";
m += "\tfor(var i=0; i<" + sig.arrdim[0] + "; i++) { " + sig.name + "[i] = []; }\n";
}
} else {
m += "\tthis." + sig.name + ";\n";
}
}
for (var i=0; i<o.funcs.length; i++) {
m += o.funcs[i];
}
return m;
}
function getStats(o : V2JS_Code) {
var nmembits = 0;
var nlines = 0;
for (var sig of o.signals.concat(o.ports)) {
var len = sig.len || 0;
if (sig.arrdim)
for (var n of sig.arrdim)
len *= n;
nmembits += len;
}
for (var fn of o.funcs) {
nlines += fn.split('\n').length;
}
//console.log(nmembits,'bits',nlines,'lines');
return {bits:nmembits, lines:nlines};
}
function translateFunction(text : string) : string {
text = text.trim();
if (text.match(/VL_RAND_RESET_Q/))
throw Error("Values longer than 32 bits are not supported");
var funcname = text.match(/(\w+)/)[1];
text = text.replace(symsName + "* __restrict ", "");
text = text.replace(moduleName + "* __restrict vlTOPp VL_ATTR_UNUSED", "var vlTOPp");
text = text.replace(/\bVL_DEBUG_IF\(([^]+?)\);\n/g,"/*VL_DEBUG_IF($1);*/\n");
//text = text.replace(/\bVL_DEBUG_IF/g,"!__debug__?0:\n");
text = text.replace(/\bVL_SIG(\d*)[(](\w+),(\d+),(\d+)[)]/g, 'var $2');
text = text.replace(/\b->\b/g, ".");
text = text.replace('VL_INLINE_OPT', '');
text = text.replace(/[(]IData[)]/g, '');
text = text.replace(/\b(0x[0-9a-f]+)U/gi, '$1');
text = text.replace(/\b([0-9]+)U/gi, '$1');
text = text.replace(/\bQData /g, 'var ');
text = text.replace(/\bbool /g, '');
text = text.replace(/\bint /g, 'var ');
text = text.replace(/(\w+ = VL_RAND_RESET_)/g, 'this.$1'); // TODO?
//text = text.replace(/^\s*(\w+ = \d+;)/gm, 'this.$1'); // TODO?
//text = text.replace(/(\w+\[\w+\] = VL_RAND_RESET_I)/g, 'this.$1');
text = text.replace(/^#/gm, '//#');
text = text.replace(/VL_LIKELY/g, '!!');
text = text.replace(/VL_UNLIKELY/g, '!!');
// for memread
text = text.replace(/VL_SIGW[(](\w+),(\d+),(\d+),(\d+)[)]/g, 'var $1 = new Uint32Array($4)');
// convert VL_ULL() 64-bits into an array of two 32-bits
text = text.replace(/VL_ULL[(]0x([0-9a-f]+?)([0-9a-f]{8})[)]/g, '[0x$2, 0x$1]');
//[%0t] %Error: scoreboard.v:53: Assertion failed in %Nscoreboard_top.scoreboard_gen: reset 64 -935359306 Vscoreboard_top
text = text.replace(/Verilated::(\w+)Error/g, 'console.log');
text = text.replace(/vlSymsp.name[(][)]/g, '"'+moduleName+'"');
return "function " + text + "\nthis." + funcname + " = " + funcname + ";\n";
}
function translateStaticVars(text : string) : string {
var s = "";
var m;
var re = /VL_ST_SIG(\d+)[(](\w+?)::(\w+).(\d+).,(\d+),(\d+)[)]/g;
while (m = re.exec(text)) {
s += "var " + m[3] + " = this." + m[3] + " = new Uint" + m[1] + "Array(" + m[4] + ");\n";
}
return s;
}
// parse header file
moduleName = /VL_MODULE.(\w+)./.exec(htext)[1];
symsName = moduleName + "__Syms";
var ports = [];
parseDecls(htext, ports, 'VL_IN', true, false);
parseDecls(htext, ports, 'VL_OUT', false, true);
var signals = [];
parseDecls(htext, signals, 'VL_SIG');
// parse cpp file
// split functions
var re_fnsplit = new RegExp("(?:void|QData) " + moduleName + "::");
var functexts = cpptext.split(re_fnsplit);
var funcs = [];
funcs.push(translateStaticVars(functexts[0]));
for (var i=4; i<functexts.length; i++) {
var fntxt = translateFunction(functexts[i]);
funcs.push(fntxt);
}
var modinput = {
name:moduleName,
ports:ports,
signals:signals,
funcs:funcs,
};
return {
output:{
code:buildModule(modinput),
name:moduleName,
ports:ports,
signals:signals,
//stats:getStats(modinput),
}
};
}