I2C problems with HMC5883L Triple Axis Breakout (Verilog)

Hi everyone,

I am very new to FPGA development and could use some help trying to figure out what is going wrong with my I2C code I’ve written to communicate with the magnetometer.

My issue is that I seem to be able to write to the configuration registers (first image) but I am only getting zeros from the data registers (second image). I even compared my results with what I was seeing with a logic analyzer when the sensor was hooked up to an Arduino and tried to match the protocol as closely as I could. I understand that the sensor sends zeros when attempting to read an invalid address but 0x03 should be valid. I have attached my code (I apologize it’s probably quite messy) and some images of what’s happening.

Another issue I’m having is that if change the orientation of the chip to flat on the table, SDA drops low and I don’t see anything happening anymore (fourth image). Am I missing some kind of necessary calibration?

Thanks!

http://i.imgur.com/wAqOUP4.png?2

http://i.imgur.com/fSxw1xr.png?1

http://i.imgur.com/GlMCMte.png?1

http://i.imgur.com/HaDLjae.png?2

///////////////////////////////////////////////////////////////////////////////////////////////////
// Company: University of Colorado
//
// File: i2c_interface2.v
//
// Description: 
//
// I2C interface with sparkfun hmc5883l magnetometer breakout
//
// Targeted device: <Family::ProASIC3L> <Die::M1A3P1000L> <Package::484 FBGA>
// Author: 
//
/////////////////////////////////////////////////////////////////////////////////////////////////// 


module i2c_interface2(
    input clk,
    input rst,
    //input byte_to_mag,
    input [23:0] timestamp,
    inout sda,
    output scl,
    output [79:0] data,
    output state
);

localparam IDLE = 4'ha,
           START = 4'h1,
           ADDR = 4'h2,
           RW = 4'h3,
           ACK_IN = 4'h4,
           ACK_OUT = 4'h5,
           INIT = 4'h6,
           DATA = 4'h7,
           STOP = 4'h8,
           WAIT = 4'h9,
           DATA_POINTER = 4'hb,
           STARTUP = 4'h0;

localparam M = 8'h4d;

localparam reg0_addr = 8'h0/*8'b11010101*/, //Testing value
        reg0_val = 8'h0c,
        reg1_addr = 8'h1,
        reg1_val = 8'h0,
        reg2_addr = 8'h2,
        reg2_val = 8'h0,
        reg3_addr = 8'h3,
        addr = 7'h1e,
        six = 8'h6;
reg [16:0] wait_ctr;
reg [7:0] state_a, state_hold,test;
reg begin_data;
reg [47:0] data_a, data_out;
reg sda_a, stop_enable, init, start_ctr, scl_enable;
reg [3:0] ctr_a, init_ctr_a, data_cntr;
reg test_sda, force_reset;
reg [1:0] data_mode;

assign scl = ((clk) | (state_a == IDLE) | (state_a == STOP) | (scl_enable == 1'b0) | (state_a == STARTUP)) & (state_a != WAIT); //high during idle/stop, pulls low on start and active high pulses
assign sda =(state_a != ACK_IN)&(state_a != DATA) ? sda_a : 1'bz; //Sets whether sda is in or out
assign data = {data_out,timestamp,M};
assign state = !((state_a != ACK_IN)&(state_a != DATA));

always @(negedge clk or negedge rst) begin //Ensures clock is low when sda changes
    
        if ((rst == 1'b0)|| force_reset) begin
            state_a = STARTUP;
            sda_a = 1'b1;
            ctr_a = 4'd0;
            begin_data = 1'b0;
            init_ctr_a = 4'd6;
            stop_enable = 1'b0;
            data_a = 48'b0;
            init = 1'b0;
            start_ctr = 1'b0;
            data_cntr = 4'd0;
            test = 8'd0;
            data_out = 48'b0;
            data_mode = 2'd0;
            wait_ctr = 17'd0;
            scl_enable = 1'b0;
            state_hold = 4'ha;
            force_reset = 1'b0;
            
        end

        else if (!clk) begin
            test_sda = !((state_a != ACK_IN)&(state_a != DATA));
            

            case(state_a)
                STARTUP: begin
                if (wait_ctr >= 100000) begin
                    state_a = IDLE;
                    wait_ctr = 0;
                end
                else wait_ctr = wait_ctr + 1;

                end
                IDLE: begin
                    scl_enable = 1'b0;
                    ctr_a = 7'd0;
                    sda_a = 1'b1;
                    start_ctr = 1'b0;
                    test = 1;
                    if ((wait_ctr >= 13340)|| ~init || (data_mode == 2'd0)) begin
                        state_a = START;
                        wait_ctr = 0;
                    end
                    else wait_ctr = wait_ctr + 1;

                    if ((wait_ctr >= 2) && (sda == 1'b0)) begin
                        force_reset = 1'b1;
                    end
                end

                START: begin
                    if ((start_ctr == 1'b0)&&(sda == 1'b1)) begin
                        start_ctr = 1'b1;
                        sda_a = 1'b0;
                        test = 2;
                        //scl_enable = 1'b0;
                    end
                    else if (start_ctr == 1'b1 && sda == 1'b0) begin
                        start_ctr = 1'b0;
                        state_a = ADDR;
                        ctr_a = 7;
                        test = 3;
                    end

                end

                ADDR: begin
                    if (ctr_a != 0) begin
                        test = 5;
                        scl_enable = 1'b1;
                        sda_a = addr[ctr_a-1];
                        ctr_a = ctr_a-1;
                    end
                    else begin
                        state_a = RW;
                        if (~init & (data_mode == 2'd0)) begin
                            sda_a = 1'b0;
                            state_hold = INIT;
                        end
                        else begin
                            if (data_mode == 2'd1) begin
                                state_hold = DATA;
                                sda_a = 1'b1;
                            end
                            else if (data_mode == 2'd0) begin
                                sda_a = 1'b0;
                                state_hold = DATA_POINTER;
                            end

                        end
                    end
                                end

                RW: begin //low is write, high is read & low ACK is confirm.
                    scl_enable = 1'b1;
                    test = 6;
                    sda_a = 1'b0;
                    state_a = ACK_IN;
                end

                ACK_IN: begin //look for slave ACK
                    test = 7;
                    if (~sda) begin
                        if (~init) begin
                            if (state_hold == STOP) begin
                                sda_a = 1'b0;
                                state_a = WAIT;
                                scl_enable = 1'b0;
                            end
                            else begin
                                sda_a = 1'b0;
                                state_a = WAIT;
                                scl_enable = 1'b1;
                            end
                        end
                       
                        if (init) begin
                            if (state_hold == START) sda_a = 1'b1;
                            else sda_a = 1'b0;
                            if (data_cntr == 4'd0) data_cntr = 4'd5;
                            state_a = WAIT;
     
                        end
                    end
                    else begin // If there's a NACK, stop and go back
                        state_a = WAIT;
                        state_hold = STOP;
                        scl_enable = 1'b0;
                        sda_a = 1'b0;
                        if (init) data_cntr = data_cntr + 1;
                        else init_ctr_a = init_ctr_a + 1;
                    end
                end

                ACK_OUT: begin //send ACK to slave
                    sda_a = 1'b0;
                    if (data_cntr == 0) begin
                        state_a = WAIT;
                        state_hold = STOP;
                        scl_enable = 1'b0;
                    end
                    else begin
                        state_a = WAIT;
                        ctr_a = 7;
                    end
                end

                INIT: begin //write to registers 
                    test = 8;
                    scl_enable = 1'b1;

                    if (ctr_a == 0) begin
                        test = 14;
                        state_a = ACK_IN;
                        sda_a = 0;
                        if (init_ctr_a !=0) begin
                            init_ctr_a = init_ctr_a - 1;
                        end
                        
                    end
                    else begin
                        if (init_ctr_a == 2) begin
                            test = 9;
                            sda_a = reg0_addr[ctr_a-1];
                        end
                        else if (init_ctr_a == 1) begin
                            test = 10;
                            sda_a = reg0_val[ctr_a-1];
                            state_hold = STOP;
                        end
                        else if (init_ctr_a == 4) begin
                            test = 11;
                            sda_a = reg1_addr[ctr_a-1];
                        end
                        else if (init_ctr_a ==3) begin
                            test = 11;
                            sda_a = reg1_val[ctr_a-1];
                            state_hold = STOP;
                        end
                        else if (init_ctr_a == 6) begin
                            test = 11;
                            sda_a = reg2_addr[ctr_a-1];
                        end
                        else if (init_ctr_a == 5) begin
                            test = 12;
                            sda_a = reg2_val[ctr_a-1];
                            state_hold = STOP;
                        end

                    end
                        if (ctr_a != 0) ctr_a = ctr_a - 1;
                end

                DATA_POINTER: begin
                    if (data_mode == 0) begin
                        if (ctr_a == 0) begin
                            state_a = ACK_IN;
                            state_hold = STOP;
                            data_mode = 2'd1;
                        end
                        else ctr_a = ctr_a - 1;
                        sda_a = reg3_addr[ctr_a];                    
                        
                    end

                end
                DATA: begin //read data

                        scl_enable = 1'b1;
                        if (ctr_a == 0) begin
                            state_a = ACK_OUT;

                            if (data_cntr == 0) begin
                                stop_enable = 1'b1; ///set low after stop mode
                                data_out = data_a; // data's ready to go to memory
                                data_mode = 2'd0;
                            end    

                        end
                        else begin
                            data_a[ctr_a+data_cntr*8] = sda;
                            ctr_a = ctr_a - 1;
                        end
                    //end
                end
                STOP: begin //initiate stop condition
                    
                    stop_enable = 1'b0;
                    sda_a = 1'b1;
                    state_a = IDLE;
                    scl_enable = 1'b0;
                    if (data_cntr == 0) data_cntr = 6;
                    sda_a = 1'b0;

                end
                WAIT: begin //waits clk period for next cycle
                    test = 15; 
                    state_a = state_hold;
                    if (~init) begin
                        if (init_ctr_a == 6) begin
                            sda_a = reg0_addr[7];
                            ctr_a = 7;
                        end
                        else if (init_ctr_a == 5) begin
                            sda_a = reg0_val[7];
                            ctr_a = 7;
                        end
                        if (init_ctr_a == 4) begin
                            sda_a = reg1_addr[7];
                            ctr_a = 7;
                        end
                        else if (init_ctr_a == 3) begin
                            sda_a = reg1_val[7];
                            ctr_a = 7;
                        end
                        if (init_ctr_a == 2) begin
                            sda_a = reg2_addr[7];
                            ctr_a = 7;
                        end
                        else if (init_ctr_a == 1) begin
                            sda_a = reg2_val[7];
                            ctr_a = 7;
                        end
                        else if (init_ctr_a == 0) begin
                            init = 1'b1;
                            ctr_a = 7;
                            //sda_a = 1'b0;
                        end

                        
                    end
                    else begin
                        ctr_a = 7;
                        if (data_cntr == 0) begin
                            scl_enable = 1'b0;
                            sda_a = 1'b0;
                        end
                        else if (data_mode == 2'd1 && (state_hold == DATA)) data_cntr = data_cntr - 1;
                    end
                end

                    
        
        endcase
    end
                
    
   end             

     
endmodule