From c72d937c2b3ed3189c4cd3446515057d1964be84 Mon Sep 17 00:00:00 2001 From: djwubs Date: Thu, 12 Mar 2020 20:49:38 +0100 Subject: [PATCH 01/32] Preparation of FlatModule for hierarchy --- built/FlatModule.js | 42 +++++++++++++++++++++++----------- built/drawModule.js | 11 +++++++-- built/index.js | 23 +++++-------------- lib/FlatModule.ts | 50 +++++++++++++++++++++++++++++------------ lib/index.ts | 24 +++++--------------- test/FlatModule.test.ts | 4 ++-- 6 files changed, 88 insertions(+), 66 deletions(-) diff --git a/built/FlatModule.js b/built/FlatModule.js index bbed9df..a3bf4d3 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -4,25 +4,41 @@ var Skin_1 = require("./Skin"); var Cell_1 = require("./Cell"); var _ = require("lodash"); var FlatModule = /** @class */ (function () { - function FlatModule(netlist) { - var _this = this; - this.moduleName = null; + function FlatModule(mod, name, parent) { + if (parent === void 0) { parent = null; } + this.parent = parent; + this.moduleName = name; + var ports = _.map(mod.ports, Cell_1.default.fromPort); + var cells = _.map(mod.cells, function (c, key) { return Cell_1.default.fromYosysCell(c, key); }); + this.nodes = cells.concat(ports); + // this can be skipped if there are no 0's or 1's + if (FlatModule.layoutProps.constants !== false) { + this.addConstants(); + } + // this can be skipped if there are no splits or joins + if (FlatModule.layoutProps.splitsAndJoins !== false) { + this.addSplitsJoins(); + } + this.createWires(); + } + FlatModule.fromNetlist = function (netlist, skin) { + this.skin = skin; + this.layoutProps = skin.getProperties(); + this.modNames = Object.keys(netlist.modules); + this.netlist = netlist; + var topName = null; _.forEach(netlist.modules, function (mod, name) { if (mod.attributes && mod.attributes.top === 1) { - _this.moduleName = name; + topName = name; } }); // Otherwise default the first one in the file... - if (this.moduleName == null) { - this.moduleName = Object.keys(netlist.modules)[0]; + if (topName == null) { + topName = this.modNames[0]; } - var top = netlist.modules[this.moduleName]; - var ports = _.map(top.ports, Cell_1.default.fromPort); - var cells = _.map(top.cells, function (c, key) { return Cell_1.default.fromYosysCell(c, key); }); - this.nodes = cells.concat(ports); - // populated by createWires - this.wires = []; - } + var top = netlist.modules[topName]; + return new FlatModule(top, topName); + }; // converts input ports with constant assignments to constant nodes FlatModule.prototype.addConstants = function () { // find the maximum signal number diff --git a/built/drawModule.js b/built/drawModule.js index 37337ae..6ea1a22 100644 --- a/built/drawModule.js +++ b/built/drawModule.js @@ -1,4 +1,11 @@ "use strict"; +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; Object.defineProperty(exports, "__esModule", { value: true }); var elkGraph_1 = require("./elkGraph"); var Skin_1 = require("./Skin"); @@ -67,8 +74,8 @@ function drawModule(g, module) { } }, }); - var elements = [styles].concat(nodes, lines); - var ret = ['svg', svgAttrs].concat(elements); + var elements = __spreadArrays([styles], nodes, lines); + var ret = __spreadArrays(['svg', svgAttrs], elements); return onml.s(ret); } exports.default = drawModule; diff --git a/built/index.js b/built/index.js index 5daabde..5811661 100644 --- a/built/index.js +++ b/built/index.js @@ -7,23 +7,10 @@ var Skin_1 = require("./Skin"); var elkGraph_1 = require("./elkGraph"); var drawModule_1 = require("./drawModule"); var elk = new ELK(); -function createFlatModule(skinData, yosysNetlist) { - Skin_1.default.skin = onml.p(skinData); - var layoutProps = Skin_1.default.getProperties(); - var flatModule = new FlatModule_1.FlatModule(yosysNetlist); - // this can be skipped if there are no 0's or 1's - if (layoutProps.constants !== false) { - flatModule.addConstants(); - } - // this can be skipped if there are no splits or joins - if (layoutProps.splitsAndJoins !== false) { - flatModule.addSplitsJoins(); - } - flatModule.createWires(); - return flatModule; -} function dumpLayout(skinData, yosysNetlist, prelayout, done) { - var flatModule = createFlatModule(skinData, yosysNetlist); + var skin = onml.p(skinData); + Skin_1.default.skin = skin; + var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist, Skin_1.default); var kgraph = elkGraph_1.buildElkGraph(flatModule); if (prelayout) { done(null, JSON.stringify(kgraph, null, 2)); @@ -39,7 +26,9 @@ function dumpLayout(skinData, yosysNetlist, prelayout, done) { } exports.dumpLayout = dumpLayout; function render(skinData, yosysNetlist, done, elkData) { - var flatModule = createFlatModule(skinData, yosysNetlist); + var skin = onml.p(skinData); + Skin_1.default.skin = skin; + var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist, Skin_1.default); var kgraph = elkGraph_1.buildElkGraph(flatModule); var layoutProps = Skin_1.default.getProperties(); var promise; diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index d6268ce..3828f5e 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -18,27 +18,49 @@ export interface Wire { } export class FlatModule { - public moduleName: string; - public nodes: Cell[]; - public wires: Wire[]; - - constructor(netlist: Yosys.Netlist) { - this.moduleName = null; + public static netlist: Yosys.Netlist; + public static skin: any; + public static layoutProps: {[x: string]: any}; + public static modNames: string[]; + public static fromNetlist(netlist: Yosys.Netlist, skin: any): FlatModule { + this.skin = skin; + this.layoutProps = skin.getProperties(); + this.modNames = Object.keys(netlist.modules); + this.netlist = netlist; + let topName = null; _.forEach(netlist.modules, (mod: Yosys.Module, name: string) => { if (mod.attributes && mod.attributes.top === 1) { - this.moduleName = name; + topName = name; } }); // Otherwise default the first one in the file... - if (this.moduleName == null) { - this.moduleName = Object.keys(netlist.modules)[0]; + if (topName == null) { + topName = this.modNames[0]; } - const top = netlist.modules[this.moduleName]; - const ports = _.map(top.ports, Cell.fromPort); - const cells = _.map(top.cells, (c, key) => Cell.fromYosysCell(c, key)); + const top = netlist.modules[topName]; + return new FlatModule(top, topName); + } + + public parent: FlatModule; + public moduleName: string; + public nodes: Cell[]; + public wires: Wire[]; + + constructor(mod: Yosys.Module, name: string, parent: FlatModule = null) { + this.parent = parent; + this.moduleName = name; + const ports = _.map(mod.ports, Cell.fromPort); + const cells = _.map(mod.cells, (c, key) => Cell.fromYosysCell(c, key)); this.nodes = cells.concat(ports); - // populated by createWires - this.wires = []; + // this can be skipped if there are no 0's or 1's + if (FlatModule.layoutProps.constants !== false) { + this.addConstants(); + } + // this can be skipped if there are no splits or joins + if (FlatModule.layoutProps.splitsAndJoins !== false) { + this.addSplitsJoins(); + } + this.createWires(); } // converts input ports with constant assignments to constant nodes diff --git a/lib/index.ts b/lib/index.ts index 818d9cb..525ba38 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -13,24 +13,10 @@ const elk = new ELK(); type ICallback = (error: Error, result?: string) => void; -function createFlatModule(skinData: string, yosysNetlist: Yosys.Netlist): FlatModule { - Skin.skin = onml.p(skinData); - const layoutProps = Skin.getProperties(); - const flatModule = new FlatModule(yosysNetlist); - // this can be skipped if there are no 0's or 1's - if (layoutProps.constants !== false) { - flatModule.addConstants(); - } - // this can be skipped if there are no splits or joins - if (layoutProps.splitsAndJoins !== false) { - flatModule.addSplitsJoins(); - } - flatModule.createWires(); - return flatModule; -} - export function dumpLayout(skinData: string, yosysNetlist: Yosys.Netlist, prelayout: boolean, done: ICallback) { - const flatModule = createFlatModule(skinData, yosysNetlist); + const skin = onml.p(skinData); + Skin.skin = skin; + const flatModule = FlatModule.fromNetlist(yosysNetlist, Skin); const kgraph: ElkModel.Graph = buildElkGraph(flatModule); if (prelayout) { done(null, JSON.stringify(kgraph, null, 2)); @@ -46,7 +32,9 @@ export function dumpLayout(skinData: string, yosysNetlist: Yosys.Netlist, prelay } export function render(skinData: string, yosysNetlist: Yosys.Netlist, done?: ICallback, elkData?: ElkModel.Graph) { - const flatModule = createFlatModule(skinData, yosysNetlist); + const skin = onml.p(skinData); + Skin.skin = skin; + const flatModule = FlatModule.fromNetlist(yosysNetlist, Skin); const kgraph: ElkModel.Graph = buildElkGraph(flatModule); const layoutProps = Skin.getProperties(); diff --git a/test/FlatModule.test.ts b/test/FlatModule.test.ts index a782d3a..a18f9ef 100644 --- a/test/FlatModule.test.ts +++ b/test/FlatModule.test.ts @@ -19,7 +19,7 @@ function createFlatModule(testFile: string): FlatModule { const netlist: Yosys.Netlist = json5.parse(testStr); const skin = onml.parse(fs.readFileSync(defaultSkin).toString()); Skin.skin = skin; - return new FlatModule(netlist); + return FlatModule.fromNetlist(netlist, Skin); } /** @@ -32,7 +32,7 @@ test('split join', () => { flatModule.addSplitsJoins(); const nodes = flatModule.nodes; // should have 3 more nodes, one split, two joins - expect(nodes.length - numStartNodes).toEqual(3); + expect(nodes.length - numStartNodes).toEqual(0); const splits = nodes.filter( (node: Cell) => node.Type === '$_split_'); expect(splits.length).toEqual(1); const split = splits[0]; From 2eac567a46cb696286af34efcda3ef61c8b0687c Mon Sep 17 00:00:00 2001 From: djwubs Date: Sat, 14 Mar 2020 19:35:21 +0100 Subject: [PATCH 02/32] Preparation of Cell for hierarchy --- built/Cell.js | 48 +++++++++++++++++++++++++++++----------- built/FlatModule.js | 14 ++++++------ built/Port.js | 10 ++++----- built/index.js | 6 ++--- lib/Cell.ts | 53 +++++++++++++++++++++++++++++++++------------ lib/FlatModule.ts | 11 +++++----- lib/Port.ts | 16 +++++++++----- lib/index.ts | 6 ++--- test/Cell.test.ts | 10 ++++----- 9 files changed, 110 insertions(+), 64 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 8259998..7a0f43e 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -8,13 +8,16 @@ var _ = require("lodash"); var clone = require("clone"); var onml = require("onml"); var Cell = /** @class */ (function () { - function Cell(key, type, inputPorts, outputPorts, attributes) { + function Cell(key, type, inputPorts, outputPorts, attributes, parent, subModule) { var _this = this; + if (subModule === void 0) { subModule = null; } this.key = key; this.type = type; this.inputPorts = inputPorts; this.outputPorts = outputPorts; this.attributes = attributes || {}; + this.parent = parent; + this.subModule = subModule; inputPorts.forEach(function (ip) { ip.parentNode = _this; }); @@ -27,14 +30,14 @@ var Cell = /** @class */ (function () { * @param yPort the Yosys Port with our port data * @param name the name of the port */ - Cell.fromPort = function (yPort, name) { + Cell.fromPort = function (yPort, name, parent) { var isInput = yPort.direction === YosysModel_1.default.Direction.Input; if (isInput) { - return new Cell(name, '$_inputExt_', [], [new Port_1.Port('Y', yPort.bits)], {}); + return new Cell(name, '$_inputExt_', [], [new Port_1.Port('Y', yPort.bits)], {}, parent); } - return new Cell(name, '$_outputExt_', [new Port_1.Port('A', yPort.bits)], [], {}); + return new Cell(name, '$_outputExt_', [new Port_1.Port('A', yPort.bits)], [], {}, parent); }; - Cell.fromYosysCell = function (yCell, name) { + Cell.fromYosysCell = function (yCell, name, parent) { var template = Skin_1.default.findSkinType(yCell.type); var templateInputPids = Skin_1.default.getInputPids(template); var templateOutputPids = Skin_1.default.getOutputPids(template); @@ -49,31 +52,31 @@ var Cell = /** @class */ (function () { inputPorts = ports.filter(function (port) { return port.keyIn(inputPids_1); }); outputPorts = ports.filter(function (port) { return port.keyIn(outputPids_1); }); } - return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent); }; - Cell.fromConstantInfo = function (name, constants) { - return new Cell(name, '$_constant_', [], [new Port_1.Port('Y', constants)], {}); + Cell.fromConstantInfo = function (name, constants, parent) { + return new Cell(name, '$_constant_', [], [new Port_1.Port('Y', constants)], {}, parent); }; /** * creates a join cell * @param target string name of net (starts and ends with and delimited by commas) * @param sources list of index strings (one number, or two numbers separated by a colon) */ - Cell.fromJoinInfo = function (target, sources) { + Cell.fromJoinInfo = function (target, sources, parent) { var signalStrs = target.slice(1, -1).split(','); var signals = signalStrs.map(function (ss) { return Number(ss); }); var joinOutPorts = [new Port_1.Port('Y', signals)]; var inPorts = sources.map(function (name) { return new Port_1.Port(name, getBits(signals, name)); }); - return new Cell('$join$' + target, '$_join_', inPorts, joinOutPorts, {}); + return new Cell('$join$' + target, '$_join_', inPorts, joinOutPorts, {}, parent); }; /** * creates a split cell * @param source string name of net (starts and ends with and delimited by commas) * @param targets list of index strings (one number, or two numbers separated by a colon) */ - Cell.fromSplitInfo = function (source, targets) { + Cell.fromSplitInfo = function (source, targets, parent) { // turn string into array of signal names var sigStrs = source.slice(1, -1).split(','); // convert the signals into actual numbers @@ -84,7 +87,25 @@ var Cell = /** @class */ (function () { var sigs = getBits(signals, name); return new Port_1.Port(name, sigs); }); - return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}); + return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}, parent); + }; + Cell.createSubModule = function (yCell, name, parent, subModule) { + var template = Skin_1.default.findSkinType(yCell.type); + var templateInputPids = Skin_1.default.getInputPids(template); + var templateOutputPids = Skin_1.default.getOutputPids(template); + var ports = _.map(yCell.connections, function (conn, portName) { + return new Port_1.Port(portName, conn); + }); + var inputPorts = ports.filter(function (port) { return port.keyIn(templateInputPids); }); + var outputPorts = ports.filter(function (port) { return port.keyIn(templateOutputPids); }); + if (inputPorts.length + outputPorts.length !== ports.length) { + var inputPids_2 = YosysModel_1.default.getInputPortPids(yCell); + var outputPids_2 = YosysModel_1.default.getOutputPortPids(yCell); + inputPorts = ports.filter(function (port) { return port.keyIn(inputPids_2); }); + outputPorts = ports.filter(function (port) { return port.keyIn(outputPids_2); }); + } + var mod = new FlatModule_1.FlatModule(subModule, name, parent); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod); }; Object.defineProperty(Cell.prototype, "Type", { get: function () { @@ -119,8 +140,9 @@ var Cell = /** @class */ (function () { return _.max([maxVal, atLeast]); }; Cell.prototype.findConstants = function (sigsByConstantName, maxNum, constantCollector) { + var _this = this; this.inputPorts.forEach(function (ip) { - maxNum = ip.findConstants(sigsByConstantName, maxNum, constantCollector); + maxNum = ip.findConstants(sigsByConstantName, maxNum, constantCollector, _this.parent); }); return maxNum; }; diff --git a/built/FlatModule.js b/built/FlatModule.js index a3bf4d3..794f2dd 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -1,15 +1,15 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var Skin_1 = require("./Skin"); var Cell_1 = require("./Cell"); var _ = require("lodash"); var FlatModule = /** @class */ (function () { function FlatModule(mod, name, parent) { + var _this = this; if (parent === void 0) { parent = null; } this.parent = parent; this.moduleName = name; - var ports = _.map(mod.ports, Cell_1.default.fromPort); - var cells = _.map(mod.cells, function (c, key) { return Cell_1.default.fromYosysCell(c, key); }); + var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this); }); + var cells = _.map(mod.cells, function (c, key) { return Cell_1.default.fromYosysCell(c, key, _this); }); this.nodes = cells.concat(ports); // this can be skipped if there are no 0's or 1's if (FlatModule.layoutProps.constants !== false) { @@ -53,6 +53,7 @@ var FlatModule = /** @class */ (function () { }; // solves for minimal bus splits and joins and adds them to module FlatModule.prototype.addSplitsJoins = function () { + var _this = this; var allInputs = _.flatMap(this.nodes, function (n) { return n.inputPortVals(); }); var allOutputs = _.flatMap(this.nodes, function (n) { return n.outputPortVals(); }); var allInputsCopy = allInputs.slice(); @@ -62,19 +63,18 @@ var FlatModule = /** @class */ (function () { gather(allOutputs, allInputsCopy, input, 0, input.length, splits, joins); }); this.nodes = this.nodes.concat(_.map(joins, function (joinOutput, joinInputs) { - return Cell_1.default.fromJoinInfo(joinInputs, joinOutput); + return Cell_1.default.fromJoinInfo(joinInputs, joinOutput, _this); })).concat(_.map(splits, function (splitOutputs, splitInput) { - return Cell_1.default.fromSplitInfo(splitInput, splitOutputs); + return Cell_1.default.fromSplitInfo(splitInput, splitOutputs, _this); })); }; // search through all the ports to find all of the wires FlatModule.prototype.createWires = function () { - var layoutProps = Skin_1.default.getProperties(); var ridersByNet = {}; var driversByNet = {}; var lateralsByNet = {}; this.nodes.forEach(function (n) { - n.collectPortsByDirection(ridersByNet, driversByNet, lateralsByNet, layoutProps.genericsLaterals); + n.collectPortsByDirection(ridersByNet, driversByNet, lateralsByNet, FlatModule.layoutProps.genericsLaterals); }); // list of unique nets var nets = removeDups(_.keys(ridersByNet).concat(_.keys(driversByNet)).concat(_.keys(lateralsByNet))); diff --git a/built/Port.js b/built/Port.js index 7833d0d..6d9a6bb 100644 --- a/built/Port.js +++ b/built/Port.js @@ -23,7 +23,7 @@ var Port = /** @class */ (function () { Port.prototype.valString = function () { return ',' + this.value.join() + ','; }; - Port.prototype.findConstants = function (sigsByConstantName, maxNum, constantCollector) { + Port.prototype.findConstants = function (sigsByConstantName, maxNum, constantCollector, parent) { var _this = this; var constNameCollector = ''; var constNumCollector = []; @@ -39,14 +39,14 @@ var Port = /** @class */ (function () { // string of constants ended before end of p.value } else if (constNumCollector.length > 0) { - _this.assignConstant(constNameCollector, constNumCollector, portSigIndex, sigsByConstantName, constantCollector); + _this.assignConstant(constNameCollector, constNumCollector, portSigIndex, sigsByConstantName, constantCollector, parent); // reset name and num collectors constNameCollector = ''; constNumCollector = []; } }); if (constNumCollector.length > 0) { - this.assignConstant(constNameCollector, constNumCollector, portSigs.length, sigsByConstantName, constantCollector); + this.assignConstant(constNameCollector, constNumCollector, portSigs.length, sigsByConstantName, constantCollector, parent); } return maxNum; }; @@ -105,7 +105,7 @@ var Port = /** @class */ (function () { return ret; } }; - Port.prototype.assignConstant = function (nameCollector, constants, currIndex, signalsByConstantName, constantCollector) { + Port.prototype.assignConstant = function (nameCollector, constants, currIndex, signalsByConstantName, constantCollector, parent) { var _this = this; // we've been appending to nameCollector, so reverse to get const name var constName = nameCollector.split('').reverse().join(''); @@ -121,7 +121,7 @@ var Port = /** @class */ (function () { }); } else { - constantCollector.push(Cell_1.default.fromConstantInfo(constName, constants)); + constantCollector.push(Cell_1.default.fromConstantInfo(constName, constants, parent)); signalsByConstantName[constName] = constants; } }; diff --git a/built/index.js b/built/index.js index 5811661..ae7cc33 100644 --- a/built/index.js +++ b/built/index.js @@ -16,8 +16,7 @@ function dumpLayout(skinData, yosysNetlist, prelayout, done) { done(null, JSON.stringify(kgraph, null, 2)); return; } - var layoutProps = Skin_1.default.getProperties(); - var promise = elk.layout(kgraph, { layoutOptions: layoutProps.layoutEngine }); + var promise = elk.layout(kgraph, { layoutOptions: FlatModule_1.FlatModule.layoutProps.layoutEngine }); promise.then(function (graph) { done(null, JSON.stringify(graph, null, 2)); }).catch(function (reason) { @@ -30,7 +29,6 @@ function render(skinData, yosysNetlist, done, elkData) { Skin_1.default.skin = skin; var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist, Skin_1.default); var kgraph = elkGraph_1.buildElkGraph(flatModule); - var layoutProps = Skin_1.default.getProperties(); var promise; // if we already have a layout then use it if (elkData) { @@ -41,7 +39,7 @@ function render(skinData, yosysNetlist, done, elkData) { } else { // otherwise use ELK to generate the layout - promise = elk.layout(kgraph, { layoutOptions: layoutProps.layoutEngine }) + promise = elk.layout(kgraph, { layoutOptions: FlatModule_1.FlatModule.layoutProps.layoutEngine }) .then(function (g) { return drawModule_1.default(g, flatModule); }) // tslint:disable-next-line:no-console .catch(function (e) { console.error(e); }); diff --git a/lib/Cell.ts b/lib/Cell.ts index a73fdbc..b43bf00 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -1,4 +1,4 @@ -import { SigsByConstName, NameToPorts, addToDefaultDict } from './FlatModule'; +import { SigsByConstName, NameToPorts, addToDefaultDict, FlatModule } from './FlatModule'; import Yosys from './YosysModel'; import Skin from './Skin'; import {Port} from './Port'; @@ -13,15 +13,15 @@ export default class Cell { * @param yPort the Yosys Port with our port data * @param name the name of the port */ - public static fromPort(yPort: Yosys.ExtPort, name: string): Cell { + public static fromPort(yPort: Yosys.ExtPort, name: string, parent: FlatModule): Cell { const isInput: boolean = yPort.direction === Yosys.Direction.Input; if (isInput) { - return new Cell(name, '$_inputExt_', [], [new Port('Y', yPort.bits)], {}); + return new Cell(name, '$_inputExt_', [], [new Port('Y', yPort.bits)], {}, parent); } - return new Cell(name, '$_outputExt_', [new Port('A', yPort.bits)], [], {}); + return new Cell(name, '$_outputExt_', [new Port('A', yPort.bits)], [], {}, parent); } - public static fromYosysCell(yCell: Yosys.Cell, name: string) { + public static fromYosysCell(yCell: Yosys.Cell, name: string, parent: FlatModule) { const template = Skin.findSkinType(yCell.type); const templateInputPids = Skin.getInputPids(template); const templateOutputPids = Skin.getOutputPids(template); @@ -36,11 +36,11 @@ export default class Cell { inputPorts = ports.filter((port) => port.keyIn(inputPids)); outputPorts = ports.filter((port) => port.keyIn(outputPids)); } - return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent); } - public static fromConstantInfo(name: string, constants: number[]): Cell { - return new Cell(name, '$_constant_', [], [new Port('Y', constants)], {}); + public static fromConstantInfo(name: string, constants: number[], parent: FlatModule): Cell { + return new Cell(name, '$_constant_', [], [new Port('Y', constants)], {}, parent); } /** @@ -48,14 +48,14 @@ export default class Cell { * @param target string name of net (starts and ends with and delimited by commas) * @param sources list of index strings (one number, or two numbers separated by a colon) */ - public static fromJoinInfo(target: string, sources: string[]): Cell { + public static fromJoinInfo(target: string, sources: string[], parent: FlatModule): Cell { const signalStrs: string[] = target.slice(1, -1).split(','); const signals: number[] = signalStrs.map((ss) => Number(ss)); const joinOutPorts: Port[] = [new Port('Y', signals)]; const inPorts: Port[] = sources.map((name) => { return new Port(name, getBits(signals, name)); }); - return new Cell('$join$' + target, '$_join_', inPorts, joinOutPorts, {}); + return new Cell('$join$' + target, '$_join_', inPorts, joinOutPorts, {}, parent); } /** @@ -63,7 +63,7 @@ export default class Cell { * @param source string name of net (starts and ends with and delimited by commas) * @param targets list of index strings (one number, or two numbers separated by a colon) */ - public static fromSplitInfo(source: string, targets: string[]): Cell { + public static fromSplitInfo(source: string, targets: string[], parent: FlatModule): Cell { // turn string into array of signal names const sigStrs: string[] = source.slice(1, -1).split(','); // convert the signals into actual numbers @@ -74,9 +74,30 @@ export default class Cell { const sigs: Yosys.Signals = getBits(signals, name); return new Port(name, sigs); }); - return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}); + return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}, parent); } + public static createSubModule(yCell: Yosys.Cell, name: string, parent: FlatModule, subModule: Yosys.Module): Cell { + const template = Skin.findSkinType(yCell.type); + const templateInputPids = Skin.getInputPids(template); + const templateOutputPids = Skin.getOutputPids(template); + const ports: Port[] = _.map(yCell.connections, (conn, portName) => { + return new Port(portName, conn); + }); + let inputPorts = ports.filter((port) => port.keyIn(templateInputPids)); + let outputPorts = ports.filter((port) => port.keyIn(templateOutputPids)); + if (inputPorts.length + outputPorts.length !== ports.length) { + const inputPids: string[] = Yosys.getInputPortPids(yCell); + const outputPids: string[] = Yosys.getOutputPortPids(yCell); + inputPorts = ports.filter((port) => port.keyIn(inputPids)); + outputPorts = ports.filter((port) => port.keyIn(outputPids)); + } + const mod = new FlatModule(subModule, name, parent); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod); + } + + public parent: FlatModule; + public subModule: FlatModule; protected key: string; protected type: string; protected inputPorts: Port[]; @@ -87,12 +108,16 @@ export default class Cell { type: string, inputPorts: Port[], outputPorts: Port[], - attributes: Yosys.CellAttributes) { + attributes: Yosys.CellAttributes, + parent: FlatModule, + subModule: FlatModule = null) { this.key = key; this.type = type; this.inputPorts = inputPorts; this.outputPorts = outputPorts; this.attributes = attributes || {}; + this.parent = parent; + this.subModule = subModule; inputPorts.forEach((ip) => { ip.parentNode = this; }); @@ -126,7 +151,7 @@ export default class Cell { maxNum: number, constantCollector: Cell[]): number { this.inputPorts.forEach((ip) => { - maxNum = ip.findConstants(sigsByConstantName, maxNum, constantCollector); + maxNum = ip.findConstants(sigsByConstantName, maxNum, constantCollector, this.parent); }); return maxNum; } diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 3828f5e..ae0a861 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -49,8 +49,8 @@ export class FlatModule { constructor(mod: Yosys.Module, name: string, parent: FlatModule = null) { this.parent = parent; this.moduleName = name; - const ports = _.map(mod.ports, Cell.fromPort); - const cells = _.map(mod.cells, (c, key) => Cell.fromYosysCell(c, key)); + const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this)); + const cells = _.map(mod.cells, (c, key) => Cell.fromYosysCell(c, key, this)); this.nodes = cells.concat(ports); // this can be skipped if there are no 0's or 1's if (FlatModule.layoutProps.constants !== false) { @@ -97,15 +97,14 @@ export class FlatModule { }); this.nodes = this.nodes.concat(_.map(joins, (joinOutput, joinInputs) => { - return Cell.fromJoinInfo(joinInputs, joinOutput); + return Cell.fromJoinInfo(joinInputs, joinOutput, this); })).concat(_.map(splits, (splitOutputs, splitInput) => { - return Cell.fromSplitInfo(splitInput, splitOutputs); + return Cell.fromSplitInfo(splitInput, splitOutputs, this); })); } // search through all the ports to find all of the wires public createWires() { - const layoutProps = Skin.getProperties(); const ridersByNet: NameToPorts = {}; const driversByNet: NameToPorts = {}; const lateralsByNet: NameToPorts = {}; @@ -114,7 +113,7 @@ export class FlatModule { ridersByNet, driversByNet, lateralsByNet, - layoutProps.genericsLaterals as boolean); + FlatModule.layoutProps.genericsLaterals as boolean); }); // list of unique nets const nets = removeDups(_.keys(ridersByNet).concat(_.keys(driversByNet)).concat(_.keys(lateralsByNet))); diff --git a/lib/Port.ts b/lib/Port.ts index 04f9d14..0386799 100644 --- a/lib/Port.ts +++ b/lib/Port.ts @@ -1,5 +1,5 @@ import Cell from './Cell'; -import {SigsByConstName} from './FlatModule'; +import {SigsByConstName, FlatModule} from './FlatModule'; import Yosys from './YosysModel'; import _ = require('lodash'); import { ElkModel } from './elkGraph'; @@ -32,7 +32,8 @@ export class Port { public findConstants(sigsByConstantName: SigsByConstName, maxNum: number, - constantCollector: Cell[]): number { + constantCollector: Cell[], + parent: FlatModule): number { let constNameCollector = ''; let constNumCollector: number[] = []; const portSigs: Yosys.Signals = this.value; @@ -51,7 +52,8 @@ export class Port { constNumCollector, portSigIndex, sigsByConstantName, - constantCollector); + constantCollector, + parent); // reset name and num collectors constNameCollector = ''; constNumCollector = []; @@ -63,7 +65,8 @@ export class Port { constNumCollector, portSigs.length, sigsByConstantName, - constantCollector); + constantCollector, + parent); } return maxNum; } @@ -133,7 +136,8 @@ export class Port { constants: number[], currIndex: number, signalsByConstantName: SigsByConstName, - constantCollector: Cell[]) { + constantCollector: Cell[], + parent: FlatModule) { // we've been appending to nameCollector, so reverse to get const name const constName = nameCollector.split('').reverse().join(''); // if the constant has already been used @@ -147,7 +151,7 @@ export class Port { this.value[i] = constSig; }); } else { - constantCollector.push(Cell.fromConstantInfo(constName, constants)); + constantCollector.push(Cell.fromConstantInfo(constName, constants, parent)); signalsByConstantName[constName] = constants; } } diff --git a/lib/index.ts b/lib/index.ts index 525ba38..7016b2e 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -22,8 +22,7 @@ export function dumpLayout(skinData: string, yosysNetlist: Yosys.Netlist, prelay done(null, JSON.stringify(kgraph, null, 2)); return; } - const layoutProps = Skin.getProperties(); - const promise = elk.layout(kgraph, { layoutOptions: layoutProps.layoutEngine }); + const promise = elk.layout(kgraph, { layoutOptions: FlatModule.layoutProps.layoutEngine }); promise.then((graph: ElkModel.Graph) => { done(null, JSON.stringify(graph, null, 2)); }).catch((reason) => { @@ -36,7 +35,6 @@ export function render(skinData: string, yosysNetlist: Yosys.Netlist, done?: ICa Skin.skin = skin; const flatModule = FlatModule.fromNetlist(yosysNetlist, Skin); const kgraph: ElkModel.Graph = buildElkGraph(flatModule); - const layoutProps = Skin.getProperties(); let promise; // if we already have a layout then use it @@ -47,7 +45,7 @@ export function render(skinData: string, yosysNetlist: Yosys.Netlist, done?: ICa }); } else { // otherwise use ELK to generate the layout - promise = elk.layout(kgraph, { layoutOptions: layoutProps.layoutEngine }) + promise = elk.layout(kgraph, { layoutOptions: FlatModule.layoutProps.layoutEngine }) .then((g) => drawModule(g, flatModule)) // tslint:disable-next-line:no-console .catch((e) => { console.error(e); }); diff --git a/test/Cell.test.ts b/test/Cell.test.ts index 7c93c4d..58468e9 100644 --- a/test/Cell.test.ts +++ b/test/Cell.test.ts @@ -3,7 +3,7 @@ import Yosys from '../lib/YosysModel'; test('Create Cell from Yosys Input Port', () => { const inputPort: Yosys.ExtPort = {direction: Yosys.Direction.Input, bits: [47, 12, 16]}; - const cell: Cell = Cell.fromPort(inputPort, 'testInput'); + const cell: Cell = Cell.fromPort(inputPort, 'testInput', null); expect(cell.Type).toEqual('$_inputExt_'); expect(cell.outputPortVals()).toEqual([',47,12,16,']); expect(cell.inputPortVals()).toEqual([]); @@ -11,28 +11,28 @@ test('Create Cell from Yosys Input Port', () => { test('Create Cell from Yosys Output Port', () => { const inputPort: Yosys.ExtPort = {direction: Yosys.Direction.Output, bits: [47, 12, 16]}; - const cell: Cell = Cell.fromPort(inputPort, 'testOutput'); + const cell: Cell = Cell.fromPort(inputPort, 'testOutput', null); expect(cell.Type).toEqual('$_outputExt_'); expect(cell.outputPortVals()).toEqual([]); expect(cell.inputPortVals()).toEqual([',47,12,16,']); }); test('Create Cell from Constant', () => { - const cell: Cell = Cell.fromConstantInfo('bob', [0, 1, 0, 1, 1]); + const cell: Cell = Cell.fromConstantInfo('bob', [0, 1, 0, 1, 1], null); expect(cell.Type).toEqual('$_constant_'); expect(cell.outputPortVals()).toEqual([',0,1,0,1,1,']); expect(cell.inputPortVals()).toEqual([]); }); test('Create Cell from Join', () => { - const cell: Cell = Cell.fromJoinInfo(',3,4,5,', ['0', '1:2']); + const cell: Cell = Cell.fromJoinInfo(',3,4,5,', ['0', '1:2'], null); expect(cell.Type).toEqual('$_join_'); expect(cell.inputPortVals()).toEqual([',3,', ',4,5,']); expect(cell.outputPortVals()).toEqual([',3,4,5,']); }); test('Create Cell from Split', () => { - const cell: Cell = Cell.fromSplitInfo(',3,4,5,', ['0:1', '2']); + const cell: Cell = Cell.fromSplitInfo(',3,4,5,', ['0:1', '2'], null); expect(cell.Type).toEqual('$_split_'); expect(cell.inputPortVals()).toEqual([',3,4,5,']); expect(cell.outputPortVals()).toEqual([',3,4,', ',5,']); From cbff383b67298e5e3381d0cf3d8ac4f336baa970 Mon Sep 17 00:00:00 2001 From: djwubs Date: Sun, 15 Mar 2020 17:03:12 +0100 Subject: [PATCH 03/32] Hierarchy changes for elkGraph (not working) --- built/Cell.js | 39 ++++++++++++++++++++++++++++++++++++++- built/FlatModule.js | 9 ++++++++- lib/Cell.ts | 38 ++++++++++++++++++++++++++++++++++++-- lib/FlatModule.ts | 9 ++++++++- lib/elkGraph.ts | 6 ++++-- 5 files changed, 94 insertions(+), 7 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 7a0f43e..4755a74 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -5,6 +5,7 @@ var YosysModel_1 = require("./YosysModel"); var Skin_1 = require("./Skin"); var Port_1 = require("./Port"); var _ = require("lodash"); +var elkGraph_1 = require("./elkGraph"); var clone = require("clone"); var onml = require("onml"); var Cell = /** @class */ (function () { @@ -206,7 +207,7 @@ var Cell = /** @class */ (function () { } if (type === 'join' || type === 'split' || - type === 'generic') { + (type === 'generic' && this.subModule === null)) { var inTemplates_1 = Skin_1.default.getPortsWithPrefix(template, 'in'); var outTemplates_1 = Skin_1.default.getPortsWithPrefix(template, 'out'); var inPorts = this.inputPorts.map(function (ip, i) { @@ -232,6 +233,42 @@ var Cell = /** @class */ (function () { this.addLabels(template, cell); return cell; } + if (type === 'generic' && this.subModule !== null) { + var inTemplates_2 = Skin_1.default.getPortsWithPrefix(template, 'in'); + var outTemplates_2 = Skin_1.default.getPortsWithPrefix(template, 'out'); + var inPorts = this.inputPorts.map(function (ip, i) { + return ip.getGenericElkPort(i, inTemplates_2, 'in'); + }); + var outPorts = this.outputPorts.map(function (op, i) { + return op.getGenericElkPort(i, outTemplates_2, 'out'); + }); + var elk = elkGraph_1.buildElkGraph(this.subModule); + var cell_1 = { + id: this.key, + layoutOptions: layoutAttrs, + labels: [], + ports: inPorts.concat(outPorts), + children: [], + edges: [], + }; + // Bad practice, solution? + _.forEach(cell_1.ports, function (port) { + delete port.x; + delete port.y; + }); + _.forEach(elk.children, function (child) { + var inc = true; + _.forEach(cell_1.ports, function (port) { + if (_this.key + '.' + child.id === port.id) { + inc = false; + } + }); + if (inc) { + cell_1.children.push(child); + } + }); + return cell_1; + } var ports = Skin_1.default.getPortsWithPrefix(template, '').map(function (tp) { return { id: _this.key + '.' + tp[1]['s:pid'], diff --git a/built/FlatModule.js b/built/FlatModule.js index 794f2dd..2b5c62b 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -9,7 +9,14 @@ var FlatModule = /** @class */ (function () { this.parent = parent; this.moduleName = name; var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this); }); - var cells = _.map(mod.cells, function (c, key) { return Cell_1.default.fromYosysCell(c, key, _this); }); + var cells = _.map(mod.cells, function (c, key) { + if (!_.includes(FlatModule.modNames, c.type)) { + return Cell_1.default.fromYosysCell(c, key, _this); + } + else { + return Cell_1.default.createSubModule(c, key, _this, FlatModule.netlist.modules[c.type]); + } + }); this.nodes = cells.concat(ports); // this can be skipped if there are no 0's or 1's if (FlatModule.layoutProps.constants !== false) { diff --git a/lib/Cell.ts b/lib/Cell.ts index b43bf00..0c12184 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -3,7 +3,7 @@ import Yosys from './YosysModel'; import Skin from './Skin'; import {Port} from './Port'; import _ = require('lodash'); -import { ElkModel } from './elkGraph'; +import { ElkModel, buildElkGraph } from './elkGraph'; import clone = require('clone'); import onml = require('onml'); @@ -221,7 +221,7 @@ export default class Cell { } if (type === 'join' || type === 'split' || - type === 'generic') { + (type === 'generic' && this.subModule === null)) { const inTemplates: any[] = Skin.getPortsWithPrefix(template, 'in'); const outTemplates: any[] = Skin.getPortsWithPrefix(template, 'out'); const inPorts = this.inputPorts.map((ip, i) => @@ -245,6 +245,40 @@ export default class Cell { this.addLabels(template, cell); return cell; } + if (type === 'generic' && this.subModule !== null) { + const inTemplates: any[] = Skin.getPortsWithPrefix(template, 'in'); + const outTemplates: any[] = Skin.getPortsWithPrefix(template, 'out'); + const inPorts = this.inputPorts.map((ip, i) => + ip.getGenericElkPort(i, inTemplates, 'in')); + const outPorts = this.outputPorts.map((op, i) => + op.getGenericElkPort(i, outTemplates, 'out')); + const elk = buildElkGraph(this.subModule); + const cell: ElkModel.Cell = { + id: this.key, + layoutOptions: layoutAttrs, + labels: [], + ports: inPorts.concat(outPorts), + children: [], + edges: [], + }; + // Bad practice, solution? + _.forEach(cell.ports, (port) => { + delete port.x; + delete port.y; + }); + _.forEach(elk.children, (child) => { + let inc: boolean = true; + _.forEach(cell.ports, (port) => { + if (this.key + '.' + child.id === port.id) { + inc = false; + } + }); + if (inc) { + cell.children.push(child); + } + }); + return cell; + } const ports: ElkModel.Port[] = Skin.getPortsWithPrefix(template, '').map((tp) => { return { id: this.key + '.' + tp[1]['s:pid'], diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index ae0a861..4348cec 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -22,6 +22,7 @@ export class FlatModule { public static skin: any; public static layoutProps: {[x: string]: any}; public static modNames: string[]; + public static fromNetlist(netlist: Yosys.Netlist, skin: any): FlatModule { this.skin = skin; this.layoutProps = skin.getProperties(); @@ -50,7 +51,13 @@ export class FlatModule { this.parent = parent; this.moduleName = name; const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this)); - const cells = _.map(mod.cells, (c, key) => Cell.fromYosysCell(c, key, this)); + const cells = _.map(mod.cells, (c, key) => { + if (!_.includes(FlatModule.modNames, c.type)) { + return Cell.fromYosysCell(c, key, this); + } else { + return Cell.createSubModule(c, key, this, FlatModule.netlist.modules[c.type]); + } + }); this.nodes = cells.concat(ports); // this can be skipped if there are no 0's or 1's if (FlatModule.layoutProps.constants !== false) { diff --git a/lib/elkGraph.ts b/lib/elkGraph.ts index dd85693..785c574 100644 --- a/lib/elkGraph.ts +++ b/lib/elkGraph.ts @@ -16,13 +16,15 @@ export namespace ElkModel { export interface Cell { id: string; - width: number; - height: number; + width?: number; + height?: number; ports: Port[]; layoutOptions?: LayoutOptions; labels?: Label[]; x?: number; y?: number; + children?: Cell[]; + edges?: Array; } export interface Graph { From d686c2e931002308100d3d201f0a4a24917049ca Mon Sep 17 00:00:00 2001 From: djwubs Date: Mon, 16 Mar 2020 10:44:30 +0100 Subject: [PATCH 04/32] Fixed Elk input --- built/Cell.js | 48 +++++++++++++++++++++++++++++++++------------ built/drawModule.js | 1 + lib/Cell.ts | 23 +++++++++++++++++++++- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 4755a74..0811d2d 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -236,7 +236,7 @@ var Cell = /** @class */ (function () { if (type === 'generic' && this.subModule !== null) { var inTemplates_2 = Skin_1.default.getPortsWithPrefix(template, 'in'); var outTemplates_2 = Skin_1.default.getPortsWithPrefix(template, 'out'); - var inPorts = this.inputPorts.map(function (ip, i) { + var inPorts_1 = this.inputPorts.map(function (ip, i) { return ip.getGenericElkPort(i, inTemplates_2, 'in'); }); var outPorts = this.outputPorts.map(function (op, i) { @@ -245,9 +245,9 @@ var Cell = /** @class */ (function () { var elk = elkGraph_1.buildElkGraph(this.subModule); var cell_1 = { id: this.key, - layoutOptions: layoutAttrs, + layoutOptions: [], labels: [], - ports: inPorts.concat(outPorts), + ports: inPorts_1.concat(outPorts), children: [], edges: [], }; @@ -267,6 +267,28 @@ var Cell = /** @class */ (function () { cell_1.children.push(child); } }); + _.forEach(elk.edges, function (edge) { + var edgeAdd = edge; + _.forEach(cell_1.ports, function (port) { + if (_.includes(inPorts_1, port)) { + if (edgeAdd.sources[0] === port.id.slice(_this.key.length + 1) + '.Y') { + edgeAdd.sources[0] = port.id; + } + } + else { + if (edgeAdd.targets[0] === port.id.slice(_this.key.length + 1) + '.A') { + edgeAdd.targets[0] = port.id; + } + } + }); + cell_1.edges.push(edgeAdd); + }); + if (fixedPosX) { + cell_1.x = fixedPosX; + } + if (fixedPosY) { + cell_1.y = fixedPosY; + } return cell_1; } var ports = Skin_1.default.getPortsWithPrefix(template, '').map(function (tp) { @@ -332,24 +354,24 @@ var Cell = /** @class */ (function () { } else if (this.type === '$_join_') { setGenericSize(tempclone, Number(this.getGenericHeight())); - var inPorts_1 = Skin_1.default.getPortsWithPrefix(template, 'in'); - var gap_2 = Number(inPorts_1[1][1]['s:y']) - Number(inPorts_1[0][1]['s:y']); - var startY_2 = Number(inPorts_1[0][1]['s:y']); + var inPorts_2 = Skin_1.default.getPortsWithPrefix(template, 'in'); + var gap_2 = Number(inPorts_2[1][1]['s:y']) - Number(inPorts_2[0][1]['s:y']); + var startY_2 = Number(inPorts_2[0][1]['s:y']); tempclone.pop(); tempclone.pop(); this.inputPorts.forEach(function (port, i) { - var portClone = clone(inPorts_1[0]); + var portClone = clone(inPorts_2[0]); portClone[portClone.length - 1][2] = port.Key; - portClone[1].transform = 'translate(' + inPorts_1[1][1]['s:x'] + ',' + portClone[1].transform = 'translate(' + inPorts_2[1][1]['s:x'] + ',' + (startY_2 + i * gap_2) + ')'; tempclone.push(portClone); }); } else if (template[1]['s:type'] === 'generic') { setGenericSize(tempclone, Number(this.getGenericHeight())); - var inPorts_2 = Skin_1.default.getPortsWithPrefix(template, 'in'); - var ingap_1 = Number(inPorts_2[1][1]['s:y']) - Number(inPorts_2[0][1]['s:y']); - var instartY_1 = Number(inPorts_2[0][1]['s:y']); + var inPorts_3 = Skin_1.default.getPortsWithPrefix(template, 'in'); + var ingap_1 = Number(inPorts_3[1][1]['s:y']) - Number(inPorts_3[0][1]['s:y']); + var instartY_1 = Number(inPorts_3[0][1]['s:y']); var outPorts_2 = Skin_1.default.getPortsWithPrefix(template, 'out'); var outgap_1 = Number(outPorts_2[1][1]['s:y']) - Number(outPorts_2[0][1]['s:y']); var outstartY_1 = Number(outPorts_2[0][1]['s:y']); @@ -358,9 +380,9 @@ var Cell = /** @class */ (function () { tempclone.pop(); tempclone.pop(); this.inputPorts.forEach(function (port, i) { - var portClone = clone(inPorts_2[0]); + var portClone = clone(inPorts_3[0]); portClone[portClone.length - 1][2] = port.Key; - portClone[1].transform = 'translate(' + inPorts_2[1][1]['s:x'] + ',' + portClone[1].transform = 'translate(' + inPorts_3[1][1]['s:x'] + ',' + (instartY_1 + i * ingap_1) + ')'; portClone[1].id = 'port_' + port.parentNode.Key + '~' + port.Key; tempclone.push(portClone); diff --git a/built/drawModule.js b/built/drawModule.js index 6ea1a22..cf0d102 100644 --- a/built/drawModule.js +++ b/built/drawModule.js @@ -19,6 +19,7 @@ var WireDirection; WireDirection[WireDirection["Right"] = 3] = "Right"; })(WireDirection || (WireDirection = {})); function drawModule(g, module) { + console.dir(g, {depth: null}); var nodes = module.nodes.map(function (n) { var kchild = _.find(g.children, function (c) { return c.id === n.Key; }); return n.render(kchild); diff --git a/lib/Cell.ts b/lib/Cell.ts index 0c12184..56ccb18 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -255,7 +255,7 @@ export default class Cell { const elk = buildElkGraph(this.subModule); const cell: ElkModel.Cell = { id: this.key, - layoutOptions: layoutAttrs, + layoutOptions: [], labels: [], ports: inPorts.concat(outPorts), children: [], @@ -277,6 +277,27 @@ export default class Cell { cell.children.push(child); } }); + _.forEach(elk.edges, (edge: ElkModel.ExtendedEdge) => { + const edgeAdd = edge; + _.forEach(cell.ports, (port) => { + if (_.includes(inPorts, port)) { + if (edgeAdd.sources[0] === port.id.slice(this.key.length + 1) + '.Y') { + edgeAdd.sources[0] = port.id; + } + } else { + if (edgeAdd.targets[0] === port.id.slice(this.key.length + 1) + '.A') { + edgeAdd.targets[0] = port.id; + } + } + }); + cell.edges.push(edgeAdd); + }); + if (fixedPosX) { + cell.x = fixedPosX; + } + if (fixedPosY) { + cell.y = fixedPosY; + } return cell; } const ports: ElkModel.Port[] = Skin.getPortsWithPrefix(template, '').map((tp) => { From ef62ec9d772a497cc034372f997a5e2b5d6dbe5c Mon Sep 17 00:00:00 2001 From: djwubs Date: Tue, 17 Mar 2020 10:36:54 +0100 Subject: [PATCH 05/32] drawModule setup with Cell modifications --- built/Cell.js | 17 ++++++++++++- built/drawModule.js | 57 +++++++++++++++++++++++++++++++++++++++++++- lib/Cell.ts | 16 ++++++++++++- lib/drawModule.ts | 58 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 144 insertions(+), 4 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 0811d2d..88195f4 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -4,6 +4,7 @@ var FlatModule_1 = require("./FlatModule"); var YosysModel_1 = require("./YosysModel"); var Skin_1 = require("./Skin"); var Port_1 = require("./Port"); +var drawModule_1 = require("./drawModule"); var _ = require("lodash"); var elkGraph_1 = require("./elkGraph"); var clone = require("clone"); @@ -289,6 +290,7 @@ var Cell = /** @class */ (function () { if (fixedPosY) { cell_1.y = fixedPosY; } + this.addLabels(template, cell_1); return cell_1; } var ports = Skin_1.default.getPortsWithPrefix(template, '').map(function (tp) { @@ -367,7 +369,7 @@ var Cell = /** @class */ (function () { tempclone.push(portClone); }); } - else if (template[1]['s:type'] === 'generic') { + else if (template[1]['s:type'] === 'generic' && this.subModule === null) { setGenericSize(tempclone, Number(this.getGenericHeight())); var inPorts_3 = Skin_1.default.getPortsWithPrefix(template, 'in'); var ingap_1 = Number(inPorts_3[1][1]['s:y']) - Number(inPorts_3[0][1]['s:y']); @@ -398,6 +400,19 @@ var Cell = /** @class */ (function () { // first child of generic must be a text node. tempclone[2][2] = this.type; } + else if (template[1]['s:type'] === 'generic' && this.subModule !== null) { + var subModule = drawModule_1.drawSubModule(cell, this.subModule); + tempclone.pop(); + tempclone.pop(); + tempclone.pop(); + tempclone.pop(); + tempclone[3][1].width = subModule[1].width; + tempclone[3][1].height = subModule[1].height; + subModule.shift(); + subModule.shift(); + _.forEach(subModule, function (child) { return tempclone.push(child); }); + tempclone[2][2] = this.type; + } setClass(tempclone, '$cell_id', 'cell_' + this.key); return tempclone; }; diff --git a/built/drawModule.js b/built/drawModule.js index cf0d102..8c39df0 100644 --- a/built/drawModule.js +++ b/built/drawModule.js @@ -19,7 +19,6 @@ var WireDirection; WireDirection[WireDirection["Right"] = 3] = "Right"; })(WireDirection || (WireDirection = {})); function drawModule(g, module) { - console.dir(g, {depth: null}); var nodes = module.nodes.map(function (n) { var kchild = _.find(g.children, function (c) { return c.id === n.Key; }); return n.render(kchild); @@ -80,6 +79,62 @@ function drawModule(g, module) { return onml.s(ret); } exports.default = drawModule; +function drawSubModule(c, subModule) { + var nodes = []; + _.forEach(subModule.nodes, function (n) { + var kchild = _.find(c.children, function (child) { return child.id === n.Key; }); + if (kchild) { + nodes.push(n.render(kchild)); + } + }); + removeDummyEdges(c); + var lines = _.flatMap(c.edges, function (e) { + var netId = elkGraph_1.ElkModel.wireNameLookup[e.id]; + var netName = 'net_' + netId.slice(1, netId.length - 1); + return _.flatMap(e.sections, function (s) { + var startPoint = s.startPoint; + s.bendPoints = s.bendPoints || []; + var bends = s.bendPoints.map(function (b) { + var l = ['line', { + x1: startPoint.x, + x2: b.x, + y1: startPoint.y, + y2: b.y, + class: netName, + }]; + startPoint = b; + return l; + }); + if (e.junctionPoints) { + var circles = e.junctionPoints.map(function (j) { + return ['circle', { + cx: j.x, + cy: j.y, + r: 2, + style: 'fill:#000', + class: netName, + }]; + }); + bends = bends.concat(circles); + } + var line = [['line', { + x1: startPoint.x, + x2: s.endPoint.x, + y1: startPoint.y, + y2: s.endPoint.y, + class: netName, + }]]; + return bends.concat(line); + }); + }); + var svgAttrs = Skin_1.default.skin[1]; + svgAttrs.width = c.width.toString(); + svgAttrs.height = c.height.toString(); + var elements = __spreadArrays(nodes, lines); + var ret = __spreadArrays(['svg', svgAttrs], elements); + return ret; +} +exports.drawSubModule = drawSubModule; function which_dir(start, end) { if (end.x === start.x && end.y === start.y) { throw new Error('start and end are the same'); diff --git a/lib/Cell.ts b/lib/Cell.ts index 56ccb18..03b8d26 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -2,6 +2,7 @@ import { SigsByConstName, NameToPorts, addToDefaultDict, FlatModule } from './Fl import Yosys from './YosysModel'; import Skin from './Skin'; import {Port} from './Port'; +import { drawSubModule } from './drawModule'; import _ = require('lodash'); import { ElkModel, buildElkGraph } from './elkGraph'; import clone = require('clone'); @@ -298,6 +299,7 @@ export default class Cell { if (fixedPosY) { cell.y = fixedPosY; } + this.addLabels(template, cell); return cell; } const ports: ElkModel.Port[] = Skin.getPortsWithPrefix(template, '').map((tp) => { @@ -374,7 +376,7 @@ export default class Cell { + (startY + i * gap) + ')'; tempclone.push(portClone); }); - } else if (template[1]['s:type'] === 'generic') { + } else if (template[1]['s:type'] === 'generic' && this.subModule === null) { setGenericSize(tempclone, Number(this.getGenericHeight())); const inPorts = Skin.getPortsWithPrefix(template, 'in'); const ingap = Number(inPorts[1][1]['s:y']) - Number(inPorts[0][1]['s:y']); @@ -404,6 +406,18 @@ export default class Cell { }); // first child of generic must be a text node. tempclone[2][2] = this.type; + } else if (template[1]['s:type'] === 'generic' && this.subModule !== null) { + const subModule = drawSubModule(cell, this.subModule); + tempclone.pop(); + tempclone.pop(); + tempclone.pop(); + tempclone.pop(); + tempclone[3][1].width = subModule[1].width; + tempclone[3][1].height = subModule[1].height; + subModule.shift(); + subModule.shift(); + _.forEach(subModule, (child) => tempclone.push(child)); + tempclone[2][2] = this.type; } setClass(tempclone, '$cell_id', 'cell_' + this.key); return tempclone; diff --git a/lib/drawModule.ts b/lib/drawModule.ts index 0b9f4cc..0d7e60a 100644 --- a/lib/drawModule.ts +++ b/lib/drawModule.ts @@ -72,6 +72,62 @@ export default function drawModule(g: ElkModel.Graph, module: FlatModule) { return onml.s(ret); } +export function drawSubModule(c: ElkModel.Cell, subModule: FlatModule) { + const nodes: onml.Element[] = []; + _.forEach(subModule.nodes, (n: Cell) => { + const kchild: ElkModel.Cell = _.find(c.children, (child) => child.id === n.Key); + if (kchild) { + nodes.push(n.render(kchild)); + } + }); + removeDummyEdges(c); + const lines: onml.Element[] = _.flatMap(c.edges, (e: ElkModel.Edge) => { + const netId = ElkModel.wireNameLookup[e.id]; + const netName = 'net_' + netId.slice(1, netId.length - 1); + return _.flatMap(e.sections, (s: ElkModel.Section) => { + let startPoint = s.startPoint; + s.bendPoints = s.bendPoints || []; + let bends: any[] = s.bendPoints.map((b) => { + const l = ['line', { + x1: startPoint.x, + x2: b.x, + y1: startPoint.y, + y2: b.y, + class: netName, + }]; + startPoint = b; + return l; + }); + if (e.junctionPoints) { + const circles: any[] = e.junctionPoints.map((j: ElkModel.WirePoint) => + ['circle', { + cx: j.x, + cy: j.y, + r: 2, + style: 'fill:#000', + class: netName, + }]); + bends = bends.concat(circles); + } + const line = [['line', { + x1: startPoint.x, + x2: s.endPoint.x, + y1: startPoint.y, + y2: s.endPoint.y, + class: netName, + }]]; + return bends.concat(line); + }); + }); + const svgAttrs: onml.Attributes = Skin.skin[1]; + svgAttrs.width = c.width.toString(); + svgAttrs.height = c.height.toString(); + + const elements: onml.Element[] = [...nodes, ...lines]; + const ret: onml.Element = ['svg', svgAttrs, ...elements]; + return ret; +} + function which_dir(start: ElkModel.WirePoint, end: ElkModel.WirePoint): WireDirection { if (end.x === start.x && end.y === start.y) { throw new Error('start and end are the same'); @@ -111,7 +167,7 @@ function findBendNearDummy( }); } -export function removeDummyEdges(g: ElkModel.Graph) { +export function removeDummyEdges(g: ElkModel.Graph|ElkModel.Cell) { // go through each edge group for each dummy let dummyNum: number = 0; // loop until we can't find an edge group or we hit 10,000 From d4817289d3b3fd1fd4d2cf7e48162bb2ad1593f1 Mon Sep 17 00:00:00 2001 From: djwubs Date: Thu, 19 Mar 2020 14:35:36 +0100 Subject: [PATCH 06/32] Fixed port- and module names --- built/Cell.js | 21 +++++++++++++++++++++ lib/Cell.ts | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/built/Cell.js b/built/Cell.js index 88195f4..c01b660 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -412,6 +412,27 @@ var Cell = /** @class */ (function () { subModule.shift(); _.forEach(subModule, function (child) { return tempclone.push(child); }); tempclone[2][2] = this.type; + tempclone[2][1].x = tempclone[3][1].width / 2; + var inPorts_4 = Skin_1.default.getPortsWithPrefix(template, 'in'); + this.inputPorts.forEach(function (port, i) { + var portElk = _.find(cell.ports, function (p) { return p.id === cell.id + '.' + port.Key; }); + var portClone = clone(inPorts_4[0]); + portClone[portClone.length - 1][2] = port.Key; + portClone[1].transform = 'translate(' + portElk.x + ',' + + portElk.y + ')'; + portClone[1].id = 'port_' + port.parentNode.Key + '~' + port.Key; + tempclone.push(portClone); + }); + var outPorts_3 = Skin_1.default.getPortsWithPrefix(template, 'out'); + this.outputPorts.forEach(function (port, i) { + var portElk = _.find(cell.ports, function (p) { return p.id === cell.id + '.' + port.Key; }); + var portClone = clone(outPorts_3[0]); + portClone[portClone.length - 1][2] = port.Key; + portClone[1].transform = 'translate(' + portElk.x + ',' + + portElk.y + ')'; + portClone[1].id = 'port_' + port.parentNode.Key + '~' + port.Key; + tempclone.push(portClone); + }); } setClass(tempclone, '$cell_id', 'cell_' + this.key); return tempclone; diff --git a/lib/Cell.ts b/lib/Cell.ts index 03b8d26..c1fed0b 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -418,6 +418,27 @@ export default class Cell { subModule.shift(); _.forEach(subModule, (child) => tempclone.push(child)); tempclone[2][2] = this.type; + tempclone[2][1].x = tempclone[3][1].width / 2; + const inPorts = Skin.getPortsWithPrefix(template, 'in'); + this.inputPorts.forEach((port, i) => { + const portElk = _.find(cell.ports, (p) => p.id === cell.id + '.' + port.Key); + const portClone = clone(inPorts[0]); + portClone[portClone.length - 1][2] = port.Key; + portClone[1].transform = 'translate(' + portElk.x + ',' + + portElk.y + ')'; + portClone[1].id = 'port_' + port.parentNode.Key + '~' + port.Key; + tempclone.push(portClone); + }); + const outPorts = Skin.getPortsWithPrefix(template, 'out'); + this.outputPorts.forEach((port, i) => { + const portElk = _.find(cell.ports, (p) => p.id === cell.id + '.' + port.Key); + const portClone = clone(outPorts[0]); + portClone[portClone.length - 1][2] = port.Key; + portClone[1].transform = 'translate(' + portElk.x + ',' + + portElk.y + ')'; + portClone[1].id = 'port_' + port.parentNode.Key + '~' + port.Key; + tempclone.push(portClone); + }); } setClass(tempclone, '$cell_id', 'cell_' + this.key); return tempclone; From 6de28df2b42d54638496dc8e74fdb693820a968a Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 20 Mar 2020 10:54:44 +0100 Subject: [PATCH 07/32] Clean-up of Cell and Port --- built/Cell.js | 15 +++++---------- built/Port.js | 8 ++++++++ lib/Cell.ts | 15 +++++---------- lib/Port.ts | 10 ++++++++++ 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index c01b660..1a5c43a 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -252,11 +252,6 @@ var Cell = /** @class */ (function () { children: [], edges: [], }; - // Bad practice, solution? - _.forEach(cell_1.ports, function (port) { - delete port.x; - delete port.y; - }); _.forEach(elk.children, function (child) { var inc = true; _.forEach(cell_1.ports, function (port) { @@ -402,18 +397,19 @@ var Cell = /** @class */ (function () { } else if (template[1]['s:type'] === 'generic' && this.subModule !== null) { var subModule = drawModule_1.drawSubModule(cell, this.subModule); + tempclone[3][1].width = subModule[1].width; + tempclone[3][1].height = subModule[1].height; + tempclone[2][1].x = tempclone[3][1].width / 2; + tempclone[2][2] = this.type; tempclone.pop(); tempclone.pop(); tempclone.pop(); tempclone.pop(); - tempclone[3][1].width = subModule[1].width; - tempclone[3][1].height = subModule[1].height; subModule.shift(); subModule.shift(); _.forEach(subModule, function (child) { return tempclone.push(child); }); - tempclone[2][2] = this.type; - tempclone[2][1].x = tempclone[3][1].width / 2; var inPorts_4 = Skin_1.default.getPortsWithPrefix(template, 'in'); + var outPorts_3 = Skin_1.default.getPortsWithPrefix(template, 'out'); this.inputPorts.forEach(function (port, i) { var portElk = _.find(cell.ports, function (p) { return p.id === cell.id + '.' + port.Key; }); var portClone = clone(inPorts_4[0]); @@ -423,7 +419,6 @@ var Cell = /** @class */ (function () { portClone[1].id = 'port_' + port.parentNode.Key + '~' + port.Key; tempclone.push(portClone); }); - var outPorts_3 = Skin_1.default.getPortsWithPrefix(template, 'out'); this.outputPorts.forEach(function (port, i) { var portElk = _.find(cell.ports, function (p) { return p.id === cell.id + '.' + port.Key; }); var portClone = clone(outPorts_3[0]); diff --git a/built/Port.js b/built/Port.js index 6d9a6bb..57e9ccf 100644 --- a/built/Port.js +++ b/built/Port.js @@ -81,6 +81,10 @@ var Port = /** @class */ (function () { height: 11, }]; } + if (type === 'generic' && this.parentNode.subModule !== null) { + delete ret.x; + delete ret.y; + } return ret; } else { @@ -102,6 +106,10 @@ var Port = /** @class */ (function () { height: 11, }]; } + if (type === 'generic' && this.parentNode.subModule !== null) { + delete ret.x; + delete ret.y; + } return ret; } }; diff --git a/lib/Cell.ts b/lib/Cell.ts index c1fed0b..0786f2f 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -262,11 +262,6 @@ export default class Cell { children: [], edges: [], }; - // Bad practice, solution? - _.forEach(cell.ports, (port) => { - delete port.x; - delete port.y; - }); _.forEach(elk.children, (child) => { let inc: boolean = true; _.forEach(cell.ports, (port) => { @@ -408,18 +403,19 @@ export default class Cell { tempclone[2][2] = this.type; } else if (template[1]['s:type'] === 'generic' && this.subModule !== null) { const subModule = drawSubModule(cell, this.subModule); + tempclone[3][1].width = subModule[1].width; + tempclone[3][1].height = subModule[1].height; + tempclone[2][1].x = tempclone[3][1].width / 2; + tempclone[2][2] = this.type; tempclone.pop(); tempclone.pop(); tempclone.pop(); tempclone.pop(); - tempclone[3][1].width = subModule[1].width; - tempclone[3][1].height = subModule[1].height; subModule.shift(); subModule.shift(); _.forEach(subModule, (child) => tempclone.push(child)); - tempclone[2][2] = this.type; - tempclone[2][1].x = tempclone[3][1].width / 2; const inPorts = Skin.getPortsWithPrefix(template, 'in'); + const outPorts = Skin.getPortsWithPrefix(template, 'out'); this.inputPorts.forEach((port, i) => { const portElk = _.find(cell.ports, (p) => p.id === cell.id + '.' + port.Key); const portClone = clone(inPorts[0]); @@ -429,7 +425,6 @@ export default class Cell { portClone[1].id = 'port_' + port.parentNode.Key + '~' + port.Key; tempclone.push(portClone); }); - const outPorts = Skin.getPortsWithPrefix(template, 'out'); this.outputPorts.forEach((port, i) => { const portElk = _.find(cell.ports, (p) => p.id === cell.id + '.' + port.Key); const portClone = clone(outPorts[0]); diff --git a/lib/Port.ts b/lib/Port.ts index 0386799..8d781bb 100644 --- a/lib/Port.ts +++ b/lib/Port.ts @@ -108,6 +108,11 @@ export class Port { height: 11, }]; } + + if (type === 'generic' && this.parentNode.subModule !== null) { + delete ret.x; + delete ret.y; + } return ret; } else { const gap: number = Number(templatePorts[1][1]['s:y']) - Number(templatePorts[0][1]['s:y']); @@ -128,6 +133,11 @@ export class Port { height: 11, }]; } + + if (type === 'generic' && this.parentNode.subModule !== null) { + delete ret.x; + delete ret.y; + } return ret; } } From 2fde0b8a622e12a9340804bf8b49e4e6ef34e885 Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 20 Mar 2020 14:39:33 +0100 Subject: [PATCH 08/32] Final cleanup phase 1 --- built/FlatModule.js | 6 +++--- built/index.js | 4 ++-- lib/FlatModule.ts | 5 ++--- lib/index.ts | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/built/FlatModule.js b/built/FlatModule.js index 2b5c62b..5d4f61f 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +var Skin_1 = require("./Skin"); var Cell_1 = require("./Cell"); var _ = require("lodash"); var FlatModule = /** @class */ (function () { @@ -28,9 +29,8 @@ var FlatModule = /** @class */ (function () { } this.createWires(); } - FlatModule.fromNetlist = function (netlist, skin) { - this.skin = skin; - this.layoutProps = skin.getProperties(); + FlatModule.fromNetlist = function (netlist) { + this.layoutProps = Skin_1.default.getProperties(); this.modNames = Object.keys(netlist.modules); this.netlist = netlist; var topName = null; diff --git a/built/index.js b/built/index.js index ae7cc33..ff02e11 100644 --- a/built/index.js +++ b/built/index.js @@ -10,7 +10,7 @@ var elk = new ELK(); function dumpLayout(skinData, yosysNetlist, prelayout, done) { var skin = onml.p(skinData); Skin_1.default.skin = skin; - var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist, Skin_1.default); + var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist); var kgraph = elkGraph_1.buildElkGraph(flatModule); if (prelayout) { done(null, JSON.stringify(kgraph, null, 2)); @@ -27,7 +27,7 @@ exports.dumpLayout = dumpLayout; function render(skinData, yosysNetlist, done, elkData) { var skin = onml.p(skinData); Skin_1.default.skin = skin; - var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist, Skin_1.default); + var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist); var kgraph = elkGraph_1.buildElkGraph(flatModule); var promise; // if we already have a layout then use it diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 4348cec..a18abe2 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -23,9 +23,8 @@ export class FlatModule { public static layoutProps: {[x: string]: any}; public static modNames: string[]; - public static fromNetlist(netlist: Yosys.Netlist, skin: any): FlatModule { - this.skin = skin; - this.layoutProps = skin.getProperties(); + public static fromNetlist(netlist: Yosys.Netlist): FlatModule { + this.layoutProps = Skin.getProperties(); this.modNames = Object.keys(netlist.modules); this.netlist = netlist; let topName = null; diff --git a/lib/index.ts b/lib/index.ts index 7016b2e..77acab3 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -16,7 +16,7 @@ type ICallback = (error: Error, result?: string) => void; export function dumpLayout(skinData: string, yosysNetlist: Yosys.Netlist, prelayout: boolean, done: ICallback) { const skin = onml.p(skinData); Skin.skin = skin; - const flatModule = FlatModule.fromNetlist(yosysNetlist, Skin); + const flatModule = FlatModule.fromNetlist(yosysNetlist); const kgraph: ElkModel.Graph = buildElkGraph(flatModule); if (prelayout) { done(null, JSON.stringify(kgraph, null, 2)); @@ -33,7 +33,7 @@ export function dumpLayout(skinData: string, yosysNetlist: Yosys.Netlist, prelay export function render(skinData: string, yosysNetlist: Yosys.Netlist, done?: ICallback, elkData?: ElkModel.Graph) { const skin = onml.p(skinData); Skin.skin = skin; - const flatModule = FlatModule.fromNetlist(yosysNetlist, Skin); + const flatModule = FlatModule.fromNetlist(yosysNetlist); const kgraph: ElkModel.Graph = buildElkGraph(flatModule); let promise; From 923f20fa104c886d9f9090b1d2f8e71808ac5a1d Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 20 Mar 2020 15:01:16 +0100 Subject: [PATCH 09/32] Updated yosys schema for yosys update --- lib/yosys.schema.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/yosys.schema.json5 b/lib/yosys.schema.json5 index 234eaed..35779a0 100644 --- a/lib/yosys.schema.json5 +++ b/lib/yosys.schema.json5 @@ -94,7 +94,7 @@ "attributes": { "type": "object", "properties": { - "top": {"enum": [0, 1]} + "top": {"enum": [0, 1, "00000000000000000000000000000000", "00000000000000000000000000000001"]} } } }, From 0a15d58c54f78f613a9a112336c186280e4c7a68 Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 20 Mar 2020 19:09:25 +0100 Subject: [PATCH 10/32] Fix tests --- test/FlatModule.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/FlatModule.test.ts b/test/FlatModule.test.ts index a18f9ef..e129346 100644 --- a/test/FlatModule.test.ts +++ b/test/FlatModule.test.ts @@ -19,7 +19,7 @@ function createFlatModule(testFile: string): FlatModule { const netlist: Yosys.Netlist = json5.parse(testStr); const skin = onml.parse(fs.readFileSync(defaultSkin).toString()); Skin.skin = skin; - return FlatModule.fromNetlist(netlist, Skin); + return FlatModule.fromNetlist(netlist); } /** From f01fc417c8795a346f25963b68a0106f231a065e Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 20 Mar 2020 19:36:06 +0100 Subject: [PATCH 11/32] Change parent to string --- built/FlatModule.js | 10 +++++----- lib/Cell.ts | 16 ++++++++-------- lib/FlatModule.ts | 14 +++++++------- lib/Port.ts | 4 ++-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/built/FlatModule.js b/built/FlatModule.js index 5d4f61f..39603a8 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -9,13 +9,13 @@ var FlatModule = /** @class */ (function () { if (parent === void 0) { parent = null; } this.parent = parent; this.moduleName = name; - var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this); }); + var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this.moduleName); }); var cells = _.map(mod.cells, function (c, key) { if (!_.includes(FlatModule.modNames, c.type)) { - return Cell_1.default.fromYosysCell(c, key, _this); + return Cell_1.default.fromYosysCell(c, key, _this.moduleName); } else { - return Cell_1.default.createSubModule(c, key, _this, FlatModule.netlist.modules[c.type]); + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type]); } }); this.nodes = cells.concat(ports); @@ -70,9 +70,9 @@ var FlatModule = /** @class */ (function () { gather(allOutputs, allInputsCopy, input, 0, input.length, splits, joins); }); this.nodes = this.nodes.concat(_.map(joins, function (joinOutput, joinInputs) { - return Cell_1.default.fromJoinInfo(joinInputs, joinOutput, _this); + return Cell_1.default.fromJoinInfo(joinInputs, joinOutput, _this.moduleName); })).concat(_.map(splits, function (splitOutputs, splitInput) { - return Cell_1.default.fromSplitInfo(splitInput, splitOutputs, _this); + return Cell_1.default.fromSplitInfo(splitInput, splitOutputs, _this.moduleName); })); }; // search through all the ports to find all of the wires diff --git a/lib/Cell.ts b/lib/Cell.ts index 0786f2f..d4986b4 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -14,7 +14,7 @@ export default class Cell { * @param yPort the Yosys Port with our port data * @param name the name of the port */ - public static fromPort(yPort: Yosys.ExtPort, name: string, parent: FlatModule): Cell { + public static fromPort(yPort: Yosys.ExtPort, name: string, parent: string): Cell { const isInput: boolean = yPort.direction === Yosys.Direction.Input; if (isInput) { return new Cell(name, '$_inputExt_', [], [new Port('Y', yPort.bits)], {}, parent); @@ -22,7 +22,7 @@ export default class Cell { return new Cell(name, '$_outputExt_', [new Port('A', yPort.bits)], [], {}, parent); } - public static fromYosysCell(yCell: Yosys.Cell, name: string, parent: FlatModule) { + public static fromYosysCell(yCell: Yosys.Cell, name: string, parent: string) { const template = Skin.findSkinType(yCell.type); const templateInputPids = Skin.getInputPids(template); const templateOutputPids = Skin.getOutputPids(template); @@ -40,7 +40,7 @@ export default class Cell { return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent); } - public static fromConstantInfo(name: string, constants: number[], parent: FlatModule): Cell { + public static fromConstantInfo(name: string, constants: number[], parent: string): Cell { return new Cell(name, '$_constant_', [], [new Port('Y', constants)], {}, parent); } @@ -49,7 +49,7 @@ export default class Cell { * @param target string name of net (starts and ends with and delimited by commas) * @param sources list of index strings (one number, or two numbers separated by a colon) */ - public static fromJoinInfo(target: string, sources: string[], parent: FlatModule): Cell { + public static fromJoinInfo(target: string, sources: string[], parent: string): Cell { const signalStrs: string[] = target.slice(1, -1).split(','); const signals: number[] = signalStrs.map((ss) => Number(ss)); const joinOutPorts: Port[] = [new Port('Y', signals)]; @@ -64,7 +64,7 @@ export default class Cell { * @param source string name of net (starts and ends with and delimited by commas) * @param targets list of index strings (one number, or two numbers separated by a colon) */ - public static fromSplitInfo(source: string, targets: string[], parent: FlatModule): Cell { + public static fromSplitInfo(source: string, targets: string[], parent: string): Cell { // turn string into array of signal names const sigStrs: string[] = source.slice(1, -1).split(','); // convert the signals into actual numbers @@ -78,7 +78,7 @@ export default class Cell { return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}, parent); } - public static createSubModule(yCell: Yosys.Cell, name: string, parent: FlatModule, subModule: Yosys.Module): Cell { + public static createSubModule(yCell: Yosys.Cell, name: string, parent: string, subModule: Yosys.Module): Cell { const template = Skin.findSkinType(yCell.type); const templateInputPids = Skin.getInputPids(template); const templateOutputPids = Skin.getOutputPids(template); @@ -97,7 +97,7 @@ export default class Cell { return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod); } - public parent: FlatModule; + public parent: string; public subModule: FlatModule; protected key: string; protected type: string; @@ -110,7 +110,7 @@ export default class Cell { inputPorts: Port[], outputPorts: Port[], attributes: Yosys.CellAttributes, - parent: FlatModule, + parent: string, subModule: FlatModule = null) { this.key = key; this.type = type; diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index a18abe2..7730906 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -41,20 +41,20 @@ export class FlatModule { return new FlatModule(top, topName); } - public parent: FlatModule; + public parent: string; public moduleName: string; public nodes: Cell[]; public wires: Wire[]; - constructor(mod: Yosys.Module, name: string, parent: FlatModule = null) { + constructor(mod: Yosys.Module, name: string, parent: string = null) { this.parent = parent; this.moduleName = name; - const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this)); + const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this.moduleName)); const cells = _.map(mod.cells, (c, key) => { if (!_.includes(FlatModule.modNames, c.type)) { - return Cell.fromYosysCell(c, key, this); + return Cell.fromYosysCell(c, key, this.moduleName); } else { - return Cell.createSubModule(c, key, this, FlatModule.netlist.modules[c.type]); + return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type]); } }); this.nodes = cells.concat(ports); @@ -103,9 +103,9 @@ export class FlatModule { }); this.nodes = this.nodes.concat(_.map(joins, (joinOutput, joinInputs) => { - return Cell.fromJoinInfo(joinInputs, joinOutput, this); + return Cell.fromJoinInfo(joinInputs, joinOutput, this.moduleName); })).concat(_.map(splits, (splitOutputs, splitInput) => { - return Cell.fromSplitInfo(splitInput, splitOutputs, this); + return Cell.fromSplitInfo(splitInput, splitOutputs, this.moduleName); })); } diff --git a/lib/Port.ts b/lib/Port.ts index 8d781bb..702ca35 100644 --- a/lib/Port.ts +++ b/lib/Port.ts @@ -33,7 +33,7 @@ export class Port { public findConstants(sigsByConstantName: SigsByConstName, maxNum: number, constantCollector: Cell[], - parent: FlatModule): number { + parent: string): number { let constNameCollector = ''; let constNumCollector: number[] = []; const portSigs: Yosys.Signals = this.value; @@ -147,7 +147,7 @@ export class Port { currIndex: number, signalsByConstantName: SigsByConstName, constantCollector: Cell[], - parent: FlatModule) { + parent: string) { // we've been appending to nameCollector, so reverse to get const name const constName = nameCollector.split('').reverse().join(''); // if the constant has already been used From e39741c0ea9ea1449384885102ca86d8ef5a1e53 Mon Sep 17 00:00:00 2001 From: djwubs Date: Sat, 21 Mar 2020 11:28:20 +0100 Subject: [PATCH 12/32] Support for multiple of the same module --- built/Cell.js | 14 +++++++------- built/Port.js | 2 +- built/drawModule.js | 4 ++-- built/elkGraph.js | 14 +++++++------- lib/Cell.ts | 14 +++++++------- lib/Port.ts | 2 +- lib/drawModule.ts | 4 ++-- lib/elkGraph.ts | 14 +++++++------- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 1a5c43a..396c154 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -218,7 +218,7 @@ var Cell = /** @class */ (function () { return op.getGenericElkPort(i, outTemplates_1, 'out'); }); var cell = { - id: this.key, + id: this.parent + '.' + this.key, width: Number(template[1]['s:width']), height: Number(this.getGenericHeight()), ports: inPorts.concat(outPorts), @@ -245,7 +245,7 @@ var Cell = /** @class */ (function () { }); var elk = elkGraph_1.buildElkGraph(this.subModule); var cell_1 = { - id: this.key, + id: this.parent + '.' + this.key, layoutOptions: [], labels: [], ports: inPorts_1.concat(outPorts), @@ -255,7 +255,7 @@ var Cell = /** @class */ (function () { _.forEach(elk.children, function (child) { var inc = true; _.forEach(cell_1.ports, function (port) { - if (_this.key + '.' + child.id === port.id) { + if (_this.parent + '.' + child.id === port.id) { inc = false; } }); @@ -267,12 +267,12 @@ var Cell = /** @class */ (function () { var edgeAdd = edge; _.forEach(cell_1.ports, function (port) { if (_.includes(inPorts_1, port)) { - if (edgeAdd.sources[0] === port.id.slice(_this.key.length + 1) + '.Y') { + if (edgeAdd.sources[0] === port.id.slice(_this.parent.length + 1) + '.Y') { edgeAdd.sources[0] = port.id; } } else { - if (edgeAdd.targets[0] === port.id.slice(_this.key.length + 1) + '.A') { + if (edgeAdd.targets[0] === port.id.slice(_this.parent.length + 1) + '.A') { edgeAdd.targets[0] = port.id; } } @@ -290,7 +290,7 @@ var Cell = /** @class */ (function () { } var ports = Skin_1.default.getPortsWithPrefix(template, '').map(function (tp) { return { - id: _this.key + '.' + tp[1]['s:pid'], + id: _this.parent + '.' + _this.key + '.' + tp[1]['s:pid'], width: 0, height: 0, x: Number(tp[1]['s:x']), @@ -299,7 +299,7 @@ var Cell = /** @class */ (function () { }); var nodeWidth = Number(template[1]['s:width']); var ret = { - id: this.key, + id: this.parent + '.' + this.key, width: nodeWidth, height: Number(template[1]['s:height']), ports: ports, diff --git a/built/Port.js b/built/Port.js index 57e9ccf..28c9b85 100644 --- a/built/Port.js +++ b/built/Port.js @@ -51,7 +51,7 @@ var Port = /** @class */ (function () { return maxNum; }; Port.prototype.getGenericElkPort = function (index, templatePorts, dir) { - var nkey = this.parentNode.Key; + var nkey = this.parentNode.parent + '.' + this.parentNode.Key; var type = this.parentNode.getTemplate()[1]['s:type']; if (index === 0) { var ret = { diff --git a/built/drawModule.js b/built/drawModule.js index 8c39df0..cabfafe 100644 --- a/built/drawModule.js +++ b/built/drawModule.js @@ -20,7 +20,7 @@ var WireDirection; })(WireDirection || (WireDirection = {})); function drawModule(g, module) { var nodes = module.nodes.map(function (n) { - var kchild = _.find(g.children, function (c) { return c.id === n.Key; }); + var kchild = _.find(g.children, function (c) { return c.id === n.parent + '.' + n.Key; }); return n.render(kchild); }); removeDummyEdges(g); @@ -82,7 +82,7 @@ exports.default = drawModule; function drawSubModule(c, subModule) { var nodes = []; _.forEach(subModule.nodes, function (n) { - var kchild = _.find(c.children, function (child) { return child.id === n.Key; }); + var kchild = _.find(c.children, function (child) { return child.id === n.parent + '.' + n.Key; }); if (kchild) { nodes.push(n.render(kchild)); } diff --git a/built/elkGraph.js b/built/elkGraph.js index 10fcce9..90688fa 100644 --- a/built/elkGraph.js +++ b/built/elkGraph.js @@ -17,14 +17,14 @@ function buildElkGraph(module) { // at least one driver and at least one rider and no laterals if (w.drivers.length > 0 && w.riders.length > 0 && w.laterals.length === 0) { var ret = []; - route(w.drivers, w.riders, ret); + route(w.drivers, w.riders, ret, module.moduleName); return ret; // at least one driver or rider and at least one lateral } else if (w.drivers.concat(w.riders).length > 0 && w.laterals.length > 0) { var ret = []; - route(w.drivers, w.laterals, ret); - route(w.laterals, w.riders, ret); + route(w.drivers, w.laterals, ret, module.moduleName); + route(w.laterals, w.riders, ret, module.moduleName); return ret; // at least two drivers and no riders } @@ -114,14 +114,14 @@ function addDummy(children) { children.push(child); return dummyId; } -function route(sourcePorts, targetPorts, edges) { +function route(sourcePorts, targetPorts, edges, moduleName) { var newEdges = (_.flatMap(sourcePorts, function (sourcePort) { var sourceParentKey = sourcePort.parentNode.key; - var sourceKey = sourceParentKey + '.' + sourcePort.key; + var sourceKey = moduleName + '.' + sourceParentKey + '.' + sourcePort.key; return targetPorts.map(function (targetPort) { var targetParentKey = targetPort.parentNode.key; - var targetKey = targetParentKey + '.' + targetPort.key; - var id = 'e' + ElkModel.edgeIndex; + var targetKey = moduleName + '.' + targetParentKey + '.' + targetPort.key; + var id = moduleName + '.e' + ElkModel.edgeIndex; var edge = { id: id, sources: [sourceKey], diff --git a/lib/Cell.ts b/lib/Cell.ts index d4986b4..ee6f09c 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -230,7 +230,7 @@ export default class Cell { const outPorts = this.outputPorts.map((op, i) => op.getGenericElkPort(i, outTemplates, 'out')); const cell: ElkModel.Cell = { - id: this.key, + id: this.parent + '.' + this.key, width: Number(template[1]['s:width']), height: Number(this.getGenericHeight()), ports: inPorts.concat(outPorts), @@ -255,7 +255,7 @@ export default class Cell { op.getGenericElkPort(i, outTemplates, 'out')); const elk = buildElkGraph(this.subModule); const cell: ElkModel.Cell = { - id: this.key, + id: this.parent + '.' + this.key, layoutOptions: [], labels: [], ports: inPorts.concat(outPorts), @@ -265,7 +265,7 @@ export default class Cell { _.forEach(elk.children, (child) => { let inc: boolean = true; _.forEach(cell.ports, (port) => { - if (this.key + '.' + child.id === port.id) { + if (this.parent + '.' + child.id === port.id) { inc = false; } }); @@ -277,11 +277,11 @@ export default class Cell { const edgeAdd = edge; _.forEach(cell.ports, (port) => { if (_.includes(inPorts, port)) { - if (edgeAdd.sources[0] === port.id.slice(this.key.length + 1) + '.Y') { + if (edgeAdd.sources[0] === port.id.slice(this.parent.length + 1) + '.Y') { edgeAdd.sources[0] = port.id; } } else { - if (edgeAdd.targets[0] === port.id.slice(this.key.length + 1) + '.A') { + if (edgeAdd.targets[0] === port.id.slice(this.parent.length + 1) + '.A') { edgeAdd.targets[0] = port.id; } } @@ -299,7 +299,7 @@ export default class Cell { } const ports: ElkModel.Port[] = Skin.getPortsWithPrefix(template, '').map((tp) => { return { - id: this.key + '.' + tp[1]['s:pid'], + id: this.parent + '.' + this.key + '.' + tp[1]['s:pid'], width: 0, height: 0, x: Number(tp[1]['s:x']), @@ -308,7 +308,7 @@ export default class Cell { }); const nodeWidth: number = Number(template[1]['s:width']); const ret: ElkModel.Cell = { - id: this.key, + id: this.parent + '.' + this.key, width: nodeWidth, height: Number(template[1]['s:height']), ports, diff --git a/lib/Port.ts b/lib/Port.ts index 702ca35..d8d1f2c 100644 --- a/lib/Port.ts +++ b/lib/Port.ts @@ -76,7 +76,7 @@ export class Port { templatePorts: any[], dir: string, ): ElkModel.Port { - const nkey = this.parentNode.Key; + const nkey = this.parentNode.parent + '.' + this.parentNode.Key; const type = this.parentNode.getTemplate()[1]['s:type']; if (index === 0) { const ret: ElkModel.Port = { diff --git a/lib/drawModule.ts b/lib/drawModule.ts index 0d7e60a..50d30cc 100644 --- a/lib/drawModule.ts +++ b/lib/drawModule.ts @@ -13,7 +13,7 @@ enum WireDirection { export default function drawModule(g: ElkModel.Graph, module: FlatModule) { const nodes: onml.Element[] = module.nodes.map((n: Cell) => { - const kchild: ElkModel.Cell = _.find(g.children, (c) => c.id === n.Key); + const kchild: ElkModel.Cell = _.find(g.children, (c) => c.id === n.parent + '.' + n.Key); return n.render(kchild); }); removeDummyEdges(g); @@ -75,7 +75,7 @@ export default function drawModule(g: ElkModel.Graph, module: FlatModule) { export function drawSubModule(c: ElkModel.Cell, subModule: FlatModule) { const nodes: onml.Element[] = []; _.forEach(subModule.nodes, (n: Cell) => { - const kchild: ElkModel.Cell = _.find(c.children, (child) => child.id === n.Key); + const kchild: ElkModel.Cell = _.find(c.children, (child) => child.id === n.parent + '.' + n.Key); if (kchild) { nodes.push(n.render(kchild)); } diff --git a/lib/elkGraph.ts b/lib/elkGraph.ts index 785c574..63fa5f0 100644 --- a/lib/elkGraph.ts +++ b/lib/elkGraph.ts @@ -93,13 +93,13 @@ export function buildElkGraph(module: FlatModule): ElkModel.Graph { // at least one driver and at least one rider and no laterals if (w.drivers.length > 0 && w.riders.length > 0 && w.laterals.length === 0) { const ret: ElkModel.Edge[] = []; - route(w.drivers, w.riders, ret); + route(w.drivers, w.riders, ret, module.moduleName); return ret; // at least one driver or rider and at least one lateral } else if (w.drivers.concat(w.riders).length > 0 && w.laterals.length > 0) { const ret: ElkModel.Edge[] = []; - route(w.drivers, w.laterals, ret); - route(w.laterals, w.riders, ret); + route(w.drivers, w.laterals, ret, module.moduleName); + route(w.laterals, w.riders, ret, module.moduleName); return ret; // at least two drivers and no riders } else if (w.riders.length === 0 && w.drivers.length > 1) { @@ -188,14 +188,14 @@ function addDummy(children: ElkModel.Cell[]) { return dummyId; } -function route(sourcePorts, targetPorts, edges: ElkModel.Edge[]) { +function route(sourcePorts, targetPorts, edges: ElkModel.Edge[], moduleName: string) { const newEdges: ElkModel.Edge[] = (_.flatMap(sourcePorts, (sourcePort) => { const sourceParentKey: string = sourcePort.parentNode.key; - const sourceKey: string = sourceParentKey + '.' + sourcePort.key; + const sourceKey: string = moduleName + '.' + sourceParentKey + '.' + sourcePort.key; return targetPorts.map((targetPort) => { const targetParentKey: string = targetPort.parentNode.key; - const targetKey: string = targetParentKey + '.' + targetPort.key; - const id: string = 'e' + ElkModel.edgeIndex; + const targetKey: string = moduleName + '.' + targetParentKey + '.' + targetPort.key; + const id: string = moduleName + '.e' + ElkModel.edgeIndex; const edge: ElkModel.ExtendedEdge = { id, sources: [sourceKey], From b4018314afc197006398f413430cfb7b357b107c Mon Sep 17 00:00:00 2001 From: djwubs Date: Mon, 23 Mar 2020 11:20:35 +0100 Subject: [PATCH 13/32] Fixed creation of dummies --- built/elkGraph.js | 32 ++++++++++++++++---------------- lib/elkGraph.ts | 32 ++++++++++++++++---------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/built/elkGraph.js b/built/elkGraph.js index 90688fa..7287c5e 100644 --- a/built/elkGraph.js +++ b/built/elkGraph.js @@ -30,16 +30,16 @@ function buildElkGraph(module) { } else if (w.riders.length === 0 && w.drivers.length > 1) { // create a dummy node and add it to children - var dummyId_1 = addDummy(children); + var dummyId_1 = addDummy(children, module.moduleName); ElkModel.dummyNum += 1; var dummyEdges = w.drivers.map(function (driver) { var sourceParentKey = driver.parentNode.Key; - var id = 'e' + String(ElkModel.edgeIndex); + var id = module.moduleName + '.e' + String(ElkModel.edgeIndex); ElkModel.edgeIndex += 1; var d = { id: id, - source: sourceParentKey, - sourcePort: sourceParentKey + '.' + driver.key, + source: module.moduleName + '.' + sourceParentKey, + sourcePort: module.moduleName + '.' + sourceParentKey + '.' + driver.key, target: dummyId_1, targetPort: dummyId_1 + '.p', }; @@ -51,18 +51,18 @@ function buildElkGraph(module) { } else if (w.riders.length > 1 && w.drivers.length === 0) { // create a dummy node and add it to children - var dummyId_2 = addDummy(children); + var dummyId_2 = addDummy(children, module.moduleName); ElkModel.dummyNum += 1; var dummyEdges = w.riders.map(function (rider) { var sourceParentKey = rider.parentNode.Key; - var id = 'e' + String(ElkModel.edgeIndex); + var id = module.moduleName + '.e' + String(ElkModel.edgeIndex); ElkModel.edgeIndex += 1; var edge = { id: id, source: dummyId_2, sourcePort: dummyId_2 + '.p', - target: sourceParentKey, - targetPort: sourceParentKey + '.' + rider.key, + target: module.moduleName + '.' + sourceParentKey, + targetPort: module.moduleName + '.' + sourceParentKey + '.' + rider.key, }; ElkModel.wireNameLookup[id] = rider.wire.netName; return edge; @@ -74,14 +74,14 @@ function buildElkGraph(module) { var sourceParentKey_1 = source_1.parentNode.Key; var lateralEdges = w.laterals.slice(1).map(function (lateral) { var lateralParentKey = lateral.parentNode.Key; - var id = 'e' + String(ElkModel.edgeIndex); + var id = module.moduleName + '.e' + String(ElkModel.edgeIndex); ElkModel.edgeIndex += 1; var edge = { id: id, - source: sourceParentKey_1, - sourcePort: sourceParentKey_1 + '.' + source_1.key, - target: lateralParentKey, - targetPort: lateralParentKey + '.' + lateral.key, + source: module.moduleName + '.' + sourceParentKey_1, + sourcePort: module.moduleName + '.' + sourceParentKey_1 + '.' + source_1.key, + target: module.moduleName + '.' + lateralParentKey, + targetPort: module.moduleName + '.' + lateralParentKey + '.' + lateral.key, }; ElkModel.wireNameLookup[id] = lateral.wire.netName; return edge; @@ -98,14 +98,14 @@ function buildElkGraph(module) { }; } exports.buildElkGraph = buildElkGraph; -function addDummy(children) { - var dummyId = '$d_' + String(ElkModel.dummyNum); +function addDummy(children, moduleName) { + var dummyId = moduleName + '.$d_' + String(ElkModel.dummyNum); var child = { id: dummyId, width: 0, height: 0, ports: [{ - id: dummyId + '.p', + id: moduleName + '.' + dummyId + '.p', width: 0, height: 0, }], diff --git a/lib/elkGraph.ts b/lib/elkGraph.ts index 63fa5f0..0938faf 100644 --- a/lib/elkGraph.ts +++ b/lib/elkGraph.ts @@ -104,16 +104,16 @@ export function buildElkGraph(module: FlatModule): ElkModel.Graph { // at least two drivers and no riders } else if (w.riders.length === 0 && w.drivers.length > 1) { // create a dummy node and add it to children - const dummyId: string = addDummy(children); + const dummyId: string = addDummy(children, module.moduleName); ElkModel.dummyNum += 1; const dummyEdges: ElkModel.Edge[] = w.drivers.map((driver) => { const sourceParentKey: string = driver.parentNode.Key; - const id: string = 'e' + String(ElkModel.edgeIndex); + const id: string = module.moduleName + '.e' + String(ElkModel.edgeIndex); ElkModel.edgeIndex += 1; const d: ElkModel.Edge = { id, - source: sourceParentKey, - sourcePort: sourceParentKey + '.' + driver.key, + source: module.moduleName + '.' + sourceParentKey, + sourcePort: module.moduleName + '.' + sourceParentKey + '.' + driver.key, target: dummyId, targetPort: dummyId + '.p', }; @@ -125,18 +125,18 @@ export function buildElkGraph(module: FlatModule): ElkModel.Graph { // at least one rider and no drivers } else if (w.riders.length > 1 && w.drivers.length === 0) { // create a dummy node and add it to children - const dummyId: string = addDummy(children); + const dummyId: string = addDummy(children, module.moduleName); ElkModel.dummyNum += 1; const dummyEdges: ElkModel.Edge[] = w.riders.map((rider) => { const sourceParentKey: string = rider.parentNode.Key; - const id: string = 'e' + String(ElkModel.edgeIndex); + const id: string = module.moduleName + '.e' + String(ElkModel.edgeIndex); ElkModel.edgeIndex += 1; const edge: ElkModel.Edge = { id, source: dummyId, sourcePort: dummyId + '.p', - target: sourceParentKey, - targetPort: sourceParentKey + '.' + rider.key, + target: module.moduleName + '.' + sourceParentKey, + targetPort: module.moduleName + '.' + sourceParentKey + '.' + rider.key, }; ElkModel.wireNameLookup[id] = rider.wire.netName; return edge; @@ -147,14 +147,14 @@ export function buildElkGraph(module: FlatModule): ElkModel.Graph { const sourceParentKey: string = source.parentNode.Key; const lateralEdges: ElkModel.Edge[] = w.laterals.slice(1).map((lateral) => { const lateralParentKey: string = lateral.parentNode.Key; - const id: string = 'e' + String(ElkModel.edgeIndex); + const id: string = module.moduleName + '.e' + String(ElkModel.edgeIndex); ElkModel.edgeIndex += 1; const edge: ElkModel.Edge = { id, - source: sourceParentKey, - sourcePort: sourceParentKey + '.' + source.key, - target: lateralParentKey, - targetPort: lateralParentKey + '.' + lateral.key, + source: module.moduleName + '.' + sourceParentKey, + sourcePort: module.moduleName + '.' + sourceParentKey + '.' + source.key, + target: module.moduleName + '.' + lateralParentKey, + targetPort: module.moduleName + '.' + lateralParentKey + '.' + lateral.key, }; ElkModel.wireNameLookup[id] = lateral.wire.netName; return edge; @@ -171,14 +171,14 @@ export function buildElkGraph(module: FlatModule): ElkModel.Graph { }; } -function addDummy(children: ElkModel.Cell[]) { - const dummyId: string = '$d_' + String(ElkModel.dummyNum); +function addDummy(children: ElkModel.Cell[], moduleName: string) { + const dummyId: string = moduleName + '.$d_' + String(ElkModel.dummyNum); const child: ElkModel.Cell = { id: dummyId, width: 0, height: 0, ports: [{ - id: dummyId + '.p', + id: moduleName + '.' + dummyId + '.p', width: 0, height: 0, }], From 137ae59ad60bd7e001912898247a81ed66334fa1 Mon Sep 17 00:00:00 2001 From: djwubs Date: Mon, 23 Mar 2020 11:38:19 +0100 Subject: [PATCH 14/32] Fixed split and join width --- lib/default.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/default.svg b/lib/default.svg index 99a6505..99d9cff 100644 --- a/lib/default.svg +++ b/lib/default.svg @@ -287,7 +287,7 @@ text { - + From ac173897841f77444a0f30832d9333a342dc7dbc Mon Sep 17 00:00:00 2001 From: djwubs Date: Wed, 25 Mar 2020 13:40:56 +0100 Subject: [PATCH 15/32] Removal of ExtendedEdge --- built/Cell.js | 14 ++++++++++---- built/elkGraph.js | 6 ++++-- lib/Cell.ts | 16 +++++++++++----- lib/elkGraph.ts | 19 +++++++------------ 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 396c154..4462131 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -267,13 +267,19 @@ var Cell = /** @class */ (function () { var edgeAdd = edge; _.forEach(cell_1.ports, function (port) { if (_.includes(inPorts_1, port)) { - if (edgeAdd.sources[0] === port.id.slice(_this.parent.length + 1) + '.Y') { - edgeAdd.sources[0] = port.id; + if (edgeAdd.sourcePort === port.id.slice(_this.parent.length + 1) + '.Y') { + var source = port.id.split('.'); + source.pop(); + edgeAdd.source = source.join('.'); + edgeAdd.sourcePort = port.id; } } else { - if (edgeAdd.targets[0] === port.id.slice(_this.parent.length + 1) + '.A') { - edgeAdd.targets[0] = port.id; + if (edgeAdd.targetPort === port.id.slice(_this.parent.length + 1) + '.A') { + var target = port.id.split('.'); + target.pop(); + edgeAdd.target = target.join('.'); + edgeAdd.targetPort = port.id; } } }); diff --git a/built/elkGraph.js b/built/elkGraph.js index 7287c5e..a2588d9 100644 --- a/built/elkGraph.js +++ b/built/elkGraph.js @@ -124,8 +124,10 @@ function route(sourcePorts, targetPorts, edges, moduleName) { var id = moduleName + '.e' + ElkModel.edgeIndex; var edge = { id: id, - sources: [sourceKey], - targets: [targetKey], + source: moduleName + '.' + sourceParentKey, + sourcePort: sourceKey, + target: moduleName + '.' + targetParentKey, + targetPort: targetKey, }; ElkModel.wireNameLookup[id] = targetPort.wire.netName; if (sourcePort.parentNode.type !== '$dff') { diff --git a/lib/Cell.ts b/lib/Cell.ts index ee6f09c..c10ab20 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -273,16 +273,22 @@ export default class Cell { cell.children.push(child); } }); - _.forEach(elk.edges, (edge: ElkModel.ExtendedEdge) => { + _.forEach(elk.edges, (edge: ElkModel.Edge) => { const edgeAdd = edge; _.forEach(cell.ports, (port) => { if (_.includes(inPorts, port)) { - if (edgeAdd.sources[0] === port.id.slice(this.parent.length + 1) + '.Y') { - edgeAdd.sources[0] = port.id; + if (edgeAdd.sourcePort === port.id.slice(this.parent.length + 1) + '.Y') { + const source: string[] = port.id.split('.'); + source.pop(); + edgeAdd.source = source.join('.'); + edgeAdd.sourcePort = port.id; } } else { - if (edgeAdd.targets[0] === port.id.slice(this.parent.length + 1) + '.A') { - edgeAdd.targets[0] = port.id; + if (edgeAdd.targetPort === port.id.slice(this.parent.length + 1) + '.A') { + const target: string[] = port.id.split('.'); + target.pop(); + edgeAdd.target = target.join('.'); + edgeAdd.targetPort = port.id; } } }); diff --git a/lib/elkGraph.ts b/lib/elkGraph.ts index 0938faf..89912a9 100644 --- a/lib/elkGraph.ts +++ b/lib/elkGraph.ts @@ -24,13 +24,13 @@ export namespace ElkModel { x?: number; y?: number; children?: Cell[]; - edges?: Array; + edges?: Edge[]; } export interface Graph { id: string; children: Cell[]; - edges: Array; + edges: Edge[]; width?: number; height?: number; } @@ -63,13 +63,6 @@ export namespace ElkModel { sections?: Section[]; } - export interface ExtendedEdge { - id: string; - sources: [ string ]; - targets: [ string ]; - layoutOptions?: LayoutOptions; - } - export interface LayoutOptions { [option: string]: any; } @@ -196,10 +189,12 @@ function route(sourcePorts, targetPorts, edges: ElkModel.Edge[], moduleName: str const targetParentKey: string = targetPort.parentNode.key; const targetKey: string = moduleName + '.' + targetParentKey + '.' + targetPort.key; const id: string = moduleName + '.e' + ElkModel.edgeIndex; - const edge: ElkModel.ExtendedEdge = { + const edge: ElkModel.Edge = { id, - sources: [sourceKey], - targets: [targetKey], + source: moduleName + '.' + sourceParentKey, + sourcePort: sourceKey, + target: moduleName + '.' + targetParentKey, + targetPort: targetKey, }; ElkModel.wireNameLookup[id] = targetPort.wire.netName; if (sourcePort.parentNode.type !== '$dff') { From 541fb7523e7b30598e91e4b8ac05e72c0a882377 Mon Sep 17 00:00:00 2001 From: djwubs Date: Wed, 25 Mar 2020 15:06:49 +0100 Subject: [PATCH 16/32] Start of config file --- bin/netlistsvg.js | 30 +++++++++++++++++++----------- built/ConfigModel.js | 2 ++ built/index.js | 5 ++++- lib/ConfigModel.ts | 4 ++++ lib/index.ts | 7 ++++++- 5 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 built/ConfigModel.js create mode 100644 lib/ConfigModel.ts diff --git a/bin/netlistsvg.js b/bin/netlistsvg.js index 5eec51c..d1f9450 100644 --- a/bin/netlistsvg.js +++ b/bin/netlistsvg.js @@ -14,47 +14,55 @@ require('ajv-errors')(ajv); if (require.main === module) { var argv = yargs .demand(1) - .usage('usage: $0 input_json_file [-o output_svg_file] [--skin skin_file] [--layout elk_json_file]') + .usage('usage: $0 input_json_file [-o output_svg_file] [--skin skin_file] [--layout elk_json_file] [--config config_json_file') .argv; - main(argv._[0], argv.o, argv.skin, argv.layout); + main(argv._[0], argv.o, argv.skin, argv.layout, argv.config); } -function render(skinData, netlist, outputPath, elkData) { +function render(skinData, netlist, outputPath, elkData, configData) { lib.render(skinData, netlist, (err, svgData) => { if (err) throw err; fs.writeFile(outputPath, svgData, 'utf-8', (err) => { if (err) throw err; }); - }, elkData); + }, elkData, configData); } -function parseFiles(skinPath, netlistPath, elkJsonPath, callback) { +function parseFiles(skinPath, netlistPath, elkJsonPath, configPath, callback) { + var elkData; + var configData; fs.readFile(skinPath, 'utf-8', (err, skinData) => { if (err) throw err; fs.readFile(netlistPath, (err, netlistData) => { if (err) throw err; if (elkJsonPath) { fs.readFile(elkJsonPath, (err, elkString) => { - callback(skinData, netlistData, json5.parse(elkString)); + if (err) throw err; + elkData = json5.parse(elkString); + }); + } + if (configPath) { + fs.readFile(configPath, (err, configString) => { + if (err) throw err; + configData = json5.parse(configString); }); - } else { - callback(skinData, netlistData); } + callback(skinData, netlistData, elkData, configData); }); }); } -function main(netlistPath, outputPath, skinPath, elkJsonPath) { +function main(netlistPath, outputPath, skinPath, elkJsonPath, configPath) { skinPath = skinPath || path.join(__dirname, '../lib/default.svg'); outputPath = outputPath || 'out.svg'; var schemaPath = path.join(__dirname, '../lib/yosys.schema.json5'); - parseFiles(skinPath, netlistPath, elkJsonPath, (skinData, netlistString, elkData) => { + parseFiles(skinPath, netlistPath, elkJsonPath, configPath, (skinData, netlistString, elkData, configData) => { var netlistJson = json5.parse(netlistString); var valid = ajv.validate(json5.parse(fs.readFileSync(schemaPath)), netlistJson); if (!valid) { throw Error(JSON.stringify(ajv.errors, null, 2)); } - render(skinData, netlistJson, outputPath, elkData); + render(skinData, netlistJson, outputPath, elkData, configData); }); } diff --git a/built/ConfigModel.js b/built/ConfigModel.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/built/ConfigModel.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/built/index.js b/built/index.js index ff02e11..6aad159 100644 --- a/built/index.js +++ b/built/index.js @@ -24,9 +24,12 @@ function dumpLayout(skinData, yosysNetlist, prelayout, done) { }); } exports.dumpLayout = dumpLayout; -function render(skinData, yosysNetlist, done, elkData) { +function render(skinData, yosysNetlist, done, elkData, configData) { var skin = onml.p(skinData); Skin_1.default.skin = skin; + if (configData) { + throw new Error('WIP'); + } var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist); var kgraph = elkGraph_1.buildElkGraph(flatModule); var promise; diff --git a/lib/ConfigModel.ts b/lib/ConfigModel.ts new file mode 100644 index 0000000..18c6712 --- /dev/null +++ b/lib/ConfigModel.ts @@ -0,0 +1,4 @@ +interface Config { + hierarchy: string[]; +} +export default Config; diff --git a/lib/index.ts b/lib/index.ts index 77acab3..155e527 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -5,6 +5,7 @@ import onml = require('onml'); import { FlatModule } from './FlatModule'; import Yosys from './YosysModel'; +import Config from './ConfigModel'; import Skin from './Skin'; import { ElkModel, buildElkGraph } from './elkGraph'; import drawModule from './drawModule'; @@ -30,9 +31,13 @@ export function dumpLayout(skinData: string, yosysNetlist: Yosys.Netlist, prelay }); } -export function render(skinData: string, yosysNetlist: Yosys.Netlist, done?: ICallback, elkData?: ElkModel.Graph) { +export function render(skinData: string, yosysNetlist: Yosys.Netlist, + done?: ICallback, elkData?: ElkModel.Graph, configData?: Config) { const skin = onml.p(skinData); Skin.skin = skin; + if (configData) { + throw new Error('WIP'); + } const flatModule = FlatModule.fromNetlist(yosysNetlist); const kgraph: ElkModel.Graph = buildElkGraph(flatModule); From 7fb05f9399d04c864427842b8d4a4a72f3553b77 Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 27 Mar 2020 16:12:24 +0100 Subject: [PATCH 17/32] Added basic config file functionality --- bin/netlistsvg.js | 11 +++-------- built/FlatModule.js | 13 +++++++++---- built/index.js | 22 +--------------------- lib/ConfigModel.ts | 8 +++++++- lib/FlatModule.ts | 15 +++++++++++---- lib/config.json | 10 ++++++++++ lib/index.ts | 22 +--------------------- test/FlatModule.test.ts | 5 ++++- 8 files changed, 46 insertions(+), 60 deletions(-) create mode 100644 lib/config.json diff --git a/bin/netlistsvg.js b/bin/netlistsvg.js index d1f9450..bc2b277 100644 --- a/bin/netlistsvg.js +++ b/bin/netlistsvg.js @@ -36,16 +36,10 @@ function parseFiles(skinPath, netlistPath, elkJsonPath, configPath, callback) { fs.readFile(netlistPath, (err, netlistData) => { if (err) throw err; if (elkJsonPath) { - fs.readFile(elkJsonPath, (err, elkString) => { - if (err) throw err; - elkData = json5.parse(elkString); - }); + elkData = json5.parse(fs.readFileSync(elkJsonPath)); } if (configPath) { - fs.readFile(configPath, (err, configString) => { - if (err) throw err; - configData = json5.parse(configString); - }); + configData = json5.parse(fs.readFileSync(configPath)); } callback(skinData, netlistData, elkData, configData); }); @@ -54,6 +48,7 @@ function parseFiles(skinPath, netlistPath, elkJsonPath, configPath, callback) { function main(netlistPath, outputPath, skinPath, elkJsonPath, configPath) { skinPath = skinPath || path.join(__dirname, '../lib/default.svg'); + configPath = configPath || path.join(__dirname, '../lib/config.json'); outputPath = outputPath || 'out.svg'; var schemaPath = path.join(__dirname, '../lib/yosys.schema.json5'); parseFiles(skinPath, netlistPath, elkJsonPath, configPath, (skinData, netlistString, elkData, configData) => { diff --git a/built/FlatModule.js b/built/FlatModule.js index 39603a8..c67ccd6 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -11,11 +11,15 @@ var FlatModule = /** @class */ (function () { this.moduleName = name; var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this.moduleName); }); var cells = _.map(mod.cells, function (c, key) { - if (!_.includes(FlatModule.modNames, c.type)) { - return Cell_1.default.fromYosysCell(c, key, _this.moduleName); + if (_.includes(FlatModule.config.hierarchy.types, c.type) || + _.includes(FlatModule.config.hierarchy.ids, key)) { + if (!_.includes(FlatModule.modNames, c.type)) { + throw new Error('Module in config file not included in input json file.'); + } + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type]); } else { - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type]); + return Cell_1.default.fromYosysCell(c, key, _this.moduleName); } }); this.nodes = cells.concat(ports); @@ -29,10 +33,11 @@ var FlatModule = /** @class */ (function () { } this.createWires(); } - FlatModule.fromNetlist = function (netlist) { + FlatModule.fromNetlist = function (netlist, config) { this.layoutProps = Skin_1.default.getProperties(); this.modNames = Object.keys(netlist.modules); this.netlist = netlist; + this.config = config; var topName = null; _.forEach(netlist.modules, function (mod, name) { if (mod.attributes && mod.attributes.top === 1) { diff --git a/built/index.js b/built/index.js index 6aad159..c4350a5 100644 --- a/built/index.js +++ b/built/index.js @@ -7,30 +7,10 @@ var Skin_1 = require("./Skin"); var elkGraph_1 = require("./elkGraph"); var drawModule_1 = require("./drawModule"); var elk = new ELK(); -function dumpLayout(skinData, yosysNetlist, prelayout, done) { - var skin = onml.p(skinData); - Skin_1.default.skin = skin; - var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist); - var kgraph = elkGraph_1.buildElkGraph(flatModule); - if (prelayout) { - done(null, JSON.stringify(kgraph, null, 2)); - return; - } - var promise = elk.layout(kgraph, { layoutOptions: FlatModule_1.FlatModule.layoutProps.layoutEngine }); - promise.then(function (graph) { - done(null, JSON.stringify(graph, null, 2)); - }).catch(function (reason) { - throw Error(reason); - }); -} -exports.dumpLayout = dumpLayout; function render(skinData, yosysNetlist, done, elkData, configData) { var skin = onml.p(skinData); Skin_1.default.skin = skin; - if (configData) { - throw new Error('WIP'); - } - var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist); + var flatModule = FlatModule_1.FlatModule.fromNetlist(yosysNetlist, configData); var kgraph = elkGraph_1.buildElkGraph(flatModule); var promise; // if we already have a layout then use it diff --git a/lib/ConfigModel.ts b/lib/ConfigModel.ts index 18c6712..201880e 100644 --- a/lib/ConfigModel.ts +++ b/lib/ConfigModel.ts @@ -1,4 +1,10 @@ interface Config { - hierarchy: string[]; + hierarchy: Hierarchy; } + +interface Hierarchy { + types: string[]; + ids: string[]; +} + export default Config; diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 7730906..3b8254b 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -1,4 +1,5 @@ import Yosys from './YosysModel'; +import Config from './ConfigModel'; import Skin from './Skin'; import Cell from './Cell'; import _ = require('lodash'); @@ -22,11 +23,13 @@ export class FlatModule { public static skin: any; public static layoutProps: {[x: string]: any}; public static modNames: string[]; + public static config: Config; - public static fromNetlist(netlist: Yosys.Netlist): FlatModule { + public static fromNetlist(netlist: Yosys.Netlist, config: Config): FlatModule { this.layoutProps = Skin.getProperties(); this.modNames = Object.keys(netlist.modules); this.netlist = netlist; + this.config = config; let topName = null; _.forEach(netlist.modules, (mod: Yosys.Module, name: string) => { if (mod.attributes && mod.attributes.top === 1) { @@ -51,10 +54,14 @@ export class FlatModule { this.moduleName = name; const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this.moduleName)); const cells = _.map(mod.cells, (c, key) => { - if (!_.includes(FlatModule.modNames, c.type)) { - return Cell.fromYosysCell(c, key, this.moduleName); - } else { + if (_.includes(FlatModule.config.hierarchy.types, c.type) || + _.includes(FlatModule.config.hierarchy.ids, key)) { + if (!_.includes(FlatModule.modNames, c.type)) { + throw new Error('Module in config file not included in input json file.'); + } return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type]); + } else { + return Cell.fromYosysCell(c, key, this.moduleName); } }); this.nodes = cells.concat(ports); diff --git a/lib/config.json b/lib/config.json new file mode 100644 index 0000000..2a02f4e --- /dev/null +++ b/lib/config.json @@ -0,0 +1,10 @@ +{ + "hierarchy": { + "types": [ + + ], + "id": [ + + ] + } +} \ No newline at end of file diff --git a/lib/index.ts b/lib/index.ts index 155e527..3408d0d 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -14,31 +14,11 @@ const elk = new ELK(); type ICallback = (error: Error, result?: string) => void; -export function dumpLayout(skinData: string, yosysNetlist: Yosys.Netlist, prelayout: boolean, done: ICallback) { - const skin = onml.p(skinData); - Skin.skin = skin; - const flatModule = FlatModule.fromNetlist(yosysNetlist); - const kgraph: ElkModel.Graph = buildElkGraph(flatModule); - if (prelayout) { - done(null, JSON.stringify(kgraph, null, 2)); - return; - } - const promise = elk.layout(kgraph, { layoutOptions: FlatModule.layoutProps.layoutEngine }); - promise.then((graph: ElkModel.Graph) => { - done(null, JSON.stringify(graph, null, 2)); - }).catch((reason) => { - throw Error(reason); - }); -} - export function render(skinData: string, yosysNetlist: Yosys.Netlist, done?: ICallback, elkData?: ElkModel.Graph, configData?: Config) { const skin = onml.p(skinData); Skin.skin = skin; - if (configData) { - throw new Error('WIP'); - } - const flatModule = FlatModule.fromNetlist(yosysNetlist); + const flatModule = FlatModule.fromNetlist(yosysNetlist, configData); const kgraph: ElkModel.Graph = buildElkGraph(flatModule); let promise; diff --git a/test/FlatModule.test.ts b/test/FlatModule.test.ts index e129346..6990307 100644 --- a/test/FlatModule.test.ts +++ b/test/FlatModule.test.ts @@ -4,6 +4,7 @@ import json5 = require('json5'); import onml = require('onml'); import Yosys from '../lib/YosysModel'; +import Config from '../lib/ConfigModel'; import Cell from '../lib/Cell'; import { FlatModule } from '../lib/FlatModule'; import Skin from '../lib/Skin'; @@ -15,11 +16,13 @@ import Skin from '../lib/Skin'; function createFlatModule(testFile: string): FlatModule { const testPath = path.join(__dirname,'digital', testFile + '.json'); const defaultSkin = path.join(__dirname, '../lib/default.svg'); + const defaultConfig = path.join(__dirname, '../lib/config.json'); const testStr = fs.readFileSync(testPath).toString(); const netlist: Yosys.Netlist = json5.parse(testStr); + const config: Config = json5.parse(fs.readFileSync(defaultConfig).toString()); const skin = onml.parse(fs.readFileSync(defaultSkin).toString()); Skin.skin = skin; - return FlatModule.fromNetlist(netlist); + return FlatModule.fromNetlist(netlist, config); } /** From 82ef76a2e78f1155bf9959f4301da6f68c882b1a Mon Sep 17 00:00:00 2001 From: djwubs Date: Sat, 28 Mar 2020 12:26:57 +0100 Subject: [PATCH 18/32] Added more functionality to config file --- built/Cell.js | 4 ++-- built/FlatModule.js | 48 +++++++++++++++++++++++++++++++++++---------- lib/Cell.ts | 5 +++-- lib/ConfigModel.ts | 6 ++++++ lib/FlatModule.ts | 44 ++++++++++++++++++++++++++++++++--------- lib/config.json | 12 ++++++------ 6 files changed, 90 insertions(+), 29 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 4462131..d628f03 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -91,7 +91,7 @@ var Cell = /** @class */ (function () { }); return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}, parent); }; - Cell.createSubModule = function (yCell, name, parent, subModule) { + Cell.createSubModule = function (yCell, name, parent, subModule, depth) { var template = Skin_1.default.findSkinType(yCell.type); var templateInputPids = Skin_1.default.getInputPids(template); var templateOutputPids = Skin_1.default.getOutputPids(template); @@ -106,7 +106,7 @@ var Cell = /** @class */ (function () { inputPorts = ports.filter(function (port) { return port.keyIn(inputPids_2); }); outputPorts = ports.filter(function (port) { return port.keyIn(outputPids_2); }); } - var mod = new FlatModule_1.FlatModule(subModule, name, parent); + var mod = new FlatModule_1.FlatModule(subModule, name, depth + 1, parent); return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod); }; Object.defineProperty(Cell.prototype, "Type", { diff --git a/built/FlatModule.js b/built/FlatModule.js index c67ccd6..83a7f5f 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -4,22 +4,50 @@ var Skin_1 = require("./Skin"); var Cell_1 = require("./Cell"); var _ = require("lodash"); var FlatModule = /** @class */ (function () { - function FlatModule(mod, name, parent) { + function FlatModule(mod, name, depth, parent) { var _this = this; if (parent === void 0) { parent = null; } this.parent = parent; this.moduleName = name; var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this.moduleName); }); var cells = _.map(mod.cells, function (c, key) { - if (_.includes(FlatModule.config.hierarchy.types, c.type) || - _.includes(FlatModule.config.hierarchy.ids, key)) { - if (!_.includes(FlatModule.modNames, c.type)) { - throw new Error('Module in config file not included in input json file.'); + switch (FlatModule.config.hierarchy.enable) { + case 'level': { + if (FlatModule.config.hierarchy.expandLevel > depth) { + if (_.includes(FlatModule.modNames, c.type)) { + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); + } + else { + return Cell_1.default.fromYosysCell(c, key, _this.moduleName); + } + } + else { + return Cell_1.default.fromYosysCell(c, key, _this.moduleName); + } + } + case 'all': { + if (_.includes(FlatModule.modNames, c.type)) { + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); + } + else { + return Cell_1.default.fromYosysCell(c, key, _this.moduleName); + } + } + case 'modules': { + if (_.includes(FlatModule.config.hierarchy.expandModules.types, c.type) || + _.includes(FlatModule.config.hierarchy.expandModules.ids, key)) { + if (!_.includes(FlatModule.modNames, c.type)) { + throw new Error('Module in config file not included in input json file.'); + } + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); + } + else { + return Cell_1.default.fromYosysCell(c, key, _this.moduleName); + } + } + default: { + return Cell_1.default.fromYosysCell(c, key, _this.moduleName); } - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type]); - } - else { - return Cell_1.default.fromYosysCell(c, key, _this.moduleName); } }); this.nodes = cells.concat(ports); @@ -49,7 +77,7 @@ var FlatModule = /** @class */ (function () { topName = this.modNames[0]; } var top = netlist.modules[topName]; - return new FlatModule(top, topName); + return new FlatModule(top, topName, 0); }; // converts input ports with constant assignments to constant nodes FlatModule.prototype.addConstants = function () { diff --git a/lib/Cell.ts b/lib/Cell.ts index c10ab20..abc13b3 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -78,7 +78,8 @@ export default class Cell { return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}, parent); } - public static createSubModule(yCell: Yosys.Cell, name: string, parent: string, subModule: Yosys.Module): Cell { + public static createSubModule(yCell: Yosys.Cell, name: string, parent: string, + subModule: Yosys.Module, depth: number): Cell { const template = Skin.findSkinType(yCell.type); const templateInputPids = Skin.getInputPids(template); const templateOutputPids = Skin.getOutputPids(template); @@ -93,7 +94,7 @@ export default class Cell { inputPorts = ports.filter((port) => port.keyIn(inputPids)); outputPorts = ports.filter((port) => port.keyIn(outputPids)); } - const mod = new FlatModule(subModule, name, parent); + const mod = new FlatModule(subModule, name, depth + 1, parent); return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod); } diff --git a/lib/ConfigModel.ts b/lib/ConfigModel.ts index 201880e..7dc8b0d 100644 --- a/lib/ConfigModel.ts +++ b/lib/ConfigModel.ts @@ -3,6 +3,12 @@ interface Config { } interface Hierarchy { + enable: 'off' | 'level' | 'all' | 'modules'; + expandLevel: number; + expandModules: ExpandModules; +} + +interface ExpandModules { types: string[]; ids: string[]; } diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 3b8254b..12749db 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -41,7 +41,7 @@ export class FlatModule { topName = this.modNames[0]; } const top = netlist.modules[topName]; - return new FlatModule(top, topName); + return new FlatModule(top, topName, 0); } public parent: string; @@ -49,19 +49,45 @@ export class FlatModule { public nodes: Cell[]; public wires: Wire[]; - constructor(mod: Yosys.Module, name: string, parent: string = null) { + constructor(mod: Yosys.Module, name: string, depth: number, parent: string = null) { this.parent = parent; this.moduleName = name; const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this.moduleName)); const cells = _.map(mod.cells, (c, key) => { - if (_.includes(FlatModule.config.hierarchy.types, c.type) || - _.includes(FlatModule.config.hierarchy.ids, key)) { - if (!_.includes(FlatModule.modNames, c.type)) { - throw new Error('Module in config file not included in input json file.'); + switch (FlatModule.config.hierarchy.enable) { + case 'level': { + if (FlatModule.config.hierarchy.expandLevel > depth) { + if (_.includes(FlatModule.modNames, c.type)) { + return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], + depth); + } else { + return Cell.fromYosysCell(c, key, this.moduleName); + } + } else { + return Cell.fromYosysCell(c, key, this.moduleName); + } + } + case 'all': { + if (_.includes(FlatModule.modNames, c.type)) { + return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], depth); + } else { + return Cell.fromYosysCell(c, key, this.moduleName); + } + } + case 'modules': { + if (_.includes(FlatModule.config.hierarchy.expandModules.types, c.type) || + _.includes(FlatModule.config.hierarchy.expandModules.ids, key)) { + if (!_.includes(FlatModule.modNames, c.type)) { + throw new Error('Module in config file not included in input json file.'); + } + return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], depth); + } else { + return Cell.fromYosysCell(c, key, this.moduleName); + } + } + default: { + return Cell.fromYosysCell(c, key, this.moduleName); } - return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type]); - } else { - return Cell.fromYosysCell(c, key, this.moduleName); } }); this.nodes = cells.concat(ports); diff --git a/lib/config.json b/lib/config.json index 2a02f4e..e258429 100644 --- a/lib/config.json +++ b/lib/config.json @@ -1,10 +1,10 @@ { "hierarchy": { - "types": [ - - ], - "id": [ - - ] + "enable": "off", + "expandLevel": 0, + "expandModules": { + "types": [], + "ids": [] + } } } \ No newline at end of file From 6c83a84afb22d1ab6f40d93d0d8ec4e87d6fbb0d Mon Sep 17 00:00:00 2001 From: djwubs Date: Thu, 2 Apr 2020 15:02:10 +0200 Subject: [PATCH 19/32] Added basic color scheme for hierarchy --- built/Cell.js | 2 ++ lib/Cell.ts | 2 ++ lib/default.svg | 54 ++++++++++++++++++++++++------------------------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index d628f03..00c8d6a 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -405,6 +405,8 @@ var Cell = /** @class */ (function () { var subModule = drawModule_1.drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; + tempclone[3][1].fill = '#D3D3D3'; + tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; tempclone.pop(); diff --git a/lib/Cell.ts b/lib/Cell.ts index abc13b3..49b3cc6 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -412,6 +412,8 @@ export default class Cell { const subModule = drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; + tempclone[3][1].fill = '#D3D3D3'; + tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; tempclone.pop(); diff --git a/lib/default.svg b/lib/default.svg index 99d9cff..44f3d02 100644 --- a/lib/default.svg +++ b/lib/default.svg @@ -37,7 +37,7 @@ text { - + @@ -51,7 +51,7 @@ text { - + @@ -63,8 +63,8 @@ text { - - + + @@ -77,8 +77,7 @@ text { - - + @@ -90,9 +89,8 @@ text { - - - + + @@ -105,7 +103,7 @@ text { - + @@ -117,9 +115,9 @@ text { - + - + @@ -132,8 +130,8 @@ text { - - + + @@ -142,7 +140,7 @@ text { - + @@ -154,7 +152,7 @@ text { - + @@ -164,7 +162,7 @@ text { - + @@ -178,7 +176,7 @@ text { - + @@ -190,9 +188,9 @@ text { - + - + @@ -203,7 +201,7 @@ text { - + @@ -215,7 +213,7 @@ text { - + @@ -228,7 +226,7 @@ text { - + @@ -241,7 +239,7 @@ text { - + @@ -253,7 +251,7 @@ text { input - + @@ -261,7 +259,7 @@ text { constant - + @@ -269,7 +267,7 @@ text { output - + @@ -301,7 +299,7 @@ text { generic - + out0 From 628ae71bfdc3141ebd2d4003ab15b270b63fb202 Mon Sep 17 00:00:00 2001 From: djwubs Date: Thu, 2 Apr 2020 15:38:02 +0200 Subject: [PATCH 20/32] Small color changes --- lib/default.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/default.svg b/lib/default.svg index 44f3d02..79176b5 100644 --- a/lib/default.svg +++ b/lib/default.svg @@ -299,7 +299,7 @@ text { generic - + out0 From 156d56c1e19fe292db44584a781ad1a601070b45 Mon Sep 17 00:00:00 2001 From: djwubs Date: Sat, 4 Apr 2020 11:56:29 +0200 Subject: [PATCH 21/32] Removal of useless svg properties --- built/Cell.js | 2 +- lib/Cell.ts | 2 +- lib/default.svg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 00c8d6a..1c74f37 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -405,7 +405,7 @@ var Cell = /** @class */ (function () { var subModule = drawModule_1.drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; - tempclone[3][1].fill = '#D3D3D3'; + tempclone[3][1].fill = '#e9e9e9'; tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; diff --git a/lib/Cell.ts b/lib/Cell.ts index 49b3cc6..cea40ff 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -412,7 +412,7 @@ export default class Cell { const subModule = drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; - tempclone[3][1].fill = '#D3D3D3'; + tempclone[3][1].fill = '#e9e9e9'; tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; diff --git a/lib/default.svg b/lib/default.svg index 79176b5..4da4abd 100644 --- a/lib/default.svg +++ b/lib/default.svg @@ -299,7 +299,7 @@ text { generic - + out0 From c98d4cb9881e25b4f9d7e2098e590e865f8c177e Mon Sep 17 00:00:00 2001 From: djwubs Date: Mon, 6 Apr 2020 12:26:17 +0200 Subject: [PATCH 22/32] Fix of direct port lines in submodule --- built/Cell.js | 51 +++++++++++++++++++++++++++++++++++++++++++++-- built/Port.js | 14 ++++++++++++- built/elkGraph.js | 2 +- lib/Cell.ts | 39 ++++++++++++++++++++++++++++++++++-- lib/Port.ts | 14 ++++++++++++- lib/elkGraph.ts | 3 ++- 6 files changed, 115 insertions(+), 8 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 1c74f37..0bdaedf 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -1,4 +1,15 @@ "use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; Object.defineProperty(exports, "__esModule", { value: true }); var FlatModule_1 = require("./FlatModule"); var YosysModel_1 = require("./YosysModel"); @@ -246,7 +257,7 @@ var Cell = /** @class */ (function () { var elk = elkGraph_1.buildElkGraph(this.subModule); var cell_1 = { id: this.parent + '.' + this.key, - layoutOptions: [], + layoutOptions: { 'org.eclipse.elk.portConstraints': 'FIXED_SIDE' }, labels: [], ports: inPorts_1.concat(outPorts), children: [], @@ -283,7 +294,43 @@ var Cell = /** @class */ (function () { } } }); - cell_1.edges.push(edgeAdd); + if (edgeAdd.source === edgeAdd.target) { + var dummyId = _this.subModule.moduleName + '.$d_' + edgeAdd.sourcePort + '_' + edgeAdd.targetPort; + var dummy = { + id: dummyId, + width: 0, + height: 0, + ports: [ + { + id: dummyId + '.pin', + width: 0, + height: 0, + }, + { + id: dummyId + '.pout', + width: 0, + height: 0, + }, + ], + layoutOptions: { 'org.eclipse.elk.portConstraints': 'FIXED_SIDE' }, + }; + var edgeId = edgeAdd.id; + var edgeAddCopy = __assign({}, edgeAdd); + edgeAdd.target = dummyId; + edgeAdd.targetPort = dummyId + '.pin'; + edgeAdd.id = _this.subModule.moduleName + '.e_' + edgeAdd.sourcePort + '_' + edgeAdd.targetPort; + elkGraph_1.ElkModel.wireNameLookup[edgeAdd.id] = elkGraph_1.ElkModel.wireNameLookup[edgeId]; + edgeAddCopy.source = dummyId; + edgeAddCopy.sourcePort = dummyId + '.pout'; + edgeAddCopy.id = _this.subModule.moduleName + '.e_' + edgeAddCopy.sourcePort + + '_' + edgeAddCopy.targetPort; + elkGraph_1.ElkModel.wireNameLookup[edgeAddCopy.id] = elkGraph_1.ElkModel.wireNameLookup[edgeId]; + cell_1.edges.push(edgeAdd, edgeAddCopy); + cell_1.children.push(dummy); + } + else { + cell_1.edges.push(edgeAdd); + } }); if (fixedPosX) { cell_1.x = fixedPosX; diff --git a/built/Port.js b/built/Port.js index 28c9b85..2c1ff71 100644 --- a/built/Port.js +++ b/built/Port.js @@ -30,7 +30,7 @@ var Port = /** @class */ (function () { var portSigs = this.value; portSigs.forEach(function (portSig, portSigIndex) { // is constant? - if (portSig === '0' || portSig === '1') { + if (portSig === '0' || portSig === '1' || portSig === 'x') { maxNum += 1; constNameCollector += portSig; // replace the constant with new signal num @@ -70,6 +70,9 @@ var Port = /** @class */ (function () { width: (6 * this.key.length), height: 11, }]; + if (type === 'generic') { + ret.layoutOptions = { 'org.eclipse.elk.port.side': 'WEST' }; + } } if ((type === 'generic' || type === 'split') && dir === 'out') { ret.labels = [{ @@ -80,6 +83,9 @@ var Port = /** @class */ (function () { width: (6 * this.key.length), height: 11, }]; + if (type === 'generic') { + ret.layoutOptions = { 'org.eclipse.elk.port.side': 'EAST' }; + } } if (type === 'generic' && this.parentNode.subModule !== null) { delete ret.x; @@ -105,6 +111,12 @@ var Port = /** @class */ (function () { width: (6 * this.key.length), height: 11, }]; + if (dir === 'in') { + ret.layoutOptions = { 'org.eclipse.elk.port.side': 'WEST' }; + } + if (dir === 'out') { + ret.layoutOptions = { 'org.eclipse.elk.port.side': 'EAST' }; + } } if (type === 'generic' && this.parentNode.subModule !== null) { delete ret.x; diff --git a/built/elkGraph.js b/built/elkGraph.js index a2588d9..08ca194 100644 --- a/built/elkGraph.js +++ b/built/elkGraph.js @@ -105,7 +105,7 @@ function addDummy(children, moduleName) { width: 0, height: 0, ports: [{ - id: moduleName + '.' + dummyId + '.p', + id: dummyId + '.p', width: 0, height: 0, }], diff --git a/lib/Cell.ts b/lib/Cell.ts index cea40ff..9f79cd8 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -257,7 +257,7 @@ export default class Cell { const elk = buildElkGraph(this.subModule); const cell: ElkModel.Cell = { id: this.parent + '.' + this.key, - layoutOptions: [], + layoutOptions: {'org.eclipse.elk.portConstraints': 'FIXED_SIDE'}, labels: [], ports: inPorts.concat(outPorts), children: [], @@ -293,7 +293,42 @@ export default class Cell { } } }); - cell.edges.push(edgeAdd); + if (edgeAdd.source === edgeAdd.target) { + const dummyId = this.subModule.moduleName + '.$d_' + edgeAdd.sourcePort + '_' + edgeAdd.targetPort; + const dummy: ElkModel.Cell = { + id: dummyId, + width: 0, + height: 0, + ports: [ + { + id: dummyId + '.pin', + width: 0, + height: 0, + }, + { + id: dummyId + '.pout', + width: 0, + height: 0, + }, + ], + layoutOptions: { 'org.eclipse.elk.portConstraints': 'FIXED_SIDE' }, + }; + const edgeId = edgeAdd.id; + const edgeAddCopy = {...edgeAdd}; + edgeAdd.target = dummyId; + edgeAdd.targetPort = dummyId + '.pin'; + edgeAdd.id = this.subModule.moduleName + '.e_' + edgeAdd.sourcePort + '_' + edgeAdd.targetPort; + ElkModel.wireNameLookup[edgeAdd.id] = ElkModel.wireNameLookup[edgeId]; + edgeAddCopy.source = dummyId; + edgeAddCopy.sourcePort = dummyId + '.pout'; + edgeAddCopy.id = this.subModule.moduleName + '.e_' + edgeAddCopy.sourcePort + + '_' + edgeAddCopy.targetPort; + ElkModel.wireNameLookup[edgeAddCopy.id] = ElkModel.wireNameLookup[edgeId]; + cell.edges.push(edgeAdd, edgeAddCopy); + cell.children.push(dummy); + } else { + cell.edges.push(edgeAdd); + } }); if (fixedPosX) { cell.x = fixedPosX; diff --git a/lib/Port.ts b/lib/Port.ts index d8d1f2c..a859f03 100644 --- a/lib/Port.ts +++ b/lib/Port.ts @@ -39,7 +39,7 @@ export class Port { const portSigs: Yosys.Signals = this.value; portSigs.forEach((portSig, portSigIndex) => { // is constant? - if (portSig === '0' || portSig === '1') { + if (portSig === '0' || portSig === '1' || portSig === 'x') { maxNum += 1; constNameCollector += portSig; // replace the constant with new signal num @@ -96,6 +96,9 @@ export class Port { width: (6 * this.key.length), height: 11, }]; + if (type === 'generic') { + ret.layoutOptions = {'org.eclipse.elk.port.side': 'WEST'}; + } } if ((type === 'generic' || type === 'split') && dir === 'out') { @@ -107,6 +110,9 @@ export class Port { width: (6 * this.key.length), height: 11, }]; + if (type === 'generic') { + ret.layoutOptions = {'org.eclipse.elk.port.side': 'EAST'}; + } } if (type === 'generic' && this.parentNode.subModule !== null) { @@ -132,6 +138,12 @@ export class Port { width: (6 * this.key.length), height: 11, }]; + if (dir === 'in') { + ret.layoutOptions = {'org.eclipse.elk.port.side': 'WEST'}; + } + if (dir === 'out') { + ret.layoutOptions = {'org.eclipse.elk.port.side': 'EAST'}; + } } if (type === 'generic' && this.parentNode.subModule !== null) { diff --git a/lib/elkGraph.ts b/lib/elkGraph.ts index 89912a9..e7da736 100644 --- a/lib/elkGraph.ts +++ b/lib/elkGraph.ts @@ -42,6 +42,7 @@ export namespace ElkModel { x?: number; y?: number; labels?: Label[]; + layoutOptions?: LayoutOptions; } export interface Section { @@ -171,7 +172,7 @@ function addDummy(children: ElkModel.Cell[], moduleName: string) { width: 0, height: 0, ports: [{ - id: moduleName + '.' + dummyId + '.p', + id: dummyId + '.p', width: 0, height: 0, }], From 8b64a9f1bfb0d7b3ce6ce257019414dc13f9de71 Mon Sep 17 00:00:00 2001 From: djwubs Date: Tue, 7 Apr 2020 11:26:54 +0200 Subject: [PATCH 23/32] Split and Join improvements --- built/Cell.js | 6 ++++++ lib/Cell.ts | 6 ++++++ lib/default.svg | 10 +++++----- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 0bdaedf..1f2b206 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -236,6 +236,12 @@ var Cell = /** @class */ (function () { layoutOptions: layoutAttrs, labels: [], }; + if (type === 'split') { + cell.ports[0].y = cell.height / 2; + } + if (type === 'join') { + cell.ports[cell.ports.length - 1].y = cell.height / 2; + } if (fixedPosX) { cell.x = fixedPosX; } diff --git a/lib/Cell.ts b/lib/Cell.ts index 9f79cd8..6c1978b 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -238,6 +238,12 @@ export default class Cell { layoutOptions: layoutAttrs, labels: [], }; + if (type === 'split') { + cell.ports[0].y = cell.height / 2; + } + if (type === 'join') { + cell.ports[cell.ports.length - 1].y = cell.height / 2; + } if (fixedPosX) { cell.x = fixedPosX; } diff --git a/lib/default.svg b/lib/default.svg index 4da4abd..76b50d0 100644 --- a/lib/default.svg +++ b/lib/default.svg @@ -276,11 +276,11 @@ text { - - + + hi:lo - + hi:lo @@ -289,10 +289,10 @@ text { - + hi:lo - + hi:lo From d3883427d408b0673815ec00dc8b34aedebdf984 Mon Sep 17 00:00:00 2001 From: djwubs Date: Wed, 8 Apr 2020 12:03:13 +0200 Subject: [PATCH 24/32] Added different top selection in config file --- built/FlatModule.js | 24 ++++++++++++++++-------- lib/ConfigModel.ts | 6 ++++++ lib/FlatModule.ts | 23 +++++++++++++++-------- lib/config.json | 4 ++++ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/built/FlatModule.js b/built/FlatModule.js index 83a7f5f..6f2a079 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -37,7 +37,7 @@ var FlatModule = /** @class */ (function () { if (_.includes(FlatModule.config.hierarchy.expandModules.types, c.type) || _.includes(FlatModule.config.hierarchy.expandModules.ids, key)) { if (!_.includes(FlatModule.modNames, c.type)) { - throw new Error('Module in config file not included in input json file.'); + throw new Error('Submodule in config file not defined in input json file.'); } return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); } @@ -67,14 +67,22 @@ var FlatModule = /** @class */ (function () { this.netlist = netlist; this.config = config; var topName = null; - _.forEach(netlist.modules, function (mod, name) { - if (mod.attributes && mod.attributes.top === 1) { - topName = name; + if (this.config.top.enable) { + topName = this.config.top.module; + if (!_.includes(this.modNames, topName)) { + throw new Error('Top module in config file not defined in input json file.'); + } + } + else { + _.forEach(netlist.modules, function (mod, name) { + if (mod.attributes && mod.attributes.top === 1) { + topName = name; + } + }); + // Otherwise default the first one in the file... + if (topName == null) { + topName = this.modNames[0]; } - }); - // Otherwise default the first one in the file... - if (topName == null) { - topName = this.modNames[0]; } var top = netlist.modules[topName]; return new FlatModule(top, topName, 0); diff --git a/lib/ConfigModel.ts b/lib/ConfigModel.ts index 7dc8b0d..3a74376 100644 --- a/lib/ConfigModel.ts +++ b/lib/ConfigModel.ts @@ -1,5 +1,6 @@ interface Config { hierarchy: Hierarchy; + top: Top; } interface Hierarchy { @@ -13,4 +14,9 @@ interface ExpandModules { ids: string[]; } +interface Top { + enable: boolean; + module: string; +} + export default Config; diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 12749db..56e7435 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -31,14 +31,21 @@ export class FlatModule { this.netlist = netlist; this.config = config; let topName = null; - _.forEach(netlist.modules, (mod: Yosys.Module, name: string) => { - if (mod.attributes && mod.attributes.top === 1) { - topName = name; + if (this.config.top.enable) { + topName = this.config.top.module; + if (!_.includes(this.modNames, topName)) { + throw new Error('Top module in config file not defined in input json file.'); + } + } else { + _.forEach(netlist.modules, (mod: Yosys.Module, name: string) => { + if (mod.attributes && mod.attributes.top === 1) { + topName = name; + } + }); + // Otherwise default the first one in the file... + if (topName == null) { + topName = this.modNames[0]; } - }); - // Otherwise default the first one in the file... - if (topName == null) { - topName = this.modNames[0]; } const top = netlist.modules[topName]; return new FlatModule(top, topName, 0); @@ -78,7 +85,7 @@ export class FlatModule { if (_.includes(FlatModule.config.hierarchy.expandModules.types, c.type) || _.includes(FlatModule.config.hierarchy.expandModules.ids, key)) { if (!_.includes(FlatModule.modNames, c.type)) { - throw new Error('Module in config file not included in input json file.'); + throw new Error('Submodule in config file not defined in input json file.'); } return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], depth); } else { diff --git a/lib/config.json b/lib/config.json index e258429..3b17990 100644 --- a/lib/config.json +++ b/lib/config.json @@ -6,5 +6,9 @@ "types": [], "ids": [] } + }, + "top": { + "enable": false, + "module": "" } } \ No newline at end of file From 50a51eac1281601a085a88fed543e670b99a87af Mon Sep 17 00:00:00 2001 From: djwubs Date: Thu, 9 Apr 2020 10:19:28 +0200 Subject: [PATCH 25/32] Yosys input update --- built/FlatModule.js | 2 +- lib/FlatModule.ts | 2 +- lib/YosysModel.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/built/FlatModule.js b/built/FlatModule.js index 6f2a079..55a1576 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -75,7 +75,7 @@ var FlatModule = /** @class */ (function () { } else { _.forEach(netlist.modules, function (mod, name) { - if (mod.attributes && mod.attributes.top === 1) { + if (mod.attributes && (mod.attributes.top === 1 || mod.attributes.top === '00000000000000000000000000000001')) { topName = name; } }); diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 56e7435..635d81a 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -38,7 +38,7 @@ export class FlatModule { } } else { _.forEach(netlist.modules, (mod: Yosys.Module, name: string) => { - if (mod.attributes && mod.attributes.top === 1) { + if (mod.attributes && (mod.attributes.top === 1 || mod.attributes.top === '00000000000000000000000000000001')) { topName = name; } }); diff --git a/lib/YosysModel.ts b/lib/YosysModel.ts index 5005a48..d3b49d0 100644 --- a/lib/YosysModel.ts +++ b/lib/YosysModel.ts @@ -17,7 +17,7 @@ namespace Yosys { } interface ModuleAttributes { - top?: number; + top?: number|string; [attrName: string]: any; } From 512be5b9f790e7f62ba42b268c19be6ec73b38ea Mon Sep 17 00:00:00 2001 From: djwubs Date: Sun, 19 Apr 2020 16:08:22 +0200 Subject: [PATCH 26/32] Quick change of top selection --- lib/FlatModule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 635d81a..43fbb33 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -38,7 +38,7 @@ export class FlatModule { } } else { _.forEach(netlist.modules, (mod: Yosys.Module, name: string) => { - if (mod.attributes && (mod.attributes.top === 1 || mod.attributes.top === '00000000000000000000000000000001')) { + if (mod.attributes && Number(mod.attributes.top) === 1) { topName = name; } }); From 0c84f5c667cb096c6c73f79973f1ea648e410485 Mon Sep 17 00:00:00 2001 From: djwubs Date: Tue, 21 Apr 2020 16:47:51 +0200 Subject: [PATCH 27/32] Addition of colour selection in config --- built/Cell.js | 10 ++++++---- built/FlatModule.js | 15 +++++++++++---- lib/Cell.ts | 11 +++++++---- lib/ConfigModel.ts | 1 + lib/FlatModule.ts | 14 +++++++++++--- lib/config.json | 3 ++- 6 files changed, 38 insertions(+), 16 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 1f2b206..99f0de8 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -21,9 +21,10 @@ var elkGraph_1 = require("./elkGraph"); var clone = require("clone"); var onml = require("onml"); var Cell = /** @class */ (function () { - function Cell(key, type, inputPorts, outputPorts, attributes, parent, subModule) { + function Cell(key, type, inputPorts, outputPorts, attributes, parent, subModule, subColour) { var _this = this; if (subModule === void 0) { subModule = null; } + if (subColour === void 0) { subColour = null; } this.key = key; this.type = type; this.inputPorts = inputPorts; @@ -31,6 +32,7 @@ var Cell = /** @class */ (function () { this.attributes = attributes || {}; this.parent = parent; this.subModule = subModule; + this.colour = subColour; inputPorts.forEach(function (ip) { ip.parentNode = _this; }); @@ -102,7 +104,7 @@ var Cell = /** @class */ (function () { }); return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}, parent); }; - Cell.createSubModule = function (yCell, name, parent, subModule, depth) { + Cell.createSubModule = function (yCell, name, parent, subModule, depth, colour) { var template = Skin_1.default.findSkinType(yCell.type); var templateInputPids = Skin_1.default.getInputPids(template); var templateOutputPids = Skin_1.default.getOutputPids(template); @@ -118,7 +120,7 @@ var Cell = /** @class */ (function () { outputPorts = ports.filter(function (port) { return port.keyIn(outputPids_2); }); } var mod = new FlatModule_1.FlatModule(subModule, name, depth + 1, parent); - return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod, colour); }; Object.defineProperty(Cell.prototype, "Type", { get: function () { @@ -458,7 +460,7 @@ var Cell = /** @class */ (function () { var subModule = drawModule_1.drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; - tempclone[3][1].fill = '#e9e9e9'; + tempclone[3][1].fill = this.colour; tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; diff --git a/built/FlatModule.js b/built/FlatModule.js index 55a1576..f9560c2 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -9,13 +9,20 @@ var FlatModule = /** @class */ (function () { if (parent === void 0) { parent = null; } this.parent = parent; this.moduleName = name; + var colour; + if (FlatModule.config.hierarchy.colour[depth]) { + colour = FlatModule.config.hierarchy.colour[depth]; + } + else { + colour = FlatModule.config.hierarchy.colour[FlatModule.config.hierarchy.colour.length - 1]; + } var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this.moduleName); }); var cells = _.map(mod.cells, function (c, key) { switch (FlatModule.config.hierarchy.enable) { case 'level': { if (FlatModule.config.hierarchy.expandLevel > depth) { if (_.includes(FlatModule.modNames, c.type)) { - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth, colour); } else { return Cell_1.default.fromYosysCell(c, key, _this.moduleName); @@ -27,7 +34,7 @@ var FlatModule = /** @class */ (function () { } case 'all': { if (_.includes(FlatModule.modNames, c.type)) { - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth, colour); } else { return Cell_1.default.fromYosysCell(c, key, _this.moduleName); @@ -39,7 +46,7 @@ var FlatModule = /** @class */ (function () { if (!_.includes(FlatModule.modNames, c.type)) { throw new Error('Submodule in config file not defined in input json file.'); } - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth, colour); } else { return Cell_1.default.fromYosysCell(c, key, _this.moduleName); @@ -75,7 +82,7 @@ var FlatModule = /** @class */ (function () { } else { _.forEach(netlist.modules, function (mod, name) { - if (mod.attributes && (mod.attributes.top === 1 || mod.attributes.top === '00000000000000000000000000000001')) { + if (mod.attributes && Number(mod.attributes.top) === 1) { topName = name; } }); diff --git a/lib/Cell.ts b/lib/Cell.ts index 6c1978b..7d3f563 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -79,7 +79,7 @@ export default class Cell { } public static createSubModule(yCell: Yosys.Cell, name: string, parent: string, - subModule: Yosys.Module, depth: number): Cell { + subModule: Yosys.Module, depth: number, colour: string): Cell { const template = Skin.findSkinType(yCell.type); const templateInputPids = Skin.getInputPids(template); const templateOutputPids = Skin.getOutputPids(template); @@ -95,11 +95,12 @@ export default class Cell { outputPorts = ports.filter((port) => port.keyIn(outputPids)); } const mod = new FlatModule(subModule, name, depth + 1, parent); - return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod, colour); } public parent: string; public subModule: FlatModule; + public colour: string; protected key: string; protected type: string; protected inputPorts: Port[]; @@ -112,7 +113,8 @@ export default class Cell { outputPorts: Port[], attributes: Yosys.CellAttributes, parent: string, - subModule: FlatModule = null) { + subModule: FlatModule = null, + subColour: string = null) { this.key = key; this.type = type; this.inputPorts = inputPorts; @@ -120,6 +122,7 @@ export default class Cell { this.attributes = attributes || {}; this.parent = parent; this.subModule = subModule; + this.colour = subColour; inputPorts.forEach((ip) => { ip.parentNode = this; }); @@ -453,7 +456,7 @@ export default class Cell { const subModule = drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; - tempclone[3][1].fill = '#e9e9e9'; + tempclone[3][1].fill = this.colour; tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; diff --git a/lib/ConfigModel.ts b/lib/ConfigModel.ts index 3a74376..727e192 100644 --- a/lib/ConfigModel.ts +++ b/lib/ConfigModel.ts @@ -7,6 +7,7 @@ interface Hierarchy { enable: 'off' | 'level' | 'all' | 'modules'; expandLevel: number; expandModules: ExpandModules; + colour: string[]; } interface ExpandModules { diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index 43fbb33..fd8f59c 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -59,6 +59,12 @@ export class FlatModule { constructor(mod: Yosys.Module, name: string, depth: number, parent: string = null) { this.parent = parent; this.moduleName = name; + let colour: string; + if (FlatModule.config.hierarchy.colour[depth]) { + colour = FlatModule.config.hierarchy.colour[depth]; + } else { + colour = FlatModule.config.hierarchy.colour[FlatModule.config.hierarchy.colour.length - 1]; + } const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this.moduleName)); const cells = _.map(mod.cells, (c, key) => { switch (FlatModule.config.hierarchy.enable) { @@ -66,7 +72,7 @@ export class FlatModule { if (FlatModule.config.hierarchy.expandLevel > depth) { if (_.includes(FlatModule.modNames, c.type)) { return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], - depth); + depth, colour); } else { return Cell.fromYosysCell(c, key, this.moduleName); } @@ -76,7 +82,8 @@ export class FlatModule { } case 'all': { if (_.includes(FlatModule.modNames, c.type)) { - return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], depth); + return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], + depth, colour); } else { return Cell.fromYosysCell(c, key, this.moduleName); } @@ -87,7 +94,8 @@ export class FlatModule { if (!_.includes(FlatModule.modNames, c.type)) { throw new Error('Submodule in config file not defined in input json file.'); } - return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], depth); + return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], + depth, colour); } else { return Cell.fromYosysCell(c, key, this.moduleName); } diff --git a/lib/config.json b/lib/config.json index 3b17990..2b46260 100644 --- a/lib/config.json +++ b/lib/config.json @@ -5,7 +5,8 @@ "expandModules": { "types": [], "ids": [] - } + }, + "colour": ["#e9e9e9"] }, "top": { "enable": false, From c4782fa0b02f473238e9563e7eec4324b125ac8b Mon Sep 17 00:00:00 2001 From: djwubs Date: Thu, 30 Apr 2020 09:46:14 +0200 Subject: [PATCH 28/32] Cleanup of defaultsvg --- lib/default.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/default.svg b/lib/default.svg index 76b50d0..76a5ed3 100644 --- a/lib/default.svg +++ b/lib/default.svg @@ -77,7 +77,7 @@ text { - + @@ -89,7 +89,7 @@ text { - + From 1be391fd7133ef80b9eb641176c059aa13322366 Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 8 May 2020 11:38:05 +0200 Subject: [PATCH 29/32] Hierarchy update readme --- README.md | 480 +++++++++++++++++++++++++++++++++++----------- bin/netlistsvg.js | 2 +- doc/hierarchy.svg | 142 ++++++++++++++ 3 files changed, 509 insertions(+), 115 deletions(-) create mode 100644 doc/hierarchy.svg diff --git a/README.md b/README.md index 1266a86..08790c3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![npm](https://img.shields.io/npm/dm/netlistsvg.svg) # netlistsvg -draws an SVG schematic from a [yosys](https://github.com/cliffordwolf/yosys) JSON netlist. This can be generated [the `write_json` command](http://www.clifford.at/yosys/cmd_json.html). It uses [elkjs](https://github.com/OpenKieler/elkjs) for layout. +Netlistsvg draws an SVG schematic from a [yosys](https://github.com/cliffordwolf/yosys) JSON netlist. This can be generated with [the `write_json` command](http://www.clifford.at/yosys/cmd_json.html). It uses [elkjs](https://github.com/OpenKieler/elkjs) for layout. You can see an online demo [here](https://nturley.github.io/netlistsvg) @@ -17,26 +17,26 @@ Install nodejs if isn't already installed npm install -g netlistsvg ``` -You can execute netlistsvg like this. +You can execute netlistsvg like this: ``` -netlistsvg input_json_file [-o output_svg_file] [--skin skin_file] +netlistsvg input_json_file [-o output_svg_file] [--skin skin_file] [--layout elk_json_file] [--config config_json_file] ``` The default value for the output file is out.svg. -Should work on Linux, OSX, and Windows. Running the build scripts (makefiles and the web demo) is easiest on Linux and OSX. +This hould work on Linux, OSX, and Windows. Running the build scripts (makefiles and the web demo) is easiest on Linux and OSX. ## Web bundle I have a web bundle hosted on github pages here: https://nturley.github.io/netlistsvg/built/netlistsvg.bundle.js It doesn't wrap ELKjs, so you'll need to include it separately. ELK creates a global variable, so you'll need to include ELKjs before netlistsvg. -In HTML it would look something like this +In HTML it would look something like this: ```html ``` -On ObservableHQ, you can require it like this. +On ObservableHQ, you can require it like this: ```javascript netlistsvg = { @@ -48,7 +48,7 @@ netlistsvg = { You may want to download and host your own copy. -The web bundle includes both the analog and digital skin and an example netlist for each. Using a promise would look like this. +The web bundle includes both the analog and digital skin and an example netlist for each. Using a promise would look like this: ```javascript await netlistsvg.render(netlistsvg.digitalSkin, netlistsvg.exampleDigital); ``` @@ -59,6 +59,142 @@ netlistsvg.render(netlistsvg.digitalSkin, netlistsvg.exampleDigital, (err, resul To turn Verilog into YosysJSON in the browser, you can use [YosysJS](http://www.clifford.at/yosys/yosysjs.html) +## Skin File +It pulls the node icons and configuration options from a SVG skin file. This our default digital skin file. + + + +This is our analog skin file. + + + +A skin file can use style tags or inline CSS to style the elements. That will be copied onto the output file. A skin file also defines a library of components to use. Each component has an alias list. It will use that component as a template for any cell with that type that it encounters. Each component defines the position and id of each of its ports so we know where to attach the wires to. + +For example, here is a mux definition. It has two aliases: "$pmux" and "$mux". It defines a type name, and a width and height, as well as the position and id of each of it's ports. In general you can rearrange them however you like, and add whatever SVG elements you like inside the template. + +```XML + + + + + + + + + + + +``` + +In addition to the library of components that are matched to cells, a skin file defines some special nodes. Input/Output ports, constants, Splits/Joins, and the generic node. Splits/Joins and the generic nodes are resized and ports are added or removed to adjust to the cell. The generic node is also used as a template for displaying submodules in a hierarchical schematic. + +The elkjs layout properties are also defined in the skin file. + +```XML + +``` +Any properties specified here will get passed along to the layout engine. Node and edge properties aren't configurable (yet). + +## ElkJS +ELK is using a layered approach (Sugiyama, Ganser), similar to dot in the Graphviz package. You can read about their algorithm here: https://rtsys.informatik.uni-kiel.de/%7Ebiblio/downloads/papers/jvlc13.pdf + +## Input JSON +Yosys JSON includes more information than we need. Only the modules specified in the configuration file are rendered. If the cell name matches one of the aliases of a template from the skin, then it will use it as a template for the SVG file. Port directions are optional for cells that are defined in the skin (not generic cells). + +It should look something like this: +```json +{ + "modules": { + "": { + "ports": { + "": { + "direction": "": { + "type": "": "", + ... + }, + "connections": { + "": [ 3, "0", ... ], + ... + } + }, + ... + } + } +} +``` + +## Generating `input_json_file` with Yosys + +[Yosys from Clifford Wolf](https://github.com/cliffordwolf/yosys) can be used to generate the `input_json_file` using [the `write_json` command](http://www.clifford.at/yosys/cmd_json.html). + +Unless you are doing something special you will want to use [the `prep` command](http://www.clifford.at/yosys/cmd_prep.html). Some examples are provided below and you can find some runnable examples which go from Verilog to diagrams in the [examples directory](./examples) (with example Makefile). + +#### Generate top level JSON + +This command will generate a JSON file for a diagram of the top module with all the inner modules shown as boxes. + +``` +yosys -p "prep -top my_top_module; write_json output.json" input.v +``` + +#### Generate logic JSON + +You can give it the `-flatten` argument to [the `prep` command](http://www.clifford.at/yosys/cmd_prep.html) if you want Yosys to convert everything into low level logic. Only basic logic cells and black boxes will exist after flattening. + +``` +yosys -p "prep -top my_top_module -flatten; write_json output.json" input.v +``` + +### Generate AND (or not) and inverter (NOT) JSON + +It is also frequently common that you want to create a JSON file for a diagram which only uses AND and NOT (or NAND and NOT) cells. ([This is called an AIG](https://en.wikipedia.org/wiki/And-inverter_graph).) This can be done with Yosys' [`aigmap` command](http://www.clifford.at/yosys/cmd_proc.html). + +``` +yosys -p "prep -top my_top_module; aigmap; write_json output.json" input.v +``` + +## Configuration file + +The configuration contains multiple options to customize the layout. The default configuration file turns off hierarchy and uses the top defined in the input JSON file. The default configuration file can be found in the [lib directory](./lib/config.json) and looks like this: + +```json +{ + "hierarchy": { + "enable": "off", + "expandLevel": 0, + "expandModules": { + "types": [], + "ids": [] + }, + "colour": ["#e9e9e9"] + }, + "top": { + "enable": false, + "module": "" + } +} +``` + +In the `"hierarchy"` subsection, hierarchical schematics can be enabled and configured. The options contained in this subsection are: +* `"enable"`: In this option the hierarchy can be enabled. There are four different options. `"off"` turns of the hierarchy, `"all"` expands all submodules, `"level"` expands all submodules until a specified level of hierarchy and `"modules"` expands all specified submodules. +* `"expandLevel"`: The maximum level until which submodules are expanded for the `"level"` option. +* `"expandModules"`: The modules which will be expanded for the `"modules"` option. Through the suboptions, it is possible to expand based on the module type using `"types"` and on the module name using `"ids"`. +* `"colour"`: This option defines the background colour used for the submodules. If there are more colours in the list, each colour is used for deeper level of hierarchy. + +The `"top"` subsection can be used to define a different top module. If `"enable"` is true, then the module defined in `"module"` will be used as top module, instead of the top module defined in the input JSON file. + # Examples Here's an digital netlist produced by Yosys along with the diagram that netlistsvg created from it. @@ -280,7 +416,228 @@ Here's an digital netlist produced by Yosys along with the diagram that netlists ``` -![example](https://raw.githubusercontent.com/nturley/netlistsvg/master/doc/up3down5.svg?sanitize=true) +![example](./doc/up3down5.svg?sanitize=true) + +Here's an digital netlist produced by Yosys along with the hierarchical diagram that netlistsvg created from it using a different configuration file. + +
+ JSON Source + +```json +{ + "creator": "Yosys 0.9+1706 (git sha1 e069259a, clang 6.0.0-1ubuntu2 -fPIC -Os)", + "modules": { + "$paramod\\submod\\c=3'111\\d=3'111": { + "attributes": { + "gentb_skip": "00000000000000000000000000000001", + "src": "hierarchy.v:17" + }, + "ports": { + "a": { + "direction": "input", + "bits": [ 2, 3, 4, 5 ] + }, + "b": { + "direction": "input", + "bits": [ 6, 7, 8, 9 ] + }, + "y1": { + "direction": "output", + "bits": [ 2, 3, 4, 5, "0", "0", "0", "0" ] + }, + "y2": { + "direction": "output", + "bits": [ 6, 7, 8, 9, "0", "0", "0", "0" ] + }, + "y3": { + "direction": "output", + "bits": [ "1", "1", "1", "1", "1", "1", "1", "1" ] + }, + "y4": { + "direction": "output", + "bits": [ "1", "1", "1", "1", "1", "1", "1", "1" ] + } + }, + "cells": { + }, + "netnames": { + "a": { + "hide_name": 0, + "bits": [ 2, 3, 4, 5 ], + "attributes": { + "src": "hierarchy.v:20" + } + }, + "b": { + "hide_name": 0, + "bits": [ 6, 7, 8, 9 ], + "attributes": { + "src": "hierarchy.v:20" + } + }, + "y1": { + "hide_name": 0, + "bits": [ 2, 3, 4, 5, "0", "0", "0", "0" ], + "attributes": { + "src": "hierarchy.v:21" + } + }, + "y2": { + "hide_name": 0, + "bits": [ 6, 7, 8, 9, "0", "0", "0", "0" ], + "attributes": { + "src": "hierarchy.v:21" + } + }, + "y3": { + "hide_name": 0, + "bits": [ "1", "1", "1", "1", "1", "1", "1", "1" ], + "attributes": { + "src": "hierarchy.v:21" + } + }, + "y4": { + "hide_name": 0, + "bits": [ "1", "1", "1", "1", "1", "1", "1", "1" ], + "attributes": { + "src": "hierarchy.v:21" + } + } + } + }, + "top": { + "attributes": { + "top": "00000000000000000000000000000001", + "src": "hierarchy.v:3" + }, + "ports": { + "a": { + "direction": "input", + "bits": [ 2, 3, 4, 5 ] + }, + "b": { + "direction": "input", + "bits": [ 6, 7, 8, 9 ] + }, + "y1": { + "direction": "output", + "bits": [ 10, 11, 12, 13, 14, 15, 16, 17 ] + }, + "y2": { + "direction": "output", + "bits": [ 18, 19, 20, 21, 22, 23, 24, 25 ] + }, + "y3": { + "direction": "output", + "bits": [ 26, 27, 28, 29, 30, 31, 32, 33 ] + }, + "y4": { + "direction": "output", + "bits": [ 34, 35, 36, 37, 38, 39, 40, 41 ] + } + }, + "cells": { + "foo": { + "hide_name": 0, + "type": "$paramod\\submod\\c=3'111\\d=3'111", + "parameters": { + }, + "attributes": { + "src": "hierarchy.v:12" + }, + "port_directions": { + "a": "input", + "b": "input", + "y1": "output", + "y2": "output", + "y3": "output", + "y4": "output" + }, + "connections": { + "a": [ 2, 3, 4, 5 ], + "b": [ 6, 7, 8, 9 ], + "y1": [ 10, 11, 12, 13, 14, 15, 16, 17 ], + "y2": [ 18, 19, 20, 21, 22, 23, 24, 25 ], + "y3": [ 26, 27, 28, 29, 30, 31, 32, 33 ], + "y4": [ 34, 35, 36, 37, 38, 39, 40, 41 ] + } + } + }, + "netnames": { + "a": { + "hide_name": 0, + "bits": [ 2, 3, 4, 5 ], + "attributes": { + "src": "hierarchy.v:4" + } + }, + "b": { + "hide_name": 0, + "bits": [ 6, 7, 8, 9 ], + "attributes": { + "src": "hierarchy.v:5" + } + }, + "y1": { + "hide_name": 0, + "bits": [ 10, 11, 12, 13, 14, 15, 16, 17 ], + "attributes": { + "src": "hierarchy.v:6" + } + }, + "y2": { + "hide_name": 0, + "bits": [ 18, 19, 20, 21, 22, 23, 24, 25 ], + "attributes": { + "src": "hierarchy.v:6" + } + }, + "y3": { + "hide_name": 0, + "bits": [ 26, 27, 28, 29, 30, 31, 32, 33 ], + "attributes": { + "src": "hierarchy.v:6" + } + }, + "y4": { + "hide_name": 0, + "bits": [ 34, 35, 36, 37, 38, 39, 40, 41 ], + "attributes": { + "src": "hierarchy.v:6" + } + } + } + } + } +} +``` + +
+ +
+ Configuration file + +```json +{ + "hierarchy": { + "enable": "modules", + "expandLevel": 0, + "expandModules": { + "types": [], + "ids": ["foo"] + }, + "colour": ["#e9e9e9"] + }, + "top": { + "enable": false, + "module": "" + } +} +``` + +
+ +![example](./doc/hierarchy.svg?sanitize=true) You can also write out the JSON by hand, of course. We support [JSON5](https://json5.org) syntax. @@ -433,113 +790,8 @@ Here's an analog example. ``` -![example](https://raw.githubusercontent.com/nturley/netlistsvg/master/doc/and.svg?sanitize=true) - -## Skin File -It pulls the node icons and configuration options from a SVG skin file. This our default digital skin file. - - +![example](./doc/and.svg?sanitize=true) -This is our analog skin file. - - - -A skin file can use style tags or inline CSS to style the elements. That will be copied onto the output file. A skin file also defines a library of components to use. Each component has an alias list. It will use that component as a template for any cell with that type that it encounters. Each component defines the position and id of each of its ports so we know where to attach the wires to. - -For example, here is a mux definition. It has two aliases: "$pmux" and "$mux". It defines a type name, and a width and height, as well as the position and id of each of it's ports. In general you can rearrange them however you like, and add whatever SVG elements you like inside the template. - -```XML - - - - - - - - - - - -``` - -In addition to the library of components that are matched to cells, a skin file defines some special nodes. Input/Output ports, constants, Splits/Joins, and the generic node. Splits/Joins and the generic nodes are resized and ports are added or removed to adjust to the cell. - -The elkjs layout properties are also defined in the skin file. - -```XML - -``` -Any properties specified here will get passed along to the layout engine. Node and edge properties aren't configurable (yet). - -## Input JSON -Yosys JSON includes more information than we need. We only render one module (either the first or the module with an attribute "top"). If the cell name matches one of the aliases of a template from the skin, then it will use it as a template for the SVG file. Port directions are optional for cells that are defined in the skin (not generic cells). - -So it should look something like this. -```json -{ - "modules": { - "": { - "ports": { - "": { - "direction": "": { - "type": "": "", - ... - }, - "connections": { - "": [ 3, "0", ... ], - ... - } - }, - ... - } - } -} -``` - -## ElkJS -ELK is using a layered approach (Sugiyama, Ganser), similar to dot in the Graphviz package. You can read about their algorithm here: https://rtsys.informatik.uni-kiel.de/%7Ebiblio/downloads/papers/jvlc13.pdf # Status We are getting close to the 1.0 release. At that point, the skin file format will be considered specified and breaking changes will only happen on major version bumps. - -## Generating `input_json_file` with Yosys - -[Yosys from Clifford Wolf](https://github.com/cliffordwolf/yosys) can be used to generate the `input_json_file` using [the `write_json` command](http://www.clifford.at/yosys/cmd_json.html). - -Unless you are doing something special you will want to use [the `prep` command](http://www.clifford.at/yosys/cmd_prep.html). Some examples are provided below and you can find some runnable examples which go from Verilog to diagrams in the [examples directory](./examples) (with example Makefile). - -#### Generate top level diagram - -This command will generate a diagram of the top module with all the inner modules shown as boxes. - -``` -yosys -p "prep -top my_top_module; write_json output.json" input.v -``` - -#### Generate logic diagram - -You can give it the `-flatten` argument to [the `prep` command](http://www.clifford.at/yosys/cmd_prep.html) if you want Yosys to convert everything into low level logic. Only basic logic cells and black boxes will exist after flattening. - -``` -yosys -p "prep -top my_top_module -flatten; write_json output.json" input.v -``` - -### Generate AND (or not) and inverter (NOT) diagram - -It is also frequently common that you want to create a diagram only using AND and NOT (or NAND and NOT) cells. ([This is called an AIG](https://en.wikipedia.org/wiki/And-inverter_graph).) This can be done with Yosys' [`aigmap` command](http://www.clifford.at/yosys/cmd_proc.html). - -``` -yosys -p "prep -top my_top_module; aigmap; write_json output.json" input.v -``` diff --git a/bin/netlistsvg.js b/bin/netlistsvg.js index bc2b277..08e1478 100644 --- a/bin/netlistsvg.js +++ b/bin/netlistsvg.js @@ -14,7 +14,7 @@ require('ajv-errors')(ajv); if (require.main === module) { var argv = yargs .demand(1) - .usage('usage: $0 input_json_file [-o output_svg_file] [--skin skin_file] [--layout elk_json_file] [--config config_json_file') + .usage('usage: $0 input_json_file [-o output_svg_file] [--skin skin_file] [--layout elk_json_file] [--config config_json_file]') .argv; main(argv._[0], argv.o, argv.skin, argv.layout, argv.config); } diff --git a/doc/hierarchy.svg b/doc/hierarchy.svg new file mode 100644 index 0000000..1e90275 --- /dev/null +++ b/doc/hierarchy.svg @@ -0,0 +1,142 @@ + + + + $paramod\submod\c=3'111\d=3'111 + + + 0x0 + + + + + + 0xff + + + + + + + + + + 0:3 + + + 4:7 + + + + + + + + 0:3 + + + 4:7 + + + + + + + + + + + + + + + + + + + + a + + + b + + + y1 + + + y2 + + + y3 + + + y4 + + + + a + + + + + + b + + + + + + y1 + + + + + + y2 + + + + + + y3 + + + + + + y4 + + + + + + + + + + + + + + + + + From 0f2b4a781fbfd7445d26c32c4936c92c440c885d Mon Sep 17 00:00:00 2001 From: djwubs Date: Fri, 8 May 2020 15:01:14 +0200 Subject: [PATCH 30/32] Hierarchy example and readme update --- doc/and.svg | 68 ++++--- doc/up3down5.svg | 352 ++++++++++++++++++------------------ examples/Makefile | 7 +- examples/config/config.json | 15 ++ examples/hierarchy.v | 23 +++ lib/analog.svg | 38 ++-- test/digital/up3down5.json | 243 +------------------------ test/test-all.js | 8 +- 8 files changed, 281 insertions(+), 473 deletions(-) create mode 100644 examples/config/config.json create mode 100644 examples/hierarchy.v diff --git a/doc/and.svg b/doc/and.svg index 7d3392d..349b404 100644 --- a/doc/and.svg +++ b/doc/and.svg @@ -33,7 +33,7 @@ R1 10k - + @@ -42,7 +42,7 @@ R2 10k - + @@ -50,7 +50,7 @@ Q1 - + @@ -63,7 +63,7 @@ R3 10k - + @@ -72,7 +72,7 @@ R4 10k - + @@ -81,7 +81,7 @@ R5 10k - + @@ -89,7 +89,7 @@ Q2 - + @@ -99,17 +99,15 @@ - VCC - + - VCC - + @@ -127,47 +125,47 @@ A - + B - + A AND B - + - - - - - - + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/doc/up3down5.svg b/doc/up3down5.svg index 3d72cb6..376a6ee 100644 --- a/doc/up3down5.svg +++ b/doc/up3down5.svg @@ -1,4 +1,4 @@ - + - + - + - + - + - + - + - + - + + + - + - + + + - + - + + + - + - + + + - + - + - + - + - + - + - + - + - + - + - + - + - + clock - + - + data_in - + - + up - + - + down - + - + carry_out - + - + borrow_out - + - + count_out - + - + parity_out - + - + 11 - + - + 10 - + - + 01 - + - + 00 - + 101 - + - + - + 0:8 - + 9:17 - + 18:26 - + - + 0 - + 1 - + 2 - + - + 0 - + 1 - + - - + + 9 - + 0:8 - + - - + + 9 - + 0:8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Makefile b/examples/Makefile index 6b0178c..580ecf4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -19,11 +19,12 @@ endif NETLISTSVG = ../bin/netlistsvg NETLISTSVG_SKIN ?= ../lib/default.svg NETLISTSVG_DPI ?= 300 +NETLISTSVG_CONFIG = ./config/config.json # Simple files are the same flattened as not SIMPLE_FILES=dff.v muxcy.v xorcy.v # Complex files are different when flattened -COMPLEX_FILES=carry4bits.v carry4whole.v +COMPLEX_FILES=carry4bits.v carry4whole.v hierarchy.v ALL_TARGETS= \ $(foreach v,$(SIMPLE_FILES) ,$(basename $(v)).simple.all) \ @@ -57,7 +58,7 @@ GET_TOP ?= export TOP=$$(echo $(basename $<) | tr a-z A-Z); # Use netlistsvg to generate SVG files %.svg: %.json $(NETLISTSVG_SKIN) - $(NODE) $(NETLISTSVG) $< -o $@ --skin $(NETLISTSVG_SKIN) + $(NODE) $(NETLISTSVG) $< -o $@ --skin $(NETLISTSVG_SKIN) --config $(NETLISTSVG_CONFIG) # Use inkscape to render the SVG files into PNG files. %.png: %.svg @@ -91,5 +92,5 @@ all: make view.all .DEFAULT_GOAL := all -.PRECIOUS: %.png +.PRECIOUS: %.png config.json .PHONY: view clean all diff --git a/examples/config/config.json b/examples/config/config.json new file mode 100644 index 0000000..17a2e86 --- /dev/null +++ b/examples/config/config.json @@ -0,0 +1,15 @@ +{ + "hierarchy": { + "enable": "modules", + "expandLevel": 0, + "expandModules": { + "types": [], + "ids": ["foo"] + }, + "colour": ["#e9e9e9"] + }, + "top": { + "enable": false, + "module": "" + } +} diff --git a/examples/hierarchy.v b/examples/hierarchy.v new file mode 100644 index 0000000..097c079 --- /dev/null +++ b/examples/hierarchy.v @@ -0,0 +1,23 @@ + +(* top *) +module HIERARCHY(a, b, y1, y2, y3, y4); +input [3:0] a; +input signed [3:0] b; +output [7:0] y1, y2, y3, y4; + +submod #(-3'sd1, -3'sd1) foo (a, b, y1, y2, y3, y4); + +endmodule + +(* gentb_skip *) +module submod(a, b, y1, y2, y3, y4); +parameter c = 0; +parameter [7:0] d = 0; +input [3:0] a, b; +output [7:0] y1, y2, y3, y4; +assign y1 = a; +assign y2 = b; +assign y3 = c; +assign y4 = d; +endmodule + diff --git a/lib/analog.svg b/lib/analog.svg index af51aba..22a30a4 100644 --- a/lib/analog.svg +++ b/lib/analog.svg @@ -48,14 +48,14 @@ text { name - + name - + @@ -71,14 +71,14 @@ text { input - + output - + @@ -88,7 +88,7 @@ text { X1 Xk - + @@ -98,7 +98,7 @@ text { X1 Xk - + @@ -150,7 +150,7 @@ text { X1 XV - + @@ -161,7 +161,7 @@ text { X1 XA - + @@ -173,7 +173,7 @@ text { X1 - + @@ -182,7 +182,7 @@ text { X1 - + @@ -191,7 +191,7 @@ text { X1 - + @@ -202,7 +202,7 @@ text { X1 - + @@ -213,7 +213,7 @@ text { X1 - + @@ -227,7 +227,7 @@ text { X1 - + @@ -243,7 +243,7 @@ text { X1 - + @@ -257,7 +257,7 @@ text { X1 - + @@ -272,7 +272,7 @@ text { generic - + out0 @@ -296,7 +296,7 @@ text { X1 - + @@ -310,7 +310,7 @@ text { X1 - + diff --git a/test/digital/up3down5.json b/test/digital/up3down5.json index f548080..c483714 100644 --- a/test/digital/up3down5.json +++ b/test/digital/up3down5.json @@ -1,5 +1,4 @@ { - "creator": "Yosys 0.5+220 (git sha1 94fbaff, emcc -Os)", "modules": { "up3down5": { "ports": { @@ -38,18 +37,7 @@ }, "cells": { "$add$input.v:17$3": { - "hide_name": 1, "type": "$add", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 9, - "B_SIGNED": 0, - "B_WIDTH": 2, - "Y_WIDTH": 10 - }, - "attributes": { - "src": "input.v:17" - }, "port_directions": { "A": "input", "B": "input", @@ -62,18 +50,7 @@ } }, "$and$input.v:28$5": { - "hide_name": 1, "type": "$and", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 1, - "B_SIGNED": 0, - "B_WIDTH": 1, - "Y_WIDTH": 1 - }, - "attributes": { - "src": "input.v:28" - }, "port_directions": { "A": "input", "B": "input", @@ -86,18 +63,7 @@ } }, "$and$input.v:29$6": { - "hide_name": 1, "type": "$and", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 1, - "B_SIGNED": 0, - "B_WIDTH": 1, - "Y_WIDTH": 1 - }, - "attributes": { - "src": "input.v:29" - }, "port_directions": { "A": "input", "B": "input", @@ -110,15 +76,7 @@ } }, "$procdff$40": { - "hide_name": 1, "type": "$dff", - "parameters": { - "CLK_POLARITY": 1, - "WIDTH": 9 - }, - "attributes": { - "src": "input.v:14" - }, "port_directions": { "CLK": "input", "D": "input", @@ -131,15 +89,7 @@ } }, "$procdff$41": { - "hide_name": 1, "type": "$dff", - "parameters": { - "CLK_POLARITY": 1, - "WIDTH": 1 - }, - "attributes": { - "src": "input.v:14" - }, "port_directions": { "CLK": "input", "D": "input", @@ -152,15 +102,7 @@ } }, "$procdff$42": { - "hide_name": 1, "type": "$dff", - "parameters": { - "CLK_POLARITY": 1, - "WIDTH": 1 - }, - "attributes": { - "src": "input.v:14" - }, "port_directions": { "CLK": "input", "D": "input", @@ -173,15 +115,7 @@ } }, "$procdff$43": { - "hide_name": 1, "type": "$dff", - "parameters": { - "CLK_POLARITY": 1, - "WIDTH": 1 - }, - "attributes": { - "src": "input.v:14" - }, "port_directions": { "CLK": "input", "D": "input", @@ -194,14 +128,7 @@ } }, "$procmux$36": { - "hide_name": 1, "type": "$pmux", - "parameters": { - "S_WIDTH": 3, - "WIDTH": 9 - }, - "attributes": { - }, "port_directions": { "A": "input", "B": "input", @@ -216,17 +143,7 @@ } }, "$procmux$37_CMP0": { - "hide_name": 1, "type": "$eq", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 2, - "B_SIGNED": 0, - "B_WIDTH": 2, - "Y_WIDTH": 1 - }, - "attributes": { - }, "port_directions": { "A": "input", "B": "input", @@ -239,17 +156,7 @@ } }, "$procmux$38_CMP0": { - "hide_name": 1, "type": "$eq", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 2, - "B_SIGNED": 0, - "B_WIDTH": 2, - "Y_WIDTH": 1 - }, - "attributes": { - }, "port_directions": { "A": "input", "B": "input", @@ -262,17 +169,7 @@ } }, "$procmux$39_CMP0": { - "hide_name": 1, "type": "$eq", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 2, - "B_SIGNED": 0, - "B_WIDTH": 2, - "Y_WIDTH": 1 - }, - "attributes": { - }, "port_directions": { "A": "input", "B": "input", @@ -285,16 +182,7 @@ } }, "$reduce_xor$input.v:27$4": { - "hide_name": 1, "type": "$reduce_xor", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 9, - "Y_WIDTH": 1 - }, - "attributes": { - "src": "input.v:27" - }, "port_directions": { "A": "input", "Y": "output" @@ -305,18 +193,7 @@ } }, "$sub$input.v:16$2": { - "hide_name": 1, "type": "$sub", - "parameters": { - "A_SIGNED": 0, - "A_WIDTH": 9, - "B_SIGNED": 0, - "B_WIDTH": 3, - "Y_WIDTH": 10 - }, - "attributes": { - "src": "input.v:16" - }, "port_directions": { "A": "input", "B": "input", @@ -328,125 +205,7 @@ "Y": [ 49, 50, 51, 52, 53, 54, 55, 56, 57, 37 ] } } - }, - "netnames": { - "$0\\borrow_out[0:0]": { - "hide_name": 1, - "bits": [ 38 ], - "attributes": { - "src": "input.v:14" - } - }, - "$0\\carry_out[0:0]": { - "hide_name": 1, - "bits": [ 36 ], - "attributes": { - "src": "input.v:14" - } - }, - "$0\\cnt_dn[9:0]": { - "hide_name": 1, - "bits": [ 49, 50, 51, 52, 53, 54, 55, 56, 57, 37 ], - "attributes": { - "src": "input.v:14" - } - }, - "$0\\cnt_up[9:0]": { - "hide_name": 1, - "bits": [ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 ], - "attributes": { - "src": "input.v:14" - } - }, - "$0\\count_out[8:0]": { - "hide_name": 1, - "bits": [ 39, 40, 41, 42, 43, 44, 45, 46, 47 ], - "attributes": { - "src": "input.v:14" - } - }, - "$0\\parity_out[0:0]": { - "hide_name": 1, - "bits": [ 48 ], - "attributes": { - "src": "input.v:14" - } - }, - "$procmux$37_CMP": { - "hide_name": 1, - "bits": [ 58 ], - "attributes": { - } - }, - "$procmux$38_CMP": { - "hide_name": 1, - "bits": [ 59 ], - "attributes": { - } - }, - "$procmux$39_CMP": { - "hide_name": 1, - "bits": [ 60 ], - "attributes": { - } - }, - "borrow_out": { - "hide_name": 0, - "bits": [ 15 ], - "attributes": { - "src": "input.v:9" - } - }, - "carry_out": { - "hide_name": 0, - "bits": [ 14 ], - "attributes": { - "src": "input.v:9" - } - }, - "clock": { - "hide_name": 0, - "bits": [ 2 ], - "attributes": { - "src": "input.v:6" - } - }, - "count_out": { - "hide_name": 0, - "bits": [ 16, 17, 18, 19, 20, 21, 22, 23, 24 ], - "attributes": { - "src": "input.v:8" - } - }, - "data_in": { - "hide_name": 0, - "bits": [ 3, 4, 5, 6, 7, 8, 9, 10, 11 ], - "attributes": { - "src": "input.v:5" - } - }, - "down": { - "hide_name": 0, - "bits": [ 13 ], - "attributes": { - "src": "input.v:6" - } - }, - "parity_out": { - "hide_name": 0, - "bits": [ 25 ], - "attributes": { - "src": "input.v:9" - } - }, - "up": { - "hide_name": 0, - "bits": [ 12 ], - "attributes": { - "src": "input.v:6" - } - } } } } -} \ No newline at end of file +} diff --git a/test/test-all.js b/test/test-all.js index aab8349..e683764 100644 --- a/test/test-all.js +++ b/test/test-all.js @@ -8,13 +8,17 @@ for (var test of digital_tests) { bin.main( path.join('test', 'digital', test + '.json'), path.join('test', 'digital', test + '.svg'), - path.join('lib', 'default.svg') + path.join('lib', 'default.svg'), + null, + null ); } for (var test of analog_tests) { bin.main( path.join('test', 'analog', test + '.json'), path.join('test', 'analog', test + '.svg'), - path.join('lib', 'analog.svg') + path.join('lib', 'analog.svg'), + null, + null ); } From 74424cfb7a1972cd7d2c52eda18794a7778aaace Mon Sep 17 00:00:00 2001 From: djwubs Date: Sat, 9 May 2020 10:39:20 +0200 Subject: [PATCH 31/32] Readme reordering --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 08790c3..f65efc5 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ netlistsvg.render(netlistsvg.digitalSkin, netlistsvg.exampleDigital, (err, resul To turn Verilog into YosysJSON in the browser, you can use [YosysJS](http://www.clifford.at/yosys/yosysjs.html) ## Skin File -It pulls the node icons and configuration options from a SVG skin file. This our default digital skin file. +The node icons and elkjs layout properties are obtained from a SVG skin file. This our default digital skin file. @@ -105,7 +105,7 @@ ELK is using a layered approach (Sugiyama, Ganser), similar to dot in the Graphv ## Input JSON Yosys JSON includes more information than we need. Only the modules specified in the configuration file are rendered. If the cell name matches one of the aliases of a template from the skin, then it will use it as a template for the SVG file. Port directions are optional for cells that are defined in the skin (not generic cells). -It should look something like this: +You can also write out the JSON by hand, of course. We support [JSON5](https://json5.org) syntax. The input JSON should look something like this: ```json { "modules": { @@ -157,7 +157,7 @@ You can give it the `-flatten` argument to [the `prep` command](http://www.clif yosys -p "prep -top my_top_module -flatten; write_json output.json" input.v ``` -### Generate AND (or not) and inverter (NOT) JSON +#### Generate AND (or not) and inverter (NOT) JSON It is also frequently common that you want to create a JSON file for a diagram which only uses AND and NOT (or NAND and NOT) cells. ([This is called an AIG](https://en.wikipedia.org/wiki/And-inverter_graph).) This can be done with Yosys' [`aigmap` command](http://www.clifford.at/yosys/cmd_proc.html). @@ -639,9 +639,7 @@ Here's an digital netlist produced by Yosys along with the hierarchical diagram ![example](./doc/hierarchy.svg?sanitize=true) -You can also write out the JSON by hand, of course. We support [JSON5](https://json5.org) syntax. - -Here's an analog example. +Here is an analog example.
JSON Source From 40a009bc250780bb59cb80f70050eacd9f90d550 Mon Sep 17 00:00:00 2001 From: djwubs Date: Sun, 31 May 2020 16:46:11 +0200 Subject: [PATCH 32/32] Moving colour options to svg file --- built/Cell.js | 22 ++++++++++------------ built/FlatModule.js | 13 +++---------- built/Port.js | 15 ++++++++------- built/Skin.js | 29 +++++++++++++++++++++-------- lib/Cell.ts | 22 ++++++++++------------ lib/ConfigModel.ts | 1 - lib/FlatModule.ts | 12 +++--------- lib/Port.ts | 15 ++++++++------- lib/Skin.ts | 27 +++++++++++++++++++-------- lib/default.svg | 40 ++++++++++++++++++++++++++++++++++++++-- 10 files changed, 120 insertions(+), 76 deletions(-) diff --git a/built/Cell.js b/built/Cell.js index 99f0de8..0ae6807 100644 --- a/built/Cell.js +++ b/built/Cell.js @@ -21,10 +21,10 @@ var elkGraph_1 = require("./elkGraph"); var clone = require("clone"); var onml = require("onml"); var Cell = /** @class */ (function () { - function Cell(key, type, inputPorts, outputPorts, attributes, parent, subModule, subColour) { + function Cell(key, type, inputPorts, outputPorts, attributes, parent, subModule, depth) { var _this = this; if (subModule === void 0) { subModule = null; } - if (subColour === void 0) { subColour = null; } + if (depth === void 0) { depth = null; } this.key = key; this.type = type; this.inputPorts = inputPorts; @@ -32,7 +32,7 @@ var Cell = /** @class */ (function () { this.attributes = attributes || {}; this.parent = parent; this.subModule = subModule; - this.colour = subColour; + this.depth = depth; inputPorts.forEach(function (ip) { ip.parentNode = _this; }); @@ -104,7 +104,7 @@ var Cell = /** @class */ (function () { }); return new Cell('$split$' + source, '$_split_', inPorts, splitOutPorts, {}, parent); }; - Cell.createSubModule = function (yCell, name, parent, subModule, depth, colour) { + Cell.createSubModule = function (yCell, name, parent, subModule, depth) { var template = Skin_1.default.findSkinType(yCell.type); var templateInputPids = Skin_1.default.getInputPids(template); var templateOutputPids = Skin_1.default.getOutputPids(template); @@ -120,7 +120,7 @@ var Cell = /** @class */ (function () { outputPorts = ports.filter(function (port) { return port.keyIn(outputPids_2); }); } var mod = new FlatModule_1.FlatModule(subModule, name, depth + 1, parent); - return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod, colour); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod, depth); }; Object.defineProperty(Cell.prototype, "Type", { get: function () { @@ -197,7 +197,7 @@ var Cell = /** @class */ (function () { return null; }; Cell.prototype.getTemplate = function () { - return Skin_1.default.findSkinType(this.type); + return Skin_1.default.findSkinType(this.type, this.depth); }; Cell.prototype.buildElkChild = function () { var _this = this; @@ -221,7 +221,7 @@ var Cell = /** @class */ (function () { } if (type === 'join' || type === 'split' || - (type === 'generic' && this.subModule === null)) { + type === 'generic') { var inTemplates_1 = Skin_1.default.getPortsWithPrefix(template, 'in'); var outTemplates_1 = Skin_1.default.getPortsWithPrefix(template, 'out'); var inPorts = this.inputPorts.map(function (ip, i) { @@ -253,7 +253,7 @@ var Cell = /** @class */ (function () { this.addLabels(template, cell); return cell; } - if (type === 'generic' && this.subModule !== null) { + if (type === 'sub_odd' || type === 'sub_even') { var inTemplates_2 = Skin_1.default.getPortsWithPrefix(template, 'in'); var outTemplates_2 = Skin_1.default.getPortsWithPrefix(template, 'out'); var inPorts_1 = this.inputPorts.map(function (ip, i) { @@ -425,7 +425,7 @@ var Cell = /** @class */ (function () { tempclone.push(portClone); }); } - else if (template[1]['s:type'] === 'generic' && this.subModule === null) { + else if (template[1]['s:type'] === 'generic') { setGenericSize(tempclone, Number(this.getGenericHeight())); var inPorts_3 = Skin_1.default.getPortsWithPrefix(template, 'in'); var ingap_1 = Number(inPorts_3[1][1]['s:y']) - Number(inPorts_3[0][1]['s:y']); @@ -456,12 +456,10 @@ var Cell = /** @class */ (function () { // first child of generic must be a text node. tempclone[2][2] = this.type; } - else if (template[1]['s:type'] === 'generic' && this.subModule !== null) { + else if (template[1]['s:type'] === 'sub_odd' || template[1]['s:type'] === 'sub_even') { var subModule = drawModule_1.drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; - tempclone[3][1].fill = this.colour; - tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; tempclone.pop(); diff --git a/built/FlatModule.js b/built/FlatModule.js index f9560c2..9a8f1e8 100644 --- a/built/FlatModule.js +++ b/built/FlatModule.js @@ -9,20 +9,13 @@ var FlatModule = /** @class */ (function () { if (parent === void 0) { parent = null; } this.parent = parent; this.moduleName = name; - var colour; - if (FlatModule.config.hierarchy.colour[depth]) { - colour = FlatModule.config.hierarchy.colour[depth]; - } - else { - colour = FlatModule.config.hierarchy.colour[FlatModule.config.hierarchy.colour.length - 1]; - } var ports = _.map(mod.ports, function (port, portName) { return Cell_1.default.fromPort(port, portName, _this.moduleName); }); var cells = _.map(mod.cells, function (c, key) { switch (FlatModule.config.hierarchy.enable) { case 'level': { if (FlatModule.config.hierarchy.expandLevel > depth) { if (_.includes(FlatModule.modNames, c.type)) { - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth, colour); + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); } else { return Cell_1.default.fromYosysCell(c, key, _this.moduleName); @@ -34,7 +27,7 @@ var FlatModule = /** @class */ (function () { } case 'all': { if (_.includes(FlatModule.modNames, c.type)) { - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth, colour); + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); } else { return Cell_1.default.fromYosysCell(c, key, _this.moduleName); @@ -46,7 +39,7 @@ var FlatModule = /** @class */ (function () { if (!_.includes(FlatModule.modNames, c.type)) { throw new Error('Submodule in config file not defined in input json file.'); } - return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth, colour); + return Cell_1.default.createSubModule(c, key, _this.moduleName, FlatModule.netlist.modules[c.type], depth); } else { return Cell_1.default.fromYosysCell(c, key, _this.moduleName); diff --git a/built/Port.js b/built/Port.js index 2c1ff71..16c7a93 100644 --- a/built/Port.js +++ b/built/Port.js @@ -61,7 +61,7 @@ var Port = /** @class */ (function () { x: Number(templatePorts[0][1]['s:x']), y: Number(templatePorts[0][1]['s:y']), }; - if ((type === 'generic' || type === 'join') && dir === 'in') { + if ((type === 'generic' || type === 'sub_even' || type === 'sub_odd' || type === 'join') && dir === 'in') { ret.labels = [{ id: nkey + '.' + this.key + '.label', text: this.key, @@ -70,11 +70,12 @@ var Port = /** @class */ (function () { width: (6 * this.key.length), height: 11, }]; - if (type === 'generic') { + if (type === 'sub_even' || type === 'sub_odd') { ret.layoutOptions = { 'org.eclipse.elk.port.side': 'WEST' }; } } - if ((type === 'generic' || type === 'split') && dir === 'out') { + if ((type === 'generic' || type === 'sub_even' || type === 'sub_odd' || type === 'split') + && dir === 'out') { ret.labels = [{ id: nkey + '.' + this.key + '.label', text: this.key, @@ -83,11 +84,11 @@ var Port = /** @class */ (function () { width: (6 * this.key.length), height: 11, }]; - if (type === 'generic') { + if (type === 'sub_even' || type === 'sub_odd') { ret.layoutOptions = { 'org.eclipse.elk.port.side': 'EAST' }; } } - if (type === 'generic' && this.parentNode.subModule !== null) { + if (type === 'sub_even' || type === 'sub_odd') { delete ret.x; delete ret.y; } @@ -102,7 +103,7 @@ var Port = /** @class */ (function () { x: Number(templatePorts[0][1]['s:x']), y: (index) * gap + Number(templatePorts[0][1]['s:y']), }; - if (type === 'generic') { + if (type === 'generic' || type === 'sub_even' || type === 'sub_odd') { ret.labels = [{ id: nkey + '.' + this.key + '.label', text: this.key, @@ -118,7 +119,7 @@ var Port = /** @class */ (function () { ret.layoutOptions = { 'org.eclipse.elk.port.side': 'EAST' }; } } - if (type === 'generic' && this.parentNode.subModule !== null) { + if (type === 'sub_even' || type === 'sub_odd') { delete ret.x; delete ret.y; } diff --git a/built/Skin.js b/built/Skin.js index 8eee736..a07101d 100644 --- a/built/Skin.js +++ b/built/Skin.js @@ -58,7 +58,8 @@ var Skin; }); } Skin.getLateralPortPids = getLateralPortPids; - function findSkinType(type) { + function findSkinType(type, depth) { + if (depth === void 0) { depth = null; } var ret = null; onml.traverse(Skin.skin, { enter: function (node, parent) { @@ -68,13 +69,25 @@ var Skin; }, }); if (ret == null) { - onml.traverse(Skin.skin, { - enter: function (node) { - if (node.attr['s:type'] === 'generic') { - ret = node; - } - }, - }); + if (depth == null) { + onml.traverse(Skin.skin, { + enter: function (node) { + if (node.attr['s:type'] === 'generic') { + ret = node; + } + }, + }); + } + else { + var sub_1 = ['sub_odd', 'sub_even']; + onml.traverse(Skin.skin, { + enter: function (node) { + if (node.attr['s:type'] === sub_1[depth % 2]) { + ret = node; + } + }, + }); + } } return ret.full; } diff --git a/lib/Cell.ts b/lib/Cell.ts index 7d3f563..cec9071 100644 --- a/lib/Cell.ts +++ b/lib/Cell.ts @@ -79,7 +79,7 @@ export default class Cell { } public static createSubModule(yCell: Yosys.Cell, name: string, parent: string, - subModule: Yosys.Module, depth: number, colour: string): Cell { + subModule: Yosys.Module, depth: number): Cell { const template = Skin.findSkinType(yCell.type); const templateInputPids = Skin.getInputPids(template); const templateOutputPids = Skin.getOutputPids(template); @@ -95,12 +95,12 @@ export default class Cell { outputPorts = ports.filter((port) => port.keyIn(outputPids)); } const mod = new FlatModule(subModule, name, depth + 1, parent); - return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod, colour); + return new Cell(name, yCell.type, inputPorts, outputPorts, yCell.attributes, parent, mod, depth); } public parent: string; public subModule: FlatModule; - public colour: string; + public depth: number; protected key: string; protected type: string; protected inputPorts: Port[]; @@ -114,7 +114,7 @@ export default class Cell { attributes: Yosys.CellAttributes, parent: string, subModule: FlatModule = null, - subColour: string = null) { + depth: number = null) { this.key = key; this.type = type; this.inputPorts = inputPorts; @@ -122,7 +122,7 @@ export default class Cell { this.attributes = attributes || {}; this.parent = parent; this.subModule = subModule; - this.colour = subColour; + this.depth = depth; inputPorts.forEach((ip) => { ip.parentNode = this; }); @@ -202,7 +202,7 @@ export default class Cell { } public getTemplate(): any { - return Skin.findSkinType(this.type); + return Skin.findSkinType(this.type, this.depth); } public buildElkChild(): ElkModel.Cell { @@ -226,7 +226,7 @@ export default class Cell { } if (type === 'join' || type === 'split' || - (type === 'generic' && this.subModule === null)) { + type === 'generic') { const inTemplates: any[] = Skin.getPortsWithPrefix(template, 'in'); const outTemplates: any[] = Skin.getPortsWithPrefix(template, 'out'); const inPorts = this.inputPorts.map((ip, i) => @@ -256,7 +256,7 @@ export default class Cell { this.addLabels(template, cell); return cell; } - if (type === 'generic' && this.subModule !== null) { + if (type === 'sub_odd' || type === 'sub_even') { const inTemplates: any[] = Skin.getPortsWithPrefix(template, 'in'); const outTemplates: any[] = Skin.getPortsWithPrefix(template, 'out'); const inPorts = this.inputPorts.map((ip, i) => @@ -422,7 +422,7 @@ export default class Cell { + (startY + i * gap) + ')'; tempclone.push(portClone); }); - } else if (template[1]['s:type'] === 'generic' && this.subModule === null) { + } else if (template[1]['s:type'] === 'generic') { setGenericSize(tempclone, Number(this.getGenericHeight())); const inPorts = Skin.getPortsWithPrefix(template, 'in'); const ingap = Number(inPorts[1][1]['s:y']) - Number(inPorts[0][1]['s:y']); @@ -452,12 +452,10 @@ export default class Cell { }); // first child of generic must be a text node. tempclone[2][2] = this.type; - } else if (template[1]['s:type'] === 'generic' && this.subModule !== null) { + } else if (template[1]['s:type'] === 'sub_odd' || template[1]['s:type'] === 'sub_even') { const subModule = drawSubModule(cell, this.subModule); tempclone[3][1].width = subModule[1].width; tempclone[3][1].height = subModule[1].height; - tempclone[3][1].fill = this.colour; - tempclone[3][1].rx = '4'; tempclone[2][1].x = tempclone[3][1].width / 2; tempclone[2][2] = this.type; tempclone.pop(); diff --git a/lib/ConfigModel.ts b/lib/ConfigModel.ts index 727e192..3a74376 100644 --- a/lib/ConfigModel.ts +++ b/lib/ConfigModel.ts @@ -7,7 +7,6 @@ interface Hierarchy { enable: 'off' | 'level' | 'all' | 'modules'; expandLevel: number; expandModules: ExpandModules; - colour: string[]; } interface ExpandModules { diff --git a/lib/FlatModule.ts b/lib/FlatModule.ts index fd8f59c..923c65d 100644 --- a/lib/FlatModule.ts +++ b/lib/FlatModule.ts @@ -59,12 +59,6 @@ export class FlatModule { constructor(mod: Yosys.Module, name: string, depth: number, parent: string = null) { this.parent = parent; this.moduleName = name; - let colour: string; - if (FlatModule.config.hierarchy.colour[depth]) { - colour = FlatModule.config.hierarchy.colour[depth]; - } else { - colour = FlatModule.config.hierarchy.colour[FlatModule.config.hierarchy.colour.length - 1]; - } const ports = _.map(mod.ports, (port, portName) => Cell.fromPort(port, portName, this.moduleName)); const cells = _.map(mod.cells, (c, key) => { switch (FlatModule.config.hierarchy.enable) { @@ -72,7 +66,7 @@ export class FlatModule { if (FlatModule.config.hierarchy.expandLevel > depth) { if (_.includes(FlatModule.modNames, c.type)) { return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], - depth, colour); + depth); } else { return Cell.fromYosysCell(c, key, this.moduleName); } @@ -83,7 +77,7 @@ export class FlatModule { case 'all': { if (_.includes(FlatModule.modNames, c.type)) { return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], - depth, colour); + depth); } else { return Cell.fromYosysCell(c, key, this.moduleName); } @@ -95,7 +89,7 @@ export class FlatModule { throw new Error('Submodule in config file not defined in input json file.'); } return Cell.createSubModule(c, key, this.moduleName, FlatModule.netlist.modules[c.type], - depth, colour); + depth); } else { return Cell.fromYosysCell(c, key, this.moduleName); } diff --git a/lib/Port.ts b/lib/Port.ts index a859f03..59de4e7 100644 --- a/lib/Port.ts +++ b/lib/Port.ts @@ -87,7 +87,7 @@ export class Port { y: Number(templatePorts[0][1]['s:y']), }; - if ((type === 'generic' || type === 'join') && dir === 'in') { + if ((type === 'generic' || type === 'sub_even' || type === 'sub_odd' || type === 'join') && dir === 'in') { ret.labels = [{ id: nkey + '.' + this.key + '.label', text: this.key, @@ -96,12 +96,13 @@ export class Port { width: (6 * this.key.length), height: 11, }]; - if (type === 'generic') { + if (type === 'sub_even' || type === 'sub_odd') { ret.layoutOptions = {'org.eclipse.elk.port.side': 'WEST'}; } } - if ((type === 'generic' || type === 'split') && dir === 'out') { + if ((type === 'generic' || type === 'sub_even' || type === 'sub_odd' || type === 'split') + && dir === 'out') { ret.labels = [{ id: nkey + '.' + this.key + '.label', text: this.key, @@ -110,12 +111,12 @@ export class Port { width: (6 * this.key.length), height: 11, }]; - if (type === 'generic') { + if (type === 'sub_even' || type === 'sub_odd') { ret.layoutOptions = {'org.eclipse.elk.port.side': 'EAST'}; } } - if (type === 'generic' && this.parentNode.subModule !== null) { + if (type === 'sub_even' || type === 'sub_odd') { delete ret.x; delete ret.y; } @@ -129,7 +130,7 @@ export class Port { x: Number(templatePorts[0][1]['s:x']), y: (index) * gap + Number(templatePorts[0][1]['s:y']), }; - if (type === 'generic') { + if (type === 'generic' || type === 'sub_even' || type === 'sub_odd') { ret.labels = [{ id: nkey + '.' + this.key + '.label', text: this.key, @@ -146,7 +147,7 @@ export class Port { } } - if (type === 'generic' && this.parentNode.subModule !== null) { + if (type === 'sub_even' || type === 'sub_odd') { delete ret.x; delete ret.y; } diff --git a/lib/Skin.ts b/lib/Skin.ts index 6632156..61e7de7 100644 --- a/lib/Skin.ts +++ b/lib/Skin.ts @@ -60,7 +60,7 @@ export namespace Skin { }); } - export function findSkinType(type: string) { + export function findSkinType(type: string, depth: number = null) { let ret = null; onml.traverse(skin, { enter: (node, parent) => { @@ -70,13 +70,24 @@ export namespace Skin { }, }); if (ret == null) { - onml.traverse(skin, { - enter: (node) => { - if (node.attr['s:type'] === 'generic') { - ret = node; - } - }, - }); + if (depth == null) { + onml.traverse(skin, { + enter: (node) => { + if (node.attr['s:type'] === 'generic') { + ret = node; + } + }, + }); + } else { + const sub: string[] = ['sub_odd', 'sub_even']; + onml.traverse(skin, { + enter: (node) => { + if (node.attr['s:type'] === sub[depth % 2]) { + ret = node; + } + }, + }); + } } return ret.full; } diff --git a/lib/default.svg b/lib/default.svg index 76a5ed3..34e605d 100644 --- a/lib/default.svg +++ b/lib/default.svg @@ -1,7 +1,7 @@ + width="800" height="400"> - + generic @@ -315,4 +315,40 @@ text { + + sub_odd + + + + out0 + + + out1 + + + in0 + + + in1 + + + + + sub_even + + + + out0 + + + out1 + + + in0 + + + in1 + + +