前言:接着上一篇的I2C写操作,今天要实现一个I2C的读操作。虽然在ADV7181B配置内部寄存器时没有必要使用到读操作,但是为了进一步确认寄存器是否在I2C写模块下被正确配置,这一步是必不可少的。
设计思路:由于最终的应用里I2C读模块在调试结束后还是要被剔除,因此决定还是另外建一个读的状态机独立于之前的写状态机。读状态机的思路基本和写状态机的思路一样,需要注意的是一次写操作需要两次的START信号和最后一字节传输结束后的NON-ACKNOWLEDGE。
改进和注意点:相比之前的写模块,读模块完善了以下这些
(a)时钟信号在一系列写操作完毕之后拉高,不再跳变;
(b)添加了使能信号,便于安排模块在写模块完成一些了写操作后才开始被激励工作;
(c)三态口:修改了SDAT_R,使之在LINK为0,即三态口读方向时候SDAR_T保持1。以及LINK信号在空闲状态下释放总线;
未解决的问题:因为读写两个独立的模块各自用到一个三态口,即各自有一对I2C_SCLK和I2C_SDAT,因此在顶层就驱动同一个芯片的引脚之前,还需要一个复用的逻辑开关分时切换这两个驱动源。因为之前没有这样用多个三态口驱动一个同一个三态口,综合是否可行将在下一篇I2C测试里给出答案。p.s.当然如果整合两个状态机到同一个模块下就不存在这样的问题。
源码:
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 //version:v1.0 9 //mend bug: WHEN IN IDLE SET SCLK HIGH; 10 module i2c_read_controller( 11 sys_clk, 12 sys_rst_n, 13 sys_rreq_i, 14 sys_rd_en, 15 rd_reg_addr_i, 16 sys_data_o, 17 i2c_rd_idle_o, 18 i2c_rd_ack_o, 19 i2c_sclk, 20 i2c_sdat 21 ); 22 23 input sys_clk; 24 input sys_rst_n; 25 input sys_rreq_i; 26 input sys_rd_en; //由其他信号激励使能 27 input [7:0] rd_reg_addr_i; //从机寄存器地址 28 output [7:0] sys_data_o; //待写的数据 29 output i2c_rd_idle_o; //模块空闲 30 output i2c_rd_ack_o; //非I2C的ACK,模块的ack 31 output i2c_sclk; 32 inout i2c_sdat; 33 `ifdef ADV7180 34 parameter DEVICE_READ = 8'h40; //器件读操作地址 35 parameter DEVICE_WRITE = 8'h41; //器件写操作地址 36 `endif 37 38 `ifdef SIM 39 parameter ST_WIDTH = 56; 40 parameter IDLE = "IDLE...", 41 START1 = "START1.", 42 WR_SLAVE = "WR_SLAV", 43 ACK1 = "ACK1...", 44 SET_REG = "SET_REG", 45 ACK2 = "ACK2...", 46 START2 = "START2", 47 RD_SLAVE = "RD_SLAV", 48 ACK3 = "ACK3...", 49 DATA = "DATA...", 50 NACK4 = "NACK4..", 51 STOP = "STOP..."; 52 53 `else 54 `define FSM 12 55 parameter ST_WIDTH = 12; 56 parameter IDLE = `FSM'b0000_0000_0001, 57 START1 = `FSM'b0000_0000_0010, //写操作一共有1个start,读操作一共2个start 58 WR_SLAVE = `FSM'b0000_0000_0100, 59 ACK1 = `FSM'b0000_0000_1000, 60 SET_REG = `FSM'b0000_0001_0000, 61 ACK2 = `FSM'b0000_0010_0000, 62 START2 = `FSM'b0000_0100_0000, 63 RD_SLAVE = `FSM'b0000_1000_0000, 64 ACK3 = `FSM'b0001_0000_0000, 65 DATA = `FSM'b0010_0000_0000, 66 NACK4 = `FSM'b0100_0000_0000, 67 STOP = `FSM'b1000_0000_0000; 68 `endif 69 //caputre the posedge of sys_rreq_i; 70 reg sys_rreq_r0 = 0; 71 always @ (posedge sys_clk) begin 72 if(sys_rst_n == 1'b0) sys_rreq_r0 <= 0; 73 else sys_rreq_r0 <= sys_rreq_i; 74 end 75 wire do_rreq = sys_rreq_i & ~sys_rreq_r0 & sys_rd_en; 76 //generate the rd_start; 77 reg rd_start = 0; 78 always @ (posedge sys_clk) begin 79 if(sys_rst_n == 1'b0) rd_start <= 0; 80 else if(i2c_rd_ack_o == 1'b1) rd_start <= 0; 81 else if(do_rreq) rd_start <= 1; 82 else rd_start <= rd_start; 83 end 84 //GENERATE SCLK_R 85 reg [`SCLK_CNT_WIDTH-1:0] sclk_cnt = 0; 86 always @ (posedge sys_clk) begin 87 if(1'b0 == sys_rst_n) sclk_cnt <= 0; 88 else if((sclk_cnt < `I2C_DIV-1)&&(sys_rd_en == 1'b1)&&(rd_start == 1'b1)) sclk_cnt <= sclk_cnt + 1'd1; 89 else sclk_cnt <= 0; 90 end 91 92 `define SCLK_POS (sclk_cnt == `SCLK_CNT_WIDTH'd499) 93 `define SCLK_HIGH (sclk_cnt == `SCLK_CNT_WIDTH'd124) 94 `define SCLK_NEG (sclk_cnt == `SCLK_CNT_WIDTH'd249) 95 `define SCLK_LOW (sclk_cnt == `SCLK_CNT_WIDTH'd374) 96 wire i2c_sclk_w; 97 assign i2c_sclk_w = ((sys_rd_en == 1'b1)&&(sclk_cnt <= `SCLK_CNT_WIDTH'd249))?1'b1:1'b0; 98 99 100 //FSM 101 reg [7:0] data2host = 0; 102 reg [7:0] sys_data_o = 0; 103 reg sdat_r = 1; 104 reg link = 0; //控制三态口读写方向,默认为读方向0,写时为1 105 reg [3:0] bit_cnt = 4'd0; 106 reg [ST_WIDTH-1:0] c_st = IDLE; 107 reg [ST_WIDTH-1:0] n_st = IDLE; 108 //FSM-1 109 always @ (posedge sys_clk) begin 110 if(1'b0 == sys_rst_n) c_st <= IDLE; 111 else c_st <= n_st; 112 end 113 //fsm-2 114 //实际的状态转移中ack[2:0]比物理等待的ack少四分之一 115 always @ (*) begin 116 n_st = IDLE; 117 case(c_st) 118 IDLE:begin 119 n_st = (rd_start == 1'b1)?START1:IDLE;end 120 START1:begin 121 n_st = (`SCLK_LOW)?WR_SLAVE:START1;end //sclk为高电平中心时转移 122 WR_SLAVE:begin 123 n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK1:WR_SLAVE;end//数据在低电平是更新 124 ACK1:begin 125 n_st = (`SCLK_NEG)?SET_REG:ACK1;end//为保证下一步设置寄存器,提前1/4进入下一个状态 126 SET_REG:begin 127 n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK2:SET_REG;end//数据在低电平是更新 128 ACK2:begin 129 n_st = (`SCLK_NEG)?START2:ACK2;end 130 START2:begin 131 n_st = (`SCLK_NEG)?RD_SLAVE:START2;end 132 RD_SLAVE:begin 133 n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?ACK3:RD_SLAVE;end 134 //为保证下一步设置寄存器,提前1/4进入下一个状态 135 ACK3:begin 136 n_st = (`SCLK_NEG)?DATA:ACK3;end 137 DATA:begin 138 n_st = ((`SCLK_LOW)&&(bit_cnt == 4'd8))?NACK4:DATA;end 139 NACK4:begin 140 n_st = (`SCLK_NEG)?STOP:NACK4;end 141 STOP:begin 142 n_st = (`SCLK_NEG)?IDLE:STOP;end 143 default:begin 144 n_st = IDLE;end 145 endcase 146 end 147 //FSM-3 148 always @ (posedge sys_clk) begin 149 if(sys_rst_n == 1'b0) begin 150 link <= 1'd0; //释放总线 151 data2host <= 8'd0; 152 bit_cnt <= 4'd0; 153 sdat_r <= 1'd1; 154 sys_data_o <= 0; 155 end 156 else begin 157 case(c_st) 158 IDLE:begin 159 link <= 1'd0; 160 data2host <= DEVICE_WRITE; 161 bit_cnt <= 4'd0; 162 sdat_r <= 1'd1; 163 sys_data_o <= sys_data_o; 164 end 165 START1:begin 166 link <= 1'd1; 167 sys_data_o <= sys_data_o; 168 bit_cnt <= 4'd1; 169 data2host <= (`SCLK_LOW)?data2host<<1:data2host; 170 if(`SCLK_HIGH) begin 171 sdat_r <= 1'b0;end 172 else if(`SCLK_LOW) begin 173 sdat_r <= data2host[7];end //pull down,由于data2host缓存一级的缘故,需要提前在START里输出第MSB位 174 else begin 175 sdat_r <= sdat_r;end 176 end 177 WR_SLAVE:begin 178 sys_data_o <= sys_data_o; 179 if(`SCLK_LOW) begin 180 link <= (bit_cnt == 4'd8)?1'b0:1'b1; //释放数据总线 181 bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 182 data2host <= {data2host[6:0],1'd0};//左移一位 183 sdat_r <= (bit_cnt == 4'd8)?1'd1:data2host[7];end 184 else begin 185 link <= link; 186 bit_cnt <= bit_cnt; 187 data2host <= data2host; 188 sdat_r <= sdat_r;end 189 end 190 ACK1:begin 191 link <= 1'd0; 192 sys_data_o <= sys_data_o; 193 data2host <= (`SCLK_POS)?rd_reg_addr_i:data2host; //读入待写的寄存器地址 194 bit_cnt <= 4'd0; 195 sdat_r <= 1'd1; 196 end 197 SET_REG:begin 198 sys_data_o <= sys_data_o; 199 if(`SCLK_LOW) begin 200 link <= (bit_cnt == 4'd8)?1'b0:1'b1; //释放数据总线 201 bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 202 data2host <= {data2host[6:0],1'd0};//左移一位 203 sdat_r <= (bit_cnt == 4'd8)?1'd1:data2host[7];end 204 else begin 205 link <= link; 206 bit_cnt <= bit_cnt; 207 data2host <= data2host; 208 sdat_r <= sdat_r;end 209 end 210 ACK2:begin 211 link <= 1'd0; 212 sys_data_o <= sys_data_o; 213 data2host <= (`SCLK_POS)?DEVICE_READ:data2host; //读入待写的寄存器地址 214 bit_cnt <= 4'd0; 215 sdat_r <= 1'd1; 216 end 217 START2:begin 218 link <= (`SCLK_LOW)?1'b1:link; 219 sys_data_o <= sys_data_o; 220 data2host <= data2host; 221 bit_cnt <= bit_cnt; 222 sdat_r <= (`SCLK_HIGH)?1'b0:sdat_r; 223 end 224 RD_SLAVE:begin 225 sys_data_o <= sys_data_o; 226 if(`SCLK_LOW) begin 227 link <= (bit_cnt == 4'd8)?1'b0:1'b1; 228 bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 229 data2host <= {data2host[6:0],1'd0};//左移一位 230 sdat_r <= (bit_cnt == 4'd8)?1'd1:data2host[7];end 231 else begin 232 link <= link; 233 bit_cnt <= bit_cnt; 234 data2host <= data2host; 235 sdat_r <= sdat_r;end 236 end 237 ACK3:begin 238 link <= 1'b0; 239 bit_cnt <= 4'd0; 240 sys_data_o <= sys_data_o; 241 data2host <= 0; 242 sdat_r <= 1'd1;end 243 DATA:begin 244 sys_data_o <= sys_data_o; 245 if(`SCLK_HIGH) begin 246 link <= (bit_cnt == 4'd8)?1'b1:1'b0; //为主设备产生NACK准备 247 bit_cnt <= (bit_cnt == 4'd8)?4'd0:bit_cnt+1'd1; 248 data2host[7:1] <= data2host[6:0];//左移一位 249 data2host[0] <= sdat_r;end 250 else begin 251 link <= link; 252 bit_cnt <= bit_cnt; 253 data2host <= data2host; 254 sdat_r <= sdat_r;end 255 end 256 NACK4:begin 257 link <= 1'd1; 258 sdat_r <= 1'd1;//预先拉低 259 bit_cnt <= bit_cnt; 260 sys_data_o <= data2host;end 261 STOP:begin 262 link <= 1'b1; 263 bit_cnt <= bit_cnt; 264 sys_data_o <= sys_data_o; 265 data2host <= data2host; 266 if(`SCLK_LOW) begin 267 sdat_r <= 1'b0;end 268 else if(`SCLK_HIGH) begin 269 sdat_r <= 1'b1;end 270 else begin 271 sdat_r <= sdat_r;end 272 end 273 default:begin 274 link <= 1'd0; 275 data2host <= 8'd0; 276 sys_data_o <= sys_data_o; 277 bit_cnt <= 4'd0; 278 sdat_r <= 1'd1; 279 end 280 endcase 281 end 282 end 283 //assign 284 assign i2c_sdat = (link == 1'b1)?sdat_r:8'hzz; 285 assign i2c_rd_idle_o = (c_st == IDLE)?1'b1:1'b0; 286 assign i2c_rd_ack_o = ((c_st == STOP)&&(`SCLK_NEG))?1'b1:1'b0; 287 assign i2c_sclk = (c_st != IDLE)?i2c_sclk_w:1'b1; 288 289 290 endmodule
控制读模块源码2:
1 `timescale 1 ns / 1 ps 2 `define LUT_WIDTH 4 3 module adv7181_read_back( 4 sys_clk, 5 sys_rst_n, 6 i2c_rd_ack_i, 7 rd_back_done_o, 8 sys_rreq_o, 9 rd_reg_addr_o 10 ); 11 input sys_clk; 12 input sys_rst_n; 13 input i2c_rd_ack_i; 14 output rd_back_done_o; 15 output sys_rreq_o; 16 output [7:0] rd_reg_addr_o; 17 18 //generate rreq_o 19 reg sys_rreq_o = 0; 20 reg [`LUT_WIDTH-1:0] lut_index = 0; 21 reg [7:0] lut_data = 0; 22 always @ (posedge sys_clk) begin 23 if(1'b0 == sys_rst_n) begin 24 sys_rreq_o <= 1; 25 lut_index <= 0;end 26 else if((i2c_rd_ack_i == 1'b1)&&(rd_back_done_o == 1'b0)) begin 27 sys_rreq_o <= 1; 28 lut_index <= lut_index + 1'd1;end 29 else begin 30 sys_rreq_o <= 0; 31 lut_index <= lut_index;end 32 end 33 //assign 34 assign rd_back_done_o = (lut_index == `LUT_WIDTH'd15)?1'b1:1'b0; 35 assign rd_reg_addr_o = lut_data; 36 //lut 37 always @ (*) begin 38 case(lut_index) 39 `LUT_WIDTH'd0:lut_data <= 8'h23; 40 `LUT_WIDTH'd1:lut_data <= 8'h41; 41 `LUT_WIDTH'd2:lut_data <= 8'hf2; 42 `LUT_WIDTH'd3:lut_data <= 8'ha3; 43 `LUT_WIDTH'd4:lut_data <= 8'h43; 44 `LUT_WIDTH'd5:lut_data <= 8'h13; 45 `LUT_WIDTH'd6:lut_data <= 8'h65; 46 `LUT_WIDTH'd7:lut_data <= 8'h76; 47 `LUT_WIDTH'd8:lut_data <= 8'h85; 48 `LUT_WIDTH'd9:lut_data <= 8'h93; 49 `LUT_WIDTH'd10:lut_data <= 8'h14; 50 `LUT_WIDTH'd11:lut_data <= 8'h13; 51 `LUT_WIDTH'd12:lut_data <= 8'h15; 52 `LUT_WIDTH'd13:lut_data <= 8'h11; 53 `LUT_WIDTH'd14:lut_data <= 8'h11; 54 `LUT_WIDTH'd15:lut_data <= 8'h19; 55 endcase 56 end 57 58 endmodule
仿真源码3:
1 `timescale 1 ns / 1 ps 2 module tb_read(); 3 reg sys_clk; 4 reg sys_rst_n; 5 6 initial begin 7 sys_clk=1; 8 sys_rst_n=0; 9 #100 sys_rst_n=1; 10 end 11 12 always begin 13 #10 sys_clk=~sys_clk;end 14 15 wire i2c_rd_ack; 16 wire sys_rreq; 17 wire [7:0] rd_reg_addr; 18 19 wire i2c_sclk; 20 wire i2c_sdat; 21 wire i2c_rd_idle; 22 wire [7:0] sys_data_out; 23 i2c_read_controller u0( 24 .sys_clk( sys_clk ), 25 .sys_rst_n( sys_rst_n ), 26 .sys_rreq_i( sys_rreq ), 27 .sys_rd_en( 1'b1 ), 28 .rd_reg_addr_i( rd_reg_addr ), 29 .sys_data_o( sys_data_out ), 30 .i2c_rd_idle_o( i2c_rd_idle ), 31 .i2c_rd_ack_o( i2c_rd_ack ), 32 .i2c_sclk( i2c_sclk ), 33 .i2c_sdat( i2c_sdat ) 34 ); 35 36 wire rd_back_done; 37 38 adv7181_read_back u1( 39 .sys_clk( sys_clk ), 40 .sys_rst_n( sys_rst_n ), 41 .i2c_rd_ack_i( i2c_rd_ack ), 42 .rd_back_done_o( rd_back_done ), 43 .sys_rreq_o( sys_rreq ), 44 .rd_reg_addr_o( rd_reg_addr ) 45 ); 46 endmodule