From a9fbc70db8675e5d1ba7497497e87384b6f225c9 Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Wed, 23 Jul 2025 10:36:10 +0200 Subject: [PATCH 01/15] refactor(hardware): created tlu controller --- aidatlu/hardware/tlu_controller.py | 211 +++++++++++++++++++++++++++++ aidatlu/main/tlu.py | 181 ------------------------- 2 files changed, 211 insertions(+), 181 deletions(-) create mode 100644 aidatlu/hardware/tlu_controller.py diff --git a/aidatlu/hardware/tlu_controller.py b/aidatlu/hardware/tlu_controller.py new file mode 100644 index 0000000..457a7a8 --- /dev/null +++ b/aidatlu/hardware/tlu_controller.py @@ -0,0 +1,211 @@ +import numpy as np + +from aidatlu import logger +from aidatlu.hardware.clock_controller import ClockControl +from aidatlu.hardware.dac_controller import DacControl +from aidatlu.hardware.dut_controller import DUTLogic +from aidatlu.hardware.i2c import I2CCore +from aidatlu.hardware.ioexpander_controller import IOControl +from aidatlu.hardware.trigger_controller import TriggerLogic + +class TLUControl: + """Controls general TLU functionalities. + """ + + def __init__(self, hw, i2c=I2CCore) -> None: + self.log = logger.setup_main_logger(__class__.__name__) + self.i2c = i2c(hw) + self.i2c_hw = hw + self.log.info("Initializing IPbus interface") + self.i2c.init() + + if self.i2c.modules["eeprom"]: + self.log.info("Found device with ID %s" % hex(self.get_device_id())) + + # TODO some configuration also sends out ~70 triggers. + self.io_controller = IOControl(self.i2c) + self.dac_controller = DacControl(self.i2c) + self.clock_controller = ClockControl(self.i2c, self.io_controller) + self.trigger_logic = TriggerLogic(self.i2c) + self.dut_logic = DUTLogic(self.i2c) + + ### General TLU Functions ### + + def reset_configuration(self) -> None: + # Disable all outputs + self.io_controller.clock_lemo_output(False) + for i in range(4): + self.io_controller.configure_hdmi(i + 1, 1) + self.dac_controller.set_voltage(5, 0) + self.io_controller.all_off() + # sets all thresholds to 1.2 V + for i in range(6): + self.dac_controller.set_threshold(i + 1, 0) + # Resets all internal counters and raise the trigger veto. + self.set_run_active(False) + self.reset_status() + self.reset_counters() + self.trigger_logic.set_trigger_veto(True) + self.reset_fifo() + self.reset_timestamp() + + def start_run(self) -> None: + """Start run configurations""" + self.reset_counters() + self.reset_fifo() + self.reset_timestamp() + self.set_run_active(True) + self.trigger_logic.set_trigger_veto(False) + + def stop_run(self) -> None: + """Stop run configurations""" + self.trigger_logic.set_trigger_veto(True) + self.set_run_active(False) + + ### Basic TLU Control Functions ### + + def get_device_id(self) -> int: + """Read back board id. Consists of six blocks of hex data + + Returns: + int: Board id as 48 bits integer + """ + id = [] + for addr in range(6): + id.append(self.i2c.read(self.i2c.modules["eeprom"], 0xFA + addr) & 0xFF) + return int("0x" + "".join(["{:x}".format(i) for i in id]), 16) & 0xFFFFFFFFFFFF + + def get_fw_version(self) -> int: + return self.i2c.read_register("version") + + def reset_timestamp(self) -> None: + """Sets bit to 'ResetTimestampW' register to reset the time stamp.""" + self.i2c.write_register("Event_Formatter.ResetTimestampW", 1) + + def reset_counters(self) -> None: + """Resets the trigger counters.""" + self.write_status(0x2) + self.write_status(0x0) + + def reset_status(self) -> None: + """Resets the complete status and all counters.""" + self.write_status(0x3) + self.write_status(0x0) + self.write_status(0x4) + self.write_status(0x0) + + def reset_fifo(self) -> None: + """Sets 0 to 'EventFifoCSR' this resets the FIFO.""" + self.set_event_fifo_csr(0x0) + + def set_event_fifo_csr(self, value: int) -> None: + """Sets value to the EventFifoCSR register. + + Args: + value (int): 0 resets the FIFO. #TODO can do other stuff that is not implemented + + """ + self.i2c.write_register("eventBuffer.EventFifoCSR", value) + + def write_status(self, value: int) -> None: + """Sets value to the 'SerdesRstW' register. + + Args: + value (int): Bit 0 resets the status, bit 1 resets trigger counters and bit 2 calibrates IDELAY. + """ + self.i2c.write_register("triggerInputs.SerdesRstW", value) + + def set_run_active(self, state: bool) -> None: + """Raises internal run active signal. + + Args: + state (bool): True sets run active, False disables it. + """ + if type(state) != bool: + raise TypeError("State has to be bool") + self.i2c.write_register("Shutter.RunActiveRW", int(state)) + self.log.info("Run active: %s" % self.get_run_active()) + + def get_run_active(self) -> bool: + """Reads register 'RunActiveRW' + + Returns: + bool: Returns bool of the run active register. + """ + return bool(self.i2c.read_register("Shutter.RunActiveRW")) + + def set_enable_record_data(self, value: int) -> None: + """#TODO not sure what this does. Looks like a separate internal event buffer to the FIFO. + + Args: + value (int): #TODO I think this does not work + """ + self.i2c.write_register("Event_Formatter.Enable_Record_Data", value) + + def get_event_fifo_csr(self) -> int: + """Reads value from 'EventFifoCSR', corresponds to status flags of the FIFO. + + Returns: + int: number of events + """ + return self.i2c.read_register("eventBuffer.EventFifoCSR") + + def get_event_fifo_fill_level(self) -> int: + """Reads value from 'EventFifoFillLevel' + Returns the number of words written in + the FIFO. The lowest 14-bits are the actual data. + + Returns: + int: buffer level of the fifi + """ + return self.i2c.read_register("eventBuffer.EventFifoFillLevel") + + def get_timestamp(self) -> int: + """Get current time stamp. + + Returns: + int: Time stamp is not formatted. + """ + time = self.i2c.read_register("Event_Formatter.CurrentTimestampHR") + time = time << 32 + time = time + self.i2c.read_register("Event_Formatter.CurrentTimestampLR") + return time + + def pull_fifo_event(self) -> list: + """Pulls event from the FIFO. This is needed in the run loop to prevent the buffer to get stuck. + if this register is full the fifo needs to be reset or new triggers are generated but not sent out. + #TODO check here if the FIFO is full and reset it if needed would prob. make sense. + + Returns: + list: 6 element long vector containing bitwords of the data. + """ + event_numb = self.get_event_fifo_fill_level() + if event_numb: + fifo_content = self.i2c_hw.getNode("eventBuffer.EventFifoData").readBlock( + event_numb + ) + self.i2c_hw.dispatch() + return np.array(fifo_content) + pass + + def get_scaler(self, channel: int) -> int: + """reads current scaler value from register""" + if channel < 0 or channel > 5: + raise ValueError("Only channels 0 to 5 are valid") + return self.i2c.read_register(f"triggerInputs.ThrCount{channel:d}R") + + def get_scalers(self) -> list: + """reads current sc values from registers + + Returns: + list: all 6 trigger sc values + """ + return [self.get_scaler(n) for n in range(6)] + + def get_pre_veto_trigger_number(self) -> int: + """Optains the number of triggers recorded in the TLU before the veto is applied from the trigger logic register""" + return self.trigger_logic.get_pre_veto_trigger() + + def get_post_veto_trigger_number(self) -> int: + """Optains the number of triggers recorded in the TLU after the veto is applied from the trigger logic register""" + return self.trigger_logic.get_post_veto_trigger() diff --git a/aidatlu/main/tlu.py b/aidatlu/main/tlu.py index da87f9f..1b9d177 100644 --- a/aidatlu/main/tlu.py +++ b/aidatlu/main/tlu.py @@ -22,21 +22,7 @@ class AidaTLU: def __init__(self, hw, config_dict, clock_config_path, i2c=I2CCore) -> None: self.log = logger.setup_main_logger(__class__.__name__) - self.i2c = i2c(hw) - self.i2c_hw = hw - self.log.info("Initializing IPbus interface") - self.i2c.init() - - if self.i2c.modules["eeprom"]: - self.log.info("Found device with ID %s" % hex(self.get_device_id())) - - # TODO some configuration also sends out ~70 triggers. - self.io_controller = IOControl(self.i2c) - self.dac_controller = DacControl(self.i2c) - self.clock_controller = ClockControl(self.i2c, self.io_controller) self.clock_controller.write_clock_conf(clock_config_path) - self.trigger_logic = TriggerLogic(self.i2c) - self.dut_logic = DUTLogic(self.i2c) self.reset_configuration() self.config_parser = Configure(self, config_dict) @@ -52,181 +38,14 @@ def configure(self) -> None: self.get_scalers() def reset_configuration(self) -> None: - """Switch off all outputs, reset all counters and set threshold to 1.2V""" - # Disable all outputs - self.io_controller.clock_lemo_output(False) - for i in range(4): - self.io_controller.configure_hdmi(i + 1, 1) - self.dac_controller.set_voltage(5, 0) - self.io_controller.all_off() - # sets all thresholds to 1.2 V - for i in range(6): - self.dac_controller.set_threshold(i + 1, 0) - # Resets all internal counters and raise the trigger veto. - self.set_run_active(False) - self.reset_status() - self.reset_counters() - self.trigger_logic.set_trigger_veto(True) - self.reset_fifo() - self.reset_timestamp() - self.run_number = 0 try: self.h5_file.close() except: pass - def get_device_id(self) -> int: - """Read back board id. Consists of six blocks of hex data - - Returns: - int: Board id as 48 bits integer - """ - id = [] - for addr in range(6): - id.append(self.i2c.read(self.i2c.modules["eeprom"], 0xFA + addr) & 0xFF) - return int("0x" + "".join(["{:x}".format(i) for i in id]), 16) & 0xFFFFFFFFFFFF - - def get_fw_version(self) -> int: - return self.i2c.read_register("version") - - def reset_timestamp(self) -> None: - """Sets bit to 'ResetTimestampW' register to reset the time stamp.""" - self.i2c.write_register("Event_Formatter.ResetTimestampW", 1) - - def reset_counters(self) -> None: - """Resets the trigger counters.""" - self.write_status(0x2) - self.write_status(0x0) - - def reset_status(self) -> None: - """Resets the complete status and all counters.""" - self.write_status(0x3) - self.write_status(0x0) - self.write_status(0x4) - self.write_status(0x0) - - def reset_fifo(self) -> None: - """Sets 0 to 'EventFifoCSR' this resets the FIFO.""" - self.set_event_fifo_csr(0x0) - - def set_event_fifo_csr(self, value: int) -> None: - """Sets value to the EventFifoCSR register. - - Args: - value (int): 0 resets the FIFO. #TODO can do other stuff that is not implemented - - """ - self.i2c.write_register("eventBuffer.EventFifoCSR", value) - - def write_status(self, value: int) -> None: - """Sets value to the 'SerdesRstW' register. - - Args: - value (int): Bit 0 resets the status, bit 1 resets trigger counters and bit 2 calibrates IDELAY. - """ - self.i2c.write_register("triggerInputs.SerdesRstW", value) - - def set_run_active(self, state: bool) -> None: - """Raises internal run active signal. - - Args: - state (bool): True sets run active, False disables it. - """ - if type(state) != bool: - raise TypeError("State has to be bool") - self.i2c.write_register("Shutter.RunActiveRW", int(state)) - self.log.info("Run active: %s" % self.get_run_active()) - - def get_run_active(self) -> bool: - """Reads register 'RunActiveRW' - - Returns: - bool: Returns bool of the run active register. - """ - return bool(self.i2c.read_register("Shutter.RunActiveRW")) - - def start_run(self) -> None: - """Start run configurations""" - self.reset_counters() - self.reset_fifo() - self.reset_timestamp() - self.set_run_active(True) - self.trigger_logic.set_trigger_veto(False) - def stop_run(self) -> None: - """Stop run configurations""" - self.trigger_logic.set_trigger_veto(True) - self.set_run_active(False) self.run_number += 1 - def set_enable_record_data(self, value: int) -> None: - """#TODO not sure what this does. Looks like a separate internal event buffer to the FIFO. - - Args: - value (int): #TODO I think this does not work - """ - self.i2c.write_register("Event_Formatter.Enable_Record_Data", value) - - def get_event_fifo_csr(self) -> int: - """Reads value from 'EventFifoCSR', corresponds to status flags of the FIFO. - - Returns: - int: number of events - """ - return self.i2c.read_register("eventBuffer.EventFifoCSR") - - def get_event_fifo_fill_level(self) -> int: - """Reads value from 'EventFifoFillLevel' - Returns the number of words written in - the FIFO. The lowest 14-bits are the actual data. - - Returns: - int: buffer level of the fifi - """ - return self.i2c.read_register("eventBuffer.EventFifoFillLevel") - - def get_timestamp(self) -> int: - """Get current time stamp. - - Returns: - int: Time stamp is not formatted. - """ - time = self.i2c.read_register("Event_Formatter.CurrentTimestampHR") - time = time << 32 - time = time + self.i2c.read_register("Event_Formatter.CurrentTimestampLR") - return time - - def pull_fifo_event(self) -> list: - """Pulls event from the FIFO. This is needed in the run loop to prevent the buffer to get stuck. - if this register is full the fifo needs to be reset or new triggers are generated but not sent out. - #TODO check here if the FIFO is full and reset it if needed would prob. make sense. - - Returns: - list: 6 element long vector containing bitwords of the data. - """ - event_numb = self.get_event_fifo_fill_level() - if event_numb: - fifo_content = self.i2c_hw.getNode("eventBuffer.EventFifoData").readBlock( - event_numb - ) - self.i2c_hw.dispatch() - return np.array(fifo_content) - pass - - def get_scaler(self, channel: int) -> int: - """reads current scaler value from register""" - if channel < 0 or channel > 5: - raise ValueError("Only channels 0 to 5 are valid") - return self.i2c.read_register(f"triggerInputs.ThrCount{channel:d}R") - - def get_scalers(self) -> list: - """reads current sc values from registers - - Returns: - list: all 6 trigger sc values - """ - return [self.get_scaler(n) for n in range(6)] - def init_raw_data_table(self) -> None: """Initializes the raw data table, where the raw FIFO data is found.""" data_dtype = np.dtype([("raw", "u4")]) From 83eb6de9488645cfb9c5d1e82f1fffcdf4e15b1a Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Wed, 23 Jul 2025 10:49:30 +0200 Subject: [PATCH 02/15] refactored(main): reworked tlu main control script --- aidatlu/hardware/tlu_controller.py | 3 ++ aidatlu/main/tlu.py | 68 +++++++++++++++--------------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/aidatlu/hardware/tlu_controller.py b/aidatlu/hardware/tlu_controller.py index 457a7a8..1369e5e 100644 --- a/aidatlu/hardware/tlu_controller.py +++ b/aidatlu/hardware/tlu_controller.py @@ -64,6 +64,9 @@ def stop_run(self) -> None: ### Basic TLU Control Functions ### + def write_clock_config(self, clock_config_path): + self.clock_controller.write_clock_conf(clock_config_path) + def get_device_id(self) -> int: """Read back board id. Consists of six blocks of hex data diff --git a/aidatlu/main/tlu.py b/aidatlu/main/tlu.py index 1b9d177..15d229b 100644 --- a/aidatlu/main/tlu.py +++ b/aidatlu/main/tlu.py @@ -8,12 +8,8 @@ import zmq from aidatlu import logger -from aidatlu.hardware.clock_controller import ClockControl -from aidatlu.hardware.dac_controller import DacControl -from aidatlu.hardware.dut_controller import DUTLogic +from aidatlu.hardware.tlu_controller import TLUControl from aidatlu.hardware.i2c import I2CCore -from aidatlu.hardware.ioexpander_controller import IOControl -from aidatlu.hardware.trigger_controller import TriggerLogic from aidatlu.main.config_parser import Configure, yaml_parser from aidatlu.main.data_parser import interpret_data @@ -22,7 +18,9 @@ class AidaTLU: def __init__(self, hw, config_dict, clock_config_path, i2c=I2CCore) -> None: self.log = logger.setup_main_logger(__class__.__name__) - self.clock_controller.write_clock_conf(clock_config_path) + + self.tlu_controller = TLUControl(hw=hw, i2c=i2c) + self.tlu_controller.write_clock_config(clock_config_path) self.reset_configuration() self.config_parser = Configure(self, config_dict) @@ -33,17 +31,19 @@ def configure(self) -> None: """loads the conf.yaml and configures the TLU accordingly.""" self.config_parser.configure() self.conf_list = self.config_parser.get_configuration_table() - self.get_event_fifo_fill_level() - self.get_event_fifo_csr() - self.get_scalers() + self.tlu_controller.get_event_fifo_fill_level() + self.tlu_controller.get_event_fifo_csr() + self.tlu_controller.get_scalers() def reset_configuration(self) -> None: + self.tlu_controller.reset_configuration() try: self.h5_file.close() except: pass def stop_run(self) -> None: + self.tlu_controller.stop_run() self.run_number += 1 def init_raw_data_table(self) -> None: @@ -76,7 +76,7 @@ def handle_status(self) -> None: t = threading.current_thread() while getattr(t, "do_run", True): time.sleep(0.5) - last_time = self.get_timestamp() + last_time = self.tlu_controller.get_timestamp() current_time = (last_time - self.start_time) * 25 / 1000000000 # Logs and poss. sends status every 1s. if current_time - self.last_time > 1: @@ -86,7 +86,7 @@ def handle_status(self) -> None: if current_time > self.timeout: self.stop_condition = True if self.max_trigger != None: - if self.trigger_logic.get_post_veto_trigger() > self.max_trigger: + if self.tlu_controller.trigger_logic.get_post_veto_trigger() > self.max_trigger: self.stop_condition = True def log_sent_status(self, time: int) -> None: @@ -97,15 +97,15 @@ def log_sent_status(self, time: int) -> None: time (int): current runtime of the TLU """ self.post_veto_rate = ( - self.trigger_logic.get_post_veto_trigger() - self.last_post_veto_trigger + self.tlu_controller.trigger_logic.get_post_veto_trigger() - self.last_post_veto_trigger ) / (time - self.last_time) self.pre_veto_rate = ( - self.trigger_logic.get_pre_veto_trigger() - self.last_pre_veto_trigger + self.tlu_controller.trigger_logic.get_pre_veto_trigger() - self.last_pre_veto_trigger ) / (time - self.last_time) self.run_time = time - self.total_post_veto = self.trigger_logic.get_post_veto_trigger() - self.total_pre_veto = self.trigger_logic.get_pre_veto_trigger() - s0, s1, s2, s3, s4, s5 = self.get_scalers() + self.total_post_veto = self.tlu_controller.trigger_logic.get_post_veto_trigger() + self.total_pre_veto = self.tlu_controller.trigger_logic.get_pre_veto_trigger() + s0, s1, s2, s3, s4, s5 = self.tlu_controller.get_scalers() if self.zmq_address: self.socket.send_string( @@ -122,8 +122,8 @@ def log_sent_status(self, time: int) -> None: ) self.last_time = time - self.last_post_veto_trigger = self.trigger_logic.get_post_veto_trigger() - self.last_pre_veto_trigger = self.trigger_logic.get_pre_veto_trigger() + self.last_post_veto_trigger = self.tlu_controller.trigger_logic.get_post_veto_trigger() + self.last_pre_veto_trigger = self.tlu_controller.trigger_logic.get_pre_veto_trigger() self.log.info( "Run time: %.1f s, Pre veto: %s, Post veto: %s, Pre veto rate: %.f Hz, Post veto rate.: %.f Hz" @@ -137,27 +137,27 @@ def log_sent_status(self, time: int) -> None: ) self.log.debug("Scaler %i:%i:%i:%i:%i:%i" % (s0, s1, s2, s3, s4, s5)) - self.log.debug("FIFO level: %s" % self.get_event_fifo_fill_level()) - self.log.debug("FIFO level 2: %s" % self.get_event_fifo_csr()) + self.log.debug("FIFO level: %s" % self.tlu_controller.get_event_fifo_fill_level()) + self.log.debug("FIFO level 2: %s" % self.tlu_controller.get_event_fifo_csr()) self.log.debug( "fifo csr: %s fifo fill level: %s" - % (self.get_event_fifo_fill_level(), self.get_event_fifo_csr()) + % (self.tlu_controller.get_event_fifo_fill_level(), self.tlu_controller.get_event_fifo_csr()) ) self.log.debug( "post: %s pre: %s" % ( - self.trigger_logic.get_post_veto_trigger(), - self.trigger_logic.get_pre_veto_trigger(), + self.tlu_controller.trigger_logic.get_post_veto_trigger(), + self.tlu_controller.trigger_logic.get_pre_veto_trigger(), ) ) - self.log.debug("time stamp: %s" % (self.get_timestamp())) + self.log.debug("time stamp: %s" % (self.tlu_controller.get_timestamp())) if ( self.run_time < 10 ): # Logs trigger configuration when logging level is debug for the first 10s - current_event = self.pull_fifo_event() + current_event = self.tlu_controller.pull_fifo_event() if np.size(current_event) > 1: self.log_trigger_inputs(current_event[0:6]) - if self.get_event_fifo_csr() == 0x10: + if self.tlu_controller.get_event_fifo_csr() == 0x10: self.log.warning("FIFO is full") def log_trigger_inputs(self, event_vector: list) -> None: @@ -209,14 +209,14 @@ def run(self) -> None: def start_run_configuration(self) -> None: """Start of the run configurations, consists of timestamp resets, data preparations and zmq connections initialization.""" - self.start_run() - self.get_fw_version() - self.get_device_id() + self.tlu_controller.start_run() + self.tlu_controller.get_fw_version() + self.tlu_controller.get_device_id() # reset starting parameter - self.start_time = self.get_timestamp() + self.start_time = self.tlu_controller.get_timestamp() self.last_time = 0 - self.last_post_veto_trigger = self.trigger_logic.get_post_veto_trigger() - self.last_pre_veto_trigger = self.trigger_logic.get_pre_veto_trigger() + self.last_post_veto_trigger = self.tlu_controller.trigger_logic.get_post_veto_trigger() + self.last_pre_veto_trigger = self.tlu_controller.trigger_logic.get_pre_veto_trigger() self.stop_condition = False # prepare data handling and zmq connection self.save_data = self.config_parser.get_data_handling() @@ -249,7 +249,7 @@ def run_loop(self) -> None: KeyboardInterrupt: The run loop can be interrupted when raising a KeyboardInterrupt. """ try: - current_event = self.pull_fifo_event() + current_event = self.tlu_controller.pull_fifo_event() try: if self.save_data and np.size(current_event) > 1: self.data_table.append(current_event) @@ -268,7 +268,7 @@ def run_loop(self) -> None: def stop_run_configuration(self) -> None: """Cleans remaining FIFO data and closes data files and zmq connections after a run.""" # Cleanup of FIFO - self.pull_fifo_event() + self.tlu_controller.pull_fifo_event() if self.zmq_address: self.socket.close() From f19d8ae7c6b504a1a0cf38e3750147b875e3feb0 Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Wed, 23 Jul 2025 13:40:12 +0200 Subject: [PATCH 03/15] ci: fixed tests --- aidatlu/hardware/tlu_controller.py | 2 + aidatlu/main/config_parser.py | 12 +++--- aidatlu/main/tlu.py | 3 +- .../test/fixtures/tlu_test_configuration.toml | 2 +- .../test/fixtures/tlu_test_configuration.yaml | 2 +- aidatlu/test/test_configuration.py | 5 +-- aidatlu/test/test_tlu.py | 40 +++++++++++-------- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/aidatlu/hardware/tlu_controller.py b/aidatlu/hardware/tlu_controller.py index 1369e5e..306843f 100644 --- a/aidatlu/hardware/tlu_controller.py +++ b/aidatlu/hardware/tlu_controller.py @@ -29,6 +29,8 @@ def __init__(self, hw, i2c=I2CCore) -> None: self.trigger_logic = TriggerLogic(self.i2c) self.dut_logic = DUTLogic(self.i2c) + self.reset_configuration() + ### General TLU Functions ### def reset_configuration(self) -> None: diff --git a/aidatlu/main/config_parser.py b/aidatlu/main/config_parser.py index bd3dd66..8b7ca19 100644 --- a/aidatlu/main/config_parser.py +++ b/aidatlu/main/config_parser.py @@ -20,11 +20,11 @@ def configure(self) -> None: self.tlu.set_enable_record_data(1) self.log.info("TLU configured") - def get_data_handling(self) -> tuple: + def get_data_handling(self) -> bool: """Information about data handling. Returns: - tuple: two bools, save and interpret data. + bool: save and interpret data. """ return self.conf["save_data"] @@ -76,18 +76,18 @@ def conf_dut(self) -> None: dut = [0, 0, 0, 0] dut_mode = [0, 0, 0, 0] for i in range(4): - if self.tlu.config_parser.conf["DUT_%s" % (i + 1)] == "eudet": + if self.conf["DUT_%s" % (i + 1)] == "eudet": self.tlu.io_controller.switch_led(i + 1, "g") dut[i] = 2**i # Clock output needs to be disabled for EUDET mode. self.tlu.io_controller.clock_hdmi_output(i + 1, "off") - if self.tlu.config_parser.conf["DUT_%s" % (i + 1)] == "aidatrig": + if self.conf["DUT_%s" % (i + 1)] == "aidatrig": self.tlu.io_controller.switch_led(i + 1, "w") dut[i] = 2**i dut_mode[i] = 2 ** (2 * i) # In AIDA mode the clock output is needed. self.tlu.io_controller.clock_hdmi_output(i + 1, "chip") - if self.tlu.config_parser.conf["DUT_%s" % (i + 1)] == "aida": + if self.conf["DUT_%s" % (i + 1)] == "aida": self.tlu.io_controller.switch_led(i + 1, "b") dut[i] = 2**i dut_mode[i] = 3 * (2) ** (2 * i) @@ -99,7 +99,7 @@ def conf_dut(self) -> None: "DUT %i configured in %s" % ( (i + 1), - self.tlu.config_parser.conf["DUT_%s" % (i + 1)], + self.conf["DUT_%s" % (i + 1)], ) ) for i in range(4) diff --git a/aidatlu/main/tlu.py b/aidatlu/main/tlu.py index 15d229b..356afad 100644 --- a/aidatlu/main/tlu.py +++ b/aidatlu/main/tlu.py @@ -23,7 +23,7 @@ def __init__(self, hw, config_dict, clock_config_path, i2c=I2CCore) -> None: self.tlu_controller.write_clock_config(clock_config_path) self.reset_configuration() - self.config_parser = Configure(self, config_dict) + self.config_parser = Configure(self.tlu_controller, config_dict) self.log.success("TLU initialized") @@ -37,6 +37,7 @@ def configure(self) -> None: def reset_configuration(self) -> None: self.tlu_controller.reset_configuration() + self.run_number = 0 try: self.h5_file.close() except: diff --git a/aidatlu/test/fixtures/tlu_test_configuration.toml b/aidatlu/test/fixtures/tlu_test_configuration.toml index dd71348..e58b2a0 100644 --- a/aidatlu/test/fixtures/tlu_test_configuration.toml +++ b/aidatlu/test/fixtures/tlu_test_configuration.toml @@ -10,7 +10,7 @@ trigger_signal_delay = [0, 1, 0, 0, 3, 0] enable_clock_lemo_output = 'True' pmt_power = [0.8, 0.8, 0, -0.2] save_data = 'True' -output_data_path = 'aidatlu/test/fixtures/test_output_data/' +output_data_path = 'aidatlu/test/fixtures/test_output_data' zmq_connection = 'False' # Optional stop conditions can also be added to the configuration. diff --git a/aidatlu/test/fixtures/tlu_test_configuration.yaml b/aidatlu/test/fixtures/tlu_test_configuration.yaml index 4ba3c5a..b693ba3 100644 --- a/aidatlu/test/fixtures/tlu_test_configuration.yaml +++ b/aidatlu/test/fixtures/tlu_test_configuration.yaml @@ -60,7 +60,7 @@ pmt_control: # Save and generate interpreted data from the raw data set. Set to 'True' or 'False'. # If no specific output path is provided, the data is saved in the default output data path (aidatlu/aidatlu/tlu_data). save_data: True -output_data_path: 'aidatlu/test/fixtures/test_output_data/' +output_data_path: 'aidatlu/test/fixtures/test_output_data' # zmq connection for status messages, leave it blank or set to off if not needed zmq_connection: off #"tcp://:7500" diff --git a/aidatlu/test/test_configuration.py b/aidatlu/test/test_configuration.py index e616a73..cfc97ec 100644 --- a/aidatlu/test/test_configuration.py +++ b/aidatlu/test/test_configuration.py @@ -5,6 +5,7 @@ from aidatlu.hardware.ioexpander_controller import IOControl from aidatlu.main.tlu import AidaTLU from aidatlu.hardware.i2c import I2CCore +from aidatlu.hardware.tlu_controller import TLUControl from aidatlu.test.utils import MockI2C FILEPATH = Path(__file__).parent @@ -29,10 +30,8 @@ HW = uhal.HwInterface(manager.getDevice("aida_tlu.controlhub")) I2CMETHOD = I2CCore -TLU = AidaTLU( +TLU = TLUControl( HW, - CONFIG_FILE, - FILEPATH / "../misc/aida_tlu_clk_config.txt", i2c=I2CMETHOD, ) diff --git a/aidatlu/test/test_tlu.py b/aidatlu/test/test_tlu.py index afb6c02..3b29ca1 100644 --- a/aidatlu/test/test_tlu.py +++ b/aidatlu/test/test_tlu.py @@ -4,6 +4,7 @@ import pytest from aidatlu.main.tlu import AidaTLU from aidatlu.hardware.i2c import I2CCore +from aidatlu.hardware.tlu_controller import TLUControl from aidatlu.test.utils import MockI2C from aidatlu.main.config_parser import yaml_parser @@ -35,26 +36,31 @@ i2c=I2CMETHOD, ) +TLUCONTROL = TLUControl( + HW, + i2c=I2CMETHOD, +) def test_check_ups(): """Test read write TLU configurations""" + TLUCONTROL.reset_configuration() if MOCK: - TLU.set_event_fifo_csr(0) - assert TLU.get_event_fifo_csr() == 0 - assert TLU.get_device_id() == 0xFFFFFFFFFFFF - assert TLU.get_fw_version() == -1 - assert TLU.get_run_active() == 0 - assert TLU.get_event_fifo_fill_level() == -1 - assert TLU.get_timestamp() == -0x100000001 - assert TLU.get_scalers() == [-1, -1, -1, -1, -1, -1] + TLUCONTROL.set_event_fifo_csr(0) + assert TLUCONTROL.get_event_fifo_csr() == 0 + assert TLUCONTROL.get_device_id() == 0xFFFFFFFFFFFF + assert TLUCONTROL.get_fw_version() == -1 + assert TLUCONTROL.get_run_active() == 0 + assert TLUCONTROL.get_event_fifo_fill_level() == -1 + assert TLUCONTROL.get_timestamp() == -0x100000001 + assert TLUCONTROL.get_scalers() == [-1, -1, -1, -1, -1, -1] with pytest.raises(ValueError): - TLU.get_scaler(6) + TLUCONTROL.get_scaler(6) else: - TLU.set_event_fifo_csr(0) - assert TLU.get_event_fifo_csr() == 3 - assert TLU.get_run_active() == 0 - assert TLU.get_event_fifo_fill_level() == 0 + TLUCONTROL.set_event_fifo_csr(0) + assert TLUCONTROL.get_event_fifo_csr() == 3 + assert TLUCONTROL.get_run_active() == 0 + assert TLUCONTROL.get_event_fifo_fill_level() == 0 def test_configuration(): @@ -78,10 +84,10 @@ def _pull_fifo_event(self): return 0 # Overwrite TLU methods needed for run loop - func_type = type(TLU.get_timestamp) - TLU.get_timestamp = func_type(_get_timestamp, TLU) - func_type = type(TLU.pull_fifo_event) - TLU.pull_fifo_event = func_type(_pull_fifo_event, TLU) + func_type = type(TLUCONTROL.get_timestamp) + TLU.tlu_controller.get_timestamp = func_type(_get_timestamp, TLUCONTROL) + func_type = type(TLUCONTROL.pull_fifo_event) + TLU.tlu_controller.pull_fifo_event = func_type(_pull_fifo_event, TLUCONTROL) TLU.configure() TLU.run() From 81f3dfe7796a76bfb7e6522537402abd080f4acf Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Wed, 23 Jul 2025 15:12:41 +0200 Subject: [PATCH 04/15] fix(constellation): refactored satellite --- aidatlu/constellation/aidatlu_satellite.py | 76 +++++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/aidatlu/constellation/aidatlu_satellite.py b/aidatlu/constellation/aidatlu_satellite.py index 5adf1bb..b2f95ee 100644 --- a/aidatlu/constellation/aidatlu_satellite.py +++ b/aidatlu/constellation/aidatlu_satellite.py @@ -16,8 +16,8 @@ from aidatlu import logger from aidatlu.hardware.i2c import I2CCore -from aidatlu.main.config_parser import toml_parser -from aidatlu.main.tlu import AidaTLU as TLU +from aidatlu.main.config_parser import Configure, toml_parser +from aidatlu.hardware.tlu_controller import TLUControl as TLU from aidatlu.test.utils import MockI2C @@ -53,7 +53,11 @@ def do_initializing(self, config: Configuration) -> str: return "Initializing complete" def do_launching(self, payload: Any = None) -> str: - self.aidatlu.configure() + self.config_parser.configure() + self.conf_list = self.config_parser.get_configuration_table() + self.aidatlu.get_event_fifo_fill_level() + self.aidatlu.get_event_fifo_csr() + self.aidatlu.get_scalers() return "Do launching complete" def do_landing(self) -> str: @@ -82,7 +86,14 @@ def _pull_fifo_event(self): func_type = type(self.aidatlu.pull_fifo_event) self.aidatlu.pull_fifo_event = func_type(_pull_fifo_event, self.aidatlu) - self.aidatlu.start_run_configuration() + self.aidatlu.start_run() + self.aidatlu.get_fw_version() + self.aidatlu.get_device_id() + # reset starting parameter + self.start_time = self.aidatlu.get_timestamp() + self.last_time = 0 + self.last_post_veto_trigger = self.aidatlu.trigger_logic.get_post_veto_trigger() + self.last_pre_veto_trigger = self.aidatlu.trigger_logic.get_pre_veto_trigger() # Set Begin-of-run tags self.BOR["BoardID"] = self.aidatlu.get_device_id() @@ -94,7 +105,7 @@ def _pull_fifo_event(self): return "Do starting complete" def do_run(self, run_identifier: str) -> str: - t = threading.Thread(target=self.aidatlu.handle_status) + t = threading.Thread(target=self.handle_status) t.start() # We ideally pull 6 uint32s, but we might pull more or less @@ -112,7 +123,8 @@ def do_run(self, run_identifier: str) -> str: return "Do running complete" def do_stopping(self) -> str: - self.aidatlu.stop_run_configuration() + self.aidatlu.pull_fifo_event() + self.log.info("Run finished") return "Do stopping complete" def _init_tlu(self, config: Configuration) -> None: @@ -124,9 +136,9 @@ def _init_tlu(self, config: Configuration) -> None: else: self.clock_file = self.config_file["clock_config"] self.aidatlu = TLU( - self.hw, self.config_file, self.clock_file, i2c=self.i2c_method + self.hw, i2c=self.i2c_method ) - + self.config_parser = Configure(self.aidatlu, self.config_file) # Resets aidatlu loggers and replaces them with constellation loggers logger._reset_all_loggers() self.aidatlu.log = self.log @@ -135,7 +147,7 @@ def _init_tlu(self, config: Configuration) -> None: self.aidatlu.clock_controller.log = self.log self.aidatlu.trigger_logic.log = self.log self.aidatlu.dut_logic.log = self.log - self.aidatlu.config_parser.log = self.log + self.config_parser.log = self.log def _handle_event(self, evt: list) -> None: # Calculate timestamp in nanoseconds from TLU 40MHz clock: @@ -151,6 +163,52 @@ def _handle_event(self, evt: list) -> None: payload = np.array(evt, dtype=" None: + """Status message handling in separate thread. Calculates run time and obtain trigger information and sent it out every second.""" + t = threading.current_thread() + while getattr(t, "do_run", True): + time.sleep(0.5) + last_time = self.aidatlu.get_timestamp() + current_time = (last_time - self.start_time) * 25 / 1000000000 + # Logs and poss. sends status every 1s. + if current_time - self.last_time > 1: + self.log_status(current_time) + + def log_status(self, time: int) -> None: + """Logs the status of the TLU run with trigger number, runtime usw. + Also calculates the mean trigger frequency between function calls. + + Args: + time (int): current runtime of the TLU + """ + self.post_veto_rate = ( + self.aidatlu.get_post_veto_trigger_number() - self.last_post_veto_trigger + ) / (time - self.last_time) + self.pre_veto_rate = ( + self.aidatlu.get_pre_veto_trigger_number() - self.last_pre_veto_trigger + ) / (time - self.last_time) + self.run_time = time + self.total_post_veto = self.aidatlu.get_post_veto_trigger_number() + self.total_pre_veto = self.aidatlu.get_pre_veto_trigger_number() + s0, s1, s2, s3, s4, s5 = self.aidatlu.get_scalers() + + self.last_time = time + self.last_post_veto_trigger = self.aidatlu.get_post_veto_trigger_number() + self.last_pre_veto_trigger = self.aidatlu.get_pre_veto_trigger_number() + + self.log.info( + "Run time: %.1f s, Pre veto: %s, Post veto: %s, Pre veto rate: %.f Hz, Post veto rate.: %.f Hz" + % ( + self.run_time, + self.total_pre_veto, + self.total_post_veto, + self.pre_veto_rate, + self.post_veto_rate, + ) + ) + if self.aidatlu.get_event_fifo_csr() == 0x10: + self.log.warning("FIFO is full") + @schedule_metric("Hz", MetricsType.LAST_VALUE, 1) def pre_veto_rate(self) -> Any: if self.fsm.current_state_value == SatelliteState.RUN and hasattr( From 97764e5a2903c2fac6d4bb11a77d6401757cc1e5 Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Wed, 23 Jul 2025 15:19:49 +0200 Subject: [PATCH 05/15] sty: pre-commit --- aidatlu/constellation/aidatlu_satellite.py | 26 ++++++--------- aidatlu/hardware/tlu_controller.py | 18 +++++------ aidatlu/main/tlu.py | 37 ++++++++++++++++------ aidatlu/test/test_tlu.py | 1 + 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/aidatlu/constellation/aidatlu_satellite.py b/aidatlu/constellation/aidatlu_satellite.py index b2f95ee..a7942f4 100644 --- a/aidatlu/constellation/aidatlu_satellite.py +++ b/aidatlu/constellation/aidatlu_satellite.py @@ -135,9 +135,7 @@ def _init_tlu(self, config: Configuration) -> None: self.clock_file = str(self.file_path) + "/../misc/aida_tlu_clk_config.txt" else: self.clock_file = self.config_file["clock_config"] - self.aidatlu = TLU( - self.hw, i2c=self.i2c_method - ) + self.aidatlu = TLU(self.hw, i2c=self.i2c_method) self.config_parser = Configure(self.aidatlu, self.config_file) # Resets aidatlu loggers and replaces them with constellation loggers logger._reset_all_loggers() @@ -208,40 +206,36 @@ def log_status(self, time: int) -> None: ) if self.aidatlu.get_event_fifo_csr() == 0x10: self.log.warning("FIFO is full") - + @schedule_metric("Hz", MetricsType.LAST_VALUE, 1) def pre_veto_rate(self) -> Any: if self.fsm.current_state_value == SatelliteState.RUN and hasattr( - self.aidatlu, "pre_veto_rate" + self, "pre_veto_rate" ): - return self.aidatlu.pre_veto_rate + return self.pre_veto_rate else: return None @schedule_metric("Hz", MetricsType.LAST_VALUE, 1) def post_veto_rate(self) -> Any: if self.fsm.current_state_value == SatelliteState.RUN and hasattr( - self.aidatlu, "post_veto_rate" + self, "post_veto_rate" ): - return self.aidatlu.post_veto_rate + return self.post_veto_rate else: return None @schedule_metric("", MetricsType.LAST_VALUE, 1) def post_veto(self) -> Any: - if self.fsm.current_state_value == SatelliteState.RUN and hasattr( - self.aidatlu, "total_post_veto" - ): - return self.aidatlu.total_post_veto + if self.fsm.current_state_value == SatelliteState.RUN: + return self.aidatlu.get_post_veto_trigger_number() else: return None @schedule_metric("", MetricsType.LAST_VALUE, 1) def pre_veto(self) -> Any: - if self.fsm.current_state_value == SatelliteState.RUN and hasattr( - self.aidatlu, "total_pre_veto" - ): - return self.aidatlu.total_pre_veto + if self.fsm.current_state_value == SatelliteState.RUN: + return self.aidatlu.get_pre_veto_trigger_number() else: return None diff --git a/aidatlu/hardware/tlu_controller.py b/aidatlu/hardware/tlu_controller.py index 306843f..cfbe0ac 100644 --- a/aidatlu/hardware/tlu_controller.py +++ b/aidatlu/hardware/tlu_controller.py @@ -8,9 +8,9 @@ from aidatlu.hardware.ioexpander_controller import IOControl from aidatlu.hardware.trigger_controller import TriggerLogic + class TLUControl: - """Controls general TLU functionalities. - """ + """Controls general TLU functionalities.""" def __init__(self, hw, i2c=I2CCore) -> None: self.log = logger.setup_main_logger(__class__.__name__) @@ -30,7 +30,7 @@ def __init__(self, hw, i2c=I2CCore) -> None: self.dut_logic = DUTLogic(self.i2c) self.reset_configuration() - + ### General TLU Functions ### def reset_configuration(self) -> None: @@ -79,10 +79,10 @@ def get_device_id(self) -> int: for addr in range(6): id.append(self.i2c.read(self.i2c.modules["eeprom"], 0xFA + addr) & 0xFF) return int("0x" + "".join(["{:x}".format(i) for i in id]), 16) & 0xFFFFFFFFFFFF - + def get_fw_version(self) -> int: return self.i2c.read_register("version") - + def reset_timestamp(self) -> None: """Sets bit to 'ResetTimestampW' register to reset the time stamp.""" self.i2c.write_register("Event_Formatter.ResetTimestampW", 1) @@ -174,7 +174,7 @@ def get_timestamp(self) -> int: time = self.i2c.read_register("Event_Formatter.CurrentTimestampHR") time = time << 32 time = time + self.i2c.read_register("Event_Formatter.CurrentTimestampLR") - return time + return time def pull_fifo_event(self) -> list: """Pulls event from the FIFO. This is needed in the run loop to prevent the buffer to get stuck. @@ -206,11 +206,11 @@ def get_scalers(self) -> list: list: all 6 trigger sc values """ return [self.get_scaler(n) for n in range(6)] - + def get_pre_veto_trigger_number(self) -> int: - """Optains the number of triggers recorded in the TLU before the veto is applied from the trigger logic register""" + """Obtains the number of triggers recorded in the TLU before the veto is applied from the trigger logic register""" return self.trigger_logic.get_pre_veto_trigger() def get_post_veto_trigger_number(self) -> int: - """Optains the number of triggers recorded in the TLU after the veto is applied from the trigger logic register""" + """Obtains the number of triggers recorded in the TLU after the veto is applied from the trigger logic register""" return self.trigger_logic.get_post_veto_trigger() diff --git a/aidatlu/main/tlu.py b/aidatlu/main/tlu.py index 356afad..e245740 100644 --- a/aidatlu/main/tlu.py +++ b/aidatlu/main/tlu.py @@ -18,7 +18,6 @@ class AidaTLU: def __init__(self, hw, config_dict, clock_config_path, i2c=I2CCore) -> None: self.log = logger.setup_main_logger(__class__.__name__) - self.tlu_controller = TLUControl(hw=hw, i2c=i2c) self.tlu_controller.write_clock_config(clock_config_path) @@ -87,7 +86,10 @@ def handle_status(self) -> None: if current_time > self.timeout: self.stop_condition = True if self.max_trigger != None: - if self.tlu_controller.trigger_logic.get_post_veto_trigger() > self.max_trigger: + if ( + self.tlu_controller.trigger_logic.get_post_veto_trigger() + > self.max_trigger + ): self.stop_condition = True def log_sent_status(self, time: int) -> None: @@ -98,10 +100,12 @@ def log_sent_status(self, time: int) -> None: time (int): current runtime of the TLU """ self.post_veto_rate = ( - self.tlu_controller.trigger_logic.get_post_veto_trigger() - self.last_post_veto_trigger + self.tlu_controller.trigger_logic.get_post_veto_trigger() + - self.last_post_veto_trigger ) / (time - self.last_time) self.pre_veto_rate = ( - self.tlu_controller.trigger_logic.get_pre_veto_trigger() - self.last_pre_veto_trigger + self.tlu_controller.trigger_logic.get_pre_veto_trigger() + - self.last_pre_veto_trigger ) / (time - self.last_time) self.run_time = time self.total_post_veto = self.tlu_controller.trigger_logic.get_post_veto_trigger() @@ -123,8 +127,12 @@ def log_sent_status(self, time: int) -> None: ) self.last_time = time - self.last_post_veto_trigger = self.tlu_controller.trigger_logic.get_post_veto_trigger() - self.last_pre_veto_trigger = self.tlu_controller.trigger_logic.get_pre_veto_trigger() + self.last_post_veto_trigger = ( + self.tlu_controller.trigger_logic.get_post_veto_trigger() + ) + self.last_pre_veto_trigger = ( + self.tlu_controller.trigger_logic.get_pre_veto_trigger() + ) self.log.info( "Run time: %.1f s, Pre veto: %s, Post veto: %s, Pre veto rate: %.f Hz, Post veto rate.: %.f Hz" @@ -138,11 +146,16 @@ def log_sent_status(self, time: int) -> None: ) self.log.debug("Scaler %i:%i:%i:%i:%i:%i" % (s0, s1, s2, s3, s4, s5)) - self.log.debug("FIFO level: %s" % self.tlu_controller.get_event_fifo_fill_level()) + self.log.debug( + "FIFO level: %s" % self.tlu_controller.get_event_fifo_fill_level() + ) self.log.debug("FIFO level 2: %s" % self.tlu_controller.get_event_fifo_csr()) self.log.debug( "fifo csr: %s fifo fill level: %s" - % (self.tlu_controller.get_event_fifo_fill_level(), self.tlu_controller.get_event_fifo_csr()) + % ( + self.tlu_controller.get_event_fifo_fill_level(), + self.tlu_controller.get_event_fifo_csr(), + ) ) self.log.debug( "post: %s pre: %s" @@ -216,8 +229,12 @@ def start_run_configuration(self) -> None: # reset starting parameter self.start_time = self.tlu_controller.get_timestamp() self.last_time = 0 - self.last_post_veto_trigger = self.tlu_controller.trigger_logic.get_post_veto_trigger() - self.last_pre_veto_trigger = self.tlu_controller.trigger_logic.get_pre_veto_trigger() + self.last_post_veto_trigger = ( + self.tlu_controller.trigger_logic.get_post_veto_trigger() + ) + self.last_pre_veto_trigger = ( + self.tlu_controller.trigger_logic.get_pre_veto_trigger() + ) self.stop_condition = False # prepare data handling and zmq connection self.save_data = self.config_parser.get_data_handling() diff --git a/aidatlu/test/test_tlu.py b/aidatlu/test/test_tlu.py index 3b29ca1..24c780b 100644 --- a/aidatlu/test/test_tlu.py +++ b/aidatlu/test/test_tlu.py @@ -41,6 +41,7 @@ i2c=I2CMETHOD, ) + def test_check_ups(): """Test read write TLU configurations""" From 19b3e9b32aa18a5504d0e81fad0feeb7ba8a9e18 Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Mon, 28 Jul 2025 15:09:54 +0200 Subject: [PATCH 06/15] fix: init order for clock --- aidatlu/constellation/aidatlu_satellite.py | 1 + aidatlu/hardware/tlu_controller.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/aidatlu/constellation/aidatlu_satellite.py b/aidatlu/constellation/aidatlu_satellite.py index a7942f4..f4f248b 100644 --- a/aidatlu/constellation/aidatlu_satellite.py +++ b/aidatlu/constellation/aidatlu_satellite.py @@ -137,6 +137,7 @@ def _init_tlu(self, config: Configuration) -> None: self.clock_file = self.config_file["clock_config"] self.aidatlu = TLU(self.hw, i2c=self.i2c_method) self.config_parser = Configure(self.aidatlu, self.config_file) + self.aidatlu.reset_configuration() # Resets aidatlu loggers and replaces them with constellation loggers logger._reset_all_loggers() self.aidatlu.log = self.log diff --git a/aidatlu/hardware/tlu_controller.py b/aidatlu/hardware/tlu_controller.py index cfbe0ac..4ed52a3 100644 --- a/aidatlu/hardware/tlu_controller.py +++ b/aidatlu/hardware/tlu_controller.py @@ -29,8 +29,6 @@ def __init__(self, hw, i2c=I2CCore) -> None: self.trigger_logic = TriggerLogic(self.i2c) self.dut_logic = DUTLogic(self.i2c) - self.reset_configuration() - ### General TLU Functions ### def reset_configuration(self) -> None: From cf89e7cb779a5acab4dcdcbc51ed8231c7df2182 Mon Sep 17 00:00:00 2001 From: Simon Spannagel Date: Sat, 6 Sep 2025 18:09:34 +0200 Subject: [PATCH 07/15] Satellite: adapt to CDTP2 for COnstellation v0.6+ --- aidatlu/constellation/__main__.py | 4 ++-- aidatlu/constellation/aidatlu_satellite.py | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/aidatlu/constellation/__main__.py b/aidatlu/constellation/__main__.py index 2f9766d..0f13310 100644 --- a/aidatlu/constellation/__main__.py +++ b/aidatlu/constellation/__main__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 from constellation.core.logging import setup_cli_logging -from constellation.core.datasender import DataSenderArgumentParser +from constellation.core.transmitter_satellite import TransmitterSatelliteArgumentParser from .aidatlu_satellite import AidaTLU @@ -8,7 +8,7 @@ def main(args=None): """Controlling of a Satellite for the AIDA-2020 TLU""" - parser = DataSenderArgumentParser(description=main.__doc__) + parser = TransmitterSatelliteArgumentParser(description=main.__doc__) args = vars(parser.parse_args(args)) # set up logging diff --git a/aidatlu/constellation/aidatlu_satellite.py b/aidatlu/constellation/aidatlu_satellite.py index f4f248b..0aff94c 100644 --- a/aidatlu/constellation/aidatlu_satellite.py +++ b/aidatlu/constellation/aidatlu_satellite.py @@ -12,7 +12,7 @@ from constellation.core.configuration import Configuration from constellation.core.message.cscp1 import SatelliteState from constellation.core.monitoring import schedule_metric -from constellation.core.datasender import DataSender +from constellation.core.transmitter_satellite import TransmitterSatellite from aidatlu import logger from aidatlu.hardware.i2c import I2CCore @@ -21,7 +21,7 @@ from aidatlu.test.utils import MockI2C -class AidaTLU(DataSender): +class AidaTLU(TransmitterSatellite): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -96,11 +96,11 @@ def _pull_fifo_event(self): self.last_pre_veto_trigger = self.aidatlu.trigger_logic.get_pre_veto_trigger() # Set Begin-of-run tags - self.BOR["BoardID"] = self.aidatlu.get_device_id() + self.bor["BoardID"] = self.aidatlu.get_device_id() # For EudaqNativeWriter compatibility - self.BOR["eudaq_event"] = "TluRawDataEvent" - self.BOR["frames_as_blocks"] = True + self.bor["eudaq_event"] = "TluRawDataEvent" + self.bor["frames_as_blocks"] = True return "Do starting complete" @@ -160,7 +160,9 @@ def _handle_event(self, evt: list) -> None: } # New data format: store 6 uint32 as bytes in little-endian payload = np.array(evt, dtype=" None: """Status message handling in separate thread. Calculates run time and obtain trigger information and sent it out every second.""" From 1d8c77731bb9282c07f4daeecbc963b9e21eff31 Mon Sep 17 00:00:00 2001 From: simonspa <1677436+simonspa@users.noreply.github.com> Date: Sun, 7 Sep 2025 18:36:04 +0200 Subject: [PATCH 08/15] Add metadata to new data record Co-authored-by: Stephan Lachnit --- aidatlu/constellation/aidatlu_satellite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aidatlu/constellation/aidatlu_satellite.py b/aidatlu/constellation/aidatlu_satellite.py index 0aff94c..78962a1 100644 --- a/aidatlu/constellation/aidatlu_satellite.py +++ b/aidatlu/constellation/aidatlu_satellite.py @@ -160,7 +160,7 @@ def _handle_event(self, evt: list) -> None: } # New data format: store 6 uint32 as bytes in little-endian payload = np.array(evt, dtype=" Date: Wed, 22 Oct 2025 17:08:25 +0200 Subject: [PATCH 09/15] fix (tlu): refactored registers in status msg --- aidatlu/main/tlu.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aidatlu/main/tlu.py b/aidatlu/main/tlu.py index 4f2f076..e2607ab 100644 --- a/aidatlu/main/tlu.py +++ b/aidatlu/main/tlu.py @@ -108,8 +108,8 @@ def log_sent_status(self, time: int) -> None: - self.last_pre_veto_trigger ) / (time - self.last_time) self.run_time = time - self.total_post_veto = self.trigger_logic.get_post_veto_trigger() - self.total_pre_veto = self.trigger_logic.get_pre_veto_trigger() + self.total_post_veto = self.tlu_controller.trigger_logic.get_post_veto_trigger() + self.total_pre_veto = self.tlu_controller.trigger_logic.get_pre_veto_trigger() if self.zmq_address: self.socket.send_string( @@ -144,7 +144,7 @@ def log_sent_status(self, time: int) -> None: ) ) - if self.get_event_fifo_csr() == 0x10: + if self.tlu_controller.get_event_fifo_csr() == 0x10: self.log.warning("FIFO is full") def log_trigger_inputs(self, event_vector: list) -> None: From fbf697bbefec6fddebbf5c382fb3a1e667dbc459 Mon Sep 17 00:00:00 2001 From: Stephan Lachnit Date: Mon, 17 Nov 2025 14:15:43 +0100 Subject: [PATCH 10/15] DOC: update category and add parent class in Constellation README --- aidatlu/constellation/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aidatlu/constellation/README.md b/aidatlu/constellation/README.md index d634c24..872e341 100644 --- a/aidatlu/constellation/README.md +++ b/aidatlu/constellation/README.md @@ -1,8 +1,9 @@ --- title: "AidaTLU" description: "Satellite for the AIDA-2020 TLU using a Python based control software" +category: "Readout Systems" language: "Python" -category: "External" +parent_class: "TransmitterSatellite" --- ## Description From 346ed70a2c780c193dc127d0b6ba7f080438648f Mon Sep 17 00:00:00 2001 From: Simon Spannagel Date: Wed, 19 Nov 2025 11:51:40 +0100 Subject: [PATCH 11/15] fix: actually set the clock file to TLU controller --- aidatlu/constellation/aidatlu_satellite.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aidatlu/constellation/aidatlu_satellite.py b/aidatlu/constellation/aidatlu_satellite.py index 78962a1..85bb5a0 100644 --- a/aidatlu/constellation/aidatlu_satellite.py +++ b/aidatlu/constellation/aidatlu_satellite.py @@ -136,6 +136,8 @@ def _init_tlu(self, config: Configuration) -> None: else: self.clock_file = self.config_file["clock_config"] self.aidatlu = TLU(self.hw, i2c=self.i2c_method) + self.aidatlu.write_clock_config(self.clock_file) + self.config_parser = Configure(self.aidatlu, self.config_file) self.aidatlu.reset_configuration() # Resets aidatlu loggers and replaces them with constellation loggers From 1324bbed1a5388e210e995bf01d84dc5c00edbc0 Mon Sep 17 00:00:00 2001 From: Christian Bespin <11457592+cbespin@users.noreply.github.com> Date: Thu, 20 Nov 2025 10:11:10 +0100 Subject: [PATCH 12/15] Refine descriptions Updated descriptions with minor language-related points --- aidatlu/constellation/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/aidatlu/constellation/README.md b/aidatlu/constellation/README.md index 872e341..c0ec89d 100644 --- a/aidatlu/constellation/README.md +++ b/aidatlu/constellation/README.md @@ -1,6 +1,6 @@ --- title: "AidaTLU" -description: "Satellite for the AIDA-2020 TLU using a Python based control software" +description: "Satellite for the AIDA-2020 TLU using a Python-based control software" category: "Readout Systems" language: "Python" parent_class: "TransmitterSatellite" @@ -12,13 +12,13 @@ The AIDA-2020 Trigger Logic Unit is designed to provide flexible trigger configu The Python-based control software for the AIDA-2020 TLU provides a comprehensive interface for controlling the TLU. The software establishes a connection to the hardware and allows for easy configuration of different trigger setups. -Information over each individual trigger signal is saved in a compressed and human-readable HDF5 format. +Information about each individual trigger signal is saved in a compressed (blosc) and human-readable HDF5 format. The satellite connects the AIDA-2020 TLU to the [Constellation](https://constellation.pages.desy.de/) control and data acquisition framework. ## Building -After installing [IPbus](https://ipbus.web.cern.ch/doc/user/html/software/install/compile.html), with Python bindings (`uhal`), install the [Aida-TLU](https://github.com/SiLab-Bonn/aidatlu) package with the constellation requirement. +After installing [IPbus](https://ipbus.web.cern.ch/doc/user/html/software/install/compile.html) with Python bindings (`uhal`), install the [Aida-TLU](https://github.com/SiLab-Bonn/aidatlu) package with the constellation requirement. ```bash pip install .[constellation] @@ -28,7 +28,7 @@ A more detailed description of the prerequisites can also be found [here](https: ## Usage -Add the chosen cactus library path, where the default installation location is `/opt/cactus/`: +Add the chosen cactus library path, where the default install location is `/opt/cactus/`: ```sh export LD_LIBRARY_PATH=/lib @@ -55,16 +55,16 @@ This means DUT interface signals (e.g. clock signals) are disrupted during these | Configuration | Description | Type | Default Value | |-----------|-------------|------| ------| -| `internal_trigger_rate` | (Optional) Generates internal triggers with a given frequency given in Hz | Integer | 0 | -| `dut_interfaces` | (Required) Specify the operation mode of the DUT interface (`aida`, `eudet`, `aidatrig`, `off`) given as list with a required length of 4. Where `aida` and `eudet` corresponds to the classic AIDA and EUDET mode respectively and `aidatrig` to the AIDA-mode with handshake. Disable a DUT interface with `off`. | List | None | -| `trigger_threshold` | (Required) Threshold setting of each individual trigger input channel given in V | List | None | -| `trigger_inputs_logic` | (Required) Trigger Logic configuration accept a Python expression for the trigger inputs. The logic is set by using the variables for the input channels `CH1`, `CH2`, `CH3`, `CH4`, `CH5` and `CH6` and the Python logic operators `and`, `or`, `not` and so on. Don't forget to use brackets... | String | None | +| `internal_trigger_rate` | (Optional) Generates internal triggers with a given frequency given in Hz. | Integer | 0 | +| `dut_interfaces` | (Required) Specify the operation mode of the DUT interface (`aida`, `eudet`, `aidatrig`, `off`), given as list with a required length of 4. `aida` and `eudet` correspond to the classic AIDA and EUDET mode respectively and `aidatrig` to the AIDA-mode with handshake. Disable a DUT interface with `off`. | List | None | +| `trigger_threshold` | (Required) Threshold setting of each individual trigger input channel given in V. | List | None | +| `trigger_inputs_logic` | (Required) Trigger Logic configuration accepts a Python expression for the trigger inputs. The logic is set by using the variables for the input channels `CH1`, `CH2`, `CH3`, `CH4`, `CH5` and `CH6` and the Python logic operators `and`, `or`, `not` and so on. Don't forget to use brackets... | String | None | | `trigger_polarity` | (Optional) TLU can trigger on a rising or falling edge. Set to `rising` or `falling` | String | `falling` | | `trigger_signal_stretch` | (Required) Stretches each individual trigger input by a given number of clock cycles (corresponds to `6.25ns` steps) | List | None | | `trigger_signal_delay` | (Required) Delays each individual trigger input by a given number of clock cycles (corresponds to `6.25ns` steps) | List | None | | `enable_clock_lemo_output` | (Optional) Enable the LEMO clock output. | String | False | | `pmt_power` | (Required) Sets the four PMT control voltages in V | List | None | -| `clock_config` | (Optional) Specify a custom clock configuration. If no path is provided the TLU uses the default configuration. | String | None | +| `clock_config` | (Optional) Specify a custom clock configuration. If no path is provided, the TLU uses the default configuration. | String | None | The default clock configuration can be found in [`aidatlu/misc/aida_tlu_clk_config.txt`](https://github.com/SiLab-Bonn/aidatlu/blob/main/aidatlu/misc/aida_tlu_clk_config.txt). From e53769a8a0ee05ee94c5e0e79e661a289fc728fd Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Thu, 4 Dec 2025 09:56:28 +0100 Subject: [PATCH 13/15] enh: added clock config to example --- aidatlu/constellation/example_tlu_config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/aidatlu/constellation/example_tlu_config.toml b/aidatlu/constellation/example_tlu_config.toml index ecbf417..801612c 100644 --- a/aidatlu/constellation/example_tlu_config.toml +++ b/aidatlu/constellation/example_tlu_config.toml @@ -12,3 +12,4 @@ trigger_signal_delay = [0, 0, 0, 0, 0, 0] enable_clock_lemo_output = false pmt_power = [0.8, 0.8, 0.0, 0.0] +clock_config = false \ No newline at end of file From 66157bb5b1c847911c4f2451f027ed254b3de50e Mon Sep 17 00:00:00 2001 From: rpartzsch Date: Thu, 4 Dec 2025 09:59:19 +0100 Subject: [PATCH 14/15] fix: pre-commit white line --- aidatlu/constellation/example_tlu_config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aidatlu/constellation/example_tlu_config.toml b/aidatlu/constellation/example_tlu_config.toml index 801612c..d8ec986 100644 --- a/aidatlu/constellation/example_tlu_config.toml +++ b/aidatlu/constellation/example_tlu_config.toml @@ -12,4 +12,4 @@ trigger_signal_delay = [0, 0, 0, 0, 0, 0] enable_clock_lemo_output = false pmt_power = [0.8, 0.8, 0.0, 0.0] -clock_config = false \ No newline at end of file +clock_config = false From 61b7f3a69e57d02393d06d6edec0fff20f1608c7 Mon Sep 17 00:00:00 2001 From: Stephan Lachnit Date: Mon, 8 Dec 2025 15:29:57 +0100 Subject: [PATCH 15/15] FIX: frames_as_blocks now called write_as_blocks in EudaqNativeWriter BOR --- aidatlu/constellation/aidatlu_satellite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aidatlu/constellation/aidatlu_satellite.py b/aidatlu/constellation/aidatlu_satellite.py index 85bb5a0..0363f24 100644 --- a/aidatlu/constellation/aidatlu_satellite.py +++ b/aidatlu/constellation/aidatlu_satellite.py @@ -100,7 +100,7 @@ def _pull_fifo_event(self): # For EudaqNativeWriter compatibility self.bor["eudaq_event"] = "TluRawDataEvent" - self.bor["frames_as_blocks"] = True + self.bor["write_as_blocks"] = True return "Do starting complete"