Skip to content

Commit 6bf99c3

Browse files
committed
Merge branch 'development' of https://github.com/UCSBarchlab/PyRTL into development
2 parents 06abced + 73fc1d2 commit 6bf99c3

File tree

2 files changed

+103
-15
lines changed

2 files changed

+103
-15
lines changed

pyrtl/inputoutput.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,15 @@ def twire(x):
5858
s = block.get_wirevector_by_name(x)
5959
if s is None:
6060
s = WireVector(bitwidth=1, name=x)
61-
if isinstance(s, Output) and (merge_io_vectors or len(x) == 1):
61+
elif isinstance(s, Output):
6262
# To allow an output wire to be used as an argument (legal in BLIF),
63-
# use the intermediate wire that was created in its place. extract_outputs()
64-
# creates this intermediate wire.
65-
s = block.get_wirevector_by_name(x + '[0]')
63+
# use the intermediate wire that was created in its place.
64+
# extract_outputs() creates this intermediate wire. Must check for
65+
# merge_io_vectors first!
66+
if not merge_io_vectors:
67+
s = block.get_wirevector_by_name(x + '_i')
68+
elif len(s) == 1:
69+
s = block.get_wirevector_by_name(x + '[0]')
6670
return s
6771

6872
# Begin BLIF language definition
@@ -117,35 +121,49 @@ def extract_inputs(model):
117121
bitwidth = name_counts[input_name]
118122
if input_name == clock_name:
119123
clk_set.add(input_name)
120-
elif not merge_io_vectors or bitwidth == 1:
121-
block.add_wirevector(Input(bitwidth=1, name=input_name))
122-
else:
124+
elif bitwidth == 1:
125+
block.add_wirevector(Input(bitwidth=1, name=input_name, block=block))
126+
elif merge_io_vectors:
123127
wire_in = Input(bitwidth=bitwidth, name=input_name, block=block)
124128
for i in range(bitwidth):
125129
bit_name = input_name + '[' + str(i) + ']'
126130
bit_wire = WireVector(bitwidth=1, name=bit_name, block=block)
127131
bit_wire <<= wire_in[i]
132+
else:
133+
for i in range(bitwidth):
134+
bit_name = input_name + '[' + str(i) + ']'
135+
block.add_wirevector(Input(bitwidth=1, name=bit_name, block=block))
128136

129137
def extract_outputs(model):
130138
start_names = [re.sub(r'\[([0-9]+)\]$', '', x) for x in model['output_list']]
131139
name_counts = collections.Counter(start_names)
132140
for output_name in name_counts:
133141
bitwidth = name_counts[output_name]
134-
if not merge_io_vectors or bitwidth == 1:
135-
# To allow an output wire to be used as an argument (legal in BLIF),
136-
# create an intermediate wire that will be used in its place. twire()
137-
# checks for this and uses the intermediate wire when needed
138-
w = WireVector(bitwidth=1, name=output_name + '[0]')
139-
out = Output(bitwidth=1, name=output_name)
140-
out <<= w
141-
else:
142+
# To allow an output wire to be used as an argument (legal in BLIF),
143+
# we need to create an intermediate wire, which will be used in twire()
144+
# whenever the original wire is referenced. For example, given 2-bit Output 'a',
145+
# every access to 'a[1]' will really be a reference to 'a[1]_i', a normal
146+
# WireVector connected to 'a[1]'. A key property is that the name by
147+
# which all other parts of the code refer to this wire doesn't change;
148+
# the only thing that changes is what underlying wire is used.
149+
if bitwidth == 1:
150+
bit_internal = WireVector(bitwidth=1, name=output_name + '[0]', block=block)
151+
bit_out = Output(bitwidth=1, name=output_name, block=block)
152+
bit_out <<= bit_internal
153+
elif merge_io_vectors:
142154
wire_out = Output(bitwidth=bitwidth, name=output_name, block=block)
143155
bit_list = []
144156
for i in range(bitwidth):
145157
bit_name = output_name + '[' + str(i) + ']'
146158
bit_wire = WireVector(bitwidth=1, name=bit_name, block=block)
147159
bit_list.append(bit_wire)
148160
wire_out <<= concat_list(bit_list)
161+
else:
162+
for i in range(bitwidth):
163+
bit_name = output_name + '[' + str(i) + ']'
164+
bit_internal = WireVector(bitwidth=1, name=bit_name + '_i', block=block)
165+
bit_out = Output(bitwidth=1, name=bit_name, block=block)
166+
bit_out <<= bit_internal
149167

150168
def extract_commands(model):
151169
# for each "command" (dff or net) in the model

tests/test_inputoutput.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,23 @@
238238
.end
239239
""" # noqa
240240

241+
simple_unmerged_io_blif = """
242+
# Generated by Yosys 0.9+2406 (git sha1 aee43936, clang 11.0.3 -fPIC -Os)
243+
244+
.model top
245+
.inputs a[0] a[1] a[2] a[3]
246+
.outputs b[0] b[1]
247+
.names $false
248+
.names $true
249+
1
250+
.names $undef
251+
.names a[0] b[0]
252+
1 1
253+
.names a[2] b[1]
254+
1 1
255+
.end
256+
""" # noqa
257+
241258

242259
class TestInputFromBlif(unittest.TestCase):
243260
def setUp(self):
@@ -304,6 +321,21 @@ def test_sequential_blif_input_has_correct_io_interface_counter(self):
304321
io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
305322
self.assertIn(count, io_output)
306323

324+
def test_correct_interface_with_unmerged_io(self):
325+
pyrtl.input_from_blif(simple_unmerged_io_blif, merge_io_vectors=False)
326+
a0, a1, a2, a3, b0, b1 = [
327+
pyrtl.working_block().get_wirevector_by_name(s)
328+
for s in ['a[0]', 'a[1]', 'a[2]', 'a[3]', 'b[0]', 'b[1]']
329+
]
330+
self.assertEquals(len(a0), 1)
331+
self.assertEquals(len(a1), 1)
332+
self.assertEquals(len(a2), 1)
333+
self.assertEquals(len(a3), 1)
334+
self.assertEquals(len(b0), 1)
335+
self.assertEquals(len(b1), 1)
336+
self.assertEquals({a0, a1, a2, a3}, pyrtl.working_block().wirevector_subset(pyrtl.Input))
337+
self.assertEquals({b0, b1}, pyrtl.working_block().wirevector_subset(pyrtl.Output))
338+
307339
def test_blif_input_simulates_correctly_with_merged_outputs(self):
308340
# The 'counter_blif' string contains a model of a standard 4-bit synchronous-reset
309341
# counter with enable. In particular, the model has 4 1-bit outputs named "count[0]",
@@ -332,6 +364,44 @@ def test_blif_input_simulates_correctly_with_merged_outputs(self):
332364
sim_trace.print_trace(output)
333365
self.assertEqual(output.getvalue(), correct_output)
334366

367+
def test_blif_input_simulates_correctly_with_unmerged_outputs(self):
368+
pyrtl.input_from_blif(counter4bit_blif, merge_io_vectors=False)
369+
count0, count1, count2, count3 = [
370+
pyrtl.working_block().get_wirevector_by_name(s)
371+
for s in ['count[0]', 'count[1]', 'count[2]', 'count[3]']
372+
]
373+
self.assertEquals(len(count0), 1)
374+
self.assertEquals(len(count1), 1)
375+
self.assertEquals(len(count2), 1)
376+
self.assertEquals(len(count3), 1)
377+
io_vectors = pyrtl.working_block().wirevector_subset((pyrtl.Input, pyrtl.Output))
378+
sim_trace = pyrtl.SimulationTrace(wires_to_track=io_vectors)
379+
sim = pyrtl.Simulation(sim_trace)
380+
inputs = {
381+
'rst': [1] + [0] * 20,
382+
'en': [1] + [1] * 20,
383+
}
384+
expected_merged = [0] + list(range(0, 16)) + list(range(0, 4))
385+
386+
expected = {
387+
'count[0]': [n & 0b0001 for n in expected_merged],
388+
'count[1]': [(n & 0b0010) >> 1 for n in expected_merged],
389+
'count[2]': [(n & 0b0100) >> 2 for n in expected_merged],
390+
'count[3]': [(n & 0b1000) >> 3 for n in expected_merged],
391+
}
392+
sim.step_multiple(inputs, expected)
393+
394+
correct_output = (" --- Values in base 10 ---\n"
395+
"count[0] 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1\n"
396+
"count[1] 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1\n"
397+
"count[2] 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0\n"
398+
"count[3] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0\n"
399+
"en 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\n"
400+
"rst 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n")
401+
output = six.StringIO()
402+
sim_trace.print_trace(output)
403+
self.assertEqual(output.getvalue(), correct_output)
404+
335405
def test_sequential_blif_input_fails_to_parse_with_bad_latch_init(self):
336406
with self.assertRaises(pyrtl.PyrtlError):
337407
pyrtl.input_from_blif(counter4bit_blif_bad_latch_inits)

0 commit comments

Comments
 (0)