Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Hierarchical netlists #92

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open

WIP: Hierarchical netlists #92

wants to merge 32 commits into from

Conversation

djwubs
Copy link
Contributor

@djwubs djwubs commented May 9, 2020

Hierarchical netlists

This adds support for hierarchical netlists (#64). It is now possible to show the inner schematic of the submodules. The settings for the hierarchy are controlled by a configuration file, which is explained in the next subsection. This configuration file can be passed to netlistsvg, otherwise it selects the default configuration file, which has hierarchy turned off. This is the new command line format:

netlistsvg input_json_file [-o output_svg_file] [--skin skin_file] [--layout elk_json_file] [--config config_json_file]

A simple example of an hierarchical netlist is shown below.

JSON Source
{
  "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
{
  "hierarchy": {
    "enable": "modules",
    "expandLevel": 0,
    "expandModules": {
      "types": [],
      "ids": ["foo"]
    },
    "colour": ["#e9e9e9"]
  },
  "top": {
    "enable": false,
    "module": ""
  }
}

example

Besides the support for hierarchical netlists, some other minor changes have been made, which are listed in the changelog.

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 and looks like this:

{
  "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.

Changelog

  • Added support for hierarchical netlists.
  • Added a configuration file.
  • Wires connected to splits and joins are now always horizontal.
  • The input of splits and the output of joins is now always centered with respect to the component height.
  • All components are filled with a white background, preventing the schematic background to change the component colour.
  • Updated README and examples.

Status

Currently, this is ready for review. It has been tested on multiple inputs, ranging from small to large designs. The main functionality is working properly.

The main improvement which may be necessary before merging is the routing of wires. For the larger designs, wires take a lot of strange detours, but this is already the case for large non hierarchical designs. Since the layout is done by elkjs, it is hard to improve this.

@nturley
Copy link
Owner

nturley commented May 10, 2020

Awesome! This will take me a little bit to review. Some first impressions:

Rather than using another config file, just pass configuration through the skin file. Options like color can be done in CSS of the skin file if the generated SVG elements have a class names and IDs.

@djwubs
Copy link
Contributor Author

djwubs commented May 13, 2020

I agree that the styling options, like colour, are better suited for the skin file. The skin file is, as far as I know, less suited for the other options, as it is more difficult to add lists (which are used to define which modules to expand). It might also get cluttered when more options are added, because it is not possible to create sections as I did in the JSON configuration file.

The main reason I've chosen for a separate JSON file for the configuration options is that it is easy to use and suitable for all options that were planned. As the options can be divided in sections, I think it would be less cluttered and easier to understand. I think it would be best to have all skin and style related options in the skin file and to have a separate file for the other configuration options.

Another option that can be considered is the use of command line arguments, which would be easy to explain via an --help option and is the easiest to use. But commands can then get too long, as more options are added.

@rw1nkler
Copy link

This is a great feature! When it will be available in netlistsvg? Is there any work that needs to be done here?

@djwubs
Copy link
Contributor Author

djwubs commented Sep 28, 2020

It probably will not be available in the main branch. I am not actively developing this anymore, as this feature was part of a university project, for which this version was sufficient. The main branch is only maintained, so small bugs get fixed, but no new features are in development afaik. There are multiple reasons that netlistsvg is in maintenance mode. The main reasons are the limitations from the layout engine and a messy code base.

Regarding the second question, the core functionality of hierarchical netlists is fully implemented. Some issues, which are also in the main branch, still remain (e.g. #93). The main point which may require some work are the configuration options, when it is decided that they should not be in a separate configuration file. Another point which may require some work is the routing of wires, but this is mostly done by the layout engine, and thus hard to influence.

@nturley
Copy link
Owner

nturley commented Sep 30, 2020

Yeah... Sorry about that. I shouldve been more on top of this and netlistsvg is quite messy and does need a cleanup. I haven't spent much time on it for the past ahem years.

Thank you, @djwubs for contributing this PR and good luck on your future endeavors.

The code quality for the PR looks sound from what I've looked over but I haven't reviewed it too closely. I could probably get it across the finish line. Or honestly anybody else could look it over, tell me it looks good to them, resolve the conflicts and I'd believe then and get it merged.

I think it's important that code is reviewed but I'm not picky about who reviews it. So if someone else assures me it's fine and resolves the merge conflicts then I'm happy.

@nobodywasishere
Copy link
Contributor

I have merged this with the current master on my fork here, fixing merge issues. I've tested it extensively and the only issue I've run into is a stack frame overflow due to there being too many nodes/edges in a drawing. Otherwise, I think this works well. I'm not sure if I should make a new PR or have someone change this PR to that branch.

@qarlosalberto
Copy link

qarlosalberto commented Sep 19, 2021

I have a problem with signal names (right side in the graph).

out

{
  "hierarchy": {
    "enable": "all",
    "expandLevel": 0,
    "expandModules": {
      "types": [],
      "ids": []
    },
    "colour": ["#e9e9e9"]
  },
  "top": {
    "enable": false,
    "module": ""
  }
}
{
  "creator": "Yosys 0.9+4303 (open-tool-forge build) (git sha1 c88eaea6, gcc 9.3.0-17ubuntu1~20.04 -Os)",
  "modules": {
    "Fomu_Blink": {
      "attributes": {
      },
      "ports": {
        "clki": {
          "direction": "input",
          "bits": [ 2 ]
        },
        "rgb0": {
          "direction": "output",
          "bits": [ 3 ]
        },
        "rgb1": {
          "direction": "output",
          "bits": [ 4 ]
        },
        "rgb2": {
          "direction": "output",
          "bits": [ 5 ]
        },
        "usb_dp": {
          "direction": "output",
          "bits": [ "0" ]
        },
        "usb_dn": {
          "direction": "output",
          "bits": [ "0" ]
        },
        "usb_dp_pu": {
          "direction": "output",
          "bits": [ "0" ]
        }
      },
      "cells": {
        "clk_generator": {
          "hide_name": 0,
          "type": "clkgen",
          "parameters": {
          },
          "attributes": {
          },
          "port_directions": {
            "clk": "input",
            "cnt": "output"
          },
          "connections": {
            "clk": [ 2 ],
            "cnt": [ 6, 7, 8 ]
          }
        },
        "rgba_driver": {
          "hide_name": 0,
          "type": "SB_RGBA_DRV",
          "parameters": {
            "CURRENT_MODE": "0b1",
            "RGB0_CURRENT": "0b000011",
            "RGB1_CURRENT": "0b000011",
            "RGB2_CURRENT": "0b000011"
          },
          "attributes": {
          },
          "port_directions": {
            "CURREN": "input",
            "RGB0": "output",
            "RGB0PWM": "input",
            "RGB1": "output",
            "RGB1PWM": "input",
            "RGB2": "output",
            "RGB2PWM": "input",
            "RGBLEDEN": "input"
          },
          "connections": {
            "CURREN": [ "1" ],
            "RGB0": [ 3 ],
            "RGB0PWM": [ 8 ],
            "RGB1": [ 4 ],
            "RGB1PWM": [ 7 ],
            "RGB2": [ 5 ],
            "RGB2PWM": [ 6 ],
            "RGBLEDEN": [ "1" ]
          }
        }
      },
      "netnames": {
        "clk_generator:10": {
          "hide_name": 0,
          "bits": [ 6, 7, 8 ],
          "attributes": {
          }
        },
        "clki": {
          "hide_name": 0,
          "bits": [ 2 ],
          "attributes": {
          }
        },
        "rgb0": {
          "hide_name": 0,
          "bits": [ 3 ],
          "attributes": {
          }
        },
        "rgb1": {
          "hide_name": 0,
          "bits": [ 4 ],
          "attributes": {
          }
        },
        "rgb2": {
          "hide_name": 0,
          "bits": [ 5 ],
          "attributes": {
          }
        },
        "usb_dn": {
          "hide_name": 0,
          "bits": [ "0" ],
          "attributes": {
          }
        },
        "usb_dp": {
          "hide_name": 0,
          "bits": [ "0" ],
          "attributes": {
          }
        },
        "usb_dp_pu": {
          "hide_name": 0,
          "bits": [ "0" ],
          "attributes": {
          }
        }
      }
    },
    "SB_GB": {
      "attributes": {
        "blackbox": "00000000000000000000000000000001"
      },
      "ports": {
        "USER_SIGNAL_TO_GLOBAL_BUFFER": {
          "direction": "input",
          "bits": [ 2 ]
        },
        "GLOBAL_BUFFER_OUTPUT": {
          "direction": "output",
          "bits": [ 3 ]
        }
      },
      "cells": {
      },
      "netnames": {
        "GLOBAL_BUFFER_OUTPUT": {
          "hide_name": 0,
          "bits": [ 3 ],
          "attributes": {
          }
        },
        "USER_SIGNAL_TO_GLOBAL_BUFFER": {
          "hide_name": 0,
          "bits": [ 2 ],
          "attributes": {
          }
        }
      }
    },
    "SB_RGBA_DRV": {
      "attributes": {
        "blackbox": "00000000000000000000000000000001"
      },
      "ports": {
        "RGB0PWM": {
          "direction": "input",
          "bits": [ 2 ]
        },
        "RGB1PWM": {
          "direction": "input",
          "bits": [ 3 ]
        },
        "RGB2PWM": {
          "direction": "input",
          "bits": [ 4 ]
        },
        "CURREN": {
          "direction": "input",
          "bits": [ 5 ]
        },
        "RGBLEDEN": {
          "direction": "input",
          "bits": [ 6 ]
        },
        "RGB0": {
          "direction": "output",
          "bits": [ 7 ]
        },
        "RGB1": {
          "direction": "output",
          "bits": [ 8 ]
        },
        "RGB2": {
          "direction": "output",
          "bits": [ 9 ]
        }
      },
      "cells": {
      },
      "netnames": {
        "CURREN": {
          "hide_name": 0,
          "bits": [ 5 ],
          "attributes": {
          }
        },
        "RGB0": {
          "hide_name": 0,
          "bits": [ 7 ],
          "attributes": {
          }
        },
        "RGB0PWM": {
          "hide_name": 0,
          "bits": [ 2 ],
          "attributes": {
          }
        },
        "RGB1": {
          "hide_name": 0,
          "bits": [ 8 ],
          "attributes": {
          }
        },
        "RGB1PWM": {
          "hide_name": 0,
          "bits": [ 3 ],
          "attributes": {
          }
        },
        "RGB2": {
          "hide_name": 0,
          "bits": [ 9 ],
          "attributes": {
          }
        },
        "RGB2PWM": {
          "hide_name": 0,
          "bits": [ 4 ],
          "attributes": {
          }
        },
        "RGBLEDEN": {
          "hide_name": 0,
          "bits": [ 6 ],
          "attributes": {
          }
        }
      }
    },
    "clkgen": {
      "attributes": {
      },
      "ports": {
        "clk": {
          "direction": "input",
          "bits": [ 2 ]
        },
        "cnt": {
          "direction": "output",
          "bits": [ 3, 4, 5 ]
        }
      },
      "cells": {
        "\\29": {
          "hide_name": 0,
          "type": "$add",
          "parameters": {
            "A_SIGNED": "00000000000000000000000000000000",
            "A_WIDTH": "00000000000000000000000000011100",
            "B_SIGNED": "00000000000000000000000000000000",
            "B_WIDTH": "00000000000000000000000000011100",
            "Y_WIDTH": "00000000000000000000000000011100"
          },
          "attributes": {
          },
          "port_directions": {
            "A": "input",
            "B": "input",
            "Y": "output"
          },
          "connections": {
            "A": [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 3, 4, 5 ],
            "B": [ "1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0" ],
            "Y": [ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 ]
          }
        },
        "\\32": {
          "hide_name": 0,
          "type": "$dff",
          "parameters": {
            "CLK_POLARITY": "00000000000000000000000000000001",
            "WIDTH": "00000000000000000000000000011100"
          },
          "attributes": {
          },
          "port_directions": {
            "CLK": "input",
            "D": "input",
            "Q": "output"
          },
          "connections": {
            "CLK": [ 59 ],
            "D": [ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 ],
            "Q": [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 3, 4, 5 ]
          }
        },
        "clk_gb": {
          "hide_name": 0,
          "type": "SB_GB",
          "parameters": {
          },
          "attributes": {
          },
          "port_directions": {
            "GLOBAL_BUFFER_OUTPUT": "output",
            "USER_SIGNAL_TO_GLOBAL_BUFFER": "input"
          },
          "connections": {
            "GLOBAL_BUFFER_OUTPUT": [ 59 ],
            "USER_SIGNAL_TO_GLOBAL_BUFFER": [ 2 ]
          }
        }
      },
      "netnames": {
        "$auto$ghdl.cc:762:import_module$6": {
          "hide_name": 1,
          "bits": [ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 ],
          "attributes": {
          }
        },
        "clk": {
          "hide_name": 0,
          "bits": [ 2 ],
          "attributes": {
          }
        },
        "clko": {
          "hide_name": 0,
          "bits": [ 59 ],
          "attributes": {
          }
        },
        "cnt": {
          "hide_name": 0,
          "bits": [ 3, 4, 5 ],
          "attributes": {
          }
        },
        "counter": {
          "hide_name": 0,
          "bits": [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 3, 4, 5 ],
          "attributes": {
            "init": "0000000000000000000000000000"
          }
        }
      }
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants