873 lines
22 KiB
C++
873 lines
22 KiB
C++
/**
|
|
@file Si5351C.cpp
|
|
@author Lime Microsystems
|
|
@brief Configuring of Si5351C
|
|
*/
|
|
|
|
#include "Si5351C.h"
|
|
#include "lmsComms.h"
|
|
#include <math.h>
|
|
#include <iomanip>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <set>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <iostream>
|
|
using namespace std;
|
|
|
|
#define Log(msg, type) cout << (msg) << endl;
|
|
|
|
/// Splits float into fraction integers A + B/C
|
|
void realToFrac(const float real, int &A, int &B, int &C)
|
|
{
|
|
A = (int)real;
|
|
B = (int)((real - A) * 1048576 + 0.5);
|
|
C = 1048576;
|
|
|
|
int a = B;
|
|
int b = C;
|
|
int temp;
|
|
while( b!= 0) // greatest common divider
|
|
{
|
|
temp = a % b;
|
|
a = b;
|
|
b = temp;
|
|
}
|
|
B = B/a;
|
|
C = C/a;
|
|
}
|
|
|
|
/// Default configuration
|
|
const unsigned char Si5351C::m_defaultConfiguration[] =
|
|
{ 0,0x00,
|
|
1,0x00,
|
|
2,0x08,
|
|
3,0x00,
|
|
4,0x00,
|
|
5,0x00,
|
|
6,0x00,
|
|
7,0x00,
|
|
8,0x00,
|
|
9,0x00,
|
|
10,0x00,
|
|
11,0x00,
|
|
12,0x00,
|
|
13,0x00,
|
|
14,0x00,
|
|
15,0x04,
|
|
16,0x4F,
|
|
17,0x4F,
|
|
18,0x4F,
|
|
19,0x4F,
|
|
20,0x4F,
|
|
21,0x4F,
|
|
22,0x0F,
|
|
23,0x0F,
|
|
24,0x00,
|
|
25,0x00,
|
|
26,0x00,
|
|
27,0x80,
|
|
28,0x00,
|
|
29,0x0B,
|
|
30,0x2F,
|
|
31,0x00,
|
|
32,0x00,
|
|
33,0x00,
|
|
34,0x00,
|
|
35,0x00,
|
|
36,0x00,
|
|
37,0x00,
|
|
38,0x00,
|
|
39,0x00,
|
|
40,0x00,
|
|
41,0x00,
|
|
42,0x00,
|
|
43,0x01,
|
|
44,0x00,
|
|
45,0x0D,
|
|
46,0x00,
|
|
47,0x00,
|
|
48,0x00,
|
|
49,0x00,
|
|
50,0x00,
|
|
51,0x01,
|
|
52,0x00,
|
|
53,0x0D,
|
|
54,0x00,
|
|
55,0x00,
|
|
56,0x00,
|
|
57,0x00,
|
|
58,0x00,
|
|
59,0x01,
|
|
60,0x00,
|
|
61,0x0D,
|
|
62,0x00,
|
|
63,0x00,
|
|
64,0x00,
|
|
65,0x00,
|
|
66,0x00,
|
|
67,0x01,
|
|
68,0x00,
|
|
69,0x0D,
|
|
70,0x00,
|
|
71,0x00,
|
|
72,0x00,
|
|
73,0x00,
|
|
74,0x00,
|
|
75,0x01,
|
|
76,0x00,
|
|
77,0x0D,
|
|
78,0x00,
|
|
79,0x00,
|
|
80,0x00,
|
|
81,0x00,
|
|
82,0x00,
|
|
83,0x01,
|
|
84,0x00,
|
|
85,0x0D,
|
|
86,0x00,
|
|
87,0x00,
|
|
88,0x00,
|
|
89,0x00,
|
|
90,0x1E,
|
|
91,0x1E,
|
|
92,0x00,
|
|
93,0x00,
|
|
94,0x00,
|
|
95,0x00,
|
|
96,0x00,
|
|
97,0x00,
|
|
98,0x00,
|
|
99,0x00,
|
|
100,0x00,
|
|
101,0x00,
|
|
102,0x00,
|
|
103,0x00,
|
|
104,0x00,
|
|
105,0x00,
|
|
106,0x00,
|
|
107,0x00,
|
|
108,0x00,
|
|
109,0x00,
|
|
110,0x00,
|
|
111,0x00,
|
|
112,0x00,
|
|
113,0x00,
|
|
114,0x00,
|
|
115,0x00,
|
|
116,0x00,
|
|
117,0x00,
|
|
118,0x00,
|
|
119,0x00,
|
|
120,0x00,
|
|
121,0x00,
|
|
122,0x00,
|
|
123,0x00,
|
|
124,0x00,
|
|
125,0x00,
|
|
126,0x00,
|
|
127,0x00,
|
|
128,0x00,
|
|
129,0x00,
|
|
130,0x00,
|
|
131,0x00,
|
|
132,0x00,
|
|
133,0x00,
|
|
134,0x00,
|
|
135,0x00,
|
|
136,0x00,
|
|
137,0x00,
|
|
138,0x00,
|
|
139,0x00,
|
|
140,0x00,
|
|
141,0x00,
|
|
142,0x00,
|
|
143,0x00,
|
|
144,0x00,
|
|
145,0x00,
|
|
146,0x00,
|
|
147,0x00,
|
|
148,0x00,
|
|
149,0x00,
|
|
150,0x00,
|
|
151,0x00,
|
|
152,0x00,
|
|
153,0x00,
|
|
154,0x00,
|
|
155,0x00,
|
|
156,0x00,
|
|
157,0x00,
|
|
158,0x00,
|
|
159,0x00,
|
|
160,0x00,
|
|
161,0x00,
|
|
162,0x00,
|
|
163,0x00,
|
|
164,0x00,
|
|
165,0x00,
|
|
166,0x00,
|
|
167,0x00,
|
|
168,0x00,
|
|
169,0x00,
|
|
170,0x00,
|
|
171,0x00,
|
|
172,0x00,
|
|
173,0x00,
|
|
174,0x00,
|
|
175,0x00,
|
|
176,0x00,
|
|
177,0x00,
|
|
178,0x00,
|
|
179,0x00,
|
|
180,0x00,
|
|
181,0x30,
|
|
182,0x00,
|
|
183,0xD2,
|
|
184,0x60,
|
|
185,0x60,
|
|
186,0x00,
|
|
187,0xC0,
|
|
188,0x00,
|
|
189,0x00,
|
|
190,0x00,
|
|
191,0x00,
|
|
192,0x00,
|
|
193,0x00,
|
|
194,0x00,
|
|
195,0x00,
|
|
196,0x00,
|
|
197,0x00,
|
|
198,0x00,
|
|
199,0x00,
|
|
200,0x00,
|
|
201,0x00,
|
|
202,0x00,
|
|
203,0x00,
|
|
204,0x00,
|
|
205,0x00,
|
|
206,0x00,
|
|
207,0x00,
|
|
208,0x00,
|
|
209,0x00,
|
|
210,0x00,
|
|
211,0x00,
|
|
212,0x00,
|
|
213,0x00,
|
|
214,0x00,
|
|
215,0x00,
|
|
216,0x00,
|
|
217,0x00,
|
|
218,0x00,
|
|
219,0x00,
|
|
220,0x00,
|
|
221,0x0D,
|
|
222,0x00,
|
|
223,0x00,
|
|
224,0x00,
|
|
225,0x00,
|
|
226,0x00,
|
|
227,0x00,
|
|
228,0x00,
|
|
229,0x00,
|
|
230,0x00,
|
|
231,0x00,
|
|
232,0x00};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
Si5351C::Si5351C()
|
|
{
|
|
memset(m_newConfiguration, 0, 255);
|
|
for(unsigned int i=0; i<sizeof(m_defaultConfiguration); i+=2)
|
|
{
|
|
m_newConfiguration[m_defaultConfiguration[i]] = m_defaultConfiguration[i+1];
|
|
}
|
|
device = NULL;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Si5351C::~Si5351C()
|
|
{
|
|
|
|
}
|
|
|
|
/** @brief Sends Configuration to Si5351C
|
|
*/
|
|
Si5351C::Status Si5351C::UploadConfiguration()
|
|
{
|
|
LMScomms::GenericPacket pkt;
|
|
pkt.cmd = CMD_SI5351_WR;
|
|
|
|
if (!device)
|
|
return FAILED;
|
|
//Disable outputs
|
|
pkt.outBuffer.push_back(3);
|
|
pkt.outBuffer.push_back(0xFF);
|
|
//Power down all output drivers
|
|
for(int i=0; i<8; ++i)
|
|
{
|
|
pkt.outBuffer.push_back(16 + i);
|
|
pkt.outBuffer.push_back(0x84);
|
|
}
|
|
//write new configuration
|
|
for (int i = 15; i <= 92; ++i)
|
|
{
|
|
pkt.outBuffer.push_back(i);
|
|
pkt.outBuffer.push_back(m_newConfiguration[i]);
|
|
}
|
|
for (int i = 149; i <= 170; ++i)
|
|
{
|
|
pkt.outBuffer.push_back(i);
|
|
pkt.outBuffer.push_back(m_newConfiguration[i]);
|
|
}
|
|
//apply soft reset
|
|
pkt.outBuffer.push_back(177);
|
|
pkt.outBuffer.push_back(0xAC);
|
|
//Enabe desired outputs
|
|
pkt.outBuffer.push_back(3);
|
|
pkt.outBuffer.push_back(m_newConfiguration[3]);
|
|
|
|
if( !device->IsOpen() )
|
|
{
|
|
return FAILED;
|
|
}
|
|
LMScomms::TransferStatus status;
|
|
status = device->TransferPacket(pkt);
|
|
if (status != LMScomms::TRANSFER_SUCCESS || pkt.status != STATUS_COMPLETED_CMD)
|
|
{
|
|
return FAILED;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
@brief Sets connection manager to use for data transferring Si5351C
|
|
@param mng connection manager for data transferring
|
|
*/
|
|
void Si5351C::Initialize(LMScomms *mng)
|
|
{
|
|
assert(mng != nullptr);
|
|
device = mng;
|
|
}
|
|
|
|
/**
|
|
@brief Loads register values for Si5356A from file
|
|
@param FName input filename
|
|
*/
|
|
bool Si5351C::LoadRegValuesFromFile(string FName)
|
|
{
|
|
fstream fin;
|
|
fin.open(FName.c_str(), ios::in);
|
|
|
|
const int len = 1024;
|
|
char line[len];
|
|
|
|
int addr;
|
|
int value;
|
|
|
|
while(!fin.eof())
|
|
{
|
|
fin.getline(line, len);
|
|
if( line[0] == '#')
|
|
continue;
|
|
if( strcmp(line, "#END_PROFILE") == 0)
|
|
break;
|
|
sscanf(line, "%i,%x",&addr,&value);
|
|
m_newConfiguration[addr] = value;
|
|
}
|
|
|
|
fin.close();
|
|
return false;
|
|
};
|
|
|
|
/** @brief Calculates multisynth dividers and VCO frequencies
|
|
@param clocks output clocks configuration
|
|
@param plls plls configurations
|
|
@param Fmin lowest VCO frequency
|
|
@param Fmax highest VCO frequency
|
|
*/
|
|
void Si5351C::FindVCO(Si5351_Channel *clocks, Si5351_PLL *plls, const unsigned long Fmin, const unsigned long Fmax)
|
|
{
|
|
int clockCount = 8;
|
|
//reset output parameters
|
|
for(int i=0; i<clockCount; i++)
|
|
{
|
|
clocks[i].pllSource = 0;
|
|
clocks[i].int_mode = 0;
|
|
clocks[i].multisynthDivider = 8;
|
|
}
|
|
|
|
bool clk6satisfied = !clocks[6].powered;
|
|
bool clk7satisfied = !clocks[7].powered;
|
|
|
|
bool pllAused = false;
|
|
bool pllBused = false;
|
|
|
|
map< unsigned long, int> availableFrequenciesPLLA; //all available frequencies for VCO
|
|
map< unsigned long, int> availableFrequenciesPLLB; //all available frequencies for VCO
|
|
|
|
//if clk6 or clk7 is used make available frequencies according to them
|
|
if(clocks[6].powered || clocks[7].powered)
|
|
{
|
|
set<unsigned long> clk6freqs;
|
|
set<unsigned long> clk7freqs;
|
|
set<unsigned long> sharedFreqs;
|
|
unsigned int mult = 6;
|
|
if(!clk6satisfied)
|
|
{
|
|
unsigned long freq = clocks[6].outputFreqHz;
|
|
while(freq <= Fmax && mult <= 254)
|
|
{
|
|
freq = clocks[6].outputFreqHz*mult;
|
|
if(freq >= Fmin && freq <= Fmax)
|
|
{
|
|
clk6freqs.insert(freq);
|
|
}
|
|
mult += 2;
|
|
}
|
|
}
|
|
mult = 6;
|
|
if(!clk7satisfied)
|
|
{
|
|
unsigned long freq = clocks[7].outputFreqHz;
|
|
while(freq <= Fmax && mult <= 254)
|
|
{
|
|
freq = clocks[7].outputFreqHz*mult;
|
|
if(freq >= Fmin && freq <= Fmax)
|
|
{
|
|
clk7freqs.insert(freq);
|
|
}
|
|
mult += 2;
|
|
}
|
|
}
|
|
bool canShare = false;
|
|
//find if clk6 and clk7 can share the same pll
|
|
for (set<unsigned long>::iterator it6=clk6freqs.begin(); it6!=clk6freqs.end(); ++it6)
|
|
{
|
|
for (set<unsigned long>::iterator it7=clk7freqs.begin(); it7!=clk7freqs.end(); ++it7)
|
|
{
|
|
if(*it6 == *it7)
|
|
{
|
|
canShare = true;
|
|
sharedFreqs.insert(*it6);
|
|
}
|
|
}
|
|
}
|
|
if(canShare) //assign PLLA for both clocks
|
|
{
|
|
clocks[6].pllSource = 0;
|
|
clocks[7].pllSource = 0;
|
|
pllAused = true;
|
|
for (set<unsigned long>::iterator it=sharedFreqs.begin(); it!=sharedFreqs.end(); ++it)
|
|
{
|
|
availableFrequenciesPLLA.insert( pair<unsigned long, int> (*it, 0) );
|
|
}
|
|
clk6satisfied = true;
|
|
clk7satisfied = true;
|
|
}
|
|
else //if clocks 6 and 7 can't share pll, assign pllA to clk6 and pllB to clk7
|
|
{
|
|
if(!clk6satisfied)
|
|
{
|
|
clocks[6].pllSource = 0;
|
|
clk6satisfied = true;
|
|
pllAused = true;
|
|
for (set<unsigned long>::iterator it6=clk6freqs.begin(); it6!=clk6freqs.end(); ++it6)
|
|
{
|
|
availableFrequenciesPLLA.insert( pair<unsigned long, int>(*it6, 0) );
|
|
}
|
|
}
|
|
if(!clk7satisfied)
|
|
{
|
|
clocks[7].pllSource = 1;
|
|
clk7satisfied = true;
|
|
pllBused = true;
|
|
for (set<unsigned long>::iterator it7=clk7freqs.begin(); it7!=clk7freqs.end(); ++it7)
|
|
{
|
|
availableFrequenciesPLLB.insert( pair<unsigned long, int> (*it7, 0) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//PLLA stage, find all clocks that have integer coefficients with PLLA
|
|
//if pllA is not used by clk6 or clk7, fill available frequencies according to clk1-clk5 clocks
|
|
if( availableFrequenciesPLLA.size() == 0 && !pllAused)
|
|
{
|
|
for(int i=0; i<6; ++i)
|
|
{
|
|
unsigned long freq = clocks[i].outputFreqHz > Fmin ? clocks[i].outputFreqHz : (clocks[i].outputFreqHz*((Fmin/clocks[i].outputFreqHz) + ((Fmin%clocks[i].outputFreqHz)!=0)));
|
|
while(freq >= Fmin && freq <= Fmax)
|
|
{
|
|
//add all output frequency multiples that are in VCO interval
|
|
availableFrequenciesPLLA.insert( pair<unsigned long, int> (freq, 0));
|
|
freq += clocks[i].outputFreqHz;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int bestScore = 0; //score shows how many outputs have integer dividers
|
|
//calculate scores for all available frequencies
|
|
unsigned long bestVCOA = 0;
|
|
for (map<unsigned long, int>::iterator it=availableFrequenciesPLLA.begin(); it!=availableFrequenciesPLLA.end(); ++it)
|
|
{
|
|
for(int i=0; i<8; ++i)
|
|
{
|
|
if(clocks[i].outputFreqHz == 0 || !clocks[i].powered)
|
|
continue;
|
|
|
|
if( (it->first % clocks[i].outputFreqHz) == 0)
|
|
{
|
|
it->second = it->second+1;
|
|
}
|
|
}
|
|
if(it->second >= bestScore)
|
|
{
|
|
bestScore = it->second;
|
|
bestVCOA = it->first;
|
|
}
|
|
}
|
|
//scores calculated
|
|
//cout << "PLLA stage: " << endl;
|
|
//cout << "best score: " << bestScore << " best VCO: " << bestVCOA << endl;
|
|
plls[0].VCO_Hz = bestVCOA;
|
|
plls[0].feedbackDivider = (double)bestVCOA/plls[0].inputFreqHz;
|
|
|
|
for(int i=0; i<clockCount; ++i)
|
|
{
|
|
if(clocks[i].outputFreqHz == 0 || !clocks[i].powered)
|
|
continue;
|
|
|
|
clocks[i].multisynthDivider = bestVCOA/clocks[i].outputFreqHz;
|
|
if(bestVCOA%clocks[i].outputFreqHz == 0)
|
|
{
|
|
clocks[i].int_mode = true;
|
|
clocks[i].multisynthDivider = bestVCOA/clocks[i].outputFreqHz;
|
|
}
|
|
else
|
|
{
|
|
clocks[i].int_mode = false;
|
|
clocks[i].multisynthDivider = (double)bestVCOA/clocks[i].outputFreqHz;
|
|
}
|
|
clocks[i].pllSource = 0;
|
|
}
|
|
|
|
|
|
//PLLB stage, find all clocks that have integer coefficients with PLLB
|
|
//if pllB is not used by clk6 or clk7, fill available frequencies according to clk1-clk5 clocks, that don't have integer dividers
|
|
if( availableFrequenciesPLLB.size() == 0 && !pllBused)
|
|
{
|
|
for(int i=0; i<6; ++i)
|
|
{
|
|
if(clocks[i].outputFreqHz == 0 || !clocks[i].powered)
|
|
continue;
|
|
|
|
if(clocks[i].int_mode) //skip clocks with integer dividers
|
|
continue;
|
|
unsigned long freq = clocks[i].outputFreqHz > Fmin ? clocks[i].outputFreqHz : (clocks[i].outputFreqHz*((Fmin/clocks[i].outputFreqHz) + ((Fmin%clocks[i].outputFreqHz)!=0)));
|
|
while(freq >= Fmin && freq <= Fmax)
|
|
{
|
|
availableFrequenciesPLLB.insert( pair<unsigned long, int> (freq, 0));
|
|
freq += clocks[i].outputFreqHz;
|
|
}
|
|
}
|
|
}
|
|
|
|
bestScore = 0;
|
|
//calculate scores for all available frequencies
|
|
unsigned long bestVCOB = 0;
|
|
for (map<unsigned long, int>::iterator it=availableFrequenciesPLLB.begin(); it!=availableFrequenciesPLLB.end(); ++it)
|
|
{
|
|
for(int i=0; i<8; ++i)
|
|
{
|
|
if(clocks[i].outputFreqHz == 0 || !clocks[i].powered)
|
|
continue;
|
|
|
|
if( (it->first % clocks[i].outputFreqHz) == 0)
|
|
{
|
|
it->second = it->second+1;
|
|
}
|
|
}
|
|
if(it->second >= bestScore)
|
|
{
|
|
bestScore = it->second;
|
|
bestVCOB = it->first;
|
|
}
|
|
}
|
|
//scores calculated
|
|
// cout << "PLLB stage: " << endl;
|
|
// cout << "best score: " << bestScore << " best VCO: " << bestVCOB << endl;
|
|
if(bestVCOB == 0) //just in case if pllb is not used make it the same frequency as plla
|
|
bestVCOB = bestVCOA;
|
|
plls[1].VCO_Hz = bestVCOB;
|
|
plls[1].feedbackDivider = (double)bestVCOB/plls[0].inputFreqHz;
|
|
for(int i=0; i<clockCount; ++i)
|
|
{
|
|
if(clocks[i].outputFreqHz == 0 || !clocks[i].powered)
|
|
continue;
|
|
|
|
if(clocks[i].int_mode)
|
|
continue;
|
|
clocks[i].multisynthDivider = bestVCOB/clocks[i].outputFreqHz;
|
|
if(bestVCOB%clocks[i].outputFreqHz == 0)
|
|
{
|
|
clocks[i].int_mode = true;
|
|
clocks[i].multisynthDivider = bestVCOB/clocks[i].outputFreqHz;
|
|
}
|
|
else
|
|
{
|
|
clocks[i].int_mode = false;
|
|
clocks[i].multisynthDivider = (double)bestVCOB/clocks[i].outputFreqHz;
|
|
}
|
|
clocks[i].pllSource = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/** @brief Modifies register map with clock settings
|
|
@return true if success
|
|
*/
|
|
Si5351C::Status Si5351C::ConfigureClocks()
|
|
{
|
|
FindVCO(CLK, PLL, 600000000, 900000000);
|
|
stringstream ss;
|
|
int addr;
|
|
m_newConfiguration[3] = 0;
|
|
for(int i=0; i<8; ++i)
|
|
{
|
|
m_newConfiguration[3] |= (!CLK[i].powered) << i; //enabled
|
|
m_newConfiguration[16+i] = 0;
|
|
m_newConfiguration[16+i] |= !CLK[i].powered << 7; // powered
|
|
|
|
if(CLK[i].int_mode)
|
|
{
|
|
m_newConfiguration[16+i] |= 1 << 6; //integer mode
|
|
}
|
|
else
|
|
m_newConfiguration[16+i] |= 0 << 6;
|
|
|
|
m_newConfiguration[16+i] |= CLK[i].pllSource << 5; //PLL source
|
|
m_newConfiguration[16+i] |= CLK[i].inverted << 4; // invert
|
|
m_newConfiguration[16+i] |= 3 << 2;
|
|
m_newConfiguration[16+i] |= 3;
|
|
|
|
addr = 42+i*8;
|
|
ss.clear();
|
|
ss.str( string() );
|
|
ss << "CLK" << i << " fOut = " << CLK[i].outputFreqHz/1000000.0 << " MHz";
|
|
int DivA;
|
|
int DivB;
|
|
int DivC;
|
|
|
|
realToFrac(CLK[i].multisynthDivider, DivA, DivB, DivC);
|
|
ss << " Multisynth Divider " << DivA << " " << DivB << "/" << DivC;
|
|
ss << " R divider = " << CLK[i].outputDivider << " source = " << (CLK[i].pllSource == 0 ? "PLLA" : "PLLB") << endl;
|
|
|
|
Log(ss.str(), LOG_INFO);
|
|
|
|
if( CLK[i].multisynthDivider < 8 || 900 < CLK[i].multisynthDivider)
|
|
{
|
|
Log("Si5351C - Output multisynth divider is outside [8;900] interval.\n", LOG_ERROR);
|
|
return FAILED;
|
|
}
|
|
|
|
if(i<6)
|
|
{
|
|
if(CLK[i].outputFreqHz <= 150000000)
|
|
{
|
|
unsigned MSX_P1 = 128 * DivA + floor(128 * ( (float)DivB/DivC)) - 512;
|
|
unsigned MSX_P2 = 128 * DivB - DivC * floor( 128 * DivB/DivC );
|
|
unsigned MSX_P3 = DivC;
|
|
|
|
m_newConfiguration[addr] = MSX_P3 >> 8;
|
|
m_newConfiguration[addr+1] = MSX_P3;
|
|
|
|
m_newConfiguration[addr+2] = 0;
|
|
m_newConfiguration[addr+2] |= (MSX_P1 >> 16 ) & 0x03;
|
|
m_newConfiguration[addr+3] = MSX_P1 >> 8;
|
|
m_newConfiguration[addr+4] = MSX_P1;
|
|
|
|
m_newConfiguration[addr+5] = 0;
|
|
m_newConfiguration[addr+5] = (MSX_P2 >> 16) & 0x0F;
|
|
m_newConfiguration[addr+5] |= (MSX_P3 >> 16) << 4;
|
|
|
|
m_newConfiguration[addr+6] = MSX_P2;
|
|
m_newConfiguration[addr+7] = MSX_P2 >> 8;
|
|
}
|
|
else if( CLK[i].outputFreqHz <= 160000000) // AVAILABLE ONLY ON 0-5 MULTISYNTHS
|
|
{
|
|
Log("Si5351C - clock configuring for more than 150 MHz not implemented\n", LOG_ERROR);
|
|
return FAILED;
|
|
}
|
|
}
|
|
else // CLK6 and CLK7 only integer mode
|
|
{
|
|
if(CLK[i].outputFreqHz <= 150000000)
|
|
{
|
|
if(i==6)
|
|
{
|
|
m_newConfiguration[90] = DivA;
|
|
if(DivA%2 != 0)
|
|
{
|
|
Log("Si5351C - CLK6 multisynth divider is not even integer\n", LOG_ERROR);
|
|
return FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_newConfiguration[91] = DivA;
|
|
if(DivA%2 != 0)
|
|
{
|
|
Log("Si5351C - CLK7 multisynth divider is not even integer\n", LOG_ERROR);
|
|
return FAILED;
|
|
}
|
|
}
|
|
}
|
|
else if( CLK[i].outputFreqHz <= 160000000) // AVAILABLE ONLY ON 0-5 MULTISYNTHS
|
|
{
|
|
Log("Si5351C - clock configuring for more than 150 MHz not implemented\n", LOG_ERROR);
|
|
return FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//configure pll
|
|
//set input clk source
|
|
m_newConfiguration[15] = m_newConfiguration[15] & 0xF3;
|
|
m_newConfiguration[15] |= (PLL[0].CLK_SRC & 1) << 2;
|
|
m_newConfiguration[15] |= (PLL[1].CLK_SRC & 1) << 3;
|
|
for(int i=0; i<2; ++i)
|
|
{
|
|
addr = 26+i*8;
|
|
if(PLL[i].feedbackDivider < 15 || PLL[i].feedbackDivider > 90)
|
|
{
|
|
Log("Si5351C - VCO frequency divider out of range [15:90].\n", LOG_ERROR);
|
|
return FAILED;
|
|
}
|
|
if( PLL[i].VCO_Hz < 600000000 || PLL[i].VCO_Hz > 900000000)
|
|
{
|
|
Log("Si5351C - Can't calculate valid VCO frequency.\n", LOG_ERROR);
|
|
return FAILED;
|
|
}
|
|
ss.clear();
|
|
ss.str(string());
|
|
ss << "Si5351C : VCO" << (i==0 ? "A" : "B") << " = " << PLL[i].VCO_Hz/1000000.0 << " MHz";
|
|
|
|
//calculate MSNx_P1, MSNx_P2, MSNx_P3
|
|
int MSNx_P1;
|
|
int MSNx_P2;
|
|
int MSNx_P3;
|
|
|
|
int DivA;
|
|
int DivB;
|
|
int DivC;
|
|
realToFrac(PLL[i].feedbackDivider, DivA, DivB, DivC);
|
|
ss << " Feedback Divider " << DivA << " " << DivB << "/" << DivC << endl;
|
|
Log(ss.str(), LOG_INFO);
|
|
|
|
MSNx_P1 = 128 * DivA + floor(128 * ( (float)DivB/DivC)) - 512;
|
|
MSNx_P2 = 128 * DivB - DivC * floor( 128 * DivB/DivC );
|
|
MSNx_P3 = DivC;
|
|
|
|
m_newConfiguration[addr+4] = MSNx_P1;
|
|
m_newConfiguration[addr+3] = MSNx_P1 >> 8;
|
|
m_newConfiguration[addr+2] = MSNx_P1 >> 16;
|
|
|
|
m_newConfiguration[addr+7] = MSNx_P2;
|
|
m_newConfiguration[addr+6] = MSNx_P2 >> 8;
|
|
m_newConfiguration[addr+5] = 0;
|
|
m_newConfiguration[addr+5] = (MSNx_P2 >> 16) & 0x0F;
|
|
|
|
m_newConfiguration[addr+5] |= (MSNx_P3 >> 16) << 4;
|
|
m_newConfiguration[addr+1] |= MSNx_P3;
|
|
m_newConfiguration[addr] |= MSNx_P3 >> 8;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/** @brief Sets output clock parameters
|
|
@param id clock id 0-CLK0 1-CLK1 ...
|
|
@param fOut_Hz output frequency in Hz
|
|
@param enabled is this output powered
|
|
@param inverted invert clock
|
|
*/
|
|
void Si5351C::SetClock(unsigned char id, unsigned long fOut_Hz, bool enabled, bool inverted)
|
|
{
|
|
if( id < 8)
|
|
{
|
|
if(fOut_Hz < 8000 || fOut_Hz > 160000000)
|
|
{
|
|
stringstream ss;
|
|
ss << "Si5351C - CLK" << (int)id << " output frequency must be between 8kHz and 160MHz. fOut_MHz = " << fOut_Hz/1000000.0 << endl;
|
|
Log(ss.str(), LOG_ERROR);
|
|
return;
|
|
}
|
|
CLK[id].powered = enabled;
|
|
CLK[id].inverted = inverted;
|
|
CLK[id].outputFreqHz = fOut_Hz;
|
|
}
|
|
}
|
|
|
|
/** @brief Sets PLL input frequency
|
|
@param id PLL id 0-PLLA 1-PLLB
|
|
@param CLKIN_Hz clock input in Hz
|
|
*/
|
|
void Si5351C::SetPLL(unsigned char id, unsigned long CLKIN_Hz, int CLK_SRC)
|
|
{
|
|
if (id < 2)
|
|
{
|
|
PLL[id].inputFreqHz = CLKIN_Hz;
|
|
PLL[id].CLK_SRC = CLK_SRC;
|
|
}
|
|
}
|
|
|
|
/** @brief Resets configuration registers to default values
|
|
*/
|
|
void Si5351C::Reset()
|
|
{
|
|
memset(m_newConfiguration, 0, 255);
|
|
for(unsigned int i=0; i<sizeof(m_defaultConfiguration); i+=2)
|
|
{
|
|
m_newConfiguration[m_defaultConfiguration[i]] = m_defaultConfiguration[i+1];
|
|
}
|
|
}
|
|
|
|
Si5351C::StatusBits Si5351C::GetStatusBits()
|
|
{
|
|
StatusBits stat;
|
|
LMScomms::GenericPacket pkt;
|
|
pkt.cmd = CMD_SI5351_RD;
|
|
pkt.outBuffer.push_back(0);
|
|
pkt.outBuffer.push_back(1);
|
|
LMScomms::TransferStatus status;
|
|
status = device->TransferPacket(pkt);
|
|
if (status != LMScomms::TRANSFER_SUCCESS || pkt.status != STATUS_COMPLETED_CMD)
|
|
{
|
|
return stat;
|
|
}
|
|
|
|
stat.sys_init = (pkt.inBuffer[0] >> 7);
|
|
stat.lol_b = (pkt.inBuffer[0] >> 6) & 0x1;
|
|
stat.lol_a = (pkt.inBuffer[0] >> 5) & 0x1;
|
|
stat.los = (pkt.inBuffer[0] >> 4) & 0x1;
|
|
stat.sys_init_stky = (pkt.inBuffer[1] >> 7);
|
|
stat.lol_b_stky = (pkt.inBuffer[1] >> 6) & 0x1;
|
|
stat.lol_a_stky = (pkt.inBuffer[1] >> 5) & 0x1;
|
|
stat.los_stky = (pkt.inBuffer[1] >> 4) & 0x1;
|
|
return stat;
|
|
}
|
|
|
|
Si5351C::Status Si5351C::ClearStatus()
|
|
{
|
|
LMScomms::GenericPacket pkt;
|
|
pkt.cmd = CMD_SI5351_WR;
|
|
pkt.outBuffer.push_back(1);
|
|
pkt.outBuffer.push_back(0x0);
|
|
LMScomms::TransferStatus status;
|
|
status = device->TransferPacket(pkt);
|
|
if (status != LMScomms::TRANSFER_SUCCESS || pkt.status != STATUS_COMPLETED_CMD)
|
|
return FAILED;
|
|
return SUCCESS;
|
|
}
|