debug
This commit is contained in:
parent
3620b3c9f3
commit
fb3d481397
8
package-lock.json
generated
8
package-lock.json
generated
@ -14,7 +14,8 @@
|
||||
"elkjs": "^0.9.3",
|
||||
"mitt": "^3.0.1",
|
||||
"vue": "^3.2.13",
|
||||
"vue-i18n": "10.0.5"
|
||||
"vue-i18n": "10.0.5",
|
||||
"web-worker": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
@ -11345,6 +11346,11 @@
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/web-worker": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz",
|
||||
"integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA=="
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
|
@ -14,7 +14,8 @@
|
||||
"elkjs": "^0.9.3",
|
||||
"mitt": "^3.0.1",
|
||||
"vue": "^3.2.13",
|
||||
"vue-i18n": "10.0.5"
|
||||
"vue-i18n": "10.0.5",
|
||||
"web-worker": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
|
@ -8,6 +8,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import RightNav from '@/components/right-nav.vue';
|
||||
import { onMounted } from 'vue';
|
||||
import { setDefaultCss } from './hook/css';
|
||||
import { NetlistRender } from './hook/render';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@ -18,7 +19,8 @@ onMounted(async () => {
|
||||
// 初始化载入 netlist 的 json 文件
|
||||
const [ netJson ] = await window.readNetFile();
|
||||
|
||||
console.log(netJson);
|
||||
const render = new NetlistRender(netJson);
|
||||
await render.createLayout();
|
||||
|
||||
});
|
||||
|
||||
|
@ -68,10 +68,15 @@
|
||||
|
||||
/**
|
||||
* @typedef ModuleAttribute
|
||||
* @property {string} top - 表示该模块是否为顶层模块(1 表示是,0 表示否)。
|
||||
* @property {string} src - 模块定义的源文件和行号。
|
||||
* @property {string} [dynports] - 动态端口标志(1 表示是,0 表示否)。
|
||||
* @property {string} [hdlname] - HDL 名称。
|
||||
* @property {string} [top] 表示该模块是否为顶层模块(1 表示是,0 表示否)。
|
||||
* @property {string} src 模块定义的源文件和行号。
|
||||
* @property {string} [cells_not_processed] 代表当前模块是否没有被处理
|
||||
*
|
||||
* 比如对于一个 main.v 而言,它内部定义了两个模块 A 和 B。如果只综合了 A,那么 B 也会出现在 yosys json 中
|
||||
* 但是 B 的会额外出现 `cells_not_processed` 这个字段,且值为 "00000000000000000000000000000001",代表没有处理
|
||||
*
|
||||
* @property {string} [dynports] 动态端口标志(1 表示是,0 表示否)。
|
||||
* @property {string} [hdlname] HDL 名称。
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -142,7 +147,11 @@
|
||||
|
||||
/**
|
||||
* @typedef ElkNode
|
||||
* @property {string} id - 节点的唯一标识符。
|
||||
* @property {string} id 节点的唯一标识符。
|
||||
* @property {ElkNode[]} children 当前节点的内部
|
||||
* @property {ElkPort[]} ports 当前节点的端口
|
||||
* @property {ElkEdge[]} edges 当前节点的连线
|
||||
* @property {string} type 节点的类型
|
||||
* @property {number} [x] - 节点的 X 坐标(可选,由布局算法生成)。
|
||||
* @property {number} [y] - 节点的 Y 坐标(可选,由布局算法生成)。
|
||||
* @property {number} [width] - 节点的宽度(可选)。
|
||||
@ -175,5 +184,5 @@
|
||||
* @property {string} [elk.direction] - 布局方向(可选,如 "RIGHT", "DOWN" 等)。
|
||||
* @property {number} [elk.spacing.nodeNode] - 节点之间的间距(可选)。
|
||||
* @property {number} [elk.spacing.edgeNode] - 边与节点之间的间距(可选)。
|
||||
* @property {string} [elk.port.side] - 端口的位置(可选,如 "NORTH", "SOUTH", "EAST", "WEST")。
|
||||
* @property {'NORTH' | 'SOUTH' | 'EAST' | 'WEST'} [elk.port.side] - 端口的位置(可选,如 "NORTH", "SOUTH", "EAST", "WEST")。
|
||||
*/
|
@ -1,7 +1,10 @@
|
||||
import * as d3 from 'd3';
|
||||
import { ModuleLayout } from './layout';
|
||||
import ELK from 'elkjs';
|
||||
|
||||
class NetlistRender {
|
||||
|
||||
import { Module } from './layout';
|
||||
|
||||
export class NetlistRender {
|
||||
/**
|
||||
*
|
||||
* @param {YosysRawNet} rawNet
|
||||
@ -21,6 +24,11 @@ class NetlistRender {
|
||||
edges: []
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {Map<string, Module>}
|
||||
*/
|
||||
this.nameToModule = new Map();
|
||||
|
||||
this.loadYosysRawNet(rawNet);
|
||||
}
|
||||
|
||||
@ -29,11 +37,36 @@ class NetlistRender {
|
||||
* @param {YosysRawNet} rawNet
|
||||
*/
|
||||
loadYosysRawNet(rawNet) {
|
||||
|
||||
// 转换为 elkjs 格式的 graph
|
||||
for (const [moduleName, module] of Object.entries(rawNet.modules)) {
|
||||
for (const [moduleName, rawModule] of Object.entries(rawNet.modules)) {
|
||||
const top = parseInt(rawModule.attributes.top);
|
||||
// 一开始只渲染 top 模块
|
||||
if (top) {
|
||||
const module = new Module(moduleName, rawModule);
|
||||
const portNodes = layout.makeNetsElkNodes();
|
||||
const cellNodes = layout.makeCellsElkNodes();
|
||||
const [constantNodes, connectionEdges] = layout.makeConnectionElkNodes();
|
||||
|
||||
this.elkGraph.children.push(...portNodes);
|
||||
this.elkGraph.children.push(...cellNodes);
|
||||
this.elkGraph.children.push(...constantNodes);
|
||||
this.elkGraph.edges.push(...connectionEdges);
|
||||
|
||||
this.nameToModule.set(moduleName, module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据信息创建布局对象
|
||||
* @returns {Promise<ElkNode>}
|
||||
*/
|
||||
async createLayout() {
|
||||
const elk = new ELK();
|
||||
const graph = this.elkGraph;
|
||||
const layoutGraph = await elk.layout(graph, {});
|
||||
console.log(layoutGraph);
|
||||
|
||||
return layoutGraph
|
||||
}
|
||||
}
|
@ -10,9 +10,19 @@ export const LAYOUT_CONSTANT = {
|
||||
INSTANTIATION_WIDTH: 50,
|
||||
INSTANTIATION_HEIGHT: 50,
|
||||
CONSTANT_WIDTH: 50,
|
||||
CONSTANT_HEIGHT: 50
|
||||
CONSTANT_HEIGHT: 50,
|
||||
|
||||
CELL_PORT_HEIGHT: 10,
|
||||
CELL_PORT_WIDTH: 5
|
||||
};
|
||||
|
||||
export const ELK_DIRECTION = {
|
||||
LEFT: 'WEST',
|
||||
RIGHT: 'EAST',
|
||||
TOP: 'NORTH',
|
||||
BOTTOM: 'SOUTH'
|
||||
}
|
||||
|
||||
function getCellSize(cellType) {
|
||||
if (CELL_LIBS[cellType]) {
|
||||
return {
|
||||
@ -31,7 +41,7 @@ function makeEdgeId(fromId, toId) {
|
||||
return 'edge-' + fromId + '-' + toId;
|
||||
}
|
||||
|
||||
export class ModuleLayout {
|
||||
export class Module {
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {YosysNetModule} module
|
||||
@ -44,7 +54,7 @@ export class ModuleLayout {
|
||||
* @description
|
||||
* @type {ModuleTree}
|
||||
*/
|
||||
this.moduleTree = new ModuleTree(name, moduleTree);
|
||||
this.moduleTree = new ModuleTree(name, module);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,15 +67,11 @@ export class ModuleLayout {
|
||||
// 绘制 ports
|
||||
for (const name of this.moduleTree.nameToPort.keys()) {
|
||||
const port = this.moduleTree.nameToPort.get(name);
|
||||
const side = port.direction === 'input' ? 'WEST' : 'EAST';
|
||||
|
||||
const node = {
|
||||
id: port.id,
|
||||
width: LAYOUT_CONSTANT.PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.PORT_HEIGHT,
|
||||
layoutOptions: {
|
||||
'elk.port.side': side
|
||||
}
|
||||
};
|
||||
|
||||
nodes.push(node);
|
||||
@ -78,23 +84,38 @@ export class ModuleLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 从 cells 中创建节点
|
||||
* @returns {ElkNode[]}
|
||||
* @description 从 cells(器件) 中创建节点
|
||||
* @returns {[ElkNode[], ElkEdge[]]}
|
||||
*/
|
||||
makeCellsElkNodes() {
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
|
||||
for (const name of this.moduleTree.nameToCell.keys()) {
|
||||
const cell = this.moduleTree.nameToCell.get(name);
|
||||
const { height, width } = getCellSize(cell.type);
|
||||
const side = 'NORTH';
|
||||
|
||||
// 创建器件节点的 port, port 和 connection 一一对应
|
||||
const ports = [];
|
||||
for (const connectionName of cell.nameToConnection) {
|
||||
const connection = cell.nameToConnection.get(connectionName);
|
||||
const portSide = connection.direction === 'input' ? ELK_DIRECTION.LEFT: ELK_DIRECTION.RIGHT;
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
properties: {
|
||||
'port.side': portSide,
|
||||
'allowNonFlowPortsToSwitchSides': true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const node = {
|
||||
id: cell.id,
|
||||
width, height,
|
||||
layoutOptions: {
|
||||
'elk.port.side': side
|
||||
}
|
||||
width,
|
||||
height,
|
||||
ports
|
||||
}
|
||||
|
||||
nodes.push(node);
|
||||
@ -120,9 +141,6 @@ export class ModuleLayout {
|
||||
|
||||
for (const connectionName of cell.nameToConnection.keys()) {
|
||||
const connection = cell.nameToConnection.get(connectionName);
|
||||
// 器件当前的端口的 ID,为了区别与外层 module 的端口,起名为 connection
|
||||
const connectionId = connection.id;
|
||||
|
||||
// 遍历器件端口的每一个连接点
|
||||
// 比如对于端口 input [31:0] data ,它的32个位不一定是完全导向一个变量(虽然我们愿意认为,大部分情况下是这样)
|
||||
// 大部分情况下,👇的 wireIds.length 都是 1
|
||||
@ -185,38 +203,3 @@ export class ModuleLayout {
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 使用 . 把各个部分连接起来
|
||||
* @param {string[]} args
|
||||
*/
|
||||
export function dotConnect(...args) {
|
||||
const stringArgs = args.map(arg => arg.toString());
|
||||
return stringArgs.join('.');
|
||||
}
|
||||
|
||||
export namespace ElkNodeId {
|
||||
export function module(moduleName) {
|
||||
return dotConnect(moduleName);
|
||||
}
|
||||
|
||||
export function port(moduleName, portName) {
|
||||
return dotConnect(moduleName, portName);
|
||||
}
|
||||
|
||||
export function cell(moduleName, cellName) {
|
||||
return dotConnect(moduleName, cellName);
|
||||
}
|
||||
|
||||
export function connection(moduleName, cellName, connectionName) {
|
||||
return dotConnect(moduleName, cellName, connectionName);
|
||||
}
|
||||
|
||||
export function constantBit(moduleName, cellName, connectionName, orderId, constant) {
|
||||
return dotConnect(moduleName, cellName, connectionName, orderId, 'constant-' + constant);
|
||||
}
|
||||
|
||||
export function commonBit(moduleName, cellName, connectionName, wireId) {
|
||||
return dotConnect(moduleName, cellName, connectionName, wireId);
|
||||
}
|
||||
}
|
@ -60,8 +60,8 @@ export class ModuleTree {
|
||||
*/
|
||||
this._wireIdToConnection = new Map();
|
||||
|
||||
for (const [portName, port] of Object.entries(rawModule.ports)) {
|
||||
const port = new Port(this, portName, port);
|
||||
for (const [portName, rawPort] of Object.entries(rawModule.ports)) {
|
||||
const port = new Port(this, portName, rawPort);
|
||||
this._nameToPort.set(portName, port);
|
||||
// 构建映射
|
||||
for (const id of port.bits) {
|
||||
@ -71,8 +71,8 @@ export class ModuleTree {
|
||||
}
|
||||
}
|
||||
|
||||
for (const [cellName, cell] of Object.entries(rawModule.cells)) {
|
||||
const cell = new Cell(this, cellName, cell);
|
||||
for (const [cellName, rawCell] of Object.entries(rawModule.cells)) {
|
||||
const cell = new Cell(this, cellName, rawCell);
|
||||
this._nameToCell.set(cellName, cell);
|
||||
// 构建映射
|
||||
for (const connection of cell.nameToConnection.values()) {
|
||||
@ -181,6 +181,7 @@ export class Cell {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Connection {
|
||||
/**
|
||||
* @description connection 的抽象,一个 connection 代表一个器件的一个端口是如何与外界连接的
|
||||
@ -193,6 +194,11 @@ export class Connection {
|
||||
this.name = name;
|
||||
this.wireIds = wireIds;
|
||||
|
||||
/**
|
||||
* @type {PortDirection}
|
||||
*/
|
||||
this.direction = cell.rawCell.port_directions[name];
|
||||
|
||||
/**
|
||||
* @type {Wire[]}
|
||||
*/
|
||||
@ -200,7 +206,7 @@ export class Connection {
|
||||
|
||||
for (let i = 0; i < wireIds.length; ++ i) {
|
||||
const wire = new Wire(this, i, wireIds[i]);
|
||||
this.wireIds.push(wire);
|
||||
this.wires.push(wire);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user