1.1 --- a/pkg/devices/lib/cpm/src/common.cc Fri Sep 22 21:55:54 2023 +0200
1.2 +++ b/pkg/devices/lib/cpm/src/common.cc Fri Sep 22 21:56:34 2023 +0200
1.3 @@ -226,6 +226,18 @@
1.4 return _bypass.get_field(regs);
1.5 }
1.6
1.7 +void
1.8 +Control_pll::pll_bypass(Cpm_regs ®s)
1.9 +{
1.10 + _bypass.set_field(regs, 1);
1.11 +}
1.12 +
1.13 +void
1.14 +Control_pll::pll_engage(Cpm_regs ®s)
1.15 +{
1.16 + _bypass.set_field(regs, 0);
1.17 +}
1.18 +
1.19 // Clock control.
1.20
1.21 int
1.22 @@ -290,6 +302,13 @@
1.23 }
1.24
1.25 int
1.26 +Divider::set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency)
1.27 +{
1.28 + set_divider(regs, (uint32_t) round((double) source_frequency / (double) frequency));
1.29 + return 1;
1.30 +}
1.31 +
1.32 +int
1.33 Divider::get_parameters(Cpm_regs ®s, uint32_t parameters[])
1.34 {
1.35 parameters[0] = get_divider(regs);
1.36 @@ -310,41 +329,97 @@
1.37
1.38
1.39
1.40 -// Feedback (13-bit) multiplier.
1.41 +// Common divider functionality.
1.42 +
1.43 +static int is_integer(double x)
1.44 +{
1.45 + double target = round(x) * 1000;
1.46 + double rounded = floor(x * 1000);
1.47 +
1.48 + return (target - 100 < rounded) && (rounded < target + 100);
1.49 +}
1.50 +
1.51 +static double getscale_part(double x)
1.52 +{
1.53 + double part = x - floor(x);
1.54 +
1.55 + if (part > 0.5)
1.56 + return 1 / (1 - part);
1.57 + else if (part > 0)
1.58 + return 1 / part;
1.59 + else
1.60 + return 1;
1.61 +}
1.62 +
1.63 +static double getscale(double x)
1.64 +{
1.65 + double scale = getscale_part(x);
1.66 +
1.67 + if (is_integer(scale))
1.68 + return scale;
1.69 + else
1.70 + return scale * getscale(scale);
1.71 +}
1.72 +
1.73 +static void get_divider_operands(double frequency, double source_frequency,
1.74 + double *multiplier, double *divider)
1.75 +{
1.76 + double ratio = frequency / source_frequency;
1.77 + double scale = getscale(ratio);
1.78 +
1.79 + *multiplier = scale * ratio;
1.80 + *divider = scale;
1.81 +}
1.82 +
1.83 +static void reduce_divider_operands(uint32_t *m, uint32_t *n, uint32_t m_limit,
1.84 + uint32_t n_limit)
1.85 +{
1.86 + while ((*m > m_limit) && (*n > n_limit) && (*m > 1) && (*n > 1))
1.87 + {
1.88 + *m >>= 1;
1.89 + *n >>= 1;
1.90 + }
1.91 +}
1.92 +
1.93 +#define zero_as_one(X) ((X) ? (X) : 1)
1.94 +
1.95 +
1.96 +
1.97 +// Feedback multiplier.
1.98
1.99 uint32_t
1.100 Divider_pll::get_multiplier(Cpm_regs ®s)
1.101 {
1.102 - return _multiplier.get_field(regs) + 1;
1.103 + return zero_as_one(_multiplier.get_field(regs));
1.104 }
1.105
1.106 void
1.107 Divider_pll::set_multiplier(Cpm_regs ®s, uint32_t multiplier)
1.108 {
1.109 - _multiplier.set_field(regs, multiplier - 1);
1.110 + _multiplier.set_field(regs, multiplier);
1.111 }
1.112
1.113 -// Input (6-bit) divider.
1.114 +// Input divider.
1.115
1.116 uint32_t
1.117 Divider_pll::get_input_divider(Cpm_regs ®s)
1.118 {
1.119 - return _input_divider.get_field(regs) + 1;
1.120 + return zero_as_one(_input_divider.get_field(regs));
1.121 }
1.122
1.123 void
1.124 Divider_pll::set_input_divider(Cpm_regs ®s, uint32_t divider)
1.125 {
1.126 - _input_divider.set_field(regs, divider - 1);
1.127 + _input_divider.set_field(regs, divider);
1.128 }
1.129
1.130 -// Output (dual 3-bit) dividers.
1.131 +// Output dividers.
1.132
1.133 uint32_t
1.134 Divider_pll::get_output_divider(Cpm_regs ®s)
1.135 {
1.136 - uint32_t d0 = _output_divider0.get_field(regs);
1.137 - uint32_t d1 = _output_divider1.get_field(regs);
1.138 + uint32_t d0 = zero_as_one(_output_divider0.get_field(regs));
1.139 + uint32_t d1 = zero_as_one(_output_divider1.get_field(regs));
1.140
1.141 return d0 * d1;
1.142 }
1.143 @@ -355,8 +430,21 @@
1.144 // Assert 1 as a minimum.
1.145 // Divider 0 must be less than or equal to divider 1.
1.146
1.147 - uint32_t d0 = (uint32_t) floor(sqrt(divider ? divider : 1));
1.148 - uint32_t d1 = divider / d0;
1.149 + uint32_t d0, d1;
1.150 +
1.151 + if (!divider)
1.152 + divider = 1;
1.153 +
1.154 + if (divider < _output_divider1.get_limit())
1.155 + {
1.156 + d0 = 1;
1.157 + d1 = divider;
1.158 + }
1.159 + else
1.160 + {
1.161 + d0 = (uint32_t) floor(sqrt(divider));
1.162 + d1 = divider / d0;
1.163 + }
1.164
1.165 _output_divider0.set_field(regs, d0);
1.166 _output_divider1.set_field(regs, d1);
1.167 @@ -370,6 +458,66 @@
1.168 }
1.169
1.170 int
1.171 +Divider_pll::set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency)
1.172 +{
1.173 + double intermediate_min = 600000000, intermediate_max = 2400000000;
1.174 + double intermediate_multiplier, intermediate_input_divider;
1.175 + uint32_t output_min, output_max, output0, output1;
1.176 + uint32_t multiplier, input_divider, output_divider;
1.177 +
1.178 + // Distribute the divider across the input and output divider. For the X1600,
1.179 + // the multiplier and input divider should collectively deliver a frequency in
1.180 + // the range 600-2400 MHz.
1.181 +
1.182 + output_min = (uint32_t) ceil(intermediate_min / frequency);
1.183 + output_max = (uint32_t) floor(intermediate_max / frequency);
1.184 +
1.185 + output_divider = output_min;
1.186 +
1.187 + while (output_divider <= output_max)
1.188 + {
1.189 + // Test divider constraints.
1.190 +
1.191 + output0 = (uint32_t) floor(sqrt(output_divider));
1.192 + output1 = (uint32_t) floor(output_divider / output0);
1.193 +
1.194 + if ((output0 * output1 == output_divider) &&
1.195 + (output0 <= _output_divider0.get_limit()) &&
1.196 + (output1 <= _output_divider1.get_limit()))
1.197 + {
1.198 + // Calculate the other parameters.
1.199 +
1.200 + uint32_t intermediate_frequency = frequency * output_divider;
1.201 +
1.202 + get_divider_operands(intermediate_frequency, source_frequency,
1.203 + &intermediate_multiplier, &intermediate_input_divider);
1.204 +
1.205 + multiplier = (uint32_t) round(intermediate_multiplier);
1.206 + input_divider = (uint32_t) round(intermediate_input_divider);
1.207 +
1.208 + uint32_t multiplier_limit = _multiplier.get_limit();
1.209 + uint32_t input_divider_limit = _input_divider.get_limit();
1.210 +
1.211 + reduce_divider_operands(&multiplier, &input_divider,
1.212 + multiplier_limit, input_divider_limit);
1.213 +
1.214 + if ((multiplier <= multiplier_limit) && (input_divider <= input_divider_limit))
1.215 + {
1.216 + set_multiplier(regs, multiplier);
1.217 + set_input_divider(regs, input_divider);
1.218 + set_output_divider(regs, output_divider);
1.219 +
1.220 + return 1;
1.221 + }
1.222 + }
1.223 +
1.224 + output_divider++;
1.225 + }
1.226 +
1.227 + return 0;
1.228 +}
1.229 +
1.230 +int
1.231 Divider_pll::get_parameters(Cpm_regs ®s, uint32_t parameters[])
1.232 {
1.233 parameters[0] = get_multiplier(regs);
1.234 @@ -418,11 +566,46 @@
1.235 uint32_t
1.236 Divider_i2s::get_frequency(Cpm_regs ®s, uint32_t source_frequency)
1.237 {
1.238 + /* NOTE: Assuming that this is the formula, given that the manual does not
1.239 + really describe how D is used. */
1.240 +
1.241 return (source_frequency * get_multiplier(regs)) /
1.242 (get_divider_N(regs) * get_divider_D(regs));
1.243 }
1.244
1.245 int
1.246 +Divider_i2s::set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency)
1.247 +{
1.248 + double m, n;
1.249 +
1.250 + get_divider_operands(frequency, source_frequency, &m, &n);
1.251 +
1.252 + uint32_t multiplier = (uint32_t) round(m);
1.253 + uint32_t divider = (uint32_t) round(n);
1.254 +
1.255 + reduce_divider_operands(&multiplier, ÷r,
1.256 + _multiplier.get_limit(),
1.257 + _divider_N.get_limit());
1.258 +
1.259 + // Test for operand within limits and the N >= 2M constraint.
1.260 +
1.261 + if ((multiplier <= _multiplier.get_limit()) && (divider <= _divider_N.get_limit()) &&
1.262 + (divider >= 2 * multiplier))
1.263 + {
1.264 + /* NOTE: Setting D to 1. Even though it seems that D might also be used,
1.265 + it does not seem necessary in practice, and the documentation is
1.266 + unclear about its use. */
1.267 +
1.268 + uint32_t parameters[] = {multiplier, divider, 1};
1.269 +
1.270 + set_parameters(regs, 3, parameters);
1.271 + return 1;
1.272 + }
1.273 +
1.274 + return 0;
1.275 +}
1.276 +
1.277 +int
1.278 Divider_i2s::get_parameters(Cpm_regs ®s, uint32_t parameters[])
1.279 {
1.280 parameters[0] = get_multiplier(regs);
1.281 @@ -446,7 +629,7 @@
1.282 }
1.283 else if (num_parameters > 1)
1.284 {
1.285 - // Test for N < 2M.
1.286 + // Require N >= 2M, returning otherwise.
1.287
1.288 if (parameters[1] < 2 * parameters[0])
1.289 return 0;
1.290 @@ -635,6 +818,17 @@
1.291 }
1.292
1.293 int
1.294 +Clock_divided_base::set_frequency(Cpm_regs ®s, uint32_t frequency)
1.295 +{
1.296 + _get_control().change_enable(regs);
1.297 + int result = _get_divider().set_frequency(regs, get_source_frequency(regs), frequency);
1.298 + _get_control().wait_busy(regs);
1.299 + _get_control().change_disable(regs);
1.300 +
1.301 + return result;
1.302 +}
1.303 +
1.304 +int
1.305 Clock_divided_base::get_parameters(Cpm_regs ®s, uint32_t parameters[])
1.306 {
1.307 return _get_divider().get_parameters(regs, parameters);
1.308 @@ -667,3 +861,12 @@
1.309 else
1.310 return get_source_frequency(regs);
1.311 }
1.312 +
1.313 +int
1.314 +Pll::set_frequency(Cpm_regs ®s, uint32_t frequency)
1.315 +{
1.316 + int result = Clock_divided_base::set_frequency(regs, frequency);
1.317 + _control.pll_engage(regs);
1.318 +
1.319 + return result;
1.320 +}