完成 模块树 构建
This commit is contained in:
parent
4a03fa2308
commit
3620b3c9f3
@ -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
|
||||
* @description
|
||||
* @type {ModuleTree}
|
||||
*/
|
||||
initialise() {
|
||||
// 构建模块树
|
||||
|
||||
|
||||
// 构建 wireId 到模块树节点的映射表
|
||||
|
||||
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
|
||||
|
||||
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: cellId,
|
||||
width: 50,
|
||||
height: 50,
|
||||
id: cell.id,
|
||||
width, height,
|
||||
layoutOptions: {
|
||||
"elk.port.side": "NORTH"
|
||||
'elk.port.side': side
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
// 当前器件的 ID
|
||||
const cellId = `${moduleName}.${cellName}`;
|
||||
const tree = this.moduleTree
|
||||
|
||||
for (const [connectionName, wireIds] of Object.entries(cell.connections)) {
|
||||
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);
|
||||
// 器件当前的端口的 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,24 +145,39 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return [nodes, edges];
|
||||
|
@ -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() {
|
||||
@ -91,7 +193,15 @@ export class Connection {
|
||||
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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user