1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-10 21:29:33 +00:00

verilog: $readmem, fixed while loop

This commit is contained in:
Steven Hugg 2021-07-05 13:46:24 -05:00
parent 3ec69792b0
commit bc13614b6a
6 changed files with 105 additions and 38 deletions

View File

@ -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; }

View File

@ -11,6 +11,7 @@ export interface HDLModuleRunner {
saveState() : {};
loadState(state: {}) : void;
dispose() : void;
getFileData : (filename : string) => string|Uint8Array;
}
export interface HDLModuleTrace {

View File

@ -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<data.length; i++) {
this.data8[p_rom + i] = data[i];
}
return 0;
}
private addCopyTraceRecFunction() {
const m = this.bmod;
const o_TRACERECLEN = this.globals.lookup(TRACERECLEN).offset;
@ -673,13 +703,18 @@ export class HDLModuleWASM implements HDLModuleRunner {
funccall2wasm(e: HDLFuncCall, opts?:Options) : number {
var args = [this.dataptr()];
for (var arg of e.args) {
args.push(this.e2w(arg));
}
var internal = e.funcname;
if (e.funcname.startsWith('$')) {
if ((e.funcname == '$stop' || e.funcname == '$finish') && e.$loc) {
args = [this.bmod.i32.const(e.$loc.line)]; // line # of source code
}
internal += '_' + (args.length - 1);
}
var ret = this.funcResult(e.funcname);
return this.bmod.call(e.funcname, args, ret);
return this.bmod.call(internal, args, ret);
}
const2wasm(e: HDLConstant, opts: Options) : number {
@ -707,14 +742,17 @@ export class HDLModuleWASM implements HDLModuleRunner {
if (local != null) {
return this.bmod.local.get(local.index, local.itype);
} else if (global != null) {
return this.loadmem(this.dataptr(), global.offset, global.size);
if (global.size <= 8)
return this.loadmem(this.dataptr(), global.offset, global.size);
else
return this.address2wasm(e);
}
throw new HDLError(e, `cannot lookup variable ${e.refname}`)
}
local2wasm(e: HDLVariableDef, opts:Options) : number {
var local = this.locals.lookup(e.name);
if (local == null) throw Error(`no local for ${e.name}`)
//if (local == null) throw Error(`no local for ${e.name}`)
return this.bmod.nop(); // TODO
}
@ -829,7 +867,11 @@ export class HDLModuleWASM implements HDLModuleRunner {
block.push(this.e2w(e.precond));
}
if (e.loopcond) {
block.push(this.bmod.br_if("@block", this.e2w(e.loopcond, {resulttype:binaryen.i32})));
block.push(this.bmod.if(
this.e2w(e.loopcond, {resulttype:binaryen.i32}),
this.bmod.nop(),
this.bmod.br("@block") // exit loop
));
}
if (e.body) {
block.push(this.e2w(e.body));
@ -972,6 +1014,15 @@ export class HDLModuleWASM implements HDLModuleRunner {
_sub2wasm(e: HDLBinop) {
return this.binop(e, this.i3264(e.dtype).sub);
}
_mul2wasm(e: HDLBinop) {
return this.binop(e, this.i3264(e.dtype).mul);
}
_moddiv2wasm(e: HDLBinop) {
return this.binop(e, this.i3264(e.dtype).rem_u);
}
_div2wasm(e: HDLBinop) {
return this.binop(e, this.i3264(e.dtype).div_u);
}
_moddivs2wasm(e: HDLBinop) {
return this.binop(e, this.i3264(e.dtype).rem_s);
}

View File

@ -165,6 +165,7 @@ export class VerilogXMLParser implements HDLUnit {
instances: [],
vardefs: {},
}
if (this.cur_module) throw new Error(`nested modules not supported`);
this.cur_module = module;
return module;
}
@ -187,7 +188,7 @@ export class VerilogXMLParser implements HDLUnit {
var m = re_const.exec(s);
if (m) {
var numstr = m[3];
if (numstr.length < 8)
if (numstr.length <= 8)
return parseInt(numstr, 16);
else
return BigInt('0x' + numstr);
@ -213,6 +214,9 @@ export class VerilogXMLParser implements HDLUnit {
visit_verilator_xml(node: XMLNode) {
}
visit_package(node: XMLNode) { // TODO?
}
visit_module(node: XMLNode) {
this.findChildren(node, 'var', false).forEach((n) => {
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); }

View File

@ -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();

View File

@ -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};