diff --git a/src/common/hdl/hdlruntime.ts b/src/common/hdl/hdlruntime.ts index e512e3b3..4d448b39 100644 --- a/src/common/hdl/hdlruntime.ts +++ b/src/common/hdl/hdlruntime.ts @@ -24,6 +24,7 @@ export class HDLModuleJS implements HDLModuleRunner { curconsts: {}; constused: number; specfuncs: VerilatorUnit[] = []; + getFileData = null; constructor(mod: HDLModuleDef, constpool: HDLModuleDef) { this.mod = mod; @@ -377,7 +378,7 @@ export class HDLModuleJS implements HDLModuleRunner { barr.reverse(); // reverse it var strfn = byteArrayToString(barr); // convert to string // parse hex/binary file - var strdata = this.getFile(strfn) as string; + var strdata = this.getFileData(strfn) as string; if (strdata == null) throw Error("Could not $readmem '" + strfn + "'"); var data = strdata.split('\n').filter(s => s !== '').map(s => parseInt(s, ishex ? 16 : 2)); console.log('$readmem', ishex, strfn, data.length); @@ -388,11 +389,6 @@ export class HDLModuleJS implements HDLModuleRunner { memp[i] = data[i]; } - getFile(path: string) : string { - // TODO: override - return null; - } - isStopped() { return this.stopped; } isFinished() { return this.finished; } diff --git a/src/common/hdl/hdltypes.ts b/src/common/hdl/hdltypes.ts index 05a1faa7..597dd9aa 100644 --- a/src/common/hdl/hdltypes.ts +++ b/src/common/hdl/hdltypes.ts @@ -11,6 +11,7 @@ export interface HDLModuleRunner { saveState() : {}; loadState(state: {}) : void; dispose() : void; + getFileData : (filename : string) => string|Uint8Array; } export interface HDLModuleTrace { diff --git a/src/common/hdl/hdlwasm.ts b/src/common/hdl/hdlwasm.ts index 39b230a7..10fbfb72 100644 --- a/src/common/hdl/hdlwasm.ts +++ b/src/common/hdl/hdlwasm.ts @@ -29,10 +29,12 @@ const TRACEBUF = "$$tbuf"; export class HDLError extends Error { obj: any; + $loc: HDLSourceLocation; constructor(obj: any, msg: string) { super(msg); Object.setPrototypeOf(this, HDLError.prototype); this.obj = obj; + if (obj && obj.$loc) this.$loc = obj.$loc; if (obj) console.log(obj); } } @@ -57,6 +59,10 @@ function getDataTypeSize(dt: HDLDataType) : number { } } +function isReferenceType(dt: HDLDataType) : boolean { + return getDataTypeSize(dt) > 8; +} + function getArrayElementSizeFromType(dtype: HDLDataType) : number { if (isArrayType(dtype)) { return getArrayElementSizeFromType(dtype.subtype); @@ -188,6 +194,7 @@ export class HDLModuleWASM implements HDLModuleRunner { traceStartOffset: number; traceEndOffset: number; trace: any; + getFileData = null; constructor(moddef: HDLModuleDef, constpool: HDLModuleDef) { this.hdlmod = moddef; @@ -345,7 +352,12 @@ export class HDLModuleWASM implements HDLModuleRunner { this.pushScope(fscope); block.exprs.forEach((e) => { if (e && isVarDecl(e)) { - fscope.addVar(e); + // TODO: make local reference types, instead of promoting local arrays to global + if (isReferenceType(e.dtype)) { + this.globals.addVar(e); + } else { + fscope.addVar(e); + } } }) // create function body @@ -464,26 +476,44 @@ export class HDLModuleWASM implements HDLModuleRunner { } private addImportedFunctions() { - this.bmod.addFunctionImport("$display", "builtins", "$display", binaryen.createType([binaryen.i32]), binaryen.none); - this.bmod.addFunctionImport("$finish", "builtins", "$finish", binaryen.createType([binaryen.i32]), binaryen.none); - this.bmod.addFunctionImport("$stop", "builtins", "$stop", binaryen.createType([binaryen.i32]), binaryen.none); - this.bmod.addFunctionImport("$time", "builtins", "$time", binaryen.createType([binaryen.i32]), binaryen.i64); - this.bmod.addFunctionImport("$rand", "builtins", "$rand", binaryen.createType([binaryen.i32]), binaryen.i32); + this.bmod.addFunctionImport("$finish_0", "builtins", "$finish", binaryen.createType([binaryen.i32]), binaryen.none); + this.bmod.addFunctionImport("$stop_0", "builtins", "$stop", binaryen.createType([binaryen.i32]), binaryen.none); + this.bmod.addFunctionImport("$time_0", "builtins", "$time", binaryen.createType([binaryen.i32]), binaryen.i64); + this.bmod.addFunctionImport("$rand_0", "builtins", "$rand", binaryen.createType([binaryen.i32]), binaryen.i32); + this.bmod.addFunctionImport("$readmem_2", "builtins", "$readmem", binaryen.createType([binaryen.i32, binaryen.i32, binaryen.i32]), binaryen.none); } private getImportObject() : {} { var n = 0; return { builtins: { - $display: (o) => { if (++n < 100) console.log('...',o); }, // TODO $finish: (o) => { console.log('... Finished @', o); this.finished = true; }, $stop: (o) => { console.log('... Stopped @', o); this.stopped = true; }, $time: (o) => BigInt(new Date().getTime()), // TODO: timescale $rand: (o) => (Math.random() * (65536 * 65536)) | 0, + $readmem: (o,a,b) => this.$readmem(a, b) } } } + private $readmem(p_filename, p_rom) { + var fn = ''; + for (var i=0; i<255; i++) { + var charCode = this.data8[p_filename + i]; + if (charCode == 0) break; + fn = String.fromCharCode(charCode) + fn; + } + var filedata = this.getFileData && this.getFileData(fn); + if (filedata == null) throw new HDLError(fn, `no file "${fn}" for $readmem`); + if (typeof filedata !== 'string') throw new HDLError(fn, `file "${fn}" must be lines of hex or binary values`); + var ishex = !fn.endsWith('.binary'); // TODO: hex should be attribute in xml + var data = filedata.split('\n').filter(s => s !== '').map(s => parseInt(s, ishex ? 16 : 2)); + for (var i=0; i { if (isVarDecl(n.obj)) { @@ -329,6 +333,10 @@ export class VerilogXMLParser implements HDLUnit { } visit_cfunc(node: XMLNode) : HDLBlock { + if (this.cur_module == null) { // TODO? + console.log('no module open, skipping', node); + return; + } var block = this.visit_begin(node); block.exprs = []; node.children.forEach((n) => block.exprs.push(n.obj)); @@ -648,8 +656,8 @@ export class VerilogXMLParser implements HDLUnit { visit_rand(node: XMLNode) { return this.__visit_func(node); } visit_time(node: XMLNode) { return this.__visit_func(node); } - visit_display(node: XMLNode) { return this.__visit_func(node); } - visit_sformatf(node: XMLNode) { return this.visit_begin(node); } + visit_display(node: XMLNode) { return null; } + visit_sformatf(node: XMLNode) { return null; } visit_readmem(node: XMLNode) { return this.__visit_func(node); } diff --git a/src/platform/verilog.ts b/src/platform/verilog.ts index efb33da4..0145b117 100644 --- a/src/platform/verilog.ts +++ b/src/platform/verilog.ts @@ -4,7 +4,7 @@ import { PLATFORMS, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeK import { SampleAudio } from "../common/audio"; import { safe_extend } from "../common/util"; import { WaveformView, WaveformProvider, WaveformMeta } from "../ide/waveform"; -import { setFrameRateUI, loadScript } from "../ide/ui"; +import { setFrameRateUI, loadScript, current_project } from "../ide/ui"; import { HDLModuleRunner, HDLModuleTrace, HDLUnit, isLogicType } from "../common/hdl/hdltypes"; import { HDLModuleJS } from "../common/hdl/hdlruntime"; import { HDLModuleWASM } from "../common/hdl/hdlwasm"; @@ -142,13 +142,17 @@ var VerilogPlatform = function(mainElement, options) { if (useAudio) { audio.feedSample(top.state.spkr, 1); } + resetKbdStrobe(); + if (debugCond && debugCond()) { + debugCond = null; + } + } + + function resetKbdStrobe() { if (keycode && keycode >= 128 && top.state.keystrobe) { // keystrobe = clear hi bit of key buffer keycode = keycode & 0x7f; top.state.keycode = keycode; } - if (debugCond && debugCond()) { - debugCond = null; - } } function doreset() { @@ -367,7 +371,7 @@ var VerilogPlatform = function(mainElement, options) { ncycles |= 0; var inspect = inspect_obj && inspect_sym; // use fast trace buffer-based update? - if (sync && !trace && top['trace'] != null) { + if (sync && !trace && top['trace'] != null && scanlineCycles > 0) { this.updateVideoFrameFast((top as any) as HDLModuleTrace); this.updateRecorder(); return; @@ -389,14 +393,17 @@ var VerilogPlatform = function(mainElement, options) { idata[frameidx] = rgb & 0x80000000 ? rgb : RGBLOOKUP[rgb & 15]; frameidx++; } + scanlineCycles++; } else if (!framehsync && top.state.hsync) { framehsync = true; + scanlineCycles++; } else if ((framehsync && !top.state.hsync) || framex > videoWidth*2) { framehsync = false; framex = 0; framey++; top.state.hpaddle = framey > video.paddle_x ? 1 : 0; top.state.vpaddle = framey > video.paddle_y ? 1 : 0; + scanlineCycles = 0; } if (framey > maxVideoLines || top.state.vsync) { framevsync = true; @@ -418,9 +425,9 @@ var VerilogPlatform = function(mainElement, options) { // use trace buffer to update video updateVideoFrameFast(tmod: HDLModuleTrace) { - var maxLineCycles = videoWidth < 300 ? 521 : 1009; // prime number so we eventually sync up + var maxLineCycles = 1009; // prime number so we eventually sync up if (!scanlineCycles) scanlineCycles = maxLineCycles; - var nextlineCycles = scanlineCycles; + var nextlineCycles = scanlineCycles + 1; // TODO: we can go faster if no paddle/sound frameidx = 0; var wasvsync = false; @@ -434,6 +441,8 @@ var VerilogPlatform = function(mainElement, options) { } // generate frames in trace buffer top.tick2(nextlineCycles); + // TODO: this has to be done more quickly + resetKbdStrobe(); // convert trace buffer to video/audio var n = 0; if (framey < videoHeight) { @@ -446,24 +455,25 @@ var VerilogPlatform = function(mainElement, options) { n += videoWidth; } // find hsync - while (n < maxLineCycles && !tmod.trace.hsync) { spkr(); tmod.nextTrace(); n++; } - while (n < maxLineCycles && tmod.trace.hsync) { spkr(); tmod.nextTrace(); n++; } + while (n < nextlineCycles && !tmod.trace.hsync) { spkr(); tmod.nextTrace(); n++; } + while (n < nextlineCycles && tmod.trace.hsync) { spkr(); tmod.nextTrace(); n++; } // see if our scanline cycle count is stable - if (n == scanlineCycles) { + if (n == scanlineCycles + 1) { // scanline cycle count licked in, reset buffer to improve cache locality nextlineCycles = n; - tmod.resetTrace(); + tmod.resetTrace(); } else { - // not in sync, set to prime # and we'll eventually sync - nextlineCycles = maxLineCycles; + // not in sync, don't reset buffer (TODO: take some of the cycles back) + //console.log('scanline', scanlineCycles, nextlineCycles, n); scanlineCycles = n; + nextlineCycles = n; } // exit when vsync starts and then stops if (tmod.trace.vsync) { wasvsync = true; - } else if (wasvsync) { top.state.hpaddle = 0; top.state.vpaddle = 0; + } else if (wasvsync) { break; } } @@ -541,6 +551,7 @@ var VerilogPlatform = function(mainElement, options) { // initialize top module and constant pool var useWASM = true; var _top = new (useWASM ? HDLModuleWASM : HDLModuleJS)(topmod, unit.modules['@CONST-POOL@']); + _top.getFileData = (path) => current_project.filedata[path]; // external file provider await _top.init(); _top.powercycle(); if (top) top.dispose(); diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index f0793497..af646d92 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -1806,20 +1806,20 @@ function compileVerilator(step:BuildStep) { return {errors:errors}; } var xmlParser = new emglobal['VerilogXMLParser'](); + var listings : CodeListingMap = {}; try { var xmlContent = FS.readFile(xmlPath, {encoding:'utf8'}); + listings[step.prefix + '.lst'] = {lines:[],text:xmlContent}; putWorkFile(xmlPath, xmlContent); if (!anyTargetChanged(step, [xmlPath])) return; xmlParser.parse(xmlContent); } catch(e) { - console.log(e); + console.log(e, e.stack); errors.push({line:0,msg:""+e}); - return {errors:errors}; + return {errors:errors, listings:listings}; } //rtn.intermediate = {listing:h_file + cpp_file}; // TODO - var listings : CodeListingMap = {}; - listings[step.prefix + '.lst'] = {lines:[],text:xmlContent}; // TODO: what if found in non-top-module? if (asmlines.length) listings[step.path] = {lines:asmlines};