diff --git a/README.md b/README.md index 4bedd902..23435b54 100644 --- a/README.md +++ b/README.md @@ -116,3 +116,43 @@ cd ../src/lib/lowlevel/pcie_uram/driver make sudo insmod usdr_pcie_uram.ko ```` + +## Finding the installed `usdr` library + +You can find and use the installed library in two common ways — with `pkg-config` or with CMake's `find_package`. + +- Using pkg-config + +```bash +# set to the install prefix used during `make install` +export INSTALL_PREFIX=/path/to/install +export PKG_CONFIG_PATH="$INSTALL_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH" + +# Get version, cflags and libs +pkg-config --modversion usdr +pkg-config --cflags usdr # include flags, e.g. -I/path/to/install/include +pkg-config --libs usdr # link flags, e.g. -L/path/to/install/lib -lusdr +``` + +- Using CMake `find_package` (CONFIG-mode) + +Create a small CMake project that uses the installed package (replace `/path/to/install` with your install prefix): + +```cmake +cmake_minimum_required(VERSION 3.8) +project(example C) +find_package(usdr CONFIG REQUIRED) +add_executable(example main.c) +target_link_libraries(example PRIVATE usdr::usdr) +``` + +Then configure and build: + +```bash +mkdir build && cd build +cmake -DCMAKE_PREFIX_PATH=/path/to/install .. +cmake --build . +``` + +If `find_package` fails, ensure the file `usdrConfig.cmake` is installed under `/lib/cmake/usdr/` (or the corresponding `libdir/cmake/usdr` for your system) and pass that prefix via `CMAKE_PREFIX_PATH`. + diff --git a/Taskfile.box.yaml b/Taskfile.box.yaml index af9b1d87..ef2488b3 100644 --- a/Taskfile.box.yaml +++ b/Taskfile.box.yaml @@ -7,7 +7,7 @@ vars: NOW: sh: "date +%Y%m%d%H%m%S" LABEL: - sh: "echo box-$(basename {{.ROOT_DIR}})" + sh: "echo box-$(basename {{.ROOT_DIR}} | tr '[:upper:]' '[:lower:]')" DOCKER_FILE_PREFIX: "docker/Dockerfile.box" tasks: diff --git a/docker/Taskfile.yaml b/docker/Taskfile.yaml index 101cb792..aaa5b2be 100644 --- a/docker/Taskfile.yaml +++ b/docker/Taskfile.yaml @@ -42,6 +42,8 @@ tasks: - prepare dir: /work/build/source cmds: + - echo "#!/usr/bin/env sh" > /usr/bin/lintian # dirty hack to disable lintian + - echo "exit 0" >> /usr/bin/lintian - debuild -us -uc -eDH_VERBOSE=1 - cp -ra /work/build/*.deb /work/output/{{.OUTPUT_DIR_NAME}}/ diff --git a/packaging/debian-bookworm/changelog b/packaging/debian-bookworm/changelog index ce33f529..29aeeb4c 100644 --- a/packaging/debian-bookworm/changelog +++ b/packaging/debian-bookworm/changelog @@ -1,5 +1,5 @@ -usdr (0.9.9~bookworm0) stable; urgency=low +usdr (0.9.10b~bookworm0) stable; urgency=low * Fixes and improvements - -- Ivan Kolesnikov Sun, 30 Jan 2025 00:00:00 +0000 + -- Ivan Kolesnikov Thu, 26 Jun 2025 00:00:00 +0000 diff --git a/packaging/debian-bookworm/control b/packaging/debian-bookworm/control index 762a98c0..fd306b65 100644 --- a/packaging/debian-bookworm/control +++ b/packaging/debian-bookworm/control @@ -2,7 +2,7 @@ Source: usdr Section: misc Priority: optional Maintainer: Ivan Kolesnikov -Standards-Version: 0.9.9 +Standards-Version: 0.9.10b Homepage: https://github.com/wavelet-lab/usdr-lib Vcs-Browser: https://github.com/wavelet-lab/usdr-lib Vcs-Git: https://github.com/wavelet-lab/usdr-lib.git diff --git a/packaging/ubuntu-bionic/changelog b/packaging/ubuntu-bionic/changelog index b883a498..e0128cee 100644 --- a/packaging/ubuntu-bionic/changelog +++ b/packaging/ubuntu-bionic/changelog @@ -1,5 +1,5 @@ -usdr (0.9.9~bionic0) bionic; urgency=low +usdr (0.9.10b~bionic0) bionic; urgency=low * Fixes and improvements - -- Ivan Kolesnikov Sun, 30 Jan 2025 00:00:00 +0000 + -- Ivan Kolesnikov Thu, 26 Jun 2025 00:00:00 +0000 diff --git a/packaging/ubuntu-bionic/control b/packaging/ubuntu-bionic/control index 7b2c6d56..5ed65dc3 100644 --- a/packaging/ubuntu-bionic/control +++ b/packaging/ubuntu-bionic/control @@ -2,7 +2,7 @@ Source: usdr Section: misc Priority: optional Maintainer: Ivan Kolesnikov -Standards-Version: 0.9.9 +Standards-Version: 0.9.10b Homepage: https://github.com/wavelet-lab/usdr-lib Vcs-Browser: https://github.com/wavelet-lab/usdr-lib Vcs-Git: https://github.com/wavelet-lab/usdr-lib.git diff --git a/packaging/ubuntu-focal/changelog b/packaging/ubuntu-focal/changelog index 899b3a53..cd3f1604 100644 --- a/packaging/ubuntu-focal/changelog +++ b/packaging/ubuntu-focal/changelog @@ -1,5 +1,5 @@ -usdr (0.9.9~focal0) focal; urgency=low +usdr (0.9.10b~focal0) focal; urgency=low * Fixes and improvements - -- Ivan Kolesnikov Sun, 30 Jan 2025 00:00:00 +0000 + -- Ivan Kolesnikov Thu, 26 Jun 2025 00:00:00 +0000 diff --git a/packaging/ubuntu-focal/control b/packaging/ubuntu-focal/control index 1457d8aa..22bf7ee7 100644 --- a/packaging/ubuntu-focal/control +++ b/packaging/ubuntu-focal/control @@ -2,7 +2,7 @@ Source: usdr Section: misc Priority: optional Maintainer: Ivan Kolesnikov -Standards-Version: 0.9.9 +Standards-Version: 0.9.10b Homepage: https://github.com/wavelet-lab/usdr-lib Vcs-Browser: https://github.com/wavelet-lab/usdr-lib Vcs-Git: https://github.com/wavelet-lab/usdr-lib.git diff --git a/packaging/ubuntu-jammy/changelog b/packaging/ubuntu-jammy/changelog index 765ce6f8..68fcd0e0 100644 --- a/packaging/ubuntu-jammy/changelog +++ b/packaging/ubuntu-jammy/changelog @@ -1,5 +1,5 @@ -usdr (0.9.9~jammy0) jammy; urgency=low +usdr (0.9.10b~jammy0) jammy; urgency=low * Fixes and improvements - -- Ivan Kolesnikov Sun, 30 Jan 2025 00:00:00 +0000 + -- Ivan Kolesnikov Thu, 26 Jun 2025 00:00:00 +0000 diff --git a/packaging/ubuntu-jammy/control b/packaging/ubuntu-jammy/control index 1457d8aa..22bf7ee7 100644 --- a/packaging/ubuntu-jammy/control +++ b/packaging/ubuntu-jammy/control @@ -2,7 +2,7 @@ Source: usdr Section: misc Priority: optional Maintainer: Ivan Kolesnikov -Standards-Version: 0.9.9 +Standards-Version: 0.9.10b Homepage: https://github.com/wavelet-lab/usdr-lib Vcs-Browser: https://github.com/wavelet-lab/usdr-lib Vcs-Git: https://github.com/wavelet-lab/usdr-lib.git diff --git a/packaging/ubuntu-noble/changelog b/packaging/ubuntu-noble/changelog index 97c5c4e1..14b08ad8 100644 --- a/packaging/ubuntu-noble/changelog +++ b/packaging/ubuntu-noble/changelog @@ -1,5 +1,5 @@ -usdr (0.9.9~noble0) noble; urgency=low +usdr (0.9.10b~noble0) noble; urgency=low * Fixes and improvements - -- Ivan Kolesnikov Sun, 30 Jan 2025 00:00:00 +0000 + -- Ivan Kolesnikov Thu, 26 Jun 2025 00:00:00 +0000 diff --git a/packaging/ubuntu-noble/control b/packaging/ubuntu-noble/control index 2ca6f20f..a334dba1 100644 --- a/packaging/ubuntu-noble/control +++ b/packaging/ubuntu-noble/control @@ -2,7 +2,7 @@ Source: usdr Section: misc Priority: optional Maintainer: Ivan Kolesnikov -Standards-Version: 0.9.9 +Standards-Version: 0.9.10b Homepage: https://github.com/wavelet-lab/usdr-lib Vcs-Browser: https://github.com/wavelet-lab/usdr-lib Vcs-Git: https://github.com/wavelet-lab/usdr-lib.git diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 86d6008c..6900e30f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2024 Wavelet Lab +# Copyright (c) 2023-2026 Wavelet Lab # SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.8) @@ -34,7 +34,7 @@ option(ENABLE_DMONITOR "Enable debug tools" ON) option(ENABLE_VERILATOR "Enable verilator lowlevel bridge" OFF) option(ENABLE_SOAPY "Enable SopaySDR support" ON) option(ENABLE_GUI "Enable USDR QT5 simple GUI" ON) -option(ENABLE_TESTS "Enable tests" ON) +option(ENABLE_TESTS "Enable tests" OFF) add_feature_info(VerilatorBridge ENABLE_VERILATOR "Verilator support for emulated devices") @@ -94,3 +94,8 @@ message(STATUS "Install libs: ${CMAKE_INSTALL_LIBDIR}") message(STATUS "Shared libs: ${BUILD_SHARED_LIBS}") message(STATUS "Data dir: ${CMAKE_INSTALL_FULL_DATADIR}") message(STATUS "######################################################") + +include(cmake/UsdrPackage.cmake) + +# Use helper to generate/install CMake config, exported targets and pkg-config file +usdr_install_package(NAME usdr VERSION ${USDR_VERSION} TARGETS usdrTargets PKGCONFIG ON) diff --git a/src/Changelog.txt b/src/Changelog.txt index a6caef95..c1491801 100644 --- a/src/Changelog.txt +++ b/src/Changelog.txt @@ -1,3 +1,8 @@ +Release 0.9.10b (2025-06-26) +========================== + +- Fixes and improvements + Release 0.9.9 (2025-01-30) ========================== diff --git a/src/cmake/UsdrPackage.cmake b/src/cmake/UsdrPackage.cmake new file mode 100644 index 00000000..9f079f65 --- /dev/null +++ b/src/cmake/UsdrPackage.cmake @@ -0,0 +1,73 @@ +# Usdr CMake package helper +# Provides a function `usdr_install_package(NAME VERSION TARGETS PKGCONFIG )` + +include(CMakePackageConfigHelpers) +get_filename_component(USDR_PACKAGE_HELPER_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +function(usdr_install_package) + cmake_parse_arguments(_u "" "NAME;VERSION;PKGCONFIG" "TARGETS" ${ARGN}) + + if(NOT _u_NAME) + message(FATAL_ERROR "usdr_install_package: NAME is required") + endif() + if(NOT _u_VERSION) + message(FATAL_ERROR "usdr_install_package: VERSION is required") + endif() + + set(_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${_u_NAME}") + + # Helper dir (set at include-time) points to this file's directory + set(_usdr_helper_dir "${USDR_PACKAGE_HELPER_DIR}") + + # Configure and write CMake package config files + set(_config_in "${_usdr_helper_dir}/${_u_NAME}Config.cmake.in") + if(NOT IS_ABSOLUTE "${_config_in}") + get_filename_component(_config_in "${_config_in}" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + configure_package_config_file( + "${_config_in}" + "${CMAKE_CURRENT_BINARY_DIR}/${_u_NAME}Config.cmake" + INSTALL_DESTINATION "${_install_dir}" + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${_u_NAME}ConfigVersion.cmake" + VERSION ${_u_VERSION} + COMPATIBILITY AnyNewerVersion + ) + + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${_u_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${_u_NAME}ConfigVersion.cmake" + DESTINATION "${_install_dir}" + ) + + # Install exported CMake targets + if(_u_TARGETS) + install(EXPORT ${_u_TARGETS} + FILE ${_u_NAME}Targets.cmake + NAMESPACE ${_u_NAME}:: + DESTINATION "${_install_dir}" + ) + endif() + + # Optionally generate and install a pkg-config file + if(_u_PKGCONFIG STREQUAL "ON") + set(_pc_in "${_usdr_helper_dir}/${_u_NAME}.pc.in") + if(NOT IS_ABSOLUTE "${_pc_in}") + get_filename_component(_pc_in "${_pc_in}" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + # Provide substitutions for the pkg-config template + set(PACKAGE_NAME "${_u_NAME}") + set(PACKAGE_VERSION "${_u_VERSION}") + + configure_file("${_pc_in}" + "${CMAKE_CURRENT_BINARY_DIR}/${_u_NAME}.pc" @ONLY) + unset(PACKAGE_NAME CACHE) + unset(PACKAGE_VERSION CACHE) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${_u_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + endif() +endfunction() diff --git a/src/cmake/detect_arch.cmake b/src/cmake/detect_arch.cmake index a5f8fe72..2a6507cd 100644 --- a/src/cmake/detect_arch.cmake +++ b/src/cmake/detect_arch.cmake @@ -9,7 +9,7 @@ if(${WVLT_ARCH} STREQUAL "x86") message(STATUS "Will compile for ${WVLT_ARCH} architecture") message(STATUS "Intel SIMD intrinsics will be used") -elseif(${WVLT_ARCH} STREQUAL "x86_64") +elseif(${WVLT_ARCH} STREQUAL "x86_64" OR ${WVLT_ARCH} STREQUAL "AMD64") add_definitions(-DWVLT_ARCH_X86_64) set(WVLT_ARCH_X86_64 1 CACHE BOOL "Arch x86_64") message(STATUS "Will compile for ${WVLT_ARCH} architecture") diff --git a/src/cmake/usdr.pc.in b/src/cmake/usdr.pc.in new file mode 100644 index 00000000..71e54fdf --- /dev/null +++ b/src/cmake/usdr.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix}/@CMAKE_INSTALL_BINDIR@ +libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/usdr + +Name: @PACKAGE_NAME@ +Description: usdr library +URL: https://docs.wsdr.io +Version: @PACKAGE_VERSION@ +Requires: libusb-1.0 +Libs: -L${libdir} -l@PACKAGE_NAME@ +Cflags: -I${includedir} diff --git a/src/cmake/usdrConfig.cmake.in b/src/cmake/usdrConfig.cmake.in new file mode 100644 index 00000000..a2755b58 --- /dev/null +++ b/src/cmake/usdrConfig.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +# Find dependencies used by the usdr package +include(CMakeFindDependencyMacro) +find_dependency(LIBUSB_1 REQUIRED) + +# Import the targets exported by the usdr build +include("${CMAKE_CURRENT_LIST_DIR}/usdrTargets.cmake") diff --git a/src/dmonitor/pyqt5_widget.py b/src/dmonitor/pyqt5_widget.py index e48cceab..35b83c7e 100644 --- a/src/dmonitor/pyqt5_widget.py +++ b/src/dmonitor/pyqt5_widget.py @@ -1,25 +1,31 @@ #!/usr/bin/python3 -# Copyright (c) 2023-2024 Wavelet Lab +# Copyright (c) 2023-2026 Wavelet Lab # SPDX-License-Identifier: MIT import sys -from PyQt5.QtWidgets import * -from PyQt5.QtGui import QRegExpValidator, QPalette, QColor, QIcon -from PyQt5.QtCore import Qt, QObject, pyqtSignal, QRegExp +try: + from PySide6.QtWidgets import * + from PySide6.QtGui import QRegularExpressionValidator, QPalette, QColor, QIcon + from PySide6.QtCore import Qt, QObject, Signal, QRegularExpression + QT_VER = 6 +except ImportError: + from PyQt5.QtWidgets import * + from PyQt5.QtGui import QRegExpValidator, QPalette, QColor, QIcon + from PyQt5.QtCore import Qt, QObject, pyqtSignal, QRegExp + QT_VER = 5 + import math import configparser import re import time import os - ABS_PATH = os.path.dirname(os.path.abspath(__file__)) - class LongSpinBox(QAbstractSpinBox): - valueChanged = pyqtSignal(int) - clicked = pyqtSignal(Qt.MouseButton) + valueChanged = Signal(int) if QT_VER == 6 else pyqtSignal(int) + clicked = Signal(Qt.MouseButton)if QT_VER == 6 else pyqtSignal(Qt.MouseButton) def __init__(self, parent = None): super(LongSpinBox, self).__init__(parent) @@ -28,7 +34,11 @@ def __init__(self, parent = None): self.setMaximum(255) self.lineEdit().setText("0") self.lineEdit().textChanged.connect(lambda t: self.setValue(int(t))) - self.lineEdit().setValidator(QRegExpValidator(QRegExp("[0-9]*"), self.lineEdit())) + + if QT_VER == 6: + self.lineEdit().setValidator(QRegularExpressionValidator(QRegularExpression("[0-9]*"), self.lineEdit())) + else: + self.lineEdit().setValidator(QRegExpValidator(QRegExp("[0-9]*"), self.lineEdit())) def setMaximum(self, max_value): self.maximum = max_value @@ -312,11 +322,11 @@ def load_button_clicked(self)->None: if(not line): break - line = line.rstrip('\n').strip() + line = line.rstrip(r'\n').strip() if(found): - if re.match('^\[(.*)\]$', line) is not None: + if re.match(r'^\[(.*)\]$', line) is not None: break parts = line.split('=') @@ -334,14 +344,14 @@ def load_button_clicked(self)->None: regcnt += 1 #print('loaded 0x%04x=0x%04x' % (addr, val)) else: - found = ('[%s]' % ini_section_name) == line + found = (r'[%s]' % ini_section_name) == line if(found): - print('LOAD: found section [%s] in file "%s"' % (ini_section_name, inifile_name)) + print(r'LOAD: found section [%s] in file "%s"' % (ini_section_name, inifile_name)) if(not found): - print('LOAD: section [%s] not found in file "%s", registers were not loaded!' % (ini_section_name, inifile_name)) + print(r'LOAD: section [%s] not found in file "%s", registers were not loaded!' % (ini_section_name, inifile_name)) else: - print('LOAD: %d registers were loaded from "%s".[%s]' % (regcnt, inifile_name, ini_section_name)) + print(r'LOAD: %d registers were loaded from "%s".[%s]' % (regcnt, inifile_name, ini_section_name)) self.update() f.close() diff --git a/src/dmonitor/registers_widget.py b/src/dmonitor/registers_widget.py index 1d2f83b7..f49b8e08 100755 --- a/src/dmonitor/registers_widget.py +++ b/src/dmonitor/registers_widget.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -# Copyright (c) 2023-2024 Wavelet Lab +# Copyright (c) 2023-2026 Wavelet Lab # SPDX-License-Identifier: MIT import os @@ -18,13 +18,17 @@ from conn_pipe import ConnDebugPipe -from PyQt5.QtWidgets import * -from PyQt5.QtGui import QPalette, QColor, QIcon -from PyQt5.QtCore import Qt, QTimer +try: + from PySide6.QtWidgets import * + from PySide6.QtGui import QPalette, QColor, QIcon + from PySide6.QtCore import Qt, QTimer +except ImportError: + from PyQt5.QtWidgets import * + from PyQt5.QtGui import QPalette, QColor, QIcon + from PyQt5.QtCore import Qt, QTimer import pyqtgraph as pg - ABS_PATH = os.path.dirname(os.path.abspath(__file__)) SCHEMA_PATH = "/usr/share/usdr/schema/" @@ -95,8 +99,6 @@ def update(self): pass - - class GeneralProperties(QScrollArea): def __init__(self, params, pipe): super(GeneralProperties, self).__init__() @@ -227,7 +229,7 @@ def __init__(self, paths): def find_hw_parser(self, pf): for p in self.parsers: - pattern = '^\s*' + str(p.path).replace('*', '([a-zA-Z0-9]+)') + '\s*$' + pattern = r'^\s*' + str(p.path).replace(r'*', r'([a-zA-Z0-9]+)') + r'\s*$' res = re.match(pattern, pf) if res is not None: return p diff --git a/src/dmonitor/usdr_registers b/src/dmonitor/usdr_registers index 26eab4c2..eb51d7f7 100755 --- a/src/dmonitor/usdr_registers +++ b/src/dmonitor/usdr_registers @@ -1,3 +1,3 @@ #!/usr/bin/env sh -exec /usr/bin/env python3 /usr/libexec/usdr_dmonitor/registers_widget.py +exec /usr/bin/env python3 /usr/libexec/usdr_dmonitor/registers_widget.py "$@" diff --git a/src/dmonitor/yamls/ext_fe_ch4_400_7200_e.yaml b/src/dmonitor/yamls/ext_fe_ch4_400_7200_e.yaml new file mode 120000 index 00000000..8e597490 --- /dev/null +++ b/src/dmonitor/yamls/ext_fe_ch4_400_7200_e.yaml @@ -0,0 +1 @@ +../../lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_e.yaml \ No newline at end of file diff --git a/src/dmonitor/yamls/ext_fe_ch4_400_7200_usr.yaml b/src/dmonitor/yamls/ext_fe_ch4_400_7200_usr.yaml new file mode 120000 index 00000000..107176ae --- /dev/null +++ b/src/dmonitor/yamls/ext_fe_ch4_400_7200_usr.yaml @@ -0,0 +1 @@ +../../lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_usr.yaml \ No newline at end of file diff --git a/src/hwparser/gen_h.py b/src/hwparser/gen_h.py index 665caf09..d2f88213 100755 --- a/src/hwparser/gen_h.py +++ b/src/hwparser/gen_h.py @@ -17,7 +17,7 @@ def __init__(self, parser: reg_parser.ParserTop, filename: str) -> None: self.o_name = str(os.path.splitext(os.path.basename(filename))[0]) self.l_name = self.o_name.lower() self.h_name = self.o_name.upper() - + self.parser = parser self.addr_width = parser.addr_width self.data_width = parser.data_width @@ -26,7 +26,7 @@ def __init__(self, parser: reg_parser.ParserTop, filename: str) -> None: self.page_prefix = parser.page_prefix self.reg_prefix = "%s_" % parser.reg_prefix.upper() if len(parser.reg_prefix) > 0 else '' - self.field_prefix_ar = [ a.lower() for a in parser.field_prefix_ar ] + self.field_prefix_ar = [a.lower() for a in parser.field_prefix_ar] # Flat all pages self.regs = {} @@ -36,8 +36,8 @@ def __init__(self, parser: reg_parser.ParserTop, filename: str) -> None: name = "%s_%s" % (pname, r.name) if self.page_prefix else r.name if name in self.regs.keys(): - raise(Exception("Rigester `%s` is already in flat map! Rename it" % name)) - + raise (Exception("Register `%s` is already in flat map! Rename it" % name)) + # TODO: parse ucnt if r.ucnt == 1: self.regs[name] = r.addr @@ -48,14 +48,12 @@ def __init__(self, parser: reg_parser.ParserTop, filename: str) -> None: for k in range(r.ucnt): self.regs[name + "_BY%d" % (r.ucnt - k - 1)] = r.addr_l + k - def regName(self, reg: reg_parser.ParserRegs) -> str: if self.page_prefix: return "%s_%s" % (reg.page.name.upper(), reg.name) return reg.name - def fieldName(self, field: reg_parser.ParserFields) -> str: pfx = [] for i in self.field_prefix_ar: @@ -66,7 +64,7 @@ def fieldName(self, field: reg_parser.ParserFields) -> str: elif i == "regaddr": pfx.append("%02x" % field.reg.addr_l) else: - raise(Exception("Unknown prefix type '%s'" % i)) + raise (Exception("Unknown prefix type '%s'" % i)) if len(pfx) > 0: pfx.append(field.name) @@ -74,16 +72,22 @@ def fieldName(self, field: reg_parser.ParserFields) -> str: return field.name - def normalize(self, name: str) -> str: return (name.replace('-', '_') - .replace('.', '_') - .replace(',', '_') - .replace(' ', '_') - .replace('(', '') - .replace('|', 'OR') - .replace(')', '') - .replace('/', 'DIV')) + .replace('<=', 'LE') + .replace('>=', 'GE') + .replace('>', 'GT') + .replace('<', 'LT') + .replace('=', 'EQ') + .replace('+', 'PL') + .replace("'", 'MARK') + .replace('.', '_') + .replace(',', '_') + .replace(' ', '_') + .replace('(', '') + .replace('|', 'OR') + .replace(')', '') + .replace('/', 'DIV')) def ser_ch_fenum(self, reg: reg_parser.ParserRegs, name: str) -> str: vt = False @@ -96,7 +100,7 @@ def ser_ch_fenum(self, reg: reg_parser.ParserRegs, name: str) -> str: str += "};" if vt and len(reg.fields) == 1: return "" - + return str def generate_setter_expression(self, f, custom_name) -> str: @@ -165,13 +169,13 @@ def ser_ch_enum(self, name: str, en_dict: dict, prefix: str = "") -> str: str = "enum %s_t {\n" % name for i, v in en_dict.items(): str += "%s%s%s = 0x%x,\n" % (GenH.TAB, prefix, i.replace('-', '_') - .replace('.', '_') - .replace(',', '_') - .replace(' ', '_') - .replace('(', '') - .replace('|', 'OR') - .replace(')', '') - .replace('/', 'DIV'), v) + .replace('.', '_') + .replace(',', '_') + .replace(' ', '_') + .replace('(', '') + .replace('|', 'OR') + .replace(')', '') + .replace('/', 'DIV'), v) str += "};" return str @@ -181,11 +185,19 @@ def write_ch(self, filename): print(all_regs) # Make register define + if (self.parser.wr_mask is not None) and (self.parser.rd_mask is not None): + raise (Exception("You should specify rd_mask OR wr_mask, but not both!")) + def_macro = "MAKE_%s_REG_WR" % self.h_name def_wr_msk = "0x%x | " % self.parser.wr_mask if self.parser.wr_mask is not None else "" def_wr = "#define %s(a, v) (%s((a) << %d) | ((v) & 0x%x))" % (def_macro, def_wr_msk, self.data_width, (1 << self.data_width) - 1) print(def_wr) + def_macro_rd = "MAKE_%s_REG_RD" % self.h_name + def_rd_msk = "0x%x | " % self.parser.rd_mask if self.parser.rd_mask is not None else "" + def_rd = "#define %s(a) (%s((a) << %d))" % (def_macro_rd, def_rd_msk, self.data_width) + print(def_rd) + # Predefined universal enums for e in self.enums: em = e.replace('-', '_') @@ -213,10 +225,12 @@ def write_ch(self, filename): print(self.ser_cf_fmacro(r)) if r.ucnt == 1: - defc = "#define MAKE_%s_%s(%s)" % (self.h_name, name, reduce(lambda x, y: "%s, %s" % (x, y), [x.name.lower() for x in r.fields])) - #defc += " ((%s << %d) |" % (name, self.data_width) + defc = "#define MAKE_%s_%s(%s)" % ( + self.h_name, name, reduce(lambda x, y: "%s, %s" % (x, y), [x.name.lower() for x in r.fields])) + # defc += " ((%s << %d) |" % (name, self.data_width) defc += " %s(%s," % (def_macro, name) - defc += reduce(lambda x, y: "%s | %s" % (x, y), [" \\\n%s%s" % (self.TAB, self.generate_setter_expression(x, x.name.lower())) for x in r.fields ]) + defc += reduce(lambda x, y: "%s | %s" % (x, y), + [" \\\n%s%s" % (self.TAB, self.generate_setter_expression(x, x.name.lower())) for x in r.fields]) defc += ")" print(defc) else: @@ -228,8 +242,10 @@ def write_ch(self, filename): value_msk = reduce(lambda x, y: x | y, [x.mask for x in r.fields]) value_off = reduce(lambda x, y: min(x, y), [x.bits_l for x in r.fields]) if len(r.fields) > 1: - defc = "#define MAKE_%s_%s_LONG(%s) (" % (self.h_name, name, reduce(lambda x, y: "%s, %s" % (x, y), [x.name.lower() for x in r.fields])) - defc += reduce(lambda x, y: "%s | %s" % (x, y), [" \\\n%s%s" % (self.TAB, self.generate_setter_expression(x, x.name.lower())) for x in r.fields ]) + defc = "#define MAKE_%s_%s_LONG(%s) (" % ( + self.h_name, name, reduce(lambda x, y: "%s, %s" % (x, y), [x.name.lower() for x in r.fields])) + defc += reduce(lambda x, y: "%s | %s" % (x, y), + [" \\\n%s%s" % (self.TAB, self.generate_setter_expression(x, x.name.lower())) for x in r.fields]) defc += ")" print(defc) @@ -247,9 +263,9 @@ def write_ch(self, filename): else: # defc += " ((%s_BY%d << %d) | (((value) << %d) & 0x%x))" % (name, u, self.data_width, -by_off, by_msk) defc += " (((value) << %d) & 0x%x))" % (-by_off, by_msk) - print(defc) + print(defc) - # if 'c-cache' in self.parser.raw_yaml: + # if 'c-cache' in self.parser.raw_yaml: # cc = self.parser.raw_yaml['c-cache'] # if 'regs' in cc: # print("\n\n/* Cached operations */") @@ -284,10 +300,10 @@ def parse_c_cache(self, cregs): fn += "p->%s = (p->%s & ~%s_MSK) | ((%s << %s_OFF) & %s_MSK); }" % (regn, regn, FLDN, fname, FLDN, FLDN) print(fn) - def write_vh(self, filename): pass + if __name__ == '__main__': parser = argparse.ArgumentParser(description='Debug UI options') parser.add_argument('--yaml', dest='yaml', type=str, diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 2c3ab942..00b0bc5f 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,13 +1,13 @@ -# Copyright (c) 2023-2024 Wavelet Lab +# Copyright (c) 2023-2026 Wavelet Lab # SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.8) project(usdr_lib C) -# Auto generating register descriptors +# Auto generating registor descriptors list(APPEND USDR_LIBRARY_FILES "") list(APPEND USDR_INCLUDE_FILES "") -list(APPEND USDR_LINK_FILES pthread rt) +list(APPEND USDR_LINK_FILES pthread) list(APPEND USDR_DEPEND_TARGETS "") include_directories(port) @@ -27,16 +27,40 @@ message("INC - ${USDR_INCLUDE_FILES}") message("LNK - ${USDR_LINK_FILES}") message("DEP - ${USDR_DEPEND_TARGETS}") -include_directories(${USDR_INCLUDE_FILES}) -include_directories(${USDR_INCLUDE_DIRS}) +add_library(usdr SHARED ${USDR_LIBRARY_FILES}) +if (APPLE) + set(USDR_PLATFORM_LIBS dl) +elseif(NOT WIN32) + set(USDR_PLATFORM_LIBS dl rt) +else() + set(USDR_PLATFORM_LIBS kernel32 ws2_32) +endif() -add_library(usdr ${USDR_LIBRARY_FILES}) -target_link_libraries(usdr dl pthread rt ${USDR_LINK_FILES} usdr-dsp) +message(NOTICE "Usdr link:${USDR_LINK_FILES}") + +target_link_libraries(usdr pthread ${USDR_PLATFORM_LIBS} ${USDR_LINK_FILES} usdr-dsp) add_dependencies(usdr ${USDR_DEPEND_TARGETS}) target_compile_options(usdr PRIVATE "-Wall" "-Werror=implicit-function-declaration") +# Propagate include directories to consumers via the target interface +target_include_directories(usdr PUBLIC + $ + $ +) + +# Ensure exported INTERFACE include directories are exactly the intended ones +set_target_properties(usdr PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "$;$" +) + set_target_properties(usdr PROPERTIES SOVERSION ${USDR_ABI_VERSION}) set_target_properties(usdr PROPERTIES VERSION ${USDR_LIBVER}) -install(TARGETS usdr LIBRARY) +install(TARGETS usdr + EXPORT usdrTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) diff --git a/src/lib/cal/cal_lo_iqimb.c b/src/lib/cal/cal_lo_iqimb.c index e08f5f03..7a6ffbc1 100644 --- a/src/lib/cal/cal_lo_iqimb.c +++ b/src/lib/cal/cal_lo_iqimb.c @@ -125,7 +125,7 @@ int calibrate_txlo(struct calibrate_ops* ops) if (res) return res; - res = ops->set_nco_offset(ops->param, ops->channel, ((-ops->rxtxlo_frac) << 1)); + res = ops->set_nco_offset(ops->param, ops->channel, -freqoff); if (res) return res; @@ -169,7 +169,7 @@ int _calibrate_iqimb_generic(struct calibrate_ops* ops, int32_t freqoffset, int a, giq, b; struct opt_iteration2d o[3]; - res = ops->set_nco_offset(ops->param, ops->channel, (rxreoff << 1)); + res = ops->set_nco_offset(ops->param, ops->channel, rxreoff); if (res) return res; @@ -177,7 +177,7 @@ int _calibrate_iqimb_generic(struct calibrate_ops* ops, int32_t freqoffset, if (res) return res; - res = ops->set_nco_offset(ops->param, ops->channel, (rximoff << 1)); + res = ops->set_nco_offset(ops->param, ops->channel, rximoff); if (res) return res; @@ -218,12 +218,7 @@ int _calibrate_iqimb_generic(struct calibrate_ops* ops, int32_t freqoffset, int calibrate_rxiqimb(struct calibrate_ops* ops) { int res; -#if 0 - int pwr_r; - int pwr_i; - int a, giq, b; - struct opt_iteration2d o[3]; -#endif + // Set RX to be TXLO - sampl int32_t freqoff = (((int64_t)ops->rxsamplerate * ops->rxiqimb_frac) >> 31); @@ -236,54 +231,6 @@ int calibrate_rxiqimb(struct calibrate_ops* ops) return res; return _calibrate_iqimb_generic(ops, 0, ops->rxiqimb_frac, -ops->rxiqimb_frac, _evaluate_rxaiq); -#if 0 - res = ops->set_corr_param(ops->param, ops->channel, CORR_DIR_TX | CORR_OP_SET_FREQ, - ops->rxfrequency + freqoff); - if (res) - return res; - - res = ops->set_nco_offset(ops->param, ops->channel, ((-ops->rxiqimb_frac) << 1)); - if (res) - return res; - - res = _calibrate_txpwr(ops, &pwr_r); - - res = ops->set_nco_offset(ops->param, ops->channel, ((ops->rxiqimb_frac) << 1)); - if (res) - return res; - - res = ops->do_meas_nco_avg(ops->param, ops->channel, 0, &pwr_i); - if (res) - return res; - - USDR_LOG("UDEV", USDR_LOG_WARNING, "CAL_IQIMB: Imbalance %d pwr (%d)\n", pwr_i, pwr_r - pwr_i); - // Probe AI and AQ, choice what path to go - - o[0].limit[0] = ops->rximb_ang_corr; - o[0].limit[1] = ops->rximb_iq_corr; - o[0].func = _evaluate_rxaiq; - o[0].sf = &find_golden_min; - o[0].exparam = 0; - o[1].limit[0] = ops->rximb_ang_corr; - o[1].limit[1] = ops->rximb_iq_corr; - o[1].func = _evaluate_rxaiq; - o[1].sf = &find_golden_min; - o[1].exparam = 0; - o[2].limit[0].max = 8; - o[2].limit[0].min = -8; - o[2].limit[1].max = 8; - o[2].limit[1].min = -8; - o[2].func = _evaluate_rxaiq; - o[2].sf = &find_golden_min; - o[2].exparam = 0; - - res = find_best_2d(&o[0], SIZEOF_ARRAY(o), ops, ops->defstop, &a, &giq, &b); - if (res) - return res; - - USDR_LOG("UDEV", USDR_LOG_WARNING, "CAL_IQIMB: Imbalance %d pwr (%d) improvement %d\n", b, pwr_r - b, pwr_i - b); - return 0; -#endif } diff --git a/src/lib/cal/opt_func.c b/src/lib/cal/opt_func.c index bdfeaf9b..dddddbce 100644 --- a/src/lib/cal/opt_func.c +++ b/src/lib/cal/opt_func.c @@ -4,6 +4,7 @@ #include "opt_func.h" #include #include +#include int find_golden_min(int start, int stop, void* param, evaluate_fn_t fn, int* px, int* pv, int exparam) { @@ -170,3 +171,45 @@ int find_best_2d(struct opt_iteration2d *ops, unsigned max_count, void* param, i return 0; } +// Function to implement Stein's Algorithm +// Borrowed from: https://www.geeksforgeeks.org/steins-algorithm-for-finding-gcd/ (C) +// +uint64_t find_gcd(uint64_t a, uint64_t b) +{ + if (a == b) + return a; + + // GCD(0, b) == b; GCD(a, 0) == a, + // GCD(0, 0) == 0 + if (a == 0) + return b; + if (b == 0) + return a; + + // look for factors of 2 + if (~a & 1) // a is even + { + if (b & 1) // b is odd + return find_gcd(a >> 1, b); + else // both a and b are even + return find_gcd(a >> 1, b >> 1) << 1; + } + + if (~b & 1) // a is odd, b is even + return find_gcd(a, b >> 1); + + // reduce larger number + if (a > b) + return find_gcd((a - b) >> 1, b); + + return find_gcd((b - a) >> 1, a); +} + +void binary_print_u32(uint32_t x, char* s, bool reverse) +{ + unsigned len = 0; + for(unsigned i = 0; i < sizeof(x) * 8; ++i) + { + len += sprintf(s + len, "%1u", reverse ? (x >> i) & 0x1 : (int32_t)(x << i) < 0); + } +} diff --git a/src/lib/cal/opt_func.h b/src/lib/cal/opt_func.h index 7030271d..5c37a9ab 100644 --- a/src/lib/cal/opt_func.h +++ b/src/lib/cal/opt_func.h @@ -8,6 +8,9 @@ #ifndef OPT_FUNC_H #define OPT_FUNC_H +#include "stdint.h" +#include "stdbool.h" +#include "time.h" #define MAX(a, b) \ ({ __typeof__ (a) _a = (a); \ @@ -55,5 +58,14 @@ struct opt_iteration2d int find_best_2d(struct opt_iteration2d *ops, unsigned max_count, void* param, int stop_when, int *px, int *py, int *pfxy); +uint64_t find_gcd(uint64_t a, uint64_t b); +void binary_print_u32(uint32_t x, char* s, bool reverse); + +static inline uint64_t clock_get_time() +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec/1000LL; +} #endif diff --git a/src/lib/common/clock_gen.c b/src/lib/common/clock_gen.c index a425ecc3..28ef0def 100644 --- a/src/lib/common/clock_gen.c +++ b/src/lib/common/clock_gen.c @@ -147,7 +147,7 @@ int find_best_vco(const vco_range_t* pvcos, unsigned vco_count, } } - // Freqency can't be delivered + // Frequency can't be delivered return -1; } diff --git a/src/lib/common/ring_buffer.c b/src/lib/common/ring_buffer.c index a23c00f1..8936cbd9 100644 --- a/src/lib/common/ring_buffer.c +++ b/src/lib/common/ring_buffer.c @@ -15,7 +15,7 @@ struct ring_buffer* ring_buffer_create(unsigned items, unsigned isize) if (items == 0 || isize == 0) return NULL; - int res = posix_memalign((void**)&obj, CACHE_SIZE, sz); + int res = usdr_alignalloc((void**)&obj, CACHE_SIZE, sz); if (res) return NULL; @@ -24,27 +24,27 @@ struct ring_buffer* ring_buffer_create(unsigned items, unsigned isize) obj->pidx = 0; obj->cidx = 0; - res = sem_init(&obj->producer, 0, items); + res = usdr_sem_init(&obj->producer, 0, items); if (res) goto failed_s0; - res = sem_init(&obj->consumer, 0, 0); + res = usdr_sem_init(&obj->consumer, 0, 0); if (res) goto failed_s1; return obj; failed_s1: - sem_destroy(&obj->producer); + usdr_sem_destroy(&obj->producer); failed_s0: - free(obj); + usdr_alignfree(obj); return NULL; } void ring_buffer_destroy(struct ring_buffer* rb) { - sem_destroy(&rb->producer); - sem_destroy(&rb->consumer); - free(rb); + usdr_sem_destroy(&rb->producer); + usdr_sem_destroy(&rb->consumer); + usdr_alignfree(rb); } char* ring_buffer_at(struct ring_buffer* rb, unsigned idx) @@ -53,13 +53,13 @@ char* ring_buffer_at(struct ring_buffer* rb, unsigned idx) return &rb->data[rb->isize * int_idx]; } -static int ring_buffer_stdwait(sem_t* sem, int usecs) +static int ring_buffer_stdwait(usdr_sem_t* sem, int usecs) { int res; if (usecs == -1) { - res = sem_wait(sem); + res = usdr_sem_wait(sem); } else if (usecs == 0) { - res = sem_trywait(sem); + res = usdr_sem_trywait(sem); } else { struct timespec t; res = clock_gettime(CLOCK_REALTIME, &t); @@ -73,7 +73,7 @@ static int ring_buffer_stdwait(sem_t* sem, int usecs) t.tv_sec++; } - res = sem_timedwait(sem, &t); + res = usdr_sem_timedwait(sem, &t); } return res; } @@ -92,7 +92,7 @@ unsigned ring_buffer_pwait(struct ring_buffer* rb, int usecs) void ring_buffer_ppost(struct ring_buffer* rb) { - int res = sem_post(&rb->consumer); + __attribute__((unused)) int res = usdr_sem_post(&rb->consumer); assert(res == 0); } @@ -110,7 +110,7 @@ unsigned ring_buffer_cwait(struct ring_buffer* rb, int usecs) void ring_buffer_cpost(struct ring_buffer* rb) { - int res = sem_post(&rb->producer); + __attribute__((unused)) int res = usdr_sem_post(&rb->producer); assert(res == 0); } diff --git a/src/lib/common/ring_buffer.h b/src/lib/common/ring_buffer.h index 233e706c..a64b68ea 100644 --- a/src/lib/common/ring_buffer.h +++ b/src/lib/common/ring_buffer.h @@ -4,10 +4,7 @@ #ifndef RING_BUFFER_H #define RING_BUFFER_H -#include -#include - -#define CACHE_SIZE 64 +#include // Fixed size circular array struct ring_buffer @@ -18,10 +15,17 @@ struct ring_buffer unsigned pidx; unsigned cidx; +#ifdef _WIN32 + usdr_sem_t producer; + usdr_sem_t consumer; + + char reserved[CACHE_SIZE - 4*sizeof(unsigned) - 2*sizeof(usdr_sem_t)]; +#else char reserved[CACHE_SIZE - 4*sizeof(unsigned)]; - sem_t producer; - sem_t consumer; + usdr_sem_t producer; + usdr_sem_t consumer; +#endif char data[0]; }; diff --git a/src/lib/common/ring_circbuf.c b/src/lib/common/ring_circbuf.c index d0b716bf..1cbaffbe 100644 --- a/src/lib/common/ring_circbuf.c +++ b/src/lib/common/ring_circbuf.c @@ -1,7 +1,6 @@ #include "ring_circbuf.h" #include #include -#include #include // Can be MT-safe if rpos and wpos updated atomic @@ -10,7 +9,7 @@ ring_circbuf_t* ring_circbuf_create(size_t max_size) { size_t sz = sizeof(ring_circbuf_t) + max_size; ring_circbuf_t* b; - int res = posix_memalign((void**)&b, 16, sz); + int res = usdr_alignalloc((void**)&b, CACHE_SIZE, sz); if (res) { return NULL; } @@ -23,7 +22,7 @@ ring_circbuf_t* ring_circbuf_create(size_t max_size) void ring_circbuf_destroy(ring_circbuf_t* rb) { - free(rb); + usdr_alignfree(rb); } diff --git a/src/lib/common/ring_circbuf.h b/src/lib/common/ring_circbuf.h index 8517abac..25c85b54 100644 --- a/src/lib/common/ring_circbuf.h +++ b/src/lib/common/ring_circbuf.h @@ -4,8 +4,7 @@ #ifndef RING_CIRCBUF_H #define RING_CIRCBUF_H -#include -#include +#include // Simple circbuffer struct ring_circbuf { diff --git a/src/lib/device/CMakeLists.txt b/src/lib/device/CMakeLists.txt index 4ae4b9a4..4b452188 100644 --- a/src/lib/device/CMakeLists.txt +++ b/src/lib/device/CMakeLists.txt @@ -20,6 +20,8 @@ add_subdirectory(ext_pciefe) add_subdirectory(ext_supersync) add_subdirectory(ext_simplesync) add_subdirectory(ext_fe_100_5000) +add_subdirectory(ext_fe_ch4_400_7200) +add_subdirectory(ext_xmass) add_subdirectory(u3_limesdr) diff --git a/src/lib/device/dev_param.h b/src/lib/device/dev_param.h index 37b0d4f8..2e8c0c46 100644 --- a/src/lib/device/dev_param.h +++ b/src/lib/device/dev_param.h @@ -46,7 +46,7 @@ static inline void opt_u64_set_val(opt_u64_t* p, unsigned val) struct freq_auto_band_map { - unsigned stop_freq; + uint64_t stop_freq; uint8_t band; uint8_t sw : 4; uint8_t swlb : 4; diff --git a/src/lib/device/device.c b/src/lib/device/device.c index bd74cfca..78bcd1d5 100644 --- a/src/lib/device/device.c +++ b/src/lib/device/device.c @@ -4,12 +4,12 @@ #include "device.h" #include #include +#include #include #include #include #include "device_vfs.h" #include "device_names.h" -#include static int _usdr_device_vfs_get_by_path(device_t *base, const char* fullpath, pusdr_vfs_obj_t *obj); int usdr_device_base_create(pdevice_t dev, lldev_t lldev) @@ -133,7 +133,7 @@ int usdr_device_vfs_filter(pdevice_t dev, const char* filter, unsigned max_objec unsigned i, cnt; for (i = 0, cnt = 0; cnt < max_objects && i < root->eparam[0]; i++) { - if (fnmatch(filter, nodes[i].full_path, FNM_NOESCAPE) == 0) { + if (fnmatch(filter, nodes[i].full_path, FNM_NOESCAPE) == 0 && !(nodes[i].flags & VFS_FLAG_HIDDEN)) { objs[cnt].fullpath = nodes[i].full_path; cnt++; } @@ -144,18 +144,11 @@ int usdr_device_vfs_filter(pdevice_t dev, const char* filter, unsigned max_objec int _usdr_device_vfs_get_by_path(device_t *base, const char* filter, pusdr_vfs_obj_t *obj) { - vfs_object_t *root = &base->rootfs; - vfs_object_t *nodes = (vfs_object_t *)root->data.obj; - - for (unsigned i = 0; i < base->rootfs.eparam[0]; i++) { - if (fnmatch(filter, nodes[i].full_path, FNM_NOESCAPE) == 0) { - *obj = &nodes[i]; - return 0; - } + int res = vfs_get_by_path(&base->rootfs, filter, obj); + if (res) { + USDR_LOG("UDEV", USDR_LOG_NOTE, "vfs '%s' not found!\n", filter); } - - USDR_LOG("UDEV", USDR_LOG_NOTE, "vfs '%s' not found!\n", filter); - return -ENOENT; + return res; } @@ -214,6 +207,26 @@ int usdr_vfs_obj_param_init_array_param(pdevice_t dev, return 0; } +int usdr_vfs_obj_link_init_array_param(pdevice_t dev, + void *param, + const usdr_dev_link_t* links, + unsigned count) +{ + unsigned i; + int res; + + for (i = 0; i < count; i++) { + res = vfs_add_obj_link(&dev->rootfs, + links[i].fullpath, + param == NULL ? dev : param, + links[i].linkpath); + if (res) + return res; + } + + return 0; +} + static int _usdr_device_vfs_concat(pdevice_t dev, const char* path, const char* entity, unsigned max, char* out) { snprintf(out, max, "%s/%s", path, entity); diff --git a/src/lib/device/device.h b/src/lib/device/device.h index 4b1562cb..1471179a 100644 --- a/src/lib/device/device.h +++ b/src/lib/device/device.h @@ -150,5 +150,21 @@ static inline int usdr_vfs_obj_param_init_array(pdevice_t dev, return usdr_vfs_obj_param_init_array_param(dev, NULL, params, count); } +struct usdr_dev_link { + const char* fullpath; + const char* linkpath; +}; +typedef struct usdr_dev_link usdr_dev_link_t; + +int usdr_vfs_obj_link_init_array_param(pdevice_t dev, + void *param, + const usdr_dev_link_t* links, + unsigned count); + +static inline int usdr_vfs_obj_link_init_array(pdevice_t dev, + const usdr_dev_link_t* links, + unsigned count) { + return usdr_vfs_obj_link_init_array_param(dev, NULL, links, count); +} #endif diff --git a/src/lib/device/device_cores.h b/src/lib/device/device_cores.h index 496bdada..1a65c7bd 100644 --- a/src/lib/device/device_cores.h +++ b/src/lib/device/device_cores.h @@ -32,7 +32,7 @@ enum usdr_core_subtype { // internal MCU glue logic USDR_CS_MCU = 0x7, - // Syncronization core (1PPS, Sysref, etc.) + // Synchronization core (1PPS, Sysref, etc.) USDR_CS_SYNC = 0x8, // Int bucket @@ -109,7 +109,7 @@ enum usdr_gpi_cores { #define USDR_CORE_GET_ID(c) ((c) >> 8) -// Prdefined cores +// Predefined cores #define I2C_CORE_AUTO_LUTUPD USDR_MAKE_COREID(USDR_CS_BUS, USDR_BS_DI2C_SIMPLE) #define SPI_CORE_32W USDR_MAKE_COREID(USDR_CS_BUS, USDR_BS_SPI_SIMPLE) #define SPI_CORE_CFGW_CS8 USDR_MAKE_COREID(USDR_CS_BUS, USDR_BS_SPI_CFG_CS8) diff --git a/src/lib/device/device_fe.c b/src/lib/device/device_fe.c index d44d521d..aab41d5e 100644 --- a/src/lib/device/device_fe.c +++ b/src/lib/device/device_fe.c @@ -9,6 +9,8 @@ #include "ext_supersync/ext_supersync.h" #include "ext_simplesync/ext_simplesync.h" #include "ext_fe_100_5000/ext_fe_100_5000.h" +#include "ext_xmass/ext_xmass.h" +#include "ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.h" #include #include @@ -24,13 +26,24 @@ static int _debug_ext_fe_100_5000_cmd_set(pdevice_t ud, pusdr_vfs_obj_t obj, uin static int _debug_ext_fe_100_5000_cmd_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); -static int _debug_lmk05318_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -static int _debug_lmk05318_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static int _debug_simplesync_lmk05318_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int _debug_simplesync_lmk05318_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static int _debug_xmass_lmk05318_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int _debug_xmass_lmk05318_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); + static int _debug_lmk05318_calfreq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int _debug_lmk5c33216_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int _debug_lmk5c33216_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static int _debug_xmass_ctrl_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int _debug_xmass_ctrl_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); + +static int _debug_xmass_calfreq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int _debug_xmass_calfreq_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); + +static int _debug_xmass_calpath_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); + static int _debug_typefe_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); static int _debug_ll_mdev_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -41,47 +54,53 @@ const usdr_dev_param_func_t s_fe_params[] = { { "/ll/mdev", { _debug_ll_mdev_set, NULL}}, }; -static -const usdr_dev_param_func_t s_fe_pcie_params[] = { +static const usdr_dev_param_func_t s_fe_pcie_params[] = { { "/debug/hw/pciefe/0/reg", { _debug_pciefe_reg_set, _debug_pciefe_reg_get }}, { "/debug/hw/pciefe_cmd/0/reg", { _debug_pciefe_cmd_set, _debug_pciefe_cmd_get }}, }; -static -const usdr_dev_param_func_t s_lmk05318_params[] = { - { "/debug/hw/lmk05318/0/reg", { _debug_lmk05318_reg_set, _debug_lmk05318_reg_get }}, +static const usdr_dev_param_func_t s_simplesync_params[] = { + { "/debug/hw/lmk05318/0/reg", { _debug_simplesync_lmk05318_reg_set, _debug_simplesync_lmk05318_reg_get }}, { "/dm/sync/cal/freq", { _debug_lmk05318_calfreq_set, NULL }}, }; -static -const usdr_dev_param_func_t s_lmk5c33216_params[] = { +static const usdr_dev_param_func_t s_lmk5c33216_params[] = { { "/debug/hw/lmk5c33216/0/reg", { _debug_lmk5c33216_reg_set, _debug_lmk5c33216_reg_get }}, }; - -static -const usdr_dev_param_func_t s_ext_fe_100_5000_params[] = { +static const usdr_dev_param_func_t s_ext_fe_100_5000_params[] = { { "/debug/hw/fe_100_5000_cmd/0/reg", { _debug_ext_fe_100_5000_cmd_set, _debug_ext_fe_100_5000_cmd_get }}, }; +static const usdr_dev_param_func_t s_xmass_params[] = { + { "/debug/hw/lmk05318/0/reg", { _debug_xmass_lmk05318_reg_set, _debug_xmass_lmk05318_reg_get }}, + { "/debug/hw/xmass_ctrl/0/reg", { _debug_xmass_ctrl_reg_set, _debug_xmass_ctrl_reg_get }}, + { "/dm/sync/cal/freq", { _debug_xmass_calfreq_set, _debug_xmass_calfreq_get }}, + { "/dm/sdr/0/sync/cal/freq", { _debug_xmass_calfreq_set, _debug_xmass_calfreq_get }}, + { "/dm/sdr/0/sync/cal/path", { _debug_xmass_calpath_set, NULL }}, +}; + enum fe_type { FET_PCIE_DEVBOARD, + FET_PCIE_XMASS, FET_PCIE_SUPER_SYNC, FET_PCIE_SIMPLE_SYNC, FET_PICE_BREAKOUT, FET_PCIE_FE1005000, - + FET_PCIE_FE4CH4007200, FET_COUNT }; typedef enum fe_type fe_type_t; static const char* s_fe_names[] = { "pciefe", + "xmass", "supersync", "simplesync", "exm2pe", "fe1005000", + "fe4ch4007200", }; @@ -95,6 +114,8 @@ struct dev_fe { board_ext_simplesync_t simplesync; board_ext_supersync_t supersync; ext_fe_100_5000_t fe_100_5000; + board_xmass_t xmass; + ext_fe_ch4_400_7200_t fe_4ch_400_7200; } fe; uint32_t debug_pciefe_last; @@ -102,6 +123,7 @@ struct dev_fe { uint32_t debug_ext_fe_100_5000_cmd_last; uint32_t debug_lmk05318_last; uint32_t debug_lmk5c33216_last; + uint32_t debug_xmass_ctrl_last; }; typedef struct dev_fe dev_fe_t; @@ -135,7 +157,7 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi lldev_t dev = base->dev; unsigned i; int res; - dev_fe_t dfe; + dev_fe_t *dfe = (dev_fe_t*)malloc(sizeof(dev_fe_t)); unsigned vfidx = 0; const char* hint = fename; @@ -151,7 +173,7 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi const unsigned uartbase = 54; const unsigned spiext_cfg = 58; - memset(&dfe, 0, sizeof(dfe)); + memset(dfe, 0, sizeof(dev_fe_t)); usdr_core_info_t fe_gpio; usdr_core_info_t fe_uart; @@ -178,12 +200,14 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi } switch (i) { - case FET_PICE_BREAKOUT: res = board_exm2pe_init(dev, 0, gpiobase, uartbase, hint_strip, compat, def_i2c_loc, &dfe.fe.exm2pe); break; - case FET_PCIE_DEVBOARD: res = board_ext_pciefe_init(dev, 0, gpiobase, uartbase, hint_strip, compat, def_i2c_loc, &dfe.fe.devboard); break; - case FET_PCIE_SUPER_SYNC: res = board_ext_supersync_init(dev, 0, gpiobase, compat, def_i2c_loc, &dfe.fe.supersync); break; - case FET_PCIE_SIMPLE_SYNC: res = board_ext_simplesync_init(dev, 0, gpiobase, compat, def_i2c_loc, &dfe.fe.simplesync); break; - case FET_PCIE_FE1005000: res = ext_fe_100_5000_init(dev, 0, gpiobase, spiext_cfg, 4, hint_strip, compat, &dfe.fe.fe_100_5000); break; - default: return -EIO; + case FET_PICE_BREAKOUT: res = board_exm2pe_init(dev, 0, gpiobase, uartbase, hint_strip, compat, def_i2c_loc, &dfe->fe.exm2pe); break; + case FET_PCIE_DEVBOARD: res = board_ext_pciefe_init(dev, 0, gpiobase, uartbase, hint_strip, compat, def_i2c_loc, &dfe->fe.devboard); break; + case FET_PCIE_SUPER_SYNC: res = board_ext_supersync_init(dev, 0, gpiobase, compat, def_i2c_loc, &dfe->fe.supersync); break; + case FET_PCIE_SIMPLE_SYNC: res = board_ext_simplesync_init(dev, 0, gpiobase, compat, def_i2c_loc, &dfe->fe.simplesync); break; + case FET_PCIE_FE1005000: res = ext_fe_100_5000_init(dev, 0, gpiobase, spiext_cfg, 4, hint_strip, compat, &dfe->fe.fe_100_5000); break; + case FET_PCIE_XMASS: res = board_xmass_init(dev, 0, gpiobase, compat, def_i2c_loc, &dfe->fe.xmass); break; + case FET_PCIE_FE4CH4007200: res = ext_fe_ch4_400_7200_init(dev, 0, gpiobase, hint_strip, compat, &dfe->fe.fe_4ch_400_7200); break; + default: res = -EIO; goto failed; } if (res == 0) { @@ -191,65 +215,77 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi } else if (res != -ENODEV) { USDR_LOG("DEFE", USDR_LOG_ERROR, "Unable to initialize %s, error %d\n", s_fe_names[i], res); if (hint != NULL) - return res; + goto failed; } } if (i == FET_COUNT) { if (hint == NULL) { USDR_LOG("DEFE", USDR_LOG_NOTE, "No external FE was detected, provide fe=`frontend` for a strong hint\n"); - *out = NULL; - return 0; + res = 0; + goto failed; } USDR_LOG("DEFE", USDR_LOG_WARNING, "No external FE was detected with `%s` hint and %s filter\n", hint, compat); return -ENODEV; } - dev_fe_t* n = (dev_fe_t*)malloc(sizeof(dev_fe_t)); - *n = dfe; - n->type = (fe_type_t)i; + dfe->type = (fe_type_t)i; USDR_LOG("DEFE", USDR_LOG_WARNING, "Detected external FE: %s\n", s_fe_names[i]); res = usdr_vfs_obj_param_init_array_param(base, - (void*)n, + (void*)dfe, s_fe_params, SIZEOF_ARRAY(s_fe_params)); if (res) - return res; + goto failed; vfidx += SIZEOF_ARRAY(s_fe_params); - switch (n->type) { + switch (dfe->type) { case FET_PCIE_DEVBOARD: res = usdr_vfs_obj_param_init_array_param(base, - (void*)n, + (void*)dfe, s_fe_pcie_params, SIZEOF_ARRAY(s_fe_pcie_params)); break; case FET_PCIE_SIMPLE_SYNC: res = usdr_vfs_obj_param_init_array_param(base, - (void*)n, - s_lmk05318_params, - SIZEOF_ARRAY(s_lmk05318_params)); + (void*)dfe, + s_simplesync_params, + SIZEOF_ARRAY(s_simplesync_params)); break; case FET_PCIE_SUPER_SYNC: res = usdr_vfs_obj_param_init_array_param(base, - (void*)n, + (void*)dfe, s_lmk5c33216_params, SIZEOF_ARRAY(s_lmk5c33216_params)); break; case FET_PCIE_FE1005000: res = usdr_vfs_obj_param_init_array_param(base, - (void*)n, + (void*)dfe, s_ext_fe_100_5000_params, SIZEOF_ARRAY(s_ext_fe_100_5000_params)); break; + case FET_PCIE_XMASS: + res = usdr_vfs_obj_param_init_array_param(base, + (void*)dfe, + s_xmass_params, + SIZEOF_ARRAY(s_xmass_params)); + break; default: break; } - *out = n; + if (res) + goto failed; + + *out = dfe; + return res; + +failed: + *out = NULL; + free(dfe); return res; } @@ -371,18 +407,24 @@ int _debug_pciefe_cmd_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) return res; } - -int _debug_lmk05318_reg_get(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t* ovalue) +static int _debug_lmk05318_reg_get(dev_fe_t* o, uint64_t* ovalue) { - dev_fe_t* o = (dev_fe_t*)obj->object; *ovalue = o->debug_lmk05318_last; return 0; } +int _debug_simplesync_lmk05318_reg_get(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + return _debug_lmk05318_reg_get((dev_fe_t*)obj->object, ovalue); +} -int _debug_lmk05318_reg_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) +int _debug_xmass_lmk05318_reg_get(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + return _debug_lmk05318_reg_get((dev_fe_t*)obj->object, ovalue); +} + +int _debug_lmk05318_reg_set(dev_fe_t* o, lmk05318_state_t* lmk, uint64_t value) { - dev_fe_t* o = (dev_fe_t*)obj->object; int res; unsigned addr = (value >> 8) & 0x7fff; unsigned data = value & 0xff; @@ -390,14 +432,14 @@ int _debug_lmk05318_reg_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) o->debug_lmk05318_last = ~0u; - if (value & 0x800000) { - res = lmk05318_reg_wr(&o->fe.simplesync.lmk, addr, data); + if (!(value & 0x800000)) { + res = lmk05318_reg_wr(lmk, addr, data); USDR_LOG("XDEV", USDR_LOG_WARNING, "LMK05318 WR REG %04x => %04x\n", - (unsigned)addr, data); + (unsigned)addr, data); } else { d = 0xff; - res = lmk05318_reg_rd(&o->fe.simplesync.lmk, addr, &d); + res = lmk05318_reg_rd(lmk, addr, &d); o->debug_lmk05318_last = d; USDR_LOG("XDEV", USDR_LOG_WARNING, "LMK05318 RD REG %04x <= %04x\n", @@ -408,6 +450,72 @@ int _debug_lmk05318_reg_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) return res; } +int _debug_simplesync_lmk05318_reg_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) +{ + dev_fe_t* o = (dev_fe_t*)obj->object; + return _debug_lmk05318_reg_set(o, &o->fe.simplesync.lmk, value); +} + +int _debug_xmass_lmk05318_reg_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) +{ + dev_fe_t* o = (dev_fe_t*)obj->object; + return _debug_lmk05318_reg_set(o, &o->fe.xmass.lmk, value); +} + +int _debug_xmass_ctrl_reg_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) +{ + dev_fe_t* o = (dev_fe_t*)obj->object; + unsigned addr = (value >> 24) & 0x7f; + unsigned data = value & 0xffffff; + int res; + board_xmass_t* board = &o->fe.xmass; + uint32_t d; + o->debug_xmass_ctrl_last = ~0u; + + if (value & 0x80000000) { + res = board_xmass_ctrl_cmd_wr(board, addr, data); + + USDR_LOG("XDEV", USDR_LOG_WARNING, "XMASS_CTRL WR REG %04x => %04x\n", + (unsigned)addr, data); + } else { + d = 0xffffff; + res = board_xmass_ctrl_cmd_rd(board, addr, &d); + o->debug_xmass_ctrl_last = d; + + USDR_LOG("XDEV", USDR_LOG_WARNING, "XMASS_CTRL RD REG %04x <= %04x\n", + (unsigned)addr, + o->debug_xmass_ctrl_last); + } + + return res; +} + +int _debug_xmass_ctrl_reg_get(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + dev_fe_t* o = (dev_fe_t*)obj->object; + *ovalue = o->debug_xmass_ctrl_last; + return 0; +} + +int _debug_xmass_calfreq_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) +{ + dev_fe_t* o = (dev_fe_t*)obj->object; + return board_xmass_tune_cal_lo(&o->fe.xmass, value); +} + +int _debug_xmass_calpath_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) +{ + dev_fe_t* o = (dev_fe_t*)obj->object; + return board_xmass_sync_source(&o->fe.xmass, value); +} + +int _debug_xmass_calfreq_get(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + dev_fe_t* o = (dev_fe_t*)obj->object; + *ovalue = o->fe.xmass.calfreq; + return 0; +} + int _debug_lmk05318_calfreq_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) { dev_fe_t* o = (dev_fe_t*)obj->object; diff --git a/src/lib/device/device_vfs.c b/src/lib/device/device_vfs.c index 7bb900b9..a1d4c294 100644 --- a/src/lib/device/device_vfs.c +++ b/src/lib/device/device_vfs.c @@ -2,11 +2,12 @@ // SPDX-License-Identifier: MIT #include "device_vfs.h" +#include #include #include #include #include -#include +#include #define STD_FOLDER_QTY 16 @@ -23,7 +24,7 @@ enum { int vfs_folder_init(vfs_object_t* o, const char* path, void* user) { o->type = VFST_FOLDER; - o->amask = 0; + o->flags = 0; o->eparam[RP_USED] = 0; o->eparam[RP_TOTAL] = STD_FOLDER_QTY; o->eparam[RP_UNUSED] = 0; @@ -35,19 +36,36 @@ int vfs_folder_init(vfs_object_t* o, const char* path, void* user) if (o->data.obj == NULL) return -ENOMEM; - strncpy(o->full_path, path, sizeof(o->full_path)); + snprintf(o->full_path, sizeof(o->full_path), "%s", path); return 0; } void vfs_folder_destroy(vfs_object_t* o) { + assert(o->type == VFST_FOLDER); + o->eparam[RP_USED] = 0; o->eparam[RP_TOTAL] = 0; + free(o->data.obj); o->data.obj = NULL; } +int vfs_get_by_path(vfs_object_t* root, const char* path, vfs_object_t** obj) +{ + vfs_object_t *nodes = (vfs_object_t *)root->data.obj; + + for (unsigned i = 0; i < root->eparam[RP_USED]; i++) { + if (fnmatch(path, nodes[i].full_path, FNM_NOESCAPE) == 0) { + *obj = &nodes[i]; + return 0; + } + } + + return -ENOENT; +} + static int _vfs_reserve(vfs_object_t* root, unsigned extra) { if (root->type != VFST_FOLDER) { @@ -90,7 +108,7 @@ static int _vfs_alloc_object(vfs_object_t* root, vfs_object_t** newobj, uint8_t memset(obj, 0, sizeof(vfs_object_t)); obj->type = type; - strncpy(obj->full_path, path, sizeof(obj->full_path)); + snprintf(obj->full_path, sizeof(obj->full_path), "%s", path); *newobj = obj; return 0; @@ -244,3 +262,30 @@ int vfs_add_obj_i64(vfs_object_t* root, const char* fullpath, void* obj, uint64_ return 0; } + +int vfs_add_obj_link(vfs_object_t* root, const char* fullpath, void* obj, const char* link) +{ + vfs_object_t* no; + vfs_object_t* lnk_obj; + int res = vfs_get_by_path(root, link, &lnk_obj); + if (res) + return res; + if (lnk_obj->type == VFST_FOLDER) + return -EINVAL; + + // Save found object links, since _vfs_alloc_object may realloc the pool and lnk_obj will be invalid + struct vfs_ops orig_ops = lnk_obj->ops; + union vfs_variant orig_data = lnk_obj->data; + + res = _vfs_alloc_object(root, &no, lnk_obj->type, fullpath); + if (res) + return res; + + // Mark that's a link + no->flags = VFS_FLAG_LINK; + no->object = obj; + no->data = orig_data; + no->ops = orig_ops; + + return 0; +} diff --git a/src/lib/device/device_vfs.h b/src/lib/device/device_vfs.h index de810624..108916b6 100644 --- a/src/lib/device/device_vfs.h +++ b/src/lib/device/device_vfs.h @@ -7,7 +7,6 @@ #include enum vfs_type { - VFST_LINK = 'l', VFST_FOLDER = 'f', VFST_I64 = 'i', @@ -47,10 +46,14 @@ struct vfs_ops { vfs_get_ai64_func_t gai64; }; +enum vfs_flags { + VFS_FLAG_LINK = 1, + VFS_FLAG_HIDDEN = 2, /* skip when iterating */ +}; struct vfs_object { uint8_t type; - uint8_t amask; + uint8_t flags; uint16_t eparam[3]; // Object specific paramenets void* object; // User associated object with the vfs @@ -62,16 +65,20 @@ struct vfs_object { typedef struct vfs_object vfs_object_t; int vfs_folder_init(vfs_object_t* o, const char* path, void* user); + +int vfs_get_by_path(vfs_object_t* o, const char* path, vfs_object_t** obj); + void vfs_folder_destroy(vfs_object_t* o); struct vfs_constant_i64 { const char* fullpath; uint64_t value; }; +typedef struct vfs_constant_i64 vfs_constant_i64_t; -int vfs_add_const_i64_vec(vfs_object_t* root, const struct vfs_constant_i64* params, unsigned count); +int vfs_add_const_i64_vec(vfs_object_t* root, const vfs_constant_i64_t* params, unsigned count); -static inline int vfs_add_const_i64(vfs_object_t* root, const struct vfs_constant_i64* params) { +static inline int vfs_add_const_i64(vfs_object_t* root, vfs_constant_i64_t* params) { return vfs_add_const_i64_vec(root, params, 1); } @@ -79,15 +86,16 @@ struct vfs_constant_str { const char* fullpath; const char* value; }; +typedef struct vfs_constant_str vfs_constant_str_t; -int vfs_add_const_str_vec(vfs_object_t* root, const struct vfs_constant_str* params, unsigned count); +int vfs_add_const_str_vec(vfs_object_t* root, const vfs_constant_str_t* params, unsigned count); -static inline int vfs_add_const_str(vfs_object_t* root, const struct vfs_constant_str* params) { +static inline int vfs_add_const_str(vfs_object_t* root, vfs_constant_str_t* params) { return vfs_add_const_str_vec(root, params, 1); } int vfs_add_obj_i64(vfs_object_t* root, const char* fullpath, void* obj, uint64_t defval, vfs_set_i64_func_t fs, vfs_get_i64_func_t fg); - +int vfs_add_obj_link(vfs_object_t* root, const char* fullpath, void* obj, const char* link); #endif diff --git a/src/lib/device/ext_fe_ch4_400_7200/CMakeLists.txt b/src/lib/device/ext_fe_ch4_400_7200/CMakeLists.txt new file mode 100644 index 00000000..70fab040 --- /dev/null +++ b/src/lib/device/ext_fe_ch4_400_7200/CMakeLists.txt @@ -0,0 +1,20 @@ +set(EXT_FE_CH4_400_7200_LIB_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/ext_fe_ch4_400_7200.c +) + +set(HW_FILES ext_fe_ch4_400_7200_e ext_fe_ch4_400_7200_usr) +foreach(I ${HW_FILES}) + message(STATUS "Generating header for ${I}") + GENERATE_YAML_H(${CMAKE_CURRENT_SOURCE_DIR}/${I}.yaml ${CMAKE_CURRENT_BINARY_DIR}/def_${I}.h) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${I}.yaml DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/usdr/schema/) + + list(APPEND USDR_DEPEND_TARGETS generate_${I}) +endforeach() + + +list(APPEND USDR_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) +list(APPEND USDR_LIBRARY_FILES ${EXT_FE_CH4_400_7200_LIB_FILES}) + +set(USDR_LIBRARY_FILES ${USDR_LIBRARY_FILES} PARENT_SCOPE) +set(USDR_DEPEND_TARGETS ${USDR_DEPEND_TARGETS} PARENT_SCOPE) +set(USDR_INCLUDE_DIRS ${USDR_INCLUDE_DIRS} PARENT_SCOPE) diff --git a/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.c b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.c new file mode 100644 index 00000000..a1829be8 --- /dev/null +++ b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.c @@ -0,0 +1,786 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include +#include +#include + +#include "ext_fe_ch4_400_7200.h" +#include "../ipblks/gpio.h" +#include "../ipblks/spiext.h" +#include "../hw/tmp114/tmp114.h" +#include "../hw/dac80501/dac80501.h" +#include "../hw/tca6424a/tca6424a.h" + +#include "../generic_usdr/generic_regs.h" + +#include +#include + +// M.2 breakout +// ------------------------------------------------------------------------------------- +// M.2 pin sSDR dSDR +// 8 SSDR_GPIO2 M2_1PPS_SYNC -- +// +// 10 SSDR_GPLED0 GPIO33_0 EXT_I2C_SDA +// 20 SSDR_GPIO1 -------- AUX_MUX_GPIO1 -- *EXT2_I2C_SDA / FAN0_TACH +// 38 SSDR_GPLED1_P GPIO33_3 FGPIO_N +// 40 SSDR_GPLED1_N FGPIO_P +// 54 SSDR_GPIO6 GPIO33_1 EXT_I2C_SCL +// 56 SSDR_GPIO3_P -------- n/c GPS_TX +// 57 SSDR_GPIO3_N -------- n/c GPS_RX +// 68 SSDR_GPIO5 GPIO33_2 AUX_MUX_GPIO0 -- *EXT2_I2C_SCL / FAN1_TACH +// +// I2C3: +// SSDR_GPLED0 M1 | GPIO33_0 +// SSDR_GPIO6 H2 | GPIO33_1 +// I2C4: +// SSDR_GPIO1 L1 | GPIO_EXT_18_5 +// SSDR_GPIO5 J2 | GPIO33_2 through R2012 GPIO_EXT_18_4 +// +// For DSDR GPIO33_2 / GPIO33_3 must be configured as Inputs + +enum { + GPIO_1PPS = GPIO2, + + // GPIO_I2C3_SDA = GPIO12, + GPIO_I2C3_SCL = GPIO6, + + GPIO_I2C4_SDA = GPIO1, + GPIO_I2C4_SCL = GPIO5, +}; + +enum { + TCA6424A_ADDR_L = 0x22, + TCA6424A_ADDR_H = 0x23, + + SC18IS606_ADDR = 0b0101111, +}; + +enum i2c_idx_extra { + I2C_TCA6424AR_U114 = MAKE_LSOP_I2C_ADDR(1, 0, TCA6424A_ADDR_L), + I2C_TCA6424AR_U110 = MAKE_LSOP_I2C_ADDR(1, 0, TCA6424A_ADDR_H), + I2C_TCA6424AR_U300 = MAKE_LSOP_I2C_ADDR(1, 1, TCA6424A_ADDR_L), //AB + I2C_TCA6424AR_U301 = MAKE_LSOP_I2C_ADDR(1, 1, TCA6424A_ADDR_H), //CD + + I2C_TEMP_U69 = MAKE_LSOP_I2C_ADDR(1, 0, I2C_DEV_TMP114NB), + + I2C_DAC = MAKE_LSOP_I2C_ADDR(1, 0, 0x48), +}; + +enum { + RX_DSA_MAX_ATTN = 15, + + TX_GAIN_1ST = 20, +}; + +static const uint64_t s_filerbank_ranges[] = { + 50e6, 1000e6, + 1000e6, 2000e6, + 2000e6, 3500e6, + 2500e6, 5000e6, + 3500e6, 7100e6, +}; + +//------------------------ +// Low level expanders control + +static int _ext_fe_ch4_400_7200_exp_upd(ext_fe_ch4_400_7200_t *fe, unsigned addr, unsigned data) +{ + int res = 0; + switch (addr) { + case 0x0: + res = res ? res : tca6424a_reg16_set(fe->dev, fe->subdev, I2C_TCA6424AR_U114, TCA6424_OUT0, data); + res = res ? res : tca6424a_reg8_set(fe->dev, fe->subdev, I2C_TCA6424AR_U114, TCA6424_OUT0 + 2, data >> 16); + break; + case 0x1: + case 0x2: + case 0x3: + res = tca6424a_reg8_set(fe->dev, fe->subdev, I2C_TCA6424AR_U110, TCA6424_OUT0 + (addr - 0x1), data); + break; + case 0x4: + case 0x5: + case 0x6: + // TODO: Fast path + res = tca6424a_reg8_set(fe->dev, fe->subdev, I2C_TCA6424AR_U300, TCA6424_OUT0 + (addr - 0x4), data); + break; + case 0x7: + case 0x8: + case 0x9: + // TODO: Fast path + res = tca6424a_reg8_set(fe->dev, fe->subdev, I2C_TCA6424AR_U301, TCA6424_OUT0 + (addr - 0x7), data); + break; + default: + return -EINVAL; + } + return res; +} + +static int _ext_fe_ch4_400_7200_exp_get(ext_fe_ch4_400_7200_t *fe, unsigned addr) +{ + int res = 0; + uint8_t di8 = 0; + uint16_t di16; + switch (addr) { + case 0x0: + res = res ? res : tca6424a_reg16_get(fe->dev, fe->subdev, I2C_TCA6424AR_U114, TCA6424_OUT0, &di16); + res = res ? res : tca6424a_reg8_get(fe->dev, fe->subdev, I2C_TCA6424AR_U114, TCA6424_OUT0 + 2, &di8); + + fe->debug_fe_reg_last = (unsigned)di16 | (((unsigned)di8) << 16); + + USDR_LOG("FE4C", USDR_LOG_WARNING, "FE_4CH_EXP RD %02x => %08x\n", addr, fe->debug_fe_reg_last); + return res; + case 0x1: + case 0x2: + case 0x3: + res = tca6424a_reg8_get(fe->dev, fe->subdev, I2C_TCA6424AR_U110, TCA6424_OUT0 + (addr - 0x1), &di8); + break; + + case 0x4: + case 0x5: + case 0x6: + res = tca6424a_reg8_get(fe->dev, fe->subdev, I2C_TCA6424AR_U300, TCA6424_OUT0 + (addr - 0x4), &di8); + break; + + case 0x7: + case 0x8: + case 0x9: + res = tca6424a_reg8_get(fe->dev, fe->subdev, I2C_TCA6424AR_U301, TCA6424_OUT0 + (addr - 0x7), &di8); + break; + default: + return -EINVAL; + } + + USDR_LOG("FE4C", USDR_LOG_WARNING, "FE_4CH_EXP RD %02x => %02x\n", addr, di8); + fe->debug_fe_reg_last = di8; + return res; +} + +//--------------------------------- +// high level user friendly control +static void _ext_fe_fbank_map(unsigned filsel, unsigned *bout, unsigned *bin) +{ + // Sanity check YAML <-> internal ABI constants + CHECK_CONSTANT_EQ(RX_FILT_OPTS_FILT_50_1000M, RX_FB_50_1000M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_FILT_1000_2000M, RX_FB_1000_2000M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_FILT_2000_3500M, RX_FB_2000_3500M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_FILT_2500_5000M, RX_FB_2500_5000M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_FILT_3500_7100M, RX_FB_3500_7100M); + + CHECK_CONSTANT_EQ(RX_FILT_OPTS_AUTO_50_1000M, RX_FB_AUTO | RX_FB_50_1000M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_AUTO_1000_2000M, RX_FB_AUTO | RX_FB_1000_2000M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_AUTO_2000_3500M, RX_FB_AUTO | RX_FB_2000_3500M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_AUTO_2500_5000M, RX_FB_AUTO | RX_FB_2500_5000M); + CHECK_CONSTANT_EQ(RX_FILT_OPTS_AUTO_3500_7100M, RX_FB_AUTO | RX_FB_3500_7100M); + + CHECK_CONSTANT_EQ(SW_RX_FILTER_IN_CHA_50_1000M, SW_RX_FILTER_OUT_CHA_50_1000M); + + unsigned fb_f_sel = (~RX_FB_AUTO & filsel); + switch (fb_f_sel) { + case RX_FB_50_1000M: *bout = SW_RX_FILTER_OUT_CHA_50_1000M; *bin = SW_RX_FILTER_IN_CHA_50_1000M; break; + case RX_FB_1000_2000M: *bout = SW_RX_FILTER_OUT_CHA_1000_2000M; *bin = SW_RX_FILTER_IN_CHA_1000_2000M; break; + case RX_FB_2000_3500M: *bout = SW_RX_FILTER_OUT_CHA_2000_3500M; *bin = SW_RX_FILTER_IN_CHA_2000_3500M; break; + case RX_FB_2500_5000M: *bout = SW_RX_FILTER_OUT_CHA_2500_5000M; *bin = SW_RX_FILTER_IN_CHA_2500_5000M; break; + case RX_FB_3500_7100M: *bout = SW_RX_FILTER_OUT_CHA_3500_7100M; *bin = SW_RX_FILTER_IN_CHA_3500_7100M; break; + default: *bout = SW_RX_FILTER_OUT_CHA_MUTE1; *bin = SW_RX_FILTER_IN_CHA_MUTE1; break; + } +} + +// Switch on RX path => ANT_RX external port / rfsw_rxtx / LB +enum rfsw_tddfdd_bits { + EXP_TDDFDD_SD = 0b00, // LB SW is on + EXP_TDDFDD_P1_LB_SW = 0b01, // LB SW is on + EXP_TDDFDD_P2_TRX_SW = 0b10, + EXP_TDDFDD_P3_ANT_RX = 0b11, +}; + +// Switch on ANT_TRX external port => rfsw_tddfdd / rfsw_tx_onoff +enum rfsw_rxtx_bits { + EXP_RXTX_SW_P2_RX = 1, + EXP_RXTX_SW_P1_TX = 0, +}; + +// Switch TX path => rfsw_rxtx / LB +enum rfsw_tx_onoff_bits { + EXP_TX_ONOFF_P2_LB_SW = 1, + EXP_TX_ONOFF_P1_TRX_SW = 0, +}; + +enum led_rtx_vals { + LED_TRX_RXO = 0, + LED_TRX_OFF = 1, + LED_TRX_TRX = 2, + LED_TRX_TXO = 3, +}; + +enum led_rx_cals { + LED_RX_ON = 0, + LED_RX_OFF = 1, +}; + +static void _ext_fe_antenna_sw_map_exp(unsigned antenna, bool rxen, bool txen, + uint8_t* exp_tddfdd, uint8_t* exp_rxtx, uint8_t* exp_tx_onoff, + uint8_t* exp_led_trx, uint8_t* exp_led_rx, + unsigned *arx, unsigned *atx) +{ + CHECK_CONSTANT_EQ(ANT_OPTS_RX_TO_RX_AND_TX_TO_TRX, ANT_RX_TRX); + CHECK_CONSTANT_EQ(ANT_OPTS_RX_TO_TRX_AND_TX_TERM, ANT_TRX_TERM); + CHECK_CONSTANT_EQ(ANT_OPTS_RX_TO_RX_AND_TX_TERM, ANT_RX_TERM); + CHECK_CONSTANT_EQ(ANT_OPTS_RX_TX_LOOPBACK, ANT_LOOPBACK); + CHECK_CONSTANT_EQ(ANT_OPTS_TDD_DRIVEN_AUTO, ANT_HW_TDD); + + switch (antenna) { + case ANT_RX_TRX: + *exp_tx_onoff = EXP_TX_ONOFF_P1_TRX_SW; + *exp_rxtx = EXP_RXTX_SW_P1_TX; + *exp_tddfdd = EXP_TDDFDD_P3_ANT_RX; + *exp_led_trx = txen ? LED_TRX_TXO : LED_TRX_OFF; + *exp_led_rx = rxen ? LED_RX_ON : LED_RX_OFF; + *arx = rxen; + *atx = txen; + break; + + case ANT_TRX_TERM: + *exp_tx_onoff = EXP_TX_ONOFF_P2_LB_SW; + *exp_rxtx = EXP_RXTX_SW_P2_RX; + *exp_tddfdd = EXP_TDDFDD_P2_TRX_SW; + *exp_led_trx = rxen ? LED_TRX_RXO : LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; + *arx = rxen; + *atx = 0; + break; + + case ANT_RX_TERM: + *exp_tx_onoff = EXP_TX_ONOFF_P2_LB_SW; + *exp_rxtx = EXP_RXTX_SW_P1_TX; + *exp_tddfdd = EXP_TDDFDD_P3_ANT_RX; + *exp_led_trx = LED_TRX_OFF; + *exp_led_rx = rxen ? LED_RX_ON : LED_RX_OFF; + *arx = rxen; + *atx = 0; + break; + + case ANT_LOOPBACK: + *exp_tx_onoff = EXP_TX_ONOFF_P2_LB_SW; + *exp_rxtx = EXP_RXTX_SW_P1_TX; + *exp_tddfdd = EXP_TDDFDD_P1_LB_SW; + *exp_led_trx = LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; + *arx = rxen; + *atx = txen; + break; + + case ANT_HW_TDD: + // TODO + *exp_led_trx = (rxen && txen) ? LED_TRX_TRX : txen ? LED_TRX_TXO : rxen ? LED_TRX_RXO : LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; + *arx = rxen; + *atx = txen; + break; + + default: + *exp_tx_onoff = EXP_TX_ONOFF_P2_LB_SW; + *exp_rxtx = EXP_RXTX_SW_P1_TX; + *exp_tddfdd = EXP_TDDFDD_P1_LB_SW; + + *exp_led_trx = LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; + *arx = 0; + *atx = 0; + break; + } +} + +void ext_fe_rx_filterbank_upd(ext_fe_ch4_400_7200_t* def, unsigned chno) +{ + if (def->ucfg[chno].rx_fb_sel < RX_FILT_OPTS_AUTO_50_1000M) + return; + + unsigned best_idx = 0; + unsigned best_off = 1000; + + for (unsigned i = 0; i < SIZEOF_ARRAY(s_filerbank_ranges); i+= 2) { + if (s_filerbank_ranges[i] > def->ucfg[chno].rx_freq || def->ucfg[chno].rx_freq > s_filerbank_ranges[i + 1]) { + continue; + } + + int64_t doff = (int64_t)(s_filerbank_ranges[i] + s_filerbank_ranges[i + 1]) / 2 - def->ucfg[chno].rx_freq ; + if (doff < 0) + doff = 0 - doff; + + unsigned off = 1000 * doff / (s_filerbank_ranges[i + 1] - s_filerbank_ranges[i]); + if (off < best_off) { + best_off = off; + best_idx = i / 2; + } + + USDR_LOG("FE4C", USDR_LOG_WARNING, "F%d %.3f -- %.3f DOFF=%u OFF=%u\n", + i, s_filerbank_ranges[i] / 1.0e6, s_filerbank_ranges[i + 1] / 1.0e6, (unsigned)doff, off); + } + + def->ucfg[chno].rx_fb_sel = RX_FILT_OPTS_AUTO_50_1000M | best_idx; + USDR_LOG("FE4C", USDR_LOG_WARNING, "RXFBabk[%d] = %d\n", chno, def->ucfg[chno].rx_fb_sel); +} + +// This function just update states of internal HW and I2C expander registers, +// all calculation of band, filter, lofreq, etc. has been done before this call +int ext_fe_update_user(ext_fe_ch4_400_7200_t* fe) +{ + unsigned fbanksel_out[FE_MAX_HW_CHANS]; + unsigned fbanksel_in[FE_MAX_HW_CHANS]; + + uint8_t exp_tx_onoff[FE_MAX_HW_CHANS]; + uint8_t exp_rxtx[FE_MAX_HW_CHANS]; + uint8_t exp_tddfdd[FE_MAX_HW_CHANS]; + + unsigned act_rx[FE_MAX_HW_CHANS]; + unsigned act_tx[FE_MAX_HW_CHANS]; + + uint8_t rx_led[FE_MAX_HW_CHANS]; + uint8_t trx_led[FE_MAX_HW_CHANS]; + + uint8_t enanble_rx = 0; + uint8_t enanble_tx = 0; + + int res = 0; + + for (unsigned i = 0; i < FE_MAX_HW_CHANS; i++) { + unsigned rxen = fe->ucfg[i].rx_en; + unsigned txen = fe->ucfg[i].tx_en; + + // RX filterbank + _ext_fe_fbank_map(fe->ucfg[i].rx_fb_sel, &fbanksel_out[i], &fbanksel_in[i]); // SW_RX_FILTER_OUT_CHA_MUTE1 if not enabled? + + // Antenna switch, RF PA/LNA switch, loopback switch + _ext_fe_antenna_sw_map_exp(fe->ucfg[i].ant_sel, rxen, txen, &exp_tddfdd[i], &exp_rxtx[i], &exp_tx_onoff[i], + &trx_led[i], &rx_led[i], &act_rx[i], &act_tx[i]); + + enanble_rx |= rxen; + enanble_tx |= txen; + }; + + // RX filter Bank + fe->fe_exp_regs[0] = MAKE_EXT_FE_CH4_400_7200_E_SW_RX_FILTER( + fbanksel_in[H_CHD], fbanksel_in[H_CHC], fbanksel_in[H_CHB], fbanksel_in[H_CHA], + fbanksel_out[H_CHA], fbanksel_out[H_CHB], fbanksel_out[H_CHC], fbanksel_out[H_CHD]); + + // Global enable + fe->fe_exp_regs[1] = MAKE_EXT_FE_CH4_400_7200_E_ENABLE( + fe->ucfg[H_CHD].tx_ss, fe->ucfg[H_CHC].tx_ss, fe->ucfg[H_CHB].tx_ss, fe->ucfg[H_CHA].tx_ss, + fe->if_vbyp, fe->ref_gps, enanble_tx, enanble_rx); + + // TRX leds + fe->fe_exp_regs[2] = MAKE_EXT_FE_CH4_400_7200_E_LED_TRX_CTRL(trx_led[H_CHD], trx_led[H_CHC], trx_led[H_CHB], trx_led[H_CHA]); + fe->fe_exp_regs[3] = MAKE_EXT_FE_CH4_400_7200_E_LEDRX_CH_CTRL( + rx_led[H_CHD], rx_led[H_CHC], rx_led[H_CHB], rx_led[H_CHA], + fe->ucfg[H_CHD].rx_en, fe->ucfg[H_CHC].rx_en, fe->ucfg[H_CHB].rx_en, fe->ucfg[H_CHA].rx_en); + + + // Expander & HIGH-speed IO + // AB / CD control pairs + for (unsigned pair = 0; pair < 2; pair++) { + unsigned idx = P_A_EN_AB - SW_RX_FILTER + (P_A_EN_CD - P_A_EN_AB) * pair; + + fe->fe_exp_regs[idx + 0] = MAKE_EXT_FE_CH4_400_7200_E_REG_WR(idx + SW_RX_FILTER, MAKE_EXT_FE_CH4_400_7200_E_P_A_EN_AB( + act_tx[2 * pair + 0], act_tx[2 * pair + 1])); + fe->fe_exp_regs[idx + 1] = MAKE_EXT_FE_CH4_400_7200_E_REG_WR(idx + SW_RX_FILTER, MAKE_EXT_FE_CH4_400_7200_E_ATTN_RX_CH_AB( + fe->ucfg[2 * pair + 0].rx_dsa, fe->ucfg[2 * pair + 1].rx_dsa)); + + fe->fe_exp_regs[idx + 2] = MAKE_EXT_FE_CH4_400_7200_E_REG_WR(idx + SW_RX_FILTER, MAKE_EXT_FE_CH4_400_7200_E_SW_AB( + exp_rxtx[2 * pair + 1], exp_tx_onoff[2 * pair + 1], + exp_rxtx[2 * pair + 0], exp_tx_onoff[2 * pair + 0], + exp_tddfdd[2 * pair + 0], exp_tddfdd[2 * pair + 1])); + } + + // TODO add control for high speed IO + for (unsigned addr = 0; addr < FE_CTRL_REGS; addr++) { + res = res ? res : _ext_fe_ch4_400_7200_exp_upd(fe, addr, fe->fe_exp_regs[addr]); + } + return res; +} + + +int ext_fe_rx_freq_set(ext_fe_ch4_400_7200_t* def, unsigned chno, uint64_t freq) +{ + if (chno >= FE_MAX_HW_CHANS) + return -EINVAL; + if (!def->ucfg[chno].rx_en) + return 0; + + def->ucfg[chno].rx_freq = freq; + + ext_fe_rx_filterbank_upd(def, chno); + return ext_fe_update_user(def); +} + +int ext_fe_rx_chan_en(ext_fe_ch4_400_7200_t* def, unsigned ch_fe_mask_rx) +{ + for (unsigned i = 0; i < FE_MAX_HW_CHANS; i++) { + def->ucfg[i].rx_en = (ch_fe_mask_rx & (1u << i)) ? 1 : 0; + } + return ext_fe_update_user(def); +} + +int ext_fe_tx_chan_en(ext_fe_ch4_400_7200_t* def, unsigned ch_fe_mask_tx) +{ + for (unsigned i = 0; i < FE_MAX_HW_CHANS; i++) { + def->ucfg[i].tx_en = (ch_fe_mask_tx & (1u << i)) ? 1 : 0; + } + return ext_fe_update_user(def); +} + +int ext_fe_rx_gain_set(ext_fe_ch4_400_7200_t* def, unsigned chno, unsigned gain, unsigned* actual_gain) +{ + if (chno >= FE_MAX_HW_CHANS) + return -EINVAL; + if (!def->ucfg[chno].rx_en) + return 0; + if (gain > RX_DSA_MAX_ATTN) + gain = RX_DSA_MAX_ATTN; + + def->ucfg[chno].rx_dsa = RX_DSA_MAX_ATTN - gain; + + if (actual_gain) { + *actual_gain = gain; + } + + return ext_fe_update_user(def); +} + +int ext_fe_tx_gain_set(ext_fe_ch4_400_7200_t* def, unsigned chno, unsigned gain, unsigned* actual_gain) +{ + if (chno >= FE_MAX_HW_CHANS) + return -EINVAL; + if (!def->ucfg[chno].tx_en) + return 0; + + def->ucfg[chno].tx_ss = (gain <= TX_GAIN_1ST); + + if (actual_gain) { + *actual_gain = (gain > TX_GAIN_1ST) ? TX_GAIN_1ST : 0; + } + + return ext_fe_update_user(def); +} + + +int ext_fe_ch4_sens_get(ext_fe_ch4_400_7200_t* fe, uint64_t *ovalue) +{ + int temp256 = 127*256, res; + res = tmp114_temp_get(fe->dev, fe->subdev, I2C_TEMP_U69, &temp256); + *ovalue = (int64_t)temp256; + return res; +} + + +static int ext_fe_ch4_400_7200_ctrl_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + ext_fe_ch4_400_7200_t* fe = (ext_fe_ch4_400_7200_t*)obj->object; + int res; + unsigned addr = (value >> 24) & 0x7f; + unsigned data = value & 0xffffff; + + fe->debug_fe_reg_last = ~0u; + + if (value & 0x80000000) { + USDR_LOG("FE4C", USDR_LOG_WARNING, "FE_CH4_CTRL %08x => %08x\n", addr, data); + + if (addr < SW_RX_FILTER || addr >= SW_RX_FILTER + FE_CTRL_REGS) { + return -EINVAL; + } + res = _ext_fe_ch4_400_7200_exp_upd(fe, addr - SW_RX_FILTER, data); + } else { + res = _ext_fe_ch4_400_7200_exp_get(fe, addr - SW_RX_FILTER); + } + + return res; +} + +static int ext_fe_ch4_400_7200_ctrl_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + ext_fe_ch4_400_7200_t* fe = (ext_fe_ch4_400_7200_t*)obj->object; + *ovalue = fe->debug_fe_reg_last; + return 0; +} + +int ext_fe_ch4_400_7200_temp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + return ext_fe_ch4_sens_get((ext_fe_ch4_400_7200_t*)obj->object, ovalue); +} + + +static int ext_fe_ch4_400_7200_usr_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + ext_fe_ch4_400_7200_t* fe = (ext_fe_ch4_400_7200_t*)obj->object; + int res = 0; + unsigned addr = (value >> 24) & 0x7f; + unsigned data = value & 0xffffff; + + fe->debug_fe_usr_last = ~0u; + + if (value & 0x80000000) { + USDR_LOG("FE4C", USDR_LOG_WARNING, "EXFE_4CH_USER %08x => %08x\n", addr, data); + + switch (addr) { + case RX_FILTER_BANK: + fe->ucfg[H_CHA].rx_fb_sel = GET_EXT_FE_CH4_400_7200_USR_RX_FILTER_BANK_A(data); + fe->ucfg[H_CHB].rx_fb_sel = GET_EXT_FE_CH4_400_7200_USR_RX_FILTER_BANK_B(data); + fe->ucfg[H_CHC].rx_fb_sel = GET_EXT_FE_CH4_400_7200_USR_RX_FILTER_BANK_C(data); + fe->ucfg[H_CHD].rx_fb_sel = GET_EXT_FE_CH4_400_7200_USR_RX_FILTER_BANK_D(data); + break; + case RX_ATTN: + fe->ucfg[H_CHA].rx_dsa = GET_EXT_FE_CH4_400_7200_USR_RX_ATTN_A(data); + fe->ucfg[H_CHB].rx_dsa = GET_EXT_FE_CH4_400_7200_USR_RX_ATTN_B(data); + fe->ucfg[H_CHC].rx_dsa = GET_EXT_FE_CH4_400_7200_USR_RX_ATTN_C(data); + fe->ucfg[H_CHD].rx_dsa = GET_EXT_FE_CH4_400_7200_USR_RX_ATTN_D(data); + break; + case ANT_SEL: + fe->ucfg[H_CHA].ant_sel = GET_EXT_FE_CH4_400_7200_USR_ANT_SEL_A(data); + fe->ucfg[H_CHB].ant_sel = GET_EXT_FE_CH4_400_7200_USR_ANT_SEL_B(data); + fe->ucfg[H_CHC].ant_sel = GET_EXT_FE_CH4_400_7200_USR_ANT_SEL_C(data); + fe->ucfg[H_CHD].ant_sel = GET_EXT_FE_CH4_400_7200_USR_ANT_SEL_D(data); + break; + case RX_CHEN: + fe->ucfg[H_CHA].rx_en = GET_EXT_FE_CH4_400_7200_USR_RX_CHEN_A(data); + fe->ucfg[H_CHB].rx_en = GET_EXT_FE_CH4_400_7200_USR_RX_CHEN_B(data); + fe->ucfg[H_CHC].rx_en = GET_EXT_FE_CH4_400_7200_USR_RX_CHEN_C(data); + fe->ucfg[H_CHD].rx_en = GET_EXT_FE_CH4_400_7200_USR_RX_CHEN_D(data); + break; + case TX_CHEN: + fe->ucfg[H_CHA].tx_en = GET_EXT_FE_CH4_400_7200_USR_TX_CHEN_A(data); + fe->ucfg[H_CHB].tx_en = GET_EXT_FE_CH4_400_7200_USR_TX_CHEN_B(data); + fe->ucfg[H_CHC].tx_en = GET_EXT_FE_CH4_400_7200_USR_TX_CHEN_C(data); + fe->ucfg[H_CHD].tx_en = GET_EXT_FE_CH4_400_7200_USR_TX_CHEN_D(data); + break; + case TX_2STAGE: + fe->ucfg[H_CHA].tx_ss = GET_EXT_FE_CH4_400_7200_USR_TX_2STAGE_A(data); + fe->ucfg[H_CHB].tx_ss = GET_EXT_FE_CH4_400_7200_USR_TX_2STAGE_B(data); + fe->ucfg[H_CHC].tx_ss = GET_EXT_FE_CH4_400_7200_USR_TX_2STAGE_C(data); + fe->ucfg[H_CHD].tx_ss = GET_EXT_FE_CH4_400_7200_USR_TX_2STAGE_D(data); + break; + default: + return -EINVAL; + } + + // Update state + res = ext_fe_update_user(fe); + } else { + switch (addr) { + case RX_FILTER_BANK: + fe->debug_fe_usr_last = MAKE_EXT_FE_CH4_400_7200_USR_RX_FILTER_BANK( + fe->ucfg[H_CHD].rx_fb_sel, fe->ucfg[H_CHC].rx_fb_sel, fe->ucfg[H_CHB].rx_fb_sel, fe->ucfg[H_CHA].rx_fb_sel); + break; + case RX_ATTN: + fe->debug_fe_usr_last = MAKE_EXT_FE_CH4_400_7200_USR_RX_ATTN( + fe->ucfg[H_CHD].rx_dsa, fe->ucfg[H_CHC].rx_dsa, fe->ucfg[H_CHB].rx_dsa, fe->ucfg[H_CHA].rx_dsa); + break; + case ANT_SEL: + fe->debug_fe_usr_last = MAKE_EXT_FE_CH4_400_7200_USR_ANT_SEL( + fe->ucfg[H_CHD].ant_sel, fe->ucfg[H_CHC].ant_sel, fe->ucfg[H_CHB].ant_sel, fe->ucfg[H_CHA].ant_sel); + break; + case RX_CHEN: + fe->debug_fe_usr_last = MAKE_EXT_FE_CH4_400_7200_USR_RX_CHEN( + fe->ucfg[H_CHD].rx_en, fe->ucfg[H_CHC].rx_en, fe->ucfg[H_CHB].rx_en, fe->ucfg[H_CHA].rx_en); + break; + case TX_CHEN: + fe->debug_fe_usr_last = MAKE_EXT_FE_CH4_400_7200_USR_TX_CHEN( + fe->ucfg[H_CHD].tx_en, fe->ucfg[H_CHC].tx_en, fe->ucfg[H_CHB].tx_en, fe->ucfg[H_CHA].tx_en); + break; + case TX_2STAGE: + fe->debug_fe_usr_last = MAKE_EXT_FE_CH4_400_7200_USR_TX_2STAGE( + fe->ucfg[H_CHD].tx_ss, fe->ucfg[H_CHC].tx_ss, fe->ucfg[H_CHB].tx_ss, fe->ucfg[H_CHA].tx_ss); + break; + default: + return -EINVAL; + }; + } + + return res; +} + +static int ext_fe_ch4_400_7200_usr_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + ext_fe_ch4_400_7200_t* fe = (ext_fe_ch4_400_7200_t*)obj->object; + *ovalue = fe->debug_fe_usr_last; + + return 0; +} + + +static const usdr_dev_param_func_t s_fe_parameters[] = { + { "/dm/sensor/temp_fe0", { NULL, ext_fe_ch4_400_7200_temp_get }}, + { "/debug/hw/exfe10_4ch_exp/0/reg" , { ext_fe_ch4_400_7200_ctrl_reg_set, ext_fe_ch4_400_7200_ctrl_reg_get } }, + { "/debug/hw/exfe10_4ch_usr/0/reg" , { ext_fe_ch4_400_7200_usr_reg_set, ext_fe_ch4_400_7200_usr_reg_get } }, +}; + + +int ext_fe_ch4_400_7200_init(lldev_t dev, + unsigned subdev, + unsigned gpio_base, + const char *params, + const char *compat, + ext_fe_ch4_400_7200_t* ob) +{ + int res = 0; + int val = 0; + uint16_t val16[4] = { 0xbad, 0xbad, 0xbad, 0xbad }; + device_t* base = lowlevel_get_device(dev); + ob->dev = dev; + ob->subdev = subdev; + + USDR_LOG("FE4C", USDR_LOG_INFO, "Initializing FE_4CH_400_7200 front end...\n"); + + // if (strcmp(compat, "m2m") != 0) { + // return -ENODEV; + // } + // TODO sSDR/dSDR specific + + // Configure external I2C bus + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO_I2C3_SCL, GPIO_CFG_ALT1); + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO_I2C4_SDA, GPIO_CFG_ALT1); + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO_I2C4_SCL, GPIO_CFG_ALT1); + if (res) + return res; + + res = (res) ? res : tmp114_devid_get(dev, subdev, I2C_TEMP_U69, &val); + + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U114, TCA6424_CFG0, &val16[0]); + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U110, TCA6424_CFG0, &val16[1]); + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U300, TCA6424_CFG0, &val16[2]); + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U301, TCA6424_CFG0, &val16[3]); + + if (res) { + USDR_LOG("FE4C", USDR_LOG_ERROR, "Unable to initialize I2C bus, error: %d\n", res); + } else { + USDR_LOG("FE4C", USDR_LOG_ERROR, "Temp ID = %4x, {U114/U110/U300/U301}_Cfg0 = %4x/%4x/%4x/%4x\n", val, + val16[0], val16[1], val16[2], val16[3]); + } + + if (res) + return res; + if (val != TMP114_DEVICE_ID) + return -ENODEV; + + //res = (res) ? res : tmp114_config_set(dev, subdev, I2C_TEMP_U69, 0x4); + //res = (res) ? res : tmp114_temp_get(dev, subdev, I2C_TEMP_U69, &val); + enum { + TEST_OUT_U114 = 0xf070, + TEST_OUT_U110 = 0x00e0, + TEST_OUT_U300 = 0x001e, + TEST_OUT_U301 = 0x003c, + }; + + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO_1PPS, GPIO_CFG_ALT0); + + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U114, TCA6424_OUT0, TEST_OUT_U114); + res = (res) ? res : tca6424a_reg8_set(dev, subdev, I2C_TCA6424AR_U114, TCA6424_OUT0 + 2, 0); + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U110, TCA6424_OUT0, TEST_OUT_U110); + res = (res) ? res : tca6424a_reg8_set(dev, subdev, I2C_TCA6424AR_U110, TCA6424_OUT0 + 2, 0); + + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U300, TCA6424_OUT0, TEST_OUT_U300); + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U301, TCA6424_OUT0, TEST_OUT_U301); + + // Readback test vectors to test expanders + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U114, TCA6424_OUT0, &val16[0]); + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U110, TCA6424_OUT0, &val16[1]); + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U300, TCA6424_OUT0, &val16[2]); + res = (res) ? res : tca6424a_reg16_get(dev, subdev, I2C_TCA6424AR_U301, TCA6424_OUT0, &val16[3]); + + // Set default + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U114, TCA6424_OUT0, 0); + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U110, TCA6424_OUT0, 0); + + if (res) + return res; + if (val16[0] != TEST_OUT_U114 || val16[1] != TEST_OUT_U110 || val16[2] != TEST_OUT_U300 || val16[3] != TEST_OUT_U301) { + USDR_LOG("FE4C", USDR_LOG_ERROR, "Expander test vectors failed: %04x.%04x.%04x.%04x, giving up!\n", + val16[0], val16[1], val16[2], val16[3]); + + return -ENODEV; + } + + + // Enable outputs + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U114, TCA6424_CFG0, 0); + res = (res) ? res : tca6424a_reg8_set(dev, subdev, I2C_TCA6424AR_U114, TCA6424_CFG0 + 2, 0); + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U110, TCA6424_CFG0, 0); + res = (res) ? res : tca6424a_reg8_set(dev, subdev, I2C_TCA6424AR_U110, TCA6424_CFG0 + 2, 0); + + // User initialization + ob->ref_gps = 1; + ob->if_vbyp = 1; + for (unsigned ch = 0; ch < FE_MAX_HW_CHANS; ch++) { + ob->ucfg[ch].rx_fb_sel = RX_FB_AUTO; // rx_filterbank + ob->ucfg[ch].rx_dsa = 0; + ob->ucfg[ch].ant_sel = ANT_RX_TRX; // ANT_OFF; + ob->ucfg[ch].tx_ss = 0; // Single stage PA + ob->ucfg[ch].tx_en = 0; // Channel enabled on device side + ob->ucfg[ch].rx_en = 0; // Channel enabled on device side + ob->ucfg[ch].rx_freq = 0; + } + + res = (res) ? res : ext_fe_update_user(ob); + + // TODO: HighSpeed IO + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U300, TCA6424_CFG0, 0); + res = (res) ? res : tca6424a_reg8_set(dev, subdev, I2C_TCA6424AR_U300, TCA6424_CFG0 + 2, 0); + res = (res) ? res : tca6424a_reg16_set(dev, subdev, I2C_TCA6424AR_U301, TCA6424_CFG0, 0); + res = (res) ? res : tca6424a_reg8_set(dev, subdev, I2C_TCA6424AR_U301, TCA6424_CFG0 + 2, 0); + + + res = (res) ? res : dac80501_init(dev, subdev, I2C_DAC, DAC80501_CFG_REF_DIV_GAIN_MUL); + if (res) { + USDR_LOG("FE4C", USDR_LOG_WARNING, "External DAC not recognized error=%d\n", res); + //return -ENODEV; + // ob->dac_present = false; + } + + uint64_t uid; + res = (res) ? res : tmp114_uid_get(dev, subdev, I2C_TEMP_U69, &uid); + res = (res) ? res : tmp114_temp_get(dev, subdev, I2C_TEMP_U69, &val); + if (res) + return res; + + USDR_LOG("FE4C", USDR_LOG_WARNING, "BoardID %012llx, Temp %.2fC\n", (long long)uid, val / 256.0); + + res = (res) ? res : usdr_vfs_obj_param_init_array_param(base, + (void*)ob, + s_fe_parameters, + SIZEOF_ARRAY(s_fe_parameters)); + if (res) + return res; + + ob->dev = dev; + ob->subdev = subdev; + ob->gpio_base = gpio_base; + ob->hsgpio_base = 0; + return 0; +} + +int ext_fe_destroy(ext_fe_ch4_400_7200_t* dfe) +{ + for (unsigned ch = 0; ch < FE_MAX_HW_CHANS; ch++) { + dfe->ucfg[ch].tx_en = 0; // Channel enabled on device side + dfe->ucfg[ch].rx_en = 0; // Channel enabled on device side + } + + return ext_fe_update_user(dfe); +} + +int ext_fe_set_dac(ext_fe_ch4_400_7200_t* brd, unsigned value) +{ + USDR_LOG("M2PE", USDR_LOG_WARNING, "DAC set to: %d\n", value); + return dac80501_dac_set(brd->dev, brd->subdev, I2C_DAC, value); +} + +int ext_fe_get_temp_max(ext_fe_ch4_400_7200_t* dfe, uint64_t* temp_max) +{ + int temp = 0; + int res = tmp114_temp_get(dfe->dev, dfe->subdev, I2C_TEMP_U69, &temp); + *temp_max = temp; + return res; +} diff --git a/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.h b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.h new file mode 100644 index 00000000..f84bf68f --- /dev/null +++ b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.h @@ -0,0 +1,98 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef EXT_FE_CH4_400_7200_H +#define EXT_FE_CH4_400_7200_H + +#include +#include +#include +#include "../device.h" +#include "../device_vfs.h" + + +#define H_CHA 0 +#define H_CHB 1 +#define H_CHC 2 +#define H_CHD 3 + +#define FE_MAX_HW_CHANS 4 + +#define FE_CTRL_REGS 10 + +#ifndef NO_ECFG_DEFS +enum ext_fe_rx_filterbank { + RX_FB_50_1000M, + RX_FB_1000_2000M, + RX_FB_2000_3500M, + RX_FB_2500_5000M, + RX_FB_3500_7100M, + + RX_FB_AUTO = 8, +}; + +enum ext_fe_antenna_cfg { + ANT_RX_TRX, // RX connected to RX antenna and TX connected to TRX antenna + ANT_TRX_TERM, // RX connected to TRX antenna and TX terminated + ANT_RX_TERM, // RX connected to RX antenna and TX terminated + ANT_LOOPBACK, // RX connected to TX port through attenuator + + ANT_HW_TDD, // TRX antenna is dynamically switched to TX/RX ports based on burst information + ANT_OFF, +}; +#endif + +struct fe_echan_config { + uint8_t rx_fb_sel; // rx_filterbank + uint8_t rx_dsa; + uint8_t ant_sel; // antenna selector + uint8_t tx_ss; // Single stage PA + + uint8_t tx_en; // Channel enabled on device side + uint8_t rx_en; // Channel enabled on device side + + // For auto band & filter selection + uint64_t rx_freq; +}; +typedef struct fe_echan_config fe_echan_config_t; + +struct ext_fe_ch4_400_7200 { + lldev_t dev; + unsigned subdev; + + unsigned gpio_base; + unsigned hsgpio_base; // HighSpeed gpio over LVDS + + + uint32_t fe_exp_regs[FE_CTRL_REGS]; + uint32_t debug_fe_reg_last; + uint32_t debug_fe_usr_last; + + // High level control + uint8_t ref_gps; // Globally enable GPS + uint8_t if_vbyp; // Globally enable IF BYP + fe_echan_config_t ucfg[FE_MAX_HW_CHANS]; +}; + +typedef struct ext_fe_ch4_400_7200 ext_fe_ch4_400_7200_t; + +int ext_fe_ch4_400_7200_init(lldev_t dev, + unsigned subdev, + unsigned gpio_base, + const char *params, + const char *compat, + ext_fe_ch4_400_7200_t* ob); +int ext_fe_destroy(ext_fe_ch4_400_7200_t* dfe); + +int ext_fe_rx_freq_set(ext_fe_ch4_400_7200_t* def, unsigned chno, uint64_t freq); +int ext_fe_rx_chan_en(ext_fe_ch4_400_7200_t* def, unsigned ch_fe_mask_rx); +int ext_fe_tx_chan_en(ext_fe_ch4_400_7200_t* def, unsigned ch_fe_mask_tx); + +int ext_fe_rx_gain_set(ext_fe_ch4_400_7200_t* def, unsigned chno, unsigned gain, unsigned* actual_gain); +int ext_fe_tx_gain_set(ext_fe_ch4_400_7200_t* def, unsigned chno, unsigned gain, unsigned* actual_gain); + +int ext_fe_set_dac(ext_fe_ch4_400_7200_t* brd, unsigned value); + +int ext_fe_get_temp_max(ext_fe_ch4_400_7200_t* dfe, uint64_t* temp_max); + +#endif diff --git a/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_e.yaml b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_e.yaml new file mode 100644 index 00000000..e2be614e --- /dev/null +++ b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_e.yaml @@ -0,0 +1,249 @@ +# Copyright (c) 2023-2024 Wavelet Lab +# SPDX-License-Identifier: MIT + +# Register desc and visual map +name: M2_FE_4CH_BOARD +desc: sSDR/dSDR M.2 external FE board +revision: "0.0.1" +processors: [ c ] +bus: + type: VIRTUAL + usdr_path: /debug/hw/exfe10_4ch_exp/0/reg + wr_mask: 0x80000000 +addr_width: 8 +data_width: 24 +# page_prefix: True +field_prefix: [ RegName ] +field_macros: True + +x-rx-filt-in-opts: &rx-filt-in-opts + 0b000: MUTE0 + 0b011: 50_1000M + 0b010: 1000_2000M + 0b001: 2000_3500M + 0b101: 2500_5000M + 0b100: 3500_7100M + 0b110: MUTE1 + 0b111: MUTE2 + + +x-rx-filt-out-opts: &rx-filt-out-opts + 0b000: MUTE0 + 0b011: 50_1000M + 0b100: 1000_2000M + 0b101: 2000_3500M + 0b001: 2500_5000M + 0b010: 3500_7100M + 0b110: MUTE1 + 0b111: MUTE2 + + + +pages: + - name: V0 + regs: +# + - addr: 0x30 + name: SW_RX_FILTER + fields: +# +# RX filters switching +# ---IN--- ---OUT-- +# V3 V2 V1 V3 V2 V1 +# --+--+-- --+--+-- +# 0 1 1 0 1 1 = 0.4 - 1.0 GHz +# 0 1 0 1 0 0 = 1.0 - 2.0 GHz +# 0 0 1 1 0 1 = 2.0 - 3.5 GHz +# 1 0 1 0 0 1 = 2.5 - 5.0 GHz +# 1 0 0 0 1 0 = 3.5 - 7.0 GHz +# + - bits: "8,23,22" + name: IN_CHA + desc: RX IN filters switch for Channel A + opts: *rx-filt-in-opts +# + - bits: "15,6,7" + name: OUT_CHA + desc: RX OUT filters switch for Channel A + opts: *rx-filt-out-opts +# + - bits: "9,21,20" + name: IN_CHB + desc: RX IN filters switch for Channel B + opts: *rx-filt-in-opts +# + - bits: "14,4,5" + name: OUT_CHB + desc: RX OUT filters switch for Channel B + opts: *rx-filt-out-opts +# + - bits: "10,19,18" + name: IN_CHC + desc: RX IN filters switch for Channel C + opts: *rx-filt-in-opts +# + - bits: "13,3,2" + name: OUT_CHC + desc: RX OUT filters switch for Channel C + opts: *rx-filt-out-opts +# + - bits: "11,17,16" + name: IN_CHD + desc: RX IN filters switch for Channel D + opts: *rx-filt-in-opts +# + - bits: "12,0,1" + name: OUT_CHD + desc: RX OUT filters switch for Channel D + opts: *rx-filt-out-opts +# + - addr: 0x31 + name: ENABLE + fields: + - bits: "3" + name: IF_VBYP + desc: IF Bypass + - bits: "2" + name: REF_GPS + desc: Enable GPS module + - bits: "1" + name: P8V_TX + desc: Enable +8v power supply for TX amps + - bits: "0" + name: P6V_RX + desc: Enable +6v power supply for RX amps + - bits: "7" + name: PA_BYPASS_CHD + desc: Stage-2 PA bypass, channel D + - bits: "6" + name: PA_BYPASS_CHC + desc: Stage-2 PA bypass, channel C + - bits: "5" + name: PA_BYPASS_CHB + desc: Stage-2 PA bypass, channel B + - bits: "4" + name: PA_BYPASS_CHA + desc: Stage-2 PA bypass, channel A +# + - addr: 0x32 + name: LED_TRX_CTRL + fields: + - bits: "1:0" + name: LED_CHA + desc: LED CHA + - bits: "3:2" + name: LED_CHB + desc: LED CHB + - bits: "5:4" + name: LED_CHC + desc: LED CHC + - bits: "7:6" + name: LED_CHD + desc: LED CHD +# + - addr: 0x33 + name: LEDRX_CH_CTRL + fields: + - bits: "0" + name: EN_CHA + desc: Enable CHA + - bits: "1" + name: EN_CHB + desc: Enable CHB + - bits: "2" + name: EN_CHC + desc: Enable CHC + - bits: "3" + name: EN_CHD + desc: Enable CHD + - bits: "4" + name: LED_CHA + desc: LED CHA + - bits: "5" + name: LED_CHB + desc: LED CHB + - bits: "6" + name: LED_CHC + desc: LED CHC + - bits: "7" + name: LED_CHD + desc: LED CHD +# + - addr: 0x34 + name: P_A_EN_AB + fields: + - bits: "6" + name: B + desc: Enable CHB + - bits: "7" + name: A + desc: Enable CHA + - addr: 0x35 + name: ATTN_RX_CH_AB + fields: + - bits: "3:0" + name: B + desc: Attenuator CHB + - bits: "7:4" + name: A + desc: Attenuator CHA + - addr: 0x36 + name: SW_AB + fields: + - bits: "4,1" + name: TDDFDD_A + - bits: "7,0" + name: TDDFDD_B + - bits: "2" + name: PA_ON_A + - bits: "5" + name: PA_ON_B + - bits: "3" + name: RXTX_A + - bits: "6" + name: RXTX_B +# + - addr: 0x37 + name: P_A_EN_CD + fields: + - bits: "6" + name: D + desc: Enable CHD + - bits: "7" + name: C + desc: Enable CHC + - addr: 0x38 + name: ATTN_RX_CH_CD + fields: + - bits: "3:0" + name: D + desc: Attenuator CHD + - bits: "7:4" + name: C + desc: Attenuator CHC + - addr: 0x39 + name: SW_CD + fields: + - bits: "4,1" + name: TDDFDD_C + - bits: "7,0" + name: TDDFDD_D + - bits: "2" + name: PA_ON_C + - bits: "5" + name: PA_ON_D + - bits: "3" + name: RXTX_C + - bits: "6" + name: RXTX_D + + + + + + + + + + + diff --git a/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_usr.yaml b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_usr.yaml new file mode 100644 index 00000000..5693fad3 --- /dev/null +++ b/src/lib/device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200_usr.yaml @@ -0,0 +1,144 @@ +# Copyright (c) 2023-2025 Wavelet Lab +# SPDX-License-Identifier: MIT + +# Register desc and visual map +name: M2_FE_4CH_BOARD_USER +desc: sSDR/dSDR M.2 external FE board user friendly control +revision: "0.0.1" +processors: [ c ] +bus: + type: VIRTUAL + usdr_path: /debug/hw/exfe10_4ch_usr/0/reg + wr_mask: 0x80000000 +addr_width: 8 +data_width: 24 +# page_prefix: True +field_prefix: [ RegName ] +field_macros: True + +x-rx-filt-opts: &rx-filt-opts + 0b0000: FILT_50_1000M + 0b0001: FILT_1000_2000M + 0b0010: FILT_2000_3500M + 0b0011: FILT_2500_5000M + 0b0100: FILT_3500_7100M + 0b1000: AUTO_50_1000M + 0b1001: AUTO_1000_2000M + 0b1010: AUTO_2000_3500M + 0b1011: AUTO_2500_5000M + 0b1100: AUTO_3500_7100M + +x-ant-opts: &x-ant-opts + 0b000: RX_TO_RX_AND_TX_TO_TRX + 0b001: RX_TO_TRX_AND_TX_TERM + 0b010: RX_TO_RX_AND_TX_TERM + 0b011: RX_TX_LOOPBACK + 0b100: TDD_DRIVEN_AUTO + +pages: + - name: FEUSR + regs: +# + - addr: 0x50 + name: RX_FILTER_BANK + fields: + - bits: "3:0" + name: A + desc: Filter bank selector + opts: *rx-filt-opts + - bits: "7:4" + name: B + desc: Filter bank selector + opts: *rx-filt-opts + - bits: "11:8" + name: C + desc: Filter bank selector + opts: *rx-filt-opts + - bits: "15:12" + name: D + desc: Filter bank selector + opts: *rx-filt-opts +# + - addr: 0x51 + name: RX_ATTN + fields: + - bits: "3:0" + name: A + desc: RX attenuator settings in dB + - bits: "7:4" + name: B + desc: RX attenuator settings in dB + - bits: "11:8" + name: C + desc: RX attenuator settings in dB + - bits: "15:12" + name: D + desc: RX attenuator settings in dB +# + - addr: 0x52 + name: ANT_SEL + fields: + - bits: "2:0" + name: A + desc: Antenna path selector + opts: *x-ant-opts + - bits: "6:4" + name: B + desc: Antenna path selector + opts: *x-ant-opts + - bits: "10:8" + name: C + desc: Antenna path selector + opts: *x-ant-opts + - bits: "14:12" + name: D + desc: Antenna path selector + opts: *x-ant-opts +# + - addr: 0x53 + name: RX_CHEN + fields: + - bits: "0" + name: A + desc: FE Chan enabled + - bits: "1" + name: B + desc: FE Chan enabled + - bits: "2" + name: C + desc: FE Chan enabled + - bits: "3" + name: D + desc: FE Chan enabled +# + - addr: 0x54 + name: TX_CHEN + fields: + - bits: "0" + name: A + desc: FE Chan enabled + - bits: "1" + name: B + desc: FE Chan enabled + - bits: "2" + name: C + desc: FE Chan enabled + - bits: "3" + name: D + desc: FE Chan enabled +# + - addr: 0x55 + name: TX_2STAGE + fields: + - bits: "0" + name: A + desc: FE Chan 2nd stage enabled + - bits: "1" + name: B + desc: FE Chan 2nd stage enabled + - bits: "2" + name: C + desc: FE Chan 2nd stage enabled + - bits: "3" + name: D + desc: FE Chan 2nd stage enabled diff --git a/src/lib/device/ext_pciefe/ext_pciefe.c b/src/lib/device/ext_pciefe/ext_pciefe.c index 6e870d24..09bbf6c7 100644 --- a/src/lib/device/ext_pciefe/ext_pciefe.c +++ b/src/lib/device/ext_pciefe/ext_pciefe.c @@ -16,8 +16,8 @@ #include "def_pciefe_cmd.h" enum { - GPIO_SDA = GPIO0, // Alternatide mode - GPIO_SCL = GPIO1, // Alternatide mode + GPIO_SDA = GPIO0, // Alternative mode + GPIO_SCL = GPIO1, // Alternative mode GPIO_FATTN_0 = GPIO0, // Fast Attenuator interface GPIO_FATTN_1 = GPIO1, // Fast Attenuator interface @@ -43,7 +43,7 @@ enum { // GPIO14, GPIO15 -- DIRCD }; -// GPIO Translatos +// GPIO Translators // DIRxx 0: PLD -> USDR (usdr in) // DIRxx 1: USDR -> PLD (usdr out) @@ -741,3 +741,11 @@ int board_ext_pciefe_best_path_set(board_ext_pciefe_t* ob, return -EINVAL; } + +int board_ext_pciefe_set_dac(board_ext_pciefe_t* brd, unsigned value) +{ + unsigned i2ca_dac = MAKE_LSOP_I2C_ADDR(LSOP_I2C_INSTANCE(brd->i2c_loc), LSOP_I2C_BUSNO(brd->i2c_loc), I2C_ADDR_DAC); + + brd->dac = value; + return dac80501_dac_set(brd->dev, brd->subdev, i2ca_dac, brd->dac); +} diff --git a/src/lib/device/ext_pciefe/ext_pciefe.h b/src/lib/device/ext_pciefe/ext_pciefe.h index f60ba973..945f5ed7 100644 --- a/src/lib/device/ext_pciefe/ext_pciefe.h +++ b/src/lib/device/ext_pciefe/ext_pciefe.h @@ -101,5 +101,7 @@ int board_ext_pciefe_best_path_set(board_ext_pciefe_t* ob, unsigned txlo, unsigned txbw); +int board_ext_pciefe_set_dac(board_ext_pciefe_t* brd, unsigned value); + #endif diff --git a/src/lib/device/ext_pciefe/pciefe.yaml b/src/lib/device/ext_pciefe/pciefe.yaml index b6b8194c..08d07bfb 100644 --- a/src/lib/device/ext_pciefe/pciefe.yaml +++ b/src/lib/device/ext_pciefe/pciefe.yaml @@ -83,7 +83,7 @@ pages: desc: Activate TX - RX loopback, while TX is still active; Inverted - bits: "2:0" name: DUPL_PATH - desc: Dumplexer path + desc: Duplexer path opts: 0b001: RF1_BAND5 0b010: RF2_BAND8 @@ -209,7 +209,7 @@ pages: desc: Activate TX - RX loopback, while TX is still active; Inverted - bits: "2:0" name: ADUPL_PATH - desc: Dumplexer path + desc: Duplexer path opts: 0b000: ARF1_BAND2 0b100: ARF2_BAND7 @@ -244,7 +244,7 @@ pages: desc: Enable RX LNA 1 - bits: "9:7" name: DUPL_PATH - desc: Dumplexer path + desc: Duplexer path opts: 0b001: RF1_BAND5 0b010: RF2_BAND8 diff --git a/src/lib/device/ext_pciefe/pciefe_cmd.yaml b/src/lib/device/ext_pciefe/pciefe_cmd.yaml index 6a5d336b..13257a6e 100644 --- a/src/lib/device/ext_pciefe/pciefe_cmd.yaml +++ b/src/lib/device/ext_pciefe/pciefe_cmd.yaml @@ -51,7 +51,7 @@ pages: fields: - bits: "2:0" name: DUPLSEL - desc: Dumplexer path + desc: Duplexer path opts: 0: TRX_BYPASS 1: TRX_BAND2 diff --git a/src/lib/device/ext_simplesync/ext_simplesync.c b/src/lib/device/ext_simplesync/ext_simplesync.c index 87d3485a..8b0e1e49 100644 --- a/src/lib/device/ext_simplesync/ext_simplesync.c +++ b/src/lib/device/ext_simplesync/ext_simplesync.c @@ -41,6 +41,16 @@ enum { I2C_ADDR_LMK = 0x65, }; +static int simplesync_pd_low_chs(board_ext_simplesync_t* ob) +{ + int res = 0; + res = res ? res : lmk05318_disable_port(&ob->lmk, 0); + res = res ? res : lmk05318_disable_port(&ob->lmk, 1); + res = res ? res : lmk05318_disable_port(&ob->lmk, 2); + res = res ? res : lmk05318_disable_port(&ob->lmk, 3); + return res; +} + int board_ext_simplesync_init(lldev_t dev, unsigned subdev, unsigned gpio_base, @@ -77,7 +87,30 @@ int board_ext_simplesync_init(lldev_t dev, // Wait for power up usleep(50000); - res = lmk05318_create(dev, subdev, i2ca, 0, &ob->lmk); + lmk05318_out_config_t lmk_out[4]; + + lmk05318_port_request(&lmk_out[0], 4, 25000000, false, LVCMOS_P_N); + lmk05318_port_request(&lmk_out[1], 5, 25000000, false, LVCMOS_P_N); + lmk05318_port_request(&lmk_out[2], 6, 25000000, false, LVCMOS_P_N); + lmk05318_port_request(&lmk_out[3], 7, 25000000, false, LVCMOS_P_N); + + lmk05318_set_port_affinity(&lmk_out[0], AFF_APLL1); + lmk05318_set_port_affinity(&lmk_out[1], AFF_APLL1); + lmk05318_set_port_affinity(&lmk_out[2], AFF_APLL1); + lmk05318_set_port_affinity(&lmk_out[3], AFF_APLL1); + + res = lmk05318_create(dev, subdev, i2ca, 26000000, XO_CMOS, false, NULL, lmk_out, SIZEOF_ARRAY(lmk_out), &ob->lmk, false /*dry_run*/); + if(res) + return res; + + res = simplesync_pd_low_chs(ob); //power down chs 0..3 + res = res ? res : lmk05318_wait_apll1_lock(&ob->lmk, 10000); + res = res ? res : lmk05318_wait_apll2_lock(&ob->lmk, 10000); + res = res ? res : lmk05318_sync(&ob->lmk); + + unsigned los_msk; + lmk05318_check_lock(&ob->lmk, &los_msk, false /*silent*/); //just to log state + if (res) return res; @@ -85,16 +118,40 @@ int board_ext_simplesync_init(lldev_t dev, return 0; } - +#define LO_FREQ_CUTOFF 3500000ul // VCO2_MIN / 7 / 256 = 3069196.43 Hz int simplesync_tune_lo(board_ext_simplesync_t* ob, uint32_t meas_lo) { - unsigned div = 255; - int res = lmk05318_tune_apll2(&ob->lmk, meas_lo, &div); + int res = 0; - for (unsigned p = 0; p < 4; p++) { - res = (res) ? res : lmk05318_set_out_div(&ob->lmk, p, div); - res = (res) ? res : lmk05318_set_out_mux(&ob->lmk, p, false, meas_lo < 1e6 ? OUT_OFF : LVDS); + if(meas_lo < LO_FREQ_CUTOFF) + { + res = simplesync_pd_low_chs(ob); //power down chs 0..3 + } + else + { + lmk05318_out_config_t lmk_out[4]; + + lmk05318_port_request(&lmk_out[0], 0, meas_lo, false, LVDS); + lmk05318_port_request(&lmk_out[1], 1, meas_lo, false, LVDS); + lmk05318_port_request(&lmk_out[2], 2, meas_lo, false, LVDS); + lmk05318_port_request(&lmk_out[3], 3, meas_lo, false, LVDS); + + lmk05318_set_port_affinity(&lmk_out[0], AFF_APLL2); + lmk05318_set_port_affinity(&lmk_out[1], AFF_APLL2); + lmk05318_set_port_affinity(&lmk_out[2], AFF_APLL2); + lmk05318_set_port_affinity(&lmk_out[3], AFF_APLL2); + + res = res ? res : lmk05318_solver(&ob->lmk, lmk_out, SIZEOF_ARRAY(lmk_out)); + res = res ? res : lmk05318_reg_wr_from_map(&ob->lmk, false /*dry_run*/); + if(res) + return res; + + res = res ? res : lmk05318_wait_apll2_lock(&ob->lmk, 10000); + res = res ? res : lmk05318_sync(&ob->lmk); + + unsigned los_msk; + lmk05318_check_lock(&ob->lmk, &los_msk, false /*silent*/); //just to log state } return res; diff --git a/src/lib/device/ext_xmass/CMakeLists.txt b/src/lib/device/ext_xmass/CMakeLists.txt new file mode 100644 index 00000000..f95299d3 --- /dev/null +++ b/src/lib/device/ext_xmass/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2023-2025 Wavelet Lab +# SPDX-License-Identifier: MIT + +set(BOARD_XMASS_LIB_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/ext_xmass.c +) + +set(HW_FILES ext_xmass_ctrl) +foreach(I ${HW_FILES}) + message(STATUS "Generating header for ${I}") + GENERATE_YAML_H(${CMAKE_CURRENT_SOURCE_DIR}/${I}.yaml ${CMAKE_CURRENT_BINARY_DIR}/def_${I}.h) + + list(APPEND USDR_DEPEND_TARGETS generate_${I}) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${I}.yaml DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/usdr/schema/) +endforeach() + +list(APPEND USDR_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) +list(APPEND USDR_LIBRARY_FILES ${BOARD_XMASS_LIB_FILES}) +set(USDR_LIBRARY_FILES ${USDR_LIBRARY_FILES} PARENT_SCOPE) +set(USDR_DEPEND_TARGETS ${USDR_DEPEND_TARGETS} PARENT_SCOPE) +set(USDR_INCLUDE_DIRS ${USDR_INCLUDE_DIRS} PARENT_SCOPE) + + + diff --git a/src/lib/device/ext_xmass/ext_xmass.c b/src/lib/device/ext_xmass/ext_xmass.c new file mode 100644 index 00000000..0755d031 --- /dev/null +++ b/src/lib/device/ext_xmass/ext_xmass.c @@ -0,0 +1,337 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "ext_xmass.h" +#include "../ipblks/gpio.h" + +#include +#include +#include + +#include "../hw/tca9555/tca9555.h" +#include "../hw/lmk05318/lmk05318.h" +#include "../hw/tmp114/tmp114.h" +#include "../hw/at24/at24.h" + +#include "def_ext_xmass_ctrl.h" + + +enum { + XMASS_GPIO_RF_CAL_DST_SEL = 2, // 0 - RF_CAL_EXT (general rx port) / 1 - RF_CAL_INT (LNA3 port) + XMASS_GPIO_RF_CAL_SRC_SEL = 3, // 0 - RF_LO_SRC (from LMK) / 1 - RF_NOISE_SRC (from NOISE GEN) + + XMASS_GPIO_RF_CAL_SW = 9, // 0 - Use RF cal source as FB, 1 - Use XSDR TX as FB + XMASS_GPIO_RF_LB_SW = 10, // 0 - Normal operation, 1 - use LB path to XSDR RX + XMASS_GPIO_RF_NOISE_EN = 11, // Enable 14V generator for Zener +}; + +// XRA1201IL24TR-F (0x28 I2C) +// Port0: +// 0 GPIO_BDISTRIB : Enable OUT_REF_B[0:3] and OUT_SYREF_B[0:3] for 4 board sync +// 1 GPIO_BLOCAL : Enable local PPS and REF distribution +// 2 RF_CAL_DST_SEL : 0 - RF_CAL_EXT (general rx port) / 1 - RF_CAL_INT (LNA3 port) +// 3 RF_CAL_SRC_SEL : 0 - RF_LO_SRC (from LMK) / 1 - RF_NOISE_SRC (from NOISE GEN) +// 4 GPS_PWREN : Enable GPS module + DC-bias +// 5 GPIO_SYNC : LMK05318B GPIO0/SYNC_N +// 6 SYSREF_1PPS_SEL : 0 - LMK_1PPS, 1 - From SDR_A +// 7 EN_LMX : Enable LMK05318B +// +// Port1: +// 8 RF_EN : Enables Power Amplifiers +// 9 RF_CAL_SW : 0 - Use RF cal source as FB, 1 - Use XSDR TX as FB +// 10 RF_LB_SW : 0 - Normal operation, 1 - use LB path to XSDR RX +// 11 RF_NOISE_EN : Enable 12V generator for Zener +// 12 SYSREF_GPSRX_SEL : TX_SYREF_MUX => demuliplexed to ( == 0) ? CLK_SYSREF_OUT : GPS_RX +// 13 M3_RTS +// 14 M2_RTS +// 15 M1_RTS +// +// LMK05318 (0x65 I2C) +// OUT[0:3] unused +// OUT4 RF / LVDS +// OUT5 aux_p + aux_n +// OUT6 REF / LVCMOS +// OUT7 1PPS / LVCMOS +// TMP114NAIYMTR 1001111 +// TMP114NBIYMTR 1001110 +// +// M2_Connector (master) +// LED1/SDA CLK_SDA +// LED1/SCL CLK_SCL +// GPIO0/SDA M1_CTS +// GPIO1/SCL M2_CTS +// GPIO3/RX TX_SYREF_MUX +// GPIO2/PPS 1PPS_IN +// GPIO4/TX GPS_TX +// GPIO5 M3_RXD +// GPIO6 M3_TXD +// GPIO7 M3_CTS +// GPIO8 M1_TXD +// GPIO9 M1_RXD +// GPIO10 M2_TXD +// GPIO11 M2_RXD +// +// M2_Connector (slaves) +// GPIO2/PPS 1PPS_IN +// GPIO8 Mx_RXD +// GPIO9 Mx_RTS +// GPIO10 Mx_CTS +// GPIO11 Mx_TXD + + +enum { + I2C_ADDR_LMK = 0x65, + I2C_ADDR_XRA1201 = 0x14, + I2C_GPS_RX = 0x20, + I2C_GPS_TX = 0x21, + + I2C_TMP114NB = 0x4E, + I2C_TMP114NA = 0x4F, + + I2C_DEV_AT24_MEM = 0x50, + I2C_DEV_AT24_SEC = 0x58, +}; + +static int _board_xmass_fill_lmk05318(board_xmass_t* ob, lmk05318_out_config_t lmk05318_outs_cfg[8]) +{ + unsigned cfreq = (ob->calfreq < 3.1e6) ? 0 : ob->calfreq; + lmk05318_port_request(&lmk05318_outs_cfg[0], 0, 0, false, OUT_OFF); + lmk05318_port_request(&lmk05318_outs_cfg[1], 1, 0, false, OUT_OFF); + lmk05318_port_request(&lmk05318_outs_cfg[2], 2, 0, false, OUT_OFF); + lmk05318_port_request(&lmk05318_outs_cfg[3], 3, 0, false, OUT_OFF); + lmk05318_port_request(&lmk05318_outs_cfg[4], 4, cfreq, false, cfreq == 0 ? OUT_OFF : LVDS); + lmk05318_port_request(&lmk05318_outs_cfg[5], 5, 0, false, OUT_OFF); + lmk05318_port_request(&lmk05318_outs_cfg[6], 6, ob->refclk, false, LVCMOS_P_N); + // lmk05318_port_request(&lmk05318_outs_cfg[7], 7, 1, false, LVCMOS_P_N); + lmk05318_port_request(&lmk05318_outs_cfg[7], 7, 0, false, OUT_OFF); + + lmk05318_set_port_affinity(&lmk05318_outs_cfg[4], AFF_APLL2); + lmk05318_set_port_affinity(&lmk05318_outs_cfg[6], AFF_APLL1); + lmk05318_set_port_affinity(&lmk05318_outs_cfg[7], AFF_APLL1); + return 0; +} + +int board_xmass_init(lldev_t dev, + unsigned subdev, + unsigned gpio_base, + const char* compat, + unsigned int i2c_loc, + board_xmass_t* ob) +{ + int res = 0; + + // This breakout is compatible with M.2 key A/E or A+E boards + if ((strcmp(compat, "m2a+e") != 0) && (strcmp(compat, "m2e") != 0) && (strcmp(compat, "m2a") != 0)) + return -ENODEV; + + + // Configure external SDA/SCL + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO0, GPIO_CFG_IN); + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO1, GPIO_CFG_IN); + + // Configure 1PPS input + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO2, GPIO_CFG_ALT0); + + // Configure SYSREF_GEN + res = (res) ? res : gpio_config(dev, subdev, gpio_base, GPIO3, GPIO_CFG_ALT0); + + unsigned i2c_lmka = MAKE_LSOP_I2C_ADDR(LSOP_I2C_INSTANCE(i2c_loc), LSOP_I2C_BUSNO(i2c_loc), I2C_ADDR_LMK); + unsigned i2c_xraa = MAKE_LSOP_I2C_ADDR(LSOP_I2C_INSTANCE(i2c_loc), LSOP_I2C_BUSNO(i2c_loc), I2C_ADDR_XRA1201); + unsigned i2c_tmpa = MAKE_LSOP_I2C_ADDR(LSOP_I2C_INSTANCE(i2c_loc), LSOP_I2C_BUSNO(i2c_loc), I2C_TMP114NA); + unsigned i2c_tmpb = MAKE_LSOP_I2C_ADDR(LSOP_I2C_INSTANCE(i2c_loc), LSOP_I2C_BUSNO(i2c_loc), I2C_TMP114NB); + unsigned i2c_at24 = MAKE_LSOP_I2C_ADDR(LSOP_I2C_INSTANCE(i2c_loc), LSOP_I2C_BUSNO(i2c_loc), I2C_DEV_AT24_SEC); + uint16_t val; + uint16_t out_msk = 0xe000; + + res = (res) ? res : tca9555_reg16_get(dev, subdev, i2c_xraa, TCA9555_CFG0, &val); + res = (res) ? res : tca9555_reg16_set(dev, subdev, i2c_xraa, TCA9555_CFG0, out_msk); + res = (res) ? res : tca9555_reg16_get(dev, subdev, i2c_xraa, TCA9555_CFG0, &val); + + if (res) + return res; + + if (val != out_msk) { + USDR_LOG("XMSS", USDR_LOG_INFO, "GPIO expander initialization failed! Reported mask %04x != %04x\n", val, out_msk); + return -ENODEV; + } + + // En LMK to ckeck it + res = (res) ? res : tca9555_reg16_set(dev, subdev, i2c_xraa, TCA9555_OUT0, (3) | (1 << 4) | (1 << 8) | (1 << 7) | (1 << 6) | (1 << 5)); + + usleep(250000); + + ob->refclk = 25e6; + ob->calfreq = 444e6; + + //LMK05318 init start + lmk05318_dpll_settings_t dpll; + memset(&dpll, 0, sizeof(dpll)); + dpll.enabled = false; + dpll.en[LMK05318_PRIREF] = true; + dpll.fref[LMK05318_PRIREF] = 1; + dpll.type[LMK05318_PRIREF] = DPLL_REF_TYPE_DIFF_NOTERM; + dpll.dc_mode[LMK05318_PRIREF] = DPLL_REF_DC_COUPLED_INT; + dpll.buf_mode[LMK05318_PRIREF] = DPLL_REF_AC_BUF_HYST50_DC_EN; + + lmk05318_out_config_t lmk05318_outs_cfg[8]; + res = res ? res : _board_xmass_fill_lmk05318(ob, lmk05318_outs_cfg); + res = res ? res : lmk05318_create(dev, subdev, i2c_lmka, 26000000, XO_AC_DIFF_EXT, false, &dpll, lmk05318_outs_cfg, 8, &ob->lmk, false); + if (res) { + USDR_LOG("XMSS", USDR_LOG_ERROR, "Unable to initialize XMASS\n"); + } + + //wait for PRIREF/SECREF validation + res = lmk05318_wait_dpll_ref_stat(&ob->lmk, 4*60000000); //60s - searching for satellites may take a lot of time if GPS in just turned on + if(res) + { + USDR_LOG("XMSS", USDR_LOG_ERROR, "LMK03518 DPLL input reference freqs are not validated during specified timeout"); + return res; + } + + //wait for lock + //APLL1/DPLL + res = lmk05318_wait_apll1_lock(&ob->lmk, 100000); + res = res ? res : lmk05318_wait_apll2_lock(&ob->lmk, 100000); + + unsigned los_msk; + lmk05318_check_lock(&ob->lmk, &los_msk, false /*silent*/); //just to log state + + if(res) + { + USDR_LOG("XMSS", USDR_LOG_ERROR, "LMK03518 PLLs not locked during specified timeout"); + return res; + } + + //sync to make APLL1/APLL2 & out channels in-phase + //res = lmk05318_sync(&ob->lmk); + //if(res) + // return res; + + res = (res) ? res : tca9555_reg16_set(dev, subdev, i2c_xraa, TCA9555_OUT0, (3) | (1 << 4) | (1 << 8) | (1 << 7) | (1 << 6) | (0 << 5)); + res = (res) ? res : tca9555_reg16_set(dev, subdev, i2c_xraa, TCA9555_OUT0, (3) | (1 << 4) | (1 << 8) | (1 << 7) | (1 << 6) | (1 << 5)); + USDR_LOG("XMSS", USDR_LOG_INFO, "LMK03518 outputs synced"); + + ob->i2c_xraa = i2c_xraa; + + int ida, idb, temp; + res = res ? res : tmp114_devid_get(dev, subdev, i2c_tmpa, &ida); + if (res == 0 && ida == 0x1114) { + res = res ? res : tmp114_temp_get(dev, subdev, i2c_tmpa, &temp); + USDR_LOG("XMSS", USDR_LOG_INFO, "Rev.1a: TempA = %.2fC\n", temp / 256.0); + } + res = res ? res : tmp114_devid_get(dev, subdev, i2c_tmpb, &idb); + if (res == 0 && idb == 0x1114) { + res = res ? res : tmp114_temp_get(dev, subdev, i2c_tmpb, &temp); + + USDR_LOG("XMSS", USDR_LOG_INFO, "Rev.1a: TempB = %.2fC\n", temp / 256.0); + } + + if (res == 0 && (ida == 0x1114 || idb == 0x1114)) { + uint8_t s[16] = { 0, }; + res = res ? res : at24_saddr_mem_get(dev, subdev, i2c_at24, AT24_SECURE_SERIAL_OFF, 16, s); + USDR_LL_LOG(dev, "XMSS", USDR_LOG_WARNING, "AT24_SERIAL: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]); + } + + //res = (res) ? res : tca9555_reg16_set(dev, subdev, i2c_xraa, TCA9555_OUT0, (3) | (1 << 4) | (1 << 8) | (1 << 7) | (1 << 5) | (1 << 10)); + return res; +} + + + +int board_xmass_ctrl_cmd_wr(board_xmass_t* ob, uint32_t addr, uint32_t reg) +{ + int res; + + switch (addr) { + case P0: + case P1: + res = tca9555_reg8_set(ob->lmk.dev, ob->lmk.subdev, ob->i2c_xraa, TCA9555_OUT0 + addr - P0, reg); + break; + default: + return -EINVAL; + } + + // TODO update internal state!!! + return res; +} + +int board_xmass_ctrl_cmd_rd(board_xmass_t* ob, uint32_t addr, uint32_t* preg) +{ + uint8_t oval; + int res; + + switch (addr) { + case P0: + case P1: + res = tca9555_reg8_get(ob->lmk.dev, ob->lmk.subdev, ob->i2c_xraa, TCA9555_OUT0 + addr - P0, &oval); + break; + default: + return -EINVAL; + } + + *preg = oval; + return res; +} + +// 0 - off +// 1 - LO +// 2 - noise +// 3 - LO - LNA3 +// 4 - noise - LNA3 + +int board_xmass_sync_source(board_xmass_t* ob, unsigned sync_src) +{ + int res; + unsigned default_cmd = (3) | (1 << 4) | (1 << 8) | (1 << 7) | (1 << 6) | (1 << 5); + + switch (sync_src) { + case 0: + res = tca9555_reg16_set(ob->lmk.dev, ob->lmk.subdev, ob->i2c_xraa, TCA9555_OUT0, default_cmd); + break; + case 2: + res = tca9555_reg16_set(ob->lmk.dev, ob->lmk.subdev, ob->i2c_xraa, TCA9555_OUT0, default_cmd | (1 << XMASS_GPIO_RF_LB_SW) | (1 << XMASS_GPIO_RF_NOISE_EN) | (1 << XMASS_GPIO_RF_CAL_SRC_SEL)); + break; + case 1: + res = tca9555_reg16_set(ob->lmk.dev, ob->lmk.subdev, ob->i2c_xraa, TCA9555_OUT0, default_cmd | (1 << XMASS_GPIO_RF_LB_SW)); + break; + case 4: + res = tca9555_reg16_set(ob->lmk.dev, ob->lmk.subdev, ob->i2c_xraa, TCA9555_OUT0, default_cmd | (1 << XMASS_GPIO_RF_LB_SW) | (1 << XMASS_GPIO_RF_CAL_DST_SEL) | (1 << XMASS_GPIO_RF_NOISE_EN) | (1 << XMASS_GPIO_RF_CAL_SRC_SEL)); + break; + case 3: + res = tca9555_reg16_set(ob->lmk.dev, ob->lmk.subdev, ob->i2c_xraa, TCA9555_OUT0, default_cmd | (1 << XMASS_GPIO_RF_LB_SW) | (1 << XMASS_GPIO_RF_CAL_DST_SEL)); + break; + default: + return -EINVAL; + } + + return res; +} + + +int board_xmass_tune_cal_lo(board_xmass_t* ob, uint32_t callo) +{ + int res = 0; + unsigned los_msk; + lmk05318_out_config_t lmk05318_outs_cfg[8]; + + ob->calfreq = callo; + + res = res ? res : _board_xmass_fill_lmk05318(ob, lmk05318_outs_cfg); + res = res ? res : lmk05318_solver(&ob->lmk, lmk05318_outs_cfg, 8); + res = res ? res : lmk05318_reg_wr_from_map(&ob->lmk, false); + usleep(10000); + + res = res ? res : lmk05318_wait_apll2_lock(&ob->lmk, 10000); + //res = res ? res : lmk05318_sync(&ob->lmk); + + lmk05318_check_lock(&ob->lmk, &los_msk, false /*silent*/); //just to log state + + return res; +} + + + + + + diff --git a/src/lib/device/ext_xmass/ext_xmass.h b/src/lib/device/ext_xmass/ext_xmass.h new file mode 100644 index 00000000..1c45e08d --- /dev/null +++ b/src/lib/device/ext_xmass/ext_xmass.h @@ -0,0 +1,61 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef EXT_XMASS_H +#define EXT_XMASS_H + +#include +#include +#include +#include "../device.h" +#include "../device_vfs.h" + +#include "../hw/lmk05318/lmk05318.h" + +struct board_xmass { + lmk05318_state_t lmk; + + // Board configuration + bool distrib_ext; + bool distrib_local; + bool pwr_gps_en; + bool pwr_pa_en; + bool pwr_noise_en; + + bool cal_dst_lna3; // 0 -- CAL_TO_RX; 1 -- CAL_TO_LNA3 + bool cal_src_noise; // 0 -- CAL_CW; 1 -- CAL_NOISE + bool pps_from_sdra; // 0 -- PPS_LMK; 1 -- PPS_SDRA + bool loopback_en; // 0 -- Normal; 1 -- Loopback (TX & Cal) + bool loopback_mode_tx; // 0 -- RF_CAL_SOURCE; 1 -- TX_SOURCE + + unsigned gpio_base; + unsigned i2c_xraa; + + unsigned refclk; + unsigned calfreq; +}; +typedef struct board_xmass board_xmass_t; + +int board_xmass_init(lldev_t dev, + unsigned subdev, + unsigned gpio_base, + const char* compat, + unsigned int i2c_loc, + board_xmass_t* ob); + + +// High level contorl interface +int board_xmass_ctrl_cmd_wr(board_xmass_t* ob, uint32_t addr, uint32_t reg); +int board_xmass_ctrl_cmd_rd(board_xmass_t* ob, uint32_t addr, uint32_t* preg); + +int board_xmass_tune_cal_lo(board_xmass_t* ob, uint32_t callo); + +// 0 - off +// 1 - LO +// 2 - noise +// 3 - LO - LNA3 +// 4 - noise - LNA3 +int board_xmass_sync_source(board_xmass_t* ob, unsigned sync_src); + + +#endif diff --git a/src/lib/device/ext_xmass/ext_xmass_ctrl.yaml b/src/lib/device/ext_xmass/ext_xmass_ctrl.yaml new file mode 100644 index 00000000..a1f2745c --- /dev/null +++ b/src/lib/device/ext_xmass/ext_xmass_ctrl.yaml @@ -0,0 +1,71 @@ +# Copyright (c) 2023-2024 Wavelet Lab +# SPDX-License-Identifier: MIT + +# Register desc and visual map +name: XMASS_CTRL +desc: XMass Debug control GPIOs +revision: "0.0.1" +processors: [ c ] +bus: + type: VIRTUAL + usdr_path: /debug/hw/xmass_ctrl/0/reg + wr_mask: 0x80000000 +addr_width: 8 +data_width: 24 +# page_prefix: True +field_prefix: [ RegName ] +field_macros: True + +pages: + - name: I2C_GPIO + regs: +# + - addr: 0x00 + name: P0 + fields: + - bits: "0" + name: BDISTRIB + desc: Enable OUT_REF_B and OUT_SYREF_B0 for 4 sync boards + - bits: "1" + name: BLOCAL + desc: Enable local PPS and REF distribution + - bits: "2" + name: RF_CAL_DST_SEL + desc: 0 - RF_CAL_EXT (general rx port) / 1 - RF_CAL_INT (LNA3 port) + - bits: "3" + name: RF_CAL_SRC_SEL + desc: 0 - RF_LO_SRC (from LMK) / 1 - RF_NOISE_SRC (from NOISE GEN) + - bits: "4" + name: GPS_PWREN + desc: Enable GPS module + DC-bias + - bits: "5" + name: LMK_SYNCN + desc: Set LMK05318B SYNC_N port + - bits: "6" + name: SYSREF_1PPS_SEL + desc: 0 - LMK_1PPS, 1 - From SDR_A + - bits: "7" + name: EN_LMX + desc: Enable LMK05318B +# + - addr: 0x01 + name: P1 + fields: + - bits: "0" + name: RF_EN + desc: Enables Power Amplifiers + - bits: "1" + name: RF_CAL_SW + desc: 0 - Use RF cal source as FB, 1 - Use XSDR TX as FB + - bits: "2" + name: RF_LB_SW + desc: 0 - Normal operation, 1 - use LB path to XSDR RX + - bits: "3" + name: RF_NOISE_EN + desc: Enable 14V generator for Zener noise source + - bits: "4" + name: SYSREF_GPSRX_SEL + desc: 0 - TX_SYREF_MUX demuliplexing to CLK_SYSREF_OUT, 1 TX_SYREF_MUX to GPS_RX + - bits: "7:5" + name: RTS + desc: Interboard sync logic(Not used) diff --git a/src/lib/device/generic_usdr/generic_regs.h b/src/lib/device/generic_usdr/generic_regs.h index 3d163c7b..23ba458e 100644 --- a/src/lib/device/generic_usdr/generic_regs.h +++ b/src/lib/device/generic_usdr/generic_regs.h @@ -21,8 +21,6 @@ enum REGS_generic_usdr_r000 { M2PCI_REG_RD_FBUFFS = 4, M2PCI_REG_RD_FBURSTS = 5, M2PCI_REG_RD_RXSTAT = 6, - M2PCI_REG_RD_AVGIDC = 7, - M2PCI_REG_RD_AVGQDC = 8, M2PCI_REG_WR_PNTFY_CFG = 8, M2PCI_REG_WR_PNTFY_ACK = 9, @@ -100,17 +98,19 @@ enum RFE_BUS_ADDRS { // All I2C devices used accross the famaly enum { I2C_GENERAL_CALL = 0x00, - I2C_DEV_PMIC_FPGA = 0x60, //7'b110_0000; - I2C_DEV_EXTDAC = 0x48, //7'b100_1000; - I2C_DEV_TMP114B = 0x49, //7'b100_1001; + I2C_DEV_AT24_MEM = 0x50, //7'b101_0000; + I2C_DEV_AT24_SEC = 0x58, //7'b101_1000; + I2C_DEV_PMIC_FPGA = 0x60, //7'b110_0000; + I2C_DEV_EXTDAC = 0x48, //7'b100_1000; + I2C_DEV_TMP114B = 0x49, //7'b100_1001; I2C_DEV_TMP108A_A0_SDA = 0x4A, //7'b100_1010; I2C_DEV_DAC80501M_A0_GND = 0x48, //7'b100_1000; I2C_DEV_DAC80501M_A0_VDD = 0x49, //7'b100_1001; I2C_DEV_DAC80501M_A0_SDA = 0x4A, //7'b100_1001; I2C_DEV_DAC80501M_A0_SCL = 0x4B, //7'b100_1011; - I2C_DEV_TMP114NB = 0x4E, //7'b100_1110; - I2C_DEV_CLKGEN = 0x6A, //7'b110_1010; - I2C_DEV_DCDCBOOST = 0x75, //7'b111_0101; + I2C_DEV_TMP114NB = 0x4E, //7'b100_1110; + I2C_DEV_CLKGEN = 0x6A, //7'b110_1010; + I2C_DEV_DCDCBOOST = 0x75, //7'b111_0101; }; #define MAKE_I2C_LUT(a,b,c,d) \ diff --git a/src/lib/device/m2_dsdr/CMakeLists.txt b/src/lib/device/m2_dsdr/CMakeLists.txt index 3f18ff4a..5a1d8fe1 100644 --- a/src/lib/device/m2_dsdr/CMakeLists.txt +++ b/src/lib/device/m2_dsdr/CMakeLists.txt @@ -6,10 +6,11 @@ set(USDR_DSDR_LIB_FILES ${CMAKE_CURRENT_SOURCE_DIR}/dsdr_hiper.c ) -set(HW_FILES m2_dsdr_e m2_dsdr_p m2_dsdr_usr) +set(HW_FILES m2_dsdr_e m2_dsdr_e_v2 m2_dsdr_p m2_dsdr_usr) foreach(I ${HW_FILES}) message(STATUS "Generating header for ${I}") GENERATE_YAML_H(${CMAKE_CURRENT_SOURCE_DIR}/${I}.yaml ${CMAKE_CURRENT_BINARY_DIR}/def_${I}.h) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${I}.yaml DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/usdr/schema/) list(APPEND USDR_DEPEND_TARGETS generate_${I}) endforeach() @@ -20,8 +21,3 @@ list(APPEND USDR_LIBRARY_FILES ${USDR_DSDR_LIB_FILES}) set(USDR_LIBRARY_FILES ${USDR_LIBRARY_FILES} PARENT_SCOPE) set(USDR_DEPEND_TARGETS ${USDR_DEPEND_TARGETS} PARENT_SCOPE) set(USDR_INCLUDE_DIRS ${USDR_INCLUDE_DIRS} PARENT_SCOPE) - -# Extra yaml files -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/m2_dsdr.yaml DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/usdr/schema/) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/m2_dsdr_e.yaml DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/usdr/schema/) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/m2_dsdr_p.yaml DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/usdr/schema/) diff --git a/src/lib/device/m2_dsdr/dsdr_hiper.c b/src/lib/device/m2_dsdr/dsdr_hiper.c index 341631c0..db252680 100644 --- a/src/lib/device/m2_dsdr/dsdr_hiper.c +++ b/src/lib/device/m2_dsdr/dsdr_hiper.c @@ -13,6 +13,7 @@ #include "../hw/dac80501/dac80501.h" #include "../hw/tca6424a/tca6424a.h" #include "../hw/adf4002b/adf4002b.h" +#include "../hw/lp8758/lp8758.h" #include "../ipblks/uart.h" #include "../ipblks/spiext.h" @@ -69,12 +70,14 @@ enum i2c_idx_extra { I2C_TCA6424AR_U114 = MAKE_LSOP_I2C_ADDR(1, 0, TCA6424A_ADDR_L), I2C_TCA6424AR_U113 = MAKE_LSOP_I2C_ADDR(1, 0, TCA6424A_ADDR_H), I2C_TCA6424AR_U115 = MAKE_LSOP_I2C_ADDR(1, 1, TCA6424A_ADDR_H), + I2C_TCA6424AR_U110 = MAKE_LSOP_I2C_ADDR(1, 1, TCA6424A_ADDR_L), I2C_TEMP_U69 = MAKE_LSOP_I2C_ADDR(1, 0, I2C_DEV_TMP114NB), I2C_TEMP_U70 = MAKE_LSOP_I2C_ADDR(1, 1, I2C_DEV_TMP114NB), I2C_TEMP_U71 = MAKE_LSOP_I2C_ADDR(0, 1, I2C_DEV_TMP114NB), I2C_DAC = MAKE_LSOP_I2C_ADDR(1, 1, I2C_DEV_DAC80501M_A0_GND), + I2C_PMIC_LMS8= MAKE_LSOP_I2C_ADDR(1, 0, I2C_DEV_PMIC_FPGA), }; enum spi_idx { @@ -139,6 +142,17 @@ static const uint64_t s_filerbank_ranges[] = { }; +enum led_rtx_vals { + LED_TRX_RXO = 0, + LED_TRX_OFF = 1, + LED_TRX_TRX = 2, + LED_TRX_TXO = 3, +}; + +enum led_rx_cals { + LED_RX_ON = 0, + LED_RX_OFF = 1, +}; static int dsdr_hiper_debug_lms8001_u1_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -224,6 +238,9 @@ static int dsdr_hiper_senslms4temp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64 static int dsdr_hiper_senslms5temp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static const usdr_dev_param_func_t s_fe_parameters_v2[] = { + { "/debug/hw/dsdr_hiper_exp_v2/0/reg" , { dsdr_hiper_dsdr_hiper_exp_reg_set, dsdr_hiper_dsdr_hiper_exp_reg_get } }, +}; static const usdr_dev_param_func_t s_fe_parameters[] = { { "/debug/hw/lms8001/0/reg" , { dsdr_hiper_debug_lms8001_u1_reg_set, dsdr_hiper_debug_lms8001_u1_reg_get }}, @@ -255,6 +272,14 @@ static const usdr_dev_param_func_t s_fe_parameters[] = { { "/dm/sdr/0/smart_tune/int_mod", { dsdr_hiper_lms8001_smart_tune_int_mod_set, dsdr_hiper_lms8001_smart_tune_int_mod_get }}, { "/dm/sdr/0/smart_tune/enabled", { dsdr_hiper_lms8001_smart_tune_enabled_set, dsdr_hiper_lms8001_smart_tune_enabled_get }}, + { "/dm/sdr/0/rx/ab_l/frequency", { dsdr_hiper_lms8001_rabl_reg_set, dsdr_hiper_lms8001_rabl_reg_get }}, + { "/dm/sdr/0/rx/cd_l/frequency", { dsdr_hiper_lms8001_rcdl_reg_set, dsdr_hiper_lms8001_rcdl_reg_get }}, + { "/dm/sdr/0/rx/ab_h/frequency", { dsdr_hiper_lms8001_rabh_reg_set, dsdr_hiper_lms8001_rabh_reg_get }}, + { "/dm/sdr/0/rx/cd_h/frequency", { dsdr_hiper_lms8001_rcdh_reg_set, dsdr_hiper_lms8001_rcdh_reg_get }}, + { "/dm/sdr/0/tx/ab/frequency", { dsdr_hiper_lms8001_tab_reg_set, dsdr_hiper_lms8001_tab_reg_get }}, + { "/dm/sdr/0/tx/cd/frequency", { dsdr_hiper_lms8001_tcd_reg_set, dsdr_hiper_lms8001_tcd_reg_get }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/rx/ab_l/freqency", { dsdr_hiper_lms8001_rabl_reg_set, dsdr_hiper_lms8001_rabl_reg_get }}, { "/dm/sdr/0/rx/cd_l/freqency", { dsdr_hiper_lms8001_rcdl_reg_set, dsdr_hiper_lms8001_rcdl_reg_get }}, { "/dm/sdr/0/rx/ab_h/freqency", { dsdr_hiper_lms8001_rabh_reg_set, dsdr_hiper_lms8001_rabh_reg_get }}, @@ -320,7 +345,7 @@ int dsdr_hiper_dacvctcxo_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue static int _dsdr_hiper_senslms(dsdr_hiper_fe_t* fe, unsigned idx, uint64_t* ovalue) { - int res = 0, tmp256; + int res = 0, tmp256 = 0; res = res ? res : lms8001_temp_get(&fe->lms8[idx], &tmp256); res = res ? res : lms8001_temp_start(&fe->lms8[idx]); @@ -627,6 +652,15 @@ static int _hiper_update_expander_vreg(dsdr_hiper_fe_t* hiper, unsigned addr, un case 0x6: res = tca6424a_reg8_set(hiper->dev, hiper->subdev, I2C_TCA6424AR_U115, TCA6424_OUT0 + (addr - 0x4), data); break; + + case 0x7: + case 0x8: + case 0x9: + if (hiper->rev == HIPER_REV2) { + res = tca6424a_reg8_set(hiper->dev, hiper->subdev, I2C_TCA6424AR_U110, TCA6424_OUT0 + (addr - 0x7), data); + } + break; + default: return -EINVAL; } @@ -658,9 +692,10 @@ int dsdr_hiper_dsdr_hiper_exp_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_ case 0x20: res = res ? res : tca6424a_reg16_get(hiper->dev, hiper->subdev, I2C_TCA6424AR_U114, TCA6424_OUT0, &di16); res = res ? res : tca6424a_reg8_get(hiper->dev, hiper->subdev, I2C_TCA6424AR_U114, TCA6424_OUT0 + 2, &di8); + if (res) + return res; hiper->debug_exp_reg_last = di16 | (((unsigned)di8) << 16); - USDR_LOG("HIPR", USDR_LOG_WARNING, "HIPER_EXP RD %08x => %08x\n", addr, hiper->debug_exp_reg_last); return res; @@ -675,6 +710,16 @@ int dsdr_hiper_dsdr_hiper_exp_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_ case 0x26: res = tca6424a_reg8_get(hiper->dev, hiper->subdev, I2C_TCA6424AR_U115, TCA6424_OUT0 + (addr - 0x24), &di8); break; + + case 0x27: + case 0x28: + case 0x29: + if (hiper->rev == HIPER_REV2) { + res = tca6424a_reg8_get(hiper->dev, hiper->subdev, I2C_TCA6424AR_U110, TCA6424_OUT0 + (addr - 0x27), &di8); + } else { + di8 = 0xff; + } + break; default: return -EINVAL; } @@ -716,10 +761,22 @@ int dsdr_hiper_sens2temp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue { return dsdr_hiper_sens_get((dsdr_hiper_fe_t*)obj->object, 2, ovalue); } +int dsdr_hiper_fe_get_temp_max(dsdr_hiper_fe_t* dfe, uint64_t* temp_max) +{ + int64_t tmp = 0, tmax = 0; + int res = 0; + for (unsigned i = 0; i < 3; i++) { + res = res ? res : dsdr_hiper_sens_get(dfe, i, (uint64_t*)&tmp); + if (tmp > tmax) + tmax = tmp; + } + *temp_max = tmax; + return res; +} -static int dsdr_hiper_initialize_lms8(dsdr_hiper_fe_t* dfe, unsigned addr, lms8001_state_t* obj) +static int dsdr_hiper_initialize_lms8(dsdr_hiper_fe_t* dfe, unsigned addr, unsigned stepping, lms8001_state_t* obj) { uint32_t chipver = ~0; int res = 0; @@ -727,18 +784,21 @@ static int dsdr_hiper_initialize_lms8(dsdr_hiper_fe_t* dfe, unsigned addr, lms80 res = res ? res : lowlevel_spi_tr32(dfe->dev, dfe->subdev, addr, 0x000f0000, &chipver); USDR_LOG("HIPR", USDR_LOG_WARNING, "LMS8001.%08x: version %08x\n", addr, chipver); - res = res ? res : lms8001_create(dfe->dev, dfe->subdev, addr, obj); + res = res ? res : lms8001_create(dfe->dev, dfe->subdev, addr, stepping, obj); res = res ? res : lms8001_temp_start(obj); return res; } #include -int dsdr_hiper_fe_create(lldev_t dev, unsigned int spix_num, dsdr_hiper_fe_t* dfe) +int dsdr_hiper_fe_create(lldev_t dev, unsigned int spix_num, unsigned* plms8_mpw_mask, dsdr_hiper_fe_t* dfe) { int res = 0; device_t* base = lowlevel_get_device(dev); + uint8_t check_byte = 0; + dfe->dev = dev; dfe->subdev = 0; + dfe->rev = HIPER_REV0; USDR_LOG("HIPR", USDR_LOG_INFO, "Initializing HIPER front end...\n"); dfe->ref_int_osc = DEF_OSC_INT_FREQ; @@ -764,17 +824,30 @@ int dsdr_hiper_fe_create(lldev_t dev, unsigned int spix_num, dsdr_hiper_fe_t* df res = res ? res : tca6424a_reg8_set(dev, dfe->subdev, I2C_TCA6424AR_U114, TCA6424_CFG0 + 2, 0); res = res ? res : tca6424a_reg8_set(dev, dfe->subdev, I2C_TCA6424AR_U113, TCA6424_CFG0 + 2, 0); res = res ? res : tca6424a_reg8_set(dev, dfe->subdev, I2C_TCA6424AR_U115, TCA6424_CFG0 + 2, (1 << 0) | (1 << 2)); + + res = res ? res : tca6424a_reg16_set(dev, dfe->subdev, I2C_TCA6424AR_U110, TCA6424_CFG0, 0); + res = res ? res : tca6424a_reg8_set(dev, dfe->subdev, I2C_TCA6424AR_U110, TCA6424_CFG0 + 2, 0xf); + res = res ? res : tca6424a_reg8_get(dev, dfe->subdev, I2C_TCA6424AR_U110, TCA6424_CFG0 + 2, &check_byte); + if (res) { + USDR_LOG("HIPR", USDR_LOG_WARNING, "HIPER Expanders initialization failed: %d\n", res); + return res; + } + if (check_byte == 0xf) { + dfe->rev = HIPER_REV2; + res = res ? res : tca6424a_reg8_set(dev, dfe->subdev, I2C_TCA6424AR_U115, TCA6424_CFG0 + 2, 0); + + USDR_LOG("HIPR", USDR_LOG_WARNING, "Detected HIPER rev2 board\n"); + } + // TODO: sanity check - // Reset all LMS8001 - // res = res ? res : tca6424a_reg16_set(dev, dfe->subdev, I2C_TCA6424AR_U115, TCA6424_OUT0, 0xffff); - // res = res ? res : tca6424a_reg8_set(dev, dfe->subdev, I2C_TCA6424AR_U115, TCA6424_OUT0 + 2, 0xff); - // if (res) - // return res; + dfe->fe_ctrl_regs[IF_LNA- SW_RX_FILTER] = MAKE_M2_DSDR_E_IF_LNA(IF_LNA_CTRL_CHD_DISABLE, IF_LNA_CTRL_CHC_DISABLE, IF_LNA_CTRL_CHB_DISABLE, IF_LNA_CTRL_CHA_DISABLE); + dfe->fe_ctrl_regs[ENABLE - SW_RX_FILTER] = MAKE_M2_DSDR_E_ENABLE(1, 0, 1, 1, 1, 1, 1, 1); + dfe->fe_ctrl_regs[LMS8001_RESET - SW_RX_FILTER] = MAKE_M2_DSDR_E_LMS8001_RESET(1, 1, 1, 1, 1, 1, 1); + dfe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER] = MAKE_M2_DSDR_E_AUX_CTRL(0, 0, 0, 0, 1, 1, 1, 1); - dfe->fe_ctrl_regs[ENABLE - SW_RX_FILTER] = MAKE_M2_DSDR_E_ENABLE(1, 1, 1, 1); - dfe->fe_ctrl_regs[LMS8001_RESET - SW_RX_FILTER] = MAKE_M2_DSDR_E_LMS8001_RESET(1, 1, 1, 1, 1, 1); - dfe->fe_ctrl_regs[GPIO6 - SW_RX_FILTER] = MAKE_M2_DSDR_E_GPIO6(0, 0, 0, 0, 1, 0, 1, 0); + dfe->fe_ctrl_regs[LED_TRX_CTRL - SW_RX_FILTER] = MAKE_M2_DSDR_E_LED_TRX_CTRL(LED_TRX_OFF, LED_TRX_OFF, LED_TRX_OFF, LED_TRX_OFF); + dfe->fe_ctrl_regs[LEDRX_CH_CTRL - SW_RX_FILTER] = MAKE_M2_DSDR_E_LEDRX_CH_CTRL(LED_RX_OFF, LED_RX_OFF, LED_RX_OFF, LED_RX_OFF, 0, 0, 0, 0); for (unsigned k = 0; k < SIZEOF_ARRAY(dfe->fe_ctrl_regs); k++) { res = res ? res : _hiper_update_expander_vreg(dfe, k, dfe->fe_ctrl_regs[k]); } @@ -783,10 +856,39 @@ int dsdr_hiper_fe_create(lldev_t dev, unsigned int spix_num, dsdr_hiper_fe_t* df return res; } + if (dfe->rev == HIPER_REV2) { + uint16_t rev = 0xfefe; + unsigned lms8_ldo_v = 2400; + bool pg = false; + + res = res ? res : lp8758_get_rev(dev, dfe->subdev, I2C_PMIC_LMS8, &rev); + + res = res ? res : lp8758_vout_set(dev, dfe->subdev, I2C_PMIC_LMS8, 0, lms8_ldo_v); + res = res ? res : lp8758_vout_set(dev, dfe->subdev, I2C_PMIC_LMS8, 1, lms8_ldo_v); + res = res ? res : lp8758_vout_set(dev, dfe->subdev, I2C_PMIC_LMS8, 2, lms8_ldo_v); + res = res ? res : lp8758_vout_set(dev, dfe->subdev, I2C_PMIC_LMS8, 3, lms8_ldo_v); + res = res ? res : lp8758_vout_ctrl(dev, dfe->subdev, I2C_PMIC_LMS8, 0, true, false); + res = res ? res : lp8758_vout_ctrl(dev, dfe->subdev, I2C_PMIC_LMS8, 1, true, false); + res = res ? res : lp8758_vout_ctrl(dev, dfe->subdev, I2C_PMIC_LMS8, 2, true, false); + res = res ? res : lp8758_vout_ctrl(dev, dfe->subdev, I2C_PMIC_LMS8, 3, true, false); + + for (unsigned t = 0; t < 20; t++) { + res = res ? res : lp8758_check_pg(dev, dfe->subdev, I2C_PMIC_LMS8, 0xf, &pg); + if (pg) + break; + res = res ? res : usleep(5000); + } + + USDR_LOG("HIPR", USDR_LOG_WARNING, "HIPER Rev2 board, PMIC REV=%04x PG=%x\n", rev, pg); + } + // LMS8 + unsigned lms8_mask = (plms8_mpw_mask) ? *plms8_mpw_mask : (dfe->rev == HIPER_REV2) ? 0b001100 : 0; for (unsigned k = 0; k < 6; k++) { + opt_u64_set_null(&dfe->lms8_lo[k]); + uint32_t cfg = MAKE_SPIEXT_LSOPADR(MAKE_SPIEXT_CFG(LMS8_BCNTZ, k, LMS8_DIV), 0, spix_num); - res = res ? res : dsdr_hiper_initialize_lms8(dfe, cfg, &dfe->lms8[k]); + res = res ? res : dsdr_hiper_initialize_lms8(dfe, cfg, (lms8_mask >> k) & 1, &dfe->lms8[k]); } // ADF4002 (MUX -> GND -> DVDD readback as a sanity check) @@ -843,6 +945,14 @@ int dsdr_hiper_fe_create(lldev_t dev, unsigned int spix_num, dsdr_hiper_fe_t* df (void*)dfe, s_fe_parameters, SIZEOF_ARRAY(s_fe_parameters)); + + if (dfe->rev == HIPER_REV2) { + res = res ? res : usdr_vfs_obj_param_init_array_param(base, + (void*)dfe, + s_fe_parameters_v2, + SIZEOF_ARRAY(s_fe_parameters_v2)); + } + if (res) return res; @@ -881,15 +991,18 @@ int dsdr_hiper_fe_create(lldev_t dev, unsigned int spix_num, dsdr_hiper_fe_t* df for (unsigned i = 0; i < HIPER_MAX_HW_CHANS; i++) { fe_chan_config_t* cfg = &dfe->ucfg[i]; cfg->rx_ifamp_bp = 0; - cfg->rx_band = IFBAND_AUTO; + cfg->rx_band = IFBAND_RX_AUTO; cfg->rx_fb_sel = RX_FB_AUTO; cfg->rx_dsa = 0; cfg->tx_band = IFBAND_AUTO; cfg->ant_sel = ANT_RX_TRX; + cfg->rx_en = 0; + cfg->tx_en = 0; cfg->rx_freq = 0; cfg->rx_nco = 0; cfg->tx_freq = 0; cfg->tx_nco = 0; + cfg->pa_2stage_bypass = 0; cfg->lms8_lna_gain = 0; cfg->lms8_pa_gain = 0; cfg->lms8_rx_hlmix_gain = 15; @@ -930,8 +1043,8 @@ int dsdr_hiper_fe_destroy(dsdr_hiper_fe_t* dfe) { int res = 0; - dfe->fe_ctrl_regs[ENABLE - SW_RX_FILTER] = MAKE_M2_DSDR_E_ENABLE(0, 0, 0, 0); - dfe->fe_ctrl_regs[LMS8001_RESET - SW_RX_FILTER] = MAKE_M2_DSDR_E_LMS8001_RESET(0, 0, 0, 0, 0, 0); + dfe->fe_ctrl_regs[ENABLE - SW_RX_FILTER] = MAKE_M2_DSDR_E_ENABLE(0, 0, 0, 0, 0, 0, 0, 0); + dfe->fe_ctrl_regs[LMS8001_RESET - SW_RX_FILTER] = MAKE_M2_DSDR_E_LMS8001_RESET(0, 0, 0, 0, 0, 0, 0); res = res ? res : _hiper_update_expander_vreg(dfe, LMS8001_RESET - SW_RX_FILTER, dfe->fe_ctrl_regs[LMS8001_RESET - SW_RX_FILTER]); usleep(100); @@ -1096,6 +1209,13 @@ int dsdr_hiper_dsdr_hiper_usr_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_ hiper->ucfg[H_CHC].lms8_tx_hlmix_gain = GET_M2_DSDR_USR_TX_8KB_C(data); hiper->ucfg[H_CHD].lms8_tx_hlmix_gain = GET_M2_DSDR_USR_TX_8KB_D(data); break; + case PA_2ND_BP: + hiper->ucfg[H_CHA].pa_2stage_bypass = GET_M2_DSDR_USR_PA_2ND_BP_A(data); + hiper->ucfg[H_CHB].pa_2stage_bypass = GET_M2_DSDR_USR_PA_2ND_BP_B(data); + hiper->ucfg[H_CHC].pa_2stage_bypass = GET_M2_DSDR_USR_PA_2ND_BP_C(data); + hiper->ucfg[H_CHD].pa_2stage_bypass = GET_M2_DSDR_USR_PA_2ND_BP_D(data); + break; + default: return -EINVAL; } @@ -1192,6 +1312,11 @@ int dsdr_hiper_dsdr_hiper_usr_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_ hiper->ucfg[H_CHD].lms8_tx_hlmix_gain, hiper->ucfg[H_CHC].lms8_tx_hlmix_gain, hiper->ucfg[H_CHB].lms8_tx_hlmix_gain, hiper->ucfg[H_CHA].lms8_tx_hlmix_gain); break; + case PA_2ND_BP: + hiper->debug_usr_reg_last = MAKE_M2_DSDR_USR_PA_2ND_BP( + hiper->ucfg[H_CHD].pa_2stage_bypass, hiper->ucfg[H_CHC].pa_2stage_bypass, + hiper->ucfg[H_CHB].pa_2stage_bypass, hiper->ucfg[H_CHA].pa_2stage_bypass); + break; default: return -EINVAL; } @@ -1236,7 +1361,16 @@ static void _hiper_fbank_map(unsigned filsel, unsigned *bout, unsigned *bin) } } -static void _hiper_antenna_sw_map(unsigned antenna, bool rxen, bool txen, uint8_t* gpo_ctrl, unsigned* inswlb, unsigned *arx, unsigned *atx) +// Switch on RX path => ANT_RX external port / rfsw_rxtx / LB +enum rfsw_tddfdd_bits { + EXP_TDDFDD_SD = TDD_FDD_OPTS_REV0_LNA_TO_RX_____REV2_SHUTDOWN, // LB SW is on + EXP_TDDFDD_P1_LB_SW = TDD_FDD_OPTS_REV0_LNA_TO_RX_____REV2_LNA_TO_LB, // LB SW is on + EXP_TDDFDD_P2_TRX_SW = TDD_FDD_OPTS_REV0_LNA_TO_TDDSW__REV2_LNA_TO_TDDSW, + EXP_TDDFDD_P3_ANT_RX = TDD_FDD_OPTS_REV0_LNA_TO_TDDSW__REV2_LNA_TO_RX, +}; + +static void _hiper_antenna_sw_map(bool rev2, unsigned antenna, bool rxen, bool txen, uint8_t* gpo_ctrl, unsigned* inswlb, unsigned *arx, unsigned *atx, + uint8_t* exp_led_trx, uint8_t* exp_led_rx) { CHECK_CONSTANT_EQ(ANT_OPTS_RX_TO_RX_AND_TX_TO_TRX, ANT_RX_TRX); CHECK_CONSTANT_EQ(ANT_OPTS_RX_TO_TRX_AND_TX_TERM, ANT_TRX_TERM); @@ -1249,68 +1383,80 @@ static void _hiper_antenna_sw_map(unsigned antenna, bool rxen, bool txen, uint8_ SET_M2_DSDR_P_CHD_EN_TX(*gpo_ctrl, txen); SET_M2_DSDR_P_CHD_EN_VADJ(*gpo_ctrl, txen); SET_M2_DSDR_P_CHD_EN_RX(*gpo_ctrl, rxen); - SET_M2_DSDR_P_CHD_SW_HW_TDD_CTRL(*gpo_ctrl, 0); + SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, rev2 ? EXP_TDDFDD_P3_ANT_RX : 0); SET_M2_DSDR_P_CHD_SW_PA_ONOFF(*gpo_ctrl, 0); SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, 0); SET_M2_DSDR_P_CHD_SW_RXTX(*gpo_ctrl, 0); *inswlb = 0; *arx = rxen; *atx = txen; + *exp_led_trx = txen ? LED_TRX_TXO : LED_TRX_OFF; + *exp_led_rx = rxen ? LED_RX_ON : LED_RX_OFF; break; case ANT_TRX_TERM: SET_M2_DSDR_P_CHD_EN_TX(*gpo_ctrl, 0); SET_M2_DSDR_P_CHD_EN_VADJ(*gpo_ctrl, 0); SET_M2_DSDR_P_CHD_EN_RX(*gpo_ctrl, rxen); - SET_M2_DSDR_P_CHD_SW_HW_TDD_CTRL(*gpo_ctrl, 1); + SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, rev2 ? EXP_TDDFDD_P2_TRX_SW : 1); SET_M2_DSDR_P_CHD_SW_PA_ONOFF(*gpo_ctrl, 1); SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, 1); SET_M2_DSDR_P_CHD_SW_RXTX(*gpo_ctrl, 0); *inswlb = 0; *arx = rxen; *atx = 0; + *exp_led_trx = rxen ? LED_TRX_RXO : LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; break; case ANT_RX_TERM: SET_M2_DSDR_P_CHD_EN_TX(*gpo_ctrl, 0); SET_M2_DSDR_P_CHD_EN_VADJ(*gpo_ctrl, 0); SET_M2_DSDR_P_CHD_EN_RX(*gpo_ctrl, rxen); - SET_M2_DSDR_P_CHD_SW_HW_TDD_CTRL(*gpo_ctrl, 0); + SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, rev2 ? EXP_TDDFDD_P3_ANT_RX : 0); SET_M2_DSDR_P_CHD_SW_PA_ONOFF(*gpo_ctrl, 1); SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, 0); SET_M2_DSDR_P_CHD_SW_RXTX(*gpo_ctrl, 0); *inswlb = 0; *arx = rxen; *atx = 0; + *exp_led_trx = LED_TRX_OFF; + *exp_led_rx = rxen ? LED_RX_ON : LED_RX_OFF; break; case ANT_LOOPBACK: SET_M2_DSDR_P_CHD_EN_TX(*gpo_ctrl, txen); SET_M2_DSDR_P_CHD_EN_VADJ(*gpo_ctrl, txen); SET_M2_DSDR_P_CHD_EN_RX(*gpo_ctrl, rxen); - SET_M2_DSDR_P_CHD_SW_HW_TDD_CTRL(*gpo_ctrl, 0); + SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, rev2 ? EXP_TDDFDD_P1_LB_SW : 0); SET_M2_DSDR_P_CHD_SW_PA_ONOFF(*gpo_ctrl, 1); SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, 1); SET_M2_DSDR_P_CHD_SW_RXTX(*gpo_ctrl, 0); *inswlb = 1; *arx = rxen; *atx = txen; + *exp_led_trx = LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; break; case AND_HW_TDD: SET_M2_DSDR_P_CHD_EN_TX(*gpo_ctrl, txen); SET_M2_DSDR_P_CHD_EN_VADJ(*gpo_ctrl, txen); SET_M2_DSDR_P_CHD_EN_RX(*gpo_ctrl, rxen); - SET_M2_DSDR_P_CHD_SW_HW_TDD_CTRL(*gpo_ctrl, 1); + SET_M2_DSDR_P_CHD_SW_RX_TDDFDD(*gpo_ctrl, rev2 ? EXP_TDDFDD_P2_TRX_SW : 1); *inswlb = 0; *arx = rxen; *atx = txen; + *exp_led_trx = (rxen && txen) ? LED_TRX_TRX : txen ? LED_TRX_TXO : rxen ? LED_TRX_RXO : LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; break; default: *inswlb = 0; *arx = 0; *atx = 0; + *exp_led_trx = LED_TRX_OFF; + *exp_led_rx = LED_RX_OFF; break; } } @@ -1336,6 +1482,10 @@ int dsdr_hiper_update_fe_user(dsdr_hiper_fe_t* fe) unsigned ifband_tx_h[HIPER_MAX_HW_CHANS]; unsigned ifband_rx_h[HIPER_MAX_HW_CHANS]; + uint8_t exp_led_trx[HIPER_MAX_HW_CHANS]; + uint8_t exp_led_rx[HIPER_MAX_HW_CHANS]; + bool rev2 = fe->rev == HIPER_REV2; + for (unsigned i = 0; i < HIPER_MAX_HW_CHANS; i++) { unsigned rxen = fe->ucfg[i].rx_en; unsigned txen = fe->ucfg[i].tx_en; @@ -1343,8 +1493,8 @@ int dsdr_hiper_update_fe_user(dsdr_hiper_fe_t* fe) // RX filterbank _hiper_fbank_map(fe->ucfg[i].rx_fb_sel, &fbanksel_out[i], &fbanksel_in[i]); // SW_RX_FILTER_OUT_CHA_MUTE1 if not enabled? - // Antanna switch, RF PA/LNA switch, loopback switch - _hiper_antenna_sw_map(fe->ucfg[i].ant_sel, rxen, txen, &fe->fe_gpo_regs[CHA - REFCTRL + i], &lbrxtx[i], &act_rx[i], &act_tx[i]); + // Antenna switch, RF PA/LNA switch, loopback switch + _hiper_antenna_sw_map(rev2, fe->ucfg[i].ant_sel, rxen, txen, &fe->fe_gpo_regs[CHA - REFCTRL + i], &lbrxtx[i], &act_rx[i], &act_tx[i], &exp_led_trx[i], &exp_led_rx[i]); // Update RX DSA fe->fe_gpo_regs[ATT_RX_CHA - REFCTRL + i] = fe->ucfg[i].rx_dsa; @@ -1353,16 +1503,16 @@ int dsdr_hiper_update_fe_user(dsdr_hiper_fe_t* fe) ifband_tx_h[i] = fe->ucfg[i].tx_band & (~IFBAND_AUTO); // RX IF band sel - ifband_rx_h[i] = fe->ucfg[i].rx_band & (~IFBAND_AUTO); + ifband_rx_h[i] = fe->ucfg[i].rx_band & (~IFBAND_RX_AUTO); // RX IF amplifier config iflna[i] = act_rx[i] ? ( fe->ucfg[i].rx_ifamp_bp ? IF_LNA_OPTS_BYPASS : IF_LNA_OPTS_LNA) : IF_LNA_OPTS_Disable; }; - SET_M2_DSDR_E_GPIO6_ABSLNA_PA_CHA(fe->fe_ctrl_regs[GPIO6 - SW_RX_FILTER], lbrxtx[H_CHA]); - SET_M2_DSDR_E_GPIO6_ABSLNA_PA_CHB(fe->fe_ctrl_regs[GPIO6 - SW_RX_FILTER], lbrxtx[H_CHB]); - SET_M2_DSDR_E_GPIO6_ABSLNA_PA_CHC(fe->fe_ctrl_regs[GPIO6 - SW_RX_FILTER], lbrxtx[H_CHC]); - SET_M2_DSDR_E_GPIO6_ABSLNA_PA_CHD(fe->fe_ctrl_regs[GPIO6 - SW_RX_FILTER], lbrxtx[H_CHD]); + SET_M2_DSDR_E_AUX_CTRL_ABSLNA_PA_CHA(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], lbrxtx[H_CHA]); + SET_M2_DSDR_E_AUX_CTRL_ABSLNA_PA_CHB(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], lbrxtx[H_CHB]); + SET_M2_DSDR_E_AUX_CTRL_ABSLNA_PA_CHC(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], lbrxtx[H_CHC]); + SET_M2_DSDR_E_AUX_CTRL_ABSLNA_PA_CHD(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], lbrxtx[H_CHD]); fe->fe_ctrl_regs[SW_RX_FILTER - SW_RX_FILTER] = MAKE_M2_DSDR_E_SW_RX_FILTER( @@ -1391,6 +1541,54 @@ int dsdr_hiper_update_fe_user(dsdr_hiper_fe_t* fe) ifband_rx_h[H_CHB] ? 0 : 1, ifband_rx_h[H_CHA] ? 0 : 1); + if (rev2) { + unsigned sw_out_rx[4]; + unsigned sw_in_rx[4]; + for (unsigned i = 0; i < HIPER_MAX_HW_CHANS; i++) { + switch (ifband_rx_h[i]) { + case IFBAND_400_3500: + sw_in_rx[i] = R2RX_IN_OPTS_RX_LOW; + sw_out_rx[i] = R2RX_OUT_OPTS_RX_LOW; + break; + case IFBAND_2200_7200: + sw_in_rx[i] = R2RX_IN_OPTS_RX_HI; + sw_out_rx[i] = R2RX_OUT_OPTS_RX_HI; + break; + case IFBAND_R2RX_1580_2760: + sw_in_rx[i] = R2RX_IN_OPTS_RX_BYPASS; + sw_out_rx[i] = R2RX_OUT_OPTS_RX_BYPASS; + break; + default: + sw_in_rx[i] = R2RX_IN_OPTS_DISABLE; + sw_out_rx[i] = R2RX_OUT_OPTS_DISABLE; + break; + } + } + + SET_M2_DSDR_E_SW_IN_RX_L_CHD_R2A_V2(fe->fe_ctrl_regs[SW_IN - SW_RX_FILTER], (sw_out_rx[H_CHA] >> 1)); + SET_M2_DSDR_E_SW_IN_RX_L_CHC_R2A_V1(fe->fe_ctrl_regs[SW_IN - SW_RX_FILTER], (sw_out_rx[H_CHA] & 1)); + SET_M2_DSDR_E_SW_IN_RX_L_CHB_R2B_V2(fe->fe_ctrl_regs[SW_IN - SW_RX_FILTER], (sw_out_rx[H_CHB] >> 1)); + SET_M2_DSDR_E_SW_IN_RX_L_CHA_R2B_V1(fe->fe_ctrl_regs[SW_IN - SW_RX_FILTER], (sw_out_rx[H_CHB] & 1)); + + SET_M2_DSDR_E_SW_OUT_RX_H_CHD_R2C_V2(fe->fe_ctrl_regs[SW_OUT - SW_RX_FILTER], (sw_out_rx[H_CHC] >> 1)); + SET_M2_DSDR_E_SW_OUT_RX_H_CHC_R2C_V1(fe->fe_ctrl_regs[SW_OUT - SW_RX_FILTER], (sw_out_rx[H_CHC] & 1)); + SET_M2_DSDR_E_SW_OUT_RX_H_CHB_R2D_V2(fe->fe_ctrl_regs[SW_OUT - SW_RX_FILTER], (sw_out_rx[H_CHD] >> 1)); + SET_M2_DSDR_E_SW_OUT_RX_H_CHA_R2D_V1(fe->fe_ctrl_regs[SW_OUT - SW_RX_FILTER], (sw_out_rx[H_CHD] & 1)); + + SET_M2_DSDR_E_AUX_CTRL_PA_BYPASS_A(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], fe->ucfg[H_CHA].pa_2stage_bypass); + SET_M2_DSDR_E_AUX_CTRL_PA_BYPASS_B(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], fe->ucfg[H_CHB].pa_2stage_bypass); + SET_M2_DSDR_E_AUX_CTRL_PA_BYPASS_C(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], fe->ucfg[H_CHC].pa_2stage_bypass); + SET_M2_DSDR_E_AUX_CTRL_PA_BYPASS_D(fe->fe_ctrl_regs[AUX_CTRL - SW_RX_FILTER], fe->ucfg[H_CHD].pa_2stage_bypass); + + fe->fe_ctrl_regs[SW_IN_RX - SW_RX_FILTER] = MAKE_M2_DSDR_E_SW_IN_RX(sw_in_rx[H_CHD], sw_in_rx[H_CHC], sw_in_rx[H_CHB], sw_in_rx[H_CHA]); + + fe->fe_ctrl_regs[LED_TRX_CTRL - SW_RX_FILTER] = MAKE_M2_DSDR_E_LED_TRX_CTRL( + exp_led_trx[H_CHD], exp_led_trx[H_CHC], exp_led_trx[H_CHB], exp_led_trx[H_CHA]); + + fe->fe_ctrl_regs[LEDRX_CH_CTRL - SW_RX_FILTER] = MAKE_M2_DSDR_E_LEDRX_CH_CTRL( + exp_led_rx[H_CHD], exp_led_rx[H_CHC], exp_led_rx[H_CHB], exp_led_rx[H_CHA], 0, 0, 0, 0); + } + // Update registers for (unsigned i = 0; i < FE_GPO_REGS; i++) { @@ -1445,15 +1643,18 @@ void dsdr_hiper_fe_rx_filterbank_upd(dsdr_hiper_fe_t* def, unsigned chno) USDR_LOG("HIPR", USDR_LOG_WARNING, "RXFBabk[%d] = %d\n", chno, def->ucfg[chno].rx_fb_sel); } -static void dsdr_hiper_fe_rx_band_upd(dsdr_hiper_fe_t* def, unsigned chno, bool band_high) +static void dsdr_hiper_fe_rx_band_upd(dsdr_hiper_fe_t* def, unsigned chno, unsigned band) { - if (def->ucfg[chno].rx_band < BAND_OPTS_BAND_AUTO_L) + if (def->ucfg[chno].rx_band < RXBAND_OPTS_BAND_AUTO_L) return; - def->ucfg[chno].rx_band = (band_high) ? BAND_OPTS_BAND_AUTO_H : BAND_OPTS_BAND_AUTO_L; + //def->ucfg[chno].rx_band = (band & 3); + unsigned rx_band = (band & 3); USDR_LOG("HIPR", USDR_LOG_WARNING, "RXBand[%d] switched to %c (%d)\n", chno, - def->ucfg[chno].rx_band == BAND_OPTS_BAND_AUTO_H ? 'H' : 'L', - def->ucfg[chno].rx_band); + rx_band == RXBAND_OPTS_BAND_AUTO_H ? 'H' : + rx_band == RXBAND_OPTS_BAND_AUTO_L ? 'L' : + rx_band == RXBAND_OPTS_BAND_AUTO_BP ? 'B' : 'D', + rx_band); } static void dsdr_hiper_fe_tx_band_upd(dsdr_hiper_fe_t* def, unsigned chno, bool band_high) @@ -1470,22 +1671,29 @@ static void dsdr_hiper_fe_tx_band_upd(dsdr_hiper_fe_t* def, unsigned chno, bool -int dsdr_hiper_fe_rxlo_upd(dsdr_hiper_fe_t* def, unsigned chno, bool* p_swap_rxiq, bool* p_high_path) +int dsdr_hiper_fe_rxlo_upd(dsdr_hiper_fe_t* def, unsigned chno, bool* p_swap_rxiq, unsigned* p_path) { int res = 0; bool fLOh = (def->ucfg[chno].rx_freq < 4700e6); - bool high_path; + unsigned rxpath; - if (def->ucfg[chno].rx_band < BAND_OPTS_BAND_AUTO_L) { - high_path = (def->ucfg[chno].rx_band == BAND_OPTS_BAND_400_3500) ? false : true; + if (def->ucfg[chno].rx_band < RXBAND_OPTS_BAND_AUTO_L) { + //high_path = (def->ucfg[chno].rx_band == BAND_OPTS_BAND_400_3500) ? false : true; + rxpath = def->ucfg[chno].rx_band; } else { - high_path = (def->ucfg[chno].rx_freq < 2600e6) ? false : true; + rxpath = (def->ucfg[chno].rx_freq < 2600e6) ? RXBAND_OPTS_BAND_AUTO_L : RXBAND_OPTS_BAND_AUTO_H; + + // Bypass only available in rev2 + if ((def->rev == HIPER_REV2) && (def->ucfg[chno].rx_freq > 1580e6) && (def->ucfg[chno].rx_freq < 2760e6)) { + rxpath = RXBAND_OPTS_BAND_AUTO_BP; + } } uint64_t fIF = 2075e6; uint64_t fLO = (fLOh) ? def->ucfg[chno].rx_freq + fIF : def->ucfg[chno].rx_freq - fIF; + bool high_path = ((rxpath & 0x3) == RXBAND_OPTS_BAND_2200_7200); if (high_path == false) { fIF = 1975e6; fLO = def->ucfg[chno].rx_freq + fIF; @@ -1500,8 +1708,26 @@ int dsdr_hiper_fe_rxlo_upd(dsdr_hiper_fe_t* def, unsigned chno, bool* p_swap_rxi if (high_path) { unsigned zeroes[4] = {0, 0, 0, 0}; + opt_u64_set_null(&def->lms8_lo[idx_off]); res = res ? res : lms8001a_ch_enable(&def->lms8[idx_off], 0x0, zeroes, zeroes); res = res ? res : lms8001_ch_enable(&def->lms8[idx], chmsk); + } else if ((def->rev == HIPER_REV2) && ((rxpath & 0x3) == RXBAND_OPTS_BAND_1580_2760_BP)) { + unsigned pas[4] = {~0, ~0, ~0, ~0}; + unsigned lnas[4] = {~0, ~0, ~0, ~0}; + + res = res ? res : lms8001_ch_enable(&def->lms8[idx_off], 0x0); + res = res ? res : lms8001a_ch_enable(&def->lms8[idx], 0, lnas, pas); + + opt_u64_set_null(&def->lms8_lo[idx_off]); + opt_u64_set_null(&def->lms8_lo[idx]); + + def->ucfg[chno].rx_nco = def->ucfg[chno].rx_freq; + + *p_path = rxpath; + *p_swap_rxiq = 0; + + USDR_LOG("HIPR", USDR_LOG_WARNING, "CH[%d] RX_BYPASS Mode\n", chno); + return res; } else { unsigned pas[4] = {0, 0, 0, 0}; unsigned lnas[4] = {0, 0, 0, 0}; @@ -1509,15 +1735,24 @@ int dsdr_hiper_fe_rxlo_upd(dsdr_hiper_fe_t* def, unsigned chno, bool* p_swap_rxi convert_lms8_gains_to_loss(def, (chno & 1) ? chno - 0 : chno + 1, pas + 3, lnas + 3); convert_lms8_gains_to_loss(def, (chno & 1) ? chno - 1 : chno + 0, pas + 2, lnas + 2); + opt_u64_set_null(&def->lms8_lo[idx_off]); res = res ? res : lms8001_ch_enable(&def->lms8[idx_off], 0x0); res = res ? res : lms8001a_ch_enable(&def->lms8[idx], chmsk, lnas, pas); } - res = res ? res : dsdr_hiper_fe_lms8_set_lo(def, idx, fLO); + if (def->lms8_lo[idx].set && def->lms8_lo[idx].value == fLO) { + USDR_LOG("HIPR", USDR_LOG_INFO, "HIPER_LMS8_%s: CH[%d] LMS[%d] LO Setup skipped\n", + s_lms8_names[idx], chno, idx); + } else { + res = res ? res : dsdr_hiper_fe_lms8_set_lo(def, idx, fLO); + if (res == 0) { + opt_u64_set_val(&def->lms8_lo[idx], fLO); + } + } def->ucfg[chno].rx_nco = fIF; *p_swap_rxiq = (fLOh) ? 1 : 0; - *p_high_path = high_path; + *p_path = rxpath; USDR_LOG("HIPR", USDR_LOG_WARNING, "CH[%d] RX_NCO=%.3f LO_%c=%.3f SWAP_IQ=%d PATH=%s LMS8[%d]_MSK=%x\n", chno, def->ucfg[chno].rx_nco / 1.0e6, fLOh ? 'H' : 'L', fLO / 1.0e6, *p_swap_rxiq, high_path ? "HIGH" : "LOW", idx, chmsk); @@ -1537,7 +1772,7 @@ int dsdr_hiper_fe_txlo_upd(dsdr_hiper_fe_t* def, unsigned chno, bool* p_swap_txi high_path = (def->ucfg[chno].tx_freq < 3500e6) ? false : true; } - uint64_t fIF = (high_path) ? 1875e6 : def->ucfg[chno].tx_freq; + uint64_t fIF = 1875e6; // (high_path) ? 1875e6 : def->ucfg[chno].tx_freq; uint64_t fLO = (high_path) ? def->ucfg[chno].tx_freq + fIF : 0; unsigned idx = get_lms8_tx_idx(chno); @@ -1545,14 +1780,24 @@ int dsdr_hiper_fe_txlo_upd(dsdr_hiper_fe_t* def, unsigned chno, bool* p_swap_txi (def->ucfg[chno].tx_en << 3) | (def->ucfg[chno - 1].tx_en << 2) : (def->ucfg[chno + 1].tx_en << 3) | (def->ucfg[chno].tx_en << 2); - - res = res ? res : lms8001_core_enable(&def->lms8[idx], high_path); + res = res ? res : lms8001_core_enable(&def->lms8[idx], high_path, high_path, high_path); res = res ? res : lms8001_ch_enable(&def->lms8[idx], high_path ? chmsk : 0); if (fLO > 0) { - res = res ? res : dsdr_hiper_fe_lms8_set_lo(def, idx, fLO); + if (def->lms8_lo[idx].set && def->lms8_lo[idx].value == fLO) { + USDR_LOG("HIPR", USDR_LOG_INFO, "HIPER_LMS8_%s: CH[%d] LMS[%d] LO Setup skipped\n", + s_lms8_names[idx], chno, idx); + } else { + res = res ? res : dsdr_hiper_fe_lms8_set_lo(def, idx, fLO); + if (res == 0) { + opt_u64_set_val(&def->lms8_lo[idx], fLO); + } + } + def->ucfg[chno].tx_nco = fIF; + } else { + opt_u64_set_null(&def->lms8_lo[idx]); + def->ucfg[chno].tx_nco = def->ucfg[chno].tx_freq; } - def->ucfg[chno].tx_nco = fIF; *p_swap_txiq = high_path; *p_high_path = high_path; @@ -1565,7 +1810,7 @@ int dsdr_hiper_fe_txlo_upd(dsdr_hiper_fe_t* def, unsigned chno, bool* p_swap_txi int dsdr_hiper_fe_rx_freq_set(dsdr_hiper_fe_t* def, unsigned chno, uint64_t freq, uint64_t* ncotune, bool* p_swap_rxiq) { - bool high_band = false; + unsigned band = 0; int res; if (chno >= HIPER_MAX_HW_CHANS) @@ -1576,11 +1821,11 @@ int dsdr_hiper_fe_rx_freq_set(dsdr_hiper_fe_t* def, unsigned chno, uint64_t freq def->ucfg[chno].rx_freq = freq; dsdr_hiper_fe_rx_filterbank_upd(def, chno); - res = dsdr_hiper_fe_rxlo_upd(def, chno, p_swap_rxiq, &high_band); + res = dsdr_hiper_fe_rxlo_upd(def, chno, p_swap_rxiq, &band); if (res) return res; - dsdr_hiper_fe_rx_band_upd(def, chno, high_band); + dsdr_hiper_fe_rx_band_upd(def, chno, band); *ncotune = def->ucfg[chno].rx_nco; return dsdr_hiper_update_fe_user(def); @@ -1695,6 +1940,8 @@ int dsdr_hiper_fe_tx_gain_set(dsdr_hiper_fe_t* def, unsigned chno, unsigned gain if (!def->ucfg[chno].tx_en) return 0; + // TODO check bypass path + unsigned lms8pa_gain = (gain >= RX_LMS8B_MIX_LOSS) ? RX_LMS8B_MIX_LOSS : gain; def->ucfg[chno].lms8_tx_hlmix_gain = lms8pa_gain; if (actual_gain) { @@ -1705,3 +1952,10 @@ int dsdr_hiper_fe_tx_gain_set(dsdr_hiper_fe_t* def, unsigned chno, unsigned gain chno, gain, lms8pa_gain); return dsdr_hiper_lms8001b_gain_update(def, chno, false); } + + +int dsdr_hiper_fe_set_dac(dsdr_hiper_fe_t* def, unsigned value) +{ + USDR_LOG("HIPR", USDR_LOG_WARNING, "DAC set to: %d\n", value); + return dac80501_dac_set(def->dev, def->subdev, I2C_DAC, value); +} diff --git a/src/lib/device/m2_dsdr/dsdr_hiper.h b/src/lib/device/m2_dsdr/dsdr_hiper.h index 040863f6..16ac083a 100644 --- a/src/lib/device/m2_dsdr/dsdr_hiper.h +++ b/src/lib/device/m2_dsdr/dsdr_hiper.h @@ -3,6 +3,7 @@ #include "../device.h" #include "../hw/lms8001/lms8001.h" +#include "../dev_param.h" #define HIPER_MAX_HW_CHANS 4 @@ -29,8 +30,11 @@ enum antenna_cfg { enum if_band { IFBAND_400_3500, IFBAND_2200_7200, + IFBAND_R2RX_1580_2760, + IFBAND_R2RX_OFF, IFBAND_AUTO = 2, + IFBAND_RX_AUTO = 4, }; struct fe_chan_config { @@ -52,6 +56,8 @@ struct fe_chan_config { uint64_t tx_freq; uint64_t tx_nco; + uint8_t pa_2stage_bypass; // For Rev.2 PA second stage bypass + uint8_t lms8_pa_gain; uint8_t lms8_lna_gain; uint8_t lms8_rx_hlmix_gain; @@ -60,12 +66,19 @@ struct fe_chan_config { typedef struct fe_chan_config fe_chan_config_t; #define FE_GPO_REGS 9 -#define FE_CTRL_REGS 7 +#define FE_CTRL_REGS 10 + +enum { + HIPER_REV0, + HIPER_REV2, +}; struct dsdr_hiper_fe { lldev_t dev; subdev_t subdev; + unsigned rev; + lms8001_state_t lms8[6]; uint64_t lo_lms8_freq[6]; @@ -95,15 +108,20 @@ struct dsdr_hiper_fe { uint32_t lms8st_int_mod; uint32_t lms8st_enabled; + // Cached LO values + opt_u64_t lms8_lo[6]; + // High level control fe_chan_config_t ucfg[HIPER_MAX_HW_CHANS]; }; typedef struct dsdr_hiper_fe dsdr_hiper_fe_t; -int dsdr_hiper_fe_create(lldev_t dev, unsigned spix_num, dsdr_hiper_fe_t* dfe); +int dsdr_hiper_fe_create(lldev_t dev, unsigned spix_num, unsigned int *lms8_mpw_mask, dsdr_hiper_fe_t* dfe); int dsdr_hiper_fe_destroy(dsdr_hiper_fe_t* dfe); +int dsdr_hiper_fe_get_temp_max(dsdr_hiper_fe_t* dfe, uint64_t* temp_max); + int dsdr_hiper_fe_rx_freq_set(dsdr_hiper_fe_t* def, unsigned chno, uint64_t freq, uint64_t* ncotune, bool *p_swap_rxiq); int dsdr_hiper_fe_tx_freq_set(dsdr_hiper_fe_t* def, unsigned chno, uint64_t freq, uint64_t* ncotune, bool* p_swap_txiq); @@ -115,6 +133,7 @@ int dsdr_hiper_fe_tx_gain_set(dsdr_hiper_fe_t* def, unsigned chno, unsigned gain int dsdr_hiper_fe_rx_chan_en(dsdr_hiper_fe_t* def, unsigned ch_fe_mask_rx); int dsdr_hiper_fe_tx_chan_en(dsdr_hiper_fe_t* def, unsigned ch_fe_mask_tx); +int dsdr_hiper_fe_set_dac(dsdr_hiper_fe_t* def, unsigned value); #endif diff --git a/src/lib/device/m2_dsdr/m2_dsdr.c b/src/lib/device/m2_dsdr/m2_dsdr.c index 9b5a8d16..d7a3a519 100644 --- a/src/lib/device/m2_dsdr/m2_dsdr.c +++ b/src/lib/device/m2_dsdr/m2_dsdr.c @@ -7,8 +7,7 @@ #include #include -#include - +#include #include "../device.h" #include "../device_vfs.h" @@ -17,7 +16,6 @@ #include "../device_ids.h" #include "../dev_param.h" -#include "../ipblks/streams/sfe_rx_4.h" #include "../ipblks/streams/stream_sfetrx4_dma32.h" #include "../ipblks/fgearbox.h" @@ -27,8 +25,11 @@ #include "../hw/lmk05318/lmk05318.h" #include "../hw/lp875484/lp875484.h" #include "../hw/afe79xx/afe79xx.h" +#include "../hw/tmp114/tmp114.h" #include "dsdr_hiper.h" +#define NO_ECFG_DEFS +#include "../device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.h" // Board revisions: // 1 - DSDR @@ -36,15 +37,18 @@ // enum dsdr_type { - DSDR_KCU116_EVM = 0xce, + DSDR_KCU116_EVM = 0xc0, DSDR_M2_R0 = 0xc2, + DSDR_M2_R1 = 0xce, DSDR_PCIE_HIPER_R0 = 0xcf, }; -enum dsdr_jesdv { - DSDR_JESD204B_810_245 = 1, - DSDR_JESD204C_6664_245 = 7, - DSDR_JESD204C_6664_491 = 3, +enum dsdr_jesd_config { + JESD_MODE_4X_4X_491 = 0x03, + + JESD_MODE_4X_4X_DYN = 0xc3, + JESD_MODE_8X_4X_DYN = 0xd3, + JESD_MODE_8X_8X_DYN = 0xe3, }; // I2C buses @@ -87,6 +91,8 @@ enum i2c_idx { I2C_AFE_PMIC = MAKE_LSOP_I2C_ADDR(0, 0, I2C_ADDR_PMIC_0P9), I2C_TPS63811 = MAKE_LSOP_I2C_ADDR(0, 1, I2C_DEV_DCDCBOOST), I2C_LMK = MAKE_LSOP_I2C_ADDR(0, 1, I2C_ADDR_LMK), + I2C_TEMP_AFE = MAKE_LSOP_I2C_ADDR(0, 0, I2C_DEV_TMP114NB), // Since M.2 rev1 + I2C_TEMP_FPGA = MAKE_LSOP_I2C_ADDR(0, 1, I2C_DEV_TMP114NB), // Since M.2 rev1 }; // LMK ports @@ -155,6 +161,13 @@ enum { IGPO_TX_MAP = 37, //IGPO_RX_IQS = 38, + + IGPO_TX_CHEN = 39, + IGPO_RX_CHEN = 40, + + IGPO_TX_AFETDD = 41, + IGPO_RX_AFETDD = 42, + }; enum { @@ -256,15 +269,15 @@ const usdr_dev_param_constant_t s_params_m2_dsdr_rev000[] = { { "/ll/sync/0/base", M2PCI_REG_WR_SYNC_CTRL}, { "/ll/sdr/0/rfic/0", (uintptr_t)"afe79xx" }, - { "/ll/sdr/max_hw_rx_chans", 4 }, - { "/ll/sdr/max_hw_tx_chans", 4 }, - - { "/ll/sdr/max_sw_rx_chans", 4 }, - { "/ll/sdr/max_sw_tx_chans", 4 }, + { "/ll/device/name", (uintptr_t)"dsdr"}, }; static int dev_m2_dsdr_rate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_dsdr_rate_m_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_dsdr_rate_m_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); + +static int dev_m2_dsdr_rx_enchan(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_dsdr_tx_enchan(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_dsdr_gain_tx_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -290,6 +303,8 @@ static int dev_m2_dsdr_sdr_tx_dsa_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_ static int dev_m2_dsdr_afe_health_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static int dev_m2_dsdr_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); + static int dev_m2_dsdr_dummy(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { return 0; @@ -298,7 +313,6 @@ static int dev_m2_dsdr_dummy(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) static int _debug_lmk05318_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int _debug_lmk05318_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); - static int dev_m2_dsdr_sdr_rx_remap_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_dsdr_sdr_rx_remap_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); static int dev_m2_dsdr_sdr_tx_remap_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -322,86 +336,26 @@ void chmsk_set_all(chmsk_t* msk) { *msk = ~((uint64_t)0); } -static int device_path_to_chmsk(const char* full_path, const char* basename, chmsk_t *hw_mask, chmsk_t *lg_mask); +static int device_path_to_chmsk(const char* full_path, const char* basename, const channel_map_info_t* map, const unsigned max_lchan, chmsk_t *hw_mask, chmsk_t *lg_mask); - -#if 0 -static int device_path_to_chmsk(const char* full_path, const char* basename, chmsk_t *hw_mask, chmsk_t *lg_mask) -{ - const char* delim = ":_-"; - *hw_mask = 0; - *lg_mask = 0; - - size_t len = strlen(basename); - if (strncmp(full_path, basename, len)) { - return -ENOENT; - } - const char* lst = full_path + len; - if (*lst != '/') { - chmsk_set_all(lg_mask); - return -ENAVAIL; - } - - lst++; - - char chanlist[64*4]; - SAFE_STRCPY(chanlist, lst); - - char* saveptr; - char* str1; - unsigned t; - for (t = 0, str1 = chanlist; ; str1 = NULL, t++) { - const char* token = strtok_r(str1, delim, &saveptr); - if (token == NULL) { - break; - } - - if (isdigit(*token)) { - unsigned chn = atoi(token); - if (chn >= MAX_CHANNEL_NUMBER) { - USDR_LOG("UDEV", USDR_LOG_ERROR, "Channel mask parsing for %s: incorrect channel number: %d, token# %d `%s`\n", full_path, chn, t, token); - return -EINVAL; - } - uint64_t chmsh = 1ull << chn; - if (chmsh & *lg_mask) { - USDR_LOG("UDEV", USDR_LOG_WARNING, "Channel mask parsing for %s: channel %d duplication, token# %d `%s`!\n", full_path, chn, t, token); - } - - *lg_mask |= chmsh; - } else if (isalpha(*token)) { - int chA = tolower(*token) - 'a'; - int chB = isalpha(*(token + 1)) ? tolower(*(token + 1)) - 'a' : -1; - - unsigned chn = (chB < 0) ? chA : ((chA + 1) * 26 + chB); - if (chn >= MAX_CHANNEL_NUMBER) { - USDR_LOG("UDEV", USDR_LOG_ERROR, "Channel mask parsing for %s: incorrect channel number: %d, token# %d `%s`\n", full_path, chn, t, token); - return -EINVAL; - } - uint64_t chmsh = 1ull << chn; - if (chmsh & *hw_mask) { - USDR_LOG("UDEV", USDR_LOG_WARNING, "Channel mask parsing for %s: channel %d duplication, token# %d `%s`!\n", full_path, chn, t, token); - } - - *hw_mask |= chmsh; - } else { - USDR_LOG("UDEV", USDR_LOG_ERROR, "Channel mask parsing for %s: incorrect token# %d `%s`\n", full_path, t, token); - return -EINVAL; - } - } - - if (*hw_mask && *lg_mask) { - USDR_LOG("UDEV", USDR_LOG_ERROR, "Hardware and logical channel types mixing for %s: HW_MSK=%" PRIu64 " LG_MSK=%" PRIu64 "\n", full_path, *hw_mask, *lg_mask); - return -EINVAL; - } - - return 0; -} -#endif +static int dev_m2_dsdr_hrxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static int dev_m2_dsdr_htxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static int dev_m2_dsdr_lrxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); +static int dev_m2_dsdr_ltxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); static const usdr_dev_param_func_t s_fparams_m2_dsdr_rev000[] = { + { "/ll/sdr/max_hw_rx_chans", { NULL, dev_m2_dsdr_hrxnumchans_get } }, + { "/ll/sdr/max_hw_tx_chans", { NULL, dev_m2_dsdr_htxnumchans_get } }, + + { "/ll/sdr/max_sw_rx_chans", { NULL, dev_m2_dsdr_lrxnumchans_get } }, + { "/ll/sdr/max_sw_tx_chans", { NULL, dev_m2_dsdr_ltxnumchans_get } }, + { "/dm/rate/master", { dev_m2_dsdr_rate_set, NULL }}, - { "/dm/rate/rxtxadcdac", { dev_m2_dsdr_rate_m_set, NULL }}, + { "/dm/rate/rxtxadcdac", { dev_m2_dsdr_rate_m_set, dev_m2_dsdr_rate_m_get }}, + + { "/dm/sdr/0/rx_enchan", { dev_m2_dsdr_rx_enchan, NULL }}, + { "/dm/sdr/0/tx_enchan", { dev_m2_dsdr_tx_enchan, NULL }}, { "/dm/sdr/0/rx/remap", { dev_m2_dsdr_sdr_rx_remap_set, dev_m2_dsdr_sdr_rx_remap_get }}, { "/dm/sdr/0/tx/remap", { dev_m2_dsdr_sdr_tx_remap_set, dev_m2_dsdr_sdr_tx_remap_get }}, @@ -411,90 +365,112 @@ const usdr_dev_param_func_t s_fparams_m2_dsdr_rev000[] = { { "/dm/sdr/0/rx/gain/1", { dev_m2_dsdr_gain_rx_set, NULL }}, { "/dm/sdr/0/rx/gain/2", { dev_m2_dsdr_gain_rx_set, NULL }}, { "/dm/sdr/0/rx/gain/3", { dev_m2_dsdr_gain_rx_set, NULL }}, - { "/dm/sdr/0/rx/gain/a", { dev_m2_dsdr_gain_rx_set, NULL }}, - { "/dm/sdr/0/rx/gain/b", { dev_m2_dsdr_gain_rx_set, NULL }}, - { "/dm/sdr/0/rx/gain/c", { dev_m2_dsdr_gain_rx_set, NULL }}, - { "/dm/sdr/0/rx/gain/d", { dev_m2_dsdr_gain_rx_set, NULL }}, + { "/dm/sdr/0/rx/gain/4", { dev_m2_dsdr_gain_rx_set, NULL }}, + { "/dm/sdr/0/rx/gain/5", { dev_m2_dsdr_gain_rx_set, NULL }}, + { "/dm/sdr/0/rx/gain/6", { dev_m2_dsdr_gain_rx_set, NULL }}, + { "/dm/sdr/0/rx/gain/7", { dev_m2_dsdr_gain_rx_set, NULL }}, { "/dm/sdr/0/rx/gain/auto", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, { "/dm/sdr/0/rx/gain/auto/0", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, { "/dm/sdr/0/rx/gain/auto/1", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, { "/dm/sdr/0/rx/gain/auto/2", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, { "/dm/sdr/0/rx/gain/auto/3", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, - { "/dm/sdr/0/rx/gain/auto/a", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, - { "/dm/sdr/0/rx/gain/auto/b", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, - { "/dm/sdr/0/rx/gain/auto/c", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, - { "/dm/sdr/0/rx/gain/auto/d", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, + { "/dm/sdr/0/rx/gain/auto/4", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, + { "/dm/sdr/0/rx/gain/auto/5", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, + { "/dm/sdr/0/rx/gain/auto/6", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, + { "/dm/sdr/0/rx/gain/auto/7", { dev_m2_dsdr_gain_rx_auto_set, NULL }}, { "/dm/sdr/0/rx/gain/lna", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, { "/dm/sdr/0/rx/gain/lna/0", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, { "/dm/sdr/0/rx/gain/lna/1", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, { "/dm/sdr/0/rx/gain/lna/2", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, { "/dm/sdr/0/rx/gain/lna/3", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, - { "/dm/sdr/0/rx/gain/lna/a", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, - { "/dm/sdr/0/rx/gain/lna/b", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, - { "/dm/sdr/0/rx/gain/lna/c", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, - { "/dm/sdr/0/rx/gain/lna/d", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, + { "/dm/sdr/0/rx/gain/lna/4", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, + { "/dm/sdr/0/rx/gain/lna/5", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, + { "/dm/sdr/0/rx/gain/lna/6", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, + { "/dm/sdr/0/rx/gain/lna/7", { dev_m2_dsdr_gain_rx_lna_set, NULL }}, { "/dm/sdr/0/rx/gain/pga", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, { "/dm/sdr/0/rx/gain/pga/0", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, { "/dm/sdr/0/rx/gain/pga/1", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, { "/dm/sdr/0/rx/gain/pga/2", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, { "/dm/sdr/0/rx/gain/pga/3", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, - { "/dm/sdr/0/rx/gain/pga/a", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, - { "/dm/sdr/0/rx/gain/pga/b", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, - { "/dm/sdr/0/rx/gain/pga/c", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, - { "/dm/sdr/0/rx/gain/pga/d", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, + { "/dm/sdr/0/rx/gain/pga/4", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, + { "/dm/sdr/0/rx/gain/pga/5", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, + { "/dm/sdr/0/rx/gain/pga/6", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, + { "/dm/sdr/0/rx/gain/pga/7", { dev_m2_dsdr_gain_rx_pga_set, NULL }}, { "/dm/sdr/0/tx/gain", { dev_m2_dsdr_gain_tx_set, NULL }}, { "/dm/sdr/0/tx/gain/0", { dev_m2_dsdr_gain_tx_set, NULL }}, { "/dm/sdr/0/tx/gain/1", { dev_m2_dsdr_gain_tx_set, NULL }}, { "/dm/sdr/0/tx/gain/2", { dev_m2_dsdr_gain_tx_set, NULL }}, { "/dm/sdr/0/tx/gain/3", { dev_m2_dsdr_gain_tx_set, NULL }}, - { "/dm/sdr/0/tx/gain/a", { dev_m2_dsdr_gain_tx_set, NULL }}, - { "/dm/sdr/0/tx/gain/b", { dev_m2_dsdr_gain_tx_set, NULL }}, - { "/dm/sdr/0/tx/gain/c", { dev_m2_dsdr_gain_tx_set, NULL }}, - { "/dm/sdr/0/tx/gain/d", { dev_m2_dsdr_gain_tx_set, NULL }}, - + { "/dm/sdr/0/tx/gain/4", { dev_m2_dsdr_gain_tx_set, NULL }}, + { "/dm/sdr/0/tx/gain/5", { dev_m2_dsdr_gain_tx_set, NULL }}, + { "/dm/sdr/0/tx/gain/6", { dev_m2_dsdr_gain_tx_set, NULL }}, + { "/dm/sdr/0/tx/gain/7", { dev_m2_dsdr_gain_tx_set, NULL }}, + + { "/dm/sdr/0/rx/frequency", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/0", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/1", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/2", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/3", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/4", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/5", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/6", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/frequency/7", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/rx/freqency", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/rx/freqency/0", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/rx/freqency/1", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/rx/freqency/2", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/rx/freqency/3", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, - { "/dm/sdr/0/rx/freqency/a", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, - { "/dm/sdr/0/rx/freqency/b", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, - { "/dm/sdr/0/rx/freqency/c", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, - { "/dm/sdr/0/rx/freqency/d", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/freqency/4", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/freqency/5", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/freqency/6", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/freqency/7", { dev_m2_dsdr_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/rx/dsa", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, { "/dm/sdr/0/rx/dsa/0", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, { "/dm/sdr/0/rx/dsa/1", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, { "/dm/sdr/0/rx/dsa/2", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, { "/dm/sdr/0/rx/dsa/3", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, - { "/dm/sdr/0/rx/dsa/a", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, - { "/dm/sdr/0/rx/dsa/b", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, - { "/dm/sdr/0/rx/dsa/c", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, - { "/dm/sdr/0/rx/dsa/d", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, - + { "/dm/sdr/0/rx/dsa/4", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, + { "/dm/sdr/0/rx/dsa/5", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, + { "/dm/sdr/0/rx/dsa/6", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, + { "/dm/sdr/0/rx/dsa/7", { dev_m2_dsdr_sdr_rx_dsa_set, NULL }}, + + { "/dm/sdr/0/tx/frequency", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/0", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/1", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/2", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/3", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/4", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/5", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/6", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/7", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/tx/freqency", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, { "/dm/sdr/0/tx/freqency/0", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, { "/dm/sdr/0/tx/freqency/1", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, { "/dm/sdr/0/tx/freqency/2", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, { "/dm/sdr/0/tx/freqency/3", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, - { "/dm/sdr/0/tx/freqency/a", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, - { "/dm/sdr/0/tx/freqency/b", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, - { "/dm/sdr/0/tx/freqency/c", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, - { "/dm/sdr/0/tx/freqency/d", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/freqency/4", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/freqency/5", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/freqency/6", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/tx/freqency/7", { dev_m2_dsdr_sdr_tx_freq_set, NULL }}, { "/dm/sdr/0/tx/dsa", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, { "/dm/sdr/0/tx/dsa/0", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, { "/dm/sdr/0/tx/dsa/1", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, { "/dm/sdr/0/tx/dsa/2", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, { "/dm/sdr/0/tx/dsa/3", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, - { "/dm/sdr/0/tx/dsa/a", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, - { "/dm/sdr/0/tx/dsa/b", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, - { "/dm/sdr/0/tx/dsa/c", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, - { "/dm/sdr/0/tx/dsa/d", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, + { "/dm/sdr/0/tx/dsa/4", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, + { "/dm/sdr/0/tx/dsa/5", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, + { "/dm/sdr/0/tx/dsa/6", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, + { "/dm/sdr/0/tx/dsa/7", { dev_m2_dsdr_sdr_tx_dsa_set, NULL }}, { "/dm/sdr/0/rx/bandwidth", { dev_m2_dsdr_dummy, NULL }}, @@ -513,16 +489,180 @@ const usdr_dev_param_func_t s_fparams_m2_dsdr_rev000[] = { { "/dm/stat", { NULL, dev_m2_dsdr_stat }}, { "/debug/lldev", { NULL, dev_m2_dsdr_debug_lldev_get}}, { "/dm/sdr/refclk/path", { dev_m2_dsdr_dummy, NULL}}, + + { "/dm/sdr/0/dac_vctcxo", { dev_m2_dsdr_vctcxo_set, NULL }}, + { "/debug/clk_info", { dev_m2_dsdr_debug_clk_info, dev_m2_dsdr_debug_clk_info_get }}, { "/debug/hw/lmk05318/0/reg", { _debug_lmk05318_reg_set, _debug_lmk05318_reg_get }}, }; +// Maximum supported simultanious logical channels (inc. multiband configurations) +#define MAX_LOGIC_CHANS 8 +#define MAX_HIPER_FE_PORT 4 +#define MAX_PORT_BANDS 2 // HIPER FE channel map table -static const uint8_t s_chanmap_hw_to_fe[4] = { 2, 3, 1, 0 }; -static const uint8_t s_chanmap_fe_to_hw[4] = { 3, 2, 0, 1 }; +static const uint8_t s_chanmap_hw_to_fe[MAX_HIPER_FE_PORT] = { 2, 3, 1, 0 }; +// static const uint8_t s_chanmap_fe_to_hw[MAX_HIPER_FE_PORT] = { 3, 2, 0, 1 }; + +enum DSDR_STATE { + STATE_IDLE = 0, + STATE_AFE_INIT = 1, +}; + +// Total 10 DSP chains in RX+FB and 8 DSP chains in TX +#define MAX_DSP_CHAINS 10 +// 0: TX/RX A0 +// 1: TX/RX B0 +// 2: TX/RX C0 +// 3: TX/RX D0 +// 4: TX/RX A1 +// 5: TX/RX B1 +// 6: TX/RX C1 +// 7: TX/RX D1 +// 8: FB AB0 +// 9: FB CD0 + +static unsigned make_afe79xx_dspidx(enum nco_type nco, unsigned idx, unsigned band) +{ + switch (nco) { + case NCO_TX: + case NCO_RX: + return 4 * band + idx; + default: + return 8 + idx; + } +} + +// afe79xx lib operates in RX/FB +#define GET_HWRX_TYPE(x) (((x) < 4) ? NCO_RX : NCO_FB) +#define GET_HWRX_IDX(x) (((x) < 4) ? (x) : ((x) - 4)) +#define GET_HWTX_TYPE(x) NCO_TX +#define GET_HWTX_IDX(x) (x) + +enum { + MAX_DSP_NCO_RX = 10, + MAX_DSP_NCO_TX = 8, +}; + +struct channel_logic_dsp_wire { + uint8_t dsp_type; // DSP NCO chain type + uint8_t dsp_idx; // DSP NCO chain index + uint8_t dsp_band; // DSP NCO chain band + uint8_t hwport; // External ADC / DAC port 0, 1, 2, 3 -- for RX/TX and 4, 5 -- for FB +}; +typedef struct channel_logic_dsp_wire channel_logic_dsp_wire_t; + +static unsigned make_afe79xx_idx_from_dsp_wire(const channel_logic_dsp_wire_t* w) +{ + return make_afe79xx_dspidx((enum nco_type)w->dsp_type, w->dsp_idx, w->dsp_band); +} + +static const channel_logic_dsp_wire_t s_dsdr_lmap_s_ncorx[] = { + { NCO_RX, 0, 0, 0 }, + { NCO_RX, 1, 0, 1 }, + { NCO_RX, 2, 0, 2 }, + { NCO_RX, 3, 0, 3 }, +}; + +static const channel_logic_dsp_wire_t s_dsdr_lmap_s_ncotx[] = { + { NCO_TX, 0, 0, 0 }, + { NCO_TX, 1, 0, 1 }, + { NCO_TX, 2, 0, 2 }, + { NCO_TX, 3, 0, 3 }, + }; + +static const channel_map_info_t s_dsdr_chmap_s_nco[] = { + { "a", 0 }, + { "b", 1 }, + { "c", 2 }, + { "d", 3 }, + { NULL, CH_NULL }, +}; + +static const channel_logic_dsp_wire_t s_dsdr_lmap_d_ncorx[] = { + { NCO_RX, 0, 0, 0 }, + { NCO_RX, 1, 0, 1 }, + { NCO_RX, 2, 0, 2 }, + { NCO_RX, 3, 0, 3 }, + { NCO_RX, 0, 1, 0 }, + { NCO_RX, 1, 1, 1 }, + { NCO_RX, 2, 1, 2 }, + { NCO_RX, 3, 1, 3 }, +}; +static const channel_logic_dsp_wire_t s_dsdr_lmap_d_ncotx[] = { + { NCO_TX, 0, 0, 0 }, + { NCO_TX, 1, 0, 1 }, + { NCO_TX, 2, 0, 2 }, + { NCO_TX, 3, 0, 3 }, + { NCO_TX, 0, 1, 0 }, + { NCO_TX, 1, 1, 1 }, + { NCO_TX, 2, 1, 2 }, + { NCO_TX, 3, 1, 3 }, + }; + + +static const channel_map_info_t s_dsdr_chmap_d_nco[] = { + { "a0", 0 }, + { "b0", 1 }, + { "c0", 2 }, + { "d0", 3 }, + { "a1", 4 }, + { "b1", 5 }, + { "c1", 6 }, + { "d1", 7 }, + { NULL, CH_NULL }, +}; + +static const channel_logic_dsp_wire_t s_dsdr_lmap_s_nco_fb[] = { + { NCO_RX, 0, 0, 0 }, + { NCO_RX, 1, 0, 1 }, + { NCO_RX, 2, 0, 2 }, + { NCO_RX, 3, 0, 3 }, + { NCO_FB, 0, 0, 4 }, + { NCO_FB, 1, 0, 5 }, + { NCO_FB, 0, 0, 4 }, + { NCO_FB, 1, 0, 5 }, +}; +static const channel_map_info_t s_dsdr_chmap_s_nco_fb[] = { + { "a", 0 }, + { "b", 1 }, + { "c", 2 }, + { "d", 3 }, + { "f0", 4 }, + { "f1", 5 }, + { "f2", 6 }, + { "f3", 7 }, + + { NULL, CH_NULL }, +}; + + +static const channel_logic_dsp_wire_t s_dsdr_lmap_s_nco_fbrx[] = { + { NCO_RX, 0, 0, 0 }, + { NCO_RX, 1, 0, 1 }, + { NCO_RX, 2, 0, 2 }, + { NCO_RX, 3, 0, 3 }, + { NCO_FB, 0, 0, 0 }, + { NCO_FB, 1, 0, 2 }, + { NCO_FB, 0, 0, 0 }, + { NCO_FB, 1, 0, 2 }, +}; +static const channel_map_info_t s_dsdr_chmap_s_nco_fbrx[] = { + { "a", 0 }, + { "b", 1 }, + { "c", 2 }, + { "d", 3 }, + { "a0", 4 }, + { "c0", 5 }, + { "a2", 6 }, + { "c2", 7 }, + + { NULL, CH_NULL }, +}; + struct dev_m2_dsdr { device_t base; @@ -532,29 +672,46 @@ struct dev_m2_dsdr { subdev_t subdev; unsigned type; - unsigned jesdv; + //unsigned jesdv; + unsigned jesd_x8; + unsigned has_extfe; stream_handle_t* rx; stream_handle_t* tx; afe79xx_state_t st; dsdr_hiper_fe_t hiper; + ext_fe_ch4_400_7200_t extfe; + + uint32_t dsdr_state; + //uint32_t cfg_afe_type; + uint32_t cfg_rx_lanemap; + uint32_t cfg_tx_lanemap; uint32_t debug_lmk05318_last; const char* afecongiguration; uint32_t max_rate; // Maximum I/Q rate supported by HW + const channel_map_info_t *rx_chmap_info; + const channel_map_info_t *tx_chmap_info; + const channel_logic_dsp_wire_t *rx_lmap_info; + const channel_logic_dsp_wire_t *tx_lmap_info; + unsigned hw_enabled_tx; // HW Enabled channels unsigned hw_enabled_rx; // HW Enabled channels - - unsigned hw_mask_tx; // Physically wired TX channels - unsigned hw_mask_rx; // Physically wired RX channels - unsigned hw_mask_fb; // Physically wired FB channels - + unsigned logic_enabled_tx; // Logic Enabled channels + unsigned logic_enabled_rx; // Logic Enabled channels + unsigned hw_chcnt_rx; + unsigned hw_chcnt_tx; + unsigned logic_chcnt_rx; + unsigned logic_chcnt_tx; unsigned hw_fpga_jesd_rx_en; // Physical lanes enabled bitmask 0: X0Y4, 1: X0Y5, ... 3: X0Y7 unsigned hw_fpga_jesd_tx_en; // Physical lanes enabled bitmask 0: X0Y4, 1: X0Y5, ... 3: X0Y7 + unsigned dsp_rx_chans; + unsigned dsp_tx_chans; + uint32_t adc_rate; unsigned rxbb_rate; unsigned rxbb_decim; @@ -567,12 +724,23 @@ struct dev_m2_dsdr { uint8_t rx_activated; // 0xff means channel not wired - uint8_t rx_logic_to_hw[8]; // logic <- hw, index is logic chnum - uint8_t tx_hw_to_logic[8]; // hw -> logic, index is hw chnum + uint8_t rx_ordinal_to_logic[MAX_LOGIC_CHANS]; + uint8_t tx_ordinal_to_logic[MAX_LOGIC_CHANS]; + + uint8_t rx_logic_to_ordinal[MAX_LOGIC_CHANS]; + uint8_t tx_logic_to_ordinal[MAX_LOGIC_CHANS]; + // Configuration parameters - opt_u64_t rx_freqs[8]; - opt_u64_t tx_freqs[8]; + opt_u64_t rx_ord_freqs[MAX_LOGIC_CHANS]; + opt_u64_t tx_ord_freqs[MAX_LOGIC_CHANS]; + + opt_u64_t rx_bxfc[MAX_DSP_NCO_RX]; + opt_u64_t tx_bxfc[MAX_DSP_NCO_TX]; + opt_u64_t rx_raw_nco[MAX_DSP_NCO_RX]; + opt_u64_t tx_raw_nco[MAX_DSP_NCO_TX]; + + opt_u64_t fb_raw_nco[2]; channel_info_t rx_chans; channel_info_t tx_chans; @@ -580,8 +748,6 @@ struct dev_m2_dsdr { }; typedef struct dev_m2_dsdr dev_m2_dsdr_t; - - enum dev_gpo { IGPO_BANK_LEDS = 0, IGPO_PWR_LMK = 1, @@ -603,28 +769,172 @@ static int dev_gpi_get32(lldev_t dev, unsigned bank, unsigned* data) return lowlevel_reg_rd32(dev, 0, 16 + (bank / 4), data); } +// GTH USP ALL REGS MAP +static const uint16_t s_gth_usp_qpll_regs[] = { + 0x008, 0x009, 0x00D, 0x00E, 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x018, 0x019, 0x01A, 0x01B, 0x01C, + 0x01D, 0x01E, 0x01F, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x029, 0x02D, 0x030, 0x081, 0x082, 0x083, 0x084, + 0x086, 0x088, 0x089, 0x08B, 0x08D, 0x08E, 0x08F, 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x098, 0x099, + 0x09A, 0x09B, 0x09C, 0x09D, 0x09E, 0x09F, 0x0A0, 0x0A1, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A8, 0x0A9, 0x0AD, 0x0B0, +}; + +static const uint16_t s_gth_usp_chan_regs[] = { + 0x002, 0x003, 0x004, 0x005, 0x006, 0x009, 0x00B, 0x00C, 0x00E, 0x00F, 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, + 0x016, 0x017, 0x018, 0x019, 0x01A, 0x01B, 0x01C, 0x01D, 0x01E, 0x01F, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, + 0x026, 0x027, 0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, + 0x036, 0x037, 0x038, 0x039, 0x03A, 0x03C, 0x03D, 0x03E, 0x03F, 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, + 0x047, 0x048, 0x049, 0x04A, 0x04B, 0x04C, 0x04D, 0x04E, 0x04F, 0x050, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, + 0x058, 0x059, 0x05A, 0x05B, 0x05C, 0x05D, 0x05E, 0x05F, 0x060, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, + 0x068, 0x069, 0x06A, 0x06B, 0x06C, 0x06D, 0x06E, 0x06F, 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, + 0x078, 0x079, 0x07A, 0x07B, 0x07C, 0x07D, 0x07E, 0x07F, 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x089, 0x08A, + 0x08B, 0x08C, 0x08D, 0x08E, 0x08F, 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x097, 0x098, 0x099, 0x09A, 0x09B, + 0x09C, 0x09D, 0x09E, 0x09F, 0x0A0, 0x0A1, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0AA, 0x0AB, + 0x0AC, 0x0AD, 0x0AE, 0x0AF, 0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0BA, 0x0BB, + 0x0BC, 0x0BD, 0x0BE, 0x0BF, 0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7, 0x0C8, 0x0CA, 0x0CB, 0x0CC, + 0x0CD, 0x0CE, 0x0CF, 0x0D0, 0x0D1, 0x0D2, 0x0D3, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x0D9, 0x0DA, 0x0DB, 0x0DD, + 0x0DE, 0x0DF, 0x0E0, 0x0E1, 0x0E2, 0x0E7, 0x0E8, 0x0E9, 0x0EA, 0x0EB, 0x0EC, 0x0ED, 0x0EE, 0x0EF, 0x0F0, 0x0F1, + 0x0F2, 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x0F9, 0x0FA, 0x0FB, 0x0FB, 0x0FC, 0x0FD, 0x0FE, 0x0FF, 0x100, + 0x101, 0x102, 0x103, 0x105, 0x106, 0x108, 0x109, 0x10A, 0x10B, 0x10C, 0x10D, 0x10E, 0x110, 0x111, 0x112, 0x113, + 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11A, 0x11B, 0x11C, 0x11D, 0x11E, 0x11F, 0x120, 0x121, 0x122, 0x123, + 0x124, 0x125, 0x250, 0x251, 0x252, 0x253, 0x254, 0x255, 0x256, 0x257, 0x258, 0x259, 0x25A, 0x25B, 0x25C, 0x25D, + 0x25E, 0x25F, 0x263, 0x269, +}; + + +#define MAKE_DSDR_PHY_REG(addr, data) ((1<<31) | (((addr) & 0xff) << 16) | (((data) & 0xff))) + +// +enum { + GTH_CTR_TX_EN = 0, + GTH_CTR_RX_EN = 1, + GTH_CTR_QPLL_EN = 2, +}; + +static int dsdr_gth_control(lldev_t dev, uint16_t addr, uint16_t data) +{ + int res = 0; + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, MAKE_DSDR_PHY_REG(addr, data)); + return res; +} + +#define MAKE_DSDR_DRP_CMD(wr, idx, addr, data) ((((wr) & 1) << 30) | (((idx) & 0xf) << 26) | (((addr) & 0x3ff) << 16) | ((data) & 0xffff)) + +enum { + DRP_QPLL_IDX0 = 0, + DRP_QPLL_IDX1 = 1, + DRP_CHAN_IDX0 = 8, +}; + +static int dsdr_drp_reg_wr(lldev_t dev, unsigned port, uint16_t addr, uint16_t data) +{ + int res = 0; + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, MAKE_DSDR_DRP_CMD(1, port, addr, data)); + res = res ? res : usleep(10); + return res; +} + +static int dsdr_drp_reg_rd(lldev_t dev, unsigned port, uint16_t addr, uint16_t *odata) +{ + int res = 0; + uint32_t val = 0, j; + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, MAKE_DSDR_DRP_CMD(0, port, addr, 0)); + for (j = 0; j < 1000; j++) { + res = res ? res : usleep(10); + res = res ? res : lowlevel_reg_rd32(dev, 0, REG_CFG_PHY_0, &val); + if (!(val >> 31)) + break; + } + *odata = val; + return res; +} + + +static int gth_dump_all_regs(dev_m2_dsdr_t *d) +{ + uint16_t val = 0; + int res = 0; + for (unsigned i = 0; i < SIZEOF_ARRAY(s_gth_usp_qpll_regs); i++) { + res = res ? res : dsdr_drp_reg_rd(d->base.dev, DRP_QPLL_IDX0, s_gth_usp_qpll_regs[i], &val); + USDR_LOG("GTHQ", USDR_LOG_WARNING, "GTQ[%04x] => %04x\n", s_gth_usp_qpll_regs[i], val); + } + for (unsigned i = 0; i < SIZEOF_ARRAY(s_gth_usp_chan_regs); i++) { + res = res ? res : dsdr_drp_reg_rd(d->base.dev, DRP_CHAN_IDX0, s_gth_usp_chan_regs[i], &val); + USDR_LOG("GTHC", USDR_LOG_WARNING, "GTC[%04x] => %04x\n", s_gth_usp_chan_regs[i], val); + } + return res; +} + bool dev_m2_dsdr_has_hiper(dev_m2_dsdr_t* d) { return d->type == DSDR_PCIE_HIPER_R0; } +bool dev_m2_dsdr_has_extfe0472(dev_m2_dsdr_t* d) +{ + return d->has_extfe; +} + + +int dev_m2_dsdr_lrxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + dev_m2_dsdr_t *d = (dev_m2_dsdr_t *)ud; + return d->logic_chcnt_rx; +} + +int dev_m2_dsdr_ltxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + dev_m2_dsdr_t *d = (dev_m2_dsdr_t *)ud; + return d->logic_chcnt_tx; +} + +int dev_m2_dsdr_htxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + dev_m2_dsdr_t *d = (dev_m2_dsdr_t *)ud; + return d->hw_chcnt_tx; +} + +int dev_m2_dsdr_hrxnumchans_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + dev_m2_dsdr_t *d = (dev_m2_dsdr_t *)ud; + return d->hw_chcnt_rx; +} + +int dev_m2_dsdr_rx_enchan(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + dev_m2_dsdr_t *d = (dev_m2_dsdr_t *)ud; + return dev_gpo_set(d->base.dev, IGPO_RX_CHEN, value); + +} +int dev_m2_dsdr_tx_enchan(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + dev_m2_dsdr_t *d = (dev_m2_dsdr_t *)ud; + return dev_gpo_set(d->base.dev, IGPO_TX_CHEN, value); +} + static int dsdr_update_rx_remap(dev_m2_dsdr_t* d) { // Mark corresponding RX chanel uint64_t hiper_cfg_msk = 0; - unsigned rx_remap = 0; + unsigned hw_cfg_msk = 0; + + //unsigned rx_remap = 0; int res = 0; - for (unsigned i = 0; i < 4; i++) { - if (d->rx_logic_to_hw[i] != 0xff) { - rx_remap |= (d->rx_logic_to_hw[i] & 0x3) << (2 * i); + for (unsigned i = 0; i < MAX_LOGIC_CHANS; i++) { + if (d->rx_ordinal_to_logic[i] != 0xff) { + //rx_remap |= (d->rx_ordinal_to_logic[i] & 0x3) << (2 * i); - hiper_cfg_msk |= (1u << s_chanmap_hw_to_fe[d->rx_logic_to_hw[i]]); + unsigned hw_chan = d->rx_lmap_info[d->rx_ordinal_to_logic[i]].hwport; + if (hw_chan < MAX_HIPER_FE_PORT) { + hiper_cfg_msk |= (1u << s_chanmap_hw_to_fe[hw_chan]); + } + hw_cfg_msk |= (1 << hw_chan); } } if (dev_m2_dsdr_has_hiper(d)) { res = dsdr_hiper_fe_rx_chan_en(&d->hiper, hiper_cfg_msk); + } else if (dev_m2_dsdr_has_extfe0472(d)) { + res = ext_fe_rx_chan_en(&d->extfe, hw_cfg_msk); } // TODO issue FE cmd // return res ? res : dev_gpo_set(d->base.dev, IGPO_RX_MAP, rx_remap); @@ -636,159 +946,321 @@ static int dsdr_update_tx_remap(dev_m2_dsdr_t* d) { // Mark corresponding TX chanel uint64_t hiper_cfg_msk = 0; - unsigned tx_remap = 0; + unsigned hw_cfg_msk = 0; + + //unsigned tx_remap = 0; int res = 0; for (unsigned i = 0; i < 4; i++) { - if (d->tx_hw_to_logic[i] != 0xff) { - tx_remap |= (d->tx_hw_to_logic[i] & 0x3) << (2 * i); + if (d->tx_ordinal_to_logic[i] != 0xff) { + //tx_remap |= (d->tx_ordinal_to_logic[i] & 0x3) << (2 * i); - hiper_cfg_msk |= (1u << s_chanmap_hw_to_fe[i]); + unsigned hw_chan = d->tx_lmap_info[d->tx_ordinal_to_logic[i]].hwport; + if (hw_chan < MAX_HIPER_FE_PORT) { + hiper_cfg_msk |= (1u << s_chanmap_hw_to_fe[hw_chan]); + } + hw_cfg_msk |= (1 << hw_chan); } } if (dev_m2_dsdr_has_hiper(d)) { res = dsdr_hiper_fe_tx_chan_en(&d->hiper, hiper_cfg_msk); + } else if (dev_m2_dsdr_has_extfe0472(d)) { + res = ext_fe_tx_chan_en(&d->extfe, hw_cfg_msk); } // TODO issue FE cmd //return res ? res : dev_gpo_set(d->base.dev, IGPO_TX_MAP, tx_remap); return res; } -static int dsdr_iterate_chans(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val, const char* basename, bool rxchans) +// Function tries to parse and match names to logical channels and iterate with ordinal index +static int dsdr_iterate_ordinal_chans(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val, const char* basename, bool rxchans) { dev_m2_dsdr_t *d = (dev_m2_dsdr_t *)ud; vfs_object_t ph; chmsk_t logic_msk; - chmsk_t hw_msk; - int res = device_path_to_chmsk(obj->full_path, basename, &hw_msk, &logic_msk); + chmsk_t logic_enabled; + chmsk_t ordinal_msk; + chmsk_t ordinal_enabled; + int res = device_path_to_chmsk(obj->full_path, basename, + rxchans ? d->rx_chmap_info : d->tx_chmap_info, + rxchans ? d->logic_chcnt_rx : d->logic_chcnt_tx, + &logic_msk, &ordinal_msk); if (res == -ENAVAIL) { // No channel information were specified, apply settings to all HW enabled channels or cache the value - if (rxchans) { - hw_msk = d->rx_activated ? d->hw_enabled_rx : 0xf; - } else { - hw_msk = d->tx_activated ? d->hw_enabled_tx : 0xf; - } + ordinal_msk = 0; logic_msk = 0; res = 0; } else if (res != 0) { return res; } + if (rxchans) { + logic_enabled = d->rx_activated ? d->logic_enabled_rx : 0xf; + } else { + logic_enabled = d->tx_activated ? d->logic_enabled_tx : 0xf; + } + + ordinal_enabled = 0; + if (logic_msk != 0) { + logic_enabled &= logic_msk; + } + + for (unsigned i = 0; i < (rxchans ? d->logic_chcnt_rx : d->logic_chcnt_tx); i++) { + if (chmsk_is_set(&logic_enabled, rxchans ? d->rx_ordinal_to_logic[i] : d->tx_ordinal_to_logic[i])) { + ordinal_enabled |= (1 << i); + } + } + if (ordinal_msk != 0) { + ordinal_enabled &= ordinal_msk; + } + + ph.type = obj->type; ph.object = obj->object; ph.data = obj->data; ph.ops = obj->ops; ph.full_path[0] = 0; - USDR_LOG("HIPR", USDR_LOG_WARNING, "Setting parameter `%s` to LOGIC: %08x HW: %08x chans\n", - obj->full_path, (unsigned)logic_msk, (unsigned)hw_msk); + USDR_LOG("HIPR", USDR_LOG_WARNING, "Setting parameter `%s` to LOGIC: %08x ORD: %08x chans\n", + obj->full_path, (unsigned)logic_msk, (unsigned)ordinal_enabled); - for (unsigned i = 0; i < DSDR_CHANS_HW; i++) { - if (chmsk_is_set(&hw_msk, i)) { + // We rely on ordinal numbers for API simplification + for (unsigned i = 0; i < (rxchans ? d->logic_chcnt_rx : d->logic_chcnt_tx); i++) { + if (chmsk_is_set(&ordinal_enabled, i)) { ph.full_path[1] = i; res = res ? res : obj->ops.si64(&ph, val); } } + return res; +} - for (unsigned i = 0; i < DSDR_CHANS_LOGIC; i++) { - if (chmsk_is_set(&logic_msk, i)) { - - if (rxchans) { - uint8_t map = d->rx_logic_to_hw[i]; - if (map == 0xff) { - // Channel disabled - continue; - } - - ph.full_path[1] = i; - res = res ? res : obj->ops.si64(&ph, val); - } else { - // One logical TX channel can be mapped to many physical - - for (unsigned j = 0; j < DSDR_CHANS_HW; j++) { - if (d->tx_hw_to_logic[j] != i) - continue; - - ph.full_path[1] = i; - res = res ? res : obj->ops.si64(&ph, val); - } - } - } +static const channel_logic_dsp_wire_t *get_chmapnfo_from_ordinal(dev_m2_dsdr_t* d, bool rx, unsigned ordinal) +{ + if (ordinal >= (rx ? d->logic_chcnt_rx : d->logic_chcnt_tx)) { + return NULL; } - - return res; + uint8_t logic_num = (rx ? d->rx_ordinal_to_logic[ordinal] : d->tx_ordinal_to_logic[ordinal]); + if (logic_num == 0xff) { + return NULL; + } + return rx ? &d->rx_lmap_info[logic_num] : &d->tx_lmap_info[logic_num]; } -static int dsdr_set_rx_frequency_chan(dev_m2_dsdr_t* d, uint64_t freq, unsigned chno) +static int dsdr_set_rx_frequency_chan(dev_m2_dsdr_t* d, uint64_t freq, unsigned ord) { int res = 0; - opt_u64_set_val(& d->rx_freqs[chno], freq); + opt_u64_set_val(&d->rx_ord_freqs[ord], freq); if (!d->rx_activated) { return 0; } + // Ordinal to logic converter + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, true, ord); + if (!w) { + return -EINVAL; + } + uint64_t ncoval = freq; - if (dev_m2_dsdr_has_hiper(d)) { - bool ch_rxiq; + if (dev_m2_dsdr_has_hiper(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + bool ch_rxiq; // 1 - need to mirror spectrum bool mod = false; - unsigned fe_chan = s_chanmap_hw_to_fe[chno]; - int res = dsdr_hiper_fe_rx_freq_set(&d->hiper, fe_chan, freq, &ncoval, &ch_rxiq); + const uint8_t iter_shared_lo_chans[2][2] = { { 0, 1 }, { 2, 3 } }; // Map to HW channels + const uint8_t iter_shared_selector[4] = { 0, 0, 1, 1 }; + uint64_t chan_mid = 0; + unsigned cnt = 0; + int res = 0; + const bool shared_lo_cfg_mode = true; + unsigned hwidx = w->hwport; + unsigned ncoidx = make_afe79xx_idx_from_dsp_wire(w); + + assert(ncoidx < MAX_DSP_NCO_RX); + opt_u64_set_val(&d->rx_bxfc[ncoidx], freq); + + USDR_LOG("HIPR", USDR_LOG_WARNING, "RX ORD %d: Set NCO%d to %.3f\n", ord, ncoidx, freq / 1.0e6); + + // Calculate mid frequency for all HW-related channels in the same LMS8001 LO-shared chain + for (unsigned j = 0; j < d->logic_chcnt_rx; j++) { + const channel_logic_dsp_wire_t* chinfo = &d->rx_lmap_info[j]; + if ((shared_lo_cfg_mode && (chinfo->hwport == iter_shared_lo_chans[iter_shared_selector[hwidx]][0] || chinfo->hwport == iter_shared_lo_chans[iter_shared_selector[hwidx]][1])) || + (!shared_lo_cfg_mode && (chinfo->hwport == hwidx))) { + unsigned v = make_afe79xx_idx_from_dsp_wire(chinfo); + if (d->rx_bxfc[v].set) { + chan_mid += d->rx_bxfc[v].value; + cnt++; + + USDR_LOG("HIPR", USDR_LOG_WARNING, "RX%d: IDX%d COMB_CH[%d] %.3f -- HWIDX: %d HWITER:%d\n", cnt, j, v, d->rx_bxfc[v].value / 1.0e6, hwidx, chinfo->hwport); + } + } + } + assert(cnt != 0); + chan_mid /= cnt; + + for (unsigned s = 0; s < (shared_lo_cfg_mode ? 2 : 1); s++) { + unsigned iter_hwid = shared_lo_cfg_mode ? iter_shared_lo_chans[iter_shared_selector[hwidx]][s] : hwidx; + res = res ? res : dsdr_hiper_fe_rx_freq_set(&d->hiper, s_chanmap_hw_to_fe[iter_hwid], chan_mid, &ncoval, &ch_rxiq); + } if (res) return res; - for (unsigned k = 0; k < DSDR_CHANS_HW; k++) { - if ((d->rx_chans.ch_map[k] & ~CH_SWAP_IQ_FLAG) == chno) { - uint8_t nchan = (ch_rxiq) ? d->rx_chans.ch_map[k] | CH_SWAP_IQ_FLAG : d->rx_chans.ch_map[k] & ~CH_SWAP_IQ_FLAG; + for (unsigned k = 0; k < d->logic_chcnt_rx; k++) { + uint8_t logic_ch = d->rx_ordinal_to_logic[k]; + if (logic_ch == 0xff) + continue; + + const channel_logic_dsp_wire_t* chinfo = &d->rx_lmap_info[logic_ch]; + unsigned hw_iter = chinfo->hwport; + if ((shared_lo_cfg_mode && (hw_iter == iter_shared_lo_chans[iter_shared_selector[hwidx]][0] || hw_iter == iter_shared_lo_chans[iter_shared_selector[hwidx]][1])) || + (!shared_lo_cfg_mode && (hw_iter == hwidx))) { + + uint8_t nchan = (ch_rxiq) ? (d->rx_chans.ch_map[k] | CH_SWAP_IQ_FLAG) : (d->rx_chans.ch_map[k] & ~CH_SWAP_IQ_FLAG); if (nchan != d->rx_chans.ch_map[k]) { d->rx_chans.ch_map[k] = nchan; mod = true; } + + unsigned rnco_idx = make_afe79xx_idx_from_dsp_wire(chinfo); + if (!d->rx_bxfc[rnco_idx].set) + continue; + + // Calculate individual offset, assume everything is in the same Nyquist zone + uint64_t freq_req = d->rx_bxfc[make_afe79xx_idx_from_dsp_wire(chinfo)].value; + int64_t nco_offset = freq_req - chan_mid; + uint64_t freq_mod = ch_rxiq ? (ncoval - nco_offset) : (ncoval + nco_offset); + + // Check cached value + if (d->rx_raw_nco[rnco_idx].set && d->rx_raw_nco[rnco_idx].value == freq_mod) + continue; + + res = res ? res : d->st.libcapi79xx_upd_nco(&d->st.capi, chinfo->dsp_type, chinfo->dsp_idx, freq_mod / 1000, 0, chinfo->dsp_band); + USDR_LOG("HIPR", USDR_LOG_WARNING, "CH[%d => %c: %s%d.Band%d] F=%.3f RX_NCO[%d]=%.3f MID_F=%.3f\n", + ord, hw_iter + 'A', chinfo->dsp_type == NCO_RX ? "RX" : "FB", chinfo->dsp_idx, chinfo->dsp_band, + freq_req / 1.0e6, rnco_idx, freq_mod / 1.0e6, chan_mid / 1.0e6); + + opt_u64_set_val(&d->rx_raw_nco[rnco_idx], freq_mod); } } + if (mod) { res = res ? res : d->rx->ops->option_set(d->rx, "chmap", (uintptr_t)&d->rx_chans); } + return res; + } else if (dev_m2_dsdr_has_extfe0472(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + res = res ? res : ext_fe_rx_freq_set(&d->extfe, w->hwport, ncoval); } - USDR_LOG("HIPR", USDR_LOG_WARNING, "CH[%d] F=%.3f RX_NCO=%.3f\n", chno, freq / 1.0e6, ncoval / 1.0e6); - res = res ? res : d->st.libcapi79xx_upd_nco(&d->st.capi, NCO_RX, chno, ncoval / 1000, 0, 0); + USDR_LOG("DSDR", USDR_LOG_WARNING, "RX CH[%d => %c: %s%d.Band%d] NCO[%d]=%.3f\n", + ord, w->hwport + 'A', w->dsp_type == NCO_RX ? "RX" : "FB", w->dsp_idx, w->dsp_band, + make_afe79xx_idx_from_dsp_wire(w), freq / 1.0e6); + res = res ? res : d->st.libcapi79xx_upd_nco(&d->st.capi, w->dsp_type, w->dsp_idx, ncoval / 1000, 0, w->dsp_band); return res; } -static int dsdr_set_tx_frequency_chan(dev_m2_dsdr_t* d, uint64_t freq, unsigned chno) +static int dsdr_set_tx_frequency_chan(dev_m2_dsdr_t* d, uint64_t freq, unsigned ord) { - opt_u64_set_val(& d->tx_freqs[chno], freq); + int res = 0; + opt_u64_set_val(& d->tx_ord_freqs[ord], freq); if (!d->tx_activated) { return 0; } + // Ordinal to logic converter + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, false, ord); + if (!w) { + return -EINVAL; + } + uint64_t ncoval = freq; - if (dev_m2_dsdr_has_hiper(d)) { - bool ch_txiq; + if (dev_m2_dsdr_has_hiper(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + bool ch_txiq; // 1 - need to mirror spectrum bool mod = false; - unsigned fe_chan = s_chanmap_hw_to_fe[chno]; - int res = dsdr_hiper_fe_tx_freq_set(&d->hiper, fe_chan, freq, &ncoval, &ch_txiq); + const uint8_t iter_shared_lo_chans[2][2] = { { 0, 1 }, { 2, 3 } }; // Map to HW channels + const uint8_t iter_shared_selector[4] = { 0, 0, 1, 1 }; + uint64_t chan_mid = 0; + unsigned cnt = 0; + int res = 0; + const bool shared_lo_cfg_mode = true; + unsigned hwidx = w->hwport; + unsigned ncoidx = make_afe79xx_idx_from_dsp_wire(w); + + assert(ncoidx < MAX_DSP_NCO_TX); + opt_u64_set_val(&d->tx_bxfc[ncoidx], freq); + + USDR_LOG("HIPR", USDR_LOG_WARNING, "TX ORD %d: Set NCO%d to %.3f\n", ord, ncoidx, freq / 1.0e6); + + // Calculate mid frequency for all HW-related channels in the same LMS8001 LO-shared chain + for (unsigned j = 0; j < d->logic_chcnt_tx; j++) { + const channel_logic_dsp_wire_t* chinfo = &d->tx_lmap_info[j]; + if ((shared_lo_cfg_mode && (chinfo->hwport == iter_shared_lo_chans[iter_shared_selector[hwidx]][0] || chinfo->hwport == iter_shared_lo_chans[iter_shared_selector[hwidx]][1])) || + (!shared_lo_cfg_mode && (chinfo->hwport == hwidx))) { + unsigned v = make_afe79xx_idx_from_dsp_wire(chinfo); + if (d->tx_bxfc[v].set) { + chan_mid += d->tx_bxfc[v].value; + cnt++; + + USDR_LOG("HIPR", USDR_LOG_WARNING, "TX%d: IDX%d COMB_CH[%d] %.3f -- HWIDX: %d HWITER:%d\n", cnt, j, v, d->tx_bxfc[v].value / 1.0e6, hwidx, chinfo->hwport); + } + } + } + assert(cnt != 0); + chan_mid /= cnt; + + for (unsigned s = 0; s < (shared_lo_cfg_mode ? 2 : 1); s++) { + unsigned iter_hwid = shared_lo_cfg_mode ? iter_shared_lo_chans[iter_shared_selector[hwidx]][s] : hwidx; + res = res ? res : dsdr_hiper_fe_tx_freq_set(&d->hiper, s_chanmap_hw_to_fe[iter_hwid], chan_mid, &ncoval, &ch_txiq); + } if (res) return res; - // d->txbb_swap_iq = (ch_txiq) ? d->txbb_swap_iq | (1u << chno) : d->txbb_swap_iq & (~(1u << chno)); - for (unsigned k = 0; k < DSDR_CHANS_HW; k++) { - if ((d->tx_chans.ch_map[k] & ~CH_SWAP_IQ_FLAG) == chno) { - uint8_t nchan = (ch_txiq) ? d->tx_chans.ch_map[k] | CH_SWAP_IQ_FLAG : d->tx_chans.ch_map[k] & ~CH_SWAP_IQ_FLAG; + for (unsigned k = 0; k < d->logic_chcnt_tx; k++) { + uint8_t logic_ch = d->tx_ordinal_to_logic[k]; + if (logic_ch == 0xff) + continue; + + const channel_logic_dsp_wire_t* chinfo = &d->tx_lmap_info[logic_ch]; + unsigned hw_iter = chinfo->hwport; + if ((shared_lo_cfg_mode && (hw_iter == iter_shared_lo_chans[iter_shared_selector[hwidx]][0] || hw_iter == iter_shared_lo_chans[iter_shared_selector[hwidx]][1])) || + (!shared_lo_cfg_mode && (hw_iter == hwidx))) { + + uint8_t nchan = (ch_txiq) ? (d->tx_chans.ch_map[k] | CH_SWAP_IQ_FLAG) : (d->tx_chans.ch_map[k] & ~CH_SWAP_IQ_FLAG); if (nchan != d->tx_chans.ch_map[k]) { d->tx_chans.ch_map[k] = nchan; mod = true; } + + unsigned rnco_idx = make_afe79xx_idx_from_dsp_wire(chinfo); + if (!d->tx_bxfc[rnco_idx].set) + continue; + + // Calculate individual offset, assume everything is in the same Nyquist zone + uint64_t freq_req = d->tx_bxfc[make_afe79xx_idx_from_dsp_wire(chinfo)].value; + int64_t nco_offset = freq_req - chan_mid; + uint64_t freq_mod = ch_txiq ? (ncoval - nco_offset) : (ncoval + nco_offset); + + // Check cached value + if (d->tx_raw_nco[rnco_idx].set && d->tx_raw_nco[rnco_idx].value == freq_mod) + continue; + + res = res ? res : d->st.libcapi79xx_upd_nco(&d->st.capi, chinfo->dsp_type, chinfo->dsp_idx, freq_mod / 1000, 0, chinfo->dsp_band); + USDR_LOG("HIPR", USDR_LOG_WARNING, "CH[%d => %c: %s%d.Band%d] F=%.3f TX_NCO[%d]=%.3f MID_F=%.3f\n", + ord, hw_iter + 'A', chinfo->dsp_type == NCO_TX ? "TX" : "FB", chinfo->dsp_idx, chinfo->dsp_band, + freq_req / 1.0e6, rnco_idx, freq_mod / 1.0e6, chan_mid / 1.0e6); + + opt_u64_set_val(&d->tx_raw_nco[rnco_idx], freq_mod); } } + if (mod) { res = res ? res : d->tx->ops->option_set(d->tx, "chmap", (uintptr_t)&d->tx_chans); } + return res; } - USDR_LOG("HIPR", USDR_LOG_WARNING, "CH[%d] F=%.3f TX_NCO=%.3f\n", chno, freq / 1.0e6, ncoval / 1.0e6); - return d->st.libcapi79xx_upd_nco(&d->st.capi, NCO_TX, chno, ncoval / 1000, 0, 0); + USDR_LOG("DSDR", USDR_LOG_WARNING, "TX CH[%d => %c: TX%d.Band%d] NCO[%d]=%.3f\n", + ord, w->hwport + 'A', w->dsp_idx, w->dsp_band, + make_afe79xx_idx_from_dsp_wire(w), freq / 1.0e6); + res = res ? res : d->st.libcapi79xx_upd_nco(&d->st.capi, w->dsp_type, w->dsp_idx, ncoval / 1000, 0, w->dsp_band); + return res; } int dev_m2_dsdr_sdr_rx_freq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) @@ -798,7 +1270,7 @@ int dev_m2_dsdr_sdr_rx_freq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t valu return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/rx/freqency", true); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/rx/frequency", true); return dsdr_set_rx_frequency_chan(d, value, obj->full_path[1]); } @@ -810,11 +1282,15 @@ int dev_m2_dsdr_sdr_rx_dsa_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/rx/dsa", true); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/rx/dsa", true); unsigned i = obj->full_path[1]; - int res = d->st.libcapi79xx_set_dsa(&d->st.capi, NCO_RX, i, value); - return res; + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, true, i); + if (!w) { + return -EINVAL; + } + + return d->st.libcapi79xx_set_dsa(&d->st.capi, GET_HWRX_TYPE(w->hwport), GET_HWRX_IDX(w->hwport), value); } int dev_m2_dsdr_sdr_tx_dsa_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) @@ -824,11 +1300,15 @@ int dev_m2_dsdr_sdr_tx_dsa_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/tx/dsa", false); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/tx/dsa", false); unsigned i = obj->full_path[1]; - int res = d->st.libcapi79xx_set_dsa(&d->st.capi, NCO_TX, i, value); - return res; + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, false, i); + if (!w) { + return -EINVAL; + } + + return d->st.libcapi79xx_set_dsa(&d->st.capi, GET_HWTX_TYPE(w->hwport), GET_HWTX_IDX(w->hwport), value); } @@ -839,7 +1319,7 @@ int dev_m2_dsdr_sdr_tx_freq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t valu return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/tx/freqency", false); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/tx/frequency", false); return dsdr_set_tx_frequency_chan(d, value, obj->full_path[1]); } @@ -851,17 +1331,24 @@ int dev_m2_dsdr_gain_rx_set(pdevice_t ud, pusdr_vfs_obj_t UNUSED obj, uint64_t v return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/rx/gain", false); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/rx/gain", true); int res = 0; unsigned i = obj->full_path[1]; unsigned dsa_attn = (value > 25) ? 0 : 50 - 2 * value; unsigned rem_gain = (value > 25) ? value - 25 : 0; + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, true, i); + if (!w) { + return -EINVAL; + } - if (dev_m2_dsdr_has_hiper(d)) { - res = res ? res : dsdr_hiper_fe_rx_gain_set(&d->hiper, s_chanmap_hw_to_fe[i], rem_gain, NULL); + if (dev_m2_dsdr_has_hiper(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + res = res ? res : dsdr_hiper_fe_rx_gain_set(&d->hiper, s_chanmap_hw_to_fe[w->hwport], rem_gain, NULL); + } else if (dev_m2_dsdr_has_extfe0472(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + res = res ? res : ext_fe_rx_gain_set(&d->extfe, s_chanmap_hw_to_fe[w->hwport], rem_gain, NULL); } - res = res ? res : d->st.libcapi79xx_set_dsa(&d->st.capi, NCO_RX, i, dsa_attn); + + res = res ? res : d->st.libcapi79xx_set_dsa(&d->st.capi, GET_HWRX_TYPE(w->hwport), GET_HWRX_IDX(w->hwport), dsa_attn); return res; } @@ -872,12 +1359,23 @@ int dev_m2_dsdr_gain_tx_set(pdevice_t ud, pusdr_vfs_obj_t UNUSED obj, uint64_t v return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/tx/gain", false); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/tx/gain", false); int res = 0; unsigned i = obj->full_path[1]; unsigned dsa_attn = (value > 29) ? 0 : 29 - value; - res = res ? res : d->st.libcapi79xx_set_dsa(&d->st.capi, NCO_TX, i, dsa_attn); + unsigned rem_gain = (value > 25) ? value - 29 : 0; + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, false, i); + if (!w) { + return -EINVAL; + } + + if (dev_m2_dsdr_has_hiper(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + res = res ? res : dsdr_hiper_fe_tx_gain_set(&d->hiper, s_chanmap_hw_to_fe[w->hwport], rem_gain, NULL); + } else if (dev_m2_dsdr_has_extfe0472(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + res = res ? res : ext_fe_tx_gain_set(&d->extfe, s_chanmap_hw_to_fe[w->hwport], rem_gain, NULL); + } + res = res ? res : d->st.libcapi79xx_set_dsa(&d->st.capi, GET_HWTX_TYPE(w->hwport), GET_HWTX_IDX(w->hwport), dsa_attn); return res; } @@ -888,32 +1386,48 @@ int dev_m2_dsdr_gain_rx_auto_set(pdevice_t ud, pusdr_vfs_obj_t UNUSED obj, uint6 return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/rx/gain/auto", false); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/rx/gain/auto", true); int res = 0; unsigned i = obj->full_path[1]; unsigned dsa_attn = (value > 25) ? 0 : 50 - 2 * value; unsigned rem_gain = (value > 25) ? value - 25 : 0; + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, true, i); + if (!w) { + return -EINVAL; + } - if (dev_m2_dsdr_has_hiper(d)) { - res = res ? res : dsdr_hiper_fe_rx_gain_set(&d->hiper, s_chanmap_hw_to_fe[i], rem_gain, NULL); + if (dev_m2_dsdr_has_hiper(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + res = res ? res : dsdr_hiper_fe_rx_gain_set(&d->hiper, s_chanmap_hw_to_fe[w->hwport], rem_gain, NULL); + } else if (dev_m2_dsdr_has_extfe0472(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + res = res ? res : ext_fe_rx_gain_set(&d->extfe, s_chanmap_hw_to_fe[w->hwport], rem_gain, NULL); } - res = res ? res : d->st.libcapi79xx_set_dsa(&d->st.capi, NCO_RX, i, dsa_attn); + res = res ? res : d->st.libcapi79xx_set_dsa(&d->st.capi, GET_HWRX_TYPE(w->hwport), GET_HWRX_IDX(w->hwport), dsa_attn); return res; } int dev_m2_dsdr_gain_rx_lna_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; - if (!dev_m2_dsdr_has_hiper(d)) + if (!dev_m2_dsdr_has_hiper(d) && !dev_m2_dsdr_has_extfe0472(d)) return -ENOTSUP; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/rx/gain/lna", false); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/rx/gain/lna", true); unsigned i = obj->full_path[1]; - int res = dsdr_hiper_fe_rx_gain_set(&d->hiper, s_chanmap_hw_to_fe[i], value, NULL); - return res; + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, true, i); + if (!w) { + return -EINVAL; + } + + if (dev_m2_dsdr_has_hiper(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + return dsdr_hiper_fe_rx_gain_set(&d->hiper, s_chanmap_hw_to_fe[w->hwport], value, NULL); + } else if (dev_m2_dsdr_has_extfe0472(d) && (w->hwport < MAX_HIPER_FE_PORT)) { + return ext_fe_rx_gain_set(&d->extfe, s_chanmap_hw_to_fe[w->hwport], value, NULL); + } + + return -EINVAL; } int dev_m2_dsdr_gain_rx_pga_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) @@ -923,14 +1437,15 @@ int dev_m2_dsdr_gain_rx_pga_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t valu return 0; if (obj->full_path[0]) - return dsdr_iterate_chans(ud, obj, value, "/dm/sdr/0/rx/gain/pga", false); + return dsdr_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/rx/gain/pga", true); unsigned i = obj->full_path[1]; - if (value > 25) - value = 25; - - int res = d->st.libcapi79xx_set_dsa(&d->st.capi, NCO_RX, i, 50 - 2 * value); - return res; + unsigned dsa_attn = (value > 25) ? 0 : 50 - 2 * value; + const channel_logic_dsp_wire_t* w = get_chmapnfo_from_ordinal(d, true, i); + if (!w) { + return -EINVAL; + } + return d->st.libcapi79xx_set_dsa(&d->st.capi, GET_HWRX_TYPE(w->hwport), GET_HWRX_IDX(w->hwport), dsa_attn); } @@ -957,51 +1472,45 @@ static int dsdr_set_rates(dev_m2_dsdr_t* d, uint32_t rx_rate, uint32_t tx_rate) break; } + if (d->tx_activated && tx_inters[i] == 0) { + i = j; + } + d->rxbb_rate = d->adc_rate / rx_decims[i]; d->rxbb_decim = rx_decims[i]; d->txbb_rate = tx_inters[i] == 0 ? 0 : d->dac_rate / tx_inters[i]; d->txbb_inter = tx_inters[i]; - // Reset FIFO after rate change - if (rx_rate) { - res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_RST, 0x2); - } - USDR_LOG("DSDR", USDR_LOG_ERROR, "Set rate: RX %.3f Mhz => %.3f (Decim: %d) -- TX %.3f Mhz => %.3f (Inter: %d)\n", rx_rate / 1.0e6, d->rxbb_rate / 1.0e6, d->rxbb_decim, tx_rate / 1.0e6, d->txbb_rate / 1.0e6, d->txbb_inter); - res = (res) ? res : fgearbox_load_fir(d->base.dev, IGPO_DSPCHAIN_PRG, (fgearbox_firs_t)d->rxbb_decim); - if (res) { - USDR_LOG("LSDR", USDR_LOG_ERROR, "Unable to initialize decimation FIR gearbox, error = %d!\n", res); - return res; - } - - if (d->txbb_inter > 0) { - d->txbb_rate = d->dac_rate / d->txbb_inter; + // Reset FIFO after rate change + if (rx_rate) { + res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_RST, 0x2); + res = (res) ? res : fgearbox_load_fir(d->base.dev, IGPO_DSPCHAIN_PRG, (fgearbox_firs_t)d->rxbb_decim, DSP_USSERIES); + res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_RST, 0x0); - res = (res) ? res : fgearbox_load_fir_i(d->base.dev, IGPO_DSPCHAIN_TX_PRG, (fgearbox_firs_t)d->txbb_inter); if (res) { - USDR_LOG("LSDR", USDR_LOG_ERROR, "Unable to initialize interpolation FIR gearbox, error = %d!\n", res); + USDR_LOG("LSDR", USDR_LOG_ERROR, "Unable to initialize decimation RX FIR gearbox, error = %d!\n", res); return res; } } - if (rx_rate) { - res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_RST, 0x0); - } + if (tx_rate && (d->txbb_inter > 0)) { + d->txbb_rate = d->dac_rate / d->txbb_inter; -#if 0 - for (int i = 0; i < 5; i++) { - uint32_t clk; - res = res ? res : dev_gpi_get32(d->base.dev, 20, &clk); + res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_TX_RST, 0x2); + res = (res) ? res : fgearbox_load_fir_i(d->base.dev, IGPO_DSPCHAIN_TX_PRG, (fgearbox_firs_t)d->txbb_inter, DSP_USSERIES); + res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_TX_RST, 0x0); - USDR_LOG("DSDR", USDR_LOG_ERROR, "Clk %d: %d\n", clk >> 28, clk & 0xfffffff); - usleep(0.5 * 1e6); + if (res) { + USDR_LOG("LSDR", USDR_LOG_ERROR, "Unable to initialize interpolation TX FIR gearbox, error = %d!\n", res); + return res; + } } -#endif return res; } @@ -1024,6 +1533,18 @@ int dev_m2_dsdr_rate_m_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) return dsdr_set_rates(d, rx_rate, tx_rate); } +int dev_m2_dsdr_rate_m_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; + uint32_t *rates = (uint32_t *)(uintptr_t)*ovalue; + + rates[0] = d->rxbb_rate; + rates[1] = d->txbb_rate; + rates[2] = d->adc_rate; + rates[3] = d->dac_rate; + + return 0; +} int dev_m2_dsdr_rate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { @@ -1036,22 +1557,28 @@ static int dsdr_check_fpga_gtrx(dev_m2_dsdr_t* o) uint32_t fpga_jesd = ~0, fpga_err_0 = ~0, fpga_err_1 = ~0; unsigned delay; int res = 0; - res = res ? res : dev_gpi_get32(o->base.dev, IGPI_JESD_SYSREF_RAC, &fpga_jesd); - res = res ? res : dev_gpi_get32(o->base.dev, IGPI_JESD_FPGA_ERR_0, &fpga_err_0); - res = res ? res : dev_gpi_get32(o->base.dev, IGPI_JESD_FPGA_ERR_1, &fpga_err_1); - - delay = (fpga_jesd >> 16) & 0x3ff; - USDR_LOG("DSDR", (fpga_err_0 != 0 || fpga_err_1 != 0) ? USDR_LOG_ERROR : USDR_LOG_INFO, - "FPGA JESD: SYSREF realign TX/RX = %08x Delay = %d PLL Locked %d BUFFER_OVERFLOW: %04x ERRS %04x %04x %04x %04x \n", - fpga_jesd & 0xff, delay, (fpga_jesd >> 26) & 3, fpga_jesd >> 28, - fpga_err_0 >> 16, fpga_err_0 & 0xffff, fpga_err_1 >> 16, fpga_err_1 & 0xffff); - - USDR_LOG("DSDR", USDR_LOG_INFO, "FPGA JESD lanes: 3 2 1 0\n"); - USDR_LOG("DSDR", USDR_LOG_INFO, "Block Header errors: %2d %2d %2d %2d\n", (fpga_err_0 >> 12) & 0xf, (fpga_err_0 >> 8) & 0xf, (fpga_err_0 >> 4) & 0xf, (fpga_err_0 >> 0) & 0xf); - USDR_LOG("DSDR", USDR_LOG_INFO, "End of Multi-Block errors: %2d %2d %2d %2d\n", (fpga_err_0 >> 28) & 0xf, (fpga_err_0 >> 24) & 0xf, (fpga_err_0 >> 20) & 0xf, (fpga_err_0 >> 16) & 0xf); - USDR_LOG("DSDR", USDR_LOG_INFO, "End of Extended Multi-Block errors: %2d %2d %2d %2d\n", (fpga_err_1 >> 12) & 0xf, (fpga_err_1 >> 8) & 0xf, (fpga_err_1 >> 4) & 0xf, (fpga_err_1 >> 0) & 0xf); - USDR_LOG("DSDR", USDR_LOG_INFO, "CRC mismatch errors: %2d %2d %2d %2d\n", (fpga_err_1 >> 28) & 0xf, (fpga_err_1 >> 24) & 0xf, (fpga_err_1 >> 20) & 0xf, (fpga_err_1 >> 16) & 0xf); + for (unsigned q = 0; q < (o->jesd_x8 ? 2 : 1); q++) { + if (o->jesd_x8) { + res = res ? res : dev_gpo_set(o->base.dev, IGPO_TIAFE_MASTER_RESET_N, q == 0 ? 0x01 : 0x81); + } + + res = res ? res : dev_gpi_get32(o->base.dev, IGPI_JESD_SYSREF_RAC, &fpga_jesd); + res = res ? res : dev_gpi_get32(o->base.dev, IGPI_JESD_FPGA_ERR_0, &fpga_err_0); + res = res ? res : dev_gpi_get32(o->base.dev, IGPI_JESD_FPGA_ERR_1, &fpga_err_1); + + delay = (fpga_jesd >> 16) & 0x3ff; + USDR_LOG("DSDR", (fpga_err_0 != 0 || fpga_err_1 != 0) ? USDR_LOG_ERROR : USDR_LOG_INFO, + "FPGA JESD_QUAD[%d]: SYSREF realign TX/RX = %08x Delay = %d PLL Locked %d BUFFER_OVERFLOW: %04x ERRS %04x %04x %04x %04x \n", q, + fpga_jesd & 0xff, delay, (fpga_jesd >> 26) & 3, fpga_jesd >> 28, + fpga_err_0 >> 16, fpga_err_0 & 0xffff, fpga_err_1 >> 16, fpga_err_1 & 0xffff); + + USDR_LOG("DSDR", USDR_LOG_INFO, "FPGA JESD lanes: %s\n", q == 0 ? " 3 2 1 0" : " 7 6 5 4"); + USDR_LOG("DSDR", USDR_LOG_INFO, "Block Header errors: %2d %2d %2d %2d\n", (fpga_err_0 >> 12) & 0xf, (fpga_err_0 >> 8) & 0xf, (fpga_err_0 >> 4) & 0xf, (fpga_err_0 >> 0) & 0xf); + USDR_LOG("DSDR", USDR_LOG_INFO, "End of Multi-Block errors: %2d %2d %2d %2d\n", (fpga_err_0 >> 28) & 0xf, (fpga_err_0 >> 24) & 0xf, (fpga_err_0 >> 20) & 0xf, (fpga_err_0 >> 16) & 0xf); + USDR_LOG("DSDR", USDR_LOG_INFO, "End of Extended Multi-Block errors: %2d %2d %2d %2d\n", (fpga_err_1 >> 12) & 0xf, (fpga_err_1 >> 8) & 0xf, (fpga_err_1 >> 4) & 0xf, (fpga_err_1 >> 0) & 0xf); + USDR_LOG("DSDR", USDR_LOG_INFO, "CRC mismatch errors: %2d %2d %2d %2d\n", (fpga_err_1 >> 28) & 0xf, (fpga_err_1 >> 24) & 0xf, (fpga_err_1 >> 20) & 0xf, (fpga_err_1 >> 16) & 0xf); + } return res; } @@ -1105,7 +1632,7 @@ int _debug_lmk05318_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) return 0; } - if (value & 0x800000) { + if (!(value & 0x800000)) { res = lmk05318_reg_wr(&o->lmk, addr, data); USDR_LOG("XDEV", USDR_LOG_WARNING, "LMK05318 WR REG %04x => %04x\n", @@ -1138,7 +1665,7 @@ int dev_m2_dsdr_debug_clk_info_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t * { struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; int res = 0; - uint32_t clk; + uint32_t clk = 0; res = res ? res : dev_gpi_get32(d->base.dev, 20, &clk); *value = clk & 0xfffffff; @@ -1154,17 +1681,45 @@ int dev_m2_dsdr_debug_lldev_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ova return 0; } -int dev_m2_dsdr_senstemp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue) +int dev_m2_dsdr_senstemp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue) +{ + struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; + int res = 0; + + uint64_t fe_temp = 0; + unsigned board_temp = 0; + + if (dev_m2_dsdr_has_hiper(d)) { + res = res ? res : dsdr_hiper_fe_get_temp_max(&d->hiper, &fe_temp); + } else if (dev_m2_dsdr_has_extfe0472(d)) { + res = res ? res : ext_fe_get_temp_max(&d->extfe, &fe_temp); + } + + if (d->type == DSDR_M2_R1) { + res = res ? res : tmp114_temp_get(d->base.dev, d->subdev, I2C_TEMP_AFE, (int*)&board_temp); + } else if (!dev_m2_dsdr_has_hiper(d) && !dev_m2_dsdr_has_extfe0472(d)) { + // No temp sensors + return -EINVAL; + } else { + *ovalue = fe_temp; + return res; + } + + *ovalue = fe_temp > board_temp ? fe_temp : board_temp; + return res; +} + +int dev_m2_dsdr_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { - // struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; - // lldev_t dev = d->base.dev; - // int temp, res; - // res = tmp108_temp_get(dev, 0, I2C_BUS_TMP108, &temp); - // if (res) - // return res; + struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; + int res = -ENOTSUP; - // *ovalue = (int64_t)temp; - return 0; + if (dev_m2_dsdr_has_hiper(d)) { + res = res ? res : dsdr_hiper_fe_set_dac(&d->hiper, value); + } else if (dev_m2_dsdr_has_extfe0472(d)) { + res = res ? res : ext_fe_set_dac(&d->extfe, value); + } + return res; } int dev_m2_dsdr_debug_all_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) @@ -1184,18 +1739,34 @@ static int dev_m2_dsdr_debug_rxtime_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint6 return 0; } +static int afe_update_chan_pwr(struct dev_m2_dsdr *d) +{ + int res = 0; + res = (d->st.libcapi79xx_set_tdd == NULL) ? -EINVAL : 0; + res = res ? res : d->st.libcapi79xx_set_tdd(&d->st.capi, d->hw_enabled_rx & 0xf, (d->hw_enabled_rx >> 4) & 0x3, d->hw_enabled_tx); + res = res ? res : dev_gpo_set(d->base.dev, IGPO_TX_CHEN, d->hw_enabled_tx); + res = res ? res : dev_gpo_set(d->base.dev, IGPO_RX_CHEN, d->hw_enabled_rx); -static -void usdr_device_m2_dsdr_destroy(pdevice_t udev) + USDR_LOG("XDEV", USDR_LOG_ERROR, "DSDR AFE CHANNEL POWER RX:%x TX:%x\n", d->hw_enabled_rx, d->hw_enabled_tx); + return res; +} + +static void usdr_device_m2_dsdr_destroy(pdevice_t udev) { struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)udev; lldev_t dev = d->base.dev; dev_m2_dsdr_afe_health_get(udev, NULL, NULL); + d->hw_enabled_rx = 0; + d->hw_enabled_tx = 0; + afe_update_chan_pwr(d); + // FE Power OFF if (dev_m2_dsdr_has_hiper(d)) { dsdr_hiper_fe_destroy(&d->hiper); + } else if (dev_m2_dsdr_has_extfe0472(d)) { + ext_fe_destroy(&d->extfe); } dev_gpo_set(dev, IGPO_AFE_RST, 0x1); @@ -1217,42 +1788,67 @@ void usdr_device_m2_dsdr_destroy(pdevice_t udev) // Activity LED off dev_gpo_set(dev, IGPO_BANK_LEDS, 0); + // Put JESD into power-down mode + dev_gpo_set(dev, IGPO_TIAFE_MASTER_RESET_N, 0); + dsdr_gth_control(dev, GTH_CTR_TX_EN, 0); + dsdr_gth_control(dev, GTH_CTR_RX_EN, 0); + dsdr_gth_control(dev, GTH_CTR_QPLL_EN, 0); + usdr_device_base_destroy(udev); } +static unsigned make_quad_pwr_mask(unsigned enabled_ch) +{ + unsigned out = 0; + if (enabled_ch & 0xf) + out |= 0xf; + if (enabled_ch & 0xf0) + out |= 0xf0; + + return out; +} + static int usdr_jesd204b_bringup_pre(struct dev_m2_dsdr *dd) { lldev_t dev = dd->base.dev; int res = 0; - uint32_t d; + uint32_t d = 0; bool pll_ready = false; + res = res ? res : dsdr_gth_control(dev, GTH_CTR_TX_EN, 0xffff); + res = res ? res : dsdr_gth_control(dev, GTH_CTR_RX_EN, 0xffff); + res = res ? res : dsdr_gth_control(dev, GTH_CTR_QPLL_EN, 0b0101); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_SYNC_RESET, 1); res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_SYNC_RESET, 1); res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_MASTER_RESET_N, 0); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_ENABLED, dd->hw_fpga_jesd_rx_en); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_ENABLED, dd->hw_fpga_jesd_tx_en); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_BUFFER_RELDLY_0, 0); // 0 means autodetect and adjust + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_BUFFER_RELDLY_1, 0); // 0 means autodetect and adjust - res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_MAP_0, 0x10); - res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_MAP_1, 0x32); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_MAP_0, dd->cfg_rx_lanemap & 0xff); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_MAP_1, (dd->cfg_rx_lanemap >> 8) & 0xff); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_MAP_2, (dd->cfg_rx_lanemap >> 16) & 0xff); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_MAP_3, (dd->cfg_rx_lanemap >> 24) & 0xff); - res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_MAP_0, 0x10); - res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_MAP_1, 0x32); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_MAP_0, dd->cfg_tx_lanemap & 0xff); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_MAP_1, (dd->cfg_tx_lanemap >> 8) & 0xff); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_MAP_2, (dd->cfg_tx_lanemap >> 16) & 0xff); + res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_MAP_3, (dd->cfg_tx_lanemap >> 24) & 0xff); res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_POLARITY, 0x0); res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_POLARITY, 0x0); - res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_LANE_ENABLED, dd->hw_fpga_jesd_rx_en); - res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_LANE_ENABLED, dd->hw_fpga_jesd_tx_en); - - usleep(1); + res = res ? res : usleep(1000); res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_MASTER_RESET_N, 1); for (unsigned k = 0; k < 100; k++) { - usleep(10000); - - res = dev_gpi_get32(dev, IGPI_JESD_SYSREF_RAC, &d); + res = res ? res : usleep(10000); + res = res ? res : dev_gpi_get32(dev, IGPI_JESD_SYSREF_RAC, &d); USDR_LOG("DSDR", USDR_LOG_ERROR, "STAT = %08x\n", d); if (d & 0x08000000) { pll_ready = true; @@ -1265,13 +1861,18 @@ static int usdr_jesd204b_bringup_pre(struct dev_m2_dsdr *dd) return -EIO; } + res = res ? res : usleep(100000); //TODO check + // TODO wait for PLL to lock.. res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_TX_SYNC_RESET, 0); - usleep(10000); + res = res ? res : usleep(10000); res = res ? res :dev_gpi_get32(dev, IGPI_JESD_SYSREF_RAC, &d); USDR_LOG("DSDR", USDR_LOG_ERROR, "STAT = %08x\n", d); + + // Disable unused lanes after GTH reset is done. GTWiz expects all lanes to be initialized, otherwise DONE signal won't be asserted + res = res ? res : dsdr_gth_control(dev, GTH_CTR_TX_EN, make_quad_pwr_mask(dd->hw_fpga_jesd_tx_en | dd->hw_fpga_jesd_rx_en)); return res; } @@ -1282,15 +1883,127 @@ static int usdr_jesd204b_bringup_post(struct dev_m2_dsdr *dd) uint32_t d = 0; res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_SYNC_RESET, 0); + res = res ? res : usleep(10000); - usleep(10000); + //res = res ? res : dsdr_gth_control(dev, GTH_CTR_RX_EN, dd->hw_fpga_jesd_rx_en); + res = res ? res : dsdr_gth_control(dev, GTH_CTR_RX_EN, make_quad_pwr_mask(dd->hw_fpga_jesd_rx_en)); res = res ? res : dev_gpi_get32(dev, IGPI_JESD_SYSREF_RAC, &d); USDR_LOG("DSDR", USDR_LOG_ERROR, "STAT = %08x\n", d); + return res; +} + +enum jesd_rates { + RATE_245_76 = 245760000, + RATE_368_64 = 368640000, + RATE_491_52 = 491520000, +}; + +static const uint16_t s_gth_qpll_ovrd_491[] = { 0x0008, 0x333c, 0x000d, 0x0f00, 0x011, 0x87c0, 0x0014, 0x0040, 0x0019, 0x031d, 0x001b, 0x87c0, 0x0030, 0x0045}; +static const uint16_t s_gth_qpll_ovrd_369[] = { 0x0008, 0x331c, 0x000d, 0x0800, 0x011, 0x87c1, 0x0014, 0x0040, 0x0019, 0x033f, 0x001b, 0x87c1, 0x0030, 0x0004}; +//static const uint16_t s_gth_qpll_ovrd_245[] = { 0x0008, 0x333c, 0x000d, 0x0f00, 0x011, 0x87c0, 0x0014, 0x0082, 0x0019, 0x031d, 0x001b, 0x87c0, 0x0030, 0x0045}; + +static int dsdr_gth_set_rate(struct dev_m2_dsdr *d, unsigned rate) +{ + int res = 0; + const uint16_t* mode; + switch (rate) { + //case RATE_245_76: mode = s_gth_qpll_ovrd_245; break; + case RATE_368_64: mode = s_gth_qpll_ovrd_369; break; + case RATE_491_52: mode = s_gth_qpll_ovrd_491; break; + default: + return -EINVAL; + } + + res = res ? res : dsdr_gth_control(d->base.dev, GTH_CTR_TX_EN, 0); + res = res ? res : dsdr_gth_control(d->base.dev, GTH_CTR_RX_EN, 0); + res = res ? res : dsdr_gth_control(d->base.dev, GTH_CTR_QPLL_EN, 0); + + for (unsigned i = 0; i < SIZEOF_ARRAY(s_gth_qpll_ovrd_369); i += 2) { + res = res ? res : dsdr_drp_reg_wr(d->base.dev, DRP_QPLL_IDX0, mode[i + 0], mode[i + 1]); + res = res ? res : dsdr_drp_reg_wr(d->base.dev, DRP_QPLL_IDX1, mode[i + 0], mode[i + 1]); + } + +#if 0 + switch (rate) { + case RATE_245_76: mode = s_gth_chan_ovrd_245; break; + case RATE_368_64: + case RATE_491_52: mode = s_gth_chan_ovrd_491; break; + default: + return -EINVAL; + } + for (unsigned i = 0; i < SIZEOF_ARRAY(s_gth_chan_ovrd_245); i += 2) { + for (unsigned j = 0; j < 8; j++) { + res = res ? res : dsdr_drp_reg_wr(d->base.dev, DRP_CHAN_IDX0 + j, mode[i + 0], mode[i + 1]); + res = res ? res : usleep(100); + } + } +#endif + return res; } +enum jesd_special_flags { + MODE_JESD204C = 0, + MODE_JESD204B = 2, + + // AFE_COMPAT_7900 = 1 << 8, + AFE_COMPAT_7901 = 1 << 9, + // AFE_COMPAT_7903 = 1 << 10, + AFE_COMPAT_7950 = 1 << 11, + + FPGA_COMPAT_4X_4X = 1 << 16, + FPGA_COMPAT_8X_4X = 1 << 17, + FPGA_COMPAT_8X_8X = 1 << 18, +}; + +struct jesd_config +{ + unsigned flags; + unsigned afe_jesd_rate; + + unsigned logic_chcnt_rx; + unsigned logic_chcnt_tx; + unsigned hw_chcnt_rx; + unsigned hw_chcnt_tx; + unsigned hw_fpga_jesd_rx_en; + unsigned hw_fpga_jesd_tx_en; + + const channel_map_info_t *rx_chmap; + const channel_map_info_t *tx_chmap; + const channel_logic_dsp_wire_t* rx_lchan; + const channel_logic_dsp_wire_t* tx_lchan; + + const char* config_name; + const char* config_rev_0x20; // Configuration file for 2.0 chip + const char* config_rev_0x13; // Configuration file for 1.3 chip + +}; + +struct jesd_config s_hwjesd_config[] = { + { MODE_JESD204C | AFE_COMPAT_7901 | AFE_COMPAT_7950 | FPGA_COMPAT_4X_4X | FPGA_COMPAT_8X_4X | FPGA_COMPAT_8X_8X, + RATE_491_52, 4, 4, 4, 4, 0x0f, 0x0f, s_dsdr_chmap_s_nco, s_dsdr_chmap_s_nco, s_dsdr_lmap_s_ncorx, s_dsdr_lmap_s_ncotx, "4RX_4TX_491.52", "Afe79xxPg1_6664_491_4x1_4x1_20.txt", "Afe79xxPg1_6664_491_4x1_4x1_13.txt" }, + + { MODE_JESD204C | AFE_COMPAT_7901 | AFE_COMPAT_7950 | FPGA_COMPAT_4X_4X | FPGA_COMPAT_8X_4X | FPGA_COMPAT_8X_8X, + RATE_368_64, 4, 4, 4, 4, 0x0f, 0x0f, s_dsdr_chmap_s_nco, s_dsdr_chmap_s_nco, s_dsdr_lmap_s_ncorx, s_dsdr_lmap_s_ncotx, "4RX_4TX_368.64", "Afe79xxPg1_6664_368_4x1_4x1_20.txt", "Afe79xxPg1_6664_368_4x1_4x1_13.txt" }, + + { MODE_JESD204C | AFE_COMPAT_7901 | AFE_COMPAT_7950 | FPGA_COMPAT_8X_8X, + RATE_368_64, 8, 8, 4, 4, 0xff, 0xff, s_dsdr_chmap_d_nco, s_dsdr_chmap_d_nco, s_dsdr_lmap_d_ncorx, s_dsdr_lmap_d_ncotx, "8RX_8TX_368.64", "Afe79xxPg1_6664_368_4x2_4x2_20.txt", "Afe79xxPg1_6664_368_4x2_4x2_13.txt" }, + + { MODE_JESD204C | AFE_COMPAT_7901 | AFE_COMPAT_7950 | FPGA_COMPAT_8X_8X | FPGA_COMPAT_8X_4X, + RATE_368_64, 8, 4, 4, 4, 0xff, 0x0f, s_dsdr_chmap_d_nco, s_dsdr_chmap_s_nco, s_dsdr_lmap_d_ncorx, s_dsdr_lmap_s_ncotx, "8RX_4TX_368.64", "Afe79xxPg1_6664_368_4x2_4x1_20.txt", "Afe79xxPg1_6664_368_4x2_4x1_13.txt" }, + + { MODE_JESD204C | AFE_COMPAT_7950 | FPGA_COMPAT_8X_4X | FPGA_COMPAT_8X_8X, + RATE_491_52, 8, 4, 6, 4, 0x03f, 0x0f, s_dsdr_chmap_s_nco_fb, s_dsdr_chmap_s_nco, s_dsdr_lmap_s_nco_fb, s_dsdr_lmap_s_ncotx, "4RX_4TX_2FB_491.52", "Afe79xxPg1_6664_491_6x1_4x1_20.txt", "Afe79xxPg1_6664_491_6x1_4x1_13.txt" }, + + { MODE_JESD204C | AFE_COMPAT_7950 | FPGA_COMPAT_8X_4X | FPGA_COMPAT_8X_8X, + RATE_368_64, 8, 4, 6, 4, 0x03f, 0x0f, s_dsdr_chmap_s_nco_fb, s_dsdr_chmap_s_nco, s_dsdr_lmap_s_nco_fb, s_dsdr_lmap_s_ncotx, "4RX_4TX_2FB_368.64", "Afe79xxPg1_6664_368_6x1_4x1_20.txt", "Afe79xxPg1_6664_368_6x1_4x1_13.txt" }, + + { MODE_JESD204C | AFE_COMPAT_7950 | FPGA_COMPAT_8X_4X | FPGA_COMPAT_8X_8X, + RATE_368_64, 8, 4, 4, 4, 0x3f, 0x0f, s_dsdr_chmap_s_nco_fbrx, s_dsdr_chmap_s_nco, s_dsdr_lmap_s_nco_fbrx, s_dsdr_lmap_s_ncotx, "4RX2FBRX_4TX_368.64", "Afe79xxPg1_6664_368_6x1FBRX_4x1_20.txt", "Afe79xxPg1_6664_368_6x1FBRX_4x1_13.txt" }, +}; + static int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** devparam, const char** devval) { @@ -1298,13 +2011,20 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** lldev_t dev = d->base.dev; int res = 0; uint32_t hwid, usr2, pg, los, devid, jesdv; + unsigned afeType = 0; + const char* usr_config = NULL; + + for (unsigned i = 0; i < pcount; i++) { + if (strcmp(devparam[i], "afe_profile") == 0) { + usr_config = devval[i]; + } + } + if (usr_config == NULL) { + usr_config = getenv("AFE_PROFILE"); + } d->subdev = 0; - d->hw_mask_fb = 0; - d->hw_mask_rx = 0xf; // RX_3 RX_2 RX_1 RX_0 - d->hw_mask_tx = 0xf; // TX_3 TX_2 TX_1 TX_0 - d->hw_fpga_jesd_rx_en = 0xf; - d->hw_fpga_jesd_tx_en = 0xf; + d->dsdr_state = STATE_IDLE; res = res ? res : dev_gpi_get32(dev, IGPI_USR_ACCESS2, &usr2); res = res ? res : dev_gpi_get32(dev, IGPI_HWID, &hwid); @@ -1312,67 +2032,167 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** return res; } - // TODO check for AFE7903 - if (getenv("DSDR_AFE7903")) { - d->hw_mask_rx = 0x5; // RX_3 RX_1 - d->hw_mask_tx = 0xA; // TX_4 TX_2 + res = res ? res : dev_gpo_set(dev, IGPO_AFE_RST, 0x0); + res = res ? res : dev_gpo_set(dev, IGPO_PWR_AFE, 0x0); + res = res ? res : dev_gpo_set(dev, IGPO_PWR_LMK, 0x0); + res = res ? res : dev_gpo_set(dev, IGPO_RX_CHEN, 0x0); + res = res ? res : dev_gpo_set(dev, IGPO_TX_CHEN, 0x0); + res = res ? res : dev_gpo_set(dev, IGPO_TX_AFETDD, 0x0); + res = res ? res : dev_gpo_set(dev, IGPO_RX_AFETDD, 0x0); - d->hw_fpga_jesd_rx_en = 0xc; - d->hw_fpga_jesd_tx_en = 0xc; - } + if (res) + return res; + + // TODO check for AFE7903 + //if (getenv("DSDR_AFE7903")) { + // d->hw_mask_rx = 0x5; // RX_3 RX_1 + // d->hw_mask_tx = 0xA; // TX_4 TX_2 + //} devid = (hwid >> 16) & 0xff; jesdv = (hwid >> 8) & 0xff; + bool dpump_afe_clk = false; + if (jesdv & 0x08) { + dpump_afe_clk = true; + jesdv ^= 0x08; + } + bool gt_is_gty = false; + if (jesdv & 0x04) { + gt_is_gty = true; + jesdv ^= 0x04; + } + switch (devid) { case DSDR_KCU116_EVM: case DSDR_M2_R0: + case DSDR_M2_R1: case DSDR_PCIE_HIPER_R0: d->type = devid; break; - case 0xff: - d->type = DSDR_PCIE_HIPER_R0; - break; - default: USDR_LOG("XDEV", USDR_LOG_ERROR, "Unsupported HWID = %08x, skipping initialization!\n", hwid); return -EIO; } - switch (jesdv) { - case DSDR_JESD204B_810_245: - d->max_rate = 260e6; - d->dac_rate = d->adc_rate = 245760000; - d->afecongiguration = "Afe79xxPg1_02.txt"; - break; + if (getenv("DSDR_M2_R0")) { + // Old HW revision + d->type = DSDR_M2_R0; + } - case DSDR_JESD204C_6664_245: - d->max_rate = 260e6; - d->dac_rate = d->adc_rate = 245760000; - d->afecongiguration = "Afe79xxPg1_6664_245.txt"; - break; + d->cfg_rx_lanemap = 0x76543210; + d->cfg_tx_lanemap = 0x76543210; - case DSDR_JESD204C_6664_491: - d->max_rate = 520e6; - d->dac_rate = d->adc_rate = 491520000; - d->afecongiguration = "Afe79xxPg1_6664_491.txt"; - if (d->hw_mask_rx == 0x5 && d->hw_mask_tx == 0xA) { - d->afecongiguration = "Afe79xxPg1_dsdr_491_7903.txt"; - } - break; + //gth_dump_all_regs(d); + unsigned master_rate; + unsigned dsp_rx_chans; + unsigned dsp_tx_chans; + bool gt_dyn = false; + const char* default_config; + bool jesd_204c_mode = true; + unsigned chip_rev = 0x20; //Default chip revision + + switch (jesdv) { + case JESD_MODE_4X_4X_491: dsp_rx_chans = 4; dsp_tx_chans = 4; default_config = "4RX_4TX_491.52"; master_rate = 491520000; break; + case JESD_MODE_4X_4X_DYN: dsp_rx_chans = 4; dsp_tx_chans = 4; default_config = "4RX_4TX_491.52"; master_rate = 0; gt_dyn = true; break; + case JESD_MODE_8X_4X_DYN: dsp_rx_chans = 8; dsp_tx_chans = 4; default_config = "4RX_4TX_491.52"; master_rate = 0; gt_dyn = true; break; + case JESD_MODE_8X_8X_DYN: dsp_rx_chans = 8; dsp_tx_chans = 8; default_config = "4RX_4TX_491.52"; master_rate = 0; gt_dyn = true; break; default: - USDR_LOG("XDEV", USDR_LOG_ERROR, "Unsupported JESD type %x (HWID = %08x), skipping initialization!\n", jesdv, hwid); + USDR_LOG("XDEV", USDR_LOG_ERROR, "Unsupported JESD - DSP configuration: %08x!\n", jesdv); return -EIO; } - d->jesdv = jesdv; - USDR_LOG("XDEV", USDR_LOG_WARNING, "AFE type JESD204%c CH_TX=%02x CH_RX=%02x\n", (jesdv == DSDR_JESD204B_810_245) ? 'B' : 'C', d->hw_mask_tx, d->hw_mask_rx); + if (usr_config == NULL) { + usr_config = default_config; + } + unsigned cfg_idx = ~0U; + for (unsigned i = 0; i < SIZEOF_ARRAY(s_hwjesd_config); i++) { + if (strcmp(s_hwjesd_config[i].config_name, usr_config) == 0) { + bool cfg_mode_204c = (s_hwjesd_config[i].flags & MODE_JESD204B) ? false : true; + if (jesd_204c_mode != cfg_mode_204c) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "Configuration `%s` JESD204%c != HW JESD204%c!\n", + s_hwjesd_config[i].config_name, cfg_mode_204c ? 'C' : 'B', jesd_204c_mode ? 'C' : 'B'); + return -EINVAL; + } + + if (master_rate != 0 && master_rate != s_hwjesd_config[i].afe_jesd_rate) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "Configuration `%s` rate mismatch %d != HW %d!\n", + s_hwjesd_config[i].config_name, s_hwjesd_config[i].afe_jesd_rate, master_rate); + return -EINVAL; + } + + if (s_hwjesd_config[i].logic_chcnt_rx > dsp_rx_chans || s_hwjesd_config[i].logic_chcnt_tx > dsp_tx_chans) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "Configuration `%s` insuficcient DSP chains in firmware: RXx%d TXx%d, required %d %d!\n", + s_hwjesd_config[i].config_name, dsp_rx_chans, dsp_tx_chans, s_hwjesd_config[i].logic_chcnt_rx, s_hwjesd_config[i].logic_chcnt_tx); + return -EINVAL; + } + + cfg_idx = i; + break; + } + } + + if (cfg_idx == ~0U) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "Configuration `%s` not found!\n", usr_config); + return -EINVAL; + } + + master_rate = s_hwjesd_config[cfg_idx].afe_jesd_rate; + + d->rx_chmap_info = s_hwjesd_config[cfg_idx].rx_chmap; + d->tx_chmap_info = s_hwjesd_config[cfg_idx].tx_chmap; + d->rx_lmap_info = s_hwjesd_config[cfg_idx].rx_lchan; + d->tx_lmap_info = s_hwjesd_config[cfg_idx].tx_lchan; + + d->logic_chcnt_rx = s_hwjesd_config[cfg_idx].logic_chcnt_rx; + d->logic_chcnt_tx = s_hwjesd_config[cfg_idx].logic_chcnt_tx; + d->hw_chcnt_rx = s_hwjesd_config[cfg_idx].hw_chcnt_rx; + d->hw_chcnt_tx = s_hwjesd_config[cfg_idx].hw_chcnt_tx; + d->hw_fpga_jesd_rx_en = s_hwjesd_config[cfg_idx].hw_fpga_jesd_rx_en; + d->hw_fpga_jesd_tx_en = s_hwjesd_config[cfg_idx].hw_fpga_jesd_tx_en; + + d->dac_rate = d->adc_rate = master_rate; + d->max_rate = master_rate * 1.15; + + d->dsp_rx_chans = dsp_rx_chans; + d->dsp_tx_chans = dsp_tx_chans; + + d->jesd_x8 = (dsp_rx_chans > 4) || (dsp_tx_chans > 4); + + d->hw_enabled_rx = 0; + d->hw_enabled_tx = 0; + + // TODO Proper AFE revision detection + afeType = 7901; + if (getenv("DSDR_M2_7950")) { + afeType = 7950; + chip_rev = 0x13; + } + + d->afecongiguration = (chip_rev == 0x20) ? s_hwjesd_config[cfg_idx].config_rev_0x20 : s_hwjesd_config[cfg_idx].config_rev_0x13; + if (!d->afecongiguration) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "Configuration `%s` File not defined for chirev %02x!\n", s_hwjesd_config[cfg_idx].config_name, chip_rev); + return -EINVAL; + } + + if (gt_dyn) { + if (!gt_is_gty) { + res = res ? res : dsdr_gth_set_rate(d, master_rate); + } else { + USDR_LOG("XDEV", USDR_LOG_ERROR, "GTY reconfiguration isn't supported yet!\n"); + //return -EINVAL; + } + } + if (res) + return res; + + USDR_LOG("XDEV", USDR_LOG_INFO, "DSP_RX=%d DSP_TX=%d AFE=%d Configuration `%s` RATE=%.2f == FILE:`%s`\n", + dsp_rx_chans, dsp_tx_chans, afeType, s_hwjesd_config[cfg_idx].config_name, master_rate / 1.0e6, d->afecongiguration); if (getenv("SKIPAFE")) { d->type = DSDR_KCU116_EVM; } - if (d->type == DSDR_KCU116_EVM) { USDR_LOG("XDEV", USDR_LOG_ERROR, "Skipping AFE initialization! SR=%.2f\n", d->adc_rate / 1e6); res = res ? res : afe79xx_create_dummy(&d->st); @@ -1403,8 +2223,8 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** return res; } - - res = res ? res : dev_gpo_set(dev, IGPO_PWR_LMK, 0xf); + // Put LMK into PD but enable all LDOs to settle + res = res ? res : dev_gpo_set(dev, IGPO_PWR_LMK, 0xbf); for (unsigned j = 0; j < 10; j++) { usleep(10000); @@ -1413,49 +2233,110 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** break; } - if (d->type == DSDR_M2_R0) { + if (d->type == DSDR_M2_R0 || d->type == DSDR_M2_R1) { + bool pg; for (unsigned j = 0; j < 20; j++) { usleep(10000); res = res ? res : tps6381x_init(dev, d->subdev, I2C_TPS63811, true, true, 3450); if (res == 0) break; } + if (res) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "Unable to intialize tps6381x booster!\n"); + return res; + } + + for (unsigned j = 0; j < 20; j++) { + res = res ? res : tps6381x_check_pg(dev, d->subdev, I2C_TPS63811, &pg); + if (!res || pg) { + break; + } + usleep(20000); + } + if (!pg) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "No PG signal in tps6381x booster!\n"); + return -EIO; + } } - usleep(40000); + usleep(20000); + res = res ? res : dev_gpo_set(dev, IGPO_PWR_LMK, 0xff); + usleep(200000); - for (unsigned j = 0; j < 25; j++) { - usleep(40000); - res = res ? res : lmk05318_create(dev, d->subdev, I2C_LMK, - (d->type == DSDR_PCIE_HIPER_R0) ? 2 : 1 /* TODO FIXME!!! */, &d->lmk); - if (res == 0) - break; + // + //LMK05318 init start + + //set true to enable IN_REF1 40M + bool enable_in_ref = false; + + lmk05318_dpll_settings_t dpll; + memset(&dpll, 0, sizeof(dpll)); + dpll.enabled = enable_in_ref; + dpll.en[LMK05318_PRIREF] = true; + dpll.fref[LMK05318_PRIREF] = 40000000; + dpll.type[LMK05318_PRIREF] = DPLL_REF_TYPE_DIFF_NOTERM; + dpll.dc_mode[LMK05318_PRIREF] = DPLL_REF_DC_COUPLED_INT; + dpll.buf_mode[LMK05318_PRIREF] = DPLL_REF_AC_BUF_HYST50_DC_EN; + + lmk05318_out_config_t lmk_out[8]; + + lmk05318_port_request(&lmk_out[0], 0, master_rate, false, OUT_OFF); + lmk05318_port_request(&lmk_out[1], 1, master_rate, false, LVDS); + lmk05318_port_request(&lmk_out[2], 2, 3840000, false, LVDS); + lmk05318_port_request(&lmk_out[3], 3, 3840000, false, OUT_OFF); + lmk05318_port_request(&lmk_out[4], 4, 0, false, OUT_OFF); + lmk05318_port_request(&lmk_out[5], 5, d->dac_rate / 2, false, LVDS); + lmk05318_port_request(&lmk_out[6], 6, 3840000, false, LVDS); + lmk05318_port_request(&lmk_out[7], 7, dpump_afe_clk ? d->dac_rate : d->dac_rate / 2, false, LVDS); + + res = lmk05318_create(dev, d->subdev, I2C_LMK, (d->type == DSDR_PCIE_HIPER_R0) ? 52000000 : 26000000, XO_CMOS, + false, &dpll, lmk_out, SIZEOF_ARRAY(lmk_out), &d->lmk, false /*dry_run*/); + if(res) + return res; + + // wait for PRIREF/SECREF validation + res = lmk05318_wait_dpll_ref_stat(&d->lmk, 100000); + if (res) { + USDR_LOG("DSDR", USDR_LOG_ERROR, "LMK03518 DPLL input reference freqs are not validated during specified timeout"); + return res; } - // Update deviders for 245/491MSPS rate - if (d->jesdv == DSDR_JESD204C_6664_491) { - // GT should be 245.76 - // FPGA SYSCLK should be 245.76 - res = res ? res : lmk05318_set_out_div(&d->lmk, LMK_FPGA_GT_AFEREF, 4); - res = res ? res : lmk05318_set_out_div(&d->lmk, LMK_FPGA_1PPS, 4); + //res = res ? res : dev_gpo_set(dev, IGPO_PWR_LMK, 0x7f); + + //wait for lock + res = lmk05318_wait_apll1_lock(&d->lmk, 200000); + res = res ? res : lmk05318_wait_apll2_lock(&d->lmk, 200000); + res = res ? res : lmk05318_check_lock(&d->lmk, &los, false /*silent*/); //just to log state + + if(res) + { + USDR_LOG("DSDR", USDR_LOG_ERROR, "LMK03518 PLLs not locked during specified timeout"); + return res; } - usleep(1000); + //sync to make APLL1/APLL2 & out channels in-phase + res = lmk05318_sync(&d->lmk); + //usleep(1000); + + //res = res ? res : dev_gpo_set(dev, IGPO_PWR_LMK, 0x7f); + //usleep(1000); + //res = res ? res : dev_gpo_set(dev, IGPO_PWR_LMK, 0xff); + //if(res) + // return res; - res = res ? res : lmk05318_check_lock(&d->lmk, &los); + USDR_LOG("DSDR", USDR_LOG_INFO, "LMK03518 outputs synced, LOS=%x", los); + //LMK05318 init end + // for (int i = 0; i < 5; i++) { uint32_t clk = 0; res = res ? res : dev_gpi_get32(d->base.dev, 20, &clk); USDR_LOG("DSDR", USDR_LOG_ERROR, "Clk %d: %d\n", clk >> 28, clk & 0xfffffff); - usleep(0.5 * 1e6); + res = res ? res : usleep(0.5 * 1e6); } - res = res ? res : lmk05318_check_lock(&d->lmk, &los); - // res = res ? res : lmk05318_set_out_mux(&d->lmk, LMK_FPGA_SYSREF, false, LVDS); - - usleep(1000); + res = res ? res : usleep(1000); res = res ? res : dev_gpi_get32(dev, IGPI_PGOOD, &pg); USDR_LOG("DSDR", USDR_LOG_ERROR, "Configuration: OK [%08x, %08x] res=%d PG=%08x\n", usr2, hwid, res, pg); @@ -1470,10 +2351,10 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** // Initialize AFEPWR res = res ? res : dev_gpo_set(dev, IGPO_PWR_AFE, 0x1); // Enable VIOSYS, hold RESET - usleep(10000); + res = res ? res : usleep(10000); //res = res ? res : dev_gpo_set(dev, IGPO_PWR_AFE, 0x3); // Enable VIOSYS, hold RESET res = res ? res : lp875484_init(dev, d->subdev, I2C_AFE_PMIC); - res = res ? res : lp875484_set_vout(dev, d->subdev, I2C_AFE_PMIC, 900); + res = res ? res : lp875484_set_vout(dev, d->subdev, I2C_AFE_PMIC, 930); // Recommended 925mV res = res ? res : dev_gpo_set(dev, IGPO_PWR_AFE, 0x3); // Enable VIOSYS, release RESET if (res) return res; @@ -1494,11 +2375,13 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** return -EIO; } + res = res ? res : usleep(100000); + res = res ? res : dev_gpo_set(dev, IGPO_PWR_AFE, 0x7); // Enable DCDC 1.2V; // We don't have PG_1v2 routed in this rev // We don't have EN_1v8 routed in this rev - if (d->type == DSDR_PCIE_HIPER_R0) { + if (d->type == DSDR_PCIE_HIPER_R0 || d->type == DSDR_M2_R1) { usleep(25000); res = res ? res : dev_gpo_set(dev, IGPO_PWR_AFE, 0xf); } @@ -1509,13 +2392,15 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** if (pgdat & (1 << 6)) break; - usleep(1000); + res = res ? res : usleep(1000); } if (!(pgdat & (1 << 6))) { USDR_LOG("DSDR", USDR_LOG_ERROR, "DCDC 1.8V isn't good, giving up!\n"); return -EIO; } + res = res ? res : usleep(100000); + res = res ? res : dev_gpo_set(dev, IGPO_PWR_AFE, 0x1f); if (res) return res; @@ -1523,20 +2408,24 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** // Test AFE chip USDR_LOG("DSDR", USDR_LOG_ERROR, "AFE is powered up!\n"); - usleep(100000); + res = res ? res : usleep(300000); res = res ? res : dev_gpo_set(dev, IGPO_AFE_RST, 0x0); + res = res ? res : usleep(100000); res = res ? res : dev_gpo_set(dev, IGPO_AFE_RST, 0x1); - usleep(100000); + res = res ? res : usleep(100000); + + res = res ? res : dev_gpo_set(dev, IGPO_TX_AFETDD, 0x0f); + res = res ? res : dev_gpo_set(dev, IGPO_RX_AFETDD, 0x0f); - res = res ? res : afe79xx_create(dev, d->subdev, 0, &d->st); + res = res ? res : afe79xx_create(dev, d->subdev, 0, afeType, &d->st); if (res == 0) { res = res ? res : usdr_jesd204b_bringup_pre(d); - // sleep(1); - usleep(10000); + // TODO accurate check GTH/GTY TX is up and running + res = res ? res : usleep(100000); char afeconfig_path[1024]; char *afecfgpath = getenv("AFECFG_PATH"); @@ -1548,27 +2437,68 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** res = res ? res : usdr_jesd204b_bringup_post(d); } + res = res ? res : afe_update_chan_pwr(d); + if (d->type == DSDR_PCIE_HIPER_R0) { - res = res ? res : dsdr_hiper_fe_create(dev, SPI_BUS_HIPER_FE, &d->hiper); + unsigned override = 0; + unsigned* poverride = NULL; + const char* env_over; + if ((env_over = getenv("LMS8_MPW2024_MASK"))) { + bool ok = (strlen(env_over) == 6); + if (ok) { + for (unsigned i = 0; i < 6; i++) { + if (env_over[i] == '0') { + } else if (env_over[i] == '1') { + override |= 1 << (5 - i); + } else { + ok = false; + } + } + } + + if (ok) { + poverride = &override; + USDR_LOG("DSDR", USDR_LOG_ERROR, "Applying external MPW mask for LMS8 chips: %d%d%d%d%d%d", + (override >> 5) & 1, (override >> 4) & 1, (override >> 3) & 1, + (override >> 2) & 1, (override >> 1) & 1, (override >> 0) & 1); + } else { + USDR_LOG("DSDR", USDR_LOG_ERROR, "Incorrect LMS8_MPW2024_MASK format!!! Should be like `LMS8_MPW_MASK=010101`\n"); + } + } + + res = res ? res : dsdr_hiper_fe_create(dev, SPI_BUS_HIPER_FE, poverride, &d->hiper); + } else if (d->type == DSDR_M2_R0 || d->type == DSDR_M2_R1) { + if (res) + return res; + + res = ext_fe_ch4_400_7200_init(dev, 0, M2PCI_REG_GPIO_S, "", "m2m", &d->extfe); + if (res && res != -ENODEV) + return res; + + d->has_extfe = (res == 0); + res = 0; } + res = res ? res : usleep(100000); + // check state res = res ? res : dev_m2_dsdr_afe_health_get(udev, NULL, NULL); - USDR_LOG("DSDR", USDR_LOG_ERROR, "Initializing AFE done\n"); - - - // res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_SYNC_RESET, 1); - // res = res ? res : dev_gpo_set(dev, IGPO_TIAFE_RX_SYNC_RESET, 0); + if (res) + return res; - return res; + USDR_LOG("DSDR", USDR_LOG_WARNING, "Initializing AFE done!\n"); + d->dsdr_state = STATE_AFE_INIT; + return 0; } int dev_m2_dsdr_sdr_rx_remap_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; - for (unsigned i = 0; i < 4; i++) { - d->rx_logic_to_hw[i] = (value >> (2 * i)) & 0x3; + unsigned bits = (d->logic_chcnt_rx == 1) ? 0 : (d->logic_chcnt_rx == 2) ? 1 : (d->logic_chcnt_rx <= 4) ? 2 : 3; + unsigned mask = (1 << bits) - 1; + for (unsigned i = 0; i < d->logic_chcnt_rx; i++) { + d->rx_ordinal_to_logic[i] = (value >> (bits * i)) & mask; } return dsdr_update_rx_remap(d); @@ -1577,9 +2507,11 @@ int dev_m2_dsdr_sdr_rx_remap_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val int dev_m2_dsdr_sdr_rx_remap_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) { struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; + unsigned bits = (d->logic_chcnt_rx == 1) ? 0 : (d->logic_chcnt_rx == 2) ? 1 : (d->logic_chcnt_rx <= 4) ? 2 : 3; + unsigned mask = (1 << bits) - 1; uint64_t remap = 0; - for (unsigned i = 0; i < 4; i++) { - remap |= (d->rx_logic_to_hw[i] & 0x3) << (2 * i); + for (unsigned i = 0; i < d->logic_chcnt_rx; i++) { + remap |= (d->rx_ordinal_to_logic[i] & mask) << (bits * i); } *ovalue = remap; @@ -1589,60 +2521,43 @@ int dev_m2_dsdr_sdr_rx_remap_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ov int dev_m2_dsdr_sdr_tx_remap_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; - for (unsigned i = 0; i < 4; i++) { - d->tx_hw_to_logic[i] = (value >> (2 * i)) & 0x3; + unsigned bits = (d->logic_chcnt_tx == 1) ? 0 : (d->logic_chcnt_tx == 2) ? 1 : (d->logic_chcnt_tx <= 4) ? 2 : 3; + unsigned mask = (1 << bits) - 1; + for (unsigned i = 0; i < d->logic_chcnt_tx; i++) { + d->tx_ordinal_to_logic[i] = (value >> (bits * i)) & mask; } - return dsdr_update_tx_remap(d); } + int dev_m2_dsdr_sdr_tx_remap_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) { struct dev_m2_dsdr *d = (struct dev_m2_dsdr *)ud; + unsigned bits = (d->logic_chcnt_tx == 1) ? 0 : (d->logic_chcnt_tx == 2) ? 1 : (d->logic_chcnt_tx <= 4) ? 2 : 3; + unsigned mask = (1 << bits) - 1; uint64_t remap = 0; - for (unsigned i = 0; i < 4; i++) { - remap |= (d->tx_hw_to_logic[i] & 0x3) << (2 * i); + for (unsigned i = 0; i < d->logic_chcnt_tx; i++) { + remap |= (d->tx_ordinal_to_logic[i] & mask) << (bits * i); } *ovalue = remap; return 0; } -char static dsdr_chan_name(uint8_t n) +static const char* dsdr_chan_name(struct dev_m2_dsdr *d, bool rx, uint8_t logic_number) { - return n == 0xff ? '-' : 'a' + n; -} - -char static dsdr_chan_num(uint8_t n) -{ - return n == 0xff ? '-' : '0' + n; -} - -static const channel_map_info_t s_dsdr_chmap[] = { - // Single NCO mode - { "a", 0 }, - { "b", 1 }, - { "c", 2 }, - { "d", 3 }, - - // Dual NCO mode - { "a0", 0 }, - { "b0", 1 }, - { "c0", 2 }, - { "d0", 3 }, - { "a1", 4 }, - { "b1", 5 }, - { "c1", 6 }, - { "d1", 7 }, + const channel_map_info_t *nfo = rx ? d->rx_chmap_info : d->tx_chmap_info; + for (unsigned p = 0; p < 16; p++) { + if (nfo[p].name == NULL) + break; - { NULL, CH_NULL }, - }; + if (nfo[p].hwidx == logic_number) + return nfo[p].name; + } -int dsdr_map_channels(const usdr_channel_info_t* channels, channel_info_t* core_chans) -{ - return usdr_channel_info_map_default(channels, s_dsdr_chmap, 4, core_chans); + return "-"; } -int device_path_to_chmsk(const char* full_path, const char* basename, chmsk_t *hw_mask, chmsk_t *lg_mask) +int device_path_to_chmsk(const char* full_path, const char* basename, const channel_map_info_t* map, const unsigned max_lchan, chmsk_t *hw_mask, chmsk_t *lg_mask) { *hw_mask = 0; *lg_mask = 0; @@ -1680,7 +2595,7 @@ int device_path_to_chmsk(const char* full_path, const char* basename, chmsk_t *h } channel_info_t mmaped; - res = dsdr_map_channels(&nfo, &mmaped); + res = usdr_channel_info_map_default(&nfo, map, max_lchan, &mmaped); if (res) return res; @@ -1692,7 +2607,7 @@ int device_path_to_chmsk(const char* full_path, const char* basename, chmsk_t *h return 0; } -static int parse_overriden_cahnnel_info(const char* env_string, const usdr_channel_info_t* orig, channel_info_t* override) +static int parse_overridden_channel_info(const char* env_string, const usdr_channel_info_t* orig, const channel_map_info_t* map, const unsigned max_lchan, channel_info_t* override) { char chanlist[64*4]; char* phys_names[DSDR_CHANS_LOGIC]; @@ -1708,11 +2623,11 @@ static int parse_overriden_cahnnel_info(const char* env_string, const usdr_chann return res; if (nfo.count != orig->count) { - USDR_LOG("UDEV", USDR_LOG_ERROR, "Overriden channel count %d != requested %d count!\n", nfo.count, orig->count); + USDR_LOG("UDEV", USDR_LOG_ERROR, "Overridden channel count %d != requested %d count!\n", nfo.count, orig->count); return -EINVAL; } - res = dsdr_map_channels(&nfo, override); + res = usdr_channel_info_map_default(&nfo, map, max_lchan, override); return res; } @@ -1726,53 +2641,79 @@ int usdr_device_m2_dsdr_create_stream(device_t* dev, const char* sid, const char unsigned hwchs; channel_info_t lchans; - res = dsdr_map_channels(channels, &lchans); + if (d->dsdr_state != STATE_AFE_INIT) { + return -EFAULT; + } + + bool rx_str = strstr(sid, "rx") != NULL; + bool tx_str = strstr(sid, "tx") != NULL; + if (!rx_str && !tx_str) { + USDR_LOG("UDEV", USDR_LOG_ERROR, "DSDR Unrecognised stream: %s\n", sid); + return -EINVAL; + } + + res = usdr_channel_info_map_default(channels, + rx_str ? d->rx_chmap_info : d->tx_chmap_info, + rx_str ? d->logic_chcnt_rx : d->logic_chcnt_tx, &lchans); if (res) { return res; } - if (channels->count > 4 || channels->count == 3) { - USDR_LOG("UDEV", USDR_LOG_ERROR, "DSDR %s: Unsupported channel count: %d, valid are (1, 2, 4)\n", sid, channels->count); + if (channels->count > 8 || channels->count == 5 || channels->count == 7) { + USDR_LOG("UDEV", USDR_LOG_ERROR, "DSDR %s: Unsupported channel count: %d, valid are (1, 2, 3, 4, 6, 8)\n", sid, channels->count); return -EINVAL; } - if (strstr(sid, "rx") != NULL) { + if (rx_str) { if (d->rx) { return -EBUSY; } - memset(d->rx_logic_to_hw, 0xff, sizeof(d->rx_logic_to_hw)); + memset(d->rx_ordinal_to_logic, 0xff, sizeof(d->rx_ordinal_to_logic)); + memset(d->rx_logic_to_ordinal, 0xff, sizeof(d->rx_logic_to_ordinal)); const char* env_ch = getenv("DSDR_CH_RX"); if (env_ch) { - res = parse_overriden_cahnnel_info(env_ch, channels, &lchans); + res = parse_overridden_channel_info(env_ch, channels, d->rx_chmap_info, d->logic_chcnt_rx, &lchans); if (res) return res; - USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR RX channel mask is overriden to `%s`\n", env_ch); + USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR RX channel mask is overridden to `%s`\n", env_ch); } - memcpy(d->rx_logic_to_hw, lchans.ch_map, sizeof(lchans.ch_map[0]) * channels->count); + memcpy(d->rx_ordinal_to_logic, lchans.ch_map, sizeof(lchans.ch_map[0]) * channels->count); d->hw_enabled_rx = 0; + d->logic_enabled_rx = 0; for (unsigned i = 0; i < channels->count; i++) { - unsigned hw = d->rx_logic_to_hw[i]; - if (hw >= DSDR_CHANS_HW) { + unsigned logic = d->rx_ordinal_to_logic[i]; + unsigned hw = d->rx_lmap_info[logic].hwport; + if (hw >= d->hw_chcnt_rx) { + USDR_LOG("UDEV", USDR_LOG_ERROR, "Stream RX: Logical channel %d incorrectly mmaped to HW %d\n", i, hw); return -EINVAL; } + d->rx_logic_to_ordinal[logic] = i; d->hw_enabled_rx |= (1ull << hw); + d->logic_enabled_rx |= (1ull << logic); } res = res ? res : dsdr_update_rx_remap(d); - USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR RX channels %d remmaped: [%c, %c, %c, %c] mux, hw_mask %02x\n", channels->count, - dsdr_chan_name(d->rx_logic_to_hw[0]), dsdr_chan_name(d->rx_logic_to_hw[1]), - dsdr_chan_name(d->rx_logic_to_hw[2]), dsdr_chan_name(d->rx_logic_to_hw[3]), d->hw_enabled_rx); - - uint64_t v; + USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR RX channels %d remmaped: [%s, %s, %s, %s -- %s, %s, %s, %s] mux, hw_mask %02x logic_mask %02x\n", channels->count, + dsdr_chan_name(d, true, 0), dsdr_chan_name(d, true, 1), + dsdr_chan_name(d, true, 2), dsdr_chan_name(d, true, 3), + dsdr_chan_name(d, true, 4), dsdr_chan_name(d, true, 5), + dsdr_chan_name(d, true, 6), dsdr_chan_name(d, true, 7), + d->hw_enabled_rx, d->logic_enabled_rx); + + uint64_t v0, v1; for (unsigned ch = 0; ch < 4; ch++) { - d->st.libcapi79xx_get_nco(&d->st.capi, NCO_RX, ch, &v, 0, 0); - - USDR_LOG("UDEV", USDR_LOG_INFO, "RX NCO[%d] = %lld\n", ch, (long long)v); + d->st.libcapi79xx_get_nco(&d->st.capi, NCO_RX, ch, &v0, 0, 0); + d->st.libcapi79xx_get_nco(&d->st.capi, NCO_RX, ch, &v1, 0, 1); + USDR_LOG("UDEV", USDR_LOG_INFO, "RX NCO[%d] = %lld | NCO[%d] = %lld \n", ch, (long long)v0, ch, (long long)v1); + } + for (unsigned ch = 0; ch < 2; ch++) { + d->st.libcapi79xx_get_nco(&d->st.capi, NCO_FB, ch, &v0, 0, 0); + USDR_LOG("UDEV", USDR_LOG_INFO, "FB NCO[%d] = %lld \n", ch, (long long)v0); } struct sfetrx4_config rxcfg; @@ -1786,9 +2727,10 @@ int usdr_device_m2_dsdr_create_stream(device_t* dev, const char* sid, const char usleep(1000); res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_RST, 0x0); - res = (res) ? res : create_sfetrx4_stream(dev, CORE_EXFERX_DMA32_R0, dformat, channels->count, &lchans, pktsyms, - flags, M2PCI_REG_WR_RXDMA_CONFIRM, VIRT_CFG_SFX_BASE, 0, - SRF4_FIFOBSZ, CSR_RFE4_BASE, &d->rx, &hwchs); + res = (res) ? res : create_sfetrx4_stream(dev, (d->dsp_rx_chans == 8) ? CORE_EXFERX_DMA32_R0_8 : CORE_EXFERX_DMA32_R0, + dformat, channels->count, &lchans, pktsyms, + flags, M2PCI_REG_WR_RXDMA_CONFIRM, VIRT_CFG_SFX_BASE, 0, + SRF4_FIFOBSZ, CSR_RFE4_BASE, &d->rx, &hwchs); if (res) { return res; } @@ -1797,45 +2739,61 @@ int usdr_device_m2_dsdr_create_stream(device_t* dev, const char* sid, const char d->rx_chans = lchans; // Restore cached parameters we couldn't set before activating streams - for (unsigned i = 0; i < SIZEOF_ARRAY(d->rx_freqs); i++) { - if (d->rx_freqs[i].set && (d->hw_enabled_rx & (1u << i))) { - res = (res) ? res : dsdr_set_rx_frequency_chan(d, d->rx_freqs[i].value, i); + for (unsigned i = 0; i < SIZEOF_ARRAY(d->rx_ord_freqs); i++) { + if (d->rx_ord_freqs[i].set && (d->logic_enabled_rx & (1u << i))) { + res = (res) ? res : dsdr_set_rx_frequency_chan(d, d->rx_ord_freqs[i].value, i); } } + // TODO: set actual antenna mask + //res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_RX_CHEN, 0x3f); // d->hw_enabled_rx + //res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_RX_CHEN, 0x0); + res = res ? res : afe_update_chan_pwr(d); + *out_handle = d->rx; - } else if (strstr(sid, "tx") != NULL) { + } else { if (d->tx) { return -EBUSY; - } - - d->hw_enabled_tx = 0; - memset(d->tx_hw_to_logic, 0xff, sizeof(d->tx_hw_to_logic)); + } + memset(d->tx_ordinal_to_logic, 0xff, sizeof(d->tx_ordinal_to_logic)); + memset(d->tx_logic_to_ordinal, 0xff, sizeof(d->tx_logic_to_ordinal)); const char* env_ch = getenv("DSDR_CH_TX"); if (env_ch) { - res = parse_overriden_cahnnel_info(env_ch, channels, &lchans); + res = parse_overridden_channel_info(env_ch, channels, d->tx_chmap_info, d->logic_chcnt_tx, &lchans); if (res) return res; - USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR TX channel mask is overriden to `%s`\n", env_ch); + USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR TX channel mask is overridden to `%s`\n", env_ch); } + memcpy(d->tx_ordinal_to_logic, lchans.ch_map, sizeof(lchans.ch_map[0]) * channels->count); + + //memset(d->tx_hw_to_logic, 0xff, sizeof(d->tx_hw_to_logic)); + + d->hw_enabled_tx = 0; + d->logic_enabled_tx = 0; + for (unsigned i = 0; i < channels->count; i++) { - unsigned hw = lchans.ch_map[i]; - if (hw >= DSDR_CHANS_HW) { + unsigned logic = d->tx_ordinal_to_logic[i]; + unsigned hw = d->tx_lmap_info[logic].hwport; + if (hw >= d->hw_chcnt_tx) { + USDR_LOG("UDEV", USDR_LOG_ERROR, "Stream TX: Logical channel %d incorrectly mmaped to HW %d\n", i, hw); return -EINVAL; } + d->tx_logic_to_ordinal[logic] = i; - d->tx_hw_to_logic[hw] = i; d->hw_enabled_tx |= (1ull << hw); + d->logic_enabled_tx |= (1ull << logic); } - // Map as single channel only res = res ? res : dsdr_update_tx_remap(d); - USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR TX channels %d remmaped: [A <= %c, B <= %c, C <= %c, D <= %c] mux, hw_mask %02x\n", - channels->count, dsdr_chan_num(d->tx_hw_to_logic[0]), dsdr_chan_num(d->tx_hw_to_logic[1]), - dsdr_chan_num(d->tx_hw_to_logic[2]), dsdr_chan_num(d->tx_hw_to_logic[3]), d->hw_enabled_tx); + USDR_LOG("UDEV", USDR_LOG_INFO, "DSDR TX channels %d remmaped: [%s, %s, %s, %s -- %s, %s, %s, %s] mux, hw_mask %02x logic_mask %02x\n", channels->count, + dsdr_chan_name(d, false, 0), dsdr_chan_name(d, false, 1), + dsdr_chan_name(d, false, 2), dsdr_chan_name(d, false, 3), + dsdr_chan_name(d, false, 4), dsdr_chan_name(d, false, 5), + dsdr_chan_name(d, false, 6), dsdr_chan_name(d, false, 7), + d->hw_enabled_tx, d->logic_enabled_tx); struct sfetrx4_config txcfg; res = (res) ? res : parse_sfetrx4(dformat, &lchans, pktsyms, channels->count, &txcfg); @@ -1848,7 +2806,8 @@ int usdr_device_m2_dsdr_create_stream(device_t* dev, const char* sid, const char usleep(1000); res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSPCHAIN_TX_RST, 0x0); - res = (res) ? res : create_sfetrx4_stream(dev, CORE_EXFETX_DMA32_R0, dformat, channels->count, &lchans, pktsyms, + res = (res) ? res : create_sfetrx4_stream(dev, (d->dsp_tx_chans == 8) ? CORE_EXFETX_DMA32_R0_8 : CORE_EXFETX_DMA32_R0, + dformat, channels->count, &lchans, pktsyms, flags | DMS_DONT_CHECK_FWID, M2PCI_REG_WR_TXDMA_CFG0, M2PCI_REG_WR_SYNC_CTRL, @@ -1862,12 +2821,15 @@ int usdr_device_m2_dsdr_create_stream(device_t* dev, const char* sid, const char d->tx_chans = lchans; // Restore cached parameters we couldn't set before activating streams - for (unsigned i = 0; i < SIZEOF_ARRAY(d->tx_freqs); i++) { - if (d->tx_freqs[i].set && (d->hw_enabled_tx & (1u << i))) { - res = (res) ? res : dsdr_set_tx_frequency_chan(d, d->tx_freqs[i].value, i); + for (unsigned i = 0; i < SIZEOF_ARRAY(d->tx_ord_freqs); i++) { + if (d->tx_ord_freqs[i].set && (d->logic_enabled_rx & (1u << i))) { + res = (res) ? res : dsdr_set_tx_frequency_chan(d, d->tx_ord_freqs[i].value, i); } } + // TODO: set actual antenna mask + //res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_TX_CHEN, 0xf); + res = res ? res : afe_update_chan_pwr(d); *out_handle = d->tx; } @@ -1887,7 +2849,12 @@ int usdr_device_m2_dsdr_unregister_stream(device_t* dev, stream_handle_t* stream if (dev_m2_dsdr_has_hiper(d)) { res = dsdr_hiper_fe_rx_chan_en(&d->hiper, 0); + } else if (dev_m2_dsdr_has_extfe0472(d)) { + res = ext_fe_rx_chan_en(&d->extfe, 0); } + + //dev_gpo_set(d->base.dev, IGPO_RX_CHEN, 0); + afe_update_chan_pwr(d); } else if (stream == d->tx) { d->tx = NULL; d->hw_enabled_rx = 0; @@ -1895,7 +2862,12 @@ int usdr_device_m2_dsdr_unregister_stream(device_t* dev, stream_handle_t* stream if (dev_m2_dsdr_has_hiper(d)) { res = dsdr_hiper_fe_tx_chan_en(&d->hiper, 0); + } else if (dev_m2_dsdr_has_extfe0472(d)) { + res = ext_fe_tx_chan_en(&d->extfe, 0); } + + //dev_gpo_set(d->base.dev, IGPO_TX_CHEN, 0); + afe_update_chan_pwr(d); } else { return -EINVAL; } @@ -1932,28 +2904,54 @@ int usdr_device_m2_dsdr_create(lldev_t dev, device_id_t devid) d->base.unregister_stream = &usdr_device_m2_dsdr_unregister_stream; d->base.timer_op = &sfetrx4_stream_sync; + memset(&d->st, 0, sizeof(d->st)); + d->rx = NULL; d->tx = NULL; d->hw_enabled_tx = 0; d->hw_enabled_rx = 0; - d->hw_mask_tx = 0; - d->hw_mask_rx = 0; - d->hw_mask_fb = 0; + + d->logic_enabled_rx = 0; + d->logic_enabled_tx = 0; d->hw_fpga_jesd_rx_en = 0; d->hw_fpga_jesd_tx_en = 0; - memset(d->rx_logic_to_hw, 0xff, sizeof(d->rx_logic_to_hw)); - memset(d->tx_hw_to_logic, 0xff, sizeof(d->tx_hw_to_logic)); + memset(d->rx_ordinal_to_logic, 0xff, sizeof(d->rx_ordinal_to_logic)); + memset(d->tx_ordinal_to_logic, 0xff, sizeof(d->tx_ordinal_to_logic)); d->tx_activated = false; d->rx_activated = false; - for (unsigned i = 0; i < SIZEOF_ARRAY(d->rx_freqs); i++) { - opt_u64_set_null(&d->rx_freqs[i]); - opt_u64_set_null(&d->tx_freqs[i]); + for (unsigned i = 0; i < SIZEOF_ARRAY(d->rx_ord_freqs); i++) { + opt_u64_set_null(&d->rx_ord_freqs[i]); + opt_u64_set_null(&d->tx_ord_freqs[i]); + } + + d->has_extfe = false; + +#if 0 + for (unsigned i = 0; i < MAX_HIPER_FE_PORT; i++) { + for (unsigned j = 0; j < MAX_PORT_BANDS; j++) { + opt_u64_set_null(&d->rx_bxfc[i][j]); + opt_u64_set_null(&d->tx_bxfc[i][j]); + + opt_u64_set_null(&d->rx_raw_nco[i][j]); + opt_u64_set_null(&d->tx_raw_nco[i][j]); + } + } +#endif + + for (unsigned i = 0; i < MAX_DSP_NCO_RX; i++) { + opt_u64_set_null(&d->rx_bxfc[i]); + opt_u64_set_null(&d->rx_raw_nco[i]); } + for (unsigned i = 0; i < MAX_DSP_NCO_TX; i++) { + opt_u64_set_null(&d->tx_bxfc[i]); + opt_u64_set_null(&d->tx_raw_nco[i]); + } + d->type = DSDR_PCIE_HIPER_R0; dev->pdev = &d->base; diff --git a/src/lib/device/m2_dsdr/m2_dsdr_e.yaml b/src/lib/device/m2_dsdr/m2_dsdr_e.yaml index 77491146..5db3ae20 100644 --- a/src/lib/device/m2_dsdr/m2_dsdr_e.yaml +++ b/src/lib/device/m2_dsdr/m2_dsdr_e.yaml @@ -3,7 +3,7 @@ # Register desc and visual map name: M2_DSDR -desc: DSDR M2 board +desc: Hiper board control revision: "0.0.1" processors: [ c ] bus: @@ -44,6 +44,21 @@ x-if-lna-opts: &if-lna-opts 0b10: BYPASS 0b11: BYPASS2 + +x-r2rx-in-opts: &r2rx-in-opts + 0b00: DISABLE + 0b01: RX_HI + 0b10: RX_LOW + 0b11: RX_BYPASS + + +x-r2rx-out-opts: &r2rx-out-opts + 0b00: DISABLE + 0b01: RX_LOW + 0b10: RX_HI + 0b11: RX_BYPASS + + pages: - name: V0 regs: @@ -130,16 +145,16 @@ pages: fields: # - bits: "6" - name: RX_H_CHA + name: RX_H_CHA_R2D_V1 desc: Channel A RX OUT selection, 2.5-7.1 GHz (1) or 0.4-3.5 GHz (0) - bits: "7" - name: RX_H_CHB + name: RX_H_CHB_R2D_V2 desc: Channel B RX OUT selection, 2.5-7.1 GHz (1) or 0.4-3.5 GHz (0) - bits: "4" - name: RX_H_CHC + name: RX_H_CHC_R2C_V1 desc: Channel C RX OUT selection, 2.5-7.1 GHz (1) or 0.4-3.5 GHz (0) - bits: "5" - name: RX_H_CHD + name: RX_H_CHD_R2C_V2 desc: Channel D RX OUT selection, 2.5-7.1 GHz (1) or 0.4-3.5 GHz (0) - bits: "3" name: TX_L_CHA @@ -159,16 +174,16 @@ pages: fields: # - bits: "0" - name: RX_L_CHA + name: RX_L_CHA_R2B_V1 desc: Channel A RX IN selection, 2.5-7.1 GHz (0) or 0.4-3.5 GHz (1) - bits: "1" - name: RX_L_CHB + name: RX_L_CHB_R2B_V2 desc: Channel B RX IN selection, 2.5-7.1 GHz (0) or 0.4-3.5 GHz (1) - bits: "2" - name: RX_L_CHC + name: RX_L_CHC_R2A_V1 desc: Channel C RX IN selection, 2.5-7.1 GHz (0) or 0.4-3.5 GHz (1) - bits: "3" - name: RX_L_CHD + name: RX_L_CHD_R2A_V2 desc: Channel D RX IN selection, 2.5-7.1 GHz (0) or 0.4-3.5 GHz (1) - bits: "4" name: TX_H_CHA @@ -186,6 +201,18 @@ pages: - addr: 0x24 name: ENABLE fields: + - bits: "7" + name: REV2_P5V + desc: Rev2 Enable +5v power for (IF Amplifier) + - bits: "6" + name: REV2_P5V_EXTLO + desc: Rev2 Enable +5v power for EXT_LO clock distributor + - bits: "5" + name: REV2_PG_6P0 + desc: PG signal for 6P0 line + - bits: "4" + name: REV2_PG_8P0 + desc: PG signal for 8P0 line - bits: "3" name: REF_OSC desc: Enable 25MHz reference clock oscillator @@ -197,7 +224,7 @@ pages: desc: Enable +8v power supply for TX amps - bits: "0" name: P5V_RX - desc: Enable +5v power supply for RX amps + desc: Enable +5v power supply for RX amps, Rev2 Enable 6V rail # - addr: 0x25 name: LMS8001_RESET @@ -220,9 +247,13 @@ pages: - bits: "1" name: TX_CHCD desc: TX circuit LMS8001B reset (channels C and D, 2.5-7.1 GHz (HIGH) mode) + - bits: "0" + name: REV2_DCDC + desc: Reset pin for DCDC module + # - addr: 0x26 - name: GPIO6 + name: AUX_CTRL fields: - bits: "7" name: ABSLNA_PA_CHD @@ -237,14 +268,78 @@ pages: name: ABSLNA_PA_CHA desc: 50 ohm absorptive LNA to PA switch selection, channel A - bits: "3" - name: FAN_PWM0 - desc: FAN0 PWM input + name: PA_BYPASS_D + desc: Rev0 FAN0 PWM input - bits: "2" - name: FAN_TACH0 - desc: FAN0 tachometer output + name: PA_BYPASS_C + desc: Rev0 FAN0 tachometer output - bits: "1" - name: FAN_PWM1 - desc: FAN1 PWM input + name: PA_BYPASS_B + desc: Rev0 FAN1 PWM input - bits: "0" - name: FAN_TACH1 - desc: FAN1 tachometer output + name: PA_BYPASS_A + desc: Rev0 FAN1 tachometer output +# + - addr: 0x27 + name: SW_IN_RX + fields: + - bits: "1:0" + name: CHA + desc: CHA + opts: *r2rx-in-opts + - bits: "3:2" + name: CHB + desc: CHB + opts: *r2rx-in-opts + - bits: "5:4" + name: CHC + desc: CHC + opts: *r2rx-in-opts + - bits: "7:6" + name: CHD + desc: CHD + opts: *r2rx-in-opts +# + - addr: 0x28 + name: LED_TRX_CTRL + fields: + - bits: "1:0" + name: LED_CHA + desc: LED CHA + - bits: "3:2" + name: LED_CHB + desc: LED CHB + - bits: "5:4" + name: LED_CHC + desc: LED CHC + - bits: "7:6" + name: LED_CHD + desc: LED CHD +# + - addr: 0x29 + name: LEDRX_CH_CTRL + fields: + - bits: "0" + name: EN_CHA + desc: Enable CHA + - bits: "1" + name: EN_CHB + desc: Enable CHB + - bits: "2" + name: EN_CHC + desc: Enable CHC + - bits: "3" + name: EN_CHD + desc: Enable CHD + - bits: "4" + name: LED_CHA + desc: LED CHA + - bits: "5" + name: LED_CHB + desc: LED CHB + - bits: "6" + name: LED_CHC + desc: LED CHC + - bits: "7" + name: LED_CHD + desc: LED CHD diff --git a/src/lib/device/m2_dsdr/m2_dsdr_e_v2.yaml b/src/lib/device/m2_dsdr/m2_dsdr_e_v2.yaml new file mode 100644 index 00000000..71d01995 --- /dev/null +++ b/src/lib/device/m2_dsdr/m2_dsdr_e_v2.yaml @@ -0,0 +1,336 @@ +# Copyright (c) 2023-2024 Wavelet Lab +# SPDX-License-Identifier: MIT + +# Register desc and visual map +name: M2_DSDR_V2 +desc: Hiper v2 board control +revision: "0.0.1" +processors: [ c ] +bus: + type: VIRTUAL + usdr_path: /debug/hw/dsdr_hiper_exp_v2/0/reg + wr_mask: 0x80000000 +addr_width: 8 +data_width: 24 +# page_prefix: True +field_prefix: [ RegName ] +field_macros: True + +x-rx-filt-in-opts: &rx-filt-in-opts + 0b000: MUTE0 + 0b011: 400_1000M + 0b010: 1000_2000M + 0b001: 2000_3500M + 0b101: 2500_5000M + 0b100: 3500_7100M + 0b110: MUTE1 + 0b111: MUTE2 + + +x-rx-filt-out-opts: &rx-filt-out-opts + 0b000: MUTE0 + 0b011: 400_1000M + 0b100: 1000_2000M + 0b101: 2000_3500M + 0b001: 2500_5000M + 0b010: 3500_7100M + 0b110: MUTE1 + 0b111: MUTE2 + + +x-if-lna-opts: &if-lna-opts + 0b00: LNA + 0b01: Disable + 0b10: BYPASS + 0b11: BYPASS2 + + +x-r2rx-in-opts: &r2rx-in-opts + 0b00: DISABLE + 0b01: RX_HI + 0b10: RX_LOW + 0b11: RX_BYPASS + + +x-r2rx-out-opts: &r2rx-out-opts + 0b00: DISABLE + 0b01: RX_HI + 0b10: RX_LOW + 0b11: RX_BYPASS + + +pages: + - name: V0 + regs: +# + - addr: 0x20 + name: SW_RX_FILTER + fields: +# +# RX filters switching +# ---IN--- ---OUT-- +# V3 V2 V1 V3 V2 V1 +# --+--+-- --+--+-- +# 0 1 1 0 1 1 = 0.4 - 1.0 GHz +# 0 1 0 1 0 0 = 1.0 - 2.0 GHz +# 0 0 1 1 0 1 = 2.0 - 3.5 GHz +# 1 0 1 0 0 1 = 2.5 - 5.0 GHz +# 1 0 0 0 1 0 = 3.5 - 7.0 GHz +# + - bits: "8,23,22" + name: IN_CHA + desc: RX IN filters switch for Channel A + opts: *rx-filt-in-opts +# + - bits: "15,6,7" + name: OUT_CHA + desc: RX OUT filters switch for Channel A + opts: *rx-filt-out-opts +# + - bits: "9,21,20" + name: IN_CHB + desc: RX IN filters switch for Channel B + opts: *rx-filt-in-opts +# + - bits: "14,4,5" + name: OUT_CHB + desc: RX OUT filters switch for Channel B + opts: *rx-filt-out-opts +# + - bits: "10,19,18" + name: IN_CHC + desc: RX IN filters switch for Channel C + opts: *rx-filt-in-opts +# + - bits: "13,3,2" + name: OUT_CHC + desc: RX OUT filters switch for Channel C + opts: *rx-filt-out-opts +# + - bits: "11,17,16" + name: IN_CHD + desc: RX IN filters switch for Channel D + opts: *rx-filt-in-opts +# + - bits: "12,0,1" + name: OUT_CHD + desc: RX OUT filters switch for Channel D + opts: *rx-filt-out-opts +# + - addr: 0x21 + name: IF_LNA + fields: + - bits: "7:6" + name: CTRL_CHD + desc: IF LNA control mask for Channel D + opts: *if-lna-opts +# + - bits: "5:4" + name: CTRL_CHC + desc: IF LNA control mask for Channel C + opts: *if-lna-opts +# + - bits: "3:2" + name: CTRL_CHB + desc: IF LNA control mask for Channel B + opts: *if-lna-opts +# + - bits: "1:0" + name: CTRL_CHA + desc: IF LNA control mask for Channel A + opts: *if-lna-opts +# +# 0x22 and 0x23 for HB rev 2 + - addr: 0x22 + name: SW_OUT_RX_CD + fields: + - bits: "5:4" + name: OUT_RX_CHC + desc: CHC + opts: *r2rx-out-opts + - bits: "7:6" + name: OUT_RX_CHD + desc: CHD + opts: *r2rx-out-opts + - bits: "0" + name: OUT_TX_CHD + desc: OUT_TX_CHD + - bits: "1" + name: OUT_TX_CHC + desc: OUT_TX_CHC + - bits: "2" + name: OUT_TX_CHB + desc: OUT_TX_CHB + - bits: "3" + name: OUT_TX_CHA + desc: OUT_TX_CHA +# + - addr: 0x23 + name: SW_OUT_RX_AB + fields: + - bits: "1:0" + name: OUT_RX_CHB + desc: CHA + opts: *r2rx-out-opts + - bits: "3:2" + name: OUT_RX_CHA + desc: CHB + opts: *r2rx-out-opts + - bits: "4" + name: IN_TX_CHA + desc: IN_TX_CHA + - bits: "5" + name: IN_TX_CHB + desc: IN_TX_CHB + - bits: "6" + name: IN_TX_CHC + desc: IN_TX_CHC + - bits: "7" + name: IN_TX_CHD + desc: IN_TX_CHC + + - addr: 0x24 + name: ENABLE + fields: + - bits: "7" + name: REV2_P5V + desc: Rev2 Enable +5v power for (IF Amplifier) + - bits: "6" + name: REV2_P5V_EXTLO + desc: Rev2 Enable +5v power for EXT_LO clock distributor + - bits: "5" + name: REV2_PG_6P0 + desc: PG signal for 6P0 line + - bits: "4" + name: REV2_PG_8P0 + desc: PG signal for 8P0 line + - bits: "3" + name: REF_OSC + desc: Enable 25MHz reference clock oscillator + - bits: "2" + name: REF_GPS + desc: Enable GPS module + - bits: "1" + name: P8V_TX + desc: Enable +8v power supply for TX amps + - bits: "0" + name: P5V_RX + desc: Enable +5v power supply for RX amps, Rev2 Enable 6V rail +# + - addr: 0x25 + name: LMS8001_RESET + fields: + - bits: "6" + name: RX_CHCD_HIGH + desc: RX circuit LMS8001B reset (channels C and D, 2.5-7.1 GHz (HIGH) mode) + - bits: "5" + name: RX_CHCD_LOW + desc: RX circuit LMS8001A reset (channels C and D, 0.4-3.5 GHz (LOW) mode) + - bits: "4" + name: RX_CHAB_HIGH + desc: RX circuit LMS8001B reset (channels A and B, 2.5-7.1 GHz (HIGH) mode) + - bits: "3" + name: RX_CHAB_LOW + desc: RX circuit LMS8001A reset (channels A and B, 0.4-3.5 GHz (LOW) mode) + - bits: "2" + name: TX_CHAB + desc: TX circuit LMS8001B reset (channels A and B, 2.5-7.1 GHz (HIGH) mode) + - bits: "1" + name: TX_CHCD + desc: TX circuit LMS8001B reset (channels C and D, 2.5-7.1 GHz (HIGH) mode) + - bits: "0" + name: REV2_DCDC + desc: Reset pin for DCDC module + +# + - addr: 0x26 + name: AUX_CTRL + fields: + - bits: "7" + name: ABSLNA_PA_CHD + desc: 50 ohm absorptive LNA to PA switch selection, channel D + - bits: "6" + name: ABSLNA_PA_CHC + desc: 50 ohm absorptive LNA to PA switch selection, channel C + - bits: "5" + name: ABSLNA_PA_CHB + desc: 50 ohm absorptive LNA to PA switch selection, channel B + - bits: "4" + name: ABSLNA_PA_CHA + desc: 50 ohm absorptive LNA to PA switch selection, channel A + - bits: "3" + name: PA_BYPASS_D + desc: Rev0 FAN0 PWM input + - bits: "2" + name: PA_BYPASS_C + desc: Rev0 FAN0 tachometer output + - bits: "1" + name: PA_BYPASS_B + desc: Rev0 FAN1 PWM input + - bits: "0" + name: PA_BYPASS_A + desc: Rev0 FAN1 tachometer output +# + - addr: 0x27 + name: SW_IN_RX + fields: + - bits: "1:0" + name: CHA + desc: CHA + opts: *r2rx-in-opts + - bits: "3:2" + name: CHB + desc: CHB + opts: *r2rx-in-opts + - bits: "5:4" + name: CHC + desc: CHC + opts: *r2rx-in-opts + - bits: "7:6" + name: CHD + desc: CHD + opts: *r2rx-in-opts +# + - addr: 0x28 + name: LED_TRX_CTRL + fields: + - bits: "1:0" + name: LED_CHA + desc: LED CHA + - bits: "3:2" + name: LED_CHB + desc: LED CHB + - bits: "5:4" + name: LED_CHC + desc: LED CHC + - bits: "7:6" + name: LED_CHD + desc: LED CHD +# + - addr: 0x29 + name: LEDRX_CH_CTRL + fields: + - bits: "0" + name: EN_CHA + desc: Enable CHA + - bits: "1" + name: EN_CHB + desc: Enable CHB + - bits: "2" + name: EN_CHC + desc: Enable CHC + - bits: "3" + name: EN_CHD + desc: Enable CHD + - bits: "4" + name: LED_CHA + desc: LED CHA + - bits: "5" + name: LED_CHB + desc: LED CHB + - bits: "6" + name: LED_CHC + desc: LED CHC + - bits: "7" + name: LED_CHD + desc: LED CHD diff --git a/src/lib/device/m2_dsdr/m2_dsdr_p.yaml b/src/lib/device/m2_dsdr/m2_dsdr_p.yaml index 67eae93e..44e527d1 100644 --- a/src/lib/device/m2_dsdr/m2_dsdr_p.yaml +++ b/src/lib/device/m2_dsdr/m2_dsdr_p.yaml @@ -16,6 +16,13 @@ data_width: 24 field_prefix: [ RegName ] field_macros: True +x-tdd-fdd-opts: &tdd-fdd-opts + 0b00: REV0_LNA_TO_RX_____REV2_SHUTDOWN + 0b01: REV0_LNA_TO_TDDSW__REV2_LNA_TO_TDDSW + 0b10: REV0_LNA_TO_RX_____REV2_LNA_TO_LB + 0b11: REV0_LNA_TO_TDDSW__REV2_LNA_TO_RX + + pages: - name: FPGA_GPO regs: @@ -39,15 +46,13 @@ pages: - bits: "5" name: EN_RX desc: Enable RX LNA & post-attenuator RX_RF amp - - bits: "3" - name: SW_HW_TDD_CTRL - desc: Hardware control of TRX antenna to TX/RX port based on burst boundaries. SW_PA_ONOFF, SW_RX_TDDFDD, SW_RXTX wont take effect in this mode - bits: "2" name: SW_PA_ONOFF desc: Route post-amped TX to RX, 0=PA->ANT, 1=PA->RX_LB - - bits: "1" + - bits: "3,1" name: SW_RX_TDDFDD - desc: RX TDD-FDD switch (0:FDD=ANT_RX->LNA, 1:TDD=TDD_SW->LNA) + desc: RX TDD-FDD switch + opts: *tdd-fdd-opts - bits: "0" name: SW_RXTX desc: Switch TRX antenna to TX(0) or RX(1) @@ -64,15 +69,13 @@ pages: - bits: "5" name: EN_RX desc: Enable RX LNA & post-attenuator RX_RF amp - - bits: "3" - name: SW_HW_TDD_CTRL - desc: Hardware control of TRX antenna to TX/RX port based on burst boundaries. SW_PA_ONOFF, SW_RX_TDDFDD, SW_RXTX wont take effect in this mode - bits: "2" name: SW_PA_ONOFF desc: Route post-amped TX to RX, 0=PA->ANT, 1=PA->RX_LB - - bits: "1" + - bits: "3,1" name: SW_RX_TDDFDD - desc: RX TDD-FDD switch (0:FDD=ANT_RX->LNA, 1:TDD=TDD_SW->LNA) + desc: RX TDD-FDD switch + opts: *tdd-fdd-opts - bits: "0" name: SW_RXTX desc: Switch TRX antenna to TX(0) or RX(1) @@ -89,15 +92,13 @@ pages: - bits: "5" name: EN_RX desc: Enable RX LNA & post-attenuator RX_RF amp - - bits: "3" - name: SW_HW_TDD_CTRL - desc: Hardware control of TRX antenna to TX/RX port based on burst boundaries. SW_PA_ONOFF, SW_RX_TDDFDD, SW_RXTX wont take effect in this mode - bits: "2" name: SW_PA_ONOFF desc: Route post-amped TX to RX, 0=PA->ANT, 1=PA->RX_LB - - bits: "1" + - bits: "3,1" name: SW_RX_TDDFDD - desc: RX TDD-FDD switch (0:FDD=ANT_RX->LNA, 1:TDD=TDD_SW->LNA) + desc: RX TDD-FDD switch + opts: *tdd-fdd-opts - bits: "0" name: SW_RXTX desc: Switch TRX antenna to TX(0) or RX(1) @@ -114,15 +115,13 @@ pages: - bits: "5" name: EN_RX desc: Enable RX LNA & post-attenuator RX_RF amp - - bits: "3" - name: SW_HW_TDD_CTRL - desc: Hardware control of TRX antenna to TX/RX port based on burst boundaries. SW_PA_ONOFF, SW_RX_TDDFDD, SW_RXTX wont take effect in this mode - bits: "2" name: SW_PA_ONOFF desc: Route post-amped TX to RX, 0=PA->ANT, 1=PA->RX_LB - - bits: "1" + - bits: "3,1" name: SW_RX_TDDFDD - desc: RX TDD-FDD switch (0:FDD=ANT_RX->LNA, 1:TDD=TDD_SW->LNA) + desc: RX TDD-FDD switch + opts: *tdd-fdd-opts - bits: "0" name: SW_RXTX desc: Switch TRX antenna to TX(0) or RX(1) diff --git a/src/lib/device/m2_dsdr/m2_dsdr_usr.yaml b/src/lib/device/m2_dsdr/m2_dsdr_usr.yaml index 7a281fd6..b384bd9d 100644 --- a/src/lib/device/m2_dsdr/m2_dsdr_usr.yaml +++ b/src/lib/device/m2_dsdr/m2_dsdr_usr.yaml @@ -41,7 +41,17 @@ x-band-opts: &x-band-opts 0b01: BAND_2200_7200 0b10: BAND_AUTO_L 0b11: BAND_AUTO_H - + +x-rxband-opts: &x-rxband-opts + 0b000: BAND_400_3500 + 0b001: BAND_2200_7200 + 0b010: BAND_1580_2760_BP + 0b011: DISABLE + 0b100: BAND_AUTO_L + 0b101: BAND_AUTO_H + 0b110: BAND_AUTO_BP + 0b111: BAND_AUTO_DIS + pages: - name: FEUSR regs: @@ -65,22 +75,22 @@ pages: - addr: 0x41 name: RX_H_BAND fields: - - bits: "1:0" + - bits: "2:0" name: A - desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector - opts: *x-band-opts - - bits: "5:4" + desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector or Bypass_Rev2 + opts: *x-rxband-opts + - bits: "6:4" name: B - desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector - opts: *x-band-opts - - bits: "9:8" + desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector or Bypass_Rev2 + opts: *x-rxband-opts + - bits: "10:8" name: C - desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector - opts: *x-band-opts - - bits: "13:12" + desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector or Bypass_Rev2 + opts: *x-rxband-opts + - bits: "14:12" name: D - desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector - opts: *x-band-opts + desc: Band 2.2G to 7.2G or 0.4G to 3.5G selector or Bypass_Rev2 + opts: *x-rxband-opts # - addr: 0x42 name: RX_FILTER_BANK @@ -254,5 +264,20 @@ pages: name: D desc: FE LMS8001A TX high band 0-15 PA Gain # + - addr: 0x4C + name: PA_2ND_BP + fields: + - bits: "0" + name: A + desc: Bypass 2nd stage PA + - bits: "1" + name: B + desc: Bypass 2nd stage PA + - bits: "2" + name: C + desc: Bypass 2nd stage PA + - bits: "3" + name: D + desc: Bypass 2nd stage PA diff --git a/src/lib/device/m2_lm6_1/m2_lm6_1.c b/src/lib/device/m2_lm6_1/m2_lm6_1.c index ae7dc237..ee33b0bc 100644 --- a/src/lib/device/m2_lm6_1/m2_lm6_1.c +++ b/src/lib/device/m2_lm6_1/m2_lm6_1.c @@ -81,7 +81,7 @@ const usdr_dev_param_constant_t s_params_m2_lm6_1_rev000[] = { { "/ll/srx/0/cfg_base",VIRT_CFG_SFX_BASE }, { "/ll/srx/0/irq", M2PCI_INT_RX}, { "/ll/srx/0/dmacap", 0x855 }, - { "/ll/srx/0/rfe", (uintptr_t)"/ll/rfe/0" }, +// { "/ll/srx/0/rfe", (uintptr_t)"/ll/rfe/0" }, { "/ll/rfe/0/fifobsz", SRF4_FIFOBSZ }, { "/ll/rfe/0/core", USDR_MAKE_COREID(USDR_CS_FE, USDR_FC_BRSTN) }, { "/ll/rfe/0/base", CSR_RFE4_BASE /*VIRT_CFG_SFX_BASE + 256 */}, @@ -110,7 +110,8 @@ const usdr_dev_param_constant_t s_params_m2_lm6_1_rev000[] = { { "/ll/dsp/atcrbs/0/core", USDR_MAKE_COREID(USDR_CS_DSP, 0x23675e) }, { "/ll/dsp/atcrbs/0/base", M2PCI_REG_WR_LBDSP }, - { "/ll/sdr/0/rfic/0", (uintptr_t)"lms6002d" }, +// { "/ll/sdr/0/rfic/0", (uintptr_t)"lms6002d" }, +// { "/ll/device/name", (uintptr_t)"usdr"}, { "/ll/sdr/max_hw_rx_chans", 1 }, { "/ll/sdr/max_hw_tx_chans", 1 }, @@ -129,6 +130,13 @@ const usdr_dev_param_constant_t s_params_m2_lm6_1_rev000[] = { { "/ll/fe/0/i2c_busno/0", -1}, }; +static const vfs_constant_str_t s_params_m2_lm6_1_rev000_s[] = { + { "/ll/srx/0/rfe", "/ll/rfe/0" }, + { "/ll/sdr/0/rfic/0", "lms6002d" }, + { "/ll/device/name", "usdr"}, +}; + + static int dev_m2_lm6_1_rate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_rate_m_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -149,6 +157,9 @@ static int dev_m2_lm6_1_sdr_tx_bandwidth_set(pdevice_t ud, pusdr_vfs_obj_t obj, static int dev_m2_lm6_1_sdr_rx_gainpga_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_rx_gainvga_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm6_1_sdr_rx_gainvga2a_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm6_1_sdr_rx_gainvga2b_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); + static int dev_m2_lm6_1_sdr_rx_gainlna_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_rx_gainauto_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -158,6 +169,7 @@ static int dev_m2_lm6_1_sdr_tx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint6 static int dev_m2_lm6_1_sdr_dc_calib(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_rx_dccorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm6_1_sdr_rx_ip2corr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_tx_dccorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_rx_dc_meas_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); @@ -165,9 +177,6 @@ static int dev_m2_lm6_1_sdr_rx_dc_meas_get(pdevice_t ud, pusdr_vfs_obj_t obj, ui static int dev_m2_lm6_1_sdr_rx_tia_cfb_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_rx_tia_rfb_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -static int dev_m2_lm6_1_sdr_tx_waveform_gen_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -static int dev_m2_lm6_1_sdr_tx_enable_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); - static int dev_m2_lm6_1_sdr_tx_antennat_port_cfg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_refclk_frequency_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -187,10 +196,9 @@ static int dev_m2_lm6_1_debug_tps6381x_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj static int dev_m2_lm6_1_sdr_atcrbs_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_atcrbs_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* value); -static int dev_m2_lm6_1_sdr_tx_bbloopbackm_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); - static int dev_m2_lm6_1_sdr_senstemp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); +static int dev_m2_lm6_1_sdr_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_clkmeas_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm6_1_sdr_clkmeas_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); @@ -219,19 +227,28 @@ const usdr_dev_param_func_t s_fparams_m2_lm6_1_rev000[] = { { "/dm/sdr/0/rx/dc/meas", { NULL, dev_m2_lm6_1_sdr_rx_dc_meas_get }}, { "/dm/sdr/0/rx/dccorr", { dev_m2_lm6_1_sdr_rx_dccorr_set, dev_m2_lm6_1_sdr_dccorr_get }}, + { "/dm/sdr/0/rx/ip2corr", { dev_m2_lm6_1_sdr_rx_ip2corr_set, NULL }}, { "/dm/sdr/0/tx/dccorr", { dev_m2_lm6_1_sdr_tx_dccorr_set, NULL }}, { "/dm/sdr/0/calibrate", { dev_m2_lm6_1_sdr_dc_calib, NULL }}, + { "/dm/sdr/0/rx/frequency/lob",{ dev_m2_lm6_1_sdr_rx_freq_lob_set, NULL }}, + { "/dm/sdr/0/rx/frequency", { dev_m2_lm6_1_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency", { dev_m2_lm6_1_sdr_tx_freq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/rx/freqency/lob",{ dev_m2_lm6_1_sdr_rx_freq_lob_set, NULL }}, { "/dm/sdr/0/rx/freqency", { dev_m2_lm6_1_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/tx/freqency", { dev_m2_lm6_1_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/rx/gain", { dev_m2_lm6_1_sdr_rx_gain_set, NULL }}, { "/dm/sdr/0/tx/gain", { dev_m2_lm6_1_sdr_tx_gain_set, NULL }}, { "/dm/sdr/0/tx/gain/vga1", { dev_m2_lm6_1_sdr_tx_gain_vga1_set, NULL }}, { "/dm/sdr/0/tx/gain/vga2", { dev_m2_lm6_1_sdr_tx_gain_vga2_set, NULL }}, { "/dm/sdr/0/rx/gain/pga", { dev_m2_lm6_1_sdr_rx_gainpga_set, NULL }}, { "/dm/sdr/0/rx/gain/vga", { dev_m2_lm6_1_sdr_rx_gainvga_set, NULL }}, + { "/dm/sdr/0/rx/gain/vga2a",{ dev_m2_lm6_1_sdr_rx_gainvga2a_set, NULL }}, + { "/dm/sdr/0/rx/gain/vga2b",{ dev_m2_lm6_1_sdr_rx_gainvga2b_set, NULL }}, { "/dm/sdr/0/rx/gain/lna", { dev_m2_lm6_1_sdr_rx_gainlna_set, NULL }}, { "/dm/sdr/0/rx/gain/auto", { dev_m2_lm6_1_sdr_rx_gainauto_set, NULL }}, @@ -243,21 +260,15 @@ const usdr_dev_param_func_t s_fparams_m2_lm6_1_rev000[] = { { "/dm/sdr/0/rx/bandwidth", { dev_m2_lm6_1_sdr_rx_bandwidth_set, NULL }}, { "/dm/sdr/0/tx/bandwidth", { dev_m2_lm6_1_sdr_tx_bandwidth_set, NULL }}, - { "/dm/sdr/0/tx/waveform_gen", { dev_m2_lm6_1_sdr_tx_waveform_gen_set, NULL}}, - { "/debug/hw/lms6002d/0/reg", { dev_m2_lm6_1_debug_lms6002d_reg_set, dev_m2_lm6_1_debug_lms6002d_reg_get }}, { "/debug/hw/si5332/0/reg", { dev_m2_lm6_1_debug_si5332_reg_set, dev_m2_lm6_1_debug_si5332_reg_get }}, { "/debug/hw/tps6381x/0/reg", { dev_m2_lm6_1_debug_tps6381x_reg_set, dev_m2_lm6_1_debug_tps6381x_reg_get }}, - - { "/dm/sdr/0/tx/enable", { dev_m2_lm6_1_sdr_tx_enable_set, NULL }}, - { "/dm/sdr/0/tfe/antcfg", { dev_m2_lm6_1_sdr_tx_antennat_port_cfg_set, NULL }}, { "/dm/sdr/0/core/atcrbs/reg", { dev_m2_lm6_1_sdr_atcrbs_set, dev_m2_lm6_1_sdr_atcrbs_get }}, - { "/dm/sdr/0/tx/bbloopbackm", { dev_m2_lm6_1_sdr_tx_bbloopbackm_set, NULL }}, - + { "/dm/sdr/0/dac_vctcxo", { dev_m2_lm6_1_sdr_vctcxo_set, NULL }}, { "/dm/sdr/0/clkmeas", { dev_m2_lm6_1_sdr_clkmeas_set, dev_m2_lm6_1_sdr_clkmeas_get }}, { "/dm/revision", { NULL, dev_m2_lm6_1_sdr_revision_get }}, @@ -333,12 +344,18 @@ int dev_m2_lm6_1_sdr_clkmeas_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ov int dev_m2_lm6_1_sdr_dccorr_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) { - // struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; + struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; uint32_t v = 0; int res = dev_gpi_get32(ud->dev, 20, &v); if (res) return res; + int16_t i, q; + i = (v >> 0) & 0xffff; + q = (v >> 16) & 0xffff; + + USDR_LOG("UDEV", USDR_LOG_WARNING, "%s: DC_AAVG I=%d Q=%d\n", lowlevel_get_devname(d->base.dev), i, q); + *ovalue = v; return 0; } @@ -351,7 +368,7 @@ int dev_m2_lm6_1_debug_lms6002d_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint6 d->debug_lms6002d_last = ~0u; res = lowlevel_spi_tr32(d->base.dev, 0, 0, value & 0xffff, &d->debug_lms6002d_last); - USDR_LOG("XDEV", USDR_LOG_WARNING, "%s: Debug LMS6 REG %04x => %04x\n", + USDR_LOG("UDEV", USDR_LOG_WARNING, "%s: Debug LMS6 REG %04x => %04x\n", lowlevel_get_devname(d->base.dev), (unsigned)value, d->debug_lms6002d_last); return res; @@ -425,15 +442,6 @@ int dev_m2_lm6_1_sdr_senstemp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *o return res; } - -int dev_m2_lm6_1_sdr_tx_waveform_gen_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) -{ - //struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; - //USDR_LOG("UDEV", USDR_LOG_WARNING, "M2_LM6_1: TX WAVEFORM GEN %d\n", (int)value); - //return dev_txbuffill(d, value); - return -EINVAL; -} - int dev_m2_lm6_1_sdr_dc_calib(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; @@ -482,18 +490,6 @@ int dev_m2_lm6_1_sdr_atcrbs_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* val return res; } -int dev_m2_lm6_1_sdr_tx_bbloopbackm_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) -{ - //struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; - //int res; - // res = sfe_tx4_ctl(d->base.dev, 0, M2PCI_REG_WR_TXDMA_CNF_L, 0, 0, - // value > 1 ? true : false, - // true); - //return res; - return -EINVAL; -} - - int dev_m2_lm6_1_sdr_refclk_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; @@ -588,13 +584,6 @@ int dev_m2_lm6_1_pwren_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) return 0; } -int dev_m2_lm6_1_sdr_tx_enable_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) -{ - //struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; - USDR_LOG("UDEV", USDR_LOG_INFO, "M2_LM6_1: TX en:%d\n", (int)value); - return 0; -} - int dev_m2_lm6_1_sdr_rx_freq_lob_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; @@ -661,6 +650,18 @@ int dev_m2_lm6_1_sdr_rx_gainvga_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t return usdr_rfic_set_gain(&d->d, GAIN_RX_VGA1, value, NULL); } +int dev_m2_lm6_1_sdr_rx_gainvga2a_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; + return usdr_rfic_set_gain(&d->d, GAIN_RX_VGA2A, value, NULL); +} + +int dev_m2_lm6_1_sdr_rx_gainvga2b_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; + return usdr_rfic_set_gain(&d->d, GAIN_RX_VGA2B, value, NULL); +} + int dev_m2_lm6_1_sdr_rx_gainlna_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; @@ -677,53 +678,10 @@ int dev_m2_lm6_1_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val { struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; - // 00 –All output buffers powered down; - // 01 –First buffer enabled for LNA1 path (default); WB: 250 - 2700 - // 10 –Second buffer enabledfor LNA2 path; HB: 2700 - 3800 - // 11 –Third buffer enabledfor LNA3 path LB: external / MIXER -#if 0 - enum { - LMS6_PATH_NONE = 0, - LMS6_PATH_WB = 1, - LMS6_PATH_HB = 2, - LMS6_PATH_LB = 3, - }; - - int bandsw = -1; - if (value > 4096) { - const char* param = (const char*)value; - if (strcasecmp("rxl", param) == 0) { - value = LMS6_PATH_LB; - } else if (strcasecmp("rxw", param) == 0) { - value = LMS6_PATH_WB; bandsw = 0; - } else if (strcasecmp("rxh", param) == 0) { - value = LMS6_PATH_HB; bandsw = 1; - } else { - value = LMS6_PATH_NONE; - } - } - - if (bandsw != -1) { - int res = dev_gpo_set(d->base.dev, IGPO_RXSW, (d->revision == 0) ? !bandsw : bandsw); - if (res) - return res; - } - - return lms6002d_set_rx_path(&d->lms, value); -#endif - - // if (value > 4096) { - // const char* param = (const char*)value; - // int idx = find_param_list(param, s_rx_path_list, SIZEOF_ARRAY(s_rx_path_list)); - // if (idx < 0) { - // USDR_LOG("UDEV", USDR_LOG_WARNING, "MP_LM7_1_GPS: unknown '%s' path!\n", - // param); - // return -EINVAL; - // } - - // value = s_rx_path_list[idx].param; - // } - + // 00 – All output buffers powered down; + // 01 – First buffer enabled for LNA1 path (default); WB: 250 - 2700 + // 10 – Second buffer enabledfor LNA2 path; HB: 2700 - 3800 + // 11 – Third buffer enabledfor LNA3 path LB: external / MIXER if (value > 4096) { const char* param = (const char*)value; if (strcasecmp("rxl", param) == 0) { @@ -740,48 +698,42 @@ int dev_m2_lm6_1_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val int dev_m2_lm6_1_sdr_tx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { - //struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; - return 0; -#if 0 - int bandsw = -1; + struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; + if (value > 4096) { const char* param = (const char*)value; if (strcasecmp("txw", param) == 0) { - bandsw = 1; value = TXPATH_PA1; + value = (uintptr_t)"W"; } else if (strcasecmp("txh", param) == 0) { - bandsw = 0; value = TXPATH_PA2; - } else { - value = TXPATH_OFF; + value = (uintptr_t)"H"; } } - if (bandsw != -1) { - int res = dev_gpo_set(d->base.dev, IGPO_TXSW, (d->revision == 0) ? !bandsw : bandsw); - if (res) - return res; - } - - return lms6002d_set_tx_path(&d->lms, value); -#endif + return usdr_rfic_fe_set_txlna(&d->d, (const char *)(uintptr_t)value); } int dev_m2_lm6_1_sdr_rx_dccorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { -#if 0 struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; int res; - unsigned chan = (unsigned)(value >> 32); + //unsigned chan = (unsigned)(value >> 32); unsigned vi = (value >> 16) & 0xffff; unsigned vq = (value >> 0) & 0xffff; - if (chan == 1) - res = lms6002d_set_rxfedc(&d->lms, vi, vq); - else - res = -EINVAL; + res = lms6002d_set_rxfedc(&d->d.lms, vi, vq); + return res; +} + +int dev_m2_lm6_1_sdr_rx_ip2corr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; + int res; + //unsigned chan = (unsigned)(value >> 32); + unsigned vi = (value >> 16) & 0xffff; + unsigned vq = (value >> 0) & 0xffff; + res = lms6002d_set_rxfe_ip2corr(&d->d.lms, vi, vq); return res; -#endif - return -EINVAL; } int dev_m2_lm6_1_sdr_tx_dccorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) @@ -884,6 +836,21 @@ usdr_dev_t* get_usdr_dev(pdevice_t udev) return &d->d; } +int dev_m2_lm6_1_sdr_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm6_1 *d = (struct dev_m2_lm6_1 *)ud; + board_ext_pciefe_t* board_fe = (board_ext_pciefe_t*)device_fe_to(d->fe, "pciefe"); + board_exm2pe_t* board = (board_exm2pe_t*)device_fe_to(d->fe, "exm2pe"); + if (board_fe) { + return board_ext_pciefe_set_dac(board_fe, value); + } else if (board) { + return board_exm2pe_set_dac(board, value); + } + + return -EINVAL; +} + + static int usdr_device_m2_lm6_1_initialize(pdevice_t udev, unsigned pcount, const char** devparam, const char** devval) { @@ -967,6 +934,24 @@ int usdr_device_m2_lm6_1_create_stream(device_t* dev, const char* sid, const cha return res; } + if (d->d.rx_lo == 0) { + res = usdr_rfic_fe_set_freq(&d->d, false, 320e6, NULL); + if (res) { + return res; + } + + d->d.rx_lo = 0; + } + + res = usdr_calib_dc(&d->d, true); + if (res) { + return res; + } + + if (d->d.rx_lo != 0) { + usdr_rfic_fe_set_freq(&d->d, false, d->d.rx_lo, NULL); + } + res = create_sfetrx4_stream(dev, CORE_SFERX_DMA32_R0, dformat, channels->count, &lchans, pktsyms, flags, M2PCI_REG_WR_RXDMA_CONFIRM, VIRT_CFG_SFX_BASE, 0, SRF4_FIFOBSZ, CSR_RFE4_BASE, &d->rx, &chans); @@ -1031,6 +1016,12 @@ int usdr_device_m2_lm6_1_create(lldev_t dev, /*UNUSED*/ device_id_t devid) if (res) goto failed_tree_creation; + res = vfs_add_const_str_vec(&d->base.rootfs, + s_params_m2_lm6_1_rev000_s, + SIZEOF_ARRAY(s_params_m2_lm6_1_rev000_s)); + if (res) + goto failed_tree_creation; + res = usdr_vfs_obj_param_init_array(&d->base, s_fparams_m2_lm6_1_rev000, SIZEOF_ARRAY(s_fparams_m2_lm6_1_rev000)); diff --git a/src/lib/device/m2_lm6_1/usdr_ctrl.c b/src/lib/device/m2_lm6_1/usdr_ctrl.c index 2b70c714..ee7a5949 100644 --- a/src/lib/device/m2_lm6_1/usdr_ctrl.c +++ b/src/lib/device/m2_lm6_1/usdr_ctrl.c @@ -21,6 +21,8 @@ #include "../generic_usdr/generic_regs.h" +#include "../ipblks/fgearbox.h" + // Clock configuration // rev4/rev3 rev2 rev1 // port0 - LMS_PLLCLK | RXCLK | RXCLK @@ -57,6 +59,11 @@ enum usdr_rev000 { SPI_LMS6 = 0, }; +enum lms6_vios { + LMS6_VIO_NORM = 1800, + LMS6_VIO_BOOST = 1950, +}; + // RX chain // LNA_GAIN -> mixer -> VGA1_GAIN -> lpf -> VGA2_GAIN[a,b] -> adc @@ -243,15 +250,6 @@ static int _usdr_set_lna_rx(usdr_dev_t *d, unsigned cfg_idx) int res; d->rx_cfg_path = cfg_idx; - /* - if (d->rx_lna_lb_active) { - switch (band) { - case RFE_LNAL: band = RFE_LBL; txlbband = 2; break; - case RFE_LNAW: band = RFE_LBW; txlbband = 1; break; - case RFE_LNAH: band = RFE_LBH; txlbband = 1; break; - } - } - */ USDR_LOG("UDEV", USDR_LOG_INFO, "%s: Set RX band to %d (%s/%s) %s [TXLB:%d => ATEEN=%d,%d]\n", lowlevel_get_devname(d->base.dev), band, @@ -260,9 +258,6 @@ static int _usdr_set_lna_rx(usdr_dev_t *d, unsigned cfg_idx) txlbband, 0, 0 /* d->trf_lb_atten, d->trf_lb_loss */ ); res = lms6002d_set_rx_path(&d->lms, ((d->rx_rfic_lna = band))); - //if (txlbband) { - // res = (res) ? res : lms7_trf_set_path(&d->lmsstate, ((d->tx_rfic_band = txlbband))); - //} if (res) return res; @@ -280,11 +275,6 @@ static int _usdr_set_lna_tx(usdr_dev_t *d, unsigned cfg_idx) USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Set TX band to %d (%s/%s)\n", lowlevel_get_devname(d->base.dev), band, cfg->name0, cfg->name1); -// if (d->rx_lna_lb_active) { -// res = lms7_rfe_set_path(&d->lmsstate, band == 1 ? RFE_LBW : RFE_LBL, -// d->rx_run[0], d->rx_run[1]); -// } - res = lms6002d_set_tx_path(&d->lms, ((d->tx_rfic_band = band))); if (res) return res; @@ -346,8 +336,8 @@ static int _usdr_signal_event(usdr_dev_t *d, enum sigtype t) case USDR_SAMPLERATE_CHANGED: if (d->mexir_en && (d->cfg_auto_rx[d->rx_cfg_path].band == RXPATH_LNA3)) { d->rfic_rx_lo = d->mixer_lo + d->rx_lo; - USDR_LOG("UDEV", USDR_LOG_ERROR, "%s: RX LO corrected to %.3f Mhz (LNB = %.3f)\n", - lowlevel_get_devname(d->base.dev), d->rfic_rx_lo / 1.0e6, d->mixer_lo / 1.0e6); + USDR_LOG("UDEV", USDR_LOG_ERROR, "%s: RX LO corrected to %.3f -> %.3f Mhz (LNB = %.3f)\n", + lowlevel_get_devname(d->base.dev), d->rx_lo / 1.0e6, d->rfic_rx_lo / 1.0e6, d->mixer_lo / 1.0e6); return lms6002d_tune_pll(&d->lms, false, d->rfic_rx_lo); } @@ -360,6 +350,8 @@ int usdr_set_lob_freq(struct usdr_dev *d, unsigned freqlob) { if (d->si_vco_freq == 0 || d->rawsamplerate == 0) return -EINVAL; + if (freqlob == 0) + return -EINVAL; float ceff = d->si_vco_freq / freqlob; if (ceff < 8) @@ -384,13 +376,60 @@ int usdr_set_samplerate_ex(struct usdr_dev *d, unsigned freq = rate << 1; //Link speed x2 sample rate struct si5332_layout_info nfo = { d->fref, freq }; int res = 0; + unsigned i = 0; + unsigned int_decim[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + + /* + for (unsigned ii = 0; ii < SIZEOF_ARRAY(int_decim); ii++) { + if (rxrate * int_decim[ii] < 60e6) + i = ii; + else + break; + } + */ + + nfo.out *= int_decim[i]; + + if (rate >= 60e6 && !d->vio_boost) { + USDR_LOG("UDEV", USDR_LOG_WARNING, "Boosting Vio to get stable samplerates over 60Msps\n"); + + res = res ? res : lp8758_vout_set(dev, d->subdev, I2C_BUS_LP8758, 3, LMS6_VIO_BOOST); + d->vio_boost = true; + } else if (rate < 60e6 && d->vio_boost) { + res = res ? res : lp8758_vout_set(dev, d->subdev, I2C_BUS_LP8758, 3, LMS6_VIO_NORM); + d->vio_boost = false; + } res = res ? res : _usdr_pwr_state(d, false, true); res = res ? res : si5332_set_layout(dev, 0, I2C_BUS_SI5332A, &nfo, d->hw_board_rev == USDR_REV_3 ? false : true, d->si_vco_div, &d->si_vco_freq); // TODO: Add ability to alter it d->mixer_lo = d->si_vco_freq / d->si_vco_div; - d->rawsamplerate = rate; + d->rawsamplerate = nfo.out; + /* + if (d->rxbb_decim != int_decim[i]) { + res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSP_RX_CTRL, 0x0); + res = (res) ? res : fgearbox_load_fir(d->base.dev, IGPO_DSP_RX_CFG, (fgearbox_firs_t)d->rxbb_decim, DSP_7SERIES); + res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_DSP_RX_CTRL, 0x0); + } + */ + + // Update FE + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, 7); + res = res ? res : usleep(10); + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, 0); + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, (1 << 24) | 1); + + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, (8 << 24) | 0); + res = res ? res : lowlevel_reg_wr32(dev, 0, REG_CFG_PHY_0, (9 << 24) | 16384); + + uint32_t v = 0; + res = res ? res : lowlevel_reg_rd32(dev, 0, REG_CFG_PHY_0, &v); + + USDR_LOG("UDEV", USDR_LOG_WARNING, "V=%08x\n", v); + + d->rxbb_decim = int_decim[i]; + d->txbb_intr = int_decim[i]; // Apply automatic RF Freq correction if external mixer is active if (d->mexir_en) { @@ -405,7 +444,7 @@ int usdr_set_samplerate_ex(struct usdr_dev *d, res = res ? res : lms6002d_set_bandwidth(&d->lms, true, txrate); } - USDR_LOG("UDEV", USDR_LOG_INFO, "RX_RATE %.3f TX_RATE %.3f MXLO %.3f\n", rxrate / 1.0e6, txrate / 1.0e6, d->mixer_lo / 1.0e6); + USDR_LOG("UDEV", USDR_LOG_INFO, "INTR %d RX_RATE %.3f TX_RATE %.3f MXLO %.3f\n", int_decim[i], rxrate / 1.0e6, txrate / 1.0e6, d->mixer_lo / 1.0e6); return res; } @@ -413,19 +452,26 @@ int usdr_set_samplerate_ex(struct usdr_dev *d, int usdr_rfic_streaming_up(struct usdr_dev *d, unsigned dir) { int res = 0; + bool wakeup_delay = false; if (dir & RFIC_LMS6_TX) { + wakeup_delay |= !d->tx_run; d->tx_run = true; res = (res) ? res : _usdr_pwr_state(d, true, true); } if (dir & RFIC_LMS6_RX) { + wakeup_delay |= !d->rx_run; d->rx_run = true; res = (res) ? res : _usdr_pwr_state(d, false, true); } + if (wakeup_delay) { + usleep(1000); + } + // TODO enable clock - //return lms6002d_rf_enable(&d->lms, d->tx_run, d->rx_run); + // return lms6002d_rf_enable(&d->lms, d->tx_run, d->rx_run); return res; } @@ -580,8 +626,11 @@ int usdr_init(struct usdr_dev *d, int ext_clk, unsigned ext_fref) if (res) return res; - if (devid != 0x1114) { + if (devid != TMP114_DEVICE_ID) { USDR_LOG("UDEV", USDR_LOG_ERROR, "TMP114 DEVID=%x\n", devid); + if (!getenv("USDR_IGNORE_TMP")) { + return -EIO; + } } } @@ -605,11 +654,11 @@ int usdr_init(struct usdr_dev *d, int ext_clk, unsigned ext_fref) //res = lp8758_vout_set(dev, d->subdev, I2C_BUS_LP8758, 1, // d->hw_board_rev == USDR_REV_1 ? 3360 : 1800); + // Set GPIO bank voltage to 1.8V to be compatible with xSDR res = res ? res : lp8758_vout_set(dev, d->subdev, I2C_BUS_LP8758, 1, 1800); - // Increase 1.8V rail to get more samplerate - // res = lp8758_vout_set(dev, d->subdev, I2C_BUS_LP8758, 3, 2100); - // if (res) - // return res; + + // Reset 1.8V rail default VIO + res = res ? res : lp8758_vout_set(dev, d->subdev, I2C_BUS_LP8758, 3, LMS6_VIO_NORM); res = res ? res : lp8758_vout_ctrl(dev, d->subdev, I2C_BUS_LP8758, 0, 1, 1); //1v0 -- less affected res = res ? res : lp8758_vout_ctrl(dev, d->subdev, I2C_BUS_LP8758, 1, 1, 1); //2v5 -- less affected @@ -693,19 +742,6 @@ int usdr_dtor(struct usdr_dev *d) return 0; } -// We need to enable RX RF to get forward clocking -#if 0 -int usdr_pwren(struct usdr_dev *d, bool on) -{ - // TODO RX / TX selection - lms6002d_rf_enable(&d->lms, false, on); - // TX off - lms6002d_rf_enable(&d->lms, true, on /* false */); - return 0; -} -#endif - - static int _usdr_lms6002_dc_calib(struct usdr_dev *d) { @@ -714,7 +750,7 @@ int _usdr_lms6002_dc_calib(struct usdr_dev *d) res = res ? res : lms6002d_cal_lpf(&d->lms); for (unsigned i = 0; i < 16; i++) { - res = res ? res : lms6002d_cal_lpf_bandwidth(&d->lms, i); + res = res ? res : lms6002d_cal_lpf_bandwidth(&d->lms, i, i == 0); } res = res ? res : lms6002d_cal_txrxlpfdc(&d->lms, true); @@ -726,6 +762,9 @@ int _usdr_lms6002_dc_calib(struct usdr_dev *d) res = res ? res : lms6002d_cal_vga2(&d->lms); res = res ? res : lms6002d_set_rx_extterm(&d->lms, false); + // Restore initial VGA2 gain values + res = res ? res : lms6002d_set_rxvga2ab_gain(&d->lms, d->rx_vga2a, d->rx_vga2b); + return res; } @@ -764,7 +803,7 @@ int usdr_rfic_fe_set_freq(struct usdr_dev *d, lms6002d_tune_pll(&d->lms, 0, freq); } // LDO may not be ready, check again - usleep(1000); + usleep(5000); res = lms6002d_tune_pll(&d->lms, dir_tx, freq); } if (res == -ENOLCK) { @@ -925,6 +964,21 @@ int usdr_rfic_fe_set_rxlna(struct usdr_dev *d, return _usdr_set_lna_rx(d, d->rx_rfic_path); } +int usdr_rfic_fe_set_txlna(struct usdr_dev *d, + const char *lna) +{ + int res = get_antenna_cfg_by_name(lna, d->cfg_auto_tx, SIZEOF_ARRAY(d->cfg_auto_tx)); + USDR_LOG("UDEV", USDR_LOG_INFO, "TX_PATH set to %s from `%s`\n", (res < 0) ? "AUTO" : d->cfg_auto_tx[res].name0, lna); + + if (res == -1) { + d->tx_rfic_path = USDR_TX_AUTO; + return _usdr_signal_event(d, USDR_TX_LNA_CHANGED); + } + + d->tx_rfic_path = res; + return _usdr_set_lna_tx(d, d->tx_rfic_path); +} + int usdr_set_rx_port_switch(struct usdr_dev *d, unsigned path) { diff --git a/src/lib/device/m2_lm6_1/usdr_ctrl.h b/src/lib/device/m2_lm6_1/usdr_ctrl.h index d8cf456f..5ce90796 100644 --- a/src/lib/device/m2_lm6_1/usdr_ctrl.h +++ b/src/lib/device/m2_lm6_1/usdr_ctrl.h @@ -65,7 +65,6 @@ struct usdr_dev lms6002d_state_t lms; unsigned refclkpath; unsigned fref; - unsigned rawsamplerate; uint8_t rx_cfg_path; uint8_t tx_cfg_path; @@ -75,25 +74,28 @@ struct usdr_dev uint8_t tx_rfic_path; uint8_t tx_rfic_band; - unsigned dsp_clk; - - unsigned rx_lo; - unsigned tx_lo; - - // Gain settings uint8_t rx_lna; uint8_t rx_vga1; uint8_t rx_vga2a; uint8_t rx_vga2b; - bool rx_run; bool tx_run; bool rx_pwren; bool tx_pwren; bool mexir_en; + bool vio_boost; + + unsigned rawsamplerate; + unsigned rxbb_decim; + unsigned txbb_intr; + + unsigned dsp_clk; + unsigned rx_lo; + unsigned tx_lo; + unsigned mixer_lo; unsigned rfic_rx_lo; @@ -128,6 +130,8 @@ int usdr_set_lob_freq(struct usdr_dev *d, unsigned freqlob); int usdr_rfic_fe_set_rxlna(struct usdr_dev *d, const char* lna); +int usdr_rfic_fe_set_txlna(struct usdr_dev *d, + const char *lna); int usdr_rfic_fe_set_freq(struct usdr_dev *d, bool dir_tx, @@ -149,11 +153,6 @@ int usdr_init(struct usdr_dev *d, int ext_clk, unsigned int ext_fref); int usdr_dtor(struct usdr_dev *d); -// int usdr_pwren(struct usdr_dev *d, bool on); - -int usdr_lob_set(struct usdr_dev *d, unsigned freq); - - int usdr_calib_dc(struct usdr_dev *d, bool rx); int usdr_gettemp(struct usdr_dev *d, int* temp256); @@ -179,6 +178,8 @@ enum { IGPO_LED = 8, IGPO_DCCORR = 9, + IGPO_DSP_RX_CTRL = 10, + IGPO_FRONT = 15, IGPO_CLKMEAS = 16, IGPO_ENABLE_OSC = 17, diff --git a/src/lib/device/m2_lm7_1/lms7002m_ctrl.c b/src/lib/device/m2_lm7_1/lms7002m_ctrl.c index 41632e4b..fe053553 100644 --- a/src/lib/device/m2_lm7_1/lms7002m_ctrl.c +++ b/src/lib/device/m2_lm7_1/lms7002m_ctrl.c @@ -18,13 +18,6 @@ #define MAX(x,y) (((x) > (y)) ? (x) : (y)) #endif -enum sigtype { - XSDR_TX_LO_CHANGED, - XSDR_RX_LO_CHANGED, - XSDR_TX_LNA_CHANGED, - XSDR_RX_LNA_CHANGED, -}; - static unsigned _ulog(unsigned d) { switch (d) { @@ -49,7 +42,7 @@ static int get_antenna_cfg_by_name(const char* name, const freq_auto_band_map_t* return -1; } -static int get_antenna_cfg_by_freq(unsigned freq, const freq_auto_band_map_t* maps, unsigned max) +static int get_antenna_cfg_by_freq(uint64_t freq, const freq_auto_band_map_t* maps, unsigned max) { unsigned i; for (i = 0; i < max - 1; i++) { @@ -84,6 +77,7 @@ int lms7002m_init(lms7002_dev_t* d, lldev_t dev, unsigned subdev, unsigned refcl d->lmsstate.dev = dev; d->lmsstate.subdev = subdev; d->fref = refclk; + d->on_custom_signal = NULL; d->rx_rfic_path = XSDR_RX_AUTO; d->tx_rfic_path = XSDR_TX_AUTO; @@ -113,8 +107,8 @@ static int _lms7002m_set_lna_rx(lms7002_dev_t *d, unsigned cfg_idx) txlbband = lms7002m_trf_from_rfe_path(band); } - USDR_LOG("LMS7", USDR_LOG_INFO, "%s: Set RX band to %d (%s/%s) %s [TXLB:%d => ATEEN=%d,%d]\n", - lowlevel_get_devname(d->lmsstate.dev), band, + USDR_LL_LOG(d->lmsstate.dev, "LMS7", USDR_LOG_INFO, "Set RX band to %d (%s/%s) %s [TXLB:%d => ATEEN=%d,%d]\n", + band, cfg->name0, cfg->name1, d->rx_lna_lb_active ? "loopback enabled" : "", txlbband, d->trf_lb_atten, d->trf_lb_loss); @@ -141,8 +135,8 @@ static int _lms7002m_set_lna_tx(lms7002_dev_t *d, unsigned cfg_idx) int res = 0; d->tx_cfg_path = cfg_idx; - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Set TX band to %d (%s/%s)\n", - lowlevel_get_devname(d->lmsstate.dev), band, cfg->name0, cfg->name1); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Set TX band to %d (%s/%s)\n", + band, cfg->name0, cfg->name1); if (d->rx_lna_lb_active) { res = lms7002m_rfe_path(&d->lmsstate, @@ -183,9 +177,11 @@ static int _lms7002m_signal_event(lms7002_dev_t *d, enum sigtype t) res = lms7002m_mac_set(&d->lmsstate, LMS7_CH_AB); case XSDR_RX_LNA_CHANGED: if (d->rx_rfic_path == XSDR_RX_AUTO) { - cfgidx = get_antenna_cfg_by_freq(d->rx_lo, d->cfg_auto_rx, MAX_RX_BANDS); - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Auto RX band selection: %s\n", - lowlevel_get_devname(d->lmsstate.dev), d->cfg_auto_rx[cfgidx].name0); + cfgidx = (d->on_custom_signal) ? + d->on_custom_signal(d, t) : + get_antenna_cfg_by_freq(d->rx_lo, d->cfg_auto_rx, MAX_RX_BANDS); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Auto RX band selection: %s\n", + d->cfg_auto_rx[cfgidx].name0); res = (res) ? res : _lms7002m_set_lna_rx(d, cfgidx); } @@ -194,9 +190,11 @@ static int _lms7002m_signal_event(lms7002_dev_t *d, enum sigtype t) res = lms7002m_mac_set(&d->lmsstate, LMS7_CH_AB); case XSDR_TX_LNA_CHANGED: if (d->tx_rfic_path == XSDR_TX_AUTO) { - cfgidx = get_antenna_cfg_by_freq(d->tx_lo, d->cfg_auto_tx, MAX_TX_BANDS); - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Auto TX band selection: %s\n", - lowlevel_get_devname(d->lmsstate.dev), d->cfg_auto_tx[cfgidx].name0); + cfgidx = (d->on_custom_signal) ? + d->on_custom_signal(d, t) : + get_antenna_cfg_by_freq(d->tx_lo, d->cfg_auto_tx, MAX_TX_BANDS); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Auto TX band selection: %s\n", + d->cfg_auto_tx[cfgidx].name0); res = (res) ? res : _lms7002m_set_lna_tx(d, cfgidx); } @@ -230,8 +228,7 @@ int lms7002m_set_gain(lms7002_dev_t *d, "TX_PGA", "{invalid}", }; - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Set gain %s to %d on %d channel\n", - lowlevel_get_devname(d->lmsstate.dev), + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Set gain %s to %d on %d channel\n", s_gains[MIN(gain_type, SIZEOF_ARRAY(s_gains))], gain, channel); @@ -257,7 +254,6 @@ int lms7002m_set_gain(lms7002_dev_t *d, res = lms7002m_rbb_pga(&d->lmsstate, gain * 10); break; case RFIC_LMS7_RX_LB_GAIN: - res = lms7002m_rfe_gain(&d->lmsstate, RFE_GAIN_RFB, gain * 10, &aret); actual = aret / 10; @@ -270,6 +266,7 @@ int lms7002m_set_gain(lms7002_dev_t *d, if (gain > 0) gain = 0; actual = gain; + res = lms7002m_trf_gain(&d->lmsstate, TRF_GAIN_PAD, -10 * gain, &aret); if (channel & LMS7_CH_A) d->tx_loss[0] = aret / -10; @@ -284,13 +281,10 @@ int lms7002m_set_gain(lms7002_dev_t *d, d->trf_lb_atten = -gain; d->trf_lb_loss = 0; //res = lms7002m_trf_gain(&d->lmsstate, d->trf_lb_atten, d->trf_lb_loss); - res = lms7002m_trf_gain(&d->lmsstate, TRF_GAIN_PAD, -10 * gain, &aret); + res = lms7002m_trf_gain(&d->lmsstate, TRF_GAIN_LB, -10 * gain, &aret); break; case RFIC_LMS7_TX_PGA_GAIN: - if (gain > 63) - gain = 63; - else if (gain < -62) - gain = -62; + gain = clamp(gain, -62, 63); actual = 0; res = lms7002m_tbb_gain(&d->lmsstate, gain); @@ -298,8 +292,8 @@ int lms7002m_set_gain(lms7002_dev_t *d, default: return -EINVAL; } - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Set gain %d (%d) to %d on %d channel => actual = %.3f\n", - lowlevel_get_devname(d->lmsstate.dev), gain, ogain, gain_type, channel, actual / 1e6); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Set gain %d (%d) to %d on %d channel => actual = %.3f\n", + gain, ogain, gain_type, channel, actual / 1e6); if (actualgain) *actualgain = actual; @@ -338,8 +332,8 @@ int lms7002m_fe_set_freq(lms7002_dev_t *d, lms7002m_sxx_disable(&d->lmsstate, SXX_RX); } - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: FE_FREQ path=%d type=%d freq=%f ch=%d\n", - lowlevel_get_devname(d->lmsstate.dev), path, type, freq, channel); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "FE_FREQ path=%d type=%d freq=%f ch=%d\n", + path, type, freq, channel); res = lms7002m_sxx_tune(&d->lmsstate, path, d->fref, (unsigned)freq, @@ -384,8 +378,7 @@ static int _lms7002m_lb_status_changes(lms7002_dev_t *d) { int res = 0; if (!d->rx_lna_lb_active) { - USDR_LOG("UDEV", USDR_LOG_WARNING, "%s: Turning off loopback\n", - lowlevel_get_devname(d->lmsstate.dev)); + USDR_LL_LOG(d->lmsstate.dev, "UDEV", USDR_LOG_WARNING, "Turning off loopback\n"); res = (res) ? res : lms7002m_trf_gain(&d->lmsstate, TRF_GAIN_PAD, -10 * ((d->lmsstate.reg_mac & 1) ? d->tx_loss[0] : d->tx_loss[1]), @@ -397,15 +390,9 @@ static int _lms7002m_lb_status_changes(lms7002_dev_t *d) res = (res) ? res : lms7002m_trf_gain(&d->lmsstate, TRF_GAIN_LB, 0, NULL); - res = (res) ? res : lms7002m_trf_gain(&d->lmsstate, - TRF_GAIN_PAD, //TRF_GAIN_LB, - -10 * d->trf_lb_atten, - NULL); - // res = (res) ? res : lms7002m_rfe_gain(&d->lmsstate, - // d->rfe_lb_atten, &lb_loss); - USDR_LOG("UDEV", USDR_LOG_WARNING, "%s: Turning on loopback RX + TX loss: -- + %d dB\n", - lowlevel_get_devname(d->lmsstate.dev), /*lb_loss,*/ d->trf_lb_atten); + USDR_LL_LOG(d->lmsstate.dev, "UDEV", USDR_LOG_WARNING, "Turning on loopback RX + TX loss: -- + %d dB\n", + /*lb_loss,*/ d->trf_lb_atten); } // Update LNA / LOOPBACK gain after LB activated / deactivated @@ -434,13 +421,11 @@ int lms7002m_rfe_set_path(lms7002_dev_t *d, case XSDR_RX_AUTO: break; case XSDR_RX_ADC_EXT: - USDR_LOG("UDEV", USDR_LOG_INFO, "%s: Activating external ADC input NOT IMPLEMENTED\n", - lowlevel_get_devname(d->lmsstate.dev)); + USDR_LL_LOG(d->lmsstate.dev, "UDEV", USDR_LOG_INFO, "Activating external ADC input NOT IMPLEMENTED\n"); return -EINVAL; default: - USDR_LOG("UDEV", USDR_LOG_WARNING, "%s: Unknown FE path %d\n", - lowlevel_get_devname(d->lmsstate.dev), path); + USDR_LL_LOG(d->lmsstate.dev, "UDEV", USDR_LOG_WARNING, "Unknown FE path %d\n", path); return -EINVAL; } @@ -451,7 +436,8 @@ int lms7002m_rfe_set_path(lms7002_dev_t *d, d->rx_rfic_path = path; if (path == XSDR_RX_AUTO) { - cfgidx = get_antenna_cfg_by_freq(d->rx_lo, d->cfg_auto_rx, MAX_RX_BANDS); + res = _lms7002m_signal_event(d, XSDR_RX_LNA_CHANGED); + goto finish; } else { cfgidx = get_antenna_cfg_by_band(band, d->cfg_auto_rx, MAX_RX_BANDS); } @@ -460,6 +446,8 @@ int lms7002m_rfe_set_path(lms7002_dev_t *d, res = (res) ? res : _lms7002m_set_lna_rx(d, cfgidx); + +finish: if (lb_change) { res = (res) ? res : _lms7002m_lb_status_changes(d); } @@ -485,8 +473,7 @@ int lms7002m_tfe_set_path(lms7002_dev_t *d, d->rx_lna_lb_active = false; return _lms7002m_signal_event(d, XSDR_TX_LNA_CHANGED); default: - USDR_LOG("UDEV", USDR_LOG_WARNING, "%s: Unknown FE path %d\n", - lowlevel_get_devname(d->lmsstate.dev), path); + USDR_LL_LOG(d->lmsstate.dev, "UDEV", USDR_LOG_WARNING, "Unknown FE path %d\n", path); return -EINVAL; } @@ -592,8 +579,6 @@ int lms7002m_bb_set_freq(lms7002_dev_t *d, int64_t freq) { int res; - const char* devstr = lowlevel_get_devname(d->lmsstate.dev); - res = _lms7002m_check_chan(channel); if (res) return res; @@ -602,13 +587,13 @@ int lms7002m_bb_set_freq(lms7002_dev_t *d, double conv_freq = d->cgen_clk / (dir_tx ? d->txcgen_div : d->rxcgen_div); double rel_freq = freq / conv_freq; if (rel_freq > 0.5 || rel_freq < -0.5) { - USDR_LOG("XDEV", USDR_LOG_WARNING, - "%s: NCO %s ouf of range, requested %.3f while DAC %.3f\n", - devstr, dir_tx ? "TX" : "RX", + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_WARNING, + "NCO %s ouf of range, requested %.3f while DAC %.3f\n", + dir_tx ? "TX" : "RX", rel_freq / 1000, conv_freq / 1000); return -EINVAL; } - int pfreq = rel_freq * 4294967296; + int pfreq = rel_freq * 4294967296.0; if (channel & LMS7_CH_A) opt_u32_set_val(&dsp_f[0], pfreq); if (channel & LMS7_CH_B) @@ -624,8 +609,8 @@ int lms7002m_bb_set_freq(lms7002_dev_t *d, return res; } - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: NCO ch=%d type=%d freq=%lld\n", - devstr, channel, dir_tx, (long long)freq); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "NCO ch=%d type=%d freq=%lld\n", + channel, dir_tx, (long long)freq); return 0; } @@ -679,7 +664,7 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, lms7002m_mac_mode_t rx_chs_i, unsigned rx_flags, lms7002m_mac_mode_t tx_chs_i, unsigned tx_flags) { - unsigned rx_chs, tx_chs; + unsigned rx_chs = LMS7_CH_NONE, tx_chs = LMS7_CH_NONE; bool rxafen_a = d->rx_run[0]; bool rxafen_b = d->rx_run[1]; @@ -687,7 +672,6 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, bool txafen_b = d->tx_run[1]; int res; unsigned ich; - const char* devstr = lowlevel_get_devname(d->lmsstate.dev); if (dir & RFIC_LMS7_RX) { if (_lms7002m_check_chan(rx_chs_i)) { @@ -728,8 +712,8 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, if (res) return res; - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: AFE TX=[%d;%d] RX=[%d;%d]\n", - devstr, txafen_a, txafen_b, rxafen_a, rxafen_b); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "AFE TX=[%d;%d] RX=[%d;%d]\n", + txafen_a, txafen_b, rxafen_a, rxafen_b); if (dir & RFIC_LMS7_RX) { res = res ? res : lms7002m_mac_set(&d->lmsstate, rx_chs); @@ -765,20 +749,20 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, if (d->rx_bw[ich].set) { bandwidth = d->rx_bw[ich].value; - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: RBB Restore BW[%d]=%d\n", - devstr, ich, d->rx_bw[ich].value); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "RBB Restore BW[%d]=%d\n", + ich, d->rx_bw[ich].value); } else { - bandwidth = d->cgen_clk / d->rxcgen_div / d->rxtsp_div / d->rx_host_decim; - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: No RBB[%d] was set; defaulting to current rx samplerate %u\n", - devstr, ich, bandwidth); + bandwidth = d->cgen_clk / d->rxcgen_div / d->rxtsp_div / d->rx_dsp_decim; + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "No RBB[%d] was set; defaulting to current rx samplerate %u\n", + ich, bandwidth); } res = lms7002m_rbb_bandwidth(d, bandwidth, false); if (res) return res; if (d->rx_dsp[ich].set) { - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: RBB Restore DSP[%d]=%d\n", - devstr, ich, d->rx_dsp[ich].value); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "RBB Restore DSP[%d]=%d\n", + ich, d->rx_dsp[ich].value); freqoffset = d->rx_dsp[ich].value; } else { freqoffset = 0; @@ -811,21 +795,21 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, return res; if (d->tx_bw[ich].set) { - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: TBB Restore BW[%d]=%d\n", - devstr, ich, d->tx_bw[ich].value); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "TBB Restore BW[%d]=%d\n", + ich, d->tx_bw[ich].value); bandwidth = d->tx_bw[ich].value; } else { - bandwidth = d->cgen_clk / d->txcgen_div / d->txtsp_div / d->tx_host_inter; - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: No TBB[%d] was set; defaulting to current rx samplerate %u\n", - devstr, ich, bandwidth); + bandwidth = d->cgen_clk / d->txcgen_div / d->txtsp_div / d->tx_dsp_inter; + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "No TBB[%d] was set; defaulting to current rx samplerate %u\n", + ich, bandwidth); } res = lms7002m_tbb_bandwidth(d, bandwidth, false); if (res) return res; if (d->tx_dsp[ich].set) { - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: TBB Restore DSP[%d]=%d\n", - devstr, ich, d->tx_dsp[ich].value); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "TBB Restore DSP[%d]=%d\n", + ich, d->tx_dsp[ich].value); freqoffset = d->tx_dsp[ich].value; } else { freqoffset = 0; @@ -850,11 +834,11 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, lms7002m_limelight_conf_t nlml_mode = d->lml_mode; if (rx_flags & RFIC_DIGITAL_LB) { - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Enable digital loopback\n", devstr); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Enable digital loopback\n"); nlml_mode.rx_tx_dig_loopback = 1; } if (rx_flags & RFIC_LFSR) { - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: Enable RX LFSR\n", devstr); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Enable RX LFSR\n"); nlml_mode.rx_lfsr = 1; } @@ -866,12 +850,12 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, d->lml_mode = nlml_mode; } - res = lms7002m_dc_corr_en(&d->lmsstate, d->rx_run[0], d->rx_run[1], d->tx_run[0], d->tx_run[1]); - if (res) - return res; + //res = lms7002m_dc_corr_en(&d->lmsstate, d->rx_run[0], d->rx_run[1], d->tx_run[0], d->tx_run[1]); + //if (res) + // return res; //res = lms7_dc_init(&d->lmsstate, d->rx_run[0], d->rx_run[1], d->tx_run[0], d->tx_run[1]); - USDR_LOG("XDEV", USDR_LOG_INFO, "%s: configure done RUN RX:%d%d TX:%d%d\n", devstr, + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "configure done RUN RX:%d%d TX:%d%d\n", d->rx_run[1], d->rx_run[0], d->tx_run[1], d->tx_run[0]); return 0; } @@ -901,7 +885,8 @@ enum { int lms7002m_samplerate(lms7002_dev_t *d, unsigned rxrate, unsigned txrate, unsigned adcclk, unsigned dacclk, - unsigned flags, const bool rx_port_1) + unsigned flags, const bool rx_port_1, + unsigned rx_dec, unsigned tx_int) { //bool no_8ma = false; lms7002m_limelight_conf_t cfg; @@ -927,8 +912,8 @@ int lms7002m_samplerate(lms7002_dev_t *d, unsigned mpy_dac = 4; // Might be 4,2,1 unsigned rxdiv = 1; unsigned txdiv = 1; - unsigned tx_host_inter = 1; // Off chip extra interpolator - unsigned rx_host_decim = 1; // Off chip extra decimator + unsigned tx_dsp_inter = 1; // Off chip extra interpolator + unsigned rx_dsp_decim = 1; // Off chip extra decimator unsigned tx_host_mul = 1; unsigned rx_host_div = 1; unsigned txmaster_min = mpy_dac * dacclk; @@ -939,14 +924,14 @@ int lms7002m_samplerate(lms7002_dev_t *d, for (unsigned citer = 0; citer < (extended_cgen_range ? 2 : 1); citer++) { unsigned mindecint_rx = (sisoddr_rx || extclk_rx) ? 1 : 2; unsigned mindecint_tx = (sisoddr_tx || extclk_tx) ? 1 : 2; - unsigned cgen_max = extended_cgen_range && (citer == 0) ? 380e6 : 320e6; + unsigned cgen_max = extended_cgen_range && (citer == 0) ? 370e6 : 320e6; cgen_rate = MAX(txmaster_min, rxmaster_min); if (cgen_rate < 1) { cgen_rate = MAX(mindecint_rx * rxrate * rx_host_div * mpy_adc, mindecint_tx * txrate * tx_host_mul * mpy_dac); - USDR_LOG("XDEV", USDR_LOG_NOTE, "Initial CGEN set to %03.1f Mhz\n", cgen_rate / 1.0e6); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_NOTE, "Initial CGEN set to %03.1f Mhz\n", cgen_rate / 1.0e6); // For low sample rate increase DAC/ADC due to frequency aliasing if ((rxrate > 1 && rxrate < 2e6) || (txrate > 1 && txrate < 2e6) || opt_decim_inter) { @@ -957,7 +942,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, if (rx_ndiv > LMS7_DECIM_MAX || tx_ndiv > LMS7_INTER_MAX) break; - USDR_LOG("XDEV", USDR_LOG_NOTE, "Increase RXdiv=%2d TXdiv=%2d => CGEN %03.1f Mhz\n", + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_NOTE, "Increase RXdiv=%2d TXdiv=%2d => CGEN %03.1f Mhz\n", rx_ndiv, tx_ndiv, cgen_rate * 2 / 1.0e6); } } @@ -971,7 +956,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, } if (rxrate > 1 && !_check_lime_decimation(rxdiv)) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "can't deliver " + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_ERROR, "can't deliver " "decimation: %d of %.3f MHz CGEN and %.3f MHz samplerate; TXm = %.3f RXm = %.3f\n", rxdiv, cgen_rate / 1e6, rxrate / 1e6, txmaster_min / 1e6, rxmaster_min / 1e6); @@ -979,7 +964,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, } if (txrate > 1 && !_check_lime_decimation(txdiv)) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "can't deliver " + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_ERROR, "can't deliver " "interpolation: %d of %.3f MHz CGEN and %.3f MHz samplerate; TXm = %.3f RXm = %.3f\n", txdiv, cgen_rate / 1e6, txrate / 1e6, txmaster_min / 1e6, rxmaster_min / 1e6); @@ -991,8 +976,8 @@ int lms7002m_samplerate(lms7002_dev_t *d, d->txcgen_div = mpy_dac; d->rxtsp_div = rxdiv; d->txtsp_div = txdiv; - d->tx_host_inter = tx_host_inter; - d->rx_host_decim = rx_host_decim; + d->tx_dsp_inter = tx_dsp_inter; + d->rx_dsp_decim = rx_dsp_decim; for (unsigned j = 0; j < 4; j++) { unsigned clkdiv = (mpy_dac == 1) ? 0 : @@ -1011,7 +996,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, } } if (res != 0) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "can't tune VCO for data clock\n"); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_ERROR, "can't tune VCO for data clock\n"); return -ERANGE; } d->cgen_clk = cgen_rate; @@ -1034,7 +1019,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, if (res) return res; - USDR_LOG("XDEV", USDR_LOG_INFO, "Update RXTSP divider\n"); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Update RXTSP divider\n"); res = lms7002m_xxtsp_int_dec(&d->lmsstate, LMS_RXTSP, _ulog(d->rxtsp_div)); if (res) return res; @@ -1044,7 +1029,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, if (res) return res; - USDR_LOG("XDEV", USDR_LOG_INFO, "Update TXTSP divider\n"); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Update TXTSP divider\n"); res = lms7002m_xxtsp_int_dec(&d->lmsstate, LMS_TXTSP, _ulog(d->txtsp_div)); if (res) return res; @@ -1063,34 +1048,34 @@ int lms7002m_samplerate(lms7002_dev_t *d, return res; // Set ADS for bypass mode - res = lms7002m_cds_set(&d->lmsstate, rxtsp_div == 1, rxtsp_div == 1); - if (res) - return res; + // res = lms7002m_cds_set(&d->lmsstate, rxtsp_div == 1, rxtsp_div == 1); + // if (res) + // return res; d->lml_mode = cfg; - USDR_LOG("XDEV", USDR_LOG_INFO, "rxrate=%.3fMHz txrate=%.3fMHz" + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "rxrate=%.3fMHz txrate=%.3fMHz" " rxdecim=%d(h_%d) txinterp=%d(h_%d)" " RX_ADC=%.3fMHz TX_DAC=%.3fMHz hintr=%d hdecim=%d CGEN=%.3fMhz" " RX_TSP_div=%d TX_TSP_div=%d; SISO=%d/%d; refclk=%.3f\n", rxrate / 1e6, txrate / 1e6, rxdiv, rx_host_div, txdiv, tx_host_mul, cgen_rate / mpy_adc / 1e6, cgen_rate / mpy_dac / 1e6, - tx_host_inter, rx_host_decim, cgen_rate / 1e6, + tx_dsp_inter, rx_dsp_decim, cgen_rate / 1e6, rxtsp_div, txtsp_div, sisoddr_rx, sisoddr_tx, d->fref / 1e6); // Update BW if it's in auto mode for (unsigned i = 0; i < 2; i++) { if (rxrate > 1 && d->rx_run[i] && !d->rx_bw[i].set) { - USDR_LOG("XDEV", USDR_LOG_INFO, "Set RX[%d] bandwidth to %.3f Mhz\n", i, rxrate / 1e6); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Set RX[%d] bandwidth to %.3f Mhz\n", i, rxrate / 1e6); res = res ? res : lms7002m_mac_set(&d->lmsstate, i == 0 ? LMS7_CH_A : LMS7_CH_B); - res = res ? res : lms7002m_rbb_bandwidth(d, rxrate, false); + res = res ? res : lms7002m_rbb_bandwidth(d, rxrate / rx_dec, false); } if (txrate > 1 && d->tx_run[i] && !d->tx_bw[i].set) { - USDR_LOG("XDEV", USDR_LOG_INFO, "Set TX[%d] bandwidth to %.3f Mhz\n", i, txrate / 1e6); + USDR_LL_LOG(d->lmsstate.dev, "XDEV", USDR_LOG_INFO, "Set TX[%d] bandwidth to %.3f Mhz\n", i, txrate / 1e6); res = res ? res : lms7002m_mac_set(&d->lmsstate, i == 0 ? LMS7_CH_A : LMS7_CH_B); - res = res ? res : lms7002m_tbb_bandwidth(d, txrate, false); + res = res ? res : lms7002m_tbb_bandwidth(d, txrate / tx_int, false); res = res ? res : lms7002m_set_gain(d, i == 0 ? LMS7_CH_A : LMS7_CH_B, RFIC_LMS7_TX_PGA_GAIN, 13, NULL); @@ -1106,12 +1091,17 @@ int lms7002m_samplerate(lms7002_dev_t *d, int lms7002m_set_lmlrx_mode(lms7002_dev_t *d, unsigned mode) { + d->lml_mode.rx_lfsr = 0; + d->lml_mode.rx_tx_dig_loopback = 0; + switch (mode) { - case XSDR_LMLRX_LFSR: d->lml_mode.rx_lfsr = 1; break; - case XSDR_LMLRX_DIGLOOPBACK: d->lml_mode.rx_tx_dig_loopback = 1; break; + case XSDR_LMLRX_LFSR: + d->lml_mode.rx_lfsr = 1; + break; + case XSDR_LMLRX_DIGLOOPBACK: + d->lml_mode.rx_tx_dig_loopback = 1; + break; default: - d->lml_mode.rx_lfsr = 0; - d->lml_mode.rx_tx_dig_loopback = 0; break; } @@ -1142,7 +1132,7 @@ int lms7002m_set_corr_param(lms7002_dev_t* d, int channel, int corr_type, int va switch (type) { case CORR_PARAM_I: case CORR_PARAM_Q: - USDR_LOG("LMS7", USDR_LOG_INFO, "Set %s%s%s to %d\n", + USDR_LL_LOG(d->lmsstate.dev, "LMS7", USDR_LOG_INFO, "Set %s%s%s to %d\n", rx ? "RX" : "TX", (channel == 0) ? "A" : "B", type == CORR_PARAM_I ? "I" : "Q", @@ -1150,7 +1140,7 @@ int lms7002m_set_corr_param(lms7002_dev_t* d, int channel, int corr_type, int va return lms7002m_dc_corr(&d->lmsstate, param_dc, value); case CORR_PARAM_A: - USDR_LOG("LMS7", USDR_LOG_INFO, "Set %sA to %d\n", rx ? "RX" : "TX", value); + USDR_LL_LOG(d->lmsstate.dev, "LMS7", USDR_LOG_INFO, "Set %sA to %d\n", rx ? "RX" : "TX", value); return lms7002m_xxtsp_iq_phcorr(&d->lmsstate, rx ? LMS_RXTSP : LMS_TXTSP, value); case CORR_PARAM_GIQ: @@ -1161,16 +1151,15 @@ int lms7002m_set_corr_param(lms7002_dev_t* d, int channel, int corr_type, int va qg = 2047; ig = 2047 - value; } - USDR_LOG("LMS7", USDR_LOG_INFO, "Set %sGIQ to %d => ig = %d qg = %d\n", + USDR_LL_LOG(d->lmsstate.dev, "LMS7", USDR_LOG_INFO, "Set %sGIQ to %d => ig = %d qg = %d\n", rx ? "RX" : "TX", value, ig, qg); return lms7002m_xxtsp_iq_gcorr(&d->lmsstate, rx ? LMS_RXTSP : LMS_TXTSP, ig, qg); case CORR_OP_SET_FREQ: // TODO: optimize for TDD - res = lms7002m_sxx_tune(&d->lmsstate, rx ? SXX_TX : SXX_RX, d->fref, (unsigned)value, false); - res = (res) ? res - : lms7002m_mac_set(&d->lmsstate, channel == 0 ? LMS7_CH_A : LMS7_CH_B); + res = lms7002m_sxx_tune(&d->lmsstate, rx ? SXX_RX : SXX_TX, d->fref, (unsigned)value, false); + res = (res) ? res : lms7002m_mac_set(&d->lmsstate, channel == 0 ? LMS7_CH_A : LMS7_CH_B); return res; case CORR_OP_SET_BW: if (rx) { diff --git a/src/lib/device/m2_lm7_1/lms7002m_ctrl.h b/src/lib/device/m2_lm7_1/lms7002m_ctrl.h index babd0ed9..a710f0db 100644 --- a/src/lib/device/m2_lm7_1/lms7002m_ctrl.h +++ b/src/lib/device/m2_lm7_1/lms7002m_ctrl.h @@ -63,6 +63,14 @@ enum { struct lms7002_dev; typedef struct lms7002_dev lms7002_dev_t; +enum sigtype { + XSDR_TX_LO_CHANGED, + XSDR_RX_LO_CHANGED, + XSDR_TX_LNA_CHANGED, + XSDR_RX_LNA_CHANGED, +}; + +typedef int (*on_change_signal_t)(lms7002_dev_t *d, enum sigtype t); typedef int (*on_change_antenna_port_sw_t)(lms7002_dev_t* dev, int direction, unsigned sw); typedef const lms7002m_lml_map_t (*on_get_lml_portcfg_t)(bool rx, unsigned chs, unsigned flags, bool no_siso_map); @@ -73,6 +81,7 @@ struct lms7002_dev // Callbacks on_change_antenna_port_sw_t on_ant_port_sw; on_get_lml_portcfg_t on_get_lml_portcfg; + on_change_signal_t on_custom_signal; // RFIC state uint8_t rx_cfg_path; // Configuration index in cfg_auto_rx @@ -87,8 +96,8 @@ struct lms7002_dev uint8_t txcgen_div; uint8_t rxtsp_div; uint8_t txtsp_div; - uint8_t tx_host_inter; - uint8_t rx_host_decim; + uint8_t tx_dsp_inter; + uint8_t rx_dsp_decim; uint8_t rx_no_siso_map; uint8_t tx_no_siso_map; @@ -203,7 +212,8 @@ enum { int lms7002m_samplerate(lms7002_dev_t *d, unsigned rxrate, unsigned txrate, unsigned adcclk, unsigned dacclk, - unsigned flags, const bool rx_port_1); + unsigned flags, const bool rx_port_1, + unsigned rx_dec, unsigned tx_int); enum { diff --git a/src/lib/device/m2_lm7_1/m2_lm7_1.c b/src/lib/device/m2_lm7_1/m2_lm7_1.c index a72b81e3..88d066b7 100644 --- a/src/lib/device/m2_lm7_1/m2_lm7_1.c +++ b/src/lib/device/m2_lm7_1/m2_lm7_1.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "../device.h" #include "../device_ids.h" @@ -24,7 +25,9 @@ #include "xsdr_ctrl.h" -//#include "../device/ext_exm2pe/board_exm2pe.h" +#include "../device/ext_pciefe/ext_pciefe.h" +#include "../device/ext_exm2pe/board_exm2pe.h" +#include "../device/ext_fe_ch4_400_7200/ext_fe_ch4_400_7200.h" #define USBEN 1 @@ -46,17 +49,32 @@ enum { I2C_BUS_FRONTEND = MAKE_LSOP_I2C_ADDR(0, 1, 0), }; +static const usdr_dev_param_constant_t s_params_m2_lm7_1_rev_generic[] = { + { DNLL_I2C_COUNT, 1 }, + { DNLL_IRQ_COUNT, 8 }, +}; + +// sSDR external I2C bus (3/4) +static const usdr_dev_param_constant_t s_params_m2_lm7_1_rev_advanced[] = { + { DNLL_I2C_COUNT, 2 }, + { DNLL_IRQ_COUNT, 9 }, + + { "/ll/i2c/1/core", USDR_MAKE_COREID(USDR_CS_BUS, USDR_BS_DI2C_SIMPLE) }, + { "/ll/i2c/1/base", REG_SPI_I2C2 }, + { "/ll/i2c/1/irq", M2PCI_INT_I2C_1 }, +}; + // static const usdr_dev_param_constant_t s_params_m2_lm7_1_rev000[] = { { DNLL_SPI_COUNT, 1 }, - { DNLL_I2C_COUNT, 1 }, + // { DNLL_I2C_COUNT, 1 }, { DNLL_SRX_COUNT, 1 }, { DNLL_STX_COUNT, 1 }, { DNLL_RFE_COUNT, 1 }, { DNLL_TFE_COUNT, 0 }, { DNLL_IDX_REGSP_COUNT, 1 }, - { DNLL_IRQ_COUNT, 8 }, //TODO fix segfault when int count < configured + // { DNLL_IRQ_COUNT, 8 }, //TODO fix segfault when int count < configured { DNLL_DRP_COUNT, 2 }, { DNLL_BUCKET_COUNT, 1 }, { DNLL_GPO_COUNT, 1 }, @@ -107,7 +125,7 @@ const usdr_dev_param_constant_t s_params_m2_lm7_1_rev000[] = { { "/ll/qspi_flash/core", USDR_MAKE_COREID(USDR_CS_BUS, USDR_QSPI_FLASH_24_RW) }, { "/ll/qspi_flash/base", M2PCI_REG_QSPI_FLASH }, - { "/ll/qspi_flash/master_off", 0x1C0000 }, +// { "/ll/qspi_flash/master_off", 0x1C0000 }, { "/ll/gpi/0/core", USDR_MAKE_COREID(USDR_CS_GPI, USDR_GPI_32BIT_12) }, { "/ll/gpi/0/base", M2PCI_REG_RD_GPI0_12 }, @@ -124,6 +142,7 @@ const usdr_dev_param_constant_t s_params_m2_lm7_1_rev000[] = { { "/ll/dsp/atcrbs/0/base", M2PCI_REG_WR_LBDSP }, { "/ll/sdr/0/rfic/0", (uintptr_t)"lms7002m" }, + { "/ll/sdr/max_hw_rx_chans", 2 }, { "/ll/sdr/max_hw_tx_chans", 2 }, @@ -141,6 +160,14 @@ const usdr_dev_param_constant_t s_params_m2_lm7_1_rev000[] = { { "/ll/fe/0/i2c_busno/0", -1}, }; +static const usdr_dev_param_constant_t xsdr_params_m2_lm7_1_rev000[] = { + { "/ll/device/name", (uintptr_t)"xsdr"}, +}; + +static const usdr_dev_param_constant_t ssdr_params_m2_lm7_1_rev000[] = { + { "/ll/device/name", (uintptr_t)"ssdr"}, +}; + static int dev_m2_lm7_1_rate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_rate_m_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_debug_all_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); @@ -165,8 +192,9 @@ static int dev_m2_lm7_1_sdr_rx_gainvga_set(pdevice_t ud, pusdr_vfs_obj_t obj, ui static int dev_m2_lm7_1_sdr_rx_gainlna_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_sdr_rx_gainlb_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_sdr_rfic_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -//static int dev_m2_lm7_1_sdr_tx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_sdr_tx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_senstemp_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); static int dev_m2_lm7_1_debug_lms7002m_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -177,6 +205,8 @@ static int dev_m2_lm7_1_debug_lms8001_reg_get(pdevice_t ud, pusdr_vfs_obj_t obj, static int dev_m2_lm7_1_sdr_rx_dccorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_sdr_rx_dccorr_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); + static int dev_m2_lm7_1_sdr_tx_dccorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_sdr_rx_dccorrmode_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -189,8 +219,6 @@ static int dev_m2_lm7_1_sdr_refclk_frequency_get(pdevice_t ud, pusdr_vfs_obj_t o static int dev_m2_lm7_1_sdr_refclk_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -static int dev_m2_lm7_1_usb_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue); - static int dev_m2_lm7_1_sdr_rxdsp_swapab_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_tx_antennat_port_cfg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -199,13 +227,9 @@ static int dev_m2_lm7_1_rfe_throttle_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint static int dev_m2_lm7_1_sensor_freqpps_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* value); static int dev_m2_lm7_1_rfe_nco_pwrdc_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* value); -static int dev_m2_lm7_1_rfe_nco_enable_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -static int dev_m2_lm7_1_rfe_nco_enable_frequency(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); - static int dev_m2_lm7_1_tfe_gen_en_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_tfe_gen_const_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_tfe_gen_tone_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -static int dev_m2_lm7_1_tfe_nco_enable_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_tfe_nco_enable_frequency(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_calibrate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -220,38 +244,79 @@ static int dev_m2_lm7_1_dev_atcrbs_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64 static int dev_m2_lm7_1_dev_dac_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_phyrxlm_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_phy_rx_dly_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_phy_rx_lfsr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_phy_rx_lfsr_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); + +static int dev_m2_lm7_1_phy_tx_lfsr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_phy_tx_iqsel_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); + static int dev_m2_lm7_1_lms7002rxlml_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_debug_clkinfo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_m2_lm7_1_revision_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); +static int dev_m2_lm7_1_qspi_flash_master_off_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); + +static int dev_m2_lm7_1_sdr_tx_phase_ovr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_sdr_rx_phase_ovr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_sdr_tx_phase_ovr_iq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_sdr_tx_phase_ovr_rc_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); + +static int dev_m2_lm7_1_lnb_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_lnb_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); +static int dev_m2_lm7_1_lms8_intmode_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_lms8_intmode_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); +static int dev_m2_lm7_1_lms8_switchover_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_m2_lm7_1_lms8_switchover_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue); + +static int dev_m2_lm7_1_sdr_vio_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static const usdr_dev_param_func_t s_fparams_m2_lm7_1_rev000[] = { - { "/dm/rate/master", { dev_m2_lm7_1_rate_set, NULL }}, - { "/dm/rate/rxtxadcdac", { dev_m2_lm7_1_rate_m_set, NULL }}, + { "/dm/rate/master", { dev_m2_lm7_1_rate_set, NULL }}, + { "/dm/rate/rxtxadcdac", { dev_m2_lm7_1_rate_m_set, NULL }}, - { "/dm/debug/all", { NULL, dev_m2_lm7_1_debug_all_get }}, - { "/dm/power/en", { dev_m2_lm7_1_pwren_set, NULL }}, + { "/dm/debug/all", { NULL, dev_m2_lm7_1_debug_all_get }}, + { "/dm/power/en", { dev_m2_lm7_1_pwren_set, NULL }}, - { "/dm/sdr/channels", { NULL, NULL }}, - { "/dm/sensor/temp", { NULL, dev_m2_lm7_1_senstemp_get }}, + { "/dm/sdr/channels", { NULL, NULL }}, + { "/dm/sensor/temp", { NULL, dev_m2_lm7_1_senstemp_get }}, { "/dm/sdr/0/usbclk", { dev_m2_lm7_1_usbclk_set, NULL }}, { "/dm/sdr/0/calibrate", { dev_m2_lm7_1_calibrate_set, dev_m2_lm7_1_calibrate_get }}, - { "/dm/sdr/refclk/frequency", {dev_m2_lm7_1_sdr_refclk_frequency_set, dev_m2_lm7_1_sdr_refclk_frequency_get}}, - { "/dm/sdr/refclk/path", {dev_m2_lm7_1_sdr_refclk_path_set, NULL}}, + { "/dm/sdr/refclk/frequency", { dev_m2_lm7_1_sdr_refclk_frequency_set, dev_m2_lm7_1_sdr_refclk_frequency_get }}, + { "/dm/sdr/refclk/path", { dev_m2_lm7_1_sdr_refclk_path_set, NULL }}, + + { "/dm/sdr/0/vio", { dev_m2_lm7_1_sdr_vio_set, NULL }}, + { "/dm/sdr/0/lnb", { dev_m2_lm7_1_lnb_set, dev_m2_lm7_1_lnb_get }}, + { "/dm/sdr/0/lms8_intmode", { dev_m2_lm7_1_lms8_intmode_set, dev_m2_lm7_1_lms8_intmode_get }}, + { "/dm/sdr/0/lms8_switchover", { dev_m2_lm7_1_lms8_switchover_set, dev_m2_lm7_1_lms8_switchover_get }}, - { "/dm/sdr/0/rx/dccorr", { dev_m2_lm7_1_sdr_rx_dccorr_set, NULL }}, + { "/dm/sdr/0/tx/phase_ovr", { dev_m2_lm7_1_sdr_tx_phase_ovr_set, NULL }}, + { "/dm/sdr/0/tx/phase_ovr_iq", { dev_m2_lm7_1_sdr_tx_phase_ovr_iq_set, NULL }}, + { "/dm/sdr/0/tx/phase_ovr_rc", { dev_m2_lm7_1_sdr_tx_phase_ovr_rc_set, NULL }}, + { "/dm/sdr/0/rx/phase_ovr", { dev_m2_lm7_1_sdr_rx_phase_ovr_set, NULL }}, + + { "/dm/sdr/0/rx/dccorr", { dev_m2_lm7_1_sdr_rx_dccorr_set, dev_m2_lm7_1_sdr_rx_dccorr_get }}, { "/dm/sdr/0/tx/dccorr", { dev_m2_lm7_1_sdr_tx_dccorr_set, NULL }}, { "/dm/sdr/0/rx/phgaincorr",{ dev_m2_lm7_1_sdr_rx_phgaincorr_set, NULL }}, { "/dm/sdr/0/tx/phgaincorr",{ dev_m2_lm7_1_sdr_tx_phgaincorr_set, NULL }}, + { "/dm/sdr/0/rx/frequency/bb", { dev_m2_lm7_1_sdr_rx_bbfreq_set, NULL }}, + { "/dm/sdr/0/tx/frequency/bb", { dev_m2_lm7_1_sdr_tx_bbfreq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/rx/freqency/bb", { dev_m2_lm7_1_sdr_rx_bbfreq_set, NULL }}, { "/dm/sdr/0/tx/freqency/bb", { dev_m2_lm7_1_sdr_tx_bbfreq_set, NULL }}, + { "/dm/sdr/0/rx/frequency", { dev_m2_lm7_1_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency", { dev_m2_lm7_1_sdr_tx_freq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/rx/freqency", { dev_m2_lm7_1_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/tx/freqency", { dev_m2_lm7_1_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/rx/gain", { dev_m2_lm7_1_sdr_rx_gain_set, NULL }}, { "/dm/sdr/0/tx/gain", { dev_m2_lm7_1_sdr_tx_gain_set, NULL }}, { "/dm/sdr/0/tx/gain/lb", { dev_m2_lm7_1_sdr_tx_gainlb_set, NULL }}, @@ -262,8 +327,11 @@ const usdr_dev_param_func_t s_fparams_m2_lm7_1_rev000[] = { { "/dm/sdr/0/rx/gain/lna", { dev_m2_lm7_1_sdr_rx_gainlna_set, NULL }}, { "/dm/sdr/0/rx/gain/lb", { dev_m2_lm7_1_sdr_rx_gainlb_set, NULL }}, + { "/dm/sdr/0/rx/rfic_path", { dev_m2_lm7_1_sdr_rfic_path_set, NULL }}, + { "/dm/sdr/0/tx/rfic_path", { dev_m2_lm7_1_sdr_rfic_path_set, NULL }}, + { "/dm/sdr/0/rx/path", { dev_m2_lm7_1_sdr_rx_path_set, NULL }}, - { "/dm/sdr/0/tx/path", { dev_m2_lm7_1_sdr_rx_path_set, NULL }}, + { "/dm/sdr/0/tx/path", { dev_m2_lm7_1_sdr_tx_path_set, NULL }}, { "/dm/sdr/0/rx/dccorrmode", { dev_m2_lm7_1_sdr_rx_dccorrmode_set, NULL }}, @@ -272,42 +340,101 @@ const usdr_dev_param_func_t s_fparams_m2_lm7_1_rev000[] = { { "/dm/sdr/0/rxdsp/swapab", { dev_m2_lm7_1_sdr_rxdsp_swapab_set, NULL }}, + { "/dm/sdr/0/tdd/frequency", { dev_m2_lm7_1_sdr_tdd_freq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/tdd/freqency", { dev_m2_lm7_1_sdr_tdd_freq_set, NULL }}, + { "/dm/sdr/0/tfe/antcfg", { dev_m2_lm7_1_tx_antennat_port_cfg_set, NULL }}, { "/dm/sdr/0/tfe/generator/enable", { dev_m2_lm7_1_tfe_gen_en_set, NULL }}, { "/dm/sdr/0/tfe/generator/const", { dev_m2_lm7_1_tfe_gen_const_set, NULL }}, { "/dm/sdr/0/tfe/generator/tone", { dev_m2_lm7_1_tfe_gen_tone_set, NULL }}, - { "/dm/sdr/0/tfe/nco/enable", { dev_m2_lm7_1_tfe_nco_enable_set, NULL }}, - { "/dm/sdr/0/tfe/nco/freqency", { dev_m2_lm7_1_tfe_nco_enable_frequency, NULL }}, - { "/dm/sdr/0/rfe/throttle", { dev_m2_lm7_1_rfe_throttle_set, NULL }}, + { "/dm/sdr/0/tfe/nco/frequency", { dev_m2_lm7_1_tfe_nco_enable_frequency, NULL }}, - { "/dm/sdr/0/rfe/nco/enable", { dev_m2_lm7_1_rfe_nco_enable_set, NULL }}, - { "/dm/sdr/0/rfe/nco/freqency",{ dev_m2_lm7_1_rfe_nco_enable_frequency, NULL }}, - { "/dm/sdr/0/rfe/pwrdc", { NULL, dev_m2_lm7_1_rfe_nco_pwrdc_get }}, - - // Debug interface - { "/debug/hw/lms7002m/0/reg", { dev_m2_lm7_1_debug_lms7002m_reg_set, dev_m2_lm7_1_debug_lms7002m_reg_get }}, - { "/debug/hw/lms8001/0/reg" , { dev_m2_lm7_1_debug_lms8001_reg_set, dev_m2_lm7_1_debug_lms8001_reg_get }}, + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ + { "/dm/sdr/0/tfe/nco/freqency", { dev_m2_lm7_1_tfe_nco_enable_frequency, NULL }}, + { "/dm/sdr/0/rfe/throttle", { dev_m2_lm7_1_rfe_throttle_set, NULL }}, + { "/dm/sdr/0/rfe/pwrdc", { NULL, dev_m2_lm7_1_rfe_nco_pwrdc_get }}, - // USB debug interface - { "/dm/usb", { NULL, dev_m2_lm7_1_usb_get }}, + // Debug interface + { "/debug/hw/lms7002m/0/reg", { dev_m2_lm7_1_debug_lms7002m_reg_set, dev_m2_lm7_1_debug_lms7002m_reg_get }}, + { "/debug/hw/lms8001/0/reg" , { dev_m2_lm7_1_debug_lms8001_reg_set, dev_m2_lm7_1_debug_lms8001_reg_get }}, // Get sampled amount of ticks between GPS - { "/dm/sensor/freqpps", { NULL, dev_m2_lm7_1_sensor_freqpps_get }}, + { "/dm/sensor/freqpps", { NULL, dev_m2_lm7_1_sensor_freqpps_get }}, - { "/dm/sdr/0/core/atcrbs/reg", { dev_m2_lm7_1_dev_atcrbs_set, dev_m2_lm7_1_dev_atcrbs_get }}, + { "/dm/sdr/0/core/atcrbs/reg", { dev_m2_lm7_1_dev_atcrbs_set, dev_m2_lm7_1_dev_atcrbs_get }}, - { "/dm/sdr/0/dac_vctcxo", { dev_m2_lm7_1_dev_dac_vctcxo_set, NULL }}, + { "/dm/sdr/0/dac_vctcxo", { dev_m2_lm7_1_dev_dac_vctcxo_set, NULL }}, + { "/dm/sdr/0/phy_rx_dly", { dev_m2_lm7_1_phy_rx_dly_set, NULL }}, + { "/dm/sdr/0/phy_rx_lfsr", { dev_m2_lm7_1_phy_rx_lfsr_set, dev_m2_lm7_1_phy_rx_lfsr_get }}, + { "/dm/sdr/0/phy_tx_lfsr", { dev_m2_lm7_1_phy_tx_lfsr_set, NULL }}, + { "/dm/sdr/0/phy_tx_iqsel", { dev_m2_lm7_1_phy_tx_iqsel_set, NULL }}, { "/dm/sdr/0/phyrxlml", { dev_m2_lm7_1_phyrxlm_set, NULL }}, { "/debug/hw/lms7002m/0/rxlml", { dev_m2_lm7_1_lms7002rxlml_set, NULL }}, { "/debug/clk_info", { dev_m2_lm7_1_debug_clkinfo_set, NULL }}, { "/dm/revision", { NULL, dev_m2_lm7_1_revision_get }}, + + { "/ll/qspi_flash/master_off", { NULL, dev_m2_lm7_1_qspi_flash_master_off_get }}, +}; + +static const usdr_dev_link_t s_links[] = { + { "/dm/sdr/0/rx/frequency/0", "/dm/sdr/0/rx/frequency" }, + { "/dm/sdr/0/rx/frequency/1", "/dm/sdr/0/rx/frequency" }, + { "/dm/sdr/0/tx/frequency/0", "/dm/sdr/0/tx/frequency" }, + { "/dm/sdr/0/tx/frequency/1", "/dm/sdr/0/tx/frequency" }, + { "/dm/sdr/0/rx/frequency/bb/0", "/dm/sdr/0/rx/frequency/bb" }, + { "/dm/sdr/0/rx/frequency/bb/1", "/dm/sdr/0/rx/frequency/bb" }, + { "/dm/sdr/0/tx/frequency/bb/0", "/dm/sdr/0/tx/frequency/bb" }, + { "/dm/sdr/0/tx/frequency/bb/1", "/dm/sdr/0/tx/frequency/bb" }, + + { "/dm/sdr/0/rx/gain/0", "/dm/sdr/0/rx/gain" }, + { "/dm/sdr/0/tx/gain/0", "/dm/sdr/0/tx/gain" }, + { "/dm/sdr/0/tx/gain/lb/0", "/dm/sdr/0/tx/gain/lb" }, + { "/dm/sdr/0/tx/gain/vga1/0", "/dm/sdr/0/tx/gain/vga1" }, + { "/dm/sdr/0/rx/gain/pga/0", "/dm/sdr/0/rx/gain/pga" }, + { "/dm/sdr/0/rx/gain/vga/0", "/dm/sdr/0/rx/gain/vga" }, + { "/dm/sdr/0/rx/gain/lna/0", "/dm/sdr/0/rx/gain/lna" }, + { "/dm/sdr/0/rx/gain/lb/0", "/dm/sdr/0/rx/gain/lb" }, + { "/dm/sdr/0/rx/gain/1", "/dm/sdr/0/rx/gain" }, + { "/dm/sdr/0/tx/gain/1", "/dm/sdr/0/tx/gain" }, + { "/dm/sdr/0/tx/gain/lb/1", "/dm/sdr/0/tx/gain/lb" }, + { "/dm/sdr/0/tx/gain/vga1/1", "/dm/sdr/0/tx/gain/vga1" }, + { "/dm/sdr/0/rx/gain/pga/1", "/dm/sdr/0/rx/gain/pga" }, + { "/dm/sdr/0/rx/gain/vga/1", "/dm/sdr/0/rx/gain/vga" }, + { "/dm/sdr/0/rx/gain/lna/1", "/dm/sdr/0/rx/gain/lna" }, + { "/dm/sdr/0/rx/gain/lb/1", "/dm/sdr/0/rx/gain/lb" }, + + { "/dm/sdr/0/rx/bandwidth/0", "/dm/sdr/0/rx/bandwidth" }, + { "/dm/sdr/0/tx/bandwidth/0", "/dm/sdr/0/tx/bandwidth" }, + { "/dm/sdr/0/rx/bandwidth/1", "/dm/sdr/0/rx/bandwidth" }, + { "/dm/sdr/0/tx/bandwidth/1", "/dm/sdr/0/tx/bandwidth" }, + + { "/dm/sdr/0/rx/rfic_path/0", "/dm/sdr/0/rx/rfic_path" }, + { "/dm/sdr/0/rx/rfic_path/1", "/dm/sdr/0/rx/rfic_path" }, + { "/dm/sdr/0/tx/rfic_path/0", "/dm/sdr/0/tx/rfic_path" }, + { "/dm/sdr/0/tx/rfic_path/1", "/dm/sdr/0/tx/rfic_path" }, + + { "/dm/sdr/0/rx/path/0", "/dm/sdr/0/rx/path" }, + { "/dm/sdr/0/rx/path/1", "/dm/sdr/0/rx/path" }, + { "/dm/sdr/0/tx/path/0", "/dm/sdr/0/tx/path" }, + { "/dm/sdr/0/tx/path/1", "/dm/sdr/0/tx/path" }, + + { "/dm/sdr/0/rx/dccorr/0", "/dm/sdr/0/rx/dccorr" }, + { "/dm/sdr/0/tx/dccorr/0", "/dm/sdr/0/tx/dccorr" }, + { "/dm/sdr/0/rx/phgaincorr/0","/dm/sdr/0/rx/phgaincorr" }, + { "/dm/sdr/0/tx/phgaincorr/0","/dm/sdr/0/tx/phgaincorr" }, + { "/dm/sdr/0/rx/dccorr/1", "/dm/sdr/0/rx/dccorr" }, + { "/dm/sdr/0/tx/dccorr/1", "/dm/sdr/0/tx/dccorr" }, + { "/dm/sdr/0/rx/phgaincorr/1","/dm/sdr/0/rx/phgaincorr" }, + { "/dm/sdr/0/tx/phgaincorr/1","/dm/sdr/0/tx/phgaincorr" }, + }; struct dev_m2_lm7_1_gps { @@ -324,6 +451,7 @@ struct dev_m2_lm7_1_gps { struct dev_fe* fe; bool bifurcation_en; bool nodecint; + bool double_pump; int cal_data[8]; @@ -331,6 +459,99 @@ struct dev_m2_lm7_1_gps { stream_handle_t* tx; }; +static int lms7002m_channel_info_string_parse(char* chanlist, unsigned max_chans, lms7002m_mac_mode_t* cinfo) +{ + lms7002m_mac_mode_t ch = LMS7_CH_NONE; + const char* delim = ":_-/"; + char* saveptr; + char* str1; + unsigned t; + + for (t = 0, str1 = chanlist; ; str1 = NULL, t++) { + const char* token = strtok_r(str1, delim, &saveptr); + if (token == NULL) { + break; + } + + unsigned chn; + if (isdigit(*token)) { + chn = atoi(token); + } else if (isalpha(*token)) { + chn = tolower(*token) - 'a'; + } else { + USDR_LOG("STRM", USDR_LOG_ERROR, "Channel parsing: incorrect token# %d `%s`\n", t, token); + return -EINVAL; + } + + if (chn > max_chans) { + USDR_LOG("STRM", USDR_LOG_ERROR, "Channel parsing: incorrect channel num: %d\n", chn); + return -EINVAL; + } + + ch |= 1 << chn; + } + + *cinfo = ch; + return 0; +} + +static int device_path_to_chmsk(const char* full_path, const char* basename, lms7002m_mac_mode_t* lms_ch) +{ + char chanlist[64*4]; + const char* lst; + + if (basename) { + size_t len; + len = strlen(basename); + if (strncmp(full_path, basename, len)) { + return -ENOENT; + } + + lst = full_path + len; + if (*lst != '/') { + return -ENAVAIL; + } + + lst++; + } else { + const char *pos = full_path; + lst = NULL; + + while ((pos = strchr(pos, '/')) != NULL) { + lst = pos; + pos++; + } + if (lst == NULL) { + return -ENAVAIL; + } + } + + SAFE_STRCPY(chanlist, lst); + return lms7002m_channel_info_string_parse(chanlist, 2, lms_ch); +} + +static int lms7002_iterate_ordinal_chans(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val, const char* basename, bool rxchans) +{ + //struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + vfs_object_t ph; + lms7002m_mac_mode_t selected; + int res = device_path_to_chmsk(obj->full_path, basename, &selected); + if (res == -ENAVAIL) { + selected = LMS7_CH_AB; + res = 0; + } else if (res != 0) { + return res; + } + + ph.type = obj->type; + ph.object = obj->object; + ph.data = obj->data; + ph.ops = obj->ops; + ph.full_path[0] = 0; + ph.full_path[1] = selected; + return obj->ops.si64(&ph, val); +} + int dev_m2_lm7_1_debug_clkinfo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { return xsdr_clk_debug_info(&((struct dev_m2_lm7_1_gps *)ud)->xdev); @@ -338,12 +559,69 @@ int dev_m2_lm7_1_debug_clkinfo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t v int dev_m2_lm7_1_dev_dac_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { - return xsdr_trim_dac_vctcxo(&((struct dev_m2_lm7_1_gps *)ud)->xdev, value); + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + board_ext_pciefe_t* board_fe = (board_ext_pciefe_t*)device_fe_to(d->fe, "pciefe"); + board_exm2pe_t* board = (board_exm2pe_t*)device_fe_to(d->fe, "exm2pe"); + ext_fe_ch4_400_7200_t* fe = (ext_fe_ch4_400_7200_t*)device_fe_to(d->fe, "fe4ch4007200"); + if (board_fe) { + return board_ext_pciefe_set_dac(board_fe, value); + } else if (board) { + return board_exm2pe_set_dac(board, value); + } else if (fe) { + return ext_fe_set_dac(fe, value); + } + + return xsdr_trim_dac_vctcxo(&d->xdev, value); } int dev_m2_lm7_1_phyrxlm_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { - return xsdr_phy_tune(&((struct dev_m2_lm7_1_gps *)ud)->xdev, value); + return xsdr_phy_tune_rx(&((struct dev_m2_lm7_1_gps *)ud)->xdev, value); +} + +int dev_m2_lm7_1_phy_rx_dly_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + unsigned type = (value >> 8) & 0x1f; + unsigned val = (value & 0x1f); + + return xsdr_config_rcvdly(&((struct dev_m2_lm7_1_gps *)ud)->xdev, type, val); +} + +int dev_m2_lm7_1_phy_rx_lfsr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + return xsdr_phy_en_lfsr_checker_mimo(&((struct dev_m2_lm7_1_gps *)ud)->xdev, value ? true : false); +} + +int dev_m2_lm7_1_phy_tx_lfsr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + return xsdr_phy_en_lfsr_generator_mimo(&((struct dev_m2_lm7_1_gps *)ud)->xdev, + (value & 1) ? true : false, + (value & 2) ? true : false); +} + +int dev_m2_lm7_1_phy_tx_iqsel_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + return xsdr_phy_tx_iqsel(&((struct dev_m2_lm7_1_gps *)ud)->xdev, value); +} + + +int dev_m2_lm7_1_phy_rx_lfsr_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + uint32_t v[4]; + uint64_t val = 0; + int res = xsdr_phy_lfsr_mimo_state(&((struct dev_m2_lm7_1_gps *)ud)->xdev, LFSR_CNTR_BER, v); + if (res) + return res; + + for (unsigned i = 0; i < 4; i++) { + uint64_t k = v[i]; + if (k > UINT16_MAX) { + k = UINT16_MAX; + } + val |= k << (16 * i); + } + *ovalue = val; + return 0; } int dev_m2_lm7_1_lms7002rxlml_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) @@ -371,6 +649,86 @@ int dev_m2_lm7_1_dev_atcrbs_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* val return res; } +int dev_m2_lm7_1_sdr_tx_phase_ovr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + d->xdev.tx_override_phase = value; + return 0; +} + +int dev_m2_lm7_1_sdr_tx_phase_ovr_rc_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + return xsdr_txphase_ovr(&d->xdev, value); +} + + +int dev_m2_lm7_1_sdr_tx_phase_ovr_iq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + d->xdev.tx_override_phase_iq = value; + return 0; +} + +int dev_m2_lm7_1_sdr_rx_phase_ovr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + d->xdev.rx_override_phase = value; + return 0; +} + +int dev_m2_lm7_1_sdr_vio_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + return xsdr_set_vio(&d->xdev, value); +} + +int dev_m2_lm7_1_lnb_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + if ((value < 300e6) || (value > 3800e6)) { + return -ERANGE; + } + + d->xdev.lms7_lob = value; + return 0; +} + +int dev_m2_lm7_1_lnb_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue) +{ + *ovalue = ((struct dev_m2_lm7_1_gps *)ud)->xdev.lms7_lob; + return 0; +} + +int dev_m2_lm7_1_lms8_intmode_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + d->xdev.lms8_int_mode = value ? true : false; + return 0; +} + +int dev_m2_lm7_1_lms8_intmode_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue) +{ + *ovalue = ((struct dev_m2_lm7_1_gps *)ud)->xdev.lms8_int_mode; + return 0; +} + +int dev_m2_lm7_1_lms8_switchover_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + if ((value < 1500e6) || (value > 3800e6)) { + return -ERANGE; + } + + d->xdev.lms8_switchover_freq = value; + return 0; +} + +int dev_m2_lm7_1_lms8_switchover_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue) +{ + *ovalue = ((struct dev_m2_lm7_1_gps *)ud)->xdev.lms8_switchover_freq; + return 0; +} int dev_m2_lm7_1_debug_lms7002m_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { @@ -379,12 +737,13 @@ int dev_m2_lm7_1_debug_lms7002m_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint6 unsigned chan = (unsigned)(value >> 32); res = lms7002m_mac_set(&d->xdev.base.lmsstate, chan); + if (res) + return res; d->debug_lms7002m_last = ~0u; res = lowlevel_spi_tr32(d->base.dev, 0, SPI_LMS7, value & 0xffffffff, &d->debug_lms7002m_last); - USDR_LOG("XDEV", USDR_LOG_WARNING, "%s: Debug LMS7/%d REG %08x => %08x\n", - lowlevel_get_devname(d->base.dev), chan, (unsigned)value, - d->debug_lms7002m_last); + USDR_LL_LOG(d->base.dev, "XDEV", USDR_LOG_WARNING, "Debug LMS7/%d REG %08x => %08x\n", + chan, (unsigned)value, d->debug_lms7002m_last); return res; } @@ -392,13 +751,18 @@ int dev_m2_lm7_1_debug_lms8001_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64 { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; int res = xsdr_trspi_lms8(&d->xdev, value & 0xffffffff, &d->debug_lms8001_last); - USDR_LOG("XDEV", USDR_LOG_WARNING, "%s: Debug LMS8 REG %08x => %08x\n", - lowlevel_get_devname(d->base.dev), (unsigned)value, - d->debug_lms8001_last); + USDR_LL_LOG(d->base.dev, "XDEV", USDR_LOG_WARNING, "Debug LMS8 REG %08x => %08x\n", + (unsigned)value, d->debug_lms8001_last); return res; } +int dev_m2_lm7_1_sdr_rx_dccorr_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + return xsdr_rxdccorr(&d->xdev, ovalue); +} + int dev_m2_lm7_1_sdr_rx_dccorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; @@ -446,7 +810,7 @@ int dev_m2_lm7_1_sdr_rx_phgaincorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64 unsigned qg = ((value >> 16) & 0xffff); unsigned pcorr = ((value >> 48) & 0xffff); - USDR_LOG("UDEV", USDR_LOG_NOTE, "RXGAC CH=%d I=%d Q=%d A=%d\n", chan, ig, qg, pcorr); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_NOTE, "RXGAC CH=%d I=%d Q=%d A=%d\n", chan, ig, qg, pcorr); res = (res) ? res : lms7002m_xxtsp_iq_gcorr(&d->xdev.base.lmsstate, LMS_RXTSP, ig, qg); res = (res) ? res : lms7002m_xxtsp_iq_phcorr(&d->xdev.base.lmsstate, LMS_RXTSP, pcorr); @@ -464,7 +828,7 @@ int dev_m2_lm7_1_sdr_tx_phgaincorr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64 unsigned qg = ((value >> 16) & 0xffff); unsigned pcorr = ((value >> 48) & 0xffff); - USDR_LOG("UDEV", USDR_LOG_NOTE, "TXGAC CH=%d I=%d Q=%d A=%d\n", chan, ig, qg, pcorr); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_NOTE, "TXGAC CH=%d I=%d Q=%d A=%d\n", chan, ig, qg, pcorr); res = (res) ? res : lms7002m_xxtsp_iq_gcorr(&d->xdev.base.lmsstate, LMS_TXTSP, ig, qg); res = (res) ? res : lms7002m_xxtsp_iq_phcorr(&d->xdev.base.lmsstate, LMS_TXTSP, pcorr); @@ -528,11 +892,6 @@ int dev_m2_lm7_1_tfe_gen_tone_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t va return res; } -int dev_m2_lm7_1_tfe_nco_enable_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) -{ - return 0; -} - int dev_m2_lm7_1_tfe_nco_enable_frequency(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; @@ -567,19 +926,6 @@ int dev_m2_lm7_1_rfe_throttle_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t va return -EINVAL; } - -int dev_m2_lm7_1_rfe_nco_enable_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) -{ - //struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; - //return sfe_rf4_nco_enable(d->base.dev, 0, CSR_RFE4_BASE, (value & 0xff) ? true : false, value >> 32); - return -EINVAL; -} -int dev_m2_lm7_1_rfe_nco_enable_frequency(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) -{ - //struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; - //return sfe_rf4_nco_freq(d->base.dev, 0, CSR_RFE4_BASE, (int)value); - return -EINVAL; -} int dev_m2_lm7_1_rfe_nco_pwrdc_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* value) { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; @@ -588,7 +934,7 @@ int dev_m2_lm7_1_rfe_nco_pwrdc_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* res = lowlevel_reg_rdndw(d->base.dev, 0, /* REG_RD_UNUSED_0 */ 7, (uint32_t*)&val[0], 2); - USDR_LOG("UDEV", USDR_LOG_NOTE, "DCV I=%d Q=%d\n", val[0], val[1]); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_NOTE, "DCV I=%d Q=%d\n", val[0], val[1]); double i = val[0]; double q = val[1]; @@ -621,7 +967,7 @@ int dev_m2_lm7_1_calibrate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value unsigned flags = value & 0xfffff; unsigned chan = value >> 32; - if (flags > 65536 || chan > 1) { + if (flags > 2*65536 || chan > 1) { const char* v = (const char* )value; chan = 0; // TODO B flags = 0; @@ -690,8 +1036,9 @@ int dev_m2_lm7_1_rate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; - //Simple SISO RX only + //Simple SISO RX only return xsdr_set_samplerate_ex(&d->xdev, (unsigned)value, (unsigned)value, 0, 0, + (d->bifurcation_en) ? (XSDR_LML_SISO_DDR_RX | XSDR_LML_SISO_DDR_TX) : 0 | (d->nodecint ? 0 : XSDR_SR_MAXCONVRATE) | XSDR_SR_EXTENDED_CGEN); } @@ -716,6 +1063,7 @@ int dev_m2_lm7_1_rate_m_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) return -ERANGE; return xsdr_set_samplerate_ex(&d->xdev, rx_rate, tx_rate, adc_rate, dac_rate, + (d->bifurcation_en) ? (XSDR_LML_SISO_DDR_RX | XSDR_LML_SISO_DDR_TX) : 0 | (d->nodecint ? 0 : XSDR_SR_MAXCONVRATE) | XSDR_SR_EXTENDED_CGEN); } @@ -760,11 +1108,14 @@ int dev_m2_lm7_1_sdr_tx_freq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val int dev_m2_lm7_1_sdr_rx_bbfreq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { + if (obj->full_path[0]) { + return lms7002_iterate_ordinal_chans(ud, obj, value, "/dm/sdr/0/rx/frequency/bb", true); + } + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; - unsigned channel = value >> 32; int32_t freq = (int32_t)(value & 0xffffffff); + return xsdr_rfic_bb_set_freq(&d->xdev, obj->full_path[1], false, freq); - return xsdr_rfic_bb_set_freq(&d->xdev, channel, false, freq); } int dev_m2_lm7_1_sdr_tx_bbfreq_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { @@ -869,18 +1220,24 @@ static int find_param_list(const char* param, const param_list_idx_t* lst, unsig return -1; } +int _sdr_get_path(const char* param) +{ + int idx = find_param_list(param, s_path_list, SIZEOF_ARRAY(s_path_list)); + if (idx < 0) { + USDR_LOG("UDEV", USDR_LOG_WARNING, "Unknown '%s' path!\n", + param); + return -EINVAL; + } + return idx; +} -int dev_m2_lm7_1_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +int dev_m2_lm7_1_sdr_rfic_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; if (value > 4096) { - const char* param = (const char*)value; - int idx = find_param_list(param, s_path_list, SIZEOF_ARRAY(s_path_list)); - if (idx < 0) { - USDR_LOG("UDEV", USDR_LOG_WARNING, "m2_lm7_1_GPS: unknown '%s' path!\n", - param); - return -EINVAL; - } + int idx = _sdr_get_path((const char*)value); + if (idx < 0) + return idx; value = s_path_list[idx].param; } @@ -888,11 +1245,38 @@ int dev_m2_lm7_1_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val return xsdr_rfic_fe_set_lna(&d->xdev, LMS7_CH_AB, value); } +int dev_m2_lm7_1_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + if (value > 4096) { + int idx = _sdr_get_path((const char*)value); + if (idx < 0) + return idx; + + value = s_path_list[idx].param; + } + + return xsdr_rfic_rfe_set_path(&d->xdev, value); +} + +int dev_m2_lm7_1_sdr_tx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + if (value > 4096) { + int idx = _sdr_get_path((const char*)value); + if (idx < 0) + return idx; + + value = s_path_list[idx].param; + } + + return xsdr_rfic_tfe_set_path(&d->xdev, value); +} int dev_m2_lm7_1_sdr_refclk_frequency_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; - USDR_LOG("UDEV", USDR_LOG_WARNING, "m2_lm7_1_GPS: Set fref=%d\n", (unsigned)value); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_WARNING, "Set fref=%d\n", (unsigned)value); d->xdev.base.fref = value; return 0; } @@ -919,7 +1303,7 @@ int dev_m2_lm7_1_sdr_refclk_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t } xsdr_set_extref(&d->xdev, value ? true : false, d->xdev.base.fref); - USDR_LOG("UDEV", USDR_LOG_INFO, "m2_lm7_1_GPS: set clk ref path to %d\n", (int)value); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_INFO, "Set clk ref path to %d\n", (int)value); return 0; } @@ -937,7 +1321,7 @@ void usdr_device_m2_lm7_1_destroy(pdevice_t udev) } xsdr_dtor(&d->xdev); - USDR_LOG("UDEV", USDR_LOG_INFO, "m2_lm7_1_GPS: turnoff\n"); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_INFO, "Turnoff\n"); usdr_device_base_destroy(udev); } @@ -945,7 +1329,7 @@ void usdr_device_m2_lm7_1_destroy(pdevice_t udev) int dev_m2_lm7_1_tx_antennat_port_cfg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; - USDR_LOG("UDEV", USDR_LOG_INFO, "m2_lm7_1_GPS: tx antennat port cfg:%d\n", (unsigned)value); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_INFO, "Tx antennat port cfg:%d\n", (unsigned)value); return xsdr_tx_antennat_port_cfg(&d->xdev, value); } @@ -968,35 +1352,21 @@ int dev_m2_lm7_1_sensor_freqpps_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t } static -int usdr_device_m2_lm7_1_stream_initialize(lldev_t dev, subdev_t subdev, lowlevel_stream_params_t* params, stream_t* channel) +int usdr_device_m2_lm7_1_lsop(lldev_t dev, subdev_t subdev, + unsigned ls_op, lsopaddr_t ls_op_addr, + size_t meminsz, void* pin, size_t memoutsz, + const void* pout) { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)lowlevel_get_device(dev); - int res; - unsigned streamno = params->streamno; - - if (getenv("USDR_BARE_DEV")) { - return -EOPNOTSUPP; + int res = -ENOENT; + if (ls_op == USDR_LSOP_DRP) { + res = xsdr_override_drp(&d->xdev, ls_op_addr, meminsz, pin, memoutsz, pout); } - res = xsdr_prepare(&d->xdev, true, true); - if (res) { + if (res != -ENOENT) return res; - } - - res = d->p_original_ops->stream_initialize(dev, subdev, params, channel); - if (res) { - xsdr_rfic_streaming_down(&d->xdev, streamno == 0 ? RFIC_LMS7_RX : RFIC_LMS7_TX); - } - - return res; -} -static -int usdr_device_m2_lm7_1_stream_deinitialize(lldev_t dev, subdev_t subdev, stream_t channel) -{ - struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)lowlevel_get_device(dev); - xsdr_rfic_streaming_down(&d->xdev, RFIC_LMS7_RX); - return d->p_original_ops->stream_deinitialize(dev, subdev, channel); + return d->p_original_ops->ls_op(dev, subdev, ls_op, ls_op_addr, meminsz, pin, memoutsz, pout); } xsdr_dev_t* get_xsdr_dev(pdevice_t udev) @@ -1015,53 +1385,65 @@ int usdr_device_m2_lm7_1_initialize(pdevice_t udev, unsigned pcount, const char* d->bifurcation_en = false; d->nodecint = false; + d->double_pump = false; for (unsigned i = 0; i < pcount; i++) { if (strcmp(devparam[i], "fe") == 0) { fe = devval[i]; } if (strcmp(devparam[i], "bifurcation") == 0) { - d->bifurcation_en = atoi(devval[i]); + d->bifurcation_en = (devval[i]) ? atoi(devval[i]) : 1; } if (strcmp(devparam[i], "nodec") == 0) { d->nodecint = true; } + if (strcmp(devparam[i], "dpump") == 0) { + d->double_pump = true; + } } res = xsdr_init(&d->xdev); if (res) return res; - if (d->xdev.new_rev) { - // Init FE - res = device_fe_probe(udev, d->xdev.ssdr ? "m2b+m" : "m2a+e", fe, I2C_BUS_FRONTEND, &d->fe); - if (res) { - return res; - } + if (d->xdev.ssdr) { + res = vfs_add_const_i64_vec(&udev->rootfs, ssdr_params_m2_lm7_1_rev000, SIZEOF_ARRAY(ssdr_params_m2_lm7_1_rev000)); + if (res) + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_WARNING, "Unable to set device name \"ssdr\"!\n"); + } else { + res = vfs_add_const_i64_vec(&udev->rootfs, xsdr_params_m2_lm7_1_rev000, SIZEOF_ARRAY(xsdr_params_m2_lm7_1_rev000)); + if (res) + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_WARNING, "Unable to set device name \"xsdr\"!\n"); } -#if 0 - //Load DSP ucode - lowlevel_reg_wr32(dev, 0, 0, 0x02000001); - for (unsigned k = 0; k < SIZEOF_ARRAY(s_dsp_ucode_fir2); k++) - lowlevel_reg_wr32(dev, 0, 0, s_dsp_ucode_fir2[k]); - lowlevel_reg_wr32(dev, 0, 0, 0x02000000); -#endif + d->xdev.dpump = d->double_pump; // Proxy operations memcpy(&d->my_ops, lowlevel_get_ops(dev), sizeof (lowlevel_ops_t)); - d->my_ops.stream_initialize = &usdr_device_m2_lm7_1_stream_initialize; - d->my_ops.stream_deinitialize = &usdr_device_m2_lm7_1_stream_deinitialize; + d->my_ops.ls_op = &usdr_device_m2_lm7_1_lsop; d->p_original_ops = lowlevel_get_ops(dev); dev->ops = &d->my_ops; + // Probe fe + if (d->xdev.new_rev) { + // Init FE + res = device_fe_probe(udev, d->xdev.ssdr ? "m2b+m" : "m2a+e", fe, I2C_BUS_FRONTEND, &d->fe); + if (res) { + return res; + } + } + return 0; } - -int dev_m2_lm7_1_usb_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t* ovalue) +int dev_m2_lm7_1_qspi_flash_master_off_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t *ovalue) { - *ovalue = 0; + struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)ud; + if (d->xdev.ssdr_pro) { + *ovalue = 0x07b0000; + } else { + *ovalue = MASTER_IMAGE_OFF; + } return 0; } @@ -1111,12 +1493,15 @@ int usdr_device_m2_lm7_1_create_stream(device_t* dev, const char* sid, const cha unsigned hwchs; channel_info_t lchans; + if (getenv("USDR_BARE_DEV")) { + return -EOPNOTSUPP; + } + res = xsdr_map_channels(channels, &lchans); if (res) { return res; } - if (strstr(sid, "rx") != NULL) { if (d->rx) { return -EBUSY; @@ -1125,7 +1510,7 @@ int usdr_device_m2_lm7_1_create_stream(device_t* dev, const char* sid, const cha struct sfetrx4_config rxcfg; res = parse_sfetrx4(dformat, &lchans, pktsyms, channels->count, &rxcfg); if (res) { - USDR_LOG("UDEV", USDR_LOG_ERROR, "Unable to parse RX stream configuration!\n"); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_ERROR, "Unable to parse RX stream configuration!\n"); return res; } @@ -1138,6 +1523,11 @@ int usdr_device_m2_lm7_1_create_stream(device_t* dev, const char* sid, const cha if (rxcfg.bifurcation_valid && d->bifurcation_en) { d->xdev.siso_sdr_active_rx = true; flags |= DMS_FLAG_BIFURCATION; + // TODO: update samplerate settings + } + + if (d->double_pump) { + d->xdev.siso_sdr_active_rx = true; } // Reset samplerate with proper bifurcation flags @@ -1149,12 +1539,19 @@ int usdr_device_m2_lm7_1_create_stream(device_t* dev, const char* sid, const cha } } + res = xsdr_prepare(&d->xdev, true, d->tx); + if (res) { + return res; + } + res = create_sfetrx4_stream(dev, CORE_SFERX_DMA32_R0, dformat, channels->count, &lchans, pktsyms, flags, M2PCI_REG_WR_RXDMA_CONFIRM, VIRT_CFG_SFX_BASE, 0, SRF4_FIFOBSZ, CSR_RFE4_BASE, &d->rx, &hwchs); if (res) { + USDR_LL_LOG(d->base.dev, "XSDR", USDR_LOG_ERROR, "Unable to create stream '%s': error=%d\n", sid, res); return res; } + *out_handle = d->rx; } else if (strstr(sid, "tx") != NULL) { if (d->tx) { @@ -1164,7 +1561,7 @@ int usdr_device_m2_lm7_1_create_stream(device_t* dev, const char* sid, const cha struct sfetrx4_config txcfg; res = parse_sfetrx4(dformat, &lchans, pktsyms, channels->count, &txcfg); if (res) { - USDR_LOG("UDEV", USDR_LOG_ERROR, "Unable to parse TX stream configuration!\n"); + USDR_LL_LOG(d->base.dev, "UDEV", USDR_LOG_ERROR, "Unable to parse TX stream configuration!\n"); return res; } @@ -1177,6 +1574,16 @@ int usdr_device_m2_lm7_1_create_stream(device_t* dev, const char* sid, const cha if (txcfg.bifurcation_valid && d->bifurcation_en) { d->xdev.siso_sdr_active_tx = true; flags |= DMS_FLAG_BIFURCATION; + // TODO: update samplerate settings + } + + if (d->double_pump) { + d->xdev.siso_sdr_active_tx = true; + } + + res = xsdr_prepare(&d->xdev, d->rx, true); + if (res) { + return res; } res = create_sfetrx4_stream(dev, CORE_SFETX_DMA32_R0, dformat, channels->count, &lchans, pktsyms, @@ -1187,7 +1594,8 @@ int usdr_device_m2_lm7_1_create_stream(device_t* dev, const char* sid, const cha } *out_handle = d->tx; - res = xsdr_hwchans_cnt(&d->xdev, false, hwchs); + res = res ? res : xsdr_hwchans_cnt(&d->xdev, false, hwchs); + res = res ? res : lms7002m_dc_corr_en(&d->xdev.base.lmsstate, d->xdev.base.rx_run[0], d->xdev.base.rx_run[1], d->xdev.base.tx_run[0], d->xdev.base.tx_run[1]); } return res; @@ -1198,11 +1606,19 @@ int usdr_device_m2_lm7_1_unregister_stream(device_t* dev, stream_handle_t* strea { struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)dev; if (stream == d->tx) { + xsdr_rfic_streaming_down(&d->xdev, RFIC_LMS7_TX); + d->tx->ops->destroy(d->tx); d->tx = NULL; + + d->xdev.lms7_txlo_last = 0; } else if (stream == d->rx) { + xsdr_rfic_streaming_down(&d->xdev, RFIC_LMS7_RX); + d->rx->ops->destroy(d->rx); d->rx = NULL; + + d->xdev.lms7_rxlo_last = 0; } else { return -EINVAL; } @@ -1214,6 +1630,7 @@ static int usdr_device_m2_lm7_1_create(lldev_t dev, device_id_t devid) { int res; + unsigned hwid, did; struct dev_m2_lm7_1_gps *d = (struct dev_m2_lm7_1_gps *)malloc(sizeof(struct dev_m2_lm7_1_gps)); res = xsdr_ctor(dev, &d->xdev); @@ -1226,6 +1643,24 @@ int usdr_device_m2_lm7_1_create(lldev_t dev, device_id_t devid) goto failed_free; } + res = dev_gpi_get32(dev, IGPI_HWID, &hwid); + if (res) { + goto failed_free; + } + did = ((hwid >> 16) & 0xff); + + if ((res == 0) && (did == SSDR_DEV || did == SSDRPRO_DEV)) { + res = vfs_add_const_i64_vec(&d->base.rootfs, + s_params_m2_lm7_1_rev_advanced, + SIZEOF_ARRAY(s_params_m2_lm7_1_rev_advanced)); + } else { + res = vfs_add_const_i64_vec(&d->base.rootfs, + s_params_m2_lm7_1_rev_generic, + SIZEOF_ARRAY(s_params_m2_lm7_1_rev_generic)); + } + if (res) + goto failed_tree_creation; + res = vfs_add_const_i64_vec(&d->base.rootfs, s_params_m2_lm7_1_rev000, SIZEOF_ARRAY(s_params_m2_lm7_1_rev000)); @@ -1238,6 +1673,12 @@ int usdr_device_m2_lm7_1_create(lldev_t dev, device_id_t devid) if (res) goto failed_tree_creation; + res = usdr_vfs_obj_link_init_array(&d->base, + s_links, + SIZEOF_ARRAY(s_links)); + if (res) + goto failed_tree_creation; + d->base.initialize = &usdr_device_m2_lm7_1_initialize; d->base.destroy = &usdr_device_m2_lm7_1_destroy; d->base.create_stream = &usdr_device_m2_lm7_1_create_stream; diff --git a/src/lib/device/m2_lm7_1/xsdr_ctrl.c b/src/lib/device/m2_lm7_1/xsdr_ctrl.c index b57334f5..12a2c509 100644 --- a/src/lib/device/m2_lm7_1/xsdr_ctrl.c +++ b/src/lib/device/m2_lm7_1/xsdr_ctrl.c @@ -7,7 +7,7 @@ #include "../hw/tmp114/tmp114.h" #include "../hw/tmp108/tmp108.h" #include "../hw/dac80501/dac80501.h" - +#include "../hw/at24/at24.h" #include #include @@ -19,20 +19,32 @@ #include "../cal/cal_lo_iqimb.h" #include "../ipblks/streams/sfe_rx_4.h" #include "../ipblks/xlnx_mmcm.h" +#include "../ipblks/fgearbox.h" #ifndef MAX #define MAX(x,y) (((x) > (y)) ? (x) : (y)) #endif +enum { + DRP_MMCM_PORT_RX = 0, + DRP_MMCM_PORT_TX = 1, +}; -// TXA RXB -// TXB RXA // SSDR RF routing infortmation +// ------------------------------ // LMS8_CHA <= TXOUT_A_2 <= BAND2 // LMS8_CHB <= TXOUT_B_2 <= BAND2 // LMS8_CHC => RXIN_A_2 => LNA_H // LMS8_CHD => RXIN_B_2 => LNA_H - +// ------------------------------ +// <= BAND1 +// <= BAND1 +// => LNA_W +// => LNA_W +// ------------------------------ +// loopback configuration: +// Band2 -- LNA_L +// Band1 -- LNA_W / LNA_H enum { XSDR_INT_REFCLK = 26000000, @@ -110,13 +122,23 @@ static int _xsdr_init_revo(xsdr_dev_t *d); static int _xsdr_checkpwr(xsdr_dev_t *d) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "checkpwr: %d\n", d->pwr_en); + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_ERROR, "checkpwr: %d\n", d->pwr_en); if (!d->pwr_en) { return xsdr_pwren(d, true); } return 0; } +int xsdr_config_rcvdly(xsdr_dev_t *d, unsigned type, unsigned val) +{ + lldev_t dev = d->base.lmsstate.dev; + unsigned subdev = d->base.lmsstate.subdev; + + uint32_t cmd = (1 << 31) | (((type + 14) & 0x7f) << 24) | (val & 0xffff); + + return lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, cmd); +} + int xsdr_set_samplerate(xsdr_dev_t *d, unsigned rxrate, unsigned txrate, unsigned adcclk, unsigned dacclk) @@ -124,125 +146,420 @@ int xsdr_set_samplerate(xsdr_dev_t *d, return xsdr_set_samplerate_ex(d, rxrate, txrate, adcclk, dacclk, 0); } -int xsdr_configure_lml_mmcm(xsdr_dev_t *d) + +enum { + PHY_REG_PORT_CTRL = 0, + PHY_REG_MMCM_DRP = 1, + PHY_REG_MMCM_CTRL = 2, + PHY_REG_MMCM_PSINC = 3, + PHY_REG_DLY_VALUE = 4, + PHY_REG_PORT_IQSEL = 5, + PHY_REG_LFSR_GEN = 6, + PHY_REG_IQAB_GEN = 7, +}; + +enum { + PHY_REG_RXBANK_CTRL = 0, // Control registers + PHY_REG_RXBANK_MMCM = 1, // MMCM registers + PHY_REG_RXBANK_CLKMEAS = 2, + PHY_REG_RXBANK_LFSRCHK = 3, // LFSR control + PHY_REG_RXBANK_IQABCHK = 4, // IQAB control + PHY_REG_RXBANK_CAPTURE = 5, // IQ capture + PHY_REG_RXBANK_DC_EST = 6, // DC estimator + + PHY_REG_RXBANK_CLKDLY = 14, // Clock delay + PHY_REG_RXBANK_FRMDLY = 15, // Frame delay +}; + +// PHY_REG_RXBANK_CTRL registers +enum { + ADDR_CTRL_DESER = 0, + ADDR_CTRL_MMCM = 1, + ADDR_CTRL_MAN = 2, +}; + +// PHY_REG_RXBANK_DC_EST +enum { + ADDR_DC_EST_RESET = 0, + ADDR_DC_EST_ACCM = 1, +}; + + +int xsdr_phy_rx_reg(xsdr_dev_t *d, bool wr, uint8_t bank, uint8_t addr, uint16_t val) +{ + uint32_t reg = (wr ? 0x80000000 : 0) | (((uint32_t)bank & 0x7f) << 24) | ((uint32_t)addr << 16) | val; + return lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, reg); +} + +int xsdr_phy_en_lfsr_checker_mimo(xsdr_dev_t *d, bool en) { - bool nomul = d->base.lml_mode.rxsisoddr || (d->base.rxtsp_div > 1); - unsigned rx_mclk = d->base.cgen_clk / d->base.rxcgen_div / d->base.lml_mode.rxdiv; - unsigned io_clk = (nomul) ? rx_mclk : rx_mclk * 2; - unsigned vco_div_io = (MMCM_VCO_MAX + io_clk - 1) / io_clk; - unsigned rb; int res = 0; - struct mmcm_config_raw cfg_raw; - memset(&cfg_raw, 0, sizeof(cfg_raw)); + if (!en) { + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_IQABCHK, 0, 0); + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_LFSRCHK, 0, 0x0f); + return res; + } - if (vco_div_io > 63) - vco_div_io = 63; + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_IQABCHK, 0, 0); + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_LFSRCHK, 0, 0x0f); + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_LFSRCHK, 0, 0xf0); + return res; +} - cfg_raw.type = MT_7SERIES_MMCM; - cfg_raw.ports[CLKOUT_PORT_0].period_l = (vco_div_io + 1) / 2; - cfg_raw.ports[CLKOUT_PORT_0].period_h = vco_div_io / 2; - cfg_raw.ports[CLKOUT_PORT_1].period_l = (vco_div_io + 1) / 2; - cfg_raw.ports[CLKOUT_PORT_1].period_h = vco_div_io / 2; +int xsdr_phy_en_iqab_checker_mimo(xsdr_dev_t *d, bool en) +{ + int res = 0; + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_LFSRCHK, 0, 0x0f); + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_IQABCHK, 0, 0); + usleep(1); + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_IQABCHK, 0, en ? 1 : 0); + return res; +} - cfg_raw.ports[CLKOUT_PORT_2].period_l = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_2].period_h = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_3].period_l = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_3].period_h = vco_div_io; +int xsdr_phy_lfsr_mimo_state_s(xsdr_dev_t *d, int ridx, uint32_t* v) +{ + int res = 0; + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_LFSRCHK, ridx, 0); + res = res ? res : usleep(1); + res = res ? res : lowlevel_reg_rd32(d->base.lmsstate.dev, 0, REG_CFG_PHY_0, v); + return res; +} - cfg_raw.ports[CLKOUT_PORT_4].period_l = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_4].period_h = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_5].period_l = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_5].period_h = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_6].period_l = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_6].period_h = vco_div_io; +int xsdr_phy_capture_start(xsdr_dev_t *d, bool start) +{ + return xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_CAPTURE, 0, start ? 1 : 0); +} - cfg_raw.ports[CLKOUT_PORT_0].delay = 1; +int xsdr_phy_capture_get_item(xsdr_dev_t *d, bool chb, unsigned idx, uint32_t* v) +{ + int res = 0; + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_CAPTURE, (chb ? 0x80 : 0x00) | (idx & 0x7f), 0); + res = res ? res : lowlevel_reg_rd32(d->base.lmsstate.dev, 0, REG_CFG_PHY_0, v); + return res; +} - if (nomul) { - cfg_raw.ports[CLKOUT_PORT_FB].period_l =(vco_div_io + 1) / 2; - cfg_raw.ports[CLKOUT_PORT_FB].period_h = vco_div_io / 2; +int xsdr_phy_capture_get(xsdr_dev_t *d, bool chb, unsigned count, uint32_t* odata) +{ + int res = 0; + for (unsigned i = 0; i < count; i++) { + res = res ? res : xsdr_phy_capture_get_item(d, chb, i, &odata[i]); + } + return res; +} + +enum dc_estimations { + DC_ESTIM_GEN = 0, + DC_ESTIM_AI = 4, + DC_ESTIM_AQ = 5, + DC_ESTIM_BI = 6, + DC_ESTIM_BQ = 7, +}; + +int xsdr_phy_dc_estim_start(xsdr_dev_t *d, bool start) +{ + return xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_DC_EST, ADDR_DC_EST_RESET, start ? 0 : 1); +} + +int xsdr_phy_dc_estim_accum(xsdr_dev_t *d, unsigned accum) +{ + return xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_DC_EST, ADDR_DC_EST_ACCM, accum); +} + +int xsdr_phy_dc_estim_get(xsdr_dev_t *d, enum dc_estimations v, int32_t* odata) +{ + int res = 0; + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_DC_EST, v, 0); + res = res ? res : lowlevel_reg_rd32(d->base.lmsstate.dev, 0, REG_CFG_PHY_0, (uint32_t*)odata); + return res; +} + +int xsdr_phy_lfsr_mimo_state(xsdr_dev_t *d, int type, uint32_t v[4]) +{ + int res = 0; + // Get BER statistics + for (unsigned h = 0; h < 4; h++) { + unsigned p = ((type & 0x3) << 2) | (h); + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_LFSRCHK, p, 0); + res = res ? res : usleep(1); + res = res ? res : lowlevel_reg_rd32(d->base.lmsstate.dev, 0, REG_CFG_PHY_0, &v[h]); + } + + return res; +} + +int xsdr_phy_tx_reg(xsdr_dev_t *d, uint8_t addr, uint32_t val) +{ + return lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_1, (((uint32_t)addr) << 24) | (val & 0xffffff)); +} + +int xsdr_phy_en_lfsr_generator_mimo(xsdr_dev_t *d, bool en, bool lfsr) +{ + int res = 0; + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_LFSR_GEN, 0); + res = res ? res : usleep(1); + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_IQAB_GEN, lfsr ? 0 : 1); + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_LFSR_GEN, en ? 1 : 0); + + return res; +} + +static int _xsdr_rxserdes_reset(xsdr_dev_t *d) { + int res = 0; + unsigned sisosdrflag = d->dpump ? 16 : d->base.lml_mode.rxsisoddr ? 8 : 0; + //res = res ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, 0x80000007 | sisosdrflag); + //res = res ? res : usleep(10); + //res = res ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, 0x80000000 | sisosdrflag); + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_CTRL, ADDR_CTRL_DESER, 0x80000007 | sisosdrflag); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_rx_reg(d, true, PHY_REG_RXBANK_CTRL, ADDR_CTRL_DESER, 0x80000000 | sisosdrflag); + return res; +} + +static int _xsdr_txserdes_reset(xsdr_dev_t *d) { + int res = 0; + // Reset OSERDESE2 + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_CTRL, 0); + res = res ? res : usleep(1); + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_CTRL, 1); + return res; +} + +int xsdr_override_drp(xsdr_dev_t *d, lsopaddr_t ls_op_addr, + size_t meminsz, void* pin, size_t memoutsz, + const void* pout) +{ + int res = 0; + lldev_t dev = d->base.lmsstate.dev; + unsigned subdev = d->base.lmsstate.subdev; + unsigned port = (ls_op_addr >> 16) & 0xff; + if (port != DRP_MMCM_PORT_TX) + return -ENOENT; + + uint32_t drp_cmd = (ls_op_addr & 0x7f) << 16; + if (meminsz == 2 && memoutsz == 0) { + } else if (meminsz == 0 && memoutsz == 2) { + drp_cmd |= (1 << 23) | (*((uint16_t*)pout)); } else { - cfg_raw.ports[CLKOUT_PORT_FB].period_l = vco_div_io; - cfg_raw.ports[CLKOUT_PORT_FB].period_h = vco_div_io; + return -EINVAL; } - USDR_LOG("XDEV", USDR_LOG_ERROR, "MMCM set to MCLK = %.3f IOCLK = %.3f Mhz IODIV = %d\n", - rx_mclk / (1.0e6), io_clk / (1.0e6), vco_div_io); - res = (res) ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0xF); - usleep(100); - res = (res) ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0xD); - usleep(100); - res = (res) ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0x1); + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_DRP, drp_cmd); - if (res) - return res; + // Wait for transaction to process + uint32_t rb; + for (unsigned i = 0; i < 1000; i++) { + res = res ? res : lowlevel_reg_rd32(dev, subdev, REG_CFG_PHY_1, &rb); + if (res || (!(rb & (1 << 9)))) + break; + + usleep(10); + } - usleep(1000); - res = mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, &cfg_raw); if (res) return res; - res = (res) ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0x0); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_DEBUG, "MMCM DRP_TX CMD=%08x RB=%08x\n", drp_cmd, rb); - usleep(10000); - res = (res) ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, - 0x01000000); - if (res) - return res; + if (meminsz) { + *((uint16_t*)pin) = rb >> 16; + } + return 0; +} - for (unsigned k = 0; k < 10; k++) { - // Wait for lock - res = lowlevel_reg_rd32(d->base.lmsstate.dev, d->base.lmsstate.subdev, - REG_CFG_PHY_0, &rb); +static struct mmcm_config_raw g_tx_cfg_raw; - USDR_LOG("XDEV", USDR_LOG_INFO, "MMCM FLAGS:%08x\n", rb); - if (rb & (1 << 16)) - return 0; +int xsdr_phy_tx_iqsel(xsdr_dev_t *d, uint8_t iqsel) +{ + return xsdr_phy_tx_reg(d, PHY_REG_PORT_IQSEL, iqsel); +} + +static int _xsdr_mmcm_pd(xsdr_dev_t *d) +{ + return xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, 2); +} + +// sep_clkdiv -- experimental mode with dual MMCM path to CLK/CLKDIV +static int g_clk_reduce = 0; +int xsdr_configure_lml_mmcm_tx(xsdr_dev_t *d, bool rx_master, unsigned rxphase, unsigned txphase, unsigned txphase_off) +{ + const unsigned VCO_MIN = d->ssdr_pro ? MMCM_VCO_MIN : MMCM_VCO_MIN_USP; + const unsigned VCO_MAX = d->ssdr_pro ? MMCM_VCO_MAX : MMCM_VCO_MAX_USP; + + bool nomul = d->dpump ? false : + (rx_master) ? d->base.lml_mode.rxsisoddr || (d->base.rxtsp_div > 1) : + d->base.lml_mode.txsisoddr || (d->base.txtsp_div > 1); + + unsigned mmcm_ctrl_sel = (rx_master) ? 0 : 4; + unsigned tx_mclk = d->base.cgen_clk / d->base.txcgen_div / d->base.lml_mode.txdiv; + unsigned rx_mclk = d->base.cgen_clk / d->base.rxcgen_div / d->base.lml_mode.rxdiv; + unsigned io_mclk = (rx_master) ? rx_mclk : tx_mclk; + unsigned io_clk = (nomul) ? io_mclk : io_mclk * 2; + unsigned vco_div_io = (VCO_MAX + io_clk - 1) / io_clk; + bool sep_clkdiv = d->sep_clkdiv; - usleep(5000); + // No MMCM in RX chain + if (!d->mmcm_single && !d->mmcm_rx && rx_master) + return 0; + + vco_div_io += g_clk_reduce; + + vco_div_io += (txphase_off > 2) ? 2 : txphase_off; + + if (vco_div_io > 63) { + vco_div_io = 63; } - return 0; - return -ERANGE; -#if 0 - res = lowlevel_reg_wr32(d->base.base.dev, d->base.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0x1); - if (res) - return res; - res = lowlevel_reg_wr32(d->base.base.dev, d->base.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0x0); + int res = 0; + struct mmcm_config_raw cfg_raw; + memset(&cfg_raw, 0, sizeof(cfg_raw)); + cfg_raw.type = (d->xilinx_usp) ? MT_USP_MMCM : MT_7SERIES_MMCM; + + if (vco_div_io * io_clk < VCO_MIN) { + if (nomul && !sep_clkdiv) { + vco_div_io = (VCO_MAX + io_clk - 1) / io_clk; + if (vco_div_io % 2) + vco_div_io++; + + if (vco_div_io > 126) + vco_div_io = 126; + } + + if (vco_div_io < 63) { + if ((vco_div_io + 2) * io_clk > VCO_MAX) { + vco_div_io += 1; + } else { + vco_div_io += 2; + } + } + } + + // res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, mmcm_ctrl_sel | 0); + // res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_IQSEL, d->base.lml_mode.txsisoddr ? 0b1010 : 0b1100); + + // 0 - n/a or IO_TX_DIV ( was IO_TX_IQSEL -- individual phase delay ) + // 1 - IO_TX + // 2 - IO_RX + // 3 - n/a ( was LOGIC_TX ) + // 4 - n/a + // 5 - FCLK_RX + // 6 - FCLK_TX + + if (!sep_clkdiv) { + cfg_raw.ports[CLKOUT_PORT_0].period_l = (vco_div_io + 1) / 2; // unused + cfg_raw.ports[CLKOUT_PORT_0].period_h = vco_div_io / 2; // unused + } else { + cfg_raw.ports[CLKOUT_PORT_0].period_l = vco_div_io; // IO_TX dedicated CLKDIV + cfg_raw.ports[CLKOUT_PORT_0].period_h = vco_div_io; // IO_TX dedicated CLKDIV + } + cfg_raw.ports[CLKOUT_PORT_1].period_l = (vco_div_io + 1) / 2; // IO_TX + cfg_raw.ports[CLKOUT_PORT_1].period_h = vco_div_io / 2; // IO_TX + + cfg_raw.ports[CLKOUT_PORT_2].period_l = (vco_div_io + 1) / 2; // IO_RX + cfg_raw.ports[CLKOUT_PORT_2].period_h = vco_div_io / 2; // IO_RX + + cfg_raw.ports[CLKOUT_PORT_3].period_l = (vco_div_io + 1) / 2; // not used + cfg_raw.ports[CLKOUT_PORT_3].period_h = vco_div_io / 2; // not used + + cfg_raw.ports[CLKOUT_PORT_4].period_l = (vco_div_io + 1) / 2; // not used + cfg_raw.ports[CLKOUT_PORT_4].period_h = vco_div_io / 2; // not used + + cfg_raw.ports[CLKOUT_PORT_5].period_l = (vco_div_io + 1) / 2; // FCLK_RX + cfg_raw.ports[CLKOUT_PORT_5].period_h = vco_div_io / 2; // FCLK_RX + cfg_raw.ports[CLKOUT_PORT_6].period_l = (vco_div_io + 1) / 2; // FCLK_TX + cfg_raw.ports[CLKOUT_PORT_6].period_h = vco_div_io / 2; // FCLK_TX + + unsigned total_budget = 8 * vco_div_io; + unsigned phase = total_budget / 2; + // Default 90deg F_CLK related to clocks + cfg_raw.ports[CLKOUT_PORT_6].phase = phase % 8; + cfg_raw.ports[CLKOUT_PORT_6].delay = phase / 8; + + // Old logic calibration (doesn't really work) + if (sep_clkdiv) { + unsigned phase_iq = 0; + cfg_raw.ports[CLKOUT_PORT_0].phase = phase_iq % 8; + cfg_raw.ports[CLKOUT_PORT_0].delay = phase_iq / 8; + + if (d->tx_override_phase_iq || txphase || txphase_off) { + unsigned raw = (txphase != 0) ? txphase_off : d->tx_override_phase_iq - 1; + cfg_raw.ports[CLKOUT_PORT_0].phase = raw % 8; + cfg_raw.ports[CLKOUT_PORT_0].delay = raw / 8; + } + } + + if (d->tx_override_phase || txphase) { + unsigned raw = (txphase != 0) ? (txphase - 1) : d->tx_override_phase - 1; + cfg_raw.ports[CLKOUT_PORT_6].phase = raw % 8; + cfg_raw.ports[CLKOUT_PORT_6].delay = raw / 8; + } + + if (d->rx_override_phase || rxphase) { + unsigned raw = (rxphase != 0) ? rxphase - 1 : d->rx_override_phase - 1; + cfg_raw.ports[CLKOUT_PORT_2].phase = raw % 8; + cfg_raw.ports[CLKOUT_PORT_2].delay = raw / 8; + } + + if (nomul) { + cfg_raw.ports[CLKOUT_PORT_FB].period_l =(vco_div_io + 1) / 2; + cfg_raw.ports[CLKOUT_PORT_FB].period_h = vco_div_io / 2; + } else { + cfg_raw.ports[CLKOUT_PORT_FB].period_l = vco_div_io; + cfg_raw.ports[CLKOUT_PORT_FB].period_h = vco_div_io; + } + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "MMCM_TX set to MCLK = %.3f IOCLK = %.3f Mhz IODIV = %d FWDCLK_DELAY%s = %d (VCO %d) SISO_DDR=%d VCO=%.3f MHZ OFF=%d REF=%s\n", + tx_mclk / (1.0e6), io_clk / (1.0e6), vco_div_io, + (d->tx_override_phase) ? "_OVR" : "", + cfg_raw.ports[CLKOUT_PORT_0].delay, cfg_raw.ports[CLKOUT_PORT_0].phase, + d->base.lml_mode.txsisoddr, + tx_mclk * (cfg_raw.ports[CLKOUT_PORT_FB].period_l + cfg_raw.ports[CLKOUT_PORT_FB].period_h) / 1.0e6, + txphase_off, rx_master ? "RX" : "TX"); + + + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, mmcm_ctrl_sel | 1); + res = res ? res : usleep(10); + res = res ? res : mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_TX, &cfg_raw); + + // Set IQSEL vector + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_IQSEL, d->base.lml_mode.txsisoddr ? 0b1010 : 0b1100); + + // Reset MMCM + // res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, mmcm_ctrl_sel | 1); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, mmcm_ctrl_sel | 0); if (res) return res; + for (unsigned k = 0; k < 100; k++) { + uint32_t rb; + // Wait for lock + res = res ? res : lowlevel_reg_rd32(d->base.lmsstate.dev, d->base.lmsstate.subdev, + REG_CFG_PHY_1, &rb); + if (res) + break; - //////////////////// + if (rb & 0xc00) { + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_ERROR, "MMCM NO CLOCK: %08x\n", rb); + return -EIO; + } - res = (res) ? res : lowlevel_reg_wr32(d->base.base.dev, d->base.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0x1); - usleep(1000); - res = (res) ? res : lowlevel_reg_wr32(d->base.base.dev, d->base.subdev, REG_CFG_PHY_0, - 0x80000000 | 0x10000 | 0x0); + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_DEBUG, "MMCM FLAGS:%08x\n", rb); + if (rb & (1 << 8)) { + g_tx_cfg_raw = cfg_raw; + return 0; + } - res = (res) ? res : lowlevel_reg_wr32(d->base.base.dev, d->base.subdev, REG_CFG_PHY_0, - 0x01000000); - for (unsigned k = 0; k < 10; k++) { - // Wait for lock - res = lowlevel_reg_rd32(d->base.base.dev, d->base.subdev, - REG_CFG_PHY_0, &rb); + usleep(10); } -#endif - return res; + + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_ERROR, "MMCM Ready flag timed out!\n"); + return -EIO; } -int xsdr_phy_tune(xsdr_dev_t *d, unsigned val) +int xsdr_phy_tune_rx(xsdr_dev_t *d, unsigned val) { - int res = mmcm_set_phdigdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, CLKOUT_PORT_0, val); + int res = mmcm_set_phdigdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_RX, CLKOUT_PORT_0, val); return res; } @@ -263,22 +580,460 @@ int xsdr_hwchans_cnt(xsdr_dev_t *d, bool rx, unsigned chans) return 0; } +enum HWID_MSKS { + PHY_CFG_SEP_CLKDIV_MSK = 0x80, + PHY_CFG_LML2_IS_RX = 0x40, + PHY_CFG_TX_MMCM = 0x20, + PHY_CFG_RX_MMCM = 0x10, + + PHY_CFG_HAS_DUC_DDC = 0x04, + PHY_CFG_SINGLE_MMCM = 0x01, +}; + +static bool noerrors_v4(unsigned errs[4], uint64_t* badness) +{ + if (badness) { + *badness = (errs[0]*errs[0]) + (errs[1]*errs[1]) + (errs[2]*errs[2]) + (errs[3]*errs[3]); + } + return errs[0] == 0 && errs[1] == 0 && errs[2] == 0 && errs[3] == 0; +} + +static bool noerrors_v2(unsigned errs[4], uint64_t* badness) +{ + if (badness) { + *badness = (errs[0]*errs[0]) + (errs[1]*errs[1]); + } + return errs[0] == 0 && errs[1] == 0; +} + +int xsdr_txphase_ovr(xsdr_dev_t *d, unsigned v) +{ + int res = 0; + d->tx_override_phase_iq = v; + d->tx_override_phase = v; + + res = res ? res : xsdr_configure_lml_mmcm_tx(d, false, d->lmlcal_rx_phase, 0, 0); + res = res ? res : _xsdr_txserdes_reset(d); + + return res; +} + +static int _xsdr_calibrate_txlfsr_check(xsdr_dev_t *d, unsigned check_to, + unsigned errs[4], unsigned *piqserrs, + uint64_t *pbadness) +{ + int res = 0; + unsigned w; + + // Reset OSERDESE2 + res = res ? res : _xsdr_txserdes_reset(d); + res = res ? res : usleep(1); + res = res ? res : lms7002m_limelight_fifo_reset(&d->base.lmsstate, true, true); + res = res ? res : _xsdr_rxserdes_reset(d); // In case of RX-TX in the same MMCM + res = res ? res : usleep(20); + res = res ? res : xsdr_phy_en_lfsr_checker_mimo(d, true); + //res = res ? res : xsdr_phy_en_iqab_checker_mimo(d, true); + + for (w = 0; w < check_to; w++) { + res = res ? res : usleep(100); + res = res ? res : xsdr_phy_lfsr_mimo_state(d, LFSR_CNTR_BER, errs); + res = res ? res : xsdr_phy_lfsr_mimo_state_s(d, LFSR_CNTR_IQS << 2, piqserrs); + if (res || (!noerrors_v4(errs, pbadness)) || *piqserrs) { + break; + } + } + *pbadness *= 1.0 * check_to / (w + 1); // Rescale + + return 0; +} + +static int _xsdr_calibrate_lml(xsdr_dev_t *d) +{ + lldev_t dev = d->base.lmsstate.dev; + int res = 0; + bool mmcm_rx_only_path = (!d->base.tx_run[0] && !d->base.tx_run[1]); + bool old_rx_run[2] = { d->base.rx_run[0], d->base.rx_run[1] }; + + uint64_t rx_badness = UINT64_MAX; + uint64_t tx_badness = UINT64_MAX; + unsigned tx_iqerrs = UINT_MAX; + unsigned iqserrs2 = -1; + const unsigned MAX_RTY = 5; + + g_clk_reduce = 0; + + // Fixup for SSDR_PRO +#if 0 + if (d->ssdr_pro) { + // RX SISO DDR + res = res ? res : lowlevel_reg_wr32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, + d->siso_sdr_active_rx ? (1u << 9) : 0); + return res; + } +#endif + + if (d->mmcm_tx) { + if (!d->ssdr_pro) { + // Boost IO voltage for stable high speed link + if (!d->siso_sdr_active_rx && d->new_rev && d->ssdr && (d->s_rxrate > 85e6 || d->s_txrate > 85e6)) { + res = res ? res : xsdr_set_vio(d, 1910); + } else if (!d->siso_sdr_active_rx && d->new_rev && !d->ssdr && (d->s_rxrate > 70e6 || d->s_txrate > 70e6)) { + res = res ? res : xsdr_set_vio(d, 1940); + res = res ? res : xsdr_set_lms125vdd(d, 1320); + } else if (!d->siso_sdr_active_rx && !d->new_rev && (d->s_rxrate > 50e6 || d->s_txrate > 50e6)) { + res = res ? res : xsdr_set_vio(d, ((d->s_rxrate > 60e6 || d->s_txrate > 60e6)) ? 2150 : 1960); + } + + // Fixup for 53-58 MSPS range, but still 58 to 60 might be unoperable on some chips, and 60+ works fine again + if (!mmcm_rx_only_path && d->new_rev && !d->ssdr && (d->s_txrate >= 53e6 && d->s_txrate <= 60e6)) { + res = res ? res : xsdr_set_lms125vdd(d, 1360); + } + } else { + // TODO: optimize the values + bool boost_vio = (!d->siso_sdr_active_rx) && (d->s_rxrate > 84e6 || d->s_txrate > 84e6); + if (boost_vio) { + res = res ? res : xsdr_set_lms125vdd(d, 1330); + res = res ? res : xsdr_set_vio(d, 1910); + } + } + + if (!(d->base.rx_run[0] || d->base.rx_run[1])) { + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS_PWR, IGPO_LMS_PWR_LDOEN | IGPO_LMS_PWR_NRESET | IGPO_LMS_PWR_RXEN | IGPO_LMS_PWR_TXEN); + res = res ? res : usleep(1000); + res = res ? res : lms7002m_streaming_up(&d->base, RFIC_LMS7_RX, LMS7_CH_AB, 0, LMS7_CH_NONE, 0); + } + + res = res ? res : xsdr_configure_lml_mmcm_tx(d, mmcm_rx_only_path, 0, 0, 0); + res = res ? res : lms7002m_limelight_reset(&d->base.lmsstate); + res = res ? res : _xsdr_rxserdes_reset(d); + + if (res) + return res; + + unsigned recal_rx = 0; +recalibrate_rx: + + // Autocallibration if RX phase wasn't set + if (d->rx_override_phase == 0) { + const unsigned check_to = 10; + unsigned phase_m; + int ph_ty_m = 0; + unsigned iqserrs; + unsigned errs[4] = { UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX }; + bool last_noerrs = false; + + for (unsigned rxrty = 0; rxrty < MAX_RTY; rxrty++) { + uint64_t badness_m = UINT64_MAX; + + phase_m = 0; + res = res ? res : lms7002m_set_lmlrx_mode(&d->base, XSDR_LMLRX_LFSR); + + int phase_min; + int phase_max; + int phase_last = -1; + + phase_min = 4*63 + 1; + phase_max = 0; + + badness_m = UINT64_MAX; + + for (unsigned ph = 1; ph < 4*63 + 1; ph++) { + unsigned w; + uint64_t badness = UINT64_MAX; + + res = res ? res : usleep(10); + res = res ? res : xsdr_configure_lml_mmcm_tx(d, mmcm_rx_only_path, ph, 0, 0); + res = res ? res : lms7002m_limelight_fifo_reset(&d->base.lmsstate, true, true); + res = res ? res : _xsdr_rxserdes_reset(d); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_en_lfsr_checker_mimo(d, true); + + for (w = 0; w < check_to; w++) { + res = res ? res : usleep(100); + res = res ? res : xsdr_phy_lfsr_mimo_state(d, LFSR_CNTR_BER, errs); + if (res || (d->dpump ? !noerrors_v2(errs, &badness) : !noerrors_v4(errs, &badness))) { + break; + } + } + + badness *= 1.0 * check_to / (w + 1); // Rescale + phase_last = ph; + last_noerrs = false; + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "PHASE_RX/%d=%2d I=%2d [%6d/%6d/%6d/%6d] BD=%lld\n", rxrty, ph - 1, w, + errs[0], errs[1], errs[2], errs[3], (long long)badness); + if (res) { + break; + } else if ((last_noerrs = (d->dpump ? noerrors_v2(errs, &badness) : noerrors_v4(errs, &badness)))) { + phase_m = ph; + if (ph < phase_min) + phase_min = ph; + if (ph > phase_max) + phase_max = ph; + + // Got more than 7 phases, we're safe; skip searching + unsigned sphase = (rxrty == 0) ? 8 : (rxrty == 1) ? 4 : (rxrty == 2) ? 1 : 0; + if (phase_max - phase_min >= sphase) + break; + } else if (phase_max >= phase_min) { + break; + } + + if (badness_m > badness) { + badness_m = badness; + phase_m = ph; + } + } + + if (phase_max >= phase_min) { + phase_m = (phase_max + phase_min) / 2; + } + + if (phase_last == phase_m && last_noerrs) { + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "Took PHASE_RX=%2d [%6d/%6d/%6d/%6d] BD=0\n", phase_m - 1, + errs[0], errs[1], errs[2], errs[3]); + rx_badness = 0; + break; + } + + // Try our best at least + res = res ? res : xsdr_configure_lml_mmcm_tx(d, mmcm_rx_only_path, phase_m, 0, 0); + res = res ? res : lms7002m_limelight_fifo_reset(&d->base.lmsstate, true, true); + res = res ? res : _xsdr_rxserdes_reset(d); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_en_lfsr_checker_mimo(d, true); + + uint64_t badness = UINT64_MAX; + unsigned w; + for (w = 0; w < check_to; w++) { + res = res ? res : usleep(100); + res = res ? res : xsdr_phy_lfsr_mimo_state(d, LFSR_CNTR_BER, errs); + if (res || (d->dpump ? !noerrors_v2(errs, &badness) : !noerrors_v4(errs, &badness))) { + break; + } + } + + badness *= 1.0 * check_to / (w + 1); // Rescale + rx_badness = badness; + + bool warn_badness = (rxrty == MAX_RTY - 1); + USDR_LL_LOG(dev, "XDEV", warn_badness ? USDR_LOG_WARNING : USDR_LOG_INFO, "RePHASE_RX=%2d I=%2d [%6d/%6d/%6d/%6d] BD=%lld\n", phase_m - 1, w, + errs[0], errs[1], errs[2], errs[3], (long long)badness); + + if (badness == 0) + break; + + if (g_clk_reduce < 2) + g_clk_reduce++; + } + + d->lmlcal_rx_phase = phase_m; + + if (mmcm_rx_only_path) + goto no_tx; + + res = res ? res : lms7002m_set_lmlrx_mode(&d->base, XSDR_LMLRX_DIGLOOPBACK); + res = res ? res : xsdr_phy_en_lfsr_generator_mimo(d, true, true); + res = res ? res : xsdr_phy_en_lfsr_checker_mimo(d, true); + //res = res ? res : xsdr_phy_en_iqab_checker_mimo(d, true); + + bool mmcx_rx_path = false; + bool check_rx = false; + uint64_t badness_m = UINT64_MAX; + unsigned failed_rxcnt = 0; + phase_m = 0; + for (unsigned rty = 0; rty < 8; rty++) { + const unsigned iq_phases[8] = { 0, 1, 2, 62, 63, 2, 1, 0}; + unsigned iq_ph = iq_phases[rty]; + // Once we change VCO frequency RX calibration becomes invalid and we need to adjust it + + for (unsigned ph = 1; ph < 4*63 + 1; ph++) { + //unsigned w; + uint64_t badness = UINT64_MAX; + res = res ? res : xsdr_configure_lml_mmcm_tx(d, mmcx_rx_path, d->lmlcal_rx_phase, ph, 0 /*iq_ph*/); + if (check_rx) { + res = res ? res : lms7002m_set_lmlrx_mode(&d->base, XSDR_LMLRX_LFSR); + res = res ? res : lms7002m_limelight_fifo_reset(&d->base.lmsstate, true, true); + res = res ? res : _xsdr_rxserdes_reset(d); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_en_lfsr_checker_mimo(d, true); + res = res ? res : usleep(100); + res = res ? res : xsdr_phy_lfsr_mimo_state(d, LFSR_CNTR_BER, errs); + if (res || (d->dpump ? !noerrors_v2(errs, &badness) : !noerrors_v4(errs, &badness))) { + USDR_LL_LOG(dev, "XDEV", USDR_LOG_WARNING, "FAIL_PHASE_RX=%2d PH=%2d [%6d/%6d/%6d/%6d] BD=%lld\n", d->lmlcal_rx_phase, ph - 1, + errs[0], errs[1], errs[2], errs[3], (long long)badness); + if (badness > 20) { + failed_rxcnt++; + if (failed_rxcnt < 6) { + ph--; + continue; + } + if (failed_rxcnt > 30) { + if (recal_rx < 3) { + recal_rx++; + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "GLOBAL_RESYNC\n"); + goto recalibrate_rx; + } + } + } + } + res = res ? res : lms7002m_set_lmlrx_mode(&d->base, XSDR_LMLRX_DIGLOOPBACK); + failed_rxcnt = 0; + } + res = res ? res : _xsdr_calibrate_txlfsr_check(d, check_to, errs, &iqserrs, &badness); + + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "PHASE_TX=%2d [%6d/%6d/%6d/%6d - %6d] BD=%.3e\n", ph - 1, + errs[0], errs[1], errs[2], errs[3], iqserrs, (double)badness); + if (res || (noerrors_v4(errs, &badness) /* && (iqserrs == 0)*/) || (rty > 1 && badness < 20)) { + phase_m = ph; + + for (int g = 0; g < 24; g++) { + // Check A/B & I/Q aligment is ok + res = res ? res : xsdr_phy_en_lfsr_generator_mimo(d, true, false); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_en_iqab_checker_mimo(d, true); + res = res ? res : usleep(1000); + res = res ? res : xsdr_phy_lfsr_mimo_state_s(d, LFSR_CNTR_IQS << 2, &iqserrs); + if (res) + return res; + + if ((rty == 0 && iqserrs != 0) || iqserrs > 40) { + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "PHASE_TX[%d]=%2d ABIQ=%d\n", g, ph - 1, iqserrs); + unsigned msk[4] = { 0b1100, 0b0110, 0b0011, 0b1001 }; + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_IQSEL, d->base.lml_mode.txsisoddr ? 0b1010 : msk[(g + 1) % 4]); + //res = res ? res : _xsdr_txserdes_reset(d); + res = res ? res : usleep(10); + res = res ? res : lms7002m_limelight_reset(&d->base.lmsstate); + res = res ? res : usleep(10); + + // REMOVE ME: fixup for now + // On spectrum analyzer signal looks intact but we get phase off in digital loopback + // looks like known LML 1-cycle off problem on LMS7002 chip, need to add + // reclocking, but leave it as is for now + if (g == 20) { + iqserrs = 0; + break; + } + + } else if (g > 0) { + // sometimes IQ err reports 0 while it's out of sync, check if LFSR still intact + + res = res ? res : xsdr_phy_en_lfsr_generator_mimo(d, true, true); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_en_lfsr_checker_mimo(d, true); + res = res ? res : usleep(100); + res = res ? res : xsdr_phy_lfsr_mimo_state(d, LFSR_CNTR_BER, errs); + + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "PHASE_TX=%2d ABIQ=%d [%6d/%6d/%6d/%6d] \n", ph - 1, iqserrs, + errs[0], errs[1], errs[2], errs[3]); + + if (d->dpump ? !noerrors_v2(errs, &badness) : !noerrors_v4(errs, &badness)) { + iqserrs = 500; + } else { + break; + } + + // REMOVE ME: fixup for now + if ((g > 18) && (badness < 100) && (d->s_txrate > 80e6)) + break; + } + } + + if ((rty == 0 && iqserrs != 0) || iqserrs > 40) { + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "PHASE_TX=%2d ABIQ=%d\n", ph - 1, iqserrs); + res = res ? res : xsdr_phy_en_lfsr_generator_mimo(d, true, true); + continue; + } + + tx_badness = badness; + tx_iqerrs = iqserrs; + goto phase_tx_calibrated; + } + + if (badness_m > badness) { + badness_m = badness; + phase_m = ph; + ph_ty_m = rty; + } + } + // Try to toggle clock inversion + res = res ? res : lms7002m_limelight_toggle_ntx(&d->base.lmsstate); + check_rx = true; + } + USDR_LL_LOG(dev, "XDEV", USDR_LOG_WARNING, "Restoring TX phase to %d (bandness=%" PRId64 ")\n", + phase_m, badness_m); + + // Try our best at least + res = res ? res : xsdr_configure_lml_mmcm_tx(d, mmcx_rx_path, d->lmlcal_rx_phase, phase_m, ph_ty_m); + + // Make sure it's a good value + res = res ? res : _xsdr_calibrate_txlfsr_check(d, check_to, errs, &iqserrs, &badness_m); + + // Check A/B & I/Q aligment is ok + res = res ? res : xsdr_phy_en_lfsr_generator_mimo(d, true, false); + res = res ? res : usleep(10); + res = res ? res : xsdr_phy_en_iqab_checker_mimo(d, true); + res = res ? res : usleep(1000); + res = res ? res : xsdr_phy_lfsr_mimo_state_s(d, LFSR_CNTR_IQS << 2, &iqserrs2); + if (res) + return res; + + tx_badness = badness_m; + tx_iqerrs = iqserrs2; + + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "RESTORE PHASE_TX=%2d [%6d/%6d/%6d/%6d - %6d - %6d] BD=%.3e\n", phase_m - 1, + errs[0], errs[1], errs[2], errs[3], iqserrs, iqserrs2, (double)badness_m); + + phase_tx_calibrated: + d->lmlcal_tx_phase = phase_m; + res = res ? res : xsdr_phy_en_lfsr_generator_mimo(d, false, false); + res = res ? res : xsdr_phy_en_lfsr_checker_mimo(d, false); + res = res ? res : lms7002m_set_lmlrx_mode(&d->base, XSDR_LMLRX_NORMAL); + + // Sometimes TxTSP can get off by 1TSP clock, we need to preventevly reset the path + res = res ? res : lms7002m_mac_set(&d->base.lmsstate, LMS7_CH_AB); + res = res ? res : lms7002m_xxtsp_bst(&d->base.lmsstate, LMS_TXTSP); + res = res ? res : lms7002m_xxtsp_bst(&d->base.lmsstate, LMS_RXTSP); + } + } else { + + unsigned dly = (d->tx_override_phase) ? (d->tx_override_phase - 1) : 3; + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_DLY_VALUE, dly); + } + +no_tx: + if (!old_rx_run[0] && !old_rx_run[1]) { + // No RX, disable it + // res = res ? res : lms7002m_streaming_down(&d->base, RFIC_LMS7_RX); + } else { + res = res ? res : lms7002m_set_lmlrx_mode(&d->base, XSDR_LMLRX_NORMAL); + } + + if (rx_badness || (!mmcm_rx_only_path && (tx_badness || tx_iqerrs))) { + bool severe = (rx_badness > 100) || (!mmcm_rx_only_path && (tx_badness > 100 || tx_iqerrs > 10)); + USDR_LL_LOG(dev, "XDEV", severe ? USDR_LOG_ERROR : USDR_LOG_WARNING, "LML Calibration failed: RX_BADNESS=%" PRId64 " TX_BADNESS=%" PRId64 " TX_IQERRS=%d\n", + rx_badness, mmcm_rx_only_path ? 0 : tx_badness, mmcm_rx_only_path ? 0 : tx_iqerrs); + + if (res == 0 && severe) + res = -ERANGE; + } + return res; +} + int xsdr_set_samplerate_ex(xsdr_dev_t *d, unsigned rxrate, unsigned txrate, unsigned adcclk, unsigned dacclk, unsigned flags) { - const unsigned l1_pid = (d->hwid) & 0x7; - const unsigned l2_pid = (d->hwid >> 4) & 0x7; - const bool lml1_rx_valid = (l1_pid == 3 || l1_pid == 4 || l1_pid == 5 || l1_pid == 6) || l1_pid == 7; - const bool lml2_rx_valid = (l2_pid == 3 || l2_pid == 4 || l2_pid == 5 || l2_pid == 6) || l2_pid == 7; - const unsigned rx_port = (lml2_rx_valid && !lml1_rx_valid) ? 2 : 1; - const unsigned rx_port_1 = (rx_port == 1); lldev_t dev = d->base.lmsstate.dev; subdev_t subdev = d->base.lmsstate.subdev; - unsigned sisosdrflag; + int res; + //if (!(((d->hwid) & 0xff) & PHY_CFG_VALID_MSK)) { + // USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "Incompatible firmware, please update to 20250501 at least!\n"); + // return -ENOTSUP; + //} + res = _xsdr_checkpwr(d); if (res) return res; @@ -287,20 +1042,26 @@ int xsdr_set_samplerate_ex(xsdr_dev_t *d, rxrate = 1e6; } - // TODO: Check if MMCM is present - bool mmcmrx = false; - unsigned m_flags = flags | ((d->siso_sdr_active_rx && d->hwchans_rx == 1) ? XSDR_LML_SISO_DDR_RX : 0) - | ((d->siso_sdr_active_tx && d->hwchans_tx == 1) ? XSDR_LML_SISO_DDR_TX : 0); - - res = lms7002m_samplerate(&d->base, rxrate, txrate, adcclk, dacclk, m_flags, rx_port_1); - if (res) - return res; + unsigned rx_dec = 1; + unsigned tx_inr = 1; + if (d->has_duc_ddc) { + // On UltraScale+ minimum MMCM frequency is 600/128 = 6.25 Mhz or 3.125MSPS + // With divider this extends to 98Khz + // Do x2 datarate for 30.72MSPS to use extended decimation / interpolation + const unsigned RATE_MIN = 32e6; + unsigned p = 0; + unsigned fpga_dxc[] = { 1, 2, 4, 8, 16, 32 }; + for (; p < SIZEOF_ARRAY(fpga_dxc) - 1; p++) { + if (rxrate && (rxrate * fpga_dxc[p] >= RATE_MIN)) + break; + if (txrate && (txrate * fpga_dxc[p] >= RATE_MIN)) + break; + } - d->s_rxrate = rxrate; - d->s_txrate = txrate; - d->s_adcclk = adcclk; - d->s_dacclk = dacclk; - d->s_flags = m_flags; + rx_dec = tx_inr = fpga_dxc[p]; + rxrate *= rx_dec; + txrate *= tx_inr; + } if (d->afe_active == false) { // Need AFE for reference cloking @@ -309,50 +1070,94 @@ int xsdr_set_samplerate_ex(xsdr_dev_t *d, // wait for clock to stabilize usleep(10000); + // We need RxTSP & TxTSP configured for proper LML - TSP alignment before LFSR training d->afe_active = true; } - if (mmcmrx) { - res = xsdr_configure_lml_mmcm(d); - if (res) - return res; - } + // flags |= XSDR_LML_EXT_FIFOCLK_RX | XSDR_LML_EXT_FIFOCLK_TX; + unsigned m_flags = flags | (((d->siso_sdr_active_rx && d->hwchans_rx == 1) || d->dpump) ? XSDR_LML_SISO_DDR_RX : 0) + | (((d->siso_sdr_active_tx && d->hwchans_tx == 1) || d->dpump) ? XSDR_LML_SISO_DDR_TX : 0); - sisosdrflag = d->base.lml_mode.rxsisoddr ? 8 : 0; - res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000007 | sisosdrflag); - usleep(100); - res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000000 | sisosdrflag); + res = lms7002m_samplerate(&d->base, rxrate, txrate, adcclk, dacclk, m_flags, d->rx_port_is_1, rx_dec, tx_inr); + if (res) + return res; + d->s_rxrate = rxrate; + d->s_txrate = txrate; + d->s_adcclk = adcclk; + d->s_dacclk = dacclk; + d->s_flags = m_flags; + d->cfg_srate_siso_rx = (m_flags & XSDR_LML_SISO_DDR_RX) ? 1 : 0; + d->cfg_srate_siso_tx = (m_flags & XSDR_LML_SISO_DDR_TX) ? 1 : 0; - if (mmcmrx) { - // Configure PHY (reset) - // TODO phase search - for (unsigned h = 0; h < 16; h++) { - res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000007 | sisosdrflag); - usleep(100); - res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000000 | sisosdrflag); + res = res ? res : _xsdr_calibrate_lml(d); + // if (rxrate) { + // if (!d->ssdr_pro) { + // // Switch to clock meas + // res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x02000000); + // } + // } - USDR_LOG("XDEV", USDR_LOG_INFO, "PHASE=%d\n", h); - res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x00000000); - unsigned tmp; //, tmp2; - for (unsigned k = 0; k < 100; k++) { - res = lowlevel_reg_rd32(dev, subdev, REG_CFG_PHY_0, &tmp); - } + if (d->has_duc_ddc && rxrate && d->s_rx_dec != rx_dec) { + // Optional RX DSP reset + dev_gpo_set(dev, IGPO_DSPCHAIN_RX_RST, 0xf); + usleep(10); + dev_gpo_set(dev, IGPO_DSPCHAIN_RX_RST, 0x2); + usleep(10); + res = (res) ? res : fgearbox_load_fir_ex(dev, IGPO_DSPCHAIN_RX_PRG, rx_dec, d->ssdr_pro ? DSP_USSERIES : DSP_7SERIES, 1); + usleep(10); + dev_gpo_set(dev, IGPO_DSPCHAIN_RX_RST, 0x0); + + d->s_rx_dec = rx_dec; + } - mmcm_set_digdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, CLKOUT_PORT_0, h); - } + if (d->has_duc_ddc && txrate && d->s_tx_int != tx_inr) { + // Optional TX DSP reset + usleep(10); + dev_gpo_set(dev, IGPO_DSPCHAIN_TX_RST, 0xf); + usleep(10); + dev_gpo_set(dev, IGPO_DSPCHAIN_TX_RST, 0x2); + usleep(10); + res = (res) ? res : fgearbox_load_fir_i_ex(dev, IGPO_DSPCHAIN_TX_PRG, tx_inr, d->ssdr_pro ? DSP_USSERIES : DSP_7SERIES, 1); + usleep(10); + dev_gpo_set(dev, IGPO_DSPCHAIN_TX_RST, 0x0); + + d->s_tx_int = tx_inr; } - // Switch to clock meas - res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x02000000); + // lms7002m_rxtsp_dc_corr(&d->base.lmsstate, true, 0); +/* + int32_t a, b; + int32_t q[4]; + xsdr_phy_dc_estim_accum(d, 255); + xsdr_phy_dc_estim_start(d, 1); + xsdr_phy_dc_estim_get(d, DC_ESTIM_GEN, &a); + + uint32_t cha[64], chb[64]; + lms7002m_mac_set(&d->base.lmsstate, LMS7_CH_AB); + lms7002m_xxtsp_gen(&d->base.lmsstate, LMS_RXTSP, XXTSP_TONE, 0, 1); + xsdr_phy_capture_start(d, 1); + usleep(100); + xsdr_phy_capture_start(d, 0); + xsdr_phy_capture_get(d, 0, 64, cha); + xsdr_phy_capture_get(d, 1, 64, chb); + lms7002m_xxtsp_gen(&d->base.lmsstate, LMS_RXTSP, XXTSP_NORMAL, 0, 0); - // uint32_t m; - // res = lowlevel_reg_rd32(dev, subdev, REG_CFG_PHY_0, &m); - // USDR_LOG("XDEV", USDR_LOG_INFO, "MEAS=%08x\n", m); + for (unsigned j = 0; j < 64; j++) { + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "%d: %08x %08x\n", j, cha[j], chb[j]); + } - return res; + xsdr_phy_dc_estim_get(d, DC_ESTIM_GEN, &b); + for (unsigned j = 0; j < 4; j++) { + xsdr_phy_dc_estim_get(d, DC_ESTIM_AI + j, &q[j]); + } + + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "%d -> %d: %08x %08x %08x %08x\n", + a, b, q[0], q[1], q[2], q[3]); +*/ + return res; } @@ -363,28 +1168,47 @@ int xsdr_clk_debug_info(xsdr_dev_t *d) unsigned crx, ctx, caux; int res = 0; + uint32_t dump[13]; + res = res ? res : dev_gpi_get32(dev, IGPI_MEAS_RXCLK, &crx); res = res ? res : dev_gpi_get32(dev, IGPI_MEAS_TXCLK, &ctx); res = res ? res : dev_gpi_get32(dev, IGPI_CLK1PPS, &caux); if (res) return res; - USDR_LOG("XDEV", USDR_LOG_WARNING, "PHY - RX %08x (%d) / TX %08x (%d) / AUX %08x (%d)\n", + if (!d->ssdr_pro) { + for (unsigned h = 0; h < 13; h++) { + unsigned p = ((h & 0x3) << 2) | (h >> 2); + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_LFSRCHK, p, 0); + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_LFSRCHK, p, 0); + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_LFSRCHK, p, 0); + res = res ? res : xsdr_phy_rx_reg(d, false, PHY_REG_RXBANK_LFSRCHK, p, 0); + + res = res ? res : lowlevel_reg_rd32(dev, 0, REG_CFG_PHY_0, &dump[h]); + } + } else { + memset(dump, 0, sizeof(dump)); + } + + + USDR_LL_LOG(dev, "XDEV", USDR_LOG_WARNING, "PHY - RX %08x (%d) / TX %08x (%d) / AUX %08x (%d) %d/%d/%d/%d %d/%d/%d/%d %d/%d/%d/%d -- %d \n", crx, crx & 0xfffffff, ctx, ctx & 0xfffffff, - caux, caux & 0xfffffff); + caux, caux & 0xfffffff, + dump[0], dump[1], dump[2], dump[3], dump[4], dump[5], dump[6], dump[7], dump[8], dump[9], dump[10], dump[11], dump[12] + ); return res; } int xsdr_set_rx_port_switch(xsdr_dev_t *d, unsigned path) { - USDR_LOG("XDEV", USDR_LOG_INFO, "RXSW:%d\n", path); + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "RXSW:%d\n", path); return dev_gpo_set(d->base.lmsstate.dev, IGPO_RXSW, path); } int xsdr_set_tx_port_switch(xsdr_dev_t *d, unsigned path) { - USDR_LOG("XDEV", USDR_LOG_INFO, "TXSW:%d\n", path); + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "TXSW:%d\n", path); return dev_gpo_set(d->base.lmsstate.dev, IGPO_TXSW, path); } @@ -434,11 +1258,6 @@ const lms7002m_lml_map_t lms7nfe_get_lml_portcfg(bool rx, unsigned chs, unsigned }; static const lms7002m_lml_map_t diqarray_tx[] = { - // MIMO modes - // {{ LML_BQ, LML_BI, LML_AQ, LML_AI }}, - // {{ LML_BI, LML_BQ, LML_AI, LML_AQ }}, - // {{ LML_AQ, LML_AI, LML_BQ, LML_BI }}, - // {{ LML_AI, LML_AQ, LML_BI, LML_BQ }}, {{ LML_AQ, LML_AI, LML_BQ, LML_BI }}, {{ LML_AI, LML_AQ, LML_BI, LML_BQ }}, {{ LML_BQ, LML_BI, LML_AQ, LML_AI }}, @@ -452,37 +1271,6 @@ const lms7002m_lml_map_t lms7nfe_get_lml_portcfg(bool rx, unsigned chs, unsigned }; -#if 0 - static const lms7002m_lml_map_t diqarray_tx[] = { - // MIMO modes - {{ LML_BI, LML_AI, LML_BQ, LML_AQ }}, - {{ LML_BQ, LML_AQ, LML_BI, LML_AI }}, - {{ LML_AI, LML_BI, LML_AQ, LML_BQ }}, - {{ LML_AQ, LML_BQ, LML_AI, LML_BI }}, - - {{ LML_BI, LML_AI, LML_BQ, LML_AQ }}, - {{ LML_BQ, LML_AQ, LML_BI, LML_AI }}, - {{ LML_AI, LML_BI, LML_AQ, LML_BQ }}, - {{ LML_AQ, LML_BQ, LML_AI, LML_BI }}, - // SISO modes - {{ LML_AI, LML_AI, LML_AQ, LML_AQ }}, - {{ LML_AQ, LML_AQ, LML_AI, LML_AI }}, - {{ LML_BI, LML_BI, LML_BQ, LML_BQ }}, - {{ LML_BQ, LML_BQ, LML_BI, LML_BI }}, - // MIMO test modes (swap IQ_B) - {{ LML_BQ, LML_AI, LML_BI, LML_AQ }}, - {{ LML_BI, LML_AQ, LML_BQ, LML_AI }}, - {{ LML_AQ, LML_BI, LML_AI, LML_BQ }}, - {{ LML_AI, LML_BQ, LML_AQ, LML_BI }}, - // MIMO test modes (swap IQ_A) - {{ LML_BI, LML_AQ, LML_BQ, LML_AI }}, - {{ LML_BQ, LML_AI, LML_BI, LML_AQ }}, - {{ LML_AI, LML_BQ, LML_AQ, LML_BI }}, - {{ LML_AQ, LML_BI, LML_AI, LML_BQ }}, - }; - - const lms7002m_lml_map_t *diqarray = (rx) ? diqarray_rx : diqarray_tx; -#endif const lms7002m_lml_map_t *diqarray = (rx) ? diqarray_rx : diqarray_tx;; unsigned diqidx = 0; if (flags & RFIC_SWAP_IQ) @@ -499,69 +1287,6 @@ const lms7002m_lml_map_t lms7nfe_get_lml_portcfg(bool rx, unsigned chs, unsigned return diqarray[diqidx]; } -#if 0 //unused, DO NOT DELETE -static -const lms7002m_lml_map_t lms7nfe_get_lml_portcfg_o(unsigned chs, unsigned flags, bool no_siso_map) -{ -#if 0 - static const struct lml_map diqarray[] = { - // MIMO modes - {{ LML_AI, LML_BI, LML_AQ, LML_BQ }}, - {{ LML_AQ, LML_BQ, LML_AI, LML_BI }}, - {{ LML_BI, LML_AI, LML_BQ, LML_AQ }}, - {{ LML_BQ, LML_AQ, LML_BI, LML_AI }}, - - // SISO modes - {{ LML_AI, LML_AQ, LML_AI, LML_AQ }}, - {{ LML_AQ, LML_AI, LML_AQ, LML_AI }}, - {{ LML_BI, LML_BQ, LML_BI, LML_BQ }}, - {{ LML_BQ, LML_BI, LML_BQ, LML_BI }}, - }; - -#endif - static const lms7002m_lml_map_t diqarray[16] = { - // MIMO modes - {{ LML_BI, LML_AI, LML_BQ, LML_AQ }}, - {{ LML_BQ, LML_AQ, LML_BI, LML_AI }}, - {{ LML_AI, LML_BI, LML_AQ, LML_BQ }}, - {{ LML_AQ, LML_BQ, LML_AI, LML_BI }}, - // SISO modes - {{ LML_AI, LML_AI, LML_AQ, LML_AQ }}, - {{ LML_AQ, LML_AQ, LML_AI, LML_AI }}, - {{ LML_BI, LML_BI, LML_BQ, LML_BQ }}, - {{ LML_BQ, LML_BQ, LML_BI, LML_BI }}, - // MIMO test modes (swap IQ_B) - {{ LML_BQ, LML_AI, LML_BI, LML_AQ }}, - {{ LML_BI, LML_AQ, LML_BQ, LML_AI }}, - {{ LML_AQ, LML_BI, LML_AI, LML_BQ }}, - {{ LML_AI, LML_BQ, LML_AQ, LML_BI }}, - // MIMO test modes (swap IQ_A) - {{ LML_BI, LML_AQ, LML_BQ, LML_AI }}, - {{ LML_BQ, LML_AI, LML_BI, LML_AQ }}, - {{ LML_AI, LML_BQ, LML_AQ, LML_BI }}, - {{ LML_AQ, LML_BI, LML_AI, LML_BQ }}, - }; - - unsigned diqidx = 0; - if (flags & RFIC_SWAP_IQ) - diqidx |= 1; - - if (_xsdr_run_params_stream_is_swap(chs, flags)) - diqidx |= 2; - - if (!no_siso_map && !_xsdr_run_params_stream_is_mimo(chs, flags)) - diqidx |= 4; -// else if (flags & RFIC_SWAP_IQB) -// diqidx |= 8; -// else if (flags & RFIC_SWAP_IQA) -// diqidx |= 12; - - USDR_LOG("XDEV", USDR_LOG_WARNING, "diqidx=%d\n", diqidx); - assert(diqidx < (sizeof(diqarray)/sizeof(diqarray[0]))); - return diqarray[diqidx]; -} -#endif - int xsdr_rfic_streaming_xflags(xsdr_dev_t *d, unsigned xor_rx_flags, unsigned xor_tx_flags) @@ -591,7 +1316,12 @@ int xsdr_rfic_streaming_up(xsdr_dev_t *d, unsigned dir, unsigned rx_chs, unsigned rx_flags, unsigned tx_chs, unsigned tx_flags) { - return lms7002m_streaming_up(&d->base, dir, (lms7002m_mac_mode_t)rx_chs, rx_flags, (lms7002m_mac_mode_t)tx_chs, tx_flags); + int res = lms7002m_streaming_up(&d->base, dir, (lms7002m_mac_mode_t)rx_chs, rx_flags, (lms7002m_mac_mode_t)tx_chs, tx_flags); + if (res) + return res; + + d->afe_active = true; + return 0; } @@ -628,32 +1358,148 @@ int xsdr_rfic_set_gain(xsdr_dev_t *d, return lms7002m_set_gain(&d->base, channel, gain_type, gain, actualgain); } +enum { + LMS8_TXA_CHIDX = 0, + LMS8_TXB_CHIDX = 1, + LMS8_RXA_CHIDX = 2, + LMS8_RXB_CHIDX = 3, +}; + int xsdr_rfic_fe_set_freq(xsdr_dev_t *d, unsigned channel, unsigned type, double freq, double *actualfreq) { - if (d->ssdr && freq > 3.7e9) { - int res = 0; - d->lms7_lob = 1.01e9; + int res = 0; + double original = freq; + double actual_lms7; + double actual_lms8 = 0; + int64_t lms8_freq = 0; + uint64_t other_freq = (type == RFIC_LMS7_TUNE_RX_FDD) ? d->freq_txlo : d->freq_rxlo; + bool current_changed = false; + bool other_active = (other_freq > d->lms8_switchover_freq) && ((type == RFIC_LMS7_TUNE_RX_FDD) ? + (d->base.tx_run[0] || d->base.tx_run[1]) : + (d->base.rx_run[0] || d->base.rx_run[1])); + if (d->ssdr && freq > d->lms8_switchover_freq) { + float bwef = d->lms8st_bwef_1000 / 1000.0; + unsigned lob = (d->lms7_lob == 0) ? 2.01e9 : d->lms7_lob; + unsigned pwr_msk = + (d->base.tx_run[0] ? 1 << LMS8_TXA_CHIDX : 0) | + (d->base.tx_run[1] ? 1 << LMS8_TXB_CHIDX : 0) | + (d->base.rx_run[0] ? 1 << LMS8_RXA_CHIDX : 0) | + (d->base.rx_run[1] ? 1 << LMS8_RXB_CHIDX : 0); + + if (other_active) { + double delta = fabs(original - other_freq); + if (delta > 1.5e9) { + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_ERROR, "Both TX & RX path use high band, however RX and TX freqs are %.3f Mhz apart! sSDR use shared LO for both RX & TX: either reduce delta or use different bands\n", + delta / 1e6); + return -EINVAL; + } + } - res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS8_CTRL, 0x81); - res = res ? res : lms8001_tune(&d->lms8, d->base.fref, freq - d->lms7_lob); + if (d->lms8_int_mode) { + if (other_active) { + lms8_freq = (((freq + other_freq) / 2) - lob + d->base.fref / 2) / d->base.fref; + } else { + lms8_freq = (freq - lob + d->base.fref / 2) / d->base.fref; + } + lms8_freq *= d->base.fref; + } else { + if (other_active) { + lms8_freq = ((freq + other_freq) / 2) - lob; + } else { + lms8_freq = freq - lob; + } + } - dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS8_CTRL, 0x80); - if (res) - return res; + lob = freq - lms8_freq; + if (d->lms8_lo_freq != lms8_freq) { + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS8_CTRL, 0x81); + + if (d->lms8_mode_b) { + res = res ? res : lms8001b_hlmix_loss_set(&d->lms8, LMS8_TXA_CHIDX, d->base.tx_run[0] ? 0 : 0xf); + res = res ? res : lms8001b_hlmix_loss_set(&d->lms8, LMS8_TXB_CHIDX, d->base.tx_run[1] ? 0 : 0xf); + res = res ? res : lms8001b_hlmix_loss_set(&d->lms8, LMS8_RXA_CHIDX, d->base.rx_run[0] ? 0 : 0xf); + res = res ? res : lms8001b_hlmix_loss_set(&d->lms8, LMS8_RXB_CHIDX, d->base.rx_run[1] ? 0 : 0xf); + } else { + res = res ? res : lms8001a_ch_lna_pa_set(&d->lms8, LMS8_TXA_CHIDX, d->base.tx_run[0] ? 0 : ~0, d->base.tx_run[0] ? 0 : ~0); + res = res ? res : lms8001a_ch_lna_pa_set(&d->lms8, LMS8_TXB_CHIDX, d->base.tx_run[1] ? 0 : ~0, d->base.tx_run[1] ? 0 : ~0); + res = res ? res : lms8001a_ch_lna_pa_set(&d->lms8, LMS8_RXA_CHIDX, d->base.rx_run[0] ? 0 : ~0, d->base.rx_run[0] ? 0 : ~0); + res = res ? res : lms8001a_ch_lna_pa_set(&d->lms8, LMS8_RXB_CHIDX, d->base.rx_run[1] ? 0 : ~0, d->base.rx_run[1] ? 0 : ~0); + } - USDR_LOG("XDEV", USDR_LOG_INFO, "Setting FREQ %.3f Mhz, LNB %.3f Mhz\n", freq / 1.0e6, d->lms7_lob / 1.0e6); - freq = d->lms7_lob; - } else { - d->lms7_lob = 0; + if (!d->ssdr_pro) { + res = res ? res : lms8001_core_enable(&d->lms8, 1, 1, 1); + } else { + res = res ? res : lms8001_core_enable(&d->lms8, 0, 0, 0); + } + res = res ? res : lms8001_ch_enable(&d->lms8, pwr_msk); + + res = res ? res : lms8001_smart_tune(&d->lms8, 0, freq - lob, d->base.fref, + d->lms8st_loopbw, d->lms8st_phasemargin, bwef, d->lms8st_flock_n); + + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS8_CTRL, 0x80); + if (res) + return res; + + d->lms8_lo_freq = freq - lob; + } + + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "Setting FREQ %.3f Mhz, LNB %.3f Mhz\n", freq / 1.0e6, lob / 1.0e6); + freq = lob; + actual_lms8 = d->lms8_lo_freq; + current_changed = true; + } + + if (type == RFIC_LMS7_TUNE_RX_FDD && d->lms7_rxlo_last == freq) + return 0; + if (type == RFIC_LMS7_TUNE_TX_FDD && d->lms7_txlo_last == freq) + return 0; + if (type == RFIC_LMS7_TUNE_RX_FDD) + d->freq_rxlo = original; + if (type == RFIC_LMS7_TUNE_TX_FDD) + d->freq_txlo = original; + + res = lms7002m_fe_set_freq(&d->base, channel, type, freq, &actual_lms7); + if (type == RFIC_LMS7_TUNE_RX_FDD) { + d->lms7_rxlo_last = (res == 0) ? freq : 0; + } else if (type == RFIC_LMS7_TUNE_TX_FDD) { + d->lms7_txlo_last = (res == 0) ? freq : 0; + } + + if (res == 0 && other_active && current_changed) { + unsigned ntype = (type == RFIC_LMS7_TUNE_RX_FDD) ? RFIC_LMS7_TUNE_TX_FDD : RFIC_LMS7_TUNE_RX_FDD; + double nfreq = other_freq - lms8_freq; + res = lms7002m_fe_set_freq(&d->base, channel, ntype, nfreq, NULL); + + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "Freq configuration updated: RX_LO=%.3f TX_LO=%.3f LMS8_LO=%.3f\n", + d->base.rx_lo / 1e6, d->base.tx_lo / 1e6, lms8_freq / 1e6); + } + + if (res == 0 && actualfreq) { + *actualfreq = actual_lms8 + actual_lms7; } - return lms7002m_fe_set_freq(&d->base, channel, type, freq, actualfreq); + // LO correction + res = res ? res : lms7002m_dc_corr_en(&d->base.lmsstate, d->base.rx_run[0], d->base.rx_run[1], d->base.tx_run[0], d->base.tx_run[1]); + return res; } +int xsdr_on_change_signal(lms7002_dev_t *dev, enum sigtype t) +{ + xsdr_dev_t *d = (xsdr_dev_t *)(dev); + switch (t) { + case XSDR_RX_LO_CHANGED: + case XSDR_RX_LNA_CHANGED: + return (d->freq_rxlo > d->lms8_switchover_freq) ? 1 : 0; + case XSDR_TX_LO_CHANGED: + case XSDR_TX_LNA_CHANGED: + return (d->freq_txlo > d->lms8_switchover_freq) ? 1 : 0; + }; + return 0; +} int xsdr_rfic_rfe_set_path(xsdr_dev_t *d, unsigned path) @@ -676,12 +1522,9 @@ int xsdr_rfic_fe_set_lna(xsdr_dev_t *d, int xsdr_tx_antennat_port_cfg(xsdr_dev_t *d, unsigned mask) { -#if 0 - lldev_t dev = d->base.lmsstate.dev; - unsigned subdev = 0; int res = 0; - d->dsp_txcfg = mask; + // 2 - mute_a // 1 - mute_b @@ -694,11 +1537,7 @@ int xsdr_tx_antennat_port_cfg(xsdr_dev_t *d, unsigned mask) res = res ? res : lms7002m_mac_set(&d->base.lmsstate, LMS7_CH_B); res = res ? res : lms7002m_trf_path(&d->base.lmsstate, TRF_MUTE, TRF_MODE_NORMAL); } - - res = res ? res : lowlevel_reg_wr32(dev, subdev, M2PCI_REG_WR_TXDMA_COMB, (1 << 11) | ((mask & 7) << 8) | 1); return res; -#endif - return -ENOTSUP; } @@ -710,14 +1549,29 @@ int xsdr_ctor(lldev_t dev, xsdr_dev_t *d) d->base.on_ant_port_sw = &_xsdr_antenna_port_switch; d->base.on_get_lml_portcfg = &lms7nfe_get_lml_portcfg; + d->lms8_rx_f_switchover = 3.5e9; + d->lms8_tx_f_switchover = 3.5e9; + + d->lms8st_loopbw = 300000; + d->lms8st_phasemargin = 50; + d->lms8st_bwef_1000 = 2000; + d->lms8st_flock_n = 100; + d->lms8st_iq_gen = 0; + d->lms8st_int_mod = 0; + d->lms8st_enabled = 1; + + // Use integer mode for LMS8001 by default + d->lms8_int_mode = true; + d->lms8_switchover_freq = 3e9; return 0; } int _xsdr_init_revx(xsdr_dev_t *d, unsigned hwid) { lldev_t dev = d->base.lmsstate.dev; + unsigned hwid_rev = (d->hwid >> 8) & 0xff; unsigned subdev = 0; - int res; + int res = 0; bool pg = false; enum tx_switch_cfg { @@ -765,7 +1619,7 @@ int _xsdr_init_revx(xsdr_dev_t *d, unsigned hwid) strncpy(d->base.cfg_auto_tx[1].name0, "H", sizeof(d->base.cfg_auto_tx[1].name0)); strncpy(d->base.cfg_auto_tx[1].name1, "B2", sizeof(d->base.cfg_auto_tx[1].name1)); - if (hwid == SSDR_DEV) { + if (hwid == SSDR_DEV || hwid == SSDRPRO_DEV) { // QPC8019Q 0: RFC1 -- HF; 1: RFC2 -- LF d->base.cfg_auto_rx[0].stop_freq = 3000e6; @@ -778,37 +1632,52 @@ int _xsdr_init_revx(xsdr_dev_t *d, unsigned hwid) d->base.cfg_auto_rx[1].swlb = 1; } - res = dev_gpo_set(dev, IGPO_LMS_PWR, 0); + res = res ? res : dev_gpo_set(dev, IGPO_LMS_PWR, 0x80); + res = res ? res : dev_gpo_set(dev, IGPO_LMS_PWR, 0x00); if (res) return res; + if (getenv("USDR_BARE_DEV")) { + return 0; + } + uint16_t rev = 0xffff; res = lp8758_get_rev(dev, subdev, I2C_BUS_LP8758_FPGA, &rev); if (res) return res; - if (rev == 0xe001) { + bool good_pmic = (hwid == SSDRPRO_DEV) ? (rev == 0xe302) : (rev == 0xe001); + if (good_pmic) { d->pmic_ch145_valid = true; } - USDR_LOG("XDEV", USDR_LOG_INFO, "PMIC_RFIC ver %04x (%d)\n", + USDR_LL_LOG(dev, "XDEV", (good_pmic) ? USDR_LOG_INFO : USDR_LOG_ERROR, "PMIC_RFIC ver %04x (%d)\n", rev, d->pmic_ch145_valid); - if (hwid == SSDR_DEV) { + if (hwid == SSDRPRO_DEV) { + res = lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 3, 2040); + } else if (hwid == SSDR_DEV) { res = lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 1, 2040); } else { // TODO check if we need this rail res = lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 1, 1480); } - // LMS Vcore boost to 1.25V - res = res ? res : lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 2, 1260); - res = res ? res : lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 3, 1850); + if (hwid == SSDRPRO_DEV) { + // TODO adjust VCORE to 0.88V + // res = res ? res : lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 0, 880); + res = res ? res : lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 1, 1280); + res = res ? res : lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 2, 1850); + } else { + // LMS Vcore boost to 1.25V + res = res ? res : lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 2, 1280); + res = res ? res : lp8758_vout_set(dev, subdev, I2C_BUS_LP8758_FPGA, 3, 1850); + } - res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 0, 1, 1); //1v0 -- less affected - res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 1, 1, 1); //2v5 -- less affected - res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 2, 1, 1); //1v2 - res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 3, 1, 1); //1v8 + res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 0, 1, 1); //1v0 | 0v9 -- less affected + res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 1, 1, 1); //2v5 | 1v2 -- less affected + res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 2, 1, 1); //1v2 | 1v8 + res = res ? res : lp8758_vout_ctrl(dev, subdev, I2C_BUS_LP8758_FPGA, 3, 1, 1); //1v8 | 2v7 // Wait for power to settle for (unsigned i = 0; !res && !pg && (i < 100); i++) { @@ -817,41 +1686,80 @@ int _xsdr_init_revx(xsdr_dev_t *d, unsigned hwid) } if (!pg) { - USDR_LOG("XDEV", USDR_LOG_INFO, "Couldn't set PMIC voltages!\n"); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "Couldn't set PMIC voltages!\n"); return -EIO; } + d->lms8_alive = false; // Enable internal clocking by default res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_CLK_CFG, 1); - if (hwid == SSDR_DEV) { + if (hwid == SSDR_DEV || hwid == SSDRPRO_DEV) { uint32_t chipver = ~0; + unsigned lms8_step = LMS8_MPW2024; + unsigned ssdr_rev; + + if (hwid == SSDR_DEV && hwid_rev == 0xff) { + lms8_step = LMS8_MPW2015; + d->lms8_mode_b = true; + ssdr_rev = 0; + } else if (hwid == SSDR_DEV && hwid_rev == 0x00) { + // This revision can be with A and B chips, need to set SSDR_LMS8B enviroment for B variant + ssdr_rev = 2; // Technically it's 1 but we use 2 in all documentation + } else { + ssdr_rev = 3; + } + + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "sSDR Rev%d\n", ssdr_rev); + + // Override chip settings if LMS8 was reworked to non-stadard + if (getenv("LMS8_MPW2015")) { + lms8_step = LMS8_MPW2015; + } + if (getenv("SSDR_LMS8B")) { + d->lms8_mode_b = true; + } // Check LMS8 presence - res = res ? res : dev_gpo_set(dev, IGPO_LMS8_CTRL, 0x0); + res = res ? res : dev_gpo_set(dev, IGPO_LMS8_CTRL, 0x00); res = res ? res : dev_gpo_set(dev, IGPO_LDOLMS_EN, 1); // Enable LDOs res = res ? res : dev_gpo_set(dev, IGPO_LMS_PWR, 9); // LMS usleep(100000); res = res ? res : lowlevel_spi_tr32(dev, d->base.lmsstate.subdev, 0, 0x002F0000, &chipver); - USDR_LOG("XDEV", USDR_LOG_INFO, "LMS7002 version %08x\n", chipver); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "LMS7002 version %08x\n", chipver); - res = res ? res : dev_gpo_set(dev, IGPO_LMS8_CTRL, 0x81); - usleep(100000); + for (unsigned j = 0; j < 5; j++) { + res = res ? res : dev_gpo_set(dev, IGPO_LMS8_CTRL, 0x81); + usleep(100000); - res = res ? res : lowlevel_spi_tr32(dev, d->base.lmsstate.subdev, 0, 0x800000ff, &chipver); - res = res ? res : lowlevel_spi_tr32(dev, d->base.lmsstate.subdev, 0, 0x000f0000, &chipver); - USDR_LOG("XDEV", USDR_LOG_INFO, "LMS8001 version %08x\n", chipver); + res = res ? res : lowlevel_spi_tr32(dev, d->base.lmsstate.subdev, 0, 0x800000ff, &chipver); + res = res ? res : lowlevel_spi_tr32(dev, d->base.lmsstate.subdev, 0, 0x000f0000, &chipver); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "LMS8001 version %08x, assume chip is LMS8001%c-MPW%d\n", + chipver, d->lms8_mode_b ? 'B' : 'A', lms8_step == LMS8_MPW2015 ? 2015 : 2024); - res = res ? res : lms8001_create(dev, d->base.lmsstate.subdev, 0, &d->lms8); + res = res ? res : lms8001_create(dev, d->base.lmsstate.subdev, 0, lms8_step, &d->lms8); + res = res ? res : dev_gpo_set(dev, IGPO_LMS8_CTRL, 0x80); - res = res ? res : dev_gpo_set(dev, IGPO_LMS8_CTRL, 0x80); - // res = res ? res : dev_gpo_set(dev, IGPO_LDOLMS_EN, 0); // Enable LDOs - // res = res ? res : dev_gpo_set(dev, IGPO_LMS_PWR, 0); + if (chipver != 0x00004040) { + usleep(100000); + } else { + d->lms8_alive = true; + break; + } + } - if (chipver != 0x00004040) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "LMS8001 not detected!\n"); + if (!getenv("USDR_BARE_DEV") && (!d->lms8_alive)) { + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "LMS8001 not detected, check the board!\n"); + return -EFAULT; } + if (hwid == SSDRPRO_DEV) { + uint8_t s[16] = { 0, }; + res = res ? res : at24_saddr_mem_get(dev, d->base.lmsstate.subdev, I2C_DEV_AT24_SEC, AT24_SECURE_SERIAL_OFF, 16, s); + + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "AT24_SERIAL: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]); + } } return res; } @@ -917,6 +1825,10 @@ int _xsdr_init_revo(xsdr_dev_t *d) strncpy(d->base.cfg_auto_tx[1].name0, "H", sizeof(d->base.cfg_auto_tx[1].name0)); strncpy(d->base.cfg_auto_tx[1].name1, "B1", sizeof(d->base.cfg_auto_tx[1].name1)); + + // Reset + res = res ? res : dev_gpo_set(dev, IGPO_LMS_PWR, 0x80); + // Set external GPIOs to 3.3V res = res ? res : dev_gpo_set(dev, IGPO_IOVCCSEL, 1); // Take control of second I2C bus @@ -927,7 +1839,7 @@ int _xsdr_init_revo(xsdr_dev_t *d) if (res) return res; - USDR_LOG("XDEV", USDR_LOG_INFO, "PMIC_LMS7 ver %04x\n", rev); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "PMIC_LMS7 ver %04x\n", rev); if (rev != 0xe001) { return -EIO; } @@ -956,7 +1868,7 @@ int _xsdr_init_revo(xsdr_dev_t *d) usleep(1000); } if (!bpg) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "PMIC_LMS7: couldn't set LMS7 volatges, giving up!\n"); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "PMIC_LMS7: couldn't set LMS7 volatges, giving up!\n"); return -EIO; } @@ -968,7 +1880,7 @@ int _xsdr_init_revo(xsdr_dev_t *d) if (res) return res; - USDR_LOG("XDEV", USDR_LOG_INFO, "PMIC_RFIC ver %04x\n", rev); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "PMIC_RFIC ver %04x\n", rev); if (rev != 0xe001) { return -EIO; } @@ -986,7 +1898,7 @@ int _xsdr_init_revo(xsdr_dev_t *d) if (res) return res; - USDR_LOG("XDEV", USDR_LOG_INFO, "DAC_ID=%x\n", devid); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "DAC_ID=%x\n", devid); switch (devid) { case 0x0194: case 0x0195: @@ -1010,7 +1922,7 @@ int _xsdr_init_revo(xsdr_dev_t *d) return res; d->dac_old_r5 = true; - USDR_LOG("XDEV", USDR_LOG_INFO, "Detected r5\n"); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "Detected r5\n"); return 0; rev4_check: @@ -1023,7 +1935,7 @@ int _xsdr_init_revo(xsdr_dev_t *d) return res; if (cfg == 0xdeadbeef) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "MCP Config = %08x\n", cfg); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "MCP Config = %08x\n", cfg); } // unsigned q, r; @@ -1043,7 +1955,7 @@ int _xsdr_init_revo(xsdr_dev_t *d) // } d->dac_old_r5 = false; - USDR_LOG("XDEV", USDR_LOG_INFO, "Detected r4\n"); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "Detected r4\n"); return 0; } @@ -1053,16 +1965,25 @@ int _xsdr_pwren_revx(xsdr_dev_t *d, bool on) int res; lldev_t dev = d->base.lmsstate.dev; - USDR_LOG("XDEV", USDR_LOG_INFO, "RFIC PWR:%d\n", on); - if (on && !d->pmic_ch145_valid) + USDR_LL_LOG(dev, "XDEV", (on && !d->pmic_ch145_valid) ? USDR_LOG_ERROR : USDR_LOG_INFO, + "RFIC PWR:%d CH145:%d\n", on, d->pmic_ch145_valid); + if (on && !d->pmic_ch145_valid) { + // 1V45 is cricial for Rev0 XSDR and can be ignored in Rev2 + int id = -1; + res = xsdr_gettemp_id(d, &id); + if (res == 0) { + USDR_LL_LOG(dev, "XDEV", USDR_LOG_WARNING, "TEMP ID: %04x\n", id); + } + return -EIO; + } res = dev_gpo_set(dev, IGPO_LDOLMS_EN, on ? 1 : 0); // Enable LDOs if (res) return res; if (d->ssdr) { - // Haevy load on 1.8VA + // Heavy load on 1.8VA usleep(100000); } usleep(1000); @@ -1075,12 +1996,45 @@ int _xsdr_pwren_revo(xsdr_dev_t *d, bool on) return 0; } +int xsdr_set_lms125vdd(xsdr_dev_t *d, unsigned vdd_mv) +{ + if (d->new_rev /* && !d->ssdr */) { + return lp8758_vout_set(d->base.lmsstate.dev, d->base.lmsstate.subdev, I2C_BUS_LP8758_FPGA, + d->ssdr_pro ? 1 : 2, vdd_mv); + } + + return -EINVAL; +} + +int xsdr_set_vio(xsdr_dev_t *d, unsigned vio_mv) +{ + + if (!d->new_rev) { + if (vio_mv > 3300) + vio_mv = 3300; + else if (vio_mv < 1600) + vio_mv = 1600; + + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_WARNING, "VIO set to %d mV\n", vio_mv); + return lp8758_vout_set(d->base.lmsstate.dev, d->base.lmsstate.subdev, I2C_BUS_LP8758_FPGA, 1, vio_mv); + } + + if (vio_mv > 2100) + vio_mv = 2100; + else if (vio_mv < 1600) + vio_mv = 1600; + + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_WARNING, "VIO set to %d mV\n", vio_mv); + return lp8758_vout_set(d->base.lmsstate.dev, d->base.lmsstate.subdev, I2C_BUS_LP8758_FPGA, + d->ssdr_pro ? 2 : 1, vio_mv); +} + int xsdr_pwren(xsdr_dev_t *d, bool on) { int res; lldev_t dev = d->base.lmsstate.dev; - res = dev_gpo_set(dev, IGPO_LMS_PWR, 0); //Disble, put into reset + res = dev_gpo_set(dev, IGPO_LMS_PWR, 0); //Disable, put into reset if (res) return res; usleep(5000); @@ -1129,25 +2083,46 @@ int xsdr_init(xsdr_dev_t *d) return res; hwcfg_devid = (hwid >> 16) & 0xff; - USDR_LOG("XDEV", USDR_LOG_ERROR, "HWID %08x\n", hwid); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "HWID %08x\n", hwid); + + const uint8_t phycfg_id = hwid & 0xff; + const bool rx_port_is_1 = ((phycfg_id & PHY_CFG_LML2_IS_RX) != PHY_CFG_LML2_IS_RX); + const bool tx_mmcm = ((phycfg_id & PHY_CFG_TX_MMCM) == PHY_CFG_TX_MMCM); + const bool rx_mmcm = ((phycfg_id & PHY_CFG_RX_MMCM) == PHY_CFG_RX_MMCM); + const bool mmcm_single = ((phycfg_id & PHY_CFG_SINGLE_MMCM) == PHY_CFG_SINGLE_MMCM); + const bool sep_clkdiv = ((phycfg_id & PHY_CFG_SEP_CLKDIV_MSK) == PHY_CFG_SEP_CLKDIV_MSK); + const bool has_duc_ddc = ((phycfg_id & PHY_CFG_HAS_DUC_DDC) == PHY_CFG_HAS_DUC_DDC); d->hwid = hwid; d->hwchans_rx = 2; // Defaults to MIMO; d->hwchans_tx = 2; // Defaults to MIMO; d->siso_sdr_active_rx = false; d->siso_sdr_active_tx = false; + d->rx_port_is_1 = rx_port_is_1; + d->mmcm_rx = rx_mmcm; + d->mmcm_tx = tx_mmcm; + d->mmcm_single = mmcm_single; + d->sep_clkdiv = sep_clkdiv; + d->cfg_srate_siso_rx = 0; + d->cfg_srate_siso_tx = 0; + d->dpump = false; + d->ssdr_pro = false; + d->xilinx_usp = false; + d->lms8_mode_b = false; + d->has_duc_ddc = has_duc_ddc; res = lms7002m_init(&d->base, dev, 0, XSDR_INT_REFCLK); - if (res) + if (res) { return res; - + } switch (hwcfg_devid) { case XSDR_DEV: d->new_rev = true; d->ssdr = false; break; case XTRX_DEV: d->new_rev = false; d->ssdr = false; break; case SSDR_DEV: d->new_rev = true; d->ssdr = true; break; + case SSDRPRO_DEV: d->new_rev = true; d->ssdr = true; d->ssdr_pro = true; d->xilinx_usp = true; break; default: - USDR_LOG("XDEV", USDR_LOG_ERROR, "unsupported hwcfg_devid=%02x\n", hwcfg_devid); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_ERROR, "unsupported hwcfg_devid=%02x\n", hwcfg_devid); if (getenv("XSDR_FORCE")) { d->new_rev = true; @@ -1156,6 +2131,10 @@ int xsdr_init(xsdr_dev_t *d) } } + if (d->ssdr) { + d->base.on_custom_signal = &xsdr_on_change_signal; + } + res = (d->new_rev) ? _xsdr_init_revx(d, hwcfg_devid) : _xsdr_init_revo(d); if (res) return res; @@ -1184,16 +2163,31 @@ int xsdr_dtor(xsdr_dev_t *d) res = (res) ? res : xsdr_rfic_streaming_down(d, RFIC_LMS7_RX | RFIC_LMS7_TX); res = (res) ? res : lms7002m_destroy(&d->base.lmsstate); } + + if (d->ssdr && d->lms8.dev) { + // Turn off LMS8 + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS8_CTRL, 0x81); + res = res ? res : lms8001_core_enable(&d->lms8, 0, 0, 0); + res = res ? res : lms8001_ch_enable(&d->lms8, 0); + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS8_CTRL, 0x80); + } + res = (res) ? res : dev_gpo_set(dev, IGPO_LMS_PWR, 0); res = (res) ? res : dev_gpo_set(dev, IGPO_LDOLMS_EN, 0); res = (res) ? res : dev_gpo_set(dev, IGPO_LED, 0); - if (d->ssdr) { + res = (res) ? res : _xsdr_mmcm_pd(d); + + // Set LMS8 power to 0.9V + if (d->ssdr_pro) { + res = res ? res : lp8758_vout_set(dev, d->base.lmsstate.subdev, I2C_BUS_LP8758_FPGA, 3, 900); + res = res ? res : lp8758_vout_ctrl(dev, d->base.lmsstate.subdev, I2C_BUS_LP8758_FPGA, 3, 0, 1); + } else if (d->ssdr) { res = res ? res : lp8758_vout_set(dev, d->base.lmsstate.subdev, I2C_BUS_LP8758_FPGA, 1, 900); res = res ? res : lp8758_vout_ctrl(dev, d->base.lmsstate.subdev, I2C_BUS_LP8758_FPGA, 1, 0, 1); } - USDR_LOG("XDEV", USDR_LOG_INFO, "destroyed\n"); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_INFO, "destroyed\n"); return res; } @@ -1205,17 +2199,13 @@ int xsdr_prepare(xsdr_dev_t *d, bool rxen, bool txen) if (d->base.cgen_clk == 0) { const unsigned default_rate = 1000000; - USDR_LOG("XDEV", USDR_LOG_WARNING, "clock rate isn't set, defaulting to %d!\n", default_rate); + USDR_LL_LOG(dev, "XDEV", USDR_LOG_WARNING, "clock rate isn't set, defaulting to %d!\n", default_rate); res = xsdr_set_samplerate_ex(d, rxen ? default_rate : 0, txen ? default_rate : 0, 0, 0, XSDR_SR_MAXCONVRATE | XSDR_SR_EXTENDED_CGEN); } - if (rxen) { - res = (res) ? res : dev_gpo_set(dev, IGPO_DSP_RST, 1); - res = (res) ? res : dev_gpo_set(dev, IGPO_DSP_RST, 0); - } res = (res) ? res : dev_gpo_set(dev, IGPO_LMS_PWR, IGPO_LMS_PWR_LDOEN | IGPO_LMS_PWR_NRESET | (rxen ? IGPO_LMS_PWR_RXEN : 0) | (txen ? IGPO_LMS_PWR_TXEN : 0)); @@ -1224,6 +2214,7 @@ int xsdr_prepare(xsdr_dev_t *d, bool rxen, bool txen) return res; } + // TODO: Properly set mask for A/B channels d->base.rx_run[0] = rxen; d->base.rx_run[1] = rxen; d->base.tx_run[0] = txen; @@ -1234,97 +2225,107 @@ int xsdr_prepare(xsdr_dev_t *d, bool rxen, bool txen) LMS7_CH_AB, 0, LMS7_CH_AB, 0); - //if (txen) { - // TODO: Add proper delay calibration - // assign cfg_rx_idelay_addr = ~igp_phydly[3:0]; - // assign cfg_rx_idelay_data = { 1'b0, igp_phydly[7:4] }; - // 0..11 D0..D11 - // 12 IQSEL - // 13 *idle* - // 14 FCLK - // 15 *idle* - const unsigned coeff = 0x40; - res = (res) ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_PHYCAL, coeff | 1); - usleep(1); - res = (res) ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_PHYCAL, coeff | 0); - if (res) { - return res; - } - //} - - lms7002m_limelight_reset(&d->base.lmsstate); + res = res ? res : _xsdr_calibrate_lml(d); return res; } - -int xsdr_rfe_pwrdc_get(xsdr_dev_t *d, int *meas1000db) +// Calculate power in dbfs +int xsdr_rfe_pwrdc_get(xsdr_dev_t *d, unsigned acc_norm, int prev_gen, unsigned chan_no, int *meas1000db) { int32_t val[2]; - int res = lowlevel_reg_rdndw(d->base.lmsstate.dev, 0, M2PCI_REG_RD_AVGIDC, (uint32_t*)&val[0], 2); - if (res) - return res; + int gen, gen_n; + int res = 0; - int64_t i = val[0]; - int64_t q = val[1]; - uint64_t pwr = i * i + q * q; - if (pwr == 0) - return -EAGAIN; + do { + res = res ? res : xsdr_phy_dc_estim_get(d, DC_ESTIM_GEN, &gen); + if (res) + return res; + + // USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "[]%d->%d %d %d\n", prev_gen, gen, val[0], val[1]); + if (prev_gen == gen) + return -EAGAIN; + + res = res ? res : xsdr_phy_dc_estim_get(d, chan_no ? DC_ESTIM_BI : DC_ESTIM_AI, &val[0]); + res = res ? res : xsdr_phy_dc_estim_get(d, chan_no ? DC_ESTIM_BQ : DC_ESTIM_AQ, &val[1]); + res = res ? res : xsdr_phy_dc_estim_get(d, DC_ESTIM_GEN, &gen_n); + if (res) + return res; + + + } while (gen != gen_n); + + double fs_i = val[0]; + double fs_q = val[1]; + double i = (0.5 + (fs_i / acc_norm / 65536)) / 2048; // Static correction by +0.5 bits in FPGA + double q = (0.5 + (fs_q / acc_norm / 65536)) / 2048; // Static correction by +0.5 bits in FPGA + double pwr_d = i * i + q * q; - *meas1000db = (1000 * 10 * log10(pwr) - 186639); + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "%d->%d %d %d => %.3f %.3f\n", + prev_gen, gen, val[0], val[1], i * 2048, q * 2048); + + if (pwr_d <= 1e-18) + pwr_d = 1e-18; + + *meas1000db = (1000 * 10 * log10(pwr_d)); return 0; } + + // Calibration int xsdrcal_set_nco_offset(void* param, int channel, int32_t freqoffset) { - //xsdr_dev_t *d = (xsdr_dev_t *)param; + xsdr_dev_t *d = (xsdr_dev_t *)param; //d->rxdsp_freq_offset = freqoffset; //return sfe_rf4_nco_freq(d->base.lmsstate.dev, 0, CSR_RFE4_BASE, freqoffset); - return -EINVAL; + //return -EINVAL; + return lms7002m_bb_set_freq(&d->base, channel ? LMS7_CH_B : LMS7_CH_A, false, freqoffset); } -int xsdrcal_do_meas_nco_avg(void* param, int channel, unsigned logduration, int* func) +int xsdr_rxdccorr(xsdr_dev_t *d, uint64_t *ov) { - xsdr_dev_t *d = (xsdr_dev_t *)param; - int res; - unsigned idx = 0; - int meas1000db; - int accum = 0; - int aidx = 1; //4; - - // 64 - 8M - for (unsigned g = 16; g < 24; g++, idx++) { - if (logduration <= (1 << g)) - break; + int out = 0, out2 = 0; + int res = xsdrcal_do_meas_nco_avg(d, 0, 0, &out); + res = xsdrcal_do_meas_nco_avg(d, 1, 0, &out2); - // Do accumulation of power vs I/Q, might lead to more reliable results, - // aidx <<= idx; - // idx = 0; - } + *ov = out; + return res; +} - for (unsigned k = 0; k < aidx; k++) { - //res = sfe_rf4_nco_enable(d->base.lmsstate.dev, 0, CSR_RFE4_BASE, func ? true : false, idx); - res = -EINVAL; - if (res) - return res; +int xsdrcal_do_meas_nco_avg(void* param, int channel, unsigned logduration, int* func) +{ + xsdr_dev_t *d = (xsdr_dev_t *)param; + int res = 0; + int meas1000db = 0; + int accum = 0, gen = 0; + unsigned acc_idx = 16; - if (!func) - return 0; + if (!func) + return 0; - for (unsigned k = 0; k < 8000; k++) { - res = xsdr_rfe_pwrdc_get(d, &meas1000db); - if (res != -EAGAIN) { - accum += meas1000db; - break; - } + res = res ? res : xsdr_phy_dc_estim_accum(d, acc_idx); + res = res ? res : xsdr_phy_dc_estim_start(d, false); + res = res ? res : usleep(1); + res = res ? res : xsdr_phy_dc_estim_start(d, true); + res = res ? res : xsdr_phy_dc_estim_get(d, DC_ESTIM_GEN, &gen); + if (res) + return res; - usleep(1000); + for (unsigned k = 0; k < 8000; k++) { + res = xsdr_rfe_pwrdc_get(d, acc_idx, gen, channel, &meas1000db); + if (res != -EAGAIN) { + accum += meas1000db; + break; } + + usleep(1000); } - *func = accum / aidx; + USDR_LL_LOG(d->base.lmsstate.dev, "XDEV", USDR_LOG_INFO, "MEAS[%d] = %.3f\n", channel, meas1000db/1e3); + + *func = accum; return res; } @@ -1345,8 +2346,8 @@ int xsdrcal_init_calibrate(xsdr_dev_t *d, struct calibrate_ops* ops, unsigned ch { ops->adcrate = d->base.cgen_clk / d->base.rxcgen_div; ops->dacrate = d->base.cgen_clk / d->base.txcgen_div; - ops->rxsamplerate = ops->adcrate / d->base.rxtsp_div; - ops->txsamplerate = ops->dacrate / d->base.txtsp_div; + ops->rxsamplerate = ops->adcrate / d->base.rxtsp_div;// / d->base.rx_dsp_decim; + ops->txsamplerate = ops->dacrate / d->base.txtsp_div;// / d->base.tx_dsp_inter; ops->rxfrequency = d->base.rx_lo; ops->txfrequency = d->base.tx_lo; @@ -1405,8 +2406,9 @@ int xsdrcal_init_calibrate(xsdr_dev_t *d, struct calibrate_ops* ops, unsigned ch static int _xsdr_path_lb(xsdr_dev_t *d, unsigned rx_rfic_lna, unsigned tx_rfic_band, unsigned channel, bool to_rx) { - int res; + int res = 0; unsigned path; + unsigned lb_loss = 0; if (to_rx) { switch (rx_rfic_lna) { @@ -1423,11 +2425,10 @@ static int _xsdr_path_lb(xsdr_dev_t *d, unsigned rx_rfic_lna, unsigned tx_rfic_b } } - res = xsdr_rfic_fe_set_lna(d, channel == 0 ? LMS7_CH_A : LMS7_CH_B, path); - if (res) - return res; + res = res ? res : xsdr_rfic_fe_set_lna(d, channel == 0 ? LMS7_CH_A : LMS7_CH_B, path); + res = res ? res : lms7002m_rfe_gain(&d->base.lmsstate, RFE_GAIN_RFB, lb_loss, NULL); - return 0; + return res; } // Modification tables @@ -1449,7 +2450,7 @@ int xsdr_calibrate(xsdr_dev_t *d, unsigned channel, unsigned param, int* sarray) uint8_t old_rx_lna = d->base.rx_rfic_path; uint8_t old_tx_lna = d->base.tx_rfic_path; uint8_t tx_loss[2] = { d->base.tx_loss[0] , d->base.tx_loss[1] }; - + lldev_t dev = d->base.lmsstate.dev; if (channel > 1) { return -EINVAL; } @@ -1467,23 +2468,21 @@ int xsdr_calibrate(xsdr_dev_t *d, unsigned channel, unsigned param, int* sarray) } res = (res) ? res : xsdrcal_init_calibrate(d, &cops, channel); - res = (res) ? res : xsdr_rfic_streaming_xflags(d, channel == 1 ? RFIC_SWAP_AB : 0, 0); + // res = (res) ? res : xsdr_rfic_streaming_xflags(d, channel == 1 ? RFIC_SWAP_AB : 0, 0); res = (res) ? res : lms7002m_mac_set(&d->base.lmsstate, channel == 0 ? LMS7_CH_A : LMS7_CH_B); if (res) return res; if ((param & XSDR_CAL_RXLO) && (rx_lo > 0)) { - USDR_LOG("LMS7", USDR_LOG_INFO, "------------------ Calibration RXLO(%c) ------------------\n", 'A' + channel); + USDR_LL_LOG(dev, "LMS7", USDR_LOG_INFO, "------------------ Calibration RXLO(%c) ------------------\n", 'A' + channel); // Do not touch anything since it may affect optimal I/Q correction values // Turn OFF digital RX LO cancellation in RSP res = (res) ? res : xsdrcal_set_nco_offset(d, channel, 0); - //res = (res) ? res : lms7_rxtsp_dc_corr_off(&d->base.lmsstate); res = (res) ? res : lms7002m_rxtsp_dc_corr(&d->base.lmsstate, true, 0); res = (res) ? res : calibrate_rxlo(&cops); if (!norestore) { - //res = (res) ? res : lms7_rxtsp_dc_corr(&d->base.lmsstate, 7); res = (res) ? res : lms7002m_rxtsp_dc_corr(&d->base.lmsstate, false, 7); } @@ -1510,22 +2509,20 @@ int xsdr_calibrate(xsdr_dev_t *d, unsigned channel, unsigned param, int* sarray) if ((param & XSDR_CAL_RXIQIMB) && (rx_lo > 0)) { // TODO if TX was disabled enable TX - USDR_LOG("LMS7", USDR_LOG_INFO, "------------------ Calibration RXIQIMB(%c) ------------------\n", 'A' + channel); + USDR_LL_LOG(dev, "LMS7", USDR_LOG_INFO, "------------------ Calibration RXIQIMB(%c) ------------------\n", 'A' + channel); if (!externallb) { res = (res) ? res : _xsdr_path_lb(d, rx_rfic_lna, tx_rfic_band, channel, true); } - res = calibrate_rxiqimb(&cops); + res = (res) ? res : calibrate_rxiqimb(&cops); if (tx_lo) { - res = (res) ? res : lms7002m_sxx_tune(&d->base.lmsstate, SXX_RX, d->base.fref, tx_lo, false); + res = (res) ? res : lms7002m_sxx_tune(&d->base.lmsstate, SXX_RX, d->base.fref, tx_lo, false); } else { // Looks like TX was off, turn it off res = (res) ? res : lms7002m_sxx_disable(&d->base.lmsstate, SXX_RX); - //res = (res) ? res : lms7_trf_disable(&d->lmsstate); - //res = (res) ? res : lms7_afe_ctrl(&d->lmsstate, true, false, false, false); } if (res) { - USDR_LOG("LMS7", USDR_LOG_WARNING, " RXIQIMB failed: res=%d\n", res); + USDR_LL_LOG(dev, "LMS7", USDR_LOG_WARNING, " RXIQIMB failed: res=%d\n", res); return res; } if (sarray) { @@ -1547,12 +2544,10 @@ int xsdr_calibrate(xsdr_dev_t *d, unsigned channel, unsigned param, int* sarray) return res; if (param & XSDR_CAL_TXLO) { - USDR_LOG("LMS7", USDR_LOG_INFO, "------------------ Calibration TXLO(%c) ------------------\n", 'A' + channel); - // res = (res) ? res : lms7_txtsp_dc_corr(&d->base.lmsstate, true); - // res = (res) ? res : lms7002m_xxtsp_dc_corr(&d->base.lmsstate, LMS_TXTSP, false, 0); + USDR_LL_LOG(dev, "LMS7", USDR_LOG_INFO, "------------------ Calibration TXLO(%c) ------------------\n", 'A' + channel); res = (res) ? res : calibrate_txlo(&cops); if (res) { - USDR_LOG("LMS7", USDR_LOG_WARNING, " TXLO failed: res=%d\n", res); + USDR_LL_LOG(dev, "LMS7", USDR_LOG_WARNING, " TXLO failed: res=%d\n", res); return res; } if (sarray) { @@ -1562,10 +2557,10 @@ int xsdr_calibrate(xsdr_dev_t *d, unsigned channel, unsigned param, int* sarray) } if (param & XSDR_CAL_TXIQIMB) { - USDR_LOG("LMS7", USDR_LOG_INFO, "------------------ Calibration TXIQIMB(%c) ------------------\n", 'A' + channel); + USDR_LL_LOG(dev, "LMS7", USDR_LOG_INFO, "------------------ Calibration TXIQIMB(%c) ------------------\n", 'A' + channel); res = (res) ? res : calibrate_txiqimb(&cops); if (res) { - USDR_LOG("LMS7", USDR_LOG_WARNING, " TXIQIMB failed: res=%d\n", res); + USDR_LL_LOG(dev, "LMS7", USDR_LOG_WARNING, " TXIQIMB failed: res=%d\n", res); return res; } if (sarray) { @@ -1574,20 +2569,22 @@ int xsdr_calibrate(xsdr_dev_t *d, unsigned channel, unsigned param, int* sarray) } } - if (rx_lo > 0) { - res = (res) ? res : lms7002m_sxx_tune(&d->base.lmsstate, SXX_TX, d->base.fref, rx_lo, false); - } else { - res = (res) ? res : lms7002m_sxx_disable(&d->base.lmsstate, SXX_TX); - } - if (res) { - USDR_LOG("LMS7", USDR_LOG_WARNING, "restore configuration failed: res=%d\n", res); - return res; + if (!norestore) { + if (rx_lo > 0) { + res = (res) ? res : lms7002m_sxx_tune(&d->base.lmsstate, SXX_RX, d->base.fref, rx_lo, false); + } else { + res = (res) ? res : lms7002m_sxx_disable(&d->base.lmsstate, SXX_RX); + } + if (res) { + USDR_LL_LOG(dev, "LMS7", USDR_LOG_WARNING, "restore configuration failed: res=%d\n", res); + return res; + } } } if (norestore) - return 0; + goto restore_rxcfg; - USDR_LOG("LMS7", USDR_LOG_INFO, "Calibration: restoring RXPATH=%d TXPATH=%d TXCFG=%d RXCFG=%d\n", + USDR_LL_LOG(dev, "LMS7", USDR_LOG_INFO, "Calibration: restoring RXPATH=%d TXPATH=%d TXCFG=%d RXCFG=%d\n", old_rx_lna, old_tx_lna, old_dsp_txcfg, old_dsp_rxcfg); // Restore individual PAD attenuation @@ -1606,8 +2603,8 @@ int xsdr_calibrate(xsdr_dev_t *d, unsigned channel, unsigned param, int* sarray) res = (res) ? res : lms7002m_mac_set(&d->base.lmsstate, channel == 0 ? LMS7_CH_A : LMS7_CH_B); restore_rxcfg: - res = (res) ? res : xsdrcal_do_meas_nco_avg(d, channel, 0, NULL); - res = (res) ? res : xsdr_rfic_streaming_xflags(d, old_dsp_rxcfg, 0); + // res = (res) ? res : xsdrcal_do_meas_nco_avg(d, channel, 0, NULL); + // res = (res) ? res : xsdr_rfic_streaming_xflags(d, old_dsp_rxcfg, 0); res = (res) ? res : xsdrcal_set_tx_testsig(d, channel, 0, UINT_MAX); return res; @@ -1622,6 +2619,15 @@ int xsdr_gettemp(xsdr_dev_t *d, int* temp256) } } +int xsdr_gettemp_id(xsdr_dev_t *d, int* id) +{ + if (d->new_rev) { + return tmp114_devid_get(d->base.lmsstate.dev, 0, I2C_BUS_TMP_114, id); + } else { + return tmp108_temp_get(d->base.lmsstate.dev, 0, I2C_BUS_TMP_108, id); + } +} + int xsdr_trim_dac_vctcxo(xsdr_dev_t *d, uint16_t val) { if (d->new_rev) { diff --git a/src/lib/device/m2_lm7_1/xsdr_ctrl.h b/src/lib/device/m2_lm7_1/xsdr_ctrl.h index 0d98c67d..ea9890cf 100644 --- a/src/lib/device/m2_lm7_1/xsdr_ctrl.h +++ b/src/lib/device/m2_lm7_1/xsdr_ctrl.h @@ -16,11 +16,20 @@ #define RFIC_CHANS 2 enum xsdr_devices { - SSDR_DEV = 0x31, - XSDR_DEV = 0x30, - XTRX_DEV = 0x2e, + SSDRPRO_DEV = 0x33, + //SSDR2_DEV = 0x32, + SSDR_DEV = 0x31, + XSDR_DEV = 0x30, + XTRX_DEV = 0x2e, }; +enum xsdr_lml_phy_modes { + MODE_INVALID = 0, + MODE_SHARED_MMCM_RX_TX = 1, // Single MMCM for RX / TX + MODE_MMCM_TX_ONLY = 2, + MODE_DUAL_MMCM_RX_TX = 3, +}; +typedef enum xsdr_lml_phy_modes xsdr_lml_phy_modes_t; // =================================================================== // Frequency LMS7 DAC/ADC LML interface Baseband @@ -41,24 +50,73 @@ struct xsdr_dev uint8_t hwchans_rx; uint8_t hwchans_tx; + unsigned s_rx_dec; + unsigned s_tx_int; unsigned s_rxrate; unsigned s_txrate; unsigned s_adcclk; unsigned s_dacclk; unsigned s_flags; - unsigned lms7_lob; + unsigned lms7_lob; // Preferred LMS7 lo if set + unsigned lms7_rxlo_last; + unsigned lms7_txlo_last; + unsigned lms8_switchover_freq; + + uint64_t freq_rxlo; // LMS7 + LMS8 + uint64_t freq_txlo; // LMS7 + LMS8 + int64_t lms8_lo_freq; + + int tx_override_phase; + int tx_override_phase_iq; + int rx_override_phase; + + int lmlcal_tx_phase; + int lmlcal_rx_phase; bool afe_active; + bool cfg_srate_siso_rx; + bool cfg_srate_siso_tx; bool siso_sdr_active_rx; bool siso_sdr_active_tx; + bool rx_port_is_1; + bool mmcm_tx; + bool mmcm_rx; + bool mmcm_single; + bool sep_clkdiv; bool pwr_en; bool new_rev; bool ssdr; + bool ssdr_pro; + bool lms8_alive; + bool lms8_int_mode; + bool lms8_mode_b; + bool xilinx_usp; + bool has_duc_ddc; + + bool dpump; //Dual pump data union { bool pmic_ch145_valid; bool dac_old_r5; }; + + // LMS8001 parameter + bool lms8_rx_path_active; + bool lms8_tx_path_active; + + uint32_t lms8_rx_f_switchover; + uint32_t lms8_tx_f_switchover; + uint32_t lms8st_loopbw; + uint32_t lms8st_phasemargin; + uint32_t lms8st_bwef_1000; + uint32_t lms8st_flock_n; + uint32_t lms8st_iq_gen; + uint32_t lms8st_int_mod; + uint32_t lms8st_enabled; + + // Statistics + double actual_rx_freq; + double actual_tx_freq; }; typedef struct xsdr_dev xsdr_dev_t; @@ -108,9 +166,12 @@ int xsdr_rfic_fe_set_freq(xsdr_dev_t *d, int xsdr_rfic_fe_set_lna(xsdr_dev_t *d, unsigned channel, - //unsigned dir, unsigned lna); +int xsdr_rfic_rfe_set_path(xsdr_dev_t *d, + unsigned path); +int xsdr_rfic_tfe_set_path(xsdr_dev_t *d, + unsigned path); int xsdr_rfic_streaming_xflags(xsdr_dev_t *d, unsigned xor_rx_flags, @@ -124,6 +185,8 @@ int xsdr_dtor(xsdr_dev_t *d); int xsdr_set_extref(xsdr_dev_t *d, bool ext, uint32_t freq); +int xsdr_set_vio(xsdr_dev_t *d, unsigned vio_mv); +int xsdr_set_lms125vdd(xsdr_dev_t *d, unsigned vdd_mv); // Enable RFIC, no streaming int xsdr_pwren(xsdr_dev_t *d, bool on); @@ -131,6 +194,7 @@ int xsdr_pwren(xsdr_dev_t *d, bool on); int xsdr_prepare(xsdr_dev_t *d, bool rxen, bool txen); int xsdr_gettemp(xsdr_dev_t *d, int* temp256); +int xsdr_gettemp_id(xsdr_dev_t *d, int* id); enum xsdr_tx_port_cfg_flags { MUTE_B = 0, @@ -147,12 +211,30 @@ int xsdrcal_set_corr_param(void* param, int channel, int corr_type, int value); int xsdrcal_do_meas_nco_avg(void* param, int channel, unsigned logduration, int *func); //int (*set_tx_testsig_fs8)(void* param, int channel); +int xsdr_phy_tx_iqsel(xsdr_dev_t *d, uint8_t iqsel); + +int xsdr_phy_en_lfsr_generator_mimo(xsdr_dev_t *d, bool en, bool lfsr); +int xsdr_phy_en_lfsr_checker_mimo(xsdr_dev_t *d, bool en); +int xsdr_phy_en_iqab_checker_mimo(xsdr_dev_t *d, bool en); +enum lfsr_cntr_types { + LFSR_CNTR_SYNC = 0, + LFSR_CNTR_LOST = 1, + LFSR_CNTR_BER = 2, + LFSR_CNTR_IQS = 3, +}; -int xsdr_phy_tune(xsdr_dev_t *d, unsigned val); +int xsdr_phy_lfsr_mimo_state(xsdr_dev_t *d, int type, uint32_t v[4]); +int xsdr_phy_tune_rx(xsdr_dev_t *d, unsigned val); int xsdr_clk_debug_info(xsdr_dev_t *d); int xsdr_hwchans_cnt(xsdr_dev_t *d, bool rx, unsigned chans); +int xsdr_override_drp(xsdr_dev_t *d, lsopaddr_t ls_op_addr, + size_t meminsz, void* pin, size_t memoutsz, + const void* pout); + +int xsdr_config_rcvdly(xsdr_dev_t *d, unsigned type, unsigned val); + enum { XSDR_CAL_RXLO = 1, XSDR_CAL_TXLO = 2, @@ -165,6 +247,7 @@ enum { XSDR_DONT_SETBACK = 65536, }; +int xsdr_rxdccorr(xsdr_dev_t *d, uint64_t *ov); int xsdr_usbclk(xsdr_dev_t *d, bool uclk); @@ -199,6 +282,12 @@ enum { IGPO_LDOLMS_EN = 17, IGPO_LED = 18, IGPO_PHYCAL = 19, + + IGPO_DSPCHAIN_RX_PRG = 20, + IGPO_DSPCHAIN_RX_RST = 21, + IGPO_DSPCHAIN_TX_PRG = 22, + IGPO_DSPCHAIN_TX_RST = 23, + }; enum { @@ -215,7 +304,7 @@ enum { }; - +int xsdr_txphase_ovr(xsdr_dev_t *d, unsigned v); #endif diff --git a/src/lib/device/m2_lsdr/m2_da09_4_ad45_2.c b/src/lib/device/m2_lsdr/m2_da09_4_ad45_2.c index 9efbfeb9..632c791f 100644 --- a/src/lib/device/m2_lsdr/m2_da09_4_ad45_2.c +++ b/src/lib/device/m2_lsdr/m2_da09_4_ad45_2.c @@ -125,6 +125,8 @@ const usdr_dev_param_constant_t s_params_m2_da09_4_ad45_2_rev000[] = { { "/ll/rfe/0/base", CSR_RFE4_BASE }, { "/ll/sdr/0/rfic/0", (uintptr_t)"ad45lb49" }, + { "/ll/device/name", (uintptr_t)"lsdr"}, + { "/ll/sdr/max_hw_rx_chans", 1 }, { "/ll/sdr/max_hw_tx_chans", 0 }, @@ -178,7 +180,11 @@ const usdr_dev_param_func_t s_fparams_m2_da09_4_ad45_2_rev000[] = { { "/dm/sdr/0/rx/gain/vga", { dev_m2_d09_4_ad45_2_gainvga_set, NULL }}, { "/dm/sdr/0/rx/gain/lna", { dev_m2_d09_4_ad45_2_gainlna_set, NULL }}, + { "/dm/sdr/0/rx/frequency", { dev_m2_d09_4_ad45_2_sdr_rx_freq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/rx/freqency", { dev_m2_d09_4_ad45_2_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/rx/bandwidth", { dev_m2_d09_4_ad45_2_sdr_rx_bandwidth_set, NULL }}, { "/dm/sdr/0/rx/path", { dev_m2_d09_4_ad45_2_dummy, NULL }}, @@ -797,7 +803,7 @@ int dev_m2_d09_4_ad45_2_rate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t val USDR_LOG("LSDR", USDR_LOG_ERROR, "Decimation set to %d, ADC %d\n", d->rxbb_decim, d->adc_rate); res = (res) ? res : dev_gpo_set(d->base.dev, IGPO_BANK_ADC_CHMSK, 0x0f); - res = (res) ? res : fgearbox_load_fir(d->base.dev, IGPO_BANK_ADC_DSPCHAIN_PRG, (fgearbox_firs_t)d->rxbb_decim); + res = (res) ? res : fgearbox_load_fir(d->base.dev, IGPO_BANK_ADC_DSPCHAIN_PRG, (fgearbox_firs_t)d->rxbb_decim, DSP_7SERIES); if (res) { USDR_LOG("LSDR", USDR_LOG_ERROR, "Unable to initialize FIR gearbox, error = %d!\n", res); return res; diff --git a/src/lib/device/mdev.c b/src/lib/device/mdev.c index 9bf8c9eb..5d46dc59 100644 --- a/src/lib/device/mdev.c +++ b/src/lib/device/mdev.c @@ -1,10 +1,16 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT +//#ifndef _WIN32 + #include #include #include +#ifndef _WIN32 #include +#else +//TODO add proper poll emulation +#endif #include "device.h" #include "device_vfs.h" @@ -99,7 +105,7 @@ int mdev_generic_destroy(lldev_t dev) // Destroy underlying lldevs for (unsigned i = 0; i < obj->cnt; i++) { if (obj->real[i] == NULL) { - USDR_LOG("MDEV", USDR_LOG_WARNING, "Uderlying device has been destroyed already!\n"); + USDR_LOG("MDEV", USDR_LOG_WARNING, "Underlying device has been destroyed already!\n"); continue; } @@ -181,7 +187,7 @@ int _mdev_get_obj(pdevice_t dev, const char* fullpath, pusdr_vfs_obj_t *vfsobj) vfs_object_t *vfso = &obj->vfs_obj; vfso->type = VFST_I64; - vfso->amask = 0; + vfso->flags = 0; vfso->eparam[0] = 0; vfso->eparam[1] = 0; vfso->eparam[2] = 0; @@ -193,7 +199,7 @@ int _mdev_get_obj(pdevice_t dev, const char* fullpath, pusdr_vfs_obj_t *vfsobj) vfso->ops.sai64 = NULL; vfso->ops.gai64 = NULL; vfso->data.i64 = 0; - strncpy(vfso->full_path, fullpath, sizeof(vfso->full_path)); + snprintf(vfso->full_path, sizeof(vfso->full_path), "%s", fullpath); *vfsobj = vfso; return 0; @@ -380,10 +386,10 @@ int _mdev_create_stream(device_t* dev, const char* sid, const char* dformat, // TODO proper parse with specific chnnel mixing for (unsigned k = 0; k < chans_per_dev; k++) { if (channels->phys_names) { - phys_names[k] = channels->phys_names[chans_per_dev * i + k]; + phys_names[k] = channels->phys_names[k]; } if (channels->phys_nums) { - phys_nums[k] = channels->phys_nums[chans_per_dev * i + k]; + phys_nums[k] = channels->phys_nums[k]; } } @@ -400,12 +406,15 @@ int _mdev_create_stream(device_t* dev, const char* sid, const char* dformat, return -EBUSY; } - USDR_LOG("MDEV", USDR_LOG_ERROR, "Creating stream for dev %d with %d channels\n", i, chans_per_dev); + USDR_LOG("MDEV", USDR_LOG_INFO, "Creating stream for dev %d with %d channels\n", i, chans_per_dev); pdevice_t child_dev = obj->real[i]->pdev; res = child_dev->create_stream(child_dev, sid, dformat, &subdev_info, pktsyms, flags, parameters, &real_str[i]); - if (res) + if (res) { + USDR_LOG("MDEV", USDR_LOG_ERROR, "Failed to create strem for dev %d: FMT %s, syms %d SI={CNT=%d FLAGS=%d}: Error %d\n", + i, dformat, pktsyms, subdev_info.count, subdev_info.flags, res); return res; + } mstr->dev_mask[i] = true; @@ -547,6 +556,10 @@ int mdev_create(unsigned pcnt, const char** names, const char** values, lldev_t* } memset(obj, 0, sizeof(*obj)); + if (bus_cnt > 1) { + usdrlog_ll_devname_en(true); + } + // Creating sub-device for (i = 0; i < bus_cnt; i++) { values[idx] = bus_names[i]; @@ -613,3 +626,4 @@ int mdev_create(unsigned pcnt, const char** names, const char** values, lldev_t* free(obj); return res; } + diff --git a/src/lib/device/pe_sync/pe_sync.c b/src/lib/device/pe_sync/pe_sync.c index 3a73dcd0..63c10fab 100644 --- a/src/lib/device/pe_sync/pe_sync.c +++ b/src/lib/device/pe_sync/pe_sync.c @@ -18,6 +18,10 @@ #include "sync_const.h" #include "../hw/lmk05318/lmk05318.h" +#include "../hw/lmx2820/lmx2820.h" +#include "../hw/lmx1214/lmx1214.h" +#include "../hw/lmx1204/lmx1204.h" +#include "../hw/lmk1d1208i/lmk1d1208i.h" // [0] 24bit 20Mhz AD5662 InRef::DAC_REF // [1] 24bit 20Mhz AD5662 ClockGen::GEN_DC @@ -28,11 +32,17 @@ // [6] 24bit 20Mhz AD5662 WR DAC enum i2c_addrs { - I2C_ADDR_LMK05318B = 0x65, + I2C_ADDR_LMK05318B = 0x64, + I2C_ADDR_LP87524 = 0x60, }; enum BUSIDX_I2C { - I2C_BUS_LMK05318B = MAKE_LSOP_I2C_ADDR(0, 0, 0x67), + I2C_BUS_LMK05318B = MAKE_LSOP_I2C_ADDR(0, 0, I2C_ADDR_LMK05318B), + + I2C_BUS_LMK1D1208I_LCK = MAKE_LSOP_I2C_ADDR(0, 1, 0x68), + I2C_BUS_LMK1D1208I_LRF = MAKE_LSOP_I2C_ADDR(0, 1, 0x69), + + I2C_BUS_LP87524 = MAKE_LSOP_I2C_ADDR(1, 0, I2C_ADDR_LP87524), SPI_INREF_DAC = 0, SPI_OCXO_DAC = 1, @@ -53,7 +63,7 @@ const usdr_dev_param_constant_t s_params_pe_sync_rev000[] = { { DNLL_RFE_COUNT, 0 }, { DNLL_TFE_COUNT, 0 }, { DNLL_IDX_REGSP_COUNT, 0 }, - { DNLL_IRQ_COUNT, 16 }, + { DNLL_IRQ_COUNT, 10 }, // low level buses { "/ll/irq/0/core", USDR_MAKE_COREID(USDR_CS_AUX, USDR_AC_PIC32_PCI) }, @@ -98,6 +108,8 @@ const usdr_dev_param_constant_t s_params_pe_sync_rev000[] = { { "/ll/qspi_flash/base", REG_WR_FLASHSPI_CMD }, { "/ll/sdr/0/rfic/0", (uintptr_t)"none" }, + { "/ll/device/name", (uintptr_t)"none"}, + { "/ll/sdr/max_hw_rx_chans", 0 }, { "/ll/sdr/max_hw_tx_chans", 0 }, { "/ll/sdr/max_sw_rx_chans", 0 }, @@ -116,6 +128,10 @@ struct dev_pe_sync { device_t base; lmk05318_state_t gen; + lmx2820_state_t lmx0, lmx1; + lmx1214_state_t lodistr; + lmx1204_state_t cldistr; + lmk1d1208i_state_t lmk1d0, lmk1d1; }; enum dev_gpi { @@ -151,10 +167,21 @@ int dev_pe_sync_rate_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) static void usdr_device_pe_sync_destroy(pdevice_t udev) { - // struct dev_pe_sync *d = (struct dev_pe_sync *)udev; - // lldev_t dev = d->base.dev; - // TODO: power off + struct dev_pe_sync *d = (struct dev_pe_sync *)udev; + lldev_t dev = d->base.dev; + + dev_gpo_set(dev, IGPO_DISTRIB_CTRL, 0); + dev_gpo_set(dev, IGPO_SY0_CTRL, 0); + dev_gpo_set(dev, IGPO_SY1_CTRL, 0); + usdr_device_base_destroy(udev); + USDR_LOG("SYNC", USDR_LOG_WARNING, "PESync destroyed"); +} + +static int i2c_reg_rd8(lldev_t dev, unsigned lsaddr, uint8_t reg, uint8_t* val) +{ + uint8_t addr[1] = { reg }; + return lowlevel_ls_op(dev, 0, USDR_LSOP_I2C_DEV, lsaddr, 1, val, 1, addr); } static int usdr_device_pe_sync_initialize(pdevice_t udev, unsigned pcount, const char** devparam, const char** devval) @@ -162,7 +189,8 @@ static int usdr_device_pe_sync_initialize(pdevice_t udev, unsigned pcount, const struct dev_pe_sync *d = (struct dev_pe_sync *)udev; lldev_t dev = d->base.dev; int res = 0; - uint32_t v = 0; + uint32_t v = 0, s0 = 0, s1 = 0, s2 = 0, s3 = 0; + uint8_t r = 0, r4 = 0, r5 = 0; if (getenv("USDR_BARE_DEV")) { USDR_LOG("SYNC", USDR_LOG_WARNING, "USDR_BARE_DEV is set, skipping initialization!\n"); @@ -173,7 +201,7 @@ static int usdr_device_pe_sync_initialize(pdevice_t udev, unsigned pcount, const // gpo_in_ctrl[1] -- 0 - external SMA, 1 - feedback from LCK_FB // gpo_in_ctrl[2] -- 0 - external SMA, 1 - 1PPS from GPS // gpo_in_ctrl[3] -- En GPS LDO - res = res ? res : dev_gpo_set(dev, IGPO_IN_CTRL, 0); // Disable GPS + res = res ? res : dev_gpo_set(dev, IGPO_IN_CTRL, 0b1101); // Enable GPS // gpo_sy_ctrl*[0] -- LMX2820 LDO Pwr EN // gpo_sy_ctrl*[1] -- LMX2820 CE pin @@ -182,18 +210,24 @@ static int usdr_device_pe_sync_initialize(pdevice_t udev, unsigned pcount, const res = res ? res : dev_gpo_set(dev, IGPO_SY1_CTRL, 3); // Enable LMX2820 1 // gpo_gen_ctrl[0] -- En LDO for LMK05318B - // gpo_gen_ctrl[1] -- PD for LMK05318B + // gpo_gen_ctrl[1] -- PDN for LMK05318B // gpo_gen_ctrl[2] -- En LDO for OCXO and OCXO DAC // gpo_gen_ctrl[3] -- En distribution buffer REFCLK // gpo_gen_ctrl[4] -- En distribution buffer 1PPS - res = res ? res : dev_gpo_set(dev, IGPO_GEN_CTRL, (1 << 0) | (1 << 2)); + // gpo_gen_ctrl[5] -- clk_gpio[0] + // gpo_gen_ctrl[6] -- clk_gpio[1] + // gpo_gen_ctrl[7] -- clk_gpio[2] + res = res ? res : dev_gpo_set(dev, IGPO_GEN_CTRL, (1 << 0) | (1 << 1) | (1 << 2) | (1 << 5) | (1 << 3) | (1 << 4)); // gpo_distrib_ctrl[0] -- En global LDO for all distribution logic // gpo_distrib_ctrl[2:1] -- En LDO for LMX1204/LMX1214 // gpo_distrib_ctrl[3] -- 0 - buffers LMK1D1208I disable, 1 - en // gpo_distrib_ctrl[4] -- En LDO FCLK4..0 CMOS buffers // gpo_distrib_ctrl[5] -- 0 - internal path, 1 - external LO/REFCLK/SYSREF - res = res ? res : dev_gpo_set(dev, IGPO_DISTRIB_CTRL, (1 << 0)); + res = res ? res : dev_gpo_set(dev, IGPO_DISTRIB_CTRL, (1 << 0) | (15 << 1)); + + // Wait for all LDOs to settle + usleep(200000); // gpo_led_ctrl[0] -- LEDG[0] // gpo_led_ctrl[1] -- LEDR[0] @@ -205,12 +239,25 @@ static int usdr_device_pe_sync_initialize(pdevice_t udev, unsigned pcount, const // gpo_led_ctrl[7] -- LEDR[3] res = res ? res : dev_gpo_set(dev, IGPO_LED_CTRL, 0xff); - usleep(1000); - res = res ? res : dev_gpi_get32(dev, IGPI_STAT, &v); - USDR_LOG("SYNC", USDR_LOG_WARNING, "STAT = %08x\n", v); + res = res ? res : i2c_reg_rd8(dev, I2C_BUS_LP87524, 0x01, &r); + + res = res ? res : lowlevel_spi_tr32(dev, 0, SPI_LMX2820_0, 0x9c0000, &s0); + res = res ? res : lowlevel_spi_tr32(dev, 0, SPI_LMX2820_1, 0x9c0000, &s1); + + res = res ? res : lowlevel_spi_tr32(dev, 0, SPI_LMX1204, 0x176040, NULL); //Enable MUXOUT as SPI readback + res = res ? res : lowlevel_spi_tr32(dev, 0, SPI_LMX1204, 0xA10000, &s2); + + res = res ? res : lowlevel_spi_tr32(dev, 0, SPI_LMX1214, 0x176040, NULL); //Enable MUXOUT + res = res ? res : lowlevel_spi_tr32(dev, 0, SPI_LMX1214, 0xCF0000, &s3); + + res = res ? res : i2c_reg_rd8(dev, I2C_BUS_LMK1D1208I_LCK, 0x05, &r4); + res = res ? res : i2c_reg_rd8(dev, I2C_BUS_LMK1D1208I_LRF, 0x05, &r5); - // TODO: Initialize LMK05318B + USDR_LOG("SYNC", USDR_LOG_WARNING, "STAT=%08x LP87524_OTP=%02x LMS2820[0/1]=%04x/%04x LMX1204/LMX1214=%04x/%04x LMK1D1208I_LCK/LRF=%02x/%02x\n", + v, r, s0, s1, s2, s3, r4, r5); + + // Initialize LMK05318B // XO: 25Mhz // // OUT0: LVDS 125.000 Mhz @@ -221,7 +268,308 @@ static int usdr_device_pe_sync_initialize(pdevice_t udev, unsigned pcount, const // OUT5: LVDS OFF 156.250 Mhz | OFF by default // OUT6: Dual CMOS 10.000 Mhz // OUT7: Dual CMOS 1 Hz - // res = res ? res : lmk05318_create() + + if(res) + return res; + + lmk05318_dpll_settings_t dpll; + memset(&dpll, 0, sizeof(dpll)); + dpll.enabled = true; + dpll.en[LMK05318_PRIREF] = true; + dpll.fref[LMK05318_PRIREF] = 1; + dpll.type[LMK05318_PRIREF] = DPLL_REF_TYPE_DIFF_NOTERM; + dpll.dc_mode[LMK05318_PRIREF] = DPLL_REF_DC_COUPLED_INT; + dpll.buf_mode[LMK05318_PRIREF] = DPLL_REF_AC_BUF_HYST50_DC_EN; + + const uint64_t lmk_freq[8] = + { + 125000000, + 125000000, + 150000000, + 150000000, + 156250000, + 156250000, + 10000000, + 1 + }; + + lmk05318_out_config_t lmk_out[8]; + + res = res ? res : lmk05318_port_request(&lmk_out[0], 0, lmk_freq[0], false, LVDS); + res = res ? res : lmk05318_port_request(&lmk_out[1], 1, lmk_freq[1], false, LVDS); + res = res ? res : lmk05318_port_request(&lmk_out[2], 2, lmk_freq[2], false, LVDS); + res = res ? res : lmk05318_port_request(&lmk_out[3], 3, lmk_freq[3], false, LVDS); + res = res ? res : lmk05318_port_request(&lmk_out[4], 4, lmk_freq[4], false, OUT_OFF); + res = res ? res : lmk05318_port_request(&lmk_out[5], 5, lmk_freq[5], false, OUT_OFF); + res = res ? res : lmk05318_port_request(&lmk_out[6], 6, lmk_freq[6], false, LVCMOS_P_N); + res = res ? res : lmk05318_port_request(&lmk_out[7], 7, lmk_freq[7], false, LVCMOS_P_N); + + res = res ? res : lmk05318_create(dev, 0, I2C_BUS_LMK05318B, 25000000, XO_CMOS, false, &dpll, lmk_out, SIZEOF_ARRAY(lmk_out), &d->gen, false /*dry_run*/); + if(res) + return res; + + //wait for PRIREF/SECREF validation + res = lmk05318_wait_dpll_ref_stat(&d->gen, 4*60000000); //60s - searching for satellites may take a lot of time if GPS in just turned on + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK03518 DPLL input reference freqs are not validated during specified timeout"); + return res; + } + + //wait for lock + res = lmk05318_wait_apll1_lock(&d->gen, 100000); + res = res ? res : lmk05318_wait_apll2_lock(&d->gen, 100000); + + unsigned los_msk; + lmk05318_check_lock(&d->gen, &los_msk, false /*silent*/); //just to log state + + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK03518 PLLs not locked during specified timeout"); + return res; + } + + //sync to make APLL1/APLL2 & out channels in-phase + res = lmk05318_sync(&d->gen); + if(res) + return res; + + USDR_LOG("SYNC", USDR_LOG_INFO, "LMK03518 outputs synced"); + // + + usleep(2000000); //LMX2820[0] will not start without it + + // + //LMX2820 #0 setup + // + + const uint64_t lmx0_freq[] = + { + 500000000*2, //1400000000, //OUT_A + 500000000*2, //1400000000 //OUT_B + 25000000, //SR_OUT + }; + + res = lmx2820_create(dev, 0, SPI_LMX2820_0, &d->lmx0); + + lmx2820_sysref_chain_t* lmx0_sr = &d->lmx0.lmx2820_sysref_chain; + lmx0_sr->enabled = false; + lmx0_sr->master_mode = true; + lmx0_sr->cont_pulse = true; + lmx0_sr->srout = lmx0_freq[2]; + lmx0_sr->delay_ctrl = 0; + + res = res ? res : lmx2820_tune(&d->lmx0, lmk_freq[2], 2 /*mash order 2*/, 0 /*force_mult*/, lmx0_freq[0], lmx0_freq[1]); + + lmx2820_stats_t lmxstatus0; + lmx2820_read_status(&d->lmx0, &lmxstatus0); //just for logging + + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMX2820[0] PLL not locked during specified timeout"); + return res; + } + + USDR_LOG("SYNC", USDR_LOG_INFO, "LMX2820[0] outputs locked & synced"); + // + + // + //LMX2820 #1 setup + // + + const uint64_t lmx1_freq[] = + { + 550000000, //1600000000, + 550000000, //1600000000 + }; + + res = lmx2820_create(dev, 0, SPI_LMX2820_1, &d->lmx1); + + lmx2820_sysref_chain_t* lmx1_sr = &d->lmx1.lmx2820_sysref_chain; + lmx1_sr->enabled = false; + lmx1_sr->master_mode = true; + lmx1_sr->cont_pulse = true; + lmx1_sr->srout = 25000000; + lmx1_sr->delay_ctrl = 0; + + res = res ? res : lmx2820_tune(&d->lmx1, lmk_freq[3], 2 /*mash order 2*/, 0 /*force_mult*/, lmx1_freq[0], lmx1_freq[1]); + + lmx2820_stats_t lmxstatus1; + lmx2820_read_status(&d->lmx1, &lmxstatus1); //just for logging + + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMX2820[1] PLL not locked during specified timeout"); + return res; + } + + USDR_LOG("SYNC", USDR_LOG_INFO, "LMX2820[1] outputs locked & synced"); + // + + // + //LMX1214 setup + // + + const uint64_t ld_clkout = lmx1_freq[0]; + bool ld_en[LMX1214_OUT_CNT] = {1,1,1,1}; + lmx1214_auxclkout_cfg_t ld_aux; + ld_aux.enable = 1; + ld_aux.fmt = LMX1214_FMT_LVDS; + ld_aux.freq = ld_clkout; + + res = lmx1214_create(dev, 0, SPI_LMX1214, &d->lodistr); + res = res ? res : lmx1214_solver(&d->lodistr, lmx1_freq[0], ld_clkout, ld_en, &ld_aux, false /*prec_mode*/, false /*dry run*/); + + float lmx1214_tempval; + lmx1214_get_temperature(&d->lodistr, &lmx1214_tempval); //just for logging + + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMX1214 failed to initialize, res:%d", res); + return res; + } + + USDR_LOG("SYNC", USDR_LOG_INFO, "LMX1214 initialized"); + // + + // + //LMX1204 setup + // + + res = lmx1204_create(dev, 0, SPI_LMX1204, &d->cldistr); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMX1204 failed to initialize, res:%d", res); + return res; + } + + //set 1204 params + lmx1204_state_t* lmx1204 = &d->cldistr; + lmx1204->clkin = lmx0_freq[1]; //LMX0 OUT_B + lmx1204->sysrefreq = lmx0_freq[2]; //LMX0 SR_OUT + lmx1204->clkout = d->cldistr.clkin * 4; + lmx1204->sysrefout = 3125000; + lmx1204->sysref_mode = LMX1204_CONTINUOUS; + lmx1204->logiclkout = 125000000; + + lmx1204->ch_en[LMX1204_CH0] = 1; + lmx1204->ch_en[LMX1204_CH1] = 1; + lmx1204->ch_en[LMX1204_CH2] = 1; + lmx1204->ch_en[LMX1204_CH3] = 1; + lmx1204->ch_en[LMX1204_CH_LOGIC] = 1; + + lmx1204->clkout_en[LMX1204_CH0] = 1; + lmx1204->clkout_en[LMX1204_CH1] = 1; + lmx1204->clkout_en[LMX1204_CH2] = 1; + lmx1204->clkout_en[LMX1204_CH3] = 1; + lmx1204->clkout_en[LMX1204_CH_LOGIC] = 1; + + lmx1204->sysref_en = 1; + lmx1204->sysrefout_en[LMX1204_CH0] = 1; + lmx1204->sysrefout_en[LMX1204_CH1] = 1; + lmx1204->sysrefout_en[LMX1204_CH2] = 1; + lmx1204->sysrefout_en[LMX1204_CH3] = 1; + lmx1204->sysrefout_en[LMX1204_CH_LOGIC] = 1; + + lmx1204->logiclkout_fmt = LMX1204_FMT_LVDS; + lmx1204->logisysrefout_fmt = LMX1204_FMT_LVDS; + + res = lmx1204_solver(&d->cldistr, false/*prec_mode*/, false/*dry_run*/); + if(res) + return res; + + lmx1204_stats_t lmx1204status; + lmx1204_read_status(&d->cldistr, &lmx1204status); //just for log + + res = lmx1204_wait_pll_lock(&d->cldistr, 1000000); + + lmx1204_read_status(&d->cldistr, &lmx1204status); //just for log + + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMX1204 not locked, err:%d [%s]", + res, (res == -ETIMEDOUT ? "TIMEOUT" : "ERROR")); + return res; + } + USDR_LOG("SYNC", USDR_LOG_INFO, "LMX1204 locked"); + + USDR_LOG("SYNC", USDR_LOG_INFO, "LMX1204 initialized"); + // + + // + // LMK1D1208I[0] setup + // + + lmk1d1208i_config_t lmk1d_cfg; + lmk1d_cfg.in[0].enabled = true; + lmk1d_cfg.in[1].enabled = false; + lmk1d_cfg.bank[0].mute = false; + lmk1d_cfg.bank[1].mute = false; + lmk1d_cfg.bank[0].sel = LMK1D1208I_IN0; + lmk1d_cfg.bank[1].sel = LMK1D1208I_IN0; + lmk1d_cfg.out[0].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[1].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[2].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[3].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[4].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[5].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[6].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[7].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[0].enabled = true; + lmk1d_cfg.out[1].enabled = true; + lmk1d_cfg.out[2].enabled = true; + lmk1d_cfg.out[3].enabled = true; + lmk1d_cfg.out[4].enabled = true; + lmk1d_cfg.out[5].enabled = true; + lmk1d_cfg.out[6].enabled = true; + lmk1d_cfg.out[7].enabled = true; + + res = lmk1d1208i_create(dev, 0, I2C_BUS_LMK1D1208I_LCK, &lmk1d_cfg, &d->lmk1d0); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK1D1208I[0] failed to initialize, res:%d", res); + return res; + } + + USDR_LOG("SYNC", USDR_LOG_INFO, "LMK1D1208I[0] initialized"); + // + + // + // LMK1D1208I[1] setup + // + + lmk1d_cfg.in[0].enabled = true; + lmk1d_cfg.in[1].enabled = false; + lmk1d_cfg.bank[0].mute = false; + lmk1d_cfg.bank[1].mute = false; + lmk1d_cfg.bank[0].sel = LMK1D1208I_IN0; + lmk1d_cfg.bank[1].sel = LMK1D1208I_IN0; + lmk1d_cfg.out[0].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[1].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[2].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[3].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[4].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[5].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[6].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[7].amp = LMK1D1208I_STANDARD_LVDS; + lmk1d_cfg.out[0].enabled = true; + lmk1d_cfg.out[1].enabled = true; + lmk1d_cfg.out[2].enabled = true; + lmk1d_cfg.out[3].enabled = true; + lmk1d_cfg.out[4].enabled = true; + lmk1d_cfg.out[5].enabled = true; + lmk1d_cfg.out[6].enabled = true; + lmk1d_cfg.out[7].enabled = true; + + res = lmk1d1208i_create(dev, 0, I2C_BUS_LMK1D1208I_LRF, &lmk1d_cfg, &d->lmk1d1); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK1D1208I[1] failed to initialize, res:%d", res); + return res; + } + + USDR_LOG("SYNC", USDR_LOG_INFO, "LMK1D1208I[1] initialized"); + // return res; } diff --git a/src/lib/device/pe_sync/sync_const.h b/src/lib/device/pe_sync/sync_const.h index 6bdbe234..2d949dcd 100644 --- a/src/lib/device/pe_sync/sync_const.h +++ b/src/lib/device/pe_sync/sync_const.h @@ -38,8 +38,8 @@ enum sync_base_regs { enum sync_base_ints { INT_SPI_0 = 0, - INT_SPI_1 = 2, - INT_SPI_2 = 3, + INT_SPI_1 = 1, + INT_SPI_2 = 2, INT_SPI_3 = 3, INT_SPI_4 = 4, INT_SPI_5 = 5, diff --git a/src/lib/device/u3_limesdr/limesdr_ctrl.c b/src/lib/device/u3_limesdr/limesdr_ctrl.c index ea535b2f..a52a0f48 100644 --- a/src/lib/device/u3_limesdr/limesdr_ctrl.c +++ b/src/lib/device/u3_limesdr/limesdr_ctrl.c @@ -398,7 +398,7 @@ int limesdr_set_samplerate(limesdr_dev_t *d, unsigned rx_rate, unsigned tx_rate, // LML2 - RX // LML1 - TX res = lms7002m_samplerate(&d->base, rx_rate, tx_rate, adc_rate, dac_rate, - XSDR_SR_MAXCONVRATE | XSDR_LML_SISO_DDR_RX | XSDR_LML_SISO_DDR_TX, false); + XSDR_SR_MAXCONVRATE | XSDR_LML_SISO_DDR_RX | XSDR_LML_SISO_DDR_TX, false, 1, 1); // TODO set direct mode or do PLL with phase search res = res ? res : limesdr_set_direct_clocking(d, 0); diff --git a/src/lib/device/u3_limesdr/u3_limesdr.c b/src/lib/device/u3_limesdr/u3_limesdr.c index 73899054..6418b066 100644 --- a/src/lib/device/u3_limesdr/u3_limesdr.c +++ b/src/lib/device/u3_limesdr/u3_limesdr.c @@ -42,7 +42,13 @@ const usdr_dev_param_constant_t s_params_u3_limesdr_0[] = { { "/ll/sdr/0/rfic/0", (uintptr_t)"lms7002m" }, + { "/ll/device/name", (uintptr_t)"limemini"}, + { "/ll/sdr/max_hw_rx_chans", 1 }, + { "/ll/sdr/max_hw_tx_chans", 1 }, + + { "/ll/sdr/max_sw_rx_chans", 1 }, + { "/ll/sdr/max_sw_tx_chans", 1 }, }; @@ -62,6 +68,9 @@ static int dev_limesdr_sdr_rx_gainvga_set(pdevice_t ud, pusdr_vfs_obj_t obj, uin static int dev_limesdr_sdr_rx_gainlna_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_limesdr_sdr_rx_gainlb_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_limesdr_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +static int dev_limesdr_sdr_tx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); + static int dev_mp_lm7_1_gps_sdr_rx_bandwidth_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); static int dev_mp_lm7_1_gps_sdr_tx_bandwidth_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); @@ -82,12 +91,20 @@ const usdr_dev_param_func_t s_fparams_u3_limesdr_0[] = { { "/dm/sdr/refclk/path", { dev_limesdr_refclk_path_set, NULL }}, { "/dm/power/en", { dev_limesdr_pwren_set, NULL }}, + { "/dm/sdr/0/rx/frequency", { dev_limesdr_sdr_rx_freq_set, NULL }}, + { "/dm/sdr/0/tx/frequency", { dev_limesdr_sdr_tx_freq_set, NULL }}, + + /* TODO: delete block below after several releases, these are just aliases to above due typo for compatibility with old code */ { "/dm/sdr/0/rx/freqency", { dev_limesdr_sdr_rx_freq_set, NULL }}, { "/dm/sdr/0/tx/freqency", { dev_limesdr_sdr_tx_freq_set, NULL }}, + { "/dm/sdr/0/rx/gain", { dev_limesdr_sdr_rx_gain_set, NULL }}, { "/dm/sdr/0/tx/gain", { dev_limesdr_sdr_tx_gain_set, NULL }}, { "/dm/sdr/0/tx/gain/lb", { dev_limesdr_sdr_tx_gainlb_set, NULL }}, + { "/dm/sdr/0/rx/path", { dev_limesdr_sdr_rx_path_set, NULL }}, + { "/dm/sdr/0/tx/path", { dev_limesdr_sdr_tx_path_set, NULL }}, + { "/dm/sdr/0/rx/gain/pga", { dev_limesdr_sdr_rx_gainpga_set, NULL }}, { "/dm/sdr/0/rx/gain/vga", { dev_limesdr_sdr_rx_gainvga_set, NULL }}, { "/dm/sdr/0/rx/gain/lna", { dev_limesdr_sdr_rx_gainlna_set, NULL }}, @@ -225,6 +242,16 @@ int dev_limesdr_sdr_rx_gainlb_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t va return lms7002m_set_gain(&d->limedev.base, LMS7_CH_AB, RFIC_LMS7_RX_LB_GAIN, value, NULL); } +int dev_limesdr_sdr_rx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + return 0; +} + +int dev_limesdr_sdr_tx_path_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) +{ + return 0; +} + int dev_mp_lm7_1_gps_sdr_rx_bandwidth_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { struct dev_limesdr *d = (struct dev_limesdr *)ud; diff --git a/src/lib/hw/CMakeLists.txt b/src/lib/hw/CMakeLists.txt index f8c7d417..5c04840d 100644 --- a/src/lib/hw/CMakeLists.txt +++ b/src/lib/hw/CMakeLists.txt @@ -1,9 +1,11 @@ # Copyright (c) 2023-2024 Wavelet Lab # SPDX-License-Identifier: MIT -set(HW_FILES si549 si5332 ads42lbx9 tps6594 tmp108 tmp114 lms6002d lms7002m lms8001 lp8758 lmk05318 lmk5c33216 tps6381x dac80501 lmk04832 tca6424a adf4002b lp875484 afe79xx) +set(HW_FILES common si549 si5332 ads42lbx9 tps6594 tmp108 tmp114 lms6002d lms7002m lms8001 lp8758 lmk05318 lmk5c33216 tps6381x dac80501 + lmk04832 tca6424a adf4002b lp875484 afe79xx lmx1204 lmx1214 lmk1d1208i ad5662 lmx1205 lp87524j lmx2820 tca9555 at24) # YAML registers generators -set(HW_FILES_YAML si5332 lmk05318 lmk5c33216 lms6002d lms7002m lms8001 tps6381x dac80501 lmk04832 xra1405 tca6424a adf4002b lp875484) +set(HW_FILES_YAML si5332 lmk05318 lmk5c33216 lms6002d lms7002m lms8001 tps6381x dac80501 + lmk04832 xra1405 tca6424a adf4002b lp875484 lmx1204 lmx1214 lmk1d1208i ad5662 lmx1205 lp87524j lmx2820 tca9555) foreach(I ${HW_FILES}) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/${I} HW_DIR_FILES) diff --git a/src/lib/hw/ad5662/ad5662.c b/src/lib/hw/ad5662/ad5662.c new file mode 100644 index 00000000..4d06260f --- /dev/null +++ b/src/lib/hw/ad5662/ad5662.c @@ -0,0 +1,6 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "def_ad5662.h" +#include "ad5662.h" +#include "usdr_logging.h" diff --git a/src/lib/hw/ad5662/ad5662.h b/src/lib/hw/ad5662/ad5662.h new file mode 100644 index 00000000..8b24bfe9 --- /dev/null +++ b/src/lib/hw/ad5662/ad5662.h @@ -0,0 +1,7 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef AD5662_H +#define AD5662_H + +#endif // AD5662_H diff --git a/src/lib/hw/ad5662/ad5662.yaml b/src/lib/hw/ad5662/ad5662.yaml new file mode 100644 index 00000000..b48f5f8e --- /dev/null +++ b/src/lib/hw/ad5662/ad5662.yaml @@ -0,0 +1,34 @@ +name: AD5662 +revision: 0.0.1 +processors: [ c ] +bus: + type: SPI + wr_mask: 0x80000000 + usdr_path: /debug/hw/ad5662/*/reg +addr_width: 16 +data_width: 24 + +pages: +- name: Main + regs: + - addr: 0x0 + name: OUTPUT + fields: + - bits: '5:0' + name: NOT_USED + mode: W + desc: Not used bits + - bits: '7:6' + name: POWER_DOWN + mode: W + dflt: 0b00 + desc: Power down setting + opts: + 0b00: Normal operation + 0b01: 1kOmh to GND + 0b10: 100kOhm to GND + 0b11: Tree-state + - bits: '23:8' + name: OUTPUT + mode: W + desc: Output value diff --git a/src/lib/hw/afe79xx/afe79xx.c b/src/lib/hw/afe79xx/afe79xx.c index 03c44ba5..1d016fc6 100644 --- a/src/lib/hw/afe79xx/afe79xx.c +++ b/src/lib/hw/afe79xx/afe79xx.c @@ -1,9 +1,7 @@ #include "afe79xx.h" #include +#include #include -#include -#include - enum afe79xx_regs { CHIP_TYPE = 3, @@ -88,11 +86,16 @@ int afe79xx_create_dummy(afe79xx_state_t* out) return 0; } -int afe79xx_create(lldev_t dev, unsigned subdev, unsigned lsaddr, afe79xx_state_t* out) +int afe79xx_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned chipType, afe79xx_state_t* out) { int res; - const char* afe79xxlib = "liblibcapi79xx.so"; - + const char* afe79xxlib = +#ifdef _WIN32 + "liblibcapi79xx.dll"; +#else + "liblibcapi79xx.so"; +#endif + memset(out, 0, sizeof(*out)); out->dev = dev; out->subdev = subdev; out->addr = lsaddr; @@ -119,36 +122,51 @@ int afe79xx_create(lldev_t dev, unsigned subdev, unsigned lsaddr, afe79xx_state_ out->capi.user = NULL; out->capi.driver_handle = NULL; - out->dl_handle = dlopen(afe79xxlib, RTLD_NOW); + out->dl_handle = usdr_lib_load(afe79xxlib); // dlopen(afe79xxlib, RTLD_NOW); if (out->dl_handle == NULL) { +#ifdef _WIN32 + USDR_LOG("79xx", USDR_LOG_ERROR, "Couldn't load CAPI AFE79XX NDA LIB wrapper `%s`!\n", + afe79xxlib); +#else USDR_LOG("79xx", USDR_LOG_ERROR, "Couldn't load CAPI AFE79XX NDA LIB wrapper `%s`: %s!\n", afe79xxlib, dlerror()); +#endif return -EFAULT; } - out->libcapi79xx_create = (libcapi79xx_create_fn_t)dlsym(out->dl_handle, LIBCAPI79XX_CREATE_FN); - out->libcapi79xx_destroy = (libcapi79xx_destroy_fn_t)dlsym(out->dl_handle, LIBCAPI79XX_DESTROY_FN); - out->libcapi79xx_init = (libcapi79xx_init_fn_t)dlsym(out->dl_handle, LIBCAPI79XX_INIT_FN); + out->libcapi79xx_create = (libcapi79xx_create_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_CREATE_FN); + out->libcapi79xx_destroy = (libcapi79xx_destroy_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_DESTROY_FN); + out->libcapi79xx_init = (libcapi79xx_init_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_INIT_FN); - out->libcapi79xx_upd_nco = (libcapi79xx_upd_nco_fn_t)dlsym(out->dl_handle, LIBCAPI79XX_UPD_NCO_FN); - out->libcapi79xx_get_nco = (libcapi79xx_get_nco_fn_t)dlsym(out->dl_handle, LIBCAPI79XX_GET_NCO_FN); + out->libcapi79xx_upd_nco = (libcapi79xx_upd_nco_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_UPD_NCO_FN); + out->libcapi79xx_get_nco = (libcapi79xx_get_nco_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_GET_NCO_FN); - out->libcapi79xx_set_dsa = (libcapi79xx_set_dsa_fn_t)dlsym(out->dl_handle, LIBCAPI79XX_SET_DSA_FN); + out->libcapi79xx_set_dsa = (libcapi79xx_set_dsa_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_SET_DSA_FN); - out->libcapi79xx_check_health = (libcapi79xx_check_health_fn_t)dlsym(out->dl_handle, LIBCAPI79XX_CHECK_HEALTH_FN); + out->libcapi79xx_check_health = (libcapi79xx_check_health_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_CHECK_HEALTH_FN); + + out->libcapi79xx_set_tdd = (libcapi79xx_set_tdd_fn_t)usdr_lib_sym(out->dl_handle, LIBCAPI79XX_SET_TDD_FN); if (!out->libcapi79xx_create || !out->libcapi79xx_destroy || !out->libcapi79xx_init || - !out->libcapi79xx_set_dsa || + !out->libcapi79xx_set_dsa || !out->libcapi79xx_set_tdd || !out->libcapi79xx_upd_nco || !out->libcapi79xx_get_nco || !out->libcapi79xx_check_health) { - USDR_LOG("79xx", USDR_LOG_ERROR, "Broken CAPI AFE79XX NDA LIB wrapper `%s`!\n", - afe79xxlib); - - dlclose(out->dl_handle); + USDR_LOG("79xx", USDR_LOG_ERROR, "Broken CAPI AFE79XX NDA LIB wrapper `%s`: %d%d%d%d.%d%d%d%d!\n", + afe79xxlib, + out->libcapi79xx_create != NULL, + out->libcapi79xx_destroy != NULL, + out->libcapi79xx_init != NULL, + out->libcapi79xx_upd_nco != NULL, + out->libcapi79xx_get_nco != NULL, + out->libcapi79xx_set_dsa != NULL, + out->libcapi79xx_check_health != NULL, + out->libcapi79xx_set_tdd != NULL); + + usdr_lib_close(out->dl_handle); return -EFAULT; } // TODO: version check - return out->libcapi79xx_create(&out->capi, AFE7903); + return out->libcapi79xx_create(&out->capi, (enum afe79xx_chip_type)chipType); } int afe79xx_init(afe79xx_state_t* afe, const char* configuration) diff --git a/src/lib/hw/afe79xx/afe79xx.h b/src/lib/hw/afe79xx/afe79xx.h index 31780abf..5c1858ac 100644 --- a/src/lib/hw/afe79xx/afe79xx.h +++ b/src/lib/hw/afe79xx/afe79xx.h @@ -26,11 +26,13 @@ struct afe79xx_state { libcapi79xx_check_health_fn_t libcapi79xx_check_health; + libcapi79xx_set_tdd_fn_t libcapi79xx_set_tdd; + }; typedef struct afe79xx_state afe79xx_state_t; -int afe79xx_create(lldev_t dev, unsigned subdev, unsigned lsaddr, afe79xx_state_t* out); +int afe79xx_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned chipType, afe79xx_state_t* out); int afe79xx_init(afe79xx_state_t* afe, const char *configuration); diff --git a/src/lib/hw/afe79xx/libcapi79xx_api.h b/src/lib/hw/afe79xx/libcapi79xx_api.h index b59ac5cd..9b8a99fa 100644 --- a/src/lib/hw/afe79xx/libcapi79xx_api.h +++ b/src/lib/hw/afe79xx/libcapi79xx_api.h @@ -50,6 +50,7 @@ typedef int (*libcapi79xx_set_dsa_fn_t)(libcapi79xx_t* o, unsigned type, unsigne typedef int (*libcapi79xx_check_health_fn_t)(libcapi79xx_t* o, int *ok, unsigned sz, char* buf); +typedef int (*libcapi79xx_set_tdd_fn_t)(libcapi79xx_t* o, uint8_t rx, uint8_t fb, uint8_t tx); #define LIBCAPI79XX_CREATE_FN "libcapi79xx_create" #define LIBCAPI79XX_DESTROY_FN "libcapi79xx_destroy" @@ -62,4 +63,6 @@ typedef int (*libcapi79xx_check_health_fn_t)(libcapi79xx_t* o, int *ok, unsigned #define LIBCAPI79XX_CHECK_HEALTH_FN "libcapi79xx_check_health" +#define LIBCAPI79XX_SET_TDD_FN "libcapi79xx_set_tdd" + #endif diff --git a/src/lib/hw/at24/at24.c b/src/lib/hw/at24/at24.c new file mode 100644 index 00000000..cf4c64fb --- /dev/null +++ b/src/lib/hw/at24/at24.c @@ -0,0 +1,29 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "at24.h" + +#include + +// int at24_1addr_mem_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, uint8_t addr, unsigned size, const uint8_t *pdata); +int at24_saddr_mem_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, uint8_t addr, unsigned size, uint8_t *pdata) +{ + int res = 0; + for (unsigned off = 0; off < size;) { + uint8_t odata[4] = { 0xff, 0xff, 0xff, 0xff }; + unsigned rem = size - off; + if (rem > 4) + rem = 4; + + uint8_t byte_addr = addr + off; + res = res ? res : lowlevel_ls_op(dev, subdev, USDR_LSOP_I2C_DEV, ls_op_addr, rem, odata, 1, &byte_addr); + + for (unsigned j = 0; j < rem; j++) + pdata[off + j] = odata[j]; + + off += rem; + } + + return res; +} + diff --git a/src/lib/hw/at24/at24.h b/src/lib/hw/at24/at24.h new file mode 100644 index 00000000..b1111d21 --- /dev/null +++ b/src/lib/hw/at24/at24.h @@ -0,0 +1,18 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef AT24_H +#define AT24_H + +#include + +enum { + AT24_SECURE_SERIAL_OFF = 0x80, + AT24_SECURE_USER_OFF = 0x90, +}; + +// single byte addres functions +int at24_saddr_mem_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, uint8_t addr, unsigned size, const uint8_t *pdata); +int at24_saddr_mem_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, uint8_t addr, unsigned size, uint8_t *pdata); + +#endif diff --git a/src/lib/hw/common/common.c b/src/lib/hw/common/common.c new file mode 100644 index 00000000..d506274d --- /dev/null +++ b/src/lib/hw/common/common.c @@ -0,0 +1,123 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "common.h" +#include "usdr_logging.h" +#include "../cal/opt_func.h" + +int common_ti_calc_sync_delay(uint32_t clkpos, unsigned* calced_delay) +{ + const unsigned WBSIZE = sizeof(uint32_t) * 8; + + char tmps[WBSIZE + 1]; + + binary_print_u32(clkpos, tmps, true /*reverse*/); + USDR_LOG("COMN", USDR_LOG_DEBUG, "WINDOW DATA:0b%s", tmps); + + //MSB & LSB should be 1 + if((clkpos & 0x80000001) != 0x80000001) + { + USDR_LOG("COMN", USDR_LOG_ERROR, "Window data is inconstent, check capture conditions"); + return -EINVAL; + } + + unsigned i = 0; + uint8_t idx_found[WBSIZE]; + + while(i < WBSIZE) + { + uint32_t v = clkpos >> i; + + if((v & 0b1111) == 0b1101 || (v & 0b1111) == 0b1011) + { + idx_found[i] = 1; + i += 4; + } + else if((v & 0b111) == 0b111) + { + idx_found[i] = 1; + i += 3; + } + else if((v & 0b11) == 0b11) + { + idx_found[i] = 1; + i += 2; + } + + idx_found[i++] = 0; + } + + uint8_t first_raise = 0xff, second_raise = 0xff; + for(unsigned i = 0; i < WBSIZE; ++i) + { + if(idx_found[i]) + { + if(first_raise == 0xff) + first_raise = i; + else + { + second_raise = i; + break; + } + } + } + + if(first_raise == 0xff || second_raise == 0xff) + { + USDR_LOG("COMN", USDR_LOG_ERROR, "Clock raise patterns not found, cannot determine delay"); + return -EINVAL; + } + + unsigned delay = (second_raise + first_raise) >> 1; + if(!delay || delay > 0x3f) + { + USDR_LOG("COMN", USDR_LOG_ERROR, "Invalid calculated delay:%u, out of range (0; 0x3f)", delay); + return -EINVAL; + } + USDR_LOG("COMN", USDR_LOG_DEBUG, "SYSREF vs CLOCK delay:%u", delay); + + *calced_delay = delay; + return 0; +} + +int common_print_registers_a8d16(uint32_t* regs, unsigned count, int loglevel) +{ + for (unsigned i = 0; i < count; i++) + { + uint8_t ra = regs[i] >> 16; + uint16_t rv = (uint16_t)regs[i]; + USDR_LOG("COMN", loglevel, "WRITE#%u: R%03u (0x%02x) -> 0x%04x [0x%06x]", i, ra, ra, rv, regs[i]); + } + + return 0; +} + +int common_spi_post(void* o, uint32_t* regs, unsigned count) +{ + int res; + const common_hw_state_struct_t* obj = (common_hw_state_struct_t*)o; + + for (unsigned i = 0; i < count; i++) { + res = lowlevel_spi_tr32(obj->dev, obj->subdev, obj->lsaddr, regs[i], NULL); + if (res) + return res; + + USDR_LOG("COMN", USDR_LOG_NOTE, "[%d/%d] reg wr %08x\n", i, count, regs[i]); + } + + return 0; +} + +int common_spi_get(void* o, uint32_t addr, uint16_t* out) +{ + uint32_t v; + const common_hw_state_struct_t* obj = (common_hw_state_struct_t*)o; + + int res = lowlevel_spi_tr32(obj->dev, obj->subdev, obj->lsaddr, addr, &v); + if (res) + return res; + + USDR_LOG("COMN", USDR_LOG_NOTE, " reg rd %04x => %08x\n", addr, v); + *out = v; + return 0; +} diff --git a/src/lib/hw/common/common.h b/src/lib/hw/common/common.h new file mode 100644 index 00000000..dcdc80d6 --- /dev/null +++ b/src/lib/hw/common/common.h @@ -0,0 +1,22 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT +#ifndef HW_COMMON_H +#define HW_COMMON_H + +#include "usdr_lowlevel.h" + +struct common_hw_state_struct +{ + lldev_t dev; + unsigned subdev; + unsigned lsaddr; +}; +typedef struct common_hw_state_struct common_hw_state_struct_t; + + +int common_ti_calc_sync_delay(uint32_t clkpos, unsigned* calced_delay); +int common_print_registers_a8d16(uint32_t* regs, unsigned count, int loglevel); +int common_spi_post(void* o, uint32_t* regs, unsigned count); +int common_spi_get(void* o, uint32_t addr, uint16_t* out); + +#endif // HW_COMMON_H diff --git a/src/lib/hw/lmk04832/lmk04832.c b/src/lib/hw/lmk04832/lmk04832.c index 5bc96473..408db7a3 100644 --- a/src/lib/hw/lmk04832/lmk04832.c +++ b/src/lib/hw/lmk04832/lmk04832.c @@ -10,7 +10,7 @@ #include enum { - REG_ID_DEVICE_TYPE = DEVICE_TYPE, + REG_ID_DEVICE_TYPE = LMKDEV_TYPE, REG_ID_PROD_HI = PROD_HI, REG_ID_PROD_LOW = PROD_LOW, REG_ID_MASKREV = MASKREV, @@ -335,7 +335,7 @@ pll2_configured:; MAKE_LMK04832_PLL2_N_LOW(pll2_n), 0x016959, //PLL2_DLD_EN - //SYSREF enable in continous from data clock + //SYSREF enable in continuous from data clock //0x013903, //0x013a00, //0x013b08, @@ -432,7 +432,7 @@ int lmk04832_sysref_div_set(lldev_t dev, subdev_t subdev, lsopaddr_t addr, 0x014310, 0x0144ff, - //SYSREF enable in continous from data clock + //SYSREF enable in continuous from data clock 0x013903, //DDIV diff --git a/src/lib/hw/lmk04832/lmk04832.yaml b/src/lib/hw/lmk04832/lmk04832.yaml index 29a2e501..ef055e9c 100644 --- a/src/lib/hw/lmk04832/lmk04832.yaml +++ b/src/lib/hw/lmk04832/lmk04832.yaml @@ -27,11 +27,11 @@ pages: bits: '4' desc: 3 Wire Mode disabled - addr: '0x0003' - name: DEVICE_TYPE + name: LMKDEV_TYPE fields: - - name: DEVICE_TYPE + - name: LMKDEV_TYPE bits: '7:0' - desc: DEVICE_TYPE + desc: LMKDEV_TYPE - addr: '0x0004' name: PROD_HI fields: diff --git a/src/lib/hw/lmk05318/lmk05318.c b/src/lib/hw/lmk05318/lmk05318.c index 2db8ac81..280584b6 100644 --- a/src/lib/hw/lmk05318/lmk05318.c +++ b/src/lib/hw/lmk05318/lmk05318.c @@ -1,29 +1,334 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT +#include #include #include +#include +#include +#include +#include -#include "lmk05318.h" #include "def_lmk05318.h" -#include "lmk05318_rom.h" +#include "lmk05318.h" #include +#include "../../xdsp/attribute_switch.h" +#include "../cal/opt_func.h" + +//define this for solver extra logging +#undef LMK05318_SOLVER_DEBUG + +//remove this to enable additional checks for dpll mode +#define DISABLE_ADDITIONAL_DPLL_CHECKS enum { VCO_APLL1 = 2500000000ull, + VCO_APLL1_DELTA_MAX = 250000, + VCO_APLL1_MIN = VCO_APLL1 - VCO_APLL1_DELTA_MAX, + VCO_APLL1_MAX = VCO_APLL1 + VCO_APLL1_DELTA_MAX, + VCO_APLL2_MIN = 5500000000ull, VCO_APLL2_MAX = 6250000000ull, APLL1_PD_MIN = 1000000, - APLL1_PD_MAX = 50000000, + APLL1_PD_MAX = 80000000, APLL2_PD_MIN = 10000000, APLL2_PD_MAX = 150000000, - OUT_FREQ_MAX = 800000000ull, + OUT_FREQ_MAX = 1250000000ull, + + XO_FREF_MAX = 100000000ull, + XO_FREF_MIN = 10000000ull, + + APLL1_DIVIDER_MIN = 1, + APLL1_DIVIDER_MAX = 32, + + APLL2_PDIV_MIN = 2, + APLL2_PDIV_MAX = 7, + APLL2_PDIV_COUNT = 2, //PD1 & PD2 + + OUTDIV7_STAGE2_MAX = (uint64_t)1 << 24, + OUTDIV7_STAGE1_MAX = (uint64_t)1 << 8, + OUTDIV7_STAGE1_WITH_ST2_MIN = 6, + + F_TDC_MIN = 1, + F_TDC_MAX = 26000000, + + DPLL_REF_R_DIV_MIN = 1, + DPLL_REF_R_DIV_MAX = UINT16_MAX - 1, + + DPLL_PRE_DIV_MIN = 2, + DPLL_PRE_DIV_MAX = 17, +}; + +enum +{ + SDM_DITHER_MODE_WEAK = 0x0, + SDM_DITHER_MODE_MEDIUM = 0x1, + SDM_DITHER_MODE_STRONG = 0x2, + SDM_DITHER_MODE_DISABLED = 0x3, +}; + +enum +{ + SDM_ORDER_INT = 0x0, + SDM_ORDER_FIRST = 0x1, + SDM_ORDER_SECOND = 0x2, + SDM_ORDER_THIRD = 0x3, + SDM_ORDER_FORTH = 0x4, +}; + +enum +{ + XO12_8 = 12800000, + XO25 = 25000000, + XO26 = 26000000, + XO52 = 52000000, +}; + +#define DPLL_FDIV_FRAC_MAX 0.9375f +#define DPLL_FDIV_FRAC_MIN 0.0625f +#define DPLL_MIN_BAW_DIFF 2500000ul + +struct range +{ + uint64_t min, max; }; +typedef struct range range_t; + +static const char* lmk05318_dpll_decode_ref_dc_mode(enum lmk05318_ref_dc_mode_t m) +{ + switch(m) + { + case REF_DC_MODE_AC_COUPLED_INT: return "AC_COUPLED_INT"; + case REF_DC_MODE_DC_COUPLED_INT: return "DC_COUPLED_INT"; + } + + return "UNKNOWN"; +} + +static const char* lmk05318_dpll_decode_ref_buf_mode(enum lmk05318_ref_buf_mode_t b) +{ + switch(b) + { + case REF_BUF_MODE_AC_HYST50_DC_EN: return "AC_HYST50_DC_EN"; + case REF_BUF_MODE_AC_HYST200_DC_DIS: return "AC_HYST200_DC_DIS"; + } + + return "UNKNOWN"; +} + +static const char* lmk05318_dpll_decode_ref_type(enum lmk05318_ref_input_type_t t) +{ + switch(t) + { + case REF_INPUT_TYPE_DIFF_NOTERM: return "DIFF_NOTERM"; + case REF_INPUT_TYPE_DIFF_100: return "DIFF_100"; + case REF_INPUT_TYPE_DIFF_50: return "DIFF_50"; + case REF_INPUT_TYPE_SE_NOTERM: return "SE_NOTERM"; + case REF_INPUT_TYPE_SE_50: return "SE_50"; + } + + return "UNKNOWN"; +} + +int lmk05318_dpll_config(lmk05318_state_t* d, lmk05318_dpll_settings_t* dpll) +{ + if(!dpll || !dpll->enabled) + { + d->dpll.enabled = false; + USDR_LOG("5318", USDR_LOG_INFO, "[DPLL] DPLL disabled"); + return 0; + } + + //Validate REF inputs + for(unsigned i = LMK05318_PRIREF; i <= LMK05318_SECREF; ++i) + { + if(!dpll->en[i]) + continue; + + switch(dpll->dc_mode[i]) + { + case DPLL_REF_AC_COUPLED_INT: dpll->dc_mode[i] = REF_DC_MODE_AC_COUPLED_INT; break; + case DPLL_REF_DC_COUPLED_INT: dpll->dc_mode[i] = REF_DC_MODE_DC_COUPLED_INT; break; + default: + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] %sREF DC_MODE:%u unsupported", + (i == LMK05318_PRIREF) ? "PRI" : "SEC", dpll->dc_mode[i]); + return -EINVAL; + } + + switch(dpll->buf_mode[i]) + { + case DPLL_REF_AC_BUF_HYST50_DC_EN: dpll->buf_mode[i] = REF_BUF_MODE_AC_HYST50_DC_EN; break; + case DPLL_REF_AC_BUF_HYST200_DC_DIS: dpll->buf_mode[i] = REF_BUF_MODE_AC_HYST200_DC_DIS; break; + default: + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] %sREF BUF_MODE:%u unsupported", + (i == LMK05318_PRIREF) ? "PRI" : "SEC", dpll->buf_mode[i]); + return -EINVAL; + } + + switch(dpll->type[i]) + { + case DPLL_REF_TYPE_DIFF_NOTERM: dpll->type[i] = REF_INPUT_TYPE_DIFF_NOTERM; break; + case DPLL_REF_TYPE_DIFF_100: dpll->type[i] = REF_INPUT_TYPE_DIFF_100; break; + case DPLL_REF_TYPE_DIFF_50: dpll->type[i] = REF_INPUT_TYPE_DIFF_50; break; + case DPLL_REF_TYPE_SE_NOTERM: dpll->type[i] = REF_INPUT_TYPE_SE_NOTERM; break; + case DPLL_REF_TYPE_SE_50: dpll->type[i] = REF_INPUT_TYPE_SE_50; break; + default: + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] %sREF TYPE:%u unsupported", + (i == LMK05318_PRIREF) ? "PRI" : "SEC", dpll->type[i]); + return -EINVAL; + } + } + + d->dpll.enabled = true; + + if(dpll->en[LMK05318_PRIREF] && dpll->en[LMK05318_SECREF]) + { + unsigned max_ref_id, min_ref_id; + if(dpll->fref[LMK05318_PRIREF] > dpll->fref[LMK05318_SECREF]) + { + max_ref_id = LMK05318_PRIREF; min_ref_id = LMK05318_SECREF; + } else + { + max_ref_id = LMK05318_SECREF; min_ref_id = LMK05318_PRIREF; + } + + uint64_t max_div = dpll->fref[max_ref_id]; + uint64_t min_div = dpll->fref[min_ref_id]; + uint64_t gcd = find_gcd(min_div, max_div); + if(gcd > 1) + { + min_div /= gcd; + max_div /= gcd; + } + + const unsigned min_div_required = ceil((double)dpll->fref[max_ref_id] / F_TDC_MAX); + + if(max_div > DPLL_REF_R_DIV_MAX || min_div < min_div_required) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] incorrect PRIREF/SECREF ratio (%.2f)", (double)min_div / max_div); + return -EINVAL; + } + + d->dpll.ref_en[LMK05318_PRIREF] = d->dpll.ref_en[LMK05318_SECREF] = true; + d->dpll.rdiv[min_ref_id] = min_div; + d->dpll.rdiv[max_ref_id] = max_div; + d->dpll.ftdc = (double)dpll->fref[min_ref_id] / d->dpll.rdiv[min_ref_id]; + + const double ftdc2 = (double)dpll->fref[max_ref_id] / d->dpll.rdiv[max_ref_id]; + if(fabs(ftdc2 - d->dpll.ftdc) > 1E-6) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] PRIREF/SECREF cannot be resolved to one TDC (%.6f != %.6f)", d->dpll.ftdc, ftdc2); + } + } + else if(dpll->en[LMK05318_PRIREF] || dpll->en[LMK05318_SECREF]) + { + uint8_t id = dpll->en[LMK05318_PRIREF] ? LMK05318_PRIREF : LMK05318_SECREF; + uint64_t div = ceil((double)dpll->fref[id] / F_TDC_MAX); + if(div > DPLL_REF_R_DIV_MAX) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] PRIREF or SECREF value too high"); + return -EINVAL; + } + + d->dpll.ref_en[id] = true; + d->dpll.rdiv[id] = div; + d->dpll.ftdc = (double)dpll->fref[id] / div; + } + else + { + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] PRIREF and SECREF are disabled, cannot configure DPLL"); + return -EINVAL; + } + + USDR_LOG("5318", USDR_LOG_DEBUG, "[DPLL] PRIREF:%" PRIu64 " EN:%u RDIV:%u", + dpll->fref[LMK05318_PRIREF], d->dpll.ref_en[LMK05318_PRIREF], d->dpll.rdiv[LMK05318_PRIREF]); + USDR_LOG("5318", USDR_LOG_DEBUG, "[DPLL] SECREF:%" PRIu64 " EN:%u RDIV:%u", + dpll->fref[LMK05318_SECREF], d->dpll.ref_en[LMK05318_SECREF], d->dpll.rdiv[LMK05318_SECREF]); + USDR_LOG("5318", USDR_LOG_DEBUG, "[DPLL] FTDC:%.8f", d->dpll.ftdc); + + + const uint64_t max_fbdiv = ((uint64_t)1 << 30) - 1; + const uint64_t min_fbdiv = 1; + + unsigned max_pre_div = MIN(VCO_APLL1 / d->dpll.ftdc / 2 / min_fbdiv, DPLL_PRE_DIV_MAX); + unsigned min_pre_div = MAX(VCO_APLL1 / d->dpll.ftdc / 2 / max_fbdiv, DPLL_PRE_DIV_MIN); + if(max_pre_div < min_pre_div) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] cannot calculate PRE_DIV"); + return -EINVAL; + } + + const unsigned pre_div = max_pre_div; + double fbdiv = (double)VCO_APLL1 / d->dpll.ftdc / 2.0 / pre_div; + USDR_LOG("5318", USDR_LOG_DEBUG, "[DPLL] PRE_DIV:%u FB_DIV:%.8f", pre_div, fbdiv); + if(fbdiv < min_fbdiv || fbdiv > max_fbdiv) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] FB_DIV:%.8f out of range", fbdiv); + return -EINVAL; + } + + uint32_t fb_int = (uint32_t)fbdiv; + double fb_frac = fbdiv - fb_int; + uint64_t fb_den = VCO_APLL1 * 2 * pre_div; //(uint64_t)1 << 40; + uint64_t fb_num = (uint64_t)(fb_frac * fb_den + 0.5); + uint64_t gcd = find_gcd(fb_num, fb_den); + if(gcd > 1) + { + fb_num /= gcd; + fb_den /= gcd; + } + + //check + const double vco1_fact = d->dpll.ftdc * 2.0 * pre_div * (fb_int + (double)fb_num / fb_den); + const double delta = fabs((double)VCO_APLL1 - vco1_fact); + USDR_LOG("5318", USDR_LOG_DEBUG, "[DPLL] N:%u NUM:%" PRIu64 " DEN:%" PRIu64 " VCO1_FACT:%.8f DELTA:%.8fHz", + fb_int, fb_num, fb_den, vco1_fact, delta); + if(delta > 1E-4) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[DPLL] VCO1_FACT:%.8f too rough", vco1_fact); + return -EINVAL; + } + + d->dpll.pre_div = pre_div; + d->dpll.n = fb_int; + d->dpll.num = fb_num; + d->dpll.den = fb_den; + + //DPLL BW + if((dpll->en[LMK05318_PRIREF] && dpll->fref[LMK05318_PRIREF] == 1) || (dpll->en[LMK05318_SECREF] && dpll->fref[LMK05318_SECREF] == 1)) + { + d->dpll.lbw = 0.01; + } + else + d->dpll.lbw = 100; + + USDR_LOG("5318", USDR_LOG_DEBUG, "[DPLL] LBW:%.2fHz", d->dpll.lbw); + + //log + for(unsigned i = LMK05318_PRIREF; i <= LMK05318_SECREF; ++i) + { + const char* nm = i == LMK05318_PRIREF ? "PRIREF" : "SECREF"; + if(d->dpll.ref_en[i]) + USDR_LOG("5318", USDR_LOG_INFO, "[DPLL] %s:enabled FREF:%" PRIu64 " DC_MODE:%u(%s) BUF_MODE:%u(%s) TYPE:%u(%s) RDIV:%u", + nm, + dpll->fref[i], + dpll->dc_mode[i], lmk05318_dpll_decode_ref_dc_mode((enum lmk05318_ref_dc_mode_t)dpll->dc_mode[i]), + dpll->buf_mode[i], lmk05318_dpll_decode_ref_buf_mode((enum lmk05318_ref_buf_mode_t)dpll->buf_mode[i]), + dpll->type[i], lmk05318_dpll_decode_ref_type((enum lmk05318_ref_input_type_t)dpll->type[i]), + d->dpll.rdiv[i]); + else + USDR_LOG("5318", USDR_LOG_INFO, "[DPLL] %s:disabled", nm); + } + USDR_LOG("5318", USDR_LOG_INFO, "[DPLL] FTDC:%.4f N:%" PRIu64" NUM:%" PRIu64" DEN:%" PRIu64 " PRE_DIV:%u LBW:%.2fHz VCO1:%.8f", + d->dpll.ftdc, d->dpll.n, d->dpll.num, d->dpll.den, d->dpll.pre_div, d->dpll.lbw, vco1_fact); + + return 0; +} int lmk05318_reg_wr(lmk05318_state_t* d, uint16_t reg, uint8_t out) { @@ -65,177 +370,2209 @@ int lmk05318_reg_wr_n(lmk05318_state_t* d, const uint32_t* regs, unsigned count) return 0; } -int lmk05318_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned int flags, lmk05318_state_t* out) + +enum { - int res; - uint8_t dummy[4]; + LMK05318_REGADDR_MIN = 0x0000, + LMK05318_REGADDR_MAX = 0x019B, +}; - const uint32_t* lmk_init = flags ? lmk05318_rom_49152_12288_384 : lmk05318_rom; - unsigned lmk_init_sz = flags ? SIZEOF_ARRAY(lmk05318_rom_49152_12288_384) : SIZEOF_ARRAY(lmk05318_rom); +static uint32_t registers_map[LMK05318_REGADDR_MAX - LMK05318_REGADDR_MIN + 1]; - out->dev = dev; - out->subdev = subdev; - out->lsaddr = lsaddr; +void lmk05318_registers_map_reset() +{ + memset(registers_map, 0xFF, sizeof(registers_map)); +} - res = lmk05318_reg_get_u32(out, 0, &dummy[0]); - if (res) - return res; +static int lmk05318_add_reg_to_map(lmk05318_state_t* d, const uint32_t* regs, unsigned count) +{ + for (unsigned j = 0; j < count; j++) + { + const uint16_t regaddr = (uint16_t)(regs[j] >> 8) & ~0x8000; + if(regaddr < LMK05318_REGADDR_MIN || regaddr > LMK05318_REGADDR_MAX) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 REGADDR 0x%04x out of range", regaddr); + return -EINVAL; + } - USDR_LOG("5318", USDR_LOG_INFO, "LMK05318 DEVID[0/1/2/3] = %02x %02x %02x %02x\n", dummy[3], dummy[2], dummy[1], dummy[0]); + const unsigned idx = regaddr - LMK05318_REGADDR_MIN; + uint32_t* ptr = registers_map + idx; + if(*ptr != (uint32_t)(-1) && (uint8_t)(*ptr) != (uint8_t)regs[j]) + { + USDR_LOG("5318", USDR_LOG_WARNING, "LMK05318 Rewriting REGADDR 0x%04x : 0x%02x -> 0x%02x", regaddr, (uint8_t)(*ptr), (uint8_t)regs[j]); + } + *ptr = regs[j]; + } + return 0; +} - if ( dummy[3] != 0x10 || dummy[2] != 0x0b || dummy[1] != 0x35 || dummy[0] != 0x42 ) { - return -ENODEV; +int lmk05318_reg_wr_from_map(lmk05318_state_t* d, bool dry_run) +{ + for(unsigned j = 0; j < SIZEOF_ARRAY(registers_map); ++j) + { + if(registers_map[j] == (uint32_t)(-1)) + continue; + + uint16_t addr = registers_map[j] >> 8; + uint8_t data = registers_map[j]; + + USDR_LOG("5318", USDR_LOG_DEBUG, "LMK05318 Writing register R%03u: 0x%04x = 0x%02x [0x%06x]", j, addr, data, registers_map[j]); + + int res = dry_run ? 0 : lmk05318_reg_wr(d, addr, data); + if (res) + return res; } - // Do the initialization - res = lmk05318_reg_wr_n(out, lmk_init, lmk_init_sz); - if (res) - return res; + lmk05318_registers_map_reset(); + return 0; +} - // Reset - uint32_t regs[] = { - lmk05318_rom[0] | (1 << RESET_SW_OFF), - lmk05318_rom[0] | (0 << RESET_SW_OFF), - MAKE_LMK05318_XO_CONFIG(flags > 1 ? 1 : 0), +static int lmk05318_trigger_reg_with_sleep(lmk05318_state_t* out, uint32_t regs[2]) +{ + int res = lmk05318_reg_wr_n(out, ®s[0], 1); + if(res) + return res; - MAKE_LMK05318_PLL1_CTRL0(0), - MAKE_LMK05318_PLL1_CTRL0(1), - MAKE_LMK05318_PLL1_CTRL0(0), + usleep(10000); - }; - res = lmk05318_reg_wr_n(out, regs, SIZEOF_ARRAY(regs)); - if (res) + res = lmk05318_reg_wr_n(out, ®s[1], 1); + if(res) return res; - out->fref_pll2_div_rp = 3; - out->fref_pll2_div_rs = (((VCO_APLL1 + APLL2_PD_MAX - 1) / APLL2_PD_MAX) + out->fref_pll2_div_rp - 1) / out->fref_pll2_div_rp; - USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 initialized\n"); - return 0; + usleep(10000); + return res; } - -int lmk05318_tune_apll2(lmk05318_state_t* d, uint32_t freq, unsigned *last_div) +UNUSED static int lmk05318_trigger_reg(lmk05318_state_t* out, uint32_t regs[2]) { - const unsigned pre_div = 2; - unsigned fref = VCO_APLL1 / d->fref_pll2_div_rp / d->fref_pll2_div_rs; - if (fref < APLL2_PD_MIN || fref > APLL2_PD_MAX) { - USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 APLL2 PFD should be in range [%" PRIu64 ";%" PRIu64 "] but got %d!\n", - (uint64_t)APLL2_PD_MIN, (uint64_t)APLL2_PD_MAX, fref); - return -EINVAL; - } - if (freq < 1e6) { - // Disable - uint32_t regs[] = { - MAKE_LMK05318_PLL2_CTRL0(d->fref_pll2_div_rs - 1, d->fref_pll2_div_rp - 3, 1), - }; - return lmk05318_reg_wr_n(d, regs, SIZEOF_ARRAY(regs));; - } + return lmk05318_reg_wr_n(out, ®s[0], 2); +} - unsigned div = ((VCO_APLL2_MAX / pre_div)) / freq; - uint64_t fvco = (uint64_t)freq * div * pre_div; - unsigned n = fvco / fref; - unsigned num = (fvco - n * (uint64_t)fref) * (1ull << 24) / fref; - int res; - USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 FREQ=%u FVCO=%lld N=%d NUM=%d DIV=%d\n", freq, (long long)fvco, n, num, div); +int lmk05318_softreset(lmk05318_state_t* out) +{ + uint8_t reg_ctrl; + const uint8_t mask = ((uint8_t)1 << RESET_SW_OFF); - uint32_t regs[] = { - MAKE_LMK05318_PLL2_CTRL0(d->fref_pll2_div_rs - 1, d->fref_pll2_div_rp - 3, 1), - MAKE_LMK05318_PLL2_CTRL2(pre_div - 1, pre_div - 1), - MAKE_LMK05318_PLL2_NDIV_BY0(n), - MAKE_LMK05318_PLL2_NDIV_BY1(n), - MAKE_LMK05318_PLL2_NUM_BY0(num), - MAKE_LMK05318_PLL2_NUM_BY1(num), - MAKE_LMK05318_PLL2_NUM_BY2(num), - MAKE_LMK05318_PLL2_CTRL0(d->fref_pll2_div_rs - 1, d->fref_pll2_div_rp - 3, 0), + int res = lmk05318_reg_rd(out, DEV_CTL, ®_ctrl); + if(res) + return res; + + uint32_t regs[2] = + { + MAKE_LMK05318_REG_WR(DEV_CTL, reg_ctrl | mask), + MAKE_LMK05318_REG_WR(DEV_CTL, reg_ctrl & ~mask), }; - res = lmk05318_reg_wr_n(d, regs, SIZEOF_ARRAY(regs)); - if (res) + return lmk05318_trigger_reg_with_sleep(out, regs); +} + +int lmk05318_sync(lmk05318_state_t* out) +{ + uint8_t reg_ctrl; + const uint8_t mask = ((uint8_t)1 << SYNC_SW_OFF); + + int res = lmk05318_reg_rd(out, DEV_CTL, ®_ctrl); + if(res) return res; - *last_div = div; - return 0; + uint32_t regs[2] = + { + MAKE_LMK05318_REG_WR(DEV_CTL, reg_ctrl | mask), + MAKE_LMK05318_REG_WR(DEV_CTL, reg_ctrl & ~mask), + }; + + return lmk05318_trigger_reg_with_sleep(out, regs); } +int lmk05318_mute(lmk05318_state_t* out, uint8_t chmask) +{ + for(unsigned ch = 0; ch < 8; ++ch) + { + bool muted = ((chmask >> ch) & 0x1) == 0x1; + if(muted) + { + USDR_LOG("5318", USDR_LOG_WARNING, "LMK05318 OUT CH%u is MUTED", ch); + } + } + + uint32_t reg = MAKE_LMK05318_REG_WR(OUT_MUTE, chmask); + return lmk05318_reg_wr_n(out, ®, 1); +} -int lmk05318_set_out_div(lmk05318_state_t* d, unsigned port, unsigned udiv) +int lmk05318_reset_los_flags(lmk05318_state_t* d) { - if (port > 7) - return -EINVAL; - if (udiv == 0) - return -EINVAL; + uint32_t regs[] = + { + MAKE_LMK05318_INT_FLAG0(0,0,0,0), //R19 + MAKE_LMK05318_INT_FLAG1(0,0,0,0,0,0,0,0), //R20 + }; - unsigned div = udiv - 1; - uint32_t regs[] = { - (port == 7) ? MAKE_LMK05318_OUTDIV_7(div) : - (port == 6) ? MAKE_LMK05318_OUTDIV_6(div) : - (port == 5) ? MAKE_LMK05318_OUTDIV_5(div) : - (port == 4) ? MAKE_LMK05318_OUTDIV_4(div) : - (port == 3 || port == 2) ? MAKE_LMK05318_OUTDIV_2_3(div) : MAKE_LMK05318_OUTDIV_0_1(div), - }; return lmk05318_reg_wr_n(d, regs, SIZEOF_ARRAY(regs)); } -int lmk05318_set_out_mux(lmk05318_state_t* d, unsigned port, bool pll1, unsigned otype) +static int lmk05318_set_xo_bawdetect_registers(lmk05318_state_t* d) { - unsigned ot; - switch (otype) { - case LVDS: ot = OUT_OPTS_AC_LVDS; break; - case CML: ot = OUT_OPTS_AC_CML; break; - case LVPECL: ot = OUT_OPTS_AC_LVPECL; break; - case LVCMOS: ot = OUT_OPTS_LVCMOS_P_N; break; - default: ot = OUT_OPTS_Disabled; break; + int res = 0; + + switch(d->xo.fref) + { + case XO12_8: + { + static uint32_t regs[] = + { + 0x00510A, + 0x005200, + 0x005307, + 0x005480, + 0x005500, + 0x005600, + 0x00571E, + 0x005884, + 0x005980, + 0x005A00, + 0x005B14, + 0x005C00, + 0x005D07, + 0x005E80, + 0x005F00, + 0x006000, + 0x00611E, + 0x006284, + 0x006380, + }; + + res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + break; } - unsigned mux = (pll1) ? OUT_PLL_SEL_APLL1_P1 : OUT_PLL_SEL_APLL2_P1; + case XO25: + { + static uint32_t regs[] = + { + 0x00510A, //R81 + 0x005200, // | + 0x00530E, // | + 0x0054A6, // | + 0x005500, // | + 0x005600, // | + 0x00571E, // | + 0x005884, // | + 0x005980, // | BAW lock&unlock detection, may depend on XO params + 0x005A00, // | + 0x005B14, // | + 0x005C00, // | + 0x005D0E, // | + 0x005EA6, // | + 0x005F00, // | + 0x006000, // | + 0x00611E, // | + 0x006284, // | + 0x006380, //R99 + }; - if (port > 7) + res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + break; + } + case XO26: + case XO52: // both these XOs result in FPD1=52M, so we can use just the same setting + { + static uint32_t regs[] = + { + 0x00510A, + 0x005200, + 0x00530F, + 0x00543C, + 0x005500, + 0x005600, + 0x00571E, + 0x005884, + 0x005980, + 0x005A00, + 0x005B14, + 0x005C00, + 0x005D0F, + 0x005E3C, + 0x005F00, + 0x006000, + 0x00611E, + 0x006284, + 0x006380, + }; + + res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + break; + } + default: + { + USDR_LOG("5318", USDR_LOG_ERROR, "XO=%.2fMHz not supported! Use 12.8, 25, 26 or 52M", (double)d->xo.fref / 1e6); return -EINVAL; + } + } - uint32_t regs[] = { - (port == 0) ? MAKE_LMK05318_OUTCTL_0(mux, ot) : - (port == 1) ? MAKE_LMK05318_OUTCTL_1(ot) : - (port == 2) ? MAKE_LMK05318_OUTCTL_2(mux, ot) : - (port == 3) ? MAKE_LMK05318_OUTCTL_3(ot) : - (port == 4) ? MAKE_LMK05318_OUTCTL_4(mux, ot) : - (port == 5) ? MAKE_LMK05318_OUTCTL_5(mux, ot) : - (port == 6) ? MAKE_LMK05318_OUTCTL_6(mux, ot) : MAKE_LMK05318_OUTCTL_7(mux, ot), - }; - return lmk05318_reg_wr_n(d, regs, SIZEOF_ARRAY(regs)); + USDR_LOG("5318", USDR_LOG_INFO, "XO=%.2fMHz, applying specific settings...", (double)d->xo.fref / 1e6); + + return res; } -int lmk05318_check_lock(lmk05318_state_t* d, unsigned* los_msk) +static int lmk05318_set_common_registers(lmk05318_state_t* d, lmk05318_dpll_settings_t* dpll) { - uint8_t los[3]; int res = 0; - unsigned losval; - res = res ? res : lmk05318_reg_rd(d, INT_FLAG0, &los[0]); - res = res ? res : lmk05318_reg_rd(d, INT_FLAG1, &los[1]); - res = res ? res : lmk05318_reg_rd(d, BAW_LOCKDET_PPM_MAX_BY1, &los[2]); + if(!dpll) + { + d->dpll.enabled = false; + } - if (res) + if(d->dpll.enabled == false) + { + // WITHOUT DPLL + uint32_t no_dpll_regs[] = + { + MAKE_LMK05318_DEV_CTL(0, 0, 0/*SYNC_AUTO_DPLL*/, 1, 1, 1, 1), //R12 set APLL1 mode - NO DPLL + MAKE_LMK05318_SPARE_NVMBASE2_BY2(0, 1/*Programmed 24-bit DEN*/), //R39 set fixed APLL1 denumerator for DPLL en, programmed den otherwise + MAKE_LMK05318_SPARE_NVMBASE2_BY1(0, 0, 1), //R40 set programmed APPL2 denumerator always + MAKE_LMK05318_DPLL_GEN_CTL(0, 0, 0, 0, 0, 0, 0), //R252 disable DPLL + MAKE_LMK05318_PLL1_CALCTRL0(1, 0, 1), //R79 BAW_LOCKDET_EN=1 PLL1_VCOWAIT=1 + MAKE_LMK05318_BAW_LOCKDET_PPM_MAX_BY1(1, 0), //R80 BAW_LOCK=1 + }; + res = lmk05318_add_reg_to_map(d, no_dpll_regs, SIZEOF_ARRAY(no_dpll_regs)); + } + else + { + //WITH DPLL + const bool one_pps[] = + { + (dpll->en[LMK05318_PRIREF] && dpll->fref[LMK05318_PRIREF] == 1), + (dpll->en[LMK05318_SECREF] && dpll->fref[LMK05318_SECREF] == 1), + }; + + const bool lt2k[] = + { + !one_pps[LMK05318_PRIREF] && (dpll->en[LMK05318_PRIREF] && dpll->fref[LMK05318_PRIREF] < 2000), + !one_pps[LMK05318_SECREF] && (dpll->en[LMK05318_SECREF] && dpll->fref[LMK05318_SECREF] < 2000), + }; + + const bool ge2k[] = + { + !one_pps[LMK05318_PRIREF] && !lt2k[LMK05318_PRIREF], + !one_pps[LMK05318_SECREF] && !lt2k[LMK05318_SECREF], + }; + + unsigned meas_time[] = + { + (unsigned)(log2f(10000.f / dpll->fref[LMK05318_PRIREF]) + 2.5), + (unsigned)(log2f(10000.f / dpll->fref[LMK05318_SECREF]) + 2.5), + }; + + //this value is empirical and should be clarified + uint32_t phase_valid_detection[] = + { + dpll->en[LMK05318_PRIREF] ? 50000000 : 0, + dpll->en[LMK05318_SECREF] ? 50000000 : 0, + }; + + //this value is empirical and should be clarified + uint64_t ref_cycslip_offset = 5000000000; + + //this value is empirical and should be clarified + uint16_t ref_filter_scalar = 678; + + //this value is empirical and should be clarified + uint8_t ref_quant = 10; + + uint8_t dpll_sdm_order = d->dpll.num ? SDM_ORDER_THIRD : SDM_ORDER_INT; + uint8_t dpll_sdm_dither = SDM_DITHER_MODE_WEAK; + USDR_LOG("5318", USDR_LOG_INFO, "[DPLL] MASH_ORD:%u", dpll_sdm_order); + + //this value is empirical and should be clarified + uint64_t dco_lock_det0 = 0x000a000000; + uint64_t dco_lock_det1 = 0x010635750b; + uint32_t dco_unlock_det0 = 0x006400; + uint32_t dco_unlock_det1 = 0x063575; + + //detect ZDM mode -> + // if 1) OUT7 = 1Hz + // 2) DPLL input ref = 1Hz + // 3) OUT2 routed via APLL1 + const lmk05318_output_t* p7 = &d->outputs[LMK05318_MAX_OUT_PORTS - 1]; + d->dpll.zdm = p7->freq == 1.0 && (p7->mux == OUT_PLL_SEL_APLL1_P1_INV || p7->mux == OUT_PLL_SEL_APLL1_P1); + d->dpll.zdm &= (d->dpll.ref_en[LMK05318_PRIREF] && dpll->fref[LMK05318_PRIREF] == 1) + || + (d->dpll.ref_en[LMK05318_SECREF] && dpll->fref[LMK05318_SECREF] == 1); + + USDR_LOG("5318", USDR_LOG_INFO, "[DPLL] ZDM %s", d->dpll.zdm ? "enabled" : "disabled"); + + uint32_t dpll_regs[] = + { + MAKE_LMK05318_DEV_CTL(0, 0, 1/*SYNC_AUTO_DPLL*/, 1, 1, 1, 1), //R12 set APLL1 mode - Free-run + MAKE_LMK05318_SPARE_NVMBASE2_BY2(0, 0/*Fixed 40-bit DEN*/), //R39 set fixed APLL1 denumerator for DPLL en, programmed den otherwise + MAKE_LMK05318_SPARE_NVMBASE2_BY1(dpll->dc_mode[LMK05318_SECREF], dpll->dc_mode[LMK05318_PRIREF], 1), //R40 set programmed APPL2 denumerator always + MAKE_LMK05318_REF_CLKCTL1(!dpll->en[LMK05318_SECREF] || (dpll->fref[LMK05318_SECREF] >= 5000000 && dpll->type[LMK05318_SECREF] != IN_OPTS_CMOS && dpll->type[LMK05318_SECREF] != IN_OPTS_SE_INT_50) ? 0 : 1, + !dpll->en[LMK05318_PRIREF] || (dpll->fref[LMK05318_PRIREF] >= 5000000 && dpll->type[LMK05318_PRIREF] != IN_OPTS_CMOS && dpll->type[LMK05318_PRIREF] != IN_OPTS_SE_INT_50) ? 0 : 1, + dpll->en[LMK05318_SECREF] ? dpll->buf_mode[LMK05318_SECREF] : DPLL_REF_AC_BUF_HYST200_DC_DIS, + dpll->en[LMK05318_PRIREF] ? dpll->buf_mode[LMK05318_PRIREF] : DPLL_REF_AC_BUF_HYST200_DC_DIS), //R45 + MAKE_LMK05318_REF_CLKCTL2(dpll->type[LMK05318_SECREF], dpll->type[LMK05318_PRIREF]), //R46 + + MAKE_LMK05318_PLL1_CALCTRL0(0, 0, 1), //R79 BAW_LOCKDET_EN=0 PLL1_VCOWAIT=1 + MAKE_LMK05318_BAW_LOCKDET_PPM_MAX_BY1(0, 0), //R80 BAW_LOCK=0 + + dpll->en[LMK05318_PRIREF] ? + MAKE_LMK05318_REF0_DETEN(ge2k[LMK05318_PRIREF], + lt2k[LMK05318_PRIREF] || one_pps[LMK05318_PRIREF], + 1, //validation timer en + ge2k[LMK05318_PRIREF], + ge2k[LMK05318_PRIREF], + 1 /* amp_det_en ge2k[LMK05318_PRIREF]*/) : + MAKE_LMK05318_REF0_DETEN(0,0,0,0,0,0), //R193 + + dpll->en[LMK05318_SECREF] ? + MAKE_LMK05318_REF1_DETEN(ge2k[LMK05318_SECREF], + lt2k[LMK05318_SECREF] || one_pps[LMK05318_SECREF], + 1, //validation timer en, + ge2k[LMK05318_SECREF], + ge2k[LMK05318_SECREF], + 1 /* amp_det_en ge2k[LMK05318_SECREF]*/) : + MAKE_LMK05318_REF1_DETEN(0,0,0,0,0,0), //R194 + + MAKE_LMK05318_REG_WR(REF0_VLDTMR, meas_time[LMK05318_PRIREF] & 0b00011111), //R233 + MAKE_LMK05318_REG_WR(REF1_VLDTMR, meas_time[LMK05318_SECREF] & 0b00011111), //R234 + + MAKE_LMK05318_REF0_PH_VALID_THR(lt2k[LMK05318_PRIREF] || one_pps[LMK05318_PRIREF] ? 63 : 0), //R243 *********TODO + MAKE_LMK05318_REF1_PH_VALID_THR(lt2k[LMK05318_SECREF] || one_pps[LMK05318_SECREF] ? 63 : 0), //R244 *********TODO + + MAKE_LMK05318_DPLL_REF01_PRTY(2, 1), //R249 set PRIREF 1st, SECREF 2nd priority + MAKE_LMK05318_DPLL_REF_SWMODE(0, + (d->dpll.ref_en[LMK05318_PRIREF] ? LMK05318_PRIREF : LMK05318_SECREF), + (d->dpll.ref_en[LMK05318_PRIREF] && d->dpll.ref_en[LMK05318_SECREF]) ? 0x0 : 0x3), //R251 + MAKE_LMK05318_DPLL_GEN_CTL(d->dpll.zdm ? 1 : 0, 0, 1/*DPLL_SWITCHOVER_ALWAYS*/, + !one_pps[LMK05318_PRIREF] && !one_pps[LMK05318_SECREF], 1, 0, 1), //R252 enable ZDM & enable DPLL + MAKE_LMK05318_DPLL_REF0_RDIV_BY0(d->dpll.rdiv[LMK05318_PRIREF]), //R256 + MAKE_LMK05318_DPLL_REF0_RDIV_BY1(d->dpll.rdiv[LMK05318_PRIREF]), + MAKE_LMK05318_DPLL_REF1_RDIV_BY0(d->dpll.rdiv[LMK05318_SECREF]), + MAKE_LMK05318_DPLL_REF1_RDIV_BY1(d->dpll.rdiv[LMK05318_SECREF]), //R259 + MAKE_LMK05318_DPLL_REF_FB_PREDIV(d->dpll.pre_div - 2), //R304 + MAKE_LMK05318_DPLL_REF_FB_DIV_BY0(d->dpll.n), //R305 + MAKE_LMK05318_DPLL_REF_FB_DIV_BY1(d->dpll.n), + MAKE_LMK05318_DPLL_REF_FB_DIV_BY2(d->dpll.n), + MAKE_LMK05318_DPLL_REF_FB_DIV_BY3(d->dpll.n), //R308 + MAKE_LMK05318_DPLL_REF_NUM_BY0(d->dpll.num), //R309 + MAKE_LMK05318_DPLL_REF_NUM_BY1(d->dpll.num), + MAKE_LMK05318_DPLL_REF_NUM_BY2(d->dpll.num), + MAKE_LMK05318_DPLL_REF_NUM_BY3(d->dpll.num), + MAKE_LMK05318_DPLL_REF_NUM_BY4(d->dpll.num), //R313 + MAKE_LMK05318_DPLL_REF_DEN_BY0(d->dpll.den), //R314 + MAKE_LMK05318_DPLL_REF_DEN_BY1(d->dpll.den), + MAKE_LMK05318_DPLL_REF_DEN_BY2(d->dpll.den), + MAKE_LMK05318_DPLL_REF_DEN_BY3(d->dpll.den), + MAKE_LMK05318_DPLL_REF_DEN_BY4(d->dpll.den), //R318 + + MAKE_LMK05318_REG_WR(0x00d8, 0x00), //R216 unknown, undocumented, but critical to start DPLL + + MAKE_LMK05318_REF0_PH_VALID_CNT_BY0(phase_valid_detection[LMK05318_PRIREF]), //R235 + MAKE_LMK05318_REF0_PH_VALID_CNT_BY1(phase_valid_detection[LMK05318_PRIREF]), //R236 + MAKE_LMK05318_REF0_PH_VALID_CNT_BY2(phase_valid_detection[LMK05318_PRIREF]), //R237 + MAKE_LMK05318_REF0_PH_VALID_CNT_BY3(phase_valid_detection[LMK05318_PRIREF]), //R238 + + MAKE_LMK05318_REF1_PH_VALID_CNT_BY0(phase_valid_detection[LMK05318_SECREF]), //R239 + MAKE_LMK05318_REF1_PH_VALID_CNT_BY1(phase_valid_detection[LMK05318_SECREF]), //R240 + MAKE_LMK05318_REF1_PH_VALID_CNT_BY2(phase_valid_detection[LMK05318_SECREF]), //R241 + MAKE_LMK05318_REF1_PH_VALID_CNT_BY3(phase_valid_detection[LMK05318_SECREF]), //R242 + + MAKE_LMK05318_DPLL_REF_TDC_CTL(0, 1), //R260 TDC software ctrl(dis) + DPLL_REF_AVOID_SLIP(en) + + MAKE_LMK05318_REG_WR(DPLL_REF_DLY_GEN, 0x80), //R261 empirical! clarify! + + MAKE_LMK05318_DPLL_REF_CYCSLIP_OFFSET_BY0(ref_cycslip_offset), //R262 + MAKE_LMK05318_DPLL_REF_CYCSLIP_OFFSET_BY1(ref_cycslip_offset), //R263 + MAKE_LMK05318_DPLL_REF_CYCSLIP_OFFSET_BY2(ref_cycslip_offset), //R264 + MAKE_LMK05318_DPLL_REF_CYCSLIP_OFFSET_BY3(ref_cycslip_offset), //R265 + MAKE_LMK05318_DPLL_REF_CYCSLIP_OFFSET_BY4(ref_cycslip_offset), //R266 + + MAKE_LMK05318_REG_WR(DPLL_REF_LOOPCTL, 0xa0), //R267 empirical! clarify! + + MAKE_LMK05318_REG_WR(DPLL_REF_LOOPCTL_CHG, 0), //R268 + MAKE_LMK05318_REG_WR(DPLL_REF_DECIMATION, 0), //R269 + + MAKE_LMK05318_DPLL_REF_FILTSCALAR_BY0(ref_filter_scalar), //R270 + MAKE_LMK05318_DPLL_REF_FILTSCALAR_BY1(ref_filter_scalar), //R271 + + MAKE_LMK05318_REG_WR(DPLL_REF_FILTGAIN, 0), //R272 + MAKE_LMK05318_REG_WR(DPLL_REF_FILTGAIN_FL1, 0), //R273 + MAKE_LMK05318_REG_WR(DPLL_REF_FILTGAIN_FL2, 0), //R274 + + MAKE_LMK05318_REG_WR(DPLL_REF_LOOPGAIN, 22), //R275 empirical! clarify! + MAKE_LMK05318_REG_WR(DPLL_REF_LOOPGAIN_FL1, 22), //R276 empirical! clarify! + MAKE_LMK05318_REG_WR(DPLL_REF_LOOPGAIN_FL2, 22), //R277 empirical! clarify! + + MAKE_LMK05318_REG_WR(DPLL_REF_LPF0GAIN, 0), //R278 + MAKE_LMK05318_REG_WR(DPLL_REF_LPF0GAIN_FL1, 0), //R279 + MAKE_LMK05318_REG_WR(DPLL_REF_LPF0GAIN_FL2, 0), //R280 + + MAKE_LMK05318_REG_WR(DPLL_REF_LPF1GAIN, 0), //R281 + MAKE_LMK05318_REG_WR(DPLL_REF_LPF1GAIN_FL1, 0), //R282 + MAKE_LMK05318_REG_WR(DPLL_REF_LPF1GAIN_FL2, 0), //R283 + + MAKE_LMK05318_REG_WR(DPLL_REF_LPF0GAIN2_FL, 30), //R284 empirical! clarify! + MAKE_LMK05318_REG_WR(DPLL_REF_LPF1GAIN2_FL, 30), //R285 empirical! clarify! + + MAKE_LMK05318_DPLL_REF_TMR_FL1_BY0(0), //R286 + MAKE_LMK05318_DPLL_REF_TMR_FL1_BY1(0), //R287 + MAKE_LMK05318_DPLL_REF_TMR_FL2_BY0(0), //R288 + MAKE_LMK05318_DPLL_REF_TMR_FL2_BY1(0), //R289 + + MAKE_LMK05318_DPLL_REF_TMR_LCK_BY0(0x0322), //R290 empirical! clarify! + MAKE_LMK05318_DPLL_REF_TMR_LCK_BY1(0x0322), //R291 empirical! clarify! + + MAKE_LMK05318_REG_WR(DPLL_REF_PHC_LPF, 0), //R292 | + MAKE_LMK05318_REG_WR(DPLL_REF_PHC_CTRL, 0), //R293 | + MAKE_LMK05318_DPLL_REF_PHC_TIMER_BY0(0), //R294 | Phase Cancellation for Hitless Switching + MAKE_LMK05318_DPLL_REF_PHC_TIMER_BY1(0), //R295 | + + MAKE_LMK05318_REG_WR(DPLL_REF_QUANT, ref_quant), //R296 + MAKE_LMK05318_REG_WR(DPLL_REF_QUANT_FL1, ref_quant), //R297 + MAKE_LMK05318_REG_WR(DPLL_REF_QUANT_FL2, ref_quant), //R298 + + MAKE_LMK05318_REG_WR(DPLL_PL_LPF_GAIN, 0), //R300 empirical! clarify! + MAKE_LMK05318_REG_WR(DPLL_PL_THRESH, 0x1c), //R301 empirical! clarify! + MAKE_LMK05318_REG_WR(DPLL_PL_UNLK_THRESH, 0x1e), //R302 empirical! clarify! + + MAKE_LMK05318_DPLL_REF_MASHCTL(dpll_sdm_dither, dpll_sdm_order), //R319 + + MAKE_LMK05318_DPLL_REF_LOCKDET_1_5_BY0(dco_lock_det0), //R320 + MAKE_LMK05318_DPLL_REF_LOCKDET_1_5_BY1(dco_lock_det0), + MAKE_LMK05318_DPLL_REF_LOCKDET_1_5_BY2(dco_lock_det0), + MAKE_LMK05318_DPLL_REF_LOCKDET_1_5_BY3(dco_lock_det0), + MAKE_LMK05318_DPLL_REF_LOCKDET_1_5_BY4(dco_lock_det0), + + MAKE_LMK05318_DPLL_REF_LOCKDET_6_10_BY0(dco_lock_det1), //R325 + MAKE_LMK05318_DPLL_REF_LOCKDET_6_10_BY1(dco_lock_det1), + MAKE_LMK05318_DPLL_REF_LOCKDET_6_10_BY2(dco_lock_det1), + MAKE_LMK05318_DPLL_REF_LOCKDET_6_10_BY3(dco_lock_det1), + MAKE_LMK05318_DPLL_REF_LOCKDET_6_10_BY4(dco_lock_det1), + + MAKE_LMK05318_DPLL_REF_UNLOCKDET_1_3_BY0(dco_unlock_det0), //R330 + MAKE_LMK05318_DPLL_REF_UNLOCKDET_1_3_BY1(dco_unlock_det0), + MAKE_LMK05318_DPLL_REF_UNLOCKDET_1_3_BY2(dco_unlock_det0), + + MAKE_LMK05318_DPLL_REF_UNLOCKDET_VCO_CNTSTRT_BY0(dco_unlock_det1),//R336 + MAKE_LMK05318_DPLL_REF_UNLOCKDET_VCO_CNTSTRT_BY1(dco_unlock_det1),//R337 + MAKE_LMK05318_DPLL_REF_UNLOCKDET_VCO_CNTSTRT_BY2(dco_unlock_det1),//R338 + }; + + res = res ? res : lmk05318_add_reg_to_map(d, dpll_regs, SIZEOF_ARRAY(dpll_regs)); + } + + if(res) return res; - losval = ((los[0] & LOS_XO_POL_MSK) ? LMK05318_LOS_XO : 0) | - ((los[0] & LOL_PLL1_POL_MSK) ? LMK05318_LOL_PLL1 : 0) | - ((los[0] & LOL_PLL2_POL_MSK) ? LMK05318_LOL_PLL2 : 0) | - ((los[0] & LOS_FDET_XO_POL_MSK) ? LMK05318_LOS_FDET_XO : 0) | - ((los[1] & LOPL_DPLL_POL_MSK) ? LMK05318_LOPL_DPLL : 0) | - ((los[1] & LOFL_DPLL_POL_MSK) ? LMK05318_LOFL_DPLL : 0); + //common registers + uint32_t regs[] = + { + MAKE_LMK05318_PLL_CLK_CFG(0, 0b111), //R47 APLL cascade mode + set PLL clock cfg + MAKE_LMK05318_OUTSYNCCTL(1, 1, 1), //R70 enable APLL1/APLL2 channel sync + MAKE_LMK05318_OUTSYNCEN(1, 1, 1, 1, 1, 1), //R71 enable ch0..ch7 out sync + MAKE_LMK05318_DPLL_MUTE(1,1,1,1), //R29 mute during lock + MAKE_LMK05318_OUT_MUTE(0,0,0,0,0,0,0,0), //R25 unmute all chans - USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 LOS_MAK=[%s%s%s%s%s%s%s] %02x:%02x:%02x\n", - (los[0] & LOS_XO_POL_MSK) ? "XO" : "", - (los[0] & LOL_PLL1_POL_MSK) ? " PLL1" : "", - (los[0] & LOL_PLL2_POL_MSK) ? " PLL2" : "", - (los[0] & LOS_FDET_XO_POL_MSK) ? " XO_FDET" : "", - (los[1] & LOPL_DPLL_POL_MSK) ? " DPLL_P" : "", - (los[1] & LOFL_DPLL_POL_MSK) ? " DPLL_F" : "", - (los[2] & BAW_LOCK_MSK) ? "" : " BAW", - los[0], los[1], los[2]); + MAKE_LMK05318_MUTELVL1(CH3_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW, + CH2_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW, + CH1_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW, + CH0_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW), //R23 set ch0..3 mute levels + MAKE_LMK05318_MUTELVL2(CH7_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW, + CH6_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW, + CH5_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW, + CH4_MUTE_LVL_DIFF_LOW_P_LOW_N_LOW), //R24 set ch4..7 mute levels - *los_msk = losval; + MAKE_LMK05318_INT_FLAG0(0,0,0,0), //R19 | + MAKE_LMK05318_INT_FLAG1(0,0,0,0,0,0,0,0), //R20 | reset interrupt LOS flags + MAKE_LMK05318_INTCTL(0,0), //R21 + }; + + return lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); +} + +int lmk05318_create(lldev_t dev, unsigned subdev, unsigned lsaddr, + uint32_t xo_freq, xo_input_type_t xo_fmttype, bool xo_fdet_bypass, + lmk05318_dpll_settings_t* dpll, + lmk05318_out_config_t* out_ports_cfg, unsigned out_ports_len, + lmk05318_state_t* out, bool dry_run) +{ + int res; + uint8_t dummy[4] = {0,0,0,0}; + memset(out, 0, sizeof(lmk05318_state_t)); + + lmk05318_registers_map_reset(); + + out->dev = dev; + out->subdev = subdev; + out->lsaddr = lsaddr; + out->vco2_freq = 0; + out->pd1 = 0; + out->pd2 = 0; + out->fref_pll2_div_rp = 3; + out->fref_pll2_div_rs = (((VCO_APLL1 + APLL2_PD_MAX - 1) / APLL2_PD_MAX) + out->fref_pll2_div_rp - 1) / out->fref_pll2_div_rp; + + out->xo.fref = xo_freq; + out->xo.type = xo_fmttype; + out->xo.fdet_bypass = xo_fdet_bypass; + + res = dry_run ? 0 : lmk05318_reg_get_u32(out, 0, &dummy[0]); + if (res) + return res; + + USDR_LOG("5318", USDR_LOG_INFO, "LMK05318 DEVID[0/1/2/3] = %02x %02x %02x %02x\n", dummy[3], dummy[2], dummy[1], dummy[0]); + + if (!dry_run && (dummy[3] != 0x10 || dummy[2] != 0x0b || dummy[1] != 0x35 || dummy[0] != 0x42)) { + return -ENODEV; + } + + res = lmk05318_set_xo_fref(out); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 error %d setting XO", res); + return res; + } + + res = lmk05318_dpll_config(out, dpll); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 error %d configuring DPLL", res); + return res; + } + + res = lmk05318_tune_apll1(out); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 error %d tuning APLL1", res); + return res; + } + + res = lmk05318_solver(out, out_ports_cfg, out_ports_len); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 error %d solving output frequencies", res); + return res; + } + + res = lmk05318_set_common_registers(out, dpll); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 error %d on init()", res); + return res; + } + + res = lmk05318_reg_wr_from_map(out, dry_run); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 error %d writing registers", res); + return res; + } + + usleep(10000); //wait for LMK digests it all + + res = dry_run ? 0 : lmk05318_softreset(out); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 error %d lmk05318_softreset()", res); + return res; + } + + USDR_LOG("5318", USDR_LOG_INFO, "LMK05318 initialized\n"); + return 0; +} + +VWLT_ATTRIBUTE(optimize("-Ofast")) +static inline double lmk05318_calc_vco2_div(lmk05318_state_t* d, uint64_t fvco2, unsigned* pn, unsigned* pnum, unsigned* pden) +{ + const uint64_t pll2_tot_prediv = d->fref_pll2_div_rp * d->fref_pll2_div_rs; + + uint64_t den64 = VCO_APLL1 * pll2_tot_prediv; + double r = (double)(fvco2 * pll2_tot_prediv) / VCO_APLL1; + unsigned n = (unsigned)r; + double n_frac = r - n; + uint64_t num64 = (uint64_t)(n_frac * den64 + 0.5); + + uint64_t nod = find_gcd(num64, den64); + if(nod > 1) + { +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_DEBUG, "PLL2 NUM/DEN reduced NOD:%" PRIu64 ": %" PRIu64 "/%" PRIu64" -> %" PRIu64 "/%" PRIu64, + nod, num64, den64, num64/nod, den64/nod); +#endif + num64 /= nod; + den64 /= nod; + } + + if(den64 > 0xFFFFFF) + { +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_ERROR, "PLL2_DEN overflow, cannot solve in integer values"); +#endif + return -EINVAL; + } + + uint32_t num = num64; + uint32_t den = den64; + + const double fvco2_fact = (double)VCO_APLL1 * (n + (double)num / den) / pll2_tot_prediv; + +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_ERROR, "WANTED_VCO2:%" PRIu64 " N:%u NUM:%u DEN:%u VCO2:%.8f", fvco2, n, num, den, fvco2_fact); +#endif + + if(pn) + *pn = n; + if(pnum) + *pnum = num; + if(pden) + *pden = den; + + return fvco2_fact; +} + +int lmk05318_apll1_calibrate(lmk05318_state_t* d) +{ + uint32_t regs[2] = + { + MAKE_LMK05318_PLL1_CTRL0(1), + MAKE_LMK05318_PLL1_CTRL0(0), + }; + + return lmk05318_trigger_reg_with_sleep(d, regs); +} + +int lmk05318_apll2_calibrate(lmk05318_state_t* d) +{ + uint32_t regs[2] = + { + MAKE_LMK05318_PLL2_CTRL0(d->fref_pll2_div_rs - 1, d->fref_pll2_div_rp - 3, 1), + MAKE_LMK05318_PLL2_CTRL0(d->fref_pll2_div_rs - 1, d->fref_pll2_div_rp - 3, 0), + }; + + return lmk05318_trigger_reg_with_sleep(d, regs); +} + +static int lmk05318_tune_apll2(lmk05318_state_t* d) +{ + int res; + + double fpd2 = (double)VCO_APLL1 / d->fref_pll2_div_rp / d->fref_pll2_div_rs; + if (fpd2 < APLL2_PD_MIN || fpd2 > APLL2_PD_MAX) { + USDR_LOG("5318", USDR_LOG_ERROR, "[APLL2] FPD should be in range [%" PRIu64 ";%" PRIu64 "] but got %.8f!\n", + (uint64_t)APLL2_PD_MIN, (uint64_t)APLL2_PD_MAX, fpd2); + return -EINVAL; + } + + if(d->vco2_freq < VCO_APLL2_MIN || d->vco2_freq > VCO_APLL2_MAX || + ((d->pd1 < APLL2_PDIV_MIN || d->pd1 > APLL2_PDIV_MAX) && (d->pd2 < APLL2_PDIV_MIN || d->pd2 > APLL2_PDIV_MAX)) + ) + { + USDR_LOG("5318", USDR_LOG_WARNING, "[APLL2] either FVCO2[%" PRIu64"] nor (PD1[%d] && PD2[%d]) is out of range, APLL2 will be disabled", + d->vco2_freq, d->pd1, d->pd2); + // Disable + uint32_t regs[] = { + MAKE_LMK05318_PLL2_CTRL0(d->fref_pll2_div_rs - 1, d->fref_pll2_div_rp - 3, 1), //R100 Deactivate APLL2 + }; + return lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + } + + const unsigned n = d->vco2_n; + const unsigned num = d->vco2_num; + const unsigned den = d->vco2_den; + + const uint8_t apll2_sdm_order = num ? SDM_ORDER_THIRD : SDM_ORDER_INT; //override if needed + const uint8_t apll2_sdm_dither = SDM_DITHER_MODE_WEAK; + + USDR_LOG("5318", USDR_LOG_INFO, "[APLL2] RS=%u RP=%u FPD2=%.8f FVCO2=%" PRIu64 " N=%d NUM=%d DEN=%d PD1=%d PD2=%d MASH_ORD:%u", + d->fref_pll2_div_rs, d->fref_pll2_div_rp, fpd2, d->vco2_freq, n, num, den, d->pd1, d->pd2, apll2_sdm_order); + + // one of PDs may be unused (==0) -> we should fix it before registers set + if(d->pd1 < APLL2_PDIV_MIN || d->pd1 > APLL2_PDIV_MAX) + { + d->pd1 = d->pd2; + } + else if(d->pd2 < APLL2_PDIV_MIN || d->pd2 > APLL2_PDIV_MAX) + { + d->pd2 = d->pd1; + } + + uint32_t regs[] = { + MAKE_LMK05318_PLL2_MASHCTRL(apll2_sdm_dither, apll2_sdm_order), //R139 PLL2 MASHORD=3 + MAKE_LMK05318_PLL2_CTRL2(d->pd2 - 1, d->pd1 - 1), //R102 + MAKE_LMK05318_PLL2_NDIV_BY0(n), //R135 + MAKE_LMK05318_PLL2_NDIV_BY1(n), //R134 + MAKE_LMK05318_PLL2_NUM_BY0(num), //R138 + MAKE_LMK05318_PLL2_NUM_BY1(num), //R137 + MAKE_LMK05318_PLL2_NUM_BY2(num), //R136 + MAKE_LMK05318_PLL2_DEN_BY0(den), //R333 + MAKE_LMK05318_PLL2_DEN_BY1(den), //R334 + MAKE_LMK05318_PLL2_DEN_BY2(den), //R335 + MAKE_LMK05318_PLL2_CTRL0(d->fref_pll2_div_rs - 1, d->fref_pll2_div_rp - 3, 0), //R100 Activate APLL2 + }; + + res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + if (res) + return res; + + return 0; +} + +static const char* lmk05318_decode_xo_type(enum xo_type_options t) +{ + switch(t) + { + case XO_TYPE_DC_DIFF_EXT: return "DC_DIFF_EXT"; + case XO_TYPE_AC_DIFF_EXT: return "AC_DIFF_EXT"; + case XO_TYPE_AC_DIFF_INT_100: return "AC_DIFF_INT_100"; + case XO_TYPE_HCSL_INT_50: return "HCSL_INT_50"; + case XO_TYPE_CMOS: return "CMOS"; + case XO_TYPE_SE_INT_50: return "SE_INT_50"; + } + + return "UNKNOWN"; +} + +int lmk05318_set_xo_fref(lmk05318_state_t* d) +{ + const uint32_t xo_fref = d->xo.fref; + const int xo_type = d->xo.type; + const bool xo_fdet_bypass = d->xo.fdet_bypass; + + if(xo_fref < XO_FREF_MIN || xo_fref > XO_FREF_MAX) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[XO] input fref should be in range [%" PRIu64 ";%" PRIu64 "] but got %d!\n", + (uint64_t)XO_FREF_MIN, (uint64_t)XO_FREF_MAX, xo_fref); + return -EINVAL; + } + + if(xo_fref * 2 <= APLL1_PD_MAX) + { + //use XO doubler + d->xo.doubler_enabled = true; + d->xo.pll1_fref_rdiv = 1; + } + else + { + //use XO divider + d->xo.doubler_enabled = false; + d->xo.pll1_fref_rdiv = ceil((double)xo_fref / APLL1_PD_MAX); + } + + if(d->xo.pll1_fref_rdiv > APLL1_DIVIDER_MAX) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[XO] APPL1_RDIV:%d out of range [%d;%d]", d->xo.pll1_fref_rdiv, (int)APLL1_DIVIDER_MIN, (int)APLL1_DIVIDER_MAX); + return -EINVAL; + } + + int xo_type_raw; + switch((int)xo_type) + { + case XO_DC_DIFF_EXT: xo_type_raw = XO_TYPE_DC_DIFF_EXT; break; + case XO_AC_DIFF_EXT: xo_type_raw = XO_TYPE_AC_DIFF_EXT; break; + case XO_AC_DIFF_INT_100: xo_type_raw = XO_TYPE_AC_DIFF_INT_100; break; + case XO_HCSL_INT_50: xo_type_raw = XO_TYPE_HCSL_INT_50; break; + case XO_CMOS: xo_type_raw = XO_TYPE_CMOS; break; + case XO_SE_INT_50: xo_type_raw = XO_TYPE_SE_INT_50; break; + default: + USDR_LOG("5318", USDR_LOG_ERROR, "[XO] input type %d is not supported!\n", (int)xo_type); + return -EINVAL; + } + + USDR_LOG("5318", USDR_LOG_INFO, "[XO] FREF:%u TYPE:%u(%s) DOUBLER:%u RDIV:%u FDET_BYPASS:%u", + xo_fref, + xo_type_raw, lmk05318_decode_xo_type((enum xo_type_options)xo_type_raw), + d->xo.doubler_enabled, d->xo.pll1_fref_rdiv, xo_fdet_bypass); + + uint32_t regs[] = { + MAKE_LMK05318_XO_CLKCTL1(d->xo.doubler_enabled ? 1 : 0, xo_fdet_bypass ? 1 : 0, 0, 1),//R42 + MAKE_LMK05318_XO_CLKCTL2(1, xo_type_raw, 2), //R43 + MAKE_LMK05318_XO_CONFIG(d->xo.pll1_fref_rdiv - 1), //R44 + }; + + int res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + res = res ? res : lmk05318_set_xo_bawdetect_registers(d); + + return res; +} + +int lmk05318_tune_apll1(lmk05318_state_t* d) +{ + int res; + + uint8_t apll1_sdm_order; + const uint8_t apll1_sdm_dither = SDM_DITHER_MODE_WEAK; + + uint64_t num, den; + + const double fpd1 = ((double)d->xo.fref / d->xo.pll1_fref_rdiv) * (d->xo.doubler_enabled ? 2.0f : 1.0f); + if(fpd1 < APLL1_PD_MIN || fpd1 > APLL1_PD_MAX) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 APLL1 FPD should be in range [%" PRIu64 ";%" PRIu64 "] but got %.8f!\n", + (uint64_t)APLL1_PD_MIN, (uint64_t)APLL1_PD_MAX, fpd1); + return -EINVAL; + } + + const uint64_t fvco = VCO_APLL1; + const double r = (double)fvco / fpd1; + const unsigned n = (unsigned)r; + const double n_frac = r - n; + + if(n > UINT16_MAX) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[APLL1] FDIV.N=%u out of range [1;65535]", n); + return -EINVAL; + } + + //in DPLL mode we use FIXED 40-bit APLL1 denominator and programmed 40-bit numerator + if(d->dpll.enabled) + { + den = (uint64_t)1 << 40; //fixed + num = (uint64_t)(n_frac * den + 0.5); + apll1_sdm_order = SDM_ORDER_THIRD; //for DPLL correction + +#ifndef DISABLE_ADDITIONAL_DPLL_CHECKS + //additional checks for DPLL mode + if((double)num / den <= DPLL_FDIV_FRAC_MIN || (double)num / den >= DPLL_FDIV_FRAC_MAX) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[APLL1] NUM/DEN ratio:%.8f out of range (%.4f;%.4f)", + (double)num / den, DPLL_FDIV_FRAC_MIN, DPLL_FDIV_FRAC_MAX); + return -EINVAL; + } + else + { + USDR_LOG("5318", USDR_LOG_INFO, "[APLL1] NUM/DEN ratio:%.8f within valid range (%.4f;%.4f)", + (double)num / den, DPLL_FDIV_FRAC_MIN, DPLL_FDIV_FRAC_MAX); + } + + const double f_lo = fpd1 * floor(r); + const double f_hi = fpd1 * ceil(r); + const double min_diff = MIN(fabs(f_lo - fvco), fabs(f_hi - fvco)); + if(min_diff <= DPLL_MIN_BAW_DIFF) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[APLL1] Min difference between VCO1 and FPD* = %.4f <= %lu", + min_diff, DPLL_MIN_BAW_DIFF); + return -EINVAL; + } + else + { + USDR_LOG("5318", USDR_LOG_INFO, "[APLL1] Integer boundary spur = %.2fMHz (>%.2fMHz)", + min_diff / 1e6, (double)DPLL_MIN_BAW_DIFF / 1e6); + } +#endif //DISABLE_ADDITIONAL_DPLL_CHECKS + + } + // without DPLL we use programmed 24-bit numerator & programmed 24-bit denominator + else + { + den = d->xo.fref * (d->xo.doubler_enabled ? 2 : 1); + num = (uint64_t)(n_frac * den + 0.5); + + uint64_t nod = find_gcd(num, den); + if(nod > 1) + { +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_DEBUG, "PLL1 NUM/DEN reduced NOD:%" PRIu64 ": %" PRIu64 "/%" PRIu64" -> %" PRIu64 "/%" PRIu64, + nod, num, den, num/nod, den/nod); +#endif + num /= nod; + den /= nod; + } + + static const uint64_t MAX_DEN = ((uint64_t)1 << 24) - 1; + + if(den > MAX_DEN) + { +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_ERROR, "PLL1_DEN overflow, cannot solve in integer values"); +#endif + return -EINVAL; + } + + apll1_sdm_order = num ? SDM_ORDER_THIRD : SDM_ORDER_INT; + } + + const double vco1_fact = fpd1 * (n + (double)num / den); + + USDR_LOG("5318", USDR_LOG_INFO, "[APLL1] FPD=%.8f VCO_FACT=%.8f N=%d NUM=%" PRIu64 " DEN=%" PRIu64 "[%s] MASH_ORD:%u", + fpd1, vco1_fact, n, num, den, (d->dpll.enabled ? "FIXED" : "PROGRAMMED"), apll1_sdm_order); + + if(fabs(vco1_fact - fvco) > 1E-4) + { + USDR_LOG("5318", USDR_LOG_ERROR, "[APLL1] VCO too rough"); + return -EINVAL; + } + + if(d->dpll.enabled) + { + uint32_t regs[] = { + MAKE_LMK05318_PLL1_MASHCTRL(0, 0, 0, + apll1_sdm_dither, apll1_sdm_order), //R115 PLL1 MASHORD=3 WeakDither + MAKE_LMK05318_PLL1_MODE(0, 0, 1), //R116 DPLL mode + MAKE_LMK05318_PLL1_NDIV_BY0(n), //R109 NDIV + MAKE_LMK05318_PLL1_NDIV_BY1(n), //R108 NDIV + MAKE_LMK05318_PLL1_NUM_BY0(num), //R110 | + MAKE_LMK05318_PLL1_NUM_BY1(num), //R111 | + MAKE_LMK05318_PLL1_NUM_BY2(num), //R112 | 40-bit NUM + MAKE_LMK05318_PLL1_NUM_BY3(num), //R113 | + MAKE_LMK05318_PLL1_NUM_BY4(num), //R114 | + MAKE_LMK05318_PLL1_CTRL0(0), //R74 Activate APLL1 + }; + + res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + } + else + { + uint32_t regs[] = { + MAKE_LMK05318_PLL1_MASHCTRL(0, 0, 0, + apll1_sdm_dither, apll1_sdm_order), //R115 PLL1 MASHORD=3 WeakDither + MAKE_LMK05318_PLL1_MODE(0, 0, 0), //R116 free-run mode + MAKE_LMK05318_PLL1_NDIV_BY0(n), //R109 NDIV + MAKE_LMK05318_PLL1_NDIV_BY1(n), //R108 NDIV + + MAKE_LMK05318_REG_WR(PLL1_NUM_BY4, (uint8_t)den), //R114 + MAKE_LMK05318_REG_WR(PLL1_NUM_BY3, (uint8_t)(den >> 8)), //R113 | 24-bit DEN + MAKE_LMK05318_REG_WR(PLL1_NUM_BY2, (uint8_t)(den >> 16)), //R112 + + MAKE_LMK05318_REG_WR(PLL1_NUM_BY1, (uint8_t)num), //R111 + MAKE_LMK05318_REG_WR(PLL1_NUM_BY0, (uint8_t)(num >> 8)), //R110 | 24-bit NUM + MAKE_LMK05318_PLL1_24B_NUM_23_16((uint8_t)(num >> 16)), //R339 + + MAKE_LMK05318_PLL1_CTRL0(0), //R74 Activate APLL1 + }; + + res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + } + + return res; +} + +static inline uint64_t lmk05318_max_odiv(unsigned port) +{ + switch(port) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: return ((uint64_t)1 << 8); + case 7: return ((uint64_t)1 << 32); + } + return 1; +} + +VWLT_ATTRIBUTE(optimize("-Ofast")) +static inline uint16_t lmk05318_factorize_out7div(uint64_t total_div) +{ + if(total_div <= OUTDIV7_STAGE1_MAX) + return total_div; + + uint16_t div = OUTDIV7_STAGE1_MAX; + + while(div >= OUTDIV7_STAGE1_WITH_ST2_MIN) + { + if(total_div % div == 0 && total_div / div <= OUTDIV7_STAGE2_MAX) + { + return div; + } + --div; + } + + return OUTDIV7_STAGE1_MAX; //if total div is not divisible by any of [256..6], let it be 256 +} + + +int lmk05318_set_out_div(lmk05318_state_t* d, unsigned port, uint64_t udiv) +{ + if (port > (LMK05318_MAX_OUT_PORTS - 1) || udiv < 1 || udiv > lmk05318_max_odiv(port)) + return -EINVAL; + + //out7 is special + if(port == 7) + { + uint16_t div_stage1 = lmk05318_factorize_out7div(udiv); + uint64_t div_stage2 = (uint64_t)((double)udiv / div_stage1 + 0.5); + + USDR_LOG("5318", USDR_LOG_DEBUG, "[OUT7] TOTAL_DIV:%" PRIu64 " DIV_STAGE1:%u DIV_STAGE2:%" PRIu64 " FACT:%" PRIu64 "", + udiv, div_stage1, div_stage2, div_stage2 * div_stage1); + + --div_stage1; + --div_stage2; + + uint32_t regs[] = + { + MAKE_LMK05318_OUTDIV_7(div_stage1), + MAKE_LMK05318_OUTDIV_7_STG2_BY0(div_stage2), + MAKE_LMK05318_OUTDIV_7_STG2_BY1(div_stage2), + MAKE_LMK05318_OUTDIV_7_STG2_BY2(div_stage2), + }; + + return lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + } + + uint32_t reg = 0; + switch(port) + { + case 6: reg = MAKE_LMK05318_OUTDIV_6(udiv - 1); break; + case 5: reg = MAKE_LMK05318_OUTDIV_5(udiv - 1); break; + case 4: reg = MAKE_LMK05318_OUTDIV_4(udiv - 1); break; + case 3: + case 2: reg = MAKE_LMK05318_OUTDIV_2_3(udiv - 1); break; + case 1: + case 0: reg = MAKE_LMK05318_OUTDIV_0_1(udiv-1); break; + default: + return -EINVAL; + } + + return lmk05318_add_reg_to_map(d, ®, 1); +} + +static inline const char* lmk05318_decode_fmt_to_string(unsigned f) +{ + switch (f) { + case LVDS: return "OUT_OPTS_AC_LVDS"; + case CML: return "OUT_OPTS_AC_CML"; + case LVPECL: return "OUT_OPTS_AC_LVPECL"; + case HCSL_EXT_50: return "OUT_OPTS_HCSL_EXT_50"; + case HCSL_INT_50: return "OUT_OPTS_HCSL_INT_50"; + case LVCMOS_HIZ_HIZ: return "OUT_OPTS_LVCMOS_HIZ_HIZ"; + case LVCMOS_HIZ_N: return "OUT_OPTS_LVCMOS_HIZ_N"; + case LVCMOS_HIZ_P: return "OUT_OPTS_LVCMOS_HIZ_P"; + case LVCMOS_LOW_LOW: return "OUT_OPTS_LVCMOS_LOW_LOW"; + case LVCMOS_N_HIZ: return "OUT_OPTS_LVCMOS_N_HIZ"; + case LVCMOS_N_N: return "OUT_OPTS_LVCMOS_N_N"; + case LVCMOS_N_P: return "OUT_OPTS_LVCMOS_N_P"; + case LVCMOS_P_HIZ: return "OUT_OPTS_LVCMOS_P_HIZ"; + case LVCMOS_P_N: return "OUT_OPTS_LVCMOS_P_N"; + case LVCMOS_P_P: return "OUT_OPTS_LVCMOS_P_P"; + default: return "OUT_OPTS_Disabled"; + } + return "UNKNOWN"; +} + +static inline uint8_t lmk05318_decode_fmt(unsigned f) +{ + switch (f) { + case LVDS: return OUT_OPTS_AC_LVDS; + case CML: return OUT_OPTS_AC_CML; + case LVPECL: return OUT_OPTS_AC_LVPECL; + case HCSL_EXT_50: return OUT_OPTS_HCSL_EXT_50; + case HCSL_INT_50: return OUT_OPTS_HCSL_INT_50; + case LVCMOS_HIZ_HIZ: return OUT_OPTS_LVCMOS_HIZ_HIZ; + case LVCMOS_HIZ_N: return OUT_OPTS_LVCMOS_HIZ_N; + case LVCMOS_HIZ_P: return OUT_OPTS_LVCMOS_HIZ_P; + case LVCMOS_LOW_LOW: return OUT_OPTS_LVCMOS_LOW_LOW; + case LVCMOS_N_HIZ: return OUT_OPTS_LVCMOS_N_HIZ; + case LVCMOS_N_N: return OUT_OPTS_LVCMOS_N_N; + case LVCMOS_N_P: return OUT_OPTS_LVCMOS_N_P; + case LVCMOS_P_HIZ: return OUT_OPTS_LVCMOS_P_HIZ; + case LVCMOS_P_N: return OUT_OPTS_LVCMOS_P_N; + case LVCMOS_P_P: return OUT_OPTS_LVCMOS_P_P; + } + return OUT_OPTS_Disabled; +} + +int lmk05318_disable_port(lmk05318_state_t* d, unsigned port) +{ + uint16_t regno; + switch(port) + { + case 0: regno = OUTCTL_0; break; + case 1: regno = OUTCTL_1; break; + case 2: regno = OUTCTL_2; break; + case 3: regno = OUTCTL_3; break; + case 4: regno = OUTCTL_4; break; + case 5: regno = OUTCTL_5; break; + case 6: regno = OUTCTL_6; break; + case 7: regno = OUTCTL_7; break; + default: + return -EINVAL; + } + + uint8_t regval; + int res = lmk05318_reg_rd(d, regno, ®val); + if(res) + return res; + + return lmk05318_reg_wr(d, regno, regval & ~OUT0_FMT_MSK); +} + +int lmk05318_enable_port(lmk05318_state_t* d, unsigned port, unsigned fmt) +{ + uint16_t regno; + switch(port) + { + case 0: regno = OUTCTL_0; break; + case 1: regno = OUTCTL_1; break; + case 2: regno = OUTCTL_2; break; + case 3: regno = OUTCTL_3; break; + case 4: regno = OUTCTL_4; break; + case 5: regno = OUTCTL_5; break; + case 6: regno = OUTCTL_6; break; + case 7: regno = OUTCTL_7; break; + default: + return -EINVAL; + } + + uint8_t ot = lmk05318_decode_fmt(fmt); + + uint8_t regval; + int res = lmk05318_reg_rd(d, regno, ®val); + if(res) + return res; + + return lmk05318_reg_wr(d, regno, regval | ((ot << OUT0_FMT_OFF) & OUT0_FMT_MSK)); +} + + +int lmk05318_set_out_mux(lmk05318_state_t* d, unsigned port, unsigned mux, unsigned otype) +{ + if (port > 7) + return -EINVAL; + + uint8_t ot = lmk05318_decode_fmt(otype); + + uint32_t regs[] = { + (port == 0) ? MAKE_LMK05318_OUTCTL_0(mux, ot) : + (port == 1) ? MAKE_LMK05318_OUTCTL_1(ot) : + (port == 2) ? MAKE_LMK05318_OUTCTL_2(mux, ot) : + (port == 3) ? MAKE_LMK05318_OUTCTL_3(ot) : + (port == 4) ? MAKE_LMK05318_OUTCTL_4(mux, ot) : + (port == 5) ? MAKE_LMK05318_OUTCTL_5(mux, ot) : + (port == 6) ? MAKE_LMK05318_OUTCTL_6(mux, ot) : MAKE_LMK05318_OUTCTL_7(mux, ot), + }; + return lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); +} + +static range_t lmk05318_get_freq_range(const lmk05318_out_config_t* cfg) +{ + range_t r; + + if(cfg->wanted.freq >= cfg->wanted.freq_delta_minus) + r.min = cfg->wanted.freq - cfg->wanted.freq_delta_minus; + else + r.min = 1; + + r.max = cfg->wanted.freq + cfg->wanted.freq_delta_plus; + + return r; +} + +VWLT_ATTRIBUTE(optimize("-Ofast")) +static inline int lmk05318_get_output_divider(const lmk05318_out_config_t* cfg, double ifreq, uint64_t* div) +{ + *div = (uint64_t)((double)ifreq / cfg->wanted.freq + 0.5); + + if(*div == 0 || *div > cfg->max_odiv) + return 1; + + double factf = ifreq / (*div); + return (fabs(factf - (double)cfg->wanted.freq) < 1E-6) ? 0 : 1; +} + +VWLT_ATTRIBUTE(optimize("-Ofast")) +static inline int lmk05318_solver_helper(lmk05318_out_config_t* outs, unsigned cnt_to_solve, uint64_t f_in, + lmk05318_state_t* lmkst) +{ +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_DEBUG, "Solver iteration FVCO2:%" PRIu64 "", f_in); +#endif + + struct fvco2_range + { + int port_idx; + int pd; + uint64_t od; + range_t fvco2; + }; + typedef struct fvco2_range fvco2_range_t; + + fvco2_range_t fvco2_ranges[(APLL2_PDIV_MAX - APLL2_PDIV_MIN + 1) * 2 * LMK05318_MAX_REAL_PORTS]; + int fvco2_ranges_count = 0; + + // find FVCO2 ranges for all PDs and all ports + for(unsigned i = 0; i < LMK05318_MAX_REAL_PORTS; ++i) + { + lmk05318_out_config_t* out = outs + i; + if(out->solved) + continue; + + for(int pd = out->pd_min; pd <= out->pd_max; ++pd) + { + uint64_t f_pd = (uint64_t)((double)f_in / pd + 0.5); + uint64_t divs[2]; + + lmk05318_get_output_divider(out, f_pd, &divs[0]); + divs[1] = (divs[0] < out->max_odiv) ? divs[0] + 1 : 0; + + for(int d = 0; d < 2; ++d) + { + const uint64_t div = divs[d]; + if(!div) + continue; + + if(div <= out->max_odiv) + { + uint64_t fvco2_min = MAX(pd * div * out->freq_min, VCO_APLL2_MIN); + uint64_t fvco2_max = MIN(pd * div * out->freq_max, VCO_APLL2_MAX); + + if(fvco2_min <= fvco2_max) + { + fvco2_range_t* rr = &fvco2_ranges[fvco2_ranges_count++]; + rr->port_idx = i; + rr->pd = pd; + rr->od = div; + rr->fvco2.min = fvco2_min; + rr->fvco2.max = fvco2_max; + } + } + } + } + } + + if(!fvco2_ranges_count) + { +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_ERROR, "For FVCO2:%" PRIu64 " all possible bands are out of range", f_in); +#endif + return -EINVAL; + } + +#ifdef LMK05318_SOLVER_DEBUG + for(int i = 0; i < fvco2_ranges_count; ++i) + { + fvco2_range_t* rr = &fvco2_ranges[i]; + + USDR_LOG("5318", USDR_LOG_DEBUG, "\t[%d]\tPort#%d PD:%d OD:%" PRIu64 " FVCO2 range:[%" PRIu64 "; %" PRIu64 "]", + i, outs[rr->port_idx].port, rr->pd, rr->od, rr->fvco2.min, rr->fvco2.max); + } +#endif + + struct intersects + { + int prim_idx; + range_t intersection; + int sect_counter; + int sects[(APLL2_PDIV_MAX - APLL2_PDIV_MIN + 1) * 2 * LMK05318_MAX_REAL_PORTS]; + }; + typedef struct intersects intersects_t; + + int intersects_array_count = 0; + intersects_t intersects_array[fvco2_ranges_count]; + + // find FVCO2 ranges intersections + for(int i = 0; i < fvco2_ranges_count; ++i) + { + fvco2_range_t* rr_prim = &fvco2_ranges[i]; + range_t intersection = rr_prim->fvco2; + + int sect_counter = 0; + int sects[fvco2_ranges_count]; + + for(int j = i + 1; j < fvco2_ranges_count; ++j) + { + fvco2_range_t* rr_sec = &fvco2_ranges[j]; + + //ignore equal port variants with different PDs + if(outs[rr_sec->port_idx].port == outs[rr_prim->port_idx].port && cnt_to_solve != 1) + continue; + + uint64_t nmin = MAX(intersection.min, rr_sec->fvco2.min); + uint64_t nmax = MIN(intersection.max, rr_sec->fvco2.max); + + //ignore not-intersected ranges + if(nmin > nmax) + continue; + + intersection.min = nmin; + intersection.max = nmax; + sects[sect_counter++] = j; + } + + if(sect_counter) + { + intersects_t* isect = &intersects_array[intersects_array_count++]; + isect->sect_counter = 0; + isect->prim_idx = i; + isect->intersection = intersection; + + for(int i = 0; i < sect_counter; ++i) + isect->sects[isect->sect_counter++] = sects[i]; + } + } + + if(!intersects_array_count) + { +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_ERROR, "FVCO2 bands have no intersections"); +#endif + return -EINVAL; + } + +#ifdef LMK05318_SOLVER_DEBUG + for(int i = 0; i < intersects_array_count; ++i) + { + const intersects_t* isect = &intersects_array[i]; + fvco2_range_t* rr_prim = &fvco2_ranges[isect->prim_idx]; + + USDR_LOG("5318", USDR_LOG_DEBUG, "Found sects for [%d]\tPort#%d PD:%d OD:%" PRIu64 " FVCO2 range:[%" PRIu64 "; %" PRIu64 "]:", + isect->prim_idx, outs[rr_prim->port_idx].port, rr_prim->pd, rr_prim->od, isect->intersection.min, isect->intersection.max); + + for(int j = 0; j < isect->sect_counter; ++j) + { + const fvco2_range_t* rr = &fvco2_ranges[isect->sects[j]]; + USDR_LOG("5318", USDR_LOG_DEBUG, "\twith [%d] port#%d PD:%d OD:%" PRIu64 "", + isect->sects[j], outs[rr->port_idx].port, rr->pd, rr->od); + } + } +#endif + + struct solution_var_div + { + int pd; + uint64_t od; + }; + typedef struct solution_var_div solution_var_div_t; + + struct solution_var + { + int port_idx; + solution_var_div_t divs[(APLL2_PDIV_MAX - APLL2_PDIV_MIN + 1)]; + int divs_count; + }; + typedef struct solution_var solution_var_t; + + struct solution + { + range_t fvco2; + solution_var_t vars[LMK05318_MAX_REAL_PORTS]; + int vars_count; + bool is_valid; + }; + typedef struct solution solution_t; + + + solution_t solutions[intersects_array_count]; + int solutions_count = 0; + bool has_valid_solution = false; + + // reduce intersections to solutions, filtering out invalid ones + for(int i = 0; i < intersects_array_count; ++i) + { + intersects_t* isect = &intersects_array[i]; + solution_t* sol = &solutions[solutions_count++]; + fvco2_range_t* rr = &fvco2_ranges[isect->prim_idx]; + + sol->vars_count = 0; + sol->is_valid = false; + sol->fvco2 = isect->intersection; + + solution_var_t* var = &sol->vars[sol->vars_count++]; + var->port_idx = rr->port_idx; + var->divs_count = 0; + + solution_var_div_t* div = &var->divs[var->divs_count++]; + div->pd = rr->pd; + div->od = rr->od; + + for(int j = 0; j < isect->sect_counter; ++j) + { + rr = &fvco2_ranges[isect->sects[j]]; + var = NULL; + + for(int k = 0; k < sol->vars_count; ++k) + { + solution_var_t* vv = &sol->vars[k]; + if(vv->port_idx == rr->port_idx) + { + var = vv; + break; + } + } + + if(!var) + { + var = &sol->vars[sol->vars_count++]; + var->port_idx = rr->port_idx; + var->divs_count = 0; + } + + div = NULL; + for(int k = 0; k < var->divs_count; ++k) + { + solution_var_div_t* dd = &var->divs[k]; + if(dd->pd == rr->pd) + { + div = dd; + break; + } + } + + if(!div) + { + div = &var->divs[var->divs_count++]; + div->pd = rr->pd; + div->od = rr->od; + } + } + + sol->is_valid = (sol->vars_count == cnt_to_solve); + if(sol->is_valid) + has_valid_solution = true; + } + +#ifdef LMK05318_SOLVER_DEBUG + for(int i = 0; i < solutions_count; ++i) + { + const solution_t* sol = &solutions[i]; + if(!sol->is_valid) + continue; + + USDR_LOG("5318", USDR_LOG_DEBUG, "Solution [%d] in FVCO2 range [%" PRIu64 "; %" PRIu64 "]:", + i, sol->fvco2.min, sol->fvco2.max); + + for(int j = 0; j < sol->vars_count; ++j) + { + const solution_var_t* var = &sol->vars[j]; + char tmp[1024]; + int tmp_len = sprintf(tmp, "\t Port#%d PD:[", outs[var->port_idx].port); + + for(int k = 0; k < var->divs_count; ++k) + { + const solution_var_div_t* div = &var->divs[k]; + tmp_len += sprintf(tmp + tmp_len, "%d(OD:%" PRIu64 "),", div->pd, div->od); + } + + tmp_len += sprintf(tmp + tmp_len, "]"); + + USDR_LOG("5318", USDR_LOG_DEBUG, "%s", tmp); + } + } +#endif + + if(!has_valid_solution) + { +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_ERROR, "We have NO solutions containing all ports required"); +#endif + return -EINVAL; + } + + struct pd_bind + { + int pd; + int ports[LMK05318_MAX_REAL_PORTS]; + uint64_t odivs[LMK05318_MAX_REAL_PORTS]; + int ports_count; + }; + typedef struct pd_bind pd_bind_t; + + //transform solitions to PD bindings -> PD1:[ports:ODs], PD2:[ports:ODs] + for(int i = 0; i < solutions_count; ++i) + { + solution_t* sol = &solutions[i]; + if(!sol->is_valid) + continue; + + pd_bind_t pd_binds[APLL2_PDIV_COUNT]; + memset(pd_binds, 0, sizeof(pd_binds)); + int pd_binds_count = 0; + int pd1 = 0, pd2 = 0; + + //first var is always one, assume it's PD1 + pd1 = sol->vars[0].divs[0].pd; + pd_binds[0].pd = pd1; + pd_binds[0].ports_count = 1; + pd_binds[0].ports[0] = sol->vars[0].port_idx; + pd_binds[0].odivs[0] = sol->vars[0].divs[0].od; + pd_binds_count = 1; + + int unmapped_ports[LMK05318_MAX_REAL_PORTS]; + int unmapped_ports_count = 0; + + //scan all other vars, try to find variants with PD==PD1 and add them to PD1 binding + //otherwise - add var to an unmapped_ports[] + for(int j = 1; j < sol->vars_count; ++j) + { + const solution_var_t* var = &sol->vars[j]; + bool port_mapped = false; + for(int k = 0; k < var->divs_count; ++k) + { + const solution_var_div_t* div = &var->divs[k]; + if(div->pd == pd1) + { + pd_binds[0].ports[pd_binds[0].ports_count] = var->port_idx; + pd_binds[0].odivs[pd_binds[0].ports_count] = div->od; + pd_binds[0].ports_count++; + port_mapped = true; + break; + } + } + + if(!port_mapped) + { + unmapped_ports[unmapped_ports_count++] = j; + } + } + + if(unmapped_ports_count) + { + //step on first unmapped_ports[] var + const solution_var_t* var = &sol->vars[unmapped_ports[0]]; + + //iterate through its' divs and try to find equal PDs in the unmapped_ports[] below + for(int d = 0; d < var->divs_count; ++d) + { + const solution_var_div_t* div = &var->divs[d]; + + //assume it is PD2 + pd2 = div->pd; + pd_binds[1].pd = pd2; + pd_binds[1].ports[0] = var->port_idx; + pd_binds[1].odivs[0] = div->od; + pd_binds[1].ports_count = 1; + + //iterate unmapped ports below and try to find vars with PD==PD2 and add them to PD2 binding + for(int u = 1; u < unmapped_ports_count; ++u) + { + const solution_var_t* var2 = &sol->vars[unmapped_ports[u]]; + bool found = false; + for(int dd = 0; dd < var2->divs_count; ++dd) + { + const solution_var_div_t* div2 = &var2->divs[dd]; + if(div2->pd == pd2) + { + pd_binds[1].ports[pd_binds[1].ports_count] = var2->port_idx; + pd_binds[1].odivs[pd_binds[1].ports_count] = div2->od; + pd_binds[1].ports_count++; + + found = true; + break; + } + } + + //if this var does not contain the assumed PD2, no need to continue - break and try next PD2 + if(!found) + { + break; + } + } + + //check if we mapped all the ports needed + int binded_ports = pd_binds[0].ports_count + pd_binds[1].ports_count; + if(binded_ports == cnt_to_solve) + { + pd_binds_count = pd_binds[1].ports_count ? 2 : 1; + sol->is_valid = true; + } + else + { + sol->is_valid = false; + continue; + } + } + } + + if(!sol->is_valid) + continue; + + USDR_LOG("5318", USDR_LOG_DEBUG, "SOLUTION#%d valid:%d FVCO2[%" PRIu64 "; %" PRIu64 "]->", i, sol->is_valid, sol->fvco2.min, sol->fvco2.max); + + for(uint64_t f = sol->fvco2.min; f <= sol->fvco2.max; ++f) + { + unsigned n = 0, num = 0, den = 0; + double fvco2 = lmk05318_calc_vco2_div(lmkst, f, &n, &num, &den); + + if(fvco2 < sol->fvco2.min || fvco2 > sol->fvco2.max) + continue; + + if(fabs(fvco2 - (uint64_t)fvco2) > 1E-6) + continue; + + bool ok_flag = true; + + for(int ii = 0; ii < pd_binds_count; ++ii) + { + const pd_bind_t* b = &pd_binds[ii]; + + for(int j = 0; j < b->ports_count; ++j) + { + lmk05318_out_config_t* out = &outs[b->ports[j]]; + + uint64_t div; + const uint64_t fdiv_in = (uint64_t)(fvco2 / b->pd + 0.5); + int res = lmk05318_get_output_divider(out, fdiv_in, &div); + if(res) + { + ok_flag = false; + break; + } + + out->result.out_div = div; + out->result.freq = fvco2 / b->pd / div; + out->result.mux = (b->pd == pd1) ? OUT_PLL_SEL_APLL2_P1 : OUT_PLL_SEL_APLL2_P2; + out->solved = true; + } + + if(!ok_flag) + break; + } + + if(ok_flag) + { + lmkst->vco2_freq = fvco2; + lmkst->vco2_n = n; + lmkst->vco2_num = num; + lmkst->vco2_den = den; + lmkst->pd1 = pd1; + lmkst->pd2 = pd2; + return 0; + } + } + } + +#ifdef LMK05318_SOLVER_DEBUG + USDR_LOG("5318", USDR_LOG_ERROR, "We have NO solutions using 2 PDs (need more pre-dividers)"); +#endif + return -EINVAL; +} + + +static int lmk05318_comp_port(const void * elem1, const void * elem2) +{ + const lmk05318_out_config_t* f = (lmk05318_out_config_t*)elem1; + const lmk05318_out_config_t* s = (lmk05318_out_config_t*)elem2; + + if(f->port < s->port) return -1; + if(f->port > s->port) return 1; + return 0; +} + + +static const char* lmk05318_decode_mux(enum lmk05318_out_pll_sel_t mux) +{ + switch(mux) + { + case OUT_PLL_SEL_APLL1_P1: return "APLL1"; + case OUT_PLL_SEL_APLL1_P1_INV: return "APLL1 inv"; + case OUT_PLL_SEL_APLL2_P1: return "APLL2 PD1"; + case OUT_PLL_SEL_APLL2_P2: return "APLL2 PD2"; + } + return "UNKNOWN"; +} + +VWLT_ATTRIBUTE(optimize("-Ofast")) +int lmk05318_solver(lmk05318_state_t* d, lmk05318_out_config_t* _outs, unsigned n_outs) +{ + int res; + + if(!_outs || !n_outs || n_outs > LMK05318_MAX_OUT_PORTS) + { + USDR_LOG("5318", USDR_LOG_ERROR, "input data is incorrect"); + return -EINVAL; + } + + // internally we have only _6_ out divs and freqs (0==1 and 2==3) - except the output type, but it does not matter here + lmk05318_out_config_t outs[LMK05318_MAX_REAL_PORTS]; + memset(outs, 0, sizeof(outs)); + + for(unsigned i = 0; i < n_outs; ++i) + { + lmk05318_out_config_t* out = _outs + i; + + if(out->port > LMK05318_MAX_OUT_PORTS - 1) + { + USDR_LOG("5318", USDR_LOG_ERROR, "port value should be in [0; %d] diap", (LMK05318_MAX_OUT_PORTS - 1)); + return -EINVAL; + } + + if(out->wanted.type > HCSL_INT_50 && out->port < 4 && out->wanted.freq) + { + USDR_LOG("5318", USDR_LOG_ERROR, "LVCMOS output type supported for ports# 4..7 only"); + return -EINVAL; + } + + unsigned port; + if(out->port < 2) + port = 0; + else if(out->port < 4) + port = 1; + else + port = out->port - 2; + + lmk05318_out_config_t* norm_out = outs + port; + + // check dup ports and 0-1 2-3 equality + if(norm_out->wanted.freq && + (norm_out->wanted.freq != out->wanted.freq || + norm_out->wanted.freq_delta_plus != out->wanted.freq_delta_plus || + norm_out->wanted.freq_delta_minus != out->wanted.freq_delta_minus || + norm_out->wanted.revert_phase != out->wanted.revert_phase || + norm_out->wanted.pll_affinity != out->wanted.pll_affinity + )) + { + USDR_LOG("5318", USDR_LOG_ERROR, "dup ports values detected, or ports #0:1 & #2:3 differ"); + return -EINVAL; + } + + if(out->wanted.freq == 0) + { + USDR_LOG("5318", USDR_LOG_DEBUG, "skipping port#%d freq=0", out->port); + } + else + { + range_t r = lmk05318_get_freq_range(out); + out->freq_min = r.min; + out->freq_max = r.max; + out->max_odiv = lmk05318_max_odiv(out->port); + } + + *norm_out = *out; + norm_out->solved = false; + } + + //now outs[] contains effective ports ordered (0..5) config. + //some elems may be not initialized (wanted.freq == 0) and should not be processed. + for(unsigned i = 0; i < LMK05318_MAX_REAL_PORTS; ++i) + { + outs[i].solved = outs[i].wanted.freq == 0; + USDR_LOG("5318", USDR_LOG_DEBUG, "port:%s%d freq:%d (-%d, +%d) *%s*", + outs[i].port == 1 ? "0-" : (outs[i].port == 3 ? "2-" : " "), + outs[i].port, outs[i].wanted.freq, outs[i].wanted.freq_delta_minus, outs[i].wanted.freq_delta_plus, + outs[i].solved ? "not used" : "active"); + } + + //first we try routing ports to APLL1 + //it's easy + for(unsigned i = 0; i < LMK05318_MAX_REAL_PORTS; ++i) + { + lmk05318_out_config_t* out = outs + i; + if(out->solved) + continue; + + if(out->wanted.pll_affinity == AFF_ANY || out->wanted.pll_affinity == AFF_APLL1) + { + uint64_t odiv = 0; + int res = lmk05318_get_output_divider(out, VCO_APLL1, &odiv); + if(!res) + { + out->solved = true; + out->result.out_div = odiv; + out->result.freq = (double)VCO_APLL1 / odiv; + out->result.mux = out->wanted.revert_phase ? OUT_PLL_SEL_APLL1_P1_INV : OUT_PLL_SEL_APLL1_P1; + + USDR_LOG("5318", USDR_LOG_DEBUG, "port:%d solved via APLL1 [OD:%" PRIu64 " freq:%.2f mux:%d]", + out->port, out->result.out_div, out->result.freq, out->result.mux); + } + else + { + USDR_LOG("5318", USDR_LOG_DEBUG, "port:%d cannot solve it via APLL1, will try APLL2", out->port); + } + } + else + { + USDR_LOG("5318", USDR_LOG_DEBUG, "port:%d forbidden to solve via APLL1 by config, will try APLL2", out->port); + } + + //we cannot revert phase for ports NOT linked to APLL1 + if(!out->solved && out->wanted.revert_phase) + { + USDR_LOG("5318", USDR_LOG_ERROR, "port#%d specified as phase-reverted and cannot be solved via APLL1", out->port); + return -EINVAL; + } + } + + //second - try routing to APLL2 + unsigned cnt_to_solve = 0; + USDR_LOG("5318", USDR_LOG_DEBUG,"Need to solve via APLL2:"); + for(unsigned i = 0; i < LMK05318_MAX_REAL_PORTS; ++i) + { + if(outs[i].solved) + continue; + + ++cnt_to_solve; + + USDR_LOG("5318", USDR_LOG_DEBUG, "\tport:%d freq:%d (-%d, +%d)", + outs[i].port, outs[i].wanted.freq, outs[i].wanted.freq_delta_minus, outs[i].wanted.freq_delta_plus); + } + + if(cnt_to_solve) { + static const uint64_t fvco2_pd_min = VCO_APLL2_MIN / APLL2_PDIV_MAX; + static const uint64_t fvco2_pd_max = VCO_APLL2_MAX / APLL2_PDIV_MIN; + + //determine valid PD ranges for our frequencies + for(unsigned i = 0; i < LMK05318_MAX_REAL_PORTS; ++i) + { + lmk05318_out_config_t* out = outs + i; + if(out->solved) + continue; + + const range_t r = lmk05318_get_freq_range(out); + const range_t ifreq = {MAX(r.min, fvco2_pd_min) , MIN(r.max * lmk05318_max_odiv(out->port), fvco2_pd_max)}; + + if(ifreq.min > ifreq.max) + { + USDR_LOG("5318", USDR_LOG_ERROR, "port#%d freq:%d (-%d, +%d) is totally out of available range", + out->port, out->wanted.freq, out->wanted.freq_delta_minus, out->wanted.freq_delta_plus); + return -EINVAL; + } + + const int pd_min = VCO_APLL2_MAX / ifreq.max; + const int pd_max = VCO_APLL2_MAX / ifreq.min; + + out->pd_min = pd_min; + out->pd_max = pd_max; + + USDR_LOG("5318", USDR_LOG_DEBUG, "port:%d pre-OD freq range:[%" PRIu64", %" PRIu64"], PD:[%d, %d]", + out->port, ifreq.min, ifreq.max, pd_min, pd_max); + } + + const uint64_t f_mid = (VCO_APLL2_MAX + VCO_APLL2_MIN) / 2; + res = lmk05318_solver_helper(outs, cnt_to_solve, f_mid, d); + if(res) + return res; + } + + //if ok - update the results + + qsort(_outs, n_outs, sizeof(lmk05318_out_config_t), lmk05318_comp_port); + bool complete_solution_check = true; + + USDR_LOG("5318", USDR_LOG_DEBUG, "=== COMPLETE SOLUTION @ VCO1:%" PRIu64 " VCO2:%" PRIu64 " PD1:%d PD2:%d ===", VCO_APLL1, d->vco2_freq, d->pd1, d->pd2); + for(unsigned i = 0; i < n_outs; ++i) + { + lmk05318_out_config_t* out_dst = _outs + i; + lmk05318_out_config_t* out_src = NULL; + + if(out_dst->wanted.freq == 0) + continue; + + if(out_dst->port < 2) + out_src = outs + 0; + else if(out_dst->port < 4) + out_src = outs + 1; + else + out_src = outs + (out_dst->port - 2); + + out_dst->solved = out_src->solved; + out_dst->result = out_src->result; + + //check it + const range_t r = lmk05318_get_freq_range(out_dst); + const double f = out_dst->result.freq; + const bool is_freq_ok = f >= r.min && f <= r.max; + if(!is_freq_ok) + complete_solution_check = false; + + USDR_LOG("5318", is_freq_ok ? USDR_LOG_DEBUG : USDR_LOG_ERROR, "port:%d solved [OD:%" PRIu64 " freq:%.8f mux:%d(%s) fmt:%u(%s)] %s", + out_dst->port, out_dst->result.out_div, out_dst->result.freq, out_dst->result.mux, + lmk05318_decode_mux((enum lmk05318_out_pll_sel_t)out_dst->result.mux), out_dst->wanted.type, lmk05318_decode_fmt_to_string(out_dst->wanted.type), + is_freq_ok ? "**OK**" : "**BAD**"); + } + + if(complete_solution_check == false) + return -EINVAL; + + if(d) + { + for(unsigned i = 0; i < n_outs; ++i) + { + const lmk05318_out_config_t* out = _outs + i; + d->outputs[out->port].freq = out->result.freq; + d->outputs[out->port].odiv = out->result.out_div; + d->outputs[out->port].mux = out->result.mux; + } + } + + //tune APLL2 + res = lmk05318_tune_apll2(d); + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "error %d tuning APLL2", res); + return res; + } + + //set output ports + for(unsigned i = 0; i < n_outs; ++i) + { + const lmk05318_out_config_t* out = _outs + i; + + if(out->wanted.freq == 0) + { + USDR_LOG("5318", USDR_LOG_DEBUG, "OUT%u port:%u DISABLED fmt:%u(%s)", + i, out->port, OUT_OFF, lmk05318_decode_fmt_to_string(OUT_OFF)); + + res = lmk05318_set_out_mux(d, out->port, 0, OUT_OFF); + } + else + { + USDR_LOG("5318", USDR_LOG_DEBUG, "OUT%u port:%u div:%" PRIu64 " fmt:%u(%s)", + i, out->port, out->result.out_div, out->wanted.type, lmk05318_decode_fmt_to_string(out->wanted.type)); + + res = lmk05318_set_out_mux(d, out->port, out->result.mux, out->wanted.type); + res = res ? res : lmk05318_set_out_div(d, out->port, out->result.out_div); + } + + if(res) + { + USDR_LOG("5318", USDR_LOG_ERROR, "error %d setting mux/div for port#%d", res, out->port); + return res; + } + } + + return 0; +} + +static inline void lmk05318_decode_los_mask(unsigned m, char* s) +{ + if(!s) + return; + + unsigned len = 0; + *s = 0; + + if(m & LMK05318_LOS_XO) + len += sprintf(s + len, "%s ", "XO"); + if(m & LMK05318_LOL_PLL1) + len += sprintf(s + len, "%s ", "PLL1"); + if(m & LMK05318_LOL_PLL2) + len += sprintf(s + len, "%s ", "PLL2"); + if(m & LMK05318_LOS_FDET_XO) + len += sprintf(s + len, "%s ", "XO_FDET"); + if(m & LMK05318_LOPL_DPLL) + len += sprintf(s + len, "%s ", "DPLL_P"); + if(m & LMK05318_LOFL_DPLL) + len += sprintf(s + len, "%s ", "DPLL_F"); + if(m & LMK05318_BAW_LOCK) + len += sprintf(s + len, "%s ", "BAW"); +} + +int lmk05318_check_lock(lmk05318_state_t* d, unsigned* los_msk, bool silent) +{ + uint8_t los[3]; + int res = 0; + unsigned losval; + + res = res ? res : lmk05318_reg_rd(d, INT_FLAG0, &los[0]); + res = res ? res : lmk05318_reg_rd(d, INT_FLAG1, &los[1]); + res = res ? res : lmk05318_reg_rd(d, BAW_LOCKDET_PPM_MAX_BY1, &los[2]); + + if (res) + return res; + + losval = ((los[0] & LOS_XO_POL_MSK) ? LMK05318_LOS_XO : 0) | + ((los[0] & LOL_PLL1_POL_MSK) ? LMK05318_LOL_PLL1 : 0) | + ((los[0] & LOL_PLL2_POL_MSK) ? LMK05318_LOL_PLL2 : 0) | + ((los[0] & LOS_FDET_XO_POL_MSK) ? LMK05318_LOS_FDET_XO : 0) | + ((los[1] & LOPL_DPLL_POL_MSK) ? LMK05318_LOPL_DPLL : 0) | + ((los[1] & LOFL_DPLL_POL_MSK) ? LMK05318_LOFL_DPLL : 0) | + ((los[2] & BAW_LOCK_MSK) ? LMK05318_BAW_LOCK : 0); + + if(!silent) + { + char ss[255]; + lmk05318_decode_los_mask(losval, ss); + USDR_LOG("5318", USDR_LOG_ERROR, "LMK05318 LOS_MASK=[%s] %02x:%02x:%02x\n", ss, los[0], los[1], los[2]); + } + + *los_msk = losval; + return 0; +} + +int lmk05318_wait_apll1_lock(lmk05318_state_t* d, unsigned timeout) +{ + int res = 0; + unsigned elapsed = 0; + bool locked = false; + uint8_t reg; + unsigned los_msk; + bool pll1_vm_inside; + + res = lmk05318_apll1_calibrate(d); + if(res) + return res; + + res = lmk05318_reset_los_flags(d); + if(res) + return res; + + while(timeout == 0 || elapsed < timeout) + { + uint64_t tk = clock_get_time(); + + res = lmk05318_reg_rd(d, PLL1_CALSTAT1, ®); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK05318 read(PLL1_CALSTAT1) error:%d", res); + return res; + } + pll1_vm_inside = reg & PLL1_VM_INSIDE_MSK; + + res = lmk05318_check_lock(d, &los_msk, true/*silent*/); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK05318 lmk05318_check_lock() error:%d", res); + return res; + } + + if(d->dpll.enabled) + { + locked = pll1_vm_inside && !(los_msk & LMK05318_LOPL_DPLL) && !(los_msk & LMK05318_LOFL_DPLL); + } + else + { + locked = pll1_vm_inside && (los_msk & LMK05318_BAW_LOCK); + } + + if(locked) + break; + + usleep(100); + elapsed += (clock_get_time() - tk); + } + + USDR_LOG("5318", USDR_LOG_INFO, "ELAPSED:%.4fs PLL1_CALSTAT1:%u PLL1_VM_INSIDE:0x%02x LOS_MASK:0x%02x LMK05318_BAW_LOCK:%u", + (double)elapsed / 1000000.f, reg, pll1_vm_inside ? 1 : 0, los_msk,(los_msk & LMK05318_BAW_LOCK) ? 1 : 0); + + if(!locked) + { + USDR_LOG("5318", USDR_LOG_ERROR, "APLL1 is not locked!"); + return -ETIMEDOUT; + } + + USDR_LOG("5318", USDR_LOG_INFO, "APLL1 locked OK"); + return 0; +} + +int lmk05318_wait_apll2_lock(lmk05318_state_t* d, unsigned timeout) +{ + if(d->vco2_freq == 0) + { + USDR_LOG("5318", USDR_LOG_DEBUG, "APLL2 disabled, check lock ignored"); + return 0; + } + + int res = 0; + unsigned elapsed = 0; + bool locked = false; + uint8_t reg; + unsigned los_msk; + bool pll2_vm_inside; + + res = lmk05318_apll2_calibrate(d); + if(res) + return res; + + res = lmk05318_reset_los_flags(d); + if(res) + return res; + + while(timeout == 0 || elapsed < timeout) + { + uint64_t tk = clock_get_time(); + + res = lmk05318_reg_rd(d, PLL2_CALSTAT1, ®); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK05318 read(PLL2_CALSTAT1) error:%d", res); + return res; + } + pll2_vm_inside = reg & PLL2_VM_INSIDE_MSK; + + res = lmk05318_check_lock(d, &los_msk, true/*silent*/); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK05318 lmk05318_check_lock() error:%d", res); + return res; + } + + locked = pll2_vm_inside && !(los_msk & LMK05318_LOL_PLL2); + + if(locked) + break; + + usleep(100); + elapsed += (clock_get_time() - tk); + } + + USDR_LOG("5318", USDR_LOG_INFO, "ELAPSED:%.4fs PLL2_CALSTAT1:%u PLL2_VM_INSIDE:0x%02x LOS_MASK:0x%02x LMK05318_LOL_PLL2:%u", + (double)elapsed / 1000000.f, reg, pll2_vm_inside ? 1 : 0, los_msk,(los_msk & LMK05318_LOL_PLL2) ? 1 : 0); + + + if(!locked) + { + USDR_LOG("5318", USDR_LOG_ERROR, "APLL2 is not locked!"); + return -ETIMEDOUT; + } + + USDR_LOG("5318", USDR_LOG_INFO, "APLL2 locked OK"); + return 0; +} + +static const char* lmk05318_decode_dpll_refsel_stat(uint8_t stat) +{ + switch(stat) + { + case 0: return "HOLDOVER"; + case 1: return "PRIREF"; + case 2: return "SECREF"; + case 3: return "RESERVED"; + } + + return "UNKNOWN"; +} + +int lmk05318_wait_dpll_ref_stat(lmk05318_state_t* d, unsigned timeout) +{ + if(!d->dpll.enabled) + { + USDR_LOG("5318", USDR_LOG_DEBUG, "DPLL disabled, validating ref ignored"); + return 0; + } + + int res = 0; + unsigned elapsed = 0; + bool valid = false; + uint8_t reg, reg2; + + while(timeout == 0 || elapsed < timeout) + { + uint64_t tk = clock_get_time(); + + res = lmk05318_reg_rd(d, REFVALSTAT, ®); + res = res ? res : lmk05318_reg_rd(d, 0xa7, ®2); + if(res) + { + USDR_LOG("SYNC", USDR_LOG_ERROR, "LMK05318 read(REFVALSTAT) error:%d", res); + return res; + } + + bool pri_valid = d->dpll.ref_en[LMK05318_PRIREF] ? (reg & PRIREF_VALSTAT_MSK) : true; + bool sec_valid = d->dpll.ref_en[LMK05318_SECREF] ? (reg & SECREF_VALSTAT_MSK) : true; + valid = pri_valid && sec_valid; + + if(valid) + break; + + usleep(1000000); + elapsed += (clock_get_time() - tk); + } + + USDR_LOG("5318", USDR_LOG_INFO, "ELAPSED:%.4fs PRIREF_VALSTAT:%u SECREF_VALSTAT:%u DPLL_REFSEL_STAT:0x%02x(%s)", + (double)elapsed / 1000000.f, + (reg & PRIREF_VALSTAT_MSK) >> PRIREF_VALSTAT_OFF, + (reg & SECREF_VALSTAT_MSK) >> SECREF_VALSTAT_OFF, + reg2, lmk05318_decode_dpll_refsel_stat(reg2 & 0b11)); + + if(!valid) + { + USDR_LOG("5318", USDR_LOG_ERROR, "DPLL input reference NOT VALID!"); + return -ETIMEDOUT; + } + + USDR_LOG("5318", USDR_LOG_INFO, "DPLL input reference is valid"); return 0; } diff --git a/src/lib/hw/lmk05318/lmk05318.h b/src/lib/hw/lmk05318/lmk05318.h index 5d05fd54..e9b14153 100644 --- a/src/lib/hw/lmk05318/lmk05318.h +++ b/src/lib/hw/lmk05318/lmk05318.h @@ -6,6 +6,76 @@ #include +#define LMK05318_MAX_OUT_PORTS 8 +#define LMK05318_MAX_REAL_PORTS (LMK05318_MAX_OUT_PORTS - 2) + +enum xo_input_type +{ + XO_DC_DIFF_EXT = 0, + XO_AC_DIFF_EXT, + XO_AC_DIFF_INT_100, + XO_HCSL_INT_50, + XO_CMOS, + XO_SE_INT_50, +}; +typedef enum xo_input_type xo_input_type_t; + +enum +{ + DPLL_REF_TYPE_DIFF_NOTERM = 1, + DPLL_REF_TYPE_DIFF_100 = 3, + DPLL_REF_TYPE_DIFF_50 = 5, + DPLL_REF_TYPE_SE_NOTERM = 8, + DPLL_REF_TYPE_SE_50 = 0xC, +}; + +enum +{ + DPLL_REF_AC_COUPLED_INT = 0, + DPLL_REF_DC_COUPLED_INT = 1, +}; + +enum +{ + DPLL_REF_AC_BUF_HYST50_DC_EN = 0, + DPLL_REF_AC_BUF_HYST200_DC_DIS = 1, +}; + +struct lmk05318_xo_settings +{ + unsigned pll1_fref_rdiv; + uint32_t fref; + xo_input_type_t type; + bool doubler_enabled; + bool fdet_bypass; +}; +typedef struct lmk05318_xo_settings lmk05318_xo_settings_t; + +enum +{ + LMK05318_PRIREF = 0, + LMK05318_SECREF = 1, +}; + +struct lmk05318_dpll_settings +{ + bool enabled; + bool en[2]; + uint64_t fref[2]; + uint8_t dc_mode[2]; + uint8_t buf_mode[2]; + uint8_t type[2]; +}; +typedef struct lmk05318_dpll_settings lmk05318_dpll_settings_t; + +struct lmk05318_output +{ + double freq; + uint64_t odiv; + int mux; +}; +typedef struct lmk05318_output lmk05318_output_t; + struct lmk05318_state { lldev_t dev; unsigned subdev; @@ -17,23 +87,117 @@ struct lmk05318_state { // VCO2 freq uint64_t vco2_freq; + unsigned vco2_n, vco2_num, vco2_den; + unsigned pd1, pd2; + + lmk05318_output_t outputs[LMK05318_MAX_OUT_PORTS]; + + struct { + bool enabled; + bool ref_en[2]; + uint16_t rdiv[2]; + double ftdc; + double lbw; + uint8_t pre_div; + uint64_t n, num, den; + bool zdm; + } dpll; + + lmk05318_xo_settings_t xo; }; enum lmk05318_type { + OUT_OFF = 0, + // LVDS, CML, LVPECL, - LVCMOS, - OUT_OFF, + HCSL_EXT_50, + HCSL_INT_50, + // formats below supported by ports 4..7 only + LVCMOS_HIZ_HIZ, + LVCMOS_HIZ_N, + LVCMOS_HIZ_P, + LVCMOS_LOW_LOW, + LVCMOS_N_HIZ, + LVCMOS_N_N, + LVCMOS_N_P, + LVCMOS_P_HIZ, + LVCMOS_P_N, + LVCMOS_P_P, }; typedef struct lmk05318_state lmk05318_state_t; +typedef enum lmk05318_type lmk05318_type_t; + +enum lmk05318_port_affinity +{ + AFF_ANY = 0, + AFF_APLL1, + AFF_APLL2 +}; +typedef enum lmk05318_port_affinity lmk05318_port_affinity_t; + +struct lmk05318_out_config +{ + unsigned port; //0..7 + + // these fields are inputs + struct + { + uint32_t freq; + unsigned freq_delta_plus, freq_delta_minus; + bool revert_phase; + lmk05318_type_t type; + lmk05318_port_affinity_t pll_affinity; + } wanted; + + // these fields are results + struct + { + double freq; + uint64_t out_div; + int mux; + } result; + + //* + // these fields are for internal use, do not touch them. Use lmk05318_port_request(). + bool solved; + uint64_t max_odiv; + uint32_t freq_min, freq_max; + uint32_t pd_min, pd_max; + //* +}; +typedef struct lmk05318_out_config lmk05318_out_config_t; -int lmk05318_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned flags, lmk05318_state_t* out); +#define LMK05318_FREQ_DELTA 2 -int lmk05318_tune_apll2(lmk05318_state_t* d, uint32_t freq, unsigned *last_div); -int lmk05318_set_out_div(lmk05318_state_t* d, unsigned port, unsigned div); -int lmk05318_set_out_mux(lmk05318_state_t* d, unsigned port, bool pll1, unsigned otype); +static inline int lmk05318_port_request(lmk05318_out_config_t* p, + unsigned port, + uint32_t freq, + bool revert_phase, + lmk05318_type_t type) +{ + if(port > LMK05318_MAX_OUT_PORTS - 1) + return -EINVAL; + + memset(p, 0, sizeof(*p)); + p->port = port; + p->wanted.freq = freq; + p->wanted.freq_delta_plus = LMK05318_FREQ_DELTA; + p->wanted.freq_delta_minus = LMK05318_FREQ_DELTA; + p->wanted.revert_phase = revert_phase; + p->wanted.type = type; + p->wanted.pll_affinity = AFF_ANY; + p->solved = false; + return 0; +} + +static inline int lmk05318_set_port_affinity(lmk05318_out_config_t* p, lmk05318_port_affinity_t aff) +{ + p->wanted.pll_affinity = aff; + return 0; +} enum lock_msk { LMK05318_LOS_XO = 1, @@ -43,12 +207,40 @@ enum lock_msk { LMK05318_LOPL_DPLL = 16, LMK05318_LOFL_DPLL = 32, - + LMK05318_BAW_LOCK = 64, }; -int lmk05318_check_lock(lmk05318_state_t* d, unsigned* los_msk); +int lmk05318_set_out_div(lmk05318_state_t* d, unsigned port, uint64_t div); +int lmk05318_sync(lmk05318_state_t* out); +int lmk05318_mute(lmk05318_state_t* out, uint8_t chmask); +int lmk05318_disable_port(lmk05318_state_t* d, unsigned port); +int lmk05318_enable_port(lmk05318_state_t* d, unsigned port, unsigned fmt); +int lmk05318_reset_los_flags(lmk05318_state_t* d); +int lmk05318_check_lock(lmk05318_state_t* d, unsigned* los_msk, bool silent); +int lmk05318_wait_apll1_lock(lmk05318_state_t* d, unsigned timeout); +int lmk05318_wait_apll2_lock(lmk05318_state_t* d, unsigned timeout); +int lmk05318_softreset(lmk05318_state_t* out); +int lmk05318_set_out_mux(lmk05318_state_t* d, unsigned port, unsigned mux, unsigned otype); int lmk05318_reg_wr(lmk05318_state_t* d, uint16_t reg, uint8_t out); int lmk05318_reg_rd(lmk05318_state_t* d, uint16_t reg, uint8_t* val); +int lmk05318_reg_wr_from_map(lmk05318_state_t* d, bool dry_run); + +int lmk05318_set_xo_fref(lmk05318_state_t* d); +int lmk05318_tune_apll1(lmk05318_state_t* d); + +int lmk05318_solver(lmk05318_state_t* d, lmk05318_out_config_t* _outs, unsigned n_outs); + +int lmk05318_create(lldev_t dev, unsigned subdev, unsigned lsaddr, + uint32_t xo_freq, xo_input_type_t xo_fmttype, bool xo_fdet_bypass, + lmk05318_dpll_settings_t* dpll, + lmk05318_out_config_t* out_ports_cfg, unsigned out_ports_len, + lmk05318_state_t* out, bool dry_run); + +int lmk05318_dpll_config(lmk05318_state_t* d, lmk05318_dpll_settings_t* dpll); +int lmk05318_wait_dpll_ref_stat(lmk05318_state_t* d, unsigned timeout); + +int lmk05318_apll1_calibrate(lmk05318_state_t* d); +int lmk05318_apll2_calibrate(lmk05318_state_t* d); #endif diff --git a/src/lib/hw/lmk05318/lmk05318.yaml b/src/lib/hw/lmk05318/lmk05318.yaml index 77f4aaec..94f3ad65 100644 --- a/src/lib/hw/lmk05318/lmk05318.yaml +++ b/src/lib/hw/lmk05318/lmk05318.yaml @@ -8,7 +8,7 @@ revision: "0.0.1" processors: [ c ] bus: type: I2C - wr_mask: 0x800000 + rd_mask: 0x800000 usdr_path: /debug/hw/lmk05318/0/reg addr_width: 16 data_width: 8 @@ -57,6 +57,21 @@ x-mute-lvl: &mute-lvl 0x2: DIFF_HIGH_P_LOW_N_BY 0x3: DIFF_LOW_P_LOW_N_LOW +x-ref-dc-mode: &ref-dc-mode + 0x0: AC_COUPLED_INT + 0x1: DC_COUPLED_INT + +x-ref-buf-mode: &ref-buf-mode + 0x0: AC_HYST50_DC_EN + 0x1: AC_HYST200_DC_DIS + +x-ref-input-type: &ref-input-type + 0x1: DIFF_NOTERM + 0x3: DIFF_100 + 0x5: DIFF_50 + 0x8: SE_NOTERM + 0xC: SE_50 + pages: - name: Top regs: @@ -78,6 +93,12 @@ pages: - bits: "3" name: SYNC_MUTE desc: Determines if the output drivers are muted during a SYNC event, 0x0 = Do not mute any outputs during SYNC, 0x1 = Mute all outputs during SYNC + - bits: "1" + name: PLLSTRTMODE + desc: PLL Startup Mode . When using cascade mode, PLL1 is fixed to a center value while PLL2 locks. Then PLL1 performs final lock. + - bits: "0" + name: AUTOSTRT + desc: Autostart. If AUTOSTRT is set to 1, the device will automatically initiate the PLL and output start-up sequence after a device reset. A device reset can be triggered by the power-on-reset, PDN pin, or by writing to the RESET_SW bit. If AUTOSTRT is 0, the device will halt after the configuration phase; a subsequent write to set the AUTOSTRT bit will initiate the start-up sequence. In Test mode, the AUTOSTRT bit is ignored after device reset, but start-up can be triggered by a subsequent write to set the AUTOSTART bit. - addr: 0xD name: INT_LIVE0 fields: @@ -370,9 +391,11 @@ pages: - bits: "3" name: SECREF_DC_MODE desc: SECREF DC buffer mode; 0x0 = SECREF is AC coupled internally; 0x1 = SECREF is DC coupled internally + opts: *ref-dc-mode - bits: "2" name: PRIREF_DC_MODE desc: PRIREF DC buffer mode; 0x0 = PRIREF is AC coupled internally; 0x1 = PRIREF is DC coupled internally + opts: *ref-dc-mode - bits: "0" name: APLL2_DEN_MODE desc: Programmable APLL2 denominator mode; 0x0 = APLL2 uses fixed 24-bit denominator; 0x1 = APLL2 uses programmable 24-bit denominator (R333, R334, R335) @@ -385,13 +408,25 @@ pages: - bits: "3" name: XO_FDET_BYP desc: XO Frequency Detector Bypass If bypassed, the XO detector status is ignored and the XO input is considered valid by the PLL control state machines + - bits: "2" + name: XO_DETECT_BYP + desc: XO Amplitude Detector Bypass. If bypassed, the XO input is considered to be valid by the PLL control state machines. XO_DETECT_BYP bit has no effect on the Interrupt register or status outputs. + - bits: "0" + name: XO_BUFSEL + desc: XO Input Buffer Enable - addr: 0x2B name: XO_CLKCTL2 fields: + - bits: "7" + name: XO_CLKCTL2_RESERVED7 + desc: reset=1 - bits: "6-3" name: XO_TYPE desc: XO Input Type; 0x0 = DC-Differential (external termination); 0x1 = AC-Differential (external termination); 0x3 = AC-Differential (internal termination 100-Ω); 0x4 = HCSL (internal termination 50-Ω); 0x8 = CMOS; 0xC = Single-ended (internal termination 50-Ω) opts: *in-opts + - bits: "2-0" + name: XO_CLKCTL2_RESERVED0 + desc: reset=0x10 - addr: 0x2C name: XO_CONFIG fields: @@ -407,23 +442,34 @@ pages: - bits: "2" name: PRIREF_CMOS_SLEW desc: PRIREF input buffer slew rate; 0x0 = Select Amplitude Detector Mode; 0x1 = Select CMOS Amplitude Detector Mode + - bits: "1" + name: SECREF_BUF_MODE + desc: SECREF buffer mode. 0x0 - Set AC buffer hysteresis to 50mV or enable DC buffer hysteresis; 0x1 = Set AC buffer hysteresis to 200mV or disable DC buffer hysteresis + opts: *ref-buf-mode + - bits: "0" + name: PRIREF_BUF_MODE + desc: PRIREF buffer mode. 0x0 - Set AC buffer hysteresis to 50mV or enable DC buffer hysteresis; 0x1 = Set AC buffer hysteresis to 200mV or disable DC buffer hysteresis + opts: *ref-buf-mode - addr: 0x2E name: REF_CLKCTL2 fields: - bits: "7-4" name: SECREF_TYPE desc: SECREF Input Type See PRIREF_TYPE for input type bit settings. - opts: *in-opts + opts: *ref-input-type - bits: "3-0" name: PRIREF_TYPE - desc: PRIREF Input Type; 0x0 = DC-Differential (external termination); 0x1 = AC-Differential (external termination); 0x3 = AC-Differential (internal termination 100-Ω); 0x4 = HCSL (internal termination 50-Ω); 0x8 = CMOS; 0xC = Single-ended (internal termination 50-Ω) - opts: *in-opts + desc: PRIREF Input Type + opts: *ref-input-type - addr: 0x2F name: PLL_CLK_CFG fields: - bits: "7" name: PLL2_RCLK_SEL - desc: PLL2 Reference clock selection; 0x0 = VCO1 - Cascaded Mode; 0x1 = XO + desc: PLL2 Reference clock selection; 0x0 = VCO1 - Cascaded Mode; 0x1 = XO + - bits: "2-0" + name: PLL1_VCO_TO_CNTRS_EN + desc: PLL1 VCO to counters enable. Enables VCO1 output drivers to PLL1 N counter, DPLL, digital top, PLL2 R divider. Bit 0 enables VCO1 output for DPLL TDC, reference window detect, DPLL loop filter high speed clock and ppm checker clock. Bit 1 enables VCO1 output to PLL1 N counter Bit 2 enables the PLL1_P1 output to PLL2 R divider for loop-back mode. - addr: 0x30 name: STAT0_SEL fields: @@ -587,6 +633,8 @@ pages: - addr: 0x44 name: PREDRIVER fields: + - bits: "7-4" + name: PREDRIVER_RESERVED - bits: "3-0" name: PLL1_CP_BAW desc: APLL1 Charge Pump Current Gain PLL1_CP_BAW ranges from 0 to 15. Gain = PLL1_CP_BAW x 100 μA. @@ -637,6 +685,12 @@ pages: - bits: "4" name: BAW_LOCKDET_EN desc: BAW Lock Detect Enable + - bits: "3-2" + name: PLL1_CLSDWAIT + desc: Closed Loop Wait Period, VCO calibration time per step (up to 7 steps). + - bits: "1-0" + name: PLL1_VCOWAIT + desc: VCO Wait Period. Timeout counter before starting VCO calibration. - addr: 0x50 name: BAW_LOCKDET_PPM_MAX_BY1 fields: @@ -648,6 +702,10 @@ pages: desc: BAW VCO Lock Detection - addr: 0x51 name: BAW_LOCKDET_PPM_MAX_BY0 + fields: + - bits: "7-0" + name: BAW_LOCK_DET_2 + desc: BAW VCO Lock Detection - addr: "0x52:0x55" name: BAW_LOCKDET_CNTSTRT @@ -679,6 +737,9 @@ pages: - addr: 0x65 name: PLL2_CTRL1 fields: + - bits: "2" + name: PLL2_VM_BYP + desc: PLL2 Vtune Monitor Bypass - bits: "1-0" name: PLL2_CP desc: PLL2 Charge Pump Gain; 0x0 = 1.6 mA; 0x1 = 3.2 mA; 0x2 = 4.8 mA; 0x3 = 6.4 mA @@ -693,13 +754,20 @@ pages: desc: PLL2 Post-Divider1 Note A RESET is required after changing Divider values; 0x0 = Invalid; 0x1 = 2; 0x2 = 3; 0x3 = 4; 0x4 = 5; 0x5 = 6; 0x6 = 7; 0x7 = Invalid - addr: 0x68 name: PLL2_CTRL4 + fields: + - bits: "5-0" + name: PLL2_RBLEED_CP + desc: PLL2 Bleed resistor selection - addr: 0x69 name: PLL2_CALCTRL0 fields: - bits: "3-2" name: PLL2_CLSDWAIT - desc: Closed Loop Wait Period VCO calibration time per step (up to 7 steps); 0x0 = 0.3 ms; 0x1 = 3 ms; 0x2 = 30 ms; 0x3 = 300 ms + desc: Closed Loop Wait Period VCO calibration time per step (up to 7 steps); 0x0 = 0.3 ms; 0x1 = 3 ms; 0x2 = 30 ms; 0x3 = 300 ms + - bits: "1-0" + name: PLL2_VCOWAIT + desc: VCO Wait Period. Timeout counter before starting VCO calibration. - addr: "0x6C:0x6D" name: PLL1_NDIV @@ -709,6 +777,15 @@ pages: - addr: 0x73 name: PLL1_MASHCTRL fields: + - bits: "7" + name: PLL1_DUAL_PH_EN + desc: PLL1 DUAL PHASE functionality on the feedback path enabled + - bits: "6" + name: PLL1_MASHSEED1 + desc: Mash Engine seed for second stage + - bits: "5" + name: PLL1_MASHSEED0 + desc: Mash Engine seed for first stage - bits: "4-3" name: PLL1_DTHRMODE desc: APLL1 SDM Dither Mode; 0x0 = Weak; 0x1 = Medium; 0x2 = Strong; 0x3 = Disabled @@ -718,6 +795,12 @@ pages: - addr: 0x74 name: PLL1_MODE fields: + - bits: "2" + name: PLL1_IGNORE_GPIO_PIN + desc: Ignore PLL1 frequency increment or decrement updates via pins + - bits: "1" + name: PLL1_FDEV_EN + desc: Enable PLL1 frequency increment or decrement via pins or registers - bits: "0" name: PLL1_MODE desc: PLL1 operational mode; 0x0 = Free-run mode (APLL only); 0x1 = DPLL mode @@ -726,12 +809,31 @@ pages: - addr: 0x81 name: PLL1_LF_R2 + fields: + - bits: "5-0" + name: PLL1_LF_R2 + desc: PLL1 Loop Filter R2, Ohm + + - addr: 0x82 + name: PLL1_LF_C1 + fields: + - bits: "2-0" + name: PLL1_LF_C1 + desc: PLL1 Loop Filter C1. Not Used, fixed 100 pF - addr: 0x83 name: PLL1_LF_R3 + fields: + - bits: "5-0" + name: PLL1_LF_R3 + desc: PLL1 Loop Filter R3, Ohm - addr: 0x84 name: PLL1_LF_R4 + fields: + - bits: "5-0" + name: PLL1_LF_R4 + desc: PLL1 Loop Filter R4, Ohm - addr: "0x86:0x87" name: PLL2_NDIV @@ -750,15 +852,34 @@ pages: desc: APLL2 SDM Order; 0x0 = Integer Mode; 0x1 = 1st; 0x2 = 2nd; 0x3 = 3rd; 0x4 = 4th - addr: 0x8C name: PLL2_LF_R2 + fields: + - bits: "5-0" + name: PLL2_LR_R2 + desc: PLL2 Loop Filter R2 (Ohm) - addr: 0x8E name: PLL2_LF_R3 + fields: + - bits: "5-0" + name: PLL2_LR_R3 + desc: PLL2 Loop Filter R3 (Ohm) - addr: 0x8F name: PLL2_LF_R4 + fields: + - bits: "5-0" + name: PLL2_LF_R4 + desc: PLL2 Loop Filter R4 (Ohm) - addr: 0x90 name: PLL2_LF_C3C4 + fields: + - bits: "6-4" + name: PLL2_LF_C4 + desc: PLL2 Loop Filter C4, pF + - bits: "2:0" + name: PLL2_LF_C3 + desc: PLL2 Loop Filter C3, pF - addr: 0x91 name: XO_OFFSET_SW_TIMER @@ -894,9 +1015,18 @@ pages: - addr: 0xF3 name: REF0_PH_VALID_THR + fields: + - bits: "5-0" + name: REF0_PH_VALID_THR + desc: PRIREF Phase Valid Threshold - addr: 0xF4 name: REF1_PH_VALID_THR + fields: + - bits: "5-0" + name: REF1_PH_VALID_THR + desc: SECREF Phase Valid Threshold + - name: DPllControl regs: - addr: 0xF9 @@ -926,12 +1056,18 @@ pages: - bits: "7" name: DPLL_ZDM_SYNC_EN desc: DPLL Zero Delay Synchronization enable + - bits: "6" + name: DPLL_ZDM_NDIV_RST_DIS + desc: DPLL NDIV reset disable when ZDM mode is enabled - bits: "5" name: DPLL_SWITCHOVER_1 desc: DPLL Switchover Timer - bits: "4" name: DPLL_FASTLOCK_ALWAYS desc: Enable DPLL fast lock + - bits: "3" + name: DPLL_LOCKDET_PPM_EN + desc: Enable DPLL Frequency Lock Detect - bits: "2" name: DPLL_HLDOVR_MODE desc: DPLL Holdover mode when tuning word history unavailable; 0x0 = Enter free-run mode; 0x1 = Hold last control value prior to holdover @@ -962,6 +1098,9 @@ pages: - addr: 0x104 name: DPLL_REF_TDC_CTL fields: + - bits: "4" + name: DPLL_TDC_SW_MODE + desc: DPLL TDC Software Control Enable. Value of TDC control word into the loop-filter is from register dpll_ref_frc_val[35:0]. - bits: "1" name: DPLL_REF_AVOID_SLIP desc: Disable Cycle Slip @@ -980,11 +1119,8 @@ pages: - addr: 0x10D name: DPLL_REF_DECIMATION - - addr: 0x10E - name: DPLL_REF_FILTSCALAR_BY1 - - - addr: 0x10F - name: DPLL_REF_FILTSCALAR_BY0 + - addr: "0x10E:0x10F" + name: DPLL_REF_FILTSCALAR - addr: 0x110 name: DPLL_REF_FILTGAIN @@ -1028,23 +1164,14 @@ pages: - addr: 0x11D name: DPLL_REF_LPF1GAIN2_FL - - addr: 0x11E - name: DPLL_REF_TMR_FL1_BY1 - - - addr: 0x11F - name: DPLL_REF_TMR_FL1_BY0 - - - addr: 0x120 - name: DPLL_REF_TMR_FL2_BY1 - - - addr: 0x121 - name: DPLL_REF_TMR_FL2_BY0 + - addr: "0x11E:0x11F" + name: DPLL_REF_TMR_FL1 - - addr: 0x122 - name: DPLL_REF_TMR_LCK_BY1 + - addr: "0x120:0x121" + name: DPLL_REF_TMR_FL2 - - addr: 0x123 - name: DPLL_REF_TMR_LCK_BY0 + - addr: "0x122:0x123" + name: DPLL_REF_TMR_LCK - addr: 0x124 name: DPLL_REF_PHC_LPF @@ -1052,11 +1179,8 @@ pages: - addr: 0x125 name: DPLL_REF_PHC_CTRL - - addr: 0x126 - name: DPLL_REF_PHC_TIMER_BY1 - - - addr: 0x127 - name: DPLL_REF_PHC_TIMER_BY0 + - addr: "0x126:0x127" + name: DPLL_REF_PHC_TIMER - addr: 0x128 name: DPLL_REF_QUANT @@ -1093,25 +1217,34 @@ pages: - addr: 0x13F name: DPLL_REF_MASHCTL + fields: + - bits: "4-3" + name: DPLL_REF_DTHRMODE + - bits: "2-0" + name: DPLL_REF_ORDER - - addr: "0x140:0x141" - name: DPLL_REF_LOCKDET_PPM_MAX - - - addr: "0x142:0x145" - name: DPLL_REF_LOCKDET_CNTSTRT + - addr: "0x140:0x144" + name: DPLL_REF_LOCKDET_1_5 - - addr: "0x146:0x149" - name: DPLL_REF_LOCKDET_VCO_CNTSTRT + - addr: "0x145:0x149" + name: DPLL_REF_LOCKDET_6_10 - - addr: "0x14A:0x14B" - name: DPLL_REF_UNLOCKDET_PPM_MAX + - addr: "0x14A:0x14C" + name: DPLL_REF_UNLOCKDET_1_3 - - addr: "0x14C:0x14F" - name: DPLL_REF_UNLOCKDET_CNTSTRT + - addr: "0x14D:0x14F" + name: PLL2_DEN - - addr: "0x150:0x153" + - addr: "0x150:0x152" name: DPLL_REF_UNLOCKDET_VCO_CNTSTRT + - addr: 0x153 + name: PLL1_24B_NUM_23_16 + fields: + - bits: "7-0" + name: PLL1_24B_NUM_23_16 + desc: APPL1 24-bit numerator bits 23:16 + - addr: "0x154:0x159" name: DPLL_REF_SYNC_PH_OFFSET diff --git a/src/lib/hw/lmk05318/lmk05318_rom.h b/src/lib/hw/lmk05318/lmk05318_rom.h deleted file mode 100644 index e5cad551..00000000 --- a/src/lib/hw/lmk05318/lmk05318_rom.h +++ /dev/null @@ -1,676 +0,0 @@ -// Copyright (c) 2023-2024 Wavelet Lab -// SPDX-License-Identifier: MIT - -static const uint32_t lmk05318_rom[] = { -// 0x000010, -// 0x00010B, -// 0x000235, -// 0x000332, -// 0x000404, -// 0x00050E, -// 0x000617, -// 0x00078E, -// 0x000802, -// 0x000AC8, -// 0x000B00, - 0x000C1B, - 0x000D08, - 0x000E00, - 0x000F00, - 0x001000, - 0x00111D, - 0x0012FF, - 0x001308, - 0x001420, - 0x001500, - 0x001600, - 0x001755, - 0x001855, - 0x001900, - 0x001A00, - 0x001B00, - 0x001C01, - 0x001D13, - 0x001E40, - 0x002044, - 0x002300, - 0x002403, - 0x002500, - 0x002600, - 0x002702, - 0x00280C, - 0x002900, - 0x002A01, - 0x002BE2, - 0x002C00, - 0x002D02, - 0x002E11, - 0x002F07, - 0x003050, - 0x00314A, - 0x003200, - 0x003310, - 0x003410, - 0x003504, - 0x003610, - 0x003710, - 0x003804, - 0x00393E, - 0x003A31, - 0x003B3E, - 0x003C31, - 0x003D3E, - 0x003E31, - 0x003F3E, - 0x004000, - 0x004100, - 0x004200, - 0x004331, - 0x004408, - 0x004500, - 0x004600, - 0x004700, - 0x004833, - 0x004900, - 0x004A00, - 0x004B00, - 0x004C00, - 0x004D0F, - 0x004E00, - 0x004F11, - 0x005080, - 0x00510A, - 0x005200, - 0x005307, - 0x00549E, - 0x005500, - 0x005600, - 0x00571E, - 0x005884, - 0x005980, - 0x005A00, - 0x005B14, - 0x005C00, - 0x005D07, - 0x005E9E, - 0x005F00, - 0x006000, - 0x00611E, - 0x006284, - 0x006380, - 0x006429, - 0x006501, - 0x006622, - 0x00670F, - 0x00681F, - 0x006905, - 0x006A00, - 0x006B64, - 0x006C00, - 0x006D60, - 0x006E27, - 0x006F62, - 0x007076, - 0x007127, - 0x007262, - 0x007303, - 0x007401, - 0x007500, - 0x007600, - 0x007700, - 0x007800, - 0x007900, - 0x007A00, - 0x007B28, - 0x007C00, - 0x007D11, - 0x007E79, - 0x007F7A, - 0x008000, - 0x008101, - 0x008200, - 0x008301, - 0x008401, - 0x008577, - 0x008600, - 0x008728, - 0x008800, - 0x00891D, - 0x008A18, - 0x008B03, - 0x008C02, - 0x008D00, - 0x008E01, - 0x008F01, - 0x009077, - 0x009101, - 0x009289, - 0x009320, - 0x00950D, - 0x009600, - 0x009701, - 0x00980D, - 0x009929, - 0x009A24, - 0x009B32, - 0x009C01, - // 0x009D00, - 0x009E00, - 0x009F00, - 0x00A0FC, - 0x00A132, - 0x00A200, - // 0x00A400, - 0x00A500, - 0x00A701, - 0x00B200, - 0x00B400, - 0x00B500, - 0x00B600, - 0x00B700, - 0x00B800, - 0x00B9F5, - 0x00BA01, - 0x00BB00, - 0x00BC00, - 0x00BD00, - 0x00BE00, - 0x00BF00, - 0x00C050, - 0x00C110, - 0x00C200, - 0x00C300, - 0x00C400, - 0x00C51D, - 0x00C600, - 0x00C700, - 0x00C81D, - 0x00C900, - 0x00CA00, - 0x00CB00, - 0x00CC15, - 0x00CD00, - 0x00CE00, - 0x00CF15, - 0x00D000, - 0x00D114, - 0x00D200, - 0x00D316, - 0x00D400, - 0x00D514, - 0x00D600, - 0x00D716, - 0x00D80F, - 0x00D900, - 0x00DA00, - 0x00DB19, - 0x00DC6E, - 0x00DD00, - 0x00DE03, - 0x00DF0D, - 0x00E047, - 0x00E100, - 0x00E200, - 0x00E319, - 0x00E46E, - 0x00E500, - 0x00E603, - 0x00E70D, - 0x00E847, - 0x00E90A, - 0x00EA0A, - 0x00EB01, - 0x00EC8C, - 0x00EDBA, - 0x00EE80, - 0x00EF00, - 0x00F0C3, - 0x00F150, - 0x00F200, - 0x00F33F, - 0x00F400, - 0x00F921, - 0x00FA00, - 0x00FB03, - 0x00FC2D, - 0x00FD00, - 0x00FE00, - 0x00FF00, - 0x010000, - 0x010101, - 0x010200, - 0x010300, - 0x010402, - 0x010580, - 0x010601, - 0x01072A, - 0x010805, - 0x0109F2, - 0x010A00, - 0x010BA0, - 0x010C04, - 0x010D00, - 0x010E02, - 0x010F8C, - 0x011000, - 0x011100, - 0x011200, - 0x011316, - 0x011416, - 0x011516, - 0x011600, - 0x011700, - 0x011800, - 0x011900, - 0x011A00, - 0x011B00, - 0x011C1E, - 0x011D1E, - 0x011E00, - 0x011F00, - 0x012000, - 0x012100, - 0x012203, - 0x012322, - 0x012409, - 0x012501, - 0x012600, - 0x01272C, - 0x012809, - 0x012909, - 0x012A09, - 0x012B01, - 0x012C00, - 0x012D1B, - 0x012E1E, - 0x012F01, - 0x01300F, - 0x013104, - 0x013261, - 0x0133F8, - 0x013443, - 0x0135C3, - 0x0136C3, - 0x0137C3, - 0x0138C3, - 0x0139C3, - 0x013AFF, - 0x013BFF, - 0x013CFF, - 0x013DFF, - 0x013EFF, - 0x013F03, - 0x014000, - 0x01410A, - 0x014200, - 0x014300, - 0x014400, - 0x014501, - 0x014606, - 0x014735, - 0x014875, - 0x01490B, - 0x014A00, - 0x014B64, - 0x014C00, - 0x014D00, - 0x014E3D, - 0x014F09, - 0x015000, - 0x015198, - 0x015296, - 0x015300, - 0x015400, - 0x015500, - 0x015600, - 0x015700, - 0x015800, - 0x015900, - 0x015A02, - 0x015B00, - 0x015C00, - 0x015D00, - 0x015E00, - 0x015F00, - 0x016000, - 0x016528, - 0x016F00, - 0x019B0C, -}; - -static const uint32_t lmk05318_rom_49152_12288_384[] = { -// 0x000010, -// 0x00010B, -// 0x000235, -// 0x000332, -// 0x000404, -// 0x00050E, -// 0x000617, -// 0x00078E, -// 0x000802, -// 0x000AC8, -// 0x000B00, - 0x000C1B, - 0x000D08, - 0x000E00, - 0x000F00, - 0x001000, - 0x00111D, - 0x0012FF, - 0x001308, - 0x001420, - 0x001500, - 0x001600, - 0x001755, - 0x001855, - 0x001900, - 0x001A00, - 0x001B00, - 0x001C01, - 0x001D13, - 0x001E40, - 0x002044, - 0x002300, - 0x002403, - 0x002500, - 0x002600, - 0x002703, - 0x002807, - 0x002900, - 0x002A01, - 0x002BC2, - 0x002C01, - 0x002D02, - 0x002E11, - 0x002F07, - 0x003050, - 0x00314A, - 0x003200, - 0x003380, - 0x003410, - 0x003501, - 0x003690, - 0x003700, - 0x0038FF, - 0x003910, - 0x003A07, - 0x003B90, - 0x003C07, - 0x003D90, - 0x003EFF, - 0x003F90, - 0x004000, - 0x004100, - 0x004200, - 0x004307, - 0x004408, - 0x004500, - 0x004600, - 0x004700, - 0x004833, - 0x004900, - 0x004A00, - 0x004B00, - 0x004C00, - 0x004D0F, - 0x004E00, - 0x004F11, - 0x005080, - 0x00510A, - 0x005200, - 0x005307, - 0x00549E, - 0x005500, - 0x005600, - 0x00571E, - 0x005884, - 0x005980, - 0x005A00, - 0x005B14, - 0x005C00, - 0x005D07, - 0x005E9E, - 0x005F00, - 0x006000, - 0x00611E, - 0x006284, - 0x006380, - 0x006428, - 0x006501, - 0x006655, - 0x00670F, - 0x00681F, - 0x006905, - 0x006A00, - 0x006B64, - 0x006C00, - 0x006D60, - 0x006E07, - 0x006FD0, - 0x007000, - 0x007132, - 0x0072C8, - 0x007303, - 0x007400, - 0x007500, - 0x007600, - 0x007700, - 0x007800, - 0x007900, - 0x007A00, - 0x007B28, - 0x007C00, - 0x007D11, - 0x007E79, - 0x007F7A, - 0x008000, - 0x008101, - 0x008200, - 0x008301, - 0x008401, - 0x008577, - 0x008600, - 0x00872A, - 0x008800, - 0x00891C, - 0x008A86, - 0x008B03, - 0x008C02, - 0x008D00, - 0x008E01, - 0x008F01, - 0x009077, - 0x009101, - 0x009289, - 0x009320, - 0x00950D, - 0x009600, - 0x009701, - 0x00980D, - 0x009929, - 0x009A24, - 0x009B32, - 0x009C01, -// 0x009D00, - 0x009E00, - 0x009F00, - 0x00A0FC, - 0x00A132, - 0x00A200, -// 0x00A400, - 0x00A500, - 0x00A701, - 0x00B200, - 0x00B400, - 0x00B500, - 0x00B600, - 0x00B700, - 0x00B800, - 0x00B9F5, - 0x00BA01, - 0x00BB00, - 0x00BC00, - 0x00BD00, - 0x00BE00, - 0x00BF00, - 0x00C050, - 0x00C100, - 0x00C200, - 0x00C300, - 0x00C400, - 0x00C51D, - 0x00C600, - 0x00C700, - 0x00C81D, - 0x00C900, - 0x00CA00, - 0x00CB00, - 0x00CC15, - 0x00CD00, - 0x00CE00, - 0x00CF15, - 0x00D000, - 0x00D114, - 0x00D200, - 0x00D316, - 0x00D400, - 0x00D514, - 0x00D600, - 0x00D716, - 0x00D80F, - 0x00D900, - 0x00DA00, - 0x00DB19, - 0x00DC6E, - 0x00DD00, - 0x00DE03, - 0x00DF0D, - 0x00E047, - 0x00E100, - 0x00E200, - 0x00E319, - 0x00E46E, - 0x00E500, - 0x00E603, - 0x00E70D, - 0x00E847, - 0x00E90A, - 0x00EA0A, - 0x00EB01, - 0x00EC8C, - 0x00EDBA, - 0x00EE80, - 0x00EF00, - 0x00F0C3, - 0x00F150, - 0x00F200, - 0x00F300, - 0x00F400, - 0x00F921, - 0x00FA00, - 0x00FB03, - 0x00FC2C, - 0x00FD00, - 0x00FE00, - 0x00FF00, - 0x010000, - 0x010101, - 0x010200, - 0x010300, - 0x010402, - 0x010580, - 0x010601, - 0x01072A, - 0x010805, - 0x0109F2, - 0x010A00, - 0x010BA0, - 0x010C04, - 0x010D00, - 0x010E02, - 0x010F8C, - 0x011000, - 0x011100, - 0x011200, - 0x011316, - 0x011416, - 0x011516, - 0x011600, - 0x011700, - 0x011800, - 0x011900, - 0x011A00, - 0x011B00, - 0x011C1E, - 0x011D1E, - 0x011E00, - 0x011F00, - 0x012000, - 0x012100, - 0x012203, - 0x012322, - 0x012409, - 0x012501, - 0x012600, - 0x01272C, - 0x012809, - 0x012909, - 0x012A09, - 0x012B01, - 0x012C00, - 0x012D1B, - 0x012E1E, - 0x012F01, - 0x01300F, - 0x013104, - 0x013261, - 0x0133F8, - 0x013443, - 0x0135C3, - 0x0136C3, - 0x0137C3, - 0x0138C3, - 0x0139C3, - 0x013AFF, - 0x013BFF, - 0x013CFF, - 0x013DFF, - 0x013EFF, - 0x013F03, - 0x014000, - 0x01410A, - 0x014200, - 0x014300, - 0x014400, - 0x014501, - 0x014606, - 0x014735, - 0x014875, - 0x01490B, - 0x014A00, - 0x014B64, - 0x014C00, - 0x014D00, - 0x014E3D, - 0x014F09, - 0x015000, - 0x015198, - 0x015296, - 0x015300, - 0x015400, - 0x015500, - 0x015600, - 0x015700, - 0x015800, - 0x015900, - 0x015A02, - 0x015B00, - 0x015C00, - 0x015D00, - 0x015E00, - 0x015F00, - 0x016000, - 0x016528, - 0x016F00, - 0x019B0C, -}; diff --git a/src/lib/hw/lmk1d1208i/lmk1d1208i.c b/src/lib/hw/lmk1d1208i/lmk1d1208i.c new file mode 100644 index 00000000..bcf6c348 --- /dev/null +++ b/src/lib/hw/lmk1d1208i/lmk1d1208i.c @@ -0,0 +1,134 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include "def_lmk1d1208i.h" +#include "lmk1d1208i.h" +#include "usdr_logging.h" + + +static int lmk1d1208i_reg_wr(lmk1d1208i_state_t* d, uint8_t reg, uint8_t out) +{ + uint8_t data[2] = { reg, out }; + return lowlevel_ls_op(d->dev, d->subdev, + USDR_LSOP_I2C_DEV, d->lsaddr, + 0, NULL, 2, data); +} + +static int lmk1d1208i_reg_rd(lmk1d1208i_state_t* d, uint8_t reg, uint8_t* val) +{ + uint8_t addr[1] = { reg }; + return lowlevel_ls_op(d->dev, d->subdev, + USDR_LSOP_I2C_DEV, d->lsaddr, + 1, val, 1, addr); +} + +static int lmk1d1208i_reg_print(const uint16_t* regs, unsigned count) +{ + for (unsigned j = 0; j < count; j++) + { + uint8_t addr = regs[j] >> 8; + uint8_t data = regs[j]; + USDR_LOG("1208", USDR_LOG_DEBUG, "WRITING REG R%02u -> 0x%02x [0x%04x]", addr, data, regs[j]); + } + return 0; +} + +static int lmk1d1208i_reg_wr_n(lmk1d1208i_state_t* d, const uint16_t* regs, unsigned count) +{ + int res; + // + lmk1d1208i_reg_print(regs, count); + // + for (unsigned j = 0; j < count; j++) + { + uint8_t addr = regs[j] >> 8; + uint8_t data = regs[j]; + + res = lmk1d1208i_reg_wr(d, addr, data); + if (res) + return res; + } + + return 0; +} + +int lmk1d1208i_create(lldev_t dev, unsigned subdev, unsigned lsaddr, const lmk1d1208i_config_t* cfg, + lmk1d1208i_state_t* st) +{ + int res; + memset(st, 0, sizeof(lmk1d1208i_state_t)); + + st->dev = dev; + st->subdev = subdev; + st->lsaddr = lsaddr; + + uint8_t r5; + res = lmk1d1208i_reg_rd(st, R5, &r5); + if(res) + { + USDR_LOG("1208", USDR_LOG_ERROR, "lmk1d1208i_reg_rd() error:%d", res); + return res; + } + + const uint8_t rev_id = (r5 & REV_ID_MSK) >> REV_ID_OFF; + const uint8_t dev_id = (r5 & DEV_ID_MSK) >> DEV_ID_OFF; + USDR_LOG("1208", USDR_LOG_DEBUG, "REV_ID:0x%01x DEV_ID:0x%01x", rev_id, dev_id); + + if(rev_id != 0x2 && dev_id != 0x0) + { + USDR_LOG("1208", USDR_LOG_ERROR, "1D1208I chip/bus not found"); + return -EINVAL; + } + + uint16_t regs[] = + { + MAKE_LMK1D1208I_R0(cfg->out[7].enabled ? OUT7_EN_OUTPUT_ENABLED : OUT7_EN_OUTPUT_DISABLED_HI_Z, + cfg->out[6].enabled ? OUT6_EN_OUTPUT_ENABLED : OUT6_EN_OUTPUT_DISABLED_HI_Z, + cfg->out[5].enabled ? OUT5_EN_OUTPUT_ENABLED : OUT5_EN_OUTPUT_DISABLED_HI_Z, + cfg->out[4].enabled ? OUT4_EN_OUTPUT_ENABLED : OUT4_EN_OUTPUT_DISABLED_HI_Z, + cfg->out[3].enabled ? OUT3_EN_OUTPUT_ENABLED : OUT3_EN_OUTPUT_DISABLED_HI_Z, + cfg->out[2].enabled ? OUT2_EN_OUTPUT_ENABLED : OUT2_EN_OUTPUT_DISABLED_HI_Z, + cfg->out[1].enabled ? OUT1_EN_OUTPUT_ENABLED : OUT1_EN_OUTPUT_DISABLED_HI_Z, + cfg->out[0].enabled ? OUT0_EN_OUTPUT_ENABLED : OUT0_EN_OUTPUT_DISABLED_HI_Z + ), + MAKE_LMK1D1208I_R1(cfg->out[7].amp == LMK1D1208I_BOOSTED_LVDS ? OUT7_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT7_AMP_SEL_STANDARD_LVDS_SWING_350_MV, + cfg->out[6].amp == LMK1D1208I_BOOSTED_LVDS ? OUT6_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT6_AMP_SEL_STANDARD_LVDS_SWING_350_MV, + cfg->out[5].amp == LMK1D1208I_BOOSTED_LVDS ? OUT5_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT5_AMP_SEL_STANDARD_LVDS_SWING_350_MV, + cfg->out[4].amp == LMK1D1208I_BOOSTED_LVDS ? OUT4_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT4_AMP_SEL_STANDARD_LVDS_SWING_350_MV, + cfg->out[3].amp == LMK1D1208I_BOOSTED_LVDS ? OUT3_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT3_AMP_SEL_STANDARD_LVDS_SWING_350_MV, + cfg->out[2].amp == LMK1D1208I_BOOSTED_LVDS ? OUT2_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT2_AMP_SEL_STANDARD_LVDS_SWING_350_MV, + cfg->out[1].amp == LMK1D1208I_BOOSTED_LVDS ? OUT1_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT1_AMP_SEL_STANDARD_LVDS_SWING_350_MV, + cfg->out[0].amp == LMK1D1208I_BOOSTED_LVDS ? OUT0_AMP_SEL_BOOSTED_LVDS_SWING_500_MV : OUT0_AMP_SEL_STANDARD_LVDS_SWING_350_MV + ), + MAKE_LMK1D1208I_R2(1, 1, /*reserved*/ + cfg->bank[1].sel == LMK1D1208I_IN0 ? BANK1_IN_SEL_IN0_PDIVIN0_N : BANK1_IN_SEL_IN1_PDIVIN1_N, + cfg->bank[0].sel == LMK1D1208I_IN0 ? BANK0_IN_SEL_IN0_PDIVIN0_N : BANK0_IN_SEL_IN1_PDIVIN1_N, + cfg->bank[1].mute ? BANK1_MUTE_LOGIC_LOW : BANK1_MUTE_INX_PDIVINX_N, + cfg->bank[0].mute ? BANK0_MUTE_LOGIC_LOW : BANK0_MUTE_INX_PDIVINX_N, + cfg->in[1].enabled ? IN1_EN_INPUT_ENABLED : IN1_EN_INPUT_DISABLED_REDUCES_POWER_CONSUMPTION, + cfg->in[0].enabled ? IN0_EN_INPUT_ENABLED : IN0_EN_INPUT_DISABLED_REDUCES_POWER_CONSUMPTION + ), + }; + + res = lmk1d1208i_reg_wr_n(st, regs, SIZEOF_ARRAY(regs)); + if(res) + { + USDR_LOG("1208", USDR_LOG_ERROR, "lmk1d1208i_reg_wr_n() error:%d", res); + return res; + } + + USDR_LOG("1208", USDR_LOG_DEBUG, "Create OK"); + return 0; +} + +int lmk1d1208i_destroy(lmk1d1208i_state_t* st) +{ + USDR_LOG("1208", USDR_LOG_DEBUG, "Destroy OK"); + return 0; +} + + diff --git a/src/lib/hw/lmk1d1208i/lmk1d1208i.h b/src/lib/hw/lmk1d1208i/lmk1d1208i.h new file mode 100644 index 00000000..de4e3a45 --- /dev/null +++ b/src/lib/hw/lmk1d1208i/lmk1d1208i.h @@ -0,0 +1,62 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef LMK1D1208I_H +#define LMK1D1208I_H + +#include + +#define LMK1D1208I_IN_CNT 2 +#define LMK1D1208I_BANK_CNT 2 +#define LMK1D1208I_OUT_CNT 8 + +struct lmk1d1208i_state { + lldev_t dev; + unsigned subdev; + unsigned lsaddr; +}; +typedef struct lmk1d1208i_state lmk1d1208i_state_t; + +enum lmk1d1208i_bank_sel +{ + LMK1D1208I_IN0 = 1, + LMK1D1208I_IN1 = 0, +}; +typedef enum lmk1d1208i_bank_sel lmk1d1208i_bank_sel_t; + +enum lmk1d1208i_amp_sel +{ + LMK1D1208I_STANDARD_LVDS = 0, + LMK1D1208I_BOOSTED_LVDS = 1, +}; +typedef enum lmk1d1208i_amp_sel lmk1d1208i_amp_sel_t; + +struct lmk1d1208i_config +{ + struct + { + bool enabled; + } + in[LMK1D1208I_IN_CNT]; + + struct + { + lmk1d1208i_bank_sel_t sel; + bool mute; + } + bank[LMK1D1208I_BANK_CNT]; + + struct + { + bool enabled; + lmk1d1208i_amp_sel_t amp; + } + out[LMK1D1208I_OUT_CNT]; +}; +typedef struct lmk1d1208i_config lmk1d1208i_config_t; + + +int lmk1d1208i_create(lldev_t dev, unsigned subdev, unsigned lsaddr, const lmk1d1208i_config_t* cfg, lmk1d1208i_state_t* st); +int lmk1d1208i_destroy(lmk1d1208i_state_t* st); + +#endif // LMK1D1208I_H diff --git a/src/lib/hw/lmk1d1208i/lmk1d1208i.yaml b/src/lib/hw/lmk1d1208i/lmk1d1208i.yaml new file mode 100644 index 00000000..fc654f4e --- /dev/null +++ b/src/lib/hw/lmk1d1208i/lmk1d1208i.yaml @@ -0,0 +1,228 @@ +name: LMK1D1208I +revision: 0.0.1 +processors: [ c ] +bus: + type: I2C + usdr_path: /debug/hw/lmk1d1208i/*/reg +addr_width: 8 +data_width: 8 + +pages: +- name: Main + regs: + - addr: 0x0 + name: R0 + fields: + - bits: 0 + name: OUT0_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT0_P/OUT0_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - bits: 1 + name: OUT1_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT1_P/OUT1_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - bits: 2 + name: OUT2_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT2_P/OUT2_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - bits: 3 + name: OUT3_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT3_P/OUT3_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - bits: 4 + name: OUT4_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT4_P/OUT4_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - bits: 5 + name: OUT5_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT5_P/OUT5_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - bits: 6 + name: OUT6_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT6_P/OUT6_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - bits: 7 + name: OUT7_EN + mode: RW + dflt: 0x0 + desc: This bit controls the output enable signal for output channel OUT7_P/OUT7_N. + opts: + 0b0: Output Disabled (Hi-Z) + 0b1: Output Enabled + - addr: 0x1 + name: R1 + fields: + - bits: 0 + name: OUT0_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT0_P/ OUT0_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - bits: 1 + name: OUT1_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT1_P/ OUT1_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - bits: 2 + name: OUT2_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT2_P/ OUT2_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - bits: 3 + name: OUT3_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT3_P/ OUT3_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - bits: 4 + name: OUT4_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT4_P/ OUT4_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - bits: 5 + name: OUT5_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT5_P/ OUT5_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - bits: 6 + name: OUT6_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT6_P/ OUT6_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - bits: 7 + name: OUT7_AMP_SEL + mode: RW + dflt: 0x0 + desc: This bit sets the output amplitude for output channel OUT7_P/ OUT7_N. + opts: + 0b0: Standard LVDS Swing (350 mV) + 0b1: Boosted LVDS Swing (500 mV) + - addr: 0x2 + name: R2 + fields: + - bits: 0 + name: IN0_EN + mode: RW + dflt: 0x1 + desc: This bit controls the input enable signal for input channel IN0_P/ IN0_N. + opts: + 0b0: Input Disabled (reduces power consumption) + 0b1: Input Enabled + - bits: 1 + name: IN1_EN + mode: RW + dflt: 0x0 + desc: This bit controls the input enable signal for input channel IN1_P/ IN1_N. + opts: + 0b0: Input Disabled (reduces power consumption) + 0b1: Input Enabled + - bits: 2 + name: BANK0_MUTE + mode: RW + dflt: 0x0 + desc: This bit sets the outputs in Bank 0 to logic low level. + opts: + 0b0: INx_P/INx_N + 0b1: Logic low + - bits: 3 + name: BANK1_MUTE + mode: RW + dflt: 0x0 + desc: This bit sets the outputs in Bank 1 to logic low level. + opts: + 0b0: INx_P/INx_N + 0b1: Logic low + - bits: 4 + name: BANK0_IN_SEL + mode: RW + dflt: 0x1 + desc: This bit sets the input channel for Bank 0. + opts: + 0b0: IN1_P/IN1_N + 0b1: IN0_P/IN0_N + - bits: 5 + name: BANK1_IN_SEL + mode: RW + dflt: 0x1 + desc: This bit sets the input channel for Bank 1. + opts: + 0b0: IN1_P/IN1_N + 0b1: IN0_P/IN0_N + - bits: 6 + name: R2_RESERVED_1 + mode: RW + dflt: 0x1 + desc: Register bit can be written to 1. Writing a different value than 1 will affect device functionality. + - bits: 7 + name: R2_RESERVED_0 + mode: RW + dflt: 0x1 + desc: Register bit can be written to 1. Writing a different value than 1 will affect device functionality. + - addr: 0x5 + name: R5 + fields: + - bits: '3:0' + name: DEV_ID + mode: R + dflt: 0x0 + desc: These bits provide the device identification code. + - bits: '7:4' + name: REV_ID + mode: R + dflt: 0x2 + desc: These bits provide the silicon revision code. + - addr: 0xE + name: R14 + fields: + - bits: '7:0' + name: IDX_RB + mode: R + dflt: 0x0 + desc: These bits report the I2C address state. diff --git a/src/lib/hw/lms6002d/lms6002d.c b/src/lib/hw/lms6002d/lms6002d.c index fdc0e767..3ae61455 100644 --- a/src/lib/hw/lms6002d/lms6002d.c +++ b/src/lib/hw/lms6002d/lms6002d.c @@ -790,7 +790,7 @@ int lms6002d_cal_vga2(lms6002d_state_t* obj) return 0; } -int lms6002d_cal_lpf_bandwidth(lms6002d_state_t* obj, unsigned bcode) +int lms6002d_cal_lpf_bandwidth(lms6002d_state_t* obj, unsigned bcode, bool do_tune) { // TURN ON tx, SET tx to 320Mhz int res = 0; @@ -801,7 +801,7 @@ int lms6002d_cal_lpf_bandwidth(lms6002d_state_t* obj, unsigned bcode) bool txen = GET_LMS6002D_TOP_ENCFG_STXEN(obj->top_encfg); res = res ? res : lms6002d_trf_enable(obj, 1); - res = res ? res : lms6002d_tune_pll(obj, true, 320000000); + res = (!do_tune || res) ? res : lms6002d_tune_pll(obj, true, 320000000); uint16_t regs_0[] = { MAKE_LMS6002D_TOP_LPF_CTRL(0, 0, 0, 0), @@ -860,3 +860,13 @@ int lms6002d_set_tia_rfb(lms6002d_state_t* obj, uint8_t value) }; return lms6002d_spi_post(obj, regs, SIZEOF_ARRAY(regs)); } + +int lms6002d_set_rxfe_ip2corr(lms6002d_state_t* obj, int8_t i, int8_t q) +{ + uint16_t regs[] = { + MAKE_LMS6002D_RFE_XLD_IP2I(0, i), + MAKE_LMS6002D_RFE_IP2Q(q), + }; + return lms6002d_spi_post(obj, regs, SIZEOF_ARRAY(regs)); +} + diff --git a/src/lib/hw/lms6002d/lms6002d.h b/src/lib/hw/lms6002d/lms6002d.h index a8cf1898..355797ad 100644 --- a/src/lib/hw/lms6002d/lms6002d.h +++ b/src/lib/hw/lms6002d/lms6002d.h @@ -67,6 +67,7 @@ int lms6002d_set_txvga2_gain(lms6002d_state_t* obj, unsigned vga); int lms6002d_set_rx_extterm(lms6002d_state_t* obj, bool extterm); + enum lms6002d_rx_path { RXPATH_OFF = 0, RXPATH_LNA1 = 1, @@ -87,12 +88,13 @@ typedef enum lms6002d_tx_path lms6002d_tx_path_t; int lms6002d_set_tx_path(lms6002d_state_t* obj, unsigned path); int lms6002d_set_rxfedc(lms6002d_state_t* obj, int8_t dci, int8_t dcq); +int lms6002d_set_rxfe_ip2corr(lms6002d_state_t* obj, int8_t i, int8_t q); int lms6002d_cal_lpf(lms6002d_state_t* obj); int lms6002d_cal_txrxlpfdc(lms6002d_state_t* obj, bool tx); int lms6002d_cal_vga2(lms6002d_state_t* obj); -int lms6002d_cal_lpf_bandwidth(lms6002d_state_t* obj, unsigned bcode); +int lms6002d_cal_lpf_bandwidth(lms6002d_state_t* obj, unsigned bcode, bool do_tune); // For TIA calibration diff --git a/src/lib/hw/lms7002m/lms7002m.c b/src/lib/hw/lms7002m/lms7002m.c index 4cfc4e46..a706088f 100644 --- a/src/lib/hw/lms7002m/lms7002m.c +++ b/src/lib/hw/lms7002m/lms7002m.c @@ -202,6 +202,25 @@ int lms7002m_cgen_trim_vco(lms7002m_state_t* m, int vco_cap) GET_LMS7002M_CGEN_0X008C_VCO_CMPLO(reg)); } +int lms7002m_cgen_trim_vco_slow(lms7002m_state_t* m, int vco_cap) +{ + uint16_t reg; + uint32_t cgen_regs[] = { MAKE_LMS7002M_CGEN_0x008B(15, (unsigned)vco_cap, 0) }; + int res = lms7002m_spi_post(m, cgen_regs, SIZEOF_ARRAY(cgen_regs)); + if (res) + return res; + + res = lms7002m_spi_rd(m, CGEN_0x008C, ®); + if (res) + return res; + + usleep(1000); + + return (int)((GET_LMS7002M_CGEN_0X008C_VCO_CMPHO(reg) << 1) | + GET_LMS7002M_CGEN_0X008C_VCO_CMPLO(reg)); +} + + int lms7002m_sxx_trim_vco(lms7002m_state_t* m, int vco_cap) { uint16_t reg; @@ -218,10 +237,27 @@ int lms7002m_sxx_trim_vco(lms7002m_state_t* m, int vco_cap) GET_LMS7002M_SXX_0X0123_VCO_CMPLO(reg)); } +int lms7002m_sxx_trim_vco_slow(lms7002m_state_t* m, int vco_cap) +{ + uint16_t reg; + uint32_t cgen_regs[] = { MAKE_LMS7002M_SXX_0x0121(16, (unsigned)vco_cap, m->temp, 0) }; + int res = lms7002m_spi_post(m, cgen_regs, SIZEOF_ARRAY(cgen_regs)); + if (res) + return res; + + usleep(1000); + + res = lms7002m_spi_rd(m, SXX_0x0123, ®); + if (res) + return res; + + return (int)((GET_LMS7002M_SXX_0X0123_VCO_CMPHO(reg) << 1) | + GET_LMS7002M_SXX_0X0123_VCO_CMPLO(reg)); +} static int _lms7002m_vco_range(lms7002m_state_t* m, lms7002m_trim_vco_func_t f, unsigned start, uint8_t* phi, uint8_t* plo, - const char *name) + const char *name, unsigned extend_range) { int i; int lo = 0, hi = -1; @@ -248,28 +284,50 @@ static int _lms7002m_vco_range(lms7002m_state_t* m, lms7002m_trim_vco_func_t f, // Backup by one just to be sure we don't miss it lo = i; - i = i > 1 ? i - 1 : 0; + if (lo > 255) + lo = 255; + i = i - 1 - extend_range; + if (i < 0) + i = 0; } else { i = (int)start; } unsigned log_s = i; unsigned log_b = lo; + unsigned hi_cnt = 0; + unsigned r = 0; + + struct vco_ranges { + uint8_t lo; + uint8_t hi; + } ranges[4] = {{ 255, 0}, { 255, 0}, { 255, 0}, { 255, 0}}; + + for (; i < 256 && r < 4; i++) { + res = f(m, i); + if (res != LMS7002M_VCO_HIGH) { + hi_cnt = 0; + } - for (; i < 256; i++) { - switch ((res = f(m, i))) { + switch (res) { case LMS7002M_VCO_OK: - hi = i; - if (lo > i) - lo = i; + if (ranges[r].lo > i) + ranges[r].lo = i; + ranges[r].hi = i; break; case LMS7002M_VCO_HIGH: - if (hi == -1) { - hi = (i == 0) ? 0 : i - 1; + if (ranges[r].lo <= ranges[r].hi) { + r++; } - goto find_high; + if (hi_cnt > extend_range) { + goto find_high; + } + hi_cnt++; + break; case LMS7002M_VCO_LOW: - lo = i + 1; + if (ranges[r].lo <= ranges[r].hi) { + r++; + } break; case LMS7002M_VCO_FAIL: return -EIO; @@ -278,16 +336,23 @@ static int _lms7002m_vco_range(lms7002m_state_t* m, lms7002m_trim_vco_func_t f, } } -find_high: - if (hi == -1) - hi = 0; +find_high:; + int ldelta = -1; + int idx = -1; + for (unsigned p = 0; p < r; p++) { + int delta = ranges[p].hi - ranges[p].lo; + if (delta > ldelta) { + idx = p; + ldelta = delta; + } + } - USDR_LOG("7002", USDR_LOG_INFO, "%s binary result: %d; Probed range [%d .. %d] => Good range [%d; %d]", - name, log_b, log_s, i, lo, hi); + USDR_LOG("7002", USDR_LOG_INFO, "%s binary result: %d; Probed range [%d .. %d] => Good ranges %d: [%d; %d] / [%d; %d] / [%d; %d] / [%d; %d] took %d\n", + name, log_b, log_s, i, r, ranges[0].lo, ranges[0].hi, ranges[1].lo, ranges[1].hi, ranges[2].lo, ranges[2].hi, ranges[3].lo, ranges[3].hi, idx); - if (lo > 255) { - lo = 255; - } + + hi = (idx == -1) ? 0 : ranges[idx].hi; + lo = (idx == -1) ? 255 : ranges[idx].lo; *phi = (uint8_t)hi; *plo = (uint8_t)lo; @@ -341,6 +406,66 @@ int lms7002m_limelight_reset(lms7002m_state_t* m) return lms7002m_spi_post(m, regs, SIZEOF_ARRAY(regs)); } +int lms7002m_limelight_fifo_reset(lms7002m_state_t* m, bool rx, bool tx) +{ + uint16_t reg_mac_rst = m->reg_mac; + if (rx) + SET_LMS7002M_LML_0X0020_SRST_RXFIFO(reg_mac_rst, 1); + if (tx) + SET_LMS7002M_LML_0X0020_SRST_TXFIFO(reg_mac_rst, 1); + + uint32_t regs[] = { + // Reset LML FIFO + MAKE_LMS7002M_REG_WR(LML_0x0020, reg_mac_rst), + MAKE_LMS7002M_REG_WR(LML_0x0020, m->reg_mac), + }; + + return lms7002m_spi_post(m, regs, SIZEOF_ARRAY(regs)); +} + +int lms7002m_limelight_l_reset(lms7002m_state_t* m, bool rx, bool tx) +{ + uint16_t reg_mac_rst = m->reg_mac; + if (rx) { + SET_LMS7002M_LML_0X0020_LRST_RX_B(reg_mac_rst, 1); + SET_LMS7002M_LML_0X0020_LRST_RX_A(reg_mac_rst, 1); + } + if (tx) { + SET_LMS7002M_LML_0X0020_LRST_TX_B(reg_mac_rst, 1); + SET_LMS7002M_LML_0X0020_LRST_TX_A(reg_mac_rst, 1); + } + + uint32_t regs[] = { + // Reset LML FIFO + MAKE_LMS7002M_REG_WR(LML_0x0020, reg_mac_rst), + //MAKE_LMS7002M_REG_WR(LML_0x0020, m->reg_mac), + }; + + lms7002m_spi_post(m, regs, SIZEOF_ARRAY(regs)); + + + usleep(1000); + + uint32_t regs2[] = { + // Reset LML FIFO + //MAKE_LMS7002M_REG_WR(LML_0x0020, reg_mac_rst), + MAKE_LMS7002M_REG_WR(LML_0x0020, m->reg_mac), + }; + + + return lms7002m_spi_post(m, regs2, SIZEOF_ARRAY(regs2)); + +} + +int lms7002m_limelight_toggle_ntx(lms7002m_state_t* m) +{ + uint32_t regs[] = { + MAKE_LMS7002M_CDS_0x00AD(0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1), + MAKE_LMS7002M_CDS_0x00AD(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1), + }; + + return lms7002m_spi_post(m, regs, SIZEOF_ARRAY(regs)); +} int lms7002m_limelight_configure(lms7002m_state_t* m, lms7002m_limelight_conf_t params) { @@ -350,7 +475,7 @@ int lms7002m_limelight_configure(lms7002m_state_t* m, lms7002m_limelight_conf_t unsigned rxmux = params.rx_lfsr ? LML_0X002A_RX_MUX_LFSR : params.rx_tx_dig_loopback ? LML_0X002A_RX_MUX_TXFIFO : LML_0X002A_RX_MUX_RXTSP; - unsigned rdclk = (params.rx_ext_rd_fclk || params.rx_tx_dig_loopback) ? + unsigned rdclk = (params.rx_ext_rd_fclk /* || params.rx_tx_dig_loopback */ ) ? ((params.rx_port) ? LML_0X002A_RXRDCLK_MUX_FCLK1 : LML_0X002A_RXRDCLK_MUX_FCLK2) : ((params.rx_port) ? LML_0X002A_RXRDCLK_MUX_MCLK1 : LML_0X002A_RXRDCLK_MUX_MCLK2); uint16_t reg_mac = m->reg_mac; @@ -395,6 +520,8 @@ int lms7002m_limelight_configure(lms7002m_state_t* m, lms7002m_limelight_conf_t (params.txdiv > 1) ? 1u : 0, (params.rxdiv > 1) ? 1u : 0), MAKE_LMS7002M_LML_0x002C( params.txdiv / 2u - 1u, params.rxdiv / 2u - 1u ), + MAKE_LMS7002M_CDS_0x00AD(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, params.rxsisoddr && params.rxdiv == 1 ? 0 : 1, 1, 1), + MAKE_LMS7002M_CDS_0x00AE(3, 3, 0, 0, 0, 0, 0, 0), MAKE_LMS7002M_REG_WR(LML_0x0020, reg_mac), MAKE_LMS7002M_REG_WR(LML_0x0020, m->reg_mac) }; @@ -403,6 +530,7 @@ int lms7002m_limelight_configure(lms7002m_state_t* m, lms7002m_limelight_conf_t } + int _lms7002m_fill_pos(lms7002m_lml_map_t l, lms7002m_lml_map_t* o) { lms7002m_lml_map_t p = {{0, 0, 0, 0}}; @@ -483,9 +611,14 @@ int lms7002m_cgen_tune(lms7002m_state_t* m, unsigned fref, unsigned outfreq, uns usleep(20); uint8_t hi = 255, lo = 0; - res = _lms7002m_vco_range(m, &lms7002m_cgen_trim_vco, (unsigned)-1, &hi, &lo, "CGEN"); - if (res < 0) - return res; + for (unsigned a = 0; a < 2; a++) { + res = _lms7002m_vco_range(m, a == 0 ? &lms7002m_cgen_trim_vco : &lms7002m_cgen_trim_vco_slow, (unsigned)-1, &hi, &lo, "CGEN", 0); + if (res < 0) + return res; + + if (hi >= lo) + break; + } if (hi < lo) { USDR_LOG("7002", USDR_LOG_WARNING, "CGEN: Can't find sutable VCO cap!"); @@ -548,9 +681,6 @@ int lms7002m_sxx_tune(lms7002m_state_t* m, lms7002m_sxx_path_t path, unsigned fr const char* sxxn = path == SXX_RX ? "SXR" : "SXT"; int res; - SET_LMS7002M_LML_0X0020_MAC(mac, path == SXX_RX ? LMS7_CH_A : LMS7_CH_B); - SET_LMS7002M_SXX_0X0124_EN_DIR_SXX(m->reg_en_dir[dir_idx], 1); - if (vco > SXX_VCOH_MAX) { USDR_LOG("7002", USDR_LOG_WARNING, "%s: VCO=%u is out of range\n", sxxn, lofreq); return -ERANGE; @@ -565,6 +695,9 @@ int lms7002m_sxx_tune(lms7002m_state_t* m, lms7002m_sxx_path_t path, unsigned fr vco <<= 1; } + SET_LMS7002M_LML_0X0020_MAC(mac, path == SXX_RX ? LMS7_CH_A : LMS7_CH_B); + bool pwr = GET_LMS7002M_SXX_0X0124_EN_DIR_SXX(m->reg_en_dir[dir_idx]); + SET_LMS7002M_SXX_0X0124_EN_DIR_SXX(m->reg_en_dir[dir_idx], 1); uint32_t sxx_regs[] = { MAKE_LMS7002M_REG_WR(LML_0x0020, mac), MAKE_LMS7002M_REG_WR(SXX_0x0124, m->reg_en_dir[dir_idx]), @@ -588,10 +721,17 @@ int lms7002m_sxx_tune(lms7002m_state_t* m, lms7002m_sxx_path_t path, unsigned fr 1), MAKE_LMS7002M_SXX_0x011F(3, 3, 6, 0, 0, 0, 0), }; - res = lms7002m_spi_post(m, sxx_regs, SIZEOF_ARRAY(sxx_regs)); + + //Select only MAC if VCO was powered before + res = lms7002m_spi_post(m, sxx_regs, pwr ? 1 : SIZEOF_ARRAY(sxx_regs)); if (res) return res; + if (!pwr) { + // Wait for 1st start to settle LDOs & PLL + usleep(10000); + } + bool vcoit[4] = { (SXX_VCOL_MIN < vco) && (vco < SXX_VCOL_MAX), (SXX_VCOM_MIN < vco) && (vco < SXX_VCOM_MAX), @@ -625,7 +765,7 @@ int lms7002m_sxx_tune(lms7002m_state_t* m, lms7002m_sxx_path_t path, unsigned fr return res; m->temp = vcono[i]; - res = _lms7002m_vco_range(m, &lms7002m_sxx_trim_vco, (unsigned)-1, &phi, &plo, sxxn); + res = _lms7002m_vco_range(m, t > 1 ? &lms7002m_sxx_trim_vco_slow : &lms7002m_sxx_trim_vco, (unsigned)-1, &phi, &plo, sxxn, 2 * t); if (res != 0) return res; @@ -747,17 +887,42 @@ int lms7002m_dc_corr(lms7002m_state_t* m, unsigned p, int16_t v) } -int lms7002m_cds_set(lms7002m_state_t* m, bool rxalml, bool rxblml) +int lms7002m_xxtsp_bst(lms7002m_state_t* m, lms7002m_xxtsp_t tsp) { - uint32_t regs[] = { - // 0x80AD03ff ^ ((rxalml ? 1 : 0) << 2) , //^ ((rxblml ? 1 : 0) << 3), - 0x80AD03ff ^ ((rxalml ? 1 : 0) << 2), - 0x80AE0C00, + uint32_t reg_rxmod = MAKE_LMS7002M_RXTSP_0x0400(0, + RXTSP_0X0400_CAPSEL_RSSI, //CAPSEL + RXTSP_0X0400_CAPSEL_ADC_RXTSP_INPUT, //CAPSEL_ADC + RXTSP_0X0400_TSGFC_NEG6DB, //TSGFC, + RXTSP_0X0400_TSGFCW_DIV8, //TSGFCW, + 0, //TSGDCLDQ + 0, //TSGDCLDI + 0, //TSGSWAPIQ, + RXTSP_0X0400_TSGMODE_DC, //TSGMODE, + RXTSP_0X0400_INSEL_LML, //INSEL, + 0, //BSTART, + 1); + uint32_t reg_rxmod_s = reg_rxmod; + SET_LMS7002M_RXTSP_0X0400_BSTART(reg_rxmod_s, 1); + + uint32_t reg_txmod = MAKE_LMS7002M_TXTSP_0x0200(TXTSP_0X0200_TSGFC_NEG6DB, //TSGFC, + TXTSP_0X0200_TSGFCW_DIV8, //TSGFCW, + 0, //TSGDCLDQ + 0, //TSGDCLDI + 0, //TSGSWAPIQ, + TXTSP_0X0200_TSGMODE_DC, //TSGMODE, + TXTSP_0X0200_INSEL_LML, //INSEL, + 0, //BSTART, + 1u); + uint32_t reg_txmod_s = reg_txmod; + SET_LMS7002M_TXTSP_0X0200_BSTART(reg_txmod_s, 1); + + uint32_t xxtsp_regs[] = { + (tsp == LMS_RXTSP) ? reg_rxmod : reg_txmod, + (tsp == LMS_RXTSP) ? reg_rxmod_s : reg_txmod_s, }; - return lms7002m_spi_post(m, regs, SIZEOF_ARRAY(regs)); + return lms7002m_spi_post(m, xxtsp_regs, SIZEOF_ARRAY(xxtsp_regs)); } - // xxTSP int lms7002m_xxtsp_enable(lms7002m_state_t* m, lms7002m_xxtsp_t tsp, bool enable) { @@ -1066,15 +1231,18 @@ int lms7002m_rfe_gain(lms7002m_state_t* m, lms7002m_rfe_gain_t gain, int gainx10 switch (gain) { case RFE_GAIN_LNA: idx = _find_idx(-gainx10, lna_attens, SIZEOF_ARRAY(lna_attens) - 1); - *goutx10 = -lna_attens[idx]; + if (goutx10) + *goutx10 = -lna_attens[idx]; goto update_vals; case RFE_GAIN_TIA: idx = _find_idx(-gainx10, tia_attens, SIZEOF_ARRAY(tia_attens) - 1); - *goutx10 = -tia_attens[idx]; + if (goutx10) + *goutx10 = -tia_attens[idx]; goto update_vals; case RFE_GAIN_RFB: idx = _find_idx(-gainx10, lb_attens, SIZEOF_ARRAY(lb_attens) - 1); - *goutx10 = -lb_attens[idx]; + if (goutx10) + *goutx10 = -lb_attens[idx]; goto update_vals; update_vals: for (unsigned i = 0; i < 2; i++) { @@ -1190,17 +1358,43 @@ int lms7002m_trf_gain(lms7002m_state_t* m, lms7002m_trf_gain_t gt, int gainx10, for (unsigned i = 0; i < 2; i++) { uint16_t mac = m->reg_mac; unsigned lb_loss = m->trf[i].lb ? m->trf[i].lbloss : LB_LOSS_24; + unsigned main_gain = m->trf[i].lb ? m->trf[i].gain : m->trf[i].gain; + SET_LMS7002M_LML_0X0020_MAC(mac, i + 1); + regs[j++] = MAKE_LMS7002M_REG_WR(LML_0x0020, mac); + regs[j++] = MAKE_LMS7002M_TRF_0x0101( + 3, // F_TXPAD_TRF, + lb_loss, // L_LOOPB_TXPAD_TRF, + main_gain, // LOSS_LIN_TXPAD_TRF, + main_gain, // LOSS_MAIN_TXPAD_TRF, + m->trf[i].lb); // EN_LOOPB_TXPAD_TRF + + USDR_LOG("7002", USDR_LOG_INFO, "trf_gain[%d] lb_loss=%d loss=%d en_lb=%d\n", + i, lb_loss, main_gain, m->trf[i].lb); + }; + regs[j++] = MAKE_LMS7002M_REG_WR(LML_0x0020, m->reg_mac); + + return lms7002m_spi_post(m, regs, j); +} + +int lms7002m_trf_gain_lb_off(lms7002m_state_t* m) +{ + if (_lms7002m_is_none(m)) + return -EINVAL; + + uint32_t regs[2 * 3 + 2], j = 0; + for (unsigned i = 0; i < 2; i++) { + uint16_t mac = m->reg_mac; SET_LMS7002M_LML_0X0020_MAC(mac, i + 1); regs[j++] = MAKE_LMS7002M_REG_WR(LML_0x0020, mac); regs[j++] = MAKE_LMS7002M_TRF_0x0101( 3, //F_TXPAD_TRF, - lb_loss, //L_LOOPB_TXPAD_TRF, + LB_LOSS_24, //L_LOOPB_TXPAD_TRF, m->trf[i].gain, //LOSS_LIN_TXPAD_TRF, m->trf[i].gain, //LOSS_MAIN_TXPAD_TRF, m->trf[i].lb); //EN_LOOPB_TXPAD_TRF USDR_LOG("7002", USDR_LOG_INFO, "trf_gain[%d] lb_loss=%d loss=%d en_lb=%d\n", - i, lb_loss, m->trf[i].gain, m->trf[i].lb); + i, LB_LOSS_24, m->trf[i].gain, m->trf[i].lb); }; regs[j++] = MAKE_LMS7002M_REG_WR(LML_0x0020, m->reg_mac); diff --git a/src/lib/hw/lms7002m/lms7002m.h b/src/lib/hw/lms7002m/lms7002m.h index 2d69a2df..1d0716d5 100644 --- a/src/lib/hw/lms7002m/lms7002m.h +++ b/src/lib/hw/lms7002m/lms7002m.h @@ -99,8 +99,10 @@ struct lms7002m_lml_map { }; typedef struct lms7002m_lml_map lms7002m_lml_map_t; +int lms7002m_limelight_toggle_ntx(lms7002m_state_t* m); int lms7002m_limelight_reset(lms7002m_state_t* m); - +int lms7002m_limelight_fifo_reset(lms7002m_state_t* m, bool rx, bool tx); +int lms7002m_limelight_l_reset(lms7002m_state_t* m, bool rx, bool tx); struct lms7002m_limelight_conf { uint8_t rxsisoddr : 1; @@ -156,7 +158,7 @@ enum dc_param { int lms7002m_dc_corr(lms7002m_state_t* m, unsigned p, int16_t v); // CDS -int lms7002m_cds_set(lms7002m_state_t* m, bool rxalml, bool rxblml); +// int lms7002m_cds_set(lms7002m_state_t* m, bool rxalml, bool rxblml); // This functions is sensible to A/B channel selection enum lms7002m_xxtsp { @@ -167,7 +169,7 @@ typedef enum lms7002m_xxtsp lms7002m_xxtsp_t; // TSTPs int lms7002m_rxtsp_dc_corr(lms7002m_state_t* m, bool byp, unsigned wnd); - +int lms7002m_xxtsp_bst(lms7002m_state_t* m, lms7002m_xxtsp_t tsp); int lms7002m_xxtsp_enable(lms7002m_state_t* m, lms7002m_xxtsp_t tsp, bool enable); int lms7002m_xxtsp_int_dec(lms7002m_state_t* m, lms7002m_xxtsp_t tsp, unsigned intdec_ord); @@ -247,6 +249,7 @@ enum lms7002m_lb_loss { }; int lms7002m_trf_gain(lms7002m_state_t* m, lms7002m_trf_gain_t gt, int gainx10, int *goutx10); +int lms7002m_trf_gain_lb_off(lms7002m_state_t* m); // RBB enum lms7002m_rbb_path { diff --git a/src/lib/hw/lms7002m/lms7002m.yaml b/src/lib/hw/lms7002m/lms7002m.yaml index 8773f7d7..33c00b5d 100644 --- a/src/lib/hw/lms7002m/lms7002m.yaml +++ b/src/lib/hw/lms7002m/lms7002m.yaml @@ -1918,3 +1918,76 @@ pages: name: PD_XBUF_TX - bits: 0 name: EN_G_XBUF +- name: CDS + regs: + - addr: '0x00AD' + name: '0x00AD' + fields: + - bits: '15:14' + name: MCLK2 + desc: MCLK2 clock delay + - bits: '13:12' + name: MCLK1 + desc: MCLK1 clock delay + - bits: '11:10' + name: RESERVED + - bits: 9 + name: NTXBTSP + desc: TX TSPB clock inversion control + - bits: 8 + name: NTXATSP + desc: TX TSPA clock inversion control + - bits: 7 + name: NRXBTSP + desc: RX TSPB clock inversion control + - bits: 6 + name: NRXATSP + desc: RX TSPA clock inversion control + - bits: 5 + name: NTXBLML + desc: TX LMLB clock inversion control + - bits: 4 + name: NTXALML + desc: TX LMLA clock inversion control + - bits: 3 + name: NRXBLML + desc: RX LMLB clock inversion control + - bits: 2 + name: NRXALML + desc: RX LMLA clock inversion control + - bits: 1 + name: NMCLK2 + desc: MCLK2 clock inversion control + - bits: 0 + name: NMCLK1 + desc: MCLK1 clock inversion control + - addr: '0x00AE' + name: '0x00AE' + fields: + - bits: '15:14' + name: TXBTSP + desc: TX TSP B clock delay + - bits: '13:12' + name: TXATSP + desc: TX TSP A clock delay + - bits: '11:10' + name: RXBTSP + desc: RX TSP B clock delay + - bits: '9:8' + name: RXATSP + desc: RX TSP A clock delay + - bits: '7:6' + name: TXBLML + desc: TX LML B clock delay + - bits: '5:4' + name: TXALML + desc: TX LML A clock delay + - bits: '3:2' + name: RXBLML + desc: RX LML B clock delay + - bits: '1:0' + name: RXALML + desc: RX LML A clock delay + + + diff --git a/src/lib/hw/lms8001/lms8001.c b/src/lib/hw/lms8001/lms8001.c index e05da8ea..34cc0306 100644 --- a/src/lib/hw/lms8001/lms8001.c +++ b/src/lib/hw/lms8001/lms8001.c @@ -9,24 +9,21 @@ #include enum lms8_vco_params { - LMS8_VCO1_MIN = 4100000000ULL, // 4400000000ULL, - LMS8_VCO1_MAX = 6600000000ULL, + LMS8_VCO1_MIN_MPW2015 = 4150000000ULL, + LMS8_VCO1_MIN_MPW2024 = 4550000000ULL, - LMS8_VCO2_MIN = 6200000000ULL, - LMS8_VCO2_MAX = 8300000000ULL, - - LMS8_VCO3_MIN = 7700000000ULL, - LMS8_VCO3_MAX = 10400000000ULL, - - // Safe values for LO range - LMS8_MIN_NIQ = 520000000U, - LMS8_MAX_NIQ = 9110000000ULL, - LMS8_MIN_IQ = LMS8_MIN_NIQ / 2, - LMS8_MAX_IQ = LMS8_MAX_NIQ / 2, + LMS8_VCO3_MAX_MPW2015 = 9150000000ULL, + LMS8_VCO3_MAX_MPW2024 = 9650000000ULL, }; enum { LMS_LDO_1P25 = 101, + + LMS_LDO_VDD_PLL_CP = 159, + LMS_LDO_VDD_PLL_DIV = 179, + LMS_LDO_VDD_PLL_CLKBUF = 215, + + LMS_LDO_VCO_REG = 207, }; @@ -67,20 +64,29 @@ static int lms8001_spi_get(lms8001_state_t* obj, uint16_t addr, uint16_t* out) -static int _lms8001_check_lo_range(uint64_t flo, bool geniq) +static int _lms8001_check_lo_range(lms8001_state_t* obj, uint64_t flo, bool geniq) { + uint64_t LMS8_VCO1_MIN = (obj->stepping == LMS8_MPW2024) ? LMS8_VCO1_MIN_MPW2024 : LMS8_VCO1_MIN_MPW2015; + uint64_t LMS8_VCO3_MAX = (obj->stepping == LMS8_MPW2024) ? LMS8_VCO3_MAX_MPW2024 : LMS8_VCO3_MAX_MPW2015; + uint64_t LMS8_MIN_NIQ = LMS8_VCO1_MIN / 8; + uint64_t LMS8_MIN_IQ = LMS8_MIN_NIQ / 2; + uint64_t LMS8_MAX_IQ = LMS8_VCO3_MAX / 2; + if (geniq && (!((flo >= LMS8_MIN_IQ) && (flo <= LMS8_MAX_IQ)))) { - USDR_LOG("8001", USDR_LOG_ERROR, "LO frequency should be between 260 MHz and 4.55 GHz when GenIQ is selected.\n"); + USDR_LOG("8001", USDR_LOG_ERROR, "LO frequency should be between %d MHz and %d MHz when GenIQ is selected, requested %d Mhz\n", + (unsigned)(LMS8_MIN_IQ / 1000000), (unsigned)(LMS8_MAX_IQ / 1000000), (unsigned)(flo / 1000000)); return -EINVAL; } - if (!geniq && (!((flo >= LMS8_MIN_IQ) && (flo <= LMS8_MAX_NIQ)))) { - USDR_LOG("8001", USDR_LOG_ERROR, "LO frequency should be between 260 MHz and 9.11 GHz.\n"); + if (!geniq && (!((flo >= LMS8_MIN_IQ) && (flo <= LMS8_VCO3_MAX)))) { + USDR_LOG("8001", USDR_LOG_ERROR, "LO frequency should be between %d MHz and %d MHz, requested %d Mhz\n", + (unsigned)(LMS8_MIN_IQ / 1000000), (unsigned)(LMS8_VCO3_MAX / 1000000), (unsigned)(flo / 1000000)); return -EINVAL; } if (!geniq && (((flo >= LMS8_MIN_IQ) && (flo <= LMS8_MIN_NIQ)))) { - USDR_LOG("8001", USDR_LOG_ERROR, "LO frequency values between 260 MHz and 520 MHz can only be generated when IQ=True.\n"); + USDR_LOG("8001", USDR_LOG_ERROR, "LO frequency values between %d MHz and %d MHz can only be generated when IQ=True, requested %d Mhz\n", + (unsigned)(LMS8_MIN_IQ / 1000000), (unsigned)(LMS8_MIN_NIQ / 1000000), (unsigned)(flo / 1000000)); return -EINVAL; } @@ -93,8 +99,10 @@ struct lms8001_vco_settings { }; typedef struct lms8001_vco_settings lms8001_vco_settings_t; -static int _lms8001_calc_vco(uint64_t outfreq, lms8001_vco_settings_t* ro) +static int _lms8001_calc_vco(lms8001_state_t* obj, uint64_t outfreq, lms8001_vco_settings_t* ro) { + uint64_t LMS8_VCO1_MIN = (obj->stepping == LMS8_MPW2024) ? LMS8_VCO1_MIN_MPW2024 : LMS8_VCO1_MIN_MPW2015; + uint64_t LMS8_VCO3_MAX = (obj->stepping == LMS8_MPW2024) ? LMS8_VCO3_MAX_MPW2024 : LMS8_VCO3_MAX_MPW2015; lms8001_vco_settings_t r; r.divi = 0; @@ -147,12 +155,10 @@ int lms8001_tune(lms8001_state_t* state, unsigned fref, uint64_t out) { lms8001_vco_settings_t st; uint16_t rb; - int res = _lms8001_calc_vco(out, &st); + int res = _lms8001_calc_vco(state, out, &st); if (res) return res; - // uint64_t nint = st.fvco / fref; - // uint64_t frac = (st.fvco - nint * fref) * ((uint64_t)1 << 20) / fref; lms8001_pll_settings_t pll = _lms8001_calc_pll(st.fvco, fref, 0); if (pll.nint > 1023) { @@ -212,7 +218,7 @@ int lms8001_tune(lms8001_state_t* state, unsigned fref, uint64_t out) int cal_freq = GET_LMS8001_PLL_CONFIGURATION_PLL_CAL_AUTO0_FREQ_FINAL(rb); if (!(fcst == 0 && vco_sel_v == 1 && freq_sel_v == 1)) { - USDR_LOG("8001", USDR_LOG_ERROR, "Can't perform VCO autocalibration! VCO = %.3f Mhz REF = %.3f Mhz\n", st.fvco / 1.0e6, fref / 1.0e6); + USDR_LOG("8001", USDR_LOG_ERROR, "Can't perform VCO autocallibration! VCO = %.3f Mhz REF = %.3f Mhz\n", st.fvco / 1.0e6, fref / 1.0e6); return -ERANGE; } @@ -329,17 +335,17 @@ int lms8001b_hlmix_loss_set(lms8001_state_t* state, unsigned chan, unsigned loss return lms8001_spi_post(state, en_regs, SIZEOF_ARRAY(en_regs)); } -int lms8001_core_enable(lms8001_state_t* out, bool en) +int lms8001_core_enable(lms8001_state_t* state, bool enbuf, bool endiv, bool encp) { uint32_t lms_init[] = { - MAKE_LMS8001_BIASLDOCONFIG_CLK_BUF_LDO_Config(0, 0, en ? 1 : 0, LMS_LDO_1P25), - MAKE_LMS8001_BIASLDOCONFIG_PLL_DIV_LDO_Config(0, 0, en ? 1 : 0, LMS_LDO_1P25), - MAKE_LMS8001_BIASLDOCONFIG_PLL_CP_LDO_Config(0, 0, en ? 1 : 0, LMS_LDO_1P25), + MAKE_LMS8001_BIASLDOCONFIG_CLK_BUF_LDO_Config(0, 0, enbuf ? 1 : 0, state->stepping == LMS8_MPW2024 ? LMS_LDO_VDD_PLL_CLKBUF : LMS_LDO_1P25), + MAKE_LMS8001_BIASLDOCONFIG_PLL_DIV_LDO_Config(0, 0, endiv ? 1 : 0, state->stepping == LMS8_MPW2024 ? LMS_LDO_VDD_PLL_DIV : LMS_LDO_1P25), + MAKE_LMS8001_BIASLDOCONFIG_PLL_CP_LDO_Config(0, 0, encp ? 1 : 0, state->stepping == LMS8_MPW2024 ? LMS_LDO_VDD_PLL_CP : LMS_LDO_1P25), }; - return lms8001_spi_post(out, lms_init, SIZEOF_ARRAY(lms_init)); + return lms8001_spi_post(state, lms_init, SIZEOF_ARRAY(lms_init)); } -int lms8001_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lms8001_state_t *out) +int lms8001_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned int stepping, lms8001_state_t *out) { int res; memset(&out->pll, 0, sizeof(out->pll)); @@ -348,17 +354,24 @@ int lms8001_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lms8001_state_ uint32_t lms_init[] = { MAKE_LMS8001_CHIPCONFIG_SPIConfig(1, 1, 1, 1, 1, 1, 1), - MAKE_LMS8001_BIASLDOCONFIG_CLK_BUF_LDO_Config(0, 0, 1, LMS_LDO_1P25), - MAKE_LMS8001_BIASLDOCONFIG_PLL_DIV_LDO_Config(0, 0, 1, LMS_LDO_1P25), - MAKE_LMS8001_BIASLDOCONFIG_PLL_CP_LDO_Config(0, 0, 1, LMS_LDO_1P25), + MAKE_LMS8001_BIASLDOCONFIG_BiasConfig(1, 0, 9, 0, 0, 0, 0, 0), + MAKE_LMS8001_BIASLDOCONFIG_CLK_BUF_LDO_Config(0, 0, 1, out->stepping == LMS8_MPW2024 ? LMS_LDO_VDD_PLL_CLKBUF : LMS_LDO_1P25), + MAKE_LMS8001_BIASLDOCONFIG_PLL_DIV_LDO_Config(0, 0, 1, out->stepping == LMS8_MPW2024 ? LMS_LDO_VDD_PLL_DIV : LMS_LDO_1P25), + MAKE_LMS8001_BIASLDOCONFIG_PLL_CP_LDO_Config(0, 0, 1, out->stepping == LMS8_MPW2024 ? LMS_LDO_VDD_PLL_CP : LMS_LDO_1P25), - MAKE_LMS8001_PLL_CONFIGURATION_PLL_VREG(1, 0, 1, 1, 32), + MAKE_LMS8001_PLL_CONFIGURATION_PLL_VREG(1, 0, 1, 1, 205), MAKE_LMS8001_PLL_CONFIGURATION_PLL_CFG_XBUF(1, 0, 1), + //MAKE_LMS8001_PLL_CONFIGURATION_PLL_CFG_XBUF(0, 0, 1), //////////////////////////////////////////////////////////////////////////////////// MAKE_LMS8001_PLL_PROFILE_0_PLL_ENABLE_n(1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1), }; + if (stepping > LMS8_MPW2024) + return -EINVAL; + + out->stepping = stepping; + out->pll.PLL_VREG = lms_init[4]; out->pll.PLL_CFG_XBUF = lms_init[5]; @@ -420,16 +433,17 @@ struct lms80001_tune_settings { }; typedef struct lms80001_tune_settings lms80001_tune_settings_t; -static void _lms80001_tune_settings_def(lms80001_tune_settings_t* s) { - s->vtune_vct = 1; +static void _lms80001_tune_settings_def(lms8001_state_t* m, lms80001_tune_settings_t* s) { + s->vtune_vct = m->stepping == LMS8_MPW2024 ? 2 : 1; s->vco_sel_force = 0; s->vco_sel_init = 2; s->freq_init_pos = 7; s->freq_init = 0; s->freq_settling_N = 4; s->vtune_wait_N = 128; - s->vco_sel_freq_max = 250; - s->vco_sel_freq_min = 5; + s->vco_sel_freq_max = 254; + s->vco_sel_freq_min = 1; + } static uint32_t _mk_pav(lms8001_state_t* m, unsigned addr, uint16_t val) @@ -437,7 +451,12 @@ static uint32_t _mk_pav(lms8001_state_t* m, unsigned addr, uint16_t val) return MAKE_LMS8001_REG_WR((PLL_PROFILE_1_PLL_ENABLE_n - PLL_PROFILE_0_PLL_ENABLE_n) * m->act_profile + addr, val); } -static int _lms8001_vco_tune(lms8001_state_t* m, uint64_t fvco, int fref, uint32_t flags, const lms80001_tune_settings_t* s, double *actual) +enum { + VCO_TUNE_TOO_LOW = 1, + VCO_TUNE_TOO_HIGH = 2, +}; + +static int _lms8001_vco_tune(lms8001_state_t* m, uint64_t fvco, int fref, uint32_t flags, const lms80001_tune_settings_t* s, double *actual, unsigned erange) { lms8001_pll_settings_t pll = _lms8001_calc_pll(fvco, fref, flags); bool xbuf_slfben = (flags & LMS8001_SELF_BIAS_XBUF) == LMS8001_SELF_BIAS_XBUF; @@ -448,6 +467,9 @@ static int _lms8001_vco_tune(lms8001_state_t* m, uint64_t fvco, int fref, uint32 double actual_freq = (double)fref * pll.nfix * (pll.nint + pll.nfrac / (double)(1 << 20)); lms8001_pll_state_t* curr = &m->pll_profiles[m->act_profile]; + if (pll.nfrac == 0) { + int_mode = true; + } // ======================= enablePLL part ======================= // Enable VCO Biasing Block @@ -487,7 +509,7 @@ static int _lms8001_vco_tune(lms8001_state_t* m, uint64_t fvco, int fref, uint32 m->pll.PLL_CAL_AUTO1 = MAKE_LMS8001_PLL_CONFIGURATION_PLL_CAL_AUTO1(s->vco_sel_force, s->vco_sel_init, s->freq_init_pos, s->freq_init); m->pll.PLL_CAL_AUTO2 = MAKE_LMS8001_PLL_CONFIGURATION_PLL_CAL_AUTO2(s->freq_settling_N, s->vtune_wait_N); - m->pll.PLL_CAL_AUTO3 = MAKE_LMS8001_PLL_CONFIGURATION_PLL_CAL_AUTO3(s->vco_sel_freq_max, s->vco_sel_freq_min); + m->pll.PLL_CAL_AUTO3 = MAKE_LMS8001_PLL_CONFIGURATION_PLL_CAL_AUTO3(s->vco_sel_freq_max - erange, s->vco_sel_freq_min + erange); uint32_t lms_init[] = { MAKE_LMS8001_REG_WR(PLL_CONFIGURATION_PLL_VREG, m->pll.PLL_VREG), @@ -543,12 +565,25 @@ static int _lms8001_vco_tune(lms8001_state_t* m, uint64_t fvco, int fref, uint32 res = res ? res : lms8001_spi_post(m, lms_init, SIZEOF_ARRAY(lms_init)); } else { - USDR_LOG("8001", USDR_LOG_WARNING, "WARNING: Requested frequency fvco = %.3f Mhz could not be tuned.\n", fvco / 1.0e6); + USDR_LOG("8001", USDR_LOG_WARNING, "WARNING: Requested frequency fvco = %.3f Mhz could not be tuned FCAL/VCO/FREQ: %d/%d/%d.\n", + fvco / 1.0e6, FCAL_start, VCO_sel_final_val, freq_sel_final_val); res = -ERANGE; } + bool too_low = (freq_final < erange); + bool too_high = (freq_final > 255 - erange); + + USDR_LOG("8001", (too_low || too_high) ? USDR_LOG_INFO : USDR_LOG_NOTE, "VCO Calibration finished: %s VCO:%d CAP:%d\n", + (too_low || too_high) ? "FAIL" : "OK", VCO_final, freq_final); + USDR_LOG("8001", USDR_LOG_INFO, "VCO Calibration finished, VCO:%d CAP:%d\n", VCO_final, freq_final); - if (actual) *actual = actual_freq; + if (actual) + *actual = actual_freq; + + if (too_low) + return VCO_TUNE_TOO_LOW; + if (too_high) + return VCO_TUNE_TOO_HIGH; return res; } @@ -566,7 +601,7 @@ static int _lms8001_lock_status(lms8001_state_t* m, int* vtune_high, int* vtune_ return 0; } -static int _lms8001_change_pll_vco_cfg(lms8001_state_t* m, uint64_t fvco, int fref, uint32_t flags, lms80001_tune_settings_t* vco_settings) +static int _lms8001_change_pll_vco_cfg(lms8001_state_t* m, uint64_t fvco, int fref, uint32_t flags, lms80001_tune_settings_t* vco_settings, unsigned erange) { int res = 0, vtune_high, vtune_low, pll_lock; lms8001_pll_state_t* curr = &m->pll_profiles[m->act_profile]; @@ -574,7 +609,7 @@ static int _lms8001_change_pll_vco_cfg(lms8001_state_t* m, uint64_t fvco, int fr _mk_pav(m, PLL_PROFILE_0_PLL_VCO_CFG_n, curr->VCO_CFG), }; res = res ? res : lms8001_spi_post(m, lms_init, SIZEOF_ARRAY(lms_init)); - res = res ? res : _lms8001_vco_tune(m, fvco, fref, flags, vco_settings, NULL); + res = res ? res : _lms8001_vco_tune(m, fvco, fref, flags, vco_settings, NULL, erange); res = res ? res : usleep(30000); res = res ? res : _lms8001_lock_status(m, &vtune_high, &vtune_low, &pll_lock); if (res) @@ -591,7 +626,7 @@ static int _lms8001_center_vtune(lms8001_state_t* m, uint64_t fvco, int fref, ui int res; lms8001_pll_state_t* curr = &m->pll_profiles[m->act_profile]; lms80001_tune_settings_t vco_settings; - _lms80001_tune_settings_def(&vco_settings); + _lms80001_tune_settings_def(m, &vco_settings); vco_settings.vtune_vct = 1; vco_settings.vco_sel_force = 1; vco_settings.freq_init_pos = 4; @@ -639,78 +674,85 @@ static int _lms8001_center_vtune(lms8001_state_t* m, uint64_t fvco, int fref, ui int swvdd_list[4] = { 3, 2, 1, 0 }; int amp_list[4] = { 3, 2, 1, 0 }; + bool continue_vtune = true; + for (int i = 0; i < 4; i++) { int vdiv_swvdd = swvdd_list[i]; if (vdiv_swvdd_init != vdiv_swvdd) { SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VDIV_SWVDD_n(curr->VCO_CFG, vdiv_swvdd); - res = _lms8001_change_pll_vco_cfg(m, fvco, fref, flags, &vco_settings); + res = _lms8001_change_pll_vco_cfg(m, fvco, fref, flags, &vco_settings, 0); if (res == 1) { USDR_LOG("8001", USDR_LOG_INFO, "VTUNE voltage centered successfuly by changing VDIV_SWVDD value = %d\n", vdiv_swvdd); - goto restore_autotune; + continue_vtune = false; + break; } else if (res) { return res; } } } - // Set back VDIV_SWVDD<1:0> and FREQ<7:0> to inital values - SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VDIV_SWVDD_n(curr->VCO_CFG, vdiv_swvdd_init); - SET_LMS8001_PLL_PROFILE_0_PLL_VCO_FREQ_N_VCO_FREQ_n(curr->VCO_FREQ, freq_init); - uint32_t lms_init2[] = { - _mk_pav(m, PLL_PROFILE_0_PLL_VCO_CFG_n, curr->VCO_CFG), - _mk_pav(m, PLL_PROFILE_0_PLL_VCO_FREQ_n, curr->VCO_FREQ), - }; - res = lms8001_spi_post(m, lms_init2, SIZEOF_ARRAY(lms_init2)); - if (res) { - return res; - } + if (continue_vtune) { + // Set back VDIV_SWVDD<1:0> and FREQ<7:0> to inital values + SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VDIV_SWVDD_n(curr->VCO_CFG, vdiv_swvdd_init); + SET_LMS8001_PLL_PROFILE_0_PLL_VCO_FREQ_N_VCO_FREQ_n(curr->VCO_FREQ, freq_init); + uint32_t lms_init2[] = { + _mk_pav(m, PLL_PROFILE_0_PLL_VCO_CFG_n, curr->VCO_CFG), + _mk_pav(m, PLL_PROFILE_0_PLL_VCO_FREQ_n, curr->VCO_FREQ), + }; + res = lms8001_spi_post(m, lms_init2, SIZEOF_ARRAY(lms_init2)); + if (res) { + return res; + } - for (int i = 0; i < 4; i++) { - int amp = amp_list[i]; - if (amp_init != amp) { - SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AMP_n(curr->VCO_CFG, amp); - SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AAC_EN_n(curr->VCO_CFG, 1); - res = _lms8001_change_pll_vco_cfg(m, fvco, fref, flags, &vco_settings); - if (res == 1) { - USDR_LOG("8001", USDR_LOG_INFO, "VTUNE voltage centered successfuly by changing VCO_AMP value = %d\n", amp); - goto restore_autotune; - } else if (res) { - return res; + for (int i = 0; i < 4; i++) { + int amp = amp_list[i]; + if (amp_init != amp) { + SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AMP_n(curr->VCO_CFG, amp); + SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AAC_EN_n(curr->VCO_CFG, 1); + res = _lms8001_change_pll_vco_cfg(m, fvco, fref, flags, &vco_settings, 0); + if (res == 1) { + USDR_LOG("8001", USDR_LOG_INFO, "VTUNE voltage centered successfuly by changing VCO_AMP value = %d\n", amp); + continue_vtune = false; + break; + } else if (res) { + return res; + } } } } - // Set back VCO_AMP, VCO_AAC_EN, and FREQ<7:0> to inital values - SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AMP_n(curr->VCO_CFG, amp_init); - SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AAC_EN_n(curr->VCO_CFG, aac_en_init); - SET_LMS8001_PLL_PROFILE_0_PLL_VCO_FREQ_N_VCO_FREQ_n(curr->VCO_FREQ, freq_init); + if (continue_vtune) { + // Set back VCO_AMP, VCO_AAC_EN, and FREQ<7:0> to inital values + SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AMP_n(curr->VCO_CFG, amp_init); + SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AAC_EN_n(curr->VCO_CFG, aac_en_init); + SET_LMS8001_PLL_PROFILE_0_PLL_VCO_FREQ_N_VCO_FREQ_n(curr->VCO_FREQ, freq_init); - uint32_t lms_init3[] = { - _mk_pav(m, PLL_PROFILE_0_PLL_VCO_CFG_n, curr->VCO_CFG), - _mk_pav(m, PLL_PROFILE_0_PLL_VCO_FREQ_n, curr->VCO_FREQ), - }; - res = lms8001_spi_post(m, lms_init3, SIZEOF_ARRAY(lms_init3)); - if (res) { - return res; - } + uint32_t lms_init3[] = { + _mk_pav(m, PLL_PROFILE_0_PLL_VCO_CFG_n, curr->VCO_CFG), + _mk_pav(m, PLL_PROFILE_0_PLL_VCO_FREQ_n, curr->VCO_FREQ), + }; + res = lms8001_spi_post(m, lms_init3, SIZEOF_ARRAY(lms_init3)); + if (res) { + return res; + } - // FIXME: somehow it helps! - // Last resort check - usleep(5000); - res = _lms8001_lock_status(m, &vtune_high, &vtune_low, &pll_lock); - if (res) - return res; + // FIXME: somehow it helps! + // Last resort check + usleep(5000); + res = _lms8001_lock_status(m, &vtune_high, &vtune_low, &pll_lock); + if (res) + return res; - if ((vtune_high == 0) && (vtune_low == 0)) { - USDR_LOG("8001", USDR_LOG_INFO, "Centering of VTUNE back to origianl\n"); - return 0; - } + if ((vtune_high == 0) && (vtune_low == 0)) { + USDR_LOG("8001", USDR_LOG_INFO, "Centering of VTUNE back to origianl\n"); + return 0; + } - USDR_LOG("8001", USDR_LOG_ERROR, "Centering VTUNE using VDIV_SWVDD and VCO_AMP failed!\n"); - return -ERANGE; + USDR_LOG("8001", USDR_LOG_ERROR, "Centering VTUNE using VDIV_SWVDD and VCO_AMP failed!\n"); + return -ERANGE; + } -restore_autotune: // Set back PLL_CAL_AUTO1 to starting values SET_LMS8001_PLL_CONFIGURATION_PLL_CAL_AUTO1_VCO_SEL_FORCE(m->pll.PLL_CAL_AUTO1, vco_sel_force_init); SET_LMS8001_PLL_CONFIGURATION_PLL_CAL_AUTO1_VCO_SEL_INIT(m->pll.PLL_CAL_AUTO1, vco_sel_init); @@ -795,25 +837,30 @@ static int _lms8001_optim_pll_loopbw(lms8001_state_t* m, double PM_deg, double f return -EINVAL; } - double KVCO_avg; + double KVCO_avg = 0; + const static double s_kvco_s_mpw2015[4] = {0, 44.404e6, 33.924e6, 41.455e6}; + const static double s_kvco_s_mpw2024[4] = {0, 6.2842e7, 6.4853e7, 9.1550e7}; if (!fitKVCO) { + KVCO_avg = (m->stepping == LMS8_MPW2015) ? s_kvco_s_mpw2015[vco_sel] : s_kvco_s_mpw2024[vco_sel]; + } else if (m->stepping == LMS8_MPW2015) { + // Use Fitted Values for KVCO in Calculations for mpw 2015 + int CBANK = vco_freq; if (vco_sel == 1) - KVCO_avg = 44.404e6; + KVCO_avg = 24.71e6 * (2.09e-10 * pow(CBANK, 4) + 2.77e-09 * pow(CBANK, 3) + 1.13e-05 * pow(CBANK, 2) + 3.73e-03 * CBANK + 1.01e+00); else if (vco_sel == 2) - KVCO_avg = 33.924e6; + KVCO_avg = 21.05e6 * (-9.88e-11 * pow(CBANK, 4) + 1.46e-07 * pow(CBANK, 3) + (-2.14e-05) * pow(CBANK, 2) + 5.08e-03 * CBANK + 9.99e-01); else if (vco_sel == 3) - KVCO_avg = 41.455e6; - } - else { - // Use Fitted Values for KVCO in Calculations + KVCO_avg = 32.00e6 * (-1.04e-10 * pow(CBANK, 4) + 8.72e-08 * pow(CBANK, 3) + (-4.68e-06) * pow(CBANK, 2) + 3.68e-03 * CBANK + 1.00e+00); + } else { + // Use Fitted Values for KVCO in Calculations for mpw 2024 int CBANK = vco_freq; if (vco_sel == 1) - KVCO_avg = 24.71e6 * (2.09e-10*pow(CBANK, 4) + 2.77e-09*pow(CBANK, 3) + 1.13e-05*pow(CBANK, 2) + 3.73e-03*CBANK + 1.01e+00); + KVCO_avg = 3.8492e7 * (2.1005e-10 * pow(CBANK, 4) - 3.6267e-08 * pow(CBANK, 3) + 1.3634e-05 *pow(CBANK, 2) + 3.0541e-03 * CBANK + 1.0011e+00); else if (vco_sel == 2) - KVCO_avg = 21.05e6 * (-9.88e-11*pow(CBANK, 4) + 1.46e-07*pow(CBANK, 3) + (-2.14e-05)*pow(CBANK, 2) + 5.08e-03*CBANK + 9.99e-01); + KVCO_avg = 4.4058e7 * (6.4250e-11 * pow(CBANK, 4) + 2.1831e-10 * pow(CBANK, 3) + 5.7102e-06 *pow(CBANK, 2) + 2.6286e-03 * CBANK + 1.0011e+00); else if (vco_sel == 3) - KVCO_avg = 32.00e6 * (-1.04e-10*pow(CBANK, 4) + 8.72e-08*pow(CBANK, 3) + (-4.68e-06)*pow(CBANK, 2) + 3.68e-03*CBANK + 1.00e+00); + KVCO_avg = 6.2508e7 * (3.7053e-11 * pow(CBANK, 4) + 6.3153e-09 * pow(CBANK, 3) + 5.6397e-06 *pow(CBANK, 2) + 2.5792e-03 * CBANK + 9.9837e-01); } double Kvco = 2 * M_PI * KVCO_avg; @@ -920,11 +967,11 @@ static int _lms8001_optim_cp_ld(lms8001_state_t* m) // Calculate OFS and LD_VCT optimal values if (INTMOD_EN) { - // Set Offset Current and Lock Detector Threashold for IntN - Operating Mode + // Set Offset Current and Lock Detector Threshold for IntN - Operating Mode LD_VCT = 2; OFS = 0; } else { - // Set Offset Current and Lock Detector Threashold for IntN - Operating Mode + // Set Offset Current and Lock Detector Threshold for IntN - Operating Mode LD_VCT = 0; double Icp = (25.0 * ICT_CP / 16.0) * PULSE; // Calculate Target Value for Offset Current, as 3 % of Pulse current value @@ -987,14 +1034,15 @@ int lms8001_config_pll(lms8001_state_t* m, uint64_t flo, int fref, bool iq_gen = (tune_flags & LMS8001_IQ_GEN) == LMS8001_IQ_GEN; lms8001_pll_state_t* curr = &m->pll_profiles[m->act_profile]; lms8001_vco_settings_t pll_s; - double actual_vco; + double actual_vco = 0; + uint64_t fvco; // Calculate Loop - Crossover frequency double fc = loopBW / 1.65; // Check LO range (that was inside setLOFREQ) - res = (res) ? res : _lms8001_check_lo_range(flo, iq_gen); - res = (res) ? res : _lms8001_calc_vco(iq_gen ? (flo << 1) : flo, &pll_s); + res = (res) ? res : _lms8001_check_lo_range(m, flo, iq_gen); + res = (res) ? res : _lms8001_calc_vco(m, iq_gen ? (flo << 1) : flo, &pll_s); if (res) { return res; } @@ -1004,39 +1052,80 @@ int lms8001_config_pll(lms8001_state_t* m, uint64_t flo, int fref, SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AMP_n(curr->VCO_CFG, 3); SET_LMS8001_PLL_PROFILE_0_PLL_VCO_CFG_N_VCO_AAC_EN_n(curr->VCO_CFG, 1); - // Sets FF-DIV Modulus (former setFFDIV) - SET_LMS8001_PLL_PROFILE_0_PLL_FF_CFG_N_FF_MOD_n(curr->FF_CFG, pll_s.divi); - SET_LMS8001_PLL_PROFILE_0_PLL_FF_CFG_N_FFCORE_MOD_n(curr->FF_CFG, pll_s.divi); - SET_LMS8001_PLL_PROFILE_0_PLL_FF_CFG_N_FFDIV_SEL_n(curr->FF_CFG, pll_s.divi > 0 ? 1 : 0); + for (unsigned r = 0; r < 6; r++) { + // Sets FF-DIV Modulus (former setFFDIV) + SET_LMS8001_PLL_PROFILE_0_PLL_FF_CFG_N_FF_MOD_n(curr->FF_CFG, pll_s.divi); + SET_LMS8001_PLL_PROFILE_0_PLL_FF_CFG_N_FFCORE_MOD_n(curr->FF_CFG, pll_s.divi); + SET_LMS8001_PLL_PROFILE_0_PLL_FF_CFG_N_FFDIV_SEL_n(curr->FF_CFG, pll_s.divi > 0 ? 1 : 0); - SET_LMS8001_PLL_PROFILE_0_PLL_ENABLE_N_PLL_EN_FFCORE_n(curr->ENABLE, pll_s.divi > 0 ? 1 : 0); + SET_LMS8001_PLL_PROFILE_0_PLL_ENABLE_N_PLL_EN_FFCORE_n(curr->ENABLE, pll_s.divi > 0 ? 1 : 0); - uint32_t lms_init[] = { - _mk_pav(m, PLL_PROFILE_0_PLL_VCO_CFG_n, curr->VCO_CFG), - _mk_pav(m, PLL_PROFILE_0_PLL_FF_CFG_n, curr->FF_CFG), + uint32_t lms_init[] = { + _mk_pav(m, PLL_PROFILE_0_PLL_VCO_CFG_n, curr->VCO_CFG), + _mk_pav(m, PLL_PROFILE_0_PLL_FF_CFG_n, curr->FF_CFG), - // Enable register is going to be update late anyway, so skip this - // _mk_pav(m, PLL_PROFILE_0_PLL_ENABLE_n, curr->ENABLE), - }; - res = lms8001_spi_post(m, lms_init, SIZEOF_ARRAY(lms_init)); - if (res) - return res; + // Enable register is going to be update late anyway, so skip this + // _mk_pav(m, PLL_PROFILE_0_PLL_ENABLE_n, curr->ENABLE), + }; + res = lms8001_spi_post(m, lms_init, SIZEOF_ARRAY(lms_init)); + if (res) + return res; - // Calculate actual VCO frequency - uint64_t fvco = (iq_gen) ? (flo << (pll_s.divi + 1)) : (flo << pll_s.divi); - lms80001_tune_settings_t vco_settings; - _lms80001_tune_settings_def(&vco_settings); + // Calculate actual VCO frequency + fvco = (iq_gen) ? (flo << (pll_s.divi + 1)) : (flo << pll_s.divi); + lms80001_tune_settings_t vco_settings; + _lms80001_tune_settings_def(m, &vco_settings); + + USDR_LOG("8001", USDR_LOG_NOTE, "PLL Tuning to F_VCO=%.3f GHz DIV=%d\n", fvco / 1.0e9, pll_s.divi); + USDR_LOG("8001", USDR_LOG_INFO, "PLL Tuning to F_VCO=%.3f GHz DIV=%d\n", fvco / 1.0e9, pll_s.divi); + + // Step 1 - Tune PLL to generate F_LO frequency at LODIST outputs that should be manualy enabled + // outside this method + res = _lms8001_vco_tune(m, fvco, fref, tune_flags, &vco_settings, &actual_vco, 3 - (r / 2)); + + // Try another divider and recalibrate + if (res == VCO_TUNE_TOO_LOW) { + if (pll_s.divi == 3) { + res = (m->stepping == LMS8_MPW2015) ? 0 : -ERANGE; + break; + } + + pll_s.divi++; + pll_s.fvco *= 2; + } else if (res == VCO_TUNE_TOO_HIGH) { + if (pll_s.divi == 0) { + res = (m->stepping == LMS8_MPW2015) ? 0 : -ERANGE; + break; + } + + pll_s.divi--; + pll_s.fvco /= 2; + } else if (res) { + return res; + } else { + break; + } + } - // Step 1 - Tune PLL to generate F_LO frequency at LODIST outputs that should be manualy enabled - // outside this method - res = _lms8001_vco_tune(m, fvco, fref, tune_flags, &vco_settings, &actual_vco); if (res) { USDR_LOG("8001", USDR_LOG_WARNING, "PLL Tuning to F_LO=%.3f GHz failed!\n", flo / 1.0e9); return res; } - // Step 2 - Center VCO Tuning Voltage if needed - res = _lms8001_center_vtune(m, fvco, fref, tune_flags); + int vtune_high, vtune_low, pll_lock; + res = _lms8001_lock_status(m, &vtune_high, &vtune_low, &pll_lock); + if (res) + return res; + + if ((m->stepping == LMS8_MPW2024) && (vtune_high || vtune_low)) { + USDR_LOG("8001", USDR_LOG_WARNING, "PLL didn't lock [%d/%d/%d] F_LO=%.3f Ghz F_VCO=%.3f DIV=%d!\n", + vtune_low, vtune_high, pll_lock, + flo / 1.0e9, pll_s.fvco / 1.0e9, 1 << pll_s.divi); + return -ERANGE; + } + + // Step 2 - Center VCO Tuning Voltage if needed only for MPW2015 + res = (m->stepping == LMS8_MPW2024) ? res : _lms8001_center_vtune(m, fvco, fref, tune_flags); if (res) { return res; } @@ -1047,7 +1136,7 @@ int lms8001_config_pll(lms8001_state_t* m, uint64_t flo, int fref, return res; } - // Step 4 - Optimize CP offset current Lock Detector Threashold depending on operating mode chosen(IntN or FracN) + // Step 4 - Optimize CP offset current Lock Detector Threshold depending on operating mode chosen(IntN or FracN) res = _lms8001_optim_cp_ld(m); if (res) { return res; @@ -1072,6 +1161,7 @@ int lms8001_config_pll(lms8001_state_t* m, uint64_t flo, int fref, int lms8001_smart_tune(lms8001_state_t* m, unsigned tune_flags, uint64_t flo, int fref, int loopbw, float phasemargin, float bwef, int flock_N) { int res; + uint64_t LMS8_MIN_NIQ = ((m->stepping == LMS8_MPW2024) ? LMS8_VCO1_MIN_MPW2024 : LMS8_VCO1_MIN_MPW2015) / 8; // Set IQ gen for freq < 520 Mhz if (flo < LMS8_MIN_NIQ) { @@ -1115,7 +1205,7 @@ int lms8001_smart_tune(lms8001_state_t* m, unsigned tune_flags, uint64_t flo, in int lms8001_reg_set(lms8001_state_t* m, uint16_t addr, uint16_t val) { - // Update internal state to syncronze cahce values + // Update internal state to synchronize cache values if (addr >= PLL_CONFIGURATION_PLL_VREG) { switch (addr) { diff --git a/src/lib/hw/lms8001/lms8001.h b/src/lib/hw/lms8001/lms8001.h index beda9f58..da1abf21 100644 --- a/src/lib/hw/lms8001/lms8001.h +++ b/src/lib/hw/lms8001/lms8001.h @@ -60,11 +60,19 @@ enum { LMS8001_PROFILES = 8, }; +enum lms8_stepping { + LMS8_MPW2015 = 0, + LMS8_MPW2024 = 1, +}; + struct lms8001_state { lldev_t dev; unsigned subdev; unsigned lsaddr; + // Chip stepping + unsigned stepping; + // Enabled channel masks unsigned chan_mask; @@ -78,10 +86,10 @@ struct lms8001_state { typedef struct lms8001_state lms8001_state_t; -int lms8001_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lms8001_state_t *out); +int lms8001_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned stepping, lms8001_state_t *out); int lms8001_destroy(lms8001_state_t* m); -int lms8001_core_enable(lms8001_state_t* state, bool enable); +int lms8001_core_enable(lms8001_state_t* state, bool enbuf, bool endiv, bool encp); int lms8001_tune(lms8001_state_t* state, unsigned fref, uint64_t out); int lms8001_ch_enable(lms8001_state_t* state, unsigned mask); diff --git a/src/lib/hw/lmx1204/lmx1204.c b/src/lib/hw/lmx1204/lmx1204.c new file mode 100644 index 00000000..a7049617 --- /dev/null +++ b/src/lib/hw/lmx1204/lmx1204.c @@ -0,0 +1,846 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include "def_lmx1204.h" +#include "lmx1204.h" +#include "usdr_logging.h" +#include "../cal/opt_func.h" +#include "../common/common.h" + +#define FREQ_EPS 1.0f + +enum +{ + SYSREFOUT_MIN = 0, + SYSREFOUT_MAX_GENMODE = 200000000ull, + SYSREFOUT_MAX_RPTMODE = 100000000ull, + + CLKIN_MIN = 300000000ull, + CLKIN_MAX = 12800000000ull, + + CLKOUT_MIN_DIV = 150000000ull, + CLKOUT_MAX_DIV = 6400000000ull, + + CLKOUT_MIN_BUF = CLKIN_MIN, + CLKOUT_MAX_BUF = CLKIN_MAX, + + CLKOUT_MIN_MUL = 3200000000ull, + CLKOUT_MAX_MUL = 6400000000ull, + + LOGICLKOUT_MIN = 1000000ull, + LOGICLKOUT_MAX = 800000000ull, + + SMCLK_DIV_PRE_OUT_MAX = 1600000000ull, + SMCLK_DIV_OUT_MAX = 30000000ull, + + LOGICLK_DIV_PRE_OUT_MAX = 3200000000ull, + + SYSREF_DIV_PRE_OUT_MAX = 3200000000ull, + + CLK_DIV_MIN = 2, + CLK_DIV_MAX = 8, + CLK_MULT_MIN = 1, + CLK_MULT_MAX = 4, + LOGICLK_DIV_MIN = 2, + LOGICLK_DIV_MAX = 1023, + SYSREF_DIV_MIN = 2, + SYSREF_DIV_MAX = 4095, +}; + +static int lmx1204_spi_post(lmx1204_state_t* obj, uint32_t* regs, unsigned count) +{ + return + common_print_registers_a8d16(regs, count, USDR_LOG_DEBUG) + || + common_spi_post(obj, regs, count); +} + +static int lmx1204_spi_get(lmx1204_state_t* obj, uint16_t addr, uint16_t* out) +{ + return common_spi_get(obj, MAKE_LMX1204_REG_RD((uint32_t)addr), out); +} + +int lmx1204_reset_main_divider(lmx1204_state_t* st, bool set_flag) +{ + uint16_t r25; + + int res = lmx1204_spi_get(st, R25, &r25); + if(res) + return res; + + uint32_t reg = MAKE_LMX1204_REG_WR(R25, set_flag ? (r25 | CLK_DIV_RST_MSK) : (r25 & ~CLK_DIV_RST_MSK)); + return lmx1204_spi_post(st, ®, 1); +} + +int lmx1204_get_temperature(lmx1204_state_t* st, float* value) +{ + if(!value) + return -EINVAL; + + uint16_t r24; + + int res = lmx1204_spi_get(st, R24, &r24); + if(res) + return res; + + int16_t code = (r24 & RB_TEMPSENSE_MSK) >> RB_TEMPSENSE_OFF; + *value = 0.65f * code - 351.0f; + + USDR_LOG("1204", USDR_LOG_DEBUG, "LMX1204 temperature sensor:%.2fC", *value); + return 0; +} + +static inline const char* lmx1204_decode_lock_status(enum rb_ld_options ld) +{ + switch(ld) + { + case RB_LD_UNLOCKED_VTUNE_LOW: return "UNLOCKED (VTUNE low)"; + case RB_LD_UNLOCKED_VTUNE_HIGH: return "UNLOCKED (VTUNE high)"; + case RB_LD_LOCKED: return "LOCKED"; + } + return "UNKNOWN"; +} + +static inline const char* lmx1204_decode_vco_core(enum rb_vco_sel_options c) +{ + switch(c) + { + case RB_VCO_SEL_VCO5: return "VCO5"; + case RB_VCO_SEL_VCO4: return "VCO4"; + case RB_VCO_SEL_VCO3: return "VCO3"; + case RB_VCO_SEL_VCO2: return "VCO2"; + case RB_VCO_SEL_VCO1: return "VCO1"; + } + return "UNKNOWN"; +} + +int lmx1204_read_status(lmx1204_state_t* st, lmx1204_stats_t* status) +{ + if(!status) + return -EINVAL; + + uint16_t r75, r65; + + int res = lmx1204_get_temperature(st, &status->temperature); + res = res ? res : lmx1204_spi_get(st, R75, &r75); + res = res ? res : lmx1204_spi_get(st, R65, &r65); + if(res) + return res; + + status->vco_sel = (r65 & RB_VCO_SEL_MSK) >> RB_VCO_SEL_OFF; + status->lock_detect_status = (r75 & RB_LD_MSK) >> RB_LD_OFF; + + USDR_LOG("1204", USDR_LOG_DEBUG, "STATUS> Temp:%.2fC LOCK:%u(%s) VCO_SEL:%u(%s)", + status->temperature, + status->lock_detect_status, lmx1204_decode_lock_status((enum rb_ld_options)status->lock_detect_status), + status->vco_sel, lmx1204_decode_vco_core((enum rb_vco_sel_options)status->vco_sel)); + + return 0; +} + +int lmx1204_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lmx1204_state_t* st) +{ + memset(st, 0, sizeof(*st)); + int res; + + st->dev = dev; + st->subdev = subdev; + st->lsaddr = lsaddr; + + //SYSREF individual channel delays defaults (according to TICS Pro) + //Could be overridden before solver call (lmx1204_set_sysrefout_ch_delay()) + for(unsigned i = 0; i < SIZEOF_ARRAY(st->sysref_indiv_ch_delay); ++i) + { + lmx1204_sysrefout_channel_delay_t* d = &st->sysref_indiv_ch_delay[i]; + d->phase = SYSREFOUT0_DELAY_PHASE_QCLK0; + d->i = 7; + d->q = 0x7f - d->i; + } + + uint32_t regs[] = + { + MAKE_LMX1204_R86(0), //MUXOUT_EN_OVRD=0 + MAKE_LMX1204_R24(0,0,0,1), //enable temp sensor + MAKE_LMX1204_R23(1,1,1,0,1,0,0,0), //enable temp sensor + MUXOUT_EN=1(push-pull) MUXOUT=1(SDO) + }; + res = lmx1204_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + return res; + + usleep(1000); + + float tval; + res = lmx1204_get_temperature(st, &tval); + if(res) + return res; + + lmx1204_stats_t lmx1204status; + lmx1204_read_status(st, &lmx1204status); //just for log + + return 0; +} + +int lmx1204_destroy(lmx1204_state_t* st) +{ + USDR_LOG("1204", USDR_LOG_DEBUG, "Destroy OK"); + return 0; +} + +static int lmx1204_solver_prevalidate(lmx1204_state_t* st) +{ + if(st->clkin < CLKIN_MIN || st->clkin > CLKIN_MAX) + { + USDR_LOG("1204", USDR_LOG_ERROR, "CLKIN:%" PRIu64 " out of range [%" PRIu64 "; %" PRIu64 "]", + st->clkin, (uint64_t)CLKIN_MIN, (uint64_t)CLKIN_MAX); + return -EINVAL; + } + + if(st->ch_en[LMX1204_CH_LOGIC] && st->clkout_en[LMX1204_CH_LOGIC] && (st->logiclkout < LOGICLKOUT_MIN || st->logiclkout > LOGICLKOUT_MAX)) + { + USDR_LOG("1204", USDR_LOG_ERROR, "LOGICLKOUT:%.4f out of range [%" PRIu64 "; %" PRIu64 "]", + st->logiclkout, (uint64_t)LOGICLKOUT_MIN, (uint64_t)LOGICLKOUT_MAX); + return -EINVAL; + } + + double fmin, fmax; + + switch(st->clk_mux) + { + case CLK_MUX_DIVIDER_MODE: fmin = CLKOUT_MIN_DIV; fmax = CLKOUT_MAX_DIV; break; + case CLK_MUX_MULTIPLIER_MODE: fmin = CLKOUT_MIN_MUL; fmax = CLKOUT_MAX_MUL; break; + case CLK_MUX_BUFFER_MODE: fmin = CLKOUT_MIN_BUF; fmax = CLKOUT_MAX_BUF; break; + default: + USDR_LOG("1204", USDR_LOG_ERROR, "CLK_MUX:%u unknown", st->clk_mux); + return -EINVAL; + } + + if(st->clkout < fmin || st->clkout > fmax) + { + USDR_LOG("1204", USDR_LOG_ERROR, "CLKOUT:%.4f out of range [%.0f; %.0f]", st->clkout, fmin, fmax); + return -EINVAL; + } + + if(st->sysref_en) + { + fmin = SYSREFOUT_MIN; + switch(st->sysref_mode) + { + case LMX1204_CONTINUOUS: st->sysref_mode = SYSREF_MODE_CONTINUOUS_GENERATOR_MODE; fmax = SYSREFOUT_MAX_GENMODE; break; + case LMX1204_REPEATER: st->sysref_mode = SYSREF_MODE_REPEATER_REPEATER_MODE; fmax = SYSREFOUT_MAX_RPTMODE; break; + case LMX1204_PULSER: st->sysref_mode = SYSREF_MODE_PULSER_GENERATOR_MODE; fmax = 0.0f; break; + default: + USDR_LOG("1204", USDR_LOG_ERROR, "SYSREF_MODE:%u unknown", st->sysref_mode); + return -EINVAL; + } + + if(fmax != 0.0f && (st->sysrefout < fmin || st->sysrefout > fmax)) + { + USDR_LOG("1204", USDR_LOG_ERROR, "SYSREFOUT:%.4f out of range [%.0f; %.0f]", st->sysrefout, fmin, fmax); + return -EINVAL; + } + + for(unsigned i = 0; i < SIZEOF_ARRAY(st->sysref_indiv_ch_delay); ++i) + { + if(!st->ch_en[i]) + continue; //do not check if channel is disabled + + lmx1204_sysrefout_channel_delay_t* d = &st->sysref_indiv_ch_delay[i]; + + switch(d->phase) + { + case SYSREFOUT0_DELAY_PHASE_ICLK0: + case SYSREFOUT0_DELAY_PHASE_QCLK0: + case SYSREFOUT0_DELAY_PHASE_QCLK1: + case SYSREFOUT0_DELAY_PHASE_ICLK1: break; + default: + USDR_LOG("1204", USDR_LOG_ERROR, "SYSREFOUT[%u] delay phase:%u is invalid", i, d->phase); + return -EINVAL; + } + + if(d->i + d->q != 0x7f) + { + USDR_LOG("1204", USDR_LOG_ERROR, "SYSREFOUT[%u] delay I:%u + Q:%u != 127", i, d->i, d->q); + return -EINVAL; + } + } + } + + if(st->ch_en[LMX1204_CH_LOGIC]) + { + if(st->clkout_en[LMX1204_CH_LOGIC]) + { + switch(st->logiclkout_fmt) + { + case LMX1204_FMT_LVDS: st->logiclkout_fmt = LOGICLKOUT_FMT_LVDS; break; + case LMX1204_FMT_LVPECL: st->logiclkout_fmt = LOGICLKOUT_FMT_LVPECL; break; + case LMX1204_FMT_CML: st->logiclkout_fmt = LOGICLKOUT_FMT_CML; break; + default: + USDR_LOG("1204", USDR_LOG_ERROR, "LOGICLKOUT_FMT:%u unknown", st->logiclkout_fmt); + return -EINVAL; + } + } + + if(st->sysref_en && st->sysrefout_en[LMX1204_CH_LOGIC]) + { + switch(st->logisysrefout_fmt) + { + case LMX1204_FMT_LVDS: st->logisysrefout_fmt = LOGISYSREFOUT_FMT_LVDS; break; + case LMX1204_FMT_LVPECL: st->logisysrefout_fmt = LOGISYSREFOUT_FMT_LVPECL; break; + case LMX1204_FMT_CML: st->logisysrefout_fmt = LOGISYSREFOUT_FMT_CML; break; + default: + USDR_LOG("1204", USDR_LOG_ERROR, "LOGISYSREFOUT_FMT:%u unknown", st->logisysrefout_fmt); + return -EINVAL; + } + } + } + + return 0; +} + +static const char* lmx1204_decode_clkmux(enum clk_mux_options mux) +{ + switch(mux) + { + case CLK_MUX_BUFFER_MODE: return "CLK_MUX_BUFFER_MODE"; + case CLK_MUX_MULTIPLIER_MODE: return "CLK_MUX_MULTIPLIER_MODE"; + case CLK_MUX_DIVIDER_MODE: return "CLK_MUX_DIVIDER_MODE"; + } + return "UNKNOWN"; +} + +static const char* lmx1204_decode_sysref_mode(enum sysref_mode_options sm) +{ + switch(sm) + { + case SYSREF_MODE_CONTINUOUS_GENERATOR_MODE: return "CONTINUOUS_GENERATOR"; + case SYSREF_MODE_PULSER_GENERATOR_MODE: return "PULSER_GENERATOR"; + case SYSREF_MODE_REPEATER_REPEATER_MODE: return "REPEATER"; + } + return "UNKNOWN"; +} + +static const char* lmx1204_decode_fmt(enum logiclkout_fmt_options fmt) +{ + switch(fmt) + { + case LOGICLKOUT_FMT_LVDS : return "LVDS"; + case LOGICLKOUT_FMT_LVPECL: return "LVPECL"; + case LOGICLKOUT_FMT_CML: return "CML"; + } + return "UNKNOWN"; +} + +static int lmx1204_reset(lmx1204_state_t* st) +{ + uint32_t regs[] = + { + MAKE_LMX1204_R0(0, 0, 0, 1), //set RESET bit + MAKE_LMX1204_R0(0, 0, 0, 0), //reset RESET bit + }; + + int res = lmx1204_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + return res; + + usleep(10000); + + return 0; +} + +static inline uint8_t lmx1204_get_delay_step_size(lmx1204_state_t* st) +{ + uint8_t delay_step_size = SYSREFREQ_DELAY_STEPSIZE_28_PS_1_4_GHZ_TO_2_7_GHZ; + if(st->clkin > 2400000000 && st->clkin <= 4700000000) + delay_step_size = SYSREFREQ_DELAY_STEPSIZE_15_PS_2_4_GHZ_TO_4_7_GHZ; + if(st->clkin > 3100000000 && st->clkin <= 5700000000) + delay_step_size = SYSREFREQ_DELAY_STEPSIZE_11_PS_3_1_GHZ_TO_5_7_GHZ; + if(st->clkin > 4500000000 && st->clkin <= 12800000000) + delay_step_size = SYSREFREQ_DELAY_STEPSIZE_8_PS_4_5_GHZ_TO_12_8_GHZ; + + return delay_step_size; +} + +// all params are in lmx1204_state_t struct +int lmx1204_solver(lmx1204_state_t* st, bool prec_mode, bool dry_run) +{ + int res; + enum clk_mux_options clk_mux; + unsigned mult_div; + + if(st->clkin > st->clkout) + clk_mux = CLK_MUX_DIVIDER_MODE; + else if(st->clkin < st->clkout) + clk_mux = CLK_MUX_MULTIPLIER_MODE; + else + clk_mux = st->filter_mode ? CLK_MUX_MULTIPLIER_MODE : CLK_MUX_BUFFER_MODE; + + st->clk_mux = clk_mux; + + res = lmx1204_solver_prevalidate(st); + if(res) + return res; + + double outclk_fact; + + switch(clk_mux) + { + case CLK_MUX_BUFFER_MODE: + { + mult_div = 1; + outclk_fact = st->clkin; + break; + } + case CLK_MUX_DIVIDER_MODE: + { + mult_div = (unsigned)((double)st->clkin / st->clkout + 0.5); + if(mult_div < CLK_DIV_MIN || mult_div > CLK_DIV_MAX) + { + USDR_LOG("1204", USDR_LOG_ERROR, "CLC_DIV:%u out of range", mult_div); + return -EINVAL; + } + + outclk_fact = (double)st->clkin / mult_div; + break; + } + case CLK_MUX_MULTIPLIER_MODE: + { + mult_div = (unsigned)(st->clkout / st->clkin + 0.5); + if(mult_div < CLK_MULT_MIN || mult_div > CLK_MULT_MAX) + { + USDR_LOG("1204", USDR_LOG_ERROR, "CLC_MULT:%u out of range", mult_div); + return -EINVAL; + } + + outclk_fact = (double)st->clkin * mult_div; + break; + } + } + + if(fabs(outclk_fact - st->clkout) > FREQ_EPS) + { + USDR_LOG("1204", USDR_LOG_ERROR, "Calculated CLKOUT:%.4f too rough", outclk_fact); + return -EINVAL; + } + + if(prec_mode && st->clkin != st->clkout * mult_div) + { + USDR_LOG("1204", USDR_LOG_ERROR, "Cannot solve CLKOUT:%.4f by int divider/multiplier", st->clkout); + return -EINVAL; + } + + st->clkout = outclk_fact; + st->clk_mult_div = mult_div; + + USDR_LOG("1204", USDR_LOG_DEBUG, "CLKIN:%" PRIu64 " CLK_MUX:%s CLK_MULT_DIV:%u CLKOUT:%.4f [FILTER_MODE:%u] [PREC_MODE:%u]", + st->clkin, lmx1204_decode_clkmux(clk_mux), mult_div, st->clkout, st->filter_mode, prec_mode); + + // need to setup VCO for mult + if(clk_mux == CLK_MUX_MULTIPLIER_MODE) + { + unsigned div_pre = MAX(ceil((double)st->clkin / SMCLK_DIV_PRE_OUT_MAX), SMCLK_DIV_PRE_PL2); + if(div_pre > SMCLK_DIV_PRE_PL2 && div_pre <= SMCLK_DIV_PRE_PL4) + div_pre = SMCLK_DIV_PRE_PL4; + else if(div_pre > SMCLK_DIV_PRE_PL4 && div_pre <= SMCLK_DIV_PRE_PL8) + div_pre = SMCLK_DIV_PRE_PL8; + else + div_pre = SMCLK_DIV_PRE_PL2; + + double fdiv = (double)st->clkin / div_pre / SMCLK_DIV_OUT_MAX; + unsigned n = MAX(ceil(log2(fdiv)), SMCLK_DIV_PL1); + if(n > SMCLK_DIV_PL128) + { + return -EINVAL; //impossible + } + + const unsigned div = 1u << n; + + USDR_LOG("1204", USDR_LOG_DEBUG, "[MULT VCO SMCLK] SMCLK_DIV_PRE:%u SMCLK_DIV:%u(%u) F_SM_CLK:%.4f", + div_pre, n, div, (double)st->clkin / div_pre / div); + + st->smclk_div_pre = div_pre; + st->smclk_div = n; + } + + //need to setup LOGICLKOUT + if(st->ch_en[LMX1204_CH_LOGIC] && st->clkout_en[LMX1204_CH_LOGIC]) + { + unsigned div_pre, div; + double logiclkout_fact; + + if(st->logiclkout == st->clkin) + { + div_pre = LOGICLK_DIV_PRE_PL1; + div = 0; // bypassed + logiclkout_fact = st->clkin; + } + else + { + unsigned div_pre_min = MAX(ceil((double)st->clkin / LOGICLK_DIV_PRE_OUT_MAX), LOGICLK_DIV_PRE_PL2); + + bool found = false; + for(div_pre = div_pre_min; div_pre <= LOGICLK_DIV_PRE_PL4; ++div_pre) + { + if(div_pre == LOGICLK_DIV_PRE_PL4 - 1) + continue; + + div = (unsigned)((double)st->clkin / div_pre / st->logiclkout + 0.5); + if(div < LOGICLK_DIV_MIN) + { + USDR_LOG("1204", USDR_LOG_ERROR, "LOGICLKOUT:%.4f too high", st->logiclkout); + return -EINVAL; + } + if(div > LOGICLK_DIV_MAX) + continue; + + logiclkout_fact = (double)st->clkin / div_pre / div; + if(fabs(st->logiclkout - logiclkout_fact) > FREQ_EPS) + continue; + if(prec_mode && st->clkin != st->logiclkout * div_pre * div) + continue; + + found = true; + break; + } + if(!found) + { + USDR_LOG("1204", USDR_LOG_ERROR, "Cannot solve LOGICLKOUT:%.4f", st->logiclkout); + return -EINVAL; + } + } + + st->logiclkout = logiclkout_fact; + st->logiclk_div_pre = div_pre; + st->logiclk_div_bypass = (div == 0); + st->logiclk_div = div; + + USDR_LOG("1204", USDR_LOG_DEBUG, "[LOGICLKOUT] LOGICLK_DIV_PRE:%u LOGICLK_DIV:%u%s LOGICLKOUT:%.4f", + div_pre, div, div ? "" : "(BYPASSED)", logiclkout_fact); + } + else + { + USDR_LOG("1204", USDR_LOG_DEBUG, "[LOGICLKOUT]:disabled"); + } + + //sysref + if(st->sysref_en) + { + uint64_t f_interpol; + + if(st->clkin <= 1600000000ull) + { + st->sysref_delay_div = SYSREF_DELAY_DIV_PL2_LE_1_6_GHZ; + f_interpol = st->clkin >> 1; + } + else if(st->clkin <= 3200000000ull) + { + st->sysref_delay_div = SYSREF_DELAY_DIV_PL4_1_6_GHZ_TO_3_2_GHZ; + f_interpol = st->clkin >> 2; + } + else if(st->clkin <= 6400000000ull) + { + st->sysref_delay_div = SYSREF_DELAY_DIV_PL8_3_2_GHZ_TO_6_4_GHZ; + f_interpol = st->clkin >> 3; + } + else + { + st->sysref_delay_div = SYSREF_DELAY_DIV_PL16_6_4_GHZ_TO_12_8_GHZ; + f_interpol = st->clkin >> 4; + } + + if(f_interpol <= 200000000ull) + st->sysref_delay_scale = SYSREFOUT0_DELAY_SCALE_150_MHZ_TO_200_MHZ; + else if(f_interpol <= 400000000ull) + st->sysref_delay_scale = SYSREFOUT0_DELAY_SCALE_200_MHZ_TO_400_MHZ; + else + st->sysref_delay_scale = SYSREFOUT0_DELAY_SCALE_400_MHZ_TO_800_MHZ; + + unsigned div_pre = SYSREF_DIV_PRE_PL4, div = 0x3; //defaults + + if(st->sysref_mode == SYSREF_MODE_REPEATER_REPEATER_MODE) + { + if(st->sysrefout != st->sysrefreq) + { + USDR_LOG("1204", USDR_LOG_ERROR, "[SYSREF] SYSREFREQ must be equal to SYSREFOUT for repeater mode"); + return -EINVAL; + } + } + else + { + if(f_interpol % (uint64_t)st->sysrefout) + { + USDR_LOG("1204", USDR_LOG_ERROR, "[SYSREF] F_INTERPOL:%" PRIu64 " %% SYSREFOUTCLK:%.0f != 0", f_interpol, st->sysrefout); + return -EINVAL; + } + + unsigned min_div_pre = MAX(log2f(ceil((double)st->clkin / SYSREF_DIV_PRE_OUT_MAX)), SYSREF_DIV_PRE_PL1); + + bool found = true; + + for(div_pre = min_div_pre; div_pre <= SYSREF_DIV_PRE_PL4; ++div_pre) + { + div = (unsigned)((st->clkin >> div_pre) / st->sysrefout + 0.5); + if(div < SYSREF_DIV_MIN) + { + USDR_LOG("1204", USDR_LOG_ERROR, "SYSREFOUT:%.4f too high", st->sysrefout); + return -EINVAL; + } + if(div > SYSREF_DIV_MAX) + continue; + + if(st->clkin != (((uint64_t)st->sysrefout * div) << div_pre)) + { + USDR_LOG("1204", USDR_LOG_ERROR, "SYSREFOUT:%.4f cannot be solved in integers", st->sysrefout); + return -EINVAL; + } + + found = true; + break; + } + + if(!found) + { + USDR_LOG("1204", USDR_LOG_ERROR, "Cannot solve SYSREFOUT:%.4f", st->sysrefout); + return -EINVAL; + } + } + + st->sysref_div_pre = div_pre; + st->sysref_div = div; + + USDR_LOG("1204", USDR_LOG_DEBUG, "[SYSREFCLK] SYSREFREQ:%" PRIu64 " SYSREF_MODE:%s(%u) SYSREFOUT:%.4f", + st->sysrefreq, lmx1204_decode_sysref_mode((enum sysref_mode_options)st->sysref_mode), st->sysref_mode, st->sysrefout); + + if(st->sysref_mode != SYSREF_MODE_REPEATER_REPEATER_MODE) + { + USDR_LOG("1204", USDR_LOG_DEBUG, "[SYSREFCLK] SYSREF_DELAY_DIV:%u F_INTERPOL:%" PRIu64 " DELAY_SCALE:%u SYSREF_DIV_PRE:%u(%u) SYSREF_DIV:%u", + st->sysref_delay_div, f_interpol, st->sysref_delay_scale, (1 << st->sysref_div_pre), st->sysref_div_pre, st->sysref_div); + } + } + else + { + USDR_LOG("1204", USDR_LOG_DEBUG, "[SYSREFCLK]:disabled"); + } + + // succesfull solution + USDR_LOG("1204", USDR_LOG_INFO, "LMX1204 SOLUTION FOUND:"); + USDR_LOG("1204", USDR_LOG_INFO, "CLKIN:%" PRIu64 " SYSREFREQ:%" PRIu64, st->clkin, st->sysrefreq); + for(unsigned i = 0; i < LMX1204_OUT_CNT; ++i) + { + USDR_LOG("1204", USDR_LOG_INFO, "CLKOUT%u :%.4f EN:%u", i, st->clkout, st->ch_en[0] && st->clkout_en[0]); + USDR_LOG("1204", USDR_LOG_INFO, "SYSREFOUT%u:%.4f EN:%u", i, st->sysrefout, st->sysref_en && st->ch_en[0] && st->sysrefout_en[0]); + } + USDR_LOG("1204", USDR_LOG_INFO, "LOGICLKOUT :%.4f EN:%u FMT:%s", + st->logiclkout, st->ch_en[LMX1204_CH_LOGIC] && st->clkout_en[LMX1204_CH_LOGIC], lmx1204_decode_fmt((enum logiclkout_fmt_options)st->logiclkout_fmt)); + USDR_LOG("1204", USDR_LOG_INFO, "LOGICSYSREFOUT:%.4f EN:%u FMT:%s", + st->sysrefout, st->sysref_en && st->ch_en[LMX1204_CH_LOGIC] && st->sysrefout_en[LMX1204_CH_LOGIC], lmx1204_decode_fmt((enum logiclkout_fmt_options)st->logisysrefout_fmt)); + USDR_LOG("1204", USDR_LOG_INFO, "--------------"); + + //registers + + res = dry_run ? 0 : lmx1204_reset(st); + if(res) + { + USDR_LOG("1204", USDR_LOG_ERROR, "lmx1204_reset() err:%d", res); + return res; + } + + uint32_t regs[] = + { + MAKE_LMX1204_REG_WR(R90, st->logiclk_div_bypass ? 0x60 : 0x00), + MAKE_LMX1204_R86(0), //MUXOUT_EN_OVRD=0 + MAKE_LMX1204_R79(0, st->logiclk_div_bypass ? 0x104 : 0x5), + MAKE_LMX1204_REG_WR(0x4c, 0), //R76==0 + MAKE_LMX1204_R72(0, 0, 0, 0, SYSREF_DELAY_BYPASS_ENGAGE_IN_GENERATOR_MODE__BYPASS_IN_REPEATER_MODE), + + // need for MULT VCO calibration + MAKE_LMX1204_R67(st->clk_mux == CLK_MUX_MULTIPLIER_MODE ? 0x51cb : 0x50c8), + MAKE_LMX1204_R34(0, st->clk_mux == CLK_MUX_MULTIPLIER_MODE ? 0x04c5 : 0), + MAKE_LMX1204_R33(st->clk_mux == CLK_MUX_MULTIPLIER_MODE ? 0x6666 : 0x7777), + // + + MAKE_LMX1204_R25(0x4, 0, st->clk_mux == CLK_MUX_MULTIPLIER_MODE ? st->clk_mult_div : st->clk_mult_div - 1, st->clk_mux), + MAKE_LMX1204_R24(0,0,0,1), //enable temp sensor + MAKE_LMX1204_R23(1, 1, 1, 0, 1, st->sysref_delay_scale, st->sysref_delay_scale, st->sysref_delay_scale), + MAKE_LMX1204_R22(st->sysref_delay_scale, st->sysref_delay_scale, st->sysref_delay_div, 0, st->sysref_indiv_ch_delay[LMX1204_CH_LOGIC].q), + MAKE_LMX1204_R21(st->sysref_indiv_ch_delay[LMX1204_CH_LOGIC].i, st->sysref_indiv_ch_delay[LMX1204_CH_LOGIC].phase, st->sysref_indiv_ch_delay[LMX1204_CH3].q), + MAKE_LMX1204_R20(st->sysref_indiv_ch_delay[LMX1204_CH3].i, st->sysref_indiv_ch_delay[LMX1204_CH3].phase, st->sysref_indiv_ch_delay[LMX1204_CH2].q), + MAKE_LMX1204_R19(st->sysref_indiv_ch_delay[LMX1204_CH2].i, st->sysref_indiv_ch_delay[LMX1204_CH2].phase, st->sysref_indiv_ch_delay[LMX1204_CH1].q), + MAKE_LMX1204_R18(st->sysref_indiv_ch_delay[LMX1204_CH1].i, st->sysref_indiv_ch_delay[LMX1204_CH1].phase, st->sysref_indiv_ch_delay[LMX1204_CH0].q), + MAKE_LMX1204_R17(0, st->sysref_indiv_ch_delay[LMX1204_CH0].i, st->sysref_indiv_ch_delay[LMX1204_CH0].phase, st->sysref_mode), + MAKE_LMX1204_R16(0x1, st->sysref_div), //0x1 == sysref pulse count + MAKE_LMX1204_R15(0, st->sysref_div_pre, 0x3, st->sysref_en ? 1 : 0, 0, 0), + MAKE_LMX1204_R13(0, lmx1204_get_delay_step_size(st)), + + MAKE_LMX1204_R9(0, 0, 0, st->logiclk_div_bypass ? 1 : 0, 0, st->logiclk_div), + MAKE_LMX1204_R8(0, st->logiclk_div_pre, 1, st->ch_en[LMX1204_CH_LOGIC] ? 1 : 0, st->logisysrefout_fmt, st->logiclkout_fmt), + MAKE_LMX1204_R7(0, 0, 0, 0, 0, 0, 0, st->sysrefout_en[LMX1204_CH_LOGIC] ? 1 : 0), + MAKE_LMX1204_R6(st->clkout_en[LMX1204_CH_LOGIC], 0x4, 0x4, 0x4, 0x4, 0x4), + MAKE_LMX1204_R4(0, 0x6, 0x6, + st->sysrefout_en[LMX1204_CH3], st->sysrefout_en[LMX1204_CH2], st->sysrefout_en[LMX1204_CH1], st->sysrefout_en[LMX1204_CH0], + st->clkout_en[LMX1204_CH3], st->clkout_en[LMX1204_CH2], st->clkout_en[LMX1204_CH1], st->clkout_en[LMX1204_CH0]), + MAKE_LMX1204_R3(st->ch_en[LMX1204_CH3], st->ch_en[LMX1204_CH2], st->ch_en[LMX1204_CH1], st->ch_en[LMX1204_CH0], 1, 1, 1, 1, 1, 0, st->smclk_div), + MAKE_LMX1204_R2(0,0,st->smclk_div_pre, st->clk_mux == CLK_MUX_MULTIPLIER_MODE ? 1 : 0, 0x3), + // + MAKE_LMX1204_R0(0, 0, 0, 0), //reset RESET bit last -> calibration started + }; + + res = dry_run ? common_print_registers_a8d16(regs, SIZEOF_ARRAY(regs), USDR_LOG_DEBUG) : lmx1204_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + return res; + + return 0; +} + +int lmx1204_calibrate(lmx1204_state_t* st) +{ + if(st->clk_mux != CLK_MUX_MULTIPLIER_MODE) + { + USDR_LOG("1204", USDR_LOG_DEBUG, "VCO calibration not needed for BUFFER & DIV modes"); + return 0; + } + + uint32_t regs[] = + { + MAKE_LMX1204_R67(0x51cb), + MAKE_LMX1204_R34(0, 0x04c5), + MAKE_LMX1204_R33(0x6666), + MAKE_LMX1204_R0(0, 0, 0, 0), + }; + + int res = lmx1204_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + return res; + + return 0; +} + +int lmx1204_wait_pll_lock(lmx1204_state_t* st, unsigned timeout) +{ + int res = 0; + unsigned elapsed = 0; + + if(st->clk_mux != CLK_MUX_MULTIPLIER_MODE) + { + USDR_LOG("1204", USDR_LOG_DEBUG, "VCO lock not needed for BUFFER & DIV modes"); + return 0; + } + + uint16_t r75; + while(timeout == 0 || elapsed < timeout) + { + uint64_t tk = clock_get_time(); + + res = lmx1204_spi_get(st, R75, &r75); + if(res) + return res; + + const uint16_t lock_detect_status = (r75 & RB_LD_MSK) >> RB_LD_OFF; + switch(lock_detect_status) + { + case RB_LD_LOCKED: + { + uint32_t reg = MAKE_LMX1204_R2(0,0,st->smclk_div_pre, 0, 0x3); //switch off state machine to reduce spurs + lmx1204_spi_post(st, ®, 1); + USDR_LOG("1204", USDR_LOG_DEBUG, "VCO locked in %.2f msecs. State machine disabled.", (double)elapsed / 1000); + return 0; + } + default: + usleep(1000); + elapsed += (clock_get_time() - tk); + } + } + + return -ETIMEDOUT; +} + +int lmx1204_reload_sysrefout_ch_delay(lmx1204_state_t* st) +{ + uint32_t regs[] = + { + MAKE_LMX1204_R22(st->sysref_delay_scale, st->sysref_delay_scale, st->sysref_delay_div, 0, st->sysref_indiv_ch_delay[LMX1204_CH_LOGIC].q), + MAKE_LMX1204_R21(st->sysref_indiv_ch_delay[LMX1204_CH_LOGIC].i, st->sysref_indiv_ch_delay[LMX1204_CH_LOGIC].phase, st->sysref_indiv_ch_delay[LMX1204_CH3].q), + MAKE_LMX1204_R20(st->sysref_indiv_ch_delay[LMX1204_CH3].i, st->sysref_indiv_ch_delay[LMX1204_CH3].phase, st->sysref_indiv_ch_delay[LMX1204_CH2].q), + MAKE_LMX1204_R19(st->sysref_indiv_ch_delay[LMX1204_CH2].i, st->sysref_indiv_ch_delay[LMX1204_CH2].phase, st->sysref_indiv_ch_delay[LMX1204_CH1].q), + MAKE_LMX1204_R18(st->sysref_indiv_ch_delay[LMX1204_CH1].i, st->sysref_indiv_ch_delay[LMX1204_CH1].phase, st->sysref_indiv_ch_delay[LMX1204_CH0].q), + MAKE_LMX1204_R17(0, st->sysref_indiv_ch_delay[LMX1204_CH0].i, st->sysref_indiv_ch_delay[LMX1204_CH0].phase, st->sysref_mode), + }; + + return lmx1204_spi_post(st, regs, SIZEOF_ARRAY(regs)); +} + +int lmx1204_sysref_windowing_beforesync(lmx1204_state_t* st) +{ + int res; + + uint8_t delay_step_size = lmx1204_get_delay_step_size(st); + USDR_LOG("1204", USDR_LOG_DEBUG, "DELAY_STEPSIZE:%u", delay_step_size); + + { + uint32_t regs[] = + { + MAKE_LMX1204_R9(0, 1/*SYNC_EN*/, 0, st->logiclk_div_bypass ? 1 : 0, 0, st->logiclk_div), + MAKE_LMX1204_R14(0, 0, 0, 1/*CLKPOS_CAPTURE_EN*/, 1, 0), + MAKE_LMX1204_R13(0, delay_step_size), + }; + + res = lmx1204_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + return res; + } + + { + uint16_t r15; + res = lmx1204_spi_get(st, R15, &r15); + if(res) + return res; + + uint32_t regval_set = MAKE_LMX1204_REG_WR(R15, r15 | SYSREFREQ_CLR_MSK); + uint32_t regval_rst = MAKE_LMX1204_REG_WR(R15, r15 & ~SYSREFREQ_CLR_MSK); + + res = lmx1204_spi_post(st, ®val_set, 1); + res = res ? res : lmx1204_spi_post(st, ®val_rst, 1); + } + + return res; +} + +int lmx1204_sysref_windowing_aftersync(lmx1204_state_t* st) +{ + uint32_t regs[] = + { + MAKE_LMX1204_R9(0, 0/*SYNC_EN*/, 0, st->logiclk_div_bypass ? 1 : 0, 0, st->logiclk_div), + MAKE_LMX1204_R14(0, 0, 0, 0/*CLKPOS_CAPTURE_EN*/, 1, 0), + }; + return lmx1204_spi_post(st, regs, SIZEOF_ARRAY(regs)); +} + +int lmx1204_sysref_windowing_capture(lmx1204_state_t* st) +{ + int res; + uint16_t r11, r12; + + res = lmx1204_spi_get(st, R11, &r11); + res = res ? res : lmx1204_spi_get(st, R12, &r12); + if(res) + return res; + + uint32_t clkpos = ((uint32_t)r12 << 16) | r11; + + unsigned delay; + res = common_ti_calc_sync_delay(clkpos, &delay); + if(res) + return res; + + { + uint32_t reg = MAKE_LMX1204_R15(0, st->sysref_div_pre, 0x3, st->sysref_en ? 1 : 0, delay, 0); + res = lmx1204_spi_post(st, ®, 1); + if(res) + return res; + } + + return 0; +} diff --git a/src/lib/hw/lmx1204/lmx1204.h b/src/lib/hw/lmx1204/lmx1204.h new file mode 100644 index 00000000..b9b1c654 --- /dev/null +++ b/src/lib/hw/lmx1204/lmx1204.h @@ -0,0 +1,118 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef LMX1204_H +#define LMX1204_H + +#include + +#define LMX1204_OUT_CNT 4 + +enum +{ + LMX1204_CH0 = 0, + LMX1204_CH1 = 1, + LMX1204_CH2 = 2, + LMX1204_CH3 = 3, + LMX1204_CH_LOGIC = 4, +}; + +struct lmx1204_sysrefout_channel_delay +{ + uint8_t phase; + uint8_t q; + uint8_t i; +}; +typedef struct lmx1204_sysrefout_channel_delay lmx1204_sysrefout_channel_delay_t; + +struct lmx1204_state +{ + lldev_t dev; + unsigned subdev; + unsigned lsaddr; + + uint64_t clkin; + uint64_t sysrefreq; + bool filter_mode; //affects when clkin==clkout + uint8_t sysref_mode; //enum sysref_mode_options + + bool sysref_en; + + bool ch_en[LMX1204_OUT_CNT + 1]; // 4 + logic ch + bool clkout_en[LMX1204_OUT_CNT + 1]; + bool sysrefout_en[LMX1204_OUT_CNT + 1]; + + double clkout; + double sysrefout; + double logiclkout; + + uint8_t logiclkout_fmt; + uint8_t logisysrefout_fmt; + + // + + uint8_t clk_mux; //enum clk_mux_options + uint8_t clk_mult_div; + + uint8_t smclk_div_pre; + uint8_t smclk_div; + + uint8_t logiclk_div_pre; + bool logiclk_div_bypass; + uint16_t logiclk_div; + + uint8_t sysref_delay_div; + uint8_t sysref_div_pre; + uint16_t sysref_div; + uint8_t sysref_delay_scale; + lmx1204_sysrefout_channel_delay_t sysref_indiv_ch_delay[LMX1204_OUT_CNT + 1]; // 4 + logic ch +}; +typedef struct lmx1204_state lmx1204_state_t; + +static inline void lmx1204_init_sysrefout_ch_delay(lmx1204_state_t* st, unsigned ch, uint8_t phase, uint8_t i, uint8_t q) +{ + if(ch > LMX1204_CH_LOGIC) + return; + + st->sysref_indiv_ch_delay[ch].phase = phase; + st->sysref_indiv_ch_delay[ch].i = i; + st->sysref_indiv_ch_delay[ch].q = q; +} + +struct lmx1204_stats +{ + float temperature; + uint8_t vco_sel; + uint8_t lock_detect_status; +}; +typedef struct lmx1204_stats lmx1204_stats_t; + +enum +{ + LMX1204_FMT_LVDS = 0, + LMX1204_FMT_LVPECL = 1, + LMX1204_FMT_CML = 2, +}; + +enum +{ + LMX1204_CONTINUOUS = 0, + LMX1204_PULSER = 1, + LMX1204_REPEATER = 2, +}; + +int lmx1204_read_status(lmx1204_state_t* st, lmx1204_stats_t* status); +int lmx1204_calibrate(lmx1204_state_t* st); +int lmx1204_wait_pll_lock(lmx1204_state_t* st, unsigned timeout); +int lmx1204_reset_main_divider(lmx1204_state_t* st, bool set_flag); +int lmx1204_solver(lmx1204_state_t* st, bool prec_mode, bool dry_run); +int lmx1204_get_temperature(lmx1204_state_t* st, float* value); +int lmx1204_reload_sysrefout_ch_delay(lmx1204_state_t* st); +int lmx1204_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lmx1204_state_t* st); +int lmx1204_destroy(lmx1204_state_t* st); + +int lmx1204_sysref_windowing_beforesync(lmx1204_state_t* st); +int lmx1204_sysref_windowing_capture(lmx1204_state_t* st); +int lmx1204_sysref_windowing_aftersync(lmx1204_state_t* st); + +#endif // LMX1204_H diff --git a/src/lib/hw/lmx1204/lmx1204.yaml b/src/lib/hw/lmx1204/lmx1204.yaml new file mode 100644 index 00000000..ca14a3f7 --- /dev/null +++ b/src/lib/hw/lmx1204/lmx1204.yaml @@ -0,0 +1,979 @@ +name: LMX1204 +revision: 0.0.1 +processors: [ c ] +bus: + type: SPI + rd_mask: 0x800000 + usdr_path: /debug/hw/lmx1204/*/reg +addr_width: 8 +data_width: 16 + +pages: +- name: Main + regs: + - addr: 0x0 + name: R0 + fields: + - bits: 0 + name: RESET + mode: RW + dflt: 0x0 + desc: Soft Reset. Resets the entire logic and registers (equivalent to power-on reset). Self-clearing on next register write. + - bits: 1 + name: R0_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Reserved. If this register is written, set this bit to 0x0. + - bits: 2 + name: POWERDOWN + mode: RW + dflt: 0x0 + desc: Sets the device in a low-power state. The states of other registers are maintained. + - bits: '15:3' + name: R0_RESERVED_0 + mode: R + dflt: 0x0000 + desc: Reserved (not used). + - addr: 0x2 + name: R2 + fields: + - bits: '4:0' + name: R2_RESERVED_2 + mode: RW + dflt: 0x03 + desc: Reserved. If this register is written, set these bits to 0x03. + - bits: 5 + name: SMCLK_EN + mode: RW + dflt: 0x1 + desc: Enables the state machine clock generator. Only required to calibrate the multiplier, and for multiplier lock detect (including on MUXOUT pin). If the multiplier is not used, or if the multiplier lock detect feature is not used, the state machine clock generator can be disabled to minimize crosstalk. + - bits: '9:6' + name: SMCLK_DIV_PRE + mode: RW + dflt: 0x8 + desc: "Sets pre-divider for state machine clock. The state machine clock is divided from CLKIN. The output of the pre-divider must be <= 1600 MHz. Values other than those listed below are reserved." + opts: + 0x2: "+2" + 0x4: "+4" + 0x8: "+8" + - bits: 10 + name: R2_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Reserved. If this register is written, set this bit to 0x0. + - bits: '15:11' + name: R2_RESERVED_0 + mode: R + dflt: 0x00 + desc: Reserved (not used). + - addr: 0x3 + name: R3 + fields: + - bits: '2:0' + name: SMCLK_DIV + mode: RW + dflt: 0x6 + desc: "Sets state machine clock divider. Further divides the output of the state machine clock pre-divider. Input frequency from SMCLK_DIV_PRE must be <= 1600 MHz. Output frequency must be <= 30 MHz. Divide value is 2SMCLK_DIV." + opts: + 0x0: "+1" + 0x1: "+2" + 0x2: "+4" + 0x3: "+8" + 0x4: "+16" + 0x5: "+32" + 0x6: "+64" + 0x7: "+128" + - bits: '6:3' + name: R3_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Reserved. If this register is written, set these bits to 0x0. + - bits: 7 + name: CH0_MUTE_CAL + mode: RW + dflt: 0x1 + desc: Mutes CH0 (CLKOUT0, SYSREFOUT0) during multiplier calibration. + - bits: 8 + name: CH1_MUTE_CAL + mode: RW + dflt: 0x1 + desc: Mutes CH1 (CLKOUT1, SYSREFOUT1) during multiplier calibration. + - bits: 9 + name: CH2_MUTE_CAL + mode: RW + dflt: 0x1 + desc: Mutes CH2 (CLKOUT2, SYSREFOUT2) during multiplier calibration. + - bits: 10 + name: CH3_MUTE_CAL + mode: RW + dflt: 0x1 + desc: Mutes CH3 (CLKOUT3, SYSREFOUT3) during multiplier calibration. + - bits: 11 + name: LOGIC_MUTE_CAL + mode: RW + dflt: 0x1 + desc: Mutes LOGIC outputs (LOGICLKOUT, LOGISYSREFOUT) during multiplier calibration. + - bits: 12 + name: CH0_EN + mode: RW + dflt: 0x1 + desc: Enables CH0 (CLKOUT0, SYSREFOUT0). Setting this bit to 0x0 completely disables all CH0 circuitry, overriding the state of other powerdown/enable bits. + - bits: 13 + name: CH1_EN + mode: RW + dflt: 0x1 + desc: Enables CH1 (CLKOUT1, SYSREFOUT1). Setting this bit to 0x0 completely disables all CH1 circuitry, overriding the state of other powerdown/enable bits. + - bits: 14 + name: CH2_EN + mode: RW + dflt: 0x1 + desc: Enables CH2 (CLKOUT2, SYSREFOUT2). Setting this bit to 0x0 completely disables all CH2 circuitry, overriding the state of other powerdown/enable bits. + - bits: 15 + name: CH3_EN + mode: RW + dflt: 0x1 + desc: Enables CH3 (CLKOUT3, SYSREFOUT3). Setting this bit to 0x0 completely disables all CH3 circuitry, overriding the state of other powerdown/enable bits. + - addr: 0x4 + name: R4 + fields: + - bits: 0 + name: CLKOUT0_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT0 output buffer. + - bits: 1 + name: CLKOUT1_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT1 output buffer. + - bits: 2 + name: CLKOUT2_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT2 output buffer. + - bits: 3 + name: CLKOUT3_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT3 output buffer. + - bits: 4 + name: SYSREFOUT0_EN + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT0 output buffer. + - bits: 5 + name: SYSREFOUT1_EN + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT1 output buffer. + - bits: 6 + name: SYSREFOUT2_EN + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT2 output buffer. + - bits: 7 + name: SYSREFOUT3_EN + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT3 output buffer. + - bits: '10:8' + name: CLKOUT0_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT0. Larger values correspond to higher output power. + - bits: '13:11' + name: CLKOUT1_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT1. Larger values correspond to higher output power. + - bits: '15:14' + name: R4_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x5 + name: R5 + fields: + - bits: '2:0' + name: CLKOUT2_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT2. Larger values correspond to higher output power. + - bits: '5:3' + name: CLKOUT3_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT3. Larger values correspond to higher output power. + - bits: '8:6' + name: SYSREFOUT0_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT0. Larger values correspond to higher output power. SYSREFOUT0_VCM must be set properly to bring the output common-mode voltage within permissible limits. See also R6 Register. + - bits: '11:9' + name: SYSREFOUT1_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT1. Larger values correspond to higher output power. SYSREFOUT1_VCM must be set properly to bring the output common-mode voltage within permissible limits. See also R6 Register. + - bits: '14:12' + name: SYSREFOUT2_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT2. Larger values correspond to higher output power. SYSREFOUT2_VCM must be set properly to bring the output common-mode voltage within permissible limits. See also R6 Register. + - bits: 15 + name: R5_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x6 + name: R6 + fields: + - bits: '2:0' + name: SYSREFOUT3_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT3. Larger values correspond to higher output power. SYSREFOUT3_VCM must be set properly bring the output common-mode voltage within permissible limits. + - bits: '5:3' + name: SYSREFOUT0_VCM + mode: RW + dflt: 0x3 + desc: Sets the output common mode of SYSREFOUT0. SYSREFOUT0_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. See also R5 Register. + - bits: '8:6' + name: SYSREFOUT1_VCM + mode: RW + dflt: 0x3 + desc: Sets the output common mode of SYSREFOUT1. SYSREFOUT1_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. See also R5 Register. + - bits: '11:9' + name: SYSREFOUT2_VCM + mode: RW + dflt: 0x3 + desc: Sets the output common mode of SYSREFOUT2. SYSREFOUT2_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. See also R5 Register. + - bits: '14:12' + name: SYSREFOUT3_VCM + mode: RW + dflt: 0x3 + desc: Sets the output common mode of SYSREFOUT3. SYSREFOUT3_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. + - bits: 15 + name: LOGICLKOUT_EN + mode: RW + dflt: 0x0 + desc: Enables the LOGICLKOUT output buffer. + - addr: 0x7 + name: R7 + fields: + - bits: 0 + name: LOGISYSREFOUT_EN + mode: RW + dflt: 0x0 + desc: Enables LOGISYSREFOUT output buffer. + - bits: '3:1' + name: LOGICLKOUT_PWR + mode: RW + dflt: 0x0 + desc: Sets the output power of LOGICLKOUT in CML format. Larger values correspond to higher output power. Other output formats (LVDS, LVPECL) ignore this field. Valid range is 0x0 to 0x3. + - bits: '6:4' + name: LOGISYSREFOUT_PWR + mode: RW + dflt: 0x0 + desc: Sets the output power of LOGISYSREFOUT in CML format. Larger values correspond to higher output power. Other output formats (LVDS, LVPECL) ignore this field. Valid range is 0x0 to 0x3. + - bits: '8:7' + name: LOGICLKOUT_PREDRV_PWR + mode: RW + dflt: 0x0 + desc: Sets the output power of the LOGICLKOUT pre-driver. Larger values correspond to higher output power. Default value is sufficient for typical use. + - bits: '10:9' + name: LOGISYSREFOUT_PREDRV_PWR + mode: RW + dflt: 0x0 + desc: Sets the output power of the LOGISYSREFOUT pre-driver. Larger values correspond to higher output power. Default value is sufficient for typical use. + - bits: '12:11' + name: LOGICLKOUT_VCM + mode: RW + dflt: 0x0 + desc: Sets the output common mode of LOGICLKOUT in LVDS format. Other output formats (CML, LVPECL) ignore this field. + opts: + 0x0: 0x0 = 1.2 V + 0x1: 0x1 = 1.1 V + 0x2: 0x2 = 1.0 V + 0x3: 0x3 = 0.9 V + - bits: '14:13' + name: LOGISYSREFOUT_VCM + mode: RW + dflt: 0x0 + desc: Sets the output common mode of LOGISYSREFOUT in LVDS format. Other output formats (CML, LVPECL) ignore this field. + opts: + 0x0: 0x0 = 1.2 V + 0x1: 0x1 = 1.1 V + 0x2: 0x2 = 1.0 V + 0x3: 0x3 = 0.9 V + - bits: 15 + name: R7_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x8 + name: R8 + fields: + - bits: '1:0' + name: LOGICLKOUT_FMT + mode: RW + dflt: 0x0 + desc: "Selects the output driver format of the LOGICLKOUT output. LVDS allows for common mode control with LOGICLKOUT_VCM field. CML allows for output power control with LOGICLKOUT_PWR field. CML format requires external 50-Ohm pull-up resistors. LVPECL requires external 220-Ohm emitter resistors to GND when AC-coupled, or 50-Ohm to VCC - 2 V (0.5 V) when DC-coupled. See also R7 Register." + opts: + 0x0: LVDS + 0x1: LVPECL + 0x2: CML + - bits: '3:2' + name: LOGISYSREFOUT_FMT + mode: RW + dflt: 0x0 + desc: "Selects the output driver format of the LOGISYSREFOUT output. LVDS allows for common mode control with LOGISYSREFOUT_VCM field. CML allows for output power control with LOGISYSREFOUT_PWR field. CML format requires external 50-Ohm pull-up resistors. LVPECL requires external 220-Ohm emitter resistors to GND when AC-coupled, or 50-Ohm to VCC - 2 V (0.5 V) when DC-coupled. See also R7 Register." + opts: + 0x0: LVDS + 0x1: LVPECL + 0x2: CML + - bits: 4 + name: LOGIC_EN + mode: RW + dflt: 0x0 + desc: Enables LOGICLK subsystem (LOGICLKOUT, LOGISYSREFOUT). Setting this bit to 0x0 completely disables all LOGICLKOUT and LOGISYSREFOUT circuitry, overriding the state of other powerdown/ enable bits. + - bits: 5 + name: R8_RESERVED_1 + mode: RW + dflt: 0x1 + desc: Reserved. If this register is written, set this bit to 0x1. + - bits: '8:6' + name: LOGICLK_DIV_PRE + mode: RW + dflt: 0x4 + desc: "Sets pre-divider value for logic clock divider. Output of the pre-divider must be <= 3.2 GHz. Values other than those listed below are reserved." + opts: + 0x1: "+1" + 0x2: "+2" + 0x4: "+4" + - bits: '15:9' + name: R8_RESERVED_0 + mode: R + dflt: 0x00 + desc: Reserved (not used). + - addr: 0x9 + name: R9 + fields: + - bits: '9:0' + name: LOGICLK_DIV + mode: RW + dflt: 0x1E + desc: "Sets LOGICLK divider value. Maximum input frequency from LOGICLK_DIV_PRE must be <= 3200 MHz. The maximum LOGICLKOUT frequency must be <= 800 MHz to avoid amplitude degradation. 0x0: Reserved 0x1: Reserved 0x2: +2 0x3: +3 ... 0x1FF: +1023" + - bits: 10 + name: R9_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Reserved. If this register is written, set this bit to 0x0. + - bits: 11 + name: LOGICLK_DIV_BYPASS + mode: RW + dflt: 0x0 + desc: "Bypasses the LOGICLK divider, deriving LOGICLK output directly from the pre-divider. Used to achieve divide-by-1 when LOGICLK_DIV_PRE = 0x1. When LOGICLK_DIV_PRE = 0x2 or 0x4, this bit must be set to 0x0. When LOGICLK_DIV_BYPASS = 0x1, set R90[6:5] = 0x3 and R79[9:8] = 0x0. When LOGICLK_DIV_BYPASS = 0x0, if R90[6:5] = 0x3 due to previous user setting, set R90[6:5] = 0x0. When LOGICLK_DIV_BYPASS = 0x1, the LOGICLKOUT frequency must be <= 800 MHz to avoid amplitude degradation. See also R79 Register and R90 Register." + - bits: 12 + name: LOGICLK_DIV_PD + mode: RW + dflt: 0x0 + desc: Disables the LOGICLK divider. LOGICLK pre-divider remains enabled. Used to reduce current consumption when bypassing the LOGICLK divider. When LOGICLK_DIV_PRE = 0x2 or 0x4, this bit must be set to 0x0. + - bits: 13 + name: SYNC_EN + mode: RW + dflt: 0x0 + desc: Enables synchronization path for the dividers and allows the clock position capture circuitry to be enabled. Used for multi-device synchronization. Redundant if SYSREF_EN = 0x1. + - bits: '15:14' + name: SYSREFREQ_VCM + mode: RW + dflt: 0x0 + desc: Sets the internal DC Bias for the SYSREFREQ pins. Bias must be enabled for AC-coupled inputs; but can be enabled and overdriven, or disabled, for DC-coupled inputs. SYSREFREQ DC pin voltage must be in the range of 0.7 V to VCC, including minimum and maximum signal swing. + opts: + 0x0: 1.3 V + 0x1: 1.1 V + 0x2: 1.5 V + 0x3: Disabled (DC-coupled only) + - addr: 0xB + name: R11 + fields: + - bits: '15:0' + name: RB_CLKPOS_L + mode: R + dflt: 0xFFFF + desc: Stores a snapshot of the CLKIN signal rising edge positions relative to a SYSREFREQ rising edge, with the snapshot starting from the LSB and ending at the MSB. Each bit represents a sample of the CLKIN signal, separated by a delay determined by the SYSREFREQ_DELAY_STEPSIZE field. The first and last bits of rb_CLKPOS are always set, indicating uncertainty at the capture window boundary conditions. CLKIN rising edges are represented by every sequence of two set bits from LSB to MSB, including bits at the boundary conditions. The position of the CLKIN rising edges in the snapshot, along with the CLKIN signal period and the delay step size, can be used to compute the value of SYSREFREQ_DELAY_STEP which maximizes setup and hold times for SYNC signals on the SYSREFREQ pins. See also R12 Register, R13 Register, R14 Register, and R15 Register. + - addr: 0xC + name: R12 + fields: + - bits: '15:0' + name: RB_CLKPOS_U + mode: R + dflt: 0xFFFF + desc: MSBs of rb_CLKPOS field. See also R11 Register, R13 Register, R14 Register, and R15 Register. + - addr: 0xD + name: R13 + fields: + - bits: '1:0' + name: SYSREFREQ_DELAY_STEPSIZE + mode: RW + dflt: 0x3 + desc: Sets the step size of the delay element used in the SYSREFREQ path, both for SYSREFREQ input delay and for clock position captures. The recommended frequency range for each step size creates the maximum number of usable steps for a given CLKIN frequency. The ranges include some overlap to account for process and temperature variations. If the CLKIN frequency is covered by an overlapping span, larger delay step sizes improve the likelihood of detecting a CLKIN rising edge during a clock position capture. However, since larger values include more delay steps, larger step sizes have greater total delay variation across PVT relative to smaller step sizes. See also R11 Register, R12 Register, R14 Register, and R15 Register. + opts: + 0x0: 28 ps (1.4 GHz to 2.7 GHz) + 0x1: 15 ps (2.4 GHz to 4.7 GHz) + 0x2: 11 ps (3.1 GHz to 5.7 GHz) + 0x3: 8 ps (4.5 GHz to 12.8 GHz) + - bits: '15:2' + name: R13_RESERVED_0 + mode: R + dflt: 0x0000 + desc: Reserved (not used). + - addr: 0xE + name: R14 + fields: + - bits: 0 + name: SYSREFREQ_LATCH + mode: RW + dflt: 0x0 + desc: Latches the internal SYSREFREQ state to logic high on the first rising edge of the SYSREFREQ pins. This latch can be cleared by setting SYSREFREQ_CLR to 0x1, or bypassed by setting SYSREFREQ_LATCH to 0x0. See also R15 Register. + - bits: 1 + name: SYSREFREQ_MODE + mode: RW + dflt: 0x1 + desc: Selects the function of the SYSREFREQ pins. + opts: + 0b0: SYNC Pin + 0b1: SYSREFREQ Pin + - bits: 2 + name: CLKPOS_CAPTURE_EN + mode: RW + dflt: 0x0 + desc: Enables the windowing circuit which captures the clock position in the rb_CLKPOS registers with respect to a SYSREF edge. The windowing circuit must be cleared by toggling SYSREFREQ_CLR high then low before a clock position capture. The first rising edge on the SYSREFREQ pins after clearing the windowing circuit triggers the capture. The capture circuitry greatly increases supply current, and does not need to be enabled to delay the SYSREFREQ signal in SYNC or SYSREF modes. Once the desired value of SYSREFREQ_DELAY_STEP is determined, set this bit to 0x0 to minimize current consumption. If SYNC_EN = 0x0 and SYSREF_EN = 0x0, the value of this bit is ignored, and the windowing circuit is disabled. See also R11 Register, R12 Register, R13 Register, and R15 Register. + - bits: '7:3' + name: R14_RESERVED_1 + mode: RW + dflt: 0x00 + desc: Reserved. If this register is written, set these bits to 0x00. + - bits: 8 + name: SYNC_MUTE_PD + mode: RW + dflt: 0x0 + desc: Removes the mute condition on the SYSREFOUT and LOGISYSREFOUT pins during SYNC mode (SYSREFREQ_MODE = 0x0). Since the SYNC operation also resets the SYSREF dividers, the mute condition is usually desirable, and this bit can be left at the default value. + - bits: '15:9' + name: R14_RESERVED_0 + mode: RW + dflt: 0x00 + desc: Reserved. If this register is written, set these bits to 0x00. + - addr: 0xF + name: R15 + fields: + - bits: 0 + name: SYSREFREQ_CLR + mode: RW + dflt: 0x1 + desc: Clears SYSREFREQ_LATCH, which resets the SYSREFREQ input latch, the internal divider synchronization retimers, and the clock position capture flip-flops comprising rb_CLKPOS. When set, holds the internal SYSREFREQ signal low in all modes except SYSREF repeater mode, overriding the state of SYSREFREQ_SPI. This bit must be set and cleared once before the SYNC or clock position capture operations are performed. See also R14 Register. + - bits: '6:1' + name: SYSREFREQ_DELAY_STEP + mode: RW + dflt: 0x0 + desc: Sets the delay line step for the external SYSREFREQ signal. Each delay line step delays the SYSREFREQ signal by an amount equal to SYSREFREQ_DELAY_STEP x SYSREFREQ_DELAY_STEPSIZE. In SYNC mode, the value for this field can be determined based on the rb_CLKPOS value to satisfy the internal setup and hold time of the SYNC signal with respect to the CLKIN signal. In SYSREF Repeater Mode, the value for this field can be used as a coarse global delay. Values greater than 0x3F are invalid. Since larger values include more delay steps, larger values have greater total step size variation across PVT relative to smaller values. Refer to the data sheet or the device TICS Pro profile for detailed description of the delay step computation procedure. See also R11 Register, R12 Register, R13 Register, and R14 Register. + - bits: 7 + name: SYSREF_EN + mode: RW + dflt: 0x0 + desc: Enables SYSREF subsystem (and SYNC subsystem when SYSREFREQ_MODE = 0x0). Setting this bit to 0x0 completely disables all SYNC, SYSREF, and clock position capture circuitry, overriding the state of other powerdown/enable bits except SYNC_EN. If SYNC_EN = 0x1, the SYNC path and clock position capture circuitry are still enabled, regardless of the state of SYSREF_EN. + - bits: '9:8' + name: R15_RESERVED_1 + mode: RW + dflt: 0x1 + desc: Reserved. If this register is written, set these bits to 0x1. + - bits: '11:10' + name: SYSREF_DIV_PRE + mode: RW + dflt: 0x2 + desc: "Sets the SYSREF pre-divider. Maximum output frequency must be <= 3.2 GHz." + opts: + 0x0: "+1" + 0x1: "+2" + 0x2: "+4" + - bits: '15:12' + name: R15_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x10 + name: R16 + fields: + - bits: '11:0' + name: SYSREF_DIV + mode: RW + dflt: 0x3 + desc: "Sets the SYSREF divider. Maximum input frequency from SYSREF_DIV_PRE must be <= 3200 MHz. Maximum output frequency must be <= 100 MHz. Odd divides (with duty cycle != 50%) are only allowed when the delay generators are bypassed. See also R72 Register. 0x0: Reserved 0x1: Reserved 0x2: +2 0x3: +3 ... 0xFFF: +4095" + - bits: '15:12' + name: SYSREF_PULSE_COUNT + mode: RW + dflt: 0x1 + desc: 'Programs the number of pulses generated in pulser mode. The pulser is a counter gating the SYSREF divider; consequently, the pulse duration and frequency are equal to the duty cycle and frequency of the SYSREF divider output, respectively. 0x0: Reserved 0x1: 1 pulse 0x2: 2 pulses ... 0xF: 15 pulses' + - addr: 0x11 + name: R17 + fields: + - bits: '1:0' + name: SYSREF_MODE + mode: RW + dflt: 0x0 + desc: Controls how the SYSREF signal is generated or repeated. See also SYSREF_DELAY_BYPASS in R79 Register for additional configuration options. + opts: + 0x0: Continuous (Generator Mode) + 0x1: Pulser (Generator Mode) + 0x2: Repeater (Repeater Mode) + - bits: '3:2' + name: SYSREFOUT0_DELAY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT0 delay generator retimer. Consult the data sheet for configuration instructions. See also R18 Register and R22 Register. + opts: + 0x0: ICLK0 + 0x1: QCLK0 + 0x2: QCLK1 + 0x3: ICLK1 + - bits: '10:4' + name: SYSREFOUT0_DELAY_I + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT0 delay generator. Must satisfy SYSREFOUT0_DELAY_I + SYSREFOUT0_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R18 Register and R22 Register. + - bits: '15:11' + name: R17_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x12 + name: R18 + fields: + - bits: '6:0' + name: SYSREFOUT0_DELAY_Q + mode: RW + dflt: 0x0 + desc: Sets the delay step for the SYSREFOUT0 delay generator. Must satisfy SYSREFOUT0_DELAY_I + SYSREFOUT0_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R17 Register and R22 Register. + - bits: '8:7' + name: SYSREFOUT1_DELAY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT1 delay generator retimer. Consult the data sheet for configuration instructions. See also R19 Register and R22 Register. 0x0 = ICLK 0x1 = QCLK 0x2 = QCLK 0x3 = ICLK + opts: + 0x0: ICLK0 + 0x1: QCLK0 + 0x2: QCLK1 + 0x3: ICLK1 + - bits: '15:9' + name: SYSREFOUT1_DELAY_I + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT1 delay generator. Must satisfy SYSREFOUT1_DELAY_I + SYSREFOUT1_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R19 Register and R22 Register. + - addr: 0x13 + name: R19 + fields: + - bits: '6:0' + name: SYSREFOUT1_DELAY_Q + mode: RW + dflt: 0x0 + desc: Sets the delay step for the SYSREFOUT1 delay generator. Must satisfy SYSREFOUT1_DELAY_I + SYSREFOUT1_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R18 Register and R22 Register. + - bits: '8:7' + name: SYSREFOUT2_DELAY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT2 delay generator retimer. Consult the data sheet for configuration instructions. See also R20 Register and R23 Register. + opts: + 0x0: ICLK0 + 0x1: QCLK0 + 0x2: QCLK1 + 0x3: ICLK1 + - bits: '15:9' + name: SYSREFOUT2_DELAY_I + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT2 delay generator. Must satisfy SYSREFOUT2_DELAY_I + SYSREFOUT2_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R20 Register and R23 Register. + - addr: 0x14 + name: R20 + fields: + - bits: '6:0' + name: SYSREFOUT2_DELAY_Q + mode: RW + dflt: 0x0 + desc: Sets the delay step for the SYSREFOUT2 delay generator. Must satisfy SYSREFOUT2_DELAY_I + SYSREFOUT2_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R19 Register and R23 Register. + - bits: '8:7' + name: SYSREFOUT3_DELAY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT3 delay generator retimer. Consult the data sheet for configuration instructions. See also R21 Register and R23 Register. + opts: + 0x0: ICLK0 + 0x1: QCLK0 + 0x2: QCLK1 + 0x3: ICLK1 + - bits: '15:9' + name: SYSREFOUT3_DELAY_I + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT3 delay generator. Must satisfy SYSREFOUT3_DELAY_I + SYSREFOUT3_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R21 Register and R23 Register. + - addr: 0x15 + name: R21 + fields: + - bits: '6:0' + name: SYSREFOUT3_DELAY_Q + mode: RW + dflt: 0x0 + desc: Sets the delay step for the SYSREFOUT3 delay generator. Must satisfy SYSREFOUT3_DELAY_I + SYSREFOUT3_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R20 Register and R23 Register. + - bits: '8:7' + name: LOGISYSREFOUT_DELAY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the LOGISYSREFOUT delay generator retimer. Consult the data sheet for configuration instructions. See also R22 Register and R23 Register. + opts: + 0x0: ICLK0 + 0x1: QCLK0 + 0x2: QCLK1 + 0x3: ICLK1 + - bits: '15:9' + name: LOGISYSREFOUT_DELAY_I + mode: RW + dflt: 0x7F + desc: Sets the delay step for the LOGISYSREFOUT delay generator. Must satisfy LOGISYSREFOUT_DELAY_I + LOGISYSREFOUT_DELAY_Q = 0x7F. Consult the data sheet for configuration instructions. See also R22 Register and R23 Register. + - addr: 0x16 + name: R22 + fields: + - bits: '6:0' + name: LOGISYSREFOUT_DELAY_Q + mode: RW + dflt: 0x0 + desc: Sets the delay step for the LOGISYSREFOUT delay generator. Must satisfy LOGISYSREFOUT_DELAY_I + LOGISYSREFOUT_DELAY_Q = 0x7F. See also R21 Register and R23 Register. + - bits: '8:7' + name: R22_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Reserved. If this register is written, set these bits to 0x0. + - bits: '11:9' + name: SYSREF_DELAY_DIV + mode: RW + dflt: 0x4 + desc: "Sets the delay generator clock division, determining fINTERPOLATOR and the delay generator resolution. Values other than those listed below are reserved. See also R23 Register." + opts: + 0x0: "+2 (<= 1.6 GHz)" + 0x1: "+4 (1.6 GHz to 3.2 GHz)" + 0x2: "+8 (3.2 GHz to 6.4 GHz)" + 0x4: "+16 (6.4 GHz to 12.8 GHz)" + - bits: '13:12' + name: SYSREFOUT0_DELAY_SCALE + mode: RW + dflt: 0x0 + desc: Sets the frequency range of the SYSREFOUT0 delay generator. Set according to fINTERPOLATOR frequency. Consult the data sheet for configuration instructions. See also R17 Register and R18 Register. + opts: + 0x0: 400 MHz to 800 MHz + 0x1: 200 MHz to 400 MHz + 0x2: 150 MHz to 200 MHz + - bits: '15:14' + name: SYSREFOUT1_DELAY_SCALE + mode: RW + dflt: 0x0 + desc: Sets the frequency range of the SYSREFOUT1 delay generator. Set according to fINTERPOLATOR frequency. Consult the data sheet for configuration instructions. See also R18 Register and R19 Register. + opts: + 0x0: 400 MHz to 800 MHz + 0x1: 200 MHz to 400 MHz + 0x2: 150 MHz to 200 MHz + - addr: 0x17 + name: R23 + fields: + - bits: '1:0' + name: SYSREFOUT2_DELAY_SCALE + mode: RW + dflt: 0x0 + desc: Sets the frequency range of the SYSREFOUT2 delay generator. Set according to fINTERPOLATOR frequency. Consult the data sheet for configuration instructions. See also R19 Register, R20 Register, and R22 Register. + opts: + 0x0: 400 MHz to 800 MHz + 0x1: 200 MHz to 400 MHz + 0x2: 150 MHz to 200 MHz + - bits: '3:2' + name: SYSREFOUT3_DELAY_SCALE + mode: RW + dflt: 0x0 + desc: Sets the frequency range of the SYSREFOUT3 delay generator. Set according to fINTERPOLATOR frequency. Consult the data sheet for configuration instructions. See also R20 Register, R21 Register, and R22 Register. + opts: + 0x0: 400 MHz to 800 MHz + 0x1: 200 MHz to 400 MHz + 0x2: 150 MHz to 200 MHz + - bits: '5:4' + name: LOGISYSREFOUT_DELAY_SCALE + mode: RW + dflt: 0x0 + desc: Sets the frequency range of the LOGISYSREFOUT delay generator. Set according to fINTERPOLATOR frequency. Consult the data sheet for configuration instructions. See also R21 Register and R22 Register. + opts: + 0x0: 400 MHz to 800 MHz + 0x1: 200 MHz to 400 MHz + 0x2: 150 MHz to 200 MHz + - bits: 6 + name: MUXOUT_SEL + mode: RW + dflt: 0x0 + desc: Selects MUXOUT pin function. + opts: + 0b0: Lock Detect (Multiplier Only) + 0b1: SDO (SPI readback) + - bits: '12:7' + name: R23_RESERVED_1 + mode: RW + dflt: 0x00 + desc: Reserved. If this register is written, set these bits to 0x00. + - bits: 13 + name: MUXOUT_EN + mode: RW + dflt: 0x0 + desc: Enables or tri-states the MUXOUT pin driver. See also R86 Register. + opts: + 0b0: Tri-State + 0b1: Push-Pull + - bits: 14 + name: R23_RESERVED_0 + mode: RW + dflt: 0x1 + desc: Reserved. If this register is written, set this bit to 0x1. + - bits: 15 + name: EN_TEMPSENSE + mode: RW + dflt: 0x0 + desc: Enables the on-die temperature sensor. Temperature sensor counter (EN_TS_COUNT) must also be enabled for readback. See also R24 Register. + - addr: 0x18 + name: R24 + fields: + - bits: 0 + name: EN_TS_COUNT + mode: RW + dflt: 0x0 + desc: Enables temperature sensor counter. Temperature sensor (EN_TEMPSENSE) must be enabled for accurate data. See also R23 Register. + - bits: '11:1' + name: RB_TEMPSENSE + mode: R + dflt: 0x7FF + desc: "Output of on-die temperature sensor. Readback code can be converted to junction temperature (in \xB0C) according to the following equation: TJ = 0.65 * rb_TEMPSENSE - 351" + - bits: '13:12' + name: R24_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Reserved. If this register is written, set these bits to 0x0. + - bits: '15:14' + name: R24_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x19 + name: R25 + fields: + - bits: '2:0' + name: CLK_MUX + mode: RW + dflt: 0x1 + desc: Selects the function of the device. Multiplier Mode requires writing several other registers (R33, R34, and R67) to values differing from POR defaults, as well as configuring the state machine clock (R2 and R3), before multiplier calibration. Writing any value to R0 (as long as POWERDOWN = 0x0 and RESET = 0x0) triggers a multiplier calibration. Values other than those listed below are reserved. + opts: + 0x1: Buffer Mode + 0x2: Divider Mode + 0x3: Multiplier Mode + - bits: '5:3' + name: CLK_DIVCLK_MULT + mode: RW + dflt: 0x2 + desc: CLK_DIV and CLK_MULT are aliases for the same field. When CLK_MUX = 0x2 (Divider Mode), sets the clock divider equal to CLK_DIV + 1. Valid range is 0x1 to 0x7. Setting CLK_DIV = 0x0 disables the main clock divider and reverts to buffer mode. When CLK_MUX = 0x3 (Multiplier Mode), sets the multiplier equal to CLK_MULT. Valid range is 0x1 to 0x4. Setting CLK_MULT to an invalid value disables the multiplier and reverts to buffer mode. When CLK_MUX = 0x1 (buffer mode), this field is ignored. + - bits: 6 + name: CLK_DIV_RST + mode: RW + dflt: 0x0 + desc: Resets the main clock divider. If the clock divider value is changed during operation, set this bit high then low after setting the new divider value. Synchronizing the device with the SYSREFREQ pins in SYSREFREQ_MODE = 0x0 and SYNC_EN = 0x1 also resets the main clock divider. This bit has no effect when outside of Divider Mode. + - bits: '15:7' + name: R25_RESERVED_0 + mode: RW + dflt: 0x004 + desc: Reserved. If this register is written, set these bits to 0x004. + - addr: 0x1C + name: R28 + fields: + - bits: '8:0' + name: R28_RESERVED_1 + mode: RW + dflt: 0x008 + desc: Reserved. If this register is written, set these bits to 0x008. + - bits: '11:9' + name: VCO_SEL + mode: RW + dflt: 0x5 + desc: User specified start VCO for multiplier PLL. When FORCE_VCO = 0x0, multiplier calibration starts from the VCO set by this field. When FORCE_VCO = 0x1, this field sets the VCO core used by the multiplier. Not required for Multiplier Mode programming, but can optionally be used to reduce calibration time. + - bits: 12 + name: FORCE_VCO + mode: RW + dflt: 0x0 + desc: Forces the multiplier PLL's VCO to the value selected by VCO_SEL. Not required for Multiplier Mode programming, but can optionally be used to reduce calibration time. + - bits: '15:13' + name: R28_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x1D + name: R29 + fields: + - bits: '7:0' + name: CAPCTRL + mode: RW + dflt: 0xFF + desc: Sets the starting value for the VCO tuning capacitance during multiplier calibration. Not required for Multiplier Mode programming, but can optionally be used to reduce calibration time. + - bits: '12:8' + name: R29_RESERVED_1 + mode: RW + dflt: 0x5 + desc: Reserved. If this register is written, set these bits to 0x05. + - bits: '15:13' + name: R29_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x21 + name: R33 + fields: + - bits: '15:0' + name: R33_RESERVED_0 + mode: RW + dflt: 0x7777 + desc: Reserved. If the Multiplier Mode is used, set to 0x5666 before calibration. Otherwise, writing this register can be skipped. + - addr: 0x22 + name: R34 + fields: + - bits: '13:0' + name: R34_RESERVED_1 + mode: RW + dflt: 0x0000 + desc: Reserved. If the Multiplier Mode is used, set to 0x04C5 before calibration. Otherwise, writing this register can be skipped. + - bits: '15:14' + name: R34_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x41 + name: R65 + fields: + - bits: '3:0' + name: R65_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Since this register is only used for readback, avoid writing these bits when possible. If this register must be written, set these bits to 0x0. + - bits: '8:4' + name: RB_VCO_SEL + mode: R + dflt: 0x1F + desc: Readback multiplier PLL's VCO core selection. Can be optionally used in conjunction with VCO_SEL and FORCE_VCO fields to improve calibration time. + opts: + 0xF: VCO5 + 0x17: VCO4 + 0x1B: VCO3 + 0x1D: VCO2 + 0x1E: VCO1 + - bits: '15:9' + name: R65_RESERVED_0 + mode: RW + dflt: 0x22 + desc: Since this register is only used for readback, avoid writing these bits when possible. If this register must be written, set these bits to 0x22. Readback can differ from default and written values. + - addr: 0x43 + name: R67 + fields: + - bits: '15:0' + name: R67_RESERVED_0 + mode: RW + dflt: 0x50C8 + desc: Reserved. If the Multiplier Mode is used, set to 0x51CB before calibration. Otherwise, writing this register can be skipped. + - addr: 0x48 + name: R72 + fields: + - bits: '1:0' + name: SYSREF_DELAY_BYPASS + mode: RW + dflt: 0x0 + desc: 'Option to bypass delay generator retiming. Under normal circumstances (SYSREF_DELAY_BYPASS = 0) the delay generator is engaged for continuous or pulser modes (Generator Modes), and bypassed in Repeater Mode. Generally this configuration is desirable: the delay generators rely on a signal generated by the SYSREF_DELAY_DIV from the CLKIN frequency, so the Generator Mode SYSREF signal is always well-aligned to the delay generator; in repeater mode, external signal sources can typically utilize a different delay mechanism. In certain cases, bypassing the delay generator retiming in Generator Mode by setting SYSREF_DELAY_BYPASS = 0x1 can substantially reduce the device current consumption if the SYSREF delay can be compensated at the JESD receiver. In other cases, retiming the SYSREFREQ signal to the delay generators by setting SYSREF_DELAY_BYPASS = 0x2 can improve the accuracy of the SYSREF output phase with respect to the CLKIN phase, or can vary the delay of individual outputs + independently, as long as coherent phase relationship exists between the interpolator divider phase and the SYSREFREQ phase.' + opts: + 0x0: Engage in Generator Mode, Bypass in Repeater Mode + 0x1: Bypass in All Modes + 0x2: Engage in All Modes + - bits: 2 + name: SYSREFREQ_SPI + mode: RW + dflt: 0x0 + desc: Trigger SYSREFREQ via SPI. Setting this bit emulates the behavior of a logic HIGH at SYSREFREQ pins. External signals on SYSREFREQ pins are ignored while this bit is set. + - bits: 3 + name: PULSER_LATCH + mode: RW + dflt: 0x0 + desc: Latches the pulser input when programmed to 0x1. When this bit is set, external signals on SYSREFREQ pins in pulser mode (SYSREF_MODE = 0x1) can not trigger the pulser more than once, until this bit is cleared. This bit is provided to enable changing SYSREF_MODE in repeater mode without risk of accidentally triggering the pulser. + - bits: '14:4' + name: R72_RESERVED_1 + mode: RW + dflt: 0x000 + desc: Reserved. Set to 0x000. + - bits: 15 + name: R72_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x4B + name: R75 + fields: + - bits: '3:0' + name: R75_RESERVED_2 + mode: RW + dflt: 0x6 + desc: Reserved. Since this register is only used for readback, avoid writing these bits when possible. If this register must be written, set to 0x6. + - bits: '7:4' + name: R75_RESERVED_1 + mode: R + dflt: 0x1 + desc: Read-only. Writes to these bits are ignored. Readback can differ from default values. + - bits: '9:8' + name: RB_LD + mode: R + dflt: 0x3 + desc: Multiplier PLL Lock Detect. Read-only. Field value has no meaning if device is not in Multiplier Mode. + opts: + 0x0: Unlocked (VTUNE low) + 0x2: Locked + 0x3: Unlocked (VTUNE high) + - bits: '15:10' + name: R75_RESERVED_0 + mode: R + dflt: 0x57 + desc: Read-only. Writes to these bits are ignored. Readback can differ from default values. + - addr: 0x4F + name: R79 + fields: + - bits: '14:0' + name: R79_RESERVED_1 + mode: RW + dflt: 0x0104 + desc: Reserved. Set to 0x0104 immediately after setting LOGICLK_DIV_BYPASS = 0x1; R90 must also be written immediately afterward. If LOGICLK_DIV_BYPASS is not used or set to 0x0, this register does not need to be written and can be skipped. See also R90 Register. + - bits: 15 + name: R79_RESERVED_0 + mode: R + dflt: 0x0 + desc: Reserved (not used). + - addr: 0x56 + name: R86 + fields: + - bits: '15:0' + name: R86_RESERVED_0 + mode: RW + dflt: 0x0000 + desc: Reserved. This register must be set to 0x0004 to allow MUXOUT_EN to tri-state the MUXOUT pin after SPI readback. If SPI readback is not required, or if tri-state is not required on the MUXOUT pin, writing this register can be skipped, forcing MUXOUT_EN to 0x1 (push-pull mode). + - addr: 0x5A + name: R90 + fields: + - bits: '15:0' + name: R90_RESERVED_1 + mode: RW + dflt: 0x00 + desc: Reserved. Set to 0x60 immediately after setting LOGICLK_DIV_BYPASS = 0x1 and setting R79 = 0x0104. If LOGICLK_DIV_BYPASS is not used or left at the default value, this register does not need to be written and can be skipped. However, if transitioning from LOGICLK_DIV_BYPASS = 0x1 to 0x0, this register must be re-written to 0x00. See also R79 Register. + - bits: '15:8' + name: R90_RESERVED_0 + mode: R + dflt: 0x00 + desc: Reserved (not used). diff --git a/src/lib/hw/lmx1205/lmx1205.c b/src/lib/hw/lmx1205/lmx1205.c new file mode 100644 index 00000000..0984c73a --- /dev/null +++ b/src/lib/hw/lmx1205/lmx1205.c @@ -0,0 +1,6 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "def_lmx1205.h" +#include "lmx1205.h" +#include "usdr_logging.h" diff --git a/src/lib/hw/lmx1205/lmx1205.h b/src/lib/hw/lmx1205/lmx1205.h new file mode 100644 index 00000000..bc15ceb9 --- /dev/null +++ b/src/lib/hw/lmx1205/lmx1205.h @@ -0,0 +1,7 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef LMX1205_H +#define LMX1205_H + +#endif // LMX1205_H diff --git a/src/lib/hw/lmx1205/lmx1205.yaml b/src/lib/hw/lmx1205/lmx1205.yaml new file mode 100644 index 00000000..06ce0ad8 --- /dev/null +++ b/src/lib/hw/lmx1205/lmx1205.yaml @@ -0,0 +1,1175 @@ +name: LMX1205 +revision: 0.0.1 +processors: [ c ] +bus: + type: SPI + wr_mask: 0x80000000 + usdr_path: /debug/hw/lmx1205/*/reg +addr_width: 16 +data_width: 16 + +pages: +- name: Main + regs: + - addr: 0x0 + name: R0 + fields: + - bits: 0 + name: RESET + mode: RW + dflt: 0x0 + desc: Soft Reset. Resets the entirie logic and reigsters (equivalent to power-on reset). Self-clearing on next register write. + - bits: 1 + name: POWERDOWN + mode: RW + dflt: 0x0 + desc: Sets the device in a low-power state. The states of other registers are maintained. + - bits: '15:2' + name: UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x1 + name: R1 + fields: + - bits: '2:0' + name: R1_UNDISCLOSED_0 + mode: RW + dflt: 0x2 + desc: Program this field to 0x2. + - bits: 3 + name: READBACK_CTRL + mode: RW + dflt: 0x1 + desc: Set this field to 0x1 to readback the written register values. Set this field to 0x0 to readback the value set by device internal state machine. + - bits: 4 + name: LD_DIS + mode: RW + dflt: 0x0 + desc: If set to 0x1, disables the lock detect status coming out at MUXOUT pin in multiplier mode. This bit must be set to 1, when interfacing multiple devices and wants to perform a readback operation in multiplier mode. + opts: + 0b0: Lock Detect + 0b1: Readback + - bits: '15:5' + name: R1_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x2 + name: R2 + fields: + - bits: 0 + name: CH0_EN + mode: RW + dflt: 0x1 + desc: Enables CH0 (CLKOUT0, SYSREFOUT0). Setting this bit to 0 completely disables CH0, overriding the state of other powerdown/ enable bits. + - bits: 1 + name: CH1_EN + mode: RW + dflt: 0x1 + desc: Enables CH1 (CLKOUT1, SYSREFOUT1). Setting this bit to 0 completely disables CH1, overriding the state of other powerdown/ enable bits. + - bits: 2 + name: CH2_EN + mode: RW + dflt: 0x1 + desc: Enables CH2 (CLKOUT2, SYSREFOUT2). Setting this bit to 0 completely disables CH2, overriding the state of other powerdown/ enable bits. + - bits: 3 + name: CH3_EN + mode: RW + dflt: 0x1 + desc: Enables CH3 (CLKOUT3, SYSREFOUT3). Setting this bit to 0 completely disables CH3, overriding the state of other powerdown/ enable bits. + - bits: 4 + name: LOGIC_EN + mode: RW + dflt: 0x1 + desc: Enables LOGICLK subsystem (LOGICLKOUT, LOGISYSREFOUT). Setting this bit to 0x0 completely disables all LOGICLKOUT and LOGISYSREFOUT circuitry, overriding the state of other powerdown/ enable bits. + - bits: 5 + name: R2_UNDISCLOSED_1 + mode: RW + dflt: 0x1 + desc: Program this field to 0x1. + - bits: 6 + name: SYSREF_EN + mode: RW + dflt: 0x0 + desc: Enables SYSREF subsystem (and SYNC subsystem when SYSREFREQ_MODE = 0x0). Setting this bit to 0x0 completely disables all SYNC, SYSREF, and clock position capture circuitry, overriding the state of other powerdown/enable bits except SYNC_EN. If SYNC_EN = 0x1, the SYNC path and clock position capture circuitry are still enabled, regardless of the state of SYSREF_EN. + - bits: 7 + name: R2_UNDISCLOSED_0 + mode: RW + dflt: 0x1 + desc: Program this field to 0x1. + - bits: 8 + name: SYNC_EN + mode: RW + dflt: 0x0 + desc: Enables synchronization path for the dividers and allows the clock position capture circuitry to be enabled. Used for multi-device synchronization. Redundant if SYSREF_EN = 0x1. + - bits: 9 + name: TEMPSENSE_EN + mode: RW + dflt: 0x0 + desc: Temperature sensor enable override bit + - bits: '15:10' + name: R2_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x3 + name: R3 + fields: + - bits: '6:0' + name: CLKIN_DLY + mode: RW + dflt: 0x0 + desc: Sets the delay at input clock. Delay range - 60ps and step size - 1.1ps + - bits: '15:7' + name: R3_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x4 + name: R4 + fields: + - bits: 0 + name: CLK0_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT0 output buffer. + - bits: '3:1' + name: CLK0_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT0. Larger values correspond to higher output power. + - bits: '10:4' + name: CLK0_DLY + mode: RW + dflt: 0x0 + desc: Sets the delay at CLKOUT0 output clock. Delay range - 55ps and step size - 0.9ps + - bits: '15:11' + name: R4_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x5 + name: R5 + fields: + - bits: 0 + name: CLK1_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT1 output buffer. + - bits: '3:1' + name: CLK1_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT1. Larger values correspond to higher output power. + - bits: '10:4' + name: CLK1_DLY + mode: RW + dflt: 0x0 + desc: Sets the delay at CLKOUT1 output clock. Delay range - 55ps and step size - 0.9ps + - bits: '15:11' + name: R5_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x6 + name: R6 + fields: + - bits: 0 + name: CLK2_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT2 output buffer. + - bits: '3:1' + name: CLK2_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT2. Larger values correspond to higher output power. + - bits: '10:4' + name: CLK2_DLY + mode: RW + dflt: 0x0 + desc: Sets the delay at CLKOUT2 output clock. Delay range - 55ps and step size - 0.9ps + - bits: '15:11' + name: R6_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x7 + name: R7 + fields: + - bits: 0 + name: CLK3_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT3 output buffer. + - bits: '3:1' + name: CLK3_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT3. Larger values correspond to higher output power. + - bits: '10:4' + name: CLK3_DLY + mode: RW + dflt: 0x0 + desc: Sets the delay at CLKOUT3 output clock. Delay range - 55ps and step size - 0.9ps + - bits: '15:11' + name: R7_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x8 + name: R8 + fields: + - bits: 0 + name: SYSREF0_EN + mode: RW + dflt: 0x1 + desc: Enables SYSREFOUT0 output buffer. + - bits: '3:1' + name: SYSREF0_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT0. Larger values corespond to higher output power. SYSREFOUT0_VCM must be set properly bring the output common mode voltage within permissible limits. + - bits: '9:4' + name: SYSREF0_VCM + mode: RW + dflt: 0xA + desc: Sets the output common mode of SYSREFOUT0 with 25mV step size. SYSREF0_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. + - bits: '12:10' + name: R8_UNDISCLOSED_0 + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - bits: 13 + name: SYSREF0_AC + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT0 AC coupled mode. + - bits: 14 + name: SYSREF0_PWR_LOW + mode: RW + dflt: 0x1 + desc: Sets the SYSREFOUT0 output deriver at low power. Set to value 0 for single ended higher swing. + - bits: 15 + name: R8_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x9 + name: R9 + fields: + - bits: 0 + name: SYSREF1_EN + mode: RW + dflt: 0x1 + desc: Enables SYSREFOUT1 output buffer. + - bits: '3:1' + name: SYSREF1_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT1. Larger values corespond to higher output power. SYSREFOUT1_VCM must be set properly bring the output common mode voltage within permissible limits. + - bits: '9:4' + name: SYSREF1_VCM + mode: RW + dflt: 0xA + desc: Sets the output common mode of SYSREFOUT1 with 25mV step size. SYSREF1_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. + - bits: '12:10' + name: R9_UNDISCLOSED_0 + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - bits: 13 + name: SYSREF1_AC + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT1 AC coupled mode. + - bits: 14 + name: SYSREF1_PWR_LOW + mode: RW + dflt: 0x1 + desc: Sets the SYSREFOUT1 output deriver at low power. Set to value 0 for single ended higher swing. + - bits: 15 + name: R9_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0xA + name: R10 + fields: + - bits: 0 + name: SYSREF2_EN + mode: RW + dflt: 0x1 + desc: Enables SYSREFOUT2 output buffer. + - bits: '3:1' + name: SYSREF2_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT2. Larger values corespond to higher output power. SYSREFOUT2_VCM must be set properly to bring the output common mode voltage within permissible limits. + - bits: '9:4' + name: SYSREF2_VCM + mode: RW + dflt: 0xA + desc: Sets the output common mode of SYSREFOUT2 with 25mV step size. SYSREF2_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. + - bits: '12:10' + name: R10_UNDISCLOSED_0 + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - bits: 13 + name: SYSREF2_AC + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT2 AC coupled mode. + - bits: 14 + name: SYSREF2_PWR_LOW + mode: RW + dflt: 0x1 + desc: Sets the SYSREFOUT2 output deriver at low power. Set to value for single ended higher swing. + - bits: 15 + name: R10_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0xB + name: R11 + fields: + - bits: 0 + name: SYSREF3_EN + mode: RW + dflt: 0x1 + desc: Enables SYSREFOUT3 output buffer. + - bits: '3:1' + name: SYSREF3_PWR + mode: RW + dflt: 0x4 + desc: Sets the output power of SYSREFOUT3. Larger values corespond to higher output power. SYSREFOUT3_VCM must be set properly bring the output common mode voltage within permissible limits. + - bits: '9:4' + name: SYSREF3_VCM + mode: RW + dflt: 0xA + desc: Sets the output common mode of SYSREFOUT3 with 25mV step size. SYSREF3_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. + - bits: '12:10' + name: R11_UNDISCLOSED_0 + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - bits: 13 + name: SYSREF3_AC + mode: RW + dflt: 0x0 + desc: Enables SYSREFOUT3 AC coupled mode. + - bits: 14 + name: SYSREF3_PWR_LOW + mode: RW + dflt: 0x1 + desc: Sets the SYSREFOUT3 output deriver at low power. Set to value 0 for single ended higher swing. + - bits: 15 + name: R11_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0xC + name: R12 + fields: + - bits: 0 + name: LOGICLK_EN + mode: RW + dflt: 0x1 + desc: Enables the logic clock output buffer. + - bits: '3:1' + name: LOGICLK_PWR + mode: RW + dflt: 0x5 + desc: Sets the output power of LOGICLKOUT. Larger values correspond to higher output power. + - bits: '8:4' + name: LOGICLK_VCM + mode: RW + dflt: 0x2 + desc: Sets the output common mode voltage of LOGICLKOUT in LVDS output format. LOGICLK_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. + - bits: '10:9' + name: R12_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '12:11' + name: LOGICLK_FMT + mode: RW + dflt: 0x0 + desc: Selects the output driver format of the LOGICLKOUT output. + opts: + 0x0: LVDS + 0x2: CML + - bits: '15:13' + name: R12_UNDISC + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0xD + name: R13 + fields: + - bits: 0 + name: LOGISYSREF_EN + mode: RW + dflt: 0x1 + desc: Enables the logic SYSREF output buffer. + - bits: '3:1' + name: LOGISYSREF_PWR + mode: RW + dflt: 0x5 + desc: Sets the output power of LOGISYSREFOUT. Larger values correspond to higher output power. + - bits: '8:4' + name: LOGISYSREF_VCM + mode: RW + dflt: 0x2 + desc: Sets the output common mode voltage of LOGISYSREFOUT in LVDS output format. LOGISYSREF_PWR must be set properly to bring the minimum and maximum output voltage within permissible limits. + - bits: '10:9' + name: R13_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '12:11' + name: LOGISYSREF_FMT + mode: RW + dflt: 0x0 + desc: Selects the output driver format of the LOGISYSREFOUT output. + opts: + 0x0: LVDS + 0x2: CML + - bits: '15:13' + name: R13_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0xE + name: R14 + fields: + - bits: '2:0' + name: LOGICLK_DIV_PRE + mode: RW + dflt: 0x4 + desc: "Sets pre-divider value for logic clock divider. Output of the pre-divider must be <= 3.2GHz. Values other than those listed below are reserved." + opts: + 0x1: "/1" + 0x2: "/2" + 0x4: "/4" + - bits: '12:3' + name: LOGICLK_DIV + mode: RW + dflt: 0x10 + desc: "Sets LOGICLK divider value. Maximum input frequency from LOGICLK_DIV_PRE must be <= 3200MHz. The maximum LOGICLKOUT frequency must be <= 800MHz to avoid amplitude degradation." + opts: + 0x2: "/2" + 0x3: "/3" + 0x3ff: "/1023" + - bits: '14:13' + name: R14_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 15 + name: LOGICLK_DIV_RST + mode: RW + dflt: 0x0 + desc: Manual reset for logic clock divider. + - addr: 0xF + name: R15 + fields: + - bits: 0 + name: LOGICLK2_EN + mode: RW + dflt: 0x0 + desc: Enables the LOGICLKOUT1 + opts: + 0b0: LOGISYSREFOUT + 0b1: LOGICLKOUT1 + - bits: '2:1' + name: LOGICLK2_DIV + mode: RW + dflt: 0x1 + desc: Sets the divider value for LOGICLKOUT1 logic clock. + - bits: '15:3' + name: R15_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x10 + name: R16 + fields: + - bits: '1:0' + name: SYSREFREQ_VCM + mode: RW + dflt: 0x0 + desc: Sets the SYSREFREQ input pins common mode voltage + opts: + 0x0: Zero offset (AC coupled) + 0x1: Pin P biased higher than pin N (AC coupled) + 0x2: Pin N higher than pin P (AC coupled) + 0x3: No Bias (DC coupled) + - bits: '3:2' + name: SYSREFREQ_VCM_OFFSET + mode: RW + dflt: 0x0 + desc: Sets the voltage offset at SYSREFREQ P vs N + opts: + 0x0: 25mV + 0x1: 50mV + 0x2: 100mV + 0x3: 150mV + - bits: '5:4' + name: SYSREFREQ_DLY_STEP + mode: RW + dflt: 0x3 + desc: Sets the step size of the delay element used in the SYSREFREQ path, both for SYSREFREQ input delay and for clock position captures. The recommended frequency range for each step size creates the maximum number of usable steps for a given CLKIN frequency. The ranges include some overlap to account for process and temperature variations. If the CLKIN frequency is covered by an overlapping span, larger delay step sizes improve the likelihood of detecting a CLKIN rising edge during a clock position capture. However, since larger values include more delay steps, larger step sizes have greater total delay variation across PVT relative to smaller step sizes. + opts: + 0x0: 28ps (1.4GHz to 2.7GHz) + 0x1: 15ps ( 2.4GHz to 4.7GHz) + 0x2: 11ps (3.1GHz to 5.7GHz) + 0x3: 8ps (4.5GHz to 12.8GHz) + - bits: '7:6' + name: SYSREF_DLY_SCALE + mode: RW + dflt: 0x0 + desc: Sets the frequency range of the SYSREFOUT delay generator. Set according to phase interpolator frequency. + opts: + 0x0: 400MHz to 800MHz + 0x1: 200MHz to 400MHz + 0x2: 150MHz to 200MHz + - bits: '15:8' + name: R16_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x11 + name: R17 + fields: + - bits: '1:0' + name: SYSREFREQ_MODE + mode: RW + dflt: 0x1 + desc: Sets the SYSREFREQ input mode function + opts: + 0x0: SYNC + 0x1: SYSREFREQ + 0x2: SYSREF Windowing + - bits: 2 + name: SYSREFREQ_CLR + mode: RW + dflt: 0x1 + desc: Reset synchronization path timing for SYSREFREQ signal. Holding this bit high keeps internal SYSREFREQ signal low in all modes except SYSREF repeater mode, overriding the state of SYSREFREQ_INPUT[0]. This bit must be set and cleared once before the SYNC or clock position capture operations are performed. + - bits: 3 + name: SYSWND_LATCH + mode: RW + dflt: 0x0 + desc: Sets the SYSREF Windowing at first rising edge of the SYNC input + - bits: 4 + name: SYNC_STOP + mode: RW + dflt: 0x0 + desc: Stops the reset generation after setting bit to high. + - bits: 5 + name: SYSWND_UPDATE_STOP + mode: RW + dflt: 0x0 + desc: Stops the windowing after setting bit to high. + - bits: '7:6' + name: SYSREFREQ_INPUT + mode: RW + dflt: 0x0 + desc: Sets the functionality of the SYSREFREQ block + opts: + 0x0: SYSREFREQ Pin + 0x1: Force Low + 0x3: Force High + - bits: '11:8' + name: R17_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '15:12' + name: R17_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x12 + name: R18 + fields: + - bits: '5:0' + name: SYSREFREQ_DLY + mode: RW + dflt: 0x0 + desc: Sets the delay line step for the external SYSREFREQ signal. Each delay line step delays the SYSREFREQ signal by an amount equal to SYSREFREQ_DLY x SYSREFREQ_DLY_STEP. In SYNC mode, the value for this field can be determined based on the rb_CLKPOS value to satisfy the internal setup and hold time of the SYNC signal with respect to the CLKIN signal. In SYSREF Repeater Mode, the value for this field can be used as a coarse global delay. Values greater than 0x3F are invalid. Since larger values include more delay steps, larger values have greater total step size variation across PVT relative to smaller values. Refer to the data sheet or the device TICS Pro profile for detailed description of the delay step computation procedure. + - bits: '15:6' + name: R18_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x13 + name: R19 + fields: + - bits: '1:0' + name: SYSREF_MODE + mode: RW + dflt: 0x0 + desc: Controls how the SYSREF signal is generated and is also impacted by the SYSREF_DLY_BYP field. Continuous mode generates a continuous SYSREF clock that is derived from the SYSREF divider and delay. In pulser mode, a pulse at the SYSREFREQ pin causes a specific number (determined by SYSREF_PULSE_CNT) of pulses to be generated for the SYSREF outputs. In Repeater mode, a pulse at the SYSREFREQ pins generates a single pulse at the SYSREF outputs and only the propagation delay through the device is added. + opts: + 0x0: Continuous + 0x1: Pulser + 0x2: Repeater + 0x3: Repeater Retime + - bits: '5:2' + name: SYSREF_PULSE_CNT + mode: RW + dflt: 0x1 + desc: Programs the number of pulses generated in pulser mode. The pulser is a counter gating the SYSREF divider; consequently, the pulse duration and frequency are equal to the duty cycle and frequency of the SYSREF divider output, respectively. + opts: + 0x1: 1 pulse + 0x2: 2 pulses + 0xF: 15 pulses + - bits: 6 + name: SYSREF_DLY_BYP + mode: RW + dflt: 0x0 + desc: Sets the SYSREF delay bypass + - bits: '15:7' + name: R19_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x14 + name: R20 + fields: + - bits: '1:0' + name: SYSREF_DIV_PRE + mode: RW + dflt: 0x2 + desc: "Sets the SYSREF pre-divider. Maximum output frequency must be <= 3.2GHz." + opts: + 0x0: "/1" + 0x1: "/2" + 0x2: "/4" + - bits: '13:2' + name: SYSREF_DIV + mode: RW + dflt: 0x20 + desc: "Sets the SYSREF divider. Maximum input frequency from SYSREF_DIV_PRE must be <= 3200MHz. Maximum output frequency must be <= 100MHz. Odd divides (with duty cycle < 50%) are only allowed when the delay generators are bypassed." + opts: + 0x2: /2 + 0x3: /3 + 0xFFF: /4095 + - bits: '15:14' + name: SYSREF_DLY_DIV + mode: RW + dflt: 0x2 + desc: "Sets the delay generator clock division, determining fINTERPOLATOR and the delay generator resolution." + opts: + 0x0: /2 (<= 1.6GHz) + 0x1: /4 (1.6GHz to 3.2GHz) + 0x2: /8 (3.2GHz to 6.4GHz) + 0x3: /16 (6.4GHz to 12.8GHz) + - addr: 0x15 + name: R21 + fields: + - bits: '1:0' + name: SYSREF0_DLY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT0 delay generator retimer. + opts: + 0x0: ICLK' + 0x1: QCLK' + 0x2: ICLK + 0x3: QCLK + - bits: '8:2' + name: SYSREF0_DLY + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT0 delay generator. In each quadrant, delay has 127 steps. + - bits: '15:9' + name: R21_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x16 + name: R22 + fields: + - bits: '1:0' + name: SYSREF1_DLY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT1 delay generator retimer. + opts: + 0x0: ICLK' + 0x1: QCLK' + 0x2: QCLK + 0x3: ICLK + - bits: '8:2' + name: SYSREF1_DLY + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT1 delay generator. In each quadrant, delay has 127 steps. + - bits: '15:9' + name: R22_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x17 + name: R23 + fields: + - bits: '1:0' + name: SYSREF2_DLY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT2 delay generator retimer. + opts: + 0x0: ICLK' + 0x1: QCLK' + 0x2: QCLK + 0x3: ICLK + - bits: '8:2' + name: SYSREF2_DLY + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT2 delay generator. In each quadrant, delay has 127 steps. + - bits: '15:9' + name: R23_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x18 + name: R24 + fields: + - bits: '1:0' + name: SYSREF3_DLY_PHASE + mode: RW + dflt: 0x0 + desc: Sets the quadrature phase of the interpolator clock used for the SYSREFOUT3 delay generator retimer. + opts: + 0x0: ICLK' + 0x1: QCLK' + 0x2: QCLK + 0x3: ICLK + - bits: '8:2' + name: SYSREF3_DLY + mode: RW + dflt: 0x7F + desc: Sets the delay step for the SYSREFOUT3 delay generator. In each quadrant, delay has 127 steps. + - bits: '15:9' + name: R24_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x19 + name: R25 + fields: + - bits: '8:2' + name: LOGISYSREF_DLY + mode: RW + dflt: 0x7F + desc: Sets the delay step for the LOGISYSREF delay generator. In each quadrant, delay has 127 steps. 1-0 LOGISYSREF_DLY_PHA SE R/W 0h Sets the quadrature phase of the interpolator clock used for the LOGISYSREFOUT delay generator retimer. + opts: + 0x0: ICLK' + 0x1: QCLK' + 0x2: QCLK + 0x3: ICLK + - bits: '15:9' + name: R25_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x1A + name: R26 + fields: + - bits: 0 + name: SMCLK_EN + mode: RW + dflt: 0x1 + desc: Enables the state machine clock generator. Only required to calibrate the multiplier, and for multiplier lock detect (including on MUXOUT pin). If the multiplier is not used, or if the multiplier lock detect feature is not used, the state machine clock generator can be disabled to minimize crosstalk. + - bits: '4:1' + name: SMCLK_DIV_PRE + mode: RW + dflt: 0x8 + desc: "Pre-divider for State Machine clock (one hot divider).The state machine clock is divided from the input clock. The output of the pre-divider must be <=1600MHz. Values other than those listed are reserved." + opts: + 0x2: /2 + 0x4: /4 + 0x8: /8 + - bits: '7:5' + name: SMCLK_DIV + mode: RW + dflt: 0x6 + desc: "Sets state machine clock divider. Further divides the output of the state machine clock pre-divider. Input frequency from SMCLK_DIV_PRE must be <= 1600MHz. Output frequency must be <= 30MHz. Divide value is 2SMCLK_DIV." + opts: + 0x0: "/1" + 0x1: "/2" + 0x2: "/4" + 0x3: "/8" + 0x4: "/16" + 0x5: "/32" + 0x6: "/64" + 0x7: "/128" + - bits: '15:8' + name: R26_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x1B + name: R27 + fields: + - bits: '2:0' + name: CLK_MUX + mode: RW + dflt: 0x1 + desc: Selects the function for the main clock outputs + opts: + 0x0: Reserved + 0x1: Buffer + 0x2: Dividers + 0x3: Multiplier + - bits: '5:3' + name: CLK_DIV + mode: RW + dflt: 0x1 + desc: CLK_DIV and CLK_MULT are aliases for the same field. When CLK_MUX=1 (Buffer Mode), this field is ignored. When CLK_MUX = 2 (Divider Mode), the clock divider is CLK_DIV + 1. Valid range for CLK_DIV is 1 to 7. Setting this to 0 disables the main clock divider and reverts to buffer mode. When CLK_MUX = 3 (Multiplier Mode), CLK_MULT the multiplier vaue is CLK_MULT. Valid range is 1 to 7. + - bits: 6 + name: CLK_DIV_RST + mode: RW + dflt: 0x0 + desc: Resets the main clock divider. If the clock divider value is changed during operation, set this bit high then low after setting the new divider value. Synchronizing the device with the SYSREFREQ pins in SYSREFREQ_MODE = 0x0 and SYNC_EN = 0x1 also resets the main clock divider. This bit has no effect when outside of Divider Mode. + - bits: '8:7' + name: R27_UNDISCLOSED_1 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 9 + name: FCAL_EN + mode: RW + dflt: 0x1 + desc: Enables Frequency calibration. Writing this register with this bit high triggers a multiplier frequency calibration. If the multiplier is unused, set to 0. + - bits: 10 + name: R27_UNDISCLOSED_0 + mode: RW + dflt: 0x1 + desc: Program this field to 0x1. + - bits: 11 + name: MULT_HIPFD_EN + mode: RW + dflt: 0x0 + desc: Above 4.2GHz frequency in multiplier mode, to optimized the current, toggle this bit low to high along with R0. To set the bit high without R0, increase a current with 20mA. + - bits: '15:12' + name: R27_UNDISCLOSED + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - addr: 0x1D + name: R29 + fields: + - bits: '15:0' + name: RB_CLKPOS_U + mode: R + dflt: 0x0 + desc: Stores a snapshot of the CLKIN signal rising edge positions relative to a SYSREFREQ rising edge, with the snapshot starting from the LSB and ending at the MSB. Each bit represents a sample of the CLKIN signal, separated by a delay determined by the SYSREFREQ_DLY_STEP field. The first and last bits of rb_CLKPOS are always set, indicating uncertainty at the capture window boundary conditions. CLKIN rising edges are represented by every sequence of two set bits from LSB to MSB, including bits at the boundary conditions. The position of the CLKIN rising edges in the snapshot, along with the CLKIN signal period and the delay step size, can be used to compute the value of SYSREFREQ_DLY_STEP which maximizes setup and hold times for SYNC signals on the SYSREFREQ pins. + - addr: 0x1E + name: R30 + fields: + - bits: '15:0' + name: RB_CLKPOS_L + mode: R + dflt: 0x0 + desc: LSBs of rb_CLKPOS field. + - addr: 0x1F + name: R31 + fields: + - bits: '10:0' + name: RB_TEMPSENSE + mode: R + dflt: 0x0 + desc: Readback value of on-die temperature sensor. + - bits: '13:11' + name: R31_UNDISCLOSED_0 + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '15:14' + name: R31_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x20 + name: R32 + fields: + - bits: '15:0' + name: RB_VER_ID + mode: R + dflt: 0x0 + desc: Version ID. + - addr: 0x24 + name: R36 + fields: + - bits: '5:0' + name: R36_UNDISCLOSED_2 + mode: RW + dflt: 0x23 + desc: Program this field to 0x16. + - bits: '7:6' + name: R36_UNDISCLOSED_1 + mode: RW + dflt: 0x2 + desc: Program this field to 0x0. + - bits: '9:8' + name: R36_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x3. + - bits: '15:10' + name: R36_UNDISCLOSED + mode: RW + dflt: 0x21 + desc: Program this field to 0x42. + - addr: 0x25 + name: R37 + fields: + - bits: 0 + name: RB_LOCK_DETECT + mode: R + dflt: 0x0 + desc: Reads back the lock detect status in multiplier mode + opts: + 0b0: Unlock + 0b1: Lock Detect + - bits: '14:1' + name: R37_UNDISCLOSED_0 + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 15 + name: R37_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x27 + name: R39 + fields: + - bits: '3:0' + name: R39_UNDISCLOSED_2 + mode: RW + dflt: 0x1 + desc: Program this field to 0x1. + - bits: '8:4' + name: R39_UNDISCLOSED_1 + mode: RW + dflt: 0xE + desc: Program this field to 0x16. + - bits: '11:9' + name: R39_UNDISCLOSED_0 + mode: RW + dflt: 0x4 + desc: Program this field to 0x4. + - bits: '15:12' + name: R39_UNDISCLOSED + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - addr: 0x28 + name: R40 + fields: + - bits: '3:0' + name: R40_UNDISCLOSED_2 + mode: RW + dflt: 0x1 + desc: Program this field to 0x3. + - bits: '8:4' + name: R40_UNDISCLOSED_1 + mode: RW + dflt: 0xE + desc: Program this field to 0x16. + - bits: '11:9' + name: R40_UNDISCLOSED_0 + mode: RW + dflt: 0x4 + desc: Program this field to 0x4. + - bits: '15:12' + name: R40_UNDISCLOSED + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - addr: 0x29 + name: R41 + fields: + - bits: '3:0' + name: R41_UNDISCLOSED_2 + mode: RW + dflt: 0x3 + desc: Program this field to 0x1. + - bits: '8:4' + name: R41_UNDISCLOSED_1 + mode: RW + dflt: 0xF + desc: Program this field to 0x14. + - bits: '11:9' + name: R41_UNDISCLOSED_0 + mode: RW + dflt: 0x4 + desc: Program this field to 0x2. + - bits: '15:12' + name: R41_UNDISCLOSED + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - addr: 0x2A + name: R42 + fields: + - bits: '3:0' + name: R42_UNDISCLOSED_2 + mode: RW + dflt: 0x3 + desc: Program this field to 0x1. + - bits: '8:4' + name: R42_UNDISCLOSED_1 + mode: RW + dflt: 0xF + desc: Program this field to 0x14. + - bits: '11:9' + name: R42_UNDISCLOSED_0 + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - bits: '15:12' + name: R42_UNDISCLOSED + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - addr: 0x2B + name: R43 + fields: + - bits: '3:0' + name: R43_UNDISCLOSED_2 + mode: RW + dflt: 0x7 + desc: Program this field to 0x1. + - bits: '8:4' + name: R43_UNDISCLOSED_1 + mode: RW + dflt: 0x10 + desc: Program this field to 0x14. + - bits: '11:9' + name: R43_UNDISCLOSED_0 + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - bits: '15:12' + name: R43_UNDISCLOSED + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - addr: 0x2C + name: R44 + fields: + - bits: '3:0' + name: R44_UNDISCLOSED_2 + mode: RW + dflt: 0x7 + desc: Program this field to 0x1. + - bits: '8:4' + name: R44_UNDISCLOSED_1 + mode: RW + dflt: 0x10 + desc: Program this field to 0x16. + - bits: '11:9' + name: R44_UNDISCLOSED_0 + mode: RW + dflt: 0x3 + desc: Program this field to 0x2. + - bits: '15:12' + name: R44_UNDISCLOSED + mode: RW + dflt: 0x7 + desc: Program this field to 0x7. + - addr: 0x2D + name: R45 + fields: + - bits: '1:0' + name: R45_UNDISCLOSED_5 + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - bits: '3:2' + name: R45_UNDISCLOSED_4 + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - bits: '5:4' + name: R45_UNDISCLOSED_3 + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - bits: '7:6' + name: R45_UNDISCLOSED_2 + mode: RW + dflt: 0x2 + desc: Program this field to 0x3. + - bits: '9:8' + name: R45_UNDISCLOSED_1 + mode: RW + dflt: 0x2 + desc: Program this field to 0x3. + - bits: '11:10' + name: R45_UNDISCLOSED_0 + mode: RW + dflt: 0x2 + desc: Program this field to 0x3. + - bits: '15:12' + name: R45_UNDISCLOSED + mode: RW + dflt: 0x2 + desc: Program this field to 0x2. + - addr: 0x36 + name: R54 + fields: + - bits: '1:0' + name: R54_UNDISCLOSED_2 + mode: RW + dflt: 0x0 + desc: Program this field to 0x2. + - bits: '3:2' + name: R54_UNDISCLOSED_1 + mode: RW + dflt: 0x0 + desc: Program this field to 0x3. + - bits: '13:4' + name: R54_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '15:14' + name: R54_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x37 + name: R55 + fields: + - bits: '5:0' + name: DEV_IOPT_CTRL + mode: RW + dflt: 0x0 + desc: Set this field to 0x6 in all modes, also in powerdown. Set this field to 0x6 before calibration in multiplier mode and changed to 0x1 after calibration + - bits: '15:6' + name: R55_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x4D + name: R77 + fields: + - bits: '1:0' + name: R77_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x2. + - bits: '15:2' + name: R77_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. diff --git a/src/lib/hw/lmx1214/lmx1214.c b/src/lib/hw/lmx1214/lmx1214.c new file mode 100644 index 00000000..5dea8159 --- /dev/null +++ b/src/lib/hw/lmx1214/lmx1214.c @@ -0,0 +1,506 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include "def_lmx1214.h" +#include "lmx1214.h" +#include "usdr_logging.h" +#include "../common/common.h" + +#define FREQ_EPS 1.0f + +enum +{ + CLKIN_MIN = 300000000ull, + CLKIN_MAX_DIV = 12800000000ull, + CLKIN_MAX_BUF = 18000000000ull, + + SYNC_IN_MIN = CLKIN_MIN, + SYNC_IN_MAX = CLKIN_MAX_DIV, + + CLKOUT_MIN_DIV = 150000000ull, + CLKOUT_MIN_BUF = CLKIN_MIN, + + CLKOUT_MAX_DIV_WO_SYNC = 8000000000ull, + CLKOUT_MAX_DIV_SYNC = 6400000000ull, + CLKOUT_MAX_BUF = CLKIN_MAX_BUF, + + AUXCLKOUT_MIN = 1000000ull, + AUXCLKOUT_MAX = 800000000ull, + AUXCLK_DIV_INP_MAX = 3200000000ull, + + CLK_DIV_MIN = 2, + CLK_DIV_MAX = 8, + + AUXCLK_DIV_MIN = 2, + AUXCLK_DIV_MAX = 1023, +}; + +static int lmx1214_spi_post(lmx1214_state_t* obj, uint32_t* regs, unsigned count) +{ + return + common_print_registers_a8d16(regs, count, USDR_LOG_DEBUG) + || + common_spi_post(obj, regs, count); +} + +static int lmx1214_spi_get(lmx1214_state_t* obj, uint16_t addr, uint16_t* out) +{ + return common_spi_get(obj, MAKE_LMX1214_REG_RD((uint32_t)addr), out); +} + +UNUSED static int lmx1214_read_all_regs(lmx1214_state_t* st) +{ + uint8_t regs[] = + { + R0, + R2, + R3, + R4, + R5, + R7, + R8, + R9, + R11, + R12, + R13, + R14, + R15, + R23, + R24, + R25, + R75, + R79, + R86, + R90, + }; + + for(unsigned i = 0; i < SIZEOF_ARRAY(regs); ++i) + { + uint16_t regval; + int res = lmx1214_spi_get(st, regs[i], ®val); + if(res) + return res; + USDR_LOG("1214", USDR_LOG_DEBUG, "READ R%02u = 0x%04x", regs[i], regval); + } + + return 0; +} + +int lmx1214_get_temperature(lmx1214_state_t* st, float* value) +{ + if(!value) + return -EINVAL; + + uint16_t r24; + + int res = lmx1214_spi_get(st, R24, &r24); + if(res) + return res; + + int16_t code = (r24 & RB_TS_MSK) >> RB_TS_OFF; + *value = 0.65f * code - 351.0f; + + USDR_LOG("1214", USDR_LOG_DEBUG, "LMX1214 temperature sensor:%.2fC", *value); + return 0; +} + +static int lmx1214_reset_main_divider(lmx1214_state_t* st, bool set_flag) +{ + uint16_t r25; + + int res = lmx1214_spi_get(st, R25, &r25); + if(res) + return res; + + uint32_t reg = MAKE_LMX1214_REG_WR(R25, set_flag ? (r25 | CLK_DIV_RST_MSK) : (r25 & ~CLK_DIV_RST_MSK)); + return lmx1214_spi_post(st, ®, 1); +} + +int lmx1214_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lmx1214_state_t* st) +{ + memset(st, 0, sizeof(*st)); + int res; + + st->dev = dev; + st->subdev = subdev; + st->lsaddr = lsaddr; + + uint32_t regs[] = + { + MAKE_LMX1214_R86(0, 0, 0), //MUXOUT_EN_OVRD=0 + MAKE_LMX1214_R79(0, 0x5), //magic R79->0x5 (see manual) + MAKE_LMX1214_R24(0, 0, 0, 1), //temp sensor + MAKE_LMX1214_R23(1, 1, 1, 1 << 6), //temp sensor + MUXOUT_EN=1(push-pull) MUXOUT=1(SDO) + }; + + res = lmx1214_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + { + USDR_LOG("1214", USDR_LOG_ERROR, "Registers set lmx1214_spi_post() failed, err:%d", res); + return res; + } + + usleep(1000); + + float tempval; + res = lmx1214_get_temperature(st, &tempval); + if(res) + { + USDR_LOG("1214", USDR_LOG_ERROR, "lmx1214_get_temperature() failed, err:%d", res); + return res; + } + + USDR_LOG("1214", USDR_LOG_DEBUG, "Create OK"); + return 0; +} + +int lmx1214_destroy(lmx1214_state_t* st) +{ + USDR_LOG("1214", USDR_LOG_DEBUG, "Destroy OK"); + return 0; +} + +static int lmx1214_solver_prevalidate(uint64_t in, uint64_t out, bool* out_en, lmx1214_auxclkout_cfg_t* aux) +{ + if(in < CLKIN_MIN || in > CLKIN_MAX_BUF) + { + USDR_LOG("1214", USDR_LOG_ERROR, "CLKIN:%" PRIu64 " out of range [%" PRIu64 ";%" PRIu64 "]", in, (uint64_t)CLKIN_MIN, (uint64_t)CLKIN_MAX_BUF); + return -EINVAL; + } + + const bool buffer_mode = (out == in); + if(!buffer_mode && in > CLKIN_MAX_DIV) + { + USDR_LOG("1214", USDR_LOG_ERROR, "CLKIN:%" PRIu64 " too high (>%" PRIu64 ") [BUFFERMODE:%u]", in, (uint64_t)CLKIN_MAX_DIV, buffer_mode); + return -EINVAL; + } + + const uint64_t out_min = (buffer_mode ? CLKOUT_MIN_BUF : CLKOUT_MIN_DIV); + const uint64_t out_max = (buffer_mode ? CLKOUT_MAX_BUF : CLKOUT_MAX_DIV_SYNC); + + if(out < out_min || out > out_max) + { + USDR_LOG("1214", USDR_LOG_ERROR, "CLKOUT:%" PRIu64 " out of range [%" PRIu64 ";%" PRIu64 "] [BUFFERMODE:%u]", out, out_min, out_max, buffer_mode); + return -EINVAL; + } + + if(aux->enable && (aux->freq < AUXCLKOUT_MIN || aux->freq > AUXCLKOUT_MAX)) + { + USDR_LOG("1214", USDR_LOG_ERROR, "AUXCLKOUT:%.4f out of range [%" PRIu64 ";%" PRIu64 "]", aux->freq, (uint64_t)AUXCLKOUT_MIN, (uint64_t)AUXCLKOUT_MAX); + return -EINVAL; + } + + switch(aux->fmt) + { + case LMX1214_FMT_LVDS: aux->fmt = AUXCLKOUT_FMT_LVDS; break; + case LMX1214_FMT_CML : aux->fmt = AUXCLKOUT_FMT_CML; break; + default: + { + if(!aux->enable) + { + aux->fmt = AUXCLKOUT_FMT_LVDS; + } + else + { + USDR_LOG("1214", USDR_LOG_ERROR, "AUXCLKOUT_FMT:%u is invalid", aux->fmt); + return -EINVAL; + } + } + } + + if(out_en[2] != out_en[3]) + { + USDR_LOG("1214", USDR_LOG_ERROR, "bad configuration, OUT_EN2 != OUT_EN3"); + return -EINVAL; + } + + return 0; +} + +static const char* lmx1214_decode_mux(uint8_t mux) +{ + switch(mux) + { + case CLK_MUX_BUFFER : return "CLK_MUX_BUFFER"; + case CLK_MUX_DIVIDER: return "CLK_MUX_DIVIDER"; + } + return "UNKNOWN"; +} + +static const char* lmx1214_decode_auxfmt(uint8_t fmt) +{ + switch(fmt) + { + case AUXCLKOUT_FMT_LVDS: return "LVDS"; + case AUXCLKOUT_FMT_CML : return "CML"; + } + return "UNKNOWN"; +} + +int lmx1214_solver(lmx1214_state_t* st, uint64_t in, uint64_t out, bool* out_en, lmx1214_auxclkout_cfg_t* aux, bool prec_mode, bool dry_run) +{ + int res = lmx1214_solver_prevalidate(in, out, out_en, aux); + if(res) + return res; + + const bool buffer_mode = (out == in); + + unsigned clk_div; + uint8_t clk_mux; + + if(buffer_mode) + { + clk_mux = CLK_MUX_BUFFER; + clk_div = 1; //disabled + } + else + { + clk_mux = CLK_MUX_DIVIDER; + clk_div = (unsigned)((double)in / out + 0.5); + + if(clk_div < CLK_DIV_MIN || clk_div > CLK_DIV_MAX) + { + USDR_LOG("1214", USDR_LOG_ERROR, "CLK_DIV:%u out of range", clk_div); + return -EINVAL; + } + double f = (double)in / clk_div; + if(fabs(f - out) > FREQ_EPS) + { + USDR_LOG("1214", USDR_LOG_ERROR, "Calculated CLKOUT:%.4f too rough", f); + return -EINVAL; + } + if(prec_mode && in != out * clk_div) + { + USDR_LOG("1214", USDR_LOG_ERROR, "Cannot solve CLKOUT:%" PRIu64 " by int divider", out); + return -EINVAL; + } + } + + USDR_LOG("1214", USDR_LOG_DEBUG, "CLKIN:%" PRIu64 " CLKOUT:%.4f CLK_DIV:%u MUX:%u [BUFFER_MODE:%u] [PREC_MODE:%u]", + in, (double)in / clk_div, clk_div, clk_mux, buffer_mode, prec_mode); + + + uint8_t auxclk_div_pre = AUXCLK_DIV_PRE_DIV4; + uint16_t auxclk_div = 0x20; + bool auxclk_div_byp = false; + + if(aux->enable) + { + uint8_t pre_div_min; + + if(in <= AUXCLK_DIV_INP_MAX) + pre_div_min = AUXCLK_DIV_PRE_DIV1; + else if(in <= ((uint64_t)AUXCLK_DIV_INP_MAX << 1)) + pre_div_min = AUXCLK_DIV_PRE_DIV2; + else + pre_div_min = AUXCLK_DIV_PRE_DIV4; + + bool found = false; + for(auxclk_div_pre = pre_div_min; auxclk_div_pre <= AUXCLK_DIV_PRE_DIV4; ++auxclk_div_pre) + { + if(auxclk_div_pre == AUXCLK_DIV_PRE_DIV4 - 1) + continue; + + double fmid = (double)in / auxclk_div_pre; + if(prec_mode && in != (uint64_t)(fmid + 0.5) * auxclk_div_pre) + continue; + + if(fmid == aux->freq && auxclk_div_pre == AUXCLK_DIV_PRE_DIV1) + { + found = true; + auxclk_div_byp = true; + break; + } + + if(auxclk_div_pre == AUXCLK_DIV_PRE_DIV1) //cannot use pre_div==1 without bypassing div + continue; + + unsigned div = (unsigned)(fmid / aux->freq + 0.5); + + if(div < AUXCLK_DIV_MIN || div > AUXCLK_DIV_MAX) + continue; + + double f = fmid / div; + if(fabs(f - aux->freq) > FREQ_EPS) + continue; + + if(prec_mode && fmid != aux->freq * div) + continue; + + found = true; + auxclk_div = div; + break; + } + + if(!found) + { + USDR_LOG("1214", USDR_LOG_ERROR, "AUXCLKOUT:%.4f cannot be solved with LMX1214 divs", aux->freq); + return -EINVAL; + } + } + + //if we got here - the solution is found + st->clkin = in; + st->clk_mux = clk_mux; + st->clk_div = clk_div; + st->clkout = (double)in / clk_div; + for(unsigned i = 0; i < LMX1214_OUT_CNT; ++i) st->clkout_enabled[i] = out_en[i]; + st->auxclk_div_pre = auxclk_div_pre; + st->auxclk_div_byp = auxclk_div_byp; + st->auxclk_div = auxclk_div; + st->auxclkout = *aux; + if(st->auxclkout.enable) + st->auxclkout.freq = auxclk_div_byp ? (double)in / auxclk_div_pre : (double)in / auxclk_div_pre / auxclk_div; + + USDR_LOG("1214", USDR_LOG_INFO, "LMX1214 SOLUTION FOUND:"); + USDR_LOG("1214", USDR_LOG_INFO, "CLKIN:%" PRIu64 " CLK_DIV:%u CLKMUX:%s(%u) CLKOUT:%.4f", + st->clkin, st->clk_div, lmx1214_decode_mux(st->clk_mux), st->clk_mux, st->clkout); + USDR_LOG("1214", USDR_LOG_INFO, "CLKOUT enabled - OUT0:%u OUT1:%u OUT2:%u OUT3:%u", + st->clkout_enabled[0], st->clkout_enabled[1], st->clkout_enabled[2], st->clkout_enabled[3]); + + if(st->auxclkout.enable) + USDR_LOG("1214", USDR_LOG_INFO, "AUXCLC_DIV_PRE:%u AUXCLK_DIV_BYP:%u AUXCLK_DIV:%u AUXCLKOUT:%.4f AUXCLKOUT_FMT:%s(%u)", + st->auxclk_div_pre, st->auxclk_div_byp, st->auxclk_div, st->auxclkout.freq, + lmx1214_decode_auxfmt(st->auxclkout.fmt), st->auxclkout.fmt); + else + USDR_LOG("1214", USDR_LOG_INFO, "AUXCLKOUT:disabled"); + + //Setting registers + + res = dry_run ? 0 : lmx1214_reset_main_divider(st, true); + if(res) + { + USDR_LOG("1214", USDR_LOG_ERROR, "lmx1214_reset_main_divider(1) err:%d", res); + return res; + } + + uint32_t regs[] = + { + MAKE_LMX1214_R90(0, 0, (st->auxclk_div_byp ? 1 : 0), (st->auxclk_div_byp ? 1 : 0), 0), + MAKE_LMX1214_R79(0, 0x5), + MAKE_LMX1214_REG_WR(R75, 0x6), + MAKE_LMX1214_R25(0x4, 0/*clk div reset*/, st->clk_div - 1, st->clk_mux), + MAKE_LMX1214_R14(0, 0, 1, 0), + MAKE_LMX1214_R9 (0, 0, 0, (st->auxclk_div_byp ? 1 : 0), 0, st->auxclk_div), + MAKE_LMX1214_R8 (0, st->auxclk_div_pre, 0, (st->auxclkout.enable ? 1 : 0), 0, st->auxclkout.fmt), + MAKE_LMX1214_R7 (0, 0x2, 0x2, 0x2, 0x0/*prediv-pwr 2 bits*/, 0x3, 0x7/*aux pwr*/, 0x1), +#if 0 + //according do doc: program R79 and R90 before setting logiclk_div_bypass + //desc order is broken here! + MAKE_LMX1214_R8 (0, st->auxclk_div_pre, (st->auxclkout.enable ? 1 : 0), 0, st->auxclkout.fmt), + //MAKE_LMX1214_R79(0, st->auxclk_div_byp ? 0x5 : 0x104 /*0x205*/), + MAKE_LMX1214_R90(0, 0, (st->auxclk_div_byp ? 1 : 0), (st->auxclk_div_byp ? 1 : 0), 0), + MAKE_LMX1214_R9 (0, 0, 0, (st->auxclk_div_byp ? 1 : 0), 0, st->auxclk_div), +#endif + MAKE_LMX1214_R3 (st->clkout_enabled[LMX1214_CH3] ? 1 : 0, + st->clkout_enabled[LMX1214_CH2] ? 1 : 0, + st->clkout_enabled[LMX1214_CH1] ? 1 : 0, + st->clkout_enabled[LMX1214_CH0] ? 1 : 0, + 0xF86//0xFE + ), + MAKE_LMX1214_R2 (0, 0x8, 1/*en state machine*/, 0x3), + MAKE_LMX1214_R0 (0, 0/*pwr down*/, 0), + }; + + res = dry_run ? common_print_registers_a8d16(regs, SIZEOF_ARRAY(regs), USDR_LOG_DEBUG) : lmx1214_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + { + USDR_LOG("1214", USDR_LOG_ERROR, "Registers set lmx1214_spi_post() failed, err:%d", res); + return res; + } + + usleep(10000); + + res = dry_run ? 0 : lmx1214_reset_main_divider(st, false); + if(res) + { + USDR_LOG("1214", USDR_LOG_ERROR, "lmx1214_reset_main_divider(0) err:%d", res); + return res; + } + + return 0; +} + +int lmx1214_sysref_windowing_beforesync(lmx1214_state_t* st) +{ + int res; + + uint8_t delay_step_size = SYNC_DLY_STEP_28_PS_1_4GHZ_TO_2_7GHZ; + if(st->clkin > 2400000000 && st->clkin <= 4700000000) + delay_step_size = SYNC_DLY_STEP_15_PS__2_4GHZ_TO_4_7GHZ; + if(st->clkin > 3100000000 && st->clkin <= 5700000000) + delay_step_size = SYNC_DLY_STEP_11_PS_3_1GHZ_TO_5_7GHZ; + if(st->clkin > 4500000000 && st->clkin <= 12800000000) + delay_step_size = SYNC_DLY_STEP_8_PS_4_5GHZ_TO_12_8GHZ; + + USDR_LOG("1214", USDR_LOG_DEBUG, "DELAY_STEPSIZE:%u", delay_step_size); + + { + uint32_t regs[] = + { + MAKE_LMX1214_R9 (0, 1/*SYNC_EN*/, 0, (st->auxclk_div_byp ? 1 : 0), 0, st->auxclk_div), + MAKE_LMX1214_R14(0, 1/*CLKPOS_CAPTURE_EN*/, 1, 0), + MAKE_LMX1214_R13(0, delay_step_size), + }; + + res = lmx1214_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + return res; + } + + { + uint16_t r15; + res = lmx1214_spi_get(st, R15, &r15); + if(res) + return res; + + uint32_t regval_set = MAKE_LMX1214_REG_WR(R15, r15 | SYNC_CLR_MSK); + uint32_t regval_rst = MAKE_LMX1214_REG_WR(R15, r15 & ~SYNC_CLR_MSK); + + res = lmx1214_spi_post(st, ®val_set, 1); + res = res ? res : lmx1214_spi_post(st, ®val_rst, 1); + } + + return res; +} + +int lmx1214_sysref_windowing_aftersync(lmx1214_state_t* st) +{ + uint32_t regs[] = + { + MAKE_LMX1214_R9 (0, 0/*SYNC_EN*/, 0, (st->auxclk_div_byp ? 1 : 0), 0, st->auxclk_div), + MAKE_LMX1214_R14(0, 0/*CLKPOS_CAPTURE_EN*/, 1, 0), + }; + return lmx1214_spi_post(st, regs, SIZEOF_ARRAY(regs)); +} + +int lmx1214_sysref_windowing_capture(lmx1214_state_t* st) +{ + int res; + uint16_t r11, r12; + + res = lmx1214_spi_get(st, R11, &r11); + res = res ? res : lmx1214_spi_get(st, R12, &r12); + if(res) + return res; + + uint32_t clkpos = ((uint32_t)r12 << 16) | r11; + + unsigned delay; + res = common_ti_calc_sync_delay(clkpos, &delay); + if(res) + return res; + + { + uint32_t reg = MAKE_LMX1214_R15(0, 0x16, delay, 0); + res = lmx1214_spi_post(st, ®, 1); + if(res) + return res; + } + + return 0; +} diff --git a/src/lib/hw/lmx1214/lmx1214.h b/src/lib/hw/lmx1214/lmx1214.h new file mode 100644 index 00000000..b4aeba6a --- /dev/null +++ b/src/lib/hw/lmx1214/lmx1214.h @@ -0,0 +1,64 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef LMX1214_H +#define LMX1214_H + +#include + +#define LMX1214_OUT_CNT 4 + +enum +{ + LMX1214_CH0 = 0, + LMX1214_CH1 = 1, + LMX1214_CH2 = 2, + LMX1214_CH3 = 3, +}; + +enum +{ + LMX1214_FMT_LVDS = 0, + LMX1214_FMT_CML = 2, +}; + +struct lmx1214_auxclkout_cfg +{ + double freq; + bool enable; + uint8_t fmt; +}; +typedef struct lmx1214_auxclkout_cfg lmx1214_auxclkout_cfg_t; + +struct lmx1214_state +{ + lldev_t dev; + unsigned subdev; + unsigned lsaddr; + + uint64_t clkin; + + uint8_t clk_mux; + uint8_t clk_div; + + uint8_t auxclk_div_pre; + uint16_t auxclk_div; + bool auxclk_div_byp; + + double clkout; + bool clkout_enabled[LMX1214_OUT_CNT]; + lmx1214_auxclkout_cfg_t auxclkout; +}; +typedef struct lmx1214_state lmx1214_state_t; + + +int lmx1214_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lmx1214_state_t* st); +int lmx1214_destroy(lmx1214_state_t* st); +int lmx1214_solver(lmx1214_state_t* st, uint64_t in, uint64_t out, bool* out_en, lmx1214_auxclkout_cfg_t* aux, bool prec_mode, bool dry_run); +int lmx1214_get_temperature(lmx1214_state_t* st, float* value); + +int lmx1214_sysref_windowing_beforesync(lmx1214_state_t* st); +int lmx1214_sysref_windowing_capture(lmx1214_state_t* st); +int lmx1214_sysref_windowing_aftersync(lmx1214_state_t* st); + +#endif // LMX1214_H diff --git a/src/lib/hw/lmx1214/lmx1214.yaml b/src/lib/hw/lmx1214/lmx1214.yaml new file mode 100644 index 00000000..ad940865 --- /dev/null +++ b/src/lib/hw/lmx1214/lmx1214.yaml @@ -0,0 +1,521 @@ +name: LMX1214 +revision: 0.0.1 +processors: [ c ] +bus: + type: SPI + rd_mask: 0x800000 + usdr_path: /debug/hw/lmx1214/*/reg +addr_width: 8 +data_width: 16 + + +pages: +- name: Main + regs: + - addr: 0x0 + name: R0 + fields: + - bits: '1:0' + name: R0_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 2 + name: POWERDOWN + mode: RW + dflt: 0x0 + desc: Sets the device in a low-power state. The states of other registers are maintained. + - bits: '15:3' + name: UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x2 + name: R2 + fields: + - bits: '4:0' + name: R2_UNDISCLOSED_1 + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - bits: 5 + name: SMCLK_EN + mode: RW + dflt: 0x1 + desc: Enables the state machine clock generator. This is required for pin modes to function correctly and the part must be initialized with this bit enabled. However, this bit can later on be disabled to save current and prevent the state machine clock spur. + - bits: '10:6' + name: R2_UNDISCLOSED_0 + mode: RW + dflt: 0x8 + desc: Program this field to 0x8. + - bits: '15:11' + name: R2_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x3 + name: R3 + fields: + - bits: '11:0' + name: R3_UNDISCLOSED + mode: RW + dflt: 0xFE + desc: Program this field to 0xFE. + - bits: 12 + name: CLKOUT0_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT0 + - bits: 13 + name: CLKOUT1_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT1 + - bits: 14 + name: CLKOUT2_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT2 + - bits: 15 + name: CLKOUT3_EN + mode: RW + dflt: 0x1 + desc: Enables CLKOUT3 + - addr: 0x4 + name: R4 + fields: + - bits: '7:0' + name: R4_UNDISCLOSED_0 + mode: RW + dflt: 0xFF + desc: Program this field to 0xFF. + - bits: '10:8' + name: CLKOUT0_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT0. Larger values correspond to higher output power. + - bits: '13:11' + name: CLKOUT1_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT1. Larger values correspond to higher output power. + - bits: '15:14' + name: R4_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x5 + name: R5 + fields: + - bits: '2:0' + name: CLKOUT2_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT2. Larger values correspond to higher output power. + - bits: '5:3' + name: CLKOUT3_PWR + mode: RW + dflt: 0x6 + desc: Sets the output power of CLKOUT3. Larger values correspond to higher output power. + - bits: '14:6' + name: R5_UNDISCLOSED_0 + mode: RW + dflt: 0xDB + desc: Program this field to 0xDB. + - bits: 15 + name: R5_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x7 + name: R7 + fields: + - bits: 0 + name: R7_UNDISCLOSED_3 + mode: RW + dflt: 0x1 + desc: Program this field to 0x1. + - bits: '3:1' + name: AUXCLKOUT_PWR + mode: RW + dflt: 0x7 + desc: Sets the output power of AYXCLKOUT for CML format only (other output formats ignore this field). Larger values correspond to higher output power. + - bits: '6:4' + name: R7_UNDISCLOSED_2 + mode: RW + dflt: 0x3 + desc: Program this field to 0x3. + - bits: '8:7' + name: AUXCLK_DIV_PWR_PRE + mode: RW + dflt: 0x0 + desc: Sets the output power of the AUXCLK pre-driver. Larger values correspond to higher output power. + - bits: '10:9' + name: R7_UNDISCLOSED_1 + mode: RW + dflt: 0x2 + desc: Program this field to 0x2. + - bits: '12:11' + name: AUXCLKOUT_VCM + mode: RW + dflt: 0x2 + desc: In LVDS mode, sets the output common mode of the auxiliary clock output. Other output formats ignore this field. + opts: + 0x0: 1.2V + 0x1: 1.1V + 0x2: 1.0V + 0x3: 0.9V + - bits: '14:13' + name: R7_UNDISCLOSED_0 + mode: RW + dflt: 0x2 + desc: Program this field to 0x2. + - bits: 15 + name: R7_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x8 + name: R8 + fields: + - bits: '1:0' + name: AUXCLKOUT_FMT + mode: RW + dflt: 0x0 + desc: Selects the output driver format of the AUXCLKOUT output. + opts: + 0x0: LVDS + 0x2: CML + - bits: '3:2' + name: R8_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 4 + name: AUXCLKOUT_EN + mode: RW + dflt: 0x1 + desc: Enables AUXCLK subsystem. + - bits: 5 + name: R8_UNDISCLOSED_5 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '8:6' + name: AUXCLK_DIV_PRE + mode: RW + dflt: 0x4 + desc: Sets pre-divider value. Output of the pre-divider must be less than or equal to 3.2 GHz. When AUXCLK_DIV_PRE=1, register R79 is also required to be programmed to a value of 0x0005 and R90 to 0x0060 (AUXCLK_DIV_BYP2=1, AUXCLK_DIV_BYP3=1). Values for AUXCLK_DIV_PRE other than those listed below are reserved. + opts: + 0x1: "/1" + 0x2: "/2" + 0x4: "/4" + - bits: '15:9' + name: R8_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x9 + name: R9 + fields: + - bits: '9:0' + name: AUXCLK_DIV + mode: RW + dflt: 0x20 + desc: "Sets AUXCLK divider value. Maximum input frequency from AUXCLK_DIV_PRE must be <= 3200 MHz. The maximum AUXCLKOUT frequency must be <= 800 MHz to avoid amplitude degradation." + opts: + 0x2: "/2" + 0x3: "/3" + 0x3FF: "/1023" + - bits: 10 + name: R9_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 11 + name: AUXCLK_DIV_BYP + mode: RW + dflt: 0x0 + desc: Bypasses the AUXCLK_DIV divider to derive the AUXCLK output directly from the AUXCLK_DIV_PRE divider. Use only when AUXCLK_DIV_PRE=1 as one of the steps to achieve a total divide of 1 for the AUXCLK. To achieve a divide by 1, the following steps are required. 1. Set AUXCLK_DIV_PRE=1 2. Verify that register R79 is programmed to a value of 0x0005 3. Program R90 to 0x0060 (AUXCLK_DIV_BYP2=1, AUXCLK_DIV_BYP3=1) 4. Set AUXCLK_DIV_BYP=1 If a total divide of 1 for the AUXCLK is undesired, set this bit to 0. + - bits: 12 + name: R9_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 13 + name: SYNC_EN + mode: RW + dflt: 0x0 + desc: Enables synchronization path for the dividers and allows the clock position capture circuitry to be enabled. Used for multi-device synchronization. + - bits: '15:14' + name: SYNC_VCM + mode: RW + dflt: 0x0 + desc: Sets the internal DC Bias for the SYNC pins. Bias must be enabled for AC-coupled inputs; but can be enabled and overdriven, or disabled, for DC-coupled inputs. SYNC DC pin voltage must be in the range of 0.7 V to VCC, including minimum and maximum signal swing. + opts: + 0x0: "1.3V" + 0x1: "1.1V" + 0x2: "1.5V" + 0x3: "Disabled" + - addr: 0xB + name: R11 + fields: + - bits: '15:0' + name: RB_CLKPOS_L + mode: R + dflt: 0x0 + desc: Stores a snapshot of the CLKIN signal rising edge positions relative to a SYNC rising edge, with the snapshot starting from the LSB and ending at the MSB. Each bit represents a sample of the CLKIN signal, separated by a delay determined by the SYNC_DLY_STEP field. The first and last bits of rb_CLKPOS are always set, indicating uncertainty at the capture window boundary conditions. CLKIN rising edges are represented by every sequence of two set bits from LSB to MSB, including bits at the boundary conditions. The position of the CLKIN rising edges in the snapshot, along with the CLKIN signal period and the delay step size, can be used to compute the value of SYNC_DLY which maximizes setup and hold times for SYNC signals on the SYNC pins. + - addr: 0xC + name: R12 + fields: + - bits: '15:0' + name: RB_CLKPOS_U + mode: R + dflt: 0x0 + desc: MSB of rb_CLKPOS field. + - addr: 0xD + name: R13 + fields: + - bits: '1:0' + name: SYNC_DLY_STEP + mode: RW + dflt: 0x3 + desc: Sets the step size of the delay element used in the SYSNC path, both for SYNC input delay and for clock position captures. The recommended frequency range for each step size creates the maximum number of usable steps for a given CLKIN frequency. The ranges include some overlap to account for process and temperature variations. If the CLKIN frequency is covered by an overlapping span, larger delay step sizes improve the likelihood of detecting a CLKIN rising edge during a clock position capture. However, since larger values include more delay steps, larger step sizes have greater total delay variation across PVT relative to smaller step sizes. + opts: + 0x0: 28 ps (1.4GHz to 2.7GHz) + 0x1: 15 ps ( 2.4GHz to 4.7GHz) + 0x2: 11 ps (3.1GHz to 5.7GHz) + 0x3: 8 ps (4.5GHz to 12.8GHz) + - bits: '15:2' + name: R13_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0xE + name: R14 + fields: + - bits: 0 + name: SYNC_LATCH + mode: RW + dflt: 0x0 + desc: Latches the internal SYNC state to logic high on the first rising edge of the SYNC pins. This latch can be cleared by setting SYNC_CLR=1. + - bits: 1 + name: R14_UNDISCLOSED_0 + mode: RW + dflt: 0x1 + desc: Program this field to 0x1. + - bits: 2 + name: CLKPOS_CAPTURE_EN + mode: RW + dflt: 0x0 + desc: Enables the windowing circuit which captures the clock position in the rb_CLKPOS registers with respect to a SYNC edge. The windowing circuit must be cleared by toggling SYNC_CLR high then low before a clock position capture. The first rising edge on the SYNC pins after clearing the windowing circuit triggers the capture. The capture circuitry greatly increases supply current, and does not need to be enabled to delay the SYNC signal in SYNC mode. Once the desired value of SYNC_DLY is determined, set this bit to 0x0 to minimize current consumption. If SYNC_EN = 0, the value of this bit is ignored, and the windowing circuit is disabled. + - bits: '15:3' + name: R14_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0xF + name: R15 + fields: + - bits: 0 + name: SYNC_CLR + mode: RW + dflt: 0x1 + desc: Clears SYNC_LATCH and resets synchronization path timing for SYNC signal. Holding this bit high keeps internal SYNC signal low. This bit must be set and cleared once before the SYNC or clock position capture operations are performed. + - bits: '6:1' + name: SYNC_DLY + mode: RW + dflt: 0x0 + desc: Sets the delay line step for the external SYNC signal. Each delay line step delays the SYNC signal by an amount equal to SYNC_DLY_STEP x SYNC_DLY_STEP. In SYNC mode, the value for this field can be determined based on the rb_CLKPOS value to satisfy the internal setup and hold time of the SYNC signal with respect to the CLKIN signal. In SYSREF Repeater Mode, the value for this field can be used as a coarse global delay. Values greater than 0x3F are invalid. Since larger values include more delay steps, larger values have greater total step size variation across PVT relative to smaller values. Refer to the data sheet or the device TICS Pro profile for detailed description of the delay step computation procedure. + - bits: '11:7' + name: R15_UNDISCLOSED_0 + mode: RW + dflt: 0x16 + desc: Program this field to 0x16. + - bits: '15:12' + name: R15_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x17 + name: R23 + fields: + - bits: '12:0' + name: R23_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 13 + name: MUXOUT_EN + mode: RW + dflt: 0x0 + desc: Enables or tri-states the MUXOUT pin driver. + opts: + 0b0: Tri-States + 0b1: Push-Pull + - bits: 14 + name: R23_UNDISCLOSED + mode: RW + dflt: 0x1 + desc: Program this field to 0x1. + - bits: 15 + name: TS_EN + mode: RW + dflt: 0x0 + desc: Enables the on-die temperature sensor. Temperature sensor counter (TS_CNT_EN) must also be enabled for readback. + - addr: 0x18 + name: R24 + fields: + - bits: 0 + name: TS_CNT_EN + mode: RW + dflt: 0x0 + desc: Enables temperature sensor counter. Temperature sensor (TS_EN) must be enabled for accurate data. + - bits: '11:1' + name: RB_TS + mode: R + dflt: 0x0 + desc: Readback value of on-die temperature sensor. + - bits: '13:12' + name: R24_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '15:14' + name: R24_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x19 + name: R25 + fields: + - bits: '2:0' + name: CLK_MUX + mode: RW + dflt: 0x1 + desc: Selects the function for the main clock outputs + opts: + 0x1: Buffer + 0x2: Divider + - bits: '5:3' + name: CLK_DIV + mode: RW + dflt: 0x2 + desc: Sets the clock divider value when CLK_MUX=2 (Divider Mode). The clock divider value is CLK_DIV+1. Valid value for CLK_DIV is 1 to 7. Setting this to 0 disables the main clock divider and reverts to buffer mode. + - bits: 6 + name: CLK_DIV_RST + mode: RW + dflt: 0x0 + desc: Resets the main clock divider. If the clock divider value is changed during operation, set this bit high then low after setting the new divider value. Synchronizing the device with the SYNC pins with SYNC_EN = 0x1 also resets the main clock divider. This bit has no effect when outside of Divider Mode. + - bits: '15:7' + name: R25_UNDISCLOSED + mode: RW + dflt: 0x4 + desc: Program this field to 0x4. + - addr: 0x4B + name: R75 + fields: + - bits: '3:0' + name: R75_UNDISCLOSED_0 + mode: RW + dflt: 0x6 + desc: Program this field to 0x6. + - bits: 4 + name: RB_CE + mode: R + dflt: 0x0 + desc: Readback Pin Status + - bits: 5 + name: RB_DIVSEL0 + mode: R + dflt: 0x0 + desc: Readback Pin Status + - bits: 6 + name: RB_DIVSEL1 + mode: R + dflt: 0x0 + desc: Readback Pin Status + - bits: '11:7' + name: R75_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 12 + name: RB_MUXSEL1 + mode: R + dflt: 0x0 + desc: Readback Pin Status + - bits: 13 + name: RB_CLKOUT0_EN + mode: R + dflt: 0x0 + desc: Readback Pin Status + - bits: 14 + name: RB_CLKOUT1_EN + mode: R + dflt: 0x0 + desc: Readback Pin Status + - bits: 15 + name: RB_CLKOUT2_EN + mode: R + dflt: 0x0 + desc: Readback Pin Status + - addr: 0x4F + name: R79 + fields: + - bits: '14:0' + name: R79_UNDISCLOSED_0 + mode: RW + dflt: 0x205 + desc: Program this field to 0x5. Note that this is different than the reset value. + - bits: 15 + name: R79_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x56 + name: R86 + fields: + - bits: '1:0' + name: R86_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 2 + name: MUXOUT_EN_OVRD + mode: RW + dflt: 0x0 + desc: This bit must be set to 1 to enable MUXOUT_EN to tri-state the MUXOUT pin. + - bits: '15:3' + name: R86_UNDISCLOSED + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - addr: 0x5A + name: R90 + fields: + - bits: '4:0' + name: R90_UNDISCLOSED_1 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: 5 + name: AUXCLK_DIV_BYP2 + mode: RW + dflt: 0x0 + desc: Set this bit to 1 if AUXCLK_BYP=1, set to 0 otherwise. + - bits: 6 + name: AUXCLK_DIV_BYP3 + mode: RW + dflt: 0x0 + desc: Set this bit to 1 if AUXCLK_BYP=1, set to 0 otherwise. + - bits: 7 + name: R90_UNDISCLOSED_0 + mode: RW + dflt: 0x0 + desc: Program this field to 0x0. + - bits: '15:8' + name: R90_UNDISCLOSED + mode: R + dflt: 0x0 + desc: Program this field to 0x0. diff --git a/src/lib/hw/lmx2820/lmx2820.c b/src/lib/hw/lmx2820/lmx2820.c new file mode 100644 index 00000000..c05c2cb1 --- /dev/null +++ b/src/lib/hw/lmx2820/lmx2820.c @@ -0,0 +1,1249 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include + +#include "lmx2820.h" +#include "def_lmx2820.h" +#include +#include "../cal/opt_func.h" +#include "../common/common.h" + +enum { + + OSC_IN_MIN = 5000000ull, + OSC_IN_MAX = 1400000000ull, + OSC_IN_MAX_DBLR = 250000000ull, + OSC_IN_MAX_SYNC = 200000000ull, + + OUT_FREQ_MIN = 45000000ull, + OUT_FREQ_MAX = 22600000000ull, + + VCO_MIN = 5650000000ull, + VCO_MAX = 11300000000ull, + + PLL_R_PRE_DIV_MIN = 1, + PLL_R_PRE_DIV_MAX = 4095, + + MULT_IN_FREQ_MIN = 30000000ull, + MULT_IN_FREQ_MAX = 70000000ull, + MULT_OUT_FREQ_MIN = 180000000ull, + MULT_OUT_FREQ_MAX = 250000000ull, + + MULT_MIN = MULT_X3, + MULT_MAX = MULT_X7, + + FPD_MIN = 5000000ull, + + PLL_R_DIV_MIN = 1, + PLL_R_DIV_MAX = 255, + PLL_R_DIV_2_IN_FREQ_MAX = 500000000ull, + PLL_R_DIV_GT2_IN_FREQ_MAX = 250000000ull, + + OUT_DIV_LOG2_MIN = 1, + OUT_DIV_LOG2_MAX = 7, + + SYSREF_DIV_PRE_MIN = 2, + SYSREF_DIV_PRE_MID = 4, + SYSREF_DIV_PRE_MAX = 8, + SYSREF_DIV_MIN = 2, + SYSREF_DIV_MAX = 2049, + SYSREF_FINTERPOLATOR_MIN = 600000000ull, + SYSREF_FINTERPOLATOR_MAX = 1500000000ull, + SYSREF_PULSE_CNT_MIN = 1, + SYSREF_PULSE_CNT_MAX = 15, + SYSREF_DELAY_CTRL_MIN = 0, + SYSREF_DELAY_CTRL_MAX = 251, +}; + +#define RF_ACCURACY 0.0f +#define OUT_DIV_DIAP_MAX (OUT_DIV_LOG2_MAX - OUT_DIV_LOG2_MIN + 1 + 1) + +//Pin3 bias capacitor, uF +#define C_BIAS 1.0f + +enum { + PLL_N_MIN = 12, + PLL_N_MAX = 32767, +}; + +struct range { + uint64_t min, max; +}; +typedef struct range range_t; + +struct vco_core { + range_t freq; + uint16_t ndiv_min[MASH_ORDER_THIRD_ORDER + 1]; +}; +typedef struct vco_core vco_core_t; + +static vco_core_t VCO_CORES[VCO_SEL_VCO7] = +{ + {{VCO_MIN, 6350000000}, {12,18,19,24}}, + {{6350000000, 7300000000}, {14,21,22,26}}, + {{7300000000, 8100000000}, {16,23,24,26}}, + {{8100000000, 9000000000}, {16,26,27,29}}, + {{9000000000, 9800000000}, {18,28,29,31}}, + {{9800000000, 10600000000}, {18,30,31,33}}, + {{10600000000, VCO_MAX + 1}, {20,33,34,36}} +}; + +static uint64_t FPD_MAX[MASH_ORDER_THIRD_ORDER + 1] = +{ + 400000000, 300000000, 300000000, 250000000 +}; + +#define INSTCAL_R0_MASK (((uint16_t)1 << FCAL_EN_OFF) | ((uint16_t)1 << DBLR_CAL_EN_OFF) | ((uint16_t)1 << INSTCAL_SKIP_ACAL_OFF)) + +struct jesd_flds +{ + uint8_t DAC1_CTRL; + uint8_t DAC2_CTRL; + uint8_t DAC3_CTRL; + uint8_t DAC4_CTRL; +}; + +#define JESD_SIZE 252 +static struct jesd_flds JESD[JESD_SIZE]; + +static void lmx2820_fill_jesd() +{ + JESD[0].DAC1_CTRL = 63; + JESD[0].DAC2_CTRL = 0; + JESD[0].DAC3_CTRL = 0; + JESD[0].DAC4_CTRL = 0; + + for(uint8_t i = 1; i < JESD_SIZE; ++i) + { + if(i < 64) + { + JESD[i].DAC1_CTRL = JESD[i - 1].DAC1_CTRL - 1; + JESD[i].DAC2_CTRL = JESD[i - 1].DAC2_CTRL + 1; + JESD[i].DAC3_CTRL = 0; + JESD[i].DAC4_CTRL = 0; + } + else if(i < 127) + { + JESD[i].DAC1_CTRL = 0; + JESD[i].DAC2_CTRL = JESD[i - 1].DAC2_CTRL - 1; + JESD[i].DAC3_CTRL = JESD[i - 1].DAC3_CTRL + 1; + JESD[i].DAC4_CTRL = 0; + } + else if(i < 190) + { + JESD[i].DAC1_CTRL = 0; + JESD[i].DAC2_CTRL = 0; + JESD[i].DAC3_CTRL = JESD[i - 1].DAC3_CTRL - 1; + JESD[i].DAC4_CTRL = JESD[i - 1].DAC4_CTRL + 1; + } + else + { + JESD[i].DAC1_CTRL = JESD[i - 1].DAC1_CTRL + 1; + JESD[i].DAC2_CTRL = 0; + JESD[i].DAC3_CTRL = 0; + JESD[i].DAC4_CTRL = JESD[i - 1].DAC4_CTRL - 1; + } + } +} + +static int lmx2820_spi_post(lmx2820_state_t* obj, uint32_t* regs, unsigned count) +{ + return + common_print_registers_a8d16(regs, count, USDR_LOG_DEBUG) + || + common_spi_post(obj, regs, count); +} + +static int lmx2820_spi_get(lmx2820_state_t* obj, uint16_t addr, uint16_t* out) +{ + return common_spi_get(obj, MAKE_LMX2820_REG_RD((uint32_t)addr), out); +} + +static int lmx2820_get_worst_vco_core(uint64_t vco_freq, unsigned mash_order, unsigned* vco_core, uint16_t* min_pll_n) +{ + if( vco_freq < VCO_MIN || vco_freq > VCO_MAX || + mash_order > MASH_ORDER_THIRD_ORDER) + { + USDR_LOG("2820", USDR_LOG_ERROR, "VCO core detection failed [VCO:%" PRIu64 " MASH_ORDER:%d]", vco_freq, mash_order); + return -EINVAL; + } + + for(unsigned i = 0; i < VCO_SEL_VCO7; ++i) + { + const vco_core_t r = VCO_CORES[i]; + if(vco_freq >= r.freq.min && vco_freq < r.freq.max) + { + if(vco_core) + *vco_core = i + 1; + if(min_pll_n) + *min_pll_n = r.ndiv_min[mash_order]; + USDR_LOG("2820", USDR_LOG_DEBUG, "VCO:%" PRIu64 " -> VCO_CORE%d PLL_N_MIN:%d", vco_freq, (i + 1), r.ndiv_min[mash_order]); + return 0; + } + } + + return -EINVAL; +} + +int lmx2820_sync(lmx2820_state_t* st) +{ + uint16_t r1; + + int res = lmx2820_spi_get(st, R1, &r1); + if(res) + return res; + + uint32_t regs[] = + { + MAKE_LMX2820_REG_WR(R1, (r1 & ~PHASE_SYNC_EN_MSK)), + MAKE_LMX2820_REG_WR(R1, (r1 | PHASE_SYNC_EN_MSK)), + MAKE_LMX2820_REG_WR(R1, (r1 & ~PHASE_SYNC_EN_MSK)), + }; + + return lmx2820_spi_post(st, regs, SIZEOF_ARRAY(regs)); +} + +int lmx2820_reset(lmx2820_state_t* st) +{ + uint16_t r0; + + int res = lmx2820_spi_get(st, R0, &r0); + if(res) + return res; + + uint32_t regs[] = + { + MAKE_LMX2820_REG_WR(R0, r0 | RESET_MSK), + MAKE_LMX2820_REG_WR(R0, r0 & ~RESET_MSK) + }; + + res = lmx2820_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + return res; + + usleep(10000); //reset takes <1us + return 0; +} + +static int lmx2820_calibrate(lmx2820_state_t* st, bool set_flag) +{ + uint16_t r0; + + int res = lmx2820_spi_get(st, R0, &r0); + if(res) + return res; + + if(set_flag) + r0 |= FCAL_EN_MSK; + else + r0 &= ~FCAL_EN_MSK; + + uint32_t reg = MAKE_LMX2820_REG_WR(R0, r0); + + return lmx2820_spi_post(st, ®, 1); +} + +int lmx2820_wait_pll_lock(lmx2820_state_t* st, unsigned timeout) +{ + int res = 0; + unsigned elapsed = 0; + + uint16_t r74; + while(timeout == 0 || elapsed < timeout) + { + uint64_t tk = clock_get_time(); + + res = lmx2820_spi_get(st, R74, &r74); + if(res) + return res; + + const uint16_t lock_detect_status = (r74 & RB_LD_MSK) >> RB_LD_OFF; + switch(lock_detect_status) + { + //case RB_LD_INVALID: return -EINVAL; + case RB_LD_LOCKED: return 0; + default: + usleep(100); + elapsed += (clock_get_time() - tk); + } + } + + return -ETIMEDOUT; +} + +int lmx2820_get_temperature(lmx2820_state_t* st, float* value) +{ + if(!value) + return -EINVAL; + + uint16_t r76; + + int res = lmx2820_spi_get(st, R76, &r76); + if(res) + return res; + + int16_t code = (r76 & RB_TEMP_SENS_MSK) >> RB_TEMP_SENS_OFF; + *value = 0.85f * code - 415.0f; + + return 0; +} + +static inline const char* lmx2820_decode_lock_status(uint8_t ld) +{ + switch(ld) + { + case RB_LD_UNLOCKED0: + case RB_LD_UNLOCKED1: return "UNLOCKED"; + case RB_LD_LOCKED: return "LOCKED"; + case RB_LD_INVALID: return "INVALID"; + } + return "UNKNOWN"; +} + +int lmx2820_read_status(lmx2820_state_t* st, lmx2820_stats_t* status) +{ + if(!status) + return -EINVAL; + + uint16_t r74, r75; + + int res = lmx2820_get_temperature(st, &status->temperature); + res = res ? res : lmx2820_spi_get(st, R74, &r74); + res = res ? res : lmx2820_spi_get(st, R75, &r75); + if(res) + return res; + + status->lock_detect_status = (r74 & RB_LD_MSK) >> RB_LD_OFF; + status->vco_capctrl = (r74 & RB_VCO_CAPCTRL_MSK) >> RB_VCO_CAPCTRL_OFF; + status->vco_sel = (r74 & RB_VCO_SEL_MSK) >> RB_VCO_SEL_OFF; + status->vco_daciset = (r75 & RB_VCO_DACISET_MSK) >> RB_VCO_DACISET_OFF; + + USDR_LOG("2820", USDR_LOG_DEBUG, "STATUS> Temp:%.2fC LOCK:%d(%s) VCO_CAPCTRL:%d VCO_SEL:%d VCO_DACISET:%d", + status->temperature, status->lock_detect_status, + lmx2820_decode_lock_status(status->lock_detect_status), + status->vco_capctrl, status->vco_sel, status->vco_daciset + ); + + return 0; +} + +int lmx2820_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lmx2820_state_t* st) +{ + memset(st, 0, sizeof(*st)); + + st->dev = dev; + st->subdev = subdev; + st->lsaddr = lsaddr; + + int res; + + res = lmx2820_reset(st); + if(res) + return res; + + uint32_t regs[] = + { + MAKE_LMX2820_R19(0x109, TEMPSENSE_EN_ENABLED, 0x0), //enable temperature sensor + }; + + res = lmx2820_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Registers set lmx2820_spi_post() failed, err:%d", res); + return res; + } + + usleep(10000); + + lmx2820_stats_t status; + res = lmx2820_read_status(st, &status); + if(res) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Status check failed, err:%d", res); + return res; + } + + USDR_LOG("2820", USDR_LOG_DEBUG, "Create OK"); + return 0; +} + +int lmx2820_destroy(lmx2820_state_t* st) +{ + USDR_LOG("2820", USDR_LOG_DEBUG, "Destroy OK"); + return 0; +} + +static int lmx2820_tune_vco(lmx2820_state_t* st, uint64_t vco) +{ + int res = 0; + lmx2820_input_chain_t * settings = &st->lmx2820_input_chain; + + uint16_t min_n_total; + uint16_t max_n_total = PLL_N_MAX + 1; + + unsigned vco_core; + res = lmx2820_get_worst_vco_core(vco, settings->mash_order, &vco_core, &min_n_total); + if(res) + return res; + + double n_total = (double)vco / settings->fpd; + USDR_LOG("2820", USDR_LOG_DEBUG, "N_total:%.6f", n_total); + + if(n_total < min_n_total || n_total > max_n_total) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Ntotal:%.6f out of range [%u;%u)", n_total, min_n_total, max_n_total); + return -EINVAL; + } + + uint16_t pll_n = (uint16_t)n_total; + double pll_frac = n_total - pll_n; + USDR_LOG("2820", USDR_LOG_DEBUG, "PLL_N:%u PLL_FRAC:%.8f", pll_n, pll_frac); + + if(pll_n != n_total && settings->mash_order == MASH_ORDER_INTEGER_MODE) + { + USDR_LOG("2820", USDR_LOG_ERROR, "PLL frac=%.8f, MASH_ORDER_INTEGER_MODE is inapplicable", pll_frac); + return -EINVAL; + } + + const unsigned pll_r_div = settings->pll_r * settings->pll_r_pre; + uint64_t pll_den64 = settings->fpd * pll_r_div; + uint64_t pll_num64 = (uint64_t)(pll_frac * pll_den64 + 0.5); + uint64_t nod = find_gcd(pll_num64, pll_den64); + + if(nod > 1) + { + USDR_LOG("2820", USDR_LOG_DEBUG, "PLL NUM/DEN reduced NOD:%" PRIu64 ": %" PRIu64 "/%" PRIu64" -> %" PRIu64 "/%" PRIu64, + nod, pll_num64, pll_den64, pll_num64/nod, pll_den64/nod); + pll_num64 /= nod; + pll_den64 /= nod; + } + + if(pll_den64 > UINT32_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "PLL_DEN overflow, cannot solve in integer values. Try lower OSC_IN."); + return -EINVAL; + } + + uint32_t pll_num = pll_num64; + uint32_t pll_den = pll_den64; + + const double ff = (double)settings->fosc_in * (settings->osc_2x ? 2 : 1) * settings->mult; + double vco_fact = ff * pll_n / pll_r_div + ff * pll_num / pll_r_div / pll_den; + + USDR_LOG("2820", USDR_LOG_DEBUG, "PLL_N:%u PLL_NUM:%u PLL_DEN:%u VCO:%.8f", pll_n, pll_num, pll_den, vco_fact); + + if(vco_fact != vco) + { + USDR_LOG("2820", USDR_LOG_ERROR, "VCO tuning too rough"); + return -EINVAL; + } + + settings->vco_core = vco_core; + settings->pll_n = pll_n; + settings->pll_num = pll_num; + settings->pll_den = pll_den; + settings->fvco = vco_fact; + + return 0; +} + +static int lmx2820_calculate_input_chain(lmx2820_state_t* st, uint64_t fosc_in, uint64_t vco, unsigned mash_order, unsigned force_mult) +{ + int res = 0; + unsigned mult, pll_r_pre, pll_r; + + uint16_t min_n_total; + uint16_t max_n_total = PLL_N_MAX + 1; + + res = lmx2820_get_worst_vco_core(vco, mash_order, NULL, &min_n_total); + if(res) + return res; + + uint64_t fpd_max = FPD_MAX[mash_order]; + uint64_t fpd_min = FPD_MIN; + + fpd_max = MIN(fpd_max, vco / min_n_total); + fpd_min = MAX(fpd_min, vco / max_n_total); + USDR_LOG("2820", USDR_LOG_DEBUG, "VCO:%" PRIu64 " NMIN:%u NMAX:%u FPD_MIN:%" PRIu64 " FPD_MAX:%" PRIu64, + vco, min_n_total, max_n_total, fpd_min, fpd_max); + + bool need_mult = (fosc_in < fpd_min) || force_mult; + const bool osc_2x = (fosc_in <= OSC_IN_MAX_DBLR && (fosc_in << 1) <= fpd_max && !need_mult); + + uint64_t osc_in = fosc_in * (osc_2x ? 2 : 1); + USDR_LOG("2820", USDR_LOG_DEBUG, "OSC_2X:%d -> effective OSC_IN:%" PRIu64, osc_2x, osc_in); + + if((osc_in < fpd_min) || force_mult) + { + //need mult + + if(force_mult) + USDR_LOG("2820", USDR_LOG_DEBUG, "Mult:%d forced by user", force_mult); + else + USDR_LOG("2820", USDR_LOG_DEBUG, "Need mult"); + + if(osc_in < MULT_IN_FREQ_MIN) + { + USDR_LOG("2820", USDR_LOG_ERROR, "OSC_IN:%" PRIu64" too low for mult, set %" PRIu64 " at least", fosc_in, (uint64_t)MULT_IN_FREQ_MIN); + return -EINVAL; + } + + mult = force_mult ? force_mult : (unsigned)floor((double)fpd_max / osc_in); + mult = MIN(MAX(mult, MULT_MIN), MULT_MAX); + USDR_LOG("2820", USDR_LOG_DEBUG, "Calculated mult:%u", mult); + + pll_r_pre = 1; + pll_r = 1; + + if(osc_in > MULT_IN_FREQ_MAX) + { + pll_r_pre = (unsigned)ceil((double)osc_in / MULT_IN_FREQ_MAX); + } + + uint64_t freq_pre = osc_in / pll_r_pre; + uint64_t freq_mult = freq_pre * mult; + + while(freq_mult < MULT_OUT_FREQ_MIN) + { + if(mult == MULT_MAX) + return -EINVAL; + freq_mult = freq_pre * (++mult); + if(freq_mult > MULT_OUT_FREQ_MAX) + return -EINVAL; + } + + while(freq_mult > MULT_OUT_FREQ_MAX) + { + if(mult == MULT_MIN) + return -EINVAL; + freq_mult = freq_pre * (--mult); + if(freq_mult < MULT_OUT_FREQ_MIN) + return -EINVAL; + } + + if(freq_mult > fpd_max) + { + pll_r = (unsigned)ceil((double)freq_mult / fpd_max); + } + } + else if(osc_in > fpd_max) + { + USDR_LOG("2820", USDR_LOG_DEBUG, "Need divs"); + + //no need for mult, but need for divs + mult = 1; + unsigned div = (unsigned)ceil((double)osc_in / fpd_max); + if(div > PLL_R_PRE_DIV_MAX * PLL_R_DIV_MAX) + return -EINVAL; + + + if(div <= PLL_R_PRE_DIV_MAX) + { + pll_r_pre = div; + pll_r = 1; + } + else + { + pll_r_pre = PLL_R_PRE_DIV_MAX; + pll_r = (unsigned)ceil((double)div / PLL_R_PRE_DIV_MAX); + } + + USDR_LOG("2820", USDR_LOG_DEBUG, "TOTAL_DIV:%u PLL_R_PRE:%u PLL_R:%u", div, pll_r_pre, pll_r); + } + else + { + //no need neither for mult or for divs + mult = 1; + pll_r_pre = 1; + pll_r = 1; + } + + if(pll_r > PLL_R_DIV_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "PLL_R:%d out of range", pll_r); + return -EINVAL; + } + + uint64_t f_in_pll_r = osc_in * mult / pll_r_pre; + uint64_t max_f_in_pll_r = (pll_r <= 2) ? PLL_R_DIV_2_IN_FREQ_MAX : PLL_R_DIV_GT2_IN_FREQ_MAX; + if(f_in_pll_r > max_f_in_pll_r) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Input freq for PLL_R:%d is out of range, %" PRIu64 " > %" PRIu64, pll_r, f_in_pll_r, max_f_in_pll_r); + return -EINVAL; + } + + double fpd = (double)osc_in * mult / (pll_r_pre * pll_r); + USDR_LOG("2820", USDR_LOG_DEBUG, "For VCO:%" PRIu64 " -> FPD:%.8f", vco, fpd); + + if(fpd < fpd_min || fpd > fpd_max) + { + USDR_LOG("2820", USDR_LOG_ERROR, "FPD:%.8f out of range: should never be happen!", fpd); + return -EINVAL; + } + + lmx2820_input_chain_t * settings = &st->lmx2820_input_chain; + + settings->fosc_in = fosc_in; + settings->osc_2x = osc_2x; + settings->mash_order = mash_order; + settings->pll_r_pre = pll_r_pre; + settings->mult = mult; + settings->pll_r = pll_r; + settings->fpd = fpd; + + res = lmx2820_tune_vco(st, vco); + if(res) + return res; + + USDR_LOG("2820", USDR_LOG_INFO, "Input circuit res: OSC_IN:%" PRIu64 " OSC_2X:%d PLL_R_PRE:%d MULT:%d PLL_R:%d FPD:%.0f PLL_N:%u PLL_NUM:%u PLL_DEN:%u VCO:%.2f", + settings->fosc_in, + settings->osc_2x, + settings->pll_r_pre, + settings->mult, + settings->pll_r, + settings->fpd, + settings->pll_n, + settings->pll_num, + settings->pll_den, + settings->fvco); + + return 0; +} + + +static int lmx2820_solver_prepare(uint64_t rfouta, uint64_t rfoutb, uint64_t* vco, uint8_t* diva, uint8_t* divb, uint8_t* muxa, uint8_t* muxb) +{ + uint64_t *rf_max, *rf_min; + unsigned mux_max, mux_min; + + if(rfouta > rfoutb) + { + rf_max = &rfouta; + rf_min = &rfoutb; + } + else + { + rf_max = &rfoutb; + rf_min = &rfouta; + } + + double rf_ratio = log2((double)(*rf_max)/(*rf_min)); + unsigned rf_ratio_n = (unsigned)rf_ratio; + + if(fabs(rf_ratio - rf_ratio_n) > 1E-8) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RFOUT A/B ratio must be == 2^N"); + return -EINVAL; + } + + if(rf_ratio_n > OUT_DIV_DIAP_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RFOUT ratio:%d > %d, out of range", rf_ratio_n, (int)OUT_DIV_DIAP_MAX); + return -EINVAL; + } + + UNUSED uint64_t rf_max_fact, rf_min_fact; + uint8_t div_max = 1, div_min = 1; + + if(*rf_max > VCO_MAX) + { + //rf_max is doubled VCO + //rf_min may be VCO or VCO/2..7 + + mux_max = OUTA_MUX_VCO_DOUBLER; + *vco = (uint64_t)((double)(*rf_max) / 2 + 0.5); + rf_max_fact = *vco << 1; + + switch(rf_ratio_n) + { + case 0: rf_min_fact = rf_max_fact; mux_min = OUTA_MUX_VCO_DOUBLER; break; + case 1: rf_min_fact = *vco; mux_min = OUTA_MUX_VCO; break; + default: + div_min = rf_ratio_n - 1; + rf_min_fact = *vco >> div_min; + mux_min = OUTA_MUX_CHANNEL_DIVIDER; + if(div_min == OUT_DIV_LOG2_MAX) + { + div_max = div_min; + } + } + } + else if(*rf_max < VCO_MIN) + { + //both rf_max & rf_min are VCO/2..7 + //rf_ratio_n must be <=6 + + if(rf_ratio_n > OUT_DIV_DIAP_MAX - 2) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RFOUT ratio:%d > %d, out of range", rf_ratio_n, (int)OUT_DIV_DIAP_MAX - 2); + return -EINVAL; + } + + *vco = MAX((*rf_max) << OUT_DIV_LOG2_MIN, VCO_MIN); + div_max = (uint8_t)ceil(log2((double)(*vco) / (*rf_max))); + div_max = MAX(div_max, OUT_DIV_LOG2_MIN); + div_min = div_max + rf_ratio_n; + + if(div_max < OUT_DIV_LOG2_MIN || div_max > OUT_DIV_LOG2_MAX || div_min < OUT_DIV_LOG2_MIN || div_min > OUT_DIV_LOG2_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Cannot calculate dividers for RFs specified (DIV_MIN:%d DIV_MAX:%d)", div_min, div_max); + return -EINVAL; + } + + if((div_min == OUT_DIV_LOG2_MAX || div_max == OUT_DIV_LOG2_MAX) && div_min != div_max) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Invalid RF dividers configuration (DIV_MIN:%d DIV_MAX:%d)", div_min, div_max); + return -EINVAL; + } + + *vco = (*rf_max) << div_max; + rf_max_fact = *vco >> div_max; + rf_min_fact = *vco >> div_min; + mux_min = mux_max = OUTA_MUX_CHANNEL_DIVIDER; + } + else + { + //rf_max == VCO + //rf_min - VCO if rfa==rfb, or VCO/2..7 + //rf_ratio_n must be <=7 + + if(rf_ratio_n > OUT_DIV_DIAP_MAX - 1) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RFOUT ratio:%d > %d, out of range", rf_ratio_n, (int)OUT_DIV_DIAP_MAX - 1); + return -EINVAL; + } + + *vco = *rf_max; + rf_max_fact = *vco; + mux_max = OUTA_MUX_VCO; + + switch(rf_ratio_n) + { + case 0: rf_min_fact = rf_max_fact; mux_min = OUTA_MUX_VCO; break; + default: + div_min = rf_ratio_n; + rf_min_fact = *vco >> div_min; + mux_min = OUTA_MUX_CHANNEL_DIVIDER; + if(div_min == OUT_DIV_LOG2_MAX) + { + div_max = div_min; + } + } + } + + *diva = (&rfouta == rf_min) ? div_min : div_max; + *divb = (&rfoutb == rf_min) ? div_min : div_max; + *muxa = (&rfouta == rf_min) ? mux_min : mux_max; + *muxb = (&rfoutb == rf_min) ? mux_min : mux_max; + + if(*diva < OUT_DIV_LOG2_MIN || *diva > OUT_DIV_LOG2_MAX || *divb < OUT_DIV_LOG2_MIN || *divb > OUT_DIV_LOG2_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RFOUT dividers out of range (DIV_A:%d DIV_B:%d) [should never happen]", *diva, *divb); + return -EINVAL; + } + + return 0; +} + +static int lmx2820_solver_validate_and_save(lmx2820_state_t* st, uint64_t rfouta, uint64_t rfoutb, uint8_t diva, uint8_t divb, uint8_t muxa, uint8_t muxb) +{ + double fvco = st->lmx2820_input_chain.fvco; + double rfa, rfb; + + switch(muxa) + { + case OUTA_MUX_VCO_DOUBLER: rfa = fvco * 2.0; break; + case OUTA_MUX_VCO: rfa = fvco; break; + case OUTA_MUX_CHANNEL_DIVIDER: rfa = fvco / ((unsigned)1 << diva); break; + default: + return -EINVAL; + } + switch(muxb) + { + case OUTA_MUX_VCO_DOUBLER: rfb = fvco * 2.0; break; + case OUTA_MUX_VCO: rfb = fvco; break; + case OUTA_MUX_CHANNEL_DIVIDER: rfb = fvco / ((unsigned)1 << divb); break; + default: + return -EINVAL; + } + + double rfa_delta = fabs(rfouta - rfa); + double rfb_delta = fabs(rfoutb - rfb); + + USDR_LOG("2820", USDR_LOG_DEBUG, "RF_A:%" PRIu64 "->%.6f (DIV:%u) Deviation:%.8fHz", rfouta, rfa, ((unsigned)1 << diva), rfa_delta); + USDR_LOG("2820", USDR_LOG_DEBUG, "RF_B:%" PRIu64 "->%.6f (DIV:%u) Deviation:%.8fHz", rfoutb, rfb, ((unsigned)1 << divb), rfb_delta); + + if(rfa_delta > RF_ACCURACY || rfb_delta > RF_ACCURACY) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RF tuning too rough"); + return -EINVAL; + } + + lmx2820_output_chain_t * outs = &st->lmx2820_output_chain; + outs->chdiva = diva; + outs->chdivb = divb; + outs->rfouta = rfa; + outs->rfoutb = rfb; + outs->outa_mux = muxa; + outs->outb_mux = muxb; + + return 0; +} + +static int lmx2820_calculate_systef_chain(lmx2820_state_t* st) +{ + if(!st->lmx2820_sysref_chain.enabled) + { + USDR_LOG("2820", USDR_LOG_INFO, "SYSREF disabled, skipping setup"); + return 0; + } + + lmx2820_sysref_chain_t* sr = &st->lmx2820_sysref_chain; + + if(st->lmx2820_input_chain.fosc_in > OSC_IN_MAX_SYNC) + { + USDR_LOG("2820", USDR_LOG_ERROR, "[SYSREF] OSC_IN:%" PRIu64" is too high (>%" PRIu64 ")", + st->lmx2820_input_chain.fosc_in, (uint64_t)OSC_IN_MAX_SYNC); + return -EINVAL; + } + + if(sr->master_mode == false) + { + switch(sr->srreq_fmt) + { + case SRREQ_CMOS: sr->srreq_fmt = SYSREF_INP_FMT_CMOS_INPUT_AT_SRREQ_P_PIN__1_8_V_TO_3_3_V_LOGIC; break; + case SRREQ_CMOS_AC: sr->srreq_fmt = SYSREF_INP_FMT_AC_COUPLE_CMOS_INPUT_AT_SRREQ_P_PIN; break; + case SRREQ_LVDS_AC: sr->srreq_fmt = SYSREF_INP_FMT_AC_COUPLED_DIFFERENTIAL_LVDS_INPUT__REQUIRES_EXTERNAL_100_OHM_DIFFERENTIAL_TERMINATION; break; + case SRREQ_LVDS_DC: sr->srreq_fmt = SYSREF_INP_FMT_DC_COUPLED_DIFFERENTIAL_LVDS_INPUT__REQUIRES_EXTERNAL_100_OHM_DIFFERENTIAL_TERMINATION; break; + default: + { + USDR_LOG("2820", USDR_LOG_ERROR, "[SYSREF] Invalid SRREG_FMT:%u", sr->srreq_fmt); + return -EINVAL; + } + } + } + + if(sr->cont_pulse == false && (sr->pulse_cnt < SYSREF_PULSE_CNT_MIN || sr->pulse_cnt > SYSREF_PULSE_CNT_MAX)) + { + USDR_LOG("2820", USDR_LOG_ERROR, "[SYSREF] PULSE_CNT:%u out of range [%u;%u]", + sr->pulse_cnt, (int)SYSREF_PULSE_CNT_MIN, (int)SYSREF_PULSE_CNT_MAX); + return -EINVAL; + } + + if(sr->delay_ctrl < SYSREF_DELAY_CTRL_MIN || sr->delay_ctrl > SYSREF_DELAY_CTRL_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "[SYSREF] DELAY_CTRL:%u out of range [%u;%u]", + sr->delay_ctrl, (int)SYSREF_DELAY_CTRL_MIN, (int)SYSREF_DELAY_CTRL_MAX); + return -EINVAL; + } + + const double fvco = st->lmx2820_input_chain.fvco; + + bool found = false; + double finterpolator; + for(uint8_t i = 3; i > 0; --i) + { + finterpolator = fvco / (1 << i); + if(finterpolator >= SYSREF_FINTERPOLATOR_MIN && finterpolator <= SYSREF_FINTERPOLATOR_MAX) + { + sr->div_pre = 1 << i; + found = true; + break; + } + } + + if(!found) + { + USDR_LOG("2820", USDR_LOG_ERROR, "[SYSREF] Cannot obtain Finterpolator:[%u;%u] having VCO:%.2f", + (unsigned)SYSREF_FINTERPOLATOR_MIN, (unsigned)SYSREF_FINTERPOLATOR_MAX, fvco); + return -EINVAL; + } + + double fdiv = finterpolator / 4; + unsigned div = (unsigned)(fdiv / sr->srout + 0.5f); + if(div < SYSREF_DIV_MIN || div > SYSREF_DIV_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "[SYSREF] Cannot obtain SROUT:%" PRIu64 " using SYSREF_DIV:[%u;%u]", + sr->srout, (int)SYSREF_DIV_MIN, (int)SYSREF_DIV_MAX); + return -EINVAL; + } + sr->div = div; + sr->srout_fact = fvco / sr->div_pre / sr->div / 4; + + lmx2820_fill_jesd(); + + double delay_per_inc = (double)sr->div_pre / 126.0 / fvco; + sr->delay = delay_per_inc * sr->delay_ctrl; + + USDR_LOG("2820", USDR_LOG_INFO, "[SYSREF] SROUT:%" PRIu64 " VCO:%.4f DIV_PRE:%u DIV:%u SROUT_FACT:%.4f DELAY_PER_INC:%.2fps DELAY:%.2fps", + sr->srout, fvco, sr->div_pre, sr->div, sr->srout_fact, + delay_per_inc * 1E12, sr->delay * 1E12); + + if((double)sr->srout != sr->srout_fact) + { + USDR_LOG("2820", USDR_LOG_WARNING, "[SYSREF] SROUT_FACT=%.4f differs from required SROUT, delta:%.4f", + sr->srout_fact, fabs((double)sr->srout - sr->srout_fact)); + } + + return 0; +} + +int lmx2820_solver(lmx2820_state_t* st, uint64_t osc_in, unsigned mash_order, unsigned force_mult, uint64_t rfouta, uint64_t rfoutb) +{ + int res = 0; + + if(osc_in < OSC_IN_MIN || osc_in > OSC_IN_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "OSC_IN %" PRIu64 " out of range [%" PRIu64 ";%" PRIu64 "]", osc_in, (uint64_t)OSC_IN_MIN, (uint64_t)OSC_IN_MAX); + return -EINVAL; + } + + switch(mash_order) + { + case MASH_ORDER_INTEGER_MODE: + case MASH_ORDER_FIRST_ORDER: + case MASH_ORDER_SECOND_ORDER: + case MASH_ORDER_THIRD_ORDER: break; + default: { + USDR_LOG("2820", USDR_LOG_ERROR, "MASH_ORDER %u out of range [%u;%u]", mash_order, (unsigned)MASH_ORDER_INTEGER_MODE, (unsigned)MASH_ORDER_THIRD_ORDER); + return -EINVAL; + } + } + + if(rfouta < OUT_FREQ_MIN || rfouta > OUT_FREQ_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RF_OUTA %" PRIu64 " out of range [%" PRIu64 ";%" PRIu64 "]", rfouta, (uint64_t)OUT_FREQ_MIN, (uint64_t)OUT_FREQ_MAX); + return -EINVAL; + } + if(rfoutb < OUT_FREQ_MIN || rfoutb > OUT_FREQ_MAX) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RF_OUTB %" PRIu64 " out of range [%" PRIu64 ";%" PRIu64 "]", rfoutb, (uint64_t)OUT_FREQ_MIN, (uint64_t)OUT_FREQ_MAX); + return -EINVAL; + } + + uint64_t vco; + uint8_t diva, divb, muxa, muxb; + + res = lmx2820_solver_prepare(rfouta, rfoutb, &vco, &diva, &divb, &muxa, &muxb); + if(res) + return res; + + res = lmx2820_calculate_input_chain(st, osc_in, vco, mash_order, force_mult); + if(res) + return res; + + res = lmx2820_calculate_systef_chain(st); + if(res) + return res; + + res = lmx2820_solver_validate_and_save(st, rfouta, rfoutb, diva, divb, muxa, muxb); + if(res) + return res; + + lmx2820_output_chain_t * outs = &st->lmx2820_output_chain; + + USDR_LOG("2820", USDR_LOG_INFO, "***** SOLUTION *****"); + USDR_LOG("2820", USDR_LOG_INFO, "VCO:%.2f OSC_IN:%" PRIu64 " MASH_ORDER:%u VCO_CORE%u", st->lmx2820_input_chain.fvco, osc_in, mash_order, st->lmx2820_input_chain.vco_core); + USDR_LOG("2820", USDR_LOG_INFO, "CH_A - RF:%.2f DIV:%u(%u) MUX:%s", outs->rfouta, outs->chdiva - 1, (1 << outs->chdiva), + outs->outa_mux == OUTA_MUX_VCO_DOUBLER ? "OUTA_MUX_VCO_DOUBLER" : (outs->outa_mux == OUTA_MUX_VCO ? "OUTA_MUX_VCO": "OUTA_MUX_CHANNEL_DIVIDER")); + USDR_LOG("2820", USDR_LOG_INFO, "CH_B - RF:%.2f DIV:%u(%u) MUX:%s", outs->rfoutb, outs->chdivb - 1, (1 << outs->chdivb), + outs->outb_mux == OUTB_MUX_VCO_DOUBLER ? "OUTB_MUX_VCO_DOUBLER" : (outs->outb_mux == OUTB_MUX_VCO ? "OUTB_MUX_VCO": "OUTB_MUX_CHANNEL_DIVIDER")); + + return 0; +} + +int lmx2820_solver_instcal(lmx2820_state_t* st, uint64_t rfouta, uint64_t rfoutb) +{ + int res = 0; + + unsigned mash_order = st->lmx2820_input_chain.mash_order; + if(mash_order > MASH_ORDER_THIRD_ORDER) + { + USDR_LOG("2820", USDR_LOG_ERROR, "MASH_ORDER:%u out of range, maybe uninitialized", mash_order); + return -EINVAL; + } + + double fpd = st->lmx2820_input_chain.fpd; + if(fpd < FPD_MIN || fpd > FPD_MAX[mash_order]) + { + USDR_LOG("2820", USDR_LOG_ERROR, "FPD:%.2f out of range, maybe uninitialized", fpd); + return -EINVAL; + } + + const double rfmax = MIN(fpd * (PLL_N_MAX + 1) * 2.0, OUT_FREQ_MAX); + const double rfmin = MAX(fpd * PLL_N_MIN / (1 << OUT_DIV_LOG2_MAX), OUT_FREQ_MIN); + + if(rfouta < rfmin || rfouta > rfmax) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RF_A:%" PRIu64 " out of range [%.2f;%.2f] for FPD:%.2f", rfouta, rfmin, rfmax, fpd); + return -EINVAL; + } + if(rfoutb < rfmin || rfoutb > rfmax) + { + USDR_LOG("2820", USDR_LOG_ERROR, "RF_B:%" PRIu64 " out of range [%.2f;%.2f] for FPD:%.2f", rfoutb, rfmin, rfmax, fpd); + return -EINVAL; + } + + uint64_t vco; + uint8_t diva, divb, muxa, muxb; + + res = lmx2820_solver_prepare(rfouta, rfoutb, &vco, &diva, &divb, &muxa, &muxb); + if(res) + return res; + + res = lmx2820_tune_vco(st, vco); + if(res) + return res; + + res = lmx2820_solver_validate_and_save(st, rfouta, rfoutb, diva, divb, muxa, muxb); + if(res) + return res; + + lmx2820_output_chain_t * outs = &st->lmx2820_output_chain; + + USDR_LOG("2820", USDR_LOG_INFO, "***** INSTCAL SOLUTION *****"); + USDR_LOG("2820", USDR_LOG_INFO, "VCO:%.2f", st->lmx2820_input_chain.fvco); + USDR_LOG("2820", USDR_LOG_INFO, "CH_A - RF:%.2f DIV:%u(%u) MUX:%s", outs->rfouta, outs->chdiva - 1, (1 << outs->chdiva), + outs->outa_mux == OUTA_MUX_VCO_DOUBLER ? "OUTA_MUX_VCO_DOUBLER" : (outs->outa_mux == OUTA_MUX_VCO ? "OUTA_MUX_VCO": "OUTA_MUX_CHANNEL_DIVIDER")); + USDR_LOG("2820", USDR_LOG_INFO, "CH_B - RF:%.2f DIV:%u(%u) MUX:%s", outs->rfoutb, outs->chdivb - 1, (1 << outs->chdivb), + outs->outb_mux == OUTB_MUX_VCO_DOUBLER ? "OUTB_MUX_VCO_DOUBLER" : (outs->outb_mux == OUTB_MUX_VCO ? "OUTB_MUX_VCO": "OUTB_MUX_CHANNEL_DIVIDER")); + + return 0; +} + +static uint8_t lmx2820_encode_sysref_prediv(lmx2820_state_t* st) +{ + switch(st->lmx2820_sysref_chain.div_pre) + { + case SYSREF_DIV_PRE_MAX: return 4; + case SYSREF_DIV_PRE_MID: return 2; + case SYSREF_DIV_PRE_MIN: return 1; + } + USDR_LOG("2820", USDR_LOG_ERROR, "lmx2820_encode_sysref_prediv() unsupported value!"); + return 0; +} + +static int lmx2820_tune_internal(lmx2820_state_t* st, uint64_t osc_in, unsigned mash_order, unsigned force_mult, uint64_t rfouta, uint64_t rfoutb, bool use_instcal) +{ + int res = lmx2820_solver(st, osc_in, mash_order, force_mult, rfouta, rfoutb); + if(res) + { + USDR_LOG("2820", USDR_LOG_ERROR, "lmx2820_solver() failed, err:%d", res); + return res; + } + + uint8_t HP_fd_adj, LP_fd_adj; + double fpd = st->lmx2820_input_chain.fpd; + + if(fpd < 2500000) + LP_fd_adj = FCAL_LPFD_ADJ_FPD_LT_2_5_MHZ; + else if(fpd < 5000000) + LP_fd_adj = FCAL_LPFD_ADJ_5_MHZ_GT_FPD_GE_2_5_MHZ; + else if(fpd < 10000000) + LP_fd_adj = FCAL_LPFD_ADJ_10_MHZ_GT_FPD_GE_5_MHZ; + else + LP_fd_adj = FCAL_LPFD_ADJ_FPD_GE_10_MHZ; + + if(fpd <= 100000000) + HP_fd_adj = FCAL_HPFD_ADJ_FPD_LE_100_MHZ; + else if(fpd <= 150000000) + HP_fd_adj = FCAL_HPFD_ADJ_100_MHZ_LT_FPD_LE_150_MHZ; + else if(fpd <= 200000000) + HP_fd_adj = FCAL_HPFD_ADJ_150_MHZ_LT_FPD_LE_200_MHZ; + else + HP_fd_adj = FCAL_HPFD_ADJ_FPD_GT_200_MHZ; + + uint8_t cal_clk_div; + if(osc_in <= 200000000) + cal_clk_div = CAL_CLK_DIV_FOSCIN_LE_200_MHZ; + else if(osc_in <= 400000000) + cal_clk_div = CAL_CLK_DIV_FOSCIN_LE_400_MHZ; + else if(osc_in <= 800000000) + cal_clk_div = CAL_CLK_DIV_FOSCIN_LE_800_MHZ; + else + cal_clk_div = CAL_CLK_DIV_ALL_OTHER_FOSCIN_VALUES; + + uint16_t instcal_dly = 0x1f4; + uint32_t instcal_pll_num = 0; + uint8_t instcal_dblr_en = INSTCAL_DBLR_EN_NORMAL_OPERATION; + + if(use_instcal) + { + instcal_dly = (uint16_t)((2.5f * C_BIAS / 0.47) * ((double)osc_in / 1E6) / (1 << cal_clk_div) + 0.5); + instcal_pll_num = (double)((uint64_t)1 << 32) * ((double)st->lmx2820_input_chain.pll_num / st->lmx2820_input_chain.pll_den); + instcal_dblr_en = INSTCAL_DBLR_EN_VCO_DOUBLER_IS_ENGAGED; + } + + st->instcal_dly = instcal_dly; //store it for instcal triggering + + USDR_LOG("2820", USDR_LOG_DEBUG, "REGS> LP_FD_ADJ:%d HP_FD_ADJ:%d CAL_CLK_DIV:%d INSTCAL_DLY:%d INSTCAL_PLL_NUM:%u INSTCAL_DBLR_EN:%d", + LP_fd_adj, HP_fd_adj, cal_clk_div, instcal_dly, instcal_pll_num, instcal_dblr_en); + + const lmx2820_sysref_chain_t* sr = &st->lmx2820_sysref_chain; + + uint32_t regs[] = + { + MAKE_LMX2820_R79(0, OUTB_PD_NORMAL_OPERATION, 0, st->lmx2820_output_chain.outb_mux, 0x7, 0), + MAKE_LMX2820_R78(0, OUTA_PD_NORMAL_OPERATION, 0, st->lmx2820_output_chain.outa_mux), + MAKE_LMX2820_R69(0, sr->enabled ? SROUT_PD_NORMAL_OPERATION : SROUT_PD_POWER_DOWN, 1), + MAKE_LMX2820_R68(0, 0/*psync pin ignore*/, 0, 0), + + sr->enabled ? + MAKE_LMX2820_R67(sr->cont_pulse ? 1 : sr->pulse_cnt, + JESD[sr->delay_ctrl].DAC4_CTRL, + JESD[sr->delay_ctrl].DAC3_CTRL) + : + MAKE_LMX2820_R67(0,0,0), //default + + sr->enabled ? + MAKE_LMX2820_R66(0x0, + JESD[sr->delay_ctrl].DAC2_CTRL, + JESD[sr->delay_ctrl].DAC1_CTRL) + : + MAKE_LMX2820_R66(0,0,0x3F), //default + + sr->enabled ? + MAKE_LMX2820_R65(0x0, sr->div - 2) + : + MAKE_LMX2820_R65(0,1), //default + + sr->enabled ? + MAKE_LMX2820_R64(0x00, //0x10 by doc + sr->master_mode ? 0x2 : sr->srreq_fmt, + lmx2820_encode_sysref_prediv(st), + 0, /*sysref_repeat_ns*/ + sr->cont_pulse ? 0 : 1, + 1, /*sysref enabled*/ + sr->master_mode ? 0 : 1, + 0x0) + : + MAKE_LMX2820_R64(0x10, 0, 0x4, 0, 0, 0/*sysref disabled*/, 0, 0x0), //default + + MAKE_LMX2820_R45((uint16_t)instcal_pll_num), + MAKE_LMX2820_R44((uint16_t)(instcal_pll_num >> 16)), + MAKE_LMX2820_R43((uint16_t)st->lmx2820_input_chain.pll_num), + MAKE_LMX2820_R42((uint16_t)(st->lmx2820_input_chain.pll_num >> 16)), + MAKE_LMX2820_R39((uint16_t)st->lmx2820_input_chain.pll_den), + MAKE_LMX2820_R38((uint16_t)(st->lmx2820_input_chain.pll_den >> 16)), + MAKE_LMX2820_R36(0, st->lmx2820_input_chain.pll_n), + MAKE_LMX2820_R35(1, MASH_RESET_N_NORMAL_OPERATION, 0, st->lmx2820_input_chain.mash_order, MASHSEED_EN_DISABLED, 0), + MAKE_LMX2820_R32(1, st->lmx2820_output_chain.chdivb - 1, st->lmx2820_output_chain.chdiva - 1, 1), + MAKE_LMX2820_R22(st->lmx2820_input_chain.vco_core, 0x2, 0xBF), + MAKE_LMX2820_R14(0x3, st->lmx2820_input_chain.pll_r_pre), + MAKE_LMX2820_R13(0, st->lmx2820_input_chain.pll_r, 0x18), + MAKE_LMX2820_R12(0, st->lmx2820_input_chain.mult, 0x8), + MAKE_LMX2820_R11(0x30, st->lmx2820_input_chain.osc_2x ? 1 : 0, 0x3), //0x3 by doc + MAKE_LMX2820_R2 (1, cal_clk_div, instcal_dly, QUICK_RECAL_EN_DISABLED), + MAKE_LMX2820_R1 (sr->enabled ? PHASE_SYNC_EN_PHASE_SYNCHRONIZATION_ENABLED : PHASE_SYNC_EN_NORMAL_OPERATION, + 0x15E, + LD_VTUNE_EN_VCOCAL_AND_VTUNE_LOCK_DETECT, + 0, + instcal_dblr_en, + use_instcal ? INSTCAL_EN_ENABLED : INSTCAL_EN_DISABLED), + MAKE_LMX2820_R0 (1, 1, 0, HP_fd_adj, LP_fd_adj, DBLR_CAL_EN_ENABLED, 1, FCAL_EN_ENABLED, 0, RESET_NORMAL_OPERATION, POWERDOWN_NORMAL_OPERATION) + }; + + res = lmx2820_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Registers set lmx2820_spi_post() failed, err:%d", res); + return res; + } + + //Wait 10 ms to allow the internal LDOs to power up. + usleep(10000); + + res = lmx2820_wait_pll_lock(st, 1000000); + if(res) + { + USDR_LOG("2820", USDR_LOG_ERROR, "lmx2820_wait_pll_lock() failed, err:%d [%s]", + res, (res == -ETIMEDOUT ? "TIMEOUT" : "ERROR")); + return res; + } + + if(use_instcal) + { + res = lmx2820_calibrate(st, false); + if(res) + { + USDR_LOG("2820", USDR_LOG_ERROR, "lmx2820_calibrate(0) failed, err:%d", res); + return res; + } + } + + return 0; +} + +/* + * Full tuning, including input circuit calculation OSC_IN->FPD->VCO->RFOUT + */ +int lmx2820_tune(lmx2820_state_t* st, uint64_t osc_in, unsigned mash_order, unsigned force_mult, uint64_t rfouta, uint64_t rfoutb) +{ + return lmx2820_tune_internal(st, osc_in, mash_order, force_mult, rfouta, rfoutb, false); +} + + +/* + * Initialize LMX for instant calibration + */ +int lmx2820_instant_calibration_init(lmx2820_state_t* st, uint64_t osc_in, unsigned mash_order, unsigned force_mult) +{ + int res = lmx2820_tune_internal(st, osc_in, mash_order, force_mult, VCO_MIN, VCO_MIN, true); + if(res) + return res; + + USDR_LOG("2820", USDR_LOG_DEBUG, "Device is initialized for the particular phase detector frequency = %.2f", st->lmx2820_input_chain.fpd); + + return 0; +} + +/* + * Instant tuning - phase detector freq == invar. + * Func lmx2820_instant_calibration_init MUST be called formerly. + */ +int lmx2820_tune_instcal(lmx2820_state_t* st, uint64_t rfouta, uint64_t rfoutb) +{ + int res = lmx2820_solver_instcal(st, rfouta, rfoutb); + if(res) + return res; + + const uint32_t instcal_pll_num = (double)((uint64_t)1 << 32) * ((double)st->lmx2820_input_chain.pll_num / st->lmx2820_input_chain.pll_den); + + uint16_t r0; + res = lmx2820_spi_get(st, R0, &r0); + if(res) + return res; + + r0 &= ~INSTCAL_R0_MASK; + + uint32_t regs[] = + { + MAKE_LMX2820_R79(0, OUTB_PD_NORMAL_OPERATION, 0, st->lmx2820_output_chain.outb_mux, 0x7, 0), + MAKE_LMX2820_R78(0, OUTA_PD_NORMAL_OPERATION, 0, st->lmx2820_output_chain.outa_mux), + MAKE_LMX2820_R45((uint16_t)instcal_pll_num), + MAKE_LMX2820_R44((uint16_t)(instcal_pll_num >> 16)), + MAKE_LMX2820_R43((uint16_t)st->lmx2820_input_chain.pll_num), + MAKE_LMX2820_R42((uint16_t)(st->lmx2820_input_chain.pll_num >> 16)), + MAKE_LMX2820_R39((uint16_t)st->lmx2820_input_chain.pll_den), + MAKE_LMX2820_R38((uint16_t)(st->lmx2820_input_chain.pll_den >> 16)), + MAKE_LMX2820_R36(0, st->lmx2820_input_chain.pll_n), + MAKE_LMX2820_R32(1, st->lmx2820_output_chain.chdivb - 1, st->lmx2820_output_chain.chdiva - 1, 1), + MAKE_LMX2820_REG_WR(R0, r0) + }; + + res = lmx2820_spi_post(st, regs, SIZEOF_ARRAY(regs)); + if(res) + { + USDR_LOG("2820", USDR_LOG_ERROR, "Registers set lmx2820_spi_post() failed, err:%d", res); + return res; + } + + //no need to wait PLL lock, but should wait instcal_dly us + usleep(st->instcal_dly); + + return 0; +} diff --git a/src/lib/hw/lmx2820/lmx2820.h b/src/lib/hw/lmx2820/lmx2820.h new file mode 100644 index 00000000..aa1345ac --- /dev/null +++ b/src/lib/hw/lmx2820/lmx2820.h @@ -0,0 +1,95 @@ +#ifndef LMX2820_H +#define LMX2820_H + +#include + +struct lmx2820_input_chain_st +{ + uint8_t mash_order; + uint8_t vco_core; + uint64_t fosc_in; + bool osc_2x; + uint16_t pll_r_pre; + uint8_t mult; + uint8_t pll_r; + uint16_t pll_n; + uint32_t pll_num; + uint32_t pll_den; + double fvco; + double fpd; +}; +typedef struct lmx2820_input_chain_st lmx2820_input_chain_t; + +struct lmx2820_output_chain_st +{ + uint8_t chdiva; + uint8_t chdivb; + uint8_t outa_mux; + uint8_t outb_mux; + double rfouta; + double rfoutb; +}; +typedef struct lmx2820_output_chain_st lmx2820_output_chain_t; + +enum lmx2820_sysref_in_fmt +{ + SRREQ_CMOS = 0, + SRREQ_CMOS_AC = 1, + SRREQ_LVDS_AC = 2, + SRREQ_LVDS_DC = 3, +}; +typedef enum lmx2820_sysref_in_fmt lmx2820_sysref_in_fmt_t; + +struct lmx2820_sysref_chain_st +{ + uint64_t srout; + bool enabled; + bool master_mode; //true:master mode, false:repeater mode + bool cont_pulse; //true:continious, false:pulsed + uint8_t pulse_cnt; + uint8_t delay_ctrl; + double delay; + uint8_t div_pre; + uint16_t div; + double srout_fact; + // + uint8_t srreq_fmt; +}; +typedef struct lmx2820_sysref_chain_st lmx2820_sysref_chain_t; + +struct lmx2820_stats { + float temperature; + uint16_t vco_sel; + uint16_t vco_capctrl; + uint16_t vco_daciset; + uint16_t lock_detect_status; +}; +typedef struct lmx2820_stats lmx2820_stats_t; + +struct lmx2820_state { + lldev_t dev; + unsigned subdev; + unsigned lsaddr; + + uint16_t instcal_dly; + lmx2820_input_chain_t lmx2820_input_chain; + lmx2820_output_chain_t lmx2820_output_chain; + lmx2820_sysref_chain_t lmx2820_sysref_chain; +}; +typedef struct lmx2820_state lmx2820_state_t; + +int lmx2820_solver(lmx2820_state_t* st, uint64_t osc_in, unsigned mash_order, unsigned force_mult, uint64_t rfouta, uint64_t rfoutb); +int lmx2820_solver_instcal(lmx2820_state_t* st, uint64_t rfouta, uint64_t rfoutb); + +int lmx2820_create(lldev_t dev, unsigned subdev, unsigned lsaddr, lmx2820_state_t* st); +int lmx2820_destroy(lmx2820_state_t* st); +int lmx2820_get_temperature(lmx2820_state_t* st, float* value); +int lmx2820_read_status(lmx2820_state_t* st, lmx2820_stats_t* status); +int lmx2820_tune(lmx2820_state_t* st, uint64_t osc_in, unsigned mash_order, unsigned force_mult, uint64_t rfouta, uint64_t rfoutb); +int lmx2820_instant_calibration_init(lmx2820_state_t* st, uint64_t osc_in, unsigned mash_order, unsigned force_mult); +int lmx2820_tune_instcal(lmx2820_state_t* st, uint64_t rfouta, uint64_t rfoutb); +int lmx2820_sync(lmx2820_state_t* st); +int lmx2820_reset(lmx2820_state_t* st); +int lmx2820_wait_pll_lock(lmx2820_state_t* st, unsigned timeout); + +#endif // LMX2820_H diff --git a/src/lib/hw/lmx2820/lmx2820.yaml b/src/lib/hw/lmx2820/lmx2820.yaml new file mode 100644 index 00000000..8e4934a6 --- /dev/null +++ b/src/lib/hw/lmx2820/lmx2820.yaml @@ -0,0 +1,1719 @@ +# Copyright (c) 2025 Wavelet Lab +# SPDX-License-Identifier: MIT + +name: LMX2820 +revision: 0.0.1 +processors: [ c ] +bus: + type: SPI + rd_mask: 0x800000 + usdr_path: /debug/hw/lmx2820/*/reg +addr_width: 8 +data_width: 16 + +pages: +- name: Main + regs: + - addr: 0x0 + name: R0 + fields: + - bits: 0 + name: POWERDOWN + mode: RW + dflt: 0x0 + desc: Powers down the device. + opts: + 0b0: Normal operation + 0b1: Power down + - bits: 1 + name: RESET + mode: RW + dflt: 0x0 + desc: Resets all registers to silicon default values. This bit is self-clearing. + opts: + 0b0: Normal operation + 0b1: Reset + - bits: '3:2' + name: R0_RESERVED_3 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 4 + name: FCAL_EN + mode: RW + dflt: 0x1 + desc: Enables and activates VCO calibration. Writing register R0 with this bit set to a 1 enables and triggers VCO calibration. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: 5 + name: R0_RESERVED_2 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - bits: 6 + name: DBLR_CAL_EN + mode: RW + dflt: 0x1 + desc: Enables VCO doubler calibration. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '8:7' + name: FCAL_LPFD_ADJ + mode: RW + dflt: 0x0 + desc: "Set this field in accordance to the phase detector frequency for optimal VCO calibration." + opts: + 0x0: fPD >= 10 MHz + 0x1: 10 MHz > fPD >= 5 MHz + 0x2: 5 MHz > fPD >= 2.5 MHz + 0x3: fPD < 2.5 MHz + - bits: '10:9' + name: FCAL_HPFD_ADJ + mode: RW + dflt: 0x0 + desc: "Set this field in accordance to the phase detector frequency for optimal VCO calibration." + opts: + 0x0: fPD <= 100 MHz + 0x1: 100 MHz < fPD <= 150 MHz + 0x2: 150 MHz < fPD <= 200 MHz + 0x3: fPD > 200 MHz + - bits: '12:11' + name: R0_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 13 + name: INSTCAL_SKIP_ACAL + mode: RW + dflt: 0x0 + desc: Disable this bit when doing instant calibration. When not using instant calibration, it is recommended to enable it for faster VCO Calibration. + - bits: '15:14' + name: R0_RESERVED_0 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - addr: 0x1 + name: R1 + fields: + - bits: 0 + name: INSTCAL_EN + mode: RW + dflt: 0x0 + desc: Enables instant calibration. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: 1 + name: INSTCAL_DBLR_EN + mode: RW + dflt: 0x0 + desc: Sets this bit to 1 if VCO doubler is engaged. + opts: + 0b0: Normal operation + 0b1: VCO doubler is engaged + - bits: '4:2' + name: R1_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 5 + name: LD_VTUNE_EN + mode: RW + dflt: 0x1 + desc: Selects lock detect type. VCOCal lock detect asserts a HIGH output after the VCO has finished calibration and the LD_DLY timeout counter is finished. VCOCal and Vtune lock detect asserts a HIGH output when VCOCal lock detect would assert a signal and the tuning voltage to the VCO is within acceptable limits. + opts: + 0b0: VCOCal lock detect + 0b1: VCOCal and Vtune lock detect + - bits: '14:6' + name: R1_RESERVED_0 + mode: RW + dflt: 0x15E + desc: Program 0x15E to this field. + - bits: 15 + name: PHASE_SYNC_EN + mode: RW + dflt: 0x0 + desc: Enables phase synchronization. A Low-High-Low pulse is required at the PSYNC pin to trigger synchronization. Enable SYSREF requires PHASE_SYNC_EN = 1. + opts: + 0b0: Normal operation + 0b1: Phase synchronization enabled + - addr: 0x2 + name: R2 + fields: + - bits: 0 + name: QUICK_RECAL_EN + mode: RW + dflt: 0x0 + desc: Starts VCO calibration with the current VCO_SEL, VCO_CAPCTRL and VCO_DACISET values. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '11:1' + name: INSTCAL_DLY + mode: RW + dflt: 0x1F4 + desc: "Sets the wait time for instant calibration. INSTCAL_DLY = T x fOSCIN / (2CAL_CLK_DIV). T = 2.5 x CBIASVCO / 4.7 mcF. CBIASVCO is the bypass capacitor at pin 3." + - bits: '14:12' + name: CAL_CLK_DIV + mode: RW + dflt: 0x3 + desc: "Divides down the state machine clock (fsm) during VCO calibration. Maximum fsm is 200 MHz. fsm = fOSCIN / (2CAL_CLK_DIV)." + opts: + 0x0: fOSCIN <= 200 MHz + 0x1: fOSCIN <= 400 MHz + 0x2: fOSCIN <= 800 MHz + 0x3: All other fOSCIN values + - bits: 15 + name: R2_RESERVED_0 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - addr: 0x3 + name: R3 + fields: + - bits: '15:0' + name: R3_RESERVED_0 + mode: RW + dflt: 0x41 + desc: Program 0x41 to this field. + - addr: 0x4 + name: R4 + fields: + - bits: '15:0' + name: R4_RESERVED_0 + mode: RW + dflt: 0x4204 + desc: Program 0x4204 to this field. + - addr: 0x5 + name: R5 + fields: + - bits: '15:0' + name: R5_RESERVED_0 + mode: RW + dflt: 0x3832 + desc: Program 0x32 to this field. + - addr: 0x6 + name: R6 + fields: + - bits: '7:0' + name: R6_RESERVED_0 + mode: RW + dflt: 0x43 + desc: Program 0x43 to this field. + - bits: '15:8' + name: ACAL_CMP_DLY + mode: RW + dflt: 0xA + desc: VCO amplitude calibration delay. Lowering this value can speed up calibration time. If too low, phase noise may not be optimal due to insufficient time to reach final calibrated amplitude. Delay time = ACAL_CMP_DLY x 2 x state machine clock cycle. + - addr: 0x7 + name: R7 + fields: + - bits: '15:0' + name: R7_RESERVED_0 + mode: RW + dflt: 0xC8 + desc: Program 0x0 to this field. + - addr: 0x8 + name: R8 + fields: + - bits: '15:0' + name: R8_RESERVED_0 + mode: RW + dflt: 0xC802 + desc: Program 0xC802 to this field. + - addr: 0x9 + name: R9 + fields: + - bits: '15:0' + name: R9_RESERVED_0 + mode: RW + dflt: 0x5 + desc: Program 0x5 to this field. + - addr: 0xA + name: R10 + fields: + - bits: '6:0' + name: R10_RESERVED_2 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 7 + name: VCO_CAPCTRL_FORCE + mode: RW + dflt: 0x0 + desc: Forces the VCO to use the sub-band specified by VCO_CAPCTRL. Useful for full-assisted VCO calibration and debugging purposes. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '10:8' + name: R10_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 11 + name: VCO_DACISET_FORCE + mode: RW + dflt: 0x0 + desc: Forces the VCO to use the current setting specified by VCO_DACISET. Useful for full-assisted VCO calibration and debugging purposes. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: 12 + name: PFD_DLY_MANUAL + mode: RW + dflt: 0x0 + desc: Enables manual PFD_DLY adjustment. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '15:13' + name: R10_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0xB + name: R11 + fields: + - bits: '3:0' + name: R11_RESERVED_1 + mode: RW + dflt: 0x3 + desc: Program 0x2 to this field. + - bits: 4 + name: OSC_2X + mode: RW + dflt: 0x0 + desc: Enables reference input doubler. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '15:5' + name: R11_RESERVED_0 + mode: RW + dflt: 0x30 + desc: Program 0x30 to this field. + - addr: 0xC + name: R12 + fields: + - bits: '9:0' + name: R12_RESERVED_1 + mode: RW + dflt: 0x8 + desc: Program 0x8 to this field. + - bits: '12:10' + name: MULT + mode: RW + dflt: 0x1 + desc: Sets reference path frequency multiplier value. + opts: + 0x1: Bypassed + 0x3: x3 + 0x4: x4 + 0x5: x5 + 0x6: x6 + 0x7: x7 + - bits: '15:13' + name: R12_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0xD + name: R13 + fields: + - bits: '4:0' + name: R13_RESERVED_1 + mode: RW + dflt: 0x18 + desc: Program 0x18 to this field. + - bits: '12:5' + name: PLL_R + mode: RW + dflt: 0x1 + desc: Sets reference path Post-R divider value. + - bits: '15:13' + name: R13_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0xE + name: R14 + fields: + - bits: '11:0' + name: PLL_R_PRE + mode: RW + dflt: 0x1 + desc: Sets reference path Pre-R divider value. + - bits: '15:12' + name: R14_RESERVED_0 + mode: RW + dflt: 0x3 + desc: Program 0x3 to this field. + - addr: 0xF + name: R15 + fields: + - bits: '8:0' + name: R15_RESERVED_1 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - bits: '10:9' + name: PFD_SINGLE + mode: RW + dflt: 0x0 + desc: Uses single PFD when PFDIN input is enabled. The actual charge pump current is equal to half the current setting made in CPG. + opts: + 0x0: Normal operation + 0x3: Single PFD + - bits: 11 + name: PFD_POL + mode: RW + dflt: 0x0 + desc: Sets the polarity of phase detector. Internal VCO operation requires negative Vtune with non-inverting loop filter. + opts: + 0b0: Negative Vtune + 0b1: Positive Vtune + - bits: '15:12' + name: R15_RESERVED_0 + mode: RW + dflt: 0x2 + desc: Program 0x2 to this field. + - addr: 0x10 + name: R16 + fields: + - bits: 0 + name: R16_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: '4:1' + name: CPG + mode: RW + dflt: 0xE + desc: Sets charge pump gain value. + opts: + 0: Tri-state + 1: 1.4 mA + 4: 5.6 mA + 5: 7 mA + 6: 11.2 mA + 7: 12.6 mA + 8: 2.8 mA + 9: 4.2 mA + 12: 8.4 mA + 13: 9.8 mA + 14: 14 mA + 15: 15.4 mA + - bits: '15:5' + name: R16_RESERVED_0 + mode: RW + dflt: 0x138 + desc: Program 0xB8 to this field. + - addr: 0x11 + name: R17 + fields: + - bits: '15:7' + name: R17_RESERVED_1 + mode: RW + dflt: 0x28 + desc: Program 0x2B to this field. + - bits: 6 + name: LD_TYPE + mode: RW + dflt: 0x1 + desc: Defines lock detect monitor type. One-Shot detects lock only after the VCO calibrates and the LD_DLY timeout counter is finished. Continuous lock detect checks for lock all the time, including when the input reference is removed. + opts: + 0b0: One-Shot + 0b1: Continuous + - bits: '5:0' + name: R17_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x12 + name: R18 + fields: + - bits: '15:0' + name: LD_DLY + mode: RW + dflt: 0x3E8 + desc: Lock detect assertion delay. This is the delay that is added after the VCO calibration is completed before indicating lock. This delay is only applied if LD_VTUNE_EN = 1. Delay time = LD_DLY / fPD. + - addr: 0x13 + name: R19 + fields: + - bits: '2:0' + name: R19_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: '4:3' + name: TEMPSENSE_EN + mode: RW + dflt: 0x0 + desc: Enables temperature sensor. + opts: + 0x0: Disabled + 0x3: Enabled + - bits: '15:5' + name: R19_RESERVED_0 + mode: RW + dflt: 0x109 + desc: Program 0x109 to this field. + - addr: 0x14 + name: R20 + fields: + - bits: '8:0' + name: VCO_DACISET + mode: RW + dflt: 0x12C + desc: User specified start VCO current setting for calibration. Unless QUICK_RECAL_EN = 1, VCO calibration will always start with the VCO current setting that is specified in this field. + - bits: '15:9' + name: R20_RESERVED_0 + mode: RW + dflt: 0x13 + desc: Program 0x13 to this field. + - addr: 0x15 + name: R21 + fields: + - bits: '15:0' + name: R21_RESERVED_0 + mode: RW + dflt: 0x1C64 + desc: Program 0x1C64 to this field. + - addr: 0x16 + name: R22 + fields: + - bits: '7:0' + name: VCO_CAPCTRL + mode: RW + dflt: 0xBF + desc: User specified start VCO sub-band for calibration. Valid values are 191 to 0, where the higher number represents a lower frequency band. Unless QUICK_RECAL_EN = 1, VCO calibration will always start with the VCO sub-band that is specified in this field. + - bits: '12:8' + name: R22_RESERVED_0 + mode: RW + dflt: 0x2 + desc: Program 0x2 to this field. + - bits: '15:13' + name: VCO_SEL + mode: RW + dflt: 0x7 + desc: User specified start VCO core for calibration. Unless QUICK_RECAL_EN = 1, VCO calibration will always start with the VCO core that is specified in this field. + opts: + 0x1: VCO1 + 0x2: VCO2 + 0x3: VCO3 + 0x4: VCO4 + 0x5: VCO5 + 0x6: VCO6 + 0x7: VCO7 + - addr: 0x17 + name: R23 + fields: + - bits: 0 + name: VCO_SEL_FORCE + mode: RW + dflt: 0x0 + desc: Forces the VCO to use the core specified by VCO_SEL. Useful for full-assisted VCO calibration and debugging purposes. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '15:1' + name: R23_RESERVED_0 + mode: RW + dflt: 0x881 + desc: Program 0x881 to this field. + - addr: 0x18 + name: R24 + fields: + - bits: '15:0' + name: R24_RESERVED_0 + mode: RW + dflt: 0xE34 + desc: Program 0xE34 to this field. + - addr: 0x19 + name: R25 + fields: + - bits: '15:0' + name: R25_RESERVED_0 + mode: RW + dflt: 0x624 + desc: Program 0x624 to this field. + - addr: 0x1A + name: R26 + fields: + - bits: '15:0' + name: R26_RESERVED_0 + mode: RW + dflt: 0xDB0 + desc: Program 0xDB0 to this field. + - addr: 0x1B + name: R27 + fields: + - bits: '15:0' + name: R27_RESERVED_0 + mode: RW + dflt: 0x8001 + desc: Program 0x8001 to this field. + - addr: 0x1C + name: R28 + fields: + - bits: '15:0' + name: R28_RESERVED_0 + mode: RW + dflt: 0x639 + desc: Program 0x639 to this field. + - addr: 0x1D + name: R29 + fields: + - bits: '15:0' + name: R29_RESERVED_0 + mode: RW + dflt: 0x318C + desc: Program 0x318C to this field. + - addr: 0x1E + name: R30 + fields: + - bits: '15:0' + name: R30_RESERVED_0 + mode: RW + dflt: 0xB18C + desc: Program 0xB18C to this field. + - addr: 0x1F + name: R31 + fields: + - bits: '15:0' + name: R31_RESERVED_0 + mode: RW + dflt: 0x401 + desc: Program 0x401 to this field. + - addr: 0x20 + name: R32 + fields: + - bits: '5:0' + name: R32_RESERVED_1 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - bits: '8:6' + name: CHDIVA + mode: RW + dflt: 0x0 + desc: Sets divider value for RFOUTA. + opts: + 0x0: 0x0 = Divide by 2 + 0x1: 0x1 = Divide by 4 + 0x2: 0x2 = Divide by 8 + 0x3: 0x3 = Divide by 16 + 0x4: 0x4 = Divide by 32 + 0x5: 0x5 = Divide by 64 + 0x6: 0x6 = Divide by 128 + - bits: '11:9' + name: CHDIVB + mode: RW + dflt: 0x0 + desc: Sets divider value for RFOUTB. + opts: + 0x0: 0x0 = Divide by 2 + 0x1: 0x1 = Divide by 4 + 0x2: 0x2 = Divide by 8 + 0x3: 0x3 = Divide by 16 + 0x4: 0x4 = Divide by 32 + 0x5: 0x5 = Divide by 64 + 0x6: 0x6 = Divide by 128 + - bits: '15:12' + name: R32_RESERVED_0 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - addr: 0x21 + name: R33 + fields: + - bits: '15:0' + name: R33_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x22 + name: R34 + fields: + - bits: 0 + name: EXTVCO_EN + mode: RW + dflt: 0x0 + desc: Enables external VCO mode. Set this bit to 1 will enables RFIN input path but disables internal VCO, the synthesizer will try to lock to an external source appear at RFIN pin. In loop back mode, this bit has to be set to 0, RFIN input path will be enabled by the LOOPBACK_EN bit. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '3:1' + name: R34_RESERVED_2 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 4 + name: EXTVCO_DIV + mode: RW + dflt: 0x1 + desc: Sets external VCO input divider value. + opts: + 0b0: Divide by 2 + 0b1: Bypassed + - bits: '10:5' + name: R34_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 11 + name: LOOPBACK_EN + mode: RW + dflt: 0x0 + desc: Enables loop back mode. In this mode, both RFIN input path and internal VCO are active, the synthesizer will try to lock to the internal VCO. EXTVCO_EN must be set to 0 in this mode. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '15:12' + name: R34_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x23 + name: R35 + fields: + - bits: '5:0' + name: R35_RESERVED_2 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 6 + name: MASHSEED_EN + mode: RW + dflt: 0x0 + desc: Enables MASHSEED for phase adjustment. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '8:7' + name: MASH_ORDER + mode: RW + dflt: 0x2 + desc: Sets the MASH order. + opts: + 0x0: Integer mode + 0x1: First order + 0x2: Second order + 0x3: Third order + - bits: '11:9' + name: R35_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 12 + name: MASH_RESET_N + mode: RW + dflt: 0x1 + desc: Resets the MASH (active LOW). + opts: + 0b0: Reset + 0b1: Normal operation + - bits: '15:13' + name: R35_RESERVED_0 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - addr: 0x24 + name: R36 + fields: + - bits: '14:0' + name: PLL_N + mode: RW + dflt: 0x38 + desc: Sets N divider value (integer portion). + - bits: 15 + name: R36_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x25 + name: R37 + fields: + - bits: '8:0' + name: R37_RESERVED_1 + mode: RW + dflt: 0x100 + desc: Program 0x100 to this field. + - bits: '14:9' + name: PFD_DLY + mode: RW + dflt: 0x2 + desc: Sets N divider delay time in phase detector. Effective only when PFD_DLY_MANUAL = 1. 0x0 = Reserved All other values must be set in accordance to the N divider value + - bits: 15 + name: R37_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x26 + name: R38 + fields: + - bits: '15:0' + name: PLL_DEN_U + mode: RW + dflt: 0x0 + desc: Sets the upper 16 bits of fractional denominator (DEN). + - addr: 0x27 + name: R39 + fields: + - bits: '15:0' + name: PLL_DEN_L + mode: RW + dflt: 0x3E8 + desc: Sets the lower 16 bits of fractional denominator (DEN). + - addr: 0x28 + name: R40 + fields: + - bits: '15:0' + name: MASH_SEED_U + mode: RW + dflt: 0x0 + desc: Sets the upper 16 bits of MASH_SEED. + - addr: 0x29 + name: R41 + fields: + - bits: '15:0' + name: MASH_SEED_L + mode: RW + dflt: 0x0 + desc: Sets the lower 16 bits of MASH SEED. + - addr: 0x2A + name: R42 + fields: + - bits: '15:0' + name: PLL_NUM_U + mode: RW + dflt: 0x0 + desc: Sets the upper 16 bits of fractional numerator (NUM). + - addr: 0x2B + name: R43 + fields: + - bits: '15:0' + name: PLL_NUM_L + mode: RW + dflt: 0x0 + desc: Sets the lower 16 bits of fractional numerator (NUM). + - addr: 0x2C + name: R44 + fields: + - bits: '15:0' + name: INSTCAL_PLL_NUM_U + mode: RW + dflt: 0x0 + desc: Sets the upper 16 bits of INSTCAL_PLL_NUM. INSTCAL_PLL_NUM = 232 x (PLL_NUM / PLL_DEN). + - addr: 0x2D + name: R45 + fields: + - bits: '15:0' + name: INSTCAL_PLL_NUM_L + mode: RW + dflt: 0x0 + desc: Sets the lower 16 bits of INSTCAL_PLL_NUM. INSTCAL_PLL_NUM = 232 x (PLL_NUM / PLL_DEN). + - addr: 0x2E + name: R46 + fields: + - bits: '15:0' + name: R46_RESERVED_0 + mode: RW + dflt: 0x300 + desc: Program 0x300 to this field. + - addr: 0x2F + name: R47 + fields: + - bits: '15:0' + name: R47_RESERVED_0 + mode: RW + dflt: 0x300 + desc: Program 0x300 to this field. + - addr: 0x30 + name: R48 + fields: + - bits: '15:0' + name: R48_RESERVED_0 + mode: RW + dflt: 0x4180 + desc: Program 0x4180 to this field. + - addr: 0x31 + name: R49 + fields: + - bits: '15:0' + name: R49_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x32 + name: R50 + fields: + - bits: '15:0' + name: R50_RESERVED_0 + mode: RW + dflt: 0x80 + desc: Program 0x80 to this field. + - addr: 0x33 + name: R51 + fields: + - bits: '15:0' + name: R51_RESERVED_0 + mode: RW + dflt: 0x203F + desc: Program 0x203F to this field. + - addr: 0x34 + name: R52 + fields: + - bits: '15:0' + name: R52_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x35 + name: R53 + fields: + - bits: '15:0' + name: R53_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x36 + name: R54 + fields: + - bits: '15:0' + name: R54_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x37 + name: R55 + fields: + - bits: '15:0' + name: R55_RESERVED_0 + mode: RW + dflt: 0x2 + desc: Program 0x2 to this field. + - addr: 0x38 + name: R56 + fields: + - bits: '5:0' + name: EXTPFD_DIV + mode: RW + dflt: 0x1 + desc: Sets external PFD input divider value. Set this field to 0 is not allowed. A value of 1 means bypassed. + - bits: '15:6' + name: R56_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x39 + name: R57 + fields: + - bits: 0 + name: PFD_SEL + mode: RW + dflt: 0x1 + desc: Enables PFDIN input. When using PFDIN input, the charge pump has to be set to single PFD by setting PFD_SINGLE = 0x3. + opts: + 0b0: Enabled + 0b1: Disabled + - bits: '15:1' + name: R57_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x3A + name: R58 + fields: + - bits: '15:0' + name: R58_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x3B + name: R59 + fields: + - bits: '15:0' + name: R59_RESERVED_0 + mode: RW + dflt: 0x1388 + desc: Program 0x1388 to this field. + - addr: 0x3C + name: R60 + fields: + - bits: '15:0' + name: R60_RESERVED_0 + mode: RW + dflt: 0x1F4 + desc: Program 0x1F4 to this field. + - addr: 0x3D + name: R61 + fields: + - bits: '15:0' + name: R61_RESERVED_0 + mode: RW + dflt: 0x3E8 + desc: Program 0x3E8 to this field. + - addr: 0x3E + name: R62 + fields: + - bits: '15:0' + name: MASH_RST_COUNT_U + mode: RW + dflt: 0x0 + desc: ' Sets the upper 16 bits of MASH reset delay. This is the delay that is necessary after the MASH engine is reset during phase synchronization when PLL_NUM is not equal to zero. The delay time must be set to greater than the lock time of the PLL. Delay time = MASH_RST_COUNT x (2CAL_CLK_DIV) / fOSCIN. This field can be set to 0 when PLL_NUM = 0.' + - addr: 0x3F + name: R63 + fields: + - bits: '15:0' + name: MASH_RST_COUNT_L + mode: RW + dflt: 0xC350 + desc: Sets the lower 16 bits of MASH reset delay. + - addr: 0x40 + name: R64 + fields: + - bits: 0 + name: R64_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 1 + name: SYSREF_REPEAT + mode: RW + dflt: 0x0 + desc: Defines SYSREF mode. In master mode, SYSREF pulses are generated internally. In repeater mode, SYSREF pulses are generated in response to the SRREQ pins. + opts: + 0b0: Master mode + 0b1: Repeater mode + - bits: 2 + name: SYSREF_EN + mode: RW + dflt: 0x0 + desc: Enables SYSREF mode. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: 3 + name: SYSREF_PULSE + mode: RW + dflt: 0x0 + desc: Defines SYSREF master mode. In continuous mode, SYSREF pulses are generated continuously. Pulsed mode allows multiple pulses (as determined by SYSREF_PULSE_CNT) to be sent out whenever the SRREQ pins go HIGH. + opts: + 0b0: Continuous mode + 0b1: Pulsed mode + - bits: 4 + name: SYSREF_REPEAT_NS + mode: RW + dflt: 0x0 + desc: Enables asynchronous SYSREF repeater mode. In this mode, the SYSREF signal coming from the SRREQ pin will be passed through to the SROUT pin without reclocking. + opts: + 0b0: If SYSREF_REPEAT = 1 + 0b1: Enabled + - bits: '7:5' + name: SYSREF_DIV_PRE + mode: RW + dflt: 0x4 + desc: This divider is used to get the frequency input to SYSREF_DIV within acceptable limits. + opts: + 0x1: Divide by 2 + 0x2: Divide by 4 + 0x4: Divide by 8 + - bits: '9:8' + name: SYSREF_INP_FMT + mode: RW + dflt: 0x0 + desc: "Sets SRREQ pin input format." + opts: + 0x0: CMOS input at SRREQ_P pin, 1.8-V to 3.3-V logic + 0x1: AC-couple CMOS input at SRREQ_P pin + 0x2: AC-coupled differential LVDS input, requires external 100-Ohm differential termination + 0x3: DC-coupled differential LVDS input, requires external 100-Ohm differential termination + - bits: '15:10' + name: R64_RESERVED_0 + mode: RW + dflt: 0x10 + desc: Program 0x10 to this field. + - addr: 0x41 + name: R65 + fields: + - bits: '10:0' + name: SYSREF_DIV + mode: RW + dflt: 0x1 + desc: This divider further divides the output frequency for the SYSREF. + - bits: '15:11' + name: R65_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x42 + name: R66 + fields: + - bits: '5:0' + name: JESD_DAC1_CTRL + mode: RW + dflt: 0x3F + desc: Programmable delay adjustment for SYSREF mode. + - bits: '11:6' + name: JESD_DAC2_CTRL + mode: RW + dflt: 0x0 + desc: Programmable delay adjustment for SYSREF mode. + - bits: '15:12' + name: R66_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x43 + name: R67 + fields: + - bits: '5:0' + name: JESD_DAC3_CTRL + mode: RW + dflt: 0x0 + desc: Programmable delay adjustment for SYSREF mode. + - bits: '11:6' + name: JESD_DAC4_CTRL + mode: RW + dflt: 0x0 + desc: Programmable delay adjustment for SYSREF mode. + - bits: '15:12' + name: SYSREF_PULSE_CNT + mode: RW + dflt: 0x0 + desc: Defines how many pulses are sent in SYSREF pulsed mode. + - addr: 0x44 + name: R68 + fields: + - bits: 0 + name: PSYNC_INP_FMT + mode: RW + dflt: 0x0 + desc: "Sets PSYNC pin input format." + opts: + 0b0: CMOS input, 1.8-V to 3.3-V logic + 0b1: AC-coupled differential LVDS input, requires external 100-Ohm differential termination + - bits: '4:1' + name: R68_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 5 + name: INPIN_IGNORE + mode: RW + dflt: 0x0 + desc: Disables PSYNC pin. Keep this bit equals 1 unless phase sync is required. + opts: + 0b0: Enables + 0b1: Disables pin + - bits: '15:6' + name: R68_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x45 + name: R69 + fields: + - bits: '3:0' + name: R69_RESERVED_1 + mode: RW + dflt: 0x1 + desc: Program 0x1 to this field. + - bits: 4 + name: SROUT_PD + mode: RW + dflt: 0x1 + desc: Powerdowns SYSREF output buffer. + opts: + 0b0: Normal operation + 0b1: Power down + - bits: '15:5' + name: R69_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x46 + name: R70 + fields: + - bits: '3:0' + name: R70_RESERVED_1 + mode: RW + dflt: 0xE + desc: Program 0xE to this field. + - bits: 4 + name: DBLBUF_PLL_EN + mode: RW + dflt: 0x1 + desc: Enables double buffering for PLL_N, PLL_NUM, PLL_DEN, MULT, PLL_R, PLL_R_PRE, MASH_ORDER and PFD_DLY. Changes of these registers will only be effective after R0 is programmed. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: 5 + name: DBLBUF_CHDIV_EN + mode: RW + dflt: 0x0 + desc: Enables double buffering for CHDIVA and CHDIVB. Changes of these registers will only be effective after R0 is programmed. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: 6 + name: DBLBUF_OUTBUF_EN + mode: RW + dflt: 0x0 + desc: Enables double buffering for OUTA_PD and OUTB_PD. Changes of these registers will only be effective after R0 is programmed. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: 7 + name: DBLBUF_OUTMUX_EN + mode: RW + dflt: 0x0 + desc: Enables double buffering for OUTA_MUX and OUTB_MUX. Changes of these registers will only be effective after R0 is programmed. + opts: + 0b0: Disabled + 0b1: Enabled + - bits: '15:8' + name: R70_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x47 + name: R71 + fields: + - bits: '15:0' + name: R71_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x48 + name: R72 + fields: + - bits: '15:0' + name: R72_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x49 + name: R73 + fields: + - bits: '15:0' + name: R73_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x4A + name: R74 + fields: + - bits: '1:0' + name: R74_RESERVED_1 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - bits: '4:2' + name: RB_VCO_SEL + mode: R + dflt: 0x0 + desc: Reads back the actual VCO that the VCO calibration has selected. + opts: + 0x0: Invalid + 0x1: VCO1 + 0x2: VCO2 + 0x3: VCO3 + 0x4: VCO4 + 0x5: VCO5 + 0x6: VCO6 + 0x7: VCO7 + - bits: '12:5' + name: RB_VCO_CAPCTRL + mode: R + dflt: 0x0 + desc: Reads back the actual CAPCTRL value that the VCO calibration has chosen. + - bits: 13 + name: R74_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - bits: '15:14' + name: RB_LD + mode: R + dflt: 0x0 + desc: Reads back lock detect status. + opts: + 0x0: Unlocked0 + 0x1: Unlocked1 + 0x2: Locked + 0x3: Invalid + - addr: 0x4B + name: R75 + fields: + - bits: '8:0' + name: RB_VCO_DACISET + mode: R + dflt: 0x0 + desc: Reads back the actual DACISET value that the VCO calibration has chosen. + - bits: '15:9' + name: R75_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x4C + name: R76 + fields: + - bits: '10:0' + name: RB_TEMP_SENS + mode: R + dflt: 0x0 + desc: "Reads back temperature sensor code. Temperature in C = 0.85 x code - 415. Resolution is 0.6C per code. Measurement accuracy is 5 degrees." + - bits: '15:11' + name: R76_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x4D + name: R77 + fields: + - bits: '7:0' + name: R77_RESERVED_1 + mode: RW + dflt: 0xCC + desc: Program 0x8 to this field. + - bits: 8 + name: PINMUTE_POL + mode: RW + dflt: 0x0 + desc: Sets the polarity of mute control for MUTE pin. + opts: + 0b0: Active HIGH + 0b1: Active LOW + - bits: '15:9' + name: R77_RESERVED_0 + mode: RW + dflt: 0x2B + desc: Program 0x3 to this field. + - addr: 0x4E + name: R78 + fields: + - bits: '1:0' + name: OUTA_MUX + mode: RW + dflt: 0x1 + desc: Selects the input source to RFOUTA. + opts: + 0x0: Channel divider + 0x1: VCO + 0x2: VCO doubler + - bits: '3:2' + name: R78_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 4 + name: OUTA_PD + mode: RW + dflt: 0x0 + desc: Power downs RFOUTA. + opts: + 0b0: Normal operation + 0b1: Power down + - bits: '15:5' + name: R78_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x4F + name: R79 + fields: + - bits: 0 + name: R79_RESERVED_2 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: '3:1' + name: OUTA_PWR + mode: RW + dflt: 0x7 + desc: Adjusts RFOUTA output power. Higher numbers give more output power. + - bits: '5:4' + name: OUTB_MUX + mode: RW + dflt: 0x1 + desc: Selects the input source to RFOUTB. + opts: + 0x0: Channel divider + 0x1: VCO + 0x2: VCO doubler + - bits: '7:6' + name: R79_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: 8 + name: OUTB_PD + mode: RW + dflt: 0x1 + desc: Power downs RFOUTB. + opts: + 0b0: Normal operation + 0b1: Power down + - bits: '15:9' + name: R79_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x50 + name: R80 + fields: + - bits: '5:0' + name: R80_RESERVED_1 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - bits: '8:6' + name: OUTB_PWR + mode: RW + dflt: 0x7 + desc: Adjusts RFOUTB output power. Higher numbers give more output power. + - bits: '15:9' + name: R80_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x51 + name: R81 + fields: + - bits: '15:0' + name: R81_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x52 + name: R82 + fields: + - bits: '15:0' + name: R82_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x53 + name: R83 + fields: + - bits: '15:0' + name: R83_RESERVED_0 + mode: RW + dflt: 0xF00 + desc: Program 0xF00 to this field. + - addr: 0x54 + name: R84 + fields: + - bits: '15:0' + name: R84_RESERVED_0 + mode: RW + dflt: 0x40 + desc: Program 0x40 to this field. + - addr: 0x55 + name: R85 + fields: + - bits: '15:0' + name: R85_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x56 + name: R86 + fields: + - bits: '15:0' + name: R86_RESERVED_0 + mode: RW + dflt: 0x40 + desc: Program 0x40 to this field. + - addr: 0x57 + name: R87 + fields: + - bits: '15:0' + name: R87_RESERVED_0 + mode: RW + dflt: 0xFF00 + desc: Program 0xFF00 to this field. + - addr: 0x58 + name: R88 + fields: + - bits: '15:0' + name: R88_RESERVED_0 + mode: RW + dflt: 0x3FF + desc: Program 0x3FF to this field. + - addr: 0x59 + name: R89 + fields: + - bits: '15:0' + name: R89_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x5A + name: R90 + fields: + - bits: '15:0' + name: R90_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x5B + name: R91 + fields: + - bits: '15:0' + name: R91_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x5C + name: R92 + fields: + - bits: '15:0' + name: R92_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x5D + name: R93 + fields: + - bits: '15:0' + name: R93_RESERVED_0 + mode: RW + dflt: 0x1000 + desc: Program 0x1000 to this field. + - addr: 0x5E + name: R94 + fields: + - bits: '15:0' + name: R94_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x5F + name: R95 + fields: + - bits: '15:0' + name: R95_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x60 + name: R96 + fields: + - bits: '15:0' + name: R96_RESERVED_0 + mode: RW + dflt: 0x17F8 + desc: Program 0x17F8 to this field. + - addr: 0x61 + name: R97 + fields: + - bits: '15:0' + name: R97_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x62 + name: R98 + fields: + - bits: '15:0' + name: R98_RESERVED_0 + mode: RW + dflt: 0x1C80 + desc: Program 0x1C80 to this field. + - addr: 0x63 + name: R99 + fields: + - bits: '15:0' + name: R99_RESERVED_0 + mode: RW + dflt: 0x19B9 + desc: Program 0x19B9 to this field. + - addr: 0x64 + name: R100 + fields: + - bits: '15:0' + name: R100_RESERVED_0 + mode: RW + dflt: 0x533 + desc: Program 0x533 to this field. + - addr: 0x65 + name: R101 + fields: + - bits: '15:0' + name: R101_RESERVED_0 + mode: RW + dflt: 0x3E8 + desc: Program 0x3E8 to this field. + - addr: 0x66 + name: R102 + fields: + - bits: '15:0' + name: R102_RESERVED_0 + mode: RW + dflt: 0x28 + desc: Program 0x28 to this field. + - addr: 0x67 + name: R103 + fields: + - bits: '15:0' + name: R103_RESERVED_0 + mode: RW + dflt: 0x14 + desc: Program 0x14 to this field. + - addr: 0x68 + name: R104 + fields: + - bits: '15:0' + name: R104_RESERVED_0 + mode: RW + dflt: 0x14 + desc: Program 0x14 to this field. + - addr: 0x69 + name: R105 + fields: + - bits: '15:0' + name: R105_RESERVED_0 + mode: RW + dflt: 0xA + desc: Program 0xA to this field. + - addr: 0x6A + name: R106 + fields: + - bits: '15:0' + name: R106_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x6B + name: R107 + fields: + - bits: '15:0' + name: R107_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x6C + name: R108 + fields: + - bits: '15:0' + name: R108_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x6D + name: R109 + fields: + - bits: '15:0' + name: R109_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x6E + name: R110 + fields: + - bits: '15:0' + name: R110_RESERVED_0 + mode: RW + dflt: 0x1F + desc: Program 0x1F to this field. + - addr: 0x6F + name: R111 + fields: + - bits: '15:0' + name: R111_RESERVED_0 + mode: RW + dflt: 0x0 + desc: Program 0x0 to this field. + - addr: 0x70 + name: R112 + fields: + - bits: '15:0' + name: R112_RESERVED_0 + mode: RW + dflt: 0xFFFF + desc: Program 0xFFFF to this field. + - addr: 0x71 + name: R113 + fields: + - bits: '15:0' + name: R113_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x72 + name: R114 + fields: + - bits: '15:0' + name: R114_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x73 + name: R115 + fields: + - bits: '15:0' + name: R115_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x74 + name: R116 + fields: + - bits: '15:0' + name: R116_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x75 + name: R117 + fields: + - bits: '15:0' + name: R117_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x76 + name: R118 + fields: + - bits: '15:0' + name: R118_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x77 + name: R119 + fields: + - bits: '15:0' + name: R119_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x78 + name: R120 + fields: + - bits: '15:0' + name: R120_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x79 + name: R121 + fields: + - bits: '15:0' + name: R121_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. + - addr: 0x7A + name: R122 + fields: + - bits: '15:0' + name: R122_RESERVED_0 + mode: R + dflt: 0x0 + desc: Not used. Read back only. diff --git a/src/lib/hw/lp87524j/lp87524j.c b/src/lib/hw/lp87524j/lp87524j.c new file mode 100644 index 00000000..986df9de --- /dev/null +++ b/src/lib/hw/lp87524j/lp87524j.c @@ -0,0 +1,6 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "def_lp87524j.h" +#include "lp87524j.h" +#include "usdr_logging.h" diff --git a/src/lib/hw/lp87524j/lp87524j.h b/src/lib/hw/lp87524j/lp87524j.h new file mode 100644 index 00000000..4ba9b4b6 --- /dev/null +++ b/src/lib/hw/lp87524j/lp87524j.h @@ -0,0 +1,7 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef LP87524J +#define LP87524J + +#endif // LP87524J diff --git a/src/lib/hw/lp87524j/lp87524j.yaml b/src/lib/hw/lp87524j/lp87524j.yaml new file mode 100644 index 00000000..6f15c94e --- /dev/null +++ b/src/lib/hw/lp87524j/lp87524j.yaml @@ -0,0 +1,71 @@ +name: LP87524J +revision: 0.0.1 +processors: [ c ] +bus: + type: I2C + wr_mask: 0x80000000 + usdr_path: /debug/hw/lp87524j/*/reg +addr_width: 16 +data_width: 16 + +pages: +- name: Main + regs: + - addr: 0x04 + name: BUCK1_CTRL1 + fields: + - bits: 7 + name: EN_BUCK1 + mode: RW + dflt: 1 + desc: Enable Buck1 regulator + opts: + 0: Buck1 regulator is disabled + 1: Buck1 regulator is enabled + - bits: 6 + name: EN_PIN_CTRL1 + mode: RW + dflt: 1 + desc: Enable EN1/2/3 pin control for Buck1 + opts: + 0: Only EN_BUCK1 bit controls Buck1 + 1: EN_BUCK1 bit AND ENx pin control Buck1 + - bits: '5:4' + name: BUCK1_EN_PIN_SELECT + mode: RW + dflt: 0x0 + desc: Enable EN1/2/3 pin control for Buck1 + opts: + 0x0: EN_BUCK1 bit AND EN1 pin control Buck1 + 0x1: EN_BUCK1 bit AND EN2 pin control Buck1 + 0x2: EN_BUCK1 bit AND EN3 pin control Buck1 + 0x3: Reserved + - bits: 3 + name: EN_ROOF_FLOOR1 + mode: RW + dflt: 0 + desc: Enable Roof/Floor control of EN1/2/3 pin if EN_PIN_CTRL1 = 1 + opts: + 0: Enable/Disable (1/0) control + 1: Roof/Floor (1/0) control + - bits: 2 + name: EN_RDIS1 + mode: RW + dflt: 1 + desc: Enable output discharge resistor when Buck1 is disabled + opts: + 0: Discharge resistor disabled + 1: Discharge resistor enabled + - bits: 1 + name: BUCK1_FPWM + mode: RW + dflt: 0 + desc: Forces the Buck1 regulator to operate in PWM mode + opts: + 0: Automatic transitions between PFM and PWM modes (AUTO mode). + 1: Forced to PWM operation + - bits: 0 + name: BUCK1_CTRL1_RESERVED_0 + mode: RW + dflt: 0 + desc: '' diff --git a/src/lib/hw/si5332/si5332.c b/src/lib/hw/si5332/si5332.c index fc4dbf6c..d03664a7 100644 --- a/src/lib/hw/si5332/si5332.c +++ b/src/lib/hw/si5332/si5332.c @@ -388,7 +388,7 @@ int si5332_init(lldev_t dev, subdev_t subdev, lsopaddr_t lsopaddr, unsigned div, // CLKIN_2_CLK_SEL, 1, IMUX_SEL, ext_in2 ? IMUX_IN_2 : IMUX_XOSC, - CLKIN_2_CLK_SEL, ext_in2 ? IMUX_INX_CMOS_AC : IMUX_INX_DISABLED, + CLKIN_2_CLK_SEL, ext_in2 ? IMUX_INX_DIFF /*IMUX_INX_CMOS_AC*/ : IMUX_INX_DISABLED, CLKIN_3_CLK_SEL, 0, 0x3C, 0, @@ -497,11 +497,13 @@ int si5532_set_ext_clock_sw(lldev_t dev, subdev_t subdev, lsopaddr_t lsopaddr, b { USYS_CTRL, 0x01, //READY IMUX_SEL, set_flag ? IMUX_IN_2 : IMUX_XOSC, - CLKIN_2_CLK_SEL, set_flag ? IMUX_INX_CMOS_AC : IMUX_INX_DISABLED, + CLKIN_2_CLK_SEL, set_flag ? IMUX_INX_DIFF /* IMUX_INX_CMOS_AC */ : IMUX_INX_DISABLED, 0xB9, set_flag ? (B9_XOSC_DIS /*| B9_PLL_DIS | B9_PDIV_DIS*/) : (B9_IBUF0_DIS /*| B9_PLL_DIS | B9_PDIV_DIS*/), USYS_CTRL, 0x02, //ACTIVE }; + USDR_LOG("5332", USDR_LOG_INFO, "Si5332 ExtClock=%d\n", set_flag); + int res = 0; res = si5532_get_state(dev, subdev, lsopaddr, "BEFORE SET_EX_CLK"); @@ -560,6 +562,14 @@ int si5332_set_layout(lldev_t dev, subdev_t subdev, lsopaddr_t lsopaddr, unsigned idpa_den = 32767; unsigned idpa_res = (uint64_t)idpa_den * idpa_frac / pllfreq; + unsigned error; + for (idpa_den = 2; idpa_den < 32767; idpa_den++) { + error = (idpa_den * idpa_frac) % pllfreq; + if (error == 0) + break; + } + idpa_res = (uint64_t)idpa_den * idpa_frac / pllfreq; + unsigned pll_freq_div = 0; bool jdiv = false; @@ -575,20 +585,9 @@ int si5332_set_layout(lldev_t dev, subdev_t subdev, lsopaddr_t lsopaddr, if (vcofreq) { *vcofreq = vco; } - // TODO alternative PLL ref - // if (altref) { - // pll_freq_div = (vco + 41000000) / 41000000; - // *altref = pll_freq_div; - // } - - USDR_LOG("5332", USDR_LOG_INFO, "VCO=%u IDPA_INTG=%u IDPA_RES=%u HSDIV=%u ODIV=%u JDIV=%d MXLO=%u\n", - vco, idpa_intg, idpa_res, hsdiv, odiv, jdiv, vco / lodiv); - - // terms of an a + b/c desired divider settingmust be processed into - //IDPA_INTG, ID-PA_RES, and IDPA_DEN register - //terms.intg =floor(((a*c+b)*128/c) - 512). - //res = mod(b*128, c) + USDR_LOG("5332", USDR_LOG_INFO, "VCO=%u IDPA_INTG=%u IDPA_RES/DEV=%u/%u HSDIV=%u ODIV=%u JDIV=%d MXLO=%u OLD=%d\n", + vco, idpa_intg, idpa_res, idpa_den, hsdiv, odiv, jdiv, vco / lodiv, old); // slew 0 -- fastest ; 3 -- slowest const uint8_t program_regs_init[] = { @@ -602,7 +601,7 @@ int si5332_set_layout(lldev_t dev, subdev_t subdev, lsopaddr_t lsopaddr, IDPA_DEN_L, idpa_den >> 8, PDIV_DIV, prescaler, //Prescaler - PLL_MODE, (pllfreq > 30e6) ? 11 : 4, // 4 - 500kHz | 7 - 175khz + PLL_MODE, (pllfreq > 30e6) ? 8 : 4, // 4 - 500kHz | 7 - 175khz HSDIV0A_DIV, hsdiv, HSDIV0B_DIV, hsdiv, @@ -610,8 +609,9 @@ int si5332_set_layout(lldev_t dev, subdev_t subdev, lsopaddr_t lsopaddr, HSDIV1A_DIV, pll_freq_div, HSDIV2A_DIV, pll_freq_div, - old ? OUT0_CMOS_SLEW : OUT1_CMOS_SLEW, (nfo->out > 110e6) ? 1 : (nfo->out > 50e6) ? 1 : 2, - OUT2_CMOS_SLEW, (nfo->out > 110e6) ? 1 : (nfo->out > 50e6) ? 1 : 2, + + old ? OUT0_CMOS_SLEW : OUT1_CMOS_SLEW, (nfo->out > 110e6) ? 0 : (nfo->out > 50e6) ? 1 : (nfo->out > 25e6) ? 2 : 3, + OUT2_CMOS_SLEW, (nfo->out > 110e6) ? 0 : (nfo->out > 50e6) ? 1 : (nfo->out > 25e6) ? 2 : 3, old ? OUT0_DIV : OUT1_DIV, odiv, OUT2_DIV, odiv, diff --git a/src/lib/hw/tca9555/tca9555.c b/src/lib/hw/tca9555/tca9555.c new file mode 100644 index 00000000..6146fd4e --- /dev/null +++ b/src/lib/hw/tca9555/tca9555.c @@ -0,0 +1,46 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "tca9555.h" +#include "def_tca9555.h" + +#include + +int tca9555_reg8_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint8_t value) +{ + uint8_t data[2] = { reg, value }; + return lowlevel_ls_op(dev, subdev, + USDR_LSOP_I2C_DEV, ls_op_addr, + 0, NULL, 2, data); +} + +int tca9555_reg8_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint8_t* oval) +{ + return lowlevel_ls_op(dev, subdev, + USDR_LSOP_I2C_DEV, ls_op_addr, + 1, oval, 1, ®); +} + +int tca9555_reg16_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint16_t value) +{ + uint8_t data[3] = { reg, value, value >> 8 }; + return lowlevel_ls_op(dev, subdev, + USDR_LSOP_I2C_DEV, ls_op_addr, + 0, NULL, 3, data); +} + +int tca9555_reg16_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint16_t* oval) +{ + uint8_t data[1] = { reg }; + uint8_t odata[2] = { 0x00, 0x00 }; + int res = lowlevel_ls_op(dev, subdev, + USDR_LSOP_I2C_DEV, ls_op_addr, + 2, odata, 1, data); + + *oval = (((unsigned)odata[0]) << 8) | odata[1]; + return res; +} diff --git a/src/lib/hw/tca9555/tca9555.h b/src/lib/hw/tca9555/tca9555.h new file mode 100644 index 00000000..6528bef6 --- /dev/null +++ b/src/lib/hw/tca9555/tca9555.h @@ -0,0 +1,27 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef TCA9555_H +#define TCA9555_H + +#include + +enum tca9555_regs { + TCA9555_IN0 = 0, + TCA9555_OUT0 = 2, + TCA9555_INV0 = 4, + TCA9555_CFG0 = 6, // 0 - Output, 1 - Input/Hi-Z +}; + +int tca9555_reg8_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint8_t value); +int tca9555_reg8_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint8_t* oval); + +int tca9555_reg16_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint16_t value); +int tca9555_reg16_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t reg, uint16_t* oval); + + +#endif diff --git a/src/lib/hw/tca9555/tca9555.yaml b/src/lib/hw/tca9555/tca9555.yaml new file mode 100644 index 00000000..74f3cf67 --- /dev/null +++ b/src/lib/hw/tca9555/tca9555.yaml @@ -0,0 +1,87 @@ +# Copyright (c) 2023-2024 Wavelet Lab +# SPDX-License-Identifier: MIT + +# Register desc and visual map +name: TCA9555 +desc: GPIO +revision: "0.0.1" +processors: [ c ] +bus: + type: I2C + rd_mask: 0x8000 + usdr_path: /debug/hw/tca9555/0/reg +addr_width: 8 +data_width: 8 +# page_prefix: True +# field_prefix: [ Page, RegName ] +field_macros: True + +pages: + - name: Top + regs: + - addr: 0x00 + name: GSR1 + + - addr: 0x01 + name: GSR2 + + - addr: 0x02 + name: OCR1 + + - addr: 0x03 + name: OCR2 + + - addr: 0x04 + name: PIR1 + + - addr: 0x05 + name: PIR2 + + - addr: 0x06 + name: GCR1 + + - addr: 0x07 + name: GCR2 + + - addr: 0x08 + name: PUR1 + + - addr: 0x09 + name: PUR2 + + - addr: 0x0A + name: IER1 + + - addr: 0x0B + name: IER2 + + - addr: 0x0C + name: TSCR1 + + - addr: 0x0D + name: TSCR2 + + - addr: 0x0E + name: ISR1 + + - addr: 0x0F + name: ISR2 + + - addr: 0x10 + name: REIR1 + + - addr: 0x11 + name: REIR2 + + - addr: 0x12 + name: FEIR1 + + - addr: 0x13 + name: FEIR2 + + - addr: 0x14 + name: IFR1 + + - addr: 0x15 + name: IFR2 + diff --git a/src/lib/hw/tmp114/tmp114.c b/src/lib/hw/tmp114/tmp114.c index a05f134d..a912133f 100644 --- a/src/lib/hw/tmp114/tmp114.c +++ b/src/lib/hw/tmp114/tmp114.c @@ -27,6 +27,16 @@ int tmp114_reg_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, 2, pv, 1, &taddr); } +static +int tmp114_reg_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint8_t taddr, uint16_t pv) +{ + uint8_t cmd[3] = { taddr, pv >> 8, pv }; + return lowlevel_get_ops(dev)->ls_op(dev, subdev, + USDR_LSOP_I2C_DEV, ls_op_addr, + 0, NULL, 3, cmd); +} + int tmp114_temp_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, int* outtemp) { @@ -36,6 +46,18 @@ int tmp114_temp_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, return res; } +int tmp114_config_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint16_t* config) +{ + return tmp114_reg_get(dev, subdev, ls_op_addr, TMP114_Configuration, config); +} + +int tmp114_config_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint16_t config) +{ + return tmp114_reg_set(dev, subdev, ls_op_addr, TMP114_Configuration, config); +} + int tmp114_devid_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, int* devid) { @@ -45,3 +67,17 @@ int tmp114_devid_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, return res; } +int tmp114_uid_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint64_t* devuid) +{ + uint16_t uid[3]; + for (int i = 0; i < 3; i++) { + int res = tmp114_reg_get(dev, subdev, ls_op_addr, TMP114_Unique_ID1 + i, &uid[i]); + if (res) + return res; + } + + *devuid = ((uint64_t)uid[0] << 32) | ((uint64_t)uid[1] << 16) | ((uint64_t)uid[2] << 0); + return 0; +} + diff --git a/src/lib/hw/tmp114/tmp114.h b/src/lib/hw/tmp114/tmp114.h index f3f19351..e8861d55 100644 --- a/src/lib/hw/tmp114/tmp114.h +++ b/src/lib/hw/tmp114/tmp114.h @@ -6,11 +6,22 @@ #include +#define TMP114_DEVICE_ID 0x1114 + + int tmp114_temp_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, int* outtemp); +int tmp114_config_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint16_t* config); + +int tmp114_config_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint16_t config); + int tmp114_devid_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, int* devid); +int tmp114_uid_get(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, + uint64_t* devuid); #endif diff --git a/src/lib/hw/tps6381x/tps6381x.c b/src/lib/hw/tps6381x/tps6381x.c index e4debc06..b30a00ce 100644 --- a/src/lib/hw/tps6381x/tps6381x.c +++ b/src/lib/hw/tps6381x/tps6381x.c @@ -33,6 +33,13 @@ static int tps6381x_reg_set(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, 0, NULL, 2, &tv); } +int tps6381x_check_pg(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, bool* ppg) +{ + uint8_t reg_status = 0xff; + int res = tps6381x_reg_get(dev, subdev, ls_op_addr, STATUS, ®_status); + *ppg = (reg_status == 0x00); + return res; +} int tps6381x_init(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, bool enable, bool force_pwm, int vout_mv) diff --git a/src/lib/hw/tps6381x/tps6381x.h b/src/lib/hw/tps6381x/tps6381x.h index 5afaf880..c48dd8f4 100644 --- a/src/lib/hw/tps6381x/tps6381x.h +++ b/src/lib/hw/tps6381x/tps6381x.h @@ -6,6 +6,7 @@ #include +int tps6381x_check_pg(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, bool* ppg); int tps6381x_init(lldev_t dev, subdev_t subdev, lsopaddr_t ls_op_addr, bool enable, bool force_pwm, int vout_mv); diff --git a/src/lib/ipblks/espi_flash.c b/src/lib/ipblks/espi_flash.c index 9a3949e4..ecf6122f 100644 --- a/src/lib/ipblks/espi_flash.c +++ b/src/lib/ipblks/espi_flash.c @@ -330,7 +330,7 @@ int espi_flash_erase(lldev_t dev, subdev_t subdev, unsigned cfg_base, int espi_flash_write(lldev_t dev, subdev_t subdev, unsigned cfg_base, unsigned cfg_mmap_base, const uint8_t* in, uint32_t size, uint32_t flash_off, unsigned flags) { - int res; + int res = -EINVAL; if ((flags & ESPI_FLASH_DONT_ERASE) != ESPI_FLASH_DONT_ERASE) { res = _espi_flash_erase(dev, subdev, cfg_base, flash_off, size); if (res) diff --git a/src/lib/ipblks/fgearbox.c b/src/lib/ipblks/fgearbox.c index 78f6768d..2f9339ee 100644 --- a/src/lib/ipblks/fgearbox.c +++ b/src/lib/ipblks/fgearbox.c @@ -225,6 +225,50 @@ static const uint8_t fir_data_dec4_clkr2_S[1280] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0x01, 0x33, }; +static const uint8_t fir_data_dec4_clkr2_us[1280] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0x87, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x78, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x3b, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x07, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x06, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x06, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x78, 0x78, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x66, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x66, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x28, 0x2a, 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x78, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x61, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x50, 0x55, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x50, 0x52, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x50, 0x52, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x50, 0x56, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x50, 0x52, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x50, 0x50, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x56, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x55, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x78, 0x57, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x35, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x58, 0x03, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x58, 0x1b, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x20, 0x4e, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x10, 0x77, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x30, 0x27, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x50, 0x71, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x66, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x50, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0x00, 0x30, +}; + + const uint8_t fir_data_dec5_clkr2[1280] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1e, 0x1e, 0x1e, 0x3e, @@ -355,6 +399,48 @@ const uint8_t fir_data_dec8_clkr2[1280] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x10, 0x20, }; +static const uint8_t fir_data_dec10_clkr2_us[1280] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x9f, 0x9f, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x60, 0x61, 0x7f, 0x7f, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x2f, 0x2f, 0xaf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x1f, 0x1f, 0x1f, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x63, 0x7f, 0x7c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x7c, 0x63, 0x1c, 0x03, 0x7c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, 0x0c, 0x6c, 0x0c, 0x6c, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x60, 0x7f, 0x00, 0x0f, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x40, 0x10, 0x7c, 0x6f, 0x03, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, 0x20, 0x0c, 0x73, 0x0c, 0x73, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x4f, 0x58, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x4f, 0x58, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, 0x40, 0x40, 0x4f, 0x58, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x5c, 0x4f, 0x58, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x52, 0x4f, 0x58, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x4b, 0x4f, 0x58, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x1d, 0x58, 0x58, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x6b, 0x40, 0x48, 0x43, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x4c, 0x25, 0x43, 0x53, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x74, 0x09, 0x0f, 0x4b, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0x6d, 0x0b, 0x79, 0x5f, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x06, 0x5e, 0x26, 0x1a, 0x47, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x00, 0x4f, 0x6b, 0x41, 0x36, 0x4b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x52, 0x25, 0x1c, 0x41, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0x51, 0x70, 0x52, 0x76, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, 0x0a, 0x6d, 0x18, 0x63, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x0e, 0x40, 0x4d, 0x27, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x68, 0x11, 0x02, 0x3c, 0x5c, +}; static const uint8_t fir_data_dec12_clkr2[1280] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x3f, 0x1f, 0x1f, 0x1f, 0x00, 0x00, @@ -746,6 +832,51 @@ static const uint8_t fir_data_int4_clkr2_S[1280] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uint8_t fir_data_int4_clkr2_us[1280] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x17, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x77, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x80, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x40, 0x8b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x43, 0xbc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x55, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x7f, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x4c, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x2a, 0x2a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x25, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x05, 0x65, 0xda, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x25, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x05, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x65, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, 0x55, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0f, 0x75, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x5d, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x31, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x37, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x09, 0x62, 0x72, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x05, 0x7c, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0c, 0x79, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x1d, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x69, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x04, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + + static const uint8_t fir_data_int8_clkr2[1280] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xe0, 0xe0, 0x00, @@ -877,6 +1008,93 @@ static const uint8_t fir_data_int32_clkr2[1280] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uint8_t fir_data_int32_clkr2_us[1280] = { + 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xc0, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xd0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x0d, 0x0d, 0x3f, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x0d, 0x2f, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x06, 0x07, 0x04, 0x07, 0x00, 0x0f, 0x10, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x0f, 0x0f, 0x30, 0x2f, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x07, 0x04, 0x07, 0x04, 0x0f, 0x00, 0x2f, 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x0d, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x02, 0x01, 0x0e, 0x0d, 0x32, 0x2f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x0d, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x0d, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x02, 0x01, 0x0e, 0x01, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x02, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x03, 0x02, 0x03, 0x0c, 0x0d, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x03, 0x0e, 0x01, 0x2e, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0c, 0x0f, 0x3e, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02, 0x01, 0x0e, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x00, 0x02, 0x0f, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x03, 0x02, 0x02, 0x0d, 0x01, 0x2c, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x00, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x03, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t fir_data_int64_clkr2_us[1280] = { + 0x00, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0x80, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x1f, 0x1f, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x40, 0xdf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x1d, 0x1d, 0x7f, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x1d, 0x5f, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x0e, 0x0f, 0x0c, 0x0f, 0x00, 0x1f, 0x20, 0x7d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x1f, 0x1f, 0x60, 0x5f, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x0f, 0x0c, 0x0f, 0x0c, 0x1f, 0x00, 0x5f, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x1d, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x02, 0x01, 0x1e, 0x1d, 0x62, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x1d, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x1d, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x02, 0x01, 0x1e, 0x01, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x02, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x03, 0x02, 0x03, 0x1c, 0x1d, 0x62, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x03, 0x1e, 0x01, 0x5e, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x1c, 0x1f, 0x7e, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02, 0x01, 0x1e, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x00, 0x02, 0x1f, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x03, 0x02, 0x02, 0x1d, 0x01, 0x5c, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x00, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x03, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + static const uint8_t fir_data_int64_clkr2[1280] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0xe0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x1f, 0x1f, 0x80, 0x40, @@ -963,6 +1181,49 @@ static const uint8_t fir_data_int128_clkr2[1280] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uint8_t fir_data_int128_clkr2_us[1280] = { + 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0x00, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x3f, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x3d, 0x3d, 0xff, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x3d, 0xbf, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0f, 0x0e, 0x0f, 0x0e, 0x0f, 0x0e, 0x0f, 0x1e, 0x1f, 0x1c, 0x1f, 0x00, 0x3f, 0x40, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x3f, 0x3f, 0xc0, 0xbf, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0e, 0x0f, 0x0e, 0x0f, 0x0e, 0x0f, 0x0e, 0x1f, 0x1c, 0x1f, 0x1c, 0x3f, 0x00, 0xbf, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x3d, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x02, 0x01, 0x3e, 0x3d, 0xc2, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x3d, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x3d, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x02, 0x01, 0x3e, 0x01, 0x82, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x02, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x03, 0x02, 0x03, 0x3c, 0x3d, 0xc2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x03, 0x3e, 0x01, 0xbe, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x3c, 0x3f, 0xfe, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02, 0x01, 0x3e, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x00, 0x02, 0x3f, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x03, 0x02, 0x02, 0x3d, 0x01, 0xbc, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x00, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x03, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + static const uint8_t fir_data_int256_clkr2[1280] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0x80, 0x80, 0x00, @@ -1059,19 +1320,27 @@ static int dev_gpo_set(lldev_t dev, unsigned bank, unsigned data) return lowlevel_reg_wr32(dev, 0, 0, ((bank & 0x7f) << 24) | (data & 0xff)); } -int fgearbox_load_ucode(lldev_t dev, unsigned gport, const uint8_t* ucode) +int fgearbox_load_ucode(lldev_t dev, unsigned gport, const uint8_t* ucode, unsigned sleep_us) { int res; for (unsigned i = 0; i < UCODE_SIZE; i++) { res = dev_gpo_set(dev, gport, ucode[i]); if (res) return res; + + if (sleep_us) { + usleep(sleep_us); + } } return 0; } +int fgearbox_load_fir(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam) +{ + return fgearbox_load_fir_ex(dev, gport, fir, fam, 0); +} -int fgearbox_load_fir(lldev_t dev, unsigned gport, fgearbox_firs_t fir) +int fgearbox_load_fir_ex(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam, unsigned sleep_us) { const uint8_t* s_st8_dsp; @@ -1086,7 +1355,7 @@ int fgearbox_load_fir(lldev_t dev, unsigned gport, fgearbox_firs_t fir) s_st8_dsp = fir_data_dec3_clkr2; break; case FGBOX_X4: - s_st8_dsp = fir_data_dec4_clkr2_S; //fir_data_dec4_clkr8_3; + s_st8_dsp = (fam == DSP_USSERIES) ? fir_data_dec4_clkr2_us : fir_data_dec4_clkr2_S; //fir_data_dec4_clkr8_3; break; case FGBOX_X5: s_st8_dsp = fir_data_dec5_clkr2; @@ -1097,6 +1366,11 @@ int fgearbox_load_fir(lldev_t dev, unsigned gport, fgearbox_firs_t fir) case FGBOX_X8: s_st8_dsp = fir_data_dec8_clkr2; break; + case FGBOX_X10: + if (fam != DSP_USSERIES) + return -EINVAL; + s_st8_dsp = fir_data_dec10_clkr2_us; + break; case FGBOX_X12: s_st8_dsp = fir_data_dec12_clkr2; break; @@ -1125,11 +1399,16 @@ int fgearbox_load_fir(lldev_t dev, unsigned gport, fgearbox_firs_t fir) return -EINVAL; } - return fgearbox_load_ucode(dev, gport, s_st8_dsp); + return fgearbox_load_ucode(dev, gport, s_st8_dsp, sleep_us); } -int fgearbox_load_fir_i(lldev_t dev, unsigned gport, fgearbox_firs_t fir) +int fgearbox_load_fir_i(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam) +{ + return fgearbox_load_fir_i_ex(dev, gport, fir, fam, 0); +} + +int fgearbox_load_fir_i_ex(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam, unsigned sleep_us) { const uint8_t* s_st8_dsp; @@ -1150,7 +1429,7 @@ int fgearbox_load_fir_i(lldev_t dev, unsigned gport, fgearbox_firs_t fir) s_st8_dsp = fir_data_int16_clkr2; break; case FGBOX_X32: - s_st8_dsp = fir_data_int32_clkr2; + s_st8_dsp = (fam == DSP_USSERIES) ? fir_data_int32_clkr2_us : fir_data_int32_clkr2; break; case FGBOX_X64: s_st8_dsp = fir_data_int64_clkr2; @@ -1165,7 +1444,7 @@ int fgearbox_load_fir_i(lldev_t dev, unsigned gport, fgearbox_firs_t fir) return -EINVAL; } - return fgearbox_load_ucode(dev, gport, s_st8_dsp); + return fgearbox_load_ucode(dev, gport, s_st8_dsp, sleep_us); } diff --git a/src/lib/ipblks/fgearbox.h b/src/lib/ipblks/fgearbox.h index 6f597576..b5132052 100644 --- a/src/lib/ipblks/fgearbox.h +++ b/src/lib/ipblks/fgearbox.h @@ -17,6 +17,8 @@ enum fgearbox_firs { FGBOX_X6 = 6, FGBOX_X8 = 8, + FGBOX_X10 = 10, + FGBOX_X12 = 12, FGBOX_X16 = 16, FGBOX_X24 = 24, @@ -29,10 +31,16 @@ enum fgearbox_firs { }; typedef enum fgearbox_firs fgearbox_firs_t; +typedef enum dspfamily { + DSP_7SERIES, + DSP_USSERIES, +} dspfamily_t; -int fgearbox_load_fir(lldev_t dev, unsigned gport, fgearbox_firs_t fir); +int fgearbox_load_fir(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam); +int fgearbox_load_fir_i(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam); -int fgearbox_load_fir_i(lldev_t dev, unsigned gport, fgearbox_firs_t fir); +int fgearbox_load_fir_ex(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam, unsigned sleep_us); +int fgearbox_load_fir_i_ex(lldev_t dev, unsigned gport, fgearbox_firs_t fir, dspfamily_t fam, unsigned sleep_us); #endif diff --git a/src/lib/ipblks/gpio.h b/src/lib/ipblks/gpio.h index e11c8b73..92e60f72 100644 --- a/src/lib/ipblks/gpio.h +++ b/src/lib/ipblks/gpio.h @@ -12,6 +12,7 @@ enum gpio_config_vals { GPIO_CFG_IN = 0, GPIO_CFG_OUT = 1, GPIO_CFG_ALT0 = 2, + GPIO_CFG_ALT1 = 3, }; // Core supports gpio0 through gpio14 diff --git a/src/lib/ipblks/lms64c_proto.c b/src/lib/ipblks/lms64c_proto.c index e5c46725..4dbfe39d 100644 --- a/src/lib/ipblks/lms64c_proto.c +++ b/src/lib/ipblks/lms64c_proto.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include "lms64c_cmds.h" #include "lms64c_proto.h" @@ -114,7 +114,7 @@ int lms64c_fill_packet(uint8_t cmd, uint8_t status, uint8_t id, const uint8_t* d } else if (mod == MOD_INT16) { const uint16_t* d = (const uint16_t*)&data[off]; for (unsigned i = 0; i < this_pkt_data; i += 2) { - // TODO: Endianess + // TODO: Endianness out[k].data[i + 0] = d[i >> 1] >> 8; out[k].data[i + 1] = d[i >> 1] & 0xFF; } @@ -140,7 +140,7 @@ int lms64c_parse_packet(uint8_t cmd, const proto_lms64c_t* in, unsigned in_cnt, unsigned bsz = (remaining > LMS64C_DATA_LENGTH) ? LMS64C_DATA_LENGTH : remaining; if (mod == MOD_INT32_16) { - // TODO: Endianess + // TODO: Endianness const uint16_t* d = (const uint16_t*)in[i].data; for (unsigned j = 0; j < bsz; j += 2) { data[off + j + 0] = d[j + 1] >> 8; diff --git a/src/lib/ipblks/si2c.h b/src/lib/ipblks/si2c.h index f953dc86..d0b6b035 100644 --- a/src/lib/ipblks/si2c.h +++ b/src/lib/ipblks/si2c.h @@ -8,7 +8,7 @@ #ifndef SI2C_H #define SI2C_H -#if defined(__EMSCRIPTEN__) +#if defined(__EMSCRIPTEN__) || defined(_WIN32) || defined(__APPLE__) #include #include #else diff --git a/src/lib/ipblks/spiext.h b/src/lib/ipblks/spiext.h index 8e7faa10..250744c3 100644 --- a/src/lib/ipblks/spiext.h +++ b/src/lib/ipblks/spiext.h @@ -8,7 +8,7 @@ #ifndef SPIEXT_H #define SPIEXT_H -#if defined(__EMSCRIPTEN__) +#if defined(__EMSCRIPTEN__) || defined(_WIN32) || defined(__APPLE__) #include #include typedef uint8_t __u8; diff --git a/src/lib/ipblks/streams/dma_rx_32.c b/src/lib/ipblks/streams/dma_rx_32.c index 7440aedc..2d663f02 100644 --- a/src/lib/ipblks/streams/dma_rx_32.c +++ b/src/lib/ipblks/streams/dma_rx_32.c @@ -27,10 +27,10 @@ int dma_rx32_reset(lldev_t lldev, if (tmp & 0x40000000) { if (tmp == 0xffffffff) { - USDR_LOG("DMRX", USDR_LOG_ERROR, "DMA engine is dead!\n"); + USDR_LL_LOG(lldev, "DMRX", USDR_LOG_ERROR, "DMA engine is dead!\n"); return -EIO; } - USDR_LOG("DMRX", USDR_LOG_WARNING, "DMA engine is active!\n"); + USDR_LL_LOG(lldev, "DMRX", USDR_LOG_WARNING, "DMA engine is active!\n"); } // DMA STOP, FE RESET diff --git a/src/lib/ipblks/streams/sfe_rx_4.c b/src/lib/ipblks/streams/sfe_rx_4.c index 3857082e..6513703c 100644 --- a/src/lib/ipblks/streams/sfe_rx_4.c +++ b/src/lib/ipblks/streams/sfe_rx_4.c @@ -3,6 +3,7 @@ #include "sfe_rx_4.h" #include +#include "stream_sfetrx4_dma32.h" enum sfe_rx_regs { FE_CMD_REG_ROUTE, @@ -173,15 +174,15 @@ struct rfe_burster_data { }; typedef struct rfe_burster_data rfe_burster_data_t; -static int burst_fe_calculate(const rfe_config_t* cfg, const struct stream_config* psc, unsigned chans_raw, unsigned ch_bits, rfe_burster_data_t* out) +static int burst_fe_calculate(lldev_t lldev, const rfe_config_t* cfg, const struct stream_config* psc, unsigned chans_raw, unsigned ch_bits, rfe_burster_data_t* out) { if (psc->burstspblk > cfg->cfg_max_bursts) { - USDR_LOG("STRM", USDR_LOG_ERROR, "SFERX4: bursts count `%d' exeeds maximum %d!\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_ERROR, "SFERX4: bursts count `%d' exeeds maximum %d!\n", psc->burstspblk, cfg->cfg_max_bursts); return -EINVAL; } if (chans_raw > cfg->cfg_data_lanes) { - USDR_LOG("STRM", USDR_LOG_ERROR, "SFERX4: channel count %d isn't supported!\n", chans_raw); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_ERROR, "SFERX4: channel count %d isn't supported!\n", chans_raw); return -EINVAL; } @@ -196,7 +197,7 @@ static int burst_fe_calculate(const rfe_config_t* cfg, const struct stream_confi unsigned fifo_capacity = cfg->cfg_fifo_ram_bytes / (bwords * cfg->cfg_data_lanes_bytes); if ((bursts != 0) && (bwords > cfg->limit_burst_words)) { - USDR_LOG("STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: %d samples @%s exeeds max burst size: %d words!\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: %d samples @%s exeeds max burst size: %d words!\n", psc->spburst, psc->sfmt, cfg->limit_burst_words); return -EINVAL; } @@ -238,10 +239,10 @@ static int burst_fe_calculate(const rfe_config_t* cfg, const struct stream_confi unsigned ex_bytes = ((bwords + best_bursts - 1) / best_bursts) * cfg->cfg_data_lanes_bytes - (samplerperbursts / best_bursts) * bps / 8; - USDR_LOG("STRM", USDR_LOG_INFO, "SFERX4: Adding stub %d bytes to transfer for %d burst configuration\n", ex_bytes, best_bursts); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "SFERX4: Adding stub %d bytes to transfer for %d burst configuration\n", ex_bytes, best_bursts); bursts = best_bursts; } else { - USDR_LOG("STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: %d samples @%s can't be represented with any burst count, capacity %d (FIFO is %d)!\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: %d samples @%s can't be represented with any burst count, capacity %d (FIFO is %d)!\n", psc->spburst, psc->sfmt, fifo_capacity, cfg->cfg_fifo_ram_bytes); return -EINVAL; } @@ -260,13 +261,13 @@ static int burst_fe_calculate(const rfe_config_t* cfg, const struct stream_confi } if (samplerperbursts % cfg->limit_samples_mod) { - USDR_LOG("STRM", USDR_LOG_ERROR, "SFERX4: Burst size should be multiple of %d, requested %d x %d!\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_ERROR, "SFERX4: Burst size should be multiple of %d, requested %d x %d!\n", cfg->limit_samples_mod, samplerperbursts, bursts); return -EINVAL; } if (fifo_capacity > SFE_CMD_BF_BTOTAL_MASK) { - USDR_LOG("STRM", USDR_LOG_WARNING, "SFERX4: fifo capacity exceeds fe capabilities, requested: %d!", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_WARNING, "SFERX4: fifo capacity exceeds fe capabilities, requested: %d!", fifo_capacity); fifo_capacity = SFE_CMD_BF_BTOTAL_MASK; } @@ -301,6 +302,7 @@ static int _configure_simple_fe_generic(const sfe_cfg_t* fe, unsigned chns, struct fifo_config* pfc) { + lldev_t lldev = fe->dev; // Some constants are derived from IPBLK_PARAM_BUFFER_SIZE_ADDR // and any other value may break configuration if ((1 << IPBLK_PARAM_BUFFER_SIZE_ADDR) != fe->cfg_fifomaxbytes) @@ -317,7 +319,7 @@ static int _configure_simple_fe_generic(const sfe_cfg_t* fe, cfg.limit_burst_samples = (1u << IPBLK_PARAM_BWORDS); // Replace to cfg_fifomaxbytes rfe_burster_data_t data; - res = burst_fe_calculate(&cfg, psc, chns, bps, &data); + res = burst_fe_calculate(lldev, &cfg, psc, chns, bps, &data); if (res) { return res; } @@ -327,7 +329,7 @@ static int _configure_simple_fe_generic(const sfe_cfg_t* fe, unsigned fifo_capacity = data.capacity; unsigned samplerperbursts = data.samples; - USDR_LOG("STRM", USDR_LOG_INFO, "SFERX4: Stream %s/%d configured in %d words (%d samples) X %d bursts (%d bits per sym); fifo capacity %d (FMT:%x FE:%d)\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "SFERX4: Stream %s/%d configured in %d words (%d samples) X %d bursts (%d bits per sym); fifo capacity %d (FMT:%x FE:%d)\n", psc->sfmt, samples_mod, bwords, samplerperbursts, bursts, bps, fifo_capacity, chfmt, fe_format); // Put everything into reset @@ -355,8 +357,10 @@ static int _configure_simple_fe_generic(const sfe_cfg_t* fe, int sfe_rx4_configure(const sfe_cfg_t* fe, const struct stream_config* psc, - struct fifo_config* pfc) + struct fifo_config* pfc, + uint64_t *pwr_ch_mask) { + lldev_t lldev = fe->dev; struct bitsfmt bfmt = get_bits_fmt(psc->sfmt); if (strcmp((const char*)bfmt.func, &DSPFUNC_CFFT_LPWR_I16[1]) == 0) { unsigned fft_size = 512; @@ -365,14 +369,14 @@ int sfe_rx4_configure(const sfe_cfg_t* fe, // Check why /2 is here if ((psc->burstspblk != 0) && (bwords > ((1u << SFE_CMD_BF_BWORDS_WIDTH)/2))) { - USDR_LOG("STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: FFT512 %d samples @%s exeeds max burst size!\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: FFT512 %d samples @%s exeeds max burst size!\n", psc->spburst, psc->sfmt); return -EINVAL; } return _configure_simple_fe_generic(fe, psc, bps, fft_size, IFMT_DSP, IFMT_CH_xx10, 1, pfc); } else if (bfmt.bits == 0) { - USDR_LOG("STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: RX Stream format `%s' not supported!\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: RX Stream format `%s' not supported!\n", psc->sfmt); return -EINVAL; } @@ -424,19 +428,31 @@ int sfe_rx4_configure(const sfe_cfg_t* fe, } if (chns == 0) { - USDR_LOG("STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: RX channel count %d is not supported in configuration [%d, %d, %d, %d, %d, %d, %d, %d ... ]!\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_CRITICAL_WARNING, "SFERX4: RX channel count %d is not supported in configuration [%d, %d, %d, %d, %d, %d, %d, %d ... ]!\n", psc->chcnt, psc->channels.ch_map[0], psc->channels.ch_map[1], psc->channels.ch_map[2], psc->channels.ch_map[3], psc->channels.ch_map[4], psc->channels.ch_map[5], psc->channels.ch_map[6], psc->channels.ch_map[7]); return -EINVAL; } + switch (chfmt) { + case IFMT_CH_3210: *pwr_ch_mask = 0b1111; break; + case IFMT_CH_xx10: *pwr_ch_mask = 0b0011; break; + case IFMT_CH_xxx0: *pwr_ch_mask = 0b0001; break; + case IFMT_CH_xx1x: *pwr_ch_mask = 0b0010; break; + case IFMT_CH_x2x0: *pwr_ch_mask = 0b0101; break; + case IFMT_CH_32xx: *pwr_ch_mask = 0b1100; break; + case IFMT_CH_x2xx: *pwr_ch_mask = 0b0100; break; + case IFMT_CH_3xxx: *pwr_ch_mask = 0b1000; break; + } + unsigned fe_format = ((bfmt.bits == 8) ? IFMT_8BIT : (bfmt.bits == 12) ? IFMT_12BIT : IFMT_16BIT); - return _configure_simple_fe_generic(fe, psc, bfmt.bits, 1, fe_format,chfmt, chns, pfc); + return _configure_simple_fe_generic(fe, psc, bfmt.bits, 1, fe_format, chfmt, chns, pfc); } int sfe_rx4_throttle(const sfe_cfg_t* fe, bool enable, uint8_t send, uint8_t skip) { + lldev_t lldev = fe->dev; int res; // Put everything into reset @@ -448,9 +464,9 @@ int sfe_rx4_throttle(const sfe_cfg_t* fe, bool enable, uint8_t send, uint8_t ski return res; if (!enable) { - USDR_LOG("STRM", USDR_LOG_INFO, "SFERX4: burst throttling is disabled\n"); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "SFERX4: burst throttling is disabled\n"); } else { - USDR_LOG("STRM", USDR_LOG_INFO, "SFERX4: burst throttling is enabled %d/%d\n", + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "SFERX4: burst throttling is enabled %d/%d\n", send + 1, (unsigned)send + skip + 2); } return 0; @@ -459,11 +475,12 @@ int sfe_rx4_throttle(const sfe_cfg_t* fe, bool enable, uint8_t send, uint8_t ski int sfe_rx4_startstop(const sfe_cfg_t* fe, bool start) { + lldev_t lldev = fe->dev; int res = _sfe_srx4_reg_set(fe, FE_CMD_RESET, (start) ? RX_SCMD_START_IMM : RX_SCMD_STOP_IMM); if (res) return res; - USDR_LOG("STRM", USDR_LOG_NOTE, "SFERX4: RX Stream configured to %s\n", (start) ? "start" : "stop"); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_NOTE, "SFERX4: RX Stream configured to %s\n", (start) ? "start" : "stop"); return 0; } @@ -478,13 +495,13 @@ int sfe_rf4_nco_enable(const sfe_cfg_t* fe, bool enable, unsigned iqaccum) res = (res) ? res : lowlevel_reg_wr32(fe->dev, fe->subdev, fe->cfg_base + FE_CMD_REG_CFG_CORDIC, 0); } - USDR_LOG("STRM", USDR_LOG_INFO, "SFERX4: NCO Active: %d\n", enable); + USDR_LL_LOG(fe->dev, "STRM", USDR_LOG_INFO, "SFERX4: NCO Active: %d\n", enable); return res; } int sfe_rf4_nco_freq(const sfe_cfg_t* fe, int32_t freq) { - USDR_LOG("STRM", USDR_LOG_INFO, "SFERX4: NCO FREQ Set to %d\n", freq); + USDR_LL_LOG(fe->dev, "STRM", USDR_LOG_INFO, "SFERX4: NCO FREQ Set to %d\n", freq); return lowlevel_reg_wr32(fe->dev, fe->subdev, fe->cfg_base + FE_CMD_REG_FREQ_CORDIC, freq); } @@ -492,30 +509,39 @@ int sfe_rf4_nco_freq(const sfe_cfg_t* fe, int32_t freq) #define MAX_EXLG_CHANS 4 #define MAX_EX_CHANS (1<dev; struct bitsfmt bfmt = get_bits_fmt(psc->sfmt); unsigned bps = bfmt.bits; if (bps != 12 && bps != 16) { - USDR_LOG("STRM", USDR_LOG_ERROR, "EXFERX: sample size %d isn't supported!\n", bps); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_ERROR, "EXFERX: sample size %d isn't supported!\n", bps); return -EINVAL; } unsigned chns = psc->chcnt; + bool pack_3x16 = false; + if (bfmt.complex) { chns *= 2; } + if ((bps == 16) && (chns == 6 || chns == 12 || chns == 24 || chns == 48)) { + chns = (chns / 3) * 4; + pack_3x16 = true; + bps = 12; + } + unsigned j, chlg = 0; for (j = 1; j <= fe->cfg_raw_chans; j <<= 1, chlg++) { if (chns == j) break; } if (j > fe->cfg_raw_chans || j == 0) { - USDR_LOG("STRM", USDR_LOG_ERROR, "EXFERX: Unsupported channel count %d!\n", chns); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_ERROR, "EXFERX: Unsupported channel count %d!\n", chns); return -EINVAL; } - if (fe->cfg_raw_chans >= MAX_EX_CHANS) { - USDR_LOG("STRM", USDR_LOG_ERROR, "EXFERX: Maximum channel count supported by the core is 16, requested %d!", fe->cfg_raw_chans); + if (fe->cfg_raw_chans > MAX_EX_CHANS) { + USDR_LL_LOG(lldev, "STRM", USDR_LOG_ERROR, "EXFERX: Maximum channel count supported by the core is 16, requested %d!", fe->cfg_raw_chans); return -EINVAL; } @@ -530,7 +556,7 @@ int exfe_rx4_configure(const sfe_cfg_t* fe, const struct stream_config* psc, str cfg.limit_burst_samples = fe->cfg_fifomaxbytes; rfe_burster_data_t data; - res = burst_fe_calculate(&cfg, psc, chns, bps, &data); + res = burst_fe_calculate(lldev, &cfg, psc, chns, bps, &data); if (res) { return res; } @@ -541,8 +567,8 @@ int exfe_rx4_configure(const sfe_cfg_t* fe, const struct stream_config* psc, str unsigned samplerperbursts = data.samples; unsigned raw_burst_sz = (bps == 12) ? (chns * samplerperbursts * 12 + 7) / 8 : chns * samplerperbursts * 2; - USDR_LOG("STRM", USDR_LOG_INFO, "EXFERX: Stream %s configured in %d bytes (%d samples x %d chans x %d bits) X %d bursts; naked burst size %d; fifo capacity %d\n", - psc->sfmt, bbytes, samplerperbursts, 1 << chlg, bps, bursts, raw_burst_sz, fifo_capacity); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "EXFERX: Stream %s configured in %d bytes (%d samples x %d chans x %d bits %s) X %d bursts; naked burst size %d; fifo capacity %d\n", + psc->sfmt, bbytes, samplerperbursts, 1 << chlg, bps, pack_3x16 ? "3x16 mode" : "", bursts, raw_burst_sz, fifo_capacity); res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_RESET, RX_SCMD_IDLE | @@ -554,14 +580,15 @@ int exfe_rx4_configure(const sfe_cfg_t* fe, const struct stream_config* psc, str res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_BURST_BYTES, bbytes - 1); res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_BURST_CAPACITY, fifo_capacity); res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_COMPACTER, chlg); - res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_PACKER, bps == 12 ? 1 : 0); + res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_PACKER, pack_3x16 ? 3 : bps == 12 ? 1 : 0); - res = res ? res : exfe_trx4_update_chmap(fe, false, bfmt.complex, chns, &psc->channels); + res = res ? res : exfe_trx4_update_chmap(fe, false, bfmt.complex, pack_3x16, chns, &psc->channels); pfc->bpb = bbytes; pfc->burstspblk = bursts; pfc->oob_len = 0; pfc->oob_off = 0; + *out_pack_3x16 = pack_3x16; return res; } @@ -569,9 +596,11 @@ int exfe_rx4_configure(const sfe_cfg_t* fe, const struct stream_config* psc, str int exfe_trx4_update_chmap(const sfe_cfg_t* fe, bool mask, bool complex, + bool pack_3x16, unsigned total_chan_num, - const channel_info_t* newmap) + const channel_info_t* newmap_orig) { + lldev_t lldev = fe->dev; int res = 0; uint8_t chmap[MAX_EX_CHANS]; uint8_t chmap_o[MAX_EX_CHANS]; @@ -579,10 +608,65 @@ int exfe_trx4_update_chmap(const sfe_cfg_t* fe, uint16_t ch_remapped[MAX_EXLG_CHANS]; memset(ch_remapped, 0, sizeof(ch_remapped)); memset(flag_swap_iq, 0, sizeof(flag_swap_iq)); - + channel_info_t pack_3x16_mmap; + channel_info_t reverse_map; unsigned lg_chans = (fe->cfg_raw_chans == 16) ? 4 : (fe->cfg_raw_chans == 8) ? 3 : (fe->cfg_raw_chans == 4) ? 2 : (fe->cfg_raw_chans == 2) ? 1 : 0; unsigned msk = mask ? ((complex ? total_chan_num / 2 : total_chan_num) - 1) : 0xff; + if (pack_3x16) { + // Every 7th and 8-th channel is ignored since it coinatins garbage anyway + for (unsigned g = 0, h = 0; g < fe->cfg_raw_chans; g++) { + if (complex) { + if ((g % 4) == 3) { + pack_3x16_mmap.ch_map[g] = 0xff; //newmap_orig->ch_map[h + (g % 4) - h]; + } else { + pack_3x16_mmap.ch_map[g] = newmap_orig->ch_map[h++]; + } + } else { + if ((g % 8) == 6 || (g % 8) == 7) { + pack_3x16_mmap.ch_map[g] = 0xff; //newmap_orig->ch_map[h + (g % 8) - 8]; + } else { + pack_3x16_mmap.ch_map[g] = newmap_orig->ch_map[h++]; + } + } + } + } + const channel_info_t* newmap = (pack_3x16) ? &pack_3x16_mmap : newmap_orig; + + // For TX we need invert in and out + if (fe->cfg_fecore_id == CORE_EXFETX_DMA32_R0 || fe->cfg_fecore_id == CORE_EXFETX_DMA32_R0_8) { + memset(reverse_map.ch_map, ~CH_SWAP_IQ_FLAG, sizeof(reverse_map.ch_map)); + + for (unsigned g = 0; g < fe->cfg_raw_chans; g++) { + if (newmap->ch_map[g] == 0xff) + continue; + + unsigned idx = (newmap->ch_map[g] & ~CH_SWAP_IQ_FLAG); + if (idx >= fe->cfg_raw_chans) + return -EINVAL; + + reverse_map.ch_map[idx] = g; + } + + for (unsigned g = 0; g < fe->cfg_raw_chans; g++) { + if (newmap->ch_map[g] == 0xff) + continue; + + if (newmap->ch_map[g] & CH_SWAP_IQ_FLAG) { + reverse_map.ch_map[g] |= CH_SWAP_IQ_FLAG; + } + } + + newmap = &reverse_map; + } + + + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "NEW_MAP %d x %d CHANS:\n", total_chan_num, complex ? 2 : 1); + for (unsigned g = 0; g < fe->cfg_raw_chans; g++) { + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "NEW_MAP[%d]: %d => %d\n", g, + newmap_orig->ch_map[g], newmap->ch_map[g]); + } + for (unsigned g = 0; g < fe->cfg_raw_chans; g = g + total_chan_num) { for (unsigned f = 0; f < total_chan_num; f++) { unsigned swp_msk = (g + f); @@ -608,10 +692,10 @@ int exfe_trx4_update_chmap(const sfe_cfg_t* fe, } for (unsigned g = 0; g < fe->cfg_raw_chans; g++) { - USDR_LOG("STRM", USDR_LOG_INFO, "EXFE: CH%d => %d (orig %d MSK %x/%d) %s", g, chmap[g], chmap_o[g], msk, total_chan_num, flag_swap_iq[g] ? "SWAP_IQ" : ""); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "EXFE: CH%d => %d (orig %d MSK %x/%d) %s", g, chmap[g], chmap_o[g], msk, total_chan_num, flag_swap_iq[g] ? "SWAP_IQ" : ""); } for (unsigned f = 0; f < lg_chans; f++) { - USDR_LOG("STRM", USDR_LOG_INFO, "EXFE: STAGE_%d: %04x", f, ch_remapped[f]); + USDR_LL_LOG(lldev, "STRM", USDR_LOG_INFO, "EXFE: STAGE_%d: %04x", f, ch_remapped[f]); } res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_SHUFFLE_0, ch_remapped[0]); @@ -621,11 +705,11 @@ int exfe_trx4_update_chmap(const sfe_cfg_t* fe, return res; } -int exfe_tx4_config(const sfe_cfg_t* fe, unsigned bmt, unsigned expand) +int exfe_tx4_config(const sfe_cfg_t* fe, unsigned bmt, unsigned expand, bool pack_3x16) { int res = 0; res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_EXPAND, expand); - res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_TXFMT12, bmt == 12 ? 1 : 0); + res = res ? res : _sfe_exrx_reg_set(fe, EXFE_CMD_TXFMT12, pack_3x16 ? 3 : bmt == 12 ? 1 : 0); return res; } diff --git a/src/lib/ipblks/streams/sfe_rx_4.h b/src/lib/ipblks/streams/sfe_rx_4.h index 81c29d67..8e9e038d 100644 --- a/src/lib/ipblks/streams/sfe_rx_4.h +++ b/src/lib/ipblks/streams/sfe_rx_4.h @@ -10,7 +10,7 @@ int sfe_rx4_check_format(const struct stream_config* psc); int sfe_rx4_configure(const sfe_cfg_t *fe, const struct stream_config* psc, - struct fifo_config* pfc); + struct fifo_config* pfc, uint64_t *pwr_ch_mask); int sfe_rx4_throttle(const sfe_cfg_t* fe, bool enable, uint8_t send, uint8_t skip); @@ -23,16 +23,17 @@ int sfe_rf4_nco_freq(const sfe_cfg_t* fe, int32_t freq); int exfe_rx4_configure(const sfe_cfg_t* fe, const struct stream_config* psc, - struct fifo_config* pfc); + struct fifo_config* pfc, bool *out_pack_3x16); int exfe_trx4_update_chmap(const sfe_cfg_t* fe, bool mask, bool complex, + bool pack_3x16, unsigned total_chan_num, const channel_info_t *newmap); int exfe_tx4_mute(const sfe_cfg_t* fe, uint64_t mutemask); -int exfe_tx4_config(const sfe_cfg_t* fe, unsigned bmt, unsigned expand); +int exfe_tx4_config(const sfe_cfg_t* fe, unsigned bmt, unsigned expand, bool pack_3x16); #endif diff --git a/src/lib/ipblks/streams/sfe_tx_4.c b/src/lib/ipblks/streams/sfe_tx_4.c index ff9d33e1..b82d988a 100644 --- a/src/lib/ipblks/streams/sfe_tx_4.c +++ b/src/lib/ipblks/streams/sfe_tx_4.c @@ -77,7 +77,7 @@ int sfe_extx4_push_ring_buffer(lldev_t dev, *creg2 = tsh; } - USDR_LOG("EXTX", USDR_LOG_DEBUG, "Push buffer %d [%08x:%08x:%08x:%08x] SZ=%d SPS=%d BRST=%d TS=%" PRId64 "\n",cfg_base, + USDR_LL_LOG(dev, "EXTX", USDR_LOG_DEBUG, "Push buffer %d [%08x:%08x:%08x:%08x] SZ=%d SPS=%d BRST=%d TS=%" PRId64 "\n",cfg_base, cfg0, cfg1, tsh, tsl, bytes, samples, bursts, timestamp); return res ? res : lowlevel_reg_wr32(dev, subdev, cfg_base + 3, tsl); diff --git a/src/lib/ipblks/streams/stream_limesdr.c b/src/lib/ipblks/streams/stream_limesdr.c index ac5f1fc6..73504815 100644 --- a/src/lib/ipblks/streams/stream_limesdr.c +++ b/src/lib/ipblks/streams/stream_limesdr.c @@ -1,11 +1,12 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT +#include #include #include #include +#include #include -#include #include #include "stream_limesdr.h" @@ -46,7 +47,7 @@ struct stream_limesdr { // Streaming parameters unsigned burst_count; // Bursts in packet unsigned burst_symbs; - unsigned burst_bytes; // Busrt bytes contating samples + unsigned burst_bytes; // Burst bytes containing samples unsigned burst_host_bytes; unsigned block_samples; // Number samples in one process block (4K) unsigned tx_sampl_c; @@ -103,7 +104,7 @@ int _limestr_stream_recv(stream_handle_t* str, unsigned bidx; //Burst index in the lowlevel buffer //unsigned - uint64_t brst_time; + uint64_t brst_time = ~0ULL; uint64_t fsym_time = ~0ULL; do { @@ -412,7 +413,7 @@ int create_limesdr_stream(device_t* device, stream_limesdr_t* strdev; stream_t sid; char dfmt[256]; - strncpy(dfmt, dformat, sizeof(dfmt)); + snprintf(dfmt, sizeof(dfmt), "%s", dformat); struct parsed_data_format pfmt; if (stream_parse_dformat(dfmt, &pfmt)) { diff --git a/src/lib/ipblks/streams/stream_sfetrx4_dma32.c b/src/lib/ipblks/streams/stream_sfetrx4_dma32.c index 24dd3ac1..7dd90b58 100644 --- a/src/lib/ipblks/streams/stream_sfetrx4_dma32.c +++ b/src/lib/ipblks/streams/stream_sfetrx4_dma32.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -56,7 +57,7 @@ struct stream_sfetrx_dma32 { // Cached values unsigned cnf_base; unsigned sync_base; // TODO: for compatibility with OLD APIs - unsigned cnfrd_base; // Reabdack address for OLD API + unsigned cnfrd_base; // Readback address for OLD API unsigned pkt_symbs; // Total number of symbols in a transaction (all bursts) unsigned pkt_bytes; // Wire bytes for a transaction (excl. packing overhead) @@ -80,12 +81,15 @@ struct stream_sfetrx_dma32 { uint8_t fe_old_tx_mute; // keep OLD TX FE in sync with host state uint8_t fe_old_tx_swap; // keep OLD TX FE in sync with host state unsigned fe_chans; // Number of active channels in frontend - unsigned fe_complex; // Compex data streaming + unsigned fe_complex; // Complex data streaming union { sfe_cfg_t srx4; } storage; extxcfg_cache_t cstx4; + + uint64_t hw_pwr_mask; // Powered chans (hw) + uint8_t pack_3x16; }; typedef struct stream_sfetrx_dma32 stream_sfetrx_dma32_t; @@ -100,11 +104,11 @@ int _sfetrx4_destroy(stream_handle_t* str) stream_sfetrx_dma32_t* stream = (stream_sfetrx_dma32_t*)str; lldev_t dev = stream->base.dev->dev; - USDR_LOG("DSTR", USDR_LOG_DEBUG, "Destroying strem %d\n", stream->ll_streamo); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_DEBUG, "Destroying strem %d\n", stream->ll_streamo); int res; if (stream->type == USDR_ZCPY_RX) { - //Grcefull stop + //Gracefull stop res = lowlevel_reg_wr32(dev, 0, stream->cnf_base + 1, 0); if (res) @@ -158,23 +162,60 @@ int _sfetrx4_stream_recv(stream_handle_t* str, res = ops->recv_dma_wait(dev, 0, stream->ll_streamo, (void**)&dma_buf, &oob_data, &oob_size, timeout); - if (res < 0) + if (res == -ETIMEDOUT) { + } else if (res < 0) return res; + uint32_t dma_stat = oob_data[1]; + unsigned dbno_inram; // BUF# filled in FIFO RAM + unsigned dbno_xfred; // BUF# transferred to host + unsigned dbno_ntfysent; // BUF# notification sent + unsigned dbno_confirmed; // BUF# confirmed by user (can reuse again) + unsigned fe_stat; // BUF# in FE + bool srdy = dma_stat >> 31; + fe_stat = (((dma_stat >> 22) & 3) << 4) | + (((dma_stat >> 14) & 3) << 2) | + (((dma_stat >> 6) & 3) << 0); + dbno_confirmed = (dma_stat >> 0) & 0x3f; + dbno_xfred = (dma_stat >> 8) & 0x3f; + dbno_inram = (dma_stat >> 16) & 0x3f; + dbno_ntfysent = (dma_stat >> 24) & 0x3f; + +#if DEBUG_DMA_BUFFERS + bool santify_fail = false; + { + unsigned a = dbno_inram, b = dbno_xfred, c = dbno_ntfysent; + unsigned d1 = (a - b) & 0x3f; + unsigned d2 = (b - c) & 0x3f; + if ((d1 >= 32) || (d2 >= 32)) { + santify_fail = true; + } + } +#else +#define santify_fail false +#endif + + if (res == -ETIMEDOUT) { + USDR_LL_LOG(dev, "UDMS", santify_fail ? USDR_LOG_ERROR : USDR_LOG_INFO, "Recv %016" PRIx64 ".%016" PRIx64 " TIMEDOUT:%d buf=%p seq=%16" PRIu64 " %d.%2d/%2d/%2d/%2d/%2d\n", oob_data[0], oob_data[1], res, dma_buf, + stream->rcnt, srdy, fe_stat, dbno_inram, dbno_xfred, dbno_ntfysent, dbno_confirmed); + return res; + } + if (oob_data[0] & 0xffffff) { unsigned pkt_lost = oob_data[0] & 0xffffff; - USDR_LOG("UDMS", USDR_LOG_INFO, "Recv %016" PRIx64 ".%016" PRIx64 " EXTRA:%d buf=%p seq=%16" PRIu64 "\n", oob_data[0], oob_data[1], res, dma_buf, - stream->rcnt); + USDR_LL_LOG(dev, "UDMS", santify_fail ? USDR_LOG_ERROR : USDR_LOG_INFO, "Recv %016" PRIx64 ".%016" PRIx64 " EXTRA:%d buf=%p seq=%16" PRIu64 " %d.%2d/%2d/%2d/%2d/%2d\n", oob_data[0], oob_data[1], res, dma_buf, + stream->rcnt, srdy, fe_stat, dbno_inram, dbno_xfred, dbno_ntfysent, dbno_confirmed); stream->stats.fe_drop += pkt_lost; stream->r_ts += stream->pkt_symbs * pkt_lost; } else if ((oob_data[0] >> 32) != stream->burst_mask) { - USDR_LOG("UDMS", USDR_LOG_INFO, "Recv %016" PRIx64 ".%016" PRIx64 " [%08x] EXTRA:%d buf=%p seq=%16" PRIu64 "\n", oob_data[0], oob_data[1], stream->burst_mask, res, dma_buf, - stream->rcnt); + USDR_LL_LOG(dev, "UDMS", santify_fail ? USDR_LOG_ERROR : USDR_LOG_INFO, "Recv %016" PRIx64 ".%016" PRIx64 " [%08x] EXTRA:%d buf=%p seq=%16" PRIu64 " %d.%2d/%2d/%2d/%2d/%2d\n", oob_data[0], oob_data[1], stream->burst_mask, res, dma_buf, + stream->rcnt, srdy, fe_stat, dbno_inram, dbno_xfred, dbno_ntfysent, dbno_confirmed); } else { - USDR_LOG("UDMS", USDR_LOG_DEBUG, "Recv %016" PRIx64 ".%016" PRIx64 " EXTRA:- buf=%p seq=%16" PRIu64 "\n", oob_data[0], oob_data[1], dma_buf, - stream->rcnt); + USDR_LL_LOG(dev, "UDMS", + santify_fail ? USDR_LOG_ERROR : USDR_LOG_DEBUG, "Recv %016" PRIx64 ".%016" PRIx64 " EXTRA:- buf=%p seq=%16" PRIu64 " %d.%2d/%2d/%2d/%2d/%2d\n", oob_data[0], oob_data[1], dma_buf, + stream->rcnt, srdy, fe_stat, dbno_inram, dbno_xfred, dbno_ntfysent, dbno_confirmed); } stream->stats.pktok ++; @@ -255,7 +296,7 @@ void parse_txcore_stat(uint32_t stat[4], txcore_statistics_t* s) // Understanding counters (PCIe mode) // usrbuf_posted -- PCIe Buffer metadata posted // usrbuf_requested -- PCIe Buffer all MemRd requests are sent - // usrbuf_completed -- PCIe Beffer data has been placed into FIFO RAM + // usrbuf_completed -- PCIe Buffer data has been placed into FIFO RAM // usrbuf_aired -- PCIe Buffer has been completly played out (available for reuse) bool usb = (stat[0] & 0x8); @@ -321,7 +362,7 @@ int _sfetrx4_stream_send(stream_handle_t* str, if (stream->type != USDR_ZCPY_TX) return -ENOTSUP; - if (stream->storage.srx4.cfg_fecore_id == CORE_EXFETX_DMA32_R0) { + if (stream->storage.srx4.cfg_fecore_id == CORE_EXFETX_DMA32_R0 || stream->storage.srx4.cfg_fecore_id == CORE_EXFETX_DMA32_R0_8) { res = _extx_burstup(samples, brst_samples, &lgbursts); if (res) return res; @@ -366,7 +407,7 @@ int _sfetrx4_stream_send(stream_handle_t* str, } parse_txcore_stat(stat, &st); - USDR_LOG("UDMS", USDR_LOG_ERROR, "Send timed out (Pstd/Reqd/Cpld/Aired) %2d/%2d/%2d/%2d TAGS:%d FIFO:%d DROP_DMA:%d DROP_FE:%d OVF:%d FULL:%d CPL_NO_DATA:%d FE_SENT:%d READY:%d -- %08x.%08x.%08x.%08x --\n", + USDR_LL_LOG(dev, "UDMS", USDR_LOG_ERROR, "Send timed out (Pstd/Reqd/Cpld/Aired) %2d/%2d/%2d/%2d TAGS:%d FIFO:%d DROP_DMA:%d DROP_FE:%d OVF:%d FULL:%d CPL_NO_DATA:%d FE_SENT:%d READY:%d -- %08x.%08x.%08x.%08x --\n", st.usrbuf_posted, st.usrbuf_requested, st.usrbuf_completed, st.usrbuf_aired, st.pcietags, st.fifo_used, st.drop_dma, st.drop_fe, st.stat_dmareq_ovf, st.fifo_full, st.stat_cpl_nodata, st.bursts_sent, st.core_ready, @@ -402,8 +443,8 @@ int _sfetrx4_stream_send(stream_handle_t* str, //unsigned burst_lost = (stream->stats.fe_drop - pfe) + (stream->stats.dma_drop - pda); stream->stats.pktok ++; - USDR_LOG("UDMS", USDR_LOG_DEBUG, "Send stat %d -- %08x.%08x.%08x.%08x -- HOST:%d WIRE:%d\n" - " Buff (Pstd/Reqd/Cpld/Aired) %2d/%2d/%2d/%2d DropFE:%"PRId64" DropDMA:%"PRId64" TAGS:%d FIFO:%d\n", + USDR_LL_LOG(dev, "UDMS", USDR_LOG_DEBUG, "Send stat %d -- %08x.%08x.%08x.%08x -- HOST:%d WIRE:%d\n" + " Buff (Pstd/Reqd/Cpld/Aired) %2d/%2d/%2d/%2d DropFE:%" PRId64 " DropDMA:%" PRId64 " TAGS:%d FIFO:%d\n", stat_sz, stat[0], stat[1], stat[2], stat[3], host_bytes, wire_bytes, st.usrbuf_posted, st.usrbuf_requested, st.usrbuf_completed, st.usrbuf_aired, stream->stats.fe_drop, stream->stats.dma_drop, st.pcietags, st.fifo_used); @@ -432,17 +473,17 @@ int _sfetrx4_stream_send(stream_handle_t* str, for (unsigned b = 0; b < bursts; b++) { stream->tf_data(nstreams, host_bytes, &dma_buffer, wire_bytes); for (unsigned i = 0; i < stream->channels; i++) { - nstreams[i] += host_off; + nstreams[i] = (char*)nstreams[i] + host_off; } - dma_buffer += (wire_bytes + brst_align) & ~brst_align; + dma_buffer = (char*)dma_buffer + ((wire_bytes + brst_align) & ~brst_align); } - wire_len = dma_buffer - buffer; + wire_len = (char*)dma_buffer - (char*)buffer; } else { wire_len = wire_bytes * bursts; stream->tf_data((const void**)stream_buffs, host_bytes * bursts, &buffer, wire_len); } - USDR_LOG("UDMS", USDR_LOG_DEBUG, "Send %lld [TS:%lld LG:%d BRST:%d]\n", + USDR_LL_LOG(dev, "UDMS", USDR_LOG_DEBUG, "Send %lld [TS:%lld LG:%d BRST:%d]\n", (long long)stream->rcnt, (long long)timestamp, lgbursts, (unsigned)wire_len); stream->rcnt++; @@ -472,7 +513,7 @@ static int _sfetrx4_op(stream_handle_t* str, start = true; break; default: - USDR_LOG("UDMS", USDR_LOG_INFO, "Stream[%d] STOP; STATS bytes = %" PRIu64 ", samples = %" PRIu64 ", dropped_fe/dropped_dma/rcvd = %"PRIu64"/%"PRIu64"/%"PRIu64"\n", + USDR_LL_LOG(dev, "UDMS", USDR_LOG_INFO, "Stream[%d] STOP; STATS bytes = %" PRIu64 ", samples = %" PRIu64 ", dropped_fe/dropped_dma/rcvd = %" PRIu64 "/%" PRIu64 "/%" PRIu64 "\n", stream->ll_streamo, stream->stats.wirebytes, stream->stats.symbols, stream->stats.fe_drop, stream->stats.dma_drop, stream->stats.pktok); start = false; } @@ -488,7 +529,7 @@ static int _sfetrx4_op(stream_handle_t* str, if (res) return res; } else { - // Assuming Compex IQ + // Assuming Complex IQ unsigned lgchcnt = (stream->fe_chans == 1) ? 0 : (stream->fe_chans == 2) ? 1 : (stream->fe_chans == 4) ? 2 : 3; @@ -535,10 +576,10 @@ int _sfetrx4_option_set(stream_handle_t* str, const char* name, int64_t in_val) const channel_info_t *new_map = (const channel_info_t *)in_val; if (stream->type == USDR_ZCPY_RX) { - if (stream->storage.srx4.cfg_fecore_id != CORE_EXFERX_DMA32_R0) + if (stream->storage.srx4.cfg_fecore_id != CORE_EXFERX_DMA32_R0 && stream->storage.srx4.cfg_fecore_id != CORE_EXFERX_DMA32_R0_8) return -ENOTSUP; } else if (stream->type == USDR_ZCPY_TX) { - if (stream->storage.srx4.cfg_fecore_id != CORE_EXFETX_DMA32_R0) { + if (stream->storage.srx4.cfg_fecore_id != CORE_EXFETX_DMA32_R0 && stream->storage.srx4.cfg_fecore_id != CORE_EXFETX_DMA32_R0_8) { unsigned swap_ab_flag; int res = fe_tx4_swap_ab_get(stream->fe_chans, new_map, &swap_ab_flag); if (res) @@ -560,10 +601,11 @@ int _sfetrx4_option_set(stream_handle_t* str, const char* name, int64_t in_val) return exfe_trx4_update_chmap(&stream->storage.srx4, stream->type == USDR_ZCPY_TX, stream->fe_complex, - (stream->fe_complex ? 2 : 1) * stream->fe_chans, + stream->pack_3x16, + (stream->fe_complex ? 2 : 1) * (stream->pack_3x16 ? (stream->fe_chans * 4 / 3) : stream->fe_chans), (const channel_info_t *)in_val); } else if (stream->type == USDR_ZCPY_TX && (strcmp(name, "mute") == 0)) { - if (stream->storage.srx4.cfg_fecore_id != CORE_EXFETX_DMA32_R0) { + if (stream->storage.srx4.cfg_fecore_id != CORE_EXFETX_DMA32_R0 && stream->storage.srx4.cfg_fecore_id != CORE_EXFETX_DMA32_R0_8) { stream->fe_old_tx_mute = in_val & 3; return sfe_tx4_upd(&stream->storage.srx4, stream->sync_base, @@ -620,7 +662,7 @@ int parse_sfetrx4(const char* dformat, const channel_info_t *channels, unsigned bool bifurcation = true; struct parsed_data_format pfmt; - strncpy(out->dfmt, dformat, sizeof(out->dfmt)); + snprintf(out->dfmt, sizeof(out->dfmt), "%s", dformat); if (stream_parse_dformat(out->dfmt, &pfmt)) { return -EINVAL; } @@ -676,9 +718,11 @@ static int initialize_stream_rx_32(device_t* device, bool data_lane_bifurcation) { int res; + lldev_t dev = device->dev; stream_sfetrx_dma32_t* strdev; + uint64_t hw_chan_msk = 0; - res = dma_rx32_reset(device->dev, 0, sx_base); + res = dma_rx32_reset(dev, 0, sx_base); if (res) return res; @@ -700,7 +744,7 @@ static int initialize_stream_rx_32(device_t* device, res = sfe_rx4_check_format(&sc); if (res) { if (pfmt.wire_fmt != NULL) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "Unsupported wire format '%s' by the core\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "Unsupported wire format '%s' by the core\n", sc.sfmt); return res; } @@ -708,6 +752,7 @@ static int initialize_stream_rx_32(device_t* device, // Wire format not supported, need transform function // suppose i16 but maintain complex / real format sc.sfmt = (*pfmt.host_fmt == 'C' || *pfmt.host_fmt == 'c') ? "ci16" : "i16"; + res = 0; } //Find transform function @@ -716,12 +761,12 @@ static int initialize_stream_rx_32(device_t* device, 1, logicchs); if (funcs.cfunc == NULL || funcs.sfunc == NULL) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "No transform function '%s'->'%s' are available for 1->%d demux\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "No transform function '%s'->'%s' are available for 1->%d demux\n", sc.sfmt, pfmt.host_fmt, logicchs); return -EINVAL; } if (is_transform_dummy(funcs.cfunc)) { - USDR_LOG("DSTR", USDR_LOG_INFO, "No transformation!\n"); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_INFO, "No transformation!\n"); } struct bitsfmt bfmt = get_bits_fmt(sc.sfmt); @@ -738,11 +783,11 @@ static int initialize_stream_rx_32(device_t* device, sc.channels.ch_map[2] = sc.channels.ch_map[0] + 2; sc.channels.ch_map[3] = sc.channels.ch_map[1] + 2; } else { - USDR_LOG("DSTR", USDR_LOG_ERROR, "Bifurcation is only valid for single channel complex\n"); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "Bifurcation is only valid for single channel complex\n"); return -EINVAL; } if (sc.spburst % 2) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "In Bifurcation number of samples should be even\n"); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "In Bifurcation number of samples should be even\n"); return -EINVAL; } @@ -750,9 +795,10 @@ static int initialize_stream_rx_32(device_t* device, } // TODO obtain exfe configuration constants - res = (fecfg->cfg_fecore_id == CORE_EXFERX_DMA32_R0) ? - exfe_rx4_configure(fecfg, &sc, &fc) : - sfe_rx4_configure(fecfg, &sc, &fc); + bool pack_3x16 = false; + res = (fecfg->cfg_fecore_id == CORE_EXFERX_DMA32_R0 || fecfg->cfg_fecore_id == CORE_EXFERX_DMA32_R0_8) ? + exfe_rx4_configure(fecfg, &sc, &fc, &pack_3x16) : + sfe_rx4_configure(fecfg, &sc, &fc, &hw_chan_msk); if (res) return res; @@ -803,7 +849,7 @@ static int initialize_stream_rx_32(device_t* device, strdev->pkt_bytes = sparams.block_size; strdev->host_bytes = funcs.sfunc(sparams.block_size, false); - strdev->wire_bps = 8 * strdev->pkt_bytes / strdev->pkt_symbs; + strdev->wire_bps = 8 * strdev->pkt_bytes / strdev->pkt_symbs / strdev->channels; strdev->tf_data = funcs.cfunc; strdev->tf_size = funcs.sfunc; @@ -828,8 +874,10 @@ static int initialize_stream_rx_32(device_t* device, strdev->fe_complex = bfmt.complex; strdev->storage.srx4 = *fecfg; - USDR_LOG("DSTR", USDR_LOG_INFO, "RX: Samples=%d Bps=%d WireBytes=%d HostBytes=%d Bursts=%d\n", - strdev->pkt_symbs, strdev->wire_bps, strdev->pkt_bytes, strdev->host_bytes, strdev->burst_count); + strdev->hw_pwr_mask = hw_chan_msk; + strdev->pack_3x16 = pack_3x16; + USDR_LL_LOG(dev, "DSTR", USDR_LOG_INFO, "RX: Samples=%d Bps=%dx%d WireBytes=%d HostBytes=%d Bursts=%d PwrMask=%" PRIx64 " Pack3x16=%d\n", + strdev->pkt_symbs, strdev->wire_bps, strdev->channels, strdev->pkt_bytes, strdev->host_bytes, strdev->burst_count, strdev->hw_pwr_mask, strdev->pack_3x16); *outu = strdev; return 0; @@ -880,8 +928,9 @@ static int initialize_stream_tx_32(device_t* device, bool dont_check_fw) { int res; + lldev_t dev = device->dev; stream_sfetrx_dma32_t* strdev; - + uint64_t pwr_hw_mask = 0; struct stream_config sc; unsigned logicchs = chcount; @@ -898,10 +947,10 @@ static int initialize_stream_tx_32(device_t* device, uint64_t fwid; res = usdr_device_vfs_obj_val_get_u64(device, "/dm/revision", &fwid); if (res) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "Unable to check comatability firmware!\n"); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "Unable to check comatability firmware!\n"); } if (get_xilinx_rev_h(fwid & 0xffffffff) < get_xilinx_rev_h(MINIM_FWID_COMPAT)) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "You're running outdated firmware, please update! CurrentID=%08x MinimalID=%08x\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "You're running outdated firmware, please update! CurrentID=%08x MinimalID=%08x\n", (uint32_t)(fwid & 0xffffffff), MINIM_FWID_COMPAT); return -ECONNRESET; @@ -917,7 +966,7 @@ static int initialize_stream_tx_32(device_t* device, res = sfe_tx4_check_format(&sc); if (res) { if (pfmt.wire_fmt != NULL) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "TX Stream:Unsupported wire format '%s' by the core\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "TX Stream:Unsupported wire format '%s' by the core\n", sc.sfmt); return res; } @@ -925,6 +974,7 @@ static int initialize_stream_tx_32(device_t* device, // Wire format not supported, need transform function // suppose i16 but maintain complex / real format sc.sfmt = (*pfmt.host_fmt == 'C' || *pfmt.host_fmt == 'c') ? "ci16" : "i16"; + res = 0; } //Find transform function @@ -933,12 +983,12 @@ static int initialize_stream_tx_32(device_t* device, logicchs, 1); if (funcs.cfunc == NULL || funcs.sfunc == NULL) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "TX Stream: No transform function '%s'->'%s' are available for %d->1 mux\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "TX Stream: No transform function '%s'->'%s' are available for %d->1 mux\n", pfmt.host_fmt, sc.sfmt, logicchs); return -EINVAL; } if (is_transform_dummy(funcs.cfunc)) { - USDR_LOG("DSTR", USDR_LOG_INFO, "TX Stream: No transformation!\n"); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_INFO, "TX Stream: No transformation!\n"); } struct bitsfmt bfmt = get_bits_fmt(sc.sfmt); @@ -949,19 +999,18 @@ static int initialize_stream_tx_32(device_t* device, if ((bfmt.complex) && (sc.chcnt == 1)) { sc.chcnt = 2; } else { - USDR_LOG("DSTR", USDR_LOG_ERROR, "Bifurcation is only valid for single channel complex"); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "Bifurcation is only valid for single channel complex"); return -EINVAL; } } - unsigned bits_per_single_sym = bfmt.bits * (bfmt.complex ? 2 : 1); - // Check core fe sanity + bool pack_3x16 = false; unsigned fe_old_tx_swap = 0; unsigned fe_old_tx_mute = 0; if (fecfg->cfg_fecore_id == CORE_SFETX_DMA32_R0) { if (!bfmt.complex || bfmt.bits != 16) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "Only 16 bit complex signals supported in Simple TX FE!\n"); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "Only 16 bit complex signals supported in Simple TX FE!\n"); return -EINVAL; } @@ -974,12 +1023,20 @@ static int initialize_stream_tx_32(device_t* device, } fe_old_tx_mute = (sc.chcnt == 1) ? (fe_old_tx_swap ? 1 : 2) : 0; + pwr_hw_mask = (sc.chcnt == 2) ? 0b1111 : (fe_old_tx_swap ? 0b1100 : 0b0011); } else { if (sc.chcnt > (fecfg->cfg_raw_chans / (bfmt.complex ? 2 : 1))) return -EINVAL; unsigned llcanhs = (bfmt.complex ? 2 : 1) * sc.chcnt; unsigned expand; + unsigned lbits = bfmt.bits; + if ((lbits == 16) && (llcanhs == 6 || llcanhs == 12 || llcanhs == 24 || llcanhs == 48)) { + llcanhs = (llcanhs / 3) * 4; + pack_3x16 = true; + lbits = 12; + } + switch (llcanhs) { case 1: expand = 0; break; case 2: expand = 1; break; @@ -988,12 +1045,13 @@ static int initialize_stream_tx_32(device_t* device, case 16: expand = 4; break; case 32: expand = 5; break; default: + USDR_LL_LOG(dev, "DSTR", USDR_LOG_ERROR, "TX: Unable to deliver %d chans in %d bits!\n", llcanhs, lbits); return -EINVAL; } res = res ? res : exfe_tx4_mute(fecfg, 0); - res = res ? res : exfe_tx4_config(fecfg, bfmt.bits, expand); - res = res ? res : exfe_trx4_update_chmap(fecfg, true, bfmt.complex, llcanhs, &sc.channels); + res = res ? res : exfe_tx4_config(fecfg, lbits, expand, pack_3x16); + res = res ? res : exfe_trx4_update_chmap(fecfg, true, bfmt.complex, pack_3x16, llcanhs, &sc.channels); if (res) { return res; @@ -1004,6 +1062,7 @@ static int initialize_stream_tx_32(device_t* device, if (res) return res; + unsigned bits_per_single_sym = bfmt.bits * (bfmt.complex ? 2 : 1); lowlevel_stream_params_t sparams; stream_t sid; lowlevel_ops_t* dops = lowlevel_get_ops(device->dev); @@ -1027,19 +1086,19 @@ static int initialize_stream_tx_32(device_t* device, sparams.out_max_bursts = 1; if (sparams.block_size > max_mtu) { - if (fecfg->cfg_fecore_id == CORE_EXFETX_DMA32_R0) { + if (fecfg->cfg_fecore_id == CORE_EXFETX_DMA32_R0 || fecfg->cfg_fecore_id == CORE_EXFETX_DMA32_R0_8) { unsigned max_burst_sps = 8 * max_mtu / sparams.bits_per_sym; unsigned lgbrst; res = _extx_burstup(pktsyms, max_burst_sps, &lgbrst); if (res) { - USDR_LOG("DSTR", USDR_LOG_CRITICAL_WARNING, "TX Stream couldn't breakup %d bytes in bursts, maximum per burst is %d!\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_CRITICAL_WARNING, "TX Stream couldn't breakup %d bytes in bursts, maximum per burst is %d!\n", sparams.block_size, max_mtu); goto fail_dealloc; } sparams.out_max_bursts = (1 << lgbrst); } else { - USDR_LOG("DSTR", USDR_LOG_CRITICAL_WARNING, "TX Stream maximum MTU is %d bytes, we need %d to deliver %d samples blocksize!\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_CRITICAL_WARNING, "TX Stream maximum MTU is %d bytes, we need %d to deliver %d samples blocksize!\n", max_mtu, sparams.block_size, pktsyms); res = -EINVAL; goto fail_dealloc; @@ -1048,7 +1107,7 @@ static int initialize_stream_tx_32(device_t* device, res = dops->stream_initialize(device->dev, 0, &sparams, &sid); if (res) { - USDR_LOG("DSTR", USDR_LOG_CRITICAL_WARNING, "TX Stream couldn't initialize for %d block size\n", sparams.block_size); + USDR_LL_LOG(dev, "DSTR", USDR_LOG_CRITICAL_WARNING, "TX Stream couldn't initialize for %d block size\n", sparams.block_size); goto fail_dealloc; } @@ -1059,7 +1118,7 @@ static int initialize_stream_tx_32(device_t* device, } pktsyms = 8 * sparams.out_mtu_size / (hardware_channels * bits_per_single_sym); - USDR_LOG("DSTR", USDR_LOG_INFO, "TX Stream: No desired packetsize were set, assuminng maximum %d x %d sps (%d blocksize per burst)\n", + USDR_LL_LOG(dev, "DSTR", USDR_LOG_INFO, "TX Stream: No desired packetsize were set, assuminng maximum %d x %d sps (%d blocksize per burst)\n", pktsyms, sparams.out_max_bursts, (unsigned)sparams.out_mtu_size); sparams.block_size = sparams.out_mtu_size; @@ -1101,7 +1160,7 @@ static int initialize_stream_tx_32(device_t* device, strdev->burst_mask = 0; strdev->burst_count = sparams.out_max_bursts; - strdev->burst_align_bytes = (fecfg->cfg_fecore_id == CORE_EXFETX_DMA32_R0) ? 16 : 1; + strdev->burst_align_bytes = (fecfg->cfg_fecore_id == CORE_EXFETX_DMA32_R0_8) ? 32 : (fecfg->cfg_fecore_id == CORE_EXFETX_DMA32_R0) ? 16 : 1; strdev->fe_old_tx_mute = fe_old_tx_mute; strdev->fe_old_tx_swap = fe_old_tx_swap; @@ -1110,8 +1169,10 @@ static int initialize_stream_tx_32(device_t* device, strdev->storage.srx4 = *fecfg; extxcfg_cache_init(&strdev->cstx4); - USDR_LOG("DSTR", USDR_LOG_INFO, "TX: Samples=%d Bps=%d WireBytes=%d HostBytes=%d Bursts=%d\n", - strdev->pkt_symbs, strdev->wire_bps, strdev->pkt_bytes, strdev->host_bytes, strdev->burst_count); + strdev->hw_pwr_mask = pwr_hw_mask; + strdev->pack_3x16 = pack_3x16; + USDR_LL_LOG(dev, "DSTR", USDR_LOG_INFO, "TX: Samples=%d Bps=%dx%d WireBytes=%d HostBytes=%d Bursts=%d PwrMask=%" PRIx64 " Pack3x16=%d\n", + strdev->pkt_symbs, strdev->wire_bps, strdev->channels, strdev->pkt_bytes, strdev->host_bytes, strdev->burst_count, strdev->hw_pwr_mask, strdev->pack_3x16); *outu = strdev; return 0; @@ -1149,7 +1210,7 @@ int create_sfetrx4_stream(device_t* device, fecfg.cfg_base = fe_base; fecfg.cfg_fifomaxbytes = fe_fifobsz; - strncpy(dfmt, dformat, sizeof(dfmt)); + snprintf(dfmt, sizeof(dfmt), "%s", dformat); struct parsed_data_format pfmt; if (stream_parse_dformat(dfmt, &pfmt)) { return -EINVAL; @@ -1158,9 +1219,10 @@ int create_sfetrx4_stream(device_t* device, switch (core_id) { case CORE_SFERX_DMA32_R0: case CORE_EXFERX_DMA32_R0: + case CORE_EXFERX_DMA32_R0_8: // TODO obtain dynamic config - fecfg.cfg_word_bytes = (core_id == CORE_SFERX_DMA32_R0) ? 8 : 16; - fecfg.cfg_raw_chans = (core_id == CORE_SFERX_DMA32_R0) ? 4 : 8; + fecfg.cfg_word_bytes = (core_id == CORE_SFERX_DMA32_R0) ? 8 : (core_id == CORE_EXFERX_DMA32_R0) ? 16 : 32; + fecfg.cfg_raw_chans = (core_id == CORE_SFERX_DMA32_R0) ? 4 : (core_id == CORE_EXFERX_DMA32_R0) ? 8 : 16; fecfg.cfg_dma_align_bytes = fecfg.cfg_word_bytes; res = initialize_stream_rx_32(device, chcount, channels, pktsyms, @@ -1170,9 +1232,10 @@ int create_sfetrx4_stream(device_t* device, break; case CORE_SFETX_DMA32_R0: case CORE_EXFETX_DMA32_R0: + case CORE_EXFETX_DMA32_R0_8: // TODO obtain dynamic config - fecfg.cfg_word_bytes = (core_id == CORE_SFETX_DMA32_R0) ? 8 : 16; - fecfg.cfg_raw_chans = (core_id == CORE_SFETX_DMA32_R0) ? 4 : 8; + fecfg.cfg_word_bytes = (core_id == CORE_SFETX_DMA32_R0) ? 8 : (core_id == CORE_EXFETX_DMA32_R0) ? 16 : 32; + fecfg.cfg_raw_chans = (core_id == CORE_SFETX_DMA32_R0) ? 4 : (core_id == CORE_EXFETX_DMA32_R0) ? 8 : 16; fecfg.cfg_dma_align_bytes = fecfg.cfg_word_bytes; res = initialize_stream_tx_32(device, chcount, channels, pktsyms, @@ -1212,7 +1275,7 @@ int sfetrx4_stream_sync(device_t* device, stream_sfetrx_dma32_t** pstream = (stream_sfetrx_dma32_t**)pstr; res = usdr_device_vfs_obj_val_get_u64(device, "/ll/sync/0/base", &sync_base); if (res) { - USDR_LOG("DSTR", USDR_LOG_ERROR, "SYNC: Broken device! Coulnd't obtain sync addr: %d\n", res); + USDR_LL_LOG(device->dev, "DSTR", USDR_LOG_ERROR, "SYNC: Broken device! Couldn't obtain sync addr: %d\n", res); return res; } retimer_base = sync_base; @@ -1258,7 +1321,7 @@ int sfetrx4_stream_sync(device_t* device, res = -EINVAL; } - USDR_LOG("DSTR", USDR_LOG_NOTE, "SYNC[%x] Streams %d: %s => %d\n", retimer_base, scount, synctype, res); + USDR_LL_LOG(device->dev, "DSTR", USDR_LOG_NOTE, "SYNC[%x] Streams %d: %s => %d\n", retimer_base, scount, synctype, res); return res; } diff --git a/src/lib/ipblks/streams/stream_sfetrx4_dma32.h b/src/lib/ipblks/streams/stream_sfetrx4_dma32.h index e1881692..d6f896b8 100644 --- a/src/lib/ipblks/streams/stream_sfetrx4_dma32.h +++ b/src/lib/ipblks/streams/stream_sfetrx4_dma32.h @@ -14,6 +14,9 @@ enum { CORE_EXFERX_DMA32_R0 = 256, CORE_EXFETX_DMA32_R0 = 257, + + CORE_EXFERX_DMA32_R0_8 = 258, + CORE_EXFETX_DMA32_R0_8 = 259, }; enum { @@ -53,7 +56,7 @@ int create_sfetrx4_stream(device_t* device, stream_handle_t** outu, unsigned *hw_chans_cnt); -// Syncronize streams +// Synchronize streams int sfetrx4_stream_sync(device_t* device, stream_handle_t** pstream, unsigned scount, const char* synctype); diff --git a/src/lib/ipblks/streams/streams.c b/src/lib/ipblks/streams/streams.c index 6222e399..9d250b62 100644 --- a/src/lib/ipblks/streams/streams.c +++ b/src/lib/ipblks/streams/streams.c @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT #include "streams.h" -#include +#include struct bitsfmt get_bits_fmt(const char* fmt) { @@ -17,7 +17,7 @@ struct bitsfmt get_bits_fmt(const char* fmt) else if (strcasecmp(fmt, "i16") == 0) bmft.bits = 16; else - strncpy((char*)bmft.func, fmt, sizeof(bmft.func)); + snprintf((char*)bmft.func, sizeof(bmft.func), "%s", fmt); return bmft; } diff --git a/src/lib/ipblks/streams/streams_api.c b/src/lib/ipblks/streams/streams_api.c index 3cbe2b1a..40c66f75 100644 --- a/src/lib/ipblks/streams/streams_api.c +++ b/src/lib/ipblks/streams/streams_api.c @@ -10,6 +10,8 @@ int usdr_channel_info_map_default(const usdr_channel_info_t* channels, const channel_map_info_t* map, unsigned max_chnum, channel_info_t* core_chans) { + memset(core_chans->ch_map, 0xff, sizeof(core_chans->ch_map)); + if (channels->phys_names == NULL && channels->phys_nums == NULL) { for (unsigned i = 0; i < channels->count; i++) { core_chans->ch_map[i] = i; diff --git a/src/lib/ipblks/xlnx_bitstream.c b/src/lib/ipblks/xlnx_bitstream.c index e6b55c9b..4af471b5 100644 --- a/src/lib/ipblks/xlnx_bitstream.c +++ b/src/lib/ipblks/xlnx_bitstream.c @@ -2,8 +2,10 @@ // SPDX-License-Identifier: MIT #include "xlnx_bitstream.h" +#include #include #include +#include enum { W_DUMMY = 0xffffffff, @@ -13,14 +15,92 @@ enum { W_NOP = 0x20000000, }; -int xlnx_btstrm_parse_header(const uint32_t* mem, unsigned len, xlnx_image_params_t* stat) +enum { + XLNX_REG_CRC = 0x00, + XLNX_REG_FAR = 0x01, + XLNX_REG_FDRI = 0x02, + XLNX_REG_FDRO = 0x03, + XLNX_REG_CMD = 0x04, + XLNX_REG_CTL0 = 0x05, + XLNX_REG_MASK = 0x06, + XLNX_REG_STAT = 0x07, + XLNX_REG_LOUT = 0x08, + XLNX_REG_COR0 = 0x09, + XLNX_REG_MFWR = 0x0a, + XLNX_REG_CBC = 0x0b, + XLNX_REG_IDCODE = 0x0c, + XLNX_REG_AXSS = 0x0d, + XLNX_REG_COR1 = 0x0e, + XLNX_REG_CSOB = 0x0f, + XLNX_REG_WBSTAR = 0x10, + XLNX_REG_TIMER = 0x11, + XLNX_REG_UNK12 = 0x12, + XLNX_REG_RBCRC_SW = 0x13, + XLNX_REG_UNK14 = 0x14, + XLNX_REG_UNK15 = 0x15, + XLNX_REG_BOOTSTS = 0x16, + XLNX_REG_UNK17 = 0x17, + XLNX_REG_CTL1 = 0x18, + XLNX_REG_UNK19 = 0x19, + XLNX_REG_UNK1A = 0x1a, + XLNX_REG_UNK1B = 0x1b, + XLNX_REG_UNK1C = 0x1c, + XLNX_REG_UNK1D = 0x1d, + XLNX_REG_UNK1E = 0x1e, + XLNX_REG_BSPI = 0x1f +}; + +enum { + XLNX_CMD_RCRC = 0x07, + XLNX_CMD_IPROG = 0x0f, +}; + +#define CRC32C_REFLECTED_POLY 0x82F63B78u +static inline uint32_t xlnx_btstrm_crc32_pushbit(uint32_t crc, uint32_t bit) +{ + uint32_t mix = (crc ^ bit) & 1; + crc >>= 1; + if (mix) { + crc ^= CRC32C_REFLECTED_POLY; + } + + return crc; +} + +static uint32_t xlnx_btstrm_crc32_regw(uint32_t crc, uint16_t reg, uint32_t data) +{ + // Not sure which ones to skip, but so far it works well + switch (reg) { + case XLNX_REG_BOOTSTS: + case XLNX_REG_CSOB: + case XLNX_REG_UNK12: + case XLNX_REG_UNK14: + case XLNX_REG_UNK15: + return crc; + } + + for (unsigned i = 0; i < 32; i++) { + crc = xlnx_btstrm_crc32_pushbit(crc, (data >> i) & 1); + } + + for (unsigned i = 0; i < 5; i++) { + crc = xlnx_btstrm_crc32_pushbit(crc, (reg >> i) & 1); + } + + return crc; +} + +int xlnx_btstrm_parse_header_ex(const uint32_t* mem, + unsigned len, + xlnx_image_params_t* stat, + unsigned flags) { - uint16_t blk_param; - uint16_t blk_pcount; uint32_t w; unsigned ptr = 0; bool devid_found = false; bool wbstar_found = false; + uint32_t stream_crc = 0; + uint32_t crc_word_cnt = 0; memset(stat, 0, sizeof(*stat)); @@ -51,59 +131,94 @@ int xlnx_btstrm_parse_header(const uint32_t* mem, unsigned len, xlnx_image_param if (!bo_seen || !bw_seen) return -EINVAL; - // Block FSM - bool in_param = false; - bool in_devid = false; - bool in_wbstar = false; - bool in_icmd = false; - bool in_axss = false; + uint16_t last_reg = XLNX_REG_CRC; for (; ptr < len; ptr++) { w = be32toh(mem[ptr]); - if (in_param) { - --blk_pcount; + uint8_t ptype = (w >> 29) & 0x7; + uint8_t op; + uint16_t reg; + uint32_t count; + + if (ptype == 1) { + op = (w >> 27) & 0x3; + reg = (w >> 13) & 0x3fff; + count = w & 0x7ff; + last_reg = reg; + } else if (ptype == 2) { + op = (w >> 27) & 0x3; + reg = last_reg; + count = w & 0x7ffffff; + } else { + USDR_LOG("BSTR", USDR_LOG_DEBUG, "Unrecognised WORD: n=%d w=%08x\n", ptr, w); + return -EINVAL; + } + + if (op != 2 || count == 0) { + continue; + } - if (blk_pcount == 0) - in_param = 0; + for (unsigned i = 0; i < count; i++) { + if (++ptr >= len) { + if (flags & XLNX_BSTRM_ALLOW_CROP) + break; - if (in_devid) { + return -EINVAL; + } + + w = be32toh(mem[ptr]); + if (reg == XLNX_REG_IDCODE) { stat->devid = w; devid_found = true; - } else if (in_wbstar) { + } else if (reg == XLNX_REG_WBSTAR) { stat->wbstar = w; wbstar_found = true; - } else if (in_icmd) { - if (w == 0x0000000f) + } else if (reg == XLNX_REG_CMD) { + if (w == XLNX_CMD_IPROG) stat->iprog = true; - } else if (in_axss) { + } else if (reg == XLNX_REG_AXSS) { stat->usr_access2 = w; } - continue; - } - if (w == W_NOP) - continue; + if (count == 1 && ptype == 1 && reg != 1 && reg != XLNX_REG_CMD) { + USDR_LOG("BSTR", USDR_LOG_NOTE, "Register %x: %x\n", reg, w); + } + + if (flags & XLNX_BSTRM_PARSE_F_CRC_CHECK) { + if (reg == XLNX_REG_CRC) { + crc_word_cnt++; - if (w >> 24 == 0x30) { - blk_param = (w >> 8) & 0xffff; - blk_pcount = w & 0xff; + USDR_LOG("BSTR", USDR_LOG_NOTE, "CRC BLOCK=%d BIN=%8x STR=%8x\n", crc_word_cnt, w, stream_crc); + if (w != stream_crc) { + USDR_LOG("BSTR", USDR_LOG_ERROR, "Bitstream CRC mismatch: block=%d stream=%08x infile=%08x\n", + crc_word_cnt, stream_crc, w); + return -EBADMSG; + } - if (blk_pcount > 0) { - in_param = true; - in_devid = (blk_param == 0x0180); - in_wbstar = (blk_param == 0x0200); - in_icmd = (blk_param == 0x0080); - in_axss = (blk_param == 0x01A0); + stream_crc = 0; + } else if (reg == XLNX_REG_CMD && w == XLNX_CMD_RCRC) { + stream_crc = 0; + } else { + stream_crc = xlnx_btstrm_crc32_regw(stream_crc, reg, w); + } } - continue; } - - USDR_LOG("BSTR", USDR_LOG_DEBUG, "Unrecognised WORD: n=%d w=%08x\n", ptr, w); - return -EINVAL; } + if (flags & XLNX_BSTRM_PARSE_F_CRC_CHECK) { + if (crc_word_cnt == 0) { + USDR_LOG("BSTR", USDR_LOG_ERROR, "Bitstream no CRC blocks found!\n"); + return -EBADMSG; + } + USDR_LOG("BSTR", USDR_LOG_INFO, "Bitstream CRC %d block(s) validated\n", crc_word_cnt); + } return devid_found && wbstar_found ? 0 : -ENOENT; } +int xlnx_btstrm_parse_header(const uint32_t* mem, unsigned len, xlnx_image_params_t* stat) +{ + return xlnx_btstrm_parse_header_ex(mem, len, stat, 0); +} + int xlnx_btstrm_iprgcheck( const xlnx_image_params_t* internal_golden, const xlnx_image_params_t* newimg, diff --git a/src/lib/ipblks/xlnx_bitstream.h b/src/lib/ipblks/xlnx_bitstream.h index a2253afb..f3349a8a 100644 --- a/src/lib/ipblks/xlnx_bitstream.h +++ b/src/lib/ipblks/xlnx_bitstream.h @@ -41,7 +41,16 @@ struct xlnx_image_params { }; typedef struct xlnx_image_params xlnx_image_params_t; +enum xlnx_btstrm_parse_flags { + XLNX_BSTRM_PARSE_F_CRC_CHECK = 1 << 0, + XLNX_BSTRM_ALLOW_CROP = 1 << 1, +}; + int xlnx_btstrm_parse_header(const uint32_t* mem, unsigned len, xlnx_image_params_t* stat); +int xlnx_btstrm_parse_header_ex(const uint32_t* mem, + unsigned len, + xlnx_image_params_t* stat, + unsigned flags); int xlnx_btstrm_iprgcheck( const xlnx_image_params_t* internal_golden, const xlnx_image_params_t* newimg, diff --git a/src/lib/ipblks/xlnx_mmcm.c b/src/lib/ipblks/xlnx_mmcm.c index acb2596e..555de5d6 100644 --- a/src/lib/ipblks/xlnx_mmcm.c +++ b/src/lib/ipblks/xlnx_mmcm.c @@ -77,11 +77,13 @@ int mmcm_init_raw_clkout(lldev_t dev, subdev_t subdev, if (res) return res; - USDR_LOG("MMCM", USDR_LOG_ERROR, " CLKREG %02x OLD: PHASE=%d HIGH=%d LOW=%d | MX=%d EDGE=%d NO_CNT=%d DELAY=%d\n", - clkout_reg, - (clk1_reg_old >> 13) & 0x7, (clk1_reg_old >> 6) & 0x3f, clk1_reg_old & 0x3f, - (clk2_reg_old >> 8) & 0x3, (clk2_reg_old >> 7) & 1, (clk2_reg_old >> 6) & 1, - (clk2_reg_old & 0x3f)); + USDR_LOG("MMCM", USDR_LOG_NOTE, " CLKREG %02x OLD: PHASE=%d HIGH=%d LOW=%d MX=%d EDGE=%d NO_CNT=%d DELAY=%2d | NEW: PHASE=%d HIGH=%d LOW=%d MX=%d EDGE=%d NO_CNT=%d DELAY=%2d\n", + clkout_reg, + (clk1_reg_old >> 13) & 0x7, (clk1_reg_old >> 6) & 0x3f, clk1_reg_old & 0x3f, + (clk2_reg_old >> 8) & 0x3, (clk2_reg_old >> 7) & 1, (clk2_reg_old >> 6) & 1, (clk2_reg_old & 0x3f), + (clk1_reg_out >> 13) & 0x7, (clk1_reg_out >> 6) & 0x3f, clk1_reg_out & 0x3f, + (clk2_reg_out >> 8) & 0x3, (clk2_reg_out >> 7) & 1, (clk2_reg_out >> 6) & 1, (clk2_reg_out & 0x3f) + ); return 0; } @@ -215,7 +217,8 @@ int mmcm_init_raw(lldev_t dev, subdev_t subdev, int res; unsigned clkfbdiv = cfg->ports[CLKOUT_PORT_FB].period_h + cfg->ports[CLKOUT_PORT_FB].period_l; - res = lowlevel_drp_wr16(dev, subdev, drp_port, PowerRegV7, 0xffff); + res = lowlevel_drp_wr16(dev, subdev, drp_port, + (cfg->type == MT_7SERIES_MMCM || cfg->type == MT_7SERIES_PLLE2) ? PowerRegV7 : PowerRegUS, 0xffff); if (res) { USDR_LOG("MMCM", USDR_LOG_ERROR, " unable to turn it on\n"); return res; @@ -224,8 +227,11 @@ int mmcm_init_raw(lldev_t dev, subdev_t subdev, for (unsigned i = 0; i < MAX_MMCM_PORTS; i++) { res = mmcm_init_raw_clkout(dev, subdev, drp_port, CLKOUT5_ClkReg1 + 2 * i, &cfg->ports[i]); - if (res) + if (res) { + USDR_LOG("MMCM", USDR_LOG_ERROR, "Port%d: H/L=%d/%d DLY=%d Error=%d\n", + i, cfg->ports[i].period_h, cfg->ports[i].period_l, cfg->ports[i].delay, res); return res; + } } // Input divide diff --git a/src/lib/ipblks/xlnx_mmcm.h b/src/lib/ipblks/xlnx_mmcm.h index 26b1451f..71e8363b 100644 --- a/src/lib/ipblks/xlnx_mmcm.h +++ b/src/lib/ipblks/xlnx_mmcm.h @@ -40,6 +40,9 @@ enum mmcm_vco_ranges { MMCM_VCO_MIN = 600000000, MMCM_VCO_MAX = 1440000000, MMCM_VCO_MAX_SP2 = 1900000000, + + MMCM_VCO_MIN_USP = 800000000, + MMCM_VCO_MAX_USP = 1600000000, }; struct mmcm_config_raw { diff --git a/src/lib/json_controller/controller.c b/src/lib/json_controller/controller.c index 4aa51806..342e467e 100644 --- a/src/lib/json_controller/controller.c +++ b/src/lib/json_controller/controller.c @@ -356,7 +356,7 @@ int generic_rpc_call(pdm_dev_t dmdev, uint64_t actual; res = set_endpoint_uint_param(dmdev, - (pcall->call_type == SDR_RX_FREQUENCY) ? "/dm/sdr/0/rx/freqency" : "/dm/sdr/0/tx/freqency", + (pcall->call_type == SDR_RX_FREQUENCY) ? "/dm/sdr/0/rx/frequency" : "/dm/sdr/0/tx/frequency", freq, &actual); if (res) @@ -387,6 +387,10 @@ int generic_rpc_call(pdm_dev_t dmdev, { int gain = (pcall->params.parameters_type[SDRC_GAIN] == SDRC_PARAM_TYPE_INT) ? pcall->params.parameters_uint[SDRC_GAIN] : 0; + if (sdrtype == SDR_LIME && pcall->call_type == SDR_TX_GAIN) { + //LimeSDR TX gain correction + gain = -32 - gain; + } uint64_t actual; res = set_endpoint_uint_param(dmdev, @@ -492,7 +496,8 @@ int generic_rpc_call(pdm_dev_t dmdev, if(mode & (i+1)) res = res ? res : usdr_dms_op(usds[i], stream_start ? USDR_DMS_START : USDR_DMS_STOP, 0); - res = res ? res : set_throttle(dmdev, throttleon, samplerate); + if (!(mode & 2)) + res = res ? res : set_throttle(dmdev, throttleon, samplerate); res = res ? res : usdr_dms_sync(dmdev, stypes, streams_num, streams); if(res) diff --git a/src/lib/lowlevel/CMakeLists.txt b/src/lib/lowlevel/CMakeLists.txt index 887cbd83..0fba5425 100644 --- a/src/lib/lowlevel/CMakeLists.txt +++ b/src/lib/lowlevel/CMakeLists.txt @@ -14,6 +14,8 @@ if(NOT EMSCRIPTEN) add_subdirectory(pcie_uram) endif() +include_directories(${LIBUSB_1_INCLUDE_DIRS}) + add_subdirectory(usb_uram) add_subdirectory(usb_ft601) diff --git a/src/lib/lowlevel/libusb_generic.c b/src/lib/lowlevel/libusb_generic.c index cb019d37..a992fbaa 100644 --- a/src/lib/lowlevel/libusb_generic.c +++ b/src/lib/lowlevel/libusb_generic.c @@ -48,11 +48,16 @@ unsigned find_usb_match(libusb_device **usbdev, size_t devices, struct matched_devs* md = &devs[k]; res = libusb_get_device_descriptor(usbdev[i], &desc); - if (res) + if (res) { + USDR_LOG("USBX", USDR_LOG_ERROR, "Unable to get device descriptor for device %d: %s\n", i, libusb_strerror(res)); continue; + } - j = libusb_find_dev_index_ex( busname,desc.idProduct, desc.idVendor, known_devices, known_device_count ); - if( j < 0 ) continue; + j = libusb_find_dev_index_ex(busname, desc.idProduct, desc.idVendor, known_devices, known_device_count ); + if (j < 0) { + USDR_LOG("USBX", USDR_LOG_DEBUG, "Skipping device %s: %04x:%04x, not in known devices list\n", busname, desc.idVendor, desc.idProduct); + continue; + } md->uuid_idx = j; md->sdrtype = libusb_get_dev_sdrtype(j); @@ -154,7 +159,7 @@ int libusb_generic_plugin_discovery(unsigned pcount, const char** filterparams, for (i = 0, off = 0; i < fcnt; i++) { int cap = maxbuf - off; - int l = snprintf(outarray + off, cap, "bus\t%s@%s\n", busname, md[i].devid_s); + int l = snprintf(outarray + off, cap, "bus=%s@%s\n", busname, md[i].devid_s); if (l < 0 || l > cap) { outarray[off] = 0; res = i; @@ -214,8 +219,8 @@ int libusb_generic_plugin_create(unsigned pcount, const char** devparam, fcnt = find_usb_match(usbdev, devices, &fparams, 1, &md , known_devs, known_devs_cnt, busname); if (fcnt == 0) { USDR_LOG("USBX", USDR_LOG_NOTE, - "No USB device was found to match %d/%d/%d\n", - fparams.usb_bus, fparams.usb_port, fparams.usb_addr); + "No USB device was found to match %d/%d/%d, total=%d\n", + fparams.usb_bus, fparams.usb_port, fparams.usb_addr, (unsigned)devices); libusb_exit(uctx); return -ENODEV; } @@ -310,7 +315,7 @@ void* libusb_generic_io_thread(void *arg) #if defined(__linux) || defined(__APPLE__) sigset_t set; - pthread_setname_np(pthread_self(), "usb_io"); + usdr_set_thread_name("usb_io"); sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, NULL); @@ -334,7 +339,7 @@ void* libusb_generic_io_thread(void *arg) // TODO: check res } - USDR_LOG("USBX", USDR_LOG_INFO, "IO thread termitaed with result %d", res); + USDR_LOG("USBX", USDR_LOG_INFO, "IO thread terminated with result %d", res); return (void*)((intptr_t)res); } @@ -374,12 +379,16 @@ int buffers_init(struct buffers* rb, unsigned max, unsigned zerosemval, bool has rb->buf_available = rb->buf_max = max; - if (sem_init(&rb->buf_ready, 0, zerosemval)) { + if (usdr_sem_init(&rb->buf_ready, 0, zerosemval)) { return -errno; } if (has_event) { +#ifndef __linux__ + rb->fd_event = -ENOTSUP; +#else rb->fd_event = fdevent_create(zerosemval); +#endif if (rb->fd_event < 0) { int err = -errno; USDR_LOG("USBX", USDR_LOG_ERROR, "Unable to create eventfd! err=%d\n", err); @@ -414,15 +423,17 @@ void buffers_deinit(struct buffers* rb) // TODO Add synchronization to get all outstanging endpoints usleep(10000); - sem_destroy(&rb->buf_ready); - free(rb->rqueuebuf_ptr); - free(rb->bd); + usdr_sem_destroy(&rb->buf_ready); + // usdr_alignfree(rb->rqueuebuf_ptr); + // free(rb->bd); +#ifdef __linux__ if (rb->fd_event >= 0) fdevent_destroy(rb->fd_event); +#endif rb->fd_event = -101; - rb->rqueuebuf_ptr = NULL; - rb->bd = NULL; + // rb->rqueuebuf_ptr = NULL; + // rb->bd = NULL; } int buffers_realloc(struct buffers* rb, unsigned allocsz) @@ -430,19 +441,27 @@ int buffers_realloc(struct buffers* rb, unsigned allocsz) int res; unsigned i; - free(rb->rqueuebuf_ptr); + usdr_alignfree(rb->rqueuebuf_ptr); free(rb->bd); rb->allocsz = allocsz; + rb->rqueuebuf_ptr = NULL; + rb->bd = NULL; + // Round up to maximum transfer in Bulk and reserve two more transfer in the case rb->allocsz_rounded = (allocsz + 4095) & (~4095u); rb->bd = (struct buffer_discriptor *)malloc(sizeof(struct buffer_discriptor) * (rb->buf_max + 1)); + if (rb->bd == NULL) + return -ENOMEM; - res = posix_memalign((void**)&rb->rqueuebuf_ptr, 4096, + res = usdr_alignalloc((void**)&rb->rqueuebuf_ptr, 4096, rb->allocsz_rounded * (rb->buf_max + 1)); - if (res != 0) + if (res != 0) { + free(rb->bd); + rb->bd = NULL; return -res; + } for (i = 0; i <= rb->buf_max; i++) { rb->bd[i].b = rb; @@ -450,8 +469,8 @@ int buffers_realloc(struct buffers* rb, unsigned allocsz) rb->bd[i].buffer_sz = 0; } - USDR_LOG("USBX", USDR_LOG_ERROR, "RX buffer configured to %d bytes for %d original\n", - rb->allocsz_rounded, allocsz); + USDR_LOG("USBX", USDR_LOG_INFO, "RX buffer configured to %d x %d bytes for %d original\n", + rb->allocsz_rounded, rb->buf_max, allocsz); rb->bufno_prod = 0; rb->bufno_cons = 0; @@ -463,11 +482,13 @@ void buffers_reset(struct buffers* rb) rb->bufno_cons = 0; rb->bufno_prod = 0; rb->buf_available = rb->buf_max; - sem_destroy(&rb->buf_ready); - sem_init(&rb->buf_ready, 0, 0); + usdr_sem_destroy(&rb->buf_ready); + usdr_sem_init(&rb->buf_ready, 0, 0); +#ifdef __linux__ if (rb->fd_event > 0) fdevent_get(rb->fd_event, NULL); +#endif } // Ready buffer to or from IO @@ -486,9 +507,13 @@ int buffers_ready_wait(struct buffers *rxb, int64_t timeout_us) { int res; if (rxb->fd_event >= 0) { +#ifdef __linux__ res = fdevent_get(rxb->fd_event, NULL); +#else + res = -ENOTSUP; +#endif } else { - res = sem_wait_ex(&rxb->buf_ready, timeout_us * 1000); + res = usdr_sem_wait_ex(&rxb->buf_ready, timeout_us * 1000); } return res; } @@ -497,9 +522,13 @@ int buffers_ready_post(struct buffers *rxb) { int res; if (rxb->fd_event >= 0) { +#ifdef __linux__ res = fdevent_post(rxb->fd_event, 1); +#else + res = -ENOTSUP; +#endif } else { - res = sem_post(&rxb->buf_ready); + res = usdr_sem_post(&rxb->buf_ready); } return res; } @@ -537,6 +566,10 @@ int buffers_usb_transfer_post(struct buffers *prxb, unsigned buffer_idx, unsigne unsigned transfer_idx) { int res; + + assert(buffer_idx <= prxb->buf_max); + assert(transfer_idx < prxb->transfers_count); + prxb->transfers[transfer_idx]->buffer = prxb->rqueuebuf_ptr + buffer_idx * prxb->allocsz_rounded; prxb->transfers[transfer_idx]->length = length; //prxb->allocsz_rounded; prxb->transfers[transfer_idx]->user_data = &prxb->bd[buffer_idx]; @@ -567,12 +600,13 @@ void LIBUSB_CALL libusb_transfer_buffers_cb(struct libusb_transfer *transfer) break; } assert(idx < rxb->transfers_count); + assert(rxbd->bno <= rxb->buf_max); USDR_LOG("USBX", USDR_LOG_DEBUG, "%s_STRM[%d] transfer %d => %d / %d\n", tr_type, idx, transfer->status, transfer->actual_length, transfer->length); if (rxb->stop) { - // TODO: Syncronize EPs + // TODO: Synchronize EPs return; } @@ -628,6 +662,10 @@ int buffers_usb_init(libusb_generic_dev_t* gdev, struct buffers *prxb, max_reqs = BUFFERS_MAX_TRANS; } + if (max_reqs > max_buffs) { + max_reqs = max_buffs; + } + res = res ? res : buffers_init(prxb, max_buffs, usb_in ? 0 : max_buffs, eventfd_ntfy); res = res ? res : buffers_realloc(prxb, max_blocksize); res = res ? res : libusb_generic_prepare_transfer(gdev, NULL, endpoint, @@ -661,7 +699,7 @@ int buffers_usb_free(struct buffers *prxb) prxb->transfers[j] = NULL; } - free(prxb->rqueuebuf_ptr); + usdr_alignfree(prxb->rqueuebuf_ptr); prxb->rqueuebuf_ptr = NULL; free(prxb->bd); @@ -673,7 +711,7 @@ int buffers_usb_free(struct buffers *prxb) // Helpers -int sem_wait_ex(sem_t *s, int64_t timeout_ns) +int usdr_sem_wait_ex(usdr_sem_t *s, int64_t timeout_ns) { int res; if (timeout_ns > 0) { @@ -687,17 +725,17 @@ int sem_wait_ex(sem_t *s, int64_t timeout_ns) ts.tv_sec += secs; timeout_ns -= (int64_t)1000 * 1000 * 1000 * secs; - ts.tv_nsec += timeout_ns * 1000; + ts.tv_nsec += timeout_ns; while (ts.tv_nsec > 1000 * 1000 * 1000) { ts.tv_nsec -= 1000 * 1000 * 1000; ts.tv_sec++; } - res = sem_timedwait(s, &ts); + res = usdr_sem_timedwait(s, &ts); } else if (timeout_ns < 0) { - res = sem_wait(s); + res = usdr_sem_wait(s); } else { - res = sem_trywait(s); + res = usdr_sem_trywait(s); } if (res) { // sem_* function on error returns -1, get proper error diff --git a/src/lib/lowlevel/libusb_generic.h b/src/lib/lowlevel/libusb_generic.h index 093410ce..ffc6031e 100644 --- a/src/lib/lowlevel/libusb_generic.h +++ b/src/lib/lowlevel/libusb_generic.h @@ -4,11 +4,11 @@ #ifndef _LIBUSB_GENERIC_H #define _LIBUSB_GENERIC_H +#include #include #include -#include +#include #include -#include #include #include @@ -146,7 +146,7 @@ int libusb_generic_stop_thread(libusb_generic_dev_t *dev); // Return -errno if fails -int sem_wait_ex(sem_t *s, int64_t timeout_ns); +int usdr_sem_wait_ex(usdr_sem_t *s, int64_t timeout_ns); // Buffers @@ -163,7 +163,7 @@ struct buffer_discriptor struct buffers { - sem_t buf_ready; + usdr_sem_t buf_ready; uint8_t* rqueuebuf_ptr; // cache aligned pointer to rx_queuebuf diff --git a/src/lib/lowlevel/pcie_uram/driver/Makefile b/src/lib/lowlevel/pcie_uram/driver/Makefile index 68f77e99..8204e9d3 100644 --- a/src/lib/lowlevel/pcie_uram/driver/Makefile +++ b/src/lib/lowlevel/pcie_uram/driver/Makefile @@ -1,11 +1,14 @@ ifneq ($(KERNELRELEASE),) +ccflags-y += $(shell grep -E 'vm_flags_set\s*.*\s*struct vm_area_struct' $(srctree)/include/linux/mm.h >/dev/null && echo -DHAVE_VM_FLAGS_SET) +ccflags-y += $(shell grep -E 'class_create\s*.*\s*const char' $(srctree)/include/linux/device/class.h >/dev/null && echo -DHAVE_CLASS_CREATE_ONE_ARG) obj-m += usdr_pcie_uram.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build + modules modules_install clean:: make -C $(KERNELDIR) M=$(PWD) $@ @@ -13,4 +16,3 @@ clean:: rm -f *.o Module.markers modules.order endif - diff --git a/src/lib/lowlevel/pcie_uram/driver/usdr_pcie_uram.c b/src/lib/lowlevel/pcie_uram/driver/usdr_pcie_uram.c index 05a838bf..e64b2351 100644 --- a/src/lib/lowlevel/pcie_uram/driver/usdr_pcie_uram.c +++ b/src/lib/lowlevel/pcie_uram/driver/usdr_pcie_uram.c @@ -21,11 +21,17 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include + #include "./pcie_uram_driver_if.h" #include "./si2c.c" @@ -38,6 +44,19 @@ #define DEVICE_NAME DRV_NAME #define CLASS_NAME DRV_NAME +#define XMASS_NAME "xmass" +#define XPFX XMASS_NAME ": " +#define XMASS_DEVICE_NAME XMASS_NAME +#define XMASS_CLASS_NAME XMASS_NAME + +// Maximum device supported for usdr and xmass in the system +#define TOT_USDR_DEVS 32 +typedef DECLARE_BITMAP(usdr_dev_bituse_t, TOT_USDR_DEVS); + +#define TOT_XMASS_DEVS 8 +typedef DECLARE_BITMAP(xmass_dev_bituse_t, TOT_XMASS_DEVS); + + MODULE_AUTHOR("Sergey Kostanbaev "); MODULE_DESCRIPTION("USDR PCIe UnifiedRAM driver"); MODULE_LICENSE("GPL"); @@ -65,6 +84,24 @@ static int pci_irq_vector(struct pci_dev *dev, unsigned int nr) } #endif + +// vm_flags_set introduced in 6.3.0, however enterprise-like kernels heavily backport new API to +// old base kernel version, notably RHEL, resulting in newer API in 5.x.x. So version like checks +// will fail here +#ifndef HAVE_VM_FLAGS_SET +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) +#define HAVE_VM_FLAGS_SET 1 +#endif +#endif + + +#ifndef HAVE_CLASS_CREATE_ONE_ARG +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) +#define HAVE_CLASS_CREATE_ONE_ARG 1 +#endif +#endif + + // Change anytime when extra parameter or meaning is changed in pcie_uram_driver_if.h #define USDR_DRIVER_ABI_VERSION 3 @@ -140,13 +177,34 @@ struct usdr_dev; typedef void (*bucket_func_t)(struct usdr_dev* dev, unsigned event, void* slot); +enum { + STAT_MAX_SZ = 64, +}; struct event_data_log { - uint32_t stat_data[4 * 64]; + uint32_t stat_data[4 * STAT_MAX_SZ]; uint64_t stat_wptr; uint64_t stat_rptr; }; +#define MAX_XMASS_DEVS 4 +struct xmass_dev { + struct xmass_dev *next; + unsigned long device_data; + struct cdev cdev; + struct device* cdevice; + + spinlock_t slock; + + unsigned asm_bus_number; + unsigned devno; + unsigned dev_mask; + + struct pci_dev* pasmdev; // Valid when device is locked + + struct usdr_dev* dev[MAX_XMASS_DEVS]; // 0 index means device A, 1 - B, ... +}; + struct usdr_dev { struct usdr_dev *next; unsigned long device_data; @@ -155,39 +213,47 @@ struct usdr_dev { struct pci_dev* pdev; spinlock_t slock; - + void __iomem *bar_addr; - + unsigned devno; - unsigned dev_mask; + unsigned dev_mask; struct pcie_driver_devlayout dl; - + int irq_configured; // Number of IRQ configured irq_func_t irq_funcs[MAX_INT]; atomic_t irq_ev_cnt[MAX_INT]; uint32_t rb_ev_data[MAX_INT]; bucket_func_t bucket_func[MAX_INT]; - + wait_queue_head_t irq_ev_wq[MAX_INT]; char int_names[INTNAMES_MAX * MAX_INT]; struct stream_state* streams[MAX_STREAMS]; - + struct notification_bucket buckets[MAX_BUCKETS]; - + unsigned vma_off_last; - + struct event_data_log streaming[MAX_INT]; struct i2c_cache i2cc[4 * MAX_I2C_COUNT]; uint32_t i2clut[MAX_I2C_COUNT]; }; +// USDR static struct usdr_dev *usdr_list = NULL; -static int devices = 0; -static dev_t dev_first; -static struct class* usdr_class = NULL; +static struct class* usdr_class = NULL; +static dev_t usdr_dev_first; +static usdr_dev_bituse_t usdr_busy_map; + +// XMASS +static struct xmass_dev *xmass_list = NULL; +static struct class* xmass_class = NULL; +static dev_t xmass_dev_first; +static xmass_dev_bituse_t xmass_busy_map; + // #define EXTRA_DEBUG @@ -364,7 +430,7 @@ static int usdr_allocdma(struct usdr_dev *d, struct usdr_dmabuf *pbufs, unsigned } return -1; } - + printk(KERN_NOTICE PFX "buf[%d]=%lx [virt %p]\n", i, (unsigned long)pbufs[i].phys, pbufs[i].kvirt); } return 0; @@ -384,7 +450,7 @@ static int init_bucket(struct usdr_dev *dev) unsigned i; for (i = 0; i < dev->dl.bucket_count; i++) { struct notification_bucket* b = &dev->buckets[i]; - + b->db.kvirt = dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE, &b->db.phys, GFP_KERNEL); if(!b->db.kvirt) @@ -395,10 +461,10 @@ static int init_bucket(struct usdr_dev *dev) b->db.uvirt = 0; b->rptr = 0; - + // Initialize to FF so we can see 1->0 toggle on valid entries memset(b->db.kvirt, -1, PAGE_SIZE); - + // Writing DMA address resets counter and sets toggle bit to 0 usdr_reg_wr32(dev, dev->dl.bucket_base, // CFG @@ -407,7 +473,7 @@ static int init_bucket(struct usdr_dev *dev) dev->dl.bucket_base + 1, //ACK i << 16); dev_notice(&dev->pdev->dev, "Bucket %d: DMA at %px to %llx\n", i, b->db.kvirt, b->db.phys); - + } return 0; } @@ -417,7 +483,7 @@ static void deinit_bucket(struct usdr_dev *dev) unsigned i; for (i = 0; i < dev->dl.bucket_count; i++) { struct notification_bucket* b = &dev->buckets[i]; - + //TODO block interrupt queue if(b->db.kvirt) dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, b->db.kvirt, b->db.phys); @@ -433,50 +499,50 @@ static irqreturn_t usdr_pcie_irq_bucket_128(int irq, void *data) struct notification_bucket* b; uint64_t wakeups = 0; ktime_t ets; - + for (bidx = 0; bidx < d->dl.bucket_count; bidx++) { b = &d->buckets[bidx]; if (b->irq == irq) goto irq_found; } - + dev_notice(&d->pdev->dev, "IRQ %d: Unknown bucket!\n", irq); return IRQ_HANDLED; - + irq_found: bptr = b->db.kvirt; ets = ktime_get(); for (j = 0, i = b->rptr; i < b->rptr + 256; i++, j++) { uint32_t data[4]; unsigned event_no, flags, l; - + for (l = 0; l < 4; l++) data[l] = /*be32_to_cpu*/(bptr[(4 * i + l) & 0x3ff]); - + //event_no = data[3] >> 29; //flags = (data[3] & (1u<<28)) ? 1 : 0; flags = data[0] >> 31; event_no = data[0] & 0x3f; - + if (flags != ((i >> 8) & 1)) { break; } if (i % 32 == 31) { do_cnf = (i << 1) & 0x3ff; } - + DEBUG_DEV_OUT(&d->pdev->dev, "BUCKET %d IRQ %d: Event %d Flag: %d; RPTR %d; Data: %08x_%08x_%08x_%08x\n", i, irq, event_no, flags, b->rptr, data[3], data[2], data[1], data[0]); - - + + // TODO: based on event handler process data if (event_no == 0 || event_no == 1) { - unsigned k = (d->streaming[event_no].stat_wptr * 4) & 0x3f; - d->streaming[event_no].stat_data[k + 0] = data[1]; // 0 - d->streaming[event_no].stat_data[k + 1] = data[2]; // 1 - d->streaming[event_no].stat_data[k + 2] = data[3]; // 2 - d->streaming[event_no].stat_data[k + 3] = + unsigned k = (d->streaming[event_no].stat_wptr) & (STAT_MAX_SZ - 1); + d->streaming[event_no].stat_data[4 * k + 0] = data[1]; // 0 + d->streaming[event_no].stat_data[4 * k + 1] = data[2]; // 1 + d->streaming[event_no].stat_data[4 * k + 2] = data[3]; // 2 + d->streaming[event_no].stat_data[4 * k + 3] = #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) ets.tv64; // Timestamp #else @@ -484,7 +550,7 @@ static irqreturn_t usdr_pcie_irq_bucket_128(int irq, void *data) #endif d->streaming[event_no].stat_wptr++; - + //dev_notice(&d->pdev->dev, "BUCKET %d IRQ %d: Event %d Flag: %d; RPTR %d; Data: %08x_%08x_%08x_%08x %016llx\n", // i, irq, event_no, flags, b->rptr, data[3], data[2], data[1], data[0], ets); } else { @@ -494,21 +560,21 @@ static irqreturn_t usdr_pcie_irq_bucket_128(int irq, void *data) //wake_up_interruptible(&d->irq_ev_wq[event_no]); wakeups |= (1u << event_no); } - + if (j > 1) { DEBUG_DEV_OUT(&d->pdev->dev, "BUCKET %d IRQ %d: rptr = %d int suppressed %d times\n", i, irq, b->rptr, j); } - + for (j = 0; j < MAX_INT; j++) { if (wakeups & (1u << j)) { wake_up_interruptible(&d->irq_ev_wq[j]); } } - + b->rptr = i & 0x1ff; - + //dev_notice(&d->pdev->dev, "BUCKET %d IRQ %d: rptr = %d\n", i, irq, b->rptr); - + if (do_cnf != ~0u) { //Send confirmation pointer usdr_reg_wr32(d, @@ -538,6 +604,16 @@ static int usdrfd_open(struct inode *inode, struct file *filp) } spin_unlock_irqrestore(&dev->slock, flags); + if (granted && dev->pdev->error_state != pci_channel_io_normal) { + dev_err(&dev->pdev->dev, "PCI device /dev/usdr%d is disconnected or in error state\n", dev->devno); + + spin_lock_irqsave(&dev->slock, flags); + dev->dev_mask &= ~DEV_EXCLUSIVE; + spin_unlock_irqrestore(&dev->slock, flags); + + return -EIO; + } + return (granted) ? 0 : -EBUSY; } @@ -554,7 +630,7 @@ static int usdrfd_release(struct inode *inode, struct file *filp) spin_lock_irqsave(&usdrdev->slock, flags); usdrdev->dev_mask &= ~DEV_EXCLUSIVE; spin_unlock_irqrestore(&usdrdev->slock, flags); - + return 0; } @@ -609,8 +685,13 @@ static int usdr_stream_free(struct usdr_dev *usdrdev, unsigned sno) return 0; // Release DMA buffers - // Check that mapping is invalid + if (usdrdev->dl.stream_core[sno] == USDR_MAKE_COREID(USDR_CS_STREAM, USDR_SC_TXDMA_OLD)) { + usdr_reg_wr32(usdrdev, usdrdev->dl.stream_cfg_base[sno] + s->dma_buffs, 0); + } else { + usdr_reg_wr32(usdrdev, usdrdev->dl.stream_cfg_base[sno] + 7 * s->dma_buffs, 0); + } + // Destroy buffers for (i = s->dma_buffs; i > 0; i--) { dma_free_attrs(&usdrdev->pdev->dev, s->dma_buff_size, @@ -790,15 +871,15 @@ static int usdr_device_initialie(struct usdr_dev *usdrdev) usdrdev->dl.idx_regsp_cnt, usdrdev->dl.interrupt_count, c, imsk); #else - + dev_notice(&usdrdev->pdev->dev, "Device initialized, spi buses %d, i2c buses %d, indexed %d, bucket mode\n", usdrdev->dl.spi_cnt, usdrdev->dl.i2c_cnt, usdrdev->dl.idx_regsp_cnt); - + //init_bucket(usdrdev); - + //Initialize interrupt routines #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,11,0) res = pci_enable_msi_range(usdrdev->pdev, 1, 1); @@ -820,14 +901,14 @@ static int usdr_device_initialie(struct usdr_dev *usdrdev) goto failed_cfg_mux; } usdrdev->buckets[0].irq = pci_irq_vector(usdrdev->pdev, irq); - + //Configure all IRQs to report to bucket 0 via interrupt 0 for (i = 0; i < 32; i++) { usdr_reg_wr32(usdrdev, usdrdev->dl.interrupt_base, i | (0 << 8) | (1 << 16) | (0 << 20)); usdrdev->irq_funcs[i] = usdr_pcie_irq_event; } usdrdev->irq_configured = 1; - + #endif return 0; @@ -910,12 +991,12 @@ static int usdr_stream_initialize(struct usdr_dev *usdrdev, goto failed_alloc; } s->dmab[i].uvirt = NULL; - //dev_notice(&usdrdev->pdev->dev, "buf[%d]=%lx [virt %p]\n", i, - // (unsigned long)s->dmab[i].phys, s->dmab[i].kvirt); + //dev_notice(&usdrdev->pdev->dev, "buf[%d]=%lx len=%d [virt %px]\n", i, + // (unsigned long)s->dmab[i].phys, s->dma_buff_size, s->dmab[i].kvirt); } // Initialize dma buffer pointer in the dev - if (usdrdev->dl.stream_core[sno] == USDR_MAKE_COREID(USDR_CS_STREAM, USDR_SC_RXDMA_BRSTN) || + if (usdrdev->dl.stream_core[sno] == USDR_MAKE_COREID(USDR_CS_STREAM, USDR_SC_RXDMA_BRSTN) || usdrdev->dl.stream_core[sno] == USDR_MAKE_COREID(USDR_CS_STREAM, USDR_SC_TXDMA_OLD) ) { for (i = 0; i < sdma->dma_bufs; i++) { usdr_reg_wr32(usdrdev, @@ -928,14 +1009,14 @@ static int usdr_stream_initialize(struct usdr_dev *usdrdev, usdrdev->dl.stream_core[sno]); goto failed_alloc; } - + usdrdev->streams[sno] = s; exit_success: //s->cntr_last = 0; sdma->out_vma_length = s->dma_buff_size * s->dma_buffs; sdma->out_vma_off = ((off_t)(sdma->sno + 1)) << VMA_STREAM_IDX_SHIFT; - + // Flush non-read events usdrdev->streaming[sno].stat_rptr = usdrdev->streaming[sno].stat_wptr; usdrdev->streams[sno]->abuffer_no = 0; @@ -953,6 +1034,11 @@ static int usdr_stream_initialize(struct usdr_dev *usdrdev, } else { // Clear spurious interrupts atomic_xchg(&usdrdev->irq_ev_cnt[usdrdev->dl.stream_int_number[sno]], 0); + + usdr_reg_wr32(usdrdev, + usdrdev->dl.stream_cfg_base[sno] + 7 * sdma->dma_bufs, + sdma->dma_buf_sz - 1); + dev_notice(&usdrdev->pdev->dev, "RX stream is limited to %d bytes\n", sdma->dma_buf_sz); } return 0; @@ -1031,23 +1117,23 @@ static int usdr_stream_wait_or_alloc(struct usdr_dev *usdrdev, unsigned long sno BUG_ON(cnt == 0); } } - + max = usdrdev->streaming[sno].stat_wptr; if (max > usdrdev->streaming[sno].stat_rptr + cnt) { - max = usdrdev->streaming[sno].stat_rptr + cnt; + max = usdrdev->streaming[sno].stat_rptr + cnt; } - + for (i = usdrdev->streaming[sno].stat_rptr; i < max; i++, ooidx++) { unsigned k, nreg[3], ktm; - k = (usdrdev->streaming[sno].stat_rptr * 4) & 0x3f; - nreg[0] = usdrdev->streaming[sno].stat_data[k + 0]; - nreg[1] = usdrdev->streaming[sno].stat_data[k + 1]; - nreg[2] = usdrdev->streaming[sno].stat_data[k + 2]; - ktm = usdrdev->streaming[sno].stat_data[k + 3]; - + k = (usdrdev->streaming[sno].stat_rptr) & (STAT_MAX_SZ - 1); + nreg[0] = usdrdev->streaming[sno].stat_data[4 * k + 0]; + nreg[1] = usdrdev->streaming[sno].stat_data[4 * k + 1]; + nreg[2] = usdrdev->streaming[sno].stat_data[4 * k + 2]; + ktm = usdrdev->streaming[sno].stat_data[4 * k + 3]; + //sr = ((uint64_t)nreg[1] << 32) | nreg[0]; usdrdev->streaming[sno].stat_rptr++; - + //cntr_last = (stat >> 8) & 0x3f; //dcnt = (cntr_last - usdrdev->streams[sno]->cntr_last) & 0x3f; //BUG_ON(dcnt == 0); @@ -1061,14 +1147,14 @@ static int usdr_stream_wait_or_alloc(struct usdr_dev *usdrdev, unsigned long sno oob_out_u64[2 * ooidx + 0] = (((uint64_t)nreg[1]) << 32) | nreg[0]; oob_out_u64[2 * ooidx + 1] = (((uint64_t)ktm) << 32) | nreg[2]; oobcnt++; - + // dw0 - evnt[1] - reg[0] // dw1 - evnt[2] - reg[1] // dw2 - evnt[0] - reg[2] // dw3 - timestamp } } - + if (oob_length) { *oob_length = oobcnt * 16; } @@ -1158,7 +1244,8 @@ static long usdrfd_ioctl(struct file *filp, if (ioctl_num != PCIE_DRIVER_GET_UUID && ioctl_num != PCIE_DRIVER_CLAIM && ioctl_num != PCIE_DRIVER_SET_DEVLAYOUT && - ioctl_num != PCIE_DRIVER_CLAIM_VERSION) { + ioctl_num != PCIE_DRIVER_CLAIM_VERSION && + ioctl_num != PCIE_DRIVER_HWREG_RD32) { dev_notice(&usdrdev->pdev->dev, "Device not ready!"); return -EINVAL; @@ -1461,7 +1548,7 @@ static int usdrfd_mmap_io(struct usdr_dev *usdrdev, struct vm_area_struct *vma) return -EINVAL; vma->vm_page_prot = pgprot_device(vma->vm_page_prot); -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) +#ifndef HAVE_VM_FLAGS_SET vma->vm_flags |= VM_IO; #else vm_flags_set(vma, VM_IO); @@ -1555,7 +1642,7 @@ struct file_operations usdr_fops = { static int usdr_setup_cdev(struct usdr_dev *usdrdev) { - dev_t dev_num = dev_first + usdrdev->devno; + dev_t dev_num = usdr_dev_first + usdrdev->devno; cdev_init(&usdrdev->cdev, &usdr_fops); usdrdev->cdev.owner = THIS_MODULE; @@ -1573,10 +1660,15 @@ static int usdr_probe(struct pci_dev *pdev, struct usdr_dev* usdrdev; int err; void __iomem* bar_addr; - unsigned usdr_no = devices; + //unsigned usdr_no = devices; + unsigned usdr_no = find_first_zero_bit(usdr_busy_map, TOT_USDR_DEVS); size_t bar_len; - printk(KERN_INFO PFX "Initializing %s\n", pci_name(pdev)); + struct pci_dev *bridge, *tbridge; + bridge = pci_upstream_bridge(pdev); + tbridge = (bridge) ? pci_upstream_bridge(bridge) : NULL; + + printk(KERN_INFO PFX "Initializing %s => %s => %s\n", pci_name(pdev), bridge ? pci_name(bridge) : "null", tbridge ? pci_name(tbridge) : "null"); err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "Cannot enable PCI device, " @@ -1586,16 +1678,24 @@ static int usdr_probe(struct pci_dev *pdev, pci_set_master(pdev); + if (tbridge) { + printk(KERN_INFO PFX "Initialize ASM2806 bridge: %px\n", tbridge); + // pci_write_config_byte(tbridge, 0xfff, 1); // Switch to GPIO control mode + // pci_write_config_byte(tbridge, 0x920, 0x01); + // pci_write_config_byte(tbridge, 0x928, 0x00); + } + /* Reconfigure MaxReadReq to 4KB */ - pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_READRQ, PCI_EXP_DEVCTL_READRQ_2048B); + //pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL, + // PCI_EXP_DEVCTL_READRQ, PCI_EXP_DEVCTL_READRQ_4096B); - //dma_set_mask_and_coherent + //dma_set_mask_and_coherent if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) { dev_err(&pdev->dev,"No suitable consistent DMA available.\n"); + err = -EINVAL; goto err_disable_pdev; } - + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { dev_err(&pdev->dev,"No suitable consistent DMA available.\n"); goto err_disable_pdev; @@ -1633,48 +1733,49 @@ static int usdr_probe(struct pci_dev *pdev, usdrdev->devno = usdr_no; usdrdev->pdev = pdev; usdrdev->dev_mask = 0; - + spin_lock_init(&usdrdev->slock); usdrdev->dev_mask = DEV_VALID; usdrdev->cdevice = device_create(usdr_class, &pdev->dev, - MKDEV(MAJOR(dev_first), MINOR(dev_first) + devices), + MKDEV(MAJOR(usdr_dev_first), MINOR(usdr_dev_first) + usdrdev->devno), NULL, DEVICE_NAME "%d", - devices); + usdrdev->devno); if (IS_ERR(usdrdev->cdevice)) { printk(KERN_NOTICE PFX "Unable to register device class\n"); + err = -EINVAL; goto failed_device; } - err = usdr_setup_cdev(usdrdev); + err = usdr_setup_cdev(usdrdev); if (err) { printk(KERN_NOTICE PFX "Error %d initializing cdev\n", err); goto failed_cdev; } usdrdev->device_data = id->driver_data; - - usdrdev->dl.bucket_count = 1; - usdrdev->dl.bucket_base = 8; - err = init_bucket(usdrdev); - if (err) - { - printk(KERN_NOTICE PFX "Error %d initializing bucket\n", err); - deinit_bucket(usdrdev); - goto failed_cdev; - } + usdrdev->dl.bucket_count = 1; + usdrdev->dl.bucket_base = 8; + err = init_bucket(usdrdev); + if (err) { + printk(KERN_NOTICE PFX "Error %d initializing bucket\n", err); + deinit_bucket(usdrdev); + goto failed_cdev; + } + + dev_info(&pdev->dev, "is linked to /dev/usdr%d\n", usdrdev->devno); + set_bit(usdrdev->devno, usdr_busy_map); - devices++; - usdrdev->next = usdr_list; - usdr_list = usdrdev; + usdrdev->next = usdr_list; + usdr_list = usdrdev; return 0; //cdev_del(&usdrdev->cdev); failed_cdev: - device_destroy(usdr_class, MKDEV(MAJOR(dev_first), MINOR(dev_first) + devices)); + device_destroy(usdr_class, MKDEV(MAJOR(usdr_dev_first), MINOR(usdr_dev_first) + usdrdev->devno)); failed_device: kfree(usdrdev); //err_allocdma: @@ -1689,8 +1790,7 @@ static int usdr_probe(struct pci_dev *pdev, pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); return err; -}; - +} static void usdr_remove(struct pci_dev *pdev) { @@ -1699,7 +1799,7 @@ static void usdr_remove(struct pci_dev *pdev) printk(KERN_INFO PFX "Removing device %s\n", pci_name(pdev)); if (usdrdev->dev_mask & DEV_INITIALIZED) { - + // Disable notification of all events for (i = 0; i < 32; i++) { // Dispatch ID == 0xf means to ignore this event @@ -1714,7 +1814,7 @@ static void usdr_remove(struct pci_dev *pdev) } pci_disable_msi(pdev); - + #ifndef OLD_IRQ // Remove bucket memory deinit_bucket(usdrdev); @@ -1726,8 +1826,8 @@ static void usdr_remove(struct pci_dev *pdev) } cdev_del(&usdrdev->cdev); - device_destroy(usdr_class, MKDEV(MAJOR(dev_first), MINOR(usdrdev->devno))); - + device_destroy(usdr_class, MKDEV(MAJOR(usdr_dev_first), MINOR(usdr_dev_first) + usdrdev->devno)); + usdrdev->dev_mask = 0; pci_iounmap(pdev, usdrdev->bar_addr); @@ -1736,14 +1836,18 @@ static void usdr_remove(struct pci_dev *pdev) pci_clear_master(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); - - //usdr_freedma(usdrdev, usdrdev->rxdma, usdrdev->rxdma_bufsize); - devices--; + //usdr_freedma(usdrdev, usdrdev->rxdma, usdrdev->rxdma_bufsize); + clear_bit(usdrdev->devno, usdr_busy_map); + + dev_info(&pdev->dev, "is unlinked, /dev/usdr%d removed\n", usdrdev->devno); + + //devices--; // TODO: Unchain from list and free the memory } + static struct pci_device_id usdr_pci_table[] = { { PCI_DEVICE(0x10EE, 0x7032), .driver_data = 0 }, @@ -1757,6 +1861,8 @@ static struct pci_device_id usdr_pci_table[] = { .driver_data = 3 }, { PCI_DEVICE(0x10EE, 0x7049), .driver_data = 4 }, + { PCI_DEVICE(0x10EE, 0x9049), + .driver_data = 4 }, { PCI_DEVICE(0x10EE, 0x9034), .driver_data = 5 }, { PCI_DEVICE(0x10EE, 0x9044), @@ -1766,63 +1872,997 @@ static struct pci_device_id usdr_pci_table[] = { { 0, } }; -MODULE_DEVICE_TABLE(pci, usdr_pci_table); - -static struct pci_driver usdr_driver = { - .name = DRV_NAME, - .id_table = usdr_pci_table, - .probe = usdr_probe, - .remove = usdr_remove -}; +//static struct pci_device_id xmass_pci_table[] = { +// { PCI_DEVICE(0x1B21, 0x2806), .driver_data = 0 }, +// { 0, } +//}; -static int __init usdr_init(void) +static int xmassfd_open(struct inode *inode, struct file *filp) { - int err; - err = alloc_chrdev_region(&dev_first, 0, 32, DRV_NAME); - if (err) { - printk(KERN_NOTICE PFX "Unable to allocate chrdev region: %d\n", err); - goto failed_chrdev; - } -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) - usdr_class = class_create(THIS_MODULE, CLASS_NAME); -#else - usdr_class = class_create(CLASS_NAME); -#endif - if (IS_ERR(usdr_class)) { - printk(KERN_NOTICE PFX "Unable to register usdr class\n"); - goto failed_setup_cdev; + struct xmass_dev *dev; + unsigned long flags; + int granted = 0; + + dev = container_of(inode->i_cdev, struct xmass_dev, cdev); + filp->private_data = dev; + + spin_lock_irqsave(&dev->slock, flags); + if ((dev->dev_mask & DEV_EXCLUSIVE) == 0) { + dev->dev_mask |= DEV_EXCLUSIVE; + granted = 1; } + spin_unlock_irqrestore(&dev->slock, flags); - err = pci_register_driver(&usdr_driver); - if (err) { - printk(KERN_NOTICE PFX "Unable to register PCI driver: %d\n", err); - goto failed_pci; + return (granted) ? 0 : -EBUSY; +} + +static int xmassfd_release(struct inode *inode, struct file *filp) +{ + struct xmass_dev *dev = filp->private_data; + unsigned long flags; + + if ((dev->dev_mask & DEV_VALID) == 0) { + printk(KERN_INFO XPFX "xmass:%d dev is invalid!\n", dev->devno); + return 0; } + + spin_lock_irqsave(&dev->slock, flags); + dev->dev_mask &= ~DEV_EXCLUSIVE; + spin_unlock_irqrestore(&dev->slock, flags); + return 0; +} -failed_pci: - class_destroy(usdr_class); -failed_setup_cdev: - unregister_chrdev_region(dev_first, devices); -failed_chrdev: - return err; +static ssize_t xmassfd_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + return -EINVAL; } -static void __exit usdr_cleanup(void) +static ssize_t xmassfd_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) { - struct usdr_dev *ptr = usdr_list, *next; + return -EINVAL; +} - pci_unregister_driver(&usdr_driver); - class_destroy(usdr_class); +#define MAX_PROC_BLK 128*4096 - unregister_chrdev_region(dev_first, devices); +#define SPI_BUSY_BIT 0x20 +#define SPI_TERM_BIT 0x10 +#define SPI_GO_BITS 0x28 /* GO + WRITE */ - while (ptr != NULL) { - next = ptr->next; - kfree(ptr); - ptr = next; +#define SPI_REQ_BIT 0x01 /* request/grant ownership */ +#define SPI_GNT_BIT 0x02 /* grant status */ + +enum { + ASM28XX_REG_SWITCH = 0xfff, // 0 - USP control registers, 1 - System control registers + ASM28XX_GPIO0_CTRL = 0x920, // 0 - input; 1 - output + ASM28XX_GPIO0_OUT = 0x928, + ASM28XX_GPIO0_IN = 0x930, + + ASM28XX_SPI_DATA_REG = 0x700, + ASM28XX_SPI_CTRL_REG = 0x704, + ASM28XX_SPI_3WIRE_REG = 0x705, + + ASM28XX_SPI_MISC_REG = 0x711, + ASM28XX_SPI_GRANT_REG = 0x720, + + ASM28XX_FWID_0 = 0xf0, +}; + +enum { + ESPI_CMD_QCFR_0 = 0x0B, + ESPI_CMD_WREN = 0x06, + + ESPI_CMD_QCPP_0 = 0x02, + ESPI_CMD_SSE = 0x20, + ESPI_CMD_SE = 0xD8, + ESPI_CMD_BE = 0xC7, + ESPI_CMD_RDSR = 0x05, + + ESPI_CMD_RDID = 0xAB, + ESPI_CMD_RDID_0 = 0x9E, + ESPI_CMD_RDID_1 = 0x9F, +}; + +int asm28xx_spi_idle(struct xmass_dev *d) +{ + uint32_t iter; + uint8_t status; + int res; + + for (iter = 0; ; iter++) { + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, &status); + if (res) + return res; + + /* wait until BUSY clears */ + if ((status & SPI_BUSY_BIT) == 0) + return 0; + + /* timeout: ~1,000,000 clock ticks */ + if (iter > 0xF423F) { + printk(KERN_INFO XPFX "xmass:%d SPI idle timeout! SPI status = 0x%02x\n", d->devno, status); + return -ETIMEDOUT; + } + } + + return 0; +} + +int asm28xx_spi_start(struct xmass_dev *d) +{ + uint8_t ctrl; + int res, j; + + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, &ctrl); + if (res) + return res; + + ctrl &= ~SPI_TERM_BIT; /* clear terminate / CS bit */ + + return pci_write_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, ctrl); +} + +int asm28xx_spi_terminate(struct xmass_dev *d) +{ + uint8_t ctrl; + + int res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, &ctrl); + if (res) + return res; + + ctrl |= SPI_TERM_BIT; /* set terminate / CS bit */ + + return pci_write_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, ctrl); +} + +int asm28xx_spi_write(struct xmass_dev *d, const uint8_t *buf, uint8_t len) +{ + uint32_t value = 0; + uint8_t ctrl; + uint8_t i; + int res; + + /* only 1..4 bytes supported */ + if (len < 1 || len > 4) + return -EINVAL; + + /* controller must be idle */ + res = asm28xx_spi_idle(d); + if (res) + return res; + + /* pack bytes little-endian into a 32-bit word */ + for (i = 0; i < len; i++) + ((uint8_t *)&value)[i] = buf[i]; + + /* write data */ + res = pci_write_config_dword(d->pasmdev, ASM28XX_SPI_DATA_REG, value); + if (res) + return res; + + /* start write transaction */ + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, &ctrl); + if (res) + return res; + + // ctrl &= 0x04; + ctrl = (len & 0x07) | SPI_GO_BITS; + + res = pci_write_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, ctrl); + if (res) + return res; + + /* wait for completion */ + return asm28xx_spi_idle(d); +} + +int asm28xx_spi_read(struct xmass_dev *d, uint8_t *buf, uint8_t len) +{ + uint32_t value; + uint8_t ctrl; + uint8_t i; + int res; + + /* only 1..4 bytes supported */ + if (len < 1 || len > 4) + return -EINVAL; + + /* controller must be idle */ + res = asm28xx_spi_idle(d); + if (res) + return res; + + /* + * Program control register: + * - low 3 bits = length + * - bit 0x20 = start read transaction + */ + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, &ctrl); + if (res) + return res; + + ctrl = (len & 0x07) | SPI_BUSY_BIT; + + res = pci_write_config_byte(d->pasmdev, ASM28XX_SPI_CTRL_REG, ctrl); + if (res) + return res; + + /* wait for read completion */ + res = asm28xx_spi_idle(d); + if (res) + return res; + + /* read data */ + res = pci_read_config_dword(d->pasmdev, ASM28XX_SPI_DATA_REG, &value); + if (res) + return res; + + /* unpack little-endian bytes */ + for (i = 0; i < len; i++) + buf[i] = ((uint8_t *)&value)[i]; + + return 0; +} + +int asm28xx_spi_release_grant(struct xmass_dev *d) +{ + uint8_t val; + int res; + + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_GRANT_REG, &val); + if (res) + return res; + + val &= ~SPI_REQ_BIT; + + return pci_write_config_byte(d->pasmdev, ASM28XX_SPI_GRANT_REG, val); +} + + +int asm28xx_spi_get_grant(struct xmass_dev *d) +{ + uint32_t iter; + uint8_t status; + int res; + + /* request ownership */ + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_GRANT_REG, &status); + if (res) + return res; + + status |= SPI_REQ_BIT; + + res = pci_write_config_byte(d->pasmdev, ASM28XX_SPI_GRANT_REG, status); + if (res) + return res; + + for (iter = 0; ; iter++) { + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_GRANT_REG, &status); + if (res) + return res; + + /* wait until grant bit is set */ + if (status & SPI_GNT_BIT) + return 0; + + /* timeout (~3,000,000 ticks) */ + if (iter > 0x2DC6BF) { + //puts("SPI grant timeout"); + //printf("SPI grant status = 0x%02x\n", status); + printk(KERN_INFO XPFX "xmass:%d SPI grant timeout! GRANT status = 0x%02x\n", d->devno, status); + + asm28xx_spi_release_grant(d); + return -ETIMEDOUT; + } + } +} + +int asm28xx_spi_controller_init(struct xmass_dev *d) +{ + uint8_t val; + int res; + + /* configure control register */ + res = pci_read_config_byte(d->pasmdev, ASM28XX_SPI_MISC_REG, &val); + if (res) + return res; + + val &= 0xD1; + + return pci_write_config_byte(d->pasmdev, ASM28XX_SPI_MISC_REG, val); +} + + +static int xmass_gpio_stream(struct xmass_dev *d, unsigned delay_ns, unsigned len, uint8_t* s_out, uint8_t* s_in) +{ + unsigned i; + int res = 0; + + for (i = 0; i < len; i++) { + res = pci_write_config_byte(d->pasmdev, ASM28XX_GPIO0_OUT, s_out[i]); + if (res) + return res; + + if (s_in) { + res = pci_read_config_byte(d->pasmdev, ASM28XX_GPIO0_IN, &s_in[i]); + if (res) + return res; + } + + if (delay_ns) + ndelay(delay_ns); + } + + return res; +} + +// Read 1-4 bytes flash status registers registrer +int asm28xx_spi_flash_read_reg(struct xmass_dev *d, uint8_t* cmd, unsigned wrlen, unsigned rdlen, uint8_t *id) +{ + int res; + res = asm28xx_spi_start(d); + if (res) + return res; + + if (wrlen > 0) { + /* write opcode + dummy bytes */ + res = asm28xx_spi_write(d, cmd, wrlen); + if (res) + return res; + } + + if (rdlen > 0) { + /* read 3-byte JEDEC / device ID */ + res = asm28xx_spi_read(d, id, rdlen); + if (res) + return res; + } + + res = asm28xx_spi_terminate(d); + if (res) + return res; + + return 0; +} + +#define SPI_CMD_WRSR 0x01 + +#define FLASH_READ_CMD 0x03 +#define FLASH_WREN_CMD 0x06 +#define FLASH_SE_4K_CMD 0x20 +#define FLASH_RDSR_CMD 0x05 +#define FLASH_PP_CMD 0x02 + +#define SPI_SR_WIP 0x01 /* Write In Progress */ +#define SPI_SR_WEL 0x02 /* Write Enable Latch */ + +int asm28xx_spi_flash_read(struct xmass_dev *d, unsigned size, uint8_t *out) +{ + uint32_t addr = 0; + uint8_t cmd[4]; + int res; + + while (addr < size) { + /* build READ command: 0x03 + 24-bit address */ + cmd[0] = FLASH_READ_CMD; + cmd[1] = (addr >> 16) & 0xFF; + cmd[2] = (addr >> 8) & 0xFF; + cmd[3] = (addr >> 0) & 0xFF; + res = asm28xx_spi_flash_read_reg(d, cmd, 4, 4, &out[addr]); + if (res) + return res; + + addr += 4; + } + + return 1; +} + +int asm28xx_spi_write_enable(struct xmass_dev *d) +{ + int res, cnt; + uint8_t cmd[1]; + uint8_t sr; + + for (cnt = 0; cnt < 1000; cnt++) { + /* Write Enable */ + cmd[0] = FLASH_WREN_CMD; + res = asm28xx_spi_flash_read_reg(d, cmd, 1, 0, NULL); + if (res) + return res; + + cmd[0] = FLASH_RDSR_CMD; + res = asm28xx_spi_flash_read_reg(d, cmd, 1, 1, &sr); + if (res) + return res; + + if ((sr & SPI_SR_WEL) == SPI_SR_WEL) + return 0; + + udelay(10); + } + + dev_info(&d->pasmdev->dev, "WREN TIMEDOUT RDSR = 0x%02x\n", sr); + return -ETIMEDOUT; +} + +int asm28xx_spi_wait_done(struct xmass_dev *d) +{ + int res, cnt; + uint8_t cmd[1]; + uint8_t sr; + + /* Wait until flash is ready (WIP=0) */ + cmd[0] = FLASH_RDSR_CMD; + for (cnt = 0; cnt < 1000; cnt++) { + res = asm28xx_spi_flash_read_reg(d, cmd, 1, 1, &sr); + if (res) + return res; + + if ((sr & SPI_SR_WIP) == 0) + return 0; + + udelay(1000); + } + + dev_info(&d->pasmdev->dev, "WIP TIMEOUT RDSR = 0x%02x\n", sr); + return -ETIMEDOUT; +} + +int asm28xx_spi_flash_blank(struct xmass_dev *d, unsigned size) +{ + uint32_t addr; + uint8_t cmd[4]; + uint8_t sr; + int res, cnt; + + res = asm28xx_spi_write_enable(d); + if (res) + return res; + + /* Unprotect */ + cmd[0] = SPI_CMD_WRSR; + cmd[1] = 0; + res = asm28xx_spi_flash_read_reg(d, cmd, 2, 0, NULL); + if (res) + return res; + + for (addr = 0; addr < size; addr += 0x1000) { + res = asm28xx_spi_write_enable(d); + if (res) + return res; + + /* 4KB Sector Erase (0x20) */ + cmd[0] = FLASH_SE_4K_CMD; + cmd[1] = (addr >> 16) & 0xFF; + cmd[2] = (addr >> 8) & 0xFF; + cmd[3] = (addr >> 0) & 0xFF; + res = asm28xx_spi_flash_read_reg(d, cmd, 4, 0, NULL); + if (res) + return res; + + /* Wait until flash is ready (WIP=0) */ + res = asm28xx_spi_wait_done(d); + if (res) + return res; + } + + return 0; +} + +int asm28xx_spi_flash_write_page(struct xmass_dev *d, uint32_t addr, + const uint8_t *data, uint32_t len) +{ + uint8_t cmd[4]; + unsigned off; + int res, cnt; + cmd[0] = FLASH_PP_CMD; + cmd[1] = (addr >> 16) & 0xFF; + cmd[2] = (addr >> 8) & 0xFF; + cmd[3] = (addr >> 0) & 0xFF; + + res = asm28xx_spi_write_enable(d); + if (res) + return res; + + res = asm28xx_spi_start(d); + if (res) + return res; + + res = asm28xx_spi_write(d, cmd, 4); + if (res) + return res; + + for (off = 0; off < len; off += 4) { + res = asm28xx_spi_write(d, data + off, 4); + if (res) + return res; + } + + res = asm28xx_spi_terminate(d); + if (res) + return res; + + return asm28xx_spi_wait_done(d); +} + +int asm28xx_spi_flash_write(struct xmass_dev *d, const uint8_t *image, uint32_t total) +{ + uint32_t offset; + int res; + + /* program page-by-page (256 bytes typical) */ + for (offset = 0; offset < total; offset += 256) { + res = asm28xx_spi_flash_write_page(d, offset, image + offset, 256); + if (res) + return res; + } + + return 0; +} + +void asm28xx_signoff(uint8_t *tmp, const char *payload) +{ + uint8_t sum = 0; + unsigned len = strlen(payload), i; + const unsigned max_len = 0xE30 - 0xE10; + static const uint8_t g_asm_id[16] = { + 0x88, 0xb0, 0x15, 0x9a, 0x5e, 0xe3, 0xae, 0x40, + 0xaf, 0x8f, 0x00, 0x49, 0x58, 0xba, 0x7a, 0xf2 + }; + + /* 1) place id at 0xE00 */ + memcpy(tmp + 0xE00, g_asm_id, 16); + + /* 2) place custom string at 0xE10..0xE30 (no NULL terminator) */ + if (len > max_len) + len = max_len; + + memcpy(tmp + 0xE10, payload, len); + + /* 3) marker byte */ + tmp[0xEFE] = 0x5A; + + /* 4) checksum over [0xE00 .. 0xEFE-1] */ + for (i = 0xE00; i < 0xEFE; i++) + sum += tmp[i]; + + tmp[0xEFF] = sum; +} + + +#define ASM28XX_FLASH_IMAGE_SIZE 65536 +uint8_t s_temp_data[ASM28XX_FLASH_IMAGE_SIZE]; + +static long xmassfd_ioctl(struct file *filp, + unsigned int ioctl_num,/* The number of the ioctl */ + unsigned long ioctl_param) /* The parameter to it */ +{ + int res; + struct xmass_dev *xmassdev = filp->private_data; + void __user *uptr = (void __user *)ioctl_param; + + if (!(xmassdev->dev_mask & DEV_VALID)) + return -EIO; + + if (xmassdev->pasmdev->error_state != pci_channel_io_normal) { + dev_err(&xmassdev->pasmdev->dev, "PCI device is disconnected or in error state\n"); + return -EIO; + } + + // JTAG bit-banging transfer + switch (ioctl_num) { + case PCIE_FLASH_WRITE: { + if (copy_from_user(s_temp_data, uptr, ASM28XX_FLASH_IMAGE_SIZE)) + return -EFAULT; + + asm28xx_signoff(s_temp_data, "XMASS"); + + res = pm_runtime_resume_and_get(&xmassdev->pasmdev->dev); + if (res < 0) + return res; + + res = res ? res : pci_write_config_byte(xmassdev->pasmdev, ASM28XX_REG_SWITCH, 1); // Switch to GPIO control mode + res = res ? res : asm28xx_spi_controller_init(xmassdev); + res = res ? res : asm28xx_spi_get_grant(xmassdev); + + res = res ? res : asm28xx_spi_flash_write(xmassdev, s_temp_data, ASM28XX_FLASH_IMAGE_SIZE); + + asm28xx_spi_release_grant(xmassdev); + pm_runtime_put(&xmassdev->pasmdev->dev); + return res; + } + case PCIE_FLASH_ERASE: { + res = pm_runtime_resume_and_get(&xmassdev->pasmdev->dev); + if (res < 0) + return res; + + res = res ? res : pci_write_config_byte(xmassdev->pasmdev, ASM28XX_REG_SWITCH, 1); // Switch to GPIO control mode + res = res ? res : asm28xx_spi_controller_init(xmassdev); + res = res ? res : asm28xx_spi_get_grant(xmassdev); + res = res ? res : asm28xx_spi_flash_blank(xmassdev, ASM28XX_FLASH_IMAGE_SIZE); + + asm28xx_spi_release_grant(xmassdev); + pm_runtime_put(&xmassdev->pasmdev->dev); + return res; + } + case PCIE_FLASH_READ: { + res = pm_runtime_resume_and_get(&xmassdev->pasmdev->dev); + if (res < 0) + return res; + + res = res ? res : pci_write_config_byte(xmassdev->pasmdev, ASM28XX_REG_SWITCH, 1); // Switch to GPIO control mode + res = res ? res : asm28xx_spi_controller_init(xmassdev); + res = res ? res : asm28xx_spi_get_grant(xmassdev); + res = res ? res : asm28xx_spi_flash_read(xmassdev, ASM28XX_FLASH_IMAGE_SIZE, s_temp_data); + + asm28xx_spi_release_grant(xmassdev); + pm_runtime_put(&xmassdev->pasmdev->dev); + + if (copy_to_user(uptr, s_temp_data, ASM28XX_FLASH_IMAGE_SIZE)) + return -EFAULT; + + return res; + } + case PCIE_GETIDS: { + uint8_t fwid[6] = { 0, }; + uint8_t rdid[4] = { 0, }; + uint8_t cmd = ESPI_CMD_RDID_1; + unsigned j; + + res = pm_runtime_resume_and_get(&xmassdev->pasmdev->dev); + if (res < 0) + return res; + + for (j = 0; j < 6; j++) { + res = res ? res : pci_read_config_byte(xmassdev->pasmdev, ASM28XX_FWID_0 + j, &fwid[j]); + } + + res = res ? res : pci_write_config_byte(xmassdev->pasmdev, ASM28XX_REG_SWITCH, 1); // Switch to GPIO control mode + res = res ? res : asm28xx_spi_controller_init(xmassdev); + res = res ? res : asm28xx_spi_get_grant(xmassdev); + res = res ? res : asm28xx_spi_flash_read_reg(xmassdev, &cmd, 1, 4, rdid); + + asm28xx_spi_release_grant(xmassdev); + pm_runtime_put(&xmassdev->pasmdev->dev); + + if (copy_to_user(uptr, fwid, 6)) + return -EFAULT; + + if (copy_to_user(uptr + 6, rdid, 4)) + return -EFAULT; + + return res; + } + case PCIE_GPIOS_8: { + unsigned len; + unsigned delay_ns; + struct xmass_iop iop; + int has_rx; + + static uint8_t out_data[MAX_PROC_BLK]; + static uint8_t in_data[MAX_PROC_BLK]; + + if (copy_from_user(&iop, uptr, sizeof(iop))) + return -EFAULT; + + len = iop.io_len; + if (len > MAX_PROC_BLK) + len = MAX_PROC_BLK; + + delay_ns = iop.delay_ns; + has_rx = (iop.in_buf != NULL); + + if (copy_from_user( out_data, iop.out_buf, len)) + return -EFAULT; + + /* Ensure ASM2806 isn't in sleep mode */ + res = pm_runtime_resume_and_get(&xmassdev->pasmdev->dev); + if (res < 0) + return res; + + printk(KERN_NOTICE XPFX "XMASS_JTAG_IO LEN=%d (%d) NS=%d\n", len, iop.io_len, delay_ns); + + pci_write_config_byte(xmassdev->pasmdev, ASM28XX_REG_SWITCH, 1); // Switch to GPIO control mode + pci_write_config_byte(xmassdev->pasmdev, ASM28XX_GPIO0_CTRL, (1 << 0) | (1 << 3) | (1 << 5)); + + res = xmass_gpio_stream(xmassdev, delay_ns, len, out_data, has_rx ? in_data : NULL); + + pm_runtime_put(&xmassdev->pasmdev->dev); + + if (res) + return res; + + if (has_rx) { + if (copy_to_user(iop.in_buf, in_data, len)) + return -EFAULT; + } + + return len; + } + } + + return -EINVAL; +} + + +struct file_operations xmass_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = xmassfd_ioctl, + .open = xmassfd_open, + .release = xmassfd_release, + .read = xmassfd_read, + .write = xmassfd_write, +}; + +static int xmass_setup_cdev(struct xmass_dev *xmassdev) +{ + dev_t dev_num = xmass_dev_first + xmassdev->devno; + + cdev_init(&xmassdev->cdev, &xmass_fops); + xmassdev->cdev.owner = THIS_MODULE; + xmassdev->cdev.ops = &xmass_fops; + return cdev_add (&xmassdev->cdev, dev_num, 1); +} + + +static int xmass_probe(struct pci_dev *pdev) +{ + struct xmass_dev* xmassdev; + unsigned xmass_no = find_first_zero_bit(xmass_busy_map, TOT_XMASS_DEVS); + int err; + + if (xmass_no == TOT_XMASS_DEVS) { + dev_err(&pdev->dev, "XMASS: Maximum number of devices is reahced, ignoring device!\n"); + return -ENOMEM; + } + + xmassdev = kzalloc(sizeof(*xmassdev), GFP_KERNEL); + if (!xmassdev) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + return -ENOMEM; + } + + xmassdev->devno = xmass_no; + xmassdev->pasmdev = pci_dev_get(pdev); + xmassdev->dev_mask = 0; + + spin_lock_init(&xmassdev->slock); + + xmassdev->dev_mask = DEV_VALID; + xmassdev->cdevice = device_create(xmass_class, + &pdev->dev, + MKDEV(MAJOR(xmass_dev_first), MINOR(xmass_dev_first) + xmassdev->devno), + NULL, + XMASS_DEVICE_NAME "%d", + xmassdev->devno); + if (IS_ERR(xmassdev->cdevice)) { + printk(KERN_NOTICE XPFX "Unable to register device class\n"); + goto failed_device; + } + + err = xmass_setup_cdev(xmassdev); + if (err) { + printk(KERN_NOTICE XPFX "Error %d initializing cdev\n", err); + goto failed_cdev; + } + + dev_info(&pdev->dev, "is linked to /dev/xmass%d\n", xmass_no); + + set_bit(xmass_no, xmass_busy_map); + xmassdev->asm_bus_number = pdev->bus->number; + xmassdev->next = xmass_list; + xmass_list = xmassdev; + return 0; + +failed_cdev: + device_destroy(xmass_class, MKDEV(MAJOR(xmass_dev_first), MINOR(xmass_dev_first) + xmassdev->devno)); +failed_device: + pci_dev_put(pdev); + kfree(xmassdev); + return err; +} + + +static void xmass_dev_remove(struct xmass_dev* xmassdev) +{ + dev_info(&xmassdev->pasmdev->dev, "is unlinked, /dev/xmass%d removed\n", xmassdev->devno); + + cdev_del(&xmassdev->cdev); + //struct device* p = get_device(&xmassdev->pasmdev->dev); + //if (p) { + device_destroy(xmass_class, MKDEV(MAJOR(xmass_dev_first), MINOR(xmass_dev_first) + xmassdev->devno)); + // put_device(&xmassdev->pasmdev->dev); + //} else { + // dev_info(&xmassdev->pasmdev->dev, "device is empty\n"); + //} + pci_dev_put(xmassdev->pasmdev); + + xmassdev->dev_mask = 0; + + xmassdev->pasmdev = NULL; + xmassdev->asm_bus_number = ~0U; + + clear_bit(xmassdev->devno, xmass_busy_map); +} + +static struct xmass_dev *get_xmass_by_pci(struct pci_dev *pdev) +{ + struct xmass_dev *xptr = xmass_list; + while (xptr != NULL) { + if (xptr->pasmdev == pdev) { + return xptr; + } + xptr = xptr->next; + } + return NULL; +} + +static void xmass_remove(struct pci_dev *pdev) +{ + struct xmass_dev *xptr = get_xmass_by_pci(pdev); + if (xptr != NULL) { + xmass_dev_remove(xptr); + } +} + +enum { + XMASS_PCI_VID = 0x1B21, + XMASS_PCI_DID = 0x2806, +}; + +MODULE_DEVICE_TABLE(pci, usdr_pci_table); + +static struct pci_driver usdr_driver = { + .name = DRV_NAME, + .id_table = usdr_pci_table, + .probe = usdr_probe, + .remove = usdr_remove +}; + +static int asm28xx_get_fw(struct pci_dev *pdev, uint8_t *fwid) +{ + int i, res; + for (i = 0; i < 6; i++) { + res = pci_read_config_byte(pdev, ASM28XX_FWID_0 + i, &fwid[i]); + if (res) + return res; + } + return 0; +} + +static int check_for_xamss(void) +{ + struct pci_dev *pdev = NULL; + int res; + uint8_t fwid[6]; + + while ((pdev = pci_get_device(XMASS_PCI_VID, XMASS_PCI_DID, pdev))) { + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_UPSTREAM) + continue; + + /* Ensure ASM2806 isn't in sleep mode */ + res = pm_runtime_resume_and_get(&pdev->dev); + if (res < 0) + return res; + + res = asm28xx_get_fw(pdev, fwid); + if (res) { + dev_err(&pdev->dev, "Unable to read firmware ID, error %x\n", res); + + pm_runtime_put(&pdev->dev); + continue; + } + + dev_info(&pdev->dev, "Found XMASS device: [%02x:%04x] ASM2806 Firmware %02x%02x%02x%02x%02x%02x %px\n", pdev->bus->number, pdev->devfn, fwid[0], fwid[1], fwid[2], fwid[3], fwid[4], fwid[5], pdev); + res = xmass_probe(pdev); + if (res) { + dev_info(&pdev->dev, "Unable to create XMASS char dev: %d\n", res); + } + + /* do NOT pci_dev_put(pdev) here */ + pm_runtime_put(&pdev->dev); + } + + /* Release the final reference */ + pci_dev_put(pdev); + + return 0; +} + +static void release_xmasses(void) +{ + struct xmass_dev *xptr = xmass_list; + while (xptr != NULL) { + xmass_dev_remove(xptr); + xptr = xptr->next; + } +} + +static int __init usdr_init(void) +{ + int err; + err = alloc_chrdev_region(&usdr_dev_first, 0, TOT_USDR_DEVS, DRV_NAME); + if (err) { + printk(KERN_NOTICE PFX "Unable to allocate chrdev region: %d\n", err); + goto failed_chrdev_usdr; + } + err = alloc_chrdev_region(&xmass_dev_first, 0, TOT_XMASS_DEVS, DRV_NAME); + if (err) { + printk(KERN_NOTICE XPFX "Unable to allocate chrdev region: %d\n", err); + goto failed_chrdev_xmass; + } + +#ifndef HAVE_CLASS_CREATE_ONE_ARG + usdr_class = class_create(THIS_MODULE, CLASS_NAME); +#else + usdr_class = class_create(CLASS_NAME); +#endif + if (IS_ERR(usdr_class)) { + printk(KERN_NOTICE PFX "Unable to register usdr class\n"); + goto failed_setup_cdev; + } + +#ifndef HAVE_CLASS_CREATE_ONE_ARG + xmass_class = class_create(THIS_MODULE, XMASS_CLASS_NAME); +#else + xmass_class = class_create(XMASS_CLASS_NAME); +#endif + if (IS_ERR(usdr_class)) { + printk(KERN_NOTICE PFX "Unable to register xmass class\n"); + goto failed_xmass; + } + + err = pci_register_driver(&usdr_driver); + if (err) { + printk(KERN_NOTICE PFX "Unable to register PCI driver: %d\n", err); + goto failed_pci; + } + + check_for_xamss(); + return 0; + +failed_pci: + class_destroy(xmass_class); +failed_xmass: + class_destroy(usdr_class); +failed_chrdev_xmass: + unregister_chrdev_region(xmass_dev_first, TOT_XMASS_DEVS); +failed_setup_cdev: + unregister_chrdev_region(usdr_dev_first, TOT_USDR_DEVS); +failed_chrdev_usdr: + return err; +} + +static void __exit usdr_cleanup(void) +{ + struct usdr_dev *ptr = usdr_list, *next; + struct xmass_dev *xptr = xmass_list, *xnext; + + pci_unregister_driver(&usdr_driver); + // Manually clean + release_xmasses(); + + class_destroy(usdr_class); + class_destroy(xmass_class); + + unregister_chrdev_region(usdr_dev_first, TOT_USDR_DEVS); + unregister_chrdev_region(xmass_dev_first, TOT_XMASS_DEVS); + + while (ptr != NULL) { + next = ptr->next; + kfree(ptr); + ptr = next; + } + + while (xptr != NULL) { + xnext = xptr->next; + kfree(xptr); + xptr = xnext; } } diff --git a/src/lib/lowlevel/pcie_uram/pcie_uram_driver_if.h b/src/lib/lowlevel/pcie_uram/pcie_uram_driver_if.h index f7862b77..e9956a08 100644 --- a/src/lib/lowlevel/pcie_uram/pcie_uram_driver_if.h +++ b/src/lib/lowlevel/pcie_uram/pcie_uram_driver_if.h @@ -178,4 +178,20 @@ struct pcie_driver_woa_oob { #define PCIE_DRIVER_DMA_POST _IOW(PCIE_DRIVER_MAGIC, 25, uint32_t) +// XMASS specific functions +struct xmass_iop { + unsigned io_len; // + unsigned delay_ns; // + uint8_t* out_buf; // Write buffer + uint8_t* in_buf; // Readback buffer +}; + +// +#define PCIE_XMASS_DRIVER_MAGIC 0xDC +#define PCIE_GPIOS_8 _IOWR(PCIE_XMASS_DRIVER_MAGIC, 0, struct xmass_iop) +#define PCIE_FLASH_READ _IOWR(PCIE_XMASS_DRIVER_MAGIC, 1, uint8_t*) +#define PCIE_FLASH_ERASE _IOWR(PCIE_XMASS_DRIVER_MAGIC, 2, uint32_t) +#define PCIE_FLASH_WRITE _IOWR(PCIE_XMASS_DRIVER_MAGIC, 3, uint8_t*) +#define PCIE_GETIDS _IOWR(PCIE_XMASS_DRIVER_MAGIC, 4, uint8_t*) + #endif diff --git a/src/lib/lowlevel/pcie_uram/pcie_uram_main.c b/src/lib/lowlevel/pcie_uram/pcie_uram_main.c index 2d7b615c..15148960 100644 --- a/src/lib/lowlevel/pcie_uram/pcie_uram_main.c +++ b/src/lib/lowlevel/pcie_uram/pcie_uram_main.c @@ -1,5 +1,6 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT +#ifdef __linux__ #include "pcie_uram_main.h" @@ -13,7 +14,7 @@ #include #include #include -#include +#include #include #include "../device/device.h" @@ -98,12 +99,12 @@ int pcie_reg_write32_ioctl(pcie_uram_dev_t* dev, unsigned dwoff, unsigned data) int res = ioctl(dev->fd, PCIE_DRIVER_HWREG_WR32, &rop); if (res == -1) { int err = -errno; - USDR_LOG("PCIE", USDR_LOG_ERROR, + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "pcie:%s unable to write register %d, error %d\n", dev->name, dwoff, err); return err; } - USDR_LOG("PCIE", USDR_LOG_TRACE, "Write[%d] <= %08x\n", + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_TRACE, "Write[%d] <= %08x\n", dwoff, data); return 0; } @@ -117,12 +118,12 @@ int pcie_reg_read32_ioctl(pcie_uram_dev_t* dev, unsigned dwoff, unsigned* data) int res = ioctl(dev->fd, PCIE_DRIVER_HWREG_RD32, &rop); if (res == -1) { int err = -errno; - USDR_LOG("PCIE", USDR_LOG_ERROR, + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "pcie:%s unable to read register %d, error %d\n", dev->name, dwoff, err); return err; } - USDR_LOG("PCIE", USDR_LOG_TRACE, "Read [%d] => %08x\n", + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_TRACE, "Read [%d] => %08x\n", dwoff, rop.value); *data = rop.value; return 0; @@ -198,14 +199,14 @@ int pcie_reg_op_iommap(struct pcie_uram_dev* d, unsigned ls_op_addr, d->mmaped_io[d->db.idxreg_base[k]] = htobe32(ls_op_addr - d->db.idxreg_virt_base[k] + i); if (i < memoutsz / 4) { d->mmaped_io[d->db.idxreg_base[k] + 1] = htobe32(outa[i]);\ - USDR_LOG("PCIE", USDR_LOG_TRACE, "Write[%d+%d -> %d] <= %08x\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_TRACE, "Write[%d+%d -> %d] <= %08x\n", d->db.idxreg_virt_base[k], ls_op_addr - d->db.idxreg_virt_base[k] + i, d->db.idxreg_base[k] + 1, outa[i]); } if (i < meminsz / 4) { ina[i] = be32toh(d->mmaped_io[d->db.idxreg_base[k] + 1]); - USDR_LOG("PCIE", USDR_LOG_TRACE, "Read [%d+%d -> %d] => %08x\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_TRACE, "Read [%d+%d -> %d] => %08x\n", d->db.idxreg_virt_base[k], ls_op_addr - d->db.idxreg_virt_base[k] + i, d->db.idxreg_base[k] + 1, ina[i]); @@ -224,18 +225,18 @@ int pcie_reg_op_iommap(struct pcie_uram_dev* d, unsigned ls_op_addr, uint64_t outb = htobe32(outa[i]) | (((uint64_t)htobe32(outa[i + 1])) << 32); *((uint64_t*)&d->mmaped_io[ls_op_addr + i]) = outb; - USDR_LOG("PCIE", USDR_LOG_TRACE, "Write64[%d] <= %08x%08x\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_TRACE, "Write64[%d] <= %08x%08x\n", ls_op_addr + i, outa[i], outa[i + 1]); i++; } else { d->mmaped_io[ls_op_addr + i] = htobe32(outa[i]); - USDR_LOG("PCIE", USDR_LOG_TRACE, "Write32[%d] <= %08x\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_TRACE, "Write32[%d] <= %08x\n", ls_op_addr + i, outa[i]); } } for (i = 0; i < indwsz; i++) { ina[i] = be32toh(d->mmaped_io[ls_op_addr + i]); - USDR_LOG("PCIE", USDR_LOG_TRACE, "Read [%d] => %08x\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_TRACE, "Read [%d] => %08x\n", ls_op_addr + i, ina[i]); } return 0; @@ -261,7 +262,7 @@ int pcie_i2c_read(pcie_uram_dev_t* dev, unsigned reg_base, unsigned control, uns break; } if (i == 100) { - USDR_LOG("PCIE", USDR_LOG_ERROR, "i2c: data not ready, status = %08x!\n", stat); + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "i2c: data not ready, status = %08x!\n", stat); return -EIO; } @@ -287,7 +288,7 @@ int pcie_spi_transact(pcie_uram_dev_t* dev, unsigned bus, unsigned in, unsigned break; } if (i == 100) { - USDR_LOG("PCIE", USDR_LOG_ERROR, "spi: data not ready, status = %08x!\n", out); + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "spi: data not ready, status = %08x!\n", out); return -EIO; } @@ -359,7 +360,7 @@ int pcie_uram_ls_op(lldev_t dev, subdev_t subdev, if (res) return -errno; - USDR_LOG("PCIE", USDR_LOG_NOTE, "SPI%d: DW=%08x => %08x\n", SPIEXT_LSOP_GET_BUS(ls_op_addr), *(const uint32_t*)pout, iospi.dw_io); + USDR_LL_LOG(dev, "PCIE", USDR_LOG_NOTE, "SPI%d: DW=%08x => %08x\n", SPIEXT_LSOP_GET_BUS(ls_op_addr), *(const uint32_t*)pout, iospi.dw_io); if (meminsz) { if (pdb->spi_core[SPIEXT_LSOP_GET_BUS(ls_op_addr)] == SPI_CORE_32W) { @@ -390,7 +391,7 @@ int pcie_uram_ls_op(lldev_t dev, subdev_t subdev, usleep(1000); - USDR_LOG("PCIE", USDR_LOG_NOTE, "I2C%d.%d.%d: W=%d R=%d OUT=%s\n", + USDR_LL_LOG(dev, "PCIE", USDR_LOG_NOTE, "I2C%d.%d.%d: W=%d R=%d OUT=%s\n", LSOP_I2C_INSTANCE(ls_op_addr), LSOP_I2C_BUSNO(ls_op_addr), LSOP_I2C_ADDR(ls_op_addr), ioi2c.wcnt, ioi2c.rcnt, _dump_buffer(memoutsz, pout)); @@ -401,7 +402,7 @@ int pcie_uram_ls_op(lldev_t dev, subdev_t subdev, if (meminsz <= sizeof(ioi2c.rdb)) { memcpy(pin, ioi2c.rdb, meminsz); } - USDR_LOG("PCIE", USDR_LOG_NOTE, "I2C%d.%d.%d: => %s\n", + USDR_LL_LOG(dev, "PCIE", USDR_LOG_NOTE, "I2C%d.%d.%d: => %s\n", LSOP_I2C_INSTANCE(ls_op_addr), LSOP_I2C_BUSNO(ls_op_addr), LSOP_I2C_ADDR(ls_op_addr), _dump_buffer(meminsz, pin)); @@ -440,11 +441,11 @@ int pcie_uram_stream_initialize(lldev_t dev, subdev_t subdev, res = ioctl(d->fd, PCIE_DRIVER_DMA_CONF, &pdsc); if (res) { res = -errno; - USDR_LOG("PCIE", USDR_LOG_ERROR, "Unable to initialize driver DMA configuration, error %d\n", res); + USDR_LL_LOG(dev, "PCIE", USDR_LOG_ERROR, "Unable to initialize driver DMA configuration, error %d\n", res); return res; } if (pdsc.sno >= SIZEOF_ARRAY(d->scache)) { - USDR_LOG("PCIE", USDR_LOG_ERROR, "ioctl(PCIE_DRIVER_DMA_CONF) returned incorrect stream index! idx=%d\n", pdsc.sno); + USDR_LL_LOG(dev, "PCIE", USDR_LOG_ERROR, "ioctl(PCIE_DRIVER_DMA_CONF) returned incorrect stream index! idx=%d\n", pdsc.sno); return -EINVAL; } @@ -485,7 +486,7 @@ int pcie_uram_stream_initialize(lldev_t dev, subdev_t subdev, //d->bit_per_all_sym[pdsc.sno] = params->bits_per_sym; params->underlying_fd = d->fd; params->out_mtu_size = pdsc.dma_buf_sz; - USDR_LOG("PCIE", USDR_LOG_INFO, "Configured stream%d: %d X %d (vma_off=%08lx vma_len=%08lx)\n", + USDR_LL_LOG(dev, "PCIE", USDR_LOG_INFO, "Configured stream%d: %d X %d (vma_off=%08lx vma_len=%08lx)\n", pdsc.sno, pdsc.dma_buf_sz, pdsc.dma_bufs, pdsc.out_vma_off, pdsc.out_vma_length); return 0; @@ -563,25 +564,30 @@ int pcie_uram_dma_wait_or_alloc(struct pcie_uram_dev* d, bool rx, stream_t chann sc->oob_idx = 0; if (res * 16 != data.ooblength) { - USDR_LOG("PCIE", USDR_LOG_CRITICAL_WARNING, " RES %d != %d OOBLEN\n", res, sc->oob_size); + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_CRITICAL_WARNING, " RES %d != %d OOBLEN\n", res, sc->oob_size); } } if (res < 0) { res = -errno; if (res != -ETIMEDOUT) { - USDR_LOG("PCIE", USDR_LOG_CRITICAL_WARNING, "STR[%d]: PCIe %s dma buffer alloc error: %d!\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_CRITICAL_WARNING, "STR[%d]: PCIe %s dma buffer alloc error: %d!\n", channel, rx ? "recv" : "send", res); } else if (rx) { unsigned stat[4]; // TODO: Remove hardcoded address to upper layer pcie_reg_op_iommap(d, 4, &stat[0], 12, NULL, 0); - USDR_LOG("PCIE", USDR_LOG_NOTE, "STR[%d]: PCIe recv dma buffer alloc timed out stat=%08x:%08x:%08x %08x!\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_NOTE, "STR[%d]: PCIe recv dma buffer alloc timed out stat=%08x:%08x:%08x %08x!\n", channel, stat[0], stat[1], stat[2], stat[3]); + + uint32_t* oob32 = (uint32_t*)oob_ptr; + oob32[0] = stat[0]; + oob32[1] = stat[1]; + oob32[2] = stat[2]; } else { unsigned stat[4]; // TODO: Remove hardcoded address to upper layer pcie_reg_op_iommap(d, 28, &stat[0], 16, NULL, 0); - USDR_LOG("PCIE", USDR_LOG_NOTE, "STR[%d]: PCIe send dma buffer alloc timed out stat=%08x:%08x:%08x %08x!\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_NOTE, "STR[%d]: PCIe send dma buffer alloc timed out stat=%08x:%08x:%08x %08x!\n", channel, stat[0], stat[1], stat[2], stat[3]); } return res; @@ -589,7 +595,7 @@ int pcie_uram_dma_wait_or_alloc(struct pcie_uram_dev* d, bool rx, stream_t chann } sc->bufavail = res; - USDR_LOG("PCIE", (res > 1) ? USDR_LOG_NOTE : USDR_LOG_DEBUG, "STR[%d]: Alloced %d buffs, BNO=%d (%016lx) seq=%16ld OOB_sz=%d\n", + USDR_LL_LOG(&d->ll, "PCIE", (res > 1) ? USDR_LOG_NOTE : USDR_LOG_DEBUG, "STR[%d]: Allocated %d buffs, BNO=%d (%016lx) seq=%16ld OOB_sz=%d\n", channel, res, sc->bno, (oob_ptr) ? (*(uint64_t*)sc->oob_cache) : 0, sc->seq, sc->oob_size); } @@ -608,7 +614,7 @@ int pcie_uram_dma_wait_or_alloc(struct pcie_uram_dev* d, bool rx, stream_t chann *oob_size = 2 * sizeof(uint64_t); sc->oob_idx++; } else { - USDR_LOG("PCIE", USDR_LOG_CRITICAL_WARNING, "No OOB data available for %d idx (%d size)!\n", + USDR_LL_LOG(&d->ll, "PCIE", USDR_LOG_CRITICAL_WARNING, "No OOB data available for %d idx (%d size)!\n", sc->oob_idx, sc->oob_size); sc->oob_idx++; @@ -643,7 +649,7 @@ int pcie_uram_recv_dma_release(lldev_t dev, subdev_t subdev, stream_t channel, v if (res) { res = -errno; if (res != -EAGAIN) { - USDR_LOG("PCIE", USDR_LOG_CRITICAL_WARNING, "PCIe recv dma buffer release error: %d!\n", res); + USDR_LL_LOG(dev, "PCIE", USDR_LOG_CRITICAL_WARNING, "PCIe recv dma buffer release error: %d!\n", res); } return res; } @@ -669,7 +675,7 @@ int pcie_uram_send_dma_commit(lldev_t dev, subdev_t subdev, stream_t channel, vo return -EINVAL; if (sc->cfg_bufsize < sz) { - USDR_LOG("PCIE", USDR_LOG_CRITICAL_WARNING, "Stream was configured with %d DMA buffer but tried to write %d!\n", + USDR_LL_LOG(dev, "PCIE", USDR_LOG_CRITICAL_WARNING, "Stream was configured with %d DMA buffer but tried to write %d!\n", d->scache[channel].cfg_bufsize, sz); return -EINVAL; } @@ -722,7 +728,7 @@ int pcie_uram_destroy(lldev_t dev) close(d->fd); d->fd = -1; - USDR_LOG("PCIE", USDR_LOG_INFO, "Device %s destroyed!\n", d->name); + USDR_LL_LOG(dev, "PCIE", USDR_LOG_INFO, "Device %s destroyed!\n", d->name); free(d); return 0; @@ -776,7 +782,7 @@ static int pcie_filtering_params_parse(unsigned pcount, const char** filterparam j = 3; } else { // Non-compatible bus - USDR_LOG("USBX", USDR_LOG_TRACE, "`%s` ignored by PCI driver\n", val); + USDR_LOG("PCIE", USDR_LOG_TRACE, "`%s` ignored by PCI driver\n", val); return -ENODEV; } @@ -918,14 +924,15 @@ int pcie_uram_plugin_create(unsigned pcount, const char** devparam, const char** dev->ll.ops = &s_pcie_uram_ops; dev->fd = fd; - strncpy(dev->name, devname, sizeof(dev->name) - 1); + + snprintf(dev->name, sizeof(dev->name), "%s", devname); // Get UUID device_id_t did; err = ioctl(fd, PCIE_DRIVER_GET_UUID, &did); if (err) { err = -errno; - USDR_LOG("PCIE", USDR_LOG_ERROR, + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "Unable to get device uuid, error %d\n", err); goto remove_dev; } @@ -933,7 +940,7 @@ int pcie_uram_plugin_create(unsigned pcount, const char** devparam, const char** err = ioctl(fd, PCIE_DRIVER_CLAIM_VERSION, USDR_DRIVER_ABI_VERSION); if (err) { err = -errno; - USDR_LOG("PCIE", USDR_LOG_ERROR, + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "ABI verification failed %d, you need to update the driver or host libraries!\n", err); goto remove_dev; } @@ -943,17 +950,17 @@ int pcie_uram_plugin_create(unsigned pcount, const char** devparam, const char** err = usdr_device_create(&dev->ll, did); if (err) { - USDR_LOG("PCIE", USDR_LOG_ERROR, - "Unable to find device spcec for %s, uuid %s! Update software!\n", - dev->name, - usdr_device_id_to_str(did)); + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, + "Unable to find device spec for %s, uuid %s! Update software!\n", + dev->name, + usdr_device_id_to_str(did)); goto remove_dev; } err = device_bus_init(dev->ll.pdev, &dev->db); if (err) { - USDR_LOG("PCIE", USDR_LOG_ERROR, + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "Unable to initialize bus parameters for the device %s!\n", dev->name); goto remove_dev; @@ -976,7 +983,7 @@ int pcie_uram_plugin_create(unsigned pcount, const char** devparam, const char** goto remove_dev; } if (dev->db.bucket_count != 1) { - USDR_LOG("PCIE", USDR_LOG_ERROR, "Broken device description: no bucket!\n"); + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "Broken device description: no bucket!\n"); err = -ENOSPC; goto remove_dev; } @@ -1043,7 +1050,7 @@ int pcie_uram_plugin_create(unsigned pcount, const char** devparam, const char** err = err ? err : ioctl(fd, PCIE_DRIVER_SET_DEVLAYOUT, &dl); if (err) { err = -errno; - USDR_LOG("PCIE", USDR_LOG_ERROR, + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "Unable to set device driver layout, error %d\n", err); goto remove_dev; } @@ -1051,7 +1058,7 @@ int pcie_uram_plugin_create(unsigned pcount, const char** devparam, const char** if (mmapedio) { dev->mmaped_io = mmap(NULL, iospacesz, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, 0); if (dev->mmaped_io == MAP_FAILED) { - USDR_LOG("PCIE", USDR_LOG_CRITICAL_WARNING, "Unable to use MMAPed IO, falling back to ioctl(), error: %d", + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_CRITICAL_WARNING, "Unable to use MMAPed IO, falling back to ioctl(), error: %d", errno); dev->mmaped_io = NULL; @@ -1061,7 +1068,7 @@ int pcie_uram_plugin_create(unsigned pcount, const char** devparam, const char** // Device initialization err = err ? err : dev->ll.pdev->initialize(dev->ll.pdev, pcount, devparam, devval); if (err) { - USDR_LOG("PCIE", USDR_LOG_ERROR, + USDR_LL_LOG(&dev->ll, "PCIE", USDR_LOG_ERROR, "Unable to initialize device, error %d\n", err); goto clear_map; } @@ -1094,3 +1101,6 @@ const struct lowlevel_plugin *pcie_uram_register() { return &s_pcie_uram_plugin; } + +#endif + diff --git a/src/lib/lowlevel/usb_ft601/usb_ft601_generic.c b/src/lib/lowlevel/usb_ft601/usb_ft601_generic.c index 2164d9fd..00b0d4e1 100644 --- a/src/lib/lowlevel/usb_ft601/usb_ft601_generic.c +++ b/src/lib/lowlevel/usb_ft601/usb_ft601_generic.c @@ -100,7 +100,7 @@ int usbft601_uram_ls_op(lldev_t dev, subdev_t subdev, if (memoutsz * 2 > sizeof(tmpbuf_out)) return -E2BIG; - // Rgister write + // Register write const uint16_t* out_s = (const uint16_t* )pout; for (unsigned i = 0; i < memoutsz / 2; i++) { tmpbuf_out[2 * i + 1] = ls_op_addr + i; @@ -200,8 +200,8 @@ int usbft601_uram_generic_create_and_init(lldev_t lld, unsigned pcount, const ch res = usdr_device_create(lld, *pdevid); if (res) { USDR_LOG(USBG_LOG_TAG, USDR_LOG_ERROR, - "Unable to find device spcec for %s, uuid %s! Update software!\n", - devname, usdr_device_id_to_str(*pdevid)); + "Unable to find device spec for %s, uuid %s! Update software!\n", + devname, usdr_device_id_to_str(*pdevid)); return res; } diff --git a/src/lib/lowlevel/usb_ft601/usb_ft601_libusb.c b/src/lib/lowlevel/usb_ft601/usb_ft601_libusb.c index efd277c3..2e884cf8 100644 --- a/src/lib/lowlevel/usb_ft601/usb_ft601_libusb.c +++ b/src/lib/lowlevel/usb_ft601/usb_ft601_libusb.c @@ -7,16 +7,9 @@ #include #include #include -#include -#include #include -#include #include #include -#include -#include -#include -#include #include #include "../device/device.h" @@ -37,8 +30,8 @@ struct usbft601_dev libusb_generic_dev_t gdev; usb_ft601_generic_t ft601_generic; - sem_t tr_ctrl_out; - sem_t tr_ctrl_rb; + usdr_sem_t tr_ctrl_out; + usdr_sem_t tr_ctrl_rb; unsigned len_ctrl_in_rb; struct libusb_transfer *transfer_in_ctrl[MAX_IN_CTRL_REQS]; @@ -84,7 +77,7 @@ int usbft601_uram_ctrl_out_pkt(lldev_t lld, unsigned pkt_szb, unsigned timeout_m { usbft601_dev_t* d = (usbft601_dev_t*)lld; int res; - res = sem_wait_ex(&d->tr_ctrl_out, timeout_ms * 1000 * 1000); + res = usdr_sem_wait_ex(&d->tr_ctrl_out, timeout_ms * 1000 * 1000); if (res) { return res; } @@ -98,7 +91,7 @@ int usbft601_uram_ctrl_out_pkt(lldev_t lld, unsigned pkt_szb, unsigned timeout_m res = libusb_to_errno(libusb_submit_transfer(transfer)); if (res) { USDR_LOG("USBX", USDR_LOG_ERROR, "FAILED to post CTRL_OUT %d\n", res); - sem_post(&d->tr_ctrl_out); + usdr_sem_post(&d->tr_ctrl_out); return res; } @@ -147,13 +140,13 @@ int usbft601_uram_ctrl_in_pkt(lldev_t lld, unsigned pkt_szb, unsigned timeout_ms static int usbft601_sem_ctrl_rb_wait(lldev_t lld, int64_t timeout) { usbft601_dev_t* d = (usbft601_dev_t*)lld; - return sem_wait_ex(&d->tr_ctrl_rb, timeout); + return usdr_sem_wait_ex(&d->tr_ctrl_rb, timeout); } static void usbft601_sem_ctrl_out_post(lldev_t lld) { usbft601_dev_t* d = (usbft601_dev_t*)lld; - sem_post(&d->tr_ctrl_out); + usdr_sem_post(&d->tr_ctrl_out); } void LIBUSB_CALL libusb_transfer_ctrl_rb(struct libusb_transfer *transfer) @@ -175,7 +168,7 @@ void LIBUSB_CALL libusb_transfer_ctrl_rb(struct libusb_transfer *transfer) return; } - sem_post(&dev->tr_ctrl_rb); + usdr_sem_post(&dev->tr_ctrl_rb); } static @@ -257,8 +250,8 @@ int usbft601_uram_recv_dma_wait(lldev_t dev, subdev_t subdev, stream_t channel, USDR_LOG("USBX", (rxb->allocsz == bd->buffer_sz) ? USDR_LOG_DEBUG : USDR_LOG_ERROR, - "Buffer %d / %08x %08x %08x %08x TO=%d SEQ=%16ld\n", - bd->buffer_sz, pkt[0], pkt[1], pkt[2], pkt[3], timeout, cnt); + "Buffer %d / %08x %08x %08x %08x TO=%d SEQ=%16lld\n", + bd->buffer_sz, pkt[0], pkt[1], pkt[2], pkt[3], timeout, (long long)cnt); *buffer = tr_buffer; @@ -298,7 +291,7 @@ int usbft601_uram_send_dma_get(lldev_t dev, subdev_t subdev, stream_t channel, v unsigned bno = buffers_produce(rxb); *buffer = buffers_get_ptr(rxb, bno); - USDR_LOG("USBX", USDR_LOG_DEBUG, "TX Alloc BNO=%d %ld\n", bno, cnt); + USDR_LOG("USBX", USDR_LOG_DEBUG, "TX Alloc BNO=%d %lld\n", bno, (long long)cnt); cnt++; return 0; @@ -331,7 +324,7 @@ int usbft601_uram_send_dma_commit(lldev_t dev, subdev_t subdev, stream_t channel // Add to senq res = buffers_usb_transfer_post(rxb, bno, sz, bno); if (res) { - USDR_LOG("USBX", USDR_LOG_ERROR,"USB TX%d unable to post busrt to sendq (error %d)\n", channel, res); + USDR_LOG("USBX", USDR_LOG_ERROR,"USB TX%d unable to post burst to sendq (error %d)\n", channel, res); return res; } @@ -437,12 +430,12 @@ int usbft601_uram_async_start(lldev_t lld) int res; usbft601_dev_t* dev = (usbft601_dev_t*)lld; - res = sem_init(&dev->tr_ctrl_out, 0, MAX_OUT_CTRL_REQS); + res = usdr_sem_init(&dev->tr_ctrl_out, 0, MAX_OUT_CTRL_REQS); if (res) { goto failed_prepare; } - res = sem_init(&dev->tr_ctrl_rb, 0, 0); + res = usdr_sem_init(&dev->tr_ctrl_rb, 0, 0); if (res) { goto failed_prepare; } diff --git a/src/lib/lowlevel/usb_ft601/usb_ft601_webusb.c b/src/lib/lowlevel/usb_ft601/usb_ft601_webusb.c index 5d404ac9..13e16911 100644 --- a/src/lib/lowlevel/usb_ft601/usb_ft601_webusb.c +++ b/src/lib/lowlevel/usb_ft601/usb_ft601_webusb.c @@ -123,7 +123,13 @@ static int webusb_ll_stream_initialize(lldev_t dev, subdev_t subdev, lowlevel_stream_params_t* params, stream_t* channel) { int res = 0; - UNUSED const unsigned data_endpoint = (params->streamno == DEV_RX_STREAM_NO) ? EP_IN_DEFSTREAM : EP_OUT_DEFSTREAM; + const unsigned data_endpoint = (params->streamno == DEV_RX_STREAM_NO) ? EP_IN_DEFSTREAM : EP_OUT_DEFSTREAM; + + res = res ? res : ft601_flush_pipe(dev, data_endpoint); + res = res ? res : ft601_set_stream_pipe(dev, data_endpoint, DATA_PACKET_SIZE); + + if (res) + return res; USDR_LOG("USBX", USDR_LOG_ERROR, "webusb_ll_stream_initialize(streamno=%d)\n", params->streamno); *channel = params->streamno; diff --git a/src/lib/lowlevel/usb_uram/usb_uram_generic.c b/src/lib/lowlevel/usb_uram/usb_uram_generic.c index 870d0dc6..85222572 100644 --- a/src/lib/lowlevel/usb_uram/usb_uram_generic.c +++ b/src/lib/lowlevel/usb_uram/usb_uram_generic.c @@ -5,6 +5,7 @@ #include #include #include "../../device/generic_usdr/generic_regs.h" +#include usb_uram_generic_t* get_uram_generic(lldev_t dev); @@ -261,18 +262,19 @@ int usb_uram_read_wait(lldev_t dev, unsigned lsop, lsopaddr_t ls_op_addr, size_t usb_uram_generic_t* gen = get_uram_generic(dev); unsigned int_number, reg; - char busname[4]; + const char* busname; + switch(lsop) { case USDR_LSOP_SPI: int_number = gen->spi_int_number[ls_op_addr]; reg = gen->db.spi_core[ls_op_addr]; - strcpy(busname, "SPI"); + busname = "SPI"; break; case USDR_LSOP_I2C_DEV: int_number = gen->i2c_int_number[ls_op_addr]; reg = gen->db.i2c_base[ls_op_addr]; - strcpy(busname, "I2C"); + busname = "I2C"; break; default: return -EOPNOTSUPP; @@ -299,8 +301,8 @@ int usb_uram_generic_create_and_init(lldev_t dev, unsigned pcount, const char** res = usdr_device_create(dev, *pdevid); if (res) { USDR_LOG(USBG_LOG_TAG, USDR_LOG_ERROR, - "Unable to find device spcec for %s, uuid %s! Update software!\n", - devname, usdr_device_id_to_str(*pdevid)); + "Unable to find device spec for %s, uuid %s! Update software!\n", + devname, usdr_device_id_to_str(*pdevid)); return res; } @@ -374,7 +376,8 @@ int usb_uram_generic_create_and_init(lldev_t dev, unsigned pcount, const char** interrupt_base = tmp; //Do these in case of pure USB only - + const unsigned REG_WR_MBUS2_ADDR= 6; + const unsigned REG_WR_MBUS2_DATA= 7; const unsigned REG_WR_PNTFY_CFG = 8; const unsigned REG_WR_PNTFY_ACK = 9; @@ -400,6 +403,9 @@ int usb_uram_generic_create_and_init(lldev_t dev, unsigned pcount, const char** return res; } + // Set no limit for RX USB transfers + res = res ? res : usb_uram_reg_out(dev, REG_WR_MBUS2_ADDR, 0x000000e0); + res = res ? res : usb_uram_reg_out(dev, REG_WR_MBUS2_DATA, 0xffffffff); } else { USDR_LOG(USBG_LOG_TAG, USDR_LOG_WARNING, "Omit interrupt initialization on USB+PCIE mode\n"); } diff --git a/src/lib/lowlevel/usb_uram/usb_uram_libusb.c b/src/lib/lowlevel/usb_uram/usb_uram_libusb.c index 237de7a6..e679d0e9 100644 --- a/src/lib/lowlevel/usb_uram/usb_uram_libusb.c +++ b/src/lib/lowlevel/usb_uram/usb_uram_libusb.c @@ -7,16 +7,9 @@ #include #include #include -#include -#include #include -#include #include #include -#include -#include -#include -#include #include "usb_uram_generic.h" #include "../device/device.h" @@ -62,7 +55,7 @@ enum { IN_NTFY_SIZE = 64, //256, IN_RB_SIZE = 256, - MAX_NTFY_REQS = 1, + MAX_NTFY_REQS = 32, MAX_RB_REQS = 1, MAX_REQUEST_RB_SIZE = 256, @@ -72,7 +65,11 @@ enum { // Streams IN_STRM_SIZE = 512, +#ifdef __APPLE__ + MAX_IN_STRM_REQS = 64, +#else MAX_IN_STRM_REQS = 8, +#endif MAX_OUT_STRM_REQS = 32, RX_PKT_TRAILER_EX = 16, @@ -83,7 +80,7 @@ enum { enum { STREAM_MAX_SLOTS = 64, - MAX_RB_THREADS = 8, + MAX_RB_THREADS = MAX_NTFY_REQS, }; struct stream_params { @@ -99,13 +96,13 @@ struct usb_dev libusb_generic_dev_t gdev; usb_uram_generic_t uram_generic; - sem_t interrupts[MAX_INTERRUPTS]; + usdr_sem_t interrupts[MAX_INTERRUPTS]; uint32_t rbvalue[MAX_INTERRUPTS]; bool stop; - sem_t tr_regout_a; - sem_t tr_rb_a; - sem_t rb_valid[MAX_RB_THREADS]; + usdr_sem_t tr_regout_a; + usdr_sem_t tr_rb_a; + usdr_sem_t rb_valid[MAX_RB_THREADS]; struct libusb_transfer *transfer_regout[MAX_REGOUT_REQS]; struct libusb_transfer *transfer_rb[MAX_RB_REQS]; @@ -129,6 +126,7 @@ struct usb_dev unsigned rx_buffer_missed[1]; uint32_t rb_valid_idx; + uint32_t rb_req_idx; uint32_t tx_stat_prev[4]; uint32_t tx_stat_cnt; @@ -152,13 +150,13 @@ int usb_async_start(usb_dev_t* dev) int res; unsigned i; for (i = 0; i < MAX_INTERRUPTS; i++) { - res = sem_init(&dev->interrupts[i], 0, 0); + res = usdr_sem_init(&dev->interrupts[i], 0, 0); } - res = sem_init(&dev->tr_regout_a, 0, MAX_REGOUT_REQS); - res = sem_init(&dev->tr_rb_a, 0, MAX_RB_REQS); + res = usdr_sem_init(&dev->tr_regout_a, 0, MAX_REGOUT_REQS); + res = usdr_sem_init(&dev->tr_rb_a, 0, MAX_RB_REQS); for (i = 0; i < MAX_RB_THREADS; i++) { - res = sem_init(&dev->rb_valid[i], 0, 0); + res = usdr_sem_init(&dev->rb_valid[i], 0, 0); } // Prepare transfer queues @@ -190,6 +188,7 @@ int usb_async_start(usb_dev_t* dev) } dev->rb_valid_idx = 0; + dev->rb_req_idx = 0; dev->tx_stat_cnt = 0; dev->tx_stat_rate = 64; // TX stat update rate return libusb_generic_create_thread(&dev->gdev); @@ -236,7 +235,7 @@ static int usb_post_regout(usb_dev_t* dev, uint32_t *regoutbuffer, unsigned coun dev->gdev.name, count_dw, tot_wrs, tot_rbs, tot_reqlen_dw, s_dump_buffer(regoutbuffer, count_dw * 4)); - res = sem_wait(&dev->tr_regout_a); + res = usdr_sem_wait(&dev->tr_regout_a); if (res) { res = -errno; return res; @@ -262,7 +261,7 @@ static int usb_post_regout(usb_dev_t* dev, uint32_t *regoutbuffer, unsigned coun static int usb_post_rb(usb_dev_t* dev, uint32_t* buffer, unsigned max_buffer_dw, unsigned* ridx) { int res; - res = sem_wait(&dev->tr_rb_a); + res = usdr_sem_wait(&dev->tr_rb_a); if (res) { res = -errno; return res; @@ -295,7 +294,7 @@ void LIBUSB_CALL libusb_transfer_regout(struct libusb_transfer *transfer) return; } - sem_post(&dev->tr_regout_a); + usdr_sem_post(&dev->tr_regout_a); } void LIBUSB_CALL libusb_transfer_rb(struct libusb_transfer *transfer) @@ -312,12 +311,12 @@ void LIBUSB_CALL libusb_transfer_rb(struct libusb_transfer *transfer) transfer->status, transfer->actual_length); return; } - sem_post(&dev->tr_rb_a); + usdr_sem_post(&dev->tr_rb_a); //Signal reply ready unsigned pidx = (*arefptr) & (MAX_RB_THREADS - 1); *arefptr = alen; - sem_post(&dev->rb_valid[pidx]); + usdr_sem_post(&dev->rb_valid[pidx]); } void LIBUSB_CALL libusb_transfer_ntfy(struct libusb_transfer *transfer) @@ -361,7 +360,7 @@ void LIBUSB_CALL libusb_transfer_ntfy(struct libusb_transfer *transfer) USDR_LOG("USBX", USDR_LOG_NOTE, "Got notification seq %04x event %d => %08x\n", seqnum, event, buff[i + 1]); dev->rbvalue[event] = buff[++i]; - sem_post(&dev->interrupts[event]); + usdr_sem_post(&dev->interrupts[event]); } else if ((i + 1 + blen) < packet_len / 4) { i += blen + 1; @@ -412,7 +411,7 @@ static int usb_async_regread32(lldev_t d, unsigned addr, uint32_t* data, unsigne if (res) { return res; } - res = sem_wait(&dev->rb_valid[idx]); + res = usdr_sem_wait(&dev->rb_valid[idx]); if (res) { res = -errno; return res; @@ -443,15 +442,15 @@ int usb_uram_generic_get(lldev_t dev, int generic_op, const char** pout) static int usb_uram_wait_msi(usb_dev_t* dev, unsigned i, int timeout_ms) { - return sem_wait_ex(&dev->interrupts[i], timeout_ms * 1000); + return usdr_sem_wait_ex(&dev->interrupts[i], timeout_ms * 1000 * 1000); } static int usb_read_bus(lldev_t dev, unsigned interrupt_number, UNUSED unsigned reg, size_t meminsz, void* pin) { int res; usb_dev_t* d = (usb_dev_t*)dev; - - res = libusb_to_errno(libusb_submit_transfer(d->transfer_ntfy[0])); + unsigned idx = __atomic_fetch_add(&d->rb_req_idx, 1, __ATOMIC_SEQ_CST) & (MAX_NTFY_REQS - 1); + res = libusb_to_errno(libusb_submit_transfer(d->transfer_ntfy[idx])); if (res) return res; @@ -461,11 +460,6 @@ static int usb_read_bus(lldev_t dev, unsigned interrupt_number, UNUSED unsigned if (meminsz != 0) { *(uint32_t*)pin = d->rbvalue[interrupt_number]; -#if 0 - res = usb_uram_reg_in(dev, reg, (uint32_t*)pin); - if (res) - return res; -#endif } return res; } @@ -518,6 +512,10 @@ int _usb_uram_init_rxstream(usb_dev_t* d, res = buffers_usb_init(&d->gdev, prxb, transfers, params->buffer_count, params->block_size + trailer_sz, EP_IN_DEFSTREAM, eventtype); + if (res) { + USDR_LOG("USBX", USDR_LOG_ERROR, "Stream RX initialization failed: %d!\n", res); + return res; + } prxb->auto_restart = true; d->rx_buffer_missed[0] = 0; @@ -658,8 +656,8 @@ int usb_uram_recv_dma_wait(lldev_t dev, subdev_t subdev, stream_t channel, void* USDR_LOG("USBX", (rxb->allocsz == bd->buffer_sz) ? USDR_LOG_DEBUG : USDR_LOG_ERROR, - "Buffer %d / %08x %08x TO=%d SEQ=%16ld\n", - buffer_sz, bursts, skipped, timeout, cnt); + "Buffer %d / %08x %08x TO=%d SEQ=%16lld\n", + buffer_sz, bursts, skipped, timeout, (long long)cnt); if (oob_size && *oob_size >= 8) { // memset(oob_ptr, 0, *oob_size); @@ -712,7 +710,7 @@ int usb_uram_send_dma_get(lldev_t dev, subdev_t subdev, stream_t channel, void** unsigned bno = buffers_produce(rxb); *buffer = buffers_get_ptr(rxb, bno) + TXSTRM_META_SZ; - USDR_LOG("USBX", USDR_LOG_DEBUG, "TX Alloc BNO=%d %ld\n", bno, cnt); + USDR_LOG("USBX", USDR_LOG_DEBUG, "TX Alloc BNO=%d %lld\n", bno, (long long)cnt); // Trottle statistics to relax extra load if (oob_size) { @@ -782,7 +780,7 @@ int usb_uram_send_dma_commit(lldev_t dev, subdev_t subdev, stream_t channel, voi // Add to senq res = buffers_usb_transfer_post(rxb, bno, sz + 16, bno); if (res) { - USDR_LOG("USBX", USDR_LOG_ERROR,"USB TX%d unable to post busrt to sendq (error %d)\n", channel, res); + USDR_LOG("USBX", USDR_LOG_ERROR,"USB TX%d unable to post burst to sendq (error %d)\n", channel, res); return res; } return 0; @@ -829,12 +827,12 @@ int usb_uram_destroy(lldev_t dev) libusb_close(d->gdev.dh); for (unsigned i = 0; i < MAX_INTERRUPTS; i++) - sem_destroy(&d->interrupts[i]); + usdr_sem_destroy(&d->interrupts[i]); - sem_destroy(&d->tr_regout_a); - sem_destroy(&d->tr_rb_a); + usdr_sem_destroy(&d->tr_regout_a); + usdr_sem_destroy(&d->tr_rb_a); for (unsigned i = 0; i < MAX_RB_THREADS; i++) - sem_destroy(&d->rb_valid[i]); + usdr_sem_destroy(&d->rb_valid[i]); free(d); return 0; @@ -916,7 +914,7 @@ int usb_uram_plugin_create(unsigned pcount, const char** devparam, } for (unsigned i = 0; i < MAX_INTERRUPTS; i++) { - res = sem_init(&dev->interrupts[i], 0, 0); + res = usdr_sem_init(&dev->interrupts[i], 0, 0); if (res) goto usballoc_fail; } @@ -953,7 +951,7 @@ int usb_uram_plugin_create(unsigned pcount, const char** devparam, //usb_async_stop(dev->mgr); usb_astart_fail: for (unsigned i = 0; i < MAX_INTERRUPTS; i++) { - sem_destroy(&dev->interrupts[i]); + usdr_sem_destroy(&dev->interrupts[i]); } usballoc_fail: free(dev); diff --git a/src/lib/lowlevel/usdr_lowlevel.c b/src/lib/lowlevel/usdr_lowlevel.c index 9af43a76..ef313785 100644 --- a/src/lib/lowlevel/usdr_lowlevel.c +++ b/src/lib/lowlevel/usdr_lowlevel.c @@ -4,6 +4,9 @@ #include "usdr_lowlevel.h" #include "usb_uram/usb_uram_generic.h" #include +#include +#include +#include lowlevel_ops_t *lowlevel_get_ops(lldev_t dev) { @@ -57,9 +60,9 @@ unsigned lowlevel_initialize_plugins() } //TODO driver loading - plugins[s_driver_count++] = usbft601_uram_register(); plugins[s_driver_count++] = usb_uram_register(); -#if !defined(__EMSCRIPTEN__) && !defined(WVLT_WEBUSB_BUILD) + plugins[s_driver_count++] = usbft601_uram_register(); +#if !defined(__EMSCRIPTEN__) && !defined(WVLT_WEBUSB_BUILD) && !defined(WIN32) && !defined(__APPLE__) plugins[s_driver_count++] = pcie_uram_register(); #endif @@ -107,6 +110,56 @@ int lowlevel_discovery(unsigned pcount, const char** devparam, const char **devv return count; } +void lowlevel_ops_set_custom(lldev_t obj, lowlevel_ops_t* newops) +{ + obj->ops = newops; +} + +static bool s_show_devname = false; + +void usdrlog_ll_devname_en(bool show_devname) +{ + s_show_devname = show_devname; +} + +void usdrlog_ll_out(lldev_t dev, + unsigned loglevel, + const char* subsystem, + const char* function, + const char* file, + int line, + const char* fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + usdrlog_ll_vout(dev, loglevel, subsystem, function, file, line, fmt, ap); + va_end(ap); +} + +void usdrlog_ll_vout(lldev_t dev, + unsigned loglevel, + const char* subsystem, + const char* function, + const char* file, + int line, + const char* fmt, + va_list list) +{ + if (s_show_devname) { + char buf[8192]; + int sz = snprintf(buf, sizeof(buf), "%s: %s", lowlevel_get_devname(dev), fmt); + if (sz < 0) { + buf[8192 - 1] = 0; + } + + return usdrlog_vout(loglevel, subsystem, function, file, line, buf, list); + } + + return usdrlog_vout(loglevel, subsystem, function, file, line, fmt, list); +} + + void __attribute__ ((constructor(110))) setup_lowlevel(void) { lowlevel_initialize_plugins(); } diff --git a/src/lib/lowlevel/usdr_lowlevel.h b/src/lib/lowlevel/usdr_lowlevel.h index daaf4235..6a9cf32d 100644 --- a/src/lib/lowlevel/usdr_lowlevel.h +++ b/src/lib/lowlevel/usdr_lowlevel.h @@ -4,9 +4,9 @@ #ifndef USDR_LOWLEVEL_H #define USDR_LOWLEVEL_H -#include -#include #include +#include + #define USBG_LOG_TAG "USBG" @@ -226,7 +226,6 @@ int lowlevel_discovery(unsigned pcount, const char** devparam, const char **devv unsigned maxbuf, char* buf); int lowlevel_create(unsigned pcount, const char** devparam, const char **devval, lldev_t* odev, unsigned vidpid, void* webops, uintptr_t param); - device_t* lowlevel_get_device(lldev_t obj); // Basic object @@ -236,5 +235,32 @@ struct lowlevel_dev { }; typedef struct lowlevel_dev lowlevel_dev_t; +// Set new low-level device filter to specifically process some exceptions +void lowlevel_ops_set_custom(lldev_t obj, lowlevel_ops_t* newops); + + +void usdrlog_ll_devname_en(bool show_devname); + +void usdrlog_ll_out(lldev_t dev, + unsigned loglevel, + const char* subsystem, + const char* function, + const char* file, + int line, + const char* fmt, ...) __attribute__ ((format (printf, 7, 8))); + +void usdrlog_ll_vout(lldev_t dev, + unsigned loglevel, + const char* subsystem, + const char* function, + const char* file, + int line, + const char* fmt, + va_list list) __attribute__ ((format (printf, 7, 0))); + +#define USDR_LL_LOG(dev, system, level, ...) \ +do { \ + usdrlog_ll_out(dev, (level), (system), __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__); \ +} while (0) #endif diff --git a/src/lib/lowlevel/verilator_ll/verilatorll_wrap.c b/src/lib/lowlevel/verilator_ll/verilatorll_wrap.c index c904412e..f4c66fa3 100644 --- a/src/lib/lowlevel/verilator_ll/verilatorll_wrap.c +++ b/src/lib/lowlevel/verilator_ll/verilatorll_wrap.c @@ -12,10 +12,10 @@ #include #include #include -#include #include #include +#include #include #include @@ -49,8 +49,8 @@ struct rbdata { struct verilator_protocol_unix { - sem_t tags[MAX_TAGS]; - sem_t tags_avail; + usdr_sem_t tags[MAX_TAGS]; + usdr_sem_t tags_avail; uint32_t tags_free_idx; @@ -68,12 +68,12 @@ int vpu_init(verilator_protocol_unix_t* pvpu, const char* dev) int res; for (unsigned i = 0; i < MAX_INTERRUPTS; i++) { - res = sem_init(&pvpu->tags[i], 0, 0); + res = usdr_sem_init(&pvpu->tags[i], 0, 0); if (res) goto sem_tag_fail; } - res = sem_init(&pvpu->tags_avail, 0, 1); + res = usdr_sem_init(&pvpu->tags_avail, 0, 1); if (res) goto sem_tag_avail_fail; @@ -84,7 +84,7 @@ int vpu_init(verilator_protocol_unix_t* pvpu, const char* dev) res = vll_chan_connect(&pvpu->vch, dev); if (res) { - USDR_LOG("VERI", USDR_LOG_CRITICAL_WARNING, "Connecton to verilator filed: error %d\n", res); + USDR_LOG("VERI", USDR_LOG_CRITICAL_WARNING, "Connection to verilator failed: error %d\n", res); goto conn_failed; } @@ -92,7 +92,7 @@ int vpu_init(verilator_protocol_unix_t* pvpu, const char* dev) snprintf(mmapfile, sizeof (mmapfile), "%s.mmap", dev); res = vll_mem_open(&pvpu->vchm, mmapfile, MMAP_SIZE); if (res) { - USDR_LOG("VERI", USDR_LOG_CRITICAL_WARNING, "Openning MMAP area failed: error %d\n", res); + USDR_LOG("VERI", USDR_LOG_CRITICAL_WARNING, "Opening MMAP area failed: error %d\n", res); goto conn_failed; } @@ -122,7 +122,7 @@ struct verilator_dev device_id_t devid; device_bus_t db; - sem_t interrupts[MAX_INTERRUPTS]; + usdr_sem_t interrupts[MAX_INTERRUPTS]; verilator_protocol_unix_t proto; bool terminated; @@ -184,7 +184,7 @@ int verilator_process_recv(verilator_dev_t* dev) USDR_LOG("VERI", USDR_LOG_TRACE, "Readback #%d => %08x\n", hdr.tag, buffer[0]); memcpy(dev->proto.rb[hdr.tag].data, buffer, hdr.size - sizeof(hdr)); - res = sem_post(&dev->proto.tags[hdr.tag]); + res = usdr_sem_post(&dev->proto.tags[hdr.tag]); if (res) return res; @@ -195,7 +195,7 @@ int verilator_process_recv(verilator_dev_t* dev) return -EFAULT; USDR_LOG("VERI", USDR_LOG_TRACE, "Interrupt %d\n", hdr.tag); #ifdef OLD_INTERRUPTS - res = sem_post(&dev->interrupts[hdr.tag]); + res = usdr_sem_post(&dev->interrupts[hdr.tag]); if (res) return res; #else @@ -259,7 +259,7 @@ int verilator_process_recv(verilator_dev_t* dev) } for (;;) { - res = sem_post(&dev->interrupts[irq]); + res = usdr_sem_post(&dev->interrupts[irq]); if (res) return res; if (irq != 0) @@ -317,7 +317,7 @@ int verilator_process_recv(verilator_dev_t* dev) } for (;;) { - res = sem_post(&dev->interrupts[irq]); + res = usdr_sem_post(&dev->interrupts[irq]); if (res) return res; if (irq != 0) @@ -342,7 +342,7 @@ static int verilator_tag_alloc(verilator_protocol_unix_t* dev) { int res, tag = -1; - res = sem_wait(&dev->tags_avail); + res = usdr_sem_wait(&dev->tags_avail); if (res) return res; @@ -362,7 +362,7 @@ static int verilator_tag_release(verilator_protocol_unix_t* dev, unsigned tag) { int res; - res = sem_post(&dev->tags_avail); + res = usdr_sem_post(&dev->tags_avail); if (res) return res; @@ -381,7 +381,7 @@ int verilator_in(verilator_dev_t* dev, unsigned addr, uint32_t *pinval, const un if (res < 0) return res; - res = sem_wait(&dev->proto.tags[tag]); + res = usdr_sem_wait(&dev->proto.tags[tag]); if (res) return res; @@ -570,11 +570,11 @@ static int verilator_wrap_wait_msi(verilator_dev_t* dev, unsigned i, int timeout ts.tv_nsec -= 1000 * 1000 * 1000; ts.tv_sec++; } - res = sem_timedwait(&dev->interrupts[i], &ts); + res = usdr_sem_timedwait(&dev->interrupts[i], &ts); } else if (timeout_ms < 0) { - res = sem_wait(&dev->interrupts[i]); + res = usdr_sem_wait(&dev->interrupts[i]); } else { - res = sem_trywait(&dev->interrupts[i]); + res = usdr_sem_trywait(&dev->interrupts[i]); } if (res) { // sem_* function on error returns -1, get proper error @@ -910,7 +910,7 @@ int verilator_wrap_plugin_create(unsigned pcount, const char** devparam, strncpy(dev->devid_str, usdr_device_id_to_str(did), sizeof(dev->devid_str) - 1); for (unsigned i = 0; i < MAX_INTERRUPTS; i++) { - res = sem_init(&dev->interrupts[i], 0, 0); + res = usdr_sem_init(&dev->interrupts[i], 0, 0); if (res) goto alloc_fail; } @@ -923,8 +923,8 @@ int verilator_wrap_plugin_create(unsigned pcount, const char** devparam, res = usdr_device_create(dev, did, &dev->udev); if (res) { USDR_LOG("VERI", USDR_LOG_ERROR, - "Unable to find device spcec for %s, uuid %s! Update software!\n", - dev->name, usdr_device_id_to_str(did)); + "Unable to find device spec for %s, uuid %s! Update software!\n", + dev->name, usdr_device_id_to_str(did)); goto remove_dev; } diff --git a/src/lib/lowlevel/verilator_ll/verilatorll_wrap.h b/src/lib/lowlevel/verilator_ll/verilatorll_wrap.h index a40684f7..012f9369 100644 --- a/src/lib/lowlevel/verilator_ll/verilatorll_wrap.h +++ b/src/lib/lowlevel/verilator_ll/verilatorll_wrap.h @@ -1,7 +1,7 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT -// Communiction protocol +// Communication protocol #include diff --git a/src/lib/lowlevel/verilator_ll/vpu.h b/src/lib/lowlevel/verilator_ll/vpu.h index b68534b4..6bd543a4 100644 --- a/src/lib/lowlevel/verilator_ll/vpu.h +++ b/src/lib/lowlevel/verilator_ll/vpu.h @@ -1,7 +1,7 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT -// Communiction protocol +// Communication protocol #ifndef VPU_H #define VPU_H diff --git a/src/lib/models/dm_debug.c b/src/lib/models/dm_debug.c index 626b73c7..92721fbe 100644 --- a/src/lib/models/dm_debug.c +++ b/src/lib/models/dm_debug.c @@ -4,25 +4,30 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif + +#include #include #include #include #include #include #include +#include +#include +#ifdef _WIN32 +#include +#include +#else #include -#ifndef WIN32 -#include #include +#include #endif -#include -#include - #include "dm_dev.h" -#include "dm_dev_impl.h" #include "dm_debug.h" +#include + static int usdr_dif_process_cmd(struct usdr_debug_ctx* ctx, char *cmd, unsigned len, char* reply, unsigned rlen) @@ -76,19 +81,21 @@ int usdr_dif_process_cmd(struct usdr_debug_ctx* ctx, char *cmd, unsigned len, } } -#if !defined(WIN32) && !defined(__EMSCRIPTEN__) +#if !defined(__EMSCRIPTEN__) static void* usdr_dif_thread(void* param) { int ret; struct sockaddr_un name; struct usdr_debug_ctx* ctx = (struct usdr_debug_ctx*)param; USDR_LOG("DBGS", USDR_LOG_INFO, "Starting USDR debug thread\n"); + +#ifndef _WIN32 sigset_t set; - pthread_setname_np(pthread_self(), "debug_io"); + usdr_set_thread_name("debug_io"); sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, NULL); - +#endif pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); const char* fifoname = "usdr_debug_pipe"; unlink(fifoname); @@ -162,7 +169,13 @@ static void* usdr_dif_thread(void* param) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if (replen > 0) { - write(data_socket, reply, replen); + int wres = write(data_socket, reply, replen); + if(wres < 0) + { + wres = errno; + USDR_LOG("DBGS", USDR_LOG_ERROR, "write() error:%d!", wres); + goto connection_closed; + } } ssize_t ech = end - (p + buffer); @@ -187,9 +200,10 @@ int usdr_dif_init(const char *params, //void *obj, struct usdr_debug_ctx** octx) { -#if !defined(WIN32) && !defined(__EMSCRIPTEN__) - +#if !defined(__EMSCRIPTEN__) int res; + +#ifndef _WIN32 const char* fifoname = "usdr_debug_pipe"; int fd = mkfifo(fifoname, 0666); if (fd < 0 && errno != EEXIST) { @@ -198,7 +212,7 @@ int usdr_dif_init(const char *params, fifoname, err); return err; } - +#endif struct usdr_debug_ctx* ctx = (struct usdr_debug_ctx*)malloc(sizeof(struct usdr_debug_ctx)); if (!ctx) return -ENOMEM; @@ -224,7 +238,7 @@ int usdr_dif_init(const char *params, int usdr_dif_free(struct usdr_debug_ctx* ctx) { -#if !defined(WIN32) && !defined(__EMSCRIPTEN__) +#if !defined(__EMSCRIPTEN__) close(ctx->fd); pthread_cancel(ctx->debug_thread); diff --git a/src/lib/models/dm_dev.c b/src/lib/models/dm_dev.c index 028aca67..91c4ba22 100644 --- a/src/lib/models/dm_dev.c +++ b/src/lib/models/dm_dev.c @@ -14,7 +14,6 @@ #include #include #include -#include #include "dm_stream.h" #include "../ipblks/streams/streams_api.h" @@ -101,8 +100,12 @@ int _usdr_dmd_create(const struct dev_params *par, pdm_dev_t* odev, if (bus_cnt <= 1) { res = lowlevel_create(par->num, (const char**)par->params, (const char**)par->value, &lldev, vidpid, webops, param); } else { +#ifdef WIN32 + res = -ENOTSUP; +#else res = mdev_create(par->num, (const char**)par->params, (const char**)par->value, &lldev, idx, bus_names, bus_cnt); +#endif } if (res) return res; @@ -164,7 +167,7 @@ int usdr_dmd_create_string(const char* connection_string, pdm_dev_t* odev) break; } else if (strcmp(par.params[k], "bus") == 0) { bus_idx = k; - strncpy(bus_buffer, par.value[k], sizeof(bus_buffer)); + snprintf(bus_buffer, sizeof(bus_buffer), "%s", par.value[k]); unsigned j; char *str, *token, *saveptr; diff --git a/src/lib/port/CMakeLists.txt b/src/lib/port/CMakeLists.txt index 561158dc..f3892428 100644 --- a/src/lib/port/CMakeLists.txt +++ b/src/lib/port/CMakeLists.txt @@ -5,9 +5,17 @@ set(USDR_PORT_LIB_FILES ${CMAKE_CURRENT_SOURCE_DIR}/usdr_logging.c ${CMAKE_CURRENT_SOURCE_DIR}/usdr_port.c ${CMAKE_CURRENT_SOURCE_DIR}/usdr_helpers.c + ${CMAKE_CURRENT_SOURCE_DIR}/portable_fnmatch.c ) list(APPEND USDR_LIBRARY_FILES ${USDR_PORT_LIB_FILES}) set(USDR_LIBRARY_FILES ${USDR_LIBRARY_FILES} PARENT_SCOPE) +if(NOT EMSCRIPTEN) + add_executable(test_fnmatch test_fnmatch.c portable_fnmatch.c) + if(WIN32) + set_target_properties(test_fnmatch PROPERTIES WIN32_EXECUTABLE FALSE) + endif() +endif() + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION include/usdr FILES_MATCHING PATTERN "*.h") diff --git a/src/lib/port/portable_fnmatch.c b/src/lib/port/portable_fnmatch.c new file mode 100644 index 00000000..8daea43e --- /dev/null +++ b/src/lib/port/portable_fnmatch.c @@ -0,0 +1,270 @@ +#include "portable_fnmatch.h" + +#include +#include + +static int is_sep(char c, int flags) { + if (flags & FNM_WINPATH) + return (c == '/' || c == '\\'); + return (c == '/'); +} + +static unsigned char fold_ch(unsigned char c, int flags) { + if (flags & FNM_CASEFOLD) + return (unsigned char)tolower(c); + return c; +} + +static int ch_eq(char a, char b, int flags) { + return fold_ch((unsigned char)a, flags) == fold_ch((unsigned char)b, flags); +} + +static int is_leading_period(const char *s, const char *s0, int flags) { + if (!(flags & FNM_PERIOD)) + return 0; + if (*s != '.') + return 0; + if (s == s0) + return 1; + if ((flags & FNM_PATHNAME) && is_sep(s[-1], flags)) + return 1; + return 0; +} + +static int match_posix_class(const char *name, char test) { + unsigned char c = (unsigned char)test; + if (strcmp(name, "alnum") == 0) return isalnum(c); + if (strcmp(name, "alpha") == 0) return isalpha(c); + if (strcmp(name, "blank") == 0) return (c == ' ' || c == '\t'); + if (strcmp(name, "cntrl") == 0) return iscntrl(c); + if (strcmp(name, "digit") == 0) return isdigit(c); + if (strcmp(name, "graph") == 0) return isgraph(c); + if (strcmp(name, "lower") == 0) return islower(c); + if (strcmp(name, "print") == 0) return isprint(c); + if (strcmp(name, "punct") == 0) return ispunct(c); + if (strcmp(name, "space") == 0) return isspace(c); + if (strcmp(name, "upper") == 0) return isupper(c); + if (strcmp(name, "xdigit") == 0) return isxdigit(c); + return 0; +} + +/* + * Bracket expression matcher. + * Returns: + * 1 if matched + * 0 if not matched + * -1 if invalid (no closing ']') -> caller can treat '[' literally + * On success/nomatch, *newp is set to the position after the closing ']'. + */ +static int bracket_match(const char *p, char test, int flags, const char **newp) { + const char *pp = p + 1; + int negate = 0; + int ok = 0; + + if (*pp == '\0') { + if (newp) *newp = p; + return -1; + } + + if (*pp == '!' || *pp == '^') { + negate = 1; + pp++; + } + + /* ']' allowed first in class */ + if (*pp == ']') { + if (ch_eq(']', test, flags)) + ok = 1; + pp++; + } + + for (; *pp != '\0' && *pp != ']'; pp++) { + /* POSIX character class: [[:digit:]] etc */ + if (pp[0] == '[' && pp[1] == ':' ) { + const char *name_start = pp + 2; + const char *end = strstr(name_start, ":]"); + if (end) { + char name[32]; + size_t nlen = (size_t)(end - name_start); + if (nlen < sizeof(name)) { + memcpy(name, name_start, nlen); + name[nlen] = '\0'; + if (match_posix_class(name, test)) + ok = 1; + pp = end + 1; /* loop will ++pp => char after ']' of :]' */ + continue; + } + } + } + + char c1 = *pp; + + if (c1 == '\\' && !(flags & FNM_NOESCAPE) && pp[1] != '\0') { + pp++; + c1 = *pp; + } + + /* Range a-b */ + if (pp[1] == '-' && pp[2] != '\0' && pp[2] != ']') { + const char *pp2 = pp + 2; + char c2 = *pp2; + + if (c2 == '\\' && !(flags & FNM_NOESCAPE) && pp2[1] != '\0') { + pp2++; + c2 = *pp2; + } + + unsigned char a = fold_ch((unsigned char)c1, flags); + unsigned char b = fold_ch((unsigned char)c2, flags); + unsigned char t = fold_ch((unsigned char)test, flags); + if (a <= t && t <= b) + ok = 1; + + pp = pp2; /* loop will ++pp -> next char after range end */ + continue; + } + + if (ch_eq(c1, test, flags)) + ok = 1; + } + + if (*pp != ']') { + if (newp) *newp = p; + return -1; + } + + pp++; /* consume ']' */ + if (newp) *newp = pp; + + if (negate) + ok = !ok; + + return ok; +} + +static int match_here(const char *p, const char *s, const char *s0, int flags) { + for (;;) { + char pc = *p; + + switch (pc) { + case '\0': + if ((flags & FNM_LEADING_DIR) && is_sep(*s, flags)) + return 0; + return (*s == '\0') ? 0 : FNM_NOMATCH; + + case '?': + if (*s == '\0') + return FNM_NOMATCH; + if ((flags & FNM_PATHNAME) && is_sep(*s, flags)) + return FNM_NOMATCH; + if (is_leading_period(s, s0, flags)) + return FNM_NOMATCH; + p++; s++; + continue; + + case '*': { + if (is_leading_period(s, s0, flags)) + return FNM_NOMATCH; + + while (*p == '*') + p++; + + if (*p == '\0') { + if (flags & FNM_PATHNAME) { + if (flags & FNM_LEADING_DIR) + return 0; + /* must not consume a separator */ + for (const char *t = s; *t; t++) { + if (is_sep(*t, flags)) + return FNM_NOMATCH; + } + return 0; + } + return 0; + } + + for (const char *ss = s;; ss++) { + int r = match_here(p, ss, s0, flags); + if (r == 0) + return 0; + + if (*ss == '\0') + break; + if ((flags & FNM_PATHNAME) && is_sep(*ss, flags)) + break; + } + return FNM_NOMATCH; + } + + case '[': { + const char *np = NULL; + int br; + + if (*s == '\0') + return FNM_NOMATCH; + if ((flags & FNM_PATHNAME) && is_sep(*s, flags)) + return FNM_NOMATCH; + if (is_leading_period(s, s0, flags)) + return FNM_NOMATCH; + + br = bracket_match(p, *s, flags, &np); + if (br == -1) { + if (!ch_eq('[', *s, flags)) + return FNM_NOMATCH; + p++; s++; + continue; + } + if (br == 0) + return FNM_NOMATCH; + + p = np; + s++; + continue; + } + + case '\\': + if (!(flags & FNM_NOESCAPE) && p[1] != '\0') { + p++; + pc = *p; + } + /* fallthrough */ + default: + if (*s == '\0') + return FNM_NOMATCH; + + /* If PATHNAME, separators are special and must match separators */ + if ((flags & FNM_PATHNAME) && is_sep(*s, flags)) { + if (!is_sep(pc, flags)) + return FNM_NOMATCH; + /* Treat '/' and '\\' as equivalent when WINPATH is set */ + p++; s++; + continue; + } + + if (!ch_eq(pc, *s, flags)) + return FNM_NOMATCH; + p++; s++; + continue; + } + } +} + +int portable_fnmatch(const char *pattern, const char *string, int flags) { + if (!pattern || !string) + return FNM_NOMATCH; + return match_here(pattern, string, string, flags); +} + +/* + * Optional drop-in: if the platform lacks a system fnmatch(), export one. + * This is mainly for Windows/MinGW. + * + * Define PORTABLE_FNMATCH_FORCE_EXPORT_FNMATCH to export fnmatch() even if + * a system was found. + */ +#if !PORTABLE_FNMATCH_HAVE_SYSTEM_HEADER || defined(PORTABLE_FNMATCH_FORCE_EXPORT_FNMATCH) +int fnmatch(const char *pattern, const char *string, int flags) { + return portable_fnmatch(pattern, string, flags); +} +#endif + diff --git a/src/lib/port/portable_fnmatch.h b/src/lib/port/portable_fnmatch.h new file mode 100644 index 00000000..7c0714a9 --- /dev/null +++ b/src/lib/port/portable_fnmatch.h @@ -0,0 +1,72 @@ +#ifndef PORTABLE_FNMATCH_H +#define PORTABLE_FNMATCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * If a system exists, include it so we inherit the platform's + * flag bit assignments (they are NOT universal across libcs). + * + * On MinGW/Windows, this header typically does not exist, so we fall back + * to defining the common POSIX bits. + */ +#if !defined(PORTABLE_FNMATCH_NO_SYSTEM_HEADERS) +#if defined(__has_include) +#if __has_include() +#include +#define PORTABLE_FNMATCH_HAVE_SYSTEM_HEADER 1 +#else +#define PORTABLE_FNMATCH_HAVE_SYSTEM_HEADER 0 +#endif +#else +/* Conservative fallback: assume no system header */ +#define PORTABLE_FNMATCH_HAVE_SYSTEM_HEADER 0 +#endif +#else +#define PORTABLE_FNMATCH_HAVE_SYSTEM_HEADER 0 +#endif + +/* Return codes */ +#ifndef FNM_NOMATCH +#define FNM_NOMATCH 1 +#endif + +/* POSIX flags (define only if the platform header didn't) */ +#ifndef FNM_NOESCAPE +#define FNM_NOESCAPE 0x01 +#endif +#ifndef FNM_PATHNAME +#define FNM_PATHNAME 0x02 +#endif +#ifndef FNM_PERIOD +#define FNM_PERIOD 0x04 +#endif + +/* Common extensions (define if missing) */ +#ifndef FNM_LEADING_DIR +#define FNM_LEADING_DIR 0x08 +#endif +#ifndef FNM_CASEFOLD +#define FNM_CASEFOLD 0x10 +#endif + +/* Windows-friendly extension: treat '\\' as a path separator too */ +#ifndef FNM_WINPATH +#define FNM_WINPATH 0x20 +#endif + +/* Preferred API: doesn't collide with system fnmatch() */ +int portable_fnmatch(const char *pattern, const char *string, int flags); + +#if !PORTABLE_FNMATCH_HAVE_SYSTEM_HEADER || defined(PORTABLE_FNMATCH_FORCE_EXPORT_FNMATCH) +int fnmatch(const char *pattern, const char *string, int flags); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PORTABLE_FNMATCH_H */ + diff --git a/src/lib/port/test_fnmatch.c b/src/lib/port/test_fnmatch.c new file mode 100644 index 00000000..356b33f7 --- /dev/null +++ b/src/lib/port/test_fnmatch.c @@ -0,0 +1,238 @@ +#include +#include +#include + +#include "portable_fnmatch.h" + +/* If system fnmatch exists, include it for reference comparisons */ +#if defined(__unix__) || defined(__APPLE__) +#include +#define HAVE_SYSTEM_FNMATCH 1 +#else +#define HAVE_SYSTEM_FNMATCH 0 +#endif + +/* Compare portable to system fnmatch when available. + * You can force-disable with -DNO_SYSTEM_COMPARE. + */ +#if HAVE_SYSTEM_FNMATCH && !defined(NO_SYSTEM_COMPARE) +#define DO_SYSTEM_COMPARE 1 +#else +#define DO_SYSTEM_COMPARE 0 +#endif + +typedef struct { + const char *pat; + const char *str; + int flags; + int expect; /* 0 or FNM_NOMATCH */ + const char *desc; +} tc_t; + +static const tc_t cases[] = { + {"", "", 0, 0, "empty matches empty"}, + {"", "a", 0, FNM_NOMATCH, "empty does not match non-empty"}, + {"a", "a", 0, 0, "literal"}, + {"abc", "abc", 0, 0, "literal multi"}, + {"abc", "ab", 0, FNM_NOMATCH, "literal length"}, + + {"a?c", "abc", 0, 0, "? wildcard"}, + {"a?c", "ac", 0, FNM_NOMATCH, "? requires one char"}, + + {"a*c", "abbbc", 0, 0, "* wildcard"}, + {"a*c", "ac", 0, 0, "* can be empty"}, + {"*", "anything", 0, 0, "* matches all"}, + + {"a\\*c", "a*c", 0, 0, "escaped *"}, + {"a\\?c", "a?c", 0, 0, "escaped ?"}, + {"a\\*c", "abbbc", 0, FNM_NOMATCH, "escaped * is literal"}, + {"a\\*c", "a*c", FNM_NOESCAPE, FNM_NOMATCH, "noescape treats backslash literally"}, + + {"[ab]", "a", 0, 0, "bracket set"}, + {"[ab]", "c", 0, FNM_NOMATCH, "bracket set no"}, + {"[a-c]", "b", 0, 0, "bracket range"}, + {"[!a-c]", "z", 0, 0, "bracket negate"}, + {"[!a-c]", "b", 0, FNM_NOMATCH, "bracket negate no"}, + {"[]]", "]", 0, 0, "']' first in class"}, + {"[-a]", "-", 0, 0, "'-' literal at start"}, + {"[a-]", "-", 0, 0, "'-' literal at end"}, + + {"[[:digit:]]", "5", 0, 0, "posix class digit"}, + {"[[:alpha:]]", "Z", 0, 0, "posix class alpha"}, + {"[[:xdigit:]]", "f", 0, 0, "posix class xdigit"}, + {"[[:digit:]]", "x", 0, FNM_NOMATCH, "posix class digit no"}, + + {"ABC", "abc", FNM_CASEFOLD, 0, "casefold literal"}, + {"[A-C]", "b", FNM_CASEFOLD, 0, "casefold in range"}, + + {"*", ".bashrc", FNM_PERIOD, FNM_NOMATCH, "period blocks leading dot"}, + {".*", ".bashrc", FNM_PERIOD, 0, "period explicit dot"}, + {"?", ".x", FNM_PERIOD, FNM_NOMATCH, "period blocks leading dot with ?"}, + + {"a/*", "a/.b", FNM_PATHNAME | FNM_PERIOD, FNM_NOMATCH, "period blocks dot after /"}, + {"a/.*", "a/.b", FNM_PATHNAME | FNM_PERIOD, 0, "period explicit dot after /"}, + + {"a*b", "a/x/b", FNM_PATHNAME, FNM_NOMATCH, "pathname: * cannot cross /"}, + {"a*b", "a/x/b", 0, 0, "no pathname: * can cross /"}, + {"a?b", "a/b", FNM_PATHNAME, FNM_NOMATCH, "pathname: ? cannot match /"}, + {"a[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH, "pathname: bracket cannot match /"}, + + {"abc", "abc/def", FNM_PATHNAME | FNM_LEADING_DIR, 0, "leading_dir: literal prefix"}, + {"a*", "abcd/ef", FNM_PATHNAME | FNM_LEADING_DIR, 0, "leading_dir: star prefix"}, + {"a*", "abcd/ef", FNM_PATHNAME, FNM_NOMATCH, "no leading_dir: needs full match"}, + + /* WINPATH extension */ + {"a/b", "a\\b", FNM_PATHNAME | FNM_WINPATH, 0, "winpath: / matches \\\\ in string"}, + {"a/*", "a\\b", FNM_PATHNAME | FNM_WINPATH, 0, "winpath: separator then component"}, + {"a/*", "a\\b\\c", FNM_PATHNAME | FNM_WINPATH, FNM_NOMATCH, "winpath: * cannot cross \\\\ separator"}, + + /* invalid bracket treated literally */ + {"[", "[", 0, 0, "invalid bracket treated as literal"}, + }; + +static const char *flags_to_str(int f, char *buf, size_t n) { + buf[0] = 0; + int first = 1; +#define ADD(name, val) do { if (f & (val)) { if (!first) strncat(buf, "|", n-1); strncat(buf, name, n-1); first = 0; } } while(0) + ADD("NOESCAPE", FNM_NOESCAPE); + ADD("PATHNAME", FNM_PATHNAME); + ADD("PERIOD", FNM_PERIOD); + ADD("LEADING_DIR", FNM_LEADING_DIR); + ADD("CASEFOLD", FNM_CASEFOLD); + ADD("WINPATH", FNM_WINPATH); +#undef ADD + if (first) strncpy(buf, "0", n); + buf[n-1] = 0; + return buf; +} + +static void run_fixed_tests(void) { + int fails = 0; + int sys_mismatch = 0; + + for (size_t i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { + const tc_t *t = &cases[i]; + int r = portable_fnmatch(t->pat, t->str, t->flags); + int ok = (r == t->expect); + + if (!ok) { + char fb[128]; + fprintf(stderr, "FAIL[%zu]: %s\n pat='%s' str='%s' flags=%s\n got=%d expect=%d\n", + i, t->desc, t->pat, t->str, flags_to_str(t->flags, fb, sizeof(fb)), r, t->expect); + fails++; + } + +#if DO_SYSTEM_COMPARE + { + /* System fnmatch doesn't know WINPATH; mask it out for reference comparison */ + int sys_flags = t->flags & ~FNM_WINPATH; + + int sr = fnmatch(t->pat, t->str, sys_flags); + sr = (sr == 0) ? 0 : FNM_NOMATCH; + + int pr = (r == 0) ? 0 : FNM_NOMATCH; + + if (sr != pr) { + char fb[128]; + fprintf(stderr, "SYS MISMATCH[%zu]: %s\n pat='%s' str='%s' flags=%s\n portable=%d system=%d\n", + i, t->desc, t->pat, t->str, flags_to_str(t->flags, fb, sizeof(fb)), pr, sr); + sys_mismatch++; + } + } +#endif + } + + if (fails == 0) + printf("Fixed tests: PASS (%zu cases)\n", sizeof(cases)/sizeof(cases[0])); + else { + printf("Fixed tests: FAIL (%d/%zu failed)\n", fails, sizeof(cases)/sizeof(cases[0])); + exit(1); + } + +#if DO_SYSTEM_COMPARE + if (sys_mismatch == 0) + printf("System compare: OK (no mismatches on comparable cases)\n"); + else { + printf("System compare: %d mismatches (see stderr)\n", sys_mismatch); + exit(2); + } +#endif +} + +/* Simple fuzz: generate random patterns/strings (restricted alphabet) and compare to system fnmatch. + Only enabled when system compare is available. */ +static unsigned rng_u32(unsigned *st) { + *st = (*st * 1103515245u + 12345u); + return *st; +} + +static void rand_str(unsigned *st, char *out, size_t maxlen, int want_pattern) { + static const char alpha[] = "abcXYZ012-._/"; + static const char meta[] = "*?[]!\\"; + size_t len = (rng_u32(st) % (unsigned)maxlen); + + for (size_t i = 0; i < len; i++) { + unsigned r = rng_u32(st); + if (want_pattern && (r % 6 == 0)) + out[i] = meta[r % (sizeof(meta)-1)]; + else + out[i] = alpha[r % (sizeof(alpha)-1)]; + } + out[len] = '\0'; + + if (want_pattern && (rng_u32(st) % 5 == 0)) + strcat(out, "*"); +} + +static void run_fuzz(int iters) { +#if DO_SYSTEM_COMPARE + unsigned st = 0xC0FFEEu; + int mism = 0; + + for (int i = 0; i < iters; i++) { + char pat[64], str[64]; + rand_str(&st, pat, 20, 1); + rand_str(&st, str, 20, 0); + + int flags = 0; + if (rng_u32(&st) & 1) flags |= FNM_NOESCAPE; + if (rng_u32(&st) & 1) flags |= FNM_PATHNAME; + if (rng_u32(&st) & 1) flags |= FNM_PERIOD; + + int pr = portable_fnmatch(pat, str, flags); + int sr = fnmatch(pat, str, flags); + + sr = (sr == 0) ? 0 : FNM_NOMATCH; + pr = (pr == 0) ? 0 : FNM_NOMATCH; + + if (pr != sr) { + char fb[128]; + fprintf(stderr, "FUZZ MISMATCH[%d]: pat='%s' str='%s' flags=%s portable=%d system=%d\n", + i, pat, str, flags_to_str(flags, fb, sizeof(fb)), pr, sr); + mism++; + if (mism > 50) break; + } + } + + if (mism == 0) + printf("Fuzz: OK (%d iterations)\n", iters); + else { + printf("Fuzz: %d mismatches (see stderr)\n", mism); + exit(3); + } +#else + (void)iters; + printf("Fuzz: skipped (no system fnmatch available)\n"); +#endif +} + +int main(int argc, char **argv) { + run_fixed_tests(); + + if (argc == 3 && strcmp(argv[1], "--fuzz") == 0) { + int iters = atoi(argv[2]); + if (iters <= 0) iters = 10000; + run_fuzz(iters); + } + return 0; +} diff --git a/src/lib/port/usdr_helpers.c b/src/lib/port/usdr_helpers.c index 62a42c66..c99ac3a1 100644 --- a/src/lib/port/usdr_helpers.c +++ b/src/lib/port/usdr_helpers.c @@ -11,7 +11,7 @@ void* usdr_read_file(const char* filename, size_t* len) { char *buffer = NULL; - size_t length; + off_long_t length; FILE * f = fopen(filename, "rb"); if (!f) { int err = errno; @@ -23,15 +23,22 @@ void* usdr_read_file(const char* filename, size_t* len) } fseek(f, 0, SEEK_END); - length = ftell(f); + length = ftell_long(f); fseek(f, 0, SEEK_SET); + + if (length > INTPTR_MAX) { + fclose(f); + USDR_LOG("FILE", USDR_LOG_CRITICAL_WARNING, "File is too big!\n"); + return NULL; + } + buffer = (char*)malloc(length); if (buffer) { size_t rd = fread(buffer, 1, length, f); USDR_LOG("FILE", USDR_LOG_DEBUG, - "File %s was read %zu bytes\n", - filename, rd); + "File %s was read %llu bytes\n", + filename, (long long)rd); *len = rd; } fclose(f); diff --git a/src/lib/port/usdr_port.c b/src/lib/port/usdr_port.c index e69de29b..c4ffed1f 100644 --- a/src/lib/port/usdr_port.c +++ b/src/lib/port/usdr_port.c @@ -0,0 +1,253 @@ +#include "usdr_port.h" + +#ifdef _WIN32 +void __attribute__ ((constructor(220))) setup_winsocks(void) { + WSADATA wsaData; + int iResult; + + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + _exit(2); + } +} + +library_hdl_t usdr_lib_load(const char* s) +{ + return LoadLibraryA(s); +} + +void usdr_lib_close(library_hdl_t h) +{ + CloseHandle(h); +} + +void* usdr_lib_sym(library_hdl_t h, const char* proc) +{ + return GetProcAddress(h, proc); +} + + +int vasprintf(char **strp, const char *fmt, va_list ap) +{ + int len = _vscprintf(fmt, ap); + if (len == -1) { + return -1; + } + + size_t size = (size_t)len + 1; + char *str = malloc(size); + if (!str) { + return -1; + } + + int r = _vsprintf_s_l(str, len + 1, fmt, NULL, ap); + if (r == -1) { + free(str); + return -1; + } + + *strp = str; + return r; +} + +int asprintf(char **strp, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vasprintf(strp, fmt, ap); + va_end(ap); + return r; +} + + +#else + +library_hdl_t usdr_lib_load(const char* s) +{ + int mode = RTLD_LAZY | RTLD_GLOBAL; +#ifdef RTLD_DEEPBIND + mode |= RTLD_DEEPBIND; +#endif + return dlopen(s, mode); +} + +void usdr_lib_close(library_hdl_t h) +{ + dlclose(h); +} + +void* usdr_lib_sym(library_hdl_t h, const char* proc) +{ + return dlsym(h, proc); +} + +#endif + + +#ifdef __APPLE__ +#include +#include +#include +#include +#include + +/** + * Cross-platform semaphore implementation using Mach semaphores on macOS + */ + +int usdr_sem_init(usdr_sem_t *sem, int pshared, unsigned int value) { + (void)pshared; // Mach semaphores don't support pshared + kern_return_t kr = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, (int)value); + if (kr != KERN_SUCCESS) { + errno = ENOMEM; + return -1; + } + return 0; +} + +int usdr_sem_destroy(usdr_sem_t *sem) { + kern_return_t kr = semaphore_destroy(mach_task_self(), *sem); + if (kr != KERN_SUCCESS) { + errno = EINVAL; + return -1; + } + return 0; +} + +int usdr_sem_post(usdr_sem_t *sem) { + kern_return_t kr = semaphore_signal(*sem); + if (kr != KERN_SUCCESS) { + errno = EINVAL; + return -1; + } + return 0; +} + +int usdr_sem_wait(usdr_sem_t *sem) { + kern_return_t kr; + do { + kr = semaphore_wait(*sem); + } while (kr == KERN_ABORTED); // Handle spurious wakeups + + if (kr != KERN_SUCCESS) { + errno = EINVAL; + return -1; + } + return 0; +} + +int usdr_sem_trywait(usdr_sem_t *sem) { + mach_timespec_t zero_timeout = {0, 0}; + kern_return_t kr = semaphore_timedwait(*sem, zero_timeout); + + if (kr == KERN_SUCCESS) { + return 0; + } else if (kr == KERN_OPERATION_TIMED_OUT) { + errno = EAGAIN; + return -1; + } else { + errno = EINVAL; + return -1; + } +} + +int usdr_sem_timedwait(usdr_sem_t *sem, const struct timespec *abs_timeout) { + // 1. Get current wall-clock time + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + + // 2. Calculate relative timeout + int64_t rel_sec = abs_timeout->tv_sec - now.tv_sec; + int64_t rel_nsec = abs_timeout->tv_nsec - now.tv_nsec; + + // Normalize nanoseconds + if (rel_nsec < 0) { + rel_sec--; + rel_nsec += 1000000000LL; + } + + if (rel_sec < 0) { + // Time has already passed, try a non-blocking wait + return usdr_sem_trywait(sem); + } + + // 3. Convert to mach_timespec_t (relative timeout) + mach_timespec_t wait_time; + wait_time.tv_sec = (unsigned int)rel_sec; + wait_time.tv_nsec = (clock_res_t)rel_nsec; + + // 4. Perform the wait + kern_return_t kr; + do { + kr = semaphore_timedwait(*sem, wait_time); + } while (kr == KERN_ABORTED); // Handle spurious wakeups + + if (kr == KERN_SUCCESS) { + return 0; + } else if (kr == KERN_OPERATION_TIMED_OUT) { + errno = ETIMEDOUT; + return -1; + } else { + errno = EINVAL; + return -1; + } +} + +#else +/* Non-Apple: use POSIX semaphores */ +#include +#include +#include + +int usdr_sem_init(usdr_sem_t *sem, int pshared, unsigned int value) { + return sem_init(sem, pshared, value); +} + +int usdr_sem_destroy(usdr_sem_t *sem) { + return sem_destroy(sem); +} + +int usdr_sem_post(usdr_sem_t *sem) { + return sem_post(sem); +} + +int usdr_sem_wait(usdr_sem_t *sem) { + return sem_wait(sem); +} + +int usdr_sem_trywait(usdr_sem_t *sem) { + return sem_trywait(sem); +} + +int usdr_sem_timedwait(usdr_sem_t *sem, const struct timespec *abs_timeout) { + return sem_timedwait(sem, abs_timeout); +} + +#endif + +/** + * Cross-platform thread naming implementation + */ +int usdr_set_thread_name(const char* name) { +#ifdef _WIN32 + // Windows: SetThreadDescription requires Windows 10 1607+ + // For older versions, we'd need to use the SEH exception trick + // For now, just return success without doing anything + (void)name; + return 0; +#elif defined(__APPLE__) + // macOS: pthread_setname_np takes only the name, sets current thread + return pthread_setname_np(name); +#elif defined(__linux__) + // Linux: pthread_setname_np takes thread and name + return pthread_setname_np(pthread_self(), name); +#else + // Unknown platform + (void)name; + return 0; +#endif +} + + + diff --git a/src/lib/port/usdr_port.h b/src/lib/port/usdr_port.h index d977360d..3cc29d06 100644 --- a/src/lib/port/usdr_port.h +++ b/src/lib/port/usdr_port.h @@ -3,16 +3,72 @@ #ifndef USDR_PORT_H #define USDR_PORT_H + +#define _FILE_OFFSET_BITS 64 + +#ifdef _WIN32 +#include +#include +#else +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#endif + #include #include #include #include #include #include + +#ifdef _WIN32 +#define htobe32(x) htonl(x) +#define be32toh(x) ntohl(x) +#elif defined(__APPLE__) +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define htobe16(x) OSSwapHostToBigInt16(x) +#define htole16(x) OSSwapHostToLittleInt16(x) +#define be16toh(x) OSSwapBigToHostInt16(x) +#define le16toh(x) OSSwapLittleToHostInt16(x) + +#define htobe32(x) OSSwapHostToBigInt32(x) +#define htole32(x) OSSwapHostToLittleInt32(x) +#define be32toh(x) OSSwapBigToHostInt32(x) +#define le32toh(x) OSSwapLittleToHostInt32(x) + +#define htobe64(x) OSSwapHostToBigInt64(x) +#define htole64(x) OSSwapHostToLittleInt64(x) +#define be64toh(x) OSSwapBigToHostInt64(x) +#define le64toh(x) OSSwapLittleToHostInt64(x) + + +#else #include +#include +#include + +#include +#endif #include #include +#include +#include "portable_fnmatch.h" + +#ifdef __cplusplus +extern "C" { +#endif #define PORT_THREAD __thread @@ -34,4 +90,100 @@ #define SAFE_STRCPY(dst, src) ({strncpy((dst), (src), sizeof(dst) - 1); dst[SIZEOF_ARRAY(dst) - 1] = 0; }) +#ifdef _WIN32 +static inline int usdr_alignalloc(void **memptr, size_t alignment, size_t size) { + *memptr = _aligned_malloc(size, alignment); + return (*memptr) ? 0 : ENOMEM; +} +static inline void usdr_alignfree(void* ptr) { + _aligned_free(ptr); +} + +#define localtime_r(T,Tm) (localtime_s(Tm,T) ? NULL : Tm) + +static inline int gettid(void) +{ + return GetCurrentThreadId(); +} + +#else +static inline int usdr_alignalloc(void **memptr, size_t alignment, size_t size) { + return posix_memalign(memptr, alignment, size); +} +static inline void usdr_alignfree(void* ptr) { + free(ptr); +} +#endif + +#ifdef _WIN32 +typedef HANDLE library_hdl_t; +typedef off64_t off_long_t; +#define ftell_long ftello64 +#else +typedef void* library_hdl_t; +typedef off_t off_long_t; +#define ftell_long ftello +#endif + +library_hdl_t usdr_lib_load(const char* s); +void usdr_lib_close(library_hdl_t h); +void* usdr_lib_sym(library_hdl_t h, const char* proc); + +#ifdef _WIN32 +int vasprintf(char **strp, const char *fmt, va_list ap) __attribute__ ((format (printf, 2, 0))); +int asprintf(char **strp, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +#endif + +/** + * Cross-platform semaphore abstraction + * Uses Mach semaphores on macOS, POSIX semaphores elsewhere + */ +#ifdef __APPLE__ +typedef semaphore_t usdr_sem_t; +#else +#include +typedef sem_t usdr_sem_t; +#endif + +int usdr_sem_init(usdr_sem_t *sem, int pshared, unsigned int value); +int usdr_sem_destroy(usdr_sem_t *sem); +int usdr_sem_post(usdr_sem_t *sem); +int usdr_sem_wait(usdr_sem_t *sem); +int usdr_sem_trywait(usdr_sem_t *sem); +int usdr_sem_timedwait(usdr_sem_t *sem, const struct timespec *abs_timeout); + +/** + * Cross-platform thread naming + * Sets the name of the current thread for debugging purposes + * @param name Thread name (max 15 characters on Linux, 63 on macOS) + * @return 0 on success, -1 on error + */ +int usdr_set_thread_name(const char* name); + +/** + * sincosf - compute sine and cosine simultaneously + * Available natively on Linux (GNU extension), needs implementation on macOS/Windows + */ +#ifdef __APPLE__ +#include + +// Use straightforward implementation on macOS +// Note: __sincosf is a private Apple symbol and may cause linker issues +static inline void sincosf(float x, float *sin_val, float *cos_val) { + *sin_val = sinf(x); + *cos_val = cosf(x); +} +#endif + +#define CACHE_SIZE 64 + +#if defined(_WIN32) || defined(__APPLE__) +#define ENAVAIL ENOENT +#endif + +#ifdef __cplusplus +}; +#endif + #endif diff --git a/src/lib/webusb/test_webusb_libusb.c b/src/lib/webusb/test_webusb_libusb.c index a3f2eb5b..5aef74fb 100644 --- a/src/lib/webusb/test_webusb_libusb.c +++ b/src/lib/webusb/test_webusb_libusb.c @@ -296,7 +296,7 @@ int main(int argc, char** argv) return 1; } - // Recieve test packet + // Receive test packet res = libusb_bulk_transfer(wsdr->dh, 0x03 | LIBUSB_ENDPOINT_IN, (unsigned char*)dummy_buffer, diff --git a/src/lib/xdsp/CMakeLists.txt b/src/lib/xdsp/CMakeLists.txt index 3c4608c1..de8f84e2 100644 --- a/src/lib/xdsp/CMakeLists.txt +++ b/src/lib/xdsp/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2024 Wavelet Lab +# Copyright (c) 2023-2026 Wavelet Lab # SPDX-License-Identifier: MIT # Populate a CMake variable with the sources @@ -42,6 +42,14 @@ set(xdsplib_conv_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/conv_ci12_4ci16_2.c ${CMAKE_CURRENT_SOURCE_DIR}/conv_2ci16_ci12_2.c ${CMAKE_CURRENT_SOURCE_DIR}/conv_4ci16_ci12_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_ci16_6ci16_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_ci16_6cf32_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_ci16_8cf32_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_ci16_8ci16_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_ci16_3ci16_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_ci16_3cf32_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_3cf32_ci16_2.c + ${CMAKE_CURRENT_SOURCE_DIR}/conv_3ci16_ci16_2.c ) if(WVLT_ARCH_X86 OR WVLT_ARCH_X86_64) @@ -78,10 +86,16 @@ else() #add_definitions("-mfpu=neon") endif() - add_library(usdr-dsp ${xdsplib_SRCS}) + add_library(usdr-dsp SHARED ${xdsplib_SRCS}) target_link_libraries(usdr-dsp m pthread) target_compile_options(usdr-dsp PRIVATE "-Wall" "-Werror=implicit-function-declaration") - install(TARGETS usdr-dsp LIBRARY) + install(TARGETS usdr-dsp EXPORT usdrTargets LIBRARY + DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + # Prevent exporting build-tree include paths for the dsp target + set_target_properties(usdr-dsp PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "$;$" + ) set_target_properties(usdr-dsp PROPERTIES SOVERSION ${USDR_ABI_VERSION}) set_target_properties(usdr-dsp PROPERTIES VERSION ${USDR_LIBVER}) diff --git a/src/lib/xdsp/conv.c b/src/lib/xdsp/conv.c index c92c915e..74a2c5ee 100644 --- a/src/lib/xdsp/conv.c +++ b/src/lib/xdsp/conv.c @@ -28,6 +28,19 @@ #include "conv_2ci16_ci12_2.h" #include "conv_4ci16_ci12_2.h" +#include "conv_ci16_6ci16_2.h" +#include "conv_ci16_6cf32_2.h" + +#include "conv_ci16_8cf32_2.h" +#include "conv_ci16_8ci16_2.h" + +#include "conv_ci16_3cf32_2.h" +#include "conv_ci16_3ci16_2.h" + +#include "conv_3cf32_ci16_2.h" +#include "conv_3ci16_ci16_2.h" + + #include #include @@ -125,6 +138,37 @@ transform_info_t get_transform_fn(const char* from, unsigned inveccnt, unsigned outveccnt) { + if(inveccnt == 1 && outveccnt == 8) + { + if(isCI16(from) && isCF32(to)) + { + transform_info_t l_conv_ci16_8cf32 = { conv_get_ci16_8cf32(), tr_conv_i16_f32_sz }; + return l_conv_ci16_8cf32; + } + + if(isCI16(from) && isCI16(to)) + { + transform_info_t l_conv_ci16_8ci16 = { conv_get_ci16_8ci16(), tr_dummy_sz }; + return l_conv_ci16_8ci16; + } + } + + /* Interleave 1 -> 6 */ + if(inveccnt == 1 && outveccnt == 6) + { + if(isCI16(from) && isCI16(to)) + { + transform_info_t l_conv_ci16_6ci16 = { conv_get_ci16_6ci16(), tr_dummy_sz }; + return l_conv_ci16_6ci16; + } + + if(isCI16(from) && isCF32(to)) + { + transform_info_t l_conv_ci16_6cf32 = { conv_get_ci16_6cf32(), tr_conv_i16_f32_sz }; + return l_conv_ci16_6cf32; + } + } + /* Deinterleave 4 -> 1 */ if(inveccnt == 4 && outveccnt == 1) { @@ -211,6 +255,35 @@ transform_info_t get_transform_fn(const char* from, } } + /* Deinterleave 3 -> 1 */ + if(inveccnt == 3 && outveccnt == 1) + { + if (isCF32(from) && isCI16(to)) { + transform_info_t l_conv_3cf32_ci16 = { conv_get_3cf32_ci16(), tr_conv_f32_i16_sz }; + return l_conv_3cf32_ci16; + } + + if (isCI16(from) && isCI16(to)) { + transform_info_t l_conv_3ci16_ci16 = { conv_get_3ci16_ci16(), tr_dummy_sz }; + return l_conv_3ci16_ci16; + } + + } + + /* Interleave 1 -> 3 */ + if(inveccnt == 1 && outveccnt == 3) + { + if (isCI16(from) && isCI16(to)) { + transform_info_t l_conv_ci16_3ci16 = { conv_get_ci16_3ci16(), tr_dummy_sz }; + return l_conv_ci16_3ci16; + } + + if (isCI16(from) && isCF32(to)) { + transform_info_t l_conv_ci16_3f32 = { conv_get_ci16_3cf32(), tr_conv_i16_f32_sz }; + return l_conv_ci16_3f32; + } + } + /* Interleave 1 -> 2 */ if(inveccnt == 1 && outveccnt == 2) { diff --git a/src/lib/xdsp/conv.h b/src/lib/xdsp/conv.h index 4bfdcc06..e25e2c7d 100644 --- a/src/lib/xdsp/conv.h +++ b/src/lib/xdsp/conv.h @@ -6,6 +6,7 @@ #include #include +#include "usdr_port.h" #include "vbase.h" #define I16RND(x) (int16_t)(x) @@ -53,6 +54,13 @@ typedef void (*filter_function_t)(const int16_t *__restrict data, unsigned outdatabsz) \ { conv_fn(*indata, indatabsz, outdata[0], outdata[1], outdatabsz); } +#define DECLARE_TR_FUNC_1_3(conv_fn) \ +void tr_##conv_fn (const void *__restrict *__restrict indata, \ + unsigned indatabsz, \ + void *__restrict *__restrict outdata, \ + unsigned outdatabsz) \ +{ conv_fn(*indata, indatabsz, outdata[0], outdata[1], outdata[2], outdatabsz); } + #define DECLARE_TR_FUNC_1_4(conv_fn) \ void tr_##conv_fn (const void *__restrict *__restrict indata, \ unsigned indatabsz, \ @@ -60,6 +68,13 @@ typedef void (*filter_function_t)(const int16_t *__restrict data, unsigned outdatabsz) \ { conv_fn(*indata, indatabsz, outdata[0], outdata[1], outdata[2], outdata[3], outdatabsz); } +#define DECLARE_TR_FUNC_1_8(conv_fn) \ +void tr_##conv_fn (const void *__restrict *__restrict indata, \ + unsigned indatabsz, \ + void *__restrict *__restrict outdata, \ + unsigned outdatabsz) \ +{ conv_fn(*indata, indatabsz, outdata[0], outdata[1], outdata[2], outdata[3], outdata[4], outdata[5], outdata[6], outdata[7], outdatabsz); } + #define DECLARE_TR_FUNC_2_1(conv_fn) \ void tr_##conv_fn (const void *__restrict *__restrict indata, \ unsigned indatabsz, \ @@ -67,6 +82,13 @@ typedef void (*filter_function_t)(const int16_t *__restrict data, unsigned outdatabsz) \ { conv_fn(indata[0], indata[1], indatabsz, outdata[0], outdatabsz); } +#define DECLARE_TR_FUNC_3_1(conv_fn) \ +void tr_##conv_fn (const void *__restrict *__restrict indata, \ + unsigned indatabsz, \ + void *__restrict *__restrict outdata, \ + unsigned outdatabsz) \ +{ conv_fn(indata[0], indata[1], indata[2], indatabsz, outdata[0], outdatabsz); } + #define DECLARE_TR_FUNC_4_1(conv_fn) \ void tr_##conv_fn (const void *__restrict *__restrict indata, \ unsigned indatabsz, \ @@ -74,6 +96,13 @@ typedef void (*filter_function_t)(const int16_t *__restrict data, unsigned outdatabsz) \ { conv_fn(indata[0], indata[1], indata[2], indata[3], indatabsz, outdata[0], outdatabsz); } +#define DECLARE_TR_FUNC_1_6(conv_fn) \ + void tr_##conv_fn (const void *__restrict *__restrict indata, \ + unsigned indatabsz, \ + void *__restrict *__restrict outdata, \ + unsigned outdatabsz) \ + { conv_fn(*indata, indatabsz, outdata[0], outdata[1], outdata[2], outdata[3], outdata[4], outdata[5], outdatabsz); } + typedef void (*sincos_i16_interleaved_ctrl_function_t)(int32_t *__restrict start_phase, int32_t delta_phase, int16_t gain, bool inv_sin, bool inv_cos, @@ -90,6 +119,23 @@ void tr_##conv_fn (int32_t *__restrict start_phase, \ unsigned iters) \ { conv_fn(start_phase, delta_phase, gain, inv_sin, inv_cos, outdata, iters); } +typedef void (*sincos_i16_interleaved_chirp_function_t)(int32_t *__restrict start_phase, int32_t *__restrict start_delta_phase, + int32_t *__restrict delta_phase, int32_t chip_steps, int16_t gain, bool inv_sin, bool inv_cos, + int16_t *__restrict outdata, + unsigned iters); + +#define DECLARE_TR_FUNC_SINCOS_I16_INTERLEAVED_CHIRP(conv_fn) \ +void tr_##conv_fn (int32_t *__restrict start_phase, \ + int32_t *__restrict start_delta_phase, \ + int32_t *__restrict delta_phase, \ + int32_t chip_steps, \ + int16_t gain, \ + bool inv_sin, \ + bool inv_cos, \ + int16_t *__restrict outdata, \ + unsigned iters) \ +{ conv_fn(start_phase, start_delta_phase, delta_phase, chip_steps, gain, inv_sin, inv_cos, outdata, iters); } + struct transform_info { conv_function_t cfunc; @@ -122,6 +168,7 @@ struct fft_accumulate_data { typedef struct fft_accumulate_data fft_acc_t; typedef float wvlt_fftwf_complex[2]; +typedef int16_t wvlt_fftwi16_complex[2]; typedef void (*fftad_init_function_t) (fft_acc_t* __restrict p, unsigned fftsz); @@ -237,4 +284,12 @@ void tr_##conv_fn (wvlt_fftwf_complex* __restrict in, unsigned fftsz, float* __r wvlt_fftwf_complex* __restrict out) \ { conv_fn(in, fftsz, wnd, out); } +typedef void (*fft_window_ci16_cf32_function_t) + (wvlt_fftwi16_complex* __restrict in, unsigned fftsz, float* __restrict wnd, wvlt_fftwf_complex* __restrict out); + +#define DECLARE_TR_FUNC_FFT_WINDOW_CI16_CF32(conv_fn) \ +void tr_##conv_fn (wvlt_fftwi16_complex* __restrict in, unsigned fftsz, float* __restrict wnd, \ + wvlt_fftwf_complex* __restrict out) \ +{ conv_fn(in, fftsz, wnd, out); } + #endif diff --git a/src/lib/xdsp/conv_2cf32_ci12_2.c b/src/lib/xdsp/conv_2cf32_ci12_2.c index 75f1f48c..ec15fa56 100644 --- a/src/lib/xdsp/conv_2cf32_ci12_2.c +++ b/src/lib/xdsp/conv_2cf32_ci12_2.c @@ -11,6 +11,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_2cf32_ci12_generic.t" DECLARE_TR_FUNC_2_1(conv_2cf32_ci12_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_2cf32_ci12_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_2cf32_ci12_avx512bw.t" +DECLARE_TR_FUNC_2_1(conv_2cf32_ci12_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_2cf32_ci12_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -32,6 +39,7 @@ conv_function_t conv_get_2cf32_ci12_c(generic_opts_t cpu_cap, const char** sfunc SELECT_GENERIC_FN(fn, fname, tr_conv_2cf32_ci12_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_2cf32_ci12_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_2cf32_ci12_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_2cf32_ci12_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_2ci16_ci12_2.c b/src/lib/xdsp/conv_2ci16_ci12_2.c index c358452d..1609d4b3 100644 --- a/src/lib/xdsp/conv_2ci16_ci12_2.c +++ b/src/lib/xdsp/conv_2ci16_ci12_2.c @@ -9,6 +9,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_2ci16_ci12_generic.t" DECLARE_TR_FUNC_2_1(conv_2ci16_ci12_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_2ci16_ci12_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_2ci16_ci12_avx512bw.t" +DECLARE_TR_FUNC_2_1(conv_2ci16_ci12_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_2ci16_ci12_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -30,6 +37,7 @@ conv_function_t conv_get_2ci16_ci12_c(generic_opts_t cpu_cap, const char** sfunc SELECT_GENERIC_FN(fn, fname, tr_conv_2ci16_ci12_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_2ci16_ci12_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_2ci16_ci12_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_2ci16_ci12_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_3cf32_ci16_2.c b/src/lib/xdsp/conv_3cf32_ci16_2.c new file mode 100644 index 00000000..4ca30e51 --- /dev/null +++ b/src/lib/xdsp/conv_3cf32_ci16_2.c @@ -0,0 +1,36 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_3cf32_ci16_2.h" +#include "attribute_switch.h" + +#define CONV_SCALE (1.0f/32767) + +#define TEMPLATE_FUNC_NAME conv_3cf32_ci16_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_3cf32_ci16_generic.t" +DECLARE_TR_FUNC_3_1(conv_3cf32_ci16_generic) + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_3cf32_ci16_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_3cf32_ci16_avx2.t" +DECLARE_TR_FUNC_3_1(conv_3cf32_ci16_avx2) +#endif + +conv_function_t conv_get_3cf32_ci16_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_3cf32_ci16_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_3cf32_ci16_avx2, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_3cf32_ci16() +{ + return conv_get_3cf32_ci16_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_3cf32_ci16_2.h b/src/lib/xdsp/conv_3cf32_ci16_2.h new file mode 100644 index 00000000..b1b61e05 --- /dev/null +++ b/src/lib/xdsp/conv_3cf32_ci16_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_3CF32_CI16_H +#define CONV_3CF32_CI16_H + +#include "conv.h" + +conv_function_t conv_get_3cf32_ci16(); +conv_function_t conv_get_3cf32_ci16_c(generic_opts_t cpu_cap, const char **sfunc); + +#endif diff --git a/src/lib/xdsp/conv_3ci16_ci16_2.c b/src/lib/xdsp/conv_3ci16_ci16_2.c new file mode 100644 index 00000000..b12ffc5c --- /dev/null +++ b/src/lib/xdsp/conv_3ci16_ci16_2.c @@ -0,0 +1,34 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_3ci16_ci16_2.h" +#include "attribute_switch.h" + +#define TEMPLATE_FUNC_NAME conv_3ci16_ci16_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_3ci16_ci16_generic.t" +DECLARE_TR_FUNC_3_1(conv_3ci16_ci16_generic) + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_3ci16_ci16_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_3ci16_ci16_avx2.t" +DECLARE_TR_FUNC_3_1(conv_3ci16_ci16_avx2) +#endif + +conv_function_t conv_get_3ci16_ci16_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_3ci16_ci16_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_3ci16_ci16_avx2, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_3ci16_ci16() +{ + return conv_get_3ci16_ci16_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_3ci16_ci16_2.h b/src/lib/xdsp/conv_3ci16_ci16_2.h new file mode 100644 index 00000000..d72baad4 --- /dev/null +++ b/src/lib/xdsp/conv_3ci16_ci16_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_3CI16_CI16_H +#define CONV_3CI16_CI16_H + +#include "conv.h" + +conv_function_t conv_get_3ci16_ci16(); +conv_function_t conv_get_3ci16_ci16_c(generic_opts_t cpu_cap, const char **sfunc); + +#endif diff --git a/src/lib/xdsp/conv_4cf32_ci12_2.c b/src/lib/xdsp/conv_4cf32_ci12_2.c index 9d141c35..b4c3ec3e 100644 --- a/src/lib/xdsp/conv_4cf32_ci12_2.c +++ b/src/lib/xdsp/conv_4cf32_ci12_2.c @@ -11,6 +11,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_4cf32_ci12_generic.t" DECLARE_TR_FUNC_4_1(conv_4cf32_ci12_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_4cf32_ci12_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_4cf32_ci12_avx512bw.t" +DECLARE_TR_FUNC_4_1(conv_4cf32_ci12_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_4cf32_ci12_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -32,6 +39,7 @@ conv_function_t conv_get_4cf32_ci12_c(generic_opts_t cpu_cap, const char** sfunc SELECT_GENERIC_FN(fn, fname, tr_conv_4cf32_ci12_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_4cf32_ci12_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_4cf32_ci12_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_4cf32_ci12_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_4ci16_ci12_2.c b/src/lib/xdsp/conv_4ci16_ci12_2.c index 26e69ccc..18e32f94 100644 --- a/src/lib/xdsp/conv_4ci16_ci12_2.c +++ b/src/lib/xdsp/conv_4ci16_ci12_2.c @@ -9,6 +9,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_4ci16_ci12_generic.t" DECLARE_TR_FUNC_4_1(conv_4ci16_ci12_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_4ci16_ci12_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_4ci16_ci12_avx512bw.t" +DECLARE_TR_FUNC_4_1(conv_4ci16_ci12_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_4ci16_ci12_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -30,6 +37,7 @@ conv_function_t conv_get_4ci16_ci12_c(generic_opts_t cpu_cap, const char** sfunc SELECT_GENERIC_FN(fn, fname, tr_conv_4ci16_ci12_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_4ci16_ci12_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_4ci16_ci12_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_4ci16_ci12_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_ci12_2cf32_2.c b/src/lib/xdsp/conv_ci12_2cf32_2.c index 49f21f87..8bf8898e 100644 --- a/src/lib/xdsp/conv_ci12_2cf32_2.c +++ b/src/lib/xdsp/conv_ci12_2cf32_2.c @@ -23,11 +23,11 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_ci12_2cf32_generic.t" DECLARE_TR_FUNC_1_2(conv_ci12_2cf32_generic) -#ifdef WVLT_SSSE3 -#define TEMPLATE_FUNC_NAME conv_ci12_2cf32_ssse3 -VWLT_ATTRIBUTE(optimize("-O3"), target("ssse3")) -#include "templates/conv_ci12_2cf32_ssse3.t" -DECLARE_TR_FUNC_1_2(conv_ci12_2cf32_ssse3) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_ci12_2cf32_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw,avx512vl")) +#include "templates/conv_ci12_2cf32_avx512bw.t" +DECLARE_TR_FUNC_1_2(conv_ci12_2cf32_avx512bw) #endif #ifdef WVLT_AVX2 @@ -50,8 +50,8 @@ conv_function_t conv_get_ci12_2cf32_c(generic_opts_t cpu_cap, const char** sfunc conv_function_t fn; SELECT_GENERIC_FN(fn, fname, tr_conv_ci12_2cf32_generic, cpu_cap); - SELECT_SSSE3_FN(fn, fname, tr_conv_ci12_2cf32_ssse3, cpu_cap); SELECT_AVX_FN(fn, fname, tr_conv_ci12_2cf32_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_ci12_2cf32_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_ci12_2cf32_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_ci12_2ci16_2.c b/src/lib/xdsp/conv_ci12_2ci16_2.c index e0102066..078590c2 100644 --- a/src/lib/xdsp/conv_ci12_2ci16_2.c +++ b/src/lib/xdsp/conv_ci12_2ci16_2.c @@ -10,6 +10,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_ci12_2ci16_generic.t" DECLARE_TR_FUNC_1_2(conv_ci12_2ci16_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_ci12_2ci16_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw,avx512vl")) +#include "templates/conv_ci12_2ci16_avx512bw.t" +DECLARE_TR_FUNC_1_2(conv_ci12_2ci16_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_ci12_2ci16_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -31,6 +38,7 @@ conv_function_t conv_get_ci12_2ci16_c(generic_opts_t cpu_cap, const char** sfunc SELECT_GENERIC_FN(fn, fname, tr_conv_ci12_2ci16_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_ci12_2ci16_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_ci12_2ci16_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_ci12_2ci16_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_ci12_4cf32_2.c b/src/lib/xdsp/conv_ci12_4cf32_2.c index 243aa31c..bc85ee10 100644 --- a/src/lib/xdsp/conv_ci12_4cf32_2.c +++ b/src/lib/xdsp/conv_ci12_4cf32_2.c @@ -5,12 +5,20 @@ #include "attribute_switch.h" #define CONV_SCALE (1.0f/32767) +#define SCALE2 (CONV_SCALE / 65536) #define TEMPLATE_FUNC_NAME conv_ci12_4cf32_generic VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_ci12_4cf32_generic.t" DECLARE_TR_FUNC_1_4(conv_ci12_4cf32_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_ci12_4cf32_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_ci12_4cf32_avx512bw.t" +DECLARE_TR_FUNC_1_4(conv_ci12_4cf32_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_ci12_4cf32_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -32,6 +40,7 @@ conv_function_t conv_get_ci12_4cf32_c(generic_opts_t cpu_cap, const char** sfunc SELECT_GENERIC_FN(fn, fname, tr_conv_ci12_4cf32_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_ci12_4cf32_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_ci12_4cf32_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_ci12_4cf32_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_ci12_4ci16_2.c b/src/lib/xdsp/conv_ci12_4ci16_2.c index 3acb37f3..4f8a3361 100644 --- a/src/lib/xdsp/conv_ci12_4ci16_2.c +++ b/src/lib/xdsp/conv_ci12_4ci16_2.c @@ -9,6 +9,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_ci12_4ci16_generic.t" DECLARE_TR_FUNC_1_4(conv_ci12_4ci16_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_ci12_4ci16_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw,avx512vl")) +#include "templates/conv_ci12_4ci16_avx512bw.t" +DECLARE_TR_FUNC_1_4(conv_ci12_4ci16_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_ci12_4ci16_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -30,6 +37,7 @@ conv_function_t conv_get_ci12_4ci16_c(generic_opts_t cpu_cap, const char** sfunc SELECT_GENERIC_FN(fn, fname, tr_conv_ci12_4ci16_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_ci12_4ci16_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_ci12_4ci16_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_ci12_4ci16_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_ci16_3cf32_2.c b/src/lib/xdsp/conv_ci16_3cf32_2.c new file mode 100644 index 00000000..a803e35e --- /dev/null +++ b/src/lib/xdsp/conv_ci16_3cf32_2.c @@ -0,0 +1,36 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_ci16_3cf32_2.h" +#include "attribute_switch.h" + +#define CONV_SCALE (1.0f/32767) + +#define TEMPLATE_FUNC_NAME conv_ci16_3cf32_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_3cf32_generic.t" +DECLARE_TR_FUNC_1_3(conv_ci16_3cf32_generic) + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_ci16_3cf32_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_ci16_3cf32_avx2.t" +DECLARE_TR_FUNC_1_3(conv_ci16_3cf32_avx2) +#endif + +conv_function_t conv_get_ci16_3cf32_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_ci16_3cf32_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_ci16_3cf32_avx2, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_ci16_3cf32() +{ + return conv_get_ci16_3cf32_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_ci16_3cf32_2.h b/src/lib/xdsp/conv_ci16_3cf32_2.h new file mode 100644 index 00000000..1d84f35b --- /dev/null +++ b/src/lib/xdsp/conv_ci16_3cf32_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_CI16_3CF32_H +#define CONV_CI16_3CF32_H + +#include "conv.h" + +conv_function_t conv_get_ci16_3cf32(); +conv_function_t conv_get_ci16_3cf32_c(generic_opts_t cpu_cap, const char **sfunc); + +#endif diff --git a/src/lib/xdsp/conv_ci16_3ci16_2.c b/src/lib/xdsp/conv_ci16_3ci16_2.c new file mode 100644 index 00000000..a3dfbed6 --- /dev/null +++ b/src/lib/xdsp/conv_ci16_3ci16_2.c @@ -0,0 +1,36 @@ +// Copyright (c) 2026 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_ci16_3ci16_2.h" +#include "attribute_switch.h" + +#define CONV_SCALE (1.0f/32767) + +#define TEMPLATE_FUNC_NAME conv_ci16_3ci16_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_3ci16_generic.t" +DECLARE_TR_FUNC_1_3(conv_ci16_3ci16_generic) + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_ci16_3ci16_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_ci16_3ci16_avx2.t" +DECLARE_TR_FUNC_1_3(conv_ci16_3ci16_avx2) +#endif + +conv_function_t conv_get_ci16_3ci16_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_ci16_3ci16_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_ci16_3ci16_avx2, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_ci16_3ci16() +{ + return conv_get_ci16_3ci16_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_ci16_3ci16_2.h b/src/lib/xdsp/conv_ci16_3ci16_2.h new file mode 100644 index 00000000..d66e4366 --- /dev/null +++ b/src/lib/xdsp/conv_ci16_3ci16_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2026 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_CI16_3CI16_H +#define CONV_CI16_3CI16_H + +#include "conv.h" + +conv_function_t conv_get_ci16_3ci16(); +conv_function_t conv_get_ci16_3ci16_c(generic_opts_t cpu_cap, const char **sfunc); + +#endif //CONV_CI16_3CI16_H diff --git a/src/lib/xdsp/conv_ci16_6cf32_2.c b/src/lib/xdsp/conv_ci16_6cf32_2.c new file mode 100644 index 00000000..ab3b6745 --- /dev/null +++ b/src/lib/xdsp/conv_ci16_6cf32_2.c @@ -0,0 +1,54 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_ci16_6cf32_2.h" +#include "attribute_switch.h" + +#define CONV_SCALE (1.0f/32767) + +#define TEMPLATE_FUNC_NAME conv_ci16_6cf32_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_6cf32_generic.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6cf32_generic) + +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_ci16_6cf32_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_ci16_6cf32_avx512bw.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6cf32_avx512bw) +#endif + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_ci16_6cf32_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_ci16_6cf32_avx2.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6cf32_avx2) +#endif + +#if 0 +#ifdef WVLT_NEON +#define TEMPLATE_FUNC_NAME conv_ci16_6cf32_neon +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_6cf32_neon.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6cf32_neon) +#endif +#endif + +conv_function_t conv_get_ci16_6cf32_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_ci16_6cf32_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_ci16_6cf32_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_ci16_6cf32_avx512bw, cpu_cap); + //SELECT_NEON_FN(fn, fname, tr_conv_ci16_4cf32_neon, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_ci16_6cf32() +{ + return conv_get_ci16_6cf32_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_ci16_6cf32_2.h b/src/lib/xdsp/conv_ci16_6cf32_2.h new file mode 100644 index 00000000..9eaa60be --- /dev/null +++ b/src/lib/xdsp/conv_ci16_6cf32_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_CI16_6CF32_2_H +#define CONV_CI16_6CF32_2_H + +#include "conv.h" + +conv_function_t conv_get_ci16_6cf32_c(generic_opts_t cpu_cap, const char** sfunc); +conv_function_t conv_get_ci16_6cf32(); + +#endif // CONV_CI16_6CF32_2_H diff --git a/src/lib/xdsp/conv_ci16_6ci16_2.c b/src/lib/xdsp/conv_ci16_6ci16_2.c new file mode 100644 index 00000000..94a58010 --- /dev/null +++ b/src/lib/xdsp/conv_ci16_6ci16_2.c @@ -0,0 +1,52 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_ci16_6ci16_2.h" +#include "attribute_switch.h" + +#define TEMPLATE_FUNC_NAME conv_ci16_6ci16_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_6ci16_generic.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6ci16_generic) + +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_ci16_6ci16_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_ci16_6ci16_avx512bw.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6ci16_avx512bw) +#endif + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_ci16_6ci16_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_ci16_6ci16_avx2.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6ci16_avx2) +#endif + +#if 0 +#ifdef WVLT_NEON +#define TEMPLATE_FUNC_NAME conv_ci16_6ci16_neon +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_6ci16_neon.t" +DECLARE_TR_FUNC_1_6(conv_ci16_6ci16_neon) +#endif +#endif + +conv_function_t conv_get_ci16_6ci16_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_ci16_6ci16_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_ci16_6ci16_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_ci16_6ci16_avx512bw, cpu_cap); + //SELECT_NEON_FN(fn, fname, tr_conv_ci16_4ci16_neon, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_ci16_6ci16() +{ + return conv_get_ci16_6ci16_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_ci16_6ci16_2.h b/src/lib/xdsp/conv_ci16_6ci16_2.h new file mode 100644 index 00000000..18ae9bbd --- /dev/null +++ b/src/lib/xdsp/conv_ci16_6ci16_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_CI16_6CI16_H +#define CONV_CI16_6CI16_H + +#include "conv.h" + +conv_function_t conv_get_ci16_6ci16(); +conv_function_t conv_get_ci16_6ci16_c(generic_opts_t cpu_cap, const char **sfunc); + +#endif diff --git a/src/lib/xdsp/conv_ci16_8cf32_2.c b/src/lib/xdsp/conv_ci16_8cf32_2.c new file mode 100644 index 00000000..e56bfe15 --- /dev/null +++ b/src/lib/xdsp/conv_ci16_8cf32_2.c @@ -0,0 +1,36 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_ci16_8cf32_2.h" +#include "attribute_switch.h" + +#define CONV_SCALE (1.0f/32767) + +#define TEMPLATE_FUNC_NAME conv_ci16_8cf32_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_8cf32_generic.t" +DECLARE_TR_FUNC_1_8(conv_ci16_8cf32_generic) + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_ci16_8cf32_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_ci16_8cf32_avx2.t" +DECLARE_TR_FUNC_1_8(conv_ci16_8cf32_avx2) +#endif + +conv_function_t conv_get_ci16_8cf32_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_ci16_8cf32_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_ci16_8cf32_avx2, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_ci16_8cf32() +{ + return conv_get_ci16_8cf32_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_ci16_8cf32_2.h b/src/lib/xdsp/conv_ci16_8cf32_2.h new file mode 100644 index 00000000..b527eb01 --- /dev/null +++ b/src/lib/xdsp/conv_ci16_8cf32_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_CI16_8CF32_2_H +#define CONV_CI16_8CF32_2_H + +#include "conv.h" + +conv_function_t conv_get_ci16_8cf32_c(generic_opts_t cpu_cap, const char** sfunc); +conv_function_t conv_get_ci16_8cf32(); + +#endif // CONV_CI16_8CF32_2_H diff --git a/src/lib/xdsp/conv_ci16_8ci16_2.c b/src/lib/xdsp/conv_ci16_8ci16_2.c new file mode 100644 index 00000000..5e8a191b --- /dev/null +++ b/src/lib/xdsp/conv_ci16_8ci16_2.c @@ -0,0 +1,34 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include "conv_ci16_8ci16_2.h" +#include "attribute_switch.h" + +#define TEMPLATE_FUNC_NAME conv_ci16_8ci16_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/conv_ci16_8ci16_generic.t" +DECLARE_TR_FUNC_1_8(conv_ci16_8ci16_generic) + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME conv_ci16_8ci16_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/conv_ci16_8ci16_avx2.t" +DECLARE_TR_FUNC_1_8(conv_ci16_8ci16_avx2) +#endif + +conv_function_t conv_get_ci16_8ci16_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + conv_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_conv_ci16_8ci16_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_conv_ci16_8ci16_avx2, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +conv_function_t conv_get_ci16_8ci16() +{ + return conv_get_ci16_8ci16_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/conv_ci16_8ci16_2.h b/src/lib/xdsp/conv_ci16_8ci16_2.h new file mode 100644 index 00000000..21d8f3f1 --- /dev/null +++ b/src/lib/xdsp/conv_ci16_8ci16_2.h @@ -0,0 +1,12 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#ifndef CONV_CI16_8CI16_2_H +#define CONV_CI16_8CI16_2_H + +#include "conv.h" + +conv_function_t conv_get_ci16_8ci16_c(generic_opts_t cpu_cap, const char** sfunc); +conv_function_t conv_get_ci16_8ci16(); + +#endif // CONV_CI16_8CI16_2_H diff --git a/src/lib/xdsp/conv_f32_i12_2.c b/src/lib/xdsp/conv_f32_i12_2.c index ee5dea83..7e7443ff 100644 --- a/src/lib/xdsp/conv_f32_i12_2.c +++ b/src/lib/xdsp/conv_f32_i12_2.c @@ -11,6 +11,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_f32_i12_generic.t" DECLARE_TR_FUNC_1_1(conv_f32_i12_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_f32_i12_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_f32_i12_avx512bw.t" +DECLARE_TR_FUNC_1_1(conv_f32_i12_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_f32_i12_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -32,6 +39,7 @@ conv_function_t conv_get_f32_i12_c(generic_opts_t cpu_cap, const char** sfunc) SELECT_GENERIC_FN(fn, fname, tr_conv_f32_i12_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_f32_i12_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_f32_i12_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_f32_i12_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_i12_f32_2.c b/src/lib/xdsp/conv_i12_f32_2.c index 1b5ae9ce..a5903943 100644 --- a/src/lib/xdsp/conv_i12_f32_2.c +++ b/src/lib/xdsp/conv_i12_f32_2.c @@ -23,13 +23,11 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_i12_f32_generic.t" DECLARE_TR_FUNC_1_1(conv_i12_f32_generic) -#if 0 -#ifdef WVLT_SSSE3 -#define TEMPLATE_FUNC_NAME conv_i12_f32_ssse3 -VWLT_ATTRIBUTE(optimize("-O3"), target("ssse3")) -#include "templates/conv_i12_f32_ssse3.t" -DECLARE_TR_FUNC_1_1(conv_i12_f32_ssse3) -#endif +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_i12_f32_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_i12_f32_avx512bw.t" +DECLARE_TR_FUNC_1_1(conv_i12_f32_avx512bw) #endif #ifdef WVLT_AVX2 @@ -44,7 +42,7 @@ DECLARE_TR_FUNC_1_1(conv_i12_f32_avx2) VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_i12_f32_neon.t" DECLARE_TR_FUNC_1_1(conv_i12_f32_neon) -#endif //WVLT_NEON +#endif conv_function_t conv_get_i12_f32_c(generic_opts_t cpu_cap, const char** sfunc) { @@ -52,10 +50,8 @@ conv_function_t conv_get_i12_f32_c(generic_opts_t cpu_cap, const char** sfunc) conv_function_t fn; SELECT_GENERIC_FN(fn, fname, tr_conv_i12_f32_generic, cpu_cap); -#if 0 - SELECT_SSSE3_FN(fn, fname, tr_conv_i12_f32_ssse3, cpu_cap); -#endif SELECT_AVX2_FN(fn, fname, tr_conv_i12_f32_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_i12_f32_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_i12_f32_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_i12_i16_2.c b/src/lib/xdsp/conv_i12_i16_2.c index 9e0013aa..4366927e 100644 --- a/src/lib/xdsp/conv_i12_i16_2.c +++ b/src/lib/xdsp/conv_i12_i16_2.c @@ -9,6 +9,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_i12_i16_generic.t" DECLARE_TR_FUNC_1_1(conv_i12_i16_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_i12_i16_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_i12_i16_avx512bw.t" +DECLARE_TR_FUNC_1_1(conv_i12_i16_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_i12_i16_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -30,6 +37,7 @@ conv_function_t conv_get_i12_i16_c(generic_opts_t cpu_cap, const char** sfunc) SELECT_GENERIC_FN(fn, fname, tr_conv_i12_i16_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_i12_i16_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_i12_i16_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_i12_i16_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_i16_f32_2.c b/src/lib/xdsp/conv_i16_f32_2.c index 79b35d9e..98f04e84 100644 --- a/src/lib/xdsp/conv_i16_f32_2.c +++ b/src/lib/xdsp/conv_i16_f32_2.c @@ -33,6 +33,13 @@ VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) DECLARE_TR_FUNC_1_1(conv_i16_f32_avx2) #endif +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_i16_f32_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_i16_f32_avx512bw.t" +DECLARE_TR_FUNC_1_1(conv_i16_f32_avx512bw) +#endif + #ifdef WVLT_NEON #define TEMPLATE_FUNC_NAME conv_i16_f32_neon VWLT_ATTRIBUTE(optimize("-O3")) @@ -49,6 +56,7 @@ conv_function_t conv_get_i16_f32_c(generic_opts_t cpu_cap, const char** sfunc) SELECT_SSE2_FN(fn, fname, tr_conv_i16_f32_sse2, cpu_cap); SELECT_AVX_FN(fn, fname, tr_conv_i16_f32_avx, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_i16_f32_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_i16_f32_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_i16_f32_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/conv_i16_i12_2.c b/src/lib/xdsp/conv_i16_i12_2.c index 8f05d5d9..215fb081 100644 --- a/src/lib/xdsp/conv_i16_i12_2.c +++ b/src/lib/xdsp/conv_i16_i12_2.c @@ -9,6 +9,13 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/conv_i16_i12_generic.t" DECLARE_TR_FUNC_1_1(conv_i16_i12_generic) +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME conv_i16_i12_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/conv_i16_i12_avx512bw.t" +DECLARE_TR_FUNC_1_1(conv_i16_i12_avx512bw) +#endif + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME conv_i16_i12_avx2 VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) @@ -30,6 +37,7 @@ conv_function_t conv_get_i16_i12_c(generic_opts_t cpu_cap, const char** sfunc) SELECT_GENERIC_FN(fn, fname, tr_conv_i16_i12_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_conv_i16_i12_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_conv_i16_i12_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_conv_i16_i12_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/fast_math.h b/src/lib/xdsp/fast_math.h index 46f92f76..e74678c7 100644 --- a/src/lib/xdsp/fast_math.h +++ b/src/lib/xdsp/fast_math.h @@ -103,6 +103,52 @@ static inline #endif + + +#ifdef WVLT_AVX512BW + +#define WVLT_AVX512_LOG2_POLY0(x, c0) _mm512_set1_ps(c0) +#define WVLT_AVX512_LOG2_POLY1(x, c0, c1) _mm512_add_ps(_mm512_mul_ps(WVLT_AVX512_LOG2_POLY0(x, c1), x), _mm512_set1_ps(c0)) +#define WVLT_AVX512_LOG2_POLY2(x, c0, c1, c2) _mm512_add_ps(_mm512_mul_ps(WVLT_AVX512_LOG2_POLY1(x, c1, c2), x), _mm512_set1_ps(c0)) +#define WVLT_AVX512_LOG2_POLY3(x, c0, c1, c2, c3) _mm512_add_ps(_mm512_mul_ps(WVLT_AVX512_LOG2_POLY2(x, c1, c2, c3), x), _mm512_set1_ps(c0)) +#define WVLT_AVX512_LOG2_POLY4(x, c0, c1, c2, c3, c4) _mm512_add_ps(_mm512_mul_ps(WVLT_AVX512_LOG2_POLY3(x, c1, c2, c3, c4), x), _mm512_set1_ps(c0)) +#define WVLT_AVX512_LOG2_POLY5(x, c0, c1, c2, c3, c4, c5) _mm512_add_ps(_mm512_mul_ps(WVLT_AVX512_LOG2_POLY4(x, c1, c2, c3, c4, c5), x), _mm512_set1_ps(c0)) + +#define WVLT_AVX512_POLYLOG2_DECL_CONSTS \ +const __m512i wvlt_AVX512_log2_exp = _mm512_set1_epi32(0x7F800000); \ + const __m512i wvlt_AVX512_log2_mant = _mm512_set1_epi32(0x007FFFFF); \ + const __m512 wvlt_AVX512_log2_one = _mm512_set1_ps(1.0f); \ + const __m512i wvlt_AVX512_log2_v127 = _mm512_set1_epi32(127); + +#if LOG_POLY_DEGREE == 3 +#define WVLT_AVX512_LOG2_POLY_APPROX(x) WVLT_AVX512_LOG2_POLY2(x, 2.28330284476918490682f, -1.04913055217340124191f, 0.204446009836232697516f) +#elif LOG_POLY_DEGREE == 4 +#define WVLT_AVX512_LOG2_POLY_APPROX(x) WVLT_AVX512_LOG2_POLY3(x, 2.61761038894603480148f, -1.75647175389045657003f, 0.688243882994381274313f, -0.107254423828329604454f) +#elif LOG_POLY_DEGREE == 5 +#define WVLT_AVX512_LOG2_POLY_APPROX(x) WVLT_AVX512_LOG2_POLY4(x, 2.8882704548164776201f, -2.52074962577807006663f, 1.48116647521213171641f, -0.465725644288844778798f, 0.0596515482674574969533f) +#elif LOG_POLY_DEGREE == 6 +#define WVLT_AVX512_LOG2_POLY_APPROX(x) WVLT_AVX512_LOG2_POLY5(x, 3.1157899f, -3.3241990f, 2.5988452f, -1.2315303f, 3.1821337e-1f, -3.4436006e-2f) +#else +#error +#endif + +#define WVLT_POLYLOG2F16(in, out) \ +{ \ + __m512i i = _mm512_castps_si512(in); \ + __m512 e = _mm512_cvtepi32_ps(_mm512_sub_epi32(_mm512_srli_epi32(_mm512_and_si512(i, wvlt_AVX512_log2_exp), 23), wvlt_AVX512_log2_v127)); \ + __m512 m = _mm512_or_ps(_mm512_castsi512_ps(_mm512_and_si512(i, wvlt_AVX512_log2_mant)), wvlt_AVX512_log2_one); \ + \ + /* Minimax polynomial fit of log2(x)/(x - 1), for x in range [1, 2[ */ \ + __m512 p = WVLT_AVX512_LOG2_POLY_APPROX(m); \ + \ + /* This effectively increases the polynomial degree by one, but ensures that log2(1) == 0*/ \ + p = _mm512_mul_ps(p, _mm512_sub_ps(m, wvlt_AVX512_log2_one)); \ + \ + out = _mm512_add_ps(p, e); \ +} + +#endif + #ifdef WVLT_NEON #define WVLT_LOG2_POLY0(x, c0) vdupq_n_f32(c0) diff --git a/src/lib/xdsp/fft_window_functions.c b/src/lib/xdsp/fft_window_functions.c index 7b9d7301..44302804 100644 --- a/src/lib/xdsp/fft_window_functions.c +++ b/src/lib/xdsp/fft_window_functions.c @@ -13,6 +13,32 @@ VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) DECLARE_TR_FUNC_FFT_WINDOW_CF32(fft_window_cf32_avx2) #endif +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME fft_window_cf32_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/fft_window_cf32_avx512bw.t" +DECLARE_TR_FUNC_FFT_WINDOW_CF32(fft_window_cf32_avx512bw) +#endif + +#define TEMPLATE_FUNC_NAME fft_window_ci16_cf32_generic +VWLT_ATTRIBUTE(optimize("-O3")) +#include "templates/fft_window_ci16_cf32_generic.t" +DECLARE_TR_FUNC_FFT_WINDOW_CI16_CF32(fft_window_ci16_cf32_generic) + +#ifdef WVLT_AVX2 +#define TEMPLATE_FUNC_NAME fft_window_ci16_cf32_avx2 +VWLT_ATTRIBUTE(optimize("-O3"), target("avx2")) +#include "templates/fft_window_ci16_cf32_avx2.t" +DECLARE_TR_FUNC_FFT_WINDOW_CI16_CF32(fft_window_ci16_cf32_avx2) +#endif + +#ifdef WVLT_AVX512BW +#define TEMPLATE_FUNC_NAME fft_window_ci16_cf32_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/fft_window_ci16_cf32_avx512bw.t" +DECLARE_TR_FUNC_FFT_WINDOW_CI16_CF32(fft_window_ci16_cf32_avx512bw) +#endif + fft_window_cf32_function_t fft_window_cf32_c(generic_opts_t cpu_cap, const char** sfunc) { const char* fname; @@ -20,6 +46,20 @@ fft_window_cf32_function_t fft_window_cf32_c(generic_opts_t cpu_cap, const char* SELECT_GENERIC_FN(fn, fname, tr_fft_window_cf32_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_fft_window_cf32_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_fft_window_cf32_avx512bw, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +fft_window_ci16_cf32_function_t fft_window_ci16_cf32_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + fft_window_ci16_cf32_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_fft_window_ci16_cf32_generic, cpu_cap); + SELECT_AVX2_FN(fn, fname, tr_fft_window_ci16_cf32_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_fft_window_ci16_cf32_avx512bw, cpu_cap); if (sfunc) *sfunc = fname; return fn; diff --git a/src/lib/xdsp/fft_window_functions.h b/src/lib/xdsp/fft_window_functions.h index c4e90a35..a08a5572 100644 --- a/src/lib/xdsp/fft_window_functions.h +++ b/src/lib/xdsp/fft_window_functions.h @@ -9,12 +9,18 @@ extern "C" { #endif fft_window_cf32_function_t fft_window_cf32_c(generic_opts_t cpu_cap, const char** sfunc); +fft_window_ci16_cf32_function_t fft_window_ci16_cf32_c(generic_opts_t cpu_cap, const char** sfunc); static inline void fft_window_cf32(wvlt_fftwf_complex* in, unsigned fftsz, float* wnd, wvlt_fftwf_complex* out) { return (*fft_window_cf32_c(cpu_vcap_get(), NULL))(in, fftsz, wnd, out); } +static inline void fft_window_ci16_cf32(wvlt_fftwi16_complex* in, unsigned fftsz, float* wnd, wvlt_fftwf_complex* out) +{ + return (*fft_window_ci16_cf32_c(cpu_vcap_get(), NULL))(in, fftsz, wnd, out); +} + #ifdef __cplusplus } #endif diff --git a/src/lib/xdsp/fftad_functions.c b/src/lib/xdsp/fftad_functions.c index 9139b663..617fdc36 100644 --- a/src/lib/xdsp/fftad_functions.c +++ b/src/lib/xdsp/fftad_functions.c @@ -38,6 +38,27 @@ VWLT_ATTRIBUTE(optimize("-O3")) #include "templates/fftad_norm_hwi16_generic.t" DECLARE_TR_FUNC_FFTAD_NORM_HWI16(fftad_norm_hwi16_generic) + +#ifdef WVLT_AVX512BW + +#define TEMPLATE_FUNC_NAME fftad_init_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw")) +#include "templates/fftad_init_avx512bw.t" +DECLARE_TR_FUNC_FFTAD_INIT(fftad_init_avx512bw) + +#define TEMPLATE_FUNC_NAME fftad_add_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw,avx512dq")) +#include "templates/fftad_add_avx512bw.t" +DECLARE_TR_FUNC_FFTAD_ADD(fftad_add_avx512bw) + +#define TEMPLATE_FUNC_NAME fftad_norm_avx512bw +VWLT_ATTRIBUTE(optimize("-O3"), target("avx512bw,avx512dq,fma")) +#include "templates/fftad_norm_avx512bw.t" +DECLARE_TR_FUNC_FFTAD_NORM(fftad_norm_avx512bw) + +#endif //WVLT_AVX512BW + + #ifdef WVLT_AVX2 #define TEMPLATE_FUNC_NAME fftad_init_avx2 @@ -116,6 +137,7 @@ fftad_init_function_t fftad_init_c(generic_opts_t cpu_cap, const char** sfunc) SELECT_GENERIC_FN(fn, fname, tr_fftad_init_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_fftad_init_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_fftad_init_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_fftad_init_neon, cpu_cap); if (sfunc) *sfunc = fname; @@ -129,6 +151,7 @@ fftad_add_function_t fftad_add_c(generic_opts_t cpu_cap, const char** sfunc) SELECT_GENERIC_FN(fn, fname, tr_fftad_add_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_fftad_add_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_fftad_add_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_fftad_add_neon, cpu_cap); if (sfunc) *sfunc = fname; @@ -142,6 +165,7 @@ fftad_norm_function_t fftad_norm_c(generic_opts_t cpu_cap, const char** sfunc) SELECT_GENERIC_FN(fn, fname, tr_fftad_norm_generic, cpu_cap); SELECT_AVX2_FN(fn, fname, tr_fftad_norm_avx2, cpu_cap); + SELECT_AVX512BW_FN(fn, fname, tr_fftad_norm_avx512bw, cpu_cap); SELECT_NEON_FN(fn, fname, tr_fftad_norm_neon, cpu_cap); if (sfunc) *sfunc = fname; diff --git a/src/lib/xdsp/filter.c b/src/lib/xdsp/filter.c index 736f09ee..0c789b3c 100644 --- a/src/lib/xdsp/filter.c +++ b/src/lib/xdsp/filter.c @@ -328,7 +328,7 @@ filter_data_t* filter_data_alloc(unsigned origblksz, if (flags & FDAF_SEPARATED) return NULL; - int res = posix_memalign((void**)&f, CACHE_LINE, sizeof(filter_data_t) + + int res = usdr_alignalloc((void**)&f, CACHE_LINE, sizeof(filter_data_t) + 3 * tapssz + datasz); if (res) { return NULL; @@ -424,7 +424,7 @@ filter_data_t* filter_data_alloc(unsigned origblksz, void filter_data_free(filter_data_t* o) { - free(o); + usdr_alignfree(o); } int16_t* filter_data_ptr(filter_data_t* o) diff --git a/src/lib/xdsp/fmquad.c b/src/lib/xdsp/fmquad.c index b9e65d4f..5ab8f69c 100644 --- a/src/lib/xdsp/fmquad.c +++ b/src/lib/xdsp/fmquad.c @@ -4,6 +4,7 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#include "usdr_port.h" #include #include #include "fmquad.h" diff --git a/src/lib/xdsp/sincos_functions.c b/src/lib/xdsp/sincos_functions.c index 5dddb90f..0af23f56 100644 --- a/src/lib/xdsp/sincos_functions.c +++ b/src/lib/xdsp/sincos_functions.c @@ -83,3 +83,35 @@ sincos_i16_interleaved_ctrl_function_t get_wvlt_sincos_i16_interleaved_ctrl() { return get_wvlt_sincos_i16_interleaved_ctrl_c(cpu_vcap_get(), NULL); } + + +#define TEMPLATE_FUNC_NAME wvlt_sincos_i16_interleaved_chirp_generic +VWLT_ATTRIBUTE(optimize("-O3", "inline")) +#include "templates/wvlt_sincos_i16_interleaved_chirp_generic.t" +DECLARE_TR_FUNC_SINCOS_I16_INTERLEAVED_CHIRP(wvlt_sincos_i16_interleaved_chirp_generic) + +#ifdef WVLT_SSSE3 +#define TEMPLATE_FUNC_NAME wvlt_sincos_i16_interleaved_chirp_ssse3 +VWLT_ATTRIBUTE(optimize("-O3", "inline"), target("ssse3")) +#include "templates/wvlt_sincos_i16_interleaved_chirp_ssse3.t" +DECLARE_TR_FUNC_SINCOS_I16_INTERLEAVED_CHIRP(wvlt_sincos_i16_interleaved_chirp_ssse3) +#endif + +sincos_i16_interleaved_chirp_function_t get_wvlt_sincos_i16_interleaved_chirp_c(generic_opts_t cpu_cap, const char** sfunc) +{ + const char* fname; + sincos_i16_interleaved_chirp_function_t fn; + + SELECT_GENERIC_FN(fn, fname, tr_wvlt_sincos_i16_interleaved_chirp_generic, cpu_cap); + SELECT_SSSE3_FN(fn, fname, tr_wvlt_sincos_i16_interleaved_chirp_ssse3, cpu_cap); + //SELECT_AVX2_FN(fn, fname, tr_wvlt_sincos_i16_interleaved_ctrl_avx2, cpu_cap); + //SELECT_NEON_FN(fn, fname, tr_wvlt_sincos_i16_interleaved_ctrl_neon, cpu_cap); + + if (sfunc) *sfunc = fname; + return fn; +} + +sincos_i16_interleaved_chirp_function_t get_wvlt_sincos_i16_interleaved_chirp() +{ + return get_wvlt_sincos_i16_interleaved_chirp_c(cpu_vcap_get(), NULL); +} diff --git a/src/lib/xdsp/sincos_functions.h b/src/lib/xdsp/sincos_functions.h index 0666d33b..f515fbaf 100644 --- a/src/lib/xdsp/sincos_functions.h +++ b/src/lib/xdsp/sincos_functions.h @@ -32,7 +32,7 @@ sincos_i16_interleaved_ctrl_function_t get_wvlt_sincos_i16_interleaved_ctrl(); * wvlt_sincos_i16_interleaved_ctrl() * * int32_t* start_phase: Starting phase. - * Diapazon [-PI..+PI) mapped to int32 range INT32_MIN..INT32_MAX. + * Range [-PI..+PI) mapped to int32 range INT32_MIN..INT32_MAX. * The next starting phase for consequent calls is returned by ptr. * int32_t delta_phase: Delta, applying to starting phase. int32_t range is just the same as for start_phase. * int16_t gain: Output max amplitude @@ -52,4 +52,17 @@ static inline return (*get_wvlt_sincos_i16_interleaved_ctrl())(start_phase, delta_phase, gain, inv_sin, inv_cos, outdata, iters); } + +sincos_i16_interleaved_chirp_function_t get_wvlt_sincos_i16_interleaved_chirp_c(generic_opts_t cpu_cap, const char** sfunc); +sincos_i16_interleaved_chirp_function_t get_wvlt_sincos_i16_interleaved_chirp(); +static inline +void wvlt_sincos_i16_interleaved_chirp(int32_t* start_phase, int32_t* start_delta_phase, int32_t* delta_phase, + int32_t chirp_steps, int16_t gain, + bool inv_sin, bool inv_cos, + int16_t* outdata, + unsigned iters) +{ + return (*get_wvlt_sincos_i16_interleaved_chirp())(start_phase, start_delta_phase, delta_phase, chirp_steps, gain, inv_sin, inv_cos, outdata, iters); +} + #endif // SINCOS_FUNCTIONS_H diff --git a/src/lib/xdsp/templates/conv_2cf32_ci12_avx2.inc b/src/lib/xdsp/templates/conv_2cf32_ci12_avx2.inc new file mode 100644 index 00000000..9169c888 --- /dev/null +++ b/src/lib/xdsp/templates/conv_2cf32_ci12_avx2.inc @@ -0,0 +1,15 @@ +const __m256 scale = _mm256_set1_ps(1.0f / CONV_SCALE); + +#define CONVERT_2F32_I12_BLOCK(v0, v1) \ +{ \ + v0 = _mm256_mul_ps(v0, scale); \ + v1 = _mm256_mul_ps(v1, scale); \ +\ + __m256i i0 = _mm256_cvtps_epi32(v0); \ + __m256i i1 = _mm256_cvtps_epi32(v1); \ +\ + __m256i ii0 = _mm256_packs_epi32(i0, i1); \ + ii0 = _mm256_shuffle_epi32(ii0, _MM_SHUFFLE(3,1,2,0)); \ +\ + CONVERT_I16_I12_BLOCK(ii0, out64); \ +} diff --git a/src/lib/xdsp/templates/conv_2cf32_ci12_avx2.t b/src/lib/xdsp/templates/conv_2cf32_ci12_avx2.t index 04789485..afdda200 100644 --- a/src/lib/xdsp/templates/conv_2cf32_ci12_avx2.t +++ b/src/lib/xdsp/templates/conv_2cf32_ci12_avx2.t @@ -13,24 +13,8 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, const float* indata_1 = (const float*)indata_1_p; uint64_t *out64 = (uint64_t*)outdata_p; - const __m256 scale = _mm256_set1_ps(1.0f / CONV_SCALE); - #include "conv_i16_i12_avx2.inc" - -#define CONVERT_2F32_I12_BLOCK(v0, v1) \ - { \ - v0 = _mm256_mul_ps(v0, scale); \ - v1 = _mm256_mul_ps(v1, scale); \ - \ - __m256i i0 = _mm256_cvtps_epi32(v0); \ - __m256i i1 = _mm256_cvtps_epi32(v1); \ - \ - __m256i ii0 = _mm256_packs_epi32(i0, i1); \ - ii0 = _mm256_shuffle_epi32(ii0, _MM_SHUFFLE(3,1,2,0)); \ - \ - CONVERT_I16_I12_BLOCK(ii0, out64); \ - } -// CONVERT_2F32_I12_BLOCK end +#include "conv_2cf32_ci12_avx2.inc" __m256 v0, v1, v2, v3; @@ -64,27 +48,6 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, #define I16RND(x) x > 0 ? (int16_t)(x + 0.5f) : (int16_t)(x - 0.5f) uint8_t* outdata = (uint8_t*)out64; - - for (; i >= 16; i -= 16) { - - float f0 = *(indata_0++) / CONV_SCALE; - float f1 = *(indata_0++) / CONV_SCALE; - float f2 = *(indata_1++) / CONV_SCALE; - float f3 = *(indata_1++) / CONV_SCALE; - - wu_i16u32_t a0 = {{I16RND(f0), I16RND(f1)}}; - wu_i16u32_t a1 = {{I16RND(f2), I16RND(f3)}}; - - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - - *(outdata++) = c0.b[1]; - *(outdata++) = c0.b[2]; - *(outdata++) = c0.b[3]; - - *(outdata++) = c1.b[1]; - *(outdata++) = c1.b[2]; - *(outdata++) = c1.b[3]; - } + #include "conv_2cf32_ci12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_2cf32_ci12_avx512bw.inc b/src/lib/xdsp/templates/conv_2cf32_ci12_avx512bw.inc new file mode 100644 index 00000000..b7049f97 --- /dev/null +++ b/src/lib/xdsp/templates/conv_2cf32_ci12_avx512bw.inc @@ -0,0 +1,15 @@ +const __m512 scale = _mm512_set1_ps(1.0f / CONV_SCALE); + +#define CONVERT_2F32_I12_BLOCK(v0, v1) \ +{ \ + v0 = _mm512_mul_ps(v0, scale); \ + v1 = _mm512_mul_ps(v1, scale); \ +\ + __m512i i0 = _mm512_cvtps_epi32(v0); \ + __m512i i1 = _mm512_cvtps_epi32(v1); \ +\ + __m512i ii0 = _mm512_packs_epi32(i0, i1); \ + ii0 = _mm512_shuffle_epi32(ii0, _MM_SHUFFLE(3,1,2,0)); \ +\ + CONVERT_I16_I12_BLOCK(ii0, out64); \ +} diff --git a/src/lib/xdsp/templates/conv_2cf32_ci12_avx512bw.t b/src/lib/xdsp/templates/conv_2cf32_ci12_avx512bw.t new file mode 100644 index 00000000..9f52f690 --- /dev/null +++ b/src/lib/xdsp/templates/conv_2cf32_ci12_avx512bw.t @@ -0,0 +1,64 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 8 / 3) < i) + i = (outdatabsz * 8 / 3); + + const float* indata_0 = (const float*)indata_0_p; + const float* indata_1 = (const float*)indata_1_p; + uint64_t *out64 = (uint64_t*)outdata_p; + + //AVX512 block + { + #include "conv_i16_i12_avx512bw.inc" + #include "conv_2cf32_ci12_avx512bw.inc" + + __m512 v0, v1; + + for (; i >= 64*2; i -= 64*2) + { + v0 = _mm512_loadu_ps(indata_0 + 0); + v1 = _mm512_loadu_ps(indata_1 + 0); + indata_0 += 16; + indata_1 += 16; + CONVERT_2F32_I12_BLOCK(v0, v1); + } + + #undef CONVERT_2F32_I12_BLOCK + #undef CONVERT_I16_I12_BLOCK + } + + //AVX2 block + { + #include "conv_i16_i12_avx2.inc" + #include "conv_2cf32_ci12_avx2.inc" + + if(i >= 32*2) + { + __m256 v0 = _mm256_loadu_ps(indata_0 + 0); + __m256 v1 = _mm256_loadu_ps(indata_1 + 0); + indata_0 += 8; + indata_1 += 8; + i -= 32*2; + CONVERT_2F32_I12_BLOCK(v0, v1); + } + + #undef CONVERT_2F32_I12_BLOCK + #undef CONVERT_I16_I12_BLOCK + } + + //Generic block + { + #undef I16RND + #define I16RND(x) x > 0 ? (int16_t)(x + 0.5f) : (int16_t)(x - 0.5f) + + uint8_t* outdata = (uint8_t*)out64; + #include "conv_2cf32_ci12_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_2cf32_ci12_generic.inc b/src/lib/xdsp/templates/conv_2cf32_ci12_generic.inc new file mode 100644 index 00000000..ef99f9b2 --- /dev/null +++ b/src/lib/xdsp/templates/conv_2cf32_ci12_generic.inc @@ -0,0 +1,21 @@ +for (; i >= 16; i -= 16) { + + float f0 = *(indata_0++) / CONV_SCALE; + float f1 = *(indata_0++) / CONV_SCALE; + float f2 = *(indata_1++) / CONV_SCALE; + float f3 = *(indata_1++) / CONV_SCALE; + + wu_i16u32_t a0 = {{I16RND(f0), I16RND(f1)}}; + wu_i16u32_t a1 = {{I16RND(f2), I16RND(f3)}}; + + wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; + wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; + + *(outdata++) = c0.b[1]; + *(outdata++) = c0.b[2]; + *(outdata++) = c0.b[3]; + + *(outdata++) = c1.b[1]; + *(outdata++) = c1.b[2]; + *(outdata++) = c1.b[3]; +} diff --git a/src/lib/xdsp/templates/conv_2cf32_ci12_generic.t b/src/lib/xdsp/templates/conv_2cf32_ci12_generic.t index 4885d950..c712a3b9 100644 --- a/src/lib/xdsp/templates/conv_2cf32_ci12_generic.t +++ b/src/lib/xdsp/templates/conv_2cf32_ci12_generic.t @@ -13,26 +13,6 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, const float* indata_1 = (const float*)indata_1_p; uint8_t* outdata = (uint8_t*)outdata_p; - for (; i >= 16; i -= 16) { - - float f0 = *(indata_0++) / CONV_SCALE; - float f1 = *(indata_0++) / CONV_SCALE; - float f2 = *(indata_1++) / CONV_SCALE; - float f3 = *(indata_1++) / CONV_SCALE; - - wu_i16u32_t a0 = {{I16RND(f0), I16RND(f1)}}; - wu_i16u32_t a1 = {{I16RND(f2), I16RND(f3)}}; - - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - - *(outdata++) = c0.b[1]; - *(outdata++) = c0.b[2]; - *(outdata++) = c0.b[3]; - - *(outdata++) = c1.b[1]; - *(outdata++) = c1.b[2]; - *(outdata++) = c1.b[3]; - } + #include "conv_2cf32_ci12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_2ci16_ci12_avx2.inc b/src/lib/xdsp/templates/conv_2ci16_ci12_avx2.inc new file mode 100644 index 00000000..90cf0f5a --- /dev/null +++ b/src/lib/xdsp/templates/conv_2ci16_ci12_avx2.inc @@ -0,0 +1,15 @@ +#define STORE_2CI16_CI12_BLOCK(v0, v1) \ +{ \ + __m256i i0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(v0), _mm256_castsi256_pd(v1), 0b0000)); \ + __m256i i1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(v0), _mm256_castsi256_pd(v1), 0b1111)); \ +\ + i0 = _mm256_shuffle_epi32(i0, _MM_SHUFFLE(3,1,2,0)); \ + i1 = _mm256_shuffle_epi32(i1, _MM_SHUFFLE(3,1,2,0)); \ +\ + __m256i z0 = _mm256_permute2x128_si256(i0, i1, 0b00100000); \ + __m256i z1 = _mm256_permute2x128_si256(i0, i1, 0b00110001); \ +\ + CONVERT_I16_I12_BLOCK(z0, out64); \ + CONVERT_I16_I12_BLOCK(z1, out64); \ +} +// STORE_2CI16_CI12_BLOCK end \ No newline at end of file diff --git a/src/lib/xdsp/templates/conv_2ci16_ci12_avx2.t b/src/lib/xdsp/templates/conv_2ci16_ci12_avx2.t index c5fa0aad..3f7fde16 100644 --- a/src/lib/xdsp/templates/conv_2ci16_ci12_avx2.t +++ b/src/lib/xdsp/templates/conv_2ci16_ci12_avx2.t @@ -14,22 +14,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, uint64_t *out64 = (uint64_t*)outdata_p; #include "conv_i16_i12_avx2.inc" - -#define STORE_2CI16_CI12_BLOCK(v0, v1) \ - { \ - __m256i i0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(v0), _mm256_castsi256_pd(v1), 0b0000)); \ - __m256i i1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(v0), _mm256_castsi256_pd(v1), 0b1111)); \ - \ - i0 = _mm256_shuffle_epi32(i0, _MM_SHUFFLE(3,1,2,0)); \ - i1 = _mm256_shuffle_epi32(i1, _MM_SHUFFLE(3,1,2,0)); \ - \ - __m256i z0 = _mm256_permute2x128_si256(i0, i1, 0b00100000); \ - __m256i z1 = _mm256_permute2x128_si256(i0, i1, 0b00110001); \ - \ - CONVERT_I16_I12_BLOCK(z0, out64); \ - CONVERT_I16_I12_BLOCK(z1, out64); \ - } -// STORE_2CI16_CI12_BLOCK end +#include "conv_2ci16_ci12_avx2.inc" __m256i v0, v1, v2, v3; @@ -61,26 +46,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, uint8_t* outdata = (uint8_t*)out64; - for (; i >= 8; i -= 8) { - - const int16_t i0 = *indata_0++; - const int16_t q0 = *indata_0++; - const int16_t i1 = *indata_1++; - const int16_t q1 = *indata_1++; - - wu_i16u32_t a0 = {{i0, q0}}; - wu_i16u32_t a1 = {{i1, q1}}; +#include "conv_2ci16_ci12_generic.inc" - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - - *(outdata++) = c0.b[1]; - *(outdata++) = c0.b[2]; - *(outdata++) = c0.b[3]; - - *(outdata++) = c1.b[1]; - *(outdata++) = c1.b[2]; - *(outdata++) = c1.b[3]; - } } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_2ci16_ci12_avx512bw.t b/src/lib/xdsp/templates/conv_2ci16_ci12_avx512bw.t new file mode 100644 index 00000000..6fff0d95 --- /dev/null +++ b/src/lib/xdsp/templates/conv_2ci16_ci12_avx512bw.t @@ -0,0 +1,69 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 4 / 3) < i) + i = (outdatabsz * 4 / 3); + + const int16_t* indata_0 = (const int16_t*)indata_0_p; + const int16_t* indata_1 = (const int16_t*)indata_1_p; + uint64_t *out64 = (uint64_t*)outdata_p; + + //AVX512 block + { + #include "conv_i16_i12_avx512bw.inc" + + const __m512i imask0 = _mm512_set_epi32(23, 7,22, 6, 21, 5,20, 4, 19, 3,18, 2, 17, 1,16, 0); + const __m512i imask1 = _mm512_set_epi32(31,15,30,14, 29,13,28,12, 27,11,26,10, 25, 9,24, 8); + __m512i v0, v1; + + for (; i >= 64*2; i -= 64*2) + { + v0 = _mm512_loadu_si512((__m512i*)indata_0); + v1 = _mm512_loadu_si512((__m512i*)indata_1); + indata_0 += 32; + indata_1 += 32; + + __m512i z0 = _mm512_permutex2var_epi32(v0, imask0, v1); + __m512i z1 = _mm512_permutex2var_epi32(v0, imask1, v1); + + CONVERT_I16_I12_BLOCK(z0, out64); + CONVERT_I16_I12_BLOCK(z1, out64); + } + + #undef CONVERT_I16_I12_BLOCK + } + + //AVX2 block + { + #include "conv_i16_i12_avx2.inc" + #include "conv_2ci16_ci12_avx2.inc" + + __m256i v0, v1; + + if(i >= 32*2) + { + v0 = _mm256_loadu_si256((__m256i*)indata_0); + v1 = _mm256_loadu_si256((__m256i*)indata_1); + indata_0 += 16; + indata_1 += 16; + i -= 64; + + STORE_2CI16_CI12_BLOCK(v0, v1); + } + + #undef STORE_2CI16_CI12_BLOCK + #undef CONVERT_I16_I12_BLOCK + } + + //Generic block + { + uint8_t* outdata = (uint8_t*)out64; + #include "conv_2ci16_ci12_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_2ci16_ci12_generic.inc b/src/lib/xdsp/templates/conv_2ci16_ci12_generic.inc new file mode 100644 index 00000000..8fd1a002 --- /dev/null +++ b/src/lib/xdsp/templates/conv_2ci16_ci12_generic.inc @@ -0,0 +1,21 @@ +for (; i >= 8; i -= 8) { + + const int16_t i0 = *indata_0++; + const int16_t q0 = *indata_0++; + const int16_t i1 = *indata_1++; + const int16_t q1 = *indata_1++; + + wu_i16u32_t a0 = {{i0, q0}}; + wu_i16u32_t a1 = {{i1, q1}}; + + wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; + wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; + + *(outdata++) = c0.b[1]; + *(outdata++) = c0.b[2]; + *(outdata++) = c0.b[3]; + + *(outdata++) = c1.b[1]; + *(outdata++) = c1.b[2]; + *(outdata++) = c1.b[3]; +} diff --git a/src/lib/xdsp/templates/conv_2ci16_ci12_generic.t b/src/lib/xdsp/templates/conv_2ci16_ci12_generic.t index cf0d764b..28371410 100644 --- a/src/lib/xdsp/templates/conv_2ci16_ci12_generic.t +++ b/src/lib/xdsp/templates/conv_2ci16_ci12_generic.t @@ -13,26 +13,6 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, const int16_t* indata_1 = (const int16_t*)indata_1_p; uint8_t* outdata = (uint8_t*)outdata_p; - for (; i >= 8; i -= 8) { - - const int16_t i0 = *indata_0++; - const int16_t q0 = *indata_0++; - const int16_t i1 = *indata_1++; - const int16_t q1 = *indata_1++; - - wu_i16u32_t a0 = {{i0, q0}}; - wu_i16u32_t a1 = {{i1, q1}}; - - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - - *(outdata++) = c0.b[1]; - *(outdata++) = c0.b[2]; - *(outdata++) = c0.b[3]; - - *(outdata++) = c1.b[1]; - *(outdata++) = c1.b[2]; - *(outdata++) = c1.b[3]; - } + #include "conv_2ci16_ci12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_2ci16_ci16_neon.t b/src/lib/xdsp/templates/conv_2ci16_ci16_neon.t index 8335d69c..a626dcc1 100644 --- a/src/lib/xdsp/templates/conv_2ci16_ci16_neon.t +++ b/src/lib/xdsp/templates/conv_2ci16_ci16_neon.t @@ -15,8 +15,8 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, while(i >= 32) { - vst2q_u32((uint32_t*)outdata, - (uint32x4x2_t){ {vld1q_u32((uint32_t*)indata_0), vld1q_u32((uint32_t*)indata_1)} }); + uint32x4x2_t z = {vld1q_u32((uint32_t*)indata_0), vld1q_u32((uint32_t*)indata_1)}; + vst2q_u32((uint32_t*)outdata, z); i -= 32; indata_0 += 8; diff --git a/src/lib/xdsp/templates/conv_3cf32_ci16_avx2.t b/src/lib/xdsp/templates/conv_3cf32_ci16_avx2.t new file mode 100644 index 00000000..4dd1357e --- /dev/null +++ b/src/lib/xdsp/templates/conv_3cf32_ci16_avx2.t @@ -0,0 +1,91 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + const void *__restrict indata_2_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 2) < i) + i = (outdatabsz * 2); + + const float* indata_0 = (const float*)indata_0_p; + const float* indata_1 = (const float*)indata_1_p; + const float* indata_2 = (const float*)indata_2_p; + int16_t* outdata = (int16_t*)outdata_p; + + const __m256 scale = _mm256_set1_ps(1.0f / CONV_SCALE); + +#define CONV_3CF32(r0, r1, r2) \ +{ \ + __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(r0), _mm256_castsi256_pd(r1), 0b0000)); \ + __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(r0), _mm256_castsi256_pd(r1), 0b1111)); \ + __m256i z2 = r2; \ + \ + __m256i zz0 = z0; \ + __m256i zz1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b0000)); \ + __m256i zz2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b1111)); \ + \ + \ + zz1 = _mm256_shuffle_epi32(zz1, _MM_SHUFFLE(1,0,3,2)); \ + \ + \ + __m256i x0 = _mm256_permute2x128_si256(zz0, zz1, 0b00100000); \ + __m256i x1 = _mm256_permute2x128_si256(zz0, zz1, 0b00110001); \ + __m256i x2 = zz2; \ + \ + r0 = x0; \ + r1 = _mm256_permute2x128_si256(x1, x2, 0b00100000); \ + r2 = _mm256_permute2x128_si256(x1, x2, 0b00110001); \ + \ + \ + r1 = _mm256_permute2x128_si256(r1, r1, 0x1); \ + \ +} + + for (; i >= 32 * 6; i -= 32 * 6) + { + __m256i a0 = _mm256_load_si256((const __m256i*)indata_0); + __m256i a1 = _mm256_load_si256((const __m256i*)indata_1); + __m256i a2 = _mm256_load_si256((const __m256i*)indata_2); + __m256i a3 = _mm256_load_si256((const __m256i*)(indata_0 + 8)); + __m256i a4 = _mm256_load_si256((const __m256i*)(indata_1 + 8)); + __m256i a5 = _mm256_load_si256((const __m256i*)(indata_2 + 8)); + indata_0 += 16; + indata_1 += 16; + indata_2 += 16; + + CONV_3CF32(a0, a1, a2); + CONV_3CF32(a3, a4, a5); + + __m256 p0 = _mm256_castsi256_ps(_mm256_permute2x128_si256(a0, a1, 0b00100000)); + __m256 p1 = _mm256_castsi256_ps(_mm256_permute2x128_si256(a0, a1, 0b00110001)); + __m256 p2 = _mm256_castsi256_ps(_mm256_permute2x128_si256(a2, a3, 0b00100000)); + __m256 p3 = _mm256_castsi256_ps(_mm256_permute2x128_si256(a2, a3, 0b00110001)); + __m256 p4 = _mm256_castsi256_ps(_mm256_permute2x128_si256(a4, a5, 0b00100000)); + __m256 p5 = _mm256_castsi256_ps(_mm256_permute2x128_si256(a4, a5, 0b00110001)); + + p0 = _mm256_mul_ps(p0, scale); + p1 = _mm256_mul_ps(p1, scale); + p2 = _mm256_mul_ps(p2, scale); + p3 = _mm256_mul_ps(p3, scale); + p4 = _mm256_mul_ps(p4, scale); + p5 = _mm256_mul_ps(p5, scale); + + __m256i res0 = _mm256_packs_epi32(_mm256_cvtps_epi32(p0), _mm256_cvtps_epi32(p1)); + __m256i res1 = _mm256_packs_epi32(_mm256_cvtps_epi32(p2), _mm256_cvtps_epi32(p3)); + __m256i res2 = _mm256_packs_epi32(_mm256_cvtps_epi32(p4), _mm256_cvtps_epi32(p5)); + + _mm256_storeu_si256((__m256i*)(outdata + 0), res0); + _mm256_storeu_si256((__m256i*)(outdata + 16), res1); + _mm256_storeu_si256((__m256i*)(outdata + 32), res2); + outdata += 48; + } + +#undef CONV_3CF32 + + #include "conv_3cf32_ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_3cf32_ci16_generic.inc b/src/lib/xdsp/templates/conv_3cf32_ci16_generic.inc new file mode 100644 index 00000000..8d1abc09 --- /dev/null +++ b/src/lib/xdsp/templates/conv_3cf32_ci16_generic.inc @@ -0,0 +1,14 @@ + for (; i >= 24; i -= 24, indata_0 += 2, indata_1 += 2, indata_2 += 2, outdata += 6) { + int16_t a = indata_0[0] / CONV_SCALE; + int16_t b = indata_0[1] / CONV_SCALE; + int16_t c = indata_1[0] / CONV_SCALE; + int16_t d = indata_1[1] / CONV_SCALE; + int16_t e = indata_2[0] / CONV_SCALE; + int16_t f = indata_2[1] / CONV_SCALE; + + uint64_t v = (uint64_t)(uint16_t)a | ((uint64_t)(uint16_t)b << 16) | ((uint64_t)(uint16_t)c << 32) | ((uint64_t)(uint16_t)d << 48); + uint32_t w = (uint32_t)(uint16_t)e | ((uint32_t)(uint16_t)f << 16); + *(uint64_t*)outdata = v; + *(uint32_t*)(outdata + 4) = w; + } + // do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_3cf32_ci16_generic.t b/src/lib/xdsp/templates/conv_3cf32_ci16_generic.t new file mode 100644 index 00000000..61704314 --- /dev/null +++ b/src/lib/xdsp/templates/conv_3cf32_ci16_generic.t @@ -0,0 +1,21 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + const void *__restrict indata_2_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 2) < i) + i = (outdatabsz * 2); + + const float* indata_0 = (const float*)indata_0_p; + const float* indata_1 = (const float*)indata_1_p; + const float* indata_2 = (const float*)indata_2_p; + + int16_t* outdata = (int16_t*)outdata_p; + #include "conv_3cf32_ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_3ci16_ci16_avx2.t b/src/lib/xdsp/templates/conv_3ci16_ci16_avx2.t new file mode 100644 index 00000000..0f373e82 --- /dev/null +++ b/src/lib/xdsp/templates/conv_3ci16_ci16_avx2.t @@ -0,0 +1,75 @@ + +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + const void *__restrict indata_2_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + const int16_t* indata_0 = (int16_t*)indata_0_p; + const int16_t* indata_1 = (int16_t*)indata_1_p; + const int16_t* indata_2 = (int16_t*)indata_2_p; + int16_t* outdata = (int16_t*)outdata_p; + + const __m256i mask0 = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0); + const __m256i mask1 = _mm256_set_epi32(0, -1, 0, -1, 0, -1, 0, -1); + + for (; i >= 32 * 3; i -= 32 * 3) + { + __m256i r0 = _mm256_load_si256((__m256i*)indata_0); + __m256i r1 = _mm256_load_si256((__m256i*)indata_1); + __m256i r2 = _mm256_load_si256((__m256i*)indata_2); + + indata_0 += 16; + indata_1 += 16; + indata_2 += 16; + + //(0) + __m256d ulo, uhi; + ulo = _mm256_castsi256_pd(_mm256_unpacklo_epi32(r0, r1)); + uhi = _mm256_castsi256_pd(_mm256_unpackhi_epi32(r0, r1)); + __m256i rs0 = _mm256_castpd_si256(_mm256_shuffle_pd(ulo, uhi, 0b0000)); + + ulo = _mm256_castsi256_pd(_mm256_unpacklo_epi32(r1, r2)); + uhi = _mm256_castsi256_pd(_mm256_unpackhi_epi32(r1, r2)); + __m256i rs2 = _mm256_castpd_si256(_mm256_shuffle_pd(ulo, uhi, 0b1111)); + + __m256i rs1 = _mm256_or_si256(_mm256_and_si256(r0, mask0), _mm256_and_si256(r2, mask1)); + + //(1) + __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs0), _mm256_castsi256_pd(rs1), 0b0000)); + __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs0), _mm256_castsi256_pd(rs1), 0b1111)); + __m256i z2 = rs2; + + __m256i zz0 = z0; + __m256i zz1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b0000)); + __m256i zz2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b1111)); + + zz1 = _mm256_shuffle_epi32(zz1, _MM_SHUFFLE(1,0,3,2)); + + //(2) + __m256i x0 = _mm256_permute2x128_si256(zz0, zz1, 0b00100000); + __m256i x1 = _mm256_permute2x128_si256(zz0, zz1, 0b00110001); + __m256i x2 = zz2; + + __m256i xx0 = x0; + __m256i xx1 = _mm256_permute2x128_si256(x1, x2, 0b00100000); + __m256i xx2 = _mm256_permute2x128_si256(x1, x2, 0b00110001); + + xx1 = _mm256_permute2x128_si256(xx1, xx1, 0x1); + + _mm256_storeu_si256((__m256i*)(outdata + 0), xx0); + _mm256_storeu_si256((__m256i*)(outdata + 16), xx1); + _mm256_storeu_si256((__m256i*)(outdata + 32), xx2); + outdata += 48; + } + + #include "conv_3ci16_ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_3ci16_ci16_generic.inc b/src/lib/xdsp/templates/conv_3ci16_ci16_generic.inc new file mode 100644 index 00000000..162ee1fa --- /dev/null +++ b/src/lib/xdsp/templates/conv_3ci16_ci16_generic.inc @@ -0,0 +1,14 @@ + for (; i >= 12; i -= 12, indata_0 += 2, indata_1 += 2, indata_2 += 2, outdata += 6) { + int16_t a = indata_0[0]; + int16_t b = indata_0[1]; + int16_t c = indata_1[0]; + int16_t d = indata_1[1]; + int16_t e = indata_2[0]; + int16_t f = indata_2[1]; + + uint64_t v = (uint64_t)(uint16_t)a | ((uint64_t)(uint16_t)b << 16) | ((uint64_t)(uint16_t)c << 32) | ((uint64_t)(uint16_t)d << 48); + uint32_t w = (uint32_t)(uint16_t)e | ((uint32_t)(uint16_t)f << 16); + *(uint64_t*)outdata = v; + *(uint32_t*)(outdata + 4) = w; + } + // do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_3ci16_ci16_generic.t b/src/lib/xdsp/templates/conv_3ci16_ci16_generic.t new file mode 100644 index 00000000..396ffc43 --- /dev/null +++ b/src/lib/xdsp/templates/conv_3ci16_ci16_generic.t @@ -0,0 +1,22 @@ + +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + const void *__restrict indata_2_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + const int16_t* indata_0 = (int16_t*)indata_0_p; + const int16_t* indata_1 = (int16_t*)indata_1_p; + const int16_t* indata_2 = (int16_t*)indata_2_p; + + int16_t* outdata = (int16_t*)outdata_p; + #include "conv_3ci16_ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_4cf32_ci12_avx2.inc b/src/lib/xdsp/templates/conv_4cf32_ci12_avx2.inc new file mode 100644 index 00000000..0c6bd4b3 --- /dev/null +++ b/src/lib/xdsp/templates/conv_4cf32_ci12_avx2.inc @@ -0,0 +1,25 @@ +const __m256 scale = _mm256_set1_ps(1.0f / CONV_SCALE); + +#define CONVERT_4CF32_CI12_BLOCK(f0, f1, f2, f3) \ +{ \ + __m256i i0 = _mm256_cvtps_epi32(_mm256_mul_ps(f0, scale)); \ + __m256i i1 = _mm256_cvtps_epi32(_mm256_mul_ps(f1, scale)); \ + __m256i i2 = _mm256_cvtps_epi32(_mm256_mul_ps(f2, scale)); \ + __m256i i3 = _mm256_cvtps_epi32(_mm256_mul_ps(f3, scale)); \ + \ + /* 4-way CF32 deinterleave (see conv_4cf32_ci16_avx2.t */ \ + \ + __m256i ii0 = _mm256_shuffle_epi32(_mm256_packs_epi32(i0, i1), _MM_SHUFFLE(3,1,2,0)); \ + __m256i ii1 = _mm256_shuffle_epi32(_mm256_packs_epi32(i2, i3), _MM_SHUFFLE(3,1,2,0)); \ + \ + __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(ii0), _mm256_castsi256_pd(ii1), 0b0000)); \ + __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(ii0), _mm256_castsi256_pd(ii1), 0b1111)); \ + \ + __m256i d0 = _mm256_permute2x128_si256(z0, z1, 0b00100000); \ + __m256i d1 = _mm256_permute2x128_si256(z0, z1, 0b00110001); \ + \ + /* Convert linear data to CI12 */ \ + \ + CONVERT_I16_I12_BLOCK(d0, out64); \ + CONVERT_I16_I12_BLOCK(d1, out64); \ +} diff --git a/src/lib/xdsp/templates/conv_4cf32_ci12_avx2.t b/src/lib/xdsp/templates/conv_4cf32_ci12_avx2.t index d95f24f3..6e93f6b2 100644 --- a/src/lib/xdsp/templates/conv_4cf32_ci12_avx2.t +++ b/src/lib/xdsp/templates/conv_4cf32_ci12_avx2.t @@ -18,9 +18,8 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, uint64_t *out64 = (uint64_t*)outdata_p; - const __m256 scale = _mm256_set1_ps(1.0f / CONV_SCALE); - #include "conv_i16_i12_avx2.inc" +#include "conv_4cf32_ci12_avx2.inc" __m256 f0, f1, f2, f3; @@ -31,70 +30,22 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, f2 = _mm256_loadu_ps(indata_2); f3 = _mm256_loadu_ps(indata_3); - __m256i i0 = _mm256_cvtps_epi32(_mm256_mul_ps(f0, scale)); - __m256i i1 = _mm256_cvtps_epi32(_mm256_mul_ps(f1, scale)); - __m256i i2 = _mm256_cvtps_epi32(_mm256_mul_ps(f2, scale)); - __m256i i3 = _mm256_cvtps_epi32(_mm256_mul_ps(f3, scale)); - - /* 4-way CF32 deinterleave (see conv_4cf32_ci16_avx2.t */ - - __m256i ii0 = _mm256_shuffle_epi32(_mm256_packs_epi32(i0, i1), _MM_SHUFFLE(3,1,2,0)); - __m256i ii1 = _mm256_shuffle_epi32(_mm256_packs_epi32(i2, i3), _MM_SHUFFLE(3,1,2,0)); - - __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(ii0), _mm256_castsi256_pd(ii1), 0b0000)); - __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(ii0), _mm256_castsi256_pd(ii1), 0b1111)); - - __m256i d0 = _mm256_permute2x128_si256(z0, z1, 0b00100000); - __m256i d1 = _mm256_permute2x128_si256(z0, z1, 0b00110001); - - /* Convert linear data to CI12 */ - - CONVERT_I16_I12_BLOCK(d0, out64); - CONVERT_I16_I12_BLOCK(d1, out64); + CONVERT_4CF32_CI12_BLOCK(f0, f1, f2, f3); indata_0 += 8; indata_1 += 8; indata_2 += 8; indata_3 += 8; - i -= 32*4; } +#undef CONVERT_4CF32_CI12_BLOCK #undef CONVERT_I16_I12_BLOCK #undef I16RND #define I16RND(x) x > 0 ? (int16_t)(x + 0.5f) : (int16_t)(x - 0.5f) uint8_t* outdata = (uint8_t*)out64; - - for (; i >= 32; i -= 32) { - - float f0 = *(indata_0++) / CONV_SCALE; - float f1 = *(indata_0++) / CONV_SCALE; - float f2 = *(indata_1++) / CONV_SCALE; - float f3 = *(indata_1++) / CONV_SCALE; - float f4 = *(indata_2++) / CONV_SCALE; - float f5 = *(indata_2++) / CONV_SCALE; - float f6 = *(indata_3++) / CONV_SCALE; - float f7 = *(indata_3++) / CONV_SCALE; - - wu_i16u32_t a0 = {{I16RND(f0), I16RND(f1)}}; - wu_i16u32_t a1 = {{I16RND(f2), I16RND(f3)}}; - wu_i16u32_t a2 = {{I16RND(f4), I16RND(f5)}}; - wu_i16u32_t a3 = {{I16RND(f6), I16RND(f7)}}; - - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - wu_u32b_t c2 = {(a2.u & 0xfff00000) | ((a2.u << 4) & 0x000fff00)}; - wu_u32b_t c3 = {(a3.u & 0xfff00000) | ((a3.u << 4) & 0x000fff00)}; - - const wu_u32b_t arr[] = {c0, c1, c2, c3}; - for(unsigned j = 0; j < 4; ++j) - { - *(outdata++) = arr[j].b[1]; - *(outdata++) = arr[j].b[2]; - *(outdata++) = arr[j].b[3]; - } - } + #include "conv_4cf32_ci12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_4cf32_ci12_avx512bw.inc b/src/lib/xdsp/templates/conv_4cf32_ci12_avx512bw.inc new file mode 100644 index 00000000..5d2c54d8 --- /dev/null +++ b/src/lib/xdsp/templates/conv_4cf32_ci12_avx512bw.inc @@ -0,0 +1,22 @@ +const __m512 scale = _mm512_set1_ps(1.0f / CONV_SCALE); +const __m512i imask0 = _mm512_set_epi32(23,21,7,5, 22,20,6,4, 19,17,3,1, 18,16,2,0); +const __m512i imask1 = _mm512_set_epi32(31,29,15,13, 30,28,14,12, 27,25,11,9, 26,24,10,8); + +#define CONVERT_4CF32_CI12_BLOCK(f0, f1, f2, f3) \ +{ \ + __m512i i0 = _mm512_cvtps_epi32(_mm512_mul_ps(f0, scale)); \ + __m512i i1 = _mm512_cvtps_epi32(_mm512_mul_ps(f1, scale)); \ + __m512i i2 = _mm512_cvtps_epi32(_mm512_mul_ps(f2, scale)); \ + __m512i i3 = _mm512_cvtps_epi32(_mm512_mul_ps(f3, scale)); \ + \ + __m512i ii0 = _mm512_packs_epi32(i0, i1); \ + __m512i ii1 = _mm512_packs_epi32(i2, i3); \ + \ + __m512i x0 = _mm512_permutex2var_epi32(ii0, imask0, ii1); \ + __m512i x1 = _mm512_permutex2var_epi32(ii0, imask1, ii1); \ + \ + /* Convert linear data to CI12 */ \ + \ + CONVERT_I16_I12_BLOCK(x0, out64); \ + CONVERT_I16_I12_BLOCK(x1, out64); \ +} diff --git a/src/lib/xdsp/templates/conv_4cf32_ci12_avx512bw.t b/src/lib/xdsp/templates/conv_4cf32_ci12_avx512bw.t new file mode 100644 index 00000000..b04a426f --- /dev/null +++ b/src/lib/xdsp/templates/conv_4cf32_ci12_avx512bw.t @@ -0,0 +1,81 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + const void *__restrict indata_2_p, + const void *__restrict indata_3_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 8 / 3) < i) + i = (outdatabsz * 8 / 3); + + const float* indata_0 = (const float*)indata_0_p; + const float* indata_1 = (const float*)indata_1_p; + const float* indata_2 = (const float*)indata_2_p; + const float* indata_3 = (const float*)indata_3_p; + + uint64_t *out64 = (uint64_t*)outdata_p; + + //AVX512 block + { + #include "conv_i16_i12_avx512bw.inc" + #include "conv_4cf32_ci12_avx512bw.inc" + + __m512 f0, f1, f2, f3; + + for(; i >= 64*4; i -= 64*4) + { + f0 = _mm512_loadu_ps(indata_0); + f1 = _mm512_loadu_ps(indata_1); + f2 = _mm512_loadu_ps(indata_2); + f3 = _mm512_loadu_ps(indata_3); + + CONVERT_4CF32_CI12_BLOCK(f0, f1, f2, f3); + + indata_0 += 16; + indata_1 += 16; + indata_2 += 16; + indata_3 += 16; + } + + #undef CONVERT_4CF32_CI12_BLOCK + #undef CONVERT_I16_I12_BLOCK + } + + //AVX2 block + { + #include "conv_i16_i12_avx2.inc" + #include "conv_4cf32_ci12_avx2.inc" + + if (i >= 32*4) + { + __m256 f0 = _mm256_loadu_ps(indata_0); + __m256 f1 = _mm256_loadu_ps(indata_1); + __m256 f2 = _mm256_loadu_ps(indata_2); + __m256 f3 = _mm256_loadu_ps(indata_3); + + CONVERT_4CF32_CI12_BLOCK(f0, f1, f2, f3); + + indata_0 += 8; + indata_1 += 8; + indata_2 += 8; + indata_3 += 8; + i -= 32*4; + } + + #undef CONVERT_4CF32_CI12_BLOCK + #undef CONVERT_I16_I12_BLOCK + } + + //Generic block + { + #undef I16RND + #define I16RND(x) x > 0 ? (int16_t)(x + 0.5f) : (int16_t)(x - 0.5f) + + uint8_t* outdata = (uint8_t*)out64; + #include "conv_4cf32_ci12_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_4cf32_ci12_generic.inc b/src/lib/xdsp/templates/conv_4cf32_ci12_generic.inc new file mode 100644 index 00000000..05585c3a --- /dev/null +++ b/src/lib/xdsp/templates/conv_4cf32_ci12_generic.inc @@ -0,0 +1,29 @@ +for (; i >= 32; i -= 32) { + + float f0 = *(indata_0++) / CONV_SCALE; + float f1 = *(indata_0++) / CONV_SCALE; + float f2 = *(indata_1++) / CONV_SCALE; + float f3 = *(indata_1++) / CONV_SCALE; + float f4 = *(indata_2++) / CONV_SCALE; + float f5 = *(indata_2++) / CONV_SCALE; + float f6 = *(indata_3++) / CONV_SCALE; + float f7 = *(indata_3++) / CONV_SCALE; + + wu_i16u32_t a0 = {{I16RND(f0), I16RND(f1)}}; + wu_i16u32_t a1 = {{I16RND(f2), I16RND(f3)}}; + wu_i16u32_t a2 = {{I16RND(f4), I16RND(f5)}}; + wu_i16u32_t a3 = {{I16RND(f6), I16RND(f7)}}; + + wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; + wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; + wu_u32b_t c2 = {(a2.u & 0xfff00000) | ((a2.u << 4) & 0x000fff00)}; + wu_u32b_t c3 = {(a3.u & 0xfff00000) | ((a3.u << 4) & 0x000fff00)}; + + const wu_u32b_t arr[] = {c0, c1, c2, c3}; + for(unsigned j = 0; j < 4; ++j) + { + *(outdata++) = arr[j].b[1]; + *(outdata++) = arr[j].b[2]; + *(outdata++) = arr[j].b[3]; + } +} \ No newline at end of file diff --git a/src/lib/xdsp/templates/conv_4cf32_ci12_generic.t b/src/lib/xdsp/templates/conv_4cf32_ci12_generic.t index 7d67575a..ba776e84 100644 --- a/src/lib/xdsp/templates/conv_4cf32_ci12_generic.t +++ b/src/lib/xdsp/templates/conv_4cf32_ci12_generic.t @@ -17,35 +17,6 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, const float* indata_3 = (const float*)indata_3_p; uint8_t* outdata = (uint8_t*)outdata_p; - - for (; i >= 32; i -= 32) { - - float f0 = *(indata_0++) / CONV_SCALE; - float f1 = *(indata_0++) / CONV_SCALE; - float f2 = *(indata_1++) / CONV_SCALE; - float f3 = *(indata_1++) / CONV_SCALE; - float f4 = *(indata_2++) / CONV_SCALE; - float f5 = *(indata_2++) / CONV_SCALE; - float f6 = *(indata_3++) / CONV_SCALE; - float f7 = *(indata_3++) / CONV_SCALE; - - wu_i16u32_t a0 = {{I16RND(f0), I16RND(f1)}}; - wu_i16u32_t a1 = {{I16RND(f2), I16RND(f3)}}; - wu_i16u32_t a2 = {{I16RND(f4), I16RND(f5)}}; - wu_i16u32_t a3 = {{I16RND(f6), I16RND(f7)}}; - - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - wu_u32b_t c2 = {(a2.u & 0xfff00000) | ((a2.u << 4) & 0x000fff00)}; - wu_u32b_t c3 = {(a3.u & 0xfff00000) | ((a3.u << 4) & 0x000fff00)}; - - const wu_u32b_t arr[] = {c0, c1, c2, c3}; - for(unsigned j = 0; j < 4; ++j) - { - *(outdata++) = arr[j].b[1]; - *(outdata++) = arr[j].b[2]; - *(outdata++) = arr[j].b[3]; - } - } + #include "conv_4cf32_ci12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_4ci16_ci12_avx2.t b/src/lib/xdsp/templates/conv_4ci16_ci12_avx2.t index 932552c6..c93039b5 100644 --- a/src/lib/xdsp/templates/conv_4ci16_ci12_avx2.t +++ b/src/lib/xdsp/templates/conv_4ci16_ci12_avx2.t @@ -62,35 +62,6 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, #undef CONVERT_I16_I12_BLOCK uint8_t* outdata = (uint8_t*)out64; - - for (; i >= 16; i -= 16) { - - const int16_t i0 = *indata_0++; - const int16_t q0 = *indata_0++; - const int16_t i1 = *indata_1++; - const int16_t q1 = *indata_1++; - const int16_t i2 = *indata_2++; - const int16_t q2 = *indata_2++; - const int16_t i3 = *indata_3++; - const int16_t q3 = *indata_3++; - - wu_i16u32_t a0 = {{i0, q0}}; - wu_i16u32_t a1 = {{i1, q1}}; - wu_i16u32_t a2 = {{i2, q2}}; - wu_i16u32_t a3 = {{i3, q3}}; - - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - wu_u32b_t c2 = {(a2.u & 0xfff00000) | ((a2.u << 4) & 0x000fff00)}; - wu_u32b_t c3 = {(a3.u & 0xfff00000) | ((a3.u << 4) & 0x000fff00)}; - - const wu_u32b_t arr[] = {c0, c1, c2, c3}; - for(unsigned j = 0; j < 4; ++j) - { - *(outdata++) = arr[j].b[1]; - *(outdata++) = arr[j].b[2]; - *(outdata++) = arr[j].b[3]; - } - } + #include "conv_4ci16_ci12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_4ci16_ci12_avx512bw.t b/src/lib/xdsp/templates/conv_4ci16_ci12_avx512bw.t new file mode 100644 index 00000000..113c2adb --- /dev/null +++ b/src/lib/xdsp/templates/conv_4ci16_ci12_avx512bw.t @@ -0,0 +1,118 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, + const void *__restrict indata_1_p, + const void *__restrict indata_2_p, + const void *__restrict indata_3_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 4 / 3) < i) + i = (outdatabsz * 4 / 3); + + const int16_t* indata_0 = (const int16_t*)indata_0_p; + const int16_t* indata_1 = (const int16_t*)indata_1_p; + const int16_t* indata_2 = (const int16_t*)indata_2_p; + const int16_t* indata_3 = (const int16_t*)indata_3_p; + + uint64_t *out64 = (uint64_t*)outdata_p; + + //AVX512 block + { + #include "conv_i16_i12_avx512bw.inc" + + __m512i v0, v1, v2, v3; + const __m512i idx = _mm512_set_epi32(15,11,7,3, 14,10,6,2, 13,9,5,1, 12,8,4,0); + const __m512i imask0 = _mm512_set_epi32(30,14,28,12,26,10,24,8,22,6,20,4,18,2,16,0); + const __m512i imask1 = _mm512_set_epi32(31,15,29,13,27,11,25,9,23,7,21,5,19,3,17,1); + + for (; i >= 64*4; i -= 64*4) + { + v0 = _mm512_loadu_si512((__m512i*)indata_0); + v1 = _mm512_loadu_si512((__m512i*)indata_1); + v2 = _mm512_loadu_si512((__m512i*)indata_2); + v3 = _mm512_loadu_si512((__m512i*)indata_3); + + indata_0 += 32; + indata_1 += 32; + indata_2 += 32; + indata_3 += 32; + + __m512i a0 = _mm512_permutexvar_epi32(idx, v0); + __m512i a1 = _mm512_permutexvar_epi32(idx, v1); + __m512i a2 = _mm512_permutexvar_epi32(idx, v2); + __m512i a3 = _mm512_permutexvar_epi32(idx, v3); + + __m512i b0 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a0), _mm512_castsi512_pd(a2), 0b00000000)); + __m512i b2 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a0), _mm512_castsi512_pd(a2), 0b11111111)); + __m512i b1 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a1), _mm512_castsi512_pd(a3), 0b00000000)); + __m512i b3 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a1), _mm512_castsi512_pd(a3), 0b11111111)); + + __m512i c0 = _mm512_permutex2var_epi32(b0, imask0, b1); + __m512i c1 = _mm512_permutex2var_epi32(b0, imask1, b1); + __m512i c2 = _mm512_permutex2var_epi32(b2, imask0, b3); + __m512i c3 = _mm512_permutex2var_epi32(b2, imask1, b3); + + CONVERT_I16_I12_BLOCK(c0, out64); + CONVERT_I16_I12_BLOCK(c1, out64); + CONVERT_I16_I12_BLOCK(c2, out64); + CONVERT_I16_I12_BLOCK(c3, out64); + } + + #undef CONVERT_I16_I12_BLOCK + } + + //AVX2 block + { + #include "conv_i16_i12_avx2.inc" + + if(i >= 32*4) + { + const __m256i permmask1 = _mm256_set_epi32(7,3,5,1,6,2,4,0); + + __m256i v0 = _mm256_loadu_si256((__m256i*)indata_0); + __m256i v1 = _mm256_loadu_si256((__m256i*)indata_1); + __m256i v2 = _mm256_loadu_si256((__m256i*)indata_2); + __m256i v3 = _mm256_loadu_si256((__m256i*)indata_3); + + __m256i z0 = _mm256_permute2x128_si256(v0, v1, 0b00100000); + __m256i z2 = _mm256_permute2x128_si256(v0, v1, 0b00110001); + __m256i z1 = _mm256_permute2x128_si256(v2, v3, 0b00100000); + __m256i z3 = _mm256_permute2x128_si256(v2, v3, 0b00110001); + + __m256i i0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z0), _mm256_castsi256_pd(z1), 0b0000)); + __m256i i1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z0), _mm256_castsi256_pd(z1), 0b1111)); + __m256i i2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z2), _mm256_castsi256_pd(z3), 0b0000)); + __m256i i3 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z2), _mm256_castsi256_pd(z3), 0b1111)); + + i0 = _mm256_permutevar8x32_epi32(i0, permmask1); + i1 = _mm256_permutevar8x32_epi32(i1, permmask1); + i2 = _mm256_permutevar8x32_epi32(i2, permmask1); + i3 = _mm256_permutevar8x32_epi32(i3, permmask1); + + /* Convert linear data to CI12 */ + + CONVERT_I16_I12_BLOCK(i0, out64); + CONVERT_I16_I12_BLOCK(i1, out64); + CONVERT_I16_I12_BLOCK(i2, out64); + CONVERT_I16_I12_BLOCK(i3, out64); + + indata_0 += 16; + indata_1 += 16; + indata_2 += 16; + indata_3 += 16; + + i -= 32*4; + } + + #undef CONVERT_I16_I12_BLOCK + } + + //Generic block + { + uint8_t* outdata = (uint8_t*)out64; + #include "conv_4ci16_ci12_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_4ci16_ci12_generic.inc b/src/lib/xdsp/templates/conv_4ci16_ci12_generic.inc new file mode 100644 index 00000000..bfb853e9 --- /dev/null +++ b/src/lib/xdsp/templates/conv_4ci16_ci12_generic.inc @@ -0,0 +1,29 @@ +for (; i >= 16; i -= 16) +{ + const int16_t i0 = *indata_0++; + const int16_t q0 = *indata_0++; + const int16_t i1 = *indata_1++; + const int16_t q1 = *indata_1++; + const int16_t i2 = *indata_2++; + const int16_t q2 = *indata_2++; + const int16_t i3 = *indata_3++; + const int16_t q3 = *indata_3++; + + wu_i16u32_t a0 = {{i0, q0}}; + wu_i16u32_t a1 = {{i1, q1}}; + wu_i16u32_t a2 = {{i2, q2}}; + wu_i16u32_t a3 = {{i3, q3}}; + + wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; + wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; + wu_u32b_t c2 = {(a2.u & 0xfff00000) | ((a2.u << 4) & 0x000fff00)}; + wu_u32b_t c3 = {(a3.u & 0xfff00000) | ((a3.u << 4) & 0x000fff00)}; + + const wu_u32b_t arr[] = {c0, c1, c2, c3}; + for(unsigned j = 0; j < 4; ++j) + { + *(outdata++) = arr[j].b[1]; + *(outdata++) = arr[j].b[2]; + *(outdata++) = arr[j].b[3]; + } +} diff --git a/src/lib/xdsp/templates/conv_4ci16_ci12_generic.t b/src/lib/xdsp/templates/conv_4ci16_ci12_generic.t index efece148..511235eb 100644 --- a/src/lib/xdsp/templates/conv_4ci16_ci12_generic.t +++ b/src/lib/xdsp/templates/conv_4ci16_ci12_generic.t @@ -18,34 +18,6 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, uint8_t* outdata = (uint8_t*)outdata_p; - for (; i >= 16; i -= 16) { - - const int16_t i0 = *indata_0++; - const int16_t q0 = *indata_0++; - const int16_t i1 = *indata_1++; - const int16_t q1 = *indata_1++; - const int16_t i2 = *indata_2++; - const int16_t q2 = *indata_2++; - const int16_t i3 = *indata_3++; - const int16_t q3 = *indata_3++; - - wu_i16u32_t a0 = {{i0, q0}}; - wu_i16u32_t a1 = {{i1, q1}}; - wu_i16u32_t a2 = {{i2, q2}}; - wu_i16u32_t a3 = {{i3, q3}}; - - wu_u32b_t c0 = {(a0.u & 0xfff00000) | ((a0.u << 4) & 0x000fff00)}; - wu_u32b_t c1 = {(a1.u & 0xfff00000) | ((a1.u << 4) & 0x000fff00)}; - wu_u32b_t c2 = {(a2.u & 0xfff00000) | ((a2.u << 4) & 0x000fff00)}; - wu_u32b_t c3 = {(a3.u & 0xfff00000) | ((a3.u << 4) & 0x000fff00)}; - - const wu_u32b_t arr[] = {c0, c1, c2, c3}; - for(unsigned j = 0; j < 4; ++j) - { - *(outdata++) = arr[j].b[1]; - *(outdata++) = arr[j].b[2]; - *(outdata++) = arr[j].b[3]; - } - } + #include "conv_4ci16_ci12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_4ci16_ci16_neon.t b/src/lib/xdsp/templates/conv_4ci16_ci16_neon.t index e79fe7d0..9aa6dfe3 100644 --- a/src/lib/xdsp/templates/conv_4ci16_ci16_neon.t +++ b/src/lib/xdsp/templates/conv_4ci16_ci16_neon.t @@ -20,8 +20,9 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_0_p, while(i >= 64) { + uint32x4x4_t z = {vld1q_u32(indata_0), vld1q_u32(indata_1), vld1q_u32(indata_2), vld1q_u32(indata_3)}; vst4q_u32((uint32_t*)outdata, - (uint32x4x4_t){ {vld1q_u32(indata_0), vld1q_u32(indata_1), vld1q_u32(indata_2), vld1q_u32(indata_3)} }); + z); i -= 64; indata_0 += 4; diff --git a/src/lib/xdsp/templates/conv_ci12_2cf32_avx2.t b/src/lib/xdsp/templates/conv_ci12_2cf32_avx2.t index d1b0a4e1..66881792 100644 --- a/src/lib/xdsp/templates/conv_ci12_2cf32_avx2.t +++ b/src/lib/xdsp/templates/conv_ci12_2cf32_avx2.t @@ -14,128 +14,51 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, float* outdata_0 = (float*)outdata_0_p; float* outdata_1 = (float*)outdata_1_p; - const __m256 scale = _mm256_set1_ps(CONV_SCALE); - const __m256i load_mask = _mm256_set_epi64x(0, -1, -1, -1); + #include "conv_i12_i16_avx2.inc" + #include "conv_i12_f32_avx2.inc" - const __m256i mask0 = _mm256_set1_epi64x(0xfff00000fff00000); - const __m256i mask1 = _mm256_set1_epi64x(0x0000fff00000fff0); - - const __m256i permmask = _mm256_set_epi32(5, 4, 3, 7, 6, 2, 1, 0); - const __m256i shfl = _mm256_set_epi8( - 0x0f, 0x0e, 0x0d, 0x80, 0x09, 0x08, 0x07, 0x80, - 0x0c, 0x0b, 0x0a, 0x80, 0x06, 0x05, 0x04, 0x80, - 0x0b, 0x0a, 0x09, 0x80, 0x05, 0x04, 0x03, 0x80, - 0x08, 0x07, 0x06, 0x80, 0x02, 0x01, 0x00, 0x80); + while(i >= 96) + { + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); + __m256i y2 = _mm256_maskload_epi64((const long long*)(in + 6), load_mask); + __m256i y3 = _mm256_maskload_epi64((const long long*)(in + 9), load_mask); -/* -* reg: -* | (3) | (2) | (1) | (0) | -* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -* | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | -* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -* | 0 0 0 0 0 0 0 0 | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | -* -* v0: -* | | | | | -* | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | 0 0 0 0 0 0 0 0 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | -* r: -* | | | | | -* | f15 | f14 | 0 | f11 | f10 | 0 | f13 | f12 | 0 | f9 | f8 | 0 | f7 | f6 | 0 | f3 | f2 | 0 | f5 | f4 | 0 | f1 | f0 | 0 | -* | f15 | f14 | 0 | f11 | f10 | 0 | f7 | f6 | 0 | f3 | f2 | 0 | f13 | f12 | 0 | f9 | f8 | 0 | f5 | f4 | 0 | f1 | f0 | 0 | -* r0: -* | | | | | -* | f15 |0| 00 00 | f11 |0| 00 00 | f7 |0| 00 00 | f3 |0| 00 00 | f13 |0| 00 00 | f9 |0| 00 00 | f5 |0| 00 00 | f1 |0| 00 00 | - r1: -* | | | | | -* | 00 00 | f14 |0| 00 00 | f10 |0| 00 00 | f6 |0| 00 00 | f2 |0| 00 00 | f12 |0| 00 00 | f8 |0| 00 00 | f4 |0| 00 00 | f0 |0| -* result: -* | | | | | -* | f15 |0| f14 |0| f11 |0| f10 |0| f7 |0| f6 |0| f3 |0| f2 |0| f13 |0| f12 |0| f9 |0| f8 |0| f5 |0| f4 |0| f1 |0| f0 |0| -*/ + __m256 r0, r1; -#define CONVERT_I12_F32_BLOCK(reg) \ - { \ - __m256i v0 = _mm256_permutevar8x32_epi32(reg, permmask); \ - __m256i r = _mm256_shuffle_epi8(v0, shfl); \ - r = _mm256_permute4x64_epi64(r, _MM_SHUFFLE(3, 1, 2, 0)); \ - \ - __m256i r0 = _mm256_and_si256(r, mask0); \ - __m256i r1 = _mm256_and_si256(_mm256_srli_epi64(r, 4), mask1); \ - __m256i result = _mm256_or_si256(r0, r1); \ - \ - __m256i d0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(result)); \ - __m256i d1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(result, 1)); \ - \ - __m256 f0 = _mm256_cvtepi32_ps(d0); /* 4 1|2 */ \ - __m256 f1 = _mm256_cvtepi32_ps(d1); /* 4 1|2 */ \ - \ - f0 = _mm256_mul_ps(f0, scale); /* 4 1|2 */ \ - f1 = _mm256_mul_ps(f1, scale); /* 4 1|2 */ \ - \ - _MM256_STOREX_PS(outdata_0, f0); outdata_0 += 8; /* 1 1|2 */ \ - _MM256_STOREX_PS(outdata_1, f1); outdata_1 += 8; /* 1 1|2 */ \ - } -// CONVERT_I12_F32_BLOCK end lat 40 + CONVERT_CI12_2CF32_BLOCK_OPT(y0, r0, r1); + _mm256_store_ps(outdata_0 + 0, r0); + _mm256_store_ps(outdata_1 + 0, r1); - __m256i y0, y1, y2, y3; + CONVERT_CI12_2CF32_BLOCK_OPT(y1, r0, r1); + _mm256_store_ps(outdata_0 + 8, r0); + _mm256_store_ps(outdata_1 + 8, r1); - if(i >= 96) - { - y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); // 8 1/3 - y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); // 8 1/3 - y2 = _mm256_maskload_epi64((const long long*)(in + 6), load_mask); // 8 1/3 - y3 = _mm256_maskload_epi64((const long long*)(in + 9), load_mask); // 8 1/3 - in += 12; + CONVERT_CI12_2CF32_BLOCK_OPT(y2, r0, r1); + _mm256_store_ps(outdata_0 + 16, r0); + _mm256_store_ps(outdata_1 + 16, r1); - for (; i >= 2*96; i -= 96) - { - CONVERT_I12_F32_BLOCK(y0); - CONVERT_I12_F32_BLOCK(y1); - CONVERT_I12_F32_BLOCK(y2); - CONVERT_I12_F32_BLOCK(y3); - - y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); // 8 1/3 - y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); // 8 1/3 - y2 = _mm256_maskload_epi64((const long long*)(in + 6), load_mask); // 8 1/3 - y3 = _mm256_maskload_epi64((const long long*)(in + 9), load_mask); // 8 1/3 - in += 12; - } + CONVERT_CI12_2CF32_BLOCK_OPT(y3, r0, r1); + _mm256_store_ps(outdata_0 + 24, r0); + _mm256_store_ps(outdata_1 + 24, r1); i -= 96; - - CONVERT_I12_F32_BLOCK(y0); - CONVERT_I12_F32_BLOCK(y1); - CONVERT_I12_F32_BLOCK(y2); - CONVERT_I12_F32_BLOCK(y3); + in += 12; + outdata_0 += 32; + outdata_1 += 32; } -#undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT const uint8_t *indata = (const uint8_t*)in; - float **dest = &outdata_0; - - while(i >= 3) - { - uint8_t v0 = *(indata++); - uint8_t v1 = *(indata++); - uint8_t v2 = *(indata++); - i -= 3; - - float a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); - float b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); - - *((*dest)++) = a * CONV_SCALE; - *((*dest)++) = b * CONV_SCALE; - - dest = (*dest == outdata_0) ? &outdata_1 : &outdata_0; - } - - if(i >= 2) - { - uint16_t v = *(const uint16_t*)indata; - float a = (int16_t)(v << 4); - *((*dest)++) = a * CONV_SCALE; - i -= 2; - } + #include "conv_ci12_2cf32_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_2cf32_avx512bw.t b/src/lib/xdsp/templates/conv_ci12_2cf32_avx512bw.t new file mode 100644 index 00000000..ccaa216b --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_2cf32_avx512bw.t @@ -0,0 +1,97 @@ +static inline +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + /* 12 bits -> 32 bits => 3 -> 8 */ + if ((outdatabsz * 3 / 8) < i) + i = (outdatabsz * 3 / 8); + + const uint64_t *in = (const uint64_t*)indata_p; + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + + //AVX512 block + { + #include "conv_i12_i16_avx512bw.inc" + #include "conv_i12_f32_avx512bw.inc" + + __m512i y0, y1; + __m512 res0, res1, res2, res3; + + for(; i >= 96; i -= 96) + { + y0 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 0)); + y1 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 6)); + in += 12; + + CONVERT_CI12_2CF32_BLOCK_OPT(y0, res0, res1); + _mm512_store_ps(outdata_0 + 0, res0); + _mm512_store_ps(outdata_1 + 0, res1); + + CONVERT_CI12_2CF32_BLOCK_OPT(y1, res2, res3); + _mm512_store_ps(outdata_0 + 16, res2); + _mm512_store_ps(outdata_1 + 16, res3); + + outdata_0 += 32; + outdata_1 += 32; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT + } + + //AVX2 block + { + #include "conv_i12_i16_avx2.inc" + #include "conv_i12_f32_avx2.inc" + + if(i >= 48) + { + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); + in += 6; + i -= 48; + + __m256 res0, res1, res2, res3; + + CONVERT_CI12_2CF32_BLOCK_OPT(y0, res0, res1); + _mm256_store_ps(outdata_0 + 0, res0); + _mm256_store_ps(outdata_1 + 0, res1); + + CONVERT_CI12_2CF32_BLOCK_OPT(y1, res2, res3); + _mm256_store_ps(outdata_0 + 8, res2); + _mm256_store_ps(outdata_1 + 8, res3); + + outdata_0 += 16; + outdata_1 += 16; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT + } + + //Generic block + { + const uint8_t *indata = (const uint8_t*)in; + #include "conv_ci12_2cf32_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_2cf32_generic.inc b/src/lib/xdsp/templates/conv_ci12_2cf32_generic.inc new file mode 100644 index 00000000..4f7a1a5f --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_2cf32_generic.inc @@ -0,0 +1,25 @@ +float **dest = &outdata_0; + +while(i >= 3) +{ + uint8_t v0 = *(indata++); + uint8_t v1 = *(indata++); + uint8_t v2 = *(indata++); + i -= 3; + + float a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); + float b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); + + *((*dest)++) = a * CONV_SCALE; + *((*dest)++) = b * CONV_SCALE; + + dest = (*dest == outdata_0) ? &outdata_1 : &outdata_0; +} + +if(i >= 2) +{ + uint16_t v = *(const uint16_t*)indata; + float a = (int16_t)(v << 4); + *((*dest)++) = a * CONV_SCALE; + i -= 2; +} diff --git a/src/lib/xdsp/templates/conv_ci12_2cf32_generic.t b/src/lib/xdsp/templates/conv_ci12_2cf32_generic.t index 44c3d84a..1a23fe47 100644 --- a/src/lib/xdsp/templates/conv_ci12_2cf32_generic.t +++ b/src/lib/xdsp/templates/conv_ci12_2cf32_generic.t @@ -31,31 +31,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, *(outdata_1++) = d * CONV_SCALE; } - float **dest = &outdata_0; - - while(i >= 3) - { - uint8_t v0 = *(indata++); - uint8_t v1 = *(indata++); - uint8_t v2 = *(indata++); - i -= 3; - - float a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); - float b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); - - *((*dest)++) = a * CONV_SCALE; - *((*dest)++) = b * CONV_SCALE; - - dest = (*dest == outdata_0) ? &outdata_1 : &outdata_0; - } - - if(i >= 2) - { - uint16_t v = *(const uint16_t*)indata; - float a = (int16_t)(v << 4); - *((*dest)++) = a * CONV_SCALE; - i -= 2; - } + #include "conv_ci12_2cf32_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_2ci16_avx2.t b/src/lib/xdsp/templates/conv_ci12_2ci16_avx2.t index 99715143..78997a03 100644 --- a/src/lib/xdsp/templates/conv_ci12_2ci16_avx2.t +++ b/src/lib/xdsp/templates/conv_ci12_2ci16_avx2.t @@ -80,19 +80,6 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, #undef CONVERT_CI12_2CI16_BLOCK const uint8_t *indata = (const uint8_t*)in; - - for (; i >= 6; i -= 6) - { - /* read 48 bits -> 4 int16 (64 bits) */ - - uint64_t v = *(const uint64_t *)indata; - indata += 6; - - *(outdata_0++) = (int16_t)((v << 4) ); - *(outdata_0++) = (int16_t)((v >> 8) & 0xfff0); - *(outdata_1++) = (int16_t)((v >> 20) & 0xfff0); - *(outdata_1++) = (int16_t)((v >> 32) & 0xfff0); - } - // do nothing with tail + #include "conv_ci12_2ci16_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_2ci16_avx512bw.t b/src/lib/xdsp/templates/conv_ci12_2ci16_avx512bw.t new file mode 100644 index 00000000..d4e152c6 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_2ci16_avx512bw.t @@ -0,0 +1,85 @@ +static inline +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + /* 12 bits -> 16 bits => 3 -> 4 */ + if ((outdatabsz * 3 / 4) < i) + i = (outdatabsz * 3 / 4); + + const uint64_t *in = (const uint64_t*)indata_p; + int16_t* outdata_0 = (int16_t*)outdata_0_p; + int16_t* outdata_1 = (int16_t*)outdata_1_p; + + //AVX512BW block + { + #include "conv_i12_i16_avx512bw.inc" + + __m512i y0, y1; + __m512i rs0, rs1; + + const __m512i imask0 = _mm512_set_epi32(30,28,26,24,22,20,18,16,14,12,10,8,6,4,2,0); + const __m512i imask1 = _mm512_set_epi32(31,29,27,25,23,21,19,17,15,13,11,9,7,5,3,1); + + for(; i >= 96; i -= 96) + { + y0 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 0)); + y1 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 6)); + in += 12; + + CONVERT_I12_I16_BLOCK(y0, rs0); + CONVERT_I12_I16_BLOCK(y1, rs1); + + _mm512_store_si512((__m512i*)outdata_0, _mm512_permutex2var_epi32(rs0, imask0, rs1)); + _mm512_store_si512((__m512i*)outdata_1, _mm512_permutex2var_epi32(rs0, imask1, rs1)); + outdata_0 += 32; + outdata_1 += 32; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + } + + //AVX2 block + { + #include "conv_i12_i16_avx2.inc" + + if(i >= 48) + { + const __m256i imask0 = _mm256_set_epi32(14,12,10,8,6,4,2,0); + const __m256i imask1 = _mm256_set_epi32(15,13,11,9,7,5,3,1); + + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); // 8 1/3 + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); // 8 1/3 + in += 6; + i -= 48; + + __m256i rs0, rs1; + + CONVERT_I12_I16_BLOCK(y0, rs0); + CONVERT_I12_I16_BLOCK(y1, rs1); + + _mm256_store_si256((__m256i*)outdata_0, _mm256_permutex2var_epi32(rs0, imask0, rs1)); + _mm256_store_si256((__m256i*)outdata_1, _mm256_permutex2var_epi32(rs0, imask1, rs1)); + outdata_0 += 16; + outdata_1 += 16; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + } + + //Generic block + { + const uint8_t *indata = (const uint8_t*)in; + #include "conv_ci12_2ci16_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_2ci16_generic.inc b/src/lib/xdsp/templates/conv_ci12_2ci16_generic.inc new file mode 100644 index 00000000..3e1301b2 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_2ci16_generic.inc @@ -0,0 +1,13 @@ + for (; i >= 6; i -= 6) + { + /* read 48 bits -> 4 int16 (64 bits) */ + + uint64_t v = *(const uint64_t *)indata; + indata += 6; + + *(outdata_0++) = (int16_t)((v << 4) ); + *(outdata_0++) = (int16_t)((v >> 8) & 0xfff0); + *(outdata_1++) = (int16_t)((v >> 20) & 0xfff0); + *(outdata_1++) = (int16_t)((v >> 32) & 0xfff0); + } + // do nothing with tail diff --git a/src/lib/xdsp/templates/conv_ci12_2ci16_generic.t b/src/lib/xdsp/templates/conv_ci12_2ci16_generic.t index 7943d01a..33d9fd87 100644 --- a/src/lib/xdsp/templates/conv_ci12_2ci16_generic.t +++ b/src/lib/xdsp/templates/conv_ci12_2ci16_generic.t @@ -14,19 +14,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, int16_t* outdata_0 = (int16_t*)outdata_0_p; int16_t* outdata_1 = (int16_t*)outdata_1_p; - for (; i >= 6; i -= 6) - { - /* read 48 bits -> 4 int16 (64 bits) */ - - uint64_t v = *(const uint64_t *)indata; - indata += 6; - - *(outdata_0++) = (int16_t)((v << 4) ); - *(outdata_0++) = (int16_t)((v >> 8) & 0xfff0); - *(outdata_1++) = (int16_t)((v >> 20) & 0xfff0); - *(outdata_1++) = (int16_t)((v >> 32) & 0xfff0); - } - // do nothing with tail +#include "conv_ci12_2ci16_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_4cf32_avx2.inc b/src/lib/xdsp/templates/conv_ci12_4cf32_avx2.inc new file mode 100644 index 00000000..d95160be --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_4cf32_avx2.inc @@ -0,0 +1,97 @@ +/* +* r0-r1: +* | (3) | (2) | (1) | (0) | +* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +* | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | +* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +* | 0 0 0 0 0 0 0 0 | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | +* | 0 0 0 0 0 0 0 0 | f31 | f30 | f29 | f28 | f27 | f26 | f25 | f24 | f23 | f22 | f21 | f20 | f19 | f18 | f17 | f16 | +* +* y0 -y1: _mm256_permutevar8x32_epi32 +* | | | | | +* | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | 0 0 0 0 0 0 0 0 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | +* | f31 | f30 | f29 | f28 | f27 | f26 | f25 | f24 | 0 0 0 0 0 0 0 0 | f23 | f22 | f21 | f20 | f19 | f18 | f17 | f16 | +* +* y0-y2: _mm256_shuffle_epi8 +* | | | | | +* | f15 | f14 | 0 | f13 | f12 | 0 | f11 | f10 | 0 | f9 | f8 | 0 | f7 | f6 | 0 | f5 | f4 | 0 | f3 | f2 | 0 | f1 | f0 | 0 | +* | f31 | f30 | 0 | f29 | f28 | 0 | f27 | f26 | 0 | f25 | f24 | 0 | f23 | f22 | 0 | f21 | f20 | 0 | f19 | f18 | 0 | f17 | f16 | 0 | +* +* a0-a1: +* | | | | | +* | f15 |0| 00 00 | f13 |0| 00 00 | f11 |0| 00 00 | f9 |0| 00 00 | f7 |0| 00 00 | f5 |0| 00 00 | f3 |0| 00 00 | f1 |0| 00 00 | +* | 00 00 | f14 |0| 00 00 | f12 |0| 00 00 | f10 |0| 00 00 | f8 |0| 00 00 | f6 |0| 00 00 | f4 |0| 00 00 | f2 |0| 00 00 | f0 |0| +* +* i0: _mm256_or_si256(a0, a1) +* i1: _mm256_or_si256(b0, b1) +* | | | | | +* | f15 |0| f14 |0| f13 |0| f12 |0| f11 |0| f10 |0| f9 |0| f8 |0| f7 |0| f6 |0| f5 |0| f4 |0| f3 |0| f2 |0| f1 |0| f0 |0| +* | f31 |0| f30 |0| f29 |0| f28 |0| f27 |0| f26 |0| f25 |0| f24 |0| f23 |0| f22 |0| f21 |0| f20 |0| f19 |0| f18 |0| f17 |0| f16 |0| +* +* i0-i1: _mm256_permutevar8x32_epi32 +* | | | | | +* | f15 |0| f14 |0| f7 |0| f6 |0| f11 |0| f10 |0| f3 |0| f2 |0| f13 |0| f12 |0| f5 |0| f4 |0| f9 |0| f8 |0| f1 |0| f0 |0| +* | f31 |0| f30 |0| f23 |0| f22 |0| f27 |0| f26 |0| f19 |0| f18 |0| f29 |0| f28 |0| f21 |0| f20 |0| f25 |0| f24 |0| f17 |0| f16 |0| +* +* z0-z1: _mm256_castpd_si256 +* | | | | | +* | f27 |0| f26 |0| f19 |0| f18 |0| f11 |0| f10 |0| f3 |0| f2 |0| f25 |0| f24 |0| f17 |0| f16 |0| f9 |0| f8 |0| f1 |0| f0 |0| +* | f31 |0| f30 |0| f23 |0| f22 |0| f15 |0| f14 |0| f7 |0| f6 |0| f29 |0| f28 |0| f21 |0| f20 |0| f13 |0| f12 |0| f5 |0| f4 |0| +*/ + +const __m256 scale = _mm256_set1_ps(CONV_SCALE); +const __m256i load_mask = _mm256_set_epi64x(0, -1, -1, -1); + +const __m256i mask0 = _mm256_set1_epi64x(0xfff00000fff00000); +const __m256i mask1 = _mm256_set1_epi64x(0x0000fff00000fff0); + +const __m256i permmask0 = _mm256_set_epi32(5, 4, 3, 7, 6, 2, 1, 0); +const __m256i permmask1 = _mm256_set_epi32(7, 3, 5, 1, 6, 2, 4, 0); + +const __m256i shfl = _mm256_set_epi8( + 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, + 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, + 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, + 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80); + +#define CONVERT_CI12_4F32_BLOCK(y0, y1) \ +{ \ + y0 = _mm256_permutevar8x32_epi32(y0, permmask0); \ + y1 = _mm256_permutevar8x32_epi32(y1, permmask0); \ + \ + y0 = _mm256_shuffle_epi8(y0, shfl); \ + y1 = _mm256_shuffle_epi8(y1, shfl); \ + \ + __m256i a0 = _mm256_and_si256(y0, mask0); \ + __m256i b0 = _mm256_and_si256(y1, mask0); \ + \ + __m256i a1 = _mm256_and_si256(_mm256_srli_epi64(y0, 4), mask1); \ + __m256i b1 = _mm256_and_si256(_mm256_srli_epi64(y1, 4), mask1); \ + \ + __m256i i0 = _mm256_or_si256(a0, a1); \ + __m256i i1 = _mm256_or_si256(b0, b1); \ + \ + /* Linear I12->F32 conv completed here */ \ + /* Next section dedicated to 4-way interleave processing */ \ + \ + i0 = _mm256_permutevar8x32_epi32(i0, permmask1); \ + i1 = _mm256_permutevar8x32_epi32(i1, permmask1); \ + \ + __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(i0), _mm256_castsi256_pd(i1), 0b0000)); \ + __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(i0), _mm256_castsi256_pd(i1), 0b1111)); \ + \ + __m256i d0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(z0)); \ + __m256i d1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(z0, 1)); \ + __m256i d2 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(z1)); \ + __m256i d3 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(z1, 1)); \ + \ + _mm256_storeu_ps(outdata_0, _mm256_mul_ps(_mm256_cvtepi32_ps(d0), scale)); \ + _mm256_storeu_ps(outdata_1, _mm256_mul_ps(_mm256_cvtepi32_ps(d1), scale)); \ + _mm256_storeu_ps(outdata_2, _mm256_mul_ps(_mm256_cvtepi32_ps(d2), scale)); \ + _mm256_storeu_ps(outdata_3, _mm256_mul_ps(_mm256_cvtepi32_ps(d3), scale)); \ + \ + outdata_0 += 8; \ + outdata_1 += 8; \ + outdata_2 += 8; \ + outdata_3 += 8; \ +} diff --git a/src/lib/xdsp/templates/conv_ci12_4cf32_avx2.t b/src/lib/xdsp/templates/conv_ci12_4cf32_avx2.t index 2bc97411..fd3323de 100644 --- a/src/lib/xdsp/templates/conv_ci12_4cf32_avx2.t +++ b/src/lib/xdsp/templates/conv_ci12_4cf32_avx2.t @@ -17,151 +17,76 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, float* outdata_2 = (float*)outdata_2_p; float* outdata_3 = (float*)outdata_3_p; -/* -* r0-r1: -* | (3) | (2) | (1) | (0) | -* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -* | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | -* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -* | 0 0 0 0 0 0 0 0 | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | -* | 0 0 0 0 0 0 0 0 | f31 | f30 | f29 | f28 | f27 | f26 | f25 | f24 | f23 | f22 | f21 | f20 | f19 | f18 | f17 | f16 | -* -* y0 -y1: _mm256_permutevar8x32_epi32 -* | | | | | -* | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | 0 0 0 0 0 0 0 0 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | -* | f31 | f30 | f29 | f28 | f27 | f26 | f25 | f24 | 0 0 0 0 0 0 0 0 | f23 | f22 | f21 | f20 | f19 | f18 | f17 | f16 | -* -* y0-y2: _mm256_shuffle_epi8 -* | | | | | -* | f15 | f14 | 0 | f13 | f12 | 0 | f11 | f10 | 0 | f9 | f8 | 0 | f7 | f6 | 0 | f5 | f4 | 0 | f3 | f2 | 0 | f1 | f0 | 0 | -* | f31 | f30 | 0 | f29 | f28 | 0 | f27 | f26 | 0 | f25 | f24 | 0 | f23 | f22 | 0 | f21 | f20 | 0 | f19 | f18 | 0 | f17 | f16 | 0 | -* -* a0-a1: -* | | | | | -* | f15 |0| 00 00 | f13 |0| 00 00 | f11 |0| 00 00 | f9 |0| 00 00 | f7 |0| 00 00 | f5 |0| 00 00 | f3 |0| 00 00 | f1 |0| 00 00 | -* | 00 00 | f14 |0| 00 00 | f12 |0| 00 00 | f10 |0| 00 00 | f8 |0| 00 00 | f6 |0| 00 00 | f4 |0| 00 00 | f2 |0| 00 00 | f0 |0| -* -* i0: _mm256_or_si256(a0, a1) -* i1: _mm256_or_si256(b0, b1) -* | | | | | -* | f15 |0| f14 |0| f13 |0| f12 |0| f11 |0| f10 |0| f9 |0| f8 |0| f7 |0| f6 |0| f5 |0| f4 |0| f3 |0| f2 |0| f1 |0| f0 |0| -* | f31 |0| f30 |0| f29 |0| f28 |0| f27 |0| f26 |0| f25 |0| f24 |0| f23 |0| f22 |0| f21 |0| f20 |0| f19 |0| f18 |0| f17 |0| f16 |0| -* -* i0-i1: _mm256_permutevar8x32_epi32 -* | | | | | -* | f15 |0| f14 |0| f7 |0| f6 |0| f11 |0| f10 |0| f3 |0| f2 |0| f13 |0| f12 |0| f5 |0| f4 |0| f9 |0| f8 |0| f1 |0| f0 |0| -* | f31 |0| f30 |0| f23 |0| f22 |0| f27 |0| f26 |0| f19 |0| f18 |0| f29 |0| f28 |0| f21 |0| f20 |0| f25 |0| f24 |0| f17 |0| f16 |0| -* -* z0-z1: _mm256_castpd_si256 -* | | | | | -* | f27 |0| f26 |0| f19 |0| f18 |0| f11 |0| f10 |0| f3 |0| f2 |0| f25 |0| f24 |0| f17 |0| f16 |0| f9 |0| f8 |0| f1 |0| f0 |0| -* | f31 |0| f30 |0| f23 |0| f22 |0| f15 |0| f14 |0| f7 |0| f6 |0| f29 |0| f28 |0| f21 |0| f20 |0| f13 |0| f12 |0| f5 |0| f4 |0| -*/ - - const __m256 scale = _mm256_set1_ps(CONV_SCALE); - const __m256i load_mask = _mm256_set_epi64x(0, -1, -1, -1); - - const __m256i mask0 = _mm256_set1_epi64x(0xfff00000fff00000); - const __m256i mask1 = _mm256_set1_epi64x(0x0000fff00000fff0); - - const __m256i permmask0 = _mm256_set_epi32(5, 4, 3, 7, 6, 2, 1, 0); - const __m256i permmask1 = _mm256_set_epi32(7, 3, 5, 1, 6, 2, 4, 0); - - const __m256i shfl = _mm256_set_epi8( - 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, - 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, - 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, - 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80); - const uint64_t *in = (const uint64_t*)indata_p; + #include "conv_i12_i16_avx2.inc" + #include "conv_i12_f32_avx2.inc" -#define CONVERT_CI12_4F32_BLOCK(y0, y1) \ - { \ - y0 = _mm256_permutevar8x32_epi32(y0, permmask0); \ - y1 = _mm256_permutevar8x32_epi32(y1, permmask0); \ - \ - y0 = _mm256_shuffle_epi8(y0, shfl); \ - y1 = _mm256_shuffle_epi8(y1, shfl); \ - \ - __m256i a0 = _mm256_and_si256(y0, mask0); \ - __m256i b0 = _mm256_and_si256(y1, mask0); \ - \ - __m256i a1 = _mm256_and_si256(_mm256_srli_epi64(y0, 4), mask1); \ - __m256i b1 = _mm256_and_si256(_mm256_srli_epi64(y1, 4), mask1); \ - \ - __m256i i0 = _mm256_or_si256(a0, a1); \ - __m256i i1 = _mm256_or_si256(b0, b1); \ - \ - /* Linear I12->F32 conv completed here */ \ - \ - /* Next section dedicated to 4-way interleave processing */ \ - \ - i0 = _mm256_permutevar8x32_epi32(i0, permmask1); \ - i1 = _mm256_permutevar8x32_epi32(i1, permmask1); \ - \ - __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(i0), _mm256_castsi256_pd(i1), 0b0000)); \ - __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(i0), _mm256_castsi256_pd(i1), 0b1111)); \ - \ - __m256i d0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(z0)); \ - __m256i d1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(z0, 1)); \ - __m256i d2 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(z1)); \ - __m256i d3 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(z1, 1)); \ - \ - _mm256_storeu_ps(outdata_0, _mm256_mul_ps(_mm256_cvtepi32_ps(d0), scale)); \ - _mm256_storeu_ps(outdata_1, _mm256_mul_ps(_mm256_cvtepi32_ps(d1), scale)); \ - _mm256_storeu_ps(outdata_2, _mm256_mul_ps(_mm256_cvtepi32_ps(d2), scale)); \ - _mm256_storeu_ps(outdata_3, _mm256_mul_ps(_mm256_cvtepi32_ps(d3), scale)); \ - \ - outdata_0 += 8; \ - outdata_1 += 8; \ - outdata_2 += 8; \ - outdata_3 += 8; \ + while(i >= 96) + { + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); + __m256i y2 = _mm256_maskload_epi64((const long long*)(in + 6), load_mask); + __m256i y3 = _mm256_maskload_epi64((const long long*)(in + 9), load_mask); + + __m256 f0, f1, f2, f3, f4, f5, f6, f7; + + CONVERT_CI12_4CF32_BLOCK_OPT(y0, y1, f0, f1, f2, f3); + _mm256_store_ps(outdata_0 + 0, f0); + _mm256_store_ps(outdata_1 + 0, f1); + _mm256_store_ps(outdata_2 + 0, f2); + _mm256_store_ps(outdata_3 + 0, f3); + + CONVERT_CI12_4CF32_BLOCK_OPT(y2, y3, f4, f5, f6, f7); + _mm256_store_ps(outdata_0 + 8, f4); + _mm256_store_ps(outdata_1 + 8, f5); + _mm256_store_ps(outdata_2 + 8, f6); + _mm256_store_ps(outdata_3 + 8, f7); + + outdata_0 += 16; + outdata_1 += 16; + outdata_2 += 16; + outdata_3 += 16; + + in += 12; + i -= 96; } -// CONVERT_CI12_4F32_BLOCK - - __m256i r0, r1; - while(i >= 48) + if(i >= 48) { - r0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); - r1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); - in += 6; + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); - CONVERT_CI12_4F32_BLOCK(r0, r1); + __m256 f0, f1, f2, f3; - i -= 48; - } + CONVERT_CI12_4CF32_BLOCK_OPT(y0, y1, f0, f1, f2, f3); - const uint8_t *indata = (const uint8_t*)in; + _mm256_store_ps(outdata_0 + 0, f0); + _mm256_store_ps(outdata_1 + 0, f1); + _mm256_store_ps(outdata_2 + 0, f2); + _mm256_store_ps(outdata_3 + 0, f3); + + outdata_0 += 8; + outdata_1 += 8; + outdata_2 += 8; + outdata_3 += 8; - for (; i >= 12; i -= 12) { - /* read 12 bytes -> 2*48 bits -> 4*2 floats -> 4cf32 */ - - uint64_t v0 = *(const uint64_t *)(indata + 0); - uint64_t v1 = *(const uint64_t *)(indata + 6); - indata += 12; - - float i0 = (int16_t)(v0 << 4); - float q0 = (int16_t)((v0 >> 8) & 0xfff0); - float i1 = (int16_t)((v0 >> 20) & 0xfff0); - float q1 = (int16_t)((v0 >> 32) & 0xfff0); - float i2 = (int16_t)(v1 << 4); - float q2 = (int16_t)((v1 >> 8) & 0xfff0); - float i3 = (int16_t)((v1 >> 20) & 0xfff0); - float q3 = (int16_t)((v1 >> 32) & 0xfff0); - - *(outdata_0++) = i0 * CONV_SCALE; - *(outdata_0++) = q0 * CONV_SCALE; - *(outdata_1++) = i1 * CONV_SCALE; - *(outdata_1++) = q1 * CONV_SCALE; - *(outdata_2++) = i2 * CONV_SCALE; - *(outdata_2++) = q2 * CONV_SCALE; - *(outdata_3++) = i3 * CONV_SCALE; - *(outdata_3++) = q3 * CONV_SCALE; + in += 6; + i -= 48; } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT - // tail ignored + const uint8_t *indata = (const uint8_t*)in; + #include "conv_ci12_4cf32_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_4cf32_avx512bw.t b/src/lib/xdsp/templates/conv_ci12_4cf32_avx512bw.t new file mode 100644 index 00000000..8a84d58a --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_4cf32_avx512bw.t @@ -0,0 +1,185 @@ +#ifndef UNWRAP_CNT +#define UNWRAP_CNT 4 +#endif + +#if UNWRAP_CNT > 4 +#error Maximum spported UNWRAP_CNT is 4! +#endif + + +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + /* 12 bits -> 32 bits => 3 -> 8 */ + if ((outdatabsz * 3 / 8) < i) + i = (outdatabsz * 3 / 8); + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + float* outdata_3 = (float*)outdata_3_p; + + const uint64_t *in = (const uint64_t*)indata_p; + + //AVX512 block + { + #include "conv_i12_i16_avx512bw.inc" + #include "conv_i12_f32_avx512bw.inc" + + __m512i y0, y1; + __m512 f0, f1, f2, f3; +#if UNWRAP_CNT > 1 + __m512i y2, y3; + __m512 f4, f5, f6, f7; +#if UNWRAP_CNT > 2 + __m512i y4, y5; + __m512 f8, f9, fA, fB; +#if UNWRAP_CNT > 3 + __m512i y6, y7; + __m512 fC, fD, fE, fF; +#endif +#endif +#endif + + for(; i >= 96 * UNWRAP_CNT; i -= 96 * UNWRAP_CNT) + { + y0 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 0)); + y1 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 6)); +#if UNWRAP_CNT > 1 + y2 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 12)); + y3 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 18)); +#if UNWRAP_CNT > 2 + y4 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 24)); + y5 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 30)); +#if UNWRAP_CNT > 3 + y6 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 36)); + y7 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 42)); +#endif +#endif +#endif + + in += UNWRAP_CNT * 12; + + CONVERT_CI12_4CF32_BLOCK_OPT(y0, y1, f0, f1, f2, f3); +#if UNWRAP_CNT > 1 + CONVERT_CI12_4CF32_BLOCK_OPT(y2, y3, f4, f5, f6, f7); +#if UNWRAP_CNT > 2 + CONVERT_CI12_4CF32_BLOCK_OPT(y4, y5, f8, f9, fA, fB); +#if UNWRAP_CNT > 3 + CONVERT_CI12_4CF32_BLOCK_OPT(y6, y7, fC, fD, fE, fF); +#endif +#endif +#endif + + _mm512_store_ps(outdata_0, f0); + _mm512_store_ps(outdata_1, f1); + _mm512_store_ps(outdata_2, f2); + _mm512_store_ps(outdata_3, f3); + + outdata_0 += 16; + outdata_1 += 16; + outdata_2 += 16; + outdata_3 += 16; + +#if UNWRAP_CNT > 1 + _mm512_store_ps(outdata_0, f4); + _mm512_store_ps(outdata_1, f5); + _mm512_store_ps(outdata_2, f6); + _mm512_store_ps(outdata_3, f7); + + outdata_0 += 16; + outdata_1 += 16; + outdata_2 += 16; + outdata_3 += 16; + +#if UNWRAP_CNT > 2 + _mm512_store_ps(outdata_0, f8); + _mm512_store_ps(outdata_1, f9); + _mm512_store_ps(outdata_2, fA); + _mm512_store_ps(outdata_3, fB); + + outdata_0 += 16; + outdata_1 += 16; + outdata_2 += 16; + outdata_3 += 16; +#if UNWRAP_CNT > 3 + _mm512_store_ps(outdata_0, fC); + _mm512_store_ps(outdata_1, fD); + _mm512_store_ps(outdata_2, fE); + _mm512_store_ps(outdata_3, fF); + + outdata_0 += 16; + outdata_1 += 16; + outdata_2 += 16; + outdata_3 += 16; +#endif +#endif +#endif + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT + } + + + //AVX2 block + { + #include "conv_i12_i16_avx2.inc" + #include "conv_i12_f32_avx2.inc" + + if(i >= 48) + { + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); + + __m256 f0, f1, f2, f3; + + CONVERT_CI12_4CF32_BLOCK_OPT(y0, y1, f0, f1, f2, f3); + + _mm256_store_ps(outdata_0 + 0, f0); + _mm256_store_ps(outdata_1 + 0, f1); + _mm256_store_ps(outdata_2 + 0, f2); + _mm256_store_ps(outdata_3 + 0, f3); + + outdata_0 += 8; + outdata_1 += 8; + outdata_2 += 8; + outdata_3 += 8; + + in += 6; + i -= 48; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT + } + + //Generic block + { + const uint8_t *indata = (const uint8_t*)in; + #include "conv_ci12_4cf32_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_4cf32_generic.inc b/src/lib/xdsp/templates/conv_ci12_4cf32_generic.inc new file mode 100644 index 00000000..30f3404a --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_4cf32_generic.inc @@ -0,0 +1,27 @@ +for (; i >= 12; i -= 12) { + /* read 12 bytes -> 2*48 bits -> 4*2 floats -> 4cf32 */ + + uint64_t v0 = *(const uint64_t *)(indata + 0); + uint64_t v1 = ((uint64_t)((*(const uint32_t *)(indata + 8))) << 16) | (v0 >> 48); + indata += 12; + + float i0 = (int16_t)(v0 << 4); + float q0 = (int16_t)((v0 >> 8) & 0xfff0); + float i1 = (int16_t)((v0 >> 20) & 0xfff0); + float q1 = (int16_t)((v0 >> 32) & 0xfff0); + float i2 = (int16_t)(v1 << 4); + float q2 = (int16_t)((v1 >> 8) & 0xfff0); + float i3 = (int16_t)((v1 >> 20) & 0xfff0); + float q3 = (int16_t)((v1 >> 32) & 0xfff0); + + *(outdata_0++) = i0 * CONV_SCALE; + *(outdata_0++) = q0 * CONV_SCALE; + *(outdata_1++) = i1 * CONV_SCALE; + *(outdata_1++) = q1 * CONV_SCALE; + *(outdata_2++) = i2 * CONV_SCALE; + *(outdata_2++) = q2 * CONV_SCALE; + *(outdata_3++) = i3 * CONV_SCALE; + *(outdata_3++) = q3 * CONV_SCALE; +} + +// tail ignored diff --git a/src/lib/xdsp/templates/conv_ci12_4cf32_generic.t b/src/lib/xdsp/templates/conv_ci12_4cf32_generic.t index 6e59f0b8..4040892c 100644 --- a/src/lib/xdsp/templates/conv_ci12_4cf32_generic.t +++ b/src/lib/xdsp/templates/conv_ci12_4cf32_generic.t @@ -18,33 +18,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, float* outdata_2 = (float*)outdata_2_p; float* outdata_3 = (float*)outdata_3_p; - for (; i >= 12; i -= 12) { - /* read 12 bytes -> 2*48 bits -> 4*2 floats -> 4cf32 */ - - uint64_t v0 = *(const uint64_t *)(indata + 0); - uint64_t v1 = *(const uint64_t *)(indata + 6); - indata += 12; - - float i0 = (int16_t)(v0 << 4); - float q0 = (int16_t)((v0 >> 8) & 0xfff0); - float i1 = (int16_t)((v0 >> 20) & 0xfff0); - float q1 = (int16_t)((v0 >> 32) & 0xfff0); - float i2 = (int16_t)(v1 << 4); - float q2 = (int16_t)((v1 >> 8) & 0xfff0); - float i3 = (int16_t)((v1 >> 20) & 0xfff0); - float q3 = (int16_t)((v1 >> 32) & 0xfff0); - - *(outdata_0++) = i0 * CONV_SCALE; - *(outdata_0++) = q0 * CONV_SCALE; - *(outdata_1++) = i1 * CONV_SCALE; - *(outdata_1++) = q1 * CONV_SCALE; - *(outdata_2++) = i2 * CONV_SCALE; - *(outdata_2++) = q2 * CONV_SCALE; - *(outdata_3++) = i3 * CONV_SCALE; - *(outdata_3++) = q3 * CONV_SCALE; - } - - // tail ignored + #include "conv_ci12_4cf32_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_4ci16_avx2.inc b/src/lib/xdsp/templates/conv_ci12_4ci16_avx2.inc new file mode 100644 index 00000000..5d50d319 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_4ci16_avx2.inc @@ -0,0 +1,35 @@ +const __m256i permmask1 = _mm256_set_epi32(7, 3, 5, 1, 6, 2, 4, 0); + +#define STORE_CI12_4CI16_BLOCK(reg0, reg1, reg2, reg3) \ +{ \ + __m256i rs0, rs1, rs2, rs3; \ + CONVERT_I12_I16_BLOCK(reg0, rs0); \ + CONVERT_I12_I16_BLOCK(reg1, rs1); \ + CONVERT_I12_I16_BLOCK(reg2, rs2); \ + CONVERT_I12_I16_BLOCK(reg3, rs3); \ + \ + rs0 = _mm256_permutevar8x32_epi32(rs0, permmask1); \ + rs1 = _mm256_permutevar8x32_epi32(rs1, permmask1); \ + rs2 = _mm256_permutevar8x32_epi32(rs2, permmask1); \ + rs3 = _mm256_permutevar8x32_epi32(rs3, permmask1); \ + \ + __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs0), _mm256_castsi256_pd(rs1), 0b0000)); \ + __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs0), _mm256_castsi256_pd(rs1), 0b1111)); \ + __m256i z2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs2), _mm256_castsi256_pd(rs3), 0b0000)); \ + __m256i z3 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs2), _mm256_castsi256_pd(rs3), 0b1111)); \ + \ + __m256i i0 = _mm256_permute2x128_si256(z0, z2, 0b00100000); \ + __m256i i1 = _mm256_permute2x128_si256(z0, z2, 0b00110001); \ + __m256i i2 = _mm256_permute2x128_si256(z1, z3, 0b00100000); \ + __m256i i3 = _mm256_permute2x128_si256(z1, z3, 0b00110001); \ + \ + _mm256_storeu_si256((__m256i*)outdata_0, i0); \ + _mm256_storeu_si256((__m256i*)outdata_1, i1); \ + _mm256_storeu_si256((__m256i*)outdata_2, i2); \ + _mm256_storeu_si256((__m256i*)outdata_3, i3); \ + \ + outdata_0 += 16; \ + outdata_1 += 16; \ + outdata_2 += 16; \ + outdata_3 += 16; \ +} diff --git a/src/lib/xdsp/templates/conv_ci12_4ci16_avx2.t b/src/lib/xdsp/templates/conv_ci12_4ci16_avx2.t index 687a2d34..3a4ae291 100644 --- a/src/lib/xdsp/templates/conv_ci12_4ci16_avx2.t +++ b/src/lib/xdsp/templates/conv_ci12_4ci16_avx2.t @@ -18,63 +18,8 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, int16_t* outdata_2 = (int16_t*)outdata_2_p; int16_t* outdata_3 = (int16_t*)outdata_3_p; - const __m256i load_mask = _mm256_set_epi64x(0, -1, -1, -1); - - const __m256i mask0 = _mm256_set1_epi64x(0xfff00000fff00000); - const __m256i mask1 = _mm256_set1_epi64x(0x0000fff00000fff0); - - const __m256i permmask0 = _mm256_set_epi32(5, 4, 3, 7, 6, 2, 1, 0); - const __m256i permmask1 = _mm256_set_epi32(7, 3, 5, 1, 6, 2, 4, 0); - - const __m256i shfl = _mm256_set_epi8( - 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, - 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, - 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, - 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80); - -#define CONVERT_CI12_4CI16_BLOCK(reg, result) \ - { \ - __m256i v0 = _mm256_permutevar8x32_epi32(reg, permmask0); \ - __m256i r = _mm256_shuffle_epi8(v0, shfl); \ - \ - __m256i r0 = _mm256_and_si256(r, mask0); \ - __m256i r1 = _mm256_and_si256(_mm256_srli_epi64(r, 4), mask1); \ - result = _mm256_or_si256(r0, r1); \ - } - -#define STORE_CI12_4CI16_BLOCK(reg0, reg1, reg2, reg3) \ - { \ - __m256i rs0, rs1, rs2, rs3; \ - CONVERT_CI12_4CI16_BLOCK(reg0, rs0); \ - CONVERT_CI12_4CI16_BLOCK(reg1, rs1); \ - CONVERT_CI12_4CI16_BLOCK(reg2, rs2); \ - CONVERT_CI12_4CI16_BLOCK(reg3, rs3); \ - \ - rs0 = _mm256_permutevar8x32_epi32(rs0, permmask1); \ - rs1 = _mm256_permutevar8x32_epi32(rs1, permmask1); \ - rs2 = _mm256_permutevar8x32_epi32(rs2, permmask1); \ - rs3 = _mm256_permutevar8x32_epi32(rs3, permmask1); \ - \ - __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs0), _mm256_castsi256_pd(rs1), 0b0000)); \ - __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs0), _mm256_castsi256_pd(rs1), 0b1111)); \ - __m256i z2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs2), _mm256_castsi256_pd(rs3), 0b0000)); \ - __m256i z3 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(rs2), _mm256_castsi256_pd(rs3), 0b1111)); \ - \ - __m256i i0 = _mm256_permute2x128_si256(z0, z2, 0b00100000); \ - __m256i i1 = _mm256_permute2x128_si256(z0, z2, 0b00110001); \ - __m256i i2 = _mm256_permute2x128_si256(z1, z3, 0b00100000); \ - __m256i i3 = _mm256_permute2x128_si256(z1, z3, 0b00110001); \ - \ - _mm256_storeu_si256((__m256i*)outdata_0, i0); \ - _mm256_storeu_si256((__m256i*)outdata_1, i1); \ - _mm256_storeu_si256((__m256i*)outdata_2, i2); \ - _mm256_storeu_si256((__m256i*)outdata_3, i3); \ - \ - outdata_0 += 16; \ - outdata_1 += 16; \ - outdata_2 += 16; \ - outdata_3 += 16; \ - } +#include "conv_i12_i16_avx2.inc" +#include "conv_ci12_4ci16_avx2.inc" __m256i y0, y1, y2, y3; @@ -103,26 +48,9 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, } #undef STORE_CI12_4CI16_BLOCK -#undef CONVERT_CI12_4CI16_BLOCK +#undef CONVERT_I12_I16_BLOCK const uint8_t *indata = (const uint8_t*)in; - - for (; i >= 12; i -= 12) { - /* read 12 bytes -> 4ci16 */ - - uint64_t v0 = *(const uint64_t *)(indata + 0); - uint64_t v1 = *(const uint64_t *)(indata + 6); - indata += 12; - - *(outdata_0++) = (int16_t)((v0 << 4) ); - *(outdata_0++) = (int16_t)((v0 >> 8) & 0xfff0); - *(outdata_1++) = (int16_t)((v0 >> 20) & 0xfff0); - *(outdata_1++) = (int16_t)((v0 >> 32) & 0xfff0); - *(outdata_2++) = (int16_t)((v1 << 4) ); - *(outdata_2++) = (int16_t)((v1 >> 8) & 0xfff0); - *(outdata_3++) = (int16_t)((v1 >> 20) & 0xfff0); - *(outdata_3++) = (int16_t)((v1 >> 32) & 0xfff0); - } - // do nothing with tail + #include "conv_ci12_4ci16_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_4ci16_avx512bw.t b/src/lib/xdsp/templates/conv_ci12_4ci16_avx512bw.t new file mode 100644 index 00000000..7f5e94ce --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_4ci16_avx512bw.t @@ -0,0 +1,103 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + /* 12 bits -> 16 bits => 3 -> 4 */ + if ((outdatabsz * 3 / 4) < i) + i = (outdatabsz * 3 / 4); + + const uint64_t *in = (const uint64_t*)indata_p; + int16_t* outdata_0 = (int16_t*)outdata_0_p; + int16_t* outdata_1 = (int16_t*)outdata_1_p; + int16_t* outdata_2 = (int16_t*)outdata_2_p; + int16_t* outdata_3 = (int16_t*)outdata_3_p; + + //AVX512BW block + { + #include "conv_i12_i16_avx512bw.inc" + + __m512i y0, y1, y2, y3; + __m512i rs0, rs1, rs2, rs3; + __m512i a0, a1, a2, a3; + __m512i b0, b1, b2, b3; + + const __m512i imask0 = _mm512_set_epi32(30,14,28,12,26,10,24,8,22,6,20,4,18,2,16,0); + const __m512i imask1 = _mm512_set_epi32(31,15,29,13,27,11,25,9,23,7,21,5,19,3,17,1); + const __m512i imask2 = _mm512_set_epi32(15,11,7,3, 14,10,6,2, 13,9,5,1, 12,8,4,0); + + for(; i >= 2*96; i -= 2*96) + { + y0 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 0)); + y1 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 6)); + y2 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 12)); + y3 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 18)); + in += 24; + + CONVERT_I12_I16_BLOCK(y0, rs0); + CONVERT_I12_I16_BLOCK(y1, rs1); + CONVERT_I12_I16_BLOCK(y2, rs2); + CONVERT_I12_I16_BLOCK(y3, rs3); + + a0 = _mm512_permutex2var_epi32(rs0, imask0, rs1); + a1 = _mm512_permutex2var_epi32(rs0, imask1, rs1); + a2 = _mm512_permutex2var_epi32(rs2, imask0, rs3); + a3 = _mm512_permutex2var_epi32(rs2, imask1, rs3); + + b0 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a0), _mm512_castsi512_pd(a2), 0b00000000)); //1 + b2 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a0), _mm512_castsi512_pd(a2), 0b11111111)); //1 + b1 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a1), _mm512_castsi512_pd(a3), 0b00000000)); //1 + b3 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a1), _mm512_castsi512_pd(a3), 0b11111111)); //1 + + _mm512_store_si512((__m512i*)outdata_0, _mm512_permutexvar_epi32(imask2, b0)); + _mm512_store_si512((__m512i*)outdata_1, _mm512_permutexvar_epi32(imask2, b1)); + _mm512_store_si512((__m512i*)outdata_2, _mm512_permutexvar_epi32(imask2, b2)); + _mm512_store_si512((__m512i*)outdata_3, _mm512_permutexvar_epi32(imask2, b3)); + + outdata_0 += 32; + outdata_1 += 32; + outdata_2 += 32; + outdata_3 += 32; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + } + + //AVX2 block + { + #include "conv_i12_i16_avx2.inc" + #include "conv_ci12_4ci16_avx2.inc" + + if(i >= 96) + { + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); // 8 1/3 + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); // 8 1/3 + __m256i y2 = _mm256_maskload_epi64((const long long*)(in + 6), load_mask); // 8 1/3 + __m256i y3 = _mm256_maskload_epi64((const long long*)(in + 9), load_mask); // 8 1/3 + in += 12; + i -= 96; + + STORE_CI12_4CI16_BLOCK(y0, y1, y2, y3); + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + } + + //Generic block + { + const uint8_t *indata = (const uint8_t*)in; + #include "conv_ci12_4ci16_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci12_4ci16_generic.inc b/src/lib/xdsp/templates/conv_ci12_4ci16_generic.inc new file mode 100644 index 00000000..50859a93 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci12_4ci16_generic.inc @@ -0,0 +1,16 @@ +for (; i >= 12; i -= 12) { + /* read 12 bytes -> 4ci16 */ + + uint64_t v0 = *(const uint64_t *)(indata + 0); + uint64_t v1 = *(const uint64_t *)(indata + 6); + indata += 12; + + *(outdata_0++) = (int16_t)((v0 << 4) ); + *(outdata_0++) = (int16_t)((v0 >> 8) & 0xfff0); + *(outdata_1++) = (int16_t)((v0 >> 20) & 0xfff0); + *(outdata_1++) = (int16_t)((v0 >> 32) & 0xfff0); + *(outdata_2++) = (int16_t)((v1 << 4) ); + *(outdata_2++) = (int16_t)((v1 >> 8) & 0xfff0); + *(outdata_3++) = (int16_t)((v1 >> 20) & 0xfff0); + *(outdata_3++) = (int16_t)((v1 >> 32) & 0xfff0); +} diff --git a/src/lib/xdsp/templates/conv_ci12_4ci16_generic.t b/src/lib/xdsp/templates/conv_ci12_4ci16_generic.t index 74334ba5..71813e9b 100644 --- a/src/lib/xdsp/templates/conv_ci12_4ci16_generic.t +++ b/src/lib/xdsp/templates/conv_ci12_4ci16_generic.t @@ -18,24 +18,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, int16_t* outdata_2 = (int16_t*)outdata_2_p; int16_t* outdata_3 = (int16_t*)outdata_3_p; - for (; i >= 12; i -= 12) { - /* read 12 bytes -> 4ci16 */ - - uint64_t v0 = *(const uint64_t *)(indata + 0); - uint64_t v1 = *(const uint64_t *)(indata + 6); - indata += 12; - - *(outdata_0++) = (int16_t)((v0 << 4) ); - *(outdata_0++) = (int16_t)((v0 >> 8) & 0xfff0); - *(outdata_1++) = (int16_t)((v0 >> 20) & 0xfff0); - *(outdata_1++) = (int16_t)((v0 >> 32) & 0xfff0); - *(outdata_2++) = (int16_t)((v1 << 4) ); - *(outdata_2++) = (int16_t)((v1 >> 8) & 0xfff0); - *(outdata_3++) = (int16_t)((v1 >> 20) & 0xfff0); - *(outdata_3++) = (int16_t)((v1 >> 32) & 0xfff0); - } - - // tail ignored + #include "conv_ci12_4ci16_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_3cf32_avx2.t b/src/lib/xdsp/templates/conv_ci16_3cf32_avx2.t new file mode 100644 index 00000000..c2f58d88 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_3cf32_avx2.t @@ -0,0 +1,80 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + + const __m256 scale = _mm256_set1_ps(CONV_SCALE); + const __m256i * inptr = (const __m256i*)indata; + +/* + r0: DCBA DCBA HGBA HGBA JGDA + r1: HGFE -> permute2x128_si256 -> FEHG permute2x128_si256 -> JIDC -> shuffle_epi32 -> IJCD -> _mm256_shuffle_pd -> KHEB + r2: LKJI LKJI LKFE LKJI LIFC +*/ + +#define CONV_3CF32(r0, r1, r2) \ +{ \ + r1 = _mm256_permute2x128_si256(r1, r1, 0x1); \ + \ + __m256i x0 = _mm256_permute2x128_si256(r0, r1, 0b00100000); \ + __m256i x1 = _mm256_permute2x128_si256(r0, r1, 0b00110001); \ + __m256i x2 = r2; \ + \ + __m256i xx0 = x0; \ + __m256i xx1 = _mm256_permute2x128_si256(x1, x2, 0b00100000); \ + __m256i xx2 = _mm256_permute2x128_si256(x1, x2, 0b00110001); \ + \ + xx1 = _mm256_shuffle_epi32(xx1, _MM_SHUFFLE(1,0,3,2)); \ + \ + __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(xx0), _mm256_castsi256_pd(xx1), 0b0000)); \ + __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(xx0), _mm256_castsi256_pd(xx1), 0b1111)); \ + __m256i z2 = xx2; \ + \ + __m256i zz0 = z0; \ + __m256i zz1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b0000)); \ + __m256i zz2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b1111)); \ + \ + _mm256_store_ps(outdata_0, _mm256_mul_ps(_mm256_cvtepi32_ps(zz0), scale)); \ + _mm256_store_ps(outdata_1, _mm256_mul_ps(_mm256_cvtepi32_ps(zz1), scale)); \ + _mm256_store_ps(outdata_2, _mm256_mul_ps(_mm256_cvtepi32_ps(zz2), scale)); \ + \ + outdata_0 += 8; \ + outdata_1 += 8; \ + outdata_2 += 8; \ +} + + for (; i >= 32 * 3; i -= 32 * 3) + { + __m256i a0 = _mm256_loadu_si256(inptr++); + __m256i a1 = _mm256_loadu_si256(inptr++); + __m256i a2 = _mm256_loadu_si256(inptr++); + + __m256i b0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(a0)); + __m256i b1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(a0, 1)); + __m256i b2 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(a1)); + __m256i b3 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(a1,1)); + __m256i b4 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(a2)); + __m256i b5 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(a2, 1)); + + CONV_3CF32(b0, b1, b2); + CONV_3CF32(b3, b4, b5); + } + +#undef CONV_3CF32 + + const uint32_t *ld = (const uint32_t *)inptr; + #include "conv_ci16_3cf32_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_3cf32_generic.inc b/src/lib/xdsp/templates/conv_ci16_3cf32_generic.inc new file mode 100644 index 00000000..e46ea903 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_3cf32_generic.inc @@ -0,0 +1,18 @@ + for (; i >= 12; i -= 12, ld += 3) { + uint64_t v = *((uint64_t*)ld); + uint32_t w = *(ld + 2); + float a = (int16_t)(v); + float b = (int16_t)(v>>16); + float c = (int16_t)(v>>32); + float d = (int16_t)(v>>48); + float e = (int16_t)(w); + float f = (int16_t)(w>>16); + + *(outdata_0++) = a * CONV_SCALE; + *(outdata_0++) = b * CONV_SCALE; + *(outdata_1++) = c * CONV_SCALE; + *(outdata_1++) = d * CONV_SCALE; + *(outdata_2++) = e * CONV_SCALE; + *(outdata_2++) = f * CONV_SCALE; + } + // do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_ci16_3cf32_generic.t b/src/lib/xdsp/templates/conv_ci16_3cf32_generic.t new file mode 100644 index 00000000..2d3ab1b8 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_3cf32_generic.t @@ -0,0 +1,21 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + + const uint32_t *ld = (const uint32_t *)indata; + #include "conv_ci16_3cf32_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_3ci16_avx2.t b/src/lib/xdsp/templates/conv_ci16_3ci16_avx2.t new file mode 100644 index 00000000..ae82468e --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_3ci16_avx2.t @@ -0,0 +1,86 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + int16_t* outdata_0 = (int16_t*)outdata_0_p; + int16_t* outdata_1 = (int16_t*)outdata_1_p; + int16_t* outdata_2 = (int16_t*)outdata_2_p; + + const __m256i mask0 = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0); + const __m256i mask1 = _mm256_set_epi32(0, -1, 0, -1, 0, -1, 0, -1); + const __m256i * inptr = (const __m256i*)indata; + +/* +r0: HGFEDCBA HGFEDCBA 3210DCBA 3210DCBA 7610HGBA 7610HGBA 9630JGDA +r1: 3210LKJI -0- LKJI3210 -1- 7654HGFE -2- 5476FEHG -3- 9832JIDC -4- 8923IJCD -5- a741KHEB +r2: ba987654 ba987654 ba98LKJI ba98LKJI ba54LKFE ba54LKFE b852LIFC +*/ + + for (; i >= 32 * 3; i -= 32 * 3) + { + __m256i r0 = _mm256_loadu_si256(inptr++); + __m256i r1 = _mm256_loadu_si256(inptr++); + __m256i r2 = _mm256_loadu_si256(inptr++); + + //(0) + r1 = _mm256_permute2x128_si256(r1, r1, 0x1); + + //(1) + __m256i x0 = _mm256_permute2x128_si256(r0, r1, 0b00100000); + __m256i x1 = _mm256_permute2x128_si256(r0, r1, 0b00110001); + __m256i x2 = r2; + + __m256i xx0 = x0; + __m256i xx1 = _mm256_permute2x128_si256(x1, x2, 0b00100000); + __m256i xx2 = _mm256_permute2x128_si256(x1, x2, 0b00110001); + + //(2) + xx1 = _mm256_shuffle_epi32(xx1, _MM_SHUFFLE(1,0,3,2)); + + //(3) + __m256i z0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(xx0), _mm256_castsi256_pd(xx1), 0b0000)); + __m256i z1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(xx0), _mm256_castsi256_pd(xx1), 0b1111)); + __m256i z2 = xx2; + + __m256i zz0 = z0; + __m256i zz1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b0000)); + __m256i zz2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(z1), _mm256_castsi256_pd(z2), 0b1111)); + + //(4) + zz1 = _mm256_shuffle_epi32(zz1, _MM_SHUFFLE(2,3,0,1)); + + //(5) + __m256d ulo, uhi; + ulo = _mm256_castsi256_pd(_mm256_unpacklo_epi32(zz0, zz1)); + uhi = _mm256_castsi256_pd(_mm256_unpackhi_epi32(zz0, zz1)); + __m256i rs0 = _mm256_castpd_si256(_mm256_shuffle_pd(ulo, uhi, 0b0000)); + + ulo = _mm256_castsi256_pd(_mm256_unpacklo_epi32(zz1, zz2)); + uhi = _mm256_castsi256_pd(_mm256_unpackhi_epi32(zz1, zz2)); + __m256i rs2 = _mm256_castpd_si256(_mm256_shuffle_pd(ulo, uhi, 0b1111)); + + __m256i rs1 = _mm256_or_si256(_mm256_and_si256(zz0, mask0), _mm256_and_si256(zz2, mask1)); + rs1 = _mm256_shuffle_epi32(rs1, _MM_SHUFFLE(2,3,0,1)); + + _mm256_store_si256((__m256i*)outdata_0, rs0); + _mm256_store_si256((__m256i*)outdata_1, rs1); + _mm256_store_si256((__m256i*)outdata_2, rs2); + + outdata_0 += 16; + outdata_1 += 16; + outdata_2 += 16; + } + + const uint32_t *ld = (const uint32_t *)inptr; + #include "conv_ci16_3ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_3ci16_generic.inc b/src/lib/xdsp/templates/conv_ci16_3ci16_generic.inc new file mode 100644 index 00000000..5aaae27b --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_3ci16_generic.inc @@ -0,0 +1,12 @@ + for (; i >= 12; i -= 12, ld += 3) { + uint64_t v = *((uint64_t*)ld); + uint32_t w = *(ld + 2); + + *(outdata_0++) = (int16_t)(v); + *(outdata_0++) = (int16_t)(v>>16); + *(outdata_1++) = (int16_t)(v>>32); + *(outdata_1++) = (int16_t)(v>>48); + *(outdata_2++) = (int16_t)(w); + *(outdata_2++) = (int16_t)(w>>16); + } + // do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_ci16_3ci16_generic.t b/src/lib/xdsp/templates/conv_ci16_3ci16_generic.t new file mode 100644 index 00000000..54261060 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_3ci16_generic.t @@ -0,0 +1,21 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + int16_t* outdata_0 = (int16_t*)outdata_0_p; + int16_t* outdata_1 = (int16_t*)outdata_1_p; + int16_t* outdata_2 = (int16_t*)outdata_2_p; + + const uint32_t *ld = (const uint32_t *)indata; + #include "conv_ci16_3ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_6cf32_avx2.t b/src/lib/xdsp/templates/conv_ci16_6cf32_avx2.t new file mode 100644 index 00000000..4c03627a --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6cf32_avx2.t @@ -0,0 +1,82 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + float* outdata_3 = (float*)outdata_3_p; + float* outdata_4 = (float*)outdata_4_p; + float* outdata_5 = (float*)outdata_5_p; + + const __m256i loadmask = _mm256_set_epi64x(0, -1, -1, -1); + const __m256 scale = _mm256_set1_ps(CONV_SCALE); + + const long long int* inptr64 = (long long int*)indata_p; + + while(i >= 12 * sizeof(uint64_t)) + { + __m256i in0 = _mm256_maskload_epi64(inptr64 + 0, loadmask); + __m256i in1 = _mm256_maskload_epi64(inptr64 + 3, loadmask); + __m256i in2 = _mm256_maskload_epi64(inptr64 + 6, loadmask); + __m256i in3 = _mm256_maskload_epi64(inptr64 + 9, loadmask); + + __m256i a0 = _mm256_unpacklo_epi32(in0, in1); + __m256i a1 = _mm256_unpackhi_epi32(in0, in1); + __m256i a2 = _mm256_unpacklo_epi32(in2, in3); + __m256i a3 = _mm256_unpackhi_epi32(in2, in3); + + __m256d b0 = _mm256_shuffle_pd(_mm256_castsi256_pd(a0), _mm256_castsi256_pd(a1), 0b0000); + __m256d b1 = _mm256_shuffle_pd(_mm256_castsi256_pd(a0), _mm256_castsi256_pd(a1), 0b1111); + __m256d b2 = _mm256_shuffle_pd(_mm256_castsi256_pd(a2), _mm256_castsi256_pd(a3), 0b0000); + __m256d b3 = _mm256_shuffle_pd(_mm256_castsi256_pd(a2), _mm256_castsi256_pd(a3), 0b1111); + + __m256i c0 = _mm256_castpd_si256(_mm256_shuffle_pd(b0, b2, 0b0000)); + __m256i c2 = _mm256_castpd_si256(_mm256_shuffle_pd(b0, b2, 0b1111)); + __m256i c1 = _mm256_castpd_si256(_mm256_shuffle_pd(b1, b3, 0b0000)); + __m256i c3 = _mm256_castpd_si256(_mm256_shuffle_pd(b1, b3, 0b1111)); + + __m256i d0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(c0)); + __m256i d1 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(c1)); + __m256i d2 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(c2)); + __m256i d3 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(c3)); + __m256i d4 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(c0, 1)); + __m256i d5 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(c1, 1)); + + _mm256_store_ps(outdata_0, _mm256_mul_ps(_mm256_cvtepi32_ps(d0), scale)); + _mm256_store_ps(outdata_1, _mm256_mul_ps(_mm256_cvtepi32_ps(d1), scale)); + _mm256_store_ps(outdata_2, _mm256_mul_ps(_mm256_cvtepi32_ps(d2), scale)); + _mm256_store_ps(outdata_3, _mm256_mul_ps(_mm256_cvtepi32_ps(d3), scale)); + _mm256_store_ps(outdata_4, _mm256_mul_ps(_mm256_cvtepi32_ps(d4), scale)); + _mm256_store_ps(outdata_5, _mm256_mul_ps(_mm256_cvtepi32_ps(d5), scale)); + + outdata_0 += 8; + outdata_1 += 8; + outdata_2 += 8; + outdata_3 += 8; + outdata_4 += 8; + outdata_5 += 8; + + i -= 12 * sizeof(uint64_t); + inptr64 += 12; + } + + //Generic block + { + const int16_t *in = (const int16_t *)inptr64; + #include "conv_ci16_6cf32_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_6cf32_avx512bw.t b/src/lib/xdsp/templates/conv_ci16_6cf32_avx512bw.t new file mode 100644 index 00000000..63bcff9a --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6cf32_avx512bw.t @@ -0,0 +1,112 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + float* outdata_3 = (float*)outdata_3_p; + float* outdata_4 = (float*)outdata_4_p; + float* outdata_5 = (float*)outdata_5_p; + + const long long int* inptr64 = (long long int*)indata_p; + const __m512 scale = _mm512_set1_ps(CONV_SCALE); + const __m256 scale_avx2 = _mm256_set1_ps(CONV_SCALE); + + #include "conv_ci16_6ci16_avx512bw.inc" + + while(i >= 24 * sizeof(uint64_t)) + { + __m512i in0 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 0); + __m512i in1 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 6); + __m512i in2 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 12); + __m512i in3 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 18); + + __m512i c0, c1, c2, c3; + AVX512_INTERLEAVE6(in0, in1, c0, c1); + AVX512_INTERLEAVE6(in2, in3, c2, c3); + + __m512i d0 = _mm512_permutex2var_epi64(c0, permmask1, c2); + __m512i d1 = _mm512_permutex2var_epi64(c1, permmask1, c3); + __m512i d2 = _mm512_permutex2var_epi64(c0, permmask2, c2); + __m512i d3 = _mm512_permutex2var_epi64(c1, permmask2, c3); + + __m512 r0 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(_mm512_castsi512_si256(d0))); + __m512 r1 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(_mm512_castsi512_si256(d1))); + __m512 r2 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(_mm512_castsi512_si256(d2))); + __m512 r3 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(_mm512_castsi512_si256(d3))); + __m512 r4 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(d0, 1))); + __m512 r5 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(d1, 1))); + + _mm512_store_ps(outdata_0, _mm512_mul_ps(r0, scale)); + _mm512_store_ps(outdata_1, _mm512_mul_ps(r1, scale)); + _mm512_store_ps(outdata_2, _mm512_mul_ps(r2, scale)); + _mm512_store_ps(outdata_3, _mm512_mul_ps(r3, scale)); + _mm512_store_ps(outdata_4, _mm512_mul_ps(r4, scale)); + _mm512_store_ps(outdata_5, _mm512_mul_ps(r5, scale)); + + outdata_0 += 16; + outdata_1 += 16; + outdata_2 += 16; + outdata_3 += 16; + outdata_4 += 16; + outdata_5 += 16; + + i -= 24 * sizeof(uint64_t); + inptr64 += 24; + } + + while(i >= 12 * sizeof(uint64_t)) + { + __m512i in0 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 0); + __m512i in1 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 6); + + __m512i c0, c1; + AVX512_INTERLEAVE6(in0, in1, c0, c1); + + __m256 r0 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm512_castsi512_si128(c0))); + __m256 r1 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm512_castsi512_si128(c1))); + __m256 r2 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm512_extracti32x4_epi32(c0, 1))); + __m256 r3 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm512_extracti32x4_epi32(c1, 1))); + __m256 r4 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm512_extracti32x4_epi32(c0, 2))); + __m256 r5 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm512_extracti32x4_epi32(c1, 2))); + + _mm256_store_ps(outdata_0, _mm256_mul_ps(r0, scale_avx2)); + _mm256_store_ps(outdata_1, _mm256_mul_ps(r1, scale_avx2)); + _mm256_store_ps(outdata_2, _mm256_mul_ps(r2, scale_avx2)); + _mm256_store_ps(outdata_3, _mm256_mul_ps(r3, scale_avx2)); + _mm256_store_ps(outdata_4, _mm256_mul_ps(r4, scale_avx2)); + _mm256_store_ps(outdata_5, _mm256_mul_ps(r5, scale_avx2)); + + outdata_0 += 8; + outdata_1 += 8; + outdata_2 += 8; + outdata_3 += 8; + outdata_4 += 8; + outdata_5 += 8; + + i -= 12 * sizeof(uint64_t); + inptr64 += 12; + } + +#undef AVX512_INTERLEAVE6 + + //Generic block + { + const int16_t *in = (const int16_t *)inptr64; + #include "conv_ci16_6cf32_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_6cf32_generic.inc b/src/lib/xdsp/templates/conv_ci16_6cf32_generic.inc new file mode 100644 index 00000000..363c3e91 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6cf32_generic.inc @@ -0,0 +1,29 @@ +for (; i >= 24; i -= 24) +{ + const float i0 = *in++; + const float q0 = *in++; + const float i1 = *in++; + const float q1 = *in++; + const float i2 = *in++; + const float q2 = *in++; + const float i3 = *in++; + const float q3 = *in++; + const float i4 = *in++; + const float q4 = *in++; + const float i5 = *in++; + const float q5 = *in++; + + *(outdata_0++) = i0 * CONV_SCALE; + *(outdata_0++) = q0 * CONV_SCALE; + *(outdata_1++) = i1 * CONV_SCALE; + *(outdata_1++) = q1 * CONV_SCALE; + *(outdata_2++) = i2 * CONV_SCALE; + *(outdata_2++) = q2 * CONV_SCALE; + *(outdata_3++) = i3 * CONV_SCALE; + *(outdata_3++) = q3 * CONV_SCALE; + *(outdata_4++) = i4 * CONV_SCALE; + *(outdata_4++) = q4 * CONV_SCALE; + *(outdata_5++) = i5 * CONV_SCALE; + *(outdata_5++) = q5 * CONV_SCALE; +} +// do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_ci16_6cf32_generic.t b/src/lib/xdsp/templates/conv_ci16_6cf32_generic.t new file mode 100644 index 00000000..59f6f968 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6cf32_generic.t @@ -0,0 +1,28 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + const int16_t *in = (const int16_t *)indata; + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + float* outdata_3 = (float*)outdata_3_p; + float* outdata_4 = (float*)outdata_4_p; + float* outdata_5 = (float*)outdata_5_p; + + #include "conv_ci16_6cf32_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_6ci16_avx2.t b/src/lib/xdsp/templates/conv_ci16_6ci16_avx2.t new file mode 100644 index 00000000..1566da03 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6ci16_avx2.t @@ -0,0 +1,73 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + uint32_t* outdata_0 = (uint32_t*)outdata_0_p; + uint32_t* outdata_1 = (uint32_t*)outdata_1_p; + uint32_t* outdata_2 = (uint32_t*)outdata_2_p; + uint32_t* outdata_3 = (uint32_t*)outdata_3_p; + uint32_t* outdata_4 = (uint32_t*)outdata_4_p; + uint32_t* outdata_5 = (uint32_t*)outdata_5_p; + + const __m256i loadmask = _mm256_set_epi64x(0, -1, -1, -1); + const long long int* inptr64 = (long long int*)indata_p; + + while(i >= 12 * sizeof(uint64_t)) + { + __m256i in0 = _mm256_maskload_epi64(inptr64 + 0, loadmask); + __m256i in1 = _mm256_maskload_epi64(inptr64 + 3, loadmask); + __m256i in2 = _mm256_maskload_epi64(inptr64 + 6, loadmask); + __m256i in3 = _mm256_maskload_epi64(inptr64 + 9, loadmask); + + __m256i a0 = _mm256_unpacklo_epi32(in0, in1); + __m256i a1 = _mm256_unpackhi_epi32(in0, in1); + __m256i a2 = _mm256_unpacklo_epi32(in2, in3); + __m256i a3 = _mm256_unpackhi_epi32(in2, in3); + + __m256d b0 = _mm256_shuffle_pd(_mm256_castsi256_pd(a0), _mm256_castsi256_pd(a1), 0b0000); + __m256d b1 = _mm256_shuffle_pd(_mm256_castsi256_pd(a0), _mm256_castsi256_pd(a1), 0b1111); + __m256d b2 = _mm256_shuffle_pd(_mm256_castsi256_pd(a2), _mm256_castsi256_pd(a3), 0b0000); + __m256d b3 = _mm256_shuffle_pd(_mm256_castsi256_pd(a2), _mm256_castsi256_pd(a3), 0b1111); + + __m256i c0 = _mm256_castpd_si256(_mm256_shuffle_pd(b0, b2, 0b0000)); + __m256i c2 = _mm256_castpd_si256(_mm256_shuffle_pd(b0, b2, 0b1111)); + __m256i c1 = _mm256_castpd_si256(_mm256_shuffle_pd(b1, b3, 0b0000)); + __m256i c3 = _mm256_castpd_si256(_mm256_shuffle_pd(b1, b3, 0b1111)); + + _mm_store_si128((__m128i*)outdata_0, _mm256_castsi256_si128(c0)); + _mm_store_si128((__m128i*)outdata_1, _mm256_castsi256_si128(c1)); + _mm_store_si128((__m128i*)outdata_2, _mm256_castsi256_si128(c2)); + _mm_store_si128((__m128i*)outdata_3, _mm256_castsi256_si128(c3)); + _mm_store_si128((__m128i*)outdata_4, _mm256_extracti128_si256(c0, 1)); + _mm_store_si128((__m128i*)outdata_5, _mm256_extracti128_si256(c1, 1)); + + outdata_0 += 4; + outdata_1 += 4; + outdata_2 += 4; + outdata_3 += 4; + outdata_4 += 4; + outdata_5 += 4; + + i -= 12 * sizeof(uint64_t); + inptr64 += 12; + } + + //Generic block + { + const uint32_t* indata = (uint32_t*)inptr64; + #include "conv_ci16_6ci16_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_6ci16_avx512bw.inc b/src/lib/xdsp/templates/conv_ci16_6ci16_avx512bw.inc new file mode 100644 index 00000000..6b9a364b --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6ci16_avx512bw.inc @@ -0,0 +1,13 @@ +const __m512i permmask0 = _mm512_set_epi32(15,14,13,12, 11,5,10,4, 9,3,8,2, 7,1,6,0); +const __m512i permmask1 = _mm512_set_epi64(13,12,5,4,9,8,1,0); +const __m512i permmask2 = _mm512_set_epi64(15,14,7,6,11,10,3,2); + +#define AVX512_INTERLEAVE6(in0, in1, out0, out1) \ +{ \ + __m512i a0 = _mm512_unpacklo_epi32(in0, in1); \ + __m512i a1 = _mm512_unpackhi_epi32(in0, in1); \ + __m512i b0 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a0), _mm512_castsi512_pd(a1), 0b00000000)); \ + __m512i b1 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(a0), _mm512_castsi512_pd(a1), 0b11111111)); \ + out0 = _mm512_permutexvar_epi32(permmask0, b0); \ + out1 = _mm512_permutexvar_epi32(permmask0, b1); \ +} diff --git a/src/lib/xdsp/templates/conv_ci16_6ci16_avx512bw.t b/src/lib/xdsp/templates/conv_ci16_6ci16_avx512bw.t new file mode 100644 index 00000000..198bf6a7 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6ci16_avx512bw.t @@ -0,0 +1,96 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + uint32_t* outdata_0 = (uint32_t*)outdata_0_p; + uint32_t* outdata_1 = (uint32_t*)outdata_1_p; + uint32_t* outdata_2 = (uint32_t*)outdata_2_p; + uint32_t* outdata_3 = (uint32_t*)outdata_3_p; + uint32_t* outdata_4 = (uint32_t*)outdata_4_p; + uint32_t* outdata_5 = (uint32_t*)outdata_5_p; + + const long long int* inptr64 = (long long int*)indata_p; + + #include "conv_ci16_6ci16_avx512bw.inc" + + while(i >= 24 * sizeof(uint64_t)) + { + __m512i in0 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 0); + __m512i in1 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 6); + __m512i in2 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 12); + __m512i in3 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 18); + + __m512i c0, c1, c2, c3; + AVX512_INTERLEAVE6(in0, in1, c0, c1); + AVX512_INTERLEAVE6(in2, in3, c2, c3); + + __m512i d0 = _mm512_permutex2var_epi64(c0, permmask1, c2); + __m512i d1 = _mm512_permutex2var_epi64(c1, permmask1, c3); + __m512i d2 = _mm512_permutex2var_epi64(c0, permmask2, c2); + __m512i d3 = _mm512_permutex2var_epi64(c1, permmask2, c3); + + _mm256_store_si256((__m256i*)outdata_0, _mm512_castsi512_si256(d0)); + _mm256_store_si256((__m256i*)outdata_1, _mm512_castsi512_si256(d1)); + _mm256_store_si256((__m256i*)outdata_2, _mm512_castsi512_si256(d2)); + _mm256_store_si256((__m256i*)outdata_3, _mm512_castsi512_si256(d3)); + _mm256_store_si256((__m256i*)outdata_4, _mm512_extracti64x4_epi64(d0, 1)); + _mm256_store_si256((__m256i*)outdata_5, _mm512_extracti64x4_epi64(d1, 1)); + + outdata_0 += 8; + outdata_1 += 8; + outdata_2 += 8; + outdata_3 += 8; + outdata_4 += 8; + outdata_5 += 8; + + i -= 24 * sizeof(uint64_t); + inptr64 += 24; + } + + while(i >= 12 * sizeof(uint64_t)) + { + __m512i in0 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 0); + __m512i in1 = _mm512_maskz_loadu_epi64(0b00111111, inptr64 + 6); + + __m512i c0, c1; + AVX512_INTERLEAVE6(in0, in1, c0, c1); + + _mm_store_si128((__m128i*)outdata_0, _mm512_castsi512_si128(c0)); + _mm_store_si128((__m128i*)outdata_1, _mm512_castsi512_si128(c1)); + _mm_store_si128((__m128i*)outdata_2, _mm512_extracti32x4_epi32(c0, 1)); + _mm_store_si128((__m128i*)outdata_3, _mm512_extracti32x4_epi32(c1, 1)); + _mm_store_si128((__m128i*)outdata_4, _mm512_extracti32x4_epi32(c0, 2)); + _mm_store_si128((__m128i*)outdata_5, _mm512_extracti32x4_epi32(c1, 2)); + + outdata_0 += 4; + outdata_1 += 4; + outdata_2 += 4; + outdata_3 += 4; + outdata_4 += 4; + outdata_5 += 4; + + i -= 12 * sizeof(uint64_t); + inptr64 += 12; + } + +#undef AVX512_INTERLEAVE6 + + //Generic block + { + const uint32_t* indata = (uint32_t*)inptr64; + #include "conv_ci16_6ci16_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_6ci16_generic.inc b/src/lib/xdsp/templates/conv_ci16_6ci16_generic.inc new file mode 100644 index 00000000..559b733d --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6ci16_generic.inc @@ -0,0 +1,10 @@ +for (; i >= 24; i -= 24) +{ + *outdata_0++ = *indata++; + *outdata_1++ = *indata++; + *outdata_2++ = *indata++; + *outdata_3++ = *indata++; + *outdata_4++ = *indata++; + *outdata_5++ = *indata++; +} +// do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_ci16_6ci16_generic.t b/src/lib/xdsp/templates/conv_ci16_6ci16_generic.t new file mode 100644 index 00000000..26bb1869 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_6ci16_generic.t @@ -0,0 +1,28 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + const uint32_t* indata = (uint32_t*)indata_p; + + uint32_t* outdata_0 = (uint32_t*)outdata_0_p; + uint32_t* outdata_1 = (uint32_t*)outdata_1_p; + uint32_t* outdata_2 = (uint32_t*)outdata_2_p; + uint32_t* outdata_3 = (uint32_t*)outdata_3_p; + uint32_t* outdata_4 = (uint32_t*)outdata_4_p; + uint32_t* outdata_5 = (uint32_t*)outdata_5_p; + + #include "conv_ci16_6ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_8cf32_avx2.t b/src/lib/xdsp/templates/conv_ci16_8cf32_avx2.t new file mode 100644 index 00000000..00b3d904 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_8cf32_avx2.t @@ -0,0 +1,132 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + void *__restrict outdata_6_p, + void *__restrict outdata_7_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + const __m256i * indata_p = (__m256i*)indata; + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + float* outdata_3 = (float*)outdata_3_p; + float* outdata_4 = (float*)outdata_4_p; + float* outdata_5 = (float*)outdata_5_p; + float* outdata_6 = (float*)outdata_6_p; + float* outdata_7 = (float*)outdata_7_p; + +#define USE_SSE_STORES + + //AVX2 + { + const __m256 scale = _mm256_set1_ps(CONV_SCALE); + + __m256i in0, in1, in2, in3; + __m256d f0a, f0b, f1a, f1b, f2a, f2b, f3a, f3b; + __m256d x0a, x0b, x1a, x1b, x2a, x2b, x3a, x3b; +#ifndef USE_SSE_STORES + __m256d out0, out1, out2, out3, out4, out5, out6, out7; +#endif + + for (; i >= 32 * 4; i -= 32 * 4) + { + in0 = _mm256_load_si256(indata_p++); + in1 = _mm256_load_si256(indata_p++); + in2 = _mm256_load_si256(indata_p++); + in3 = _mm256_load_si256(indata_p++); + + f0a = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_castsi256_si128(in0))), scale)); + f0b = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_extracti128_si256(in0, 1))), scale)); + f1a = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_castsi256_si128(in1))), scale)); + f1b = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_extracti128_si256(in1, 1))), scale)); + f2a = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_castsi256_si128(in2))), scale)); + f2b = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_extracti128_si256(in2, 1))), scale)); + f3a = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_castsi256_si128(in3))), scale)); + f3b = _mm256_castps_pd(_mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(_mm256_extracti128_si256(in3, 1))), scale)); + + x0a = _mm256_shuffle_pd(f0a, f1a, 0b0000); + x1a = _mm256_shuffle_pd(f0a, f1a, 0b1111); + x0b = _mm256_shuffle_pd(f0b, f1b, 0b0000); + x1b = _mm256_shuffle_pd(f0b, f1b, 0b1111); + x2a = _mm256_shuffle_pd(f2a, f3a, 0b0000); + x3a = _mm256_shuffle_pd(f2a, f3a, 0b1111); + x2b = _mm256_shuffle_pd(f2b, f3b, 0b0000); + x3b = _mm256_shuffle_pd(f2b, f3b, 0b1111); + +#ifdef USE_SSE_STORES + +//#define _MM_STORE_FN _mm_store_pd +#define _MM_STORE_FN _mm_storeu_pd + + _MM_STORE_FN((double*)outdata_0, _mm256_castpd256_pd128(x0a)); + _MM_STORE_FN((double*)outdata_1, _mm256_castpd256_pd128(x1a)); + _MM_STORE_FN((double*)outdata_2, _mm256_extractf128_pd(x0a, 1)); + _MM_STORE_FN((double*)outdata_3, _mm256_extractf128_pd(x1a, 1)); + _MM_STORE_FN((double*)outdata_4, _mm256_castpd256_pd128(x0b)); + _MM_STORE_FN((double*)outdata_5, _mm256_castpd256_pd128(x1b)); + _MM_STORE_FN((double*)outdata_6, _mm256_extractf128_pd(x0b, 1)); + _MM_STORE_FN((double*)outdata_7, _mm256_extractf128_pd(x1b, 1)); + + _MM_STORE_FN((double*)(outdata_0 + 4), _mm256_castpd256_pd128(x2a)); + _MM_STORE_FN((double*)(outdata_1 + 4), _mm256_castpd256_pd128(x3a)); + _MM_STORE_FN((double*)(outdata_2 + 4), _mm256_extractf128_pd(x2a, 1)); + _MM_STORE_FN((double*)(outdata_3 + 4), _mm256_extractf128_pd(x3a, 1)); + _MM_STORE_FN((double*)(outdata_4 + 4), _mm256_castpd256_pd128(x2b)); + _MM_STORE_FN((double*)(outdata_5 + 4), _mm256_castpd256_pd128(x3b)); + _MM_STORE_FN((double*)(outdata_6 + 4), _mm256_extractf128_pd(x2b, 1)); + _MM_STORE_FN((double*)(outdata_7 + 4), _mm256_extractf128_pd(x3b, 1)); +#else + +//#define _MM256_STORE_FN _mm256_store_pd +#define _MM256_STORE_FN _mm256_storeu_pd + + out0 = _mm256_permute2f128_pd(x0a, x2a, 0b00100000); + out2 = _mm256_permute2f128_pd(x0a, x2a, 0b00110001); + out1 = _mm256_permute2f128_pd(x1a, x3a, 0b00100000); + out3 = _mm256_permute2f128_pd(x1a, x3a, 0b00110001); + + _MM256_STORE_FN((double*)outdata_0, out0); + _MM256_STORE_FN((double*)outdata_1, out1); + _MM256_STORE_FN((double*)outdata_2, out2); + _MM256_STORE_FN((double*)outdata_3, out3); + + out4 = _mm256_permute2f128_pd(x0b, x2b, 0b00100000); + out6 = _mm256_permute2f128_pd(x0b, x2b, 0b00110001); + out5 = _mm256_permute2f128_pd(x1b, x3b, 0b00100000); + out7 = _mm256_permute2f128_pd(x1b, x3b, 0b00110001); + + _MM256_STORE_FN((double*)outdata_4, out4); + _MM256_STORE_FN((double*)outdata_5, out5); + _MM256_STORE_FN((double*)outdata_6, out6); + _MM256_STORE_FN((double*)outdata_7, out7); +#endif + outdata_0 += 8; + outdata_1 += 8; + outdata_2 += 8; + outdata_3 += 8; + outdata_4 += 8; + outdata_5 += 8; + outdata_6 += 8; + outdata_7 += 8; + } + } + + //Generic + { + const uint64_t *ld = (const uint64_t *)indata_p; + #include "conv_ci16_8cf32_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_8cf32_generic.inc b/src/lib/xdsp/templates/conv_ci16_8cf32_generic.inc new file mode 100644 index 00000000..1ea63465 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_8cf32_generic.inc @@ -0,0 +1,43 @@ +for (; i >= 32; i -= 32) +{ + const uint64_t v0 = *(ld++); + const uint64_t v1 = *(ld++); + const uint64_t v2 = *(ld++); + const uint64_t v3 = *(ld++); + + const float i0 = (int16_t)(v0); + const float q0 = (int16_t)(v0>>16); + const float i1 = (int16_t)(v0>>32); + const float q1 = (int16_t)(v0>>48); + const float i2 = (int16_t)(v1); + const float q2 = (int16_t)(v1>>16); + const float i3 = (int16_t)(v1>>32); + const float q3 = (int16_t)(v1>>48); + const float i4 = (int16_t)(v2); + const float q4 = (int16_t)(v2>>16); + const float i5 = (int16_t)(v2>>32); + const float q5 = (int16_t)(v2>>48); + const float i6 = (int16_t)(v3); + const float q6 = (int16_t)(v3>>16); + const float i7 = (int16_t)(v3>>32); + const float q7 = (int16_t)(v3>>48); + + *(outdata_0++) = i0 * CONV_SCALE; + *(outdata_0++) = q0 * CONV_SCALE; + *(outdata_1++) = i1 * CONV_SCALE; + *(outdata_1++) = q1 * CONV_SCALE; + *(outdata_2++) = i2 * CONV_SCALE; + *(outdata_2++) = q2 * CONV_SCALE; + *(outdata_3++) = i3 * CONV_SCALE; + *(outdata_3++) = q3 * CONV_SCALE; + + *(outdata_4++) = i4 * CONV_SCALE; + *(outdata_4++) = q4 * CONV_SCALE; + *(outdata_5++) = i5 * CONV_SCALE; + *(outdata_5++) = q5 * CONV_SCALE; + *(outdata_6++) = i6 * CONV_SCALE; + *(outdata_6++) = q6 * CONV_SCALE; + *(outdata_7++) = i7 * CONV_SCALE; + *(outdata_7++) = q7 * CONV_SCALE; +} +// do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_ci16_8cf32_generic.t b/src/lib/xdsp/templates/conv_ci16_8cf32_generic.t new file mode 100644 index 00000000..101bcf60 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_8cf32_generic.t @@ -0,0 +1,32 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + void *__restrict outdata_6_p, + void *__restrict outdata_7_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + const uint64_t *ld = (const uint64_t *)indata; + + float* outdata_0 = (float*)outdata_0_p; + float* outdata_1 = (float*)outdata_1_p; + float* outdata_2 = (float*)outdata_2_p; + float* outdata_3 = (float*)outdata_3_p; + float* outdata_4 = (float*)outdata_4_p; + float* outdata_5 = (float*)outdata_5_p; + float* outdata_6 = (float*)outdata_6_p; + float* outdata_7 = (float*)outdata_7_p; + + #include "conv_ci16_8cf32_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_8ci16_avx2.t b/src/lib/xdsp/templates/conv_ci16_8ci16_avx2.t new file mode 100644 index 00000000..77573b57 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_8ci16_avx2.t @@ -0,0 +1,161 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + void *__restrict outdata_6_p, + void *__restrict outdata_7_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + const __m256i * indata = (__m256i*)indata_p; + + uint32_t* outdata_0 = (uint32_t*)outdata_0_p; + uint32_t* outdata_1 = (uint32_t*)outdata_1_p; + uint32_t* outdata_2 = (uint32_t*)outdata_2_p; + uint32_t* outdata_3 = (uint32_t*)outdata_3_p; + uint32_t* outdata_4 = (uint32_t*)outdata_4_p; + uint32_t* outdata_5 = (uint32_t*)outdata_5_p; + uint32_t* outdata_6 = (uint32_t*)outdata_6_p; + uint32_t* outdata_7 = (uint32_t*)outdata_7_p; + +#undef USE_SSE_STORES + + //AVX2 + { + __m256i in0, in1, in2, in3, in4, in5, in6, in7; + __m256i a0, a1, a2, a3, a4, a5, a6, a7; + __m256i b0, b1, b2, b3, b4, b5, b6, b7; +#ifndef USE_SSE_STORES + __m256i out0, out1, out2, out3, out4, out5, out6, out7; +#endif + + for(; i >= 32 * 8; i -= 32 * 8) + { + in0 = _mm256_load_si256(indata++); + in1 = _mm256_load_si256(indata++); + in2 = _mm256_load_si256(indata++); + in3 = _mm256_load_si256(indata++); + + in0 = _mm256_shuffle_epi32(in0, _MM_SHUFFLE(3, 1, 2, 0)); + in1 = _mm256_shuffle_epi32(in1, _MM_SHUFFLE(3, 1, 2, 0)); + in2 = _mm256_shuffle_epi32(in2, _MM_SHUFFLE(3, 1, 2, 0)); + in3 = _mm256_shuffle_epi32(in3, _MM_SHUFFLE(3, 1, 2, 0)); + + a0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in0), _mm256_castsi256_pd(in1), 0b0000)); + a1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in0), _mm256_castsi256_pd(in1), 0b1111)); + a2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in2), _mm256_castsi256_pd(in3), 0b0000)); + a3 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in2), _mm256_castsi256_pd(in3), 0b1111)); + + a0 = _mm256_shuffle_epi32(a0, _MM_SHUFFLE(3, 1, 2, 0)); + a1 = _mm256_shuffle_epi32(a1, _MM_SHUFFLE(3, 1, 2, 0)); + a2 = _mm256_shuffle_epi32(a2, _MM_SHUFFLE(3, 1, 2, 0)); + a3 = _mm256_shuffle_epi32(a3, _MM_SHUFFLE(3, 1, 2, 0)); + + b0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a0), _mm256_castsi256_pd(a2), 0b0000)); + b1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a1), _mm256_castsi256_pd(a3), 0b0000)); + b2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a0), _mm256_castsi256_pd(a2), 0b1111)); + b3 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a1), _mm256_castsi256_pd(a3), 0b1111)); + + // + + in4 = _mm256_load_si256(indata++); + in5 = _mm256_load_si256(indata++); + in6 = _mm256_load_si256(indata++); + in7 = _mm256_load_si256(indata++); + + in4 = _mm256_shuffle_epi32(in4, _MM_SHUFFLE(3, 1, 2, 0)); + in5 = _mm256_shuffle_epi32(in5, _MM_SHUFFLE(3, 1, 2, 0)); + in6 = _mm256_shuffle_epi32(in6, _MM_SHUFFLE(3, 1, 2, 0)); + in7 = _mm256_shuffle_epi32(in7, _MM_SHUFFLE(3, 1, 2, 0)); + + a4 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in4), _mm256_castsi256_pd(in5), 0b0000)); + a5 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in4), _mm256_castsi256_pd(in5), 0b1111)); + a6 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in6), _mm256_castsi256_pd(in7), 0b0000)); + a7 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(in6), _mm256_castsi256_pd(in7), 0b1111)); + + a4 = _mm256_shuffle_epi32(a4, _MM_SHUFFLE(3, 1, 2, 0)); + a5 = _mm256_shuffle_epi32(a5, _MM_SHUFFLE(3, 1, 2, 0)); + a6 = _mm256_shuffle_epi32(a6, _MM_SHUFFLE(3, 1, 2, 0)); + a7 = _mm256_shuffle_epi32(a7, _MM_SHUFFLE(3, 1, 2, 0)); + + b4 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a4), _mm256_castsi256_pd(a6), 0b0000)); + b5 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a5), _mm256_castsi256_pd(a7), 0b0000)); + b6 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a4), _mm256_castsi256_pd(a6), 0b1111)); + b7 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(a5), _mm256_castsi256_pd(a7), 0b1111)); + + // + +#ifdef USE_SSE_STORES + +//#define _MM_STORE_FN _mm_store_si128 +#define _MM_STORE_FN _mm_storeu_si128 + + _MM_STORE_FN((__m128i*)outdata_0, _mm256_castsi256_si128(b0)); + _MM_STORE_FN((__m128i*)outdata_1, _mm256_castsi256_si128(b1)); + _MM_STORE_FN((__m128i*)outdata_2, _mm256_castsi256_si128(b2)); + _MM_STORE_FN((__m128i*)outdata_3, _mm256_castsi256_si128(b3)); + _MM_STORE_FN((__m128i*)outdata_4, _mm256_extractf128_si256(b0, 1)); + _MM_STORE_FN((__m128i*)outdata_5, _mm256_extractf128_si256(b1, 1)); + _MM_STORE_FN((__m128i*)outdata_6, _mm256_extractf128_si256(b2, 1)); + _MM_STORE_FN((__m128i*)outdata_7, _mm256_extractf128_si256(b3, 1)); + + _MM_STORE_FN((__m128i*)(outdata_0 + 4), _mm256_castsi256_si128(b4)); + _MM_STORE_FN((__m128i*)(outdata_1 + 4), _mm256_castsi256_si128(b5)); + _MM_STORE_FN((__m128i*)(outdata_2 + 4), _mm256_castsi256_si128(b6)); + _MM_STORE_FN((__m128i*)(outdata_3 + 4), _mm256_castsi256_si128(b7)); + _MM_STORE_FN((__m128i*)(outdata_4 + 4), _mm256_extractf128_si256(b4, 1)); + _MM_STORE_FN((__m128i*)(outdata_5 + 4), _mm256_extractf128_si256(b5, 1)); + _MM_STORE_FN((__m128i*)(outdata_6 + 4), _mm256_extractf128_si256(b6, 1)); + _MM_STORE_FN((__m128i*)(outdata_7 + 4), _mm256_extractf128_si256(b7, 1)); +#else + +//#define _MM256_STORE_FN _mm256_store_si256 +#define _MM256_STORE_FN _mm256_storeu_si256 + + out0 = _mm256_permute2x128_si256(b0, b4, 0b00100000); + out1 = _mm256_permute2x128_si256(b1, b5, 0b00100000); + out2 = _mm256_permute2x128_si256(b2, b6, 0b00100000); + out3 = _mm256_permute2x128_si256(b3, b7, 0b00100000); + + _MM256_STORE_FN((__m256i*)outdata_0, out0); + _MM256_STORE_FN((__m256i*)outdata_1, out1); + _MM256_STORE_FN((__m256i*)outdata_2, out2); + _MM256_STORE_FN((__m256i*)outdata_3, out3); + + out4 = _mm256_permute2x128_si256(b0, b4, 0b00110001); + out5 = _mm256_permute2x128_si256(b1, b5, 0b00110001); + out6 = _mm256_permute2x128_si256(b2, b6, 0b00110001); + out7 = _mm256_permute2x128_si256(b3, b7, 0b00110001); + + _MM256_STORE_FN((__m256i*)outdata_4, out4); + _MM256_STORE_FN((__m256i*)outdata_5, out5); + _MM256_STORE_FN((__m256i*)outdata_6, out6); + _MM256_STORE_FN((__m256i*)outdata_7, out7); +#endif + outdata_0 += 8; + outdata_1 += 8; + outdata_2 += 8; + outdata_3 += 8; + outdata_4 += 8; + outdata_5 += 8; + outdata_6 += 8; + outdata_7 += 8; + } + } + + //Generic + { + const uint32_t* in = (uint32_t*)indata; + #include "conv_ci16_8ci16_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_ci16_8ci16_generic.inc b/src/lib/xdsp/templates/conv_ci16_8ci16_generic.inc new file mode 100644 index 00000000..f1de8b40 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_8ci16_generic.inc @@ -0,0 +1,13 @@ +for (; i >= 32; i -= 32) +{ + *outdata_0++ = *in++; + *outdata_1++ = *in++; + *outdata_2++ = *in++; + *outdata_3++ = *in++; + *outdata_4++ = *in++; + *outdata_5++ = *in++; + *outdata_6++ = *in++; + *outdata_7++ = *in++; +} + +// do nothing with leftover diff --git a/src/lib/xdsp/templates/conv_ci16_8ci16_generic.t b/src/lib/xdsp/templates/conv_ci16_8ci16_generic.t new file mode 100644 index 00000000..d27d1e22 --- /dev/null +++ b/src/lib/xdsp/templates/conv_ci16_8ci16_generic.t @@ -0,0 +1,31 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_0_p, + void *__restrict outdata_1_p, + void *__restrict outdata_2_p, + void *__restrict outdata_3_p, + void *__restrict outdata_4_p, + void *__restrict outdata_5_p, + void *__restrict outdata_6_p, + void *__restrict outdata_7_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz) < i) + i = (outdatabsz); + + uint32_t* outdata_0 = (uint32_t*)outdata_0_p; + uint32_t* outdata_1 = (uint32_t*)outdata_1_p; + uint32_t* outdata_2 = (uint32_t*)outdata_2_p; + uint32_t* outdata_3 = (uint32_t*)outdata_3_p; + uint32_t* outdata_4 = (uint32_t*)outdata_4_p; + uint32_t* outdata_5 = (uint32_t*)outdata_5_p; + uint32_t* outdata_6 = (uint32_t*)outdata_6_p; + uint32_t* outdata_7 = (uint32_t*)outdata_7_p; + + const uint32_t* in = (uint32_t*)indata_p; + #include "conv_ci16_8ci16_generic.inc" +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_f32_i12_avx2.inc b/src/lib/xdsp/templates/conv_f32_i12_avx2.inc new file mode 100644 index 00000000..1f9f9c3c --- /dev/null +++ b/src/lib/xdsp/templates/conv_f32_i12_avx2.inc @@ -0,0 +1,15 @@ +const __m256 scale = _mm256_set1_ps(1.0f / CONV_SCALE); + +#define CONVERT_F32_I12_BLOCK(v0, v1) \ +{ \ + v0 = _mm256_mul_ps(v0, scale); \ + v1 = _mm256_mul_ps(v1, scale); \ +\ + __m256i i0 = _mm256_cvtps_epi32(v0); \ + __m256i i1 = _mm256_cvtps_epi32(v1); \ +\ + __m256i ii0 = _mm256_packs_epi32(i0, i1); \ + ii0 = _mm256_permute4x64_epi64(ii0, _MM_SHUFFLE(3,1,2,0)); \ +\ + CONVERT_I16_I12_BLOCK(ii0, out64); \ +} diff --git a/src/lib/xdsp/templates/conv_f32_i12_avx2.t b/src/lib/xdsp/templates/conv_f32_i12_avx2.t index 1ce32d22..adf45b0f 100644 --- a/src/lib/xdsp/templates/conv_f32_i12_avx2.t +++ b/src/lib/xdsp/templates/conv_f32_i12_avx2.t @@ -11,24 +11,8 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, const float *indata = (const float*)indata_p; uint64_t *out64 = (uint64_t*)outdata_p; - const __m256 scale = _mm256_set1_ps(1.0f / CONV_SCALE); - #include "conv_i16_i12_avx2.inc" - -#define CONVERT_F32_I12_BLOCK(v0, v1) \ - { \ - v0 = _mm256_mul_ps(v0, scale); \ - v1 = _mm256_mul_ps(v1, scale); \ - \ - __m256i i0 = _mm256_cvtps_epi32(v0); \ - __m256i i1 = _mm256_cvtps_epi32(v1); \ - \ - __m256i ii0 = _mm256_packs_epi32(i0, i1); \ - ii0 = _mm256_permute4x64_epi64(ii0, _MM_SHUFFLE(3,1,2,0)); \ - \ - CONVERT_I16_I12_BLOCK(ii0, out64); \ - } -// CONVERT_F32_I12_BLOCK end +#include "conv_f32_i12_avx2.inc" __m256 v0, v1, v2, v3; @@ -44,11 +28,12 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, CONVERT_F32_I12_BLOCK(v2, v3); } - for (; i >= 32*2; i -= 32*2) + if (i >= 32*2) { v0 = _mm256_loadu_ps(indata + 0); v1 = _mm256_loadu_ps(indata + 8); indata += 16; + i -= 32*2; CONVERT_F32_I12_BLOCK(v0, v1); } @@ -60,29 +45,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, #define I16RND(x) x > 0 ? (int16_t)(x + 0.5f) : (int16_t)(x - 0.5f) uint8_t* outdata = (uint8_t*)out64; - - for (; i >= 8; i -= 8) { - - float f0 = *(indata++) / CONV_SCALE; - float f1 = *(indata++) / CONV_SCALE; - - wu_i16u32_t a = {{I16RND(f0), I16RND(f1)}}; - wu_u32b_t c = {(a.u & 0xfff00000) | ((a.u << 4) & 0x000fff00)}; - - *(outdata++) = c.b[1]; - *(outdata++) = c.b[2]; - *(outdata++) = c.b[3]; - } - - if(i >= 4) - { - float f = *indata / CONV_SCALE; - wu_i16b_t c = {I16RND(f)}; - - *(outdata++) = c.b[0]; - *(outdata++) = c.b[1] >> 4; - i -= 4; - } + #include "conv_f32_i12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_f32_i12_avx512bw.inc b/src/lib/xdsp/templates/conv_f32_i12_avx512bw.inc new file mode 100644 index 00000000..4670f990 --- /dev/null +++ b/src/lib/xdsp/templates/conv_f32_i12_avx512bw.inc @@ -0,0 +1,16 @@ +const __m512 scale = _mm512_set1_ps(1.0f / CONV_SCALE); +const __m512i idx = _mm512_set_epi64(7,5,3,1,6,4,2,0); + +#define CONVERT_F32_I12_BLOCK(v0, v1) \ +{ \ + v0 = _mm512_mul_ps(v0, scale); \ + v1 = _mm512_mul_ps(v1, scale); \ +\ + __m512i i0 = _mm512_cvtps_epi32(v0); \ + __m512i i1 = _mm512_cvtps_epi32(v1); \ +\ + __m512i ii0 = _mm512_packs_epi32(i0, i1); \ + ii0 = _mm512_permutexvar_epi64(idx, ii0); \ +\ + CONVERT_I16_I12_BLOCK(ii0, out64); \ +} diff --git a/src/lib/xdsp/templates/conv_f32_i12_avx512bw.t b/src/lib/xdsp/templates/conv_f32_i12_avx512bw.t new file mode 100644 index 00000000..2b22c6f4 --- /dev/null +++ b/src/lib/xdsp/templates/conv_f32_i12_avx512bw.t @@ -0,0 +1,61 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 8 / 3) < i) + i = (outdatabsz * 8 / 3); + + const float *indata = (const float*)indata_p; + uint64_t *out64 = (uint64_t*)outdata_p; + + //AVX512 block + { + #include "conv_i16_i12_avx512bw.inc" + #include "conv_f32_i12_avx512bw.inc" + + __m512 v0, v1; + + for(; i >= 64*2; i -= 64*2) + { + v0 = _mm512_loadu_ps(indata + 0); + v1 = _mm512_loadu_ps(indata + 16); + indata += 32; + CONVERT_F32_I12_BLOCK(v0, v1); + } + + #undef CONVERT_F32_I12_BLOCK + #undef CONVERT_I16_I12_BLOCK + } + + //AVX2 block + { + #include "conv_i16_i12_avx2.inc" + #include "conv_f32_i12_avx2.inc" + + if(i >= 32*2) + { + __m256 v0 = _mm256_loadu_ps(indata + 0); + __m256 v1 = _mm256_loadu_ps(indata + 8); + indata += 16; + i -= 32*2; + CONVERT_F32_I12_BLOCK(v0, v1); + } + + #undef CONVERT_F32_I12_BLOCK + #undef CONVERT_I16_I12_BLOCK + } + + //Generic block + { + #undef I16RND + #define I16RND(x) x > 0 ? (int16_t)(x + 0.5f) : (int16_t)(x - 0.5f) + + uint8_t* outdata = (uint8_t*)out64; + #include "conv_f32_i12_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_f32_i12_generic.inc b/src/lib/xdsp/templates/conv_f32_i12_generic.inc new file mode 100644 index 00000000..98261fad --- /dev/null +++ b/src/lib/xdsp/templates/conv_f32_i12_generic.inc @@ -0,0 +1,22 @@ +for (; i >= 8; i -= 8) { + + float f0 = *(indata++) / CONV_SCALE; + float f1 = *(indata++) / CONV_SCALE; + + wu_i16u32_t a = {{I16RND(f0), I16RND(f1)}}; + wu_u32b_t c = {(a.u & 0xfff00000) | ((a.u << 4) & 0x000fff00)}; + + *(outdata++) = c.b[1]; + *(outdata++) = c.b[2]; + *(outdata++) = c.b[3]; +} + +if(i >= 4) +{ + float f = *indata / CONV_SCALE; + wu_i16b_t c = {I16RND(f)}; + + *(outdata++) = c.b[0]; + *(outdata++) = c.b[1] >> 4; + i -= 4; +} diff --git a/src/lib/xdsp/templates/conv_f32_i12_generic.t b/src/lib/xdsp/templates/conv_f32_i12_generic.t index aedb6b78..6bf80974 100644 --- a/src/lib/xdsp/templates/conv_f32_i12_generic.t +++ b/src/lib/xdsp/templates/conv_f32_i12_generic.t @@ -11,28 +11,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, const float* indata = (const float*)indata_p; uint8_t* outdata = (uint8_t*)outdata_p; - for (; i >= 8; i -= 8) { - - float f0 = *(indata++) / CONV_SCALE; - float f1 = *(indata++) / CONV_SCALE; - - wu_i16u32_t a = {{I16RND(f0), I16RND(f1)}}; - wu_u32b_t c = {(a.u & 0xfff00000) | ((a.u << 4) & 0x000fff00)}; - - *(outdata++) = c.b[1]; - *(outdata++) = c.b[2]; - *(outdata++) = c.b[3]; - } - - if(i >= 4) - { - float f = *indata / CONV_SCALE; - wu_i16b_t c = {I16RND(f)}; - - *(outdata++) = c.b[0]; - *(outdata++) = c.b[1] >> 4; - i -= 4; - } + #include "conv_f32_i12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i12_f32_avx2.inc b/src/lib/xdsp/templates/conv_i12_f32_avx2.inc new file mode 100644 index 00000000..8125e1b4 --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_f32_avx2.inc @@ -0,0 +1,87 @@ +UNUSED const __m256 scale = _mm256_set1_ps(CONV_SCALE); +UNUSED const __m256 scale2 = _mm256_set1_ps(SCALE2); + +/* +* reg: +* | (3) | (2) | (1) | (0) | +* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +* | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | +* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +* | 0 0 0 0 0 0 0 0 | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | +* +* v0: +* | | | | | +* | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | 0 0 0 0 0 0 0 0 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | +* r: +* | | | | | +* | f15 | f14 | 0 | f13 | f12 | 0 | f11 | f10 | 0 | f9 | f8 | 0 | f7 | f6 | 0 | f5 | f4 | 0 | f3 | f2 | 0 | f1 | f0 | 0 | +* r0: +* | | | | | +* | f15 |0| 00 00 | f13 |0| 00 00 | f11 |0| 00 00 | f9 |0| 00 00 | f7 |0| 00 00 | f5 |0| 00 00 | f3 |0| 00 00 | f1 |0| 00 00 | + r1: +* | | | | | +* | 00 00 | f14 |0| 00 00 | f12 |0| 00 00 | f10 |0| 00 00 | f8 |0| 00 00 | f6 |0| 00 00 | f4 |0| 00 00 | f2 |0| 00 00 | f0 |0| +* res: +* | | | | | +* | f15 |0| f14 |0| f13 |0| f12 |0| f11 |0| f10 |0| f9 |0| f8 |0| f7 |0| f6 |0| f5 |0| f4 |0| f3 |0| f2 |0| f1 |0| f0 |0| +*/ + +/* +* | f14 |0| 00 00 | f12 |0| 00 00 | f10 |0| 00 00 | f8 |0| 00 00 | f6 |0| 00 00 | f4 |0| 00 00 | f2 |0| 00 00 | f0 |0| 00 00 | +* | f15 |0| 00 00 | f13 |0| 00 00 | f11 |0| 00 00 | f9 |0| 00 00 | f7 |0| 00 00 | f5 |0| 00 00 | f3 |0| 00 00 | f1 |0| 00 00 | + + +f14 f12 f10 f8 | f6 f4 f2 f0 after 12-16 conv +f15 f13 f11 f9 | f7 f5 f3 f1 + +f11 f10 f9 f8 | f3 f2 f1 f0 unpack +f15 f14 f13 f12 | f7 f6 f5 f4 + +f13 f12 f9 f8 | f5 f4 f1 f0 _mm256_shuffle_pd +f15 f14 f11 f10 | f7 f6 f3 f2 + +*/ + +#define CONVERT_I12_F32_BLOCK(reg, res0, res1) \ +{ \ + __m256i result; \ + CONVERT_I12_I16_BLOCK(reg, result); \ + \ + __m256i d0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(result)); \ + __m256i d1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(result, 1)); \ + \ + res0 = _mm256_cvtepi32_ps(d0); /* 4 1|2 */ \ + res1 = _mm256_cvtepi32_ps(d1); /* 4 1|2 */ \ + \ + res0 = _mm256_mul_ps(res0, scale); /* 4 1|2 */ \ + res1 = _mm256_mul_ps(res1, scale); /* 4 1|2 */ \ +} + +#define CONVERT_I12_F32_BLOCK_STORE1(reg) \ +{ \ + __m256 res0, res1; \ + CONVERT_I12_F32_BLOCK(reg, res0, res1); \ + _MM256_STOREX_PS(outdata + 0, res0); \ + _MM256_STOREX_PS(outdata + 8, res1); \ + outdata += 16; \ +} + +#define CONVERT_CI12_2CF32_BLOCK_OPT(reg, res0, res1) \ +{ \ + __m256i result0, result1; \ + CONVERT_CI12_2CI32_BLOCK_OPT(reg, result0, result1); \ + \ + res0 = _mm256_mul_ps(_mm256_cvtepi32_ps(result0), scale2); \ + res1 = _mm256_mul_ps(_mm256_cvtepi32_ps(result1), scale2); \ +} + +#define CONVERT_CI12_4CF32_BLOCK_OPT(reg0, reg1, res0, res1, res2, res3) \ +{ \ + __m256i r0, r1, r2, r3; \ + CONVERT_CI12_4CI32_BLOCK_OPT(reg0, reg1, r0, r1, r2, r3); \ + \ + res0 = _mm256_mul_ps(_mm256_cvtepi32_ps(r0), scale2); \ + res1 = _mm256_mul_ps(_mm256_cvtepi32_ps(r1), scale2); \ + res2 = _mm256_mul_ps(_mm256_cvtepi32_ps(r2), scale2); \ + res3 = _mm256_mul_ps(_mm256_cvtepi32_ps(r3), scale2); \ +} diff --git a/src/lib/xdsp/templates/conv_i12_f32_avx2.t b/src/lib/xdsp/templates/conv_i12_f32_avx2.t index d02d1d84..65a0a9e3 100644 --- a/src/lib/xdsp/templates/conv_i12_f32_avx2.t +++ b/src/lib/xdsp/templates/conv_i12_f32_avx2.t @@ -10,68 +10,10 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, i = (outdatabsz * 3 / 8); const uint64_t *in = (const uint64_t*)indata_p; - float* out = (float*)outdata_p; + float* outdata = (float*)outdata_p; - const __m256 scale = _mm256_set1_ps(CONV_SCALE); - const __m256i load_mask = _mm256_set_epi64x(0, -1, -1, -1); - - const __m256i mask0 = _mm256_set1_epi64x(0xfff00000fff00000); - const __m256i mask1 = _mm256_set1_epi64x(0x0000fff00000fff0); - - const __m256i permmask = _mm256_set_epi32(5, 4, 3, 7, 6, 2, 1, 0); - const __m256i shfl = _mm256_set_epi8( - 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, - 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, - 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, - 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80); - -/* -* reg: -* | (3) | (2) | (1) | (0) | -* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -* | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | -* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -* | 0 0 0 0 0 0 0 0 | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | -* -* v0: -* | | | | | -* | f15 | f14 | f13 | f12 | f11 | f10 | f9 | f8 | 0 0 0 0 0 0 0 0 | f7 | f6 | f5 | f4 | f3 | f2 | f1 | f0 | -* r: -* | | | | | -* | f15 | f14 | 0 | f13 | f12 | 0 | f11 | f10 | 0 | f9 | f8 | 0 | f7 | f6 | 0 | f5 | f4 | 0 | f3 | f2 | 0 | f1 | f0 | 0 | -* r0: -* | | | | | -* | f15 |0| 00 00 | f13 |0| 00 00 | f11 |0| 00 00 | f9 |0| 00 00 | f7 |0| 00 00 | f5 |0| 00 00 | f3 |0| 00 00 | f1 |0| 00 00 | - r1: -* | | | | | -* | 00 00 | f14 |0| 00 00 | f12 |0| 00 00 | f10 |0| 00 00 | f8 |0| 00 00 | f6 |0| 00 00 | f4 |0| 00 00 | f2 |0| 00 00 | f0 |0| -* res: -* | | | | | -* | f15 |0| f14 |0| f13 |0| f12 |0| f11 |0| f10 |0| f9 |0| f8 |0| f7 |0| f6 |0| f5 |0| f4 |0| f3 |0| f2 |0| f1 |0| f0 |0| -*/ - -#define CONVERT_I12_F32_BLOCK(reg) \ - { \ - __m256i v0 = _mm256_permutevar8x32_epi32(reg, permmask); \ - __m256i r = _mm256_shuffle_epi8(v0, shfl); \ - \ - __m256i r0 = _mm256_and_si256(r, mask0); \ - __m256i r1 = _mm256_and_si256(_mm256_srli_epi64(r, 4), mask1); \ - __m256i result = _mm256_or_si256(r0, r1); \ - \ - __m256i d0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(result)); \ - __m256i d1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(result, 1)); \ - \ - __m256 f0 = _mm256_cvtepi32_ps(d0); /* 4 1|2 */ \ - __m256 f1 = _mm256_cvtepi32_ps(d1); /* 4 1|2 */ \ - \ - f0 = _mm256_mul_ps(f0, scale); /* 4 1|2 */ \ - f1 = _mm256_mul_ps(f1, scale); /* 4 1|2 */ \ - \ - _MM256_STOREX_PS(out, f0); out += 8; /* 1 1|2 */ \ - _MM256_STOREX_PS(out, f1); out += 8; /* 1 1|2 */ \ - } -// CONVERT_I12_F32_BLOCK end lat 40 +#include "conv_i12_i16_avx2.inc" +#include "conv_i12_f32_avx2.inc" __m256i y0, y1, y2, y3; @@ -85,10 +27,10 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, for (; i >= 2*96; i -= 96) { - CONVERT_I12_F32_BLOCK(y0); - CONVERT_I12_F32_BLOCK(y1); - CONVERT_I12_F32_BLOCK(y2); - CONVERT_I12_F32_BLOCK(y3); + CONVERT_I12_F32_BLOCK_STORE1(y0); + CONVERT_I12_F32_BLOCK_STORE1(y1); + CONVERT_I12_F32_BLOCK_STORE1(y2); + CONVERT_I12_F32_BLOCK_STORE1(y3); y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); // 8 1/3 y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); // 8 1/3 @@ -99,36 +41,23 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, i -= 96; - CONVERT_I12_F32_BLOCK(y0); - CONVERT_I12_F32_BLOCK(y1); - CONVERT_I12_F32_BLOCK(y2); - CONVERT_I12_F32_BLOCK(y3); + CONVERT_I12_F32_BLOCK_STORE1(y0); + CONVERT_I12_F32_BLOCK_STORE1(y1); + CONVERT_I12_F32_BLOCK_STORE1(y2); + CONVERT_I12_F32_BLOCK_STORE1(y3); } +#undef CONVERT_I12_I16_BLOCK +#undef CONVERT_I12_2I32_SEPARATED +#undef CONVERT_CI12_2CI32_BLOCK_OPT +#undef CONVERT_CI12_4CI32_BLOCK_OPT + #undef CONVERT_I12_F32_BLOCK +#undef CONVERT_I12_F32_BLOCK_STORE1 +#undef CONVERT_CI12_2CF32_BLOCK_OPT +#undef CONVERT_CI12_4CF32_BLOCK_OPT const uint8_t *indata = (const uint8_t*)in; - - while(i >= 3) - { - uint8_t v0 = *(indata++); - uint8_t v1 = *(indata++); - uint8_t v2 = *(indata++); - i -= 3; - - float a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); - float b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); - - *(out++) = a * CONV_SCALE; - *(out++) = b * CONV_SCALE; - } - - if(i >= 2) - { - uint16_t v = *(const uint16_t*)indata; - float a = (int16_t)(v << 4); - *(out++) = a * CONV_SCALE; - i -= 2; - } + #include "conv_i12_f32_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i12_f32_avx512bw.inc b/src/lib/xdsp/templates/conv_i12_f32_avx512bw.inc new file mode 100644 index 00000000..bee9eb28 --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_f32_avx512bw.inc @@ -0,0 +1,37 @@ +UNUSED const __m512 scale = _mm512_set1_ps(CONV_SCALE); +UNUSED const __m512 scale2 = _mm512_set1_ps(SCALE2); + +#define CONVERT_I12_F32_BLOCK(reg, res0, res1) \ +{ \ + __m512i result; \ + CONVERT_I12_I16_BLOCK(reg, result); \ + \ + __m512i d0 = _mm512_cvtepi16_epi32(_mm512_castsi512_si256(result)); \ + __m512i d1 = _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(result, 1)); \ + \ + res0 = _mm512_cvtepi32_ps(d0); \ + res1 = _mm512_cvtepi32_ps(d1); \ + \ + res0 = _mm512_mul_ps(res0, scale); \ + res1 = _mm512_mul_ps(res1, scale); \ +} + +#define CONVERT_CI12_2CF32_BLOCK_OPT(reg, res0, res1) \ +{ \ + __m512i result0, result1; \ + CONVERT_CI12_2CI32_BLOCK_OPT(reg, result0, result1); \ + \ + res0 = _mm512_mul_ps(_mm512_cvtepi32_ps(result0), scale2); \ + res1 = _mm512_mul_ps(_mm512_cvtepi32_ps(result1), scale2); \ +} + +#define CONVERT_CI12_4CF32_BLOCK_OPT(reg0, reg1, res0, res1, res2, res3) \ +{ \ + __m512i r0, r1, r2, r3; \ + CONVERT_CI12_4CI32_BLOCK_OPT(reg0, reg1, r0, r1, r2, r3); \ + \ + res0 = _mm512_mul_ps(_mm512_cvtepi32_ps(r0), scale2); \ + res1 = _mm512_mul_ps(_mm512_cvtepi32_ps(r1), scale2); \ + res2 = _mm512_mul_ps(_mm512_cvtepi32_ps(r2), scale2); \ + res3 = _mm512_mul_ps(_mm512_cvtepi32_ps(r3), scale2); \ +} diff --git a/src/lib/xdsp/templates/conv_i12_f32_avx512bw.t b/src/lib/xdsp/templates/conv_i12_f32_avx512bw.t new file mode 100644 index 00000000..f47bd533 --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_f32_avx512bw.t @@ -0,0 +1,83 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + /* 12 bits -> 32 bits => 3 -> 8 */ + if ((outdatabsz * 3 / 8) < i) + i = (outdatabsz * 3 / 8); + + const uint64_t *in = (const uint64_t*)indata_p; + float* outdata = (float*)outdata_p; + + //AVX512 block + { + #include "conv_i12_i16_avx512bw.inc" + #include "conv_i12_f32_avx512bw.inc" + + __m512i y0, y1; + __m512 res0, res1, res2, res3; + + for(; i >= 96; i -= 96) + { + y0 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 0)); + y1 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 6)); + in += 12; + + CONVERT_I12_F32_BLOCK(y0, res0, res1); + CONVERT_I12_F32_BLOCK(y1, res2, res3); + + _mm512_storeu_ps(outdata + 0, res0); + _mm512_storeu_ps(outdata + 16, res1); + _mm512_storeu_ps(outdata + 32, res2); + _mm512_storeu_ps(outdata + 48, res3); + outdata += 64; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT + } + + //AVX2 block + { + #include "conv_i12_i16_avx2.inc" + #include "conv_i12_f32_avx2.inc" + + if(i >= 48) + { + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); + in += 6; + i -= 48; + + CONVERT_I12_F32_BLOCK_STORE1(y0); + CONVERT_I12_F32_BLOCK_STORE1(y1); + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + + #undef CONVERT_I12_F32_BLOCK + #undef CONVERT_I12_F32_BLOCK_STORE1 + #undef CONVERT_CI12_2CF32_BLOCK_OPT + #undef CONVERT_CI12_4CF32_BLOCK_OPT + } + + //Generic block + { + const uint8_t *indata = (const uint8_t*)in; + #include "conv_i12_f32_generic.inc" + } +} +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i12_f32_generic.inc b/src/lib/xdsp/templates/conv_i12_f32_generic.inc new file mode 100644 index 00000000..23b1463c --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_f32_generic.inc @@ -0,0 +1,42 @@ +/* memory stream: MSB...LSB +* 0x00 f0[7:0] +* 0x01 {f1[3:0],f0[11:8]} +* 0x02 f1[11:4] +* ..... +*/ + +/* v2 v1 v0 +* +-----+-----+-----+ +* | 8 | 8 | 8 | +* +-----+-----+-----+ +* | 12 | 12 | +* +-----+-----+-----+ +* +* +-----+-----+ +* as = |v1| v0 |00| +* +-----+-----+ +* bs = | v2 |v1|00| +* +-----+-----+ +*/ + +while(i >= 3) +{ + uint8_t v0 = *(indata++); + uint8_t v1 = *(indata++); + uint8_t v2 = *(indata++); + i -= 3; + + float a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); + float b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); + + *(outdata++) = a * CONV_SCALE; + *(outdata++) = b * CONV_SCALE; +} + +if(i >= 2) +{ + uint16_t v = *(const uint16_t*)indata; + float a = (int16_t)(v << 4); + *(outdata++) = a * CONV_SCALE; + i -= 2; +} diff --git a/src/lib/xdsp/templates/conv_i12_f32_generic.t b/src/lib/xdsp/templates/conv_i12_f32_generic.t index b579f98f..a447930a 100644 --- a/src/lib/xdsp/templates/conv_i12_f32_generic.t +++ b/src/lib/xdsp/templates/conv_i12_f32_generic.t @@ -12,156 +12,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, const uint8_t* indata = (const uint8_t*)indata_p; float* outdata = (float*)outdata_p; -#if 0 - uint64_t v0, v1, v2, a0, a1, a2, a3; - - typedef int16_t v4i16 __attribute__ ((vector_size (8))); - union u_v4i16 { uint64_t vect; v4i16 arr; }; - typedef union u_v4i16 u_v4i16_t; - - u_v4i16_t x[4]; -#endif - -#if 0 - for (; i >= 3 * sizeof(uint64_t); i -= 3 * sizeof(uint64_t)) { - /* read 64*3 = 192 bits -> 16 i32 & floats */ - - v0 = *(const uint64_t *)(indata + 0); - v1 = *(const uint64_t *)(indata + 8); - v2 = *(const uint64_t *)(indata + 16); - - indata += 3 * sizeof(uint64_t); - - a0 = (v0 << 4) & 0x000000000000fff0; - a1 = (v0 << 8) & 0x00000000fff00000; - a2 = (v0 << 12) & 0x0000fff000000000; - a3 = (v0 << 16) & 0xfff0000000000000; - - x[0].vect = a0 | a1 | a2 | a3; - - a0 = (v0 >> 44) & 0x000000000000fff0; - a1 = ((v0 >> 40) & 0x0000000000f00000) | ((v1 << 24) & 0x00000000ff000000); - a2 = (v1 << 28) & 0x0000fff000000000; - a3 = (v1 << 32) & 0xfff0000000000000; - - x[1].vect = a0 | a1 | a2 | a3; - - a0 = (v1 >> 28) & 0x000000000000fff0; - a1 = (v1 >> 24) & 0x00000000fff00000; - a2 = ((v1 >> 20) & 0x00000ff000000000) | ((v2 << 44) & 0x0000f00000000000); - a3 = (v2 << 48) & 0xfff0000000000000; - - x[2].vect = a0 | a1 | a2 | a3; - - a0 = (v2 >> 12) & 0x000000000000fff0; - a1 = (v2 >> 8) & 0x00000000fff00000; - a2 = (v2 >> 4) & 0x0000fff000000000; - a3 = v2 & 0xfff0000000000000; - - x[3].vect = a0 | a1 | a2 | a3; - - for(unsigned j = 0; j < 4; j++) - { - *(outdata++) = x[j].arr[0] * CONV_SCALE; - *(outdata++) = x[j].arr[1] * CONV_SCALE; - *(outdata++) = x[j].arr[2] * CONV_SCALE; - *(outdata++) = x[j].arr[3] * CONV_SCALE; - } - } -#endif - -#if 0 //this algorithm is slow - for (; i >= 3 * sizeof(uint64_t); i -= 3 * sizeof(uint64_t)) { - /* read 64*3 = 192 bits -> 16 i32 & floats */ - - v0 = *(const uint64_t *)(indata + 0); - v1 = *(const uint64_t *)(indata + 8); - v2 = *(const uint64_t *)(indata + 16); - - indata += 3 * sizeof(uint64_t); - - float f0 = (int16_t)(v0 << 4); - float f1 = (int16_t)((v0 >> 8) & 0xfff0); - float f2 = (int16_t)((v0 >> 20) & 0xfff0); - float f3 = (int16_t)((v0 >> 32) & 0xfff0); - float f4 = (int16_t)((v0 >> 44) & 0xfff0); - - float f5 = ((int16_t)(v0 >> 56) & 0x00f0) | (int16_t)(v1 << 8); - - float f6 = (int16_t)((v1 >> 4) & 0xfff0); - float f7 = (int16_t)((v1 >> 16) & 0xfff0); - float f8 = (int16_t)((v1 >> 28) & 0xfff0); - float f9 = (int16_t)((v1 >> 40) & 0xfff0); - - float f10 = (int16_t)((v1 >> 52) & 0x0ff0) | (int16_t)(v2 << 12); - - float f11 = (int16_t)(v2 & 0xfff0); - float f12 = (int16_t)((v2 >> 12) & 0xfff0); - float f13 = (int16_t)((v2 >> 24) & 0xfff0); - float f14 = (int16_t)((v2 >> 36) & 0xfff0); - float f15 = (int16_t)((v2 >> 48) & 0xfff0); - - *(outdata++) = f0 * CONV_SCALE; - *(outdata++) = f1 * CONV_SCALE; - *(outdata++) = f2 * CONV_SCALE; - *(outdata++) = f3 * CONV_SCALE; - *(outdata++) = f4 * CONV_SCALE; - *(outdata++) = f5 * CONV_SCALE; - *(outdata++) = f6 * CONV_SCALE; - *(outdata++) = f7 * CONV_SCALE; - *(outdata++) = f8 * CONV_SCALE; - *(outdata++) = f9 * CONV_SCALE; - *(outdata++) = f10 * CONV_SCALE; - *(outdata++) = f11 * CONV_SCALE; - *(outdata++) = f12 * CONV_SCALE; - *(outdata++) = f13 * CONV_SCALE; - *(outdata++) = f14 * CONV_SCALE; - *(outdata++) = f15 * CONV_SCALE; - } -#endif - - /* memory stream: MSB...LSB - * 0x00 f0[7:0] - * 0x01 {f1[3:0],f0[11:8]} - * 0x02 f1[11:4] - * ..... - */ - - /* v2 v1 v0 - * +-----+-----+-----+ - * | 8 | 8 | 8 | - * +-----+-----+-----+ - * | 12 | 12 | - * +-----+-----+-----+ - * - * +-----+-----+ - * as = |v1| v0 |00| - * +-----+-----+ - * bs = | v2 |v1|00| - * +-----+-----+ - */ - - while(i >= 3) - { - uint8_t v0 = *(indata++); - uint8_t v1 = *(indata++); - uint8_t v2 = *(indata++); - i -= 3; - - float a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); - float b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); - - *(outdata++) = a * CONV_SCALE; - *(outdata++) = b * CONV_SCALE; - } - - if(i >= 2) - { - uint16_t v = *(const uint16_t*)indata; - float a = (int16_t)(v << 4); - *(outdata++) = a * CONV_SCALE; - i -= 2; - } + #include "conv_i12_f32_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i12_i16_avx2.inc b/src/lib/xdsp/templates/conv_i12_i16_avx2.inc new file mode 100644 index 00000000..9c3493fb --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_i16_avx2.inc @@ -0,0 +1,85 @@ +const __m256i load_mask = _mm256_set_epi64x(0, -1, -1, -1); + +const __m256i mask0 = _mm256_set1_epi64x(0xfff00000fff00000); +UNUSED const __m256i mask1 = _mm256_set1_epi64x(0x0000fff00000fff0); + +const __m256i permmask = _mm256_set_epi32(5, 4, 3, 7, 6, 2, 1, 0); +const __m256i shfl = _mm256_set_epi8( + 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, + 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, + 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, + 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80); + +#define CONVERT_I12_I16_BLOCK(reg, result) \ +{ \ + __m256i v0 = _mm256_permutevar8x32_epi32(reg, permmask); \ + __m256i r = _mm256_shuffle_epi8(v0, shfl); \ + \ + __m256i r0 = _mm256_and_si256(r, mask0); \ + __m256i r1 = _mm256_and_si256(_mm256_srli_epi64(r, 4), mask1); \ + result = _mm256_or_si256(r0, r1); \ +} + +/* +* input (i12): |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| +* +* output(i32): |14 xx|12 xx|10 xx| 8 xx| 6 xx| 4 xx| 2 xx| 0 xx| + (hi word) |15 xx|13 xx|11 xx| 9 xx| 7 xx| 5 xx| 3 xx| 1 xx| - 2CH i32 interleave +*/ +#define CONVERT_I12_2I32_SEPARATED(reg, res0, res1) \ +{ \ + __m256i v0 = _mm256_permutevar8x32_epi32(reg, permmask); \ + __m256i r = _mm256_shuffle_epi8(v0, shfl); \ + \ + res1 = _mm256_and_si256(r, mask0); \ + res0 = _mm256_and_si256(_mm256_slli_epi64(r, 12), mask0); \ +} + +/* +* input (ci12): |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| +* +* output(ci32): |13 xx|12 xx| 9 xx| 8 xx| 5 xx| 4 xx| 1 xx| 0 xx| + (hi word) |15 xx|14 xx|11 xx|10 xx| 7 xx| 6 xx| 3 xx| 2 xx| - 2CH ci32 interleave +*/ +#define CONVERT_CI12_2CI32_BLOCK_OPT(reg, res0, res1) \ +{ \ + __m256i a0, a1; \ + CONVERT_I12_2I32_SEPARATED(reg, a0, a1); \ + \ + __m256i b0 = _mm256_unpacklo_epi32(a0, a1); \ + __m256i b1 = _mm256_unpackhi_epi32(a0, a1); \ + res0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(b0), _mm256_castsi256_pd(b1), 0b0000)); \ + res1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(b0), _mm256_castsi256_pd(b1), 0b1111)); \ +} + +/* +* input (ci12): |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| +* |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16| +* +* output(ci32): |25 xx|24 xx|17 xx|16 xx| 9 xx| 8 xx| 1 xx| 0 xx| + (hi word) |27 xx|26 xx|19 xx|18 xx|11 xx|10 xx| 3 xx| 2 xx| + (hi word) |29 xx|28 xx|21 xx|20 xx|13 xx|12 xx| 5 xx| 4 xx| + (hi word) |31 xx|30 xx|23 xx|22 xx|15 xx|14 xx| 7 xx| 6 xx| - 4CH ci32 interleave +*/ +#define CONVERT_CI12_4CI32_BLOCK_OPT(reg0, reg1, res0, res1, res2, res3) \ +{ \ + __m256i a0, a1, a2, a3; \ + CONVERT_I12_2I32_SEPARATED(reg0, a0, a1); \ + CONVERT_I12_2I32_SEPARATED(reg1, a2, a3); \ + \ + __m256i b0 = _mm256_unpacklo_epi32(a0, a1); \ + __m256i b1 = _mm256_unpackhi_epi32(a0, a1); \ + __m256i b2 = _mm256_unpacklo_epi32(a2, a3); \ + __m256i b3 = _mm256_unpackhi_epi32(a2, a3); \ + \ + __m256i c0 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(b0), _mm256_castsi256_pd(b2), 0b0000)); \ + __m256i c1 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(b0), _mm256_castsi256_pd(b2), 0b1111)); \ + __m256i c2 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(b1), _mm256_castsi256_pd(b3), 0b0000)); \ + __m256i c3 = _mm256_castpd_si256(_mm256_shuffle_pd(_mm256_castsi256_pd(b1), _mm256_castsi256_pd(b3), 0b1111)); \ + \ + res0 = _mm256_permute4x64_epi64(c0, _MM_SHUFFLE(3,1,2,0)); \ + res1 = _mm256_permute4x64_epi64(c1, _MM_SHUFFLE(3,1,2,0)); \ + res2 = _mm256_permute4x64_epi64(c2, _MM_SHUFFLE(3,1,2,0)); \ + res3 = _mm256_permute4x64_epi64(c3, _MM_SHUFFLE(3,1,2,0)); \ +} + diff --git a/src/lib/xdsp/templates/conv_i12_i16_avx2.t b/src/lib/xdsp/templates/conv_i12_i16_avx2.t index a1319b22..36ee8ee3 100644 --- a/src/lib/xdsp/templates/conv_i12_i16_avx2.t +++ b/src/lib/xdsp/templates/conv_i12_i16_avx2.t @@ -12,33 +12,10 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, const uint64_t *in = (const uint64_t*)indata_p; int16_t* outdata = (int16_t*)outdata_p; - const __m256i load_mask = _mm256_set_epi64x(0, -1, -1, -1); - - const __m256i mask0 = _mm256_set1_epi64x(0xfff00000fff00000); - const __m256i mask1 = _mm256_set1_epi64x(0x0000fff00000fff0); - - const __m256i permmask = _mm256_set_epi32(5, 4, 3, 7, 6, 2, 1, 0); - const __m256i shfl = _mm256_set_epi8( - 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, - 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, - 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, - 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80); - -#define CONVERT_I12_I16_BLOCK(reg) \ - { \ - __m256i v0 = _mm256_permutevar8x32_epi32(reg, permmask); \ - __m256i r = _mm256_shuffle_epi8(v0, shfl); \ - \ - __m256i r0 = _mm256_and_si256(r, mask0); \ - __m256i r1 = _mm256_and_si256(_mm256_srli_epi64(r, 4), mask1); \ - __m256i result = _mm256_or_si256(r0, r1); \ - \ - _mm256_store_si256((__m256i*)outdata, result); \ - outdata += 16; \ - } -// CONVERT_I12_I16_BLOCK end +#include "conv_i12_i16_avx2.inc" __m256i y0, y1, y2, y3; + __m256i rs0, rs1, rs2, rs3; if(i >= 96) { @@ -50,10 +27,16 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, for (; i >= 2*96; i -= 96) { - CONVERT_I12_I16_BLOCK(y0); - CONVERT_I12_I16_BLOCK(y1); - CONVERT_I12_I16_BLOCK(y2); - CONVERT_I12_I16_BLOCK(y3); + CONVERT_I12_I16_BLOCK(y0, rs0); + CONVERT_I12_I16_BLOCK(y1, rs1); + CONVERT_I12_I16_BLOCK(y2, rs2); + CONVERT_I12_I16_BLOCK(y3, rs3); + + _mm256_store_si256((__m256i*)(outdata + 0), rs0); + _mm256_store_si256((__m256i*)(outdata + 16), rs1); + _mm256_store_si256((__m256i*)(outdata + 32), rs2); + _mm256_store_si256((__m256i*)(outdata + 48), rs3); + outdata += 64; y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); // 8 1/3 y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); // 8 1/3 @@ -64,37 +47,25 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, i -= 96; - CONVERT_I12_I16_BLOCK(y0); - CONVERT_I12_I16_BLOCK(y1); - CONVERT_I12_I16_BLOCK(y2); - CONVERT_I12_I16_BLOCK(y3); + CONVERT_I12_I16_BLOCK(y0, rs0); + CONVERT_I12_I16_BLOCK(y1, rs1); + CONVERT_I12_I16_BLOCK(y2, rs2); + CONVERT_I12_I16_BLOCK(y3, rs3); + + _mm256_store_si256((__m256i*)(outdata + 0), rs0); + _mm256_store_si256((__m256i*)(outdata + 16), rs1); + _mm256_store_si256((__m256i*)(outdata + 32), rs2); + _mm256_store_si256((__m256i*)(outdata + 48), rs3); + outdata += 64; } #undef CONVERT_I12_I16_BLOCK +#undef CONVERT_I12_2I32_SEPARATED +#undef CONVERT_CI12_2CI32_BLOCK_OPT +#undef CONVERT_CI12_4CI32_BLOCK_OPT const uint8_t* indata = (const uint8_t*)in; - - while(i >= 3) - { - const uint8_t v0 = *(indata++); - const uint8_t v1 = *(indata++); - const uint8_t v2 = *(indata++); - i -= 3; - - const int16_t a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); - const int16_t b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); - - *(outdata++) = a; - *(outdata++) = b; - } - - if(i >= 2) - { - const uint16_t v = *(const uint16_t*)indata; - const int16_t a = (int16_t)(v << 4); - *(outdata++) = a; - i -= 2; - } + #include "conv_i12_i16_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i12_i16_avx512bw.inc b/src/lib/xdsp/templates/conv_i12_i16_avx512bw.inc new file mode 100644 index 00000000..63931311 --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_i16_avx512bw.inc @@ -0,0 +1,67 @@ +const __m512i mask0 = _mm512_set1_epi64(0xfff00000fff00000); +UNUSED const __m512i mask1 = _mm512_set1_epi64(0x0000fff00000fff0); + +const __m512i permmask = _mm512_set_epi32(11,10,9, 15,14, 8,7,6, 5,4,3, 13,12, 2,1,0); +const __m512i shfl = _mm512_set_epi8( + 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, + 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, + 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, + 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80, + 0x0f, 0x0e, 0x0d, 0x80, 0x0c, 0x0b, 0x0a, 0x80, + 0x09, 0x08, 0x07, 0x80, 0x06, 0x05, 0x04, 0x80, + 0x0b, 0x0a, 0x09, 0x80, 0x08, 0x07, 0x06, 0x80, + 0x05, 0x04, 0x03, 0x80, 0x02, 0x01, 0x00, 0x80); + +UNUSED const __m512i pidx = _mm512_set_epi64(7,5,3,1,6,4,2,0); + +#define CONVERT_I12_I16_BLOCK(reg, result) \ +{ \ + __m512i v0 = _mm512_permutexvar_epi32(permmask, reg); \ + __m512i r = _mm512_shuffle_epi8(v0, shfl); \ + \ + __m512i r0 = _mm512_and_si512(r, mask0); \ + __m512i r1 = _mm512_and_si512(_mm512_srli_epi64(r, 4), mask1); \ + result = _mm512_or_si512(r0, r1); \ +} + +#define CONVERT_I12_2I32_SEPARATED(reg, res0, res1) \ +{ \ + __m512i v0 = _mm512_permutexvar_epi32(permmask, reg); \ + __m512i r = _mm512_shuffle_epi8(v0, shfl); \ + \ + res1 = _mm512_and_si512(r, mask0); \ + res0 = _mm512_and_si512(_mm512_slli_epi64(r, 12), mask0); \ +} + +#define CONVERT_CI12_2CI32_BLOCK_OPT(reg, res0, res1) \ +{ \ + __m512i a0, a1; \ + CONVERT_I12_2I32_SEPARATED(reg, a0, a1); \ + \ + __m512i b0 = _mm512_unpacklo_epi32(a0, a1); \ + __m512i b1 = _mm512_unpackhi_epi32(a0, a1); \ + res0 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(b0), _mm512_castsi512_pd(b1), 0b00000000)); \ + res1 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(b0), _mm512_castsi512_pd(b1), 0b11111111)); \ +} + +#define CONVERT_CI12_4CI32_BLOCK_OPT(reg0, reg1, res0, res1, res2, res3) \ +{ \ + __m512i a0, a1, a2, a3; \ + CONVERT_I12_2I32_SEPARATED(reg0, a0, a1); \ + CONVERT_I12_2I32_SEPARATED(reg1, a2, a3); \ + \ + __m512i b0 = _mm512_unpacklo_epi32(a0, a1); \ + __m512i b1 = _mm512_unpackhi_epi32(a0, a1); \ + __m512i b2 = _mm512_unpacklo_epi32(a2, a3); \ + __m512i b3 = _mm512_unpackhi_epi32(a2, a3); \ + \ + __m512i c0 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(b0), _mm512_castsi512_pd(b2), 0b00000000)); \ + __m512i c1 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(b0), _mm512_castsi512_pd(b2), 0b11111111)); \ + __m512i c2 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(b1), _mm512_castsi512_pd(b3), 0b00000000)); \ + __m512i c3 = _mm512_castpd_si512(_mm512_shuffle_pd(_mm512_castsi512_pd(b1), _mm512_castsi512_pd(b3), 0b11111111)); \ + \ + res0 = _mm512_permutexvar_epi64(pidx, c0); \ + res1 = _mm512_permutexvar_epi64(pidx, c1); \ + res2 = _mm512_permutexvar_epi64(pidx, c2); \ + res3 = _mm512_permutexvar_epi64(pidx, c3); \ +} diff --git a/src/lib/xdsp/templates/conv_i12_i16_avx512bw.t b/src/lib/xdsp/templates/conv_i12_i16_avx512bw.t new file mode 100644 index 00000000..6495688d --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_i16_avx512bw.t @@ -0,0 +1,76 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + /* 12 bits -> 16 bits => 3 -> 4 */ + if ((outdatabsz * 3 / 4) < i) + i = (outdatabsz * 3 / 4); + + const uint64_t *in = (const uint64_t*)indata_p; + int16_t* outdata = (int16_t*)outdata_p; + + //AVX512BW block + { + #include "conv_i12_i16_avx512bw.inc" + + __m512i y0, y1; + __m512i rs0, rs1; + + for(; i >= 96; i -= 96) + { + y0 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 0)); + y1 = _mm512_maskz_loadu_epi64(0b00111111, (const long long*)(in + 6)); + in += 12; + + CONVERT_I12_I16_BLOCK(y0, rs0); + CONVERT_I12_I16_BLOCK(y1, rs1); + + _mm512_store_si512((__m512i*)(outdata + 0), rs0); + _mm512_store_si512((__m512i*)(outdata + 32), rs1); + outdata += 64; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + } + + //AVX2 block + { + #include "conv_i12_i16_avx2.inc" + + if(i >= 48) + { + __m256i y0 = _mm256_maskload_epi64((const long long*)(in + 0), load_mask); // 8 1/3 + __m256i y1 = _mm256_maskload_epi64((const long long*)(in + 3), load_mask); // 8 1/3 + in += 6; + i -= 48; + + __m256i rs0, rs1; + + CONVERT_I12_I16_BLOCK(y0, rs0); + CONVERT_I12_I16_BLOCK(y1, rs1); + + _mm256_store_si256((__m256i*)(outdata + 0), rs0); + _mm256_store_si256((__m256i*)(outdata + 16), rs1); + outdata += 32; + } + + #undef CONVERT_I12_I16_BLOCK + #undef CONVERT_I12_2I32_SEPARATED + #undef CONVERT_CI12_2CI32_BLOCK_OPT + #undef CONVERT_CI12_4CI32_BLOCK_OPT + } + + //Generic block + { + const uint8_t* indata = (const uint8_t*)in; + #include "conv_i12_i16_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i12_i16_generic.inc b/src/lib/xdsp/templates/conv_i12_i16_generic.inc new file mode 100644 index 00000000..e77b1e79 --- /dev/null +++ b/src/lib/xdsp/templates/conv_i12_i16_generic.inc @@ -0,0 +1,42 @@ +/* memory stream: MSB...LSB +* 0x00 f0[7:0] +* 0x01 {f1[3:0],f0[11:8]} +* 0x02 f1[11:4] +* ..... +*/ + +/* v2 v1 v0 +* +-----+-----+-----+ +* | 8 | 8 | 8 | +* +-----+-----+-----+ +* | 12 | 12 | +* +-----+-----+-----+ +* +* +-----+-----+ +* as = |v1| v0 |00| +* +-----+-----+ +* bs = | v2 |v1|00| +* +-----+-----+ +*/ + +while(i >= 3) +{ + const uint8_t v0 = *(indata++); + const uint8_t v1 = *(indata++); + const uint8_t v2 = *(indata++); + i -= 3; + + const int16_t a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); + const int16_t b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); + + *(outdata++) = a; + *(outdata++) = b; +} + +if(i >= 2) +{ + const uint16_t v = *(const uint16_t*)indata; + const int16_t a = (int16_t)(v << 4); + *(outdata++) = a; + i -= 2; +} diff --git a/src/lib/xdsp/templates/conv_i12_i16_generic.t b/src/lib/xdsp/templates/conv_i12_i16_generic.t index 75b0880f..cf6e7af3 100644 --- a/src/lib/xdsp/templates/conv_i12_i16_generic.t +++ b/src/lib/xdsp/templates/conv_i12_i16_generic.t @@ -12,48 +12,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, const uint8_t* indata = (const uint8_t*)indata_p; int16_t* outdata = (int16_t*)outdata_p; - /* memory stream: MSB...LSB - * 0x00 f0[7:0] - * 0x01 {f1[3:0],f0[11:8]} - * 0x02 f1[11:4] - * ..... - */ - - /* v2 v1 v0 - * +-----+-----+-----+ - * | 8 | 8 | 8 | - * +-----+-----+-----+ - * | 12 | 12 | - * +-----+-----+-----+ - * - * +-----+-----+ - * as = |v1| v0 |00| - * +-----+-----+ - * bs = | v2 |v1|00| - * +-----+-----+ - */ - - while(i >= 3) - { - const uint8_t v0 = *(indata++); - const uint8_t v1 = *(indata++); - const uint8_t v2 = *(indata++); - i -= 3; - - const int16_t a = (int16_t) (((uint16_t)v0 << 4) | ((uint16_t)v1 << 12)); - const int16_t b = (int16_t) (((uint16_t)v2 << 8) | (v1 & 0xf0)); - - *(outdata++) = a; - *(outdata++) = b; - } - - if(i >= 2) - { - const uint16_t v = *(const uint16_t*)indata; - const int16_t a = (int16_t)(v << 4); - *(outdata++) = a; - i -= 2; - } + #include "conv_i12_i16_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i16_f32_avx2.t b/src/lib/xdsp/templates/conv_i16_f32_avx2.t index 11117297..0246d53e 100644 --- a/src/lib/xdsp/templates/conv_i16_f32_avx2.t +++ b/src/lib/xdsp/templates/conv_i16_f32_avx2.t @@ -23,16 +23,16 @@ void TEMPLATE_FUNC_NAME(const int16_t *__restrict indata, f0 = _mm256_mul_ps(f0, scale); \ f1 = _mm256_mul_ps(f1, scale); \ \ - _mm256_storeu_ps(outdata, f0); outdata += 8; \ - _mm256_storeu_ps(outdata, f1); outdata += 8; \ + _mm256_store_ps(outdata, f0); outdata += 8; \ + _mm256_store_ps(outdata, f1); outdata += 8; \ } // CONVERT_I16_F32_BLOCK end for(; i >= 96; i -= 96) { - t0 = _mm256_loadu_si256(vp++); - t1 = _mm256_loadu_si256(vp++); - t2 = _mm256_loadu_si256(vp++); + t0 = _mm256_load_si256(vp++); + t1 = _mm256_load_si256(vp++); + t2 = _mm256_load_si256(vp++); CONVERT_I16_F32_BLOCK(t0); CONVERT_I16_F32_BLOCK(t1); @@ -41,8 +41,8 @@ void TEMPLATE_FUNC_NAME(const int16_t *__restrict indata, for(; i >= 64; i -= 64) { - t0 = _mm256_loadu_si256(vp++); - t1 = _mm256_loadu_si256(vp++); + t0 = _mm256_load_si256(vp++); + t1 = _mm256_load_si256(vp++); CONVERT_I16_F32_BLOCK(t0); CONVERT_I16_F32_BLOCK(t1); @@ -50,17 +50,15 @@ void TEMPLATE_FUNC_NAME(const int16_t *__restrict indata, for(; i >= 32; i -= 32) { - t0 = _mm256_loadu_si256(vp++); + t0 = _mm256_load_si256(vp++); CONVERT_I16_F32_BLOCK(t0); } #undef CONVERT_I16_F32_BLOCK - if (i > 0) { - const int16_t *ldw = (const int16_t *)vp; - for (; i >= 2; i -= 2) { - *(outdata++) = *(ldw++) * inscale; - } + const int16_t *ldw = (const int16_t *)vp; + for (; i >= 2; i -= 2) { + *(outdata++) = *(ldw++) * CONV_SCALE; } } diff --git a/src/lib/xdsp/templates/conv_i16_f32_avx512bw.t b/src/lib/xdsp/templates/conv_i16_f32_avx512bw.t new file mode 100644 index 00000000..fe59712d --- /dev/null +++ b/src/lib/xdsp/templates/conv_i16_f32_avx512bw.t @@ -0,0 +1,138 @@ +#ifndef UNWRAP_CNT +#define UNWRAP_CNT 4 +#endif + +#if UNWRAP_CNT > 4 +#error Maximum spported UNWRAP_CNT is 4! +#endif + +static +void TEMPLATE_FUNC_NAME(const int16_t *__restrict indata, + unsigned indatabsz, + float *__restrict outdata, + unsigned outdatabsz) +{ + size_t i = indatabsz; + if ((outdatabsz / 2) < i) + i = (outdatabsz / 2); + + const __m512i* vp = (const __m512i* )indata; + __m512 scale = _mm512_set1_ps(CONV_SCALE); + __m512i t0, t1; + __m512 f0, f1, f2, f3; +#if UNWRAP_CNT > 1 + __m512i t2, t3; + __m512 f4, f5, f6, f7; +#if UNWRAP_CNT > 2 + __m512i t4, t5; + __m512 f8, f9, fA, fB; +#if UNWRAP_CNT > 3 + __m512i t6, t7; + __m512 fC, fD, fE, fF; +#endif +#endif +#endif + +#define CONVERT_I16_F32_BLOCK(reg0, reg1, f0, f1, f2, f3) \ + { \ + __m512i d0 = _mm512_cvtepi16_epi32(_mm512_castsi512_si256(reg0)); \ + __m512i d1 = _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(reg0, 1)); \ + __m512i d2 = _mm512_cvtepi16_epi32(_mm512_castsi512_si256(reg1)); \ + __m512i d3 = _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(reg1, 1)); \ + \ + f0 = _mm512_cvtepi32_ps(d0); \ + f1 = _mm512_cvtepi32_ps(d1); \ + f2 = _mm512_cvtepi32_ps(d2); \ + f3 = _mm512_cvtepi32_ps(d3); \ + \ + f0 = _mm512_mul_ps(f0, scale); \ + f1 = _mm512_mul_ps(f1, scale); \ + f2 = _mm512_mul_ps(f2, scale); \ + f3 = _mm512_mul_ps(f3, scale); \ + \ + _mm512_store_ps(outdata, f0); \ + _mm512_store_ps(outdata, f1); \ + } +// CONVERT_I16_F32_BLOCK end + + + while(i >= UNWRAP_CNT * 128) + { + t0 = _mm512_load_si512(vp++); + t1 = _mm512_load_si512(vp++); +#if UNWRAP_CNT > 1 + t2 = _mm512_load_si512(vp++); + t3 = _mm512_load_si512(vp++); +#if UNWRAP_CNT > 2 + t4 = _mm512_load_si512(vp++); + t5 = _mm512_load_si512(vp++); +#if UNWRAP_CNT > 3 + t6 = _mm512_load_si512(vp++); + t7 = _mm512_load_si512(vp++); +#endif +#endif +#endif + + CONVERT_I16_F32_BLOCK(t0, t1, f0, f1, f2, f3); +#if UNWRAP_CNT > 1 + CONVERT_I16_F32_BLOCK(t2, t3, f4, f5, f6, f7); +#if UNWRAP_CNT > 2 + CONVERT_I16_F32_BLOCK(t4, t5, f8, f9, fA, fB); +#if UNWRAP_CNT > 3 + CONVERT_I16_F32_BLOCK(t6, t7, fC, fD, fE, fF); +#endif +#endif +#endif + + _mm512_store_ps(outdata + 0x00, f0); + _mm512_store_ps(outdata + 0x10, f1); + _mm512_store_ps(outdata + 0x20, f2); + _mm512_store_ps(outdata + 0x30, f3); +#if UNWRAP_CNT > 1 + _mm512_store_ps(outdata + 0x40, f4); + _mm512_store_ps(outdata + 0x50, f5); + _mm512_store_ps(outdata + 0x60, f6); + _mm512_store_ps(outdata + 0x70, f7); +#if UNWRAP_CNT > 2 + _mm512_store_ps(outdata + 0x80, f8); + _mm512_store_ps(outdata + 0x90, f9); + _mm512_store_ps(outdata + 0xa0, fA); + _mm512_store_ps(outdata + 0xb0, fB); +#if UNWRAP_CNT > 3 + _mm512_store_ps(outdata + 0xc0, fC); + _mm512_store_ps(outdata + 0xd0, fD); + _mm512_store_ps(outdata + 0xe0, fE); + _mm512_store_ps(outdata + 0xf0, fF); +#endif +#endif +#endif + + outdata += UNWRAP_CNT * 64; + i -= UNWRAP_CNT * 128; + } + + while(i >= 128) + { + t0 = _mm512_load_si512(vp++); + t1 = _mm512_load_si512(vp++); + + CONVERT_I16_F32_BLOCK(t0, t1, f0, f1, f2, f3); + + _mm512_store_ps(outdata + 0, f0); + _mm512_store_ps(outdata + 16, f1); + _mm512_store_ps(outdata + 32, f2); + _mm512_store_ps(outdata + 48, f3); + + outdata += 64; + i -= 128; + } + +#undef CONVERT_I16_F32_BLOCK + + const int16_t *ldw = (const int16_t *)vp; + for (; i >= 2; i -= 2) { + *(outdata++) = *(ldw++) * CONV_SCALE; + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i16_f32_neon.t b/src/lib/xdsp/templates/conv_i16_f32_neon.t index e8118fa2..b04feeba 100644 --- a/src/lib/xdsp/templates/conv_i16_f32_neon.t +++ b/src/lib/xdsp/templates/conv_i16_f32_neon.t @@ -45,7 +45,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata, i -= 8; } - while(i) + while(i >= 2) { *(out++) = *(in++) * CONV_SCALE; i -= 2; diff --git a/src/lib/xdsp/templates/conv_i16_i12_avx2.t b/src/lib/xdsp/templates/conv_i16_i12_avx2.t index 8830620a..2368752a 100644 --- a/src/lib/xdsp/templates/conv_i16_i12_avx2.t +++ b/src/lib/xdsp/templates/conv_i16_i12_avx2.t @@ -44,27 +44,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, const int16_t* indata = (const int16_t*)in256; uint8_t* outdata = (uint8_t*)out64; - for (; i >= 4; i -= 4) { - - const int16_t b0 = *indata++; - const int16_t b1 = *indata++; - - wu_i16u32_t a = {{b0, b1}}; - wu_u32b_t c = {(a.u & 0xfff00000) | ((a.u << 4) & 0x000fff00)}; - - *(outdata++) = c.b[1]; - *(outdata++) = c.b[2]; - *(outdata++) = c.b[3]; - } - - if(i >= 2) - { - wu_i16b_t c = {*indata}; - - *(outdata++) = c.b[0]; - *(outdata++) = c.b[1] >> 4; - i -= 2; - } +#include "conv_i16_i12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i16_i12_avx512bw.inc b/src/lib/xdsp/templates/conv_i16_i12_avx512bw.inc new file mode 100644 index 00000000..5f726d40 --- /dev/null +++ b/src/lib/xdsp/templates/conv_i16_i12_avx512bw.inc @@ -0,0 +1,27 @@ +const __m512i maske = _mm512_set1_epi64(0x0000fff00000fff0); +const __m512i masko = _mm512_set1_epi64(0xfff00000fff00000); + +const __m512i shfl0 = _mm512_set_epi8( + 0x80, 0x80, 0x80, 0x80, 0x0f, 0x0e, 0x0d, 0x0b, + 0x0a, 0x09, 0x07, 0x06, 0x05, 0x03, 0x02, 0x01, + 0x80, 0x80, 0x80, 0x80, 0x0f, 0x0e, 0x0d, 0x0b, + 0x0a, 0x09, 0x07, 0x06, 0x05, 0x03, 0x02, 0x01, + 0x80, 0x80, 0x80, 0x80, 0x0f, 0x0e, 0x0d, 0x0b, + 0x0a, 0x09, 0x07, 0x06, 0x05, 0x03, 0x02, 0x01, + 0x80, 0x80, 0x80, 0x80, 0x0f, 0x0e, 0x0d, 0x0b, + 0x0a, 0x09, 0x07, 0x06, 0x05, 0x03, 0x02, 0x01); + +const __m512i permmask0 = _mm512_set_epi32(/*zeros*/15,11,7,3, /*data*/14,13,12, 10,9,8, 6,5,4, 2,1,0); + +#define CONVERT_I16_I12_BLOCK(rin, pout64) \ +{ \ + __m512i ro0 = _mm512_and_si512(rin, masko); \ + __m512i re0 = _mm512_slli_epi64(_mm512_and_si512(rin, maske), 4); \ + __m512i r0 = _mm512_or_si512(ro0, re0); \ +\ + __m512i res = _mm512_shuffle_epi8(r0, shfl0); \ + res = _mm512_permutexvar_epi32(permmask0, res); \ +\ + _mm512_mask_storeu_epi64((long long *)pout64, 0b00111111, res); \ + pout64 += 6; \ +} diff --git a/src/lib/xdsp/templates/conv_i16_i12_avx512bw.t b/src/lib/xdsp/templates/conv_i16_i12_avx512bw.t new file mode 100644 index 00000000..1a142edf --- /dev/null +++ b/src/lib/xdsp/templates/conv_i16_i12_avx512bw.t @@ -0,0 +1,68 @@ +static +void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, + unsigned indatabsz, + void *__restrict outdata_p, + unsigned outdatabsz) +{ + unsigned i = indatabsz; + if ((outdatabsz * 4 / 3) < i) + i = (outdatabsz * 4 / 3); + + const void* raw_ptr = indata_p; + uint64_t *out64 = (uint64_t*)outdata_p; + + //AVX512 block + { + #include "conv_i16_i12_avx512bw.inc" + + const __m512i *in512 = (__m512i*)raw_ptr; + __m512i v0, v1; + + for (; i >= 64*2; i -= 64*2) + { + v0 = _mm512_loadu_si512(in512 + 0); + v1 = _mm512_loadu_si512(in512 + 1); + in512 += 2; + + CONVERT_I16_I12_BLOCK(v0, out64); + CONVERT_I16_I12_BLOCK(v1, out64); + } + + for (; i >= 64; i -= 64) + { + v0 = _mm512_loadu_si512(in512++); + CONVERT_I16_I12_BLOCK(v0, out64); + } + + raw_ptr = (void*)in512; + #undef CONVERT_I16_I12_BLOCK + } + + //AVX2 block + { + #include "conv_i16_i12_avx2.inc" + + const __m256i *in256 = (__m256i*)raw_ptr; + __m256i v0; + + if(i >= 32) + { + v0 = _mm256_loadu_si256(in256++); + CONVERT_I16_I12_BLOCK(v0, out64); + i -= 32; + } + + raw_ptr = (void*)in256; + #undef CONVERT_I16_I12_BLOCK + } + + //Generic block + { + const int16_t* indata = (const int16_t*)raw_ptr; + uint8_t* outdata = (uint8_t*)out64; + + #include "conv_i16_i12_generic.inc" + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/conv_i16_i12_generic.inc b/src/lib/xdsp/templates/conv_i16_i12_generic.inc new file mode 100644 index 00000000..a61fe897 --- /dev/null +++ b/src/lib/xdsp/templates/conv_i16_i12_generic.inc @@ -0,0 +1,21 @@ +for (; i >= 4; i -= 4) { + + const int16_t b0 = *indata++; + const int16_t b1 = *indata++; + + wu_i16u32_t a = {{b0, b1}}; + wu_u32b_t c = {(a.u & 0xfff00000) | ((a.u << 4) & 0x000fff00)}; + + *(outdata++) = c.b[1]; + *(outdata++) = c.b[2]; + *(outdata++) = c.b[3]; +} + +if(i >= 2) +{ + wu_i16b_t c = {*indata}; + + *(outdata++) = c.b[0]; + *(outdata++) = c.b[1] >> 4; + i -= 2; +} diff --git a/src/lib/xdsp/templates/conv_i16_i12_generic.t b/src/lib/xdsp/templates/conv_i16_i12_generic.t index 4de4018d..76cf3df4 100644 --- a/src/lib/xdsp/templates/conv_i16_i12_generic.t +++ b/src/lib/xdsp/templates/conv_i16_i12_generic.t @@ -11,27 +11,7 @@ void TEMPLATE_FUNC_NAME(const void *__restrict indata_p, const int16_t* indata = (const int16_t*)indata_p; uint8_t* outdata = (uint8_t*)outdata_p; - for (; i >= 4; i -= 4) { - - const int16_t b0 = *indata++; - const int16_t b1 = *indata++; - - wu_i16u32_t a = {{b0, b1}}; - wu_u32b_t c = {(a.u & 0xfff00000) | ((a.u << 4) & 0x000fff00)}; - - *(outdata++) = c.b[1]; - *(outdata++) = c.b[2]; - *(outdata++) = c.b[3]; - } - - if(i >= 2) - { - wu_i16b_t c = {*indata}; - - *(outdata++) = c.b[0]; - *(outdata++) = c.b[1] >> 4; - i -= 2; - } + #include "conv_i16_i12_generic.inc" } #undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/fft_window_cf32_avx2.t b/src/lib/xdsp/templates/fft_window_cf32_avx2.t index 07d0d0c4..ad811d71 100644 --- a/src/lib/xdsp/templates/fft_window_cf32_avx2.t +++ b/src/lib/xdsp/templates/fft_window_cf32_avx2.t @@ -12,8 +12,8 @@ void TEMPLATE_FUNC_NAME(wvlt_fftwf_complex* __restrict in, unsigned fftsz, float __m256 e2 = _mm256_load_ps(&in[i + 8][0]); __m256 e3 = _mm256_load_ps(&in[i + 12][0]); - __m256 w0 = _mm256_load_ps(&wnd[i + 0]); - __m256 w1 = _mm256_load_ps(&wnd[i + 8]); + __m256 w0 = _mm256_castsi256_ps(_mm256_stream_load_si256((__m256i*)&wnd[i + 0])); + __m256 w1 = _mm256_castsi256_ps(_mm256_stream_load_si256((__m256i*)&wnd[i + 8])); __m256 dw0 = _mm256_permutevar8x32_ps(w0, sh0); __m256 dw1 = _mm256_permutevar8x32_ps(w0, sh1); diff --git a/src/lib/xdsp/templates/fft_window_cf32_avx512bw.t b/src/lib/xdsp/templates/fft_window_cf32_avx512bw.t new file mode 100644 index 00000000..a12e1f41 --- /dev/null +++ b/src/lib/xdsp/templates/fft_window_cf32_avx512bw.t @@ -0,0 +1,37 @@ +static +void TEMPLATE_FUNC_NAME(wvlt_fftwf_complex* __restrict in, unsigned fftsz, float* __restrict wnd, + wvlt_fftwf_complex* __restrict out) +{ + const __m512i idx0 = _mm512_set_epi32( 7,7, 6,6, 5,5, 4,4, 3,3, 2,2, 1,1, 0,0); + const __m512i idx1 = _mm512_set_epi32(15,15, 14,14, 13,13, 12,12, 11,11, 10,10, 9,9, 8,8); + + for(unsigned i = 0; i < fftsz; i += 32) + { + __m512 e0 = _mm512_load_ps(&in[i + 0][0]); + __m512 e1 = _mm512_load_ps(&in[i + 8][0]); + __m512 e2 = _mm512_load_ps(&in[i + 16][0]); + __m512 e3 = _mm512_load_ps(&in[i + 24][0]); + + __m512 w0 = _mm512_castsi512_ps(_mm512_stream_load_si512(&wnd[i + 0])); + __m512 w1 = _mm512_castsi512_ps(_mm512_stream_load_si512(&wnd[i + 16])); + //__m512 w0 = _mm512_load_ps(&wnd[i + 0]); + //__m512 w1 = _mm512_load_ps(&wnd[i + 16]); + + __m512 dw0 = _mm512_permutexvar_ps(idx0, w0); + __m512 dw1 = _mm512_permutexvar_ps(idx1, w0); + __m512 dw2 = _mm512_permutexvar_ps(idx0, w1); + __m512 dw3 = _mm512_permutexvar_ps(idx1, w1); + + __m512 r0 = _mm512_mul_ps(e0, dw0); + __m512 r1 = _mm512_mul_ps(e1, dw1); + __m512 r2 = _mm512_mul_ps(e2, dw2); + __m512 r3 = _mm512_mul_ps(e3, dw3); + + _mm512_store_ps(&out[i + 0][0], r0); + _mm512_store_ps(&out[i + 8][0], r1); + _mm512_store_ps(&out[i + 16][0], r2); + _mm512_store_ps(&out[i + 24][0], r3); + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/fft_window_ci16_cf32_avx2.t b/src/lib/xdsp/templates/fft_window_ci16_cf32_avx2.t new file mode 100644 index 00000000..8af106d1 --- /dev/null +++ b/src/lib/xdsp/templates/fft_window_ci16_cf32_avx2.t @@ -0,0 +1,45 @@ +static +void TEMPLATE_FUNC_NAME(wvlt_fftwi16_complex* __restrict in, unsigned fftsz, float* __restrict wnd, + wvlt_fftwf_complex* __restrict out) +{ + const __m256i sh0 = _mm256_setr_epi32(0, 0, 1, 1, 2, 2, 3, 3); + const __m256i sh1 = _mm256_setr_epi32(4, 4, 5, 5, 6, 6, 7, 7); + + for(unsigned i = 0; i < fftsz; i += 16) + { + __m256i i0 = _mm256_load_si256((__m256i*)&in[i + 0][0]); + __m256i i1 = _mm256_load_si256((__m256i*)&in[i + 8][0]); + + __m256 w0 = _mm256_castsi256_ps(_mm256_stream_load_si256((__m256i*)&wnd[i + 0])); + __m256 w1 = _mm256_castsi256_ps(_mm256_stream_load_si256((__m256i*)&wnd[i + 8])); + //__m256 w0 = _mm256_load_ps(&wnd[i + 0]); + //__m256 w1 = _mm256_load_ps(&wnd[i + 8]); + + __m256 dw0 = _mm256_permutevar8x32_ps(w0, sh0); + __m256 dw1 = _mm256_permutevar8x32_ps(w0, sh1); + __m256 dw2 = _mm256_permutevar8x32_ps(w1, sh0); + __m256 dw3 = _mm256_permutevar8x32_ps(w1, sh1); + + __m256i d0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(i0)); + __m256i d1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(i0, 1)); + __m256i d2 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(i1)); + __m256i d3 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(i1, 1)); + + __m256 e0 = _mm256_cvtepi32_ps(d0); + __m256 e1 = _mm256_cvtepi32_ps(d1); + __m256 e2 = _mm256_cvtepi32_ps(d2); + __m256 e3 = _mm256_cvtepi32_ps(d3); + + __m256 r0 = _mm256_mul_ps(e0, dw0); + __m256 r1 = _mm256_mul_ps(e1, dw1); + __m256 r2 = _mm256_mul_ps(e2, dw2); + __m256 r3 = _mm256_mul_ps(e3, dw3); + + _mm256_store_ps(&out[i + 0][0], r0); + _mm256_store_ps(&out[i + 4][0], r1); + _mm256_store_ps(&out[i + 8][0], r2); + _mm256_store_ps(&out[i + 12][0], r3); + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/fft_window_ci16_cf32_avx512bw.t b/src/lib/xdsp/templates/fft_window_ci16_cf32_avx512bw.t new file mode 100644 index 00000000..82d2023d --- /dev/null +++ b/src/lib/xdsp/templates/fft_window_ci16_cf32_avx512bw.t @@ -0,0 +1,93 @@ +#ifndef UNWRAP_CNT +#define UNWRAP_CNT 2 +#endif + +#if UNWRAP_CNT > 2 +#error Maximum spported UNWRAP_CNT is 2! +#endif + +static +void TEMPLATE_FUNC_NAME(wvlt_fftwi16_complex* __restrict in, unsigned fftsz, float* __restrict wnd, + wvlt_fftwf_complex* __restrict out) +{ + const __m512i idx0 = _mm512_set_epi32( 7,7, 6,6, 5,5, 4,4, 3,3, 2,2, 1,1, 0,0); + const __m512i idx1 = _mm512_set_epi32(15,15, 14,14, 13,13, 12,12, 11,11, 10,10, 9,9, 8,8); + + for(unsigned i = 0; i < fftsz; i += UNWRAP_CNT * 32) + { + __m512i i0 = _mm512_load_si512(&in[i + 0][0]); + __m512i i1 = _mm512_load_si512(&in[i + 16][0]); +#if UNWRAP_CNT > 1 + __m512i i2 = _mm512_load_si512(&in[i + 32][0]); + __m512i i3 = _mm512_load_si512(&in[i + 48][0]); +#endif + + __m512 w0 = _mm512_castsi512_ps(_mm512_stream_load_si512(&wnd[i + 0])); + __m512 w1 = _mm512_castsi512_ps(_mm512_stream_load_si512(&wnd[i + 16])); + //__m512 w0 = _mm512_load_ps(&wnd[i + 0]); + //__m512 w1 = _mm512_load_ps(&wnd[i + 16]); +#if UNWRAP_CNT > 1 + __m512 w2 = _mm512_castsi512_ps(_mm512_stream_load_si512(&wnd[i + 32])); + __m512 w3 = _mm512_castsi512_ps(_mm512_stream_load_si512(&wnd[i + 48])); + //__m512 w2 = _mm512_load_ps(&wnd[i + 32]); + //__m512 w3 = _mm512_load_ps(&wnd[i + 48]); +#endif + + __m512 dw0 = _mm512_permutexvar_ps(idx0, w0); + __m512 dw1 = _mm512_permutexvar_ps(idx1, w0); + __m512 dw2 = _mm512_permutexvar_ps(idx0, w1); + __m512 dw3 = _mm512_permutexvar_ps(idx1, w1); +#if UNWRAP_CNT > 1 + __m512 dw4 = _mm512_permutexvar_ps(idx0, w2); + __m512 dw5 = _mm512_permutexvar_ps(idx1, w2); + __m512 dw6 = _mm512_permutexvar_ps(idx0, w3); + __m512 dw7 = _mm512_permutexvar_ps(idx1, w3); +#endif + + __m512i d0 = _mm512_cvtepi16_epi32(_mm512_castsi512_si256(i0)); + __m512i d1 = _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(i0, 1)); + __m512i d2 = _mm512_cvtepi16_epi32(_mm512_castsi512_si256(i1)); + __m512i d3 = _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(i1, 1)); +#if UNWRAP_CNT > 1 + __m512i d4 = _mm512_cvtepi16_epi32(_mm512_castsi512_si256(i2)); + __m512i d5 = _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(i2, 1)); + __m512i d6 = _mm512_cvtepi16_epi32(_mm512_castsi512_si256(i3)); + __m512i d7 = _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(i3, 1)); +#endif + + __m512 e0 = _mm512_cvtepi32_ps(d0); + __m512 e1 = _mm512_cvtepi32_ps(d1); + __m512 e2 = _mm512_cvtepi32_ps(d2); + __m512 e3 = _mm512_cvtepi32_ps(d3); +#if UNWRAP_CNT > 1 + __m512 e4 = _mm512_cvtepi32_ps(d4); + __m512 e5 = _mm512_cvtepi32_ps(d5); + __m512 e6 = _mm512_cvtepi32_ps(d6); + __m512 e7 = _mm512_cvtepi32_ps(d7); +#endif + + __m512 r0 = _mm512_mul_ps(e0, dw0); + __m512 r1 = _mm512_mul_ps(e1, dw1); + __m512 r2 = _mm512_mul_ps(e2, dw2); + __m512 r3 = _mm512_mul_ps(e3, dw3); +#if UNWRAP_CNT > 1 + __m512 r4 = _mm512_mul_ps(e4, dw4); + __m512 r5 = _mm512_mul_ps(e5, dw5); + __m512 r6 = _mm512_mul_ps(e6, dw6); + __m512 r7 = _mm512_mul_ps(e7, dw7); +#endif + + _mm512_store_ps(&out[i + 0][0], r0); + _mm512_store_ps(&out[i + 8][0], r1); + _mm512_store_ps(&out[i + 16][0], r2); + _mm512_store_ps(&out[i + 24][0], r3); +#if UNWRAP_CNT > 1 + _mm512_store_ps(&out[i + 32][0], r4); + _mm512_store_ps(&out[i + 40][0], r5); + _mm512_store_ps(&out[i + 48][0], r6); + _mm512_store_ps(&out[i + 56][0], r7); +#endif + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/fft_window_ci16_cf32_generic.t b/src/lib/xdsp/templates/fft_window_ci16_cf32_generic.t new file mode 100644 index 00000000..c7de2896 --- /dev/null +++ b/src/lib/xdsp/templates/fft_window_ci16_cf32_generic.t @@ -0,0 +1,12 @@ +static +void TEMPLATE_FUNC_NAME(wvlt_fftwi16_complex* __restrict in, unsigned fftsz, float* __restrict wnd, + wvlt_fftwf_complex* __restrict out) +{ + for(unsigned i = 0; i < fftsz; ++i) + { + out[i][0] = wnd[i] * in[i][0]; //wnd[i] must be normalized! + out[i][1] = wnd[i] * in[i][1]; + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/fftad_add_avx512bw.t b/src/lib/xdsp/templates/fftad_add_avx512bw.t new file mode 100644 index 00000000..f4441ab6 --- /dev/null +++ b/src/lib/xdsp/templates/fftad_add_avx512bw.t @@ -0,0 +1,67 @@ +static +void TEMPLATE_FUNC_NAME(fft_acc_t* __restrict p, wvlt_fftwf_complex* __restrict d, unsigned fftsz) +{ + const __m512 fnoexp = _mm512_castsi512_ps(_mm512_set1_epi32(~(0xffu << 23))); + const __m512 fexp0 = _mm512_castsi512_ps(_mm512_set1_epi32(127u << 23)); + const __m512i expcorr = _mm512_set1_epi32(127); + const __m512 mine = _mm512_set1_ps(p->mine); + + const __m512i pidx0 = _mm512_set_epi32(30,28,26,24,22,20,18,16, 14,12,10,8,6,4,2,0); + const __m512i pidx1 = _mm512_set_epi32(31,29,27,25,23,21,19,17, 15,13,11,9,7,5,3,1); + + for (unsigned i = 0; i < fftsz; i += 32) + { + __m512 e0 = _mm512_load_ps(&d[i + 0][0]); + __m512 e1 = _mm512_load_ps(&d[i + 8][0]); + __m512 e2 = _mm512_load_ps(&d[i + 16][0]); + __m512 e3 = _mm512_load_ps(&d[i + 24][0]); + + __m512 acc_m0 = _mm512_load_ps(&p->f_mant[i + 0]); + __m512 acc_m1 = _mm512_load_ps(&p->f_mant[i + 16]); + + __m512i acc_p0 = _mm512_load_si512((__m512i*)&p->f_pwr[i + 0]); + __m512i acc_p1 = _mm512_load_si512((__m512i*)&p->f_pwr[i + 16]); + + __m512 p0 = _mm512_mul_ps(e0, e0); // i0 q0 ... i3 q3 + __m512 p1 = _mm512_mul_ps(e1, e1); // i4 q4 ... i7 q7 + __m512 p2 = _mm512_mul_ps(e2, e2); // i8 q8 ... iB qB + __m512 p3 = _mm512_mul_ps(e3, e3); // iC qC ... iF qF + + __m512 pm0 = _mm512_permutex2var_ps(p0, pidx0, p1); + __m512 pm1 = _mm512_permutex2var_ps(p0, pidx1, p1); + __m512 pm2 = _mm512_permutex2var_ps(p2, pidx0, p3); + __m512 pm3 = _mm512_permutex2var_ps(p2, pidx1, p3); + + __m512 en0 = _mm512_add_ps(pm0, pm1); // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + __m512 en1 = _mm512_add_ps(pm2, pm3); // 16.....31 + + __m512 enz0 = _mm512_add_ps(en0, mine); + __m512 enz1 = _mm512_add_ps(en1, mine); + + __m512 zmpy0 = _mm512_mul_ps(enz0, acc_m0); + __m512 zmpy1 = _mm512_mul_ps(enz1, acc_m1); + + __m512 zClearExp0 = _mm512_and_ps(fnoexp, zmpy0); + __m512 zClearExp1 = _mm512_and_ps(fnoexp, zmpy1); + + __m512 z0 = _mm512_or_ps(zClearExp0, fexp0); + __m512 z1 = _mm512_or_ps(zClearExp1, fexp0); + + __m512i az0 = _mm512_srli_epi32(_mm512_castps_si512(zmpy0), 23); + __m512i az1 = _mm512_srli_epi32(_mm512_castps_si512(zmpy1), 23); + + __m512i azsum0 = _mm512_add_epi32(az0, acc_p0); + __m512i azsum1 = _mm512_add_epi32(az1, acc_p1); + + __m512i azc0 = _mm512_sub_epi32(azsum0, expcorr); + __m512i azc1 = _mm512_sub_epi32(azsum1, expcorr); + + _mm512_store_ps(&p->f_mant[i + 0], z0); + _mm512_store_ps(&p->f_mant[i + 16], z1); + + _mm512_store_si512((__m512i*)&p->f_pwr[i + 0], azc0); + _mm512_store_si512((__m512i*)&p->f_pwr[i + 16], azc1); + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/fftad_init_avx512bw.t b/src/lib/xdsp/templates/fftad_init_avx512bw.t new file mode 100644 index 00000000..b12ddfda --- /dev/null +++ b/src/lib/xdsp/templates/fftad_init_avx512bw.t @@ -0,0 +1,13 @@ +static +void TEMPLATE_FUNC_NAME(fft_acc_t* __restrict p, unsigned fftsz) +{ + __m512 e1 = _mm512_set1_ps(1.0); + __m512i d1 = _mm512_set1_epi32(0); + + for (unsigned i = 0; i < fftsz; i += 16) { + _mm512_store_ps(p->f_mant + i, e1); + _mm512_store_si512((__m512i*)(p->f_pwr + i), d1); + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/fftad_norm_avx512bw.t b/src/lib/xdsp/templates/fftad_norm_avx512bw.t new file mode 100644 index 00000000..46e983d7 --- /dev/null +++ b/src/lib/xdsp/templates/fftad_norm_avx512bw.t @@ -0,0 +1,61 @@ +static +void TEMPLATE_FUNC_NAME(fft_acc_t* __restrict p, unsigned fftsz, float scale, float corr, float* __restrict outa) +{ +#ifdef USE_POLYLOG2 + WVLT_AVX512_POLYLOG2_DECL_CONSTS; +#else + const __m512 log2_mul = _mm512_set1_ps(WVLT_FASTLOG2_MUL); + const __m512 log2_sub = _mm512_set1_ps(WVLT_FASTLOG2_SUB); +#endif + const __m512 vcorr = _mm512_set1_ps(corr); + const __m512 vscale = _mm512_set1_ps(scale); + + const unsigned half = fftsz >> 1; + + for(unsigned i = 0; i < fftsz; i += 32) + { + __m512 m0 = _mm512_load_ps(p->f_mant + i + 0); + __m512 m1 = _mm512_load_ps(p->f_mant + i + 16); + + __m512i p0 = _mm512_load_si512((__m512i*)(p->f_pwr + i + 0)); + __m512i p1 = _mm512_load_si512((__m512i*)(p->f_pwr + i + 16)); + +#ifdef USE_POLYLOG2 + __m512 apwr0, apwr1; + WVLT_POLYLOG2F16(m0, apwr0); + WVLT_POLYLOG2F16(m1, apwr1); +#else + //wvlt_fastlog2 + __m512 l20 = _mm512_cvtepi32_ps(_mm512_castps_si512(m0)); + __m512 l21 = _mm512_cvtepi32_ps(_mm512_castps_si512(m1)); + __m512 apwr0 = _mm512_fmsub_ps(l20, log2_mul, log2_sub); + __m512 apwr1 = _mm512_fmsub_ps(l21, log2_mul, log2_sub); + // +#endif + __m512 s0 = _mm512_add_ps(apwr0, _mm512_cvtepi32_ps(p0)); + __m512 s1 = _mm512_add_ps(apwr1, _mm512_cvtepi32_ps(p1)); + + __m512 f0 = _mm512_fmadd_ps(vscale, s0, vcorr); + __m512 f1 = _mm512_fmadd_ps(vscale, s1, vcorr); + + int32_t offset; + + if(i + 32 <= half) + { + offset = half; + } + else if(i >= half) + { + offset = - half; + } + else + { + offset = 0; + } + + _mm512_store_ps(outa + i + offset + 0, f0); + _mm512_store_ps(outa + i + offset + 16, f1); + } +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_generic.inc b/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_generic.inc new file mode 100644 index 00000000..c6db5895 --- /dev/null +++ b/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_generic.inc @@ -0,0 +1,22 @@ + while(i > 0) + { + phase += dphase; + const float ph = WVLT_SINCOS_I32_PHSCALE * phase; + float ssin, scos; + + sincosf(ph, &ssin, &scos); + *outdata++ = ssin * gain * sign_sin; + *outdata++ = scos * gain * sign_cos; + + dphase += chirp_step; + if(dphase > delta_phase1) + { + dphase = delta_phase0; + } + else if(dphase < delta_phase0) + { + dphase = delta_phase1; + } + + --i; + } diff --git a/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_generic.t b/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_generic.t new file mode 100644 index 00000000..4a612736 --- /dev/null +++ b/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_generic.t @@ -0,0 +1,29 @@ +static +void TEMPLATE_FUNC_NAME(int32_t *__restrict start_phase, int32_t *__restrict start_delta_phase, + int32_t *__restrict delta_phase, + int32_t chirp_steps_cnt, + int16_t gain, + bool inv_sin, + bool inv_cos, + int16_t *__restrict outdata, + unsigned iters) +{ + unsigned i = iters; + + int32_t phase = *start_phase; + int32_t delta_phase0 = delta_phase[0]; + int32_t delta_phase1 = delta_phase[1]; + + const int16_t sign_sin = inv_sin ? -1 : 1; + const int16_t sign_cos = inv_cos ? -1 : 1; + + const int32_t chirp_step = ((int64_t)delta_phase1 - (int64_t)delta_phase0) / chirp_steps_cnt; + int32_t dphase = *start_delta_phase; + + #include "wvlt_sincos_i16_interleaved_chirp_generic.inc" + + *start_phase = phase; + *start_delta_phase = dphase; +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_ssse3.t b/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_ssse3.t new file mode 100644 index 00000000..d32b6459 --- /dev/null +++ b/src/lib/xdsp/templates/wvlt_sincos_i16_interleaved_chirp_ssse3.t @@ -0,0 +1,114 @@ +#include + +static +void TEMPLATE_FUNC_NAME(int32_t *__restrict start_phase, int32_t *__restrict start_delta_phase, + int32_t *__restrict delta_phase, + int32_t chirp_steps_cnt, + int16_t gain, + bool inv_sin, + bool inv_cos, + int16_t *__restrict outdata, + unsigned iters) +{ + unsigned i = iters; + + int32_t phase = *start_phase; + int32_t delta_phase0 = delta_phase[0]; + int32_t delta_phase1 = delta_phase[1]; + + const int16_t sign_sin = inv_sin ? -1 : 1; + const int16_t sign_cos = inv_cos ? -1 : 1; + + const int32_t chirp_step = ((int64_t)delta_phase1 - (int64_t)delta_phase0) / chirp_steps_cnt; + int32_t dphase = *start_delta_phase; + + const __m128i vsign_sin = _mm_set1_epi16(sign_sin); + const __m128i vsign_cos = _mm_set1_epi16(sign_cos); + + const __m128i vchirps0 = _mm_set_epi32(3 * chirp_step, 2 * chirp_step, 1 * chirp_step, 0 * chirp_step); + const __m128i vchirps1 = _mm_set_epi32(7 * chirp_step, 6 * chirp_step, 5 * chirp_step, 4 * chirp_step); + + const __m128i ph_lo_mask = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 13, 12, 9, 8, 5, 4, 1, 0); + const __m128i ph_hi_mask = _mm_set_epi8(29, 28, 25, 24, 21, 20, 17, 16, -1, -1, -1, -1, -1, -1, -1, -1); + + const __m128i mpi_2v = _mm_set1_epi32(-32768); + const __m128i pi_2v = _mm_set1_epi32( 32767); + const __m128i onev = _mm_set1_epi16(1); + const __m128i gainv = _mm_set1_epi16(gain); + + #include "wvlt_sincos_i16_ssse3.inc" + + while(i >= 8) + { + // add delta + + const __m128i vdelta_phase = _mm_set1_epi32(dphase); + + __m128i delta_ph0 = _mm_add_epi32(vchirps0, vdelta_phase); + delta_ph0 = _mm_add_epi32(delta_ph0, _mm_slli_si128(delta_ph0, 4)); + delta_ph0 = _mm_add_epi32(delta_ph0, _mm_slli_si128(delta_ph0, 8)); + + __m128i vphase0 = _mm_set1_epi32(phase); + vphase0 = _mm_add_epi32(vphase0, delta_ph0); + phase = _mm_extract_epi32(vphase0, 3); + + __m128i delta_ph1 = _mm_add_epi32(vchirps1, vdelta_phase); + delta_ph1 = _mm_add_epi32(delta_ph1, _mm_slli_si128(delta_ph1, 4)); + delta_ph1 = _mm_add_epi32(delta_ph1, _mm_slli_si128(delta_ph1, 8)); + + __m128i vphase1 = _mm_set1_epi32(phase); + vphase1 = _mm_add_epi32(vphase1, delta_ph1); + phase = _mm_extract_epi32(vphase1, 3); + + dphase += 8 * chirp_step; + if(dphase > delta_phase1) + dphase = delta_phase0; + else if(dphase < delta_phase0) + dphase = delta_phase1; + + + // _signed_ right shift to get hi word + // 15 bits because input I32 range is [-PI; +PI), but WVLT_SINCOS I16 input range is [-PI/2; +PI/2) + __m128i ph0 = _mm_srai_epi32(vphase0, 15); + __m128i ph1 = _mm_srai_epi32(vphase1, 15); + + // INT32 input ranges < INT16_MIN and > INT16_MAX should be inverted + __m128i rflag0 = _mm_or_si128(_mm_cmpgt_epi32(ph0, pi_2v), _mm_cmplt_epi32(ph0, mpi_2v)); + __m128i rflag1 = _mm_or_si128(_mm_cmpgt_epi32(ph1, pi_2v), _mm_cmplt_epi32(ph1, mpi_2v)); + __m128i sign = _mm_or_si128(_mm_shuffle_epi8(rflag0, ph_lo_mask), _mm_shuffle_epi8(rflag1, ph_hi_mask)); + + // normalize sign from (-1;0) to (-1;+1) : x*2 + 1 + sign = _mm_add_epi16(_mm_slli_epi16(sign, 1), onev); + + // pack phase low int16 words (already shifted >> 15) + __m128i reg_phase = _mm_or_si128(_mm_shuffle_epi8(ph0, ph_lo_mask), _mm_shuffle_epi8(ph1, ph_hi_mask)); + __m128i reg_sin, reg_cos; + WVLT_SINCOS(reg_phase, reg_sin, reg_cos); + + // apply sign - internal & external + reg_sin = _mm_sign_epi16(reg_sin, sign); + reg_cos = _mm_sign_epi16(reg_cos, sign); + reg_sin = _mm_sign_epi16(reg_sin, vsign_sin); + reg_cos = _mm_sign_epi16(reg_cos, vsign_cos); + + //apply amplitude normalization + reg_sin = _mm_mulhrs_epi16(reg_sin, gainv); + reg_cos = _mm_mulhrs_epi16(reg_cos, gainv); + + // interleave & store + _mm_storeu_si128((__m128i*)(outdata + 0), _mm_unpacklo_epi16(reg_sin, reg_cos)); + _mm_storeu_si128((__m128i*)(outdata + 8), _mm_unpackhi_epi16(reg_sin, reg_cos)); + + outdata += 16; + i -= 8; + } + + #undef WVLT_SINCOS + + #include "wvlt_sincos_i16_interleaved_chirp_generic.inc" + + *start_phase = phase; + *start_delta_phase = dphase; +} + +#undef TEMPLATE_FUNC_NAME diff --git a/src/lib/xdsp/utests/CMakeLists.txt b/src/lib/xdsp/utests/CMakeLists.txt index 469a0119..f7b193d6 100644 --- a/src/lib/xdsp/utests/CMakeLists.txt +++ b/src/lib/xdsp/utests/CMakeLists.txt @@ -16,10 +16,13 @@ set(TEST_SUIT_SRCS xfft_fftad_utest.c xfft_rtsa_utest.c fft_window_cf32_utest.c + fft_window_ci16_cf32_utest.c wvlt_sincos_i16_utest.c conv_4ci16_ci16_utest.c conv_ci16_4ci16_utest.c conv_ci16_4cf32_utest.c + conv_ci16_8cf32_utest.c + conv_ci16_8ci16_utest.c conv_4cf32_ci16_utest.c conv_ci12_4cf32_utest.c conv_4cf32_ci12_utest.c @@ -29,6 +32,12 @@ set(TEST_SUIT_SRCS conv_ci12_4ci16_utest.c conv_2ci16_ci12_utest.c conv_4ci16_ci12_utest.c + conv_ci16_6ci16_utest.c + conv_ci16_6cf32_utest.c + conv_ci16_3cf32_utest.c + conv_ci16_3ci16_utest.c + conv_3ci16_ci16_utest.c + conv_3cf32_ci16_utest.c ../fft_window_functions.c ../fftad_functions.c @@ -47,6 +56,8 @@ set(TEST_SUIT_SRCS ../conv_4ci16_ci16_2.c ../conv_ci16_4ci16_2.c ../conv_ci16_4cf32_2.c + ../conv_ci16_8cf32_2.c + ../conv_ci16_8ci16_2.c ../conv_4cf32_ci16_2.c ../conv_ci12_4cf32_2.c ../conv_4cf32_ci12_2.c @@ -56,6 +67,12 @@ set(TEST_SUIT_SRCS ../conv_ci12_4ci16_2.c ../conv_2ci16_ci12_2.c ../conv_4ci16_ci12_2.c + ../conv_ci16_6ci16_2.c + ../conv_ci16_6cf32_2.c + ../conv_ci16_3cf32_2.c + ../conv_ci16_3ci16_2.c + ../conv_3ci16_ci16_2.c + ../conv_3cf32_ci16_2.c ../vbase.c ) diff --git a/src/lib/xdsp/utests/README b/src/lib/xdsp/utests/README new file mode 100644 index 00000000..75b67bc7 --- /dev/null +++ b/src/lib/xdsp/utests/README @@ -0,0 +1,23 @@ +==XDSP conversion functions unit tests suite.== + +You can restrict the test subset specifying special tags. +Regression tests are tagged as REGRESS. +Performance tests are tagged as PERFORMANCE. +You can select tags by setting environment variables CK_INCLUDE_TAGS and CK_EXCLUDE_TAGS. + +For example: + +Run all tests: +./xdsp_utest_suite +or +CK_INCLUDE_TAGS="REGRESS PERFORMANCE" ./xdsp_utest_suite + +Run only regression tests: +CK_INCLUDE_TAGS=REGRESS ./xdsp_utest_suite +or +CK_EXCLUDE_TAGS=PERFORMANCE ./xdsp_utest_suite + +Run only performance tests: +CK_INCLUDE_TAGS=PERFORMANCE ./xdsp_utest_suite +or +CK_EXCLUDE_TAGS=REGRESS ./xdsp_utest_suite diff --git a/src/lib/xdsp/utests/conv_2cf32_ci12_utest.c b/src/lib/xdsp/utests/conv_2cf32_ci12_utest.c index 5549cbc7..75c1808d 100644 --- a/src/lib/xdsp/utests/conv_2cf32_ci12_utest.c +++ b/src/lib/xdsp/utests/conv_2cf32_ci12_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_2cf32_ci12_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(float) * 3 / 8) @@ -34,10 +34,12 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); - posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -67,18 +69,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_2cf32_ci12_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_2cf32_ci12_c, &last_fn_name); } static int is_equal() @@ -168,14 +159,17 @@ START_TEST(conv_2cf32_ci12_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = PACKET_SIZE * sizeof(float); + const size_t bzin = PACKET_SIZE * sizeof(float) - 64 + 32 + 10; const size_t bzout = OUT_BZ; fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out_etalon, out, bzout); while(opt != OPT_GENERIC) @@ -233,18 +227,12 @@ END_TEST Suite * conv_2cf32_ci12_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_2cf32_ci12"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_2cf32_ci12_check_simd); - tcase_add_loop_test(tc_core, conv_2cf32_ci12_speed, 0, 3); + Suite* s = suite_create("conv_2cf32_ci12"); + + ADD_REGRESS_TEST(s, conv_2cf32_ci12_check_simd); + ADD_PERF_LOOP_TEST(s, conv_2cf32_ci12_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_2cf32_ci16_utest.c b/src/lib/xdsp/utests/conv_2cf32_ci16_utest.c index 65b90ded..42332c8b 100644 --- a/src/lib/xdsp/utests/conv_2cf32_ci16_utest.c +++ b/src/lib/xdsp/utests/conv_2cf32_ci16_utest.c @@ -8,7 +8,7 @@ #include #include #include "xdsp_utest_common.h" -#include "../conv_2cf32_ci16_2.h" +#include "conv_2cf32_ci16_2.h" #undef DEBUG_PRINT @@ -34,10 +34,12 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); - posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -65,18 +67,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_2cf32_ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_2cf32_ci16_c, &last_fn_name); } static int is_equal() @@ -182,18 +173,12 @@ END_TEST Suite * conv_2cf32_ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_2cf32_ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_2cf32_ci16_check_simd); - tcase_add_loop_test(tc_core, conv_2cf32_ci16_speed, 0, 3); + Suite* s = suite_create("conv_2cf32_ci16"); + + ADD_REGRESS_TEST(s, conv_2cf32_ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_2cf32_ci16_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_2ci16_ci12_utest.c b/src/lib/xdsp/utests/conv_2ci16_ci12_utest.c index ac2bc13f..481c6f3b 100644 --- a/src/lib/xdsp/utests/conv_2ci16_ci12_utest.c +++ b/src/lib/xdsp/utests/conv_2ci16_ci12_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_2ci16_ci12_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(int16_t) * 3 / 4) @@ -31,10 +31,12 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 2); - posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 2); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 2); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 2); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -64,18 +66,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_2ci16_ci12_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_2ci16_ci12_c, &last_fn_name); } static void printer(const char* header) @@ -115,14 +106,17 @@ START_TEST(conv_2ci16_ci12_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = PACKET_SIZE * sizeof(int16_t); + const size_t bzin = PACKET_SIZE * sizeof(int16_t) - 64 + 32 + 10; //we should test all branches const size_t bzout = OUT_BZ; fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out_etalon, out, bzout); while(opt != OPT_GENERIC) @@ -179,18 +173,12 @@ END_TEST Suite * conv_2ci16_ci12_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_2ci16_ci12"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_2ci16_ci12_check_simd); - tcase_add_loop_test(tc_core, conv_2ci16_ci12_speed, 0, 3); + Suite* s = suite_create("conv_2ci16_ci12"); + + ADD_REGRESS_TEST(s, conv_2ci16_ci12_check_simd); + ADD_PERF_LOOP_TEST(s, conv_2ci16_ci12_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_2ci16_ci16_utest.c b/src/lib/xdsp/utests/conv_2ci16_ci16_utest.c index aca7ce24..9eb478d6 100644 --- a/src/lib/xdsp/utests/conv_2ci16_ci16_utest.c +++ b/src/lib/xdsp/utests/conv_2ci16_ci16_utest.c @@ -8,7 +8,7 @@ #include #include #include "xdsp_utest_common.h" -#include "../conv_2ci16_ci16_2.h" +#include "conv_2ci16_ci16_2.h" #undef DEBUG_PRINT @@ -31,10 +31,12 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, OUT_BZ / 2); - posix_memalign((void**)&in_1, ALIGN_BYTES, OUT_BZ / 2); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, OUT_BZ / 2); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, OUT_BZ / 2); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -63,18 +65,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_2ci16_ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_2ci16_ci16_c, &last_fn_name); } @@ -160,18 +151,12 @@ END_TEST Suite * conv_2ci16_ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_2ci16_ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_2ci16_ci16_check_simd); - tcase_add_loop_test(tc_core, conv_2ci16_ci16_speed, 0, 3); + Suite* s = suite_create("conv_2ci16_ci16"); + + ADD_REGRESS_TEST(s, conv_2ci16_ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_2ci16_ci16_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_3cf32_ci16_utest.c b/src/lib/xdsp/utests/conv_3cf32_ci16_utest.c new file mode 100644 index 00000000..a206007f --- /dev/null +++ b/src/lib/xdsp/utests/conv_3cf32_ci16_utest.c @@ -0,0 +1,210 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_3cf32_ci16_2.h" + +#undef DEBUG_PRINT + +#define WORD_COUNT (32768u) +#define OUT_BZ (WORD_COUNT * sizeof(int16_t)) + +#define CONV_SCALE (1.0f/32767) +#define EPS (0)//(5E-4) + +static const unsigned packet_lens[3] = { 1111u, 4123u, WORD_COUNT }; + +#define SPEED_MEASURE_ITERS 1000000 + +static float* in_0 = NULL; +static float* in_1 = NULL; +static float* in_2 = NULL; +static float* in[3] = {NULL, NULL, NULL}; + +static int16_t* out = NULL; +static int16_t* out_etalon = NULL; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 3); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 3); + res = res ? res : posix_memalign((void**)&in_2, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 3); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); + + in[0] = in_0; + in[1] = in_1; + in[2] = in_2; + + //fill + float *p0 = in_0; + float *p1 = in_1; + float *p2 = in_2; + + srand( time(0) ); + + for(int i = 0; i < WORD_COUNT;) + { + *p0++ = ((float)(i++) * CONV_SCALE) * ((float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1); + *p0++ = ((float)(i++) * CONV_SCALE) * ((float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1); + *p1++ = ((float)(i++) * CONV_SCALE) * ((float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1); + *p1++ = ((float)(i++) * CONV_SCALE) * ((float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1); + *p2++ = ((float)(i++) * CONV_SCALE) * ((float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1); + *p2++ = ((float)(i++) * CONV_SCALE) * ((float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1); + } +} + +static void teardown() +{ + free(in_0); + free(in_1); + free(in_2); + free(out); + free(out_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_3cf32_ci16_c, &last_fn_name); +} + +static int is_equal() +{ + for(unsigned i = 0; i < WORD_COUNT; ++i) + { + float a = out[i]; + float b = out_etalon[i]; + + a *= CONV_SCALE; + b *= CONV_SCALE; + + float delta = fabs(a-b); + if(delta > EPS) + { + fprintf(stderr, "i = %d : out = %d, etalon = %d, delta = %.6f\n", i, out[i], out_etalon[i], delta); + return 1; + } + } + return 0; +} + +static void print_data(const char* header) +{ + if(header) + fprintf(stderr, "%s:", header); + + for(unsigned n = 0; n < 3; ++n) + { + fprintf(stderr, "\n in%d: ", n); + for(unsigned i = 0; i < 8; ++i) + { + //fprintf(stderr, "%+.4f ", in[n][i]); + fprintf(stderr, "%+2d ", (int16_t)(in[n][i] * 32767.f)); + } + } + + fprintf(stderr, "\n out: "); + for(unsigned i = 0; i < 24; ++i) + { + fprintf(stderr, "%+2d ", out[i]); + //fprintf(stderr, "%+.4f ", (double)(out[i] / 32767.f)); + } + fprintf(stderr, "\n"); +} + +START_TEST(conv_3cf32_ci16_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void** pin = (const void**)in; + void* pout = (void*)out; + last_fn_name = NULL; + + const size_t bzin = WORD_COUNT * sizeof(float); + const size_t bzout = OUT_BZ; + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(pout, 0, bzout); + (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); + memcpy(out_etalon, out, bzout); + +#ifdef DEBUG_PRINT + print_data("ETALON"); +#endif + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out, 0, bzout); + (*fn)(pin, bzin, &pout, bzout); + int res = is_equal(); + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); +#ifdef DEBUG_PRINT + print_data(NULL); +#endif + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_3cf32_ci16_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void** pin = (const void**)in; + void* pout = (void*)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i] * sizeof(float); + const size_t bzout = packet_lens[_i] * sizeof(int16_t); + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(pin, bzin, &pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(pin, bzin, &pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_3cf32_ci16_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_3cf32_ci16"); + + ADD_REGRESS_TEST(s, conv_3cf32_ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_3cf32_ci16_speed, 60, 0, 3); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_3ci16_ci16_utest.c b/src/lib/xdsp/utests/conv_3ci16_ci16_utest.c new file mode 100644 index 00000000..2c2e0a4f --- /dev/null +++ b/src/lib/xdsp/utests/conv_3ci16_ci16_utest.c @@ -0,0 +1,194 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_3ci16_ci16_2.h" + +#undef DEBUG_PRINT + +#define PACKET_SIZE (65536u) +#define OUT_BZ (PACKET_SIZE * sizeof(int16_t)) + +static const unsigned packet_lens[4] = { 8192u, 16384u, 32768u, PACKET_SIZE }; + +#define SPEED_MEASURE_ITERS 1000000 + +static int16_t* in_0 = NULL; +static int16_t* in_1 = NULL; +static int16_t* in_2 = NULL; +static int16_t* in[3] = {NULL, NULL, NULL}; + +static int16_t* out = NULL; +static int16_t* out_etalon = NULL; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, OUT_BZ / 3); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, OUT_BZ / 3); + res = res ? res : posix_memalign((void**)&in_2, ALIGN_BYTES, OUT_BZ / 3); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); + + in[0] = in_0; + in[1] = in_1; + in[2] = in_2; + + //fill + int16_t *p0 = in_0; + int16_t *p1 = in_1; + int16_t *p2 = in_2; + + srand( time(0) ); + + for(int i = 0; i < PACKET_SIZE; i += 6) + { + /* + *p0++ = (i + 0); + *p0++ = (i + 1); + *p1++ = (i + 2); + *p1++ = (i + 3); + *p2++ = (i + 4); + *p2++ = (i + 5); + */ + int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; + *p0++ = sign * (i + 0); + *p0++ = -sign * (i + 1); + *p1++ = -sign * (i + 2); + *p1++ = sign * (i + 3); + *p2++ = sign * (i + 4); + *p2++ = -sign * (i + 5); + } +} + +static void teardown() +{ + free(in_0); + free(in_1); + free(in_2); + free(out); + free(out_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_3ci16_ci16_c, &last_fn_name); +} + +static void print_data(const char* header) +{ + if(header) + fprintf(stderr, "%s:", header); + + for(unsigned n = 0; n < 3; ++n) + { + fprintf(stderr, "\n in%d: ", n); + for(unsigned i = 0; i < 8; ++i) + { + fprintf(stderr, "%4d ", in[n][i]); + } + } + + fprintf(stderr, "\n out: "); + for(unsigned i = 0; i < 24; ++i) + { + fprintf(stderr, "%d ", out[i]); + } + fprintf(stderr, "\n"); +} + +START_TEST(conv_3ci16_ci16_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void** pin = (const void**)in; + void* pout = (void*)out; + last_fn_name = NULL; + + const size_t bzin = OUT_BZ; + const size_t bzout = OUT_BZ; + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(out, 0, bzout); + (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); + memcpy(out_etalon, out, bzout); + +#ifdef DEBUG_PRINT + print_data("ETALON DATA"); +#endif + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out, 0, bzout); + (*fn)(pin, bzin, &pout, bzout); +#ifdef DEBUG_PRINT + print_data(NULL); +#endif + int res = memcmp(out, out_etalon, bzout); + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_3ci16_ci16_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void** pin = (const void**)in; + void* pout = (void*)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i] * sizeof(int16_t); + const size_t bzout = bzin; + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(pin, bzin, &pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(pin, bzin, &pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_3ci16_ci16_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_3ci16_ci16"); + + ADD_REGRESS_TEST(s, conv_3ci16_ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_3ci16_ci16_speed, 60, 0, 4); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_4cf32_ci12_utest.c b/src/lib/xdsp/utests/conv_4cf32_ci12_utest.c index 1d9085fc..3bdfde40 100644 --- a/src/lib/xdsp/utests/conv_4cf32_ci12_utest.c +++ b/src/lib/xdsp/utests/conv_4cf32_ci12_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_4cf32_ci12_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(float) * 3 / 8) @@ -18,7 +18,7 @@ #define CONV_SCALE (1.0f/32767) #define EPS (5E-4) -static const unsigned packet_lens[3] = { 1111u, 4123u, PACKET_SIZE }; +static const unsigned packet_lens[4] = { 1111u, 4123u, PACKET_SIZE, 262144u }; #define SPEED_MEASURE_ITERS 1000000 @@ -36,12 +36,14 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 2); - posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 4); - posix_memalign((void**)&in_2, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 4); - posix_memalign((void**)&in_3, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 4); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&in_2, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&in_3, ALIGN_BYTES, PACKET_SIZE * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -77,18 +79,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_4cf32_ci12_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_4cf32_ci12_c, &last_fn_name); } static int is_equal() @@ -178,14 +169,17 @@ START_TEST(conv_4cf32_ci12_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = PACKET_SIZE * sizeof(float); + const size_t bzin = PACKET_SIZE * sizeof(float) - 64 + 32 + 10; const size_t bzout = OUT_BZ; fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out_etalon, out, bzout); while(opt != OPT_GENERIC) @@ -197,8 +191,7 @@ START_TEST(conv_4cf32_ci12_check_simd) (*fn)(pin, bzin, &pout, bzout); #ifdef DEBUG_PRINT printer(NULL); -#endif \ - //int res = memcmp(out, out_etalon, bzout); +#endif int res = is_equal(); res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); ck_assert_int_eq( res, 0 ); @@ -243,18 +236,12 @@ END_TEST Suite * conv_4cf32_ci12_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_4cf32_ci12"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_4cf32_ci12_check_simd); - tcase_add_loop_test(tc_core, conv_4cf32_ci12_speed, 0, 3); + Suite* s = suite_create("conv_4cf32_ci12"); + + ADD_REGRESS_TEST(s, conv_4cf32_ci12_check_simd); + ADD_PERF_LOOP_TEST(s, conv_4cf32_ci12_speed, 60, 0, 4); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_4cf32_ci16_utest.c b/src/lib/xdsp/utests/conv_4cf32_ci16_utest.c index a8358eda..9de597d8 100644 --- a/src/lib/xdsp/utests/conv_4cf32_ci16_utest.c +++ b/src/lib/xdsp/utests/conv_4cf32_ci16_utest.c @@ -8,9 +8,9 @@ #include #include #include "xdsp_utest_common.h" -#include "../conv_4cf32_ci16_2.h" +#include "conv_4cf32_ci16_2.h" -#define DEBUG_PRINT +#undef DEBUG_PRINT #define WORD_COUNT (8192u) #define OUT_BZ (WORD_COUNT * sizeof(int16_t)) @@ -36,12 +36,14 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); - posix_memalign((void**)&in_1, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); - posix_memalign((void**)&in_2, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); - posix_memalign((void**)&in_3, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&in_2, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&in_3, ALIGN_BYTES, WORD_COUNT * sizeof(float) / 4); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -81,18 +83,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_4cf32_ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_4cf32_ci16_c, &last_fn_name); } static int is_equal() @@ -214,18 +205,12 @@ END_TEST Suite * conv_4cf32_ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_4cf32_ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_4cf32_ci16_check_simd); - tcase_add_loop_test(tc_core, conv_4cf32_ci16_speed, 0, 3); + Suite* s = suite_create("conv_4cf32_ci16"); + + ADD_REGRESS_TEST(s, conv_4cf32_ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_4cf32_ci16_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_4ci16_ci12_utest.c b/src/lib/xdsp/utests/conv_4ci16_ci12_utest.c index 32fe7cc2..6147e35c 100644 --- a/src/lib/xdsp/utests/conv_4ci16_ci12_utest.c +++ b/src/lib/xdsp/utests/conv_4ci16_ci12_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_4ci16_ci12_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(int16_t) * 3 / 4) @@ -33,12 +33,14 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 2); - posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 4); - posix_memalign((void**)&in_2, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 4); - posix_memalign((void**)&in_3, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 4); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 4); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 4); + res = res ? res : posix_memalign((void**)&in_2, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 4); + res = res ? res : posix_memalign((void**)&in_3, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t) / 4); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -74,18 +76,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_4ci16_ci12_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_4ci16_ci12_c, &last_fn_name); } @@ -96,7 +87,7 @@ static void printer(const char* header) for(unsigned k = 0; k < 4; ++k) { fprintf(stderr, "in[%d]: ", k); - for(unsigned i = 0; i < 8; ++i) + for(unsigned i = 0; i < PACKET_SIZE / 4; ++i) { fprintf(stderr, "%.d ", in[k][i] >> 4); } @@ -104,7 +95,7 @@ static void printer(const char* header) } fprintf(stderr, "out : "); - for(unsigned i = 0; i < 48; i += 3) + for(unsigned i = 0; i < OUT_BZ; i += 3) { uint8_t v0 = out[i + 0]; uint8_t v1 = out[i + 1]; @@ -126,14 +117,17 @@ START_TEST(conv_4ci16_ci12_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = PACKET_SIZE * sizeof(int16_t); + const size_t bzin = PACKET_SIZE * sizeof(int16_t) - 64 + 32 + 10; //we should test all branches const size_t bzout = OUT_BZ; fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out_etalon, out, bzout); while(opt != OPT_GENERIC) @@ -190,18 +184,12 @@ END_TEST Suite * conv_4ci16_ci12_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_4ci16_ci12"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_4ci16_ci12_check_simd); - tcase_add_loop_test(tc_core, conv_4ci16_ci12_speed, 0, 3); + Suite* s = suite_create("conv_4ci16_ci12"); + + ADD_REGRESS_TEST(s, conv_4ci16_ci12_check_simd); + ADD_PERF_LOOP_TEST(s, conv_4ci16_ci12_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_4ci16_ci16_utest.c b/src/lib/xdsp/utests/conv_4ci16_ci16_utest.c index f6778887..fabd79ab 100644 --- a/src/lib/xdsp/utests/conv_4ci16_ci16_utest.c +++ b/src/lib/xdsp/utests/conv_4ci16_ci16_utest.c @@ -8,7 +8,7 @@ #include #include #include "xdsp_utest_common.h" -#include "../conv_4ci16_ci16_2.h" +#include "conv_4ci16_ci16_2.h" #undef DEBUG_PRINT @@ -33,12 +33,14 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in_0, ALIGN_BYTES, OUT_BZ / 4); - posix_memalign((void**)&in_1, ALIGN_BYTES, OUT_BZ / 4); - posix_memalign((void**)&in_2, ALIGN_BYTES, OUT_BZ / 4); - posix_memalign((void**)&in_3, ALIGN_BYTES, OUT_BZ / 4); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in_0, ALIGN_BYTES, OUT_BZ / 4); + res = res ? res : posix_memalign((void**)&in_1, ALIGN_BYTES, OUT_BZ / 4); + res = res ? res : posix_memalign((void**)&in_2, ALIGN_BYTES, OUT_BZ / 4); + res = res ? res : posix_memalign((void**)&in_3, ALIGN_BYTES, OUT_BZ / 4); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); in[0] = in_0; in[1] = in_1; @@ -79,18 +81,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_4ci16_ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_4ci16_ci16_c, &last_fn_name); } static void print_data(const char* header) @@ -132,7 +123,7 @@ START_TEST(conv_4ci16_ci16_check_simd) (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); memcpy(out_etalon, out, bzout); -#if 0 +#ifdef DEBUG_PRINT print_data("ETALON DATA"); #endif @@ -143,7 +134,7 @@ START_TEST(conv_4ci16_ci16_check_simd) { memset(out, 0, bzout); (*fn)(pin, bzin, &pout, bzout); -#if 0 +#ifdef DEBUG_PRINT print_data(NULL); #endif int res = memcmp(out, out_etalon, bzout); @@ -190,18 +181,12 @@ END_TEST Suite * conv_4ci16_ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_4ci16_ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_4ci16_ci16_check_simd); - tcase_add_loop_test(tc_core, conv_4ci16_ci16_speed, 0, 4); + Suite* s = suite_create("conv_4ci16_ci16"); + + ADD_REGRESS_TEST(s, conv_4ci16_ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_4ci16_ci16_speed, 60, 0, 4); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci12_2cf32_utest.c b/src/lib/xdsp/utests/conv_ci12_2cf32_utest.c index 55f1e78f..6716fc51 100644 --- a/src/lib/xdsp/utests/conv_ci12_2cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_2cf32_utest.c @@ -8,16 +8,13 @@ #include #include #include "xdsp_utest_common.h" -#include "../conv_ci12_2cf32_2.h" +#include "conv_ci12_2cf32_2.h" #undef DEBUG_PRINT #define WORD_COUNT (20u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * 12u / 8u) -//#define IN_STREAM_SIZE_BZ 29u -//#define WORD_COUNT (IN_STREAM_SIZE_BZ * 8u / 12u) // 88 i12 words - #define SPEED_WORD_COUNT (8192u) #define SPEED_SIZE_BZ (SPEED_WORD_COUNT * 12u / 8u) @@ -37,11 +34,13 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -81,18 +80,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci12_2cf32_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci12_2cf32_c, &last_fn_name); } #define CONV_SCALE (1.0f/32767) @@ -115,13 +103,6 @@ START_TEST(conv_ci12_2cf32_check) for(uint16_t i = 0; i < WORD_COUNT / 2; ++i) { fprintf(stderr, "%.6f ", out[0][i]); - -// float v = (float)(i << 4); -// v *= CONV_SCALE; -// v = (i % 4) ? v : -v; - -// fprintf(stderr, "\ni=%u\tout=%.6f\texpected=%.6f", i, out[i], v); - //ck_assert_float_eq(v, out[i]); } fprintf(stderr, "\n"); for(uint16_t i = 0; i < WORD_COUNT / 2; ++i) @@ -141,12 +122,14 @@ START_TEST(conv_ci12_2cf32_check_simd) void** pout = (void**)out; last_fn_name = NULL; - const size_t bzin = SPEED_SIZE_BZ; + const size_t bzin = SPEED_SIZE_BZ - 64 + 32 + 10; const size_t bzout = SPEED_WORD_COUNT * sizeof(float); fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out[0], 0, bzout / 2); + memset(out[1], 0, bzout / 2); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); memcpy(out1_etalon, out[0], bzout / 2); memcpy(out2_etalon, out[1], bzout / 2); @@ -222,19 +205,13 @@ END_TEST Suite * conv_ci12_2cf32_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci12_2cf32"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci12_2cf32_check); - tcase_add_test(tc_core, conv_ci12_2cf32_check_simd); - tcase_add_loop_test(tc_core, conv_ci12_2cf32_speed, 0, 3); + Suite* s = suite_create("conv_ci12_2cf32"); + + ADD_REGRESS_TEST(s, conv_ci12_2cf32_check); + ADD_REGRESS_TEST(s, conv_ci12_2cf32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci12_2cf32_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci12_2ci16_utest.c b/src/lib/xdsp/utests/conv_ci12_2ci16_utest.c index 00ff2fd0..a62aac89 100644 --- a/src/lib/xdsp/utests/conv_ci12_2ci16_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_2ci16_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_ci12_2ci16_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define WORD_COUNT (20u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * 12u / 8u) @@ -34,11 +34,13 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/2); + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -72,18 +74,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci12_2ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci12_2ci16_c, &last_fn_name); } static void printer(const char* header) @@ -123,14 +114,18 @@ START_TEST(conv_ci12_2ci16_check_simd) void** pout = (void**)out; last_fn_name = NULL; - const size_t bzin = SPEED_SIZE_BZ; + const size_t bzin = SPEED_SIZE_BZ - 64 + 32 + 10; const size_t bzout = SPEED_WORD_COUNT * sizeof(int16_t); fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out[0], 0, bzout / 2); + memset(out[1], 0, bzout / 2); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out1_etalon, out[0], bzout / 2); memcpy(out2_etalon, out[1], bzout / 2); @@ -189,18 +184,12 @@ END_TEST Suite * conv_ci12_2ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci12_2ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci12_2ci16_check_simd); - tcase_add_loop_test(tc_core, conv_ci12_2ci16_speed, 0, 3); + Suite* s = suite_create("conv_ci12_2ci16"); + + ADD_REGRESS_TEST(s, conv_ci12_2ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci12_2ci16_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c b/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c index 243e938f..dcb8f3ac 100644 --- a/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c @@ -10,18 +10,15 @@ #include "xdsp_utest_common.h" #include "conv_ci12_4cf32_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define WORD_COUNT (32u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * 12u / 8u) -//#define IN_STREAM_SIZE_BZ 29u -//#define WORD_COUNT (IN_STREAM_SIZE_BZ * 8u / 12u) // 88 i12 words - -#define SPEED_WORD_COUNT (8192u) +#define SPEED_WORD_COUNT (65536u) #define SPEED_SIZE_BZ (SPEED_WORD_COUNT * 12u / 8u) -static const unsigned packet_lens[3] = { 1235, 7777, SPEED_SIZE_BZ }; +static const unsigned packet_lens[4] = { 1235, 7777, 12288, SPEED_SIZE_BZ }; #define SPEED_MEASURE_ITERS 1000000 @@ -42,15 +39,17 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -61,7 +60,7 @@ static void setup() uint8_t *pin = (uint8_t*)in; - for(int16_t i = SPEED_WORD_COUNT, j = SPEED_SIZE_BZ; i ; i -= 2, j -= 3) + for(int32_t i = SPEED_WORD_COUNT, j = SPEED_SIZE_BZ; i ; i -= 2, j -= 3) { int16_t v0 = i - 1; int16_t v1 = i - 2; @@ -96,18 +95,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci12_4cf32_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci12_4cf32_c, &last_fn_name); } #define CONV_SCALE (1.0f/32767) @@ -147,12 +135,16 @@ START_TEST(conv_ci12_4cf32_check_simd) void** pout = (void**)out; last_fn_name = NULL; - const size_t bzin = SPEED_SIZE_BZ; + const size_t bzin = SPEED_SIZE_BZ - 64 + 32 + 10; const size_t bzout = SPEED_WORD_COUNT * sizeof(float); fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out[0], 0, bzout / 4); + memset(out[1], 0, bzout / 4); + memset(out[2], 0, bzout / 4); + memset(out[3], 0, bzout / 4); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); memcpy(out1_etalon, out[0], bzout / 4); memcpy(out2_etalon, out[1], bzout / 4); @@ -215,8 +207,10 @@ START_TEST(conv_ci12_4cf32_speed) uint64_t tk = clock_get_time(); for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); uint64_t tk1 = clock_get_time() - tk; - fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", - tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + double ref = 1e6 * tk1 / SPEED_MEASURE_ITERS / (bzin * 8 / 3); + + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s REF=%.3f\n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1), ref); } } } @@ -224,19 +218,13 @@ END_TEST Suite * conv_ci12_4cf32_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci12_2cf32"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci12_4cf32_check); - tcase_add_test(tc_core, conv_ci12_4cf32_check_simd); - tcase_add_loop_test(tc_core, conv_ci12_4cf32_speed, 0, 3); + Suite* s = suite_create("conv_ci12_2cf32"); + + ADD_REGRESS_TEST(s, conv_ci12_4cf32_check); + ADD_REGRESS_TEST(s, conv_ci12_4cf32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci12_4cf32_speed, 60, 0, 4); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci12_4ci16_utest.c b/src/lib/xdsp/utests/conv_ci12_4ci16_utest.c index 5ff17a26..c0af85de 100644 --- a/src/lib/xdsp/utests/conv_ci12_4ci16_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_4ci16_utest.c @@ -10,14 +10,11 @@ #include "xdsp_utest_common.h" #include "conv_ci12_4ci16_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define WORD_COUNT (32u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * 12u / 8u) -//#define IN_STREAM_SIZE_BZ 29u -//#define WORD_COUNT (IN_STREAM_SIZE_BZ * 8u / 12u) // 88 i12 words - #define SPEED_WORD_COUNT (8192u) #define SPEED_SIZE_BZ (SPEED_WORD_COUNT * 12u / 8u) @@ -42,15 +39,17 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/4); + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -90,18 +89,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci12_4ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci12_4ci16_c, &last_fn_name); } static void printer(const char* header) @@ -141,14 +129,20 @@ START_TEST(conv_ci12_4ci16_check_simd) void** pout = (void**)out; last_fn_name = NULL; - const size_t bzin = SPEED_SIZE_BZ; + const size_t bzin = SPEED_SIZE_BZ - 64 + 32 + 10; const size_t bzout = SPEED_WORD_COUNT * sizeof(int16_t); fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out[0], 0, bzout / 4); + memset(out[1], 0, bzout / 4); + memset(out[2], 0, bzout / 4); + memset(out[3], 0, bzout / 4); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out1_etalon, out[0], bzout / 4); memcpy(out2_etalon, out[1], bzout / 4); memcpy(out3_etalon, out[2], bzout / 4); @@ -212,18 +206,12 @@ END_TEST Suite * conv_ci12_4ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci12_4ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci12_4ci16_check_simd); - tcase_add_loop_test(tc_core, conv_ci12_4ci16_speed, 0, 3); + Suite* s = suite_create("conv_ci12_4ci16"); + + ADD_REGRESS_TEST(s, conv_ci12_4ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci12_4ci16_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci16_2cf32_utest.c b/src/lib/xdsp/utests/conv_ci16_2cf32_utest.c index 22fbbab4..20e53647 100644 --- a/src/lib/xdsp/utests/conv_ci16_2cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci16_2cf32_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_ci16_2cf32_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define WORD_COUNT (4096u + 77u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) @@ -34,11 +34,13 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/2); + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -68,18 +70,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci16_2cf32_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci16_2cf32_c, &last_fn_name); } #define CONV_SCALE (1.0f/32767) @@ -113,7 +104,9 @@ START_TEST(conv_ci16_2cf32_check_simd) //get etalon output data (generic foo) (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out1_etalon, out[0], bzout / 2); memcpy(out2_etalon, out[1], bzout / 2); @@ -172,18 +165,12 @@ END_TEST Suite * conv_ci16_2cf32_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci16_2cf32"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci16_2cf32_check_simd); - tcase_add_loop_test(tc_core, conv_ci16_2cf32_speed, 0, 3); + Suite* s = suite_create("conv_ci16_2cf32"); + + ADD_REGRESS_TEST(s, conv_ci16_2cf32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_2cf32_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci16_2ci16_utest.c b/src/lib/xdsp/utests/conv_ci16_2ci16_utest.c index 181fa8e7..a532489a 100644 --- a/src/lib/xdsp/utests/conv_ci16_2ci16_utest.c +++ b/src/lib/xdsp/utests/conv_ci16_2ci16_utest.c @@ -8,7 +8,7 @@ #include #include #include "xdsp_utest_common.h" -#include "../conv_ci16_2ci16_2.h" +#include "conv_ci16_2ci16_2.h" #undef DEBUG_PRINT @@ -34,11 +34,13 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1, ALIGN_BYTES, SPEED_SIZE_BZ/2); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, SPEED_SIZE_BZ/2); - posix_memalign((void**)&out2, ALIGN_BYTES, SPEED_SIZE_BZ/2); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, SPEED_SIZE_BZ/2); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, SPEED_SIZE_BZ/2); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, SPEED_SIZE_BZ/2); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, SPEED_SIZE_BZ/2); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, SPEED_SIZE_BZ/2); + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -68,18 +70,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci16_2ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci16_2ci16_c, &last_fn_name); } #define CONV_SCALE (1.0f/32767) @@ -173,18 +164,12 @@ END_TEST Suite * conv_ci16_2ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci16_2ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci16_2ci16_check_simd); - tcase_add_loop_test(tc_core, conv_ci16_2ci16_speed, 0, 3); + Suite* s = suite_create("conv_ci16_2ci16"); + + ADD_REGRESS_TEST(s, conv_ci16_2ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_2ci16_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci16_3cf32_utest.c b/src/lib/xdsp/utests/conv_ci16_3cf32_utest.c new file mode 100644 index 00000000..539e16b5 --- /dev/null +++ b/src/lib/xdsp/utests/conv_ci16_3cf32_utest.c @@ -0,0 +1,207 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_ci16_3cf32_2.h" + +#undef DEBUG_PRINT + +#define WORD_COUNT (4098u) +#define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) + +#define SPEED_WORD_COUNT (32768u) +#define SPEED_SIZE_BZ (SPEED_WORD_COUNT * sizeof(int16_t)) + +static const unsigned packet_lens[3] = { 1024, 16384, SPEED_SIZE_BZ }; + +#define SPEED_MEASURE_ITERS 1000000 + +static int16_t* in = NULL; +static float* out1 = NULL; +static float* out1_etalon = NULL; +static float* out2 = NULL; +static float* out2_etalon = NULL; +static float* out3 = NULL; +static float* out3_etalon = NULL; + +static float* out[3] = {NULL, NULL, NULL}; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/3); + ck_assert_int_eq(res, 0); + + out[0] = out1; + out[1] = out2; + out[2] = out3; + + srand( time(0) ); + + //fill + int16_t n = 0; + for(unsigned i = 0; i < SPEED_WORD_COUNT; ++i) + { + int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; + in[i] = sign * 32767 * (float)(rand()) / (float)RAND_MAX; + } +#if 0 + for(unsigned i = 0; i < WORD_COUNT; ++i) + { + fprintf(stderr, "%x\n", pin[i]); + } +#endif +} + +static void teardown() +{ + free(in); + free(out1); + free(out1_etalon); + free(out2); + free(out2_etalon); + free(out3); + free(out3_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_ci16_3cf32_c, &last_fn_name); +} + +static void print_data(const char* header) +{ + if(header) + fprintf(stderr, "%s:", header); + + fprintf(stderr, "\n in: "); + for(unsigned i = 0; i < 24; ++i) + { + //fprintf(stderr, "%d ", in[i]); + fprintf(stderr, "%.4f ", (double)in[i] / 32767.f); + } + + for(unsigned n = 0; n < 3; ++n) + { + fprintf(stderr, "\n out%d: ", n); + for(unsigned i = 0; i < 8; ++i) + { + fprintf(stderr, "%+.4f ", out[n][i]); + } + } + fprintf(stderr, "\n"); +} + +#define CONV_SCALE (1.0f/32767) + +START_TEST(conv_ci16_3cf32_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = IN_STREAM_SIZE_BZ; + const size_t bzout = WORD_COUNT * sizeof(float); + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(out[0], 0, bzout / 3); + memset(out[1], 0, bzout / 3); + memset(out[2], 0, bzout / 3); + (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); + memcpy(out1_etalon, out[0], bzout / 3); + memcpy(out2_etalon, out[1], bzout / 3); + memcpy(out3_etalon, out[2], bzout / 3); + +#ifdef DEBUG_PRINT + print_data("ETALON"); +#endif + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out[0], 0, bzout / 3); + memset(out[1], 0, bzout / 3); + memset(out[2], 0, bzout / 3); + (*fn)(&pin, bzin, pout, bzout); + + int res = memcmp(out[0], out1_etalon, bzout / 3) || + memcmp(out[1], out2_etalon, bzout / 3) || + memcmp(out[2], out3_etalon, bzout / 3); + + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); + +#ifdef DEBUG_PRINT + print_data("TEST"); +#endif + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_ci16_3cf32_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i]; + const size_t bzout = SPEED_WORD_COUNT * sizeof(float); + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(&pin, bzin, pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_ci16_3cf32_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_ci16_3cf32"); + + ADD_REGRESS_TEST(s, conv_ci16_3cf32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_3cf32_speed, 60, 0, 3); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_ci16_3ci16_utest.c b/src/lib/xdsp/utests/conv_ci16_3ci16_utest.c new file mode 100644 index 00000000..bf2b5498 --- /dev/null +++ b/src/lib/xdsp/utests/conv_ci16_3ci16_utest.c @@ -0,0 +1,207 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_ci16_3ci16_2.h" + +#undef DEBUG_PRINT + +#define WORD_COUNT (4098u) +#define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) + +#define SPEED_WORD_COUNT (32768u) +#define SPEED_SIZE_BZ (SPEED_WORD_COUNT * sizeof(int16_t)) + +static const unsigned packet_lens[3] = { 1024, 16384, SPEED_SIZE_BZ }; + +#define SPEED_MEASURE_ITERS 1000000 + +static int16_t* in = NULL; +static int16_t* out1 = NULL; +static int16_t* out1_etalon = NULL; +static int16_t* out2 = NULL; +static int16_t* out2_etalon = NULL; +static int16_t* out3 = NULL; +static int16_t* out3_etalon = NULL; + +static int16_t* out[3] = {NULL, NULL, NULL}; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/3); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT/3); + ck_assert_int_eq(res, 0); + + out[0] = out1; + out[1] = out2; + out[2] = out3; + + srand( time(0) ); + + //fill + int16_t n = 0; + for(unsigned i = 0; i < SPEED_WORD_COUNT; ++i) + { + int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; + in[i] = sign * 32767 * (float)(rand()) / (float)RAND_MAX; + //in[i] = i; + } +#if 0 + for(unsigned i = 0; i < WORD_COUNT; ++i) + { + fprintf(stderr, "%x\n", pin[i]); + } +#endif +} + +static void teardown() +{ + free(in); + free(out1); + free(out1_etalon); + free(out2); + free(out2_etalon); + free(out3); + free(out3_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_ci16_3ci16_c, &last_fn_name); +} + +static void print_data(const char* header) +{ + if(header) + fprintf(stderr, "%s:", header); + + fprintf(stderr, "\n in: "); + for(unsigned i = 0; i < 24; ++i) + { + fprintf(stderr, "%d ", in[i]); + } + + for(unsigned n = 0; n < 3; ++n) + { + fprintf(stderr, "\n out%d: ", n); + for(unsigned i = 0; i < 8; ++i) + { + fprintf(stderr, "%d ", out[n][i]); + } + } + fprintf(stderr, "\n"); +} + +#define CONV_SCALE (1.0f/32767) + +START_TEST(conv_ci16_3ci16_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = IN_STREAM_SIZE_BZ; + const size_t bzout = WORD_COUNT * sizeof(int16_t); + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(out[0], 0, bzout / 3); + memset(out[1], 0, bzout / 3); + memset(out[2], 0, bzout / 3); + (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); + memcpy(out1_etalon, out[0], bzout / 3); + memcpy(out2_etalon, out[1], bzout / 3); + memcpy(out3_etalon, out[2], bzout / 3); + +#ifdef DEBUG_PRINT + print_data("ETALON"); +#endif + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out[0], 0, bzout / 3); + memset(out[1], 0, bzout / 3); + memset(out[2], 0, bzout / 3); + (*fn)(&pin, bzin, pout, bzout); + + int res = memcmp(out[0], out1_etalon, bzout / 3) || + memcmp(out[1], out2_etalon, bzout / 3) || + memcmp(out[2], out3_etalon, bzout / 3); + + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); + +#ifdef DEBUG_PRINT + print_data("TEST"); +#endif + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_ci16_3ci16_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i]; + const size_t bzout = SPEED_WORD_COUNT * sizeof(int16_t); + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(&pin, bzin, pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_ci16_3ci16_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_ci16_3ci16"); + + ADD_REGRESS_TEST(s, conv_ci16_3ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_3ci16_speed, 60, 0, 3); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c b/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c index 19fa718b..dcb621ec 100644 --- a/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c @@ -10,12 +10,12 @@ #include "xdsp_utest_common.h" #include "conv_ci16_4cf32_2.h" -#define DEBUG_PRINT +#undef DEBUG_PRINT -#define WORD_COUNT (4096u + 77u) +#define WORD_COUNT (4096u + 80u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) -#define SPEED_WORD_COUNT (32768u) +#define SPEED_WORD_COUNT (65536u) #define SPEED_SIZE_BZ (SPEED_WORD_COUNT * sizeof(int16_t)) static const unsigned packet_lens[3] = { 1024, 16384, SPEED_SIZE_BZ }; @@ -38,15 +38,17 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); - posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/4); + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -85,18 +87,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci16_4cf32_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci16_4cf32_c, &last_fn_name); } #define CONV_SCALE (1.0f/32767) @@ -186,8 +177,10 @@ START_TEST(conv_ci16_4cf32_speed) uint64_t tk = clock_get_time(); for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); uint64_t tk1 = clock_get_time() - tk; - fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", - tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + double ref = 1e6 * tk1 / SPEED_MEASURE_ITERS / (bzin * 2); + + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s REF=%.3f\n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1), ref); } } } @@ -195,18 +188,12 @@ END_TEST Suite * conv_ci16_4cf32_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci16_4cf32"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci16_4cf32_check_simd); - tcase_add_loop_test(tc_core, conv_ci16_4cf32_speed, 0, 3); + Suite* s = suite_create("conv_ci16_4cf32"); + + ADD_REGRESS_TEST(s, conv_ci16_4cf32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_4cf32_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci16_4ci16_utest.c b/src/lib/xdsp/utests/conv_ci16_4ci16_utest.c index 265dd560..0901e8d8 100644 --- a/src/lib/xdsp/utests/conv_ci16_4ci16_utest.c +++ b/src/lib/xdsp/utests/conv_ci16_4ci16_utest.c @@ -10,9 +10,9 @@ #include "xdsp_utest_common.h" #include "conv_ci16_4ci16_2.h" -#define DEBUG_PRINT +#undef DEBUG_PRINT -#define CHECK_WORD_COUNT (4096u + 77u) +#define CHECK_WORD_COUNT (4096u + 80u) //must be a multiple of 4 #define CHECK_SIZE_BZ (CHECK_WORD_COUNT * sizeof(int16_t)) #define SPEED_WORD_COUNT (65536u) @@ -38,17 +38,21 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + int res = 0; - posix_memalign((void**)&out1, ALIGN_BYTES, SPEED_SIZE_BZ/4); - posix_memalign((void**)&out2, ALIGN_BYTES, SPEED_SIZE_BZ/4); - posix_memalign((void**)&out3, ALIGN_BYTES, SPEED_SIZE_BZ/4); - posix_memalign((void**)&out4, ALIGN_BYTES, SPEED_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out1_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); - posix_memalign((void**)&out2_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); - posix_memalign((void**)&out3_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); - posix_memalign((void**)&out4_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, SPEED_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, SPEED_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, SPEED_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, SPEED_SIZE_BZ/4); + + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/4); + + ck_assert_int_eq(res, 0); out[0] = out1; out[1] = out2; @@ -61,7 +65,6 @@ static void setup() for(unsigned i = 0; i < SPEED_WORD_COUNT; ++i) { int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; - //in[i] = sign * 32767 * ((float)(rand()) / (float)RAND_MAX); in[i] = sign * i; } } @@ -81,18 +84,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_ci16_4ci16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_ci16_4ci16_c, &last_fn_name); } @@ -133,8 +125,9 @@ START_TEST(conv_ci16_4ci16_check_simd) //get etalon output data (generic foo) (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT print_data("ETALON"); - +#endif memcpy(out1_etalon, out[0], bzout / 4); memcpy(out2_etalon, out[1], bzout / 4); memcpy(out3_etalon, out[2], bzout / 4); @@ -198,18 +191,12 @@ END_TEST Suite * conv_ci16_4ci16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_ci16_4ci16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_ci16_4ci16_check_simd); - tcase_add_loop_test(tc_core, conv_ci16_4ci16_speed, 0, 4); + Suite* s = suite_create("conv_ci16_4ci16"); + + ADD_REGRESS_TEST(s, conv_ci16_4ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_4ci16_speed, 60, 0, 4); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_ci16_6cf32_utest.c b/src/lib/xdsp/utests/conv_ci16_6cf32_utest.c new file mode 100644 index 00000000..e76f90a1 --- /dev/null +++ b/src/lib/xdsp/utests/conv_ci16_6cf32_utest.c @@ -0,0 +1,238 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_ci16_6cf32_2.h" + +#undef DEBUG_PRINT + +#define WORD_COUNT (4098u) +#define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) + +#define SPEED_WORD_COUNT (32768u) +#define SPEED_SIZE_BZ (SPEED_WORD_COUNT * sizeof(int16_t)) + +static const unsigned packet_lens[3] = { 1024, 16384, SPEED_SIZE_BZ }; + +#define SPEED_MEASURE_ITERS 1000000 + +static int16_t* in = NULL; +static float* out1 = NULL; +static float* out1_etalon = NULL; +static float* out2 = NULL; +static float* out2_etalon = NULL; +static float* out3 = NULL; +static float* out3_etalon = NULL; +static float* out4 = NULL; +static float* out4_etalon = NULL; +static float* out5 = NULL; +static float* out5_etalon = NULL; +static float* out6 = NULL; +static float* out6_etalon = NULL; +static float* out[6] = {NULL, NULL, NULL, NULL, NULL, NULL}; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out5, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out5_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out6, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + res = res ? res : posix_memalign((void**)&out6_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/6); + ck_assert_int_eq(res, 0); + + out[0] = out1; + out[1] = out2; + out[2] = out3; + out[3] = out4; + out[4] = out5; + out[5] = out6; + + srand( time(0) ); + + //fill + int16_t n = 0; + for(unsigned i = 0; i < SPEED_WORD_COUNT; ++i) + { + int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; + in[i] = sign * 32767 * (float)(rand()) / (float)RAND_MAX; + } +#if 0 + for(unsigned i = 0; i < WORD_COUNT; ++i) + { + fprintf(stderr, "%x\n", pin[i]); + } +#endif +} + +static void teardown() +{ + free(in); + free(out1); + free(out1_etalon); + free(out2); + free(out2_etalon); + free(out3); + free(out3_etalon); + free(out4); + free(out4_etalon); + free(out5); + free(out5_etalon); + free(out6); + free(out6_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_ci16_6cf32_c, &last_fn_name); +} + +static void print_data(const char* header) +{ + if(header) + fprintf(stderr, "%s:", header); + + fprintf(stderr, "\n in: "); + for(unsigned i = 0; i < 32; ++i) + { + fprintf(stderr, "%d ", in[i]); + } + + for(unsigned n = 0; n < 6; ++n) + { + fprintf(stderr, "\n out%d: ", n); + for(unsigned i = 0; i < 8; ++i) + { + fprintf(stderr, "%.4f ", out[n][i]); + } + } + fprintf(stderr, "\n"); +} + +#define CONV_SCALE (1.0f/32767) + +START_TEST(conv_ci16_6cf32_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = IN_STREAM_SIZE_BZ; + const size_t bzout = WORD_COUNT * sizeof(float); + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(out[0], 0, bzout / 6); + memset(out[1], 0, bzout / 6); + memset(out[2], 0, bzout / 6); + memset(out[3], 0, bzout / 6); + memset(out[4], 0, bzout / 6); + memset(out[5], 0, bzout / 6); + (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); + memcpy(out1_etalon, out[0], bzout / 6); + memcpy(out2_etalon, out[1], bzout / 6); + memcpy(out3_etalon, out[2], bzout / 6); + memcpy(out4_etalon, out[3], bzout / 6); + memcpy(out5_etalon, out[4], bzout / 6); + memcpy(out6_etalon, out[5], bzout / 6); + +#ifdef DEBUG_PRINT + print_data("ETALON"); +#endif + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out[0], 0, bzout / 6); + memset(out[1], 0, bzout / 6); + memset(out[2], 0, bzout / 6); + memset(out[3], 0, bzout / 6); + memset(out[4], 0, bzout / 6); + memset(out[5], 0, bzout / 6); + (*fn)(&pin, bzin, pout, bzout); + + int res = memcmp(out[0], out1_etalon, bzout / 6) || + memcmp(out[1], out2_etalon, bzout / 6) || + memcmp(out[2], out3_etalon, bzout / 6) || + memcmp(out[3], out4_etalon, bzout / 6) || + memcmp(out[4], out5_etalon, bzout / 6) || + memcmp(out[5], out6_etalon, bzout / 6); + + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); + +#ifdef DEBUG_PRINT + print_data("TEST"); +#endif + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_ci16_6cf32_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i]; + const size_t bzout = SPEED_WORD_COUNT * sizeof(float); + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(&pin, bzin, pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_ci16_6cf32_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_ci16_6cf32"); + + ADD_REGRESS_TEST(s, conv_ci16_6cf32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_6cf32_speed, 60, 0, 3); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_ci16_6ci16_utest.c b/src/lib/xdsp/utests/conv_ci16_6ci16_utest.c new file mode 100644 index 00000000..c85b53d8 --- /dev/null +++ b/src/lib/xdsp/utests/conv_ci16_6ci16_utest.c @@ -0,0 +1,227 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_ci16_6ci16_2.h" + +#undef DEBUG_PRINT + +#define CHECK_WORD_COUNT (4098u) //must be a multiple of 6 +#define CHECK_SIZE_BZ (CHECK_WORD_COUNT * sizeof(int16_t)) + +#define SPEED_WORD_COUNT (65536u) +#define SPEED_SIZE_BZ (SPEED_WORD_COUNT * sizeof(int16_t)) + +static const unsigned packet_lens[4] = { 8192u, 16384u, 32768u, SPEED_WORD_COUNT }; + +#define SPEED_MEASURE_ITERS 1000000 + +static int16_t* in = NULL; +static int16_t* out1 = NULL; +static int16_t* out1_etalon = NULL; +static int16_t* out2 = NULL; +static int16_t* out2_etalon = NULL; +static int16_t* out3 = NULL; +static int16_t* out3_etalon = NULL; +static int16_t* out4 = NULL; +static int16_t* out4_etalon = NULL; +static int16_t* out5 = NULL; +static int16_t* out5_etalon = NULL; +static int16_t* out6 = NULL; +static int16_t* out6_etalon = NULL; +static int16_t* out[6] = {NULL, NULL, NULL, NULL, NULL, NULL}; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, SPEED_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, SPEED_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, SPEED_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, SPEED_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out5, ALIGN_BYTES, SPEED_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out6, ALIGN_BYTES, SPEED_SIZE_BZ/6); + + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out5_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/6); + res = res ? res : posix_memalign((void**)&out6_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/6); + + ck_assert_int_eq(res, 0); + + out[0] = out1; + out[1] = out2; + out[2] = out3; + out[3] = out4; + out[4] = out5; + out[5] = out6; + + srand( time(0) ); + + //fill + for(unsigned i = 0; i < SPEED_WORD_COUNT; ++i) + { + int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; + in[i] = sign * i; + } +} + +static void teardown() +{ + free(in); + free(out1); + free(out2); + free(out3); + free(out4); + free(out5); + free(out6); + free(out1_etalon); + free(out2_etalon); + free(out3_etalon); + free(out4_etalon); + free(out5_etalon); + free(out6_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_ci16_6ci16_c, &last_fn_name); +} + + +static void print_data(const char* header) +{ + if(header) + fprintf(stderr, "%s:", header); + + fprintf(stderr, "\n in: "); + for(unsigned i = 0; i < 32; ++i) + { + fprintf(stderr, "%d ", in[i]); + } + + for(unsigned n = 0; n < 6; ++n) + { + fprintf(stderr, "\n out%d: ", n); + for(unsigned i = 0; i < 8; ++i) + { + fprintf(stderr, "%4d ", out[n][i]); + } + } + fprintf(stderr, "\n"); +} + +START_TEST(conv_ci16_6ci16_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = CHECK_SIZE_BZ; + const size_t bzout = CHECK_SIZE_BZ; + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(out[0], 0, bzout / 6); + memset(out[1], 0, bzout / 6); + memset(out[2], 0, bzout / 6); + memset(out[3], 0, bzout / 6); + memset(out[4], 0, bzout / 6); + memset(out[5], 0, bzout / 6); + (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT + print_data("ETALON"); +#endif + memcpy(out1_etalon, out[0], bzout / 6); + memcpy(out2_etalon, out[1], bzout / 6); + memcpy(out3_etalon, out[2], bzout / 6); + memcpy(out4_etalon, out[3], bzout / 6); + memcpy(out5_etalon, out[4], bzout / 6); + memcpy(out6_etalon, out[5], bzout / 6); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out[0], 0, bzout / 6); + memset(out[1], 0, bzout / 6); + memset(out[2], 0, bzout / 6); + memset(out[3], 0, bzout / 6); + memset(out[4], 0, bzout / 6); + memset(out[5], 0, bzout / 6); + (*fn)(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT + print_data(0); +#endif + int res = memcmp(out[0], out1_etalon, bzout / 6) || memcmp(out[1], out2_etalon, bzout / 6) || + memcmp(out[2], out3_etalon, bzout / 6) || memcmp(out[3], out4_etalon, bzout / 6) || + memcmp(out[4], out5_etalon, bzout / 6) || memcmp(out[5], out6_etalon, bzout / 6); + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_ci16_6ci16_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i] * sizeof(int16_t); + const size_t bzout = bzin; + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(&pin, bzin, pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_ci16_6ci16_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_ci16_6ci16"); + + ADD_REGRESS_TEST(s, conv_ci16_6ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_6ci16_speed, 60, 0, 4); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_ci16_8cf32_utest.c b/src/lib/xdsp/utests/conv_ci16_8cf32_utest.c new file mode 100644 index 00000000..955dce96 --- /dev/null +++ b/src/lib/xdsp/utests/conv_ci16_8cf32_utest.c @@ -0,0 +1,246 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_ci16_8cf32_2.h" + +#undef DEBUG_PRINT + +#define WORD_COUNT (4096u + 80u) +#define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) + +#define SPEED_WORD_COUNT (65536u) +#define SPEED_SIZE_BZ (SPEED_WORD_COUNT * sizeof(int16_t)) + +static const unsigned packet_lens[4] = { 1024, 16384, 32768, SPEED_SIZE_BZ }; + +#define SPEED_MEASURE_ITERS 1000000 + +static int16_t* in = NULL; +static float* out1 = NULL; +static float* out1_etalon = NULL; +static float* out2 = NULL; +static float* out2_etalon = NULL; +static float* out3 = NULL; +static float* out3_etalon = NULL; +static float* out4 = NULL; +static float* out4_etalon = NULL; +static float* out5 = NULL; +static float* out5_etalon = NULL; +static float* out6 = NULL; +static float* out6_etalon = NULL; +static float* out7 = NULL; +static float* out7_etalon = NULL; +static float* out8 = NULL; +static float* out8_etalon = NULL; + +static float* out[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out5, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out5_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out6, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out6_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out7, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out7_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out8, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + res = res ? res : posix_memalign((void**)&out8_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT/8); + ck_assert_int_eq(res, 0); + + out[0] = out1; + out[1] = out2; + out[2] = out3; + out[3] = out4; + out[4] = out5; + out[5] = out6; + out[6] = out7; + out[7] = out8; + + srand( time(0) ); + + //fill + int16_t n = 0; + for(unsigned i = 0; i < SPEED_WORD_COUNT; ++i) + { + int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; + in[i] = sign * 32767 * (float)(rand()) / (float)RAND_MAX; + } +#if 0 + for(unsigned i = 0; i < WORD_COUNT; ++i) + { + fprintf(stderr, "%x\n", pin[i]); + } +#endif +} + +static void teardown() +{ + free(in); + free(out1); + free(out1_etalon); + free(out2); + free(out2_etalon); + free(out3); + free(out3_etalon); + free(out4); + free(out4_etalon); + free(out5); + free(out5_etalon); + free(out6); + free(out6_etalon); + free(out7); + free(out7_etalon); + free(out8); + free(out8_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_ci16_8cf32_c, &last_fn_name); +} + +#define CONV_SCALE (1.0f/32767) + +START_TEST(conv_ci16_8cf32_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = IN_STREAM_SIZE_BZ; + const size_t bzout = WORD_COUNT * sizeof(float); + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(out1_etalon, 0, bzout / 8); + memset(out2_etalon, 0, bzout / 8); + memset(out3_etalon, 0, bzout / 8); + memset(out4_etalon, 0, bzout / 8); + memset(out5_etalon, 0, bzout / 8); + memset(out6_etalon, 0, bzout / 8); + memset(out7_etalon, 0, bzout / 8); + memset(out8_etalon, 0, bzout / 8); + (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); + memcpy(out1_etalon, out[0], bzout / 8); + memcpy(out2_etalon, out[1], bzout / 8); + memcpy(out3_etalon, out[2], bzout / 8); + memcpy(out4_etalon, out[3], bzout / 8); + memcpy(out5_etalon, out[4], bzout / 8); + memcpy(out6_etalon, out[5], bzout / 8); + memcpy(out7_etalon, out[6], bzout / 8); + memcpy(out8_etalon, out[7], bzout / 8); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out[0], 0, bzout / 8); + memset(out[1], 0, bzout / 8); + memset(out[2], 0, bzout / 8); + memset(out[3], 0, bzout / 8); + memset(out[4], 0, bzout / 8); + memset(out[5], 0, bzout / 8); + memset(out[6], 0, bzout / 8); + memset(out[7], 0, bzout / 8); + (*fn)(&pin, bzin, pout, bzout); + + int res = memcmp(out[0], out1_etalon, bzout / 8) || + memcmp(out[1], out2_etalon, bzout / 8) || + memcmp(out[2], out3_etalon, bzout / 8) || + memcmp(out[3], out4_etalon, bzout / 8) || + memcmp(out[4], out5_etalon, bzout / 8) || + memcmp(out[5], out6_etalon, bzout / 8) || + memcmp(out[6], out7_etalon, bzout / 8) || + memcmp(out[7], out8_etalon, bzout / 8); + + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); +#ifdef DEBUG_PRINT + for(unsigned i = 0; res && i < WORD_COUNT; i += 8) + { + if(i > 32) break; + + fprintf(stderr,"#%d in: {", i); + for(unsigned j = 0; j < 8; ++j) fprintf(stderr,"%d, ", in[i+j]); + fprintf(stderr,"}\n"); + + for(unsigned k = 0; k < 8; ++k) + { + fprintf(stderr,"\tout[%d]: {%.2f, %.2f}\n", k, out[k][i / 8] / CONV_SCALE, out[k][i / 8 + 1] / CONV_SCALE); + } + } +#endif + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_ci16_8cf32_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i]; + const size_t bzout = SPEED_WORD_COUNT * sizeof(float); + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(&pin, bzin, pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_ci16_8cf32_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_ci16_8cf32"); + + ADD_REGRESS_TEST(s, conv_ci16_8cf32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_8cf32_speed, 60, 0, 4); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_ci16_8ci16_utest.c b/src/lib/xdsp/utests/conv_ci16_8ci16_utest.c new file mode 100644 index 00000000..5c698f46 --- /dev/null +++ b/src/lib/xdsp/utests/conv_ci16_8ci16_utest.c @@ -0,0 +1,248 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "conv_ci16_8ci16_2.h" + +#undef DEBUG_PRINT + +#define CHECK_WORD_COUNT (4096u + 80u) //must be a multiple of 4 +#define CHECK_SIZE_BZ (CHECK_WORD_COUNT * sizeof(int16_t)) + +#define SPEED_WORD_COUNT (65536u) +#define SPEED_SIZE_BZ (SPEED_WORD_COUNT * sizeof(int16_t)) + +static const unsigned packet_lens[4] = { 8192u, 16384u, 32768u, SPEED_WORD_COUNT }; + +#define SPEED_MEASURE_ITERS 1000000 + +static int16_t* in = NULL; +static int16_t* out1 = NULL; +static int16_t* out1_etalon = NULL; +static int16_t* out2 = NULL; +static int16_t* out2_etalon = NULL; +static int16_t* out3 = NULL; +static int16_t* out3_etalon = NULL; +static int16_t* out4 = NULL; +static int16_t* out4_etalon = NULL; +static int16_t* out5 = NULL; +static int16_t* out5_etalon = NULL; +static int16_t* out6 = NULL; +static int16_t* out6_etalon = NULL; +static int16_t* out7 = NULL; +static int16_t* out7_etalon = NULL; +static int16_t* out8 = NULL; +static int16_t* out8_etalon = NULL; +static int16_t* out[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void setup() +{ + int res = 0; + + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + + res = res ? res : posix_memalign((void**)&out1, ALIGN_BYTES, SPEED_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out2, ALIGN_BYTES, SPEED_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out3, ALIGN_BYTES, SPEED_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out4, ALIGN_BYTES, SPEED_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out5, ALIGN_BYTES, SPEED_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out6, ALIGN_BYTES, SPEED_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out7, ALIGN_BYTES, SPEED_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out8, ALIGN_BYTES, SPEED_SIZE_BZ/8); + + res = res ? res : posix_memalign((void**)&out1_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out2_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out3_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out4_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out5_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out6_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out7_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + res = res ? res : posix_memalign((void**)&out8_etalon, ALIGN_BYTES, CHECK_SIZE_BZ/8); + + ck_assert_int_eq(res, 0); + + out[0] = out1; + out[1] = out2; + out[2] = out3; + out[3] = out4; + out[4] = out5; + out[5] = out6; + out[6] = out7; + out[7] = out8; + + srand( time(0) ); + + //fill + for(unsigned i = 0; i < SPEED_WORD_COUNT; ++i) + { + int sign = (float)(rand()) / (float)RAND_MAX > 0.5 ? -1 : 1; + in[i] = sign * i; + } +} + +static void teardown() +{ + free(in); + free(out1); + free(out2); + free(out3); + free(out4); + free(out5); + free(out6); + free(out7); + free(out8); + free(out1_etalon); + free(out2_etalon); + free(out3_etalon); + free(out4_etalon); + free(out5_etalon); + free(out6_etalon); + free(out7_etalon); + free(out8_etalon); +} + +static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, conv_get_ci16_8ci16_c, &last_fn_name); +} + + +static void print_data(const char* header) +{ + if(header) + fprintf(stderr, "%s:", header); + + fprintf(stderr, "\n in: "); + for(unsigned i = 0; i < 32; ++i) + { + fprintf(stderr, "%d ", in[i]); + } + + for(unsigned n = 0; n < 8; ++n) + { + fprintf(stderr, "\n out%d: ", n); + for(unsigned i = 0; i < 8; ++i) + { + fprintf(stderr, "%4d ", out[n][i]); + } + } + fprintf(stderr, "\n"); +} + +START_TEST(conv_ci16_8ci16_check_simd) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = CHECK_SIZE_BZ; + const size_t bzout = CHECK_SIZE_BZ; + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + //get etalon output data (generic foo) + memset(out[0], 0, bzout / 8); + memset(out[1], 0, bzout / 8); + memset(out[2], 0, bzout / 8); + memset(out[3], 0, bzout / 8); + memset(out[4], 0, bzout / 8); + memset(out[5], 0, bzout / 8); + memset(out[6], 0, bzout / 8); + memset(out[7], 0, bzout / 8); + (*get_fn(OPT_GENERIC, 0))(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT + print_data("ETALON"); +#endif + memcpy(out1_etalon, out[0], bzout / 8); + memcpy(out2_etalon, out[1], bzout / 8); + memcpy(out3_etalon, out[2], bzout / 8); + memcpy(out4_etalon, out[3], bzout / 8); + memcpy(out5_etalon, out[4], bzout / 8); + memcpy(out6_etalon, out[5], bzout / 8); + memcpy(out7_etalon, out[6], bzout / 8); + memcpy(out8_etalon, out[7], bzout / 8); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + memset(out[0], 0, bzout / 8); + memset(out[1], 0, bzout / 8); + memset(out[2], 0, bzout / 8); + memset(out[3], 0, bzout / 8); + memset(out[4], 0, bzout / 8); + memset(out[5], 0, bzout / 8); + memset(out[6], 0, bzout / 8); + memset(out[7], 0, bzout / 8); + (*fn)(&pin, bzin, pout, bzout); +#ifdef DEBUG_PRINT + print_data(0); +#endif + int res = memcmp(out[0], out1_etalon, bzout / 8) || memcmp(out[1], out2_etalon, bzout / 8) || + memcmp(out[2], out3_etalon, bzout / 8) || memcmp(out[3], out4_etalon, bzout / 8) || + memcmp(out[4], out5_etalon, bzout / 8) || memcmp(out[5], out6_etalon, bzout / 8) || + memcmp(out[6], out7_etalon, bzout / 8) || memcmp(out[7], out8_etalon, bzout / 8); + res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); + ck_assert_int_eq( res, 0 ); + } + } +} +END_TEST + + +START_TEST(conv_ci16_8ci16_speed) +{ + generic_opts_t opt = max_opt; + conv_function_t fn = NULL; + const void* pin = (const void*)in; + void** pout = (void**)out; + last_fn_name = NULL; + + const size_t bzin = packet_lens[_i] * sizeof(int16_t); + const size_t bzout = bzin; + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %lu bytes, iters: %u ***\n", bzin, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + conv_function_t fn = get_fn(opt--, 1); + if(fn) + { + //warming + for(int i = 0; i < 100; ++i) (*fn)(&pin, bzin, pout, bzout); + + //measuring + uint64_t tk = clock_get_time(); + for(int i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(&pin, bzin, pout, bzout); + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 call, ave speed = %" PRIu64 " calls/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + } + } +} +END_TEST + +Suite * conv_ci16_8ci16_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("conv_ci16_8ci16"); + + ADD_REGRESS_TEST(s, conv_ci16_8ci16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_ci16_8ci16_speed, 60, 0, 4); + + return s; +} diff --git a/src/lib/xdsp/utests/conv_f32_i12_utest.c b/src/lib/xdsp/utests/conv_f32_i12_utest.c index b4f009e7..b8077252 100644 --- a/src/lib/xdsp/utests/conv_f32_i12_utest.c +++ b/src/lib/xdsp/utests/conv_f32_i12_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_f32_i12_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(float) * 3 / 8) @@ -31,9 +31,11 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, PACKET_SIZE * sizeof(float)); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, PACKET_SIZE * sizeof(float)); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); //fill srand( time(0) ); @@ -54,18 +56,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_f32_i12_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_f32_i12_c, &last_fn_name); } static int is_equal() @@ -152,14 +143,17 @@ START_TEST(conv_f32_i12_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = PACKET_SIZE * sizeof(float); + const size_t bzin = PACKET_SIZE * sizeof(float) - 64 + 32 + 10; const size_t bzout = OUT_BZ; fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, &pout, bzout); +#ifdef DEBUG_PRINT printer("HEADER:"); +#endif memcpy(out_etalon, out, bzout); while(opt != OPT_GENERIC) @@ -173,7 +167,6 @@ START_TEST(conv_f32_i12_check_simd) printer(NULL); #endif int res = is_equal(); - //int res = memcmp(out, out_etalon, bzout); res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); ck_assert_int_eq( res, 0 ); } @@ -217,18 +210,12 @@ END_TEST Suite * conv_f32_i12_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_f32_i12"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_f32_i12_check_simd); - tcase_add_loop_test(tc_core, conv_f32_i12_speed, 0, 3); + Suite* s = suite_create("conv_f32_i12"); + + ADD_REGRESS_TEST(s, conv_f32_i12_check_simd); + ADD_PERF_LOOP_TEST(s, conv_f32_i12_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_f32_i16_utest.c b/src/lib/xdsp/utests/conv_f32_i16_utest.c index 9127f983..072cc52b 100644 --- a/src/lib/xdsp/utests/conv_f32_i16_utest.c +++ b/src/lib/xdsp/utests/conv_f32_i16_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_f32_i16_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define STREAM_SIZE (8192 + 16 + 8 + 7) #define STREAM_SIZE_CHECK STREAM_SIZE @@ -32,9 +32,11 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, sizeof(float) * STREAM_SIZE_SPEED); - posix_memalign((void**)&out, ALIGN_BYTES, sizeof(int16_t) * STREAM_SIZE_SPEED); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(int16_t) * STREAM_SIZE_SPEED); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, sizeof(float) * STREAM_SIZE_SPEED); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(int16_t) * STREAM_SIZE_SPEED); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(int16_t) * STREAM_SIZE_SPEED); + ck_assert_int_eq(res, 0); srand( time(0) ); @@ -74,18 +76,7 @@ static int is_equal() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_f32_i16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_f32_i16_c, &last_fn_name); } static void printer(const char* header) @@ -113,7 +104,9 @@ START_TEST(conv_f32_i16_check) //get etalon output data (generic foo) (*get_fn(OPT_GENERIC, 0))(&pin, bzin, &pout, bzout); +#ifdef DEBUG_PRINT printer("ETALON:"); +#endif memcpy(out_etalon, out, bzout); while(opt != OPT_GENERIC) @@ -176,17 +169,12 @@ END_TEST Suite * conv_f32_i16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_f32_i16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_f32_i16_check); - tcase_add_loop_test(tc_core, conv_f32_i16_speed, 0, 3); - suite_add_tcase(s, tc_core); + Suite* s = suite_create("conv_f32_i16"); + + ADD_REGRESS_TEST(s, conv_f32_i16_check); + ADD_PERF_LOOP_TEST(s, conv_f32_i16_speed, 60, 0, 3); + return s; } diff --git a/src/lib/xdsp/utests/conv_i12_f32_utest.c b/src/lib/xdsp/utests/conv_i12_f32_utest.c index 77a33b5b..c4373c49 100644 --- a/src/lib/xdsp/utests/conv_i12_f32_utest.c +++ b/src/lib/xdsp/utests/conv_i12_f32_utest.c @@ -8,9 +8,9 @@ #include #include #include "xdsp_utest_common.h" -#include "../conv_i12_f32_2.h" +#include "conv_i12_f32_2.h" -#define DEBUG_PRINT +#undef DEBUG_PRINT #define IN_STREAM_SIZE_BZ (132u) // (6 + 3 + 2)*12 = 132 bytes #define WORD_COUNT (IN_STREAM_SIZE_BZ * 8u / 12u) // 88 i12 words @@ -31,9 +31,11 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(float) * SPEED_WORD_COUNT); + ck_assert_int_eq(res, 0); //fill @@ -68,18 +70,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_i12_f32_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_i12_f32_c, &last_fn_name); } #define CONV_SCALE (1.0f/32767) @@ -104,9 +95,10 @@ START_TEST(conv_i12_f32_check) v *= CONV_SCALE; v = (i % 4) ? v : -v; - int16_t i12 = (int16_t)(out[i] / CONV_SCALE) >> 4; - +#ifdef DEBUG_PRINT + int16_t i12 = (int16_t)(out[i] / CONV_SCALE) >> 4; fprintf(stderr, "\ni=%u\ti12=%d\tout=%.6f\texpected=%.6f", i, i12, out[i], v); +#endif #ifdef ck_assert_float_eq ck_assert_float_eq(v, out[i]); #else @@ -125,12 +117,13 @@ START_TEST(conv_i12_f32_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = IN_STREAM_SIZE_BZ; + const size_t bzin = IN_STREAM_SIZE_BZ - 64 + 32 + 10; const size_t bzout = WORD_COUNT * sizeof(float); fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, &pout, bzout); memcpy(out_etalon, out, bzout); @@ -200,19 +193,13 @@ END_TEST Suite * conv_i12_f32_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_i12_f32"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_i12_f32_check); - tcase_add_test(tc_core, conv_i12_f32_check_simd); - tcase_add_loop_test(tc_core, conv_i12_f32_speed, 0, 3); + Suite* s = suite_create("conv_i12_f32"); + + ADD_REGRESS_TEST(s, conv_i12_f32_check); + ADD_REGRESS_TEST(s, conv_i12_f32_check_simd); + ADD_PERF_LOOP_TEST(s, conv_i12_f32_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_i12_i16_utest.c b/src/lib/xdsp/utests/conv_i12_i16_utest.c index f87a154d..31b80176 100644 --- a/src/lib/xdsp/utests/conv_i12_i16_utest.c +++ b/src/lib/xdsp/utests/conv_i12_i16_utest.c @@ -10,7 +10,7 @@ #include "xdsp_utest_common.h" #include "conv_i12_i16_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define IN_STREAM_SIZE_BZ (132u) // (6 + 3 + 2)*12 = 132 bytes #define WORD_COUNT (IN_STREAM_SIZE_BZ * 8u / 12u) // 88 i12 words @@ -31,9 +31,11 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&out, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(int16_t) * SPEED_WORD_COUNT); + ck_assert_int_eq(res, 0); //fill @@ -62,18 +64,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_i12_i16_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_i12_i16_c, &last_fn_name); } START_TEST(conv_i12_i16_check) @@ -111,12 +102,13 @@ START_TEST(conv_i12_i16_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = SPEED_SIZE_BZ; + const size_t bzin = SPEED_SIZE_BZ - 64 + 32 + 10; const size_t bzout = SPEED_WORD_COUNT * sizeof(int16_t); fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, &pout, bzout); memcpy(out_etalon, out, bzout); @@ -194,19 +186,13 @@ END_TEST Suite * conv_i12_i16_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_i12_i16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_i12_i16_check); - tcase_add_test(tc_core, conv_i12_i16_check_simd); - tcase_add_loop_test(tc_core, conv_i12_i16_speed, 0, 3); + Suite* s = suite_create("conv_i12_i16"); + + ADD_REGRESS_TEST(s, conv_i12_i16_check); + ADD_REGRESS_TEST(s, conv_i12_i16_check_simd); + ADD_PERF_LOOP_TEST(s, conv_i12_i16_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/conv_i16_f32_utest.c b/src/lib/xdsp/utests/conv_i16_f32_utest.c index 6c54685b..adc9d556 100644 --- a/src/lib/xdsp/utests/conv_i16_f32_utest.c +++ b/src/lib/xdsp/utests/conv_i16_f32_utest.c @@ -29,9 +29,11 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, sizeof(int16_t) * STREAM_SIZE_SPEED); - posix_memalign((void**)&out, ALIGN_BYTES, sizeof(float) * STREAM_SIZE_SPEED); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(float) * STREAM_SIZE_SPEED); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, sizeof(int16_t) * STREAM_SIZE_SPEED); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(float) * STREAM_SIZE_SPEED); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(float) * STREAM_SIZE_SPEED); + ck_assert_int_eq(res, 0); srand( time(0) ); @@ -51,18 +53,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_i16_f32_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_i16_f32_c, &last_fn_name); } START_TEST(conv_i16_f32_check) @@ -139,17 +130,12 @@ END_TEST Suite * conv_i16_f32_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_i16_f32"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_i16_f32_check); - tcase_add_loop_test(tc_core, conv_i16_f32_speed, 0, 3); - suite_add_tcase(s, tc_core); + Suite* s = suite_create("conv_i16_f32"); + + ADD_REGRESS_TEST(s, conv_i16_f32_check); + ADD_PERF_LOOP_TEST(s, conv_i16_f32_speed, 60, 0, 3); + return s; } diff --git a/src/lib/xdsp/utests/conv_i16_i12_utest.c b/src/lib/xdsp/utests/conv_i16_i12_utest.c index bf103bbe..237bb5e2 100644 --- a/src/lib/xdsp/utests/conv_i16_i12_utest.c +++ b/src/lib/xdsp/utests/conv_i16_i12_utest.c @@ -10,12 +10,11 @@ #include "xdsp_utest_common.h" #include "conv_i16_i12_2.h" -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(int16_t) * 3 / 4) - static const unsigned packet_lens[3] = { 1111u, 4123u, PACKET_SIZE }; #define SPEED_MEASURE_ITERS 1000000 @@ -29,9 +28,11 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t)); - posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, PACKET_SIZE * sizeof(int16_t)); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, OUT_BZ); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, OUT_BZ); + ck_assert_int_eq(res, 0); //fill for(int i = 0; i < PACKET_SIZE; ++i) @@ -49,18 +50,7 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - const char* fn_name = NULL; - conv_function_t fn = conv_get_i16_i12_c(o, &fn_name); - - //ignore dups - if(last_fn_name && !strcmp(last_fn_name, fn_name)) - return NULL; - - if(log) - fprintf(stderr, "%-20s\t", fn_name); - - last_fn_name = fn_name; - return fn; + return generic_get_fn(o, log, conv_get_i16_i12_c, &last_fn_name); } static void printer(const char* header) @@ -87,12 +77,13 @@ START_TEST(conv_i16_i12_check_simd) void* pout = (void*)out; last_fn_name = NULL; - const size_t bzin = PACKET_SIZE * sizeof(int16_t); + const size_t bzin = PACKET_SIZE * sizeof(int16_t) - 64 + 32 + 10; //we should test all branches const size_t bzout = OUT_BZ; fprintf(stderr,"\n**** Check SIMD implementations ***\n"); //get etalon output data (generic foo) + memset(out, 0, bzout); (*get_fn(OPT_GENERIC, 0))(&pin, bzin, &pout, bzout); memcpy(out_etalon, out, bzout); @@ -158,18 +149,12 @@ END_TEST Suite * conv_i16_i12_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("conv_i16_i12"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, conv_i16_i12_check_simd); - tcase_add_loop_test(tc_core, conv_i16_i12_speed, 0, 3); + Suite* s = suite_create("conv_i16_i12"); + + ADD_REGRESS_TEST(s, conv_i16_i12_check_simd); + ADD_PERF_LOOP_TEST(s, conv_i16_i12_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/fft_window_cf32_utest.c b/src/lib/xdsp/utests/fft_window_cf32_utest.c index 3c137c66..1b6a90e2 100644 --- a/src/lib/xdsp/utests/fft_window_cf32_utest.c +++ b/src/lib/xdsp/utests/fft_window_cf32_utest.c @@ -6,10 +6,10 @@ #include #include #include "xdsp_utest_common.h" -#include "../fft_window_functions.h" +#include "fft_window_functions.h" #define FFT_SIZE (65536) -static const unsigned packet_lens[3] = { 256, 4096, FFT_SIZE }; +static const unsigned packet_lens[3] = { 4096, 16384, FFT_SIZE }; #define SPEED_MEASURE_ITERS 1000000 #define EPSILON 1E-4 @@ -24,42 +24,25 @@ static generic_opts_t max_opt = OPT_GENERIC; static void recalcWnd(unsigned fft_size) { - for(unsigned i = 0; i < fft_size * 2; i += 2) + for(unsigned i = 0; i < fft_size; i++) { wnd[i] = (1 - cos(2 * M_PI * i / fft_size)) / 2; - wnd[i+1] = (1 - cos(2 * M_PI * (i + 1) / fft_size)) / 2; } } -#if 0 -static void recalcWnd(unsigned fft_size) -{ - float wc = 0.f; - for(unsigned i = 0; i < fft_size; ++i) - { - //Hann - wnd[i] = (1 - cos(2 * M_PI * i / fft_size)) / 2; - wc += wnd[i] * wnd[i]; - } - - float corr = 1.0f / sqrt(wc / fft_size); - for(unsigned i = 0; i < fft_size; ++i) - { - wnd[i] *= corr; - } -} -#endif static void setup() { - posix_memalign((void**)&in, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); - posix_memalign((void**)&out, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); - posix_memalign((void**)&wnd, ALIGN_BYTES, sizeof(float) * 2 * FFT_SIZE); + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); + res = res ? res : posix_memalign((void**)&wnd, ALIGN_BYTES, sizeof(float) * FFT_SIZE); + ck_assert_int_eq(res, 0); for(unsigned i = 0; i < FFT_SIZE; ++i) { - in[i][0] = 100.0f * (float)(rand()) / (float)RAND_MAX; - in[i][1] = -100.0f * (float)(rand()) / (float)RAND_MAX; + in[i][0] = 1.0f * (float)(rand()) / (float)RAND_MAX; + in[i][1] = -1.0f * (float)(rand()) / (float)RAND_MAX; } recalcWnd(FFT_SIZE); @@ -165,17 +148,12 @@ END_TEST Suite * fft_window_cf32_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("fft_window_cf32_functions"); - tc_core = tcase_create("XFFT"); - tcase_set_timeout(tc_core, 300); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, wnd_check); - tcase_add_loop_test(tc_core, wnd_speed, 0, 3); - suite_add_tcase(s, tc_core); + Suite* s = suite_create("fft_window_cf32_functions"); + + ADD_REGRESS_TEST(s, wnd_check); + ADD_PERF_LOOP_TEST(s, wnd_speed, 300, 0, 3); + return s; } diff --git a/src/lib/xdsp/utests/fft_window_ci16_cf32_utest.c b/src/lib/xdsp/utests/fft_window_ci16_cf32_utest.c new file mode 100644 index 00000000..aae826c8 --- /dev/null +++ b/src/lib/xdsp/utests/fft_window_ci16_cf32_utest.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include "xdsp_utest_common.h" +#include "fft_window_functions.h" + +#define FFT_SIZE (65536) +static const unsigned packet_lens[3] = { 4096, 16384, FFT_SIZE }; + +#define SPEED_MEASURE_ITERS 1000000 +#define EPSILON 1E-4 +#define CONV_SCALE (1.0f/32767) + +static wvlt_fftwi16_complex* in = NULL; +static wvlt_fftwf_complex* out = NULL; +static wvlt_fftwf_complex* out_etalon = NULL; +static float* wnd = NULL; + +static const char* last_fn_name = NULL; +static generic_opts_t max_opt = OPT_GENERIC; + +static void recalcWnd(unsigned fft_size) +{ + for(unsigned i = 0; i < fft_size; i++) + { + wnd[i] = CONV_SCALE * (1 - cos(2 * M_PI * i / fft_size)) / 2; + } +} + +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, sizeof(wvlt_fftwi16_complex) * FFT_SIZE); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * FFT_SIZE); + res = res ? res : posix_memalign((void**)&wnd, ALIGN_BYTES, sizeof(float) * FFT_SIZE); + ck_assert_int_eq(res, 0); + + for(unsigned i = 0; i < FFT_SIZE; ++i) + { + in[i][0] = 32767 * (float)(rand()) / (float)RAND_MAX; + in[i][1] = -32768 * (float)(rand()) / (float)RAND_MAX; + } + + recalcWnd(FFT_SIZE); +} + +static void teardown(void) +{ + free(in); + free(out); + free(out_etalon); + free(wnd); +} + +static int32_t is_equal() +{ + for(unsigned i = 0; i < FFT_SIZE; i++) + { + if(fabs(out[i][0] - out_etalon[i][0]) > EPSILON) return i; + if(fabs(out[i][1] - out_etalon[i][1]) > EPSILON) return i; + } + return -1; +} + +START_TEST(wnd_check) +{ + generic_opts_t opt = max_opt; + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + fft_window_ci16_cf32_c(OPT_GENERIC, NULL)(in, FFT_SIZE, wnd, out_etalon); + last_fn_name = NULL; + const char* fn_name = NULL; + fft_window_ci16_cf32_function_t fn = NULL; + + while(opt != OPT_GENERIC) + { + fn = fft_window_ci16_cf32_c(opt, &fn_name); + + if(last_fn_name && !strcmp(last_fn_name, fn_name)) + { + --opt; + continue; + } + + last_fn_name = fn_name; + fn(in, FFT_SIZE, wnd, out); + + int res = is_equal(); + fprintf(stderr, "%-30s\t", fn_name); + (res >= 0) ? fprintf(stderr, "\tFAILED!\n") : fprintf(stderr, "\tOK!\n"); +#if 1 + if(res >= 0) + { + unsigned i = res; + fprintf(stderr, "TEST > i:%u in=(%d,%d) out=(%.6f,%.6f) <---> out_etalon=(%.6f,%.6f)\n", + i, in[i][0], in[i][1], out[i][0], out[i][1], out_etalon[i][0], out_etalon[i][1]); + } +#endif + ck_assert_int_eq( res, -1 ); + --opt; + } +} +END_TEST + +START_TEST(wnd_speed) +{ + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + const char* fn_name = NULL; + fft_window_ci16_cf32_function_t fn = NULL; + + unsigned size = packet_lens[_i]; + + last_fn_name = NULL; + generic_opts_t opt = max_opt; + + fprintf(stderr, "**** packet: %u elems, iters: %u ***\n", size, SPEED_MEASURE_ITERS); + + while(opt != OPT_GENERIC) + { + fn = fft_window_ci16_cf32_c(opt, &fn_name); + if(last_fn_name && !strcmp(last_fn_name, fn_name)) + { + --opt; + continue; + } + last_fn_name = fn_name; + fprintf(stderr, "%-30s\t", fn_name); + + //warming + for(unsigned i = 0; i < 100; ++i) (*fn)(in, size, wnd, out); + + //measuring + uint64_t tk = clock_get_time(); + for(unsigned i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn)(in, size, wnd, out); + uint64_t tk1 = clock_get_time() - tk; + + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 cycle, ave speed = %" PRIu64 " cycles/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_MEASURE_ITERS), (uint64_t)(1000000LL*SPEED_MEASURE_ITERS/tk1)); + + --opt; + } +} +END_TEST + +Suite * fft_window_ci16_cf32_suite(void) +{ + max_opt = cpu_vcap_get(); + + Suite* s = suite_create("fft_window_ci16_cf32_functions"); + + ADD_REGRESS_TEST(s, wnd_check); + ADD_PERF_LOOP_TEST(s, wnd_speed, 300, 0, 3); + + return s; +} diff --git a/src/lib/xdsp/utests/wvlt_sincos_i16_utest.c b/src/lib/xdsp/utests/wvlt_sincos_i16_utest.c index d962e9a9..b11a386a 100644 --- a/src/lib/xdsp/utests/wvlt_sincos_i16_utest.c +++ b/src/lib/xdsp/utests/wvlt_sincos_i16_utest.c @@ -11,7 +11,7 @@ #include "sincos_functions.h" #include -//#define DEBUG_PRINT +#undef DEBUG_PRINT #define WORD_COUNT (65536) #define STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) @@ -41,17 +41,28 @@ static int16_t gain[SPEED_CYCLES]; static const char* last_fn_name = NULL; static generic_opts_t max_opt = OPT_GENERIC; -static void setup() +struct chirp_t { - posix_memalign((void**)&in_check, ALIGN_BYTES, STREAM_SIZE_BZ); - posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&sindata, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&sindata_etalon, ALIGN_BYTES, STREAM_SIZE_BZ); - posix_memalign((void**)&cosdata, ALIGN_BYTES, SPEED_SIZE_BZ); - posix_memalign((void**)&cosdata_etalon, ALIGN_BYTES, STREAM_SIZE_BZ); + int32_t phase_diap[2]; + int32_t start_dphase; + int32_t steps_count; +}; + +static struct chirp_t chirp = { {-1000000, 1000000}, -1000000, (100000 / 8) * 8 + 7}; - posix_memalign((void**)&sincosdata, ALIGN_BYTES, SPEED_WORD_COUNT * 2 * sizeof(int16_t)); - posix_memalign((void**)&sincosdata_etalon, ALIGN_BYTES, WORD_COUNT * 2 * sizeof(int16_t)); +static void setup() +{ + int res = 0; + res = res ? res : posix_memalign((void**)&in_check, ALIGN_BYTES, STREAM_SIZE_BZ); + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&sindata, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&sindata_etalon, ALIGN_BYTES, STREAM_SIZE_BZ); + res = res ? res : posix_memalign((void**)&cosdata, ALIGN_BYTES, SPEED_SIZE_BZ); + res = res ? res : posix_memalign((void**)&cosdata_etalon, ALIGN_BYTES, STREAM_SIZE_BZ); + + res = res ? res : posix_memalign((void**)&sincosdata, ALIGN_BYTES, SPEED_WORD_COUNT * 2 * sizeof(int16_t)); + res = res ? res : posix_memalign((void**)&sincosdata_etalon, ALIGN_BYTES, WORD_COUNT * 2 * sizeof(int16_t)); + ck_assert_int_eq(res, 0); srand( time(0) ); @@ -98,9 +109,14 @@ static void teardown() } static conv_function_t get_fn(generic_opts_t o, int log) +{ + return generic_get_fn(o, log, get_wvlt_sincos_i16_c, &last_fn_name); +} + +static sincos_i16_interleaved_ctrl_function_t get_fn_interleaved(generic_opts_t o, int log) { const char* fn_name = NULL; - conv_function_t fn = get_wvlt_sincos_i16_c(o, &fn_name); + sincos_i16_interleaved_ctrl_function_t fn = get_wvlt_sincos_i16_interleaved_ctrl_c(o, &fn_name); //ignore dups if(last_fn_name && !strcmp(last_fn_name, fn_name)) @@ -113,10 +129,10 @@ static conv_function_t get_fn(generic_opts_t o, int log) return fn; } -static sincos_i16_interleaved_ctrl_function_t get_fn_interleaved(generic_opts_t o, int log) +static sincos_i16_interleaved_chirp_function_t get_fn_interleaved_chirp(generic_opts_t o, int log) { const char* fn_name = NULL; - sincos_i16_interleaved_ctrl_function_t fn = get_wvlt_sincos_i16_interleaved_ctrl_c(o, &fn_name); + sincos_i16_interleaved_chirp_function_t fn = get_wvlt_sincos_i16_interleaved_chirp_c(o, &fn_name); //ignore dups if(last_fn_name && !strcmp(last_fn_name, fn_name)) @@ -129,6 +145,7 @@ static sincos_i16_interleaved_ctrl_function_t get_fn_interleaved(generic_opts_t return fn; } + static int32_t is_equal() { for(unsigned i = 0; i < WORD_COUNT; i++) @@ -338,23 +355,133 @@ START_TEST(wvlt_sincos_i16_speed) END_TEST +// CHIRP +START_TEST(wvlt_sincos_i16_interleaved_chirp_check_simd) +{ + generic_opts_t opt = max_opt; + sincos_i16_interleaved_chirp_function_t fn = NULL; + last_fn_name = NULL; + + fprintf(stderr,"\n**** Check SIMD implementations ***\n"); + + int32_t ph_etalon = start_phase[0]; + int32_t phdelta_etalon = chirp.start_dphase; -Suite * wvlt_sincos_i16_suite(void) + const int32_t delta_ph = delta_phase[0]; + const bool inv_sin = invert_sin[0]; + const bool inv_cos = invert_cos[0]; + const int16_t gain_c = gain[0]; + + //get etalon output data (generic foo) + (*get_fn_interleaved_chirp(OPT_GENERIC, 0)) + (&ph_etalon, &phdelta_etalon, chirp.phase_diap, chirp.steps_count, gain_c, inv_sin, inv_cos, sincosdata_etalon, WORD_COUNT); + + fprintf(stderr, "-- start_phase:%d delta_phase:%d final_phase:%d\n", start_phase[0], delta_ph, ph_etalon); + + + while(opt != OPT_GENERIC) + { + sincos_i16_interleaved_chirp_function_t fn = get_fn_interleaved_chirp(opt--, 1); + if(fn) + { + memset(sincosdata, 0, WORD_COUNT * 2 * sizeof(int16_t)); + int32_t ph = start_phase[0]; + int32_t phdelta = chirp.start_dphase; + + (*fn)(&ph, &phdelta, chirp.phase_diap, chirp.steps_count, gain_c, inv_sin, inv_cos, sincosdata, WORD_COUNT); + + int32_t tmp_ph = start_phase[0]; + int32_t max_eps = 0; + for(unsigned i = 0; i < WORD_COUNT; ++i, tmp_ph += delta_ph) + { + int16_t ss = sincosdata[i*2]; + int16_t cc = sincosdata[i*2 + 1]; + int16_t sse = sincosdata_etalon[i*2]; + int16_t cce = sincosdata_etalon[i*2 + 1]; +#ifdef DEBUG_PRINT + fprintf(stderr, "i#%d : phase:%12d, out{sin:%6d cos:%6d}, etalon{sin:%6d cos:%6d}, delta = {%4d %4d}\n", + i, tmp_ph, ss, cc, sse, cce, abs(ss-sse), abs(cc-cce)); +#endif + if(abs(ss-sse) > max_eps) + max_eps = abs(ss-sse); + if(abs(cc-cce) > max_eps) + max_eps = abs(cc-cce); + } + fprintf(stderr, "-- final_phase:%d (est:%d) max_eps:%d\n", ph, tmp_ph, max_eps); + + int res = is_equal_interleaved(); + res >= 0 ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); + + for(int i = res - 20; res >= 0 && i <= res + 20; ++i) + { + if(i >= 0 && i < WORD_COUNT) + fprintf(stderr, "%si#%d : in = %d, out = {sin:%d cos:%d}, etalon = {sin:%d cos:%d}, delta = {%d %d}\n", + i == res ? ">>>" : " ", + i, in_check[i], sincosdata[i*2], sincosdata[i*2 + 1], sincosdata_etalon[i*2], sincosdata_etalon[i*2 + 1], + abs(sincosdata_etalon[i*2] - sincosdata[i*2]), abs(sincosdata_etalon[i*2 + 1] - sincosdata[i*2 + 1])); + } + + ck_assert_int_eq( res, -1 ); + ck_assert_int_eq( ph, ph_etalon ); + } + } +} +END_TEST + + +START_TEST(wvlt_sincos_i16_interleaved_chirp_speed) { - Suite *s; - TCase *tc_core; + generic_opts_t opt = max_opt; + sincos_i16_interleaved_chirp_function_t fn = NULL; + last_fn_name = NULL; + + const unsigned iters = packet_lens[_i]; + + fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "**** packet: %d IQs, cycles: %u ***\n", iters, SPEED_CYCLES); + + while(opt != OPT_GENERIC) + { + sincos_i16_interleaved_chirp_function_t fn = get_fn_interleaved_chirp(opt--, 1); + if(fn) + { + int32_t ph = start_phase[0]; + int32_t phdelta = chirp.start_dphase; + //warming + for(int i = 0; i < 10; ++i) + (*fn)(&ph, &phdelta, chirp.phase_diap, chirp.steps_count, gain[0], invert_sin[0], invert_cos[0], sincosdata, SPEED_WORD_COUNT); + + //measuring + phdelta = chirp.start_dphase; + uint64_t tk = clock_get_time(); + for(unsigned i = 0; i < SPEED_CYCLES; ++i) + { + (*fn) + (&start_phase[i], &phdelta, chirp.phase_diap, chirp.steps_count, gain[i], invert_sin[i], invert_cos[i], sincosdata, iters); + } + uint64_t tk1 = clock_get_time() - tk; + fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 IQ, ave speed = %.2f mln IQs/s \n", + tk1, (uint64_t)(tk1*1000LL/SPEED_CYCLES/iters), (uint64_t)(1000000LL*SPEED_CYCLES*iters/tk1)/(float)1000000); + } + } +} +END_TEST +// + + +Suite * wvlt_sincos_i16_suite(void) +{ max_opt = cpu_vcap_get(); - s = suite_create("wvlt_sincos_i16"); - tc_core = tcase_create("XDSP"); - tcase_set_timeout(tc_core, 60); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, wvlt_sincos_i16_check_simd); - tcase_add_loop_test(tc_core, wvlt_sincos_i16_speed, 0, 3); - tcase_add_test(tc_core, wvlt_sincos_i16_interleaved_ctrl_check_simd); - tcase_add_loop_test(tc_core, wvlt_sincos_i16_interleaved_ctrl_speed, 0, 3); + Suite* s = suite_create("wvlt_sincos_i16"); + + ADD_REGRESS_TEST(s, wvlt_sincos_i16_check_simd); + ADD_PERF_LOOP_TEST(s, wvlt_sincos_i16_speed, 60, 0, 3); + ADD_REGRESS_TEST(s, wvlt_sincos_i16_interleaved_ctrl_check_simd); + ADD_PERF_LOOP_TEST(s, wvlt_sincos_i16_interleaved_ctrl_speed, 60, 0, 3); + ADD_REGRESS_TEST(s, wvlt_sincos_i16_interleaved_chirp_check_simd); + ADD_PERF_LOOP_TEST(s, wvlt_sincos_i16_interleaved_chirp_speed, 60, 0, 3); - suite_add_tcase(s, tc_core); return s; } diff --git a/src/lib/xdsp/utests/xdsp_utest_common.h b/src/lib/xdsp/utests/xdsp_utest_common.h index 09cb0de5..74e63ead 100644 --- a/src/lib/xdsp/utests/xdsp_utest_common.h +++ b/src/lib/xdsp/utests/xdsp_utest_common.h @@ -4,17 +4,66 @@ #ifndef XDSP_UTEST_COMMON_H #define XDSP_UTEST_COMMON_H -#include #include #include +#include +#include + +#include "../../cal/opt_func.h" +#include "conv.h" + +// tcase_set_tags() was introduced in check 0.11.0 +#if CHECK_MINOR_VERSION < 11 +#define tcase_set_tags(x, y) +#endif #define ALIGN_BYTES (size_t)64 -static inline uint64_t clock_get_time() +#define ADD_REGRESS_TEST(suitename, testname) \ +{ \ + TCase* tc_regress = tcase_create("REGRESS"); \ + tcase_set_tags(tc_regress, "REGRESS"); \ + tcase_add_unchecked_fixture(tc_regress, setup, teardown); \ + tcase_add_test(tc_regress, testname); \ + suite_add_tcase(suitename, tc_regress); \ +} + +#define ADD_PERF_LOOP_TEST(suitename, testname, timeout, from, to) \ +{ \ + TCase* tc_perf = tcase_create("PERFORMANCE"); \ + tcase_set_tags(tc_perf, "PERFORMANCE"); \ + tcase_add_unchecked_fixture(tc_perf, setup, teardown); \ + tcase_set_timeout(tc_perf, timeout); \ + tcase_add_loop_test(tc_perf, testname, from, to); \ + suite_add_tcase(suitename, tc_perf); \ +} + +#define ADD_PERF_TEST(suitename, testname, timeout) \ +{ \ + TCase* tc_perf = tcase_create("PERFORMANCE"); \ + tcase_set_tags(tc_perf, "PERFORMANCE"); \ + tcase_add_unchecked_fixture(tc_perf, setup, teardown); \ + tcase_set_timeout(tc_perf, timeout); \ + tcase_add_test(tc_perf, testname); \ + suite_add_tcase(suitename, tc_perf); \ +} + +typedef conv_function_t (*conv_wrapper_fn_t)(generic_opts_t cpu_cap, const char** sfunc); + +static inline conv_function_t generic_get_fn(generic_opts_t o, int log, conv_wrapper_fn_t wfn, const char** last_fn_name) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec/1000LL; + const char* fn_name = NULL; + conv_function_t fn = wfn(o, &fn_name); + + //ignore dups + if(*last_fn_name && !strcmp(*last_fn_name, fn_name)) + return NULL; + + if(log) + fprintf(stderr, "%-20s\t", fn_name); + + *last_fn_name = fn_name; + return fn; } #endif // XDSP_UTEST_COMMON_H diff --git a/src/lib/xdsp/utests/xdsp_utest_suite.c b/src/lib/xdsp/utests/xdsp_utest_suite.c index 163bb5e9..fc89b6a8 100644 --- a/src/lib/xdsp/utests/xdsp_utest_suite.c +++ b/src/lib/xdsp/utests/xdsp_utest_suite.c @@ -15,6 +15,7 @@ Suite * conv_2ci16_ci16_suite(void); Suite * fftad_suite(void); Suite * rtsa_suite(void); Suite * fft_window_cf32_suite(void); +Suite * fft_window_ci16_cf32_suite(void); Suite * conv_i12_f32_suite(void); Suite * conv_ci12_2cf32_suite(void); Suite * conv_f32_i12_suite(void); @@ -32,6 +33,14 @@ Suite * conv_ci12_2ci16_suite(void); Suite * conv_ci12_4ci16_suite(void); Suite * conv_2ci16_ci12_suite(void); Suite * conv_4ci16_ci12_suite(void); +Suite * conv_ci16_6ci16_suite(void); +Suite * conv_ci16_6cf32_suite(void); +Suite * conv_ci16_8cf32_suite(void); +Suite * conv_ci16_8ci16_suite(void); +Suite * conv_ci16_3cf32_suite(void); +Suite * conv_ci16_3ci16_suite(void); +Suite * conv_3ci16_ci16_suite(void); +Suite * conv_3cf32_ci16_suite(void); int main(int argc, char** argv) { @@ -49,15 +58,23 @@ int main(int argc, char** argv) // srunner_add_suite(sr, conv_i16_f32_suite()); srunner_add_suite(sr, conv_ci16_2cf32_suite()); + srunner_add_suite(sr, conv_ci16_3cf32_suite()); srunner_add_suite(sr, conv_ci16_4cf32_suite()); + srunner_add_suite(sr, conv_ci16_6cf32_suite()); + srunner_add_suite(sr, conv_ci16_8cf32_suite()); // srunner_add_suite(sr, conv_f32_i16_suite()); srunner_add_suite(sr, conv_2cf32_ci16_suite()); + srunner_add_suite(sr, conv_3cf32_ci16_suite()); srunner_add_suite(sr, conv_4cf32_ci16_suite()); // srunner_add_suite(sr, conv_ci16_2ci16_suite()); + srunner_add_suite(sr, conv_ci16_3ci16_suite()); srunner_add_suite(sr, conv_ci16_4ci16_suite()); + srunner_add_suite(sr, conv_ci16_6ci16_suite()); + srunner_add_suite(sr, conv_ci16_8ci16_suite()); srunner_add_suite(sr, conv_2ci16_ci16_suite()); + srunner_add_suite(sr, conv_3ci16_ci16_suite()); srunner_add_suite(sr, conv_4ci16_ci16_suite()); // srunner_add_suite(sr, conv_i16_i12_suite()); @@ -77,10 +94,10 @@ int main(int argc, char** argv) srunner_add_suite(sr, conv_ci12_4cf32_suite()); // #else - sr = srunner_create(wvlt_sincos_i16_suite()); - //srunner_add_suite(sr, conv_2ci16_ci16_suite()); - //srunner_add_suite(sr, conv_f32_i12_suite()); - //srunner_add_suite(sr, conv_2cf32_ci12_suite()); + sr = srunner_create( conv_3cf32_ci16_suite()); + srunner_add_suite(sr, conv_ci16_3cf32_suite()); + srunner_add_suite(sr, conv_3ci16_ci16_suite()); + srunner_add_suite(sr, conv_ci16_3ci16_suite()); #endif srunner_set_fork_status (sr, CK_NOFORK); srunner_run_all(sr, (argc > 1) ? CK_VERBOSE : CK_NORMAL); diff --git a/src/lib/xdsp/utests/xfft_fftad_utest.c b/src/lib/xdsp/utests/xfft_fftad_utest.c index 502893df..ce89f421 100644 --- a/src/lib/xdsp/utests/xfft_fftad_utest.c +++ b/src/lib/xdsp/utests/xfft_fftad_utest.c @@ -9,7 +9,7 @@ #include #include #include "xdsp_utest_common.h" -#include "../fftad_functions.h" +#include "fftad_functions.h" #undef DEBUG_PRINT @@ -17,7 +17,8 @@ static_assert( STREAM_SIZE >= 4096, "STREAM_SIZE should be >= 4096!" ); static const unsigned packet_lens[3] = { 256, 4096, STREAM_SIZE }; -#define SPEED_MEASURE_ITERS 1000000 +#define SPEED_MEASURE_ITERS 10000 +#define AVGS 256 #define EPSILON 1E-4 @@ -35,11 +36,14 @@ static void setup(void) { srand( time(0) ); - posix_memalign((void**)&in, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * STREAM_SIZE); - posix_memalign((void**)&f_mant, ALIGN_BYTES, sizeof(float) * STREAM_SIZE); - posix_memalign((void**)&f_pwr, ALIGN_BYTES, sizeof(int32_t) * STREAM_SIZE); - posix_memalign((void**)&out, ALIGN_BYTES, sizeof(float) * STREAM_SIZE); - posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(float) * STREAM_SIZE); + int res = 0; + + res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * STREAM_SIZE); + res = res ? res : posix_memalign((void**)&f_mant, ALIGN_BYTES, sizeof(float) * STREAM_SIZE); + res = res ? res : posix_memalign((void**)&f_pwr, ALIGN_BYTES, sizeof(int32_t) * STREAM_SIZE); + res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(float) * STREAM_SIZE); + res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(float) * STREAM_SIZE); + ck_assert_int_eq(res, 0); //init input data for(unsigned i = 0; i < STREAM_SIZE; ++i) @@ -148,7 +152,7 @@ END_TEST START_TEST(fftad_speed) { - fprintf(stderr, "\n**** Compare SIMD implementations speed ***\n"); + fprintf(stderr, "\n**** Compare SIMD implementations speed (%d adds + 1 norm within 1 iteration) ***\n", AVGS); const char* fn_name = NULL; fftad_init_function_t fn_init = NULL; @@ -184,8 +188,11 @@ START_TEST(fftad_speed) //measuring uint64_t tk = clock_get_time(); (*fn_init)(&acc, size); - for(unsigned i = 0; i < SPEED_MEASURE_ITERS; ++i) (*fn_add)(&acc, in, size); - (*fn_norm)(&acc, size, 1.0, 0.0, out); + for(unsigned i = 0; i < SPEED_MEASURE_ITERS; ++i) + { + for(unsigned j = 0; j < AVGS; ++j) (*fn_add)(&acc, in, size); + (*fn_norm)(&acc, size, 1.0, 0.0, out); + } uint64_t tk1 = clock_get_time() - tk; fprintf(stderr, "\t%" PRIu64 " us elapsed, %" PRIu64 " ns per 1 cycle, ave speed = %" PRIu64 " cycles/s \n", @@ -198,17 +205,12 @@ END_TEST Suite * fftad_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("xfft_ftad_functions"); - tc_core = tcase_create("XFFT"); - tcase_set_timeout(tc_core, 300); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, fftad_check); - tcase_add_loop_test(tc_core, fftad_speed, 0, 3); - suite_add_tcase(s, tc_core); + Suite* s = suite_create("xfft_ftad_functions"); + + ADD_REGRESS_TEST(s, fftad_check); + ADD_PERF_LOOP_TEST(s, fftad_speed, 300, 0, 3); + return s; } diff --git a/src/lib/xdsp/utests/xfft_rtsa_utest.c b/src/lib/xdsp/utests/xfft_rtsa_utest.c index e892c1f6..1bb67831 100644 --- a/src/lib/xdsp/utests/xfft_rtsa_utest.c +++ b/src/lib/xdsp/utests/xfft_rtsa_utest.c @@ -8,7 +8,7 @@ #include #include #include "xdsp_utest_common.h" -#include "../rtsa_functions.h" +#include "rtsa_functions.h" #undef DEBUG_PRINT @@ -27,7 +27,8 @@ static const unsigned packet_lens[4] = { 512, 1024, 2048, STREAM_SIZE }; #define SPEED_MEASURE_ITERS 256 -#define EPSILON MAX_RTSA_PWR / 10 +#define EPSILON MAX_RTSA_PWR / 10000 +#define MAX_ERRS 10 static const char* last_fn_name = NULL; static generic_opts_t max_opt = OPT_GENERIC; @@ -52,7 +53,7 @@ static void setup(void) int res = 0; res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, sizeof(wvlt_fftwf_complex) * STREAM_SIZE * AVGS); res = res ? res : posix_memalign((void**)&in16, ALIGN_BYTES, sizeof(uint16_t) * STREAM_SIZE * AVGS); - assert(res == 0); + ck_assert_int_eq(res, 0); //init input data srand( time(0) ); @@ -88,7 +89,7 @@ static void setup(void) res = 0; res = res ? res : posix_memalign((void**)&out, ALIGN_BYTES, sizeof(rtsa_pwr_t) * STREAM_SIZE * st->rtsa_depth); res = res ? res : posix_memalign((void**)&out_etalon, ALIGN_BYTES, sizeof(rtsa_pwr_t) * STREAM_SIZE * st->rtsa_depth); - assert(res == 0); + ck_assert_int_eq(res, 0); memset(out , 0, sizeof(rtsa_pwr_t) * STREAM_SIZE * st->rtsa_depth); memset(out_etalon, 0, sizeof(rtsa_pwr_t) * STREAM_SIZE * st->rtsa_depth); @@ -107,9 +108,13 @@ static void teardown(void) static int32_t is_equal() { + int errs = 0; + for(unsigned i = 0; i < STREAM_SIZE * rtsa_settings.rtsa_depth; i++) { - if(abs(out[i] - out_etalon[i]) > EPSILON) return i; + errs += (abs(out[i] - out_etalon[i]) > EPSILON); + if(errs > MAX_ERRS) + return i; } return -1; } @@ -168,8 +173,6 @@ START_TEST(rtsa_check) fprintf(stderr, "%sTEST > i:%u in=(%.6f,%.6f) out=%u <---> out_etalon=%u\n", j == res ? ">>>>>>>>> " : "", j, in[j][0], in[j][1], out[j], out_etalon[j]); - - exit(1); } #endif ck_assert_int_eq( res, -1 ); @@ -305,24 +308,15 @@ START_TEST(rtsa_speed_u16) END_TEST - - Suite * rtsa_suite(void) { - Suite *s; - TCase *tc_core; - max_opt = cpu_vcap_get(); - s = suite_create("xfft_rtsa_functions"); - tc_core = tcase_create("XFFT"); - tcase_set_timeout(tc_core, 300); - tcase_add_unchecked_fixture(tc_core, setup, teardown); - tcase_add_test(tc_core, rtsa_check); - //tcase_add_loop_test(tc_core, rtsa_speed, 0, 4); - tcase_add_loop_test(tc_core, rtsa_speed_u16, 0, 4); - suite_add_tcase(s, tc_core); - return s; -} + Suite* s = suite_create("xfft_rtsa_functions"); + ADD_REGRESS_TEST(s, rtsa_check); + //ADD_PERF_LOOP_TEST(s, rtsa_speed, 300, 0, 4); + ADD_PERF_LOOP_TEST(s, rtsa_speed_u16, 300, 0, 4); + return s; +} diff --git a/src/lib/xdsp/vbase.c b/src/lib/xdsp/vbase.c index 86957a62..361b80f6 100644 --- a/src/lib/xdsp/vbase.c +++ b/src/lib/xdsp/vbase.c @@ -19,14 +19,16 @@ generic_opts_t cpu_vcap_obtain(unsigned flags) cap = OPT_SSE41; #else - unsigned max_cpu = OPT_AVX512BW; + unsigned max_cpu = OPT_AVX512VBMI; if (flags & CVF_LIMIT_VCPU) { max_cpu = (flags & 0xffff); } __builtin_cpu_init(); - if (__builtin_cpu_supports("avx512bw") && max_cpu >= OPT_AVX512BW) + if (__builtin_cpu_supports("avx512vbmi") && max_cpu >= OPT_AVX512VBMI) + cap = OPT_AVX512VBMI; + else if (__builtin_cpu_supports("avx512bw") && max_cpu >= OPT_AVX512BW) cap = OPT_AVX512BW; else if (__builtin_cpu_supports("avx2") && max_cpu >= OPT_AVX2) cap = OPT_AVX2; @@ -67,6 +69,8 @@ void cpu_vcap_str(char* buffer, unsigned buflen, generic_opts_t caps) case OPT_AVX: type = "AVX"; break; case OPT_AVX2: type = "AVX2"; break; case OPT_AVX512BW: type = "AVX512BW"; break; + case OPT_AVX512VBMI: type = "AVX512VBMI"; break; + case OPT_NEON: type = "ARM_NEON"; break; } @@ -84,6 +88,7 @@ unsigned cpu_vcap_align(generic_opts_t caps) case OPT_SSE42: return 16; case OPT_AVX: case OPT_AVX2: return 32; + case OPT_AVX512VBMI: case OPT_AVX512BW: return 64; case OPT_NEON: return 16; default: diff --git a/src/lib/xdsp/vbase.h b/src/lib/xdsp/vbase.h index 0c456124..072c7909 100644 --- a/src/lib/xdsp/vbase.h +++ b/src/lib/xdsp/vbase.h @@ -19,6 +19,7 @@ enum generic_opts { OPT_AVX, OPT_AVX2, OPT_AVX512BW, + OPT_AVX512VBMI, //ARM-specific OPT_NEON = 2000, @@ -40,6 +41,8 @@ typedef enum generic_opts generic_opts_t; #include #ifndef __EMSCRIPTEN__ +#define WVLT_AVX512VBMI +#define WVLT_AVX512BW #define WVLT_AVX2 #define WVLT_AVX #define WVLT_SSE4_2 @@ -59,6 +62,19 @@ typedef enum generic_opts generic_opts_t; #endif //WVLT_SIMD_INTEL +#ifdef WVLT_AVX512VBMI +#define SELECT_AVX512VBMI_FN(a, b, fn, cap) do { \ +if (cap >= OPT_AVX512VBMI) {a = &fn; b = VB_STRINGIFY(fn);} } while(0) +#else +#define SELECT_AVX512VBMI_FN(a, b, fn, cap) +#endif + +#ifdef WVLT_AVX512BW +#define SELECT_AVX512BW_FN(a, b, fn, cap) do { \ + if (cap >= OPT_AVX512BW) {a = &fn; b = VB_STRINGIFY(fn);} } while(0) +#else +#define SELECT_AVX512BW_FN(a, b, fn, cap) +#endif #ifdef WVLT_AVX2 #define SELECT_AVX2_FN(a, b, fn, cap) do { \ diff --git a/src/soapysdr/test_usdr_soapy.c b/src/soapysdr/test_usdr_soapy.c index b30967f6..2670e304 100644 --- a/src/soapysdr/test_usdr_soapy.c +++ b/src/soapysdr/test_usdr_soapy.c @@ -1,33 +1,50 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT -#include -#include -#include + #include //printf #include //free #include #include +#include +#include +#include #define MAX_CHANS 16 -#define MAX_PACKETSIZE 4096 +#define MAX_PACKETSIZE 1024 * 1024 + +void decToString(int val, char* buf) +{ + snprintf(buf, 31, "%d", val); +} + +float buff[2 * MAX_CHANS * MAX_PACKETSIZE]; int main(int argc, char** argv) { int opt; const char* device = ""; unsigned channels = 1; + unsigned packetSize = 131072; + unsigned samplerate = 4e6; + double rxFreq = 912.3e6; size_t act_channels[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; - while ((opt = getopt(argc, argv, "d:c:")) != -1) { + while ((opt = getopt(argc, argv, "d:c:i:r:")) != -1) { switch (opt) { + case 'r': + samplerate = atof(optarg); + break; case 'd': device = optarg; break; case 'c': channels = atoi(optarg); break; + case 'i': + packetSize = atoi(optarg); + break; } } @@ -36,6 +53,11 @@ int main(int argc, char** argv) exit(1); } + if (packetSize == 0 || packetSize > MAX_PACKETSIZE) { + printf("Incorrect packet size!\n"); + exit(1); + } + size_t length; //enumerate devices @@ -94,11 +116,13 @@ int main(int argc, char** argv) free(ranges); //apply settings - if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_RX, 0, 1e6) != 0) { + if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_RX, 0, samplerate) != 0) { printf("setSampleRate fail: %s\n", SoapySDRDevice_lastError()); + exit(1); } - if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_RX, 0, 912.3e6, NULL) != 0) { + if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_RX, 0, rxFreq, NULL) != 0) { printf("setFrequency fail: %s\n", SoapySDRDevice_lastError()); + exit(1); } ranges = SoapySDRDevice_getFrequencyRangeComponent(sdr, SOAPY_SDR_RX, 0, "BB", &length); @@ -110,18 +134,25 @@ int main(int argc, char** argv) free(ranges); //setup a stream (complex floats) + char packetSizeStr[32]; + decToString(packetSize, packetSizeStr); + SoapySDRKwargs streamArgs = {}; + SoapySDRKwargs_set(&streamArgs, "bufferLength", packetSizeStr); + SoapySDRStream *rxStream; #if (SOAPY_SDR_API_VERSION < 0x00080000) - if (SoapySDRDevice_setupStream(sdr, &rxStream, SOAPY_SDR_RX, SOAPY_SDR_CF32, act_channels, channels, NULL) != 0) { + if (SoapySDRDevice_setupStream(sdr, &rxStream, SOAPY_SDR_RX, SOAPY_SDR_CF32, act_channels, channels, &streamArgs) != 0) { #else - if ((rxStream = SoapySDRDevice_setupStream(sdr, SOAPY_SDR_RX, SOAPY_SDR_CF32, act_channels, channels, NULL)) != NULL) { + if ((rxStream = SoapySDRDevice_setupStream(sdr, SOAPY_SDR_RX, SOAPY_SDR_CF32, act_channels, channels, &streamArgs)) != NULL) { #endif printf("setupStream fail: %s\n", SoapySDRDevice_lastError()); + exit(1); } SoapySDRDevice_activateStream(sdr, rxStream, 0, 0, 0); //start streaming + SoapySDRKwargs_clear(&streamArgs); + //create a re-usable buffer for rx samples - float buff[2 * MAX_CHANS * MAX_PACKETSIZE]; void* buffs[MAX_CHANS]; for (unsigned j = 0; j < MAX_CHANS; j++) { buffs[j] = &buff[2 * j * MAX_PACKETSIZE]; @@ -131,7 +162,7 @@ int main(int argc, char** argv) for (size_t i = 0; i < 10; i++) { int flags; //flags set by receive operation long long timeNs; //timestamp for receive buffer - int ret = SoapySDRDevice_readStream(sdr, rxStream, buffs, MAX_PACKETSIZE, &flags, &timeNs, 100000); + int ret = SoapySDRDevice_readStream(sdr, rxStream, buffs, packetSize, &flags, &timeNs, 100000); printf("ret=%d, flags=%d, timeNs=%lld\n", ret, flags, timeNs); } diff --git a/src/soapysdr/usdr_soapy.cpp b/src/soapysdr/usdr_soapy.cpp index ca1061d6..7d816875 100644 --- a/src/soapysdr/usdr_soapy.cpp +++ b/src/soapysdr/usdr_soapy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2023-2024 Wavelet Lab +// Copyright (c) 2023-2025 Wavelet Lab // SPDX-License-Identifier: MIT #include "usdr_soapy.h" @@ -55,7 +55,7 @@ struct rfic_gain_descriptor const char* property_name; }; -const rfic_gain_descriptor lms7_gains[] { +static const rfic_gain_descriptor lms7_gains[] { { SOAPY_SDR_RX, SoapySDR::Range(0.0, 30.0), "LNA", nullptr, nullptr, "/dm/sdr/0/rx/gain/lna" }, { SOAPY_SDR_RX, SoapySDR::Range(0.0, 12.0), "TIA", "VGA", "VGA1", "/dm/sdr/0/rx/gain/vga" }, { SOAPY_SDR_RX, SoapySDR::Range(-12.0, 19.0), "PGA", "VGA2", nullptr, "/dm/sdr/0/rx/gain/pga" }, @@ -63,7 +63,7 @@ const rfic_gain_descriptor lms7_gains[] { { 0, SoapySDR::Range(), nullptr, nullptr, nullptr, nullptr } }; -const rfic_gain_descriptor lms6_gains[] { +static const rfic_gain_descriptor lms6_gains[] { { SOAPY_SDR_RX, SoapySDR::Range(0.0, 6.0), "LNA", nullptr, nullptr, "/dm/sdr/0/rx/gain/lna" }, { SOAPY_SDR_RX, SoapySDR::Range(5, 31), "VGA1", "TIA", nullptr, "/dm/sdr/0/rx/gain/vga" }, { SOAPY_SDR_RX, SoapySDR::Range(0, 60.0), "VGA2", "PGA", nullptr, "/dm/sdr/0/rx/gain/pga" }, @@ -72,14 +72,14 @@ const rfic_gain_descriptor lms6_gains[] { { 0, SoapySDR::Range(), nullptr, nullptr, nullptr, nullptr } }; -const rfic_gain_descriptor ad45lb49_gains[] { +static const rfic_gain_descriptor ad45lb49_gains[] { { SOAPY_SDR_RX, SoapySDR::Range(0, 4), "LNA", "SEL", nullptr, "/dm/sdr/0/rx/gain/lna" }, { SOAPY_SDR_RX, SoapySDR::Range(0, 31), "VGA", "ATTN", nullptr, "/dm/sdr/0/rx/gain/vga" }, { SOAPY_SDR_RX, SoapySDR::Range(0, 31), "PGA", nullptr, nullptr, "/dm/sdr/0/rx/gain/pga" }, { 0, SoapySDR::Range(), nullptr, nullptr, nullptr, nullptr } }; -const rfic_gain_descriptor unk_gains[] { +static const rfic_gain_descriptor unk_gains[] { { SOAPY_SDR_RX, SoapySDR::Range(-99, 99), "GRX", nullptr, nullptr, "/dm/sdr/0/rx/gain" }, { SOAPY_SDR_TX, SoapySDR::Range(-99, 99), "GTX", nullptr, nullptr, "/dm/sdr/0/tx/gain" }, { 0, SoapySDR::Range(), nullptr, nullptr, nullptr, nullptr } @@ -94,6 +94,67 @@ static inline const rfic_gain_descriptor* get_gains(rfic_type_t t) { } } +struct device_ranges +{ + SoapySDR::Range frequency_range; + SoapySDR::Range samplerate_range; + SoapySDR::Range bandwidth_range; +}; + +static const device_ranges usdr_ranges { + .frequency_range = SoapySDR::Range(0.1e6, 3800e6), + .samplerate_range = SoapySDR::Range(1e6, 85e6), + .bandwidth_range = SoapySDR::Range(0.5e6, 40e6), +}; + +static const device_ranges xsdr_ranges { + .frequency_range = SoapySDR::Range(0.1e6, 3800e6), + .samplerate_range = SoapySDR::Range(1e6, 125e6), + .bandwidth_range = SoapySDR::Range(0.5e6, 125e6), +}; + +static const device_ranges ssdr_ranges { + .frequency_range = SoapySDR::Range(0.1e6, 12500e6), + .samplerate_range = SoapySDR::Range(4e6, 125e6), + .bandwidth_range = SoapySDR::Range(0.5e6, 125e6), +}; + +static const device_ranges dsdr_ranges { + .frequency_range = SoapySDR::Range(5e6, 12500e6), + .samplerate_range = SoapySDR::Range(4e6, 500e6), + .bandwidth_range = SoapySDR::Range(0.5e6, 500e6), +}; + +static const device_ranges lsdr_ranges { + .frequency_range = SoapySDR::Range(0.1e6, 3800e6), + .samplerate_range = SoapySDR::Range(2e6, 125e6), + .bandwidth_range = SoapySDR::Range(0.5e6, 125e6), +}; + +static const device_ranges limesdr_mini_ranges { + .frequency_range = SoapySDR::Range(30e6, 3800e6), + .samplerate_range = SoapySDR::Range(0.1e6, 40e6), + .bandwidth_range = SoapySDR::Range(0.5e6, 40e6), +}; + +static const device_ranges unk_ranges { + .frequency_range = SoapySDR::Range(30e6, 3800e6), + .samplerate_range = SoapySDR::Range(1e6, 40e6), + .bandwidth_range = SoapySDR::Range(0.5e6, 40e6), +}; + +static inline const device_ranges* get_ranges(device_type_t t) { + switch (t) { + case DEVICE_USDR: return &usdr_ranges; + case DEVICE_XSDR: return &xsdr_ranges; + case DEVICE_SSDR: return &ssdr_ranges; + case DEVICE_DSDR: return &dsdr_ranges; + case DEVICE_LSDR: return &lsdr_ranges; + case DEVICE_LIMESDR_MINI: return &limesdr_mini_ranges; + default: return &unk_ranges; + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const char* SoapyUSDR::get_sdr_param(int sdridx, const char* dir, const char* par, const char* subpar) @@ -106,6 +167,19 @@ const char* SoapyUSDR::get_sdr_param(int sdridx, const char* dir, const char* pa return _param_name; } +const char* SoapyUSDR::get_sdr_param_chan(int sdridx, const char* dir, const char* par, const char* subpar, unsigned chan) +{ + if (max_sw_rx_chans <= 1 && max_sw_tx_chans <= 1) + return get_sdr_param(sdridx, dir, par, subpar); + + if (subpar) { + snprintf(_param_name, sizeof(_param_name), "/dm/sdr/%d/%s/%s/%s/%d", sdridx, dir, par, subpar, chan); + } else { + snprintf(_param_name, sizeof(_param_name), "/dm/sdr/%d/%s/%s/%d", sdridx, dir, par, chan); + } + return _param_name; +} + SoapyUSDR::SoapyUSDR(const SoapySDR::Kwargs &args_orig) @@ -136,25 +210,43 @@ SoapyUSDR::SoapyUSDR(const SoapySDR::Kwargs &args_orig) const SoapySDR::Kwargs &args = (env_str.length() > 0) ? env_args : args_orig; - std::string dev = (args.count("dev")) ? args.at("dev") : ""; - if (args.count("loglevel")) { loglevel = std::stoi(args.at("loglevel")); } + + SoapySDR::Kwargs dev_args = SoapySDR:: + KwargsFromString((args.count("dev")) ? args.at("dev") : ""); + + if (args.count("bus")) { + dev_args["bus"] = args.at("bus"); + } + + if (args.count("device")) { + dev_args["device"] = args.at("device"); + } + if (args.count("fe")) { - if (dev.length() != 0) { - dev += ","; - } - dev += "fe="; - dev += args.at("fe").c_str(); + dev_args["fe"] = args.at("fe"); } - if (args.count("bus")) { - if (dev.length() > 0) { + + if (args.count("extclk")) { + dev_args["extclk"] = args.at("extclk"); + } + + if (args.count("extref")) { + dev_args["extref"] = args.at("extref"); + } + + bool first = true; + std::string dev = ""; + for (const auto &dev_arg : dev_args) { + if (first) + first = false; + else dev += ","; - } - dev += "bus="; - dev += args.at("bus").c_str(); + dev += dev_arg.first + "=" + dev_arg.second; } + if (args.count("txcorr")) { _txcorr = atoi(args.at("txcorr").c_str()); } @@ -164,11 +256,11 @@ SoapyUSDR::SoapyUSDR(const SoapySDR::Kwargs &args_orig) usdrlog_setlevel(NULL, loglevel); - - SoapySDR::logf(callLogLvl(), "Make connection: '%s'", args.count("dev") ? args.at("dev").c_str() : "*"); + SoapySDR::logf(SOAPY_SDR_DEBUG, "Make connection: '%s'", args.count("dev") ? args.at("dev").c_str() : "*"); for (auto& i: args) { - SoapySDR::logf(callLogLvl(), "Param %s => %s", i.first.c_str(), i.second.c_str()); + SoapySDR::logf(SOAPY_SDR_DEBUG, "Param %s => %s", i.first.c_str(), i.second.c_str()); } + _dev = usdr_handle::get(dev); if (args.count("refclk")) { @@ -198,7 +290,14 @@ SoapyUSDR::SoapyUSDR(const SoapySDR::Kwargs &args_orig) } uint64_t val; - int res = usdr_dme_get_uint(_dev->dev(), "/ll/sdr/0/rfic/0", &val); + int res; + + usdr_dme_get_uint(_dev->dev(), "/ll/sdr/max_hw_rx_chans", &max_hw_rx_chans); + usdr_dme_get_uint(_dev->dev(), "/ll/sdr/max_hw_tx_chans", &max_hw_tx_chans); + usdr_dme_get_uint(_dev->dev(), "/ll/sdr/max_sw_rx_chans", &max_sw_rx_chans); + usdr_dme_get_uint(_dev->dev(), "/ll/sdr/max_sw_tx_chans", &max_sw_tx_chans); + + res = usdr_dme_get_uint(_dev->dev(), "/ll/sdr/0/rfic/0", &val); if (res == 0) { const char* rfic = reinterpret_cast(val); if (strcmp(rfic, "lms6002d") == 0) @@ -211,6 +310,24 @@ SoapyUSDR::SoapyUSDR(const SoapySDR::Kwargs &args_orig) type = RFIC_AFE79XX; } + res = usdr_dme_get_uint(_dev->dev(), "/ll/device/name", &val); + if (res == 0) { + const char* device = reinterpret_cast(val); + SoapySDR::logf(callLogLvl(), "SoapyUSDR::SoapyUSDR() Device name is \"%s\"", device); + if (strcmp(device, "usdr") == 0) + device_type = DEVICE_USDR; + else if (strcmp(device, "xsdr") == 0) + device_type = DEVICE_XSDR; + else if (strcmp(device, "ssdr") == 0) + device_type = DEVICE_SSDR; + else if (strcmp(device, "dsdr") == 0) + device_type = DEVICE_DSDR; + else if (strcmp(device, "lsdr") == 0) + device_type = DEVICE_LSDR; + else if (strcmp(device, "limemini") == 0) + device_type = DEVICE_LIMESDR_MINI; + } + if (args.count("rx_bw")) { unsigned bw = atoi(args.at("rx_bw").c_str()); SoapySDR::logf(callLogLvl(), "SoapyUSDR::SoapyUSDR() RX_BW set to %d", bw); @@ -394,7 +511,7 @@ void SoapyUSDR::setGain(const int direction, const size_t channel, const double SoapySDR::logf(callLogLvl(), "SoapyUSDR::setGain(%s, %d, %g dB)", dir, int(channel), value); std::unique_lock lock(_dev->accessMutex); - const char* defparam = get_sdr_param(0, dir, "gain", "auto"); + const char* defparam = get_sdr_param_chan(0, dir, "gain", "auto", channel); int res = usdr_dme_set_uint(_dev->dev(), defparam, value); if (res) { SoapySDR::logf(callLogLvl(), "SoapyUSDR::setGain(%s, %d, %g dB) => %s failed %d", @@ -411,7 +528,7 @@ void SoapyUSDR::setGain(const int direction, const size_t channel, const std::st const rfic_gain_descriptor* gains = get_gains(type); std::unique_lock lock(_dev->accessMutex); - const char* defparam = get_sdr_param(0, dir, "gain", nullptr); + const char* defparam = get_sdr_param_chan(0, dir, "gain", nullptr, channel); unsigned i; for (i = 0; gains[i].name != nullptr; i++) { @@ -494,16 +611,14 @@ void SoapyUSDR::setFrequency(const int direction, const size_t channel, const st int res; const char* dir = (direction == SOAPY_SDR_TX) ? "tx" : "rx"; - const char* pname = get_sdr_param(0, dir, "freqency", (name == "BB") ? "bb" : NULL); + const char* pname = get_sdr_param_chan(0, dir, "frequency", (name == "BB") ? "bb" : NULL, channel); - uint64_t val = (((uint64_t)channel) << 32) | (uint32_t)frequency; - - res = usdr_dme_set_uint(_dev->dev(), pname, - type == RFIC_AFE79XX ? (uint64_t)frequency : val); + res = usdr_dme_set_uint(_dev->dev(), pname, (uint64_t)(int64_t)frequency); if (res) - throw std::runtime_error(std::string("SoapyUSDR::setFrequency(") + pname + ", " + ")"); + throw std::runtime_error(std::string("SoapyUSDR::setFrequency(") + pname + ", " + std::to_string(frequency) + ")"); - _actual_frequency[direction] = val; + // TODO: refactor this + _actual_frequency[direction] = frequency; } double SoapyUSDR::getFrequency(const int direction, const size_t channel, const std::string &name) const @@ -528,11 +643,10 @@ SoapySDR::RangeList SoapyUSDR::getFrequencyRange(const int /*direction*/, const SoapySDR::RangeList ranges; if (name == "RF") { - if (type == RFIC_AFE79XX) { - ranges.push_back(SoapySDR::Range(5e6, 12.5e9)); - } else { - ranges.push_back(SoapySDR::Range(1e5, 3.8e9)); - } + const device_ranges *dev_ranges = get_ranges(device_type); + if (!dev_ranges) + return ranges; + ranges.push_back(dev_ranges->frequency_range); } else if (name == "BB") { @@ -548,7 +662,9 @@ SoapySDR::RangeList SoapyUSDR::getFrequencyRange(const int /*direction*/, const SoapySDR::RangeList SoapyUSDR::getFrequencyRange(const int /*direction*/, const size_t /*channel*/) const { SoapySDR::RangeList ranges; - if (type == RFIC_AFE79XX) { + if (device_type == DEVICE_SSDR) { + ranges.push_back(SoapySDR::Range(30e6, 11.5e9)); + } else if (type == RFIC_AFE79XX) { ranges.push_back(SoapySDR::Range(5e6, 12.5e9)); } else { ranges.push_back(SoapySDR::Range(0e6, 3.8e9)); @@ -613,17 +729,24 @@ double SoapyUSDR::getSampleRate(const int direction, const size_t /*channel*/) c SoapySDR::RangeList SoapyUSDR::getSampleRateRange(const int /*direction*/, const size_t /*channel*/) const { SoapySDR::RangeList ranges; - ranges.push_back(SoapySDR::Range((type == RFIC_AFE79XX) ? 1.92e6 : 0.1e6, - (type == RFIC_AFE79XX) ? 500e6 : (type == RFIC_AD45LB49) ? 130e6 : 80e6)); + const device_ranges *dev_ranges = get_ranges(device_type); + if (!dev_ranges) + return ranges; + ranges.push_back(dev_ranges->samplerate_range); return ranges; } std::vector SoapyUSDR::listSampleRates(const int /*direction*/, const size_t /*channel*/) const { std::vector rates; - for (int i = 2; i < 57; i++) + const device_ranges *dev_ranges = get_ranges(device_type); + if (!dev_ranges) + return rates; + const int min_sr = (int) (dev_ranges->bandwidth_range.minimum() / 1e6); + const int max_sr = (int) (dev_ranges->bandwidth_range.maximum() / 1e6); + for (int i = min_sr; i < max_sr; ++i) { - rates.push_back(i*1e6); + rates.push_back(i * 1e6); } return rates; } @@ -647,7 +770,7 @@ void SoapyUSDR::setBandwidth(const int direction, const size_t channel, const do if (bw == 0.0) return; //special ignore value const char* dir = (direction == SOAPY_SDR_TX) ? "tx" : "rx"; - const char* pname = get_sdr_param(0, dir, "bandwidth", NULL); + const char* pname = get_sdr_param_chan(0, dir, "bandwidth", NULL, channel); int res; SoapySDR::logf(callLogLvl(), "SoapyUSDR::setBandwidth(%s, %g MHz)",dir, bw/1e6); @@ -669,7 +792,10 @@ double SoapyUSDR::getBandwidth(const int direction, const size_t /*channel*/) co SoapySDR::RangeList SoapyUSDR::getBandwidthRange(const int /*direction*/, const size_t /*channel*/) const { SoapySDR::RangeList bws; - bws.push_back(SoapySDR::Range(0.5e6, 80e6)); + const device_ranges *dev_ranges = get_ranges(device_type); + if (!dev_ranges) + return bws; + bws.push_back(dev_ranges->bandwidth_range); return bws; } @@ -757,6 +883,11 @@ void SoapyUSDR::setHardwareTime(const long long timeNs, const std::string &what) SoapySDR::logf(callLogLvl(), "SoapyUSDR::setHardwareTime(%lld)", timeNs); } +std::vector SoapyUSDR::listTimeSources(void) const +{ + return { "internal", "external" }; +} + /******************************************************************* * Sensor API ******************************************************************/ @@ -765,6 +896,7 @@ std::vector SoapyUSDR::listSensors(void) const { std::vector sensors; sensors.push_back("clock_locked"); + sensors.push_back("ref_locked"); sensors.push_back("board_temp"); return sensors; } @@ -780,6 +912,14 @@ SoapySDR::ArgInfo SoapyUSDR::getSensorInfo(const std::string &name) const info.value = "false"; info.description = "CGEN clock is locked, good VCO selection."; } + else if (name == "ref_locked") + { + info.key = "ref_locked"; + info.name = "Reference Locked"; + info.type = SoapySDR::ArgInfo::BOOL; + info.value = "false"; + info.description = "Reference clock is locked."; + } else if (name == "board_temp") { info.key = "board_temp"; @@ -799,6 +939,10 @@ std::string SoapyUSDR::readSensor(const std::string &name) const { return "true"; } + else if (name == "ref_locked") + { + return "true"; + } else if (name == "board_temp") { uint64_t val; @@ -832,6 +976,7 @@ SoapySDR::ArgInfo SoapyUSDR::getSensorInfo(const int /*direction*/, const size_t info.value = "false"; info.description = "LO synthesizer is locked, good VCO selection."; } + return info; } @@ -1080,6 +1225,14 @@ SoapySDR::Stream *SoapyUSDR::setupStream( _streams[direction].chmsk = chmsk; _streams[direction].stream = direction == SOAPY_SDR_RX ? "/ll/srx/0" : "/ll/stx/0"; + if (_actual_rx_rate == 0) { + const device_ranges *dev_ranges = get_ranges(device_type); + if (dev_ranges) + setSampleRate(SOAPY_SDR_RX, 0, dev_ranges->samplerate_range.minimum()); + else + setSampleRate(SOAPY_SDR_RX, 0, 5e6); + } + if (direction == SOAPY_SDR_RX) { // We need a better way to calculate packet size unsigned defbufsz = @@ -1112,10 +1265,6 @@ SoapySDR::Stream *SoapyUSDR::setupStream( SoapySDR::logf(callLogLvl(), "SoapyUSDR::setupStream(%s) %d Samples per packet, burst size %d * %d chs; res = %d", ustr->stream, numElems, ustr->nfo.pktsyms, ustr->nfo.channels, res); - if (_actual_rx_rate == 0) { - setSampleRate(SOAPY_SDR_RX, 0, 1.92e6); - } - res = usdr_dms_sync(_dev->dev(), "off", 1, &ustr->strm); if (res) { throw std::runtime_error("SoapyUSDR::setupStream failed!"); @@ -1296,17 +1445,21 @@ int SoapyUSDR::readStream( } } -int SoapyUSDR::writeStream( - SoapySDR::Stream *stream, - const void * const *buffs, - const size_t numElems, - int &flags, - const long long timeNs, - const long timeoutUs) +int SoapyUSDR::writeStream(SoapySDR::Stream *stream, + const void *const *buffs, + const size_t numElems, + int &flags, + const long long timeNs, + const long timeoutUs) { - USDRStream* ustr = (USDRStream*)(stream); - long long ts = (flags & SOAPY_SDR_HAS_TIME) ? - SoapySDR::timeNsToTicks(timeNs, _actual_tx_rate) + _txcorr : -1; + USDRStream *ustr = (USDRStream *) (stream); + long long ts; + if (flags & SOAPY_SDR_HAS_TIME) { + ts = SoapySDR::timeNsToTicks(timeNs, _actual_tx_rate) + _txcorr; + this->calc_ts = ts; + } else { + ts = this->calc_ts; + } int64_t lag = ts - last_recv_pkt_time; if (tx_pkts == 0) { @@ -1319,7 +1472,9 @@ int SoapyUSDR::writeStream( SoapySDR::logf(SOAPY_SDR_DEBUG, "writeStream::writeStream(%s) @ %lld num %d should be %d\n", ustr->stream, ts, numElems, ustr->nfo.pktsyms); unsigned toSend = numElems; - int res = usdr_dms_send(ustr->strm, (const void **)buffs, numElems, ts, timeoutUs / 1000); + int res = usdr_dms_send(ustr->strm, (const void **) buffs, numElems, ts, timeoutUs / 1000); + if (this->calc_ts >= 0) + this->calc_ts += numElems; if (tx_pkts % 1000 == 0) { SoapySDR::logf(_dump_calls ? SOAPY_SDR_ERROR : SOAPY_SDR_TRACE, diff --git a/src/soapysdr/usdr_soapy.h b/src/soapysdr/usdr_soapy.h index 890ceeab..190f7669 100644 --- a/src/soapysdr/usdr_soapy.h +++ b/src/soapysdr/usdr_soapy.h @@ -26,6 +26,16 @@ enum rfic_type_t { RFIC_UNKNOWN }; +enum device_type_t { + DEVICE_USDR, + DEVICE_XSDR, + DEVICE_SSDR, + DEVICE_DSDR, + DEVICE_LSDR, + DEVICE_LIMESDR_MINI, + DEVICE_UNKNOWN, +}; + class usdr_handle { public: @@ -241,6 +251,8 @@ class SoapyUSDR : public SoapySDR::Device void setHardwareTime(const long long timeNs, const std::string &what = ""); + std::vector listTimeSources(void) const; + /******************************************************************* * Sensor API ******************************************************************/ @@ -313,8 +325,7 @@ class SoapyUSDR : public SoapySDR::Device }; const char* get_sdr_param(int sdridx, const char* dir, const char* par, const char* subpar); - - enum { MAX_CHANNELS = 2 }; + const char* get_sdr_param_chan(int sdridx, const char* dir, const char* par, const char* subpar, unsigned chan); std::shared_ptr _dev; char _param_name[128]; @@ -338,6 +349,11 @@ class SoapyUSDR : public SoapySDR::Device double avg_gap; + uint64_t max_hw_rx_chans = 1; + uint64_t max_hw_tx_chans = 1; + uint64_t max_sw_rx_chans = 1; + uint64_t max_sw_tx_chans = 1; + // Stats uint64_t rx_pkts; uint64_t tx_pkts; @@ -345,6 +361,8 @@ class SoapyUSDR : public SoapySDR::Device FILE* rd; rfic_type_t type = RFIC_UNKNOWN; + device_type_t device_type = DEVICE_UNKNOWN; + double _actual_bandwidth[2] = { 0, 0 }; double _actual_frequency[2] = { 0, 0 }; @@ -352,6 +370,8 @@ class SoapyUSDR : public SoapySDR::Device int _txcorr = 0; + int64_t calc_ts = -1LL; + std::string _clk_source = "internal"; }; diff --git a/src/tests/lms7_cal.c b/src/tests/lms7_cal.c index 0a56fb53..a0dd360b 100644 --- a/src/tests/lms7_cal.c +++ b/src/tests/lms7_cal.c @@ -162,14 +162,14 @@ int dev_initialize(const char* devstring, unsigned spb, unsigned loglevel, float fprintf(stderr, "Configured %d x %d buffs\n", blksz, bufcnt); - res = usdr_dme_set_uint(dev, usdr_dmd_find_entity(dev, "/dm/sdr/0/rx/freqency"), freq); + res = usdr_dme_set_uint(dev, usdr_dmd_find_entity(dev, "/dm/sdr/0/rx/frequency"), freq); if (res) { fprintf(stderr, "Unable to set rx frequency: errno %d\n", res); goto dev_close; } if (freq_tx > 0) { - res = usdr_dme_set_uint(dev, usdr_dmd_find_entity(dev, "/dm/sdr/0/tx/freqency"), freq_tx); + res = usdr_dme_set_uint(dev, usdr_dmd_find_entity(dev, "/dm/sdr/0/tx/frequency"), freq_tx); if (res) { fprintf(stderr, "Unable to set tx frequency: errno %d\n", res); goto dev_close; diff --git a/src/tests/lowlevel_lvds_perf.c b/src/tests/lowlevel_lvds_perf.c index b64187b4..ed3aaf25 100644 --- a/src/tests/lowlevel_lvds_perf.c +++ b/src/tests/lowlevel_lvds_perf.c @@ -479,7 +479,7 @@ int main(int argc, char** argv) usdrlog_setlevel(NULL, USDR_LOG_TRACE); usdrlog_enablecolorize(NULL); unsigned iten = 100; // Iterate over smaplerate - unsigned cont = 0; // Continous test + unsigned cont = 0; // Continuous test unsigned longtest = 0; unsigned loglevel = 2; unsigned verbosity = 1; diff --git a/src/tests/simpleapi.c b/src/tests/simpleapi.c index ac041a3c..196437a3 100644 --- a/src/tests/simpleapi.c +++ b/src/tests/simpleapi.c @@ -54,7 +54,7 @@ void Initialize(const char* device_string, int loglevel, CheckErrorAndDie(res, "rx info"); res = usdr_dms_sync(pdev->dev, "off", 2, pdev->strms); - CheckErrorAndDie(res, "tx & rx syncronization off"); + CheckErrorAndDie(res, "tx & rx synchronization off"); res = usdr_dms_op(pdev->strms[0], USDR_DMS_START, 0); CheckErrorAndDie(res, "rx stream precharge"); @@ -179,14 +179,14 @@ void SetRxPgaGain(sdr_data_t* pdev, int gain) void SetTxFreq(sdr_data_t* pdev, unsigned freq) { int res; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/freqency", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/frequency", freq); CheckErrorAndDie(res, "SetTxFreq"); } void SetRxFreq(sdr_data_t* pdev, unsigned freq) { int res; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/freqency", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/frequency", freq); CheckErrorAndDie(res, "SetRxFreq"); } @@ -195,7 +195,7 @@ void SetBBTxFreq(sdr_data_t* pdev, int chan, int freq) { int res; uint64_t val = (((uint64_t)chan) << 32) | (uint32_t)freq; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/freqency/bb", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/frequency/bb", val); CheckErrorAndDie(res, "SetBBTxFreq"); } @@ -203,7 +203,7 @@ void SetBBRxFreq(sdr_data_t* pdev, int chan, int freq) { int res; uint64_t val = (((uint64_t)chan) << 32) | (uint32_t)freq; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/freqency/bb", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/frequency/bb", val); CheckErrorAndDie(res, "SetBBRxFreq"); } diff --git a/src/tests/usdr_simple_api_test.c b/src/tests/usdr_simple_api_test.c index 838ba576..b6cda5de 100644 --- a/src/tests/usdr_simple_api_test.c +++ b/src/tests/usdr_simple_api_test.c @@ -120,7 +120,7 @@ void Initialize(const char* device_string, int loglevel, CheckErrorAndDie(res, "rx info"); res = usdr_dms_sync(pdev->dev, "off", 2, pdev->strms); - CheckErrorAndDie(res, "tx & rx syncronization off"); + CheckErrorAndDie(res, "tx & rx synchronization off"); res = usdr_dms_op(pdev->strms[0], USDR_DMS_START, 0); CheckErrorAndDie(res, "rx stream precharge"); @@ -235,14 +235,14 @@ void SetRxPgaGain(sdr_data_t* pdev, int gain) void SetTxFreq(sdr_data_t* pdev, unsigned freq) { int res; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/freqency", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/frequency", freq); CheckErrorAndDie(res, "SetTxFreq"); } void SetRxFreq(sdr_data_t* pdev, unsigned freq) { int res; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/freqency", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/frequency", freq); CheckErrorAndDie(res, "SetRxFreq"); } @@ -251,7 +251,7 @@ void SetBBTxFreq(sdr_data_t* pdev, int chan, int freq) { int res; uint64_t val = (((uint64_t)chan) << 32) | (uint32_t)freq; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/freqency/bb", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/tx/frequency/bb", val); CheckErrorAndDie(res, "SetBBTxFreq"); } @@ -259,7 +259,7 @@ void SetBBRxFreq(sdr_data_t* pdev, int chan, int freq) { int res; uint64_t val = (((uint64_t)chan) << 32) | (uint32_t)freq; - res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/freqency/bb", + res = usdr_dme_set_uint(pdev->dev, "/dm/sdr/0/rx/frequency/bb", val); CheckErrorAndDie(res, "SetBBRxFreq"); } @@ -378,7 +378,7 @@ int main(int argc, char** argv) SetTxBandwidth(&data, tx_bandwidth); SetRxBandwidth(&data, rx_bandwidth); - // Optionall tune frequency offset + // Optionally tune frequency offset // TrimDacVCTCXO(&data, 43981); // Do calibration only when All gains / freqs are set but before actual start diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index fca5deb1..c1075ba2 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -22,7 +22,25 @@ add_executable(usdr_dm_sensors usdr_dm_sensors.c) target_link_libraries(usdr_dm_sensors usdr) install(TARGETS usdr_dm_sensors RUNTIME) +add_executable(usdr_lml7_test lms7002_dm_limelight.c) +target_link_libraries(usdr_lml7_test usdr) + +add_executable(usdr_dm_gpsdo usdr_dm_gpsdo.c) +target_link_libraries(usdr_dm_gpsdo usdr) +install(TARGETS usdr_dm_gpsdo RUNTIME) + list(APPEND WVLT_SCRIPTS wvlt_sdr_wrapper.py + python/usdr_bindings.py + python/example_minimal.py + python/example_fft_rx.py ) configure_file(${WVLT_SCRIPTS} . USE_SOURCE_PERMISSIONS COPYONLY) + +if(WIN32) + set_target_properties(usdr_dm_create PROPERTIES WIN32_EXECUTABLE FALSE) + set_target_properties(usdr_dm_sensors PROPERTIES WIN32_EXECUTABLE FALSE) + set_target_properties(usdr_flash PROPERTIES WIN32_EXECUTABLE FALSE) + set_target_properties(usdr_lml7_test PROPERTIES WIN32_EXECUTABLE FALSE) + set_target_properties(usdr_dm_gpsdo PROPERTIES WIN32_EXECUTABLE FALSE) +endif() diff --git a/src/tools/lms7002_dm_limelight.c b/src/tools/lms7002_dm_limelight.c new file mode 100644 index 00000000..0fd25b4f --- /dev/null +++ b/src/tools/lms7002_dm_limelight.c @@ -0,0 +1,1029 @@ +// Copyright (c) 2023-2024 Wavelet Lab +// SPDX-License-Identifier: MIT + +#define _GNU_SOURCE +#include +#include + +#include +#include +#include +#include "../ipblks/streams/streams.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/ring_buffer.h" +#include "sincos_functions.h" +#include "fast_math.h" + +#define LOG_TAG "LML7" + +#define MAX_CHS 2 +#define MAX_PATTERN 16384 + +static unsigned num_rx = 2; + +static uint16_t s_pattern_r0[2 * MAX_PATTERN]; +static uint16_t s_pattern_r1[2 * MAX_PATTERN]; + +static uint16_t s_pattern_t0[2 * MAX_PATTERN]; +static uint16_t s_pattern_t1[2 * MAX_PATTERN]; + +static size_t d_off = 0; +static uint16_t* d_pattern_r0 = NULL; +static uint16_t* d_pattern_r1 = NULL; + +static const uint32_t s_dtest[] = { + // Single + 0x00000000, + 0xFFFFFFFF, + 0xAAAAAAAA, + 0x55555555, + 0xA5A5A5A5, + 0x5A5A5A5A, + 0x11111111, + 0x44444444, + + // Dual + 0x0000FFFF, + 0x0000AAAA, + 0x00005555, + 0x0000A55A, + 0xAAAAFFFF, + 0xFFFFAAAA, + 0xFFFF5555, + 0xFFFFA55A, + 0xAAAA5555, +}; + +#define DUAL_TESTS SIZEOF_ARRAY(s_dtest) + +static void fill_test(uint16_t* ptr, unsigned test_no) +{ + if (test_no >= DUAL_TESTS) + return; + + uint32_t* pc = (uint32_t*)ptr; + uint32_t c = s_dtest[test_no]; + for (unsigned i = 0; i < MAX_PATTERN; i++) { + pc[i] = c; + } +} + +static uint32_t s_sync = 0; +static uint32_t s_sync_errs = 0; +static uint32_t s_off = 0; +static uint32_t s_resync = 0; +static uint32_t s_sbit_err = 0; +static uint32_t s_mbit_err = 0; +static uint32_t s_bpos_err[16]; + +struct bit_error_stat { + unsigned bit_errors[16]; // Bit error counts + unsigned bit_pos_errors[16]; // Individual bit pos errors +}; +typedef struct bit_error_stat bit_error_stat_t; + +__attribute__((optimize("-O3"))) +static int bitcount(unsigned v) +{ + unsigned c = 0; + for (unsigned i = 32; i != 0; i--) { + if (v & 1) + c++; + v >>= 1; + } + return c; +} + +static void bit_error_init(bit_error_stat_t* p) +{ + for (unsigned i = 0; i < 16; i++) { + p->bit_errors[i] = 0; + p->bit_pos_errors[i] = 0; + } +} + +__attribute__((optimize("-O3"))) +static unsigned bit_error_check(bit_error_stat_t* p, uint16_t a, uint16_t b) +{ + unsigned diff = (a ^ b); + if (diff == 0) { + return 0; + } + + unsigned bcnt = bitcount(diff); + p->bit_errors[bcnt]++; + + for (unsigned i = 0; i != 16; i++) { + if (diff & (1 << i)) { + p->bit_pos_errors[i]++; + } + } + return bcnt; +} + +static void check_test(const uint16_t* data, uint16_t mask, unsigned count, unsigned test_no) +{ + if (test_no >= DUAL_TESTS) + return; + + const uint32_t* pc = (const uint32_t*)data; + uint32_t c = s_dtest[test_no]; + uint16_t p0 = c >> 16; + uint16_t p1 = c; + unsigned j = s_off; + unsigned l = 0; + unsigned errs = 0; + + if (!s_sync) { + for (j = 0; j < count; j++) { + if (data[j] == p0) { + s_sync = 0; + goto synced; + } + s_sync_errs ++; + } + return; + } + +synced: + + for (; j < count; j++, l++) { + uint16_t p = ((l % 2) ? p1 : p0) & mask; + unsigned diff = (p ^ data[j]); + if (diff == 0) { + errs = 0; + continue; + } + + errs++; + if (bitcount(diff) > 1) { + s_mbit_err++; + } else { + s_sbit_err++; + } + + for (unsigned i = 0; i != 16; i--) { + if (diff & (1 << i)) { + s_bpos_err[i]++; + } + } + } + + s_off = l % 2; +} + +enum LML_MODE { + LML_MODE_NORMAL = 0, + LML_MODE_LOOPBACK = 1, + LML_MODE_LFSR = 2, + LML_MODE_LOOPBACK_GEN = 3, +}; + +static pdm_dev_t dev = NULL; +static int mode = 0; +static bool mimo_mode = true; +static const char* fmt_rx = "ci16"; +static const char* fmt_tx = "ci16"; + +static unsigned samples_rx = MAX_PATTERN; +static unsigned samples_tx = MAX_PATTERN; +static pusdr_dms_t usds_rx = NULL; +static pusdr_dms_t usds_tx = NULL; +static pusdr_dms_t strms[2] = { NULL, NULL }; +static FILE* files_r[2] = { NULL, NULL }; + +static float specific_rate = 10.0e6; +static unsigned maximum_iterations = 10; +static bool memcached = false; // Store data in RAM before doing processing + +static void dev_exit() +{ + exit(EXIT_FAILURE); +} + +static void dev_set_rate(unsigned rate) +{ + int res; + unsigned lmode = mode; + + for (unsigned k = 0; ; k ++) { + res = !dev ? 0 : usdr_dmr_rate_set(dev, NULL, rate); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device rate: errno %d", res); + + if (k == 10) + dev_exit(); + } else { + break; + } + } + + if (lmode == LML_MODE_LOOPBACK_GEN) { + lmode = LML_MODE_LOOPBACK; + } + + res = !dev ? 0 : usdr_dme_set_uint(dev, "/debug/hw/lms7002m/0/rxlml", lmode); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device mode: errno %d", res); + dev_exit(); + } + + +} + +static void dev_set_vio(unsigned mv) +{ + int res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/vio", mv); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device LML VIO: errno %d", res); + dev_exit(); + } +} + +static void dev_set_rx_dly(unsigned type, unsigned val) +{ + unsigned mode = ((type) << 8) | val; + int res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/phy_rx_dly", mode); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device LML RX DLY: errno %d", res); + dev_exit(); + } +} + +static void dev_set_rx_phase(unsigned val) +{ + unsigned mode = val + 1; + int res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/rx/phase_ovr", mode); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device LML RX DLY: errno %d", res); + dev_exit(); + } + +} + +static void dev_set_tx_phase_rc(unsigned val) +{ + int res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/tx/phase_ovr_rc", val); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device LML TX VAL IQ: errno %d", res); + dev_exit(); + } +} + +static void dev_set_tx_phase(unsigned val, unsigned val_iq) +{ + unsigned mode = val + 1; + unsigned mode_iq = val_iq + 1; + int res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/tx/phase_ovr", mode); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device LML TX: errno %d", res); + dev_exit(); + } + + res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/tx/phase_ovr_iq", mode_iq); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device LML TX IQ: errno %d", res); + dev_exit(); + } +} + +static void dev_set_rx_lfsr_hw_checker(bool en) +{ + int res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/phy_rx_lfsr", en); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set HW LFSR Checker: errno %d", res); + dev_exit(); + } +} + +static void dev_set_tx_lfsr_hw_generator(bool en) +{ + int res = !dev ? 0 : usdr_dme_set_uint(dev, "/dm/sdr/0/phy_tx_lfsr", en); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set HW LFSR Generator: errno %d", res); + dev_exit(); + } +} + +static void dev_get_rx_lfsr_hw_checker(unsigned errs[4]) +{ + uint64_t v = 0; + int res = !dev ? 0 : usdr_dme_get_uint(dev, "/dm/sdr/0/phy_rx_lfsr", &v); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to get HW LFSR BER: errno %d", res); + dev_exit(); + } + + for (unsigned j = 0; j < 4; j++) { + errs[j] = (v >> (j * 16)) & 0xffff; + } +} + +static void dev_deinit() +{ + if (dev == NULL) return; + int res; + + if (usds_rx) { + res = usdr_dms_op(usds_rx, USDR_DMS_STOP, 0); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to stop RX data stream: errno %d", res); + dev_exit(); + } + } + + if (usds_tx) { + res = usdr_dms_op(usds_tx, USDR_DMS_STOP, 0); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to stop TX data stream: errno %d", res); + dev_exit(); + } + } + + if (strms[1]) usdr_dms_destroy(strms[1]); + if (strms[0]) usdr_dms_destroy(strms[0]); + + usds_rx = usds_tx = NULL; + strms[0] = strms[1] = NULL; +} + +static void dev_init() +{ + if (dev == NULL) return; + + const char* synctype = "all"; + int res; + bool tx = (mode == LML_MODE_LOOPBACK); + + usdr_channel_info_t chans_rx; + usdr_channel_info_t chans_tx; + + chans_rx.count = 2; + chans_rx.flags = 0; + chans_rx.phys_names = NULL; + chans_rx.phys_nums = NULL; + + chans_tx.count = 2; + chans_tx.flags = 0; + chans_tx.phys_names = NULL; + chans_tx.phys_nums = NULL; + + res = usdr_dms_create_ex2(dev, "/ll/srx/0", fmt_rx, &chans_rx, samples_rx, 0, NULL, &usds_rx); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to initialize RX data stream: errno %d", res); + dev_exit(); + } + + if (tx) { + res = usdr_dms_create_ex2(dev, "/ll/stx/0", fmt_tx, &chans_tx, samples_tx, 0, NULL, &usds_tx); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to initialize TX data stream: errno %d", res); + dev_exit(); + } + } else { + usds_tx = NULL; + } + + strms[1] = usds_tx; + strms[0] = usds_rx; + res = usdr_dms_sync(dev, "off", 2, strms); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to sync data streams: errno %d", res); + dev_exit(); + } + + //Start RX streaming + res = usdr_dms_op(usds_rx, USDR_DMS_START, 0); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to start RX data stream: errno %d", res); + dev_exit(); + } + + //Start TX streaming + if (tx) { + res = usdr_dms_op(usds_tx, USDR_DMS_START, 0); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to start TX data stream: errno %d", res); + dev_exit(); + } + } + + //Sync TX&RX data streams + res = usdr_dms_sync(dev, synctype, 2, strms); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to sync data streams: errno %d", res); + dev_exit(); + } +} + +void bits16_to_str(uint16_t v, char str[17]) +{ + for (unsigned h = 0; h < 16; h++) { + str[h] = '0' + (((v << h) & 0x8000) ? 1 : 0); + } + str[16] = 0; +} + +void bits32_to_str(uint32_t v, char str[33]) +{ + for (unsigned h = 0; h < 32; h++) { + str[h] = '0' + (((v << h) & 0x80000000) ? 1 : 0); + } + str[32] = 0; +} + + + +static uint32_t lfsr24_next(uint32_t lfsr) +{ + // Feedback taps: 24, 23, 22, 17 (zero-indexed: 23, 22, 21, 16) + uint32_t bit = ((lfsr >> 23) ^ (lfsr >> 22) ^ (lfsr >> 21) ^ (lfsr >> 16)) & 1; + lfsr = ((lfsr << 1) | bit); + return lfsr; +} + +static uint32_t lfsr16_next(uint32_t lfsr) { + // Feedback taps at 16, 14, 13, 11 (zero-indexed: 15, 13, 12, 10) + uint32_t bit = ((lfsr >> 15) ^ (lfsr >> 13) ^ (lfsr >> 12) ^ (lfsr >> 10)) & 1; + lfsr = ((lfsr << 1) | bit); + return lfsr; +} + +__attribute__((optimize("-O3"))) +static uint32_t lfsr15_next(uint32_t lfsr) { + // Feedback taps at 15 and 14 (zero-indexed: 14, 13) + uint32_t bit = ((lfsr >> 14) ^ (lfsr >> 13)) & 1; + lfsr = ((lfsr << 1) | bit); + return lfsr; +} + +static uint32_t lfsr14_next(uint32_t lfsr) { + // Feedback taps at 14, 13, 11, 10 (zero-indexed: 13, 12, 10, 9) + uint32_t bit = ((lfsr >> 13) ^ (lfsr >> 12) ^ (lfsr >> 10) ^ (lfsr >> 9)) & 1; + lfsr = ((lfsr << 1) | bit); + return lfsr; +} + +static uint32_t lfsr12_next(uint32_t lfsr) { + // Feedback taps at 12, 11, 10, 4 (zero-indexed: 11, 10, 9, 3) + uint32_t bit = ((lfsr >> 11) ^ (lfsr >> 10) ^ (lfsr >> 9) ^ (lfsr >> 3)) & 1; + lfsr = ((lfsr << 1) | bit); + return lfsr; +} + +static uint32_t lfsr8_next(uint32_t lfsr) { + // Feedback taps at 8, 6, 5, 4 (zero-indexed: 7, 5, 4, 3) + uint32_t bit = ((lfsr >> 7) ^ (lfsr >> 5) ^ (lfsr >> 4) ^ (lfsr >> 3)) & 1; + lfsr = ((lfsr << 1) | bit); + return lfsr; +} + +unsigned lfsr_off = 4; +unsigned lfsr_mask_ch = 0xffe; + +unsigned lfsr_g[2] = { 0, 0 }; +unsigned lfsr_r[2] = { 0, 0 }; +unsigned lfsr_xorm[2] = { 0, 1 }; + +struct lfsr_stream { + unsigned off; + unsigned mask_ch; + unsigned mask_lfsr; + + unsigned cnt_burst_good; + unsigned cnt_sync; // Number of sync + unsigned cnt_good; + unsigned cnt_resync; + unsigned xor_stage; + + unsigned state; // current state + unsigned next; // prediction for next + + bit_error_stat_t berr; +}; +typedef struct lfsr_stream lfsr_stream_t; + +void lfsr_init(lfsr_stream_t* d, unsigned off, unsigned m_check) +{ + d->off = off; + d->mask_ch = m_check; + d->mask_lfsr = m_check | 1; + + d->cnt_burst_good = 0; + d->cnt_resync = 0; + d->cnt_sync = 0; + d->xor_stage = 0; + + d->state = 0; + d->next = 0; + + bit_error_init(&d->berr); +} + +enum { + FLAGS_ERRORS = 1, + FLAGS_VERBOSE = 2, +}; + +__attribute__((optimize("-O3"))) +static void dump_lfsr_complex(const char* prefix, lfsr_stream_t* d, const uint16_t* pattern, unsigned sps_count, unsigned flags) +{ + char str[2][17]; + char str_f[2][33]; + bool show_error = (flags & FLAGS_ERRORS) == FLAGS_ERRORS; + bool verbose = (flags & FLAGS_VERBOSE) == FLAGS_VERBOSE; + + for (unsigned p = 0; p < sps_count; p++) { + unsigned a[2]; + bool match[2]; + + for (unsigned j = 0; j < 2; j++) { + a[j] = pattern[2 * p + j] >> d[j].off; + + if (d[j].cnt_burst_good == 0) { + // Resync + d[j].state = a[j]; + d[j].cnt_burst_good++; + } else { + + d[j].state <<= 1; + + if ((d[j].state & d[j].mask_ch) == (a[j] & d[j].mask_ch)) { + d[j].state |= a[j]; + d[j].cnt_burst_good++; + d[j].cnt_sync++; + } else if (d[j].cnt_sync < 20) { + // Bit error burins sync + d[j].state |= (a[j] & 1); + d[j].cnt_sync++; + } else { + unsigned bcnt = bit_error_check(&d[j].berr, a[j] & d->mask_lfsr, d[j].state & d->mask_lfsr); + if (bcnt >= 6) { + // Looks like we lost LFSR sync + d[j].state = 0; + d[j].cnt_resync++; + d[j].cnt_burst_good = 0; + d[j].cnt_sync = 0; + } else { + // Recover single bits error to continue checking BER + d[j].state = lfsr15_next(d[j].state >> 1) ^ d[j].xor_stage; + } + } + } + + match[j] = d[j].next == d[j].state; + d[j].next = lfsr15_next(d[j].state) ^ d[j].xor_stage; + } + + if ((show_error || verbose) && (!match[0] || !match[1] || verbose)) { + for (unsigned j = 0; j < 2; j++) { + bits16_to_str(a[j], str[j]); + bits32_to_str(d[j].state, str_f[j]); + } + + fprintf(stderr, "%s%3d: [%04x %04x]: %s %s -- [%08x %08x]: %s %s [%08x %08x] %c%c\n", prefix, p, a[0], a[1], str[0], str[1], + d[0].state, d[1].state, str_f[0], str_f[1], d[0].next, d[1].next, + match[0] ? '+' : '-', match[1] ? '+' : '-'); + } + + } +} + +static void do_receive_rst() +{ + if (d_pattern_r0 == NULL) { + d_pattern_r0 = malloc(2 * sizeof(uint16_t) * samples_rx * maximum_iterations); + } + if (d_pattern_r1 == NULL) { + d_pattern_r1 = malloc(2 * sizeof(uint16_t) * samples_rx * maximum_iterations); + } + + if (d_pattern_r0 == NULL || d_pattern_r1 == NULL) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "NOT ENOUGH MEMORY\n"); + dev_exit(); + } + + d_off = 0; +} +static int do_receive(bool do_rcv, void* buffers[MAX_CHS]) +{ + usdr_dms_recv_nfo_t rxstat; + int res = 0; + buffers[0] = memcached ? d_pattern_r0 + 2 * d_off : s_pattern_r0; + buffers[1] = memcached ? d_pattern_r1 + 2 * d_off : s_pattern_r1; + + d_off += samples_rx; + + if (!do_rcv) + return 0; + + res = usdr_dms_recv(strms[0], buffers, 2250, &rxstat); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "RX error, unable to recv data: errno %d", res); + return res; + } + + fprintf(stderr, "."); + for (unsigned i = 0; i < num_rx; i++) { + if (files_r[i]) { + fwrite(buffers[i], 2 * samples_rx, 1, files_r[i]); + } + } + + + return 0; +} + +enum { + IDX_AI, + IDX_AQ, + IDX_BI, + IDX_BQ, +}; +#define TOTAL_STREAMS 4 + + +void do_lfsr_test(lfsr_stream_t str[TOTAL_STREAMS]) +{ + int res; + unsigned flags = 0; //FLAGS_VERBOSE; + for (unsigned i = 0; i < TOTAL_STREAMS; i++) { + lfsr_init(&str[i], 4, 0xffe); + str[i].xor_stage = i & 1; // LML interface invert bit in LFSR stream for Q components + } + + for (unsigned k = 0; k < (memcached ? 2 : 1); k++) { + if (k == 1) { + fprintf(stderr, "\nChecking..."); + } + do_receive_rst(); + + for (unsigned cnt = 0; cnt < maximum_iterations; cnt++) { + bool do_rcv = !memcached || (k == 0); + bool do_chk = !memcached || (k == 1); + void* buffers[MAX_CHS]; + + res = do_receive(do_rcv, buffers); + if (res) { + return; + } + + // Do inplace checks (for low samplerate) + if (do_chk) { + dump_lfsr_complex("A", &str[0], buffers[0], samples_rx, flags); + dump_lfsr_complex("B", &str[2], buffers[1], samples_rx, flags); + } + } + } + + + fprintf(stderr, "\n==============================================================\n"); + //Total statisics + const char* ch_names[TOTAL_STREAMS] = { "AI", "AQ", "BI", "BQ" }; + for (unsigned i = 0; i < TOTAL_STREAMS; i++) { + unsigned rest = 0; + for (unsigned k = 5; k < 16; k++) { + rest += str[i].berr.bit_errors[k]; + } + fprintf(stderr, " %s: Resync %8d [%8d %8d %8d %8d -- %8d]\n", + ch_names[i], str[i].cnt_resync, + str[i].berr.bit_errors[1], str[i].berr.bit_errors[2], str[i].berr.bit_errors[3], str[i].berr.bit_errors[4], rest); + for (unsigned j = 0; j < 16; j++) { + if (str[i].berr.bit_pos_errors[j]) { + fprintf(stderr, " %s: BITp[%2d]: %8d\n", ch_names[i], j, str[i].berr.bit_pos_errors[j]); + } + } + } +} + +#define MAX_TAPS 64 + +void do_lfsr_hwtx_test(unsigned str[TOTAL_STREAMS]) +{ + dev_set_tx_lfsr_hw_generator(false); + usleep(1); + dev_set_tx_lfsr_hw_generator(true); + dev_set_rx_lfsr_hw_checker(true); + usleep(1000 * maximum_iterations); + dev_get_rx_lfsr_hw_checker(str); +} + +void lfsr_test_hw_tx() +{ + mode = LML_MODE_LOOPBACK_GEN; + float rate = specific_rate; + unsigned str[MAX_TAPS][TOTAL_STREAMS]; + + dev_set_rate(rate); + dev_init(); + + for (unsigned k = 0; k < MAX_TAPS; k++) { + //dev_init(); + //dev_set_tx_phase(k, 0); // CLK + //dev_set_rate(rate); + + dev_set_tx_phase_rc(k); + do_lfsr_hwtx_test(str[k]); + } + + dev_deinit(); + + for (unsigned j = 0; j < 4; j++) { + fprintf(stderr, "TXCH%d: ", j); + + for (unsigned k = 0; k < MAX_TAPS; k++) { + unsigned ecnt = str[k][j]; + + fprintf(stderr, "%c", (ecnt == 0) ? ' ' : (ecnt < 1000) ? 'x' : 'X'); + } + + fprintf(stderr, "\n"); + } +} + + +void lfsr_test_shw_tx() +{ + mode = LML_MODE_LOOPBACK_GEN; + float rate = specific_rate; + dev_set_rate(rate); + + unsigned str[MAX_TAPS][MAX_TAPS][TOTAL_STREAMS]; + + for (unsigned l = 0; l < MAX_TAPS; l++) { + for (unsigned k = 0; k < MAX_TAPS; k++) { + dev_init(); + + dev_set_tx_phase(k, l); // CLK + dev_set_rate(rate); + + do_lfsr_hwtx_test(str[l][k]); + + dev_deinit(); + } + } + + for (unsigned l = 0; l < MAX_TAPS; l++) { + for (unsigned j = 0; j < 4; j++) { + fprintf(stderr, "TX_IQ%d_CH%d: ", l, j); + + for (unsigned k = 0; k < MAX_TAPS; k++) { + unsigned ecnt = str[l][k][j]; + + fprintf(stderr, "%c", (ecnt == 0) ? ' ' : (ecnt < 1000) ? 'x' : 'X'); + } + fprintf(stderr, "\n"); + } + } + +} + + +void do_lfsr_hw_test(unsigned str[TOTAL_STREAMS]) +{ + dev_set_rx_lfsr_hw_checker(true); + usleep(1000 * maximum_iterations); + dev_get_rx_lfsr_hw_checker(str); +} + +void lfsr_test_hw() +{ + mode = LML_MODE_LFSR; + + float rate = specific_rate; + dev_set_rate(rate); + unsigned str[MAX_TAPS][TOTAL_STREAMS]; + + for (unsigned k = 0; k < MAX_TAPS; k++) { + dev_init(); + + dev_set_rx_phase(k); // CLK + dev_set_rate(rate); + + do_lfsr_hw_test(str[k]); + + dev_deinit(); + } + + for (unsigned j = 0; j < 4; j++) { + fprintf(stderr, "CH%d: ", j); + + for (unsigned k = 0; k < MAX_TAPS; k++) { + unsigned ecnt = str[k][j]; + + fprintf(stderr, "%c", (ecnt == 0) ? ' ' : (ecnt < 1000) ? 'x' : 'X'); + } + + fprintf(stderr, "\n"); + } + + +} + +void lfsr_tests(bool hwgen) +{ + mode = hwgen ? LML_MODE_LOOPBACK_GEN : LML_MODE_LFSR ; + lfsr_stream_t str[MAX_TAPS][TOTAL_STREAMS]; + + dev_set_vio(2300); + float rate = specific_rate; + dev_set_rate(rate); + + // looks like MCLK comes later, we need to delay all the data + for (unsigned k = 0; k < MAX_TAPS; k++) { + dev_init(); + +#if 0 + dev_set_rx_dly(0, 0); // CLK + dev_set_rx_dly(1, k); // FRAME + for (unsigned h = 0; h < 12; h++) { + dev_set_rx_dly(h + 2, k); // D0 + } +#endif + if (hwgen) { + dev_set_tx_lfsr_hw_generator(true); + dev_set_tx_phase(k, k); // CLK + } else { + dev_set_rx_phase(k); // CLK + } + dev_set_rate(rate); + + +//#endif + do_lfsr_test(str[k]); + + dev_deinit(); + } + + fprintf(stderr, "\nX_BITXX: "); + for (unsigned k = 0; k < MAX_TAPS; k++) { + fprintf(stderr, (MAX_TAPS <= 32) ? "%6d" : "%3d", k); + } + fprintf(stderr, "\nX_BITXX: "); + for (unsigned k = 0; k < MAX_TAPS; k++) { + float period = 1e9 / specific_rate / 2; + float off = 0.078 * k; + unsigned p = 1000 * off / period; + + fprintf(stderr, (MAX_TAPS <= 32) ? "%6d" : "%3d", p); + } + + // Individual bit errors first + // Total bit error distribution + for (unsigned h = 0; h < 2; h++) { + for (unsigned j = 0; j < 4; j++) { + for (unsigned b = 0; b < 12; b++) { + fprintf(stderr, "\n%d_BIT%c%2d: ", j, h == 0 ? 'p' : 't', b); + for (unsigned k = 0; k < MAX_TAPS; k++) { + unsigned ecnt = (h == 0) ? str[k][j].berr.bit_pos_errors[b] : str[k][j].berr.bit_errors[b + 1]; + + if (MAX_TAPS <= 32) { + if (ecnt <= 999999) { + fprintf(stderr, "%6d", ecnt); + } else { + fprintf(stderr, (k % 2) ? "xxxxxx" : "XXXXXX"); + } + } else if (MAX_TAPS <= 64) { + if (ecnt <= 999) { + fprintf(stderr, "%3d", ecnt); + } else { + fprintf(stderr, (k % 2) ? "xxx" : "XXX"); + } + } else { + if (ecnt <= 9) { + fprintf(stderr, "%1d", ecnt); + } else { + fprintf(stderr, (k % 2) ? "x" : "X"); + } + } + } + } + } + fprintf(stderr, "\n================== distribution of total bit errors =============================="); + } + fprintf(stderr, "\n"); + +} + + +int main(int argc, char** argv) +{ + bool dry_run = false; + float sbit_err_p = 0.0; // Emulation + float mbit_err_p = 0.0; // Emulation + + unsigned specific_test = 0; + + const char* device_name = NULL; + unsigned loglevel = USDR_LOG_INFO; + + unsigned statistics = 0; + + bool hwtxrx_tests = false; + bool shwtx_tests = false; + bool hwtx_tests = false; + bool hw_tests = false; + bool dump_rx = false; + int opt, res; + + while ((opt = getopt(argc, argv, "Mi:r:j:D:t:l:dowWZz")) != -1) { + switch (opt) { + case 'M': + memcached = true; + break; + case 'i': + maximum_iterations = atoi(optarg); + break; + case 'r': + specific_rate = atof(optarg); + break; + case 'j': + statistics = atoi(optarg); + break; + case 'D': + device_name = optarg; + break; + case 't': + specific_test = atoi(optarg); + break; + case 'l': + loglevel = atof(optarg); + usdrlog_setlevel(NULL, loglevel); + break; + case 'd': + dry_run = true; + break; + case 'o': + dump_rx = true; + break; + case 'z': + hwtxrx_tests = true; + break; + case 'W': + hwtx_tests = true; + break; + case 'w': + hw_tests = true; + break; + case 'Z': + shwtx_tests = true; + break; + default: + exit(EXIT_FAILURE); + } + } + + usdrlog_setlevel(NULL, loglevel); + // Check if is tty + usdrlog_enablecolorize(NULL); + + //Open device & create dev handle + res = dry_run ? 0 : usdr_dmd_create_string(device_name, &dev); + if (res) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to create device: errno %d", res); + return 1; + } + + if (dump_rx) { + for (unsigned i = 0; i < num_rx; i++) { + files_r[i] = fopen(i == 0 ? "out_lml7_0.dat" : "out_lml7_1.dat", "wb+c"); + if (!files_r[i]) { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to create RX storage data file!\n"); + return 3; + } + } + } + + + // Tests + if (shwtx_tests) { + lfsr_test_shw_tx(); + } else if (hwtx_tests) { + lfsr_test_hw_tx(); + } else if (hw_tests) { + lfsr_test_hw(); + } else { + lfsr_tests(hwtxrx_tests); + } + + res = dry_run ? 0 : usdr_dmd_close(dev); + return res; +} + + + + + + + + diff --git a/src/tools/python/example_fft_rx.py b/src/tools/python/example_fft_rx.py new file mode 100755 index 00000000..7a999fda --- /dev/null +++ b/src/tools/python/example_fft_rx.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Receive IQ samples from USDR and plot accumulated FFT magnitude. + +Example: + python3 example_fft_rx.py \ + --device "" \ + --rx-frequency 900e6 \ + --rx-bandwidth 1e6 \ + --rx-gain 15 \ + --samplerate 50e6 \ + --accumulation 16 \ + --fft-size 4096 \ + --window hann +""" + +from __future__ import annotations + +import argparse +import numpy as np +from scipy import signal +import matplotlib.pyplot as plt + +from usdr_bindings import USDR_DMS_START, USDR_DMS_STOP, UsdrDevice + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="USDR RX FFT plotter") + parser.add_argument("--device", default="", help="USDR device string") + parser.add_argument("--samplerate", type=float, default=5e6, help="Sample rate in SPS") + parser.add_argument("--loglevel", type=int, default=None, help="Optional libusdr log level") + parser.add_argument("--rx-frequency", type=float, required=True, help="RX frequency in Hz") + parser.add_argument("--rx-bandwidth", type=float, required=True, help="RX bandwidth in Hz") + parser.add_argument("--rx-gain", type=float, default=15, help="RX gain value (mapped to LNA gain)") + + parser.add_argument("--fft-size", type=int, default=4096, help="FFT bin size") + parser.add_argument("--accumulation", type=int, default=16, help="Number of FFT frames to accumulate") + parser.add_argument("--window", default="hann", help="Scipy window name (e.g. hann, blackman, flattop)") + parser.add_argument("--channel", type=int, default=0, help="RX channel index") + parser.add_argument("--timeout-ms", type=int, default=1000, help="Receive timeout in milliseconds") + + return parser.parse_args() + + +def main() -> None: + args = parse_args() + + if args.fft_size <= 0: + raise ValueError("--fft-size must be > 0") + if args.accumulation <= 0: + raise ValueError("--accumulation must be > 0") + + fft_size = int(args.fft_size) + window = signal.get_window(args.window, fft_size, fftbins=True).astype(np.float32) + + with UsdrDevice(args.device, loglevel=args.loglevel) as dev: + dev.set_samplerate(int(args.samplerate)) + dev.set_rx_frequency(int(args.rx_frequency)) + dev.set_rx_bandwidth(int(args.rx_bandwidth)) + dev.set_rx_gain_lna(int(args.rx_gain)) + + with dev.create_stream( + sobj="/ll/srx/0", + dformat="cf32", + channels=[int(args.channel)], + pktsyms=fft_size, + ) as rx_stream: + rx_stream.sync("any") + rx_stream.op(USDR_DMS_START) + + psd_acc = np.zeros(fft_size, dtype=np.float64) + + for _ in range(args.accumulation): + arrays, _ = rx_stream.recv(timeout_ms=args.timeout_ms, with_info=False) + iq = arrays[0] + + if iq.shape[0] < fft_size: + raise RuntimeError(f"Received {iq.shape[0]} samples, expected at least {fft_size}") + + x = iq[:fft_size] * window + spec = np.fft.fftshift(np.fft.fft(x, n=fft_size)) + psd_acc += np.abs(spec) ** 2 + + rx_stream.op(USDR_DMS_STOP) + + psd = psd_acc / args.accumulation + psd_db = 10.0 * np.log10(psd + 1e-20) + + fs = float(args.samplerate) + f_axis = np.fft.fftshift(np.fft.fftfreq(fft_size, d=1.0 / fs)) + + plt.figure(figsize=(10, 5)) + plt.plot(f_axis / 1e6, psd_db) + plt.title("USDR RX FFT") + plt.xlabel("Frequency offset (MHz)") + plt.ylabel("Power (dB, arbitrary)") + plt.grid(True, alpha=0.3) + plt.tight_layout() + plt.show() + + +if __name__ == "__main__": + main() diff --git a/src/tools/python/example_fft_rx_soapy.py b/src/tools/python/example_fft_rx_soapy.py new file mode 100755 index 00000000..f83f4d25 --- /dev/null +++ b/src/tools/python/example_fft_rx_soapy.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +"""Receive IQ samples from USDR and plot accumulated FFT magnitude (using SoapySDR). + +Example: + python3 example_soapy_rx.py \ + --device "driver=usdr" \ + --rx-frequency 900e6 \ + --rx-bandwidth 1e6 \ + --rx-gain 80 \ + --samplerate 50e6 \ + --accumulation 16 \ + --fft-size 4096 \ + --window hann \ + --loglevel 3 + + Parameter `device` can be any SoapySDR device string, e.g. "driver=usdr" or "driver=usdr,bus=" + device_bus can be: + - bus=pci,device=, where is the PCI device Path (e.g. /dev/usdr0) + - bus=usb@, where is the USB address (e.g. 3/3/6 for bus 3, device 3, function 6) +""" + +from __future__ import annotations + +import argparse +import numpy as np +from scipy import signal +import matplotlib.pyplot as plt + +import SoapySDR +from SoapySDR import SOAPY_SDR_RX + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="USDR RX FFT plotter") + parser.add_argument("--device", default="", help="USDR device string") + parser.add_argument("--samplerate", type=float, default=5e6, help="Sample rate in SPS") + parser.add_argument("--loglevel", type=int, default=None, help="Optional libusdr log level") + parser.add_argument("--rx-frequency", type=float, required=True, help="RX frequency in Hz") + parser.add_argument("--rx-bandwidth", type=float, required=True, help="RX bandwidth in Hz") + parser.add_argument("--rx-gain", type=float, default=15, help="RX gain value (mapped to LNA gain)") + + parser.add_argument("--fft-size", type=int, default=4096, help="FFT bin size") + parser.add_argument("--accumulation", type=int, default=16, help="Number of FFT frames to accumulate") + parser.add_argument("--window", default="hann", help="Scipy window name (e.g. hann, blackman, flattop)") + parser.add_argument("--channel", type=int, default=0, help="RX channel index") + parser.add_argument("--timeout-ms", type=int, default=1000, help="Receive timeout in milliseconds") + + return parser.parse_args() + +# Allocate a 64-byte-aligned buffer to avoid AVX512 alignment issues +def aligned_empty(shape, dtype, alignment=64): + dtype = np.dtype(dtype) + n_elems = int(np.prod(shape)) + nbytes = n_elems * dtype.itemsize + raw = np.empty(nbytes + alignment, dtype=np.uint8) + start = raw.ctypes.data + offset = (-start) % alignment + buf = raw[offset:offset + nbytes].view(dtype) + return buf.reshape(shape) + + +def main() -> None: + args = parse_args() + + if args.fft_size <= 0: + raise ValueError("--fft-size must be > 0") + if args.accumulation <= 0: + raise ValueError("--accumulation must be > 0") + + fft_size = int(args.fft_size) + window = signal.get_window(args.window, fft_size, fftbins=True).astype(np.float32) + + # Open SoapySDR device + if args.device: + dev_kwargs = SoapySDR.KwargsFromString(args.device) + else: + dev_kwargs = SoapySDR.SoapySDRKwargs() + if args.loglevel is not None: + dev_kwargs["loglevel"] = str(int(args.loglevel)) + try: + dev = SoapySDR.Device(dev_kwargs) + except Exception as e: + print(f"Failed to open SoapySDR device: {e}") + exit(1) + + chan = int(args.channel) + + # Configure device + dev.setSampleRate(SOAPY_SDR_RX, chan, float(args.samplerate)) + dev.setFrequency(SOAPY_SDR_RX, chan, float(args.rx_frequency)) + dev.setBandwidth(SOAPY_SDR_RX, chan, float(args.rx_bandwidth)) + dev.setGain(SOAPY_SDR_RX, chan, float(args.rx_gain)) + + # Setup stream for complex float32 (direction, format, channels, args) + args_kw = SoapySDR.SoapySDRKwargs() + args_kw["linkFormat"] = "CS16" # Request int16 samples from driver also can be "CS12" + args_kw["bufferLength"] = str(int(fft_size)) + rx_stream = dev.setupStream(SOAPY_SDR_RX, "CF32", [chan], args_kw) + dev.activateStream(rx_stream) + + psd_acc = np.zeros(fft_size, dtype=np.float64) + + timeout_us = int(args.timeout_ms * 1000) + + # Receive and accumulate FFT frames + for _ in range(int(args.accumulation)): + buf = aligned_empty((fft_size,), np.complex64, alignment=64) + + res = dev.readStream(rx_stream, [buf], int(fft_size), timeoutUs=timeout_us) + + n = res.ret + if n < fft_size: + raise RuntimeError(f"Received {n} samples, expected at least {fft_size}") + + x = buf[:fft_size] * window + spec = np.fft.fftshift(np.fft.fft(x, n=fft_size)) + psd_acc += np.abs(spec) ** 2 + + # Close stream + dev.deactivateStream(rx_stream) + dev.closeStream(rx_stream) + + # Close device + if dev.close is not None: + dev.close() # Not strictly necessary, but good practice + dev = None + + psd = psd_acc / args.accumulation + psd_db = 10.0 * np.log10(psd + 1e-20) + + fs = float(args.samplerate) + f_axis = np.fft.fftshift(np.fft.fftfreq(fft_size, d=1.0 / fs)) + + plt.figure(figsize=(10, 5)) + plt.plot(f_axis / 1e6, psd_db) + plt.title("USDR RX FFT (SoapySDR)") + plt.xlabel("Frequency offset (MHz)") + plt.ylabel("Power (dB, arbitrary)") + plt.grid(True, alpha=0.3) + plt.tight_layout() + plt.show() + + +if __name__ == "__main__": + main() diff --git a/src/tools/python/example_fft_tx_soapy.py b/src/tools/python/example_fft_tx_soapy.py new file mode 100755 index 00000000..8155ff78 --- /dev/null +++ b/src/tools/python/example_fft_tx_soapy.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +"""Generate a sine wave and transmit IQ samples via USDR (using SoapySDR). + +Example: + python3 example_fft_tx_soapy.py \ + --device "driver=usdr" \ + --tx-frequency 900e6 \ + --tx-bandwidth 5e6 \ + --tx-gain 16 \ + --samplerate 5e6 \ + --tone-offset 1000e3 \ + --amplitude 0.9 \ + --burst-size 4096 \ + --num-bursts 8192 \ + --loglevel 3 + + Parameter `device` can be any SoapySDR device string, e.g. "driver=usdr" or "driver=usdr,bus=" + device_bus can be: + - bus=pci,device=, where is the PCI device Path (e.g. /dev/usdr0) + - bus=usb@, where is the USB address (e.g. 3/3/6 for bus 3, device 3, function 6) +""" + +from __future__ import annotations + +import argparse +import numpy as np + +import SoapySDR +from SoapySDR import SOAPY_SDR_TX + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="USDR TX sine wave generator") + parser.add_argument("--device", default="", help="USDR device string") + parser.add_argument("--samplerate", type=float, default=5e6, help="Sample rate in SPS") + parser.add_argument("--loglevel", type=int, default=None, help="Optional libusdr log level") + parser.add_argument("--tx-frequency", type=float, required=True, help="TX center frequency in Hz") + parser.add_argument("--tx-bandwidth", type=float, required=True, help="TX bandwidth in Hz") + parser.add_argument("--tx-gain", type=float, default=0, help="TX gain value") + + parser.add_argument("--tone-offset", type=float, default=100e3, help="Tone frequency offset from center in Hz") + parser.add_argument("--amplitude", type=float, default=0.9, help="Tone amplitude (0.0 .. 1.0)") + parser.add_argument("--burst-size", type=int, default=4096, help="Number of samples per write burst") + parser.add_argument("--num-bursts", type=int, default=128, help="Number of bursts to transmit (0 = infinite)") + parser.add_argument("--channel", type=int, default=0, help="TX channel index") + parser.add_argument("--timeout-ms", type=int, default=1000, help="Write timeout in milliseconds") + + return parser.parse_args() + + +# Allocate a 64-byte-aligned buffer to avoid AVX512 alignment issues +def aligned_empty(shape, dtype, alignment=64): + dtype = np.dtype(dtype) + n_elems = int(np.prod(shape)) + nbytes = n_elems * dtype.itemsize + raw = np.empty(nbytes + alignment, dtype=np.uint8) + start = raw.ctypes.data + offset = (-start) % alignment + buf = raw[offset:offset + nbytes].view(dtype) + return buf.reshape(shape) + + +def generate_tone(n_samples: int, samplerate: float, tone_offset: float, + amplitude: float, phase_offset: float = 0.0) -> tuple[np.ndarray, float]: + """Generate a complex sine tone and return (samples, next_phase_offset). + + Keeping track of *phase_offset* across successive calls ensures a continuous + waveform without discontinuities at burst boundaries. + """ + t = np.arange(n_samples, dtype=np.float64) / samplerate + phase = 2.0 * np.pi * tone_offset * t + phase_offset + iq = amplitude * np.exp(1j * phase) + + # Compute the phase right after the last sample so the next call can continue + next_phase = phase[-1] + 2.0 * np.pi * tone_offset / samplerate + next_phase = next_phase % (2.0 * np.pi) + + return iq.astype(np.complex64), next_phase + + +def main() -> None: + args = parse_args() + + if args.burst_size <= 0: + raise ValueError("--burst-size must be > 0") + if args.amplitude < 0.0 or args.amplitude > 1.0: + raise ValueError("--amplitude must be in [0.0, 1.0]") + + burst_size = int(args.burst_size) + + # Open SoapySDR device + if args.device: + dev_kwargs = SoapySDR.KwargsFromString(args.device) + else: + dev_kwargs = SoapySDR.SoapySDRKwargs() + if args.loglevel is not None: + dev_kwargs["loglevel"] = str(int(args.loglevel)) + try: + dev = SoapySDR.Device(dev_kwargs) + except Exception as e: + print(f"Failed to open SoapySDR device: {e}") + exit(1) + + chan = int(args.channel) + + # Configure device + dev.setSampleRate(SOAPY_SDR_TX, chan, float(args.samplerate)) + dev.setFrequency(SOAPY_SDR_TX, chan, float(args.tx_frequency)) + dev.setBandwidth(SOAPY_SDR_TX, chan, float(args.tx_bandwidth)) + dev.setGain(SOAPY_SDR_TX, chan, float(args.tx_gain)) + + # Setup stream for complex float32 (direction, format, channels, args) + args_kw = SoapySDR.SoapySDRKwargs() + args_kw["linkFormat"] = "CS16" # Request int16 samples from driver, also can be "CS12" + args_kw["bufferLength"] = str(int(burst_size)) + tx_stream = dev.setupStream(SOAPY_SDR_TX, "CF32", [chan], args_kw) + dev.activateStream(tx_stream) + + timeout_us = int(args.timeout_ms * 1000) + phase = 0.0 + num_bursts = int(args.num_bursts) + infinite = num_bursts == 0 + + print(f"Transmitting tone at {args.tx_frequency + args.tone_offset:.0f} Hz " + f"(center {args.tx_frequency:.0f} Hz + offset {args.tone_offset:.0f} Hz)") + print(f" samplerate={args.samplerate:.0f} amplitude={args.amplitude} " + f"burst_size={burst_size} num_bursts={'inf' if infinite else num_bursts}") + + # Transmit sine-wave bursts + burst_idx = 0 + try: + while infinite or burst_idx < num_bursts: + tone, phase = generate_tone(burst_size, float(args.samplerate), + float(args.tone_offset), float(args.amplitude), + phase) + + buf = aligned_empty((burst_size,), np.complex64, alignment=64) + buf[:] = tone + + res = dev.writeStream(tx_stream, [buf], int(burst_size), timeoutUs=timeout_us) + + n = res.ret + if n < 0: + raise RuntimeError(f"writeStream returned error code {n}") + if n < burst_size: + print(f"Warning: wrote only {n}/{burst_size} samples") + + burst_idx += 1 + except KeyboardInterrupt: + print(f"\nInterrupted after {burst_idx} bursts") + + # Close stream + dev.deactivateStream(tx_stream) + dev.closeStream(tx_stream) + + # Close device + if dev.close is not None: + dev.close() # Not strictly necessary, but good practice + dev = None + + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/src/tools/python/example_minimal.py b/src/tools/python/example_minimal.py new file mode 100755 index 00000000..5543d695 --- /dev/null +++ b/src/tools/python/example_minimal.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Minimal usage example for high-level UsdrDevice/UsdrStream API.""" + +import argparse +import numpy as np + +from usdr_bindings import USDR_DMS_START, USDR_DMS_STOP, UsdrDevice + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="USDR minimal RX/TX example") + parser.add_argument("--device", default="", help="USDR device string") + parser.add_argument("--loglevel", type=int, default=None, help="Optional libusdr log level") + return parser.parse_args() + + +def main() -> None: + args = parse_args() + + with UsdrDevice(args.device, loglevel=args.loglevel) as dev: + dev.set_samplerate(5_000_000) + dev.configure_rf( + rx_freq=900_000_000, + tx_freq=920_000_000, + rx_bandwidth=1_000_000, + tx_bandwidth=1_000_000, + rx_gain_lna=15, + rx_gain_vga=15, + rx_gain_pga=15, + tx_gain=0, + rx_path="rx_auto", + tx_path="tx_auto", + ) + + with dev.create_stream("/ll/srx/0", "cf32", [0], 8*2048) as rx_stream, dev.create_stream("/ll/stx/0", "ci16", [0], 2048) as tx_stream: + info = rx_stream.info() + print(f"RX stream: channels={info.channels} pktsyms={info.pktsyms} pktbytes={info.pktbszie}") + + rx_stream.sync("any", tx_stream) + rx_stream.op(USDR_DMS_START) + tx_stream.op(USDR_DMS_START) + + rx_arrays, rx_nfo = rx_stream.recv(timeout_ms=1000, with_info=True) + print(f"RX dtype={rx_arrays[0].dtype} shape={rx_arrays[0].shape}") + print(f"Received symbols={rx_nfo.totsyms} lost={rx_nfo.totlost} hw_time={rx_nfo.fsymtime}") + + tx_data = np.zeros((info.pktsyms, 2), dtype=np.int16) + tx_stream.send([tx_data], timestamp=0, timeout_ms=1000) + + rx_stream.op(USDR_DMS_STOP) + tx_stream.op(USDR_DMS_STOP) + + +if __name__ == "__main__": + main() diff --git a/src/tools/python/usdr_bindings.py b/src/tools/python/usdr_bindings.py new file mode 100644 index 00000000..3fc7649c --- /dev/null +++ b/src/tools/python/usdr_bindings.py @@ -0,0 +1,733 @@ +"""Minimal ctypes bindings and high-level Pythonic wrappers for libusdr.""" + +from __future__ import annotations + +import ctypes +import errno +import ctypes.util +from dataclasses import dataclass +from typing import Dict, Iterable, Optional, Sequence, Tuple, Union + + +class UsdrException(RuntimeError): + """Raised by high-level wrappers when a libusdr operation fails.""" + + def __init__(self, fn_name: str, code: int): + super().__init__(f"{fn_name} failed with error code {code}") + self.fn_name = fn_name + self.code = code + + +# Backward-compatible alias from previous revisions. +USDRError = UsdrException + + +class UsdrDmsNfo(ctypes.Structure): + _fields_ = [ + ("type", ctypes.c_uint), + ("channels", ctypes.c_uint), + ("pktbszie", ctypes.c_uint), + ("pktsyms", ctypes.c_uint), + ("totsamptick", ctypes.c_uint), + ("burst_count", ctypes.c_uint), + ] + + +class UsdrChannelInfo(ctypes.Structure): + _fields_ = [ + ("count", ctypes.c_uint), + ("flags", ctypes.c_uint), + ("phys_names", ctypes.POINTER(ctypes.c_char_p)), + ("phys_nums", ctypes.POINTER(ctypes.c_uint)), + ] + + +class UsdrDmsRecvNfo(ctypes.Structure): + _fields_ = [ + ("fsymtime", ctypes.c_uint64), + ("totsyms", ctypes.c_uint), + ("totlost", ctypes.c_uint), + ("max_parts", ctypes.c_uint), + ("extra", ctypes.c_uint64), + ] + + +@dataclass(frozen=True) +class UsdrStreamInfo: + """Pure-Python stream metadata object returned by :meth:`UsdrStream.info`.""" + + type: int + channels: int + pktbszie: int + pktsyms: int + totsamptick: int + burst_count: int + + @classmethod + def from_ctypes(cls, nfo: "UsdrDmsNfo") -> "UsdrStreamInfo": + return cls( + type=int(nfo.type), + channels=int(nfo.channels), + pktbszie=int(nfo.pktbszie), + pktsyms=int(nfo.pktsyms), + totsamptick=int(nfo.totsamptick), + burst_count=int(nfo.burst_count), + ) + + def __str__(self) -> str: + return ( + f"UsdrStreamInfo(type={self.type}, channels={self.channels}, " + f"pktbytes={self.pktbszie}, pktsyms={self.pktsyms}, " + f"totsamptick={self.totsamptick}, burst_count={self.burst_count})" + ) + + +USDR_DMS_START = 0 +USDR_DMS_STOP = 1 +USDR_DMS_START_AT = 2 +USDR_DMS_STOP_AT = 3 + +ChannelId = Union[int, str] +ChannelMap = Optional[Sequence[ChannelId]] + + +def empty_aligned_complex64(shape, alignment=64): + """ + Creates an uninitialized complex64 array aligned to the specified byte boundary. + """ + import numpy as np + + dtype = np.dtype(np.complex64) + # Calculate total bytes needed + n_bytes = np.prod(shape) * dtype.itemsize + + # Allocate extra 'alignment' bytes to ensure we can find an aligned start + raw_buffer = np.empty(n_bytes + alignment, dtype=np.uint8) + + # Find the starting address and calculate the offset to the next 64-byte boundary + start_address = raw_buffer.ctypes.data + offset = (alignment - (start_address % alignment)) % alignment + + # Create the view starting at the aligned offset + aligned_array = raw_buffer[offset : offset + n_bytes].view(dtype).reshape(shape) + return aligned_array + + +class UsdrLib: + """Thin low-level wrapper around selected libusdr C APIs (non-raising).""" + + def __init__(self, library_path: Optional[str] = None) -> None: + candidates = [ + library_path, + ctypes.util.find_library("usdr"), + "libusdr.so", + "libusdr.dylib", + "usdr.dll", + ] + + last_exc = None + self._lib = None + self._stream_meta: Dict[int, Tuple[str, UsdrDmsNfo]] = {} + for name in candidates: + if not name: + continue + try: + self._lib = ctypes.CDLL(name) + break + except OSError as exc: + last_exc = exc + + if self._lib is None: + raise OSError(f"Unable to load libusdr: {last_exc}") + + self._configure_signatures() + + def _configure_signatures(self) -> None: + self._lib.usdr_dmd_create_string.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)] + self._lib.usdr_dmd_create_string.restype = ctypes.c_int + + self._lib.usdr_dmd_close.argtypes = [ctypes.c_void_p] + self._lib.usdr_dmd_close.restype = ctypes.c_int + + self._lib.usdr_dme_set_uint.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint64] + self._lib.usdr_dme_set_uint.restype = ctypes.c_int + + self._lib.usdr_dme_set_string.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p] + self._lib.usdr_dme_set_string.restype = ctypes.c_int + + self._lib.usdr_dme_get_uint.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint64)] + self._lib.usdr_dme_get_uint.restype = ctypes.c_int + + self._lib.usdr_dme_get_u32.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32)] + self._lib.usdr_dme_get_u32.restype = ctypes.c_int + + self._lib.usdr_dmr_rate_set.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint] + self._lib.usdr_dmr_rate_set.restype = ctypes.c_int + + self._lib.usdr_dms_create_ex2.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.POINTER(UsdrChannelInfo), + ctypes.c_uint, + ctypes.c_uint, + ctypes.c_char_p, + ctypes.POINTER(ctypes.c_void_p), + ] + self._lib.usdr_dms_create_ex2.restype = ctypes.c_int + + self._lib.usdr_dms_destroy.argtypes = [ctypes.c_void_p] + self._lib.usdr_dms_destroy.restype = ctypes.c_int + + self._lib.usdr_dms_info.argtypes = [ctypes.c_void_p, ctypes.POINTER(UsdrDmsNfo)] + self._lib.usdr_dms_info.restype = ctypes.c_int + + self._lib.usdr_dms_op.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint64] + self._lib.usdr_dms_op.restype = ctypes.c_int + + self._lib.usdr_dms_sync.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p)] + self._lib.usdr_dms_sync.restype = ctypes.c_int + + self._lib.usdr_dms_recv.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_void_p), + ctypes.c_uint, + ctypes.POINTER(UsdrDmsRecvNfo), + ] + self._lib.usdr_dms_recv.restype = ctypes.c_int + + self._lib.usdr_dms_send.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_void_p), + ctypes.c_uint, + ctypes.c_uint64, + ctypes.c_uint, + ] + self._lib.usdr_dms_send.restype = ctypes.c_int + + @staticmethod + def _normalize_host_format(dformat: str) -> str: + host_fmt = dformat.split("@", 1)[0] + host_fmt = host_fmt.split(";", 1)[0] + return host_fmt.strip().lower() + + def _stream_key(self, stream: ctypes.c_void_p) -> int: + return int(ctypes.cast(stream, ctypes.c_void_p).value) + + @staticmethod + def _validate_channels(channels: ChannelMap) -> Tuple[int, Sequence[ChannelId]]: + if channels is None: + return 0, [0] + if not isinstance(channels, Sequence) or isinstance(channels, (str, bytes)): + return -errno.EINVAL, [] + if len(channels) == 0: + return -errno.EINVAL, [] + + all_int = all(isinstance(ch, int) and not isinstance(ch, bool) for ch in channels) + all_str = all(isinstance(ch, str) for ch in channels) + if not (all_int or all_str): + return -errno.EINVAL, [] + + return 0, channels + + # Low-level methods return integer status codes (0 on success). + def usdrlog_setlevel(self, loglevel: int, subsystem: Optional[str] = None) -> None: + """Set libusdr logging level for a subsystem or default (None).""" + self._lib.usdrlog_setlevel(subsystem.encode("utf-8") if subsystem else None, int(loglevel)) + + # Low-level methods return integer status codes (0 on success). + def usdr_dmd_create_string(self, connection_string: str) -> Tuple[int, ctypes.c_void_p]: + """Create device handle from a connection string. + + Returns: + Tuple of ``(res, dev_handle)`` where ``res == 0`` on success. + """ + dev = ctypes.c_void_p() + res = self._lib.usdr_dmd_create_string(connection_string.encode("utf-8"), ctypes.byref(dev)) + return int(res), dev + + def usdr_dmd_close(self, device: ctypes.c_void_p) -> int: + """Close device handle previously created by :meth:`usdr_dmd_create_string`.""" + return int(self._lib.usdr_dmd_close(device)) + + def usdr_dme_set_uint(self, device: ctypes.c_void_p, path: str, value: int) -> int: + """Set unsigned integer parameter by full path.""" + return int(self._lib.usdr_dme_set_uint(device, path.encode("utf-8"), int(value))) + + def usdr_dme_set_string(self, device: ctypes.c_void_p, path: str, value: str) -> int: + """Set string parameter by full path.""" + return int(self._lib.usdr_dme_set_string(device, path.encode("utf-8"), value.encode("utf-8"))) + + def usdr_dme_get_uint(self, device: ctypes.c_void_p, path: str) -> Tuple[int, int]: + """Read unsigned integer parameter by full path. + + Returns: + ``(res, value)`` where value is valid when ``res == 0``. + """ + out = ctypes.c_uint64() + res = self._lib.usdr_dme_get_uint(device, path.encode("utf-8"), ctypes.byref(out)) + return int(res), int(out.value) + + def usdr_dme_get_u32(self, device: ctypes.c_void_p, path: str) -> Tuple[int, int]: + """Read 32-bit unsigned parameter by full path.""" + out = ctypes.c_uint32() + res = self._lib.usdr_dme_get_u32(device, path.encode("utf-8"), ctypes.byref(out)) + return int(res), int(out.value) + + def usdr_dmr_rate_set(self, device: ctypes.c_void_p, rate: int, rate_name: Optional[str] = None) -> int: + """Set sample rate for device (master rate when ``rate_name`` is ``None``).""" + return int(self._lib.usdr_dmr_rate_set(device, rate_name.encode("utf-8") if rate_name else None, int(rate))) + + def usdr_dms_create_ex2( + self, + device: ctypes.c_void_p, + sobj: str, + dformat: str = "cf32", + channels: ChannelMap = None, + pktsyms: int = 4096, + flags: int = 0, + parameters: Optional[str] = None, + ) -> Tuple[int, ctypes.c_void_p]: + """Create stream handle with explicit channel mapping and format. + + ``channels`` may be either a list of integers (mapped to ``phys_nums``) or a + list of strings (mapped to ``phys_names``). If omitted, ``[0]`` is used. + + Returns: + ``(res, stream_handle)`` where ``res == 0`` on success. + """ + vres, channels = self._validate_channels(channels) + if vres != 0: + return vres, ctypes.c_void_p() + + chan_info = UsdrChannelInfo(count=len(channels), flags=0, phys_names=None, phys_nums=None) + + chan_nums = None + chan_names_arr = None + chan_names_storage = None + if isinstance(channels[0], int): + chan_nums = (ctypes.c_uint * len(channels))(*[int(ch) for ch in channels]) + chan_info.phys_nums = chan_nums + else: + chan_names_storage = [ch.encode("utf-8") for ch in channels] + chan_names_arr = (ctypes.c_char_p * len(channels))(*chan_names_storage) + chan_info.phys_names = chan_names_arr + + out_stream = ctypes.c_void_p() + res = self._lib.usdr_dms_create_ex2( + device, + sobj.encode("utf-8"), + dformat.encode("utf-8"), + ctypes.byref(chan_info), + pktsyms, + flags, + parameters.encode("utf-8") if parameters is not None else None, + ctypes.byref(out_stream), + ) + + if res == 0: + info_res, stream_info = self.usdr_dms_info(out_stream) + if info_res == 0: + self._stream_meta[self._stream_key(out_stream)] = (self._normalize_host_format(dformat), stream_info) + return int(res), out_stream + + def usdr_dms_destroy(self, stream: ctypes.c_void_p) -> int: + """Destroy stream handle.""" + res = int(self._lib.usdr_dms_destroy(stream)) + if res == 0: + self._stream_meta.pop(self._stream_key(stream), None) + return res + + def usdr_dms_info(self, stream: ctypes.c_void_p) -> Tuple[int, UsdrDmsNfo]: + """Get low-level stream information struct.""" + info = UsdrDmsNfo() + res = self._lib.usdr_dms_info(stream, ctypes.byref(info)) + return int(res), info + + def usdr_dms_op(self, stream: ctypes.c_void_p, command: int, tm: int = 0) -> int: + """Perform stream operation (`START`, `STOP`, timed variants).""" + return int(self._lib.usdr_dms_op(stream, command, tm)) + + def usdr_dms_sync(self, device: ctypes.c_void_p, synctype: str, streams: Iterable[ctypes.c_void_p]) -> int: + """Synchronize one or more streams using a named sync policy.""" + stream_list = list(streams) + arr = (ctypes.c_void_p * len(stream_list))(*stream_list) + return int(self._lib.usdr_dms_sync(device, synctype.encode("utf-8"), len(stream_list), arr)) + + def usdr_dms_recv( + self, + stream: ctypes.c_void_p, + buffers: Sequence[ctypes.Array], + timeout_ms: int, + with_info: bool = True, + ) -> Tuple[int, Optional[UsdrDmsRecvNfo]]: + """Receive into caller-provided ctypes buffers.""" + c_bufs = (ctypes.c_void_p * len(buffers))(*(ctypes.addressof(b) for b in buffers)) + print(c_bufs) + rx_nfo = UsdrDmsRecvNfo() if with_info else None + res = self._lib.usdr_dms_recv(stream, c_bufs, timeout_ms, ctypes.byref(rx_nfo) if rx_nfo else None) + return int(res), rx_nfo + + def usdr_dms_recv_numpy(self, stream: ctypes.c_void_p, timeout_ms: int, with_info: bool = True): + """Receive into numpy arrays based on stream host format metadata. + + Returns: + ``(res, arrays, rx_info)``. ``arrays`` is ``None`` when ``res != 0``. + """ + import numpy as np + + meta = self._stream_meta.get(self._stream_key(stream)) + if meta is None: + return -errno.EINVAL, None, None + + host_fmt, stream_info = meta + if host_fmt not in ("ci16", "cf32"): + return -errno.EOPNOTSUPP, None, None + + out = [ empty_aligned_complex64(stream_info.pktsyms) if host_fmt == "cf32" else np.empty((stream_info.pktsyms, 2), dtype=np.int16) for _ in range(stream_info.channels)] + raw_buffers_t = ctypes.c_void_p * stream_info.channels + raw_buffers = raw_buffers_t() + for i in range(stream_info.channels): + raw_buffers[i] = out[i].ctypes.data_as(ctypes.c_void_p) + + rx_nfo = UsdrDmsRecvNfo() if with_info else None + res = self._lib.usdr_dms_recv(stream, raw_buffers, timeout_ms, ctypes.byref(rx_nfo) if rx_nfo else None) + if res != 0: + return res, None, rx_nfo + + return 0, out, rx_nfo + + def usdr_dms_send(self, stream: ctypes.c_void_p, buffers: Sequence[ctypes.Array], samples: int, timestamp: int, timeout_ms: int) -> int: + """Send from caller-provided ctypes buffers.""" + c_bufs = (ctypes.c_void_p * len(buffers))(*(ctypes.addressof(b) for b in buffers)) + return int(self._lib.usdr_dms_send(stream, c_bufs, samples, timestamp, timeout_ms)) + + def usdr_dms_send_numpy(self, stream: ctypes.c_void_p, buffers, timestamp: int, timeout_ms: int, samples: Optional[int] = None) -> int: + """Send from numpy arrays validated against stream host format metadata.""" + import numpy as np + + meta = self._stream_meta.get(self._stream_key(stream)) + if meta is None: + return -errno.EINVAL + + host_fmt, stream_info = meta + if host_fmt not in ("ci16", "cf32"): + return -errno.EOPNOTSUPP + if len(buffers) != stream_info.channels: + return -errno.EINVAL + + np_buffers = [] + inferred_samples = None + for buf in buffers: + arr = np.asarray(buf) + if host_fmt == "ci16": + if arr.dtype != np.int16 or arr.ndim != 2 or arr.shape[1] != 2: + return -errno.EINVAL + n = arr.shape[0] + else: + if arr.dtype != np.complex64 or arr.ndim != 1: + return -errno.EINVAL + n = arr.shape[0] + if inferred_samples is None: + inferred_samples = int(n) + elif int(n) != inferred_samples: + return -errno.EINVAL + np_buffers.append(np.ascontiguousarray(arr)) + + send_samples = inferred_samples if samples is None else int(samples) + c_bufs = (ctypes.c_void_p * len(np_buffers))(*(ctypes.c_void_p(arr.ctypes.data) for arr in np_buffers)) + return int(self._lib.usdr_dms_send(stream, c_bufs, send_samples, timestamp, timeout_ms)) + + +class UsdrDevice: + """Create device wrapper and optionally set global libusdr log level. + Args: + device_string: Connection string passed to ``usdr_dmd_create_string``. + loglevel: Optional log level passed to ``usdrlog_setlevel(NULL, loglevel)`` before opening device. + lib: Optional pre-created low-level wrapper. + """ + def __init__(self, device_string: str = "", loglevel: Optional[int] = None, lib: Optional[UsdrLib] = None) -> None: + self._lib = lib or UsdrLib() + if loglevel is not None: + self._lib.usdrlog_setlevel(loglevel) + self._closed = True + res, dev = self._lib.usdr_dmd_create_string(device_string) + if res != 0: + raise UsdrException("usdr_dmd_create_string", res) + self._dev = dev + self._closed = False + + def __enter__(self) -> "UsdrDevice": + return self + + def __exit__(self, exc_type, exc, tb) -> None: + self.close() + + def __del__(self) -> None: + try: + self.close() + except Exception: + pass + + @property + def handle(self) -> ctypes.c_void_p: + return self._dev + + @staticmethod + def _raise_if_error(fn_name: str, code: int) -> None: + if code != 0: + raise UsdrException(fn_name, code) + + def _ensure_open(self) -> None: + if self._closed: + raise UsdrException("device_closed", -errno.EBADF) + + def close(self) -> None: + """Close device and release underlying low-level handle.""" + if not self._closed: + res = self._lib.usdr_dmd_close(self._dev) + self._raise_if_error("usdr_dmd_close", res) + self._closed = True + + def set_uint(self, path: str, value: int) -> None: + """Set arbitrary unsigned integer parameter by full path.""" + self._ensure_open() + res = self._lib.usdr_dme_set_uint(self._dev, path, value) + self._raise_if_error("usdr_dme_set_uint", res) + + def set_string(self, path: str, value: str) -> None: + """Set arbitrary string parameter by full path.""" + self._ensure_open() + res = self._lib.usdr_dme_set_string(self._dev, path, value) + self._raise_if_error("usdr_dme_set_string", res) + + def get_uint(self, path: str) -> int: + """Get arbitrary unsigned integer parameter by full path.""" + self._ensure_open() + res, value = self._lib.usdr_dme_get_uint(self._dev, path) + self._raise_if_error("usdr_dme_get_uint", res) + return value + + def get_u32(self, path: str) -> int: + """Get arbitrary 32-bit unsigned parameter by full path.""" + self._ensure_open() + res, value = self._lib.usdr_dme_get_u32(self._dev, path) + self._raise_if_error("usdr_dme_get_u32", res) + return value + + def set_samplerate(self, rate: int, rate_name: Optional[str] = None) -> None: + """Set device sample rate in samples per second. + + Mirrors `usdr_dm_create` behavior (`usdr_dmr_rate_set(dev, NULL, rate)`) when + `rate_name` is omitted. + + Args: + rate: Target sample rate in SPS (e.g. 50_000_000). + rate_name: Optional named rate domain; use ``None`` for master rate. + """ + self._ensure_open() + res = self._lib.usdr_dmr_rate_set(self._dev, rate=rate, rate_name=rate_name) + self._raise_if_error("usdr_dmr_rate_set", res) + + def _set_sdr0_uint(self, endpoint: str, value: int) -> None: + self.set_uint(f"/dm/sdr/0/{endpoint}", value) + + def _set_sdr0_string(self, endpoint: str, value: str) -> None: + self.set_string(f"/dm/sdr/0/{endpoint}", value) + + def set_rx_frequency(self, hz: int) -> None: + """Set RX LO frequency in Hz (`/dm/sdr/0/rx/freqency`).""" + self._set_sdr0_uint("rx/freqency", hz) + + def set_tx_frequency(self, hz: int) -> None: + """Set TX LO frequency in Hz (`/dm/sdr/0/tx/freqency`).""" + self._set_sdr0_uint("tx/freqency", hz) + + def set_tdd_frequency(self, hz: int) -> None: + """Set TDD frequency in Hz (`/dm/sdr/0/tdd/freqency`).""" + self._set_sdr0_uint("tdd/freqency", hz) + + def set_rx_bandwidth(self, hz: int) -> None: + """Set RX bandwidth in Hz (`/dm/sdr/0/rx/bandwidth`).""" + self._set_sdr0_uint("rx/bandwidth", hz) + + def set_tx_bandwidth(self, hz: int) -> None: + """Set TX bandwidth in Hz (`/dm/sdr/0/tx/bandwidth`).""" + self._set_sdr0_uint("tx/bandwidth", hz) + + def set_rx_gain_lna(self, gain_db: int) -> None: + """Set RX LNA gain (`/dm/sdr/0/rx/gain/lna`).""" + self._set_sdr0_uint("rx/gain/lna", gain_db) + + def set_rx_gain_vga(self, gain_db: int) -> None: + """Set RX VGA gain (`/dm/sdr/0/rx/gain/vga`).""" + self._set_sdr0_uint("rx/gain/vga", gain_db) + + def set_rx_gain_pga(self, gain_db: int) -> None: + """Set RX PGA gain (`/dm/sdr/0/rx/gain/pga`).""" + self._set_sdr0_uint("rx/gain/pga", gain_db) + + def set_tx_gain(self, gain_db: int) -> None: + """Set TX gain (`/dm/sdr/0/tx/gain`).""" + self._set_sdr0_uint("tx/gain", gain_db) + + def set_rx_path(self, path: str) -> None: + """Set RX path (`/dm/sdr/0/rx/path`). + + Typical values from `usdr_dm_create`: `rx_auto`, `rxl`, `rxw`, `rxh`, `adc`, + `rxl_lb`, `rxw_lb`, `rxh_lb`. + """ + self._set_sdr0_string("rx/path", path) + + def set_tx_path(self, path: str) -> None: + """Set TX path (`/dm/sdr/0/tx/path`). + + Typical values from `usdr_dm_create`: `tx_auto`, `txb1`, `txb2`, `txw`, `txh`. + """ + self._set_sdr0_string("tx/path", path) + + def configure_rf( + self, + *, + rx_freq: Optional[int] = None, + tx_freq: Optional[int] = None, + tdd_freq: Optional[int] = None, + rx_bandwidth: Optional[int] = None, + tx_bandwidth: Optional[int] = None, + rx_gain_lna: Optional[int] = None, + rx_gain_vga: Optional[int] = None, + rx_gain_pga: Optional[int] = None, + tx_gain: Optional[int] = None, + rx_path: Optional[str] = None, + tx_path: Optional[str] = None, + ) -> None: + """Apply multiple RF parameters from one call. + + This mirrors the `dev_data`/CLI controls used by `usdr_dm_create.c` and writes + corresponding `/dm/sdr/0/*` endpoints. Only non-``None`` values are applied. + """ + if rx_freq is not None: + self.set_rx_frequency(rx_freq) + if tx_freq is not None: + self.set_tx_frequency(tx_freq) + if tdd_freq is not None: + self.set_tdd_frequency(tdd_freq) + if rx_bandwidth is not None: + self.set_rx_bandwidth(rx_bandwidth) + if tx_bandwidth is not None: + self.set_tx_bandwidth(tx_bandwidth) + if rx_gain_lna is not None: + self.set_rx_gain_lna(rx_gain_lna) + if rx_gain_vga is not None: + self.set_rx_gain_vga(rx_gain_vga) + if rx_gain_pga is not None: + self.set_rx_gain_pga(rx_gain_pga) + if tx_gain is not None: + self.set_tx_gain(tx_gain) + if rx_path is not None: + self.set_rx_path(rx_path) + if tx_path is not None: + self.set_tx_path(tx_path) + + def create_stream( + self, + sobj: str, + dformat: str = "cf32", + channels: ChannelMap = None, + pktsyms: int = 4096, + flags: int = 0, + parameters: Optional[str] = None, + ) -> "UsdrStream": + """Create and return a high-level :class:`UsdrStream` wrapper. + + Args: + channels: Either list of int channel IDs (``phys_nums``), list of str + physical channel names (``phys_names``), or ``None`` (defaults to ``[0]``). + + Raises: + UsdrException: When channel type is invalid or stream creation fails. + """ + self._ensure_open() + vres, _ = self._lib._validate_channels(channels) + self._raise_if_error("create_stream(channels)", vres) + res, handle = self._lib.usdr_dms_create_ex2(self._dev, sobj, dformat, channels, pktsyms, flags, parameters) + self._raise_if_error("usdr_dms_create_ex2", res) + return UsdrStream(self, handle) + + +class UsdrStream: + """High-level stream wrapper bound to a UsdrDevice.""" + + def __init__(self, device: UsdrDevice, handle: ctypes.c_void_p) -> None: + self._device = device + self._lib = device._lib + self._stream = handle + self._closed = False + + def __enter__(self) -> "UsdrStream": + return self + + def __exit__(self, exc_type, exc, tb) -> None: + self.close() + + def __del__(self) -> None: + try: + self.close() + except Exception: + pass + + @property + def handle(self) -> ctypes.c_void_p: + return self._stream + + @staticmethod + def _raise_if_error(fn_name: str, code: int) -> None: + if code != 0: + raise UsdrException(fn_name, code) + + def _ensure_open(self) -> None: + if self._closed: + raise UsdrException("stream_closed", -errno.EBADF) + + def close(self) -> None: + """Close stream and release underlying low-level handle.""" + if not self._closed: + res = self._lib.usdr_dms_destroy(self._stream) + self._raise_if_error("usdr_dms_destroy", res) + self._closed = True + + def info(self) -> UsdrStreamInfo: + """Return stream metadata as a pure-Python :class:`UsdrStreamInfo` object.""" + self._ensure_open() + res, info = self._lib.usdr_dms_info(self._stream) + self._raise_if_error("usdr_dms_info", res) + return UsdrStreamInfo.from_ctypes(info) + + def op(self, command: int, tm: int = 0) -> None: + """Run stream operation command (start/stop/timed variants).""" + self._ensure_open() + res = self._lib.usdr_dms_op(self._stream, command, tm) + self._raise_if_error("usdr_dms_op", res) + + def sync(self, synctype: str = "off", *other_streams: "UsdrStream") -> None: + """Synchronize this stream together with optional peer streams.""" + self._ensure_open() + streams = [self._stream] + for st in other_streams: + st._ensure_open() + streams.append(st._stream) + res = self._lib.usdr_dms_sync(self._device.handle, synctype, streams) + self._raise_if_error("usdr_dms_sync", res) + + def recv(self, timeout_ms: int, with_info: bool = True): + """Receive one block as numpy arrays; returns ``(arrays, recv_info)``.""" + self._ensure_open() + res, arrays, rx_nfo = self._lib.usdr_dms_recv_numpy(self._stream, timeout_ms=timeout_ms, with_info=with_info) + self._raise_if_error("usdr_dms_recv", res) + return arrays, rx_nfo + + def send(self, buffers, timestamp: int = 0, timeout_ms: int = 1000, samples: Optional[int] = None) -> None: + """Send one block from numpy arrays using stored stream format metadata.""" + self._ensure_open() + res = self._lib.usdr_dms_send_numpy(self._stream, buffers, timestamp=timestamp, timeout_ms=timeout_ms, samples=samples) + self._raise_if_error("usdr_dms_send", res) diff --git a/src/tools/usdr_dm_create.c b/src/tools/usdr_dm_create.c index eb50e5ba..5733530b 100644 --- a/src/tools/usdr_dm_create.c +++ b/src/tools/usdr_dm_create.c @@ -68,6 +68,10 @@ struct tx_thread_input_s float gain; double start_phase; double delta_phase; + + //chirp_gen params + double chirp_freq0, chirp_freq1; + int32_t chirp_steps; }; typedef struct tx_thread_input_s tx_thread_input_t; @@ -239,12 +243,65 @@ void* freq_gen_thread_ci16_lut(void* obj) #define USE_WVLT_SINCOS #define MAX_TXGEN_CI16_AMPL 32760 +static void* chirp_gen_thread_ci16(void* obj) +{ +#ifndef USE_WVLT_SINCOS + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Not implemented"); + return NULL; +#endif + + tx_thread_input_t* inp = (tx_thread_input_t*)obj; + + const unsigned p = inp->chan; + const unsigned tx_get_samples = inp->samples_count; + const int16_t gain = DBFS_TO_AMPLITUDE(inp->gain, MAX_TXGEN_CI16_AMPL); + + const bool upchirp = inp->chirp_steps > 0; + USDR_LOG(LOG_TAG, USDR_LOG_WARNING, "Using TX ci16 CHIRP sinus generator with USE_WVLT_SINCOS opt @ ch#%d F1:%.6f MHz F2:%.6f MHz GAIN:(%.2fdBFS = %d)", + p, inp->chirp_freq0 / 1000000.f, inp->chirp_freq1 / 1000000.f, inp->gain, gain); + USDR_LOG(LOG_TAG, USDR_LOG_WARNING, "CHIRP steps count: %d [%s]", inp->chirp_steps, (upchirp ? "UP_CHIRP":"DOWN_CHIRP")); + USDR_LOG(LOG_TAG, USDR_LOG_WARNING, "CHIRP period: %.6f s (having sr:%u Ms)", (double)inp->chirp_steps / (double)inp->samplerate, inp->samplerate / 1000000); + + int32_t phase = WVLT_CONVPHASE_F32_I32(inp->start_phase); + + const int32_t dp0 = WVLT_CONVPHASE_F32_I32(inp->chirp_freq0 / inp->samplerate); + const int32_t dp1 = WVLT_CONVPHASE_F32_I32(inp->chirp_freq1 / inp->samplerate); + int32_t delta_phase_arr[] = { dp0, dp1 }; + int32_t delta_phase = inp->chirp_steps >= 0 ? dp0 : dp1; + + while (!s_stop && !thread_stop) + { + unsigned idx = ring_buffer_pwait(tbuff[p], 100000); + if (idx == IDX_TIMEDOUT) + continue; + + char* data = ring_buffer_at(tbuff[p], idx); + + tx_header_t* hdr = (tx_header_t*)data; + hdr->len = tx_get_samples * sizeof(uint16_t) * 2; + hdr->flags = TXF_NONE; + + int16_t *iqp = (int16_t *)(data + sizeof(tx_header_t)); + + wvlt_sincos_i16_interleaved_chirp(&phase, &delta_phase, delta_phase_arr, inp->chirp_steps, + gain, true/*invert sin*/, false/*invert cos*/, iqp, tx_get_samples); + ring_buffer_ppost(tbuff[p]); + } + + return NULL; +} + /* * Thread function - Sine generator to TX stream (ci16) */ void* freq_gen_thread_ci16(void* obj) { tx_thread_input_t* inp = (tx_thread_input_t*)obj; + if(inp->chirp_steps) + { + return chirp_gen_thread_ci16(obj); + } + const unsigned p = inp->chan; const unsigned tx_get_samples = inp->samples_count; const int16_t gain = DBFS_TO_AMPLITUDE(inp->gain, MAX_TXGEN_CI16_AMPL); @@ -344,16 +401,17 @@ void* freq_gen_thread_cf32(void* obj) */ bool print_device_temperature(pdm_dev_t dev) { - uint64_t temp; + uint64_t temp = -1; int res = usdr_dme_get_uint(dev, "/dm/sensor/temp", &temp); + int traw = (temp & 0xffffffff); if (res) { - USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to get device temperature: errno %d", res); + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to get device temperature: errno %d\n", res); return false; - } else if (temp > 65535) { - USDR_LOG(LOG_TAG, USDR_LOG_WARNING, "The temperature sensor doen't seem to be supported by your hardware - or your device has already melted)"); + } else if (traw > 65535 || traw < -65535) { + USDR_LOG(LOG_TAG, USDR_LOG_WARNING, "The temperature sensor reports incorrect value!\n"); } else { - USDR_LOG(LOG_TAG, USDR_LOG_INFO, "Temp = %.1f C", temp / 256.0); + USDR_LOG(LOG_TAG, USDR_LOG_INFO, "Temp = %.1f C\n", traw / 256.0); } return true; } @@ -370,6 +428,8 @@ enum { DD_TX_GAIN, DD_TX_PATH, DD_RX_PATH, + + DD_TX_GAIN_LB, //Must be followed by DD_TX_PATH & DD_RX_PATH }; /* @@ -416,9 +476,12 @@ static void usage(int severity, const char* me) "\t[-g comma-separated list of sin generator gains (FP values, dBFS -100..0)] \n" "\t[-X ] \n" "\t[-z ] \n" + "\t[-b TX packet precharge count before doing RX (valid with -T flag) [16]\n" "\t[-l loglevel [3(INFO)]] \n" "\t[-G calibration [algo#]] \n" "\t[-Z param1=value1,param2=value2,...] \n" + "\t[-m ] \n" + "\t[-M comma-separated list of CHIRP generator params (each channel specified as ::)] \n" "\t[-h ]", me); } @@ -637,6 +700,8 @@ int main(UNUSED int argc, UNUSED char** argv) unsigned calibrate = 0; param_list_t extra_params[32]; unsigned extra_param_len = 0; + int tx_pkt_precharge = 16; + bool use_chirp_gen = false; memset(rx_thread_inputs, 0, sizeof(rx_thread_inputs)); memset(tx_thread_inputs, 0, sizeof(tx_thread_inputs)); @@ -646,6 +711,9 @@ int main(UNUSED int argc, UNUSED char** argv) inp->start_phase = -10; inp->delta_phase = -10; inp->gain = INT16_MIN; + inp->chirp_freq0 = 0.f; + inp->chirp_freq1 = 0.f; + inp->chirp_steps = 0; } channel_info_init(&chl_rx); @@ -654,10 +722,10 @@ int main(UNUSED int argc, UNUSED char** argv) //Device parameters // { endpoint, default_value, ignore flag, stop_on_fail flag } struct dme_findsetv_data dev_data[] = { - [DD_RX_FREQ] = { "rx/freqency", 900e6, true, true }, - [DD_TX_FREQ] = { "tx/freqency", 920e6, true, true }, + [DD_RX_FREQ] = { "rx/frequency", 900e6, true, true }, + [DD_TX_FREQ] = { "tx/frequency", 920e6, true, true }, - [DD_TDD_FREQ] = { "tdd/freqency", 910e6, true, true }, + [DD_TDD_FREQ] = { "tdd/frequency", 910e6, true, true }, [DD_RX_BANDWIDTH] = { "rx/bandwidth", 1e6, true, true }, [DD_TX_BANDWIDTH] = { "tx/bandwidth", 1e6, true, true }, @@ -670,14 +738,16 @@ int main(UNUSED int argc, UNUSED char** argv) [DD_RX_PATH] = { "rx/path", (uintptr_t)"rx_auto", false, true }, [DD_TX_PATH] = { "tx/path", (uintptr_t)"tx_auto", false, true }, + [DD_TX_GAIN_LB] = { "tx/gain/lb", 0, true, true }, }; - //primary logging for proper usage() call - may be overriden below + //primary logging for proper usage() call - may be overridden below usdrlog_setlevel(NULL, loglevel); //set colored log output usdrlog_enablecolorize(NULL); - while ((opt = getopt(argc, argv, "B:U:u:R:Qq:e:E:w:W:y:Y:l:S:O:C:F:f:c:r:i:XtTNAoha:D:s:p:P:z:I:x:j:H:d:g:JG:Z:")) != -1) { + // Still available: kvVL + while ((opt = getopt(argc, argv, "b:B:U:u:R:Qq:e:E:w:W:y:Y:l:S:O:C:F:f:c:r:i:XtTNAoha:D:s:p:P:z:I:x:j:H:d:g:JG:Z:K:mM:")) != -1) { switch (opt) { //Time-division duplexing (TDD) frequency case 'q': dev_data[DD_TDD_FREQ].value = atof(optarg); dev_data[DD_TDD_FREQ].ignore = false; break; @@ -701,6 +771,8 @@ int main(UNUSED int argc, UNUSED char** argv) case 'u': dev_data[DD_RX_GAIN_PGA].value = atoi(optarg); dev_data[DD_RX_GAIN_PGA].ignore = false; break; //RX VGA gain case 'U': dev_data[DD_RX_GAIN_VGA].value = atoi(optarg); dev_data[DD_RX_GAIN_VGA].ignore = false; break; + //TX loopback gain + case 'K': dev_data[DD_TX_GAIN_LB].value = atoi(optarg); dev_data[DD_TX_GAIN_LB].ignore = false; break; case 'G': calibrate = atoi(optarg); break; @@ -720,6 +792,9 @@ int main(UNUSED int argc, UNUSED char** argv) case 'x': fref = atof(optarg); break; + case 'b': + tx_pkt_precharge = atoi(optarg); + break; //Calibration frequency case 'B': cal_freq = atof(optarg); @@ -897,6 +972,42 @@ int main(UNUSED int argc, UNUSED char** argv) case 'Z': extra_param_len = parse_param_list(optarg, SIZEOF_ARRAY(extra_params), extra_params); break; + case 'm': + use_chirp_gen = true; + break; + case 'M': + { + char* pt_end; + char *pt = strtok_r(optarg, ",", &pt_end); + unsigned i = 0; + + while(pt && i < MAX_CHS) + { + char *chirp_pt_end; + char *chirp_pt = strtok_r(pt, ":", &chirp_pt_end); + if(!chirp_pt) + exit(EXIT_FAILURE); + else + tx_thread_inputs[i].chirp_steps = atoi(chirp_pt); + + chirp_pt = strtok_r(NULL, ":", &chirp_pt_end); + if(!chirp_pt) + exit(EXIT_FAILURE); + else + tx_thread_inputs[i].chirp_freq0 = atof(chirp_pt); + + chirp_pt = strtok_r(NULL, ":", &chirp_pt_end); + if(!chirp_pt) + exit(EXIT_FAILURE); + else + tx_thread_inputs[i].chirp_freq1 = atof(chirp_pt); + + ++i; + pt = strtok_r(NULL, ",", &pt_end); + } + + break; + } //Show usage case 'h': usdrlog_disablecolorize(NULL); @@ -1054,7 +1165,7 @@ int main(UNUSED int argc, UNUSED char** argv) res = res ? res : usdr_dms_info(usds_rx, &snfo_rx); if (res) { USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to get RX data stream info: errno %d", res); - goto dev_close; + if (stop_on_error) goto dev_close; } else { s_rx_blksampl = snfo_rx.pktsyms; s_rx_blksz = snfo_rx.pktbszie; @@ -1078,7 +1189,7 @@ int main(UNUSED int argc, UNUSED char** argv) res = res ? res : usdr_dms_info(usds_tx, &snfo_tx); if (res) { USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to get TX data stream info: errno %d", res); - goto dev_close; + if (stop_on_error) goto dev_close; } else { s_tx_blksz = snfo_tx.pktbszie; s_tx_blksampl = snfo_tx.pktsyms; @@ -1105,6 +1216,9 @@ int main(UNUSED int argc, UNUSED char** argv) static double start_phase[] = { 0, 0.5, 0.25, 0.125 }; static double start_dphase[] = { 0.3333333333333333333333333, 0.02, 0.03, 0.04 }; static int16_t gains[] = {0.0, 0.0, 0.0, 0.0}; + static int32_t chirp_steps[] = { -100007, 100007, -1000007, 1000007 }; + static int32_t chirp_freq0[] = { -333333, -666666, -1E6, -10E6 }; + static int32_t chirp_freq1[] = { 333333, 666666, 1E6, 10E6 }; for(unsigned i = 0; i < tx_bufcnt; ++i) { tx_thread_input_t* inp = &tx_thread_inputs[i]; @@ -1114,6 +1228,13 @@ int main(UNUSED int argc, UNUSED char** argv) inp->start_phase = inp->start_phase > -1 ? inp->start_phase : start_phase[i % (sizeof(start_phase) / sizeof(*start_phase))]; inp->delta_phase = inp->delta_phase > -1 ? inp->delta_phase : start_dphase[i % (sizeof(start_dphase) / sizeof(*start_dphase))]; inp->gain = inp->gain != INT16_MIN ? inp->gain : gains[i % (sizeof(gains) / sizeof(*gains))]; + + if(use_chirp_gen) + { + inp->chirp_steps = inp->chirp_steps ? inp->chirp_steps : chirp_steps[i % (sizeof(chirp_steps) / sizeof(*chirp_steps))]; + inp->chirp_freq0 = (inp->chirp_freq0 != 0.f) ? inp->chirp_freq0 : chirp_freq0[i % (sizeof(chirp_freq0) / sizeof(*chirp_freq0))]; + inp->chirp_freq1 = (inp->chirp_freq1 != 0.f) ? inp->chirp_freq1 : chirp_freq1[i % (sizeof(chirp_freq1) / sizeof(*chirp_freq1))]; + } } for(unsigned i = 0; i < MAX_CHS; ++i) { @@ -1121,6 +1242,25 @@ int main(UNUSED int argc, UNUSED char** argv) i, tx_thread_inputs[i].start_phase, tx_thread_inputs[i].delta_phase); } + for(unsigned i = 0; i < tx_bufcnt && use_chirp_gen; ++i) { + USDR_LOG(LOG_TAG, USDR_LOG_DEBUG, "TX CHIRP_GEN CH#%2d FROM_FREQ:%.4f TO_FREQ:%.4f STEPS:%d", + i, tx_thread_inputs[i].chirp_freq0, tx_thread_inputs[i].chirp_freq1, tx_thread_inputs[i].chirp_steps); + + if(tx_thread_inputs[i].chirp_freq0 >= tx_thread_inputs[i].chirp_freq1) + { + USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "CH#%2d CHIRP params error: freq0 >= freq1 [%.2f >= %.2f]", + i, tx_thread_inputs[i].chirp_freq0, tx_thread_inputs[i].chirp_freq1); + goto dev_close; + } + + if(abs(tx_thread_inputs[i].chirp_steps % 8) != 7) + { + USDR_LOG(LOG_TAG, USDR_LOG_WARNING, "CH#%2d CHIRP steps count[%d]: recommended condition - step %% 8 = 7(-1)", + i, tx_thread_inputs[i].chirp_steps); + tx_thread_inputs[i].chirp_steps = (tx_thread_inputs[i].chirp_steps / 8 ) * 8 + 7; + } + } + //Create TX buffers and threads if (dotx) { unsigned fidx = 1; @@ -1253,6 +1393,11 @@ int main(UNUSED int argc, UNUSED char** argv) //Set device parameters from the dev_data struct (see above) if (!noinit) { + if (!stop_on_error) { + for (unsigned i = 0; i < SIZEOF_ARRAY(dev_data); i++) { + dev_data[i].stopOnFail = false; + } + } res = usdr_dme_findsetv_uint(dev, "/dm/sdr/0/", SIZEOF_ARRAY(dev_data), dev_data); if (res) { USDR_LOG(LOG_TAG, USDR_LOG_ERROR, "Unable to set device parameters: errno %d", res); @@ -1301,8 +1446,19 @@ int main(UNUSED int argc, UNUSED char** argv) for (unsigned i = 0; !s_stop && (i < count); i++) { - if(dotx && !do_transmit(usds_tx, &stm, &snfo_tx, nots, i, &tx_samples_cnt, &txstat)) - goto stop; + if (tx_pkt_precharge < 0) { + if (tx_pkt_precharge != 0) { + ++tx_pkt_precharge; + } + } else { + if (dotx && !do_transmit(usds_tx, &stm, &snfo_tx, nots, i, &tx_samples_cnt, &txstat)) + goto stop; + + if (tx_pkt_precharge != 0) { + --tx_pkt_precharge; + continue; + } + } if(dorx && !do_receive(usds_rx, i, &rxstat)) goto stop; diff --git a/src/tools/usdr_dm_gpsdo.c b/src/tools/usdr_dm_gpsdo.c new file mode 100644 index 00000000..eeac4e21 --- /dev/null +++ b/src/tools/usdr_dm_gpsdo.c @@ -0,0 +1,383 @@ +// Copyright (c) 2025 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#include +#endif + +// PID parameters +#define KP 0.75 // Proportional gain +#define KI 0.02 // Integral gain +#define KD 0.0075 // Derivative gain +#define DT 1.0 // Update interval (1 second) +#define HZ_PER_STEP 0.01 +#define INTEGRAL_MAX 1e6 +#define OUTPUT_MAX 1e-3 +#define DEVIATION_MAX 7e-6 + +typedef struct { + uint32_t bits; + uint32_t center; + uint32_t min_value; + uint32_t max_value; + double hz_per_step; // Hz per DAC step +} DAC; + +int dac_init(DAC *dac, uint32_t dac_bits, uint32_t freq) +{ + if (!dac) + return 1; + if (dac_bits < 8 || dac_bits > 16) + return 2; + dac->bits = dac_bits; + dac->min_value = 0; + dac->max_value = (1 << dac_bits) - 1; + dac->center = dac->max_value >> 1; + dac->hz_per_step = 2 * DEVIATION_MAX * (double)freq / dac->max_value; + return 0; +} + +// DAC control +uint32_t set_dac_value(pdm_dev_t dev, const DAC *dac, uint32_t dac_offset, double step) +{ + static const char *dac_vctcxo_path = "/dm/sdr/0/dac_vctcxo"; + static uint64_t old_dac_value = 0; + // double dac_delta = freq_offset / dac_gain; // Δdac = Δf / gain + // uint32_t dac_value = (uint32_t) (dac_mid + dac_delta); + uint32_t dac_value = (uint32_t) lround(dac_offset + step); + if (dac_value < 0) + dac_value = 0; + if (dac_value > dac->max_value) + dac_value = dac->max_value; + + if (dac_value != old_dac_value) { + // Send to DAC + printf( + "DAC value: %d (%d bits, dac_offset %d, step %f)\n", dac_value, dac->bits, dac_offset, step); + const int res = usdr_dme_set_uint(dev, dac_vctcxo_path, (uint64_t)dac_value); + if (res) { + fprintf(stderr, "Unable to set vctcxo dac value: errno %d\n", res); + return res; + } + old_dac_value = dac_value; + } + return dac_value; +} + +typedef struct +{ + double kp; + double ki; + double kd; + double integral; + double prev_error; + double integral_max; + double output_max; +} PID; + +void pid_init(PID *p, double kp, double ki, double kd, double integral_max, double output_max) +{ + p->kp = kp; + p->ki = ki; + p->kd = kd; + p->integral = 0.0; + p->prev_error = 0.0; + p->integral_max = integral_max; + p->output_max = output_max; +} + +double pid_update(PID *p, double error, double dt) +{ + double P = p->kp * error; + p->integral += error * dt; + if (p->integral > p->integral_max) + p->integral = p->integral_max; + if (p->integral < -p->integral_max) + p->integral = -p->integral_max; + double I = p->ki * p->integral; + double D = p->kd * (error - p->prev_error) / dt; + p->prev_error = error; + double out = P + I + D; + if (out > p->output_max) + out = p->output_max; + if (out < -p->output_max) + out = -p->output_max; + return out; +} + +// Stub for getting PPS count and measured frequency from FPGA +uint8_t get_pps_count(uint64_t ppsparm) +{ + return (ppsparm >> 28) & 0xf; +} + +uint32_t get_measured_freq(uint64_t ppsparm) +{ + return ppsparm & 0xfffffff; +} + +void show_usage(const char *procname) +{ + fprintf(stderr, "Usage %s:\n", procname); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -d - device\n"); + // fprintf(stderr, " -f - target frequency of oscillator [26e6] Hz\n"); + fprintf(stderr, " -o - external oscilator frequency [25e6] Hz\n"); + fprintf(stderr, " -r - target samplerate [4e6] Samples\n"); + fprintf(stderr, " -h - this help\n"); +} + +int main(int argc, char **argv) +{ + const char *pps_path = "/dm/sensor/freqpps";//"/dm/sdr/0/clkmeas"; + const char *ext_osc_path = "/dm/sdr/refclk/path"; + const char *ext_osc_parm = "external"; + const char *ext_freq_path = "/dm/sdr/refclk/frequency"; + const uint64_t ext_freq_value = 25000000ull; + const double dt = 1.0; // Update interval (1 second) + const int dac_bits = 16; // DAC bitness + + int res, opt; + uint64_t ppsparm = 0; + pdm_dev_t dev; + const char *device = "";//"fe=exm2pe:gps_on:osc_on"; + // double target_freq = 26e6; // Target frequency, 26 MHz + double osc_freq = (double)ext_freq_value; + double target_freq = 4e6; // Target samplerate, 4 Msps + bool new_holdover_msg = true; + + while ((opt = getopt(argc, argv, "d:f:o:r:h")) != -1) { + switch (opt) { + case 'd': + device = optarg; + break; + // case 'f': + // target_freq = atof(optarg); + // if (target_freq < 100.0) + // target_freq *= 1e6; + // break; + case 'o': + osc_freq = atof(optarg); + if (osc_freq < 100.0) + osc_freq *= 1e6; + break; + case 'r': + target_freq = atof(optarg); + if (target_freq < 100.0) + target_freq *= 1e6; + break; + case 'h': + default: + show_usage(argv[0]); + return 1; + } + } + + usdrlog_setlevel(NULL, USDR_LOG_WARNING); + usdrlog_enablecolorize(NULL); + + res = usdr_dmd_create_string(device, &dev); + if (res) { + fprintf(stderr, "Unable to create device: errno %d\n", res); + return 1; + } + + res = usdr_dme_set_uint(dev, ext_freq_path, (uint64_t)osc_freq); + if (res) { + fprintf(stderr, "Unable to setup external oscilator frequency %" PRId64 ": errno %d\n", ext_freq_value, res); + usdr_dmd_close(dev); + return 1; + } + + res = usdr_dme_set_uint(dev, ext_osc_path, (uint64_t) ext_osc_parm); + if (res) { + fprintf(stderr, "Unable to select external oscilator: errno %d\n", res); + usdr_dmd_close(dev); + return 1; + } + + res = usdr_dmr_rate_set(dev, NULL, (uint64_t) target_freq); + if (res) { + fprintf(stderr, "Unable to set device rate: errno %d", res); + usdr_dmd_close(dev); + return 1; + } + + pusdr_dms_t usds_rx = NULL; + res = usdr_dms_create(dev, "/ll/srx/0", "ci16", 1, 4096, &usds_rx); + if (res) { + fprintf(stderr, "Unable to initialize RX data stream: errno %d", res); + usdr_dmd_close(dev); + return 1; + } + + res = usdr_dms_sync(dev, "any", 1, &usds_rx); + if (res) { + fprintf(stderr, "Unable to sync data streams: errno %d", res); + usdr_dmd_close(dev); + return 1; + } + + res = usds_rx ? usdr_dms_op(usds_rx, USDR_DMS_START, 0) : -EPROTONOSUPPORT; + if (res) { + fprintf(stderr, "Unable to start RX data stream: errno %d", res); + usdr_dmd_close(dev); + return 1; + } + + res = usdr_dme_get_uint(dev, pps_path, &ppsparm); + if (res) { + fprintf(stderr, "Unable to get pps: errno %d\n", res); + usdr_dmd_close(dev); + return 1; + } + // Previous PPS count for trigger detection + int prev_pps_count = get_pps_count(ppsparm); + + DAC dac; + res = dac_init(&dac, dac_bits, target_freq); + if (res) { + fprintf(stderr, "Unable to init DAC for dac bits %d: errno %d", dac_bits, res); + usdr_dmd_close(dev); + return 1; + } + + PID pid; + pid_init(&pid, KP, KI, KD, INTEGRAL_MAX, OUTPUT_MAX); + + + struct timespec start_time; + clock_gettime(CLOCK_MONOTONIC, &start_time); +#ifdef __linux__ + // Create timerfd for 100 ms polling + int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (timer_fd == -1) { + perror("timerfd_create"); + return 1; + } + + struct itimerspec timer_spec; + timer_spec.it_value.tv_sec = 0; + timer_spec.it_value.tv_nsec = 100000000; // 100 ms + timer_spec.it_interval = timer_spec.it_value; // Repeat every 100 ms + if (timerfd_settime(timer_fd, 0, &timer_spec, NULL) == -1) { + perror("timerfd_settime"); + + return 1; + } + + struct pollfd fds[1]; + fds[0].fd = timer_fd; + fds[0].events = POLLIN; +#endif + + const double holdover_interval = 2 * dt; + uint32_t dac_offset = dac.center; // initial + double measured_freq = 0.0, error = 0.0; + + while (true) { + // Wait for timer event +#ifdef _WIN32 + Sleep(100); + { +#elif defined(__APPLE__) + usleep(100000); // 100 ms + { +#else + int ret = poll(fds, 1, -1); + if (ret == -1) { + perror("poll"); + break; + } + + if (fds[0].revents & POLLIN) { + uint64_t expirations; + read(timer_fd, &expirations, sizeof(expirations)); // Clear timer event +#endif + res = usdr_dme_get_uint(dev, pps_path, &ppsparm); + if (res) { + fprintf(stderr, "Unable to get pps: errno %d\n", res); + break; + } + + const uint8_t current_pps_count = get_pps_count(ppsparm); + + // int current_pps_count = get_pps_count(); + + // Check if PPS count changed (trigger for update) + if (current_pps_count != prev_pps_count) { + const uint8_t pps_delta = current_pps_count < prev_pps_count + ? current_pps_count + 0x10 - prev_pps_count + : current_pps_count - prev_pps_count; + measured_freq = (double)get_measured_freq(ppsparm); + error = (measured_freq - target_freq) / target_freq; + + printf("PPS=0x%" PRIx64 ", pps_count=%d, measured_freq = %.0f, error = %.3f ppm\n", + ppsparm, + current_pps_count, + measured_freq, + error * 1e6); + + if (error > 0.02) { + printf("Error more than 2%%, skip iteration\n"); + continue; + } + + double pout = pid_update(&pid, error, dt); + // map pid output (fractional) to Hz correction + double freq_corr_hz = -pout * target_freq; // desired Hz change + double steps_cmd = freq_corr_hz / dac.hz_per_step; + printf("POUT=%f, freq_corr_hz=%f, steps_cmd=%f\n", pout, freq_corr_hz, steps_cmd); + // limit steps + if (steps_cmd > 2000.0) + steps_cmd = 2000.0; + if (steps_cmd < -2000.0) + steps_cmd = -2000.0; + dac_offset = set_dac_value(dev, &dac, dac_offset, steps_cmd); + + prev_pps_count = current_pps_count; + + // Reset timer for holdover tracking + clock_gettime(CLOCK_MONOTONIC, &start_time); + } + + // Check elapsed time for holdover + struct timespec current_time; + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + double elapsed = (current_time.tv_sec - start_time.tv_sec) + + (current_time.tv_nsec - start_time.tv_nsec) / 1e9; + if (elapsed > holdover_interval) { + // Apply last known offset or extrapolate + if (new_holdover_msg) { + printf("No PPS trigger for more than %.1f seconds - entering holdover mode\n", holdover_interval); + new_holdover_msg = false; + } + } else { + if (!new_holdover_msg) { + printf("Found PPS trigger\n"); + new_holdover_msg = true; + } + } + } + } + +#ifdef __linux__ + close(timer_fd); +#endif + usdr_dmd_close(dev); + + return 0; +} diff --git a/src/tools/usdr_dm_sensors.c b/src/tools/usdr_dm_sensors.c index b2588167..2814281e 100644 --- a/src/tools/usdr_dm_sensors.c +++ b/src/tools/usdr_dm_sensors.c @@ -47,7 +47,7 @@ int main(UNUSED int argc, UNUSED char** argv) enum sensor_type type = 0; const char *list_pattern = NULL; - while ((opt = getopt(argc, argv, "d:i:l:s:S:r:c:t:L:")) != -1) { + while ((opt = getopt(argc, argv, "D:d:i:l:s:S:r:c:t:L:")) != -1) { switch (opt) { case 't': if (strcmp(optarg, "temp") == 0) @@ -61,6 +61,7 @@ int main(UNUSED int argc, UNUSED char** argv) exit(1); } break; + case 'D': case 'd': device = optarg; break; diff --git a/src/tools/usdr_flash.c b/src/tools/usdr_flash.c index 474b560b..8ebb28e0 100644 --- a/src/tools/usdr_flash.c +++ b/src/tools/usdr_flash.c @@ -36,6 +36,7 @@ enum flash_action { ACTION_READBACK, ACTION_WRITE, ACTION_INFO, + ACTION_ERASE_MASTER, }; int main(int argc, char** argv) @@ -48,8 +49,10 @@ int main(int argc, char** argv) bool force = false; bool golden = false; bool corrupt = false; + bool verbose = false; uint32_t curfwid; bool no_device = false; + bool crc_check = true; uint64_t master_offset = MASTER_IMAGE_OFF; uint64_t qspi_base = 10; @@ -59,7 +62,7 @@ int main(int argc, char** argv) usdrlog_setlevel(NULL, USDR_LOG_WARNING); usdrlog_enablecolorize(NULL); - while ((opt = getopt(argc, argv, "U:l:i:w:r:FGC")) != -1) { + while ((opt = getopt(argc, argv, "U:l:i:w:r:FGCvkE")) != -1) { switch (opt) { case 'U': busname = optarg; @@ -88,6 +91,15 @@ int main(int argc, char** argv) case 'C': corrupt = true; break; + case 'v': + verbose = true; + break; + case 'k': + crc_check = false; + break; + case 'E': + rdwr = ACTION_ERASE_MASTER; + break; default: fprintf(stderr, "Usage: %s [-U device_bus] [-l loglevel] [-r filename | -w filename | -i filename] [-G]\n", argv[0]); @@ -130,8 +142,12 @@ int main(int argc, char** argv) } if (!(no_device)) { - usdr_device_vfs_obj_val_get_u64(dev->pdev, "/ll/qspi_flash/master_off", &master_offset); - usdr_device_vfs_obj_val_get_u64(dev->pdev, "/ll/qspi_flash/base", &qspi_base); + res = res ? res : usdr_device_vfs_obj_val_get_u64(dev->pdev, "/ll/qspi_flash/master_off", &master_offset); + res = res ? res : usdr_device_vfs_obj_val_get_u64(dev->pdev, "/ll/qspi_flash/base", &qspi_base); + + if (res) { + fprintf(stderr, "Unable to get board memory configuration, assuming MASTER_OFF=%x!\n", (unsigned)master_offset); + } } usleep(1000); @@ -160,12 +176,12 @@ int main(int argc, char** argv) return 4; } - res = (no_device) ? 0 : xlnx_btstrm_parse_header((const uint32_t* )outb, 256/4, &image); + res = (no_device) ? 0 : xlnx_btstrm_parse_header_ex((const uint32_t* )outb, 256/4, &image, XLNX_BSTRM_ALLOW_CROP); if (res) { fprintf(stderr, "It looks like the FPGA G image is corrupted! res=%d\n", res); return 4; } - res = (no_device) ? 0 : xlnx_btstrm_parse_header((const uint32_t* )(outb + 256), 256/4, &image_master); + res = (no_device) ? 0 : xlnx_btstrm_parse_header_ex((const uint32_t* )(outb + 256), 256/4, &image_master, XLNX_BSTRM_ALLOW_CROP); if (res) { fprintf(stderr, "It looks like the FPGA M image is corrupted! res=%d\n", res); } else { @@ -186,32 +202,43 @@ int main(int argc, char** argv) if (rdwr == ACTION_WRITE || rdwr == ACTION_INFO) { FILE* w = fopen(filename, "rb"); if (w == NULL) { - fprintf(stderr, "Unabe to read file '%s': %s\n", filename, strerror(errno)); + fprintf(stderr, "Unable to read file '%s': %s\n", filename, strerror(errno)); return 3; } res = fseek(w, 0, SEEK_END); if (res) { - fprintf(stderr, "Unabe to seek file '%s': %s\n", filename, strerror(errno)); + fprintf(stderr, "Unable to seek file '%s': %s\n", filename, strerror(errno)); return 3; } total_length = ftell(w); res = fseek(w, 0, SEEK_SET); if (res) { - fprintf(stderr, "Unabe to seek file '%s': %s\n", filename, strerror(errno)); + fprintf(stderr, "Unable to seek file '%s': %s\n", filename, strerror(errno)); return 3; } res = fread(outa, 1, total_length, w); if ((unsigned)res != total_length) { - fprintf(stderr, "Unabe to read file '%s': %d read\n", filename, res); + fprintf(stderr, "Unable to read file '%s': %d read\n", filename, res); return 3; } fclose(w); - res = xlnx_btstrm_parse_header((const uint32_t* )outa, 256/4, &file); + // res = xlnx_btstrm_parse_header((const uint32_t* )outa, 256/4, &file); + res = xlnx_btstrm_parse_header_ex((const uint32_t* )outa, + crc_check ? (total_length / 4) : (256 / 4), + &file, + crc_check ? XLNX_BSTRM_PARSE_F_CRC_CHECK : XLNX_BSTRM_ALLOW_CROP); if (res) { fprintf(stderr, "It looks like the file is corrupted! res=%d\n", res); return 4; } + if (verbose) { + fprintf(stderr, "- GOLDEN: WBSTART=%08x IPROG=%d\n", image.wbstar, image.iprog); + fprintf(stderr, "- MASTER: WBSTART=%08x IPROG=%d\n", image_master.wbstar, image_master.iprog); + fprintf(stderr, "- FILE: WBSTART=%08x IPROG=%d\n", file.wbstar, file.iprog); + fprintf(stderr, "- DEVICE: OFFSET= %08x\n", (unsigned)master_offset); + } + res = (no_device) ? 0 : xlnx_btstrm_iprgcheck(&image, &file, master_offset, golden); if (res) { fprintf(stderr, "Image check failed! res=%d, file revision=%12ld\n", res, get_xilinx_rev_h(file.usr_access2)); @@ -239,6 +266,15 @@ int main(int argc, char** argv) fprintf(stderr, "Looks like you're using latest firmware already\n"); return 9; } + if (image.usr_access2 == file.usr_access2 && golden && !force) { + fprintf(stderr, "Looks like GOLD image %08x is already flashed!\n", file.usr_access2); + return 9; + } + if (image_master.usr_access2 == file.usr_access2 && !golden && !force) { + fprintf(stderr, "Looks like MASTER image %08x is already flashed!\n", file.usr_access2); + return 9; + } + if (corrupt) { memset(outa + 512*1024, -1, 512*1024); fprintf(stderr, "CORRUPTING IMAGE!!!\n\n"); @@ -277,6 +313,49 @@ int main(int argc, char** argv) } } + if (rdwr == ACTION_ERASE_MASTER) { + char reply[100]; + char* term; + + if (!mp) { + fprintf(stderr, "Master image is not detected!\n\n"); + } + + fprintf(stderr, " ===========================================\n"); + fprintf(stderr, " == YOU'RE GOING TO BLANK MASTER FIRMWARE ==\n"); + fprintf(stderr, " ===========================================\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Type YES if you know what're doing: "); + + if (fgets(reply, sizeof(reply), stdin) == NULL) + return 0; + + term = strstr(reply, "\n"); + if (term) { + *term = 0; + } + + if (strcmp(reply, "YES") != 0) + return 0; + + if (image.wbstar == 0) { + fprintf(stderr, "No Master support detected: WBSTAR is 0!\n"); + return 7; + } + if (image.wbstar != master_offset) { + fprintf(stderr, "Master trampoline mismatches: WBSTAR is %08x != %08x in the software!\n", + image.wbstar, (unsigned)master_offset); + return 8; + } + + fprintf(stderr, "Blanking flash starting from %08x...\n", image.wbstar); + res = espi_flash_erase(dev, 0, qspi_base, 65536, master_offset); + if (res) { + fprintf(stderr, "Failed to blank flash header! res=%d", res); + return 4; + } + } + if (rdwr == ACTION_WRITE || rdwr == ACTION_READBACK) { fprintf(stderr, "Reading %d bytes!\n", total_length); res = espi_flash_read(dev, 0, qspi_base, 512, off, total_length, outb); @@ -306,7 +385,7 @@ int main(int argc, char** argv) } else if (rdwr == ACTION_READBACK) { FILE* w = fopen(filename, "wb"); if (w == NULL) { - fprintf(stderr, "Unabe to create file '%s': %s\n", filename, strerror(errno)); + fprintf(stderr, "Unable to create file '%s': %s\n", filename, strerror(errno)); return 3; } fwrite(outb, 1, total_length, w); diff --git a/src/tools/xmass_fwupd.c b/src/tools/xmass_fwupd.c new file mode 100644 index 00000000..55d21e75 --- /dev/null +++ b/src/tools/xmass_fwupd.c @@ -0,0 +1,108 @@ +// Copyright (c) 2026 Wavelet Lab +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIE_XMASS_DRIVER_MAGIC 0xDC +#define PCIE_FLASH_READ _IOWR(PCIE_XMASS_DRIVER_MAGIC, 1, uint8_t*) +#define PCIE_FLASH_ERASE _IOWR(PCIE_XMASS_DRIVER_MAGIC, 2, uint32_t) +#define PCIE_FLASH_WRITE _IOWR(PCIE_XMASS_DRIVER_MAGIC, 3, uint8_t*) +#define PCIE_GETIDS _IOWR(PCIE_XMASS_DRIVER_MAGIC, 4, uint8_t*) + +#define BUFFER_SIZE 65536 + +int main(int argc, char *argv[]) +{ + int fd; + uint32_t magic = 0; + uint8_t buffer[BUFFER_SIZE]; + uint8_t rb_buffer[BUFFER_SIZE]; + uint8_t info[10]; + int do_info = 0; + + const char* device_name = (argc >= 3) ? argv[2] : "/dev/xmass0"; + + if (argc < 2) { + fprintf(stderr, "Usage: %s filename.bin [/dev/xmass0]\n", argv[0]); + fprintf(stderr, "Usage: %s info -- display actual firmware version\n", argv[0]); + return EXIT_FAILURE; + } + + do_info = (strcmp(argv[1], "info") == 0); + if (!do_info) { + FILE *file = fopen(argv[1], "rb"); + if (file == NULL) { + perror("Error opening file"); + return EXIT_FAILURE; + } + + size_t bytesRead = fread(buffer, 1, BUFFER_SIZE, file); + printf("Read %zu bytes from %s\n", bytesRead, argv[1]); + + fclose(file); + + if (bytesRead != BUFFER_SIZE) { + fprintf(stderr, "File is too small, only got %zd bytes!\n", bytesRead); + return EXIT_FAILURE; + } + } + + fd = open(device_name, O_RDWR); + if (fd < 0) { + perror("Could't open device"); + return EXIT_FAILURE; + } + + if (ioctl(fd, PCIE_GETIDS, info) < 0) { + perror("Unable to get ids"); + close(fd); + return EXIT_FAILURE; + } + + printf("ASM2806 Firmware in use: %02x%02x%02x%02x%02x%02x, FlashID: %02x %02x %02x\n", + info[0], info[1], info[2], info[3], info[4], info[5], + info[6], info[7], info[8]); + + if (do_info) { + close(fd); + return EXIT_SUCCESS; + } + + printf("Opened %s, flashing %s\n", device_name, argv[1]); + printf("Erasing flash...\n"); + + if (ioctl(fd, PCIE_FLASH_ERASE, magic) < 0) { + perror("Erasing flash error"); + close(fd); + return EXIT_FAILURE; + } + + printf("Writing new image...\n"); + if (ioctl(fd, PCIE_FLASH_WRITE, buffer) < 0) { + perror("Writing flash error"); + close(fd); + return EXIT_FAILURE; + } + + printf("Verifying image...\n"); + if (ioctl(fd, PCIE_FLASH_READ, rb_buffer) < 0) { + perror("Readback flash error"); + close(fd); + return EXIT_FAILURE; + } + + if (memcmp(buffer, rb_buffer, 0xE00) || memcmp(buffer + 0xF00, rb_buffer + 0xF00, BUFFER_SIZE - 0xF00)) { + fprintf(stderr, "Verification failed!\n"); + return EXIT_FAILURE; + } + + close(fd); + return EXIT_SUCCESS; +} diff --git a/src/utests/CMakeLists.txt b/src/utests/CMakeLists.txt index 5468ec63..bb2ddc3e 100644 --- a/src/utests/CMakeLists.txt +++ b/src/utests/CMakeLists.txt @@ -3,6 +3,7 @@ include_directories(../lib/port) include_directories(../lib/lowlevel) +include_directories(../lib/hw) add_library(mock_lowlevel STATIC mock_lowlevel.c) @@ -11,6 +12,10 @@ set(TEST_SUIT_SRCS ring_buffer_test.c trig_test.c clockgen_test.c + lmk05318_solver_test.c + lmx2820_solver_test.c + lmx1214_solver_test.c + lmx1204_solver_test.c ) include_directories(../lib/xdsp) diff --git a/src/utests/lmk05318_solver_test.c b/src/utests/lmk05318_solver_test.c new file mode 100644 index 00000000..abce5b17 --- /dev/null +++ b/src/utests/lmk05318_solver_test.c @@ -0,0 +1,506 @@ +#include +#include "lmk05318/lmk05318.h" + +#define OUTS_LEN LMK05318_MAX_OUT_PORTS + +static lmk05318_out_config_t cfg[OUTS_LEN]; +static lmk05318_state_t dev; + +void lmk05318_registers_map_reset(); + +static void setup() +{ + memset(cfg, 0, sizeof(cfg)); + memset(&dev, 0, sizeof(dev)); + + dev.fref_pll2_div_rp = 3; + dev.fref_pll2_div_rs = 6; + + int res = 0; + lmk05318_out_config_t* p = &cfg[0]; + res = res ? res : lmk05318_port_request(p++, 0, 100000000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 1, 100000000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 2, 122880000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 3, 122880000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 4, 31250000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 3840000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 6, 491520000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 7, 1, true, OUT_OFF); + ck_assert_int_eq( res, 0 ); + + lmk05318_registers_map_reset(); +} + +static void teardown() +{ +} + +START_TEST(lmk05318_solver_test1) +{ + lmk05318_registers_map_reset(); + int res = lmk05318_solver(&dev, cfg, SIZEOF_ARRAY(cfg)); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_solver_test3) +{ + uint64_t fvco1 = 2500000000ull; + uint64_t f0_3 = fvco1 / 16; + uint64_t f4_7 = 12500000; //3840000; + + int res = 0; + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 1, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 2, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 3, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 4, f4_7, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, f4_7, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 6, f4_7, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 7, f4_7, false, OUT_OFF); + ck_assert_int_eq( res, 0 ); + + p = &cfg[0]; + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + ck_assert_int_eq( res, 0 ); + + lmk05318_registers_map_reset(); + res = lmk05318_solver(&dev, cfg, SIZEOF_ARRAY(cfg)); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_solver_test4) +{ + uint64_t fvco1 = 2500000000ull; + uint64_t f0_3 = fvco1 / 16; + uint64_t f4_7 = 12500000; //3840000; + + memset(cfg, 0, sizeof(cfg)); + + int res = 0; + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 1, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 2, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 3, f0_3, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 4, f4_7, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, f4_7, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 6, f4_7, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 7, f4_7, false, OUT_OFF); + ck_assert_int_eq( res, 0 ); + + p = &cfg[0]; + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + res = res ? res : lmk05318_set_port_affinity(p++, AFF_APLL2); + ck_assert_int_eq( res, 0 ); + + lmk05318_registers_map_reset(); + res = lmk05318_solver(&dev, cfg, 4); + ck_assert_int_eq( res, 0 ); + + lmk05318_registers_map_reset(); + res = lmk05318_solver(&dev, cfg + 4, 4); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_solver_test5) +{ + int res = 0; + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 125000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 1, 125000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 250000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 250000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 4, 156250000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 156250000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 6, 10000000, false, LVCMOS_P_N); + res = res ? res : lmk05318_port_request(p++, 7, 1, false, LVCMOS_P_N); + ck_assert_int_eq( res, 0 ); + + lmk05318_registers_map_reset(); + res = lmk05318_solver(&dev, cfg, SIZEOF_ARRAY(cfg)); + ck_assert_int_eq( res, 0 ); + +} +END_TEST + +START_TEST(lmk05318_solver_pesync) +{ + int res = 0; + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 125000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 1, 125000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 250000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 250000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 4, 156250000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 156250000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 6, 10000000, false, LVCMOS_P_N); + res = res ? res : lmk05318_port_request(p++, 7, 1, false, LVCMOS_P_N); + ck_assert_int_eq( res, 0 ); + + lmk05318_dpll_settings_t dpll; + memset(&dpll, 0, sizeof(dpll)); + dpll.enabled = true; + dpll.en[LMK05318_PRIREF] = true; + dpll.fref[LMK05318_PRIREF] = 1; + dpll.type[LMK05318_PRIREF] = DPLL_REF_TYPE_DIFF_NOTERM; + dpll.dc_mode[LMK05318_PRIREF] = DPLL_REF_DC_COUPLED_INT; + dpll.buf_mode[LMK05318_PRIREF] = DPLL_REF_AC_BUF_HYST50_DC_EN; + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 12800000, XO_CMOS, false, &dpll, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + ck_assert_int_eq( res, 0 ); + +} +END_TEST + +START_TEST(lmk05318_solver_pesync_free_run) +{ + int res = 0; + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 125000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 1, 125000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 250000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 250000000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 4, 156250000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 156250000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 6, 10000000, false, LVCMOS_P_N); + res = res ? res : lmk05318_port_request(p++, 7, 1, false, LVCMOS_P_N); + ck_assert_int_eq( res, 0 ); + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 25000000, XO_CMOS, false, NULL, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_dpll_test1) +{ + lmk05318_dpll_settings_t dpll; + memset(&dpll, 0, sizeof(dpll)); + + dpll.enabled = true; + dpll.en[LMK05318_PRIREF] = true; + dpll.fref[LMK05318_PRIREF] = 1; + dpll.type[LMK05318_PRIREF] = DPLL_REF_TYPE_DIFF_NOTERM; + dpll.dc_mode[LMK05318_PRIREF] = DPLL_REF_DC_COUPLED_INT; + dpll.buf_mode[LMK05318_PRIREF] = DPLL_REF_AC_BUF_HYST50_DC_EN; + + int res = lmk05318_dpll_config(&dev, &dpll); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_dsdr_test1) +{ + int res = 0; + + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 491520000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 1, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 3840000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 4, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 122880000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 6, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 7, 122880000, false, LVDS); + ck_assert_int_eq( res, 0 ); + + lmk05318_dpll_settings_t dpll; + memset(&dpll, 0, sizeof(dpll)); + + dpll.enabled = true; + dpll.en[LMK05318_PRIREF] = true; + dpll.fref[LMK05318_PRIREF] = 40000000; + dpll.type[LMK05318_PRIREF] = DPLL_REF_TYPE_DIFF_NOTERM; + dpll.dc_mode[LMK05318_PRIREF] = DPLL_REF_DC_COUPLED_INT; + dpll.buf_mode[LMK05318_PRIREF] = DPLL_REF_AC_BUF_HYST50_DC_EN; + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 26000000, XO_CMOS, false, &dpll, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_dsdr_test2) +{ + int res = 0; + + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 491520000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 1, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 3840000, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 4, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 122880000 * 2, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 6, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 7, 122880000 * 2, false, LVDS); + ck_assert_int_eq( res, 0 ); + + lmk05318_dpll_settings_t dpll; + memset(&dpll, 0, sizeof(dpll)); + + dpll.enabled = true; + dpll.en[LMK05318_PRIREF] = true; + dpll.fref[LMK05318_PRIREF] = 40000000; + dpll.type[LMK05318_PRIREF] = DPLL_REF_TYPE_DIFF_NOTERM; + dpll.dc_mode[LMK05318_PRIREF] = DPLL_REF_DC_COUPLED_INT; + dpll.buf_mode[LMK05318_PRIREF] = DPLL_REF_AC_BUF_HYST50_DC_EN; + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 26000000, XO_CMOS, false, &dpll, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_dsdr_test3) +{ + int res = 0; + + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 1, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 4, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 245760000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 6, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 7, 245760000, false, LVDS); + ck_assert_int_eq( res, 0 ); + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 26000000, XO_CMOS, false, NULL, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + ck_assert_int_eq( res, 0 ); +} +END_TEST + + +static int simplesync_pd_low_chs(lmk05318_state_t* st) +{ + int res = 0; + res = res ? res : lmk05318_set_out_mux(st, 0, 0, OUT_OFF); + res = res ? res : lmk05318_set_out_mux(st, 1, 0, OUT_OFF); + res = res ? res : lmk05318_set_out_mux(st, 2, 0, OUT_OFF); + res = res ? res : lmk05318_set_out_mux(st, 3, 0, OUT_OFF); + res = res ? res : lmk05318_reg_wr_from_map(st, true /*dry_run*/); + return res; +} + +#define LO_FREQ_CUTOFF 3500000ul // VCO2_MIN / 7 / 256 = 3069196.43 Hz + +static int lmk05318_simplesync_set_lo_freq(lmk05318_state_t* st, uint64_t meas_lo) +{ + int res; + if(meas_lo < LO_FREQ_CUTOFF) + { + res = simplesync_pd_low_chs(st); + } + else + { + lmk05318_out_config_t cfg2[4]; + + lmk05318_port_request(&cfg2[0], 0, meas_lo, false, LVDS); + lmk05318_port_request(&cfg2[1], 1, meas_lo, false, LVDS); + lmk05318_port_request(&cfg2[2], 2, meas_lo, false, LVDS); + lmk05318_port_request(&cfg2[3], 3, meas_lo, false, LVDS); + + lmk05318_set_port_affinity(&cfg2[0], AFF_APLL2); + lmk05318_set_port_affinity(&cfg2[1], AFF_APLL2); + lmk05318_set_port_affinity(&cfg2[2], AFF_APLL2); + lmk05318_set_port_affinity(&cfg2[3], AFF_APLL2); + + res = lmk05318_solver(st, cfg2, SIZEOF_ARRAY(cfg2)); + res = res ? res : lmk05318_reg_wr_from_map(st, true /*dry_run*/); + } + + return res; +} + +START_TEST(lmk05318_simplesync_test1) +{ + int res = 0; + + lmk05318_out_config_t cfg1[4]; + lmk05318_out_config_t* p = &cfg1[0]; + + lmk05318_port_request(p++, 4, 25000000, false, LVCMOS_P_N); + lmk05318_port_request(p++, 5, 25000000, false, LVCMOS_P_N); + lmk05318_port_request(p++, 6, 25000000, false, LVCMOS_P_N); + lmk05318_port_request(p++, 7, 25000000, false, LVCMOS_P_N); + + p = &cfg1[0]; + + lmk05318_set_port_affinity(p++, AFF_APLL1); + lmk05318_set_port_affinity(p++, AFF_APLL1); + lmk05318_set_port_affinity(p++, AFF_APLL1); + lmk05318_set_port_affinity(p++, AFF_APLL1); + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 26000000, XO_CMOS, false, NULL, cfg1, SIZEOF_ARRAY(cfg1), &st, true /*dry_run*/); + res = res ? res : simplesync_pd_low_chs(&st); + + ck_assert_int_eq( res, 0 ); + + res = lmk05318_simplesync_set_lo_freq(&st, 122800000); + ck_assert_int_eq( res, 0 ); + + res = lmk05318_simplesync_set_lo_freq(&st, 3500000); + ck_assert_int_eq( res, 0 ); + + res = lmk05318_simplesync_set_lo_freq(&st, 3000000); + ck_assert_int_eq( res, 0 ); + + res = lmk05318_simplesync_set_lo_freq(&st, 0); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_solver_test_xmass) +{ + int res = 0; + + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 1, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 2, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 3, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 4, 1000666000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 5, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 6, 25000000, false, LVCMOS_P_N); + res = res ? res : lmk05318_port_request(p++, 7, 1, false, LVCMOS_P_N); + + res = res ? res : lmk05318_set_port_affinity(&cfg[4], AFF_APLL2); + res = res ? res : lmk05318_set_port_affinity(&cfg[6], AFF_APLL1); + res = res ? res : lmk05318_set_port_affinity(&cfg[7], AFF_APLL1); + ck_assert_int_eq( res, 0 ); + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 26000000, XO_CMOS, false, NULL, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + + ck_assert_int_eq( res, 0 ); + + res = lmk05318_port_request(&cfg[4], 4, 4000000, false, LVDS); + res = res ? res : lmk05318_set_port_affinity(&cfg[4], AFF_APLL2); + res = res ? res : lmk05318_solver(&st, cfg, 8); + res = res ? res : lmk05318_reg_wr_from_map(&st, true); + ck_assert_int_eq( res, 0 ); + + res = lmk05318_port_request(&cfg[4], 4, 5000000, false, LVDS); + res = res ? res : lmk05318_set_port_affinity(&cfg[4], AFF_APLL2); + res = res ? res : lmk05318_solver(&st, &cfg[4], 1); + res = res ? res : lmk05318_reg_wr_from_map(&st, true); + ck_assert_int_eq( res, 0 ); + + res = lmk05318_port_request(&cfg[4], 4, 0, false, LVDS); + res = res ? res : lmk05318_set_port_affinity(&cfg[4], AFF_APLL2); + res = res ? res : lmk05318_solver(&st, cfg, 8); + res = res ? res : lmk05318_reg_wr_from_map(&st, true); + ck_assert_int_eq( res, 0 ); + + res = lmk05318_port_request(&cfg[4], 4, 6000000, false, LVDS); + res = res ? res : lmk05318_set_port_affinity(&cfg[4], AFF_APLL2); + res = res ? res : lmk05318_solver(&st, cfg, 8); + res = res ? res : lmk05318_reg_wr_from_map(&st, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_hyper_test1) +{ + int res = 0; + + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 1, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 4, 0, false, OUT_OFF); + res = res ? res : lmk05318_port_request(p++, 5, 245760000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 6, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 7, 245760000, false, LVDS); + ck_assert_int_eq( res, 0 ); + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 52000000, XO_CMOS, false, NULL, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmk05318_customer_test1) +{ + int res = 0; + + lmk05318_out_config_t* p = &cfg[0]; + + res = res ? res : lmk05318_port_request(p++, 0, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 1, 491520000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 2, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 3, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 4, 312500000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 5, 245760000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 6, 3840000, false, LVDS); + res = res ? res : lmk05318_port_request(p++, 7, 245760000, false, LVDS); + ck_assert_int_eq( res, 0 ); + + lmk05318_state_t st; + res = lmk05318_create(NULL, 0, 0, 52000000, XO_CMOS, false, NULL, cfg, SIZEOF_ARRAY(cfg), &st, true /*dry_run*/); + ck_assert_int_eq( res, 0 ); +} +END_TEST + + +Suite * lmk05318_solver_suite(void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create("lmk05318_solver"); + tc_core = tcase_create("HW"); + tcase_set_timeout(tc_core, 1); + tcase_add_checked_fixture(tc_core, setup, teardown); +/* + tcase_add_test(tc_core, lmk05318_solver_test1); + tcase_add_test(tc_core, lmk05318_solver_test3); + tcase_add_test(tc_core, lmk05318_solver_test4); + tcase_add_test(tc_core, lmk05318_solver_test5); + tcase_add_test(tc_core, lmk05318_solver_pesync); + tcase_add_test(tc_core, lmk05318_solver_pesync_free_run); + tcase_add_test(tc_core, lmk05318_dpll_test1); + tcase_add_test(tc_core, lmk05318_dsdr_test1); + tcase_add_test(tc_core, lmk05318_dsdr_test2); + tcase_add_test(tc_core, lmk05318_dsdr_test3); + tcase_add_test(tc_core, lmk05318_simplesync_test1); + tcase_add_test(tc_core, lmk05318_solver_test_xmass); + tcase_add_test(tc_core, lmk05318_hyper_test1); */ + + tcase_add_test(tc_core, lmk05318_customer_test1); + + suite_add_tcase(s, tc_core); + return s; +} diff --git a/src/utests/lmx1204_solver_test.c b/src/utests/lmx1204_solver_test.c new file mode 100644 index 00000000..7406f69a --- /dev/null +++ b/src/utests/lmx1204_solver_test.c @@ -0,0 +1,143 @@ +#include +#include +#include "lmx1204/lmx1204.h" + +static lmx1204_state_t st; + +static void setup() +{ + memset(&st, 0, sizeof(st)); + + st.ch_en[LMX1204_CH0] = 1; + st.ch_en[LMX1204_CH1] = 1; + st.ch_en[LMX1204_CH2] = 1; + st.ch_en[LMX1204_CH3] = 1; + st.ch_en[LMX1204_CH_LOGIC] = 1; + + st.clkout_en[LMX1204_CH0] = 1; + st.clkout_en[LMX1204_CH1] = 1; + st.clkout_en[LMX1204_CH2] = 1; + st.clkout_en[LMX1204_CH3] = 1; + st.clkout_en[LMX1204_CH_LOGIC] = 1; + + st.sysref_en = 1; + st.sysrefout_en[LMX1204_CH0] = 1; + st.sysrefout_en[LMX1204_CH1] = 1; + st.sysrefout_en[LMX1204_CH2] = 1; + st.sysrefout_en[LMX1204_CH3] = 1; + st.sysrefout_en[LMX1204_CH_LOGIC] = 1; + + st.logiclkout_fmt = LMX1204_FMT_LVDS; + st.logisysrefout_fmt = LMX1204_FMT_LVDS; + + lmx1204_init_sysrefout_ch_delay(&st, LMX1204_CH0, 1/*SYSREFOUT0_DELAY_PHASE_QCLK0*/, 7, 120); + lmx1204_init_sysrefout_ch_delay(&st, LMX1204_CH1, 1/*SYSREFOUT0_DELAY_PHASE_QCLK0*/, 7, 120); + lmx1204_init_sysrefout_ch_delay(&st, LMX1204_CH2, 1/*SYSREFOUT0_DELAY_PHASE_QCLK0*/, 7, 120); + lmx1204_init_sysrefout_ch_delay(&st, LMX1204_CH3, 1/*SYSREFOUT0_DELAY_PHASE_QCLK0*/, 7, 120); + lmx1204_init_sysrefout_ch_delay(&st, LMX1204_CH_LOGIC, 1/*SYSREFOUT0_DELAY_PHASE_QCLK0*/, 7, 120); +} + +static void teardown() {} + +START_TEST(lmx1204_solver_test1) +{ + st.clkin = 12800000000; + st.clkout = st.clkin; + + st.sysrefreq = 1000000; + st.sysrefout = st.sysrefreq; + st.sysref_mode = LMX1204_REPEATER; + + st.logiclkout = 400000000; + + int res = lmx1204_solver(&st, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1204_solver_test2) +{ + st.clkin = 12800000000; + st.clkout = st.clkin; + + st.sysrefreq = 0; + st.sysrefout = 40000000; + st.sysref_mode = LMX1204_CONTINUOUS; + + st.logiclkout = 8000000; + + int res = lmx1204_solver(&st, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1204_solver_test3) +{ + st.clkin = 6400000000; + st.clkout = st.clkin; + st.filter_mode = 1; + + st.sysrefreq = 0; + st.sysrefout = 20000000; + st.sysref_mode = LMX1204_CONTINUOUS; + + st.logiclkout = 4000000; + + int res = lmx1204_solver(&st, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1204_solver_test4) +{ + st.clkin = 1400000000; + st.clkout = st.clkin * 4; + + st.sysrefreq = 0; + st.sysrefout = 4375000; + st.sysref_mode = LMX1204_CONTINUOUS; + + st.logiclkout = 1400000; + + int res = lmx1204_solver(&st, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1204_solver_test5) +{ + st.clkin = 500000000; + st.clkout = st.clkin; + + st.sysrefreq = 0; + st.sysrefout = 3125000; + st.sysref_mode = LMX1204_CONTINUOUS; + + //st.ch_en[LMX1204_CH_LOGIC] = false; + st.logiclkout = 125000000; + + int res = lmx1204_solver(&st, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +Suite * lmx1204_solver_suite(void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create("lmx1204_solver"); + tc_core = tcase_create("HW"); + tcase_set_timeout(tc_core, 1); + tcase_add_checked_fixture(tc_core, setup, teardown); + + tcase_add_test(tc_core, lmx1204_solver_test1); + tcase_add_test(tc_core, lmx1204_solver_test2); + tcase_add_test(tc_core, lmx1204_solver_test3); + tcase_add_test(tc_core, lmx1204_solver_test4); + tcase_add_test(tc_core, lmx1204_solver_test5); + + suite_add_tcase(s, tc_core); + return s; +} + diff --git a/src/utests/lmx1214_solver_test.c b/src/utests/lmx1214_solver_test.c new file mode 100644 index 00000000..98b62db8 --- /dev/null +++ b/src/utests/lmx1214_solver_test.c @@ -0,0 +1,147 @@ +#include +#include +#include "lmx1214/lmx1214.h" + +static lmx1214_state_t st; + +static void setup() +{ + memset(&st, 0, sizeof(st)); +} + +static void teardown() {} + +START_TEST(lmx1214_solver_test1) +{ + const uint64_t osc_in = 600000000; + uint64_t out_freq = osc_in / 4; + bool en[4] = {1,1,1,1}; + + lmx1214_auxclkout_cfg_t aux; + aux.enable = 1; + aux.fmt = LMX1214_FMT_LVDS; + aux.freq = osc_in / 16; + + int res = lmx1214_solver(&st, osc_in, out_freq, en, &aux, true, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1214_solver_test2) +{ + const uint64_t osc_in = 640000000; + uint64_t out_freq = osc_in; + bool en[4] = {1,1,1,1}; + + lmx1214_auxclkout_cfg_t aux; + aux.enable = 1; + aux.fmt = LMX1214_FMT_LVDS; + aux.freq = osc_in / 160; + + int res = lmx1214_solver(&st, osc_in, out_freq, en, &aux, true, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1214_solver_test3) +{ + const uint64_t osc_in = 640000000; + uint64_t out_freq = osc_in; + bool en[4] = {1,1,1,1}; + + lmx1214_auxclkout_cfg_t aux; + aux.enable = 1; + aux.fmt = LMX1214_FMT_LVDS; + aux.freq = osc_in; + + int res = lmx1214_solver(&st, osc_in, out_freq, en, &aux, true, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1214_solver_test4_pesync0) +{ + const uint64_t osc_in = 1600000000; + uint64_t out_freq = osc_in / 2; + bool en[4] = {1,1,1,1}; + + lmx1214_auxclkout_cfg_t aux; + aux.enable = 1; + aux.fmt = LMX1214_FMT_LVDS; + aux.freq = 800000000/4; + + int res = lmx1214_solver(&st, osc_in, out_freq, en, &aux, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1214_solver_test4_pesync1) +{ + const uint64_t osc_in = 1600000000; + uint64_t out_freq = osc_in / 2; + bool en[4] = {1,1,1,1}; + + lmx1214_auxclkout_cfg_t aux; + aux.enable = 0; + aux.fmt = LMX1214_FMT_LVDS; + aux.freq = 0; + + int res = lmx1214_solver(&st, osc_in, out_freq, en, &aux, true, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1214_solver_test4_pesync2) +{ + const uint64_t osc_in = 1600000000; + uint64_t out_freq = osc_in / 3; + bool en[4] = {1,1,1,1}; + + lmx1214_auxclkout_cfg_t aux; + aux.enable = 1; + aux.fmt = LMX1214_FMT_LVDS; + aux.freq = 1600000000 / 124; + + int res = lmx1214_solver(&st, osc_in, out_freq, en, &aux, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx1214_solver_test4_pesync3) +{ + const uint64_t osc_in = 550000000; + uint64_t out_freq = osc_in; + bool en[4] = {1,1,1,1}; + + lmx1214_auxclkout_cfg_t aux; + aux.enable = 1; + aux.fmt = LMX1214_FMT_LVDS; + aux.freq = osc_in; + + int res = lmx1214_solver(&st, osc_in, out_freq, en, &aux, false, true); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +Suite * lmx1214_solver_suite(void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create("lmx1214_solver"); + tc_core = tcase_create("HW"); + tcase_set_timeout(tc_core, 1); + tcase_add_checked_fixture(tc_core, setup, teardown); + + tcase_add_test(tc_core, lmx1214_solver_test1); + tcase_add_test(tc_core, lmx1214_solver_test2); + tcase_add_test(tc_core, lmx1214_solver_test3); + tcase_add_test(tc_core, lmx1214_solver_test4_pesync0); + tcase_add_test(tc_core, lmx1214_solver_test4_pesync1); + tcase_add_test(tc_core, lmx1214_solver_test4_pesync2); + tcase_add_test(tc_core, lmx1214_solver_test4_pesync3); + + suite_add_tcase(s, tc_core); + return s; +} + diff --git a/src/utests/lmx2820_solver_test.c b/src/utests/lmx2820_solver_test.c new file mode 100644 index 00000000..e6e2a725 --- /dev/null +++ b/src/utests/lmx2820_solver_test.c @@ -0,0 +1,218 @@ +#include +#include +#include "lmx2820/lmx2820.h" + +static lmx2820_state_t st; + +static void setup() +{ + memset(&st, 0, sizeof(st)); +} + +static void teardown() {} + +START_TEST(lmx2820_solver_test1) +{ + const uint64_t osc_in = 5000000; + const int mash_order = 0; + uint64_t out_freq1 = 45000000; + uint64_t out_freq2 = out_freq1; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test2) +{ + const uint64_t osc_in = 1400000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 45000000; + uint64_t out_freq2 = out_freq1; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test3) +{ + const uint64_t osc_in = 5000000; + const int mash_order = 0; + uint64_t out_freq1 = 22600000000ull; + uint64_t out_freq2 = out_freq1; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test4) +{ + const uint64_t osc_in = 1400000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 22600000000ull; + uint64_t out_freq2 = out_freq1; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test5) +{ + const uint64_t osc_in = 250000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 5600000000ull; + uint64_t out_freq2 = out_freq1 >> 3; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test6) +{ + const uint64_t osc_in = 250000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 5600000000ull; + uint64_t out_freq2 = out_freq1 << 1; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test7) +{ + const uint64_t osc_in = 250000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 5800000000ull; + uint64_t out_freq2 = out_freq1 << 1; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test8) +{ + const uint64_t osc_in = 250000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 2000098000ull; + uint64_t out_freq2 = out_freq1 >> 4; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test9_force_mult) +{ + const uint64_t osc_in = 250000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 20000988000ull; + uint64_t out_freq2 = out_freq1 >> 4; + + int res = lmx2820_solver(&st, osc_in, mash_order, _i, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test10_mash_order) +{ + const uint64_t osc_in = 250000000ull; + uint64_t out_freq1 = 5600000000ull; + uint64_t out_freq2 = out_freq1 >> 3; + + int res = lmx2820_solver(&st, osc_in, _i, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test11_mash_order) +{ + const uint64_t osc_in = 1400000000ull; + uint64_t out_freq1 = 45000000; + uint64_t out_freq2 = out_freq1; + + int res = lmx2820_solver(&st, osc_in, _i, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test12_instcal) +{ + const uint64_t osc_in = 1400000000ull; + const int mash_order = 0; + uint64_t out_freq1 = 45000000ull << 8; + uint64_t out_freq2 = out_freq1 >> 3; + + int res = lmx2820_solver(&st, osc_in, 2, 0, 5650000000ull, 5650000000ull); + ck_assert_int_eq( res, 0 ); + + fprintf(stderr, "Calibrating for INSTCAL, fixing at FPD:%.2f\n", st.lmx2820_input_chain.fpd); + + res = lmx2820_solver_instcal(&st, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test13_pesync) +{ + const uint64_t osc_in = 250000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 320000000ull; + uint64_t out_freq2 = 160000000ull; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + +START_TEST(lmx2820_solver_test14_pesync) +{ + const uint64_t osc_in = 25000000ull; + const int mash_order = 2; + uint64_t out_freq1 = 4000000000ull; + uint64_t out_freq2 = 4000000000ull; + + st.lmx2820_sysref_chain.enabled = true; + st.lmx2820_sysref_chain.srout = 25000000; + st.lmx2820_sysref_chain.master_mode = true; + st.lmx2820_sysref_chain.cont_pulse = true; + + int res = lmx2820_solver(&st, osc_in, mash_order, 0, out_freq1, out_freq2); + ck_assert_int_eq( res, 0 ); +} +END_TEST + + +Suite * lmx2820_solver_suite(void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create("lmx2820_solver"); + tc_core = tcase_create("HW"); + tcase_set_timeout(tc_core, 1); + tcase_add_checked_fixture(tc_core, setup, teardown); + + tcase_add_test(tc_core, lmx2820_solver_test1); + tcase_add_test(tc_core, lmx2820_solver_test2); + tcase_add_test(tc_core, lmx2820_solver_test3); + tcase_add_test(tc_core, lmx2820_solver_test4); + tcase_add_test(tc_core, lmx2820_solver_test5); + tcase_add_test(tc_core, lmx2820_solver_test6); + tcase_add_test(tc_core, lmx2820_solver_test7); + tcase_add_test(tc_core, lmx2820_solver_test8); + tcase_add_loop_test(tc_core, lmx2820_solver_test9_force_mult, 3, 8); + tcase_add_loop_test(tc_core, lmx2820_solver_test10_mash_order, 1, 4); + tcase_add_loop_test(tc_core, lmx2820_solver_test11_mash_order, 1, 4); + tcase_add_test(tc_core, lmx2820_solver_test12_instcal); + tcase_add_test(tc_core, lmx2820_solver_test13_pesync); + tcase_add_test(tc_core, lmx2820_solver_test14_pesync); + + suite_add_tcase(s, tc_core); + return s; +} diff --git a/src/utests/test_suite.c b/src/utests/test_suite.c index 043afe29..6abfd781 100644 --- a/src/utests/test_suite.c +++ b/src/utests/test_suite.c @@ -11,6 +11,10 @@ Suite * ring_buffer_suite(void); Suite * trig_suite(void); Suite * clockgen_suite(void); +Suite * lmk05318_solver_suite(void); +Suite * lmx2820_solver_suite(void); +Suite * lmx1214_solver_suite(void); +Suite * lmx1204_solver_suite(void); int main(int argc, char** argv) { @@ -24,10 +28,18 @@ int main(int argc, char** argv) fprintf(stderr, "Running with %s CPU features\n", buffer); usdrlog_setlevel(NULL, (argc > 1) ? USDR_LOG_TRACE : USDR_LOG_INFO); usdrlog_enablecolorize(NULL); - +/* sr = srunner_create(ring_buffer_suite()); srunner_add_suite(sr, trig_suite()); srunner_add_suite(sr, clockgen_suite()); + srunner_add_suite(sr, lmk05318_solver_suite()); + srunner_add_suite(sr, lmx2820_solver_suite()); + srunner_add_suite(sr, lmx1214_solver_suite()); + srunner_add_suite(sr, lmx1204_solver_suite()); +*/ + sr = srunner_create(lmk05318_solver_suite()); + + srunner_set_fork_status (sr, CK_NOFORK); srunner_run_all(sr, (argc > 1) ? CK_VERBOSE : CK_NORMAL); number_failed = srunner_ntests_failed(sr);