Metadata-Version: 2.1
Name: salamandra
Version: 0.2
Summary: Framework for describing hierarchical systems
Home-page: http://enicslabs.com/
Author: Tzachi Noy
Author-email: tzachi.noy@biu.ac.il
License: UNKNOWN
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.5
Description-Content-Type: text/markdown

# Project Salamandra

**Salamandra** is an extensible [Pythonic](https://en.wikipedia.org/wiki/Python_(programming_language)) 
infrastructure for loading, analyzing, generating and storing of netlists. 
It is hosted under Enics's gitlab repository. 
For an introductory presentation see [on the git](http://gitlab.local/noytzach/salamandra/raw/master/docs/salamandra.pptx) 
or this [slide show](http://enicskb.local/wiki/images/2/2a/Salamandra.pptx). 
Although still in alpha testing, Salamandra is considered by many to be 'the next big thing'. 
As such, this page is expected to change more frequently than others in this wiki (like duh), so stay tuned.

## Getting Salamandra - Setup
Salamandra is hosted under gitlab at http://gitlab.local/noytzach/salamandra.

To install Salamandra use `pip install salamandra`

If you want a specific version you can do `pip install salamandra==#version#`

To install a specific version locally (only for user) you can install it inside a `virtualenv`

or `pip install --user salamandra==#version#`
which will Install it to the Python user install directory for your platform. 

Typically `~/.local/` on Linux, or `%APPDATA%Python` on Windows

## Basic Commands

**Note: Only Python3 is supported by Salamandra.**

Salamandra is object-oriented. The four basic classes are `Component`, `Net` , `Param` and `Pin`. The following code creates an NMOS transistor skeleton.

```
import salamandra as slm
nmos = slm.Component('nmos')
nmos.add_pin(slm.Pin('source'))
nmos.add_pin(slm.Pin('drain'))
nmos.add_pin(slm.Pin('gate'))
nmos.add_pin(slm.Pin('body'))
```

The nmos component above has no internal structure, but can be used as a leaf node for higher levels, 
as shown in the example below. Note below the usage of `Input`, `Output` and `Inout`, 
which are of subclass `Pin`, and imply the pin's direction in digital designs.
```
inv = slm.Component('inv')

#pins
inv.add_pin(slm.Input('I'))
inv.add_pin(slm.Output('ZN'))
inv.add_pin(slm.Inout('VDD'))
inv.add_pin(slm.Inout('VSS'))

#subcomponents
inv.add_subcomponent(nmos, 'n1')
inv.add_subcomponent(pmos, 'p1')

#connections
inv.connect('I', 'n1.gate')
inv.connect('I', 'p1.gate')
inv.connect('ZN', 'n1.drain')
inv.connect('ZN', 'p1.drain')
inv.connect('VDD', 'p1.source')
inv.connect('VDD', 'p1.body')
inv.connect('VSS', 'n1.source')
inv.connect('VSS', 'n1.body')
```
Component like nmos, resistor, capacitors etc. 
may have parameters like width, length etc. 
To be able to make the component compatible to Netlist exporting, those parameters should be added to the component. 
The following code demonstrate how to add parameters to components.
```
# adding new params to inv
nmos.set_spice_param('W', 200E-9)
nmos.set_spice_param('L', 60E-9)
nmos.set_spice_param('mult', 2)
nmos.set_spice_param('nf', 0)

# changing a specific parameter value in the component
nmos.set_spice_param('nf',2)
```

## Other Commands

-   Component can be created from an existing one(clone it):
    ```
    inv = Component('inv')
    inv.add_pin(Input('A'))
    inv.add_pin(Output('Z'))

    inv_cloned = Component('inv2', inv)
    ```
-   Salamandra can use busses also:
     ```
    and_ = Component('and')
    and_.add_pinbus(Bus(Input, 'A', 2))  # arguments: type-input, bus_name-A, width-2.
    and_.add_pin(Output('Z'))

    nand = Component('nand')
    nand.add_pinbus(Bus(Input, 'A', 2))
    nand.add_pin(Output('Z'))
    nand.add_net(Net('A1A2'))

    nand.add_subcomponent(and_, 'i_and')
    nand.add_subcomponent(inv, 'i_inv')

    nand.connect_bus('A', 'i_and.A')  # connect_bus for busses
    nand.connect('A1A2', 'i_and.Z')
    nand.connect('A1A2', 'i_inv.A')
    nand.connect('Z', 'i_inv.Z')

    # now the connectivity is:
    #    //instances
    #    and i_and(.A({A[1:0]}), .Z(A1A2));
    #    inv i_inv(.A(A1A2), .Z(Z));

    # you can disconnect them also
    nand.disconnect('i_inv.Z')
    nand.disconnect_bus('i_and.A')

    # and after disconnect the connectivity is:
    #    //instances
    #    and i_and(.A(), .Z(A1A2));
    #    inv i_inv(.A(A1A2), .Z());
    ```
-   Pins can also be connected to constants like 1'bx, 1'b1 or 1'b0

-   And if you want to map constants('1', '0') to 'tiehi' and 'tielo' driver cells,
    it can be done with hilomap function:
    ```
    tielo = Component('tielo')
    tielo.add_pin(Output('Y'))

    tiehi = Component('tiehi')
    tiehi.add_pin(Output('Y'))

    inv.add_pin(Inout('VDD'))
    nand.connect("1'b1", 'i_inv.VDD')

    # relevant verilog print:
    # 	//instances
    #	and i_and(.A(), .Z(A1A2));
    #	inv i_inv(.A(A1A2), .VDD(1'b1), .Z());  # 1'b1 connected to VDD

    nand.hilomap(tiehi, tielo)

    # relevant verilog print:
    #	//instances
    #	and i_and(.A(), .Z(A1A2));
    #	inv i_inv(.A(A1A2), .VDD(HI), .Z());  # connected VDD to tiehi output
    #	tiehi tiehi(.Y(HI));  # added tiehi instance to component
    ```
-   If you want to uniqueness your instances in the netlist, 
    e.g. every instance of component will be new component
    you can do this using uniq function: 
    ```
    nand.add_subcomponent(inv, 'i_inv2')  
    # now there are two instances of inv component: 'i_inv1', 'i_inv2'
    # relevant verilog print:
    #	//instances
    #	inv i_inv(.A(A1A2), .VDD(HI), .Z());
    #	inv i_inv2(.A(), .VDD(), .Z());

    nand.uniq()

    # now there are two components of inv(inv_0, inv_1), one for each instance
    # relevant verilog print:
    #	//instances
    #	inv_0 i_inv(.A(A1A2), .VDD(HI), .Z());
    #	inv_1 i_inv2(.A(), .VDD(), .Z());
    ```
-   Salamandra also support assignments using `connect_nets` function:
    ```
    inv = Component('inv')
    inv.add_pin(Input('A'))  
    inv.add_pin(Output('Z'))
    # add_pin also add Net with same name attached to this pin
    # you can disable it with add_pin_adds_net in Component

    inv.connect_nets('A', 'Z')

    # relevant verilog print:
    #	//assignments
    #	assign Z = A;
    ```
-   you can flatten your netlist using `flatten` function, this will flatten your
    netlist until the physical components(checking with __is_physical flag):
    ```
    inv.set_is_physical(True)
    and_.set_is_physical(True)

    # we will add another wrapper to nand(that we saw earlier)
    chip = Component('chip')
    chip.add_pinbus(Bus(Input, 'A', 2))
    chip.add_pin(Output('Z'))
    chip.add_subcomponent(nand, 'nand')
    chip.connect_bus('A', 'nand.A')
    chip.connect('Z', 'nand.Z')

    chip.print_verilog()
    # relevant verilog print:
    #	//wires
    #	wire [1:0] A;
    #	wire Z;
    #
    #	//instances
    #	nand nand(.A({A[1:0]}), .Z(Z));

    chip.flatten()
    chip.print_verilog()
    # relevant verilog print:
    #	//wires
    #	wire [1:0] A;
    #	wire Z;
    #	wire nand__A1A2;
    #
    #	//instances
    #	and nand__i_and(.A({A[1:0]}), .Z(nand__A1A2));
    #	inv nand__i_inv(.A(nand__A1A2), .Z(Z));
    ```
    we can see that it added it's inner nets, subcomponents and their connectivity
    to himself, with the prefix of belongs ("A1A2" -> "nand__A1A2")

-   You can also add properties to objects(pin/net/bus/component) like add tpd value to pin, or 'is_clk' flag
    and after that you can filter pins(or subcomponents/nets...) like this:
    ```
    inv = Component('inv').set_property('is_optimized', True)  # set_property(property, value)
    inv.add_pin(Input('A').set_property('tpd', 0.1))
    inv.add_pin(Inout('VDD').set_property('is_supp', True))

    # now we can get filterd pins as we wish
    fast_pins = inv.get_pins(filter=lambda p: p.get_property('tpd') and p.get_property('tpd') < 0.2)
    vdd_pins = inv.get_pins(filter=lambda p: p.get_property('is_supp'))
    ```

- If you want to get component's connectivity paths, you can get this with `connectivity_paths` method
    which will return 3 dictionaries, one dict that has all connectivity, forward and backward of pins&nets.
    second dict that has forward connectivity, and third backward connectivity.
    for example:

    all connectivity dict -(('A',Input): [('A',Net)], ('A',Net): [('A',Input),('X.A',Input),..])

    forward connectivity dict - (('A',Input): [('A',Net)], ('A',Net): [('X.A',Input)], ('X.A',Input): [('X.O',Output)],..)

- Before you export your netlist you can add verilog code to your netlist:
    ```
    y = Net('y')
    nand.add_net(y)
    nand.add_verilog_code("\tassign {}=1'b0;".format(y.get_object_name()))

    # relevant verilog print:
    #	//verilog code
    #	assign y=1'b0;
    ```

## Export Netlist
-   Salamandra currently supports exporting netlists as verilog netlist or as spectere netlist (cadence version of spice).
    The following code will print the verilog netlist to STDOUT using `print_verilog`.
    ```
    riscv.print_verilog()
    ```
-   And if you want to export also it's subcomponents(as modules) you can do that using the flag `include_subcomponents`

    You can also print it to file using `write_verilog_to_file(path)`

-   Similarly, for exporting into a file you can use `write_verilog`. 
    The function returns a list of lines defining the component as a verilog module:
    ```
    f = open('sfpga.v', 'w')
    for l in sfpga.write_verilog():
       f.write(l+'\n')
    f.close()
    ```

-   In a similar way you can write spectre netlist by using `print_netlist` like this:
    ```
    comp.print_netlist()
    ```
    or any of the verilog functions above

Note: you should call legalize function before export to verilog, to check for example if you have 
half-unconnected pinbusses. And if you do, it will connect the unconnected pins. 
inputs to 1'bx, outputs/inouts to UC_# wires

Note: if you want to use salamandra for spectre netlist you need 
to give your basic component significant names (that fit to the process) 
and attach to them the relevant parameters as explained above.


## Import Netlist
Salamandra currently supports importing netlists from verilog and spectre netlists.
`verilog2slm` is used to import a verilog netlist as components. 
The following code will read the verilog netlist and convert it to components in salamandra:

`components = verilog2slm_file('verilog_file.v')`

it's also support reading an STD cells(takes only I/Os of the modules)

`std_components = verilog2slm_file('std_cells.v',  is_std_cell=True)`
Note: there is an std_cells.v file in verilog_files/more

and if you want to enable implicit wires you can also
enable it through the flag `implicit_wire=True`

The same way you can import spectre netlist using `spectre2slm`:

`comp = spectre2slm_file('spectre_file.sp', top_cell_name='comp')`

## Getting Support
The following people consider themselves (or are considered by others) as Salamandra Gurus. 
You can always ask for their assistance or blessing. 
While support is given for free, smiles or small donations are always welcome.

- [Tzachi Noy](mailto:tzachi.noy@biu.ac.il)
- [Hanan Marinberg](mailto:hanan.marinberg@biu.ac.il)
- [Or Maltabashi](mailto:ormaltab@gmail.com)
- [Roi Gelis](mailto:roi6633@gmail.com)
- [Bnayah Levy](mailto:levi.1001@hotmail.com)

If you think you deserve it - feel free to add yourself to the exclusive list above.

## Salamandra in the academic world
On Spring of 2018, an homework assignment in the course 
[Advanced Digital VLSI Design II](https://lemida.biu.ac.il/course/view.php?id=38992)
given in [Bar-Ilan university](https://en.wikipedia.org/wiki/Bar-Ilan_University), 
was based on the Salamandra infrastructure. 
This never happened in any other reputable academic institute since.

## Create/Update Salamandra as a python package
First create/update a file called `setup.py` in the package's root directory 
in a specific format(see example at `setup.py`)

Then, To create a binary distribution, 
you need to download wheel package using `pip install wheel`

and then to create the distribution run: `python setup.py bdist_wheel`

and it will create a wheel file inside `dist` directory

And that it's all you need to install it using `pip install /path/to/wheelfile.whl`

To upload it to PyPI you will need twine package, so install it first using `pip install twine`

Then upload your package using twine `twine upload dist/* --config-file .pypirc`

And now you can check it on https://pypi.org/project/salamandra
and install wherever you want it using `pip install salamandra`


## See also
- [sclib](http://enicskb.local/wiki/index.php/Standard_Cell_Library_for_Salamandra)
- [Placeable component (componentXY)](http://enicskb.local/wiki/index.php/Placeable_component_in_salamandra)

