Source code for basil.HL.tdl_tdc

#
# ------------------------------------------------------------
# Copyright (c) All rights reserved
# SiLab, Institute of Physics, University of Bonn
# ------------------------------------------------------------
#

import numpy as np

from basil.HL.RegisterHardwareLayer import RegisterHardwareLayer


[docs] class tdl_tdc(RegisterHardwareLayer): """TDC controller interface""" GHZ_S_FREQ = 0.48 CLK_DIV = 3 word_type_codes = {0: "TRIGGERED", 1: "RISING", 2: "FALLING", 3: "TIMESTAMP", 4: "CALIB", 5: "MISS", 6: "RST"} _registers = { "RESET": {"descr": {"addr": 0, "size": 7, "offset": 1, "properties": ["writeonly"]}}, "VERSION": {"descr": {"addr": 0, "size": 8, "properties": ["ro"]}}, "ENABLE": {"descr": {"addr": 1, "size": 1, "offset": 0}}, "ENABLE_EXTERN": {"descr": {"addr": 1, "size": 1, "offset": 1}}, "EN_ARMING": {"descr": {"addr": 1, "size": 1, "offset": 2}}, "EN_WRITE_TIMESTAMP": {"descr": {"addr": 1, "size": 1, "offset": 3}}, "EN_TRIGGER_DIST": {"descr": {"addr": 1, "size": 1, "offset": 4}}, "EN_NO_WRITE_TRIG_ERR": {"descr": {"addr": 1, "size": 1, "offset": 5}}, "EN_INVERT_TDC": {"descr": {"addr": 1, "size": 1, "offset": 6}}, "EN_INVERT_TRIGGER": {"descr": {"addr": 1, "size": 1, "offset": 7}}, "EVENT_COUNTER": {"descr": {"addr": 2, "size": 32, "properties": ["ro"]}}, "LOST_DATA_COUNTER": {"descr": {"addr": 6, "size": 8, "properties": ["ro"]}}, "TDL_MISS_COUNTER": {"descr": {"addr": 7, "size": 8, "porperties": ["ro"]}}, "EN_CALIBRATION_MODE": {"descr": {"addr": 8, "size": 1, "offset": 0}}, } _require_version = "==2" calib_vector = np.ones(92) calib_sum = np.sum(calib_vector) def __init__(self, intf, conf): super(tdl_tdc, self).__init__(intf, conf) def get_tdc_value(self, word): # The last 7 bit are tdl data, the first 7 bits are word type and source, so 18 bits are counter information # Of these, the last two bist are timing wrt. the fast clock and the first 16 to rhe slow clock return self.CLK_DIV * ((word >> 9) & 0x0FFFF) + ((word >> 7) & 0x3) def get_word_type(self, word): return word >> (32 - 7) & 0b111 def is_calib_word(self, word): return self.get_word_type(word) == 4 def is_time_word(self, word): if isinstance(word, np.ndarray): return [(self.word_type_codes[t] in ["TRIGGERED", "RISING", "FALLING"]) for t in self.get_word_type(word)] else: return self.word_type_codes[self.get_word_type(word)] in ["TRIGGERED", "RISING", "FALLING"] def get_raw_tdl_values(self, word): return word & 0b1111111 def tdl_to_time(self, tdl_value): sample_proportion = self.calib_vector[tdl_value] return sample_proportion * 1 / self.GHZ_S_FREQ def set_calib_values(self, calib_values): # calib_values = np.append(calib_values) data_sort, value_counts = np.unique(calib_values % 128, return_counts=True) self.calib_vector = np.zeros(100) for i, zero in enumerate(self.calib_vector): bins_lt_i = data_sort <= i self.calib_vector[i] = np.sum(value_counts[bins_lt_i]) self.calib_sum = np.sum(value_counts) self.calib_vector = self.calib_vector / self.calib_sum def plot_calib_values(self, data): import matplotlib.pyplot as plt # This if is a safeguard: If data with a large range of values is given to the below # the code takes forever to return. if max(data) - min(data) < 1000: d = 1 left_of_first_bin = data.min() - float(d) / 2 right_of_last_bin = data.max() + float(d) / 2 _ = plt.hist(data, np.arange(left_of_first_bin, right_of_last_bin + d, d)) else: # data = np.random.choice(data,,replace=False) _ = plt.hist(data, 1000) plt.title("Histogram of TDL code density") plt.show() def tdc_word_to_time(self, word): if isinstance(word, dict): word = word["raw_word"] if isinstance(word, np.ndarray): if not all(self.is_time_word(word)): raise ValueError("can not convert tdc word of given types to time") else: if not self.is_time_word(word): word_type = self.word_type_codes[self.get_word_type(word)] raise ValueError("can not convert tdc word of type %s to time" % word_type) tdc_value = self.get_tdc_value(word) tdc_time = 1 / self.GHZ_S_FREQ * tdc_value return tdc_time - self.tdl_to_time(self.get_raw_tdl_values(word)) def disassemble_tdc_word(self, word): # Shift away the 32 - 7 data bits and grab 3 bit word type word_type = self.word_type_codes[self.get_word_type(word)] if word_type in ["CALIB", "TRIGGERED", "RISING", "FALLING"]: return { "source_id": (word >> (32 - 4)), "word_type": word_type, "tdl_value": word & 0b1111111, "fine_clk_value": self.get_tdc_value(word), "raw_word": word, } elif word_type in ["TIMESTAMP", "RST"]: return { "source_id": (word >> (32 - 4)), "word_type": word_type, "timestamp": (word >> 9) & 0xFFFF, "raw_word": word, } else: return {"source_id": (word >> (32 - 4)), "word_type": word_type, "raw_word": word} def reset(self): self.RESET = 0 def get_lost_data_counter(self): return self.LOST_DATA_COUNTER def missed_data_counter(self): return self.TDL_MISS_COUNTER def set_en(self, value): self.ENABLE = value def get_en(self): return self.ENABLE def set_en_extern(self, value): self.ENABLE_EXTERN = value def get_en_extern(self): return self.ENABLE_EXTERN def set_arming(self, value): self.EN_ARMING = value def get_arming(self): return self.EN_ARMING def set_write_timestamp(self, value): self.EN_WRITE_TIMESTAMP = value def get_write_timestamp(self): return self.EN_WRITE_TIMESTAMP def get_event_counter(self): return self.EVENT_COUNTER