Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions aidatlu/constellation/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
---
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"
category: "External"
parent_class: "TransmitterSatellite"
---

## Description
Expand All @@ -11,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]
Expand All @@ -27,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=<install_location>/lib
Expand All @@ -54,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).

Expand Down
4 changes: 2 additions & 2 deletions aidatlu/constellation/__main__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#!/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


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
Expand Down
113 changes: 85 additions & 28 deletions aidatlu/constellation/aidatlu_satellite.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
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
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


class AidaTLU(DataSender):
class AidaTLU(TransmitterSatellite):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -82,19 +86,26 @@ 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()
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["write_as_blocks"] = True

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
Expand All @@ -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:
Expand All @@ -123,10 +135,11 @@ 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, self.config_file, self.clock_file, i2c=self.i2c_method
)
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
logger._reset_all_loggers()
self.aidatlu.log = self.log
Expand All @@ -135,7 +148,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:
Expand All @@ -149,41 +162,85 @@ def _handle_event(self, evt: list) -> None:
}
# New data format: store 6 uint32 as bytes in little-endian
payload = np.array(evt, dtype="<u4").tobytes()
self.data_queue.put((payload, meta))
data_record = self.new_data_record(meta)
data_record.add_block(payload)
self.send_data_record(data_record)

def handle_status(self) -> 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(
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

Expand Down
1 change: 1 addition & 0 deletions aidatlu/constellation/example_tlu_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading