verilog: randomizeOnReset = true except for unit tests (only <=32 bit values reset)

This commit is contained in:
Steven Hugg 2021-07-09 13:57:54 -05:00
parent 876d66e6de
commit 10d04f9114
8 changed files with 87 additions and 41 deletions

View File

@ -113,7 +113,7 @@ TODO:
- multidim arrays
- optimize trace log w/ wasm buffer
- yosys compatibility
- randomize on reset? (https://www.xilinx.com/support/documentation/white_papers/wp272.pdf)
- single-stepping vector games makes screen fade
- break on stack overflow, illegal op, bad access, BRK, etc
- show in scope instead?

View File

@ -80,5 +80,5 @@ module test_ram1_top(clk, reset, hsync, vsync, rgb);
ram_writeenable <= 0;
end
endcase
endmodule

View File

@ -163,11 +163,9 @@ module digits10_array(digit, yofs, bits);
bitarray[9][4] = 5'b11111;
// clear unused array entries
/* TODO
for (i = 10; i <= 15; i++)
for (j = 0; j <= 4; j++)
bitarray[i][j] = 0;
*/
end
endmodule

View File

@ -111,6 +111,7 @@ interface StructRec {
index: number;
init: HDLBlock;
constval: HDLConstant;
reset: boolean;
}
class Struct {
@ -144,6 +145,7 @@ class Struct {
offset: this.len,
init: null,
constval: null,
reset: false,
}
this.len += size;
if (rec.name != null) this.vars[rec.name] = rec;
@ -196,6 +198,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
traceEndOffset: number;
trace: any;
randomizeOnReset: boolean = false;
finished: boolean;
stopped: boolean;
resetStartTimeMsec : number;
@ -211,7 +214,6 @@ export class HDLModuleWASM implements HDLModuleRunner {
async init() {
await this.genModule();
this.genInitData();
this.enableTracing();
}
@ -220,6 +222,8 @@ export class HDLModuleWASM implements HDLModuleRunner {
this.resetStartTimeMsec = new Date().getTime() - 1;
this.finished = false;
this.stopped = false;
this.clearMutableState();
this.setInitialValues();
(this.instance.exports as any)._ctor_var_reset(GLOBALOFS);
(this.instance.exports as any)._eval_initial(GLOBALOFS);
for (var i=0; i<100; i++) {
@ -258,6 +262,7 @@ export class HDLModuleWASM implements HDLModuleRunner {
this.data8.set(state.o as Uint8Array);
}
// get tree of global variables for debugging
getGlobals() {
var g = {};
for (const [varname, vardef] of Object.entries(this.hdlmod.vardefs)) {
@ -329,8 +334,10 @@ export class HDLModuleWASM implements HDLModuleRunner {
// generate global variables
var state = new Struct();
this.globals = state;
// separate vars and constants
var vardefs = Object.values(this.hdlmod.vardefs).filter(vd => vd.constValue == null);
var constdefs = Object.values(this.hdlmod.vardefs).filter(vd => vd.constValue != null);
// sort globals by output flag and size
var vardefs = Object.values(this.hdlmod.vardefs);
function getVarDefSortKey(vdef: HDLVariableDef) {
var val = getDataTypeSize(vdef.dtype); // sort by size
if (!vdef.isOutput) val += 1000000; // outputs are first in list
@ -353,8 +360,11 @@ export class HDLModuleWASM implements HDLModuleRunner {
}
state.alignTo(8);
this.statebytes = state.len;
// followed by constant pool
// followed by constants and constant pool
if (this.constpool) {
for (const vardef of Object.values(constdefs)) {
state.addVar(vardef);
}
for (const vardef of Object.values(this.constpool.vardefs)) {
state.addVar(vardef);
}
@ -444,11 +454,22 @@ export class HDLModuleWASM implements HDLModuleRunner {
private defineProperty(proxy, basefn: () => number, vref: StructRec) {
var _this = this;
// precompute some things
var elsize = vref.type && getArrayElementSizeFromType(vref.type);
var eltype = vref.type;
while (eltype && isArrayType(eltype)) {
eltype = eltype.subtype;
}
var mask = -1; // set all bits
if (eltype && isLogicType(eltype) && eltype.left < 31) {
mask = (1 << (eltype.left+1)) - 1; // set partial bits
}
// define get/set on proxy object
Object.defineProperty(proxy, vref.name, {
get() {
let base = basefn();
if (vref.type && isArrayType(vref.type)) {
var elsize = getArrayElementSizeFromType(vref.type);
// TODO: can't mask unused bits in array
if (elsize == 1) {
return new Uint8Array(_this.databuf, base + vref.offset, vref.size);
} else if (elsize == 2) {
@ -470,13 +491,13 @@ export class HDLModuleWASM implements HDLModuleRunner {
set(value) {
var base = basefn();
if (vref.size == 1) {
_this.data8[(base + vref.offset)] = value;
_this.data8[(base + vref.offset)] = value & mask;
return true;
} else if (vref.size == 2) {
_this.data16[(base + vref.offset) >> 1] = value;
_this.data16[(base + vref.offset) >> 1] = value & mask;
return true;
} else if (vref.size == 4) {
_this.data32[(base + vref.offset) >> 2] = value;
_this.data32[(base + vref.offset) >> 2] = value & mask;
return true;
} else {
throw new HDLError(vref, `can't set property ${vref.name}`);
@ -495,27 +516,46 @@ export class HDLModuleWASM implements HDLModuleRunner {
return proxy;
}
private genInitData() {
setInitialValues() {
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 (multidimensional arrays not supported)`);
}
this.setInitialValue(rec);
}
}
private setInitialValue(rec: StructRec) {
var arr = this.state[rec.name];
if (rec.init) {
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 (multidimensional arrays not supported)`);
}
//console.log(rec.name, rec.type, arr);
}
if (rec.constval) {
this.state[rec.name] = rec.constval.cvalue || rec.constval.bigvalue;
//console.log(rec.name, rec.type, arr);
} else if (rec.constval) {
this.state[rec.name] = rec.constval.cvalue || rec.constval.bigvalue;
} else if (rec.type && rec.reset && this.randomizeOnReset) {
if (isLogicType(rec.type) && typeof arr === 'number') {
this.state[rec.name] = Math.random() * 4294967296; // don't need to mask
} else if (isArrayType(rec.type) && isLogicType(rec.type.subtype)) {
// array types are't mask-protected yet
var mask = (1 << (rec.type.subtype.left + 1)) - 1;
for (var i=0; i<arr.length; i++) {
arr[i] = (Math.random() * 4294967296) & mask;
}
} else {
console.log(`could not reset ${rec.name}`)
}
}
}
clearMutableState() {
this.data32.fill(0, 0, this.statebytes >> 2);
}
private addHelperFunctions() {
this.addCopyTraceRecFunction();
this.addEvalFunction();
@ -979,6 +1019,11 @@ export class HDLModuleWASM implements HDLModuleRunner {
}
_creset2wasm(e: HDLUnop, opts:Options) {
if (isVarRef(e.left)) {
var glob = this.globals.lookup(e.left.refname);
// TOOD: must be better way to tell non-randomize values
if (glob && !glob.name.startsWith("__")) glob.reset = true;
}
// TODO return this.e2w(e.left, opts);
return this.bmod.nop();
}

View File

@ -66,13 +66,13 @@ function addPageFocusHandlers() {
});
}
function startROM(title, rom) {
async function startROM(title, rom) {
if (!rom ) {
alert("No ROM found.");
return;
}
console.log(rom.length + ' bytes');
platform.loadROM(title, rom);
await platform.loadROM(title, rom);
platform.resume();
}

View File

@ -1156,7 +1156,7 @@ function measureBuildTime() {
//gaEvent('build', platform_id);
}
function setCompileOutput(data: WorkerResult) {
async function setCompileOutput(data: WorkerResult) {
// errors? mark them in editor
if (data && data.errors && data.errors.length > 0) {
toolbar.addClass("has-errors");
@ -1178,7 +1178,7 @@ function setCompileOutput(data: WorkerResult) {
try {
clearBreakpoint(); // so we can replace memory (TODO: change toolbar btn)
_resetRecording();
platform.loadROM(getCurrentPresetTitle(), rom); // TODO: should be async?
await platform.loadROM(getCurrentPresetTitle(), rom);
current_output = rom;
if (!userPaused) _resume();
measureBuildTime();

View File

@ -190,7 +190,7 @@ var VerilogPlatform = function(mainElement, options) {
await loadScript('./gen/common/hdl/hdltypes.js');
await loadScript('./gen/common/hdl/hdlruntime.js');
await loadScript('./gen/common/hdl/hdlwasm.js');
await loadScript('./lib/binaryen.js'); // TODO: path?
await loadScript('./lib/binaryen.js');
video = new RasterVideo(mainElement,videoWidth,videoHeight,{overscan:true});
video.create();
poller = setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP, (o,key,code,flags) => {
@ -275,7 +275,7 @@ var VerilogPlatform = function(mainElement, options) {
}
// paint into frame, synched with vsync if full speed
var trace = this.isScopeVisible();
this.updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace);
this.updateVideoFrameCycles(Math.ceil(cyclesPerFrame * fps/60), sync, trace);
if (fps < 0.25) {
idata[frameidx] = -1;
}
@ -431,9 +431,7 @@ var VerilogPlatform = function(mainElement, options) {
if (scanlineCycles <= 0) throw new Error(`scanlineCycles must be > 0`);
var maxLineCycles = 1009; // prime number so we eventually sync up
var nextlineCycles = scanlineCycles || maxLineCycles;
// 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
@ -446,7 +444,6 @@ var VerilogPlatform = function(mainElement, options) {
if (nextlineCycles > 0) {
top.tick2(nextlineCycles);
}
// TODO: this has to be done more quickly
resetKbdStrobe();
// convert trace buffer to video/audio
var n = 0;
@ -496,10 +493,12 @@ var VerilogPlatform = function(mainElement, options) {
tmod.resetTrace();
// exit when vsync starts and then stops
if (tmod.trace.vsync) {
wasvsync = true;
framevsync = true;
top.state.hpaddle = 0;
top.state.vpaddle = 0;
} else if (wasvsync) {
framex = framey = frameidx = 0;
} else if (framevsync) {
framevsync = false;
break;
}
}
@ -577,7 +576,6 @@ var VerilogPlatform = function(mainElement, options) {
}
}
// TODO: can this be async?
async loadROM(title:string, output:any) {
var unit = output as HDLUnit;
var topmod = unit.modules['TOP'];
@ -589,7 +587,6 @@ var VerilogPlatform = function(mainElement, options) {
var _top = new topcons(topmod, unit.modules['@CONST-POOL@']);
_top.getFileData = (path) => current_project.filedata[path]; // external file provider
await _top.init();
_top.powercycle();
this.dispose();
top = _top;
// create signal array
@ -610,6 +607,9 @@ var VerilogPlatform = function(mainElement, options) {
trace_signals = trace_signals.filter((v) => { return !v.label.startsWith("__V"); }); // remove __Vclklast etc
trace_index = 0;
// reset
if (top instanceof HDLModuleWASM) {
top.randomizeOnReset = true;
}
this.poweron();
// query output signals -- video or not?
this.hasvideo = top.state.vsync != null && top.state.hsync != null && top.state.rgb != null;
@ -702,7 +702,7 @@ var VerilogPlatform = function(mainElement, options) {
}
reset() {
if (!top) return;
//top.reset(); // to avoid clobbering user inputs
// TODO: how do we avoid clobbering user-modified signals?
doreset();
trace_index = 0;
if (trace_buffer) trace_buffer.fill(0);
@ -754,7 +754,6 @@ var VerilogPlatform = function(mainElement, options) {
}
}
// TODO: bind() a function to avoid depot?
saveState() {
return {o: top && top.saveState()};
}
@ -782,7 +781,6 @@ var VerilogPlatform = function(mainElement, options) {
keycode = state.keycode;
}
getDownloadFile() {
// TODO: WASM code too?
if (top instanceof HDLModuleJS) {
return {
extension:".js",
@ -795,6 +793,9 @@ var VerilogPlatform = function(mainElement, options) {
};
}
}
getHDLModuleRunner() {
return top;
}
} // end of inner class
return new _VerilogPlatform();

View File

@ -28,6 +28,8 @@ async function loadPlatform(msg) {
//console.log(msg.output.ports);
//console.log(msg.output.signals);
await platform.loadROM("ROM", msg.output);
platform.getHDLModuleRunner().randomizeOnReset = false;
platform.getHDLModuleRunner().powercycle();
//await platform.loadROM("ROM", msg.output);
for (var i=0; i<100000 && !platform.isBlocked(); i++) {
platform.tick();