Thursday, December 8, 2011

Synchronization in sequential circuits (clock dividers)

    Oscillators and frequency
    A very important component of any FPGA development board is the crystal oscillator. The oscillator emits a sequence of 0,1 at a given time based on it's frequency. Oscillators are used in lots of electronic devices like TVs, radios, cell phones, computers and others. For example when you buy a PC or a processor and you read that it's frequency is 2 GHz (gigahertz) it means that the oscillator has a frequency of 2 GHz. It means that the oscillator emits 2 billion sequences of 0,1 signals.
    Frequency can be used to measure time as well. The formula to transform from frequency in time is:
    T is the period and it's measured in seconds and frequency is measured in hertz.
    1 Hz is equal to 1 second (1=1/1).
    A 1 Hz signal i represented like so:

    If you want to read in more detail about oscillators you can read the Wikipedia article.

    Though we haven't used it yet, almost all FPGA projects use the oscillator either for synchronization of different circuits or in order to use some I/O circuits like the serial port or the VGA port. The signal we get from the oscillator is called a clock signal and abbreviated clk in code. Synchronization is important because in complex designs it assures you that commands are not executed in arbitrary mode by the components of the main circuit. Xilinx recommends synchronizing signals where you can and using asynchronous signals only if necessary.
    Example of a register  using synchronous signals and asynchronous signals:

                  if(clk'event and clk='1') then
                      if(reset='1') then
                      end if;
                  end if;
              end process;

    This is the synchronous design. as you can see in the first if the clk signal is tested. The 'event keyword is used in order to see if a signal changed it's value (from 0 to 1 or from 1 to 0) and then we see if with the event on the signal it's value is now 1. You can test if clk='0' if you want but it's advised to use non inverted logic.
    clk'event and clk='1' can be written as rising_edge(clk) and clk'event and clk='0' can be written as falling_edge(clk).
    In this process all actions are synchronized with the clk signal. This means that if the clk hasn't changed it's value to 1, the other statements are not executed.
    You can tell that this circuit is synchronized from 2 things:
       - the sensitivity list of the process contains only the clk signal so it's executed only when it changes it's value
       - all the other statements are inside the first if. This means that the state of the clk controls the other signal tests and assignments. This is why when using lots of circuits synchronized with the same clk the signals won't be assigned as random signals change their values.

    Let's see the same circuit but now with an asynchronous reset signal:

                   if(reset='1') then
                  elsif(clk'event and clk='1') then
                  end if;
              end process;

    As you can see the process is executed now when clk changes it's value or when reset changes it's value. The reset is checked first and if it's 1 then the register will be reset. If the reset is not active then the clk signal is tested. 
    I recommend using the first design.

    Clock dividers
    These circuits are important because you will usually need more than 1 clock signal in a design. For example consider you have the global clock of your board at 50 Mhz and you will use it to design a VGA port and a serial port. In order to use these peripherals you will need a 25 MHz clock for the VGA and usually a 9,6 KHz clock for the serial port. You will need 2 clock dividers in order to obtain the 2 frequencies needed.
    Often you will want to count time like seconds or milliseconds. In order to do this you need to divide the clock so that you get the frequency resulted from the formula from the beginning of the tutorial.
    It is advised to use the hertz and seconds for f and T in order to avoid any mistakes.

    Time conversions:
    1 millisecond=0.001 seconds
    1 microsecond=0.000 001 seconds
    1 nanosecond=0.000 000 001 seconds

    Basic frequency-time conversion:
    1 Hz <=> 1 second
    10 Hz <=> 0.1 seconds
    100 Hz <=> 0.01 seconds
    1 KHz (1 000 Hz) <=> 1 millisecond
    1 MHz (1 000 000 Hz) <=> 1 microsecond
    1 GHz (1 000 000 000 Hz) <=> 1 nanosecond

    These 2 tables are important when you want to check that you made the right division for obtaining a certain time. 
    Let's take an example: Been given a dev board with the global clock of 100 MHz, divide it so that a LED of the board is on half a second and off the other half. 
    First we need to calculate the frequency needed using the formula:
    f=1/0.500=2 Hz
    0.500 seconds or half a second is equal to 2 Hz.
    The type of divider we will implement is a counting divider so we need a constant to count witch is calculated as so:
    gclk is the global clock frequency and clk is the desired frequency so the constant is:
    c=100 000 000/2=50 000 000
    So each time the global clock changes it's value and is 1 we increment the counter and when it reaches 50 000 000 or 49 000 000, depending on the design used, the clk signal gets the value 1. For any other variables the clk will be 0.

    Now it's time to design the circuit. It has 2 inputs (clk and reset) and one output (clko).

    Now let's see the process:

    As you can see this circuit is synchronized with the clk signal. The variable cnt is used to count the clock signals so that we can divide it. I recommend using the type std_logic_vector for the variable type. It's 26 bits in width because you need 26 bits in order to form the number 49 000 000. In order to see the number of bits a number is formed with you can write it in the Calculator application from Windows and then select View->Programmer and check Bin. The number will be converted to binary.
    When reset is active the variable gets the value 0 and the output clock is 0. As cnt is incremented, the output clock stays 0 but when it reaches the value needed, the output clock will be 1.
    In order to use the + on std_logic_vector types you need to include a new library:


    A test circuit
    Let's continue the example. Now that we have the divided clock we need to link it to the circuit which controls the LED. 
    A diagram of the circuit:
    The led_driver represents a process that has an asynchronous reset input and the divided clk input. The output connects to the LED.
    Let's start. Create another VHDL module in the project and declare the div component in it. We will also declare a signal that links the output of the div circuit to the driver.
    Let's see the architecture body:

    The led output is connected to the l variable. When reset is 1, l gets the value 0. When the divided clk changes it's value in 1, l is inverted so that the LED is on half a second and then off the other half and so on.

No comments:

Post a Comment