完成多个器件桥接类型的渲染
This commit is contained in:
parent
425c513635
commit
2f1d16c303
244
public/test.json
244
public/test.json
@ -1,125 +1,137 @@
|
||||
{
|
||||
"creator": "Yosys 0.48+5 (git sha1 7a362f1f7, clang++ 18.1.2-wasi-sdk -Oz)",
|
||||
"modules": {
|
||||
"half_adder": {
|
||||
"half_adder": {
|
||||
"attributes": {
|
||||
"top": "00000000000000000000000000000001",
|
||||
"src": "/root/half_adder.v:1.1-9.10"
|
||||
},
|
||||
"ports": {
|
||||
"a": {
|
||||
"direction": "input",
|
||||
"bits": [ 2 ]
|
||||
},
|
||||
"b": {
|
||||
"direction": "input",
|
||||
"bits": [ 3 ]
|
||||
},
|
||||
"c": {
|
||||
"direction": "input",
|
||||
"bits": [ 4 ]
|
||||
},
|
||||
"sum": {
|
||||
"direction": "output",
|
||||
"bits": [ 5 ]
|
||||
},
|
||||
"carry": {
|
||||
"direction": "output",
|
||||
"bits": [ 6 ]
|
||||
}
|
||||
},
|
||||
"cells": {
|
||||
"$auto$simplemap.cc:75:simplemap_bitop$81": {
|
||||
"hide_name": 1,
|
||||
"type": "$_AND_",
|
||||
"parameters": {
|
||||
},
|
||||
"attributes": {
|
||||
"top": "00000000000000000000000000000001",
|
||||
"src": "/root/half_adder.v:1.1-8.10"
|
||||
"src": "/root/half_adder.v:7.18-7.23"
|
||||
},
|
||||
"ports": {
|
||||
"a": {
|
||||
"direction": "input",
|
||||
"bits": [
|
||||
2
|
||||
]
|
||||
},
|
||||
"b": {
|
||||
"direction": "input",
|
||||
"bits": [
|
||||
3
|
||||
]
|
||||
},
|
||||
"sum": {
|
||||
"direction": "output",
|
||||
"bits": [
|
||||
4
|
||||
]
|
||||
},
|
||||
"carry": {
|
||||
"direction": "output",
|
||||
"bits": [
|
||||
5
|
||||
]
|
||||
}
|
||||
"port_directions": {
|
||||
"A": "input",
|
||||
"B": "input",
|
||||
"Y": "output"
|
||||
},
|
||||
"cells": {
|
||||
"$auto$simplemap.cc:75:simplemap_bitop$80": {
|
||||
"hide_name": 1,
|
||||
"type": "$_AND_",
|
||||
"parameters": {},
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:6.18-6.23"
|
||||
},
|
||||
"port_directions": {
|
||||
"A": "input",
|
||||
"B": "input",
|
||||
"Y": "output"
|
||||
},
|
||||
"connections": {
|
||||
"A": [
|
||||
2
|
||||
],
|
||||
"B": [
|
||||
3
|
||||
],
|
||||
"Y": [
|
||||
5
|
||||
]
|
||||
}
|
||||
},
|
||||
"$auto$simplemap.cc:75:simplemap_bitop$81": {
|
||||
"hide_name": 1,
|
||||
"type": "$_XOR_",
|
||||
"parameters": {},
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:7.16-7.21"
|
||||
},
|
||||
"port_directions": {
|
||||
"A": "input",
|
||||
"B": "input",
|
||||
"Y": "output"
|
||||
},
|
||||
"connections": {
|
||||
"A": [
|
||||
2
|
||||
],
|
||||
"B": [
|
||||
3
|
||||
],
|
||||
"Y": [
|
||||
4
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"netnames": {
|
||||
"a": {
|
||||
"hide_name": 0,
|
||||
"bits": [
|
||||
2
|
||||
],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:2.9-2.10"
|
||||
}
|
||||
},
|
||||
"b": {
|
||||
"hide_name": 0,
|
||||
"bits": [
|
||||
3
|
||||
],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:3.9-3.10"
|
||||
}
|
||||
},
|
||||
"carry": {
|
||||
"hide_name": 0,
|
||||
"bits": [
|
||||
5
|
||||
],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:4.10-4.15"
|
||||
}
|
||||
},
|
||||
"sum": {
|
||||
"hide_name": 0,
|
||||
"bits": [
|
||||
4
|
||||
],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:5.10-5.13"
|
||||
}
|
||||
}
|
||||
"connections": {
|
||||
"A": [ 2 ],
|
||||
"B": [ 3 ],
|
||||
"Y": [ 7 ]
|
||||
}
|
||||
},
|
||||
"$auto$simplemap.cc:75:simplemap_bitop$82": {
|
||||
"hide_name": 1,
|
||||
"type": "$_OR_",
|
||||
"parameters": {
|
||||
},
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:7.18-7.27"
|
||||
},
|
||||
"port_directions": {
|
||||
"A": "input",
|
||||
"B": "input",
|
||||
"Y": "output"
|
||||
},
|
||||
"connections": {
|
||||
"A": [ 7 ],
|
||||
"B": [ 4 ],
|
||||
"Y": [ 6 ]
|
||||
}
|
||||
},
|
||||
"$auto$simplemap.cc:75:simplemap_bitop$83": {
|
||||
"hide_name": 1,
|
||||
"type": "$_XOR_",
|
||||
"parameters": {
|
||||
},
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:8.16-8.21"
|
||||
},
|
||||
"port_directions": {
|
||||
"A": "input",
|
||||
"B": "input",
|
||||
"Y": "output"
|
||||
},
|
||||
"connections": {
|
||||
"A": [ 2 ],
|
||||
"B": [ 3 ],
|
||||
"Y": [ 5 ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"netnames": {
|
||||
"$and$/root/half_adder.v:7$1_Y": {
|
||||
"hide_name": 1,
|
||||
"bits": [ 7 ],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:7.18-7.23"
|
||||
}
|
||||
},
|
||||
"a": {
|
||||
"hide_name": 0,
|
||||
"bits": [ 2 ],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:2.9-2.10"
|
||||
}
|
||||
},
|
||||
"b": {
|
||||
"hide_name": 0,
|
||||
"bits": [ 3 ],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:3.9-3.10"
|
||||
}
|
||||
},
|
||||
"c": {
|
||||
"hide_name": 0,
|
||||
"bits": [ 4 ],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:4.9-4.10"
|
||||
}
|
||||
},
|
||||
"carry": {
|
||||
"hide_name": 0,
|
||||
"bits": [ 6 ],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:5.10-5.15"
|
||||
}
|
||||
},
|
||||
"sum": {
|
||||
"hide_name": 0,
|
||||
"bits": [ 5 ],
|
||||
"attributes": {
|
||||
"src": "/root/half_adder.v:6.10-6.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
19
src/App.vue
19
src/App.vue
@ -6,17 +6,22 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Render from '@/components/render';
|
||||
import RightNav from '@/components/right-nav.vue';
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, watch } from 'vue';
|
||||
import { setDefaultCss } from './hook/css';
|
||||
import { NetlistRender } from './hook/render';
|
||||
|
||||
import * as d3 from 'd3';
|
||||
import { globalLookup } from './hook/global';
|
||||
import { globalLookup, globalSetting } from './hook/global';
|
||||
|
||||
// 监听 globalSetting 并录入 localStorage
|
||||
watch(
|
||||
() => globalSetting,
|
||||
() => {
|
||||
localStorage.setItem('setting', JSON.stringify(globalSetting));
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
onMounted(async () => {
|
||||
// 设置 css 宏,纠正样式
|
||||
@ -29,7 +34,7 @@ onMounted(async () => {
|
||||
const skinManager = globalLookup.skinManager;
|
||||
|
||||
skinManager.load(skinBinary);
|
||||
|
||||
|
||||
render.load(netJson);
|
||||
const layout = await render.createLayout();
|
||||
const svg = await render.render(layout, '#netlist');
|
||||
|
@ -48,7 +48,6 @@
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="globalSetting.renderAnimation"
|
||||
size="large"
|
||||
active-text="ON"
|
||||
inactive-text="OFF"
|
||||
/>
|
||||
@ -66,7 +65,7 @@ import { useI18n } from 'vue-i18n';
|
||||
|
||||
defineComponent({ name: "dide-setting" });
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
locale.value = globalSetting.language;
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
@ -85,7 +84,7 @@ function confirmLanguageDialog() {
|
||||
|
||||
function onlanguagechange(code) {
|
||||
const option = languageSetting.options.find(item => item.value === code);
|
||||
currentLanguage.value = option.text;
|
||||
currentLanguage.value = option.text;
|
||||
languageDialogShow.value = true;
|
||||
}
|
||||
|
||||
|
@ -22,20 +22,6 @@ export const globalLookup = {
|
||||
*/
|
||||
netlistRender: new NetlistRender(),
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
svgScale: 1,
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
svgTranslateX: 0,
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
svgTranslateY: 0
|
||||
};
|
||||
|
||||
function loadSetting() {
|
||||
|
@ -1,13 +1,16 @@
|
||||
import * as d3 from 'd3';
|
||||
import { globalSetting } from '../global';
|
||||
import { NetlistRender } from '.';
|
||||
|
||||
export class CellRender {
|
||||
/**
|
||||
*
|
||||
* @param {d3.Selection} selection
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
constructor(selection, cells) {
|
||||
constructor(selection, rootRender) {
|
||||
this.parentSelection = selection;
|
||||
this.rootRender = rootRender;
|
||||
|
||||
/**
|
||||
* @type {BasicD3DataItem[]}
|
||||
@ -33,6 +36,7 @@ export class CellRender {
|
||||
|
||||
render() {
|
||||
const data = this.data;
|
||||
const rootRender = this.rootRender;
|
||||
|
||||
let cellSelections = this.parentSelection.selectAll('g')
|
||||
.data(data)
|
||||
@ -53,19 +57,22 @@ export class CellRender {
|
||||
.duration(1000)
|
||||
.attr('stroke-opacity', 1)
|
||||
.attr('class', 'grab')
|
||||
.on('end', function (data) {
|
||||
console.log('enter end');
|
||||
|
||||
.on('end', function (data) {
|
||||
const cellSelection = d3.select(this);
|
||||
registerDragEvent(cellSelection, data);
|
||||
|
||||
|
||||
registerDragEvent(cellSelection, data, rootRender);
|
||||
});
|
||||
} else {
|
||||
cellSelections = cellSelections
|
||||
.attr('class', 'grab')
|
||||
.each(function (data) {
|
||||
console.log('enter end'); // 在这里执行你需要的逻辑
|
||||
const cellSelection = d3.select(this);
|
||||
registerDragEvent(cellSelection, data);
|
||||
|
||||
// 计算最小拓扑图
|
||||
const mintoGraph = undefined;
|
||||
|
||||
registerDragEvent(cellSelection, data, rootRender);
|
||||
});
|
||||
}
|
||||
|
||||
@ -80,14 +87,15 @@ export class CellRender {
|
||||
* 需要提取最小拓扑子图,然后重新调整各个区域的尺寸
|
||||
* @param {d3.Selection} selection
|
||||
* @param {any} data
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
export function registerDragEvent(selection, data) {
|
||||
export function registerDragEvent(selection, data, rootRender) {
|
||||
// 创建拖拽行为
|
||||
const drag = d3.drag();
|
||||
|
||||
drag.on('start', event => dragStart(event, selection, data));
|
||||
drag.on('drag', event => dragged(event, selection, data));
|
||||
drag.on('end', event => dragEnd(event, selection, data))
|
||||
drag.on('start', async event => dragStart(event, selection, data));
|
||||
drag.on('drag', async event => dragged(event, selection, data, rootRender));
|
||||
drag.on('end', async event => dragEnd(event, selection, data))
|
||||
|
||||
selection.call(drag);
|
||||
}
|
||||
@ -105,14 +113,20 @@ function dragStart(event, selection, data) {
|
||||
*
|
||||
* @param {d3.D3DragEvent} event
|
||||
* @param {d3.Selection} selection
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
function dragged(event, selection, data) {
|
||||
function dragged(event, selection, data, rootRender) {
|
||||
// 当拖动结束时,D3 会根据绑定的数据(data.x 和 data.y)重新渲染元素,导致元素回到初始位置。
|
||||
// 所以需要 手动更新 data.x 和 data.y
|
||||
data.x = event.x;
|
||||
data.y = event.y;
|
||||
|
||||
selection
|
||||
.attr('x', event.x)
|
||||
.attr('y', event.y);
|
||||
|
||||
// 根据最小拓扑图,提取出关键点,重新计算布局
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,11 +31,31 @@ export class NetlistRender {
|
||||
edges: []
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {import('elkjs').ELK}
|
||||
*/
|
||||
this.elk = new ELK();
|
||||
|
||||
/**
|
||||
* @type {Map<string, Module>}
|
||||
*/
|
||||
this.nameToModule = new Map();
|
||||
|
||||
this.maxX = -1;
|
||||
this.minX = 1e12;
|
||||
this.maxY = -1;
|
||||
this.minY = 1e12;
|
||||
|
||||
/**
|
||||
* @type {number} 一开始绘制完成后整体的横向偏移量(默认一开始从左上角开始画,不居中)
|
||||
*/
|
||||
this.startOffsetX = 0;
|
||||
|
||||
/**
|
||||
* @type {number} 一开始绘制完成后整体的纵向偏移量(默认一开始从左上角开始画,不居中)
|
||||
*/
|
||||
this.startOffsetY = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,16 +68,19 @@ export class NetlistRender {
|
||||
*/
|
||||
this.rawNet = rawNet;
|
||||
|
||||
// 转换为 elkjs 格式的 graph
|
||||
// 构造模块树
|
||||
for (const [moduleName, rawModule] of Object.entries(rawNet.modules)) {
|
||||
const top = parseInt(rawModule.attributes.top);
|
||||
// 一开始只渲染 top 模块
|
||||
// 一开始只渲染 top 模块,或者告诉我当前的 top 是什么
|
||||
if (top) {
|
||||
const module = new Module(moduleName, rawModule);
|
||||
|
||||
// 构造符合 elk 格式的节点数据
|
||||
const portNodes = module.makeNetsElkNodes();
|
||||
const cellNodes = module.makeCellsElkNodes();
|
||||
const [constantNodes, connectionEdges] = module.makeConnectionElkNodes();
|
||||
|
||||
// 挂载到渲染图中
|
||||
this.elkGraph.children.push(...portNodes);
|
||||
this.elkGraph.children.push(...cellNodes);
|
||||
this.elkGraph.children.push(...constantNodes);
|
||||
@ -73,15 +96,21 @@ export class NetlistRender {
|
||||
* @returns {Promise<ElkNode>}
|
||||
*/
|
||||
async createLayout() {
|
||||
const elk = new ELK();
|
||||
const graph = this.elkGraph;
|
||||
const layoutGraph = await elk.layout(graph, {
|
||||
|
||||
const start = performance.now();
|
||||
const layoutGraph = await this.elk.layout(graph, {
|
||||
layoutOptions: {
|
||||
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
|
||||
'org.eclipse.elk.spacing.nodeNode': 35,
|
||||
'org.eclipse.elk.layered.layering.strategy': 'LONGEST_PATH'
|
||||
}
|
||||
});
|
||||
const timecost = (performance.now() - start).toFixed(3);
|
||||
console.log(
|
||||
`%c 布局计算耗时 ${timecost} ms`,
|
||||
'background-color: #CB81DA; color: white; padding: 3px; border-radius: 3px;'
|
||||
);
|
||||
|
||||
return layoutGraph;
|
||||
}
|
||||
@ -132,6 +161,8 @@ export class NetlistRender {
|
||||
// 将分组作为一个后续操作的 parent selection
|
||||
const g = svg.append('g');
|
||||
|
||||
console.log(computedLayout);
|
||||
|
||||
await this.renderLine(g, computedLayout, ratio);
|
||||
await this.renderEntity(g, computedLayout, ratio);
|
||||
|
||||
@ -139,7 +170,10 @@ export class NetlistRender {
|
||||
this.selection = svg;
|
||||
|
||||
// 注册平移和缩放
|
||||
// this.registerRenderTransform(g);
|
||||
this.registerRenderTransform(g);
|
||||
|
||||
// 根据最大最小尺寸微调全局方位
|
||||
this.adjustLocation(g);
|
||||
|
||||
return svg;
|
||||
}
|
||||
@ -164,6 +198,9 @@ export class NetlistRender {
|
||||
this.connectionRender = new ConnectionRender(parentSelection);
|
||||
|
||||
for (const node of computedLayout.children) {
|
||||
// 只计算形体的,因为 连接点 非常小,几乎不影响布局
|
||||
this.updateMaxMinSize(node.x, node.y, node.width, node.height);
|
||||
|
||||
const skin = skinManager.querySkin(node.renderName);
|
||||
if (skin) {
|
||||
// 具有 skin 的器件
|
||||
@ -223,15 +260,16 @@ export class NetlistRender {
|
||||
// 创建缩放行为
|
||||
const zoom = d3.zoom()
|
||||
// 设置缩放范围(最小缩放比例 0.5,最大缩放比例 5)
|
||||
.scaleExtent([0.5, 5])
|
||||
.scaleExtent([0.1, 10])
|
||||
.on("zoom", zoomed);
|
||||
|
||||
// 将缩放行为应用到 SVG
|
||||
this.selection.call(zoom);
|
||||
this.zoom = zoom;
|
||||
|
||||
// 缩放事件处理函数
|
||||
function zoomed(event) {
|
||||
const { transform, sourceEvent } = event;
|
||||
const { transform, sourceEvent } = event;
|
||||
|
||||
if (sourceEvent && sourceEvent.type === "wheel") {
|
||||
if (globalSetting.renderAnimation) {
|
||||
@ -248,54 +286,64 @@ export class NetlistRender {
|
||||
} else {
|
||||
parentSelection.attr('transform', transform);
|
||||
}
|
||||
} else {
|
||||
// 系统调用的变换,慢一点变换
|
||||
if (globalSetting.renderAnimation) {
|
||||
parentSelection
|
||||
.transition()
|
||||
.duration(1000)
|
||||
.attr('transform', transform);
|
||||
} else {
|
||||
parentSelection.attr('transform', transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
updateMaxMinSize(x, y, width, height) {
|
||||
let x1 = x;
|
||||
let y1 = y;
|
||||
let x2 = x1 + width;
|
||||
let y2 = y1 + height;
|
||||
|
||||
this.minX = Math.min(this.minX, x1);
|
||||
this.maxX = Math.max(this.maxX, x2);
|
||||
this.minY = Math.min(this.minY, y1);
|
||||
this.maxY = Math.max(this.maxY, y2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 从 globalLookup 中更新 svg 的方位
|
||||
* @description 调整位姿
|
||||
* @param {d3.Selection} parentSelection
|
||||
*/
|
||||
updateLocationFromGlobal() {
|
||||
// const svg = globalLookup.netlistRender.selection;
|
||||
// if (!svg) {
|
||||
// return;
|
||||
adjustLocation(parentSelection) {
|
||||
const renderHeight = this.renderHeight;
|
||||
const renderWidth = this.renderWidth;
|
||||
// TODO: 更加智能的方位调整
|
||||
const cx = (this.minX + this.maxX) / 2;
|
||||
const cy = (this.minY + this.maxY) / 2;
|
||||
|
||||
this.startOffsetX = renderWidth / 2 - cx;
|
||||
this.startOffsetY = renderHeight / 2 - cy;
|
||||
|
||||
const transform = d3.zoomIdentity.translate(
|
||||
this.startOffsetX, this.startOffsetY
|
||||
);
|
||||
|
||||
this.selection.call(
|
||||
this.zoom.transform,
|
||||
transform
|
||||
);
|
||||
|
||||
// if (globalSetting.renderAnimation) {
|
||||
// parentSelection
|
||||
// .transition()
|
||||
// .duration(1000)
|
||||
// .attr('transform', transform);
|
||||
// } else {
|
||||
// parentSelection.attr('transform', transform);
|
||||
// }
|
||||
|
||||
// svg.attr('transform', `translate(${globalLookup.svgTranslateX}, ${globalLookup.svgTranslateY}) scale(${globalLookup.svgScale})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 渲染 module 中的 port
|
||||
*/
|
||||
renderPorts() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 渲染 module 中的例化模块
|
||||
*/
|
||||
renderInstantiations() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 渲染 module 中的基础器件
|
||||
*/
|
||||
renderCells() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 渲染每一个 例化模块/基础器件 的接入端口
|
||||
*/
|
||||
renderConnections() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 渲染连线
|
||||
*/
|
||||
renderWires() {
|
||||
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { globalLookup } from "../global";
|
||||
import { Cell, CELL_LIBS, ModuleTree } from "./yosys";
|
||||
import { Cell, ModuleTree } from "./yosys";
|
||||
|
||||
export const SKIN_SCALE = 1;
|
||||
export const LINE_WIDTH = 2;
|
||||
@ -33,16 +33,9 @@ export const ELK_DIRECTION = {
|
||||
BOTTOM: 'SOUTH'
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Cell} cell
|
||||
*/
|
||||
function getInstanceSize(cell) {
|
||||
// 根据 port 尺寸来进行计算
|
||||
}
|
||||
|
||||
function makeEdgeId(fromId, toId) {
|
||||
return 'edge-' + fromId + '-' + toId;
|
||||
export function makeEdgeId(fromId, toId) {
|
||||
return fromId + '-' + toId;
|
||||
}
|
||||
|
||||
export class Module {
|
||||
@ -229,8 +222,6 @@ export class Module {
|
||||
|
||||
for (const cellName of tree.nameToCell.keys()) {
|
||||
const cell = tree.nameToCell.get(cellName);
|
||||
// 当前器件的 ID
|
||||
const cellId = cell.id;
|
||||
|
||||
for (const connectionName of cell.nameToConnection.keys()) {
|
||||
const connection = cell.nameToConnection.get(connectionName);
|
||||
@ -257,7 +248,7 @@ export class Module {
|
||||
|
||||
// 创建常数到器件的连线
|
||||
const edge = {
|
||||
id: makeEdgeId(cellId, id),
|
||||
id: makeEdgeId(cell.id, id),
|
||||
sources: [connection.id],
|
||||
targets: [id]
|
||||
};
|
||||
@ -268,11 +259,14 @@ export class Module {
|
||||
// 1. 当前的器件的这个端口和某一个 port 连接
|
||||
// 2. 当前的器件的这个端口和另一个器件的一个端口连接
|
||||
if (tree.wireIdToPort.has(wireId)) {
|
||||
|
||||
// 当前的器件的这个端口和某一个 port 连接
|
||||
const port = tree.wireIdToPort.get(wireId);
|
||||
|
||||
if (port.direction === 'input') {
|
||||
const edge = {
|
||||
id: makeEdgeId(cellId, port.id),
|
||||
// id 遵循 sourcePort-targetPort
|
||||
id: makeEdgeId(port.id, cell.id),
|
||||
source: port.id,
|
||||
sourcePort: port.id,
|
||||
target: cell.id,
|
||||
@ -282,7 +276,7 @@ export class Module {
|
||||
edges.push(edge);
|
||||
} else {
|
||||
const edge = {
|
||||
id: makeEdgeId(cellId, port.id),
|
||||
id: makeEdgeId(cell.id, port.id),
|
||||
source: cell.id,
|
||||
sourcePort: connection.id,
|
||||
target: port.id,
|
||||
@ -292,14 +286,32 @@ export class Module {
|
||||
edges.push(edge);
|
||||
}
|
||||
} else if (tree.wireIdToConnection.has(wireId)) {
|
||||
const connection = tree.wireIdToConnection.get(wireId);
|
||||
const edge = {
|
||||
id: makeEdgeId(cellId, connection.id),
|
||||
sources: [cellId],
|
||||
targets: [connection.id]
|
||||
};
|
||||
|
||||
// 当前的器件的这个端口和另一个器件的一个端口连接
|
||||
const conn = tree.wireIdToConnection.get(wireId);
|
||||
|
||||
edges.push(edge);
|
||||
if (conn.direction === 'input') {
|
||||
const edge = {
|
||||
// id 遵循 sourcePort-targetPort
|
||||
id: makeEdgeId(conn.id, cell.id),
|
||||
source: conn.cell.id,
|
||||
sourcePort: conn.id,
|
||||
target: cell.id,
|
||||
targetPort: connection.id
|
||||
};
|
||||
|
||||
edges.push(edge);
|
||||
} else {
|
||||
const edge = {
|
||||
id: makeEdgeId(cell.id, conn.id),
|
||||
source: cell.id,
|
||||
sourcePort: connection.id,
|
||||
target: conn.id,
|
||||
targetPort: conn.cell.id
|
||||
};
|
||||
|
||||
edges.push(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,12 +47,10 @@ export class PortRender {
|
||||
|
||||
|
||||
portSelections.append("text")
|
||||
.attr("x", d => d.width / 2) // 文本的 x 坐标(居中)
|
||||
.attr("y", d => d.height / 2) // 文本的 y 坐标(居中)
|
||||
.attr("text-anchor", "middle") // 文本水平居中
|
||||
.attr("dominant-baseline", "middle") // 文本垂直居中
|
||||
.text(d => d.text) // 设置文本内容
|
||||
.attr("fill", "black"); // 文本颜色
|
||||
.attr("x", d => d.width / 2)
|
||||
.attr("y", d => d.height / 2)
|
||||
.text(d => d.text)
|
||||
.attr('font-size', '12px');
|
||||
|
||||
if (globalSetting.renderAnimation) {
|
||||
portSelections = portSelections
|
||||
|
@ -1,29 +1,3 @@
|
||||
/**
|
||||
* @type {Record<string, string>} yosys 器件映射
|
||||
* - key: 器件的 type,比如 $_AND_
|
||||
* - value: 器件对应的我们内部的名称,用于检索皮肤系统的
|
||||
*/
|
||||
export const CELL_LIBS = {
|
||||
"$_AND_": "and",
|
||||
"$_XOR_": "xor",
|
||||
"$_NOT_": "not",
|
||||
"$_OR_": "or",
|
||||
"$_NAND_": "nand",
|
||||
"$_NOR_": "nor",
|
||||
"$_XNOR_": "xnor",
|
||||
"$_MUX_": "mux",
|
||||
"$_AOI3_": "aoi3",
|
||||
"$_OAI3_": "oai3",
|
||||
"$_AOI4_": "aoi4",
|
||||
"$_OAI4_": "oai4",
|
||||
"$_DFF_P_": "dff_pos",
|
||||
"$_DFF_N_": "dff_neg",
|
||||
"$_SR_NN_": "sr_nn",
|
||||
"$_SR_NP_": "sr_np",
|
||||
"$_SR_PN_": "sr_pn",
|
||||
"$_SR_PP_": "sr_pp"
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 模块树对象,直接作为 treeview 的渲染视图加入运算
|
||||
* 相比于 YosysNetModule, ModuleTree 只关心和渲染有关的那部分变量,且不可被序列化
|
||||
|
@ -18,19 +18,11 @@ function windowsKeydown(element, event) {
|
||||
if (event.ctrlKey) {
|
||||
if (deltaY < 0) {
|
||||
// scale up
|
||||
const nextScale = globalLookup.svgScale + 0.1;
|
||||
if (nextScale <= MAX_SCALE) {
|
||||
globalLookup.svgScale = nextScale;
|
||||
globalLookup.netlistRender.updateLocationFromGlobal();
|
||||
}
|
||||
|
||||
|
||||
} else if (deltaY > 0) {
|
||||
// scale down
|
||||
const nextScale = globalLookup.svgScale - 0.1;
|
||||
if (nextScale >= MIN_SCALE) {
|
||||
globalLookup.svgScale = nextScale;
|
||||
globalLookup.netlistRender.updateLocationFromGlobal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
@ -39,28 +31,20 @@ function windowsKeydown(element, event) {
|
||||
} else if (deltaX !== 0 && deltaY === 0) {
|
||||
if (deltaX > 0) {
|
||||
// scroll left
|
||||
const translateX = globalLookup.svgTranslateX - 50;
|
||||
globalLookup.svgTranslateX = translateX;
|
||||
globalLookup.netlistRender.updateLocationFromGlobal();
|
||||
|
||||
} else {
|
||||
// scroll right
|
||||
const translateX = globalLookup.svgTranslateX + 50;
|
||||
globalLookup.svgTranslateX = translateX;
|
||||
globalLookup.netlistRender.updateLocationFromGlobal();
|
||||
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
} else if (deltaX === 0 && deltaY !== 0) {
|
||||
if (deltaY > 0) {
|
||||
// scroll up
|
||||
const translateY = globalLookup.svgTranslateY - 50;
|
||||
globalLookup.svgTranslateY = translateY;
|
||||
globalLookup.netlistRender.updateLocationFromGlobal();
|
||||
|
||||
} else {
|
||||
// scroll down
|
||||
const translateY = globalLookup.svgTranslateY + 50;
|
||||
globalLookup.svgTranslateY = translateY;
|
||||
globalLookup.netlistRender.updateLocationFromGlobal();
|
||||
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
Loading…
x
Reference in New Issue
Block a user