// // Author: lsilvest // Create Date: 10/10/2009 // // // Copyright (c) 2009 Authors // // 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. // `include "i2c_controller_const.v" module i2c_controller( input clk, input rst_n, input [7:0] data_i, output reg [7:0] data_o, input sdin_i, output reg sdin_o, output sdin_oe, input sclk_i, output reg sclk_o, output sclk_oe, input [2:0] cmd_i, output cmd_a, output ack_o, output ack_e, output b_done, output busy ); reg [3:0] state, next_state, prior_state; reg [6:0] cntr; reg [1:0] pulse_cntr; reg i2c_sclk_reg; reg [4:0] bit_cntr; wire pulse; wire cnt_en; //reg a_done; wire byte_done; assign state_test = state; assign sclk_oe = 0; wire i2c_clk_edge_tick; localparam F_DIV = 64; // localparam F_DIV = 2; localparam idl = 4'b0000, s = 4'b0001, w1 = 4'b0010, w = 4'b0011, r = 4'b0100, r1 = 4'b0101, a_w = 4'b0110, a_r = 4'b0111, na_r = 4'b1000, p = 4'b1001; // generate pulse (really a frequency divide since i2c needs to run <= 400kHz) always@(posedge clk or negedge rst_n) begin if (!rst_n) cntr <= 0; else if (cntr == F_DIV) cntr <= 0; else cntr <= cntr + 1; end assign pulse = cntr == F_DIV; // generate pulse counter always@(posedge clk or negedge rst_n) begin if (!rst_n) pulse_cntr <= 2'b0; else if (pulse) pulse_cntr <= pulse_cntr + 2'b1; end // generate i2c clock from pulse counter always@(posedge clk or negedge rst_n) begin if (!rst_n) i2c_sclk_reg <= 1'b0; else if (pulse && pulse_cntr[0] == 1'b1) i2c_sclk_reg <= ~i2c_sclk_reg; end // generate an edge signal on i2c_clk so we can have short ack edge_detector i2c_clk_edge_detector( .clk(clk), .rst_n(rst_n), .sig(i2c_sclk_reg), .tick(i2c_clk_edge_tick)); // generate state change always@(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= idl; prior_state <= idl; end else if (pulse) if (pulse_cntr == 2'b11) state <= next_state; else prior_state <= state; // always lag state by one pulse end // state logic: always@(*) begin case (state) idl: if (cmd_i == `s_cmd) next_state = s; else next_state = idl; s: next_state = w1; w1: next_state = w; w: if (byte_done) next_state = a_w; else next_state = w; a_w: if (cmd_i == `w_cmd) next_state = w1; else if (cmd_i == `s_cmd) next_state = s; else if (cmd_i == `r_cmd) next_state = r1; else if (cmd_i == `p_cmd) next_state = p; else next_state = idl; r1: next_state = r; r: if (byte_done && cmd_i == `a_cmd) next_state = a_r; else if (byte_done && cmd_i == `na_cmd) next_state = na_r; else next_state = r; a_r: if (cmd_i == `r_cmd) next_state = r1; else if (cmd_i == `p_cmd) next_state = p; else next_state = idl; na_r: if (cmd_i == `r_cmd) next_state = r1; else if (cmd_i == `p_cmd) next_state = p; else next_state = idl; p: next_state = idl; default: next_state = idl; endcase end // generate bit_cntr always@(posedge clk or negedge rst_n) begin if (!rst_n) bit_cntr <= 5'd7; else if (pulse && pulse_cntr == 2'b00) if (bit_cntr == 0) bit_cntr <= 5'd7; else if (cnt_en) bit_cntr <= bit_cntr - 5'b1; end always@(posedge clk or negedge rst_n) begin if (!rst_n) data_o <= 8'b0; else if ((state == r1 || state == r) && pulse && pulse_cntr == 2'b10) data_o <= (data_o << 1) | sdin_i; end assign ack_o = prior_state == a_w ? i2c_clk_edge_tick && sdin_i == 0 : (prior_state == a_r || prior_state == na_r) ? i2c_clk_edge_tick : 0; // generate ack_o also on read so we know when to change states assign ack_e = prior_state == a_w || prior_state == a_r || prior_state == na_r; // logic to control sclk always@(*) begin if (prior_state == idl) sclk_o = 0; else sclk_o = i2c_sclk_reg; end // logic to control sdin always@(*) begin if (prior_state == idl) sdin_o = 0; else if (prior_state == s) sdin_o = pulse_cntr == 1 || pulse_cntr == 2; else if (prior_state == p) sdin_o = pulse_cntr == 3 || pulse_cntr == 0; else if (prior_state == a_r) sdin_o = 0; else if (prior_state == na_r) sdin_o = 1; else if (prior_state == a_w) // not needed but makes the waveforms easier to read sdin_o = 0; else sdin_o = data_i[bit_cntr]; end assign sdin_oe = prior_state == a_w || prior_state == r1 || prior_state == r; // logic to control bit selection assign cnt_en = state == w || state == r; assign byte_done = bit_cntr == 'd0; // command acknowledge assign cmd_a = (state == w1 || state == r1 || state == p || state == a_r || state == na_r) && i2c_clk_edge_tick; assign busy = state != idl; assign b_done = byte_done && i2c_clk_edge_tick; endmodule