[Verilog HDL] IIC的Verilog实现

[复制链接]
 楼主| gaochy1126 发表于 2023-1-31 21:46 | 显示全部楼层 |阅读模式
实现的代码详情:
端口声明与内部信号
  1. module IIC#(parameter id0 = 1'b0,id1 = 1'b0,id2 = 1'b1)(
  2. input CLK_50M,
  3. input rst_n,
  4. input [7:0] data,
  5. input [7:0] command,
  6. input req,
  7. inout iic_SLC,
  8. inout iic_SDA,
  9. output reg [7:0]data_out,
  10. output reg finish,
  11. output reg io_en   
  12. );

  13. //iic控制
  14. reg [8:0] SLC_cnt;
  15. reg cnt_flag;
  16. reg [4:0] n_cnt;
  17. reg n_cnt_flag;
  18. reg iic_SDA_reg;
  19. reg iic_SLC_reg;
  20. //外部输入缓存
  21. reg [7:0] data_reg;
  22. reg [7:0] command_reg;
  23. reg req_reg;
  24. //输出数据缓存
  25. reg [7:0] data_out_reg;
  26. //内部需要被访问的寄存器
  27. reg [7:0] inner_data;
  28. //状态信号
  29. reg busy;
  30. wire send;
  31. wire start;
  32. wire stop;
  33. //id寻找
  34. wire match_flag;
  35. //ack错误
  36. reg error;
  37. //状态机
  38. reg [6:0]cur_state;
  39. reg [6:0]next_state;
  40. localparam   s_idle   =  7'b000_0001;
  41. localparam   s_send   =  7'b000_0010;
  42. localparam   s_mwrite =  7'b000_0100;
  43. localparam   s_mread  =  7'b000_1000;
  44. localparam   s_busy   =  7'b001_0000;
  45. localparam   s_swrite =  7'b010_0000;
  46. localparam   s_sread  =  7'b100_0000;

  47. //id序列检测
  48. reg [7:0]id_state;
  49. localparam   id_idle      = 8'b0000_0001;
  50. localparam   id_1         = 8'b0000_0010;
  51. localparam   id_10        = 8'b0000_0100;
  52. localparam   id_101       = 8'b0000_1000;
  53. localparam   id_1010      = 8'b0001_0000;
  54. localparam   id_1010_0    = 8'b0010_0000;
  55. localparam   id_1010_00   = 8'b0100_0000;
  56. localparam   id_1010_001  = 8'b1000_0000;


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

  8. always@(posedge CLK_50M or negedge rst_n)
  9.   if(~rst_n)
  10.     SCL_cnt <= 'd0;
  11.   else if(SCL_cnt == 'd499)
  12.     SCL_cnt <= 'd0;
  13.   else if(cnt_flag)
  14.     SCL_cnt <= SCL_cnt + 1'b1;
  15.   else
  16.     SCL_cnt <= 'd0;
  17.        
  18. always@(posedge CLK_50M or negedge rst_n)       
  19.   if(~rst_n)
  20.     iic_SCL_reg <= 1'b1;
  21.   else if(SCL_cnt == 'd249 || SCL_cnt == 'd499)
  22.     iic_SCL_reg <= ~iic_SCL_reg;
  23.          
  24. 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。
状态信号及状态机
  1. assign start = ((cur_state == s_send && n_cnt == 'd0) ||
  2. ((cur_state == s_idle || cur_state == s_busy) && n_cnt == 'd0))? (iic_SCL && ~iic_SDA) : 1'b0;
  3. assign stop  = (n_cnt == 'd19 && SCL_cnt > 'd125)? (iic_SCL && iic_SDA) : 1'b0;

  4. always@(posedge CLK_50M or negedge rst_n)
  5.   if(~rst_n)begin
  6.     command_reg <= 'd0;
  7.          data_reg <= 'd0;
  8.          req_reg <= 1'b0;end
  9.   else if(req)begin
  10.     command_reg <= command;
  11.     data_reg <= data;
  12.          req_reg <= req;end
  13.   else if(next_state == s_send && SCL_cnt == 'd249)
  14.     req_reg <= 1'b0;
  15.   else if(error)         
  16.     req_reg <= 1'b1;
  17.        
  18. assign send = req_reg && ~finish && ~busy;

  19. always@(posedge CLK_50M or negedge rst_n)
  20.   if(~rst_n)
  21.     busy <= 1'b0;
  22.   else if(stop)
  23.     busy <= 1'b0;  
  24.   else if(~req_reg && start)
  25.     busy <= 1'b1;  

  26. always@(posedge CLK_50M or negedge rst_n)
  27.   if(~rst_n)
  28.     next_state <= s_idle;
  29.   else
  30.     next_state <= cur_state;

  31. always@(*)
  32.   case(next_state)
  33.     s_idle : begin
  34.       if(busy)
  35.         cur_state = s_busy;
  36.       else if(send)
  37.              cur_state = s_send;
  38.            else
  39.              cur_state = s_idle;end
  40.          
  41.          s_send : begin
  42.            if(n_cnt == 'd8 && iic_SDA == 1'b0 && SCL_cnt == 'd499)
  43.              cur_state = s_mwrite;
  44.                 else if(n_cnt == 'd8 && iic_SDA == 1'b1 && SCL_cnt == 'd499)
  45.                   cur_state = s_mread;
  46.                 else
  47.                   cur_state = s_send;end
  48.    
  49.     s_busy : begin   
  50.             if(match_flag && iic_SDA == 1'b0 && SCL_cnt == 'd499)//这时候作为从机,主机的写是从机的读
  51.         cur_state = s_sread;
  52.       else if(match_flag && iic_SDA == 1'b1 && SCL_cnt == 'd499)//这时候作为从机,主机的读是从机的写               
  53.         cur_state = s_swrite;
  54.                 else if(stop)
  55.                   cur_state = s_idle;
  56.       else
  57.         cur_state = s_busy;end

  58.     s_mwrite : begin
  59.            if(error)
  60.                   cur_state = s_idle;
  61.                 else if(stop)
  62.              cur_state = s_idle;
  63.                 else
  64.                   cur_state = s_mwrite;end

  65.     s_mread : begin
  66.            if(error)
  67.                   cur_state = s_idle;      
  68.                 else if(stop)
  69.              cur_state = s_idle;
  70.                 else
  71.                   cur_state = s_mread; end

  72.     s_swrite : begin
  73.            if(error)
  74.                   cur_state = s_idle;      
  75.                 else if(stop)
  76.              cur_state = s_idle;
  77.                 else
  78.                   cur_state = s_swrite;end

  79.     s_sread : begin
  80.       if(stop)
  81.              cur_state = s_idle;
  82.                 else
  83.                   cur_state = s_sread;end                           
  84.     default : cur_state = s_idle;                  
  85. endcase


id序列检测
  1. always@(posedge iic_SCL or negedge rst_n)
  2.   if(~rst_n)
  3.     id_state <= id_idle;
  4.   else if(busy && n_cnt <= 'd8) begin
  5.     case(id_state)
  6.          id_idle:begin  
  7.            if(iic_SDA == 1'b1)
  8.              id_state <= id_1;
  9.                 else
  10.                   id_state <= id_idle; end         
  11.     id_1:begin         
  12.       if(iic_SDA == 1'b0)
  13.              id_state <= id_10;
  14.                 else
  15.                   id_state <= id_idle; end         
  16.     id_10:begin         
  17.       if(iic_SDA == 1'b1)
  18.              id_state <= id_101;
  19.                 else
  20.                   id_state <= id_1; end             
  21.     id_101:begin         
  22.       if(iic_SDA == 1'b0)
  23.              id_state <= id_1010;
  24.                 else
  25.                   id_state <= id_1; end
  26.     id_1010:begin         
  27.       if(iic_SDA == 1'b0)
  28.              id_state <= id_1010_0;
  29.                 else
  30.                   id_state <= id_101; end
  31.     id_1010_0:begin         
  32.       if(iic_SDA == 1'b0)
  33.              id_state <= id_1010_00;
  34.                 else
  35.                   id_state <= id_1; end
  36.     id_1010_00:begin         
  37.       if(iic_SDA == 1'b1)
  38.              id_state <= id_1010_001;
  39.                 else
  40.                   id_state <= id_idle; end
  41.     id_1010_001:         
  42.                   id_state <= id_idle;           
  43.          default:
  44.              id_state <= id_idle;
  45.          endcase
  46.          end         
  47.     else                  
  48.       id_state <= id_idle;

  49. assign match_flag = (id_state == id_1010_001);


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

  8. always@(posedge CLK_50M or negedge rst_n)
  9.   if(~rst_n)
  10.     n_cnt_flag <= 1'b0;
  11.   else if(cur_state == s_idle)
  12.     n_cnt_flag <= 1'b0;
  13.   else if(stop)       
  14.     n_cnt_flag <= 1'b0;
  15.   else if(start)
  16.     n_cnt_flag <= 1'b1;

  17. always@(*)
  18. begin
  19.   if(next_state == s_idle) begin
  20.       iic_SDA_reg = 1'bz;
  21.       io_en = 1'b0;        end
  22.   else if(next_state == s_send) begin
  23.   case(n_cnt)
  24.     0:begin iic_SDA_reg = 1'b0;io_en = 1'b1;end
  25.     1:begin iic_SDA_reg = command_reg[7];io_en = 1'b1;end
  26.     2:begin iic_SDA_reg = command_reg[6];io_en = 1'b1;end
  27.     3:begin iic_SDA_reg = command_reg[5];io_en = 1'b1;end
  28.     4:begin iic_SDA_reg = command_reg[4];io_en = 1'b1;end
  29.     5:begin iic_SDA_reg = command_reg[3];io_en = 1'b1;end
  30.     6:begin iic_SDA_reg = command_reg[2];io_en = 1'b1;end
  31.     7:begin iic_SDA_reg = command_reg[1];io_en = 1'b1;end
  32.     8:begin iic_SDA_reg = command_reg[0];io_en = 1'b1;end
  33.   default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end
  34.   endcase
  35.   end
  36.   
  37.   else if(next_state == s_mwrite) begin
  38.   case(n_cnt)  
  39.     9: begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
  40.     10:begin iic_SDA_reg = data_reg[7];io_en = 1'b1;end
  41.     11:begin iic_SDA_reg = data_reg[6];io_en = 1'b1;end
  42.     12:begin iic_SDA_reg = data_reg[5];io_en = 1'b1;end       
  43.     13:begin iic_SDA_reg = data_reg[4];io_en = 1'b1;end
  44.     14:begin iic_SDA_reg = data_reg[3];io_en = 1'b1;end
  45.     15:begin iic_SDA_reg = data_reg[2];io_en = 1'b1;end
  46.     16:begin iic_SDA_reg = data_reg[1];io_en = 1'b1;end
  47.     17:begin iic_SDA_reg = data_reg[0];io_en = 1'b1;end
  48.     18:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
  49.     19:begin
  50.       io_en = 1'b1;
  51.       if(SCL_cnt > 'd125)
  52.         iic_SDA_reg = 1'b1;
  53.       else if(SCL_cnt <= 'd124)
  54.         iic_SDA_reg = 1'b0; end
  55.     default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end
  56.   endcase
  57.   end
  58.   
  59.   else if(next_state == s_mread) begin
  60.   case(n_cnt)  
  61.     9,10,11,12,13,14,15,16,17:begin
  62.       iic_SDA_reg = 1'bz;io_en = 1'b0;end
  63.     18:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack       
  64.     19:begin
  65.       io_en = 1'b1;
  66.       if(SCL_cnt > 'd125)begin
  67.         iic_SDA_reg = 1'b1;end
  68.       else if(SCL_cnt <= 'd124)
  69.         iic_SDA_reg = 1'b0; end         
  70.     default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end           
  71.   endcase
  72.   end

  73.   else if(next_state == s_sread) begin
  74.   case(n_cnt)
  75.     9:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack
  76.     10,11,12,13,14,15,16,17:begin
  77.       iic_SDA_reg = 1'bz;io_en = 1'b0;end       
  78.     18:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
  79.     19:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//等待主机发送stop
  80.     default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end  
  81.   endcase
  82.   end

  83.   else if(next_state == s_swrite) begin
  84.   case(n_cnt)
  85.     9 :begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack
  86.     10:begin iic_SDA_reg = inner_data[7];io_en = 1'b1;end
  87.     11:begin iic_SDA_reg = inner_data[6];io_en = 1'b1;end
  88.     12:begin iic_SDA_reg = inner_data[5];io_en = 1'b1;end
  89.     13:begin iic_SDA_reg = inner_data[4];io_en = 1'b1;end
  90.     14:begin iic_SDA_reg = inner_data[3];io_en = 1'b1;end
  91.     15:begin iic_SDA_reg = inner_data[2];io_en = 1'b1;end
  92.     16:begin iic_SDA_reg = inner_data[1];io_en = 1'b1;end
  93.     17:begin iic_SDA_reg = inner_data[0];io_en = 1'b1;end
  94.     18:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
  95.     19:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//等待主机发送stop
  96.     default:begin iic_SDA_reg =  1'b1;io_en = 1'b1;end          
  97.   endcase
  98.   end       
  99.        
  100.   else begin
  101.     iic_SDA_reg = 1'bz;io_en = 1'b0;end
  102. end  

  103. assign iic_SDA = io_en ? iic_SDA_reg : 1'bz;


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

  6. always@(posedge CLK_50M or negedge rst_n)
  7.   if(~rst_n)
  8.     finish <= 1'b0;
  9.   else if(next_state == s_mread && n_cnt == 'd19)         
  10.          finish <= 1'b1;
  11.   else if(next_state == s_mwrite && n_cnt == 'd19)         
  12.          finish <= 1'b1;
  13.   else
  14.     finish <= 1'b0;  

  15. always@(posedge CLK_50M or negedge rst_n)
  16.   if(~rst_n)
  17.     error <= 1'b0;
  18.   else if(next_state == s_mwrite)begin       
  19.     if(n_cnt == 'd9 ||n_cnt == 'd18) begin
  20.       if(iic_SDA == 1'b0)
  21.         error <= 1'b1;
  22.       else
  23.         error <= 1'b0; end
  24.          else
  25.       error <= 1'b0;        end
  26.   else if(next_state == s_mread) begin
  27.     if(n_cnt == 'd9) begin
  28.       if(iic_SDA == 1'b0)
  29.         error <= 1'b1;
  30.       else
  31.         error <= 1'b0; end
  32.          else
  33.       error <= 1'b0;         end         
  34.   else if(next_state == s_swrite)begin
  35.     if(n_cnt == 'd18) begin
  36.       if(iic_SDA == 1'b0)
  37.         error <= 1'b1;
  38.       else
  39.         error <= 1'b0; end
  40.          else
  41.       error <= 1'b0;         end
  42.   else
  43.     error <= 1'b0;                 

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

  49. always@(posedge CLK_50M or negedge rst_n)         
  50.   if(~rst_n)
  51.     data_out <= 'd0;
  52.   else if(next_state == s_mread && n_cnt == 'd19)       
  53.     data_out <= data_out_reg;
  54.   else
  55.     data_out <= 'd0;

  56. endmodule


top顶层
  1. module IIC_top(
  2. input ack,
  3. input CLK_50M,
  4. input rst_n,
  5. input [7:0] data,
  6. input [7:0] command,
  7. input req,
  8. inout iic_SCL,
  9. output  finish,
  10. output [7:0] data_out   
  11. );

  12. wire iic_SDA_top;
  13. wire io_en;

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


  15. IIC#(.id0(1'b0),
  16.     .id1 (1'b0),
  17.     .id2 (1'b1)
  18.          )master1(
  19. .CLK_50M (CLK_50M) ,
  20. .rst_n   (rst_n)   ,
  21. .data    (data)    ,
  22. .command (command) ,
  23. .req     (req  )   ,
  24. .iic_SCL (iic_SCL) ,
  25. .iic_SDA (iic_SDA_top) ,
  26. .finish  (finish ) ,
  27. .io_en   (io_en  ) ,
  28. .data_out (data_out)
  29. );

  30. endmodule





您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

1148

主题

11651

帖子

26

粉丝
快速回复 在线客服 返回列表 返回顶部
个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1148

主题

11651

帖子

26

粉丝
快速回复 在线客服 返回列表 返回顶部