220 lines
5.9 KiB
JavaScript

/* eslint-disable no-undef */
'use strict';
const createVCD = require('../out/vcd.js');
const webVcdParser = require('../lib/web-vcd-parser.js');
/**
* @typedef {{ maxChunkLength: number, useGcd: boolean }} consumeConfig
*
* @typedef {{ kind: string, wave: number[] }} vcdValue
*
* @typedef {Object} VarSignal
* @property { 'var' } kind
* @property { 'event' | 'integer' | 'parameter' | 'real' | 'realtime' | 'reg' | 'supply0' | 'supply1' | 'time' | 'tri' | 'triand' | 'trior' | 'trireg' | 'tri0' | 'tri1' | 'wand' | 'wire' | 'wor' | 'string' } type
* @property {string} name 信号的真实名字
* @property {string} link 信号的 id
* @property {number} size 位宽
*
* @typedef {Object} ScopeSignal
* @property { 'scope' } kind
* @property { 'module' | 'begin' | 'fork' | 'function' | 'task' } type
* @property {string} name
* @property {ScopeSignal[]} body
*
* @typedef { VarSignal | ScopeSignal } VcdSignal
*
* @typedef {Object} VcdInfo
* @property {number} t0
* @property {number} timescale
* @property {string} version
* @property {string} date
* @property {string} status
* @property {VcdSignal} wires
*
* @typedef {Object} VcdBasicInfo
* @property {number} time
* @property {number} tgcd
* @property {VcdInfo} vcdInfo
* @property {any} signalValues
*/
// 结果变量
const vcdBasicInfo = {
signalValues: {},
vcdInfo: undefined,
tgcd: undefined,
time: undefined
};
/**
*
* @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;
}
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
/**
*
* @param {BigInt} val
* @returns {string | number}
*/
function numberOrString(val) {
if (val < MAX_SAFE_INTEGER) {
return Number(val);
}
const stringNumber = '0x' + val.toString(16);
return stringNumber;
}
/**
*
* @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;
}
/**
*
* @returns {{
* write: (piece: Uint8Array) => void
* consume: (arraybuffer: ArrayBuffer, config?: consumeConfig) => void
* getBasicInfo: () => VcdBasicInfo
* clean: () => void
* }}
*/
async function makeVcdStream() {
const vcdstream = await getVcdStream();
// 使用 vcdstream 的 any 回调获取波形数据,并按照正确的格式进行解码和存储
// 这段处理来自 https://github.com/wavedrom/vcd 的 vcd-pipe-deso.js 的 58 行
// 请严格对准转换规则
vcdstream.change.any((id, time, cmd, value, mask) => {
const time53 = Number(time);
vcdBasicInfo.tgcd = gcd(vcdBasicInfo.tgcd, time53);
vcdBasicInfo.signalValues[id] = vcdBasicInfo.signalValues[id] || { kind: '', wave: [] };
// if (id === 'x#') {
// console.log(id, time, cmd, value, mask);
// console.log(time > MAX_SAFE_INTEGER);
// }
if (cmd >= 14 && cmd <= 28) {
vcdBasicInfo.signalValues[id].kind = 'bit';
vcdBasicInfo.signalValues[id].wave.push([time53, cmd - 14]);
} else {
vcdBasicInfo.signalValues[id].kind = 'vec';
const point = [time53, numberOrString(value)];
if (mask !== 0n) {
point.push(numberOrString(mask));
}
vcdBasicInfo.signalValues[id].wave.push(point);
}
});
vcdstream.consume = (arraybuffer, config) => {
return consume(vcdstream, arraybuffer, config);
};
vcdstream.getBasicInfo = () => {
return vcdBasicInfo;
};
vcdstream.clean = () => {
vcdBasicInfo.signalValues = {};
vcdBasicInfo.vcdInfo = undefined;
vcdBasicInfo.tgcd = undefined;
vcdBasicInfo.time = undefined;
}
return vcdstream;
}
/**
*
* @param {ArrayBuffer} arraybuffer
* @param {consumeConfig} config
*/
function consume(vcdstream, arraybuffer, config) {
config = config || { maxChunkLength: 1 << 17, useGcd: true };
const maxChunkLength = config.maxChunkLength;
const uint8array = new Uint8Array(arraybuffer);
for (let i = 0; i < uint8array.length; i += maxChunkLength) {
const piece = uint8array.slice(i, i + maxChunkLength);
vcdstream.write(piece);
}
// 装载信息
if (vcdBasicInfo.time === undefined) {
vcdBasicInfo.time = Number(vcdstream.getTime());
}
if (vcdBasicInfo.vcdInfo === undefined) {
vcdBasicInfo.vcdInfo = vcdstream.info;
}
// 通过 gcd 来缩放时间
const tgcd = vcdBasicInfo.tgcd;
const signalValues = vcdBasicInfo.signalValues;
vcdBasicInfo.time /= tgcd;
vcdBasicInfo.vcdInfo.t0 /= tgcd;
vcdBasicInfo.vcdInfo.timescale = parseTimescale(vcdBasicInfo.vcdInfo.timescale);
for (const id of Object.keys(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)) {
vcdBasicInfo.tgcd = scaleGcd;
vcdBasicInfo.vcdInfo.timescale += exp;
}
}
}
async function getVcdStream() {
const wasm = await createVCD();
const vcdstream = await webVcdParser(wasm);
return vcdstream;
}
// 测试时关闭该函数
// 部署时激活该函数
try {
self.getVcdStream = getVcdStream;
self.makeVcdStream = makeVcdStream;
self.vcdBasicInfo = vcdBasicInfo;
} catch (error) {}
module.exports = {
getVcdStream,
makeVcdStream,
vcdBasicInfo
};