AnsweredAssumed Answered

VC707 - I2C configuration

Question asked by tomatobiscuit on Sep 29, 2013
Latest reply on Oct 8, 2013 by tomatobiscuit

Hi,

 

I am trying to configure the i2c mux in hardware and I am trying to follow the reference design as a guide. I am using the i2c core from Open Cores and I am fairly certain that I am creating the right SDA and SCL outputs. However I never get an acknowledge from the i2c mux. This leads me to believe that I haven't done all the proper configuration steps prior to using the i2c bus. There are two lines in particular in the reference design that I was not able to decipher:

 

Xil_Out32((IIC_BASEADDR + 0x100), 0x002); // reset tx fifo

Xil_Out32((IIC_BASEADDR + 0x100), 0x001); // enable iic

 

This is called before every i2c interaction, but I can't anything in any documentation about an 'i2c enable' or the 'tx fifo'.

 

Here is the relevant parts of code: (I couldn't find syntax highlighting for any hdl )

typedef enum reg [3:0] {  STB_CYC,
                                CORE,
                                RESET,
                                C_INIT_W,
                                C_WAIT_W,
                                C_TIP_W,
                                C_ACK_W,
                                C_INIT_R,
                                C_WAIT_R,
                                C_TIP_R,
                                C_ACK_R,
                                IDLE    } iic_fsm_t;


module iic_config(input logic        clk,
                      input logic                rst,
                      inout logic                SDA,
                      inout logic                SCL,
                      output logic                IIC_RST,
                      output logic                done); 

   localparam  PRER_LO  = 8'hF4;
   localparam  PRER_HI  = 8'h01;
   localparam  CTRL_REG = 8'h80;
   localparam  WRITE    = 1'b0;
   localparam  READ     = 1'b1;


   /* IIC Switch configuration addr & data */
   localparam  SWITCH_ADDR = 7'h74;
   localparam  SWITCH_DATA = 8'h20;


   /* i2c core registers */
   reg [2:0]   wb_adr_i, next_wb_adr;
   reg [7:0]   wb_dat_i, next_wb_dat;
   reg [7:0]   wb_dat_o;
   reg                  wb_we_i;
   reg                  wb_stb_i;
   reg                  wb_cyc_i;
   reg                  wb_ack_o;
   reg                  wb_inta_o;
   reg                  scl_pad_i;
   reg                  scl_pad_o;
   reg                  scl_padoen_o;
   reg                  sda_pad_i;
   reg                  sda_pad_o;
   reg                  sda_padoen_o;


   reg                  wb_rst_i;

   /* internal registers */
   reg                  IIC_RST_out;
   reg                  done_out;

   reg                  switch_reset;
   reg [1:0]   count;

   iic_fsm_t   curr_state, next_state;


   assign IIC_RST = IIC_RST_out;
   assign done    = done_out;

   /* setup tri-state buffers for SDA and SCL */
   assign SCL       = scl_padoen_o ? 1'bz : scl_pad_o;
   assign SDA       = sda_padoen_o ? 1'bz : sda_pad_o;
   assign scl_pad_i = SCL;
   assign sda_pad_i = SDA;


   assign wb_rst_i  = 1'b0;


   /* i2c core top module */
   i2c_master_top i2c_core(.wb_clk_i     (clk),
                                 .wb_rst_i     (wb_rst_i),
                                 .arst_i       (rst),
                                 .wb_adr_i     (wb_adr_i),
                                 .wb_dat_i     (wb_dat_i),
                                 .wb_dat_o     (wb_dat_o),
                                 .wb_we_i      (wb_we_i),
                                 .wb_stb_i     (wb_stb_i),
                                 .wb_cyc_i     (wb_cyc_i),
                                 .wb_ack_o     (wb_ack_o),
                                 .wb_inta_o    (wb_inta_o),
                                 .scl_pad_i    (scl_pad_i),
                                 .scl_pad_o    (scl_pad_o),
                                 .scl_padoen_o (scl_padoen_o),
                                 .sda_pad_i    (sda_pad_i),
                                 .sda_pad_o    (sda_pad_o),
                                 .sda_padoen_o (sda_padoen_o),
                                 .*);

   /* reset IIC switch (pull low for one clock) */
   always @ (posedge clk, posedge rst) begin
      if (rst) begin
           switch_reset <= 1'b0;
           IIC_RST_out  <= 1'b0;
      end
      else begin
           IIC_RST_out <= 1'b1;
           if (~switch_reset)
             switch_reset <= 1'b1;
      end
   end // always @ (posedge clk, posedge rst)


   /* assert done once all the registers have been set */
   always @ (posedge clk, posedge rst) begin
      if (rst) begin
           done_out <= 1'b0;
      end
      else if (curr_state == IDLE) begin
           done_out <= 1'b1;
      end
   end


   always @ (posedge clk, posedge rst) begin
      if (rst) begin
           count <= 2'b00;
      end
      else begin
           count <= count + 1;
      end
   end


   /* FSM next state logic */
   always @ (posedge clk, posedge rst) begin
      if (rst) begin
           curr_state <= STB_CYC;
           wb_adr_i   <= 3'b000;
           wb_dat_i   <= 8'b00000000;
      end
      else begin
           curr_state <= next_state;
           wb_adr_i   <= next_wb_adr;
           wb_dat_i   <= next_wb_dat;
      end
   end


   /* FSM */
   always_comb begin
      /* Defaults */
      next_state  = curr_state;
      next_wb_adr = wb_adr_i;
      next_wb_dat = wb_dat_i;


      wb_stb_i = 1'b1;
      wb_cyc_i = 1'b1;
      wb_we_i  = 1'b1;


      cw_wait_rdy = 1'b0;
      cw_ack_rdy  = 1'b0;
      cr_wait_rdy = 1'b0;
      cr_ack_rdy  = 1'b0;


      case (curr_state)
          /* prepare the core with correct clock and parameters */
          STB_CYC: begin
             wb_stb_i = 1'b0;
             wb_cyc_i = 1'b0;
             next_state = CORE;
          end
          CORE: begin
             if (wb_adr_i <= 3'b010) begin
                case (wb_adr_i)
                    3'b000: begin // PRER low
                       next_wb_dat = PRER_LO;
                       if (wb_dat_o == PRER_LO) begin
                          next_wb_adr = 3'b001;
                       end
                    end
                    3'b001: begin // PRER high
                       next_wb_dat = PRER_HI;
                       if (wb_dat_o == PRER_HI) begin
                          next_wb_adr = 3'b010;
                       end
                    end
                    3'b010: begin // enable core
                       next_wb_dat = CTRL_REG;
                       if (wb_dat_o == CTRL_REG) begin
                          next_state = RESET;
                       end
                    end
                endcase // case (config_count)

             end // if (~core_config && wb_adr_i <= 3'b010)
          end

          /* reset the IIC bust switch */
          RESET: begin
             if (switch_reset && count == 0) begin
                next_state = C_INIT_W;
                next_wb_adr = 3'b011;
                next_wb_dat = { SWITCH_ADDR, WRITE };
             end
          end


          /* write destination address to i2c core */
          C_INIT_W: begin
             if (count == 0) begin
                next_state = C_WAIT_W;
                next_wb_adr = 3'b100;
                next_wb_dat = 8'h90;  // set START and WR bits
             end
          end


          /* give commands to i2c core */
          C_WAIT_W: begin
             if (wb_dat_o[1] && count == 0) begin
                next_state = C_TIP_W;
             end
          end


          /* wait for tip to negate */
          C_TIP_W: begin
             if (~wb_dat_o[1]) begin
                next_state = C_ACK_W;
             end
          end

          /* check for ack */
          C_ACK_W: begin
             if (~wb_dat_o[7]) begin
                next_state = C_INIT_R;
                next_wb_adr = 3'b011;
                next_wb_dat = SWITCH_DATA;
             end
          end


          /* write value to be sent to switch */
          C_INIT_R: begin
             if (count == 0) begin
                next_state = C_WAIT_R;
                next_wb_adr = 3'b100;
                next_wb_dat = 8'h50;  // set STOP and WR bits
             end
          end


          /* tell i2c core to write data to switch */
          C_WAIT_R: begin
             if (wb_dat_o[1] && count == 0) begin
                next_state = C_TIP_R;
             end
          end


          /* wait for top to negate */
          C_TIP_R: begin
             if (~wb_dat_o[1]) begin
                next_state = C_ACK_R;
             end
          end


          /* check for ack */
          C_ACK_R: begin
             if (~wb_dat_o[7]) begin
                next_state = IDLE;
             end
          end


          /* end of configuration */
          IDLE: begin
             next_state = IDLE;
          end


          default:
            next_state = curr_state;
      endcase // case (curr_state)
   end // always_comb

endmodule // iic_config


 

Ack isn't pulled low when it should be. Any help would be appreciated. We want a hardware solution; using the software layer isn't an option for us at this point.

 

Thanks

Outcomes