| /* |
| |
| 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] cmd_address, |
| input wire cmd_start, |
| input wire cmd_read, |
| input wire cmd_write, |
| input wire cmd_write_multiple, |
| input wire cmd_stop, |
| input wire cmd_valid, |
| output wire cmd_ready, |
| |
| input wire [7:0] data_in, |
| input wire data_in_valid, |
| output wire data_in_ready, |
| input wire data_in_last, |
| |
| output wire [7:0] data_out, |
| output wire data_out_valid, |
| input wire data_out_ready, |
| output wire data_out_last, |
| |
| /* |
| * 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 data_out_last 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 data_in_last) |
| 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 cmd_ready_reg = 1'b0, cmd_ready_next; |
| |
| reg data_in_ready_reg = 1'b0, data_in_ready_next; |
| |
| reg [7:0] data_out_reg = 8'd0, data_out_next; |
| reg data_out_valid_reg = 1'b0, data_out_valid_next; |
| reg data_out_last_reg = 1'b0, data_out_last_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 cmd_ready = cmd_ready_reg; |
| |
| assign data_in_ready = data_in_ready_reg; |
| |
| assign data_out = data_out_reg; |
| assign data_out_valid = data_out_valid_reg; |
| assign data_out_last = data_out_last_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; |
| |
| cmd_ready_next = 1'b0; |
| |
| data_in_ready_next = 1'b0; |
| |
| data_out_next = data_out_reg; |
| data_out_valid_next = data_out_valid_reg & ~data_out_ready; |
| data_out_last_next = data_out_last_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 |
| cmd_ready_next = 1'b1; |
| |
| if (cmd_ready & cmd_valid) begin |
| // command valid |
| if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin |
| // read or write command |
| addr_next = cmd_address; |
| mode_read_next = cmd_read; |
| mode_write_multiple_next = cmd_write_multiple; |
| mode_stop_next = cmd_stop; |
| |
| 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 |
| cmd_ready_next = 1'b1; |
| |
| if (cmd_ready & cmd_valid) begin |
| // command valid |
| if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin |
| // read or write command |
| addr_next = cmd_address; |
| mode_read_next = cmd_read; |
| mode_write_multiple_next = cmd_write_multiple; |
| mode_stop_next = cmd_stop; |
| |
| cmd_ready_next = 1'b0; |
| |
| if (cmd_start || cmd_address != addr_reg || 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 |
| data_in_ready_next = 1'b1; |
| state_next = STATE_WRITE_1; |
| end |
| end else if (cmd_stop && !(cmd_read || cmd_write || 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 & cmd_ready & ~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 |
| cmd_ready_next = ~data_out_valid; |
| |
| if (cmd_ready & cmd_valid) begin |
| // command valid |
| if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin |
| // read or write command |
| addr_next = cmd_address; |
| mode_read_next = cmd_read; |
| mode_write_multiple_next = cmd_write_multiple; |
| mode_stop_next = cmd_stop; |
| |
| cmd_ready_next = 1'b0; |
| |
| if (cmd_start || cmd_address != addr_reg || 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 (cmd_stop && !(cmd_read || cmd_write || 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 & cmd_ready & ~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 |
| data_in_ready_next = 1'b1; |
| state_next = STATE_WRITE_1; |
| end |
| end |
| STATE_WRITE_1: begin |
| data_in_ready_next = 1'b1; |
| |
| if (data_in_ready & data_in_valid) begin |
| // got data, start write |
| data_next = data_in; |
| last_next = data_in_last; |
| bit_count_next = 4'd8; |
| data_in_ready_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 |
| data_out_next = data_next; |
| data_out_valid_next = 1'b1; |
| data_out_last_next = 1'b0; |
| if (mode_stop_reg) begin |
| // send nack and stop |
| data_out_last_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 |
| 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; |
| cmd_ready_reg <= 1'b0; |
| data_in_ready_reg <= 1'b0; |
| data_out_valid_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 else begin |
| state_reg <= state_next; |
| phy_state_reg <= phy_state_next; |
| |
| delay_reg <= delay_next; |
| delay_scl_reg <= delay_scl_next; |
| delay_sda_reg <= delay_sda_next; |
| |
| cmd_ready_reg <= cmd_ready_next; |
| data_in_ready_reg <= data_in_ready_next; |
| data_out_valid_reg <= data_out_valid_next; |
| |
| scl_o_reg <= scl_o_next; |
| sda_o_reg <= sda_o_next; |
| |
| 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; |
| end |
| |
| 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; |
| |
| bit_count_reg <= bit_count_next; |
| |
| data_out_reg <= data_out_next; |
| data_out_last_reg <= data_out_last_next; |
| |
| scl_i_reg <= scl_i; |
| sda_i_reg <= sda_i; |
| last_scl_i_reg <= scl_i_reg; |
| last_sda_i_reg <= sda_i_reg; |
| end |
| |
| endmodule |