345 lines
8.8 KiB
JavaScript
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> ${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
|
|
}; |