案例采用明德扬设计思想完成。IIC协议是非常常用的接口协议,在电子类岗位招聘要求中经常出现它的身影。关于IIC协议这里只做简要介绍,详细信息请自行百度或查阅相关Datasheet,网上资料非常多。该篇博文主要讲如何使用verilog来描述IIC协议,以读写EEPROM为例带领大家了解下明德扬四段式状态机规范和优势,另外还有一些自己在设计过程中总结的经验技巧。

  IIC协议时序格式以Datasheet中时序图的形式供大家参考。IIC协议有一条时钟线SCL和一条双线数据总线SDA。SDA在SCL高电平时保持稳定,否则视为开始或结束条件。

【代码更新】IIC协议建模——读写EEPROM

【代码更新】IIC协议建模——读写EEPROM

  发送端发送1byte数据后,接收端在下一个SCL高电平期间拉低总线表示应答,即接收数据成功。

【代码更新】IIC协议建模——读写EEPROM

  以下分别是器件地址为1字节的EEPROM的单字节写和读操作,需要注意的是DEVICE ADDRESS段中前四位固定是4'b1010,后三位根据EEPROM地址信号电平决定(本次实验地址信号引脚均接地,因此后三位为000),最后一位是读写标志位,低电平写。

【代码更新】IIC协议建模——读写EEPROM

【代码更新】IIC协议建模——读写EEPROM

  好了,有了以上五张时序图我们便知道要干什么了,就是实现这些时序嘛!对于这种串行时序,时间有先后且操作差异较大的要用状态机实现。每种类型操作定义在一个状态中,状态内部需要多个操作则配合计数器实现。整体设计思路如下:先构造时钟信号SCL,这里频率定义为200KHz,而系统时钟有频率为200MHz差分晶振提供,显然需要用到分频计数器。由于SCL高电平期间数据要保持稳定,所以我们在分频计数器计数到1/4处拉高SCL,3/4处拉低SCL,这样做的好处是在结束计数时正好处于SCL低电平中间点,此处作为数据变化的时刻再合适不过。

【代码更新】IIC协议建模——读写EEPROM

  有了时钟信号,下一步就是通过不同的状态实现SDA信号线满足上述时序要求。我们先来划分状态(实际上时序图中都给我们标识好了),很明显综合考虑写和读两种时序,状态应定义为:初始状态、开始、写控制、响应1、写地址、响应2、写数据、响应3、重新开始、读控制、响应4、读数据、不响应、停止。这里写控制和读控制即是DEVICE ADDRESS阶段,唯一的区别在于读写标志位不同。能看出以上状态划分包括写流程分支和读流程分支,可以根据指令用一个标志位加以区分。

  定义状态参数并采用独热码进行编码:

【代码更新】IIC协议建模——读写EEPROM

  IIC协议中每次SCL高电平期间视为一次操作,因此为了让每个状态都有整数个SCL周期(完整分频计数周期),对每个状态进行比特计数,写控制、地址、写数据、读控制、读数据阶段计数周期是8,其他为1。另外为保证代码的“健壮性”,也就是即使发送1byte数据后没有响应也不至于挂死在等待响应阶段,设定在每次等待响应阶段若响应才进入下一操作,否则回到初始状态。由此得到状态转移图(只包括主要流程,转移条件及未响应回到IDLE状态未画出):

【代码更新】IIC协议建模——读写EEPROM

  至此所有的设计工作都已经完成,接下来就是根据上述分析编写代码。在编写代码之前简要介绍明德扬四段式状态机的设计思想和代码规范:四段式状态机实质上是在三段式状态机基础上单独提出状态转移条件定义的结构。目的是让设计者一个时间段只专注于一件事情,也就是说当设计状态机的时候先把状态转移流程确定,而条件用不同的信号名代替,等状态转移流程确定后再定义转移条件。这样做的另一个好处是作为条件的信号名可以很方便的在后续时序逻辑中使用。其中用于代替条件的信号名要遵循类似如下格式:<state_c>2<state_n>。<>处用状态名代替。

整体代码如下:

  1 `timescale 1ns / 1ps
  2 
  3 module i2c_interface#(parameter SCL_CYC = 1000)//200KHz
  4 (
  5     input clk,
  6     input rst_n,
  7     
  8     //用户侧接口
  9     input write_en,//写指令
 10     input read_en, //读指令
 11     input [7:0]share_addr,    //读写复用地址
 12     input [7:0] wri_data,//代写入数据
 13     input wri_data_vld,
 14     
 15     output reg busy,//总线忙信号
 16     output reg [7:0] rd_data,//读回数据
 17     output reg rd_data_vld,
 18     
 19     //仿真用接口
 20     output reg [13:0] state_c,
 21     
 22     //eeprom侧接口
 23     output reg scl, //时钟
 24     input sda_in,
 25     output reg sda_en,
 26     output reg sda_reg
 27     
 28     );
 29     
 30     reg [11:0] div_cnt;
 31     reg high_middle,low_middle;
 32     reg [3:0] bit_cnt;
 33     reg [3:0] N;
 34     //(*keep = "true"*)reg [13:0] state_c;
 35     reg [13:0] state_n;
 36     reg [7:0] wri_byte;
 37     reg rd_flag;
 38     reg [7:0] rd_buf;
 39     reg [13:0] state_c_tmp;
 40     reg [7:0] device_addr_wr_shift;
 41     
 42     wire add_bit_cnt,end_bit_cnt;
 43     wire add_div_cnt,end_div_cnt;
 44     wire idle2start,start2wri_ctrl,wri_ctrl2ack1,ack12addr,addr2ack2,ack22wri_data;
 45     wire wri_data2ack3,ack32stop,ack22re_start,re_start2rd_ctrl,rd_ctrl2ack4;
 46     wire ack42rd_data,rd_data2nack,nack2stop,stop2idle,ack2idle;
 47     reg ack_valid,ack_invalid;
 48     wire [2:0] cs;
 49     wire wri_vld;
 50     wire [7:0] device_addr_rd,device_addr_wr;
 51     wire [7:0] word_addr;
 52     wire ack_state;
 53     
 54     //状态编码
 55     localparam IDLE     = 14'b00_0000_0000_0001,//1
 56                START    = 14'b00_0000_0000_0010,//2
 57                WRI_CTRL = 14'b00_0000_0000_0100,//4
 58                ACK1     = 14'b00_0000_0000_1000,//8
 59                ADDR     = 14'b00_0000_0001_0000,//10
 60                ACK2     = 14'b00_0000_0010_0000,//20
 61                WRI_DATA = 14'b00_0000_0100_0000,//40
 62                ACK3     = 14'b00_0000_1000_0000,//80
 63                RE_START = 14'b00_0001_0000_0000,//100
 64                RD_CTRL  = 14'b00_0010_0000_0000,//200
 65                ACK4     = 14'b00_0100_0000_0000,//400
 66                RD_DATA  = 14'b00_1000_0000_0000,//800
 67                NACK     = 14'b01_0000_0000_0000,//1000
 68                STOP     = 14'b10_0000_0000_0000;//2000
 69     
 70     //分频计数器 在响应操作直到完成或退出到IDLE中间都计数
 71     always@(posedge clk or negedge rst_n)begin
 72         if(!rst_n)
 73             div_cnt <= 0;
 74         else if(add_div_cnt)begin
 75             if(end_div_cnt)
 76                 div_cnt <= 0;
 77             else 
 78                 div_cnt <= div_cnt + 1'b1;
 79         end
 80         else 
 81             div_cnt <= 0;
 82     end
 83     
 84     assign add_div_cnt = busy == 1;
 85     assign end_div_cnt = add_div_cnt && div_cnt == SCL_CYC - 1;
 86     
 87     //比特计数器
 88     always@(posedge clk or negedge rst_n)begin
 89         if(!rst_n)
 90             bit_cnt <= 0;
 91         else if(add_bit_cnt)begin
 92             if(end_bit_cnt)
 93                 bit_cnt <= 0;
 94             else 
 95                 bit_cnt <= bit_cnt + 1'b1;
 96         end
 97     end
 98     
 99     assign add_bit_cnt = end_div_cnt;
100     assign end_bit_cnt = add_bit_cnt && bit_cnt == N - 1;
101     
102     always@(*)begin
103         case(state_c)
104             WRI_CTRL:N = 8;
105             ADDR:N = 8;
106             WRI_DATA:N = 8;
107             RD_CTRL:N = 8;
108             RD_DATA:N = 8;
109             default:N = 1;
110         endcase
111     end
112     
113     //---------------------iic时序四段式状态机部分-------------------------
114     
115     //时序逻辑描述状态转移
116     always@(posedge clk or negedge rst_n)begin
117         if(!rst_n)
118             state_c <= IDLE;
119         else 
120             state_c <= state_n;
121     end
122     
123     //组合逻辑描述状态转移条件
124     always@(*)begin
125         case(state_c)
126             IDLE:begin       //空闲状态
127                 if(idle2start)
128                     state_n = START;
129                 else 
130                     state_n = state_c;
131             end
132             
133             START:begin    //产生开始条件 即SCL高电平期间SDA拉低
134                 if(start2wri_ctrl)
135                     state_n = WRI_CTRL;
136                 else 
137                     state_n = state_c;
138             end
139             
140             WRI_CTRL:begin  //写器件地址和写标志位
141                 if(wri_ctrl2ack1)
142                     state_n = ACK1;
143                 else 
144                     state_n = state_c;
145             end
146             
147             ACK1:begin   //等待响应
148                 if(ack12addr)
149                     state_n = ADDR;
150                 else if(ack2idle)
151                     state_n = IDLE;
152                 else 
153                     state_n = state_c;
154             end
155             
156             ADDR:begin  //写存储单元地址
157                 if(addr2ack2)
158                     state_n = ACK2;
159                 else 
160                     state_n = state_c;
161             end
162             
163             ACK2:begin   //等待响应2
164                 if(ack22wri_data)   //写操作
165                     state_n = WRI_DATA;
166                 else if(ack22re_start)//读操作
167                     state_n = RE_START;
168                 else if(ack2idle)
169                     state_n = IDLE;
170                 else 
171                     state_n = state_c;
172             end
173             
174             WRI_DATA:begin   //写数据 8bit
175                 if(wri_data2ack3)
176                     state_n = ACK3;
177                 else 
178                     state_n = state_c;
179             end
180             
181             ACK3:begin   //等待响应3
182                 if(ack32stop)
183                     state_n = STOP;
184                 else if(ack2idle)
185                     state_n = IDLE;
186                 else 
187                     state_n = state_c;
188             end
189             
190             RE_START:begin  //若为读操作在响应2后再次构造开始条件
191                 if(re_start2rd_ctrl)
192                     state_n = RD_CTRL;
193                 else 
194                     state_n = state_c;
195             end
196             
197             RD_CTRL:begin   //写入存储单元地址和读标志位
198                 if(rd_ctrl2ack4)
199                     state_n = ACK4;
200                 else 
201                     state_n = state_c;
202             end
203             
204             ACK4:begin  //等待响应4
205                 if(ack42rd_data)
206                     state_n = RD_DATA;
207                 else if(ack2idle)
208                     state_n = IDLE;
209                 else 
210                     state_n = state_c;
211             end
212             
213             RD_DATA:begin  //读数据 8bit
214                 if(rd_data2nack)
215                     state_n = NACK;
216                 else 
217                     state_n = state_c;
218             end
219             
220             NACK:begin  //不响应 无操作即可
221                 if(nack2stop)
222                     state_n = STOP;
223                 else 
224                     state_n = state_c;
225             end
226             
227             STOP:begin  //构造停止条件
228                 if(stop2idle)
229                     state_n = IDLE;
230                 else 
231                     state_n = state_c;
232             end
233             
234             default:
235                 state_n = IDLE;
236         endcase
237     end
238     
239     //连续赋值语句定义状态转移条件
240     assign idle2start       = state_c  == IDLE     && (write_en || read_en);
241     assign start2wri_ctrl   = state_c  == START    && end_bit_cnt;  
242     assign wri_ctrl2ack1    = state_c  == WRI_CTRL && end_bit_cnt;
243     assign ack12addr        = state_c  == ACK1     && ack_valid && end_bit_cnt;
244     assign addr2ack2        = state_c  == ADDR     && end_bit_cnt;
245     assign ack22wri_data    = state_c  == ACK2     && ack_valid && !rd_flag && end_bit_cnt;
246     assign wri_data2ack3    = state_c  == WRI_DATA && end_bit_cnt;
247     assign ack32stop        = state_c  == ACK3     && ack_valid && end_bit_cnt;
248     assign ack22re_start    = state_c  == ACK2     && ack_valid && rd_flag && end_bit_cnt;
249     assign re_start2rd_ctrl = state_c  == RE_START && end_bit_cnt;
250     assign rd_ctrl2ack4     = state_c  == RD_CTRL  && end_bit_cnt;
251     assign ack42rd_data     = state_c  == ACK4     && ack_valid && end_bit_cnt;
252     assign rd_data2nack     = state_c  == RD_DATA  && end_bit_cnt;
253     assign nack2stop        = state_c  == NACK     && end_bit_cnt;
254     assign stop2idle        = state_c  == STOP     && end_bit_cnt;
255     assign ack2idle         = ack_state && ack_invalid;
256     
257 
258     
259     always@(posedge clk or negedge rst_n)begin
260         if(!rst_n)
261             ack_valid <= 0;
262         else if(ack12addr || ack22wri_data || ack32stop || ack22re_start || ack42rd_data || ack2idle)
263             ack_valid <= 0;
264         else if(ack_state && high_middle && !sda_en && !sda_in)
265             ack_valid <= 1;
266     end
267     
268     assign ack_state = state_c == ACK1 || state_c == ACK2 || state_c == ACK3 || state_c == ACK4;
269     
270     always@(posedge clk or negedge rst_n)begin
271         if(!rst_n)
272             ack_invalid <= 0;
273         else if(state_c == NACK && high_middle && !sda_en && sda_in)
274             ack_invalid <= 1;
275         else if(end_bit_cnt)
276             ack_invalid <= 0;
277     end
278     
279     //时序逻辑描述状态输出
280     
281     //scl时钟信号
282     always@(posedge clk or negedge rst_n)begin
283         if(!rst_n)
284             scl <= 0;
285         else if(add_div_cnt && div_cnt == SCL_CYC/4 - 1)
286             scl <= 1;
287         else if(add_div_cnt && div_cnt == SCL_CYC/4 + SCL_CYC/2 - 1)
288             scl <= 0;
289     end
290     
291     //找到scl高低电平中间点
292     always@(posedge clk or negedge rst_n)begin
293         if(!rst_n)
294             high_middle <= 0;
295         else if(add_div_cnt && div_cnt == SCL_CYC/2 - 1)
296             high_middle <= 1;
297         else 
298             high_middle <= 0;
299     end
300     
301     //三态门输出使能
302     always@(posedge clk or negedge rst_n)begin
303         if(!rst_n)
304             sda_en <= 1;
305         else if(idle2start || ack12addr || ack22wri_data || ack32stop || ack22re_start || nack2stop|| rd_data2nack)
306             sda_en <= 1;
307         else if(wri_ctrl2ack1 || addr2ack2 || wri_data2ack3 || rd_ctrl2ack4 || ack2idle || stop2idle)
308             sda_en <= 0;
309     end
310     
311     //数据总线输出寄存器
312     always@(posedge clk or negedge rst_n)begin
313         if(!rst_n)
314             sda_reg <= 1;
315         else if(idle2start)
316             sda_reg <= 1;
317         else if((state_c == START || state_c == RE_START) && high_middle)
318             sda_reg <= 0;
319         else if(state_c == WRI_CTRL)
320             sda_reg <= device_addr_wr[7-bit_cnt];
321         else if(state_c == ADDR)
322             sda_reg <= word_addr[7 - bit_cnt];
323         else if(state_c == WRI_DATA)
324             sda_reg <= wri_data[7 - bit_cnt];
325         else if(state_c == STOP && high_middle)
326             sda_reg <= 1;
327         else if(ack22re_start)
328             sda_reg <= 1;
329         else if(state_c == RE_START && high_middle)
330             sda_reg <= 0;
331         else if(state_c == RD_CTRL)
332             sda_reg <= device_addr_rd[7- bit_cnt];
333         else if(ack_state)
334             sda_reg <= 0;
335         else if(rd_data2nack)
336             sda_reg <= 1;
337         else if(nack2stop)
338             sda_reg <= 0;
339     end
340     
341     assign device_addr_wr = {4'b1010,cs,1'b0};
342     assign cs             = 3'b000;
343     assign word_addr      = share_addr;
344     assign device_addr_rd = {4'b1010,cs,1'b1};
345     
346     //读取数据缓存
347     always@(posedge clk or negedge rst_n)begin
348         if(!rst_n)
349             rd_buf <= 0;
350         else if(state_c == RD_DATA && high_middle)
351             rd_buf <= {rd_buf[6:0],sda_in};
352     end
353     
354     //读数据有效指示
355     always@(posedge clk or negedge rst_n)begin
356         if(!rst_n)
357             rd_data_vld <= 0;
358         else if(rd_data2nack)
359             rd_data_vld <= 1;
360         else 
361             rd_data_vld <= 0;
362     end
363     
364     //读数据输出
365     always@(posedge clk or negedge rst_n)begin
366         if(!rst_n)
367             rd_data <= 0;
368         else 
369             rd_data <= rd_buf;
370     end
371     
372     //读标志位
373     always@(posedge clk or negedge rst_n)begin
374         if(!rst_n)
375             rd_flag <= 0;
376         else if(read_en)
377             rd_flag <= 1;
378         else if(rd_flag && (stop2idle || state_c == IDLE))
379             rd_flag <= 0;
380     end
381     
382     //总线忙信号
383     always@(posedge clk or negedge rst_n)begin
384         if(!rst_n)
385             busy <= 0;
386         else if(write_en || read_en)
387             busy <= 1;
388         else if(busy == 1 &&(stop2idle || state_c == IDLE))
389             busy <= 0;
390     end
391     
392 endmodule
i2c_interface.v

相关文章: