To use AxLS, Python, Yosys, and Icarus Verilog are required, at least in the following versions:
name | version | - |
---|---|---|
Icarus Verilog | 10.3 | |
Yosys | 0.9+932 | |
Python | 3.6.8 |
sudo apt-get install build-essential clang bison flex \
libreadline-dev gawk tcl-dev libffi-dev git \
graphviz xdot pkg-config python3 libboost-system-dev \
libboost-python-dev libboost-filesystem-dev zlib1g-dev
git clone https://github.com/cliffordwolf/yosys.git
cd yosys/
make config-clang
make config-gcc
make
make test #optional
sudo make install
wget ftp://ftp.icarus.com/pub/eda/verilog/v10/verilog-10.3.tar.gz
tar -zxvf verilog-10.3.tar.gz
cd verilog-10.3/
./configure
make
sudo make install
- First, import the
Circuit
class:
from circuit import Circuit
- Some constants are required to define files and their corresponding path:
# verilog file of the circuit we want to approximate
RTL='circuits/brent.kung.16b/UBBKA_15_0_15_0.v'
# testbench file for the circuit we want to approximate
TB='circuits/brent.kung.16b/UBBKA_15_0_15_0_tb.v'
# [optional] a saif for the circuit we want to approximate
SAIF='circuits/brent.kung.16b/UBBKA_15_0_15_0.saif'
- When creating a
Circuit
object, the library parse every file and builds an XML tree with all the relevant information related with the circuit
# Circuit creates a representation of the circuit using python objects
our_circuit = Circuit(RTL, "NanGate15nm", SAIF)
- You can print the circuit from the XML file, by calling the
get_circuit_xml()
function:
print(our_circuit.get_circuit_xml())
You should see something like this:
<!-- complete file at circuits/brent.kung.16b/UBBKA_15_0_15_0.xml -->
- Or you can also print the circuit as an graph with
show()
our_circuit.show()
- From
our_circuit
, you can also obtain the circuit inputs/outputs
print("Circuit inputs...")
print(our_circuit.inputs)
print("Circuit outputs...")
print(our_circuit.outputs)
That will return something like:
Circuit inputs...
['X[0]', 'X[1]', 'X[2]', 'X[3]', 'X[4]', 'X[5]', 'X[6]', 'X[7]', 'X[8]', 'X[9]', 'X[10]', 'X[11]', 'X[12]', 'X[13]', 'X[14]', 'X[15]', 'Y[0]', 'Y[1]', 'Y[2]', 'Y[3]', 'Y[4]', 'Y[5]', 'Y[6]', 'Y[7]', 'Y[8]', 'Y[9]', 'Y[10]', 'Y[11]', 'Y[12]', 'Y[13]', 'Y[14]', 'Y[15]']
Circuit outputs...
['S[0]', 'S[1]', 'S[2]', 'S[3]', 'S[4]', 'S[5]', 'S[6]', 'S[7]', 'S[8]', 'S[9]', 'S[10]', 'S[11]', 'S[12]', 'S[13]', 'S[14]', 'S[15]', 'S[16]']
- Remember, the circuit is represented as an XML (ElementTree) so if you want to iterate over the XML just get the root of the tree:
print(our_circuit.netl_root)
[<Element 'node' at 0xb587de14>, <Element 'node' at 0xb581057c>]
Using this node you can implement your own pruning algorithms. Because ElementTree allows you to search XML nodes based on their attributes using xpath syntax.
Don't Reinvent the Wheel!
- The first example method we provide to delete nodes is quite simple, just delete a node based on its name. You can do it in two different ways:
# Using ElementTree xpath syntax
node101 = our_circuit.netl_root.find("./node[@var='_101_']")
node101.set("delete", "yes")
Or
# Using the built-in functionality
our_circuit.delete("_101_")
When you set the attribute delete
of a node to yes
, it means that this node will be deleted the next time our circuit is saved in the filesystem. The node will remain in the xml tree! (just in case we need to revert a deletion).
This framework currently provides two approaches, as examples, in order to suggest which nodes you should delete:
- InOuts: suggest which nodes to delete if the inputs or the outputs are constants.
- Pseudo-Probabilistic Pruning: suggest nodes to delete based on the toggling time a specific node keep a constant value (1 or 0) in their output. Similar as presented in J. Schlachter, V. Camus, K. V. Palem and C. Enz, "Design and Applications of Approximate Circuits by Gate-Level Pruning," in IEEE Transactions on Very Large Scale Integration (VLSI) Systems, vol. 25, no. 5, pp. 1694-1702, May 2017, doi: 10.1109/TVLSI.2017.2657799.
- Lets start with InOuts methods. Import both
GetInputs
andGetOutputs
from pruning_algorithms.inouts import GetInputs, GetOutputs
GetInputs
will give you a list of nodes that can be deleted if the inputs specified are constants:
# Extracts the nodes that can be deleted if inputs of bit 0 are constants
inputs = ["X[0]","Y[0]"]
depricable_nodes = GetInputs(our_circuit.netl_root, inputs)
print(depricable_nodes)
print("Nodes to delete if input 0 is constant")
print([ n.attrib["var"] for n in depricable_nodes ])
Shows:
Nodes to delete if input 0 is constant
['_069_', '_147_']
Other example:
# Extracts the nodes that can be deleted if inputs of bit 3 are constants
inputs = ["X[0]","Y[0]","X[1]","Y[1]","X[2]","Y[2]","X[3]","Y[3]"]
depricable_nodes = GetInputs(our_circuit.netl_root, inputs)
print("Nodes to delete if input 3 is constant")
print([ n.attrib["var"] for n in depricable_nodes ])
Shows:
Nodes to delete if input 3 is constant
['_069_', '_147_', '_066_', '_067_', '_075_', '_068_', '_070_', '_071_', '_072_', '_073_', '_074_', '_076_', '_080_', '_077_', '_078_', '_086_', '_079_', '_081_']
GetOutputs
will give you a list of nodes that can be deleted if the outputs specified are constants:
# Extracts the nodes that can be deleted if output of bit 0 is constant
outputs = ["S[0]"]
depricable_nodes = GetOutputs(our_circuit.netl_root, outputs)
print(depricable_nodes)
print("Nodes to delete if output 0 is constant")
print([ n.attrib["var"] for n in depricable_nodes ])
Shows:
Nodes to delete if output 0 is constant
['_147_']
Other example:
# Extracts the nodes that can be deleted if outputs of bit 3 is constant
outputs = ["S[5]"]
depricable_nodes = GetOutputs(our_circuit.netl_root, outputs)
print("Nodes to delete if output 5 is constant")
print([ n.attrib["var"] for n in depricable_nodes ])
Shows:
Nodes to delete if output 5 is constant
['_091_']
- In order to use ProbPrun methods make sure you specified a SAIF file when you created the Circuit object. First lets import the method:
from pruning_algorithms.probprun import GetOneNode
GetOneMethod
is a generator, so it will return one node every time you call it, so lets first create it:
pseudo_probprun = GetOneNode(our_circuit.netl_root)
- Now we can call it, every time it retrieves the node to delete, the logic value it has most of the time, and how much time it keeps that value:
node, output, time = next(pseudo_probprun)
print(f"ProbPrun suggest delete the node {node} because is {output} {time}% of the time")
This should show:
ProbPrun suggest delete the node _114_ because is 0 100% of the time
- As any generator, you can use it in for loops:
for x in range (30):
node, output, time = next(pseudo_probprun)
print(f"{node} is {output} {time}% of the time")
This will return:
ProbPrun suggest delete the node _114_ because is 0 100% of the time
_115_ is 1 100% of the time
_116_ is 0 100% of the time
_117_ is 1 100% of the time
_120_ is 1 100% of the time
_121_ is 1 100% of the time
_122_ is 0 100% of the time
_123_ is 1 100% of the time
_125_ is 0 100% of the time
_126_ is 1 100% of the time
_127_ is 0 100% of the time
_128_ is 1 100% of the time
_129_ is 0 100% of the time
_131_ is 1 100% of the time
_132_ is 1 100% of the time
_133_ is 0 100% of the time
_134_ is 1 100% of the time
_136_ is 0 100% of the time
_137_ is 1 100% of the time
_138_ is 0 100% of the time
_139_ is 1 100% of the time
_140_ is 0 100% of the time
_142_ is 1 100% of the time
_143_ is 1 100% of the time
_144_ is 0 100% of the time
_145_ is 1 100% of the time
_066_ is 1 75% of the time
_071_ is 0 75% of the time
_072_ is 1 75% of the time
_077_ is 1 75% of the time
_082_ is 0 75% of the tim
Simulation stage and error estimation are executed inside one method called simulate
. In order to execute a simulation you need to provide:
- The exact results
- The name of the approximated results file
- Error metric
- Lets start defining the names of the original and approximated results files. ORIGINAL must exists, while APPROX is the name of the file that will be produced by the testbench.
ORIGINAL='circuits/brent.kung.16b/output0.txt'
APPROX='circuits/brent.kung.16b/output.txt'
- Now we are ready to execute the simulation
error = our_circuit.simulate(TB, "med", ORIGINAL, APPROX)
print(error)
This should returns:
63.011
Files and Folders description:
Name | Description | Used |
---|---|---|
circuits | Contains the rtl and testbench of some sample circuits. | |
prunning_algorithms | Folder containing pruning techniques implementations. | |
inouts.py |
Contains the implementation of GetInputs and GetOutputs example pruning methods. |
|
probprun.py |
Contains the implementation of a pseudo Probabilistic Pruning method. GetOneNode is a python generator. It will retrieve one node to delete each time it is called. |
|
templates | Folder containing some libraries and scripts used for synthesis. | |
NanGate15nm.lib |
||
NanGate15nm.v |
||
synth.ys |
Script for synthesize a circuit using yosys. | |
__main__.py |
It executes the tool using the arguments from the command line. Still in progress. | No |
barcas.py |
Is the Pruning Implementation using the InOuts techniques. | |
circuit.py |
Object that represents a circuit as a XML tree. Receives a rtl and a library in order to build the circuit and be able to simulate it. | |
circuiterror.py |
Compares two outputs and computes different error metrics. | |
demo.py |
This file is a complete example of how the library should be used. | |
netlist.py |
This class parses, extracts and represents the circuit from rtl into an object understandable by python. | |
poisonoak.config |
This is going to be used along with __main__.py in order to execute poisonoak as an app, and not as a library. |
No |
poisonoak.help |
Contains the menu and tool description of the poison oak app. | No |
synthesis.py |
Executes the synthesis script (in our case yosys) and clean the intermediate files generated. At the end returns the path of the netlist. | |
technology.py |
This class parses, extracts and represents the technology library file into an object understandable by python. | |
test.py |
This class implements some unit tests for the poison oak library. Not implemented yet. | No |
utils.py |
Some functions not related with any other class but useful. |