I2C控制器的Verilog建模之二

前言:接着上一篇的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
原文地址:https://www.cnblogs.com/loadomain/p/3250986.html