前言:之前申请了ADI公司的一款ADV7181CBSTZ的视频解码芯片,正好原装DE2板子安的是同系列的ADV7181BBSTZ。虽然都是ADV7181的宗出,但是寄存器配置等等还是有些诧异,引脚也不兼容。而且ADI已经停产了ADV7181B,现在主推C系列的。关于7181不同系列配置和操作问题可以在http://ez.analog.com社区,ADI公司的工程师都会直接帮你回答,我搜索了一部分Q&A,里面的问题基本都能得到不错解决。ps.其实从工程师解答的方式,对比一下自己遇到问题首先该怎么思考解决方法,这一点是可以学到很多知识。
设计思路:废话多说了,I2C总线协议主要用来给模拟和数字芯片配置内能寄存器的主要方式,因为用ADI的芯片经历比较多,他们的芯片主要是I2C和SPI配置。I2C总线标准细节请参照《i2c总线协议(中文版pdf)》。以下设计的I2C模型肯定没有协议里的功能那么完备,这里主要是考虑到I2C仅仅用做配置寄存器这一点点的通信量,不像早前E2PROM还能涉及到I2C块页读写操作,状态机可以简单到用一个计数器就可以控制完成,没有那么多条件转移。
所以类似这种应用场合下,I2C、SPI协议可以简单到和UART一样。那么就涉及到状态的颗粒程度了,参考了一篇opencores上发布LGPL开源的I2C控制器,其功能复杂程度不是一般人能写出来的,该控制器将手册读写SDA和SCLK的上升时间、保持时间参数化,细化了一个比特的电平变化。网络上还有一种思路是颗粒化成一比特四个状态(上升沿、高电平、下降沿、低电平)。我选择了后者,设计模块功能够用就行。具体写操作代码比较简单最后附出。
难点和技巧:
(a)SDA三态口设计。由于I2C总线里SDA数据线是双向线,多个I2C设备的SDA是想“与”的逻辑,因此在不是拥有控制权的时候应该释放且高阻输出。以前用51单片机配置SA7113时用C写的I2C里面是没有办法输出高阻的,因此在等待从机ACK时候,主机在这个时刻是置SDA为1的。提这一点避免让一些人误会设置高祖就是置1。多建议inout的使用尽量用在最顶层模块,避免综合出错,或者在STP2使用时候出错。我见过Terasic的sdram控制器就是这么做的。
(b)读操作。注意读操作需要两个START信号。否则读寄存器收不到ACK信号,然后误以为写操作有问题。
(c)关于ACK和NACK。这里站在主机角度,每次读写都应该等待ACK,这一点肯定是没错的。其实NACK是主机在结束最后一个比特之后结束信号之间由主机产生NACK。关于这一点的解释,这里贴出ADI工程师的回答:”In Read Mode, the highest subaddress register contents continue to be output until the master device issues a no-acknowledge. This indicates the end of a read. A no-acknowledge condition is where the SDA line is not pulled low on the ninth pulse.I think this is what you are referring to. A master NACK during the read back byte stops the read back. In absolute I2C standards probably not but for this part it's ok since you can only access one register at a time and the subaddress register does not auto-increment so doing another byte read will just return the same byte you just read. A stop essentially kills the transaction anyways. This is an older device.“
(d)个人习惯。在每个操作结束后产生一个脉冲宽度的模块ack信号(非I2C的ACK),这样做可以将该信号反馈激励该模块,提高模块使用效率。对于读写请求的处理,个人喜欢采信号的边沿变化,因为对于一些前个模块给的是脉冲信号就不一定可以保持到完整激励状态机转移时候的电平宽度。另外:配合这模块ack和读写请求就能完美提高模块效率。
写操作源码1:
1 `timescale 1 ns / 1 ps 2 `define SIM 3 `define SYS_CLK 50000000 4 `define I2C_CLK 100000 5 `define I2C_DIV `SYS_CLK/`I2C_CLK 6 `define ADV7180 7 `define SCLK_CNT_WIDTH 9 8 module i2c_controller( 9 sys_clk, 10 sys_rst_n, 11 sys_wreq_i, 12 reg_addr_i, 13 sys_data_i, 14 i2c_idle_o, 15 i2c_ack_o, 16 i2c_sclk, 17 i2c_sdat 18 ); 19 input sys_clk; 20 input sys_rst_n; 21 input sys_wreq_i; 22 input [7:0] reg_addr_i; //从机寄存器地址 23 input [7:0] sys_data_i; //待写的数据 24 output i2c_idle_o; //模块空闲 25 output i2c_ack_o; //非I2C的ACK,模块的ack 26 output i2c_sclk; 27 inout i2c_sdat; 28 `ifdef ADV7180 29 parameter DEVICE_READ = 8'h40; //器件读操作地址 30 parameter DEVICE_WRITE = 8'h41; //器件写操作地址 31 `endif 32 33 `ifdef SIM 34 parameter ST_WIDTH = 56; 35 parameter IDLE = "IDLE...", 36 START1 = "START1.", 37 SET_SLAVE = "SET_SLA", 38 ACK1 = "ACK1...", 39 SET_REG = "SET_REG", 40 ACK2 = "ACK2...", 41 WR_DATA = "WR_DATA", 42 ACK3 = "ACK3...", 43 STOP = "STOP..."; 44 45 `else 46 `define FSM 9 47 parameter ST_WIDTH = 9; 48 parameter IDLE = `FSM'b0_0000_0001, 49 START1 = `FSM'b0_0000_0010, //写操作一共有1个start,读操作一共2个start 50 SET_SLAVE = `FSM'b0_0000_0100, 51 ACK1 = `FSM'b0_0000_1000, 52 SET_REG = `FSM'b0_0001_0000, 53 ACK2 = `FSM'b0_0010_0000, 54 WR_DATA = `FSM'b0_0100_0000, 55 ACK3 = `FSM'b0_1000_0000, 56 STOP = `FSM'b1_0000_0000; 57 `endif 58 59 //GENERATE SCLK_R 60 reg [`SCLK_CNT_WIDTH-1:0] sclk_cnt = 0; 61 always @ (posedge sys_clk) begin 62 if(1'b0 == sys_rst_n) sclk_cnt <= 0; 63 else if(sclk_cnt < `I2C_DIV-1) sclk_cnt <= sclk_cnt + 1'd1; 64 else sclk_cnt <= 0; 65 end 66 67 `define SCLK_POS (sclk_cnt == `SCLK_CNT_WIDTH'd499) 68 `define SCLK_HIGH (sclk_cnt == `SCLK_CNT_WIDTH'd124) 69 `define SCLK_NEG (sclk_cnt == `SCLK_CNT_WIDTH'd249) 70 `define SCLK_LOW (sclk_cnt == `SCLK_CNT_WIDTH'd374) 71 72 assign i2c_sclk = (sclk_cnt <= `SCLK_CNT_WIDTH'd249)?1'b1:1'b0; 73 74 //caputre the posedge of sys_wreq_i; 75 reg sys_wreq_r0 = 0; 76 always @ (posedge sys_clk) begin 77 if(sys_rst_n == 1'b0) sys_wreq_r0 <= 0; 78 else sys_wreq_r0 <= sys_wreq_i; 79 end 80 wire do_wreq = sys_wreq_i & ~sys_wreq_r0; 81 //generate the wr_start; 82 reg wr_start = 0; 83 always @ (posedge sys_clk) begin 84 if(sys_rst_n == 1'b0) wr_start <= 0; 85 else if(i2c_ack_o == 1'b1) wr_start <= 0; 86 else if(do_wreq) wr_start <= 1; 87 else wr_start <= wr_start; 88 end 89 //FSM 90 reg [7:0] data2slave = 0; 91 reg sdat_r = 1; 92 reg link = 0; //控制三态口读写方向,默认为读方向0,写时为1 93 reg [3:0] bit_cnt = 4'd0; 94 reg [ST_WIDTH-1:0] c_st = IDLE; 95 reg [ST_WIDTH-1:0] n_st = IDLE; 96 //FSM-1 97 always @ (posedge sys_clk) begin 98 if(1'b0 == sys_rst_n) c_st <= IDLE; 99 else c_st <= n_st; 100 end 101 //fsm-2 102 //实际的状态转移中ack[2:0]比物理等待的ack少四分之一 103 always @ (*) begin 104 n_st = IDLE; 105 case(c_st) 106 IDLE:begin 107 n_st = ((wr_start == 1'b1)&&(`SCLK_HIGH))?START1:IDLE;end 108 START1:begin 109 n_st = (`SCLK_LOW)?SET_SLAVE:START1;end //sclk为高电平中心时转移 110 SET_SLAVE:begin 111 n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK1:SET_SLAVE;end//数据在低电平是更新 112 ACK1:begin 113 n_st = (`SCLK_NEG)?SET_REG:ACK1;end//为保证下一步设置寄存器,提前1/4进入下一个状态 114 SET_REG:begin 115 n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK2:SET_REG;end//数据在低电平是更新 116 ACK2:begin 117 n_st = (`SCLK_NEG)?WR_DATA:ACK2;end//为保证下一步设置寄存器,提前1/4进入下一个状态 118 WR_DATA:begin 119 n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK3:WR_DATA;end 120 ACK3:begin 121 n_st = (`SCLK_NEG)?STOP:ACK3;end 122 STOP:begin 123 n_st = (`SCLK_NEG)?IDLE:STOP;end 124 default:begin 125 n_st = IDLE;end 126 endcase 127 end 128 //FSM-3 129 always @ (posedge sys_clk) begin 130 if(sys_rst_n == 1'b0) begin 131 link <= 1'd1; 132 data2slave <= 8'd0; 133 bit_cnt <= 4'd0; 134 sdat_r <= 1'd1; 135 end 136 else begin 137 case(c_st) 138 IDLE:begin 139 link <= 1'd1; 140 data2slave <= DEVICE_WRITE; 141 bit_cnt <= 4'd0; 142 sdat_r <= 1'd1; 143 end 144 START1:begin 145 link <= 1'd1; 146 bit_cnt <= 4'd1; 147 data2slave <= (`SCLK_LOW)?data2slave<<1:data2slave; 148 sdat_r <= (`SCLK_LOW)?data2slave[7]:1'd0; //pull down,由于data2slave缓存一级的缘故,需要提前在START里输出第MSB位 149 end 150 SET_SLAVE:begin 151 if(`SCLK_LOW) begin 152 link <= (bit_cnt == 4'd8)?1'b0:1'b1; //释放数据总线 153 bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 154 data2slave <= {data2slave[6:0],1'd0};//左移一位 155 sdat_r <= (bit_cnt == 4'd8)?1'd1:data2slave[7];end 156 else begin 157 link <= link; 158 bit_cnt <= bit_cnt; 159 data2slave <= data2slave; 160 sdat_r <= sdat_r;end 161 end 162 ACK1:begin 163 link <= 1'd0; 164 data2slave <= (`SCLK_POS)?reg_addr_i:data2slave; //读入待写的寄存器地址 165 bit_cnt <= 4'd0; 166 sdat_r <= 1'd1; 167 end 168 SET_REG:begin 169 if(`SCLK_LOW) begin 170 link <= (bit_cnt == 4'd8)?1'b0:1'b1; //释放数据总线 171 bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 172 data2slave <= {data2slave[6:0],1'd0};//左移一位 173 sdat_r <= (bit_cnt == 4'd8)?1'd1:data2slave[7];end 174 else begin 175 link <= link; 176 bit_cnt <= bit_cnt; 177 data2slave <= data2slave; 178 sdat_r <= sdat_r;end 179 end 180 ACK2:begin 181 link <= 1'd0; 182 data2slave <= (`SCLK_POS)?sys_data_i:data2slave; //读入待写的寄存器地址 183 bit_cnt <= 4'd0; 184 sdat_r <= 1'd1; 185 end 186 WR_DATA:begin 187 if(`SCLK_LOW) begin 188 link <= (bit_cnt == 4'd8)?1'b0:1'b1; //释放数据总线 189 bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 190 data2slave <= {data2slave[6:0],1'd0};//左移一位 191 sdat_r <= data2slave[7];end 192 else begin 193 link <= link; 194 bit_cnt <= bit_cnt; 195 data2slave <= data2slave; 196 sdat_r <= sdat_r;end 197 end 198 ACK3:begin 199 link <= 1'd0; 200 sdat_r <= 1'd0;//预先拉低 201 bit_cnt <= bit_cnt; 202 data2slave <= data2slave;end 203 STOP:begin 204 link <= (`SCLK_LOW)?1'b1:link; 205 bit_cnt <= bit_cnt; 206 data2slave <= data2slave; 207 sdat_r <= (`SCLK_HIGH)?1'b1:sdat_r;end 208 default:begin 209 link <= 1'd1; 210 data2slave <= 8'd0; 211 bit_cnt <= 4'd0; 212 sdat_r <= 1'd1; 213 end 214 endcase 215 end 216 end 217 //assign 218 assign i2c_sdat = (link == 1'b1)?sdat_r:8'hzz; 219 assign i2c_idle_o = (c_st == IDLE)?1'b1:1'b0; 220 assign i2c_ack_o = ((c_st == STOP)&&(`SCLK_NEG))?1'b1:1'b0; 221 222 223 224 endmodule
写操作源码2:
`timescale 1 ns / 1 ps `define LUT_WIDTH 4 module adv7180_config( sys_clk, sys_rst_n, i2c_ack_i, sys_wreq_o, sys_data_o, reg_addr_o, config_done_o ); input sys_clk; input sys_rst_n; input i2c_ack_i; output sys_wreq_o; output [7:0] sys_data_o; output [7:0] reg_addr_o; output config_done_o; //generate wreq_o reg sys_wreq_o = 0; reg [`LUT_WIDTH-1:0] lut_index = 0; reg [15:0] lut_data = 0; always @ (posedge sys_clk) begin if(1'b0 == sys_rst_n) begin sys_wreq_o <= 1; lut_index <= 0;end else if((i2c_ack_i == 1'b1)&&(config_done_o == 1'b0)) begin sys_wreq_o <= 1; lut_index <= lut_index + 1'd1;end else begin sys_wreq_o <= 0; lut_index <= lut_index;end end //assign assign config_done_o = (lut_index == `LUT_WIDTH'd15)?1'b1:1'b0; assign sys_data_o = lut_data[7:0]; assign reg_addr_o = lut_data[15:8]; //lut always @ (*) begin case(lut_index) `LUT_WIDTH'd0:lut_data <= 16'h2330; `LUT_WIDTH'd1:lut_data <= 16'h4161; `LUT_WIDTH'd2:lut_data <= 16'hf22a; `LUT_WIDTH'd3:lut_data <= 16'ha344; `LUT_WIDTH'd4:lut_data <= 16'h4353; `LUT_WIDTH'd5:lut_data <= 16'h1325; `LUT_WIDTH'd6:lut_data <= 16'h6546; `LUT_WIDTH'd7:lut_data <= 16'h7657; `LUT_WIDTH'd8:lut_data <= 16'h8565; `LUT_WIDTH'd9:lut_data <= 16'h9357; `LUT_WIDTH'd10:lut_data <= 16'h1450; `LUT_WIDTH'd11:lut_data <= 16'h1311; `LUT_WIDTH'd12:lut_data <= 16'h1542; `LUT_WIDTH'd13:lut_data <= 16'h1133; `LUT_WIDTH'd14:lut_data <= 16'h1134; `LUT_WIDTH'd15:lut_data <= 16'h1965; endcase end endmodule
仿真写操作源码3:
1 `timescale 1 ns / 1 ps 2 module tb_i2c(); 3 reg sys_clk; 4 reg sys_rst_n; 5 initial begin 6 sys_clk = 1; 7 sys_rst_n = 0; 8 #100 sys_rst_n = 1; 9 end 10 11 always begin 12 #10 sys_clk=~sys_clk;end 13 14 wire i2c_sclk; 15 wire sys_wreq; 16 wire [7:0] reg_addr; 17 wire [7:0] sys_data; 18 wire i2c_idle; 19 wire i2c_ack; 20 wire i2c_sdat; 21 i2c_controller u0( 22 .sys_clk( sys_clk ), 23 .sys_rst_n( sys_rst_n ), 24 .sys_wreq_i( sys_wreq ), 25 .reg_addr_i( reg_addr ), 26 .sys_data_i( sys_data ), 27 .i2c_idle_o( i2c_idle ), 28 .i2c_ack_o( i2c_ack ), 29 .i2c_sclk( i2c_sclk), 30 .i2c_sdat( i2c_sdat ) 31 ); 32 33 wire config_done; 34 adv7180_config u1( 35 .sys_clk( sys_clk ), 36 .sys_rst_n( sys_rst_n ), 37 .i2c_ack_i( i2c_ack ), 38 .sys_wreq_o( sys_wreq ), 39 .sys_data_o( sys_data ), 40 .reg_addr_o( reg_addr ), 41 .config_done_o( config_done ) 42 ); 43 44 45 46 endmodule