# Copyright (c) 2018, Xilinx, Inc.
# SPDX-License-Identifier: BSD-3-Clause
import cffi
import math
import numpy as np
from .constants import *
_ffi = cffi.FFI()
[docs]class DP159:
"""Class to configure the TI SNDP159 HDMI redriver/retimer
"""
def __init__(self, master, address):
"""Construct a new driver
Parameters
----------
master : IIC master
I2C master that the device is connected to
address : int
I2C address of device
"""
self._master = master
self._address = address
self._buffer = _ffi.new("unsigned char [32]")
def _read(self, reg_addr):
self._buffer[0] = reg_addr
self._master.send(self._address, self._buffer, 1, 1)
self._master.receive(self._address, self._buffer, 1)
self._master.wait()
# Clear all of the interrupts
self._master.write(0x20, self._master.read(0x20))
return self._buffer[0]
def _write(self, reg_addr, data):
self._buffer[0] = reg_addr
self._buffer[1] = data
self._master.send(self._address, self._buffer, 2)
self._master.wait()
# Clear all of the interrupts
self._master.write(0x20, self._master.read(0x20))
[docs] def set_clock(self, refclk, line_rate):
"""Configure the device based on the line rate
"""
is20 = (line_rate // 1000000) > 3400
# These parameters are derived from the Xilinx ZCU104 reference
self._write(0x09, 0x06)
if is20:
self._write(0x0B, 0x9A)
self._write(0x0C, 0x49)
self._write(0x0D, 0x00)
self._write(0x0A, 0x36)
else:
self._write(0x0B, 0x80)
self._write(0x0C, 0x48)
self._write(0x0D, 0x00)
self._write(0x0A, 0x35)
def _get_int_div_table(fout, bypass):
if bypass:
NS1_Options = [1, 4, 5, 6]
else:
NS1_Options = [4, 5, 6]
table = []
OutDivMin = math.ceil(IDT_8T49N24X_FVCO_MIN / fout)
OutDivMax = math.floor(IDT_8T49N24X_FVCO_MAX / fout)
if OutDivMax in NS1_Options or OutDivMin in NS1_Options:
# Bypass NS2
NS2Min = 0
NS2Max = 0
else:
NS2Min = math.ceil(OutDivMin / NS1_Options[-1] / 2)
NS2Max = math.floor(OutDivMax / NS1_Options[0] / 2)
if NS2Max == 0:
NS2Max = 1
NS2Temp = NS2Min
while NS2Temp <= NS2Max:
for ns1 in NS1_Options:
if NS2Temp == 0:
OutDivTemp = ns1
else:
OutDivTemp = ns1 * NS2Temp * 2
VCOTemp = fout * OutDivTemp
if IDT_8T49N24X_FVCO_MIN <= VCOTemp <= IDT_8T49N24X_FVCO_MAX:
table.append((OutDivTemp, ns1))
NS2Temp += 1
return table
NS1Lookup = {4: 2, 5: 0, 6: 1}
def _calculate_settings(fin, fout):
settings = {}
divide = max(_get_int_div_table(fout, False))
fvco = fout * divide[0]
settings['NS1Ratio'] = divide[1]
settings['NS1_Reg'] = NS1Lookup[settings['NS1Ratio']]
settings['NS2Ratio'] = divide[0] // divide[1]
settings['NS2_Reg'] = settings['NS2Ratio'] // 2
# Assume always integer division
settings['NInt'] = divide[0] // 2
settings['NFrac'] = 0
# Calculate the divider from the reference crystal
fbdiv = fvco / (2 * IDT_8T49N24X_XTAL_FREQ)
settings['DSMInt'] = math.floor(fbdiv)
settings['DSMFrac'] = round((fbdiv - settings['DSMInt']) * pow(2, 21))
# Calculate settings for the phase detector
fin_ratio = fvco / fin
PMin = fin // IDT_8T49N24X_FPD_MAX
min_error = 99999999
M1_best = 0
P_best = 0
for i in range(PMin, IDT_8T49N24X_P_MAX):
M1 = round(i * fin_ratio)
if M1 < IDT_8T49N24X_M_MAX:
error = abs(fin_ratio - (M1 / i))
if error < min_error:
M1_best = M1
P_best = i
min_error = error
if error < 1e-9:
break
else:
break
settings['M1'] = M1_best
settings['Pre'] = P_best
LOS = (fvco // 8 // fin) + 3
if LOS < 6:
LOS = 6
settings['LOS'] = LOS
return settings
[docs]class IDT_8T49N24:
"""Driver for the IDT 8T49N24x series of clock generators
"""
def __init__(self, master, address):
"""Create a new instance of the IDT driver
Parameters
----------
master : IIC master
IIC master the device is connected to
address : int
IIC address of the device
"""
self._master = master
self._address = address
self._buffer = _ffi.new("unsigned char [32]")
if not self.check_device_id():
raise RuntimeError("Could not find IDT8TN24x")
self.enable(False)
self._configure(IDT_Synth)
self.enable(True)
def _configure(self, values):
for i, v in enumerate(values):
if i != 0x70: # Skip Calibration
self._write(i, v)
def _read(self, reg_addr):
attempts = 0
while True:
try:
self._buffer[0] = reg_addr >> 8
self._buffer[1] = reg_addr & 0xFF
self._master.send(self._address, self._buffer, 2, 1)
self._master.receive(self._address, self._buffer, 1, 0)
except:
attempts += 1
if attempts > 100:
raise
continue
break
return self._buffer[0]
def _write(self, reg_addr, value):
attempts = 0
while True:
try:
self._buffer[0] = reg_addr >> 8
self._buffer[1] = reg_addr & 0xFF
self._buffer[2] = value
self._master.send(self._address, self._buffer, 3, 0)
except:
attempts += 1
if attempts > 100:
raise
continue
break
def _update(self, reg_addr, value, mask):
data = self._read(reg_addr)
data &= ~mask
data |= (value & mask)
self._write(reg_addr, data)
[docs] def check_device_id(self):
device_id = (self._read(0x0002) & 0xF) << 12
device_id |= self._read(0x0003) << 4
device_id |= self._read(0x0004) >> 4
return device_id == 0x0606 or device_id == 65535
[docs] def enable(self, active):
if active:
value = 0x00
else:
value = 0x05
self._update(0x0070, value, 0x05)
[docs] def set_clock(self, freq, line_rate):
"""Configure the device based on the line rate
The parameter `line_rate` is left to keep consistent API with
other clock drivers.
"""
self._set_clock(IDT_8T49N24X_XTAL_FREQ, freq, True)
def _set_clock(self, fin, fout, free_run):
if fin < IDT_8T49N24X_FIN_MIN:
raise RuntimeError("Input Frequency Below Minimum")
if fin > IDT_8T49N24X_FIN_MAX:
raise RuntimeError("Input Frequency Above Maximum")
if fout < IDT_8T49N24X_FOUT_MIN:
raise RuntimeError("Output Frequency Below Minimum")
if fout > IDT_8T49N24X_FOUT_MAX:
raise RuntimeError("Output Frequency Above Maximum")
settings = _calculate_settings(fin, fout)
self.enable(False)
if free_run:
self._reference_input(0, False)
self._reference_input(1, False)
self._mode(True)
else:
self._reference_input(0, True)
self._reference_input(1, False)
self._mode(False)
# Set up input clock
self._pre_divider(0, settings['Pre'])
self._pre_divider(1, settings['Pre'])
self._m1_feedback(0, settings['M1'])
self._m1_feedback(1, settings['M1'])
self._los(0, settings['LOS'])
self._los(1, settings['LOS'])
# FVCO configuration
self._dsm_int(settings['DSMInt'])
self._dsm_frac(settings['DSMFrac'])
# Output clock
self._output_divider(2, settings['NInt'])
self._output_divider(3, settings['NInt'])
self._output_divider_frac(2, settings['NFrac'])
self._output_divider_frac(3, settings['NFrac'])
self.enable(True)
def _reference_input(self, channel, enable):
if channel == 1:
shift = 5
else:
shift = 4
if enable:
value = 0
else:
value = 1 << shift
mask = 1 << shift
self._update(0x000a, value, mask)
def _mode(self, free_run):
if free_run:
self._update(0x000a, 0x31, 0x33)
self._update(0x0069, 0x08, 0x08)
else:
self._update(0x000a, 0x20, 0x33)
self._update(0x0069, 0x00, 0x08)
def _pre_divider(self, channel, value):
if channel == 1:
address = 0x000e
else:
address = 0x000b
self._write(address, (value >> 16) & 0x1F)
self._write(address + 1, (value >> 8) & 0xFF)
self._write(address + 2, value & 0xFF)
def _m1_feedback(self, channel, value):
if channel == 1:
address = 0x0011
else:
address = 0x0014
self._write(address, value >> 16)
self._write(address + 1, (value >> 8) & 0xFF)
self._write(address + 2, value & 0xFF)
def _los(self, channel, value):
if channel == 1:
address = 0x0074
else:
address = 0x0071
self._write(address, value >> 16)
self._write(address + 1, (value >> 8) & 0xFF)
self._write(address + 2, value & 0xFF)
def _dsm_int(self, value):
self._write(0x25, (value >> 8) & 0x01)
self._write(0x26, value & 0xFF)
def _dsm_frac(self, value):
self._write(0x28, (value >> 16) & 0x1F)
self._write(0x29, (value >> 8) & 0xFF)
self._write(0x2a, value & 0xFF)
def _output_divider(self, channel, value):
addresses = [0x003f, 0x0042, 0x0045, 0x0048]
address = addresses[channel]
self._write(address, (value >> 16) & 0x3)
self._write(address + 1, (value >> 8) & 0xFF)
self._write(address + 2, value & 0xFF)
def _output_divider_frac(self, channel, value):
addresses = [0x0000, 0x0057, 0x005b, 0x005f]
address = addresses[channel]
self._write(address, (value >> 24) & 0x0F)
self._write(address + 1, (value >> 16) & 0xFF)
self._write(address + 2, (value >> 8) & 0xFF)
self._write(address + 3, value & 0xFF)
[docs]class SI_5324C:
"""Driver for the SI 5324C series of clock generators
"""
def __init__(self, master, address):
"""Create a new instance of the SI_5324C driver
Parameters
----------
master : IIC master
IIC master the device is connected to
address : int
IIC address of the device
"""
self._master = master
self._address = address
self._buffer = _ffi.new("unsigned char [32]")
if not self.check_device_id():
raise RuntimeError("Could not find SI5324")
self.vals = [0 for _ in range(6)]
self.n1_min = 1
self.n1_max = 1
self.n1_hs = 1
self.nc_ls_min = 1
self.nc_ls_max = 1
self.nc_ls = 1
self.n2_hs = 1
self.n2_ls_min = 1
self.n2_ls_max = 1
self.n2_ls = 1
self.n3_min = 1
self.n3_max = 1
self.n3 = 1
self.best_n1_hs = 1
self.best_nc_ls = 1
self.best_n2_hs = 1
self.best_n2_ls = 1
self.best_n3 = 1
self.fin = 1
self.fout = 1
self.fosc = 1
self.best_fout_delta = 1
self.best_fout = 1
self.enable(False)
self._configure()
self.enable(True)
def _configure(self):
self._write(3, 0x15)
self._write(4, 0x92)
self._write(6, 0x2f)
self._write(10, 0x08)
self._write(11, 0x42)
self._write(19, 0x23)
self._write(137, 0x01)
def _read(self, reg_addr):
attempts = 0
while True:
try:
self._buffer[0] = reg_addr
self._master.send(self._address, self._buffer, 1, 1)
self._master.receive(self._address, self._buffer, 1, 0)
except Exception:
attempts += 1
if attempts > 100:
raise RuntimeError(
"Timeout when reading from address {}".format(reg_addr))
continue
break
return self._buffer[0]
def _write(self, reg_addr, value):
attempts = 0
while True:
try:
self._buffer[0] = reg_addr
self._buffer[1] = value
self._master.send(self._address, self._buffer, 2, 0)
except Exception:
attempts += 1
if attempts > 100:
raise RuntimeError(
"Timeout when writing to address {}".format(reg_addr))
continue
break
def _update(self, reg_addr, value, mask):
data = self._read(reg_addr)
data &= ~mask
data |= (value & mask)
self._write(reg_addr, data)
[docs] def check_device_id(self):
device_id = self._read(0x86) << 8
device_id |= self._read(0x87)
return device_id == 0x0182
[docs] def enable(self, active):
if active:
value = 0x00
else:
value = 0x01
self._update(0x0B, value, 0x01)
[docs] def set_clock(self, freq, line_rate):
self.enable(False)
self._set_clock(SI5324_CLKSRC_XTAL, SI5324_XTAL_FREQ, freq)
self.enable(True)
def _rate_approx(self, f):
h = np.array([0, 1, 0])
k = np.array([1, 0, 0])
n = 1
if self.n3_max <= 1:
self.n3 = 1
self.n2_ls = f >> 28
return
n = n << 28
for i in range(0, 28):
if (f % 2) == 0:
n = n//2
f = f//2
else:
break
d = f
for i in range(64):
if n:
a = d//n
else:
a = 0
if i and not a:
break
x = d
d = n
n = x % n
x = a
if k[1]*a+k[0] >= self.n3_max:
x = (self.n3_max-k[0])//k[1]
if not (x*2 >= a or k[1] >= self.n3_max):
break
h[2] = x*h[1]+h[0]
h[0] = h[1]
h[1] = h[2]
k[2] = x*k[1]+k[0]
k[0] = k[1]
k[1] = k[2]
self.n3 = k[1]
self.n2_ls = h[1]
def _find_n2ls(self):
result = 0
np.seterr(divide='ignore', invalid='ignore')
n2_ls_div_n3 = self.fosc//(self.fin >> 28)//self.n2_hs//2
self._rate_approx(n2_ls_div_n3)
self.n2_ls = self.n2_ls*2
if self.n2_ls < self.n2_ls_min:
mult = self.n2_ls_min % self.n2_ls
if mult == 1:
mult = mult+1
self.n2_ls = self.n2_ls*mult
self.n3 = self.n3*mult
if self.n3 < self.n3_min:
mult = self.n3_min % self.n3
if mult == 1:
mult = mult+1
self.n2_ls = self.n2_ls*mult
self.n3 = self.n3*mult
else:
f3_actual = self.fin//self.n3
fosc_actual = f3_actual * self.n2_hs * self.n2_ls
fout_actual = fosc_actual//(self.n1_hs * self.nc_ls)
delta_fout = fout_actual - self.fout
if f3_actual < (SI5324_F3_MIN << 28) or \
f3_actual > (SI5324_F3_MAX << 28):
pass
elif fosc_actual < (SI5324_FOSC_MIN << 28) or \
fosc_actual > (SI5324_FOSC_MAX << 28):
pass
elif fout_actual < (SI5324_FOUT_MIN << 28) or \
fout_actual > (SI5324_FOUT_MAX << 28):
pass
else:
if abs(delta_fout) < self.best_fout_delta:
self.best_n1_hs = self.n1_hs
self.best_nc_ls = self.nc_ls
self.best_n2_hs = self.n2_hs
self.best_n2_ls = self.n2_ls
self.best_n3 = self.n3
self.best_fout = fout_actual
self.best_fout_delta = abs(delta_fout)
if delta_fout == 0:
result = 1
return result
def _find_n2(self):
result = 0
for i in range(SI5324_N2_HS_MAX, SI5324_N2_HS_MIN-1, -1):
self.n2_hs = i
self.n2_ls_min = self.fosc//((SI5324_F3_MAX * i) << 28)
if self.n2_ls_min < SI5324_N2_LS_MIN:
self.n2_ls_min = SI5324_N2_LS_MIN
self.n2_ls_max = self.fosc//((SI5324_F3_MIN * i) << 28)
if self.n2_ls_max > SI5324_N2_LS_MAX:
self.n2_ls_max = SI5324_N2_LS_MAX
result = self._find_n2ls()
if result:
break
return result
def _calc_ncls_limits(self):
self.nc_ls_min = self.n1_min//self.n1_hs
if self.nc_ls_min < SI5324_NC_LS_MIN:
self.nc_ls_min = SI5324_NC_LS_MIN
if self.nc_ls_min > 1 and self.nc_ls_min & 0x1 == 1:
self.nc_ls_min = self.nc_ls_min+1
self.nc_ls_max = self.n1_max//self.n1_hs
if self.nc_ls_max > SI5324_NC_LS_MAX:
self.nc_ls_max = SI5324_NC_LS_MAX
if self.nc_ls_max & 0x1 == 1:
self.nc_ls_max = self.nc_ls_max-1
if self.nc_ls_max * self.n1_hs < self.n1_min or \
self.nc_ls_min * self.n1_hs > self.n1_max:
return -1
return 0
def _find_ncls(self):
fosc_1 = self.fout * self.n1_hs
result = 0
for i in range(self.nc_ls_max, self.nc_ls_max+1):
self.fosc = fosc_1 * i
self.nc_ls = i
result = self._find_n2()
if result:
break
if i == 1:
self.nc_ls = i+1
else:
self.nc_ls = i+2
return result
def _calc_freq_settings(self, clk_in_freq, clk_out_freq):
self.fin = clk_in_freq << 28
self.fout = clk_out_freq << 28
best_delta_fout = self.fout
self.n1_min = SI5324_FOSC_MIN//clk_out_freq
if self.n1_min < SI5324_N1_HS_MIN * SI5324_NC_LS_MIN:
self.n1_min = SI5324_N1_HS_MIN * SI5324_NC_LS_MIN
self.n1_max = SI5324_FOSC_MAX//clk_out_freq
if self.n1_max > SI5324_N1_HS_MAX * SI5324_NC_LS_MAX:
self.n1_max = SI5324_N1_HS_MAX * SI5324_NC_LS_MAX
self.n3_min = clk_in_freq//SI5324_F3_MAX
if self.n3_min < SI5324_N3_MIN:
self.n3_min = SI5324_N3_MIN
self.n3_max = clk_in_freq//SI5324_F3_MIN
if self.n3_max > SI5324_N3_MAX:
self.n3_max = SI5324_N3_MAX
for i in range(SI5324_N1_HS_MAX, SI5324_N1_HS_MIN-1, -1):
self.n1_hs = i
result = self._calc_ncls_limits()
if result:
continue
result = self._find_ncls()
if result:
break
if best_delta_fout == best_delta_fout//self.fout:
return SI5234_ERR_FREQ
self.vals[0] = self.best_n1_hs-4
self.vals[1] = self.best_nc_ls-1
self.vals[2] = self.best_n2_hs-4
self.vals[3] = self.best_n2_ls-1
self.vals[4] = self.best_n3-1
self.vals[5] = 6
return SI5324_SUCCESS
def _set_clock(self, clk_src, clk_in_freq, clk_out_freq):
buf = np.zeros(30, dtype=np.uint8)
if clk_src < SI5324_CLKSRC_CLK1 or clk_src > SI5324_CLKSRC_XTAL:
raise RuntimeError("Si5324 Error : Incorrect input clock selected")
if clk_src == SI5324_CLKSRC_CLK2:
raise RuntimeError("Si5324 Error : clock input 2 not supported")
if clk_in_freq < SI5324_FIN_MIN or clk_in_freq > SI5324_FIN_MAX:
raise RuntimeError("Si5324 Error :Input Frequency out of range")
if clk_out_freq < SI5324_FOUT_MIN or clk_out_freq > SI5324_FOUT_MAX:
raise RuntimeError("Si5324 ERROR: Output frequency out of range")
result = self._calc_freq_settings(clk_in_freq, clk_out_freq)
if result != SI5324_SUCCESS:
raise RuntimeError("Si5324 ERROR: Could not determine settings "
"for requested frequency")
i = 0
buf[i] = 0
if clk_src == SI5324_CLKSRC_XTAL:
buf[i+1] = 0x54
else:
buf[i+1] = 0x14
i = i+2
buf[i] = 2
buf[i+1] = (self.vals[5] << 4) | 0x02
i += 2
buf[i] = 11
if clk_src == SI5324_CLKSRC_CLK1:
buf[i+1] = 0x42
else:
buf[i+1] = 0x41
i += 2
buf[i] = 13
buf[i+1] = 0x2f
i += 2
buf[i] = 25
buf[i+1] = self.vals[0] << 5
i += 2
buf[i] = 31
buf[i+1] = (self.vals[1] & 0x000F0000) >> 16
buf[i+2] = 32
buf[i+3] = (self.vals[1] & 0x0000FF00) >> 8
buf[i+4] = 33
buf[i+5] = self.vals[1] & 0x000000FF
i += 6
buf[i] = 40
buf[i+1] = self.vals[2] << 5
temp = (self.vals[3] & 0x000F0000) >> 16
buf[i+1] = buf[i+1] | temp
buf[i+2] = 41
buf[i+3] = (self.vals[3] & 0x0000FF00) >> 8
buf[i+4] = 42
buf[i+5] = self.vals[3] & 0x000000FF
i += 6
if clk_src == SI5324_CLKSRC_CLK1:
buf[i] = 43
buf[i+2] = 44
buf[i+4] = 45
else:
buf[i] = 46
buf[i+2] = 47
buf[i+4] = 48
buf[i+1] = (self.vals[4] & 0x00070000) >> 16
buf[i+3] = (self.vals[4] & 0x0000FF00) >> 8
buf[i+5] = self.vals[4] & 0x000000FF
i += 6
buf[i] = 136
buf[i+1] = 0x40
i += 2
if i != buf.shape[0]:
return
for index in range(0, i, 2):
reg_addr = buf[index]
data = buf[index+1]
self._write(reg_addr, data)
return result