Skip to content

Commit 9dc3fdb

Browse files
committed
Simplify BLIF cover import to be more programmatic
1 parent 06505b5 commit 9dc3fdb

File tree

2 files changed

+173
-14
lines changed

2 files changed

+173
-14
lines changed

pyrtl/importexport.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .pyrtlexceptions import PyrtlError, PyrtlInternalError
1414
from .core import working_block, _NameSanitizer
1515
from .wire import WireVector, Input, Output, Const, Register, next_tempvar_name
16-
from .corecircuits import concat_list
16+
from .corecircuits import concat_list, rtl_all, rtl_any
1717
from .memory import RomBlock
1818
from .passes import two_way_concat, one_bit_selects
1919

@@ -315,26 +315,42 @@ def twire(w):
315315
elif command['cover_list'].asList() == ['11', '1']:
316316
output_wire = twire(netio[2])
317317
output_wire <<= twire(netio[0]) & twire(netio[1]) # and gate
318-
elif command['cover_list'].asList() == ['00', '1']:
319-
output_wire = twire(netio[2])
320-
output_wire <<= ~ (twire(netio[0]) | twire(netio[1])) # nor gate
321318
elif command['cover_list'].asList() == ['1-', '1', '-1', '1']:
322319
output_wire = twire(netio[2])
323320
output_wire <<= twire(netio[0]) | twire(netio[1]) # or gate
321+
elif command['cover_list'].asList() == ['0-', '1', '-0', '1']:
322+
output_wire = twire(netio[2])
323+
output_wire <<= twire(netio[0]).nand(twire(netio[1])) # nand gate
324324
elif command['cover_list'].asList() == ['10', '1', '01', '1']:
325325
output_wire = twire(netio[2])
326326
output_wire <<= twire(netio[0]) ^ twire(netio[1]) # xor gate
327-
elif command['cover_list'].asList() == ['1-0', '1', '-11', '1']:
328-
output_wire = twire(netio[3])
329-
output_wire <<= (twire(netio[0]) & ~ twire(netio[2])) \
330-
| (twire(netio[1]) & twire(netio[2])) # mux
331-
elif command['cover_list'].asList() == ['-00', '1', '0-0', '1']:
332-
output_wire = twire(netio[3])
333-
output_wire <<= (~twire(netio[1]) & ~twire(netio[2])) \
334-
| (~twire(netio[0]) & ~twire(netio[2]))
335327
else:
336-
raise PyrtlError('Blif file with unknown logic cover set "%s" '
337-
'(currently gates are hard coded)' % command['cover_list'])
328+
# Although the following is fully generic and thus encompasses all of the
329+
# special cases after the simple wire case above, we leave the above in because
330+
# they are commonly found and lead to a slightly cleaner (though equivalent) netlist,
331+
# because we can use nand/xor primitives, or avoid the extra fluff of concat/select
332+
# wires that might be created implicitly as part of rtl_all/rtl_any.
333+
def convert_val(ix, val):
334+
wire = twire(netio[ix])
335+
if val == '0':
336+
wire = ~wire
337+
return wire
338+
339+
cover = command['cover_list'].asList()
340+
output_wire = twire(netio[-1])
341+
conjunctions = []
342+
while cover:
343+
if len(cover) < 2:
344+
raise PyrtlError('BLIF file with malformed cover set "%s" '
345+
% command['cover_list'])
346+
input_plane, output_plane, cover = cover[0], cover[1], cover[2:]
347+
if output_plane != '1':
348+
raise PyrtlError('Off-set found in the output plane of BLIF cover set "%s" '
349+
'(only on-sets are supported)' % command['cover_list'])
350+
conj = rtl_all(*[convert_val(ix, val) for ix, val
351+
in enumerate(input_plane) if val != '-'])
352+
conjunctions.append(conj)
353+
output_wire <<= rtl_any(*conjunctions)
338354

339355
def extract_flop(subckt, command):
340356

tests/test_importexport.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,149 @@ def test_blif_with_clock_passing(self):
581581
cvals = sim.tracer.trace[c.name]
582582
self.assertEqual(cvals, [0, 0, 0, 1, 1, 1, 0])
583583

584+
def test_blif_error_zeroes_in_offset(self):
585+
zeroes_in_offset = """\
586+
.model Top
587+
.inputs clk in[0] in[1]
588+
.outputs out
589+
.names in[0] in[1] out
590+
10 0
591+
.end
592+
"""
593+
594+
with self.assertRaisesRegex(pyrtl.PyrtlError, "Off-set found"):
595+
pyrtl.input_from_blif(zeroes_in_offset)
596+
597+
def test_blif_error_bad_coverset(self):
598+
bad_coverset = """\
599+
.model Top
600+
.inputs clk in[0] in[1]
601+
.outputs out
602+
.names in[0] in[1] out
603+
10 1 1
604+
.end
605+
"""
606+
with self.assertRaisesRegex(pyrtl.PyrtlError, "malformed cover set"):
607+
pyrtl.input_from_blif(bad_coverset)
608+
609+
def test_blif_not_gate_correct(self):
610+
blif = """\
611+
.model Top
612+
.inputs a
613+
.outputs o
614+
.names a o
615+
0 1
616+
.end
617+
"""
618+
pyrtl.input_from_blif(blif)
619+
block = pyrtl.working_block()
620+
self.assertEqual(len(block.logic_subset('~')), 1)
621+
sim = pyrtl.Simulation()
622+
sim.step_multiple({
623+
'a': '01',
624+
})
625+
self.assertEqual(sim.tracer.trace['o'], [1, 0])
626+
627+
def test_blif_and_gate_correct(self):
628+
blif = """\
629+
.model Top
630+
.inputs a b
631+
.outputs o
632+
.names a b o
633+
11 1
634+
.end
635+
"""
636+
pyrtl.input_from_blif(blif)
637+
block = pyrtl.working_block()
638+
self.assertEqual(len(block.logic_subset('&')), 1)
639+
sim = pyrtl.Simulation()
640+
sim.step_multiple({
641+
'a': '0011',
642+
'b': '0101',
643+
})
644+
self.assertEqual(sim.tracer.trace['o'], [0, 0, 0, 1])
645+
646+
def test_blif_or_gate_correct(self):
647+
blif = """\
648+
.model Top
649+
.inputs a b
650+
.outputs o
651+
.names a b o
652+
1- 1
653+
-1 1
654+
.end
655+
"""
656+
pyrtl.input_from_blif(blif)
657+
block = pyrtl.working_block()
658+
self.assertEqual(len(block.logic_subset('|')), 1)
659+
sim = pyrtl.Simulation()
660+
sim.step_multiple({
661+
'a': '0011',
662+
'b': '0101',
663+
})
664+
self.assertEqual(sim.tracer.trace['o'], [0, 1, 1, 1])
665+
666+
def test_blif_nand_gate_correct(self):
667+
blif = """\
668+
.model Top
669+
.inputs a b
670+
.outputs o
671+
.names a b o
672+
0- 1
673+
-0 1
674+
.end
675+
"""
676+
pyrtl.input_from_blif(blif)
677+
block = pyrtl.working_block()
678+
self.assertEqual(len(block.logic_subset('n')), 1)
679+
sim = pyrtl.Simulation()
680+
sim.step_multiple({
681+
'a': '0011',
682+
'b': '0101',
683+
})
684+
self.assertEqual(sim.tracer.trace['o'], [1, 1, 1, 0])
685+
686+
def test_blif_xor_gate_correct(self):
687+
blif = """\
688+
.model Top
689+
.inputs a b
690+
.outputs o
691+
.names a b o
692+
10 1
693+
01 1
694+
.end
695+
"""
696+
pyrtl.input_from_blif(blif)
697+
block = pyrtl.working_block()
698+
self.assertEqual(len(block.logic_subset('^')), 1)
699+
sim = pyrtl.Simulation()
700+
sim.step_multiple({
701+
'a': '0011',
702+
'b': '0101',
703+
})
704+
self.assertEqual(sim.tracer.trace['o'], [0, 1, 1, 0])
705+
706+
def test_blif_nor_gate_correct(self):
707+
# This is a non-primitive, so tests the last branch of cover list parsing
708+
blif = """\
709+
.model Top
710+
.inputs a b
711+
.outputs o
712+
.names a b o
713+
00 1
714+
.end
715+
"""
716+
pyrtl.input_from_blif(blif)
717+
block = pyrtl.working_block()
718+
self.assertEqual(len(block.logic_subset('~')), 2)
719+
self.assertEqual(len(block.logic_subset('&')), 1)
720+
sim = pyrtl.Simulation()
721+
sim.step_multiple({
722+
'a': '0011',
723+
'b': '0101',
724+
})
725+
self.assertEqual(sim.tracer.trace['o'], [1, 0, 0, 0])
726+
584727

585728
verilog_output_small = """\
586729
// Generated automatically via PyRTL

0 commit comments

Comments
 (0)