打印
[Verilog HDL]

IIC的Verilog实现

[复制链接]
1425|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gaochy1126|  楼主 | 2023-1-31 21:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
实现的代码详情:
端口声明与内部信号
module IIC#(parameter id0 = 1'b0,id1 = 1'b0,id2 = 1'b1)(
input CLK_50M,
input rst_n,
input [7:0] data,
input [7:0] command,
input req,
inout iic_SLC,
inout iic_SDA,
output reg [7:0]data_out,
output reg finish,
output reg io_en   
);

//iic控制
reg [8:0] SLC_cnt;
reg cnt_flag;
reg [4:0] n_cnt;
reg n_cnt_flag;
reg iic_SDA_reg;
reg iic_SLC_reg;
//外部输入缓存
reg [7:0] data_reg;
reg [7:0] command_reg;
reg req_reg;
//输出数据缓存
reg [7:0] data_out_reg;
//内部需要被访问的寄存器
reg [7:0] inner_data;
//状态信号
reg busy;
wire send;
wire start;
wire stop;
//id寻找
wire match_flag;
//ack错误
reg error;
//状态机
reg [6:0]cur_state;
reg [6:0]next_state;
localparam   s_idle   =  7'b000_0001;
localparam   s_send   =  7'b000_0010;
localparam   s_mwrite =  7'b000_0100;
localparam   s_mread  =  7'b000_1000;
localparam   s_busy   =  7'b001_0000;
localparam   s_swrite =  7'b010_0000;
localparam   s_sread  =  7'b100_0000;

//id序列检测
reg [7:0]id_state;
localparam   id_idle      = 8'b0000_0001;
localparam   id_1         = 8'b0000_0010;
localparam   id_10        = 8'b0000_0100;
localparam   id_101       = 8'b0000_1000;
localparam   id_1010      = 8'b0001_0000;
localparam   id_1010_0    = 8'b0010_0000;
localparam   id_1010_00   = 8'b0100_0000;
localparam   id_1010_001  = 8'b1000_0000;


我们把这个IIC设备的id低三位设置成001,前四位假设为1010,则此IIC设备的id码为1010 001。
SCL时钟生成
always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    cnt_flag <= 1'b0;
  else if(start)
    cnt_flag <= 1'b1;
  else if(stop)
    cnt_flag <= 1'b0;

always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    SCL_cnt <= 'd0;
  else if(SCL_cnt == 'd499)
    SCL_cnt <= 'd0;
  else if(cnt_flag)
    SCL_cnt <= SCL_cnt + 1'b1;
  else
    SCL_cnt <= 'd0;
       
always@(posedge CLK_50M or negedge rst_n)       
  if(~rst_n)
    iic_SCL_reg <= 1'b1;
  else if(SCL_cnt == 'd249 || SCL_cnt == 'd499)
    iic_SCL_reg <= ~iic_SCL_reg;
         
assign iic_SCL = iic_SCL_reg;


50M时钟计数 250 翻转一次 iic_SCL ,生成100K频率的 iic_SCL 同步时钟 。
由于SCL形成伪握手机制需要配合IIC的物理结构实现,只用verilog实现不了,我们就没有给 iic_SCL 添加IOBUF,使得 iic_SCL 一直输出有效。
同时由于 inout 信号需要为 wire 型,所以用reg型的 iic_SCL_reg 放置在 always 块中,并用 assign 赋值给iic_SCL。
状态信号及状态机
assign start = ((cur_state == s_send && n_cnt == 'd0) || 
((cur_state == s_idle || cur_state == s_busy) && n_cnt == 'd0))? (iic_SCL && ~iic_SDA) : 1'b0;
assign stop  = (n_cnt == 'd19 && SCL_cnt > 'd125)? (iic_SCL && iic_SDA) : 1'b0;

always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)begin
    command_reg <= 'd0;
         data_reg <= 'd0;
         req_reg <= 1'b0;end
  else if(req)begin
    command_reg <= command;
    data_reg <= data;
         req_reg <= req;end
  else if(next_state == s_send && SCL_cnt == 'd249)
    req_reg <= 1'b0;
  else if(error)         
    req_reg <= 1'b1;
       
assign send = req_reg && ~finish && ~busy;

always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    busy <= 1'b0;
  else if(stop)
    busy <= 1'b0;  
  else if(~req_reg && start)
    busy <= 1'b1;  

always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    next_state <= s_idle;
  else
    next_state <= cur_state;

always@(*)
  case(next_state)
    s_idle : begin
      if(busy)
        cur_state = s_busy;
      else if(send)
             cur_state = s_send;
           else
             cur_state = s_idle;end
         
         s_send : begin
           if(n_cnt == 'd8 && iic_SDA == 1'b0 && SCL_cnt == 'd499)
             cur_state = s_mwrite;
                else if(n_cnt == 'd8 && iic_SDA == 1'b1 && SCL_cnt == 'd499)
                  cur_state = s_mread;
                else
                  cur_state = s_send;end
   
    s_busy : begin   
            if(match_flag && iic_SDA == 1'b0 && SCL_cnt == 'd499)//这时候作为从机,主机的写是从机的读
        cur_state = s_sread;
      else if(match_flag && iic_SDA == 1'b1 && SCL_cnt == 'd499)//这时候作为从机,主机的读是从机的写               
        cur_state = s_swrite;
                else if(stop)
                  cur_state = s_idle;
      else
        cur_state = s_busy;end

    s_mwrite : begin
           if(error)
                  cur_state = s_idle;
                else if(stop)
             cur_state = s_idle;
                else
                  cur_state = s_mwrite;end

    s_mread : begin
           if(error)
                  cur_state = s_idle;      
                else if(stop)
             cur_state = s_idle;
                else
                  cur_state = s_mread; end

    s_swrite : begin
           if(error)
                  cur_state = s_idle;      
                else if(stop)
             cur_state = s_idle;
                else
                  cur_state = s_swrite;end

    s_sread : begin
      if(stop)
             cur_state = s_idle;
                else
                  cur_state = s_sread;end                           
    default : cur_state = s_idle;                  
endcase


id序列检测
always@(posedge iic_SCL or negedge rst_n)
  if(~rst_n)
    id_state <= id_idle;
  else if(busy && n_cnt <= 'd8) begin
    case(id_state)
         id_idle:begin  
           if(iic_SDA == 1'b1)
             id_state <= id_1;
                else
                  id_state <= id_idle; end         
    id_1:begin         
      if(iic_SDA == 1'b0)
             id_state <= id_10;
                else
                  id_state <= id_idle; end         
    id_10:begin         
      if(iic_SDA == 1'b1)
             id_state <= id_101;
                else
                  id_state <= id_1; end             
    id_101:begin         
      if(iic_SDA == 1'b0)
             id_state <= id_1010;
                else
                  id_state <= id_1; end
    id_1010:begin         
      if(iic_SDA == 1'b0)
             id_state <= id_1010_0;
                else
                  id_state <= id_101; end
    id_1010_0:begin         
      if(iic_SDA == 1'b0)
             id_state <= id_1010_00;
                else
                  id_state <= id_1; end
    id_1010_00:begin         
      if(iic_SDA == 1'b1)
             id_state <= id_1010_001;
                else
                  id_state <= id_idle; end
    id_1010_001:         
                  id_state <= id_idle;           
         default:
             id_state <= id_idle;
         endcase
         end         
    else                  
      id_state <= id_idle;

assign match_flag = (id_state == id_1010_001);


线性序列机
always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    n_cnt <= 'd0;
  else if(~n_cnt_flag)
    n_cnt <= 'd0;       
  else if(n_cnt_flag && SCL_cnt == 'd499)
    n_cnt <= n_cnt + 1'b1;

always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    n_cnt_flag <= 1'b0;
  else if(cur_state == s_idle)
    n_cnt_flag <= 1'b0;
  else if(stop)       
    n_cnt_flag <= 1'b0;
  else if(start)
    n_cnt_flag <= 1'b1;

always@(*)
begin
  if(next_state == s_idle) begin
      iic_SDA_reg = 1'bz;
      io_en = 1'b0;        end
  else if(next_state == s_send) begin
  case(n_cnt)
    0:begin iic_SDA_reg = 1'b0;io_en = 1'b1;end
    1:begin iic_SDA_reg = command_reg[7];io_en = 1'b1;end
    2:begin iic_SDA_reg = command_reg[6];io_en = 1'b1;end
    3:begin iic_SDA_reg = command_reg[5];io_en = 1'b1;end
    4:begin iic_SDA_reg = command_reg[4];io_en = 1'b1;end
    5:begin iic_SDA_reg = command_reg[3];io_en = 1'b1;end
    6:begin iic_SDA_reg = command_reg[2];io_en = 1'b1;end
    7:begin iic_SDA_reg = command_reg[1];io_en = 1'b1;end
    8:begin iic_SDA_reg = command_reg[0];io_en = 1'b1;end
  default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end
  endcase
  end
  
  else if(next_state == s_mwrite) begin
  case(n_cnt)  
    9: begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
    10:begin iic_SDA_reg = data_reg[7];io_en = 1'b1;end
    11:begin iic_SDA_reg = data_reg[6];io_en = 1'b1;end
    12:begin iic_SDA_reg = data_reg[5];io_en = 1'b1;end       
    13:begin iic_SDA_reg = data_reg[4];io_en = 1'b1;end
    14:begin iic_SDA_reg = data_reg[3];io_en = 1'b1;end
    15:begin iic_SDA_reg = data_reg[2];io_en = 1'b1;end
    16:begin iic_SDA_reg = data_reg[1];io_en = 1'b1;end
    17:begin iic_SDA_reg = data_reg[0];io_en = 1'b1;end
    18:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
    19:begin
      io_en = 1'b1;
      if(SCL_cnt > 'd125)
        iic_SDA_reg = 1'b1;
      else if(SCL_cnt <= 'd124)
        iic_SDA_reg = 1'b0; end
    default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end
  endcase
  end
  
  else if(next_state == s_mread) begin
  case(n_cnt)  
    9,10,11,12,13,14,15,16,17:begin
      iic_SDA_reg = 1'bz;io_en = 1'b0;end
    18:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack       
    19:begin
      io_en = 1'b1;
      if(SCL_cnt > 'd125)begin
        iic_SDA_reg = 1'b1;end
      else if(SCL_cnt <= 'd124)
        iic_SDA_reg = 1'b0; end         
    default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end           
  endcase
  end

  else if(next_state == s_sread) begin
  case(n_cnt)
    9:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack
    10,11,12,13,14,15,16,17:begin
      iic_SDA_reg = 1'bz;io_en = 1'b0;end       
    18:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
    19:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//等待主机发送stop
    default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end  
  endcase
  end

  else if(next_state == s_swrite) begin
  case(n_cnt)
    9 :begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack
    10:begin iic_SDA_reg = inner_data[7];io_en = 1'b1;end
    11:begin iic_SDA_reg = inner_data[6];io_en = 1'b1;end
    12:begin iic_SDA_reg = inner_data[5];io_en = 1'b1;end
    13:begin iic_SDA_reg = inner_data[4];io_en = 1'b1;end
    14:begin iic_SDA_reg = inner_data[3];io_en = 1'b1;end
    15:begin iic_SDA_reg = inner_data[2];io_en = 1'b1;end
    16:begin iic_SDA_reg = inner_data[1];io_en = 1'b1;end
    17:begin iic_SDA_reg = inner_data[0];io_en = 1'b1;end
    18:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
    19:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//等待主机发送stop
    default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end          
  endcase
  end       
       
  else begin
    iic_SDA_reg = 1'bz;io_en = 1'b0;end
end  

assign iic_SDA = io_en ? iic_SDA_reg : 1'bz;


inner_data,dout,finish控制
always@(posedge iic_SCL or negedge rst_n)
  if(~rst_n)
    inner_data <= 'd0;
  else if(next_state == s_sread && n_cnt >= 'd11 && n_cnt <= 'd18)
    inner_data <= {inner_data[6:0],iic_SDA};  

always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    finish <= 1'b0;
  else if(next_state == s_mread && n_cnt == 'd19)         
         finish <= 1'b1;
  else if(next_state == s_mwrite && n_cnt == 'd19)         
         finish <= 1'b1;
  else
    finish <= 1'b0;  

always@(posedge CLK_50M or negedge rst_n)
  if(~rst_n)
    error <= 1'b0;
  else if(next_state == s_mwrite)begin       
    if(n_cnt == 'd9 ||n_cnt == 'd18) begin
      if(iic_SDA == 1'b0)
        error <= 1'b1;
      else
        error <= 1'b0; end
         else
      error <= 1'b0;        end
  else if(next_state == s_mread) begin
    if(n_cnt == 'd9) begin
      if(iic_SDA == 1'b0)
        error <= 1'b1;
      else
        error <= 1'b0; end
         else
      error <= 1'b0;         end         
  else if(next_state == s_swrite)begin
    if(n_cnt == 'd18) begin
      if(iic_SDA == 1'b0)
        error <= 1'b1;
      else
        error <= 1'b0; end
         else
      error <= 1'b0;         end
  else
    error <= 1'b0;                 

always@(posedge iic_SCL or negedge rst_n)
  if(~rst_n)
    data_out_reg <= 'd0;
  else if(next_state == s_mread && n_cnt < 'd18)
    data_out_reg <= {data_out_reg[6:0],iic_SDA};

always@(posedge CLK_50M or negedge rst_n)         
  if(~rst_n)
    data_out <= 'd0;
  else if(next_state == s_mread && n_cnt == 'd19)       
    data_out <= data_out_reg;
  else
    data_out <= 'd0;

endmodule


top顶层
module IIC_top(
input ack,
input CLK_50M,
input rst_n,
input [7:0] data,
input [7:0] command,
input req,
inout iic_SCL,
output  finish,
output [7:0] data_out   
);

wire iic_SDA_top;
wire io_en;

assign iic_SDA_top = (!io_en)? ack : 1'bz;


IIC#(.id0(1'b0),
    .id1 (1'b0),
    .id2 (1'b1)
         )master1(
.CLK_50M (CLK_50M) ,
.rst_n   (rst_n)   ,
.data    (data)    ,
.command (command) ,
.req     (req  )   ,
.iic_SCL (iic_SCL) ,
.iic_SDA (iic_SDA_top) ,
.finish  (finish ) ,
.io_en   (io_en  ) ,
.data_out (data_out)
);

endmodule





使用特权

评论回复

相关帖子

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

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1050

主题

11299

帖子

26

粉丝