Overview
CIBModules is a DUNE DAQ plugin package that provides a command and readout interface for the Calibration Interface Board (CIB) hardware. The CIB controls and operates the Ionization Laser System (IoLS) used for charge calibration in the DUNE detector. This module bridges detector calibration hardware with the DUNE DAQ framework, enabling synchronized trigger generation and detector readout.
Purpose
The CIB hardware is a specialized hardware controller that manages:
- Laser calibration system operation: Controls ionization laser delivery and positioning
- Detector trigger generation: Produces trigger words that are then used by the DUNE DAQ to read out the detector
- Motor control: Manages three stepper motors for laser alignment and positioning
- Timing and synchronization: Coordinates with DUNE's timing and trigger system
CIBModules implements the DUNE DAQ interface to interface the CIB trigger functionality as a pluggable DAQModule. CIBModules handles the TCP communication with the CIB hardware, parses incoming trigger data, and translates it into DUNE's HSI (Hardware Signal Interface) format for subsequent trigger candidate generation and detector readout.
Architecture & Design Principles
High-Level Design
The CIBModule bridges two separate network connections:
- Control connection (ephemeral): CIBModule → CIB hardware (port 8992)
- Used for JSON-formatted configuration commands
- Blocks until response received, ensuring synchronization
- Data connection (persistent): CIB → CIBModule (port 8993 by default, but configurable)
- Runs in dedicated worker thread with 70 ms receive timeout
- Receives IoLS trigger detector data continuously during run
- Parses incoming trigger packets, extracts motor positions and timestamps and produces HSI frames for DAQ backend
┌──────────────────────────────┐
│ DUNE DAQ Framework │
│ (appfwk, iomanager) │
└──────────────┬────────────────┘
│
┌───────▼────────┐
│ CIBModule │ (HSIEventSender)
│ DAQModule │
└───┬──────────┬─┘
│ │
┌──────▼──┐ ┌───▼─────────┐
│ Control │ │ Data Worker │
│ Socket │ │ Thread │
│ (JSON) │ │ (TCP recv) │
└──────┬──┘ └───┬─────────┘
│ │
┌──────▴──────────▴──┐
│ CIB Hardware │ (port 8992)
│ (ZynqMP board) │
└────────────────────┘
Key Design Principles
- Dual TCP Connection Pattern
- This decoupling prevents deadlocks and enables non-blocking trigger reception
- Control channel remains responsive for stop commands even during heavy data flow
- HSI Frame Generation
CIBModule inherits from HSIEventSender
- Converts incoming IoLS trigger packets to standardized HSI frames
- Encodes trigger information (motor positions, timestamps) in HSI format
- Sends frames to DAQ backend via IOManager sender for downstream processing
- Worker Thread Lifecycle
- Worker thread created but not started until
do_start() is called
- Thread runs socket listener accepting CIB connections and reading trigger data
- Respects run state via
std::atomic<bool> flags for lock-free synchronization
- Cleanly shuts down on
do_stop(), waiting for connection closure and socket EOF
- Graceful Shutdown Handshake
do_stop()
├─ Set m_stop_requested = true (signals data thread to stop accepting)
├─ Send {"command":"stop_run"} via control socket
├─ CIB closes data connection (TCP FIN)
├─ Receiver thread detects EOF, exits loop
├─ do_stop() awaits thread completion
└─ Return response to DAQ framework
- Configuration-Driven Initialization
- Appmodel schema validation ensures required fields before startup
- All connection parameters (host, port, timeout) configurable
- Trigger bit mapping defines which HSI trigger index this receiver uses
- Calibration stream settings enable offline data dumping for analysis
- At nominal 10 Hz laser trigger rate, the calibration stream produces manageable file sizes (approx. 560 kB per hour)
- Thread Safety
- Atomic flags for state synchronization (
m_is_running, m_is_configured, m_stop_requested)
- No locks in data path: Single-threaded worker thread receives all data; main thread only reads atomic counters
- Shared mutex for buffer count averaging (monitoring only)
- Exception-based error propagation for command-phase errors (configure, start, stop)
Components & Functionality
CIBModule.hpp - Class Definition
Primary class: CIBModule : public dunedaq::hsilibs::HSIEventSender
Key members:
m_control_socket: Boost ASIO TCP socket for JSON command/response exchange
m_receiver_socket: Boost ASIO TCP socket receiving trigger data from CIB
m_receiver_ios: Boost ASIO IO service context for async socket operations
m_thread_: Worker thread (via dunedaq::utilities::WorkerThread)
m_cib_hsi_data_sender: IOManager sender for HSI frames to downstream modules
- Atomic counters:
m_num_control_messages_sent, m_num_run_triggers_received, etc.
CIBModule.cpp - Implementation
Lifecycle methods:
CIBModule(name): Constructor initializing atomic flags and sockets
init(cfgMgr): Loads appmodel configuration; sets up HSI frame output sender
do_configure(obj): Connects to CIB hardware, establishes control socket, parses geo_id and trigger configuration
do_start(startobj): Launches worker thread, sets up receiver socket listener, sends "start_run" command
do_stop(obj): Sends "stop_run" command, awaits worker thread exit, closes sockets
~CIBModule(): Cleanup (closes control socket if still open)
Worker thread:
do_hsi_work(): Runs in dedicated thread for entire run duration
- Sets up listener on port 8871, awaits CIB connection
- Reads IoLS trigger packets in a loop
- Parses 3-motor position data + timestamp from each packet
- Constructs HSI_FRAME_STRUCT with trigger data
- Sends HSI frame via m_cib_hsi_data_sender
- Logs calibration data if enabled
- Exits cleanly when EOF received or stop requested
Utility methods:
send_config(json_string): Sends configuration to CIB via control socket
send_message(json_string): Sends command to CIB, waits for response with 1 sec timeout
read<T>(socket, obj): Generic template method to read binary data from socket
parse_hex(string, uint32): Parses hex trigger bit string for HSI frame encoding
set_calibration_stream(prefix): Opens timestamped calibration file for trigger dumping
update_calibration_file(): Rotates calibration file at configurable interval (default 15 min)
CIBModuleIssues.hpp - Error Handling
Custom ERS (Error Reporting System) exception types for DUNE DAQ:
CIBCommunicationError: Socket/network failures, timeouts
CIBConfigFailure: Configuration parsing or appmodel missing fields
CIBModuleError: Internal state violations, receiver setup failures
CIBWrongState: State machine violations (e.g., start without configure)
- Message/warning/debug variants for non-fatal issues
cib_data_fmt.h - Data Format Definitions
Struct definitions for IoLS trigger data exchange (shared with cib_utils):
iols_trigger_t: Motor position data (m1, m2, m3 with 17-22 bit resolution) + timestamp
tcp_header_t: Packet framing (version, sequence, size)
iols_tcp_packet_t: Full packet = header + trigger payload
- Bitmask utilities for signed value extraction (handles motor position signedness)
Configuration Schema
File: schema/cibmodules/cib_config.json
Provides defaults for JSON configuration:
{
"cib": {
"sockets": {
"receiver": {
"host": "localhost",
"port": 8992
}
},
"trigger_bit": "0x4"
}
}
Appmodel schema: DUNE-DAQ/appmodel
OKS schema classes:
CIBModule: Top-level DAQModule configuration (has configuration ↔ CIBConf and board ↔ CIBoardConf)
CIBConf : Connection parameters (host, port, timeout)
CIBoardConf: Board identity and geo_id (detector_id, crate_id, slot_id), as well as receiver location (port)
CIBTrigger : Trigger mapping (trigger_id, trigger_bit hex string, description)
CIBCalibrationStream: Calibration output settings (directory, update period)
Data Flow
Configuration Phase
DAQ Manager sends config JSON
↓
do_configure() parses appmodel
↓
Extract CIB host/port/trigger bit
↓
Create Boost ASIO control socket → CIB:8992
↓
send_config() → JSON configuration message → CIB
↓
CIB acknowledges (sets m_is_configured = true)
Run Execution
do_start() called with run number
↓
Launch worker thread do_hsi_work()
↓
Worker: Create listener socket on port 8993
↓
Worker: Signal m_receiver_ready = true
↓
Main thread: send_message({"command":"start_run", "run_number":N})
↓
CIB connects to port 8871, begins streaming triggers
↓
Worker: read() io-loops receiving IoLS trigger packets
↓
For each packet:
- Extract motor positions (m1, m2, m3)
- Get timestamp
- Build HSI_FRAME_STRUCT
- Send via m_cib_hsi_data_sender → downstream DAQ modules
- Increment m_num_run_triggers_received
- Optionally write to calibration file
↓
(main thread continues, allows other modules to run)
↓
do_stop() called
↓
send_message({"command":"stop_run"})
↓
CIB closes data socket (EOF on port 8871)
↓
Worker: read() returns false, exits loop
↓
Main: m_thread_.stop_working_thread() joins worker
↓
Return to DAQ (m_is_running = false)
Calibration Stream (Optional)
- If enabled via configuration, worker thread opens timestamped file in
m_calibration_dir
- Every trigger packet written as binary data to file
- File rotated at
m_calibration_file_interval (default 15 min) to manage size
- Useful for offline trigger analysis, debugging laser performance
Dependencies
External Libraries
| Dependency | Role | Version |
| Boost ASIO | Async TCP networking (socket, resolver, endpoint) | Part of Boost 1.70+ |
| nlohmann::json | JSON parsing/generation for CIB commands | Header-only, v3.x+ |
| DUNE appfwk | DAQModule base class, ConfigurationManager | DUNE DAQ suite |
| DUNE iomanager | Sender/Receiver for module connectivity | DUNE DAQ suite |
| DUNE hsilibs | HSIEventSender base, HSI_FRAME_STRUCT | DUNE DAQ suite |
| DUNE logging | TLOG macros for structured logging | DUNE DAQ suite |
| DUNE ers | Exception/error reporting system | DUNE DAQ suite |
| DUNE appmodel | Configuration schema (DAL) for CIB parameters | DUNE DAQ suite |
| spdlog | Underlying logging backend | Indirect via DUNE |
Internal Dependencies
- cib_utils (
cib_data_fmt.h): Data structure definitions shared with CIB hardware and other CIB tools
- appmodel (
CIB.schema.xml, generated DAL classes): Configuration object access
Build System
Configuration & Usage
Module Configuration (Appmodel)
In OKS database configuration:
<CIBModule>
<name>cib_receiver_0</name>
<configuration class="CIBConf">
<cib_host>np04-iols-cib-02.cern.ch</cib_host>
<cib_port>8992</cib_port>
<connection_timeout_ms>1000</connection_timeout_ms>
<cib_trigger class="CIBTrigger">
<trigger_id>iols_laser</trigger_id>
<trigger_bit>0x4</trigger_bit>
<description>IoLS Laser Trigger</description>
</cib_trigger>
<calibration_stream class="CIBCalibrationStream">
<output_directory>/data/cib_triggers</output_directory>
<update_period_s>900</update_period_s>
</calibration_stream>
</configuration>
<board class="CIBoardConf">
<geo_id>
<detector_id>1</detector_id>
<crate_id>0</crate_id>
<slot_id>4</slot_id>
</geo_id>
</board>
<outputs>
<connection UID="HSI_trigger_cib">
</connection>
</outputs>
</CIBModule>
Runtime Command Sequence
- Configure: Frame sends configuration from appmodel → CIB hardware setup
- Start: Worker thread launches, listens for CIB connection, DAQ tells CIB to start sending triggers
- Run: Trigger data flows, HSI frames generated and shipped downstream
- Stop: DAQ stops CIB streaming, worker thread exits, resources cleaned up
Monitoring & Telemetry
Metrics tracked (via Protobuf opmon):
num_control_messages_sent: Commands sent to CIB
num_control_responses_received: Acknowledgments from CIB
num_total_triggers_received: Cumulative trigger count
num_run_triggers_received: Triggers in current run
hardware_running: Boolean state
hardware_configured: Boolean state
average_buffer_counts: Average pending trigger queue depth
Accessible via DUNE's OpMon (operational monitoring) system for real-time diagnostics.
Error Handling & Diagnostics
Common Issues
- "Unable to connect to CIB"
- CIB hardware not running or wrong IP/port
- Network connectivity check:
ping CIB host, verify port 8992 open
- "Receiver socket timed out"
- CIB failed to connect within 500 ms (50 × 10 ms retries)
- CIB may not have started data streaming; check CIB firmware logs
- "Listener port in use"
- Another process using port 8993
- Increments port number in configuration and retry until find unused port
- Updates configuration and sends new port to CIB via control socket
- "CIB has not been successfully configured"
do_start() called without successful do_configure()
- Verify configuration JSON sent and CIB acknowledged
Logging
Set log level to debug for detailed execution trace:
export DUNEDAQ_LOG_LEVEL=debug
Output shows:
- Control socket connection attempts
- JSON messages sent/received
- Worker thread lifecycle events
- Trigger packet parsing details
- HSI frame construction
Integration with DUNE DAQ Ecosystem
Application Composition
CIBModule is one module in a DAQ application instance. Typical composition:
DAQ Application
├─ CIBModule (produces HSI frames)
├─ DataHandlerModule (ingests HSI frames, formats for detector readout)
├─ TriggerPrimitiveModule (optional: interprets trigger bits)
└─ Other modules (timing, monitoring, etc.)
Data flow: CIBModule → IOManager queue/network → DataHandlerModule → detector data formatting → storage
HSI Integration
CIBModule extends HSIEventSender, meaning:
- Registers with IOManager as source of HSI trigger events
- Produces 128-bit HSI frames with:
- 10-bit crate ID
- 4-bit slot ID
- 16-bit trigger bits (e.g., 0x0004 for laser trigger)
- 64-bit timestamp
- Integrates with TPC trigger logic and timing system
Appmodel Configuration Management
Leverages DUNE's configuration framework:
- Schema validation on load (ensures required fields present)
- Dependency resolution (board config validates geo_id)
- Runtime reconfiguration (supported; triggers
do_configure() again)
- Session awareness (accesses session info for output directory paths)
Testing & Development
Unit Tests
Run via CMake:
cd build
ctest --output-on-failure
Current test coverage (minimal):
- Placeholder tests in
unittest/Placeholder_test.cxx
- TODO: Full lifecycle tests, socket mocking, error injection
Manual Testing (Standalone)
For development without full DAQ:
- Mock CIB hardware with cib_utils
cib_daq_server in simulation mode
- Create test appmodel config with localhost addresses
- Run DAQ shell with manual
conf, start, stop commands
Debug Targets
Appmodel generation:
# Regenerate C++ classes from schema (auto on build)
dune_oks_generate --schema schema/appmodel/CIB.schema.xml
Code Quality & Standards
- C++ Standard: C++11 (minimum requirement per DUNE policy)
- Naming conventions:
- Member variables:
m_ prefix (e.g., m_receiver_port)
- Private/protected: lowercase with underscores
- Public: camelCase methods
- Resource management: RAII with
std::unique_ptr, std::lock_guard for locks
- No raw pointers: Prefer smart pointers; raw pointers only in appmodel DAL objects (managed by framework)
- Linting: Code follows DUNE's cpplint style (enforced in CI)
Related Modules & Documentation
Authors & Acknowledgments
Development team:
- Nuno Barros (primary developer)
- DUNE Calibration Working Group
License
This project is part of the DUNE DAQ Software Suite. See the COPYING file in the root of the DUNE repository for full licensing details.
Project Status
Current Version: 1.0.0
Development Status: Active
- ✅ Core functionality (trigger reception, HSI generation) stable
- ✅ Multi-motor position parsing and reporting
- ✅ Calibration stream offline dumping
- 🔄 Full test coverage (in progress)