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'},
|
|
|
|
{id:'lfsr.v', name:'Linear Feedback Shift Register'},
|
2017-11-18 19:51:25 +00:00
|
|
|
{id:'digits10.v', name:'Digits'},
|
2017-11-18 00:40:44 +00:00
|
|
|
{id:'ball_slip_counter.v', name:'Ball Motion (slipping counter)'},
|
|
|
|
{id:'ball_paddle.v', name:'Brick Smash Game'},
|
2017-11-21 21:36:38 +00:00
|
|
|
{id:'sprite_bitmap.v', name:'Sprite Bitmaps'},
|
|
|
|
{id:'sprite_renderer.v', name:'Sprite Rendering'},
|
|
|
|
{id:'sprite_multiple.v', name:'Multiple Sprites'},
|
2017-11-29 01:48:27 +00:00
|
|
|
{id:'cpu8.v', name:'Simple 8-Bit CPU'},
|
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],
|
|
|
|
[Keys.VK_1, 0, 0x40],
|
|
|
|
[Keys.VK_2, 0, 0x80],
|
|
|
|
[Keys.VK_5, 0, 0x100],
|
|
|
|
[Keys.VK_6, 0, 0x200],
|
|
|
|
[Keys.VK_7, 0, 0x400],
|
|
|
|
]);
|
|
|
|
|
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) {
|
|
|
|
return VL_EXTENDS_II(x,lbits,lhs) < VL_EXTENDS_II(x,lbits,rhs); }
|
|
|
|
|
|
|
|
var VL_GTS_III = this.VL_GTS_III = function(x,lbits,y,lhs,rhs) {
|
|
|
|
return VL_EXTENDS_II(x,lbits,lhs) > VL_EXTENDS_II(x,lbits,rhs); }
|
|
|
|
|
|
|
|
var VL_LTES_III = this.VL_LTES_III = function(x,lbits,y,lhs,rhs) {
|
|
|
|
return VL_EXTENDS_II(x,lbits,lhs) <= VL_EXTENDS_II(x,lbits,rhs); }
|
|
|
|
|
|
|
|
var VL_GTES_III = this.VL_GTES_III = function(x,lbits,y,lhs,rhs) {
|
|
|
|
return VL_EXTENDS_II(x,lbits,lhs) >= VL_EXTENDS_II(x,lbits,rhs); }
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
|
|
|
var VL_RAND_RESET_I = this.VL_RAND_RESET_I = function(bits) { return 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
var vlSymsp = {TOPp:this};
|
|
|
|
var maxVclockLoop = 1;
|
|
|
|
|
|
|
|
this.eval = function() {
|
|
|
|
vlSymsp.TOPp = this;
|
|
|
|
// 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) {
|
|
|
|
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;
|
2017-11-15 00:12:52 +00:00
|
|
|
var videoWidth = 288;
|
|
|
|
var videoHeight = 248;
|
2017-11-11 19:45:32 +00:00
|
|
|
var idata, timer;
|
|
|
|
var gen;
|
|
|
|
var frameRate = 60;
|
2017-11-15 00:12:52 +00:00
|
|
|
var AUDIO_FREQ = 15750;
|
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;
|
|
|
|
var switches = [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;
|
|
|
|
var scope_max_y = 0;
|
|
|
|
|
|
|
|
var yposlist = [];
|
|
|
|
var lasty = [];
|
|
|
|
var lastval = [];
|
|
|
|
var ports_and_signals;
|
|
|
|
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,
|
|
|
|
];
|
|
|
|
|
2017-11-24 19:14:22 +00:00
|
|
|
var debugCond;
|
|
|
|
|
2017-11-15 00:12:52 +00:00
|
|
|
function vidtick() {
|
|
|
|
gen.tick2();
|
|
|
|
audio.addSingleSample(0+gen.spkr); // TODO: sync with audio freq
|
2017-11-24 19:14:22 +00:00
|
|
|
if (debugCond && debugCond()) debugCond = null;
|
2017-11-15 00:12:52 +00:00
|
|
|
}
|
|
|
|
|
2017-11-21 16:15:08 +00:00
|
|
|
function updateInspectionFrame() {
|
|
|
|
if (inspect_obj && inspect_sym) {
|
|
|
|
var COLOR_BIT_OFF = 0xffff3333;
|
|
|
|
var COLOR_BIT_ON = 0xffffffff;
|
|
|
|
for (var y=0; y<videoHeight; y++) {
|
|
|
|
var val = inspect_data[y * videoWidth];
|
|
|
|
var i = y * videoWidth + 16;
|
|
|
|
do {
|
|
|
|
idata[i] = (val & 1) ? COLOR_BIT_ON : COLOR_BIT_OFF;
|
|
|
|
i -= 2;
|
|
|
|
val >>= 1;
|
|
|
|
} while (val != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-14 14:33:15 +00:00
|
|
|
function updateVideoFrame() {
|
2017-11-24 19:14:22 +00:00
|
|
|
debugCond = self.getDebugCallback();
|
2017-11-21 16:15:08 +00:00
|
|
|
var i=4; // TODO, start @ 0?
|
|
|
|
var trace=inspect_obj && inspect_sym;
|
2017-11-14 14:33:15 +00:00
|
|
|
for (var y=0; y<videoHeight; y++) {
|
2017-11-15 00:12:52 +00:00
|
|
|
gen.hpaddle = y > paddle_x ? 1 : 0;
|
|
|
|
gen.vpaddle = y > paddle_y ? 1 : 0;
|
2017-11-14 14:33:15 +00:00
|
|
|
for (var x=0; x<videoWidth; x++) {
|
2017-11-15 00:12:52 +00:00
|
|
|
vidtick();
|
2017-11-21 16:15:08 +00:00
|
|
|
if (trace) {
|
|
|
|
inspect_data[i] = inspect_obj[inspect_sym];
|
|
|
|
}
|
2017-11-14 14:33:15 +00:00
|
|
|
idata[i++] = RGBLOOKUP[gen.rgb];
|
|
|
|
}
|
|
|
|
var z=0;
|
2017-11-15 00:12:52 +00:00
|
|
|
while (!gen.hsync && z++<videoWidth) vidtick();
|
2017-11-21 16:15:08 +00:00
|
|
|
while (gen.hsync && z++<videoWidth) vidtick();
|
2017-11-14 14:33:15 +00:00
|
|
|
}
|
|
|
|
var z=0;
|
2017-11-15 00:12:52 +00:00
|
|
|
while (!gen.vsync && z++<videoWidth*80) vidtick();
|
2017-11-21 16:15:08 +00:00
|
|
|
while (gen.vsync && z++<videoWidth*80) vidtick();
|
|
|
|
updateInspectionFrame();
|
2017-11-14 14:33:15 +00:00
|
|
|
video.updateFrame();
|
2017-11-21 16:15:08 +00:00
|
|
|
updateInspectionPostFrame();
|
2017-11-24 19:14:22 +00:00
|
|
|
self.restartDebugState();
|
2017-11-28 02:08:19 +00:00
|
|
|
gen.__unreset();
|
2017-11-14 14:33:15 +00:00
|
|
|
}
|
|
|
|
|
2017-11-28 02:08:19 +00:00
|
|
|
function fillTraceBuffer(count) {
|
|
|
|
var arr = ports_and_signals;
|
|
|
|
var max_index = Math.min(trace_buffer.length, trace_index + count);
|
|
|
|
while (trace_index < max_index) {
|
|
|
|
gen.clk ^= 1;
|
|
|
|
gen.eval();
|
|
|
|
for (var i=0; i<arr.length; i++) {
|
|
|
|
var v = arr[i];
|
|
|
|
var z = gen[v.name];
|
|
|
|
trace_buffer[trace_index++] = z;
|
|
|
|
}
|
|
|
|
dirty = true;
|
|
|
|
}
|
|
|
|
gen.__unreset();
|
|
|
|
}
|
2017-11-14 14:33:15 +00:00
|
|
|
|
|
|
|
function updateScopeFrame() {
|
2017-11-28 02:08:19 +00:00
|
|
|
var arr = ports_and_signals;
|
2017-11-14 14:33:15 +00:00
|
|
|
if (!arr) return;
|
2017-11-28 02:08:19 +00:00
|
|
|
fillTraceBuffer(Math.floor(videoWidth/4) * arr.length);
|
|
|
|
if (!dirty) return;
|
|
|
|
dirty = false;
|
2017-11-14 14:33:15 +00:00
|
|
|
for (var i=0; i<idata.length; i++) {
|
|
|
|
if (idata[i])
|
|
|
|
idata[i] = 0; //<<= 1;
|
|
|
|
}
|
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;
|
2017-11-29 01:48:27 +00:00
|
|
|
var jstart = scope_x_offset * arr.length;
|
|
|
|
var j = jstart;
|
2017-11-14 14:33:15 +00:00
|
|
|
for (var x=0; x<videoWidth; x++) {
|
|
|
|
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?
|
|
|
|
var hi = v.len ? ((2 << v.len)-1) : 1;
|
|
|
|
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++];
|
2017-11-15 00:12:52 +00:00
|
|
|
var y = Math.round(y2 - ys*((z-lo)/hi));
|
2017-11-14 14:33:15 +00:00
|
|
|
yposlist[i] = y2;
|
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) {
|
|
|
|
idata[x + ly*videoWidth] = COLOR_TRANS_SIGNAL;
|
|
|
|
}
|
|
|
|
}
|
2017-11-28 02:08:19 +00:00
|
|
|
idata[x + y*videoWidth] = 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;
|
2017-11-14 14:33:15 +00:00
|
|
|
video.updateFrame();
|
|
|
|
// draw labels
|
|
|
|
var ctx = video.getContext();
|
|
|
|
for (var i=0; i<arr.length; i++) {
|
|
|
|
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,'.');
|
|
|
|
ctx.textAlign = 'left';
|
|
|
|
ctx.fillText(name, 1, yposlist[i]);
|
|
|
|
if (scope_time_x > 0) {
|
|
|
|
ctx.textAlign = 'right';
|
2017-11-29 01:48:27 +00:00
|
|
|
var value = arr.length * scope_time_x + i + jstart;
|
2017-11-28 02:08:19 +00:00
|
|
|
ctx.fillText(""+trace_buffer[value], videoWidth-1, yposlist[i]);
|
|
|
|
}
|
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";
|
|
|
|
ctx.fillText(""+(scope_time_x+scope_x_offset),
|
|
|
|
(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);
|
|
|
|
$(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);
|
|
|
|
dirty = true;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
e.target.setCapture();
|
|
|
|
dirty = true;
|
|
|
|
});
|
|
|
|
$(video.canvas).mouseup(function(e) {
|
|
|
|
mouse_pressed = false;
|
|
|
|
e.target.releaseCapture();
|
2017-11-15 00:12:52 +00:00
|
|
|
});
|
2017-11-13 05:24:19 +00:00
|
|
|
audio = new SampleAudio(AUDIO_FREQ);
|
2017-11-11 19:45:32 +00:00
|
|
|
idata = video.getFrameData();
|
|
|
|
// TODO: 15.7 kHz?
|
|
|
|
timer = new AnimationTimer(frameRate, function() {
|
|
|
|
if (!self.isRunning())
|
|
|
|
return;
|
2017-11-15 00:12:52 +00:00
|
|
|
gen.switches = switches[0];
|
2017-11-14 14:33:15 +00:00
|
|
|
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
|
|
|
|
updateVideoFrame();
|
|
|
|
else
|
|
|
|
updateScopeFrame();
|
2017-11-11 19:45:32 +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;
|
|
|
|
try {
|
|
|
|
mod = new Function('base', output.code);
|
|
|
|
} catch (e) {
|
2017-11-22 14:42:07 +00:00
|
|
|
this.printErrorCodeContext(e, output.code);
|
2017-11-15 00:12:52 +00:00
|
|
|
throw e;
|
|
|
|
}
|
2017-11-11 19:45:32 +00:00
|
|
|
var base = new VerilatorBase();
|
|
|
|
gen = new mod(base);
|
|
|
|
gen.__proto__ = base;
|
2017-11-14 14:33:15 +00:00
|
|
|
current_output = output;
|
2017-11-28 02:08:19 +00:00
|
|
|
ports_and_signals = current_output.ports.concat(current_output.signals);
|
|
|
|
trace_buffer = new Uint32Array(0x10000);
|
|
|
|
trace_index = 0;
|
2017-11-21 16:15:08 +00:00
|
|
|
this.poweron();
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.isRunning = function() {
|
|
|
|
return timer && timer.isRunning();
|
|
|
|
}
|
|
|
|
this.pause = function() {
|
|
|
|
timer.stop();
|
2017-11-20 01:32:58 +00:00
|
|
|
if (gen.spkr !== undefined) audio.stop();
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
this.resume = function() {
|
|
|
|
timer.start();
|
2017-11-20 01:32:58 +00:00
|
|
|
if (gen.spkr !== undefined) audio.start();
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
|
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;
|
2017-11-28 02:08:19 +00:00
|
|
|
trace_buffer.fill(0);
|
|
|
|
dirty = true;
|
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) {
|
|
|
|
return "verilator";
|
|
|
|
}
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (val !== undefined) {
|
|
|
|
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() {
|
|
|
|
if (gen.vsync && gen.ticks() > debugTargetClock+1000) {
|
|
|
|
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;
|