230 lines
6.1 KiB
JavaScript
230 lines
6.1 KiB
JavaScript
/* eslint-disable no-undef */
|
|
'use strict';
|
|
|
|
const createVCD = require('../out/vcd.js');
|
|
const webVcdParser = require('../lib/web-vcd-parser.js');
|
|
|
|
/**
|
|
* @typedef {Object} consumeConfig
|
|
* @property {number} maxChunkLength
|
|
* @property {boolean} useGcd
|
|
*
|
|
* @typedef {Object} vcdValue
|
|
* @property {string} kind
|
|
* @property {number[]} wave
|
|
*
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* @param {{
|
|
* wasmBinary: any
|
|
* }} moduleArg
|
|
* @returns {{
|
|
* write: (piece: Uint8Array) => void
|
|
* consume: (arraybuffer: ArrayBuffer, config?: consumeConfig) => void
|
|
* getBasicInfo: () => VcdBasicInfo
|
|
* clean: () => void
|
|
* }}
|
|
*/
|
|
async function makeVcdStream(moduleArg) {
|
|
const vcdstream = await getVcdStream(moduleArg);
|
|
// 使用 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);
|
|
const values = vcdBasicInfo.signalValues[id] || { kind: '', wave: [] };
|
|
vcdBasicInfo.signalValues[id] = values;
|
|
|
|
if (cmd >= 14 && cmd <= 28) {
|
|
values.kind = 'bit';
|
|
values.wave.push([time53, cmd - 14]);
|
|
// if (values.wave.length >= 2) {
|
|
// const first = values.wave.at(-2)[0];
|
|
// const second = values.wave.at(-1)[0];
|
|
// if (first > second) {
|
|
// console.log(first, second);
|
|
// }
|
|
// }
|
|
|
|
} else {
|
|
values.kind = 'vec';
|
|
const point = [time53, numberOrString(value)];
|
|
if (mask !== 0n) {
|
|
point.push(numberOrString(mask));
|
|
}
|
|
values.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(moduleArg) {
|
|
const wasm = await createVCD(moduleArg);
|
|
const vcdstream = await webVcdParser(wasm);
|
|
return vcdstream;
|
|
}
|
|
|
|
// 测试时关闭该函数
|
|
// 部署时激活该函数
|
|
try {
|
|
self.getVcdStream = getVcdStream;
|
|
self.makeVcdStream = makeVcdStream;
|
|
self.vcdBasicInfo = vcdBasicInfo;
|
|
} catch (error) {}
|
|
|
|
module.exports = {
|
|
getVcdStream,
|
|
makeVcdStream,
|
|
vcdBasicInfo
|
|
}; |