Commit 2a260f5a authored by Andrey Filippov's avatar Andrey Filippov
Browse files

continue on sensor i2c

parent 730d8ca4
Loading
Loading
Loading
Loading
+181 −41
Original line number Diff line number Diff line
@@ -30,11 +30,12 @@ module sensor_i2c_prot(
    input            tand,         // table address/not data
    input     [19:0] td,           // table address/data in            
    input            twe,          // table write enable
    input            sda_in,       // data from sda pad
    output reg       sda,
    output reg       sda_en,
    output reg       scl,
    output           i2c_run,
    output [ 1:0] seq_mem_ra, // number of byte to read from the sequencer memory
    output reg [1:0] seq_mem_ra, // number of byte to read from the sequencer memory
    output    [ 1:0] seq_mem_re, // [0] - re, [1] - regen to teh sequencer memory
    input     [ 7:0] seq_rd,     // data from the sequencer memory 
    output    [ 7:0] rdata,
@@ -42,45 +43,184 @@ module sensor_i2c_prot(
);
    reg    [ 7:0] twa;
    wire   [31:0] tdout;
    wire   [ 7:0] reg_ah =         tdout[7:0];   // MSB of the register address (instead of the byte 2)
    wire   [ 6:0] slave_a =        tdout[14:8];  // 7-bit slave address (lsb == 1), used instead of the byte 3
    wire   [ 3:0] num_bytes_send = tdout[19:16]; // number of bytes to send (if more than 4 will skip stop and continue with next data
    reg    [ 7:0] reg_ah;   // MSB of the register address (instead of the byte 2)
    reg    [ 7:0] slave_a_rah;    // 8-bit slave address , used instead of the byte 3, later replaced with reg_ah
    reg    [ 3:0] num_bytes_send; // number of bytes to send (if more than 4 will skip stop and continue with next data
    reg    [ 3:0] bytes_left_send; // Number of bytes left in register write sequence (not counting sa?)
    reg           run_reg_wr;       // run register write
    reg           run_extra_wr;    // continue register write (if more than sa + 4bytes)
    reg           run_reg_rd;
    reg    [ 6:0] run_reg_wr;      // run register write [6] - start, [5] - send sa, [4] - send high byte (from table),..[0] - send stop 
    reg    [ 4:0] run_extra_wr;    // continue register write (if more than sa + 4bytes) [4] - byte 3, .. [1]- byte0,  [0] - stop
    reg           run_extra_wr_d;  // any of run_extra_wr bits, delayed by 1
    reg    [ 7:0] run_reg_rd; // [7] - start, [6] SA (byte 3), [5] (optional) - RA_msb, [4] - RA_lsb, [3] - restart, [2] - SA, [1] - read bytes, [0] - stop
    reg           i2c_done;
    wire          i2c_next_byte;
    reg    [ 2:0] mem_re;
    reg    [ 2:0] table_re;
    reg           mem_valid;
    reg    [ 3:0] table_re;
    
    
    wire          decode_reg_rd = &seq_rd[7:5];
    wire          start_wr_seq = !run_extra_wr && !decode_reg_rd && read_mem_msb;
    reg           read_mem_msb;
    wire          decode_reg_rd = &seq_rd[7:4];
    wire          start_wr_seq_w = !run_extra_wr_d && !decode_reg_rd && read_mem_msb;
    
    wire          snd_start_w = run_reg_wr[6] || 1'b0; // add start & restart of read
    wire          snd_stop_w =  run_reg_wr[0] || 1'b0; // add stop of read
    wire          snd9_w     =  (|run_reg_wr[5:1]) || 1'b0; // add for read and extra write;

    reg           snd_start;
    reg           snd_stop;
    reg           snd9;
    // the following signals are mutually exclusive, can be encoded to 2 bits. Invaluid during next_cmd_d
    reg           send_seq_data; // including first SA for read
    reg           send_rd_sa;    // from the table
    reg           send_sa_rah;   // send slave address/ high address from the table
    reg           send_rd;       // send 1fe/1ff to read data
    reg     [6:0] rd_sa;         // 7-bit slave address for reading
    wire    [1:0] sel_sr_in = { // select source for the shift register
                  send_sa_rah | send_rd,
                  send_rd_sa | send_rd};
    reg     [8:0] sr_in; // input data for the shift register
   
    
    

    wire          i2c_rdy;
    reg           next_cmd; // i2c command (start/stop/data) accepted, proceed to the next stage
    reg           next_cmd_d; // next cycle after next_cmd (first at new state)
    wire          pre_next_cmd = (snd_start || snd_stop || snd9) && i2c_rdy;
    reg           next_byte_wr;
    
    reg           read_address_bytes; // 0 - single-byte register adderss, 1 - two-byte register address
    reg    [ 2:0] read_data_bytes;    // 1..8 bytes to read from teh i2c slave (0 is 8!)
    
    reg    [ 1:0] initial_address; // initial data byte to read: usually 3  but for extra write may be different
    wire   [ 3:0] initial_address_w =  bytes_left_send - 1; // if bytes left to send == 0 - will be 3                      
    wire          unused;            // unused ackn signal  
    assign seq_mem_re = mem_re[1:0];
    
    always @ (posedge mclk) begin
        read_mem_msb <= mem_re[1] && (seq_mem_ra == 3); // reading sequencer data MSB
        mem_re <= {mem_re[1:0], i2c_start | i2c_next_byte}; 
        table_re <= {table_re[1:0], start_wr_seq};
        run_extra_wr_d <= |run_extra_wr;
        read_mem_msb <= mem_re[1] && (seq_mem_ra == 3); // reading sequencer data MSB - change to other one-hot?
        
        if      (mrst || i2c_rst)                                 run_extra_wr <= 0;
        else if (i2c_start && (bytes_left_send !=0))              run_extra_wr <= 1;
        else if (i2c_done)                                        run_extra_wr <= 0;
        table_re <= {table_re[2:0], start_wr_seq_w};
        
        if (table_re[2]) begin
            reg_ah <=         tdout[7:0];   // MSB of the register address (instead of the byte 2)
            num_bytes_send <= tdout[19:16]; // number of bytes to send (if more than 4 will skip stop and continue with next data
        end
        if      (table_re[2])               slave_a_rah <= {tdout[14:8], 1'b0};
        else if (next_cmd && run_reg_wr[6]) slave_a_rah <= reg_ah; // will copy even if not used
        
        next_cmd <= pre_next_cmd;
        
        next_cmd_d <= next_cmd;
        
        next_byte_wr <= snd9 && i2c_rdy; // same time as next_cmd

        snd_start <= snd_start_w; // add & i2c_ready? Not really needed as any i2c stage will be busy for long enough
        snd_stop <=  snd_stop_w;
        snd9 <=      snd9_w;
        
        if     (mrst || i2c_rst) bytes_left_send <= 0;
        else if (start_wr_seq_w) bytes_left_send <= num_bytes_send;
        else if (next_byte_wr)   bytes_left_send <= bytes_left_send - 1;

        // calculate stages for each type of commands
        // start and write sa and some bytes, stop if number of bytes <= 4 at teh end        
        if      (mrst || i2c_rst) run_reg_wr <= 0;
        else if (start_wr_seq)                                    run_reg_wr <= 1;
        else if (i2c_done)                                        run_reg_wr <= 0;
        else if (start_wr_seq_w)  run_reg_wr <= 7'h40;
        else if (next_cmd)        run_reg_wr <= {1'b0,         // first "start"
                                                run_reg_wr[6], // slave_addr - always after start
                                                run_reg_wr[5] & (|num_bytes_send[3:2]), // MSB (from the table)
                                                run_reg_wr[4] | (run_reg_wr[5] & (num_bytes_send == 4'h3)), // byte 2 (from input)
                                                run_reg_wr[3] | (run_reg_wr[5] & (num_bytes_send == 4'h2)), // byte 1 (from input)
                                                run_reg_wr[2] | (run_reg_wr[5] & (num_bytes_send == 4'h1)), // byte 0 (from input)
                                                run_reg_wr[1] & (bytes_left_send == 4'h1)
                                                };
        // send just bytes (up to 4), stop if nothing left 
        if      (mrst || i2c_rst)                    run_extra_wr <= 0;
        else if (i2c_start && (bytes_left_send !=0)) run_extra_wr <= {
                                                        |bytes_left_send[3:2], // >= 4 bytes left
                                                        (bytes_left_send == 3), // exactly 3 bytes left
                                                        (bytes_left_send == 2), // exactly 2 bytes left
                                                        (bytes_left_send == 1), // exactly 1 bytes left (zero should never be left)
                                                        1'b0
                                                     };
        else if (next_cmd)                          run_extra_wr <= {
                                                        1'b0,
                                                        run_extra_wr[4],
                                                        run_extra_wr[3],
                                                        run_extra_wr[2],
                                                        run_extra_wr[1] & (bytes_left_send == 4'h1)
                                                     };

//     reg    [ 7:0] run_reg_rd; // [7] - start, [6] SA (byte 3), [5] (optional) - RA_msb, [4] - RA_lsb, [3] - restart, [2] - SA, [1] - read bytes, [0] - stop

        if (!run_extra_wr &&  decode_reg_rd && read_mem_msb) read_address_bytes <= seq_rd[3];
        
        if (!run_extra_wr &&  decode_reg_rd && read_mem_msb) read_data_bytes <= seq_rd[2:0];
        else if (run_reg_rd[1] && next_cmd)                  read_data_bytes <= read_data_bytes - 1;
        // read i2c data
        if      (mrst || i2c_rst)                                  run_reg_rd <= 0;
        else if (!run_extra_wr &&  decode_reg_rd && read_mem_msb) run_reg_rd <= 1;
        else if (i2c_done)                                        run_reg_rd <= 0;
        else if (!run_extra_wr_d &&  decode_reg_rd && read_mem_msb)  run_reg_rd <= 8'h80;
        else if (next_cmd)        run_reg_rd <= {1'b0,         // first "start"
                                                run_reg_rd[7], // slave_addr - always after start (bit0 = 0)
                                                run_reg_rd[6] & read_address_bytes, // optional MSB of the register address
                                                run_reg_rd[5] | (run_reg_rd[6] & ~read_address_bytes), // LSB of the register address
                                                run_reg_rd[4], // restart
                                                run_reg_rd[3], // send slave address with 1 in bit[0]
                                                run_reg_rd[2] | (run_reg_rd[1] & (read_data_bytes != 3'h1)), // repeat reading bytes
                                                run_reg_rd[1] & (read_data_bytes == 3'h1)
                                                };
        // read sequencer memory byte (for the current word)
        mem_re <= {mem_re[1:0], i2c_start | next_cmd_d & (
                               (|run_reg_wr[3:1]) |
                               (|run_extra_wr[4:1]) |
                               (|run_reg_rd[6:4]))};
        initial_address <=  initial_address_w[1:0]; // if bytes left to send is 0 mod 4 - will be 3 (read MSB)
        seq_mem_ra <= i2c_start ? initial_address : { // run_extra_wr[4] is not needed - it will be read by i2c_start
                                                       run_reg_wr[3] | run_extra_wr[3] | run_reg_rd[6],
                                                       run_reg_wr[2] | run_extra_wr[2] | run_reg_rd[5]
                                                    };
        if (mrst || i2c_rst || i2c_start || next_cmd ) mem_valid <= 0;
        else if (mem_re[2])                            mem_valid <= 1;
        
        // calculate snd9 and delay it if waiting for memory using mem_valid, set din[8:0] 
        if (run_reg_rd[6] && mem_re[2]) rd_sa <= seq_rd[7:1]; // store sa to use with read
        
        // table_re[2] - valid table data (slave_a, reg_ah, reg_ah
        send_seq_data <= !next_cmd && mem_valid && ((|run_reg_wr[3:1]) || (|run_extra_wr[4:1]) || (|run_reg_rd[6:4])); 
        send_rd_sa <=    !next_cmd && run_reg_rd[2];
        send_sa_rah <=   !next_cmd && (|run_reg_wr[6:5]);
        send_rd <=       !next_cmd && run_reg_rd[1];
        
        if (mrst || i2c_rst) snd9 <= 0;
        else snd9 <= snd9 ? (!i2c_rdy) : ((send_seq_data || send_rd_sa || send_sa_rah || send_rd) && !next_cmd);
        case (sel_sr_in)
            2'h0: sr_in <= {seq_rd, 1'b1};
            2'h1: sr_in <= {rd_sa, 2'b11};
            2'h2: sr_in <= {slave_a_rah, 1'b1};
            2'h3: sr_in <= {8'hff,(read_data_bytes == 3'h1)}; 
        endcase
        
    end    

    sensor_i2c_scl_sda sensor_i2c_scl_sda_i (
        .mrst      (mrst),          // input
        .mclk      (mclk),          // input
        .i2c_rst   (i2c_rst),       // input
        .i2c_dly   (i2c_dly),       // input[7:0] 
        .snd_start (snd_start),     // input
        .snd_stop  (snd_stop),      // input
        .snd9      (snd9),          // input
        .din       (sr_in),         // input[8:0] 
        .dout      ({rdata,unused}),// output[8:0] 
        .dout_stb  (rvalid),        // output reg 
        .scl       (), // output reg 
        .sda_in    (sda_in),        // input
        .sda       (), // output reg 
        .ready     (i2c_rdy),       // output register
        .bus_busy  (), // output reg 
        .is_open   () // output
    );
     
     
    // table write  
    always @ (posedge mclk) begin
+126 −0
Original line number Diff line number Diff line
/*******************************************************************************
 * Module: sensor_i2c_scl_sda
 * Date:2015-10-06  
 * Author: andrey     
 * 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/> .
 *******************************************************************************/
`timescale 1ns/1ps

module  sensor_i2c_scl_sda(
    input         mrst,         // @ posedge mclk
    input         mclk,         // global clock
    input         i2c_rst,
    input  [ 7:0] i2c_dly,      // bit duration-1 (>=2?), 1 unit - 4 mclk periods
    input         snd_start,
    input         snd_stop,
    input         snd9,
    input  [ 8:0] din,
    output [ 8:0] dout,        //
    output reg    dout_stb,    // dout contains valid data
    output reg    scl,         // i2c SCL signal
    input         sda_in,      // i2c SDA signal form I/O pad           
    output reg    sda,         // i2c SDA signal           
    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;
    wire         snd_start_w =  snd_start && !busy_r;
    wire         snd_stop_w =   snd_stop && !busy_r;
    wire         snd9_w =       snd9 && !busy_r;
    wire         start_w =      (snd_start || snd_stop || snd9_w) && !busy_r;
    reg          pre_dly_over;
    reg          dly_over;
    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
    assign ready = !busy_r;
    assign is_open =  is_open_r;
    assign dout =     sr;
    always @ (posedge mclk) begin
        if      (rst)         seq_start_restart <= 0;
        else if (snd_start_w) seq_start_restart <= is_open_r ? 4'h8 : 4'h4;
        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]};

        if      (rst)                                             seq_bit <= 0;
        else if (snd_start_w || (seq_bit[0] && (bits_left != 0))) seq_bit <= 4'h8;
        else if (dly_over)                                        seq_bit <= {1'b0,seq_bit[3:1]};
        
        if      (rst)                                             bits_left <= 0;
        else if (snd9_w)                                          bits_left <= 4'h8;
        else if (dly_over && seq_bit[0])                          bits_left <= bits_left - 1;


        if      (rst)     busy_r <= 0;
        else if (start_w) busy_r <= 1;
        else if (done_r)  busy_r <= 0;
        
        pre_dly_over <=  (dly_cntr == 2);
        
        dly_over <=      pre_dly_over;
        
        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) &&
                                   (bits_left[3:1] == 0);
                                   
        if (!busy_r || dly_over) dly_cntr <= i2c_dly;
        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?
        
        if      (snd_start_w)            sr <= din;
        else if (dly_over && seq_bit[0]) sr <= {sr[7:0], sda_r};
        
        dout_stb <= dly_over && seq_bit[0] && (bits_left == 0);
        
        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;
        else if (first_cyc)  scl <= !busy_r ||
                                     seq_start_restart[2] ||  seq_start_restart[1] ||
                                     seq_stop[1] || seq_stop[0] ||
                                     seq_bit[2] || seq_bit[1];
                                                       
        if      (rst)        sda <= 1;
        else if (first_cyc)  sda <= !busy_r ||
                                     seq_start_restart[3] ||  seq_start_restart[2] ||
                                     seq_stop[0] ||
                                     (sr[8] && (|seq_bit));
       bus_busy <= busy_r;
    end

endmodule