完成 模块树 构建

This commit is contained in:
锦恢 2024-12-21 03:12:40 +08:00
parent 4a03fa2308
commit 3620b3c9f3
2 changed files with 198 additions and 78 deletions

View File

@ -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);
}
}
}
}
}

View File

@ -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<string, Port>}
*/
this._nameToPort = new Map();
/**
* @type {Map<string, Cell>}
*/
this._nameToCell = new Map();
// 给非常数的 wireId 构建映射
// 因为非常数类型的 wireId 只关联 port 和 connection
/**
* @type {Map<number, Port>}
*/
this._wireIdToPort = new Map();
/**
* @type {Map<number, Connection>}
*/
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<string, Port>}
*/
get nameToPort() {
return this._nameToPort;
}
/**
* @returns {Map<string, Cell>}
*/
get nameToCell() {
return this._nameToCell;
}
/**
* @type {Map<number, Port>}
*/
get wireIdToPort() {
return this._wireIdToPort;
}
/**
* @type {Map<number, Connection>}
*/
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<string, Connection>}
*/
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() {