diff --git a/src/hook/render/layout.js b/src/hook/render/layout.js index d12a7c6..b8093cb 100644 --- a/src/hook/render/layout.js +++ b/src/hook/render/layout.js @@ -2,16 +2,35 @@ * 将各个节点转换为 elknode 的数据结构 */ -import { DEVICE_MAPPER } from "./yosys"; +import { CELL_LIBS, ModuleTree } from "./yosys"; export const LAYOUT_CONSTANT = { PORT_WIDTH: 50, PORT_HEIGHT: 50, INSTANTIATION_WIDTH: 50, INSTANTIATION_HEIGHT: 50, - + CONSTANT_WIDTH: 50, + CONSTANT_HEIGHT: 50 }; +function getCellSize(cellType) { + if (CELL_LIBS[cellType]) { + return { + width: 50, + height: 50 + }; + } else { + return { + width: LAYOUT_CONSTANT.INSTANTIATION_WIDTH, + height: LAYOUT_CONSTANT.INSTANTIATION_HEIGHT + }; + } +} + +function makeEdgeId(fromId, toId) { + return 'edge-' + fromId + '-' + toId; +} + export class ModuleLayout { /** * @param {string} name @@ -21,43 +40,34 @@ export class ModuleLayout { this.module = module; this.name = name; - this.initialise(); - } - - /** - * @description 初始化,构建信息更加丰富的 RawYosysNet - */ - initialise() { - // 构建模块树 - - - // 构建 wireId 到模块树节点的映射表 - + /** + * @description + * @type {ModuleTree} + */ + this.moduleTree = new ModuleTree(name, moduleTree); } /** - * @description 从 ports 和 netnames 中创建所有变量相关的节点 + * @description 从 ports 中创建所有变量相关的节点 * @returns {ElkNode[]} */ makeNetsElkNodes() { - const ports = this.module.ports; - const nets = this.module.netnames; - - const moduleName = this.name; const nodes = []; // 绘制 ports - for (const [portName, port] of Object.entries(ports)) { - const portId = ElkNodeId.port(moduleName, portName); - // TODO: 把当前的 id 绑定到模块树上面 + 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: portId, + id: port.id, width: LAYOUT_CONSTANT.PORT_WIDTH, height: LAYOUT_CONSTANT.PORT_HEIGHT, layoutOptions: { - "elk.port.side": port.direction === "input" ? "WEST" : "EAST" + 'elk.port.side': side } - } + }; + nodes.push(node); } @@ -72,36 +82,22 @@ export class ModuleLayout { * @returns {ElkNode[]} */ makeCellsElkNodes() { - const cells = this.module.cells; - const moduleName = this.name; const nodes = []; - for (const [cellName, cell] of Object.entries(cells)) { - const cellId = ElkNodeId.cell(moduleName, cellName); - // 查看当前的器件是否为 内置器件 - if (DEVICE_MAPPER[cell.type]) { - // 是内置器件 - // TODO: 查询内置器件的 size - const node = { - id: cellId, - width: 50, - height: 50, - layoutOptions: { - "elk.port.side": "NORTH" - } - }; - nodes.push(node); - } else { - // 是例化模块 - const node = { - id: cellId, - width: LAYOUT_CONSTANT.INSTANTIATION_WIDTH, - height: LAYOUT_CONSTANT.INSTANTIATION_HEIGHT, - layoutOptions: { - "elk.port.side": "NORTH" - } - }; - nodes.push(node); + + for (const name of this.moduleTree.nameToCell.keys()) { + const cell = this.moduleTree.nameToCell.get(name); + const { height, width } = getCellSize(cell.type); + const side = 'NORTH'; + + const node = { + id: cell.id, + width, height, + layoutOptions: { + 'elk.port.side': side + } } + + nodes.push(node); } return nodes; @@ -112,37 +108,36 @@ export class ModuleLayout { * @returns {[ElkNode[], ElkEdge[]]} */ makeConnectionElkNodes() { - const cells = this.module.cells; - const moduleName = this.name; const nodes = []; const edges = []; - for (const [cellName, cell] of Object.entries(cells)) { + const tree = this.moduleTree + + for (const cellName of tree.nameToCell.keys()) { + const cell = tree.nameToCell.get(cellName); // 当前器件的 ID - const cellId = `${moduleName}.${cellName}`; + const cellId = cell.id; - for (const [connectionName, wireIds] of Object.entries(cell.connections)) { + for (const connectionName of cell.nameToConnection.keys()) { + const connection = cell.nameToConnection.get(connectionName); // 器件当前的端口的 ID,为了区别与外层 module 的端口,起名为 connection - const connectionId = `${moduleName}.${cellName}.${connectionName}`; + const connectionId = connection.id; // 遍历器件端口的每一个连接点 // 比如对于端口 input [31:0] data ,它的32个位不一定是完全导向一个变量(虽然我们愿意认为,大部分情况下是这样) // 大部分情况下,👇的 wireIds.length 都是 1 - for (let i = 0; i < wireIds.length; ++ i) { - const bit = wireIds[i]; - // bit 可以是一个 id,也可以是一个常数 - if (typeof bit === 'string') { + for (const wire of connection.wires) { + const wireId = wire.wireId; + const id = wire.id; + if (typeof wireId === 'string') { // 常数 // 如果是常数,需要先创建代表常数的节点,常数一定是器件的输入,而非输出 - const constant = parseInt(bit); - const constantBitId = ElkNodeId.constantBit(moduleName, cellName, connectionName, i, constant); - const node = { - id: constantBitId, - width: 50, - height: 50, + id, + width: LAYOUT_CONSTANT.CONSTANT_WIDTH, + height: LAYOUT_CONSTANT.CONSTANT_HEIGHT, layoutOptions: { - "elk.port.side": "WEST" + 'elk.port.side': 'WEST' } }; @@ -150,23 +145,38 @@ export class ModuleLayout { // 创建常数到器件的连线 const edge = { - id: constantBitId, + id: makeEdgeId(cellId, id), sources: [cellId], - targets: [node.id] + targets: [id] }; - // TODO: 把创建的 edge 关联到模块树上 edges.push(edge); - } else { // 如果不是一个常数连接,那么存在两种可能 // 1. 当前的器件的这个端口和某一个 port 连接 // 2. 当前的器件的这个端口和另一个器件的一个端口连接 + if (tree.wireIdToPort.has(wireId)) { + const port = tree.wireIdToPort.get(wireId); + const edge = { + id: makeEdgeId(cellId, port.id), + sources: [cellId], + targets: [port.id] + }; + 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] + }; - // TODO: 把创建的 edge 关联到模块树上 + edges.push(edge); + } } } + } } diff --git a/src/hook/render/yosys.js b/src/hook/render/yosys.js index 0a05feb..e1aef1b 100644 --- a/src/hook/render/yosys.js +++ b/src/hook/render/yosys.js @@ -3,7 +3,7 @@ * - key: 器件的 type,比如 $_AND_ * - value: 器件对应的我们内部的名称,用于检索皮肤系统的 */ -export const DEVICE_MAPPER = { +export const CELL_LIBS = { "$_AND_": "and", "$_XOR_": "xor", "$_NOT_": "not", @@ -36,6 +36,82 @@ export class ModuleTree { constructor(name, rawModule) { this.name = name; this.rawModule = rawModule; + + /** + * @type {Map} + */ + this._nameToPort = new Map(); + + /** + * @type {Map} + */ + this._nameToCell = new Map(); + + // 给非常数的 wireId 构建映射 + // 因为非常数类型的 wireId 只关联 port 和 connection + + /** + * @type {Map} + */ + this._wireIdToPort = new Map(); + + /** + * @type {Map} + */ + this._wireIdToConnection = new Map(); + + for (const [portName, port] of Object.entries(rawModule.ports)) { + const port = new Port(this, portName, port); + this._nameToPort.set(portName, port); + // 构建映射 + for (const id of port.bits) { + if (typeof id === 'number') { + this._wireIdToPort.set(id, port); + } + } + } + + for (const [cellName, cell] of Object.entries(rawModule.cells)) { + const cell = new Cell(this, cellName, cell); + this._nameToCell.set(cellName, cell); + // 构建映射 + for (const connection of cell.nameToConnection.values()) { + for (const wire of connection.wires) { + const id = wire.wireId; + if (typeof id === 'number') { + this._wireIdToConnection.set(id, connection); + } + } + } + } + } + + /** + * @returns {Map} + */ + get nameToPort() { + return this._nameToPort; + } + + /** + * @returns {Map} + */ + get nameToCell() { + return this._nameToCell; + } + + /** + * @type {Map} + */ + get wireIdToPort() { + return this._wireIdToPort; + } + + /** + * @type {Map} + */ + get wireIdToConnection() { + return this._wireIdToConnection; } get id() { @@ -56,6 +132,14 @@ export class Port { this.rawPort = rawPort; } + get bits() { + return this.rawPort.bits; + } + + get direction() { + return this.rawPort.direction; + } + get id() { return dotConnect(this.moduleTree.id, this.name); } @@ -72,6 +156,24 @@ export class Cell { this.moduleTree = moduleTree; this.name = name; this.rawCell = rawCell; + + this._nameToConnection = new Map(); + + for (const [connectionName, wireIds] of Object.entries(rawCell.connections)) { + const connection = new Connection(this, connectionName, wireIds); + this._nameToConnection.set(connectionName, connection); + } + } + + /** + * @returns {Map} + */ + get nameToConnection() { + return this._nameToConnection; + } + + get type() { + return this.rawCell.type; } get id() { @@ -90,8 +192,16 @@ export class Connection { this.cell = cell; this.name = name; this.wireIds = wireIds; - - + + /** + * @type {Wire[]} + */ + this.wires = []; + + for (let i = 0; i < wireIds.length; ++ i) { + const wire = new Wire(this, i, wireIds[i]); + this.wireIds.push(wire); + } } get id() {