更新布局

This commit is contained in:
锦恢 2024-12-28 21:57:52 +08:00
parent 0ffc2dc0e5
commit 6e4615a236
6 changed files with 94 additions and 40 deletions

View File

@ -15,7 +15,7 @@
<script> <script>
window.readNetFile = async () => { window.readNetFile = async () => {
const inputVcdFile = 'test.json'; const inputVcdFile = 'full_adder.json';
const skin = 'test.skin'; const skin = 'test.skin';
const r1 = await fetch(inputVcdFile); const r1 = await fetch(inputVcdFile);
const r2 = await fetch(skin); const r2 = await fetch(skin);
@ -23,7 +23,7 @@
const skinBinary = await r2.arrayBuffer(); const skinBinary = await r2.arrayBuffer();
return [ netJson, skinBinary ]; return [ netJson, skinBinary ];
} }
window.moduleName = 'half_adder'; window.moduleName = 'full_adder';
// window.avoidWasm = 'avoid.wasm'; // window.avoidWasm = 'avoid.wasm';
</script> </script>
</head> </head>

View File

@ -4,9 +4,11 @@
<div class="setting-section"> <div class="setting-section">
<h2>{{ t('general-setting') }}</h2> <h2>{{ t('general-setting') }}</h2>
<div class="setting-option" style="width: 220px;"> <div class="setting-option" style="width: 220px;">
<span class="iconfont icon-i18n"></span> <span>
&ensp; <span class="iconfont icon-i18n"></span>
<span class="option-title">{{ t('language-setting') }}</span> &ensp;
<span class="option-title">{{ t('language-setting') }}</span>
</span>
<div style="width: 100px;"> <div style="width: 100px;">
<el-select <el-select
name="language-setting" name="language-setting"
@ -151,23 +153,35 @@ const languageSetting = reactive({
} }
.setting-option { .setting-option {
margin: 5px; margin: 3px;
padding: 8px 12px; padding: 8px 12px;
height: 40px; height: 40px;
width: fit-content; width: 280px !important;
border-radius: .5em; border-radius: .5em;
background-color: var(--background); background-color: var(--background);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
font-size: 0.9rem; font-size: 0.9rem;
} }
.option-group {
display: flex;
width: fit-content;
}
.option-title { .option-title {
font-size: 0.8rem;
min-width: 80px; min-width: 80px;
margin-right: 12px; margin-right: 12px;
user-select: none; user-select: none;
} }
.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner,
.el-checkbox-button__inner {
font-size: 0.8rem !important;
}
.el-slider__button { .el-slider__button {
background-color: var(--background) !important; background-color: var(--background) !important;
} }

View File

@ -151,7 +151,7 @@ import { Module } from "./render/layout";
* @typedef ElkNode * @typedef ElkNode
* @property {string} id 节点的唯一标识符 * @property {string} id 节点的唯一标识符
* @property {string} [renderName] node 展示的名字布局算法前生成 * @property {string} [renderName] node 展示的名字布局算法前生成
* @property {'port' | 'cell' | 'cellPort'} [renderType] 渲染的类型 * @property {'port' | 'cell' | 'cellPort' | 'portConnection'} [renderType] 渲染的类型
* @property {ElkNode[]} children 当前节点的内部 * @property {ElkNode[]} children 当前节点的内部
* @property {ElkPort[]} ports 当前节点的端口 * @property {ElkPort[]} ports 当前节点的端口
* @property {ElkEdge[]} edges 当前节点的连线 * @property {ElkEdge[]} edges 当前节点的连线

View File

@ -30,10 +30,19 @@ export class NetlistRender {
layoutOptions: { layoutOptions: {
// org.eclipse. 可以去除 // org.eclipse. 可以去除
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35, 'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
// node 之间的最小间距
'elk.spacing.nodeNode': 35, 'elk.spacing.nodeNode': 35,
'elk.layered.layering.strategy': 'NETWORK_SIMPLEX', // layered 算法布局,尽可能让全体布局从左向右
'elk.algorithm': 'layered', 'elk.algorithm': 'layered',
// layered 算法的风格
'elk.layered.layering.strategy': 'NETWORK_SIMPLEX',
// ...
'elk.layered.considerModelOrder.longEdgeStrategy': 'DUMMY_NODE_UNDER',
// edge 的生成采用正交路由算法
'elk.edgeRouting': 'ORTHOGONAL',
// 指定 layered 算法的方向为从左到右
'elk.direction': 'RIGHT', 'elk.direction': 'RIGHT',
// 激活 node 分区功能,可以通过 partitioning.partition 赋予 id 的方式给 node 进行分组
'elk.partitioning.activate': true 'elk.partitioning.activate': true
} }
}; };
@ -259,8 +268,7 @@ export class NetlistRender {
} }
this.portRender.render(); this.portRender.render();
// TODO: 实现它 this.instantiationRender.render();
// this.instantiationRender.render();
this.cellRender.render(); this.cellRender.render();
this.connectionRender.render(); this.connectionRender.render();
} }

View File

@ -1,5 +1,6 @@
import * as d3 from 'd3'; import * as d3 from 'd3';
import { NetlistRender } from '.'; import { NetlistRender } from '.';
import { globalSetting } from '../global';
export class InstantiationRender { export class InstantiationRender {
/** /**
@ -8,7 +9,7 @@ export class InstantiationRender {
* @param {NetlistRender} rootRender * @param {NetlistRender} rootRender
*/ */
constructor(selection, rootRender) { constructor(selection, rootRender) {
this.selection = selection; this.parentSelection = selection;
this.rootRender = rootRender; this.rootRender = rootRender;
/** /**
@ -29,6 +30,7 @@ export class InstantiationRender {
*/ */
addAsD3DataItem(node) { addAsD3DataItem(node) {
this.data.push({ this.data.push({
id: node.id,
x: node.x, x: node.x,
y: node.y, y: node.y,
name: node.name, name: node.name,

View File

@ -3,7 +3,7 @@
*/ */
import { globalLookup } from "../global"; import { globalLookup } from "../global";
import { Cell, ModuleView } from "./yosys"; import { Cell, dotConnect, ModuleView } from "./yosys";
export const LINE_WIDTH = 2; export const LINE_WIDTH = 2;
@ -13,8 +13,11 @@ export const LAYOUT_CONSTANT = {
PORT_HEIGHT: 20, PORT_HEIGHT: 20,
// 例化模块这只是最小height 会根据端口数量进行调整) // 例化模块这只是最小height 会根据端口数量进行调整)
INSTANTIATION_WIDTH: 50, INSTANTIATION_WIDTH: 80,
INSTANTIATION_HEIGHT: 50, INSTANTIATION_HEIGHT: 80,
// port 顶部的空余,一般用来放置标签的
PORT_TOP_MARGIN: 30,
// 常数 // 常数
CONSTANT_WIDTH: 50, CONSTANT_WIDTH: 50,
@ -64,7 +67,18 @@ export class Module {
for (const name of this.view.nameToPort.keys()) { for (const name of this.view.nameToPort.keys()) {
const port = this.view.nameToPort.get(name); const port = this.view.nameToPort.get(name);
if (port.direction === 'input') { if (port.direction === 'input') {
// 为 port 设置连接点
const portConnection = {
id: dotConnect(port.id, '0'),
renderType: 'portConnection',
width: 1,
height: 1,
x: LAYOUT_CONSTANT.PORT_WIDTH,
y: LAYOUT_CONSTANT.PORT_HEIGHT / 2
};
const node = { const node = {
id: port.id, id: port.id,
name: port.name, name: port.name,
@ -73,13 +87,25 @@ export class Module {
type: port.direction, type: port.direction,
width: LAYOUT_CONSTANT.PORT_WIDTH, width: LAYOUT_CONSTANT.PORT_WIDTH,
height: LAYOUT_CONSTANT.PORT_HEIGHT, height: LAYOUT_CONSTANT.PORT_HEIGHT,
ports: [portConnection],
layoutOptions: { layoutOptions: {
'partitioning.partition': 1 'partitioning.partition': 1,
'org.eclipse.elk.portConstraints': 'FIXED_POS'
} }
}; };
nodes.push(node); nodes.push(node);
} else { } else {
// 为 port 设置连接点
const portConnection = {
id: dotConnect(port.id, '0'),
renderType: 'portConnection',
width: 1,
height: 1,
x: 0,
y: LAYOUT_CONSTANT.PORT_HEIGHT / 2
};
const node = { const node = {
id: port.id, id: port.id,
name: port.name, name: port.name,
@ -88,8 +114,10 @@ export class Module {
type: port.direction, type: port.direction,
width: LAYOUT_CONSTANT.PORT_WIDTH, width: LAYOUT_CONSTANT.PORT_WIDTH,
height: LAYOUT_CONSTANT.PORT_HEIGHT, height: LAYOUT_CONSTANT.PORT_HEIGHT,
ports: [portConnection],
layoutOptions: { layoutOptions: {
'partitioning.partition': 999 'partitioning.partition': 999,
'org.eclipse.elk.portConstraints': 'FIXED_POS'
} }
}; };
@ -192,7 +220,6 @@ export class Module {
// 创建器件节点的 port, port 和 connection 一一对应 // 创建器件节点的 port, port 和 connection 一一对应
const ports = []; const ports = [];
for (const connectionName of cell.nameToConnection.keys()) { for (const connectionName of cell.nameToConnection.keys()) {
const connection = cell.nameToConnection.get(connectionName); const connection = cell.nameToConnection.get(connectionName);
const portSide = connection.direction === 'input' ? ELK_DIRECTION.LEFT: ELK_DIRECTION.RIGHT; const portSide = connection.direction === 'input' ? ELK_DIRECTION.LEFT: ELK_DIRECTION.RIGHT;
@ -202,9 +229,9 @@ export class Module {
renderType: 'cellPort', renderType: 'cellPort',
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH, width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT, height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
// properties: { properties: {
// 'port.side': portSide 'port.side': portSide
// } }
}) })
} }
@ -213,8 +240,8 @@ export class Module {
name: cell.name, name: cell.name,
renderName: cell.type, renderName: cell.type,
renderType: 'cell', renderType: 'cell',
width, width: LAYOUT_CONSTANT.INSTANTIATION_WIDTH,
height, height: LAYOUT_CONSTANT.INSTANTIATION_HEIGHT,
ports, ports,
layoutOptions: { layoutOptions: {
// 强制固定 port 的方向 // 强制固定 port 的方向
@ -289,9 +316,9 @@ export class Module {
if (port.direction === 'input') { if (port.direction === 'input') {
const edge = { const edge = {
// id 遵循 sourcePort-targetPort // id 遵循 sourcePort-targetPort
id: makeEdgeId(port.id, cell.id), id: makeEdgeId(port.id, connection.id),
source: port.id, source: port.id,
sourcePort: port.id, sourcePort: dotConnect(port.id, '0'),
target: cell.id, target: cell.id,
targetPort: connection.id targetPort: connection.id
}; };
@ -299,13 +326,12 @@ export class Module {
edges.push(edge); edges.push(edge);
} else { } else {
const edge = { const edge = {
id: makeEdgeId(cell.id, port.id), id: makeEdgeId(connection.id, port.id),
source: cell.id, source: cell.id,
sourcePort: connection.id, sourcePort: connection.id,
target: port.id, target: port.id,
targetPort: port.id targetPort: dotConnect(port.id, '0')
}; };
edges.push(edge); edges.push(edge);
} }
} else if (tree.wireIdToConnection.has(wireId)) { } else if (tree.wireIdToConnection.has(wireId)) {
@ -313,10 +339,15 @@ export class Module {
// 当前的器件的这个端口和另一个器件的一个端口连接 // 当前的器件的这个端口和另一个器件的一个端口连接
const conn = tree.wireIdToConnection.get(wireId); const conn = tree.wireIdToConnection.get(wireId);
// 防止查询到自己
if (conn.id === connection.id && conn.cell.id === cell.id) {
continue;
}
if (conn.direction === 'input') { if (conn.direction === 'input') {
const edge = { const edge = {
// id 遵循 sourcePort-targetPort // id 遵循 sourcePort-targetPort
id: makeEdgeId(conn.id, cell.id), id: makeEdgeId(conn.id, connection.id),
source: conn.cell.id, source: conn.cell.id,
sourcePort: conn.id, sourcePort: conn.id,
target: cell.id, target: cell.id,
@ -326,13 +357,12 @@ export class Module {
edges.push(edge); edges.push(edge);
} else { } else {
const edge = { const edge = {
id: makeEdgeId(cell.id, conn.id), id: makeEdgeId(connection.id, conn.id),
source: cell.id, source: cell.id,
sourcePort: connection.id, sourcePort: connection.id,
target: conn.id, target: conn.cell.id,
targetPort: conn.cell.id targetPort: conn.id
}; };
edges.push(edge); edges.push(edge);
} }
} }