Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADD: Automatic finding of USB binds based on instrument IDN #210

Merged
merged 24 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b3b04d6
ENH: Added method to find linux USB binds based on instrument IDN
mjmucha Jan 15, 2024
af9c24a
ENH: Added method to update port/resource name in basil conf based on…
mjmucha Jan 15, 2024
5b3fad9
MAINT: removed unnecessary code
mjmucha Jan 15, 2024
edd182d
Update keithley_2400.yaml
konstantinmauer Feb 15, 2024
efc2467
ENH: Adds function to keithley2400 to return set source current and v…
matthias-schuessler Apr 4, 2024
7576232
Update workflows (#208)
YannickDieter Apr 22, 2024
8c9f630
ENH: added documentation
mjmucha Apr 24, 2024
7f8062d
ENH: removed unnecessary argument
mjmucha Apr 24, 2024
51ff359
ENH: try ASRL ports first
mjmucha Apr 24, 2024
6a73e50
ENH: added saving of found usb ports to basil config
mjmucha Apr 24, 2024
a143733
ADD: added new requirements
mjmucha Apr 24, 2024
85f381e
ENH: PEP8 formatting
mjmucha Apr 24, 2024
7217c62
function names are now more pythonic
mjmucha Apr 30, 2024
2b321b9
use default basil read/write termination
mjmucha Apr 30, 2024
d37beaf
ENH: Adds new functionality to keithley_2634b
matthias-schuessler Apr 11, 2023
a24a256
ENH: Adds functions to change source range.
matthias-schuessler Apr 11, 2023
f411096
ENH: Adds readout for set source voltage and current.
matthias-schuessler Aug 1, 2023
e1fa883
ADD: Adds an example for keithley_2634b connexion using Serial
AntonioT7 Apr 11, 2023
1ab8928
ENH: get_die now outputs the unmodified die position from the prober api
mjmucha Apr 29, 2024
470c8fa
fixed PEP8 formatting
mjmucha Apr 29, 2024
49fe305
fixed missed renaming of functions
mjmucha Apr 30, 2024
23e282c
PEP8 formatting
mjmucha Apr 30, 2024
62e5772
PEP8 formatting
mjmucha Apr 30, 2024
06c4761
removed pyvisa requirement
mjmucha Apr 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 11 additions & 11 deletions .github/workflows/regression-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.8]
python-version: [3.11]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{matrix.python-version}}
Expand All @@ -31,32 +31,32 @@ jobs:
tests:

name: Python ${{matrix.python-version}} | ${{matrix.sim}}
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
SIM: ${{matrix.sim}}

strategy:
fail-fast: false
matrix:
include:

- sim: icarus
python-version: 3.7
python-version: '3.11'

- sim: icarus
python-version: 3.8
python-version: '3.10'

- sim: verilator
sim-version: v4.106
python-version: 3.8
sim-version: v5.020
python-version: '3.10'
pytest-marker: "-m verilator"

steps:
- uses: actions/checkout@v1
- uses: conda-incubator/setup-miniconda@v2
- name: Set up Anaconda ${{matrix.python-version}}
uses: conda-incubator/setup-miniconda@v2
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
python-version: ${{matrix.python-version}}

- name: Install Conda dependencies
shell: bash -l {0}
Expand All @@ -67,12 +67,12 @@ jobs:
- name: Install Python dependencies
shell: bash -l {0}
run: |
pip install pyvisa pyvisa-sim pytest coveralls pytest-cov cocotb==1.5.2
pip install pyvisa pyvisa-sim pytest coveralls pytest-cov cocotb>=1.8.1 cocotb-bus

- name: Install Verilator
if: matrix.sim == 'verilator'
run: |
sudo apt install -y --no-install-recommends make g++ perl python3 autoconf flex bison libfl2 libfl-dev zlibc zlib1g zlib1g-dev
sudo apt install -y --no-install-recommends make g++ help2man perl autoconf flex bison libfl2 libfl-dev zlib1g zlib1g-dev
git clone https://github.com/verilator/verilator.git -b ${{matrix.sim-version}}
cd verilator
autoconf
Expand Down
4 changes: 2 additions & 2 deletions basil/HL/JtagMaster.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def scan_ir(self, data, readback=True):
bit_number = self._test_input(data)
self.SIZE = bit_number

if type(data[0]) == BitLogic:
if isinstance(data[0], BitLogic):
data_byte = self._bitlogic2bytes(data)
else:
data_byte = self._raw_data2bytes(data)
Expand Down Expand Up @@ -135,7 +135,7 @@ def scan_dr(self, data, readback=True, word_size=None):
self.WORD_COUNT = bit_number // word_size
self.SIZE = word_size

if type(data[0]) == BitLogic:
if isinstance(data[0], BitLogic):
data_byte = self._bitlogic2bytes(data)
else:
data_byte = self._raw_data2bytes(data)
Expand Down
8 changes: 8 additions & 0 deletions basil/HL/keithley_2400.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ identifier : KEITHLEY INSTRUMENTS INC.,MODEL 2400
on : OUTP ON
off : OUTP OFF
get_on: OUTP?
get_source_current: SOUR:CURR?
get_current : SENSE:FUNC 'CURR';:READ?
set_current : SOUR:CURR
get_source_voltage: SOUR:VOLT?
set_voltage : SOUR:VOLT
get_voltage : SENSE:FUNC 'VOLT';:READ?
set_current_limit : SENS:CURR:PROT
Expand All @@ -23,3 +25,9 @@ get_autorange : SOUR:CURR:RANG:AUTO?
four_wire_on: SYST:RSEN ON
four_wire_off: SYST:RSEN OFF
get_remote_sense: SYST:RSEN ?
# Special keyword for formatting query results to allow direct conversions to numeric types (e.g. float(get_current()))
__scpi_query_fmt:
fmt_sep: ','
fmt_method:
get_voltage: '{0}'
get_current: '{1}'
2 changes: 1 addition & 1 deletion basil/TL/Socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def close(self):
self._sock.close()

def write(self, data):
if type(data) == bytes:
if isinstance(data, bytes):
cmd = data
else:
cmd = data.encode(self.encoding)
Expand Down
2 changes: 1 addition & 1 deletion basil/firmware/modules/utils/CG_MOD_neg.v
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ input ck_in,enable;
output ck_out;
reg enl;

always @(ck_in or enable)
always_latch
if (ck_in)
enl = enable;
assign ck_out = ck_in | ~enl;
Expand Down
2 changes: 1 addition & 1 deletion basil/firmware/modules/utils/CG_MOD_pos.v
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ wire ck_inb;
reg enl;

assign ck_inb = ~ck_in;
always @(ck_inb or enable)
always_latch
if (ck_inb)
enl = enable;
assign ck_out = ck_in & enl;
Expand Down
245 changes: 245 additions & 0 deletions basil/utils/USBBinds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import os

from pathlib import Path

from six import string_types

import pyvisa

import ruamel.yaml


def queryIdentification(rm, resource, baud_rate, read_termination="\n", write_termination="\n", timeout=1000 * 5):
mjmucha marked this conversation as resolved.
Show resolved Hide resolved
"""
Queries the identification of the instrument connected via USB.

Args:
rm (pyvisa.ResourceManager): The pyvisa resource manager.
resource (str): The resource name or address of the instrument.
baud_rate (int): The baud rate for communication with the instrument.
read_termination (str, optional): The read termination character(s). Defaults to "\n".
write_termination (str, optional): The write termination character(s). Defaults to "\n".
timeout (int, optional): The timeout value in milliseconds. Defaults to 5000.

Returns:
str: The identification string of the instrument.

"""
inst = rm.open_resource(resource)
inst.timeout = timeout
inst.baud_rate = baud_rate
inst.read_termination = read_termination
inst.write_termination = write_termination

return inst.query("*IDN?", delay=0.5)


def findUSBBinds(rm, log,
mjmucha marked this conversation as resolved.
Show resolved Hide resolved
instruments,
binds_to_skip=[],
memorized_binds=[],
timeout=1000 * 4,
verbose=False,
):
"""
Finds the USB bind for each instrument in the given list of instruments.

Args:
rm (pyvisa.ResourceManager): The pyvisa resource manager.
log (logging.Logger): The logger object for logging messages.
instruments (list): List of dictionaries representing the instruments.
Each dictionary should contain the following keys:
- 'baud_rate': The baud rate for the instrument.
- 'read_termination': The read termination character for the instrument.
- 'write_termination' (optional): The write termination character for the instrument.
- 'identification': The identification string for the instrument.
binds_to_skip (list, optional): List of binds to skip during the search.
Defaults to an empty list.
memorized_binds (list, optional): List of memorized binds.
Defaults to an empty list.
timeout (int, optional): Timeout value in milliseconds.
Defaults to 4000.
verbose (bool, optional): Flag indicating whether to log verbose messages.
Defaults to False.

Returns:
dict: A dictionary mapping the identification strings of the instruments
to their corresponding USB binds.
"""
skip_binds = binds_to_skip + [
str(Path(f"/dev/{bind}").resolve()) for bind in binds_to_skip
]

results = {}

for instrument in instruments:
# try first ASRL port
if instrument.get("port") and 'ASRL' not in instrument.get("port"):
instrument["port"] = f'ASRL{instrument["port"]}::INSTR'
resources = (instrument["port"],) + rm.list_resources() if instrument.get("port") else rm.list_resources()

for res in resources:
if "USB" not in res: # Only search for USB devices
continue

if any(bind in res for bind in skip_binds):
if verbose:
log.info(f"Skipping USB bind {res}")
continue

try:
if verbose:
log.info(f"Trying {res} with baud rate {instrument['baud_rate']}")

if any(res in bind for bind in memorized_binds):
if verbose:
log.info(f"Found memorized bind {res}")
result = memorized_binds[res]
else:
result = queryIdentification(rm, res, instrument['baud_rate'], instrument['read_termination'], instrument['write_termination'], timeout=timeout, verbose=verbose)

memorized_binds.append({res, result})

if verbose:
log.info(f"Found {result.strip()}")

if result.lower().strip() in [inst["identification"].lower().strip() for inst in instruments]:
substring = res.split("/")[2].split("::")[0]

log.info(f"Matched instrument {instrument['identification']} to /dev/{str(substring)}")
skip_binds.append(f"/dev/{str(substring)}")

results[result.lower().strip()] = f"/dev/{str(substring)}"

if len(results) == len(instruments):
return results

except pyvisa.VisaIOError:
pass

return results


def getBaudrate(dictionary):
mjmucha marked this conversation as resolved.
Show resolved Hide resolved
"""
Gets the baud rate from the given dictionary.

Args:
dict (dict): The dictionary to get the baud rate from.

Returns:
int: The baud rate.
"""
if "baud_rate" in dictionary.keys():
return dictionary["baud_rate"]
elif "baudrate" in dictionary.keys():
return dictionary["baudrate"]
else:
return 9600


def load_yaml_with_comments(conf):
"""
Load YAML configuration file with comments.

Args:
conf: The YAML configuration file path or a file-like object containing YAML content.

Returns:
dict: A dictionary containing the parsed YAML configuration.

"""
conf_dict = {}

if not conf:
pass
elif isinstance(conf, string_types): # parse the first YAML document in a stream
yaml = ruamel.yaml.YAML()
if os.path.isfile(conf):
with open(conf, 'r') as f:
yaml = ruamel.yaml.YAML()
conf_dict.update(yaml.load(f))
else: # YAML string
try:
conf_dict.update(yaml.load(conf))
except ValueError: # invalid path/filename
raise IOError("File not found: %s" % conf)
elif hasattr(conf, 'read'): # parse the first YAML document in a stream
yaml = ruamel.yaml.YAML()
conf_dict.update(yaml.load(conf))
conf.close()
else: # conf is already a dict
conf_dict.update(conf)

return conf_dict


def modify_basil_config(conf, log, skip_binds=[], save_modified=None, verbose=False):
"""
Modifies the basil configuration file by finding USB binds for devices.

Args:
conf (dict): The basil configuration dictionary.
log: The logger object for logging messages.
skip_binds (list, optional): List of USB binds to skip. Defaults to [].
save_modified (str, optional): Path to save the modified basil configuration file. Defaults to None.
verbose (bool, optional): Flag to enable verbose logging. Defaults to False.

Returns:
dict: The modified basil configuration dictionary.
"""
log.info("Trying to find USB binds for devices in basil configuration file")

rm = pyvisa.ResourceManager()

instruments = []
insts_idx_map = {}

# Iterate over transfer layers in the configuration
for i, tf in enumerate(conf["transfer_layer"]):
if (
"identification" not in tf["init"].keys()
or "read_termination" not in tf["init"].keys()
or not any(e in tf["init"].keys() for e in ["baud_rate", "baudrate"])
):
if verbose:
log.debug(f"Skipping {tf['type']} transfer layer with name {tf['name']}")
continue

instrument = tf["init"]["identification"]
baud_rate = getBaudrate(tf["init"])
read_termination = tf["init"]["read_termination"]
write_termination = tf["init"]["write_termination"] if "write_termination" in tf["init"].keys() else "\n"
port = tf["init"]["port"] if "port" in tf["init"].keys() else None

instruments.append({
"identification": instrument,
"baud_rate": baud_rate,
"read_termination": read_termination,
"write_termination": write_termination,
"port": port,
})

insts_idx_map[instrument.lower().strip()] = i

found_binds = findUSBBinds(rm, log=log, instruments=instruments, binds_to_skip=skip_binds, verbose=verbose)

for inst in found_binds.keys():
if found_binds[inst] is None:
raise LookupError(f"Could not find USB bind for {inst.title().replace('_', '')}")

if conf["transfer_layer"][insts_idx_map[inst]]["type"].lower() == "serial":
conf["transfer_layer"][insts_idx_map[inst]]["init"]["port"] = found_binds[inst]
elif conf["transfer_layer"][insts_idx_map[inst]]["type"].lower() == "visa":
conf["transfer_layer"][insts_idx_map[inst]]["init"]["resource_name"] = f"ASRL{found_binds[inst]}::INSTR"

if save_modified is not None:
yaml = ruamel.yaml.YAML()
log.info(f'Saving modified periphery file: {save_modified}')
with open(save_modified, 'w') as f:
yaml.dump(conf, f)

for inst in found_binds.keys():
del conf["transfer_layer"][insts_idx_map[inst]]["init"]["identification"]

return conf
2 changes: 1 addition & 1 deletion basil/utils/sim/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def cocotb_makefile(sim_files, top_level='tb', test_module='basil.utils.sim.Test
#export COCOTB=$(shell SPHINX_BUILD=1 python -c "import cocotb; import os; print(os.path.dirname(os.path.dirname(os.path.abspath(cocotb.__file__))))")
#export PYTHONPATH=$(shell python -c "from distutils import sysconfig; print(sysconfig.get_python_lib())"):$(COCOTB)
#export LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:$(PYTHONLIBS)
export PYTHONHOME=$(shell python -c "from distutils.sysconfig import get_config_var; print(get_config_var('prefix'))")
#export PYTHONHOME=$(shell python -c "from distutils.sysconfig import get_config_var; print(get_config_var('prefix'))")

ifeq ($(SIM),questa)
EXTRA_ARGS += $(NOT_ICARUS_DEFINES)
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ numpy
pyyaml
bitarray>=2.0.0
six
pyvisa
mjmucha marked this conversation as resolved.
Show resolved Hide resolved
ruamel.yaml