From 3dd751f30e7541ac0d0e89508f222cc9d78301a3 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 18:59:29 -0400
Subject: [PATCH 01/11] SCA mapping default change for module PCB v0, will be
 obsolete soon

---
 configs/SCA_mapping_v2.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/configs/SCA_mapping_v2.yaml b/configs/SCA_mapping_v2.yaml
index 7cd5e0e..47f9138 100644
--- a/configs/SCA_mapping_v2.yaml
+++ b/configs/SCA_mapping_v2.yaml
@@ -182,7 +182,7 @@ gpio:
     mod_d00:
         pin: 0x1E
         default: 0
-        direction: out
+        direction: in
         flavor: small
         comment: pin 32 in ETROC 0 and 1
     mod_d01:
-- 
GitLab


From 3c974d6c47c8055daa3a1abc748002a02c866d1f Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 19:00:04 -0400
Subject: [PATCH 02/11] increasing frequency tolerance by 500 Hz because we're
 sometimes just slightly outside

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

diff --git a/tamalero/KCU.py b/tamalero/KCU.py
index e65230b..7e35c84 100644
--- a/tamalero/KCU.py
+++ b/tamalero/KCU.py
@@ -177,7 +177,7 @@ class KCU:
         # print("%s = %6.2f MHz" % (id, freq))
 
         errs = 0
-        tolerance = 2500  # increased tolerance to 2.5kHz (from 2kHz)
+        tolerance = 3000  # increased tolerance to 3.0kHz (from 2kHz)
         for clock in clocks:
             freq = self.read_node(clock[0]).value()
             expect = clock[1]
-- 
GitLab


From b3b7aeab9d8fe72f95ec95ddc411504b68c6e0fb Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 19:01:06 -0400
Subject: [PATCH 03/11] adding utility functions for new configurations, fw
 download, majority vote

---
 tamalero/utils.py | 50 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 46 insertions(+), 4 deletions(-)

diff --git a/tamalero/utils.py b/tamalero/utils.py
index f96bde7..d6d7ad9 100644
--- a/tamalero/utils.py
+++ b/tamalero/utils.py
@@ -1,5 +1,6 @@
 import math
 import numpy as np
+from itertools import combinations
 from time import sleep
 from yaml import load, dump
 import os
@@ -210,6 +211,16 @@ def make_version_header(res):
 def chunk(in_list, n):
     return [in_list[i * n:(i + 1) * n] for i in range((len(in_list) + n - 1) // n )] 
 
+def get_last_commit_sha(version):
+    import requests
+    import json
+
+
+    r2 = requests.get(f"https://gitlab.cern.ch/api/v4/projects/107856/repository/commits?ref=devel")
+    log = json.loads(r2.content)
+    last_commit_sha = log[0]['id'][:7]
+    return last_commit_sha
+
 def download_address_table(version):
     import os
     import requests
@@ -219,7 +230,7 @@ def download_address_table(version):
 
     r2 = requests.get(f"https://gitlab.cern.ch/api/v4/projects/107856/repository/commits?ref=devel")
     log = json.loads(r2.content)
-    last_commit_sha = log[0]['id'][:7]
+    last_commit_sha = get_last_commit_sha(version)
 
     r = requests.get(f"https://gitlab.cern.ch/api/v4/projects/107856/repository/tree?ref={version}&&path=address_tables&&recursive=True")
     tree = json.loads(r.content)
@@ -234,7 +245,7 @@ def download_address_table(version):
         tree = json.loads(r.content)
         print (f"Local firmware version detected. Will download address table corresponding to commit {version}.")
 
-    
+    print("Making directory: address_table/{version}")
     os.makedirs(f"address_table/{version}")
     for f in tree:
         if f['type'] == 'tree':
@@ -316,9 +327,12 @@ def get_kcu(kcu_address, control_hub=True, host='localhost', verbose=False):
     if verbose:
         print (f"Address table hash: {xml_sha}")
 
-    if not os.path.isdir(f"address_table/{xml_sha}"):
-        print ("Downloading latest firmware version address table.")
+    last_commit = get_last_commit_sha(xml_sha)
+    if not os.path.isdir(f"address_table/{last_commit}"):
+        print (f"Downloading latest firmware version address table to address_table/{last_commit}")
         xml_sha = download_address_table(xml_sha)
+    else:
+        xml_sha = last_commit
 
     kcu = KCU(name="my_device",
               ipb_path=ipb_path,
@@ -328,6 +342,34 @@ def get_kcu(kcu_address, control_hub=True, host='localhost', verbose=False):
 
     return kcu
 
+def get_config(config, version='v2', verbose=False):
+    default_cfg = load_yaml(os.path.join(here, f'../configs/rb_default_{version}.yaml'))
+    if config != 'default':
+        updated_cfg = load_yaml(os.path.join(here, f'../configs/{config}_{version}.yaml'))
+        for chip in ['SCA', 'LPGBT']:
+            for interface in ['adc', 'gpio']:
+                if updated_cfg[chip][interface] is not None:
+                    for k in updated_cfg[chip][interface]:
+                        if verbose:
+                            print(f"\n - Updating configuration for {chip}, {interface}, {k} to:")
+                            print(updated_cfg[chip][interface][k])
+                        default_cfg[chip][interface][k] = updated_cfg[chip][interface][k]
+    return default_cfg
+
+def majority_vote(values, majority=None):
+    from functools import reduce
+    if majority is None:
+        majority = len(values)-1
+    combs = combinations(range(len(values)), majority)
+    votes = []
+    for comb in combs:
+        #print(comb)
+        tmp_list = [values[i] for i in comb]
+        votes.append(reduce(lambda x, y: x & y, tmp_list))
+
+    #print(votes)
+    return reduce(lambda x, y: x | y, votes)
+
 if __name__ == '__main__':
     print ("Temperature example:")
     print (get_temp(0.8159, 1.5, 10000, 25, 10000, 3900))
-- 
GitLab


From ea0fbfeca755d8d05105d0d7bde3588fd0e857b9 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 19:02:22 -0400
Subject: [PATCH 04/11] single configurations for different setups instead of
 SCA and lpGBT files. fixing lpGBT serial number

---
 configs/emulator_v2.yaml   |  25 ++
 configs/modulev0_v2.yaml   | 211 +++++++++++++++++
 configs/rb_default_v2.yaml | 468 +++++++++++++++++++++++++++++++++++++
 tamalero/LPGBT.py          | 117 ++++++----
 tamalero/ReadoutBoard.py   |  19 +-
 tamalero/SCA.py            |  37 ++-
 test_modulev0.py           | 162 +++++++++++++
 test_tamalero.py           |   7 +-
 8 files changed, 985 insertions(+), 61 deletions(-)
 create mode 100644 configs/emulator_v2.yaml
 create mode 100644 configs/modulev0_v2.yaml
 create mode 100644 configs/rb_default_v2.yaml
 create mode 100644 test_modulev0.py

diff --git a/configs/emulator_v2.yaml b/configs/emulator_v2.yaml
new file mode 100644
index 0000000..6321b87
--- /dev/null
+++ b/configs/emulator_v2.yaml
@@ -0,0 +1,25 @@
+SCA:
+    adc:
+    gpio:
+        mod_d01:
+            pin: 0x1F
+            default: 0
+            direction: out
+            flavor: small
+            comment: pin 30 in ETROC 0 and 1
+        mod_d09:
+            pin: 0x19
+            default: 0
+            direction: out
+            flavor: small
+            comment: pin 30 in ETROC 4 and 5
+        mod_d17:
+            pin: 0x0E
+            default: 0
+            direction: out
+            flavor: small
+            comment: pin 30 in ETROC 8 and 9
+
+LPGBT:
+    adc:
+    gpio:
diff --git a/configs/modulev0_v2.yaml b/configs/modulev0_v2.yaml
new file mode 100644
index 0000000..65c2179
--- /dev/null
+++ b/configs/modulev0_v2.yaml
@@ -0,0 +1,211 @@
+SCA:
+    adc:
+        mod_a00:
+            pin: 0x0B
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 0 and 1
+        mod_a01:
+            pin: 0x0C
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 0 and 1
+        mod_a02:
+            pin: 0x0E
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 0 and 1
+        mod_a03:
+            pin: 0x0F
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 0 and 1
+        mod_a04:
+            pin: 0x03
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 2 and 3
+        mod_a05:
+            pin: 0x01
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 2 and 3
+        mod_a06:
+            pin: 0x02
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 2 and 3
+        mod_a07:
+            pin: 0x0
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 2 and 3
+        mod_a08:
+            pin: 0x12
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 4 and 5
+        mod_a09:
+            pin: 0x15
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 4 and 5
+        mod_a10:
+            pin: 0x14
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 4 and 5
+        mod_a11:
+            pin: 0x11
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 4 and 5
+        mod_a12:
+            pin: 0x05
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 6 and 7
+        mod_a13:
+            pin: 0x06
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 6 and 7
+        mod_a14:
+            pin: 0x08
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 6 and 7
+        mod_a15:
+            pin: 0x09
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 6 and 7
+        mod_a16:
+            pin: 0x17
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 8 and 9
+        mod_a17:
+            pin: 0x16
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 8 and 9
+        mod_a18:
+            pin: 0x13
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 8 and 9
+        mod_a19:
+            pin: 0x10
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 8 and 9
+        mod_a20:
+            pin: 0x0D
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 10 and 11
+        mod_a21:
+            pin: 0x0A
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 10 and 11
+        mod_a22:
+            pin: 0x07
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 10 and 11
+        mod_a23:
+            pin: 0x04
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 10 and 11
+
+    gpio:
+        mod_d00:
+            pin: 0x1E
+            default: 1
+            direction: out
+            flavor: small
+            comment: 2.5V for ETROC on module 1. 1 = 2.5V is OFF, 0 = 2.5V is ON
+        mod_d01:
+            pin: 0x1F
+            default: 0
+            direction: out
+            flavor: small
+            comment: enable FEAST on module 1
+        mod_d08:
+            pin: 0x16
+            default: 1
+            direction: out
+            flavor: small
+            comment: 2.5V for ETROC on module 2. 1 = 2.5V is OFF, 0 = 2.5V is ON
+        mod_d09:
+            pin: 0x19
+            default: 0
+            direction: out
+            flavor: small
+            comment: enable FEAST on module 2
+        mod_d16:
+            pin: 0x0F
+            default: 1
+            direction: out
+            flavor: small
+            comment: 2.5V for ETROC on module 3. 1 = 2.5V is OFF, 0 = 2.5V is ON
+        mod_d17:
+            pin: 0x0E
+            default: 0
+            direction: out
+            flavor: small
+            comment: enable FEAST on module 3
+LPGBT:
+    adc:
+    gpio:
diff --git a/configs/rb_default_v2.yaml b/configs/rb_default_v2.yaml
new file mode 100644
index 0000000..7f99e79
--- /dev/null
+++ b/configs/rb_default_v2.yaml
@@ -0,0 +1,468 @@
+SCA:
+    adc:
+        mod_a00:
+            pin: 0x0B
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 0 and 1
+        mod_a01:
+            pin: 0x0C
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 0 and 1
+        mod_a02:
+            pin: 0x0E
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 0 and 1
+        mod_a03:
+            pin: 0x0F
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 0 and 1
+        mod_a04:
+            pin: 0x03
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 2 and 3
+        mod_a05:
+            pin: 0x01
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 2 and 3
+        mod_a06:
+            pin: 0x02
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 2 and 3
+        mod_a07:
+            pin: 0x0
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 2 and 3
+        mod_a08:
+            pin: 0x12
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 4 and 5
+        mod_a09:
+            pin: 0x15
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 4 and 5
+        mod_a10:
+            pin: 0x14
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 4 and 5
+        mod_a11:
+            pin: 0x11
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 4 and 5
+        mod_a12:
+            pin: 0x05
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 6 and 7
+        mod_a13:
+            pin: 0x06
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 6 and 7
+        mod_a14:
+            pin: 0x08
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 6 and 7
+        mod_a15:
+            pin: 0x09
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 6 and 7
+        mod_a16:
+            pin: 0x17
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 8 and 9
+        mod_a17:
+            pin: 0x16
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 8 and 9
+        mod_a18:
+            pin: 0x13
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 8 and 9
+        mod_a19:
+            pin: 0x10
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 8 and 9
+        mod_a20:
+            pin: 0x0D
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 10 and 11
+        mod_a21:
+            pin: 0x0A
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 10 and 11
+        mod_a22:
+            pin: 0x07
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 10 and 11
+        mod_a23:
+            pin: 0x04
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 10 and 11
+        LV_RB:
+            pin: 0x1C
+            conv: 11.
+            flavor: small
+            comment: low voltage
+        VDAC:
+            pin: 0x1D
+            conv: 1.
+            flavor: small
+            comment: temperature sensor
+
+    gpio:
+        mod_d00:
+            pin: 0x1E
+            default: 1
+            direction: in
+            flavor: small
+            comment: pin 32 in ETROC 0 and 1
+        mod_d01:
+            pin: 0x1F
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 30 in ETROC 0 and 1
+        mod_d02:
+            pin: 0x1D
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 28 in ETROC 0 and 1
+        mod_d03:
+            pin: 0x1C
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 26 in ETROC 0 and 1
+        mod_d04:
+            pin: 0x00
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 9 in ETROC 2 and 3
+        mod_d05:
+            pin: 0x02
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 11 in ETROC 2 and 3
+        mod_d06:
+            pin: 0x01
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 13 in ETROC 2 and 3
+        mod_d07:
+            pin: 0x03
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 15 in ETROC 2 and 3
+        mod_d08:
+            pin: 0x16
+            default: 1
+            direction: in
+            flavor: small
+            comment: pin 32 in ETROC 4 and 5
+        mod_d09:
+            pin: 0x19
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 30 in ETROC 4 and 5
+        mod_d10:
+            pin: 0x13
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 28 in ETROC 4 and 5
+        mod_d11:
+            pin: 0x10
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 26 in ETROC 4 and 5
+        mod_d12:
+            pin: 0x0A
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 9 in ETROC 6 and 7
+        mod_d13:
+            pin: 0x04
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 11 in ETROC 6 and 7
+        mod_d14:
+            pin: 0x07
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 13 in ETROC 6 and 7
+        mod_d15:
+            pin: 0x0D
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 15 in ETROC 6 and 7
+        mod_d16:
+            pin: 0x0F
+            default: 1
+            direction: in
+            flavor: small
+            comment: pin 32 in ETROC 8 and 9
+        mod_d17:
+            pin: 0x0E
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 30 in ETROC 8 and 9
+        mod_d18:
+            pin: 0x0C
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 28 in ETROC 8 and 9
+        mod_d19:
+            pin: 0x0B
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 26 in ETROC 8 and 9
+        mod_d20:
+            pin: 0x09
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 9 in ETROC 10 and 11
+        mod_d21:
+            pin: 0x08
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 11 in ETROC 10 and 11
+        mod_d22:
+            pin: 0x06
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 13 in ETROC 10 and 11
+        mod_d23:
+            pin: 0x05
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 15 in ETROC 10 and 11
+        sca_led:
+            pin: 0x1B
+            default: 1
+            direction: out
+            flavor: small
+            comment: SCA LED indicator
+
+LPGBT:
+    adc:
+        TH1:
+            pin: 0x00
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: VTRX TH1
+        1V4D_ADC:
+            pin: 0x01
+            conv: 1.82
+            min: 1.12
+            max: 1.68
+            flavor: small
+            comment: 1V4D * 0.55
+        1V5A_ADC:
+            pin: 0x02
+            conv: 1.82
+            min: 1.2
+            max: 1.8
+            flavor: small
+            comment: 1V5D * 0.55
+        2V5TX_ADC:
+            pin: 0x03
+            conv: 3.0
+            min: 2.0
+            max: 3.0
+            flavor: small
+            comment: 2V5TX * 0.33
+        RSSI_ADC:
+            pin: 0x04
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: RSSI
+        ADC5:
+            pin: 0x05
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: N/A
+        2V5RX_ADC:
+            pin: 0x06
+            conv: 3.0
+            min: 2.0
+            max: 3.0
+            flavor: small
+            comment: 2V5RX * 0.33
+        VTEMP_ADC:
+            pin: 0x07
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: RT1
+        EOM_ADC:
+            pin: 0x08
+            conv: 1
+            flavor: small
+            comment: EOM DAC (internal signal)
+        VDDIO:
+            pin: 0x09
+            conv: 2.38
+            flavor: small
+            comment: VDDIO * 0.42 (internal signal)
+        VDDTX:
+            pin: 0x0a
+            conv: 2.38
+            flavor: small
+            comment: VDDTX * 0.42 (internal signal)
+        VDDRX:
+            pin: 0x0b
+            conv: 2.38
+            flavor: small
+            comment: VDDRX * 0.42 (internal signal)
+        VDD:
+            pin: 0x0c
+            conv: 2.38
+            flavor: small
+            comment: VDD * 0.42 (internal signal)
+        VDDA:
+            pin: 0x0d
+            conv: 2.38
+            flavor: small
+            comment: VDDA * 0.42 (internal signal)
+        TEMP:
+            pin: 0x0e
+            conv: 1
+            flavor: small
+            comment: Temperature sensor (internal signal)
+        VREF:
+            pin: 0x0f
+            conv: 2.0
+            flavor: small
+            comment: VREF/2 (internal signal)
+    gpio:
+        SCA_RESETB:
+            pin: 0x00
+            default: 1
+            direction: out
+            flavor: small
+            comment: GBT SCA reset
+        LED_0:
+            pin: 0x01
+            default: 1
+            direction: out
+            flavor: small
+            comment: LPGBT GPIO configuration LED
+        LED_1:
+            pin: 0x02
+            default: 0
+            direction: out
+            flavor: small
+            comment: tamalero LED
+        LED_RHETT:
+            pin: 0x03
+            default: 1
+            direction: out
+            flavor: small
+            comment: success LED
+        LD_RSTN:
+            pin: 0x0A
+            default: 1
+            direction: out
+            flavor: small
+            comment: VTRX reset
+        LD_DIS:
+            pin: 0x0D
+            default: 0
+            direction: out
+            flavor: small
+            comment: VTRX DIS
diff --git a/tamalero/LPGBT.py b/tamalero/LPGBT.py
index 6f98b0e..7152a24 100644
--- a/tamalero/LPGBT.py
+++ b/tamalero/LPGBT.py
@@ -8,7 +8,7 @@ import json
 from functools import wraps
 import tamalero.colors as colors
 from tamalero.colors import red, green
-from tamalero.utils import read_mapping, chunk, load_yaml
+from tamalero.utils import read_mapping, chunk, load_yaml, get_config, majority_vote
 from time import sleep
 from datetime import datetime
 try:
@@ -35,7 +35,7 @@ def gpio_byname(gpio_func):
 
 class LPGBT(RegParser):
 
-    def __init__(self, rb=0, trigger=False, flavor='small', master=None, kcu=None, do_adc_calibration=False):
+    def __init__(self, rb=0, trigger=False, flavor='small', master=None, kcu=None, do_adc_calibration=False, config='default'):
         '''
         Initialize lpGBT for a certain readout board number (rb).
         The trigger lpGBT is accessed through I2C of the master (= DAQ lpGBT).
@@ -54,6 +54,7 @@ class LPGBT(RegParser):
         if kcu != None:
             self.kcu = kcu
 
+        self.config = config
         self.configure(do_adc_calibration=do_adc_calibration)
 
     def configure(self, do_adc_calibration=True):
@@ -168,22 +169,25 @@ class LPGBT(RegParser):
 
     def set_adc_mapping(self):
         assert self.ver in [0, 1], f"Unrecognized version {self.ver}"
-        if self.ver == 0:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'adc')
-        elif self.ver == 1:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'adc')
+        self.adc_mapping = get_config(self.config, version=f'v{self.ver+1}')['LPGBT']['adc']
+        #if self.ver == 0:
+        #    self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'adc')
+        #elif self.ver == 1:
+        #    self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'adc')
 
     def set_gpio_mapping(self):
         assert self.ver in [0, 1], f"Unrecognized version {self.ver}"
-        if self.ver == 0:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'gpio')
-        elif self.ver == 1:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'gpio')
+        self.gpio_mapping = get_config(self.config, version=f'v{self.ver+1}')['LPGBT']['gpio']
+        #if self.ver == 0:
+        #    self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'gpio')
+        #elif self.ver == 1:
+        #    self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'gpio')
 
     def update_ver(self, new_ver):
-        assert new_ver in [1, 2], f"Unrecognized version {new_ver}"
+        assert new_ver in [0, 1], f"Unrecognized version {new_ver}"
         self.ver = new_ver
         self.set_adc_mapping()
+        self.set_gpio_mapping()
 
     def link_status(self, verbose=False):
         if self.trigger:
@@ -314,32 +318,36 @@ class LPGBT(RegParser):
 
     def wr_adr(self, adr, data):
         if self.trigger:
-            raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to write to a servant")
-        #defer = not self.kcu.auto_dispatch  # if auto dispatch is turned off, keep it off.
-        #self.kcu.toggle_dispatch()  # turn off auto dispatch for this transaction
-        #self.kcu.write_node("READOUT_BOARD_%d.SC.TX_GBTX_ADDR" % self.rb, 115)
-        self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
-        self.kcu.write_node("READOUT_BOARD_%d.SC.TX_DATA_TO_GBTX" % self.rb, data)
-        self.kcu.action("READOUT_BOARD_%d.SC.TX_WR" % self.rb)
-        self.kcu.action("READOUT_BOARD_%d.SC.TX_START_WRITE" % self.rb)
-        #return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
-        #if not defer:  # turn auto dispatch back on only if it wasn't set to false before
-        #    self.kcu.dispatch()
-        #self.rd_flush()
+            return self.master.I2C_write(adr, data)
+            #raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to write to a servant")
+        else:
+            #defer = not self.kcu.auto_dispatch  # if auto dispatch is turned off, keep it off.
+            #self.kcu.toggle_dispatch()  # turn off auto dispatch for this transaction
+            #self.kcu.write_node("READOUT_BOARD_%d.SC.TX_GBTX_ADDR" % self.rb, 115)
+            self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
+            self.kcu.write_node("READOUT_BOARD_%d.SC.TX_DATA_TO_GBTX" % self.rb, data)
+            self.kcu.action("READOUT_BOARD_%d.SC.TX_WR" % self.rb)
+            self.kcu.action("READOUT_BOARD_%d.SC.TX_START_WRITE" % self.rb)
+            #return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
+            #if not defer:  # turn auto dispatch back on only if it wasn't set to false before
+            #    self.kcu.dispatch()
+            #self.rd_flush()
 
     def rd_adr(self, adr):
         if self.trigger:
-            raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to read from a servant")
-        self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
-        self.kcu.action("READOUT_BOARD_%d.SC.TX_START_READ" % self.rb)
-        valid = self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_VALID" % self.rb).valid()
-        if valid:
-            # this only means that the KCU successfully read data
-            # not necessarily does it mean there's communication with the lpGBT
-            return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
-
-        print("LpGBT read failed!")
-        return None
+            return self.master.I2C_read(adr)
+            #raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to read from a servant")
+        else:
+            self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
+            self.kcu.action("READOUT_BOARD_%d.SC.TX_START_READ" % self.rb)
+            valid = self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_VALID" % self.rb).valid()
+            if valid:
+                # this only means that the KCU successfully read data
+                # not necessarily does it mean there's communication with the lpGBT
+                return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
+
+            print("LpGBT read failed!")
+            return None
 
     def wr_reg(self, id, data):
         node = self.get_node(id)
@@ -752,9 +760,9 @@ class LPGBT(RegParser):
             return serial != 0
 
         if (self.ver==0):
-            serial = self.get_chip_userid()
+            serial = str(self.get_chip_userid())
         else:
-            serial = self.get_chip_serial()
+            serial = str(self.get_chip_serial())
 
         cal_file = "lpgbt_adc_calibrations.json"
 
@@ -769,7 +777,7 @@ class LPGBT(RegParser):
         if serial_valid(serial) and serial in cal_data and not recalibrate:
             gain = cal_data[serial]['gain']
             offset = cal_data[serial]['offset']
-            print("Loaded ADC calibration data for chip %d. Gain: %f / Offset: %d" % (serial, gain, offset))
+            print("Loaded ADC calibration data for chip %s. Gain: %f / Offset: %d" % (serial, gain, offset))
 
         # else, determine calibration constants
         else:
@@ -788,6 +796,7 @@ class LPGBT(RegParser):
             self.wr_reg("LPGBT.RW.ADC.VDDMONENA", initial_val)
             type = "Trigger" if self.trigger else "DAQ"
             print("Calibrated %s ADC. Gain: %f / Offset: %d" % (type, gain, offset))
+            print("Chip %s"%serial)
 
             if gain < 1.65 or gain > 2 or offset < 490 or offset > 530:
                 raise RuntimeError("ADC Calibration Failed!")
@@ -1429,10 +1438,36 @@ class LPGBT(RegParser):
                self.rd_reg("LPGBT.RWF.CHIPID.USERID0")
 
     def get_chip_serial(self):
-        return self.rd_reg("LPGBT.RWF.CHIPID.CHIPID3") << 24 |\
-               self.rd_reg("LPGBT.RWF.CHIPID.CHIPID2") << 16 |\
-               self.rd_reg("LPGBT.RWF.CHIPID.CHIPID1") << 8 |\
-               self.rd_reg("LPGBT.RWF.CHIPID.CHIPID0")
+        if self.ver == 1:
+            # NOTE we have to read from the fuses directly.
+            # ideally this can still be verified (May 2023)
+            self.wr_adr(0x119, 0x1 << 1)  # write FuseRead https://lpgbt.web.cern.ch/lpgbt/v1/registermap.html#reg-fusecontrol
+            while True:
+                # wait for FuseDataValid https://lpgbt.web.cern.ch/lpgbt/v1/registermap.html#reg-fusestatus
+                if self.rd_adr(0x1b1) >> 2 == 1: break
+
+            chipids = []
+            # there should be 5 copies of the chipid, but I can only find 4
+            # there's nothing else in the fuses that's non-zero
+            for i in range(4):
+                self.wr_adr(0x11f, i)
+                chipids.append(self.rd_adr(0x1b2) << 24 | self.rd_adr(0x1b3) << 16 | self.rd_adr(0x1b4) << 8 | self.rd_adr(0x1b5) << 24)
+
+            self.wr_adr(0x119, 0)  # write FuseRead https://lpgbt.web.cern.ch/lpgbt/v1/registermap.html#reg-fusecontrol
+
+            if all([c==chipids[0] for c in chipids]):
+                return chipids[0]
+            else:
+                print("CHIPD serial needs majority vote")
+                return majority_vote(chipids, majority=3)
+
+        elif self.ver == 0:
+            # NOTE: this is what's supposed to work for lpGBT v0
+            # but note sure if that's actually true
+            return self.rd_reg("LPGBT.RWF.CHIPID.CHIPID3") << 24 |\
+                self.rd_reg("LPGBT.RWF.CHIPID.CHIPID2") << 16 |\
+                self.rd_reg("LPGBT.RWF.CHIPID.CHIPID1") << 8 |\
+                self.rd_reg("LPGBT.RWF.CHIPID.CHIPID0")
 
     def get_power_up_state_machine(self, quiet=True):
 
diff --git a/tamalero/ReadoutBoard.py b/tamalero/ReadoutBoard.py
index 92dd53c..ff35243 100644
--- a/tamalero/ReadoutBoard.py
+++ b/tamalero/ReadoutBoard.py
@@ -8,22 +8,23 @@ from time import sleep
 
 class ReadoutBoard:
 
-    def __init__(self, rb=0, trigger=True, flavor='small', kcu=None):
+    def __init__(self, rb=0, trigger=True, flavor='small', kcu=None, config='default'):
         '''
         create a readout board.
         trigger: if true, also configure a trigger lpGBT
         '''
         self.rb = rb
         self.flavor = flavor
-        self.ver = 1
+        self.ver = 2
+        self.config = config
 
         self.trigger = trigger
-        self.DAQ_LPGBT = LPGBT(rb=rb, flavor=flavor, kcu=kcu)
+        self.DAQ_LPGBT = LPGBT(rb=rb, flavor=flavor, kcu=kcu, config=self.config)
         self.VTRX = VTRX(self.DAQ_LPGBT)
         # This is not yet recommended:
         #for adr in [0x06, 0x0A, 0x0E, 0x12]:
         #    self.VTRX.wr_adr(adr, 0x20)
-        self.SCA = SCA(rb=rb, flavor=flavor, ver=self.DAQ_LPGBT.ver)
+        self.SCA = SCA(rb=rb, flavor=flavor, ver=self.DAQ_LPGBT.ver, config=self.config)
 
         if kcu != None:
             self.kcu = kcu
@@ -32,7 +33,11 @@ class ReadoutBoard:
             if self.DAQ_LPGBT.ver == 1:
                 self.ver = 2
                 self.SCA.update_ver(self.ver)
-                self.DAQ_LPGBT.set_adc_mapping()
+                self.DAQ_LPGBT.update_ver(self.ver-1)  #  FIXME we need to disentangle lpGBT version from RB version
+            elif self.DAQ_LPGBT.ver == 0:
+                self.ver = 1
+                self.SCA.update_ver(self.ver)
+                self.DAQ_LPGBT.update_ver(self.ver-1)  # FIXME we need to disentangle lpGBT version from RB version
             self.SCA.connect_KCU(kcu)
 
     def get_trigger(self):
@@ -52,7 +57,7 @@ class ReadoutBoard:
             print ("Trigger lpGBT was found, but will not be added.")
 
         if self.trigger:
-            self.TRIG_LPGBT = LPGBT(rb=self.rb, flavor=self.flavor, trigger=True, master=self.DAQ_LPGBT, kcu=self.kcu)
+            self.TRIG_LPGBT = LPGBT(rb=self.rb, flavor=self.flavor, trigger=True, master=self.DAQ_LPGBT, kcu=self.kcu, config=self.config)
 
 
     def connect_KCU(self, kcu):
@@ -291,6 +296,8 @@ class ReadoutBoard:
         self.SCA.reset()
         self.SCA.connect()
         try:
+            print("version in SCA", self.SCA.ver)
+            print("config in SCA", self.SCA.config)
             self.SCA.config_gpios()  # this sets the directions etc according to the mapping
         except TimeoutError:
             print ("SCA config failed. Will continue without SCA.")
diff --git a/tamalero/SCA.py b/tamalero/SCA.py
index f744058..2809d9c 100644
--- a/tamalero/SCA.py
+++ b/tamalero/SCA.py
@@ -1,6 +1,7 @@
 import os
 import random
-from tamalero.utils import read_mapping
+from tamalero.utils import read_mapping, get_config
+from functools import wraps
 import time
 try:
     from tabulate import tabulate
@@ -110,34 +111,43 @@ class SCA_I2C:
     I2C_R_DATA3 = 0x71 # read from data register 3
     I2C_RW_DATA_OFFSET = 16 # offset to access data register 1, 2, 3
 
+def gpio_byname(gpio_func):
+    @wraps(gpio_func)
+    def wrapper(lpgbt, pin, direction=1):
+        if isinstance(pin, str):
+            gpio_dict = lpgbt.gpio_mapping
+            pin = gpio_dict[pin]['pin']
+            return gpio_func(lpgbt, pin, direction)
+        elif isinstance(pin, int):
+            return gpio_func(lpgbt, pin, direction)
+        else:
+            invalid_type = type(pin)
+            raise TypeError(f"{gpio_func.__name__} can only take positional arguments of type int or str, but argument of type {invalid_type} was given.")
+
+    return wrapper
 
 class SCA:
 
-    def __init__(self, rb=0, flavor='small', ver=0):
+    def __init__(self, rb=0, flavor='small', ver=0, config='default'):
         self.rb = rb
         self.flavor = flavor
         self.err_count = 0
         self.ver = ver + 1  # NOTE don't particularly like this, but we're giving it the lpGBT version
+        self.config = config
+        self.locked = False
         self.set_adc_mapping()
         self.set_gpio_mapping()
-        self.locked = False
 
     def connect_KCU(self, kcu):
         self.kcu = kcu
 
     def set_adc_mapping(self):
         assert self.ver in [1, 2], f"Unrecognized version {self.ver}"
-        if self.ver == 1:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping.yaml'), 'adc')
-        elif self.ver == 2:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping_v2.yaml'), 'adc')
+        self.adc_mapping = get_config(self.config, version=f'v{self.ver}')['SCA']['adc']
 
     def set_gpio_mapping(self):
         assert self.ver in [1, 2], f"Unrecognized version {self.ver}"
-        if self.ver == 1:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping.yaml'), 'gpio')
-        elif self.ver == 2:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping_v2.yaml'), 'gpio')
+        self.gpio_mapping = get_config(self.config, version=f'v{self.ver}')['SCA']['gpio']
 
     def update_ver(self, new_ver):
         assert new_ver in [1, 2], f"Unrecognized version {new_ver}"
@@ -447,6 +457,7 @@ class SCA:
         val = self.rw_reg(SCA_GPIO.GPIO_R_DATAIN).value()
         return int((val >> line) & 1)
 
+    @gpio_byname
     def set_gpio(self, line, to=1):
         self.enable_gpio()  # enable GPIO
         currently_set = self.rw_reg(SCA_GPIO.GPIO_R_DATAOUT).value()
@@ -458,6 +469,7 @@ class SCA:
         self.rw_reg(SCA_GPIO.GPIO_W_DATAOUT, currently_set)
         return self.read_gpio(line)  # in order to check it is actually set
 
+    @gpio_byname
     def set_gpio_direction(self, line, to=1):
         self.enable_gpio()  # enable GPIO
         currently_set = self.rw_reg(SCA_GPIO.GPIO_R_DIRECTION).value()
@@ -491,8 +503,9 @@ class SCA:
             default     = gpio_dict[gpio_reg]['default']
             if verbose:
                 print("Setting SCA GPIO pin %s (%s) to %s"%(pin, comment, gpio_dict[gpio_reg]['direction']))
+            self.set_gpio(pin, default)  # NOTE this is important because otherwise the GPIO pin can be set to a false default value when switched to output
             self.set_gpio_direction(pin, direction)
-            self.set_gpio(pin, default)
+            self.set_gpio(pin, default)  # redundant but keep it
 
     def get_I2C_channel(self, channel):
         channel_str = hex(channel).upper()[-1]
diff --git a/test_modulev0.py b/test_modulev0.py
new file mode 100644
index 0000000..5a4692e
--- /dev/null
+++ b/test_modulev0.py
@@ -0,0 +1,162 @@
+from tamalero.KCU import KCU
+from tamalero.ReadoutBoard import ReadoutBoard
+from tamalero.utils import header, make_version_header, get_kcu, check_repo_status
+from tamalero.FIFO import FIFO
+from tamalero.DataFrame import DataFrame
+from tamalero.ETROC import ETROC
+from tamalero.Module import Module
+
+from tamalero.SCA import SCA_CONTROL
+
+import time
+import random
+import sys
+import os
+import uhal
+from emoji import emojize
+
+if __name__ == '__main__':
+
+
+    import argparse
+
+    argParser = argparse.ArgumentParser(description = "Argument parser")
+    argParser.add_argument('--verbose', action='store_true', default=False, help="Verbose power up sequence")
+    argParser.add_argument('--power_up', action='store_true', default=False, help="Do lpGBT power up init?")
+    argParser.add_argument('--reconfigure', action='store_true', default=False, help="Configure the RB electronics: SCA and lpGBT?")
+    argParser.add_argument('--adcs', action='store_true', default=False, help="Read ADCs?")
+    argParser.add_argument('--i2c_temp', action='store_true', default=False, help="Do temp monitoring on I2C from lpGBT?")
+    argParser.add_argument('--i2c_sca', action='store_true', default=False, help="I2C tests on SCA?")
+    argParser.add_argument('--kcu', action='store', default="192.168.0.10", help="Specify the IP address for KCU")
+    argParser.add_argument('--control_hub', action='store_true', default=False, help="Use control hub for communication?")
+    argParser.add_argument('--host', action='store', default='localhost', help="Specify host for control hub")
+    argParser.add_argument('--configuration', action='store', default='modulev0', choices=['default', 'emulator', 'modulev0'], help="Specify a configuration of the RB, e.g. emulator or modulev0")
+    argParser.add_argument('--devel', action='store_true', default=False, help="Don't check repo status (not recommended)")
+    argParser.add_argument('--connection_test', action='store_true', default=False, help="Check the PCB connections.")
+    args = argParser.parse_args()
+
+
+    verbose = args.verbose
+
+    #-------------------------------------------------------------------------------
+    # Try to Connect to the KCU105
+    #-------------------------------------------------------------------------------
+
+    print ("Using KCU at address: %s"%args.kcu)
+
+    kcu = None
+    rb_0 = 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 0
+        # this would cause the RB init to fail.
+        sys.exit(1)
+
+
+    rb_0 = ReadoutBoard(0, 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()
+    if not is_configured:
+        print("RB is not configured, exiting.")
+        exit(0)
+    header(configured=is_configured)
+
+    if not args.devel:
+        check_repo_status(kcu_version=kcu.get_firmware_version(verbose=True))
+
+    rb_0.VTRX.get_version()
+
+    if not hasattr(rb_0, "TRIG_LPGBT"):
+        rb_0.get_trigger()
+
+    res = rb_0.DAQ_LPGBT.get_board_id()
+    res['trigger'] = 'yes' if rb_0.trigger else 'no'
+
+    if (verbose):
+        make_version_header(res)
+
+    if args.adcs:
+        print("\n\nReading GBT-SCA ADC values:")
+        rb_0.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)
+
+        # High level reading of temperatures
+        temp = rb_0.read_temp(verbose=True)
+
+    #-------------------------------------------------------------------------------
+    # Read SCA
+    #-------------------------------------------------------------------------------
+
+    if args.i2c_sca:
+
+        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)
+            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)
+        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)
+
+        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)))
+
+
+    #-------------------------------------------------------------------------------
+    # Success LEDs
+    #-------------------------------------------------------------------------------
+    rb_0.DAQ_LPGBT.set_gpio("LED_1", 1) # Set LED1 after tamalero finishes succesfully
+    rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1) # Set LED1 after tamalero finishes succesfully
+    if rb_0.DAQ_LPGBT.ver == 1 and args.connection_test:
+        print("Toggling FEAST and 2.5V on/off for 1min")
+        t_end = time.time() + 60
+        while time.time() < t_end:
+            rb_0.DAQ_LPGBT.set_gpio("LED_1", 1) # Let Rhett LED blink for 10s
+            rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1) # Let Rhett LED blink for 10s
+            rb_0.SCA.set_gpio("mod_d00", 1)
+            rb_0.SCA.set_gpio("mod_d01", 1)
+            rb_0.SCA.set_gpio("mod_d08", 1)
+            rb_0.SCA.set_gpio("mod_d09", 1)
+            rb_0.SCA.set_gpio("mod_d16", 1)
+            rb_0.SCA.set_gpio("mod_d17", 1)
+            time.sleep(2.0)
+            rb_0.DAQ_LPGBT.set_gpio("LED_1", 0)
+            rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 0)
+            rb_0.SCA.set_gpio("mod_d00", 0)
+            rb_0.SCA.set_gpio("mod_d01", 0)
+            rb_0.SCA.set_gpio("mod_d08", 0)
+            rb_0.SCA.set_gpio("mod_d09", 0)
+            rb_0.SCA.set_gpio("mod_d16", 0)
+            rb_0.SCA.set_gpio("mod_d17", 0)
+            time.sleep(2.0)
+        rb_0.DAQ_LPGBT.set_gpio("LED_1", 1)
+        rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1)
+
+        # disabling 2.5V
+        rb_0.SCA.set_gpio("mod_d00", 1)
+        rb_0.SCA.set_gpio("mod_d08", 1)
+        rb_0.SCA.set_gpio("mod_d16", 1)
+
+        # enabling FEAST
+        rb_0.SCA.set_gpio("mod_d01", 1)
+        rb_0.SCA.set_gpio("mod_d09", 1)
+        rb_0.SCA.set_gpio("mod_d17", 1)
diff --git a/test_tamalero.py b/test_tamalero.py
index a1adf0f..57d941c 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -39,6 +39,7 @@ if __name__ == '__main__':
     argParser.add_argument('--recal_lpgbt', action='store_true', default=False, help="Recalibrate ADC in LPGBT? (instead of using saved values)")
     argParser.add_argument('--control_hub', action='store_true', default=False, help="Use control hub for communication?")
     argParser.add_argument('--host', action='store', default='localhost', help="Specify host for control hub")
+    argParser.add_argument('--configuration', action='store', default='default', choices=['default', 'emulator', 'modulev0'], help="Specify a configuration of the RB, e.g. emulator or modulev0")
     argParser.add_argument('--devel', action='store_true', default=False, help="Don't check repo status (not recommended)")
     argParser.add_argument('--monitor', action='store_true', default=False, help="Start up montoring threads in the background")
     argParser.add_argument('--strict', action='store_true', default=False, help="Enforce strict limits on ADC reads for SCA and LPGBT")
@@ -63,7 +64,9 @@ if __name__ == '__main__':
         # if not basic connection was established the get_kcu function returns 0
         # this would cause the RB init to fail.
         sys.exit(1)
-    rb_0 = ReadoutBoard(0, trigger=(not args.force_no_trigger), kcu=kcu)
+
+
+    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")):
@@ -159,7 +162,7 @@ if __name__ == '__main__':
     # Module Status
     #-------------------------------------------------------------------------------
 
-    if args.verbose:
+    if args.verbose and args.configuration == 'emulator':
         print("Configuring ETROCs")
         modules = []
         for i in range(res['n_module']):
-- 
GitLab


From 8551043583e0131bdade4e9b2de4a7a2d72605b0 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 18:59:29 -0400
Subject: [PATCH 05/11] SCA mapping default change for module PCB v0, will be
 obsolete soon

---
 configs/SCA_mapping_v2.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/configs/SCA_mapping_v2.yaml b/configs/SCA_mapping_v2.yaml
index 7cd5e0e..47f9138 100644
--- a/configs/SCA_mapping_v2.yaml
+++ b/configs/SCA_mapping_v2.yaml
@@ -182,7 +182,7 @@ gpio:
     mod_d00:
         pin: 0x1E
         default: 0
-        direction: out
+        direction: in
         flavor: small
         comment: pin 32 in ETROC 0 and 1
     mod_d01:
-- 
GitLab


From c60a5e314067014e348f74afb699a1cf6a6b45e9 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 19:00:04 -0400
Subject: [PATCH 06/11] increasing frequency tolerance by 500 Hz because we're
 sometimes just slightly outside

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

diff --git a/tamalero/KCU.py b/tamalero/KCU.py
index e65230b..7e35c84 100644
--- a/tamalero/KCU.py
+++ b/tamalero/KCU.py
@@ -177,7 +177,7 @@ class KCU:
         # print("%s = %6.2f MHz" % (id, freq))
 
         errs = 0
-        tolerance = 2500  # increased tolerance to 2.5kHz (from 2kHz)
+        tolerance = 3000  # increased tolerance to 3.0kHz (from 2kHz)
         for clock in clocks:
             freq = self.read_node(clock[0]).value()
             expect = clock[1]
-- 
GitLab


From 6a2643f2897b56bd0bebfa15b4b4c6efac75d67b Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 19:01:06 -0400
Subject: [PATCH 07/11] adding utility functions for new configurations, fw
 download, majority vote

---
 tamalero/utils.py | 50 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 46 insertions(+), 4 deletions(-)

diff --git a/tamalero/utils.py b/tamalero/utils.py
index f96bde7..d6d7ad9 100644
--- a/tamalero/utils.py
+++ b/tamalero/utils.py
@@ -1,5 +1,6 @@
 import math
 import numpy as np
+from itertools import combinations
 from time import sleep
 from yaml import load, dump
 import os
@@ -210,6 +211,16 @@ def make_version_header(res):
 def chunk(in_list, n):
     return [in_list[i * n:(i + 1) * n] for i in range((len(in_list) + n - 1) // n )] 
 
+def get_last_commit_sha(version):
+    import requests
+    import json
+
+
+    r2 = requests.get(f"https://gitlab.cern.ch/api/v4/projects/107856/repository/commits?ref=devel")
+    log = json.loads(r2.content)
+    last_commit_sha = log[0]['id'][:7]
+    return last_commit_sha
+
 def download_address_table(version):
     import os
     import requests
@@ -219,7 +230,7 @@ def download_address_table(version):
 
     r2 = requests.get(f"https://gitlab.cern.ch/api/v4/projects/107856/repository/commits?ref=devel")
     log = json.loads(r2.content)
-    last_commit_sha = log[0]['id'][:7]
+    last_commit_sha = get_last_commit_sha(version)
 
     r = requests.get(f"https://gitlab.cern.ch/api/v4/projects/107856/repository/tree?ref={version}&&path=address_tables&&recursive=True")
     tree = json.loads(r.content)
@@ -234,7 +245,7 @@ def download_address_table(version):
         tree = json.loads(r.content)
         print (f"Local firmware version detected. Will download address table corresponding to commit {version}.")
 
-    
+    print("Making directory: address_table/{version}")
     os.makedirs(f"address_table/{version}")
     for f in tree:
         if f['type'] == 'tree':
@@ -316,9 +327,12 @@ def get_kcu(kcu_address, control_hub=True, host='localhost', verbose=False):
     if verbose:
         print (f"Address table hash: {xml_sha}")
 
-    if not os.path.isdir(f"address_table/{xml_sha}"):
-        print ("Downloading latest firmware version address table.")
+    last_commit = get_last_commit_sha(xml_sha)
+    if not os.path.isdir(f"address_table/{last_commit}"):
+        print (f"Downloading latest firmware version address table to address_table/{last_commit}")
         xml_sha = download_address_table(xml_sha)
+    else:
+        xml_sha = last_commit
 
     kcu = KCU(name="my_device",
               ipb_path=ipb_path,
@@ -328,6 +342,34 @@ def get_kcu(kcu_address, control_hub=True, host='localhost', verbose=False):
 
     return kcu
 
+def get_config(config, version='v2', verbose=False):
+    default_cfg = load_yaml(os.path.join(here, f'../configs/rb_default_{version}.yaml'))
+    if config != 'default':
+        updated_cfg = load_yaml(os.path.join(here, f'../configs/{config}_{version}.yaml'))
+        for chip in ['SCA', 'LPGBT']:
+            for interface in ['adc', 'gpio']:
+                if updated_cfg[chip][interface] is not None:
+                    for k in updated_cfg[chip][interface]:
+                        if verbose:
+                            print(f"\n - Updating configuration for {chip}, {interface}, {k} to:")
+                            print(updated_cfg[chip][interface][k])
+                        default_cfg[chip][interface][k] = updated_cfg[chip][interface][k]
+    return default_cfg
+
+def majority_vote(values, majority=None):
+    from functools import reduce
+    if majority is None:
+        majority = len(values)-1
+    combs = combinations(range(len(values)), majority)
+    votes = []
+    for comb in combs:
+        #print(comb)
+        tmp_list = [values[i] for i in comb]
+        votes.append(reduce(lambda x, y: x & y, tmp_list))
+
+    #print(votes)
+    return reduce(lambda x, y: x | y, votes)
+
 if __name__ == '__main__':
     print ("Temperature example:")
     print (get_temp(0.8159, 1.5, 10000, 25, 10000, 3900))
-- 
GitLab


From edd66fb63ff1e8e8c00fb8bfe27dfccc5138c5e6 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Fri, 5 May 2023 19:02:22 -0400
Subject: [PATCH 08/11] single configurations for different setups instead of
 SCA and lpGBT files. fixing lpGBT serial number

---
 configs/emulator_v2.yaml   |  25 ++
 configs/modulev0_v2.yaml   | 211 +++++++++++++++++
 configs/rb_default_v2.yaml | 468 +++++++++++++++++++++++++++++++++++++
 tamalero/LPGBT.py          | 117 ++++++----
 tamalero/ReadoutBoard.py   |  19 +-
 tamalero/SCA.py            |  37 ++-
 test_modulev0.py           | 162 +++++++++++++
 test_tamalero.py           |   7 +-
 8 files changed, 985 insertions(+), 61 deletions(-)
 create mode 100644 configs/emulator_v2.yaml
 create mode 100644 configs/modulev0_v2.yaml
 create mode 100644 configs/rb_default_v2.yaml
 create mode 100644 test_modulev0.py

diff --git a/configs/emulator_v2.yaml b/configs/emulator_v2.yaml
new file mode 100644
index 0000000..6321b87
--- /dev/null
+++ b/configs/emulator_v2.yaml
@@ -0,0 +1,25 @@
+SCA:
+    adc:
+    gpio:
+        mod_d01:
+            pin: 0x1F
+            default: 0
+            direction: out
+            flavor: small
+            comment: pin 30 in ETROC 0 and 1
+        mod_d09:
+            pin: 0x19
+            default: 0
+            direction: out
+            flavor: small
+            comment: pin 30 in ETROC 4 and 5
+        mod_d17:
+            pin: 0x0E
+            default: 0
+            direction: out
+            flavor: small
+            comment: pin 30 in ETROC 8 and 9
+
+LPGBT:
+    adc:
+    gpio:
diff --git a/configs/modulev0_v2.yaml b/configs/modulev0_v2.yaml
new file mode 100644
index 0000000..65c2179
--- /dev/null
+++ b/configs/modulev0_v2.yaml
@@ -0,0 +1,211 @@
+SCA:
+    adc:
+        mod_a00:
+            pin: 0x0B
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 0 and 1
+        mod_a01:
+            pin: 0x0C
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 0 and 1
+        mod_a02:
+            pin: 0x0E
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 0 and 1
+        mod_a03:
+            pin: 0x0F
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 0 and 1
+        mod_a04:
+            pin: 0x03
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 2 and 3
+        mod_a05:
+            pin: 0x01
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 2 and 3
+        mod_a06:
+            pin: 0x02
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 2 and 3
+        mod_a07:
+            pin: 0x0
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 2 and 3
+        mod_a08:
+            pin: 0x12
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 4 and 5
+        mod_a09:
+            pin: 0x15
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 4 and 5
+        mod_a10:
+            pin: 0x14
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 4 and 5
+        mod_a11:
+            pin: 0x11
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 4 and 5
+        mod_a12:
+            pin: 0x05
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 6 and 7
+        mod_a13:
+            pin: 0x06
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 6 and 7
+        mod_a14:
+            pin: 0x08
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 6 and 7
+        mod_a15:
+            pin: 0x09
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 6 and 7
+        mod_a16:
+            pin: 0x17
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 8 and 9
+        mod_a17:
+            pin: 0x16
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 8 and 9
+        mod_a18:
+            pin: 0x13
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 8 and 9
+        mod_a19:
+            pin: 0x10
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 8 and 9
+        mod_a20:
+            pin: 0x0D
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 10 and 11
+        mod_a21:
+            pin: 0x0A
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 10 and 11
+        mod_a22:
+            pin: 0x07
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 10 and 11
+        mod_a23:
+            pin: 0x04
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 10 and 11
+
+    gpio:
+        mod_d00:
+            pin: 0x1E
+            default: 1
+            direction: out
+            flavor: small
+            comment: 2.5V for ETROC on module 1. 1 = 2.5V is OFF, 0 = 2.5V is ON
+        mod_d01:
+            pin: 0x1F
+            default: 0
+            direction: out
+            flavor: small
+            comment: enable FEAST on module 1
+        mod_d08:
+            pin: 0x16
+            default: 1
+            direction: out
+            flavor: small
+            comment: 2.5V for ETROC on module 2. 1 = 2.5V is OFF, 0 = 2.5V is ON
+        mod_d09:
+            pin: 0x19
+            default: 0
+            direction: out
+            flavor: small
+            comment: enable FEAST on module 2
+        mod_d16:
+            pin: 0x0F
+            default: 1
+            direction: out
+            flavor: small
+            comment: 2.5V for ETROC on module 3. 1 = 2.5V is OFF, 0 = 2.5V is ON
+        mod_d17:
+            pin: 0x0E
+            default: 0
+            direction: out
+            flavor: small
+            comment: enable FEAST on module 3
+LPGBT:
+    adc:
+    gpio:
diff --git a/configs/rb_default_v2.yaml b/configs/rb_default_v2.yaml
new file mode 100644
index 0000000..7f99e79
--- /dev/null
+++ b/configs/rb_default_v2.yaml
@@ -0,0 +1,468 @@
+SCA:
+    adc:
+        mod_a00:
+            pin: 0x0B
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 0 and 1
+        mod_a01:
+            pin: 0x0C
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 0 and 1
+        mod_a02:
+            pin: 0x0E
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 0 and 1
+        mod_a03:
+            pin: 0x0F
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 0 and 1
+        mod_a04:
+            pin: 0x03
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 2 and 3
+        mod_a05:
+            pin: 0x01
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 2 and 3
+        mod_a06:
+            pin: 0x02
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 2 and 3
+        mod_a07:
+            pin: 0x0
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 2 and 3
+        mod_a08:
+            pin: 0x12
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 4 and 5
+        mod_a09:
+            pin: 0x15
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 4 and 5
+        mod_a10:
+            pin: 0x14
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 4 and 5
+        mod_a11:
+            pin: 0x11
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 4 and 5
+        mod_a12:
+            pin: 0x05
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 6 and 7
+        mod_a13:
+            pin: 0x06
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 6 and 7
+        mod_a14:
+            pin: 0x08
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 6 and 7
+        mod_a15:
+            pin: 0x09
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 6 and 7
+        mod_a16:
+            pin: 0x17
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 20 in ETROC 8 and 9
+        mod_a17:
+            pin: 0x16
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 18 in ETROC 8 and 9
+        mod_a18:
+            pin: 0x13
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 16 in ETROC 8 and 9
+        mod_a19:
+            pin: 0x10
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 14 in ETROC 8 and 9
+        mod_a20:
+            pin: 0x0D
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 21 in ETROC 10 and 11
+        mod_a21:
+            pin: 0x0A
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 23 in ETROC 10 and 11
+        mod_a22:
+            pin: 0x07
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 25 in ETROC 10 and 11
+        mod_a23:
+            pin: 0x04
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: pin 27 in ETROC 10 and 11
+        LV_RB:
+            pin: 0x1C
+            conv: 11.
+            flavor: small
+            comment: low voltage
+        VDAC:
+            pin: 0x1D
+            conv: 1.
+            flavor: small
+            comment: temperature sensor
+
+    gpio:
+        mod_d00:
+            pin: 0x1E
+            default: 1
+            direction: in
+            flavor: small
+            comment: pin 32 in ETROC 0 and 1
+        mod_d01:
+            pin: 0x1F
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 30 in ETROC 0 and 1
+        mod_d02:
+            pin: 0x1D
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 28 in ETROC 0 and 1
+        mod_d03:
+            pin: 0x1C
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 26 in ETROC 0 and 1
+        mod_d04:
+            pin: 0x00
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 9 in ETROC 2 and 3
+        mod_d05:
+            pin: 0x02
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 11 in ETROC 2 and 3
+        mod_d06:
+            pin: 0x01
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 13 in ETROC 2 and 3
+        mod_d07:
+            pin: 0x03
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 15 in ETROC 2 and 3
+        mod_d08:
+            pin: 0x16
+            default: 1
+            direction: in
+            flavor: small
+            comment: pin 32 in ETROC 4 and 5
+        mod_d09:
+            pin: 0x19
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 30 in ETROC 4 and 5
+        mod_d10:
+            pin: 0x13
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 28 in ETROC 4 and 5
+        mod_d11:
+            pin: 0x10
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 26 in ETROC 4 and 5
+        mod_d12:
+            pin: 0x0A
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 9 in ETROC 6 and 7
+        mod_d13:
+            pin: 0x04
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 11 in ETROC 6 and 7
+        mod_d14:
+            pin: 0x07
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 13 in ETROC 6 and 7
+        mod_d15:
+            pin: 0x0D
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 15 in ETROC 6 and 7
+        mod_d16:
+            pin: 0x0F
+            default: 1
+            direction: in
+            flavor: small
+            comment: pin 32 in ETROC 8 and 9
+        mod_d17:
+            pin: 0x0E
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 30 in ETROC 8 and 9
+        mod_d18:
+            pin: 0x0C
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 28 in ETROC 8 and 9
+        mod_d19:
+            pin: 0x0B
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 26 in ETROC 8 and 9
+        mod_d20:
+            pin: 0x09
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 9 in ETROC 10 and 11
+        mod_d21:
+            pin: 0x08
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 11 in ETROC 10 and 11
+        mod_d22:
+            pin: 0x06
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 13 in ETROC 10 and 11
+        mod_d23:
+            pin: 0x05
+            default: 0
+            direction: in
+            flavor: small
+            comment: pin 15 in ETROC 10 and 11
+        sca_led:
+            pin: 0x1B
+            default: 1
+            direction: out
+            flavor: small
+            comment: SCA LED indicator
+
+LPGBT:
+    adc:
+        TH1:
+            pin: 0x00
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: VTRX TH1
+        1V4D_ADC:
+            pin: 0x01
+            conv: 1.82
+            min: 1.12
+            max: 1.68
+            flavor: small
+            comment: 1V4D * 0.55
+        1V5A_ADC:
+            pin: 0x02
+            conv: 1.82
+            min: 1.2
+            max: 1.8
+            flavor: small
+            comment: 1V5D * 0.55
+        2V5TX_ADC:
+            pin: 0x03
+            conv: 3.0
+            min: 2.0
+            max: 3.0
+            flavor: small
+            comment: 2V5TX * 0.33
+        RSSI_ADC:
+            pin: 0x04
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: RSSI
+        ADC5:
+            pin: 0x05
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: N/A
+        2V5RX_ADC:
+            pin: 0x06
+            conv: 3.0
+            min: 2.0
+            max: 3.0
+            flavor: small
+            comment: 2V5RX * 0.33
+        VTEMP_ADC:
+            pin: 0x07
+            conv: 1
+            min: 0
+            max: 1.0
+            flavor: small
+            comment: RT1
+        EOM_ADC:
+            pin: 0x08
+            conv: 1
+            flavor: small
+            comment: EOM DAC (internal signal)
+        VDDIO:
+            pin: 0x09
+            conv: 2.38
+            flavor: small
+            comment: VDDIO * 0.42 (internal signal)
+        VDDTX:
+            pin: 0x0a
+            conv: 2.38
+            flavor: small
+            comment: VDDTX * 0.42 (internal signal)
+        VDDRX:
+            pin: 0x0b
+            conv: 2.38
+            flavor: small
+            comment: VDDRX * 0.42 (internal signal)
+        VDD:
+            pin: 0x0c
+            conv: 2.38
+            flavor: small
+            comment: VDD * 0.42 (internal signal)
+        VDDA:
+            pin: 0x0d
+            conv: 2.38
+            flavor: small
+            comment: VDDA * 0.42 (internal signal)
+        TEMP:
+            pin: 0x0e
+            conv: 1
+            flavor: small
+            comment: Temperature sensor (internal signal)
+        VREF:
+            pin: 0x0f
+            conv: 2.0
+            flavor: small
+            comment: VREF/2 (internal signal)
+    gpio:
+        SCA_RESETB:
+            pin: 0x00
+            default: 1
+            direction: out
+            flavor: small
+            comment: GBT SCA reset
+        LED_0:
+            pin: 0x01
+            default: 1
+            direction: out
+            flavor: small
+            comment: LPGBT GPIO configuration LED
+        LED_1:
+            pin: 0x02
+            default: 0
+            direction: out
+            flavor: small
+            comment: tamalero LED
+        LED_RHETT:
+            pin: 0x03
+            default: 1
+            direction: out
+            flavor: small
+            comment: success LED
+        LD_RSTN:
+            pin: 0x0A
+            default: 1
+            direction: out
+            flavor: small
+            comment: VTRX reset
+        LD_DIS:
+            pin: 0x0D
+            default: 0
+            direction: out
+            flavor: small
+            comment: VTRX DIS
diff --git a/tamalero/LPGBT.py b/tamalero/LPGBT.py
index 6f98b0e..7152a24 100644
--- a/tamalero/LPGBT.py
+++ b/tamalero/LPGBT.py
@@ -8,7 +8,7 @@ import json
 from functools import wraps
 import tamalero.colors as colors
 from tamalero.colors import red, green
-from tamalero.utils import read_mapping, chunk, load_yaml
+from tamalero.utils import read_mapping, chunk, load_yaml, get_config, majority_vote
 from time import sleep
 from datetime import datetime
 try:
@@ -35,7 +35,7 @@ def gpio_byname(gpio_func):
 
 class LPGBT(RegParser):
 
-    def __init__(self, rb=0, trigger=False, flavor='small', master=None, kcu=None, do_adc_calibration=False):
+    def __init__(self, rb=0, trigger=False, flavor='small', master=None, kcu=None, do_adc_calibration=False, config='default'):
         '''
         Initialize lpGBT for a certain readout board number (rb).
         The trigger lpGBT is accessed through I2C of the master (= DAQ lpGBT).
@@ -54,6 +54,7 @@ class LPGBT(RegParser):
         if kcu != None:
             self.kcu = kcu
 
+        self.config = config
         self.configure(do_adc_calibration=do_adc_calibration)
 
     def configure(self, do_adc_calibration=True):
@@ -168,22 +169,25 @@ class LPGBT(RegParser):
 
     def set_adc_mapping(self):
         assert self.ver in [0, 1], f"Unrecognized version {self.ver}"
-        if self.ver == 0:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'adc')
-        elif self.ver == 1:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'adc')
+        self.adc_mapping = get_config(self.config, version=f'v{self.ver+1}')['LPGBT']['adc']
+        #if self.ver == 0:
+        #    self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'adc')
+        #elif self.ver == 1:
+        #    self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'adc')
 
     def set_gpio_mapping(self):
         assert self.ver in [0, 1], f"Unrecognized version {self.ver}"
-        if self.ver == 0:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'gpio')
-        elif self.ver == 1:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'gpio')
+        self.gpio_mapping = get_config(self.config, version=f'v{self.ver+1}')['LPGBT']['gpio']
+        #if self.ver == 0:
+        #    self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'gpio')
+        #elif self.ver == 1:
+        #    self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping_v2.yaml'), 'gpio')
 
     def update_ver(self, new_ver):
-        assert new_ver in [1, 2], f"Unrecognized version {new_ver}"
+        assert new_ver in [0, 1], f"Unrecognized version {new_ver}"
         self.ver = new_ver
         self.set_adc_mapping()
+        self.set_gpio_mapping()
 
     def link_status(self, verbose=False):
         if self.trigger:
@@ -314,32 +318,36 @@ class LPGBT(RegParser):
 
     def wr_adr(self, adr, data):
         if self.trigger:
-            raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to write to a servant")
-        #defer = not self.kcu.auto_dispatch  # if auto dispatch is turned off, keep it off.
-        #self.kcu.toggle_dispatch()  # turn off auto dispatch for this transaction
-        #self.kcu.write_node("READOUT_BOARD_%d.SC.TX_GBTX_ADDR" % self.rb, 115)
-        self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
-        self.kcu.write_node("READOUT_BOARD_%d.SC.TX_DATA_TO_GBTX" % self.rb, data)
-        self.kcu.action("READOUT_BOARD_%d.SC.TX_WR" % self.rb)
-        self.kcu.action("READOUT_BOARD_%d.SC.TX_START_WRITE" % self.rb)
-        #return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
-        #if not defer:  # turn auto dispatch back on only if it wasn't set to false before
-        #    self.kcu.dispatch()
-        #self.rd_flush()
+            return self.master.I2C_write(adr, data)
+            #raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to write to a servant")
+        else:
+            #defer = not self.kcu.auto_dispatch  # if auto dispatch is turned off, keep it off.
+            #self.kcu.toggle_dispatch()  # turn off auto dispatch for this transaction
+            #self.kcu.write_node("READOUT_BOARD_%d.SC.TX_GBTX_ADDR" % self.rb, 115)
+            self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
+            self.kcu.write_node("READOUT_BOARD_%d.SC.TX_DATA_TO_GBTX" % self.rb, data)
+            self.kcu.action("READOUT_BOARD_%d.SC.TX_WR" % self.rb)
+            self.kcu.action("READOUT_BOARD_%d.SC.TX_START_WRITE" % self.rb)
+            #return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
+            #if not defer:  # turn auto dispatch back on only if it wasn't set to false before
+            #    self.kcu.dispatch()
+            #self.rd_flush()
 
     def rd_adr(self, adr):
         if self.trigger:
-            raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to read from a servant")
-        self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
-        self.kcu.action("READOUT_BOARD_%d.SC.TX_START_READ" % self.rb)
-        valid = self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_VALID" % self.rb).valid()
-        if valid:
-            # this only means that the KCU successfully read data
-            # not necessarily does it mean there's communication with the lpGBT
-            return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
-
-        print("LpGBT read failed!")
-        return None
+            return self.master.I2C_read(adr)
+            #raise NotImplementedError("rd_adr does only read from the master lpGBT, and you're trying to read from a servant")
+        else:
+            self.kcu.write_node("READOUT_BOARD_%d.SC.TX_REGISTER_ADDR" % self.rb, adr)
+            self.kcu.action("READOUT_BOARD_%d.SC.TX_START_READ" % self.rb)
+            valid = self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_VALID" % self.rb).valid()
+            if valid:
+                # this only means that the KCU successfully read data
+                # not necessarily does it mean there's communication with the lpGBT
+                return self.kcu.read_node("READOUT_BOARD_%d.SC.RX_DATA_FROM_GBTX" % self.rb)
+
+            print("LpGBT read failed!")
+            return None
 
     def wr_reg(self, id, data):
         node = self.get_node(id)
@@ -752,9 +760,9 @@ class LPGBT(RegParser):
             return serial != 0
 
         if (self.ver==0):
-            serial = self.get_chip_userid()
+            serial = str(self.get_chip_userid())
         else:
-            serial = self.get_chip_serial()
+            serial = str(self.get_chip_serial())
 
         cal_file = "lpgbt_adc_calibrations.json"
 
@@ -769,7 +777,7 @@ class LPGBT(RegParser):
         if serial_valid(serial) and serial in cal_data and not recalibrate:
             gain = cal_data[serial]['gain']
             offset = cal_data[serial]['offset']
-            print("Loaded ADC calibration data for chip %d. Gain: %f / Offset: %d" % (serial, gain, offset))
+            print("Loaded ADC calibration data for chip %s. Gain: %f / Offset: %d" % (serial, gain, offset))
 
         # else, determine calibration constants
         else:
@@ -788,6 +796,7 @@ class LPGBT(RegParser):
             self.wr_reg("LPGBT.RW.ADC.VDDMONENA", initial_val)
             type = "Trigger" if self.trigger else "DAQ"
             print("Calibrated %s ADC. Gain: %f / Offset: %d" % (type, gain, offset))
+            print("Chip %s"%serial)
 
             if gain < 1.65 or gain > 2 or offset < 490 or offset > 530:
                 raise RuntimeError("ADC Calibration Failed!")
@@ -1429,10 +1438,36 @@ class LPGBT(RegParser):
                self.rd_reg("LPGBT.RWF.CHIPID.USERID0")
 
     def get_chip_serial(self):
-        return self.rd_reg("LPGBT.RWF.CHIPID.CHIPID3") << 24 |\
-               self.rd_reg("LPGBT.RWF.CHIPID.CHIPID2") << 16 |\
-               self.rd_reg("LPGBT.RWF.CHIPID.CHIPID1") << 8 |\
-               self.rd_reg("LPGBT.RWF.CHIPID.CHIPID0")
+        if self.ver == 1:
+            # NOTE we have to read from the fuses directly.
+            # ideally this can still be verified (May 2023)
+            self.wr_adr(0x119, 0x1 << 1)  # write FuseRead https://lpgbt.web.cern.ch/lpgbt/v1/registermap.html#reg-fusecontrol
+            while True:
+                # wait for FuseDataValid https://lpgbt.web.cern.ch/lpgbt/v1/registermap.html#reg-fusestatus
+                if self.rd_adr(0x1b1) >> 2 == 1: break
+
+            chipids = []
+            # there should be 5 copies of the chipid, but I can only find 4
+            # there's nothing else in the fuses that's non-zero
+            for i in range(4):
+                self.wr_adr(0x11f, i)
+                chipids.append(self.rd_adr(0x1b2) << 24 | self.rd_adr(0x1b3) << 16 | self.rd_adr(0x1b4) << 8 | self.rd_adr(0x1b5) << 24)
+
+            self.wr_adr(0x119, 0)  # write FuseRead https://lpgbt.web.cern.ch/lpgbt/v1/registermap.html#reg-fusecontrol
+
+            if all([c==chipids[0] for c in chipids]):
+                return chipids[0]
+            else:
+                print("CHIPD serial needs majority vote")
+                return majority_vote(chipids, majority=3)
+
+        elif self.ver == 0:
+            # NOTE: this is what's supposed to work for lpGBT v0
+            # but note sure if that's actually true
+            return self.rd_reg("LPGBT.RWF.CHIPID.CHIPID3") << 24 |\
+                self.rd_reg("LPGBT.RWF.CHIPID.CHIPID2") << 16 |\
+                self.rd_reg("LPGBT.RWF.CHIPID.CHIPID1") << 8 |\
+                self.rd_reg("LPGBT.RWF.CHIPID.CHIPID0")
 
     def get_power_up_state_machine(self, quiet=True):
 
diff --git a/tamalero/ReadoutBoard.py b/tamalero/ReadoutBoard.py
index 92dd53c..ff35243 100644
--- a/tamalero/ReadoutBoard.py
+++ b/tamalero/ReadoutBoard.py
@@ -8,22 +8,23 @@ from time import sleep
 
 class ReadoutBoard:
 
-    def __init__(self, rb=0, trigger=True, flavor='small', kcu=None):
+    def __init__(self, rb=0, trigger=True, flavor='small', kcu=None, config='default'):
         '''
         create a readout board.
         trigger: if true, also configure a trigger lpGBT
         '''
         self.rb = rb
         self.flavor = flavor
-        self.ver = 1
+        self.ver = 2
+        self.config = config
 
         self.trigger = trigger
-        self.DAQ_LPGBT = LPGBT(rb=rb, flavor=flavor, kcu=kcu)
+        self.DAQ_LPGBT = LPGBT(rb=rb, flavor=flavor, kcu=kcu, config=self.config)
         self.VTRX = VTRX(self.DAQ_LPGBT)
         # This is not yet recommended:
         #for adr in [0x06, 0x0A, 0x0E, 0x12]:
         #    self.VTRX.wr_adr(adr, 0x20)
-        self.SCA = SCA(rb=rb, flavor=flavor, ver=self.DAQ_LPGBT.ver)
+        self.SCA = SCA(rb=rb, flavor=flavor, ver=self.DAQ_LPGBT.ver, config=self.config)
 
         if kcu != None:
             self.kcu = kcu
@@ -32,7 +33,11 @@ class ReadoutBoard:
             if self.DAQ_LPGBT.ver == 1:
                 self.ver = 2
                 self.SCA.update_ver(self.ver)
-                self.DAQ_LPGBT.set_adc_mapping()
+                self.DAQ_LPGBT.update_ver(self.ver-1)  #  FIXME we need to disentangle lpGBT version from RB version
+            elif self.DAQ_LPGBT.ver == 0:
+                self.ver = 1
+                self.SCA.update_ver(self.ver)
+                self.DAQ_LPGBT.update_ver(self.ver-1)  # FIXME we need to disentangle lpGBT version from RB version
             self.SCA.connect_KCU(kcu)
 
     def get_trigger(self):
@@ -52,7 +57,7 @@ class ReadoutBoard:
             print ("Trigger lpGBT was found, but will not be added.")
 
         if self.trigger:
-            self.TRIG_LPGBT = LPGBT(rb=self.rb, flavor=self.flavor, trigger=True, master=self.DAQ_LPGBT, kcu=self.kcu)
+            self.TRIG_LPGBT = LPGBT(rb=self.rb, flavor=self.flavor, trigger=True, master=self.DAQ_LPGBT, kcu=self.kcu, config=self.config)
 
 
     def connect_KCU(self, kcu):
@@ -291,6 +296,8 @@ class ReadoutBoard:
         self.SCA.reset()
         self.SCA.connect()
         try:
+            print("version in SCA", self.SCA.ver)
+            print("config in SCA", self.SCA.config)
             self.SCA.config_gpios()  # this sets the directions etc according to the mapping
         except TimeoutError:
             print ("SCA config failed. Will continue without SCA.")
diff --git a/tamalero/SCA.py b/tamalero/SCA.py
index f744058..2809d9c 100644
--- a/tamalero/SCA.py
+++ b/tamalero/SCA.py
@@ -1,6 +1,7 @@
 import os
 import random
-from tamalero.utils import read_mapping
+from tamalero.utils import read_mapping, get_config
+from functools import wraps
 import time
 try:
     from tabulate import tabulate
@@ -110,34 +111,43 @@ class SCA_I2C:
     I2C_R_DATA3 = 0x71 # read from data register 3
     I2C_RW_DATA_OFFSET = 16 # offset to access data register 1, 2, 3
 
+def gpio_byname(gpio_func):
+    @wraps(gpio_func)
+    def wrapper(lpgbt, pin, direction=1):
+        if isinstance(pin, str):
+            gpio_dict = lpgbt.gpio_mapping
+            pin = gpio_dict[pin]['pin']
+            return gpio_func(lpgbt, pin, direction)
+        elif isinstance(pin, int):
+            return gpio_func(lpgbt, pin, direction)
+        else:
+            invalid_type = type(pin)
+            raise TypeError(f"{gpio_func.__name__} can only take positional arguments of type int or str, but argument of type {invalid_type} was given.")
+
+    return wrapper
 
 class SCA:
 
-    def __init__(self, rb=0, flavor='small', ver=0):
+    def __init__(self, rb=0, flavor='small', ver=0, config='default'):
         self.rb = rb
         self.flavor = flavor
         self.err_count = 0
         self.ver = ver + 1  # NOTE don't particularly like this, but we're giving it the lpGBT version
+        self.config = config
+        self.locked = False
         self.set_adc_mapping()
         self.set_gpio_mapping()
-        self.locked = False
 
     def connect_KCU(self, kcu):
         self.kcu = kcu
 
     def set_adc_mapping(self):
         assert self.ver in [1, 2], f"Unrecognized version {self.ver}"
-        if self.ver == 1:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping.yaml'), 'adc')
-        elif self.ver == 2:
-            self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping_v2.yaml'), 'adc')
+        self.adc_mapping = get_config(self.config, version=f'v{self.ver}')['SCA']['adc']
 
     def set_gpio_mapping(self):
         assert self.ver in [1, 2], f"Unrecognized version {self.ver}"
-        if self.ver == 1:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping.yaml'), 'gpio')
-        elif self.ver == 2:
-            self.gpio_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/SCA_mapping_v2.yaml'), 'gpio')
+        self.gpio_mapping = get_config(self.config, version=f'v{self.ver}')['SCA']['gpio']
 
     def update_ver(self, new_ver):
         assert new_ver in [1, 2], f"Unrecognized version {new_ver}"
@@ -447,6 +457,7 @@ class SCA:
         val = self.rw_reg(SCA_GPIO.GPIO_R_DATAIN).value()
         return int((val >> line) & 1)
 
+    @gpio_byname
     def set_gpio(self, line, to=1):
         self.enable_gpio()  # enable GPIO
         currently_set = self.rw_reg(SCA_GPIO.GPIO_R_DATAOUT).value()
@@ -458,6 +469,7 @@ class SCA:
         self.rw_reg(SCA_GPIO.GPIO_W_DATAOUT, currently_set)
         return self.read_gpio(line)  # in order to check it is actually set
 
+    @gpio_byname
     def set_gpio_direction(self, line, to=1):
         self.enable_gpio()  # enable GPIO
         currently_set = self.rw_reg(SCA_GPIO.GPIO_R_DIRECTION).value()
@@ -491,8 +503,9 @@ class SCA:
             default     = gpio_dict[gpio_reg]['default']
             if verbose:
                 print("Setting SCA GPIO pin %s (%s) to %s"%(pin, comment, gpio_dict[gpio_reg]['direction']))
+            self.set_gpio(pin, default)  # NOTE this is important because otherwise the GPIO pin can be set to a false default value when switched to output
             self.set_gpio_direction(pin, direction)
-            self.set_gpio(pin, default)
+            self.set_gpio(pin, default)  # redundant but keep it
 
     def get_I2C_channel(self, channel):
         channel_str = hex(channel).upper()[-1]
diff --git a/test_modulev0.py b/test_modulev0.py
new file mode 100644
index 0000000..5a4692e
--- /dev/null
+++ b/test_modulev0.py
@@ -0,0 +1,162 @@
+from tamalero.KCU import KCU
+from tamalero.ReadoutBoard import ReadoutBoard
+from tamalero.utils import header, make_version_header, get_kcu, check_repo_status
+from tamalero.FIFO import FIFO
+from tamalero.DataFrame import DataFrame
+from tamalero.ETROC import ETROC
+from tamalero.Module import Module
+
+from tamalero.SCA import SCA_CONTROL
+
+import time
+import random
+import sys
+import os
+import uhal
+from emoji import emojize
+
+if __name__ == '__main__':
+
+
+    import argparse
+
+    argParser = argparse.ArgumentParser(description = "Argument parser")
+    argParser.add_argument('--verbose', action='store_true', default=False, help="Verbose power up sequence")
+    argParser.add_argument('--power_up', action='store_true', default=False, help="Do lpGBT power up init?")
+    argParser.add_argument('--reconfigure', action='store_true', default=False, help="Configure the RB electronics: SCA and lpGBT?")
+    argParser.add_argument('--adcs', action='store_true', default=False, help="Read ADCs?")
+    argParser.add_argument('--i2c_temp', action='store_true', default=False, help="Do temp monitoring on I2C from lpGBT?")
+    argParser.add_argument('--i2c_sca', action='store_true', default=False, help="I2C tests on SCA?")
+    argParser.add_argument('--kcu', action='store', default="192.168.0.10", help="Specify the IP address for KCU")
+    argParser.add_argument('--control_hub', action='store_true', default=False, help="Use control hub for communication?")
+    argParser.add_argument('--host', action='store', default='localhost', help="Specify host for control hub")
+    argParser.add_argument('--configuration', action='store', default='modulev0', choices=['default', 'emulator', 'modulev0'], help="Specify a configuration of the RB, e.g. emulator or modulev0")
+    argParser.add_argument('--devel', action='store_true', default=False, help="Don't check repo status (not recommended)")
+    argParser.add_argument('--connection_test', action='store_true', default=False, help="Check the PCB connections.")
+    args = argParser.parse_args()
+
+
+    verbose = args.verbose
+
+    #-------------------------------------------------------------------------------
+    # Try to Connect to the KCU105
+    #-------------------------------------------------------------------------------
+
+    print ("Using KCU at address: %s"%args.kcu)
+
+    kcu = None
+    rb_0 = 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 0
+        # this would cause the RB init to fail.
+        sys.exit(1)
+
+
+    rb_0 = ReadoutBoard(0, 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()
+    if not is_configured:
+        print("RB is not configured, exiting.")
+        exit(0)
+    header(configured=is_configured)
+
+    if not args.devel:
+        check_repo_status(kcu_version=kcu.get_firmware_version(verbose=True))
+
+    rb_0.VTRX.get_version()
+
+    if not hasattr(rb_0, "TRIG_LPGBT"):
+        rb_0.get_trigger()
+
+    res = rb_0.DAQ_LPGBT.get_board_id()
+    res['trigger'] = 'yes' if rb_0.trigger else 'no'
+
+    if (verbose):
+        make_version_header(res)
+
+    if args.adcs:
+        print("\n\nReading GBT-SCA ADC values:")
+        rb_0.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)
+
+        # High level reading of temperatures
+        temp = rb_0.read_temp(verbose=True)
+
+    #-------------------------------------------------------------------------------
+    # Read SCA
+    #-------------------------------------------------------------------------------
+
+    if args.i2c_sca:
+
+        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)
+            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)
+        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)
+
+        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)))
+
+
+    #-------------------------------------------------------------------------------
+    # Success LEDs
+    #-------------------------------------------------------------------------------
+    rb_0.DAQ_LPGBT.set_gpio("LED_1", 1) # Set LED1 after tamalero finishes succesfully
+    rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1) # Set LED1 after tamalero finishes succesfully
+    if rb_0.DAQ_LPGBT.ver == 1 and args.connection_test:
+        print("Toggling FEAST and 2.5V on/off for 1min")
+        t_end = time.time() + 60
+        while time.time() < t_end:
+            rb_0.DAQ_LPGBT.set_gpio("LED_1", 1) # Let Rhett LED blink for 10s
+            rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1) # Let Rhett LED blink for 10s
+            rb_0.SCA.set_gpio("mod_d00", 1)
+            rb_0.SCA.set_gpio("mod_d01", 1)
+            rb_0.SCA.set_gpio("mod_d08", 1)
+            rb_0.SCA.set_gpio("mod_d09", 1)
+            rb_0.SCA.set_gpio("mod_d16", 1)
+            rb_0.SCA.set_gpio("mod_d17", 1)
+            time.sleep(2.0)
+            rb_0.DAQ_LPGBT.set_gpio("LED_1", 0)
+            rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 0)
+            rb_0.SCA.set_gpio("mod_d00", 0)
+            rb_0.SCA.set_gpio("mod_d01", 0)
+            rb_0.SCA.set_gpio("mod_d08", 0)
+            rb_0.SCA.set_gpio("mod_d09", 0)
+            rb_0.SCA.set_gpio("mod_d16", 0)
+            rb_0.SCA.set_gpio("mod_d17", 0)
+            time.sleep(2.0)
+        rb_0.DAQ_LPGBT.set_gpio("LED_1", 1)
+        rb_0.DAQ_LPGBT.set_gpio("LED_RHETT", 1)
+
+        # disabling 2.5V
+        rb_0.SCA.set_gpio("mod_d00", 1)
+        rb_0.SCA.set_gpio("mod_d08", 1)
+        rb_0.SCA.set_gpio("mod_d16", 1)
+
+        # enabling FEAST
+        rb_0.SCA.set_gpio("mod_d01", 1)
+        rb_0.SCA.set_gpio("mod_d09", 1)
+        rb_0.SCA.set_gpio("mod_d17", 1)
diff --git a/test_tamalero.py b/test_tamalero.py
index a1adf0f..57d941c 100644
--- a/test_tamalero.py
+++ b/test_tamalero.py
@@ -39,6 +39,7 @@ if __name__ == '__main__':
     argParser.add_argument('--recal_lpgbt', action='store_true', default=False, help="Recalibrate ADC in LPGBT? (instead of using saved values)")
     argParser.add_argument('--control_hub', action='store_true', default=False, help="Use control hub for communication?")
     argParser.add_argument('--host', action='store', default='localhost', help="Specify host for control hub")
+    argParser.add_argument('--configuration', action='store', default='default', choices=['default', 'emulator', 'modulev0'], help="Specify a configuration of the RB, e.g. emulator or modulev0")
     argParser.add_argument('--devel', action='store_true', default=False, help="Don't check repo status (not recommended)")
     argParser.add_argument('--monitor', action='store_true', default=False, help="Start up montoring threads in the background")
     argParser.add_argument('--strict', action='store_true', default=False, help="Enforce strict limits on ADC reads for SCA and LPGBT")
@@ -63,7 +64,9 @@ if __name__ == '__main__':
         # if not basic connection was established the get_kcu function returns 0
         # this would cause the RB init to fail.
         sys.exit(1)
-    rb_0 = ReadoutBoard(0, trigger=(not args.force_no_trigger), kcu=kcu)
+
+
+    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")):
@@ -159,7 +162,7 @@ if __name__ == '__main__':
     # Module Status
     #-------------------------------------------------------------------------------
 
-    if args.verbose:
+    if args.verbose and args.configuration == 'emulator':
         print("Configuring ETROCs")
         modules = []
         for i in range(res['n_module']):
-- 
GitLab


From 9d6140491083038cd4a260e0945976ff495171ce Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Mon, 15 May 2023 17:32:42 -0400
Subject: [PATCH 09/11] trying to get rid of all the configs

---
 configs/current_adcs.yaml  |  5 -----
 configs/emulator_v2.yaml   | 33 +++++++++++++++++++++++++++++++++
 configs/modulev0_v2.yaml   | 33 +++++++++++++++++++++++++++++++++
 configs/rb_default_v2.yaml | 22 ++++++++++++++++++++++
 tamalero/LPGBT.py          | 36 ++++++++++++++++++++----------------
 tamalero/Module.py         | 10 +++++-----
 tamalero/ReadoutBoard.py   |  2 --
 tamalero/utils.py          |  2 ++
 8 files changed, 115 insertions(+), 28 deletions(-)
 delete mode 100644 configs/current_adcs.yaml

diff --git a/configs/current_adcs.yaml b/configs/current_adcs.yaml
deleted file mode 100644
index 239b5eb..0000000
--- a/configs/current_adcs.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-# FIXME: these should be moved to the readout board yaml
-# this file doesn't really make sense
-lpGBT:
-   - 0
-   - 7
diff --git a/configs/emulator_v2.yaml b/configs/emulator_v2.yaml
index 6321b87..3694d87 100644
--- a/configs/emulator_v2.yaml
+++ b/configs/emulator_v2.yaml
@@ -23,3 +23,36 @@ SCA:
 LPGBT:
     adc:
     gpio:
+
+inversions:
+    clocks:
+      - 3
+      - 4
+      - 5
+      - 22
+      - 23
+      - 25
+      - 26
+      - 27
+    downlink:
+      - 2
+      - 4
+      - 8
+      - 10
+      - 12
+    uplink:
+      - 6
+      - 12
+      - 14
+      - 16
+      - 18
+      - 20
+      - 22
+    trigger:
+      - 2
+      - 6
+      - 10
+      - 16
+      - 18
+      - 20
+      - 22
diff --git a/configs/modulev0_v2.yaml b/configs/modulev0_v2.yaml
index 65c2179..346d56f 100644
--- a/configs/modulev0_v2.yaml
+++ b/configs/modulev0_v2.yaml
@@ -209,3 +209,36 @@ SCA:
 LPGBT:
     adc:
     gpio:
+
+inversions:
+   clocks:
+      - 3
+      - 4
+      - 5
+      - 22
+      - 23
+      - 25
+      - 26
+      - 27
+   downlink:
+      - 2
+      - 4
+      - 8
+      - 10
+      - 12
+   uplink:
+      - 6
+      - 12
+      - 14
+      - 16
+      - 18
+      - 20
+      - 22
+   trigger:
+      - 2
+      - 6
+      - 10
+      - 16
+      - 18
+      - 20
+      - 22
diff --git a/configs/rb_default_v2.yaml b/configs/rb_default_v2.yaml
index 7f99e79..1279acc 100644
--- a/configs/rb_default_v2.yaml
+++ b/configs/rb_default_v2.yaml
@@ -335,6 +335,7 @@ LPGBT:
     adc:
         TH1:
             pin: 0x00
+            current: 1
             conv: 1
             min: 0
             max: 1.0
@@ -342,6 +343,7 @@ LPGBT:
             comment: VTRX TH1
         1V4D_ADC:
             pin: 0x01
+            current: 0
             conv: 1.82
             min: 1.12
             max: 1.68
@@ -349,6 +351,7 @@ LPGBT:
             comment: 1V4D * 0.55
         1V5A_ADC:
             pin: 0x02
+            current: 0
             conv: 1.82
             min: 1.2
             max: 1.8
@@ -356,6 +359,7 @@ LPGBT:
             comment: 1V5D * 0.55
         2V5TX_ADC:
             pin: 0x03
+            current: 0
             conv: 3.0
             min: 2.0
             max: 3.0
@@ -363,6 +367,7 @@ LPGBT:
             comment: 2V5TX * 0.33
         RSSI_ADC:
             pin: 0x04
+            current: 0
             conv: 1
             min: 0
             max: 1.0
@@ -370,6 +375,7 @@ LPGBT:
             comment: RSSI
         ADC5:
             pin: 0x05
+            current: 0
             conv: 1
             min: 0
             max: 1.0
@@ -377,6 +383,7 @@ LPGBT:
             comment: N/A
         2V5RX_ADC:
             pin: 0x06
+            current: 0
             conv: 3.0
             min: 2.0
             max: 3.0
@@ -384,6 +391,7 @@ LPGBT:
             comment: 2V5RX * 0.33
         VTEMP_ADC:
             pin: 0x07
+            current: 1
             conv: 1
             min: 0
             max: 1.0
@@ -391,41 +399,49 @@ LPGBT:
             comment: RT1
         EOM_ADC:
             pin: 0x08
+            current: 0
             conv: 1
             flavor: small
             comment: EOM DAC (internal signal)
         VDDIO:
             pin: 0x09
+            current: 0
             conv: 2.38
             flavor: small
             comment: VDDIO * 0.42 (internal signal)
         VDDTX:
             pin: 0x0a
+            current: 0
             conv: 2.38
             flavor: small
             comment: VDDTX * 0.42 (internal signal)
         VDDRX:
             pin: 0x0b
+            current: 0
             conv: 2.38
             flavor: small
             comment: VDDRX * 0.42 (internal signal)
         VDD:
             pin: 0x0c
+            current: 0
             conv: 2.38
             flavor: small
             comment: VDD * 0.42 (internal signal)
         VDDA:
             pin: 0x0d
+            current: 0
             conv: 2.38
             flavor: small
             comment: VDDA * 0.42 (internal signal)
         TEMP:
             pin: 0x0e
+            current: 0
             conv: 1
             flavor: small
             comment: Temperature sensor (internal signal)
         VREF:
             pin: 0x0f
+            current: 0
             conv: 2.0
             flavor: small
             comment: VREF/2 (internal signal)
@@ -466,3 +482,9 @@ LPGBT:
             direction: out
             flavor: small
             comment: VTRX DIS
+
+inversions:
+   clocks:
+   downlink:
+   uplink:
+   trigger:
diff --git a/tamalero/LPGBT.py b/tamalero/LPGBT.py
index 7152a24..be2c147 100644
--- a/tamalero/LPGBT.py
+++ b/tamalero/LPGBT.py
@@ -116,9 +116,6 @@ class LPGBT(RegParser):
                 print (" > unsure about lpGBT version. This case should have been impossible to reach.")
                 raise Exception("Spurious lpGBT version.")
 
-        self.set_adc_mapping()
-        self.set_gpio_mapping()
-
         self.base_config = load_yaml(os.path.expandvars('$TAMALERO_BASE/configs/lpgbt_config.yaml'))['base'][f'v{self.ver}']
         self.ec_config = load_yaml(os.path.expandvars('$TAMALERO_BASE/configs/lpgbt_config.yaml'))['ec'][f'v{self.ver}']
 
@@ -132,10 +129,13 @@ class LPGBT(RegParser):
             self.wr_reg("LPGBT.RWF.POWERUP.DLLCONFIGDONE", 0x1)  # NOTE untested change
             self.wr_reg("LPGBT.RWF.POWERUP.PLLCONFIGDONE", 0x1)
 
+        self.set_adc_mapping()
+        self.set_gpio_mapping()
+
         # Get LPGBT Serial Num
         self.serial_num = 0# self.get_board_id()['lpgbt_serial']
 
-        self.link_inversions = load_yaml(os.path.expandvars('$TAMALERO_BASE/configs/link_inversions.yaml'))
+        self.link_inversions = get_config(self.config, version=f'v{self.ver+1}')['inversions']
 
         if not self.power_up_done():
             print("Running power up within LPGBT.configure()")
@@ -147,9 +147,9 @@ class LPGBT(RegParser):
         if do_adc_calibration and not self.calibrated:
             self.calibrate_adc()
 
-        self.current_adcs = load_yaml(os.path.expandvars('$TAMALERO_BASE/configs/current_adcs.yaml'))['lpGBT']
-        for adc in self.current_adcs:
-            self.set_current_adc(adc)
+        #self.current_adcs = load_yaml(os.path.expandvars('$TAMALERO_BASE/configs/current_adcs.yaml'))['lpGBT']
+        #for adc in self.current_adcs:
+        #    self.set_current_adc(adc)
 
     def read_base_config(self):
         #
@@ -170,6 +170,10 @@ class LPGBT(RegParser):
     def set_adc_mapping(self):
         assert self.ver in [0, 1], f"Unrecognized version {self.ver}"
         self.adc_mapping = get_config(self.config, version=f'v{self.ver+1}')['LPGBT']['adc']
+        for channel in self.adc_mapping:
+            if self.adc_mapping[channel]['current'] == 1:
+                print(f'Setting {channel} to current DAC')
+                self.set_current_dac(self.adc_mapping[channel]['pin'])
         #if self.ver == 0:
         #    self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'adc')
         #elif self.ver == 1:
@@ -485,38 +489,38 @@ class LPGBT(RegParser):
 
     def invert_links(self, trigger=False):
         if trigger:
-            for link in self.link_inversions['emulator_adapter']['trigger']:
+            for link in self.link_inversions['trigger']:
                 self.set_uplink_invert(link)
         else:
-            for link in self.link_inversions['emulator_adapter']['clocks']:
+            for link in self.link_inversions['clocks']:
                 self.set_clock_invert(link)
-            for link in self.link_inversions['emulator_adapter']['downlink']:
+            for link in self.link_inversions['downlink']:
                 self.set_downlink_invert(link)
-            for link in self.link_inversions['emulator_adapter']['uplink']:
+            for link in self.link_inversions['uplink']:
                 self.set_uplink_invert(link)
 
     def read_inversions(self):
         if self.trigger:
             print("Trigger LPGBT Registers -- Uplinks")
-            for link in self.link_inversions['emulator_adapter']['trigger']:
+            for link in self.link_inversions['trigger']:
                 register = "LPGBT.RWF.EPORTRX.EPRX_CHN_CONTROL.EPRX%dINVERT" % link
                 val = self.rd_reg(register)
                 print(register, "\t", val)
         else:
             print("DAQ LPGBT Registers -- Clocks")
-            for link in self.link_inversions['emulator_adapter']['clocks']:
+            for link in self.link_inversions['clocks']:
                 register = "LPGBT.RWF.EPORTCLK.EPCLK%dINVERT" % link
                 val = self.rd_reg(register)
                 print(register, "\t", val)
             print("DAQ LPGBT Registers -- Downlinks")
-            for link in self.link_inversions['emulator_adapter']['downlink']:
+            for link in self.link_inversions['downlink']:
                 group = link // 4
                 elink = link % 4
                 register = "LPGBT.RWF.EPORTTX.EPTX%d%dINVERT" % (group, elink)
                 val = self.rd_reg(register)
                 print(register, "\t", val)
             print("DAQ LPGBT Registers -- Uplinks")
-            for link in self.link_inversions['emulator_adapter']['uplink']:
+            for link in self.link_inversions['uplink']:
                 register = "LPGBT.RWF.EPORTRX.EPRX_CHN_CONTROL.EPRX%dINVERT" % link
                 val = self.rd_reg(register)
                 print(register, "\t", val)
@@ -817,7 +821,7 @@ class LPGBT(RegParser):
 
         self.wr_reg("LPGBT.RWF.VOLTAGE_DAC.CURDACENABLE", 0x1)
         if verbose:
-            print("Set current DAC...", self.rd_reg("LPGBT.RWF.VOLTAGE_DAC.CURDACENABLE"))
+            print("Enable DAC current source...", self.rd_reg("LPGBT.RWF.VOLTAGE_DAC.CURDACENABLE"))
 
         if channel == 0:
             adc_chn = self.LPGBT_CONST.CURDAC_CHN0_bm
diff --git a/tamalero/Module.py b/tamalero/Module.py
index 5b454cc..4b5f462 100644
--- a/tamalero/Module.py
+++ b/tamalero/Module.py
@@ -113,15 +113,15 @@ class Module:
                 if etroc.daq_locked:
                     status += 1
 
-            self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], to=0)
+            self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], 0)
             sleep(0.25)
             for i in range(status):
-                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], to=1)
+                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], 1)
                 sleep(0.25)
-                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], to=0)
+                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], 0)
                 sleep(0.25)
 
             if status > 0:
-                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], to=1)
+                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], 1)
             else:
-                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], to=0)
+                self.rb.SCA.set_gpio(self.rb.SCA.gpio_mapping[self.config['status']]['pin'], 0)
diff --git a/tamalero/ReadoutBoard.py b/tamalero/ReadoutBoard.py
index ff35243..3285fe6 100644
--- a/tamalero/ReadoutBoard.py
+++ b/tamalero/ReadoutBoard.py
@@ -296,8 +296,6 @@ class ReadoutBoard:
         self.SCA.reset()
         self.SCA.connect()
         try:
-            print("version in SCA", self.SCA.ver)
-            print("config in SCA", self.SCA.config)
             self.SCA.config_gpios()  # this sets the directions etc according to the mapping
         except TimeoutError:
             print ("SCA config failed. Will continue without SCA.")
diff --git a/tamalero/utils.py b/tamalero/utils.py
index d6d7ad9..8571bf6 100644
--- a/tamalero/utils.py
+++ b/tamalero/utils.py
@@ -354,6 +354,8 @@ def get_config(config, version='v2', verbose=False):
                             print(f"\n - Updating configuration for {chip}, {interface}, {k} to:")
                             print(updated_cfg[chip][interface][k])
                         default_cfg[chip][interface][k] = updated_cfg[chip][interface][k]
+        for links in ['trigger', 'clocks', 'downlink', 'uplink']:
+            default_cfg['inversions'][links] = updated_cfg['inversions'][links]
     return default_cfg
 
 def majority_vote(values, majority=None):
-- 
GitLab


From 342507d59639bb79894ab290558e9160e0579ee1 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Mon, 15 May 2023 17:38:50 -0400
Subject: [PATCH 10/11] fixing current DAC bug

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

diff --git a/tamalero/LPGBT.py b/tamalero/LPGBT.py
index be2c147..bb8fc70 100644
--- a/tamalero/LPGBT.py
+++ b/tamalero/LPGBT.py
@@ -173,7 +173,7 @@ class LPGBT(RegParser):
         for channel in self.adc_mapping:
             if self.adc_mapping[channel]['current'] == 1:
                 print(f'Setting {channel} to current DAC')
-                self.set_current_dac(self.adc_mapping[channel]['pin'])
+                self.set_current_adc(self.adc_mapping[channel]['pin'])
         #if self.ver == 0:
         #    self.adc_mapping = read_mapping(os.path.expandvars('$TAMALERO_BASE/configs/LPGBT_mapping.yaml'), 'adc')
         #elif self.ver == 1:
-- 
GitLab


From c6230196cc8a39b71d644ea3e7db360e19585c88 Mon Sep 17 00:00:00 2001
From: dspitzba <daniel.spitzbart@cern.ch>
Date: Mon, 15 May 2023 18:05:25 -0400
Subject: [PATCH 11/11] fixing startup script

---
 tests/startup.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/startup.sh b/tests/startup.sh
index 922e165..b62a71b 100644
--- a/tests/startup.sh
+++ b/tests/startup.sh
@@ -108,7 +108,7 @@ fi
 
 # run test_tamalero with power up
 info "Running test_tamalero..."
-/usr/bin/env python3 test_tamalero.py --kcu "${KCU}" --power_up --control_hub --verbose --adcs --strict
+/usr/bin/env python3 test_tamalero.py --kcu "${KCU}" --power_up --control_hub --verbose --adcs --strict --configuration emulator
 EXIT=$?
 if [ ${EXIT} -ne 0 ]; then
 	error "Failure when running test_tamalero.py; exit code is ${EXIT}. Blocking merge."
-- 
GitLab