2
\$\begingroup\$

I am trying to implement switched capacitor circuits and I hence need to generate a two-phase non-overlapping clock. I have been trying to use an FPGA for the same. Unfortunately, my synthesis tool- Quartus II is throwing timing warnings. Further, when I dump the code onto the FPGA (Altera MAX7000S series FPGA), I clearly observe metastable levels and unpredictable outputs.

The code that I've written to implement this is given below:

architecture clock_gen_integrator_arch of clock_gen_integrator is signal counter_15 : STD_LOGIC_VECTOR(3 downto 0); signal phi1_sig, phi2_sig: STD_LOGIC; signal counter : STD_LOGIC_VECTOR(14 downto 0);

begin
phi1     <= phi1_sig;
phi2     <= phi2_sig;

signal_gen: process (reset, clock25M) begin
    if(reset = '0') then
        counter_15    <= (others => '0');
        counter       <= (others => '0');
        phi1_sig      <= '1';
        phi2_sig      <= '0';
        reset_int     <= '1';
    elsif(clock25M = '1' and clock25M'EVENT) then
              if(counter < "000010000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "011100000000000" and counter_15 < "1111") then
              phi1_sig      <= '1';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "100010000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "111100000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '1';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "111111111111111" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter = "111111111111111" and counter_15 < "1111") then
              counter_15    <= counter_15 + 1;
              counter       <= counter + 1;
              reset_int     <= '0';
          else 
              phi1_sig      <= phi1_sig;
              phi2_sig      <= phi2_sig;
              reset_int     <= '0';
          end if;

        end if;
    end process;
end architecture;

Unforunately, I'm not able to include the entire code here, I'm having formatting issues. However, since it's just the entity decleration, I have left it out. phi1(out), phi2(out), reset(in), reset_int(out), clock25M(in) are present in the port list.

In this code, I'm arbitrarily choosing the frequency and duty cycles of the required clocks. I specifically want 15 pulses of phi1 and 15 pulses of phi2 and counter_15 helps me achieve this.

I am being told by QuartusII that I have setup time violations. Setup time violations reported by QuartusII. There are 100 of them in total

This is the output

Sorry for the greyscale image, I had to reduce the size of the image somehow in order to upload it. The first channel is the phi1 output and the second channel is the phi2 output. Being new to the tool as well as timing analysis, I'd be grateful if someone could point out what I am doing wrong and how I can fix the timing violation. Also, any tips on how to avoid these issues in general are welcome.

\$\endgroup\$
10
  • 1
    \$\begingroup\$ Have you considered changing the code to get rid of the "<" comparisons? These are logically costly. It could be that your logic is just too slow to meet the timing. Since your counter is always incrementing, you can just pick specific points in the count for the PHI outputs to change, and do an equality comparison for those points. Equality comparisons are much faster, since they don't require an adder and the associated carry propagation delay. \$\endgroup\$
    – crj11
    Commented Jun 16, 2018 at 12:06
  • \$\begingroup\$ I don't think you simulated this because after a reset, counter is always incrementing. VHDL does not like that. \$\endgroup\$
    – Oldfart
    Commented Jun 16, 2018 at 12:12
  • \$\begingroup\$ @crj11, thank you for your comment. I had a code earlier that used equalities instead of the < operator. It had the same timing warnings and similar problems with the output; logic 0 and logic 1 weren't a single, constant voltage. During the logic 1 phase, it used to oscillate with a mean value probably equal to that of 3.3V and during the logic 0 phase with a mean value probably equal to 0V. \$\endgroup\$
    – Amogh
    Commented Jun 16, 2018 at 16:10
  • \$\begingroup\$ @Oldfart, thank you for your comment. I did simulate it as well. The presynthesis as well as the post synthesis simulations were perfect and just the way I hoped they'd be. However, regarding your observation about how I am incrementing my counter, do you mean that I should have given counter <= "00...0" when it becomes "11...1"? I tried that just now and it doesn't make a difference on either the synthesis or simulation results. \$\endgroup\$
    – Amogh
    Commented Jun 16, 2018 at 16:12
  • \$\begingroup\$ I am not familiar with Quartus so I am struggling with the timing. -11 slack and 9 delay I would interpret as a 20 (ns?) period which would be a 50MHz clock. But that does not match with -7 slack and 5 delay. The only consistence is the delta between slack & delay which is 2 (nS?) every where but that would be a 500MHz. clock. At 25MHz I would expect your circuit to meet timing. It is not that complex. \$\endgroup\$
    – Oldfart
    Commented Jun 16, 2018 at 17:11

4 Answers 4

1
\$\begingroup\$

One general method for improving timing is to split logic over multiple cycles by registering results.

You could try something like this...

signal_gen: process (reset, clock25M) begin
    if(reset = '0') then
        counter_15    <= (others => '0');
        counter       <= (others => '0');
        phi1_sig      <= '1';
        phi2_sig      <= '0';
        reset_int     <= '1';
        counter_15_not_done <= true;
        count_lt_000010000000000 <= false;
        count_lt_011100000000000 <= false;
        count_lt_100010000000000 <= false;
        count_lt_111100000000000 <= false;
        count_lt_111111111111111 <= false;
        count_eq_111111111111111 <= false;
    elsif(clock25M = '1' and clock25M'EVENT) then
          --the comparisons are computed in parallel and registered
          counter_15_not_done <= counter_15 < "1111";
          count_lt_000010000000000 <= count < "000010000000000";
          count_lt_011100000000000 <= count < "011100000000000";
          count_lt_100010000000000 <= count < "100010000000000";
          count_lt_111100000000000 <= count < "111100000000000";
          count_lt_111111111111111 <= count < "111111111111111";
          count_eq_111111111111111 <= count = "111111111111111";
          --the logic now only depends on the registered results.
          --The registered results are only 7 bits rather than 19 bits
          --this should have much better timing
          if(count_lt_000010000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_011100000000000 and counter_15_not_done) then
              phi1_sig      <= '1';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_100010000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_111100000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '1';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_111111111111111 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_eq_111111111111111 and counter_15_not_done) then
              counter_15    <= counter_15 + 1;
              counter       <= counter + 1;
              reset_int     <= '0';
          else 
              phi1_sig      <= phi1_sig;
              phi2_sig      <= phi2_sig;
              reset_int     <= '0';
          end if;

        end if;
    end process;
end architecture;

Note that the registered results will lag the actual counts by 1 clock cycle so you may need to adjust the comparison points back 1 cycle.

\$\endgroup\$
1
  • \$\begingroup\$ I had tried something along these lines; I put a temporary vector that would be assigned to as: counter_nxt <= counter + 1 on the positive edge. And later, on the negative edge(in another process) I had assigned counter <= counter_nxt. However, that hardly made a difference. I just tried your code as well on the hardware. While it simulates perfectly on my laptop, the timing analyser still shows timing violations and it doesn't yield expected results on the hardware. I'm now beginning to doubt if everything's fine with the FPGA. Thank you for your reply, I learnt something new! \$\endgroup\$
    – Amogh
    Commented Jun 17, 2018 at 5:06
1
\$\begingroup\$

Equality is implemented with fewer 'gate' levels of delay than magnitude ("<") which involves subtraction and won't get completely optimized away if there are operand values greater than the largest static magnitude operand. (Highest counter comparison operand is x"7800", counter highest count is x"7FFF".)

Use equality comparisons only, for example:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;          -- ADDED

entity clock_gen_integrator is
    port (
        clock25M:   in  std_logic;
        reset:      in  std_logic;
        reset_int:  out std_logic;
        phi1:       out std_logic;
        phi2:       out std_logic
    );
end entity;

architecture foo of clock_gen_integrator is 

    signal counter_15:  unsigned (3 downto 0); -- WAS std_logic_vector
    signal phi1_sig:    std_logic;
    signal phi2_sig:    std_logic; 
    signal counter:     unsigned(14 downto 0); -- WAS std_logic_vector 
    signal notcount15:  boolean;
    signal notcount15h: boolean; 
begin

    phi1 <= phi1_sig;
    phi2 <= phi2_sig;

    notcount15 <= counter_15 /= 15;  -- 4 input NAND gate

signal_gen: 
    process (reset, clock25M) 
    begin
        if reset = '0' then
            counter_15    <= (others => '0');
            notcount15h     <= true;
            counter       <= (others => '0');
            phi1_sig      <= '1';
            phi2_sig      <= '0';
            reset_int     <= '1';
        -- elsif clock25M = '1' and clock25M'EVENT then 
        elsif rising_edge (clock25M) then
            -- the counters:
            counter <= counter + 1;
            if counter = x"3800" and notcount15 then
                counter_15 <= counter_15 + 1;
            end if;
            -- notcount15h holdover flip flop for phi2
            if counter = x"3800" and not notcount15 then
                notcount15h <= false;
            end if;
            -- reset_int flip flop:
            reset_int <= not reset;
            -- toggle phi1:
            if counter = x"400"  and notcount15 then
                phi1_sig <= '1';
            elsif counter = x"3800" and notcount15 then
                phi1_sig <= '0';
            end if;
            -- toggle phi2:
            if counter = x"4400" and notcount15h then
                phi2_sig <=  '1';
            elsif counter = x"7800" and notcount15h then
                phi2_sig <= '0';
            end if;  
            -- four equality comparators to specific 15 bits of counter
        end if;
    end process;
end architecture;


library ieee;
use ieee.std_logic_1164.all;

entity clock_gen_integrator_tb is
end entity;

architecture foo of clock_gen_integrator_tb is
    signal clock25M:   std_logic := '0';
    signal reset:      std_logic := '1';
    signal reset_int:  std_logic;
    signal phi1:       std_logic;
    signal phi2:       std_logic;
begin

CLOCK:
    process
    begin
        wait for 20 ns;
        clock25M <= not clock25m;
        if now > 21 ms then
            wait;
        end if;
    end process;
DUT:
    entity work.clock_gen_integrator
        port map (
            clock25M => clock25M,
            reset => reset,
            reset_int => reset_int,
            phi1 => phi1,
            phi2 => phi2
        );

STIMULUS:
    process
    begin
        wait for 30 ns;
        reset <= '0';
        wait for 80 ns;
        reset <= '1';
        wait;
    end process;

end architecture;

The testbench demonstrates 15 phi1 and phi2 clocks (" I specifically want 15 pulses of phi1 and 15 pulses of phi2 and counter_15 helps me achieve this") on the same counter values as in the original architecture clock_gen_integrator_arch:

clock_gen_integrator_tb.jpg

\$\endgroup\$
1
  • \$\begingroup\$ I did try your suggested code on the hardware and the voltage levels weren't constant. I also observe some small pulses after the fifteen clock cycles. I'm now doubting my hardware. Also, thank you for the recommended changes; looks like I've still got a lot to learn. \$\endgroup\$
    – Amogh
    Commented Jun 17, 2018 at 4:58
1
\$\begingroup\$

All the lower bits in your transition points are zero (not counting the instance where you clear phi2 twice, so you can divide the clock a few times and use a smaller counter.

You can also split up generation of a continuous train of pulses of the desired length (lower order bits of the counter) and routing to an output (higher order bits), e.g. 16 pulses with an \$\frac{1}{8}\$ duty cycle and two outputs could be achieved with an 8-bit counter (not even compile tested, but you should get the idea):

pulse <= '1' WHEN counter(2 downto 0) = "000" ELSE '0';
output <= counter(3);
phi1 <= pulse AND NOT output;
phi2 <= pulse AND output;

Have counter run upwards and stop after "11111111".

Depending on what else is going on in the design, I'd probably also use a PLL to derive a slow (1 MHz) clock and divide from there.

\$\endgroup\$
0
\$\begingroup\$

Unfortunately this is a poor implementation of a design that has no specifications or tolerances. Always start with design specifications and tolerances before attempting to design a solution.

Your lack of impedance design (RdsOn or switch ESR) and sag from leakage show evidence of quantized ringing and commutation noise.

Choice of caps should not be ceramic which have hysteresis and microphonic issues in S&H designs, unless NP0 type.

Given;

switched capacitor circuits and I hence need to generate a two-phase non-overlapping clock

What are the following? Include expected tolerances for each.

  • desired filter responses and tolerance
  • input clock f
  • output clock f
  • output clock current limit
  • output load capacitance (hence rise time)
  • output delay skews from latency worst case
  • switch resistance
  • switched capacitance, hence rise time
  • non-overlapping deadtime of bi-phase clocks
  • effect on RC time constant with dead-time (RdsOn/d)
  • required Pulse Width timing for leading and trailing edges at V+/2 or “PW50”
  • sensitivity to above with temperature and supply voltage.

The more specs you define up front that may cause errors, the better chance you get it right , the first time. Otherwise iterations on design specs and retry or confusion when it fails.

Now which method will be easiest to create these requirements ? Analog ? As done by all full bridge designs or digital with quantization time resolution limits or ?

What circuits has you read that are published in CMOS App books that already work? And if not, why not?

p.s.
There are many tools to change image size or compression ratio to reduce file size and retain 32 bit colour.

Increasing distance and steady hand with camera then auto focus , straight to monitor will give clearest image , then crop as needed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.