From c05531edb86a043d4afd9d98ae83b961857be8dc Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Mon, 24 Jun 2024 23:43:21 +0000
Subject: [PATCH 01/23] Return results from selftest

---
 models/board_id.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/models/board_id.py b/models/board_id.py
index abe68ca..bfc9063 100644
--- a/models/board_id.py
+++ b/models/board_id.py
@@ -66,7 +66,7 @@ class BoardID(SelfTestExecutor):
 
 
     def self_test_main(self, **kwargs):
-        self.get_board_info()
+        return self.get_board_info()
 
 
     def self_test_check_result(self, result):
-- 
GitLab


From 8a2472c1c074a1d9d811172d2b2a88329836ee78 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Thu, 18 Jul 2024 16:07:33 +0200
Subject: [PATCH 02/23] read limits for LTM4700 and TMP435

---
 boards/flx182.py      |  8 +++++++-
 templates/sysmon.html | 20 +++++++++++++++++---
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index fb3b38a..c0c6693 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -61,9 +61,14 @@ config = {
 				"title" : "LTM4700",						
 				"patterns" : {
 					"voltage" : "(?P<name>in\\d+_input)",
+					"voltage_min" : "(?P<name>in\\d+_min)(?!_alarm)",
+					"voltage_max" : "(?P<name>in\\d+_max)(?!_alarm)",
+					"voltage_crit" : "(?P<name>in\\d+_crit)(?!_alarm)",
 					"current" : "(?P<name>curr\\d+_input)",
-					"temp" : "(?P<name>temp\\d+_input)",
+					"current_max" : "(?P<name>curr\\d+_max)(?!_alarm)",
 					"power" : "(?P<name>power\\d+_input)",
+					"temp" : "(?P<name>temp\\d+_input)",
+					"temp_max" : "(?P<name>temp\\d+_crit)(?!_alarm)",
 				},
 			},
 
@@ -72,6 +77,7 @@ config = {
 				"title" : "TMP435",
 				"patterns" : {
 					"probe" : "(?P<name>temp\\d+_input)",
+					"max" : "(?P<name>temp\\d+_crit)(?!_alarm)(?!_hyst)",
 				},
 				"self_test" : {
 					"description" : "TMP435 readout",
diff --git a/templates/sysmon.html b/templates/sysmon.html
index a06c9c9..ff87d15 100644
--- a/templates/sysmon.html
+++ b/templates/sysmon.html
@@ -10,7 +10,9 @@
     <tr>
       <th scope="col">Sensor</th>
       <th scope="col">Local temperature [deg C]</th>
+      <th scope="col">Max [deg C]</th>
       <th scope="col">Probe temperature [deg C]</th>
+      <th scope="col">Max [deg C]</th>
     </tr>
   </thead>
   <tbody>
@@ -19,7 +21,9 @@
   <tr>
     <th scope="row">{{device_data['name']}}</th>
     <td>{{ "{:.3f}".format(device_data["probe"]['temp1_input'] / 1000) }} </td>
+    <td>{{ "{:.3f}".format(device_data["max"]['temp1_crit'] / 1000) }} </td>
     <td>{{ "{:.3f}".format(device_data["probe"]['temp2_input'] / 1000) }} </td>
+    <td>{{ "{:.3f}".format(device_data["max"]['temp2_crit'] / 1000) }} </td>
   </tr>
   {% endfor %}
 
@@ -37,7 +41,9 @@
       <tr>
         <th scope="col">Sensor</th>
         <th scope="col">Voltage [V]</th>
+        <th scope="col">Range [V]</th>
         <th scope="col">Current [A]</th>
+        <th scope="col">Max [A]</th>
         <th scope="col">Power [W]</th>
         <th scope="col">Temperature [deg C]</th>
       </tr>
@@ -49,13 +55,17 @@
       <td></td>
       <td></td>
       <td></td>
+      <td></td>
+      <td></td>
       <td>{{ "{:.3f}".format(device_data["temp"]['temp3_input'] / 1000) }}</td>
     </tr>
 
     <tr>
-      <th scope="row">Input</th>
+      <th scope="row">INPUT</th>
       <td>{{ "{:.3f}".format(device_data["voltage"]['in1_input'] / 1000) }} </td>
+      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage_min"]['in1_min'] / 1000, device_data["voltage_crit"]['in1_crit'] / 1000) }} </td>
       <td>{{ "{:.3f}".format(device_data["current"]['curr1_input'] / 1000) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current_max"]['curr1_max'] / 1000) }} </td>
       <td>{{ "{:.3f}".format(device_data["power"]['power1_input'] / 1e6) }} </td>
       <td></td>
     </tr>
@@ -63,7 +73,9 @@
     <tr>
       <th scope="row">OUT1</th>
       <td>{{ "{:.3f}".format(device_data["voltage"]['in2_input'] / 1000) }} </td>
+      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage_min"]['in2_min'] / 1000, device_data["voltage_max"]['in2_max'] / 1000) }} </td>
       <td>{{ "{:.3f}".format(device_data["current"]['curr2_input'] / 1000) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current_max"]['curr2_max'] / 1000) }} </td>
       <td>{{ "{:.3f}".format(device_data["power"]['power2_input'] / 1e6) }} </td>
       <td>{{ "{:.3f}".format(device_data["temp"]['temp1_input'] / 1000) }} </td>
     </tr>
@@ -71,7 +83,9 @@
     <tr>
       <th scope="row">OUT2</th>
       <td>{{ "{:.3f}".format(device_data["voltage"]['in3_input'] / 1000) }} </td>
+      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage_min"]['in3_min'] / 1000, device_data["voltage_max"]['in3_max'] / 1000) }} </td>
       <td>{{ "{:.3f}".format(device_data["current"]['curr3_input'] / 1000) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current_max"]['curr3_max'] / 1000) }} </td>
       <td>{{ "{:.3f}".format(device_data["power"]['power3_input'] / 1e6) }} </td>
       <td>{{ "{:.3f}".format(device_data["temp"]['temp2_input'] / 1000) }} </td>
     </tr>
@@ -120,7 +134,7 @@
   </thead>
   <tbody>
 
-  	{% for name, value in device_data['temperature'].items() | sort %}
+    {% for name, value in device_data['temperature'].items() | sort %}
     <tr>
       <th scope="row">{{name}}</th>
       <td>{{ "{:.1f}".format(value) }} C</td>
@@ -143,7 +157,7 @@
   </thead>
   <tbody>
 
-  	{% for name, value in device_data['voltage'].items() | sort %}
+    {% for name, value in device_data['voltage'].items() | sort %}
     <tr>
       <th scope="row">{{name}}</th>
       <td>{{ "{:.3f}".format(value) }} V</td>
-- 
GitLab


From b471693f987e1c7e3bf8c4c9c59ddfdfd314acd3 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Thu, 18 Jul 2024 18:22:29 +0200
Subject: [PATCH 03/23] Limits and tolerance for INA226

---
 boards/flx182.py      | 12 ++++++++++++
 models/ina226.py      | 27 +++++++++++++++++++++------
 templates/ina226.html | 14 ++++++++++----
 3 files changed, 43 insertions(+), 10 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index c0c6693..0d80996 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -39,6 +39,18 @@ config = {
 		"name" : "ina226",
 		"self_test" : {
 			"description" : "INA226 chip readout",
+		},
+		"reference" : {
+			"12P0V" :   {"V" : 12.00, "V_tolerance" : 0.03, "I" : 12.00, "I_max" : 1.0},
+			"MGTAVCC" : {"V" : 0.88 , "V_tolerance" : 0.03, "I" : 0.88 , "I_max" : 3.5},
+			"MGTAVTT" : {"V" : 1.20 , "V_tolerance" : 0.03, "I" : 1.20 , "I_max" : 6.5},
+			"SYS12" :   {"V" : 1.20 , "V_tolerance" : 0.03, "I" : 1.20 , "I_max" : 4.1},
+			"SYS15" :   {"V" : 1.50 , "V_tolerance" : 0.03, "I" : 1.50 , "I_max" : 1.6},
+			"SYS18" :   {"V" : 1.80 , "V_tolerance" : 0.03, "I" : 1.80 , "I_max" : 1.5},
+			"SYS25" :   {"V" : 2.50 , "V_tolerance" : 0.03, "I" : 2.50 , "I_max" : 0.5},
+			"SYS33" :   {"V" : 3.30 , "V_tolerance" : 0.03, "I" : 3.30 , "I_max" : 6.0},
+			"SYS38" :   {"V" : 3.80 , "V_tolerance" : 0.03, "I" : 3.80 , "I_max" : 1.0},
+			"VCCINT" :  {"V" : 0.80 , "V_tolerance" : 0.03, "I" : 0.80 , "I_max" : 50.0}
 		}
 	},
 
diff --git a/models/ina226.py b/models/ina226.py
index a6e7df6..d018ecb 100644
--- a/models/ina226.py
+++ b/models/ina226.py
@@ -11,7 +11,7 @@ class INA226(SelfTestExecutor):
 		self.config = config	
 		self.device_path = config["device_path"]
 		self.name = config["name"]
-		self.devices = get_devices(self.device_path, self.name)	
+		self.devices = get_devices(self.device_path, self.name)
 
 
 	# returns hashmap of all readings available for this device
@@ -34,12 +34,27 @@ class INA226(SelfTestExecutor):
 			print(err)
 			return {}
 
+		voltage = v1_raw * v1_scale / 1000.0
+		current = i3_raw * i3_scale / 1000.0
+		power   = p2_raw * p2_scale / 1000.0
+		V_dev = -1
+		V_tol = -1
+		I_max = -1
+
+		if label in self.config["reference"]:
+			rail = self.config["reference"][label]
+			V_dev = (voltage - rail["V"]) / rail["V"]
+			V_tol = self.config["reference"][label]["V_tolerance"]
+			I_max = self.config["reference"][label]["I_max"]
+
 		data = {
-			"V0" : v0_raw * v0_scale,
-			"V1" : v1_raw * v1_scale,
-			"P2" : p2_raw * p2_scale,
-			"I3" : i3_raw * i3_scale,
-			"R_shunt" : r_shunt
+			"V" : voltage,
+			"Vdev" : V_dev,
+			"Vtol" : V_tol,
+			"I" : current,
+			"Imax" : I_max,
+			"P" : power,
+			"R_shunt" : r_shunt / 1000.0
 		}
 
 		return data, label
diff --git a/templates/ina226.html b/templates/ina226.html
index bf30a7e..744d0dd 100644
--- a/templates/ina226.html
+++ b/templates/ina226.html
@@ -9,7 +9,10 @@
     <tr>
       <th scope="col">Name</th>
       <th scope="col">VCC [V]</th>
+      <th scope="col">VCC deviation [%]</th>
+      <th scope="col">VCC tolerance [%]</th>
       <th scope="col">I [A]</th>
+      <th scope="col">I max [A]</th>
       <th scope="col">P [W]</th>
       <th scope="col">R_sh [mOhm]</th>
     </tr>
@@ -19,10 +22,13 @@
   	{% for label, device in data.items() | sort %}
     <tr>
         <th scope="row">{{ label }}</th>
-        <td>{{ "{:.3f}".format(device["V1"] / 1000) }}</td>
-        <td>{{ "{:.3f}".format(device["I3"] / 1000) }}</td>
-        <td>{{ "{:.3f}".format(device["P2"] / 1000) }}</td>
-        <td>{{ "{:.3f}".format(device["R_shunt"] * 1000) }}</td>
+        <td>{{ "{:.3f}".format(device["V"]) }}</td>
+        <td>{{ "{:.3f}".format(device["Vdev"]) }}</td>
+        <td>{{ "{:.3f}".format(device["Vtol"]) }}</td>
+        <td>{{ "{:.3f}".format(device["I"]) }}</td>
+        <td>{{ "{:.3f}".format(device["Imax"]) }}</td>
+        <td>{{ "{:.3f}".format(device["P"]) }}</td>
+        <td>{{ "{:.3f}".format(device["R_shunt"]) }}</td>
     </tr>
     {% endfor %}
 
-- 
GitLab


From 025659017f0d5fdc4fc8eaf6be5af84d22ed57a4 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Fri, 19 Jul 2024 18:21:34 +0200
Subject: [PATCH 04/23] Added hwmon limits from Xilinx document.

---
 boards/flx182.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++--
 models/sysmon.py | 13 ++++++--
 2 files changed, 91 insertions(+), 5 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index 0d80996..b26c36a 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -40,6 +40,7 @@ config = {
 		"self_test" : {
 			"description" : "INA226 chip readout",
 		},
+		#tolerance and max current set by hand
 		"reference" : {
 			"12P0V" :   {"V" : 12.00, "V_tolerance" : 0.03, "I" : 12.00, "I_max" : 1.0},
 			"MGTAVCC" : {"V" : 0.88 , "V_tolerance" : 0.03, "I" : 0.88 , "I_max" : 3.5},
@@ -66,6 +67,74 @@ config = {
 					"voltage" : "in_voltage(?P<id>\\d+)_(?P<name>.+)_input",
 					"temperature" : "in_temp(?P<id>\\d+)_(?P<name>.+)_input",
 				},
+				"reference" : {
+					# Reference Versal Prime Series Data Sheet: DC and AC Switching Characteristics
+					# DS956 2024-04-30
+					# Table: Recommended Operating Conditions
+					"max_max" :         {"nominal" : 36, "min" : 0, "max" : 100, "source" : "N/A"},
+					"temp" : 		     {"nominal" : 36, "min" : 0, "max" : 100, "source" : "N/A"},
+					"gty_avcc_103" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_104" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_105" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_106" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_200" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_201" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_202" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_203" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_204" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_205" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avcc_206" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+					"gty_avccaux_103" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_104" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_105" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_106" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_200" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_201" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_202" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_203" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_204" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_205" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avccaux_206" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+					"gty_avtt_103" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_104" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_105" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_106" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_200" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_201" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_202" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_203" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_204" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_205" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"gty_avtt_206" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+					"vcc_batt" : 		 {"nominal" : 0, "min" : 1.200, "max" : 1.500, "source" : "DS956", "desc" : "Battery power supply"},
+					"vcc_pmc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PMC primary power supply, mid (M) voltage"},
+					"vcc_psfp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS full-power domain power supply, mid (M) voltage"},
+					"vcc_pslp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS low-power domain power supply, mid (M) voltage"},
+					"vcc_ram" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL RAM and clocking network" },
+					"vcc_soc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "Network on Chip (NoC) and DDR memory controller power supply"},
+					"vccaux" : 		 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "Auxiliary power supply	"},
+					"vccaux_pmc" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC auxiliary power supply voltage"},
+					"vccaux_smon" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC system monitor power supply relative to GND_SMON"},
+					"vccint" : 	     {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL primary power supply"},
+					"vcco_306" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 3 output driver"},
+					"vcco_406" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 4 output driver"},
+					"vcco_500" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+					"vcco_501" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+					"vcco_502" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+					"vcco_503" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+					"vcco_700" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_701" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_702" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_703" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_704" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_705" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_706" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_707" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_708" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_709" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_710" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+					"vcco_711" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"}
+				}
 			},
 
 			"ltm4700" : {
@@ -82,7 +151,7 @@ config = {
 					"temp" : "(?P<name>temp\\d+_input)",
 					"temp_max" : "(?P<name>temp\\d+_crit)(?!_alarm)",
 				},
-			},
+			}, #end of ltm4700
 
 			"tmp435" : {
 				"device_path" : "/sys/class/hwmon/",
@@ -93,6 +162,16 @@ config = {
 				},
 				"self_test" : {
 					"description" : "TMP435 readout",
+				},
+				#max temp by common sense
+				"reference" : {
+					"MGTAVCC_TMP" : {"max" : 50.0 },
+					"MGTAVTT_TMP" : {"max" : 50.0 },
+					"SYS12_TMP" :   {"max" : 50.0 },
+					"SYS15_TMP" :   {"max" : 50.0 },
+					"SYS18_TMP" :   {"max" : 50.0 },
+					"SYS25_TMP" :   {"max" : 50.0 },
+					"SYS33_TMP" :   {"max" : 50.0 },
 				}
 			},
 
@@ -105,7 +184,7 @@ config = {
 			},
 		},
 		"self_test" : {
-			"description" : "HWMON device readout (SYSMON, TMP435, etc)",
+			"description" : "HWMON device readout (SYSMON, LTM4700, TMP435, etc)",
 		}
 	},
 
diff --git a/models/sysmon.py b/models/sysmon.py
index 5c3b60c..208e9a3 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -55,7 +55,7 @@ class SYSMON(SelfTestExecutor):
 
 
 	# read temperature, voltage, current, etc of a single device of specific class
-	def read_device(self, device_path, compiled_patterns):		
+	def read_device(self, device_path, compiled_patterns):
 		result = {}
 		file_names = listdir(device_path)
 		for pattern_name, pattern in compiled_patterns.items():
@@ -74,7 +74,14 @@ class SYSMON(SelfTestExecutor):
 			return {"error" : f"Can't access HWMON device: {device_class}"}
 
 		class_config = self.config["device_classes"][device_class]
-		compiled_patterns = compile_patterns(class_config["patterns"]) 
+		compiled_patterns = compile_patterns(class_config["patterns"])
+
+		#read reference values if available
+		#if "reference" in class_config:
+		#	references = class_config["reference"]
+		#else:
+		#	references = {}
+
 		result = { 
 			"title" : class_config["title"],
 			"class" : device_class,			
@@ -83,7 +90,7 @@ class SYSMON(SelfTestExecutor):
 
 		# read all devices of the requested class	
 		# device is folder path to IIO device, e.g. "/sys/bus/iio/devices/iio:device4"
-		for device_path in devices:			
+		for device_path in devices:
 			result["devices"][device_path] = self.read_device(device_path, compiled_patterns)
 			result["devices"][device_path]["name"] = get_name(device_path)
 
-- 
GitLab


From 249c3a05a67e34ee572161310e310febafcfe3b7 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Mon, 22 Jul 2024 18:10:34 +0200
Subject: [PATCH 05/23] same limit format for LTM4700, TMP and SYSMON

---
 boards/flx182.py      | 147 ++++++++++++++++++++++--------------------
 models/sysmon.py      |  94 ++++++++++++++++++++++++---
 templates/sysmon.html |  67 ++++++++++---------
 3 files changed, 195 insertions(+), 113 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index b26c36a..cfd206b 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -71,69 +71,73 @@ config = {
 					# Reference Versal Prime Series Data Sheet: DC and AC Switching Characteristics
 					# DS956 2024-04-30
 					# Table: Recommended Operating Conditions
-					"max_max" :         {"nominal" : 36, "min" : 0, "max" : 100, "source" : "N/A"},
-					"temp" : 		     {"nominal" : 36, "min" : 0, "max" : 100, "source" : "N/A"},
-					"gty_avcc_103" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_104" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_105" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_106" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_200" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_201" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_202" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_203" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_204" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_205" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avcc_206" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
-					"gty_avccaux_103" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_104" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_105" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_106" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_200" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_201" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_202" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_203" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_204" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_205" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avccaux_206" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
-					"gty_avtt_103" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_104" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_105" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_106" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_200" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_201" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_202" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_203" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_204" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_205" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"gty_avtt_206" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-					"vcc_batt" : 		 {"nominal" : 0, "min" : 1.200, "max" : 1.500, "source" : "DS956", "desc" : "Battery power supply"},
-					"vcc_pmc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PMC primary power supply, mid (M) voltage"},
-					"vcc_psfp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS full-power domain power supply, mid (M) voltage"},
-					"vcc_pslp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS low-power domain power supply, mid (M) voltage"},
-					"vcc_ram" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL RAM and clocking network" },
-					"vcc_soc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "Network on Chip (NoC) and DDR memory controller power supply"},
-					"vccaux" : 		 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "Auxiliary power supply	"},
-					"vccaux_pmc" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC auxiliary power supply voltage"},
-					"vccaux_smon" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC system monitor power supply relative to GND_SMON"},
-					"vccint" : 	     {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL primary power supply"},
-					"vcco_306" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 3 output driver"},
-					"vcco_406" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 4 output driver"},
-					"vcco_500" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
-					"vcco_501" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
-					"vcco_502" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
-					"vcco_503" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
-					"vcco_700" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_701" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_702" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_703" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_704" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_705" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_706" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_707" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_708" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_709" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_710" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
-					"vcco_711" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"}
+					"temperature" : {
+						"max_max" :         {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A"},
+						"temp" : 		     {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A"},
+					},
+					"voltage" : {
+						"gty_avcc_103" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_104" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_105" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_106" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_200" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_201" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_202" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_203" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_204" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_205" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_206" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avccaux_103" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_104" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_105" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_106" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_200" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_201" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_202" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_203" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_204" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_205" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_206" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avtt_103" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_104" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_105" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_106" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_200" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_201" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_202" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_203" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_204" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_205" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_206" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"vcc_batt" : 		 {"nominal" : 0, "min" : 1.200, "max" : 1.500, "source" : "DS956", "desc" : "Battery power supply"},
+						"vcc_pmc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PMC primary power supply, mid (M) voltage"},
+						"vcc_psfp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS full-power domain power supply, mid (M) voltage"},
+						"vcc_pslp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS low-power domain power supply, mid (M) voltage"},
+						"vcc_ram" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL RAM and clocking network" },
+						"vcc_soc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "Network on Chip (NoC) and DDR memory controller power supply"},
+						"vccaux" : 		 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "Auxiliary power supply	"},
+						"vccaux_pmc" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC auxiliary power supply voltage"},
+						"vccaux_smon" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC system monitor power supply relative to GND_SMON"},
+						"vccint" : 	     {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL primary power supply"},
+						"vcco_306" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 3 output driver"},
+						"vcco_406" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 4 output driver"},
+						"vcco_500" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_501" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_502" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_503" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_700" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_701" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_702" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_703" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_704" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_705" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_706" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_707" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_708" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_709" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_710" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_711" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"}
+					}
 				}
 			},
 
@@ -158,20 +162,21 @@ config = {
 				"title" : "TMP435",
 				"patterns" : {
 					"probe" : "(?P<name>temp\\d+_input)",
-					"max" : "(?P<name>temp\\d+_crit)(?!_alarm)(?!_hyst)",
 				},
 				"self_test" : {
 					"description" : "TMP435 readout",
 				},
 				#max temp by common sense
 				"reference" : {
-					"MGTAVCC_TMP" : {"max" : 50.0 },
-					"MGTAVTT_TMP" : {"max" : 50.0 },
-					"SYS12_TMP" :   {"max" : 50.0 },
-					"SYS15_TMP" :   {"max" : 50.0 },
-					"SYS18_TMP" :   {"max" : 50.0 },
-					"SYS25_TMP" :   {"max" : 50.0 },
-					"SYS33_TMP" :   {"max" : 50.0 },
+					"probe" : {
+						"MGTAVCC_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+						"MGTAVTT_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+						"SYS12_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+						"SYS15_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+						"SYS18_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+						"SYS25_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+						"SYS33_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+					}
 				}
 			},
 
diff --git a/models/sysmon.py b/models/sysmon.py
index 208e9a3..4997df7 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -65,6 +65,7 @@ class SYSMON(SelfTestExecutor):
 
 	# read all sensor outputs of SYSMON
 	def read_all(self, device_class):
+
 		if not device_class in self.config["device_classes"]:
 			return { "error" : f"Unknown HWMON device class {device_class}. "
 				+ f'Valid choices: {list(self.config["device_classes"].keys())}'}
@@ -73,16 +74,10 @@ class SYSMON(SelfTestExecutor):
 		if not devices:
 			return {"error" : f"Can't access HWMON device: {device_class}"}
 
+		#struct that describes the hwmon device
 		class_config = self.config["device_classes"][device_class]
-		compiled_patterns = compile_patterns(class_config["patterns"])
-
-		#read reference values if available
-		#if "reference" in class_config:
-		#	references = class_config["reference"]
-		#else:
-		#	references = {}
 
-		result = { 
+		result = {
 			"title" : class_config["title"],
 			"class" : device_class,			
 			"devices" : {}
@@ -90,15 +85,94 @@ class SYSMON(SelfTestExecutor):
 
 		# read all devices of the requested class	
 		# device is folder path to IIO device, e.g. "/sys/bus/iio/devices/iio:device4"
+		compiled_patterns = compile_patterns(class_config["patterns"])
 		for device_path in devices:
 			result["devices"][device_path] = self.read_device(device_path, compiled_patterns)
 			result["devices"][device_path]["name"] = get_name(device_path)
 
-		return result
-		
+		# Process readings to add reference values
+		if device_class == "xlnx,versal-sysmon":
+			return self.process_versal_sysmon(result, class_config, device_path)
+		elif device_class == "tmp435":
+			return self.process_tmp(result, class_config)
+		elif device_class == "ltm4700":
+			return self.process_ltm(result, class_config, device_path)
+
 
 	def self_test_main(self, **kwargs):
 		result = {}
 		for device_class in self.config["device_classes"]:
 			result[device_class] = self.read_all(device_class)
 		return result
+
+
+	def process_versal_sysmon(self, result, class_config, device_path):
+		compiled_patterns = compile_patterns(class_config["patterns"])
+		for quantity in compiled_patterns:
+			for measurment_name in result["devices"][device_path][quantity]:
+				value = result["devices"][device_path][quantity][measurment_name]
+				if measurment_name in class_config["reference"][quantity]:
+					nominal = class_config["reference"][quantity][measurment_name]["nominal"]
+					max_value = class_config["reference"][quantity][measurment_name]["max"]
+					min_value = class_config["reference"][quantity][measurment_name]["min"]
+					new_value = {"read" : value, "min" : min_value, "nominal" : nominal, "max": max_value}
+				else:
+					new_value = {"read" : value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan')}
+
+				result["devices"][device_path][quantity][measurment_name] = new_value
+		return result
+
+
+	# Each TMP probe is on a different file device.
+	# Data has to be reformatted to have the same format as versal-sysmon's
+	def process_tmp(self, result, class_config):
+		compiled_patterns = compile_patterns(class_config["patterns"])
+		new_devices = {'/sys/class/hwmon/hwmonX' : {}}
+		for device_path, device_data in result["devices"].items():
+			probe_name = device_data['name']
+			local_value = device_data['probe']['temp1_input']/1000.0
+			probe_value = device_data['probe']['temp2_input']/1000.0
+			if probe_name in class_config["reference"]["probe"]:
+				nominal = class_config["reference"]["probe"][probe_name]["nominal"]
+				max_value = class_config["reference"]["probe"][probe_name]["max"]
+				min_value = class_config["reference"]["probe"][probe_name]["min"]				
+				measurement = {"local" : local_value, "probe" : probe_value, "min" : min_value, "nominal" : nominal, "max": max_value}
+			else:
+				measurement = {"local" : local_value, "probe" : probe_value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan')}
+			new_devices['/sys/class/hwmon/hwmonX'][probe_name] = measurement
+		result['devices'] = new_devices
+		return result
+
+
+	# Limits derived from hardware
+	def process_ltm(self, result, class_config, device_path):
+		new_device_data = {'voltage':{}, 'current':{}, 'power':{}, 'temperature':{}}
+		data = result["devices"][device_path]
+		for key in ['in1', 'in2', 'in3']:
+			new_device_data['voltage'][key] = {
+				'read': data['voltage'].get(f'{key}_input', float('nan'))/1000.0,
+				'min': data['voltage_min'].get(f'{key}_min', float('nan'))/1000.0,
+				'max': data['voltage_max'].get(f'{key}_max', float('nan'))/1000.0
+			}
+		for key in ['curr1', 'curr2', 'curr3']:
+			new_device_data['current'][key] = {
+				'read': data['current'].get(f'{key}_input', float('nan'))/1000.0,
+				'max': data['current_max'].get(f'{key}_max', float('nan'))
+			}
+		for key in ['curr1', 'curr2', 'curr3']:
+			new_device_data['current'][key] = {
+				'read': data['current'].get(f'{key}_input', float('nan'))/1000.0,
+				'max': data['current_max'].get(f'{key}_max', float('nan'))/1000.0
+			}
+		for key in ['power1', 'power2', 'power3']:
+			new_device_data['power'][key] = {
+				'read': data['power'].get(f'{key}_input', float('nan'))/1e6
+			}
+		for key in ['temp1', 'temp2', 'temp3']:
+			new_device_data['temperature'][key] = {
+				'read': data['temp'].get(f'{key}_input', float('nan'))/1000.0,
+				'max': data['temp_max'].get(f'{key}_max', float('nan'))/1000.0
+			}
+		result["devices"][device_path] = new_device_data
+		return result
+
diff --git a/templates/sysmon.html b/templates/sysmon.html
index ff87d15..a6f182e 100644
--- a/templates/sysmon.html
+++ b/templates/sysmon.html
@@ -9,24 +9,23 @@
   <thead>
     <tr>
       <th scope="col">Sensor</th>
-      <th scope="col">Local temperature [deg C]</th>
-      <th scope="col">Max [deg C]</th>
-      <th scope="col">Probe temperature [deg C]</th>
+      <th scope="col">Local temp [deg C]</th>
+      <th scope="col">Probe temp [deg C]</th>
       <th scope="col">Max [deg C]</th>
     </tr>
   </thead>
   <tbody>
 
   {% for device_path, device_data in data["devices"].items() | sort %}
+  {% for name, value in device_data.items() | sort %}
   <tr>
-    <th scope="row">{{device_data['name']}}</th>
-    <td>{{ "{:.3f}".format(device_data["probe"]['temp1_input'] / 1000) }} </td>
-    <td>{{ "{:.3f}".format(device_data["max"]['temp1_crit'] / 1000) }} </td>
-    <td>{{ "{:.3f}".format(device_data["probe"]['temp2_input'] / 1000) }} </td>
-    <td>{{ "{:.3f}".format(device_data["max"]['temp2_crit'] / 1000) }} </td>
+    <th scope="row">{{name}}</th>
+    <td>{{ "{:.1f}".format(value["local"]) }}</td>
+    <td>{{ "{:.1f}".format(value["probe"]) }}</td>
+    <td>{{ "{:.1f}".format(value["max"])}}</td>
   </tr>
   {% endfor %}
-
+  {% endfor %}
   </tbody>
 </table>
 
@@ -57,37 +56,38 @@
       <td></td>
       <td></td>
       <td></td>
-      <td>{{ "{:.3f}".format(device_data["temp"]['temp3_input'] / 1000) }}</td>
+      <td>{{ "{:.3f}".format(device_data["temperature"]['temp3']['read']) }}</td>
     </tr>
 
     <tr>
       <th scope="row">INPUT</th>
-      <td>{{ "{:.3f}".format(device_data["voltage"]['in1_input'] / 1000) }} </td>
-      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage_min"]['in1_min'] / 1000, device_data["voltage_crit"]['in1_crit'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["current"]['curr1_input'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["current_max"]['curr1_max'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["power"]['power1_input'] / 1e6) }} </td>
+      <td>{{ "{:.3f}".format(device_data["voltage"]['in1']['read']) }} </td>
+      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage"]['in1']['min'], device_data["voltage"]['in1']['max']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current"]['curr1']['read']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current"]['curr1']['max']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["power"]['power1']['read']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["temperature"]['temp1']['read']) }} </td>
       <td></td>
     </tr>
     
     <tr>
       <th scope="row">OUT1</th>
-      <td>{{ "{:.3f}".format(device_data["voltage"]['in2_input'] / 1000) }} </td>
-      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage_min"]['in2_min'] / 1000, device_data["voltage_max"]['in2_max'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["current"]['curr2_input'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["current_max"]['curr2_max'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["power"]['power2_input'] / 1e6) }} </td>
-      <td>{{ "{:.3f}".format(device_data["temp"]['temp1_input'] / 1000) }} </td>
+      <td>{{ "{:.3f}".format(device_data["voltage"]['in2']['read']) }} </td>
+      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage"]['in2']['min'], device_data["voltage"]['in2']['max']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current"]['curr2']['read']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current"]['curr2']['max']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["power"]['power2']['read']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["temperature"]['temp2']['read']) }} </td>
     </tr>
 
     <tr>
       <th scope="row">OUT2</th>
-      <td>{{ "{:.3f}".format(device_data["voltage"]['in3_input'] / 1000) }} </td>
-      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage_min"]['in3_min'] / 1000, device_data["voltage_max"]['in3_max'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["current"]['curr3_input'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["current_max"]['curr3_max'] / 1000) }} </td>
-      <td>{{ "{:.3f}".format(device_data["power"]['power3_input'] / 1e6) }} </td>
-      <td>{{ "{:.3f}".format(device_data["temp"]['temp2_input'] / 1000) }} </td>
+      <td>{{ "{:.3f}".format(device_data["voltage"]['in3']['read']) }} </td>
+      <td>{{ "[{:.3f}, {:.3f}]".format(device_data["voltage"]['in3']['min'], device_data["voltage"]['in3']['max']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current"]['curr3']['read']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["current"]['curr3']['max']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["power"]['power3']['read']) }} </td>
+      <td>{{ "{:.3f}".format(device_data["temperature"]['temp3']['read']) }} </td>
     </tr>
 
     </tbody>
@@ -129,7 +129,8 @@
   <thead>
     <tr>
       <th scope="col">Input</th>
-      <th scope="col">Value</th>
+      <th scope="col">Value (deg C)</th>
+      <th scope="col">[Min, Max] (deg C)</th>
     </tr>
   </thead>
   <tbody>
@@ -137,7 +138,8 @@
     {% for name, value in device_data['temperature'].items() | sort %}
     <tr>
       <th scope="row">{{name}}</th>
-      <td>{{ "{:.1f}".format(value) }} C</td>
+      <td>{{ "{:.1f}".format(value["read"]) }}</td>
+      <td>{{ "[{:.1F}, {:.1F}]".format(value["min"], value["max"])}}</td>
     </tr>
     {% endfor %}
 
@@ -152,15 +154,16 @@
   <thead>
     <tr>
       <th scope="col">Input</th>
-      <th scope="col">Value</th>
+      <th scope="col">Value (V)</th>
+      <th scope="col">[Min, Max] (V)</th>
     </tr>
   </thead>
   <tbody>
-
     {% for name, value in device_data['voltage'].items() | sort %}
     <tr>
       <th scope="row">{{name}}</th>
-      <td>{{ "{:.3f}".format(value) }} V</td>
+      <td>{{ "{:.3f}".format(value["read"]) }}</td>
+      <td>{{ "[{:.3f}, {:.3f}]".format(value["min"], value["max"]) }}</td>
     </tr>
     {% endfor %}
 
-- 
GitLab


From 1b7d60af10efb3b8e3fa67b56472b92d757dd797 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Tue, 23 Jul 2024 16:04:51 +0200
Subject: [PATCH 06/23] Voltage and current limits on tables

---
 boards/flx182.py      | 50 +++++++++++++++++++++++--------------------
 models/ina226.py      | 11 +++++-----
 models/sysmon.py      | 29 ++++++++++++++++++++-----
 templates/ina226.html | 16 ++++++--------
 templates/sysmon.html | 16 ++++++++++++--
 5 files changed, 76 insertions(+), 46 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index cfd206b..752a8c4 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -42,16 +42,16 @@ config = {
 		},
 		#tolerance and max current set by hand
 		"reference" : {
-			"12P0V" :   {"V" : 12.00, "V_tolerance" : 0.03, "I" : 12.00, "I_max" : 1.0},
-			"MGTAVCC" : {"V" : 0.88 , "V_tolerance" : 0.03, "I" : 0.88 , "I_max" : 3.5},
-			"MGTAVTT" : {"V" : 1.20 , "V_tolerance" : 0.03, "I" : 1.20 , "I_max" : 6.5},
-			"SYS12" :   {"V" : 1.20 , "V_tolerance" : 0.03, "I" : 1.20 , "I_max" : 4.1},
-			"SYS15" :   {"V" : 1.50 , "V_tolerance" : 0.03, "I" : 1.50 , "I_max" : 1.6},
-			"SYS18" :   {"V" : 1.80 , "V_tolerance" : 0.03, "I" : 1.80 , "I_max" : 1.5},
-			"SYS25" :   {"V" : 2.50 , "V_tolerance" : 0.03, "I" : 2.50 , "I_max" : 0.5},
-			"SYS33" :   {"V" : 3.30 , "V_tolerance" : 0.03, "I" : 3.30 , "I_max" : 6.0},
-			"SYS38" :   {"V" : 3.80 , "V_tolerance" : 0.03, "I" : 3.80 , "I_max" : 1.0},
-			"VCCINT" :  {"V" : 0.80 , "V_tolerance" : 0.03, "I" : 0.80 , "I_max" : 50.0}
+			"12P0V" :   {"V" : 12.00, "V_min" : 11.0,  "V_max" : 23.0,  "I" : 1,    "I_max" : 8, "desc" : "+/- 1V expected from power supply"},
+			"MGTAVCC" : {"V" : 0.88 , "V_min" : 0.854, "V_max" : 0.906, "I" : 1.5 , "I_max" : 3.5, "desc" : "V limits from FPGA recommended ranges"},
+			"MGTAVTT" : {"V" : 1.20 , "V_min" : 1.164, "V_max" : 1.236, "I" : 3.4 , "I_max" : 6.5, "desc" : "V limits from FPGA recommended ranges"},
+			"SYS12" :   {"V" : 1.20 , "V_min" : 1.164, "V_max" : 1.236, "I" : 0.8 , "I_max" : 1.5, "desc" : "V limits from FPGA recommended ranges"},
+			"SYS15" :   {"V" : 1.50 , "V_min" : 1.455, "V_max" : 1.545, "I" : 0.9 , "I_max" : 1.5, "desc" : "V limits from FPGA recommended ranges"},
+			"SYS18" :   {"V" : 1.80 , "V_min" : 1.710, "V_max" : 1.890, "I" : 0.8 , "I_max" : 1.5, "desc" : "V limits from 25G FF"},
+			"SYS25" :   {"V" : 2.50 , "V_min" : 2.15,  "V_max" : 2.85,  "I" : 0.3 , "I_max" : 0.5, "desc" : ""},
+			"SYS33" :   {"V" : 3.30 , "V_min" : 3.15,  "V_max" : 3.45,  "I" : 3.30 , "I_max" : 6.0, "desc" : "V limits from 14/16G FF"},
+			"SYS38" :   {"V" : 3.80 , "V_min" : 3.50,  "V_max" : 4.10,  "I" : 0.0 , "I_max" : 5, "desc" : ""},
+			"VCCINT" :  {"V" : 0.80 , "V_min" : 0.775, "V_max" : 0.825, "I" : 20.0 , "I_max" : 50.0,"desc" : ""}
 		}
 	},
 
@@ -72,8 +72,8 @@ config = {
 					# DS956 2024-04-30
 					# Table: Recommended Operating Conditions
 					"temperature" : {
-						"max_max" :         {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A"},
-						"temp" : 		     {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A"},
+						"max_max" :         {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A", "desc" : "FPGA diode temperature (latched max)"},
+						"temp" : 		     {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A", "desc" : "FPGA diode temperature"},
 					},
 					"voltage" : {
 						"gty_avcc_103" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
@@ -110,9 +110,9 @@ config = {
 						"gty_avtt_205" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
 						"gty_avtt_206" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
 						"vcc_batt" : 		 {"nominal" : 0, "min" : 1.200, "max" : 1.500, "source" : "DS956", "desc" : "Battery power supply"},
-						"vcc_pmc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PMC primary power supply, mid (M) voltage"},
-						"vcc_psfp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS full-power domain power supply, mid (M) voltage"},
-						"vcc_pslp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS low-power domain power supply, mid (M) voltage"},
+						"vcc_pmc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PMC primary power supply"},
+						"vcc_psfp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS full-power domain power supply"},
+						"vcc_pslp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS low-power domain power supply"},
 						"vcc_ram" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL RAM and clocking network" },
 						"vcc_soc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "Network on Chip (NoC) and DDR memory controller power supply"},
 						"vccaux" : 		 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "Auxiliary power supply	"},
@@ -166,16 +166,15 @@ config = {
 				"self_test" : {
 					"description" : "TMP435 readout",
 				},
-				#max temp by common sense
 				"reference" : {
 					"probe" : {
-						"MGTAVCC_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
-						"MGTAVTT_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
-						"SYS12_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
-						"SYS15_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
-						"SYS18_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
-						"SYS25_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
-						"SYS33_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0 },
+						"LTM4642_TMP" : {"min" : 20.0, "nominal" : 30.0, "max" : 50.0, "desc" : "LTM4642, 2.5V (DDR, ETH) and 3.8V (FF) rails"},
+						"MGTAVCC_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 0.88V rail (FPGA) "},
+						"MGTAVTT_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.2V rail (FPGA, DDR)"},
+						"SYS12_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.2V rail (FPGA, DDR)"},
+						"SYS15_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.5V rail (FPGA)"},
+						"SYS18_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.8V rail (FPGA, FF, Si5345)"},
+						"SYS33_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 3.3V rail (FF, Si5345, QSPI)"},
 					}
 				}
 			},
@@ -186,6 +185,11 @@ config = {
 				"patterns" : {
 					"probe" : "(?P<name>temp\\d+_input)",
 				},
+				"reference" : {
+					"probe" : {
+						"DRAM temperature sensor" : {"min" : 20.0, "nominal" : 33.0, "max" : 50.0, "desc" : "DRAM temperature"},
+					}
+				}
 			},
 		},
 		"self_test" : {
diff --git a/models/ina226.py b/models/ina226.py
index d018ecb..89b53b7 100644
--- a/models/ina226.py
+++ b/models/ina226.py
@@ -42,17 +42,16 @@ class INA226(SelfTestExecutor):
 		I_max = -1
 
 		if label in self.config["reference"]:
-			rail = self.config["reference"][label]
-			V_dev = (voltage - rail["V"]) / rail["V"]
-			V_tol = self.config["reference"][label]["V_tolerance"]
+			V_min = self.config["reference"][label]["V_min"]
+			V_max = self.config["reference"][label]["V_max"]
 			I_max = self.config["reference"][label]["I_max"]
 
 		data = {
 			"V" : voltage,
-			"Vdev" : V_dev,
-			"Vtol" : V_tol,
+			"V_min" : V_min,
+			"V_max" : V_max,
 			"I" : current,
-			"Imax" : I_max,
+			"I_max" : I_max,
 			"P" : power,
 			"R_shunt" : r_shunt / 1000.0
 		}
diff --git a/models/sysmon.py b/models/sysmon.py
index 4997df7..e8e805d 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -97,6 +97,8 @@ class SYSMON(SelfTestExecutor):
 			return self.process_tmp(result, class_config)
 		elif device_class == "ltm4700":
 			return self.process_ltm(result, class_config, device_path)
+		elif device_class == "jc42":
+			return self.process_jc42(result, class_config)
 
 
 	def self_test_main(self, **kwargs):
@@ -115,9 +117,10 @@ class SYSMON(SelfTestExecutor):
 					nominal = class_config["reference"][quantity][measurment_name]["nominal"]
 					max_value = class_config["reference"][quantity][measurment_name]["max"]
 					min_value = class_config["reference"][quantity][measurment_name]["min"]
-					new_value = {"read" : value, "min" : min_value, "nominal" : nominal, "max": max_value}
+					description = class_config["reference"][quantity][measurment_name]["desc"]
+					new_value = {"read" : value, "min" : min_value, "nominal" : nominal, "max": max_value, "desc" : description}
 				else:
-					new_value = {"read" : value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan')}
+					new_value = {"read" : value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan'), "desc" : ""}
 
 				result["devices"][device_path][quantity][measurment_name] = new_value
 		return result
@@ -135,10 +138,11 @@ class SYSMON(SelfTestExecutor):
 			if probe_name in class_config["reference"]["probe"]:
 				nominal = class_config["reference"]["probe"][probe_name]["nominal"]
 				max_value = class_config["reference"]["probe"][probe_name]["max"]
-				min_value = class_config["reference"]["probe"][probe_name]["min"]				
-				measurement = {"local" : local_value, "probe" : probe_value, "min" : min_value, "nominal" : nominal, "max": max_value}
+				min_value = class_config["reference"]["probe"][probe_name]["min"]
+				description = class_config["reference"]["probe"][probe_name]["desc"]
+				measurement = {"local" : local_value, "probe" : probe_value, "min" : min_value, "nominal" : nominal, "max": max_value, "desc" : description}
 			else:
-				measurement = {"local" : local_value, "probe" : probe_value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan')}
+				measurement = {"local" : local_value, "probe" : probe_value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan'), "desc" : ""}
 			new_devices['/sys/class/hwmon/hwmonX'][probe_name] = measurement
 		result['devices'] = new_devices
 		return result
@@ -176,3 +180,18 @@ class SYSMON(SelfTestExecutor):
 		result["devices"][device_path] = new_device_data
 		return result
 
+
+	def process_jc42(self, result, class_config):
+		value = result['devices']['/sys/class/hwmon/hwmon8']['probe']['temp1_input']/1000.0
+		name = result['devices']['/sys/class/hwmon/hwmon8']['name']
+		if name in class_config['reference']['probe']:
+			min_value = class_config['reference']['probe'][name]["min"]
+			max_value = class_config['reference']['probe'][name]["max"]
+			description = class_config['reference']['probe'][name]["desc"]
+			measurement = {"read" : value, "min" : min_value, "max": max_value, "desc" : description}
+		else:
+			measurement = {"read" : value, "min" : float('nan'), "max": float('nan'), "desc" : ""}
+
+		result['devices']['/sys/class/hwmon/hwmon8'] = {}
+		result['devices']['/sys/class/hwmon/hwmon8'][name] = measurement
+		return result
\ No newline at end of file
diff --git a/templates/ina226.html b/templates/ina226.html
index 744d0dd..4ef15ca 100644
--- a/templates/ina226.html
+++ b/templates/ina226.html
@@ -8,13 +8,11 @@
   <thead>
     <tr>
       <th scope="col">Name</th>
-      <th scope="col">VCC [V]</th>
-      <th scope="col">VCC deviation [%]</th>
-      <th scope="col">VCC tolerance [%]</th>
-      <th scope="col">I [A]</th>
-      <th scope="col">I max [A]</th>
+      <th scope="col">Voltage (V)</th>
+      <th scope="col">V range (V)</th>
+      <th scope="col">I (A)</th>
+      <th scope="col">Max I (A)</th>
       <th scope="col">P [W]</th>
-      <th scope="col">R_sh [mOhm]</th>
     </tr>
   </thead>
   <tbody>
@@ -23,12 +21,10 @@
     <tr>
         <th scope="row">{{ label }}</th>
         <td>{{ "{:.3f}".format(device["V"]) }}</td>
-        <td>{{ "{:.3f}".format(device["Vdev"]) }}</td>
-        <td>{{ "{:.3f}".format(device["Vtol"]) }}</td>
+        <td>{{ "[{:.3f}, {:.3f}]".format(device["V_min"], device["V_max"]) }}</td>
         <td>{{ "{:.3f}".format(device["I"]) }}</td>
-        <td>{{ "{:.3f}".format(device["Imax"]) }}</td>
+        <td>{{ "{:.3f}".format(device["I_max"]) }}</td>
         <td>{{ "{:.3f}".format(device["P"]) }}</td>
-        <td>{{ "{:.3f}".format(device["R_shunt"]) }}</td>
     </tr>
     {% endfor %}
 
diff --git a/templates/sysmon.html b/templates/sysmon.html
index a6f182e..ff7e0c1 100644
--- a/templates/sysmon.html
+++ b/templates/sysmon.html
@@ -12,6 +12,7 @@
       <th scope="col">Local temp [deg C]</th>
       <th scope="col">Probe temp [deg C]</th>
       <th scope="col">Max [deg C]</th>
+      <th scope="col">Description</th>
     </tr>
   </thead>
   <tbody>
@@ -23,6 +24,7 @@
     <td>{{ "{:.1f}".format(value["local"]) }}</td>
     <td>{{ "{:.1f}".format(value["probe"]) }}</td>
     <td>{{ "{:.1f}".format(value["max"])}}</td>
+    <td>{{ value["desc"] }}</td>
   </tr>
   {% endfor %}
   {% endfor %}
@@ -102,16 +104,22 @@
     <tr>
       <th scope="col">Device</th>
       <th scope="col">Temperature [deg C]</th>
+      <th scope="col">Max Temp [deg C]</th>
     </tr>
   </thead>
   <tbody>
 
   {% for device_path, device_data in data["devices"].items() | sort %}
+  {% for key, meas in device_data.items() | sort %}
+
   <tr>
-    <th scope="row">{{device_data['name']}}</th>
-    <td>{{ "{:.3f}".format(device_data["probe"]['temp1_input'] / 1000) }} </td>
+    <th scope="row">{{key}}</th>
+    <td>{{ "{:.3f}".format(meas["read"]) }} </td>
+    <td>{{ "{:.3f}".format(meas["max"]) }} </td>
   </tr>
   {% endfor %}
+  {% endfor %}
+
 
   </tbody>
 </table>
@@ -131,6 +139,7 @@
       <th scope="col">Input</th>
       <th scope="col">Value (deg C)</th>
       <th scope="col">[Min, Max] (deg C)</th>
+      <th scope="col">Description</th>
     </tr>
   </thead>
   <tbody>
@@ -140,6 +149,7 @@
       <th scope="row">{{name}}</th>
       <td>{{ "{:.1f}".format(value["read"]) }}</td>
       <td>{{ "[{:.1F}, {:.1F}]".format(value["min"], value["max"])}}</td>
+      <td>{{ value["desc"] }}</td>
     </tr>
     {% endfor %}
 
@@ -156,6 +166,7 @@
       <th scope="col">Input</th>
       <th scope="col">Value (V)</th>
       <th scope="col">[Min, Max] (V)</th>
+      <th scope="col">Description</th>
     </tr>
   </thead>
   <tbody>
@@ -164,6 +175,7 @@
       <th scope="row">{{name}}</th>
       <td>{{ "{:.3f}".format(value["read"]) }}</td>
       <td>{{ "[{:.3f}, {:.3f}]".format(value["min"], value["max"]) }}</td>
+      <td>{{ value["desc"] }}</td>
     </tr>
     {% endfor %}
 
-- 
GitLab


From 64ede4b2d32487245256d95616fcdce4d1e0d5f0 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Tue, 6 Aug 2024 13:24:00 +0200
Subject: [PATCH 07/23] HWMON test conditions

---
 boards/flx182.py             |   2 +-
 controllers/sysmon.py        |   2 -
 models/self_test_executor.py |   2 +-
 models/sysmon.py             | 112 ++++++++++++++++++++++++++++-------
 templates/sysmon.html        |   2 +
 5 files changed, 94 insertions(+), 26 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index 752a8c4..20367f0 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -109,7 +109,7 @@ config = {
 						"gty_avtt_204" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
 						"gty_avtt_205" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
 						"gty_avtt_206" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
-						"vcc_batt" : 		 {"nominal" : 0, "min" : 1.200, "max" : 1.500, "source" : "DS956", "desc" : "Battery power supply"},
+#						"vcc_batt" : 		 {"nominal" : 0, "min" : 1.200, "max" : 1.500, "source" : "DS956", "desc" : "Battery power supply"},
 						"vcc_pmc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PMC primary power supply"},
 						"vcc_psfp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS full-power domain power supply"},
 						"vcc_pslp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS low-power domain power supply"},
diff --git a/controllers/sysmon.py b/controllers/sysmon.py
index ba67770..bd6ebfb 100644
--- a/controllers/sysmon.py
+++ b/controllers/sysmon.py
@@ -23,5 +23,3 @@ class SYSMON_Controller():
 			return render_template("error.html", error=data["error"])
 		else:
 			return render_template("sysmon.html", data=data)
-			
-		
\ No newline at end of file
diff --git a/models/self_test_executor.py b/models/self_test_executor.py
index 057ac01..6bed147 100644
--- a/models/self_test_executor.py
+++ b/models/self_test_executor.py
@@ -36,7 +36,7 @@ class SelfTestExecutor():
 		human_readable_name_getter=None):
 
 		if result is None or not result:
-			return { "status" : "error", "result" : "Test results are missing" }
+			return { "status" : "error", "result" : "No test results available" }
 			
 		devices_with_errors = []
 		devices_with_missing_data = []
diff --git a/models/sysmon.py b/models/sysmon.py
index e8e805d..427bd95 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -1,5 +1,4 @@
-import re, os, pdb
-
+import re, os, pdb, math
 from os import path, listdir
 from util.iio import load_file, get_devices
 from os.path import isfile
@@ -108,21 +107,67 @@ class SYSMON(SelfTestExecutor):
 		return result
 
 
+	def check_measured_value(self, meas_type, name, read_value, min_value, max_value):
+		check = {"status" : "", "msg" : ""}
+		if math.isnan(read_value):
+			check["status"] = "BAD"
+			check["msg"] = "{0} {1} is NaN".format(meas_type, name)
+		elif (not math.isnan(max_value) and read_value > max_value) :
+			check["status"] = "HIGH"
+			check["msg"] = "{0} {1} is {2} above max {3}".format(meas_type, name, read_value, max_value)
+		elif (not math.isnan(min_value) and read_value < min_value):
+			check["status"] = "LOW"
+			check["msg"] = "{0} {1} is {2} below min {3}".format(meas_type, name, read_value, min_value)
+		else:
+			check["status"] = "OK"
+		return check
+
+
+	def self_test_check_result(self, result):
+		status_ok = True
+		msg = []
+		for device_type in result:
+			if 'devices' not in device_type:
+				if 'error' in result[device_type]:
+					msg.append(result[device_type]["error"])
+					status_ok = False
+			else:
+				for device_path in result[device_type]['devices']:
+					for quantity in result[device_type]['devices'][device_path]:
+						if quantity == "name":
+							continue
+						for measurement_name in result[device_type]['devices'][device_path][quantity]:
+							measurement = result[device_type]['devices'][device_path][quantity][measurement_name]
+							if measurement["status"] != "OK":
+								msg.append(measurement["msg"])
+								status_ok = False
+		if status_ok:
+			msg = ["OK"]
+			msg_level = "success"
+		else:
+			msg_level = "error"
+
+		return { "status" : msg_level, "result" : msg }
+
+
+
 	def process_versal_sysmon(self, result, class_config, device_path):
 		compiled_patterns = compile_patterns(class_config["patterns"])
 		for quantity in compiled_patterns:
-			for measurment_name in result["devices"][device_path][quantity]:
-				value = result["devices"][device_path][quantity][measurment_name]
-				if measurment_name in class_config["reference"][quantity]:
-					nominal = class_config["reference"][quantity][measurment_name]["nominal"]
-					max_value = class_config["reference"][quantity][measurment_name]["max"]
-					min_value = class_config["reference"][quantity][measurment_name]["min"]
-					description = class_config["reference"][quantity][measurment_name]["desc"]
-					new_value = {"read" : value, "min" : min_value, "nominal" : nominal, "max": max_value, "desc" : description}
-				else:
-					new_value = {"read" : value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan'), "desc" : ""}
-
-				result["devices"][device_path][quantity][measurment_name] = new_value
+			for measurement_name in result["devices"][device_path][quantity]:
+				value = result["devices"][device_path][quantity][measurement_name]
+				# New result format
+				new_value = {"read" : value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan'), "desc" : ""}
+				# Populate reference fields
+				if measurement_name in class_config["reference"][quantity]:
+					new_value["nominal"] = class_config["reference"][quantity][measurement_name]["nominal"]
+					new_value["max"] = class_config["reference"][quantity][measurement_name]["max"]
+					new_value["min"] = class_config["reference"][quantity][measurement_name]["min"]
+					new_value["desc"] = class_config["reference"][quantity][measurement_name]["desc"]
+				# Check value
+				check = self.check_measured_value(quantity, measurement_name, value, new_value["min"], new_value["max"])
+				new_value = {**new_value, **check}
+				result["devices"][device_path][quantity][measurement_name] = new_value
 		return result
 
 
@@ -135,14 +180,17 @@ class SYSMON(SelfTestExecutor):
 			probe_name = device_data['name']
 			local_value = device_data['probe']['temp1_input']/1000.0
 			probe_value = device_data['probe']['temp2_input']/1000.0
+			measurement = {"local" : local_value, "probe" : probe_value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan'), "desc" : ""}
+
 			if probe_name in class_config["reference"]["probe"]:
-				nominal = class_config["reference"]["probe"][probe_name]["nominal"]
-				max_value = class_config["reference"]["probe"][probe_name]["max"]
-				min_value = class_config["reference"]["probe"][probe_name]["min"]
-				description = class_config["reference"]["probe"][probe_name]["desc"]
-				measurement = {"local" : local_value, "probe" : probe_value, "min" : min_value, "nominal" : nominal, "max": max_value, "desc" : description}
-			else:
-				measurement = {"local" : local_value, "probe" : probe_value, "min" : float('nan'), "nominal" : float('nan'), "max": float('nan'), "desc" : ""}
+				measurement["nominal"] = class_config["reference"]["probe"][probe_name]["nominal"]
+				measurement["max"] = class_config["reference"]["probe"][probe_name]["max"]
+				measurement["min"] = class_config["reference"]["probe"][probe_name]["min"]
+				measurement["desc"] = class_config["reference"]["probe"][probe_name]["desc"]
+
+			# Check value
+			check = self.check_measured_value(quantity, measurement_name, value, new_value["min"], new_value["max"])
+			measurement = {**measurement, **check}
 			new_devices['/sys/class/hwmon/hwmonX'][probe_name] = measurement
 		result['devices'] = new_devices
 		return result
@@ -158,25 +206,45 @@ class SYSMON(SelfTestExecutor):
 				'min': data['voltage_min'].get(f'{key}_min', float('nan'))/1000.0,
 				'max': data['voltage_max'].get(f'{key}_max', float('nan'))/1000.0
 			}
+			check = check_measured_value('voltage', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
+			new_device_data = {**new_device_data, **check}
+
 		for key in ['curr1', 'curr2', 'curr3']:
 			new_device_data['current'][key] = {
 				'read': data['current'].get(f'{key}_input', float('nan'))/1000.0,
+				'min': float('nan'),
 				'max': data['current_max'].get(f'{key}_max', float('nan'))
 			}
+			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
+			new_device_data = {**new_device_data, **check}
+
 		for key in ['curr1', 'curr2', 'curr3']:
 			new_device_data['current'][key] = {
 				'read': data['current'].get(f'{key}_input', float('nan'))/1000.0,
+				'min': float('nan'),
 				'max': data['current_max'].get(f'{key}_max', float('nan'))/1000.0
 			}
+			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
+			new_device_data = {**new_device_data, **check}
+
 		for key in ['power1', 'power2', 'power3']:
 			new_device_data['power'][key] = {
-				'read': data['power'].get(f'{key}_input', float('nan'))/1e6
+				'read': data['power'].get(f'{key}_input', float('nan'))/1e6,
+				'min': float('nan'),
+				'max': float('nan')
 			}
+			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
+			new_device_data = {**new_device_data, **check}
+
 		for key in ['temp1', 'temp2', 'temp3']:
 			new_device_data['temperature'][key] = {
 				'read': data['temp'].get(f'{key}_input', float('nan'))/1000.0,
+				'min': float('nan'),
 				'max': data['temp_max'].get(f'{key}_max', float('nan'))/1000.0
 			}
+			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
+			new_device_data = {**new_device_data, **check}
+
 		result["devices"][device_path] = new_device_data
 		return result
 
diff --git a/templates/sysmon.html b/templates/sysmon.html
index ff7e0c1..c42046b 100644
--- a/templates/sysmon.html
+++ b/templates/sysmon.html
@@ -167,6 +167,7 @@
       <th scope="col">Value (V)</th>
       <th scope="col">[Min, Max] (V)</th>
       <th scope="col">Description</th>
+      <th scope="col">Status</th>
     </tr>
   </thead>
   <tbody>
@@ -176,6 +177,7 @@
       <td>{{ "{:.3f}".format(value["read"]) }}</td>
       <td>{{ "[{:.3f}, {:.3f}]".format(value["min"], value["max"]) }}</td>
       <td>{{ value["desc"] }}</td>
+      <td>{{ value["status"] }}</td>
     </tr>
     {% endfor %}
 
-- 
GitLab


From 632a881b5c6790782e0d05f2d7e49087adfd4150 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Tue, 6 Aug 2024 13:41:24 +0200
Subject: [PATCH 08/23] Include raw data in json report

---
 models/sysmon.py | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/models/sysmon.py b/models/sysmon.py
index 427bd95..8079797 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -107,6 +107,7 @@ class SYSMON(SelfTestExecutor):
 		return result
 
 
+	# Check whether the measured value is within range
 	def check_measured_value(self, meas_type, name, read_value, min_value, max_value):
 		check = {"status" : "", "msg" : ""}
 		if math.isnan(read_value):
@@ -123,9 +124,11 @@ class SYSMON(SelfTestExecutor):
 		return check
 
 
+	# Test outcome
 	def self_test_check_result(self, result):
 		status_ok = True
 		msg = []
+		raw_data = {}
 		for device_type in result:
 			if 'devices' not in device_type:
 				if 'error' in result[device_type]:
@@ -138,6 +141,7 @@ class SYSMON(SelfTestExecutor):
 							continue
 						for measurement_name in result[device_type]['devices'][device_path][quantity]:
 							measurement = result[device_type]['devices'][device_path][quantity][measurement_name]
+							raw_data[measurement_name] = measurement["read"]
 							if measurement["status"] != "OK":
 								msg.append(measurement["msg"])
 								status_ok = False
@@ -147,10 +151,10 @@ class SYSMON(SelfTestExecutor):
 		else:
 			msg_level = "error"
 
-		return { "status" : msg_level, "result" : msg }
-
+		return { "status" : msg_level, "result" : msg, "data" : raw_data }
 
 
+	# Format sysmon meaurements
 	def process_versal_sysmon(self, result, class_config, device_path):
 		compiled_patterns = compile_patterns(class_config["patterns"])
 		for quantity in compiled_patterns:
@@ -196,7 +200,7 @@ class SYSMON(SelfTestExecutor):
 		return result
 
 
-	# Limits derived from hardware
+	# Format ltm meaurements
 	def process_ltm(self, result, class_config, device_path):
 		new_device_data = {'voltage':{}, 'current':{}, 'power':{}, 'temperature':{}}
 		data = result["devices"][device_path]
@@ -249,16 +253,20 @@ class SYSMON(SelfTestExecutor):
 		return result
 
 
+	# Format jc42 meaurements
 	def process_jc42(self, result, class_config):
 		value = result['devices']['/sys/class/hwmon/hwmon8']['probe']['temp1_input']/1000.0
 		name = result['devices']['/sys/class/hwmon/hwmon8']['name']
+		measurement = {"read" : value, "min" : float('nan'), "max": float('nan'), "desc" : ""}
+
 		if name in class_config['reference']['probe']:
-			min_value = class_config['reference']['probe'][name]["min"]
-			max_value = class_config['reference']['probe'][name]["max"]
-			description = class_config['reference']['probe'][name]["desc"]
+			measurement["min"] = class_config['reference']['probe'][name]["min"]
+			measurement["max"] = class_config['reference']['probe'][name]["max"]
+			measurement["desc"] = class_config['reference']['probe'][name]["desc"]
 			measurement = {"read" : value, "min" : min_value, "max": max_value, "desc" : description}
-		else:
-			measurement = {"read" : value, "min" : float('nan'), "max": float('nan'), "desc" : ""}
+
+		check = check_measured_value('temperature', name, value, measurement["min"], measurement["max"])
+		measurement = {**measurement, **check}
 
 		result['devices']['/sys/class/hwmon/hwmon8'] = {}
 		result['devices']['/sys/class/hwmon/hwmon8'][name] = measurement
-- 
GitLab


From b062053d0c6df6442294b8fe5f934ba9729673ae Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Tue, 6 Aug 2024 14:17:36 +0200
Subject: [PATCH 09/23] remove useless raw_data result field

---
 models/sysmon.py      | 4 +---
 templates/sysmon.html | 2 ++
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/models/sysmon.py b/models/sysmon.py
index 8079797..0139335 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -128,7 +128,6 @@ class SYSMON(SelfTestExecutor):
 	def self_test_check_result(self, result):
 		status_ok = True
 		msg = []
-		raw_data = {}
 		for device_type in result:
 			if 'devices' not in device_type:
 				if 'error' in result[device_type]:
@@ -141,7 +140,6 @@ class SYSMON(SelfTestExecutor):
 							continue
 						for measurement_name in result[device_type]['devices'][device_path][quantity]:
 							measurement = result[device_type]['devices'][device_path][quantity][measurement_name]
-							raw_data[measurement_name] = measurement["read"]
 							if measurement["status"] != "OK":
 								msg.append(measurement["msg"])
 								status_ok = False
@@ -151,7 +149,7 @@ class SYSMON(SelfTestExecutor):
 		else:
 			msg_level = "error"
 
-		return { "status" : msg_level, "result" : msg, "data" : raw_data }
+		return { "status" : msg_level, "result" : msg}
 
 
 	# Format sysmon meaurements
diff --git a/templates/sysmon.html b/templates/sysmon.html
index c42046b..34a100d 100644
--- a/templates/sysmon.html
+++ b/templates/sysmon.html
@@ -140,6 +140,7 @@
       <th scope="col">Value (deg C)</th>
       <th scope="col">[Min, Max] (deg C)</th>
       <th scope="col">Description</th>
+      <th scope="col">Status</th>
     </tr>
   </thead>
   <tbody>
@@ -150,6 +151,7 @@
       <td>{{ "{:.1f}".format(value["read"]) }}</td>
       <td>{{ "[{:.1F}, {:.1F}]".format(value["min"], value["max"])}}</td>
       <td>{{ value["desc"] }}</td>
+      <td>{{ value["status"] }}</td>
     </tr>
     {% endfor %}
 
-- 
GitLab


From d67352487770aa0e0dbe6ec31dd4c689b4f764e5 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Wed, 7 Aug 2024 14:23:54 +0200
Subject: [PATCH 10/23] extract and show DNA

---
 boards/flx182.py    |  1 +
 models/board_id.py  | 52 ++++++++++++++++++++++++++++++++-------------
 templates/base.html |  2 +-
 3 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index 20367f0..b10b447 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -23,6 +23,7 @@ config = {
 		"interface" : "zynq_mpsoc",
 		"board" : "FLX-182",
 		"revision" : "3",
+		"DNAaddress" : [0xF1250024, 0xF1250020],
 		"self_test" : {
 			"description" : "Identify the board",
 		}
diff --git a/models/board_id.py b/models/board_id.py
index abe68ca..a139c2d 100644
--- a/models/board_id.py
+++ b/models/board_id.py
@@ -1,5 +1,5 @@
-import pdb, re
-from os import path
+import pdb, re, mmap, os
+from os import path, sysconf
 from os.path import isdir
 from util.iio import load_file
 from models.self_test_executor import SelfTestExecutor, field_present
@@ -12,6 +12,7 @@ class BoardID(SelfTestExecutor):
         self.config = config
         self.idcode = None
 
+
     def get_id(self):
         if not "interface" in self.config:
             return {"error" : "You must specify interface type for retrieving the IDCODE!" }
@@ -19,9 +20,6 @@ class BoardID(SelfTestExecutor):
         if self.config['interface'] == 'zynq_mpsoc':
             return self.get_id_zynq_mpsoc()            
 
-        #TODO: add interface that reads a specific memory location for IDCODE
-        # to support Zynq7000 using periphery library
-
         else:
             return {"error" : "Unsupported interface for retrieving the IDCODE" }
 
@@ -48,29 +46,52 @@ class BoardID(SelfTestExecutor):
             return {"error" : f"Cannot extract IDCODE from string '{pm_response}'"}
         
         idcode = int(match.group('idcode'), 16)
-        return {"idcode" : idcode}
+        return hex(idcode)
+
+
+    def devmem(self, target_adr, length=4):
+        page_size= os.sysconf("SC_PAGE_SIZE")
+        reg_base = int(target_adr // page_size) * page_size
+        seek_size= int(target_adr %  page_size)
+        map_size = seek_size+length
+        fd = os.open("/dev/mem", os.O_RDWR|os.O_SYNC)
+        mem = mmap.mmap(fd, map_size, mmap.MAP_SHARED, mmap.PROT_READ|mmap.PROT_WRITE, offset=reg_base)
+        mem.seek(seek_size, os.SEEK_SET)
+        value = mem.read(length)
+        os.close(fd)
+        return value
+
+
+    def get_dna(self):
+        if not "DNAaddress" in self.config:
+            return {"error" : "You must specify a memory address to retrieve the FPGA DNA!" }
+        else:
+            dna_msb = int.from_bytes(self.devmem(self.config['DNAaddress'][0]), "little")
+            dna_lsb = int.from_bytes(self.devmem(self.config['DNAaddress'][1]), "little")
+            dna = ( dna_msb << 32 ) |  dna_lsb
+            return hex(dna) 
 
 
     def get_board_name(self):
         board = self.config['board']
         revision = self.config['revision']
-        idcode = self.get_id()["idcode"]
-        return f"{board}_rev_{revision}_{idcode:X}"
+        idcode = self.get_id()
+        dna = self.get_dna()
+        return f"{board}_rev_{revision}_id_{idcode}_dna_{dna}"
 
 
     def get_board_info(self):
-        result = self.get_id()
-        for key, value in self.config.items():
-            result[key] = value
+        result = {"board" : self.config['board'], "revision" : self.config['revision'], "idcode" : self.get_id(), "dna" : self.get_dna()}
         return result
 
 
     def self_test_main(self, **kwargs):
-        self.get_board_info()
+        return self.get_board_info()
 
 
     def self_test_check_result(self, result):
-        if result is None or not result:
+
+        if result is None:
             return { "status" : "error", "result" : "Test results are missing" }
 
         if not field_present(result, "idcode"):
@@ -82,8 +103,9 @@ class BoardID(SelfTestExecutor):
         if not field_present(result, "revision"):
             return { "status" : "error", "result" : "Board revision is not specified" }
 
-        return { "status" : "success", "result" :
-            f"{result['board']} rev. {result['revision']}; IDCODE=0x{result['idcode']:X}" }
+        if not field_present(result, "dna"):
+            return { "status" : "error", "result" : "DNA is not specified" }
 
+        return { "status" : "success", "result" : "OK" }
 
 
diff --git a/templates/base.html b/templates/base.html
index 4d066e3..d346a62 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -19,7 +19,7 @@
         {% if "error" in board_id %}
           <span class="badge rounded-pill bg-danger">IDCODE READOUT ERROR</span>
         {% else %}
-          <span class="badge rounded-pill bg-primary">{{ "{:X}".format(board_id['idcode']) }}</span>
+          <span class="badge rounded-pill bg-primary">{{ "Rev {} | ID {} | DNA {}".format(board_id["revision"], board_id["idcode"], board_id["dna"]) }}</span>
         {% endif %}
       {% endif %}
     </h1></div>
-- 
GitLab


From bc7c48465bdd9f7dcf50934d8186267faf543b85 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Wed, 7 Aug 2024 17:06:07 +0200
Subject: [PATCH 11/23] HWMON test ok

---
 boards/flx182.py      |  2 +-
 models/sysmon.py      | 53 ++++++++++++++++++++-----------------------
 templates/sysmon.html | 10 +++++---
 3 files changed, 32 insertions(+), 33 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index b10b447..56fb66c 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -179,7 +179,7 @@ config = {
 					}
 				}
 			},
-
+#
 			"jc42" : {
 				"device_path" : "/sys/class/hwmon/",
 				"title" : "JC-42.4",
diff --git a/models/sysmon.py b/models/sysmon.py
index 0139335..2ea9995 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -43,7 +43,6 @@ class SYSMON(SelfTestExecutor):
 	def __init__(self, config):
 		self.config = config
 
-
 	# Check if device is available
 	def device_exists(self, device_class):
 		if not device_class in self.config["device_classes"]:
@@ -115,10 +114,10 @@ class SYSMON(SelfTestExecutor):
 			check["msg"] = "{0} {1} is NaN".format(meas_type, name)
 		elif (not math.isnan(max_value) and read_value > max_value) :
 			check["status"] = "HIGH"
-			check["msg"] = "{0} {1} is {2} above max {3}".format(meas_type, name, read_value, max_value)
+			check["msg"] = "{0} {1} is {2} > max {3}".format(meas_type, name, read_value, max_value)
 		elif (not math.isnan(min_value) and read_value < min_value):
 			check["status"] = "LOW"
-			check["msg"] = "{0} {1} is {2} below min {3}".format(meas_type, name, read_value, min_value)
+			check["msg"] = "{0} {1} is {2} < min {3}".format(meas_type, name, read_value, min_value)
 		else:
 			check["status"] = "OK"
 		return check
@@ -129,7 +128,7 @@ class SYSMON(SelfTestExecutor):
 		status_ok = True
 		msg = []
 		for device_type in result:
-			if 'devices' not in device_type:
+			if 'devices' not in result[device_type]:
 				if 'error' in result[device_type]:
 					msg.append(result[device_type]["error"])
 					status_ok = False
@@ -140,11 +139,13 @@ class SYSMON(SelfTestExecutor):
 							continue
 						for measurement_name in result[device_type]['devices'][device_path][quantity]:
 							measurement = result[device_type]['devices'][device_path][quantity][measurement_name]
+							if "status" not in measurement:
+								continue
 							if measurement["status"] != "OK":
 								msg.append(measurement["msg"])
 								status_ok = False
 		if status_ok:
-			msg = ["OK"]
+			msg = "OK"
 			msg_level = "success"
 		else:
 			msg_level = "error"
@@ -177,7 +178,7 @@ class SYSMON(SelfTestExecutor):
 	# Data has to be reformatted to have the same format as versal-sysmon's
 	def process_tmp(self, result, class_config):
 		compiled_patterns = compile_patterns(class_config["patterns"])
-		new_devices = {'/sys/class/hwmon/hwmonX' : {}}
+		new_devices = {'/sys/class/hwmon/hwmonX' : {'temperature' : {} }}
 		for device_path, device_data in result["devices"].items():
 			probe_name = device_data['name']
 			local_value = device_data['probe']['temp1_input']/1000.0
@@ -191,9 +192,9 @@ class SYSMON(SelfTestExecutor):
 				measurement["desc"] = class_config["reference"]["probe"][probe_name]["desc"]
 
 			# Check value
-			check = self.check_measured_value(quantity, measurement_name, value, new_value["min"], new_value["max"])
+			check = self.check_measured_value('temperature', probe_name, probe_value, measurement["min"], measurement["max"])
 			measurement = {**measurement, **check}
-			new_devices['/sys/class/hwmon/hwmonX'][probe_name] = measurement
+			new_devices['/sys/class/hwmon/hwmonX']['temperature'][probe_name] = measurement
 		result['devices'] = new_devices
 		return result
 
@@ -208,17 +209,9 @@ class SYSMON(SelfTestExecutor):
 				'min': data['voltage_min'].get(f'{key}_min', float('nan'))/1000.0,
 				'max': data['voltage_max'].get(f'{key}_max', float('nan'))/1000.0
 			}
-			check = check_measured_value('voltage', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
-			new_device_data = {**new_device_data, **check}
-
-		for key in ['curr1', 'curr2', 'curr3']:
-			new_device_data['current'][key] = {
-				'read': data['current'].get(f'{key}_input', float('nan'))/1000.0,
-				'min': float('nan'),
-				'max': data['current_max'].get(f'{key}_max', float('nan'))
-			}
-			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
-			new_device_data = {**new_device_data, **check}
+			d = new_device_data['voltage'][key]
+			check = self.check_measured_value('voltage', key, d["read"], d["min"], d["max"])
+			new_device_data['voltage'][key] = {**new_device_data['voltage'][key], **check}
 
 		for key in ['curr1', 'curr2', 'curr3']:
 			new_device_data['current'][key] = {
@@ -226,8 +219,9 @@ class SYSMON(SelfTestExecutor):
 				'min': float('nan'),
 				'max': data['current_max'].get(f'{key}_max', float('nan'))/1000.0
 			}
-			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
-			new_device_data = {**new_device_data, **check}
+			d = new_device_data['current'][key]
+			check = self.check_measured_value('current', key, d["read"], d["min"], d["max"])
+			new_device_data['current'][key] = {**new_device_data['current'][key], **check}
 
 		for key in ['power1', 'power2', 'power3']:
 			new_device_data['power'][key] = {
@@ -235,8 +229,9 @@ class SYSMON(SelfTestExecutor):
 				'min': float('nan'),
 				'max': float('nan')
 			}
-			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
-			new_device_data = {**new_device_data, **check}
+			d = new_device_data['power'][key]
+			check = self.check_measured_value('power', key, d["read"], d["min"], d["max"])
+			new_device_data['power'][key] = {**new_device_data['power'][key], **check}
 
 		for key in ['temp1', 'temp2', 'temp3']:
 			new_device_data['temperature'][key] = {
@@ -244,8 +239,9 @@ class SYSMON(SelfTestExecutor):
 				'min': float('nan'),
 				'max': data['temp_max'].get(f'{key}_max', float('nan'))/1000.0
 			}
-			check = check_measured_value('current', key, new_device_data["read"], new_device_data["min"], new_device_data["max"])
-			new_device_data = {**new_device_data, **check}
+			d = new_device_data['temperature'][key]
+			check = self.check_measured_value('temperature', key, d["read"], d["min"], d["max"])
+			new_device_data['temperature'][key] = {**new_device_data['temperature'][key], **check}
 
 		result["devices"][device_path] = new_device_data
 		return result
@@ -261,11 +257,10 @@ class SYSMON(SelfTestExecutor):
 			measurement["min"] = class_config['reference']['probe'][name]["min"]
 			measurement["max"] = class_config['reference']['probe'][name]["max"]
 			measurement["desc"] = class_config['reference']['probe'][name]["desc"]
-			measurement = {"read" : value, "min" : min_value, "max": max_value, "desc" : description}
 
-		check = check_measured_value('temperature', name, value, measurement["min"], measurement["max"])
+		check = self.check_measured_value('temperature', name, value, measurement["min"], measurement["max"])
 		measurement = {**measurement, **check}
 
-		result['devices']['/sys/class/hwmon/hwmon8'] = {}
-		result['devices']['/sys/class/hwmon/hwmon8'][name] = measurement
+		result['devices']['/sys/class/hwmon/hwmon8'] = {"temperature" : {}}
+		result['devices']['/sys/class/hwmon/hwmon8']["temperature"][name] = measurement
 		return result
\ No newline at end of file
diff --git a/templates/sysmon.html b/templates/sysmon.html
index 34a100d..5e0aa38 100644
--- a/templates/sysmon.html
+++ b/templates/sysmon.html
@@ -11,19 +11,21 @@
       <th scope="col">Sensor</th>
       <th scope="col">Local temp [deg C]</th>
       <th scope="col">Probe temp [deg C]</th>
-      <th scope="col">Max [deg C]</th>
+      <th scope="col">Probe max [deg C]</th>
+      <th scope="col">Status</th>
       <th scope="col">Description</th>
     </tr>
   </thead>
   <tbody>
 
   {% for device_path, device_data in data["devices"].items() | sort %}
-  {% for name, value in device_data.items() | sort %}
+  {% for name, value in device_data["temperature"].items() | sort %}
   <tr>
     <th scope="row">{{name}}</th>
     <td>{{ "{:.1f}".format(value["local"]) }}</td>
     <td>{{ "{:.1f}".format(value["probe"]) }}</td>
     <td>{{ "{:.1f}".format(value["max"])}}</td>
+    <td>{{ value["status"]}}</td>
     <td>{{ value["desc"] }}</td>
   </tr>
   {% endfor %}
@@ -105,17 +107,19 @@
       <th scope="col">Device</th>
       <th scope="col">Temperature [deg C]</th>
       <th scope="col">Max Temp [deg C]</th>
+      <th scope="col">Status</th>
     </tr>
   </thead>
   <tbody>
 
   {% for device_path, device_data in data["devices"].items() | sort %}
-  {% for key, meas in device_data.items() | sort %}
+  {% for key, meas in device_data["temperature"].items() | sort %}
 
   <tr>
     <th scope="row">{{key}}</th>
     <td>{{ "{:.3f}".format(meas["read"]) }} </td>
     <td>{{ "{:.3f}".format(meas["max"]) }} </td>
+    <td>{{ meas["status"] }} </td>
   </tr>
   {% endfor %}
   {% endfor %}
-- 
GitLab


From c279e6fe2e8b5a89a43171ca82719be01ea84735 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Wed, 7 Aug 2024 18:02:58 +0200
Subject: [PATCH 12/23] INA226 test

---
 boards/flx182.py      | 36 +++++++++++++++++++--------
 models/board_id.py    |  2 +-
 models/ina226.py      | 58 +++++++++++++++++++++++++++++++++++++------
 models/sysmon.py      |  6 ++---
 templates/ina226.html |  4 +++
 5 files changed, 84 insertions(+), 22 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index 56fb66c..525be38 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -43,16 +43,30 @@ config = {
 		},
 		#tolerance and max current set by hand
 		"reference" : {
-			"12P0V" :   {"V" : 12.00, "V_min" : 11.0,  "V_max" : 23.0,  "I" : 1,    "I_max" : 8, "desc" : "+/- 1V expected from power supply"},
-			"MGTAVCC" : {"V" : 0.88 , "V_min" : 0.854, "V_max" : 0.906, "I" : 1.5 , "I_max" : 3.5, "desc" : "V limits from FPGA recommended ranges"},
-			"MGTAVTT" : {"V" : 1.20 , "V_min" : 1.164, "V_max" : 1.236, "I" : 3.4 , "I_max" : 6.5, "desc" : "V limits from FPGA recommended ranges"},
-			"SYS12" :   {"V" : 1.20 , "V_min" : 1.164, "V_max" : 1.236, "I" : 0.8 , "I_max" : 1.5, "desc" : "V limits from FPGA recommended ranges"},
-			"SYS15" :   {"V" : 1.50 , "V_min" : 1.455, "V_max" : 1.545, "I" : 0.9 , "I_max" : 1.5, "desc" : "V limits from FPGA recommended ranges"},
-			"SYS18" :   {"V" : 1.80 , "V_min" : 1.710, "V_max" : 1.890, "I" : 0.8 , "I_max" : 1.5, "desc" : "V limits from 25G FF"},
-			"SYS25" :   {"V" : 2.50 , "V_min" : 2.15,  "V_max" : 2.85,  "I" : 0.3 , "I_max" : 0.5, "desc" : ""},
-			"SYS33" :   {"V" : 3.30 , "V_min" : 3.15,  "V_max" : 3.45,  "I" : 3.30 , "I_max" : 6.0, "desc" : "V limits from 14/16G FF"},
-			"SYS38" :   {"V" : 3.80 , "V_min" : 3.50,  "V_max" : 4.10,  "I" : 0.0 , "I_max" : 5, "desc" : ""},
-			"VCCINT" :  {"V" : 0.80 , "V_min" : 0.775, "V_max" : 0.825, "I" : 20.0 , "I_max" : 50.0,"desc" : ""}
+			"voltage" : {
+				"12P0V" :   {"nominal" : 12.00, "min" : 11.0,  "max" : 23.0,  "desc" : "+/- 1V expected from power supply"},
+				"MGTAVCC" : {"nominal" : 0.88 , "min" : 0.854, "max" : 0.906, "desc" : "V limits from FPGA recommended ranges"},
+				"MGTAVTT" : {"nominal" : 1.20 , "min" : 1.164, "max" : 1.236, "desc" : "V limits from FPGA recommended ranges"},
+				"SYS12" :   {"nominal" : 1.20 , "min" : 1.164, "max" : 1.236, "desc" : "V limits from FPGA recommended ranges"},
+				"SYS15" :   {"nominal" : 1.50 , "min" : 1.455, "max" : 1.545, "desc" : "V limits from FPGA recommended ranges"},
+				"SYS18" :   {"nominal" : 1.80 , "min" : 1.710, "max" : 1.890, "desc" : "V limits from 25G FF"},
+				"SYS25" :   {"nominal" : 2.50 , "min" : 2.15,  "max" : 2.85,  "desc" : ""},
+				"SYS33" :   {"nominal" : 3.30 , "min" : 3.15,  "max" : 3.45,  "desc" : "V limits from 14/16G FF"},
+				"SYS38" :   {"nominal" : 3.80 , "min" : 3.50,  "max" : 4.10,  "desc" : ""},
+				"VCCINT" :  {"nominal" : 0.80 , "min" : 0.775, "max" : 0.825, "desc" : ""}
+			},
+			"current" : {
+				"12P0V" :   {"nominal" : 1,     "max" : 8,    "desc" : ""},
+				"MGTAVCC" : {"nominal" : 1.5 ,  "max" : 3.5,  "desc" : ""},
+				"MGTAVTT" : {"nominal" : 3.4 ,  "max" : 6.5,  "desc" : ""},
+				"SYS12" :   {"nominal" : 0.8 ,  "max" : 1.5,  "desc" : ""},
+				"SYS15" :   {"nominal" : 0.9 ,  "max" : 1.5,  "desc" : ""},
+				"SYS18" :   {"nominal" : 0.8 ,  "max" : 1.5,  "desc" : ""},
+				"SYS25" :   {"nominal" : 0.3 ,  "max" : 0.5,  "desc" : ""},
+				"SYS33" :   {"nominal" : 3.30 , "max" : 6.0,  "desc" : ""},
+				"SYS38" :   {"nominal" : 0.0 ,  "max" : 5,    "desc" : ""},
+				"VCCINT" :  {"nominal" : 20.0 , "max" : 50.0, "desc" : ""}
+			}
 		}
 	},
 
@@ -179,7 +193,7 @@ config = {
 					}
 				}
 			},
-#
+
 			"jc42" : {
 				"device_path" : "/sys/class/hwmon/",
 				"title" : "JC-42.4",
diff --git a/models/board_id.py b/models/board_id.py
index a139c2d..2b13549 100644
--- a/models/board_id.py
+++ b/models/board_id.py
@@ -106,6 +106,6 @@ class BoardID(SelfTestExecutor):
         if not field_present(result, "dna"):
             return { "status" : "error", "result" : "DNA is not specified" }
 
-        return { "status" : "success", "result" : "OK" }
+        return { "status" : "success", "result" : result["dna"] }
 
 
diff --git a/models/ina226.py b/models/ina226.py
index 89b53b7..e2332da 100644
--- a/models/ina226.py
+++ b/models/ina226.py
@@ -1,4 +1,5 @@
 import pdb
+import math
 from os import path
 from os.path import isdir
 from util.iio import load_file, get_devices
@@ -14,6 +15,22 @@ class INA226(SelfTestExecutor):
 		self.devices = get_devices(self.device_path, self.name)
 
 
+	def check_measured_value(self, meas_type, name, read_value, min_value, max_value):
+		check = {"status" : "", "msg" : ""}
+		if math.isnan(read_value):
+			check["status"] = "BAD"
+			check["msg"] = "{0} {1} NaN".format(meas_type, name)
+		elif (not math.isnan(max_value) and read_value > max_value) :
+			check["status"] = "HIGH"
+			check["msg"] = "{0} {1} {2} > max {3}".format(meas_type, name, read_value, max_value)
+		elif (not math.isnan(min_value) and read_value < min_value):
+			check["status"] = "LOW"
+			check["msg"] = "{0} {1} {2} < min {3}".format(meas_type, name, read_value, min_value)
+		else:
+			check["status"] = "OK"
+		return check
+
+
 	# returns hashmap of all readings available for this device
 	def get_data(self, device):
 		if not isdir(device):
@@ -41,19 +58,24 @@ class INA226(SelfTestExecutor):
 		V_tol = -1
 		I_max = -1
 
-		if label in self.config["reference"]:
-			V_min = self.config["reference"][label]["V_min"]
-			V_max = self.config["reference"][label]["V_max"]
-			I_max = self.config["reference"][label]["I_max"]
+		if label in self.config["reference"]["voltage"]:
+			V_min = self.config["reference"]["voltage"][label]["min"]
+			V_max = self.config["reference"]["voltage"][label]["max"]
+
+		if label in self.config["reference"]["current"]:
+			I_max = self.config["reference"]["current"][label]["max"]
 
 		data = {
 			"V" : voltage,
 			"V_min" : V_min,
 			"V_max" : V_max,
+			"V_status" : self.check_measured_value('voltage', label, voltage, V_min, V_max)["status"],
+			"V_msg" : self.check_measured_value('voltage', label, voltage, V_min, V_max)["msg"],
 			"I" : current,
 			"I_max" : I_max,
-			"P" : power,
-			"R_shunt" : r_shunt / 1000.0
+			"I_status" : self.check_measured_value('current', label, current, float('nan'), I_max)["status"],
+			"I_msg" : self.check_measured_value('current', label, current, float('nan'), I_max)["msg"],
+			"P" : power
 		}
 
 		return data, label
@@ -62,7 +84,6 @@ class INA226(SelfTestExecutor):
 	# returns data for all devices
 	def read_all(self):
 		result = {}
-
 		for device in self.devices:
 			device_data = self.get_data(device)
 			if device_data:
@@ -70,3 +91,26 @@ class INA226(SelfTestExecutor):
 				result[label] = data
 
 		return result
+
+	def self_test_main(self, **kwargs):
+		return self.read_all()
+
+
+	def self_test_check_result(self, result):
+		status_ok = True
+		msg = []
+		for entry in result:
+			if result[entry]["V_status"] != "OK":
+				status_ok = False
+				msg.append(result[entry]["V_msg"])
+			if result[entry]["I_status"] != "OK":
+				status_ok = False
+				msg.append(result[entry]["I_msg"])
+
+		if status_ok:
+			msg = "OK"
+			msg_level = "success"
+		else:
+			msg_level = "error"
+
+		return { "status" : msg_level, "result" : msg}
\ No newline at end of file
diff --git a/models/sysmon.py b/models/sysmon.py
index 2ea9995..32c43fe 100644
--- a/models/sysmon.py
+++ b/models/sysmon.py
@@ -111,13 +111,13 @@ class SYSMON(SelfTestExecutor):
 		check = {"status" : "", "msg" : ""}
 		if math.isnan(read_value):
 			check["status"] = "BAD"
-			check["msg"] = "{0} {1} is NaN".format(meas_type, name)
+			check["msg"] = "{0} {1} NaN".format(meas_type, name)
 		elif (not math.isnan(max_value) and read_value > max_value) :
 			check["status"] = "HIGH"
-			check["msg"] = "{0} {1} is {2} > max {3}".format(meas_type, name, read_value, max_value)
+			check["msg"] = "{0} {1} {2} > max {3}".format(meas_type, name, read_value, max_value)
 		elif (not math.isnan(min_value) and read_value < min_value):
 			check["status"] = "LOW"
-			check["msg"] = "{0} {1} is {2} < min {3}".format(meas_type, name, read_value, min_value)
+			check["msg"] = "{0} {1} {2} < min {3}".format(meas_type, name, read_value, min_value)
 		else:
 			check["status"] = "OK"
 		return check
diff --git a/templates/ina226.html b/templates/ina226.html
index 4ef15ca..c2d3998 100644
--- a/templates/ina226.html
+++ b/templates/ina226.html
@@ -10,8 +10,10 @@
       <th scope="col">Name</th>
       <th scope="col">Voltage (V)</th>
       <th scope="col">V range (V)</th>
+      <th scope="col">V status</th>
       <th scope="col">I (A)</th>
       <th scope="col">Max I (A)</th>
+      <th scope="col">I status</th>
       <th scope="col">P [W]</th>
     </tr>
   </thead>
@@ -22,8 +24,10 @@
         <th scope="row">{{ label }}</th>
         <td>{{ "{:.3f}".format(device["V"]) }}</td>
         <td>{{ "[{:.3f}, {:.3f}]".format(device["V_min"], device["V_max"]) }}</td>
+        <td>{{ device["V_status"] }}</td>
         <td>{{ "{:.3f}".format(device["I"]) }}</td>
         <td>{{ "{:.3f}".format(device["I_max"]) }}</td>
+        <td>{{ device["I_status"] }}</td>
         <td>{{ "{:.3f}".format(device["P"]) }}</td>
     </tr>
     {% endfor %}
-- 
GitLab


From fbdfb0d5929e1ae517f131b98ad42029eba49c55 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Thu, 8 Aug 2024 15:41:13 +0200
Subject: [PATCH 13/23] Tests for SI570 and FFLY

---
 boards/flx182.py |  6 ++++++
 models/sfp.py    | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
 models/si570.py  | 26 ++++++++++++++++++++++++--
 3 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index 525be38..e0e03c7 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -379,6 +379,7 @@ config = {
 				"i2c_bus" : "/dev/i2c-8",
 				"i2c_addr" : 0x50,
 				"type" : "CERN-B-* Transmitter",
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 6,
@@ -389,6 +390,7 @@ config = {
 				"type" : "CERN-B-* Receiver",
 				"i2c_bus" : "/dev/i2c-8",
 				"i2c_addr" : 0x54,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 2,
@@ -399,6 +401,7 @@ config = {
 				"type" : "CERN-B-* Transmitter",
 				"i2c_bus" : "/dev/i2c-9",
 				"i2c_addr" : 0x50,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 14,
@@ -409,6 +412,7 @@ config = {
 				"type" : "CERN-B-* Receiver",
 				"i2c_bus" : "/dev/i2c-9",
 				"i2c_addr" : 0x54,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 10,
@@ -416,8 +420,10 @@ config = {
 				# }
 			},
 			"FireFly_J29" : {
+				"type" : "B04 Transceiver",
 				"i2c_bus" : "/dev/i2c-10",
 				"i2c_addr" : 0x50,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 18,
diff --git a/models/sfp.py b/models/sfp.py
index f75bdc5..622d755 100644
--- a/models/sfp.py
+++ b/models/sfp.py
@@ -13,7 +13,6 @@ class SFP(SelfTestExecutor):
     # returns configuration of all known SI570 devices
     def read_all(self):            
         data = {}
-
         try:
             for name, config in self.devices.items():
                 device = SFP_Device(config)
@@ -22,3 +21,49 @@ class SFP(SelfTestExecutor):
             return {"error" : "Can't read SFP devices: {}".format(err)}       
 
         return data
+
+
+    def self_test_main(self, **kwargs):
+        return self.read_all()
+
+
+    def self_test_check_result(self, result):
+        msg_level = "success"
+        msg = []
+
+        if len(result.keys()) is not 5:
+            msg_level = "error"
+
+        for label in self.config["mapping"].keys():
+
+            # Missing FF
+            if label not in result.keys():
+                msg_level = "error"
+                msg.append("{} not detected".format(label))
+                continue
+
+            # Wrong FF type
+            reference = self.config["mapping"][label]
+            reading = result[label]
+
+            expected_type = reference['type']
+            expected_y12 = True if "CERN" in expected_type else False
+            read_part_number = reading['part_number']
+            if expected_y12 and "Y12" not in read_part_number:
+                msg_level = "error"
+                msg.append("{} is not a Y12 module. (P/N: {})".format(label, read_part_number))
+            if not expected_y12 and "B04" not in read_part_number:
+                msg_level = "error"
+                msg.append("{} is not a B04 module (P/N: {})".format(label, read_part_number))
+
+            # Temperature
+            ref_value = reference['max_temp']
+            read_value = reading['temperature']
+            if read_value > ref_value:
+                msg_level = "error"
+                msg.append("{} temp {} > {}".format(label, read_value, ref_value))
+
+        if msg_level == "success":
+            msg = "OK"
+
+        return { "status" : msg_level, "result" : msg}
diff --git a/models/si570.py b/models/si570.py
index f86824f..59982da 100644
--- a/models/si570.py
+++ b/models/si570.py
@@ -2,7 +2,7 @@ import pdb
 from functools import wraps
 
 from util.si570 import read_device, set_frequency, reset, load_nvm
-from util.si570 import fmin_MHz, fmax_MHz, compute_fxtal, fxtal_MHz_nominal
+from util.si570 import fmin_MHz, fmax_MHz, compute_fxtal, fxtal_MHz_nominal, read_default_frequency
 
 from util.i2c import I2CError
 from threading import Lock
@@ -159,7 +159,29 @@ class SI570(SelfTestExecutor):
         return result
 
 
+    def compare_clocks(self, name, fout, fref, ppm):
+        if abs(fout - fref) > 2*ppm*1e-6*fref:
+            return "Clock {} at {:.6f} MHz instead of {:.6f}".format(name, fout, fref)
+        else:
+            return "OK"
+
+
     def self_test_check_result(self, result):
-        return self.self_test_check_device_list(result)
+        device_check = self.self_test_check_device_list(result)
+        device_check['result'] = [device_check['result']]
+        for device in result:
+            reading = result[device]
+            if "error" in reading:
+                continue
+            else:
+                fout = reading['FOUT']
+                fref = self.config["mapping"][device]["freq_MHz"]
+                ppm  = float(self.config["mapping"][device]["stability"].strip('ppm'))
+                check = "OK" #self.compare_clocks(device, fout, fref, ppm)
+                if check != "OK":
+                    device_check['status'] = "error"
+                    device_check['result'].append(check)
+
+        return { "status" : device_check['status'], "result" : device_check['result']}
         
 
-- 
GitLab


From acc44535590a081873c893f37012a93be46a0779 Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Thu, 8 Aug 2024 19:27:33 +0000
Subject: [PATCH 14/23] Add FLX-182B and ADM1266

---
 boards/flx182.py       |    4 +-
 boards/flx182b.py      | 1279 ++++++++++++++++++++++++++++++++++++++++
 controllers/adm1266.py |   25 +
 models/adm1266.py      |   44 ++
 routes/__init__.py     |    9 +
 templates/adm1266.html |   69 +++
 templates/base.html    |    4 +
 util/adm1266.py        |   92 +++
 8 files changed, 1524 insertions(+), 2 deletions(-)
 create mode 100644 boards/flx182b.py
 create mode 100644 controllers/adm1266.py
 create mode 100644 models/adm1266.py
 create mode 100644 templates/adm1266.html
 create mode 100644 util/adm1266.py

diff --git a/boards/flx182.py b/boards/flx182.py
index fb3b38a..ffb1bdb 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -1124,7 +1124,7 @@ config = {
 			},
 
 			"ff2_pma_near_loopback" : {
-				"comment" : "FireFly 2 (J27, J27) Near-End PMA Loopback",
+				"comment" : "FireFly 2 (J27, J28) Near-End PMA Loopback",
 				"links" : [ "FF2_0", "FF2_1", "FF2_2", "FF2_3", "FF2_4",  "FF2_5", "FF2_6", "FF2_7",
 					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
 
@@ -1147,7 +1147,7 @@ config = {
 			},
 			
 			"ff2_fiber_loopback" : {
-				"comment" : "FireFly 2 (J27, J27) Physical Loopback",
+				"comment" : "FireFly 2 (J27, J28) Physical Loopback",
 				"links" : [ "FF2_0", "FF2_1", "FF2_2", "FF2_3", "FF2_4",  "FF2_5", "FF2_6", "FF2_7",
 					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
 
diff --git a/boards/flx182b.py b/boards/flx182b.py
new file mode 100644
index 0000000..b0df411
--- /dev/null
+++ b/boards/flx182b.py
@@ -0,0 +1,1279 @@
+__author__ = "Elena Zhivun"
+__version__ = "0.0.1"
+__maintainer__ = "Elena Zhivun"
+__email__ = "elena.zhivun@cern.ch"
+
+
+# This frequency will be assumed for the reference clocks on IN1 of SI5345_A
+# and SI5345_B in case if the alternative part is used that doesn't have I2C 
+# communications
+default_si570_frequency = 40.08
+
+# Possible alternative frequencies for SI570 chips at IN1 of SI5345_A and SI5345_B
+alt_si570_frequencies = [200.0]
+
+# Change the settings below to match your platform
+
+config = {
+	"global" : {
+		"title" : "FLX182 system monitor"
+	},
+
+	"board_id" : {
+		"interface" : "zynq_mpsoc",
+		"board" : "FLX-182",
+		"revision" : "3_1b",
+		"self_test" : {
+			"description" : "Identify the board",
+		}
+	},
+
+	"self_test" : {
+		"endpoint" : "https://bnl-board-database.web.cern.ch",
+	},
+
+	# fancy summary table for INA226
+	# TODO: merge with HWMON
+	"ina226" : {
+		"device_path" : "/sys/bus/iio/devices/",
+		"name" : "ina226",
+		"self_test" : {
+			"description" : "INA226 chip readout",
+		}
+	},
+
+	# crudely summarize IIO/HWMON devices with available driver
+	# "name" in the regular expression determines the row title
+	# TODO: this class should be handling INA226
+	"hwmon" : {	
+		"device_classes" : {
+			"xlnx,versal-sysmon" : {
+				"device_path" : "/sys/bus/iio/devices/",
+				"title" : "SYSMON",
+				"patterns" : {
+					"voltage" : "in_voltage(?P<id>\\d+)_(?P<name>.+)_input",
+					"temperature" : "in_temp(?P<id>\\d+)_(?P<name>.+)_input",
+				},
+			},
+
+			"ltm4700" : {
+				"device_path" : "/sys/class/hwmon/",
+				"title" : "LTM4700",						
+				"patterns" : {
+					"voltage" : "(?P<name>in\\d+_input)",
+					"current" : "(?P<name>curr\\d+_input)",
+					"temp" : "(?P<name>temp\\d+_input)",
+					"power" : "(?P<name>power\\d+_input)",
+				},
+			},
+
+			"tmp435" : {
+				"device_path" : "/sys/class/hwmon/",
+				"title" : "TMP435",
+				"patterns" : {
+					"probe" : "(?P<name>temp\\d+_input)",
+				},
+				"self_test" : {
+					"description" : "TMP435 readout",
+				}
+			},
+
+			"jc42" : {
+				"device_path" : "/sys/class/hwmon/",
+				"title" : "JC-42.4",
+				"patterns" : {
+					"probe" : "(?P<name>temp\\d+_input)",
+				},
+			},
+		},
+		"self_test" : {
+			"description" : "HWMON device readout (SYSMON, TMP435, etc)",
+		}
+	},
+
+
+	"si53156" : {
+		"mapping" : {
+			"U36" : {
+				"i2c_bus" : "/dev/i2c-7",
+				"i2c_addr" : 0x6b,
+				"name" : "PCIe clk buffer (U37)"
+			},
+		},
+
+		"self_test" : {
+			"description" : "Probe SI53156",
+		}
+	},
+
+
+	# displays clocks listed in Linux device tree and
+	# controlled by kernel via the Common Clock Framework
+	# "ccf_clocks" : {
+	# 	"whitelist" : [
+	# 		"si570_sys_clk_40_for_SI5345A",
+	# 		"si570_sys_clk_40_for_SI5345B",
+	# 		"si570_sys_clk_100",
+	# 		"si570_lti_ref_clk",
+	# 		"si570_100G_ref_clk",
+	# 		"si570_ref_clk"
+	# 	],
+	# 	"clk_dump_path" : "/sys/kernel/debug/clk/clk_dump"
+	# },
+
+
+	#"adm106x" : { 
+	#	"mapping" : {
+	#		"U68" : {
+	#			"i2c_bus" : "/dev/i2c-6",
+	#			"i2c_addr" : 0x34,
+	#		},
+	#		"U69" : {
+	#			"i2c_bus" : "/dev/i2c-6",
+	#			"i2c_addr" : 0x35,
+	#		},
+	#	},
+
+	#	"self_test" : {
+	#		"description" : "ADM106x chip readout",
+	#	}
+	#},
+
+	"adm1266" : { 
+		"mapping" : {
+			"U68" : {
+				"i2c_bus" : "/dev/i2c-6",
+				"i2c_addr" : 0x40,
+			},
+		},
+
+		"self_test" : {
+			"description" : "ADM1266 chip readout",
+		}
+	},
+
+	"si5345" : {
+		"mapping" : {
+			"si5345_a" : {
+				"comment" : "SI5345_A (U60)",
+				"i2c_bus" : "/dev/i2c-11",
+				"i2c_addr" : 0x68,
+				# if the device is found at one of the alternative addresses, 
+				# it's base I2C address will be updated to match the i2c_addr
+				"i2c_addr_alt" : [0x7c],
+				"f_xaxb_MHz"	: 48.0,
+				"f_in0_MHz"		: 125.0,
+				"f_in1_MHz"		: 40.08,
+				"f_in2_MHz"		: 0.0,
+				"f_in3_MHz"		: 0.0,
+				"in_sel"		: 0b00, # device in_sel pins value
+			},
+			"si5345_b" : {
+				"comment" : "SI5345_B (U59)",
+				"i2c_bus" : "/dev/i2c-11",
+				"i2c_addr" : 0x69,
+				"i2c_addr_alt" : [0x7d],
+				"f_xaxb_MHz"	: 48.0,
+				"f_in0_MHz"		: 40.08,
+				"f_in1_MHz"		: 40.08,
+				"f_in2_MHz"		: 0.0,
+				"f_in3_MHz"		: 0.0,
+				"in_sel"		: 0b00, # device in_sel pins value
+			},
+		},
+
+		"self_test" : {
+			"description" : "SI5345 readout",
+		}
+	},
+
+	# si569 might be replaced by Crystek CVPD-922
+	"si570" : {
+		# Valid stability values: 7ppm, 20ppm, 50ppm
+
+		# Protected flag forbids updating the clock, for example
+		# because the system will become unstable if it is touched
+
+		# If system or memory clock are not marked as protected
+		# the webapp will freeze the OS on startup since it 
+		# resets the clocks to calculate fxtal
+
+		"mapping" : {
+
+			"ddr4_clk" : {
+				"i2c_bus" : "/dev/i2c-6",
+				"i2c_addr" : 0x60,
+				"freq_MHz" : 200,
+				"stability" : "20ppm",
+				"protected" : True,
+			},
+
+			"sys_clk_100" : {
+				"i2c_bus" : "/dev/i2c-7",
+				"i2c_addr" : 0x55,
+				"freq_MHz" : 100,
+				"stability" : "20ppm",
+				"protected" : False,
+			},
+
+			# NOTE: alternative part may be placed
+			"SI5345A_ref_clk" : {
+				"i2c_bus" : "/dev/i2c-7",
+				"i2c_addr" : 0x60,
+				"freq_MHz" : default_si570_frequency,
+				"stability" : "20ppm",
+				"protected" : False,
+			},
+
+			# NOTE: alternative part may be placed
+			"SI5345B_ref_clk" : {
+				"i2c_bus" : "/dev/i2c-8",
+				"i2c_addr" : 0x60,
+				"freq_MHz" : default_si570_frequency,
+				"stability" : "20ppm",
+				"protected" : False,
+			},
+
+			# NOTE: alternative part may be placed
+			"lti_ref_clk" : {
+				"i2c_bus" : "/dev/i2c-9",
+				"i2c_addr" : 0x60,
+				"freq_MHz" : 240.474,
+				"stability" : "20ppm",
+				"protected" : False,
+			},
+
+			"100G_ref_clk" : {
+				"i2c_bus" : "/dev/i2c-10",
+				"i2c_addr" : 0x60,
+				"freq_MHz" : 322.265625,
+				"stability" : "20ppm",
+				"protected" : False,
+			},
+
+			"ps_ref_clk" : {
+				"i2c_bus" : "/dev/i2c-11",
+				"i2c_addr" : 0x5d,
+				"freq_MHz" : 33.333333, # schematic says 30.3333333?
+				"stability" : "20ppm",
+				"protected" : True,
+			},
+		},
+		"self_test" : {
+			"description" : "SI570 device readout",
+			"warn_on_error" : True,
+		}
+	},
+
+	"sfp" : {
+		"mapping" : {
+			"FireFly_J36" : {
+				"i2c_bus" : "/dev/i2c-8",
+				"i2c_addr" : 0x50,
+				"type" : "CERN-B-* Transmitter",
+				# "select" : {
+				# 	"gpio" : "gpiochip3",
+				# 	"line" : 6,
+				# 	"active_level" : 0
+				# }
+			},
+			"FireFly_J35" : {
+				"type" : "CERN-B-* Receiver",
+				"i2c_bus" : "/dev/i2c-8",
+				"i2c_addr" : 0x54,
+				# "select" : {
+				# 	"gpio" : "gpiochip3",
+				# 	"line" : 2,
+				# 	"active_level" : 0
+				# }
+			},
+			"FireFly_J33" : {
+				"type" : "CERN-B-* Transmitter",
+				"i2c_bus" : "/dev/i2c-9",
+				"i2c_addr" : 0x50,
+				# "select" : {
+				# 	"gpio" : "gpiochip3",
+				# 	"line" : 14,
+				# 	"active_level" : 0
+				# }
+			},
+			"FireFly_J32" : {
+				"type" : "CERN-B-* Receiver",
+				"i2c_bus" : "/dev/i2c-9",
+				"i2c_addr" : 0x54,
+				# "select" : {
+				# 	"gpio" : "gpiochip3",
+				# 	"line" : 10,
+				# 	"active_level" : 0
+				# }
+			},
+			"FireFly_J34" : {
+				"i2c_bus" : "/dev/i2c-10",
+				"i2c_addr" : 0x50,
+				# "select" : {
+				# 	"gpio" : "gpiochip3",
+				# 	"line" : 18,
+				# 	"active_level" : 0
+				# }
+			},
+		},
+		"self_test" : {
+			"description" : "SFP and FireFly module readout",
+		}
+	},
+
+	"process_runners" : {
+		"memtester" : {
+			"binary" : ["/usr/bin/memtester", "/usr/sbin/memtester"],
+			"mask_variable" : "MEMTESTER_TEST_MASK",
+			"mask_default" : "6144",
+			"name" : "memtester",
+			"title" : "DRAM test",
+			"self_test" : {
+				"description" : "DRAM module test",
+			}
+		},
+
+		"iperf3" : {
+			"binary" : ["/usr/bin/iperf3"],
+			"name" : "iperf3",
+			"title" : "Ethernet",
+			"self_test" : {
+				"description" : "Ethernet test",
+			}
+		},
+
+		"mtd" : {
+			"binary" : ["<app_root_path>/executables/mtd_test.sh"],
+			"name" : "mtd",
+			"default_device" : 4,
+			"title" : "QSPI flash",
+			"self_test" : {
+				"description" : "QSPI flash test",
+			}
+		}
+	},
+
+	"gpio" : {
+		"self_test" : {
+			"description" : "GPIO readout",
+		},
+		"mapping" : {
+			"gpiochip0" : {
+				"comment" : "Versal AXI GPIO",
+				"lines" : {
+					# GPIO_0 port in Vivado
+					0 : {
+						"label" : "LED_2",
+						"direction" : "out",
+						"value" : False
+					},
+					1 : {
+						"label" : "i2c_sw_reset_b",
+						"direction" : "out",
+						"value" : True
+					},
+					2 : {
+						"label" : "LED_1",
+						"direction" : "out",
+						"value" : False
+					},
+					3 : {
+						"label" : "LED_0",
+						"direction" : "out",
+						"value" : False
+					},
+					4 : {
+						"label" : "LED_3",
+						"direction" : "out",
+						"value" : False
+					},
+					5 : {
+						"label" : "fan_fail_b",
+						"direction" : "in"
+					},
+					6 : {
+						"label" : "fan_fullsp",
+						"direction" : "in"
+					},
+					7 : {
+						"label" : "pcie_perst_b",
+						"direction" : "in"
+					},
+					8 : {
+						"label" : "ff3_prsnt_b",
+						"direction" : "in"
+					},
+					9 : {
+						"label" : "ioexpan1_intr_b",
+						"direction" : "in",
+					},
+					10 : {
+						"label" : "fan_overtemp_b",
+						"direction" : "in"
+					},
+					11 : {
+						"label" : "fan_pwm_out",
+						"direction" : "in"
+					},
+					12 : {
+						"label" : "ioexpand2_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					13 : {
+						"label" : "ioexpand2_intr_b",
+						"direction" : "in"
+					},
+					14 : {
+						"label" : "fan_tach",
+						"direction" : "in"
+					},
+					15 : {
+						"label" : "pcie_wake_b",
+						"direction" : "out",
+						"value" : True,
+					},
+					16 : {
+						"label" : "ioexpand1_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+
+					# There is gpio-hog on these lines
+					# 17 : {
+					# 	"label" : "qspi1_rst_b (R433 DNP)",
+					# 	"direction" : "out",
+					# 	"value" : True
+					# },
+					# 18 : {
+					# 	"label" : "qspi_rst_b",
+					# 	"direction" : "out",
+					# 	"value" : True
+					# },
+					# 19 : {
+					# 	"label" : "qspi0_rst_b (R432 DNP)",
+					# 	"direction" : "out",
+					# 	"value" : True
+					# },
+
+					20 : {
+						"label" : "ARM_CPU_controls_GPIO",
+						"direction" : "out",
+						"value" : False
+					},
+
+					# GPIO_1 port in Vivado
+					21 : {
+						"label" : "si5345a_sa1",
+						"direction" : "out",
+						"value" : False
+					},
+					22 : {
+						"label" : "si5345b_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					23 : {
+						"label" : "si5345a_lol_b",
+						"direction" : "in"
+					},
+					24 : {
+						"label" : "si5345a_finc",
+						"direction" : "out",
+						"value" : False
+					},
+					25 : {
+						"label" : "si5345b_sa0",
+						"direction" : "out",
+						"value" : True
+					},
+					26 : {
+						"label" : "si5345a_fdec",
+						"direction" : "out",
+						"value" : False
+					},
+					27 : {
+						"label" : "si5345b_fdec",
+						"direction" : "out",
+						"value" : False
+					},
+					28 : {
+						"label" : "si5345b_lol_b",
+						"direction" : "in"
+					},
+					29 : {
+						"label" : "si5345a_intr_b",
+						"direction" : "in"
+					},
+					30 : {
+						"label" : "si5345a_output_enable_b",
+						"direction" : "out",
+						"value" : False
+					},
+					31 : {
+						"label" : "si5345b_sa1",
+						"direction" : "out",
+						"value" : False
+					},
+					32 : {
+						"label" : "si5345b_intr_b",
+						"direction" : "in"
+					},
+					33 : {
+						"label" : "si5345a_sa0",
+						"direction" : "out",
+						"value" : False
+					},
+					34 : {
+						"label" : "si5345b_output_enable_b",
+						"direction" : "out",
+						"value" : False
+					},
+					35 : {
+						"label" : "si5345b_insel0",
+						"direction" : "out",
+						"value" : False
+					},
+					36 : {
+						"label" : "si5345b_insel1",
+						"direction" : "out",
+						"value" : False
+					},
+					37 : {
+						"label" : "si5345a_insel0",
+						"direction" : "out",
+						"value" : False
+					},
+					38 : {
+						"label" : "si5345a_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					39 : {
+						"label" : "si5345b_finc",
+						"direction" : "out",
+						"value" : False
+					},
+					40 : {
+						"label" : "si5345a_insel1",
+						"direction" : "out",
+						"value" : False
+					},
+					41 : {
+						"label" : "pcie_pwrbrk",
+						"direction" : "in",
+					},
+				}
+			},
+
+
+			"gpiochip1" : {
+				"comment" : "Versal PMC Bank 1 GPIO",
+				"lines" : {
+					37 : {
+						"label" : "ADM1066 VX1 (U68)",
+						"direction" : "out",
+						"value" : True,
+					},
+					38 : {
+						"label" : "PCIE_PERST_B (R337 DNP)",
+						"direction" : "in",
+					},
+					39 : {
+						"label" : "PCIE_PERST_B (R780 DNP)",
+						"direction" : "in"
+					},
+				}
+			},
+
+			#"gpiochip2" : {
+			#	"comment" : "ADM1266 GPIO (U33)",
+			#},
+			
+			"gpiochip3" : {
+				"comment" : "I2C GPIO expander TCA6424A (U80)",
+				"lines" : {
+					0 : {
+						"label" : "alert_sys12",
+						"direction" : "in"
+					},
+					1 : {
+						"label" : "alert_vcc_int",
+						"direction" : "in"
+					},
+					2 : {
+						"label" : "sys12_temp_alert_b",
+						"direction" : "in"
+					},
+					3 : {
+						"label" : "sys12_temp_err_b",
+						"direction" : "in"
+					},
+					4 : {
+						"label" : "sys15_temp_alert_b",
+						"direction" : "in"
+					},
+					5 : {
+						"label" : "alert_sys15",
+						"direction" : "in"
+					},
+					6 : {
+						"label" : "alert_sys33",
+						"direction" : "in"
+					},
+					7 : {
+						"label" : "alert_sys38",
+						"direction" : "in"
+					},
+					8 : {
+						"label" : "sys33_temp_alert_b",
+						"direction" : "in"
+					},
+					9 : {
+						"label" : "sys33_temp_err_b",
+						"direction" : "in",
+					},
+					10 : {
+						"label" : "ltm4700_alert_b",
+						"direction" : "in"
+					},
+					11 : {
+						"label" : "vcc_int_fault",
+						"direction" : "in"
+					},
+					12 : {
+						"label" : "sys18_temp_err_b",
+						"direction" : "in"
+					},
+					13 : {
+						"label" : "alert_sys18",
+						"direction" : "in"
+					},
+					14 : {
+						"label" : "sys18_temp_alert_b",
+						"direction" : "in"
+					},
+					15 : {
+						"label" : "alert_sys25",
+						"direction" : "in"
+					},
+					16 : {
+						"label" : "alert_mgtavcc",
+						"direction" : "in"
+					},
+					17 : {
+						"label" : "mgtavcc_temp_alert_b",
+						"direction" : "in"
+					},
+					18 : {
+						"label" : "mgtavcc_temp_err_b",
+						"direction" : "in"
+					},
+					19 : {
+						"label" : "alert_12p0v",
+						"direction" : "in"
+					},
+					20 : {
+						"label" : "mgtavtt_temp_err_b",
+						"direction" : "in"
+					},
+					21 : {
+						"label" : "mgtavtt_temp_alert_b",
+						"direction" : "in"
+					},
+					22 : {
+						"label" : "alert_mgtavtt",
+						"direction" : "in"
+					},
+					23 : {
+						"label" : "sys15_tmp_err_b",
+						"direction" : "in"
+					},
+				}
+			},
+
+
+			"gpiochip4" : {
+				"comment" : "I2C GPIO expander TCA6424A (U81)",
+				"lines" : {
+					0 : {
+						"label" : "ff1_rx_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					1 : {
+						"label" : "ff1_rx_intr_b",
+						"direction" : "in"
+					},
+					2 : {
+						# Mark as "in" if controlled by the SFP model
+						"label" : "ff1_rx_select_b",
+						"direction" : "out",
+						"value" : False
+					},
+					3 : {
+						"label" : "ff1_rx_prsnt_b",
+						"direction" : "in"
+					},
+
+					4 : {
+						"label" : "ff1_tx_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					5 : {
+						"label" : "ff1_tx_intr_b",
+						"direction" : "in"
+					},
+					6 : {
+						# Mark as "in" if controlled by the SFP model
+						"label" : "ff1_tx_select_b",
+						"direction" : "out",
+						"value" : False
+					},
+					7 : {
+						"label" : "ff1_tx_prsnt_b",
+						"direction" : "in"
+					},
+					8 : {
+						"label" : "ff2_rx_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					9 : {
+						"label" : "ff2_rx_intr_b",
+						"direction" : "in",
+					},
+					10 : {
+						# Mark as "in" if controlled by the SFP model
+						"label" : "ff2_rx_select_b",
+						"direction" : "out",
+						"value" : False
+					},
+					11 : {
+						"label" : "ff2_rx_prsnt_b",
+						"direction" : "in"
+					},
+					12 : {
+						"label" : "ff2_tx_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					13 : {
+						"label" : "ff2_tx_intr_b",
+						"direction" : "in"
+					},
+					14 : {
+						# Mark as "in" if controlled by the SFP model
+						"label" : "ff2_tx_select_b",
+						"direction" : "out",
+						"value" : False
+					},
+					15 : {
+						"label" : "ff2_tx_prsnt_b",
+						"direction" : "in"
+					},
+					16 : {
+						"label" : "ff3_rst_b",
+						"direction" : "out",
+						"value" : True
+					},
+					17 : {
+						"label" : "ff3_intr_b",
+						"direction" : "in"
+					},
+					18 : {
+						# Mark as "in" if controlled by the SFP model
+						"label" : "ff3_select_b",
+						"direction" : "out",
+						"value" : False
+					},
+					19 : {
+						"label" : "ltm4642_temp_alt_b",
+						"direction" : "in"
+					},
+					20 : {
+						"label" : "ltm4642_temp_err_b",
+						"direction" : "in"
+					},
+					21 : {
+						"label" : "versal_cfg_done_gpio",
+						"direction" : "in"
+					},
+					22 : {
+						"label" : "i2c_gpio_por",
+						"direction" : "in"
+					},
+					23 : {
+						"label" : "versal_error_out_gpio",
+						"direction" : "in"
+					},
+				}
+			},
+		},
+
+	},
+
+
+	"chipscopy" : {
+		"cs_server_url" : "TCP:192.168.0.18:3042",
+		"hw_server_url" : "TCP:192.168.0.18:3121",
+		"device_family" : "versal",
+		"bypass_version_check" : False,
+	},
+
+	"chipscopy-ddrmc" : {
+		"self_test" : {
+			"description" : "Chipscopy-based DDRMC test",
+		},
+	},
+
+	"chipscopy-ibert" : {		
+		# Allow scheduling scans in entire quad at once. This may or may not work well
+		# and may or may not be faster than doing the scans one by one
+
+		# True = schedule scans in the entire quad at once 
+		# False = schedule scans one by one (use that if Eye Scan page freezes)
+		'scan_by_quad' : False,
+        
+		# how long before the scheduler gives up on the scan
+        'scan_timeout_seconds' : 300,
+
+		"self_test" : {
+			"description" : "Chipscopy-based IBERT test and eye scans",
+		},
+        
+		# please list the links where RX is from the same quad contiguously
+		# one after another so that scans can be scheduled in batches
+		"gt_links" : {
+			"PCIE_0" : {
+				"rx" : { "quad" : "Quad_103", "channel" : 0},
+				"tx" : { "quad" : "Quad_103", "channel" : 0},
+			},
+			"PCIE_1" : {
+				"rx" : { "quad" : "Quad_103", "channel" : 1},
+				"tx" : { "quad" : "Quad_103", "channel" : 1},
+			},
+			"PCIE_2" : {
+				"rx" : { "quad" : "Quad_103", "channel" : 2},
+				"tx" : { "quad" : "Quad_103", "channel" : 2},
+			},
+			"PCIE_3" : {
+				"rx" : { "quad" : "Quad_103", "channel" : 3},
+				"tx" : { "quad" : "Quad_103", "channel" : 3},
+			},
+			"PCIE_4" : {
+				"rx" : { "quad" : "Quad_104", "channel" : 0},
+				"tx" : { "quad" : "Quad_104", "channel" : 0},
+			},
+			"PCIE_5" : {
+				"rx" : { "quad" : "Quad_104", "channel" : 1},
+				"tx" : { "quad" : "Quad_104", "channel" : 1},
+			},
+			"PCIE_6" : {
+				"rx" : { "quad" : "Quad_104", "channel" : 2},
+				"tx" : { "quad" : "Quad_104", "channel" : 2},
+			},
+			"PCIE_7" : {
+				"rx" : { "quad" : "Quad_104", "channel" : 3},
+				"tx" : { "quad" : "Quad_104", "channel" : 3},
+			},
+			"PCIE_8" : {
+				"rx" : { "quad" : "Quad_105", "channel" : 0},
+				"tx" : { "quad" : "Quad_105", "channel" : 0},
+			},
+			"PCIE_9" : {
+				"rx" : { "quad" : "Quad_105", "channel" : 1},
+				"tx" : { "quad" : "Quad_105", "channel" : 1},
+			},
+			"PCIE_10" : {
+				"rx" : { "quad" : "Quad_105", "channel" : 2},
+				"tx" : { "quad" : "Quad_105", "channel" : 2},
+			},
+			"PCIE_11" : {
+				"rx" : { "quad" : "Quad_105", "channel" : 3},
+				"tx" : { "quad" : "Quad_105", "channel" : 3},
+			},
+			"PCIE_12" : {
+				"rx" : { "quad" : "Quad_106", "channel" : 0},
+				"tx" : { "quad" : "Quad_106", "channel" : 0},
+			},
+			"PCIE_13" : {
+				"rx" : { "quad" : "Quad_106", "channel" : 1},
+				"tx" : { "quad" : "Quad_106", "channel" : 1},
+			},
+			"PCIE_14" : {
+				"rx" : { "quad" : "Quad_106", "channel" : 2},
+				"tx" : { "quad" : "Quad_106", "channel" : 2},
+			},
+			"PCIE_15" : {
+				"rx" : { "quad" : "Quad_106", "channel" : 3},
+				"tx" : { "quad" : "Quad_106", "channel" : 3},
+			},
+			"FF3_0" : {
+				"rx" : { "quad" : "Quad_200", "channel" : 0},
+				"tx" : { "quad" : "Quad_200", "channel" : 0},
+			},
+			"FF3_1" : {
+				"rx" : { "quad" : "Quad_200", "channel" : 1},
+				"tx" : { "quad" : "Quad_200", "channel" : 1},
+			},
+			"FF3_2" : {
+				"rx" : { "quad" : "Quad_200", "channel" : 2},
+				"tx" : { "quad" : "Quad_200", "channel" : 2},
+			},
+			"FF3_3" : {
+				"rx" : { "quad" : "Quad_200", "channel" : 3},
+				"tx" : { "quad" : "Quad_200", "channel" : 3},
+			},
+			"FF1_0" : {
+				"rx" : { "quad" : "Quad_201", "channel" : 0},
+				"tx" : { "quad" : "Quad_201", "channel" : 0},
+			},
+			"FF1_1" : {
+				"rx" : { "quad" : "Quad_201", "channel" : 1},
+				"tx" : { "quad" : "Quad_201", "channel" : 1},
+			},
+			"FF1_2" : {
+				"rx" : { "quad" : "Quad_201", "channel" : 2},
+				"tx" : { "quad" : "Quad_201", "channel" : 2},
+			},
+			"FF1_3" : {
+				"rx" : { "quad" : "Quad_201", "channel" : 3},
+				"tx" : { "quad" : "Quad_201", "channel" : 3},
+			},
+			"FF1_4" : {
+				"rx" : { "quad" : "Quad_202", "channel" : 0},
+				"tx" : { "quad" : "Quad_202", "channel" : 0},
+			},
+			"FF1_5" : {
+				"rx" : { "quad" : "Quad_202", "channel" : 1},
+				"tx" : { "quad" : "Quad_202", "channel" : 1},
+			},
+			"FF1_6" : {
+				"rx" : { "quad" : "Quad_202", "channel" : 2},
+				"tx" : { "quad" : "Quad_202", "channel" : 2},
+			},
+			"FF1_7" : {
+				"rx" : { "quad" : "Quad_202", "channel" : 3},
+				"tx" : { "quad" : "Quad_202", "channel" : 3},
+			},
+			"FF1_8" : {
+				"rx" : { "quad" : "Quad_203", "channel" : 0},
+				"tx" : { "quad" : "Quad_203", "channel" : 0},
+			},
+			"FF1_9" : {
+				"rx" : { "quad" : "Quad_203", "channel" : 1},
+				"tx" : { "quad" : "Quad_203", "channel" : 1},
+			},
+			"FF1_10" : {
+				"rx" : { "quad" : "Quad_203", "channel" : 2},
+				"tx" : { "quad" : "Quad_203", "channel" : 2},
+			},
+			"FF1_11" : {
+				"rx" : { "quad" : "Quad_203", "channel" : 3},
+				"tx" : { "quad" : "Quad_203", "channel" : 3},
+			},
+			"FF2_0" : {
+				"rx" : { "quad" : "Quad_204", "channel" : 0},
+				"tx" : { "quad" : "Quad_204", "channel" : 0},
+			},
+			"FF2_1" : {
+				"rx" : { "quad" : "Quad_204", "channel" : 1},
+				"tx" : { "quad" : "Quad_204", "channel" : 1},
+			},
+			"FF2_2" : {
+				"rx" : { "quad" : "Quad_204", "channel" : 2},
+				"tx" : { "quad" : "Quad_204", "channel" : 2},
+			},
+			"FF2_3" : {
+				"rx" : { "quad" : "Quad_204", "channel" : 3},
+				"tx" : { "quad" : "Quad_204", "channel" : 3},
+			},
+			"FF2_4" : {
+				"rx" : { "quad" : "Quad_205", "channel" : 0},
+				"tx" : { "quad" : "Quad_205", "channel" : 0},
+			},
+			"FF2_5" : {
+				"rx" : { "quad" : "Quad_205", "channel" : 1},
+				"tx" : { "quad" : "Quad_205", "channel" : 1},
+			},
+			"FF2_6" : {
+				"rx" : { "quad" : "Quad_205", "channel" : 2},
+				"tx" : { "quad" : "Quad_205", "channel" : 2},
+			},
+			"FF2_7" : {
+				"rx" : { "quad" : "Quad_205", "channel" : 3},
+				"tx" : { "quad" : "Quad_205", "channel" : 3},
+			},
+			"FF2_8" : {
+				"rx" : { "quad" : "Quad_206", "channel" : 0},
+				"tx" : { "quad" : "Quad_206", "channel" : 0},
+			},
+			"FF2_9" : {
+				"rx" : { "quad" : "Quad_206", "channel" : 1},
+				"tx" : { "quad" : "Quad_206", "channel" : 1},
+			},
+			"FF2_10" : {
+				"rx" : { "quad" : "Quad_206", "channel" : 2},
+				"tx" : { "quad" : "Quad_206", "channel" : 2},
+			},
+			"FF2_11" : {
+				"rx" : { "quad" : "Quad_206", "channel" : 3},
+				"tx" : { "quad" : "Quad_206", "channel" : 3},
+			},
+		},
+
+		"apply_default_link_config_on_start_up" : False,
+		
+		"default_link_config" : {
+			"tx" : {
+				"Pattern"	: "PRBS 31",
+				"Differential Control" :  "804 mV",
+			},
+			"rx" : {
+				"Loopback"	: "Near-End PMA",
+				"Pattern"	: "PRBS 31",
+				"Termination Voltage" : "800mv",
+			}
+		},
+
+
+		"scans" : {
+            
+			"pcie_pma_near_loopback" : {
+				"comment" : "PCIe Near-End PMA Loopback",
+				"links" : [
+					"PCIE_0", "PCIE_1", "PCIE_2", "PCIE_3", "PCIE_4",  "PCIE_5", "PCIE_6", "PCIE_7",
+					"PCIE_8", "PCIE_9", "PCIE_10", "PCIE_11", "PCIE_12", "PCIE_13", "PCIE_14", "PCIE_15", 
+				],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},
+				
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+					"Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "Near-End PMA",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",               
+				}
+			},
+			
+			"pcie_fiber_loopback" : {
+				"comment" : "PCIe Physical Loopback",
+				"links" : [
+					"PCIE_0", "PCIE_1", "PCIE_2", "PCIE_3", "PCIE_4",  "PCIE_5", "PCIE_6", "PCIE_7",
+					"PCIE_8", "PCIE_9", "PCIE_10", "PCIE_11", "PCIE_12", "PCIE_13", "PCIE_14", "PCIE_15", 
+				],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},				
+
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+                    "Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "User Design",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",
+				}
+			},
+
+
+			"ff1_pma_near_loopback" : {
+				"comment" : "FireFly 1 (J32, J33) Near-End PMA Loopback",
+				"links" : [ "FF1_0", "FF1_1", "FF1_2", "FF1_3", "FF1_4",  "FF1_5", "FF1_6", "FF1_7",
+					"FF1_8", "FF1_9", "FF1_10", "FF1_11", ],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},
+				
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+                    "Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "Near-End PMA",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",
+				}
+			},
+			
+			"ff1_fiber_loopback" : {
+				"comment" : "FireFly 1 (J32, J33) Physical Loopback",
+				"links" : [ "FF1_0", "FF1_1", "FF1_2", "FF1_3", "FF1_4",  "FF1_5", "FF1_6", "FF1_7",
+					"FF1_8", "FF1_9", "FF1_10", "FF1_11", ],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},				
+
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+                    "Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "User Design",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",
+				}
+			},
+
+			"ff2_pma_near_loopback" : {
+				"comment" : "FireFly 2 (J32, J33) Near-End PMA Loopback",
+				"links" : [ "FF2_0", "FF2_1", "FF2_2", "FF2_3", "FF2_4",  "FF2_5", "FF2_6", "FF2_7",
+					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},
+				
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+                    "Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "Near-End PMA",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",
+				}
+			},
+			
+			"ff2_fiber_loopback" : {
+				"comment" : "FireFly 2 (J32, J33) Physical Loopback",
+				"links" : [ "FF2_0", "FF2_1", "FF2_2", "FF2_3", "FF2_4",  "FF2_5", "FF2_6", "FF2_7",
+					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},				
+
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+                    "Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "User Design",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",
+				}
+			},
+
+			"ff3_pma_near_loopback" : {
+				"comment" : "FireFly 3 (J34) Near-End PMA Loopback",
+				"links" : [ "FF3_0", "FF3_1", "FF3_2", "FF3_3", ],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},
+				
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+                    "Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "Near-End PMA",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",
+				}
+			},
+			
+			"ff3_fiber_loopback" : {
+				"comment" : "FireFly 3 (J34) Physical Loopback",
+				"links" : [ "FF3_0", "FF3_1", "FF3_2", "FF3_3", ],
+
+				"scan" : {
+					"Target BER" : 1e-6,
+					"Horizontal Step" : 2,
+					"Vertical Step" : 2,
+				},				
+
+				"tx" : {
+					"Pattern"	: "PRBS 31",
+                    "Differential Control" :  "804 mV",
+				},
+
+				"rx" : {
+					"Loopback"	: "User Design",
+					"Pattern"	: "PRBS 31",
+                    "Termination Voltage" : "500mv",
+				}
+			},
+
+		},
+	},
+
+}
+
+
+# check that the I2C bus looks more or less as expected
+# to protect the devices from being overwritten
+# import re
+# import subprocess
+
+# output = subprocess.check_output(["i2cdetect", "-l"]).decode('utf-8')
+# pattern = r"i2c-3\s+i2c\s+xiic-i2c\s+[0-9a-fA-F]+\.i2c\s+I2C adapter"
+
+# if not re.search(pattern, output):
+# 	print(output)
+# 	raise RuntimeError("Expected xiic-i2c driver on i2c-3 bus! Aborting the self-test!")
+
+
+# check the SI570 frequency and update the configuration 
+# when alternative parts are used
+
+from util.si570 import read_default_frequency
+from util.i2c import I2CError
+import math, pdb
+
+
+def check_and_redefine_clock(ref_clk_name, target_clk_name, alt_frequency, tolerance=5e-2):	
+	try:
+		ref_clk = config['si570']['mapping'][ref_clk_name]		
+		f_actual = read_default_frequency(ref_clk)		
+		if math.isclose(f_actual, alt_frequency, rel_tol=tolerance):
+			print(f"{ref_clk_name} is {alt_frequency} MHz instead of {default_si570_frequency} MHz!")
+			config['si5345']["mapping"][target_clk_name]["f_in1_MHz"] = alt_frequency
+			ref_clk["freq_MHz"] = alt_frequency
+	except I2CError:
+		pass
+
+
+for alt_frequency in alt_si570_frequencies:
+	check_and_redefine_clock(ref_clk_name="SI5345A_ref_clk", target_clk_name='si5345_a',
+		alt_frequency=alt_frequency)
+	check_and_redefine_clock(ref_clk_name="SI5345B_ref_clk", target_clk_name='si5345_b',
+		alt_frequency=alt_frequency)
+
+
+
+
diff --git a/controllers/adm1266.py b/controllers/adm1266.py
new file mode 100644
index 0000000..16f3ecd
--- /dev/null
+++ b/controllers/adm1266.py
@@ -0,0 +1,25 @@
+from flask import render_template, request, current_app
+from markupsafe import Markup
+from models.adm1266 import ADM1266
+import pdb
+
+
+class ADM1266_Controller():
+	def __init__(self, config):
+		self.config = config
+		self.skip_io = current_app.config["USER_SKIP_IO"] if "USER_SKIP_IO" in current_app.config else False
+		self.model = ADM1266(config) if not self.skip_io else None
+
+
+	def read_all(self):
+		if self.skip_io:
+			return render_template("error.html", error="I/O is disabled in test mode")
+
+		data = self.model.read_all()
+		
+		for name, device in data.items():
+			if "error" in device:
+				device["device_type"] = "(N/A)"
+				continue
+
+		return render_template("adm1266.html", data=data)
diff --git a/models/adm1266.py b/models/adm1266.py
new file mode 100644
index 0000000..f30542e
--- /dev/null
+++ b/models/adm1266.py
@@ -0,0 +1,44 @@
+import pdb
+from util.adm1266 import ADM1266 as ADM1266_Device
+from util.i2c import I2CError
+from threading import Lock
+from functools import wraps
+from models.self_test_executor import SelfTestExecutor
+
+
+def make_device(settings):
+	return ADM1266_Device(i2c_bus=settings["i2c_bus"], i2c_addr=settings["i2c_addr"])
+
+
+class ADM1266(SelfTestExecutor):
+	def __init__(self, config):
+		self.config = config
+		self.lock = Lock()
+		self.devices = {}
+
+		for name, settings in config["mapping"].items():
+			try:
+				self.devices[name] = make_device(settings)
+			except I2CError as err:
+				self.devices[name] = None
+
+
+	def read(self, name):
+
+		with self.lock:			
+			try:
+				if self.devices[name] is None:
+					self.devices[name] = make_device(self.config["mapping"][name])
+				return self.devices[name].read()
+			except (IOError, I2CError) as err:
+				return { "error" : str(err) }
+
+
+	def read_all(self):
+		result = {}
+		for name, settings in self.config["mapping"].items():
+			result[name] = self.read(name)
+		return result
+
+
+		
\ No newline at end of file
diff --git a/routes/__init__.py b/routes/__init__.py
index cd00258..666b3a3 100644
--- a/routes/__init__.py
+++ b/routes/__init__.py
@@ -15,6 +15,7 @@ from controllers.ina226 import INA226_Controller as INA226
 from controllers.ccf_clocks import CCF_ClocksController as CCF_Clocks
 from controllers.gpio import GPIO_Controller as GPIO
 from controllers.adm106x import ADM106x_Controller as ADM106x
+from controllers.adm1266 import ADM1266_Controller as ADM1266
 from controllers.vivado_ddrmc import Vivado_DDRMC_Controller as Vivado_DDRMC
 from controllers.vivado_ibert import Vivado_IBERT_Controller as Vivado_IBERT
 from controllers.chipscopy_ddrmc import Chipscopy_DDRMC_Controller as Chipscopy_DDRMC
@@ -151,6 +152,14 @@ def create_app(name, config):
             def adm106x_read_all():
                 return adm106x.read_all()
 
+    if "adm1266" in board_config:
+        with app.app_context():
+            adm1266 = ADM1266(board_config["adm1266"])
+            self_test_models["adm1266"] = adm1266.model
+            
+            @app.route("/adm1266")
+            def adm1266_read_all():
+                return adm1266.read_all()
 
     if "si5345" in board_config:
         with app.app_context():
diff --git a/templates/adm1266.html b/templates/adm1266.html
new file mode 100644
index 0000000..a6a40ab
--- /dev/null
+++ b/templates/adm1266.html
@@ -0,0 +1,69 @@
+{% extends "base.html" %}
+{% block title %}System clocks{% endblock %}
+{% block content %}
+
+<h2>ADM1266 report</h2>
+
+{% for name, device in data.items() %}
+
+<h3>{{name}}: {{device["device_type"]}}
+  {% if device["error"] %}
+  <span class="badge rounded-pill bg-danger">ERROR</span>
+  {% endif %}
+</h3>
+
+{% if device["error"] %}
+
+<div class="alert alert-danger">
+  {{device['error']}}
+</div>
+
+{% else %}
+<div class="container">
+
+<h4>Input pins</h4>
+
+<table class="table table-striped">
+  <thead>
+    <tr>
+      <th scope="col">Name</th>
+      <th scope="col">ADC readout [V]</th>
+      <th scope="col">UV fault limit [V]</th>
+      <th scope="col">UV warning limit [V]</th>
+      <th scope="col">OV warning limit [V]</th>
+      <th scope="col">OV fault limit [V]</th>            
+      <th scope="col">Status</th>
+    </tr>
+  </thead>
+  <tbody>
+
+  	{% for idx, pin in device["pins"].items() | sort %}
+  
+    {% if pin["status"] %}
+    <tr class="table-warning">
+    {% else %}
+    <tr>
+    {% endif %}
+      <th scope="row">{{ pin["pin"] }}</th>
+      <td>{{ "{:.3f}".format(pin["vout"]) }}</td>
+      <td>{{ "{:.3f}".format(pin["uv_fault"]) }}</td>
+      <td>{{ "{:.3f}".format(pin["uv_warn"]) }}</td>
+      <td>{{ "{:.3f}".format(pin["ov_warn"]) }}</td>
+      <td>{{ "{:.3f}".format(pin["ov_fault"]) }}</td>
+      <td>{{ pin["status"] }}</td>
+    </tr>
+
+    {% endfor %}
+
+  </tbody>
+</table>
+
+
+</div>
+{% endif %}
+
+<hr>
+
+{% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index 4d066e3..63db22b 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -75,6 +75,10 @@
                 {% if "adm106x" in config %}
                 <li><a class="dropdown-item" href="/adm106x">ADM106x</a></li>
                 {% endif %}
+
+                {% if "adm1266" in config %}
+                <li><a class="dropdown-item" href="/adm1266">ADM1266</a></li>
+                {% endif %}
               </ul>
             </li>
 
diff --git a/util/adm1266.py b/util/adm1266.py
new file mode 100644
index 0000000..251ab76
--- /dev/null
+++ b/util/adm1266.py
@@ -0,0 +1,92 @@
+import pdb
+from util.i2c import i2c_handler, I2CError, I2C
+
+def sign_extend(value, bits):
+	sign = 1 << (bits - 1)
+	return (value & (sign - 1)) - (value & sign)
+
+class ADM1266():
+
+	pins = {
+		0 : "VH1",
+		1 : "VH2",
+		2 : "VH3",
+		3 : "VH4",
+		4 : "VP1",
+		5 : "VP2",
+		6 : "VP3",
+		7 : "VP4",
+		8 : "VP5",
+		9 : "VP6",
+		10 : "VP7",
+		11 : "VP8",
+		12 : "VP9",
+		13 : "VP10",
+		14 : "VP11",
+		15 : "VP12",
+		16 : "VP13",
+	}
+
+	flags = {
+		1 << 4 : "UV fault",
+		1 << 5 : "UV warn",
+		1 << 6 : "OV warn",
+		1 << 7 : "OV fault",
+	}
+
+	NUM_CHANNELS = 17
+
+	PAGE = 0x00
+	VOUT_MODE = 0x20
+	VOUT_OV_FAULT_LIMIT = 0x40
+	VOUT_OV_WARN_LIMIT = 0x42
+	VOUT_UV_WARN_LIMIT = 0x43
+	VOUT_UV_FAULT_LIMIT = 0x44
+	STATUS_VOUT = 0x7A
+	READ_VOUT = 0x8B
+
+	def write_reg(self, cmd, data):
+		with i2c_handler(self.i2c_bus) as i2c:
+			messages = [
+				{ "data" : [cmd] + data, "read" : False }
+			]
+
+			i2c.transfer(self.i2c_addr, messages)
+
+	def read_reg(self, cmd, size):
+		with i2c_handler(self.i2c_bus) as i2c:
+			messages = [
+				{ "data" : [cmd], "read" : False },
+				{ "data" : [0x00] * size, "read" : True }
+			]
+
+			messages = i2c.transfer(self.i2c_addr, messages)
+			return messages[1].data
+
+	def __init__(self, i2c_bus, i2c_addr):
+		self.i2c_addr = i2c_addr
+		self.i2c_bus = i2c_bus
+
+	def read(self):
+		result = { 'pins' : {} }
+
+		for i in range(0, self.NUM_CHANNELS):
+			self.write_reg(self.PAGE, [i])
+
+			exp = sign_extend(self.read_reg(self.VOUT_MODE, 1)[0] & 0x1F, 5)
+			status = self.read_reg(self.STATUS_VOUT, 1)[0]
+			vout = int.from_bytes(self.read_reg(self.READ_VOUT, 2), 'little') * 2 ** exp
+			ov_fault = int.from_bytes(self.read_reg(self.VOUT_OV_FAULT_LIMIT, 2), 'little') * 2 ** exp
+			ov_warn = int.from_bytes(self.read_reg(self.VOUT_OV_WARN_LIMIT, 2), 'little') * 2 ** exp
+			uv_warn = int.from_bytes(self.read_reg(self.VOUT_UV_WARN_LIMIT, 2), 'little') * 2 ** exp
+			uv_fault = int.from_bytes(self.read_reg(self.VOUT_UV_FAULT_LIMIT, 2), 'little') * 2 ** exp
+
+			status_str = ""
+
+			for f, s in self.flags.items():
+				if status & f:
+					status_str += s
+
+			result['pins'][i] = { 'pin' : self.pins[i], 'vout' : vout, 'uv_fault' : uv_fault, 'uv_warn' : uv_warn, 'ov_warn' : ov_warn, 'ov_fault' : ov_fault, 'status' : status_str}
+
+		return result
-- 
GitLab


From 0fb00656f3ebede368a4e94b43f8b71f131ac574 Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Thu, 8 Aug 2024 21:37:16 +0000
Subject: [PATCH 15/23] Update ADM1266

---
 boards/flx182b.py      | 24 +++++---------------
 models/adm1266.py      |  2 +-
 templates/adm1266.html | 10 +++++++++
 util/adm1266.py        | 51 +++++++++++++++++++++++++-----------------
 4 files changed, 47 insertions(+), 40 deletions(-)

diff --git a/boards/flx182b.py b/boards/flx182b.py
index b0df411..094e75b 100644
--- a/boards/flx182b.py
+++ b/boards/flx182b.py
@@ -121,29 +121,17 @@ config = {
 	# 	"clk_dump_path" : "/sys/kernel/debug/clk/clk_dump"
 	# },
 
-
-	#"adm106x" : { 
-	#	"mapping" : {
-	#		"U68" : {
-	#			"i2c_bus" : "/dev/i2c-6",
-	#			"i2c_addr" : 0x34,
-	#		},
-	#		"U69" : {
-	#			"i2c_bus" : "/dev/i2c-6",
-	#			"i2c_addr" : 0x35,
-	#		},
-	#	},
-
-	#	"self_test" : {
-	#		"description" : "ADM106x chip readout",
-	#	}
-	#},
-
 	"adm1266" : { 
 		"mapping" : {
 			"U68" : {
 				"i2c_bus" : "/dev/i2c-6",
 				"i2c_addr" : 0x40,
+				"rails" : ["12P0V", "NC", "NC", "NC",
+					"VCCINT", "MGTAVCC", "MGTAVTT", "SYS12",
+					"MGTYVCCAUX", "SYS15", "SYS18", "SYS25",
+					"SYS33", "SYS38", "3V3_AUX", "VCC5V",
+					"DDR4_B_VTT"
+				],
 			},
 		},
 
diff --git a/models/adm1266.py b/models/adm1266.py
index f30542e..0652a85 100644
--- a/models/adm1266.py
+++ b/models/adm1266.py
@@ -7,7 +7,7 @@ from models.self_test_executor import SelfTestExecutor
 
 
 def make_device(settings):
-	return ADM1266_Device(i2c_bus=settings["i2c_bus"], i2c_addr=settings["i2c_addr"])
+	return ADM1266_Device(i2c_bus=settings["i2c_bus"], i2c_addr=settings["i2c_addr"], rails=settings["rails"])
 
 
 class ADM1266(SelfTestExecutor):
diff --git a/templates/adm1266.html b/templates/adm1266.html
index a6a40ab..9d86cbe 100644
--- a/templates/adm1266.html
+++ b/templates/adm1266.html
@@ -21,12 +21,21 @@
 {% else %}
 <div class="container">
 
+  <ul>
+    <li>Chip ID: {{ device["chip_id"] }}</li>
+    <li>Firmware revision: {{ device["fw_rev"] }}</li>
+    <li>Bootloader revision: {{ device["boot_rev"] }} </li>
+    <li>Chip revision: {{ device["chip_rev"] }} </li>
+    <li>Powerup counter: {{ device["powerup_counter"] }}</li>
+  </ul>
+
 <h4>Input pins</h4>
 
 <table class="table table-striped">
   <thead>
     <tr>
       <th scope="col">Name</th>
+      <th scope="col">Rail</th>
       <th scope="col">ADC readout [V]</th>
       <th scope="col">UV fault limit [V]</th>
       <th scope="col">UV warning limit [V]</th>
@@ -45,6 +54,7 @@
     <tr>
     {% endif %}
       <th scope="row">{{ pin["pin"] }}</th>
+      <td>{{ pin["rail"] }}</td>
       <td>{{ "{:.3f}".format(pin["vout"]) }}</td>
       <td>{{ "{:.3f}".format(pin["uv_fault"]) }}</td>
       <td>{{ "{:.3f}".format(pin["uv_warn"]) }}</td>
diff --git a/util/adm1266.py b/util/adm1266.py
index 251ab76..3685bfb 100644
--- a/util/adm1266.py
+++ b/util/adm1266.py
@@ -7,25 +7,13 @@ def sign_extend(value, bits):
 
 class ADM1266():
 
-	pins = {
-		0 : "VH1",
-		1 : "VH2",
-		2 : "VH3",
-		3 : "VH4",
-		4 : "VP1",
-		5 : "VP2",
-		6 : "VP3",
-		7 : "VP4",
-		8 : "VP5",
-		9 : "VP6",
-		10 : "VP7",
-		11 : "VP8",
-		12 : "VP9",
-		13 : "VP10",
-		14 : "VP11",
-		15 : "VP12",
-		16 : "VP13",
-	}
+	pins = [
+		"VH1", "VH2", "VH3", "VH4",
+		"VP1", "VP2", "VP3", "VP4",
+		"VP5", "VP6", "VP7", "VP8",
+		"VP9", "VP10", "VP11", "VP12",
+		"VP13",
+	]
 
 	flags = {
 		1 << 4 : "UV fault",
@@ -44,6 +32,9 @@ class ADM1266():
 	VOUT_UV_FAULT_LIMIT = 0x44
 	STATUS_VOUT = 0x7A
 	READ_VOUT = 0x8B
+	IC_DEVICE = 0xAD
+	IC_DEVICE_REV = 0xAE
+	POWERUP_COUNTER = 0xE4
 
 	def write_reg(self, cmd, data):
 		with i2c_handler(self.i2c_bus) as i2c:
@@ -63,13 +54,29 @@ class ADM1266():
 			messages = i2c.transfer(self.i2c_addr, messages)
 			return messages[1].data
 
-	def __init__(self, i2c_bus, i2c_addr):
+	def __init__(self, i2c_bus, i2c_addr, rails):
 		self.i2c_addr = i2c_addr
 		self.i2c_bus = i2c_bus
+		self.rails = rails
 
 	def read(self):
 		result = { 'pins' : {} }
 
+		chip_id = self.read_reg(self.IC_DEVICE, 4)
+		result['chip_id'] = f'{chip_id[1]:02X}{chip_id[2]:02X}{chip_id[3]:02X}'
+
+		data = self.read_reg(self.IC_DEVICE_REV, 9)
+		fw_rev = data[1:4]
+		boot_rev = data[4:7]
+		chip_rev = data[7:9]
+
+		result['fw_rev'] = f'{fw_rev[0]}.{fw_rev[1]}.{fw_rev[2]}'
+		result['boot_rev'] = f'{boot_rev[0]}.{boot_rev[1]}.{boot_rev[2]}'
+		result['chip_rev'] = f'{chr(chip_rev[0])}{chr(chip_rev[1])}'
+
+		counter = int.from_bytes(self.read_reg(self.POWERUP_COUNTER, 3)[1:2], 'little')
+		result['powerup_counter'] = counter
+
 		for i in range(0, self.NUM_CHANNELS):
 			self.write_reg(self.PAGE, [i])
 
@@ -87,6 +94,8 @@ class ADM1266():
 				if status & f:
 					status_str += s
 
-			result['pins'][i] = { 'pin' : self.pins[i], 'vout' : vout, 'uv_fault' : uv_fault, 'uv_warn' : uv_warn, 'ov_warn' : ov_warn, 'ov_fault' : ov_fault, 'status' : status_str}
+			rail = self.rails[i] if i < len(self.rails) else "N/A"
+
+			result['pins'][i] = { 'pin' : self.pins[i], 'rail' : rail, 'vout' : vout, 'uv_fault' : uv_fault, 'uv_warn' : uv_warn, 'ov_warn' : ov_warn, 'ov_fault' : ov_fault, 'status' : status_str}
 
 		return result
-- 
GitLab


From e81fc26fbc4ab44d371be86a0ca4b70c6f579b9f Mon Sep 17 00:00:00 2001
From: Carlo Alberto Gottardo <carlo.alberto.gottardo@cern.ch>
Date: Fri, 9 Aug 2024 15:59:32 +0200
Subject: [PATCH 16/23] Dwell BER in place of Target BER for Chipscopy 2024.1

---
 boards/flx182.py                       | 16 ++++++++--------
 examples/chipscopy_ibert.py            |  5 +++--
 examples/scan_data/chipscopy_data.json |  2 +-
 models/chipscopy_ibert.py              |  2 +-
 4 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index e0e03c7..ce7306d 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -1161,7 +1161,7 @@ config = {
 				],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1186,7 +1186,7 @@ config = {
 				],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
@@ -1210,7 +1210,7 @@ config = {
 					"FF1_8", "FF1_9", "FF1_10", "FF1_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1233,7 +1233,7 @@ config = {
 					"FF1_8", "FF1_9", "FF1_10", "FF1_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
@@ -1256,7 +1256,7 @@ config = {
 					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1279,7 +1279,7 @@ config = {
 					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
@@ -1301,7 +1301,7 @@ config = {
 				"links" : [ "FF3_0", "FF3_1", "FF3_2", "FF3_3", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1323,7 +1323,7 @@ config = {
 				"links" : [ "FF3_0", "FF3_1", "FF3_2", "FF3_3", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
diff --git a/examples/chipscopy_ibert.py b/examples/chipscopy_ibert.py
index 3b5fe69..ac29925 100644
--- a/examples/chipscopy_ibert.py
+++ b/examples/chipscopy_ibert.py
@@ -20,7 +20,7 @@ def convert_to_dict(scan):
     y_values = set()
     result = {}
     result.update(scan.scan_data.all_params)
-    target_ber = result["Target BER"]
+    target_ber = result["Dwell BER"]
 
     data_in = scan.scan_data.processed
     for (x, y) in data_in.scan_points.keys():
@@ -117,7 +117,7 @@ for eye_scan in eye_scans:
     eye_scan.params[EYE_SCAN_VERT_RANGE].value = "100%"
     eye_scan.params[EYE_SCAN_TARGET_BER].value = 1e-6
 
-    eye_scan.start(show_progress_bar=False)
+    eye_scan.start(show_progress_bar=True)
     print(f"Started eye scan {eye_scan}")
 
 print("Waiting for the scans to complete...")
@@ -125,6 +125,7 @@ print("Waiting for the scans to complete...")
 for eye_scan in eye_scans:
     eye_scan.wait_till_done()
 
+pdb.set_trace()
 scan = eye_scans[0]
 data_dict = convert_to_dict(scan)
 
diff --git a/examples/scan_data/chipscopy_data.json b/examples/scan_data/chipscopy_data.json
index da77512..f547f6b 100644
--- a/examples/scan_data/chipscopy_data.json
+++ b/examples/scan_data/chipscopy_data.json
@@ -4,7 +4,7 @@
     "Prescale": 0,
     "Clock Divider": 0,
     "Counts per UI": 32,
-    "Target BER": 1e-06,
+    "Dwell BER": 1e-06,
     "Horizontal Range": "-0.500 UI to 0.500 UI",
     "Vertical Range": "100%",
     "Maximum Horizontal Range": 32,
diff --git a/models/chipscopy_ibert.py b/models/chipscopy_ibert.py
index 63d6901..236188b 100644
--- a/models/chipscopy_ibert.py
+++ b/models/chipscopy_ibert.py
@@ -547,7 +547,7 @@ def extract_data(scan):
 	data_out = [[1 for x in range(len(x_axis))] for y in range(len(y_axis))]
 	open_points = 0
 	num_points = len(y_axis) * len(x_axis)
-	target_ber = result["Target BER"]
+	target_ber = result["Dwell BER"]
 
 	for i in range(len(x_axis)):
 		for j in range(len(y_axis)):
-- 
GitLab


From 1b0d584682f04dad5d080b578428bc7633c5370b Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Wed, 14 Aug 2024 20:06:52 +0000
Subject: [PATCH 17/23] Use labels to identify gpiochip devices

---
 boards/flx182.py    |  8 ++++----
 boards/flx182b.py   | 19 ++++++++-----------
 models/gpio.py      |  8 +++++---
 templates/gpio.html |  2 +-
 util/gpio.py        | 23 +++++++++++++++++++++++
 5 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index ffb1bdb..b91d7a1 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -346,7 +346,7 @@ config = {
 			"description" : "GPIO readout",
 		},
 		"mapping" : {
-			"gpiochip0" : {
+			"20100000000.gpio" : {
 				"comment" : "Versal AXI GPIO",
 				"lines" : {
 					# GPIO_0 port in Vivado
@@ -555,7 +555,7 @@ config = {
 			},
 
 
-			"gpiochip1" : {
+			"pmc_gpio" : {
 				"comment" : "Versal PMC Bank 1 GPIO",
 				"lines" : {
 					37 : {
@@ -575,7 +575,7 @@ config = {
 			},
 
 			
-			"gpiochip2" : {
+			"11-0022" : {
 				"comment" : "I2C GPIO expander TCA6424A (U65)",
 				"lines" : {
 					0 : {
@@ -678,7 +678,7 @@ config = {
 			},
 
 
-			"gpiochip3" : {
+			"11-0023" : {
 				"comment" : "I2C GPIO expander TCA6424A (U66)",
 				"lines" : {
 					0 : {
diff --git a/boards/flx182b.py b/boards/flx182b.py
index 094e75b..24408f0 100644
--- a/boards/flx182b.py
+++ b/boards/flx182b.py
@@ -347,7 +347,7 @@ config = {
 			"description" : "GPIO readout",
 		},
 		"mapping" : {
-			"gpiochip0" : {
+			"20100000000.gpio" : {
 				"comment" : "Versal AXI GPIO",
 				"lines" : {
 					# GPIO_0 port in Vivado
@@ -556,30 +556,27 @@ config = {
 			},
 
 
-			"gpiochip1" : {
+			"pmc_gpio" : {
 				"comment" : "Versal PMC Bank 1 GPIO",
 				"lines" : {
 					37 : {
-						"label" : "ADM1066 VX1 (U68)",
+						"label" : "ADM1266 GPIO1 (U33)",
 						"direction" : "out",
 						"value" : True,
 					},
 					38 : {
-						"label" : "PCIE_PERST_B (R337 DNP)",
+						"label" : "PCIE_PERST_B (R396 DNP)",
 						"direction" : "in",
 					},
 					39 : {
-						"label" : "PCIE_PERST_B (R780 DNP)",
+						"label" : "PCIE_PERST_B (R398 DNP)",
 						"direction" : "in"
 					},
 				}
 			},
 
-			#"gpiochip2" : {
-			#	"comment" : "ADM1266 GPIO (U33)",
-			#},
-			
-			"gpiochip3" : {
+
+			"11-0022" : {
 				"comment" : "I2C GPIO expander TCA6424A (U80)",
 				"lines" : {
 					0 : {
@@ -682,7 +679,7 @@ config = {
 			},
 
 
-			"gpiochip4" : {
+			"11-0023" : {
 				"comment" : "I2C GPIO expander TCA6424A (U81)",
 				"lines" : {
 					0 : {
diff --git a/models/gpio.py b/models/gpio.py
index b90385b..bc6da8c 100644
--- a/models/gpio.py
+++ b/models/gpio.py
@@ -13,6 +13,8 @@ class GPIO(SelfTestExecutor):
 			params["has_inputs"] = self.has_direction(params["lines"], "in")
 			params["has_outputs"] = self.has_direction(params["lines"], "out")
 
+			params["device"] = gpio.detect(device)
+
 		self.initialize_lines()		
 
 
@@ -52,7 +54,7 @@ class GPIO(SelfTestExecutor):
 				else:
 					return {"error" : "Bit value '{}' must be '0' or '1'".format(bit)}
 				with self.lock:
-					gpio.set_line(device, bit, value_bool)
+					gpio.set_line(self.config["mapping"][device]["device"], bit, value_bool)
 					self.config["mapping"][device]["lines"][bit]["value"] = value_bool
 					result[bit] = value_bool		
 		except Exception as err:
@@ -75,7 +77,7 @@ class GPIO(SelfTestExecutor):
 				if props["direction"] == "out" and "value" in props:
 					value = props["value"]
 				else:
-					value = gpio.get_line(device, line)
+					value = gpio.get_line(self.config["mapping"][device]["device"], line)
 					if props["direction"] == "out":
 						props["value"] = value
 				result["lines"][line]["value"] = value
@@ -97,7 +99,7 @@ class GPIO(SelfTestExecutor):
 				continue
 			with self.lock:
 				value = props["value"] if "value" in props else False
-				gpio.set_line(device, line, value)
+				gpio.set_line(self.config["mapping"][device]["device"], line, value)
 				props["value"] = value
 
 
diff --git a/templates/gpio.html b/templates/gpio.html
index 27682ad..d60bf53 100644
--- a/templates/gpio.html
+++ b/templates/gpio.html
@@ -12,7 +12,7 @@
 {% else %}
 
   <div class="container py-4 px-0"> 
-  <h2>{{ device }}
+  <h2>{{ device }} ({{ params["device"] }})
     <small class="text-muted">{{ params["comment"] }}</small></h2>
 
   {% if params["has_outputs"] %}
diff --git a/util/gpio.py b/util/gpio.py
index 1d63a83..0e92e11 100644
--- a/util/gpio.py
+++ b/util/gpio.py
@@ -1,5 +1,8 @@
 import periphery
 from periphery import GPIO as GPIO_periphery
+import fcntl
+import struct
+import glob
 
 GPIOError = periphery.GPIOError
 
@@ -14,3 +17,23 @@ def get_line(device, line):
 	value = gpio.read()
 	gpio.close()
 	return value
+
+
+GPIO_GET_CHIPINFO_IOCTL = 0x8044B401
+
+def detect(chip_label):
+	chips = glob.glob("/dev/gpiochip*")
+	for chip in chips:
+		f = open(chip, "rb")
+
+		gpiochip_info = struct.Struct("32s 32s L")
+		buffer = gpiochip_info.pack(b' ', b' ', 0)
+		result = fcntl.ioctl(f, GPIO_GET_CHIPINFO_IOCTL, buffer)
+
+		name, label, lines = gpiochip_info.unpack(result)
+
+		name = name.rstrip(b'\0').decode("utf-8")
+		label = label.rstrip(b'\0').decode("utf-8")
+
+		if label == chip_label:
+			return name
-- 
GitLab


From fde83de563d6b8affc9f0f48deb2259217f3ade1 Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Wed, 14 Aug 2024 20:21:48 +0000
Subject: [PATCH 18/23] Update configuration for FLX-182B

---
 boards/flx182b.py | 149 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 138 insertions(+), 11 deletions(-)

diff --git a/boards/flx182b.py b/boards/flx182b.py
index 24408f0..7b0c957 100644
--- a/boards/flx182b.py
+++ b/boards/flx182b.py
@@ -23,6 +23,7 @@ config = {
 		"interface" : "zynq_mpsoc",
 		"board" : "FLX-182",
 		"revision" : "3_1b",
+		"DNAaddress" : [0xF1250024, 0xF1250020],
 		"self_test" : {
 			"description" : "Identify the board",
 		}
@@ -39,6 +40,33 @@ config = {
 		"name" : "ina226",
 		"self_test" : {
 			"description" : "INA226 chip readout",
+		},
+		#tolerance and max current set by hand
+		"reference" : {
+			"voltage" : {
+				"12P0V" :   {"nominal" : 12.00, "min" : 11.0,  "max" : 23.0,  "desc" : "+/- 1V expected from power supply"},
+				"MGTAVCC" : {"nominal" : 0.88 , "min" : 0.854, "max" : 0.906, "desc" : "V limits from FPGA recommended ranges"},
+				"MGTAVTT" : {"nominal" : 1.20 , "min" : 1.164, "max" : 1.236, "desc" : "V limits from FPGA recommended ranges"},
+				"SYS12" :   {"nominal" : 1.20 , "min" : 1.164, "max" : 1.236, "desc" : "V limits from FPGA recommended ranges"},
+				"SYS15" :   {"nominal" : 1.50 , "min" : 1.455, "max" : 1.545, "desc" : "V limits from FPGA recommended ranges"},
+				"SYS18" :   {"nominal" : 1.80 , "min" : 1.710, "max" : 1.890, "desc" : "V limits from 25G FF"},
+				"SYS25" :   {"nominal" : 2.50 , "min" : 2.15,  "max" : 2.85,  "desc" : ""},
+				"SYS33" :   {"nominal" : 3.30 , "min" : 3.15,  "max" : 3.45,  "desc" : "V limits from 14/16G FF"},
+				"SYS38" :   {"nominal" : 3.80 , "min" : 3.50,  "max" : 4.10,  "desc" : ""},
+				"VCCINT" :  {"nominal" : 0.80 , "min" : 0.775, "max" : 0.825, "desc" : ""}
+			},
+			"current" : {
+				"12P0V" :   {"nominal" : 1,     "max" : 8,    "desc" : ""},
+				"MGTAVCC" : {"nominal" : 1.5 ,  "max" : 3.5,  "desc" : ""},
+				"MGTAVTT" : {"nominal" : 3.4 ,  "max" : 6.5,  "desc" : ""},
+				"SYS12" :   {"nominal" : 0.8 ,  "max" : 1.5,  "desc" : ""},
+				"SYS15" :   {"nominal" : 0.9 ,  "max" : 1.5,  "desc" : ""},
+				"SYS18" :   {"nominal" : 0.8 ,  "max" : 1.5,  "desc" : ""},
+				"SYS25" :   {"nominal" : 0.3 ,  "max" : 0.5,  "desc" : ""},
+				"SYS33" :   {"nominal" : 3.30 , "max" : 6.0,  "desc" : ""},
+				"SYS38" :   {"nominal" : 0.0 ,  "max" : 5,    "desc" : ""},
+				"VCCINT" :  {"nominal" : 20.0 , "max" : 50.0, "desc" : ""}
+			}
 		}
 	},
 
@@ -54,6 +82,78 @@ config = {
 					"voltage" : "in_voltage(?P<id>\\d+)_(?P<name>.+)_input",
 					"temperature" : "in_temp(?P<id>\\d+)_(?P<name>.+)_input",
 				},
+				"reference" : {
+					# Reference Versal Prime Series Data Sheet: DC and AC Switching Characteristics
+					# DS956 2024-04-30
+					# Table: Recommended Operating Conditions
+					"temperature" : {
+						"max_max" :         {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A", "desc" : "FPGA diode temperature (latched max)"},
+						"temp" : 		     {"nominal" : 36, "min" : 0, "max" : 50, "source" : "N/A", "desc" : "FPGA diode temperature"},
+					},
+					"voltage" : {
+						"gty_avcc_103" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_104" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_105" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_106" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_200" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_201" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_202" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_203" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_204" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_205" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avcc_206" :    {"nominal" : 0.880, "min" : 0.854, "max" : 0.906, "source" : "DS956", "desc" : "GTY primary analog (PLL) power supply"},
+						"gty_avccaux_103" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_104" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_105" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_106" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_200" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_201" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_202" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_203" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_204" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_205" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avccaux_206" : {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "GTY aux analog (PLL) power supply"},
+						"gty_avtt_103" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_104" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_105" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_106" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_200" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_201" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_202" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_203" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_204" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_205" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+						"gty_avtt_206" : 	 {"nominal" : 1.200, "min" : 1.164, "max" : 1.236, "source" : "DS956", "desc" : "GTY termination power supply"},
+#						"vcc_batt" : 		 {"nominal" : 0, "min" : 1.200, "max" : 1.500, "source" : "DS956", "desc" : "Battery power supply"},
+						"vcc_pmc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PMC primary power supply"},
+						"vcc_psfp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS full-power domain power supply"},
+						"vcc_pslp" :        {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PS low-power domain power supply"},
+						"vcc_ram" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL RAM and clocking network" },
+						"vcc_soc" :         {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "Network on Chip (NoC) and DDR memory controller power supply"},
+						"vccaux" : 		 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "Auxiliary power supply	"},
+						"vccaux_pmc" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC auxiliary power supply voltage"},
+						"vccaux_smon" : 	 {"nominal" : 1.500, "min" : 1.455, "max" : 1.545, "source" : "DS956", "desc" : "PMC system monitor power supply relative to GND_SMON"},
+						"vccint" : 	     {"nominal" : 0.800, "min" : 0.775, "max" : 0.825, "source" : "DS956", "desc" : "PL primary power supply"},
+						"vcco_306" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 3 output driver"},
+						"vcco_406" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "HDIO bank 4 output driver"},
+						"vcco_500" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_501" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_502" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_503" : 		 {"nominal" : 1.800, "min" : 1.710, "max" : 3.400, "source" : "DS956", "desc" : "PSIO bank 5 power supply"},
+						"vcco_700" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_701" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_702" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_703" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_704" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_705" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_706" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_707" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_708" : 		 {"nominal" : 1.500, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_709" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_710" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"},
+						"vcco_711" : 		 {"nominal" : 1.200, "min" : 0.950, "max" : 1.575, "source" : "DS956", "desc" : "XPIO bank 7 output driver"}
+					}
+				}
 			},
 
 			"ltm4700" : {
@@ -61,11 +161,16 @@ config = {
 				"title" : "LTM4700",						
 				"patterns" : {
 					"voltage" : "(?P<name>in\\d+_input)",
+					"voltage_min" : "(?P<name>in\\d+_min)(?!_alarm)",
+					"voltage_max" : "(?P<name>in\\d+_max)(?!_alarm)",
+					"voltage_crit" : "(?P<name>in\\d+_crit)(?!_alarm)",
 					"current" : "(?P<name>curr\\d+_input)",
-					"temp" : "(?P<name>temp\\d+_input)",
+					"current_max" : "(?P<name>curr\\d+_max)(?!_alarm)",
 					"power" : "(?P<name>power\\d+_input)",
+					"temp" : "(?P<name>temp\\d+_input)",
+					"temp_max" : "(?P<name>temp\\d+_crit)(?!_alarm)",
 				},
-			},
+			}, #end of ltm4700
 
 			"tmp435" : {
 				"device_path" : "/sys/class/hwmon/",
@@ -75,6 +180,17 @@ config = {
 				},
 				"self_test" : {
 					"description" : "TMP435 readout",
+				},
+				"reference" : {
+					"probe" : {
+						"LTM4642_TMP" : {"min" : 20.0, "nominal" : 30.0, "max" : 50.0, "desc" : "LTM4642, 2.5V (DDR, ETH) and 3.8V (FF) rails"},
+						"MGTAVCC_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 0.88V rail (FPGA) "},
+						"MGTAVTT_TMP" : {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.2V rail (FPGA, DDR)"},
+						"SYS12_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.2V rail (FPGA, DDR)"},
+						"SYS15_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.5V rail (FPGA)"},
+						"SYS18_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 1.8V rail (FPGA, FF, Si5345)"},
+						"SYS33_TMP" :   {"min" : 20.0, "nominal" : 40.0, "max" : 60.0, "desc" : "LTM4638, 3.3V rail (FF, Si5345, QSPI)"},
+					}
 				}
 			},
 
@@ -84,10 +200,15 @@ config = {
 				"patterns" : {
 					"probe" : "(?P<name>temp\\d+_input)",
 				},
+				"reference" : {
+					"probe" : {
+						"DRAM temperature sensor" : {"min" : 20.0, "nominal" : 33.0, "max" : 50.0, "desc" : "DRAM temperature"},
+					}
+				}
 			},
 		},
 		"self_test" : {
-			"description" : "HWMON device readout (SYSMON, TMP435, etc)",
+			"description" : "HWMON device readout (SYSMON, LTM4700, TMP435, etc)",
 		}
 	},
 
@@ -259,6 +380,7 @@ config = {
 				"i2c_bus" : "/dev/i2c-8",
 				"i2c_addr" : 0x50,
 				"type" : "CERN-B-* Transmitter",
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 6,
@@ -269,6 +391,7 @@ config = {
 				"type" : "CERN-B-* Receiver",
 				"i2c_bus" : "/dev/i2c-8",
 				"i2c_addr" : 0x54,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 2,
@@ -279,6 +402,7 @@ config = {
 				"type" : "CERN-B-* Transmitter",
 				"i2c_bus" : "/dev/i2c-9",
 				"i2c_addr" : 0x50,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 14,
@@ -289,6 +413,7 @@ config = {
 				"type" : "CERN-B-* Receiver",
 				"i2c_bus" : "/dev/i2c-9",
 				"i2c_addr" : 0x54,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 10,
@@ -296,8 +421,10 @@ config = {
 				# }
 			},
 			"FireFly_J34" : {
+				"type" : "B04 Transceiver",
 				"i2c_bus" : "/dev/i2c-10",
 				"i2c_addr" : 0x50,
+				"max_temp" : 75,
 				# "select" : {
 				# 	"gpio" : "gpiochip3",
 				# 	"line" : 18,
@@ -1035,7 +1162,7 @@ config = {
 				],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1060,7 +1187,7 @@ config = {
 				],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
@@ -1084,7 +1211,7 @@ config = {
 					"FF1_8", "FF1_9", "FF1_10", "FF1_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1107,7 +1234,7 @@ config = {
 					"FF1_8", "FF1_9", "FF1_10", "FF1_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
@@ -1130,7 +1257,7 @@ config = {
 					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1153,7 +1280,7 @@ config = {
 					"FF2_8", "FF2_9", "FF2_10", "FF2_11", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
@@ -1175,7 +1302,7 @@ config = {
 				"links" : [ "FF3_0", "FF3_1", "FF3_2", "FF3_3", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},
@@ -1197,7 +1324,7 @@ config = {
 				"links" : [ "FF3_0", "FF3_1", "FF3_2", "FF3_3", ],
 
 				"scan" : {
-					"Target BER" : 1e-6,
+					"Dwell BER" : 1e-6,
 					"Horizontal Step" : 2,
 					"Vertical Step" : 2,
 				},				
-- 
GitLab


From 5af07c23a54af33ec00203c53b36dead5210bcf7 Mon Sep 17 00:00:00 2001
From: felix <felix@130-199-21-158.dhcp.bnl.gov>
Date: Wed, 14 Aug 2024 17:09:41 -0400
Subject: [PATCH 19/23] Remove absent components

---
 boards/flx182.py  | 48 ++++++++++++++++-------------------------------
 boards/flx182b.py | 48 ++++++++++++++++-------------------------------
 2 files changed, 32 insertions(+), 64 deletions(-)

diff --git a/boards/flx182.py b/boards/flx182.py
index f3ada46..3dcfc36 100644
--- a/boards/flx182.py
+++ b/boards/flx182.py
@@ -212,22 +212,6 @@ config = {
 		}
 	},
 
-
-	"si53156" : {
-		"mapping" : {
-			"U37" : {
-				"i2c_bus" : "/dev/i2c-7",
-				"i2c_addr" : 0x6b,
-				"name" : "PCIe clk buffer (U37)"
-			},
-		},
-
-		"self_test" : {
-			"description" : "Probe SI53156",
-		}
-	},
-
-
 	# displays clocks listed in Linux device tree and
 	# controlled by kernel via the Common Clock Framework
 	# "ccf_clocks" : {
@@ -342,22 +326,22 @@ config = {
 				"protected" : False,
 			},
 
-			# NOTE: alternative part may be placed
-			"lti_ref_clk" : {
-				"i2c_bus" : "/dev/i2c-9",
-				"i2c_addr" : 0x60,
-				"freq_MHz" : 240.474,
-				"stability" : "20ppm",
-				"protected" : False,
-			},
-
-			"100G_ref_clk" : {
-				"i2c_bus" : "/dev/i2c-10",
-				"i2c_addr" : 0x60,
-				"freq_MHz" : 322.265625,
-				"stability" : "20ppm",
-				"protected" : False,
-			},
+			#  No I2C access
+#			"lti_ref_clk" : {
+#				"i2c_bus" : "/dev/i2c-9",
+#				"i2c_addr" : 0x60,
+#				"freq_MHz" : 240.474,
+#				"stability" : "20ppm",
+#				"protected" : False,
+#			},
+#
+#			"100G_ref_clk" : {
+#				"i2c_bus" : "/dev/i2c-10",
+#				"i2c_addr" : 0x60,
+#				"freq_MHz" : 322.265625,
+#				"stability" : "20ppm",
+#				"protected" : False,
+#			},
 
 			"ps_ref_clk" : {
 				"i2c_bus" : "/dev/i2c-11",
diff --git a/boards/flx182b.py b/boards/flx182b.py
index 7b0c957..780d0e3 100644
--- a/boards/flx182b.py
+++ b/boards/flx182b.py
@@ -212,22 +212,6 @@ config = {
 		}
 	},
 
-
-	"si53156" : {
-		"mapping" : {
-			"U36" : {
-				"i2c_bus" : "/dev/i2c-7",
-				"i2c_addr" : 0x6b,
-				"name" : "PCIe clk buffer (U37)"
-			},
-		},
-
-		"self_test" : {
-			"description" : "Probe SI53156",
-		}
-	},
-
-
 	# displays clocks listed in Linux device tree and
 	# controlled by kernel via the Common Clock Framework
 	# "ccf_clocks" : {
@@ -343,22 +327,22 @@ config = {
 				"protected" : False,
 			},
 
-			# NOTE: alternative part may be placed
-			"lti_ref_clk" : {
-				"i2c_bus" : "/dev/i2c-9",
-				"i2c_addr" : 0x60,
-				"freq_MHz" : 240.474,
-				"stability" : "20ppm",
-				"protected" : False,
-			},
-
-			"100G_ref_clk" : {
-				"i2c_bus" : "/dev/i2c-10",
-				"i2c_addr" : 0x60,
-				"freq_MHz" : 322.265625,
-				"stability" : "20ppm",
-				"protected" : False,
-			},
+			# No I2C access
+#			"lti_ref_clk" : {
+#				"i2c_bus" : "/dev/i2c-9",
+#				"i2c_addr" : 0x60,
+#				"freq_MHz" : 240.474,
+#				"stability" : "20ppm",
+#				"protected" : False,
+#			},
+#
+#			"100G_ref_clk" : {
+#				"i2c_bus" : "/dev/i2c-10",
+#				"i2c_addr" : 0x60,
+#				"freq_MHz" : 322.265625,
+#				"stability" : "20ppm",
+#				"protected" : False,
+#			},
 
 			"ps_ref_clk" : {
 				"i2c_bus" : "/dev/i2c-11",
-- 
GitLab


From 32102a8028ea9a28beb42dafbbecfa47f05e8b73 Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Thu, 15 Aug 2024 19:29:46 +0000
Subject: [PATCH 20/23] Add settings for 28G transceiver

---
 controllers/sfp.py | 12 ++++++++++--
 models/sfp.py      |  5 +++++
 routes/__init__.py |  4 ++++
 templates/sfp.html | 40 ++++++++++++++++++++++++++++++++++++++++
 util/sfp.py        | 23 +++++++++++++++++++++++
 5 files changed, 82 insertions(+), 2 deletions(-)

diff --git a/controllers/sfp.py b/controllers/sfp.py
index efc1f9a..20d1dd5 100644
--- a/controllers/sfp.py
+++ b/controllers/sfp.py
@@ -1,4 +1,4 @@
-from flask import render_template, current_app
+from flask import render_template, current_app, redirect, request
 from markupsafe import Markup
 from models.sfp import SFP
 
@@ -8,6 +8,7 @@ class SFP_Controller():
 		self.config = config
 		self.skip_io = current_app.config["USER_SKIP_IO"] if "USER_SKIP_IO" in current_app.config else False
 		self.model = SFP(config) if not self.skip_io else None
+		self.name = "sfp"
 
 
 	def read_all(self):
@@ -25,5 +26,12 @@ class SFP_Controller():
 		else:
 			return render_template("error.html",
 				error="No data received from SFP devices")
-		
 
+
+	def configure(self, device_name):
+		if self.skip_io:
+			return redirect(f"/{self.name}")
+	
+		self.model.configure(device_name, dict(request.form))
+
+		return redirect(f"/{self.name}")
diff --git a/models/sfp.py b/models/sfp.py
index 622d755..af8797d 100644
--- a/models/sfp.py
+++ b/models/sfp.py
@@ -67,3 +67,8 @@ class SFP(SelfTestExecutor):
             msg = "OK"
 
         return { "status" : msg_level, "result" : msg}
+    
+    def configure(self, device_name, config):
+        cfg = self.devices[device_name]
+        device = SFP_Device(cfg)
+        device.configure(config)
diff --git a/routes/__init__.py b/routes/__init__.py
index 666b3a3..53106aa 100644
--- a/routes/__init__.py
+++ b/routes/__init__.py
@@ -104,6 +104,10 @@ def create_app(name, config):
         def sfp_read_all():
             return sfp.read_all()
 
+        @app.route("/sfp/configure/<device_name>", methods=["POST"])
+        def sfp_configure(device_name):
+            return sfp.configure(device_name)
+
         # api.add_resource(SFP_API.SFP_List, "/api/sfp",
         #     resource_class_kwargs={"model" : sfp.model})
 
diff --git a/templates/sfp.html b/templates/sfp.html
index 13adf5e..4bc6607 100644
--- a/templates/sfp.html
+++ b/templates/sfp.html
@@ -31,4 +31,44 @@
   </tbody>
 </table>
 
+<hr>
+
+<h2>Settings</h2>
+
+
+  <table class="table table-striped">
+    <thead>
+      <tr>
+        <th scope="col">Name</th>
+        <th scope="col">Enable CDR</th>
+        <th scope="col">CDR Rate</th>
+        <th scope="col"></th>
+      </tr>
+    </thead>
+    <tbody>
+
+  {% for label, params in data.items()  | sort  %}
+  {% if params["part_number"] == "B042804005170" %}
+  <form method="post" class="mb-4" role=form>
+  <tr>
+    <th>{{label}}</th>
+    <td>
+      <input type="checkbox" id="cdr" name="cdr" checked>
+    </td>
+    <td>
+      <input type="radio" id="25g" name="rate" value="25g">
+      <label for="25g">25.5-26G</label>
+      <input type="radio" id="28g" name="rate" value="28g" checked>
+      <label for="28g">27.5-28.1G</label>
+    </td>
+    <td>
+      <button class="btn btn-primary" type="submit" formaction="/sfp/configure/{{label}}">Update</button>
+    </td>
+  </tr>
+  </form>
+  {% endif %}
+  {% endfor %}
+  </tbody>
+  </table>
+
 {% endblock %}
\ No newline at end of file
diff --git a/util/sfp.py b/util/sfp.py
index b5ac315..760b54f 100644
--- a/util/sfp.py
+++ b/util/sfp.py
@@ -83,6 +83,8 @@ register_maps = {
             "address" : 22,
             "type" : "samtec"
         },
+        "cdr_enable" : 98,
+        "cdr_rate" : 99,
     },    
 
     "CERN-B-* Receiver" : regs_samtec_cern,
@@ -238,7 +240,28 @@ class SFP():
                 "error" : str(err)
             } 
             return data
+    
+    def configure(self, config):
+        with self.open_device():
+            part_number = self.__read_field("part_number")
 
+            # Only applies to the 28G transceivers
+
+            if part_number == "B042804005170":
+                cdr = 0
+                rate = 0
+
+                if('cdr' in config):
+                    cdr = 0xFF
+                
+                if(config['rate'] == '25g'):
+                    rate = 0
+                else:
+                    rate = 0xFF
+            
+                i2c_addr = self.device["i2c_addr"]
+                self.i2c.write(i2c_addr, self.registers["cdr_enable"], cdr)
+                self.i2c.write(i2c_addr, self.registers["cdr_rate"], rate)
 
 
 class TransactionValidator():
-- 
GitLab


From 31181d774989e674c7ae8a89ce7f427e98e31588 Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Thu, 15 Aug 2024 21:30:04 +0000
Subject: [PATCH 21/23] Added GTY reset

---
 controllers/sfp.py |  8 ++++++++
 models/board_id.py | 18 +++---------------
 models/sfp.py      |  9 ++++++++-
 routes/__init__.py |  4 ++++
 templates/sfp.html |  6 ++++++
 util/devmem.py     | 24 ++++++++++++++++++++++++
 6 files changed, 53 insertions(+), 16 deletions(-)
 create mode 100644 util/devmem.py

diff --git a/controllers/sfp.py b/controllers/sfp.py
index 20d1dd5..1c9fdd3 100644
--- a/controllers/sfp.py
+++ b/controllers/sfp.py
@@ -35,3 +35,11 @@ class SFP_Controller():
 		self.model.configure(device_name, dict(request.form))
 
 		return redirect(f"/{self.name}")
+
+	def reset(self):
+		if self.skip_io:
+			return redirect(f"/{self.name}")
+	
+		self.model.reset()
+
+		return redirect(f"/{self.name}")
diff --git a/models/board_id.py b/models/board_id.py
index 2b13549..cbce791 100644
--- a/models/board_id.py
+++ b/models/board_id.py
@@ -2,6 +2,7 @@ import pdb, re, mmap, os
 from os import path, sysconf
 from os.path import isdir
 from util.iio import load_file
+from util.devmem import devmem_read
 from models.self_test_executor import SelfTestExecutor, field_present
 
 
@@ -49,25 +50,12 @@ class BoardID(SelfTestExecutor):
         return hex(idcode)
 
 
-    def devmem(self, target_adr, length=4):
-        page_size= os.sysconf("SC_PAGE_SIZE")
-        reg_base = int(target_adr // page_size) * page_size
-        seek_size= int(target_adr %  page_size)
-        map_size = seek_size+length
-        fd = os.open("/dev/mem", os.O_RDWR|os.O_SYNC)
-        mem = mmap.mmap(fd, map_size, mmap.MAP_SHARED, mmap.PROT_READ|mmap.PROT_WRITE, offset=reg_base)
-        mem.seek(seek_size, os.SEEK_SET)
-        value = mem.read(length)
-        os.close(fd)
-        return value
-
-
     def get_dna(self):
         if not "DNAaddress" in self.config:
             return {"error" : "You must specify a memory address to retrieve the FPGA DNA!" }
         else:
-            dna_msb = int.from_bytes(self.devmem(self.config['DNAaddress'][0]), "little")
-            dna_lsb = int.from_bytes(self.devmem(self.config['DNAaddress'][1]), "little")
+            dna_msb = int.from_bytes(devmem_read(self.config['DNAaddress'][0]), "little")
+            dna_lsb = int.from_bytes(devmem_read(self.config['DNAaddress'][1]), "little")
             dna = ( dna_msb << 32 ) |  dna_lsb
             return hex(dna) 
 
diff --git a/models/sfp.py b/models/sfp.py
index af8797d..42c6028 100644
--- a/models/sfp.py
+++ b/models/sfp.py
@@ -1,6 +1,8 @@
 import pdb
 from util.sfp import SFP as SFP_Device
 from util.i2c import I2CError
+from util.devmem import devmem_write
+import time
 from models.self_test_executor import SelfTestExecutor
 
 
@@ -31,7 +33,7 @@ class SFP(SelfTestExecutor):
         msg_level = "success"
         msg = []
 
-        if len(result.keys()) is not 5:
+        if len(result.keys()) != 5:
             msg_level = "error"
 
         for label in self.config["mapping"].keys():
@@ -72,3 +74,8 @@ class SFP(SelfTestExecutor):
         cfg = self.devices[device_name]
         device = SFP_Device(cfg)
         device.configure(config)
+
+    def reset(self):
+        devmem_write(0x20100020000, int(0xFFFFFFFF).to_bytes(4, 'little'))
+        time.sleep(0.1)
+        devmem_write(0x20100020000, int(0).to_bytes(4, 'little'))
diff --git a/routes/__init__.py b/routes/__init__.py
index 53106aa..e831150 100644
--- a/routes/__init__.py
+++ b/routes/__init__.py
@@ -108,6 +108,10 @@ def create_app(name, config):
         def sfp_configure(device_name):
             return sfp.configure(device_name)
 
+        @app.route("/sfp/reset", methods=["POST"])
+        def sfp_reset():
+            return sfp.reset()
+
         # api.add_resource(SFP_API.SFP_List, "/api/sfp",
         #     resource_class_kwargs={"model" : sfp.model})
 
diff --git a/templates/sfp.html b/templates/sfp.html
index 4bc6607..3cf6827 100644
--- a/templates/sfp.html
+++ b/templates/sfp.html
@@ -71,4 +71,10 @@
   </tbody>
   </table>
 
+<hr>
+
+<form method="post" class="mb-4" role=form>
+  <button class="btn btn-primary" type="submit" formaction="/sfp/reset">Reset GTY Tx/Rx &amp; PLL</button>
+</form>
+
 {% endblock %}
\ No newline at end of file
diff --git a/util/devmem.py b/util/devmem.py
new file mode 100644
index 0000000..08f5f33
--- /dev/null
+++ b/util/devmem.py
@@ -0,0 +1,24 @@
+import os, mmap
+
+def devmem_read(target_adr, length=4):
+    page_size= os.sysconf("SC_PAGE_SIZE")
+    reg_base = int(target_adr // page_size) * page_size
+    seek_size= int(target_adr %  page_size)
+    map_size = seek_size+length
+    fd = os.open("/dev/mem", os.O_RDWR|os.O_SYNC)
+    mem = mmap.mmap(fd, map_size, mmap.MAP_SHARED, mmap.PROT_READ|mmap.PROT_WRITE, offset=reg_base)
+    mem.seek(seek_size, os.SEEK_SET)
+    value = mem.read(length)
+    os.close(fd)
+    return value
+
+def devmem_write(target_adr, value):
+    page_size= os.sysconf("SC_PAGE_SIZE")
+    reg_base = int(target_adr // page_size) * page_size
+    seek_size= int(target_adr %  page_size)
+    map_size = seek_size+len(value)
+    fd = os.open("/dev/mem", os.O_RDWR|os.O_SYNC)
+    mem = mmap.mmap(fd, map_size, mmap.MAP_SHARED, mmap.PROT_READ|mmap.PROT_WRITE, offset=reg_base)
+    mem.seek(seek_size, os.SEEK_SET)
+    value = mem.write(value)
+    os.close(fd)
-- 
GitLab


From 465f203d5b68fa9346e2949f0b68acad65aa2184 Mon Sep 17 00:00:00 2001
From: Eric Buschmann <eric.buschmann@cern.ch>
Date: Wed, 30 Oct 2024 15:21:50 -0400
Subject: [PATCH 22/23] Only compare part of the FireFly part number

---
 templates/sfp.html | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/templates/sfp.html b/templates/sfp.html
index 3cf6827..d0989b5 100644
--- a/templates/sfp.html
+++ b/templates/sfp.html
@@ -48,7 +48,7 @@
     <tbody>
 
   {% for label, params in data.items()  | sort  %}
-  {% if params["part_number"] == "B042804005170" %}
+  {% if params["part_number"][:3] == "B04" %}
   <form method="post" class="mb-4" role=form>
   <tr>
     <th>{{label}}</th>
@@ -56,9 +56,9 @@
       <input type="checkbox" id="cdr" name="cdr" checked>
     </td>
     <td>
-      <input type="radio" id="25g" name="rate" value="25g">
+      <input type="radio" id="25g" name="rate" value="25g" checked>
       <label for="25g">25.5-26G</label>
-      <input type="radio" id="28g" name="rate" value="28g" checked>
+      <input type="radio" id="28g" name="rate" value="28g">
       <label for="28g">27.5-28.1G</label>
     </td>
     <td>
@@ -77,4 +77,4 @@
   <button class="btn btn-primary" type="submit" formaction="/sfp/reset">Reset GTY Tx/Rx &amp; PLL</button>
 </form>
 
-{% endblock %}
\ No newline at end of file
+{% endblock %}
-- 
GitLab


From 999048dd08b4654a199784058c10eda68b5fd4ad Mon Sep 17 00:00:00 2001
From: "Carlo A. Gottardo" <cerlo.alberto.gottardo@cern.ch>
Date: Thu, 31 Oct 2024 16:33:05 +0100
Subject: [PATCH 23/23] add button to load default Si5345 configuration and
 reset GTY & PLL

---
 boards/flx182b.py        |  3 ++-
 controllers/si5345.py    | 50 +++++++++++++++++++++++++++-------------
 routes/__init__.py       | 10 +++++++-
 templates/self_test.html |  1 +
 4 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/boards/flx182b.py b/boards/flx182b.py
index 780d0e3..f0db6d4 100644
--- a/boards/flx182b.py
+++ b/boards/flx182b.py
@@ -249,6 +249,7 @@ config = {
 		"mapping" : {
 			"si5345_a" : {
 				"comment" : "SI5345_A (U60)",
+				"default_preset" : "Si5345-RevD-5345EVB2_si5345a_200_to_320-Registers.txt",
 				"i2c_bus" : "/dev/i2c-11",
 				"i2c_addr" : 0x68,
 				# if the device is found at one of the alternative addresses, 
@@ -263,6 +264,7 @@ config = {
 			},
 			"si5345_b" : {
 				"comment" : "SI5345_B (U59)",
+				"default_preset" : "Si5345-RevD-5345EVB2_si5345a_200_to_320-Registers.txt",
 				"i2c_bus" : "/dev/i2c-11",
 				"i2c_addr" : 0x69,
 				"i2c_addr_alt" : [0x7d],
@@ -274,7 +276,6 @@ config = {
 				"in_sel"		: 0b00, # device in_sel pins value
 			},
 		},
-
 		"self_test" : {
 			"description" : "SI5345 readout",
 		}
diff --git a/controllers/si5345.py b/controllers/si5345.py
index 87b6ae6..1514e1e 100644
--- a/controllers/si5345.py
+++ b/controllers/si5345.py
@@ -25,7 +25,7 @@ class SI5345_Controller():
 		if self.skip_io:
 			return render_template("error.html", error="I/O is disabled in test mode")
 
-		data = self.model.read_all()		
+		data = self.model.read_all()
 
 		if data:
 			forms = self.build_forms()
@@ -85,6 +85,13 @@ class SI5345_Controller():
 		return redirect(f"/{self.name}")
 
 
+	def load_configuration(self, device_name, register_data):
+		result = self.model.load_configuration(device_name, register_data)
+		if "error" in result:
+			return render_template(f"{self.name}.html", data=data, forms=forms,
+				error=result["error"])		
+
+
 	def upload_register_configuration(self):
 		if self.skip_io:
 			return redirect(f"/{self.name}")
@@ -100,14 +107,11 @@ class SI5345_Controller():
 		
 		f = form.file.data
 		register_data = f.stream.readlines()
-		result = self.model.load_configuration(device_name, register_data)
 
-		if "error" in result:
-			return render_template(f"{self.name}.html", data=data, forms=forms,
-				error=result["error"])
-		
+		self.load_configuration(device_name, register_data)
+
 		return redirect(f"/{self.name}")
-		
+
 
 	def select_preset_configuration(self):	
 		if self.skip_io:
@@ -117,12 +121,12 @@ class SI5345_Controller():
 		form = forms["preset_select"]
 		device_name = form.device_name.data
 		data = self.model.read_all()
-				
+
 		if not form.validate():				
 			return render_template(f"{self.name}.html", data=data, forms=forms,
-				error="Please select a valid preset!")		
-
-		file_name = path.join(current_app.root_path, "chip_configs", "si5345", form.preset_name.data)		
+				error="Please select a valid preset!")
+		
+		file_name = path.join(current_app.root_path, "chip_configs", "si5345", form.preset_name.data)
 
 		if not isfile(file_name):
 			return render_template(f"{self.name}.html", data=data, forms=forms,
@@ -130,14 +134,28 @@ class SI5345_Controller():
 
 		with open(file_name, 'r') as file:
 			register_data = file.readlines()	
+			self.load_configuration(device_name, register_data)
 
-		result = self.model.load_configuration(device_name, register_data)
+		return redirect(f"/{self.name}")
 
-		if "error" in result:
-			return render_template(f"{self.name}.html", data=data, forms=forms,
-				error=result["error"])
 
-		return redirect(f"/{self.name}")
+	def load_default_preset(self):
+		if self.skip_io:
+			return redirect(f"/{self.name}")
+
+		for device in self.config['mapping'].keys():
+			preset = self.config['mapping'][device]['default_preset']
+			file_name = path.join(current_app.root_path, "chip_configs", "si5345", preset)
+
+			if not isfile(file_name):
+				forms = self.build_forms()
+				data = self.model.read_all()
+				return render_template(f"{self.name}.html", data=data, forms=forms,
+					error="Preset file doesn't exist!")
+
+			with open(file_name, 'r') as file:
+				register_data = file.readlines()
+				self.load_configuration(device, register_data)
 
 
 	def _reset(self, device_name, reset_function):
diff --git a/routes/__init__.py b/routes/__init__.py
index e831150..def5f40 100644
--- a/routes/__init__.py
+++ b/routes/__init__.py
@@ -182,7 +182,7 @@ def create_app(name, config):
             return si5345.update_input_config()
 
         @app.route("/si5345/select_preset", methods=["POST"])
-        def si5345_select_preset():
+        def si5345_select_preset(use_default = False):
             return si5345.select_preset_configuration()
 
         @app.route("/si5345/upload_config", methods=["POST"])
@@ -197,6 +197,13 @@ def create_app(name, config):
         def si5345_hard_reset(device_name):
             return si5345.hard_rst(device_name)
 
+    if "si5345" in board_config and "sfp" in board_config:
+        @app.route("/configure_firefly", methods=["POST"])
+        def configure_firefly():
+            si5345.load_default_preset()
+            sfp.reset()
+            return redirect(request.referrer)
+
 
     if "si53156" in board_config:
         with app.app_context():
@@ -212,6 +219,7 @@ def create_app(name, config):
             return si53156.configure()
 
 
+
     # Initialize user-space test pages and api
     for key, resource in board_config["process_runners"].items():
         name = resource["name"]
diff --git a/templates/self_test.html b/templates/self_test.html
index 3bc003b..5ae644f 100644
--- a/templates/self_test.html
+++ b/templates/self_test.html
@@ -58,6 +58,7 @@
         <button class="btn btn-danger" type="submit" formaction="{{url_for('self_test_stop')}}">Stop</button>    
     {% else %}
         <button class="btn btn-primary" type="submit" formaction="{{url_for('self_test_start')}}">Start</button>
+        <button class="btn btn-primary" type="submit" formaction="{{url_for('configure_firefly')}}">Configure for eye scans</button>
     {% endif %}
 </form>
 
-- 
GitLab