def test_union(self): union_mix = dl.Union([ dl.Int(dl.Size(16)), dl.Bool(), dl.Str(dl.Size(10)), dl.Tuple([ dl.Int(dl.Size(8)), dl.UInt(dl.Size(16)), dl.Float(dl.Size(64)), dl.Complex(dl.Size(128)), dl.Bool(), dl.Str(dl.Size(10)) ]), dl.Record(RecATI) ]) @dl.Interactive([("ack_union_mix", bool)], [("out_union_mix", union_mix)]) def testbench(node: dl.PythonNode): node.send(out_union_mix=5) assert node.receive("ack_union_mix") node.send(out_union_mix=False) assert node.receive("ack_union_mix") node.send(out_union_mix='abcd') assert node.receive("ack_union_mix") node.send(out_union_mix=(-5, 10, -1.5, (1.5 + 2.5j), False, 'hello')) assert node.receive("ack_union_mix") node.send(out_union_mix=RecATI([1, 2], (3.0, 4), 5)) assert node.receive("ack_union_mix") raise DeltaRuntimeExit s_union_mix = dl.lib.StateSaver(union_mix, verbose=True) with dl.DeltaGraph() as graph: p = dl.placeholder_node_factory() p.specify_by_node( testbench.call(s_union_mix.save_and_ack(p.out_union_mix))) self.check_executes_graph( graph, """\ saving 5 saving False saving abcd saving (-5, 10, -1.5, (1.5+2.5j), False, 'hello') saving RecATI(x=[1, 2], y=(3.0, 4), z=5) """)
def test_compound(self): tuple_mix = dl.Tuple([ dl.Int(dl.Size(8)), dl.UInt(dl.Size(16)), dl.Float(dl.Size(64)), dl.Complex(dl.Size(128)), dl.Bool(), dl.Str(dl.Size(10)) ]) array_float = dl.Array(dl.Float(dl.Size(64)), dl.Size(3)) record_mix = dl.Record(RecATI) @dl.Interactive([("ack_tuple_mix", bool), ("ack_array_float", bool), ("ack_record_mix", bool)], [("out_tuple_mix", tuple_mix), ("out_array_float", array_float), ("out_record_mix", record_mix)]) def testbench(node: dl.PythonNode): node.send(out_tuple_mix=(-5, 1000, -100.5, (1.5 + 2.5j), False, '0123456789')) assert node.receive("ack_tuple_mix") node.send(out_array_float=[0.5, -0.25, 0.125]) assert node.receive("ack_array_float") node.send(out_record_mix=RecATI([1, 2], (3.0, 4), 5)) assert node.receive("ack_record_mix") raise DeltaRuntimeExit s_tuple_mix = dl.lib.StateSaver(tuple_mix, verbose=True) s_array_float = dl.lib.StateSaver(array_float, verbose=True) s_record_mix = dl.lib.StateSaver(record_mix, verbose=True) with dl.DeltaGraph() as graph: p = dl.placeholder_node_factory() p.specify_by_node( testbench.call(s_tuple_mix.save_and_ack(p.out_tuple_mix), s_array_float.save_and_ack(p.out_array_float), s_record_mix.save_and_ack(p.out_record_mix))) self.check_executes_graph( graph, """\ saving (-5, 1000, -100.5, (1.5+2.5j), False, '0123456789') saving [0.5, -0.25, 0.125] saving RecATI(x=[1, 2], y=(3.0, 4), z=5) """)
def migen_body(self, template): """ This is the body of the migen node connecting the pulser and timestamper as 2 submodules. """ # Node inputs self.reset = template.add_pa_in_port('reset', dl.Optional(int)) self.photon = template.add_pa_in_port('photon', dl.Optional(int)) # Node outputs self.time = template.add_pa_out_port('time', dl.UInt()) self.error = template.add_pa_out_port('error', dl.Int()) self.rf_trigger = Signal(1) self.pmt_trigger = Signal(1) self.hit_channels = Signal(2) self.clock = Signal(TIME_RES) ### self.comb += [ self.hit_channels.eq(self.pmt_trigger + 2 * self.rf_trigger), self.photon.ready.eq(1), ] # error management ( if photon is outside valid range) self.sync += [ If( self.photon.valid & ((self.photon.data < 1) | (self.photon.data > TIME_RES - 1)), self.error.data.eq(1), self.error.valid.eq(1)).Elif( self.photon.valid, self.error.data.eq(0), self.error.valid.eq(1)).Else( self.error.data.eq(self.error.data), self.error.valid.eq(0)) ] self.pulser_inst = TimestamperModel.Pulser(self.reset, self.pmt_trigger, self.rf_trigger, self.photon, self.clock) self.timestamper_inst = TimestamperModel.Timestamper( self.hit_channels, self.time, self.reset, self.clock) self.submodules += [self.timestamper_inst, self.pulser_inst]
def migen_body(self, template): _TIME_RES = 32 # Node inputs self.time_in = template.add_pa_in_port('time_in', dl.Optional(dl.UInt())) # Node outputs self.time_out = template.add_pa_out_port('time_out', dl.UInt()) self.counter_reset = template.add_pa_out_port('counter_reset', dl.Int()) # Internal signals self.pmt_reg = Signal(_TIME_RES) self.rf_reg = Signal(_TIME_RES) self.pmt_trig = Signal(1) self.rf_trig = Signal(1) self.submodules.fsm = FSM(reset_state="RESET_COUNTER") self.sync += [ If( self.pmt_trig, self.pmt_reg.eq(self.time_in.data), ).Elif(self.fsm.ongoing("RESET_COUNTER"), self.pmt_reg.eq(0)).Else(self.pmt_reg.eq(self.pmt_reg)), If( self.rf_trig, self.rf_reg.eq(self.time_in.data), ).Elif(self.fsm.ongoing("RESET_COUNTER"), self.rf_reg.eq(0)).Else(self.rf_reg.eq(self.rf_reg)) ] """FSM The FSM is used to control the readouts from the HPTDC chip and generate a time signal for the accumulator RESET_COUNTER This is the dinitial state of the FSM at the start of the experiment. It resets the "coarse counter" of the HPTDC chip to establish a TO time reference. WAIT_FOR_PMT This state holds until the PMT timestamp is available at the HPTDC chip readout (first data_ready sync pulse) WAIT_FOR_RF This state holds until the RMT timestamp is available at the HPTDC chip readout (second data_ready sync pulse) SEND_TIME In this state, the difference between t_PMT and t_RF is derived and sent to the accumulator. WAIT_ACC_LATENCY This state is used to wait for any delays on inter-node communication """ self.fsm.act( "RESET_COUNTER", self.pmt_trig.eq(0), self.rf_trig.eq(0), self.time_in.ready.eq(1), self.counter_reset.data.eq(1), # reset counters self.counter_reset.valid.eq(1), NextState("WAIT_FOR_PMT")) self.fsm.act( "WAIT_FOR_PMT", self.counter_reset.data.eq(0), self.time_in.ready.eq(1), If(self.time_in.valid, self.pmt_trig.eq(1), NextState("WAIT_FOR_RF"))) self.fsm.act( "WAIT_FOR_RF", self.time_in.ready.eq(1), If(self.time_in.valid, self.rf_trig.eq(1), NextState("SEND_TIME"))) self.fsm.act("SEND_TIME", self.time_in.ready.eq(1), self.time_out.data.eq(self.rf_reg - self.pmt_reg), self.time_out.valid.eq(1), NextState("WAIT_ACC_LATENCY")) self.fsm.act("WAIT_ACC_LATENCY", If(self.time_in.valid == 0, NextState("RESET_COUNTER")))
If(self.time_in.valid, self.pmt_trig.eq(1), NextState("WAIT_FOR_RF"))) self.fsm.act( "WAIT_FOR_RF", self.time_in.ready.eq(1), If(self.time_in.valid, self.rf_trig.eq(1), NextState("SEND_TIME"))) self.fsm.act("SEND_TIME", self.time_in.ready.eq(1), self.time_out.data.eq(self.rf_reg - self.pmt_reg), self.time_out.valid.eq(1), NextState("WAIT_ACC_LATENCY")) self.fsm.act("WAIT_ACC_LATENCY", If(self.time_in.valid == 0, NextState("RESET_COUNTER"))) @dl.Interactive(inputs=[('time_out', dl.UInt()), ('reset', dl.Int())], outputs=[('output', dl.UInt())]) def testbench(node): """ Testbench for Timestamper interface node. Starts with random testing and ends with corner cases """ _ITER = 10 for i in range(_ITER): logging.debug(f'---Testbench iter {i}---') time_pmt = random.randint(0, 100) time_rf = random.randint(0, 100) do_test(node, time_pmt, time_rf) raise dl.DeltaRuntimeExit
self.sync += migen.If( i1.valid == 1, o1.valid.eq(1), o1.data.eq(i1.data+1) ).Else( o1.data.eq(0), migen.If(started == 0, o1.valid.eq(1), started.eq(1) ).Else( o1.valid.eq(0) ) ) @dl.Interactive([("measurement", dl.UInt(dl.Size(32)))], [("output", dl.UInt(dl.Size(32)))], name="interactive_simple") def send_gates_list_then_exit(node: dl.PythonNode): cmds = ["RX", "RZ", "RY"] # for non-deterministic tests use random.randint(0, 255) args = [99, 250, 11] node.send(dl.lib.command_creator("STATE_PREPARATION")) for cmd, arg in zip(cmds, args): node.send(dl.lib.command_creator(cmd, argument=arg)) node.send(dl.lib.command_creator("STATE_MEASURE")) measurement = node.receive("measurement") print(f"Measurement: {measurement}")
self.error.data.eq(self.error.data), self.error.valid.eq(0)) ] self.pulser_inst = TimestamperModel.Pulser(self.reset, self.pmt_trigger, self.rf_trigger, self.photon, self.clock) self.timestamper_inst = TimestamperModel.Timestamper( self.hit_channels, self.time, self.reset, self.clock) self.submodules += [self.timestamper_inst, self.pulser_inst] @dl.Interactive(inputs=[('time', dl.UInt()), ('error', dl.Int())], outputs=[('reset', dl.Int()), ('photon', dl.Int())]) def testbench(node): """ Testbench for Timestamper model node. Starts with random testing and ends with corner cases """ for i in range(TEST_LENGTH): photon = random.randint(1, TIME_RES - 2) do_test(i, photon, node) # corner cases # 1 : photon arrival time 0 - error expected do_test(-1, 0, node) # 2: photon arrival time TIME_RES - no expected error do_test(-2, TIME_RES - 1, node)
def test_primitives(self): tuple_int = dl.Tuple([ dl.Int(dl.Size(8)), dl.Int(dl.Size(16)), dl.Int(dl.Size(32)), dl.Int(dl.Size(64)) ]) tuple_uint = dl.Tuple([ dl.UInt(dl.Size(8)), dl.UInt(dl.Size(16)), dl.UInt(dl.Size(32)), dl.UInt(dl.Size(64)) ]) tuple_float = dl.Tuple([dl.Float(dl.Size(32)), dl.Float(dl.Size(64))]) tuple_complex = dl.Tuple( [dl.Complex(dl.Size(64)), dl.Complex(dl.Size(128))]) tuple_bool_char = dl.Tuple([dl.Bool(), dl.Str(dl.Size(1))]) @dl.Interactive([("ack_int", bool), ("ack_uint", bool), ("ack_float", bool), ("ack_complex", bool), ("ack_bool_char", bool)], [("out_int", tuple_int), ("out_uint", tuple_uint), ("out_float", tuple_float), ("out_complex", tuple_complex), ("out_bool_char", tuple_bool_char)]) def testbench(node: dl.PythonNode): node.send(out_int=(-128, -32768, -2147483648, -9223372036854775808)) assert node.receive("ack_int") node.send(out_int=(127, 32767, 2147483647, 9223372036854775807)) assert node.receive("ack_int") node.send(out_uint=(0, 0, 0, 0)) assert node.receive("ack_uint") node.send(out_uint=(255, 65535, 4294967295, 18446744073709551615)) assert node.receive("ack_uint") # this is just a rough estimate node.send(out_float=(1.0000001, 1.000000000000001)) assert node.receive("ack_float") node.send(out_complex=((1.0000001 + 1.0000001j), (1.000000000000001 + 1.000000000000001j))) assert node.receive("ack_complex") node.send(out_bool_char=(True, 'a')) assert node.receive("ack_bool_char") raise DeltaRuntimeExit s_int = dl.lib.StateSaver(tuple_int, verbose=True) s_uint = dl.lib.StateSaver(tuple_uint, verbose=True) s_float = dl.lib.StateSaver(tuple_float, verbose=True) s_complex = dl.lib.StateSaver(tuple_complex, verbose=True) s_bool_char = dl.lib.StateSaver(tuple_bool_char, verbose=True) with dl.DeltaGraph() as graph: p = dl.placeholder_node_factory() p.specify_by_node( testbench.call(s_int.save_and_ack(p.out_int), s_uint.save_and_ack(p.out_uint), s_float.save_and_ack(p.out_float), s_complex.save_and_ack(p.out_complex), s_bool_char.save_and_ack(p.out_bool_char))) self.check_executes_graph( graph, f"""\ saving (-128, -32768, -2147483648, -9223372036854775808) saving (127, 32767, 2147483647, 9223372036854775807) saving (0, 0, 0, 0) saving (255, 65535, 4294967295, 18446744073709551615) saving ({np.float32(1.0000001)}, 1.000000000000001) saving (({np.float32(1.0000001)}+{np.float32(1.0000001)}j), (1.000000000000001+1.000000000000001j)) saving (True, 'a') """)
import matplotlib.pyplot as plt from matplotlib import ticker from progress.bar import Bar import time import math import random import numpy as np import logging import deltalanguage as dl TIME_RES = 30 @dl.Interactive(inputs=[('new_time', dl.UInt()), ('DAC_status', int), ('DAC_voltage', int), ('experiment_start', dl.Optional(bool))], outputs=[('DAC_command', int), ('DAC_param', int), ('photon', int), ('reset', int)]) def accumulator(node): """ Accumulator Node This node collects times sent from the counter FPGA node and issues commands to the DAC controller. This allows the node to collect data, fit to the data and feedback to the experiment. The process can loop until some minimum threshold is reached, allowing full automation of micromotion compensation. Inputs: - new_time: Time from Counter node