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