From 5e57b95d7b39486eb4588a771162054bfd009ace Mon Sep 17 00:00:00 2001 From: Ivan <33198864+vd2org@users.noreply.github.com> Date: Fri, 6 Jun 2025 16:43:40 +0400 Subject: [PATCH 1/4] Feature pe sync and xmass (#82) * add xo & apll1 settings * add dpll_mode * fix in apll1 divider setting * solver stage#1 * solver stage#2 * mk05318 solver is working.. sometimes... beta-version * pd2 temporarily disabled + minor changes * another lmk05318 version, elaborate but seems to be perfect. Need testing * headers refact (lost #include) * lmk05318 solver test rewritten to CHECK and added to the test suite * refactoring + add hw registers set to solver * intermediate commit * enforcing solver - now it can work with incomplete set of ports * try to use solver in ext_simplesync (needs testing) * fix typo: * Initial commit * Fixed lmx2820 yaml. * Fixed lmx2820 yaml. * Updated gen_h.py. * lmx2820 solver & tuner * ++lmx2820 unit test * minor fixes/ench + additional test cases * Fixes & emprovements + instcal (not not completely ready) * minor changes * instcal implemented. need to test on hardware * minor change * fix R0 reg mask for instcal * ++comments (intermediate commit, amend it later) * First try to tune LMK05318 * reset() added * minor but valueable changes * pe_sync: fix SPI / I2C settings * init() added - dump decomposed * comment out init() * lmk05318 regs are written in asc order (as by doc) * refactoring, minor changes, ++wait_lock * minor fix * fixed in yaml - add bus info & some minors * validate&add yamls: ad5662, lmk1d1208i, lmx1204, lmx1205, lmx1214, lp87524j_q1 * gen_h.py generator update * update yamls, add bus info * tmp version with TI dump * many fixes in yaml & logic * fix in pe_sync logging * add rd_mask(RD macro) to parser + fixes for LMX2820 * add init regs + fixes * add all regs from dump * comment out soft-reset * add R19 R20 * APLL2 - fix try, rom again * APLL2 fixed * fix 05318 solver for 1 APLL2 out * fix lock & LOS flags reset * extra comments * fix BAW lock * add all regs from dump to init() * Final (I hope) BAW detect fix + rm redundant code * refactoring * fix in solver + APLL2 programmed denum * minor fixes & code cleaning * update ext_simplesync logic * fix accuracy for lmx2820 * add CGD (common greatest divider) to VCOs num/den * lmx2820 lmx1 added to pe_sync * add logging * fix memset * fix mash order * add lmx2820 dump for testing * llog fmt fix * comment out lmx2820 reset + fix for osc_2x detection * try to fix lmx2820 reset * skip reset * add LMX1214 support (need testing) * extra logging * add lmx1214 test dump * several minor changes + print all regs in create() * lmx1204 first stub * lmx1204 works, 1214 fails * code cleaning & fixes in regs. Stable * LMX2124 1)fix for aux.disables 2)prec mode * lmx1204 (not ready yet) * lmx1204 - solver completed (except registers), unit-test added * lmx1204 add regs, lock etc * add LMX2820[0] & LMX1204 to pe_sync * minor changes * add forgotten test * LMK05380 add log * sporadic fixes to make lmx2820 work * LMX1204 individual sysref delays settings * LMX1204/1214 sync wnd capture + refactoring * move spi common funcs to common.c * lite refactoring * refactoring + fix + skip 2820 reset * DPLL added - testing needed * minors * LMK - add load DPLL from dump * multiple LMK DPLL regs fixes * enable GPS & minor fix * add terra-incognita LMK DPLL registers, it works sometimes * LMK DPLL fixes * minor LMK fix * minor fixes * LMK DPLL another dump * another DPLL patch bundle * LMK add empirical regs set (for debug) * minor fixes & comments * wait functions refactoring, timeout more accurate * minor change for debug * LMK DPLL registers cleared: * minor change * several fixes in LMK DPLL regs * LMK yaml upd + minor changes * inscrease lock timeouts - due to timeout functions change several commits above * LMK APLL1 tune refactoring * DPLL ZDM refactored & several minor refact * fix err in prev commit + refact + additional log * APLL1 additional check * fix inaccuracy in OUT7 div calculation * lmx1204 does not lock * LMX1204 works (except multiplier mode), LMX1214 - AUXCLK doesn't work. New freqs settings for PESync * Add distributers LMK1D1208i for LMX1204 LOGICLK/LOGICREF * LMX1204 MULT mode lock fixed * LMX2820 SYSREF added (not tested) * LMX2820@PESync SYSREF implementation * fix in LMX2820 solver for SROUT. SROUT still not working * enable DPLL + timeout 4min * minor cleaning * cleaning before merge [1] * cleaning before merge [2] * cleaning before merge [3] + simplesync/dsdr * lmk05318 fixpack for simplesync, dsdr & pe_sync * code a bit more safe * simplesync cut-off frequency corrected * increase LMK max out freq setting + new unit-test * xmass: add initial support * fix for multithreading accesing the same bus * add check APLL locks for XMASS * LMK - add settings for XO=26M * fix minor issuies * try te fix XMASS freq change, +extra diagnostic +lite refact * xmass: lmk05318b soft reset fails retune APPL2 on preconfigured system, ignore it * add extra LMK out types * make LMK APLL1 fdiv calculation more flexible * LMK refactoring, disable if freq==0, new api to enable/disable ports * add & correct LMK APLL1 checks for dpll-mode * add define macro for disabling dpll-mode checks (XO25M case) * LMK: improving XO chain solver, refactoring, XO=12.8M settings * xsdr wip * fix * lmk05138b: skip soft sync since it fails somehow * LMK create() refactored, xo params simplified * xmass: initial fix * xmass: fix sysref_gen routing * refactoring * Fix potential error in lmk05318_get_output_divider() + new dsdr unit-test * fix in lmk solver, comparing int and double * xdsp test refactoring & cleaning * xdsp test refactoring & cleaning * Startup fix --------- Co-authored-by: Yuri Nikolaev Co-authored-by: Sergey Kostanbaev --- src/hwparser/gen_h.py | 78 +- src/lib/cal/opt_func.c | 43 + src/lib/cal/opt_func.h | 12 + src/lib/device/CMakeLists.txt | 1 + src/lib/device/device_fe.c | 144 +- .../device/ext_simplesync/ext_simplesync.c | 71 +- src/lib/device/ext_xmass/CMakeLists.txt | 24 + src/lib/device/ext_xmass/ext_xmass.c | 304 ++ src/lib/device/ext_xmass/ext_xmass.h | 61 + src/lib/device/ext_xmass/ext_xmass_ctrl.yaml | 71 + src/lib/device/m2_dsdr/m2_dsdr.c | 73 +- src/lib/device/m2_lm7_1/lms7002m_ctrl.c | 14 +- src/lib/device/m2_lm7_1/lms7002m_ctrl.h | 4 +- src/lib/device/m2_lm7_1/m2_lm7_1.c | 42 +- src/lib/device/m2_lm7_1/xsdr_ctrl.c | 311 +- src/lib/device/m2_lm7_1/xsdr_ctrl.h | 9 +- src/lib/device/mdev.c | 11 +- src/lib/device/pe_sync/pe_sync.c | 378 ++- src/lib/device/pe_sync/sync_const.h | 4 +- src/lib/hw/CMakeLists.txt | 6 +- src/lib/hw/ad5662/ad5662.c | 6 + src/lib/hw/ad5662/ad5662.h | 7 + src/lib/hw/ad5662/ad5662.yaml | 34 + src/lib/hw/common/common.c | 123 + src/lib/hw/common/common.h | 22 + src/lib/hw/lmk05318/lmk05318.c | 2598 ++++++++++++++++- src/lib/hw/lmk05318/lmk05318.h | 208 +- src/lib/hw/lmk05318/lmk05318.yaml | 219 +- src/lib/hw/lmk05318/lmk05318_rom.h | 676 ----- src/lib/hw/lmk1d1208i/lmk1d1208i.c | 134 + src/lib/hw/lmk1d1208i/lmk1d1208i.h | 62 + src/lib/hw/lmk1d1208i/lmk1d1208i.yaml | 228 ++ src/lib/hw/lmx1204/lmx1204.c | 846 ++++++ src/lib/hw/lmx1204/lmx1204.h | 118 + src/lib/hw/lmx1204/lmx1204.yaml | 979 +++++++ src/lib/hw/lmx1205/lmx1205.c | 6 + src/lib/hw/lmx1205/lmx1205.h | 7 + src/lib/hw/lmx1205/lmx1205.yaml | 1175 ++++++++ src/lib/hw/lmx1214/lmx1214.c | 506 ++++ src/lib/hw/lmx1214/lmx1214.h | 64 + src/lib/hw/lmx1214/lmx1214.yaml | 521 ++++ src/lib/hw/lmx2820/lmx2820.c | 1249 ++++++++ src/lib/hw/lmx2820/lmx2820.h | 95 + src/lib/hw/lmx2820/lmx2820.yaml | 1719 +++++++++++ src/lib/hw/lp87524j/lp87524j.c | 6 + src/lib/hw/lp87524j/lp87524j.h | 7 + src/lib/hw/lp87524j/lp87524j.yaml | 71 + src/lib/hw/tca9555/tca9555.c | 46 + src/lib/hw/tca9555/tca9555.h | 27 + src/lib/hw/tca9555/tca9555.yaml | 87 + src/lib/lowlevel/usb_uram/usb_uram_generic.c | 8 +- src/lib/lowlevel/usb_uram/usb_uram_libusb.c | 15 +- src/lib/lowlevel/usdr_lowlevel.c | 5 + src/lib/lowlevel/usdr_lowlevel.h | 3 +- src/lib/xdsp/utests/README | 23 + src/lib/xdsp/utests/conv_2cf32_ci12_utest.c | 41 +- src/lib/xdsp/utests/conv_2cf32_ci16_utest.c | 39 +- src/lib/xdsp/utests/conv_2ci16_ci12_utest.c | 41 +- src/lib/xdsp/utests/conv_2ci16_ci16_utest.c | 39 +- src/lib/xdsp/utests/conv_4cf32_ci12_utest.c | 48 +- src/lib/xdsp/utests/conv_4cf32_ci16_utest.c | 45 +- src/lib/xdsp/utests/conv_4ci16_ci12_utest.c | 45 +- src/lib/xdsp/utests/conv_4ci16_ci16_utest.c | 47 +- src/lib/xdsp/utests/conv_ci12_2cf32_utest.c | 53 +- src/lib/xdsp/utests/conv_ci12_2ci16_utest.c | 43 +- src/lib/xdsp/utests/conv_ci12_4cf32_utest.c | 54 +- src/lib/xdsp/utests/conv_ci12_4ci16_utest.c | 54 +- src/lib/xdsp/utests/conv_ci16_2cf32_utest.c | 43 +- src/lib/xdsp/utests/conv_ci16_2ci16_utest.c | 41 +- src/lib/xdsp/utests/conv_ci16_4cf32_utest.c | 51 +- src/lib/xdsp/utests/conv_ci16_4ci16_utest.c | 57 +- src/lib/xdsp/utests/conv_f32_i12_utest.c | 40 +- src/lib/xdsp/utests/conv_f32_i16_utest.c | 40 +- src/lib/xdsp/utests/conv_i12_f32_utest.c | 46 +- src/lib/xdsp/utests/conv_i12_i16_utest.c | 39 +- src/lib/xdsp/utests/conv_i16_f32_utest.c | 36 +- src/lib/xdsp/utests/conv_i16_i12_utest.c | 38 +- src/lib/xdsp/utests/fft_window_cf32_utest.c | 45 +- src/lib/xdsp/utests/wvlt_sincos_i16_utest.c | 53 +- src/lib/xdsp/utests/xdsp_utest_common.h | 55 +- src/lib/xdsp/utests/xfft_fftad_utest.c | 30 +- src/lib/xdsp/utests/xfft_rtsa_utest.c | 38 +- src/utests/CMakeLists.txt | 5 + src/utests/lmk05318_solver_test.c | 445 +++ src/utests/lmx1204_solver_test.c | 138 + src/utests/lmx1214_solver_test.c | 140 + src/utests/lmx2820_solver_test.c | 204 ++ src/utests/test_suite.c | 10 + 88 files changed, 13884 insertions(+), 1820 deletions(-) create mode 100644 src/lib/device/ext_xmass/CMakeLists.txt create mode 100644 src/lib/device/ext_xmass/ext_xmass.c create mode 100644 src/lib/device/ext_xmass/ext_xmass.h create mode 100644 src/lib/device/ext_xmass/ext_xmass_ctrl.yaml create mode 100644 src/lib/hw/ad5662/ad5662.c create mode 100644 src/lib/hw/ad5662/ad5662.h create mode 100644 src/lib/hw/ad5662/ad5662.yaml create mode 100644 src/lib/hw/common/common.c create mode 100644 src/lib/hw/common/common.h delete mode 100644 src/lib/hw/lmk05318/lmk05318_rom.h create mode 100644 src/lib/hw/lmk1d1208i/lmk1d1208i.c create mode 100644 src/lib/hw/lmk1d1208i/lmk1d1208i.h create mode 100644 src/lib/hw/lmk1d1208i/lmk1d1208i.yaml create mode 100644 src/lib/hw/lmx1204/lmx1204.c create mode 100644 src/lib/hw/lmx1204/lmx1204.h create mode 100644 src/lib/hw/lmx1204/lmx1204.yaml create mode 100644 src/lib/hw/lmx1205/lmx1205.c create mode 100644 src/lib/hw/lmx1205/lmx1205.h create mode 100644 src/lib/hw/lmx1205/lmx1205.yaml create mode 100644 src/lib/hw/lmx1214/lmx1214.c create mode 100644 src/lib/hw/lmx1214/lmx1214.h create mode 100644 src/lib/hw/lmx1214/lmx1214.yaml create mode 100644 src/lib/hw/lmx2820/lmx2820.c create mode 100644 src/lib/hw/lmx2820/lmx2820.h create mode 100644 src/lib/hw/lmx2820/lmx2820.yaml create mode 100644 src/lib/hw/lp87524j/lp87524j.c create mode 100644 src/lib/hw/lp87524j/lp87524j.h create mode 100644 src/lib/hw/lp87524j/lp87524j.yaml create mode 100644 src/lib/hw/tca9555/tca9555.c create mode 100644 src/lib/hw/tca9555/tca9555.h create mode 100644 src/lib/hw/tca9555/tca9555.yaml create mode 100644 src/lib/xdsp/utests/README create mode 100644 src/utests/lmk05318_solver_test.c create mode 100644 src/utests/lmx1204_solver_test.c create mode 100644 src/utests/lmx1214_solver_test.c create mode 100644 src/utests/lmx2820_solver_test.c diff --git a/src/hwparser/gen_h.py b/src/hwparser/gen_h.py index 665caf09..b1b7a111 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("Rigester `%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/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/device/CMakeLists.txt b/src/lib/device/CMakeLists.txt index 4ae4b9a4..1192cd3c 100644 --- a/src/lib/device/CMakeLists.txt +++ b/src/lib/device/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(ext_pciefe) add_subdirectory(ext_supersync) add_subdirectory(ext_simplesync) add_subdirectory(ext_fe_100_5000) +add_subdirectory(ext_xmass) add_subdirectory(u3_limesdr) diff --git a/src/lib/device/device_fe.c b/src/lib/device/device_fe.c index d44d521d..5176701c 100644 --- a/src/lib/device/device_fe.c +++ b/src/lib/device/device_fe.c @@ -9,6 +9,7 @@ #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 #include @@ -24,13 +25,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,32 +53,36 @@ 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, @@ -78,6 +94,7 @@ typedef enum fe_type fe_type_t; static const char* s_fe_names[] = { "pciefe", + "xmass", "supersync", "simplesync", "exm2pe", @@ -95,6 +112,7 @@ 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; } fe; uint32_t debug_pciefe_last; @@ -102,6 +120,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; @@ -183,6 +202,7 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi 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; default: return -EIO; } @@ -230,8 +250,8 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi case FET_PCIE_SIMPLE_SYNC: res = usdr_vfs_obj_param_init_array_param(base, (void*)n, - s_lmk05318_params, - SIZEOF_ARRAY(s_lmk05318_params)); + s_simplesync_params, + SIZEOF_ARRAY(s_simplesync_params)); break; case FET_PCIE_SUPER_SYNC: res = usdr_vfs_obj_param_init_array_param(base, @@ -245,6 +265,12 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi 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*)n, + s_xmass_params, + SIZEOF_ARRAY(s_xmass_params)); + break; default: break; } @@ -371,18 +397,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_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(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) +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 +422,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 +440,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/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..0a579cb9 --- /dev/null +++ b/src/lib/device/ext_xmass/ext_xmass.c @@ -0,0 +1,304 @@ +// 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 "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 +// +// 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, +}; + +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); + 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; + + //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..74f89f52 --- /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_B[0 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 diff --git a/src/lib/device/m2_dsdr/m2_dsdr.c b/src/lib/device/m2_dsdr/m2_dsdr.c index 9b5a8d16..f5cb2c90 100644 --- a/src/lib/device/m2_dsdr/m2_dsdr.c +++ b/src/lib/device/m2_dsdr/m2_dsdr.c @@ -1105,7 +1105,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", @@ -1421,28 +1421,66 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** break; } } - usleep(40000); + usleep(100000); + // + //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, 491520000, false, OUT_OFF); + lmk05318_port_request(&lmk_out[1], 1, 491520000, 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, d->dac_rate / 2, false, LVDS); + + res = lmk05318_create(dev, d->subdev, I2C_LMK, 26000000, XO_CMOS, false, &dpll, lmk_out, SIZEOF_ARRAY(lmk_out), &d->lmk, false /*dry_run*/); + if(res) + return res; - 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; + //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); + //wait for lock + res = lmk05318_wait_apll1_lock(&d->lmk, 100000); + res = res ? res : lmk05318_wait_apll2_lock(&d->lmk, 100000); + + 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); + if(res) + return res; - res = res ? res : lmk05318_check_lock(&d->lmk, &los); + USDR_LOG("DSDR", USDR_LOG_INFO, "LMK03518 outputs synced"); + //LMK05318 init end + // for (int i = 0; i < 5; i++) { uint32_t clk = 0; @@ -1452,9 +1490,6 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** 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 : dev_gpi_get32(dev, IGPI_PGOOD, &pg); diff --git a/src/lib/device/m2_lm7_1/lms7002m_ctrl.c b/src/lib/device/m2_lm7_1/lms7002m_ctrl.c index 41632e4b..bb7c7fe4 100644 --- a/src/lib/device/m2_lm7_1/lms7002m_ctrl.c +++ b/src/lib/device/m2_lm7_1/lms7002m_ctrl.c @@ -768,7 +768,7 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, USDR_LOG("XDEV", USDR_LOG_INFO, "%s: RBB Restore BW[%d]=%d\n", devstr, ich, d->rx_bw[ich].value); } else { - bandwidth = d->cgen_clk / d->rxcgen_div / d->rxtsp_div / d->rx_host_decim; + bandwidth = d->cgen_clk / d->rxcgen_div / d->rxtsp_div / d->rx_dsp_decim; USDR_LOG("XDEV", USDR_LOG_INFO, "%s: No RBB[%d] was set; defaulting to current rx samplerate %u\n", devstr, ich, bandwidth); } @@ -815,7 +815,7 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, devstr, 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; + bandwidth = d->cgen_clk / d->txcgen_div / d->txtsp_div / d->tx_dsp_inter; USDR_LOG("XDEV", USDR_LOG_INFO, "%s: No TBB[%d] was set; defaulting to current rx samplerate %u\n", devstr, ich, bandwidth); } @@ -927,8 +927,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; @@ -991,8 +991,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 : @@ -1075,7 +1075,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, 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); diff --git a/src/lib/device/m2_lm7_1/lms7002m_ctrl.h b/src/lib/device/m2_lm7_1/lms7002m_ctrl.h index babd0ed9..785185a9 100644 --- a/src/lib/device/m2_lm7_1/lms7002m_ctrl.h +++ b/src/lib/device/m2_lm7_1/lms7002m_ctrl.h @@ -87,8 +87,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; 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..81055db5 100644 --- a/src/lib/device/m2_lm7_1/m2_lm7_1.c +++ b/src/lib/device/m2_lm7_1/m2_lm7_1.c @@ -225,6 +225,9 @@ static int dev_m2_lm7_1_debug_clkinfo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uin 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_sdr_tx_phase_ovr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); +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 }}, @@ -242,6 +245,10 @@ const usdr_dev_param_func_t s_fparams_m2_lm7_1_rev000[] = { { "/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/tx/phase_ovr", { dev_m2_lm7_1_sdr_tx_phase_ovr_set, NULL }}, + { "/dm/sdr/0/rx/dccorr", { dev_m2_lm7_1_sdr_rx_dccorr_set, NULL }}, { "/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 }}, @@ -343,7 +350,7 @@ int dev_m2_lm7_1_dev_dac_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t 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_lms7002rxlml_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) @@ -371,6 +378,18 @@ 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_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_debug_lms7002m_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { @@ -967,6 +986,24 @@ int dev_m2_lm7_1_sensor_freqpps_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t return res; } +static +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 = -ENOENT; + if (ls_op == USDR_LSOP_DRP) { + res = xsdr_override_drp(&d->xdev, ls_op_addr, meminsz, pin, memoutsz, pout); + } + + if (res != -ENOENT) + return res; + + return d->p_original_ops->ls_op(dev, subdev, ls_op, ls_op_addr, meminsz, pin, memoutsz, pout); +} + static int usdr_device_m2_lm7_1_stream_initialize(lldev_t dev, subdev_t subdev, lowlevel_stream_params_t* params, stream_t* channel) { @@ -1050,6 +1087,7 @@ int usdr_device_m2_lm7_1_initialize(pdevice_t udev, unsigned pcount, const char* // Proxy operations memcpy(&d->my_ops, lowlevel_get_ops(dev), sizeof (lowlevel_ops_t)); + d->my_ops.ls_op = &usdr_device_m2_lm7_1_lsop; 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->p_original_ops = lowlevel_get_ops(dev); @@ -1138,6 +1176,7 @@ 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 } // Reset samplerate with proper bifurcation flags @@ -1177,6 +1216,7 @@ 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 } res = create_sfetrx4_stream(dev, CORE_SFETX_DMA32_R0, dformat, channels->count, &lchans, pktsyms, diff --git a/src/lib/device/m2_lm7_1/xsdr_ctrl.c b/src/lib/device/m2_lm7_1/xsdr_ctrl.c index b57334f5..b4bfbcb0 100644 --- a/src/lib/device/m2_lm7_1/xsdr_ctrl.c +++ b/src/lib/device/m2_lm7_1/xsdr_ctrl.c @@ -24,6 +24,10 @@ #define MAX(x,y) (((x) > (y)) ? (x) : (y)) #endif +enum { + DRP_MMCM_PORT_RX = 0, + DRP_MMCM_PORT_TX = 1, +}; // TXA RXB // TXB RXA @@ -124,7 +128,161 @@ 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, +}; + +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_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 { + return -EINVAL; + } + + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_DRP, drp_cmd); + + // 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(1000); + } + + if (res) + return res; + + USDR_LOG("XDEV", USDR_LOG_ERROR, "MMCM DRP_TX CMD=%08x RB=%08x\n", drp_cmd, rb); + + if (meminsz) { + *((uint16_t*)pin) = rb >> 16; + } + return 0; +} + +int xsdr_configure_lml_mmcm_tx(xsdr_dev_t *d) +{ + bool nomul = d->base.lml_mode.txsisoddr || (d->base.txtsp_div > 1); + unsigned tx_mclk = d->base.cgen_clk / d->base.txcgen_div / d->base.lml_mode.txdiv; + unsigned io_clk = (nomul) ? tx_mclk : tx_mclk * 2; + unsigned vco_div_io_m = (MMCM_VCO_MAX + io_clk - 1) / io_clk; + if (vco_div_io_m > 63) + vco_div_io_m = 63; + + unsigned vco_div_io = vco_div_io_m & 0xfc; //Multiply of 4 + int res = 0; + struct mmcm_config_raw cfg_raw; + memset(&cfg_raw, 0, sizeof(cfg_raw)); + + if (vco_div_io * io_clk < MMCM_VCO_MIN) { + if ((vco_div_io + 2) * io_clk > MMCM_VCO_MAX) { + vco_div_io += 1; + } else { + vco_div_io += 2; + } + } + + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, 0); + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_IQSEL, d->base.lml_mode.txsisoddr ? 0b1010 : 0b1100); + + 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; + + 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; + + 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; + + cfg_raw.ports[CLKOUT_PORT_0].phase = ((vco_div_io & 3) == 0) ? 0 : + ((vco_div_io & 3) == 1) ? 2 : + ((vco_div_io & 3) == 2) ? 4 : 6; + cfg_raw.ports[CLKOUT_PORT_0].delay = vco_div_io / 4; + + cfg_raw.ports[CLKOUT_PORT_3].phase = cfg_raw.ports[CLKOUT_PORT_0].phase; + cfg_raw.ports[CLKOUT_PORT_3].delay = vco_div_io / 2; + + if (d->tx_override_phase) { + unsigned raw = d->tx_override_phase - 1; + cfg_raw.ports[CLKOUT_PORT_0].phase = raw & 7; + cfg_raw.ports[CLKOUT_PORT_3].phase = raw & 7; + cfg_raw.ports[CLKOUT_PORT_0].delay = (raw >> 3); + cfg_raw.ports[CLKOUT_PORT_3].delay = 2 * (raw >> 3); + } + + 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_LOG("XDEV", USDR_LOG_ERROR, "MMCM_TX set to MCLK = %.3f IOCLK = %.3f Mhz IODIV = %d FWDCLK_DELAY%s = %d (VCO %d) SISO_DDR=%d\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); + + res = res ? res : mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_TX, &cfg_raw); + + // Reset MMCM + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, 1); + usleep(100); + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, 0); + + for (unsigned k = 0; k < 10; k++) { + // Wait for lock + uint32_t rb; + res = res ? res : lowlevel_reg_rd32(d->base.lmsstate.dev, d->base.lmsstate.subdev, + REG_CFG_PHY_1, &rb); + if (res) + break; + + USDR_LOG("XDEV", USDR_LOG_INFO, "MMCM FLAGS:%08x\n", rb); + if (rb & (1 << 8)) + return 0; + + usleep(5000); + } + + return res; +} + +int xsdr_configure_lml_mmcm_rx(xsdr_dev_t *d) { 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; @@ -165,7 +323,7 @@ int xsdr_configure_lml_mmcm(xsdr_dev_t *d) cfg_raw.ports[CLKOUT_PORT_FB].period_l = vco_div_io; cfg_raw.ports[CLKOUT_PORT_FB].period_h = vco_div_io; } - USDR_LOG("XDEV", USDR_LOG_ERROR, "MMCM set to MCLK = %.3f IOCLK = %.3f Mhz IODIV = %d\n", + USDR_LOG("XDEV", USDR_LOG_ERROR, "MMCM_RX 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, @@ -181,7 +339,7 @@ int xsdr_configure_lml_mmcm(xsdr_dev_t *d) return res; usleep(1000); - res = mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, &cfg_raw); + res = mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_RX, &cfg_raw); if (res) return res; @@ -198,7 +356,8 @@ int xsdr_configure_lml_mmcm(xsdr_dev_t *d) // Wait for lock res = lowlevel_reg_rd32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, &rb); - + if (res) + break; USDR_LOG("XDEV", USDR_LOG_INFO, "MMCM FLAGS:%08x\n", rb); if (rb & (1 << 16)) @@ -206,43 +365,13 @@ int xsdr_configure_lml_mmcm(xsdr_dev_t *d) usleep(5000); } - 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); - if (res) - return res; - - - - //////////////////// - - 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); - 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); - } -#endif return res; } -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 +392,33 @@ int xsdr_hwchans_cnt(xsdr_dev_t *d, bool rx, unsigned chans) return 0; } +enum { + PHY_CFG_VALID_MSK = 0x80, + PHY_CFG_LML2_IS_RX = 0x40, + PHY_CFG_TX_MMCM = 0x20, + PHY_CFG_RX_MMCM = 0x10, +}; + 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); + const uint8_t phycfg_id = (d->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); + lldev_t dev = d->base.lmsstate.dev; subdev_t subdev = d->base.lmsstate.subdev; unsigned sisosdrflag; int res; + if (!(phycfg_id & PHY_CFG_VALID_MSK)) { + USDR_LOG("XDEV", USDR_LOG_ERROR, "Incompatible firmware, please update to 20250501 at least!\n"); + return -ENOTSUP; + } + res = _xsdr_checkpwr(d); if (res) return res; @@ -287,12 +427,10 @@ 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); + res = lms7002m_samplerate(&d->base, rxrate, txrate, adcclk, dacclk, m_flags, rx_port_is_1); if (res) return res; @@ -312,47 +450,58 @@ int xsdr_set_samplerate_ex(xsdr_dev_t *d, d->afe_active = true; } - if (mmcmrx) { - res = xsdr_configure_lml_mmcm(d); - if (res) - return res; - } + if (rxrate) { + if (rx_mmcm) { + res = res ? res : xsdr_configure_lml_mmcm_rx(d); + } - 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); + sisosdrflag = d->base.lml_mode.rxsisoddr ? 8 : 0; + res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000007 | sisosdrflag); + usleep(100); + res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000000 | sisosdrflag); - 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); + if (rx_mmcm) { + // Configure PHY (reset) + // TODO phase search + for (unsigned h = 0; h < 16; h++) { + res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000007 | sisosdrflag); + usleep(100); + res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000000 | sisosdrflag); - 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); - } - mmcm_set_digdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, CLKOUT_PORT_0, h); + USDR_LOG("XDEV", USDR_LOG_INFO, "PHASE=%d\n", h); + res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x00000000); + unsigned tmp; //, tmp2; + for (unsigned k = 0; k < 100; k++) { + res = res ? res : lowlevel_reg_rd32(dev, subdev, REG_CFG_PHY_0, &tmp); + } + + res = res ? res : mmcm_set_digdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_RX, CLKOUT_PORT_0, h); + } } + + // Switch to clock meas + res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x02000000); } - // Switch to clock meas - res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x02000000); + if (txrate) { + res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_CTRL, 1); - // uint32_t m; - // res = lowlevel_reg_rd32(dev, subdev, REG_CFG_PHY_0, &m); - // USDR_LOG("XDEV", USDR_LOG_INFO, "MEAS=%08x\n", m); + if (tx_mmcm) { + res = res ? res : xsdr_configure_lml_mmcm_tx(d); + } else { + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS_PWR, 0x8f); + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS_PWR, 0x8f); + res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS_PWR, 0x0f); - return res; + 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); + } + } + return res; } @@ -1075,6 +1224,20 @@ int _xsdr_pwren_revo(xsdr_dev_t *d, bool on) return 0; } +int xsdr_set_vio(xsdr_dev_t *d, unsigned vio_mv) +{ + if (!d->new_rev) + return -EINVAL; + + if (vio_mv > 2100) + vio_mv = 2100; + else if (vio_mv < 1600) + vio_mv = 1600; + + USDR_LOG("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, 3, vio_mv); +} + int xsdr_pwren(xsdr_dev_t *d, bool on) { int res; diff --git a/src/lib/device/m2_lm7_1/xsdr_ctrl.h b/src/lib/device/m2_lm7_1/xsdr_ctrl.h index 0d98c67d..01f165da 100644 --- a/src/lib/device/m2_lm7_1/xsdr_ctrl.h +++ b/src/lib/device/m2_lm7_1/xsdr_ctrl.h @@ -49,6 +49,8 @@ struct xsdr_dev unsigned lms7_lob; + int tx_override_phase; + bool afe_active; bool siso_sdr_active_rx; bool siso_sdr_active_tx; @@ -124,6 +126,7 @@ 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); // Enable RFIC, no streaming int xsdr_pwren(xsdr_dev_t *d, bool on); @@ -148,11 +151,15 @@ int xsdrcal_do_meas_nco_avg(void* param, int channel, unsigned logduration, int //int (*set_tx_testsig_fs8)(void* param, int channel); -int xsdr_phy_tune(xsdr_dev_t *d, unsigned val); +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); + enum { XSDR_CAL_RXLO = 1, XSDR_CAL_TXLO = 2, diff --git a/src/lib/device/mdev.c b/src/lib/device/mdev.c index 9bf8c9eb..1f3f6c2a 100644 --- a/src/lib/device/mdev.c +++ b/src/lib/device/mdev.c @@ -380,10 +380,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 +400,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; diff --git a/src/lib/device/pe_sync/pe_sync.c b/src/lib/device/pe_sync/pe_sync.c index 3a73dcd0..3865fa71 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) }, @@ -116,6 +126,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 +165,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 +187,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 +199,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 +208,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 +237,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); - // TODO: Initialize LMK05318B + 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); + + 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 +266,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/hw/CMakeLists.txt b/src/lib/hw/CMakeLists.txt index f8c7d417..5d3fcfcf 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) # 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/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/lmk05318/lmk05318.c b/src/lib/hw/lmk05318/lmk05318.c index 2db8ac81..0d9a0d70 100644 --- a/src/lib/hw/lmk05318/lmk05318.c +++ b/src/lib/hw/lmk05318/lmk05318.c @@ -1,29 +1,333 @@ // 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, +}; + +#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(dpll->dc_mode[i]), + dpll->buf_mode[i], lmk05318_dpll_decode_ref_buf_mode(dpll->buf_mode[i]), + dpll->type[i], lmk05318_dpll_decode_ref_type(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 +369,2215 @@ 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: + { + USDR_LOG("5318", USDR_LOG_INFO, "XO=12.8M, applying specific settings..."); + + 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: + { + USDR_LOG("5318", USDR_LOG_INFO, "XO=25M, applying specific settings..."); - if (port > 7) + 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 + }; + + res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); + break; + } + case XO26: + { + USDR_LOG("5318", USDR_LOG_INFO, "XO=26M, applying specific settings..."); + + 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 or 26M", (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)); + 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(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) + goto have_complete_solution; + + 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; + +have_complete_solution: + + //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(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/lmx1204/lmx1204.c b/src/lib/hw/lmx1204/lmx1204.c new file mode 100644 index 00000000..12c1e26d --- /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(status->lock_detect_status), + status->vco_sel, lmx1204_decode_vco_core(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 overriden 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(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(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(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/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/lowlevel/usb_uram/usb_uram_generic.c b/src/lib/lowlevel/usb_uram/usb_uram_generic.c index 870d0dc6..493d1ede 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; diff --git a/src/lib/lowlevel/usb_uram/usb_uram_libusb.c b/src/lib/lowlevel/usb_uram/usb_uram_libusb.c index 237de7a6..9fbc730b 100644 --- a/src/lib/lowlevel/usb_uram/usb_uram_libusb.c +++ b/src/lib/lowlevel/usb_uram/usb_uram_libusb.c @@ -62,7 +62,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, @@ -83,7 +83,7 @@ enum { enum { STREAM_MAX_SLOTS = 64, - MAX_RB_THREADS = 8, + MAX_RB_THREADS = MAX_NTFY_REQS, }; struct stream_params { @@ -129,6 +129,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; @@ -190,6 +191,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); @@ -450,8 +452,8 @@ static int usb_read_bus(lldev_t dev, unsigned interrupt_number, UNUSED unsigned { 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 +463,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; } diff --git a/src/lib/lowlevel/usdr_lowlevel.c b/src/lib/lowlevel/usdr_lowlevel.c index 9af43a76..e1307d71 100644 --- a/src/lib/lowlevel/usdr_lowlevel.c +++ b/src/lib/lowlevel/usdr_lowlevel.c @@ -107,6 +107,11 @@ 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; +} + 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..89fa5e2f 100644 --- a/src/lib/lowlevel/usdr_lowlevel.h +++ b/src/lib/lowlevel/usdr_lowlevel.h @@ -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,7 @@ 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); #endif 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..16594123 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() @@ -175,7 +166,9 @@ START_TEST(conv_2cf32_ci12_check_simd) //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) @@ -233,18 +226,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..b338195a 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) @@ -122,7 +113,9 @@ START_TEST(conv_2ci16_ci12_check_simd) //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) @@ -179,18 +172,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_4cf32_ci12_utest.c b/src/lib/xdsp/utests/conv_4cf32_ci12_utest.c index 1d9085fc..345298ba 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) @@ -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() @@ -185,7 +176,9 @@ START_TEST(conv_4cf32_ci12_check_simd) //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) @@ -197,8 +190,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 +235,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, 3); - 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..821a9504 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) / 2); + 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); } @@ -133,7 +124,9 @@ START_TEST(conv_4ci16_ci12_check_simd) //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) @@ -190,18 +183,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..d75e37d6 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) @@ -222,19 +203,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..b7862819 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) @@ -130,7 +121,9 @@ START_TEST(conv_ci12_2ci16_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); @@ -189,18 +182,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..3cbb02cb 100644 --- a/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c @@ -10,14 +10,11 @@ #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_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(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; @@ -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) @@ -224,19 +212,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, 3); - 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..1ace2bb4 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) @@ -148,7 +136,9 @@ START_TEST(conv_ci12_4ci16_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 / 4); memcpy(out2_etalon, out[1], bzout / 4); memcpy(out3_etalon, out[2], bzout / 4); @@ -212,18 +202,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_4cf32_utest.c b/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c index 19fa718b..3d9a264f 100644 --- a/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c @@ -10,9 +10,9 @@ #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) @@ -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) @@ -195,18 +186,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_f32_i12_utest.c b/src/lib/xdsp/utests/conv_f32_i12_utest.c index b4f009e7..98b6f0d1 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() @@ -159,7 +150,9 @@ START_TEST(conv_f32_i12_check_simd) //get etalon output data (generic foo) (*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 +166,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 +209,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..23ca462a 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 @@ -200,19 +192,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..53546797 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) @@ -194,19 +185,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..0c040460 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) @@ -158,18 +148,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..2125c9ad 100644 --- a/src/lib/xdsp/utests/fft_window_cf32_utest.c +++ b/src/lib/xdsp/utests/fft_window_cf32_utest.c @@ -6,7 +6,7 @@ #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 }; @@ -30,31 +30,15 @@ static void recalcWnd(unsigned fft_size) 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) * 2 * FFT_SIZE); + ck_assert_int_eq(res, 0); for(unsigned i = 0; i < FFT_SIZE; ++i) { @@ -165,17 +149,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/wvlt_sincos_i16_utest.c b/src/lib/xdsp/utests/wvlt_sincos_i16_utest.c index d962e9a9..a9c83ce4 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)) @@ -43,15 +43,17 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); - - 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)); + 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) ); @@ -99,18 +101,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 = get_wvlt_sincos_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, get_wvlt_sincos_i16_c, &last_fn_name); } static sincos_i16_interleaved_ctrl_function_t get_fn_interleaved(generic_opts_t o, int log) @@ -341,20 +332,14 @@ END_TEST Suite * wvlt_sincos_i16_suite(void) { - Suite *s; - TCase *tc_core; - 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); - 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..22fd488f 100644 --- a/src/lib/xdsp/utests/xdsp_utest_common.h +++ b/src/lib/xdsp/utests/xdsp_utest_common.h @@ -4,17 +4,62 @@ #ifndef XDSP_UTEST_COMMON_H #define XDSP_UTEST_COMMON_H -#include #include #include +#include +#include + +#include "../../cal/opt_func.h" +#include "conv.h" + #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/xfft_fftad_utest.c b/src/lib/xdsp/utests/xfft_fftad_utest.c index 502893df..c675e4bc 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 @@ -35,11 +35,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) @@ -198,17 +201,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/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..8ad4b8d9 --- /dev/null +++ b/src/utests/lmk05318_solver_test.c @@ -0,0 +1,445 @@ +#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 ); +} + +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 ); +} + +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 ); +} + +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 ); + +} + +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 ); + +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + + +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 ); +} + +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 );} + +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); + + 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..7bb4b6fc --- /dev/null +++ b/src/utests/lmx1204_solver_test.c @@ -0,0 +1,138 @@ +#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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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..0f65f122 --- /dev/null +++ b/src/utests/lmx1214_solver_test.c @@ -0,0 +1,140 @@ +#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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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..f3655069 --- /dev/null +++ b/src/utests/lmx2820_solver_test.c @@ -0,0 +1,204 @@ +#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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + + +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..eb1f4209 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) { @@ -28,6 +32,12 @@ int main(int argc, char** argv) 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()); + + srunner_set_fork_status (sr, CK_NOFORK); srunner_run_all(sr, (argc > 1) ? CK_VERBOSE : CK_NORMAL); number_failed = srunner_ntests_failed(sr); From 15c19bc5d0031da0aed9e2d405c0b5e1af58b074 Mon Sep 17 00:00:00 2001 From: Ivan <33198864+vd2org@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:07:48 +0400 Subject: [PATCH 2/4] Revert "Feature pe sync and xmass (#82)" (#83) This reverts commit 5e57b95d7b39486eb4588a771162054bfd009ace. --- src/hwparser/gen_h.py | 78 +- src/lib/cal/opt_func.c | 43 - src/lib/cal/opt_func.h | 12 - src/lib/device/CMakeLists.txt | 1 - src/lib/device/device_fe.c | 144 +- .../device/ext_simplesync/ext_simplesync.c | 71 +- src/lib/device/ext_xmass/CMakeLists.txt | 24 - src/lib/device/ext_xmass/ext_xmass.c | 304 -- src/lib/device/ext_xmass/ext_xmass.h | 61 - src/lib/device/ext_xmass/ext_xmass_ctrl.yaml | 71 - src/lib/device/m2_dsdr/m2_dsdr.c | 73 +- src/lib/device/m2_lm7_1/lms7002m_ctrl.c | 14 +- src/lib/device/m2_lm7_1/lms7002m_ctrl.h | 4 +- src/lib/device/m2_lm7_1/m2_lm7_1.c | 42 +- src/lib/device/m2_lm7_1/xsdr_ctrl.c | 311 +- src/lib/device/m2_lm7_1/xsdr_ctrl.h | 9 +- src/lib/device/mdev.c | 11 +- src/lib/device/pe_sync/pe_sync.c | 378 +-- src/lib/device/pe_sync/sync_const.h | 4 +- src/lib/hw/CMakeLists.txt | 6 +- src/lib/hw/ad5662/ad5662.c | 6 - src/lib/hw/ad5662/ad5662.h | 7 - src/lib/hw/ad5662/ad5662.yaml | 34 - src/lib/hw/common/common.c | 123 - src/lib/hw/common/common.h | 22 - src/lib/hw/lmk05318/lmk05318.c | 2596 +---------------- src/lib/hw/lmk05318/lmk05318.h | 208 +- src/lib/hw/lmk05318/lmk05318.yaml | 219 +- src/lib/hw/lmk05318/lmk05318_rom.h | 676 +++++ src/lib/hw/lmk1d1208i/lmk1d1208i.c | 134 - src/lib/hw/lmk1d1208i/lmk1d1208i.h | 62 - src/lib/hw/lmk1d1208i/lmk1d1208i.yaml | 228 -- src/lib/hw/lmx1204/lmx1204.c | 846 ------ src/lib/hw/lmx1204/lmx1204.h | 118 - src/lib/hw/lmx1204/lmx1204.yaml | 979 ------- src/lib/hw/lmx1205/lmx1205.c | 6 - src/lib/hw/lmx1205/lmx1205.h | 7 - src/lib/hw/lmx1205/lmx1205.yaml | 1175 -------- src/lib/hw/lmx1214/lmx1214.c | 506 ---- src/lib/hw/lmx1214/lmx1214.h | 64 - src/lib/hw/lmx1214/lmx1214.yaml | 521 ---- src/lib/hw/lmx2820/lmx2820.c | 1249 -------- src/lib/hw/lmx2820/lmx2820.h | 95 - src/lib/hw/lmx2820/lmx2820.yaml | 1719 ----------- src/lib/hw/lp87524j/lp87524j.c | 6 - src/lib/hw/lp87524j/lp87524j.h | 7 - src/lib/hw/lp87524j/lp87524j.yaml | 71 - src/lib/hw/tca9555/tca9555.c | 46 - src/lib/hw/tca9555/tca9555.h | 27 - src/lib/hw/tca9555/tca9555.yaml | 87 - src/lib/lowlevel/usb_uram/usb_uram_generic.c | 8 +- src/lib/lowlevel/usb_uram/usb_uram_libusb.c | 15 +- src/lib/lowlevel/usdr_lowlevel.c | 5 - src/lib/lowlevel/usdr_lowlevel.h | 3 +- src/lib/xdsp/utests/README | 23 - src/lib/xdsp/utests/conv_2cf32_ci12_utest.c | 41 +- src/lib/xdsp/utests/conv_2cf32_ci16_utest.c | 39 +- src/lib/xdsp/utests/conv_2ci16_ci12_utest.c | 41 +- src/lib/xdsp/utests/conv_2ci16_ci16_utest.c | 39 +- src/lib/xdsp/utests/conv_4cf32_ci12_utest.c | 48 +- src/lib/xdsp/utests/conv_4cf32_ci16_utest.c | 45 +- src/lib/xdsp/utests/conv_4ci16_ci12_utest.c | 45 +- src/lib/xdsp/utests/conv_4ci16_ci16_utest.c | 47 +- src/lib/xdsp/utests/conv_ci12_2cf32_utest.c | 53 +- src/lib/xdsp/utests/conv_ci12_2ci16_utest.c | 43 +- src/lib/xdsp/utests/conv_ci12_4cf32_utest.c | 54 +- src/lib/xdsp/utests/conv_ci12_4ci16_utest.c | 54 +- src/lib/xdsp/utests/conv_ci16_2cf32_utest.c | 43 +- src/lib/xdsp/utests/conv_ci16_2ci16_utest.c | 41 +- src/lib/xdsp/utests/conv_ci16_4cf32_utest.c | 51 +- src/lib/xdsp/utests/conv_ci16_4ci16_utest.c | 57 +- src/lib/xdsp/utests/conv_f32_i12_utest.c | 40 +- src/lib/xdsp/utests/conv_f32_i16_utest.c | 40 +- src/lib/xdsp/utests/conv_i12_f32_utest.c | 46 +- src/lib/xdsp/utests/conv_i12_i16_utest.c | 39 +- src/lib/xdsp/utests/conv_i16_f32_utest.c | 36 +- src/lib/xdsp/utests/conv_i16_i12_utest.c | 38 +- src/lib/xdsp/utests/fft_window_cf32_utest.c | 45 +- src/lib/xdsp/utests/wvlt_sincos_i16_utest.c | 53 +- src/lib/xdsp/utests/xdsp_utest_common.h | 55 +- src/lib/xdsp/utests/xfft_fftad_utest.c | 30 +- src/lib/xdsp/utests/xfft_rtsa_utest.c | 38 +- src/utests/CMakeLists.txt | 5 - src/utests/lmk05318_solver_test.c | 445 --- src/utests/lmx1204_solver_test.c | 138 - src/utests/lmx1214_solver_test.c | 140 - src/utests/lmx2820_solver_test.c | 204 -- src/utests/test_suite.c | 10 - 88 files changed, 1819 insertions(+), 13883 deletions(-) delete mode 100644 src/lib/device/ext_xmass/CMakeLists.txt delete mode 100644 src/lib/device/ext_xmass/ext_xmass.c delete mode 100644 src/lib/device/ext_xmass/ext_xmass.h delete mode 100644 src/lib/device/ext_xmass/ext_xmass_ctrl.yaml delete mode 100644 src/lib/hw/ad5662/ad5662.c delete mode 100644 src/lib/hw/ad5662/ad5662.h delete mode 100644 src/lib/hw/ad5662/ad5662.yaml delete mode 100644 src/lib/hw/common/common.c delete mode 100644 src/lib/hw/common/common.h create mode 100644 src/lib/hw/lmk05318/lmk05318_rom.h delete mode 100644 src/lib/hw/lmk1d1208i/lmk1d1208i.c delete mode 100644 src/lib/hw/lmk1d1208i/lmk1d1208i.h delete mode 100644 src/lib/hw/lmk1d1208i/lmk1d1208i.yaml delete mode 100644 src/lib/hw/lmx1204/lmx1204.c delete mode 100644 src/lib/hw/lmx1204/lmx1204.h delete mode 100644 src/lib/hw/lmx1204/lmx1204.yaml delete mode 100644 src/lib/hw/lmx1205/lmx1205.c delete mode 100644 src/lib/hw/lmx1205/lmx1205.h delete mode 100644 src/lib/hw/lmx1205/lmx1205.yaml delete mode 100644 src/lib/hw/lmx1214/lmx1214.c delete mode 100644 src/lib/hw/lmx1214/lmx1214.h delete mode 100644 src/lib/hw/lmx1214/lmx1214.yaml delete mode 100644 src/lib/hw/lmx2820/lmx2820.c delete mode 100644 src/lib/hw/lmx2820/lmx2820.h delete mode 100644 src/lib/hw/lmx2820/lmx2820.yaml delete mode 100644 src/lib/hw/lp87524j/lp87524j.c delete mode 100644 src/lib/hw/lp87524j/lp87524j.h delete mode 100644 src/lib/hw/lp87524j/lp87524j.yaml delete mode 100644 src/lib/hw/tca9555/tca9555.c delete mode 100644 src/lib/hw/tca9555/tca9555.h delete mode 100644 src/lib/hw/tca9555/tca9555.yaml delete mode 100644 src/lib/xdsp/utests/README delete mode 100644 src/utests/lmk05318_solver_test.c delete mode 100644 src/utests/lmx1204_solver_test.c delete mode 100644 src/utests/lmx1214_solver_test.c delete mode 100644 src/utests/lmx2820_solver_test.c diff --git a/src/hwparser/gen_h.py b/src/hwparser/gen_h.py index b1b7a111..665caf09 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("Rigester `%s` is already in flat map! Rename it" % name)) + # TODO: parse ucnt if r.ucnt == 1: self.regs[name] = r.addr @@ -48,12 +48,14 @@ 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: @@ -64,7 +66,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) @@ -72,22 +74,16 @@ def fieldName(self, field: reg_parser.ParserFields) -> str: return field.name + def normalize(self, name: str) -> str: return (name.replace('-', '_') - .replace('<=', 'LE') - .replace('>=', 'GE') - .replace('>', 'GT') - .replace('<', 'LT') - .replace('=', 'EQ') - .replace('+', 'PL') - .replace("'", 'MARK') - .replace('.', '_') - .replace(',', '_') - .replace(' ', '_') - .replace('(', '') - .replace('|', 'OR') - .replace(')', '') - .replace('/', 'DIV')) + .replace('.', '_') + .replace(',', '_') + .replace(' ', '_') + .replace('(', '') + .replace('|', 'OR') + .replace(')', '') + .replace('/', 'DIV')) def ser_ch_fenum(self, reg: reg_parser.ParserRegs, name: str) -> str: vt = False @@ -100,7 +96,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: @@ -169,13 +165,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 @@ -185,19 +181,11 @@ 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('-', '_') @@ -225,12 +213,10 @@ 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: @@ -242,10 +228,8 @@ 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) @@ -263,9 +247,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 */") @@ -300,10 +284,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/cal/opt_func.c b/src/lib/cal/opt_func.c index dddddbce..bdfeaf9b 100644 --- a/src/lib/cal/opt_func.c +++ b/src/lib/cal/opt_func.c @@ -4,7 +4,6 @@ #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) { @@ -171,45 +170,3 @@ 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 5c37a9ab..7030271d 100644 --- a/src/lib/cal/opt_func.h +++ b/src/lib/cal/opt_func.h @@ -8,9 +8,6 @@ #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); \ @@ -58,14 +55,5 @@ 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/device/CMakeLists.txt b/src/lib/device/CMakeLists.txt index 1192cd3c..4ae4b9a4 100644 --- a/src/lib/device/CMakeLists.txt +++ b/src/lib/device/CMakeLists.txt @@ -20,7 +20,6 @@ add_subdirectory(ext_pciefe) add_subdirectory(ext_supersync) add_subdirectory(ext_simplesync) add_subdirectory(ext_fe_100_5000) -add_subdirectory(ext_xmass) add_subdirectory(u3_limesdr) diff --git a/src/lib/device/device_fe.c b/src/lib/device/device_fe.c index 5176701c..d44d521d 100644 --- a/src/lib/device/device_fe.c +++ b/src/lib/device/device_fe.c @@ -9,7 +9,6 @@ #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 #include @@ -25,24 +24,13 @@ 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_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_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_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); @@ -53,36 +41,32 @@ 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_simplesync_params[] = { - { "/debug/hw/lmk05318/0/reg", { _debug_simplesync_lmk05318_reg_set, _debug_simplesync_lmk05318_reg_get }}, +static +const usdr_dev_param_func_t s_lmk05318_params[] = { + { "/debug/hw/lmk05318/0/reg", { _debug_lmk05318_reg_set, _debug_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[] = { - { "/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 }}, +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 }}, }; enum fe_type { FET_PCIE_DEVBOARD, - FET_PCIE_XMASS, FET_PCIE_SUPER_SYNC, FET_PCIE_SIMPLE_SYNC, FET_PICE_BREAKOUT, @@ -94,7 +78,6 @@ typedef enum fe_type fe_type_t; static const char* s_fe_names[] = { "pciefe", - "xmass", "supersync", "simplesync", "exm2pe", @@ -112,7 +95,6 @@ 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; } fe; uint32_t debug_pciefe_last; @@ -120,7 +102,6 @@ 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; @@ -202,7 +183,6 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi 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; default: return -EIO; } @@ -250,8 +230,8 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi case FET_PCIE_SIMPLE_SYNC: res = usdr_vfs_obj_param_init_array_param(base, (void*)n, - s_simplesync_params, - SIZEOF_ARRAY(s_simplesync_params)); + s_lmk05318_params, + SIZEOF_ARRAY(s_lmk05318_params)); break; case FET_PCIE_SUPER_SYNC: res = usdr_vfs_obj_param_init_array_param(base, @@ -265,12 +245,6 @@ int device_fe_probe(device_t* base, const char* compat, const char* fename, unsi 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*)n, - s_xmass_params, - SIZEOF_ARRAY(s_xmass_params)); - break; default: break; } @@ -397,24 +371,18 @@ int _debug_pciefe_cmd_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) return res; } -static int _debug_lmk05318_reg_get(dev_fe_t* o, uint64_t* ovalue) + +int _debug_lmk05318_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_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_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) +int _debug_lmk05318_reg_set(pdevice_t ud_x, pusdr_vfs_obj_t obj, uint64_t value) { + dev_fe_t* o = (dev_fe_t*)obj->object; int res; unsigned addr = (value >> 8) & 0x7fff; unsigned data = value & 0xff; @@ -422,14 +390,14 @@ int _debug_lmk05318_reg_set(dev_fe_t* o, lmk05318_state_t* lmk, uint64_t value) o->debug_lmk05318_last = ~0u; - if (!(value & 0x800000)) { - res = lmk05318_reg_wr(lmk, addr, data); + if (value & 0x800000) { + res = lmk05318_reg_wr(&o->fe.simplesync.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(lmk, addr, &d); + res = lmk05318_reg_rd(&o->fe.simplesync.lmk, addr, &d); o->debug_lmk05318_last = d; USDR_LOG("XDEV", USDR_LOG_WARNING, "LMK05318 RD REG %04x <= %04x\n", @@ -440,72 +408,6 @@ int _debug_lmk05318_reg_set(dev_fe_t* o, lmk05318_state_t* lmk, 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/ext_simplesync/ext_simplesync.c b/src/lib/device/ext_simplesync/ext_simplesync.c index 8b0e1e49..87d3485a 100644 --- a/src/lib/device/ext_simplesync/ext_simplesync.c +++ b/src/lib/device/ext_simplesync/ext_simplesync.c @@ -41,16 +41,6 @@ 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, @@ -87,30 +77,7 @@ int board_ext_simplesync_init(lldev_t dev, // Wait for power up usleep(50000); - 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 - + res = lmk05318_create(dev, subdev, i2ca, 0, &ob->lmk); if (res) return res; @@ -118,40 +85,16 @@ 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) { - int res = 0; + unsigned div = 255; + int res = lmk05318_tune_apll2(&ob->lmk, meas_lo, &div); - 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 + 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); } return res; diff --git a/src/lib/device/ext_xmass/CMakeLists.txt b/src/lib/device/ext_xmass/CMakeLists.txt deleted file mode 100644 index f95299d3..00000000 --- a/src/lib/device/ext_xmass/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# 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 deleted file mode 100644 index 0a579cb9..00000000 --- a/src/lib/device/ext_xmass/ext_xmass.c +++ /dev/null @@ -1,304 +0,0 @@ -// 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 "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 -// -// 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, -}; - -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); - 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; - - //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 deleted file mode 100644 index 1c45e08d..00000000 --- a/src/lib/device/ext_xmass/ext_xmass.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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 deleted file mode 100644 index 74f89f52..00000000 --- a/src/lib/device/ext_xmass/ext_xmass_ctrl.yaml +++ /dev/null @@ -1,71 +0,0 @@ -# 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_B[0 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 diff --git a/src/lib/device/m2_dsdr/m2_dsdr.c b/src/lib/device/m2_dsdr/m2_dsdr.c index f5cb2c90..9b5a8d16 100644 --- a/src/lib/device/m2_dsdr/m2_dsdr.c +++ b/src/lib/device/m2_dsdr/m2_dsdr.c @@ -1105,7 +1105,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", @@ -1421,66 +1421,28 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** break; } } - usleep(100000); + usleep(40000); - // - //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, 491520000, false, OUT_OFF); - lmk05318_port_request(&lmk_out[1], 1, 491520000, 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, d->dac_rate / 2, false, LVDS); - - res = lmk05318_create(dev, d->subdev, I2C_LMK, 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; + 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; } + // Update deviders for 245/491MSPS rate + if (d->jesdv == DSDR_JESD204C_6664_491) { + // GT should be 245.76 + // FPGA SYSCLK should be 245.76 - //wait for lock - res = lmk05318_wait_apll1_lock(&d->lmk, 100000); - res = res ? res : lmk05318_wait_apll2_lock(&d->lmk, 100000); - - 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; + 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); } - //sync to make APLL1/APLL2 & out channels in-phase - res = lmk05318_sync(&d->lmk); - if(res) - return res; + usleep(1000); - USDR_LOG("DSDR", USDR_LOG_INFO, "LMK03518 outputs synced"); - //LMK05318 init end - // + res = res ? res : lmk05318_check_lock(&d->lmk, &los); for (int i = 0; i < 5; i++) { uint32_t clk = 0; @@ -1490,6 +1452,9 @@ int usdr_device_m2_dsdr_initialize(pdevice_t udev, unsigned pcount, const char** 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 : dev_gpi_get32(dev, IGPI_PGOOD, &pg); diff --git a/src/lib/device/m2_lm7_1/lms7002m_ctrl.c b/src/lib/device/m2_lm7_1/lms7002m_ctrl.c index bb7c7fe4..41632e4b 100644 --- a/src/lib/device/m2_lm7_1/lms7002m_ctrl.c +++ b/src/lib/device/m2_lm7_1/lms7002m_ctrl.c @@ -768,7 +768,7 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, USDR_LOG("XDEV", USDR_LOG_INFO, "%s: RBB Restore BW[%d]=%d\n", devstr, ich, d->rx_bw[ich].value); } else { - bandwidth = d->cgen_clk / d->rxcgen_div / d->rxtsp_div / d->rx_dsp_decim; + 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); } @@ -815,7 +815,7 @@ int lms7002m_streaming_up(lms7002_dev_t *d, unsigned dir, devstr, 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_dsp_inter; + 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); } @@ -927,8 +927,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_dsp_inter = 1; // Off chip extra interpolator - unsigned rx_dsp_decim = 1; // Off chip extra decimator + unsigned tx_host_inter = 1; // Off chip extra interpolator + unsigned rx_host_decim = 1; // Off chip extra decimator unsigned tx_host_mul = 1; unsigned rx_host_div = 1; unsigned txmaster_min = mpy_dac * dacclk; @@ -991,8 +991,8 @@ int lms7002m_samplerate(lms7002_dev_t *d, d->txcgen_div = mpy_dac; d->rxtsp_div = rxdiv; d->txtsp_div = txdiv; - d->tx_dsp_inter = tx_dsp_inter; - d->rx_dsp_decim = rx_dsp_decim; + d->tx_host_inter = tx_host_inter; + d->rx_host_decim = rx_host_decim; for (unsigned j = 0; j < 4; j++) { unsigned clkdiv = (mpy_dac == 1) ? 0 : @@ -1075,7 +1075,7 @@ int lms7002m_samplerate(lms7002_dev_t *d, rxrate / 1e6, txrate / 1e6, rxdiv, rx_host_div, txdiv, tx_host_mul, cgen_rate / mpy_adc / 1e6, cgen_rate / mpy_dac / 1e6, - tx_dsp_inter, rx_dsp_decim, cgen_rate / 1e6, + tx_host_inter, rx_host_decim, cgen_rate / 1e6, rxtsp_div, txtsp_div, sisoddr_rx, sisoddr_tx, d->fref / 1e6); diff --git a/src/lib/device/m2_lm7_1/lms7002m_ctrl.h b/src/lib/device/m2_lm7_1/lms7002m_ctrl.h index 785185a9..babd0ed9 100644 --- a/src/lib/device/m2_lm7_1/lms7002m_ctrl.h +++ b/src/lib/device/m2_lm7_1/lms7002m_ctrl.h @@ -87,8 +87,8 @@ struct lms7002_dev uint8_t txcgen_div; uint8_t rxtsp_div; uint8_t txtsp_div; - uint8_t tx_dsp_inter; - uint8_t rx_dsp_decim; + uint8_t tx_host_inter; + uint8_t rx_host_decim; uint8_t rx_no_siso_map; uint8_t tx_no_siso_map; 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 81055db5..a72b81e3 100644 --- a/src/lib/device/m2_lm7_1/m2_lm7_1.c +++ b/src/lib/device/m2_lm7_1/m2_lm7_1.c @@ -225,9 +225,6 @@ static int dev_m2_lm7_1_debug_clkinfo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uin 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_sdr_tx_phase_ovr_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value); -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 }}, @@ -245,10 +242,6 @@ const usdr_dev_param_func_t s_fparams_m2_lm7_1_rev000[] = { { "/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/tx/phase_ovr", { dev_m2_lm7_1_sdr_tx_phase_ovr_set, NULL }}, - { "/dm/sdr/0/rx/dccorr", { dev_m2_lm7_1_sdr_rx_dccorr_set, NULL }}, { "/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 }}, @@ -350,7 +343,7 @@ int dev_m2_lm7_1_dev_dac_vctcxo_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t int dev_m2_lm7_1_phyrxlm_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { - return xsdr_phy_tune_rx(&((struct dev_m2_lm7_1_gps *)ud)->xdev, value); + return xsdr_phy_tune(&((struct dev_m2_lm7_1_gps *)ud)->xdev, value); } int dev_m2_lm7_1_lms7002rxlml_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) @@ -378,18 +371,6 @@ 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_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_debug_lms7002m_reg_set(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t value) { @@ -986,24 +967,6 @@ int dev_m2_lm7_1_sensor_freqpps_get(pdevice_t ud, pusdr_vfs_obj_t obj, uint64_t return res; } -static -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 = -ENOENT; - if (ls_op == USDR_LSOP_DRP) { - res = xsdr_override_drp(&d->xdev, ls_op_addr, meminsz, pin, memoutsz, pout); - } - - if (res != -ENOENT) - return res; - - return d->p_original_ops->ls_op(dev, subdev, ls_op, ls_op_addr, meminsz, pin, memoutsz, pout); -} - static int usdr_device_m2_lm7_1_stream_initialize(lldev_t dev, subdev_t subdev, lowlevel_stream_params_t* params, stream_t* channel) { @@ -1087,7 +1050,6 @@ int usdr_device_m2_lm7_1_initialize(pdevice_t udev, unsigned pcount, const char* // Proxy operations memcpy(&d->my_ops, lowlevel_get_ops(dev), sizeof (lowlevel_ops_t)); - d->my_ops.ls_op = &usdr_device_m2_lm7_1_lsop; 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->p_original_ops = lowlevel_get_ops(dev); @@ -1176,7 +1138,6 @@ 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 } // Reset samplerate with proper bifurcation flags @@ -1216,7 +1177,6 @@ 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 } res = create_sfetrx4_stream(dev, CORE_SFETX_DMA32_R0, dformat, channels->count, &lchans, pktsyms, diff --git a/src/lib/device/m2_lm7_1/xsdr_ctrl.c b/src/lib/device/m2_lm7_1/xsdr_ctrl.c index b4bfbcb0..b57334f5 100644 --- a/src/lib/device/m2_lm7_1/xsdr_ctrl.c +++ b/src/lib/device/m2_lm7_1/xsdr_ctrl.c @@ -24,10 +24,6 @@ #define MAX(x,y) (((x) > (y)) ? (x) : (y)) #endif -enum { - DRP_MMCM_PORT_RX = 0, - DRP_MMCM_PORT_TX = 1, -}; // TXA RXB // TXB RXA @@ -128,161 +124,7 @@ int xsdr_set_samplerate(xsdr_dev_t *d, return xsdr_set_samplerate_ex(d, rxrate, txrate, adcclk, dacclk, 0); } - -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, -}; - -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_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 { - return -EINVAL; - } - - res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_DRP, drp_cmd); - - // 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(1000); - } - - if (res) - return res; - - USDR_LOG("XDEV", USDR_LOG_ERROR, "MMCM DRP_TX CMD=%08x RB=%08x\n", drp_cmd, rb); - - if (meminsz) { - *((uint16_t*)pin) = rb >> 16; - } - return 0; -} - -int xsdr_configure_lml_mmcm_tx(xsdr_dev_t *d) -{ - bool nomul = d->base.lml_mode.txsisoddr || (d->base.txtsp_div > 1); - unsigned tx_mclk = d->base.cgen_clk / d->base.txcgen_div / d->base.lml_mode.txdiv; - unsigned io_clk = (nomul) ? tx_mclk : tx_mclk * 2; - unsigned vco_div_io_m = (MMCM_VCO_MAX + io_clk - 1) / io_clk; - if (vco_div_io_m > 63) - vco_div_io_m = 63; - - unsigned vco_div_io = vco_div_io_m & 0xfc; //Multiply of 4 - int res = 0; - struct mmcm_config_raw cfg_raw; - memset(&cfg_raw, 0, sizeof(cfg_raw)); - - if (vco_div_io * io_clk < MMCM_VCO_MIN) { - if ((vco_div_io + 2) * io_clk > MMCM_VCO_MAX) { - vco_div_io += 1; - } else { - vco_div_io += 2; - } - } - - res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, 0); - res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_IQSEL, d->base.lml_mode.txsisoddr ? 0b1010 : 0b1100); - - 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; - - 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; - - 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; - - cfg_raw.ports[CLKOUT_PORT_0].phase = ((vco_div_io & 3) == 0) ? 0 : - ((vco_div_io & 3) == 1) ? 2 : - ((vco_div_io & 3) == 2) ? 4 : 6; - cfg_raw.ports[CLKOUT_PORT_0].delay = vco_div_io / 4; - - cfg_raw.ports[CLKOUT_PORT_3].phase = cfg_raw.ports[CLKOUT_PORT_0].phase; - cfg_raw.ports[CLKOUT_PORT_3].delay = vco_div_io / 2; - - if (d->tx_override_phase) { - unsigned raw = d->tx_override_phase - 1; - cfg_raw.ports[CLKOUT_PORT_0].phase = raw & 7; - cfg_raw.ports[CLKOUT_PORT_3].phase = raw & 7; - cfg_raw.ports[CLKOUT_PORT_0].delay = (raw >> 3); - cfg_raw.ports[CLKOUT_PORT_3].delay = 2 * (raw >> 3); - } - - 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_LOG("XDEV", USDR_LOG_ERROR, "MMCM_TX set to MCLK = %.3f IOCLK = %.3f Mhz IODIV = %d FWDCLK_DELAY%s = %d (VCO %d) SISO_DDR=%d\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); - - res = res ? res : mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_TX, &cfg_raw); - - // Reset MMCM - res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, 1); - usleep(100); - res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_MMCM_CTRL, 0); - - for (unsigned k = 0; k < 10; k++) { - // Wait for lock - uint32_t rb; - res = res ? res : lowlevel_reg_rd32(d->base.lmsstate.dev, d->base.lmsstate.subdev, - REG_CFG_PHY_1, &rb); - if (res) - break; - - USDR_LOG("XDEV", USDR_LOG_INFO, "MMCM FLAGS:%08x\n", rb); - if (rb & (1 << 8)) - return 0; - - usleep(5000); - } - - return res; -} - -int xsdr_configure_lml_mmcm_rx(xsdr_dev_t *d) +int xsdr_configure_lml_mmcm(xsdr_dev_t *d) { 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; @@ -323,7 +165,7 @@ int xsdr_configure_lml_mmcm_rx(xsdr_dev_t *d) cfg_raw.ports[CLKOUT_PORT_FB].period_l = vco_div_io; cfg_raw.ports[CLKOUT_PORT_FB].period_h = vco_div_io; } - USDR_LOG("XDEV", USDR_LOG_ERROR, "MMCM_RX set to MCLK = %.3f IOCLK = %.3f Mhz IODIV = %d\n", + 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, @@ -339,7 +181,7 @@ int xsdr_configure_lml_mmcm_rx(xsdr_dev_t *d) return res; usleep(1000); - res = mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_RX, &cfg_raw); + res = mmcm_init_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, &cfg_raw); if (res) return res; @@ -356,8 +198,7 @@ int xsdr_configure_lml_mmcm_rx(xsdr_dev_t *d) // Wait for lock res = lowlevel_reg_rd32(d->base.lmsstate.dev, d->base.lmsstate.subdev, REG_CFG_PHY_0, &rb); - if (res) - break; + USDR_LOG("XDEV", USDR_LOG_INFO, "MMCM FLAGS:%08x\n", rb); if (rb & (1 << 16)) @@ -365,13 +206,43 @@ int xsdr_configure_lml_mmcm_rx(xsdr_dev_t *d) usleep(5000); } + 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); + if (res) + return res; + + + + //////////////////// + + 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); + 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); + } +#endif return res; } -int xsdr_phy_tune_rx(xsdr_dev_t *d, unsigned val) +int xsdr_phy_tune(xsdr_dev_t *d, unsigned val) { - int res = mmcm_set_phdigdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_RX, CLKOUT_PORT_0, val); + int res = mmcm_set_phdigdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, CLKOUT_PORT_0, val); return res; } @@ -392,33 +263,22 @@ int xsdr_hwchans_cnt(xsdr_dev_t *d, bool rx, unsigned chans) return 0; } -enum { - PHY_CFG_VALID_MSK = 0x80, - PHY_CFG_LML2_IS_RX = 0x40, - PHY_CFG_TX_MMCM = 0x20, - PHY_CFG_RX_MMCM = 0x10, -}; - int xsdr_set_samplerate_ex(xsdr_dev_t *d, unsigned rxrate, unsigned txrate, unsigned adcclk, unsigned dacclk, unsigned flags) { - const uint8_t phycfg_id = (d->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 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 (!(phycfg_id & PHY_CFG_VALID_MSK)) { - USDR_LOG("XDEV", USDR_LOG_ERROR, "Incompatible firmware, please update to 20250501 at least!\n"); - return -ENOTSUP; - } - res = _xsdr_checkpwr(d); if (res) return res; @@ -427,10 +287,12 @@ 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_is_1); + res = lms7002m_samplerate(&d->base, rxrate, txrate, adcclk, dacclk, m_flags, rx_port_1); if (res) return res; @@ -450,58 +312,47 @@ int xsdr_set_samplerate_ex(xsdr_dev_t *d, d->afe_active = true; } - if (rxrate) { - if (rx_mmcm) { - res = res ? res : xsdr_configure_lml_mmcm_rx(d); - } - - sisosdrflag = d->base.lml_mode.rxsisoddr ? 8 : 0; - res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000007 | sisosdrflag); - usleep(100); - res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000000 | sisosdrflag); - + if (mmcmrx) { + res = xsdr_configure_lml_mmcm(d); + if (res) + return res; + } - if (rx_mmcm) { - // Configure PHY (reset) - // TODO phase search + 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); - for (unsigned h = 0; h < 16; h++) { - res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000007 | sisosdrflag); - usleep(100); - res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x80000000 | sisosdrflag); + 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); - USDR_LOG("XDEV", USDR_LOG_INFO, "PHASE=%d\n", h); - res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x00000000); - unsigned tmp; //, tmp2; - for (unsigned k = 0; k < 100; k++) { - res = res ? res : lowlevel_reg_rd32(dev, subdev, REG_CFG_PHY_0, &tmp); - } - res = res ? res : mmcm_set_digdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, DRP_MMCM_PORT_RX, CLKOUT_PORT_0, h); + 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); } - } - // Switch to clock meas - res = res ? res : lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x02000000); + mmcm_set_digdelay_raw(d->base.lmsstate.dev, d->base.lmsstate.subdev, 0, CLKOUT_PORT_0, h); + } } - if (txrate) { - res = res ? res : xsdr_phy_tx_reg(d, PHY_REG_PORT_CTRL, 1); + // Switch to clock meas + res = lowlevel_reg_wr32(dev, subdev, REG_CFG_PHY_0, 0x02000000); - if (tx_mmcm) { - res = res ? res : xsdr_configure_lml_mmcm_tx(d); - } else { - res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS_PWR, 0x8f); - res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS_PWR, 0x8f); - res = res ? res : dev_gpo_set(d->base.lmsstate.dev, IGPO_LMS_PWR, 0x0f); - - 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); - } - } + // uint32_t m; + // res = lowlevel_reg_rd32(dev, subdev, REG_CFG_PHY_0, &m); + // USDR_LOG("XDEV", USDR_LOG_INFO, "MEAS=%08x\n", m); return res; + } @@ -1224,20 +1075,6 @@ int _xsdr_pwren_revo(xsdr_dev_t *d, bool on) return 0; } -int xsdr_set_vio(xsdr_dev_t *d, unsigned vio_mv) -{ - if (!d->new_rev) - return -EINVAL; - - if (vio_mv > 2100) - vio_mv = 2100; - else if (vio_mv < 1600) - vio_mv = 1600; - - USDR_LOG("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, 3, vio_mv); -} - int xsdr_pwren(xsdr_dev_t *d, bool on) { int res; diff --git a/src/lib/device/m2_lm7_1/xsdr_ctrl.h b/src/lib/device/m2_lm7_1/xsdr_ctrl.h index 01f165da..0d98c67d 100644 --- a/src/lib/device/m2_lm7_1/xsdr_ctrl.h +++ b/src/lib/device/m2_lm7_1/xsdr_ctrl.h @@ -49,8 +49,6 @@ struct xsdr_dev unsigned lms7_lob; - int tx_override_phase; - bool afe_active; bool siso_sdr_active_rx; bool siso_sdr_active_tx; @@ -126,7 +124,6 @@ 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); // Enable RFIC, no streaming int xsdr_pwren(xsdr_dev_t *d, bool on); @@ -151,15 +148,11 @@ int xsdrcal_do_meas_nco_avg(void* param, int channel, unsigned logduration, int //int (*set_tx_testsig_fs8)(void* param, int channel); -int xsdr_phy_tune_rx(xsdr_dev_t *d, unsigned val); +int xsdr_phy_tune(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); - enum { XSDR_CAL_RXLO = 1, XSDR_CAL_TXLO = 2, diff --git a/src/lib/device/mdev.c b/src/lib/device/mdev.c index 1f3f6c2a..9bf8c9eb 100644 --- a/src/lib/device/mdev.c +++ b/src/lib/device/mdev.c @@ -380,10 +380,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[k]; + phys_names[k] = channels->phys_names[chans_per_dev * i + k]; } if (channels->phys_nums) { - phys_nums[k] = channels->phys_nums[k]; + phys_nums[k] = channels->phys_nums[chans_per_dev * i + k]; } } @@ -400,15 +400,12 @@ int _mdev_create_stream(device_t* dev, const char* sid, const char* dformat, return -EBUSY; } - USDR_LOG("MDEV", USDR_LOG_INFO, "Creating stream for dev %d with %d channels\n", i, chans_per_dev); + USDR_LOG("MDEV", USDR_LOG_ERROR, "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) { - 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); + if (res) return res; - } mstr->dev_mask[i] = true; diff --git a/src/lib/device/pe_sync/pe_sync.c b/src/lib/device/pe_sync/pe_sync.c index 3865fa71..3a73dcd0 100644 --- a/src/lib/device/pe_sync/pe_sync.c +++ b/src/lib/device/pe_sync/pe_sync.c @@ -18,10 +18,6 @@ #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 @@ -32,17 +28,11 @@ // [6] 24bit 20Mhz AD5662 WR DAC enum i2c_addrs { - I2C_ADDR_LMK05318B = 0x64, - I2C_ADDR_LP87524 = 0x60, + I2C_ADDR_LMK05318B = 0x65, }; enum BUSIDX_I2C { - 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), + I2C_BUS_LMK05318B = MAKE_LSOP_I2C_ADDR(0, 0, 0x67), SPI_INREF_DAC = 0, SPI_OCXO_DAC = 1, @@ -63,7 +53,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, 10 }, + { DNLL_IRQ_COUNT, 16 }, // low level buses { "/ll/irq/0/core", USDR_MAKE_COREID(USDR_CS_AUX, USDR_AC_PIC32_PCI) }, @@ -126,10 +116,6 @@ 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 { @@ -165,21 +151,10 @@ 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; - - dev_gpo_set(dev, IGPO_DISTRIB_CTRL, 0); - dev_gpo_set(dev, IGPO_SY0_CTRL, 0); - dev_gpo_set(dev, IGPO_SY1_CTRL, 0); - + // struct dev_pe_sync *d = (struct dev_pe_sync *)udev; + // lldev_t dev = d->base.dev; + // TODO: power off 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) @@ -187,8 +162,7 @@ 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, s0 = 0, s1 = 0, s2 = 0, s3 = 0; - uint8_t r = 0, r4 = 0, r5 = 0; + uint32_t v = 0; if (getenv("USDR_BARE_DEV")) { USDR_LOG("SYNC", USDR_LOG_WARNING, "USDR_BARE_DEV is set, skipping initialization!\n"); @@ -199,7 +173,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, 0b1101); // Enable GPS + res = res ? res : dev_gpo_set(dev, IGPO_IN_CTRL, 0); // Disable GPS // gpo_sy_ctrl*[0] -- LMX2820 LDO Pwr EN // gpo_sy_ctrl*[1] -- LMX2820 CE pin @@ -208,24 +182,18 @@ 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] -- PDN for LMK05318B + // gpo_gen_ctrl[1] -- PD 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 - // 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)); + res = res ? res : dev_gpo_set(dev, IGPO_GEN_CTRL, (1 << 0) | (1 << 2)); // 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) | (15 << 1)); - - // Wait for all LDOs to settle - usleep(200000); + res = res ? res : dev_gpo_set(dev, IGPO_DISTRIB_CTRL, (1 << 0)); // gpo_led_ctrl[0] -- LEDG[0] // gpo_led_ctrl[1] -- LEDR[0] @@ -237,25 +205,12 @@ 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); - res = res ? res : dev_gpi_get32(dev, IGPI_STAT, &v); - res = res ? res : i2c_reg_rd8(dev, I2C_BUS_LP87524, 0x01, &r); + usleep(1000); - 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); - - 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); + res = res ? res : dev_gpi_get32(dev, IGPI_STAT, &v); + USDR_LOG("SYNC", USDR_LOG_WARNING, "STAT = %08x\n", v); - // Initialize LMK05318B + // TODO: Initialize LMK05318B // XO: 25Mhz // // OUT0: LVDS 125.000 Mhz @@ -266,308 +221,7 @@ 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 - - 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"); - // + // res = res ? res : lmk05318_create() return res; } diff --git a/src/lib/device/pe_sync/sync_const.h b/src/lib/device/pe_sync/sync_const.h index 2d949dcd..6bdbe234 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 = 1, - INT_SPI_2 = 2, + INT_SPI_1 = 2, + INT_SPI_2 = 3, INT_SPI_3 = 3, INT_SPI_4 = 4, INT_SPI_5 = 5, diff --git a/src/lib/hw/CMakeLists.txt b/src/lib/hw/CMakeLists.txt index 5d3fcfcf..f8c7d417 100644 --- a/src/lib/hw/CMakeLists.txt +++ b/src/lib/hw/CMakeLists.txt @@ -1,11 +1,9 @@ # Copyright (c) 2023-2024 Wavelet Lab # SPDX-License-Identifier: MIT -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) +set(HW_FILES si549 si5332 ads42lbx9 tps6594 tmp108 tmp114 lms6002d lms7002m lms8001 lp8758 lmk05318 lmk5c33216 tps6381x dac80501 lmk04832 tca6424a adf4002b lp875484 afe79xx) # YAML registers generators -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) +set(HW_FILES_YAML si5332 lmk05318 lmk5c33216 lms6002d lms7002m lms8001 tps6381x dac80501 lmk04832 xra1405 tca6424a adf4002b lp875484) 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 deleted file mode 100644 index 4d06260f..00000000 --- a/src/lib/hw/ad5662/ad5662.c +++ /dev/null @@ -1,6 +0,0 @@ -// 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 deleted file mode 100644 index 8b24bfe9..00000000 --- a/src/lib/hw/ad5662/ad5662.h +++ /dev/null @@ -1,7 +0,0 @@ -// 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 deleted file mode 100644 index b48f5f8e..00000000 --- a/src/lib/hw/ad5662/ad5662.yaml +++ /dev/null @@ -1,34 +0,0 @@ -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/common/common.c b/src/lib/hw/common/common.c deleted file mode 100644 index d506274d..00000000 --- a/src/lib/hw/common/common.c +++ /dev/null @@ -1,123 +0,0 @@ -// 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 deleted file mode 100644 index dcdc80d6..00000000 --- a/src/lib/hw/common/common.h +++ /dev/null @@ -1,22 +0,0 @@ -// 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/lmk05318/lmk05318.c b/src/lib/hw/lmk05318/lmk05318.c index 0d9a0d70..2db8ac81 100644 --- a/src/lib/hw/lmk05318/lmk05318.c +++ b/src/lib/hw/lmk05318/lmk05318.c @@ -1,333 +1,29 @@ // Copyright (c) 2023-2024 Wavelet Lab // SPDX-License-Identifier: MIT -#include #include #include -#include -#include -#include -#include -#include "def_lmk05318.h" #include "lmk05318.h" +#include "def_lmk05318.h" +#include "lmk05318_rom.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 = 80000000, + APLL1_PD_MAX = 50000000, APLL2_PD_MIN = 10000000, APLL2_PD_MAX = 150000000, - 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, -}; - -#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; + OUT_FREQ_MAX = 800000000ull, }; -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(dpll->dc_mode[i]), - dpll->buf_mode[i], lmk05318_dpll_decode_ref_buf_mode(dpll->buf_mode[i]), - dpll->type[i], lmk05318_dpll_decode_ref_type(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) { @@ -369,2215 +65,177 @@ int lmk05318_reg_wr_n(lmk05318_state_t* d, const uint32_t* regs, unsigned count) return 0; } - -enum -{ - LMK05318_REGADDR_MIN = 0x0000, - LMK05318_REGADDR_MAX = 0x019B, -}; - -static uint32_t registers_map[LMK05318_REGADDR_MAX - LMK05318_REGADDR_MIN + 1]; - -void lmk05318_registers_map_reset() -{ - memset(registers_map, 0xFF, sizeof(registers_map)); -} - -static int lmk05318_add_reg_to_map(lmk05318_state_t* d, const uint32_t* regs, unsigned count) +int lmk05318_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned int flags, lmk05318_state_t* out) { - 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; - } + int res; + uint8_t dummy[4]; - 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; -} + 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); -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; + out->dev = dev; + out->subdev = subdev; + out->lsaddr = lsaddr; - uint16_t addr = registers_map[j] >> 8; - uint8_t data = registers_map[j]; + res = lmk05318_reg_get_u32(out, 0, &dummy[0]); + if (res) + return res; - USDR_LOG("5318", USDR_LOG_DEBUG, "LMK05318 Writing register R%03u: 0x%04x = 0x%02x [0x%06x]", j, addr, data, registers_map[j]); + 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]); - int res = dry_run ? 0 : lmk05318_reg_wr(d, addr, data); - if (res) - return res; + if ( dummy[3] != 0x10 || dummy[2] != 0x0b || dummy[1] != 0x35 || dummy[0] != 0x42 ) { + return -ENODEV; } - lmk05318_registers_map_reset(); - return 0; -} + // Do the initialization + res = lmk05318_reg_wr_n(out, lmk_init, lmk_init_sz); + if (res) + return res; + // Reset + uint32_t regs[] = { + lmk05318_rom[0] | (1 << RESET_SW_OFF), + lmk05318_rom[0] | (0 << RESET_SW_OFF), -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_XO_CONFIG(flags > 1 ? 1 : 0), - usleep(10000); + MAKE_LMK05318_PLL1_CTRL0(0), + MAKE_LMK05318_PLL1_CTRL0(1), + MAKE_LMK05318_PLL1_CTRL0(0), - res = lmk05318_reg_wr_n(out, ®s[1], 1); - if(res) + }; + res = lmk05318_reg_wr_n(out, regs, SIZEOF_ARRAY(regs)); + if (res) return res; - usleep(10000); - return res; -} - -UNUSED static int lmk05318_trigger_reg(lmk05318_state_t* out, uint32_t regs[2]) -{ - return lmk05318_reg_wr_n(out, ®s[0], 2); + 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; } -int lmk05318_softreset(lmk05318_state_t* out) +int lmk05318_tune_apll2(lmk05318_state_t* d, uint32_t freq, unsigned *last_div) { - uint8_t reg_ctrl; - const uint8_t mask = ((uint8_t)1 << RESET_SW_OFF); - - int res = lmk05318_reg_rd(out, DEV_CTL, ®_ctrl); - if(res) - return res; + 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));; + } - uint32_t regs[2] = - { - MAKE_LMK05318_REG_WR(DEV_CTL, reg_ctrl | mask), - MAKE_LMK05318_REG_WR(DEV_CTL, reg_ctrl & ~mask), - }; + 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; - return lmk05318_trigger_reg_with_sleep(out, regs); -} + 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_sync(lmk05318_state_t* out) -{ - uint8_t reg_ctrl; - const uint8_t mask = ((uint8_t)1 << SYNC_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) + res = lmk05318_reg_wr_n(d, regs, SIZEOF_ARRAY(regs)); + 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), - }; - - return lmk05318_trigger_reg_with_sleep(out, regs); + *last_div = div; + return 0; } -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_reset_los_flags(lmk05318_state_t* d) +int lmk05318_set_out_div(lmk05318_state_t* d, unsigned port, unsigned udiv) { - 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 - }; + if (port > 7) + return -EINVAL; + if (udiv == 0) + return -EINVAL; + 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)); } -static int lmk05318_set_xo_bawdetect_registers(lmk05318_state_t* d) +int lmk05318_set_out_mux(lmk05318_state_t* d, unsigned port, bool pll1, unsigned otype) { - int res = 0; - - switch(d->xo.fref) - { - case XO12_8: - { - USDR_LOG("5318", USDR_LOG_INFO, "XO=12.8M, applying specific settings..."); - - 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; - } - case XO25: - { - USDR_LOG("5318", USDR_LOG_INFO, "XO=25M, applying specific settings..."); - - 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 - }; - - res = lmk05318_add_reg_to_map(d, regs, SIZEOF_ARRAY(regs)); - break; + 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; } - case XO26: - { - USDR_LOG("5318", USDR_LOG_INFO, "XO=26M, applying specific settings..."); - - static uint32_t regs[] = - { - 0x00510A, - 0x005200, - 0x00530F, - 0x00543C, - 0x005500, - 0x005600, - 0x00571E, - 0x005884, - 0x005980, - 0x005A00, - 0x005B14, - 0x005C00, - 0x005D0F, - 0x005E3C, - 0x005F00, - 0x006000, - 0x00611E, - 0x006284, - 0x006380, - }; + unsigned mux = (pll1) ? OUT_PLL_SEL_APLL1_P1 : OUT_PLL_SEL_APLL2_P1; - 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 or 26M", (double)d->xo.fref / 1e6); + if (port > 7) return -EINVAL; - } - } - return res; + 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)); } -static int lmk05318_set_common_registers(lmk05318_state_t* d, lmk05318_dpll_settings_t* dpll) +int lmk05318_check_lock(lmk05318_state_t* d, unsigned* los_msk) { + uint8_t los[3]; int res = 0; + unsigned losval; - if(!dpll) - { - d->dpll.enabled = false; - } - - 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)); - } + 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) + if (res) return res; - //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 - - 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 - - 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)); -} + 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); -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(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) - goto have_complete_solution; - - 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; - -have_complete_solution: - - //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(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]); - } + 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]); *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 e9b14153..5d05fd54 100644 --- a/src/lib/hw/lmk05318/lmk05318.h +++ b/src/lib/hw/lmk05318/lmk05318.h @@ -6,76 +6,6 @@ #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; @@ -87,117 +17,23 @@ 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, - 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, + LVCMOS, + OUT_OFF, }; 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; -#define LMK05318_FREQ_DELTA 2 +int lmk05318_create(lldev_t dev, unsigned subdev, unsigned lsaddr, unsigned flags, lmk05318_state_t* out); -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; -} +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); enum lock_msk { LMK05318_LOS_XO = 1, @@ -207,40 +43,12 @@ enum lock_msk { LMK05318_LOPL_DPLL = 16, LMK05318_LOFL_DPLL = 32, - LMK05318_BAW_LOCK = 64, + }; -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_check_lock(lmk05318_state_t* d, unsigned* los_msk); 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 94f3ad65..77f4aaec 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 - rd_mask: 0x800000 + wr_mask: 0x800000 usdr_path: /debug/hw/lmk05318/0/reg addr_width: 16 data_width: 8 @@ -57,21 +57,6 @@ 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: @@ -93,12 +78,6 @@ 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: @@ -391,11 +370,9 @@ 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) @@ -408,25 +385,13 @@ 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: @@ -442,34 +407,23 @@ 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: *ref-input-type + opts: *in-opts - bits: "3-0" name: PRIREF_TYPE - desc: PRIREF Input Type - opts: *ref-input-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 - addr: 0x2F name: PLL_CLK_CFG fields: - bits: "7" name: PLL2_RCLK_SEL - 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. + desc: PLL2 Reference clock selection; 0x0 = VCO1 - Cascaded Mode; 0x1 = XO - addr: 0x30 name: STAT0_SEL fields: @@ -633,8 +587,6 @@ 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. @@ -685,12 +637,6 @@ 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: @@ -702,10 +648,6 @@ 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 @@ -737,9 +679,6 @@ 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 @@ -754,20 +693,13 @@ 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 - - bits: "1-0" - name: PLL2_VCOWAIT - desc: VCO Wait Period. Timeout counter before starting VCO calibration. + 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 - addr: "0x6C:0x6D" name: PLL1_NDIV @@ -777,15 +709,6 @@ 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 @@ -795,12 +718,6 @@ 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 @@ -809,31 +726,12 @@ 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 @@ -852,34 +750,15 @@ 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 @@ -1015,18 +894,9 @@ 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 @@ -1056,18 +926,12 @@ 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 @@ -1098,9 +962,6 @@ 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 @@ -1119,8 +980,11 @@ pages: - addr: 0x10D name: DPLL_REF_DECIMATION - - addr: "0x10E:0x10F" - name: DPLL_REF_FILTSCALAR + - addr: 0x10E + name: DPLL_REF_FILTSCALAR_BY1 + + - addr: 0x10F + name: DPLL_REF_FILTSCALAR_BY0 - addr: 0x110 name: DPLL_REF_FILTGAIN @@ -1164,14 +1028,23 @@ pages: - addr: 0x11D name: DPLL_REF_LPF1GAIN2_FL - - addr: "0x11E:0x11F" - name: DPLL_REF_TMR_FL1 + - 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: "0x120:0x121" - name: DPLL_REF_TMR_FL2 + - addr: 0x122 + name: DPLL_REF_TMR_LCK_BY1 - - addr: "0x122:0x123" - name: DPLL_REF_TMR_LCK + - addr: 0x123 + name: DPLL_REF_TMR_LCK_BY0 - addr: 0x124 name: DPLL_REF_PHC_LPF @@ -1179,8 +1052,11 @@ pages: - addr: 0x125 name: DPLL_REF_PHC_CTRL - - addr: "0x126:0x127" - name: DPLL_REF_PHC_TIMER + - addr: 0x126 + name: DPLL_REF_PHC_TIMER_BY1 + + - addr: 0x127 + name: DPLL_REF_PHC_TIMER_BY0 - addr: 0x128 name: DPLL_REF_QUANT @@ -1217,33 +1093,24 @@ pages: - addr: 0x13F name: DPLL_REF_MASHCTL - fields: - - bits: "4-3" - name: DPLL_REF_DTHRMODE - - bits: "2-0" - name: DPLL_REF_ORDER - - addr: "0x140:0x144" - name: DPLL_REF_LOCKDET_1_5 + - addr: "0x140:0x141" + name: DPLL_REF_LOCKDET_PPM_MAX - - addr: "0x145:0x149" - name: DPLL_REF_LOCKDET_6_10 + - addr: "0x142:0x145" + name: DPLL_REF_LOCKDET_CNTSTRT - - addr: "0x14A:0x14C" - name: DPLL_REF_UNLOCKDET_1_3 + - addr: "0x146:0x149" + name: DPLL_REF_LOCKDET_VCO_CNTSTRT - - addr: "0x14D:0x14F" - name: PLL2_DEN + - addr: "0x14A:0x14B" + name: DPLL_REF_UNLOCKDET_PPM_MAX - - addr: "0x150:0x152" - name: DPLL_REF_UNLOCKDET_VCO_CNTSTRT + - addr: "0x14C:0x14F" + name: DPLL_REF_UNLOCKDET_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: "0x150:0x153" + name: DPLL_REF_UNLOCKDET_VCO_CNTSTRT - 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 new file mode 100644 index 00000000..e5cad551 --- /dev/null +++ b/src/lib/hw/lmk05318/lmk05318_rom.h @@ -0,0 +1,676 @@ +// 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 deleted file mode 100644 index bcf6c348..00000000 --- a/src/lib/hw/lmk1d1208i/lmk1d1208i.c +++ /dev/null @@ -1,134 +0,0 @@ -// 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 deleted file mode 100644 index de4e3a45..00000000 --- a/src/lib/hw/lmk1d1208i/lmk1d1208i.h +++ /dev/null @@ -1,62 +0,0 @@ -// 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 deleted file mode 100644 index fc654f4e..00000000 --- a/src/lib/hw/lmk1d1208i/lmk1d1208i.yaml +++ /dev/null @@ -1,228 +0,0 @@ -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/lmx1204/lmx1204.c b/src/lib/hw/lmx1204/lmx1204.c deleted file mode 100644 index 12c1e26d..00000000 --- a/src/lib/hw/lmx1204/lmx1204.c +++ /dev/null @@ -1,846 +0,0 @@ -// 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(status->lock_detect_status), - status->vco_sel, lmx1204_decode_vco_core(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 overriden 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(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(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(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 deleted file mode 100644 index b9b1c654..00000000 --- a/src/lib/hw/lmx1204/lmx1204.h +++ /dev/null @@ -1,118 +0,0 @@ -// 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 deleted file mode 100644 index ca14a3f7..00000000 --- a/src/lib/hw/lmx1204/lmx1204.yaml +++ /dev/null @@ -1,979 +0,0 @@ -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 deleted file mode 100644 index 0984c73a..00000000 --- a/src/lib/hw/lmx1205/lmx1205.c +++ /dev/null @@ -1,6 +0,0 @@ -// 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 deleted file mode 100644 index bc15ceb9..00000000 --- a/src/lib/hw/lmx1205/lmx1205.h +++ /dev/null @@ -1,7 +0,0 @@ -// 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 deleted file mode 100644 index 06ce0ad8..00000000 --- a/src/lib/hw/lmx1205/lmx1205.yaml +++ /dev/null @@ -1,1175 +0,0 @@ -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 deleted file mode 100644 index 5dea8159..00000000 --- a/src/lib/hw/lmx1214/lmx1214.c +++ /dev/null @@ -1,506 +0,0 @@ -// 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 deleted file mode 100644 index b4aeba6a..00000000 --- a/src/lib/hw/lmx1214/lmx1214.h +++ /dev/null @@ -1,64 +0,0 @@ -// 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 deleted file mode 100644 index ad940865..00000000 --- a/src/lib/hw/lmx1214/lmx1214.yaml +++ /dev/null @@ -1,521 +0,0 @@ -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 deleted file mode 100644 index c05c2cb1..00000000 --- a/src/lib/hw/lmx2820/lmx2820.c +++ /dev/null @@ -1,1249 +0,0 @@ -// 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 deleted file mode 100644 index aa1345ac..00000000 --- a/src/lib/hw/lmx2820/lmx2820.h +++ /dev/null @@ -1,95 +0,0 @@ -#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 deleted file mode 100644 index 8e4934a6..00000000 --- a/src/lib/hw/lmx2820/lmx2820.yaml +++ /dev/null @@ -1,1719 +0,0 @@ -# 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 deleted file mode 100644 index 986df9de..00000000 --- a/src/lib/hw/lp87524j/lp87524j.c +++ /dev/null @@ -1,6 +0,0 @@ -// 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 deleted file mode 100644 index 4ba9b4b6..00000000 --- a/src/lib/hw/lp87524j/lp87524j.h +++ /dev/null @@ -1,7 +0,0 @@ -// 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 deleted file mode 100644 index 6f15c94e..00000000 --- a/src/lib/hw/lp87524j/lp87524j.yaml +++ /dev/null @@ -1,71 +0,0 @@ -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/tca9555/tca9555.c b/src/lib/hw/tca9555/tca9555.c deleted file mode 100644 index 6146fd4e..00000000 --- a/src/lib/hw/tca9555/tca9555.c +++ /dev/null @@ -1,46 +0,0 @@ -// 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 deleted file mode 100644 index 6528bef6..00000000 --- a/src/lib/hw/tca9555/tca9555.h +++ /dev/null @@ -1,27 +0,0 @@ -// 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 deleted file mode 100644 index 74f3cf67..00000000 --- a/src/lib/hw/tca9555/tca9555.yaml +++ /dev/null @@ -1,87 +0,0 @@ -# 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/lowlevel/usb_uram/usb_uram_generic.c b/src/lib/lowlevel/usb_uram/usb_uram_generic.c index 493d1ede..870d0dc6 100644 --- a/src/lib/lowlevel/usb_uram/usb_uram_generic.c +++ b/src/lib/lowlevel/usb_uram/usb_uram_generic.c @@ -5,7 +5,6 @@ #include #include #include "../../device/generic_usdr/generic_regs.h" -#include usb_uram_generic_t* get_uram_generic(lldev_t dev); @@ -262,19 +261,18 @@ 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; - const char* busname; - + char busname[4]; switch(lsop) { case USDR_LSOP_SPI: int_number = gen->spi_int_number[ls_op_addr]; reg = gen->db.spi_core[ls_op_addr]; - busname = "SPI"; + strcpy(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]; - busname = "I2C"; + strcpy(busname, "I2C"); break; default: return -EOPNOTSUPP; diff --git a/src/lib/lowlevel/usb_uram/usb_uram_libusb.c b/src/lib/lowlevel/usb_uram/usb_uram_libusb.c index 9fbc730b..237de7a6 100644 --- a/src/lib/lowlevel/usb_uram/usb_uram_libusb.c +++ b/src/lib/lowlevel/usb_uram/usb_uram_libusb.c @@ -62,7 +62,7 @@ enum { IN_NTFY_SIZE = 64, //256, IN_RB_SIZE = 256, - MAX_NTFY_REQS = 32, + MAX_NTFY_REQS = 1, MAX_RB_REQS = 1, MAX_REQUEST_RB_SIZE = 256, @@ -83,7 +83,7 @@ enum { enum { STREAM_MAX_SLOTS = 64, - MAX_RB_THREADS = MAX_NTFY_REQS, + MAX_RB_THREADS = 8, }; struct stream_params { @@ -129,7 +129,6 @@ 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; @@ -191,7 +190,6 @@ 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); @@ -452,8 +450,8 @@ static int usb_read_bus(lldev_t dev, unsigned interrupt_number, UNUSED unsigned { int res; usb_dev_t* d = (usb_dev_t*)dev; - 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])); + + res = libusb_to_errno(libusb_submit_transfer(d->transfer_ntfy[0])); if (res) return res; @@ -463,6 +461,11 @@ 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; } diff --git a/src/lib/lowlevel/usdr_lowlevel.c b/src/lib/lowlevel/usdr_lowlevel.c index e1307d71..9af43a76 100644 --- a/src/lib/lowlevel/usdr_lowlevel.c +++ b/src/lib/lowlevel/usdr_lowlevel.c @@ -107,11 +107,6 @@ 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; -} - 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 89fa5e2f..daaf4235 100644 --- a/src/lib/lowlevel/usdr_lowlevel.h +++ b/src/lib/lowlevel/usdr_lowlevel.h @@ -226,6 +226,7 @@ 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 @@ -235,7 +236,5 @@ 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); #endif diff --git a/src/lib/xdsp/utests/README b/src/lib/xdsp/utests/README deleted file mode 100644 index 75b67bc7..00000000 --- a/src/lib/xdsp/utests/README +++ /dev/null @@ -1,23 +0,0 @@ -==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 16594123..5549cbc7 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(float) * 3 / 8) @@ -34,12 +34,10 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); in[0] = in_0; in[1] = in_1; @@ -69,7 +67,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_2cf32_ci12_c, &last_fn_name); + 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; } static int is_equal() @@ -166,9 +175,7 @@ START_TEST(conv_2cf32_ci12_check_simd) //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) @@ -226,12 +233,18 @@ END_TEST Suite * conv_2cf32_ci12_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_2cf32_ci12"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_2cf32_ci12_check_simd); - ADD_PERF_LOOP_TEST(s, conv_2cf32_ci12_speed, 60, 0, 3); + 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_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 42332c8b..65b90ded 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,12 +34,10 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); in[0] = in_0; in[1] = in_1; @@ -67,7 +65,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_2cf32_ci16_c, &last_fn_name); + 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; } static int is_equal() @@ -173,12 +182,18 @@ END_TEST Suite * conv_2cf32_ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_2cf32_ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_2cf32_ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_2cf32_ci16_speed, 60, 0, 3); + 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_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 b338195a..ac2bc13f 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(int16_t) * 3 / 4) @@ -31,12 +31,10 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); in[0] = in_0; in[1] = in_1; @@ -66,7 +64,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_2ci16_ci12_c, &last_fn_name); + 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; } static void printer(const char* header) @@ -113,9 +122,7 @@ START_TEST(conv_2ci16_ci12_check_simd) //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) @@ -172,12 +179,18 @@ END_TEST Suite * conv_2ci16_ci12_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_2ci16_ci12"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_2ci16_ci12_check_simd); - ADD_PERF_LOOP_TEST(s, conv_2ci16_ci12_speed, 60, 0, 3); + 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_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 9eb478d6..aca7ce24 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,12 +31,10 @@ 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 / 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); + 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); in[0] = in_0; in[1] = in_1; @@ -65,7 +63,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_2ci16_ci16_c, &last_fn_name); + 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; } @@ -151,12 +160,18 @@ END_TEST Suite * conv_2ci16_ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_2ci16_ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_2ci16_ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_2ci16_ci16_speed, 60, 0, 3); + 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_add_tcase(s, tc_core); 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 345298ba..1d9085fc 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(float) * 3 / 8) @@ -36,14 +36,12 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); in[0] = in_0; in[1] = in_1; @@ -79,7 +77,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_4cf32_ci12_c, &last_fn_name); + 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; } static int is_equal() @@ -176,9 +185,7 @@ START_TEST(conv_4cf32_ci12_check_simd) //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) @@ -190,7 +197,8 @@ START_TEST(conv_4cf32_ci12_check_simd) (*fn)(pin, bzin, &pout, bzout); #ifdef DEBUG_PRINT printer(NULL); -#endif +#endif \ + //int res = memcmp(out, out_etalon, bzout); int res = is_equal(); res ? fprintf(stderr,"\tFAILED!\n") : fprintf(stderr,"\tOK!\n"); ck_assert_int_eq( res, 0 ); @@ -235,12 +243,18 @@ END_TEST Suite * conv_4cf32_ci12_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_4cf32_ci12"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_4cf32_ci12_check_simd); - ADD_PERF_LOOP_TEST(s, conv_4cf32_ci12_speed, 60, 0, 3); + 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_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 9de597d8..a8358eda 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" -#undef DEBUG_PRINT +#define DEBUG_PRINT #define WORD_COUNT (8192u) #define OUT_BZ (WORD_COUNT * sizeof(int16_t)) @@ -36,14 +36,12 @@ 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) / 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); + 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); in[0] = in_0; in[1] = in_1; @@ -83,7 +81,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_4cf32_ci16_c, &last_fn_name); + 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; } static int is_equal() @@ -205,12 +214,18 @@ END_TEST Suite * conv_4cf32_ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_4cf32_ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_4cf32_ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_4cf32_ci16_speed, 60, 0, 3); + 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_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 821a9504..32fe7cc2 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(int16_t) * 3 / 4) @@ -33,14 +33,12 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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) / 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); + 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); in[0] = in_0; in[1] = in_1; @@ -76,7 +74,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_4ci16_ci12_c, &last_fn_name); + 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; } @@ -124,9 +133,7 @@ START_TEST(conv_4ci16_ci12_check_simd) //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) @@ -183,12 +190,18 @@ END_TEST Suite * conv_4ci16_ci12_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_4ci16_ci12"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_4ci16_ci12_check_simd); - ADD_PERF_LOOP_TEST(s, conv_4ci16_ci12_speed, 60, 0, 3); + 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_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 fabd79ab..f6778887 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,14 +33,12 @@ 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 / 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); + 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); in[0] = in_0; in[1] = in_1; @@ -81,7 +79,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_4ci16_ci16_c, &last_fn_name); + 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; } static void print_data(const char* header) @@ -123,7 +132,7 @@ START_TEST(conv_4ci16_ci16_check_simd) (*get_fn(OPT_GENERIC, 0))(pin, bzin, &pout, bzout); memcpy(out_etalon, out, bzout); -#ifdef DEBUG_PRINT +#if 0 print_data("ETALON DATA"); #endif @@ -134,7 +143,7 @@ START_TEST(conv_4ci16_ci16_check_simd) { memset(out, 0, bzout); (*fn)(pin, bzin, &pout, bzout); -#ifdef DEBUG_PRINT +#if 0 print_data(NULL); #endif int res = memcmp(out, out_etalon, bzout); @@ -181,12 +190,18 @@ END_TEST Suite * conv_4ci16_ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_4ci16_ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_4ci16_ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_4ci16_ci16_speed, 60, 0, 4); + 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_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 d75e37d6..55f1e78f 100644 --- a/src/lib/xdsp/utests/conv_ci12_2cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_2cf32_utest.c @@ -8,13 +8,16 @@ #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) @@ -34,13 +37,11 @@ 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/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); + 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); out[0] = out1; out[1] = out2; @@ -80,7 +81,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci12_2cf32_c, &last_fn_name); + 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; } #define CONV_SCALE (1.0f/32767) @@ -103,6 +115,13 @@ 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) @@ -203,13 +222,19 @@ END_TEST Suite * conv_ci12_2cf32_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci12_2cf32"); + max_opt = cpu_vcap_get(); - 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); + 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_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 b7862819..00ff2fd0 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define WORD_COUNT (20u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * 12u / 8u) @@ -34,13 +34,11 @@ 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/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); + 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); out[0] = out1; out[1] = out2; @@ -74,7 +72,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci12_2ci16_c, &last_fn_name); + 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; } static void printer(const char* header) @@ -121,9 +130,7 @@ START_TEST(conv_ci12_2ci16_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); @@ -182,12 +189,18 @@ END_TEST Suite * conv_ci12_2ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci12_2ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_ci12_2ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_ci12_2ci16_speed, 60, 0, 3); + 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_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 3cbb02cb..243e938f 100644 --- a/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_4cf32_utest.c @@ -10,11 +10,14 @@ #include "xdsp_utest_common.h" #include "conv_ci12_4cf32_2.h" -#undef DEBUG_PRINT +//#define 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) @@ -39,17 +42,15 @@ 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/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); + 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); out[0] = out1; out[1] = out2; @@ -95,7 +96,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci12_4cf32_c, &last_fn_name); + 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; } #define CONV_SCALE (1.0f/32767) @@ -212,13 +224,19 @@ END_TEST Suite * conv_ci12_4cf32_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci12_2cf32"); + max_opt = cpu_vcap_get(); - 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, 3); + 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_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 1ace2bb4..5ff17a26 100644 --- a/src/lib/xdsp/utests/conv_ci12_4ci16_utest.c +++ b/src/lib/xdsp/utests/conv_ci12_4ci16_utest.c @@ -10,11 +10,14 @@ #include "xdsp_utest_common.h" #include "conv_ci12_4ci16_2.h" -#undef DEBUG_PRINT +//#define 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) @@ -39,17 +42,15 @@ 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/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); + 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); out[0] = out1; out[1] = out2; @@ -89,7 +90,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci12_4ci16_c, &last_fn_name); + 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; } static void printer(const char* header) @@ -136,9 +148,7 @@ START_TEST(conv_ci12_4ci16_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 / 4); memcpy(out2_etalon, out[1], bzout / 4); memcpy(out3_etalon, out[2], bzout / 4); @@ -202,12 +212,18 @@ END_TEST Suite * conv_ci12_4ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci12_4ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_ci12_4ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_ci12_4ci16_speed, 60, 0, 3); + 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_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 20e53647..22fbbab4 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define WORD_COUNT (4096u + 77u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) @@ -34,13 +34,11 @@ 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/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); + 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); out[0] = out1; out[1] = out2; @@ -70,7 +68,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci16_2cf32_c, &last_fn_name); + 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; } #define CONV_SCALE (1.0f/32767) @@ -104,9 +113,7 @@ 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); @@ -165,12 +172,18 @@ END_TEST Suite * conv_ci16_2cf32_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci16_2cf32"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_ci16_2cf32_check_simd); - ADD_PERF_LOOP_TEST(s, conv_ci16_2cf32_speed, 60, 0, 3); + 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_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 a532489a..181fa8e7 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,13 +34,11 @@ 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/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); + 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); out[0] = out1; out[1] = out2; @@ -70,7 +68,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci16_2ci16_c, &last_fn_name); + 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; } #define CONV_SCALE (1.0f/32767) @@ -164,12 +173,18 @@ END_TEST Suite * conv_ci16_2ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci16_2ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_ci16_2ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_ci16_2ci16_speed, 60, 0, 3); + 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_add_tcase(s, tc_core); 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 3d9a264f..19fa718b 100644 --- a/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c +++ b/src/lib/xdsp/utests/conv_ci16_4cf32_utest.c @@ -10,9 +10,9 @@ #include "xdsp_utest_common.h" #include "conv_ci16_4cf32_2.h" -#undef DEBUG_PRINT +#define DEBUG_PRINT -#define WORD_COUNT (4096u + 80u) +#define WORD_COUNT (4096u + 77u) #define IN_STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) #define SPEED_WORD_COUNT (32768u) @@ -38,17 +38,15 @@ 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/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); + 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); out[0] = out1; out[1] = out2; @@ -87,7 +85,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci16_4cf32_c, &last_fn_name); + 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; } #define CONV_SCALE (1.0f/32767) @@ -186,12 +195,18 @@ END_TEST Suite * conv_ci16_4cf32_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci16_4cf32"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_ci16_4cf32_check_simd); - ADD_PERF_LOOP_TEST(s, conv_ci16_4cf32_speed, 60, 0, 3); + 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_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 0901e8d8..265dd560 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" -#undef DEBUG_PRINT +#define DEBUG_PRINT -#define CHECK_WORD_COUNT (4096u + 80u) //must be a multiple of 4 +#define CHECK_WORD_COUNT (4096u + 77u) #define CHECK_SIZE_BZ (CHECK_WORD_COUNT * sizeof(int16_t)) #define SPEED_WORD_COUNT (65536u) @@ -38,21 +38,17 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - int res = 0; + posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); - res = res ? res : posix_memalign((void**)&in, ALIGN_BYTES, SPEED_SIZE_BZ); + 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**)&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); + 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); out[0] = out1; out[1] = out2; @@ -65,6 +61,7 @@ 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; } } @@ -84,7 +81,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_ci16_4ci16_c, &last_fn_name); + 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; } @@ -125,9 +133,8 @@ 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); @@ -191,12 +198,18 @@ END_TEST Suite * conv_ci16_4ci16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_ci16_4ci16"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_ci16_4ci16_check_simd); - ADD_PERF_LOOP_TEST(s, conv_ci16_4ci16_speed, 60, 0, 4); + 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_add_tcase(s, tc_core); 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 98b6f0d1..b4f009e7 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define PACKET_SIZE (8192u) #define OUT_BZ (PACKET_SIZE * sizeof(float) * 3 / 8) @@ -31,11 +31,9 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); //fill srand( time(0) ); @@ -56,7 +54,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_f32_i12_c, &last_fn_name); + 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; } static int is_equal() @@ -150,9 +159,7 @@ START_TEST(conv_f32_i12_check_simd) //get etalon output data (generic foo) (*get_fn(OPT_GENERIC, 0))(&pin, bzin, &pout, bzout); -#ifdef DEBUG_PRINT printer("HEADER:"); -#endif memcpy(out_etalon, out, bzout); while(opt != OPT_GENERIC) @@ -166,6 +173,7 @@ 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 ); } @@ -209,12 +217,18 @@ END_TEST Suite * conv_f32_i12_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_f32_i12"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_f32_i12_check_simd); - ADD_PERF_LOOP_TEST(s, conv_f32_i12_speed, 60, 0, 3); + 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_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 072cc52b..9127f983 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" -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define STREAM_SIZE (8192 + 16 + 8 + 7) #define STREAM_SIZE_CHECK STREAM_SIZE @@ -32,11 +32,9 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); srand( time(0) ); @@ -76,7 +74,18 @@ static int is_equal() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_f32_i16_c, &last_fn_name); + 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; } static void printer(const char* header) @@ -104,9 +113,7 @@ 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) @@ -169,12 +176,17 @@ END_TEST Suite * conv_f32_i16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *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); + 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); 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 23ca462a..77a33b5b 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" -#undef DEBUG_PRINT +#define 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,11 +31,9 @@ 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**)&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); + 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); //fill @@ -70,7 +68,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_i12_f32_c, &last_fn_name); + 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; } #define CONV_SCALE (1.0f/32767) @@ -95,10 +104,9 @@ START_TEST(conv_i12_f32_check) v *= CONV_SCALE; v = (i % 4) ? v : -v; -#ifdef DEBUG_PRINT - int16_t i12 = (int16_t)(out[i] / CONV_SCALE) >> 4; + 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 @@ -192,13 +200,19 @@ END_TEST Suite * conv_i12_f32_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_i12_f32"); + max_opt = cpu_vcap_get(); - 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); + 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_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 53546797..f87a154d 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" -#undef DEBUG_PRINT +//#define 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,11 +31,9 @@ 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**)&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); + 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); //fill @@ -64,7 +62,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_i12_i16_c, &last_fn_name); + 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; } START_TEST(conv_i12_i16_check) @@ -185,13 +194,19 @@ END_TEST Suite * conv_i12_i16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_i12_i16"); + max_opt = cpu_vcap_get(); - 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); + 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_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 adc9d556..6c54685b 100644 --- a/src/lib/xdsp/utests/conv_i16_f32_utest.c +++ b/src/lib/xdsp/utests/conv_i16_f32_utest.c @@ -29,11 +29,9 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); srand( time(0) ); @@ -53,7 +51,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_i16_f32_c, &last_fn_name); + 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; } START_TEST(conv_i16_f32_check) @@ -130,12 +139,17 @@ END_TEST Suite * conv_i16_f32_suite(void) { - max_opt = cpu_vcap_get(); - - Suite* s = suite_create("conv_i16_f32"); + Suite *s; + TCase *tc_core; - ADD_REGRESS_TEST(s, conv_i16_f32_check); - ADD_PERF_LOOP_TEST(s, conv_i16_f32_speed, 60, 0, 3); + 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); 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 0c040460..bf103bbe 100644 --- a/src/lib/xdsp/utests/conv_i16_i12_utest.c +++ b/src/lib/xdsp/utests/conv_i16_i12_utest.c @@ -10,11 +10,12 @@ #include "xdsp_utest_common.h" #include "conv_i16_i12_2.h" -#undef DEBUG_PRINT +//#define 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 @@ -28,11 +29,9 @@ static generic_opts_t max_opt = OPT_GENERIC; static void setup() { - 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); + 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); //fill for(int i = 0; i < PACKET_SIZE; ++i) @@ -50,7 +49,18 @@ static void teardown() static conv_function_t get_fn(generic_opts_t o, int log) { - return generic_get_fn(o, log, conv_get_i16_i12_c, &last_fn_name); + 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; } static void printer(const char* header) @@ -148,12 +158,18 @@ END_TEST Suite * conv_i16_i12_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("conv_i16_i12"); + max_opt = cpu_vcap_get(); - ADD_REGRESS_TEST(s, conv_i16_i12_check_simd); - ADD_PERF_LOOP_TEST(s, conv_i16_i12_speed, 60, 0, 3); + 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_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 2125c9ad..3c137c66 100644 --- a/src/lib/xdsp/utests/fft_window_cf32_utest.c +++ b/src/lib/xdsp/utests/fft_window_cf32_utest.c @@ -6,7 +6,7 @@ #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 }; @@ -30,15 +30,31 @@ static void recalcWnd(unsigned fft_size) 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() { - 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) * 2 * FFT_SIZE); - ck_assert_int_eq(res, 0); + 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); for(unsigned i = 0; i < FFT_SIZE; ++i) { @@ -149,12 +165,17 @@ END_TEST Suite * fft_window_cf32_suite(void) { - max_opt = cpu_vcap_get(); - - Suite* s = suite_create("fft_window_cf32_functions"); + Suite *s; + TCase *tc_core; - ADD_REGRESS_TEST(s, wnd_check); - ADD_PERF_LOOP_TEST(s, wnd_speed, 300, 0, 3); + 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); 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 a9c83ce4..d962e9a9 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 -#undef DEBUG_PRINT +//#define DEBUG_PRINT #define WORD_COUNT (65536) #define STREAM_SIZE_BZ (WORD_COUNT * sizeof(int16_t)) @@ -43,17 +43,15 @@ static generic_opts_t max_opt = OPT_GENERIC; 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); + 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); + + 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)); srand( time(0) ); @@ -101,7 +99,18 @@ 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); + const char* fn_name = NULL; + conv_function_t fn = get_wvlt_sincos_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; } static sincos_i16_interleaved_ctrl_function_t get_fn_interleaved(generic_opts_t o, int log) @@ -332,14 +341,20 @@ END_TEST Suite * wvlt_sincos_i16_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - Suite* s = suite_create("wvlt_sincos_i16"); + max_opt = cpu_vcap_get(); - 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); + 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_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 22fd488f..09cb0de5 100644 --- a/src/lib/xdsp/utests/xdsp_utest_common.h +++ b/src/lib/xdsp/utests/xdsp_utest_common.h @@ -4,62 +4,17 @@ #ifndef XDSP_UTEST_COMMON_H #define XDSP_UTEST_COMMON_H +#include #include #include -#include -#include - -#include "../../cal/opt_func.h" -#include "conv.h" - #define ALIGN_BYTES (size_t)64 -#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) +static inline uint64_t clock_get_time() { - 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; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec/1000LL; } #endif // XDSP_UTEST_COMMON_H diff --git a/src/lib/xdsp/utests/xfft_fftad_utest.c b/src/lib/xdsp/utests/xfft_fftad_utest.c index c675e4bc..502893df 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 @@ -35,14 +35,11 @@ static void setup(void) { srand( time(0) ); - 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); + 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); //init input data for(unsigned i = 0; i < STREAM_SIZE; ++i) @@ -201,12 +198,17 @@ END_TEST Suite * fftad_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *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); + 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); return s; } diff --git a/src/lib/xdsp/utests/xfft_rtsa_utest.c b/src/lib/xdsp/utests/xfft_rtsa_utest.c index 1bb67831..e892c1f6 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,8 +27,7 @@ static const unsigned packet_lens[4] = { 512, 1024, 2048, STREAM_SIZE }; #define SPEED_MEASURE_ITERS 256 -#define EPSILON MAX_RTSA_PWR / 10000 -#define MAX_ERRS 10 +#define EPSILON MAX_RTSA_PWR / 10 static const char* last_fn_name = NULL; static generic_opts_t max_opt = OPT_GENERIC; @@ -53,7 +52,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); - ck_assert_int_eq(res, 0); + assert(res == 0); //init input data srand( time(0) ); @@ -89,7 +88,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); - ck_assert_int_eq(res, 0); + assert(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); @@ -108,13 +107,9 @@ static void teardown(void) static int32_t is_equal() { - int errs = 0; - for(unsigned i = 0; i < STREAM_SIZE * rtsa_settings.rtsa_depth; i++) { - errs += (abs(out[i] - out_etalon[i]) > EPSILON); - if(errs > MAX_ERRS) - return i; + if(abs(out[i] - out_etalon[i]) > EPSILON) return i; } return -1; } @@ -173,6 +168,8 @@ 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 ); @@ -308,15 +305,24 @@ START_TEST(rtsa_speed_u16) END_TEST + + Suite * rtsa_suite(void) { - max_opt = cpu_vcap_get(); + Suite *s; + TCase *tc_core; - 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); + 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; } + + diff --git a/src/utests/CMakeLists.txt b/src/utests/CMakeLists.txt index bb2ddc3e..5468ec63 100644 --- a/src/utests/CMakeLists.txt +++ b/src/utests/CMakeLists.txt @@ -3,7 +3,6 @@ include_directories(../lib/port) include_directories(../lib/lowlevel) -include_directories(../lib/hw) add_library(mock_lowlevel STATIC mock_lowlevel.c) @@ -12,10 +11,6 @@ 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 deleted file mode 100644 index 8ad4b8d9..00000000 --- a/src/utests/lmk05318_solver_test.c +++ /dev/null @@ -1,445 +0,0 @@ -#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 ); -} - -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 ); -} - -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 ); -} - -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 ); - -} - -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 ); - -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - - -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 ); -} - -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 );} - -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); - - suite_add_tcase(s, tc_core); - return s; -} diff --git a/src/utests/lmx1204_solver_test.c b/src/utests/lmx1204_solver_test.c deleted file mode 100644 index 7bb4b6fc..00000000 --- a/src/utests/lmx1204_solver_test.c +++ /dev/null @@ -1,138 +0,0 @@ -#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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 deleted file mode 100644 index 0f65f122..00000000 --- a/src/utests/lmx1214_solver_test.c +++ /dev/null @@ -1,140 +0,0 @@ -#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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 deleted file mode 100644 index f3655069..00000000 --- a/src/utests/lmx2820_solver_test.c +++ /dev/null @@ -1,204 +0,0 @@ -#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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - -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 ); -} - - -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 eb1f4209..043afe29 100644 --- a/src/utests/test_suite.c +++ b/src/utests/test_suite.c @@ -11,10 +11,6 @@ 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) { @@ -32,12 +28,6 @@ int main(int argc, char** argv) 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()); - - srunner_set_fork_status (sr, CK_NOFORK); srunner_run_all(sr, (argc > 1) ? CK_VERBOSE : CK_NORMAL); number_failed = srunner_ntests_failed(sr); From 588838532ec3a022ae8446021ac99fa07bd0240f Mon Sep 17 00:00:00 2001 From: vd <33198864+vd2org@users.noreply.github.com> Date: Thu, 26 Jun 2025 18:59:57 +0400 Subject: [PATCH 3/4] Switching to 0.9.10b. --- packaging/debian-bookworm/changelog | 4 ++-- packaging/debian-bookworm/control | 2 +- packaging/ubuntu-bionic/changelog | 4 ++-- packaging/ubuntu-bionic/control | 2 +- packaging/ubuntu-focal/changelog | 4 ++-- packaging/ubuntu-focal/control | 2 +- packaging/ubuntu-jammy/changelog | 4 ++-- packaging/ubuntu-jammy/control | 2 +- packaging/ubuntu-noble/changelog | 4 ++-- packaging/ubuntu-noble/control | 2 +- src/Changelog.txt | 5 +++++ 11 files changed, 20 insertions(+), 15 deletions(-) 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/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) ========================== From 03c73b3a7db9faac30682e6195dfa754f73c5dc3 Mon Sep 17 00:00:00 2001 From: Ivan <33198864+vd2org@users.noreply.github.com> Date: Fri, 19 Sep 2025 18:58:06 +0400 Subject: [PATCH 4/4] Improved error message for missing SoapySDR package in CMake --- src/soapysdr/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/soapysdr/CMakeLists.txt b/src/soapysdr/CMakeLists.txt index 2d6923ae..68b3da25 100644 --- a/src/soapysdr/CMakeLists.txt +++ b/src/soapysdr/CMakeLists.txt @@ -5,7 +5,13 @@ cmake_minimum_required(VERSION 3.8) set(CMAKE_CXX_STANDARD 11) project(usdr-soapy C CXX) -find_package(SoapySDR REQUIRED) +find_package(SoapySDR) +if(NOT SoapySDR_FOUND) + message(FATAL_ERROR +"SoapySDR package not found! +Use the following command to install dependencies on Ubuntu: + sudo apt install libsoapysdr-dev") +endif() include_directories(../lib/port) include_directories(../lib/models)