2
votes

My question is relatively simple: is it possible in VHDL to generate a clock with a specific, non-regular pattern without using a process and the after keyword?

In other words, if I have a process that generates a clock like this:

process(Start)
    begin   
        if Start = '0' then 
            clock <= '0';
        else if Start = '1' then
            clock <= '0',
            '1' after 25 ns,
            '0' after 35 ns,
            '1' after 50 ns,
            '0' after 75 ns,
            '1' after 105 ns,
            ...
end process;

can I get the same output using (for istance) some components, like some sort of delays?

I know that probably it wouldn't be of any use, but my professor asked us to replicate this exercise using structural VHDL. I just need to simulate it, I don't need to make it synthesizable.

3
Consider a counter, a clock (the Most Common Denominator appears to be 5 ns, noting your entire pattern is not shown), and gates to either produce the low baud or high baud combinatorially fed through a clocked flip flop to generate clock or clock toggle events. The 'high speed' clock would come from a testbench or the simulator itself. - user1155120
@user1155120 Thanks for the answer. I think I got your idea, but how do I set the counter to count for different time intervals? Would I need multiple counters? And can you please explain better what should I do with the counter(s) output? - Alessandro
Imagine a counter that counts 5 ns period clocks and an initial value for clock of '0' and the counter of (others => '0'). at count = 5 (25 ns), 7 (35 ns), 10 (50 ns) a combinatorial signal toggle goes true ('1'),... with others toggle set to false ('0'). If the clock is continuous you pick a count to restore the count to all '0's. The toggle signal is an enable for a D FF (clock is the output) with it's Qnot connected to D run off the high speed clock (supplied externally from a testbench). The size of count is determined last even in 5 ns increments. - user1155120

3 Answers

2
votes

Imagine a counter that counts 5 ns period clocks and an initial value for clock of '0' and the counter of (others => '0'). at count = 5 (25 ns), 7 (35 ns), 10 (50 ns) a combinatorial signal toggle goes true ('1'),... with others toggle set to false ('0'). If the clock is continuous you pick a count to restore the count to all '0's. The toggle signal is an enable for a D Flip Flop (clock is the output) with it's Qnot connected to D run off the high speed clock (supplied externally from a testbench). The size of count is determined last even in 5 ns increments.

Well we'd adjust the count comparison values down by one to compensate for the D Flip Flop and it would look something like this structurally:

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

entity counter is           -- counts 0 to 31
    port (
        clk:    in  std_logic;
        count:  out std_logic_vector (4 downto 0);
        zero:   out std_logic
    );
end entity;

architecture foo of counter is
    signal cnt:     unsigned (count'range) := (others => '0'); -- start at zero
    constant period:    integer := 31;  -- 32 is the period of the the sequence
begin
    process (clk)
    begin
        if rising_edge (clk) then
            if cnt < period then
                cnt <= cnt + 1;
            else
                cnt <= (others => '0');
            end if;
        end if;
    end process;

    count <= std_logic_vector(cnt);

    zero <= '1' when cnt = 0 else  
            '0';
end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity d_flip_flop is   -- has enable
    port (
        resetn: in  std_logic;
        clk:    in  std_logic;
        en:     in  std_logic;
        d:      in  std_logic;
        q:      out std_logic
    );
end entity;

architecture foo of d_flip_flop is
    signal qn: std_logic := '1';
begin
    process (resetn, clk)
    begin
        if resetn = '0' then
            qn <= '1';
        elsif rising_edge (clk) and en = '1' then
            qn <= not d;
        end if;
    end process;
    q <= not qn;
end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity inv is
    port (
        a:  in  std_logic;
        o:  out std_logic
    );
end entity;

architecture foo of inv is
begin
    o <= not a;
end architecture;


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

entity toggle_pla_rom is
    port (
        count:  in  std_logic_vector (4 downto 0);
        toggle: out std_logic
    );
end entity;

architecture foo of toggle_pla_rom is
    type rom_array is array (0 to 31) of std_logic;   -- fully constrained
    constant rom: rom_array := ( 4 | 6 | 9 | 14 | 20 => '1', others => '0');
begin
    toggle <= rom(to_integer(unsigned(count)));
end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity clock_gen is
    port (
        clk:    in  std_logic;
        clock:  out std_logic
    );
end entity;

architecture foo of clock_gen is
    signal toggle:  std_logic;
    signal resetn:  std_logic;
    signal din:     std_logic;
    signal count:   std_logic_vector (4 downto 0);
    signal q:       std_logic;
    signal nq:      std_logic;
    signal zero:    std_logic;
begin
CNTR:
    entity work.counter
        port map (
            clk => clk,
            count => count,
            zero => zero
        );

DIN_INV:
    entity work.inv
        port map (
            a => q,
            o => nq
        );

ZERO_INV:
    entity work.inv
        port map (
            a => zero,
            o => resetn
        );
DFF:
    entity work.d_flip_flop
        port map (
            resetn => resetn,
            clk => clk,
            en => toggle,
            d  => nq,
            q => q
        );

TOGGLE_LUT:
    entity work. toggle_pla_rom
        port map (
            count => count,
            toggle => toggle
        );

    clock <= q;

end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity clock_gen_tb is
end entity;

architecture foo of clock_gen_tb is
    signal clk:     std_logic := '1';  -- clk has 5 ns period
    signal clock:   std_logic;
begin
CLOCKGEN:
    entity work.clock_gen
        port map (
            clk => clk,
            clock => clock
        );

CLKGEN:
    process
    begin
        wait for 2.5 ns;
        clk <= not clk;
        if now > 300 ns then
            wait;
        end if;
    end process;

MONITOR:
    process 
    begin
        wait for 0 ns;
        wait until clock'event;
        report "clock = " & std_logic'image(clock);
    end process;
end architecture;

The look up table used to determine the count values where a toggle occurs could be replaced with and/ors, etc. for true primitive level description.

The counter wraps, which is easy to cure (it could stop instead for instance). Because you didn't specify any further edge events it's tailored to the size needed for the number of edge events you did specify. The size of the counter and the number of events can be scaled up.

When run the clock_gen_tb testbench reports clock events and shows that it meets your specification:

logarithmic_clock.vhdl:188:9:@0ms:(report note): clock = '0' logarithmic_clock.vhdl:188:9:@25ns:(report note): clock = '1' logarithmic_clock.vhdl:188:9:@35ns:(report note): clock = '0' logarithmic_clock.vhdl:188:9:@50ns:(report note): clock = '1' logarithmic_clock.vhdl:188:9:@75ns:(report note): clock = '0' logarithmic_clock.vhdl:188:9:@105ns:(report note): clock = '1' logarithmic_clock.vhdl:188:9:@160ns:(report note): clock = '0' logarithmic_clock.vhdl:188:9:@185ns:(report note): clock = '1' logarithmic_clock.vhdl:188:9:@195ns:(report note): clock = '0' logarithmic_clock.vhdl:188:9:@210ns:(report note): clock = '1' logarithmic_clock.vhdl:188:9:@235ns:(report note): clock = '0' logarithmic_clock.vhdl:188:9:@265ns:(report note): clock = '1'

The first 6 times match your specification.

A waveform display shows the counts, toggles, zero (representing the flip flop reset) and clock:

clock_gen_tb.png

One thing this example points out is that all VHDL structural models capable of simulation are described behaviorally.

1
votes

ok, you could try to use others keywords to generate a delay like wait for and loop inside of process block. This is an example:

clock_gen: process
   constant duty_cycle : real := 0.40
   constant period : time := 100 ns ;
   constant clk_high : time := duty_cycle * period ;
begin
   loop
      clk <= '0';
      wait for period - clk_high; -- clock low time
      clk <= '1';
          wait for clk_high; -- clock high time
      end loop;
    end process;
end behavioral;

This is a clock with a variable duty cycle and period.

0
votes

Creating a clock without a process doesn't make any sense (structural VHDL doesn't provide any kind of synchronization), however you can simulate the special clock pattern by applying the right logic to some regular clock signals provided by a testbench for example.

For example consider having an input of 2 clocks (one start at 0, the other at 1), this will give you the possibility to implement a 2 state machine, think of it like a counter from based on the periodicity of your source clock..

You can also have 2 clocks sources where the periodicity of the second clock is twice the first one to get a 2^2 state machine, in this case you are free to start both clocks at low level (0).

This way you can implement the needed pattern without using any process (no behavioral VHDL is used).