Skip to content

Si5351C refactoring#1747

Open
martinling wants to merge 4 commits intogreatscottgadgets:mainfrom
martinling:si5351c-defs
Open

Si5351C refactoring#1747
martinling wants to merge 4 commits intogreatscottgadgets:mainfrom
martinling:si5351c-defs

Conversation

@martinling
Copy link
Copy Markdown
Member

@martinling martinling commented May 2, 2026

This PR refactors the Si5351C driver code. The goal is to make it more maintainable and easier to add new features, by depending less on hand-written bit wrangling.

  1. Although this PR is primarily an internal refactor, I've made a couple of changes to the external API used by clock_gen.c in order to better name and distinguish different concepts and types. These can be made independently of the refactoring work so are placed first.

  2. Next, we add a new si5351c_regs.def file with register definitions and helper functions. This is similar to our other def files for other register-based parts, but with additional macros that simplify access to fields which are split across multiple registers, and for indexed access to fields which are duplicated for each multisynth, or for each PLL.

  3. Finally, the implementation in si5351.c is rewritten to take advantage of the new helpers, while retaining the same structure and external API. As with other register-based parts, we now maintain a cached copy of the registers and a regs_dirty bitmask, and flush changes in a si5351c_regs_commit() function. The commit implementation will group writes to sequences of adjacent registers, so performance should match the previous hand-written code.

@martinling martinling force-pushed the si5351c-defs branch 2 times, most recently from 15ef8db to 9fa07ca Compare May 2, 2026 22:50
@martinling martinling changed the title Si5351C Si5351C refactoring May 2, 2026
@martinling martinling force-pushed the si5351c-defs branch 5 times, most recently from d98a294 to 4d8e4ff Compare May 3, 2026 08:35
@martinling
Copy link
Copy Markdown
Member Author

This has been tested on HackRF One and Praline in the HITL CI, and also verified locally by taking diffs of hackrf_debug register dumps before and after the changes.

There is one remaining difference in my register dumps:

 [180] -> 0x00
 [181] -> 0x30
 [182] -> 0x1f
-[183] -> 0x80
+[183] -> 0x92
 [184] -> 0x60
 [185] -> 0x60
 [186] -> 0xb8

Previously, we were writing 0x80 to register 183, which sets the crystal load capacitance in the top two bits, and sets the rest of the register to zero. However, the documentation specifies that the reserved lower bits should be written as 010010:

image

The new implementation does this, because it reads the reserved bits during setup and retains them when updating the XTAL_CL field later.

@martinling martinling marked this pull request as ready for review May 3, 2026 09:02
@martinling
Copy link
Copy Markdown
Member Author

There is another difference in register dumps after this change, which happens specifically on Praline:

 [ 16] -> 0x0d
 [ 17] -> 0x0c
 [ 18] -> 0x0c
-[ 19] -> 0xc0
+[ 19] -> 0xe0
 [ 20] -> 0x7d
 [ 21] -> 0x6d
 [ 22] -> 0xc0

This corresponds to MS3_SRC changing from 0 to 1, meaning that CLKOUT is now using PLL B instead of PLL A.

This is because of what I assume is a bug in the IS_PRALINE branch in si5351c_configure_clock_control, which previously did:

/* 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);

...but never copied the new value of clkout_ctrl to the data array which was about to be sent over I2C. So the change to clkout_ctrl, which differed only in the PLL selection, never took effect.

I have assumed that the intent was to use PLL B for CLKOUT on Praline, so I now have:

/* CLK3: CLKOUT */
clkout.pll = SI5351C_PLL_B;
clk[3] = clkout;

@martinling martinling marked this pull request as draft May 3, 2026 12:10
@martinling
Copy link
Copy Markdown
Member Author

Something is still wrong with this on HackRF One r9; achieved sample rate appears to be around 14MHz (with XTAL) or 15MHz (with CLKIN) when it should be 10MHz.

After startup and a hackrf_transfer -r /dev/null run, I get the following register differences before and after this PR:

@@ -1,4 +1,4 @@
-[  0] -> 0x11
+[  0] -> 0x71
 [  1] -> 0xf8
 [  2] -> 0x03
 [  3] -> 0xfc
@@ -17,9 +17,9 @@
 [ 16] -> 0x4e
 [ 17] -> 0x0d
 [ 18] -> 0xc0
-[ 19] -> 0x80
-[ 20] -> 0x80
-[ 21] -> 0x80
+[ 19] -> 0xc0
+[ 20] -> 0xc0
+[ 21] -> 0xc0
 [ 22] -> 0xc0
 [ 23] -> 0xc0
 [ 24] -> 0x00
@@ -220,8 +220,8 @@
 [219] -> 0x00
 [220] -> 0x00
 [221] -> 0x00
-[222] -> 0x00
-[223] -> 0x40
+[222] -> 0x94
+[223] -> 0x28
 [224] -> 0x00
 [225] -> 0x00
 [226] -> 0x38

Register 0 is the status register, and is indicating loss of lock on both PLLs, which would certainly explain the wrong sample rate, but I don't know why.

Registers 19-21 should be irrelevant; these are the clock control registers for the CLK3-CLK5 outputs, which don't exist on the Si5351A, and are also flagged as powered down.

Registers 222-223 are undocumented, and listed as reserved in AN619, so I've no idea what that change means.

martinling added 4 commits May 5, 2026 17:48
There are multiple similar but different things called a "source"
or "clock source", or that are set in a register called "SRC", in the
context of the Si5351 and its usage in HackRF.

One place we can be unambiguous is that there are only two inputs to
the Si5351C itself: XTAL and CLKIN. Let's have a type for that alone,
and name it as such when we use it.

There should be no functional changes in this commit.
We're going to want a type that just identifies one PLL, and is
consistent with how the PLLs are numbered in register settings.

The current si5351c_pll_t is a bitmask which allows referring to
both PLLs. Let's make that a different type, and name things more
clearly when using it.
@martinling
Copy link
Copy Markdown
Member Author

Found the problem on r9 - just a dumb mistake in the first commit, when updating the logic controlling h1r9_clkin_en.

@martinling martinling marked this pull request as ready for review May 5, 2026 16:53
@martinling martinling requested a review from mossmann May 5, 2026 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant