Hierarchical Design
Master hierarchical circuit composition and abstraction levels.
Introduction
SNL supports hierarchy in a regular manner, allowing you to build complex circuits from simpler building blocks. Components can be built-in primitives, user-defined primitives, or macros (structural descriptions containing primitives and/or other macros). The depth or level of nesting is unrestricted.
A SNL description contains one or more type blocks, each defining a macro or BOOLEAN subcircuit. An entire circuit description can span multiple files, making large designs manageable.
Instantiating Macros
Instantiating a macro in a PART statement is structurally identical to instantiating a primitive. The key is that the number of connections must match the macro's declared pins.
Example: Four-Bit Ripple-Carry Adder
This example demonstrates hierarchical composition using a full-adder macro as a building block:
* Full-adder macro definition:
!format p= t= i= o=
type=full-adder i=ia,ib,c-in o=sum,c-out
xor exor ia,ib,c-in sum
and1 and ia,c-in
and2 and ib,c-in
and3 and ia,ib
or1 or and1,and2,and3 c-out
* Four-bit adder using full-adder macros:
type=four-bit i=a[3:0],b[3:0],carry-in o=s[4:0],carry-out
%declare integer2=a[3:0],b[3:0],s[4:0]
a0 full-adder a[0],b[0],carry-in s[0],c[0]
a1 full-adder a[1],b[1],c[0] s[1],c[1]
a2 full-adder a[2],b[2],c[1] s[2],c[2]
a3 full-adder a[3],b[3],c[2] s[3],carry-out
s4 exor a[3],b[3],carry-out s[4]
Key observations:
- PART statements instantiating macros have the same structure as those instantiating primitives
- Pin counts must match: part
a1has 3 inputs connecting to the macro's 3 input pins, and 2 outputs connecting to the 2 output pins - Internal carry signals (
c[0], c[1], c[2]) connect adder stages
Hierarchical Naming (Dotted Notation)
Internal parts and signals of macro instances must be distinguished from those in other instances of the same macro. SIMIC uses pathname prefixing with dot delimiters.
Pathname Construction
The pathname is constructed by concatenating all part names from the highest level to the current part,
separated by dots (.).
Example: Hierarchical Names in Four-Bit Adder
For the full-adder instance named a1 in the four-bit adder:
- Internal parts:
a1.xor,a1.and1,a1.and2,a1.and3,a1.or1 - Internal signals:
a1.and1,a1.and2,a1.and3(outputs of the AND gates) - External connections: Signals connected to the macro's pins replace the macro's internal pin names
For example, the macro instance:
a1 full-adder a[1],b[1],c[0] s[1],c[1]
Expands internally with:
- Input
iareplaced bya[1] - Input
ibreplaced byb[1] - Input
c-inreplaced byc[0] - Output
sumreplaced bys[1] - Output
c-outreplaced byc[1]
TRACE and LOOK commands
to inspect specific instances in nested designs (e.g., TRACE a1.xor).
Levels of Abstraction
Any component instantiated in a PART statement has one of four abstraction levels:
Abstraction Hierarchy
- MACRO - Structural description using gates and macros (default search order)
- BOOLEAN - Behavioral functional description using Boolean equations
- BEHAVIORAL - High-level models (C, Python, shared libraries)
- PRIMITIVE - Built-in SIMIC elements (AND, OR, flip-flops, etc.)
Naming Rules
- Error: Multiple type blocks at the same abstraction level cannot have the same name
- Allowed: Identically-named variants at different abstraction levels can coexist in the same file
- SIMIC issues a warning when type blocks at different abstraction levels share names
The COMPOSITION Keyword
The COMPOSITION keyword-field specifies which abstraction level to instantiate when multiple definitions exist.
Default Search Order
If COMPOSITION is not specified, SIMIC searches in this order:
- MACRO (checked first)
- BOOLEAN
- BEHAVIORAL
- PRIMITIVE (checked last)
Explicit Selection
* Force instantiation of BOOLEAN variant:
p=fad t=full-adder i=a1,b1,c1 o=s1,co1 composition=boolean
* Force instantiation of PRIMITIVE variant:
p=g1 t=and i=a,b,c composition=primitive
COMPOSITION=PRIMITIVE to ensure the built-in is used.
Multi-File Designs
Large designs can be organized across multiple files. Type definitions are accessible across file boundaries.
Managing Variants
If you have multiple implementations of the same type name at the same abstraction level:
- Place each variant in a separate file
- Use the
GET FILEkeyword option to select the appropriate variant
Example: Variant Selection
* Load fast variant:
>>: get type=processor file=proc_fast.net
* Load low-power variant:
>>: get type=processor file=proc_lowpower.net
BOOLEAN Type Blocks
The BOOLEAN abstraction level allows functional descriptions using Boolean equations instead of structural gates.
Basic Structure
A BOOLEAN type block contains only a TYPE statement with Boolean equations (no PART statements):
type=half-adder i=a,b o=sum,carry boolean=BEGIN; sum == a@b; carry == a*b; END;
Placeholder BOOLEAN Types
For experimentation, you can define a BOOLEAN type without equations and specify them at runtime using the CLAMP command.
In this case, include COMPOSITION=BOOLEAN to identify the type:
type=test-logic i=a,b,c o=z composition=boolean
* Specify behavior at runtime:
>>: clamp type=test-logic boolean=BEGIN; z == a*b+c; END;
Design Patterns & Best Practices
Modular Design
- Break complex circuits into reusable macros
- Test macros independently before integration
- Use meaningful, hierarchical names
Abstraction Strategy
- MACRO level: Use for structural designs where gate-level detail is important
- BOOLEAN level: Use for rapid prototyping or when logic equations are clearer than gate schematics
- BEHAVIORAL level: Use for complex algorithms, processors, or custom timing models
Debugging Hierarchical Designs
- Use
TRACEwith hierarchical names to monitor specific instances - Use
LOOKto inspect internal states at any hierarchy level - Use
EXPANDcommand to flatten hierarchy for analysis