更新架构,进行依赖注入
This commit is contained in:
parent
eabdba2602
commit
0aa38f5133
@ -23,7 +23,8 @@
|
|||||||
const skinBinary = await r2.arrayBuffer();
|
const skinBinary = await r2.arrayBuffer();
|
||||||
return [ netJson, skinBinary ];
|
return [ netJson, skinBinary ];
|
||||||
}
|
}
|
||||||
window.avoidWasm = 'avoid.wasm';
|
window.moduleName = 'half_adder';
|
||||||
|
// window.avoidWasm = 'avoid.wasm';
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ body::-webkit-scrollbar {
|
|||||||
|
|
||||||
* hr {
|
* hr {
|
||||||
border: none;
|
border: none;
|
||||||
background-color: var(--vscode-focusBorder);
|
background-color: var(--main-color);
|
||||||
height: 2px;
|
height: 1.5px;
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -36,6 +36,8 @@ onMounted(async () => {
|
|||||||
background: 'rgba(0, 0, 0, 0.7)'
|
background: 'rgba(0, 0, 0, 0.7)'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
globalLookup.topModuleName = window.moduleName;
|
||||||
|
|
||||||
// 初始化载入 netlist 的 json 文件
|
// 初始化载入 netlist 的 json 文件
|
||||||
const [ netJson, skinBinary ] = await window.readNetFile();
|
const [ netJson, skinBinary ] = await window.readNetFile();
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vcd-right-nav">
|
<div class="netlist-right-nav">
|
||||||
|
|
||||||
<div class="vcd-function-panel">
|
<div class="netlist-function-panel">
|
||||||
<TreeView
|
<TreeView
|
||||||
v-show="controlPanel.currentIndex === 0"></TreeView>
|
v-show="controlPanel.currentIndex === 0"></TreeView>
|
||||||
<Setting
|
<Setting
|
||||||
@ -10,17 +10,17 @@
|
|||||||
v-show="controlPanel.currentIndex === 2"></About>
|
v-show="controlPanel.currentIndex === 2"></About>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="vcd-function-option">
|
<div class="netlist-function-option">
|
||||||
<div class="vcd-control-panel-wrapper">
|
<div class="netlist-control-panel-wrapper">
|
||||||
<div class="vcd-control-panel-icon digital-ide-icon" />
|
<div class="netlist-control-panel-icon digital-ide-icon" />
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="vcd-control-panel-wrapper"
|
<div class="netlist-control-panel-wrapper"
|
||||||
v-for="(section, index) of controlPanel.sections" :key="index"
|
v-for="(section, index) of controlPanel.sections" :key="index"
|
||||||
@click="controlPanel.click(index)"
|
@click="controlPanel.click(index)"
|
||||||
>
|
>
|
||||||
<div :class="controlPanel.currentIndex === index ? 'vcd-control-panel-active': ''"><span
|
<div :class="controlPanel.currentIndex === index ? 'netlist-control-panel-active': ''"><span
|
||||||
class="vcd-control-panel-icon"
|
class="netlist-control-panel-icon"
|
||||||
:class="section.iconClass"
|
:class="section.iconClass"
|
||||||
></span></div>
|
></span></div>
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +51,7 @@ emitter.on('right-nav', index => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.vcd-right-nav {
|
.netlist-right-nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -59,21 +59,21 @@ emitter.on('right-nav', index => {
|
|||||||
z-index: 230;
|
z-index: 230;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-function-panel {
|
.netlist-function-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
box-shadow: var(--gray-box-shadow-1);
|
box-shadow: var(--gray-box-shadow-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-function-option {
|
.netlist-function-option {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
box-shadow: var(--gray-box-shadow-1);
|
box-shadow: var(--gray-box-shadow-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-control-panel-wrapper {
|
.netlist-control-panel-wrapper {
|
||||||
width: var(--right-nav-width);
|
width: var(--right-nav-width);
|
||||||
height: var(--right-nav-width);
|
height: var(--right-nav-width);
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -84,17 +84,17 @@ emitter.on('right-nav', index => {
|
|||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-control-panel-wrapper:hover {
|
.netlist-control-panel-wrapper:hover {
|
||||||
color: var(--vscode-icon-foreground);
|
color: var(--vscode-icon-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-control-panel-icon {
|
.netlist-control-panel-icon {
|
||||||
width: 45px;
|
width: 45px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-control-panel-active {
|
.netlist-control-panel-active {
|
||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -1,20 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="netlist-tree-view">
|
<div class="netlist-tree-view">
|
||||||
<div class="search-nothing">
|
<div class="netlist-module-info">
|
||||||
<span class="iconfont icon-empty"></span>
|
<div class="netlist-signal-title">{{ t('module') }}</div>
|
||||||
<span>Mirror the unknown</span>
|
<hr>
|
||||||
|
<el-scrollbar height="86vh" style="padding-right: 7px;">
|
||||||
|
<modules
|
||||||
|
v-for="mod of treeviewData.modules"
|
||||||
|
:key="mod.name"
|
||||||
|
:module="mod"
|
||||||
|
></modules>
|
||||||
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
import modules from './modules.vue';
|
||||||
|
import { treeviewData } from './tree';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineComponent({ name: 'tree-view' });
|
defineComponent({ name: 'tree-view' });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.netlist-module-info {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.netlist-signal-title {
|
||||||
|
margin-left: 5px;
|
||||||
|
height: 2.5vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wave-square {
|
||||||
|
color: var(--signal-default-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-brackets {
|
||||||
|
color: #75BEDF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-register {
|
||||||
|
color: #885dff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-integer {
|
||||||
|
color: #C76B3C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-string {
|
||||||
|
color: #8CC265;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-real {
|
||||||
|
color: #5fb4d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-task {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
.search-nothing {
|
.search-nothing {
|
||||||
height: 40vh;
|
height: 40vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
166
src/components/treeview/modules.vue
Normal file
166
src/components/treeview/modules.vue
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<template>
|
||||||
|
<div class="module">
|
||||||
|
<div @click="clickItem()" class="netlist-treeview-item">
|
||||||
|
<span class="module-tag-status" @click.stop="expandManage.click">
|
||||||
|
<div :class="expandManage.expandTagClass"></div>
|
||||||
|
</span>
|
||||||
|
<span :class="`iconfont ${makeIconClass(module)}`"></span>
|
||||||
|
 {{ module.name }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 渲染这个 module scope 有的组件 -->
|
||||||
|
<div v-if="ports.length > 0 || cells.length > 0" class="netlist-subtree-wrapper">
|
||||||
|
<div style="width: 20px;"></div>
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<!-- ports -->
|
||||||
|
<div v-for="port in ports" :key="port.name" class="netlist-treeview-item">
|
||||||
|
<span class="module-tag-status">
|
||||||
|
</span>
|
||||||
|
<span class="iconfont icon-wave-square"></span>
|
||||||
|
 {{ port.name }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 例化模块 -->
|
||||||
|
<modules
|
||||||
|
v-for="(cell, index) in cells"
|
||||||
|
:module="cell.view"
|
||||||
|
:key="index"
|
||||||
|
></modules>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
/* eslint-disable */
|
||||||
|
import { ModuleView } from '@/hook/render/yosys';
|
||||||
|
import { defineComponent, reactive } from 'vue';
|
||||||
|
|
||||||
|
defineComponent({ name: 'modules' });
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
module: {
|
||||||
|
type: ModuleView,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const module = props.module;
|
||||||
|
|
||||||
|
// 当前 scope 的例化模块下的
|
||||||
|
const ports = [];
|
||||||
|
const cells = [];
|
||||||
|
|
||||||
|
// 初始化渲染的子视图 ports & modules
|
||||||
|
for (const portName of module.nameToPort.keys()) {
|
||||||
|
ports.push({
|
||||||
|
name: portName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cellName of module.nameToCell.keys()) {
|
||||||
|
const cell = module.nameToCell.get(cellName);
|
||||||
|
if (cell.view === module) {
|
||||||
|
// 防止递归
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cell.isInstantiation) {
|
||||||
|
cells.push({
|
||||||
|
name: cellName,
|
||||||
|
view: cell.belongModuleView
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickItem() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExpandStatus() {
|
||||||
|
if (ports.length === 0 && cells.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandManage = reactive({
|
||||||
|
expanded: getExpandStatus(),
|
||||||
|
expandTagClass: ports.length === 0 && cells.length === 0 ? '' : 'collapse-tag',
|
||||||
|
click() {
|
||||||
|
this.expanded = !this.expanded;
|
||||||
|
if (this.expandTagClass) {
|
||||||
|
this.expandTagClass = this.expanded ? 'expand-tag' : 'collapse-tag';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeIconClass() {
|
||||||
|
return 'icon-memory-chip';
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon-memory-chip {
|
||||||
|
color: #FF7043;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.netlist-subtree-wrapper {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.netlist-treeview-item {
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.netlist-treeview-item::selection {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.netlist-treeview-item:hover {
|
||||||
|
background-color: var(--sidebar-item-selected);
|
||||||
|
border-radius: .3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.netlist-treeview-selected {
|
||||||
|
color: #ddd;
|
||||||
|
background-color: var(--button-active) !important;
|
||||||
|
border-radius: .3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-tag-status {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-tag {
|
||||||
|
height: 5px;
|
||||||
|
width: 5px;
|
||||||
|
border-top: solid 1.7px var(--vscode-foreground);
|
||||||
|
border-left: solid 1.7px var(--vscode-foreground);
|
||||||
|
transform: rotate(225deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-tag {
|
||||||
|
height: 5px;
|
||||||
|
width: 5px;
|
||||||
|
border-top: solid 1.7px var(--vscode-foreground);
|
||||||
|
border-left: solid 1.7px var(--vscode-foreground);
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
</style>
|
13
src/components/treeview/tree.js
Normal file
13
src/components/treeview/tree.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ModuleView } from '@/hook/render/yosys';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
|
export const treeviewData = reactive({
|
||||||
|
/**
|
||||||
|
* @description 当前的顶层模块数量,内部数据结构是递归的
|
||||||
|
* 大部分情况下,它应该是只有一个元素
|
||||||
|
*
|
||||||
|
* @type {ModuleView[]}
|
||||||
|
*/
|
||||||
|
modules: []
|
||||||
|
});
|
||||||
|
|
@ -8,7 +8,7 @@ export function setDefaultCss() {
|
|||||||
document.body.style.setProperty('--el-color-primary-light-3', 'var(--main-color)');
|
document.body.style.setProperty('--el-color-primary-light-3', 'var(--main-color)');
|
||||||
document.body.style.setProperty('--el-text-color-secondary', 'var(--foreground)');
|
document.body.style.setProperty('--el-text-color-secondary', 'var(--foreground)');
|
||||||
document.body.style.setProperty('--el-text-color-regular', 'var(--foreground)');
|
document.body.style.setProperty('--el-text-color-regular', 'var(--foreground)');
|
||||||
document.body.style.setProperty('--el-border-color', 'var(--vscode-focusBorder)');
|
document.body.style.setProperty('--el-border-color', 'var(--main-color)');
|
||||||
document.body.style.setProperty('--el-fill-color-blank', 'var(--sidebar)');
|
document.body.style.setProperty('--el-fill-color-blank', 'var(--sidebar)');
|
||||||
document.body.style.setProperty('--el-fill-color-light', 'var(--vscode-button-hoverBackground)');
|
document.body.style.setProperty('--el-fill-color-light', 'var(--vscode-button-hoverBackground)');
|
||||||
document.body.style.setProperty('--el-switch-on-color', 'var(--main-color)');
|
document.body.style.setProperty('--el-switch-on-color', 'var(--main-color)');
|
||||||
@ -16,14 +16,15 @@ export function setDefaultCss() {
|
|||||||
document.body.style.setProperty('--el-border-color-light', 'var(--sidebar)');
|
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-border-color-lighter', 'var(--sidebar)');
|
||||||
document.body.style.setProperty('--el-bg-color-overlay', 'var(--sidebar)');
|
document.body.style.setProperty('--el-bg-color-overlay', 'var(--sidebar)');
|
||||||
document.body.style.setProperty('--el-color-info-light-9', 'var(--vscode-focusBorder)');
|
document.body.style.setProperty('--el-color-info-light-9', 'var(--main-color)');
|
||||||
document.body.style.setProperty('--el-color-info', 'var(--foreground)');
|
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-color-info-light-8', 'var(--main-color)');
|
||||||
|
document.body.style.setProperty('--el-fill-color-light', 'var(--sidebar-item-selected)');
|
||||||
|
|
||||||
// document.body.style.setProperty('--el-color-white', 'var(--background)');
|
// document.body.style.setProperty('--el-color-white', 'var(--background)');
|
||||||
|
|
||||||
// 设置全局宏
|
// 设置全局宏
|
||||||
document.body.style.setProperty('--vcd-render-padding', '30px');
|
document.body.style.setProperty('--netlist-render-padding', '30px');
|
||||||
document.body.style.setProperty('--sidebar-width', '330px');
|
document.body.style.setProperty('--sidebar-width', '330px');
|
||||||
document.body.style.setProperty('--toolbar-height', '60px');
|
document.body.style.setProperty('--toolbar-height', '60px');
|
||||||
|
|
||||||
|
@ -25,7 +25,18 @@ export const globalLookup = {
|
|||||||
/**
|
/**
|
||||||
* @type {Avoid}
|
* @type {Avoid}
|
||||||
*/
|
*/
|
||||||
Avoid: undefined
|
Avoid: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 当前仿真结果的顶层模块的名字
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
topModuleName: '',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 当前选择的实体,可以是 wire,也可以是 cell
|
||||||
|
*/
|
||||||
|
currentSelectEntity: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadSetting() {
|
function loadSetting() {
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
* @property {Record<string, YosysModel>} [models] 模型定义的映射(仅在使用 `-aig` 选项时存在)。
|
* @property {Record<string, YosysModel>} [models] 模型定义的映射(仅在使用 `-aig` 选项时存在)。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Module } from "./render/layout";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {number | string} WireId 信号ID
|
* @typedef {number | string} WireId 信号ID
|
||||||
*/
|
*/
|
||||||
|
@ -9,7 +9,8 @@ import { InstantiationRender } from './instantiation';
|
|||||||
import { CellRender } from './cell';
|
import { CellRender } from './cell';
|
||||||
import { ConnectionRender } from './connection';
|
import { ConnectionRender } from './connection';
|
||||||
import { WireRender } from './wire';
|
import { WireRender } from './wire';
|
||||||
import { pinkLog } from '../utils';
|
import { pinkLog, redLog } from '../utils';
|
||||||
|
import { treeviewData } from '@/components/treeview/tree';
|
||||||
|
|
||||||
export class NetlistRender {
|
export class NetlistRender {
|
||||||
/**
|
/**
|
||||||
@ -82,15 +83,20 @@ export class NetlistRender {
|
|||||||
* @type {YosysRawNet}
|
* @type {YosysRawNet}
|
||||||
*/
|
*/
|
||||||
this.rawNet = rawNet;
|
this.rawNet = rawNet;
|
||||||
|
this.topModuleName = globalLookup.topModuleName;
|
||||||
|
pinkLog(`当前模块 ${this.topModuleName}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Module}
|
||||||
|
*/
|
||||||
|
let topModule = undefined;
|
||||||
|
|
||||||
// 构造模块树
|
// 构造模块树
|
||||||
for (const [moduleName, rawModule] of Object.entries(rawNet.modules)) {
|
for (const [moduleName, rawModule] of Object.entries(rawNet.modules)) {
|
||||||
const top = parseInt(rawModule.attributes.top);
|
|
||||||
// 一开始只渲染 top 模块,或者告诉我当前的 top 是什么
|
|
||||||
if (top) {
|
|
||||||
const module = new Module(moduleName, rawModule);
|
const module = new Module(moduleName, rawModule);
|
||||||
|
if (moduleName === this.topModuleName) {
|
||||||
// 构造符合 elk 格式的节点数据
|
// 构造符合 elk 格式的节点数据
|
||||||
|
topModule = module;
|
||||||
const portNodes = module.makeNetsElkNodes();
|
const portNodes = module.makeNetsElkNodes();
|
||||||
const cellNodes = module.makeCellsElkNodes();
|
const cellNodes = module.makeCellsElkNodes();
|
||||||
const [constantNodes, connectionEdges] = module.makeConnectionElkNodes();
|
const [constantNodes, connectionEdges] = module.makeConnectionElkNodes();
|
||||||
@ -104,6 +110,42 @@ export class NetlistRender {
|
|||||||
this.nameToModule.set(moduleName, module);
|
this.nameToModule.set(moduleName, module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (topModule === undefined) {
|
||||||
|
redLog(`当前 netlist 中没有找到名为 ${this.topModuleName} 的顶层模块! Digital IDE 无法构建它的 treeview 视图!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 topModule + this.nameToModule 构建出用于渲染右侧列表的数据结构
|
||||||
|
const nameToModule = this.nameToModule;
|
||||||
|
|
||||||
|
// 记录当前需要不断递归解析成渲染视图的 module 的 name
|
||||||
|
// 从将每一个 module 的 tree 中关于 Cell 的部分完成指向(判断这个 Cell 是不是 例化模块,并找到它们对应的 Module 对象)
|
||||||
|
const moduleStack = [topModule.name];
|
||||||
|
|
||||||
|
while (moduleStack.length > 0) {
|
||||||
|
const moduleName = moduleStack.pop();
|
||||||
|
const currentModule = nameToModule.get(moduleName);
|
||||||
|
// 需要展示的数据:port, cell, (other netnames)
|
||||||
|
const view = currentModule.view;
|
||||||
|
for (const cellName of view.nameToCell.keys()) {
|
||||||
|
const cell = view.nameToCell.get(cellName);
|
||||||
|
const solveInstanceTypes = new Set();
|
||||||
|
if (nameToModule.has(cell.type)) {
|
||||||
|
cell.isInstantiation = true;
|
||||||
|
const instModule = nameToModule.get(cell.type);
|
||||||
|
cell.belongModuleView = instModule.view;
|
||||||
|
solveInstanceTypes.add(cell.type);
|
||||||
|
}
|
||||||
|
for (const instanceBelongModuleName of solveInstanceTypes) {
|
||||||
|
moduleStack.push(instanceBelongModuleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将默认的顶层模块的 view 加入响应数组中,响应地去渲染右侧的 view
|
||||||
|
const rootView = topModule.view;
|
||||||
|
treeviewData.modules.push(rootView);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,7 @@ export class InstantiationRender {
|
|||||||
this.data.push({
|
this.data.push({
|
||||||
x: node.x,
|
x: node.x,
|
||||||
y: node.y,
|
y: node.y,
|
||||||
|
name: node.name,
|
||||||
width: node.width,
|
width: node.width,
|
||||||
height: node.height,
|
height: node.height,
|
||||||
fill: 'var(--main-dark-color)',
|
fill: 'var(--main-dark-color)',
|
||||||
@ -39,4 +40,98 @@ export class InstantiationRender {
|
|||||||
ry: 3
|
ry: 3
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const data = this.data;
|
||||||
|
const rootRender = this.rootRender;
|
||||||
|
const id2manager = this.id2manager;
|
||||||
|
const _this = this;
|
||||||
|
|
||||||
|
let instantiationSelections = this.parentSelection.selectAll('g.instance')
|
||||||
|
.data(data)
|
||||||
|
.enter()
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'instance')
|
||||||
|
.attr("transform", d => `translate(${d.x}, ${d.y})`);
|
||||||
|
|
||||||
|
let instances = instantiationSelections.append('rect')
|
||||||
|
.attr('width', data => data.width)
|
||||||
|
.attr('height', data => data.height)
|
||||||
|
.attr('fill', d => d.fill);
|
||||||
|
|
||||||
|
let texts = instantiationSelections.append('text')
|
||||||
|
.attr('x', data => data.width / 2) // 文本的 x 坐标(居中)
|
||||||
|
.attr('y', data => data.height / 2) // 文本的 y 坐标(居中)
|
||||||
|
.attr('dominant-baseline', 'middle') // 文本垂直居中
|
||||||
|
.attr('text-anchor', 'middle') // 文本水平居中
|
||||||
|
.attr('fill', 'var(--foreground)') // 文本颜色
|
||||||
|
.attr('font-size', '0')
|
||||||
|
.transition()
|
||||||
|
.duration(1000)
|
||||||
|
.attr('font-size', '15px')
|
||||||
|
.attr('class', 'port-caption')
|
||||||
|
.text(data => data.name); // 设置文本内容
|
||||||
|
|
||||||
|
|
||||||
|
if (globalSetting.renderAnimation) {
|
||||||
|
instances.transition()
|
||||||
|
.duration(1000)
|
||||||
|
.attr('stroke', 'var(--main-color)')
|
||||||
|
.attr('stroke-width', 2)
|
||||||
|
.attr('rx', d => d.rx)
|
||||||
|
.attr('ry', d => d.ry);
|
||||||
|
} else {
|
||||||
|
instances.attr('stroke', 'var(--main-color)')
|
||||||
|
.attr('stroke-width', 2)
|
||||||
|
.attr('rx', d => d.rx)
|
||||||
|
.attr('ry', d => d.ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
instantiationSelections
|
||||||
|
.attr('class', 'grab')
|
||||||
|
.each(function (data) {
|
||||||
|
const portSelection = d3.select(this);
|
||||||
|
const manager = _this.createDataManager(portSelection, data);
|
||||||
|
|
||||||
|
// TODO: 实现拖拽
|
||||||
|
// registerDragEvent(manager, rootRender);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selections = instantiationSelections;
|
||||||
|
return instantiationSelections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {d3.Selection} selection
|
||||||
|
* @param {BasicD3DataItem} data
|
||||||
|
* @returns {BasicD3ManagmentItem}
|
||||||
|
*/
|
||||||
|
createDataManager(selection, data) {
|
||||||
|
const id2manager = this.id2manager;
|
||||||
|
// 创建拖拽上下文
|
||||||
|
const dragContext = {
|
||||||
|
neighbors: [],
|
||||||
|
elkGraph: {
|
||||||
|
// elk 是无状态的,id 取什么名字都行
|
||||||
|
id: 'root',
|
||||||
|
children: [],
|
||||||
|
edges: [],
|
||||||
|
layoutOptions: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const managerItem = {
|
||||||
|
data,
|
||||||
|
selection,
|
||||||
|
type: 'cell',
|
||||||
|
dragContext
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!id2manager.has(data.id)) {
|
||||||
|
id2manager.set(data.id, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
id2manager.get(data.id).push(managerItem);
|
||||||
|
return managerItem;
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { globalLookup } from "../global";
|
import { globalLookup } from "../global";
|
||||||
import { Cell, ModuleTree } from "./yosys";
|
import { Cell, ModuleView } from "./yosys";
|
||||||
|
|
||||||
export const LINE_WIDTH = 2;
|
export const LINE_WIDTH = 2;
|
||||||
|
|
||||||
@ -48,9 +48,9 @@ export class Module {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* @type {ModuleTree}
|
* @type {ModuleView}
|
||||||
*/
|
*/
|
||||||
this.moduleTree = new ModuleTree(name, module);
|
this.view = new ModuleView(name, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,12 +61,13 @@ export class Module {
|
|||||||
const nodes = [];
|
const nodes = [];
|
||||||
|
|
||||||
// 绘制 ports
|
// 绘制 ports
|
||||||
for (const name of this.moduleTree.nameToPort.keys()) {
|
for (const name of this.view.nameToPort.keys()) {
|
||||||
const port = this.moduleTree.nameToPort.get(name);
|
const port = this.view.nameToPort.get(name);
|
||||||
|
|
||||||
if (port.direction === 'input') {
|
if (port.direction === 'input') {
|
||||||
const node = {
|
const node = {
|
||||||
id: port.id,
|
id: port.id,
|
||||||
|
name: port.name,
|
||||||
renderName: name,
|
renderName: name,
|
||||||
renderType: 'port',
|
renderType: 'port',
|
||||||
type: port.direction,
|
type: port.direction,
|
||||||
@ -81,6 +82,7 @@ export class Module {
|
|||||||
} else {
|
} else {
|
||||||
const node = {
|
const node = {
|
||||||
id: port.id,
|
id: port.id,
|
||||||
|
name: port.name,
|
||||||
renderName: name,
|
renderName: name,
|
||||||
renderType: 'port',
|
renderType: 'port',
|
||||||
type: port.direction,
|
type: port.direction,
|
||||||
@ -111,8 +113,8 @@ export class Module {
|
|||||||
const edges = [];
|
const edges = [];
|
||||||
|
|
||||||
const skinManager = globalLookup.skinManager;
|
const skinManager = globalLookup.skinManager;
|
||||||
for (const name of this.moduleTree.nameToCell.keys()) {
|
for (const name of this.view.nameToCell.keys()) {
|
||||||
const cell = this.moduleTree.nameToCell.get(name);
|
const cell = this.view.nameToCell.get(name);
|
||||||
const skin = skinManager.querySkin(cell.type);
|
const skin = skinManager.querySkin(cell.type);
|
||||||
|
|
||||||
if (skin) {
|
if (skin) {
|
||||||
@ -173,6 +175,7 @@ export class Module {
|
|||||||
|
|
||||||
const node = {
|
const node = {
|
||||||
id: cell.id,
|
id: cell.id,
|
||||||
|
name: cell.name,
|
||||||
renderName: cell.type,
|
renderName: cell.type,
|
||||||
renderType: 'cell',
|
renderType: 'cell',
|
||||||
width,
|
width,
|
||||||
@ -207,6 +210,7 @@ export class Module {
|
|||||||
|
|
||||||
const node = {
|
const node = {
|
||||||
id: cell.id,
|
id: cell.id,
|
||||||
|
name: cell.name,
|
||||||
renderName: cell.type,
|
renderName: cell.type,
|
||||||
renderType: 'cell',
|
renderType: 'cell',
|
||||||
width,
|
width,
|
||||||
@ -234,7 +238,7 @@ export class Module {
|
|||||||
const nodes = [];
|
const nodes = [];
|
||||||
const edges = [];
|
const edges = [];
|
||||||
|
|
||||||
const tree = this.moduleTree
|
const tree = this.view
|
||||||
|
|
||||||
for (const cellName of tree.nameToCell.keys()) {
|
for (const cellName of tree.nameToCell.keys()) {
|
||||||
const cell = tree.nameToCell.get(cellName);
|
const cell = tree.nameToCell.get(cellName);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* @description 模块树对象,直接作为 treeview 的渲染视图加入运算
|
* @description 模块树对象,直接作为 treeview 的渲染视图加入运算
|
||||||
* 相比于 YosysNetModule, ModuleTree 只关心和渲染有关的那部分变量,且不可被序列化
|
* 相比于 YosysNetModule, ModuleView 只关心和渲染有关的那部分变量,且不可被序列化
|
||||||
*/
|
*/
|
||||||
export class ModuleTree {
|
export class ModuleView {
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {YosysNetModule} rawModule
|
* @param {YosysNetModule} rawModule
|
||||||
@ -96,12 +96,12 @@ export class ModuleTree {
|
|||||||
export class Port {
|
export class Port {
|
||||||
/**
|
/**
|
||||||
* @description port 的抽象
|
* @description port 的抽象
|
||||||
* @param {ModuleTree} moduleTree
|
* @param {ModuleView} view
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {YosysPort} rawPort
|
* @param {YosysPort} rawPort
|
||||||
*/
|
*/
|
||||||
constructor(moduleTree, name, rawPort) {
|
constructor(view, name, rawPort) {
|
||||||
this.moduleTree = moduleTree;
|
this.view = view;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.rawPort = rawPort;
|
this.rawPort = rawPort;
|
||||||
}
|
}
|
||||||
@ -115,22 +115,33 @@ export class Port {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return dotConnect(this.moduleTree.id, this.name);
|
return dotConnect(this.view.id, this.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Cell {
|
export class Cell {
|
||||||
/**
|
/**
|
||||||
* @description 器件的抽象
|
* @description 器件的抽象
|
||||||
* @param {ModuleTree} moduleTree
|
* @param {ModuleView} view
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {YosysCell} rawCell
|
* @param {YosysCell} rawCell
|
||||||
*/
|
*/
|
||||||
constructor(moduleTree, name, rawCell) {
|
constructor(view, name, rawCell) {
|
||||||
this.moduleTree = moduleTree;
|
this.view = view;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.rawCell = rawCell;
|
this.rawCell = rawCell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 是否为例化模块
|
||||||
|
*/
|
||||||
|
this.isInstantiation = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 如果是例化模块,它对应的 module 的 tree
|
||||||
|
* @type {ModuleView}
|
||||||
|
*/
|
||||||
|
this.belongModuleView = undefined;
|
||||||
|
|
||||||
this._nameToConnection = new Map();
|
this._nameToConnection = new Map();
|
||||||
|
|
||||||
for (const [connectionName, wireIds] of Object.entries(rawCell.connections)) {
|
for (const [connectionName, wireIds] of Object.entries(rawCell.connections)) {
|
||||||
@ -146,12 +157,17 @@ export class Cell {
|
|||||||
return this._nameToConnection;
|
return this._nameToConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 当前器件的类型
|
||||||
|
* - 如果器件是一个例化模块,那么 type 就是这个例化的 module 的名字,比如 `full_adder_1bit`
|
||||||
|
* - 如果器件是一个基本器件,那么 type 就是 yosys 文档中记录的关于这个器件的名字,比如 $_AND_(与门), $_OR_(或门)
|
||||||
|
*/
|
||||||
get type() {
|
get type() {
|
||||||
return this.rawCell.type;
|
return this.rawCell.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return dotConnect(this.moduleTree.id, this.name);
|
return dotConnect(this.view.id, this.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
const pinkLogStyle = 'background-color: #CB81DA; color: white; padding: 3px; border-radius: 3px;';
|
const pinkLogStyle = 'background-color: #CB81DA; color: white; padding: 3px; border-radius: 3px;';
|
||||||
|
const redLogStyle = 'background-color:rgb(227, 91, 49); color: white; padding: 3px; border-radius: 3px;';
|
||||||
|
|
||||||
export function pinkLog(message) {
|
export function pinkLog(message) {
|
||||||
console.log('%c' + message, pinkLogStyle);
|
console.log('%c' + message, pinkLogStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function redLog(message) {
|
||||||
|
console.log('%c' + message, redLogStyle);
|
||||||
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "عرض الجوال",
|
"usermanual.move-view": "عرض الجوال",
|
||||||
"usermanual.scale-view": "تكبير/تصغير العرض",
|
"usermanual.scale-view": "تكبير/تصغير العرض",
|
||||||
"usermanual.scale-view-more": "تكبير العرض (مقياس أكبر)",
|
"usermanual.scale-view-more": "تكبير العرض (مقياس أكبر)",
|
||||||
"loading": "جاري التحميل"
|
"loading": "جاري التحميل",
|
||||||
|
"module": "وحدة"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "Mobile Ansicht",
|
"usermanual.move-view": "Mobile Ansicht",
|
||||||
"usermanual.scale-view": "Ansicht zoomen",
|
"usermanual.scale-view": "Ansicht zoomen",
|
||||||
"usermanual.scale-view-more": "Ansicht vergrößern (größerer Maßstab)",
|
"usermanual.scale-view-more": "Ansicht vergrößern (größerer Maßstab)",
|
||||||
"loading": "Laden"
|
"loading": "Laden",
|
||||||
|
"module": "Modul"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "Mobile View",
|
"usermanual.move-view": "Mobile View",
|
||||||
"usermanual.scale-view": "View zoom",
|
"usermanual.scale-view": "View zoom",
|
||||||
"usermanual.scale-view-more": "View zoom (larger scale)",
|
"usermanual.scale-view-more": "View zoom (larger scale)",
|
||||||
"loading": "loading"
|
"loading": "loading",
|
||||||
|
"module": "Modules"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "Vue mobile",
|
"usermanual.move-view": "Vue mobile",
|
||||||
"usermanual.scale-view": "Zoom de la vue",
|
"usermanual.scale-view": "Zoom de la vue",
|
||||||
"usermanual.scale-view-more": "Zoom de la vue (échelle plus grande)",
|
"usermanual.scale-view-more": "Zoom de la vue (échelle plus grande)",
|
||||||
"loading": "Chargement"
|
"loading": "Chargement",
|
||||||
|
"module": "Module"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "モバイル表示",
|
"usermanual.move-view": "モバイル表示",
|
||||||
"usermanual.scale-view": "ビューのズーム",
|
"usermanual.scale-view": "ビューのズーム",
|
||||||
"usermanual.scale-view-more": "ビューのズーム(より大きなスケール)",
|
"usermanual.scale-view-more": "ビューのズーム(より大きなスケール)",
|
||||||
"loading": "読み込み中"
|
"loading": "読み込み中",
|
||||||
|
"module": "モジュール"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "모바일 보기",
|
"usermanual.move-view": "모바일 보기",
|
||||||
"usermanual.scale-view": "보기 확대/축소",
|
"usermanual.scale-view": "보기 확대/축소",
|
||||||
"usermanual.scale-view-more": "보기 확대 (더 큰 스케일)",
|
"usermanual.scale-view-more": "보기 확대 (더 큰 스케일)",
|
||||||
"loading": "로딩 중"
|
"loading": "로딩 중",
|
||||||
|
"module": "모듈"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "Мобильный вид",
|
"usermanual.move-view": "Мобильный вид",
|
||||||
"usermanual.scale-view": "Масштабирование вида",
|
"usermanual.scale-view": "Масштабирование вида",
|
||||||
"usermanual.scale-view-more": "Масштабирование вида (больший масштаб)",
|
"usermanual.scale-view-more": "Масштабирование вида (больший масштаб)",
|
||||||
"loading": "Загрузка"
|
"loading": "Загрузка",
|
||||||
|
"module": "Модуль"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "移动视图",
|
"usermanual.move-view": "移动视图",
|
||||||
"usermanual.scale-view": "视图缩放",
|
"usermanual.scale-view": "视图缩放",
|
||||||
"usermanual.scale-view-more": "视图缩放(尺度更大)",
|
"usermanual.scale-view-more": "视图缩放(尺度更大)",
|
||||||
"loading": "加载中"
|
"loading": "加载中",
|
||||||
|
"module": "模块"
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"usermanual.move-view": "移動視圖",
|
"usermanual.move-view": "移動視圖",
|
||||||
"usermanual.scale-view": "視圖縮放",
|
"usermanual.scale-view": "視圖縮放",
|
||||||
"usermanual.scale-view-more": "視圖縮放(尺度更大)",
|
"usermanual.scale-view-more": "視圖縮放(尺度更大)",
|
||||||
"loading": "加載中"
|
"loading": "加載中",
|
||||||
|
"module": "模塊"
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user