Skip to content

Commit 71a927a

Browse files
authored
Merge pull request #341 from pllab/blif-multi-module
Update blif importer to allow multi-model import
2 parents 4713260 + 57f1d41 commit 71a927a

File tree

2 files changed

+433
-90
lines changed

2 files changed

+433
-90
lines changed

pyrtl/inputoutput.py

Lines changed: 210 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,51 @@
1818
from .passes import two_way_concat, one_bit_selects
1919

2020

21+
class Subcircuit:
22+
"""
23+
This is a way to create and track per-module-instance wire names, so there
24+
are not name clashes when we instantiate a module more than once.
25+
"""
26+
27+
def __init__(self, model, is_top=False, clk_set={'clk'}, block=None):
28+
self.model = model
29+
self.is_top = is_top
30+
self.clk_set = clk_set
31+
self.block = working_block(block)
32+
self.inputs = {}
33+
self.outputs = {}
34+
self.wirevector_by_name = {}
35+
36+
def add_input(self, original_name, wire):
37+
self.inputs[original_name] = wire
38+
self.wirevector_by_name[original_name] = wire
39+
40+
def add_output(self, original_name, wire):
41+
self.outputs[original_name] = wire
42+
self.wirevector_by_name[original_name] = wire
43+
44+
def add_reg(self, original_name, wire):
45+
self.wirevector_by_name[original_name] = wire
46+
47+
def add_clock(self, clock_name):
48+
self.clk_set.add(clock_name)
49+
50+
def twire(self, x):
51+
""" Find or make wire named x and return it. """
52+
s = self.wirevector_by_name.get(x)
53+
if s is None:
54+
# Purposefully *not* setting its name to 'x', so we don't have name clashes
55+
s = WireVector(bitwidth=1)
56+
self.wirevector_by_name[x] = s
57+
return s
58+
2159
# -----------------------------------------------------------------
2260
# __ ___
2361
# | |\ | |__) | | |
2462
# | | \| | \__/ |
2563

2664

27-
def input_from_blif(blif, block=None, merge_io_vectors=True, clock_name='clk'):
65+
def input_from_blif(blif, block=None, merge_io_vectors=True, clock_name='clk', top_model=None):
2866
""" Read an open BLIF file or string as input, updating the block appropriately.
2967
3068
:param blif: An open BLIF file to read
@@ -33,6 +71,8 @@ def input_from_blif(blif, block=None, merge_io_vectors=True, clock_name='clk'):
3371
by a indexing subscript (e.g. 1-bit wires 'a[0]' and 'a[1]') will be combined
3472
into a single Input/Output (e.g. a 2-bit wire 'a').
3573
:param clock_name: The name of the clock (defaults to 'clk')
74+
:param top_model: name of top-level model to instantiate; if None, defaults to first model
75+
listed in the BLIF
3676
3777
If merge_io_vectors is True, then given 1-bit Input wires 'a[0]' and 'a[1]', these
3878
wires will be combined into a single 2-bit Input wire 'a' that can be accessed
@@ -67,29 +107,13 @@ def SKeyword(x):
67107
def SLiteral(x):
68108
return Suppress(Literal(x))
69109

70-
def twire(x):
71-
""" Find or make wire named x and return it. """
72-
s = block.get_wirevector_by_name(x)
73-
if s is None:
74-
s = WireVector(bitwidth=1, name=x)
75-
elif isinstance(s, Output):
76-
# To allow an output wire to be used as an argument (legal in BLIF),
77-
# use the intermediate wire that was created in its place.
78-
# extract_outputs() creates this intermediate wire. Must check for
79-
# merge_io_vectors first!
80-
if not merge_io_vectors:
81-
s = block.get_wirevector_by_name(x + '_i')
82-
elif len(s) == 1:
83-
s = block.get_wirevector_by_name(x + '[0]')
84-
return s
85-
86110
# Begin BLIF language definition
87-
signal_start = pyparsing.alphas + r'$:[]_<>\\\/?'
88-
signal_middle = pyparsing.alphas + pyparsing.nums + r'$:[]_<>\\\/.?'
111+
signal_start = pyparsing.alphas + '$:[]_<>\\/?'
112+
signal_middle = pyparsing.alphas + pyparsing.nums + '$:[]_<>\\/.?-'
89113
signal_id = Word(signal_start, signal_middle)
90114
header = SKeyword('.model') + signal_id('model_name')
91-
input_list = Group(SKeyword('.inputs') + OneOrMore(signal_id))('input_list')
92-
output_list = Group(SKeyword('.outputs') + OneOrMore(signal_id))('output_list')
115+
input_list = Group(SKeyword('.inputs') + ZeroOrMore(signal_id))('input_list')
116+
output_list = Group(SKeyword('.outputs') + ZeroOrMore(signal_id))('output_list')
93117

94118
cover_atom = Word('01-')
95119
cover_list = Group(ZeroOrMore(cover_atom))('cover_list')
@@ -113,7 +137,15 @@ def twire(x):
113137
+ SLiteral('re')
114138
+ signal_id('C')
115139
+ dffs_init_val('I'))('dffs_def')
116-
command_def = name_def | dffas_def | dffs_def
140+
141+
# model reference
142+
formal_actual = Group(signal_id('formal') + SLiteral('=')
143+
+ signal_id('actual'))('formal_actual')
144+
formal_actual_list = Group(OneOrMore(formal_actual))('formal_actual_list')
145+
model_name = signal_id('model_name')
146+
model_ref = Group(SKeyword('.subckt') + model_name + formal_actual_list)('model_ref')
147+
148+
command_def = name_def | dffas_def | dffs_def | model_ref
117149
command_list = Group(OneOrMore(command_def))('command_list')
118150

119151
footer = SKeyword('.end')
@@ -123,75 +155,115 @@ def twire(x):
123155

124156
# Begin actually reading and parsing the BLIF file
125157
result = parser.parseString(blif_string, parseAll=True)
126-
# Blif file with multiple models (currently only handles one flattened models)
127-
assert(len(result) == 1)
128-
clk_set = set([])
129158
ff_clk_set = set([])
130-
131-
def extract_inputs(model):
132-
start_names = [re.sub(r'\[([0-9]+)\]$', '', x) for x in model['input_list']]
133-
name_counts = collections.Counter(start_names)
134-
for input_name in name_counts:
135-
bitwidth = name_counts[input_name]
136-
if input_name == clock_name:
137-
clk_set.add(input_name)
138-
elif bitwidth == 1:
139-
block.add_wirevector(Input(bitwidth=1, name=input_name, block=block))
140-
elif merge_io_vectors:
141-
wire_in = Input(bitwidth=bitwidth, name=input_name, block=block)
142-
for i in range(bitwidth):
143-
bit_name = input_name + '[' + str(i) + ']'
144-
bit_wire = WireVector(bitwidth=1, name=bit_name, block=block)
145-
bit_wire <<= wire_in[i]
146-
else:
147-
for i in range(bitwidth):
148-
bit_name = input_name + '[' + str(i) + ']'
149-
block.add_wirevector(Input(bitwidth=1, name=bit_name, block=block))
150-
151-
def extract_outputs(model):
152-
start_names = [re.sub(r'\[([0-9]+)\]$', '', x) for x in model['output_list']]
153-
name_counts = collections.Counter(start_names)
154-
for output_name in name_counts:
155-
bitwidth = name_counts[output_name]
156-
# To allow an output wire to be used as an argument (legal in BLIF),
157-
# we need to create an intermediate wire, which will be used in twire()
158-
# whenever the original wire is referenced. For example, given 2-bit Output 'a',
159-
# every access to 'a[1]' will really be a reference to 'a[1]_i', a normal
160-
# WireVector connected to 'a[1]'. A key property is that the name by
161-
# which all other parts of the code refer to this wire doesn't change;
162-
# the only thing that changes is what underlying wire is used.
163-
if bitwidth == 1:
164-
bit_internal = WireVector(bitwidth=1, name=output_name + '[0]', block=block)
165-
bit_out = Output(bitwidth=1, name=output_name, block=block)
166-
bit_out <<= bit_internal
167-
elif merge_io_vectors:
168-
wire_out = Output(bitwidth=bitwidth, name=output_name, block=block)
169-
bit_list = []
170-
for i in range(bitwidth):
171-
bit_name = output_name + '[' + str(i) + ']'
172-
bit_wire = WireVector(bitwidth=1, name=bit_name, block=block)
173-
bit_list.append(bit_wire)
174-
wire_out <<= concat_list(bit_list)
175-
else:
176-
for i in range(bitwidth):
177-
bit_name = output_name + '[' + str(i) + ']'
178-
bit_internal = WireVector(bitwidth=1, name=bit_name + '_i', block=block)
179-
bit_out = Output(bitwidth=1, name=bit_name, block=block)
159+
models = {} # model name -> model, for subckt instantiation
160+
161+
def extract_inputs(subckt):
162+
if subckt.is_top:
163+
# NOTE: Assumes that:
164+
# - Top-level inputs starting with the same prefix are part of the same wire
165+
# - Indices start at 0
166+
start_names = [re.sub(r'\[([0-9]+)\]$', '', x) for x in subckt.model['input_list']]
167+
name_counts = collections.Counter(start_names)
168+
for input_name in name_counts:
169+
bitwidth = name_counts[input_name]
170+
if input_name in subckt.clk_set:
171+
continue
172+
elif bitwidth == 1:
173+
wire_in = Input(bitwidth=1, name=input_name, block=block)
174+
subckt.add_input(input_name, wire_in)
175+
block.add_wirevector(wire_in)
176+
elif merge_io_vectors:
177+
wire_in = Input(bitwidth=bitwidth, name=input_name, block=block)
178+
for i in range(bitwidth):
179+
bit_name = input_name + '[' + str(i) + ']'
180+
bit_wire = WireVector(bitwidth=1, block=block)
181+
bit_wire <<= wire_in[i]
182+
subckt.add_input(bit_name, bit_wire)
183+
else:
184+
for i in range(bitwidth):
185+
bit_name = input_name + '[' + str(i) + ']'
186+
wire_in = Input(bitwidth=1, name=bit_name, block=block)
187+
subckt.add_input(bit_name, wire_in)
188+
block.add_wirevector(wire_in)
189+
else:
190+
# For subckts:
191+
# - Never merge input vectors
192+
# - All inputs are 1-bit
193+
for input_name in subckt.model['input_list']:
194+
if input_name in subckt.clk_set:
195+
continue
196+
wire_in = WireVector(bitwidth=1, block=block) # Internal name prevents name clash
197+
subckt.add_input(input_name, wire_in)
198+
block.add_wirevector(wire_in)
199+
200+
def extract_outputs(subckt):
201+
if subckt.is_top:
202+
# NOTE: Assumes that:
203+
# - Top-level outputs starting with the same prefix are part of the same wire
204+
# - Indices start at 0
205+
start_names = [re.sub(r'\[([0-9]+)\]$', '', x) for x in subckt.model['output_list']]
206+
name_counts = collections.Counter(start_names)
207+
for output_name in name_counts:
208+
bitwidth = name_counts[output_name]
209+
# To allow an output wire to be used as an argument (legal in BLIF),
210+
# we need to create an intermediate wire, which will be used in twire()
211+
# whenever the original wire is referenced. For example, given 2-bit Output 'a',
212+
# every access to 'a[1]' will really be a reference to 'a[1]_i', a normal
213+
# WireVector connected to 'a[1]'. A key property is that the name by
214+
# which all other parts of the code refer to this wire doesn't change;
215+
# the only thing that changes is what underlying wire is used.
216+
if bitwidth == 1:
217+
bit_internal = WireVector(bitwidth=1, block=block)
218+
bit_out = Output(bitwidth=1, name=output_name, block=block)
180219
bit_out <<= bit_internal
181-
182-
def extract_commands(model):
220+
# NOTE this is important: redirecting user-visible name to internal wire
221+
subckt.add_output(output_name, bit_internal)
222+
elif merge_io_vectors:
223+
wire_out = Output(bitwidth=bitwidth, name=output_name, block=block)
224+
bit_list = []
225+
for i in range(bitwidth):
226+
bit_name = output_name + '[' + str(i) + ']'
227+
bit_wire = WireVector(bitwidth=1, block=block)
228+
bit_list.append(bit_wire)
229+
subckt.add_output(bit_name, bit_wire)
230+
wire_out <<= concat_list(bit_list)
231+
else:
232+
for i in range(bitwidth):
233+
bit_name = output_name + '[' + str(i) + ']'
234+
bit_internal = WireVector(bitwidth=1, block=block)
235+
bit_out = Output(bitwidth=1, name=bit_name, block=block)
236+
bit_out <<= bit_internal
237+
# NOTE this is important: redirecting user-visible name to internal wire
238+
subckt.add_output(bit_name, bit_internal)
239+
else:
240+
# For subckts:
241+
# - Never merge outputs vectors
242+
# - All outputs are 1-bit
243+
for output_name in subckt.model['output_list']:
244+
bit_out = WireVector(bitwidth=1, block=block)
245+
block.add_wirevector(bit_out)
246+
subckt.add_output(output_name, bit_out)
247+
248+
def extract_commands(subckt):
183249
# for each "command" (dff or net) in the model
184-
for command in model['command_list']:
250+
for command in subckt.model['command_list']:
185251
# if it is a net (specified as a cover)
186252
if command.getName() == 'name_def':
187-
extract_cover(command)
253+
extract_cover(subckt, command)
188254
# else if the command is a d flop flop
189255
elif command.getName() == 'dffas_def' or command.getName() == 'dffs_def':
190-
extract_flop(command)
256+
extract_flop(subckt, command)
257+
elif command.getName() == 'model_ref':
258+
extract_model_reference(subckt, command)
191259
else:
192260
raise PyrtlError('unknown command type')
193261

194-
def extract_cover(command):
262+
def extract_cover(subckt, command):
263+
264+
def twire(w):
265+
return subckt.twire(w)
266+
195267
# pylint: disable=invalid-unary-operand-type
196268
netio = command['namesignal_list']
197269
if len(command['cover_list']) == 0:
@@ -202,10 +274,10 @@ def extract_cover(command):
202274
output_wire <<= Const(1, bitwidth=1, block=block) # const "TRUE"
203275
elif command['cover_list'].asList() == ['1', '1']:
204276
# Populate clock list if one input is already a clock
205-
if(netio[1] in clk_set):
206-
clk_set.add(netio[0])
207-
elif(netio[0] in clk_set):
208-
clk_set.add(netio[1])
277+
if (netio[1] in subckt.clk_set):
278+
subckt.add_clock(netio[0])
279+
elif (netio[0] in subckt.clk_set):
280+
subckt.add_clock(netio[1])
209281
else:
210282
output_wire = twire(netio[1])
211283
output_wire <<= twire(netio[0]) # simple wire
@@ -236,13 +308,18 @@ def extract_cover(command):
236308
raise PyrtlError('Blif file with unknown logic cover set "%s"'
237309
'(currently gates are hard coded)' % command['cover_list'])
238310

239-
def extract_flop(command):
311+
def extract_flop(subckt, command):
312+
313+
def twire(w):
314+
return subckt.twire(w)
315+
240316
if(command['C'] not in ff_clk_set):
241317
ff_clk_set.add(command['C'])
242318

243319
# Create register and assign next state to D and output to Q
244320
regname = command['Q'] + '_reg'
245-
flop = Register(bitwidth=1, name=regname)
321+
flop = Register(bitwidth=1)
322+
subckt.add_reg(regname, flop)
246323
flop.next <<= twire(command['D'])
247324
flop_output = twire(command['Q'])
248325
init_val = command['I']
@@ -255,10 +332,53 @@ def extract_flop(command):
255332
"logic.")
256333
flop_output <<= flop
257334

335+
def extract_model_reference(parent, command):
336+
337+
def twire(w):
338+
return parent.twire(w)
339+
340+
def get_formal_connected_to_parent_clocks():
341+
clks = set()
342+
for fa in command['formal_actual_list']:
343+
if fa['actual'] in parent.clk_set:
344+
clks.add(fa['formal'])
345+
return clks
346+
formal_clks = get_formal_connected_to_parent_clocks()
347+
348+
subckt = Subcircuit(models[command['model_name']], clk_set=formal_clks, block=block)
349+
instantiate(subckt)
350+
for fa in command['formal_actual_list']:
351+
formal = fa['formal']
352+
actual = fa['actual']
353+
if actual in parent.clk_set:
354+
assert(formal in subckt.clk_set)
355+
# We didn't create an input wire corresponding to this.
356+
continue
357+
elif formal in subckt.inputs:
358+
wf = subckt.inputs[formal]
359+
wa = twire(actual)
360+
wf <<= wa
361+
elif formal in subckt.outputs:
362+
wf = subckt.outputs[formal]
363+
wa = twire(actual)
364+
wa <<= wf
365+
else:
366+
raise PyrtlError("%s formal parameter is neither an input nor output of subckt %s"
367+
% (formal, command['model_name']))
368+
369+
def instantiate(subckt):
370+
extract_inputs(subckt)
371+
extract_outputs(subckt)
372+
extract_commands(subckt)
373+
374+
# Get all model definitions
258375
for model in result:
259-
extract_inputs(model)
260-
extract_outputs(model)
261-
extract_commands(model)
376+
if not top_model:
377+
top_model = model['model_name']
378+
models[model['model_name']] = model
379+
380+
top = Subcircuit(models[top_model], is_top=True, clk_set={clock_name}, block=block)
381+
instantiate(top)
262382

263383

264384
# ----------------------------------------------------------------

0 commit comments

Comments
 (0)