netlistsvg

repository·master·Indexed 21 days ago

https://github.com/nturley/netlistsvg

A tool for rendering SVG schematics from Yosys JSON netlists using the ELKjs layout engine. It provides a CLI for converting JSON netlists to SVG files and a web bundle API for integration into web applications and ObservableHQ notebooks. The library supports custom skin files for defining component visual libraries and layout configurations, and includes built-in digital and analog skins.

Tokens
7.8K
Snippets
28
Records
32
Agent score
74%

What's inside netlistsvg

  1. Understand the Input JSON Schema

    master

    netlistsvg consumes a JSON (or JSON5) object representing a netlist. The structure is organized by modules. Each module contains:

    • ports: A dictionary of port names. Each port defines its direction (input or output) and a bits array representing the wire indices.
    • cells: A dictionary of components (e.g., logic gates, resistors, transistors). Each cell includes:
      • type: A string matching an alias in a skin file.
      • port_directions (optional): Defines the direction of ports for that specific cell type.
      • connections: A mapping of port names to arrays of wire indices (e.g., "A": [16, 17, 18]).
      • attributes (optional): Arbitrary metadata (e.g., resistor values like "value": "10k").

    You can generate this JSON automatically using tools like Yosys or write it manually using JSON5 syntax.

    {
      "modules": {
        "module_name": {
          "ports": {
            "port_name": {
              "direction": "input",
              "bits": [ 0, 1, 2 ]
            }
          },
          "cells": {
            "cell_id": {
              "type": "component_type",
              "connections": {
                "port_a": [ 0, 1, 2 ],
                "port_b": [ 3 ]
              }
            }
          }
        }
      }
    }
  2. Create and use Skin Files

    master

    A skin file is an SVG that defines the visual library for the netlist. It serves two primary purposes:

    1. Component Library: Defines templates for cells. Each template uses <s:alias val="..."/> to map a type from the input JSON to an SVG group. Templates must define the position and ID of ports using <g s:x="..." s:y="..." s:pid="PORT_NAME"/> so the engine knows where to attach wires.
    2. Special Nodes: Defines how to render Input/Output ports, constants, Splits/Joins, and generic nodes. These are automatically resized and adjusted to fit the cell.

    Skin files can include <style> tags or inline CSS, which are copied to the final output SVG. This allows for conditional styling based on bus width using classes like .busLabel_* and .width_*.

    <g s:type="mux" transform="translate(50, 50)" s:width="20" s:height="40">
      <s:alias val="$pmux"/>
      <s:alias val="$mux"/>
    
      <path d="M0,0 L20,10 L20,30 L0,40 Z"/>
    
      <g s:x="0" s:y="10" s:pid="A"/>
      <g s:x="0" s:y="30" s:pid="B"/>
      <g s:x="10" s:y="35" s:pid="S"/>
      <g s:x="20" s:y="20" s:pid="Y"/>
    </g>
  3. Integrate netlistsvg in a web application

    master

    The netlistsvg web bundle does not include ELKjs. You must include ELKjs in your HTML before the netlistsvg bundle, as ELKjs creates a global window.ELK variable required by the library.

    HTML Implementation:

    <script type="text/javascript" src="https://nturley.github.io/netlistsvg/elk.bundled.js"></script>
    <script type="text/javascript" src="https://nturley.github.io/netlistsvg/built/netlistsvg.bundle.js"></script>
  4. Develop and build netlistsvg

    master

    The source code is located in lib/ (TypeScript) and compiled to built/ (JavaScript). To develop, modify the TypeScript files and then compile them.

    • Run tests (compile, lint, and self-tests): npm test
    • Build the web bundle: npm run build-module
    npm test
  5. Style netlist elements based on bus width

    master

    The output SVG supports styling elements based on their bus width using specific CSS classes. This is useful for hiding labels on small buses or changing colors for specific widths.

    • Bus Labels: Use .busLabel_<width> (e.g., .busLabel_2) to target labels for a specific bus width.
    • Lines/Wires: Use .width_<width> (e.g., .width_4) to target lines of a specific width.

    Example: To hide labels for 2-wire buses and make 4-wire lines red, add this to your skin file's <style> block:

    .busLabel_2 {
        fill-opacity: 0;
    }
    line.width_4 {
        stroke: red;
    }
  6. Generate input JSON using Yosys

    master

    You can use Yosys to generate the required input_json_file using the write_json command. It is recommended to use the prep command first.

    Depending on your goal, use one of the following Yosys command patterns:

    ### Generate top level diagram
    # Shows the top module with inner modules as boxes
    yosys -p "prep -top my_top_module; write_json output.json" input.v
    
    ### Generate logic diagram
    # Uses -flatten to convert everything to low-level logic (basic cells and black boxes)
    yosys -p "prep -top my_top_module -flatten; write_json output.json" input.v
    
    ### Generate AIG (And-Inverter Graph) diagram
    # Uses aigmap to create a diagram using only AND/NAND and NOT cells
    yosys -p "prep -top my_top_module; aigmap; write_json output.json" input.v
  7. Install netlistsvg from source

    master

    To install the latest version from the source repository, clone the repository, install dependencies, and install it globally using npm install -g .:

    git clone https://github.com/nturley/netlistsvg
    cd netlistsvg
    npm install # install dependencies
    sudo npm install -g .

    To uninstall the package from your system, use:

    sudo npm uninstall -g netlistsvg
  8. Use netlistsvg on ObservableHQ

    master

    To use netlistsvg in an ObservableHQ notebook, you must first require ELKjs and assign it to the global window.ELK object before requiring the netlistsvg bundle.

    netlistsvg = {
      var ELK = await require('https://nturley.github.io/netlistsvg/elk.bundled.js')
      window.ELK = ELK
      return require('https://nturley.github.io/netlistsvg/built/netlistsvg.bundle.js')
    }
  9. Render schematics using the web bundle API

    master

    The web bundle provides a render method to generate schematics. You can use it with a Promise or a callback function. The bundle includes both digitalSkin and analogSkin, along with example netlists.

    Using Promises:

    await netlistsvg.render(netlistsvg.digitalSkin, netlistsvg.exampleDigital);

    Using Callbacks:

    netlistsvg.render(netlistsvg.digitalSkin, netlistsvg.exampleDigital, (err, result) => console.log(result));
  10. Configure ElkJS layout properties in a Skin File

    master

    You can pass layout configuration directly to the ElkJS engine via a special <s:layoutEngine> tag in your skin file. Properties specified here are passed to the layout engine to control spacing and direction.

    Common properties include:

    • org.eclipse.elk.direction: The direction of the layout (e.g., DOWN).
    • org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers: Spacing between layers.
    • org.eclipse.elk.spacing.nodeNode: Spacing between nodes.
    <s:layoutEngine
          org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers="5"
          org.eclipse.elk.spacing.nodeNode= "35"
          org.eclipse.elk.direction="DOWN"
        />
  11. Format of the Input JSON file

    master

    The input_json_file used by netlistsvg follows a specific structure derived from Yosys JSON. The renderer focuses on a single module (either the first one or the one marked with a top attribute).

    Key behaviors:

    • Skin Templates: If a cell name matches an alias in the provided skin file, that template is used for the SVG.
    • Port Directions: For cells defined in a skin, port directions are optional.
    • Bus Handling: If a cell has a WIDTH parameter > 1, -bus is appended to the cell type. This allows for different skinning of single-bit vs. multi-bit variants (currently primarily used for $mux). For cells not in the skin file, the -bus suffix will appear in the generic name displayed above the cell.
    {
      "modules": {
        "<dont care>": {
          "ports": {
            "<port name>": {
              "direction": "<input|output>",
              "bits": [ 2, "1", ... ]
            }
          },
          "cells": {
            "<cell name>": {
              "type": "<type name>",
              "parameters": {
                "WIDTH": 3
              },
              "port_directions": {
                "<port name>": "<input|output>"
              },
              "connections": {
                "<port name>": [ 3, "0", ... ]
              }
            }
          }
        }
      }
    }