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_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_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_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_const_folding_basic_two_var_op_2(self): inwire = pyrtl.Input(bitwidth=1) constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() outwire <<= inwire | constwire 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.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), 0)
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_basic_two_var_op_2(self): inwire = pyrtl.Input(bitwidth=1) constwire = pyrtl.Const(0, 1) outwire = pyrtl.Output() outwire <<= inwire | constwire 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.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, 0, block)
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_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_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 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_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_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_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_two_var_op_correct_wire_prop(self): ins = [pyrtl.Input(1) for i in range(3)] outwire = pyrtl.Output() const_w = pyrtl.Const(0) temp1 = ins[1] & ins[2] temp2 = temp1 ^ const_w temp3 = const_w | temp2 outwire <<= temp3 & ins[2] pyrtl.optimize() block = pyrtl.working_block() block.sanity_check() # just in case self.num_net_of_type('&', 2) self.num_net_of_type('w', 1) self.assert_num_net(3) self.assert_num_wires(6) self.num_wire_of_type(Const, 0)
def test_two_var_op_correct_not_wire_replacement(self): ins = [pyrtl.Input(1) for i in range(3)] outwire = pyrtl.Output() const_0 = pyrtl.Const(0) const_1 = pyrtl.Const(1) temp1 = ins[0] & ins[1] temp2 = const_0 | temp1 temp3 = temp2 ^ const_1 outwire <<= temp3 & ins[2] pyrtl.optimize() block = pyrtl.working_block() block.sanity_check() # just in case self.num_net_of_type('&', 2) self.num_net_of_type('~', 1) self.num_net_of_type('w', 1) self.assert_num_net(4) self.assert_num_wires(7) self.num_wire_of_type(Const, 0)
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_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.output3 = pyrtl.Output(self.bitwidth, "o3") # self.read_out = pyrtl.WireVector(self.bitwidth) 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.read_out <<= self.rom[self.read_addr1] # self.output1 <<= self.read_out - 1 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 = pyrtl.FastSimulation(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 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 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)
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())
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 class OutputVerilogTestbench grabs the inputs used in the simulation trace # and sets them up in a standard verilog testbench. print("--- Verilog for the TestBench ---") with io.StringIO() as tbfile: pyrtl.output_verilog_testbench(dest_file=tbfile, simulation_trace=sim_trace) print(tbfile.getvalue()) # Now 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, concatenation, etc. and just think 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 propagation 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())