verilog -> typescript, but why does loadState() make it slow with racing_game_cpu?

This commit is contained in:
Steven Hugg 2018-08-29 08:24:13 -04:00
parent 132147b1a4
commit 05f5b96256
8 changed files with 389 additions and 368 deletions

View File

@ -60,8 +60,8 @@ TODO:
- links to external tools in ide
- error msg when #link doesn't work
- figure out folders for projects for real
On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional?
- verilog download rom
- why loadState() on verilog kill perf?

View File

@ -22,8 +22,10 @@ export interface CpuState {
N,V,D,Z,C:boolean*/
};
export interface EmuState {
c:CpuState, // CPU state
b?:number[] // RAM (TODO: not for vcs)
c?:CpuState, // CPU state
b?:number[], // RAM (TODO: not for vcs)
o?:{}, // verilog
T?:number, // verilog
};
export interface EmuControlsState {
}
@ -107,6 +109,7 @@ export abstract class BasePlatform {
abstract saveState() : EmuState;
abstract pause() : void;
abstract resume() : void;
abstract advance(novideo? : boolean) : void;
setRecorder(recorder : EmuRecorder) : void {
this.recorder = recorder;
@ -129,7 +132,6 @@ export abstract class BaseDebugPlatform extends BasePlatform {
abstract getCPUState() : CpuState;
abstract readAddress(addr:number) : number;
abstract advance(novideo? : boolean) : void;
getDebugCallback() : DebugCondition {
return this.debugCondition;

View File

@ -1,8 +1,9 @@
"use strict";
import { Platform } from "../baseplatform";
import { Platform, BasePlatform } from "../baseplatform";
import { PLATFORMS, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu";
import { SampleAudio } from "../audio";
import { safe_extend } from "../util";
var VERILOG_PRESETS = [
{id:'clock_divider.v', name:'Clock Divider'},
@ -55,109 +56,113 @@ var VERILOG_KEYCODE_MAP = makeKeycodeMap([
[Keys.VK_7, 2, 0x10],
]);
var vl_finished = false;
var vl_stopped = false;
// SIMULATOR STUFF (should be global)
// TODO: these have to be global
export var vl_finished = false;
export var vl_stopped = false;
var VL_UL = this.VL_UL = function(x) { return x|0; }
var VL_ULL = this.VL_ULL = function(x) { return x|0; }
var VL_TIME_Q = this.VL_TIME_Q = function() { return (new Date().getTime())|0; }
export function VL_UL(x) { return x|0; }
export function VL_ULL(x) { return x|0; }
export function VL_TIME_Q() { return (new Date().getTime())|0; }
/// Return true if data[bit] set
var VL_BITISSET_I = this.VL_BITISSET_I = function(data,bit) { return (data & (VL_UL(1)<<VL_UL(bit))); }
export function VL_BITISSET_I(data,bit) { return (data & (VL_UL(1)<<VL_UL(bit))); }
var VL_EXTENDSIGN_I = this.VL_EXTENDSIGN_I = function(lbits, lhs) { return (-((lhs)&(VL_UL(1)<<(lbits-1)))); }
export function VL_EXTENDSIGN_I(lbits, lhs) { return (-((lhs)&(VL_UL(1)<<(lbits-1)))); }
var VL_EXTEND_II = this.VL_EXTEND_II = function(obits,lbits,lhs) { return lhs; }
export function VL_EXTEND_II(obits,lbits,lhs) { return lhs; }
var VL_EXTENDS_II = this.VL_EXTENDS_II = function(x,lbits,lhs) {
export function VL_EXTENDS_II(x,lbits,lhs) {
return VL_EXTENDSIGN_I(lbits,lhs) | lhs;
}
var VL_EXTEND_II = this.VL_EXTEND_II = function(obits,lbits,lhs) { return lhs; }
export function VL_NEGATE_I(x) { return -x; }
var VL_NEGATE_I = this.VL_NEGATE_I = function(x) { return -x; }
var VL_LTS_III = this.VL_LTS_III = function(x,lbits,y,lhs,rhs) {
export function VL_LTS_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) < VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
var VL_GTS_III = this.VL_GTS_III = function(x,lbits,y,lhs,rhs) {
export function VL_GTS_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) > VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
var VL_LTES_III = this.VL_LTES_III = function(x,lbits,y,lhs,rhs) {
export function VL_LTES_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) <= VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
var VL_GTES_III = this.VL_GTES_III = function(x,lbits,y,lhs,rhs) {
export function VL_GTES_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) >= VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
var VL_MODDIV_III = this.VL_MODDIV_III = function(lbits,lhs,rhs) {
export function VL_MODDIV_III(lbits,lhs,rhs) {
return (((rhs)==0)?0:(lhs)%(rhs)); }
var VL_MODDIVS_III = this.VL_MODDIVS_III = function(lbits,lhs,rhs) {
export function VL_MODDIVS_III(lbits,lhs,rhs) {
return (((rhs)==0)?0:(lhs)%(rhs)); }
var VL_REDXOR_32 = this.VL_REDXOR_32 = function(r) {
export function VL_REDXOR_32(r) {
r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); r=(r^(r>>16));
return r;
}
var VL_WRITEF = this.VL_WRITEF = console.log; // TODO: $write
export var VL_WRITEF = console.log; // TODO: $write
var vl_finish = this.vl_finish = function(filename,lineno,hier) {
export function vl_finish(filename,lineno,hier) {
console.log("Finished at " + filename + ":" + lineno, hier);
vl_finished = true;
}
var vl_stop = this.vl_stop = function(filename,lineno,hier) {
export function vl_stop(filename,lineno,hier) {
console.log("Stopped at " + filename + ":" + lineno, hier);
vl_stopped = true;
}
var VL_RAND_RESET_I = this.VL_RAND_RESET_I = function(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
export function VL_RAND_RESET_I(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
var VL_RANDOM_I = this.VL_RANDOM_I = function(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
export function VL_RANDOM_I(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
// SIMULATOR BASE
//
abstract class VerilatorBase {
function VerilatorBase() {
totalTicks = 0;
maxVclockLoop = 1;
clk = 0;
reset = 0;
//
var totalTicks = 0;
function vl_fatal(msg) {
vl_fatal(msg:string) {
console.log(msg);
}
this.ticks = function() { return totalTicks; }
this.setTicks = function(T) { totalTicks = T|0; }
ticks() : number { return this.totalTicks; }
setTicks(T:number) { this.totalTicks = T|0; }
this.__reset = function() {
__reset() {
if (this.reset !== undefined) {
totalTicks = 0;
this.totalTicks = 0;
this.reset = 0;
this.tick2();
this.reset = 1;
}
}
this.__unreset = function() {
__unreset() {
if (this.reset !== undefined) {
this.reset = 0;
}
}
this.tick2 = function() {
tick2() {
this.clk = 0;
this.eval();
this.clk = 1;
this.eval();
}
abstract _eval(vlSymsp);
abstract __Vm_didInit : boolean;
abstract __Vm_activity : boolean;
abstract _change_request(vlSymsp);
abstract _eval_initial(vlSymsp);
abstract _eval_settle(vlSymsp);
var vlSymsp = this; //{TOPp:this};
var maxVclockLoop = 1;
this.eval = function() {
eval() {
let vlSymsp = this; //{TOPp:this};
// Initialize
if (!vlSymsp.__Vm_didInit)
this._eval_initial_loop(vlSymsp);
@ -170,16 +175,16 @@ function VerilatorBase() {
vlSymsp.__Vm_activity = true;
this._eval(vlSymsp);
__Vchange = this._change_request(vlSymsp);
if (++__VclockLoop > 100) { vl_fatal("Verilated model didn't converge"); }
if (++__VclockLoop > 100) { this.vl_fatal("Verilated model didn't converge"); }
}
if (__VclockLoop > maxVclockLoop) {
maxVclockLoop = __VclockLoop;
console.log("Graph took " + maxVclockLoop + " iterations to stabilize");
if (__VclockLoop > this.maxVclockLoop) {
this.maxVclockLoop = __VclockLoop;
console.log("Graph took " + this.maxVclockLoop + " iterations to stabilize");
}
totalTicks++;
this.totalTicks++;
}
this._eval_initial_loop = function(vlSymsp) {
_eval_initial_loop(vlSymsp) {
vlSymsp.TOPp = this;
vlSymsp.__Vm_didInit = true;
this._eval_initial(vlSymsp);
@ -190,13 +195,16 @@ function VerilatorBase() {
this._eval_settle(vlSymsp);
this._eval(vlSymsp);
__Vchange = this._change_request(vlSymsp);
if (++__VclockLoop > 100) { vl_fatal("Verilated model didn't DC converge"); }
if (++__VclockLoop > 100) { this.vl_fatal("Verilated model didn't DC converge"); }
}
}
}
// PLATFORM
var VerilogPlatform = function(mainElement, options) {
var self = this;
this.__proto__ = new (BasePlatform as any)();
var video, audio;
var useAudio = false;
var videoWidth = 292;
@ -206,11 +214,17 @@ var VerilogPlatform = function(mainElement, options) {
var gen;
var cyclesPerFrame = (256+23+7+23)*262; // 4857480/60 Hz
var current_output;
// control inputs
var paddle_x = 0;
var paddle_y = 0;
var switches = [0,0,0];
// inspect feature
var inspect_obj, inspect_sym;
var inspect_data = new Uint32Array(videoWidth * videoHeight);
// for scope
var scope_time_x = 0; // scope cursor
var scope_x_offset = 0;
var scope_y_offset = 0;
@ -223,7 +237,6 @@ var VerilogPlatform = function(mainElement, options) {
var scopeImageData;
var sdata; // scope data
var module_name;
var yposlist = [];
var lasty = [];
var lastval = [];
@ -234,7 +247,12 @@ var VerilogPlatform = function(mainElement, options) {
var mouse_pressed;
var dirty = false;
this.getPresets = function() { return VERILOG_PRESETS; }
// for virtual CRT
var framex=0;
var framey=0;
var frameidx=0;
var framehsync=false;
var framevsync=false;
var RGBLOOKUP = [
0xff222222,
@ -256,6 +274,7 @@ var VerilogPlatform = function(mainElement, options) {
];
var debugCond;
var frameRate = 0;
function vidtick() {
gen.tick2();
@ -265,7 +284,168 @@ var VerilogPlatform = function(mainElement, options) {
debugCond = null;
}
function updateInspectionFrame() {
function clamp(minv,maxv,v) {
return (v < minv) ? minv : (v > maxv) ? maxv : v;
}
function shadowText(ctx, txt, x, y) {
ctx.shadowColor = "black";
ctx.shadowBlur = 0;
ctx.shadowOffsetY = -1;
ctx.shadowOffsetX = 0;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 1;
ctx.shadowOffsetX = 0;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 0;
ctx.shadowOffsetX = -1;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 0;
ctx.shadowOffsetX = 1;
ctx.fillText(txt, x, y);
ctx.shadowOffsetX = 0;
}
// inner Platform class
class _VerilogPlatform extends BasePlatform {
getPresets() { return VERILOG_PRESETS; }
start() {
video = new RasterVideo(mainElement,videoWidth,videoHeight);
video.create();
var ctx = video.getContext();
ctx.font = "8px TinyFont";
ctx.fillStyle = "white";
ctx.textAlign = "left";
setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP);
var vcanvas = $(video.canvas);
vcanvas.mousemove( (e) => {
var new_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 20);
var new_y = Math.floor(e.offsetY * video.canvas.height / vcanvas.height() - 20);
if (mouse_pressed) {
scope_y_offset = clamp(Math.min(0,-scope_max_y+videoHeight), 0, scope_y_offset + new_y - paddle_y);
scope_time_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 16);
dirty = true;
this.refreshFrame();
}
paddle_x = clamp(8, 240, new_x);
paddle_y = clamp(8, 240, new_y);
});
vcanvas.mousedown( (e) => {
scope_time_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 16);
mouse_pressed = true;
//if (e.target.setCapture) e.target.setCapture(); // TODO: pointer capture
dirty = true;
this.refreshFrame();
});
vcanvas.mouseup( (e) => {
mouse_pressed = false;
//if (e.target.setCapture) e.target.releaseCapture(); // TODO: pointer capture
dirty = true;
this.refreshFrame();
});
vcanvas.keydown( (e) => {
switch (e.keyCode) {
case 37: scope_time_x--; dirty=true; this.refreshFrame(); break;
case 39: scope_time_x++; dirty=true; this.refreshFrame(); break;
}
});
idata = video.getFrameData();
timerCallback = () => {
if (!this.isRunning())
return;
gen.switches = switches[0];
this.updateFrame();
};
trace_buffer = new Uint32Array(0x10000);
this.setFrameRate(60);
}
setGenInputs() {
useAudio = (audio != null);
//TODO debugCond = this.getDebugCallback();
gen.switches_p1 = switches[0];
gen.switches_p2 = switches[1];
gen.switches_gen = switches[2];
}
updateVideoFrame() {
this.setGenInputs();
var fps = this.getFrameRate();
// darken the previous frame?
if (fps < 45) {
var mask = fps > 5 ? 0xe7ffffff : 0x7fdddddd;
for (var i=0; i<idata.length; i++)
idata[i] &= mask;
}
// paint into frame, synched with vsync if full speed
var sync = fps > 45;
var trace = fps < 0.02;
this.updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace);
//if (trace) displayTraceBuffer();
//this.restartDebugState();
gen.__unreset();
this.refreshVideoFrame();
}
// TODO: merge with prev func
advance(novideo : boolean) {
this.setGenInputs();
this.updateVideoFrameCycles(cyclesPerFrame, true, false);
gen.__unreset();
if (!novideo) {
this.refreshVideoFrame();
}
}
refreshVideoFrame() {
this.updateInspectionFrame();
this.updateAnimateScope();
this.updateInspectionPostFrame();
}
updateFrame() {
if (!gen) return;
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
this.updateVideoFrame();
else
this.updateScopeFrame();
}
refreshFrame() {
if (!gen) return;
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
this.refreshVideoFrame();
else
this.refreshScopeOverlay(trace_ports);
}
updateAnimateScope() {
var fps = this.getFrameRate();
var trace = fps < 0.02;
var ctx = video.getContext();
if (scope_a > 0.01) {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, videoWidth, videoHeight);
var vidyoffset = Math.round(scope_a*(-framey+videoHeight/6));
video.updateFrame(0, vidyoffset, 0, 0, videoWidth, videoHeight);
ctx.fillStyle = "white";
ctx.fillRect(framex, framey+vidyoffset, 1, 1);
scope_index_offset = (trace_index - trace_signals.length*scopeWidth + trace_buffer.length) % trace_buffer.length;
scope_x_offset = 0;
this.refreshScopeOverlay(trace_signals);
} else {
video.updateFrame();
scope_index_offset = 0;
}
// smooth transition
scope_a = scope_a * 0.9 + (trace?1.0:0.0) * 0.1;
scope_y_top = (1 - scope_a*0.7) * videoHeight - (1 - scope_a) * scope_y_offset;
}
updateInspectionFrame() {
useAudio = false;
if (inspect_obj && inspect_sym) {
var COLOR_BIT_OFF = 0xffff6666;
@ -280,7 +460,7 @@ var VerilogPlatform = function(mainElement, options) {
}
}
function updateInspectionPostFrame() {
updateInspectionPostFrame() {
if (inspect_obj && inspect_sym) {
var ctx = video.getContext();
var val = inspect_data[inspect_data.length-1];
@ -291,17 +471,12 @@ var VerilogPlatform = function(mainElement, options) {
}
}
var framex=0;
var framey=0;
var frameidx=0;
var framehsync=false;
var framevsync=false;
function updateVideoFrameCycles(ncycles, sync, trace) {
updateVideoFrameCycles(ncycles, sync, trace) {
ncycles |= 0;
var inspect = inspect_obj && inspect_sym;
while (ncycles--) {
if (trace) snapshotTrace(true);
if (trace)
this.snapshotTrace(true);
vidtick();
if (framex++ < videoWidth) {
if (framey < videoHeight) {
@ -330,80 +505,15 @@ var VerilogPlatform = function(mainElement, options) {
} else {
var wasvsync = framevsync;
framevsync = false;
if (sync && wasvsync) return; // exit when vsync ends
if (sync && wasvsync) {
this.updateRecorder();
return; // exit when vsync ends
}
}
}
}
function updateVideoFrame() {
useAudio = (audio != null);
debugCond = self.getDebugCallback();
gen.switches_p1 = switches[0];
gen.switches_p2 = switches[1];
gen.switches_gen = switches[2];
var fps = self.getFrameRate();
// darken the previous frame?
if (fps < 45) {
var mask = fps > 5 ? 0xe7ffffff : 0x7fdddddd;
for (var i=0; i<idata.length; i++)
idata[i] &= mask;
}
// paint into frame, synched with vsync if full speed
var sync = fps > 45;
var trace = fps < 0.02;
updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace);
//if (trace) displayTraceBuffer();
self.restartDebugState();
gen.__unreset();
refreshVideoFrame();
}
function refreshVideoFrame() {
updateInspectionFrame();
updateAnimateScope();
updateInspectionPostFrame();
}
function updateFrame() {
if (!gen) return;
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
updateVideoFrame();
else
updateScopeFrame();
}
function refreshFrame() {
if (!gen) return;
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
refreshVideoFrame();
else
refreshScopeOverlay(trace_ports);
}
function updateAnimateScope() {
var fps = self.getFrameRate();
var trace = fps < 0.02;
var ctx = video.getContext();
if (scope_a > 0.01) {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, videoWidth, videoHeight);
var vidyoffset = Math.round(scope_a*(-framey+videoHeight/6));
video.updateFrame(0, vidyoffset, 0, 0, videoWidth, videoHeight);
ctx.fillStyle = "white";
ctx.fillRect(framex, framey+vidyoffset, 1, 1);
scope_index_offset = (trace_index - trace_signals.length*scopeWidth + trace_buffer.length) % trace_buffer.length;
scope_x_offset = 0;
refreshScopeOverlay(trace_signals);
} else {
video.updateFrame();
scope_index_offset = 0;
}
// smooth transition
scope_a = scope_a * 0.9 + (trace?1.0:0.0) * 0.1;
scope_y_top = (1 - scope_a*0.7) * videoHeight - (1 - scope_a) * scope_y_offset;
}
function displayTraceBuffer() {
displayTraceBuffer() {
var skip = trace_signals.length;
var src = trace_index;
for (var dest=0; dest<idata.length; dest+=videoWidth) {
@ -415,7 +525,7 @@ var VerilogPlatform = function(mainElement, options) {
}
}
function snapshotTrace(signals) {
snapshotTrace(signals) {
var arr = signals ? trace_signals : trace_ports;
for (var i=0; i<arr.length; i++) {
var v = arr[i];
@ -425,44 +535,26 @@ var VerilogPlatform = function(mainElement, options) {
}
}
function fillTraceBuffer(count) {
fillTraceBuffer(count) {
var max_index = Math.min(trace_buffer.length - trace_ports.length, trace_index + count);
while (trace_index < max_index) {
gen.clk ^= 1;
gen.eval();
snapshotTrace(false);
this.snapshotTrace(false);
dirty = true;
}
gen.__unreset();
}
function shadowText(ctx, txt, x, y) {
ctx.shadowColor = "black";
ctx.shadowBlur = 0;
ctx.shadowOffsetY = -1;
ctx.shadowOffsetX = 0;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 1;
ctx.shadowOffsetX = 0;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 0;
ctx.shadowOffsetX = -1;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 0;
ctx.shadowOffsetX = 1;
ctx.fillText(txt, x, y);
ctx.shadowOffsetX = 0;
}
function updateScopeFrame() {
fillTraceBuffer(Math.floor(videoWidth/4) * trace_ports.length);
updateScopeFrame() {
this.fillTraceBuffer(Math.floor(videoWidth/4) * trace_ports.length);
if (!dirty) return;
dirty = false;
scope_y_top = 0;
refreshScopeOverlay(trace_ports);
this.refreshScopeOverlay(trace_ports);
}
function refreshScopeOverlay(arr) {
refreshScopeOverlay(arr) {
if (!sdata) {
scopeImageData = video.getContext().createImageData(scopeWidth,scopeHeight);
sdata = new Uint32Array(scopeImageData.data.buffer);
@ -540,62 +632,7 @@ var VerilogPlatform = function(mainElement, options) {
}
}
function clamp(minv,maxv,v) {
return (v < minv) ? minv : (v > maxv) ? maxv : v;
}
this.start = function() {
video = new RasterVideo(mainElement,videoWidth,videoHeight);
video.create();
var ctx = video.getContext();
ctx.font = "8px TinyFont";
ctx.fillStyle = "white";
ctx.textAlign = "left";
setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP);
var vcanvas = $(video.canvas);
vcanvas.mousemove(function(e) {
var new_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 20);
var new_y = Math.floor(e.offsetY * video.canvas.height / vcanvas.height() - 20);
if (mouse_pressed) {
scope_y_offset = clamp(Math.min(0,-scope_max_y+videoHeight), 0, scope_y_offset + new_y - paddle_y);
scope_time_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 16);
dirty = true;
refreshFrame();
}
paddle_x = clamp(8, 240, new_x);
paddle_y = clamp(8, 240, new_y);
});
vcanvas.mousedown(function(e) {
scope_time_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 16);
mouse_pressed = true;
//if (e.target.setCapture) e.target.setCapture(); // TODO: pointer capture
dirty = true;
refreshFrame();
});
vcanvas.mouseup(function(e) {
mouse_pressed = false;
//if (e.target.setCapture) e.target.releaseCapture(); // TODO: pointer capture
dirty = true;
refreshFrame();
});
vcanvas.keydown(function(e) {
switch (e.keyCode) {
case 37: scope_time_x--; dirty=true; refreshFrame(); break;
case 39: scope_time_x++; dirty=true; refreshFrame(); break;
}
});
idata = video.getFrameData();
timerCallback = function() {
if (!self.isRunning())
return;
gen.switches = switches[0];
updateFrame();
};
trace_buffer = new Uint32Array(0x10000);
self.setFrameRate(60);
}
this.printErrorCodeContext = function(e, code) {
printErrorCodeContext(e, code) {
if (e.lineNumber && e.message) {
var lines = code.split('\n');
var s = e.message + '\n';
@ -608,7 +645,7 @@ var VerilogPlatform = function(mainElement, options) {
}
}
this.loadROM = function(title, output) {
loadROM(title, output) {
var mod;
if (output.code) {
// is code identical?
@ -621,8 +658,8 @@ var VerilogPlatform = function(mainElement, options) {
throw e;
}
// compile Verilog code
var base = new VerilatorBase();
gen = new mod(base);
var base = new (VerilatorBase as any)();
gen = new mod();
//$.extend(gen, base);
gen.__proto__ = base;
current_output = output;
@ -647,37 +684,35 @@ var VerilogPlatform = function(mainElement, options) {
this.reset();
}
// restart audio
restartAudio();
this.restartAudio();
}
function restartAudio() {
restartAudio() {
// stop/start audio
var hasAudio = gen && gen.spkr !== undefined && frameRate > 1;
if (audio && !hasAudio) {
audio.stop();
audio = null;
} else if (!audio && hasAudio) {
audio = new SampleAudio(cyclesPerFrame * self.getFrameRate());
if (self.isRunning())
audio = new SampleAudio(cyclesPerFrame * this.getFrameRate());
if (this.isRunning())
audio.start();
}
}
this.isRunning = function() {
isRunning() {
return timer && timer.isRunning();
}
this.pause = function() {
pause() {
timer.stop();
if (audio) audio.stop();
}
this.resume = function() {
resume() {
timer.start();
if (audio) audio.start();
}
var frameRate = 0;
this.setFrameRate = function(rateHz) {
setFrameRate(rateHz) {
frameRate = rateHz;
var fps = Math.min(60, rateHz*cyclesPerFrame);
if (!timer || timer.frameRate != fps) {
@ -690,33 +725,33 @@ var VerilogPlatform = function(mainElement, options) {
audio.stop();
audio = null;
}
restartAudio();
}
this.getFrameRate = function() { return frameRate; }
this.restartAudio();
}
getFrameRate() { return frameRate; }
this.poweron = function() {
poweron() {
gen._ctor_var_reset();
this.reset();
}
this.reset = function() {
reset() {
gen.__reset();
trace_index = scope_x_offset = 0;
if (trace_buffer) trace_buffer.fill(0);
dirty = true;
if (video) video.setRotate(gen.rotate ? -90 : 0);
}
this.tick = function() {
tick() {
gen.tick2();
}
this.getToolForFilename = function(fn) {
getToolForFilename(fn) {
if (fn.endsWith("asm"))
return "jsasm";
else
return "verilator";
}
this.getDefaultExtension = function() { return ".v"; };
getDefaultExtension() { return ".v"; };
this.inspect = function(name) {
inspect(name) {
if (!gen) return;
if (name && !name.match(/^\w+$/)) return;
var val = gen[name];
@ -739,114 +774,40 @@ var VerilogPlatform = function(mainElement, options) {
// DEBUGGING
this.saveState = function() {
return {T:gen.ticks(), o:$.extend(true, {}, gen)};
// TODO: bind() a function to avoid depot?
saveState() {
var state = {
T:gen.ticks(),
o:safe_extend(true, {}, gen)
};
state.o.TOPp = null;
return state;
}
this.loadState = function(state) {
gen = $.extend(true, gen, state.o);
loadState(state) {
gen = safe_extend(true, gen, state.o);
gen.setTicks(state.T);
gen.TOPp = gen;
//console.log(gen, state.o);
}
saveControlsState() {
return {
p1x: paddle_x,
p1y: paddle_y,
sw0: switches[0],
sw1: switches[1],
sw2: switches[2],
};
}
loadControlsState(state) {
paddle_x = state.p1x;
paddle_y = state.p1y;
switches[0] = state.sw0;
switches[1] = state.sw1;
switches[2] = state.sw2;
}
var onBreakpointHit;
var debugCondition;
var debugSavedState = null;
var debugBreakState = null;
var debugTargetClock = 0;
this.setDebugCondition = function(debugCond) {
if (debugSavedState) {
this.loadState(debugSavedState);
} else {
debugSavedState = this.saveState();
}
debugCondition = debugCond;
debugBreakState = null;
this.resume();
}
this.restartDebugState = function() {
if (debugCondition && !debugBreakState) {
debugSavedState = this.saveState();
if (debugTargetClock > 0)
debugTargetClock -= debugSavedState.T;
debugSavedState.T = 0;
this.loadState(debugSavedState);
}
}
this.getDebugCallback = function() {
return debugCondition;
}
this.setupDebug = function(callback) {
onBreakpointHit = callback;
}
this.clearDebug = function() {
debugSavedState = null;
debugBreakState = null;
debugTargetClock = 0;
onBreakpointHit = null;
debugCondition = null;
}
this.breakpointHit = function(targetClock) {
debugTargetClock = targetClock;
debugBreakState = this.saveState();
console.log("Breakpoint at clk", debugBreakState.T);
this.pause();
if (onBreakpointHit) {
onBreakpointHit(debugBreakState);
}
}
this.wasBreakpointHit = function() {
return debugBreakState != null;
}
this.step = function() {
var self = this;
this.setDebugCondition(function() {
if (gen.ticks() > debugTargetClock) {
self.breakpointHit(gen.ticks());
return true;
}
return false;
});
}
this.runToVsync = function() {
var self = this;
this.setDebugCondition(function() {
if (gen.vsync && gen.ticks() > debugTargetClock+2000) {
self.breakpointHit(gen.ticks());
return true;
}
return false;
});
}
this.stepBack = function() {
var self = this;
var prevState;
var prevClock;
this.setDebugCondition(function() {
var debugClock = gen.ticks();
if (debugClock >= debugTargetClock && prevState) {
self.loadState(prevState);
self.breakpointHit(prevClock);
return true;
} else if (debugClock >= debugTargetClock-2 && debugClock < debugTargetClock) {
prevState = self.saveState();
prevClock = debugClock;
}
return false;
});
}
this.runEval = function(evalfunc) {
var self = this;
this.setDebugCondition(function() {
if (gen.ticks() > debugTargetClock) {
if (evalfunc(gen)) {
self.breakpointHit(gen.ticks());
return true;
}
}
return false;
});
}
} // end of inner class
return new _VerilogPlatform();
};
////////////////

View File

@ -366,12 +366,17 @@ function _revertFile(e) {
}
function _downloadROMImage(e) {
if (current_output == null) { // TODO
alert("Please fix errors before downloading ROM.");
if (current_output == null) {
alert("Please finish compiling with no errors before downloading ROM.");
return true;
}
var blob = new Blob([current_output], {type: "application/octet-stream"});
saveAs(blob, getCurrentMainFilename()+".rom");
if (current_output.code) { // TODO
var blob = new Blob([current_output.code], {type: "text/plain"});
saveAs(blob, getCurrentMainFilename()+".js");
} else {
var blob = new Blob([current_output], {type: "application/octet-stream"});
saveAs(blob, getCurrentMainFilename()+".rom");
}
}
function _downloadSourceFile(e) {

View File

@ -336,3 +336,27 @@ export function compressLZG(em_module, inBuffer:number[], levelArg?:boolean) : U
em_module._free(outPtr);
return outBuffer;
}
// only does primitives, 1D arrays and no recursion
export function safe_extend(deep, dest, src) {
// TODO: deep ignored
for (var key in src) {
var val = src[key];
var type = typeof(val);
if (val === null || type == 'undefined') {
dest[key] = val;
} else if (type == 'function') {
// ignore function
} else if (type == 'object') {
if (val['slice']) { // array?
dest[key] = val.slice();
} else {
// ignore object
}
} else {
dest[key] = val;
}
}
return dest;
}

View File

@ -65,21 +65,20 @@ function parseDecls(text:string, arr:V2JS_Var[], name:string, bin?:boolean, bout
function buildModule(o : V2JS_Code) : string {
var m = '"use strict";\n';
m += '\tvar self = this;\n';
for (var i=0; i<o.ports.length; i++) {
m += "\tself." + o.ports[i].name + ";\n";
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 + " = self." + sig.name + " = [];\n";
m += "\tvar " + sig.name + " = this." + sig.name + " = [];\n";
} else if (sig.arrdim.length == 2) {
m += "\tvar " + sig.name + " = self." + sig.name + " = [];\n";
m += "\tvar " + sig.name + " = this." + sig.name + " = [];\n";
m += "\tfor(var i=0; i<" + sig.arrdim[0] + "; i++) { " + sig.name + "[i] = []; }\n";
}
} else {
m += "\tself." + sig.name + ";\n";
m += "\tthis." + sig.name + ";\n";
}
}
for (var i=0; i<o.funcs.length; i++) {
@ -121,16 +120,16 @@ function translateFunction(text : string) : string {
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, 'self.$1'); // TODO?
//text = text.replace(/^\s*(\w+ = \d+;)/gm, 'self.$1'); // TODO?
//text = text.replace(/(\w+\[\w+\] = VL_RAND_RESET_I)/g, 'self.$1');
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, '!!');
//[%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 + "\nself." + funcname + " = " + funcname + ";\n";
return "function " + text + "\nthis." + funcname + " = " + funcname + ";\n";
}
function translateStaticVars(text : string) : string {

View File

@ -456,7 +456,6 @@ var re_crlf = /\r?\n/;
function parseListing(code, lineMatch, iline, ioffset, iinsns) {
var lines = [];
origin |= 0;
for (var line of code.split(re_crlf)) {
var linem = lineMatch.exec(line);
if (linem && linem[1]) {
@ -466,7 +465,7 @@ function parseListing(code, lineMatch, iline, ioffset, iinsns) {
if (insns) {
lines.push({
line:linenum,
offset:offset + origin,
offset:offset,
insns:insns,
});
}
@ -475,10 +474,9 @@ function parseListing(code, lineMatch, iline, ioffset, iinsns) {
return lines;
}
function parseSourceLines(code, lineMatch, offsetMatch, origin) {
function parseSourceLines(code, lineMatch, offsetMatch) {
var lines = [];
var lastlinenum = 0;
origin |= 0;
for (var line of code.split(re_crlf)) {
var linem = lineMatch.exec(line);
if (linem && linem[1]) {
@ -489,7 +487,7 @@ function parseSourceLines(code, lineMatch, offsetMatch, origin) {
var offset = parseInt(linem[1], 16);
lines.push({
line:lastlinenum,
offset:offset + origin,
offset:offset,
});
lastlinenum = 0;
}

View File

@ -15,10 +15,17 @@ function loadPlatform(msg) {
//console.log(msg.output.ports);
//console.log(msg.output.signals);
platform.loadROM("ROM", msg.output);
vl_finished = vl_stopped = false;
for (var i=0; i<10000 && !(vl_finished||vl_stopped); i++)
platform.loadROM("ROM", msg.output);
platform.loadROM("ROM", msg.output);
verilog.vl_finished = verilog.vl_stopped = false;
for (var i=0; i<10000 && !(verilog.vl_finished||verilog.vl_stopped); i++) {
platform.tick();
assert.ok(!vl_stopped);
}
assert.ok(!verilog.vl_stopped);
var state = platform.saveState();
platform.reset();
platform.loadState(state);
assert.deepEqual(state, platform.saveState());
} catch (e) {
//platform.printErrorCodeContext(e, msg.output.code);
console.log(msg.intermediate.listing);
@ -29,7 +36,28 @@ function loadPlatform(msg) {
return platform;
}
function compileVerilator(code, callback, nerrors) {
function testPerf(msg) {
var platform = new VerilogPlatform();
platform.loadROM("ROM", msg.output);
var niters = 2000000;
console.time("before");
for (var i=0; i<niters; i++)
platform.tick();
console.timeEnd("before");
var state = platform.saveState();
platform.reset();
platform.loadState(state);
console.time("after");
for (var i=0; i<niters; i++)
platform.tick();
console.timeEnd("after");
return platform;
}
function compileVerilator(filename, code, callback, nerrors) {
global.postMessage = function(msg) {
if (msg.errors && msg.errors.length) {
console.log(msg.errors);
@ -37,6 +65,10 @@ function compileVerilator(code, callback, nerrors) {
} else {
assert.equal(nerrors||0, 0, "errors");
loadPlatform(msg);
//testPerf(msg);
if (filename.indexOf('t_') >= 0) {
//assert.ok(verilog.vl_finished);
}
}
callback(null, msg);
};
@ -50,7 +82,7 @@ function testVerilator(filename, disables, nerrors) {
var csource = ab2str(fs.readFileSync(filename));
for (var i=0; i<(disables||[]).length; i++)
csource = "/* verilator lint_off " + disables[i] + " */\n" + csource;
compileVerilator(csource, done, nerrors||0);
compileVerilator(filename, csource, done, nerrors||0);
});
}