Category Archives: VHDL

Home / VHDL
4 Posts

This article discusses how to create a clock divider in VHDL using the counter method. A clock divider is also known as a frequency divider. I also provide the source code for a simple and configurable clock divider implementation.

A clock divider takes an input clock with a given frequency and produces an output clock with some lower, divided frequency.

Scaling factor

The first thing that needs to be determined is the scaling factor. The scaling factor is simply the ratio of the input clock frequency and the desired output clock frequency. For this article, the scaling factor will be abbreviated as the variable N.

\frac{Input Clk}{Output Clk} = Scaling Factor (N)

N represents the number of input clock cycles needed for one output clock cycle. In other words, for every N cycles on the input clock, one cycle on the output clock is generated. Remember that a clock signal is a square wave with a 50% duty cycle. This means that, in one cycle, half of the time is spent high (active) and half of the time is spent low (inactive). Therefore, for a scaling factor of N, the output clock will be set high for N/2 input clock cycles and set low for N/2 input clock cycles.

A clock divider implementation therefore involves using a counter to count to N/2 cycles on the input clock. Once it hits N/2, it toggles the output clock and resets the counter. Note that N must be an even integer in order to have a precise clock divider. If N is not an even integer, you can truncate it to the nearest one and in many cases this will still provide a fairly accurate output.

Clock Divider Source Code

This post discusses the design for a FPGA I2C Slave implementation in VHDL. The final source code for this design can be found in this post here. This I2C Slave implementation provides basic read, write, and addressing functionality. The address of the slave is configurable through a generic. The implementation supports repeated START conditions, but it does not support other "advanced" features such as clock stretching and 10-bit addressing.

The final implementation has been tested on an Altera Cyclone IV FPGA, using a Raspberry Pi 2 as the I2C master.

Learning I2C

This post requires a basic knowledge of the I2C bus. I will not be teaching about or providing a tutorial for I2C here, as there are already many great resources on the internet for learning about it.

The ultimate authority for I2C will always be the specification and user manual. However, this document might be too detailed for those who are only looking for a basic overview. Personally, I recommend any of the following three resources for learning I2C.

  • Sparkfun I2C Tutorial.
  • ESAcademy I2C Bus Overview.
  • Columbia Lecture Presentation.
  • I2C Slave VHDL Interface

    The VHDL implementation will provide the following interface for usage:

    The naming of the signals here may be a little confusing. I2C transactions take place from the master's perspective. Therefore, a READ command means that the master is reading data from the slave (i.e the slave is sending data to the master). A WRITE command means that the master is writing data to the slave (i.e the slave is receiving data from the master). In the interface defined above, however, the naming is done from the perspective of the slave. By convention, TX refers to transmitting data and RX refers to receiving data. The signals that begin with "tx_" are used when the slave is transmitting data to the master (during a READ command). The signals that begin with "rx_" are used when the slave is receiving data from the master (during a WRITE command).

    tx_done is an indicator signal that goes high when the I2C Slave module has finished transmitting data to the master. The data to be sent to the master must be set in tx_byte.

    rx_data_rdy is an indicator signal that goes high when data is received from the master. The received data can be found in rx_byte.

    clk is the clock signal for the I2C Slave module itself. This should not be confused with SCL, which is the I2C-bus clock line. This will be explained more in the next section. For best performance, clk should be significantly faster than SCL.

    in_progress is an output indicator signal I decided to add, as it may be useful in some applications. It goes high whenever there is activity on the bus that is addressed to the slave.

    I2C Slave FSM Design

    This I2C Slave implementation uses a fast clock to oversample the SDA and SCL signals. This is more reliable than using the SCL line itself as the FPGA's clock signal. There are four particular events that need to be captured. The first two are the rising and falling edges of the SCL clock. These edges trigger transitions between states in the FSM. The next two are the START and STOP conditions. These occur when the SCL line is high, so they must be captured separately. Four strobe signals are used to capture these events. These strobe signals go high to indicate that the event has occurred.

    Using these four strobe signals, it is easy to implement a state machine for the I2C Slave. The FSM uses eight states. The states and their general functions are listed below.

  • IDLE - No bus activity that is addressed toward the slave. The slave is waiting for a START condition in this state.
  • READ_ADDRESS - Reads the 7 bit address and the R/W bit from the master. Also determines whether the address is a match
  • SEND_ACK - Sends an ACK bit by holding SDA low through one SCL cycle.
  • WRITE_CMD - Reads one byte of data from master by polling SDA line.
  • READ_CMD - Writes one byte of data to master by setting SDA line.
  • WAIT_ACK_1 - Waits for ACK bit from master. This requires two states.
  • WAIT_ACK_2
  • WAIT_STOP - A NACK was received during a READ command. Therefore, the I2C shouldn't do anything and should just wait for a STOP bit.
  • Below is a diagram of the FSM. Note that this FSM diagram does not show all the transitions that occur because of START and STOP conditions. When a START condition is received, the FSM immediately transitions to the READ_ADDRESS state to begin a new transaction. This allows Repeated Start Conditions to work. When a STOP condition is received, the FSM immediately goes into the IDLE state. These transitions due to START and STOP conditions can occur at any point and in any state of the FSM. They are not shown in the FSM diagram because it would be too messy.

    microps_i2c_fsm

    Note that the FSM diagram contains some internal strobes and logic signals for its transitions. scl_falling and scl_rising are the two strobe signals for the rising and falling edges of the SCL line, as previously discussed. rw is a signal that holds the value of the R/W bit. It is set during the READ_ADDRESS state. bit_count is a counter. Every transaction involves a single byte that is clocked in or out one bit at a time. The counter keeps track of the number of bits received or sent and transitions between states after a full byte. continue_read is a signal that goes high when the I2C slave should continue reading from the master during a WRITE command. This depends on whether a ACK or a NACK was received after reading a byte.

    Finally, because the I2C bus lines are open-drain, a tri-state buffer must be used to manage when the I2C slave pulls the SDA line low. An enable signal, sda_out_en is high when the slave has control of the SDA line. sda_out_en is high only when the slave is sending data during the SEND_ACK and READ_CMD states. The tri-state buffer is simple to implement:

    For the full source code, click here.

    Below I provide the entire source code for a FPGA I2C Slave implementation in VHDL. To learn more about the implementation's design and FSM, see this post here.

    This I2C Slave implementation provides basic read, write, and addressing functionality. It also supports repeated start conditions. The address of the slave is configurable through a generic. Note that this implementation does not currently support "advanced" features such as clock stretching. It also does not support 10-bit addressing, although I'd imagine it wouldn't be too hard to implement this.

    This implementation has been tested on an Altera Cyclone IV FPGA. A Raspberry Pi 2 was used as the I2C master.

    How to use

    The I2C Slave FSM provides the following interface:

    tx_done goes high when the I2C Slave module has finished transmitting data to the master. In other words, it signals the completion of a READ command (master reads from slave). The data to be sent to the master must be set in tx_byte.

    rx_data_rdy goes high when data is received from the master. In other words, it indicates the completion of a WRITE command (master writes to slave). The received data can be found in rx_byte.

    clk is the clock signal for the I2C Slave module itself. This should not be confused with SCL, which is the I2C-bus clock line. For best performance, clk should be significantly faster than SCL.

    Source Code

    This article discusses how to convert between the common VHDL types. The most common VHDL types are std_logic_vector, signed, unsigned, and integer. The std_logic and std_logic_vector types are defined in the standard logic 1164 package of the IEEE library. This package can be imported as follows:

    library IEEE;
    use IEEE.STD_LOGIC_1664.ALL

    The signed and unsigned types can be found in the numeric_std package. This package can be imported using the code below. The signed type is represented in two's complement form. The source file for the numeric_std package can be found here. For more information on two's complement, check the Wikipedia page.

    use ieee.numeric_std.all

    VHDL is a strongly-typed language, and does not have automatic type conversion. As a result, converting between different types is often necessary. The libraries above provide type casts and conversion functions between the common VHDL types. These are depicted by the image below.

    How to convert between the most common VHDL types

     

    It is important to note that casting between a std_logic_vector and a signed/unsigned type requires that both signals have the same bit width. Integers do not have a set bit width. Therefore, in the to_signed and to_unsigned conversion functions, note that the first argument is the integer that is being converted and the second argument specifies the bit width of the resulting signed/unsigned type.

    It is important to realize that there is no direct conversion between signed and unsigned types. There is also no direct conversion between the std_logic_vector and integer types.

    Using these conversions and casts is straightforward. Below is example code illustrating their usage:

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

    signal stdlvec : std_logic_vector(31 downto 0);
    signal unsgn : unsigned(31 downto 0);
    signal sgn : signed(31 downto 0);
    signal int : integer;


    --Conversion from std_logic_vector to signed and unsigned types
    unsgn <= unsigned(stdlvec);
    sgn <= signed(stdlvec);


    --Conversion from signed/unsigned types back to std_logic_vector
    stdlvec <= std_logic_vector(unsgn);
    stdlvec <= std_logic_vector(sgn);


    --Conversion from signed/unsigned types to integer
    int <= to_integer(sgn);
    int <= to_integer(unsgn);


    --Conversion from integer back to 32-bit signed/unsigned types
    unsgn <= to_unsigned(int, 32);
    sgn <= to_signed(int, 32);