diff --git a/scripts/runFileTest.sh b/scripts/runFileTest.sh
index d6a4b149bb97a6b3033095f071120daf94d46532..e69338450a882a795daf2c262c77280c69543cf2 100755
--- a/scripts/runFileTest.sh
+++ b/scripts/runFileTest.sh
@@ -8,6 +8,10 @@ function scdaq_test {
     echo "Starting scdaq test with CALO P5 capture file: check for crash"
     timeout 10s src/scdaq --config test/config/scdaq-calo.conf | uniq -uc
     CALORET="${PIPESTATUS[0]}"
+    echo "#############################################################################"
+    echo "Starting scdaq test with 2 passthrough P5 capture files: check for crash"
+    timeout 10s src/scdaq --config test/config/scdaq-passthrough.conf --nstreams 2 | uniq
+    MULTI_PIPELINE_RET="${PIPESTATUS[0]}"
     if [[ "$GMTRET" -ne 124 ]]; then # We expect to fail with a timeout because scdaq isn't supposed to return.
         echo "SCDAQ test for GMT failed with exit code ${GMTRET}!"
         GMT_FAILED=true
@@ -22,6 +26,13 @@ function scdaq_test {
         echo "SCDAQ test for CALO failed, no output files found!"
         CALO_FAILED=true
     fi
+    if [[ "$MULTI_PIPELINE_RET" -ne 124 ]]; then
+        echo "SCDAQ test for MULTIPLE PIPELINES failed with exit code ${MULTI_PIPELINE_RET}!"
+        MULTI_PIPELINE_FAILED=true
+    elif [[ $(find test/data -name "*scout_PASS1_CALO*" | wc -l) -eq 0 || $(find test/data -name "*scout_PASS2_GMT*" | wc -l) -eq 0 ]]; then
+        echo "SCDAQ test for MULTIPLE PIPELINES failed, no output files found!"
+        MULTI_PIPELINE_FAILED=true
+    fi
 }
 
 echo "building scdaq locally"
@@ -34,26 +45,29 @@ tar -xzvf gmt_testfile.tar.gz
 cd ../../
 OUTPUT_EXPECTED=false
 scdaq_test
-echo "Starting second set of test files"
 echo $GMT_FAILED
 echo $CALO_FAILED
+echo $MULTI_PIPELINE_FAILED
 
+echo "Starting second set of test files"
 OUTPUT_EXPECTED=true
 cd test/data
 rm calo_testfile.dat
 rm gmt_testfile.dat
 tar -xzvf calo_testfile_mod.tar.gz
 tar -xzvf gmt_testfile_mod.tar.gz
-rm ../test/data/run000000/in_progress/*
+rm -rf run000000/in_progress/*
 cd ../../
-#next line needed because older GMT files were taken with 5 orbits per packet 
+#next line needed because older GMT files were taken with 5 orbits per packet
 sed -i 's/nOrbitsPerPacket:1/nOrbitsPerPacket:5/' test/config/scdaq-gmt.conf
 scdaq_test
 
 echo $GMT_FAILED
 echo $CALO_FAILED
+echo $MULTI_PIPELINE_FAILED
+
 
-if [[ $GMT_FAILED = true || $CALO_FAILED = true ]]; then
+if [[ $GMT_FAILED = true || $CALO_FAILED = true || $MULTI_PIPELINE_FAILED = true ]]; then
     exit 1
 fi
 
diff --git a/scripts/scdaqrpm.sh b/scripts/scdaqrpm.sh
index 65cc978347d39f7d726edb2ea12dde0930a1d9a1..1021a9376e35abee1f377d99c7e6a5cd29251d12 100755
--- a/scripts/scdaqrpm.sh
+++ b/scripts/scdaqrpm.sh
@@ -133,7 +133,7 @@ cp -R $BASEDIR/src SOURCES/
 echo $RPM_SOURCE_DIR
 cd SOURCES
 pwd
-make
+make all
 
 %install
 echo $RPM_SOURCE_DIR
diff --git a/src/InputFilter.cc b/src/InputFilter.cc
index e7838ed947ef4a838009bfd212fe3eb2ac0b454c..a1788ac4fe205e700361b42a64e3d6ca97175567 100644
--- a/src/InputFilter.cc
+++ b/src/InputFilter.cc
@@ -8,6 +8,7 @@
 #include "log.h"
 #include "slice.h"
 
+std::atomic<int> filterCounter{0};
 InputFilter::InputFilter(size_t packetBufferSize, size_t nbPacketBuffers, ctrl &control)
     : filter(serial_in_order),
       control_(control),
@@ -20,7 +21,9 @@ InputFilter::InputFilter(size_t packetBufferSize, size_t nbPacketBuffers, ctrl &
   maxBytesRead_ = 0;
   previousNbReads_ = 0;
 
-  LOG(TRACE) << "Configuration translated into:";
+  filterId = filterCounter++;
+
+  LOG(TRACE) << "[" << std::to_string(filterId) << "] Configuration translated into:";
   LOG(TRACE) << "  MAX_BYTES_PER_INPUT_SLICE: " << packetBufferSize;
   LOG(TRACE) << "  TOTAL_SLICES: " << nbPacketBuffers;
   LOG(TRACE) << "Created input filter and allocated at " << static_cast<void *>(nextSlice_);
@@ -56,7 +59,8 @@ void InputFilter::printStats(std::ostream &out, ssize_t lastBytesRead) {
   std::ios state(nullptr);
   state.copyfmt(out);
 
-  out << "#" << nbReads_ << ": Reading " << std::fixed << std::setprecision(1) << bwd << " MB/sec, "
+  out << "[" << std::to_string(filterId) << "] "
+      << "#" << nbReads_ << ": Reading " << std::fixed << std::setprecision(1) << bwd << " MB/sec, "
       << nbReadsDiff << " packet(s) min/avg/max " << minBytesRead_ << '/' << avgBytesRead << '/'
       << maxBytesRead_ << " last " << lastBytesRead;
 
diff --git a/src/InputFilter.h b/src/InputFilter.h
index 9b4b9a4b171aff324ed6d418fa96a5ad64ce37cb..4a99895209df1fbb8fb6ede5591ccbe53cff371a 100644
--- a/src/InputFilter.h
+++ b/src/InputFilter.h
@@ -1,6 +1,7 @@
 #ifndef INPUT_FILTER_H
 #define INPUT_FILTER_H
 
+#include <atomic>
 #include <cstddef>
 #include <iostream>
 
@@ -19,6 +20,7 @@ class InputFilter : public tbb::filter {
   InputFilter(size_t packet_buffer_size, size_t number_of_packet_buffers, ctrl &control);
   virtual ~InputFilter();
 
+  int filterId = 0;
   // Return the number of read calls
   uint64_t nbReads() { return nbReads_; }
 
diff --git a/src/Makefile b/src/Makefile
index 849f8ad714741a62a872c532eb000b8eddfbf8e1..57ed2aae389c3e05334db62c3c53cb0b1c380ad1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -12,25 +12,33 @@
 TARGET = scdaq
 
 # source files
-SOURCES = config.cc DmaInputFilter.cc FileDmaInputFilter.cc InputFilter.cc MicronDmaInputFilter.cc OutputByOrbit.cc OutputBySize.cc OutputFileHandler.cc processor.cc scdaq.cc session.cc slice.cc TcpInputFilter.cc tools.cc WZDmaInputFilter.cc
+SOURCES = pipeline.cc config.cc DmaInputFilter.cc FileDmaInputFilter.cc InputFilter.cc MicronDmaInputFilter.cc OutputByOrbit.cc OutputBySize.cc OutputFileHandler.cc processor.cc scdaq.cc session.cc slice.cc TcpInputFilter.cc tools.cc WZDmaInputFilter.cc
 C_SOURCES = wz_dma.c
 
 # work out names of object files from sources
 OBJECTS = $(SOURCES:.cc=.o)
 OBJECTS += $(C_SOURCES:.c=.o)
 
+TEST_PROGRESS_DIR = test/in_progress
+
 # compiler flags (do not include -c here as it's dealt with by the
 # appropriate rules; CXXFLAGS gets passed as part of command
 # invocation for both compilation (where -c is needed) and linking
 # (where it's not.)
-CXXFLAGS = -std=c++11 -Wall -Wextra -Og -g -rdynamic -Wconversion 
+CXXFLAGS = -std=c++11 -Wall -Wextra -Og -g -rdynamic -Wconversion
 
 CFLAGS = $(CXXFLAGS)
 LDFLAGS = -Llibmicron -ltbb -ltbbmalloc -lboost_thread -lboost_chrono -lcurl -lpthread -lcrypto
 CPPFLAGS = -I. -Iwzdma
 
+
 # default target (to build all)
-all: ${TARGET}
+all: clean_progress ${TARGET}
+
+clean_progress:
+	rm -rf ${TEST_PROGRESS_DIR}
+
+TGT_CLEAN_PROG = cleanprogress
 
 # clean target
 clean:
@@ -49,7 +57,7 @@ ${TARGET}: ${OBJECTS}
 # header dependencies (in more complicated cases, you can
 # look into generating these automatically.)
 
-scdaq.o:	processor.h OutputBySize.h OutputByOrbit.h format.h server.h controls.h config.h session.h log.h
+scdaq.o:	processor.h OutputBySize.h OutputByOrbit.h format.h server.h controls.h config.h session.h log.h pipeline.h
 config.o:	config.h log.h
 DmaInputFilter.o:	DmaInputFilter.h slice.h
 FileDmaInputFilter.o:	FileDmaInputFilter.h InputFilter.h log.h
@@ -65,3 +73,4 @@ TcpInputFilter.o:	TcpInputFilter.h InputFilter.h tools.h log.h
 wz_dma.o:	wz_dma.h
 MicronDmaInputFilter.o: MicronDmaInputFilter.h
 tools.o: tools.h log.h
+pipeline.o: pipeline.h
diff --git a/src/config.cc b/src/config.cc
index 7e28b37ab19efc32e8fe780354eb83d96e52b131..41d547e6bf6a6d23d6e0ac82ac82dced627146a4 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -8,6 +8,11 @@ config::config(std::string filename) {
   bool valid = true;
 
   std::ifstream in(filename.c_str(), std::ios_base::in);
+
+  if (!in.is_open()) {
+    throw std::invalid_argument("Configuration file could not be opened. Does it exist?");
+  }
+
   std::string line;
   while (!std::getline(in, line).eof()) {
     // Perform left trim
diff --git a/src/pipeline.cc b/src/pipeline.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5a9d79c4bae439174b29de44429d767c226a288a
--- /dev/null
+++ b/src/pipeline.cc
@@ -0,0 +1,151 @@
+#include "pipeline.h"
+
+Pipeline::Pipeline(config &conf, uint maxTokensPerThread)
+    : fMaxTokensPerThread(maxTokensPerThread),
+      fConf(conf),
+      fStreamProcessor(conf.getDmaPacketBufferSize(), conf.getDoZS(), conf.getProcessorType(),
+                       conf.getNOrbitsPerPacket(), conf.getCMSSWHeaders(),
+                       static_cast<uint16_t>(conf.getSourceID()), control) {
+  tools::log::set_filter_min_severity(fConf.getLogMinSeverity());
+  fConf.print();
+  const config::InputType &input = fConf.getInput();
+  const size_t &nbPacketBuffers = fConf.getNumberOfDmaPacketBuffers();
+  const size_t &packetBufferSize = fConf.getDmaPacketBufferSize();
+
+  if (input == config::InputType::FILEDMA) {
+    // Create FILE DMA reader
+    fInputFilter = std::make_shared<FileDmaInputFilter>(fConf.getInputFile(), packetBufferSize,
+                                                        nbPacketBuffers, control);
+
+  } else if (input == config::InputType::WZDMA) {
+    // Create WZ DMA reader
+    fInputFilter =
+        std::make_shared<WZDmaInputFilter>(packetBufferSize, nbPacketBuffers, control, fConf);
+
+  } else if (input == config::InputType::MICRONDMA) {
+    // create MicronDmaInputFilter reader
+    fInputFilter =
+        std::make_shared<MicronDmaInputFilter>(packetBufferSize, nbPacketBuffers, control, fConf);
+
+  } else if (input == config::InputType::TCPIP) {
+    // create TcpInputFilter reader
+    if (fConf.getDev_TCPAutoReconnectOnFailure()) {
+      fInputFilter = std::make_shared<TcpInputFilter>(fConf.getTcpDestPort(), packetBufferSize,
+                                                      nbPacketBuffers, fConf.getNOrbitsPerPacket(),
+                                                      control, fConf);
+
+    } else {
+      throw std::invalid_argument(
+          "Configuration error: enable developmentMode to use TCP input filter");
+    }
+  } else {
+    throw std::invalid_argument("Configuration error: Unknown input type was specified");
+  }
+
+  // Add input reader to a pipeline
+  fPipeline.add_filter(*fInputFilter);
+
+  // Create reformatter and add it to the pipeline
+  if (fConf.getEnableStreamProcessor()) {
+    fPipeline.add_filter(fStreamProcessor);
+  }
+
+  // Create file-writing stage and add it to the pipeline
+  if (fConf.getNOrbitsPerFile()) {
+    fOutputStream = std::make_shared<OutputByOrbitStream>(control, fConf);
+  } else {
+    fOutputStream = std::make_shared<OutputBySizeStream>(
+        fConf.getOutputFilenameBase(), fConf.getOutputFilenamePrefix(), Pipeline::control,
+        fConf.getProcessorType());
+  }
+  fPipeline.add_filter(*fOutputStream);
+
+  //    tbb::tick_count mainStartTime = tbb::tick_count::now();
+  control.running = false;
+  control.run_number = 0;
+  control.max_file_size = fConf.getOutputMaxFileSize();  // in Bytes
+  control.packets_per_report = static_cast<int>(fConf.getPacketsPerReport());
+  control.output_force_write = fConf.getOutputForceWrite();
+  control.n_orbits_per_packet = static_cast<int>(fConf.getNOrbitsPerPacket());
+
+  // Firmware needs at least 1MB buffer for DMA
+  if (fConf.getDmaPacketBufferSize() < 1024 * 1024) {
+    LOG(ERROR) << "dma_packet_buffer_size must be at least 1048576 bytes (1MB), but "
+               << fConf.getDmaPacketBufferSize()
+               << " bytes was given. Check the configuration file.";
+    throw std::invalid_argument("Configuration error: DMA packet buffer size too small. ");
+  }
+}
+
+MultiPipeline::MultiPipeline(std::vector<config> &configs) {
+  for (auto &config : configs) {
+    try {
+      fPipelines.emplace_back(config);
+      LOG(DEBUG) << "-Configuration loaded";
+      LOG(DEBUG) << "-Pipeline set up";
+    } catch (std::invalid_argument &e) {
+      LOG(FATAL) << "-Configuration invalid! Error text is \"" << e.what() << "\" Bailing out.";
+      throw;
+    } catch (std::exception &e) {
+      LOG(ERROR) << "-Error occurred. error text is: \"" << e.what() << "\"";
+      throw;
+    }
+  }
+
+  {  // RAII
+    fServer = std::unique_ptr<server>(
+        new server(io_service, fPipelines[0].fConf.getPortNumber(), Pipeline::control));
+    fServerThread = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
+  }
+}
+
+int MultiPipeline::Run() {
+  int retval = 0;
+  assert(!fPipelines.empty());
+  try {
+    if (!RunPipelines(static_cast<unsigned int>(fPipelines.size()))) {
+      retval = 1;  // Will terminate with error code set
+    }
+  } catch (std::exception &e) {
+    LOG(ERROR) << "Error in pipelines. Error text is: \"" << e.what() << "\"";
+    retval = 1;                             // Will terminate with error code set
+    config &conf = fPipelines.at(0).fConf;  // Use info from first file for SCONE reset
+    LOG(INFO) << "Resetting board at http://" << conf.getSconeHost() << ":" << conf.getSconePort()
+              << "/" << conf.getSconeBoard() << " before exiting.. ";
+    int res = tools::reset_board(conf.getSconeHost(), conf.getSconePort(), conf.getSconeBoard());
+    if (res != 0) {
+      LOG(ERROR) << "Reset failed! Error code: " << res;
+    } else {
+      LOG(INFO) << "Successfully reset the board.";
+    }
+  }
+
+  LOG(DEBUG) << "Terminating the internal TCP server.";
+  io_service.stop();
+  if (!fServerThread.try_join_for(boost::chrono::milliseconds(2000))) {
+    LOG(ERROR) << "Failed to terminate the internal TCP server, the program "
+                  "may crash on exit.";
+  } else {
+    LOG(DEBUG) << "Internal TCP server is terminated.";
+  }
+
+  return retval;
+}
+
+int MultiPipeline::RunPipelines(uint numStreams) {
+  tbb::task_group group;
+  // Run pipelines and measure real time
+  tbb::tick_count t0 = tbb::tick_count::now();
+  for (uint i = 0; i < numStreams; ++i) {
+    group.run([p = &this->fPipelines.at(i)] {
+      return p->fPipeline.run(p->fConf.getNumThreads() * p->fMaxTokensPerThread);
+    });
+  }
+  group.wait();
+  tbb::tick_count t1 = tbb::tick_count::now();
+
+  if (fVerbose) {
+    LOG(INFO) << "time = " << (t1 - t0).seconds();
+  }
+  return 1;
+}
\ No newline at end of file
diff --git a/src/pipeline.h b/src/pipeline.h
new file mode 100644
index 0000000000000000000000000000000000000000..b3818e9b3247f036e5c900423992d609654bf458
--- /dev/null
+++ b/src/pipeline.h
@@ -0,0 +1,72 @@
+#ifndef PIPELINE_H
+#define PIPELINE_H
+
+#include <sysexits.h>
+
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <memory>
+
+#include "DmaInputFilter.h"
+#include "FileDmaInputFilter.h"
+#include "InputFilter.h"
+#include "MicronDmaInputFilter.h"
+#include "OutputByOrbit.h"
+#include "OutputBySize.h"
+#include "TcpInputFilter.h"
+#include "WZDmaInputFilter.h"
+#include "config.h"
+#include "controls.h"
+#include "format.h"
+#include "processor.h"
+#include "server.h"
+#include "tbb/concurrent_queue.h"
+#include "tbb/pipeline.h"
+#include "tbb/task_group.h"
+#include "tbb/task_scheduler_init.h"
+#include "tbb/tbb_allocator.h"
+#include "tbb/tick_count.h"
+#include "tools.h"
+
+class Pipeline {
+  friend class MultiPipeline;
+  using OutputFilter = tbb::filter;
+
+ public:
+  static ctrl control;
+  // Impacts the maximum number of in-flight TBB tokens
+  const uint fMaxTokensPerThread;
+  config fConf;
+
+  std::shared_ptr<InputFilter> fInputFilter;
+  StreamProcessor fStreamProcessor;
+  std::shared_ptr<OutputFilter> fOutputStream;
+  tbb::pipeline fPipeline;
+
+  explicit Pipeline(config &conf, uint maxTokensPerThread = 4);
+};
+
+class MultiPipeline {
+ public:
+  std::deque<Pipeline> fPipelines;
+
+  tbb::task_scheduler_init fSchedulerInit;
+  boost::asio::io_service io_service;
+  boost::thread fServerThread;
+  std::unique_ptr<server> fServer;
+  bool fVerbose = true;
+
+  explicit MultiPipeline(std::vector<config> &configs);
+  int Run();
+
+ private:
+  int RunPipelines(uint numStreams);
+};
+
+#endif
\ No newline at end of file
diff --git a/src/scdaq.cc b/src/scdaq.cc
index 1bb5ee8595ab57c03a7aaae4aa9f3890ef0cfb29..1843b92a01024f20efa8fbeae4f3ded081f95d7a 100644
--- a/src/scdaq.cc
+++ b/src/scdaq.cc
@@ -1,120 +1,39 @@
-#include <sysexits.h>
-
-#include <boost/asio.hpp>
-#include <boost/bind.hpp>
-#include <boost/thread.hpp>
-#include <cctype>
-#include <cstdio>
-#include <cstdlib>
 #include <cstring>
 #include <iostream>
-#include <string>
+#include <memory>
 
-#include "DmaInputFilter.h"
-#include "FileDmaInputFilter.h"
-#include "InputFilter.h"
-#include "MicronDmaInputFilter.h"
-#include "OutputByOrbit.h"
-#include "OutputBySize.h"
-#include "TcpInputFilter.h"
-#include "WZDmaInputFilter.h"
 #include "config.h"
-#include "controls.h"
-#include "format.h"
 #include "log.h"
-#include "processor.h"
-#include "server.h"
-#include "tbb/concurrent_queue.h"
-#include "tbb/pipeline.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/tbb_allocator.h"
-#include "tbb/tick_count.h"
-#include "tools.h"
-
-using namespace std;
-
-int run_pipeline(int nbThreads, ctrl &control, config &conf) {
-  config::InputType input = conf.getInput();
-  size_t packetBufferSize = conf.getDmaPacketBufferSize();
-  size_t nbPacketBuffers = conf.getNumberOfDmaPacketBuffers();
-  uint32_t tcpDestPort = conf.getTcpDestPort();
-
-  // Create empty input reader, will assign later when we know what is the data
-  // source
-  std::shared_ptr<InputFilter> input_filter;
-  std::shared_ptr<OutputByOrbitStream> output_stream_by_orbit;
-  std::shared_ptr<OutputBySizeStream> output_stream_by_size;
-  // Create the pipeline
-  tbb::pipeline pipeline;
+#include "pipeline.h"
 
-  if (input == config::InputType::FILEDMA) {
-    // Create FILE DMA reader
-    input_filter = std::make_shared<FileDmaInputFilter>(conf.getInputFile(), packetBufferSize,
-                                                        nbPacketBuffers, control);
+#define MAX_PIPELINES 15
 
-  } else if (input == config::InputType::WZDMA) {
-    // Create WZ DMA reader
-    input_filter =
-        std::make_shared<WZDmaInputFilter>(packetBufferSize, nbPacketBuffers, control, conf);
-
-  } else if (input == config::InputType::MICRONDMA) {
-    // create MicronDmaInputFilter reader
-    input_filter =
-        std::make_shared<MicronDmaInputFilter>(packetBufferSize, nbPacketBuffers, control, conf);
-
-  } else if (input == config::InputType::TCPIP) {
-    // create TcpInputFilter reader
-    if (conf.getDev_TCPAutoReconnectOnFailure()) {
-      input_filter =
-          std::make_shared<TcpInputFilter>(tcpDestPort, packetBufferSize, nbPacketBuffers,
-                                           conf.getNOrbitsPerPacket(), control, conf);
+void LoadConfigs(std::string &basename, uint numStreams, std::vector<config> &out_configs) {
+  if (!numStreams) {
+    // No number of streams specified, assumes unique config file with provided basename
+    out_configs.emplace_back(basename);
+  } else {
+    // Assumes basename followed by a natural integer between 1..N streams
+    std::string::size_type dot_position = basename.find_last_of('.');
+    std::string base, postfix{};
+    if (dot_position == std::string::npos) {
+      base = basename;
     } else {
-      throw std::invalid_argument(
-          "Configuration error: enable developmentMode to use TCP input filter");
+      base = basename.substr(0, dot_position);
+      postfix = "." + basename.substr(dot_position + 1, std::string::npos);
     }
 
-  } else {
-    throw std::invalid_argument("Configuration error: Unknown input type was specified");
-  }
-
-  // Add input reader to a pipeline
-  pipeline.add_filter(*input_filter);
-
-  // Create reformatter and add it to the pipeline
-  // TODO: Created here so we are not subject of scoping, fix later...
-  StreamProcessor stream_processor(packetBufferSize, conf.getDoZS(), conf.getProcessorType(),
-                                   conf.getNOrbitsPerPacket(), conf.getCMSSWHeaders(),
-                                   conf.getSourceID(), control);
-  if (conf.getEnableStreamProcessor()) {
-    pipeline.add_filter(stream_processor);
-  }
-
-  // Create file-writing stage and add it to the pipeline
-  if (conf.getNOrbitsPerFile()) {
-    output_stream_by_orbit = std::make_shared<OutputByOrbitStream>(control, conf);
-    pipeline.add_filter(*output_stream_by_orbit);
-  } else {
-    output_stream_by_size = std::make_shared<OutputBySizeStream>(conf.getOutputFilenameBase(),
-                                                                 conf.getOutputFilenamePrefix(),
-                                                                 control, conf.getProcessorType());
-    pipeline.add_filter(*output_stream_by_size);
+    for (uint i = 0; i < numStreams; ++i) {
+      out_configs.emplace_back(base + std::to_string(i + 1) + postfix);
+    }
   }
-
-  // Run the pipeline
-  tbb::tick_count t0 = tbb::tick_count::now();
-  // Need more than one token in flight per thread to keep all threads
-  // busy; 2-4 works
-  pipeline.run(nbThreads * 4);
-  tbb::tick_count t1 = tbb::tick_count::now();
-  LOG(INFO) << "time = " << (t1 - t0).seconds();
-
-  return 1;
 }
 
+ctrl Pipeline::control;
+
 int main(int argc, char *argv[]) {
   (void)(argc);
   (void)(argv);
-  int retval = 0;
 
   if (argc < 2) {
     LOG(DEBUG) << "no arguments provided to scdaq, try --help";
@@ -122,90 +41,54 @@ int main(int argc, char *argv[]) {
   }
 
   if ((std::string(argv[1]) == "-h") || (std::string(argv[1]) == "--help")) {
-    LOG(DEBUG) << "HELP: expected arguments --config [configfilename]";
+    LOG(DEBUG) << "HELP: expected arguments --config [config basename] --nstreams [number of "
+                  "parallel streams]";
     return 1;
   }
 
-  if ((argc != 3)) {
-    LOG(ERROR) << "error occurred, number of arguments != 2, expected --config "
-                  "[configfilename] , try --help";
+  if ((argc < 3)) {
+    LOG(ERROR) << "error occurred, number of arguments is less than 2, expected --config [config "
+                  "basename] (and optionally --nstreams [number of parallel streams]), try --help";
     return 1;
   }
 
+  uint num_streams = 0;
+  std::string config_basename{};
+
   if (std::string(argv[1]) == "--config") {
-    LOG(DEBUG) << "scdaq started with conffile:   " << std::string(argv[2]);
+    LOG(DEBUG) << "scdaq started with config files basename:   " << std::string(argv[2]);
+    config_basename.assign(argv[2]);
   } else {
     LOG(ERROR) << "invalid argument, expected --config, see --help";
     return 1;
   }
 
-  try {
-    config conf(argv[2]);
-    conf.print();
-    tools::log::set_filter_min_severity(conf.getLogMinSeverity());
-    LOG(DEBUG) << "Configuration loaded";
-    ctrl control;
-    //    tbb::tick_count mainStartTime = tbb::tick_count::now();
-
-    control.running = false;
-    control.run_number = 0;
-    control.max_file_size = conf.getOutputMaxFileSize();  // in Bytes
-    control.packets_per_report = conf.getPacketsPerReport();
-    control.output_force_write = conf.getOutputForceWrite();
-    control.n_orbits_per_packet = conf.getNOrbitsPerPacket();
-
-    // Firmware needs at least 1MB buffer for DMA
-    if (conf.getDmaPacketBufferSize() < 1024 * 1024) {
-      LOG(ERROR) << "dma_packet_buffer_size must be at least 1048576 bytes (1MB), but "
-                 << conf.getDmaPacketBufferSize()
-                 << " bytes was given. Check the configuration file.";
-      return 1;
-    }
-
-    boost::asio::io_service io_service;
-    server s(io_service, conf.getPortNumber(), control);
-    boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
-
-    int nbThreads = conf.getNumThreads();
-
-    retval = 0;
-    try {
-      tbb::task_scheduler_init init(nbThreads);
-      if (!run_pipeline(nbThreads, control, conf)) {
-        retval = 1;
-        // Will terminate with errorocde set
-      }
-    } catch (std::exception &e) {
-      LOG(ERROR) << "Error in pipelines. Error text is: \"" << e.what() << "\"";
-      retval = 1;
-      // Will terminate with errorcode set
-      LOG(INFO) << "Resetting board at http://" << conf.getSconeHost() << ":" << conf.getSconePort()
-                << "/" << conf.getSconeBoard() << " before exiting.. ";
-      int res = tools::reset_board(conf.getSconeHost(), conf.getSconePort(), conf.getSconeBoard());
-      if (res != 0) {
-        LOG(ERROR) << "Reset failed! Error code: " << res;
-      } else {
-        LOG(INFO) << "Successfully reset the board.";
-      }
-    }
+  if (argc >= 5 && std::string(argv[3]) == "--nstreams") {
+    num_streams = static_cast<uint>(std::stoi(argv[4]));
+    LOG(DEBUG) << "scdaq started with number of parallel streams / config files:   "
+               << std::string(argv[4]);
+    // Check if `nstreams` exceeds maximum number of pipelines --> recompile with a larger
+    // MAX_PIPELINES if necessary.
+    assert(num_streams <= MAX_PIPELINES);
+  } else {
+    LOG(WARNING) << "scdaq is implicitly processing a single stream configured at "
+                 << config_basename;
+  }
 
-    LOG(DEBUG) << "Terminating the internal TCP server.";
-    io_service.stop();
-    if (!t.try_join_for(boost::chrono::milliseconds(2000))) {
-      LOG(ERROR) << "Failed to terminate the internal TCP server, the program "
-                    "may crash on exit.";
-    } else {
-      LOG(DEBUG) << "Internal TCP server is terminated.";
-    }
+  // Collect loaded config instances from configuration files
+  std::vector<config> configs{};
+  LoadConfigs(config_basename, num_streams, configs);
 
-    //    utility::report_elapsed_time((tbb::tick_count::now() -
-    //    mainStartTime).seconds());
-    return retval;
-  } catch (std::invalid_argument &e) {
-    LOG(FATAL) << "Configuration invalid! Error text is \"" << e.what() << "\" Bailing out.";
-    return EX_CONFIG;
+  // Instantiate multiple pipelines (up to the directive-defined limit) with the settings from the
+  // config instances.
+  MultiPipeline multi_pipeline(configs);
+  int ret = 0;
+  try {
+    // Launch blocking call to run the pipelines in parallel until all streams are severed
+    ret = multi_pipeline.Run();
   } catch (std::exception &e) {
-    LOG(ERROR) << "Error occurred. error text is: \"" << e.what() << "\"";
+    LOG(ERROR) << "Error in pipelines. Error text is: \"" << e.what() << "\"";
     return 1;
   }
+  return ret;
 }
diff --git a/src/wz_dma.c b/src/wz_dma.c
index c2f18d5cafad42c6e63336eb999413dad3e1c46e..0b12b89895e43bd1588691ba48ed04eddd1ce94f 100644
--- a/src/wz_dma.c
+++ b/src/wz_dma.c
@@ -2,189 +2,184 @@
  * This file defines the API for the DWZ DMA driver.
  */
 
-#include <stdio.h>
-#include <stdint.h>
-#include <unistd.h>
+#include "wz_dma.h"
+
+#include <errno.h>
 #include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
-#include <errno.h>
-
-#include "wz_dma.h"
+#include <unistd.h>
 
 // perror may modify errno, we have to save it
-#define PERROR(msg) do { int err = errno; perror(msg); errno = err; } while (0)
+#define PERROR(msg)  \
+  do {               \
+    int err = errno; \
+    perror(msg);     \
+    errno = err;     \
+  } while (0)
 
 /*
  * Open necessary devices and map DMAable memory.
- * Variable usr_regs_size is the size (in power of 2) of the user memory space that should be allocated for I/O communication with the hardware.
- * Set to 0 if not necessary.
+ * Variable usr_regs_size is the size (in power of 2) of the user memory space that should be
+ * allocated for I/O communication with the hardware. Set to 0 if not necessary.
  */
-int wz_init(struct wz_private* wz, size_t usr_regs_size)
-{
-	int res;
-
-	wz->fd_user = -1;
-	wz->fd_control = -1;
-	wz->fd_memory = -1;
-	wz->fd_memory_is_allocated = 0;
-	wz->usr_regs_size = usr_regs_size;
-	wz->usr_regs = NULL;
-	wz->data_buf = NULL;
-
-
-	if ( (wz->fd_user = open("/dev/wz-xdma0_user", O_RDWR)) < 0 ) {
-		wz->fd_user = -1;
-		PERROR("Can't open /dev/wz-xdma0_user");
-		return -1;
-	};
-
-	if ( (wz->fd_control = open("/dev/wz-xdma0_control", O_RDWR)) < 0 ) {
-		wz->fd_control = -1;
-		PERROR("Can't open /dev/wz-xdma0_control");
-		wz_close(wz);
-		return -1;
-	};
-
-	if ( (wz->fd_memory = open("/dev/wz-xdma0_c2h_0", O_RDWR)) < 0 ) {
-		wz->fd_memory = -1;
-		PERROR("Can't open /dev/wz-xdma0_c2h_0");
-		wz_close(wz);
-		return -1;
-	};
-
-	//Allocate buffers
-	if ( (res = ioctl(wz->fd_memory, IOCTL_XDMA_WZ_ALLOC_BUFFERS, 0L)) < 0 ) {
-		PERROR("Can't mmap DMA buffers");
-		wz_close(wz);
-		return -1;
-	}
-	wz->fd_memory_is_allocated = 1;
-
-	//Now mmap the user registers
-	if (wz->usr_regs_size && ((wz->usr_regs = (volatile uint32_t *) mmap(NULL, wz->usr_regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, wz->fd_user, 0)) == MAP_FAILED)) {
-		wz->usr_regs = NULL;
-		PERROR("Can't mmap user registers");
-		wz_close(wz);
-		return -1;	
-	}
-
-	if ( (wz->data_buf = (volatile char *) mmap(NULL, TOT_BUF_LEN, PROT_READ|PROT_WRITE, MAP_SHARED, wz->fd_memory, 0)) == MAP_FAILED) { 
-		wz->data_buf = NULL;
-		PERROR("Can't mmap data buffer");
-		wz_close(wz);
-		return -1;
-	}
-
-	// Ensure, that all pages are mapped (allocated)
-	// Assume page size is 4096. If it is larger, it will not hurt, just take long(er) time
-	int pagesize = 4096;
-	int64_t i;
-	for (i = 0; i < TOT_BUF_LEN; i += pagesize) {
-		// Touch memory so it is really allocated
-        ((volatile char*) wz->data_buf)[i] = ((volatile char*) wz->data_buf)[i];
-	}
-
-	return 0;
+int wz_init(struct wz_private* wz, size_t usr_regs_size) {
+  int res;
+
+  wz->fd_user = -1;
+  wz->fd_control = -1;
+  wz->fd_memory = -1;
+  wz->fd_memory_is_allocated = 0;
+  wz->usr_regs_size = usr_regs_size;
+  wz->usr_regs = NULL;
+  wz->data_buf = NULL;
+
+  if ((wz->fd_user = open("/dev/wz-xdma0_user", O_RDWR)) < 0) {
+    wz->fd_user = -1;
+    PERROR("Can't open /dev/wz-xdma0_user");
+    return -1;
+  };
+
+  if ((wz->fd_control = open("/dev/wz-xdma0_control", O_RDWR)) < 0) {
+    wz->fd_control = -1;
+    PERROR("Can't open /dev/wz-xdma0_control");
+    wz_close(wz);
+    return -1;
+  };
+
+  if ((wz->fd_memory = open("/dev/wz-xdma0_c2h_0", O_RDWR)) < 0) {
+    wz->fd_memory = -1;
+    PERROR("Can't open /dev/wz-xdma0_c2h_0");
+    wz_close(wz);
+    return -1;
+  };
+
+  // Allocate buffers
+  if ((res = ioctl(wz->fd_memory, IOCTL_XDMA_WZ_ALLOC_BUFFERS, 0L)) < 0) {
+    PERROR("Can't mmap DMA buffers");
+    wz_close(wz);
+    return -1;
+  }
+  wz->fd_memory_is_allocated = 1;
+
+  // Now mmap the user registers
+  if (wz->usr_regs_size &&
+      ((wz->usr_regs = (volatile uint32_t*)mmap(NULL, wz->usr_regs_size, PROT_READ | PROT_WRITE,
+                                                MAP_SHARED, wz->fd_user, 0)) == MAP_FAILED)) {
+    wz->usr_regs = NULL;
+    PERROR("Can't mmap user registers");
+    wz_close(wz);
+    return -1;
+  }
+
+  if ((wz->data_buf = (volatile char*)mmap(NULL, TOT_BUF_LEN, PROT_READ | PROT_WRITE, MAP_SHARED,
+                                           wz->fd_memory, 0)) == MAP_FAILED) {
+    wz->data_buf = NULL;
+    PERROR("Can't mmap data buffer");
+    wz_close(wz);
+    return -1;
+  }
+
+  // Ensure, that all pages are mapped (allocated)
+  // Assume page size is 4096. If it is larger, it will not hurt, just take long(er) time
+  int pagesize = 4096;
+  int64_t i;
+  for (i = 0; i < TOT_BUF_LEN; i += pagesize) {
+    // Touch memory so it is really allocated
+    ((volatile char*)wz->data_buf)[i] = ((volatile char*)wz->data_buf)[i];
+  }
+
+  return 0;
 }
 
 /* The opposite of wz_init */
-int wz_close(struct wz_private* wz) 
-{
-	if (wz->data_buf) {
-		munmap((void *)wz->data_buf, TOT_BUF_LEN);
-	}
-	if (wz->usr_regs) {
-		munmap((void *)wz->usr_regs, wz->usr_regs_size);
-	}
-	if (wz->fd_memory_is_allocated) {
-		ioctl(wz->fd_memory, IOCTL_XDMA_WZ_FREE_BUFFERS, 0L);
-	}
-	if (wz->fd_memory != -1) {
-		close(wz->fd_memory);
-	}
-	if (wz->fd_control != -1) {
-		close(wz->fd_control);
-	}
-	if (wz->fd_user != -1) {
-		close(wz->fd_user);
-	}
-
-	wz->fd_user = -1;
-	wz->fd_control = -1;
-	wz->fd_memory = -1;
-	wz->fd_memory_is_allocated = 0;
-	wz->usr_regs = NULL;
-	wz->data_buf = NULL;
-
-	return 0;
+int wz_close(struct wz_private* wz) {
+  if (wz->data_buf) {
+    munmap((void*)wz->data_buf, TOT_BUF_LEN);
+  }
+  if (wz->usr_regs) {
+    munmap((void*)wz->usr_regs, wz->usr_regs_size);
+  }
+  if (wz->fd_memory_is_allocated) {
+    ioctl(wz->fd_memory, IOCTL_XDMA_WZ_FREE_BUFFERS, 0L);
+  }
+  if (wz->fd_memory != -1) {
+    close(wz->fd_memory);
+  }
+  if (wz->fd_control != -1) {
+    close(wz->fd_control);
+  }
+  if (wz->fd_user != -1) {
+    close(wz->fd_user);
+  }
+
+  wz->fd_user = -1;
+  wz->fd_control = -1;
+  wz->fd_memory = -1;
+  wz->fd_memory_is_allocated = 0;
+  wz->usr_regs = NULL;
+  wz->data_buf = NULL;
+
+  return 0;
 }
 
 /* Start the DMA engine */
-inline int wz_start_dma(struct wz_private* wz)
-{
-	return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_START, 0L);
+inline int wz_start_dma(struct wz_private* wz) {
+  return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_START, 0L);
 }
 
 /* Stop the DMA engine */
-inline int wz_stop_dma(struct wz_private* wz)
-{
-	return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_STOP, 0L);
+inline int wz_stop_dma(struct wz_private* wz) {
+  return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_STOP, 0L);
 }
 
-
-static inline int wz_get_buf(struct wz_private* wz) 
-{
-	return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_GETBUF, (long) &wz->bdesc);
+static inline int wz_get_buf(struct wz_private* wz) {
+  return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_GETBUF, (long)&wz->bdesc);
 }
 
-static inline int wz_confirm_buf(struct wz_private* wz)
-{		
-	return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_CONFIRM, (long) &wz->bconf);
+static inline int wz_confirm_buf(struct wz_private* wz) {
+  return ioctl(wz->fd_memory, IOCTL_XDMA_WZ_CONFIRM, (long)&wz->bconf);
 }
 
-
 /* Acquire and return buffer */
-inline ssize_t wz_read_start(struct wz_private* wz, char **buffer)
-{
-	int ret;
-	if ( (ret = wz_get_buf( wz )) < 0) {
-		return ret;
-	}
+inline ssize_t wz_read_start(struct wz_private* wz, char** buffer) {
+  int ret;
+  if ((ret = wz_get_buf(wz)) < 0) {
+    return ret;
+  }
 
-	int64_t start_offset = (int64_t)wz->bdesc.first_desc * (int64_t)WZ_DMA_BUFLEN;
-	int64_t end_offset   = (int64_t)wz->bdesc.last_desc * (int64_t)WZ_DMA_BUFLEN + (int64_t) wz->bdesc.last_len;
-	int64_t bytes_read = end_offset - start_offset;
+  int64_t start_offset = (int64_t)wz->bdesc.first_desc * (int64_t)WZ_DMA_BUFLEN;
+  int64_t end_offset =
+      (int64_t)wz->bdesc.last_desc * (int64_t)WZ_DMA_BUFLEN + (int64_t)wz->bdesc.last_len;
+  int64_t bytes_read = end_offset - start_offset;
 
-	*buffer = (char *)(wz->data_buf + start_offset);
+  *buffer = (char*)(wz->data_buf + start_offset);
 
-	return bytes_read;
+  return bytes_read;
 }
 
-
 /* Mark the buffer to be available for DMA */
-inline int wz_read_complete(struct wz_private* wz)
-{
-	wz->bconf.first_desc = wz->bdesc.first_desc;
-	wz->bconf.last_desc = wz->bdesc.last_desc;
+inline int wz_read_complete(struct wz_private* wz) {
+  wz->bconf.first_desc = wz->bdesc.first_desc;
+  wz->bconf.last_desc = wz->bdesc.last_desc;
 
-	return wz_confirm_buf( wz );
+  return wz_confirm_buf(wz);
 }
 
-
-/* 
- * The user logic is driving custom FPGA logic. This is not necessary to use if not implemented in the FPGA.
+/*
+ * The user logic is driving custom FPGA logic. This is not necessary to use if not implemented in
+ * the FPGA.
  */
 
-inline void wz_start_source(struct wz_private* wz)
-{
-	wz->usr_regs[0x10000/4] = 1;
-	asm volatile ("" : : : "memory");
+inline void wz_start_source(struct wz_private* wz) {
+  wz->usr_regs[0x10000 / 4] = 1;
+  asm volatile("" : : : "memory");
 }
 
 // User logic
-inline void wz_stop_source(struct wz_private* wz)
-{
-	wz->usr_regs[0x10000/4] = 0;
-	asm volatile ("" : : : "memory");
+inline void wz_stop_source(struct wz_private* wz) {
+  wz->usr_regs[0x10000 / 4] = 0;
+  asm volatile("" : : : "memory");
 }
diff --git a/test/config/scdaq-calo-cmssw.conf b/test/config/scdaq-calo-cmssw.conf
index 271647177e7647c0e4cafb618029305a37a104a4..9a2acba1cd19d643b3886c2ca7b07590747fc8dc 100644
--- a/test/config/scdaq-calo-cmssw.conf
+++ b/test/config/scdaq-calo-cmssw.conf
@@ -78,6 +78,9 @@ output_force_write:yes
 ##
 ################################################################################
 
+# enable development functionalities (e.g. TCP input filter)
+dev_TCPAutoReconnectOnFailure:false
+
 #scdaq port
 port:8000
 
diff --git a/test/config/scdaq-passthrough.conf b/test/config/scdaq-passthrough.conf
new file mode 100644
index 0000000000000000000000000000000000000000..865f229003ce3da87113078136b798b8be87df76
--- /dev/null
+++ b/test/config/scdaq-passthrough.conf
@@ -0,0 +1,127 @@
+################################################################################
+##
+## Input settings
+##
+################################################################################
+
+# Input settings, allowed values are:
+#   "wzdma"     for DMA driver from Wojciech M. Zabolotny
+#   "dma"       for XILINX DMA driver
+#   "filedma"   for reading from file and simulating DMA
+#   "micronDMA" for PICO driver
+#   "tcpip"     for TCP/IP input receving
+
+input:filedma
+
+## Settings for DMA input
+
+# DMA device
+dma_dev:/dev/xdma0_c2h_0
+
+# Max received packet size in bytes (buffer to reserve)
+dma_packet_buffer_size:1261568
+
+# Number of packet buffers to allocate
+dma_number_of_packet_buffers:1000
+
+# Print report each N packets, use 0 to disable
+packets_per_report:2000
+
+# number of orbits per packet, in decimal
+nOrbitsPerPacket:4
+
+## Extra settings for "filedma" input
+input_file:./test/data/calo_testfile.dat
+
+## Extra settings for "tcpip" input
+tcpDestPort:10000
+
+
+################################################################################
+##
+## Stream processor settings
+##
+################################################################################
+
+enable_stream_processor:yes
+
+# Define processing type (unpacking), allowed values are:
+#   "PASS_THROUGH"
+#   "GMT"
+#   "CALO"
+# Note: When changing the processing type, change also "output_filename_prefix"
+# in the file output section.
+#
+processor_type:PASS_THROUGH
+
+# Enable software zero-supression
+doZS:yes
+
+################################################################################
+##
+## File output settings
+##
+################################################################################
+
+output_filename_prefix:scout_PASS0_GMT
+
+output_filename_base:test/data
+
+max_file_size:8589934592
+
+# Always write data to a file regardless of the run status, useful for debugging
+output_force_write:yes
+
+
+################################################################################
+##
+## Elastics processor settings (obsolete)
+##
+################################################################################
+
+enable_elastic_processor:no
+
+port:8000
+elastic_url:http://something.somewhere
+pt_cut:7
+quality_cut:12
+
+
+################################################################################
+##
+## SCDAQ Generic Settings
+##
+################################################################################
+
+# enable development functionalities (e.g. TCP input filter)
+dev_TCPAutoReconnectOnFailure:false
+
+## Logging, supported LOG severities:
+#   TRACE
+#   DEBUG
+#   INFO
+#   WARNING
+#   ERROR
+#   FATAL
+#
+# Log only severities at the same level or more severe than the log_min_severity
+# Use TRACE to log everything
+#
+log_min_severity:ERROR
+
+# Pipeline settings
+threads:8
+
+# N orbits to store to each file
+# Configured to store fixed number of orbits per file when nOrbitsPerFile > 1
+# Set to 0 to use fixed file size instead
+nOrbitsPerFile:0
+
+# Headers for cmssw support
+cmsswHeaders:no
+
+##  Information necessary to issue a reset request for the board
+scone_host:localhost
+scone_port:8080
+# Currently can be one of kcu1500_ugmt and kcu1500_demux
+scone_board:kcu1500_ugmt
diff --git a/test/config/scdaq-passthrough1.conf b/test/config/scdaq-passthrough1.conf
new file mode 100644
index 0000000000000000000000000000000000000000..9139d7d0abc89dd8e2ae450fda936d764ab4b5fd
--- /dev/null
+++ b/test/config/scdaq-passthrough1.conf
@@ -0,0 +1,127 @@
+################################################################################
+##
+## Input settings
+##
+################################################################################
+
+# Input settings, allowed values are:
+#   "wzdma"     for DMA driver from Wojciech M. Zabolotny
+#   "filedma"   for reading from file and simulating DMA
+#   "micronDMA" for PICO driver
+#   "tcpip"     for TCP/IP input receving
+
+input:filedma
+
+## Settings for DMA input
+
+# DMA device
+dma_dev:/dev/xdma0_c2h_0
+
+# Max received packet size in bytes (buffer to reserve)
+dma_packet_buffer_size:1261568
+
+# Number of packet buffers to allocate
+dma_number_of_packet_buffers:1000
+
+# Print report each N packets, use 0 to disable
+packets_per_report:2000
+
+# number of orbits per packet, in decimal
+nOrbitsPerPacket:4
+
+## Extra settings for "filedma" input
+input_file:./test/data/calo_testfile.dat
+
+## Extra settings for "tcpip" input
+tcpDestPort:10000
+
+
+################################################################################
+##
+## Stream processor settings
+##
+################################################################################
+
+enable_stream_processor:yes
+
+# Define processing type (unpacking), allowed values are:
+#   "PASS_THROUGH"
+#   "GMT"
+#   "CALO"
+#   "BRIL"
+# Note: When changing the processing type, change also "output_filename_prefix"
+# in the file output section.
+#
+processor_type:PASS_THROUGH
+
+# Enable software zero-supression
+doZS:yes
+
+################################################################################
+##
+## File output settings
+##
+################################################################################
+
+output_filename_prefix:scout_PASS1_CALO
+
+output_filename_base:test/data
+
+max_file_size:8589934592
+
+# Always write data to a file regardless of the run status, useful for debugging
+output_force_write:yes
+
+
+################################################################################
+##
+## Elastics processor settings (obsolete)
+##
+################################################################################
+
+enable_elastic_processor:no
+
+port:8000
+elastic_url:http://something.somewhere
+pt_cut:7
+quality_cut:12
+
+
+################################################################################
+##
+## SCDAQ Generic Settings
+##
+################################################################################
+
+# enable development functionalities (e.g. TCP input filter)
+dev_TCPAutoReconnectOnFailure:false
+
+## Logging, supported LOG severities:
+#   TRACE
+#   DEBUG
+#   INFO
+#   WARNING
+#   ERROR
+#   FATAL
+#
+# Log only severities at the same level or more severe than the log_min_severity
+# Use TRACE to log everything
+#
+log_min_severity:ERROR
+
+# Pipeline settings
+threads:8
+
+# N orbits to store to each file
+# Configured to store fixed number of orbits per file when nOrbitsPerFile > 1
+# Set to 0 to use fixed file size instead
+nOrbitsPerFile:0
+
+# Headers for cmssw support
+cmsswHeaders:no
+
+##  Information necessary to issue a reset request for the board
+scone_host:localhost
+scone_port:8080
+# Currently can be one of kcu1500_ugmt and kcu1500_demux
+scone_board:kcu1500_ugmt
diff --git a/test/config/scdaq-passthrough2.conf b/test/config/scdaq-passthrough2.conf
new file mode 100644
index 0000000000000000000000000000000000000000..6e4e0e665b60943ce6d93e6363ea1f4239a09ae5
--- /dev/null
+++ b/test/config/scdaq-passthrough2.conf
@@ -0,0 +1,131 @@
+################################################################################
+##
+## Input settings
+##
+################################################################################
+
+# Input settings, allowed values are:
+#   "wzdma"     for DMA driver from Wojciech M. Zabolotny
+#   "dma"       for XILINX DMA driver
+#   "filedma"   for reading from file and simulating DMA
+#   "micronDMA" for PICO driver
+#   "tcpip"     for TCP/IP input receving
+
+input:filedma
+
+## Settings for DMA input
+
+# DMA device
+dma_dev:/dev/xdma0_c2h_0
+
+# Max received packet size in bytes (buffer to reserve)
+dma_packet_buffer_size:1261568
+
+# Number of packet buffers to allocate
+dma_number_of_packet_buffers:1000
+
+# Print report each N packets, use 0 to disable
+packets_per_report:2000
+
+# number of orbits per packet, in decimal
+nOrbitsPerPacket:4
+
+# Extra settings for "filedma" input
+input_file:./test/data/gmt_testfile.dat
+
+## Extra settings for "tcpip" input
+tcpDestPort:10001
+
+
+################################################################################
+##
+## Stream processor settings
+##
+################################################################################
+
+enable_stream_processor:yes
+
+# Define processing type (unpacking), allowed values are:
+#   "PASS_THROUGH"
+#   "GMT"
+#   "CALO"
+#   "BRIL"
+# Note: When changing the processing type, change also "output_filename_prefix"
+# in the file output section.
+
+processor_type:PASS_THROUGH
+
+# Enable software zero-supression
+doZS:yes
+
+################################################################################
+##
+## File output settings
+##
+################################################################################
+
+output_filename_prefix:scout_PASS2_GMT
+
+output_filename_base:test/data
+
+max_file_size:8589934592
+
+# Always write data to a file regardless of the run status, useful for debugging
+output_force_write:yes
+
+
+################################################################################
+##
+## Elastics processor settings (obsolete)
+##
+################################################################################
+
+enable_elastic_processor:no
+
+port:8000
+elastic_url:http://something.somewhere
+pt_cut:7
+quality_cut:12
+
+
+################################################################################
+##
+## SCDAQ Generic Settings
+##
+################################################################################
+
+# enable development functionalities (e.g. TCP input filter)
+dev_TCPAutoReconnectOnFailure:false
+
+## Logging, supported LOG severities:
+#   TRACE
+#   DEBUG
+#   INFO
+#   WARNING
+#   ERROR
+#   FATAL
+#
+# Log only severities at the same level or more severe than the log_min_severity
+# Use TRACE to log everything
+#
+log_min_severity:ERROR
+
+# Pipeline settings
+threads:8
+
+# verbosity level, currently supports 0 and 1
+verbosity:0
+
+# N orbits to store to each file
+# Configured to store fixed number of orbits per file when nOrbitsPerFile > 1
+# Set to 0 to use fixed file size instead
+nOrbitsPerFile:0
+
+# Headers for cmssw support
+cmsswHeaders:no
+
+##  Information necessary to issue a reset request for the board
+scone_host:localhost
+scone_port:8080
+# Currently can be one of kcu1500_ugmt and kcu1500_demux
+scone_board:kcu1500_ugmt
diff --git a/test/config/tcp-test.conf b/test/config/tcp-test.conf
new file mode 100644
index 0000000000000000000000000000000000000000..23d30532b1be5dd55c41dba91a12d6a067894be6
--- /dev/null
+++ b/test/config/tcp-test.conf
@@ -0,0 +1,112 @@
+################################################################################
+##
+## Input settings
+##
+################################################################################
+
+# Input settings, allowed values are:
+#   "wzdma"     for DMA driver from Wojciech M. Zabolotny
+#   "filedma"   for reading from file and simulating DMA
+#   "micronDMA" for PICO driver
+#   "tcpip"     for TCP/IP input receving
+
+input:filedma
+
+## Settings for DMA input
+
+# DMA device
+dma_dev:/dev/xdma0_c2h_0
+
+# Max received packet size in bytes (buffer to reserve)
+dma_packet_buffer_size:1261568
+
+# Number of packet buffers to allocate
+dma_number_of_packet_buffers:1000
+
+# Print report each N packets, use 0 to disable
+packets_per_report:2000
+
+# number of orbits per packet, in decimal
+nOrbitsPerPacket:1
+
+## Extra settings for "filedma" input
+input_file:./test/data/calo_testfile.dat
+
+## Extra settings for "tcpip" input
+tcpDestPort:9000
+
+
+################################################################################
+##
+## Stream processor settings
+##
+################################################################################
+
+enable_stream_processor:yes
+
+# Define processing type (unpacking), allowed values are:
+#   "PASS_THROUGH"
+#   "GMT"
+#   "CALO"
+#   "BRIL"
+# Note: When changing the processing type, change also "output_filename_prefix"
+# in the file output section.
+#
+processor_type:PASS_THROUGH
+
+# Enable software zero-supression
+doZS:yes
+
+################################################################################
+##
+## File output settings
+##
+################################################################################
+
+output_filename_prefix:tcp-single-scouting_gmt_1
+
+output_filename_base:test/in_progress
+
+max_file_size:8589934592
+
+# Always write data to a file regardless of the run status, useful for debugging
+output_force_write:no
+
+################################################################################
+##
+## SCDAQ Generic Settings
+##
+################################################################################
+
+# enable development functionalities (e.g. TCP input filter)
+dev_TCPAutoReconnectOnFailure:true
+
+## Logging, supported LOG severities:
+#   TRACE
+#   DEBUG
+#   INFO
+#   WARNING
+#   ERROR
+#   FATAL
+#
+# Log only severities at the same level or more severe than the log_min_severity
+# Use TRACE to log everything
+#
+log_min_severity:TRACE
+
+# Pipeline settings
+threads:8
+
+# N orbits to store to each file
+# Configured to store fixed number of orbits per file when nOrbitsPerFile > 1
+# Set to 0 to use fixed file size instead
+nOrbitsPerFile:4096
+
+# Headers for cmssw support
+cmsswHeaders:no
+
+##  Information necessary to issue a reset request for the board
+scone_host:localhost
+scone_port:8080
+# Currently can be one of kcu1500_ugmt and kcu1500_demux
+scone_board:vcu128_bmtf_tcp
diff --git a/test/config/tcp-test1.conf b/test/config/tcp-test1.conf
new file mode 100644
index 0000000000000000000000000000000000000000..0d2a67ebb29018e5f385af25bbf8c1d33bd9edcc
--- /dev/null
+++ b/test/config/tcp-test1.conf
@@ -0,0 +1,112 @@
+################################################################################
+##
+## Input settings
+##
+################################################################################
+
+# Input settings, allowed values are:
+#   "wzdma"     for DMA driver from Wojciech M. Zabolotny
+#   "filedma"   for reading from file and simulating DMA
+#   "micronDMA" for PICO driver
+#   "tcpip"     for TCP/IP input receving
+
+input:tcpip
+
+## Settings for DMA input
+
+# DMA device
+dma_dev:/dev/xdma0_c2h_0
+
+# Max received packet size in bytes (buffer to reserve)
+dma_packet_buffer_size:1261568
+
+# Number of packet buffers to allocate
+dma_number_of_packet_buffers:1000
+
+# Print report each N packets, use 0 to disable
+packets_per_report:2000
+
+# number of orbits per packet, in decimal
+nOrbitsPerPacket:1
+
+## Extra settings for "filedma" input
+input_file:./test/data/calo_testfile.dat
+
+## Extra settings for "tcpip" input
+tcpDestPort:10000
+
+
+################################################################################
+##
+## Stream processor settings
+##
+################################################################################
+
+enable_stream_processor:yes
+
+# Define processing type (unpacking), allowed values are:
+#   "PASS_THROUGH"
+#   "GMT"
+#   "CALO"
+#   "BRIL"
+# Note: When changing the processing type, change also "output_filename_prefix"
+# in the file output section.
+#
+processor_type:PASS_THROUGH
+
+# Enable software zero-supression
+doZS:yes
+
+################################################################################
+##
+## File output settings
+##
+################################################################################
+
+output_filename_prefix:tcp-mp1-scouting_calo_1
+
+output_filename_base:test/in_progress
+
+max_file_size:8589934592
+
+# Always write data to a file regardless of the run status, useful for debugging
+output_force_write:no
+
+################################################################################
+##
+## SCDAQ Generic Settings
+##
+################################################################################
+
+# enable development functionalities (e.g. TCP input filter)
+dev_TCPAutoReconnectOnFailure:true
+
+## Logging, supported LOG severities:
+#   TRACE
+#   DEBUG
+#   INFO
+#   WARNING
+#   ERROR
+#   FATAL
+#
+# Log only severities at the same level or more severe than the log_min_severity
+# Use TRACE to log everything
+#
+log_min_severity:TRACE
+
+# Pipeline settings
+threads:8
+
+# N orbits to store to each file
+# Configured to store fixed number of orbits per file when nOrbitsPerFile > 1
+# Set to 0 to use fixed file size instead
+nOrbitsPerFile:4096
+
+# Headers for cmssw support
+cmsswHeaders:no
+
+##  Information necessary to issue a reset request for the board
+scone_host:localhost
+scone_port:8080
+# Currently can be one of kcu1500_ugmt and kcu1500_demux
+scone_board:vcu128_bmtf_tcp
diff --git a/test/config/tcp-test2.conf b/test/config/tcp-test2.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5ccc067f9036f5c1bfe46545bc90b7e0263aaab7
--- /dev/null
+++ b/test/config/tcp-test2.conf
@@ -0,0 +1,112 @@
+################################################################################
+##
+## Input settings
+##
+################################################################################
+
+# Input settings, allowed values are:
+#   "wzdma"     for DMA driver from Wojciech M. Zabolotny
+#   "filedma"   for reading from file and simulating DMA
+#   "micronDMA" for PICO driver
+#   "tcpip"     for TCP/IP input receving
+
+input:tcpip
+
+## Settings for DMA input
+
+# DMA device
+dma_dev:/dev/xdma0_c2h_0
+
+# Max received packet size in bytes (buffer to reserve)
+dma_packet_buffer_size:1261568
+
+# Number of packet buffers to allocate
+dma_number_of_packet_buffers:1000
+
+# Print report each N packets, use 0 to disable
+packets_per_report:2000
+
+# number of orbits per packet, in decimal
+nOrbitsPerPacket:1
+
+## Extra settings for "filedma" input
+input_file:./test/data/gmt_testfile2.dat
+
+## Extra settings for "tcpip" input
+tcpDestPort:10010
+
+
+################################################################################
+##
+## Stream processor settings
+##
+################################################################################
+
+enable_stream_processor:yes
+
+# Define processing type (unpacking), allowed values are:
+#   "PASS_THROUGH"
+#   "GMT"
+#   "CALO"
+#   "BRIL"
+# Note: When changing the processing type, change also "output_filename_prefix"
+# in the file output section.
+#
+processor_type:PASS_THROUGH
+
+# Enable software zero-supression
+doZS:yes
+
+################################################################################
+##
+## File output settings
+##
+################################################################################
+
+output_filename_prefix:tcp-mp2-scouting_gmt
+
+output_filename_base:test/in_progress
+
+max_file_size:8589934592
+
+# Always write data to a file regardless of the run status, useful for debugging
+output_force_write:no
+
+################################################################################
+##
+## SCDAQ Generic Settings
+##
+################################################################################
+
+# enable development functionalities (e.g. TCP input filter)
+dev_TCPAutoReconnectOnFailure:true
+
+## Logging, supported LOG severities:
+#   TRACE
+#   DEBUG
+#   INFO
+#   WARNING
+#   ERROR
+#   FATAL
+#
+# Log only severities at the same level or more severe than the log_min_severity
+# Use TRACE to log everything
+#
+log_min_severity:TRACE
+
+# Pipeline settings
+threads:8
+
+# N orbits to store to each file
+# Configured to store fixed number of orbits per file when nOrbitsPerFile > 1
+# Set to 0 to use fixed file size instead
+nOrbitsPerFile:4096
+
+# Headers for cmssw support
+cmsswHeaders:no
+
+##  Information necessary to issue a reset request for the board
+scone_host:localhost
+scone_port:8080
+# Currently can be one of kcu1500_ugmt and kcu1500_demux
+scone_board:vcu128_bmtf_tcp