打印
[FPGA]

IIC主机可综合

[复制链接]
5761|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zzz645510|  楼主 | 2024-7-26 09:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
module i2c_master(
      wclk         ,        //系统时钟
      rst_wclk_n   ,        //系统复位
      i2c_en       ,        //使能信号
      txfifo_empty ,        //接收FIFO空标志
      hs_mode      ,        //高速模式
      data_wr_rd   ,       
      master_code  ,        //判断高速模式代码
      slave_addr   ,
      tx_data      ,
      i2c_comb_wr  ,
      rx_byte_num  ,
      div_num      ,
      hs_div       ,
//i2c input
      sda_in       ,
      scl_in       ,
//i2c output
      sda_out      ,
      scl_out      ,
      txfifo_rd_en ,
      rx_data      ,
      rx_wr_en_out
);

input       wclk         ;
input       rst_wclk_n   ;
input       i2c_en       ;
input       txfifo_empty ;
input       hs_mode      ;
input       data_wr_rd   ;
input [7:0] master_code  ;
input [7:0] slave_addr   ;
input [7:0] tx_data      ;
input       i2c_comb_wr  ;
input [7:0] rx_byte_num  ;
input [7:0] div_num      ;
input [7:0] hs_div       ;
input       sda_in       ;
input       scl_in       ;

output      sda_out      ;
output      scl_out      ;
output      txfifo_rd_en ;
output [7:0]rx_data      ;
output      rx_wr_en_out ;

wire [7:0]  master_code  ;
wire [7:0]  slave_addr   ;
reg         sda_out      ;
reg         scl_out      ;
reg [7:0]   rx_data      ;
//reg         txfifo_rd_en ;

//status define
localparam  s_idle          = 8'b0000_0000;
localparam  s_start         = 8'b0000_0001;
localparam  s_send_mst_code = 8'b0000_0011;
localparam  s_send_addr     = 8'b0000_0010;
localparam  s_addr_ack      = 8'b0000_0110;
localparam  s_tx_data       = 8'b0000_0111;
localparam  s_tx_ack        = 8'b0000_1111;
localparam  s_stop          = 8'b0000_1110;
localparam  s_rx_data       = 8'b0000_1100;
localparam  s_send_ack      = 8'b0000_1000;
localparam  s_restart       = 8'b0001_1000;

reg [7:0]   current_state ;
reg [7:0]   next_state    ;
reg         next_sda_out  ;
reg         next_scl_out  ;
reg [2:0]   clk_cnt       ;
reg [2:0]   next_clk_cnt  ;
//reg [2:0]   byte_cnt      ;
//reg [2:0]   next_byte_cnt ;
reg [7:0]   shift_dat     ;
reg [7:0]   next_shift_dat;
reg         txfifo_rd     ;
reg         txfifo_rd_d1  ;
reg         txfifo_rd_d2  ;

reg [7:0]   next_rx_data  ;
reg [7:0]   rx_byte       ;
reg [7:0]   next_rx_byte  ;
reg         i2c_wr_rd     ;
reg [3:0]   next_bit_cnt  ;
reg [3:0]   bit_cnt       ;
//reg [2:0]   rx_byte       ;
//reg [2:0]   next_rx_byte  ;
reg [7:0]   rx_wr_data    ;
reg         rx_wr_en      ;
reg [2:0]   rx_byte_cnt   ;
reg [2:0]   next_rx_byte_cnt;
reg [7:0]   frequence_div ;
reg [7:0]   time_out_cnt  ;
reg         rx_wr_en_d1   ;

wire        no_ack        ;
wire        div_end       ;
assign txfifo_rd_en = (current_state == s_idle || current_state == s_idle)? 1'b0:txfifo_rd_d1 ^ txfifo_rd_d2;
assign rx_wr_en_out = (current_state == s_idle || current_state == s_idle)? 1'b0:rx_wr_en ^ rx_wr_en_d1;
always @(posedge wclk or negedge rst_wclk_n)
begin
    if(!rst_wclk_n)
    begin
        sda_out      <= 1'b1;
        scl_out      <= 1'b1;
//        clk_cnt      <= 3'd0;
//        byte_cnt     <= 3'd0;
//        shift_dat    <= 8'd0;
        txfifo_rd_d1 <= 1'b0;
        txfifo_rd_d2 <= 1'b0;
        rx_data      <= 8'd0;
//        next_rx_data <= 8'd0;
        rx_byte      <= 8'd0;
        rx_byte_cnt  <= 3'd0;
//        bit_cnt      <= 8'd0;
        rx_wr_en_d1    <= 1'b0;
    end
    else
    begin
        sda_out      <= next_sda_out    ;
        scl_out      <= next_scl_out    ;
//       clk_cnt      <= next_clk_cnt    ;
//       byte_cnt     <= next_byte_cnt   ;
//       shift_dat    <= next_shift_dat  ;
        txfifo_rd_d1 <= txfifo_rd       ;
        txfifo_rd_d2 <= txfifo_rd_d1    ;
        rx_data      <= next_rx_data    ;
        rx_byte      <= next_rx_byte    ;
        rx_byte_cnt  <= next_rx_byte_cnt;
//        bit_cnt      <= next_bit_cnt    ;
        rx_wr_en_d1  <= rx_wr_en;
    end
end
always @(posedge wclk or negedge rst_wclk_n)
begin
    if(!rst_wclk_n)
    begin
        shift_dat <= 8'd0;     
    end
    else
        shift_dat <= next_shift_dat;
end

//clk divide
always @(posedge wclk or negedge rst_wclk_n)
begin
    if(!rst_wclk_n)
        frequence_div <= 8'd0;
    else if(div_end || i2c_en == 1'b0)
        frequence_div <= 8'd0;
    else if(scl_in == 1'b1)
        frequence_div <= frequence_div + 1'b1;   
    else
        frequence_div <= frequence_div;
end
assign div_end = hs_mode ?  ((current_state == s_start || current_state == s_send_mst_code )? frequence_div == div_num
                            : frequence_div == hs_div)
                            : frequence_div == div_num;
always @(posedge wclk or negedge rst_wclk_n)
begin
    if(!rst_wclk_n)
    begin
        clk_cnt <= 3'd1;
        bit_cnt <= 8'd0;
    end
    else if(scl_in == 1'b1 && div_end)
    begin
        clk_cnt <= next_clk_cnt;
        bit_cnt <= next_bit_cnt;
    end
    else
    begin
        clk_cnt <= clk_cnt;
        bit_cnt <= bit_cnt;
    end
end

always @(posedge wclk or negedge rst_wclk_n)
begin
    if(!rst_wclk_n)
        current_state <= s_idle;
    else if(time_out_cnt == 8'hFF)
        current_state <= s_stop;
    else
        current_state <= next_state;
end

always @(posedge wclk or negedge rst_wclk_n)
begin
    if(!rst_wclk_n)
        time_out_cnt = 8'd0;
    else if(time_out_cnt == 8'hFF)
        time_out_cnt = 8'd0;
    else if(scl_in == 1'b0)
        time_out_cnt = time_out_cnt + 1'b1;
end

always @(*)
begin
    case(current_state)
        s_idle:begin
            if(i2c_en && (!txfifo_empty && data_wr_rd) || !data_wr_rd)
            begin
                next_state   = s_start;
                next_clk_cnt = 3'd1 ;
                i2c_wr_rd    = data_wr_rd;
            end
            else
            begin
                next_state       = s_idle;
                next_sda_out     = 1'b1  ;
                next_scl_out     = 1'b1  ;
                next_rx_byte     = 8'd0  ;
                next_bit_cnt     = 4'd0  ;
                next_shift_dat   = 8'd0  ;
                txfifo_rd        = 1'b0  ;
                rx_wr_en         = 1'b0  ;
                next_rx_data     = 8'd0  ;
                next_rx_byte_cnt = 3'd0  ;
                rx_wr_data       = 8'd0;
            end
        end
        s_start:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd1:begin
                        next_sda_out = 1'b0;
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_sda_out = 1'b0;
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                        if(hs_mode)
                        begin
                            next_state      = s_send_mst_code;
                            next_shift_dat  = master_code;
                        end
                        else
                        begin
                            next_state     = s_send_addr;
                            next_shift_dat = slave_addr;
                        end
                    end
                    default :next_state = s_stop;
                endcase
            end
        end
        s_send_mst_code:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_sda_out = shift_dat[7];
                        next_scl_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                       if(bit_cnt == 4'd8)
                        begin
                            next_state     = s_send_addr;
                            next_shift_dat = slave_addr;
                            next_bit_cnt   = 3'd0;
                        end
                        else
                        begin
                            next_state     = s_send_mst_code;
                            next_shift_dat = {shift_dat[6:0],1'b1};
                            next_bit_cnt   = bit_cnt + 1'b1;
                        end
                    end
                default :next_state = s_stop;
                endcase
            end
        end
        s_send_addr:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_sda_out = shift_dat[7];
                        next_scl_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                       if(bit_cnt == 4'd7)
                        begin
                            next_state     = s_addr_ack;
                            next_bit_cnt   = 4'd0;
                            if( i2c_wr_rd == 1'b1)
                                txfifo_rd = ~txfifo_rd;
                        end
                        else
                        begin
                            next_state     = s_send_addr;
                            next_shift_dat = {shift_dat[6:0],1'b1};
                            next_bit_cnt   = bit_cnt + 1'b1;
                        end
                    end
                default :next_state = s_stop;
                endcase
            end
        end
        s_addr_ack:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_sda_out = 1'b1;
                        next_scl_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                        if(!no_ack && i2c_wr_rd == 1'b1)
                        begin
                            next_shift_dat = tx_data;
                            next_state = s_tx_data;
                        end
                        else if(!no_ack && i2c_wr_rd == 1'b0)
                            next_state = s_rx_data;
                        else
                            next_state = s_stop;
                    end
                endcase
            end
        end
        s_tx_data:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_sda_out = shift_dat[7];
                        next_scl_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                       if(bit_cnt == 4'd7)
                        begin
                            next_state     = s_tx_ack;
                            next_bit_cnt   = 4'd0;
                            if(txfifo_empty == 1'b0)
                                txfifo_rd = ~txfifo_rd;
                        end
                        else
                        begin
                            next_shift_dat = {shift_dat[6:0],1'b1};
                            next_bit_cnt   = bit_cnt + 1'b1;
                        end
                    end
                default :next_state = s_stop;
                endcase
            end
        end
        s_tx_ack:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_sda_out = 1'b1;
                        next_scl_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        if(!no_ack && txfifo_empty == 1'b1 && i2c_comb_wr)
                        begin
                            next_state  = s_restart;
                            i2c_wr_rd   = 1'b0   ;
                            next_clk_cnt = 3'd4;
                        end
                        else if(!no_ack && txfifo_empty == 1'b0)
                        begin
                            next_state     = s_tx_data;
                            next_shift_dat = tx_data;
                            next_clk_cnt   = 3'd2;
                        end
                        else
                            next_state = s_stop;
                    end
                    default :next_state = s_stop;
                endcase
            end
        end
        s_restart:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd4:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd3:begin
                        next_sda_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd2:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = 3'd1;
                    end
                    3'd1:begin
                        next_sda_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                        next_state     = s_send_addr;
                        next_shift_dat = {slave_addr[7:1],1'b1};
                    end
                    default :next_state = s_stop;
                endcase
            end
        end
        s_rx_data:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_scl_out = 1'b0;
                        next_sda_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                        next_rx_data = {rx_data[6:0],sda_in};
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                        if(rx_byte == 3'd7)
                        begin
                            next_rx_byte = 8'd0;
                            next_state   = s_send_ack;
                            rx_wr_data   = rx_data;
                            rx_wr_en     = ~rx_wr_en ;
                        end
                        else
                        begin
                            next_rx_byte = rx_byte + 1'b1;
                            next_state   = s_rx_data;
                        end
                    end
                    default :next_state = s_stop;
                endcase
            end
        end
        s_send_ack:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                        if(rx_byte_cnt == rx_byte_num)
                            next_sda_out = 1'b1;
                        else
                            next_sda_out = 1'b0;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_scl_out = 1'b0;
                        next_clk_cnt = 3'd2;
                        if(rx_byte_cnt == rx_byte_num)
                        begin
                            next_state = s_stop;
                        end
                        else
                        begin
                            next_rx_byte_cnt = rx_byte_cnt + 1'b1;
                            next_state       = s_rx_data;
                        end
                    end
                    default :next_state = s_stop;
                endcase
            end
        end
        s_stop:begin
            if(div_end)
            begin
                case(clk_cnt)
                    3'd2:begin
                        next_scl_out = 1'b0;
                        next_sda_out = 1'b0;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd1:begin
                        next_scl_out = 1'b1;
                        next_clk_cnt = clk_cnt - 1'b1;
                    end
                    3'd0:begin
                        next_sda_out = 1'b1;
                        next_clk_cnt = 3'd2;
                        next_state   = s_idle;
                    end
                    default :next_state = s_stop;
                endcase
            end
        end
    default:begin
        next_state = s_idle;
    end
    endcase
end

assign no_ack = ((current_state == s_addr_ack || current_state == s_tx_ack) && (sda_in == 1'b1))? 1'b1 : 1'b0;
endmodule

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

4

帖子

0

粉丝