345 lines
8.8 KiB
JavaScript

/* eslint-disable no-undef */
import { Stream } from "stream";
import { globalLookup } from "./global";
/**
*
* @returns {Promise<Stream>}
*/
async function getVcdStream() {
const ostream = await window.getVcdStream();
return ostream;
}
/**
* @returns {Promise<Uint8Array>}
*/
async function readVcdFile() {
const response = await fetch('test.vcd');
const blob = await response.blob();
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onload = event => {
const fileContent = event.target.result;
const unintarray = new Uint8Array(fileContent);
resolve(unintarray);
};
reader.readAsArrayBuffer(blob);
});
}
function debounceWrapper(fn, delay) {
let timer
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn()
}, delay)
}
}
function makeIconClass(mod) {
switch (mod.type) {
case 'module': return 'icon-memory-chip';
case 'begin': return 'icon-brackets';
case 'fork': return 'icon-fork';
case 'function': return 'icon-Function';
case 'task': return 'icon-task-';
case 'event': return 'icon-prevent';
case 'integer': return 'icon-integer';
case 'parameter': return 'icon-parameter';
case 'real': return 'icon-R';
case 'realtime': return 'icon-wave-square';
case 'reg': return 'icon-register';
case 'supply0': return 'icon-wave-square';
case 'supply1': return 'icon-wave-square';
case 'time': return 'icon-wave-square';
case 'tri': return 'icon-wave-square';
case 'triand': return 'icon-wave-square';
case 'trior': return 'icon-wave-square';
case 'trireg': return 'icon-wave-square';
case 'tri0': return 'icon-wave-square';
case 'tri1': return 'icon-wave-square';
case 'wand': return 'icon-wave-square';
case 'wire': return 'icon-wave-square';
case 'wor': return 'icon-wave-square';
case 'string': return 'icon-String';
default:
break;
}
}
/**
* @param { string } searchString
* @param {{
* kind: 'var' | 'scope',
* type: 'wire' | 'reg' | 'module',
* name: string,
* size?: number,
* link?: string
* }} module
* @param { Set<string> } searchScope
* @param { boolean } caseSensitivity
* @param { boolean } displayParentOnly
* @returns { { htmlString: string, module } | null }
*/
function makeSearchResultItem(searchString, module, searchScope, caseSensitivity, displayParentOnly) {
if (searchScope.has(module.type)) {
let pattern = module.name;
if (!caseSensitivity) {
pattern = pattern.toLowerCase();
searchString = searchString.toLowerCase();
}
if (pattern.includes(searchString)) {
let p = module;
const deps = [];
while (p) {
if (p.name && p.type) {
deps.push(p);
}
p = p.parent;
if (displayParentOnly && deps.length == 2) {
break;
}
}
let htmlString = '';
let currentCounts = 0;
for (let i = deps.length - 1; i >= 0; -- i) {
const mod = deps[i];
// const displayName = mod.name.length > 6 ? mod.name.substring(0, 6) + '...' : mod.name;
const iconClass = makeIconClass(mod);
const iconText = `<span class="iconfont ${iconClass}"></span>&ensp;${mod.name}`;
htmlString += iconText;
if (i > 0) {
htmlString += '<div class="dep-arrow"></div>';
}
}
return {
htmlString,
module
};
}
}
return null;
}
/**
*
* @param {'bit' | 'vec'} kind
* @param {BigInt} value
* @param {BigInt} mask
* @returns {number | string}
*/
function getWireValueCaption(kind, value, mask) {
if (kind === 'bit') {
if (value === 2) {
return 'x';
}
return value;
} else if (kind === 'vec') {
if (!mask) {
return value;
}
// mask 不为空,代表存在问题
if (value) {
return '?';
} else {
return 'x';
}
}
}
async function updateWireCurrentValue() {
const chango = globalLookup.chango;
const time = globalLookup.currentTime;
const currentSignalValues = globalLookup.currentSignalValues;
for (const signal of globalLookup.currentWires) {
const signalItem = chango[signal.link];
const { wave, kind } = signalItem;
// console.log(signalItem);
if (wave === undefined || wave.length === 0) {
currentSignalValues[signal.link] = 'x';
} else if (wave.length === 1) {
currentSignalValues[signal.link] = getWireValueCaption(kind, wave[0][1], wave[0][2]);
} else {
currentSignalValues[signal.link] = findCurrentSignalValue(kind, wave, time);
}
// console.log(signal.name, currentSignalValues[signal.link]);
}
}
/**
* @param {'bit' | 'vec'} kind
* @param {Array<Array<string | number>>} wave
* @param {number} time
*/
function findCurrentSignalValue(kind, wave, time) {
const times = wave.map(p => p[0]);
// 二分查找,并将结果存入 i
let i = 0, j = wave.length - 1;
while (i < j) {
if (times[i] === time) {
break;
}
if (times[j] <= time) {
i = j;
break;
}
if (j - i === 1) {
break;
}
const mid = (i + j) >> 1;
if (times[mid] > time) {
j = mid;
} else {
i = mid;
}
}
const value = getWireValueCaption(kind, wave[i][1], wave[i][2]);
return value;
}
/**
*
* @param {string} timescale
* @return {{
* scale: number,
* unit: string
* }}
*/
function handleTimeScale(timescale) {
const match = timescale.match(/^(\d+)(.*)$/);
if (match.length === 3) {
const scale = parseInt(match[1]);
const unit = match[2];
return { scale, unit };
} else {
console.log('Error: fail to parse timescale ' + timescale);
return { scale: null, unit: null };
}
}
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
/**
*
* @param {string} timescale
* @returns {string}
*/
function parseTimescale(timescale) {
if (typeof timescale !== 'string') {
return;
}
const str1 = timescale.trim();
const m = str1.match(/^(\d+)\s*(\w+)$/);
const res1 = ({ 1: 0, 10: 1, 100: 2 })[m[1]];
const res2 = ({ s: 0, ms: -3, us: -6, ns: -9, ps: -12, fs: -15 })[m[2]];
return res1 + res2;
}
/**
*
* @param {BigInt} val
* @returns {string | number}
*/
function numberOrString(val) {
if (val < MAX_SAFE_INTEGER) {
return Number(val);
}
return '0x' + val.toString(16);
}
/**
*
* @param {number} a
* @param {number} b
* @returns {number}
*/
function gcd(a, b) {
if (a === undefined) {
return b;
}
let r;
while (b !== 0) {
r = a % b;
a = b;
b = r;
}
return (a < 0) ? -a : a;
}
function normaliseUsingGCD(globalLookup, signalValues) {
// const { tgcd, chango } = o;
const tgcd = globalLookup.tgcd;
// 使用全局最大公约数缩小时间倍率
globalLookup.t0 /= tgcd;
globalLookup.time /= tgcd;
for (const id of Reflect.ownKeys(signalValues)) {
// point[0] 是当前这个点的时间点
signalValues[id].wave.map(point => { point[0] /= tgcd });
}
const exp = Math.log10(tgcd) | 0;
if (exp > 0) {
const scale = Math.pow(10, exp);
const scaleGcd = tgcd / scale;
if (scaleGcd === (scaleGcd | 0)) {
globalLookup.tgcd = scaleGcd;
globalLookup.timescale += exp;
}
}
}
const scopes = new Set(['begin', 'fork', 'function', 'module', 'task']);
const variables = new Set(['event', 'integer', 'parameter', 'real', 'realtime', 'reg', 'supply0',
'supply1', 'time', 'tri', 'triand', 'trior', 'trireg', 'tri0', 'tri1',
'wand', 'wire', 'wor', 'string']);
function isScope(wire) {
return scopes.has(wire.type);
}
function isVariable(wire) {
return variables.has(wire.type);
}
export {
getVcdStream,
readVcdFile,
debounceWrapper,
makeSearchResultItem,
handleTimeScale,
isScope,
isVariable,
makeIconClass,
scopes,
variables,
parseTimescale,
numberOrString,
gcd,
findCurrentSignalValue,
normaliseUsingGCD,
updateWireCurrentValue
};