From 0cf6e5290badc4404964721cc8557b00c0e43cb5 Mon Sep 17 00:00:00 2001
From: Andrew Peck <andrew.peck@cern.ch>
Date: Thu, 6 Jul 2023 12:01:57 -0400
Subject: [PATCH 01/18] change address hierarchy for multi-rb updates

---
 tamalero/LPGBT.py        | 30 +++++++++++++++---------------
 tamalero/ReadoutBoard.py | 18 +++++++++---------
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/tamalero/LPGBT.py b/tamalero/LPGBT.py
index 992e96c..4025b19 100644
--- a/tamalero/LPGBT.py
+++ b/tamalero/LPGBT.py
@@ -199,20 +199,20 @@ class LPGBT(RegParser):
         if self.trigger:
             if verbose:
                 print ("Checking trigger link status")
-                print ("Uplink ready:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.TRIGGER.UPLINK.READY"%self.rb).value()==1 )
-                print ("FEC count:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.TRIGGER.UPLINK.FEC_ERR_CNT"%self.rb).value())
+                print ("Uplink ready:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_1.READY"%self.rb).value()==1 )
+                print ("FEC count:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_1.FEC_ERR_CNT"%self.rb).value())
             return (
-                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.TRIGGER.UPLINK.FEC_ERR_CNT"%self.rb).value() == 0) &
-                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.TRIGGER.UPLINK.READY"%self.rb).value() == 1)
+                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_1.FEC_ERR_CNT"%self.rb).value() == 0) &
+                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_1.READY"%self.rb).value() == 1)
             )
         else:
             if verbose:
                 print ("Checking DAQ link status")
-                print ("Uplink ready:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.DAQ.UPLINK.READY"%self.rb).value()==1 )
-                print ("FEC count:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.DAQ.UPLINK.FEC_ERR_CNT"%self.rb).value())
+                print ("Uplink ready:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_0.READY"%self.rb).value()==1 )
+                print ("FEC count:", self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_0.FEC_ERR_CNT"%self.rb).value())
             return (
-                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.DAQ.UPLINK.FEC_ERR_CNT"%self.rb).value() == 0) &
-                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.DAQ.UPLINK.READY"%self.rb).value() == 1)
+                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_0.FEC_ERR_CNT"%self.rb).value() == 0) &
+                (self.kcu.read_node("READOUT_BOARD_%i.LPGBT.UPLINK_0.READY"%self.rb).value() == 1)
             )
 
     def get_version(self):
@@ -277,7 +277,7 @@ class LPGBT(RegParser):
             # needed for the mgt to lock
 
             if (not self.kcu.read_node(
-                    "READOUT_BOARD_%d.LPGBT.DAQ.UPLINK.READY" % self.rb)):
+                    "READOUT_BOARD_%d.LPGBT.UPLINK_0.READY" % self.rb)):
                 print("  > Performing LpGBT Magic...")
                 id = "LPGBT.RW.TESTING.ULECDATASOURCE"
                 self.wr_reg(id, 6)
@@ -319,7 +319,7 @@ class LPGBT(RegParser):
 
     def align_DAQ(self):
         for i in range(28):
-            id = "READOUT_BOARD_%d.LPGBT.DAQ.UPLINK.ALIGN_%d" % (self.rb, i)
+            id = "READOUT_BOARD_%d.LPGBT.UPLINK_0.ALIGN_%d" % (self.rb, i)
             self.kcu.write_node(id, 2)
 
     def wr_adr(self, adr, data):
@@ -467,18 +467,18 @@ class LPGBT(RegParser):
         if self.trigger:
             if not quiet:
                 print ("Setting uplink alignment for trigger link %i to %i"%(link, val))
-            id = "READOUT_BOARD_%d.LPGBT.TRIGGER.UPLINK.ALIGN_%d" % (self.rb, link)
+            id = "READOUT_BOARD_%d.LPGBT.UPLINK_1.ALIGN_%d" % (self.rb, link)
         else:
             if not quiet:
                 print ("Setting uplink alignment for DAQ link %i to %i"%(link, val))
-            id = "READOUT_BOARD_%d.LPGBT.DAQ.UPLINK.ALIGN_%d" % (self.rb, link)
+            id = "READOUT_BOARD_%d.LPGBT.UPLINK_0.ALIGN_%d" % (self.rb, link)
         self.kcu.write_node(id, val)
 
     def get_uplink_alignment(self, link):
         if self.trigger:
-            return self.kcu.read_node("READOUT_BOARD_%d.LPGBT.TRIGGER.UPLINK.ALIGN_%d"%(self.rb, link)).value()
+            return self.kcu.read_node("READOUT_BOARD_%d.LPGBT.UPLINK_1.ALIGN_%d"%(self.rb, link)).value()
         else:
-            return self.kcu.read_node("READOUT_BOARD_%d.LPGBT.DAQ.UPLINK.ALIGN_%d"%(self.rb, link)).value()
+            return self.kcu.read_node("READOUT_BOARD_%d.LPGBT.UPLINK_0.ALIGN_%d"%(self.rb, link)).value()
 
     def set_uplink_invert(self, link, invert=True):
         self.wr_reg("LPGBT.RWF.EPORTRX.EPRX_CHN_CONTROL.EPRX%dINVERT" % link, invert)
@@ -1070,7 +1070,7 @@ class LPGBT(RegParser):
                 self.wr_reg("LPGBT.RW.TESTING.DPDATAPATTERN%d"%i, 0xff&(pattern >> (i*8)))
 
     def set_downlink_data_src(self, source):
-        id = "READOUT_BOARD_%d.LPGBT.DAQ.DOWNLINK.DL_SRC" % self.rb
+        id = "READOUT_BOARD_%d.LPGBT.DOWNLINK.DL_SRC" % self.rb
         if (source == "etroc"):
             self.kcu.write_node(id, 0)
         if (source == "upcnt"):
diff --git a/tamalero/ReadoutBoard.py b/tamalero/ReadoutBoard.py
index 5e19acf..01a3df0 100644
--- a/tamalero/ReadoutBoard.py
+++ b/tamalero/ReadoutBoard.py
@@ -197,11 +197,11 @@ class ReadoutBoard:
 
     def status(self):
         nodes = list(map (lambda x : "READOUT_BOARD_%s.LPGBT." % self.rb + x,
-                          ("DAQ.DOWNLINK.READY",
-                      "DAQ.UPLINK.READY",
-                      "DAQ.UPLINK.FEC_ERR_CNT",
-                      "TRIGGER.UPLINK.READY",
-                      "TRIGGER.UPLINK.FEC_ERR_CNT",)))
+                          ("DOWNLINK.READY",
+                      "UPLINK_0.READY",
+                      "UPLINK_0.FEC_ERR_CNT",
+                      "UPLINK_1.READY",
+                      "UPLINK_1.FEC_ERR_CNT",)))
         for node in nodes:
             val = self.kcu.read_node(node)
             err = 0
@@ -247,11 +247,11 @@ class ReadoutBoard:
     def get_FEC_error_count(self, quiet=False):
         if not quiet:
             print("{:<8}{:<8}{:<50}{:<8}".format("Address", "Perm.", "Name", "Value"))
-            self.kcu.print_reg(self.kcu.hw.getNode("READOUT_BOARD_%s.LPGBT.DAQ.UPLINK.FEC_ERR_CNT" % self.rb), use_color=True, invert=True)
-            self.kcu.print_reg(self.kcu.hw.getNode("READOUT_BOARD_%s.LPGBT.TRIGGER.UPLINK.FEC_ERR_CNT" % self.rb), use_color=True, invert=True)
+            self.kcu.print_reg(self.kcu.hw.getNode("READOUT_BOARD_%s.LPGBT.UPLINK_0.FEC_ERR_CNT" % self.rb), use_color=True, invert=True)
+            self.kcu.print_reg(self.kcu.hw.getNode("READOUT_BOARD_%s.LPGBT.UPLINK_1.FEC_ERR_CNT" % self.rb), use_color=True, invert=True)
         return {
-            'DAQ': self.kcu.read_node("READOUT_BOARD_%s.LPGBT.DAQ.UPLINK.FEC_ERR_CNT" % self.rb).value(),
-            'TRIGGER': self.kcu.read_node("READOUT_BOARD_%s.LPGBT.TRIGGER.UPLINK.FEC_ERR_CNT" % self.rb).value()
+            'DAQ': self.kcu.read_node("READOUT_BOARD_%s.LPGBT.UPLINK_0.FEC_ERR_CNT" % self.rb).value(),
+            'TRIGGER': self.kcu.read_node("READOUT_BOARD_%s.LPGBT.UPLINK_1.FEC_ERR_CNT" % self.rb).value()
         }
 
     def reset_FEC_error_count(self, quiet=False):
-- 
GitLab


From c252c83228972612b57d7709c549dacfe48427c3 Mon Sep 17 00:00:00 2001
From: Andrew Peck <andrew.peck@cern.ch>
Date: Fri, 7 Jul 2023 10:25:36 -0400
Subject: [PATCH 02/18] bump required firmware version

---
 tamalero/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tamalero/__init__.py b/tamalero/__init__.py
index 3aaa979..061b0cb 100644
--- a/tamalero/__init__.py
+++ b/tamalero/__init__.py
@@ -1,2 +1,2 @@
 __version__ = "0.1.0"
-__fw_version__ = "2.1.11"
+__fw_version__ = "3.0.0"
-- 
GitLab


From 097521241c86c818942d36a4f9fa9c06074aaa64 Mon Sep 17 00:00:00 2001
From: Andrew Peck <andrew.peck@cern.ch>
Date: Tue, 11 Jul 2023 09:51:26 -0400
Subject: [PATCH 03/18] bump firmware version

---
 tamalero/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tamalero/__init__.py b/tamalero/__init__.py
index 061b0cb..1d6af48 100644
--- a/tamalero/__init__.py
+++ b/tamalero/__init__.py
@@ -1,2 +1,2 @@
 __version__ = "0.1.0"
-__fw_version__ = "3.0.0"
+__fw_version__ = "3.0.5"
-- 
GitLab


From 600c97691db1fd43a8b309437e7971275ddbd5de Mon Sep 17 00:00:00 2001
From: Andrew Peck <andrew.peck@cern.ch>
Date: Tue, 11 Jul 2023 16:50:34 -0400
Subject: [PATCH 04/18] update generic address table

---
 address_table/generic/etl_test_fw.xml         | 16 ++++++-
 address_table/generic/modules/DAQ.xml         |  2 +-
 .../generic/modules/LPGBT_DOWNLINK.xml        |  9 +---
 .../generic/modules/LPGBT_UPLINK.xml          | 28 -----------
 .../generic/modules/READOUT_BOARD.xml         | 48 ++++++++++++-------
 address_table/generic/modules/SYSTEM.xml      | 15 +++---
 6 files changed, 57 insertions(+), 61 deletions(-)

diff --git a/address_table/generic/etl_test_fw.xml b/address_table/generic/etl_test_fw.xml
index 29d2f85..ae3fdf4 100644
--- a/address_table/generic/etl_test_fw.xml
+++ b/address_table/generic/etl_test_fw.xml
@@ -2,7 +2,19 @@
 <node id="TOP">
   <node id="LOOPBACK"        module="file://modules/LOOPBACK.xml"      address="0x0000" />
   <node id="FW_INFO"         module="file://modules/FW_INFO.xml"       address="0x1000" />
-  <node id="READOUT_BOARD_0" module="file://modules/READOUT_BOARD.xml" address="0x2000"/>
+
+  <node id="READOUT_BOARD_0" fwinfo="type=array" module="file://modules/READOUT_BOARD.xml" address="0x2000"/>
+  <node id="READOUT_BOARD_1" fwinfo="type=array" module="file://modules/READOUT_BOARD.xml" address="0x3000"/>
+  <node id="READOUT_BOARD_2" fwinfo="type=array" module="file://modules/READOUT_BOARD.xml" address="0x4000"/>
+  <node id="READOUT_BOARD_3" fwinfo="type=array" module="file://modules/READOUT_BOARD.xml" address="0x5000"/>
+  <node id="READOUT_BOARD_4" fwinfo="type=array" module="file://modules/READOUT_BOARD.xml" address="0x6000"/>
+
   <node id="SYSTEM"          module="file://modules/SYSTEM.xml"        address="0xB000" />
-  <node id="DAQ_RB0"  module="file://modules/DAQ.xml"       address="0x120000" />
+
+  <node id="DAQ_RB0" fwinfo="type=array" module="file://modules/DAQ.xml" address="0x0100000" />
+  <node id="DAQ_RB1" fwinfo="type=array" module="file://modules/DAQ.xml" address="0x0200000" />
+  <node id="DAQ_RB2" fwinfo="type=array" module="file://modules/DAQ.xml" address="0x0400000" />
+  <node id="DAQ_RB3" fwinfo="type=array" module="file://modules/DAQ.xml" address="0x0800000" />
+  <node id="DAQ_RB4" fwinfo="type=array" module="file://modules/DAQ.xml" address="0x1000000" />
+
 </node>
diff --git a/address_table/generic/modules/DAQ.xml b/address_table/generic/modules/DAQ.xml
index eef446f..c09adf3 100644
--- a/address_table/generic/modules/DAQ.xml
+++ b/address_table/generic/modules/DAQ.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <node id="DAQ_FIFO" fwinfo="endpoint; width=16">
-  <node id="FIFO"      address="0x0"    mode="non-incremental" size="32768" permission="r"/>
+  <node id="FIFO"      address="0x0"    mode="non-incremental" size="16384" permission="r"/>
 </node>
diff --git a/address_table/generic/modules/LPGBT_DOWNLINK.xml b/address_table/generic/modules/LPGBT_DOWNLINK.xml
index fadf1d2..f449e8b 100644
--- a/address_table/generic/modules/LPGBT_DOWNLINK.xml
+++ b/address_table/generic/modules/LPGBT_DOWNLINK.xml
@@ -2,12 +2,5 @@
 <node id="DOWNLINK">
   <node id="RESET"          address="0x0" mask="0x00000001" permission="w"  description="Reset this Downlink LpGBT Encoder"/>
   <node id="READY"          address="0x1" mask="0x00000001" permission="r"  description="LPGBT Downlink Ready"/>
-  <node id="ALIGN_0"        address="0x2" mask="0x00000007" permission="rw" description="Downlink bitslip alignment for Group 0"/>
-  <node id="ALIGN_1"        address="0x2" mask="0x00000070" permission="rw" description="Downlink bitslip alignment for Group 1"/>
-  <node id="ALIGN_2"        address="0x2" mask="0x00000700" permission="rw" description="Downlink bitslip alignment for Group 2"/>
-  <node id="ALIGN_3"        address="0x2" mask="0x00007000" permission="rw" description="Downlink bitslip alignment for Group 3"/>
-  <node id="DL_SRC"         address="0x3" mask="0x0000000f" permission="rw" description="0=etroc, 1=upcnt, 2=prbs, 3=sw fast command"/>
-  <node id="FAST_CMD_IDLE"  address="0x4" mask="0x0000ff00" permission="rw" description="Data to send on fast_cmd" parameters="default=0xF0"/>
-  <node id="FAST_CMD_DATA"  address="0x4" mask="0x00ff0000" permission="rw" description="Data to send on fast_cmd"/>
-  <node id="FAST_CMD_PULSE" address="0x5" mask="0x00000001" permission="w"  description="Write 1 to pulse fast_cmd"/>
+  <node id="DL_SRC"         address="0x3" mask="0x0000000f" permission="rw" description="0=etroc, 1=upcnt, 2=prbs, 3=txfifo"/>
 </node>
diff --git a/address_table/generic/modules/LPGBT_UPLINK.xml b/address_table/generic/modules/LPGBT_UPLINK.xml
index bc3591b..3bc710e 100644
--- a/address_table/generic/modules/LPGBT_UPLINK.xml
+++ b/address_table/generic/modules/LPGBT_UPLINK.xml
@@ -3,32 +3,4 @@
   <node id="RESET"       address="0x0" mask="0x00000001" permission="w" description="Reset this Uplink"/>
   <node id="READY"       address="0x1" mask="0x00000001" permission="r" description="LPGBT Uplink Ready"/>
   <node id="FEC_ERR_CNT" address="0x1" mask="0xFFFF0000" permission="r" description="Data Corrected Count"/>
-  <node id="ALIGN_0"     address="0x2" mask="0x00000007" permission="rw" description=""/>
-  <node id="ALIGN_1"     address="0x2" mask="0x00000070" permission="rw" description=""/>
-  <node id="ALIGN_2"     address="0x2" mask="0x00000700" permission="rw" description=""/>
-  <node id="ALIGN_3"     address="0x2" mask="0x00007000" permission="rw" description=""/>
-  <node id="ALIGN_4"     address="0x2" mask="0x00070000" permission="rw" description=""/>
-  <node id="ALIGN_5"     address="0x2" mask="0x00700000" permission="rw" description=""/>
-  <node id="ALIGN_6"     address="0x2" mask="0x07000000" permission="rw" description=""/>
-  <node id="ALIGN_7"     address="0x2" mask="0x70000000" permission="rw" description=""/>
-  <node id="ALIGN_8"     address="0x3" mask="0x00000007" permission="rw" description=""/>
-  <node id="ALIGN_9"     address="0x3" mask="0x00000070" permission="rw" description=""/>
-  <node id="ALIGN_10"    address="0x3" mask="0x00000700" permission="rw" description=""/>
-  <node id="ALIGN_11"    address="0x3" mask="0x00007000" permission="rw" description=""/>
-  <node id="ALIGN_12"    address="0x3" mask="0x00070000" permission="rw" description=""/>
-  <node id="ALIGN_13"    address="0x3" mask="0x00700000" permission="rw" description=""/>
-  <node id="ALIGN_14"    address="0x3" mask="0x07000000" permission="rw" description=""/>
-  <node id="ALIGN_15"    address="0x3" mask="0x70000000" permission="rw" description=""/>
-  <node id="ALIGN_16"    address="0x4" mask="0x00000007" permission="rw" description=""/>
-  <node id="ALIGN_17"    address="0x4" mask="0x00000070" permission="rw" description=""/>
-  <node id="ALIGN_18"    address="0x4" mask="0x00000700" permission="rw" description=""/>
-  <node id="ALIGN_19"    address="0x4" mask="0x00007000" permission="rw" description=""/>
-  <node id="ALIGN_20"    address="0x4" mask="0x00070000" permission="rw" description=""/>
-  <node id="ALIGN_21"    address="0x4" mask="0x00700000" permission="rw" description=""/>
-  <node id="ALIGN_22"    address="0x4" mask="0x07000000" permission="rw" description=""/>
-  <node id="ALIGN_23"    address="0x4" mask="0x70000000" permission="rw" description=""/>
-  <node id="ALIGN_24"    address="0x5" mask="0x00000007" permission="rw" description=""/>
-  <node id="ALIGN_25"    address="0x5" mask="0x00000070" permission="rw" description=""/>
-  <node id="ALIGN_26"    address="0x5" mask="0x00000700" permission="rw" description=""/>
-  <node id="ALIGN_27"    address="0x5" mask="0x00007000" permission="rw" description=""/>
 </node>
diff --git a/address_table/generic/modules/READOUT_BOARD.xml b/address_table/generic/modules/READOUT_BOARD.xml
index 1f2a309..3138f3e 100644
--- a/address_table/generic/modules/READOUT_BOARD.xml
+++ b/address_table/generic/modules/READOUT_BOARD.xml
@@ -4,23 +4,22 @@
   <!-- LPGBT-FPGA  -->
   <node id="LPGBT" address="0x0">
 
-    <node id="DAQ" address="0x00">
-      <node id="UPLINK"    address="0x00" module="file://LPGBT_UPLINK.xml"/>
-      <node id="DOWNLINK"  address="0x10" module="file://LPGBT_DOWNLINK.xml"/>
-    </node>
 
-    <node id="FEC_ERR_RESET" address="0x1f" mask="0x1" permission="w" description="Write 1 to reset FEC error counter"/>
+    <node id="UPLINK_0" fwinfo="type=array" address="0x00" module="file://LPGBT_UPLINK.xml"/>
+    <node id="UPLINK_1" fwinfo="type=array" address="0x10" module="file://LPGBT_UPLINK.xml"/>
 
-    <node id="TRIGGER"  address="0x20">
-      <node id="UPLINK" address="0x00" module="file://LPGBT_UPLINK.xml"/>
-    </node>
+    <node id="DOWNLINK" address="0x20" module="file://LPGBT_DOWNLINK.xml"/>
 
-    <node id="PATTERN_CHECKER" address="0x30" module="file://PATTERN_CHECKER.xml"/>
+    <node id="FEC_ERR_RESET" address="0x1f" mask="0x40" permission="w" description="Write 1 to reset FEC error counter"/>
+
+    <node id="PATTERN_CHECKER" address="0x41" module="file://PATTERN_CHECKER.xml"/>
 
   </node>
 
   <node id="BITSLIP_AUTO_EN" address="0x107" mask="0x1"        permission="rw" description="1 to enable automatic bitslipping alignment" parameters="default=0x1"/>
 
+  <node id="ELINK_WIDTH"     address="0x107" mask="0xe"        permission="rw" description="2 = 320 Mbps, 3 = 640 Mbps, 4 = 1280 Mbps" parameters="default=0x2"/>
+
   <node id="ETROC_BITSLIP"   address="0x104" mask="0xffffffff" permission="w"  description="1 to bitslip an ETROC" />
   <node id="RESET_ETROC_RX"  address="0x105" mask="0xffffffff" permission="w"  description="1 to reset the ETROC rx module" />
   <node id="ZERO_SUPRESS"    address="0x106" mask="0xffffffff" permission="rw" description="1 to zero suppress fillers out from the ETROC RX" parameters="default=0xfffffff" />
@@ -37,9 +36,14 @@
   <node id="FIFO_ELINK_SEL0"     address="0x300" mask="0x1f"         permission="rw" description="Choose which e-link the readout fifo connects to (0-27)"/>
   <node id="FIFO_LPGBT_SEL0"     address="0x300" mask="0x100"        permission="rw" description="Choose which lpgbt the readout fifo connects to (0-1)"/>
 
+  <node id="TX_FIFO_RESET"  address="0x30e" mask="0x1"        permission="w"  description="Reset the tx FIFO"/>
+  <node id="TX_FIFO_WR_EN"  address="0x30e" mask="0x2"        permission="w"  description="TX Fifo Write enable"/>
+  <node id="TX_FIFO_RD_EN"  address="0x30f" mask="0x2"        permission="rw" description="TX Fifo Read enable"/>
+  <node id="TX_FIFO_DATA"   address="0x310" mask="0xffffffff" permission="rw" description="TX Fifo Data"/>
+
   <node id="FIFO_RESET"          address="0x311" mask="0x1"          permission="w"  description="Reset the daq FIFO"/>
 
-  <node id="RX_FIFO_LOST_WORD_CNT" address="0x312" mask="0x0000ffff"   permission="r" description="# of words lost to a full FIFO"/>
+  <node id="RX_FIFO_LOST_WORD_CNT" address="0x312" mask="0xffffffff"   permission="r" description="# of words lost to a full FIFO"/>
   <node id="RX_FIFO_FULL" address="0x313" mask="0x00000001"   permission="r" description="RX FIFO is full"/>
   <node id="RX_FIFO_OCCUPANCY" address="0x314" mask="0xffffffff"   permission="r" description="RX FIFO occupancy"/>
 
@@ -50,12 +54,24 @@
   <node id="ETROC_DISABLE"        address="0x423" mask="0x0fffffff"   permission="rw"  description="Write a 1 to disable this ETROC from readout"/>
   <node id="ETROC_DISABLE_SLAVE"  address="0x424" mask="0x0fffffff"   permission="rw"  description="Write a 1 to disable this ETROC from readout"/>
 
-  <node id="LINK_RESET_PULSE" address="0x501" mask="0x00000001" permission="w"  description="Write 1 to pulse Link reset"/>
-
-  <node id="PACKET_RX_RATE" address="0x504" mask="0xffffffff" permission="r" description="Measured rate of generated received packets in Hz" />
-  <node id="PACKET_CNT" address="0x505" mask="0xffff" permission="r" description="Count of packets received (muxed across elinks)" />
-  <node id="ERROR_CNT" address="0x505" mask="0xffff0000" permission="r" description="Count of packet errors (muxed across elinks)" />
+  <node id="LINK_RESET_PULSE"  address="0x501" mask="0x00000001" permission="w"  description="Write 1 to pulse Link reset"/>
+  <node id="WS_STOP_PULSE"     address="0x501" mask="0x00000002" permission="w"  description="Write 1 to pulse Waveform Stop"/>
+  <node id="WS_START_PULSE"    address="0x501" mask="0x00000004" permission="w"  description="Write 1 to pulse Waveform Start"/>
+  <node id="QINJ_PULSE"        address="0x501" mask="0x00000008" permission="w"  description="Write 1 to pulse Charge Injection"/>
+  <node id="STP_PULSE"         address="0x501" mask="0x00000010" permission="w"  description="Write 1 to pulse STP"/>
+  <node id="ECR_PULSE"         address="0x501" mask="0x00000020" permission="w"  description="Write 1 to pulse ECR"/>
+  <node id="BC0_PULSE"         address="0x501" mask="0x00000040" permission="w"  description="Write 1 to pulse BC0"/>
+  <node id="L1A_PULSE"         address="0x501" mask="0x00000080" permission="w"  description="Write 1 to pulse L1A"/>
+  <node id="L1A_QINJ_PULSE"    address="0x501" mask="0x00000100" permission="w"  description="Write 1 to pulse Charge Injection followed by L1A"/>
+  <node id="L1A_INJ_DLY"       address="0x502" mask="0x0000ffff" permission="rw" description="Number of clock cycles (40MHz) after which the L1A should be generated for a QINJ+L1A"/>
+
+  <node id="PACKET_RX_RATE"   address="0x504" mask="0xffffffff" permission="r"  description="Measured rate of generated received packets in Hz" />
+  <node id="PACKET_CNT"       address="0x505" mask="0x0000ffff" permission="r"  description="Count of packets received (muxed across elinks)" />
+  <node id="ERROR_CNT"        address="0x505" mask="0xffff0000" permission="r"  description="Count of packet errors (muxed across elinks)" />
+  <node id="DATA_CNT"         address="0x506" mask="0xffff0000" permission="r"  description="Count of packet data frames (muxed across elinks)"/>
+  <node id="FILLER_RATE"      address="0x507" mask="0x00ffffff" permission="r"  description="Rate of packet filler frames (muxed across elinks)"/>
   <node id="PACKET_CNT_RESET" address="0x506" mask="0x00000001" permission="w"  description="Write 1 to reset packet counters"/>
-  <node id="ERR_CNT_RESET" address="0x506" mask="0x00000002" permission="w"  description="Write 1 to reset error counters"/>
+  <node id="ERR_CNT_RESET"    address="0x506" mask="0x00000002" permission="w"  description="Write 1 to reset error counters"/>
+  <node id="DATA_CNT_RESET"   address="0x506" mask="0x00000004" permission="w"  description="Write 1 to reset data counters"/>
 
 </node>
diff --git a/address_table/generic/modules/SYSTEM.xml b/address_table/generic/modules/SYSTEM.xml
index f5fee94..7b2051c 100644
--- a/address_table/generic/modules/SYSTEM.xml
+++ b/address_table/generic/modules/SYSTEM.xml
@@ -9,11 +9,14 @@
   <node id="SFP0_TX_DIS" address="0x40" mask="0x000001" permission="rw" description="Controls SFP0 Disable" parameters="default=0x0"/>
   <node id="SFP1_TX_DIS" address="0x40" mask="0x000002" permission="rw" description="Controls SFP1 Disable" parameters="default=0x0"/>
 
-  <node id="L1A_PULSE" address="0x500" mask="0x00000001" permission="w"  description="Write 1 to pulse L1A"/>
-  <node id="LINK_RESET_PULSE" address="0x501" mask="0x00000001" permission="w"  description="Write 1 to pulse Link reset"/>
-  <node id="L1A_RATE" address="0x502" mask="0xffffffff" permission="rw" description="Rate of generated triggers f_trig =(2^32-1) * clk_period * rate" parameters="default=0x00000000"/>
-  <node id="L1A_RATE_CNT" address="0x503" mask="0xffffffff" permission="r" description="Measured rate of generated triggers in Hz" />
-  <node id="EN_EXT_TRIGGER" address="0x507" mask="0x1" permission="rw" description="1 to enable the external SMA trigger" />
+  <node id="L1A_PULSE"      address="0x500" mask="0x00000001" permission="w"  description="Write 1 to pulse L1A"/>
+  <node id="QINJ_PULSE"     address="0x500" mask="0x00001000" permission="w"  description="Write 1 to pulse QINJ"/>
+  <node id="L1A_DELAY"      address="0x500" mask="0x00000ff8" permission="rw" description="Number of clock cycles to delay the L1A after a QINJ" parameters="default=0x190"/>
+  <node id="QINJ_MAKES_L1A" address="0x500" mask="0x00000004" permission="rw" description="1 for QINJ to make L1As" parameters="default=0x1"/>
+  <node id="QINJ_DEADTIME"  address="0x500" mask="0xffff0000" permission="rw" description="Minimum deadtime between charge injections" parameters="default=0x000000ff"/>
+  <node id="QINJ_RATE"      address="0x501" mask="0xffffffff" permission="rw" description="Rate of generated qinj f_trig =(2^32-1) * clk_period * rate" parameters="default=0x00000000"/>
+  <node id="L1A_RATE"       address="0x502" mask="0xffffffff" permission="rw" description="Rate of generated triggers f_trig =(2^32-1) * clk_period * rate" parameters="default=0x00000000"/>
+  <node id="L1A_RATE_CNT"   address="0x503" mask="0xffffffff" permission="r"  description="Measured rate of generated triggers in Hz" />
+  <node id="EN_EXT_TRIGGER" address="0x507" mask="0x00000001" permission="rw" description="1 to enable the external SMA trigger" />
 
 </node>
-
-- 
GitLab


From 934cf2fd5813f7d9773757530c1ccadd6542a0a3 Mon Sep 17 00:00:00 2001
From: Andrew Peck <andrew.peck@cern.ch>
Date: Tue, 11 Jul 2023 17:36:32 -0400
Subject: [PATCH 05/18] bump firmware version

---
 tamalero/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tamalero/__init__.py b/tamalero/__init__.py
index 1d6af48..f82c5ac 100644
--- a/tamalero/__init__.py
+++ b/tamalero/__init__.py
@@ -1,2 +1,2 @@
 __version__ = "0.1.0"
-__fw_version__ = "3.0.5"
+__fw_version__ = "3.0.6"
-- 
GitLab


From b11e38d2bb130b2eb01cf0f107a611fcf9f527b2 Mon Sep 17 00:00:00 2001
From: Andrew Peck <andrew.peck@cern.ch>
Date: Tue, 11 Jul 2023 17:36:42 -0400
Subject: [PATCH 06/18] better error handling

---
 tamalero/KCU.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/tamalero/KCU.py b/tamalero/KCU.py
index 7e35c84..aa5ae9f 100644
--- a/tamalero/KCU.py
+++ b/tamalero/KCU.py
@@ -48,7 +48,10 @@ class KCU:
                 self.dispatch()
 
     def read_node(self, id):
-        reg = self.hw.getNode(id)
+        try:
+            reg = self.hw.getNode(id)
+        except:
+            raise Exception(f"Failed finding node {id} in read_node")
         ret = reg.read()
         if self.auto_dispatch:
             self.dispatch()
-- 
GitLab


From d63401dad19a73b1057c6d7b79a951b900b897b0 Mon Sep 17 00:00:00 2001
From: Andrew Peck <andrew.peck@cern.ch>
Date: Tue, 11 Jul 2023 17:36:49 -0400
Subject: [PATCH 07/18] add a kcu method to print all registers

---
 tamalero/KCU.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/tamalero/KCU.py b/tamalero/KCU.py
index aa5ae9f..8ac9e7d 100644
--- a/tamalero/KCU.py
+++ b/tamalero/KCU.py
@@ -68,6 +68,15 @@ class KCU:
         reg = self.hw.getNode(id)
         self.action_reg(reg)
 
+    def print_regs(self):
+
+        for id in self.hw.getNodes():
+            reg = self.hw.getNode(id)
+            # if (reg.getModule() == ""):
+            if (reg.getMode() != uhal.BlockReadWriteMode.HIERARCHICAL):
+                print(self.format_reg(reg.getAddress(), reg.getPath()[4:], -1,
+                                self.format_permission(reg.getPermission())))
+
     def get_firmware_version(self, verbose=False, string=True):
 
         nodes = ("FW_INFO.HOG_INFO.GLOBAL_DATE",
-- 
GitLab


From 6aed589ec86d7a62e483876b22ec16a450bd6447 Mon Sep 17 00:00:00 2001
From: IsaulG3 <mygithub_account@tutanota.com>
Date: Thu, 29 Jun 2023 10:48:42 -0400
Subject: [PATCH 08/18] Modifed etroc_status endpoint to return matrix

---
 test_tamalero.py | 15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/test_tamalero.py b/test_tamalero.py
index 4fe22e8..bd25ac4 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -59,20 +59,7 @@ def create_app(rb, modules=[]):
             for j, etroc in enumerate(module.ETROCs):  # 4 ETROCs expected per module
                 if etroc.is_connected():
                     stat = etroc.pixel_sanity_check(return_matrix=True)
-                    etroc_status[i][j] = {}
-                    for k in range(16):
-                        etroc_status[i][j][k] = {}
-                        for l in range(16):
-                            etroc_status[i][j][k][l] = int(stat[k][l])
-            #for j in range(4):
-            #    etroc_status[i*4+j] = {}
-            #    # NOTE here we should get the actual status
-            #    # below is just a placeholder
-            #    for k in range(16):
-            #        etroc_status[i*4+j][k] = {}
-            #        for l in range(16):
-            #            etroc_status[i*4+j][k][l] = 1
-
+                    etroc_status[i][j] = stat.astype(int).tolist()
         return etroc_status
 
     return app
-- 
GitLab


From c95eb7d82c237f523dc2713f826a1c76a1719a8e Mon Sep 17 00:00:00 2001
From: IsaulG3 <mygithub_account@tutanota.com>
Date: Thu, 29 Jun 2023 14:47:16 -0400
Subject: [PATCH 09/18] created threshold endpoint

---
 test_tamalero.py | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/test_tamalero.py b/test_tamalero.py
index bd25ac4..22c595d 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -6,6 +6,10 @@ from tamalero.DataFrame import DataFrame
 from tamalero.ETROC import ETROC
 from tamalero.Module import Module
 
+# Import vth_scan function for threshold test endpoint
+from test_ETROC import vth_scan
+
+
 from tamalero.SCA import SCA_CONTROL
 
 import time
@@ -62,6 +66,31 @@ def create_app(rb, modules=[]):
                     etroc_status[i][j] = stat.astype(int).tolist()
         return etroc_status
 
+    @app.route('/threshold_test')
+    def get_threshold_test():
+        # These are the default arguments pass when running test_ETROC with --scan full, which runs
+        # a threshold scan for entire chip
+
+        # vth_scan_data = vth_scan(
+        #     etroc,
+        #     vth_min = 800,
+        #     vth_max = 880,
+        #     decimal = True,
+        #     fifo = fifo,
+        #     absolute = True,
+        # )
+        # vth_axis    = np.array(vth_scan_data[0])
+        # hit_rate    = np.array(vth_scan_data[1])
+        # N_pix       = len(hit_rate) # total # of pixels
+        # N_pix_w     = int(round(np.sqrt(N_pix))) # N_pix in NxN layout
+        # max_indices = np.argmax(hit_rate, axis=1)
+        # maximums    = vth_axis[max_indices]
+        # max_matrix  = np.empty([N_pix_w, N_pix_w])
+
+
+        result = "\n You Made Request to Threshold Endpoint \n"
+        return result
+
     return app
 
 if __name__ == '__main__':
-- 
GitLab


From 1a04dc0ef63471b45959230aa99dd7d21227e23d Mon Sep 17 00:00:00 2001
From: IsaulG3 <mygithub_account@tutanota.com>
Date: Thu, 20 Jul 2023 12:04:21 -0400
Subject: [PATCH 10/18] Move instantiation to allow importation of vth_scan
 into dashboard API

---
 test_ETROC.py | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/test_ETROC.py b/test_ETROC.py
index 26473ef..8ed2f12 100644
--- a/test_ETROC.py
+++ b/test_ETROC.py
@@ -104,13 +104,12 @@ def vth_scan(ETROC2, vth_min=693, vth_max=709, vth_step=1, decimal=False, fifo=N
     return [vth_axis.tolist(), run_results.tolist()]
 
 
-if __name__ == '__main__':
-
-    # initiate
-    ETROC2 = software_ETROC2()  # currently using Software ETROC2 (fake)
-    print("ETROC2 emulator instantiated, base configuration successful")
-    DF = DataFrame('ETROC2')
+# initiate
+ETROC2 = software_ETROC2()  # currently using Software ETROC2 (fake)
+print("ETROC2 emulator instantiated, base configuration successful")
+DF = DataFrame('ETROC2')
 
+if __name__ == '__main__':
     # argsparser
     import argparse
     argParser = argparse.ArgumentParser(description = "Argument parser")
-- 
GitLab


From 3370f439d3f032ed3e51a2d7cc377e90bc05e25b Mon Sep 17 00:00:00 2001
From: IsaulG3 <mygithub_account@tutanota.com>
Date: Thu, 20 Jul 2023 12:16:56 -0400
Subject: [PATCH 11/18] Added threshold scan endpoint to API

---
 test_tamalero.py | 74 ++++++++++++++++++++++++++++--------------------
 1 file changed, 44 insertions(+), 30 deletions(-)

diff --git a/test_tamalero.py b/test_tamalero.py
index 22c595d..8cc48cc 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -6,19 +6,19 @@ from tamalero.DataFrame import DataFrame
 from tamalero.ETROC import ETROC
 from tamalero.Module import Module
 
-# Import vth_scan function for threshold test endpoint
-from test_ETROC import vth_scan
-
-
 from tamalero.SCA import SCA_CONTROL
+from test_ETROC import vth_scan, fromPixNum
 
 import time
 import random
 import sys
 import os
 import uhal
+import json
+import datetime
+import numpy as np
 from emoji import emojize
-from flask import Flask
+from flask import Flask, request
 
 def create_app(rb, modules=[]):
     # FIXME this should live somewhere else in the future
@@ -31,7 +31,9 @@ def create_app(rb, modules=[]):
 
     @app.route('/rb_temp')
     def temperatures():
-        return rb.read_temp()
+        temp = rb.read_temp()
+        temp['time'] = datetime.datetime.now().isoformat()
+        return temp
 
     @app.route('/module_links')
     def get_link_status():
@@ -66,30 +68,42 @@ def create_app(rb, modules=[]):
                     etroc_status[i][j] = stat.astype(int).tolist()
         return etroc_status
 
-    @app.route('/threshold_test')
-    def get_threshold_test():
-        # These are the default arguments pass when running test_ETROC with --scan full, which runs
-        # a threshold scan for entire chip
-
-        # vth_scan_data = vth_scan(
-        #     etroc,
-        #     vth_min = 800,
-        #     vth_max = 880,
-        #     decimal = True,
-        #     fifo = fifo,
-        #     absolute = True,
-        # )
-        # vth_axis    = np.array(vth_scan_data[0])
-        # hit_rate    = np.array(vth_scan_data[1])
-        # N_pix       = len(hit_rate) # total # of pixels
-        # N_pix_w     = int(round(np.sqrt(N_pix))) # N_pix in NxN layout
-        # max_indices = np.argmax(hit_rate, axis=1)
-        # maximums    = vth_axis[max_indices]
-        # max_matrix  = np.empty([N_pix_w, N_pix_w])
-
-
-        result = "\n You Made Request to Threshold Endpoint \n"
-        return result
+    @app.route('/threshold_scan', methods=['POST'])
+    def run_threshold_scan():
+        payload = json.loads(request.data)
+        print(f"Threshold Scan on module {payload['module']}, etroc {payload['etroc']}")
+
+        module = modules[int(payload['module'])]
+        etroc = module.ETROCs[int(payload['etroc'])]
+        fifo = FIFO(rb=rb_0)
+
+        vth_scan_data = vth_scan(
+            etroc,
+            vth_min = 180,
+            vth_max = 260,
+            decimal = True,
+            fifo = fifo,
+            absolute = True,
+        )
+
+        vth_axis    = np.array(vth_scan_data[0])
+        hit_rate    = np.array(vth_scan_data[1])
+        N_pix       = len(hit_rate) # total # of pixels
+        N_pix_w     = int(round(np.sqrt(N_pix))) # N_pix in NxN layout
+        max_indices = np.argmax(hit_rate, axis=1)
+        maximums    = vth_axis[max_indices]
+        max_matrix  = np.empty([N_pix_w, N_pix_w])
+
+        for pix in range(N_pix):
+            r, c = fromPixNum(pix, N_pix_w)
+            max_matrix[r][c] = maximums[pix]
+
+        threshold_status  = {}
+        threshold_status['vth_axis'] = vth_axis.tolist()
+        threshold_status['hit_rate'] = hit_rate.tolist()
+        threshold_status['max_matrix'] = max_matrix.tolist()
+
+        return threshold_status
 
     return app
 
-- 
GitLab


From 0759c2964d65f7dced2acad5366544b4d6bcde5b Mon Sep 17 00:00:00 2001
From: IsaulG3 <mygithub_account@tutanota.com>
Date: Fri, 21 Jul 2023 13:07:55 -0400
Subject: [PATCH 12/18] disabled threading for API

---
 test_tamalero.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test_tamalero.py b/test_tamalero.py
index 8cc48cc..e506405 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -79,8 +79,8 @@ def create_app(rb, modules=[]):
 
         vth_scan_data = vth_scan(
             etroc,
-            vth_min = 180,
-            vth_max = 260,
+            vth_min = 220,
+            vth_max = 290,
             decimal = True,
             fifo = fifo,
             absolute = True,
@@ -281,7 +281,7 @@ if __name__ == '__main__':
 
     if args.server:
         app = create_app(rb_0, modules=modules)
-        app.run(port=args.port)
+        app.run(port=args.port, threaded=False)
 
     #-------------------------------------------------------------------------------
     # Read ADCs
-- 
GitLab


From b421cb6f2bfcf78b8278be17c9d3636895cf6bbd Mon Sep 17 00:00:00 2001
From: IsaulG3 <mygithub_account@tutanota.com>
Date: Mon, 24 Jul 2023 11:33:45 -0400
Subject: [PATCH 13/18] Added Readout Board flag and removed hardcoded board

---
 test_tamalero.py | 109 +++++++++++++++++++++++++----------------------
 1 file changed, 57 insertions(+), 52 deletions(-)

diff --git a/test_tamalero.py b/test_tamalero.py
index e506405..c68641e 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -75,7 +75,7 @@ def create_app(rb, modules=[]):
 
         module = modules[int(payload['module'])]
         etroc = module.ETROCs[int(payload['etroc'])]
-        fifo = FIFO(rb=rb_0)
+        fifo = FIFO(rb=rb)
 
         vth_scan_data = vth_scan(
             etroc,
@@ -137,6 +137,7 @@ if __name__ == '__main__':
     argParser.add_argument('--strict', action='store_true', default=False, help="Enforce strict limits on ADC reads for SCA and LPGBT")
     argParser.add_argument('--server', action='store_true', default=False, help="Start server")
     argParser.add_argument('--port', action='store', default=5000, type=int, help="Port to use for server")
+    argParser.add_argument('--rb', action='store', default=0, type=int, help="Specify Readout Board")
     args = argParser.parse_args()
 
 
@@ -150,7 +151,7 @@ if __name__ == '__main__':
     print ("Using KCU at address: %s"%args.kcu)
 
     kcu = None
-    rb_0 = None
+    rb = None
 
     # write to the loopback node of the KCU105 to check ethernet communication
     kcu = get_kcu(args.kcu, control_hub=args.control_hub, host=args.host, verbose=args.verbose)
@@ -159,21 +160,25 @@ if __name__ == '__main__':
         # this would cause the RB init to fail.
         sys.exit(1)
 
+    print(f'Utilizing ReadoutBoard {args.rb}')
+    rb = ReadoutBoard(args.rb, trigger=(not args.force_no_trigger), kcu=kcu, config=args.configuration)
+
+    # IDEA Loop over boards for configuration?
+    print(kcu.readout_boards)
 
-    rb_0 = ReadoutBoard(0, trigger=(not args.force_no_trigger), kcu=kcu, config=args.configuration)
     data = 0xabcd1234
     kcu.write_node("LOOPBACK.LOOPBACK", data)
     if (data != kcu.read_node("LOOPBACK.LOOPBACK")):
         print("No communications with KCU105... quitting")
         sys.exit(1)
 
-    is_configured = rb_0.DAQ_LPGBT.is_configured()
+    is_configured = rb.DAQ_LPGBT.is_configured()
     header(configured=is_configured)
 
     if args.recal_lpgbt:
-        rb_0.DAQ_LPGBT.calibrate_adc(recalibrate=True)
-        if rb_0.trigger:
-            rb_0.TRIG_LPGBT.calibrate_adc(recalibrate=True)
+        rb.DAQ_LPGBT.calibrate_adc(recalibrate=True)
+        if rb.trigger:
+            rb.TRIG_LPGBT.calibrate_adc(recalibrate=True)
 
     if not args.devel:
         check_repo_status(kcu_version=kcu.get_firmware_version(verbose=True))
@@ -186,27 +191,27 @@ if __name__ == '__main__':
 
         print("Power up init sequence for: DAQ")
 
-        #rb_0.DAQ_LPGBT.power_up_init()
+        #rb.DAQ_LPGBT.power_up_init()
 
-        rb_0.VTRX.get_version()
+        rb.VTRX.get_version()
         if (verbose):
             print ("VTRX status at power up:")
-            _ = rb_0.VTRX.status()
-            print (rb_0.VTRX.ver)
+            _ = rb.VTRX.status()
+            print (rb.VTRX.ver)
 
-        rb_0.get_trigger()
+        rb.get_trigger()
 
-        if rb_0.trigger:
+        if rb.trigger:
             #print("Configuring Trigger lpGBT")
             print (" > Power up init sequence for: Trigger")
-            rb_0.TRIG_LPGBT.power_up_init()
+            rb.TRIG_LPGBT.power_up_init()
             print("Done Configuring Trigger lpGBT")
     else:
-        rb_0.VTRX.get_version()
+        rb.VTRX.get_version()
 
 
-    if not hasattr(rb_0, "TRIG_LPGBT"):
-        rb_0.get_trigger()
+    if not hasattr(rb, "TRIG_LPGBT"):
+        rb.get_trigger()
 
     if args.power_up or args.reconfigure:
 
@@ -220,13 +225,13 @@ if __name__ == '__main__':
         else:
             alignment = False
 
-        #if rb_0.trigger:
-        #    rb_0.TRIG_LPGBT.power_up_init()
+        #if rb.trigger:
+        #    rb.TRIG_LPGBT.power_up_init()
         print("Configuring readout board")
-        rb_0.configure(alignment=alignment, data_mode=data_mode, etroc=args.etroc, verbose=args.verbose)
+        rb.configure(alignment=alignment, data_mode=data_mode, etroc=args.etroc, verbose=args.verbose)
 
-    res = rb_0.DAQ_LPGBT.get_board_id()
-    res['trigger'] = 'yes' if rb_0.trigger else 'no'
+    res = rb.DAQ_LPGBT.get_board_id()
+    res['trigger'] = 'yes' if rb.trigger else 'no'
 
     if (verbose):
         make_version_header(res)
@@ -234,25 +239,25 @@ if __name__ == '__main__':
     if args.power_up or args.reconfigure:
         # FIXME this is taken out because it sometimes sends the RB into the Nirvana.
         # Daniel will fix it when he has time.
-        #rb_0.reset_problematic_links(
+        #rb.reset_problematic_links(
         #    max_retries=10,
         #    allow_bad_links=args.allow_bad_links)
         if verbose:
-            rb_0.status()
+            rb.status()
 
     if (verbose):
         kcu.status()
 
-    rb_0.VTRX.get_version()
+    rb.VTRX.get_version()
     if (verbose):
-        _ = rb_0.VTRX.status()
+        _ = rb.VTRX.status()
 
 
     if args.power_up or args.reconfigure:
         print("Link inversions")
-        rb_0.DAQ_LPGBT.invert_links()
-        if rb_0.trigger:
-            rb_0.TRIG_LPGBT.invert_links(trigger=rb_0.trigger)
+        rb.DAQ_LPGBT.invert_links()
+        if rb.trigger:
+            rb.TRIG_LPGBT.invert_links(trigger=rb.trigger)
 
     #-------------------------------------------------------------------------------
     # Module Status
@@ -262,7 +267,7 @@ if __name__ == '__main__':
     if args.configuration == 'emulator' or args.configuration == 'modulev0':
         print("Configuring ETROCs")
         for i in range(res['n_module']):
-            modules.append(Module(rb_0, i+1))
+            modules.append(Module(rb, i+1))
 
         print()
         print("Querying module status")
@@ -280,7 +285,7 @@ if __name__ == '__main__':
                     monitoring_threads.append(module_mon(modules[i]))
 
     if args.server:
-        app = create_app(rb_0, modules=modules)
+        app = create_app(rb, modules=modules)
         app.run(port=args.port, threaded=False)
 
     #-------------------------------------------------------------------------------
@@ -289,13 +294,13 @@ if __name__ == '__main__':
 
     if args.adcs:
         print("\n\nReading GBT-SCA ADC values:")
-        rb_0.SCA.read_adcs(check=True, strict_limits=args.strict)
+        rb.SCA.read_adcs(check=True, strict_limits=args.strict)
 
         print("\n\nReading DAQ lpGBT ADC values:")
-        rb_0.DAQ_LPGBT.read_adcs(check=True, strict_limits=args.strict)
+        rb.DAQ_LPGBT.read_adcs(check=True, strict_limits=args.strict)
 
         # High level reading of temperatures
-        temp = rb_0.read_temp(verbose=True)
+        temp = rb.read_temp(verbose=True)
 
     #-------------------------------------------------------------------------------
     # I2C Test
@@ -304,7 +309,7 @@ if __name__ == '__main__':
     if args.i2c_temp:
 
         for i in range(100):
-            print ( rb_0.DAQ_LPGBT.read_temp_i2c() )
+            print ( rb.DAQ_LPGBT.read_temp_i2c() )
             time.sleep(1)
 
     #-------------------------------------------------------------------------------
@@ -316,24 +321,24 @@ if __name__ == '__main__':
         print("Writing and Reading I2C_ctrl register:")
         for n in range(10):
             wr = random.randint(0, 100)
-            rb_0.SCA.I2C_write_ctrl(channel=3, data=wr)
-            rd = rb_0.SCA.I2C_read_ctrl(channel=3)
+            rb.SCA.I2C_write_ctrl(channel=3, data=wr)
+            rd = rb.SCA.I2C_read_ctrl(channel=3)
             print("write: {} \t read: {}".format(wr, rd))
 
         print("Testing multi-byte read:")
-        multi_out = rb_0.SCA.I2C_read_multi(channel=3, servant = 0x48, nbytes=2)
+        multi_out = rb.SCA.I2C_read_multi(channel=3, servant = 0x48, nbytes=2)
         print("servant: 0x48, channel: 3, nbytes: 2, output = {}".format(multi_out))
 
         print("Testing multi-byte write:")
 
         write_value = [0x2, 25, (27&240)]
         print("servant: 0x48, channel: 3, nbytes: 2, data:{}".format(write_value))
-        rb_0.SCA.I2C_write_multi(write_value, channel=3, servant=0x48)
-        read_value = rb_0.SCA.I2C_read_multi(channel=3, servant=0x48, nbytes = 2, reg=0x2)
+        rb.SCA.I2C_write_multi(write_value, channel=3, servant=0x48)
+        read_value = rb.SCA.I2C_read_multi(channel=3, servant=0x48, nbytes = 2, reg=0x2)
 
         if read_value == write_value[1:]:
             print ("write/read successful!")
-        print("read value = {}".format(rb_0.SCA.I2C_read_multi(channel=3, servant=0x48, nbytes = 2, reg=0x2)))
+        print("read value = {}".format(rb.SCA.I2C_read_multi(channel=3, servant=0x48, nbytes = 2, reg=0x2)))
 
     #-------------------------------------------------------------------------------
     # Pattern Checkers
@@ -341,39 +346,39 @@ if __name__ == '__main__':
 
     if args.reset_pattern_checker:
         print ("\nResetting the pattern checker.")
-        rb_0.DAQ_LPGBT.set_uplink_group_data_source("normal")
-        rb_0.DAQ_LPGBT.set_downlink_data_src(args.reset_pattern_checker)
+        rb.DAQ_LPGBT.set_uplink_group_data_source("normal")
+        rb.DAQ_LPGBT.set_downlink_data_src(args.reset_pattern_checker)
         time.sleep(0.1)
-        rb_0.DAQ_LPGBT.reset_pattern_checkers()
+        rb.DAQ_LPGBT.reset_pattern_checkers()
         time.sleep(0.1)
 
     if args.run_pattern_checker:
         print ("\nReading the pattern checker counter. Waiting 1 sec.")
         time.sleep(1)
-        rb_0.DAQ_LPGBT.read_pattern_checkers()
+        rb.DAQ_LPGBT.read_pattern_checkers()
 
     #-------------------------------------------------------------------------------
     # Eyescan
     #-------------------------------------------------------------------------------
 
     if args.eyescan:
-        rb_0.DAQ_LPGBT.eyescan()
+        rb.DAQ_LPGBT.eyescan()
 
     all_tests_passed = True  # FIXME this should be properly defined
     if all_tests_passed:
-        rb_0.DAQ_LPGBT.set_configured()
+        rb.DAQ_LPGBT.set_configured()
 
     #-------------------------------------------------------------------------------
     # Success LEDs
     #-------------------------------------------------------------------------------
-    if rb_0.DAQ_LPGBT.ver == 1:
-        rb_0.DAQ_LPGBT.set_gpio("LED_1", 1) # Set LED1 after tamalero finishes succesfully
+    if rb.DAQ_LPGBT.ver == 1:
+        rb.DAQ_LPGBT.set_gpio("LED_1", 1) # Set LED1 after tamalero finishes succesfully
         t_end = time.time() + 10
         if args.power_up:
             print("RB configured successfully. Rhett is happy " + emojize(":dog_face:"))
             while time.time() < t_end:
-                rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1) # Let Rhett LED blink for 10s
+                rb.DAQ_LPGBT.set_gpio("LED_RHETT", 1) # Let Rhett LED blink for 10s
                 time.sleep(0.5)
-                rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 0)
+                rb.DAQ_LPGBT.set_gpio("LED_RHETT", 0)
                 time.sleep(0.5)
-        rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1)
+        rb.DAQ_LPGBT.set_gpio("LED_RHETT", 1)
-- 
GitLab


From 1a372888fc6ba7a7c108b257f4f1d6a5e556e565 Mon Sep 17 00:00:00 2001
From: jocain <jgocain@bu.edu>
Date: Mon, 24 Jul 2023 14:19:04 -0400
Subject: [PATCH 14/18] Allow KCU.status to show all boards, check for multiple
 boards in test_tamalero.py

---
 tamalero/KCU.py  | 30 ++++++++++++++++--------------
 test_tamalero.py | 30 +++++++++++++++++++++++++++++-
 2 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/tamalero/KCU.py b/tamalero/KCU.py
index 8ac9e7d..0216718 100644
--- a/tamalero/KCU.py
+++ b/tamalero/KCU.py
@@ -131,20 +131,22 @@ class KCU:
             self.print_reg(self.hw.getNode(id), use_color=True, threshold=1, invert=True)
 
         self.check_clock_frequencies()
-
-        locked = self.read_node(f"READOUT_BOARD_0.ETROC_LOCKED").value()
-        locked_slave = self.read_node(f"READOUT_BOARD_0.ETROC_LOCKED_SLAVE").value()
-
-        for l in range(28):
-            if (locked >> l) & 1:
-                print(green(f'Master elink {l} is locked.'))
-        for l in range(28):
-            if (locked_slave >> l) & 1:
-                print(green(f'Slave elink {l} is locked.'))
-
-        if locked | locked_slave == 0:
-            print(red('Warning: No elink is locked.'))
-
+        
+        for rb in self.readout_boards:
+            print('Checking Readout Board {rb.rb}')
+            locked = self.read_node(f"READOUT_BOARD_{rb.rb}.ETROC_LOCKED").value()
+            locked_slave = self.read_node(f"READOUT_BOARD_{rb.rb}.ETROC_LOCKED_SLAVE").value()
+
+            for l in range(28):
+                if (locked >> l) & 1:
+                    print(green(f'Master elink {l} is locked.'))
+            for l in range(28):
+                if (locked_slave >> l) & 1:
+                    print(green(f'Slave elink {l} is locked.'))
+
+            if locked | locked_slave == 0:
+                print(red('Warning: No elink is locked.'))
+            print()
 
     def print_reg(self, reg, threshold=1, maxval=0xFFFFFFFF, use_color=False, invert=False):
         from tamalero.colors import green, red, dummy
diff --git a/test_tamalero.py b/test_tamalero.py
index c68641e..8780518 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -138,6 +138,7 @@ if __name__ == '__main__':
     argParser.add_argument('--server', action='store_true', default=False, help="Start server")
     argParser.add_argument('--port', action='store', default=5000, type=int, help="Port to use for server")
     argParser.add_argument('--rb', action='store', default=0, type=int, help="Specify Readout Board")
+    argParser.add_argument('--multiboard', action = 'store_true')
     args = argParser.parse_args()
 
 
@@ -153,6 +154,33 @@ if __name__ == '__main__':
     kcu = None
     rb = None
 
+    # write to the loopback node of the KCU105 to check ethernet communication
+    kcu = get_kcu(args.kcu, control_hub=args.control_hub, host=args.host, verbose=args.verbose)
+    if (kcu == 0):
+        # if not basic connection was established the get_kcu function returns 1
+        # this would cause the RB init to fail.
+        sys.exit(1)
+
+    if args.multi_board:
+        temp = get_kcu(args.kcu, control_hub=args.control_hub, host=args.host, verbose=args.verbose)
+        if kcu == 0:
+            sys.exit(1)
+        for i in range(3):
+            try:
+                print(f'Checking ReadoutBoard {i}')
+                rb = ReadoutBoard(args.rb, trigger=(not args.force_no_trigger), kcu=kcu, config=args.configuration)
+
+                kcu.write_node("LOOPBACK.LOOPBACK", data)
+                if (data != kcu.read_node("LOOPBACK.LOOPBACK")):
+                    print(f"No communications with KCU105 for board {i}")
+                else:
+                    print(f'Successfully connected to ReadoutBoard {i}')
+                    rb.get_trigger()
+                    rb.read_temp()
+            except:
+                print(f'Connecting to ReadoutBoard {i} failed')
+
+        kcu.status()
     # write to the loopback node of the KCU105 to check ethernet communication
     kcu = get_kcu(args.kcu, control_hub=args.control_hub, host=args.host, verbose=args.verbose)
     if (kcu == 0):
@@ -162,7 +190,7 @@ if __name__ == '__main__':
 
     print(f'Utilizing ReadoutBoard {args.rb}')
     rb = ReadoutBoard(args.rb, trigger=(not args.force_no_trigger), kcu=kcu, config=args.configuration)
-
+    
     # IDEA Loop over boards for configuration?
     print(kcu.readout_boards)
 
-- 
GitLab


From bdb4d2e736db58c1493ae984b17c6cebefd6a81b Mon Sep 17 00:00:00 2001
From: jocain <jgocain@bu.edu>
Date: Mon, 24 Jul 2023 15:03:34 -0400
Subject: [PATCH 15/18] Fixed some typos in test_tamalero.py --multi_board

---
 test_tamalero.py | 21 +++++++--------------
 1 file changed, 7 insertions(+), 14 deletions(-)

diff --git a/test_tamalero.py b/test_tamalero.py
index 8780518..8618c72 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -138,7 +138,7 @@ if __name__ == '__main__':
     argParser.add_argument('--server', action='store_true', default=False, help="Start server")
     argParser.add_argument('--port', action='store', default=5000, type=int, help="Port to use for server")
     argParser.add_argument('--rb', action='store', default=0, type=int, help="Specify Readout Board")
-    argParser.add_argument('--multiboard', action = 'store_true')
+    argParser.add_argument('--multi_board', action = 'store_true')
     args = argParser.parse_args()
 
 
@@ -154,24 +154,17 @@ if __name__ == '__main__':
     kcu = None
     rb = None
 
-    # write to the loopback node of the KCU105 to check ethernet communication
-    kcu = get_kcu(args.kcu, control_hub=args.control_hub, host=args.host, verbose=args.verbose)
-    if (kcu == 0):
-        # if not basic connection was established the get_kcu function returns 1
-        # this would cause the RB init to fail.
-        sys.exit(1)
-
     if args.multi_board:
         temp = get_kcu(args.kcu, control_hub=args.control_hub, host=args.host, verbose=args.verbose)
-        if kcu == 0:
+        if temp == 0:
             sys.exit(1)
         for i in range(3):
             try:
                 print(f'Checking ReadoutBoard {i}')
-                rb = ReadoutBoard(args.rb, trigger=(not args.force_no_trigger), kcu=kcu, config=args.configuration)
-
-                kcu.write_node("LOOPBACK.LOOPBACK", data)
-                if (data != kcu.read_node("LOOPBACK.LOOPBACK")):
+                rb = ReadoutBoard(i, trigger=(not args.force_no_trigger), kcu=temp, config=args.configuration)
+                print(f'Checking data loopback')
+                temp.write_node("LOOPBACK.LOOPBACK", data)
+                if (data != temp.read_node("LOOPBACK.LOOPBACK")):
                     print(f"No communications with KCU105 for board {i}")
                 else:
                     print(f'Successfully connected to ReadoutBoard {i}')
@@ -180,7 +173,7 @@ if __name__ == '__main__':
             except:
                 print(f'Connecting to ReadoutBoard {i} failed')
 
-        kcu.status()
+        temp.status()
     # write to the loopback node of the KCU105 to check ethernet communication
     kcu = get_kcu(args.kcu, control_hub=args.control_hub, host=args.host, verbose=args.verbose)
     if (kcu == 0):
-- 
GitLab


From 0cbe52154eb06ce3740c133f9267573e44a6d7b6 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Wed, 26 Jul 2023 14:17:48 -0400
Subject: [PATCH 16/18] data_cnt reset is fixed in FW v3, fixing test_ETROC to
 be compatible

---
 test_ETROC.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/test_ETROC.py b/test_ETROC.py
index 4884280..d23cc9f 100644
--- a/test_ETROC.py
+++ b/test_ETROC.py
@@ -570,6 +570,7 @@ if __name__ == '__main__':
             else:
                 fifo.select_elink(2)
             rb_0.kcu.write_node("READOUT_BOARD_0.ERR_CNT_RESET", 1)
+            rb_0.kcu.write_node("READOUT_BOARD_0.DATA_CNT_RESET", 1)
             print("\n - Running simple threshold scan on single pixel")
             vth     = []
             count   = []
@@ -584,6 +585,7 @@ if __name__ == '__main__':
                 count.append(rb_0.kcu.read_node("READOUT_BOARD_0.DATA_CNT").value())
                 print(i, rb_0.kcu.read_node("READOUT_BOARD_0.DATA_CNT").value())
                 rb_0.kcu.write_node("READOUT_BOARD_0.ERR_CNT_RESET", 1)
+                rb_0.kcu.write_node("READOUT_BOARD_0.DATA_CNT_RESET", 1)
 
             vth_a = np.array(vth)
             count_a = np.array(count)
@@ -601,6 +603,7 @@ if __name__ == '__main__':
                 count.append(rb_0.kcu.read_node("READOUT_BOARD_0.DATA_CNT").value())
                 print(i, rb_0.kcu.read_node("READOUT_BOARD_0.DATA_CNT").value())
                 rb_0.kcu.write_node("READOUT_BOARD_0.ERR_CNT_RESET", 1)
+                rb_0.kcu.write_node("READOUT_BOARD_0.DATA_CNT_RESET", 1)
 
             print(vth)
             print(count)
@@ -701,6 +704,7 @@ if __name__ == '__main__':
 
             # reset the counter
             rb_0.kcu.write_node("READOUT_BOARD_0.ERR_CNT_RESET", 1)
+            rb_0.kcu.write_node("READOUT_BOARD_0.DATA_CNT_RESET", 1)
 
             # turn off data readout for all pixels
             etroc.wr_reg("disDataReadout", 1, broadcast=True)
@@ -723,6 +727,7 @@ if __name__ == '__main__':
             hits = rb_0.kcu.read_node("READOUT_BOARD_0.DATA_CNT").value()
             print(f"Found {hits} hits when sitting mid-slope and sending 5000 L1As")
             rb_0.kcu.write_node("READOUT_BOARD_0.ERR_CNT_RESET", 1)
+            rb_0.kcu.write_node("READOUT_BOARD_0.DATA_CNT_RESET", 1)
 
             # set threshold to threshold
             print(f"Using found threshold at {dac[res==0][0]}, using value {dac[res==0][2]} for DAC.")
@@ -736,6 +741,7 @@ if __name__ == '__main__':
             counts = []
             for i in range(500, 510, 1):
                 rb_0.kcu.write_node("READOUT_BOARD_0.ERR_CNT_RESET", 1)
+                rb_0.kcu.write_node("READOUT_BOARD_0.DATA_CNT_RESET", 1)
                 fifo.send_QInj(5000, delay=i)
                 hits = rb_0.kcu.read_node("READOUT_BOARD_0.DATA_CNT").value()
                 print(i, hits)
@@ -804,6 +810,7 @@ if __name__ == '__main__':
 
             # reset the counter
             rb_0.kcu.write_node("READOUT_BOARD_0.ERR_CNT_RESET", 1)
+            rb_0.kcu.write_node("READOUT_BOARD_0.DATA_CNT_RESET", 1)
 
             # turn off data readout for all pixels
             etroc.wr_reg("disDataReadout", 1, broadcast=True)
-- 
GitLab


From c787c9ad52411b7c6511d58344d0626cfc59c638 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Wed, 26 Jul 2023 14:18:50 -0400
Subject: [PATCH 17/18] bumping FW version to v3.0.7

---
 tamalero/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tamalero/__init__.py b/tamalero/__init__.py
index f82c5ac..7540101 100644
--- a/tamalero/__init__.py
+++ b/tamalero/__init__.py
@@ -1,2 +1,2 @@
 __version__ = "0.1.0"
-__fw_version__ = "3.0.6"
+__fw_version__ = "3.0.7"
-- 
GitLab


From ccd61e321140d40971a78849a0db84641fe7847a Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Wed, 26 Jul 2023 14:21:31 -0400
Subject: [PATCH 18/18] stupid bug, thanks CI

---
 test_tamalero.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test_tamalero.py b/test_tamalero.py
index 1a15592..b09b75a 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -260,7 +260,7 @@ if __name__ == '__main__':
     if args.power_up or args.reconfigure:
         # FIXME this is taken out because it sometimes sends the RB into the Nirvana.
         # Daniel will fix it when he has time.
-        rb_0.reset_problematic_links(
+        rb.reset_problematic_links(
             max_retries=10,
             allow_bad_links=args.allow_bad_links)
         if verbose:
-- 
GitLab