diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index 3f9760a..5c0d3a9 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -28,4 +28,4 @@ jobs: - name: Install flake8 run: pip install flake8 - name: Check code - run: while read in; do flake8 --extend-ignore E501,W503 "$in"; done < <(git diff --name-only --diff-filter=ACMTUXB origin/master -- '*.py') + run: while read in; do flake8 --extend-ignore E501,W503 "$in"; done < <(git diff --name-only --diff-filter=ACMTUXB origin/development -- '*.py') diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b9c6b65..9639db8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ on: jobs: tests: name: Python ${{matrix.python-version}} | ${{matrix.sim}} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: SIM: ${{matrix.sim}} strategy: @@ -20,7 +20,7 @@ jobs: matrix: include: - sim: verilator - sim-version: v4.106 + sim-version: v5.020 python-version: '3.10' steps: - uses: actions/checkout@v3 @@ -45,7 +45,7 @@ jobs: - name: Install Verilator if: matrix.sim == 'verilator' run: | - sudo apt install -y --no-install-recommends make g++ perl python3 autoconf flex bison libfl2 libfl-dev zlibc zlib1g zlib1g-dev + sudo apt install -y --no-install-recommends make g++ help2man perl python3 autoconf flex bison libfl2 libfl-dev zlib1g zlib1g-dev git clone https://github.com/verilator/verilator.git -b ${{matrix.sim-version}} cd verilator autoconf diff --git a/firmware/src/tjmonopix2_core.v b/firmware/src/tjmonopix2_core.v index 1ebfeb5..d8de2bc 100644 --- a/firmware/src/tjmonopix2_core.v +++ b/firmware/src/tjmonopix2_core.v @@ -533,7 +533,8 @@ rrp_arbiter .DATA_IN({ RX_FIFO_DATA, TLU_FIFO_DATA, - TDC_FIFO_DATA}), + TDC_FIFO_DATA + }), .READ_GRANT({ RX_FIFO_READ, TLU_FIFO_READ, @@ -612,7 +613,7 @@ pulse_gen #( // ----- TDC ----- // localparam CLKDV = 4; // division factor from 160 MHz clock to DV_CLK (here 40 MHz) -wire [CLKDV * 4 - 1:0] FAST_TRIGGER_OUT; +// wire [CLKDV * 4 - 1:0] FAST_TRIGGER_OUT; // wire LEMO_RX0_FROM_TDC; // wire HITOR_FROM_TDC; @@ -624,12 +625,12 @@ tdc_s3 #( .DATA_IDENTIFIER(4'b0010), .FAST_TDC(1), .FAST_TRIGGER(1), - .BROADCAST(0) // generate for first TDC module the 640MHz sampled trigger signal and share it with other modules using TRIGGER input + .BROADCAST(0) // generate for LVDS TDC module the 640MHz sampled trigger signal and share it with other modules using TRIGGER input ) i_tdc ( .CLK320(CLK320), // 320 MHz .CLK160(CLK160), // 160 MHz .DV_CLK(CLK40), // 40 MHz - .TDC_IN(LVDS_HITOR), // HITOR + .TDC_IN(LVDS_HITOR), // LVDS HITOR (DP) .TDC_OUT(), .TRIG_IN(LEMO_RX[0]), .TRIG_OUT(), diff --git a/tjmonopix2/analysis/analysis.py b/tjmonopix2/analysis/analysis.py index f850776..21fbbed 100644 --- a/tjmonopix2/analysis/analysis.py +++ b/tjmonopix2/analysis/analysis.py @@ -19,7 +19,7 @@ class Analysis(object): - def __init__(self, raw_data_file=None, analyzed_data_file=None, + def __init__(self, raw_data_file=None, analyzed_data_file=None, tot_calib_file=None, store_hits=True, cluster_hits=False, analyze_tdc=False, use_tdc_trigger_dist=False, build_events=False, chunk_size=1000000, **_): self.log = logger.setup_derived_logger('Analysis') @@ -32,6 +32,7 @@ def __init__(self, raw_data_file=None, analyzed_data_file=None, self.chunk_size = chunk_size self.analyze_tdc = analyze_tdc self.use_tdc_trigger_dist = use_tdc_trigger_dist + self.tot_calib_file = tot_calib_file if self.build_events: self.cluster_hits = True @@ -62,6 +63,7 @@ def _get_configs(self): self.scan_config = au.ConfigDict(in_file.root.configuration_in.scan.scan_config[:]) self.chip_settings = au.ConfigDict(in_file.root.configuration_in.chip.settings[:]) self.tlu_config = au.ConfigDict(in_file.root.configuration_in.bench.TLU[:]) + self.tdc_config = au.ConfigDict(in_file.root.configuration_in.bench.TDC[:]) def __enter__(self): return self @@ -156,7 +158,7 @@ def _setup_clusterizer(self): ('frame', 'u1'), ('column', 'u2'), ('row', 'u2'), - ('charge', 'u1'), + ('charge', 'u2'), ('timestamp', 'i8')] cluster_fields = {'event_number': 'event_number', 'column': 'column', @@ -172,7 +174,7 @@ def _setup_clusterizer(self): cluster_description = [('event_number', 'u4'), ('id', ' 0, cluster['cluster_shape'] < 300) cs_shape = np.bincount(cluster['cluster_shape'][sel], minlength=300)[:300] # Add to total hists @@ -360,13 +388,13 @@ def analyze_data(self): pbar.update(upd) pbar.close() - hist_occ, hist_tot, hist_tdc = interpreter.get_histograms() + hist_occ, hist_tot, hist_tdc, hist_trigger_dist = interpreter.get_histograms() - self._create_additional_hit_data(hist_occ, hist_tot) + self._create_additional_hit_data(hist_occ, hist_tot, hist_tdc, hist_trigger_dist) if self.cluster_hits: self._create_additional_cluster_data(hist_cs_size, hist_cs_tot, hist_cs_shape) - def _create_additional_hit_data(self, hist_occ, hist_tot): + def _create_additional_hit_data(self, hist_occ, hist_tot, hist_tdc, hist_trigger_dist): with tb.open_file(self.analyzed_data_file, 'r+') as out_file: scan_id = self.run_config['scan_id'] @@ -385,7 +413,23 @@ def _create_additional_hit_data(self, hist_occ, hist_tot): complevel=5, fletcher32=False)) - # if self.analyze_tdc: # Only store if TDC analysis is used. + if self.analyze_tdc: # Only store if TDC analysis is used. + out_file.create_carray(out_file.root, + name='HistTdc', + title='TDC Histogram', + obj=hist_tdc, + filters=tb.Filters(complib='blosc', + complevel=5, + fletcher32=False)) + + out_file.create_carray(out_file.root, + name='HistTriggerDist', + title='Trigger Dist Histogram', + obj=hist_trigger_dist, + filters=tb.Filters(complib='blosc', + complevel=5, + fletcher32=False)) + # out_file.create_carray(out_file.root, # name='HistTdcStatus', # title='Tdc status Histogram', @@ -394,11 +438,11 @@ def _create_additional_hit_data(self, hist_occ, hist_tot): # complevel=5, # fletcher32=False)) - if scan_id in ['threshold_scan']: + if scan_id in ['threshold_scan', 'calibrate_tot']: n_injections = self.scan_config['n_injections'] hist_scurve = hist_occ.reshape((self.rows * self.columns, -1)) - if scan_id in ['threshold_scan']: + if scan_id in ['threshold_scan', 'calibrate_tot']: scan_params = [self.scan_config['VCAL_HIGH'] - v for v in range(self.scan_config['VCAL_LOW_start'], self.scan_config['VCAL_LOW_stop'], self.scan_config['VCAL_LOW_step'])] self.threshold_map, self.noise_map, self.chi2_map = au.fit_scurves_multithread(hist_scurve, scan_params, n_injections, optimize_fit_range=False) diff --git a/tjmonopix2/analysis/analysis_utils.py b/tjmonopix2/analysis/analysis_utils.py index 9d0be87..47f0d20 100644 --- a/tjmonopix2/analysis/analysis_utils.py +++ b/tjmonopix2/analysis/analysis_utils.py @@ -35,7 +35,7 @@ ("frame", " 0]) == 0: + return (0., 0., 0., 0.) + + p0 = [40, 0.005, 0.1] + + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", OptimizeWarning) + popt = curve_fit(f=lambda x, a, b, d: _tot_response_func(x, a, b, d), + xdata=x[y > 0], ydata=y[y > 0], p0=p0, sigma=yerr[y > 0], + absolute_sigma=True)[0] + chi2 = np.sum((y - _tot_response_func(x, *popt)) ** 2) + except RuntimeError: # fit failed + return (0., 0., 0., 0.) + + return (*popt, chi2 / (y.shape[0] - 3 - 1)) diff --git a/tjmonopix2/analysis/interpreter.py b/tjmonopix2/analysis/interpreter.py index eafcf75..9114a9a 100644 --- a/tjmonopix2/analysis/interpreter.py +++ b/tjmonopix2/analysis/interpreter.py @@ -12,12 +12,15 @@ ('le', numba.int8), ('te', numba.int8), ('tj_timestamp', numba.int64), + ('n_scan_params', numba.int32), ('trigger_data_format', numba.uint8), + ('en_trigger_dist', numba.uint8), ('hist_occ', numba.uint32[:, :, :]), ('hist_tot', numba.uint16[:, :, :, :]), ('hist_tdc', numba.uint32[:]), + ('hist_trigger_dist', numba.uint32[:]), ('n_triggers', numba.int64), ('n_tdc', numba.int64), ] @@ -59,13 +62,26 @@ def get_tlu_word(word, trigger_data_format): @numba.njit -def get_tdc_value(word): - return word & 0xFFF +def get_tdc_word(word, en_trigger_dist): + """Get TDC module data depending on data format + + Args: + word (int): raw data word + en_trigger_dist (bool): TDC module data format to record trigger distance + + Returns: + (tuple): triggerdist, timestamp/event counter (interpretation by user), tdc value + """ + + if not en_trigger_dist: + return 255, (word & 0xFFFF000) >> 12, word & 0xFFF + else: + return (word & 0xFF00000) >> 20, (word & 0xFF000) >> 12, word & 0xFFF @numba.experimental.jitclass(class_spec) class RawDataInterpreter(object): - def __init__(self, n_scan_params=1, trigger_data_format=1): + def __init__(self, n_scan_params=1, trigger_data_format=1, en_trigger_dist=False): self.sof = False self.eof = False self.error_cnt = 0 @@ -74,6 +90,7 @@ def __init__(self, n_scan_params=1, trigger_data_format=1): self.n_scan_params = n_scan_params self.trigger_data_format = trigger_data_format + self.en_trigger_dist = en_trigger_dist self.n_triggers = 0 self.n_tdc = 0 @@ -168,18 +185,19 @@ def interpret(self, raw_data, hit_data, scan_param_id=0): # Part 3: interpret TDC word # ############################## elif is_tdc(raw_data_word): - tdc_value = get_tdc_value(raw_data_word) + trigger_dist, tdc_timestamp, tdc_value = get_tdc_word(raw_data_word, self.en_trigger_dist) hit_data[hit_index]["col"] = 0x3FE # 1022 as TDC identifier hit_data[hit_index]["row"] = 0 hit_data[hit_index]["le"] = 0 - hit_data[hit_index]["te"] = 0 + hit_data[hit_index]["te"] = trigger_dist hit_data[hit_index]["token_id"] = tdc_value - hit_data[hit_index]["timestamp"] = 0 + hit_data[hit_index]["timestamp"] = tdc_timestamp hit_data[hit_index]["scan_param_id"] = scan_param_id self.n_tdc += 1 self.hist_tdc[tdc_value] += 1 + self.hist_trigger_dist[trigger_dist] += 1 # Prepare for next data block. Increase hit index hit_index += 1 @@ -189,7 +207,7 @@ def interpret(self, raw_data, hit_data, scan_param_id=0): return hit_data def get_histograms(self): - return self.hist_occ, self.hist_tot, self.hist_tdc + return self.hist_occ, self.hist_tot, self.hist_tdc, self.hist_trigger_dist def get_n_triggers(self): return self.n_triggers @@ -201,6 +219,7 @@ def reset(self): self.hist_occ = np.zeros((512, 512, self.n_scan_params), dtype=numba.uint32) self.hist_tot = np.zeros((512, 512, self.n_scan_params, 128), dtype=numba.uint16) self.hist_tdc = np.zeros(4096, dtype=numba.uint32) + self.hist_trigger_dist = np.zeros(256, dtype=numba.uint32) self.n_triggers = 0 self.n_tdc = 0 diff --git a/tjmonopix2/analysis/plotting.py b/tjmonopix2/analysis/plotting.py index 350ad36..5d1e1d7 100644 --- a/tjmonopix2/analysis/plotting.py +++ b/tjmonopix2/analysis/plotting.py @@ -115,7 +115,7 @@ def __init__(self, analyzed_data_file, pdf_file=None, level='preliminary', mask_ self.HistTdcStatus = None self.HistOcc = root.HistOcc[:] self.HistTot = root.HistTot[:] - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'global_threshold_tuning', 'in_time_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'calibrate_tot', 'fast_threshold_scan', 'global_threshold_tuning', 'in_time_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan']: self.ThresholdMap = root.ThresholdMap[:, :] self.Chi2Map = root.Chi2Map[:, :] self.Chi2Sel = (self.Chi2Map > 0) & (self.Chi2Map < SCURVE_CHI2_UPPER_LIMIT) & (~self.enable_mask) @@ -131,7 +131,7 @@ def __init__(self, analyzed_data_file, pdf_file=None, level='preliminary', mask_ self.log.warning('Disabled {} noisy pixels in total.'.format(len(noisy_pixels[0]))) try: - _ = root.Cluster[:] # FIXME: This line of code does not take chunking into account + _ = root.Cluster self.HistClusterSize = root.HistClusterSize[:] self.HistClusterShape = root.HistClusterShape[:] self.HistClusterTot = root.HistClusterTot[:] @@ -139,6 +139,19 @@ def __init__(self, analyzed_data_file, pdf_file=None, level='preliminary', mask_ except tb.NoSuchNodeError: pass + try: + conversion_factors = au.ConfigDict(root.configuration_in.bench.electron_conversion[:]) + if self.scan_config['start_column'] in range(0, 448) and self.scan_config['stop_column'] in range(0, 448): # TODO: Get rid of hardcoded values. + self.electron_conversion = conversion_factors['DC_coupled'] + elif self.scan_config['start_column'] in range(448, 512) and self.scan_config['stop_column'] in range(448, 512): + self.electron_conversion = conversion_factors['AC_coupled'] + else: + self.log.warning('Both DC and AC coupled pixels enabled. Choose ambiguous electron conversion factor of DC falvors!') + self.electron_conversion = conversion_factors['DC_coupled'] + self.plot_electron_axis = True + except tb.NoSuchNodeError: + self.plot_electron_axis = False + try: in_file.close() except Exception: @@ -164,14 +177,14 @@ def create_standard_plots(self): else: self.create_parameter_page() self.create_occupancy_map() - if self.run_config['scan_id'] in ['simple_scan']: + if self.run_config['scan_id'] in ['source_scan', 'ext_trigger_scan']: self.create_fancy_occupancy() - if self.run_config['scan_id'] in ['analog_scan', 'threshold_scan', 'global_threshold_tuning', 'simple_scan']: + if self.run_config['scan_id'] in ['analog_scan', 'threshold_scan', 'global_threshold_tuning', 'source_scan', 'ext_trigger_scan', 'calibrate_tot']: self.create_hit_pix_plot() self.create_tdac_plot() self.create_tdac_map() self.create_tot_plot() - if self.run_config['scan_id'] in ['threshold_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'calibrate_tot']: self.create_tot_hist() self.create_scurves_plot() self.create_threshold_plot() @@ -254,20 +267,20 @@ def create_scurves_plot(self, scan_parameter_name='Scan parameter'): try: if self.run_config['scan_id'] == 'injection_delay_scan': scan_parameter_name = 'Finedelay [LSB]' - electron_axis = False + plot_electron_axis = False scan_parameter_range = range(0, 16) elif self.run_config['scan_id'] == 'global_threshold_tuning': scan_parameter_name = self.scan_config['VTH_name'] - electron_axis = False + plot_electron_axis = False scan_parameter_range = self.scan_parameter_range else: scan_parameter_name = '$\\Delta$ VCAL' - electron_axis = False # TODO: True after calibration code is done scan_parameter_range = self.scan_parameter_range + plot_electron_axis = self.plot_electron_axis params = [{'scurves': self.HistOcc[:].ravel().reshape((self.rows * self.cols, -1)).T, 'scan_parameters': scan_parameter_range, - 'electron_axis': electron_axis, + 'electron_axis': plot_electron_axis, 'scan_parameter_name': scan_parameter_name}] for param in params: @@ -280,21 +293,21 @@ def create_threshold_plot(self, logscale=False, scan_parameter_name='Scan parame title = 'Threshold distribution for enabled pixels' if self.run_config['scan_id'] == 'injection_delay_scan': scan_parameter_name = 'Finedelay [LSB]' - electron_axis = False + plot_electron_axis = False plot_range = range(0, 16) title = 'Fine delay distribution for enabled pixels' elif self.run_config['scan_id'] == 'global_threshold_tuning': plot_range = self.scan_parameter_range scan_parameter_name = self.scan_config['VTH_name'] - electron_axis = False + plot_electron_axis = False else: plot_range = self.scan_parameter_range scan_parameter_name = '$\\Delta$ VCAL' - electron_axis = False # TODO: True after calibration code is done + plot_electron_axis = self.plot_electron_axis self._plot_distribution(self.ThresholdMap[self.Chi2Sel].T, plot_range=plot_range, - electron_axis=electron_axis, + electron_axis=plot_electron_axis, x_axis_title=scan_parameter_name, title=title, log_y=logscale, @@ -311,15 +324,15 @@ def create_stacked_threshold_plot(self, scan_parameter_name='Scan parameter'): plot_range = self.scan_parameter_range if self.run_config['scan_id'] == 'global_threshold_tuning': scan_parameter_name = self.scan_config['VTH_name'] - electron_axis = False + plot_electron_axis = False else: scan_parameter_name = '$\\Delta$ VCAL' - electron_axis = False # TODO: True after calibration code is done + plot_electron_axis = self.plot_electron_axis self._plot_stacked_threshold(data=self.ThresholdMap[self.Chi2Sel].T, tdac_mask=self.tdac_mask[self.Chi2Sel].T, plot_range=plot_range, - electron_axis=electron_axis, + electron_axis=plot_electron_axis, x_axis_title=scan_parameter_name, y_axis_title='# of pixels', title='Threshold distribution for enabled pixels', @@ -336,22 +349,22 @@ def create_threshold_map(self): sel = self.Chi2Map[:] > 0. # Mask not converged fits (chi2 = 0) mask[~sel] = True if self.run_config['scan_id'] == 'injection_delay_scan': - electron_axis = False + plot_electron_axis = False use_electron_offset = False z_label = 'Finedelay [LSB]' title = 'Injection Delay' z_min = 0 z_max = 16 else: - electron_axis = False # TODO: True after calibration code is done - use_electron_offset = False # TODO: True after calibration code is done + plot_electron_axis = self.plot_electron_axis + use_electron_offset = False z_label = 'Threshold' title = 'Threshold' z_min = None z_max = None self._plot_occupancy(hist=np.ma.masked_array(self.ThresholdMap, mask).T, - electron_axis=electron_axis, + electron_axis=plot_electron_axis, z_label=z_label, title=title, use_electron_offset=use_electron_offset, @@ -371,18 +384,18 @@ def create_noise_plot(self, logscale=False, scan_parameter_name='Scan parameter' plot_range = None if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'in_time_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan']: scan_parameter_name = '$\\Delta$ VCAL' - electron_axis = False # TODO: True after calibration code is done + plot_electron_axis = self.plot_electron_axis elif self.run_config['scan_id'] == 'global_threshold_tuning': scan_parameter_name = self.scan_config['VTH_name'] - electron_axis = False + plot_electron_axis = False elif self.run_config['scan_id'] == 'injection_delay_scan': - electron_axis = False scan_parameter_name = 'Finedelay [LSB]' + plot_electron_axis = False self._plot_distribution(np.ma.masked_array(self.NoiseMap, mask).T, title='Noise distribution for enabled pixels', plot_range=plot_range, - electron_axis=electron_axis, + electron_axis=plot_electron_axis, use_electron_offset=False, x_axis_title=scan_parameter_name, y_axis_title='# of pixels', @@ -399,14 +412,14 @@ def create_noise_map(self): mask[~sel] = True z_label = 'Noise' title = 'Noise' - electron_axis = False # TODO: True after calibration code is done + plot_electron_axis = self.plot_electron_axis if self.run_config['scan_id'] == 'injection_delay_scan': z_label = 'Finedelay [LSB]' title = 'Injection Delay Noise' - electron_axis = False + plot_electron_axis = False self._plot_occupancy(hist=np.ma.masked_array(self.NoiseMap, mask).T, - electron_axis=electron_axis, + electron_axis=plot_electron_axis, use_electron_offset=False, z_label=z_label, z_max='median', @@ -479,9 +492,16 @@ def create_cluster_tot_plot(self): else: plot_range = range(0, np.max(np.nonzero(self.HistClusterTot))) + tot_calib_file = self.configuration['scan'].get('tot_calib_file', None) + if tot_calib_file is not None: + x_axis_title = 'Cluster charge [e⁻]' + plot_range = plot_range * self.electron_conversion + else: + x_axis_title = 'Cluster ToT [25 ns]' + self._plot_1d_hist(hist=self.HistClusterTot[:], title='Cluster ToT', log_y=False, plot_range=plot_range, - x_axis_title='Cluster ToT [25 ns]', + x_axis_title=x_axis_title, y_axis_title='# of clusters', suffix='cluster_tot') except Exception: self.log.error('Could not create cluster TOT plot!') @@ -550,16 +570,16 @@ def _add_text(self, fig): if self.internal: fig.text(0.1, 1, 'Internal', fontsize=16, color='r', rotation=45, bbox=dict(boxstyle='round', facecolor='white', edgecolor='red', alpha=0.7), transform=fig.transFigure) - def _convert_to_e(self, dac, use_offset=True): + def _convert_to_e(self, dac, use_offset=False): if use_offset: e = dac * self.calibration['e_conversion_slope'] + self.calibration['e_conversion_offset'] de = math.sqrt((dac * self.calibration['e_conversion_slope_error'])**2 + self.calibration['e_conversion_offset_error']**2) else: - e = dac * self.calibration['e_conversion_slope'] - de = dac * self.calibration['e_conversion_slope_error'] + e = dac * self.electron_conversion + de = dac * self.electron_conversion * 0.15 # TODO: Assume generic 15% error of conversion factor, need precise measurement. return e, de - def _add_electron_axis(self, fig, ax, use_electron_offset=True): + def _add_electron_axis(self, fig, ax, use_electron_offset=False): fig.subplots_adjust(top=0.75) ax.title.set_position([.5, 1.15]) @@ -671,7 +691,7 @@ def _plot_parameter_page(self): self._save_plots(fig, suffix='parameter_page') - def _plot_occupancy(self, hist, electron_axis=False, use_electron_offset=True, title='Occupancy', z_label='# of hits', z_min=None, z_max=None, show_sum=True, suffix=None, extend_upper_bound=True): + def _plot_occupancy(self, hist, electron_axis=False, use_electron_offset=False, title='Occupancy', z_label='# of hits', z_min=None, z_max=None, show_sum=True, suffix=None, extend_upper_bound=True): if z_max == 'median': z_max = 2 * np.ma.median(hist) elif z_max == 'maximum': @@ -1054,7 +1074,7 @@ def _plot_stacked_threshold(self, data, tdac_mask, plot_range=None, electron_axi self._save_plots(fig, suffix=suffix) - def _plot_distribution(self, data, plot_range=None, x_axis_title=None, electron_axis=False, use_electron_offset=True, y_axis_title='# of hits', log_y=False, align='edge', title=None, print_failed_fits=False, fit_gauss=True, plot_legend=True, suffix=None): + def _plot_distribution(self, data, plot_range=None, x_axis_title=None, electron_axis=False, use_electron_offset=False, y_axis_title='# of hits', log_y=False, align='edge', title=None, print_failed_fits=False, fit_gauss=True, plot_legend=True, suffix=None): if plot_range is None: diff = np.amax(data) - np.amin(data) median = np.ma.median(data) diff --git a/tjmonopix2/online_monitor/tjmonopix2_inter.py b/tjmonopix2/online_monitor/tjmonopix2_inter.py index d415163..b232e8b 100644 --- a/tjmonopix2/online_monitor/tjmonopix2_inter.py +++ b/tjmonopix2/online_monitor/tjmonopix2_inter.py @@ -96,7 +96,7 @@ def interpret_data(self, data): self.total_trigger_words = n_triggers self.readout += 1 - self.hist_occ, self.hist_tot, self.hist_tdc = self.interpreter.get_histograms() + self.hist_occ, self.hist_tot, self.hist_tdc, _ = self.interpreter.get_histograms() occupancy_hist = self.hist_occ.sum(axis=2) # Mask noisy pixels diff --git a/tjmonopix2/online_monitor/tjmonopix2_recv.py b/tjmonopix2/online_monitor/tjmonopix2_recv.py index a58232b..757b8b7 100644 --- a/tjmonopix2/online_monitor/tjmonopix2_recv.py +++ b/tjmonopix2/online_monitor/tjmonopix2_recv.py @@ -27,9 +27,9 @@ def setup_widgets(self, parent, name): dock_tot = Dock("Time over threshold values (TOT)", size=(400, 400)) dock_tdc = Dock("TDC", size=(400, 400)) dock_status = Dock("Status", size=(800, 40)) - dock_area.addDock(dock_occcupancy, 'top') - dock_area.addDock(dock_tot, 'bottom', dock_occcupancy) - dock_area.addDock(dock_tdc, 'right', dock_tot) + dock_area.addDock(dock_occcupancy, 'left') + dock_area.addDock(dock_tot, 'right', dock_occcupancy) + dock_area.addDock(dock_tdc, 'bottom', dock_tot) dock_area.addDock(dock_status, 'top') # Status dock on top @@ -109,14 +109,14 @@ def handle_data(self, data): self.occupancy_data = data['occupancy'] self.tot_data = data['tot_hist'] self.tdc_data = data['tdc_hist'] - + # Meta data self._update_rate(data['meta_data']['fps'], data['meta_data']['hps'], data['meta_data']['tps'], data['meta_data']['total_hits'], data['meta_data']['total_triggers']) - + self.timestamp_label.setText("Data Timestamp\n%s" % time.asctime(time.localtime(data['meta_data']['timestamp_stop']))) self.scan_parameter_label.setText("Parameter ID\n%d" % data['meta_data']['scan_param_id']) now = time.time() @@ -124,7 +124,7 @@ def handle_data(self, data): self.plot_delay_label.setText("Plot Delay\n%s" % 'not realtime' if abs(self.plot_delay) > 5 else "Plot Delay\n%1.2f ms" % (self.plot_delay * 1.e3)) def refresh_data(self): - + if self.occupancy_data is not None: self.occupancy_img.setImage(self.occupancy_data[:, :], autoDownSample=True) if self.tot_data is not None: @@ -136,4 +136,4 @@ def refresh_data(self): y=self.tdc_data, # stepMode=True, fillLevel=0, - brush=(0, 0, 255, 150)) \ No newline at end of file + brush=(0, 0, 255, 150)) diff --git a/tjmonopix2/scans/calibrate_tot.py b/tjmonopix2/scans/calibrate_tot.py new file mode 100644 index 0000000..d1015f4 --- /dev/null +++ b/tjmonopix2/scans/calibrate_tot.py @@ -0,0 +1,87 @@ +# +# ------------------------------------------------------------ +# Copyright (c) All rights reserved +# SiLab, Institute of Physics, University of Bonn +# ------------------------------------------------------------ +# + +import numpy as np +import tables as tb + +from numba import njit + +from tjmonopix2.analysis import analysis_utils as au +from tjmonopix2.analysis import analysis, plotting +from tjmonopix2.scans.scan_threshold import ThresholdScan + +scan_configuration = { + 'start_column': 0, + 'stop_column': 32, + 'start_row': 0, + 'stop_row': 512, + + 'n_injections': 100, + 'VCAL_HIGH': 145, + 'VCAL_LOW_start': 130, + 'VCAL_LOW_stop': 0, + 'VCAL_LOW_step': -1, +} + + +@njit +def _calc_mean_avg(array, weights): + return np.sum(array * weights) / np.sum(weights) + + +@njit +def _create_tot_avg(array): + original_shape = array.shape + array = array.reshape((original_shape[0] * original_shape[1], original_shape[2], original_shape[3])) + tot_avg = np.zeros((array.shape[0], array.shape[1])) + + # loop through all pixels + for pixel in range(array.shape[0]): + for idx in range(array.shape[1]): + if array[pixel, idx].any() != 0: + tot_avg[pixel, idx] = _calc_mean_avg(np.arange(0, 128, 1), array[pixel, idx]) + + return np.reshape(tot_avg, (original_shape[0], original_shape[1], original_shape[2])) + + +class CalibrateToT(ThresholdScan): + scan_id = 'calibrate_tot' + + def _analyze(self): + with analysis.Analysis(raw_data_file=self.output_filename + '.h5', **self.configuration['bench']['analysis']) as a: + a.analyze_data() + + self.log.info("Calibrating ToT...") + + analyzed_data_file = self.output_filename + '_interpreted.h5' + with tb.open_file(analyzed_data_file, 'r') as in_file: + HistTot = in_file.root.HistTot[:, :] + scan_params = in_file.root.configuration_in.scan.scan_params[:] + + scan_parameter_range = np.array(scan_params['vcal_high'] - scan_params['vcal_low'], dtype=float) + tot_avg = _create_tot_avg(HistTot) + inj_tot_cal = au.fit_tot_inj_multithread(tot_avg=tot_avg.reshape(512 * 512, -1), scan_params=scan_parameter_range) + + self.log.success("{0} pixels with successful ToT calibration".format(int(np.count_nonzero(inj_tot_cal[:, :]) / 4))) + + with tb.open_file(analyzed_data_file, 'r+') as out_file: + out_file.create_carray(out_file.root, + name='InjTotCalibration', + title='Injection Tot Calibration Fit', + obj=inj_tot_cal, + filters=tb.Filters(complib='blosc', + complevel=5, + fletcher32=False)) + + if self.configuration['bench']['analysis']['create_pdf']: + with plotting.Plotting(analyzed_data_file=a.analyzed_data_file) as p: + p.create_standard_plots() + + +if __name__ == "__main__": + with CalibrateToT(scan_config=scan_configuration) as scan: + scan.start() diff --git a/tjmonopix2/scans/scan_simple.py b/tjmonopix2/scans/scan_ext_trigger.py similarity index 78% rename from tjmonopix2/scans/scan_simple.py rename to tjmonopix2/scans/scan_ext_trigger.py index 08bfe09..a7b3974 100644 --- a/tjmonopix2/scans/scan_simple.py +++ b/tjmonopix2/scans/scan_ext_trigger.py @@ -20,15 +20,19 @@ 'scan_timeout': False, # Timeout for scan after which the scan will be stopped, in seconds; if False no limit on scan time 'max_triggers': 1000000, # Number of maximum received triggers after stopping readout, if False no limit on received trigger + + 'tot_calib_file': None # path to ToT calibration file for charge to e⁻ conversion, if None no conversion will be done } -class SimpleScan(ScanBase): - scan_id = 'simple_scan' +class ExtTriggerScan(ScanBase): + scan_id = 'ext_trigger_scan' stop_scan = threading.Event() - def _configure(self, scan_timeout=10, max_triggers=False, start_column=0, stop_column=512, start_row=0, stop_row=512, **_): + def _configure(self, scan_timeout=False, max_triggers=1000, start_column=0, stop_column=512, start_row=0, stop_row=512, **_): + self.log.info('External trigger scan needs TLU running!') + if scan_timeout and max_triggers: self.log.warning('You should only use one of the stop conditions at a time.') @@ -37,9 +41,10 @@ def _configure(self, scan_timeout=10, max_triggers=False, start_column=0, stop_c self.chip.masks.update() self.daq.configure_tlu_veto_pulse(veto_length=500) - self.daq.configure_tlu_module(max_triggers=max_triggers) + if max_triggers: + self.daq.configure_tlu_module(max_triggers=max_triggers) - def _scan(self, scan_timeout=10, max_triggers=False, **_): + def _scan(self, scan_timeout=False, max_triggers=1000, **_): def timed_out(): if scan_timeout: current_time = time.time() @@ -83,13 +88,15 @@ def timed_out(): self.log.info('Scan was stopped due to keyboard interrupt') self.pbar.close() - if max_triggers: - self.daq.disable_tlu_module() - + self.daq.disable_tlu_module() self.log.success('Scan finished') def _analyze(self): - with analysis.Analysis(raw_data_file=self.output_filename + '.h5', **self.configuration['bench']['analysis']) as a: + tot_calib_file = self.configuration['scan'].get('tot_calib_file', None) + if tot_calib_file is not None: + self.configuration['bench']['analysis']['cluster_hits'] = True + + with analysis.Analysis(raw_data_file=self.output_filename + '.h5', tot_calib_file=tot_calib_file, **self.configuration['bench']['analysis']) as a: a.analyze_data() if self.configuration['bench']['analysis']['create_pdf']: @@ -98,5 +105,5 @@ def _analyze(self): if __name__ == "__main__": - with SimpleScan(scan_config=scan_configuration) as scan: + with ExtTriggerScan(scan_config=scan_configuration) as scan: scan.start() diff --git a/tjmonopix2/scans/scan_source.py b/tjmonopix2/scans/scan_source.py new file mode 100644 index 0000000..1eda3a7 --- /dev/null +++ b/tjmonopix2/scans/scan_source.py @@ -0,0 +1,84 @@ +# +# ------------------------------------------------------------ +# Copyright (c) All rights reserved +# SiLab, Institute of Physics, University of Bonn +# ------------------------------------------------------------ +# + +import time +import threading +from tqdm import tqdm + +from tjmonopix2.analysis import analysis, plotting +from tjmonopix2.system.scan_base import ScanBase + +scan_configuration = { + 'start_column': 0, + 'stop_column': 224, + 'start_row': 0, + 'stop_row': 512, + + 'scan_timeout': 30, # Timeout for scan after which the scan will be stopped, in seconds; if False no limit on scan time + + 'tot_calib_file': None # path to ToT calibration file for charge to e⁻ conversion, if None no conversion will be done +} + + +class SourceScan(ScanBase): + scan_id = 'source_scan' + + stop_scan = threading.Event() + + def _configure(self, start_column=0, stop_column=512, start_row=0, stop_row=512, **_): + self.chip.masks['enable'][start_column:stop_column, start_row:stop_row] = True + self.chip.masks.apply_disable_mask() + self.chip.masks.update() + + def _scan(self, scan_timeout=10, **_): + def timed_out(): + if scan_timeout: + current_time = time.time() + if current_time - start_time > scan_timeout: + self.log.info('Scan timeout was reached') + return True + return False + + self.pbar = tqdm(total=scan_timeout, unit='') # [s] + start_time = time.time() + + with self.readout(): + self.stop_scan.clear() + + while not (self.stop_scan.is_set() or timed_out()): + try: + time.sleep(1) + + # Update progress bar + try: + self.pbar.update(1) + except ValueError: + pass + + except KeyboardInterrupt: # React on keyboard interupt + self.stop_scan.set() + self.log.info('Scan was stopped due to keyboard interrupt') + + self.pbar.close() + self.log.success('Scan finished') + + def _analyze(self): + tot_calib_file = self.configuration['scan'].get('tot_calib_file', None) + if tot_calib_file is not None: + self.configuration['bench']['analysis']['cluster_hits'] = True + + with analysis.Analysis(raw_data_file=self.output_filename + '.h5', tot_calib_file=tot_calib_file, **self.configuration['bench']['analysis']) as a: + a.analyze_data() + + if self.configuration['bench']['analysis']['create_pdf']: + with plotting.Plotting(analyzed_data_file=a.analyzed_data_file) as p: + p.create_standard_plots() + + +if __name__ == "__main__": + with SourceScan(scan_config=scan_configuration) as scan: + scan.start() diff --git a/tjmonopix2/scans/shift_and_inject.py b/tjmonopix2/scans/shift_and_inject.py index 76231c8..b5b8018 100644 --- a/tjmonopix2/scans/shift_and_inject.py +++ b/tjmonopix2/scans/shift_and_inject.py @@ -11,7 +11,7 @@ ''' -def shift_and_inject(chip, n_injections, pbar=None, scan_param_id=0, masks=['injection', 'enable'], pattern='default', cache=False, skip_empty=True): +def shift_and_inject(chip, n_injections, pbar=None, scan_param_id=0, masks=['injection', 'enable'], pattern='default', cache=False, skip_empty=True, PulseStartCnfg=19, wait_cycles=1, latency=1400): ''' Regular mask shift and analog injection function. Parameters: @@ -35,7 +35,7 @@ def shift_and_inject(chip, n_injections, pbar=None, scan_param_id=0, masks=['inj ''' for fe, active_pixels in chip.masks.shift(masks=masks, pattern=pattern, cache=cache, skip_empty=skip_empty): if not fe == 'skipped': - chip.inject(PulseStartCnfg=1, PulseStopCnfg=257, repetitions=n_injections, latency=1400) + chip.inject(PulseStartCnfg=PulseStartCnfg, PulseStopCnfg=PulseStartCnfg + 512, repetitions=n_injections, wait_cycles=wait_cycles, latency=latency) if pbar is not None: pbar.update(1) diff --git a/tjmonopix2/system/bdaq53.py b/tjmonopix2/system/bdaq53.py index 1b6aeea..2f9dfae 100644 --- a/tjmonopix2/system/bdaq53.py +++ b/tjmonopix2/system/bdaq53.py @@ -241,19 +241,22 @@ def set_chip_type(self): # '''Disables automatic sending of sync commands''' # self['cmd'].set_auto_sync(0) - def configure_tdc_module(self): - self.log.info('Configuring TDC module') + def _set_tdc_registers(self): self['tdc'].EN_WRITE_TIMESTAMP = self.configuration['TDC'].get('EN_WRITE_TIMESTAMP', 1) self['tdc'].EN_TRIGGER_DIST = self.configuration['TDC'].get('EN_TRIGGER_DIST', 1) self['tdc'].EN_NO_WRITE_TRIG_ERR = self.configuration['TDC'].get('EN_NO_WRITE_TRIG_ERR', 1) self['tdc'].EN_INVERT_TDC = self.configuration['TDC'].get('EN_INVERT_TDC', 0) self['tdc'].EN_INVERT_TRIGGER = self.configuration['TDC'].get('EN_INVERT_TRIGGER', 0) + def configure_tdc_module(self): + self.log.info('Configuring TDC module') + self._set_tdc_registers() + def enable_tdc_module(self): - self['tdc'].ENABLE = 1 + self['tdc'].ENABLE = True def disable_tdc_module(self): - self['tdc'].ENABLE = 0 + self['tdc'].ENABLE = False def enable_tlu_module(self): self['tlu']['TRIGGER_ENABLE'] = True diff --git a/tjmonopix2/system/tjmonopix2.py b/tjmonopix2/system/tjmonopix2.py index fa88b61..a037cb1 100755 --- a/tjmonopix2/system/tjmonopix2.py +++ b/tjmonopix2/system/tjmonopix2.py @@ -1024,11 +1024,12 @@ def _get_register_value(self, address, timeout=1000, tries=10): else: raise RuntimeError('Timeout while waiting for register response.') - def write_cal(self, PulseStartCnfg=1, PulseStopCnfg=10, write=True): + def write_cal(self, PulseStartCnfg=1, PulseStopCnfg=10, wait_cycles=0, write=True): ''' Command to send a digital or analog injection to the chip. Digital or analog injection is selected globally via the INJECTION_SELECT register. Need to reset BCID counter (register address 146) before injection. + wait_cycle delays injection by 4 BCID CLK cycles. For digital injection, only CAL_edge signal is relevant: - CAL_edge_mode switches between step (0) and pulse (1) mode @@ -1041,6 +1042,7 @@ def write_cal(self, PulseStartCnfg=1, PulseStopCnfg=10, write=True): ''' indata = self._write_register(146, 0b100, write=False) indata += self._write_register(146, 0b000, write=False) + indata += self.write_sync(write=False) * wait_cycles indata += [self.CMD_CAL] indata += [self.cmd_data_map[self.chip_id]] indata += [self.cmd_data_map[(PulseStartCnfg & 0b11_1110) >> 1]] @@ -1053,9 +1055,9 @@ def write_cal(self, PulseStartCnfg=1, PulseStopCnfg=10, write=True): return indata - def inject(self, PulseStartCnfg=1, PulseStopCnfg=10, repetitions=1, latency=400, write=True): + def inject(self, PulseStartCnfg=1, PulseStopCnfg=10, repetitions=1, latency=400, wait_cycles=0, write=True): indata = self.write_sync(write=False) * 4 - indata += self.write_cal(PulseStartCnfg=PulseStartCnfg, PulseStopCnfg=PulseStopCnfg, write=False) # Injection + indata += self.write_cal(PulseStartCnfg=PulseStartCnfg, PulseStopCnfg=PulseStopCnfg, wait_cycles=wait_cycles, write=False) # Injection indata += self.write_sync(write=False) * latency if write: diff --git a/tjmonopix2/testbench.yaml b/tjmonopix2/testbench.yaml index 595fc40..d5c9d9c 100644 --- a/tjmonopix2/testbench.yaml +++ b/tjmonopix2/testbench.yaml @@ -28,14 +28,17 @@ TLU: TDC: EN_WRITE_TIMESTAMP: 1 # Writing trigger timestamp - EN_TRIGGER_DIST: 0 # Measuring trigger to TDC delay with 640MHz clock + EN_TRIGGER_DIST: 1 # Measuring trigger to TDC delay with 640MHz clock EN_NO_WRITE_TRIG_ERR: 1 # Writing TDC word only if valid trigger occurred EN_INVERT_TDC: 0 # Inverting TDC input - EN_INVERT_TRIGGER: 0 # Inverting trigger input, e.g. for using Test output from EUDET TLU + EN_INVERT_TRIGGER: 1 # Inverting trigger input, e.g. for using Test output from EUDET TLU hardware: # Setup-specific hardware settings enable_NTC: False # Only enable if you know you have the correct resistors mounted on the BDAQ board! +electron_conversion: # Charge conversion from DAC to e⁻, typically 8.8 for DC and 13.3 for AC (slightly chip dependent!) + DC_coupled: 8.8 + AC_coupled: 13.3 calibration: # Setup-specific calibration constants bdaq_ntc: # Resistors on BDAQ board for NTC readout R16: 412.00e3 @@ -50,7 +53,7 @@ analysis: create_pdf: True # Create analysis summary pdf # module_plotting: True # Create combined plots for chip in a module store_hits: True # store hit table - # cluster_hits: False # store cluster data + cluster_hits: False # store cluster data # analyze_tdc: False # analyze TDC words # use_tdc_trigger_dist: False # analyze TDC to TRG distance # align_method: 0 # how to detect new events diff --git a/tjmonopix2/tests/test_hardware/drivers/Drive320Clock.py b/tjmonopix2/tests/test_hardware/drivers/Drive320Clock.py index 75a40df..0cdde6b 100644 --- a/tjmonopix2/tests/test_hardware/drivers/Drive320Clock.py +++ b/tjmonopix2/tests/test_hardware/drivers/Drive320Clock.py @@ -18,5 +18,4 @@ def __init__(self, entity): BusDriver.__init__(self, entity, "", entity.CLK320) async def run(self): - - cocotb.fork(Clock(self.clock, 3120).start()) + cocotb.start_soon(Clock(self.clock, 3120).start()) diff --git a/tjmonopix2/tests/test_hardware/hdl/libmonopix2.a b/tjmonopix2/tests/test_hardware/hdl/libmonopix2.a index 746cd9e..baa20b0 100644 Binary files a/tjmonopix2/tests/test_hardware/hdl/libmonopix2.a and b/tjmonopix2/tests/test_hardware/hdl/libmonopix2.a differ diff --git a/tjmonopix2/tests/test_hardware/hdl/monopix2.sv b/tjmonopix2/tests/test_hardware/hdl/monopix2.sv index 1341252..b1a8fd7 100644 --- a/tjmonopix2/tests/test_hardware/hdl/monopix2.sv +++ b/tjmonopix2/tests/test_hardware/hdl/monopix2.sv @@ -4,15 +4,15 @@ // See instructions in your simulator for how to use DPI libraries module monopix2 ( - input logic LVDS_CMD - , input logic LVDS_CMD_CLK + input logic LVDS_CMD_CLK , input logic LVDS_SER_CLK + , output logic LVDS_CHSYNC_CLK_OUT + , input logic RESETB_EXT + , input logic LVDS_CMD , input logic LVDS_PULSE_EXT , output logic LVDS_DATA_OUT , output logic LVDS_HITOR_OUT , output logic LVDS_CHSYNC_LOCKED_OUT - , output logic LVDS_CHSYNC_CLK_OUT - , input logic RESETB_EXT , input logic [262143:0] ANALOG_HIT ); @@ -23,20 +23,20 @@ module monopix2 ( // Checks to make sure the .sv wrapper and library agree import "DPI-C" function void monopix2_protectlib_check_hash(int protectlib_hash__V); - // Creates an instance of the secret module at initial-time + // Creates an instance of the library module at initial-time // (one for each instance in the user's design) also evaluates - // the secret module's initial process + // the library module's initial process import "DPI-C" function chandle monopix2_protectlib_create(string scope__V); // Updates all non-clock inputs and retrieves the results import "DPI-C" function longint monopix2_protectlib_combo_update ( chandle handle__V + , output logic LVDS_CHSYNC_CLK_OUT , input logic LVDS_CMD , input logic LVDS_PULSE_EXT , output logic LVDS_DATA_OUT , output logic LVDS_HITOR_OUT , output logic LVDS_CHSYNC_LOCKED_OUT - , output logic LVDS_CHSYNC_CLK_OUT , input logic [262143:0] ANALOG_HIT ); @@ -45,11 +45,11 @@ module monopix2 ( chandle handle__V , input logic LVDS_CMD_CLK , input logic LVDS_SER_CLK + , output logic LVDS_CHSYNC_CLK_OUT + , input logic RESETB_EXT , output logic LVDS_DATA_OUT , output logic LVDS_HITOR_OUT , output logic LVDS_CHSYNC_LOCKED_OUT - , output logic LVDS_CHSYNC_CLK_OUT - , input logic RESETB_EXT ); // Need to convince some simulators that the input to the module @@ -61,46 +61,45 @@ module monopix2 ( , input logic [262143:0] ANALOG_HIT ); - // Evaluates the secret module's final process + // Evaluates the library module's final process import "DPI-C" function void monopix2_protectlib_final(chandle handle__V); + // verilator tracing_off chandle handle__V; - + time last_combo_seqnum__V; + time last_seq_seqnum__V; + + logic LVDS_CHSYNC_CLK_OUT_combo__V; logic LVDS_DATA_OUT_combo__V; logic LVDS_HITOR_OUT_combo__V; logic LVDS_CHSYNC_LOCKED_OUT_combo__V; - logic LVDS_CHSYNC_CLK_OUT_combo__V; + logic LVDS_CHSYNC_CLK_OUT_seq__V; logic LVDS_DATA_OUT_seq__V; logic LVDS_HITOR_OUT_seq__V; logic LVDS_CHSYNC_LOCKED_OUT_seq__V; - logic LVDS_CHSYNC_CLK_OUT_seq__V; + logic LVDS_CHSYNC_CLK_OUT_tmp__V; logic LVDS_DATA_OUT_tmp__V; logic LVDS_HITOR_OUT_tmp__V; logic LVDS_CHSYNC_LOCKED_OUT_tmp__V; - logic LVDS_CHSYNC_CLK_OUT_tmp__V; - - time last_combo_seqnum__V; - time last_seq_seqnum__V; - // Hash value to make sure this file and the corresponding // library agree - localparam int protectlib_hash__V = 32'd1563611712; - + localparam int protectlib_hash__V = 32'd2954403249; + initial begin monopix2_protectlib_check_hash(protectlib_hash__V); handle__V = monopix2_protectlib_create($sformatf("%m")); end // Combinatorialy evaluate changes to inputs - always @(*) begin + always @* begin last_combo_seqnum__V = monopix2_protectlib_combo_update( handle__V + , LVDS_CHSYNC_CLK_OUT_combo__V , LVDS_CMD , LVDS_PULSE_EXT , LVDS_DATA_OUT_combo__V , LVDS_HITOR_OUT_combo__V , LVDS_CHSYNC_LOCKED_OUT_combo__V - , LVDS_CHSYNC_CLK_OUT_combo__V , ANALOG_HIT ); end @@ -117,30 +116,31 @@ module monopix2 ( handle__V , LVDS_CMD_CLK , LVDS_SER_CLK + , LVDS_CHSYNC_CLK_OUT_tmp__V + , RESETB_EXT , LVDS_DATA_OUT_tmp__V , LVDS_HITOR_OUT_tmp__V , LVDS_CHSYNC_LOCKED_OUT_tmp__V - , LVDS_CHSYNC_CLK_OUT_tmp__V - , RESETB_EXT ); + LVDS_CHSYNC_CLK_OUT_seq__V <= LVDS_CHSYNC_CLK_OUT_tmp__V; LVDS_DATA_OUT_seq__V <= LVDS_DATA_OUT_tmp__V; LVDS_HITOR_OUT_seq__V <= LVDS_HITOR_OUT_tmp__V; LVDS_CHSYNC_LOCKED_OUT_seq__V <= LVDS_CHSYNC_LOCKED_OUT_tmp__V; - LVDS_CHSYNC_CLK_OUT_seq__V <= LVDS_CHSYNC_CLK_OUT_tmp__V; end // Select between combinatorial and sequential results - always @(*) begin + always @* begin if (last_seq_seqnum__V > last_combo_seqnum__V) begin + LVDS_CHSYNC_CLK_OUT = LVDS_CHSYNC_CLK_OUT_seq__V; LVDS_DATA_OUT = LVDS_DATA_OUT_seq__V; LVDS_HITOR_OUT = LVDS_HITOR_OUT_seq__V; LVDS_CHSYNC_LOCKED_OUT = LVDS_CHSYNC_LOCKED_OUT_seq__V; - LVDS_CHSYNC_CLK_OUT = LVDS_CHSYNC_CLK_OUT_seq__V; - end else begin + end + else begin + LVDS_CHSYNC_CLK_OUT = LVDS_CHSYNC_CLK_OUT_combo__V; LVDS_DATA_OUT = LVDS_DATA_OUT_combo__V; LVDS_HITOR_OUT = LVDS_HITOR_OUT_combo__V; LVDS_CHSYNC_LOCKED_OUT = LVDS_CHSYNC_LOCKED_OUT_combo__V; - LVDS_CHSYNC_CLK_OUT = LVDS_CHSYNC_CLK_OUT_combo__V; end end diff --git a/tjmonopix2/tests/test_hardware/utils.py b/tjmonopix2/tests/test_hardware/utils.py index e65fdff..5eb40c4 100644 --- a/tjmonopix2/tests/test_hardware/utils.py +++ b/tjmonopix2/tests/test_hardware/utils.py @@ -10,10 +10,10 @@ import typing import basil -import cocotb -import tjmonopix2 import yaml from basil.utils.sim.utils import cocotb_compile_and_run + +import tjmonopix2 from tjmonopix2 import utils from tjmonopix2.system.bdaq53 import BDAQ53 from tjmonopix2.system.tjmonopix2 import TJMonoPix2 @@ -23,9 +23,6 @@ def setup_cocotb(extra_defines: list = []) -> dict: if "SIM" not in os.environ.keys(): os.environ["SIM"] = "verilator" - if os.environ["SIM"] == "verilator": - patch_cocotb_makefile() - tjmonopix2_path = os.path.dirname(tjmonopix2.__file__) # tjmonopix2 package path top_dir = os.path.join( tjmonopix2_path, ".." @@ -64,15 +61,10 @@ def setup_cocotb(extra_defines: list = []) -> dict: "-DVERSION_PATCH={:s}".format(version[2]), "-LDFLAGS {:s}/tjmonopix2/tests/test_hardware/hdl/libmonopix2.a".format(top_dir), "--hierarchical", + "-Wno-fatal", "-Wno-COMBDLY", "-Wno-PINMISSING", - "-Wno-fatal", - "--output-split 15000", - "-O3", - "-CFLAGS -O3", - ], - build_args=[ - "-j 2" + "-Wno-LATCH", ], extra_defines=extra_defines, ) @@ -100,17 +92,6 @@ def setup_cocotb(extra_defines: list = []) -> dict: return cnfg -def patch_cocotb_makefile() -> None: - makefile_path = os.path.join( - os.path.dirname(cocotb.__file__), "share/makefiles/simulators" - ) - with open(os.path.join(makefile_path, "Makefile.verilator"), "r+") as makefile: - content = makefile.read() - makefile.seek(0) - makefile.truncate() - makefile.write(content.replace("--public-flat-rw ", "")) - - def wait_for_sim(dut: BDAQ53, repetitions=8) -> None: dut.write_command(dut.write_sync(write=False), repetitions=repetitions)