update loading
This commit is contained in:
parent
7a16c7add2
commit
284bb4bd1a
@ -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
9529
public/test.vcd
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
40
src/App.vue
40
src/App.vue
@ -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>
|
||||
|
181
src/components/render/cursor.vue
Normal file
181
src/components/render/cursor.vue
Normal 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>
|
48
src/components/render/index.vue
Normal file
48
src/components/render/index.vue
Normal 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>
|
144
src/components/render/time-scale.vue
Normal file
144
src/components/render/time-scale.vue
Normal 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>
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
 {{ 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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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> {{ signal.name }}</div>
|
||||
<div><span :class="`iconfont ${makeIconClass(signal)}`"></span> {{ 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;
|
||||
}
|
||||
|
||||
|
@ -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
146
src/hook/render.js
Normal 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
|
||||
}
|
@ -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> ' + mod.name;
|
||||
} else {
|
||||
htmlString += '<span class="iconfont icon-wave-square"></span> ' + 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> ${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
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user