OpenCores
URL https://opencores.org/ocsvn/i2c/i2c/trunk

Subversion Repositories i2c

[/] [i2c/] [trunk/] [bench/] [verilog/] [i2c_slave_model.v] - Diff between revs 58 and 68

Only display areas with differences | Details | Blame | View Log

Rev 58 Rev 68
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
////                                                             ////
////                                                             ////
////  WISHBONE rev.B2 compliant synthesizable I2C Slave model    ////
////  WISHBONE rev.B2 compliant synthesizable I2C Slave model    ////
////                                                             ////
////                                                             ////
////                                                             ////
////                                                             ////
////  Authors: Richard Herveille (richard@asics.ws) www.asics.ws ////
////  Authors: Richard Herveille (richard@asics.ws) www.asics.ws ////
////           John Sheahan (jrsheahan@optushome.com.au)         ////
////           John Sheahan (jrsheahan@optushome.com.au)         ////
////                                                             ////
////                                                             ////
////  Downloaded from: http://www.opencores.org/projects/i2c/    ////
////  Downloaded from: http://www.opencores.org/projects/i2c/    ////
////                                                             ////
////                                                             ////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
////                                                             ////
////                                                             ////
//// Copyright (C) 2001,2002 Richard Herveille                   ////
//// Copyright (C) 2001,2002 Richard Herveille                   ////
////                         richard@asics.ws                    ////
////                         richard@asics.ws                    ////
////                                                             ////
////                                                             ////
//// This source file may be used and distributed without        ////
//// This source file may be used and distributed without        ////
//// restriction provided that this copyright statement is not   ////
//// restriction provided that this copyright statement is not   ////
//// removed from the file and that any derivative work contains ////
//// removed from the file and that any derivative work contains ////
//// the original copyright notice and the associated disclaimer.////
//// the original copyright notice and the associated disclaimer.////
////                                                             ////
////                                                             ////
////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ////
////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ////
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ////
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ////
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ////
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ////
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ////
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ////
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ////
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ////
//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ////
//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ////
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ////
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ////
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ////
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ////
//// POSSIBILITY OF SUCH DAMAGE.                                 ////
//// POSSIBILITY OF SUCH DAMAGE.                                 ////
////                                                             ////
////                                                             ////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
 
 
//  CVS Log
//  CVS Log
//
//
//  $Id: i2c_slave_model.v,v 1.7 2006-09-04 09:08:51 rherveille Exp $
//  $Id: i2c_slave_model.v,v 1.7 2006-09-04 09:08:51 rherveille Exp $
//
//
//  $Date: 2006-09-04 09:08:51 $
//  $Date: 2006-09-04 09:08:51 $
//  $Revision: 1.7 $
//  $Revision: 1.7 $
//  $Author: rherveille $
//  $Author: rherveille $
//  $Locker:  $
//  $Locker:  $
//  $State: Exp $
//  $State: Exp $
//
//
// Change History:
// Change History:
//               $Log: not supported by cvs2svn $
//               $Log: not supported by cvs2svn $
//               Revision 1.6  2005/02/28 11:33:48  rherveille
//               Revision 1.6  2005/02/28 11:33:48  rherveille
//               Fixed Tsu:sta timing check.
//               Fixed Tsu:sta timing check.
//               Added Thd:sta timing check.
//               Added Thd:sta timing check.
//
//
//               Revision 1.5  2003/12/05 11:05:19  rherveille
//               Revision 1.5  2003/12/05 11:05:19  rherveille
//               Fixed slave address MSB='1' bug
//               Fixed slave address MSB='1' bug
//
//
//               Revision 1.4  2003/09/11 08:25:37  rherveille
//               Revision 1.4  2003/09/11 08:25:37  rherveille
//               Fixed a bug in the timing section. Changed 'tst_scl' into 'tst_sto'.
//               Fixed a bug in the timing section. Changed 'tst_scl' into 'tst_sto'.
//
//
//               Revision 1.3  2002/10/30 18:11:06  rherveille
//               Revision 1.3  2002/10/30 18:11:06  rherveille
//               Added timing tests to i2c_model.
//               Added timing tests to i2c_model.
//               Updated testbench.
//               Updated testbench.
//
//
//               Revision 1.2  2002/03/17 10:26:38  rherveille
//               Revision 1.2  2002/03/17 10:26:38  rherveille
//               Fixed some race conditions in the i2c-slave model.
//               Fixed some race conditions in the i2c-slave model.
//               Added debug information.
//               Added debug information.
//               Added headers.
//               Added headers.
//
//
 
 
`include "timescale.v"
`include "timescale.v"
 
 
module i2c_slave_model (scl, sda);
module i2c_slave_model (scl, sda);
 
 
        //
        //
        // parameters
        // parameters
        //
        //
        parameter I2C_ADR = 7'b001_0000;
        parameter I2C_ADR = 7'b001_0000;
 
 
        //
        //
        // input && outpus
        // input && outpus
        //
        //
        input scl;
        input scl;
        inout sda;
        inout sda;
 
 
        //
        //
        // Variable declaration
        // Variable declaration
        //
        //
        wire debug = 1'b1;
        wire debug = 1'b1;
 
 
        reg [7:0] mem [3:0]; // initiate memory
        reg [7:0] mem [3:0]; // initiate memory
        reg [7:0] mem_adr;   // memory address
        reg [7:0] mem_adr;   // memory address
        reg [7:0] mem_do;    // memory data output
        reg [7:0] mem_do;    // memory data output
 
 
        reg sta, d_sta;
        reg sta, d_sta;
        reg sto, d_sto;
        reg sto, d_sto;
 
 
        reg [7:0] sr;        // 8bit shift register
        reg [7:0] sr;        // 8bit shift register
        reg       rw;        // read/write direction
        reg       rw;        // read/write direction
 
 
        wire      my_adr;    // my address called ??
        wire      my_adr;    // my address called ??
        wire      i2c_reset; // i2c-statemachine reset
        wire      i2c_reset; // i2c-statemachine reset
        reg [2:0] bit_cnt;   // 3bit downcounter
        reg [2:0] bit_cnt;   // 3bit downcounter
        wire      acc_done;  // 8bits transfered
        wire      acc_done;  // 8bits transfered
        reg       ld;        // load downcounter
        reg       ld;        // load downcounter
 
 
        reg       sda_o;     // sda-drive level
        reg       sda_o;     // sda-drive level
        wire      sda_dly;   // delayed version of sda
        wire      sda_dly;   // delayed version of sda
 
 
        // statemachine declaration
        // statemachine declaration
        parameter idle        = 3'b000;
        parameter idle        = 3'b000;
        parameter slave_ack   = 3'b001;
        parameter slave_ack   = 3'b001;
        parameter get_mem_adr = 3'b010;
        parameter get_mem_adr = 3'b010;
        parameter gma_ack     = 3'b011;
        parameter gma_ack     = 3'b011;
        parameter data        = 3'b100;
        parameter data        = 3'b100;
        parameter data_ack    = 3'b101;
        parameter data_ack    = 3'b101;
 
 
        reg [2:0] state; // synopsys enum_state
        reg [2:0] state; // synopsys enum_state
 
 
        //
        //
        // module body
        // module body
        //
        //
 
 
        initial
        initial
        begin
        begin
           sda_o = 1'b1;
           sda_o = 1'b1;
           state = idle;
           state = idle;
        end
        end
 
 
        // generate shift register
        // generate shift register
        always @(posedge scl)
        always @(posedge scl)
          sr <= #1 {sr[6:0],sda};
          sr <= #1 {sr[6:0],sda};
 
 
        //detect my_address
        //detect my_address
        assign my_adr = (sr[7:1] == I2C_ADR);
        assign my_adr = (sr[7:1] == I2C_ADR);
        // FIXME: This should not be a generic assign, but rather
        // FIXME: This should not be a generic assign, but rather
        // qualified on address transfer phase and probably reset by stop
        // qualified on address transfer phase and probably reset by stop
 
 
        //generate bit-counter
        //generate bit-counter
        always @(posedge scl)
        always @(posedge scl)
          if(ld)
          if(ld)
            bit_cnt <= #1 3'b111;
            bit_cnt <= #1 3'b111;
          else
          else
            bit_cnt <= #1 bit_cnt - 3'h1;
            bit_cnt <= #1 bit_cnt - 3'h1;
 
 
        //generate access done signal
        //generate access done signal
        assign acc_done = !(|bit_cnt);
        assign acc_done = !(|bit_cnt);
 
 
        // generate delayed version of sda
        // generate delayed version of sda
        // this model assumes a hold time for sda after the falling edge of scl.
        // this model assumes a hold time for sda after the falling edge of scl.
        // According to the Phillips i2c spec, there s/b a 0 ns hold time for sda
        // According to the Phillips i2c spec, there s/b a 0 ns hold time for sda
        // with regards to scl. If the data changes coincident with the clock, the
        // with regards to scl. If the data changes coincident with the clock, the
        // acknowledge is missed
        // acknowledge is missed
        // Fix by Michael Sosnoski
        // Fix by Michael Sosnoski
        assign #1 sda_dly = sda;
        assign #1 sda_dly = sda;
 
 
 
 
        //detect start condition
        //detect start condition
        always @(negedge sda)
        always @(negedge sda)
          if(scl)
          if(scl)
            begin
            begin
                sta   <= #1 1'b1;
                sta   <= #1 1'b1;
                d_sta <= #1 1'b0;
                d_sta <= #1 1'b0;
                sto   <= #1 1'b0;
                sto   <= #1 1'b0;
 
 
                if(debug)
                if(debug)
                  $display("DEBUG i2c_slave; start condition detected at %t", $time);
                  $display("DEBUG i2c_slave; start condition detected at %t", $time);
            end
            end
          else
          else
            sta <= #1 1'b0;
            sta <= #1 1'b0;
 
 
        always @(posedge scl)
        always @(posedge scl)
          d_sta <= #1 sta;
          d_sta <= #1 sta;
 
 
        // detect stop condition
        // detect stop condition
        always @(posedge sda)
        always @(posedge sda)
          if(scl)
          if(scl)
            begin
            begin
               sta <= #1 1'b0;
               sta <= #1 1'b0;
               sto <= #1 1'b1;
               sto <= #1 1'b1;
 
 
               if(debug)
               if(debug)
                 $display("DEBUG i2c_slave; stop condition detected at %t", $time);
                 $display("DEBUG i2c_slave; stop condition detected at %t", $time);
            end
            end
          else
          else
            sto <= #1 1'b0;
            sto <= #1 1'b0;
 
 
        //generate i2c_reset signal
        //generate i2c_reset signal
        assign i2c_reset = sta || sto;
        assign i2c_reset = sta || sto;
 
 
        // generate statemachine
        // generate statemachine
        always @(negedge scl or posedge sto)
        always @(negedge scl or posedge sto)
          if (sto || (sta && !d_sta) )
          if (sto || (sta && !d_sta) )
            begin
            begin
                state <= #1 idle; // reset statemachine
                state <= #1 idle; // reset statemachine
 
 
                sda_o <= #1 1'b1;
                sda_o <= #1 1'b1;
                ld    <= #1 1'b1;
                ld    <= #1 1'b1;
            end
            end
          else
          else
            begin
            begin
                // initial settings
                // initial settings
                sda_o <= #1 1'b1;
                sda_o <= #1 1'b1;
                ld    <= #1 1'b0;
                ld    <= #1 1'b0;
 
 
                case(state) // synopsys full_case parallel_case
                case(state) // synopsys full_case parallel_case
                    idle: // idle state
                    idle: // idle state
                      if (acc_done && my_adr)
                      if (acc_done && my_adr)
                        begin
                        begin
                            state <= #1 slave_ack;
                            state <= #1 slave_ack;
                            rw <= #1 sr[0];
                            rw <= #1 sr[0];
                            sda_o <= #1 1'b0; // generate i2c_ack
                            sda_o <= #1 1'b0; // generate i2c_ack
 
 
                            #2;
                            #2;
                            if(debug && rw)
                            if(debug && rw)
                              $display("DEBUG i2c_slave; command byte received (read) at %t", $time);
                              $display("DEBUG i2c_slave; command byte received (read) at %t", $time);
                            if(debug && !rw)
                            if(debug && !rw)
                              $display("DEBUG i2c_slave; command byte received (write) at %t", $time);
                              $display("DEBUG i2c_slave; command byte received (write) at %t", $time);
 
 
                            if(rw)
                            if(rw)
                              begin
                              begin
                                  mem_do <= #1 mem[mem_adr];
                                  mem_do <= #1 mem[mem_adr];
 
 
                                  if(debug)
                                  if(debug)
                                    begin
                                    begin
                                        #2 $display("DEBUG i2c_slave; data block read %x from address %x (1)", mem_do, mem_adr);
                                        #2 $display("DEBUG i2c_slave; data block read %x from address %x (1)", mem_do, mem_adr);
                                        #2 $display("DEBUG i2c_slave; memcheck [0]=%x, [1]=%x, [2]=%x", mem[4'h0], mem[4'h1], mem[4'h2]);
                                        #2 $display("DEBUG i2c_slave; memcheck [0]=%x, [1]=%x, [2]=%x", mem[4'h0], mem[4'h1], mem[4'h2]);
                                    end
                                    end
                              end
                              end
                        end
                        end
 
 
                    slave_ack:
                    slave_ack:
                      begin
                      begin
                          if(rw)
                          if(rw)
                            begin
                            begin
                                state <= #1 data;
                                state <= #1 data;
                                sda_o <= #1 mem_do[7];
                                sda_o <= #1 mem_do[7];
                            end
                            end
                          else
                          else
                            state <= #1 get_mem_adr;
                            state <= #1 get_mem_adr;
 
 
                          ld    <= #1 1'b1;
                          ld    <= #1 1'b1;
                      end
                      end
 
 
                    get_mem_adr: // wait for memory address
                    get_mem_adr: // wait for memory address
                      if(acc_done)
                      if(acc_done)
                        begin
                        begin
                            state <= #1 gma_ack;
                            state <= #1 gma_ack;
                            mem_adr <= #1 sr; // store memory address
                            mem_adr <= #1 sr; // store memory address
                            sda_o <= #1 !(sr <= 15); // generate i2c_ack, for valid address
                            sda_o <= #1 !(sr <= 15); // generate i2c_ack, for valid address
 
 
                            if(debug)
                            if(debug)
                              #1 $display("DEBUG i2c_slave; address received. adr=%x, ack=%b", sr, sda_o);
                              #1 $display("DEBUG i2c_slave; address received. adr=%x, ack=%b", sr, sda_o);
                        end
                        end
 
 
                    gma_ack:
                    gma_ack:
                      begin
                      begin
                          state <= #1 data;
                          state <= #1 data;
                          ld    <= #1 1'b1;
                          ld    <= #1 1'b1;
                      end
                      end
 
 
                    data: // receive or drive data
                    data: // receive or drive data
                      begin
                      begin
                          if(rw)
                          if(rw)
                            sda_o <= #1 mem_do[7];
                            sda_o <= #1 mem_do[7];
 
 
                          if(acc_done)
                          if(acc_done)
                            begin
                            begin
                                state <= #1 data_ack;
                                state <= #1 data_ack;
                                mem_adr <= #2 mem_adr + 8'h1;
                                mem_adr <= #2 mem_adr + 8'h1;
                                sda_o <= #1 (rw && (mem_adr <= 15) ); // send ack on write, receive ack on read
                                sda_o <= #1 (rw && (mem_adr <= 15) ); // send ack on write, receive ack on read
 
 
                                if(rw)
                                if(rw)
                                  begin
                                  begin
                                      #3 mem_do <= mem[mem_adr];
                                      #3 mem_do <= mem[mem_adr];
 
 
                                      if(debug)
                                      if(debug)
                                        #5 $display("DEBUG i2c_slave; data block read %x from address %x (2)", mem_do, mem_adr);
                                        #5 $display("DEBUG i2c_slave; data block read %x from address %x (2)", mem_do, mem_adr);
                                  end
                                  end
 
 
                                if(!rw)
                                if(!rw)
                                  begin
                                  begin
                                      mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory
                                      mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory
 
 
                                      if(debug)
                                      if(debug)
                                        #2 $display("DEBUG i2c_slave; data block write %x to address %x", sr, mem_adr);
                                        #2 $display("DEBUG i2c_slave; data block write %x to address %x", sr, mem_adr);
                                  end
                                  end
                            end
                            end
                      end
                      end
 
 
                    data_ack:
                    data_ack:
                      begin
                      begin
                          ld <= #1 1'b1;
                          ld <= #1 1'b1;
 
 
                          if(rw)
                          if(rw)
                            if(sr[0]) // read operation && master send NACK
                            if(sr[0]) // read operation && master send NACK
                              begin
                              begin
                                  state <= #1 idle;
                                  state <= #1 idle;
                                  sda_o <= #1 1'b1;
                                  sda_o <= #1 1'b1;
                              end
                              end
                            else
                            else
                              begin
                              begin
                                  state <= #1 data;
                                  state <= #1 data;
                                  sda_o <= #1 mem_do[7];
                                  sda_o <= #1 mem_do[7];
                              end
                              end
                          else
                          else
                            begin
                            begin
                                state <= #1 data;
                                state <= #1 data;
                                sda_o <= #1 1'b1;
                                sda_o <= #1 1'b1;
                            end
                            end
                      end
                      end
 
 
                endcase
                endcase
            end
            end
 
 
        // read data from memory
        // read data from memory
        always @(posedge scl)
        always @(posedge scl)
          if(!acc_done && rw)
          if(!acc_done && rw)
            mem_do <= #1 {mem_do[6:0], 1'b1}; // insert 1'b1 for host ack generation
            mem_do <= #1 {mem_do[6:0], 1'b1}; // insert 1'b1 for host ack generation
 
 
        // generate tri-states
        // generate tri-states
        assign sda = sda_o ? 1'bz : 1'b0;
        assign sda = sda_o ? 1'bz : 1'b0;
 
 
 
 
        //
        //
        // Timing checks
        // Timing checks
        //
        //
 
 
        wire tst_sto = sto;
        wire tst_sto = sto;
        wire tst_sta = sta;
        wire tst_sta = sta;
 
 
        specify
        specify
          specparam normal_scl_low  = 4700,
          specparam normal_scl_low  = 4700,
                    normal_scl_high = 4000,
                    normal_scl_high = 4000,
                    normal_tsu_sta  = 4700,
                    normal_tsu_sta  = 4700,
                    normal_thd_sta  = 4000,
                    normal_thd_sta  = 4000,
                    normal_tsu_sto  = 4000,
                    normal_tsu_sto  = 4000,
                    normal_tbuf     = 4700,
                    normal_tbuf     = 4700,
 
 
                    fast_scl_low  = 1300,
                    fast_scl_low  = 1300,
                    fast_scl_high =  600,
                    fast_scl_high =  600,
                    fast_tsu_sta  = 1300,
                    fast_tsu_sta  = 1300,
                    fast_thd_sta  =  600,
                    fast_thd_sta  =  600,
                    fast_tsu_sto  =  600,
                    fast_tsu_sto  =  600,
                    fast_tbuf     = 1300;
                    fast_tbuf     = 1300;
 
 
          $width(negedge scl, normal_scl_low);  // scl low time
          $width(negedge scl, normal_scl_low);  // scl low time
          $width(posedge scl, normal_scl_high); // scl high time
          $width(posedge scl, normal_scl_high); // scl high time
 
 
          $setup(posedge scl, negedge sda &&& scl, normal_tsu_sta); // setup start
          $setup(posedge scl, negedge sda &&& scl, normal_tsu_sta); // setup start
          $setup(negedge sda &&& scl, negedge scl, normal_thd_sta); // hold start
          $setup(negedge sda &&& scl, negedge scl, normal_thd_sta); // hold start
          $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // setup stop
          $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // setup stop
 
 
          $setup(posedge tst_sta, posedge tst_sto, normal_tbuf); // stop to start time
          $setup(posedge tst_sta, posedge tst_sto, normal_tbuf); // stop to start time
        endspecify
        endspecify
 
 
endmodule
endmodule
 
 
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.