- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

Link Copied

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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?

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page