Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • wittgen/optoboard_felix
  • bat/optoboard_felix
2 results
Show changes
Commits on Source (55)
Showing with 381 additions and 385 deletions
......@@ -18,4 +18,4 @@ poetry.lock
dist
.python-version
Eye_Diagram*
lpgbt_calibration.csv
\ No newline at end of file
lpgbt_calibration.csv
stages:
- publish_tag
- trigger_opto_container_pipeline
variables:
GL_PROJECT: "optoboard_felix"
......@@ -17,3 +18,11 @@ publish_tag:
only:
- tags
trigger_opto_container_pipeline:
needs:
- publish_tag
stage: trigger_opto_container_pipeline
script:
- curl --request POST --fail -F token=$GL_OPTO_CONTAINER_PIPELINE_TRIGGER -F ref=master https://gitlab.cern.ch/api/v4/projects/152254/trigger/pipeline
only:
- tags
# Most recent changes
- The parameter "woflxcore" of the script InitOpto.py has been substituted with "commToolName" (string) that accepts "flpgbtconf", "ic-over-netio" or "lpgbt-com" as values.
- Communication via felixClient is now supported, but still WIP. The path to the "bus" folder of the felix software in use which is stored in driver/CommConfig.py and obtained from environment variables. We recommend to source the same felix software used to run felix client. In case of troubles, this is one of the possible causes. One has to ***source the setup.sh file in the lpgbt-com folder***.
- The parameter "woflxcore" of the script InitOpto.py has been substituted with "commToolName" (string) that accepts "flpgbtconf", "flpgbtconfAPI", "ic-over-netio", "itk-ic-over-netio-next" or "lpgbt-com" as values.
- Communication via felixClient is now supported, but still WIP and doesn't work in the containerized software yet. The path to the "bus" folder of the felix software in use which is stored in driver/CommConfig.py and obtained from environment variables. We recommend to source the same felix software used to run felix client. In case of troubles, this is one of the possible causes. One has to ***source the setup.sh file in the lpgbt-com folder***.
- The optoboard_felix software is now a python package. The old version has been tagged as [0.0.0](https://gitlab.cern.ch/bat/optoboard_felix/-/tree/0.0.0?ref_type=tags) and is still available, but won't be supported.
# Optoboard FELIX Configuration Interface
......@@ -17,24 +17,24 @@ This is the backend to the [ITk demonstrator software](https://gitlab.cern.ch/at
Please check our documentation: [https://optoboard-system.docs.cern.ch](https://optoboard-system.docs.cern.ch/). Support requests through our [Bern-Optoboard Mattermost](https://mattermost.web.cern.ch/bat-optoboard) (sign up [here](https://mattermost.web.cern.ch/signup_user_complete/?id=778hfkdwfjd5mm899qhs8m9m5w)).
## Prerequisites
### Prerequisites
Everything hardware related or software that is needed for running a full data transmission setup is summarised in [our documentation](https://optoboard-system.docs.cern.ch/software/). The dependency installation is handled using the `pyproject.toml` file.
# How to configure/use an Optoboard
**The suggested way to use the `optoboard_felix` software is via its containerized version: follow the instructions in [https://gitlab.cern.ch/atlas-itk-pixel-systemtest/itk-demo-sw/containers/optoboard-container](https://gitlab.cern.ch/atlas-itk-pixel-systemtest/itk-demo-sw/containers/optoboard-container).** It is also possible to locally install the `optoboard_felix` software using the following instructions.
1. Install the package from the Package Registry:
```
```bash
pip install optoboard-felix --index-url https://gitlab.cern.ch/api/v4/projects/113584/packages/pypi/simple
```
2. Set up your FELIX environment, configure FELIX card and check link alignment
3. Run `felixcore` in a separate terminal or `felixClient`
4. After the installation of the optoboard_felix package, the `InitOpto` command is available (more on how to use it later). Notice that one has to specify the communication tool (`ic-over-netio` <-> `felixcore`, `lpgbt-com` <-> `felixClient`) to be used for the IC communication when creating instances of the `Optoboard` class with `InitOpto`
3. Run `felixcore` in a separate terminal or `felixStar`
4. After the installation of the optoboard_felix package, the `InitOpto` command is available (more on how to use it later). Notice that one has to specify the communication tool (`ic-over-netio` <-> `felixcore`, `lpgbt-com`, `itk-ic-over-netio-next` <-> `felixStar`) to be used for the IC communication when creating instances of the `Optoboard` class with `InitOpto`
## Configure through CLI
The InitOpto command is a shim for the InitOpto.py script.
```
```bash
InitOpto [-h]
[-optoListPath OPTOLISTPATH]
[-config_path CONFIG_PATH]
......@@ -42,7 +42,7 @@ InitOpto [-h]
-vtrx_v {1.2,1.3}
-flx_G {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}
-flx_d FLX_D
[-commToolName {"flpgbtconf","ic-over-netio","lpgbt-com"}]
[-commToolName {"flpgbtconf","flpgbtconfAPI", "ic-over-netio","itk-ic-over-netio-next", "lpgbt-com"}]
[-woflxcore {0,1}]
[-configure {0,1}]
[-debug {0,1}]
......@@ -56,14 +56,20 @@ For an explanation on the arguments, run `InitOpto -h` or consult the [API](http
### Examples
Minimal example to configure a full Optoboard using default configuration files and using the Python session afterwards:
```
```python
InitOpto -optoboard_serial 30000000 -vtrx_v 1.3 -flx_G 0 -flx_d 0 -i -configure 1
>>> opto.lpgbt1.check_PUSM_status()
>>> opto.lpgbt1.swap_rx_polarity(0) # invert rx polarity for channel 0 (possible values: 0 -> 5)
>>> opto.lpgbt1.swap_tx_polarity(0) # invert tx polarity for channel 0 (possible values: 0 -> 7)
```
or by providing a config like `30000000_test_lpgbtv1_vtrxv1_3.json`:
```
```bash
InitOpto -config_path configs/30000000_test_lpgbtv1_vtrxv1_3.json -configure 1 -i
```
Notice that if one uses the `config_path` argument, then `optoboard_serial`, `vtrx_v`, `flx_G` and `flx_d` **must be specified inside the configuration file** by adding a key-value pair of the type:
```python
......@@ -88,7 +94,8 @@ The script has default configs according to the lpGBT and VTRx+ quad laser drive
## Create multiple optoboard instances
Multiple optoboard instances can be initialized at once by providing a json following the model of `optoConfigList_example.json` to `InitOpto`:
```
```bash
InitOpto -optoListPath optoConfigList_example.json -i
```
......@@ -141,3 +148,13 @@ opto.vtrx.read("GCR", None) # check enabled fibre channels of VTRx+ (only v1.
opto1.bertScan("lpgbt3", "gbcr3", 0, 6, 11) # perform an GBCR3 equalizer scan with BERT on lpGBT3
```
## Info for developers
To test changes in the code it is suggested to install the package in a virtual environment by using `poetry`:
```bash
poetry install
poetry shell
```
Make sure to have sourced the felix software beforehand.
......@@ -119,7 +119,7 @@ def main():
parser.add_argument("-commToolName", "--commToolName",
type=str,
default="ic-over-netio",
choices=["flpgbtconf", "ic-over-netio", "lpgbt-com"],
choices=["flpgbtconf", "flpgbtconfAPI", "ic-over-netio", "itk-ic-over-netio-next", "lpgbt-com"],
help="communicate with subprocesses, does not rely on felix-core (default: %(default)s)")
parser.add_argument("-CONFIGDB_ADDRESS", "--CONFIGDB_ADDRESS",
......
......@@ -2069,13 +2069,13 @@
},
"CH1UPLINK1":
{
"CH1CTLEHF1SR": [11, 11, 11, 11],
"CH1CTLEHF2SR": [11, 11, 11, 11]
"CH1CTLEHF1SR": [8, 8, 8, 8],
"CH1CTLEHF2SR": [8, 8, 8, 8]
},
"CH1UPLINK2":
{
"CH1CTLEHF3SR": [11, 11, 11, 11],
"CH1CTLEMFSR": [11, 11, 11, 11]
"CH1CTLEHF3SR": [8, 8, 8, 8],
"CH1CTLEMFSR": [8, 8, 8, 8]
},
"CH1UPLINK3":
{
......@@ -2091,13 +2091,13 @@
},
"CH2UPLINK1":
{
"CH2CTLEHF1SR": [11, 11, 11, 11],
"CH2CTLEHF2SR": [11, 11, 11, 11]
"CH2CTLEHF1SR": [8, 8, 8, 8],
"CH2CTLEHF2SR": [8, 8, 8, 8]
},
"CH2UPLINK2":
{
"CH2CTLEHF3SR": [11, 11, 11, 11],
"CH2CTLEMFSR": [11, 11, 11, 11]
"CH2CTLEHF3SR": [8, 8, 8, 8],
"CH2CTLEMFSR": [8, 8, 8, 8]
},
"CH2UPLINK3":
{
......@@ -2113,13 +2113,13 @@
},
"CH3UPLINK1":
{
"CH3CTLEHF1SR": [11, 11, 11, 11],
"CH3CTLEHF2SR": [11, 11, 11, 11]
"CH3CTLEHF1SR": [8, 8, 8, 8],
"CH3CTLEHF2SR": [8, 8, 8, 8]
},
"CH3UPLINK2":
{
"CH3CTLEHF3SR": [11, 11, 11, 11],
"CH3CTLEMFSR": [11, 11, 11, 11]
"CH3CTLEHF3SR": [8, 8, 8, 8],
"CH3CTLEMFSR": [8, 8, 8, 8]
},
"CH3UPLINK3":
{
......@@ -2135,13 +2135,13 @@
},
"CH4UPLINK1":
{
"CH4CTLEHF1SR": [11, 11, 11, 11],
"CH4CTLEHF2SR": [11, 11, 11, 11]
"CH4CTLEHF1SR": [8, 8, 8, 8],
"CH4CTLEHF2SR": [8, 8, 8, 8]
},
"CH4UPLINK2":
{
"CH4CTLEHF3SR": [11, 11, 11, 11],
"CH4CTLEMFSR": [11, 11, 11, 11]
"CH4CTLEHF3SR": [8, 8, 8, 8],
"CH4CTLEMFSR": [8, 8, 8, 8]
},
"CH4UPLINK3":
{
......@@ -2157,13 +2157,13 @@
},
"CH5UPLINK1":
{
"CH5CTLEHF1SR": [11, 11, 11, 11],
"CH5CTLEHF2SR": [11, 11, 11, 11]
"CH5CTLEHF1SR": [8, 8, 8, 8],
"CH5CTLEHF2SR": [8, 8, 8, 8]
},
"CH5UPLINK2":
{
"CH5CTLEHF3SR": [11, 11, 11, 11],
"CH5CTLEMFSR": [11, 11, 11, 11]
"CH5CTLEHF3SR": [8, 8, 8, 8],
"CH5CTLEMFSR": [8, 8, 8, 8]
},
"CH5UPLINK3":
{
......@@ -2179,13 +2179,13 @@
},
"CH6UPLINK1":
{
"CH6CTLEHF1SR": [11, 11, 11, 11],
"CH6CTLEHF2SR": [11, 11, 11, 11]
"CH6CTLEHF1SR": [8, 8, 8, 8],
"CH6CTLEHF2SR": [8, 8, 8, 8]
},
"CH6UPLINK2":
{
"CH6CTLEHF3SR": [11, 11, 11, 11],
"CH6CTLEMFSR": [11, 11, 11, 11]
"CH6CTLEHF3SR": [8, 8, 8, 8],
"CH6CTLEMFSR": [8, 8, 8, 8]
},
"CH6UPLINK3":
{
......
......@@ -2069,13 +2069,13 @@
},
"CH1UPLINK1":
{
"CH1CTLEHF1SR": [11, 11, 11, 11],
"CH1CTLEHF2SR": [11, 11, 11, 11]
"CH1CTLEHF1SR": [8, 8, 8, 8],
"CH1CTLEHF2SR": [8, 8, 8, 8]
},
"CH1UPLINK2":
{
"CH1CTLEHF3SR": [11, 11, 11, 11],
"CH1CTLEMFSR": [11, 11, 11, 11]
"CH1CTLEHF3SR": [8, 8, 8, 8],
"CH1CTLEMFSR": [8, 8, 8, 8]
},
"CH1UPLINK3":
{
......@@ -2091,13 +2091,13 @@
},
"CH2UPLINK1":
{
"CH2CTLEHF1SR": [11, 11, 11, 11],
"CH2CTLEHF2SR": [11, 11, 11, 11]
"CH2CTLEHF1SR": [8, 8, 8, 8],
"CH2CTLEHF2SR": [8, 8, 8, 8]
},
"CH2UPLINK2":
{
"CH2CTLEHF3SR": [11, 11, 11, 11],
"CH2CTLEMFSR": [11, 11, 11, 11]
"CH2CTLEHF3SR": [8, 8, 8, 8],
"CH2CTLEMFSR": [8, 8, 8, 8]
},
"CH2UPLINK3":
{
......@@ -2113,13 +2113,13 @@
},
"CH3UPLINK1":
{
"CH3CTLEHF1SR": [11, 11, 11, 11],
"CH3CTLEHF2SR": [11, 11, 11, 11]
"CH3CTLEHF1SR": [8, 8, 8, 8],
"CH3CTLEHF2SR": [8, 8, 8, 8]
},
"CH3UPLINK2":
{
"CH3CTLEHF3SR": [11, 11, 11, 11],
"CH3CTLEMFSR": [11, 11, 11, 11]
"CH3CTLEHF3SR": [8, 8, 8, 8],
"CH3CTLEMFSR": [8, 8, 8, 8]
},
"CH3UPLINK3":
{
......@@ -2135,13 +2135,13 @@
},
"CH4UPLINK1":
{
"CH4CTLEHF1SR": [11, 11, 11, 11],
"CH4CTLEHF2SR": [11, 11, 11, 11]
"CH4CTLEHF1SR": [8, 8, 8, 8],
"CH4CTLEHF2SR": [8, 8, 8, 8]
},
"CH4UPLINK2":
{
"CH4CTLEHF3SR": [11, 11, 11, 11],
"CH4CTLEMFSR": [11, 11, 11, 11]
"CH4CTLEHF3SR": [8, 8, 8, 8],
"CH4CTLEMFSR": [8, 8, 8, 8]
},
"CH4UPLINK3":
{
......@@ -2157,13 +2157,13 @@
},
"CH5UPLINK1":
{
"CH5CTLEHF1SR": [11, 11, 11, 11],
"CH5CTLEHF2SR": [11, 11, 11, 11]
"CH5CTLEHF1SR": [8, 8, 8, 8],
"CH5CTLEHF2SR": [8, 8, 8, 8]
},
"CH5UPLINK2":
{
"CH5CTLEHF3SR": [11, 11, 11, 11],
"CH5CTLEMFSR": [11, 11, 11, 11]
"CH5CTLEHF3SR": [8, 8, 8, 8],
"CH5CTLEMFSR": [8, 8, 8, 8]
},
"CH5UPLINK3":
{
......@@ -2179,13 +2179,13 @@
},
"CH6UPLINK1":
{
"CH6CTLEHF1SR": [11, 11, 11, 11],
"CH6CTLEHF2SR": [11, 11, 11, 11]
"CH6CTLEHF1SR": [8, 8, 8, 8],
"CH6CTLEHF2SR": [8, 8, 8, 8]
},
"CH6UPLINK2":
{
"CH6CTLEHF3SR": [11, 11, 11, 11],
"CH6CTLEMFSR": [11, 11, 11, 11]
"CH6CTLEHF3SR": [8, 8, 8, 8],
"CH6CTLEMFSR": [8, 8, 8, 8]
},
"CH6UPLINK3":
{
......
......@@ -2069,13 +2069,13 @@
},
"CH1UPLINK1":
{
"CH1CTLEHF1SR": [11, 11, 11, 11],
"CH1CTLEHF2SR": [11, 11, 11, 11]
"CH1CTLEHF1SR": [8, 8, 8, 8],
"CH1CTLEHF2SR": [8, 8, 8, 8]
},
"CH1UPLINK2":
{
"CH1CTLEHF3SR": [11, 11, 11, 11],
"CH1CTLEMFSR": [11, 11, 11, 11]
"CH1CTLEHF3SR": [8, 8, 8, 8],
"CH1CTLEMFSR": [8, 8, 8, 8]
},
"CH1UPLINK3":
{
......@@ -2091,13 +2091,13 @@
},
"CH2UPLINK1":
{
"CH2CTLEHF1SR": [11, 11, 11, 11],
"CH2CTLEHF2SR": [11, 11, 11, 11]
"CH2CTLEHF1SR": [8, 8, 8, 8],
"CH2CTLEHF2SR": [8, 8, 8, 8]
},
"CH2UPLINK2":
{
"CH2CTLEHF3SR": [11, 11, 11, 11],
"CH2CTLEMFSR": [11, 11, 11, 11]
"CH2CTLEHF3SR": [8, 8, 8, 8],
"CH2CTLEMFSR": [8, 8, 8, 8]
},
"CH2UPLINK3":
{
......@@ -2113,13 +2113,13 @@
},
"CH3UPLINK1":
{
"CH3CTLEHF1SR": [11, 11, 11, 11],
"CH3CTLEHF2SR": [11, 11, 11, 11]
"CH3CTLEHF1SR": [8, 8, 8, 8],
"CH3CTLEHF2SR": [8, 8, 8, 8]
},
"CH3UPLINK2":
{
"CH3CTLEHF3SR": [11, 11, 11, 11],
"CH3CTLEMFSR": [11, 11, 11, 11]
"CH3CTLEHF3SR": [8, 8, 8, 8],
"CH3CTLEMFSR": [8, 8, 8, 8]
},
"CH3UPLINK3":
{
......@@ -2135,13 +2135,13 @@
},
"CH4UPLINK1":
{
"CH4CTLEHF1SR": [11, 11, 11, 11],
"CH4CTLEHF2SR": [11, 11, 11, 11]
"CH4CTLEHF1SR": [8, 8, 8, 8],
"CH4CTLEHF2SR": [8, 8, 8, 8]
},
"CH4UPLINK2":
{
"CH4CTLEHF3SR": [11, 11, 11, 11],
"CH4CTLEMFSR": [11, 11, 11, 11]
"CH4CTLEHF3SR": [8, 8, 8, 8],
"CH4CTLEMFSR": [8, 8, 8, 8]
},
"CH4UPLINK3":
{
......@@ -2157,13 +2157,13 @@
},
"CH5UPLINK1":
{
"CH5CTLEHF1SR": [11, 11, 11, 11],
"CH5CTLEHF2SR": [11, 11, 11, 11]
"CH5CTLEHF1SR": [8, 8, 8, 8],
"CH5CTLEHF2SR": [8, 8, 8, 8]
},
"CH5UPLINK2":
{
"CH5CTLEHF3SR": [11, 11, 11, 11],
"CH5CTLEMFSR": [11, 11, 11, 11]
"CH5CTLEHF3SR": [8, 8, 8, 8],
"CH5CTLEMFSR": [8, 8, 8, 8]
},
"CH5UPLINK3":
{
......@@ -2179,13 +2179,13 @@
},
"CH6UPLINK1":
{
"CH6CTLEHF1SR": [11, 11, 11, 11],
"CH6CTLEHF2SR": [11, 11, 11, 11]
"CH6CTLEHF1SR": [8, 8, 8, 8],
"CH6CTLEHF2SR": [8, 8, 8, 8]
},
"CH6UPLINK2":
{
"CH6CTLEHF3SR": [11, 11, 11, 11],
"CH6CTLEMFSR": [11, 11, 11, 11]
"CH6CTLEHF3SR": [8, 8, 8, 8],
"CH6CTLEMFSR": [8, 8, 8, 8]
},
"CH6UPLINK3":
{
......
......@@ -6,5 +6,6 @@ import os
CommConfig = {
"IChandler_config": {"ip": "127.0.0.1", "tx_port": 12340, "rx_port": 12350, "tx_tag": 17, "rx_tag": 29},
"ICNetioNextHandler_config": {"tx_tag": 0x1000000000118000, "rx_tag": 0x10000000001d0000, "interface": "lo", "bus_dir":"/tmp/bus" },
"lpgbt_com_config": {"local_eth": "eth0", "bus_dir": str(os.environ.get("VIRTUAL_ENV") or "") + "/../bus", "tx_tag": 17, "rx_tag": 29},
}
......@@ -5,6 +5,7 @@ import re
import subprocess
import sys
import time
import requests
from optoboard_felix.driver.log import logger
from optoboard_felix.driver.CommConfig import *
......@@ -18,6 +19,14 @@ try:
from libic_comms import IChandler
except ImportError:
logger.warning("IChandler library not found, check if installed with FELIX software! Check the README on how to install.")
try:
from libitk_ic_over_netio_next import ICNetioNextHandler
except ImportError:
logger.warning("ICNetioNextHandler library not found, check if installed with FELIX software! Check the README on how to install.")
try:
import pyLpGBT
from pyconnect import *
......@@ -82,6 +91,18 @@ class CommWrapper():
self.ICHandler.connect()
except:
logger.warning("ICHandler.connect() failed! Check if the method is available and if the ICHandler configuration is correct!")
elif commToolName == "itk-ic-over-netio-next":
if "libitk_ic_over_netio_next" not in sys.modules:
logger.error("ICNetioNextHandler library libitk_ic_over_netio_next not found, setting commToolName to \"flpgbtconf\"! To configure you now need to disable felixcore and use \"flpgbtconf\" or install the library!")
self.commToolName = "flpgbtconf"
else:
self.ICHandler = ICNetioNextHandler(CommConfig["ICNetioNextHandler_config"]["interface"], CommConfig["ICNetioNextHandler_config"]["bus_dir"],
CommConfig["ICNetioNextHandler_config"]["tx_tag"] + 0x0000000000400000*int(self.flx_G), CommConfig["ICNetioNextHandler_config"]["rx_tag"] + 0x0000000000400000*int(self.flx_G) , False, self.lpgbt_v, self.lpgbt_master_addr)
logger.info("TX: ",CommConfig["ICNetioNextHandler_config"]["tx_tag"] + 0x0000000000400000*int(self.flx_G),"RX:",CommConfig["ICNetioNextHandler_config"]["rx_tag"] + 0x0000000000400000*int(self.flx_G))
try:
self.ICHandler.connect()
except:
logger.warning("ICHandler.connect() failed! Check if the method is available and if the ICHandler configuration is correct!")
elif commToolName == "lpgbt-com":
if "pyLpGBT" not in sys.modules:
logger.error("pyLpGBT library libic_comms not found, setting commToolName to \"flpgbtconf\"! To configure you now need to disable felixclient and use \"flpgbtconf\" or install the library!")
......@@ -178,7 +199,62 @@ class CommWrapper():
return read_reg, read
elif self.commToolName == "ic-over-netio":
elif self.commToolName == "flpgbtconfAPI":
flpgbtconfAPI_tries = 0
url = os.environ.get("FELIX_BACKEND_URL") if os.environ.get("FELIX_BACKEND_URL") is not None else "http://localhost:8001"
if reg_data is None:
endpoint = url + "/readoptoboard/"
else:
endpoint = url + "/writeoptoboard/"
endpoint += "-G" + str(self.flx_G) + "-d" + str(self.flx_d) + "-I" + hex(self.lpgbt_master_addr)
if self.lpgbt_v:
endpoint += "-1"
endpoint += "/" + reg_name
if reg_data is not None:
endpoint += "/" + str(reg_data)
logger.debug("comm_wrapper - endpoint: %s", endpoint)
while flpgbtconfAPI_tries<max_tries:
output = requests.get(endpoint, headers={"content-type": "application/json"}).json()["result"].split("\n")
logger.debug("comm_wrapper - readlines() output - output_lines: %s", output)
output_last_line = output[-2]
logger.debug("comm_wrapper - output_last_line: %s", output_last_line)
if not readback_bool:
return "Write-only operation"
if "Reply not received" in output_last_line:
flpgbtconfAPI_tries += 1
logger.warning("Reply not received from FELIX! - tried %s time(s)", flpgbtconfAPI_tries)
if flpgbtconfAPI_tries>max_tries-1:
logger.error("comm_wrapper - no reply from FELIX. Aborting, check link, Optoboard serial, config file or FELIX settings! Also check that the url of the FELIX Server is correct!")
raise Exception("No FELIX connection.")
else: break
read_reg = output_last_line.split(":")[0]
read = output_last_line.split("(")[1].split(")")[0]
logger.debug("comm_wrapper - returning - read_reg: %s, read: %s", read_reg, read)
flpgbtconf_tries = 0
return read_reg, read
elif self.commToolName == "ic-over-netio" or self.commToolName == "itk-ic-over-netio-next":
ICHandler_tries = 0
while ICHandler_tries<max_tries:
......
......@@ -143,3 +143,39 @@ class Gbcr(Hardware):
self.write_read("CH" + str(channel+1) + "UPLINK3", "CH" + str(channel+1) + "CLKDELAY", phase)
logger.info("%s channel %s retiming mode enabled and set to phase %s!", self.device, channel, phase)
def set_equalizer(self,channel_lpgbt,mf_eq=None,hf_eq=None):
"""Change the settings of the equalization on one of the channels of the gbcr and read it back.
Args:
channel_lpgbt (int): channel of lpGBT
mf_eq (int, optional): middle frequencies of the gbcr equalization strength
hf_eq (int, optional): high frequencies of the gbcr equalization strength
"""
channel_gbcr=6-channel_lpgbt
if self.gbcr_v == 2:
if mf_eq == None:
mid_freq = self.read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEMFSR")
else:
mid_freq = self.write_read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEMFSR", mf_eq)
if hf_eq == None:
high_freq=self.read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEHFSR")
else:
high_freq = self.write_read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEHF1SR", hf_eq)
logger.info("Current gbcr equaltization strengh for version 2 on channel %s: mid_freq %s/15 and high_freq %s/15",channel_gbcr,mid_freq, high_freq)
if self.gbcr_v == 3:
if mf_eq == None:
mid_freq = self.read("CH" + str(channel_gbcr) + "UPLINK2", "CH" + str(channel_gbcr) + "CTLEMFSR")
else:
mid_freq = self.write_read("CH" + str(channel_gbcr) + "UPLINK2", "CH" + str(channel_gbcr) + "CTLEMFSR", mf_eq)
if hf_eq == None:
high_freq1 = self.read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEHF1SR")
high_freq2 = self.read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEHF2SR")
high_freq3 = self.read("CH" + str(channel_gbcr) + "UPLINK2", "CH" + str(channel_gbcr) + "CTLEHF3SR")
else:
high_freq1 = self.write_read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEHF1SR", hf_eq)
high_freq2 = self.write_read("CH" + str(channel_gbcr) + "UPLINK1", "CH" + str(channel_gbcr) + "CTLEHF2SR", hf_eq)
high_freq3 = self.write_read("CH" + str(channel_gbcr) + "UPLINK2", "CH" + str(channel_gbcr) + "CTLEHF3SR", hf_eq)
logger.info("Current gbcr equaltization strengh for version 3 on channel %s: mid_freq %s/15, high_freq1 %s/15, high_freq2 %s/15, high_freq3 %s/15",channel_gbcr,mid_freq, high_freq1, high_freq2, high_freq3)
\ No newline at end of file
# pylint: disable=line-too-long, invalid-name, import-error
"""Lpgbt module."""
import csv
import time
import math
from time import sleep
import traceback
import requests
import os
from optoboard_felix.driver.Hardware import Hardware, ordered_configuration
from optoboard_felix.driver.log import logger, LoggerFormat
......@@ -66,7 +65,7 @@ class Lpgbt(Hardware):
self.optoboard_serial = optoboard_serial
self.optoboard_v = optoboard_v
self.efused = efused
self.lpgbt_calibration = {}
self.state = None
self.ready = False
......@@ -598,6 +597,61 @@ class Lpgbt(Hardware):
return dict_EPRX_locking
def check_EPTX_status(self):
"""Check lpGBTs elink downlink status for all channels.
Specifically: outputs frequency and polarity of the downlinks.
Downlink numbering convention is as follows:
dl 0 == (e-group,channel) 00
1 == 02
2 == 10
3 == 12
4 == 20
5 == 22
6 == 30
7 == 32
Returns:
Nested dictionary with the status of the 8 downlinks with keys:
["downlink"+str(dl)]["polarity"] and ["downlink"+str(dl)]["data_rate"]
polarity (string): 0=NON INVERTED, 1=INVERTED
data_rate (int): possible values in [0,3], 2 corresponds to 160Mb/s
"""
output_driver = ["01_00","03_02","11_10","13_12","21_20","23_22","31_30","33_32"]
group = [0,0,1,1,2,2,3,3]
channel = [0,2,0,2,0,2,0,2]
downlink_bitrates = ["0 Mb/s", "80 Mb/s", "160 Mb/s", "320 Mb/s"]
dict_downlink_status = {}
for dl in range(0,8):
dict_downlink_status["EPTX"+str(group[dl])+str(channel[dl])] = {}
polarity = self.read("EPTX"+str(output_driver[dl])+"CHNCNTR","EPTX"+str(group[dl])+str(channel[dl])+"INVERT")
if polarity:
polarity_str = "INVERTED (1)"
else:
polarity_str = "NOT INVERTED (0)"
dict_downlink_status["EPTX"+str(group[dl])+str(channel[dl])]["polarity"] = polarity_str
logger.info("Status of EPTX%s%s:", str(group[dl]), str(channel[dl]))
logger.info("The polarity is %s%s%s", LoggerFormat.yellow, polarity_str, LoggerFormat.reset)
data_rate = self.read("EPTXDATARATE","EPTX"+str(group[dl])+"DATARATE")
dict_downlink_status["EPTX"+str(group[dl])+str(channel[dl])]["data_rate"] = data_rate
if data_rate == 2:
logger.info("The data rate is %s%s%s", LoggerFormat.green, downlink_bitrates[data_rate], LoggerFormat.reset)
else:
logger.info("The data rate is %s%s%s", LoggerFormat.red, downlink_bitrates[data_rate], LoggerFormat.reset)
return dict_downlink_status
def check_EPRX_DLL_locking(self):
"""Check lpGBTs elink uplink DLL locking for all channels.
......@@ -701,59 +755,6 @@ class Lpgbt(Hardware):
logger.info("%s e-link %s (EPRX group %s channel 0) is %snot enabled%s", self.device, G_str, G_str, LoggerFormat.red, LoggerFormat.reset)
return dict_EPRX_status
def check_EPTX_status(self):
"""Check lpGBTs elink downlink status for all channels.
Specifically: outputs frequency and polarity of the downlinks.
Downlink numbering convention is as follows:
dl 0 == (e-group,channel) 00
1 == 02
2 == 10
3 == 12
4 == 20
5 == 22
6 == 30
7 == 32
Returns:
Nested dictionary with the status of the 8 downlinks with keys:
["EPTX"+str(dl)]["polarity"] and ["EPTX"+str(dl)]["data_rate"]
polarity (string): 0=NON INVERTED, 1=INVERTED
data_rate (int): possible values in [0,3], 2 corresponds to 160Mb/s
"""
output_driver = ["01_00","03_02","11_10","13_12","21_20","23_22","31_30","33_32"]
group = [0,0,1,1,2,2,3,3]
channel = [0,2,0,2,0,2,0,2]
dict_EPTX_status = {}
for dl in range(0,8):
dict_EPTX_status["EPTX"+str(dl)] = {}
polarity = self.read("EPTX"+str(output_driver[dl])+"CHNCNTR","EPTX"+str(group[dl])+str(channel[dl])+"INVERT")
if polarity:
polarity_str = "INVERTED"
else:
polarity_str = "NOT INVERTED"
dict_EPTX_status["EPTX"+str(dl)]["polarity"] = polarity_str
logger.info("Status of EPTX %s:", str(dl))
logger.info("The polarity is %s",polarity_str)
data_rate = self.read("EPTXDATARATE","EPTX"+str(group[dl])+"DATARATE")
dict_EPTX_status["EPTX"+str(dl)]["data_rate"] = data_rate
logger.info("The data rate is %s",str(data_rate))
logger.info("\n")
return dict_EPTX_status
def bypass_ul(self):
"""Set the lpGBTs 10.24 Gb/s uplink to output a 40 MHz clock and bypass the interleaver, scrambler and FEC encoder.
......@@ -1139,7 +1140,7 @@ class Lpgbt(Hardware):
self.write_read("PROCESSANDSEUMONITOR", "SEUENABLE", 0)
return seuCounter
def swap_rx_polarity(self,group):
"""Swapping the polarity of RX (uplink) for all channels.
This always concerns channel 0 of each e-group.
......@@ -1218,229 +1219,46 @@ class Lpgbt(Hardware):
else:
raise Exception("The ID identification wasn't successful")
def tx_rx_scan(self, optical_channel, url=os.environ.get("FELIX_BACKEND_URL") if os.environ.get("FELIX_BACKEND_URL") is not None else "http://localhost:8001"):
"""Iteratively turn off the lpGBT downlink to identify to which of them each FE is connected
def read_estimation_temperature(self, reset_temperature_sensor = True):
'''Read the estimated temperature of the lpgbt.
More specifically, read the adc voltage and use the calibration equation to translate it to temperature
Args:
reset_temperature_sensor (boolean) should be true if one would like to reset the temperature sensor before reading the adc value.
Returns:
The estimate temperature in degree Celsius
'''
if reset_temperature_sensor:
t=self.read(reg="ADCMon",reg_field="TEMPSensReset")
t1=self.write_read(reg="ADCMon",reg_field="TEMPSensReset",reg_data=1)
time.sleep(0.1)
t2=self.write_read(reg="ADCMon",reg_field="TEMPSensReset",reg_data=0)
adc_value = self.read_adc(14)
if len(self.lpgbt_calibration) == 0:
self.read_lpgbt_calibration_file()
temperature_offset = self.lpgbt_calibration['TEMPERATURE_UNCALVREF_OFFSET']
temperature_slope = self.lpgbt_calibration['TEMPERATURE_UNCALVREF_SLOPE']
temperature= (adc_value*temperature_slope)+temperature_offset
logger.info("Estimation of temperature is %.1f C" %temperature)
return temperature
def read_accurate_temperature(self, reset_temperature_sensor = True):
'''Read the accurate temperature of the lpgbt.
More specifically, calculate the temperature by using the estimate temperature from above and another calibration equation
Args:
reset_temperature_sensor (boolean) is used to reset the temperature sensor before reading it
Returns:
The accurate temperature in degree Celsius
'''
if reset_temperature_sensor:
t=self.read(reg="ADCMon",reg_field="TEMPSensReset")
t1=self.write_read(reg="ADCMon",reg_field="TEMPSensReset",reg_data=1)
time.sleep(0.1)
t2=self.write_read(reg="ADCMon",reg_field="TEMPSensReset",reg_data=0)
adc_value = self.read_adc(14)
if len(self.lpgbt_calibration) == 0:
self.read_lpgbt_calibration_file()
adc_x2_slope = self.lpgbt_calibration['ADC_X2_SLOPE']
adc_x2_slope_temp = self.lpgbt_calibration['ADC_X2_SLOPE_TEMP']
adc_x2_offset = self.lpgbt_calibration['ADC_X2_OFFSET']
adc_x2_offset_temp = self.lpgbt_calibration['ADC_X2_OFFSET_TEMP']
temperature_offset = self.lpgbt_calibration['TEMPERATURE_OFFSET']
temperature_slope = self.lpgbt_calibration['TEMPERATURE_SLOPE']
temperature = self.read_estimation_temperature()
v_adc = adc_value * (adc_x2_slope + temperature * adc_x2_slope_temp) + adc_x2_offset + temperature * adc_x2_offset_temp
temp_c= (v_adc*temperature_slope)+temperature_offset
logger.info("Accurate temperature is %.1f C"%temp_c)
return temp_c
def read_lpgbt_calibration_file(self, variables_name = [], calibration_file_path = "lpgbt_calibration.csv"):
'''Read the calibration values for the lpgbt. More specifically, find the right row in the table corresponding to the id
Args:
variable_name (array of strings) is the parameters one would like to access for the specific lpgbt id.
Returns:
The values of the parameters given, corresponding to the id of the lpgbt.
'''
id = self.read_id()
id_hex=(hex(id)[2:]).upper()
column_name = {}
names = []
try:
f = open(calibration_file_path)
except:
logger.warning("The csv file "+ calibration_file_path + " couldn't be read.")
return None
try:
reader = csv.reader(f)
for row in reader:
if row[0] == "CHIPID":
for idx, name in enumerate(row):
column_name[name] = idx
if name != "CHIPID":
names.append(name)
if row[0] == id_hex:
for name in names:
self.lpgbt_calibration[name] = float(row[column_name[name]])
break
f.close()
except:
f.close()
logger.warning("The parameters and/or ID couldn't be find in the csv calibration file")
def read_monitor_voltage(self, adc_channel = 10, disable_monitor_after_measurement =True):
'''Measure voltage from the dedicated adc channel
adc_channel is either equal to 10 (VDDTX), 11 (VDDRX), 12 (VDD), 13(VDDA)
adc_channel can be 1 or 0 for external pin ADC voltage reading
Args:
adc_channel (int) is the channel that one wants to read the voltage from. Could be 0,1,10,11,12,13
disable_monitor_after_measurement (boolean) is used to disable the vddmon.
optical_channel (str): felix channel to be scanned
url (str): felix backend url
Returns:
the voltage in V from the desired channel.
'''
assert (adc_channel == 13 or adc_channel ==12 or adc_channel == 11 or adc_channel == 10 or adc_channel == 1 or adc_channel == 0), 'Invalid adc_channely'
#First is required to enable the VDD monitor
self.enable_voltage_monitor(adc_channel,True)
adc_value = self.read_adc(adcchannel=adc_channel)
if len(self.lpgbt_calibration) == 0:
self.read_lpgbt_calibration_file()
adc_x2_slope = self.lpgbt_calibration['ADC_X2_SLOPE']
adc_x2_slope_temp = self.lpgbt_calibration['ADC_X2_SLOPE_TEMP']
adc_x2_offset = self.lpgbt_calibration['ADC_X2_OFFSET']
adc_x2_offset_temp = self.lpgbt_calibration['ADC_X2_OFFSET_TEMP']
vdd_mon_slope = self.lpgbt_calibration['VDDMON_SLOPE']
vdd_mon_slope_temp = self.lpgbt_calibration['VDDMON_SLOPE_TEMP']
temperature = self.read_estimation_temperature()
v_adc = adc_value * (adc_x2_slope + temperature * adc_x2_slope_temp) + adc_x2_offset + temperature * adc_x2_offset_temp
vsup = v_adc * (vdd_mon_slope + temperature * vdd_mon_slope_temp)
if disable_monitor_after_measurement:
self.enable_voltage_monitor(adc_channel,False)
logger.info("The voltage monitor of channel %i is %.2f V" %(adc_channel,vsup))
return vsup
def read_temperature_NTC(self,estimate_resistance = 10000):
link_list (list): list containing the connected downlink and uplink numbers
"""
First give an estimage of the resistance (10kohm) and send a reasonable current to the NTC. Then read the voltage. Then measure the resistance.
if not self.master:
raise Exception("The only downlinks in use are those of lpGBT1!")
Args:
estimate_resistance (int) is used to compute the current that need to be applied.
Returns:
The temperature in degree based on the NTC.
"""
try:
B = 3453
R0 = 10000
T0 = 25
current_temperature = self.read_estimation_temperature()
#Defining the current that needs to be set
vref = 0.5
current = vref / estimate_resistance
assert 0<= current < 1e-3,"Invalid CDAC current"
if len(self.lpgbt_calibration) == 0:
self.read_lpgbt_calibration_file()
CDAC3_SLOPE = self.lpgbt_calibration['CDAC3_SLOPE']
CDAC3_SLOPE_TEMP = self.lpgbt_calibration['CDAC3_SLOPE_TEMP']
CDAC3_OFFSET = self.lpgbt_calibration['CDAC3_OFFSET']
CDAC3_OFFSET_TEMP = self.lpgbt_calibration['CDAC3_OFFSET_TEMP']
CDAC3_R0 = self.lpgbt_calibration['CDAC3_R0']
CDAC3_R0_TEMP = self.lpgbt_calibration['CDAC3_R0_TEMP']
adc_current = round((CDAC3_SLOPE+ current_temperature * CDAC3_SLOPE_TEMP)* current + CDAC3_OFFSET+ current_temperature * CDAC3_OFFSET_TEMP)
if adc_current <= 0 or adc_current > 256:
logger.error("adc_current can not deliver requested current")
return None
#enabling the current in the channel
self.write_read(reg="DACConfigH",reg_field="CURDACEnable",reg_data=1)
self.write_read(reg="CURDACValue", reg_field = None,reg_data=adc_current)
self.write_read(reg="CURDACCHN", reg_field = None,reg_data=0x04) #adc 3
#reading all the parameters
r_out = (CDAC3_R0+ current_temperature * CDAC3_R0_TEMP)/ max((1,adc_current))
v_adc = self.read_adc(adcchannel = 3)
r_measured = current /v_adc
#compute the resistance and then the temperature
R = r_measured/(1- r_measured/r_out)
T = 1/(1/T0 + (2.3026 * (math.log10(R) - math.log10(R0)) /B )) #Formula taken from the farnell NTC manual (https://www.farnell.com/datasheets/3097877.pdf)
#disabling the current in the channel
self.write_read(reg="DACConfigH",reg_field="CURDACEnable",reg_data=0)
self.write_read(reg="CURDACValue", reg_field = None,reg_data=0)
self.write_read(reg="CURDACCHN", reg_field = None,reg_data=0)
logger.info("The temperature is %.1f C" %T)
return T
except:
logger.warning("The temperature couldn't be read. Putting the current to 0.")
self.write_read(reg="DACConfigH",reg_field="CURDACEnable",reg_data=0)
self.write_read(reg="CURDACValue", reg_field = None,reg_data=0)
self.write_read(reg="CURDACCHN", reg_field = None,reg_data=0)
def read_external_pin_voltage_monitor(self, adc_channel = 0):
"""
Read the external pin from the ADC of the lpbgt channel 0 or 1
A calibration is applied directly with the function "read_monitor_voltage" used in this function.
Then the resistency calibration is applied to get the vtrx voltage (?)
Args:
adc_channel (int) can be either 0 or 1 depending on the desired external pin reading
Returns:
the adv value read from the specified adc channel.
"""
assert(adc_channel == 0 or adc_channel == 1), "Invalid adc_channel input"
adc_v = self.read_monitor_voltage(adc_channel = adc_channel)
if adc_channel == 0:
adc_v = 1560 * adc_v /560 #taken from the eletrical schematics in the optoboard documention in gitlab
logger.info("The voltage monitor of channel %i after resistance conversion is %.2f V" %(adc_channel,adc_v))
return adc_v
def enable_voltage_monitor(self, power_supply, enable=True):
"""Helper function to enable/disable a specific VDD monitor
Args:
power_supply (int): 10 (VDDTX), 11 (VDDRX), 12 (VDD) or 13 (VDDA)
Returns:
None if the power supply number was not in the exact specified range
or the register "ADCMon" reading after changing it (should be 1 for enabling, 0 for disabling)
"""
if power_supply == 10:
power_supply_name = "VDDTXmonEna"
elif power_supply == 11:
power_supply_name = "VDDRXmonEna"
elif power_supply == 12:
power_supply_name = "VDDmonEna"
elif power_supply == 13:
power_supply_name = "VDDANmonEna"
elif power_supply== 0 or power_supply == 1:
return None
else:
logger.warning("The powersupply number was not correct")
return None
if enable:
t=self.write_read(reg="ADCMon",reg_field=power_supply_name,reg_data=1)
else:
t=self.write_read(reg="ADCMon",reg_field=power_supply_name,reg_data=0)
return t
aligned_link = "DECODING_LINK_ALIGNED_" + str(optical_channel)
link_list=[]
registers = ["EPTX10ENABLE_EPTX00ENABLE", "EPTX10ENABLE_EPTX02ENABLE" , "EPTX10ENABLE_EPTX10ENABLE" , "EPTX10ENABLE_EPTX12ENABLE", "EPTX32ENABLE_EPTX20ENABLE" , "EPTX32ENABLE_EPTX22ENABLE", "EPTX32ENABLE_EPTX30ENABLE" , "EPTX32ENABLE_EPTX32ENABLE"]
aligned_initial = requests.get(url + "/flxconfigget/" + aligned_link, headers={"content-type": "application/json"}).json()["result"].strip().split("=")[1]
if "1" not in aligned_initial:
logger.warning("No decoding alignment has been found!")
return link_list
for downlink_index, register in enumerate(registers):
# disable i-th downlink
self.write_read(register.split("_")[0] , register.split("_")[1] , 0)
# Check new alignment
aligned = requests.get(url + "/flxconfigget/" + aligned_link, headers={"content-type": "application/json"}).json()["result"].strip().split("=")[1]
if aligned != aligned_initial:
for uplink_index in range(6):
if aligned_initial[-uplink_index-1] != aligned[-uplink_index-1]:
logger.info("Optical link " + optical_channel + " => Downlink " + str(downlink_index) + " <-> Uplink " + str(uplink_index))
link_list.append({"downlink": downlink_index, "uplink": uplink_index})
# enable i-th downlink
self.write_read(register.split("_")[0] , register.split("_")[1] , 1)
aligned = requests.get(url + "/flxconfigget/" + aligned_link, headers={"content-type": "application/json"}).json()["result"].strip().split("=")[1]
if aligned_initial != aligned:
raise Exception("Use this function before configuring the FE!")
return link_list
\ No newline at end of file
......@@ -24,6 +24,7 @@ except (ImportError, ModuleNotFoundError):
import time
import os
import subprocess
import requests
from optoboard_felix.driver.Lpgbt import Lpgbt
from optoboard_felix.driver.Gbcr import Gbcr
......@@ -222,7 +223,6 @@ class Optoboard:
"""Check the status of the different devices on the Optoboard.
Checks lpGBT info, PUSM status, lpGBT configuration pins, EPRX and EPTX settings, and locking status.
Returns:
opto_doc_dic (dict): status dictionary with devices as keys
"""
......@@ -349,27 +349,44 @@ class Optoboard:
self.plotHeatMap(filename, data)
def softErrorCounter(self, optical_link, egroup, meastime):
def softErrorCounter(self, optical_link, egroup, meastime, API=True, url=os.environ.get("FELIX_BACKEND_URL") if os.environ.get("FELIX_BACKEND_URL") is not None else "http://localhost:8001"):
"""The number of soft errors is monitored by checking the associated felix register.
`flx-reset SOFT_RESET` command is called after each readout of the felix register therefore this method CANNOT BE CALLED IF FELIXCORE IS RUNNING!
Args:
optical_link (str): optical link to monitor, example: "00" for felix channel 0
egroup (int): lpGBT egroup to monitor
meastime (int): measuring time in seconds
API (bool): use endpoint call to monitor the soft error
url (str): url of the server with FELIX backend
Returns:
List with the measured errors, to be summed to have the total amount of errors
"""
logger.warning("Turn off felixcore")
aligned_link = "ALIGNED_" + optical_link
aligned_link = "DECODING_LINK_ALIGNED_" + optical_link
felix_link = "LINK_" + optical_link + "_ERRORS_EGROUP" + str(egroup)
start_time = time.time()
counter = []
subprocess.Popen(["flx-reset", "SOFT_RESET"], stdout=subprocess.PIPE)
aligned = subprocess.Popen(["flx-config", "list"], stdout=subprocess.PIPE)
aligned = subprocess.Popen(["grep", aligned_link], stdin=aligned.stdout, stdout=subprocess.PIPE).stdout.readlines()[0].decode().split()[4][-(egroup+1)]
if API:
logger.warning("Turn off felixcore")
aligned = requests.get(url + "/flxconfigget/" + aligned_link, headers={"content-type": "application/json"}).json()["result"].strip().split("=")[1][-(egroup+1)]
# select the egroup
set_egroup = requests.get(url + "/flxconfigset/" + "LINK_" + optical_link + "_ERRORS_EGROUP_SELECT/" + str(egroup), headers={"content-type": "application/json"}).json()["result"]
# reset the counter
reset_counter = requests.get(url + "/flxconfigset/" + "LINK_" + optical_link + "_ERRORS_CLEAR_COUNTERS/1", headers={"content-type": "application/json"}).json()["result"]
reset_counter = requests.get(url + "/flxconfigset/" + "LINK_" + optical_link + "_ERRORS_CLEAR_COUNTERS/0", headers={"content-type": "application/json"}).json()["result"]
if (set_egroup != "") or (reset_counter != ""):
logger.error("Unable to write felix registers! Check if felixcore is running, the validity of the provided arguments and the version of the felix fw/sw in use!")
raise Exception("Unable to write felix registers!")
else:
logger.warning("Turn off felixcore")
logger.warning("Only working for outdated firmware with short soft error registers!")
subprocess.Popen(["flx-reset", "SOFT_RESET"], stdout=subprocess.PIPE)
aligned = subprocess.Popen(["flx-config", "list"], stdout=subprocess.PIPE)
aligned = subprocess.Popen(["grep", aligned_link], stdin=aligned.stdout, stdout=subprocess.PIPE).stdout.readlines()[0].decode().split()[4][-(egroup+1)]
start_time = time.time()
while (time.time()-start_time < meastime):
if (aligned!="1"):
......@@ -377,17 +394,30 @@ class Optoboard:
counter.append(-1)
break
try:
output = subprocess.Popen(["flx-config", "list"], stdout=subprocess.PIPE)
output = subprocess.Popen(["grep", felix_link], stdin=output.stdout, stdout=subprocess.PIPE).stdout.readlines()[0].decode().split()[4]
except:
logger.error("Readout of FELIX register failed!")
raise Exception("Readout of FELIX register failed!")
if (output!="0x0"):
counter.append(int(output,16))
#if (output=="0xf"):
subprocess.Popen(["flx-reset", "SOFT_RESET"], stdout=subprocess.PIPE)
time.sleep(2)
if API:
try:
output = int(requests.get(url + "/flxconfigget/" + "LINK_" + optical_link + "_ERRORS_COUNT", headers={"content-type": "application/json"}).json()["result"].strip().split("=")[1], 16)
except:
logger.error("Readout of FELIX register failed!")
raise Exception("Readout of FELIX register failed!")
if (output!=0):
counter.append(output)
# requests.get(url + "/flxconfigset/" + "LINK_" + optical_link + "_ERRORS_CLEAR_COUNTERS/1", headers={"content-type": "application/json"}).json()
time.sleep(2)
else:
try:
output = subprocess.Popen(["flx-config", "list"], stdout=subprocess.PIPE)
output = subprocess.Popen(["grep", felix_link], stdin=output.stdout, stdout=subprocess.PIPE).stdout.readlines()[0].decode().split()[4]
except:
logger.error("Readout of FELIX register failed!")
raise Exception("Readout of FELIX register failed!")
if (output!="0x0"):
counter.append(int(output,16))
subprocess.Popen(["flx-reset", "SOFT_RESET"], stdout=subprocess.PIPE)
time.sleep(2)
logger.info("Soft errors on optical link " + str(optical_link) + " egroup " + str(egroup) + ": " + str(counter))
return counter
......@@ -451,6 +481,15 @@ class Optoboard:
logger.info("Plotting the results of the BERT scan")
self.plotHeatMap(filename, data)
def flx_info_link(self, url=os.environ.get("FELIX_BACKEND_URL") if os.environ.get("FELIX_BACKEND_URL") is not None else "http://localhost:8001"):
"""Check optical alignment via flx-info link through FELIX endpoint
Return:
optical_alignment (dict): dictionary with integer keys and boolean values parsing the return of flx-info link
"""
optical_alignment = requests.get(url + "/getlink", headers={"content-type": "application/json"}).json()
return optical_alignment
@staticmethod
def plotHeatMap(filename, data):
......
......@@ -131,4 +131,4 @@ class Vtrx(Hardware):
id = register3 << 24 | register2 << 16 | register1 << 8 | register0
logger.info("VTRx unique chip ID: %s",id)
return id
\ No newline at end of file
return id