Whee, now we're doing duplicate work, like I said I've been looking at Ghidra
I was wondering if I should've posted the garbage that it decompiled, haha. But yeah I figured the basics, the actual register files not so much... But after the last post I was reading machine translation of the Chinese datasheet for the clock gen, until I discovered it's all explained in AN619 of the Silicon Labs chip
https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/application-notes/AN619.pdf (Is it as simple as PLL 30 x 25 = 750MHz VCO / MS 30 = 50MHz? I'll find out!). Now that I understand the values bit better and know they're not magic, the register settings are starting to make a bit more sense to me. There's also Arduino library
https://github.com/etherkit/Si5351Arduino which I'd forgotten about for directly calculating & setting the values dynamically, so could even just use that.
I guess I shouldn't be surprised if the clocks are really distributed that way... There's an odd extra function called for the second clock, but other than that they look pretty symmetrical in code. (Function names random guessed of course).
void fpga_init(void)
{
uint *puVar1;
uint uVar2;
init_porta_0_1_output();
uVar2 = DAT_80017534;
i2c_fpga_clockgen_ch0_reset(DAT_80017534);
i2c_fpga_clockgen_ch1_reset(uVar2);
i2c_fpga_clockgen_ch1_set(1);
puVar1 = DAT_80017538;
gpio_config_pin(DAT_80017538,9,1);
Which is Ghidra being weird, because yes, both initial functions are called with the same parameter = 02FAF080h.
Then the i2c_fpga_clockgen_ch0_reset() ends in:
LAB_80026220:
if ((uVar3 <= uVar4) && (uVar4 = uVar3, uVar3 < uVar1)) {
uVar4 = uVar1;
}
uVar1 = (uint)((ulonglong)uVar4 * (ulonglong)DAT_800262f8 >> 0x37);
iVar6 = DAT_800262fc * uVar1;
uVar5 = FUN_8003bd3c(uVar4 + iVar6 * 0x40,iVar6,DAT_800262fc,
(int)((ulonglong)uVar4 * (ulonglong)DAT_800262f8));
uVar5 = FUN_8003bde8(uVar5,DAT_80026300);
FUN_8003ba60(uVar5,DAT_80026304);
uVar5 = FUN_8003bcb8();
i2c_fpga_clockgen_config_pll(0x1a,uVar1 & 0xff,uVar5,DAT_80026308);
if (uVar2 < uVar7 - 0x9c4) {
i2c_fpga_clockgen_config_ms(0x2a,4,0xc);
}
else {
i2c_fpga_clockgen_config_ms(0x2a,unaff_r6,bVar8);
}
i2c_fpga_clockgen_reg(0xb1,0x20);
ptr = DAT_8001e440;
set_gpio_pin_high(DAT_8001e440,0);
set_gpio_pin_high(ptr,1);
set_gpio_pin_low(ptr,0);
_delay(10);
set_gpio_pin_low(ptr,1);
_delay(10);
_delay(10);
i2c_fpga_clockgen_chg(0xc0);
_delay(10);
i2c_fpga_clockgen_chg(0x10);
_delay(10);
i2c_fpga_clockgen_chg(0x4f);
_delay(10);
set_gpio_pin_low(ptr,1);
_delay(10);
set_gpio_pin_low(ptr,0);
_delay(10);
set_gpio_pin_high(ptr,1);
_delay(10);
set_gpio_pin_high(ptr,0);
delay(10);
return;
So we have a LOT of weird functions (possibly more before that point) with pointers for figuring out the values. We set the PLL divider and then the MultiSynth counters with out magic values. Register 0xb1 = 177 = PLL RESET, specifically PLLA here, which makes the new parameters take force. Then, for who knows what reason, we have the register setting protocol expanded out. Register 0x10 = 16 = CLK0 Control. Maximum 8 mA drive, clock from MultiSynth 0 in "integer mode" to improve jitter.
Of note, none of this is actually going according to either the Silicon Labs or chinese datasheet. They have "disable output, power down output drivers" before divider settings and tell to use 177 = 0xAC to reset PLL.
The second clock is handled in exactly the same way, only the register addresses seem to change for the second clock. But of course THEN we get the i2c_fpga_clockgen_ch1_set() which changes the MultiSynth values based on yet other functions... And looking again, i2c_fpaga_clockgen_ch0_reset is called again in relation to the scope, but I guess i2c_fpaga_clockgen_ch0_reset and i2c_fpaga_clockgen_ch0_set are never called again after initialization.
Unknown function end:
fpga_write_cmd('G');
fpga_write_data((uchar)((*(ushort *)(iVar2 + 2) - 1) * 0x10000 >> 0x18));
fpga_write_data(*(char *)(iVar2 + 2) + 0xff);
uVar13 = DAT_8001c128;
uVar8 = *(uint *)(iVar2 + 4) * (uint)*(ushort *)(iVar2 + 2);
if (DAT_8001c124 < uVar8) {
uVar8 = DAT_8001c124;
}
if (*(uint *)(iVar2 + 4) < 10) {
*(undefined *)(iVar2 + 0xb) = 1;
if (uVar8 < 0xfa) {
uVar8 = 0xfa;
}
i2c_fpga_clockgen_ch0_reset(uVar8 * 10);
}
else {
*(undefined *)(iVar2 + 0xb) = 0;
if (uVar8 < uVar13) {
uVar8 = DAT_8001c128;
}
i2c_fpga_clockgen_ch0_reset(uVar8);
}
fpga_write_cmd('H');
fpga_write_data(*(uchar *)(iVar2 + 0xb));
if (*(int *)(iVar2 + 4) == 0) {
fpga_write_cmd('F');
uVar13 = 0;
do {
fpga_write_data('c');
uVar13 = uVar13 + 1 & 0xfffeffff;
} while (uVar13 < 1000);
return;
}
fpga_write_cmd('F');
uVar13 = 0;
do {
fpga_write_data(pbVar3[uVar13]);
uVar13 = uVar13 + 1 & 0xfffeffff;
} while (uVar13 < 1000);
return;
None of those FPGA commands are in the
https://github.com/pecostm32/FNIRSI-1013D-1014D-Hack/blob/main/FPGA%20explained/FPGA%20explained.txt document, so that's some more reverse engineering along with trying to find out what it does to the clock gen. And whoops, just re-read what you said with thought, so this must be for the clock-gen whereas the other, fixed output with extra function is indeed for the oscilloscope. No wonder I was thinking it was being changed!
It seems like I might need a crash-course in Ghidra. I'm going to try to solve these BEFORE posting, though
EDIT: Oh yeah, also want to use FEL for all the reasons, but as complained before, libusb doesn't really work over Microsoft RDP RemoteFX USB pass-through. Or it does, if the USB device is part of a composite device and the software looks for that device directly and not the composite device... but yeah, let's just say it doesn't work until fixed.
EDITn: Yes I also looked at how the Sipeed Lichee Nano with F1C100s has 16MB flash, so I guess the 1MB set aside for firmware on the SD-card is just a matter of choice and guessing it won't grow above that. It would also theoretically be possible to load different modules from SD into memory?
Si5351A ClockBuilder Pro lets me choose 200MHz + 50MHz clock even though I guess it's way beyond spec (actually looks like it's in, but needs special settings). So this is something like what I should be looking for in the code. I wonder why there's so many functions & apparent dynamicity to the setting in FNIRSI, just stupid choice/ready-made library I guess? Also note you can write i2c in consequential manner, even according to the Chinese clone datasheet, no need to give write & register address for each. And most registers default to 0x00 value after reset, so those register writes can generally be ignored (unless needed to cover a gap in the register sequence), but that's on ClockBuilder Pro.
Also, this dump shows I still really don't understand the register configuration. The math of "Fvco = 32 x 25MHz = 800MHz, CLK0 divider 4 = 200MHz, CLK1 divider 16 = 50MHz" is just as I'd expect, then in the register file... 0x00E00. WTAF
/*
* Si5351A Rev B Configuration Register Export Header File
*
* This file represents a series of Skyworks Si5351A Rev B
* register writes that can be performed to load a single configuration
* on a device. It was created by a Skyworks ClockBuilder Pro
* export tool.
*
* Part: Si5351A Rev B
* Design ID:
* Includes Pre/Post Download Control Register Writes: Yes
* Created By: ClockBuilder Pro v4.7 [2022-11-18]
* Timestamp: 2023-01-06 18:41:46 GMT+02:00
*
* A complete design report corresponding to this export is included at the end
* of this header file.
*
*/
#ifndef SI5351A_REVB_REG_CONFIG_HEADER
#define SI5351A_REVB_REG_CONFIG_HEADER
#define SI5351A_REVB_REG_CONFIG_NUM_REGS 53
typedef struct
{
unsigned int address; /* 16-bit register address */
unsigned char value; /* 8-bit register data */
} si5351a_revb_register_t;
si5351a_revb_register_t const si5351a_revb_registers[SI5351A_REVB_REG_CONFIG_NUM_REGS] =
{
{ 0x0002, 0x53 },
{ 0x0003, 0x00 },
{ 0x0004, 0x20 },
{ 0x0007, 0x00 },
{ 0x000F, 0x00 },
{ 0x0010, 0x4F },
{ 0x0011, 0x0F },
{ 0x0012, 0x8C },
{ 0x0013, 0x8C },
{ 0x0014, 0x8C },
{ 0x0015, 0x8C },
{ 0x0016, 0x8C },
{ 0x0017, 0x8C },
{ 0x0018, 0x0A },
{ 0x001A, 0x00 },
{ 0x001B, 0x01 },
{ 0x001C, 0x00 },
{ 0x001D, 0x0E },
{ 0x001E, 0x00 },
{ 0x001F, 0x00 },
{ 0x0020, 0x00 },
{ 0x0021, 0x00 },
{ 0x002A, 0x00 },
{ 0x002B, 0x01 },
{ 0x002C, 0x0C },
{ 0x002D, 0x00 },
{ 0x002E, 0x00 },
{ 0x002F, 0x00 },
{ 0x0030, 0x00 },
{ 0x0031, 0x00 },
{ 0x0032, 0x00 },
{ 0x0033, 0x01 },
{ 0x0034, 0x00 },
{ 0x0035, 0x06 },
{ 0x0036, 0x00 },
{ 0x0037, 0x00 },
{ 0x0038, 0x00 },
{ 0x0039, 0x00 },
{ 0x005A, 0x00 },
{ 0x005B, 0x00 },
{ 0x0095, 0x00 },
{ 0x0096, 0x00 },
{ 0x0097, 0x00 },
{ 0x0098, 0x00 },
{ 0x0099, 0x00 },
{ 0x009A, 0x00 },
{ 0x009B, 0x00 },
{ 0x00A2, 0x00 },
{ 0x00A3, 0x00 },
{ 0x00A4, 0x00 },
{ 0x00A5, 0x00 },
{ 0x00A6, 0x00 },
{ 0x00B7, 0x92 },
};
/*
* Design Report
*
* Overview
* ========
* Part: Si5351A
* Created By: ClockBuilder Pro v4.7 [2022-11-18]
* Timestamp: 2023-01-06 18:41:46 GMT+02:00
*
* Design Rule Check
* =================
* Errors:
* - No errors
*
* Warnings:
* - No warnings
*
* Design
* ======
* I2C Address: 0x60
*
* Inputs:
* IN0: 25 MHz
*
* Outputs:
* OUT0: 200 MHz
* Enabled LVCMOS 8 mA
* Offset 0.000 s
* OUT1: 50 MHz
* Enabled LVCMOS 8 mA
* Offset 0.000 s
* OUT2: Unused
*
* Frequency Plan
* ==============
* PLL_A:
* Enabled Features = None
* Fvco = 800 MHz
* M = 32
* Input0:
* Source = Crystal
* Source Frequency = 25 MHz
* Fpfd = 25 MHz
* Load Capacitance = Load_08pF
* Output0:
* Features = None
* Disabled State = HiZ
* R = 1 (2^0)
* Fout = 200 MHz
* N = 4
* Output1:
* Features = None
* Disabled State = HiZ
* R = 1 (2^0)
* Fout = 50 MHz
* N = 16
*
* Settings
* ========
*
* Location Setting Name Decimal Value Hex Value
* ------------ -------------- ----------------- -----------------
* 0x0002[3] XO_LOS_MASK 0 0x0
* 0x0002[4] CLK_LOS_MASK 1 0x1
* 0x0002[5] LOL_A_MASK 0 0x0
* 0x0002[6] LOL_B_MASK 1 0x1
* 0x0002[7] SYS_INIT_MASK 0 0x0
* 0x0003[7:0] CLK_OEB 0 0x00
* 0x0004[4] DIS_RESET_LOLA 0 0x0
* 0x0004[5] DIS_RESET_LOLB 1 0x1
* 0x0007[7:4] I2C_ADDR_CTRL 0 0x0
* 0x000F[2] PLLA_SRC 0 0x0
* 0x000F[3] PLLB_SRC 0 0x0
* 0x000F[4] PLLA_INSELB 0 0x0
* 0x000F[5] PLLB_INSELB 0 0x0
* 0x000F[7:6] CLKIN_DIV 0 0x0
* 0x0010[1:0] CLK0_IDRV 3 0x3
* 0x0010[3:2] CLK0_SRC 3 0x3
* 0x0010[4] CLK0_INV 0 0x0
* 0x0010[5] MS0_SRC 0 0x0
* 0x0010[6] MS0_INT 1 0x1
* 0x0010[7] CLK0_PDN 0 0x0
* 0x0011[1:0] CLK1_IDRV 3 0x3
* 0x0011[3:2] CLK1_SRC 3 0x3
* 0x0011[4] CLK1_INV 0 0x0
* 0x0011[5] MS1_SRC 0 0x0
* 0x0011[6] MS1_INT 0 0x0
* 0x0011[7] CLK1_PDN 0 0x0
* 0x0012[1:0] CLK2_IDRV 0 0x0
* 0x0012[3:2] CLK2_SRC 3 0x3
* 0x0012[4] CLK2_INV 0 0x0
* 0x0012[5] MS2_SRC 0 0x0
* 0x0012[6] MS2_INT 0 0x0
* 0x0012[7] CLK2_PDN 1 0x1
* 0x0013[1:0] CLK3_IDRV 0 0x0
* 0x0013[3:2] CLK3_SRC 3 0x3
* 0x0013[4] CLK3_INV 0 0x0
* 0x0013[5] MS3_SRC 0 0x0
* 0x0013[6] MS3_INT 0 0x0
* 0x0013[7] CLK3_PDN 1 0x1
* 0x0014[1:0] CLK4_IDRV 0 0x0
* 0x0014[3:2] CLK4_SRC 3 0x3
* 0x0014[4] CLK4_INV 0 0x0
* 0x0014[5] MS4_SRC 0 0x0
* 0x0014[6] MS4_INT 0 0x0
* 0x0014[7] CLK4_PDN 1 0x1
* 0x0015[1:0] CLK5_IDRV 0 0x0
* 0x0015[3:2] CLK5_SRC 3 0x3
* 0x0015[4] CLK5_INV 0 0x0
* 0x0015[5] MS5_SRC 0 0x0
* 0x0015[6] MS5_INT 0 0x0
* 0x0015[7] CLK5_PDN 1 0x1
* 0x0016[1:0] CLK6_IDRV 0 0x0
* 0x0016[3:2] CLK6_SRC 3 0x3
* 0x0016[4] CLK6_INV 0 0x0
* 0x0016[5] MS6_SRC 0 0x0
* 0x0016[6] FBA_INT 0 0x0
* 0x0016[7] CLK6_PDN 1 0x1
* 0x0017[1:0] CLK7_IDRV 0 0x0
* 0x0017[3:2] CLK7_SRC 3 0x3
* 0x0017[4] CLK7_INV 0 0x0
* 0x0017[5] MS7_SRC 0 0x0
* 0x0017[6] FBB_INT 0 0x0
* 0x0017[7] CLK7_PDN 1 0x1
* 0x0018[1:0] CLK0_DIS_STATE 2 0x2
* 0x0018[3:2] CLK1_DIS_STATE 2 0x2
* 0x001C[17:0] MSNA_P1 3584 0x00E00
* 0x001F[19:0] MSNA_P2 0 0x00000
* 0x001F[23:4] MSNA_P3 1 0x00001
* 0x002C[17:0] MS0_P1 0 0x00000
* 0x002C[2] MS0_DIVBY4_0 1 0x1
* 0x002C[3] MS0_DIVBY4_1 1 0x1
* 0x002F[19:0] MS0_P2 0 0x00000
* 0x002F[23:4] MS0_P4 1 0x00001
* 0x0034[17:0] MS1_P1 1536 0x00600
* 0x0037[19:0] MS1_P2 0 0x00000
* 0x0037[23:4] MS1_P4 1 0x00001
* 0x005A[7:0] MS6_P2 0 0x00
* 0x005B[7:0] MS7_P2 0 0x00
* 0x0095[14:0] SSDN_P2 0 0x0000
* 0x0095[7] SSC_EN 0 0x0
* 0x0097[14:0] SSDN_P3 0 0x0000
* 0x0097[7] SSC_MODE 0 0x0
* 0x0099[11:0] SSDN_P1 0 0x000
* 0x009A[15:4] SSUDP 0 0x000
* 0x00A2[21:0] VCXO_PARAM 0 0x000000
* 0x00A5[7:0] CLK0_PHOFF 0 0x00
* 0x00A6[7:0] CLK1_PHOFF 0 0x00
* 0x00B7[7:6] XTAL_CL 2 0x2
*
*
*/
#endif