Merge pull request #1747 from martinling/si5351c-defs

Si5351C refactoring
This commit is contained in:
Michael Ossmann 2026-05-18 11:20:03 -04:00 committed by GitHub
commit c77e7e7358
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 871 additions and 254 deletions

View file

@ -111,9 +111,9 @@ void clock_gen_init(void)
sample_rate_set(SR_FP_MHZ(10), true);
si5351c_configure_clock_control(&si5351c);
si5351c_set_clock_source(&si5351c, PLL_SOURCE_XTAL);
si5351c_change_input(&si5351c, SI5351C_INPUT_XTAL);
// soft reset
si5351c_reset_pll(&si5351c, SI5351C_PLL_BOTH);
si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_BOTH);
si5351c_enable_clock_outputs(&si5351c);
}
@ -159,9 +159,10 @@ clock_source_t activate_best_clock_source(void)
/* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */
}
si5351c_set_clock_source(
&si5351c,
(source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN);
si5351c_input_t input = (source == CLOCK_SOURCE_HACKRF) ? SI5351C_INPUT_XTAL :
SI5351C_INPUT_CLKIN;
si5351c_change_input(&si5351c, input);
hackrf_ui()->set_clock_source(source);
return source;
@ -411,7 +412,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program)
}
/* Reset PLL to synchronize output clock phase. */
si5351c_reset_pll(&si5351c, SI5351C_PLL_A);
si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_A);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
*
* This file is part of HackRF.
@ -22,6 +22,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "clock_io.h"
#include "delay.h"
@ -36,6 +37,8 @@
#include "platform_scu.h"
#endif
#include "si5351c_regs.def"
/* Driver instance. */
// const i2c_lpc_config_t i2c_config_si5351c_slow_clock = {
// .duty_cycle_count = 15,
@ -48,16 +51,20 @@ si5351c_driver_t si5351c = {
.i2c_address = 0x60,
};
static enum pll_sources active_clock_source = PLL_SOURCE_UNINITIALIZED;
static bool input_initialized = false;
static si5351c_input_t active_input;
/* External clock output default is deactivated as it creates noise */
static bool clkout_enabled = false;
static uint8_t outputs_disabled = 0xff;
/* write to single register */
void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val)
{
const uint8_t data_tx[] = {reg, val};
si5351c_write(drv, data_tx, 2);
i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 2, NULL, 0);
if (reg < SI5351C_CACHED_REGS) {
drv->regs[reg] = val;
si5351c_reg_set_clean(drv, reg);
}
}
/* read single register */
@ -66,72 +73,100 @@ uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg)
const uint8_t data_tx[] = {reg};
uint8_t data_rx[] = {0x00};
i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 1, data_rx, 1);
return data_rx[0];
uint8_t val = data_rx[0];
if (reg < SI5351C_CACHED_REGS) {
drv->regs[reg] = val;
si5351c_reg_set_clean(drv, reg);
}
return val;
}
/*
* Write to one or more contiguous registers. data[0] should be the first
* register number, one or more values follow.
*/
void si5351c_write(
si5351c_driver_t* const drv,
const uint8_t* const data,
const size_t data_count)
/* Commit changes to register values. */
void si5351c_regs_commit(si5351c_driver_t* drv)
{
i2c_bus_transfer(drv->bus, drv->i2c_address, data, data_count, NULL, 0);
for (int start = 0; start < SI5351C_CACHED_REGS; start++) {
if (si5351c_reg_is_dirty(drv, start)) {
if (start == SI5351C_CACHED_REGS - 1) {
si5351c_write_single(drv, start, drv->regs[start]);
} else {
int end;
for (end = start + 1; end < 256; end++) {
if (!si5351c_reg_is_dirty(drv, end))
break;
}
size_t len = 1 + (end - start);
uint8_t data_tx[len];
data_tx[0] = start;
for (int i = 0; i < (end - start); i++) {
data_tx[1 + i] = drv->regs[start + i];
}
i2c_bus_transfer(
drv->bus,
drv->i2c_address,
data_tx,
len,
NULL,
0);
for (int i = start; i < end; i++) {
si5351c_reg_set_clean(drv, i);
}
start = end;
}
}
}
/* Reset bits are self-clearing. */
drv->regs[177] = 0;
}
/* Disable all CLKx outputs. */
void si5351c_disable_all_outputs(si5351c_driver_t* const drv)
{
outputs_disabled = 0xff;
uint8_t data[] = {3, outputs_disabled};
si5351c_write(drv, data, sizeof(data));
set_all_CLK_OEB(drv, SI5351C_OUTPUT_DISABLE);
si5351c_regs_commit(drv);
}
/* Disable all CLKx outputs using selected PLL. */
void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_t pll)
void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_mask_t mask)
{
/*
* Bitmask defines outputs using PLL B. Other outputs are assumed to
* use PLL A.
*/
uint8_t pllb_outputs = 0x00;
if (detected_platform() == BOARD_ID_PRALINE) {
pllb_outputs = 0x30;
/* For each CLKx output, check if it is using the specified PLL. */
for (int op = 0; op < 8; op++) {
int ms;
/* First check which multisynth is used by this output. */
switch (get_CLK_SRC(drv, op)) {
case SI5351C_SRC_MULTISYNTH_SELF:
ms = op;
break;
case SI5351C_SRC_MULTISYNTH_0_4:
ms = (op < 4) ? 0 : 4;
break;
default:
/* This output is not using any PLL */
continue;
}
/* Now check which PLL is used by that multisynth. */
if (mask & (1 << get_MS_SRC(drv, ms))) {
/* This output depends on the specified PLL; disable it. */
set_CLK_OEB(drv, op, SI5351C_OUTPUT_DISABLE);
}
}
if (pll & SI5351C_PLL_A) {
outputs_disabled |= ~pllb_outputs;
}
if (pll & SI5351C_PLL_B) {
outputs_disabled |= pllb_outputs;
}
uint8_t data[] = {3, outputs_disabled};
si5351c_write(drv, data, sizeof(data));
si5351c_regs_commit(drv);
}
/* Turn off OEB pin control for all CLKx */
void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv)
{
uint8_t data[] = {9, 0xFF};
si5351c_write(drv, data, sizeof(data));
set_all_OEB_MASK(drv, true);
si5351c_regs_commit(drv);
}
/* Power down all CLKx */
void si5351c_power_down_all_clocks(si5351c_driver_t* const drv)
{
uint8_t data[] = {
16,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE,
SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE};
si5351c_write(drv, data, sizeof(data));
set_all_CLK_PDN(drv, true);
set_FBA_INT(drv, true);
set_FBB_INT(drv, true);
si5351c_regs_commit(drv);
}
/*
@ -141,8 +176,8 @@ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv)
*/
void si5351c_set_crystal_configuration(si5351c_driver_t* const drv)
{
uint8_t data[] = {183, 0x80};
si5351c_write(drv, data, sizeof(data));
set_XTAL_CL(drv, SI5351C_XTAL_8PF);
si5351c_regs_commit(drv);
}
/*
@ -151,8 +186,10 @@ void si5351c_set_crystal_configuration(si5351c_driver_t* const drv)
*/
void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv)
{
uint8_t data[] = {187, 0xD0};
si5351c_write(drv, data, sizeof(data));
set_CLKIN_FANOUT_EN(drv, true);
set_XO_FANOUT_EN(drv, true);
set_MS_FANOUT_EN(drv, true);
si5351c_regs_commit(drv);
}
/*
@ -160,50 +197,39 @@ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv)
* CLKIN_DIV=0 (Divide by 1)
* Set both PLLA_SRC and PLLB_SRC
*/
void si5351c_configure_pll_sources(
si5351c_driver_t* const drv,
const enum pll_sources source)
void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input)
{
uint8_t data[] = {15, 0x00};
if (source == PLL_SOURCE_CLKIN) {
data[1] = 0x0c;
}
si5351c_write(drv, data, sizeof(data));
set_CLKIN_DIV(drv, SI5351C_DIV_1);
set_PLLA_SRC(drv, input);
set_PLLB_SRC(drv, input);
si5351c_regs_commit(drv);
}
/* MultiSynth NA (PLLA) and NB (PLLB) */
void si5351c_configure_pll_multisynth(
si5351c_driver_t* const drv,
const enum pll_sources source)
const si5351c_input_t input)
{
/* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */
uint8_t data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00};
if (source == PLL_SOURCE_CLKIN) {
/* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */
data[4] = 0x26;
for (si5351c_pll_t pll = SI5351C_PLL_A; pll <= SI5351C_PLL_B; pll++) {
if (input == SI5351C_INPUT_CLKIN) {
/* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */
set_MSN_P1(drv, pll, 0x2600);
} else {
/* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */
set_MSN_P1(drv, pll, 0x0E00);
}
set_MSN_P2(drv, pll, 0);
set_MSN_P3(drv, pll, 1);
}
si5351c_write(drv, data, sizeof(data));
/* Apply same configuration to PLL B. */
data[0] = 34;
si5351c_write(drv, data, sizeof(data));
si5351c_regs_commit(drv);
}
void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll)
void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask)
{
uint8_t value = 0;
if (pll & SI5351C_PLL_A) {
value |= 0x20;
}
if (pll & SI5351C_PLL_B) {
value |= 0x80;
}
si5351c_disable_pll_outputs(drv, pll);
uint8_t data[] = {177, value};
si5351c_write(drv, data, sizeof(data));
si5351c_disable_pll_outputs(drv, mask);
set_PLLA_RST(drv, (mask & SI5351C_PLL_MASK_A) ? true : false);
set_PLLB_RST(drv, (mask & SI5351C_PLL_MASK_B) ? true : false);
si5351c_regs_commit(drv);
delay_us_at_mhz(2000, 204);
si5351c_enable_clock_outputs(drv);
}
@ -228,110 +254,166 @@ void si5351c_configure_multisynth(
* ...
* 7 means divide by 128
*/
const uint_fast8_t register_number = 42 + (ms_number * 8);
uint8_t data[] = {
register_number,
(p3 >> 8) & 0xFF,
(p3 >> 0) & 0xFF,
(r_div << 4) | (0 << 2) | ((p1 >> 16) & 0x3),
(p1 >> 8) & 0xFF,
(p1 >> 0) & 0xFF,
(((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0),
(p2 >> 8) & 0xFF,
(p2 >> 0) & 0xFF};
si5351c_write(drv, data, sizeof(data));
set_MS_P1(drv, ms_number, p1);
set_MS_P2(drv, ms_number, p2);
set_MS_P3(drv, ms_number, p3);
set_R_DIV(drv, ms_number, r_div);
si5351c_regs_commit(drv);
}
void si5351c_configure_clock_control(si5351c_driver_t* const drv)
{
uint8_t clkout_ctrl;
si5351c_clk_ctrl_t clkout = {
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_8MA,
};
if (clkout_enabled) {
clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA);
} else {
clkout_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE;
si5351c_clk_ctrl_t powered_down = {
.power_down = true,
.mode = SI5351C_MODE_INT,
};
if (!clkout_enabled) {
clkout = powered_down;
}
si5351c_clk_ctrl_t clk[8];
/* Clock to CPU is deactivated as it is not used and creates noise */
/* External clock output is kept in current state */
uint8_t data[] = {
16,
SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA),
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) | SI5351C_CLK_INV,
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA),
clkout_ctrl,
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV,
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA),
SI5351C_CLK_POWERDOWN |
SI5351C_CLK_INT_MODE, /* not connected, but: PLL A int mode */
SI5351C_CLK_POWERDOWN |
SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */
/* CLK0: MAX5864/CPLD */
clk[0] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_8MA,
};
/* CLK1: CPLD */
clk[1] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_0_4,
.drive = SI5351C_DRIVE_2MA,
.invert = true,
};
/* CLK2: SGPIO */
clk[2] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_0_4,
.drive = SI5351C_DRIVE_2MA,
};
/* CLK3: CLKOUT */
clk[3] = clkout;
/* CLK4: RFFC5072 (MAX2837 on rad1o) */
clk[4] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_6MA,
.invert = true,
};
/* CLK5: MAX2837 (MAX2871 on rad1o) */
clk[5] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
/* CLK6: none */
clk[6] = powered_down;
/* CLK7: LPC43xx */
clk[7] = powered_down;
#ifdef IS_H1_R9
if (IS_H1_R9) {
data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA);
data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
data[3] = clkout_ctrl;
data[4] = SI5351C_CLK_POWERDOWN;
data[5] = SI5351C_CLK_POWERDOWN;
data[6] = SI5351C_CLK_POWERDOWN;
/* CLK0: MAX5864/CPLD/SGPIO (sample clocks) */
clk[0] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_6MA,
};
/* CLK1: RFFC5072/MAX2839 */
clk[1] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
/* CLK2: CLKOUT/LPC4320 */
clk[2] = clkout;
/* Other outputs not present on Si5351A */
clk[3] = powered_down;
clk[4] = powered_down;
clk[5] = powered_down;
clk[6] = powered_down;
clk[7] = powered_down;
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
/* CLK0: AFE_CLK */
data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
clk[0] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
/* CLK1: SCT_CLK and FPGA_CLK */
data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA);
clk[1] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_2MA,
};
/* CLK3: CLKOUT */
clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA);
clkout.pll = SI5351C_PLL_B;
clk[3] = clkout;
/* CLK4: XCVR_CLK */
data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV;
data[6] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
clk[4] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_B,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
.invert = true,
};
/* CLK5: MIX_CLK */
clk[5] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_B,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
/* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */
data[3] = SI5351C_CLK_FRAC_MODE |
SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA);
clk[2] = (si5351c_clk_ctrl_t){
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_2MA,
};
} else {
/* CLK2: MCU_CLK */
data[3] = SI5351C_CLK_POWERDOWN;
clk[2] = powered_down;
}
}
#endif
si5351c_write(drv, data, sizeof(data));
}
#define SI5351C_CLK_ENABLE(x) (0 << x)
#define SI5351C_CLK_DISABLE(x) (1 << x)
#define SI5351C_REG_OUTPUT_EN (3)
for (int i = 0; i < 8; i++) {
set_CLK_PDN(drv, i, clk[i].power_down);
set_MS_INT(drv, i, clk[i].mode);
set_MS_SRC(drv, i, clk[i].pll);
set_CLK_SRC(drv, i, clk[i].source);
set_CLK_IDRV(drv, i, clk[i].drive);
set_CLK_INV(drv, i, clk[i].invert);
}
si5351c_regs_commit(drv);
}
void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
{
@ -341,46 +423,61 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
/* 3: External clock output is deactivated by default */
uint8_t clkout = 3;
uint8_t value = 0;
bool enable[8];
#ifdef IS_PRALINE
if (IS_PRALINE) {
value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) |
SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7);
enable[0] = true;
enable[1] = true;
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
/* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */
value |= SI5351C_CLK_ENABLE(2);
enable[2] = true;
} else {
value |= SI5351C_CLK_DISABLE(2);
enable[2] = false;
}
enable[3] = false;
enable[4] = true;
enable[5] = true;
enable[6] = false;
enable[7] = false;
}
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) |
SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) |
SI5351C_CLK_DISABLE(7);
enable[0] = true;
enable[1] = true;
enable[2] = true;
enable[3] = false;
enable[4] = true;
enable[5] = true;
enable[6] = false;
enable[7] = false;
/* HackRF One r9 has only three clock generator outputs. */
#ifdef IS_H1_R9
if (IS_H1_R9) {
clkout = 2;
value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) |
SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) |
SI5351C_CLK_DISABLE(7);
enable[0] = true;
enable[1] = true;
enable[3] = false;
enable[4] = false;
enable[5] = false;
enable[6] = false;
enable[7] = false;
}
#endif
}
#endif
enable[clkout] = clkout_enabled;
value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) :
SI5351C_CLK_DISABLE(clkout);
uint8_t data[] = {SI5351C_REG_OUTPUT_EN, value};
si5351c_write(drv, data, sizeof(data));
outputs_disabled = value;
for (int i = 0; i < 8; i++) {
set_CLK_OEB(
drv,
i,
enable[i] ? SI5351C_OUTPUT_ENABLE : SI5351C_OUTPUT_DISABLE);
}
si5351c_regs_commit(drv);
#ifdef IS_H1_R9
if (IS_H1_R9) {
@ -399,25 +496,13 @@ void si5351c_set_int_mode(
const uint_fast8_t ms_number,
const uint_fast8_t on)
{
uint8_t data[] = {16, 0};
if (ms_number < 8) {
data[0] = 16 + ms_number;
data[1] = si5351c_read_single(drv, data[0]);
if (on) {
data[1] |= SI5351C_CLK_INT_MODE;
} else {
data[1] &= ~(SI5351C_CLK_INT_MODE);
}
si5351c_write(drv, data, 2);
}
set_MS_INT(drv, ms_number, on);
si5351c_regs_commit(drv);
}
void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source)
void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input)
{
if (source == active_clock_source) {
if (input_initialized && input == active_input) {
return;
}
si5351c_disable_all_outputs(drv);
@ -427,8 +512,8 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source
* HackRF One r9 always uses PLL A on the XTAL input
* but externally switches that input to CLKIN.
*/
si5351c_configure_pll_sources(drv, PLL_SOURCE_XTAL);
if (source == PLL_SOURCE_CLKIN) {
si5351c_configure_inputs(drv, SI5351C_INPUT_XTAL);
if (input == SI5351C_INPUT_CLKIN) {
gpio_set(platform_gpio()->h1r9_clkin_en);
} else {
gpio_clear(platform_gpio()->h1r9_clkin_en);
@ -437,12 +522,13 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
si5351c_configure_pll_sources(drv, source);
si5351c_configure_inputs(drv, input);
}
#endif
si5351c_configure_pll_multisynth(drv, source);
active_clock_source = source;
si5351c_reset_pll(drv, SI5351C_PLL_BOTH);
si5351c_configure_pll_multisynth(drv, input);
active_input = input;
input_initialized = true;
si5351c_reset_plls(drv, SI5351C_PLL_MASK_BOTH);
}
bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
@ -451,7 +537,9 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
uint32_t f = clkin_frequency();
return (f > 9000000) && (f < 11000000);
} else {
return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0;
si5351c_read_single(drv, LOS_CLKIN);
bool los_clkin = get_LOS_CLKIN(drv);
return !los_clkin;
}
}
@ -475,7 +563,8 @@ void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable)
void si5351c_init(si5351c_driver_t* const drv)
{
/* Read revision ID */
selftest.si5351_rev_id = si5351c_read_single(drv, 0) & SI5351C_REVID;
si5351c_read_single(drv, REVID);
selftest.si5351_rev_id = get_REVID(drv);
/* Read back interrupt status mask register, flip the mask bits and verify. */
uint8_t int_mask = si5351c_read_single(drv, 2);
@ -494,6 +583,17 @@ void si5351c_init(si5351c_driver_t* const drv)
selftest.report.pass = false;
}
/* Cache all current register values. */
uint8_t data_tx[] = {0};
i2c_bus_transfer(
drv->bus,
drv->i2c_address,
data_tx,
1,
drv->regs,
sizeof(drv->regs));
memset(drv->regs_dirty, 0, sizeof(drv->regs_dirty));
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
@ -529,8 +629,6 @@ void si5351c_set_phase(
const uint8_t ms_number,
const uint8_t offset)
{
const uint8_t address = 165 + ms_number;
if (ms_number < 8) {
si5351c_write_single(drv, address, offset & 0x7f);
}
set_CLK_PHOFF(drv, ms_number, offset);
si5351c_regs_commit(drv);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
*
* This file is part of HackRF.
@ -28,65 +28,93 @@ extern "C" {
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "i2c_bus.h"
#include "i2c_lpc.h"
#define SI_INTDIV(x) (x * 128 - 512)
#define SI5351C_CLK_POWERDOWN (1 << 7)
#define SI5351C_CLK_INT_MODE (1 << 6)
#define SI5351C_CLK_FRAC_MODE (0 << 6)
#define SI5351C_CLK_INV (1 << 4)
#define SI5351C_CLK_SRC(x) (x << 2)
#define SI5351C_CLK_SRC_XTAL 0
#define SI5351C_CLK_SRC_CLKIN 1
#define SI5351C_CLK_SRC_MULTISYNTH_0_4 2
#define SI5351C_CLK_SRC_MULTISYNTH_SELF 3
#define SI5351C_CLK_IDRV(x) (x << 0)
#define SI5351C_CLK_IDRV_2MA 0
#define SI5351C_CLK_IDRV_4MA 1
#define SI5351C_CLK_IDRV_6MA 2
#define SI5351C_CLK_IDRV_8MA 3
#define SI5351C_LOS (1 << 4)
#define SI5351C_REVID 0x03
#define SI5351C_CACHED_REGS 188
typedef enum {
SI5351C_PLL_A = 1,
SI5351C_PLL_B = 2,
SI5351C_PLL_BOTH = 3,
SI5351C_MODE_FRAC = 0,
SI5351C_MODE_INT = 1,
} si5351c_mode_t;
typedef enum {
SI5351C_SRC_XTAL = 0,
SI5351C_SRC_CLKIN = 1,
SI5351C_SRC_MULTISYNTH_0_4 = 2,
SI5351C_SRC_MULTISYNTH_SELF = 3,
} si5351c_src_t;
typedef enum {
SI5351C_DRIVE_2MA = 0,
SI5351C_DRIVE_4MA = 1,
SI5351C_DRIVE_6MA = 2,
SI5351C_DRIVE_8MA = 3,
} si5351c_drive_t;
typedef enum {
SI5351C_PLL_A = 0,
SI5351C_PLL_B = 1,
} si5351c_pll_t;
#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 4)
typedef enum {
SI5351C_PLL_MASK_A = 1,
SI5351C_PLL_MASK_B = 2,
SI5351C_PLL_MASK_BOTH = 3,
} si5351c_pll_mask_t;
enum pll_sources {
PLL_SOURCE_UNINITIALIZED = -1,
PLL_SOURCE_XTAL = 0,
PLL_SOURCE_CLKIN = 1,
};
typedef enum {
SI5351C_XTAL_6PF = 1,
SI5351C_XTAL_8PF = 2,
SI5351C_XTAL_10PF = 3,
} si5351c_xtal_t;
typedef enum {
SI5351C_OUTPUT_ENABLE = 0,
SI5351C_OUTPUT_DISABLE = 1,
} si5351c_output_t;
typedef enum {
SI5351C_DIV_1 = 0,
SI5351C_DIV_2 = 1,
SI5351C_DIV_4 = 2,
SI5351C_DIV_8 = 3,
} si5351c_div_t;
typedef enum {
SI5351C_INPUT_XTAL = 0,
SI5351C_INPUT_CLKIN = 1,
} si5351c_input_t;
typedef struct {
i2c_bus_t* const bus;
uint8_t i2c_address;
uint8_t regs[SI5351C_CACHED_REGS];
uint32_t regs_dirty[(SI5351C_CACHED_REGS + 31) / 32];
} si5351c_driver_t;
typedef struct {
bool power_down;
si5351c_mode_t mode;
si5351c_pll_t pll;
si5351c_src_t source;
si5351c_drive_t drive;
bool invert;
} si5351c_clk_ctrl_t;
void si5351c_disable_all_outputs(si5351c_driver_t* const drv);
void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv);
void si5351c_power_down_all_clocks(si5351c_driver_t* const drv);
void si5351c_set_crystal_configuration(si5351c_driver_t* const drv);
void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv);
void si5351c_configure_pll_sources(
si5351c_driver_t* const drv,
const enum pll_sources source);
void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input);
void si5351c_configure_pll_multisynth(
si5351c_driver_t* const drv,
const enum pll_sources source);
void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll);
const si5351c_input_t input);
void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask);
void si5351c_configure_multisynth(
si5351c_driver_t* const drv,
const uint_fast8_t ms_number,
@ -100,15 +128,11 @@ void si5351c_set_int_mode(
si5351c_driver_t* const drv,
const uint_fast8_t ms_number,
const uint_fast8_t on);
void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source);
void si5351c_change_input(si5351c_driver_t* const drv, const si5351c_input_t input);
bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv);
void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val);
uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg);
void si5351c_write(
si5351c_driver_t* const drv,
const uint8_t* const data,
const size_t data_count);
void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable);
void si5351c_init(si5351c_driver_t* const drv);
void si5351c_set_phase(

View file

@ -0,0 +1,494 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
static inline void si5351c_reg_set_clean(si5351c_driver_t* drv, int reg)
{
drv->regs_dirty[reg / 32] &= ~(1 << (reg % 32));
}
static inline void si5351c_reg_set_dirty(si5351c_driver_t* drv, int reg)
{
drv->regs_dirty[reg / 32] |= (1 << (reg % 32));
}
static inline bool si5351c_reg_is_dirty(si5351c_driver_t* drv, int reg)
{
return (drv->regs_dirty[reg / 32] & (1 << (reg % 32))) ? true : false;
}
/* Generate static inline accessors that operate on the global
* regs. Done this way to (1) allow defs to be scraped out and used
* elsewhere, e.g. in scripts, (2) to avoid dealing with endian
* (structs). This may be used in firmware, or on host predefined
* register loads. */
/* On set_, register is always set dirty, even if nothing
* changed. This makes sure that writes that have side effects,
* e.g. frequency setting, are not skipped. */
/* n=name, r=regnum, o=offset (bits from LSB) of LSB of field,
* l=length (bits) */
#define __MREG__(n,r,o,l) \
static inline uint8_t get_##n(si5351c_driver_t* const _d) { \
return (_d->regs[r] >> o) & ((1L<<l)-1); \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint8_t v) { \
_d->regs[r] &= (uint8_t)(~(((1L<<l)-1)<<o)); \
_d->regs[r] |= (uint8_t)(((v&((1L<<l)-1))<<o)); \
si5351c_reg_set_dirty(_d, r); \
} \
const uint8_t n = r;
/* Generate accessors for numeric fields split across multiple registers */
/* n = name */
#define __2PART__(n) \
static inline uint16_t get_##n(si5351c_driver_t* const _d) { \
return (get_##n##_TOP(_d) << 8) | get_##n##_BOT(_d); \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint16_t v) { \
set_##n##_TOP(_d, v >> 8); \
set_##n##_BOT(_d, v); \
}
#define __3PART__(n) \
static inline uint32_t get_##n(si5351c_driver_t* const _d) { \
return (get_##n##_TOP(_d) << 16) | (get_##n##_MID(_d) << 8) | get_##n##_BOT(_d); \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint32_t v) { \
set_##n##_TOP(_d, v >> 16); \
set_##n##_MID(_d, v >> 8); \
set_##n##_BOT(_d, v); \
}
/* Generate accessors for per-PLL and per-multisynth registers */
/* n=name, p=prefix, s=suffix */
#define __PER_PLL__(n, p, s) \
static inline uint32_t get_##n(si5351c_driver_t* const _d, si5351c_pll_t pll) { \
return (pll == SI5351C_PLL_A) ? get_##p##A##s(_d) : get_##p##B##s(_d); \
} \
static inline void set_##n(si5351c_driver_t* const _d, si5351c_pll_t pll, uint32_t v) { \
if (pll == SI5351C_PLL_A) { \
set_##p##A##s(_d, v); \
} else { \
set_##p##B##s(_d, v); \
} \
}
#define __PER_MS__(n, p, s) \
static inline uint32_t get_##n(si5351c_driver_t* const _d, uint8_t i) { \
switch (i) { \
case 0: return get_##p##0##s(_d); \
case 1: return get_##p##1##s(_d); \
case 2: return get_##p##2##s(_d); \
case 3: return get_##p##3##s(_d); \
case 4: return get_##p##4##s(_d); \
case 5: return get_##p##5##s(_d); \
case 6: return get_##p##6##s(_d); \
case 7: return get_##p##7##s(_d); \
default: return 0; \
} \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \
switch (i) { \
case 0: set_##p##0##s(_d, v); return; \
case 1: set_##p##1##s(_d, v); return; \
case 2: set_##p##2##s(_d, v); return; \
case 3: set_##p##3##s(_d, v); return; \
case 4: set_##p##4##s(_d, v); return; \
case 5: set_##p##5##s(_d, v); return; \
case 6: set_##p##6##s(_d, v); return; \
case 7: set_##p##7##s(_d, v); return; \
} \
} \
static inline void set_all_##n(si5351c_driver_t* const _d, uint32_t v) { \
set_##p##0##s(_d, v); \
set_##p##1##s(_d, v); \
set_##p##2##s(_d, v); \
set_##p##3##s(_d, v); \
set_##p##4##s(_d, v); \
set_##p##5##s(_d, v); \
set_##p##6##s(_d, v); \
set_##p##7##s(_d, v); \
}
#define __PER_FRAC_MS__(n, p, s) \
static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \
switch (i) { \
case 0: set_##p##0##s(_d, v); return; \
case 1: set_##p##1##s(_d, v); return; \
case 2: set_##p##2##s(_d, v); return; \
case 3: set_##p##3##s(_d, v); return; \
case 4: set_##p##4##s(_d, v); return; \
case 5: set_##p##5##s(_d, v); return; \
} \
}
/* Register 0: Device Status */
__MREG__(SYS_INIT, 0, 7, 1)
__MREG__(LOL_B, 0, 6, 1)
__MREG__(LOL_A, 0, 5, 1)
__MREG__(LOS_CLKIN, 0, 4, 1)
__MREG__(LOS_XTAL, 0, 3, 1)
__MREG__(REVID, 0, 0, 2)
/* Register 1: Interrupt Status Sticky */
__MREG__(SYS_INIT_STKY, 1, 7, 1)
__MREG__(LOL_B_STKY, 1, 6, 1)
__MREG__(LOL_A_STKY, 1, 5, 1)
__MREG__(LOS_CLKIN_STKY, 1, 4, 1)
__MREG__(LOS_XTAL_STKY, 1, 3, 1)
/* Register 2: Interrupt Status Mask */
__MREG__(SYS_INIT_MASK, 2, 7, 1)
__MREG__(LOL_B_MASK, 2, 6, 1)
__MREG__(LOL_A_MASK, 2, 5, 1)
__MREG__(LOS_CLKIN_MASK, 2, 4, 1)
__MREG__(LOS_XTAL_MASK, 2, 3, 1)
/* Register 3: Output Enable Control */
__MREG__(CLK7_OEB, 3, 7, 1)
__MREG__(CLK6_OEB, 3, 6, 1)
__MREG__(CLK5_OEB, 3, 5, 1)
__MREG__(CLK4_OEB, 3, 4, 1)
__MREG__(CLK3_OEB, 3, 3, 1)
__MREG__(CLK2_OEB, 3, 2, 1)
__MREG__(CLK1_OEB, 3, 1, 1)
__MREG__(CLK0_OEB, 3, 0, 1)
/* Register 4: OEB Pin Enable Control */
__MREG__(OEB_CLK7, 4, 7, 1)
__MREG__(OEB_CLK6, 4, 6, 1)
__MREG__(OEB_CLK5, 4, 5, 1)
__MREG__(OEB_CLK4, 4, 4, 1)
__MREG__(OEB_CLK3, 4, 3, 1)
__MREG__(OEB_CLK2, 4, 2, 1)
__MREG__(OEB_CLK1, 4, 1, 1)
__MREG__(OEB_CLK0, 4, 0, 1)
/* Register 9: OEB Pin Enable Control Mask */
__MREG__(OEB_MASK7, 9, 7, 1)
__MREG__(OEB_MASK6, 9, 6, 1)
__MREG__(OEB_MASK5, 9, 5, 1)
__MREG__(OEB_MASK4, 9, 4, 1)
__MREG__(OEB_MASK3, 9, 3, 1)
__MREG__(OEB_MASK2, 9, 2, 1)
__MREG__(OEB_MASK1, 9, 1, 1)
__MREG__(OEB_MASK0, 9, 0, 1)
/* Register 15: PLL Input Source */
__MREG__(CLKIN_DIV, 15, 6, 2)
__MREG__(PLLB_SRC, 15, 3, 1)
__MREG__(PLLA_SRC, 15, 2, 1)
/* Register 16: CLK0 Control */
__MREG__(CLK0_PDN, 16, 7, 1)
__MREG__(MS0_INT, 16, 6, 1)
__MREG__(MS0_SRC, 16, 5, 1)
__MREG__(CLK0_INV, 16, 4, 1)
__MREG__(CLK0_SRC, 16, 2, 2)
__MREG__(CLK0_IDRV, 16, 0, 2)
/* Register 17: CLK1 Control */
__MREG__(CLK1_PDN, 17, 7, 1)
__MREG__(MS1_INT, 17, 6, 1)
__MREG__(MS1_SRC, 17, 5, 1)
__MREG__(CLK1_INV, 17, 4, 1)
__MREG__(CLK1_SRC, 17, 2, 2)
__MREG__(CLK1_IDRV, 17, 0, 2)
/* Register 18: CLK2 Control */
__MREG__(CLK2_PDN, 18, 7, 1)
__MREG__(MS2_INT, 18, 6, 1)
__MREG__(MS2_SRC, 18, 5, 1)
__MREG__(CLK2_INV, 18, 4, 1)
__MREG__(CLK2_SRC, 18, 2, 2)
__MREG__(CLK2_IDRV, 18, 0, 2)
/* Register 19: CLK3 Control */
__MREG__(CLK3_PDN, 19, 7, 1)
__MREG__(MS3_INT, 19, 6, 1)
__MREG__(MS3_SRC, 19, 5, 1)
__MREG__(CLK3_INV, 19, 4, 1)
__MREG__(CLK3_SRC, 19, 2, 2)
__MREG__(CLK3_IDRV, 19, 0, 2)
/* Register 20: CLK4 Control */
__MREG__(CLK4_PDN, 20, 7, 1)
__MREG__(MS4_INT, 20, 6, 1)
__MREG__(MS4_SRC, 20, 5, 1)
__MREG__(CLK4_INV, 20, 4, 1)
__MREG__(CLK4_SRC, 20, 2, 2)
__MREG__(CLK4_IDRV, 20, 0, 2)
/* Register 21: CLK5 Control */
__MREG__(CLK5_PDN, 21, 7, 1)
__MREG__(MS5_INT, 21, 6, 1)
__MREG__(MS5_SRC, 21, 5, 1)
__MREG__(CLK5_INV, 21, 4, 1)
__MREG__(CLK5_SRC, 21, 2, 2)
__MREG__(CLK5_IDRV, 21, 0, 2)
/* Register 22: CLK6 Control */
__MREG__(CLK6_PDN, 22, 7, 1)
__MREG__(FBA_INT, 22, 6, 1)
__MREG__(MS6_SRC, 22, 5, 1)
__MREG__(CLK6_INV, 22, 4, 1)
__MREG__(CLK6_SRC, 22, 2, 2)
__MREG__(CLK6_IDRV, 22, 0, 2)
/* Register 23: CLK7 Control */
__MREG__(CLK7_PDN, 23, 7, 1)
__MREG__(FBB_INT, 23, 6, 1)
__MREG__(MS7_SRC, 23, 5, 1)
__MREG__(CLK7_INV, 23, 4, 1)
__MREG__(CLK7_SRC, 23, 2, 2)
__MREG__(CLK7_IDRV, 23, 0, 2)
/* Register 24: CLK0-CLK3 Disable State */
__MREG__(CLK3_DIS_STATE, 24, 6, 2)
__MREG__(CLK2_DIS_STATE, 24, 4, 2)
__MREG__(CLK1_DIS_STATE, 24, 2, 2)
__MREG__(CLK0_DIS_STATE, 24, 0, 2)
/* Register 25: CLK4-CLK7 Disable State */
__MREG__(CLK7_DIS_STATE, 25, 6, 2)
__MREG__(CLK6_DIS_STATE, 25, 4, 2)
__MREG__(CLK5_DIS_STATE, 25, 2, 2)
__MREG__(CLK4_DIS_STATE, 25, 0, 2)
/* Registers 26-33: Multisynth NA Parameters */
__MREG__(MSNA_P3_MID, 26, 0, 8)
__MREG__(MSNA_P3_BOT, 27, 0, 8)
__MREG__(MSNA_P1_TOP, 28, 0, 2)
__MREG__(MSNA_P1_MID, 29, 0, 8)
__MREG__(MSNA_P1_BOT, 30, 0, 8)
__MREG__(MSNA_P3_TOP, 31, 4, 4)
__MREG__(MSNA_P2_TOP, 31, 0, 4)
__MREG__(MSNA_P2_MID, 32, 0, 8)
__MREG__(MSNA_P2_BOT, 33, 0, 8)
/* Registers 34-41: Multisynth NB Parameters */
__MREG__(MSNB_P3_MID, 34, 0, 8)
__MREG__(MSNB_P3_BOT, 35, 0, 8)
__MREG__(MSNB_P1_TOP, 36, 0, 2)
__MREG__(MSNB_P1_MID, 37, 0, 8)
__MREG__(MSNB_P1_BOT, 38, 0, 8)
__MREG__(MSNB_P3_TOP, 39, 4, 4)
__MREG__(MSNB_P2_TOP, 39, 0, 4)
__MREG__(MSNB_P2_MID, 40, 0, 8)
__MREG__(MSNB_P2_BOT, 41, 0, 8)
/* Registers 42-49: Multisynth 0 Parameters */
__MREG__(MS0_P3_MID, 42, 0, 8)
__MREG__(MS0_P3_BOT, 43, 0, 8)
__MREG__(R0_DIV, 44, 4, 3)
__MREG__(MS0_DIVBY4, 44, 2, 2)
__MREG__(MS0_P1_TOP, 44, 0, 2)
__MREG__(MS0_P1_MID, 45, 0, 8)
__MREG__(MS0_P1_BOT, 46, 0, 8)
__MREG__(MS0_P3_TOP, 47, 4, 4)
__MREG__(MS0_P2_TOP, 47, 0, 4)
__MREG__(MS0_P2_MID, 48, 0, 8)
__MREG__(MS0_P2_BOT, 49, 0, 8)
/* Registers 50-57: Multisynth 1 Parameters */
__MREG__(MS1_P3_MID, 50, 0, 8)
__MREG__(MS1_P3_BOT, 51, 0, 8)
__MREG__(R1_DIV, 52, 4, 3)
__MREG__(MS1_DIVBY4, 52, 2, 2)
__MREG__(MS1_P1_TOP, 52, 0, 2)
__MREG__(MS1_P1_MID, 53, 0, 8)
__MREG__(MS1_P1_BOT, 54, 0, 8)
__MREG__(MS1_P3_TOP, 55, 4, 4)
__MREG__(MS1_P2_TOP, 55, 0, 4)
__MREG__(MS1_P2_MID, 56, 0, 8)
__MREG__(MS1_P2_BOT, 57, 0, 8)
/* Registers 58-65: Multisynth 2 Parameters */
__MREG__(MS2_P3_MID, 58, 0, 8)
__MREG__(MS2_P3_BOT, 59, 0, 8)
__MREG__(R2_DIV, 60, 4, 3)
__MREG__(MS2_DIVBY4, 60, 2, 2)
__MREG__(MS2_P1_TOP, 60, 0, 2)
__MREG__(MS2_P1_MID, 61, 0, 8)
__MREG__(MS2_P1_BOT, 62, 0, 8)
__MREG__(MS2_P3_TOP, 63, 4, 4)
__MREG__(MS2_P2_TOP, 63, 0, 4)
__MREG__(MS2_P2_MID, 64, 0, 8)
__MREG__(MS2_P2_BOT, 65, 0, 8)
/* Registers 66-73: Multisynth 3 Parameters */
__MREG__(MS3_P3_MID, 66, 0, 8)
__MREG__(MS3_P3_BOT, 67, 0, 8)
__MREG__(R3_DIV, 68, 4, 3)
__MREG__(MS3_DIVBY4, 68, 2, 2)
__MREG__(MS3_P1_TOP, 68, 0, 2)
__MREG__(MS3_P1_MID, 69, 0, 8)
__MREG__(MS3_P1_BOT, 70, 0, 8)
__MREG__(MS3_P3_TOP, 71, 4, 4)
__MREG__(MS3_P2_TOP, 71, 0, 4)
__MREG__(MS3_P2_MID, 72, 0, 8)
__MREG__(MS3_P2_BOT, 73, 0, 8)
/* Registers 74-81: Multisynth 4 Parameters */
__MREG__(MS4_P3_MID, 74, 0, 8)
__MREG__(MS4_P3_BOT, 75, 0, 8)
__MREG__(R4_DIV, 76, 4, 3)
__MREG__(MS4_DIVBY4, 76, 2, 2)
__MREG__(MS4_P1_TOP, 76, 0, 2)
__MREG__(MS4_P1_MID, 77, 0, 8)
__MREG__(MS4_P1_BOT, 78, 0, 8)
__MREG__(MS4_P3_TOP, 79, 4, 4)
__MREG__(MS4_P2_TOP, 79, 0, 4)
__MREG__(MS4_P2_MID, 80, 0, 8)
__MREG__(MS4_P2_BOT, 81, 0, 8)
/* Registers 82-89: Multisynth 5 Parameters */
__MREG__(MS5_P3_MID, 82, 0, 8)
__MREG__(MS5_P3_BOT, 83, 0, 8)
__MREG__(R5_DIV, 84, 4, 3)
__MREG__(MS5_DIVBY4, 84, 2, 2)
__MREG__(MS5_P1_TOP, 84, 0, 2)
__MREG__(MS5_P1_MID, 85, 0, 8)
__MREG__(MS5_P1_BOT, 86, 0, 8)
__MREG__(MS5_P3_TOP, 87, 4, 4)
__MREG__(MS5_P2_TOP, 87, 0, 4)
__MREG__(MS5_P2_MID, 88, 0, 8)
__MREG__(MS5_P2_BOT, 89, 0, 8)
/* Register 90: Multisynth 6 Parameters */
__MREG__(MS6_P1, 90, 0, 8)
/* Register 91: Multisynth 7 Parameters */
__MREG__(MS7_P1, 91, 0, 8)
/* Register 92: Clock 6 and 7 Output Divider */
__MREG__(R7_DIV, 92, 4, 3)
__MREG__(R6_DIV, 92, 0, 3)
/* Registers 149-161: Spread Spectrum Parameters */
__MREG__(SSC_EN, 149, 7, 1)
__MREG__(SSDN_P2_TOP, 149, 0, 7)
__MREG__(SSDN_P2_BOT, 150, 0, 8)
__MREG__(SSC_MODE, 151, 7, 1)
__MREG__(SSDN_P3_TOP, 151, 0, 7)
__MREG__(SSDN_P3_BOT, 152, 0, 8)
__MREG__(SSDN_P1_BOT, 153, 0, 8)
__MREG__(SSUDP_TOP, 154, 4, 4)
__MREG__(SSDN_P1_TOP, 154, 0, 4)
__MREG__(SSUDP_BOT, 155, 0, 8)
__MREG__(SSUP_P2_TOP, 156, 0, 7)
__MREG__(SSUP_P2_BOT, 157, 0, 8)
__MREG__(SSUP_P3_TOP, 158, 0, 8)
__MREG__(SSUP_P3_BOT, 159, 0, 8)
__MREG__(SSUP_P1_BOT, 160, 0, 8)
__MREG__(SS_NCLK, 161, 4, 4)
__MREG__(SSUP_P1_TOP, 161, 0, 4)
/* Registers 162-163: VCXO Parameter */
__MREG__(VCXO_PARAM_BOT, 162, 0, 8)
__MREG__(VCXO_PARAM_MID, 163, 0, 8)
__MREG__(VCXO_PARAM_TOP, 164, 0, 6)
/* Registers 165-170: CLK0-CLK5 Initial Phase Offsets */
__MREG__(CLK0_PHOFF, 165, 0, 7)
__MREG__(CLK1_PHOFF, 166, 0, 7)
__MREG__(CLK2_PHOFF, 167, 0, 7)
__MREG__(CLK3_PHOFF, 168, 0, 7)
__MREG__(CLK4_PHOFF, 169, 0, 7)
__MREG__(CLK5_PHOFF, 170, 0, 7)
/* Register 177: PLL Reset */
__MREG__(PLLB_RST, 177, 7, 1)
__MREG__(PLLA_RST, 177, 5, 1)
/* Register 183: Crystal Internal Load Capacitance */
__MREG__(XTAL_CL, 183, 6, 2)
/* Register 187: Fanout Enable */
__MREG__(CLKIN_FANOUT_EN, 187, 7, 1)
__MREG__(XO_FANOUT_EN, 187, 6, 1)
__MREG__(MS_FANOUT_EN, 187, 4, 1)
/* 3-part parameters */
__3PART__(MSNA_P1)
__3PART__(MSNA_P2)
__3PART__(MSNA_P3)
__3PART__(MSNB_P1)
__3PART__(MSNB_P2)
__3PART__(MSNB_P3)
__3PART__(MS0_P1)
__3PART__(MS0_P2)
__3PART__(MS0_P3)
__3PART__(MS1_P1)
__3PART__(MS1_P2)
__3PART__(MS1_P3)
__3PART__(MS2_P1)
__3PART__(MS2_P2)
__3PART__(MS2_P3)
__3PART__(MS3_P1)
__3PART__(MS3_P2)
__3PART__(MS3_P3)
__3PART__(MS4_P1)
__3PART__(MS4_P2)
__3PART__(MS4_P3)
__3PART__(MS5_P1)
__3PART__(MS5_P2)
__3PART__(MS5_P3)
/* 2-part parameters */
__2PART__(SSDN_P1)
__2PART__(SSDN_P2)
__2PART__(SSDN_P3)
__2PART__(SSUP_P1)
__2PART__(SSUP_P2)
__2PART__(SSUP_P3)
__2PART__(SSUDP)
/* Per-PLL register groups */
__PER_PLL__(LOL, LOL_, )
__PER_PLL__(LOL_STKY, LOL_, _STKY)
__PER_PLL__(LOL_MASK, LOL_, _MASK)
__PER_PLL__(PLL_SRC, PLL, _SRC)
__PER_PLL__(MSN_P1, MSN, _P1)
__PER_PLL__(MSN_P2, MSN, _P2)
__PER_PLL__(MSN_P3, MSN, _P3)
__PER_PLL__(PLL_RST, PLL, _RST)
/* Per-multisynth register groups */
__PER_MS__(CLK_OEB, CLK, _OEB)
__PER_MS__(OEB_CLK, OEB_CLK, )
__PER_MS__(OEB_MASK, OEB_MASK, )
__PER_MS__(CLK_PDN, CLK, _PDN)
__PER_MS__(MS_SRC, MS, _SRC)
__PER_MS__(CLK_INV, CLK, _INV)
__PER_MS__(CLK_SRC, CLK, _SRC)
__PER_MS__(CLK_IDRV, CLK, _IDRV)
__PER_MS__(CLK_DIS_STATE, CLK, _DIS_STATE)
__PER_MS__(R_DIV, R, _DIV)
__PER_MS__(MS_P1, MS, _P1)
__PER_FRAC_MS__(MS_INT, MS, _INT)
__PER_FRAC_MS__(MS_P2, MS, _P2)
__PER_FRAC_MS__(MS_P3, MS, _P3)
__PER_FRAC_MS__(MS_DIVBY4, MS, _DIVBY4)
__PER_FRAC_MS__(CLK_PHOFF, CLK, _PHOFF)