Skip to content

Commit

Permalink
Merge pull request #134 from yrabbit/stage-1
Browse files Browse the repository at this point in the history
Add parameter check and DUTY handling
  • Loading branch information
yrabbit authored Dec 6, 2022
2 parents 368e518 + 83fdc73 commit 61b1c14
Show file tree
Hide file tree
Showing 12 changed files with 344 additions and 57 deletions.
131 changes: 107 additions & 24 deletions apycula/gowin_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,59 @@ def get_pips(data):
def infovaluemap(infovalue, start=2):
return {tuple(iv[:start]):iv[start:] for iv in infovalue}

# Permitted frequencies for chips
# { device : (max_in, max_out, min_out, max_vco, min_vco) }
_permitted_freqs = {
"GW1N-1": (400, 450, 3.125, 900, 400),
"GW1NZ-1": (400, 400, 3.125, 800, 400),
"GW1N-4": (400, 500, 3.125, 1000, 400),
"GW1NS-4": (400, 600, 4.6875, 1200, 600),
"GW1N-9": (400, 500, 3.125, 1000, 400),
"GW1N-9C": (400, 600, 3.125, 1200, 400),
"GW1NS-2": (400, 500, 3.125, 1200, 400),
}
# input params are calculated as described in GOWIN doc (UG286-1.7E_Gowin Clock User Guide)
# fref = fclkin / idiv
# fvco = (odiv * fdiv * fclkin) / idiv
#
# returns (fclkin_idx, icp, r_idx)
# fclkin_idx - input frequency range index
# icp - charge current
# r_idx - resistor value index

# There are not many resistors so the whole frequency range is divided into
# 30MHz intervals and the number of this interval is one of the fuse sets. But
# the resistor itself is not directly dependent on the input frequency.
_freq_R = [(2.6, 65100.0), (3.87, 43800.0), (7.53, 22250.0), (14.35, 11800.0), (28.51, 5940.0), (57.01, 2970.0), (114.41, 1480), (206.34, 820.0)]
def calc_pll_pump(fref, fvco):
fclkin_idx = int((fref - 1) // 30)
if (fclkin_idx == 13 and fref <= 395) or (fclkin_idx == 14 and fref <= 430) or (fclkin_idx == 15 and fref <= 465) or fclkin_idx == 16:
fclkin_idx = fclkin_idx - 1

r_vals = [(fr[1], len(_freq_R) - 1 - idx) for idx, fr in enumerate(_freq_R) if fr[0] < fref]
r_vals.reverse()

# Find the resistor that provides the minimum current through the capacitor
K0 = (497.5 - math.sqrt(247506.25 - (2675.4 - fvco) * 78.46)) / 39.23
K1 = 4.8714 * K0 * K0 + 6.5257 * K0 + 142.67
Kvco = 1000000.0 * K1
Ndiv = fvco / fref
C1 = 6.69244e-11

for R1, r_idx in r_vals:
Ic = (1.8769 / (R1 * R1 * Kvco * C1)) * 4.0 * Ndiv
if Ic <= 0.00028:
icp = int(Ic * 100000.0 + 0.5) * 10
break

return ((fclkin_idx + 1) * 16, icp, r_idx)

# add the default pll attributes according to the documentation
_default_pll_inattrs = {
'FCLKIN' : '100.00',
'IDIV_SEL' : '0',
'DYN_IDIV_SEL' : 'false',
'FBDIV_SEL' : '00000000000000000000000000000010', # XXX not as in doc
'FBDIV_SEL' : '00000000000000000000000000000000',
'DYN_FBDIV_SEL' : 'false',
'ODIV_SEL' : '00000000000000000000000000001000',
'DYN_ODIV_SEL' : 'false',
Expand Down Expand Up @@ -122,8 +169,8 @@ def add_pll_default_attrs(attrs):
'CLKOUTPS': 'ENABLE',
'PDN': 'ENABLE',
'PASEL': 0,
'IRSTEN': 'ENABLE',
'SRSTEN': 'ENABLE',
'IRSTEN': 'DISABLE',
'SRSTEN': 'DISABLE',
'PWDEN': 'ENABLE',
'RSTEN': 'ENABLE',
'FLDCOUNT': 16,
Expand All @@ -136,15 +183,14 @@ def add_pll_default_attrs(attrs):
def set_pll_attrs(db, typ, attrs):
pll_inattrs = add_pll_default_attrs(attrs)
pll_attrs = _default_pll_internal_attrs.copy()
pll_attrs['IRSTEN'] = 'DISABLE'
pll_attrs['SRSTEN'] = 'DISABLE'

if typ not in ['RPLL']:
raise Exception(f"PLL type {typ} is not supported for now")

# parse attrs
for attr, val in pll_inattrs.items():
# XXX clock in and feedback in
if attr in pll_attrs.keys():
pll_attrs[attr] = val
if attr == 'CLKOUTD_SRC':
if val == 'CLKOUTP':
pll_attrs['CLKOUTDIVSEL'] = 'CLKOUTPS'
Expand All @@ -153,7 +199,6 @@ def set_pll_attrs(db, typ, attrs):
if val == 'CLKOUTP':
pll_attrs['CLKOUTDIV3SEL'] = 'CLKOUTPS'
continue
# XXX selin
if attr == 'DYN_IDIV_SEL':
if val == 'true':
pll_attrs['IDIVSEL'] = 'DYN'
Expand Down Expand Up @@ -187,38 +232,76 @@ def set_pll_attrs(db, typ, attrs):
pll_attrs['FDIV'] = fbdiv
continue
if attr == 'DYN_SDIV_SEL':
pll_attrs['SDIV'] = 2 + int(val, 2)
pll_attrs['SDIV'] = int(val, 2)
continue
if attr == 'ODIV_SEL':
odiv = int(val, 2)
pll_attrs['ODIV'] = odiv
continue
if attr == 'DYN_DA_EN':
if val == 'true':
pll_attrs['DPSEL'] = 'DYN'
pll_attrs['DUTY'] = 0
pll_attrs['PHASE'] = 0
pll_attrs['PASEL'] = 'DISABLE'
# steps in 50ps
tmp_val = int(pll_inattrs['CLKOUT_DLY_STEP'], 2) * 50
pll_attrs['OPDLY'] = tmp_val
# XXX here is unclear according to the documentation only three
# values are allowed: 0, 1 and 2, but there are 4 fuses (0, 50,
# 75, 100). Find out what to do with 75
tmp_val = int(pll_inattrs['CLKOUTP_DLY_STEP'], 2) * 50
pll_attrs['OSDLY'] = tmp_val
else:
pll_attrs['OSDLY'] = 'DISABLE'
pll_attrs['OPDLY'] = 'DISABLE'
phase_val = int(pll_inattrs['PSDA_SEL'].strip(), 2)
pll_attrs['PHASE'] = phase_val
duty_val = int(pll_inattrs['DUTYDA_SEL'].strip(), 2)
# XXX there are fuses for 15 variants (excluding 0) so for now
# we will implement all of them, including those prohibited by
# documentation 1 and 15
if (phase_val + duty_val) < 16:
duty_val = phase_val + duty_val
else:
duty_val = phase_val + duty_val - 16
pll_attrs['DUTY'] = duty_val
continue
if attr == 'FCLKIN':
fclkin = float(val)
if fclkin < 3 or fclkin > _permitted_freqs[device][0]:
print(f"The {fclkin}MHz frequency is outside the permissible range of 3-{_permitted_freqs[device][0]}MHz.")
fclkin = 100.0
continue

# XXX input is 24MHz only and output either 52MHz or 56MHz
# XXX input is 27MHz only and output either 58.5MHz or 63MHz
if device != "GW1N-1" and device != "GW1NZ-1":
raise Exception(f"PLL is not supported")
if (abs(fclkin - 24) > 0.01 and device == "GW1N-1") or (abs(fclkin - 27) > 0.01 and device == "GW1NZ-1"):
raise Exception(f"PLL input frequency {fclkin} is not supported")
if fbdiv == 13 and idiv == 6 and odiv == 8:
pll_attrs['FLDCOUNT'] = 16
pll_attrs['ICPSEL'] = 20
pll_attrs['LPR'] = 6
elif fbdiv == 7 and idiv == 3 and odiv == 8:
pll_attrs['FLDCOUNT'] = 16
pll_attrs['ICPSEL'] = 40
pll_attrs['LPR'] = 5
else:
raise Exception(f"PLL parameters are not supported for now")
# static vs dynamic
if pll_inattrs['DYN_IDIV_SEL'] == 'false' and pll_inattrs['DYN_FBDIV_SEL'] == 'false' and pll_inattrs['DYN_ODIV_SEL'] == 'false':
# static. We can immediately check the compatibility of the divisors
clkout = fclkin * fbdiv / idiv
if clkout <= _permitted_freqs[device][2] or clkout > _permitted_freqs[device][1]:
raise Exception(f"CLKOUT = FCLKIN*(FBDIV_SEL+1)/(IDIV_SEL+1) = {clkout}MHz not in range {_permitted_freqs[device][2]} - {_permitted_freqs[device][1]}MHz")
pfd = fclkin / idiv
if pfd < 3.0 or pfd > _permitted_freqs[device][0]:
raise Exception(f"PFD = FCLKIN/(IDIV_SEL+1) = {pfd}MHz not in range 3.0 - {_permitted_freqs[device][0]}MHz")
fvco = odiv * fclkin * fbdiv / idiv
if fvco < _permitted_freqs[device][4] or fvco > _permitted_freqs[device][3]:
raise Exception(f"VCO = FCLKIN*(FBDIV_SEL+1)*ODIV_SEL/(IDIV_SEL+1) = {fvco}MHz not in range {_permitted_freqs[device][4]} - {_permitted_freqs[device][3]}MHz")

# pump
fref = fclkin / idiv
fvco = (odiv * fbdiv * fclkin) / idiv
fclkin_idx, icp, r_idx = calc_pll_pump(fref, fvco)

pll_attrs['FLDCOUNT'] = fclkin_idx
pll_attrs['ICPSEL'] = int(icp)
pll_attrs['LPR'] = f"R{r_idx}"

fin_attrs = set()
for attr, val in pll_attrs.items():
if isinstance(val, str):
val = pll_attrvals[val]
add_attr_val(db, 'PLL', fin_attrs, pll_attrids[attr], val)
#print(fin_attrs)
return fin_attrs

iostd_alias = {
Expand Down
Loading

0 comments on commit 61b1c14

Please sign in to comment.