Skip to content

Commit 2d3781b

Browse files
authored
Merge pull request #372 from pllab/iscas_bench
Add ISCAS benchmark importer
2 parents 7b357f1 + db4de4a commit 2d3781b

File tree

3 files changed

+568
-1
lines changed

3 files changed

+568
-1
lines changed

pyrtl/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
from .importexport import output_verilog_testbench
9898
from .importexport import input_from_blif
9999
from .importexport import output_to_firrtl
100+
from .importexport import input_from_iscas_bench
100101

101102
# different transform passes
102103
from .passes import common_subexp_elimination

pyrtl/importexport.py

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def input_from_blif(blif, block=None, merge_io_vectors=True, clock_name='clk', t
127127
if isinstance(blif, six.string_types):
128128
blif_string = blif
129129
else:
130-
raise PyrtlError('input_blif expecting either open file or string')
130+
raise PyrtlError('input_from_blif expecting either open file or string')
131131

132132
def SKeyword(x):
133133
return Suppress(Keyword(x))
@@ -947,3 +947,111 @@ def output_to_firrtl(open_file, rom_blocks=None, block=None):
947947
pass
948948

949949
return 0
950+
951+
# -----------------------------------------------------------------
952+
# __ __ __ __ ___ __
953+
# | /__` / /\ /__` |__) |__ |\ | / |__|
954+
# | .__/ \__ /~~\ .__/ |__) |___ | \| \__ | |
955+
956+
957+
def input_from_iscas_bench(bench, block=None):
958+
''' Import an ISCAS .bench file
959+
960+
:param file bench: an open ISCAS .bench file to read
961+
:param block: block to add the imported logic (defaults to current working block)
962+
'''
963+
964+
import pyparsing
965+
import six
966+
from pyparsing import (Word, Literal, OneOrMore, ZeroOrMore, Suppress, Group, Keyword, oneOf)
967+
968+
block = working_block(block)
969+
970+
try:
971+
bench_string = bench.read()
972+
except AttributeError:
973+
if isinstance(bench, six.string_types):
974+
bench_string = bench
975+
else:
976+
raise PyrtlError('input_from_bench expecting either open file or string')
977+
978+
def SKeyword(x):
979+
return Suppress(Keyword(x))
980+
981+
def SLiteral(x):
982+
return Suppress(Literal(x))
983+
984+
# NOTE: The acceptable signal characters are based on viewing the I/O names
985+
# in the available ISCAS benchmark files, and may not be complete.
986+
signal_start = pyparsing.alphas + pyparsing.nums + '$:[]_<>\\/?'
987+
signal_middle = pyparsing.alphas + pyparsing.nums + '$:[]_<>\\/.?-'
988+
signal_id = Word(signal_start, signal_middle)
989+
990+
gate_names = "AND OR NAND NOR XOR NOT BUFF DFF"
991+
992+
src_list = Group(signal_id + ZeroOrMore(SLiteral(",") + signal_id))("src_list")
993+
net_def = Group(signal_id("dst") + SLiteral("=") + oneOf(gate_names)("gate")
994+
+ SLiteral("(") + src_list + SLiteral(")"))("net_def")
995+
996+
input_def = Group(SKeyword("INPUT") + SLiteral("(")
997+
+ signal_id + SLiteral(")"))("input_def")
998+
output_def = Group(SKeyword("OUTPUT") + SLiteral("(")
999+
+ signal_id + SLiteral(")"))("output_def")
1000+
command_def = input_def | output_def | net_def
1001+
1002+
commands = OneOrMore(command_def)("command_list")
1003+
parser = commands.ignore(pyparsing.pythonStyleComment)
1004+
1005+
# Begin actually reading and parsing the BENCH file
1006+
result = parser.parseString(bench_string, parseAll=True)
1007+
1008+
output_to_internal = {} # dict: name -> wire
1009+
1010+
def twire(name):
1011+
""" Find or make wire named 'name' and return it. """
1012+
w = output_to_internal.get(name)
1013+
if w is None:
1014+
w = block.wirevector_by_name.get(name)
1015+
if w is None:
1016+
w = WireVector(bitwidth=1, name=name)
1017+
return w
1018+
1019+
for cmd in result["command_list"]:
1020+
if cmd.getName() == "input_def":
1021+
_wire_in = Input(bitwidth=1, name=str(cmd[0]), block=block)
1022+
elif cmd.getName() == "output_def":
1023+
# Create internal wire for indirection, since Outputs can't be inputs to nets in PyRTL
1024+
wire_internal = WireVector(bitwidth=1, block=block)
1025+
wire_out = Output(bitwidth=1, name=str(cmd[0]), block=block)
1026+
wire_out <<= wire_internal
1027+
output_to_internal[cmd[0]] = wire_internal
1028+
elif cmd.getName() == "net_def":
1029+
srcs = cmd["src_list"]
1030+
if cmd["gate"] == "AND":
1031+
dst_wire = twire(cmd["dst"])
1032+
dst_wire <<= twire(srcs[0]) & twire(srcs[1])
1033+
elif cmd["gate"] == "OR":
1034+
dst_wire = twire(cmd["dst"])
1035+
dst_wire <<= twire(srcs[0]) | twire(srcs[1])
1036+
elif cmd["gate"] == "NAND":
1037+
dst_wire = twire(cmd["dst"])
1038+
dst_wire <<= twire(srcs[0]).nand(twire(srcs[1]))
1039+
elif cmd["gate"] == "NOR":
1040+
dst_wire = twire(cmd["dst"])
1041+
dst_wire <<= ~(twire(srcs[0]) | twire(srcs[1]))
1042+
elif cmd["gate"] == "XOR":
1043+
dst_wire = twire(cmd["dst"])
1044+
dst_wire <<= twire(srcs[0]) ^ twire(srcs[1])
1045+
elif cmd["gate"] == "NOT":
1046+
dst_wire = twire(cmd["dst"])
1047+
dst_wire <<= ~twire(srcs[0])
1048+
elif cmd["gate"] == "BUFF":
1049+
dst_wire = twire(cmd["dst"])
1050+
dst_wire <<= twire(srcs[0])
1051+
elif cmd["gate"] == "DFF":
1052+
dst_wire = twire(cmd["dst"])
1053+
reg = Register(bitwidth=1)
1054+
reg.next <<= twire(srcs[0])
1055+
dst_wire <<= reg
1056+
else:
1057+
raise PyrtlError("Unexpected gate {%s}" % cmd["gate"])

0 commit comments

Comments
 (0)