dan-fritchman / Hdl21

Hardware Description Library
BSD 3-Clause "New" or "Revised" License
69 stars 16 forks source link

Hierarchical Flattening for Modules #86

Open uduse opened 1 year ago

uduse commented 1 year ago

Why we need it

Hierarchical information might not be needed for some use cases. For example, listing out all MOSes and compute their total area. It would be nice to have a functionality that flattens a module into a module with a single layer.

Specifications

(partial credit from @dan-fritchman)

Example

I have a small script to do this for simple cases (will PR later).

Source Module Vlsir ``` modules { name: "src.hdl.Inverter" ports { signal: "vdd" direction: NONE } ports { signal: "vss" direction: NONE } ports { signal: "vin" direction: NONE } ports { signal: "vout" direction: NONE } signals { name: "vdd" width: 1 } signals { name: "vss" width: 1 } signals { name: "vin" width: 1 } signals { name: "vout" width: 1 } instances { name: "pmos" module { external { domain: "hdl21.primitives" name: "Mos" } } parameters { name: "tp" value { literal: "PMOS" } } connections { portname: "d" target { sig: "vout" } } connections { portname: "g" target { sig: "vin" } } connections { portname: "s" target { sig: "vdd" } } } instances { name: "nmos" module { external { domain: "hdl21.primitives" name: "Mos" } } parameters { name: "tp" value { literal: "NMOS" } } connections { portname: "d" target { sig: "vout" } } connections { portname: "g" target { sig: "vin" } } connections { portname: "s" target { sig: "vss" } } } } modules { name: "src.hdl.Buffer" ports { signal: "vdd" direction: NONE } ports { signal: "vss" direction: NONE } ports { signal: "vin" direction: NONE } ports { signal: "vout" direction: NONE } signals { name: "n0" width: 1 } signals { name: "vdd" width: 1 } signals { name: "vss" width: 1 } signals { name: "vin" width: 1 } signals { name: "vout" width: 1 } instances { name: "inv_1" module { local: "src.hdl.Inverter" } connections { portname: "vdd" target { sig: "vdd" } } connections { portname: "vss" target { sig: "vss" } } connections { portname: "vin" target { sig: "vin" } } connections { portname: "vout" target { sig: "n0" } } } instances { name: "inv_2" module { local: "src.hdl.Inverter" } connections { portname: "vdd" target { sig: "vdd" } } connections { portname: "vss" target { sig: "vss" } } connections { portname: "vin" target { sig: "n0" } } connections { portname: "vout" target { sig: "vout" } } } } ```
Flattened Module Vlsir ``` modules { name: "Buffer_flat" ports { signal: "vdd" direction: NONE } ports { signal: "vss" direction: NONE } ports { signal: "vin" direction: NONE } ports { signal: "vout" direction: NONE } signals { name: "n0" width: 1 } signals { name: "vdd" width: 1 } signals { name: "vss" width: 1 } signals { name: "vin" width: 1 } signals { name: "vout" width: 1 } instances { name: "inv_1:pmos" module { external { domain: "hdl21.primitives" name: "Mos" } } parameters { name: "tp" value { literal: "PMOS" } } connections { portname: "d" target { sig: "n0" } } connections { portname: "g" target { sig: "vin" } } connections { portname: "s" target { sig: "vdd" } } } instances { name: "inv_1:nmos" module { external { domain: "hdl21.primitives" name: "Mos" } } parameters { name: "tp" value { literal: "NMOS" } } connections { portname: "d" target { sig: "n0" } } connections { portname: "g" target { sig: "vin" } } connections { portname: "s" target { sig: "vss" } } } instances { name: "inv_2:pmos" module { external { domain: "hdl21.primitives" name: "Mos" } } parameters { name: "tp" value { literal: "PMOS" } } connections { portname: "d" target { sig: "vout" } } connections { portname: "g" target { sig: "n0" } } connections { portname: "s" target { sig: "vdd" } } } instances { name: "inv_2:nmos" module { external { domain: "hdl21.primitives" name: "Mos" } } parameters { name: "tp" value { literal: "NMOS" } } connections { portname: "d" target { sig: "vout" } } connections { portname: "g" target { sig: "n0" } } connections { portname: "s" target { sig: "vss" } } } } ```
dan-fritchman commented 1 year ago

This would be a welcome addition!
A few notes:


All instances in the module are PrimitiveCalls or other types of leaf nodes.

Particularly re the "other types of leaf nodes": Hdl21 has four Instantiable types -

Instantiable = Union[Module, ExternalModuleCall, GeneratorCall, PrimitiveCall]

I.e. those are the four kinds of things that can be the target of an hdl21.Instance.

Of those, two are "irreducible": primitives (PrimitiveCall) and external-modules (ExternalModuleCall). These would be the leaf-nodes when flattening.

Another (bonus!) feature would be adding the capacity for a "stop list" of Modules and/or GeneratorCalls, which when detected would just be turned into ExternalModuleCalls. Such things are common in popular "big EDA" software.


Module signals are the signals from all levels and the ports from all non-root levels.

I think that is potentially true, depending whether you'd want to include "floating" Signals, i.e. those which don't ultimately reach a leaf-level component.

Do Modules actually have these? Sure! They have all kinds of weird stuff, both intentionally and not. E.g. for:

Does it matter whether we do? Probably not; if the source-Module has floating Signals, I suppose it makes sense to just transfer them along to the flattened-Module. (Inside of a SPICE simulator, in contrast, you'd need to filter these out, or generally get a singular matrix due to them.)

uduse commented 1 year ago

Where should this feature go in Hdl21 file structure? elab? Additionally, what role does Bundle play in this feature? (I have no idea how Bundle works...)

dan-fritchman commented 1 year ago

I think a package-level hdl21.flatten module, with a flatten() function, would be a good place.
And you shouldn't have to even notice Bundles are a thing, if you invoke elaboration before the flattening-work. So flatten() might look something like:

def flatten(m: Module) -> Module:
  elaborate(m)
  return flattening_stuff_youre_already_doing(m)

def flattening_stuff_youre_already_doing(m: Module) -> Module:
  # Ya know, the stuff you're already doing!