Ring Oscillator

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA project was completed in May 2015, with inspiration from a fellow FPGA user Shahin.


What is a ring oscillator?

A ring oscillator is made when you have an unstable loop in your logic and it spontaneously oscillates. The easiest way to create one is to connect the output of a NOT gate back into it's own input, and if the it will oscillate at a frequency determined by propagation delay of gate.

Normally a longer chain of logic is used (e.g. 6 buffers and a not gate), which not only gives a cleaner square-wave output, but allows you to determine the propagation delay more accurately.

Creating one in and FPGA using VHDL is hard, as the tools don't like to synthesize the structure, and the optimizer can remove all the redundant logic. But with a bit of persistence it can be made to work.

Here's the floorplan of the FPGA with the ring highlighted:


Here's how I made it...

How is it implemented

The ring oscillator has an AND get in the loop, with one of its inputs acting as an enable signal for the oscillator.

A eight-bit counter is driven by the ring oscillator, and the counter has an async reset, allowing it to be cleared from a different clock domain.

A counter in the 32 MHz clock domain is used to perform the following actions

  • Enable the oscillator
  • Leave it running for 100 cycles.
  • Disable the oscillator
  • Wait for a short while
  • Read the count of how many cycles have been occurred in the ring oscillator, and display it on the LEDs
  • Reset the count
  • Wait a long while - a little over 0.5 s - so you can observer a static set of LEDs.

And that is it!

Source files

First, here are the source files.


library IEEE;

Library UNISIM;
use UNISIM.vcomponents.all;

entity ring_test is
    Port ( clk          : in  STD_LOGIC;
           led : out  STD_LOGIC_VECTOR (7 downto 0));
end ring_test;

architecture Behavioral of ring_test is
   COMPONENT my_and
      a : IN std_logic;
      b : IN std_logic;          
      o : OUT std_logic

   COMPONENT my_not
      a : IN std_logic;
      o : OUT std_logic

   signal signals       : std_logic_vector(75 downto 0) := (others => '0');
   signal reset_count   : std_logic := '0';
   signal enable        : std_logic := '0';
   signal ref_counter   : unsigned(23 downto 0) := (others => '0');
   signal pulse_counter : unsigned(9 downto 0) := (others => '0');

g1: for i in 1 to signals'high generate
i_my_not : my_not port map ( 
      a => signals(i-1), -- LUT input
      O  => signals(i)    -- LUT general output
end generate;

i_my_and: my_and PORT MAP(
      a => enable,
      b => signals(signals'high),
      o => signals(0)

      if reset_count = '1' then
         pulse_counter <= (others =>'0');
      elsif rising_edge(signals(signals'high)) then
         pulse_counter <= pulse_counter+1;
      end if;
   end process;

control_proc: process(clk)
      if rising_edge(clk) then
         case to_integer(ref_counter) is
            when      0 => enable       <= '1';
            when    100 => enable       <= '0';
            when    128 => led(7 downto 0) <= std_logic_vector(pulse_counter(7 downto 0));
            when    200 => reset_count  <= '1';
            when    230 => reset_count  <= '0';
            when others => NULL;
         end case;
         ref_counter <= ref_counter+1;
      end if;
   end process;

end Behavioral;


library IEEE;

entity my_not is
    Port ( a : in  STD_LOGIC;
           o : out  STD_LOGIC);
end my_not;

architecture Behavioral of my_not is
   o <= not a;
end Behavioral;


library IEEE;

entity my_and is
    Port ( a : in  STD_LOGIC;
           b : in  STD_LOGIC;
           o : out  STD_LOGIC);
end my_and;

architecture Behavioral of my_and is

   o <= a and b;
end Behavioral;


NET CLK            LOC="P94"  | IOSTANDARD=LVTTL | PERIOD=31.25ns;               # CLK
NET LED(7)         LOC="P123" | IOSTANDARD=LVTTL;                                # C8
NET LED(6)         LOC="P124" | IOSTANDARD=LVTTL;                                # C9
NET LED(5)         LOC="P126" | IOSTANDARD=LVTTL;                                # C10
NET LED(4)         LOC="P127" | IOSTANDARD=LVTTL;                                # C11
NET LED(3)         LOC="P131" | IOSTANDARD=LVTTL;                                # C12
NET LED(2)         LOC="P132" | IOSTANDARD=LVTTL;                                # C13
NET LED(1)         LOC="P133" | IOSTANDARD=LVTTL;                                # C14
NET LED(0)         LOC="P134" | IOSTANDARD=LVTTL;                                # C15

Compiler options

To ensure the VHDL tools don't optimize your logic out you need to set the "Keep hierarchy" option in the design's Synthesis settings.


The LEDs alternated between 0x2D (45) and 0x2E (46). As the Papilio Plus's clock speed is 31.25 ns and I let the oscillator run for 100 cycles (3,125 ns), giving the ring's period of 67.93 ns. The ring consists of 75 NOT gates and an AND gate (each implemented in a LUT) giving a per-LUT delay of 0.893 ns (which includes the routing delays).

I then updated the code to have a ring of 49 NOT gates and an AND gate, and while it was building calculated that it should have a period of about 41.4 ns. This should give a count of around 0x4B (75) or 0x4C (76) displayed on the LEDs. It actually got 0x4E or 0x4F - just a tiny bit faster than predicted, so I'm pretty happy that this is working correctly.

Note to self - the lotto numbers were - 8 10 14 17 33 34 (9) - must check my ticket later!

Personal tools