diff --git a/.gitignore b/.gitignore index 9d08039e..c69c870b 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,8 @@ examples/mmc3_eth/firmware/src/SiTCP/* examples/mio3_eth_gpac/firmware/vivado/* !examples/mio3_eth_gpac/firmware/vivado/*.xpr examples/mio3_eth_gpac/firmware/src/SiTCP/* + +examples/tdc_bdaq/firmware/vivado/* +!examples/tdc_bdaq/firmware/vivado/*.sh +!examples/tdc_bdaq/firmware/vivado/run.tcl +examples/tdc_bdaq/firmware/src/SiTCP/* diff --git a/basil/HL/tdl_tdc.py b/basil/HL/tdl_tdc.py new file mode 100644 index 00000000..ed2443ce --- /dev/null +++ b/basil/HL/tdl_tdc.py @@ -0,0 +1,165 @@ +# +# ------------------------------------------------------------ +# Copyright (c) All rights reserved +# SiLab, Institute of Physics, University of Bonn +# ------------------------------------------------------------ +# + +from basil.HL.RegisterHardwareLayer import RegisterHardwareLayer +import numpy as np + + +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 diff --git a/basil/TL/SiSim.py b/basil/TL/SiSim.py index 467a9df6..0964115a 100644 --- a/basil/TL/SiSim.py +++ b/basil/TL/SiSim.py @@ -40,7 +40,7 @@ def init(self): port = self._init['port'] # try few times for simulator to setup - try_cnt = 60 + try_cnt = 120 if 'timeout' in self._init.keys(): try_cnt = self._init['timeout'] diff --git a/basil/firmware/modules/tdl_tdc/Readme.rst b/basil/firmware/modules/tdl_tdc/Readme.rst new file mode 100644 index 00000000..e80af62c --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/Readme.rst @@ -0,0 +1,216 @@ +========================================================================== +**Tdl Tdc** - Tapped Delay Line based Time to Digital Converter +========================================================================== + +---------------- +Required modules +---------------- + +* `utils/3_stage_synchronizer.v` +* `utils/flag_domain_crossing.v` +* `utils/generic_fifo.v` +* `utils/cdc_syncfifo.v` +* `utils/clock_divider.v` + +---------------- +Key TDC figures +---------------- + +* 30ps RMS accuracy +* 40ns shortest reliably detectable pulse length +* 400us dynamic range + + +---------------- +Usage +---------------- +Before every experiment, the Tdc should be calibrated, ideally after it had time running to warm up. This can be done as follows:: + + chip['TDL_TDC'].EN_CALIBRATION_MOD = 1 + time.sleep(1) + chip['TDL_TDC'].EN_CALIBRATION_MOD = 0 + + + + +This will cause the Tdc to write many ``CALIB`` words to the fifo. Subsequently any required configuration bits for the particular experiment may be set. During analysis the recorded +calibration stream is easily incorporated:: + + calib_data_indices = chip['TDL_TDC'].is_calib_word(collected_data) + + if any(calib_data_indices) : + calib_values = chip['TDL_TDC'].get_raw_tdl_values(np.array(collected_data[calib_data_indices])) + chip['TDL_TDC'].set_calib_values(calib_values) + logging.info("Calibration set using %s samples" % len(calib_values)) + +Optionally, you can view a histogram of the calibration using ``chip['TDL_TDC'].plot_calib_values(calib_values)``. + + If measurements are made over a longer time span, recalibration might be necessary, however note that the above example lumps all the calibration(s) in ``collected_data`` together. + +The calibrated basil module can then be used the following way:: + + time_word_indices = chip['TDL_TDC'].is_time_word(collected_data) + time_data = collected_data[time_word_indices] + if any(calib_data_indices) : + time_in_ns = chip['TDL_TDC'].tdc_word_to_time(time_data[0]) + + + +---------------- +Test module +---------------- + +Under ``examples/tdc_bdaq`` you can find an example implementation of the module along side a sequence generator to generate test signals. The TDC uses the external Si570 oscillator while the signal generator uses the external Si550. The script generates pulses of varying trigger distance and creates a plot of the difference of measured and expected time values, alongside errorbars of the standard deviation. + +**Unit test/Example:** +`test_SimTdl_Tlu.v `_ +`test_SimTdl_Tlu.py `_ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +------------------------------+---------------------+---------------------------------------------------------------------------------------------------------------------+ + | Name | Default | Description | + +==============================+=====================+=====================================================================================================================+ + | DATA_IDENTIFIER | 4'b0100 | 4-bit data identifier for TDC words. | + +------------------------------+---------------------+---------------------------------------------------------------------------------------------------------------------+ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Pins +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | Name | Size | Direction | Description | + +==========================+=====================+=======================+=================================================================================================+ + | CLK480 | 1 | input | 480 MHz clock | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | CLK160 | 1 | input | 160 MHz clock | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | CALIB_CLK | 1 | input | Any clock that is uncorrelated. For example the ethernet clock ``CLK125RX``. | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | tdc_in | 1 | input | input pulse which is digitized | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | trig_in | 1 | input | input trigger signal (distance between TRIG_IN and TDC_IN can be measured) | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | arm_tdc | 1 | input | enable TDC for single measurement | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | ext_en | 1 | input | enable TDC for a fixed time period | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + | timestamp | 16 | input | timestamp counter from other modules (e.g. tlu module) | + +--------------------------+---------------------+-----------------------+-------------------------------------------------------------------------------------------------+ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Registers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | Name | Address | Bits | r/w | Default | Description | + +========================================+==================================+========+=======+=============+===============================================================+ + | RESET | 0 | | wo | | reset | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | VERSION | 0 | [7:0] | ro | 3 | version | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | ENABLE | 1 | [0] | r/w | 0 | enable TDC module (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | ENABLE_EXTERN | 1 | [1] | r/w | 0 | external enable (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EN_ARMING | 1 | [2] | r/w | 0 | enable arming (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EN_WRITE_TIMESTAMP | 1 | [3] | r/w | 0 | write timestamp to output data (see data format description ) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EN_TRIGGER_DIST | 1 | [4] | r/w | 0 | write trigger distance to output data | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EN_NO_WRITE_TRIG_ERR | 1 | [5] | r/w | 0 | NOT IMPLEMENTED | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EN_INVERT_TDC | 1 | [6] | r/w | 0 | invert TDC input (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EN_INVERT_TRIGGER | 1 | [7] | r/w | 0 | invert trigger input (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EVENT_COUNTER | 2 | [31:0] | ro | | event counter value (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | LOST_DATA_COUNTER | 6 | [7:0] | ro | | lost data counter (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | TDL_MISS_COUNTER | 7 | [7:0] | ro | | signal transistions not registered by delay line (*) | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + | EN_CALIBRATION_MODE | 8 | [0] | ro | | sample and write calibration data | + +----------------------------------------+----------------------------------+--------+-------+-------------+---------------------------------------------------------------+ + + +---------------- +Data Format +---------------- +The Tdc module uses a state machine to send various types of 32 bit data words, however the first seven bits always follow the same structure: + ++-------------------------+-------------------+---------------------------------------------------------+ +| DATA IDENTIFIER (4 bit) | WORD TYPE (3 bit) | Data (25 bits) | ++-------------------------+-------------------+---------------------------------------------------------+ + +The three bit codes for the individual word types can be found in `word_broker.v `_, but the basil driver can decode these without manual effort. The words containing the tdc information are in the following order:: + + [TRIGGERED] -> RISING -> FALLING -> [TIMESTAMP] + +``TRIGGERED`` is only sent if ``EN_TRIGGER_DIST``, and ``TIMESTAMP`` only if ``EN_WRITE_TIMESTAMP`` is set. Note however, that this sequence can be interrupted and appear incomplete, for example if the module is reset during a measurement. + + +In the following, we list how the remaining 25 bits are allocated for the various words. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +TRIGGERED, RISING, FALLING +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + + ++---------------------------------------------+---------------------------+-----------------------------+ +| 160 Mhz Counter (16 bits) | 480 Mhz Counter (2 bits) | Delay Line (7 bits) | ++---------------------------------------------+---------------------------+-----------------------------+ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +TIMESTAMP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +This word comes after the ``FALLING`` word, but the Timestamp is actually sampled two 160Mhz clock cycles after a measurement has been started. + + ++-------------------------------------------------------------------+-----------------------------------+ +| Timestamp (16 bits) | 0 (9 bits) | ++-------------------------------------------------------------------+-----------------------------------+ + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +CALIB +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If the Tdc is set for self-calibration using ``EN_CALIBRATION_MODE``, it will repeatedly send this word. + + + ++---------------------------------------------+---------------------------+-----------------------------+ +| 0 (16 bits) | 480 Mhz Counter (2 bits) | Delay Line (7 bits) | ++---------------------------------------------+---------------------------+-----------------------------+ + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +RESET +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If a reset is issued to the Tdc, either as a global bus reset or through basil, this word is sent. It might be useful for resetting a state machine +decoding the words on the receiving end. The Timestamp included is sampled as soon as +the reset signal has passed the clock domain crossing circuitry. + ++-------------------------------------------------------------------+-----------------------------------+ +| Timestamp (16 bits) | 0 (9 bits) | ++-------------------------------------------------------------------+-----------------------------------+ + +---------------- +Architecture +---------------- + +The TDC is built around the delay line found in the `Link jTDC `_ which uses about 200 delay elements of which only every second is sampled. This is done because the bin size variance is very large so that if every tap is used, hardly more precision will be obtained. However, this relatively long delay line can be sampled with a lower rate without missing signals. Implementations using every tap usually use several delay lines of about 100 elements which are then sampled using different clock phases. One such implementation can be found in the `Link master thesis `_ of Benjamin Blase, which will also give interesting insights to TDC architecture considerations. The priority encoder of this TDC, i.e. the module converting the delay line thermometer code to binary, is based on his work. + +In order to sample the delay elements at a high enough rate while still being able to process them, we use a 3x multisampling approach: There is a shift register shifting in the entire information of the delay line using the 3x clock and at every slow clock tick the contents of the shift register are copied for further processing in the slow clock domain. Only then do we detect signal transitions and convert the thermometer code to binary. + +.. image:: delayline.png + +The most distinct design choice in this implementation is that it uses only a single delay line for measuring rising and falling edges of two inputs. As rising and falling edges propagate delay elements differently it makes sense to treat with distinct calibrations or even separate delay lines. To circumvent this additional space requirement and complexity, we use a multiplexer co-ordinating which input, in which polarity, gets seen by the delay elements. This induces a lower bound on width of pulses we can measure as this input multiplexer needs some time to switch between signals. To drive the multiplexer we use a simple state machine, which also drives the word output generation in the ``word_broker`` module. + +.. image:: architecture.png + + diff --git a/basil/firmware/modules/tdl_tdc/architecture.png b/basil/firmware/modules/tdl_tdc/architecture.png new file mode 100644 index 00000000..2ebaff84 Binary files /dev/null and b/basil/firmware/modules/tdl_tdc/architecture.png differ diff --git a/basil/firmware/modules/tdl_tdc/controller.v b/basil/firmware/modules/tdl_tdc/controller.v new file mode 100644 index 00000000..0158b948 --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/controller.v @@ -0,0 +1,230 @@ +// Main state machine of the tdc. Contains the logic for switching the +// multiplexer, controling the corse counter, arming, calibration +// states and the trigger distance mode. Furthermore counts successful events +// and tdl misses. +module controller #( + parameter state_bits = 4, + parameter mux_bits =2 +)( + input wire CLK, + input wire rst, + input wire [1:0] hit_status, + input wire tdl_status, + input wire arm_flag, + input wire en, + input wire en_arm_mode, + input wire en_calib_mode, + input wire en_write_trigger_distance, + + output reg counter_count, + output reg counter_reset, + output wire [state_bits-1:0] tdc_state, + output reg [7:0] miss_cnt, + output reg [31:0] event_cnt, + output reg [mux_bits-1:0] mux_addr +); + + +// 2 bit flag to store hit and miss information of the group of +// samples. This needs to be in sync with the sample deser +localparam TDL_IDLE = 0; +localparam TDL_HIT = 1; +localparam TDL_MISSED = 2; + +// Input Mux addresses +// These need to be in sync with the tdc core. +localparam TRIG_IN = 0; +localparam SIG_IN = 1; +localparam SIG_IN_B = 2; +localparam CALIB_OSC = 3; + +// function [state_bits-1:0] int_to_gray; +// input [state_bits-1:0] int; +// begin +// int_to_gray = int ^ (int >> 1); +// end +// endfunction +// TDC states +// These need to be in sync with the word broker. +localparam [state_bits-1:0] IDLE = 0; +localparam [state_bits-1:0] IDLE_TRIG = 1; +localparam [state_bits-1:0] TRIGGERED = 2; +localparam [state_bits-1:0] RIS_EDGE = 3; +localparam [state_bits-1:0] FAL_EDGE = 4; +localparam [state_bits-1:0] MISSED = 6; +localparam [state_bits-1:0] CALIB = 7; +localparam [state_bits-1:0] CALIB_HIT = 8; +localparam [state_bits-1:0] RESET = 9; + + +reg [state_bits-1:0] state = 0; +reg [state_bits-1:0] previous_state = 0; +always @(posedge CLK) begin + previous_state <= state; +end + +assign tdc_state = state; +wire hit; +// Here we safeguard against really short (narrow) pulses triggering the +// sampling. Only if a hit was detected and on the next cycle the input is +// still a solid 1 do we count a hit. +assign hit = (hit_status == TDL_HIT) && tdl_status; + + + + +always @(state, en_write_trigger_distance) begin + // State dependent control outputs + case(state) + RESET: begin + mux_addr <= TRIG_IN; + counter_reset <= 1; + counter_count <= 0; + end + IDLE: begin + mux_addr <= SIG_IN; + counter_reset <= 1; + counter_count <= 0; + end + IDLE_TRIG: begin + mux_addr <= TRIG_IN; + counter_reset <= 1; + counter_count <= 0; + end + TRIGGERED: begin + mux_addr <= SIG_IN; + counter_reset <= 0; + counter_count <= 1; + end + RIS_EDGE: begin + mux_addr <= SIG_IN_B; + counter_reset <= 0; + counter_count <= 1; + end + FAL_EDGE: begin + if (en_write_trigger_distance) + mux_addr <= TRIG_IN; + else + mux_addr <= SIG_IN; + counter_reset <= 1; + counter_count <= 0; + end + CALIB: begin + mux_addr <= CALIB_OSC; + counter_reset <= 1; + counter_count <= 0; + end + CALIB_HIT: begin + mux_addr <= CALIB_OSC; + counter_reset <= 1; + counter_count <= 0; + end + MISSED: begin + counter_reset <= 1; + counter_count <= 0; + mux_addr <= TRIG_IN; + end + default: begin + mux_addr <= TRIG_IN; + counter_reset <= 1; + counter_count <= 0; + end + endcase +end + +always @(posedge CLK) begin + // State dependent counting of events + case(state) + RESET: begin + event_cnt <= 0; + miss_cnt <= 0; + end + IDLE: begin + if (previous_state == FAL_EDGE) + event_cnt <= event_cnt + 1; // This is really a multicycle path: Only every 4 cycles can this occur. + end + IDLE_TRIG: begin + if (previous_state == FAL_EDGE) + event_cnt <= event_cnt + 1; // This is really a multicycle path: Only every 4 cycles can this occur. + end + MISSED: + miss_cnt <= miss_cnt + 1; + endcase +end + +wire armed; +reg arm_flag_latch; +assign armed = en_arm_mode?arm_flag_latch:1; + +// State transitions +always @(posedge CLK) begin + arm_flag_latch <= arm_flag | arm_flag_latch; + // Interrupt-style state transitions + if (rst) begin + state <= RESET; + arm_flag_latch <= 0; + end else if (~en) begin + state <= IDLE; + end else if (hit_status == TDL_MISSED) begin + state <= MISSED; + end else begin + // Regular state-dependent transition + case(state) + IDLE: begin + if (en_calib_mode) begin + state <= CALIB; + end else if (en_write_trigger_distance) begin + state <= IDLE_TRIG; + end else if (armed) begin + if (hit) + state <= RIS_EDGE; + end else begin + state <= state; + end + end + IDLE_TRIG: begin + if (en_calib_mode) begin + state <= CALIB; + end else if (~en_write_trigger_distance) begin + state <= IDLE; + end else if (armed) begin + if (hit) + state <= TRIGGERED; + end else begin + state <= state; + end + end + TRIGGERED: begin + if (hit) + state <= RIS_EDGE; + else + state <= state; + end + RIS_EDGE: begin + if (hit) + state <= FAL_EDGE; + else + state <= state; + end + FAL_EDGE: begin + arm_flag_latch <= 0; + if (en_write_trigger_distance) + state <= IDLE_TRIG; + else + state <= IDLE; + end + CALIB: begin + if (~en_calib_mode) + state <= IDLE_TRIG; + else if (hit) + state <= CALIB_HIT; + else + state <= CALIB; + end + CALIB_HIT: state <= CALIB; + MISSED: state <= IDLE_TRIG; + RESET: state <= IDLE; + endcase + end +end +endmodule diff --git a/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.ucf b/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.ucf new file mode 100644 index 00000000..638d347a --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.ucf @@ -0,0 +1,29 @@ +##--------------------------------------------------------------------- +##-- -- +##-- Company: University of Bonn -- +##-- Engineer: John Bieling -- +##-- -- +##--------------------------------------------------------------------- +##-- -- +##-- Copyright (C) 2015 John Bieling -- +##-- -- +##-- This program is free software; you can redistribute it and/or -- +##-- modify it under the terms of the GNU General Public License as -- +##-- published by the Free Software Foundation; either version 3 of -- +##-- the License, or (at your option) any later version. -- +##-- -- +##-- This program is distributed in the hope that it will be useful, -- +##-- but WITHOUT ANY WARRANTY; without even the implied warranty of -- +##-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- +##-- GNU General Public License for more details. -- +##-- -- +##-- You should have received a copy of the GNU General Public -- +##-- License along with this program; if not, see -- +##-- . -- +##-- -- +##--------------------------------------------------------------------- + +TIMEGRP "SFCounter" = ffs("*/SFC_*"); +TIMESPEC TS_SlimFastCounter = FROM "SFCounter" TO "SFCounter" 40 ns; +#set_multicycle_path -setup 15 -from [get_cells -hier *SFC_*] -to [get_cells -hier *SFC_* ] +#set_multicycle_path -hold 2 -from [get_cells -hier *SFC_*] -to [get_cells -hier *SFC_* ] diff --git a/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.v b/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.v new file mode 100644 index 00000000..e97d7719 --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.v @@ -0,0 +1,169 @@ +//`include "tdl_tdc/counter/slimfast_multioption_counter.xdc" +//`include "tdl_tdc/counter/signal_clipper.vhdl" +`include "utils/pulse_gen_rising.v" + +//`default_nettype none +//--------------------------------------------------------------------- +//-- -- +//-- Company: University of Bonn -- +//-- Engineer: John Bieling -- +//-- -- +//--------------------------------------------------------------------- +//-- -- +//-- Copyright (C) 2015 John Bieling -- +//-- -- +//-- This program is free software; you can redistribute it and/or -- +//-- modify it under the terms of the GNU General Public License as -- +//-- published by the Free Software Foundation; either version 3 of -- +//-- the License, or (at your option) any later version. -- +//-- -- +//-- This program is distributed in the hope that it will be useful, -- +//-- but WITHOUT ANY WARRANTY; without even the implied warranty of -- +//-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- +//-- GNU General Public License for more details. -- +//-- -- +//-- You should have received a copy of the GNU General Public -- +//-- License along with this program; if not, see -- +//-- . -- +//-- -- +//--------------------------------------------------------------------- + + +//-- The module can be configured with these parameters (defaults given in braces): +//-- +//-- outputwidth(32) : width of output register +//-- size(31) : Size of counter, set from 5 to outputwidth-1. (overflow bit is extra, so max (outputwidth-1) Bit) +//-- clip_count(1) : sets if the count signal is to be clipped +//-- clip_reset(1 : sets if the reset signal is to be clipped +//-- +//-- !!! IMPORTANT !!! Include slimfast_multioption_counter.ucf + + +module slimfast_multioption_counter #( + parameter clip_count = 1, + parameter clip_reset = 1, + parameter size = 31, + parameter outputwidth = 32 +)( + input wire countClock, + input wire count, + input wire reset, + output wire [outputwidth-1:0] countout); + + + + +wire [size-3:0] highbits_this; +wire [size-3:0] highbits_next; + +//-- Counter +slimfast_multioption_counter_core #(.clip_count(clip_count),.clip_reset(clip_reset),.size(size),.outputwidth(outputwidth)) counter( + .countClock(countClock), + .count(count), + .reset(reset), + .highbits_this(highbits_this), + .highbits_next(highbits_next), + .countout(countout) +); + +//-- pure combinatorial +1 operation (multi cycle path, this may take up to 40ns without breaking the counter) +assign highbits_next = highbits_this + 1; + +endmodule + + +module slimfast_multioption_counter_core (countClock, + count, + reset, + highbits_this, + highbits_next, + countout); + +parameter clip_count = 1; +parameter clip_reset = 1; +parameter size = 31; +parameter outputwidth = 32; + +input wire countClock; +input wire count; +input wire reset; + +input wire [size-3:0] highbits_next; +output wire [size-3:0] highbits_this; + +output wire [outputwidth-1:0] countout; + + +wire final_count; +wire final_reset; + +reg [2:0] fast_counts = 3'b0; +(* KEEP = "true" *) reg [size-3:0] SFC_slow_counts = 'b0; //SFC_ prefix to make this name unique +wire [size-3:0] slow_counts_next; + + + +//-- if an if-statement compares a value to 1 (not 1'b1), it is a generate-if +generate + + //-- this is pure combinatorial + //-- after change of SFC_slow_counts, the update of slow_counts_next is allowed to take + //-- 16clk cycles of countClock + assign highbits_this = SFC_slow_counts; + assign slow_counts_next[size-4:0] = highbits_next[size-4:0]; + + //the overflow bit is counted like all the other bits, but it cannot fall back to zero + assign slow_counts_next[size-3] = highbits_next[size-3] || highbits_this[size-3]; + + if (clip_count == 0) assign final_count = count; else + if (clip_count == 1) + begin + wire clipped_count; + pulse_gen_rising countclip ( .in(count), .clk_in(countClock), .out(clipped_count)); + assign final_count = clipped_count; + end else begin // I added this, so that one could switch from "clipped" to "not clipped" without changing the number of flip flop stages + reg piped_count; + always@(posedge countClock) + begin + piped_count <= count; + end + assign final_count = piped_count; + end + + if (clip_reset == 0) assign final_reset = reset; else + begin + wire clipped_reset; + pulse_gen_rising resetclip ( .in(reset), .clk_in(countClock), .out(clipped_reset)); + assign final_reset = clipped_reset; + end + + + always@(posedge countClock) + begin + + if (final_reset == 1'b1) + begin + + fast_counts <= 0; + SFC_slow_counts <= 0; + + end else begin + + //-- uses overflow as CE, valid only one clock cycle + if (final_count == 1'b1 && fast_counts == 3'b111) begin + SFC_slow_counts <= slow_counts_next; + end + + //-- uses final_count as CE + if (final_count == 1'b1) fast_counts <= fast_counts + 1'b1; + + end + + end + +endgenerate + +assign countout[outputwidth-1] = SFC_slow_counts[size-3]; +assign countout[outputwidth-2:0] = {SFC_slow_counts[size-4:0],fast_counts}; + +endmodule diff --git a/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.xdc b/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.xdc new file mode 100644 index 00000000..2423e633 --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/counter/slimfast_multioption_counter.xdc @@ -0,0 +1,31 @@ +##--------------------------------------------------------------------- +##-- -- +##-- Company: University of Bonn -- +##-- Engineer: John Bieling -- +##-- -- +##--------------------------------------------------------------------- +##-- -- +##-- Copyright (C) 2015 John Bieling -- +##-- -- +##-- This program is free software; you can redistribute it and/or -- +##-- modify it under the terms of the GNU General Public License as -- +##-- published by the Free Software Foundation; either version 3 of -- +##-- the License, or (at your option) any later version. -- +##-- -- +##-- This program is distributed in the hope that it will be useful, -- +##-- but WITHOUT ANY WARRANTY; without even the implied warranty of -- +##-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- +##-- GNU General Public License for more details. -- +##-- -- +##-- You should have received a copy of the GNU General Public -- +##-- License along with this program; if not, see -- +##-- . -- +##-- -- +##--------------------------------------------------------------------- + +#TIMEGRP "SFCounter" = ffs("*/SFC_*"); +#TIMESPEC TS_SlimFastCounter = FROM "SFCounter" TO "SFCounter" 40 ns; +# We should have up to 8 cycles to do this computation, as the fast count has 3 bits +set_multicycle_path -setup -from [get_pins -hier *SFC_*/C] -to [get_pins -hier *SFC_*/D] 8; +set_multicycle_path -hold -end -from [get_pins -hier *SFC_*/C] -to [get_pins -hier *SFC_*/D] 7; + diff --git a/basil/firmware/modules/tdl_tdc/delayline.png b/basil/firmware/modules/tdl_tdc/delayline.png new file mode 100644 index 00000000..a8166b63 Binary files /dev/null and b/basil/firmware/modules/tdl_tdc/delayline.png differ diff --git a/basil/firmware/modules/tdl_tdc/delayline/carrysampler_spartan6_20ps.v b/basil/firmware/modules/tdl_tdc/delayline/carrysampler_spartan6_20ps.v new file mode 100644 index 00000000..192bd17a --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/delayline/carrysampler_spartan6_20ps.v @@ -0,0 +1,112 @@ + +//--------------------------------------------------------------------- +//-- -- +//-- Company: University of Bonn -- +//-- Engineer: John Bieling -- +//-- -- +//--------------------------------------------------------------------- +//-- -- +//-- Copyright (C) 2015 John Bieling -- +//-- -- +//-- This program is free software; you can redistribute it and/or -- +//-- modify it under the terms of the GNU General Public License as -- +//-- published by the Free Software Foundation; either version 3 of -- +//-- the License, or (at your option) any later version. -- +//-- -- +//-- This program is distributed in the hope that it will be useful, -- +//-- but WITHOUT ANY WARRANTY; without even the implied warranty of -- +//-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- +//-- GNU General Public License for more details. -- +//-- -- +//-- You should have received a copy of the GNU General Public -- +//-- License along with this program; if not, see -- +//-- . -- +//-- -- +//--------------------------------------------------------------------- + +module CHAIN_CELL (CINIT, CI, CO, DO, CLK); + + output wire [3:0] DO; + output wire CO; + input wire CI; + input wire CLK; + input wire CINIT; + + wire [3:0] carry_out; + + CARRY4 CARRY4_inst ( + .CO(carry_out), // 4-bit carry out + .O(), // 4-bit carry chain XOR data out + .CI(CI), // 1-bit carry cascade input + .CYINIT(CINIT), // 1-bit carry initialization + .DI(4'b0000), // 4-bit carry-MUX data in + .S(4'b1111) // 4-bit carry-MUX select input + ); + assign CO = carry_out[3]; + + (* BEL = "FFD" *) FDCE #(.INIT(1'b0)) TDL_FF_D (.D(carry_out[3]), .Q(DO[3]), .C(CLK), .CE(1'b1), .CLR(1'b0)); + (* BEL = "FFC" *) FDCE #(.INIT(1'b0)) TDL_FF_C (.D(carry_out[2]), .Q(DO[2]), .C(CLK), .CE(1'b1), .CLR(1'b0)); + (* BEL = "FFB" *) FDCE #(.INIT(1'b0)) TDL_FF_B (.D(carry_out[1]), .Q(DO[1]), .C(CLK), .CE(1'b1), .CLR(1'b0)); + (* BEL = "FFA" *) FDCE #(.INIT(1'b0)) TDL_FF_A (.D(carry_out[0]), .Q(DO[0]), .C(CLK), .CE(1'b1), .CLR(1'b0)); + +endmodule + + + +module carry_sampler_spartan6 (d, q, CLK); + + parameter bits = 74; + parameter resolution = 1; + + input wire d; + input wire CLK; + output wire [bits-1:0] q; + +`ifndef SIMULATION + wire [(bits*resolution/4)-1:0] connect; + wire [bits*resolution-1:0] register_out; + + genvar i,j; + generate + + CHAIN_CELL FirstCell( + .DO({register_out[2],register_out[3],register_out[0],register_out[1]}), + .CINIT(d), + .CI(1'b0), + .CO(connect[0]), + .CLK(CLK) + ); + + for (i=1; i < bits*resolution/4; i=i+1) begin : carry_chain + CHAIN_CELL MoreCells( + .DO({register_out[4*i+2],register_out[4*i+3],register_out[4*i+0],register_out[4*i+1]}), //swapped to avoid empty bins + .CINIT(1'b0), + .CI(connect[i-1]), + .CO(connect[i]), + .CLK(CLK) + ); + end + + for (j=0; j < bits; j=j+1) begin : carry_sampler + assign q[j] = register_out[j*resolution]; + end + + endgenerate +`else + reg sim_buffer; + reg [bits-1:0] sim_out; + always @(posedge CLK) begin + sim_buffer <= d; + if (d & ~ sim_buffer) + sim_out <= 3'b001; + else if (~d & sim_buffer) + sim_out <= (1 << bits-1); + else if (d & sim_buffer) + sim_out <= {bits{1'b1}}; + else if (~d & ~sim_buffer) + sim_out <= 0; + end + assign q = sim_out; +`endif + +endmodule diff --git a/basil/firmware/modules/tdl_tdc/delayline/sample_deser.v b/basil/firmware/modules/tdl_tdc/delayline/sample_deser.v new file mode 100644 index 00000000..1392f696 --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/delayline/sample_deser.v @@ -0,0 +1,115 @@ +module sample_deser #( +parameter dlyline_bits = 96, +parameter internally_rising = 1'b1, +parameter fine_time_bits = 2, +parameter clk_ratio = 3 // This parameter almost works, only the mux address generation below needs to be set manually +)( + input wire CLK_FAST, + input wire CLK_SLOW, + input wire [dlyline_bits-1:0] sample_in, + + output reg [1:0 ] hit_status, + output reg [fine_time_bits-1:0] fine_time, + output reg [dlyline_bits-1:0] selected_sample +); +// We will use a 2 bit flag to store hit and miss information of the group of +// samples. This needs to be in sync with the tdc controller. +localparam IDLE = 0; +localparam HIT = 1; +localparam MISSED = 2; + + + +// shift register of delay line samples on the fast clock +integer i; +reg [dlyline_bits-1:0] samples [clk_ratio-1:0]; +always @(posedge CLK_FAST) begin + samples[0] <= sample_in; + for(i=1; i >1) ^ data; + +reg [DATA_WIDTH-1:0] gray_cdc0, gray_cdc1, data_bus_clk; +always @(posedge OUT_CLK) begin + gray_cdc0 <= gray; + gray_cdc1 <= gray_cdc0; +end + +integer i; +always @(*) begin + data_bus_clk[DATA_WIDTH-1] = gray_cdc1[DATA_WIDTH-1]; + for(i = DATA_WIDTH-2; i >= 0; i = i - 1) begin + data_bus_clk[i] = gray_cdc1[i] ^ data_bus_clk[i + 1]; + end +end + +assign data_out_clk = data_bus_clk; + +endmodule diff --git a/basil/firmware/modules/tdl_tdc/word_broker.v b/basil/firmware/modules/tdl_tdc/word_broker.v new file mode 100644 index 00000000..2a635101 --- /dev/null +++ b/basil/firmware/modules/tdl_tdc/word_broker.v @@ -0,0 +1,129 @@ +module word_broker #( + parameter state_bits = 4, + parameter counter_bits = 10, + parameter encodebits = 7, + parameter fine_time_bits = 2 +)( + input wire CLK, + input wire [counter_bits-1:0] corse_count, + input wire [fine_time_bits-1:0] fine_time, + input wire [encodebits-1:0] tdl_time, + input wire [state_bits-1:0] tdc_state, + input wire en_write_timestamp, + input wire en_no_trig_err, // This is not implemented as there are no trig errors in this design. The tdl expects bubble errors. + input wire [15:0] signal_timestamp, + input wire [15:0] reset_timestamp, + + output reg out_valid, + output reg [32-1:0] out_word +); + + + + +localparam DATA_IDENTIFIER = 4'b0100; +localparam word_type_bits = 3; + +// Word type codes +localparam [word_type_bits-1:0] TRIGGERED_WORD = 0; +localparam [word_type_bits-1:0] RISING_WORD = 1; +localparam [word_type_bits-1:0] FALLING_WORD = 2; +localparam [word_type_bits-1:0] TIMESTAMP_WORD = 3; +localparam [word_type_bits-1:0] CALIB_WORD = 4; +localparam [word_type_bits-1:0] MISS_WORD = 5; +localparam [word_type_bits-1:0] RESET_WORD = 6; + +// function [state_bits-1:0] int_to_gray; +// input [state_bits-1:0] int; +// begin +// int_to_gray = int ^ (int >> 1); +// end +// endfunction +// TDC states +localparam [state_bits-1:0] IDLE = 0; +localparam [state_bits-1:0] IDLE_TRIG = 1; +localparam [state_bits-1:0] TRIGGERED = 2; +localparam [state_bits-1:0] RIS_EDGE = 3; +localparam [state_bits-1:0] FAL_EDGE = 4; +localparam [state_bits-1:0] FIFO_FULL = 5; +localparam [state_bits-1:0] MISSED = 6; +localparam [state_bits-1:0] CALIB = 7; +localparam [state_bits-1:0] CALIB_HIT = 8; +localparam [state_bits-1:0] RESET = 9; + + +wire counter_overflow; +assign counter_overflow = corse_count[counter_bits-1]; +wire [15:0] corse_time; +assign corse_time = corse_count[counter_bits-1:0]; + + +reg [state_bits-1:0] previous_state; +always @(posedge CLK) begin + previous_state <= tdc_state; +end + +always @(posedge CLK) begin + case({previous_state, tdc_state}) + {IDLE_TRIG, TRIGGERED}: begin + out_valid <= 1; + out_word <= {DATA_IDENTIFIER, TRIGGERED_WORD, corse_time, fine_time, tdl_time}; + end + {IDLE, RIS_EDGE}: begin + out_valid <= 1; + out_word <= {DATA_IDENTIFIER, RISING_WORD, corse_time, fine_time, tdl_time}; + end + {TRIGGERED,RIS_EDGE}: begin + out_valid <= 1; + if (~counter_overflow) + out_word <= {DATA_IDENTIFIER, RISING_WORD, corse_time, fine_time, tdl_time}; + else + out_word <= {DATA_IDENTIFIER, RISING_WORD, {(counter_bits-1){1'b1}}, {fine_time_bits{1'b1}}, tdl_time}; + end + {RIS_EDGE,FAL_EDGE}: begin + out_valid <= 1; + if (~counter_overflow) + out_word <= {DATA_IDENTIFIER, FALLING_WORD, corse_time, fine_time, tdl_time}; + else + out_word <= {DATA_IDENTIFIER, FALLING_WORD, {(counter_bits-1){1'b1}}, {fine_time_bits{1'b1}}, tdl_time}; + end + {FAL_EDGE,IDLE}: begin + if(en_write_timestamp) begin + out_valid <= 1; + out_word <= {DATA_IDENTIFIER, TIMESTAMP_WORD, signal_timestamp, 9'b0}; + end + else begin + out_valid <= 0; + out_word <= 0; + end + end + {FAL_EDGE,IDLE_TRIG}: begin + if(en_write_timestamp) begin + out_valid <= 1; + out_word <= {DATA_IDENTIFIER, TIMESTAMP_WORD, signal_timestamp, 9'b0}; + end + else begin + out_valid <= 0; + out_word <= 0; + end + end + {MISSED, IDLE}: begin + out_valid <= 1; + out_word <= {DATA_IDENTIFIER, MISS_WORD, 25'b0}; + end + {RESET, IDLE}: begin + out_valid <= 1; + out_word <= {DATA_IDENTIFIER, RESET_WORD, reset_timestamp, 9'b0}; + end + {CALIB, CALIB_HIT}: begin + out_valid <= 1; + out_word <= {DATA_IDENTIFIER, CALIB_WORD,16'b0, fine_time, tdl_time}; + end + default: begin + out_valid <= 0; + out_word <= 0; + end + endcase +end + +endmodule diff --git a/examples/tdc_bdaq/firmware/src/SiTCP.xdc b/examples/tdc_bdaq/firmware/src/SiTCP.xdc new file mode 100644 index 00000000..6c5df838 --- /dev/null +++ b/examples/tdc_bdaq/firmware/src/SiTCP.xdc @@ -0,0 +1,32 @@ +#######SiTCP####### + +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_RXBUF/cmpWrAddr*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXBUF/smpWrStatusAddr*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_TXBUF/orRdAct*/C}] -to [get_pins -hier -filter {name =~ */GMII_TXBUF/irRdAct*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_TXBUF/muxEndTgl/C}] -to [get_pins -hier -filter {name =~ */GMII_TXBUF/rsmpMuxTrnsEnd*/D}] 5.500 + +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX10Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/irMacFlowEnb/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX12Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyMac*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX13Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyMac*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX14Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyMac*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX15Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyMac*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX16Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyMac*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX17Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyMac*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX18Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyIp*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX19Data*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyIp*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX1AData*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyIp*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */SiTCP_INT_REG/regX1BData*/C}] -to [get_pins -hier -filter {name =~ */GMII_RXCNT/muxMyIp*/D}] 5.500 + +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_TXBUF/dlyBank0LastWrAddr*/C}] -to [get_pins -hier -filter {name =~ */GMII_TXBUF/rsmpBank0LastWrAddr*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_TXBUF/dlyBank1LastWrAddr*/C}] -to [get_pins -hier -filter {name =~ */GMII_TXBUF/rsmpBank1LastWrAddr*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_TXBUF/memRdReq*/C}] -to [get_pins -hier -filter {name =~ */GMII_TXBUF/irMemRdReq*/D}] 5.500 + +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_RXCNT/orMacTim*/C}] -to [get_pins -hier -filter {name =~ */GMII_TXCNT/irMacPauseTime*/D}] 5.500 +set_max_delay -datapath_only -from [get_pins -hier -filter {name =~ */GMII_RXCNT/orMacPause/C}] -to [get_pins -hier -filter {name =~ */GMII_TXCNT/irMacPauseExe_0/D}] 5.500 + +set_false_path -from [get_pins -hier -filter {name =~ */SiTCP_INT/SiTCP_RESET_OUT/C}] + + + + + + diff --git a/examples/tdc_bdaq/firmware/src/bdaq53.xdc b/examples/tdc_bdaq/firmware/src/bdaq53.xdc new file mode 100644 index 00000000..b71e2aa8 --- /dev/null +++ b/examples/tdc_bdaq/firmware/src/bdaq53.xdc @@ -0,0 +1,153 @@ + +create_clock -period 10.000 -name clkin -add [get_ports clkin] +create_clock -period 8.000 -name rgmii_rxc -add [get_ports rgmii_rxc] + +create_clock -period 6.250 -name CLK_160_in -add [get_ports Si570_P] +create_clock -period 6.400 -name CLK_156M250_in -add [get_ports Si511_P] + +create_generated_clock -name I2C_CLK -source [get_pins PLLE2_BASE_BUS/CLKOUT0] -divide_by 1600 [get_pins -hier -filter {NAME =~ *i_clock_divisor_i2c/CLOCK_reg/Q}] + +set_false_path -from [get_clocks CLK125PLLTX] -to [get_clocks BUS_CLK_PLL] +set_false_path -from [get_clocks BUS_CLK_PLL] -to [get_clocks CLK125PLLTX] +set_false_path -from [get_clocks BUS_CLK_PLL] -to [get_clocks rgmii_rxc] +set_false_path -from [get_clocks rgmii_rxc] -to [get_clocks BUS_CLK_PLL] +set_false_path -from [get_clocks BUS_CLK_PLL] -to [get_clocks CLK160PLL] +set_false_path -from [get_clocks CLK160PLL] -to [get_clocks BUS_CLK_PLL] +set_false_path -from [get_clocks CLK160PLL] -to [get_clocks CLK480PLL] +set_false_path -from [get_clocks BUS_CLK_PLL] -to [get_clocks CLK480PLL] +set_false_path -from [get_clocks rgmii_rxc] -to [get_clocks CLK160PLL] +set_false_path -from [get_clocks CLK_156M250_in] -to [get_clocks CLK160PLL] +set_false_path -from [get_clocks CLK_156M250_in] -to [get_clocks CLK480PLL] +set_false_path -from [get_clocks CLK_156M250_in] -to [get_clocks BUS_CLK_PLL] +set_false_path -from [get_clocks BUS_CLK_PLL] -to [get_clocks CLK_156M250_in] +set_false_path -from [get_clocks BUS_CLK_PLL] -to [get_clocks I2C_CLK] + +set_false_path -from [get_cells -hier -filter {NAME =~ */calib_sig_gen/* && IS_SEQUENTIAL ==1}] -to [get_cells -hier -filter {NAME =~ */i_controller/* && IS_SEQUENTIAL ==1 }] +set_false_path -from [get_cells -hier -filter {NAME =~ */input_mux_addr_buf_reg* && IS_SEQUENTIAL ==1}] -to [get_cells -hier -filter {NAME =~ */tdl_sampler/carry_chain* && IS_SEQUENTIAL ==1 }] +set_false_path -from [get_cells -hier -filter {NAME =~ */calib_sig_gen/* && IS_SEQUENTIAL ==1}] -to [get_cells -hier -filter {NAME =~ */tdl_sampler/* && IS_SEQUENTIAL ==1 }] +set_false_path -from [get_cells -hier -filter {NAME =~ */conf_en_invert_tdc_synchronizer_dv_clk/* && IS_SEQUENTIAL ==1}] -to [get_cells -hier -filter {NAME =~ */tdl_sampler/carry_chain* && IS_SEQUENTIAL ==1}] + +#CLK Mux +set_property PACKAGE_PIN D23 [get_ports MGT_REF_SEL] +set_property IOSTANDARD LVCMOS33 [get_ports MGT_REF_SEL] +set_property PULLUP true [get_ports MGT_REF_SEL] + +# I2C pins +set_property PACKAGE_PIN L23 [get_ports I2C_SCL] +set_property PACKAGE_PIN C24 [get_ports I2C_SDA] +set_property IOSTANDARD LVCMOS33 [get_ports I2C_*] +set_property SLEW SLOW [get_ports I2C_*] + +# Si570 Clock, MGT_REFCLK0 in bdaq +set_property PACKAGE_PIN H6 [get_ports Si570_P] +set_property PACKAGE_PIN H5 [get_ports Si570_N] + +# Si511 Clock, MGT_REFCLK3 in bdaq +set_property PACKAGE_PIN F6 [get_ports Si511_P] +set_property PACKAGE_PIN F5 [get_ports Si511_N] + +#NET "Clk100" +set_property PACKAGE_PIN AA4 [get_ports clkin] +set_property IOSTANDARD LVCMOS15 [get_ports clkin] + +set_property PACKAGE_PIN G9 [get_ports RESET_N] +set_property IOSTANDARD LVCMOS33 [get_ports RESET_N] +set_property PULLUP true [get_ports RESET_N] + +set_property SLEW FAST [get_ports mdio_phy_mdc] +set_property IOSTANDARD LVCMOS33 [get_ports mdio_phy_mdc] +set_property PACKAGE_PIN B25 [get_ports mdio_phy_mdc] + +set_property SLEW FAST [get_ports mdio_phy_mdio] +set_property IOSTANDARD LVCMOS33 [get_ports mdio_phy_mdio] +set_property PACKAGE_PIN B26 [get_ports mdio_phy_mdio] + +#M20 is routed to Connector C. The Ethernet PHY on th KX2 board has NO reset connection +set_property SLEW FAST [get_ports phy_rst_n] +set_property IOSTANDARD LVCMOS33 [get_ports phy_rst_n] +set_property PACKAGE_PIN M20 [get_ports phy_rst_n] + +set_property IOSTANDARD LVCMOS33 [get_ports rgmii_rxc] +set_property PACKAGE_PIN G22 [get_ports rgmii_rxc] + +set_property IOSTANDARD LVCMOS33 [get_ports rgmii_rx_ctl] +set_property PACKAGE_PIN F23 [get_ports rgmii_rx_ctl] + +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_rxd[0]}] +set_property PACKAGE_PIN H23 [get_ports {rgmii_rxd[0]}] +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_rxd[1]}] +set_property PACKAGE_PIN H24 [get_ports {rgmii_rxd[1]}] +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_rxd[2]}] +set_property PACKAGE_PIN J21 [get_ports {rgmii_rxd[2]}] +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_rxd[3]}] +set_property PACKAGE_PIN H22 [get_ports {rgmii_rxd[3]}] + +set_property SLEW FAST [get_ports rgmii_txc] +set_property IOSTANDARD LVCMOS33 [get_ports rgmii_txc] +set_property PACKAGE_PIN K23 [get_ports rgmii_txc] + +set_property SLEW FAST [get_ports rgmii_tx_ctl] +set_property IOSTANDARD LVCMOS33 [get_ports rgmii_tx_ctl] +set_property PACKAGE_PIN J23 [get_ports rgmii_tx_ctl] + +set_property SLEW FAST [get_ports {rgmii_txd[0]}] +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_txd[0]}] +set_property PACKAGE_PIN J24 [get_ports {rgmii_txd[0]}] +set_property SLEW FAST [get_ports {rgmii_txd[1]}] +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_txd[1]}] +set_property PACKAGE_PIN J25 [get_ports {rgmii_txd[1]}] +set_property SLEW FAST [get_ports {rgmii_txd[2]}] +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_txd[2]}] +set_property PACKAGE_PIN L22 [get_ports {rgmii_txd[2]}] +set_property SLEW FAST [get_ports {rgmii_txd[3]}] +set_property IOSTANDARD LVCMOS33 [get_ports {rgmii_txd[3]}] +set_property PACKAGE_PIN K22 [get_ports {rgmii_txd[3]}] + +# LEDs +# LED 0..3 are onboard LEDs +set_property PACKAGE_PIN U9 [get_ports {LED[0]}] +set_property IOSTANDARD LVCMOS15 [get_ports {LED[0]}] +set_property PACKAGE_PIN V12 [get_ports {LED[1]}] +set_property IOSTANDARD LVCMOS15 [get_ports {LED[1]}] +set_property PACKAGE_PIN V13 [get_ports {LED[2]}] +set_property IOSTANDARD LVCMOS15 [get_ports {LED[2]}] +set_property PACKAGE_PIN W13 [get_ports {LED[3]}] +set_property IOSTANDARD LVCMOS15 [get_ports {LED[3]}] +# LED 4..7 are LEDs on the BDAQ53 base board +set_property PACKAGE_PIN E21 [get_ports {LED[4]}] +set_property IOSTANDARD LVCMOS33 [get_ports {LED[4]}] +set_property PACKAGE_PIN E22 [get_ports {LED[5]}] +set_property IOSTANDARD LVCMOS33 [get_ports {LED[5]}] +set_property PACKAGE_PIN D21 [get_ports {LED[6]}] +set_property IOSTANDARD LVCMOS33 [get_ports {LED[6]}] +set_property PACKAGE_PIN C22 [get_ports {LED[7]}] +set_property IOSTANDARD LVCMOS33 [get_ports {LED[7]}] +set_property SLEW SLOW [get_ports LED*] + +# DP_ML ("DP2") L_3_P (B148) +set_property PACKAGE_PIN B17 [get_ports sig_out] +set_property IOSTANDARD LVCMOS25 [get_ports sig_out] + +# DP_ML ("DP2") L_2_P (B154) +set_property PACKAGE_PIN E18 [get_ports trig_out] +set_property IOSTANDARD LVCMOS25 [get_ports trig_out] + +# TDL input +# LEMO RX_0 +# set_property PACKAGE_PIN AB22 [get_ports sig_in] + +# DP_ML ("DP2") L_1_P (B160) +set_property PACKAGE_PIN C19 [get_ports sig_in] +set_property IOSTANDARD LVCMOS25 [get_ports sig_in] + +# LEMO RX_1 +#set_property PACKAGE_PIN AD23 [get_ports trig_in] + +# DP_ML ("DP2") L_0_P (B164) +set_property PACKAGE_PIN A18 [get_ports trig_in] +set_property IOSTANDARD LVCMOS25 [get_ports trig_in] + +# SPI configuration flash +set_property CONFIG_MODE SPIx4 [current_design] +set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design] +set_property BITSTREAM.CONFIG.CONFIGRATE 33 [current_design] diff --git a/examples/tdc_bdaq/firmware/src/tdc_bdaq.v b/examples/tdc_bdaq/firmware/src/tdc_bdaq.v new file mode 100644 index 00000000..97f1906f --- /dev/null +++ b/examples/tdc_bdaq/firmware/src/tdc_bdaq.v @@ -0,0 +1,598 @@ +// /** +// * ------------------------------------------------------------ +// * Copyright (c) All rights reserved +// * SiLab, Physics Institute, University of Bonn +// * ------------------------------------------------------------ + +`include "utils/fifo_32_to_8.v" +`include "utils/rgmii_io.v" +`include "utils/rbcp_to_bus.v" +`include "utils/bus_to_ip.v" +`include "rrp_arbiter/rrp_arbiter.v" +`include "gpio/gpio_core.v" +`include "gpio/gpio.v" +`include "tdl_tdc/tdl_tdc.v" + +// tdl_tdc dependencies +`include "utils/3_stage_synchronizer.v" +`include "utils/flag_domain_crossing.v" +`include "utils/generic_fifo.v" +`include "utils/cdc_syncfifo.v" +`include "utils/clock_divider.v" + + +// Seq_gen +`include "seq_gen/seq_gen.v" +`include "seq_gen/seq_gen_core.v" + +// Seq_gen dependencies + +`include "utils/cdc_pulse_sync.v" +`include "utils/ramb_8_to_n.v" + +// For Si570 +`include "i2c/i2c.v" +`include "i2c/i2c_core.v" + +module tdc_bdaq( + input wire sig_in, + input wire trig_in, + input wire RESET_N, + input wire clkin, + input wire Si511_P, + input wire Si511_N, + input wire Si570_P, + input wire Si570_N, + + output wire [3:0] rgmii_txd, + output wire rgmii_tx_ctl, + output wire rgmii_txc, + input wire [3:0] rgmii_rxd, + input wire rgmii_rx_ctl, + input wire rgmii_rxc, + output wire mdio_phy_mdc, + inout wire mdio_phy_mdio, + output wire phy_rst_n, + + inout wire I2C_SDA, + output wire I2C_SCL, + + output wire [7:0] LED, + output wire trig_out, + output wire sig_out, + output wire MGT_REF_SEL +); +assign MGT_REF_SEL = 1; + +wire CLK_156M250_in; +wire CLK_156M250; +IBUFDS_GTE2 IBUFDS_GTE2_inst_156 ( + .O(CLK_156M250_in), // Buffer output + .ODIV2(), + .CEB(1'b0), + .I(Si511_P), // Diff_p buffer input (connect directly to top-level port) + .IB(Si511_N) // Diff_n buffer input (connect directly to top-level port) +); + +BUFG BUFG_inst ( + .O(CLK_156M250), + .I(CLK_156M250_in) +); + +wire CLK_160_in; +wire CLK_160; +IBUFDS_GTE2 IBUFDS_GTE2_inst_160 ( + .O(CLK_160_in), // Buffer output + .ODIV2(), + .CEB(1'b0), + .I(Si570_P), // Diff_p buffer input (connect directly to top-level port) + .IB(Si570_N) // Diff_n buffer input (connect directly to top-level port) +); + + BUFG BUFG_inst_2 ( + .O(CLK_160), + .I(CLK_160_in) + ); + +wire RST; +wire BUS_CLK_PLL, CLK125PLLTX, CLK125PLLTX90; +wire PLL_FEEDBACK, LOCKED; + +PLLE2_BASE #( + .BANDWIDTH("OPTIMIZED"), // OPTIMIZED, HIGH, LOW + .CLKFBOUT_MULT(10), // Multiply value for all CLKOUT, (2-64) + .CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB, (-360.000-360.000). + .CLKIN1_PERIOD(10.000), // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz). + + .CLKOUT0_DIVIDE(7), // Divide amount for CLKOUT0 (1-128) + .CLKOUT0_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT0_PHASE(0.0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT1_DIVIDE(4), // Divide amount for CLKOUT0 (1-128) + .CLKOUT1_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT1_PHASE(0.0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT2_DIVIDE(8), // Divide amount for CLKOUT0 (1-128) + .CLKOUT2_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT2_PHASE(0.0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT3_DIVIDE(8), // Divide amount for CLKOUT0 (1-128) + .CLKOUT3_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT3_PHASE(90.0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT4_DIVIDE(8), // Divide amount for CLKOUT0 (1-128) + .CLKOUT4_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT4_PHASE(-5.625), // Phase offset for CLKOUT0 (-360.000-360.000). + + .DIVCLK_DIVIDE(1), // Master division value, (1-56) + .REF_JITTER1(0.0), // Reference input jitter in UI, (0.000-0.999). + .STARTUP_WAIT("FALSE") // Delay DONE until PLL Locks, ("TRUE"/"FALSE") +) +PLLE2_BASE_BUS ( + .CLKOUT0(BUS_CLK_PLL), + .CLKOUT1(), + .CLKOUT2(CLK125PLLTX), + .CLKOUT3(CLK125PLLTX90), + .CLKOUT4(), + .CLKOUT5(), + .CLKFBOUT(PLL_FEEDBACK), + .LOCKED(LOCKED), + .CLKIN1(clkin), + .PWRDWN(0), + .RST(!RESET_N), + .CLKFBIN(PLL_FEEDBACK) +); + +// wire CLK160, CLK40; +// wire PLL2_FEEDBACK, LOCKED160; +// PLLE2_BASE #( +// .BANDWIDTH("OPTIMIZED"), // OPTIMIZED, HIGH, LOW +// .CLKFBOUT_MULT(16), // Multiply value for all CLKOUT, (2-64) +// .CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB, (-360.000-360.000). +// .CLKIN1_PERIOD(10.000), // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz). +// +// .CLKOUT0_DIVIDE(10), // Divide amount for CLKOUT0 (1-128) +// .CLKOUT0_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). +// .CLKOUT0_PHASE(90.0), // Phase offset for CLKOUT0 (-360.000-360.000). +// +// .CLKOUT1_DIVIDE(10), // Divide amount for CLKOUT0 (1-128) +// .CLKOUT1_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). +// .CLKOUT1_PHASE(0.0), // Phase offset for CLKOUT0 (-360.000-360.000). +// +// .CLKOUT2_DIVIDE(40), // Divide amount for CLKOUT0 (1-128) +// .CLKOUT2_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). +// .CLKOUT2_PHASE(0.0), // Phase offset for CLKOUT0 (-360.000-360.000). +// +// .CLKOUT3_DIVIDE(8), // Divide amount for CLKOUT0 (1-128) +// .CLKOUT3_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). +// .CLKOUT3_PHASE(90.0), // Phase offset for CLKOUT0 (-360.000-360.000). +// +// .CLKOUT4_DIVIDE(8), // Divide amount for CLKOUT0 (1-128) +// .CLKOUT4_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). +// .CLKOUT4_PHASE(-5.625), // Phase offset for CLKOUT0 (-360.000-360.000). +// +// .DIVCLK_DIVIDE(1), // Master division value, (1-56) +// .REF_JITTER1(0.0), // Reference input jitter in UI, (0.000-0.999). +// .STARTUP_WAIT("FALSE") // Delay DONE until PLL Locks, ("TRUE"/"FALSE") +// ) +// PLLE2_BASE_160 ( +// .CLKOUT0(CLK160), +// .CLKOUT1(), +// .CLKOUT2(CLK40), +// .CLKOUT3(), +// .CLKOUT4(), +// .CLKOUT5(), +// .CLKFBOUT(PLL2_FEEDBACK), +// .LOCKED(LOCKED160), +// .CLKIN1(clkin), +// .PWRDWN(0), +// .RST(!RESET_N), +// .CLKFBIN(PLL2_FEEDBACK) +// ); + +wire PLL3_FEEDBACK, LOCKED480; +wire CLK160PLL, CLK480PLL; + +PLLE2_BASE #( + .BANDWIDTH("HIGH"), // OPTIMIZED, HIGH, LOW + .CLKFBOUT_MULT(6), // Multiply value for all CLKOUT, (2-64) + .CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB, (-360.000-360.000). + .CLKIN1_PERIOD(6.250), // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz). + + .CLKOUT0_DIVIDE(6), // Divide amount for CLKOUT0 (1-128) + .CLKOUT0_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT0_PHASE(0.0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT1_DIVIDE(2), // Divide amount for CLKOUT0 (1-128) + .CLKOUT1_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT1_PHASE(0.000), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT2_DIVIDE(24), // Divide amount for CLKOUT0 (1-128) + .CLKOUT2_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT2_PHASE(0.0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT3_DIVIDE(8), // Divide amount for CLKOUT0 (1-128) + .CLKOUT3_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT3_PHASE(90.0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .CLKOUT4_DIVIDE(8), // Divide amount for CLKOUT0 (1-128) + .CLKOUT4_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0 (0.001-0.999). + .CLKOUT4_PHASE(0), // Phase offset for CLKOUT0 (-360.000-360.000). + + .DIVCLK_DIVIDE(1), // Master division value, (1-56) + .REF_JITTER1(0.0), // Reference input jitter in UI, (0.000-0.999). + .STARTUP_WAIT("FALSE") // Delay DONE until PLL Locks, ("TRUE"/"FALSE") +) +PLLE2_BASE_480 ( + .CLKOUT0(CLK160PLL), + .CLKOUT1(CLK480PLL), + .CLKOUT2(), + .CLKOUT3(), + .CLKOUT4(), + .CLKOUT5(), + .CLKFBOUT(PLL3_FEEDBACK), + .LOCKED(LOCKED480), + .CLKIN1(CLK_160), + .PWRDWN(0), + .RST(!RESET_N), + .CLKFBIN(PLL3_FEEDBACK) +); + + + +wire BUS_CLK; +BUFG BUFG_inst_BUS_CKL (.O(BUS_CLK), .I(BUS_CLK_PLL) ); + +wire CLK125TX, CLK125TX90, CLK125RX; +BUFG BUFG_inst_CLK125TX ( .O(CLK125TX), .I(CLK125PLLTX) ); +BUFG BUFG_inst_CLK125TX90 ( .O(CLK125TX90), .I(CLK125PLLTX90) ); +BUFG BUFG_inst_CLK125RX ( .O(CLK125RX), .I(rgmii_rxc) ); + +assign RST = !RESET_N | !LOCKED; +wire I2C_CLK; + +clock_divider #( + .DIVISOR(1600) +) i_clock_divisor_i2c ( + .CLK(BUS_CLK), + .RESET(1'b0), + .CE(), + .CLOCK(I2C_CLK) +); + + +localparam I2C_MEM_BYTES = 32; + +localparam I2C_BASEADDR = 32'h6000; +localparam I2C_HIGHADDR = 32'h6100-1; + +i2c #( + .BASEADDR(I2C_BASEADDR), + .HIGHADDR(I2C_HIGHADDR), + .ABUSWIDTH(32), + .MEM_BYTES(I2C_MEM_BYTES) +) i_i2c ( + .BUS_CLK(BUS_CLK), + .BUS_RST(BUS_RST), + .BUS_ADD(BUS_ADD), + .BUS_DATA(BUS_DATA), + .BUS_RD(BUS_RD), + .BUS_WR(BUS_WR), + + .I2C_CLK(I2C_CLK), + .I2C_SDA(I2C_SDA), + .I2C_SCL(I2C_SCL) +); + + + +wire gmii_tx_en; +wire [7:0] gmii_txd; +wire gmii_tx_er; +wire gmii_crs; +wire gmii_col; +wire gmii_rx_dv; +wire [7:0] gmii_rxd; +wire gmii_rx_er; +wire mdio_gem_i; +wire mdio_gem_o; +wire mdio_gem_t; +wire link_status; +wire [1:0] clock_speed; +wire duplex_status; + +rgmii_io rgmii +( + .rgmii_txd(rgmii_txd), + .rgmii_tx_ctl(rgmii_tx_ctl), + .rgmii_txc(rgmii_txc), + + .rgmii_rxd(rgmii_rxd), + .rgmii_rx_ctl(rgmii_rx_ctl), + + .gmii_txd_int(gmii_txd), // Internal gmii_txd signal. + .gmii_tx_en_int(gmii_tx_en), + .gmii_tx_er_int(gmii_tx_er), + .gmii_col_int(gmii_col), + .gmii_crs_int(gmii_crs), + .gmii_rxd_reg(gmii_rxd), // RGMII double data rate data valid. + .gmii_rx_dv_reg(gmii_rx_dv), // gmii_rx_dv_ibuf registered in IOBs. + .gmii_rx_er_reg(gmii_rx_er), // gmii_rx_er_ibuf registered in IOBs. + + .eth_link_status(link_status), + .eth_clock_speed(clock_speed), + .eth_duplex_status(duplex_status), + + // Following are generated by DCMs + .tx_rgmii_clk_int(CLK125TX), // Internal RGMII transmitter clock. + .tx_rgmii_clk90_int(CLK125TX90), // Internal RGMII transmitter clock w/ 90 deg phase + .rx_rgmii_clk_int(CLK125RX), // Internal RGMII receiver clock + + .reset(!phy_rst_n) +); + + +// Instantiate tri-state buffer for MDIO +IOBUF i_iobuf_mdio( + .O(mdio_gem_i), + .IO(mdio_phy_mdio), + .I(mdio_gem_o), + .T(mdio_gem_t) +); + +wire TCP_CLOSE_REQ; +wire TCP_OPEN_ACK; +wire RBCP_ACT, RBCP_WE, RBCP_RE; +wire [7:0] RBCP_WD, RBCP_RD; +wire [31:0] RBCP_ADDR; +wire TCP_RX_WR; +wire TCP_TX_WR; +wire [7:0] TCP_RX_DATA; +wire [7:0] TCP_TX_DATA; +wire TCP_TX_FULL; +wire RBCP_ACK; +wire SiTCP_RST; +reg [10:0] TCP_RX_WC_11B; + + +WRAP_SiTCP_GMII_XC7K_32K sitcp( + .CLK(BUS_CLK) , // in : System Clock >129MHz + .RST(RST) , // in : System reset + + .FORCE_DEFAULTn(1'b0) , // in : Load default parameters + .EXT_IP_ADDR(32'hc0a80a10) , // in : IP address[31:0] //192.168.10.16 + .EXT_TCP_PORT(16'd24) , // in : TCP port #[15:0] + .EXT_RBCP_PORT(16'd4660) , // in : RBCP port #[15:0] + .PHY_ADDR(5'd3) , // in : PHY-device MIF address[4:0] + + .EEPROM_CS() , // out : Chip select + .EEPROM_SK() , // out : Serial data clock + .EEPROM_DI() , // out : Serial write data + .EEPROM_DO(1'b0) , // in : Serial read data + + .USR_REG_X3C() , // out : Stored at 0xFFFF_FF3C + .USR_REG_X3D() , // out : Stored at 0xFFFF_FF3D + .USR_REG_X3E() , // out : Stored at 0xFFFF_FF3E + .USR_REG_X3F() , // out : Stored at 0xFFFF_FF3F + + .GMII_RSTn(phy_rst_n) , // out : PHY reset + .GMII_1000M(1'b1) , // in : GMII mode (0:MII, 1:GMII) + + .GMII_TX_CLK(CLK125TX) , // in : Tx clock + .GMII_TX_EN(gmii_tx_en) , // out : Tx enable + .GMII_TXD(gmii_txd) , // out : Tx data[7:0] + .GMII_TX_ER(gmii_tx_er) , // out : TX error + .GMII_RX_CLK(CLK125RX) , // in : Rx clock + .GMII_RX_DV(gmii_rx_dv) , // in : Rx data valid + .GMII_RXD(gmii_rxd) , // in : Rx data[7:0] + .GMII_RX_ER(gmii_rx_er) , // in : Rx error + .GMII_CRS(gmii_crs) , // in : Carrier sense + .GMII_COL(gmii_col) , // in : Collision detected + .GMII_MDC(mdio_phy_mdc) , // out : Clock for MDIO + .GMII_MDIO_IN(mdio_gem_i) , // in : Data + .GMII_MDIO_OUT(mdio_gem_o) , // out : Data + .GMII_MDIO_OE(mdio_gem_t) , // out : MDIO output enable + .SiTCP_RST(SiTCP_RST) , // out : Reset for SiTCP and related circuits + .TCP_OPEN_REQ(1'b0) , // in : Reserved input, shoud be 0 + .TCP_OPEN_ACK(TCP_OPEN_ACK) , // out : Acknowledge for open (=Socket busy) + .TCP_ERROR() , // out : TCP error, its active period is equal to MSL + .TCP_CLOSE_REQ(TCP_CLOSE_REQ) , // out : Connection close request + .TCP_CLOSE_ACK(TCP_CLOSE_REQ) , // in : Acknowledge for closing + .TCP_RX_WC({5'b1,TCP_RX_WC_11B}) , // in : Rx FIFO write count[15:0] (Unused bits should be set 1) + .TCP_RX_WR(TCP_RX_WR) , // out : Write enable + .TCP_RX_DATA(TCP_RX_DATA) , // out : Write data[7:0] + .TCP_TX_FULL(TCP_TX_FULL) , // out : Almost full flag + .TCP_TX_WR(TCP_TX_WR) , // in : Write enable + .TCP_TX_DATA(TCP_TX_DATA) , // in : Write data[7:0] + .RBCP_ACT(RBCP_ACT) , // out : RBCP active + .RBCP_ADDR(RBCP_ADDR) , // out : Address[31:0] + .RBCP_WD(RBCP_WD) , // out : Data[7:0] + .RBCP_WE(RBCP_WE) , // out : Write enable + .RBCP_RE(RBCP_RE) , // out : Read enable + .RBCP_ACK(RBCP_ACK) , // in : Access acknowledge + .RBCP_RD(RBCP_RD) // in : Read data[7:0] +); + + +wire [31:0] BUS_ADD; +wire [7:0] BUS_DATA; +wire BUS_WR, BUS_RD, BUS_RST; +assign BUS_RST = SiTCP_RST; + +rbcp_to_bus irbcp_to_bus( + .BUS_RST(BUS_RST), + .BUS_CLK(BUS_CLK), + + .RBCP_ACT(RBCP_ACT), + .RBCP_ADDR(RBCP_ADDR), + .RBCP_WD(RBCP_WD), + .RBCP_WE(RBCP_WE), + .RBCP_RE(RBCP_RE), + .RBCP_ACK(RBCP_ACK), + .RBCP_RD(RBCP_RD), + + .BUS_WR(BUS_WR), + .BUS_RD(BUS_RD), + .BUS_ADD(BUS_ADD), + .BUS_DATA(BUS_DATA) + ); + +localparam GPIO_BASEADDR = 32'h1000; +localparam GPIO_HIGHADDR = 32'h101f; +wire [7:0] GPIO; +gpio #( + .BASEADDR(GPIO_BASEADDR), + .HIGHADDR(GPIO_HIGHADDR), + .ABUSWIDTH(32), + .IO_WIDTH(8), + .IO_DIRECTION(8'hff) +) i_gpio_rx ( + .BUS_CLK(BUS_CLK), + .BUS_RST(BUS_RST), + .BUS_ADD(BUS_ADD), + .BUS_DATA(BUS_DATA[7:0]), + .BUS_RD(BUS_RD), + .BUS_WR(BUS_WR), + .IO(GPIO) +); + + +localparam SEQ_BASEADDR = 32'h1120; +localparam SEQ_HIGHADDR = 32'h5160 - 1; + +wire [7:0] SEQ_OUT; +seq_gen #( + .BASEADDR(SEQ_BASEADDR), + .HIGHADDR(SEQ_HIGHADDR), + .ABUSWIDTH(32), + .MEM_BYTES(16384), + .OUT_BITS(8) +) i_seq_gen ( + .BUS_CLK(BUS_CLK), + .BUS_RST(BUS_RST), + .BUS_ADD(BUS_ADD), + .BUS_DATA(BUS_DATA[7:0]), + .BUS_RD(BUS_RD), + .BUS_WR(BUS_WR), + + .SEQ_EXT_START(1'b0), + .SEQ_CLK(CLK_156M250), + .SEQ_OUT(SEQ_OUT) +); + +reg trig_out_buf, sig_out_buf; +assign trig_out = trig_out_buf; +assign sig_out = sig_out_buf; + +always @(posedge CLK_156M250) begin + trig_out_buf <= SEQ_OUT[0]; + sig_out_buf <= SEQ_OUT[1]; +end + + +wire EN, ARM; +assign EN = GPIO[0]; +assign ARM = GPIO[1]; + +localparam TDC_BASEADDR = 32'h1020; +localparam TDC_HIGHADDR = 32'h111f; +wire [31:0] tdc_fifo_data; +tdl_tdc #( + .BASEADDR(TDC_BASEADDR), + .HIGHADDR(TDC_HIGHADDR), + .ABUSWIDTH(32), + .DATA_IDENTIFIER(4'b0100) +) i_tdc ( + .BUS_CLK(BUS_CLK), + .bus_add(BUS_ADD), + .bus_data(BUS_DATA), + .bus_rst(BUS_RST), + .bus_wr(BUS_WR), + .bus_rd(BUS_RD), + + .CLK480(CLK480PLL), + .CLK160(CLK160PLL), + .CALIB_CLK(CLK125RX), + .tdc_in(sig_in),//(sig_out_buf),//(sig_in), + .trig_in(trig_in),//(trig_out_buf),//(trig_in), + + .timestamp(42), + .ext_en(EN), + .arm_tdc(ARM), + .fifo_read(tdc_fifo_read), + + .fifo_empty(tdc_fifo_empty), + .fifo_data(tdc_fifo_data) +); + +wire tdc_fifo_read, tdc_fifo_empty; +wire ARB_WRITE_OUT; +wire [31:0] ARB_DATA_OUT; +wire TCP_FIFO_FULL, TCP_FIFO_EMPTY; +reg FIFO_NEXT; + +rrp_arbiter #( + .WIDTH(1) +) i_rrp_arbiter ( + .RST(BUS_RST), + .CLK(BUS_CLK), + .WRITE_REQ(!tdc_fifo_empty), + .HOLD_REQ(1'b0), // wait for writing for given stream (priority) + .DATA_IN(tdc_fifo_data), // incoming data for arbitration + .READ_GRANT(tdc_fifo_read), // indicate to stream that data has been accepted + .READY_OUT(~TCP_FIFO_FULL && FIFO_NEXT), // indicates ready for outgoing stream (input) + .WRITE_OUT(ARB_WRITE_OUT), // indicates will of write to outgoing stream + .DATA_OUT(ARB_DATA_OUT) // outgoing data stream +); + +assign TCP_TX_WR = !TCP_TX_FULL & !TCP_FIFO_EMPTY; + +fifo_32_to_8 #( + .DEPTH(128*1024) +) i_data_fifo ( + .RST(BUS_RST), + .CLK(BUS_CLK), + + .WRITE(ARB_WRITE_OUT && FIFO_NEXT), + .READ(TCP_TX_WR), + .DATA_IN(ARB_DATA_OUT), + .FULL(TCP_FIFO_FULL), + .EMPTY(TCP_FIFO_EMPTY), + .DATA_OUT(TCP_TX_DATA) +); + +reg ETH_START_SENDING, ETH_START_SENDING_temp, ETH_START_SENDING_LOCK; + + +/* ------- Main FSM ------- */ +always @(posedge BUS_CLK) begin + // wait for start condition + ETH_START_SENDING <= EN; //TCP_OPEN_ACK; + + if (ETH_START_SENDING && !ETH_START_SENDING_temp) + ETH_START_SENDING_LOCK <= 1; + ETH_START_SENDING_temp <= ETH_START_SENDING; + + // RX FIFO word counter + if (TCP_RX_WR) + TCP_RX_WC_11B <= TCP_RX_WC_11B + 1; + else + TCP_RX_WC_11B <= 11'd0; + + + // FIFO handshake + if (ETH_START_SENDING_LOCK) + FIFO_NEXT <= 1'b1; + else + FIFO_NEXT <= 1'b0; + + // stop, if connection is closed by host + if (TCP_CLOSE_REQ || !EN) + ETH_START_SENDING_LOCK <= 0; +end + +assign LED = ~{ TCP_OPEN_ACK, TCP_CLOSE_REQ, TCP_RX_WR, TCP_TX_WR, + TCP_FIFO_FULL, TCP_FIFO_EMPTY, sig_out, trig_out}; + + +endmodule diff --git a/examples/tdc_bdaq/firmware/src/tdc_bdaq.xdc b/examples/tdc_bdaq/firmware/src/tdc_bdaq.xdc new file mode 100644 index 00000000..5371ff16 --- /dev/null +++ b/examples/tdc_bdaq/firmware/src/tdc_bdaq.xdc @@ -0,0 +1,22 @@ +set_property LOC SLICE_X38Y3 [get_cells -hier -regexp .*FirstCell/CARRY4_inst] + +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/hit_status[0]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/hit_status[1]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/fine_time[0]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/fine_time[1]}] + +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/encoder/position_out[0]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/encoder/position_out[1]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/encoder/position_out[2]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/encoder/position_out[3]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/encoder/position_out[4]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/encoder/position_out[5]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/encoder/position_out[6]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/tdc_state_delayed[0]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/tdc_state_delayed[1]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/tdc_state_delayed[2]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/tdc_state_delayed[3]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/fine_time_delayed[0]}] +set_property MARK_DEBUG true [get_nets {i_tdc/i_tdc_core/fine_time_delayed[1]}] + + diff --git a/examples/tdc_bdaq/firmware/vivado/run.tcl b/examples/tdc_bdaq/firmware/vivado/run.tcl new file mode 100644 index 00000000..2cda798b --- /dev/null +++ b/examples/tdc_bdaq/firmware/vivado/run.tcl @@ -0,0 +1,68 @@ + +# ----------------------------------------------------------- +# Copyright (c) SILAB , Physics Institute, University of Bonn +# ----------------------------------------------------------- +# +# This script creates Vivado projects and bitfiles for the supported hardware platforms +# +# vivado -mode tcl -source run.tcl +# + +# Use current environment python instead of vivado included python +if {[info exists ::env(PYTHONPATH)]} { + unset ::env(PYTHONPATH) +} +if {[info exists ::env(PYTHONHOME)]} { + unset ::env(PYTHONHOME) +} +# Get rid of Vivado python (since Vivado 2021) in PATH and use python from calling shell +set env(PATH) [join [lsearch -inline -all -not -regexp [split $::env(PATH) ":"] (.*)lnx64\/python(.*)] ":"] + +set basil_dir [exec python -c "import basil, os; print(str(os.path.dirname(basil.__file__)))"] +set include_dirs [list $basil_dir/firmware/modules $basil_dir/firmware/modules/utils] + +file mkdir output reports + + +proc read_design_files {} { + read_verilog ../src/tdc_bdaq.v + + read_edif ../SiTCP/SiTCP_XC7K_32K_BBT_V110.ngc + read_verilog ../SiTCP/TIMER.v + read_verilog ../SiTCP/SiTCP_XC7K_32K_BBT_V110.V + read_verilog ../SiTCP/WRAP_SiTCP_GMII_XC7K_32K.V +} + + +proc run_bit { part board xdc_file size option} { + set prjname $board$option\_TDL_TDC + + create_project -force -part $part $prjname designs + read_design_files + read_xdc $xdc_file + read_xdc ../src/tdc_bdaq.xdc + read_xdc ../src/SiTCP.xdc + + global include_dirs + + synth_design -top tdc_bdaq -include_dirs $include_dirs -verilog_define "$board=1" -verilog_define "SYNTHESIS=1" -verilog_define "$option=1" + opt_design + place_design + phys_opt_design + route_design + report_utilization -file "reports/report_utilization_$prjname.log" + report_timing -file "reports/report_timing_$prjname.log" + write_bitstream -force -file output/$prjname + write_cfgmem -format mcs -size $size -interface SPIx4 -loadbit "up 0x0 output/$prjname.bit" -force -file output/$prjname + close_project + + exec tar -C ./output -cvzf output/$prjname.tar.gz $prjname.bit $prjname.mcs +} + + +# Create projects and bitfiles + +# FPGA type board name constraints file flash size option +run_bit xc7k160tffg676-2 BDAQ53 ../src/bdaq53.xdc 64 "" + +exit diff --git a/examples/tdc_bdaq/tdc_bdaq.py b/examples/tdc_bdaq/tdc_bdaq.py new file mode 100644 index 00000000..6d183f99 --- /dev/null +++ b/examples/tdc_bdaq/tdc_bdaq.py @@ -0,0 +1,191 @@ +# ------------------------------------------------------------ +# SiTCP throughput test +# Reads data for a couple of seconds and displays the data rate +# +# Copyright (c) All rights reserved +# SiLab, Physics Institute, University of Bonn +# ------------------------------------------------------------ +# +import logging +import time +from tqdm import tqdm + +import numpy as np +from scipy import stats +import matplotlib.pyplot as plt + +from basil.dut import Dut +from basil.HL import si570 + + +def disassemble_tdc_word(word): + word_type_codes = {0: 'TRIGGERED', + 1: 'RISING', + 2: 'FALLING', + 3: 'TIMESTAMP', + 4: 'OVFLOW', + 5: 'CALIB', + 6: 'MISS', + 7: 'RST'} + # Shift away the 32 - 7 data bits and grab 3 bit word type + return {'source_id': (word >> (32 - 4)), + 'word_type': word_type_codes[(word >> (32 - 7)) & 0b111], + 'tdl_value': word & 0b1111111, + 'fine_value': (word >> 7) & 0b11, + 'corse_value': (word >> 9) & 0xFFFF} + + +chip = Dut("tdc_bdaq.yaml") +chip.init() + +si570_conf = {'name': 'si570', 'type': 'bdaq53.si570', 'interface': 'intf', 'base_addr': 0xba, 'init': {'frequency': 160}} +si570_clk = si570.si570(chip['i2c'], si570_conf) +time.sleep(0.1) +si570_clk.init() +time.sleep(0.1) + +chip['TDL_TDC'].reset() + + +chip['CONTROL']['EN'] = 0 +chip['CONTROL'].write() + +logging.info("Starting TDC...") + +chip['CONTROL']['EN'] = 1 +chip['CONTROL'].write() + +chip['TDL_TDC'].RESET = 1 + +chip['TDL_TDC'].EN_TRIGGER_DIST = 0 +chip['TDL_TDC'].ENABLE = 1 +chip['TDL_TDC'].RESET = 0 +chip['TDL_TDC'].EN_TRIGGER_DIST = 1 + +collected_data = np.empty(0, dtype=np.uint32) +calib_duration = 3 +start_time = time.time() + +chip['TDL_TDC'].EN_CALIBRATION_MODE = 1 + +while time.time() - start_time < calib_duration: + time.sleep(.01) + + fifo_data = chip['FIFO'].get_data() + data_size = len(fifo_data) + collected_data = np.concatenate((collected_data, fifo_data), dtype=np.uint32) + + +chip['TDL_TDC'].EN_CALIBRATION_MODE = 0 + +chip['TDL_TDC'].RESET = 1 +chip['FIFO'].get_data() + +chip['CONTROL']['EN'] = 0 # stop data source +chip['CONTROL'].write() + +calib_data_indices = chip['TDL_TDC'].is_calib_word(collected_data) +print(calib_data_indices) + + +if any(calib_data_indices): + calib_values = chip['TDL_TDC'].get_raw_tdl_values(np.array(collected_data[calib_data_indices])) + print(calib_values[-20:]) + chip['TDL_TDC'].set_calib_values(calib_values) + # chip['TDL_TDC'].plot_calib_values(calib_values) + logging.info("Calibration set using %s samples" % len(calib_values)) + + +collected_rising = [] +collected_falling = [] +chip['CONTROL']['EN'] = 1 # start data source +chip['CONTROL'].write() +chip['FIFO'].get_data() + +chip['TDL_TDC'].RESET = 1 +time.sleep(0.1) +chip['FIFO'].get_data() +logging.info("Ready for measurements") +chip['TDL_TDC'].EN_TRIGGER_DIST = 1 +chip['TDL_TDC'].EN_WRITE_TIMESTAMP = 0 + +delta_t = 0 + + +def reject_outliers(data, m=2): + return data[abs(data - np.median(data)) < m] + + +def load_and_start_trig_seq(trig_dis_cycles): + # 10/0.125Ghz = 80ns + sig_cycles = 10 + + kilo_trig_cycles = int(np.floor(trig_dis_cycles / 1000)) + need_kilo_cycles = 0 + if kilo_trig_cycles > 0: + need_kilo_cycles = 1 + remain_trig_cycles = trig_dis_cycles % 1000 + + trig_total = remain_trig_cycles + need_kilo_cycles * 1000 + + chip['SEQ'].reset() + while not chip['SEQ'].is_ready: + pass + chip['SEQ'].SIZE = sig_cycles + trig_total + 1 + chip['SEQ']['TDC_IN'][:] = False + chip['SEQ']['TDC_IN'][trig_total:sig_cycles + trig_total] = True + chip['SEQ']['TDC_TRIGGER_IN'][0:20] = True + chip['SEQ'].write(sig_cycles + trig_total + 1) + + chip['SEQ'].REPEAT = 1 + # This repeat window isn't optimal as the trigger signal is repeated. + # However, once first registered, the repeated trigger signal will + # be ignored by the TDC implementation. + chip['SEQ'].NESTED_START = 0 + chip['SEQ'].NESTED_STOP = 1000 + chip['SEQ'].NESTED_REPEAT = kilo_trig_cycles + + chip['SEQ'].START + + +seq_clk_GHZ = 0.15625 +N_measure = 500 +current_measurements = np.zeros(N_measure) +measured = [] +stds = [] +actual = [] +for i in tqdm(range(10, 4000, 100)): + for j in range(N_measure): + + load_and_start_trig_seq(i) + while not chip['SEQ'].is_ready: + time.sleep(0.0001) + pass + fifo_size = chip['FIFO']._intf._get_tcp_data_size() + fifo_int_size = int((fifo_size - (fifo_size % 4)) / 4) + while fifo_int_size < 2: + time.sleep(0.0001) + fifo_size = chip['FIFO']._intf._get_tcp_data_size() + fifo_int_size = int((fifo_size - (fifo_size % 4)) / 4) + fifo_data = chip['FIFO'].get_data() + times = chip['TDL_TDC'].tdc_word_to_time(fifo_data) + current_measurements[j] = times[1] - times[0] + actual.append(i / seq_clk_GHZ) + std = np.std(current_measurements) + print('Actual time: %5.3f Measured time: %5.3f Difference: %.3f Std %.3f' % (i / seq_clk_GHZ, times[1] - times[0], times[1] - times[0] - i / seq_clk_GHZ, std)) + measured.append(np.mean(current_measurements)) + stds.append(std) + print() +actual = np.asarray(actual) +measured = np.asarray(measured) +stds = np.asarray(stds) +res = stats.linregress(actual, measured - actual) +print(f"R-squared: {res.rvalue**2:.6f}") +print('Intercept: %f' % (res.intercept)) +print('Slope: %.6e' % (res.slope)) +plt.errorbar(actual, measured - actual, yerr=stds) +plt.plot(actual, res.intercept + res.slope * actual, 'r') +plt.show() + +plt.plot(actual, stds) +plt.show() diff --git a/examples/tdc_bdaq/tdc_bdaq.yaml b/examples/tdc_bdaq/tdc_bdaq.yaml new file mode 100644 index 00000000..f6e80053 --- /dev/null +++ b/examples/tdc_bdaq/tdc_bdaq.yaml @@ -0,0 +1,79 @@ +# +# ------------------------------------------------------------ +# Copyright (c) All rights reserved +# SiLab, Physics Institute, University of Bonn +# ------------------------------------------------------------ +# + +transfer_layer: + - name : intf + type : SiTcp + init: + ip : "192.168.10.16" + udp_port : 4660 + tcp_port : 24 + tcp_connection : True + +hw_drivers: + - name : GPIO_DRV + type : gpio + interface : intf + base_addr : 0x1000 + size : 8 + + - name : TDL_TDC + type : tdl_tdc + interface : intf + base_addr : 0x1020 + + - name : SEQ_GEN + type : seq_gen + interface : intf + base_addr : 0x1120 + + - name : FIFO + type : sitcp_fifo + interface : intf + base_addr : 0x200000000 + base_data_addr : 0x100000000 + + - name : i2c + type : i2c + interface : intf + base_addr : 0x6000 + +registers: + - name : SEQ + type : TrackRegister + hw_driver : SEQ_GEN + seq_width : 8 + seq_size : 65535 + tracks: + - name : TDC_TRIGGER_IN + position : 0 + - name : TDC_IN + position : 1 + - name : UNUSED1 + position : 2 + - name : UNUSED2 + position : 3 + - name : UNUSED3 + position : 4 + - name : UNUSED4 + position : 5 + - name : UNUSED5 + position : 6 + - name : UNUSED6 + position : 7 + + - name : CONTROL + type : StdRegister + hw_driver : GPIO_DRV + size : 8 + fields: + - name : EN + size : 1 + offset : 0 + - name : ARM + size : 1 + offset : 1 diff --git a/tests/test_SimTdl_Tdc.py b/tests/test_SimTdl_Tdc.py new file mode 100644 index 00000000..4fa1a32b --- /dev/null +++ b/tests/test_SimTdl_Tdc.py @@ -0,0 +1,179 @@ +# +# ------------------------------------------------------------ +# Copyright (c) All rights reserved +# SiLab, Institute of Physics, University of Bonn +# ------------------------------------------------------------ +# + +import unittest +import os + +from basil.dut import Dut +from basil.utils.sim.utils import cocotb_compile_and_run, cocotb_compile_clean + +cnfg_yaml = """ +transfer_layer: + - name : INTF + type : SiSim + init: + host : localhost + port : 12345 + +hw_drivers: + - name : SEQ_GEN + type : seq_gen + interface : INTF + base_addr : 0x0000 + + - name : TDC0 + type : tdc_s3 + interface : INTF + base_addr : 0x8000 + + - name : TDC1 + type : tdc_s3 + interface : INTF + base_addr : 0x8100 + + - name : TDC2 + type : tdc_s3 + interface : INTF + base_addr : 0x8200 + + - name : FIFO0 + type : bram_fifo + interface : INTF + base_addr : 0x8300 + base_data_addr : 0x60000000 + + - name : FIFO1 + type : bram_fifo + interface : INTF + base_addr : 0x8400 + base_data_addr : 0x70000000 + + - name : FIFO2 + type : bram_fifo + interface : INTF + base_addr : 0x8500 + base_data_addr : 0x80000000 + +registers: + - name : SEQ + type : TrackRegister + hw_driver : SEQ_GEN + seq_width : 8 + seq_size : 65535 + tracks : + - name : TDC_TRIGGER_IN + position : 0 + - name : TDC_IN + position : 1 + - name : TDC_ARM + position : 2 + - name : TDC_EXT_EN + position : 3 + - name : S4 + position : 4 + - name : S5 + position : 5 + - name : S6 + position : 6 + - name : S7 + position : 7 +""" + + +class TestSimTdlTdc(unittest.TestCase): + def setUp(self): + cocotb_compile_and_run([os.path.join(os.path.dirname(__file__), 'test_SimTdl_Tdc.v')]) + + self.chip = Dut(cnfg_yaml) + self.chip.init() + + @unittest.skip("Currently not compatible with Icarus Verilog") + def test_tdc(self): + self.chip['TDC0'].ENABLE = 1 + self.chip['TDC0'].EN_TRIGGER_DIST = 1 + self.chip['SEQ'].REPEAT = 1 + + for write_timestamp in range(0, 1): + for index, i in enumerate(range(1045, 1047)): + trigger_dis = 50 + length = i + 1 + trigger_dis + self.chip['SEQ'].SIZE = length + 1 + self.chip['SEQ']['TDC_IN'][trigger_dis:length + trigger_dis] = True + self.chip['SEQ']['TDC_TRIGGER_IN'][0:10] = True + self.chip['SEQ'].write(length) + self.chip['SEQ'].START + while not self.chip['SEQ'].is_ready: + pass + self.assertEqual(self.chip['FIFO0'].get_FIFO_INT_SIZE(), 3 + write_timestamp) + + data = self.chip['FIFO0'].get_data() + data = [self.chip['TDC0'].disassemble_tdc_word(w) for w in data] + self.assertEqual(data[0]['word_type'], 'TRIGGERED') + self.assertEqual(data[1]['word_type'], 'RISING') + self.assertEqual(data[1]['tdc_value'], trigger_dis) + self.assertEqual(data[2]['word_type'], 'FALLING') + self.assertEqual(data[2]['tdc_value'], trigger_dis + i + 1) + if write_timestamp: + self.assertEqual(data[3]['word_type'], 'TIMESTAMP') + self.asserEqual(data[3]['timestamp'], 42) + + @unittest.skip("Currently not compatible with Icarus Verilog") + def test_broadcasting(self): + self.chip['TDC0'].ENABLE = 1 + self.chip['TDC1'].ENABLE = 1 + self.chip['TDC2'].ENABLE = 1 + self.chip['TDC0'].EN_TRIGGER_DIST = 1 + self.chip['TDC1'].EN_TRIGGER_DIST = 1 + self.chip['TDC2'].EN_TRIGGER_DIST = 1 + self.chip['SEQ'].REPEAT = 1 + TDC_TRIG_DIST_MASK = 0x0FF00000 + TDC_VALUE_MASK = 0x00000FFF + + for _, i in enumerate([1045, 1046, 1047]): + offset = 50 # trigger distance + length = i + 1 + offset + self.chip['SEQ_GEN'].SIZE = length + 1 + self.chip['SEQ']['TDC_IN'][offset:length + offset] = True + self.chip['SEQ']['TDC_TRIGGER_IN'][0:10] = True + self.chip['SEQ'].write(length) + self.chip['SEQ'].START + while not self.chip['SEQ_GEN'].is_ready: + pass + self.assertEqual(self.chip['FIFO0'].get_FIFO_INT_SIZE(), 1) + self.assertEqual(self.chip['FIFO1'].get_FIFO_INT_SIZE(), 1) + self.assertEqual(self.chip['FIFO2'].get_FIFO_INT_SIZE(), 1) + + data0 = self.chip['FIFO0'].get_data() + data1 = self.chip['FIFO1'].get_data() + data2 = self.chip['FIFO2'].get_data() + + # Check data from first TDC module + self.assertEqual(data0[0] & TDC_VALUE_MASK, i + 1) # TDC value + self.assertEqual((TDC_TRIG_DIST_MASK & data0[0]) >> 20, offset) # TDC trigger distance + # Check if all TDC gave same data + self.assertEqual(data0[0], data1[0]) # Compare TDC0 with TDC1 + self.assertEqual(data0[0], data2[0]) # Compare TDC0 with TDC2 + + # def test_tdc_delay(self): + # pass + + # def test_tdc_delay_overflow(self): + # pass + + # def test_tdc_delay_late_trigger(self): + # pass + + # def test_tdc_arm(self): + # pass + + def tearDown(self): + self.chip.close() # let it close connection and stop simulator + cocotb_compile_clean() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_SimTdl_Tdc.v b/tests/test_SimTdl_Tdc.v new file mode 100644 index 00000000..cd80d49d --- /dev/null +++ b/tests/test_SimTdl_Tdc.v @@ -0,0 +1,212 @@ +// /** +// * ------------------------------------------------------------ +// * Copyright (c) All rights reserved +// * SiLab, Institute of Physics, University of Bonn +// +// * ------------------------------------------------------------ +// */ + +`timescale 1ps / 1ps + +`include "utils/clock_multiplier.v" +`include "utils/clock_divider.v" + +`include "seq_gen/seq_gen.v" +`include "seq_gen/seq_gen_core.v" +`include "utils/ramb_8_to_n.v" + +`include "tdl_tdc/tdc.v" + +`include "bram_fifo/bram_fifo_core.v" +`include "bram_fifo/bram_fifo.v" + +`include "utils/bus_to_ip.v" + + +module tb ( + input wire BUS_CLK, + input wire BUS_RST, + input wire [31:0] BUS_ADD, + inout wire [31:0] BUS_DATA, + input wire BUS_RD, + input wire BUS_WR, + output wire BUS_BYTE_ACCESS +); + +localparam SEQ_GEN_BASEADDR = 32'h0000; +localparam SEQ_GEN_HIGHADDR = 32'h8000 - 1; + +localparam TDC_BASEADDR = 32'h8000; +localparam TDC_HIGHADDR = 32'h8100 - 1; + +localparam FIFO_BASEADDR = 32'h8300; +localparam FIFO_HIGHADDR = 32'h8400 - 1; + +localparam FIFO_BASEADDR_DATA = 32'h6000_0000; +localparam FIFO_HIGHADDR_DATA = 32'h7000_0000 - 1; + +localparam ABUSWIDTH = 32; +assign BUS_BYTE_ACCESS = BUS_ADD < 32'h6000_0000 ? 1'b1 : 1'b0; + +wire TDC_TRIGGER_IN, TDC_ARM, TDC_EXT_EN; +wire [2:0] TDC_IN; +wire [3:0] NOT_CONNECTED; +wire [7:0] SEQ_OUT; +assign NOT_CONNECTED = SEQ_OUT[7:4]; +assign TDC_TRIGGER_IN = SEQ_OUT[0]; +// Use same TDC for all TDC modules +assign TDC_IN[0] = SEQ_OUT[1]; +assign TDC_IN[1] = SEQ_OUT[1]; +assign TDC_IN[2] = SEQ_OUT[1]; +assign TDC_ARM = SEQ_OUT[2]; +assign TDC_EXT_EN = SEQ_OUT[3]; + +wire CLK_160, CLK_480, CLK_160_TO_DCM, + +DCM #( + .CLKFX_MULTIPLY(20), + .CLKFX_DIVIDE(3) +) i_dcm_1 ( + .CLK0(), .CLK180(), .CLK270(), .CLK2X(), .CLK2X180(), .CLK90(), + .CLKDV(), .CLKFX(CLK_160_TO_DCM), .CLKFX180(), .LOCKED(), .PSDONE(), .STATUS(), + .CLKFB(1'b0), .CLKIN(BUS_CLK), .DSSEN(1'b0), .PSCLK(1'b0), .PSEN(1'b0), .PSINCDEC(1'b0), .RST(1'b0) +); + +DCM #( + .CLKFX_MULTIPLY(3), + .CLKFX_DIVIDE(1), + .CLKDV_DIVIDE(1) +) i_dcm_2 ( + .CLK0(), .CLK180(), .CLK270(), .CLK2X(), .CLK2X180(), .CLK90(), + .CLKDV(CLK_160), .CLKFX(CLK_480), .CLKFX180(), .LOCKED(), .PSDONE(), .STATUS(), + .CLKFB(1'b0), .CLKIN(CLK_160_TO_DCM), .DSSEN(1'b0), .PSCLK(1'b0), .PSEN(1'b0), .PSINCDEC(1'b0), .RST(1'b0) +); + + +seq_gen #( + .BASEADDR(SEQ_GEN_BASEADDR), + .HIGHADDR(SEQ_GEN_HIGHADDR), + .ABUSWIDTH(ABUSWIDTH), + .MEM_BYTES(8 * 8 * 1024), + .OUT_BITS(8) +) i_seq_gen ( + .BUS_CLK(BUS_CLK), + .BUS_RST(BUS_RST), + .BUS_ADD(BUS_ADD), + .BUS_DATA(BUS_DATA[7:0]), + .BUS_RD(BUS_RD), + .BUS_WR(BUS_WR), + .SEQ_EXT_START(), + .SEQ_CLK(CLK_480), + .SEQ_OUT(SEQ_OUT) +); + + +wire [2:0] TDC_FIFO_EMPTY; +wire [31:0] TDC_FIFO_DATA [2:0]; +wire [2:0] TDC_FIFO_READ; +// First TDC module: creates fast sampled trigger signal to use it for other TDC modules. +tdc #( +.BASEADDR(TDC_BASEADDR), +.HIGHADDR(TDC_HIGHADDR), +.ABUSWIDTH(ABUSWIDTH), +.DATA_IDENTIFIER(4'b0000) +) i_tdc ( + .BUS_CLK(BUS_CLK), + .bus_add(BUS_ADD), + .bus_data(BUS_DATA[7:0]), + .bus_rst(BUS_RST), + .bus_wr(BUS_WR), + .bus_rd(BUS_RD), + + .CLK480(CLK480), + .CLK160(CLK160), + .CALIB_CLK(CLK125RX), + .tdc_in(TDC_IN[0]), + .trig_in(TDC_TRIGGER_IN), + + .timestamp(16'd42), + .ext_en(TDC_EXT_EN), + .arm_tdc(TDC_ARM), + .fifo_read(TDC_FIFO_READ[0]), + + .fifo_empty(TDC_FIFO_EMPTY[0]), + .fifo_data(TDC_FIFO_DATA[0]) +); +// Additional TDC modules: Use the fast sampled trigger signal from first TDC module. +genvar i; +generate + for (i = 1; i < 3; i = i + 1) begin: tdc_gen + tdc #( + .BASEADDR(TDC_BASEADDR + 32'h0100*i), + .HIGHADDR(TDC_HIGHADDR + 21'h0100*i), + .ABUSWIDTH(ABUSWIDTH), + .DATA_IDENTIFIER(4'b0000) + ) i_tdc ( + .BUS_CLK(BUS_CLK), + .bus_add(BUS_ADD), + .bus_data(BUS_DATA[7:0]), + .bus_rst(BUS_RST), + .bus_wr(BUS_WR), + .bus_rd(BUS_RD), + + .CLK480(CLK480), + .CLK160(CLK160), + .CALIB_CLK(CLK125RX), + .tdc_in(TDC_IN[i]), + .trig_in(TDC_TRIGGER_IN), + + .timestamp(16'd42), + .ext_en(TDC_EXT_EN), + .arm_tdc(TDC_ARM), + .fifo_read(TDC_FIFO_READ[i]), + + .fifo_empty(TDC_FIFO_EMPTY[i]), + .fifo_data(TDC_FIFO_DATA[i]) + ); + end +endgenerate + + +wire FIFO_READ [2:0], FIFO_EMPTY [2:0], FIFO_FULL [2:0]; +wire [31:0] FIFO_DATA [2:0]; +genvar k; +generate + for (k = 0; k < 3; k = k + 1) begin: bram_fifo_gen + assign FIFO_DATA[k] = TDC_FIFO_DATA[k]; + assign FIFO_EMPTY[k] = TDC_FIFO_EMPTY[k]; + assign TDC_FIFO_READ[k] = FIFO_READ[k]; + + bram_fifo #( + .BASEADDR(FIFO_BASEADDR + 32'h0100*k), + .HIGHADDR(FIFO_HIGHADDR + 32'h0100*k), + .BASEADDR_DATA(FIFO_BASEADDR_DATA + 32'h1000_0000*k), + .HIGHADDR_DATA(FIFO_HIGHADDR_DATA + 32'h1000_0000*k), + .ABUSWIDTH(ABUSWIDTH) + ) i_out_fifo ( + .BUS_CLK(BUS_CLK), + .BUS_RST(BUS_RST), + .BUS_ADD(BUS_ADD), + .BUS_DATA(BUS_DATA), + .BUS_RD(BUS_RD), + .BUS_WR(BUS_WR), + + .FIFO_READ_NEXT_OUT(FIFO_READ[k]), + .FIFO_EMPTY_IN(FIFO_EMPTY[k]), + .FIFO_DATA(FIFO_DATA[k]), + + .FIFO_NOT_EMPTY(), + .FIFO_FULL(FIFO_FULL[k]), + .FIFO_NEAR_FULL(), + .FIFO_READ_ERROR() + ); + end +endgenerate + + +initial begin + $dumpfile("tdc.vcd"); + $dumpvars(0); +end + +endmodule