sensor_i2c_scl_sda.v 9.07 KB
Newer Older
Andrey Filippov's avatar
Andrey Filippov committed
1 2 3
/*******************************************************************************
 * Module: sensor_i2c_scl_sda
 * Date:2015-10-06  
4
 * Author: Andrey Filippov     
Andrey Filippov's avatar
Andrey Filippov committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * Description: Generation of i2c signals
 *
 * Copyright (c) 2015 Elphel, Inc .
 * sensor_i2c_scl_sda.v is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  sensor_i2c_scl_sda.v is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
20 21 22 23 24 25
 *
 * Additional permission under GNU GPL version 3 section 7:
 * If you modify this Program, or any covered work, by linking or combining it
 * with independent modules provided by the FPGA vendor only (this permission
 * does not extend to any 3-rd party modules, "soft cores" or macros) under
 * different license terms solely for the purpose of generating binary "bitstream"
26
 * files and/or simulating the code, the copyright holders of this Program give
27 28
 * you the right to distribute the covered work without those independent modules
 * as long as the source code for them is available from the FPGA vendor free of
Andrey Filippov's avatar
Andrey Filippov committed
29
 * charge, and there is no dependence on any encrypted modules for simulating of
30 31 32
 * the combined code. This permission applies to you if the distributed code
 * contains all the components and scripts required to completely simulate it
 * with at least one of the Free Software programs.
Andrey Filippov's avatar
Andrey Filippov committed
33 34 35 36
 *******************************************************************************/
`timescale 1ns/1ps

module  sensor_i2c_scl_sda(
Andrey Filippov's avatar
Andrey Filippov committed
37 38
    input         mrst,           // @ posedge mclk
    input         mclk,           // global clock
Andrey Filippov's avatar
Andrey Filippov committed
39
    input         i2c_rst,
Andrey Filippov's avatar
Andrey Filippov committed
40 41 42
    input  [ 7:0] i2c_dly,        // bit duration-1 (>=2?), 1 unit - 4 mclk periods
    input         active_sda,     // active pull SDA 
    input         early_release_0,// release SDA immediately after end of SCL if next bit is 1 (for ACKN). Data hold time by slow 0->1 
Andrey Filippov's avatar
Andrey Filippov committed
43 44 45
    input         snd_start,
    input         snd_stop,
    input         snd9,
46
    input         rcv,         // receive mode (valid with snd9) - master receives, slave - sends
Andrey Filippov's avatar
Andrey Filippov committed
47
    input  [ 8:0] din,
48
    output reg [ 8:0] dout,        //
Andrey Filippov's avatar
Andrey Filippov committed
49 50 51
    output reg    dout_stb,    // dout contains valid data
    output reg    scl,         // i2c SCL signal
    input         sda_in,      // i2c SDA signal form I/O pad           
Andrey Filippov's avatar
Andrey Filippov committed
52 53
    output reg    sda,         // i2c SDA signal
    output reg    sda_en,      // drive SDA when SDA=0 and during second half of SCL = 0 interval (also during stop) 
Andrey Filippov's avatar
Andrey Filippov committed
54 55 56 57 58 59 60 61 62
    output        ready,       // ready to accept commands
    output reg    bus_busy,    // i2c bus busy (1 cycle behind !ready)
    output        is_open      // i2c channel is open (started, no stop yet)
);
    wire         rst = mrst || i2c_rst;
    reg          is_open_r;
    reg    [8:0] sr;
    reg    [7:0] dly_cntr;
    reg          busy_r;
63 64 65 66
    wire         snd_start_w =  snd_start && ready; //!busy_r;
    wire         snd_stop_w =   snd_stop && ready; // !busy_r;
    wire         snd9_w =       snd9 && ready; //!busy_r;
    wire         start_w =      (snd_start || snd_stop || snd9_w) && ready; //!busy_r;
Andrey Filippov's avatar
Andrey Filippov committed
67 68
    reg          pre_dly_over;
    reg          dly_over;
69
//    reg          dly_over_d;
Andrey Filippov's avatar
Andrey Filippov committed
70 71 72 73 74 75 76
    reg    [3:0] seq_start_restart;
    reg    [2:0] seq_stop;
    reg    [3:0] seq_bit;
    reg    [3:0] bits_left;
    reg          done_r;
    reg          sda_r;
    reg          first_cyc; // first clock cycle for the delay interval - update SCL/SDA outputs
77 78
    reg          active_sda_r; // registered @ snd9, disable in rcv mode
    reg          active_sda_was_0; // only use active SDA if previous bit was 0 or it is receive mode
79
    reg          early_release_r;  // to enable it only for LSB before ACKN during send
80
    reg          rcv_r;
81 82
    wire         busy_w = busy_r && ! done_r;
//    wire         pre_dout_stb = dly_over && seq_bit[0] && (bits_left == 0); 
83 84 85
//    assign ready = !busy_r;
    assign ready = !busy_w;
    
Andrey Filippov's avatar
Andrey Filippov committed
86
    assign is_open =  is_open_r;
87
//    assign dout =     sr;
Andrey Filippov's avatar
Andrey Filippov committed
88
    always @ (posedge mclk) begin
89 90
        active_sda_was_0 <= !sda || rcv_r;
        if (snd9_w) rcv_r <= rcv;
91 92
        
        early_release_r <= early_release_0 && !rcv_r && (bits_left == 1); // only before ACN during master send
93 94 95 96 97 98

        // disable active_sda in send messages for the last (ACKN) bit, for the receive - all but ACKN
        if      (snd9_w)                    active_sda_r <= active_sda && !rcv;
        else if (snd_start_w || snd_stop_w) active_sda_r <= active_sda;
        else if (dly_over && seq_bit[0])    active_sda_r <= active_sda && ((bits_left != 1) ^ rcv_r);
//active_sda_r && !sda    
Andrey Filippov's avatar
Andrey Filippov committed
99
        if      (rst)         seq_start_restart <= 0;
100
        else if (snd_start_w) seq_start_restart <= (is_open_r && !seq_stop[0]) ? 4'h8 : 4'h4;
Andrey Filippov's avatar
Andrey Filippov committed
101 102 103 104 105 106
        else if (dly_over)    seq_start_restart <= {1'b0,seq_start_restart[3:1]};
    
        if      (rst)         seq_stop <= 0;
        else if (snd_stop_w)  seq_stop <= 3'h4;
        else if (dly_over)    seq_stop <= {1'b0,seq_stop[2:1]};

107 108 109
        if      (rst)                                                    seq_bit <= 0;
        else if (snd9_w || (seq_bit[0] && (bits_left != 0) && dly_over)) seq_bit <= 4'h8;
        else if (dly_over)                                               seq_bit <= {1'b0,seq_bit[3:1]};
Andrey Filippov's avatar
Andrey Filippov committed
110
        
111
        if      (rst)                                             bits_left <= 4'h0;
Andrey Filippov's avatar
Andrey Filippov committed
112
        else if (snd9_w)                                          bits_left <= 4'h8;
113
        else if (dly_over && seq_bit[0] && (|bits_left))          bits_left <= bits_left - 1;
Andrey Filippov's avatar
Andrey Filippov committed
114 115 116 117 118 119


        if      (rst)     busy_r <= 0;
        else if (start_w) busy_r <= 1;
        else if (done_r)  busy_r <= 0;
        
120
//        pre_dly_over <=  (dly_cntr == 3);
Andrey Filippov's avatar
Andrey Filippov committed
121 122 123
        pre_dly_over <=  (dly_cntr == 2);
        
        dly_over <=      pre_dly_over;
124
//        dly_over_d <=    dly_over;
Andrey Filippov's avatar
Andrey Filippov committed
125 126 127 128 129
        if      (rst)     done_r <= 0;
        else              done_r <= pre_dly_over &&
                                   (bits_left == 0) &&
                                   (seq_start_restart[3:1] == 0) &&
                                   (seq_stop[2:1] == 0) &&
130
                                   (seq_bit[3:1] == 0);
Andrey Filippov's avatar
Andrey Filippov committed
131
                                   
132 133 134 135
//        if (!busy_r || dly_over_d) dly_cntr <= i2c_dly;
//        else                       dly_cntr <= dly_cntr - 1;
        
        if (!busy_w || dly_over) dly_cntr <= i2c_dly;
Andrey Filippov's avatar
Andrey Filippov committed
136 137 138 139
        else                     dly_cntr <= dly_cntr - 1;
        
        if (dly_over && seq_bit[1]) sda_r <= sda_in; // just before the end of SCL pulse - delay it by a few clocks to match external latencies?
        
140
        if      (snd9_w)                 sr <= din;
Andrey Filippov's avatar
Andrey Filippov committed
141 142
        else if (dly_over && seq_bit[0]) sr <= {sr[7:0], sda_r};
        
143 144 145 146
        dout_stb <= dly_over && seq_bit[0] && (bits_left == 0) && rcv_r;
//        dout_stb <= pre_dout_stb;
//        if (pre_dout_stb) dout <= {sr[7:0],sda_r};
        if (done_r) dout <= {sr[7:0],sda_r};
Andrey Filippov's avatar
Andrey Filippov committed
147 148 149 150 151 152 153 154
        
        if      (rst)                              is_open_r <= 0;
        else if (dly_over && seq_start_restart[0]) is_open_r <= 1;                       
        else if (dly_over && seq_stop[0])          is_open_r <= 0;
        
        first_cyc <= start_w || dly_over;

        if      (rst)        scl <= 1;
155 156
//        else if (first_cyc)  scl <= !busy_r || // Wrong whe "open"?
        else if (first_cyc)  scl <= (scl && !busy_w) || // Wrong whe "open"?
Andrey Filippov's avatar
Andrey Filippov committed
157 158 159 160 161
                                     seq_start_restart[2] ||  seq_start_restart[1] ||
                                     seq_stop[1] || seq_stop[0] ||
                                     seq_bit[2] || seq_bit[1];
                                                       
        if      (rst)        sda <= 1;
162 163
//        else if (first_cyc)  sda <= !busy_r ||
        else if (first_cyc)  sda <= (sda && !busy_w) ||
Andrey Filippov's avatar
Andrey Filippov committed
164 165 166
                                     seq_start_restart[3] ||  seq_start_restart[2] ||
                                     seq_stop[0] ||
                                     (sr[8] && (|seq_bit));
Andrey Filippov's avatar
Andrey Filippov committed
167 168

        if      (rst)        sda_en <= 1;
169 170 171 172
//        else if (first_cyc)  sda_en <= busy_r && (
// TODO: no active SDA if previous SDA was 1
        else if (first_cyc)  sda_en <= busy_w && (
                                     (active_sda_r && active_sda_was_0 && (seq_start_restart[3] || seq_stop[0] || (sr[8] && seq_bit[3]))) || // !sda uses output reg
Andrey Filippov's avatar
Andrey Filippov committed
173 174 175
                                     (|seq_start_restart[1:0]) ||
                                     (|seq_stop[2:1]) ||
                                     (!sr[8] && (|seq_bit[3:1])) ||
176 177
//                                     (!sr[8] && seq_bit[0] && (!early_release_0 || !sr[7])));
                                     (!sr[8] && seq_bit[0] && (!early_release_r || !sr[7])));
Andrey Filippov's avatar
Andrey Filippov committed
178 179 180 181 182
       bus_busy <= busy_r;
    end

endmodule