summaryrefslogtreecommitdiff
path: root/tests/i2c_bench/i2c_master_bit_ctrl.v
diff options
context:
space:
mode:
Diffstat (limited to 'tests/i2c_bench/i2c_master_bit_ctrl.v')
-rw-r--r--tests/i2c_bench/i2c_master_bit_ctrl.v576
1 files changed, 576 insertions, 0 deletions
diff --git a/tests/i2c_bench/i2c_master_bit_ctrl.v b/tests/i2c_bench/i2c_master_bit_ctrl.v
new file mode 100644
index 00000000..6594fd60
--- /dev/null
+++ b/tests/i2c_bench/i2c_master_bit_ctrl.v
@@ -0,0 +1,576 @@
+/////////////////////////////////////////////////////////////////////
+//// ////
+//// WISHBONE rev.B2 compliant I2C Master bit-controller ////
+//// ////
+//// ////
+//// Author: Richard Herveille ////
+//// richard@asics.ws ////
+//// www.asics.ws ////
+//// ////
+//// Downloaded from: http://www.opencores.org/projects/i2c/ ////
+//// ////
+/////////////////////////////////////////////////////////////////////
+//// ////
+//// Copyright (C) 2001 Richard Herveille ////
+//// richard@asics.ws ////
+//// ////
+//// This source file may be used and distributed without ////
+//// restriction provided that this copyright statement is not ////
+//// removed from the file and that any derivative work contains ////
+//// the original copyright notice and the associated disclaimer.////
+//// ////
+//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ////
+//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ////
+//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ////
+//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ////
+//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ////
+//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ////
+//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ////
+//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ////
+//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ////
+//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ////
+//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ////
+//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ////
+//// POSSIBILITY OF SUCH DAMAGE. ////
+//// ////
+/////////////////////////////////////////////////////////////////////
+
+// CVS Log
+//
+// $Id: i2c_master_bit_ctrl.v,v 1.14 2009-01-20 10:25:29 rherveille Exp $
+//
+// $Date: 2009-01-20 10:25:29 $
+// $Revision: 1.14 $
+// $Author: rherveille $
+// $Locker: $
+// $State: Exp $
+//
+// Change History:
+// $Log: $
+// Revision 1.14 2009/01/20 10:25:29 rherveille
+// Added clock synchronization logic
+// Fixed slave_wait signal
+//
+// Revision 1.13 2009/01/19 20:29:26 rherveille
+// Fixed synopsys miss spell (synopsis)
+// Fixed cr[0] register width
+// Fixed ! usage instead of ~
+// Fixed bit controller parameter width to 18bits
+//
+// Revision 1.12 2006/09/04 09:08:13 rherveille
+// fixed short scl high pulse after clock stretch
+// fixed slave model not returning correct '(n)ack' signal
+//
+// Revision 1.11 2004/05/07 11:02:26 rherveille
+// Fixed a bug where the core would signal an arbitration lost (AL bit set), when another master controls the bus and the other master generates a STOP bit.
+//
+// Revision 1.10 2003/08/09 07:01:33 rherveille
+// Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line.
+// Fixed a potential bug in the byte controller's host-acknowledge generation.
+//
+// Revision 1.9 2003/03/10 14:26:37 rherveille
+// Fixed cmd_ack generation item (no bug).
+//
+// Revision 1.8 2003/02/05 00:06:10 rherveille
+// Fixed a bug where the core would trigger an erroneous 'arbitration lost' interrupt after being reset, when the reset pulse width < 3 clk cycles.
+//
+// Revision 1.7 2002/12/26 16:05:12 rherveille
+// Small code simplifications
+//
+// Revision 1.6 2002/12/26 15:02:32 rherveille
+// Core is now a Multimaster I2C controller
+//
+// Revision 1.5 2002/11/30 22:24:40 rherveille
+// Cleaned up code
+//
+// Revision 1.4 2002/10/30 18:10:07 rherveille
+// Fixed some reported minor start/stop generation timing issuess.
+//
+// Revision 1.3 2002/06/15 07:37:03 rherveille
+// Fixed a small timing bug in the bit controller.\nAdded verilog simulation environment.
+//
+// Revision 1.2 2001/11/05 11:59:25 rherveille
+// Fixed wb_ack_o generation bug.
+// Fixed bug in the byte_controller statemachine.
+// Added headers.
+//
+
+//
+/////////////////////////////////////
+// Bit controller section
+/////////////////////////////////////
+//
+// Translate simple commands into SCL/SDA transitions
+// Each command has 5 states, A/B/C/D/idle
+//
+// start: SCL ~~~~~~~~~~\____
+// SDA ~~~~~~~~\______
+// x | A | B | C | D | i
+//
+// repstart SCL ____/~~~~\___
+// SDA __/~~~\______
+// x | A | B | C | D | i
+//
+// stop SCL ____/~~~~~~~~
+// SDA ==\____/~~~~~
+// x | A | B | C | D | i
+//
+//- write SCL ____/~~~~\____
+// SDA ==X=========X=
+// x | A | B | C | D | i
+//
+//- read SCL ____/~~~~\____
+// SDA XXXX=====XXXX
+// x | A | B | C | D | i
+//
+
+// Timing: Normal mode Fast mode
+///////////////////////////////////////////////////////////////////////
+// Fscl 100KHz 400KHz
+// Th_scl 4.0us 0.6us High period of SCL
+// Tl_scl 4.7us 1.3us Low period of SCL
+// Tsu:sta 4.7us 0.6us setup time for a repeated start condition
+// Tsu:sto 4.0us 0.6us setup time for a stop conditon
+// Tbuf 4.7us 1.3us Bus free time between a stop and start condition
+//
+
+// synopsys translate_off
+`include "timescale.v"
+// synopsys translate_on
+
+`include "i2c_master_defines.v"
+
+module i2c_master_bit_ctrl (
+ input clk, // system clock
+ input rst, // synchronous active high reset
+ input nReset, // asynchronous active low reset
+ input ena, // core enable signal
+
+ input [15:0] clk_cnt, // clock prescale value
+
+ input [ 3:0] cmd, // command (from byte controller)
+ output reg cmd_ack, // command complete acknowledge
+ output reg busy, // i2c bus busy
+ output reg al, // i2c bus arbitration lost
+
+ input din,
+ output reg dout,
+
+ input scl_i, // i2c clock line input
+ output scl_o, // i2c clock line output
+ output reg scl_oen, // i2c clock line output enable (active low)
+ input sda_i, // i2c data line input
+ output sda_o, // i2c data line output
+ output reg sda_oen // i2c data line output enable (active low)
+);
+
+
+ //
+ // variable declarations
+ //
+
+ reg [ 1:0] cSCL, cSDA; // capture SCL and SDA
+ reg [ 2:0] fSCL, fSDA; // SCL and SDA filter inputs
+ reg sSCL, sSDA; // filtered and synchronized SCL and SDA inputs
+ reg dSCL, dSDA; // delayed versions of sSCL and sSDA
+ reg dscl_oen; // delayed scl_oen
+ reg sda_chk; // check SDA output (Multi-master arbitration)
+ reg clk_en; // clock generation signals
+ reg slave_wait; // slave inserts wait states
+ reg [15:0] cnt; // clock divider counter (synthesis)
+ reg [13:0] filter_cnt; // clock divider for filter
+
+
+ // state machine variable
+ reg [17:0] c_state; // synopsys enum_state
+
+ //
+ // module body
+ //
+
+ // whenever the slave is not ready it can delay the cycle by pulling SCL low
+ // delay scl_oen
+ always @(posedge clk)
+ dscl_oen <= scl_oen;
+
+ // slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low
+ // slave_wait remains asserted until the slave releases SCL
+ always @(posedge clk or negedge nReset)
+ if (!nReset) slave_wait <= 1'b0;
+ else slave_wait <= (scl_oen & ~dscl_oen & ~sSCL) | (slave_wait & ~sSCL);
+
+ // master drives SCL high, but another master pulls it low
+ // master start counting down its low cycle now (clock synchronization)
+ wire scl_sync = dSCL & ~sSCL & scl_oen;
+
+
+ // generate clk enable signal
+ always @(posedge clk or negedge nReset)
+ if (~nReset)
+ begin
+ cnt <= 16'h0;
+ clk_en <= 1'b1;
+ end
+ else if (rst || ~|cnt || !ena || scl_sync)
+ begin
+ cnt <= clk_cnt;
+ clk_en <= 1'b1;
+ end
+ else if (slave_wait)
+ begin
+ cnt <= cnt;
+ clk_en <= 1'b0;
+ end
+ else
+ begin
+ cnt <= cnt - 16'h1;
+ clk_en <= 1'b0;
+ end
+
+
+ // generate bus status controller
+
+ // capture SDA and SCL
+ // reduce metastability risk
+ always @(posedge clk or negedge nReset)
+ if (!nReset)
+ begin
+ cSCL <= 2'b00;
+ cSDA <= 2'b00;
+ end
+ else if (rst)
+ begin
+ cSCL <= 2'b00;
+ cSDA <= 2'b00;
+ end
+ else
+ begin
+ cSCL <= {cSCL[0],scl_i};
+ cSDA <= {cSDA[0],sda_i};
+ end
+
+
+ // filter SCL and SDA signals; (attempt to) remove glitches
+ always @(posedge clk or negedge nReset)
+ if (!nReset ) filter_cnt <= 14'h0;
+ else if (rst || !ena ) filter_cnt <= 14'h0;
+ else if (~|filter_cnt) filter_cnt <= clk_cnt[15:2]; //16x I2C bus frequency
+ else filter_cnt <= filter_cnt -1;
+
+
+ always @(posedge clk or negedge nReset)
+ if (!nReset)
+ begin
+ fSCL <= 3'b111;
+ fSDA <= 3'b111;
+ end
+ else if (rst)
+ begin
+ fSCL <= 3'b111;
+ fSDA <= 3'b111;
+ end
+ else if (~|filter_cnt)
+ begin
+ fSCL <= {fSCL[1:0],cSCL[1]};
+ fSDA <= {fSDA[1:0],cSDA[1]};
+ end
+
+
+ // generate filtered SCL and SDA signals
+ always @(posedge clk or negedge nReset)
+ if (~nReset)
+ begin
+ sSCL <= 1'b1;
+ sSDA <= 1'b1;
+
+ dSCL <= 1'b1;
+ dSDA <= 1'b1;
+ end
+ else if (rst)
+ begin
+ sSCL <= 1'b1;
+ sSDA <= 1'b1;
+
+ dSCL <= 1'b1;
+ dSDA <= 1'b1;
+ end
+ else
+ begin
+ sSCL <= &fSCL[2:1] | &fSCL[1:0] | (fSCL[2] & fSCL[0]);
+ sSDA <= &fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0]);
+
+ dSCL <= sSCL;
+ dSDA <= sSDA;
+ end
+
+ // detect start condition => detect falling edge on SDA while SCL is high
+ // detect stop condition => detect rising edge on SDA while SCL is high
+ reg sta_condition;
+ reg sto_condition;
+ always @(posedge clk or negedge nReset)
+ if (~nReset)
+ begin
+ sta_condition <= 1'b0;
+ sto_condition <= 1'b0;
+ end
+ else if (rst)
+ begin
+ sta_condition <= 1'b0;
+ sto_condition <= 1'b0;
+ end
+ else
+ begin
+ sta_condition <= ~sSDA & dSDA & sSCL;
+ sto_condition <= sSDA & ~dSDA & sSCL;
+ end
+
+
+ // generate i2c bus busy signal
+ always @(posedge clk or negedge nReset)
+ if (!nReset) busy <= 1'b0;
+ else if (rst ) busy <= 1'b0;
+ else busy <= (sta_condition | busy) & ~sto_condition;
+
+
+ // generate arbitration lost signal
+ // aribitration lost when:
+ // 1) master drives SDA high, but the i2c bus is low
+ // 2) stop detected while not requested
+ reg cmd_stop;
+ always @(posedge clk or negedge nReset)
+ if (~nReset)
+ cmd_stop <= 1'b0;
+ else if (rst)
+ cmd_stop <= 1'b0;
+ else if (clk_en)
+ cmd_stop <= cmd == `I2C_CMD_STOP;
+
+ always @(posedge clk or negedge nReset)
+ if (~nReset)
+ al <= 1'b0;
+ else if (rst)
+ al <= 1'b0;
+ else
+ al <= (sda_chk & ~sSDA & sda_oen) | (|c_state & sto_condition & ~cmd_stop);
+
+
+ // generate dout signal (store SDA on rising edge of SCL)
+ always @(posedge clk)
+ if (sSCL & ~dSCL) dout <= sSDA;
+
+
+ // generate statemachine
+
+ // nxt_state decoder
+ parameter [17:0] idle = 18'b0_0000_0000_0000_0000;
+ parameter [17:0] start_a = 18'b0_0000_0000_0000_0001;
+ parameter [17:0] start_b = 18'b0_0000_0000_0000_0010;
+ parameter [17:0] start_c = 18'b0_0000_0000_0000_0100;
+ parameter [17:0] start_d = 18'b0_0000_0000_0000_1000;
+ parameter [17:0] start_e = 18'b0_0000_0000_0001_0000;
+ parameter [17:0] stop_a = 18'b0_0000_0000_0010_0000;
+ parameter [17:0] stop_b = 18'b0_0000_0000_0100_0000;
+ parameter [17:0] stop_c = 18'b0_0000_0000_1000_0000;
+ parameter [17:0] stop_d = 18'b0_0000_0001_0000_0000;
+ parameter [17:0] rd_a = 18'b0_0000_0010_0000_0000;
+ parameter [17:0] rd_b = 18'b0_0000_0100_0000_0000;
+ parameter [17:0] rd_c = 18'b0_0000_1000_0000_0000;
+ parameter [17:0] rd_d = 18'b0_0001_0000_0000_0000;
+ parameter [17:0] wr_a = 18'b0_0010_0000_0000_0000;
+ parameter [17:0] wr_b = 18'b0_0100_0000_0000_0000;
+ parameter [17:0] wr_c = 18'b0_1000_0000_0000_0000;
+ parameter [17:0] wr_d = 18'b1_0000_0000_0000_0000;
+
+ always @(posedge clk or negedge nReset)
+ if (!nReset)
+ begin
+ c_state <= idle;
+ cmd_ack <= 1'b0;
+ scl_oen <= 1'b1;
+ sda_oen <= 1'b1;
+ sda_chk <= 1'b0;
+ end
+ else if (rst | al)
+ begin
+ c_state <= idle;
+ cmd_ack <= 1'b0;
+ scl_oen <= 1'b1;
+ sda_oen <= 1'b1;
+ sda_chk <= 1'b0;
+ end
+ else
+ begin
+ cmd_ack <= 1'b0; // default no command acknowledge + assert cmd_ack only 1clk cycle
+
+ if (clk_en)
+ case (c_state) // synopsys full_case parallel_case
+ // idle state
+ idle:
+ begin
+ case (cmd) // synopsys full_case parallel_case
+ `I2C_CMD_START: c_state <= start_a;
+ `I2C_CMD_STOP: c_state <= stop_a;
+ `I2C_CMD_WRITE: c_state <= wr_a;
+ `I2C_CMD_READ: c_state <= rd_a;
+ default: c_state <= idle;
+ endcase
+
+ scl_oen <= scl_oen; // keep SCL in same state
+ sda_oen <= sda_oen; // keep SDA in same state
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ // start
+ start_a:
+ begin
+ c_state <= start_b;
+ scl_oen <= scl_oen; // keep SCL in same state
+ sda_oen <= 1'b1; // set SDA high
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ start_b:
+ begin
+ c_state <= start_c;
+ scl_oen <= 1'b1; // set SCL high
+ sda_oen <= 1'b1; // keep SDA high
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ start_c:
+ begin
+ c_state <= start_d;
+ scl_oen <= 1'b1; // keep SCL high
+ sda_oen <= 1'b0; // set SDA low
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ start_d:
+ begin
+ c_state <= start_e;
+ scl_oen <= 1'b1; // keep SCL high
+ sda_oen <= 1'b0; // keep SDA low
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ start_e:
+ begin
+ c_state <= idle;
+ cmd_ack <= 1'b1;
+ scl_oen <= 1'b0; // set SCL low
+ sda_oen <= 1'b0; // keep SDA low
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ // stop
+ stop_a:
+ begin
+ c_state <= stop_b;
+ scl_oen <= 1'b0; // keep SCL low
+ sda_oen <= 1'b0; // set SDA low
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ stop_b:
+ begin
+ c_state <= stop_c;
+ scl_oen <= 1'b1; // set SCL high
+ sda_oen <= 1'b0; // keep SDA low
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ stop_c:
+ begin
+ c_state <= stop_d;
+ scl_oen <= 1'b1; // keep SCL high
+ sda_oen <= 1'b0; // keep SDA low
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ stop_d:
+ begin
+ c_state <= idle;
+ cmd_ack <= 1'b1;
+ scl_oen <= 1'b1; // keep SCL high
+ sda_oen <= 1'b1; // set SDA high
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ // read
+ rd_a:
+ begin
+ c_state <= rd_b;
+ scl_oen <= 1'b0; // keep SCL low
+ sda_oen <= 1'b1; // tri-state SDA
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ rd_b:
+ begin
+ c_state <= rd_c;
+ scl_oen <= 1'b1; // set SCL high
+ sda_oen <= 1'b1; // keep SDA tri-stated
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ rd_c:
+ begin
+ c_state <= rd_d;
+ scl_oen <= 1'b1; // keep SCL high
+ sda_oen <= 1'b1; // keep SDA tri-stated
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ rd_d:
+ begin
+ c_state <= idle;
+ cmd_ack <= 1'b1;
+ scl_oen <= 1'b0; // set SCL low
+ sda_oen <= 1'b1; // keep SDA tri-stated
+ sda_chk <= 1'b0; // don't check SDA output
+ end
+
+ // write
+ wr_a:
+ begin
+ c_state <= wr_b;
+ scl_oen <= 1'b0; // keep SCL low
+ sda_oen <= din; // set SDA
+ sda_chk <= 1'b0; // don't check SDA output (SCL low)
+ end
+
+ wr_b:
+ begin
+ c_state <= wr_c;
+ scl_oen <= 1'b1; // set SCL high
+ sda_oen <= din; // keep SDA
+ sda_chk <= 1'b0; // don't check SDA output yet
+ // allow some time for SDA and SCL to settle
+ end
+
+ wr_c:
+ begin
+ c_state <= wr_d;
+ scl_oen <= 1'b1; // keep SCL high
+ sda_oen <= din;
+ sda_chk <= 1'b1; // check SDA output
+ end
+
+ wr_d:
+ begin
+ c_state <= idle;
+ cmd_ack <= 1'b1;
+ scl_oen <= 1'b0; // set SCL low
+ sda_oen <= din;
+ sda_chk <= 1'b0; // don't check SDA output (SCL low)
+ end
+
+ endcase
+ end
+
+
+ // assign scl and sda output (always gnd)
+ assign scl_o = 1'b0;
+ assign sda_o = 1'b0;
+
+endmodule