/blifparser

A simple BLIF (berkeley logic interchange format) parser

Primary LanguagePythonMIT LicenseMIT

BLIFPARSER

This is a simple Python library that parses BLIF (Berkeley Logic Interchange Format) files (used by SIS, Sequential Interactive Synthesis).

And also evalutes if some keyword's parameters have syntactically correct values.

Only the basic keywords are parsed (.model, .inputs, .outputs, .names, all the FSM keywords, .latch, .exdc, .end).

More complex BLIF files with .clock, .gate, .mlatch, .clock_event and delay constraits are only partially parsed.

This is because the workflow I am supporting does not use these keywords

Currently only one .model keyword per file is supported.

You can see this as a "feature" because it forces the use of a good practise: use many files, one per each component.

You can also use this library as a basic BLIF validator.

Complex checks such as cross file definition checks and input-output names check are NOT implemented because the primary intent of this library is to parse BLIF files.



WARNING

This parser DOES NOT use grammar files NOR PEG for parsing blif files:

this means that the parsing could be "not perfect".

If someone wants to contribute and change this feel free to make pull requests!

If this library inspires you to write a better parser from scratch, please let me know by making a GH issue

This means that the library works because:

  • the BLIF (format) is simple: most of the time parameters are on the same line of the keywords
  • (some) unit and end to end tests were written


Index

Requirements

  • python 3 (>= 3.7)
  • networkx library: needed to create the graph (get_graph() method)
  • matplotlib library: needed for networkx

Installation

First, install the library using PIP:

pip install blifparser

Then:

  • if you want to use this library as a validation tool, check the "As a validator tool" section

  • if you want to use this library inside your software, check the "As a library" section

    If this is the only way you want to use this software, you can also install it using the installer on the Github Release page

Usage

As a validator tool

Execute the script using this command:

blifparser <input_path>

You can also execute it this way:

python -m blifparser <input_path>

Replace <input_path> with the path to the BLIF file to validate

When you have fixed the errors, execute the script until you have fixed all the errors.

Not all errors appear after the first script execution.

As a library

Basic/common usage:

# import the os library: useful to get the absolute path to the input file
import os
# import this library
import blifparser.blifparser as blifparser

# get the file path and pass it to the parser
filepath = os.path.abspath("example.blif")
parser = blifparser.BlifParser(filepath)

# get the object that contains the parsed data
# from the parser
blif = parser.blif

Now you can:

# get the name of the model
print(blif.model.name)

# get the list of the inputs
print(blif.inputs.inputs)

# get the list of the outputs
print(blif.outputs.outputs)

# get the list of .search keyword
print(blif.imports)

# get the imported file name/path of the first .search keyword
first_import = blif.imports[0]
print(first_import.filepath)

# get the list of subcircuits (.subckt)
print(blif.subcircuits)

# get data from the first subcircuit definition
first_subcircuit = blif.subcircuits[0]
print(first_subcircuit.modelname)  # name of the model
print(first_subcircuit.params)     # subcircuit's parameters

# get the list of boolean functions (.names)
print(blif.booleanfunctions)

# get data from the first boolean function definition
first_boolfunc = blif.booleanfunctions[0]
print(first_boolfunc.inputs)      # list with the names of the inputs
print(first_boolfunc.output)      # string with the name of the output
print(first_boolfunc.truthtable)  # list of lists (each row is a truth table row)

# get the dictionary with the number of occurrencies of each keyword
print(blif.nkeywords)

# get the list of problems/issues
print(blif.problems)

# get the list of the latches
print(blif.latches)

# get the data of the first latch
first_latch = blif.latches[0]
print(first_latch.input)    # name of the input
print(first_latch.output)   # name of the output
print(first_latch.type)     # type of latch (like "re", ...)
print(first_latch.control)  # clock name
print(first_latch.initval)  # initial value

# get the data of the FSM (Finite State Machine)
print(blif.fsm.i.num)       # number of inputs
print(blif.fsm.o.num)       # number of outputs
print(blif.fsm.s.num)       # number of states
print(blif.fsm.p.num)       # number of state transitions
print(blif.fsm.r.name)      # name of the reset state
print(blif.fsm.transtable)  # list of lists (contains the transition table)

You can also obtain a networkx graph using the get_graph() method:

# import the os library: useful to get the absolute path to the input file
import os
# import this library
import blifparser.blifparser as blifparser

# get the file path and pass it to the parser
filepath = os.path.abspath("example.blif")
parser = blifparser.BlifParser(filepath)

# generate the graph and memorize some statistics
graph_data = parser.get_graph()

# retrive networkx graph: can be used to export it as an image or customize it
nx_graph = graph_data.nx_graph

# extract nodes from the graph: each node has inputs, outputs, a type and the color that can be used to fill the node
# > by default .model inputs are red, .model outputs are blue, everything else is black
ext_nodes = [n for n in nx_graph.nodes]

# retrive the length of the longest label: can be used to determine the height of the graph exported onto an image
longest_label = nx_graph.longest_label

# retrive the size of the "item" node that has the most number of inputs: can be used to determine the width of the graph exported onto an image
# > where "item" is .inputs/.outputs/.names/.subckt/.latch
max_inputs = nx_graph.max_inputs

Description

These are the first steps to use this library:

# import the os library: useful to get the absolute path to the input file
import os
# import this library
import blifparser.blifparser as blifparser

# get the file path and pass it to the parser
filepath = os.path.abspath("example.blif")
parser = blifparser.BlifParser(filepath)

# get the object that contains the parsed data
# from the parser
blif = parser.blif

# get an object that contains a networkx graph of the blif file and memorize some statistics
graph_data = parser.get_graph()

The blifparser.BlifParser() object is the parser: it prepares the file for parsing using the prepare_file() method and then creates a keywords.generic.Blif() object that will contain all the information parsed from the file.

The prepare_file() method copies the file and then removes (on the copy):

  • the newlines made with the backslash "\"
  • the comments made with "#".

Then each line is read and parsed:

  • if the line contains a keyword, a "keyword" object is created and then "linked" to the keywords.generic.Blif() object (its parameters get parsed with the keyword)

    For example: if a .model keyword is found, a keywords.generic.Model() object gets created and set in the keywords.generic.Blif() object. (keywords.generic.Blif().model)

  • if the line contains text and the line comes after the .names keyword it is interpreted as the truth table of the latest boolean function (defined by .names)

    This behavior stops when the next keyword is found

  • if the line contains text and the line comes after the .start_kiss keyword it is interpreted as the transition table of the Finite State Machine.

    This behavior stops when the next keyword is found

  • If an unexpected text/keyword is found a "problem" or issue is collected inside the keywords.generic.Blif().problems list.

At the end of the parsing:

  • if an FSM was found, a validation step checks if it is syntactically correct
  • if some boolean functions were found, a validation step checks if they are syntactically correct

The other validation steps are executed during object definition

Now you can use the blif object to get the parsed data

Check the "Usage > As a library" section for more details

Changelog

2023-03-01 2.0.1:

  • fix dependencies: removed unused dependencies, made requirements.txt equal to setup.cfg dependencies
  • added more comments, types and unit tests
  • fix "Replacement index 1 out of range" error caused by missing string format parameter
  • check before accessing variables that might be None and lists that might be empty

2022-12-12 2.0.0:

  • Added get_graph() method to the BlifParser class: generates and returns an object containing a networkx graph with inputs, outputs, latches, subckts and boolean functions connected together.

    This object also contains statistics which can be used when exporting the graph to an image file to roughly determine the dimensions of the image.

  • Dropped python < 3.7 support.

2021-04-23 1.0.0:

First commit

Author

Zenaro Stefano (mario33881)