Forum Discussion

Gerhard56's avatar
Gerhard56
Icon for Occasional Contributor rankOccasional Contributor
5 years ago

Block design file v. Verilog

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

4 Replies

  • sstrell's avatar
    sstrell
    Icon for Super Contributor rankSuper Contributor

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

    • Gerhard56's avatar
      Gerhard56
      Icon for Occasional Contributor rankOccasional Contributor

      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

  • 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?

    • Gerhard56's avatar
      Gerhard56
      Icon for Occasional Contributor rankOccasional Contributor

      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