update loading

This commit is contained in:
锦恢 2024-03-05 07:35:25 +08:00
parent 7a16c7add2
commit 284bb4bd1a
18 changed files with 10314 additions and 79 deletions

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4440655 */
src: url('iconfont.woff2?t=1709106897565') format('woff2'),
url('iconfont.woff?t=1709106897565') format('woff'),
url('iconfont.ttf?t=1709106897565') format('truetype');
src: url('iconfont.woff2?t=1709378049768') format('woff2'),
url('iconfont.woff?t=1709378049768') format('woff'),
url('iconfont.ttf?t=1709378049768') format('truetype');
}
.iconfont {
@ -13,6 +13,42 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-parameter:before {
content: "\e6be";
}
.icon-task-:before {
content: "\e602";
}
.icon-fork:before {
content: "\e735";
}
.icon-prevent:before {
content: "\e69d";
}
.icon-Function:before {
content: "\e90e";
}
.icon-R:before {
content: "\e6ec";
}
.icon-integer:before {
content: "\e603";
}
.icon-String:before {
content: "\e614";
}
.icon-brackets:before {
content: "\e613";
}
.icon-register:before {
content: "\e611";
}

Binary file not shown.

9529
public/test.vcd Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,15 @@
--display-signal-info-height: 50px;
--signal-default-color: #4CAF50;
--main-color: #CB81DA;
--sidebar-width: 280px;
--right-nav-width: 60px;
--time-scale-height: 50px;
}
html, body {
background-color: var(--background);
color: var(--foreground);
overflow: hidden;
}
body::-webkit-scrollbar {
@ -76,3 +80,11 @@ body::-webkit-scrollbar {
.el-checkbox-button__inner {
font-size: 16px !important;
}
.el-select {
width: fit-content !important;
}
a {
color: var(--main-color);
}

View File

@ -1,13 +1,12 @@
<template>
<div class="vcd-render-wrapper" @mousemove="getMousemove">
</div>
<!-- 主渲染区 -->
<MainRender></MainRender>
<!-- 左上角的 sidebar用于显示 -->
<Sidebar></Sidebar>
<!-- 工具栏 -->
<div class="vcd-toolbar">
</div>
<!-- 显示当前信号树形关系 -->
@ -18,15 +17,17 @@
<script>
import { onMounted, reactive } from 'vue';
import { getVcdStream, readVcdFile } from '@/hook/utils';
import { emitter, globalLookup, globalSetting } from '@/hook/global';
import { emitter, globalLookup, globalSetting, signalValues } from '@/hook/global';
import Sidebar from '@/components/sidebar.vue';
import RightNav from '@/components/right-nav.vue';
import MainRender from '@/components/render';
export default {
components: {
RightNav,
Sidebar
Sidebar,
MainRender
},
setup() {
@ -50,6 +51,10 @@ export default {
document.body.style.setProperty('--el-border-color-light', 'var(--sidebar)');
document.body.style.setProperty('--el-border-color-lighter', 'var(--sidebar)');
document.body.style.setProperty('--el-bg-color-overlay', 'transplant');
document.body.style.setProperty('--el-color-info-light-9', 'var(--vscode-focusBorder)');
document.body.style.setProperty('--el-color-info', 'var(--foreground)');
document.body.style.setProperty('--el-color-info-light-8', 'var(--vscode-focusBorder)');
document.body.style.setProperty('--el-bg-color-overlay', 'transplant');
// signal height
@ -58,21 +63,34 @@ export default {
const uint8array = await readVcdFile();
const vcdstream = await getVcdStream();
vcdstream.change.any((id, time, cmd, value, mask) => {
if (signalValues[id] === undefined) {
signalValues[id] = [];
}
signalValues[id].push({time, cmd, value, mask});
});
const maxChunkLength = 1 << 17;
for (let i = 0; i < uint8array.length; i += maxChunkLength) {
const piece = uint8array.slice(i, i + maxChunkLength);
vcdstream.write(piece);
}
for (const topModule of vcdstream.info.wires.body) {
VcdInfo.topModules.push(topModule);
}
globalLookup.status = vcdstream.info.status;
globalLookup.version = vcdstream.info.version;
globalLookup.timescale = vcdstream.info.timescale;
globalLookup.date = vcdstream.info.date;
globalLookup.t0 = vcdstream.info.t0;
emitter.emit('meta-ready', null);
//
//
if (VcdInfo.topModules.length > 0) {
@ -86,13 +104,8 @@ export default {
}
});
function getMousemove(event) {
console.log(event);
}
return {
VcdInfo,
getMousemove
VcdInfo
}
}
}
@ -107,9 +120,4 @@ export default {
-moz-osx-font-smoothing: grayscale;
}
.vcd-render-wrapper {
height: 98vh;
width: 98vw;
}
</style>

View File

@ -0,0 +1,181 @@
<template>
<div class="vertical-cursor-wrapper"
@click="makeCursor"
:style="`left: ${currentX}px`">
<div class="current-display-cursor-up">
<div class="current-time-value">{{ currentX }} px</div>
<div class="cursor-down-arrow"></div>
</div>
<div class="vertical-line"></div>
<div class="current-display-cursor-down">
<div class="current-time-value">{{ currentX }} px</div>
<div class="cursor-up-arrow"></div>
</div>
</div>
<div v-show="showFixedOne"
class="vertical-cursor-wrapper"
@click="makeCursor"
:style="`left: ${fixedLeft}px`">
<div class="fix-current-display-cursor-up">
{{ globalLookup.view.currentX }} px
</div>
<div class="fix-vertical-line"></div>
<div class="fix-current-display-cursor-down">
{{ globalLookup.view.currentX }} px
</div>
</div>
</template>
<script>
import { reactive, ref } from 'vue';
import { globalLookup } from '../../hook/global';
export default {
name: 'vertical-cursor',
setup() {
// see --sidebar-width
const left = 280 + 20;
// see --right-nav-width
const right = document.body.clientWidth - 60;
const showFixedOne = ref(false);
const fixedLeft = ref(0);
const currentX = ref(left);
const boxShift = 25;
document.onmousemove = event => {
const x = event.x;
if (x < left || x > right) {
return;
}
currentX.value = x - boxShift;
}
/**
* @param { PointerEvent } event
*/
function makeCursor(event) {
console.log(event.x);
showFixedOne.value = true;
fixedLeft.value = event.x - boxShift;
globalLookup.view.currentX = event.x - boxShift;
}
return {
makeCursor,
fixedLeft,
showFixedOne,
boxShift,
globalLookup,
currentX
}
}
}
</script>
<style>
.vertical-cursor-wrapper {
position: absolute;
height: 100vh;
padding: 0;
top: 0;
cursor: crosshair;
z-index: 55;
display: flex;
flex-direction: column;
align-items: center; /* 沿着横轴(水平方向)对齐 */
justify-content: center; /* 沿着纵轴(垂直方向)对齐 */
}
.current-display-cursor-up {
height: var(--time-scale-height);
width: 50px;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
position: relative;
}
.current-display-cursor-down {
height: var(--time-scale-height);
width: 50px;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
position: relative;
}
.current-time-value {
border-radius: .4em;
background-color: var(--color-deepPurple);
color: var(--sidebar-item-text);
padding: 5px;
width: fit-content;
white-space: nowrap;
}
.cursor-down-arrow {
border-left: 5px solid var(--color-deepPurple);
border-top: 5px solid var(--color-deepPurple);
transform: rotate(225deg);
position: absolute;
width: 10px;
height: 10px;
/* 25px - 10 * 2 / \sqrt{2} */
left: 17.93px;
bottom: 0;
}
.cursor-up-arrow {
border-left: 5px solid var(--color-deepPurple);
border-top: 5px solid var(--color-deepPurple);
transform: rotate(45deg);
position: absolute;
width: 10px;
height: 10px;
left: 17.93px;
top: 0;
}
.vertical-line {
width: 1px;
height: calc(100vh - 2 * var(--time-scale-height));
}
.fix-vertical-line {
width: 1px;
height: calc(100vh - 2 * var(--time-scale-height));
background-color: var(--color-deepPurple);
}
.fix-current-display-cursor-up {
height: var(--time-scale-height);
width: 50px;
background-color: var(--color-deepPurple);
border-radius: .5em .5em .5em .5em;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
}
.fix-current-display-cursor-down {
height: var(--time-scale-height);
width: 50px;
background-color: var(--color-deepPurple);
border-radius: .5em .5em .5em .5em;
display: flex;
align-items: center;
justify-content: space-around;
color: var(--sidebar-item-text);
font-size: 14px;
}
</style>

View File

@ -0,0 +1,48 @@
<template>
<VerticalCursor></VerticalCursor>
<TimeScale></TimeScale>
<div class="vcd-render-wrapper" >
<div style="height: var(--time-scale-height);"></div>
</div>
</template>
<script>
import { reactive, ref } from 'vue';
import { emitter, globalLookup, globalSetting } from '@/hook/global';
import VerticalCursor from '@/components/render/cursor.vue';
import TimeScale from '@/components/render/time-scale.vue';
export default {
name: 'main-render',
components: {
VerticalCursor,
TimeScale
},
setup() {
const cursorX = ref(0);
return {
cursorX
}
}
}
</script>
<style>
.vcd-render-wrapper {
height: 98vh;
width: 98vw;
cursor: crosshair;
padding: 10px;
}
.render-canvas {
}
</style>

View File

@ -0,0 +1,144 @@
<template>
<div class="time-scale-up-wrapper">
</div>
<div class="time-scale-wrapper" :style="`left: ${currentX}px; width: ${300}px;`">
<div class="current-display-cursor-up">
<div class="time-scale-value">{{ currentX }} {{ timeScaleManage.unit }}</div>
<div class="time-scale-down"></div>
</div>
<div class="time-scale-line"></div>
<div class="current-display-cursor-down">
<div class="time-scale-value">{{ currentX }} {{ timeScaleManage.unit }}</div>
<div class="time-scale-up"></div>
</div>
</div>
<div class="time-scale-down-wrapper">
</div>
</template>
<script>
import { onMounted, reactive } from 'vue';
import { globalLookup, emitter } from '@/hook/global';
import { handleTimeScale } from '@/hook/utils';
export default {
name: 'time-scale',
setup() {
// see --sidebar-width
const left = 280 + 20;
// see --right-nav-width
const right = document.body.clientWidth - 60;
const timeScaleManage = reactive({
scale: 1,
unit: 'ns'
});
//
emitter.on('meta-ready', () => {
const { scale, unit } = handleTimeScale(globalLookup.timescale);
timeScaleManage.scale = scale;
timeScaleManage.unit = unit;
console.log(timeScaleManage.unit);
const viewInfo = globalLookup.view;
});
document.addEventListener('wheel', event => {
if ((event.wheelDelta && event.ctrlKey) || event.detail) {
event.preventDefault();
}
if (event.ctrlKey) {
// console.log(event.wheelDelta);
console.log('deltaX', event.deltaX);
console.log('deltaY', event.deltaY);
} else if (event.shiftKey) {
// console.log(event.wheelDelta);
console.log('deltaX', event.deltaX);
console.log('deltaY', event.deltaY);
}
}, { capture: false, passive: false });
const currentX = 300;
return {
currentX,
timeScaleManage
}
}
}
</script>
<style>
.time-scale-up-wrapper {
position: absolute;
top: 0;
left: calc(var(--sidebar-width) + 20px);
width: calc(100vw - var(--sidebar-width) - var(--right-nav-width) - 20px);
height: var(--time-scale-height);
background-color: var(--sidebar);
z-index: 50;
cursor: crosshair;
}
.time-scale-down-wrapper {
position: absolute;
bottom: 0;
left: 0;
width: calc(100vw - var(--right-nav-width));
height: var(--time-scale-height);
background-color: var(--sidebar);
z-index: 50;
cursor: crosshair;
}
.time-scale-down {
position: absolute;
bottom: 0;
width: 1px;
height: 10px;
border: 1px solid var(--scrollbar-hover);
background-color: var(--scrollbar-hover);
}
.time-scale-up {
position: absolute;
top: 0;
width: 1px;
height: 10px;
border: 1px solid var(--scrollbar-hover);
background-color: var(--scrollbar-hover);
}
.time-scale-wrapper {
position: absolute;
height: 100vh;
padding: 0;
top: 0;
cursor: crosshair;
z-index: 55;
display: flex;
flex-direction: column;
align-items: center;
/* 沿着横轴(水平方向)对齐 */
justify-content: center;
/* 沿着纵轴(垂直方向)对齐 */
}
.time-scale-value {}
.time-scale-line {
width: 1px;
background-color: var(--scrollbar-hover);
height: calc(100vh - 2 * var(--time-scale-height));
}
</style>

View File

@ -95,6 +95,7 @@ export default {
position: fixed;
top: 0;
right: 0;
z-index: 60;
}
.vcd-function-panel {
@ -107,12 +108,13 @@ export default {
.vcd-function-option {
width: fit-content;
height: 100vh;
background-color: var(--sidebar);
box-shadow: 0 0 15px 1px rgb(16, 16, 16);
}
.vcd-control-panel-wrapper {
width: 60px;
height: 60px;
width: var(--right-nav-width);
height: var(--right-nav-width);
display: flex;
align-items: center;
justify-content: space-around;

View File

@ -18,11 +18,18 @@
<div class="setting-option">
<span class="option-title">{{ t('search-scope') }}</span>
<el-checkbox-group v-model="globalSetting.searchScope" size="default">
<el-checkbox-button label="wire" border>wire </el-checkbox-button>
<el-checkbox-button label="reg" border>reg </el-checkbox-button>
<el-checkbox-button label="module" border>module</el-checkbox-button>
</el-checkbox-group>
<el-select
v-model="globalSetting.searchScope"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="Select"
>
<el-option v-for="name in scopes" :key="name"
:label="name" :value="name" />
<el-option v-for="name in variables" :key="name"
:label="name" :value="name" />
</el-select>
</div>
</div>
@ -69,7 +76,7 @@
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { globalSetting } from '@/hook/global';
import { debounceWrapper } from '@/hook/utils';
import { debounceWrapper, scopes, variables } from '@/hook/utils';
export default {
name: 'dide-setting',
@ -101,7 +108,9 @@ export default {
languageSetting,
globalSetting,
safeModifySignalTrackHeight,
locale
locale,
scopes,
variables
}
}
}

View File

@ -1,6 +1,7 @@
<template>
<div class="vcd-sidebar-wrapper">
<div class="display-signal-wrapper">
<div style="height: var(--time-scale-height);"></div>
<div class="display-signal-item"
v-for="(signal, index) in globalLookup.currentWires" :key="index">
<div class="signal-color-vendor">
@ -25,6 +26,7 @@
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { emitter, globalLookup, globalSetting } from '@/hook/global';
import { makeIconClass } from '@/hook/utils';
export default {
name: 'sidebar',
@ -36,22 +38,9 @@ export default {
}
function makeSignalIconClass(signal) {
if (signal.type === 'wire') {
return 'iconfont icon-wave-square';
} else if (signal.type === 'reg') {
return 'iconfont icon-register';
} else {
return 'iconfont icon-wave-square';
}
return 'iconfont ' + makeIconClass(signal);
}
function makeSignalInfo(signal) {
let htmlString = '';
}
return {
t,
globalLookup,
@ -67,10 +56,11 @@ export default {
position: absolute;
top: 0px;
left: 0px;
z-index: 60;
}
.add-signal-button {
margin: 10px;
margin-top: 10px;
padding: 10px 25px;
background-color: var(--color-deepPurple);
border-radius: 0 0 .8em .8em;
@ -85,12 +75,11 @@ export default {
}
.display-signal-wrapper {
margin: 10px;
padding: 10px;
background-color: var(--sidebar);
border: solid 1px var(--sidebar-border);
min-width: var(--sidebar-min-width);
min-height: 50vh;
min-width: var(--sidebar-width);
height: calc(100vh - 90px);
box-shadow: 0 0 15px 1px rgb(16, 16, 16);
}

View File

@ -55,7 +55,7 @@ export default {
<style>
.vcd-module-info {
width: 300px;
width: fit-content;
padding-right: 5px;
}
@ -64,8 +64,8 @@ export default {
}
.vcd-module-display-wrapper {
height: 80vh;
overflow-x: scroll;
height: 90vh;
overflow: scroll;
}
.vcd-treeview {
@ -82,7 +82,7 @@ export default {
flex: 1;
display: flex;
z-index: 5;
max-height: 98vh;
max-height: 99vh;
/* overflow: scroll; */
transition: flex .5s ease-in-out;
}

View File

@ -5,7 +5,7 @@
<span class="module-tag-status" @click.stop="expandManage.click">
<div :class="expandManage.expandTagClass"></div>
</span>
<span class="iconfont icon-memory-chip"></span>
<span :class="`iconfont ${makeIconClass(module)}`"></span>
&ensp;{{ module.name }}
</div>
@ -26,6 +26,7 @@
/* eslint-disable */
import { reactive, onMounted } from 'vue';
import { emitter, globalLookup } from '@/hook/global';
import { isScope, isVariable, makeIconClass } from '@/hook/utils';
export default {
name: "modules",
@ -40,9 +41,9 @@ export default {
const mods = [];
for (const wire of module.body) {
if (wire.body) {
if (isScope(wire)) {
mods.push(wire);
} else {
} else if (isVariable(wire)) {
signals.push(wire);
}
}
@ -51,7 +52,7 @@ export default {
emitter.emit('tree-view', signals);
// color change into selected
globalLookup.currentModule = module;
console.log(module);
}
const expandManage = reactive({
@ -68,10 +69,10 @@ export default {
return {
module,
mods,
signals,
clickItem,
expandManage,
globalLookup
globalLookup,
makeIconClass
}
}
};
@ -97,6 +98,8 @@ export default {
height: 27px;
display: flex;
align-items: center;
white-space: nowrap;
text-overflow: ellipsis;
}
.vcd-treeview-item::selection {

View File

@ -12,13 +12,13 @@
<div class="search-result-wrapper" v-if="searchManage.content.trim().length > 0">
<div class="search-result">
<div v-if="searchManage.searchResult.length > 0">
<div class="search-result-item"
v-for="(searchResult, index) in searchManage.searchResult"
:key="index"
:class="globalLookup.currentWires.has(searchResult.module) ? 'vcd-treeview-selected' : ''"
v-html="searchResult.htmlString"
@click="clickItem(searchResult.module)">
</div>
<div v-for="(searchResult, index) in searchManage.searchResult"
:key="index"
:class="globalLookup.currentWires.has(searchResult.module) ? 'vcd-treeview-selected' : ''"
@click="clickItem(searchResult.module)">
<div v-html="searchResult.htmlString" class="search-result-item"></div>
</div>
</div>
<div v-else>
{{ t('search-nothing') }}
@ -76,6 +76,7 @@ export default {
});
function clickItem(signal) {
console.log(signal);
if (!signal.link) {
return;
}
@ -108,6 +109,7 @@ export default {
}
.search-result-wrapper {
position: absolute;
padding: 10px;
margin: 10px;
background-color: var(--sidebar);
@ -115,7 +117,6 @@ export default {
color: var(--sidebar-item-text);
border-radius: .5em;
min-width: 500px;
position: absolute;
z-index: 10;
}
@ -123,6 +124,8 @@ export default {
overflow-x: scroll;
overflow-y: scroll;
max-height: 80vh;
max-width: 600px;
padding-right: 5px;
}
@ -131,6 +134,7 @@ export default {
align-items: center;
height: 25px;
margin: 3px;
width: 800px;
padding-left: 3px;
cursor: pointer;
}

View File

@ -7,7 +7,7 @@
@click="clickItem(signal)"
class="vcd-signal-signal-item"
:class="globalLookup.currentWires.has(signal) ? 'vcd-treeview-selected' : ''">
<div><span class="iconfont icon-wave-square"></span>&ensp;{{ signal.name }}</div>
<div><span :class="`iconfont ${makeIconClass(signal)}`"></span>&ensp;{{ signal.name }}</div>
<div>
<div :class="signal.size > 1 ? 'vcd-signal-signal-caption' : ''">
{{ makeSignalCaption(signal) }}
@ -23,6 +23,8 @@ import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { emitter, globalLookup } from '@/hook/global';
import { makeIconClass } from '@/hook/utils';
import { makeWaveCanvas, removeWaveCanvas } from '@/hook/render';
/* eslint-disable */
export default {
@ -37,7 +39,7 @@ export default {
});
emitter.on('tree-view', sendWires => {
signals.content.length = 0;
signals.content = [];
signals.content = sendWires;
});
@ -48,8 +50,10 @@ export default {
function clickItem(signal) {
if (globalLookup.currentWires.has(signal)) {
globalLookup.currentWires.delete(signal);
removeWaveCanvas(signal);
} else {
globalLookup.currentWires.add(signal);
makeWaveCanvas(signal);
}
}
@ -58,7 +62,8 @@ export default {
clickItem,
globalLookup,
makeSignalCaption,
t
t,
makeIconClass
}
}
}
@ -73,10 +78,34 @@ export default {
color: var(--signal-default-color);
}
.icon-brackets {
color: #75BEDF;
}
.icon-register {
color:#885dff;
}
.icon-integer {
color: #C76B3C;
}
.icon-String {
color: #8CC265;
}
.icon-R {
color: #5fb4d8;
}
.icon-task- {
color: orange;
}
.vcd-signal-signals-display {
color: var(--sidebar-item-text);
padding: 0px 8px;
height: 80vh;
height: 90vh;
overflow-x: scroll;
}

View File

@ -23,6 +23,16 @@ const globalLookup = reactive({
// 初始时间,一般都是 0
t0: 0,
// 当前视图的左上角的坐标
view : {
x: 0,
y: 0,
gridGap: 300,
currentBaseUnit: 100,
currentX: 0,
currentScale: 1
},
initcurrentModule(module) {
if (this.currentModule === undefined && module) {
this.currentModule = module;
@ -41,12 +51,17 @@ const globalSetting = reactive({
displayParentOnly: false,
displaySignalHeight: 50,
searchMode: 'so', // so, mo, sm
searchScope: ['wire', 'reg'],
searchScope: ['wire', 'reg', 'integer'],
displaySignalInfoScope: ['width', 'parent'],
})
minGridWidth: 300
});
const signalValues = {};
export {
emitter,
globalLookup,
globalSetting
globalSetting,
signalValues
};

146
src/hook/render.js Normal file
View File

@ -0,0 +1,146 @@
import { globalLookup, globalSetting, signalValues } from "./global";
let mainRenderEl = null;
const canvasMap = {};
/**
*
* @returns {Element}
*/
function getMainRenderEl() {
if (!mainRenderEl) {
const el = document.querySelectorAll('.vcd-render-wrapper')[0];
if (el) {
mainRenderEl = el;
}
}
return mainRenderEl;
}
/**
*
* @param {{
* time: BigInt,
* cmd: number,
* value?: BigInt,
* mask?: BigInt
* }} dataItem
* @returns
*/
function isBlocked(dataItem) {
const mask = dataItem.mask;
if (mask && mask === 1n) {
return true;
}
if (mask && mask === 0n) {
return false;
}
if (mask === undefined && dataItem.cmd > 15) {
return true;
}
return false;
}
/**
* @param {{
* kind: 'var' | 'scope',
* type: 'wire' | 'reg' | 'module',
* name: string,
* size: number,
* link: string
* }} signal
*/
function makeWaveCanvas(signal) {
const mainRender = getMainRenderEl();
const canvas = document.createElement('canvas');
canvas.id = 'wave-' + signal.link;
canvas.className = 'render-canvas';
canvas.width = document.body.clientWidth;
canvas.height = globalSetting.displaySignalHeight;
mainRender.appendChild(canvas);
canvasMap[signal] = canvas;
const ctx = canvas.getContext('2d');
const datas = signalValues[signal.link];
if (signal.size === 1) {
drawSimpleSignal(ctx, datas);
} else {
// adwa
}
}
/**
* @param {{
* kind: 'var' | 'scope',
* type: 'wire' | 'reg' | 'module',
* name: string,
* size: number,
* link: string
* }} signal
*/
function removeWaveCanvas(signal) {
const canvas = canvasMap[signal];
if (canvas instanceof HTMLCanvasElement) {
canvas.parentNode.removeChild(canvas);
}
}
/**
*
* @param {CanvasRenderingContext2D} ctx
* @param {Array<{
* time: BigInt,
* cmd: number,
* value?: BigInt,
* mask?: BigInt
* }>} signals
*/
function drawSimpleSignal(ctx, signals) {
let last_data = null;
let last_x = 300;
ctx.lineWidth = 3;
ctx.lineJoin = 'round';
ctx.beginPath();
for (const data of signals) {
console.log(data);
if (last_data === null) {
last_data = data;
last_x = 300;
let last_y = Math.max((15 - last_data.cmd) * 30, 0) + 10;
ctx.moveTo(last_x, last_y);
} else {
// 高阻态
const start = last_data.time;
const end = data.time;
ctx.strokeStyle = isBlocked(last_data) ? 'rgb(224,108,117)' : 'rgb(10, 200, 10)';
let last_y = Math.max((15 - last_data.cmd) * 30, 0) + 10;
const duration = parseInt(end - start);
const current_x = last_x + 100;
console.log(current_x, last_y, duration);
ctx.lineTo(current_x, last_y);
if (last_data.cmd !== data.cmd) {
ctx.strokeStyle = isBlocked(data) ? 'rgb(224,108,117)' : 'rgb(10, 200, 10)';
let current_y = Math.max((15 - data.cmd) * 30, 0) + 10;
ctx.lineTo(current_x, current_y);
}
last_data = data;
last_x = current_x;
}
}
ctx.stroke();
}
export {
getMainRenderEl,
makeWaveCanvas,
removeWaveCanvas
}

View File

@ -15,7 +15,7 @@ async function getVcdStream() {
* @returns {Promise<Uint8Array>}
*/
async function readVcdFile() {
const response = await fetch('cpu.vcd');
const response = await fetch('test.vcd');
const blob = await response.blob();
const reader = new FileReader();
return new Promise((resolve, reject) => {
@ -40,6 +40,40 @@ function debounceWrapper(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 {{
@ -76,20 +110,27 @@ function makeSearchResultItem(searchString, module, searchScope, caseSensitivity
}
let htmlString = '';
let depString = '';
let currentCounts = 0;
for (let i = deps.length - 1; i >= 0; -- i) {
const mod = deps[i];
if (mod.type === 'module') {
htmlString += '<span class="iconfont icon-memory-chip"></span>&ensp;' + mod.name;
} else {
htmlString += '<span class="iconfont icon-wave-square"></span>&ensp;' + mod.name;
}
// 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}`;
depString += iconText;
htmlString += iconText;
if (i > 0) {
depString += '<br>';
htmlString += '<div class="dep-arrow"></div>';
}
}
return {
htmlString,
module
module,
depString
};
}
}
@ -97,10 +138,49 @@ function makeSearchResultItem(searchString, module, searchScope, caseSensitivity
return null;
}
/**
*
* @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 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
makeSearchResultItem,
handleTimeScale,
isScope,
isVariable,
makeIconClass,
scopes,
variables
};