Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added dynamic phase shift information into output and output file comments #175

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 108 additions & 46 deletions libtrellis/tools/ecppll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ struct secondary_params{

float freq;
float phase;
float phase_vco_step;
float phase_div_step;
};

struct pll_params{
Expand All @@ -62,6 +64,10 @@ struct pll_params{
int feedback_div;
int output_div;
int primary_cphase;
int primary_fphase;
float primary_phase_vco_step;
float primary_phase_div_step;

string clkin_name;
string clkout0_name;
int dynamic;
Expand All @@ -85,6 +91,8 @@ struct pll_params{
}
};

float calc_vco_phase_step(int fphase, int div);
float calc_div_phase_step(int cphase, int div);
void calc_pll_params(pll_params &params, float input, float output);
void calc_pll_params_highres(pll_params &params, float input, float output);
void generate_secondary_output(pll_params &params, int channel, string name, float frequency, float phase);
Expand Down Expand Up @@ -245,22 +253,38 @@ int main(int argc, char** argv){
cout << "Feedback divisor: " << params.feedback_div << endl;
cout << "clkout0 divisor: " << params.output_div << "" << endl;
cout << "clkout0 frequency: " << params.fout << " MHz" << endl;
if(params.dynamic){
cout << "clkout0 vco step: " << params.primary_phase_vco_step << " degrees" << endl;
cout << "clkout0 div step: " << params.primary_phase_div_step << " degrees" << endl;
}
if(params.secondary[0].enabled){
cout << "clkout1 divisor: " << params.secondary[0].div << endl;
cout << "clkout1 frequency: " << params.secondary[0].freq << " MHz" << endl;
cout << "clkout1 phase shift: " << params.secondary[0].phase << " degrees" << endl;
if(params.dynamic){
cout << "clkout1 vco step: " << params.secondary[0].phase_vco_step << " degrees" << endl;
cout << "clkout1 div step: " << params.secondary[0].phase_div_step << " degrees" << endl;
}
}
if(params.secondary[1].enabled){
cout << "clkout2 divisor: " << params.secondary[1].div << endl;
cout << "clkout2 frequency: " << params.secondary[1].freq << " MHz" << endl;
cout << "clkout2 phase shift: " << params.secondary[1].phase << " degrees" << endl;
if(params.dynamic){
cout << "clkout2 vco step: " << params.secondary[1].phase_vco_step << " degrees" << endl;
cout << "clkout2 div step: " << params.secondary[1].phase_div_step << " degrees" << endl;
}
}
if(params.secondary[2].enabled){
cout << "clkout3 divisor: " << params.secondary[2].div << endl;
cout << "clkout3 frequency: " << params.secondary[2].freq << " MHz" << endl;
cout << "clkout3 phase shift: " << params.secondary[2].phase << " degrees" << endl;
if(params.dynamic){
cout << "clkout3 vco step: " << params.secondary[2].phase_vco_step << " degrees" << endl;
cout << "clkout3 div step: " << params.secondary[2].phase_div_step << " degrees" << endl;
}
}
cout << "VCO frequency: " << params.fvco << endl;
cout << "VCO frequency: " << params.fvco << " Mhz" << endl;
if(vm.count("file")){
ofstream f;
f.open(vm["file"].as<string>().c_str());
Expand All @@ -269,6 +293,14 @@ int main(int argc, char** argv){
}
}

float calc_vco_phase_step(int fphase, int div){
return (360.f * (float)fphase) / (8.f * (float)div);
}

float calc_div_phase_step(int cphase, int div){
return (360.f * ((float)cphase - (float)div)) / (1.f + (float)div);
}

void calc_pll_params(pll_params &params, float input, float output){
float error = std::numeric_limits<float>::max();
for(int input_div=1;input_div <= 128; input_div++){
Expand All @@ -278,24 +310,29 @@ void calc_pll_params(pll_params &params, float input, float output){
continue;
for(int feedback_div=1;feedback_div <= 80; feedback_div++){
for(int output_div=1;output_div <= 128; output_div++){
float fvco = fpfd * (float)feedback_div * (float) output_div;

if(fvco < VCO_MIN || fvco > VCO_MAX)
continue;

float fout = fvco / (float) output_div;
if(fabsf(fout - output) < error ||
(fabsf(fout-output) == error && fabsf(fvco - 600) < fabsf(params.fvco - 600))){
error = fabsf(fout-output);
params.refclk_div = input_div;
params.feedback_div = feedback_div;
params.output_div = output_div;
params.fout = fout;
params.fvco = fvco;
// shift the primary by 180 degrees. Lattice seems to do this
float ns_phase = 1/(fout * 1e6) * 0.5;
params.primary_cphase = ns_phase * (fvco * 1e6);
}
float fvco = fpfd * (float)feedback_div * (float) output_div;

if(fvco < VCO_MIN || fvco > VCO_MAX)
continue;

float fout = fvco / (float) output_div;
if(fabsf(fout - output) < error ||
(fabsf(fout-output) == error && fabsf(fvco - 600) < fabsf(params.fvco - 600))){
error = fabsf(fout-output);
params.refclk_div = input_div;
params.feedback_div = feedback_div;
params.output_div = output_div;
params.fout = fout;
params.fvco = fvco;
// shift the primary by 180 degrees. Lattice seems to do this
float ns_phase = 1/(fout * 1e6) * 0.5;
float phase_count = ns_phase * (fvco * 1e6);
params.primary_cphase = phase_count;
int cphase = (int) phase_count;
params.primary_fphase = (int) ((phase_count - cphase) * 8);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same calculation as done for secondary clocks is this correct?

params.primary_phase_vco_step = calc_vco_phase_step(params.primary_fphase, params.output_div);
params.primary_phase_div_step = calc_div_phase_step(params.primary_cphase, params.output_div);
}
}
}
}
Expand All @@ -310,29 +347,38 @@ void calc_pll_params_highres(pll_params &params, float input, float output){
continue;
for(int feedback_div=1;feedback_div <= 80; feedback_div++){
for(int output_div=1;output_div <= 128; output_div++){
float fvco = fpfd * (float)feedback_div * (float) output_div;

if(fvco < VCO_MIN || fvco > VCO_MAX)
continue;
float ffeedback = fvco / (float) output_div;
if(ffeedback < OUTPUT_MIN || ffeedback > OUTPUT_MAX)
continue;
for(int secondary_div = 1; secondary_div <= 128; secondary_div++){
float fout = fvco / (float) secondary_div;
if(fabsf(fout - output) < error ||
(fabsf(fout-output) == error && fabsf(fvco - 600) < fabsf(params.fvco - 600))){
error = fabsf(fout-output);
params.mode = pll_mode::HIGHRES;
params.refclk_div = input_div;
params.feedback_div = feedback_div;
params.output_div = output_div;
params.secondary[0].div = secondary_div;
params.secondary[0].enabled = true;
params.secondary[0].freq = fout;
params.fout = fout;
params.fvco = fvco;
}
}
float fvco = fpfd * (float)feedback_div * (float) output_div;

if(fvco < VCO_MIN || fvco > VCO_MAX)
continue;
float ffeedback = fvco / (float) output_div;
if(ffeedback < OUTPUT_MIN || ffeedback > OUTPUT_MAX)
continue;
for(int secondary_div = 1; secondary_div <= 128; secondary_div++){
float fout = fvco / (float) secondary_div;
if(fabsf(fout - output) < error ||
(fabsf(fout-output) == error && fabsf(fvco - 600) < fabsf(params.fvco - 600))){
error = fabsf(fout-output);
params.mode = pll_mode::HIGHRES;
params.refclk_div = input_div;
params.feedback_div = feedback_div;
params.output_div = output_div;
params.secondary[0].div = secondary_div;
params.secondary[0].enabled = true;
params.secondary[0].freq = fout;
params.fout = fout;
params.fvco = fvco;

// Is this correct for highres mode?
float ns_phase = 1/(fout * 1e6) * 0.5;
float phase_count = ns_phase * (fvco * 1e6);
params.primary_cphase = phase_count;
int cphase = (int) phase_count;
params.primary_fphase = (int) ((phase_count - cphase) * 8);
params.primary_phase_vco_step = calc_vco_phase_step(params.primary_fphase, params.output_div);
params.primary_phase_div_step = calc_div_phase_step(params.primary_cphase, params.output_div);
}
}
}
}
}
Expand All @@ -359,6 +405,8 @@ void generate_secondary_output(pll_params &params, int channel, string name, flo
params.secondary[channel].cphase = cphase + params.primary_cphase;
params.secondary[channel].fphase = fphase;
params.secondary[channel].name = name;
params.secondary[channel].phase_vco_step = calc_vco_phase_step(fphase, div);
params.secondary[channel].phase_div_step = calc_div_phase_step(params.secondary[channel].cphase, div);
}

void write_pll_config(const pll_params & params, const string &name, ofstream& file)
Expand All @@ -376,8 +424,22 @@ void write_pll_config(const pll_params & params, const string &name, ofstream& f
{
file << " input [1:0] phasesel, // clkout[] index affected by dynamic phase shift (except clkfb), 5 ns min before apply\n";
file << " input phasedir, // 0:delayed (lagging), 1:advence (leading), 5 ns min before apply\n";
file << " input phasestep, // 45 deg step, high for 5 ns min, falling edge = apply\n";
file << " input phaseloadreg, // high for 10 ns min, falling edge = apply\n";
file << " // high for 5 ns min, falling edge = apply\n";
file << " // " << params.primary_phase_vco_step << " deg step for " << params.clkout0_name << "\n";
for (int i = 0; i < 3; ++i){
if(params.secondary[i].enabled){
file << " // " << params.secondary[i].phase_vco_step << " deg step for " << params.secondary[i].name << "\n";
}
}
file << " input phasestep,\n";
file << " // high for 10 ns min, falling edge = apply\n";
file << " // " << params.primary_phase_div_step << " deg step for " << params.clkout0_name << "\n";
for (int i = 0; i < 3; ++i){
if(params.secondary[i].enabled){
file << " // " << params.secondary[i].phase_div_step << " deg step for " << params.secondary[i].name << "\n";
}
}
file << " input phaseloadreg,\n";
}
file << " input " << params.clkin_name << ", // " << params.clkin_frequency << " MHz, 0 deg\n";
file << " output " << params.clkout0_name << ", // " << params.fout << " MHz, 0 deg\n";
Expand Down Expand Up @@ -422,7 +484,7 @@ void write_pll_config(const pll_params & params, const string &name, ofstream& f
file << " .CLKOP_ENABLE(\"ENABLED\"),\n";
file << " .CLKOP_DIV(" << params.output_div << "),\n";
file << " .CLKOP_CPHASE(" << params.primary_cphase << "),\n";
file << " .CLKOP_FPHASE(0),\n";
file << " .CLKOP_FPHASE(" << params.primary_fphase << "),\n";
Copy link
Author

@rowanG077 rowanG077 Jul 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was 0 used here? The calculated fphase is not always 0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll need to take some time to look into this as it's been a good while since I touched the ECP5 PLLs. I will keep this on my list though.

if(params.secondary[0].enabled){
file << " .CLKOS_ENABLE(\"ENABLED\"),\n";
file << " .CLKOS_DIV(" << params.secondary[0].div << "),\n";
Expand Down Expand Up @@ -504,6 +566,6 @@ void write_pll_config(const pll_params & params, const string &name, ofstream& f
file << " .PLLWAKESYNC(1'b0),\n";
file << " .ENCLKOP(1'b0),\n";
file << " .LOCK(locked)\n";
file << " );\n";
file << " );\n";
file << "endmodule\n";
}