diff --git a/.gitignore b/.gitignore index 964df4d4..5154d16d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ *.pyc *.vvp *.kate-swp - +python/xfcp.egg-info +python/build/ +python/venv/ +python/.eggs/ diff --git a/python/Makefile b/python/Makefile new file mode 100644 index 00000000..9d86a2fe --- /dev/null +++ b/python/Makefile @@ -0,0 +1,37 @@ +MKFILE_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +VENVDIR = venv +VENVACTIVATE = $(VENVDIR)/bin/activate +XFCP_DIR = $(MKFILE_DIR)/ +PYTHON_VERSION=$(shell python3 -c "import sys; v = list(sys.version_info[:2]); print(f'{int(v[0])}.{int(v[1])}')") +ifeq ($(PYTHON_VERSION), 3.6) +$(info Python Version supported) +else ifeq ($(PYTHON_VERSION), 3.10) +$(info Python Version supported) +else +$(error Python Version not supported, the supported versions are 3.6 and 3.10) +endif + +.PHONY: xfcp + +venv: $(VENVACTIVATE) + +xfcp: venv + . $(VENVACTIVATE) && \ + if [ $(PYTHON_VERSION) = "3.6" ]; then \ + python setup.py install; \ + else \ + pip install $(XFCP_DIR); \ + fi \ + +$(VENVACTIVATE): requirements.txt + test -d $(VENVDIR) || python3 -m venv $(VENVDIR) --copies + . $(VENVACTIVATE) && \ + pip3 install wheel && \ + pip3 install -r requirements.txt && \ + touch $(VENVACTIVATE) + +clean: + rm -rf __pycache__ + +mrproper: clean + rm -rf $(VENVDIR) diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 00000000..4e329922 --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,39 @@ +##Copyright (c) 2023 Jakob Arndt Missing Link Electronics, Inc. +## +##Permission is hereby granted, free of charge, to any person obtaining a copy +##of this software and associated documentation files (the "Software"), to deal +##in the Software without restriction, including without limitation the rights +##to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +##copies of the Software, and to permit persons to whom the Software is +##furnished to do so, subject to the following conditions: +## +##The above copyright notice and this permission notice shall be included in +##all copies or substantial portions of the Software. +## +##THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +##IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +##FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +##AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +##LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +##OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +##THE SOFTWARE. + +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "xfcp" +authors = [{name="Alex Forencich", email="jforenci@eng.ucsd.edu"}] +description="This python package enables the use of xfcp as a library for your python implementation." +version = "1.0" +requires-python = ">=3.6" +dependencies = [ + "pyserial" +] + +[tool.setuptools] +packages = ['xfcp'] + +[tool.setuptools.package-dir] +"xfcp" = "xfcp" diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 00000000..f6c1a1f5 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1 @@ +pyserial diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 00000000..8ae94a41 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,38 @@ +##Copyright (c) 2023 Jakob Arndt Missing Link Electronics, Inc. +## +##Permission is hereby granted, free of charge, to any person obtaining a copy +##of this software and associated documentation files (the "Software"), to deal +##in the Software without restriction, including without limitation the rights +##to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +##copies of the Software, and to permit persons to whom the Software is +##furnished to do so, subject to the following conditions: +## +##The above copyright notice and this permission notice shall be included in +##all copies or substantial portions of the Software. +## +##THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +##IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +##FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +##AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +##LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +##OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +##THE SOFTWARE. + +from setuptools import setup + +setup( + name="xfcp", + version="1.0.0", + description="This python package enables you to control the NPAP Test FPGA", + author="Alex Forencich", + author_email="jforenci@eng.ucsd.edu", + packages=[ + 'xfcp'], + package_dir={ + "xfcp": "xfcp" + }, + setup_requires=['setuptools_scm'], + install_requires=[ + 'pyserial', + ] +) diff --git a/python/xfcp/interface.py b/python/xfcp/interface.py index 3db1ebb3..d590ef85 100644 --- a/python/xfcp/interface.py +++ b/python/xfcp/interface.py @@ -108,12 +108,20 @@ def get_root(self): class SerialInterface(Interface): - def __init__(self, port='/dev/ttyUSB0', baud=115200, timeout=10): + def __init__(self, port='/dev/ttyUSB0', baud=115200, timeout=10, driver=None): + """ + driver is expected to be a SerialDriver class from the labgrid module + """ super().__init__() self.port = port self.baud = baud - self.serial_port = serial.Serial(port, baud, timeout=timeout) + + # if labgrid driver is given, use labgrid driver + if driver is None: + self.serial_port = serial.Serial(port, baud, timeout=timeout) + else: + self.serial_port = driver.serial @property def timeout(self): diff --git a/python/xfcp/ltile_htile_node.py b/python/xfcp/ltile_htile_node.py new file mode 100644 index 00000000..9a69a6dc --- /dev/null +++ b/python/xfcp/ltile_htile_node.py @@ -0,0 +1,500 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2022 Karol Budniak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from . import node + + +PRINT_VERBOSITY_TRACE = 4 +PRINT_VERBOSITY_INFO = 3 +PRINT_VERBOSITY_WARN = 2 +PRINT_VERBOSITY_ERROR = 1 +PRINT_VERBOSITY_QUIET = 0 +PRINT_VERBOSITY = PRINT_VERBOSITY_INFO + +# register offsets taken from Intel document: +# L- and H-Tile Transceiver PHY User Guide 683621 | 2022.07.20 +# chapter A. Logical View of the L-Tile/H-Tile Transceiver Registers +# pages 480-504 + +PRBS_MODE_OFF = 0b00000 +PRBS_MODE_PRBS7 = 0b00001 +PRBS_MODE_PRBS9 = 0b00010 +PRBS_MODE_PRBS15 = 0b00100 +PRBS_MODE_PRBS23 = 0b01000 +PRBS_MODE_PRBS31 = 0b10000 + +prbs_mode_mapping = { + 'off': PRBS_MODE_OFF, + 'prbs7': PRBS_MODE_PRBS7, + 'prbs9': PRBS_MODE_PRBS9, + 'prbs15': PRBS_MODE_PRBS15, + 'prbs23': PRBS_MODE_PRBS23, + 'prbs31': PRBS_MODE_PRBS31 +} + +PRBS_GENERATOR_TX_PMA_DATA_SEL_SQUARE_WAVE = 0b00101 +PRBS_GENERATOR_TX_PMA_DATA_SEL_PRBS_PATTERN = 0b00100 + +PRBS_GENERATOR_PRBS9_DWIDTH_ENABLE = 0b1 +PRBS_GENERATOR_PRBS9_DWIDTH_DISABLE = 0b0 + +PRBS_GENERATOR_SERIALIZER_MODE_64BIT = 0b011 +PRBS_GENERATOR_SERIALIZER_MODE_10BIT = 0b100 + +prbs_generator_serializer_mode_mapping = { + 64: PRBS_GENERATOR_SERIALIZER_MODE_64BIT, + 10: PRBS_GENERATOR_SERIALIZER_MODE_10BIT +} + +PRBS_VERIFIER_DESERIALIZER_FACTOR_64BIT = 0xE +PRBS_VERIFIER_DESERIALIZER_FACTOR_10BIT = 0x1 + +prbs_verifier_deserializer_factor_mapping = { + 64: PRBS_VERIFIER_DESERIALIZER_FACTOR_64BIT, + 10: PRBS_VERIFIER_DESERIALIZER_FACTOR_10BIT +} + +class LTileHTileNode(node.MemoryNode): + # expected byte address + def masked_read(self, addr, mask): + # multiple byte address by 4 so it does not get + # truncated because of 32 bit access of AXI4-Lite (converted to Avalon-MM) + val = self.read_word(addr << 2) & mask + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: masked_read: address: " + str(hex(addr)) + ", mask: " + str(hex(mask)) + ", data: " + str(hex(val))) + return val + + # expected byte address + def masked_write(self, addr, mask, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: masked_write: address: " + str(hex(addr)) + ", mask: " + str(hex(mask) + ", data: " + str(hex(val)))) + # multiple byte address by 4 so it does not get + # truncated because of 32 bit access of AXI4-Lite (converted to Avalon-MM) + return self.write_word(addr << 2, (self.read_word(addr << 2) & ~mask) | (val & mask)) + + def dump_reg_space(self, addr_off, start, stop): + for addr in range(start, stop, 0x001): + print("Address: " + str(hex(addr)) + ": " + str(hex(self.masked_read(addr, 0xFF)))) + + # return True if the receiver is locked to data + def get_rx_locked_to_data(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_rx_locked_to_data") + return bool(self.masked_read((addr_off << 11) + 0x480, 0x0001)) + + # return True if the receiver is locked to reference clock + def get_rx_locked_to_ref(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_rx_locked_to_ref") + return bool(self.masked_read((addr_off << 11) + 0x480, 0x0002)) + + # steps to follow to view Eyescan may also apply to run BER measurement + # presented in documentation L- and H-Tile Transceiver PHY User Guide 683621 | 2022.07.20 + # on pages 161 - 162 + def disable_background_calibration(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: disable_background_calibration") + self.masked_write((addr_off << 11) + 0x542, 0x01, 0x00) + + def is_avmm_bus_busy(self, addr_off): + # return True if PreSICE has control of the internal configuration bus + # return False if you have control of internal configuration bus + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: is_avmm_bus_busy") + return bool(self.masked_read((addr_off << 11) + 0x481, 0x04)) + + def is_rx_adaption_mode_manual(self, addr_off): + # return True if RX adaptation is in manual mode, return False otherwise + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: is_rx_adaption_mode_manual") + return bool(self.masked_read((addr_off << 11) + 0x161, 0x20)) + + def release_adaptation_from_reset(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: release_adaptation_from_reset") + self.masked_write((addr_off << 11) + 0x148, 0x01, 0x01) + + def enable_cnt_to_detect_error_bits(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: enable_cnt_to_detect_error_bits") + self.masked_write((addr_off << 11) + 0x169, 0x40, 0x01) + + def enable_serial_bit_checker(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: enable_serial_bit_checker") + self.masked_write((addr_off << 11) + 0x168, 0x01, 0x01) + + def is_dfe_enabled(self, addr_off): + # return True if DFE is enabled, return False otherwise + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: is_dfe_enabled") + return True if (self.masked_read((addr_off << 11) + 0x161, 0x40) == 0) else False + + def enable_dfe_speculation(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: enable_dfe_speculation") + self.masked_write((addr_off << 11) + 0x169, 0x04, 0x04) + + def disable_dfe_speculation(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: disable_dfe_speculation") + self.masked_write((addr_off << 11) + 0x169, 0x04, 0x00) + + def enable_serial_bit_checker_control(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: enable_serial_bit_checker_control") + self.masked_write((addr_off << 11) + 0x158, 0x20, 0x20) + + # PRBS Generator registers + def get_prbs_gen_prbs_tx_pma_data_sel(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_gen_prbs_tx_pma_data_sel") + # bits[4:3] from 0x008[6:5] + msb = self.masked_read((addr_off << 11) + 0x008, 0b1100000) >> 5 + # bits[2:0] from 0x006[2:0] + lsb = self.masked_read((addr_off << 11) + 0x006, 0b00111) >> 0 + val = (msb << 3) + lsb + return val + + def set_prbs_gen_prbs_tx_pma_data_sel(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_gen_prbs_tx_pma_data_sel") + if ((val != PRBS_GENERATOR_TX_PMA_DATA_SEL_SQUARE_WAVE) and \ + (val != PRBS_GENERATOR_TX_PMA_DATA_SEL_PRBS_PATTERN)): + raise Exception("set_prbs_gen_prbs_tx_pma_data_sel: unsupport TX PMA data selected: " + str(hex(val))) + # bits[4:3] to 0x008[6:5] + self.masked_write((addr_off << 11) + 0x008, 0b1100000, (val & 0b11000) << 2) + # bits[2:0] to 0x006[2:0] + self.masked_write((addr_off << 11) + 0x006, 0b00111, val & 0b00111) + + def get_prbs_gen_prbs9_dwidth(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_gen_prbs9_dwidth") + return bool(self.masked_read((addr_off << 11) + 0x006, 0x0008)) + + def set_prbs_gen_prbs9_dwidth(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_gen_prbs9_dwidth") + self.masked_write((addr_off << 11) + 0x006, 0x0008, 0x0008 if val else 0x0000) + + def get_prbs_gen_prbs_clken(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_gen_prbs_clken") + return bool(self.masked_read((addr_off << 11) + 0x006, 0x0040)) + + def set_prbs_gen_prbs_clken(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_gen_prbs_clken") + self.masked_write((addr_off << 11) + 0x006, 0x0040, 0x0040 if val else 0x0000) + + def get_prbs_gen_prbs_pat(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_gen_prbs_pat") + # bit[4] from 0x008[4] + msb = self.masked_read((addr_off << 11) + 0x008, 0x0010) + # bits[3:0] from 0x007[7:4] + lsb = self.masked_read((addr_off << 11) + 0x007, 0x00F0) >> 4 + val = msb + lsb + return val + + def set_prbs_gen_prbs_pat(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_gen_prbs_pat") + # check if argument is a string and map it to register value + if type(val) is str: + val = prbs_mode_mapping[val] + else: + raise Exception("set_prbs_gen_prbs_pat: argument given is not a string") + if ((val != PRBS_MODE_OFF) and \ + (val != PRBS_MODE_PRBS7) and \ + (val != PRBS_MODE_PRBS9) and \ + (val != PRBS_MODE_PRBS15) and \ + (val != PRBS_MODE_PRBS23) and \ + (val != PRBS_MODE_PRBS31)): + raise Exception("set_prbs_gen_prbs_pat: unsupported PRBS pattern selected: " + str(hex(val))) + # bit[4] to 0x008[4] + self.masked_write((addr_off << 11) + 0x008, 0x0010, val & 0x0010) + # bits[3:0] to 0x007[7:4] + self.masked_write((addr_off << 11) + 0x007, 0x00F0, (val & 0x000F) << 4) + + def get_prbs_gen_ser_mode(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_gen_ser_mode") + read = self.masked_read((addr_off << 11) + 0x110, 0x0007) + if (read == PRBS_GENERATOR_SERIALIZER_MODE_64BIT): + return 64 + elif (read == PRBS_GENERATOR_SERIALIZER_MODE_10BIT): + return 10 + else: + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_WARN): + print("WARN: get_prbs_gen_ser_mode: unknown PRBS generator serializer mode: " + str(hex(read))) + return 0 + + def set_prbs_gen_ser_mode(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_gen_ser_mode") + if type(val) is int: + val = prbs_generator_serializer_mode_mapping[val] + else: + raise Exception("set_prbs_gen_ser_mode: argument given is not an integer") + if ((val != PRBS_GENERATOR_SERIALIZER_MODE_64BIT) and + (val != PRBS_GENERATOR_SERIALIZER_MODE_10BIT)): + raise Exception("set_prbs_gen_ser_mode: unsupported PRBS generator serializer mode: " + str(hex(val))) + self.masked_write((addr_off << 11) + 0x110, 0x0007, val & 0x0007) + + # PRBS Verifier registers + def get_prbs_ver_prbs_clken(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_ver_prbs_clken") + return bool(self.masked_read((addr_off << 11) + 0x00A, 0x0080)) + + def set_prbs_ver_prbs_clken(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_ver_prbs_clken") + self.masked_write((addr_off << 11) + 0x00A, 0x0080, 0x0080 if val else 0x0000) + + def get_prbs_ver_rx_prbs_mask(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_ver_rx_prbs_mask") + return (self.masked_read((addr_off << 11) + 0x00B, 0x000C) >> 2) + + def set_prbs_ver_rx_prbs_mask(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_ver_rx_prbs_mask") + self.masked_write((addr_off << 11) + 0x00B, 0x000C, val << 2) + + def get_prbs_ver_prbs_pat(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_ver_prbs_pat") + # bit[4] from 0x00C[0] + msb = self.masked_read((addr_off << 11) + 0x00C, 0x0001) + # bits[3:0] from 0x00B[7:4] + lsb = self.masked_read((addr_off << 11) + 0x00B, 0x00F0) >> 4 + val = (msb << 4) + lsb + return val + + def set_prbs_ver_prbs_pat(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_ver_prbs_pat") + # check if argument is a string and map it to register value + if type(val) is str: + val = prbs_mode_mapping[val] + else: + raise Exception("set_prbs_ver_prbs_pat: argument given is not a string") + if ((val != PRBS_MODE_OFF) and \ + (val != PRBS_MODE_PRBS7) and \ + (val != PRBS_MODE_PRBS9) and \ + (val != PRBS_MODE_PRBS15) and \ + (val != PRBS_MODE_PRBS23) and \ + (val != PRBS_MODE_PRBS31)): + raise Exception("set_prbs_ver_prbs_pat: unsupported PRBS pattern selected: " + str(hex(val))) + # bit[4] to 0x00C[0] + self.masked_write((addr_off << 11) + 0x00C, 0x0001, (val & 0x0010) >> 4) + # bits[3:0] to 0x00B[7:4] + self.masked_write((addr_off << 11) + 0x00B, 0x00F0, (val & 0x000F) << 4) + + def get_prbs_ver_prbs9_dwidth(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_ver_prbs9_dwidth") + return bool(self.masked_read((addr_off << 11) + 0x00C, 0x0008)) + + def set_prbs_ver_prbs9_dwidth(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_ver_prbs9_dwidth") + self.masked_write((addr_off << 11) + 0x00C, 0x0008, 0x0008 if val else 0x0000) + + def get_prbs_ver_deser_factor(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_ver_deser_factor") + read = self.masked_read((addr_off << 11) + 0x13F, 0x000F) + if (read == PRBS_VERIFIER_DESERIALIZER_FACTOR_64BIT): + return 64 + elif (read == PRBS_VERIFIER_DESERIALIZER_FACTOR_10BIT): + return 10 + else: + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_WARN): + print("WARN: get_prbs_ver_deser_factor: unknown PRBS verifier deserializer factor: " + str(hex(read))) + return 0 + + def set_prbs_ver_deser_factor(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_ver_deser_factor") + if type(val) is int: + val = prbs_verifier_deserializer_factor_mapping[val] + else: + raise Exception("set_prbs_ver_deser_factor: argument given is not an integer") + if ((val != PRBS_VERIFIER_DESERIALIZER_FACTOR_64BIT) and + (val != PRBS_VERIFIER_DESERIALIZER_FACTOR_10BIT)): + raise Exception("set_prbs_ver_deser_factor: unsupported PRBS verifier deserializer factor: " + str(hex(val))) + self.masked_write((addr_off << 11) + 0x13F, 0x000F, val & 0x000F) + + # PRBS Soft Accumulators registers + def get_prbs_soft_acc_prbs_counter_en(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_soft_acc_prbs_counter_en") + return bool(self.masked_read((addr_off << 11) + 0x500, 0x0001)) + + def set_prbs_soft_acc_prbs_counter_en(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_soft_acc_prbs_counter_en") + self.masked_write((addr_off << 11) + 0x500, 0x0001, 0x0001 if val else 0x0000) + + def get_prbs_soft_acc_prbs_reset(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_soft_acc_prbs_reset") + return bool(self.masked_read((addr_off << 11) + 0x500, 0x0002)) + + def set_prbs_soft_acc_prbs_reset(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_soft_acc_prbs_reset") + self.masked_write((addr_off << 11) + 0x500, 0x0002, 0x0002 if val else 0x0000) + + def get_prbs_soft_acc_prbs_snap(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_soft_acc_prbs_snap") + return bool(self.masked_read((addr_off << 11) + 0x500, 0x0004)) + + def set_prbs_soft_acc_prbs_snap(self, addr_off, val): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_soft_acc_prbs_snap") + self.masked_write((addr_off << 11) + 0x500, 0x0004, 0x0004 if val else 0x0000) + + def get_prbs_soft_acc_prbs_done(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_soft_acc_prbs_done") + return bool(self.masked_read((addr_off << 11) + 0x500, 0x0008)) + + def get_prbs_soft_acc_prbs_acc_err_cnt(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_soft_acc_prbs_acc_err_cnt") + val = 0 + move_iter = 0 + # accumulated error count under addresses 0x507 - 0x501 + for addr in range(0x501, 0x508): + # use only two bits from the most significant byte + if (addr == 0x507): + mask = 0x0003 + else: + mask = 0x00FF + read = self.masked_read((addr_off << 11) + addr, mask) + # move each byte (8 * byte indicator) bits to the right + val += read << (move_iter * 8) + move_iter += 1 + return val + + def get_prbs_soft_acc_prbs_acc_bit_cnt(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: get_prbs_soft_acc_prbs_acc_bit_cnt") + val = 0 + move_iter = 0 + # accumulated bit count under addresses 0x513 - 0x50D + for addr in range(0x50D, 0x514): + # use only two bits from the most significant byte + if (addr == 0x513): + mask = 0x0003 + else: + mask = 0x00FF + read = self.masked_read((addr_off << 11) + addr, mask) + # move each byte (8 * byte indicator) bits to the right + val += read << (move_iter * 8) + move_iter += 1 + return val + + # function sets transceiver PRBS components to do BER measurement according to Intel example found on + # https://community.intel.com/t5/FPGA-Wiki/High-Speed-Transceiver-Demo-Designs-Stratix-10-GX-Series/ta-p/735749 + # and downloaded via link https://www.intel.com/content/dam/altera-www/global/en_US/uploads/1/1c/Stratix10GX_software_lib.zip + # no guide references for these operations have ben found in L- and H-Tile Transceiver PHY User Guide 683621 | 2022.07.20 + def set_transceiver_prbs_components(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_transceiver_prbs_components") + self.masked_write((addr_off << 11) + 0x164, 0x80, 0x80) + self.masked_write((addr_off << 11) + 0x210, 0x1F, 0x09) + self.masked_write((addr_off << 11) + 0x212, 0xE0, 0x00) + self.masked_write((addr_off << 11) + 0x213, 0xFF, 0x47) + self.masked_write((addr_off << 11) + 0x214, 0x01, 0x00) + self.masked_write((addr_off << 11) + 0x215, 0x01, 0x00) + self.masked_write((addr_off << 11) + 0x218, 0xC1, 0x40) + self.masked_write((addr_off << 11) + 0x223, 0x1F, 0x00) + self.masked_write((addr_off << 11) + 0x300, 0x3F, 0x00) + self.masked_write((addr_off << 11) + 0x312, 0xFF, 0x07) + self.masked_write((addr_off << 11) + 0x313, 0xFF, 0x02) + self.masked_write((addr_off << 11) + 0x315, 0x47, 0x00) + self.masked_write((addr_off << 11) + 0x318, 0x03, 0x02) + self.masked_write((addr_off << 11) + 0x31A, 0x1C, 0x04) + self.masked_write((addr_off << 11) + 0x320, 0x07, 0x02) + self.masked_write((addr_off << 11) + 0x321, 0x1E, 0x18) + self.masked_write((addr_off << 11) + 0x322, 0x73, 0x41) + + # function sets PRBS generator and PRBS verifier + def set_prbs_gen_and_ver(self, addr_off, \ + prbs_pattern = "prbs31", \ + use_bounded_channels_config = False, \ + serializer_mode = 64, \ + deserializer_factor = 64): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_gen_and_ver") + self.set_prbs_gen_prbs_pat(addr_off, prbs_pattern) + self.set_prbs_ver_prbs_pat(addr_off, prbs_pattern) + self.set_prbs_gen_ser_mode(addr_off, serializer_mode) + self.masked_write((addr_off << 11) + 0x06, 0xCF, 0x44) + self.masked_write((addr_off << 11) + 0x0B, 0x0E, 0x00) + self.masked_write((addr_off << 11) + 0x0C, 0x0A, 0x00) + self.set_prbs_ver_deser_factor(addr_off, serializer_mode) + self.masked_write((addr_off << 11) + 0x0A, 0x80, 0x80) + self.masked_write((addr_off << 11) + 0x500, 0x07, 0x01) + if (use_bounded_channels_config): + self.set_transceiver_bounded_channels_configuration(addr_off) + + # function performs bounded channels transceiver configuration + # reverse engineered from transceiver register space dump + # taken after successful BERT test for PCIe transceiver + # neither Intel SW example perform these steps, nor they have not been described in + # L- and H-Tile Transceiver PHY User Guide 683621 | 2022.07.20 + def set_transceiver_bounded_channels_configuration(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_transceiver_bounded_channels_configuration") + self.masked_write((addr_off << 11) + 0x0B, 0x02, 0x02) + self.masked_write((addr_off << 11) + 0x0C, 0x02, 0x02) + self.masked_write((addr_off << 11) + 0x10A, 0x01, 0x00) + self.masked_write((addr_off << 11) + 0x10B, 0x01, 0x01) + self.masked_write((addr_off << 11) + 0x111, 0x19, 0x18) + self.masked_write((addr_off << 11) + 0x123, 0xC0, 0x80) + self.masked_write((addr_off << 11) + 0x12A, 0x90, 0x90) + + # function sets PRBS accumulator + def set_prbs_soft_accumulator(self, addr_off): + if (PRINT_VERBOSITY >= PRINT_VERBOSITY_TRACE): + print("TRACE: set_prbs_soft_accumulator") + self.set_prbs_soft_acc_prbs_counter_en(addr_off, 1) + self.set_prbs_soft_acc_prbs_reset(addr_off, 1) + self.set_prbs_soft_acc_prbs_reset(addr_off, 0) + self.set_prbs_soft_acc_prbs_snap(addr_off, 0) + if (self.get_prbs_soft_acc_prbs_acc_err_cnt(addr_off)): + raise Exception("set_prbs_soft_accumulator: PRBS error counter has not been reseted") + if (self.get_prbs_soft_acc_prbs_acc_bit_cnt(addr_off)): + raise Exception("set_prbs_soft_accumulator: PRBS bit counter has not been reseted") + +node.register(LTileHTileNode, 0x9A83) + diff --git a/python/xfcp/node.py b/python/xfcp/node.py index c8d9863a..01f59ef7 100644 --- a/python/xfcp/node.py +++ b/python/xfcp/node.py @@ -113,6 +113,16 @@ def get_by_path(self, path): return n + def get_by_name(self, name, recursive=True): + for n in self.children: + if n.name == name: + return n + if recursive and len(n.children): + ret = n.get_by_name(name, recursive) + if ret: + return ret + return None + def find_by_type(self, t, prefix=16): l = [] diff --git a/rtl/avalon_mm_addr_conv.v b/rtl/avalon_mm_addr_conv.v new file mode 100644 index 00000000..79e18c42 --- /dev/null +++ b/rtl/avalon_mm_addr_conv.v @@ -0,0 +1,93 @@ +/* + +Copyright (c) 2022 Missing Link Electronics, Inc. + +Author: Karol Budniak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Description: This module implements a conversion from (double-word aligned) +Byte-Addressing (the two LSBs are always zero) to Word-Addressing by removing +the two LSBs and optionally expanding the MSBs with zero, if required to +satisfy the requested target address width. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +module avalon_mm_addr_conv #( + /* + * The only one tested use case is ADDR_WIDTH = 32 + * not all widths are supported neither have not been + * tested however may work as well + */ + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter BYTEENABLE_WIDTH = (DATA_WIDTH/8) +) +( + /* + * Avalon MM in interface + */ + input wire [ADDR_WIDTH-1:0] avalon_mm_in_address, + input wire [BYTEENABLE_WIDTH-1:0] avalon_mm_in_byteenable, + input wire avalon_mm_in_read, + output wire [DATA_WIDTH-1:0] avalon_mm_in_readdata, + output wire [1:0] avalon_mm_in_response, + input wire avalon_mm_in_write, + input wire [DATA_WIDTH-1:0] avalon_mm_in_writedata, + output wire avalon_mm_in_waitrequest, + + /* + * Avalon MM out interface + */ + output wire [ADDR_WIDTH-1:0] avalon_mm_out_address, + output wire [BYTEENABLE_WIDTH-1:0] avalon_mm_out_byteenable, + output wire avalon_mm_out_read, + input wire [DATA_WIDTH-1:0] avalon_mm_out_readdata, + input wire [1:0] avalon_mm_out_response, + output wire avalon_mm_out_write, + output wire [DATA_WIDTH-1:0] avalon_mm_out_writedata, + input wire avalon_mm_out_waitrequest +); + +// bus width assertions +initial begin + if (ADDR_WIDTH < 4) begin + $error("Error: Avalon-MM address width smaller than 4, this is not supported"); + $finish; + end +end + +/* + * double word address becomes byte address + */ +assign avalon_mm_out_address = {2'b00, avalon_mm_in_address[ADDR_WIDTH-1:2]}; +assign avalon_mm_out_byteenable = avalon_mm_in_byteenable; +assign avalon_mm_out_read = avalon_mm_in_read; +assign avalon_mm_in_readdata = avalon_mm_out_readdata; +assign avalon_mm_in_response = avalon_mm_out_response; +assign avalon_mm_out_write = avalon_mm_in_write; +assign avalon_mm_out_writedata = avalon_mm_in_writedata; +assign avalon_mm_in_waitrequest = avalon_mm_out_waitrequest; + +endmodule + diff --git a/rtl/axil_avalon_mm.v b/rtl/axil_avalon_mm.v new file mode 100644 index 00000000..97fdd531 --- /dev/null +++ b/rtl/axil_avalon_mm.v @@ -0,0 +1,275 @@ +/* + +Copyright (c) 2022 Missing Link Electronics, Inc. + +Author: Karol Budniak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +Description: The AXI4-Lite to Avalon-MM bridge implementation. + +Convert a single AXI4-Lite read/write transfer into Avalon-MM transfer +Avalon-MM specification taken from Intel Avalon Interface Specifications +version 683091 | 2022.01.24 + +Features not supported: +a) write/read burst +b) AXI4-Lite protection +c) Avalon-MM interface: 'lock' signal, pipelined read with + fixed and with variable latency, other than default properties +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +module axil_avalon_mm #( + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STRB_WIDTH = (DATA_WIDTH/8), + parameter BYTEENABLE_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interface + */ + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [DATA_WIDTH-1:0] s_axil_wdata, + input wire [STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * Avalon MM host interface + */ + output wire [ADDR_WIDTH-1:0] h_avalon_mm_address, + output wire [BYTEENABLE_WIDTH-1:0] h_avalon_mm_byteenable, + output wire h_avalon_mm_read, + input wire [DATA_WIDTH-1:0] h_avalon_mm_readdata, + input wire [1:0] h_avalon_mm_response, + output wire h_avalon_mm_write, + output wire [DATA_WIDTH-1:0] h_avalon_mm_writedata, + input wire h_avalon_mm_waitrequest +); + +reg s_axil_awready_reg = 1'b0; +reg s_axil_wready_reg = 1'b0; +reg [1:0] s_axil_bresp_reg; +reg s_axil_bvalid_reg = 1'b0; +reg s_axil_arready_reg = 1'b0; +reg [DATA_WIDTH-1:0] s_axil_rdata_reg; +reg [1:0] s_axil_rresp_reg; +reg s_axil_rvalid_reg = 1'b0; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = s_axil_bresp_reg; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = s_axil_rresp_reg; +assign s_axil_rvalid = s_axil_rvalid_reg; + +reg [ADDR_WIDTH-1:0] h_avalon_mm_address_reg = 0; +reg [BYTEENABLE_WIDTH-1:0] h_avalon_mm_byteenable_reg = 0; +reg h_avalon_mm_read_reg = 1'b0; +reg h_avalon_mm_write_reg = 1'b0; +reg [DATA_WIDTH-1:0] h_avalon_mm_writedata_reg = 0; + +assign h_avalon_mm_address = h_avalon_mm_address_reg; +assign h_avalon_mm_byteenable = h_avalon_mm_byteenable_reg; +assign h_avalon_mm_read = h_avalon_mm_read_reg; +assign h_avalon_mm_write = h_avalon_mm_write_reg; +assign h_avalon_mm_writedata = h_avalon_mm_writedata_reg; + +localparam [2:0] + STATE_IDLE = 4'd0, + STATE_START_AVALON_WRITE = 4'd1, + STATE_START_AVALON_READ = 4'd2, + STATE_CHECK_AVALON_WAITREQUEST = 4'd3, + STATE_VALIDATE_AXI_BRESP = 4'd4, + STATE_WAIT_AXI_BREADY = 4'd5, + STATE_VALIDATE_AXI_RVALID = 4'd6, + STATE_WAIT_AXI_RREADY = 4'd7; + +reg [2:0] state_reg = STATE_IDLE; +reg [2:0] state_next; +reg rnw = 1'b0; +reg [ADDR_WIDTH-1:0] address; +reg [DATA_WIDTH-1:0] writedata; +reg [DATA_WIDTH-1:0] readdata; +reg [BYTEENABLE_WIDTH-1:0] byteenable; +reg [1:0] response; + +always @(posedge clk) begin + state_reg <= state_next; + if (rst) begin + state_reg <= STATE_IDLE; + end +end + +always @* begin + state_next = state_reg; + case (state_reg) + STATE_IDLE: begin + if (s_axil_awvalid && s_axil_awready && + s_axil_wvalid && s_axil_wready) + begin + state_next = STATE_START_AVALON_WRITE; + end else if (s_axil_arvalid && s_axil_arready) begin + state_next = STATE_START_AVALON_READ; + end + end + STATE_START_AVALON_WRITE, + STATE_START_AVALON_READ: begin + state_next = STATE_CHECK_AVALON_WAITREQUEST; + end + STATE_CHECK_AVALON_WAITREQUEST: begin + if (!h_avalon_mm_waitrequest) begin + if (rnw) begin + state_next = STATE_VALIDATE_AXI_RVALID; + end else begin + state_next = STATE_VALIDATE_AXI_BRESP; + end + end + end + STATE_VALIDATE_AXI_BRESP: begin + state_next = STATE_WAIT_AXI_BREADY; + end + STATE_WAIT_AXI_BREADY: begin + if (s_axil_bready) begin + state_next = STATE_IDLE; + end + end + STATE_VALIDATE_AXI_RVALID: begin + state_next = STATE_WAIT_AXI_RREADY; + end + STATE_WAIT_AXI_RREADY: begin + if (s_axil_rready) begin + state_next = STATE_IDLE; + end + end + default: begin + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_arready_reg <= 1'b0; + + case (state_reg) + STATE_IDLE: begin + if (s_axil_awvalid && !s_axil_awready && + s_axil_wvalid && !s_axil_wready) + begin + s_axil_awready_reg <= 1'b1; + s_axil_wready_reg <= 1'b1; + address <= s_axil_awaddr; + writedata <= s_axil_wdata; + byteenable <= s_axil_wstrb; + end else if (s_axil_arvalid && !s_axil_arready) begin + s_axil_arready_reg <= 1'b1; + address <= s_axil_araddr; + end + end + STATE_START_AVALON_WRITE: begin + h_avalon_mm_address_reg <= address; + h_avalon_mm_byteenable_reg <= byteenable; + h_avalon_mm_writedata_reg <= writedata; + h_avalon_mm_write_reg <= 1'b1; + rnw <= 1'b0; + end + STATE_START_AVALON_READ: begin + h_avalon_mm_address_reg <= address; + h_avalon_mm_byteenable_reg <= byteenable; + h_avalon_mm_read_reg <= 1'b1; + rnw <= 1'b1; + end + STATE_CHECK_AVALON_WAITREQUEST: begin + if (!h_avalon_mm_waitrequest) begin + h_avalon_mm_write_reg <= 1'b0; + h_avalon_mm_read_reg <= 1'b0; + // capture read data also in case of write transfer + // it will not be used but makes will simplify logic + readdata <= h_avalon_mm_readdata; + // response encoding is the same for AXI4-Lite and Avalon-MM + // according to specification Intel Avalon Interface + // Specifications 683091 | 2022.01.24 + // chapter 3.2 Avalon Memory Mapped Interface Signal Roles + response <= h_avalon_mm_response; + end + end + STATE_VALIDATE_AXI_BRESP: begin + s_axil_bresp_reg <= response; + s_axil_bvalid_reg <= 1'b1; + end + STATE_WAIT_AXI_BREADY: begin + if (s_axil_bready) begin + s_axil_bvalid_reg <= 1'b0; + end + end + STATE_VALIDATE_AXI_RVALID: begin + s_axil_rdata_reg <= readdata; + s_axil_rresp_reg <= response; + s_axil_rvalid_reg <= 1'b1; + end + STATE_WAIT_AXI_RREADY: begin + if (s_axil_rready) begin + s_axil_rvalid_reg <= 1'b0; + end + end + default: begin + s_axil_bvalid_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + h_avalon_mm_write_reg <= 1'b0; + h_avalon_mm_read_reg <= 1'b0; + end + endcase + + if (rst) begin + s_axil_bvalid_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + h_avalon_mm_write_reg <= 1'b0; + h_avalon_mm_read_reg <= 1'b0; + end +end + +endmodule + diff --git a/rtl/xfcp_mod_avmm.v b/rtl/xfcp_mod_avmm.v new file mode 100644 index 00000000..62d1d12d --- /dev/null +++ b/rtl/xfcp_mod_avmm.v @@ -0,0 +1,237 @@ +/* + +Copyright (c) 2022 Missing Link Electronics, Inc. + +Author: Karol Budniak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Description: The XFCP Avalon-MM wrapper with XFCP to AXI4-Lite converter +AXI4-Lite to Avalon-MM converter and Avalon-MM address converter instances + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +module xfcp_mod_avmm # +( + parameter XFCP_ID_TYPE = 16'h0001, + parameter XFCP_ID_STR = "AXIL Master", + /* + * The only one tested use case is ADDR_WIDTH = 32 + * not all widths are supported neither have not been + * tested however may work as well + */ + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STRB_WIDTH = (DATA_WIDTH/8), + parameter BYTEENABLE_WIDTH = (DATA_WIDTH/8), + parameter USE_ADDR_CONVERTER = 0 +) +( + input wire clk, + input wire rst, + + /* + * XFCP upstream interface + */ + input wire [7:0] up_xfcp_in_tdata, + input wire up_xfcp_in_tvalid, + output wire up_xfcp_in_tready, + input wire up_xfcp_in_tlast, + input wire up_xfcp_in_tuser, + + output wire [7:0] up_xfcp_out_tdata, + output wire up_xfcp_out_tvalid, + input wire up_xfcp_out_tready, + output wire up_xfcp_out_tlast, + output wire up_xfcp_out_tuser, + + /* + * Avalon MM host interface + */ + output wire [ADDR_WIDTH-1:0] h_avalon_mm_address, + output wire [BYTEENABLE_WIDTH-1:0] h_avalon_mm_byteenable, + output wire h_avalon_mm_read, + input wire [DATA_WIDTH-1:0] h_avalon_mm_readdata, + input wire [1:0] h_avalon_mm_response, + output wire h_avalon_mm_write, + output wire [DATA_WIDTH-1:0] h_avalon_mm_writedata, + input wire h_avalon_mm_waitrequest +); + +/* + * AXI4-Lite internal interface + */ +wire [ADDR_WIDTH-1:0] axil_awaddr; +wire [2:0] axil_awprot; +wire axil_awvalid; +wire axil_awready; +wire [DATA_WIDTH-1:0] axil_wdata; +wire [STRB_WIDTH-1:0] axil_wstrb; +wire axil_wvalid; +wire axil_wready; +wire [1:0] axil_bresp; +wire axil_bvalid; +wire axil_bready; +wire [ADDR_WIDTH-1:0] axil_araddr; +wire [2:0] axil_arprot; +wire axil_arvalid; +wire axil_arready; +wire [DATA_WIDTH-1:0] axil_rdata; +wire [1:0] axil_rresp; +wire axil_rvalid; +wire axil_rready; + + +/* + * Avalon MM internal interface + */ +wire [ADDR_WIDTH-1:0] avalon_mm_address; +wire [BYTEENABLE_WIDTH-1:0] avalon_mm_byteenable; +wire avalon_mm_read; +wire [DATA_WIDTH-1:0] avalon_mm_readdata; +wire [1:0] avalon_mm_response; +wire avalon_mm_write; +wire [DATA_WIDTH-1:0] avalon_mm_writedata; +wire avalon_mm_waitrequest; + +xfcp_mod_axil #( + .XFCP_ID_TYPE(XFCP_ID_TYPE), + .XFCP_ID_STR(XFCP_ID_STR), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH) +) +xfcp_mod_axil_inst ( + .clk(clk), + .rst(rst), + .up_xfcp_in_tdata(up_xfcp_in_tdata), + .up_xfcp_in_tvalid(up_xfcp_in_tvalid), + .up_xfcp_in_tready(up_xfcp_in_tready), + .up_xfcp_in_tlast(up_xfcp_in_tlast), + .up_xfcp_in_tuser(up_xfcp_in_tuser), + .up_xfcp_out_tdata(up_xfcp_out_tdata), + .up_xfcp_out_tvalid(up_xfcp_out_tvalid), + .up_xfcp_out_tready(up_xfcp_out_tready), + .up_xfcp_out_tlast(up_xfcp_out_tlast), + .up_xfcp_out_tuser(up_xfcp_out_tuser), + .m_axil_awaddr(axil_awaddr), + .m_axil_awprot(axil_awprot), + .m_axil_awvalid(axil_awvalid), + .m_axil_awready(axil_awready), + .m_axil_wdata(axil_wdata), + .m_axil_wstrb(axil_wstrb), + .m_axil_wvalid(axil_wvalid), + .m_axil_wready(axil_wready), + .m_axil_bresp(axil_bresp), + .m_axil_bvalid(axil_bvalid), + .m_axil_bready(axil_bready), + .m_axil_araddr(axil_araddr), + .m_axil_arprot(axil_arprot), + .m_axil_arvalid(axil_arvalid), + .m_axil_arready(axil_arready), + .m_axil_rdata(axil_rdata), + .m_axil_rresp(axil_rresp), + .m_axil_rvalid(axil_rvalid), + .m_axil_rready(axil_rready) +); + + +axil_avalon_mm #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH) +) +axil_avalon_mm_inst ( + .clk(clk), + .rst(rst), + + .s_axil_awaddr(axil_awaddr), + .s_axil_awprot(axil_awprot), + .s_axil_awvalid(axil_awvalid), + .s_axil_awready(axil_awready), + .s_axil_wdata(axil_wdata), + .s_axil_wstrb(axil_wstrb), + .s_axil_wvalid(axil_wvalid), + .s_axil_wready(axil_wready), + .s_axil_bresp(axil_bresp), + .s_axil_bvalid(axil_bvalid), + .s_axil_bready(axil_bready), + .s_axil_araddr(axil_araddr), + .s_axil_arprot(axil_arprot), + .s_axil_arvalid(axil_arvalid), + .s_axil_arready(axil_arready), + .s_axil_rdata(axil_rdata), + .s_axil_rresp(axil_rresp), + .s_axil_rvalid(axil_rvalid), + .s_axil_rready(axil_rready), + + + .h_avalon_mm_address(avalon_mm_address), + .h_avalon_mm_byteenable(avalon_mm_byteenable), + .h_avalon_mm_read(avalon_mm_read), + .h_avalon_mm_readdata(avalon_mm_readdata), + .h_avalon_mm_response(avalon_mm_response), + .h_avalon_mm_write(avalon_mm_write), + .h_avalon_mm_writedata(avalon_mm_writedata), + .h_avalon_mm_waitrequest(avalon_mm_waitrequest) +); + +generate +if (USE_ADDR_CONVERTER) begin + + avalon_mm_addr_conv #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH) + ) + avalon_mm_addr_conv_inst ( + .avalon_mm_in_address(avalon_mm_address), + .avalon_mm_in_byteenable(avalon_mm_byteenable), + .avalon_mm_in_read(avalon_mm_read), + .avalon_mm_in_readdata(avalon_mm_readdata), + .avalon_mm_in_response(avalon_mm_response), + .avalon_mm_in_write(avalon_mm_write), + .avalon_mm_in_writedata(avalon_mm_writedata), + .avalon_mm_in_waitrequest(avalon_mm_waitrequest), + + .avalon_mm_out_address(h_avalon_mm_address), + .avalon_mm_out_byteenable(h_avalon_mm_byteenable), + .avalon_mm_out_read(h_avalon_mm_read), + .avalon_mm_out_readdata(h_avalon_mm_readdata), + .avalon_mm_out_response(h_avalon_mm_response), + .avalon_mm_out_write(h_avalon_mm_write), + .avalon_mm_out_writedata(h_avalon_mm_writedata), + .avalon_mm_out_waitrequest(h_avalon_mm_waitrequest) + ); +end else begin + + assign h_avalon_mm_address = avalon_mm_address; + assign h_avalon_mm_byteenable = avalon_mm_byteenable; + assign h_avalon_mm_read = avalon_mm_read; + assign avalon_mm_readdata = h_avalon_mm_readdata; + assign avalon_mm_response = h_avalon_mm_response; + assign h_avalon_mm_write = avalon_mm_write; + assign h_avalon_mm_writedata = avalon_mm_writedata; + assign avalon_mm_waitrequest = h_avalon_mm_waitrequest; +end + +endgenerate + +endmodule