i2c_master VHDL Module ====================== Overview -------- The ``i2c_master`` module is a synthesizable, parameterizable I2C master controller written in VHDL. It supports 7-bit addressing, single-byte read/write transactions, and implements essential I2C protocol features such as start/stop condition generation, acknowledge checking, and clock stretching. The design is suitable for integration into FPGA or ASIC projects requiring I2C master functionality. This module is based on the open-source implementation by user "rick" on the `Digi-Key TechForum `_, with minor modifications for documentation and maintainability. Key Features ------------ - **Parameterizable Clocking:** The module accepts generics for the input system clock (`input_clk`) and desired I2C bus clock (`bus_clk`), allowing flexible adaptation to different hardware platforms. - **I2C Protocol Compliance:** Implements start and stop conditions, address and data transfer, acknowledge checking, and clock stretching as per the I2C specification. - **Single-Byte Transactions:** Each enable (`ena`) pulse initiates a single-byte read or write transaction. Repeated start and multi-byte transactions can be implemented by controlling `ena` and updating address/data inputs. - **Bus Arbitration:** The module does not support multi-master arbitration; it is intended for single-master applications. - **High-Impedance Bus Control:** SCL and SDA lines are driven low only when required; otherwise, they are set to high-impedance ('Z'), allowing for proper I2C open-drain operation. Module Overview --------------- This section provides an overview of the firmware module implementing the I2C master controller. Below is a diagram of the input and output signals, followed by a table detailing each signal. .. vhdlentity:: :file: ../Firmware/Source/modules/i2c_master/hdl/i2c_master.vhd :entity: i2c_master Generics -------- - **input_clk** (INTEGER): Input system clock frequency in Hz (default: 50,000,000). - **bus_clk** (INTEGER): Desired I2C bus clock (SCL) frequency in Hz (default: 400,000). Ports ----- - **clk** (IN STD_LOGIC): System clock. - **reset_n** (IN STD_LOGIC): Active-low synchronous reset. - **ena** (IN STD_LOGIC): Latch command to initiate a transaction. - **addr** (IN STD_LOGIC_VECTOR(6 DOWNTO 0)): 7-bit I2C slave address. - **rw** (IN STD_LOGIC): Read/Write control ('0' = write, '1' = read). - **data_wr** (IN STD_LOGIC_VECTOR(7 DOWNTO 0)): Data byte to write. - **busy** (OUT STD_LOGIC): Indicates transaction in progress. - **data_rd** (OUT STD_LOGIC_VECTOR(7 DOWNTO 0)): Data byte read from slave. - **ack_error** (BUFFER STD_LOGIC): Acknowledge error flag (set if slave does not acknowledge address or data). - **sda** (INOUT STD_LOGIC): I2C serial data line (open-drain). - **scl** (INOUT STD_LOGIC): I2C serial clock line (open-drain). Implementation Details ---------------------- **Clock Generation:** The module derives the I2C SCL clock from the input clock using a divider. The divider is calculated as: ``divider := (input_clk / bus_clk) / 4`` This divider creates four phases per SCL period, allowing precise control of SCL and SDA timing for protocol compliance. Clock stretching is supported by monitoring the SCL line during the high phase; if a slave holds SCL low, the controller pauses until SCL is released. **State Machine:** The core logic is implemented as a finite state machine (FSM) with the following states: - **ready:** Idle, waiting for a transaction. - **start:** Generate I2C start condition. - **command:** Send address and R/W bit. - **slv_ack1:** Wait for slave acknowledge (address/command). - **wr:** Write data byte. - **rd:** Read data byte. - **slv_ack2:** Wait for slave acknowledge (write). - **mstr_ack:** Master acknowledge after read. - **stop:** Generate I2C stop condition. .. graphviz:: digraph i2c_master_fsm { rankdir=TD; ready [shape=ellipse, label="ready"]; start [shape=ellipse, label="start"]; command [shape=ellipse, label="command"]; slv_ack1 [shape=ellipse, label="slv_ack1"]; wr [shape=ellipse, label="wr"]; rd [shape=ellipse, label="rd"]; slv_ack2 [shape=ellipse, label="slv_ack2"]; mstr_ack [shape=ellipse, label="mstr_ack"]; stop [shape=ellipse, label="stop"]; ready -> start [label="ena=1"]; ready -> ready [label="ena=0"]; start -> command [label=""]; command -> slv_ack1 [label="bit_cnt=0"]; command -> command [label="bit_cnt>0"]; slv_ack1 -> wr [label="rw=0"]; slv_ack1 -> rd [label="rw=1"]; wr -> slv_ack2 [label="bit_cnt=0"]; wr -> wr [label="bit_cnt>0"]; rd -> mstr_ack [label="bit_cnt=0"]; rd -> rd [label="bit_cnt>0"]; slv_ack2 -> wr [label="ena=1, addr_rw=addr&rw"]; slv_ack2 -> start [label="ena=1, addr_rw!=addr&rw"]; slv_ack2 -> stop [label="ena=0"]; mstr_ack -> rd [label="ena=1, addr_rw=addr&rw"]; mstr_ack -> start [label="ena=1, addr_rw!=addr&rw"]; mstr_ack -> stop [label="ena=0"]; stop -> ready [label=""]; } **Transaction Flow:** 1. **Start Condition:** Generated by pulling SDA low while SCL is high. 2. **Address/Command Byte:** The 7-bit address and R/W bit are shifted out MSB first. 3. **Acknowledge:** After each byte, the slave must acknowledge by pulling SDA low. If not, `ack_error` is set. 4. **Data Transfer:** - **Write:** Data byte is shifted out to the slave. - **Read:** Data byte is shifted in from the slave. 5. **Stop Condition:** Generated by releasing SDA high while SCL is high. **Clock Stretching:** During the SCL high phase, if the slave holds SCL low, the controller pauses until SCL is released, supporting clock stretching. **Bus Control:** SCL and SDA are driven low only when required; otherwise, they are set to high-impedance ('Z'), enabling open-drain operation and allowing external pull-ups. Limitations ----------- - Only 7-bit addressing is supported. - Single-byte transactions per enable pulse. - No multi-master or arbitration support. - No support for 10-bit addressing or general call. Usage Example ------------- .. code-block:: vhdl i2c_master_inst : entity work.i2c_master generic map ( input_clk => 50000000, bus_clk => 400000 ) port map ( clk => clk, reset_n => reset_n, ena => ena, addr => addr, rw => rw, data_wr => data_wr, busy => busy, data_rd => data_rd, ack_error => ack_error, sda => sda, scl => scl ); References ---------- - I2C-bus specification and user manual (UM10204), NXP Semiconductors. - IEEE Std 1076 VHDL Language Reference Manual. - Original implementation and discussion: https://forum.digikey.com/t/i2c-master-vhdl/12797 Authors ------- - Original Author: rick (Digi-Key TechForum) - Maintainer: Xavier Ruche (F4E external)