diff --git a/L1Trigger/TrackFindingTracklet/interface/Settings.h b/L1Trigger/TrackFindingTracklet/interface/Settings.h
index 6d4abf30d37a7..9675d6ae85dbe 100644
--- a/L1Trigger/TrackFindingTracklet/interface/Settings.h
+++ b/L1Trigger/TrackFindingTracklet/interface/Settings.h
@@ -158,6 +158,7 @@ namespace trklet {
     }
 
     unsigned int teunits(unsigned int iSeed) const { return teunits_[iSeed]; }
+    unsigned int trpunits(unsigned int iSeed) const { return trpunits_[iSeed]; }
 
     unsigned int NTC(int seed) const { return ntc_[seed]; }
 
@@ -666,7 +667,8 @@ namespace trklet {
     int chisqphifactbits_{14};
     int chisqzfactbits_{14};
 
-    std::array<unsigned int, N_SEED> teunits_{{5, 2, 5, 3, 3, 2, 3, 2, 0, 0, 0, 0}};  //teunits used by seed
+    std::array<unsigned int, N_SEED> teunits_{{5, 2, 5, 3, 3, 2, 3, 2, 0, 0, 0, 0}};       //teunits used by seed
+    std::array<unsigned int, N_SEED> trpunits_{{0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10}};  //trpunits used by seed
 
     std::array<unsigned int, N_LAYER + N_DISK> vmrlutzbits_{
         {7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3}};  // zbits used by LUT in VMR
diff --git a/L1Trigger/TrackFindingTracklet/interface/TrackletProcessorDisplaced.h b/L1Trigger/TrackFindingTracklet/interface/TrackletProcessorDisplaced.h
index 6cf8abd6e714a..7f0f3a38c647c 100644
--- a/L1Trigger/TrackFindingTracklet/interface/TrackletProcessorDisplaced.h
+++ b/L1Trigger/TrackFindingTracklet/interface/TrackletProcessorDisplaced.h
@@ -6,9 +6,9 @@
 #include "L1Trigger/TrackFindingTracklet/interface/TrackletCalculatorDisplaced.h"
 #include "L1Trigger/TrackFindingTracklet/interface/TrackletLUT.h"
 #include "L1Trigger/TrackFindingTracklet/interface/CircularBuffer.h"
-#include "L1Trigger/TrackFindingTracklet/interface/TrackletEngineUnit.h"
 #include "L1Trigger/TrackFindingTracklet/interface/TrackletParametersMemory.h"
 #include "L1Trigger/TrackFindingTracklet/interface/TrackletProjectionsMemory.h"
+#include "L1Trigger/TrackFindingTracklet/interface/TripletEngineUnit.h"
 
 #include <vector>
 #include <tuple>
@@ -40,6 +40,10 @@ namespace trklet {
 
   private:
     int iTC_;
+    unsigned int maxStep_;
+
+    std::tuple<CircularBuffer<TrpEData>, unsigned int, unsigned int, unsigned int, unsigned int> trpbuffer_;
+    std::vector<TripletEngineUnit> trpunits_;
 
     unsigned int layerdisk1_;
     unsigned int layerdisk2_;
diff --git a/L1Trigger/TrackFindingTracklet/interface/TripletEngineUnit.h b/L1Trigger/TrackFindingTracklet/interface/TripletEngineUnit.h
new file mode 100644
index 0000000000000..4f75a3a39bc3c
--- /dev/null
+++ b/L1Trigger/TrackFindingTracklet/interface/TripletEngineUnit.h
@@ -0,0 +1,94 @@
+#ifndef L1Trigger_TrackFindingTracklet_interface_TripletEngineUnit_h
+#define L1Trigger_TrackFindingTracklet_interface_TripletEngineUnit_h
+
+#include "L1Trigger/TrackFindingTracklet/interface/VMStubsTEMemory.h"
+#include "L1Trigger/TrackFindingTracklet/interface/CircularBuffer.h"
+#include "L1Trigger/TrackFindingTracklet/interface/TrackletLUT.h"
+
+#include <cassert>
+#include <vector>
+
+namespace trklet {
+
+  class Settings;
+  class Stub;
+  class L1TStub;
+
+  struct TrpEData {
+    const Stub* stub_;
+    int start_out_;
+    int start_in_;
+    int rzbinfirst_out_;
+    int rzdiffmax_out_;
+    std::vector<std::tuple<int, int, int> > projbin_out_;  // next z/r bin; outer stub mem; nstub
+    std::vector<std::tuple<int, int, int> > projbin_in_;   // next z/r bin; inner stub mem; nstub
+  };
+
+  class TripletEngineUnit {
+  public:
+    TripletEngineUnit(const Settings* const settings,
+                      unsigned int layerdisk1,
+                      unsigned int layerdisk2,
+                      unsigned int layerdisk3,
+                      unsigned int iSeed,
+                      std::vector<VMStubsTEMemory*> innervmstubs,
+                      std::vector<VMStubsTEMemory*> outervmstubs);
+
+    ~TripletEngineUnit() = default;
+
+    void init(const TrpEData& trpdata);
+
+    bool getGoodTriplet() { return goodtriplet__; }
+
+    bool empty() const { return candtriplets_.empty(); }
+
+    const std::tuple<const Stub*, const Stub*, const Stub*>& read() { return candtriplets_.read(); }
+
+    const std::tuple<const Stub*, const Stub*, const Stub*>& peek() const { return candtriplets_.peek(); }
+
+    bool idle() const { return idle_; }
+
+    void setNearFull() { nearfull_ = candtriplets_.nearfull(); }
+
+    void reset();
+
+    void step();
+
+    const Stub* innerStub() const { return trpdata_.stub_; }
+
+  private:
+    std::vector<VMStubsTEMemory*> innervmstubs_;
+    std::vector<VMStubsTEMemory*> outervmstubs_;
+    TrpEData trpdata_;
+    const Settings* settings_;
+    unsigned int layerdisk1_;
+    unsigned int layerdisk2_;
+    unsigned int layerdisk3_;
+    unsigned int iSeed_;
+    bool nearfull_;  //initialized at start of each processing step
+
+    //unsigned int memory slot
+    unsigned int nmem_out_;
+    unsigned int nmem_in_;
+    unsigned int istub_out_;
+    unsigned int istub_in_;
+    unsigned int next_out_;
+    unsigned int next_in_;
+    unsigned int nstub_out_;
+    unsigned int nstub_in_;
+    unsigned int outmem_;
+    unsigned int inmem_;
+    unsigned int nproj_out_;
+    unsigned int nproj_in_;
+
+    bool idle_;
+
+    std::tuple<const Stub*, const Stub*, const Stub*> candtriplet_, candtriplet__;
+    bool goodtriplet_, goodtriplet__;
+
+    //save the candidate matches
+    CircularBuffer<std::tuple<const Stub*, const Stub*, const Stub*> > candtriplets_;
+  };  // TripletEngineUnit
+
+};  // namespace trklet
+#endif
diff --git a/L1Trigger/TrackFindingTracklet/src/TrackletProcessorDisplaced.cc b/L1Trigger/TrackFindingTracklet/src/TrackletProcessorDisplaced.cc
index 903c62cf0a124..0c8a1424264e0 100644
--- a/L1Trigger/TrackFindingTracklet/src/TrackletProcessorDisplaced.cc
+++ b/L1Trigger/TrackFindingTracklet/src/TrackletProcessorDisplaced.cc
@@ -23,10 +23,13 @@ using namespace trklet;
 // This module takes in collections of stubs within a phi region and a
 // displaced seed name and tries to create that displaced seed out of the stubs
 //
-// Update: Claire Savard, Oct. 2024
+// Update: Claire Savard, Nov. 2024
 
 TrackletProcessorDisplaced::TrackletProcessorDisplaced(string name, Settings const& settings, Globals* globals)
-    : TrackletCalculatorDisplaced(name, settings, globals), innerTable_(settings), innerThirdTable_(settings) {
+    : TrackletCalculatorDisplaced(name, settings, globals),
+      trpbuffer_(CircularBuffer<TrpEData>(3), 0, 0, 0, 0),
+      innerTable_(settings),
+      innerThirdTable_(settings) {
   innerallstubs_.clear();
   middleallstubs_.clear();
   outerallstubs_.clear();
@@ -58,10 +61,9 @@ TrackletProcessorDisplaced::TrackletProcessorDisplaced(string name, Settings con
 
   // set TC index
   iTC_ = region;
-  constexpr int TCIndexMin = 128;
-  constexpr int TCIndexMax = 191;
-  TCIndex_ = (iSeed_ << 4) + iTC_;
-  assert(TCIndex_ >= TCIndexMin && TCIndex_ < TCIndexMax);
+  TCIndex_ = (iSeed_ << settings.nbitsseed()) + iTC_;
+
+  maxStep_ = settings_.maxStep("TPD");
 }
 
 void TrackletProcessorDisplaced::addOutputProjection(TrackletProjectionsMemory*& outputProj, MemoryBase* memory) {
@@ -156,177 +158,249 @@ void TrackletProcessorDisplaced::addInput(MemoryBase* memory, string input) {
 }
 
 void TrackletProcessorDisplaced::execute(unsigned int iSector, double phimin, double phimax) {
-  unsigned int countall = 0;
-  unsigned int countsel = 0;
-
   phimin_ = phimin;
   phimax_ = phimax;
   iSector_ = iSector;
 
-  // loop over the middle stubs in the potential seed
-  for (unsigned int midmem = 0; midmem < middleallstubs_.size(); midmem++) {
-    for (unsigned int i = 0; i < middleallstubs_[midmem]->nStubs(); i++) {
-      const Stub* midallstub = middleallstubs_[midmem]->getStub(i);
+  unsigned int countall = 0;
+  unsigned int countsel = 0;
+  int donecount = 0;
+
+  // set the triplet engine units and buffer
+  TripletEngineUnit trpunit(&settings_, layerdisk1_, layerdisk2_, layerdisk3_, iSeed_, innervmstubs_, outervmstubs_);
+  trpunits_.resize(settings_.trpunits(iSeed_), trpunit);
+  trpbuffer_ = tuple<CircularBuffer<TrpEData>, unsigned int, unsigned int, unsigned int, unsigned int>(
+      CircularBuffer<TrpEData>(3), 0, 0, 0, middleallstubs_.size());
+
+  // reset the trpunits
+  for (auto& trpunit : trpunits_) {
+    trpunit.reset();
+  }
+
+  // reset the tebuffer
+  std::get<0>(trpbuffer_).reset();
+  std::get<1>(trpbuffer_) = 0;
+  std::get<2>(trpbuffer_) = std::get<3>(trpbuffer_);
+
+  TrpEData trpdata;
+  TrpEData trpdata__;
+  TrpEData trpdata___;
+  bool goodtrpdata = false;
+  bool goodtrpdata__ = false;
+  bool goodtrpdata___ = false;
+
+  bool trpbuffernearfull;
+  for (unsigned int istep = 0; istep < maxStep_; istep++) {
+    CircularBuffer<TrpEData>& trpdatabuffer = std::get<0>(trpbuffer_);
+    trpbuffernearfull = trpdatabuffer.nearfull();
+
+    //
+    // First block here checks if there is a trpunit with data that should be used
+    // to calculate the tracklet parameters
+    //
+
+    // set pointer to the last filled trpunit
+    TripletEngineUnit* trpunitptr = nullptr;
+    for (auto& trpunit : trpunits_) {
+      trpunit.setNearFull();
+      if (!trpunit.empty()) {
+        trpunitptr = &trpunit;
+      }
+    }
+
+    if (trpunitptr != nullptr) {
+      auto stubtriplet = trpunitptr->read();
+
+      countall++;
+
+      const Stub* innerFPGAStub = std::get<0>(stubtriplet);
+      const Stub* middleFPGAStub = std::get<1>(stubtriplet);
+      const Stub* outerFPGAStub = std::get<2>(stubtriplet);
+
+      const L1TStub* innerStub = innerFPGAStub->l1tstub();
+      const L1TStub* middleStub = middleFPGAStub->l1tstub();
+      const L1TStub* outerStub = outerFPGAStub->l1tstub();
+
+      if (settings_.debugTracklet()) {
+        edm::LogVerbatim("Tracklet") << "TrackletProcessorDisplaced execute " << getName() << "[" << iSector_ << "]";
+      }
+
+      // check if the seed made from the 3 stubs is valid
+      bool accept = false;
+      if (iSeed_ == Seed::L2L3L4 || iSeed_ == Seed::L4L5L6)
+        accept = LLLSeeding(innerFPGAStub, innerStub, middleFPGAStub, middleStub, outerFPGAStub, outerStub);
+      else if (iSeed_ == Seed::L2L3D1)
+        accept = LLDSeeding(innerFPGAStub, innerStub, middleFPGAStub, middleStub, outerFPGAStub, outerStub);
+      else if (iSeed_ == Seed::D1D2L2)
+        accept = DDLSeeding(innerFPGAStub, innerStub, middleFPGAStub, middleStub, outerFPGAStub, outerStub);
+
+      if (accept)
+        countsel++;
+
+      if (trackletpars_->nTracklets() >= settings_.ntrackletmax()) {
+        edm::LogVerbatim("Tracklet") << "Will break on number of tracklets in " << getName();
+        assert(0);
+        break;
+      }
+
+      if (settings_.debugTracklet()) {
+        edm::LogVerbatim("Tracklet") << "TrackletProcessor execute done";
+      }
+    }
+
+    //
+    // The second block fills the trpunit if data in buffer and process TripletEngineUnit step
+    //
+    //
+
+    bool notemptytrpbuffer = !trpdatabuffer.empty();
+    for (auto& trpunit : trpunits_) {
+      if (trpunit.idle() && notemptytrpbuffer) {  // only fill one idle unit every step
+        trpunit.init(std::get<0>(trpbuffer_).read());
+        notemptytrpbuffer = false;  //prevent initializing another triplet engine unit
+      }
+      trpunit.step();
+    }
+
+    //
+    // The third block here checks if we have input stubs to process
+    //
+    //
+
+    if (goodtrpdata___)
+      trpdatabuffer.store(trpdata___);
+    goodtrpdata = false;
+
+    unsigned int& istub = std::get<1>(trpbuffer_);
+    unsigned int& midmem = std::get<2>(trpbuffer_);
+    unsigned int midmemend = std::get<4>(trpbuffer_);
+
+    if ((!trpbuffernearfull) && midmem < midmemend && istub < middleallstubs_[midmem]->nStubs()) {
+      const Stub* stub = middleallstubs_[midmem]->getStub(istub);
 
       if (settings_.debugTracklet()) {
         edm::LogVerbatim("Tracklet") << "In " << getName() << " have middle stub";
       }
 
       // get r/z index of the middle stub
-      int indexz = (((1 << (midallstub->z().nbits() - 1)) + midallstub->z().value()) >>
-                    (midallstub->z().nbits() - nbitszfinebintable_));
+      int indexz = (((1 << (stub->z().nbits() - 1)) + stub->z().value()) >> (stub->z().nbits() - nbitszfinebintable_));
       int indexr = -1;
-      bool negdisk = (midallstub->disk().value() < 0);  // check if disk in negative z region
-      if (layerdisk1_ >= LayerDisk::D1) {               // if a disk
+      bool negdisk = (stub->disk().value() < 0);  // check if disk in negative z region
+      if (layerdisk1_ >= LayerDisk::D1) {         // if a disk
         if (negdisk)
           indexz = (1 << nbitszfinebintable_) - indexz;
-        indexr = midallstub->r().value();
-        if (midallstub->isPSmodule()) {
-          indexr = midallstub->r().value() >> (midallstub->r().nbits() - nbitsrfinebintable_);
+        indexr = stub->r().value();
+        if (stub->isPSmodule()) {
+          indexr = stub->r().value() >> (stub->r().nbits() - nbitsrfinebintable_);
         }
       } else {  // else a layer
-        indexr = (((1 << (midallstub->r().nbits() - 1)) + midallstub->r().value()) >>
-                  (midallstub->r().nbits() - nbitsrfinebintable_));
+        indexr = (((1 << (stub->r().nbits() - 1)) + stub->r().value()) >> (stub->r().nbits() - nbitsrfinebintable_));
       }
 
-      assert(indexz >= 0);
-      assert(indexr >= 0);
-      assert(indexz < (1 << nbitszfinebintable_));
-      assert(indexr < (1 << nbitsrfinebintable_));
-
       // create lookupbits that define projections from middle stub
-      unsigned int lutwidth = settings_.lutwidthtabextended(0, iSeed_);
       int lutval = -1;
       const auto& lutshift = innerTable_.nbits();
       lutval = innerTable_.lookup((indexz << nbitsrfinebintable_) + indexr);
       int lutval2 = innerThirdTable_.lookup((indexz << nbitsrfinebintable_) + indexr);
       if (lutval != -1 && lutval2 != -1)
         lutval += (lutval2 << lutshift);
-      if (lutval == -1)
-        continue;
-      FPGAWord lookupbits(lutval, lutwidth, true, __LINE__, __FILE__);
-
-      // get r/z bins for projection into outer layer/disk
-      int nbitsrzbin = N_RZBITS;
-      if (iSeed_ == Seed::D1D2L2)
-        nbitsrzbin--;
-      int rzbinfirst = lookupbits.bits(0, NFINERZBITS);
-      int next = lookupbits.bits(NFINERZBITS, 1);
-      int rzdiffmax = lookupbits.bits(NFINERZBITS + 1 + nbitsrzbin, NFINERZBITS);
-
-      int start = lookupbits.bits(NFINERZBITS + 1, nbitsrzbin);  // first rz bin projection
-      if (iSeed_ == Seed::D1D2L2 && negdisk)                     // if projecting into disk
-        start += (1 << nbitsrzbin);
-      int last = start + next;  // last rz bin projection
-
-      if (settings_.debugTracklet()) {
-        edm::LogVerbatim("Tracklet") << "Will look in r/z bins for outer stub " << start << " to " << last << endl;
-      }
 
-      // loop over outer stubs that the middle stub can project to
-      for (int ibin = start; ibin <= last; ibin++) {
-        for (unsigned int outmem = 0; outmem < outervmstubs_.size(); outmem++) {
-          for (unsigned int j = 0; j < outervmstubs_[outmem]->nVMStubsBinned(ibin); j++) {
-            if (settings_.debugTracklet())
-              edm::LogVerbatim("Tracklet") << "In " << getName() << " have outer stub" << endl;
-
-            const VMStubTE& outvmstub = outervmstubs_[outmem]->getVMStubTEBinned(ibin, j);
-
-            // check if r/z of outer stub is within projection range
-            int rzbin = (outvmstub.vmbits().value() & (settings_.NLONGVMBINS() - 1));
-            if (start != ibin)
-              rzbin += 8;
-            if (rzbin < rzbinfirst || rzbin - rzbinfirst > rzdiffmax) {
-              if (settings_.debugTracklet()) {
-                edm::LogVerbatim("Tracklet") << "Outer stub rejected because of wrong r/z bin";
-              }
-              continue;
-            }
-
-            // get r/z bins for projection into third layer/disk
-            int nbitsrzbin_ = N_RZBITS;
-            int next_ = lookupbits.bits(lutshift + NFINERZBITS, 1);
-
-            int start_ = lookupbits.bits(lutshift + NFINERZBITS + 1, nbitsrzbin_);  // first rz bin projection
-            if (iSeed_ == Seed::D1D2L2 && negdisk)  // if projecting from disk into layer
-              start_ = settings_.NLONGVMBINS() - 1 - start_ - next_;
-            int last_ = start_ + next_;  // last rz bin projection
-
-            if (settings_.debugTracklet()) {
-              edm::LogVerbatim("Tracklet")
-                  << "Will look in rz bins for inner stub " << start_ << " to " << last_ << endl;
-            }
-
-            // loop over inner stubs that the middle stub can project to
-            for (int ibin_ = start_; ibin_ <= last_; ibin_++) {
-              for (unsigned int inmem = 0; inmem < innervmstubs_.size(); inmem++) {
-                for (unsigned int k = 0; k < innervmstubs_[inmem]->nVMStubsBinned(ibin_); k++) {
-                  if (settings_.debugTracklet())
-                    edm::LogVerbatim("Tracklet") << "In " << getName() << " have inner stub" << endl;
-
-                  const VMStubTE& invmstub = innervmstubs_[inmem]->getVMStubTEBinned(ibin_, k);
-
-                  countall++;
-
-                  const Stub* innerFPGAStub = invmstub.stub();
-                  const Stub* middleFPGAStub = midallstub;
-                  const Stub* outerFPGAStub = outvmstub.stub();
-
-                  const L1TStub* innerStub = innerFPGAStub->l1tstub();
-                  const L1TStub* middleStub = middleFPGAStub->l1tstub();
-                  const L1TStub* outerStub = outerFPGAStub->l1tstub();
-
-                  if (settings_.debugTracklet()) {
-                    edm::LogVerbatim("Tracklet")
-                        << "triplet seeding\n"
-                        << innerFPGAStub->strbare() << middleFPGAStub->strbare() << outerFPGAStub->strbare()
-                        << innerStub->stubword() << middleStub->stubword() << outerStub->stubword()
-                        << innerFPGAStub->layerdisk() << middleFPGAStub->layerdisk() << outerFPGAStub->layerdisk();
-                    edm::LogVerbatim("Tracklet")
-                        << "TrackletCalculatorDisplaced execute " << getName() << "[" << iSector_ << "]";
-                  }
-
-                  // check if the seed made from the 3 stubs is valid
-                  bool accept = false;
-                  if (iSeed_ == Seed::L2L3L4 || iSeed_ == Seed::L4L5L6)
-                    accept = LLLSeeding(innerFPGAStub, innerStub, middleFPGAStub, middleStub, outerFPGAStub, outerStub);
-                  else if (iSeed_ == Seed::L2L3D1)
-                    accept = LLDSeeding(innerFPGAStub, innerStub, middleFPGAStub, middleStub, outerFPGAStub, outerStub);
-                  else if (iSeed_ == Seed::D1D2L2)
-                    accept = DDLSeeding(innerFPGAStub, innerStub, middleFPGAStub, middleStub, outerFPGAStub, outerStub);
-
-                  if (accept)
-                    countsel++;
-
-                  if (settings_.debugTracklet()) {
-                    edm::LogVerbatim("Tracklet") << "TrackletCalculatorDisplaced execute done";
-                  }
-                  if (countall >= settings_.maxStep("TPD"))
-                    break;
-                }
-                if (countall >= settings_.maxStep("TPD"))
-                  break;
-              }
-              if (countall >= settings_.maxStep("TPD"))
-                break;
-            }
-            if (countall >= settings_.maxStep("TPD"))
-              break;
+      if (lutval != -1) {
+        unsigned int lutwidth = settings_.lutwidthtabextended(0, iSeed_);
+        FPGAWord lookupbits(lutval, lutwidth, true, __LINE__, __FILE__);
+
+        // get r/z bins for projection into outer layer/disk
+        int nbitsrzbin_out = N_RZBITS;
+        if (iSeed_ == Seed::D1D2L2)
+          nbitsrzbin_out--;
+        int rzbinfirst_out = lookupbits.bits(0, NFINERZBITS);
+        int rzdiffmax_out = lookupbits.bits(NFINERZBITS + 1 + nbitsrzbin_out, NFINERZBITS);
+        int start_out = lookupbits.bits(NFINERZBITS + 1, nbitsrzbin_out);  // first rz bin projection
+        int next_out = lookupbits.bits(NFINERZBITS, 1);
+        if (iSeed_ == Seed::D1D2L2 && negdisk)  // if projecting into disk
+          start_out += (1 << nbitsrzbin_out);
+        int last_out = start_out + next_out;  // last rz bin projection
+
+        // get r/z bins for projection into third (inner) layer/disk
+        int nbitsrzbin_in = N_RZBITS;
+        int start_in = lookupbits.bits(lutshift + NFINERZBITS + 1, nbitsrzbin_in);  // first rz bin projection
+        int next_in = lookupbits.bits(lutshift + NFINERZBITS, 1);
+        if (iSeed_ == Seed::D1D2L2 && negdisk)  // if projecting from disk into layer
+          start_in = settings_.NLONGVMBINS() - 1 - start_in - next_in;
+        int last_in = start_in + next_in;  // last rz bin projection
+
+        // fill trpdata with projection info of middle stub
+        trpdata.stub_ = stub;
+        trpdata.rzbinfirst_out_ = rzbinfirst_out;
+        trpdata.rzdiffmax_out_ = rzdiffmax_out;
+        trpdata.start_out_ = start_out;
+        trpdata.start_in_ = start_in;
+
+        // fill projection bins info for single engine unit
+        trpdata.projbin_out_.clear();
+        trpdata.projbin_in_.clear();
+        for (int ibin_out = start_out; ibin_out <= last_out; ibin_out++) {
+          for (unsigned int outmem = 0; outmem < outervmstubs_.size(); outmem++) {
+            int nstubs_out = outervmstubs_[outmem]->nVMStubsBinned(ibin_out);
+            if (nstubs_out > 0)
+              trpdata.projbin_out_.emplace_back(tuple<int, int, int>(ibin_out - start_out, outmem, nstubs_out));
+          }
+        }
+        for (int ibin_in = start_in; ibin_in <= last_in; ibin_in++) {
+          for (unsigned int inmem = 0; inmem < innervmstubs_.size(); inmem++) {
+            int nstubs_in = innervmstubs_[inmem]->nVMStubsBinned(ibin_in);
+            if (nstubs_in > 0)
+              trpdata.projbin_in_.emplace_back(tuple<int, int, int>(ibin_in - start_in, inmem, nstubs_in));
           }
-          if (countall >= settings_.maxStep("TPD"))
-            break;
         }
-        if (countall >= settings_.maxStep("TPD"))
-          break;
+
+        if (!trpdata.projbin_in_.empty() && !trpdata.projbin_out_.empty()) {
+          goodtrpdata = true;
+        }
       }
-      if (countall >= settings_.maxStep("TPD"))
-        break;
+
+      istub++;
+      if (istub >= middleallstubs_[midmem]->nStubs()) {
+        istub = 0;
+        midmem++;
+      }
+
+    } else if ((!trpbuffernearfull) && midmem < midmemend && istub == 0)
+      midmem++;
+
+    goodtrpdata___ = goodtrpdata__;
+    goodtrpdata__ = goodtrpdata;
+
+    trpdata___ = trpdata__;
+    trpdata__ = trpdata;
+
+    //
+    // stop looping over istep if done
+    //
+
+    bool done = true;
+
+    if (midmem < midmemend || (!trpdatabuffer.empty())) {
+      done = false;
+    }
+
+    for (auto& trpunit : trpunits_) {
+      if (!(trpunit.idle() && trpunit.empty()))
+        done = false;
     }
-    if (countall >= settings_.maxStep("TPD"))
+
+    if (done) {
+      donecount++;
+    }
+
+    //FIXME This should be done cleaner... Not too hard, but need to check fully the TEBuffer and TEUnit buffer.
+    if (donecount > 4) {
       break;
+    }
   }
 
   if (settings_.writeMonitorData("TPD")) {
-    globals_->ofstream("trackletprocessordisplaced.txt") << getName() << " " << countall << " " << countsel << endl;
+    globals_->ofstream("trackletprocessordisplaced.txt")
+        << getName() << " " << countall << " " << countsel << std::endl;
   }
 }
diff --git a/L1Trigger/TrackFindingTracklet/src/TripletEngineUnit.cc b/L1Trigger/TrackFindingTracklet/src/TripletEngineUnit.cc
new file mode 100644
index 0000000000000..709cf34bfd468
--- /dev/null
+++ b/L1Trigger/TrackFindingTracklet/src/TripletEngineUnit.cc
@@ -0,0 +1,112 @@
+#include "L1Trigger/TrackFindingTracklet/interface/TripletEngineUnit.h"
+#include "L1Trigger/TrackFindingTracklet/interface/Settings.h"
+
+using namespace trklet;
+
+// TripletEngineUnit
+//
+// This script sets a processing unit for the TrackletProcessorDisplaced
+// based on information from the middle stub and its projections in and out,
+// and a new triplet seed is checked and created for each processing step
+//
+// Author: Claire Savard, Nov. 2024
+
+TripletEngineUnit::TripletEngineUnit(const Settings* const settings,
+                                     unsigned int layerdisk1,
+                                     unsigned int layerdisk2,
+                                     unsigned int layerdisk3,
+                                     unsigned int iSeed,
+                                     std::vector<VMStubsTEMemory*> innervmstubs,
+                                     std::vector<VMStubsTEMemory*> outervmstubs)
+    : settings_(settings), candtriplets_(3) {
+  idle_ = true;
+  layerdisk1_ = layerdisk1;
+  layerdisk2_ = layerdisk2;
+  layerdisk3_ = layerdisk3;
+  iSeed_ = iSeed;
+  innervmstubs_ = innervmstubs;
+  outervmstubs_ = outervmstubs;
+}
+
+void TripletEngineUnit::init(const TrpEData& trpdata) {
+  trpdata_ = trpdata;
+  istub_out_ = 0;
+  istub_in_ = 0;
+  nproj_out_ = 0;
+  nproj_in_ = 0;
+  idle_ = false;
+
+  assert(!trpdata_.projbin_out_.empty() && !trpdata_.projbin_in_.empty());
+  std::tie(next_out_, outmem_, nstub_out_) = trpdata_.projbin_out_[0];
+  std::tie(next_in_, inmem_, nstub_in_) = trpdata_.projbin_in_[0];
+}
+
+void TripletEngineUnit::reset() {
+  idle_ = true;
+  goodtriplet_ = false;
+  goodtriplet__ = false;
+  candtriplets_.reset();
+}
+
+void TripletEngineUnit::step() {
+  if (goodtriplet__) {
+    candtriplets_.store(candtriplet__);
+  }
+
+  goodtriplet__ = goodtriplet_;
+  candtriplet__ = candtriplet_;
+
+  goodtriplet_ = false;
+
+  if (idle_ || nearfull_) {
+    return;
+  }
+
+  // get inner and outer projected stub for certain next value
+  int ibin_out = trpdata_.start_out_ + next_out_;
+  int ibin_in = trpdata_.start_in_ + next_in_;
+  const VMStubTE& outervmstub = outervmstubs_[outmem_]->getVMStubTEBinned(ibin_out, istub_out_);
+  const VMStubTE& innervmstub = innervmstubs_[inmem_]->getVMStubTEBinned(ibin_in, istub_in_);
+
+  // check if r/z of outer stub is within projection range
+  int rzbin = (outervmstub.vmbits().value() & (settings_->NLONGVMBINS() - 1));
+  if (trpdata_.start_out_ != ibin_out)
+    rzbin += 8;
+  if (rzbin < trpdata_.rzbinfirst_out_ || rzbin - trpdata_.rzbinfirst_out_ > trpdata_.rzdiffmax_out_) {
+    if (settings_->debugTracklet()) {
+      edm::LogVerbatim("Tracklet") << "Outer stub rejected because of wrong r/z bin";
+    }
+  } else {
+    candtriplet_ =
+        std::tuple<const Stub*, const Stub*, const Stub*>(innervmstub.stub(), trpdata_.stub_, outervmstub.stub());
+    goodtriplet_ = true;
+  }
+
+  // go to next projection (looping through all inner stubs for each outer stub)
+  istub_in_++;
+  if (istub_in_ >= nstub_in_) {  // if gone through all in stubs, move to next in proj bin
+    nproj_in_++;
+    istub_in_ = 0;
+    if (nproj_in_ >= trpdata_.projbin_in_.size()) {  // if gone through all in proj bins, move to next out stub
+      istub_out_++;
+      nproj_in_ = 0;
+      if (istub_out_ >= nstub_out_) {  // if gone through all out stubs, move to next out proj bin
+        nproj_out_++;
+        istub_out_ = 0;
+        if (nproj_out_ >=
+            trpdata_.projbin_out_.size()) {  // if gone through all out proj bins, reset everything and stop engine unit
+          istub_in_ = 0;
+          istub_out_ = 0;
+          nproj_in_ = 0;
+          nproj_out_ = 0;
+          idle_ = true;
+          return;
+        }
+        // get next out proj bin
+        std::tie(next_out_, outmem_, nstub_out_) = trpdata_.projbin_out_[nproj_out_];
+      }
+    }
+    // get next in proj bin
+    std::tie(next_in_, inmem_, nstub_in_) = trpdata_.projbin_in_[nproj_in_];
+  }
+}