212 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 将各个节点转换为 elknode 的数据结构
*/
import { DEVICE_MAPPER } from "./yosys";
export const LAYOUT_CONSTANT = {
PORT_WIDTH: 50,
PORT_HEIGHT: 50,
INSTANTIATION_WIDTH: 50,
INSTANTIATION_HEIGHT: 50,
};
export class ModuleLayout {
/**
* @param {string} name
* @param {YosysNetModule} module
*/
constructor(name, module) {
this.module = module;
this.name = name;
this.initialise();
}
/**
* @description 初始化,构建信息更加丰富的 RawYosysNet
*/
initialise() {
// 构建模块树
// 构建 wireId 到模块树节点的映射表
}
/**
* @description 从 ports 和 netnames 中创建所有变量相关的节点
* @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 绑定到模块树上面
const node = {
id: portId,
width: LAYOUT_CONSTANT.PORT_WIDTH,
height: LAYOUT_CONSTANT.PORT_HEIGHT,
layoutOptions: {
"elk.port.side": port.direction === "input" ? "WEST" : "EAST"
}
}
nodes.push(node);
}
// 非 port 的其他内部变量的节点不需要绘制,因为它们总能被表示为中间变量
// 它们的操作可以表示为一系列器件的组合或者直连
return nodes;
}
/**
* @description 从 cells 中创建节点
* @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);
}
}
return nodes;
}
/**
* @description 从 cells.connections 中创建边
* @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}`;
for (const [connectionName, wireIds] of Object.entries(cell.connections)) {
// 器件当前的端口的 ID为了区别与外层 module 的端口,起名为 connection
const connectionId = `${moduleName}.${cellName}.${connectionName}`;
// 遍历器件端口的每一个连接点
// 比如对于端口 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') {
// 常数
// 如果是常数,需要先创建代表常数的节点,常数一定是器件的输入,而非输出
const constant = parseInt(bit);
const constantBitId = ElkNodeId.constantBit(moduleName, cellName, connectionName, i, constant);
const node = {
id: constantBitId,
width: 50,
height: 50,
layoutOptions: {
"elk.port.side": "WEST"
}
};
nodes.push(node);
// 创建常数到器件的连线
const edge = {
id: constantBitId,
sources: [cellId],
targets: [node.id]
};
// TODO: 把创建的 edge 关联到模块树上
edges.push(edge);
} else {
// 如果不是一个常数连接,那么存在两种可能
// 1. 当前的器件的这个端口和某一个 port 连接
// 2. 当前的器件的这个端口和另一个器件的一个端口连接
// TODO: 把创建的 edge 关联到模块树上
}
}
}
}
return [nodes, edges];
}
}
/**
* @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);
}
}