User Tools

Site Tools


blog:2025-07-23-001

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

blog:2025-07-23-001 [2025/07/23 08:32] (current)
jethro created
Line 1: Line 1:
 +~~NOTOC~~
 +====== 2025-07-23 Verilog I2C Master Example ======
 +  * Source: [[https://​github.com/​alexforencich/​verilog-i2c/​blob/​master/​rtl/​i2c_master.v]]
 +  * {{:​qlink:​memo_used_photo:​20250715:​pasted:​20250723-083119.png?​300}}
 +===== i2c_master.v =====
 +  * <​sxh>/​*
 +
 +Copyright (c) 2015-2017 Alex Forencich
 +
 +Permission is hereby granted, free of charge, to any person obtaining a copy
 +of this software and associated documentation files (the "​Software"​),​ to deal
 +in the Software without restriction,​ including without limitation the rights
 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 +copies of the Software, and to permit persons to whom the Software is
 +furnished to do so, subject to the following conditions:
 +
 +The above copyright notice and this permission notice shall be included in
 +all copies or substantial portions of the Software.
 +
 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 +THE SOFTWARE.
 +
 +*/
 +
 +// Language: Verilog 2001
 +
 +`timescale 1ns / 1ps
 +
 +/*
 + * I2C master
 + */
 +module i2c_master (
 +    input  wire        clk,
 +    input  wire        rst,
 +
 +    /*
 +     * Host interface
 +     */
 +    input  wire [6:0]  s_axis_cmd_address,​
 +    input  wire        s_axis_cmd_start,​
 +    input  wire        s_axis_cmd_read,​
 +    input  wire        s_axis_cmd_write,​
 +    input  wire        s_axis_cmd_write_multiple,​
 +    input  wire        s_axis_cmd_stop,​
 +    input  wire        s_axis_cmd_valid,​
 +    output wire        s_axis_cmd_ready,​
 +
 +    input  wire [7:0]  s_axis_data_tdata,​
 +    input  wire        s_axis_data_tvalid,​
 +    output wire        s_axis_data_tready,​
 +    input  wire        s_axis_data_tlast,​
 +
 +    output wire [7:0]  m_axis_data_tdata,​
 +    output wire        m_axis_data_tvalid,​
 +    input  wire        m_axis_data_tready,​
 +    output wire        m_axis_data_tlast,​
 +
 +    /*
 +     * I2C interface
 +     */
 +    input  wire        scl_i,
 +    output wire        scl_o,
 +    output wire        scl_t,
 +    input  wire        sda_i,
 +    output wire        sda_o,
 +    output wire        sda_t,
 +
 +    /*
 +     * Status
 +     */
 +    output wire        busy,
 +    output wire        bus_control,​
 +    output wire        bus_active,
 +    output wire        missed_ack,
 +
 +    /*
 +     * Configuration
 +     */
 +    input  wire [15:0] prescale,
 +    input  wire        stop_on_idle
 +);
 +
 +/*
 +
 +I2C
 +
 +Read
 +    __    ___ ___ ___ ___ ___ ___ ___         ___ ___ ___ ___ ___ ___ ___ ___     ___ ___ ___ ___ ___ ___ ___ ___        __
 +sda   ​\__/​_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/​_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/​_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/​
 +    ____   ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ____
 +scl  ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP
 +
 +Write
 +    __    ___ ___ ___ ___ ___ ___ ___ ___     ___ ___ ___ ___ ___ ___ ___ ___     ___ ___ ___ ___ ___ ___ ___ ___ ___    __
 +sda   ​\__/​_6_X_5_X_4_X_3_X_2_X_1_X_0_/​ W \_A_/​_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/​_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/​ N \__/
 +    ____   ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ​_ ​  ____
 +scl  ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP
 +
 +Commands:
 +
 +read
 +    read data byte
 +    set start to force generation of a start condition
 +    start is implied when bus is inactive or active with write or different address
 +    set stop to issue a stop condition after reading current byte
 +    if stop is set with read command, then m_axis_data_tlast will be set
 +
 +write
 +    write data byte
 +    set start to force generation of a start condition
 +    start is implied when bus is inactive or active with read or different address
 +    set stop to issue a stop condition after writing current byte
 +
 +write multiple
 +    write multiple data bytes (until s_axis_data_tlast)
 +    set start to force generation of a start condition
 +    start is implied when bus is inactive or active with read or different address
 +    set stop to issue a stop condition after writing block
 +
 +stop
 +    issue stop condition if bus is active
 +
 +Status:
 +
 +busy
 +    module is communicating over the bus
 +
 +bus_control
 +    module has control of bus in active state
 +
 +bus_active
 +    bus is active, not necessarily controlled by this module
 +
 +missed_ack
 +    strobed when a slave ack is missed
 +
 +Parameters:
 +
 +prescale
 +    set prescale to 1/4 of the minimum clock period in units
 +    of input clk cycles (prescale = Fclk / (FI2Cclk * 4))
 +
 +stop_on_idle
 +    automatically issue stop when command input is not valid
 +
 +Example of interfacing with tristate pins:
 +(this will work for any tristate bus)
 +
 +assign scl_i = scl_pin;
 +assign scl_pin = scl_t ? 1'bz : scl_o;
 +assign sda_i = sda_pin;
 +assign sda_pin = sda_t ? 1'bz : sda_o;
 +
 +Equivalent code that does not use *_t connections:​
 +(we can get away with this because I2C is open-drain)
 +
 +assign scl_i = scl_pin;
 +assign scl_pin = scl_o ? 1'bz : 1'b0;
 +assign sda_i = sda_pin;
 +assign sda_pin = sda_o ? 1'bz : 1'b0;
 +
 +Example of two interconnected I2C devices:
 +
 +assign scl_1_i = scl_1_o & scl_2_o;
 +assign scl_2_i = scl_1_o & scl_2_o;
 +assign sda_1_i = sda_1_o & sda_2_o;
 +assign sda_2_i = sda_1_o & sda_2_o;
 +
 +Example of two I2C devices sharing the same pins:
 +
 +assign scl_1_i = scl_pin;
 +assign scl_2_i = scl_pin;
 +assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0;
 +assign sda_1_i = sda_pin;
 +assign sda_2_i = sda_pin;
 +assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0;
 +
 +Notes:
 +
 +scl_o should not be connected directly to scl_i, only via AND logic or a tristate
 +I/O pin.  This would prevent devices from stretching the clock period.
 +
 +*/
 +
 +localparam [4:0]
 +    STATE_IDLE = 4'd0,
 +    STATE_ACTIVE_WRITE = 4'd1,
 +    STATE_ACTIVE_READ = 4'd2,
 +    STATE_START_WAIT = 4'd3,
 +    STATE_START = 4'd4,
 +    STATE_ADDRESS_1 = 4'd5,
 +    STATE_ADDRESS_2 = 4'd6,
 +    STATE_WRITE_1 = 4'd7,
 +    STATE_WRITE_2 = 4'd8,
 +    STATE_WRITE_3 = 4'd9,
 +    STATE_READ = 4'd10,
 +    STATE_STOP = 4'd11;
 +
 +reg [4:0] state_reg = STATE_IDLE, state_next;
 +
 +localparam [4:0]
 +    PHY_STATE_IDLE = 5'd0,
 +    PHY_STATE_ACTIVE = 5'd1,
 +    PHY_STATE_REPEATED_START_1 = 5'd2,
 +    PHY_STATE_REPEATED_START_2 = 5'd3,
 +    PHY_STATE_START_1 = 5'd4,
 +    PHY_STATE_START_2 = 5'd5,
 +    PHY_STATE_WRITE_BIT_1 = 5'd6,
 +    PHY_STATE_WRITE_BIT_2 = 5'd7,
 +    PHY_STATE_WRITE_BIT_3 = 5'd8,
 +    PHY_STATE_READ_BIT_1 = 5'd9,
 +    PHY_STATE_READ_BIT_2 = 5'd10,
 +    PHY_STATE_READ_BIT_3 = 5'd11,
 +    PHY_STATE_READ_BIT_4 = 5'd12,
 +    PHY_STATE_STOP_1 = 5'd13,
 +    PHY_STATE_STOP_2 = 5'd14,
 +    PHY_STATE_STOP_3 = 5'd15;
 +
 +reg [4:0] phy_state_reg = STATE_IDLE, phy_state_next;​
 +
 +reg phy_start_bit;​
 +reg phy_stop_bit;​
 +reg phy_write_bit;​
 +reg phy_read_bit;​
 +reg phy_release_bus;​
 +
 +reg phy_tx_data;​
 +
 +reg phy_rx_data_reg = 1'b0, phy_rx_data_next;​
 +
 +reg [6:0] addr_reg = 7'd0, addr_next;
 +reg [7:0] data_reg = 8'd0, data_next;
 +reg last_reg = 1'b0, last_next;
 +
 +reg mode_read_reg = 1'b0, mode_read_next;​
 +reg mode_write_multiple_reg = 1'b0, mode_write_multiple_next;​
 +reg mode_stop_reg = 1'b0, mode_stop_next;​
 +
 +reg [16:0] delay_reg = 16'd0, delay_next;
 +reg delay_scl_reg = 1'b0, delay_scl_next;​
 +reg delay_sda_reg = 1'b0, delay_sda_next;​
 +
 +reg [3:0] bit_count_reg = 4'd0, bit_count_next;​
 +
 +reg s_axis_cmd_ready_reg = 1'b0, s_axis_cmd_ready_next;​
 +
 +reg s_axis_data_tready_reg = 1'b0, s_axis_data_tready_next;​
 +
 +reg [7:0] m_axis_data_tdata_reg = 8'd0, m_axis_data_tdata_next;​
 +reg m_axis_data_tvalid_reg = 1'b0, m_axis_data_tvalid_next;​
 +reg m_axis_data_tlast_reg = 1'b0, m_axis_data_tlast_next;​
 +
 +reg scl_i_reg = 1'b1;
 +reg sda_i_reg = 1'b1;
 +
 +reg scl_o_reg = 1'b1, scl_o_next;
 +reg sda_o_reg = 1'b1, sda_o_next;
 +
 +reg last_scl_i_reg = 1'b1;
 +reg last_sda_i_reg = 1'b1;
 +
 +reg busy_reg = 1'b0;
 +reg bus_active_reg = 1'b0;
 +reg bus_control_reg = 1'b0, bus_control_next;​
 +reg missed_ack_reg = 1'b0, missed_ack_next;​
 +
 +assign s_axis_cmd_ready = s_axis_cmd_ready_reg;​
 +
 +assign s_axis_data_tready = s_axis_data_tready_reg;​
 +
 +assign m_axis_data_tdata = m_axis_data_tdata_reg;​
 +assign m_axis_data_tvalid = m_axis_data_tvalid_reg;​
 +assign m_axis_data_tlast = m_axis_data_tlast_reg;​
 +
 +assign scl_o = scl_o_reg;
 +assign scl_t = scl_o_reg;
 +assign sda_o = sda_o_reg;
 +assign sda_t = sda_o_reg;
 +
 +assign busy = busy_reg;
 +assign bus_active = bus_active_reg;​
 +assign bus_control = bus_control_reg;​
 +assign missed_ack = missed_ack_reg;​
 +
 +wire scl_posedge = scl_i_reg & ~last_scl_i_reg;​
 +wire scl_negedge = ~scl_i_reg & last_scl_i_reg;​
 +wire sda_posedge = sda_i_reg & ~last_sda_i_reg;​
 +wire sda_negedge = ~sda_i_reg & last_sda_i_reg;​
 +
 +wire start_bit = sda_negedge & scl_i_reg;
 +wire stop_bit = sda_posedge & scl_i_reg;
 +
 +always @* begin
 +    state_next = STATE_IDLE;
 +
 +    phy_start_bit = 1'b0;
 +    phy_stop_bit = 1'b0;
 +    phy_write_bit = 1'b0;
 +    phy_read_bit = 1'b0;
 +    phy_tx_data = 1'b0;
 +    phy_release_bus = 1'b0;
 +
 +    addr_next = addr_reg;
 +    data_next = data_reg;
 +    last_next = last_reg;
 +
 +    mode_read_next = mode_read_reg;​
 +    mode_write_multiple_next = mode_write_multiple_reg;​
 +    mode_stop_next = mode_stop_reg;​
 +
 +    bit_count_next = bit_count_reg;​
 +
 +    s_axis_cmd_ready_next = 1'b0;
 +
 +    s_axis_data_tready_next = 1'b0;
 +
 +    m_axis_data_tdata_next = m_axis_data_tdata_reg;​
 +    m_axis_data_tvalid_next = m_axis_data_tvalid_reg & ~m_axis_data_tready;​
 +    m_axis_data_tlast_next = m_axis_data_tlast_reg;​
 +
 +    missed_ack_next = 1'b0;
 +
 +    // generate delays
 +    if (phy_state_reg != PHY_STATE_IDLE && phy_state_reg != PHY_STATE_ACTIVE) begin
 +        // wait for phy operation
 +        state_next = state_reg;
 +    end else begin
 +        // process states
 +        case (state_reg)
 +            STATE_IDLE: begin
 +                // line idle
 +                s_axis_cmd_ready_next = 1'b1;
 +
 +                if (s_axis_cmd_ready & s_axis_cmd_valid) begin
 +                    // command valid
 +                    if (s_axis_cmd_read ^ (s_axis_cmd_write | s_axis_cmd_write_multiple)) begin
 +                        // read or write command
 +                        addr_next = s_axis_cmd_address;​
 +                        mode_read_next = s_axis_cmd_read;​
 +                        mode_write_multiple_next = s_axis_cmd_write_multiple;​
 +                        mode_stop_next = s_axis_cmd_stop;​
 +
 +                        s_axis_cmd_ready_next = 1'b0;
 +
 +                        // start bit
 +                        if (bus_active) begin
 +                            state_next = STATE_START_WAIT;​
 +                        end else begin
 +                            phy_start_bit = 1'b1;
 +                            bit_count_next = 4'd8;
 +                            state_next = STATE_ADDRESS_1;​
 +                        end
 +                    end else begin
 +                        // invalid or unspecified - ignore
 +                        state_next = STATE_IDLE;
 +                    end
 +                end else begin
 +                    state_next = STATE_IDLE;
 +                end
 +            end
 +            STATE_ACTIVE_WRITE:​ begin
 +                // line active with current address and read/write mode
 +                s_axis_cmd_ready_next = 1'b1;
 +
 +                if (s_axis_cmd_ready & s_axis_cmd_valid) begin
 +                    // command valid
 +                    if (s_axis_cmd_read ^ (s_axis_cmd_write | s_axis_cmd_write_multiple)) begin
 +                        // read or write command
 +                        addr_next = s_axis_cmd_address;​
 +                        mode_read_next = s_axis_cmd_read;​
 +                        mode_write_multiple_next = s_axis_cmd_write_multiple;​
 +                        mode_stop_next = s_axis_cmd_stop;​
 +
 +                        s_axis_cmd_ready_next = 1'b0;
 +                        ​
 +                        if (s_axis_cmd_start || s_axis_cmd_address != addr_reg || s_axis_cmd_read) begin
 +                            // address or mode mismatch or forced start - repeated start
 +
 +                            // repeated start bit
 +                            phy_start_bit = 1'b1;
 +                            bit_count_next = 4'd8;
 +                            state_next = STATE_ADDRESS_1;​
 +                        end else begin
 +                            // address and mode match
 +
 +                            // start write
 +                            s_axis_data_tready_next = 1'b1;
 +                            state_next = STATE_WRITE_1;​
 +                        end
 +                    end else if (s_axis_cmd_stop && !(s_axis_cmd_read || s_axis_cmd_write || s_axis_cmd_write_multiple)) begin
 +                        // stop command
 +                        phy_stop_bit = 1'b1;
 +                        state_next = STATE_IDLE;
 +                    end else begin
 +                        // invalid or unspecified - ignore
 +                        state_next = STATE_ACTIVE_WRITE;​
 +                    end
 +                end else begin
 +                    if (stop_on_idle & s_axis_cmd_ready & ~s_axis_cmd_valid) begin
 +                        // no waiting command and stop_on_idle selected, issue stop condition
 +                        phy_stop_bit = 1'b1;
 +                        state_next = STATE_IDLE;
 +                    end else begin
 +                        state_next = STATE_ACTIVE_WRITE;​
 +                    end
 +                end
 +            end
 +            STATE_ACTIVE_READ:​ begin
 +                // line active to current address
 +                s_axis_cmd_ready_next = ~m_axis_data_tvalid;​
 +
 +                if (s_axis_cmd_ready & s_axis_cmd_valid) begin
 +                    // command valid
 +                    if (s_axis_cmd_read ^ (s_axis_cmd_write | s_axis_cmd_write_multiple)) begin
 +                        // read or write command
 +                        addr_next = s_axis_cmd_address;​
 +                        mode_read_next = s_axis_cmd_read;​
 +                        mode_write_multiple_next = s_axis_cmd_write_multiple;​
 +                        mode_stop_next = s_axis_cmd_stop;​
 +
 +                        s_axis_cmd_ready_next = 1'b0;
 +                        ​
 +                        if (s_axis_cmd_start || s_axis_cmd_address != addr_reg || s_axis_cmd_write) begin
 +                            // address or mode mismatch or forced start - repeated start
 +
 +                            // write nack for previous read
 +                            phy_write_bit = 1'b1;
 +                            phy_tx_data = 1'b1;
 +                            // repeated start bit
 +                            state_next = STATE_START;​
 +                        end else begin
 +                            // address and mode match
 +
 +                            // write ack for previous read
 +                            phy_write_bit = 1'b1;
 +                            phy_tx_data = 1'b0;
 +                            // start next read
 +                            bit_count_next = 4'd8;
 +                            data_next = 8'd0;
 +                            state_next = STATE_READ;
 +                        end
 +                    end else if (s_axis_cmd_stop && !(s_axis_cmd_read || s_axis_cmd_write || s_axis_cmd_write_multiple)) begin
 +                        // stop command
 +                        // write nack for previous read
 +                        phy_write_bit = 1'b1;
 +                        phy_tx_data = 1'b1;
 +                        // send stop bit
 +                        state_next = STATE_STOP;
 +                    end else begin
 +                        // invalid or unspecified - ignore
 +                        state_next = STATE_ACTIVE_READ;​
 +                    end
 +                end else begin
 +                    if (stop_on_idle & s_axis_cmd_ready & ~s_axis_cmd_valid) begin
 +                        // no waiting command and stop_on_idle selected, issue stop condition
 +                        // write ack for previous read
 +                        phy_write_bit = 1'b1;
 +                        phy_tx_data = 1'b1;
 +                        // send stop bit
 +                        state_next = STATE_STOP;
 +                    end else begin
 +                        state_next = STATE_ACTIVE_READ;​
 +                    end
 +                end
 +            end
 +            STATE_START_WAIT:​ begin
 +                // wait for bus idle
 +
 +                if (bus_active) begin
 +                    state_next = STATE_START_WAIT;​
 +                end else begin
 +                    // bus is idle, take control
 +                    phy_start_bit = 1'b1;
 +                    bit_count_next = 4'd8;
 +                    state_next = STATE_ADDRESS_1;​
 +                end
 +            end
 +            STATE_START:​ begin
 +                // send start bit
 +
 +                phy_start_bit = 1'b1;
 +                bit_count_next = 4'd8;
 +                state_next = STATE_ADDRESS_1;​
 +            end
 +            STATE_ADDRESS_1:​ begin
 +                // send address
 +                bit_count_next = bit_count_reg - 1;
 +                if (bit_count_reg > 1) begin
 +                    // send address
 +                    phy_write_bit = 1'b1;
 +                    phy_tx_data = addr_reg[bit_count_reg-2];​
 +                    state_next = STATE_ADDRESS_1;​
 +                end else if (bit_count_reg > 0) begin
 +                    // send read/write bit
 +                    phy_write_bit = 1'b1;
 +                    phy_tx_data = mode_read_reg;​
 +                    state_next = STATE_ADDRESS_1;​
 +                end else begin
 +                    // read ack bit
 +                    phy_read_bit = 1'b1;
 +                    state_next = STATE_ADDRESS_2;​
 +                end
 +            end
 +            STATE_ADDRESS_2:​ begin
 +                // read ack bit
 +                missed_ack_next = phy_rx_data_reg;​
 +
 +                if (mode_read_reg) begin
 +                    // start read
 +                    bit_count_next = 4'd8;
 +                    data_next = 1'b0;
 +                    state_next = STATE_READ;
 +                end else begin
 +                    // start write
 +                    s_axis_data_tready_next = 1'b1;
 +                    state_next = STATE_WRITE_1;​
 +                end
 +            end
 +            STATE_WRITE_1:​ begin
 +                s_axis_data_tready_next = 1'b1;
 +
 +                if (s_axis_data_tready & s_axis_data_tvalid) begin
 +                    // got data, start write
 +                    data_next = s_axis_data_tdata;​
 +                    last_next = s_axis_data_tlast;​
 +                    bit_count_next = 4'd8;
 +                    s_axis_data_tready_next = 1'b0;
 +                    state_next = STATE_WRITE_2;​
 +                end else begin
 +                    // wait for data
 +                    state_next = STATE_WRITE_1;​
 +                end
 +            end
 +            STATE_WRITE_2:​ begin
 +                // send data
 +                bit_count_next = bit_count_reg - 1;
 +                if (bit_count_reg > 0) begin
 +                    // write data bit
 +                    phy_write_bit = 1'b1;
 +                    phy_tx_data = data_reg[bit_count_reg-1];​
 +                    state_next = STATE_WRITE_2;​
 +                end else begin
 +                    // read ack bit
 +                    phy_read_bit = 1'b1;
 +                    state_next = STATE_WRITE_3;​
 +                end
 +            end
 +            STATE_WRITE_3:​ begin
 +                // read ack bit
 +                missed_ack_next = phy_rx_data_reg;​
 +
 +                if (mode_write_multiple_reg && !last_reg) begin
 +                    // more to write
 +                    state_next = STATE_WRITE_1;​
 +                end else if (mode_stop_reg) begin
 +                    // last cycle and stop selected
 +                    phy_stop_bit = 1'b1;
 +                    state_next = STATE_IDLE;
 +                end else begin
 +                    // otherwise, return to bus active state
 +                    state_next = STATE_ACTIVE_WRITE;​
 +                end
 +            end
 +            STATE_READ: begin
 +                // read data
 +
 +                bit_count_next = bit_count_reg - 1;
 +                data_next = {data_reg[6:​0],​ phy_rx_data_reg};​
 +                if (bit_count_reg > 0) begin
 +                    // read next bit
 +                    phy_read_bit = 1'b1;
 +                    state_next = STATE_READ;
 +                end else begin
 +                    // output data word
 +                    m_axis_data_tdata_next = data_next;
 +                    m_axis_data_tvalid_next = 1'b1;
 +                    m_axis_data_tlast_next = 1'b0;
 +                    if (mode_stop_reg) begin
 +                        // send nack and stop
 +                        m_axis_data_tlast_next = 1'b1;
 +                        phy_write_bit = 1'b1;
 +                        phy_tx_data = 1'b1;
 +                        state_next = STATE_STOP;
 +                    end else begin
 +                        // return to bus active state
 +                        state_next = STATE_ACTIVE_READ;​
 +                    end
 +                end
 +            end
 +            STATE_STOP: begin
 +                // send stop bit
 +                phy_stop_bit = 1'b1;
 +                state_next = STATE_IDLE;
 +            end
 +        endcase
 +    end
 +end
 +
 +always @* begin
 +    phy_state_next = PHY_STATE_IDLE;​
 +
 +    phy_rx_data_next = phy_rx_data_reg;​
 +
 +    delay_next = delay_reg;
 +    delay_scl_next = delay_scl_reg;​
 +    delay_sda_next = delay_sda_reg;​
 +
 +    scl_o_next = scl_o_reg;
 +    sda_o_next = sda_o_reg;
 +
 +    bus_control_next = bus_control_reg;​
 +
 +    if (phy_release_bus) begin
 +        // release bus and return to idle state
 +        sda_o_next = 1'b1;
 +        scl_o_next = 1'b1;
 +        delay_scl_next = 1'b0;
 +        delay_sda_next = 1'b0;
 +        delay_next = 1'b0;
 +        phy_state_next = PHY_STATE_IDLE;​
 +    end else if (delay_scl_reg) begin
 +        // wait for SCL to match command
 +        delay_scl_next = scl_o_reg & ~scl_i_reg;
 +        phy_state_next = phy_state_reg;​
 +    end else if (delay_sda_reg) begin
 +        // wait for SDA to match command
 +        delay_sda_next = sda_o_reg & ~sda_i_reg;
 +        phy_state_next = phy_state_reg;​
 +    end else if (delay_reg > 0) begin
 +        // time delay
 +        delay_next = delay_reg - 1;
 +        phy_state_next = phy_state_reg;​
 +    end else begin
 +        case (phy_state_reg)
 +            PHY_STATE_IDLE:​ begin
 +                // bus idle - wait for start command
 +                sda_o_next = 1'b1;
 +                scl_o_next = 1'b1;
 +                if (phy_start_bit) begin
 +                    sda_o_next = 1'b0;
 +                    delay_next = prescale;
 +                    phy_state_next = PHY_STATE_START_1;​
 +                end else begin
 +                    phy_state_next = PHY_STATE_IDLE;​
 +                end
 +            end
 +            PHY_STATE_ACTIVE:​ begin
 +                // bus active
 +                if (phy_start_bit) begin
 +                    sda_o_next = 1'b1;
 +                    delay_next = prescale;
 +                    phy_state_next = PHY_STATE_REPEATED_START_1;​
 +                end else if (phy_write_bit) begin
 +                    sda_o_next = phy_tx_data;​
 +                    delay_next = prescale;
 +                    phy_state_next = PHY_STATE_WRITE_BIT_1;​
 +                end else if (phy_read_bit) begin
 +                    sda_o_next = 1'b1;
 +                    delay_next = prescale;
 +                    phy_state_next = PHY_STATE_READ_BIT_1;​
 +                end else if (phy_stop_bit) begin
 +                    sda_o_next = 1'b0;
 +                    delay_next = prescale;
 +                    phy_state_next = PHY_STATE_STOP_1;​
 +                end else begin
 +                    phy_state_next = PHY_STATE_ACTIVE;​
 +                end
 +            end
 +            PHY_STATE_REPEATED_START_1:​ begin
 +                // generate repeated start bit
 +                //         ​______
 +                // sda XXX/      \_______
 +                //            _______
 +                // scl ______/ ​      \___
 +                //
 +
 +                scl_o_next = 1'b1;
 +                delay_scl_next = 1'b1;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_REPEATED_START_2;​
 +            end
 +            PHY_STATE_REPEATED_START_2:​ begin
 +                // generate repeated start bit
 +                //         ​______
 +                // sda XXX/      \_______
 +                //            _______
 +                // scl ______/ ​      \___
 +                //
 +
 +                sda_o_next = 1'b0;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_START_1;​
 +            end
 +            PHY_STATE_START_1:​ begin
 +                // generate start bit
 +                //     ___
 +                // sda    \_______
 +                //     ​_______
 +                // scl        \___
 +                //
 +
 +                scl_o_next = 1'b0;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_START_2;​
 +            end
 +            PHY_STATE_START_2:​ begin
 +                // generate start bit
 +                //     ___
 +                // sda    \_______
 +                //     ​_______
 +                // scl        \___
 +                //
 +
 +                bus_control_next = 1'b1;
 +                phy_state_next = PHY_STATE_ACTIVE;​
 +            end
 +            PHY_STATE_WRITE_BIT_1:​ begin
 +                // write bit
 +                //      ________
 +                // sda X________X
 +                //        ____
 +                // scl __/    \__
 +
 +                scl_o_next = 1'b1;
 +                delay_scl_next = 1'b1;
 +                delay_next = prescale << 1;
 +                phy_state_next = PHY_STATE_WRITE_BIT_2;​
 +            end
 +            PHY_STATE_WRITE_BIT_2:​ begin
 +                // write bit
 +                //      ________
 +                // sda X________X
 +                //        ____
 +                // scl __/    \__
 +
 +                scl_o_next = 1'b0;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_WRITE_BIT_3;​
 +            end
 +            PHY_STATE_WRITE_BIT_3:​ begin
 +                // write bit
 +                //      ________
 +                // sda X________X
 +                //        ____
 +                // scl __/    \__
 +
 +                phy_state_next = PHY_STATE_ACTIVE;​
 +            end
 +            PHY_STATE_READ_BIT_1:​ begin
 +                // read bit
 +                //      ________
 +                // sda X________X
 +                //        ____
 +                // scl __/    \__
 +
 +                scl_o_next = 1'b1;
 +                delay_scl_next = 1'b1;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_READ_BIT_2;​
 +            end
 +            PHY_STATE_READ_BIT_2:​ begin
 +                // read bit
 +                //      ________
 +                // sda X________X
 +                //        ____
 +                // scl __/    \__
 +
 +                phy_rx_data_next = sda_i_reg;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_READ_BIT_3;​
 +            end
 +            PHY_STATE_READ_BIT_3:​ begin
 +                // read bit
 +                //      ________
 +                // sda X________X
 +                //        ____
 +                // scl __/    \__
 +
 +                scl_o_next = 1'b0;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_READ_BIT_4;​
 +            end
 +            PHY_STATE_READ_BIT_4:​ begin
 +                // read bit
 +                //      ________
 +                // sda X________X
 +                //        ____
 +                // scl __/    \__
 +
 +                phy_state_next = PHY_STATE_ACTIVE;​
 +            end
 +            PHY_STATE_STOP_1:​ begin
 +                // stop bit
 +                //                 ___
 +                // sda XXX\_______/​
 +                //             ​_______
 +                // scl _______/
 +
 +                scl_o_next = 1'b1;
 +                delay_scl_next = 1'b1;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_STOP_2;​
 +            end
 +            PHY_STATE_STOP_2:​ begin
 +                // stop bit
 +                //                 ___
 +                // sda XXX\_______/​
 +                //             ​_______
 +                // scl _______/
 +
 +                sda_o_next = 1'b1;
 +                delay_next = prescale;
 +                phy_state_next = PHY_STATE_STOP_3;​
 +            end
 +            PHY_STATE_STOP_3:​ begin
 +                // stop bit
 +                //                 ___
 +                // sda XXX\_______/​
 +                //             ​_______
 +                // scl _______/
 +
 +                bus_control_next = 1'b0;
 +                phy_state_next = PHY_STATE_IDLE;​
 +            end
 +        endcase
 +    end
 +end
 +
 +always @(posedge clk) begin
 +    state_reg <= state_next;
 +    phy_state_reg <= phy_state_next;​
 +
 +    phy_rx_data_reg <= phy_rx_data_next;​
 +
 +    addr_reg <= addr_next;
 +    data_reg <= data_next;
 +    last_reg <= last_next;
 +
 +    mode_read_reg <= mode_read_next;​
 +    mode_write_multiple_reg <= mode_write_multiple_next;​
 +    mode_stop_reg <= mode_stop_next;​
 +
 +    delay_reg <= delay_next;
 +    delay_scl_reg <= delay_scl_next;​
 +    delay_sda_reg <= delay_sda_next;​
 +
 +    bit_count_reg <= bit_count_next;​
 +
 +    s_axis_cmd_ready_reg <= s_axis_cmd_ready_next;​
 +
 +    s_axis_data_tready_reg <= s_axis_data_tready_next;​
 +
 +    m_axis_data_tdata_reg <= m_axis_data_tdata_next;​
 +    m_axis_data_tlast_reg <= m_axis_data_tlast_next;​
 +    m_axis_data_tvalid_reg <= m_axis_data_tvalid_next;​
 +
 +    scl_i_reg <= scl_i;
 +    sda_i_reg <= sda_i;
 +
 +    scl_o_reg <= scl_o_next;
 +    sda_o_reg <= sda_o_next;
 +
 +    last_scl_i_reg <= scl_i_reg;
 +    last_sda_i_reg <= sda_i_reg;
 +
 +    busy_reg <= !(state_reg == STATE_IDLE || state_reg == STATE_ACTIVE_WRITE || state_reg == STATE_ACTIVE_READ) || !(phy_state_reg == PHY_STATE_IDLE || phy_state_reg == PHY_STATE_ACTIVE);​
 +
 +    if (start_bit) begin
 +        bus_active_reg <= 1'b1;
 +    end else if (stop_bit) begin
 +        bus_active_reg <= 1'b0;
 +    end else begin
 +        bus_active_reg <= bus_active_reg;​
 +    end
 +
 +    bus_control_reg <= bus_control_next;​
 +    missed_ack_reg <= missed_ack_next;​
 +
 +    if (rst) begin
 +        state_reg <= STATE_IDLE;
 +        phy_state_reg <= PHY_STATE_IDLE;​
 +        delay_reg <= 16'd0;
 +        delay_scl_reg <= 1'b0;
 +        delay_sda_reg <= 1'b0;
 +        s_axis_cmd_ready_reg <= 1'b0;
 +        s_axis_data_tready_reg <= 1'b0;
 +        m_axis_data_tvalid_reg <= 1'b0;
 +        scl_o_reg <= 1'b1;
 +        sda_o_reg <= 1'b1;
 +        busy_reg <= 1'b0;
 +        bus_active_reg <= 1'b0;
 +        bus_control_reg <= 1'b0;
 +        missed_ack_reg <= 1'b0;
 +    end
 +end
 +
 +endmodule</​sxh>​
 +===== TAGS =====
 +  * 
 +====== ======
 +  * {{counter}} person(s) visited this page until now.
 +  * [[:​memo:​index|Back]]
 +====== ======
 +<​html><​!-- ​
 +PDF for A4-Portrait:​ {{pdfjs 50%,450px > xxx.pdf?​page-fit}}
 +PDF for A4-Landscape:​ {{pdfjs 500px,700px > xxx.pdf?​page-fit}}
 +PDF for iPad Note: {{pdfjs 700px,500px > xxx.pdf?​page-fit}}
 +
 +Youtube: {{youtube>​large:​XXXXX}}
 +Code Highlight: <sxh php; first-line: 70; highlight: [89,92]; title: New title attribute in action>
 +
 +--></​html>​
  
blog/2025-07-23-001.txt · Last modified: 2025/07/23 08:32 by jethro