/* eslint-disable no-undef */ import { Stream } from "stream"; import { globalLookup } from "./global"; /** * * @returns {Promise} */ async function getVcdStream() { const ostream = await window.getVcdStream(); return ostream; } /** * @returns {Promise} */ 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 } 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 = ` ${mod.name}`; htmlString += iconText; if (i > 0) { htmlString += '
'; } } 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>} 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 };