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:

One thing this example points out is that all VHDL structural models capable of simulation are described behaviorally.
clockorclocktoggle events. The 'high speed' clock would come from a testbench or the simulator itself. - user1155120clockof '0' and the counter of (others => '0'). at count = 5 (25 ns), 7 (35 ns), 10 (50 ns) a combinatorial signaltogglegoes 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. Thetogglesignal is an enable for a D FF (clockis 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