summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/driver/si5338.c
diff options
context:
space:
mode:
authorArturs Artamonovs <arturs.artamonovs@protonmail.com>2024-11-03 15:56:55 +0000
committerArturs Artamonovs <arturs.artamonovs@protonmail.com>2024-11-03 15:56:55 +0000
commitcf4444e7390365df43ecbd3d130015c1e06ef88f (patch)
tree8a6eb114135a04d5efd5af213577b4fac47532ae /Radio/HW/BladeRF/src/driver/si5338.c
parentca50c0f64f1b2fce46b4cb83ed111854bac13852 (diff)
downloadPrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.tar.gz
PrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.zip
BladeRF library compiles
Diffstat (limited to 'Radio/HW/BladeRF/src/driver/si5338.c')
-rw-r--r--Radio/HW/BladeRF/src/driver/si5338.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/driver/si5338.c b/Radio/HW/BladeRF/src/driver/si5338.c
new file mode 100644
index 0000000..80cda11
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/si5338.c
@@ -0,0 +1,669 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+#include "log.h"
+
+#include "si5338.h"
+
+#define SI5338_EN_A 0x01
+#define SI5338_EN_B 0x02
+
+#define SI5338_F_VCO (38400000UL * 66UL)
+
+/**
+ * This is used set or recreate the si5338 frequency
+ * Each si5338 multisynth module can be set independently
+ */
+struct si5338_multisynth {
+ /* Multisynth to program (0-3) */
+ uint8_t index;
+
+ /* Base address of the multisynth */
+ uint16_t base;
+
+ /* Requested and actual sample rates */
+ struct bladerf_rational_rate requested;
+ struct bladerf_rational_rate actual;
+
+ /* Enables for A and/or B outputs */
+ uint8_t enable;
+
+ /* f_out = fvco / (a + b/c) / r */
+ uint32_t a, b, c, r;
+
+ /* (a, b, c) in multisynth (p1, p2, p3) form */
+ uint32_t p1, p2, p3;
+
+ /* (p1, p2, p3) in register form */
+ uint8_t regs[10];
+};
+
+static void si5338_log_read_error(int error, const char *s)
+{
+ log_debug("Could not read from si5338 (%d): %s\n", error, s);
+ return;
+}
+
+static void si5338_log_write_error(int error, const char *s)
+{
+ log_debug("Could not write to si5338 (%d): %s\n", error, s);
+ return;
+}
+
+static uint64_t si5338_gcd(uint64_t a, uint64_t b)
+{
+ uint64_t t;
+ while (b != 0) {
+ t = b;
+ b = a % t;
+ a = t;
+ }
+ return a;
+}
+
+static void si5338_rational_reduce(struct bladerf_rational_rate *r)
+{
+ int64_t val;
+
+ if ((r->den > 0) && (r->num >= r->den)) {
+ /* Get whole number */
+ uint64_t whole = r->num / r->den;
+ r->integer += whole;
+ r->num = r->num - whole*r->den;
+ }
+
+ /* Reduce fraction */
+ val = si5338_gcd(r->num, r->den);
+ if (val > 0) {
+ r->num /= val;
+ r->den /= val;
+ }
+
+ return ;
+}
+
+static void si5338_rational_double(struct bladerf_rational_rate *r)
+{
+ r->integer *= 2;
+ r->num *= 2;
+ si5338_rational_reduce(r);
+ return;
+}
+
+/**
+ * Update the base address of the selected multisynth
+ */
+static void si5338_update_base(struct si5338_multisynth *ms)
+{
+ ms->base = 53 + ms->index*11 ;
+ return;
+}
+
+/**
+ * Unpack the recently read registers into (p1, p2, p3) and (a, b, c)
+ *
+ * Precondition:
+ * regs[10] and r have been read
+ *
+ * Post-condition:
+ * (p1, p2, p3), (a, b, c) and actual are populated
+ */
+static void si5338_unpack_regs(struct si5338_multisynth *ms)
+{
+ uint64_t temp;
+
+ /* Zeroize */
+ ms->p1 = ms->p2 = ms->p3 = 0;
+
+ /* Populate */
+ ms->p1 = ((ms->regs[2]&3)<<16) | (ms->regs[1]<<8) | (ms->regs[0]);
+ ms->p2 = (ms->regs[5]<<22) | (ms->regs[4]<<14) | (ms->regs[3]<<6) | ((ms->regs[2]>>2)&0x3f);
+ ms->p3 = ((ms->regs[9]&0x3f)<<24) | (ms->regs[8]<<16) | (ms->regs[7]<<8) | (ms->regs[6]);
+
+ log_verbose("Unpacked P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n",
+ ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3);
+
+ /* c = p3 */
+ ms->c = ms->p3;
+
+ /* a = (p1+512)/128
+ *
+ * NOTE: The +64 is for rounding purposes.
+ */
+ ms->a = (ms->p1+512)/128;
+
+ /* b = (((p1+512)-128*a)*c + (b % c) + 64)/128 */
+ temp = (ms->p1+512)-128*(uint64_t)ms->a;
+ temp = (temp * ms->c) + ms->p2;
+ temp = (temp + 64) / 128;
+ assert(temp <= UINT32_MAX);
+ ms->b = (uint32_t)temp;
+
+ log_verbose("Unpacked a + b/c: %d + %d/%d\n", ms->a, ms->b, ms->c);
+ log_verbose("Unpacked r: %d\n", ms->r);
+}
+
+/*
+ * Pack (a, b, c, r) into (p1, p2, p3) and regs[]
+ */
+static void si5338_pack_regs(struct si5338_multisynth *ms)
+{
+ /* Precondition:
+ * (a, b, c) and r have been populated
+ *
+ * Post-condition:
+ * (p1, p2, p3) and regs[10] are populated
+ */
+
+ /* p1 = (a * c + b) * 128 / c - 512 */
+ uint64_t temp;
+ temp = (uint64_t)ms->a * ms->c + ms->b;
+ temp = temp * 128 ;
+ temp = temp / ms->c - 512;
+ assert(temp <= UINT32_MAX);
+ ms->p1 = (uint32_t)temp;
+ //ms->p1 = ms->a * ms->c + ms->b;
+ //ms->p1 = ms->p1 * 128;
+ //ms->p1 = ms->p1 / ms->c - 512;
+
+ /* p2 = (b * 128) % c */
+ temp = (uint64_t)ms->b * 128;
+ temp = temp % ms->c;
+ assert(temp <= UINT32_MAX);
+ ms->p2 = (uint32_t)temp;
+
+ /* p3 = c */
+ ms->p3 = ms->c;
+
+ log_verbose("MSx P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n",
+ ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3);
+
+ /* Regs */
+ ms->regs[0] = ms->p1 & 0xff;
+ ms->regs[1] = (ms->p1 >> 8) & 0xff;
+ ms->regs[2] = ((ms->p2 & 0x3f) << 2) | ((ms->p1 >> 16) & 0x3);
+ ms->regs[3] = (ms->p2 >> 6) & 0xff;
+ ms->regs[4] = (ms->p2 >> 14) & 0xff;
+ ms->regs[5] = (ms->p2 >> 22) & 0xff;
+ ms->regs[6] = ms->p3 & 0xff;
+ ms->regs[7] = (ms->p3 >> 8) & 0xff;
+ ms->regs[8] = (ms->p3 >> 16) & 0xff;
+ ms->regs[9] = (ms->p3 >> 24) & 0xff;
+
+ return ;
+}
+
+static int si5338_write_multisynth(struct bladerf *dev,
+ struct si5338_multisynth *ms)
+{
+ int i, status;
+ uint8_t r_power, r_count, val;
+
+ log_verbose("Writing MS%d\n", ms->index);
+
+ /* Write out the enables */
+ status = dev->backend->si5338_read(dev, 36 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ val |= ms->enable;
+ log_verbose("Wrote enable register: 0x%2.2x\n", val);
+ status = dev->backend->si5338_write(dev, 36 + ms->index, val);
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ /* Write out the registers */
+ for (i = 0 ; i < 10 ; i++) {
+ status = dev->backend->si5338_write(dev, ms->base + i, *(ms->regs+i));
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Wrote regs[%d]: 0x%2.2x\n", i, *(ms->regs+i));
+ }
+
+ /* Calculate r_power from c_count */
+ r_power = 0;
+ r_count = ms->r >> 1 ;
+ while (r_count > 0) {
+ r_count >>= 1;
+ r_power++;
+ }
+
+ /* Set the r value to the log2(r_count) to match Figure 18 */
+ val = 0xc0;
+ val |= (r_power<<2);
+
+ log_verbose("Wrote r register: 0x%2.2x\n", val);
+
+ status = dev->backend->si5338_write(dev, 31 + ms->index, val);
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ }
+
+ return status ;
+}
+
+static int si5338_read_multisynth(struct bladerf *dev,
+ struct si5338_multisynth *ms)
+{
+ int i, status;
+ uint8_t val;
+
+ log_verbose("Reading MS%d\n", ms->index);
+
+ /* Read the enable bits */
+ status = dev->backend->si5338_read(dev, 36 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status ;
+ }
+ ms->enable = val&7;
+ log_verbose("Read enable register: 0x%2.2x\n", val);
+
+ /* Read all of the multisynth registers */
+ for (i = 0; i < 10; i++) {
+ status = dev->backend->si5338_read(dev, ms->base + i, ms->regs+i);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Read regs[%d]: 0x%2.2x\n", i, *(ms->regs+i));
+ }
+
+ /* Populate the RxDIV value from the register */
+ status = dev->backend->si5338_read(dev, 31 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ /* RxDIV is stored as a power of 2, so restore it on readback */
+ log_verbose("Read r register: 0x%2.2x\n", val);
+ val = (val>>2)&7;
+ ms->r = (1<<val);
+
+ /* Unpack the regs into appropriate values */
+ si5338_unpack_regs(ms) ;
+
+ return 0;
+}
+
+static void si5338_calculate_ms_freq(struct si5338_multisynth *ms,
+ struct bladerf_rational_rate *rate)
+{
+ struct bladerf_rational_rate abc;
+ abc.integer = ms->a;
+ abc.num = ms->b;
+ abc.den = ms->c;
+
+ rate->integer = 0;
+ rate->num = SI5338_F_VCO * abc.den;
+ rate->den = (uint64_t)ms->r*(abc.integer * abc.den + abc.num);
+
+ /* Compensate for doubling of frequency for LMS sampling clocks */
+ if(ms->index == 1 || ms->index == 2) {
+ rate->den *= 2;
+ }
+
+ si5338_rational_reduce(rate);
+
+ log_verbose("Calculated multisynth frequency: %"
+ PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ rate->integer, rate->num, rate->den);
+
+ return;
+}
+
+static int si5338_calculate_multisynth(struct si5338_multisynth *ms,
+ struct bladerf_rational_rate *rate)
+{
+
+ struct bladerf_rational_rate req;
+ struct bladerf_rational_rate abc;
+ uint8_t r_value;
+
+ /* Don't muss with the users data */
+ req = *rate;
+
+ /* Double requested frequency for sample clocks since LMS requires
+ * 2:1 clock:sample rate
+ */
+ if(ms->index == 1 || ms->index == 2) {
+ si5338_rational_double(&req);
+ }
+
+ /* Find a suitable R value */
+ r_value = 1;
+ while (req.integer < 5000000 && r_value < 32) {
+ si5338_rational_double(&req);
+ r_value <<= 1;
+ }
+
+ if (r_value == 32 && req.integer < 5000000) {
+ log_debug("Sample rate requires r > 32\n");
+ return BLADERF_ERR_INVAL;
+ } else {
+ log_verbose("Found r value of: %d\n", r_value);
+ }
+
+ /* Find suitable MS (a, b, c) values */
+ abc.integer = 0;
+ abc.num = SI5338_F_VCO * req.den;
+ abc.den = req.integer * req.den + req.num;
+ si5338_rational_reduce(&abc);
+
+ log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ abc.integer, abc.num, abc.den);
+
+ /* Check values to make sure they are OK */
+ if (abc.integer < 8) {
+ switch (abc.integer) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ case 7:
+ log_debug("Integer portion too small: %"PRIu64"\n", abc.integer);
+ return BLADERF_ERR_INVAL;
+ }
+ } else if (abc.integer > 567) {
+ log_debug("Integer portion too large: %"PRIu64"\n", abc.integer);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Loss of precision if num or den are greater than 2^30-1 */
+ while (abc.num > (1<<30) || abc.den > (1<<30) ) {
+ log_debug("Loss of precision in reducing fraction from "
+ "%"PRIu64"/%"PRIu64" to %"PRIu64"/%"PRIu64"\n",
+ abc.num, abc.den, abc.num>>1, abc.den>>1);
+ abc.num >>= 1;
+ abc.den >>= 1;
+ }
+
+ log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ abc.integer, abc.num, abc.den);
+
+ /* Set it in the multisynth */
+ assert(abc.integer <= UINT32_MAX);
+ assert(abc.num <= UINT32_MAX);
+ assert(abc.den <= UINT32_MAX);
+ ms->a = (uint32_t)abc.integer;
+ ms->b = (uint32_t)abc.num;
+ ms->c = (uint32_t)abc.den;
+ ms->r = r_value;
+
+ /* Pack the registers */
+ si5338_pack_regs(ms);
+
+ return 0;
+}
+
+/**
+ * Configure a multisynth for either the RX/TX sample clocks (index=1 or 2)
+ * or for the SMB output (index=3).
+ */
+static int si5338_set_rational_multisynth(struct bladerf *dev,
+ uint8_t index, uint8_t channel,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual_ret)
+{
+ struct si5338_multisynth ms;
+ struct bladerf_rational_rate req;
+ struct bladerf_rational_rate actual;
+ int status;
+
+ si5338_rational_reduce(rate);
+
+ /* Save off the value */
+ req = *rate;
+
+ /* Setup the multisynth enables and index */
+ ms.index = index;
+ ms.enable = channel;
+
+ /* Update the base address register */
+ si5338_update_base(&ms);
+
+ /* Calculate multisynth values */
+ status = si5338_calculate_multisynth(&ms, &req);
+ if(status != 0) {
+ return status;
+ }
+
+ /* Get the actual rate */
+ si5338_calculate_ms_freq(&ms, &actual);
+ if (actual_ret) {
+ memcpy(actual_ret, &actual, sizeof(*actual_ret));
+ }
+
+ /* Program it to the part */
+ status = si5338_write_multisynth(dev, &ms);
+
+ /* Done */
+ return status ;
+}
+
+
+int si5338_set_rational_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ struct bladerf_rational_rate rate_reduced = *rate;
+ uint8_t index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2;
+ uint8_t channel = SI5338_EN_A;
+
+ /* Enforce minimum sample rate */
+ si5338_rational_reduce(&rate_reduced);
+ if (rate_reduced.integer < BLADERF_SAMPLERATE_MIN) {
+ log_debug("%s: provided sample rate violates minimum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (ch == BLADERF_CHANNEL_TX(0)) {
+ channel |= SI5338_EN_B;
+ }
+
+ return si5338_set_rational_multisynth(dev, index, channel, &rate_reduced, actual);
+}
+
+int si5338_set_rational_smb_freq(struct bladerf *dev,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ struct bladerf_rational_rate rate_reduced = *rate;
+
+ /* Enforce minimum and maximum frequencies */
+ si5338_rational_reduce(&rate_reduced);
+
+ if (rate_reduced.integer < BLADERF_SMB_FREQUENCY_MIN) {
+ log_debug("%s: provided SMB freq violates minimum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (rate_reduced.integer > BLADERF_SMB_FREQUENCY_MAX) {
+ log_debug("%s: provided SMB freq violates maximum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return si5338_set_rational_multisynth(dev, 3, SI5338_EN_A, &rate_reduced, actual);
+}
+
+int si5338_set_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ uint32_t rate, uint32_t *actual)
+{
+ struct bladerf_rational_rate req, act;
+ int status;
+
+ memset(&act, 0, sizeof(act));
+ log_verbose("Setting integer sample rate: %d\n", rate);
+ req.integer = rate;
+ req.num = 0;
+ req.den = 1;
+
+ status = si5338_set_rational_sample_rate(dev, ch, &req, &act);
+
+ if (status == 0 && act.num != 0) {
+ log_info("Non-integer sample rate set from integer sample rate, "
+ "truncating output.\n");
+ }
+
+ assert(act.integer <= UINT32_MAX);
+
+ if (actual) {
+ *actual = (uint32_t)act.integer;
+ }
+ log_verbose("Set actual integer sample rate: %d\n", act.integer);
+
+ return status ;
+}
+
+int si5338_set_smb_freq(struct bladerf *dev, uint32_t rate, uint32_t *actual)
+{
+ struct bladerf_rational_rate req, act;
+ int status;
+
+ memset(&act, 0, sizeof(act));
+ log_verbose("Setting integer SMB frequency: %d\n", rate);
+ req.integer = rate;
+ req.num = 0;
+ req.den = 1;
+
+ status = si5338_set_rational_smb_freq(dev, &req, &act);
+
+ if (status == 0 && act.num != 0) {
+ log_info("Non-integer SMB frequency set from integer frequency, "
+ "truncating output.\n");
+ }
+
+ assert(act.integer <= UINT32_MAX);
+
+ if (actual) {
+ *actual = (uint32_t)act.integer;
+ }
+ log_verbose("Set actual integer SMB frequency: %d\n", act.integer);
+
+ return status;
+}
+
+int si5338_get_rational_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ struct bladerf_rational_rate *rate)
+{
+
+ struct si5338_multisynth ms;
+ int status;
+
+ /* Select the multisynth we want to read */
+ ms.index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2;
+
+ /* Update the base address */
+ si5338_update_base(&ms);
+
+ /* Readback */
+ status = si5338_read_multisynth(dev, &ms);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ si5338_calculate_ms_freq(&ms, rate);
+
+ return 0;
+}
+
+int si5338_get_rational_smb_freq(struct bladerf *dev,
+ struct bladerf_rational_rate *rate)
+{
+ struct si5338_multisynth ms;
+ int status;
+
+ /* Select MS3 for the SMB output */
+ ms.index = 3;
+ si5338_update_base(&ms);
+
+ status = si5338_read_multisynth(dev, &ms);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ si5338_calculate_ms_freq(&ms, rate);
+
+ return 0;
+}
+
+int si5338_get_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ unsigned int *rate)
+{
+ struct bladerf_rational_rate actual;
+ int status;
+
+ status = si5338_get_rational_sample_rate(dev, ch, &actual);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ if (actual.num != 0) {
+ log_debug("Fractional sample rate truncated during integer sample rate"
+ "retrieval\n");
+ }
+
+ assert(actual.integer <= UINT_MAX);
+ *rate = (unsigned int)actual.integer;
+
+ return 0;
+}
+
+int si5338_get_smb_freq(struct bladerf *dev, unsigned int *rate)
+{
+ struct bladerf_rational_rate actual;
+ int status;
+
+ status = si5338_get_rational_smb_freq(dev, &actual);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ if (actual.num != 0) {
+ log_debug("Fractional SMB frequency truncated during integer SMB"
+ " frequency retrieval\n");
+ }
+
+ assert(actual.integer <= UINT_MAX);
+ *rate = (unsigned int)actual.integer;
+
+ return 0;
+}