Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20703 Discussions

Block design file v. Verilog

Gerhard56
New Contributor I
611 Views

Hi,

I first tried to implement a (faaast) PWM using bdf schematic input. Runs quite good, but as things going more sophisticated (altering signal amplitude during runtime) I implement it in Verilog.

Now it seems, that I have a timing issue.

So I found the TCL viewer and I get out this.

A simple binary counter in Verilog is now a register and an adder, adding a 1.  In the bdf I placed a LPM_COUNTER out of the IP catalog and supplied a clock signal at its input. The RTL viewer can't look into that IP, ok, so is there the same structure (register and adder) insight, or is there a better/faster way to have counters? A binary counter is done with somd FFs, no adder needed (as far as I know).

My first version runs at 400MHz input clock producing ~195kHz multiphase PWM, and the Verilig version fails at 100Mhz input clock.

Did I miss something?

I use Cyclon 10 LP family.

Thanks for helping.

 

With best regards

Gerhard

0 Kudos
4 Replies
sstrell
Honored Contributor III
601 Views

Can you post your code and schematic?  It's a bit hard to picture this.

0 Kudos
Gerhard56
New Contributor I
597 Views

Hi,

here is the code.

 

It should be a differential PWM, e.g. two signals with 50% duty cycle phase shifted.

The resolution of the amount of phase shift should be very high.

So I use the phase shift options of the PLL-IP for the two LSB of duty value.

And one pulse should get addidional delay.

 

module DifferentialPWM_V3( input clk, input clk_P45, input clk_P90, input clk_P135, input enable,
input [13:0] signal, input [10:0] zvs_delay,
input [7:0] masterVolume,
output sigClk, output trg,
output Prim_SW1, output Prim_SW2, output Sec_SW1, output Sec_SW2,
output [12:0] duty,
output [ 1:0] phase_sel,
output pwmP0, output pwmP45, output pwmP90, output pwmP135);

// signal parts
reg sign_reg = 1'd0;
reg [ 1:0] phase_reg = 2'd0;
reg [10:0] duty_reg = 11'd0;
// ZVS delay
reg [10:0] delay_reg = 11'd20;
// values
wire signBit;
wire [12:0] amplitude;
wire [12:0] absAmplitude;
wire [20:0] mulRes;
wire [12:0] effAmplitude;
wire [12:0] limit;
wire [12:0] limitedAmplitude;
// counters
// Simulate random values as they will be found in the FPGA after power on.
reg [10:0] cnt_base = 11'd0;
reg [10:0] cnt_P45 = 11'd0;
reg [10:0] cnt_P90 = 11'd0;
reg [10:0] cnt_P135 = 11'd0;
reg en_cntP45 = 1'b0;
reg en_cntP90 = 1'b0;
reg en_cntP135= 1'b0;
reg blanking = 1'd0;
// PWM ffs
reg pwm_base = 1'd0;
reg [ 1:0] pwm_P0 = 2'd0;
reg [ 1:0] pwm_P45 = 2'd0;
reg [ 1:0] pwm_P90 = 2'd0;
reg [ 1:0] pwm_P135 = 2'd0;

// Just to generate test signals
reg [ 9:0] cnt_test = 10'd0;

reg p1, p2, s1, s2;

reg [13:0] sigTest;

 


// Advance the four counters at the positive edge
// of their individual clocks
always @(posedge clk)
begin
//cnt_base <= (enable == 1'b1) ? cnt_base + 11'd1 : 11'd0;
//cnt_base <= cnt_base + 11'd1;
if (enable == 1'b0)
begin
cnt_base <= 11'd00;
en_cntP45 <= 1'b0;
end
else
begin
cnt_base <= cnt_base + 11'd1;
en_cntP45 <= 1'b1;
end
end

always @(posedge clk_P45)
begin
//cnt_P45 <= (enable == 1'b1) ? cnt_P45 + 11'd1 : 11'd0;
//cnt_P45 <= cnt_P45 + 11'd1;
if (en_cntP45 == 1'b0)
begin
cnt_P45 <= 11'd0;
en_cntP90 <= 1'b0;
end
else
begin
cnt_P45 <= cnt_P45 + 11'd1;
en_cntP90 <= 1'b1;
end
end

always @(posedge clk_P90)
begin
//cnt_P90 <= (enable == 1'b1) ? cnt_P90 + 11'd1 : 11'd0;
//clk_P90 <= cnt_P90 + 11'd1;
if ( en_cntP90 == 1'b0)
begin
cnt_P90 <= 11'd0;
en_cntP135 <= 1'b0;
end
else
begin
cnt_P90 <= cnt_P90 + 11'd1;
en_cntP135 <= 1'b1;
end
end

always @(posedge clk_P135)
begin
cnt_P135 <= (en_cntP135 == 1'b1) ? cnt_P135 + 11'd1 : 11'd0;
//cnt_P135 <= cnt_P135 + 11'd1;
end


// Generate Prim_SW1 signal, this is the base for all
// Generate Prim_SW2 signal, which is shifted by the value in duty_reg
// Generate Sec_SW1 which is delayed version of Prim_SW2 (ZVS-delay) and
// flipped for negative signal values
// Generate some sub clocks
always @(negedge clk)
begin
if ( enable == 1'b1)
begin
if (cnt_base == 11'd0) pwm_base <= ~pwm_base;
if (cnt_base == duty_reg ) pwm_P0[0] <= ~pwm_P0[0];
if (cnt_base == delay_reg) pwm_P0[1] <= sign_reg == 0 ? pwm_P0[0] : ~pwm_P0[0];
if (cnt_base == 11'd2040) blanking <= ~pwm_base;
if (cnt_base == 11'd2047) blanking <= 0;
if (cnt_base == duty_reg) s1 <= ~s1;
end
else
begin
pwm_P0 <= 2'b0;
end
end

// Generate Prim_SW2 and Sec_SW1 as above but use the counter
// which phase is 45° behind clk
always @(negedge clk_P45)
begin
if (enable == 1'b1)
begin
pwm_P45[0] <= (cnt_P45 == duty_reg ) ? ~pwm_P45[0] : pwm_P45[0];
pwm_P45[1] <= (cnt_P45 == delay_reg) ? (sign_reg == 0) ? pwm_P45[0] : ~pwm_P45[0] : pwm_P45[1];
s2 <= (cnt_P45 == duty_reg) ? ~s2 : s2;
end
else
begin
pwm_P45 <= 2'b0;
end
end

// Generate Prim_SW2 and Sec_SW1 as above but use the counter
// which phase is 90° behind clk
always @(negedge clk_P90)
begin
if ( enable == 1'b1)
begin
if (cnt_P90 == duty_reg ) pwm_P90[0] <= ~pwm_P90[0];
if (cnt_P90 == delay_reg) pwm_P90[1] <= (sign_reg == 0) ? pwm_P90[0] : ~pwm_P90[0];
end
else
begin
pwm_P90 <= 2'b0;
end
end


// Generate Prim_SW2 and Sec_SW1 as above but use the counter
// which phase is 135° behind clk
always @(negedge clk_P135)
begin
if ( enable == 1'b1)
begin
if (cnt_P135 == duty_reg ) pwm_P135[0] <= ~pwm_P135[0];
if (cnt_P135 == delay_reg) pwm_P135[1] <= (sign_reg == 0) ? pwm_P135[0] : ~pwm_P135[0];
end
else
begin
pwm_P135 <= 2'b0;
end
end

 

always @(negedge blanking)
begin
cnt_test <= cnt_test + 9'd1;
//sigTest <= signal;
end

// Update duty cycle data
// Extract the sign bit from signal, calc the absolute value of the rest,
// Calculate the effective value according the masterVolume setting and
// limit the result, we can't over drive the output stage
// The update of the duty_reg must be done before the cnt_base counter
// reaches his end value
always @(posedge blanking)
begin
sign_reg <= signBit;
{duty_reg, phase_reg} <= limitedAmplitude;
delay_reg <= limitedAmplitude[12:2] + zvs_delay;

$display("duty-reg = %d, delay_reg = %d", duty_reg, delay_reg);
end

assign {signBit, amplitude} = signal;
assign absAmplitude = (signBit == 1'b0) ? amplitude : -amplitude;
assign mulRes = absAmplitude * masterVolume;
assign effAmplitude = mulRes >> 8;
assign limit = 13'd8148 - (zvs_delay << 2);
assign limitedAmplitude = (effAmplitude < limit) ? effAmplitude : limit;

assign trg = blanking;

assign Prim_SW1 = pwm_base;
// assign Prim_SW2 = phase_reg == 2'd0 ? pwm_P0 [0] : phase_reg == 2'd1 ? pwm_P45 [0] :
// phase_reg == 2'd2 ? pwm_P90[0] : pwm_P135[0];
assign Sec_SW1 = phase_reg == 2'd0 ? pwm_P0 [1] : phase_reg == 2'd1 ? pwm_P45 [1] :
phase_reg == 2'd2 ? pwm_P90[1] : pwm_P135[1];

//assign Prim_SW2 = pwm_P90 [0];
//assign Sec_SW1 = pwm_P90 [1];

assign Sec_SW2 = ~Sec_SW1;

assign duty = {duty_reg, phase_reg};

assign phase_sel = phase_reg;
assign pwmP0 = pwm_P0[0];
assign pwmP45 = pwm_P45[0];
assign pwmP90 = pwm_P90[0];
assign pwmP135 = pwm_P135[0];


endmodule

// if ( cnt_180 >= 2025 & pwm_180[0] == 0)
// begin
// end
// else
// begin
// if (phase_reg[1] == 1'd1)
// begin
// pwm_180[0] <= (cnt_180 == duty_reg ) ? ~pwm_180[0] : pwm_180[0];
// pwm_180[1] <= (cnt_180 == delay_reg) ? (sign_reg == 0) ? pwm_180[0] : ~pwm_180[0] : pwm_180[1];
// end
// end

 

// if (cnt_base == 0) pwm_base <= ~pwm_base;
//pwm_base <= (cnt_base == 0) ? ~pwm_base : pwm_base;

// if (cnt_base >= 2028 & pwm_base == 0)
// begin
// sign_reg <= sign;
// phase_reg <= duty[ 1:0];
// duty_reg <= duty[12:2];
// delay_reg <= duty[12:2] + zvs_delay;
// end
// else
// begin
// if (phase_reg[1] == 1'd0)
// begin
// if (cnt_base == duty_reg ) pwm_0[0] = ~pwm_0[0];
// if (cnt_base == delay_reg) pwm_0[1] <= sign_reg == 0 ? pwm_0[0] : ~pwm_0[0];
// end
// end


// always @(pwm_0[0] or pwm_180[0])
// begin
// //p2 <= phase_reg[1] == 1'd0 ? pwm_0[0] : pwm_180[0];//pwm[1];
// Prim_SW2 <= phase_reg[1] == 1'd0 ? pwm_0[0] : pwm_180[0];//pwm[1];
// end
//
// always @(pwm_0[1] or pwm_180[1])
// begin
// //s1 <= phase_reg[1] == 1'd0 ? pwm_0[1] : pwm_180[1];//pwm[2];
// Sec_SW1 <= phase_reg[1] == 1'd0 ? pwm_0[1] : pwm_180[1];//pwm[2];
// //s2 <= ~Sec_SW1;//~pwm[2];
// end


//assign Prim_SW2 = phase_reg[1] == 1'd0 ? pwm_P0[0] : pwm_P90[0];//pwm[1];
//assign Sec_SW1 = s1; //phase_reg[1] == 1'd0 ? pwm_0[1] : pwm_180[1];//pwm[2];
//~Sec_SW1;//~pwm[2];

 

With bets regards

 

Gerhard

0 Kudos
RichardTanSY_Intel
568 Views

I not sure about the lpm_counter structure as I don't have the visibility but a simple binary counter is just an adder with one input tied to 1 followed by a register. Our Quartus tool has the counter verilog design template in which you can just pop it into your project and use it right away. 

In the new Verilog .v file, right-click to opens the context menu, scroll to Insert Template, a window Insert Template will pop up, choose Verilog HDL > Full Designs > Counters > Binary Counter

You mentioned that the Verilog version fails, could you help to clarify what type of failure do you refer to?  Have you try to test it at 400MHz input clock in which the first version passed? 

0 Kudos
Gerhard56
New Contributor I
557 Views

Hi,

I used the LPM_COUNTER out of the IP catalog I get together with Quartus Premium lite. Here I can't look into with the TCL viewer tool. This IP works fine at 400MHz.

But I need some arithmetic, so I started with Verilog. It is also easier to change the design editing some lines of code than drawing boxes and lines (block design).

Now it fails with 400MHz and I have to go down to 100Mhz.

After verification of the design, I split it into a counter/comparer part and an arithmetic part. Arithmetic is still Verilog, but for the counter and comparer I use the LPM_XXX IP blocks out of the library.

Now it works at 400MHz again.

I want to ask the community before I started this amount of work, to draw the schematic. It also could be, that it isn't faster than the Verilog design.

But now I can say and I know, that a simple counter, which is an adder and a register after the Verilog compiler is slower than the LPM_COUNTER library block.

And if you take a look at the old logic chips (74xxx) you will see, that a binary counter didn't use an adder to simply increment by one each clock cycle ...

With best regards

Gerhard

0 Kudos
Reply