2017-11-11 19:45:32 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var VERILOG_PRESETS = [
|
2017-11-15 00:12:52 +00:00
|
|
|
{id:'clock_divider.v', name:'Clock Divider'},
|
|
|
|
{id:'hvsync_generator.v', name:'Video Sync Generator'},
|
|
|
|
{id:'test_hvsync.v', name:'Test Pattern'},
|
2018-02-03 20:20:56 +00:00
|
|
|
{id:'7segment.v', name:'7-Segment Decoder'},
|
2018-02-21 19:32:11 +00:00
|
|
|
{id:'digits10.v', name:'Bitmapped Digits'},
|
2018-02-07 00:07:40 +00:00
|
|
|
{id:'scoreboard.v', name:'Scoreboard'},
|
2018-06-01 17:33:37 +00:00
|
|
|
{id:'ball_absolute.v', name:'Ball Motion (absolute position)'},
|
2017-11-18 00:40:44 +00:00
|
|
|
{id:'ball_slip_counter.v', name:'Ball Motion (slipping counter)'},
|
2018-06-04 02:01:40 +00:00
|
|
|
{id:'paddles.v', name:'Paddle Inputs'},
|
2017-11-18 00:40:44 +00:00
|
|
|
{id:'ball_paddle.v', name:'Brick Smash Game'},
|
2018-02-07 00:07:40 +00:00
|
|
|
{id:'ram1.v', name:'RAM Text Display'},
|
2017-11-21 21:36:38 +00:00
|
|
|
{id:'sprite_bitmap.v', name:'Sprite Bitmaps'},
|
|
|
|
{id:'sprite_renderer.v', name:'Sprite Rendering'},
|
2018-06-04 02:01:40 +00:00
|
|
|
{id:'racing_game.v', name:'Racing Game'},
|
2018-02-03 20:20:56 +00:00
|
|
|
{id:'sprite_rotation.v', name:'Sprite Rotation'},
|
|
|
|
{id:'tank.v', name:'Tank Game'},
|
2018-02-21 19:32:11 +00:00
|
|
|
{id:'sound_generator.v', name:'Sound Generator'},
|
2018-02-14 01:04:52 +00:00
|
|
|
{id:'lfsr.v', name:'Linear Feedback Shift Register'},
|
|
|
|
{id:'starfield.v', name:'Scrolling Starfield'},
|
2018-02-21 19:32:11 +00:00
|
|
|
{id:'cpu8.v', name:'Simple 8-Bit CPU'},
|
|
|
|
{id:'racing_game_cpu.v', name:'Racing Game with CPU'},
|
2018-02-15 18:31:32 +00:00
|
|
|
{id:'framebuffer.v', name:'Frame Buffer'},
|
2018-02-27 04:48:36 +00:00
|
|
|
{id:'tile_renderer.v', name:'Tile Renderer'},
|
2018-02-21 19:32:11 +00:00
|
|
|
{id:'sprite_scanline_renderer.v', name:'Sprite Scanline Renderer'},
|
2018-02-28 03:35:42 +00:00
|
|
|
{id:'cpu16.v', name:'16-Bit CPU'},
|
2018-02-28 17:09:29 +00:00
|
|
|
{id:'cpu_platform.v', name:'CPU Platform'},
|
2018-06-11 17:01:09 +00:00
|
|
|
{id:'test2.asm', name:'16-bit ASM Game'},
|
2017-11-11 19:45:32 +00:00
|
|
|
];
|
|
|
|
|
2017-11-15 00:12:52 +00:00
|
|
|
var VERILOG_KEYCODE_MAP = makeKeycodeMap([
|
|
|
|
[Keys.VK_LEFT, 0, 0x1],
|
|
|
|
[Keys.VK_RIGHT, 0, 0x2],
|
|
|
|
[Keys.VK_UP, 0, 0x4],
|
|
|
|
[Keys.VK_DOWN, 0, 0x8],
|
|
|
|
[Keys.VK_SPACE, 0, 0x10],
|
|
|
|
[Keys.VK_SHIFT, 0, 0x20],
|
2017-12-04 21:40:10 +00:00
|
|
|
[Keys.VK_A, 1, 0x1],
|
|
|
|
[Keys.VK_D, 1, 0x2],
|
|
|
|
[Keys.VK_W, 1, 0x4],
|
|
|
|
[Keys.VK_S, 1, 0x8],
|
|
|
|
[Keys.VK_Z, 1, 0x10],
|
|
|
|
[Keys.VK_X, 1, 0x20],
|
|
|
|
[Keys.VK_1, 2, 0x1],
|
|
|
|
[Keys.VK_2, 2, 0x2],
|
|
|
|
[Keys.VK_5, 2, 0x4],
|
|
|
|
[Keys.VK_6, 2, 0x8],
|
|
|
|
[Keys.VK_7, 2, 0x10],
|
2017-11-15 00:12:52 +00:00
|
|
|
]);
|
|
|
|
|
2017-11-22 14:42:07 +00:00
|
|
|
var vl_finished = false;
|
|
|
|
var vl_stopped = false;
|
|
|
|
|
|
|
|
var VL_UL = function(x) { return x|0; }
|
|
|
|
var VL_ULL = function(x) { return x|0; }
|
|
|
|
var VL_TIME_Q = function() { return (new Date().getTime())|0; }
|
2017-11-17 21:01:07 +00:00
|
|
|
|
|
|
|
/// Return true if data[bit] set
|
|
|
|
var VL_BITISSET_I = this.VL_BITISSET_I = function(data,bit) { return (data & (VL_UL(1)<<VL_BITBIT_I(bit))); }
|
|
|
|
|
|
|
|
var VL_EXTENDSIGN_I = this.VL_EXTENDSIGN_I = function(lbits, lhs) { return (-((lhs)&(VL_UL(1)<<(lbits-1)))); }
|
|
|
|
|
|
|
|
var VL_EXTEND_II = this.VL_EXTEND_II = function(obits,lbits,lhs) { return lhs; }
|
|
|
|
|
|
|
|
var VL_EXTENDS_II = this.VL_EXTENDS_II = function(x,lbits,lhs) {
|
|
|
|
return VL_EXTENDSIGN_I(lbits,lhs) | lhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
var VL_EXTEND_II = this.VL_EXTEND_II = function(obits,lbits,lhs) { return lhs; }
|
|
|
|
|
|
|
|
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) {
|
2018-02-14 01:04:52 +00:00
|
|
|
return 0 | (VL_EXTENDS_II(x,lbits,lhs) < VL_EXTENDS_II(x,lbits,rhs)); }
|
2017-11-17 21:01:07 +00:00
|
|
|
|
|
|
|
var VL_GTS_III = this.VL_GTS_III = function(x,lbits,y,lhs,rhs) {
|
2018-02-14 01:04:52 +00:00
|
|
|
return 0 | (VL_EXTENDS_II(x,lbits,lhs) > VL_EXTENDS_II(x,lbits,rhs)); }
|
2017-11-17 21:01:07 +00:00
|
|
|
|
|
|
|
var VL_LTES_III = this.VL_LTES_III = function(x,lbits,y,lhs,rhs) {
|
2018-02-14 01:04:52 +00:00
|
|
|
return 0 | (VL_EXTENDS_II(x,lbits,lhs) <= VL_EXTENDS_II(x,lbits,rhs)); }
|
2017-11-17 21:01:07 +00:00
|
|
|
|
|
|
|
var VL_GTES_III = this.VL_GTES_III = function(x,lbits,y,lhs,rhs) {
|
2018-02-14 01:04:52 +00:00
|
|
|
return 0 | (VL_EXTENDS_II(x,lbits,lhs) >= VL_EXTENDS_II(x,lbits,rhs)); }
|
2017-11-17 21:01:07 +00:00
|
|
|
|
2017-11-22 14:42:07 +00:00
|
|
|
var VL_MODDIV_III = this.VL_MODDIV_III = function(lbits,lhs,rhs) {
|
|
|
|
return (((rhs)==0)?0:(lhs)%(rhs)); }
|
2017-11-17 21:01:07 +00:00
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
var VL_MODDIVS_III = this.VL_MODDIVS_III = function(lbits,lhs,rhs) {
|
|
|
|
return (((rhs)==0)?0:(lhs)%(rhs)); }
|
|
|
|
|
2017-12-04 21:40:10 +00:00
|
|
|
var VL_REDXOR_32 = this.VL_REDXOR_32 = function(r) {
|
|
|
|
r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); r=(r^(r>>16));
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2017-11-22 14:42:07 +00:00
|
|
|
var VL_WRITEF = this.VL_WRITEF = console.log; // TODO: $write
|
|
|
|
|
|
|
|
var vl_finish = this.vl_finish = function(filename,lineno,hier) {
|
2017-11-22 23:23:00 +00:00
|
|
|
console.log("Finished at " + filename + ":" + lineno, hier);
|
2017-11-22 14:42:07 +00:00
|
|
|
vl_finished = true;
|
|
|
|
}
|
|
|
|
var vl_stop = this.vl_stop = function(filename,lineno,hier) {
|
2017-11-22 23:23:00 +00:00
|
|
|
console.log("Stopped at " + filename + ":" + lineno, hier);
|
2017-11-22 14:42:07 +00:00
|
|
|
vl_stopped = true;
|
|
|
|
}
|
2017-11-17 21:01:07 +00:00
|
|
|
|
2018-02-14 01:04:52 +00:00
|
|
|
var VL_RAND_RESET_I = this.VL_RAND_RESET_I = function(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
|
2017-11-17 21:01:07 +00:00
|
|
|
|
2018-02-21 19:32:11 +00:00
|
|
|
var VL_RANDOM_I = this.VL_RANDOM_I = function(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
|
|
|
|
|
2017-11-22 14:42:07 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
function VerilatorBase() {
|
|
|
|
|
2017-11-17 21:01:07 +00:00
|
|
|
//
|
2017-11-11 19:45:32 +00:00
|
|
|
|
2017-11-24 19:14:22 +00:00
|
|
|
var totalTicks = 0;
|
|
|
|
|
2017-11-13 05:24:19 +00:00
|
|
|
function vl_fatal(msg) {
|
|
|
|
console.log(msg);
|
|
|
|
}
|
|
|
|
|
2017-11-24 19:14:22 +00:00
|
|
|
this.ticks = function() { return totalTicks; }
|
|
|
|
this.setTicks = function(T) { totalTicks = T|0; }
|
|
|
|
|
2017-11-28 02:08:19 +00:00
|
|
|
this.__reset = function() {
|
2017-11-17 21:01:07 +00:00
|
|
|
if (this.reset !== undefined) {
|
2017-11-24 19:14:22 +00:00
|
|
|
totalTicks = 0;
|
2017-11-18 19:51:25 +00:00
|
|
|
this.reset = 0;
|
|
|
|
this.tick2();
|
2017-11-13 05:24:19 +00:00
|
|
|
this.reset = 1;
|
2017-11-28 02:08:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.__unreset = function() {
|
|
|
|
if (this.reset !== undefined) {
|
2017-11-13 05:24:19 +00:00
|
|
|
this.reset = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.tick2 = function() {
|
|
|
|
this.clk = 0;
|
|
|
|
this.eval();
|
|
|
|
this.clk = 1;
|
2017-11-11 19:45:32 +00:00
|
|
|
this.eval();
|
|
|
|
}
|
|
|
|
|
2018-02-14 01:04:52 +00:00
|
|
|
var vlSymsp = this; //{TOPp:this};
|
2017-11-11 19:45:32 +00:00
|
|
|
var maxVclockLoop = 1;
|
|
|
|
|
|
|
|
this.eval = function() {
|
|
|
|
// Initialize
|
|
|
|
if (!vlSymsp.__Vm_didInit)
|
|
|
|
this._eval_initial_loop(vlSymsp);
|
|
|
|
// Evaluate till stable
|
|
|
|
//VL_DEBUG_IF(VL_PRINTF("\n----TOP Evaluate Vmain::eval\n"); );
|
|
|
|
var __VclockLoop = 0;
|
|
|
|
var __Vchange=1;
|
|
|
|
while (__Vchange) {
|
|
|
|
//VL_DEBUG_IF(VL_PRINTF(" Clock loop\n"););
|
|
|
|
vlSymsp.__Vm_activity = true;
|
|
|
|
this._eval(vlSymsp);
|
|
|
|
__Vchange = this._change_request(vlSymsp);
|
2017-11-13 05:24:19 +00:00
|
|
|
if (++__VclockLoop > 100) { vl_fatal("Verilated model didn't converge"); }
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
if (__VclockLoop > maxVclockLoop) {
|
|
|
|
maxVclockLoop = __VclockLoop;
|
|
|
|
console.log("Graph took " + maxVclockLoop + " iterations to stabilize");
|
|
|
|
}
|
2017-11-24 19:14:22 +00:00
|
|
|
totalTicks++;
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this._eval_initial_loop = function(vlSymsp) {
|
2018-02-14 01:04:52 +00:00
|
|
|
vlSymsp.TOPp = this;
|
2017-11-11 19:45:32 +00:00
|
|
|
vlSymsp.__Vm_didInit = true;
|
|
|
|
this._eval_initial(vlSymsp);
|
|
|
|
vlSymsp.__Vm_activity = true;
|
|
|
|
var __VclockLoop = 0;
|
|
|
|
var __Vchange=1;
|
|
|
|
while (__Vchange) {
|
|
|
|
this._eval_settle(vlSymsp);
|
|
|
|
this._eval(vlSymsp);
|
|
|
|
__Vchange = this._change_request(vlSymsp);
|
2017-11-13 05:24:19 +00:00
|
|
|
if (++__VclockLoop > 100) { vl_fatal("Verilated model didn't DC converge"); }
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var VerilogPlatform = function(mainElement, options) {
|
|
|
|
var self = this;
|
2017-11-13 05:24:19 +00:00
|
|
|
var video, audio;
|
2018-02-14 01:04:52 +00:00
|
|
|
var useAudio = false;
|
2018-02-27 04:48:36 +00:00
|
|
|
var videoWidth = 292;
|
|
|
|
var videoHeight = 256;
|
2018-02-21 19:32:11 +00:00
|
|
|
var maxVideoLines = 262+40; // vertical hold
|
2018-02-26 23:18:23 +00:00
|
|
|
var idata, timer, timerCallback;
|
2017-11-11 19:45:32 +00:00
|
|
|
var gen;
|
2018-02-26 23:18:23 +00:00
|
|
|
var cyclesPerFrame = (256+23+7+23)*262; // 4857480/60 Hz
|
2017-11-14 14:33:15 +00:00
|
|
|
var current_output;
|
2017-11-15 00:12:52 +00:00
|
|
|
var paddle_x = 0;
|
|
|
|
var paddle_y = 0;
|
2017-12-04 21:40:10 +00:00
|
|
|
var switches = [0,0,0];
|
2017-11-21 16:15:08 +00:00
|
|
|
var inspect_obj, inspect_sym;
|
|
|
|
var inspect_data = new Uint32Array(videoWidth * videoHeight);
|
2017-11-29 01:48:27 +00:00
|
|
|
var scope_time_x = 0; // scope cursor
|
|
|
|
var scope_x_offset = 0;
|
2017-11-28 02:08:19 +00:00
|
|
|
var scope_y_offset = 0;
|
2018-02-26 23:18:23 +00:00
|
|
|
var scope_index_offset = 0;
|
2017-11-28 02:08:19 +00:00
|
|
|
var scope_max_y = 0;
|
2018-02-26 23:18:23 +00:00
|
|
|
var scope_y_top = 0;
|
2018-02-27 04:48:36 +00:00
|
|
|
var scope_a = 0; // used for transitions
|
2018-02-26 23:18:23 +00:00
|
|
|
var scopeWidth = videoWidth;
|
|
|
|
var scopeHeight = videoHeight;
|
|
|
|
var scopeImageData;
|
|
|
|
var sdata; // scope data
|
2018-02-28 03:35:42 +00:00
|
|
|
var module_name;
|
2017-11-28 02:08:19 +00:00
|
|
|
|
|
|
|
var yposlist = [];
|
|
|
|
var lasty = [];
|
|
|
|
var lastval = [];
|
2018-02-26 23:18:23 +00:00
|
|
|
var trace_ports;
|
|
|
|
var trace_signals;
|
2017-11-28 02:08:19 +00:00
|
|
|
var trace_buffer;
|
|
|
|
var trace_index;
|
|
|
|
var mouse_pressed;
|
|
|
|
var dirty = false;
|
2017-11-11 19:45:32 +00:00
|
|
|
|
|
|
|
this.getPresets = function() { return VERILOG_PRESETS; }
|
|
|
|
|
2017-11-13 05:24:19 +00:00
|
|
|
var RGBLOOKUP = [
|
2017-11-21 16:15:08 +00:00
|
|
|
0xff222222,
|
|
|
|
0xff2222ff,
|
|
|
|
0xff22ff22,
|
|
|
|
0xff22ffff,
|
|
|
|
0xffff2222,
|
|
|
|
0xffff22ff,
|
|
|
|
0xffffff22,
|
2017-11-13 05:24:19 +00:00
|
|
|
0xffffffff,
|
2018-02-21 19:32:11 +00:00
|
|
|
0xff999999,
|
|
|
|
0xff9999ff,
|
|
|
|
0xff99ff99,
|
|
|
|
0xff99ffff,
|
|
|
|
0xffff9999,
|
|
|
|
0xffff99ff,
|
|
|
|
0xffffff99,
|
|
|
|
0xff666666,
|
2017-11-13 05:24:19 +00:00
|
|
|
];
|
|
|
|
|
2017-11-24 19:14:22 +00:00
|
|
|
var debugCond;
|
|
|
|
|
2017-11-15 00:12:52 +00:00
|
|
|
function vidtick() {
|
|
|
|
gen.tick2();
|
2018-02-14 01:04:52 +00:00
|
|
|
if (useAudio)
|
|
|
|
audio.feedSample(gen.spkr*(1.0/255.0), 1);
|
|
|
|
if (debugCond && debugCond())
|
|
|
|
debugCond = null;
|
2017-11-15 00:12:52 +00:00
|
|
|
}
|
|
|
|
|
2017-11-21 16:15:08 +00:00
|
|
|
function updateInspectionFrame() {
|
2018-02-14 01:04:52 +00:00
|
|
|
useAudio = false;
|
2017-11-21 16:15:08 +00:00
|
|
|
if (inspect_obj && inspect_sym) {
|
2018-02-21 19:32:11 +00:00
|
|
|
var COLOR_BIT_OFF = 0xffff6666;
|
|
|
|
var COLOR_BIT_ON = 0xffff9999;
|
2018-02-26 23:18:23 +00:00
|
|
|
var i = videoWidth;
|
|
|
|
for (var y=0; y<videoHeight-2; y++) {
|
2018-02-19 00:19:20 +00:00
|
|
|
for (var x=0; x<videoWidth; x++) {
|
|
|
|
var val = inspect_data[i];
|
|
|
|
idata[i++] = (val & 1) ? COLOR_BIT_ON : COLOR_BIT_OFF;
|
|
|
|
}
|
|
|
|
}
|
2017-11-21 16:15:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateInspectionPostFrame() {
|
|
|
|
if (inspect_obj && inspect_sym) {
|
|
|
|
var ctx = video.getContext();
|
|
|
|
var val = inspect_data[inspect_data.length-1];
|
|
|
|
ctx.fillStyle = "black";
|
|
|
|
ctx.fillRect(18, videoHeight-8, 30, 8);
|
|
|
|
ctx.fillStyle = "white";
|
|
|
|
ctx.fillText(val.toString(10), 20, videoHeight-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-27 04:48:36 +00:00
|
|
|
var framex=0;
|
2018-02-26 23:18:23 +00:00
|
|
|
var framey=0;
|
2018-02-27 04:48:36 +00:00
|
|
|
var frameidx=0;
|
2018-02-26 23:18:23 +00:00
|
|
|
var framehsync=false;
|
|
|
|
var framevsync=false;
|
|
|
|
|
|
|
|
function updateVideoFrameCycles(ncycles, sync, trace) {
|
|
|
|
ncycles |= 0;
|
|
|
|
var inspect = inspect_obj && inspect_sym;
|
|
|
|
while (ncycles--) {
|
|
|
|
if (trace) snapshotTrace(true);
|
|
|
|
vidtick();
|
|
|
|
if (framex++ < videoWidth) {
|
|
|
|
if (framey < videoHeight) {
|
|
|
|
if (inspect) {
|
|
|
|
inspect_data[frameidx] = inspect_obj[inspect_sym];
|
|
|
|
}
|
|
|
|
idata[frameidx] = RGBLOOKUP[gen.rgb & 15];
|
|
|
|
frameidx++;
|
|
|
|
}
|
|
|
|
} else if (!framehsync && gen.hsync) {
|
|
|
|
framehsync = true;
|
|
|
|
} else if ((framehsync && !gen.hsync) || framex > videoWidth*2) {
|
|
|
|
framehsync = false;
|
|
|
|
framex = 0;
|
|
|
|
framey++;
|
|
|
|
gen.hpaddle = framey > paddle_x ? 1 : 0;
|
|
|
|
gen.vpaddle = framey > paddle_y ? 1 : 0;
|
|
|
|
}
|
|
|
|
if (framey > maxVideoLines || gen.vsync) {
|
|
|
|
framevsync = true;
|
|
|
|
framey = 0;
|
2018-02-27 04:48:36 +00:00
|
|
|
framex = 0;
|
|
|
|
frameidx = 0;
|
2018-07-15 04:29:27 +00:00
|
|
|
gen.hpaddle = 0;
|
|
|
|
gen.vpaddle = 0;
|
2018-02-26 23:18:23 +00:00
|
|
|
} else {
|
2018-02-27 04:48:36 +00:00
|
|
|
var wasvsync = framevsync;
|
2018-02-26 23:18:23 +00:00
|
|
|
framevsync = false;
|
2018-02-27 04:48:36 +00:00
|
|
|
if (sync && wasvsync) return; // exit when vsync ends
|
2018-02-26 23:18:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-14 14:33:15 +00:00
|
|
|
function updateVideoFrame() {
|
2018-02-21 18:54:53 +00:00
|
|
|
useAudio = (audio != null);
|
2017-11-24 19:14:22 +00:00
|
|
|
debugCond = self.getDebugCallback();
|
2018-02-05 20:51:20 +00:00
|
|
|
gen.switches_p1 = switches[0];
|
|
|
|
gen.switches_p2 = switches[1];
|
|
|
|
gen.switches_gen = switches[2];
|
2018-02-26 23:18:23 +00:00
|
|
|
var fps = self.getFrameRate();
|
|
|
|
// darken the previous frame?
|
|
|
|
if (fps < 45) {
|
2018-02-28 17:09:29 +00:00
|
|
|
var mask = fps > 5 ? 0xe7ffffff : 0x7fdddddd;
|
2018-02-26 23:18:23 +00:00
|
|
|
for (var i=0; i<idata.length; i++)
|
|
|
|
idata[i] &= mask;
|
2017-11-14 14:33:15 +00:00
|
|
|
}
|
2018-02-26 23:18:23 +00:00
|
|
|
// paint into frame, synched with vsync if full speed
|
|
|
|
var sync = fps > 45;
|
2018-02-27 04:48:36 +00:00
|
|
|
var trace = fps < 0.02;
|
2018-02-26 23:18:23 +00:00
|
|
|
updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace);
|
|
|
|
//if (trace) displayTraceBuffer();
|
2017-11-21 16:15:08 +00:00
|
|
|
updateInspectionFrame();
|
2018-07-13 19:39:08 +00:00
|
|
|
updateAnimateScope();
|
2018-02-28 17:09:29 +00:00
|
|
|
updateInspectionPostFrame();
|
|
|
|
self.restartDebugState();
|
|
|
|
gen.__unreset();
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateAnimateScope() {
|
|
|
|
var fps = self.getFrameRate();
|
|
|
|
var trace = fps < 0.02;
|
2018-07-13 19:39:08 +00:00
|
|
|
var ctx = video.getContext();
|
2018-02-27 04:48:36 +00:00
|
|
|
if (scope_a > 0.01) {
|
2018-07-13 19:39:08 +00:00
|
|
|
ctx.fillStyle = "black";
|
|
|
|
ctx.fillRect(0, 0, videoWidth, videoHeight);
|
2018-02-27 04:48:36 +00:00
|
|
|
var vidyoffset = Math.round(scope_a*(-framey+videoHeight/6));
|
|
|
|
video.updateFrame(0, vidyoffset, 0, 0, videoWidth, videoHeight);
|
2018-07-13 19:39:08 +00:00
|
|
|
ctx.fillStyle = "white";
|
|
|
|
ctx.fillRect(framex, framey+vidyoffset, 1, 1);
|
2018-02-26 23:18:23 +00:00
|
|
|
scope_index_offset = (trace_index - trace_signals.length*scopeWidth + trace_buffer.length) % trace_buffer.length;
|
|
|
|
scope_x_offset = 0;
|
|
|
|
updateScopeOverlay(trace_signals);
|
|
|
|
} else {
|
2018-07-14 19:58:16 +00:00
|
|
|
video.updateFrame();
|
2018-02-26 23:18:23 +00:00
|
|
|
scope_index_offset = 0;
|
|
|
|
}
|
2018-02-27 04:48:36 +00:00
|
|
|
// 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;
|
2017-11-14 14:33:15 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 23:18:23 +00:00
|
|
|
function displayTraceBuffer() {
|
|
|
|
var skip = trace_signals.length;
|
|
|
|
var src = trace_index;
|
|
|
|
for (var dest=0; dest<idata.length; dest+=videoWidth) {
|
|
|
|
for (var i=0; i<skip; i++) {
|
|
|
|
if (--src < 0) src = trace_buffer.length-1;
|
|
|
|
var v = trace_buffer[src];
|
|
|
|
idata[dest+i] = RGBLOOKUP[v & 15]; // TODO?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function snapshotTrace(signals) {
|
|
|
|
var arr = signals ? trace_signals : trace_ports;
|
|
|
|
for (var i=0; i<arr.length; i++) {
|
|
|
|
var v = arr[i];
|
|
|
|
var z = gen[v.name];
|
|
|
|
trace_buffer[trace_index++] = z;
|
|
|
|
if (trace_index >= trace_buffer.length) trace_index = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-28 02:08:19 +00:00
|
|
|
function fillTraceBuffer(count) {
|
2018-02-26 23:18:23 +00:00
|
|
|
var max_index = Math.min(trace_buffer.length - trace_ports.length, trace_index + count);
|
2017-11-28 02:08:19 +00:00
|
|
|
while (trace_index < max_index) {
|
|
|
|
gen.clk ^= 1;
|
|
|
|
gen.eval();
|
2018-02-26 23:18:23 +00:00
|
|
|
snapshotTrace(false);
|
2017-11-28 02:08:19 +00:00
|
|
|
dirty = true;
|
|
|
|
}
|
|
|
|
gen.__unreset();
|
|
|
|
}
|
2017-11-14 14:33:15 +00:00
|
|
|
|
2018-02-09 22:23:14 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-14 14:33:15 +00:00
|
|
|
function updateScopeFrame() {
|
2018-02-26 23:18:23 +00:00
|
|
|
fillTraceBuffer(Math.floor(videoWidth/4) * trace_ports.length);
|
2017-11-28 02:08:19 +00:00
|
|
|
if (!dirty) return;
|
|
|
|
dirty = false;
|
2018-02-26 23:18:23 +00:00
|
|
|
scope_y_top = 0;
|
|
|
|
updateScopeOverlay(trace_ports);
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateScopeOverlay(arr) {
|
|
|
|
if (!sdata) {
|
|
|
|
scopeImageData = video.getContext().createImageData(scopeWidth,scopeHeight);
|
|
|
|
sdata = new Uint32Array(scopeImageData.data.buffer);
|
|
|
|
}
|
2018-02-14 01:04:52 +00:00
|
|
|
var COLOR_BLACK = 0xff000000;
|
2017-11-21 16:15:08 +00:00
|
|
|
var COLOR_SIGNAL = 0xff22ff22;
|
|
|
|
var COLOR_BORDER = 0xff662222;
|
|
|
|
var COLOR_TRANS_SIGNAL = 0xff226622;
|
2017-11-28 02:08:19 +00:00
|
|
|
var COLOR_BLIP_SIGNAL = 0xff226622;
|
2018-02-26 23:18:23 +00:00
|
|
|
sdata.fill(0xff000000);
|
|
|
|
var jstart = scope_x_offset * arr.length + scope_index_offset;
|
2017-11-29 01:48:27 +00:00
|
|
|
var j = jstart;
|
2018-02-26 23:18:23 +00:00
|
|
|
for (var x=0; x<scopeWidth; x++) {
|
2017-11-14 14:33:15 +00:00
|
|
|
var yb = 8;
|
2017-11-28 02:08:19 +00:00
|
|
|
var y1 = scope_y_offset;
|
2017-11-14 14:33:15 +00:00
|
|
|
for (var i=0; i<arr.length; i++) {
|
|
|
|
var v = arr[i];
|
|
|
|
var lo = 0; // TODO? v.ofs?
|
2018-02-26 23:18:23 +00:00
|
|
|
var hi = ((1 << v.len)-1);
|
2017-11-14 14:33:15 +00:00
|
|
|
var ys = hi>1 ? v.len*2+8 : 8;
|
|
|
|
var y2 = y1+ys;
|
2017-11-28 02:08:19 +00:00
|
|
|
var z = trace_buffer[j++];
|
2018-02-26 23:18:23 +00:00
|
|
|
if (j >= trace_buffer.length) j = 0;
|
2017-11-15 00:12:52 +00:00
|
|
|
var y = Math.round(y2 - ys*((z-lo)/hi));
|
2018-02-26 23:18:23 +00:00
|
|
|
yposlist[i] = y2 + scope_y_top;
|
2017-11-15 00:12:52 +00:00
|
|
|
var ly = lasty[i];
|
|
|
|
if (x > 0 && ly != y) {
|
|
|
|
var dir = ly < y ? 1 : -1;
|
|
|
|
while ((ly += dir) != y && ly >= y1 && ly <= y2) {
|
2018-02-26 23:18:23 +00:00
|
|
|
sdata[x + ly * scopeWidth] = COLOR_TRANS_SIGNAL;
|
2017-11-15 00:12:52 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-26 23:18:23 +00:00
|
|
|
sdata[x + y * scopeWidth] = lastval[i]==z ? COLOR_SIGNAL : COLOR_BLIP_SIGNAL;
|
2017-11-15 00:12:52 +00:00
|
|
|
lasty[i] = y;
|
2017-11-28 02:08:19 +00:00
|
|
|
lastval[i] = z;
|
2017-11-14 14:33:15 +00:00
|
|
|
y1 += ys+yb;
|
|
|
|
}
|
|
|
|
}
|
2017-11-29 01:48:27 +00:00
|
|
|
scope_max_y = y1 - scope_y_offset;
|
2018-02-26 23:18:23 +00:00
|
|
|
video.getContext().putImageData(scopeImageData, 0, scope_y_top);
|
2017-11-14 14:33:15 +00:00
|
|
|
// draw labels
|
|
|
|
var ctx = video.getContext();
|
|
|
|
for (var i=0; i<arr.length; i++) {
|
2018-02-26 23:18:23 +00:00
|
|
|
var yp = yposlist[i];
|
|
|
|
if (yp < 20 || yp > videoHeight) continue;
|
2017-11-14 14:33:15 +00:00
|
|
|
var v = arr[i];
|
2017-11-28 02:08:19 +00:00
|
|
|
var name = v.name;
|
|
|
|
ctx.fillStyle = name == inspect_sym ? "yellow" : "white";
|
|
|
|
name = name.replace(/__DOT__/g,'.');
|
2018-02-28 03:35:42 +00:00
|
|
|
name = name.replace(module_name+'.','');
|
2017-11-28 02:08:19 +00:00
|
|
|
ctx.textAlign = 'left';
|
2018-02-09 22:23:14 +00:00
|
|
|
ctx.fillStyle = "white";
|
|
|
|
shadowText(ctx, name, 1, yposlist[i]);
|
2017-11-28 02:08:19 +00:00
|
|
|
if (scope_time_x > 0) {
|
|
|
|
ctx.textAlign = 'right';
|
2018-02-26 23:18:23 +00:00
|
|
|
var value = (arr.length * scope_time_x + i + jstart) % trace_buffer.length;
|
|
|
|
shadowText(ctx, ""+trace_buffer[value], videoWidth-1, yp);
|
2017-11-28 02:08:19 +00:00
|
|
|
}
|
2017-11-14 14:33:15 +00:00
|
|
|
}
|
2017-11-29 01:48:27 +00:00
|
|
|
// draw scope line & label
|
|
|
|
if (scope_time_x > 0) {
|
|
|
|
ctx.fillStyle = "cyan";
|
2018-02-09 22:23:14 +00:00
|
|
|
shadowText(ctx, ""+(scope_time_x+scope_x_offset),
|
2017-11-29 01:48:27 +00:00
|
|
|
(scope_time_x>10)?(scope_time_x-2):(scope_time_x+20), videoHeight-2);
|
|
|
|
ctx.fillRect(scope_time_x, 0, 1, 4000);
|
|
|
|
}
|
|
|
|
// scroll left/right
|
|
|
|
if (scope_time_x >= videoWidth && scope_x_offset < (trace_buffer.length / arr.length) - videoWidth) {
|
|
|
|
scope_x_offset += 1 + (scope_time_x - videoWidth);
|
|
|
|
dirty = true;
|
|
|
|
}
|
|
|
|
else if (scope_time_x < 0 && scope_x_offset > 0) {
|
|
|
|
scope_x_offset = Math.max(0, scope_x_offset + scope_time_x);
|
|
|
|
dirty = true;
|
|
|
|
}
|
2017-11-14 14:33:15 +00:00
|
|
|
}
|
|
|
|
|
2017-11-17 21:01:07 +00:00
|
|
|
function clamp(minv,maxv,v) {
|
|
|
|
return (v < minv) ? minv : (v > maxv) ? maxv : v;
|
|
|
|
}
|
|
|
|
|
2017-11-11 19:45:32 +00:00
|
|
|
this.start = function() {
|
|
|
|
// TODO
|
|
|
|
video = new RasterVideo(mainElement,videoWidth,videoHeight);
|
|
|
|
video.create();
|
2017-11-21 16:15:08 +00:00
|
|
|
var ctx = video.getContext();
|
|
|
|
ctx.font = "8px TinyFont";
|
|
|
|
ctx.fillStyle = "white";
|
|
|
|
ctx.textAlign = "left";
|
2017-11-15 00:12:52 +00:00
|
|
|
setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP);
|
2018-07-15 04:29:27 +00:00
|
|
|
// TODO: make it stop incrementing time when clicked
|
2017-11-15 00:12:52 +00:00
|
|
|
$(video.canvas).mousemove(function(e) {
|
2017-11-28 02:08:19 +00:00
|
|
|
var new_x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width() - 20);
|
|
|
|
var new_y = Math.floor(e.offsetY * video.canvas.height / $(video.canvas).height() - 20);
|
|
|
|
if (mouse_pressed) {
|
2017-11-29 01:48:27 +00:00
|
|
|
scope_y_offset = clamp(Math.min(0,-scope_max_y+videoHeight), 0, scope_y_offset + new_y - paddle_y);
|
2017-11-28 02:08:19 +00:00
|
|
|
scope_time_x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width() - 16);
|
2018-07-14 19:58:16 +00:00
|
|
|
dirty = true;
|
2018-02-28 17:09:29 +00:00
|
|
|
redrawFrame();
|
2017-11-28 02:08:19 +00:00
|
|
|
}
|
|
|
|
paddle_x = clamp(8, 240, new_x);
|
|
|
|
paddle_y = clamp(8, 240, new_y);
|
|
|
|
});
|
|
|
|
$(video.canvas).mousedown(function(e) {
|
|
|
|
scope_time_x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width() - 16);
|
|
|
|
mouse_pressed = true;
|
2018-07-13 19:39:08 +00:00
|
|
|
if (e.target.setCapture) e.target.setCapture(); // TODO: pointer capture
|
2018-07-14 19:58:16 +00:00
|
|
|
dirty = true;
|
2018-02-28 17:09:29 +00:00
|
|
|
redrawFrame();
|
2017-11-28 02:08:19 +00:00
|
|
|
});
|
|
|
|
$(video.canvas).mouseup(function(e) {
|
|
|
|
mouse_pressed = false;
|
2018-07-13 19:39:08 +00:00
|
|
|
if (e.target.setCapture) e.target.releaseCapture(); // TODO: pointer capture
|
2018-07-14 19:58:16 +00:00
|
|
|
dirty = true;
|
2018-02-28 17:09:29 +00:00
|
|
|
redrawFrame();
|
2017-11-15 00:12:52 +00:00
|
|
|
});
|
2018-02-18 05:12:09 +00:00
|
|
|
$(video.canvas).keydown(function(e) {
|
|
|
|
switch (e.keyCode) {
|
2018-07-14 19:58:16 +00:00
|
|
|
case 37: scope_time_x--; dirty=true; redrawFrame(); break;
|
|
|
|
case 39: scope_time_x++; dirty=true; redrawFrame(); break;
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
});
|
2017-11-11 19:45:32 +00:00
|
|
|
idata = video.getFrameData();
|
2018-02-26 23:18:23 +00:00
|
|
|
timerCallback = function() {
|
2017-11-11 19:45:32 +00:00
|
|
|
if (!self.isRunning())
|
|
|
|
return;
|
2017-11-15 00:12:52 +00:00
|
|
|
gen.switches = switches[0];
|
2018-07-14 19:58:16 +00:00
|
|
|
redrawFrame();
|
2018-02-26 23:18:23 +00:00
|
|
|
};
|
2018-02-21 18:54:53 +00:00
|
|
|
trace_buffer = new Uint32Array(0x10000);
|
2018-02-26 23:18:23 +00:00
|
|
|
self.setFrameRate(60);
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
2018-07-14 19:58:16 +00:00
|
|
|
|
2018-02-28 17:09:29 +00:00
|
|
|
function redrawFrame() {
|
2018-07-14 19:58:16 +00:00
|
|
|
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
|
|
|
|
updateVideoFrame();
|
|
|
|
else
|
|
|
|
updateScopeFrame();
|
2018-02-28 17:09:29 +00:00
|
|
|
}
|
|
|
|
|
2017-11-22 14:42:07 +00:00
|
|
|
this.printErrorCodeContext = function(e, code) {
|
2017-11-15 00:12:52 +00:00
|
|
|
if (e.lineNumber && e.message) {
|
|
|
|
var lines = code.split('\n');
|
|
|
|
var s = e.message + '\n';
|
|
|
|
for (var i=0; i<lines.length; i++) {
|
|
|
|
if (i > e.lineNumber-5 && i < e.lineNumber+5) {
|
|
|
|
s += lines[i] + '\n';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
console.log(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-14 14:33:15 +00:00
|
|
|
this.loadROM = function(title, output) {
|
2017-11-15 00:12:52 +00:00
|
|
|
var mod;
|
2018-03-02 05:15:33 +00:00
|
|
|
if (output.code) {
|
|
|
|
try {
|
|
|
|
mod = new Function('base', output.code);
|
|
|
|
} catch (e) {
|
|
|
|
this.printErrorCodeContext(e, output.code);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
// compile Verilog code
|
|
|
|
var base = new VerilatorBase();
|
|
|
|
gen = new mod(base);
|
|
|
|
gen.__proto__ = base;
|
|
|
|
current_output = output;
|
|
|
|
module_name = output.name ? output.name.substr(1) : "top";
|
|
|
|
trace_ports = current_output.ports;
|
|
|
|
trace_signals = current_output.ports.concat(current_output.signals);
|
|
|
|
trace_index = 0;
|
|
|
|
// power on module
|
|
|
|
this.poweron();
|
|
|
|
} else {
|
|
|
|
// TODO: :^P
|
|
|
|
output = {program_rom_variable: title, program_rom: output};
|
|
|
|
}
|
|
|
|
// replace program ROM, if using the assembler
|
|
|
|
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);
|
|
|
|
else
|
|
|
|
gen[output.program_rom_variable] = output.program_rom;
|
|
|
|
} else {
|
|
|
|
alert("No program_rom variable found (" + output.program_rom_variable + ")");
|
|
|
|
}
|
2017-11-15 00:12:52 +00:00
|
|
|
}
|
2018-03-02 05:15:33 +00:00
|
|
|
// restart audio
|
2018-02-26 23:18:23 +00:00
|
|
|
restartAudio();
|
|
|
|
}
|
|
|
|
|
|
|
|
function restartAudio() {
|
2018-02-21 18:54:53 +00:00
|
|
|
// stop/start audio
|
2018-02-26 23:18:23 +00:00
|
|
|
var hasAudio = gen && gen.spkr !== undefined && frameRate > 1;
|
|
|
|
if (audio && !hasAudio) {
|
2018-02-21 18:54:53 +00:00
|
|
|
audio.stop();
|
|
|
|
audio = null;
|
2018-02-26 23:18:23 +00:00
|
|
|
} else if (!audio && hasAudio) {
|
|
|
|
audio = new SampleAudio(cyclesPerFrame * self.getFrameRate());
|
|
|
|
if (self.isRunning())
|
2018-02-21 18:54:53 +00:00
|
|
|
audio.start();
|
|
|
|
}
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.isRunning = function() {
|
|
|
|
return timer && timer.isRunning();
|
|
|
|
}
|
|
|
|
this.pause = function() {
|
|
|
|
timer.stop();
|
2018-02-21 18:54:53 +00:00
|
|
|
if (audio) audio.stop();
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
this.resume = function() {
|
|
|
|
timer.start();
|
2018-02-21 18:54:53 +00:00
|
|
|
if (audio) audio.start();
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 23:18:23 +00:00
|
|
|
var frameRate = 0;
|
|
|
|
|
|
|
|
this.setFrameRate = function(rateHz) {
|
|
|
|
frameRate = rateHz;
|
|
|
|
var fps = Math.min(60, rateHz*cyclesPerFrame);
|
|
|
|
if (!timer || timer.frameRate != fps) {
|
|
|
|
var running = this.isRunning();
|
|
|
|
if (timer) timer.stop();
|
|
|
|
timer = new AnimationTimer(fps, timerCallback);
|
|
|
|
if (running) timer.start();
|
|
|
|
}
|
|
|
|
if (audio) {
|
|
|
|
audio.stop();
|
|
|
|
audio = null;
|
|
|
|
}
|
|
|
|
restartAudio();
|
|
|
|
}
|
|
|
|
this.getFrameRate = function() { return frameRate; }
|
|
|
|
|
2017-11-21 16:15:08 +00:00
|
|
|
this.poweron = function() {
|
2017-11-11 19:45:32 +00:00
|
|
|
gen._ctor_var_reset();
|
2017-11-21 16:15:08 +00:00
|
|
|
this.reset();
|
|
|
|
}
|
|
|
|
this.reset = function() {
|
2017-11-28 02:08:19 +00:00
|
|
|
gen.__reset();
|
2017-11-29 01:48:27 +00:00
|
|
|
trace_index = scope_x_offset = 0;
|
2018-02-27 04:48:36 +00:00
|
|
|
if (trace_buffer) trace_buffer.fill(0);
|
2017-11-28 02:08:19 +00:00
|
|
|
dirty = true;
|
2018-02-18 05:12:09 +00:00
|
|
|
if (video) video.setRotate(gen.rotate ? -90 : 0);
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
2017-11-22 14:42:07 +00:00
|
|
|
this.tick = function() {
|
|
|
|
gen.tick2();
|
|
|
|
}
|
2017-11-11 19:45:32 +00:00
|
|
|
this.getToolForFilename = function(fn) {
|
2018-02-15 00:26:40 +00:00
|
|
|
if (fn.endsWith("asm"))
|
2018-03-02 05:15:33 +00:00
|
|
|
return "jsasm";
|
2018-02-15 00:26:40 +00:00
|
|
|
else
|
|
|
|
return "verilator";
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
this.getDefaultExtension = function() { return ".v"; };
|
2017-11-21 16:15:08 +00:00
|
|
|
|
|
|
|
this.inspect = function(name) {
|
|
|
|
if (!gen) return;
|
|
|
|
if (name && !name.match(/^\w+$/)) return;
|
|
|
|
var val = gen[name];
|
|
|
|
if (val === undefined && current_output.code) {
|
|
|
|
var re = new RegExp("(\\w+__DOT__" + name + ")\\b", "gm");
|
|
|
|
var m = re.exec(current_output.code);
|
|
|
|
if (m) {
|
|
|
|
name = m[1];
|
|
|
|
val = gen[name];
|
|
|
|
}
|
|
|
|
}
|
2018-03-19 01:11:11 +00:00
|
|
|
if (typeof(val) === 'number') {
|
2017-11-21 16:15:08 +00:00
|
|
|
inspect_obj = gen;
|
|
|
|
inspect_sym = name;
|
|
|
|
} else {
|
|
|
|
inspect_obj = inspect_sym = null;
|
|
|
|
}
|
2017-11-29 01:48:27 +00:00
|
|
|
dirty = true;
|
2017-11-21 16:15:08 +00:00
|
|
|
}
|
2017-11-24 19:14:22 +00:00
|
|
|
|
|
|
|
// DEBUGGING
|
|
|
|
|
|
|
|
this.saveState = function() {
|
|
|
|
return {T:gen.ticks(), o:$.extend(true, {}, gen)};
|
|
|
|
}
|
|
|
|
this.loadState = function(state) {
|
|
|
|
gen = $.extend(true, gen, state.o);
|
|
|
|
gen.setTicks(state.T);
|
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
2018-02-03 20:20:56 +00:00
|
|
|
if (gen.vsync && gen.ticks() > debugTargetClock+2000) {
|
2017-11-24 19:14:22 +00:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-11-11 19:45:32 +00:00
|
|
|
};
|
|
|
|
|
2017-11-20 01:32:58 +00:00
|
|
|
function traceTiming() {
|
|
|
|
// TODO: merge with main setCode(text)
|
|
|
|
var text = editor.getValue();
|
|
|
|
worker.postMessage({
|
|
|
|
code:text,
|
|
|
|
dependencies:loadFileDependencies(text),
|
|
|
|
platform:platform_id,
|
|
|
|
tool:'yosys'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-11-11 19:45:32 +00:00
|
|
|
////////////////
|
|
|
|
|
|
|
|
PLATFORMS['verilog'] = VerilogPlatform;
|