def make_example_graph_2(): in1, in2 = pyrtl.Input(16, 'in1'), pyrtl.Input(16, 'in2') out = pyrtl.Output(17, 'output') out <<= adders.cla_adder(in1, in2) pyrtl.synthesize() pyrtl.optimize()
def test_longer_wires(self): a = pyrtl.Input(3, 'a') b = pyrtl.Input(2, 'b') out = pyrtl.Output(name='out') out <<= a + b pyrtl.synthesize() self.assertTrue(simulators.circuit_equivalence(lambda i, j: i + j, in_wires=(a, b)))
def test_function_RomBlock_with_optimization(self): def rom_data_function(add): return int((add + 5)/2) pyrtl.reset_working_block() self.bitwidth = 4 self.addrwidth = 4 self.output1 = pyrtl.Output(self.bitwidth, "o1") self.output2 = pyrtl.Output(self.bitwidth, "o2") self.read_addr1 = pyrtl.Input(self.addrwidth) self.read_addr2 = pyrtl.Input(self.addrwidth) self.rom = pyrtl.RomBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth, name='rom', romdata=rom_data_function) self.output1 <<= self.rom[self.read_addr1] self.output2 <<= self.rom[self.read_addr2] pyrtl.synthesize() pyrtl.optimize() # build the actual simulation environment self.sim_trace = pyrtl.SimulationTrace() self.sim = self.sim(tracer=self.sim_trace) input_signals = {} for i in range(0, 5): input_signals[i] = {self.read_addr1: i, self.read_addr2: 2*i} input_signals[i] = {self.read_addr1: i, self.read_addr2: 2*i} self.sim.step(input_signals[i]) # exp_out = self.generate_expected_output((("o1", lambda x: rom_data_function(x) - 1), exp_out = self.generate_expected_output((("o1", lambda x: rom_data_function(x)), ("o2", lambda x: rom_data_function(2*x))), 6) self.compareIO(self.sim_trace, exp_out)
def make_example_graph(): in1, in2 = pyrtl.Input(8, 'in1'), pyrtl.Input(8, 'in2') out = pyrtl.Output(9, 'output') out <<= adders.kogge_stone(in1, in2) pyrtl.synthesize() pyrtl.optimize()
def test_function_RomBlock_with_optimization(self): def rom_data_function(add): return int((add + 5) / 2) pyrtl.reset_working_block() self.bitwidth = 4 self.addrwidth = 4 self.output1 = pyrtl.Output(self.bitwidth, "o1") self.output2 = pyrtl.Output(self.bitwidth, "o2") self.read_addr1 = pyrtl.Input(self.addrwidth) self.read_addr2 = pyrtl.Input(self.addrwidth) self.rom = pyrtl.RomBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth, name='rom', romdata=rom_data_function) self.output1 <<= self.rom[self.read_addr1] self.output2 <<= self.rom[self.read_addr2] pyrtl.synthesize() pyrtl.optimize() # build the actual simulation environment self.sim_trace = pyrtl.SimulationTrace() self.sim = self.sim(tracer=self.sim_trace) input_signals = {} for i in range(0, 5): input_signals[i] = {self.read_addr1: i, self.read_addr2: 2 * i} input_signals[i] = {self.read_addr1: i, self.read_addr2: 2 * i} self.sim.step(input_signals[i]) # exp_out = self.generate_expected_output((("o1", lambda x: rom_data_function(x) - 1), exp_out = self.generate_expected_output((("o1", lambda x: rom_data_function(x)), ("o2", lambda x: rom_data_function(2 * x))), 6) self.compareIO(self.sim_trace, exp_out)
def check_trace(self, correct_string): pyrtl.synthesize() sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for i in range(8): sim.step({}) output = io.StringIO() sim_trace.print_trace(output, compact=True) self.assertEqual(output.getvalue(), correct_string)
def check_trace(self, correct_string): pyrtl.synthesize() sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for i in range(8): sim.step({}) output = io.StringIO() sim_trace.print_trace(output) self.assertEqual(output.getvalue(), correct_string)
def test_longer_wires(self): a = pyrtl.Input(3, 'a') b = pyrtl.Input(2, 'b') out = pyrtl.Output(name='out') out <<= a + b pyrtl.synthesize() self.assertTrue( simulators.circuit_equivalence(lambda i, j: i + j, in_wires=(a, b)))
def test_single_mul(self): ina, inb = pyrtl.Input(bitwidth=4, name='a'), pyrtl.Input(bitwidth=4, name='b') self.output <<= ina * inb pyrtl.synthesize() sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for a in range(16): for b in range(16): sim.step({'a': a, 'b': b}) result = sim_trace.trace['r'] self.assertEqual(result, [a*b for a in range(16) for b in range(16)])
def test_single_mul(self): ina, inb = pyrtl.Input(bitwidth=4, name='a'), pyrtl.Input(bitwidth=4, name='b') self.output <<= ina * inb pyrtl.synthesize() sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for a in range(16): for b in range(16): sim.step({'a': a, 'b': b}) result = sim_trace.trace['r'] self.assertEqual(result, [a * b for a in range(16) for b in range(16)])
def check_op(self, op): ina, inb = pyrtl.Input(bitwidth=4, name='a'), pyrtl.Input(bitwidth=4, name='b') self.output <<= op(ina, inb) pyrtl.synthesize() sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for a in range(16): for b in range(16): sim.step({'a': a, 'b': b}) result = sim_trace.trace['r'] self.assertEqual(result, [op(a, b) for a in range(16) for b in range(16)])
def test_wire_net_removal_1(self): inwire = pyrtl.Input(bitwidth=3) tempwire = pyrtl.WireVector() outwire = pyrtl.Output() tempwire <<= inwire outwire <<= tempwire pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block() # should remove the middle wire but keep the input self.assert_num_net(5, block) self.assert_num_wires(6, block)
def test_synthesize_merged_io_simulates_correctly(self): pyrtl.synthesize() sim = pyrtl.Simulation() sim.step_multiple({ 'a': [4, 6, 2, 3], 'b': [2, 9, 11, 4], }) output = six.StringIO() sim.tracer.print_trace(output, compact=True) self.assertEqual(output.getvalue(), 'a 4623\n' 'b 29114\n' 'o 615137\n')
def test_synthesize_merged_io_mapped_correctly(self): old_io = pyrtl.working_block().wirevector_subset( (pyrtl.Input, pyrtl.Output)) pyrtl.synthesize() new_io = pyrtl.working_block().wirevector_subset( (pyrtl.Input, pyrtl.Output)) for oi in old_io: io_list = pyrtl.working_block().io_map[oi] self.assertEqual(len(io_list), 1) for ni in new_io: if oi.name == ni.name: self.assertEqual(io_list, [ni])
def test_synthesize_unmerged_io_simulates_correctly(self): pyrtl.synthesize(merge_io_vectors=False) sim = pyrtl.Simulation() for (a, b) in [(4, 2), (6, 9), (2, 11), (3, 4)]: args = {} for ix in range(4): args['a[' + str(ix) + ']'] = (a >> ix) & 1 args['b[' + str(ix) + ']'] = (b >> ix) & 1 sim.step(args) expected = a + b for ix in range(5): out = sim.inspect('o[' + str(ix) + ']') self.assertEqual(out, (expected >> ix) & 1)
def test_wire_net_removal_1(self): inwire = pyrtl.Input(bitwidth=3) tempwire = pyrtl.WireVector() outwire = pyrtl.Output() tempwire <<= inwire outwire <<= tempwire pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block(None) # should remove the middle wire but keep the input self.assertEqual(len(block.logic), 5) self.assertEqual(len(block.wirevector_set), 6)
def test_basic_one_var_op_1(self): constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() outwire <<= ~constwire pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block() self.num_net_of_type('~', 0, block) self.num_net_of_type('w', 1, block) self.assertEqual(len(block.logic), 1) self.assertEqual(len(block.wirevector_set), 2) self.num_wire_of_type(Const, 1, block)
def test_synthesize_unmerged_io_mapped_correctly(self): old_io = pyrtl.working_block().wirevector_subset( (pyrtl.Input, pyrtl.Output)) pyrtl.synthesize(merge_io_vectors=False) new_io = pyrtl.working_block().wirevector_subset( (pyrtl.Input, pyrtl.Output)) for oi in old_io: io_list = [w.name for w in pyrtl.working_block().io_map[oi]] self.assertEqual(len(io_list), len(oi)) for ni in new_io: if ni.name.startswith(oi.name): # Dev note: comparing names because comparing wires (e.g. list/set inclusion) # creates an '=' net, which is definitely not what we want here. self.assertIn(ni.name, io_list)
def test_chained_mul(self): ina, inb, inc = ( pyrtl.Input(bitwidth=2, name='a'), pyrtl.Input(bitwidth=2, name='b'), pyrtl.Input(bitwidth=2, name='c')) self.output <<= ina * inb * inc pyrtl.synthesize() sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for a in range(4): for b in range(4): for c in range(4): sim.step({'a': a, 'b': b, 'c': c}) result = sim_trace.trace['r'] self.assertEqual(result, [a * b * c for a in range(4) for b in range(4) for c in range(4)])
def test_chained_mul(self): ina, inb, inc = ( pyrtl.Input(bitwidth=2, name='a'), pyrtl.Input(bitwidth=2, name='b'), pyrtl.Input(bitwidth=2, name='c')) self.output <<= ina * inb * inc pyrtl.synthesize() sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for a in range(4): for b in range(4): for c in range(4): sim.step({'a': a, 'b': b, 'c': c}) result = sim_trace.trace['r'] self.assertEqual(result, [a*b*c for a in range(4) for b in range(4) for c in range(4)])
def test_timing_error(self): inwire, inwire2 = pyrtl.Input(bitwidth=1), pyrtl.Input(bitwidth=1) tempwire, tempwire2 = pyrtl.WireVector(1), pyrtl.WireVector(1) outwire = pyrtl.Output() tempwire <<= ~(inwire & tempwire2) tempwire2 <<= ~(inwire2 & tempwire) outwire <<= tempwire with self.assertRaises(pyrtl.PyrtlError): pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block() timing = estimate.TimingAnalysis(block) timing_max_length = timing.max_length()
def test_const_folding_basic_two_var_op_1(self): inwire = pyrtl.Input(bitwidth=1) constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() outwire <<= inwire & constwire pyrtl.synthesize() pyrtl.optimize() # should remove the or block and replace it with a # wire net (to separate the const from the output) block = pyrtl.working_block(None) self.assertEqual(self.num_net_of_type('&', block), 0) self.assertEqual(self.num_net_of_type('w', block), 1) self.assertEqual(len(block.logic), 2) self.assertEqual(len(block.wirevector_set), 4) self.assertEqual(self.num_wire_of_type(pyrtl.wire.Const, block), 1)
def test_basic_two_var_op_1(self): inwire = pyrtl.Input(bitwidth=1) constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() outwire <<= inwire & constwire pyrtl.synthesize() pyrtl.optimize() # should remove the and block and replace it with a # wire net (to separate the const from the output) block = pyrtl.working_block(None) self.num_net_of_type('&', 0, block) self.num_net_of_type('w', 1, block) self.assert_num_net(1, block) self.assert_num_wires(3, block) self.num_wire_of_type(Const, 1, block)
def test_basic_two_var_op_3(self): constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() # playing with edge cases outwire <<= constwire ^ constwire pyrtl.synthesize() pyrtl.optimize() # should remove the and block and replace it with a # wirevector (to separate the input from the output) block = pyrtl.working_block(None) self.num_net_of_type('^', 0, block) self.num_net_of_type('w', 1, block) self.assert_num_net(1, block) self.assert_num_wires(2, block) self.num_wire_of_type(Const, 1, block)
def test_const_folding_basic_two_var_op_3(self): constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() # playing with edge cases outwire <<= constwire ^ constwire pyrtl.synthesize() pyrtl.optimize() # should remove the and block and replace it with a # wirevector (to separate the input from the output) block = pyrtl.working_block(None) self.assertEqual(self.num_net_of_type('|', block), 0) self.assertEqual(self.num_net_of_type('w', block), 1) self.assertEqual(len(block.logic), 1) self.assertEqual(len(block.wirevector_set), 2) self.assertEqual(self.num_wire_of_type(pyrtl.wire.Const, block), 1)
def test_synth_simple_memblock(self): pyrtl.synthesize() pyrtl.optimize() self.sim_trace = pyrtl.SimulationTrace() sim = self.sim(tracer=self.sim_trace) input_signals = [[0, 1, 4, 5], [4, 1, 0, 5], [0, 4, 1, 6], [1, 1, 0, 0], [6, 0, 6, 7]] for signals in input_signals: sim.step({self.read_addr1: signals[0], self.read_addr2: signals[1], self.write_addr: signals[2], self.write_data: signals[3]}) output = six.StringIO() self.sim_trace.print_trace(output, compact=True) self.assertEqual(output.getvalue(), 'o1 05560\no2 00560\n')
def test_synthesize_regs_mapped_correctly(self): r2 = pyrtl.Register(5) self.r.next <<= ~self.r r2.next <<= self.r + 1 synth_block = pyrtl.synthesize() self.assertEqual(len(synth_block.reg_map), 2) self.assertEqual(len(synth_block.reg_map[self.r]), len(self.r)) self.assertEqual(len(synth_block.reg_map[r2]), len(r2))
def test_two_var_op_produce_not(self): constwire = pyrtl.Const(1, 1) inwire = pyrtl.Input(bitwidth=1) outwire = pyrtl.Output() # playing with edge cases outwire <<= constwire ^ inwire pyrtl.synthesize() pyrtl.optimize() # should remove the and block and replace it with a # wirevector (to separate the input from the output) block = pyrtl.working_block(None) self.num_net_of_type('~', 1, block) self.num_net_of_type('w', 1, block) self.num_net_of_type('s', 1, block) # due to synthesis self.assert_num_net(3, block) self.assert_num_wires(4, block) self.num_wire_of_type(Const, 0, block)
def test_timing_error(self): inwire, inwire2 = pyrtl.Input(bitwidth=1), pyrtl.Input(bitwidth=1) tempwire, tempwire2 = pyrtl.WireVector(1), pyrtl.WireVector(1) outwire = pyrtl.Output() tempwire <<= ~(inwire & tempwire2) tempwire2 <<= ~(inwire2 & tempwire) outwire <<= tempwire output = six.StringIO() sys.stdout = output with self.assertRaises(pyrtl.PyrtlError): pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block() _timing = pyrtl.TimingAnalysis(block) sys.stdout = sys.__stdout__ self.assertTrue(output.getvalue().startswith("Loop found:"))
def test_const_folding_adv_one_var_op_1(self): constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() tempwire = pyrtl.WireVector() reg = pyrtl.Register(1, 'test register') tempwire <<= ~constwire reg.next <<= tempwire outwire <<= reg pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block(None) self.assertEqual(self.num_net_of_type('w', block), 1) self.assertEqual(len(block.logic), 1) self.assertEqual(len(block.wirevector_set), 2) self.assertEqual(self.num_wire_of_type(pyrtl.wire.Const, block), 1) self.assertEqual(self.num_wire_of_type(pyrtl.wire.Output, block), 1)
def test_adv_one_var_op_1(self): constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() tempwire = pyrtl.WireVector() reg = pyrtl.Register(1, 'test register') tempwire <<= ~constwire reg.next <<= tempwire outwire <<= reg pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block(None) self.num_net_of_type('w', 1, block) self.assert_num_net(1, block) self.assert_num_wires(2, block) self.num_wire_of_type(Const, 1, block) self.num_wire_of_type(Output, 1, block)
def everything_t_procedure(self, timing_val=None, opt_timing_val=None): # if there is a nondefault timing val supplied, then it will check # to make sure that the timing matches # this is a subprocess to do the synth and timing block = pyrtl.working_block() timing = estimate.TimingAnalysis(block) timing_max_length = timing.max_length() if timing_val is not None: self.assertEqual(timing_max_length, timing_val) critical_path = timing.critical_path() pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block() timing = estimate.TimingAnalysis(block) timing_max_length = timing.max_length() if opt_timing_val is not None: self.assertEqual(timing_max_length, opt_timing_val) critical_path = timing.critical_path() pyrtl.and_inverter_synth() pyrtl.optimize() block = pyrtl.working_block() timing = estimate.TimingAnalysis(block) timing_max_length = timing.max_length() critical_path = timing.critical_path() block = pyrtl.working_block() self.num_net_of_type('|', 0, block) self.num_net_of_type('^', 0, block) pyrtl.nand_synth() pyrtl.optimize() block = pyrtl.working_block() timing = estimate.TimingAnalysis(block) timing_max_length = timing.max_length() critical_path = timing.critical_path() block.sanity_check() self.num_net_of_type('|', 0, block) self.num_net_of_type('^', 0, block) self.num_net_of_type('&', 0, block)
def test_edge_case_1(self): in_1 = pyrtl.Input(10) in_2 = pyrtl.Input(9) fake_loop_wire = pyrtl.WireVector(1) comp_wire = pyrtl.corecircuits.concat(in_2[0:4], fake_loop_wire, in_2[4:9]) r_wire = in_1 & comp_wire fake_loop_wire <<= r_wire[3] out = pyrtl.Output(10) out <<= fake_loop_wire # Yes, because we only check loops on a net level, this will still be # a loop pre synth self.assertNotEqual(pyrtl.find_loop(), None) pyrtl.synthesize() # Because synth separates the individual wires, it also resolves the loop self.assertEqual(pyrtl.find_loop(), None) pyrtl.optimize() self.assertEqual(pyrtl.find_loop(), None)
def test_edge_case_1(self): in_1 = pyrtl.Input(10) in_2 = pyrtl.Input(9) fake_loop_wire = pyrtl.WireVector(1) comp_wire = pyrtl.concat(in_2[0:4], fake_loop_wire, in_2[4:9]) r_wire = in_1 & comp_wire fake_loop_wire <<= r_wire[3] out = pyrtl.Output(10) out <<= fake_loop_wire # Yes, because we only check loops on a net level, this will still be # a loop pre synth self.assertNotEqual(pyrtl.find_loop(), None) pyrtl.synthesize() # Because synth separates the individual wires, it also resolves the loop self.assertEqual(pyrtl.find_loop(), None) pyrtl.optimize() self.assertEqual(pyrtl.find_loop(), None)
def test_adv_one_var_op_2(self): # this one tests to see that an input wirevector is properly preserved inwire = pyrtl.Input(bitwidth=1) outwire = pyrtl.Output() tempwire = pyrtl.WireVector() reg = pyrtl.Register(1, 'test register') tempwire <<= ~inwire reg.next <<= tempwire outwire <<= reg pyrtl.synthesize() pyrtl.optimize() # should remove the and block and replace it with a # wire net (to separate the input from the output) block = pyrtl.working_block(None) # Note: the current implementation still sticks a wire net between # a register 'nextsetter' wire and the output wire self.num_net_of_type('w', 1, block) self.assert_num_net(4, block) self.assert_num_wires(5, block) self.num_wire_of_type(Const, 0, block) self.num_wire_of_type(Output, 1, block)
def test_synth_simple_memblock(self): synth_out = pyrtl.synthesize() pyrtl.optimize() self.sim_trace = pyrtl.SimulationTrace() self.sim = pyrtl.FastSimulation(tracer=self.sim_trace) input_signals = { 0: { self.read_addr1: 0, self.read_addr2: 1, self.write_addr: 4, self.write_data: 5 }, 1: { self.read_addr1: 4, self.read_addr2: 1, self.write_addr: 0, self.write_data: 5 }, 2: { self.read_addr1: 0, self.read_addr2: 4, self.write_addr: 1, self.write_data: 6 }, 3: { self.read_addr1: 1, self.read_addr2: 1, self.write_addr: 0, self.write_data: 0 }, 4: { self.read_addr1: 6, self.read_addr2: 0, self.write_addr: 6, self.write_data: 7 } } for i in range(5): self.sim.step(input_signals[i]) output = io.StringIO() self.sim_trace.print_trace(output) self.assertEqual(output.getvalue(), 'o1 05560\no2 00560\n')
def test_synth_simple_memblock(self): synth_out = pyrtl.synthesize() pyrtl.optimize() self.sim_trace = pyrtl.SimulationTrace() self.sim = pyrtl.FastSimulation(tracer=self.sim_trace) input_signals = {0: {self.read_addr1: 0, self.read_addr2: 1, self.write_addr: 4, self.write_data: 5}, 1: {self.read_addr1: 4, self.read_addr2: 1, self.write_addr: 0, self.write_data: 5}, 2: {self.read_addr1: 0, self.read_addr2: 4, self.write_addr: 1, self.write_data: 6}, 3: {self.read_addr1: 1, self.read_addr2: 1, self.write_addr: 0, self.write_data: 0}, 4: {self.read_addr1: 6, self.read_addr2: 0, self.write_addr: 6, self.write_data: 7}} for i in range(5): self.sim.step(input_signals[i]) output = io.StringIO() self.sim_trace.print_trace(output) self.assertEqual(output.getvalue(), 'o1 05560\no2 00560\n')
def doAllOps(): pyrtl.synthesize() pyrtl.optimize() block = pyrtl.working_block() timing_map = pyrtl.timing_analysis(block) block_max_time = pyrtl.timing_max_length(timing_map)
def assert_has_loop(self): self.assertNotEqual(pyrtl.find_loop(), None) pyrtl.synthesize() self.assertNotEqual(pyrtl.find_loop(), None) pyrtl.optimize() self.assertNotEqual(pyrtl.find_loop(), None)
for cycle in range(15): sim.step({zero: random.choice([0, 0, 0, 1])}) sim_trace.render_trace() # We already did the "hard" work of generating a test input for this simulation so # we might want to reuse that work when we take this design through a verilog toolchain. # The function output_verilog_testbench grabs the inputs used in the simulation trace # and sets them up in a standar verilog testbench. print("--- Verilog for the TestBench ---") with io.StringIO() as tbfile: pyrtl.output_verilog_testbench(tbfile, sim_trace) print(tbfile.getvalue()) # Not let's talk about transformations of the hardware block. Many times when you are # doing some hardware-level analysis you might wish to ignore higher level things like # multi-bit wirevectors, adds, concatination, etc. and just thing about wires and basic # gates. PyRTL supports "lowering" of designs into this more restricted set of functionality # though the function "synthesize". Once we lower a design to this form we can then apply # basic optimizations like constant propgation and dead wire elimination as well. By # printing it out to verilog we can see exactly how the design changed. print("--- Optimized Single-bit Verilog for the Counter ---") pyrtl.synthesize() pyrtl.optimize() with io.StringIO() as vfile: pyrtl.output_to_verilog(vfile) print(vfile.getvalue())