案例采用明德扬设计思想完成。IIC协议是非常常用的接口协议,在电子类岗位招聘要求中经常出现它的身影。关于IIC协议这里只做简要介绍,详细信息请自行百度或查阅相关Datasheet,网上资料非常多。该篇博文主要讲如何使用verilog来描述IIC协议,以读写EEPROM为例带领大家了解下明德扬四段式状态机规范和优势,另外还有一些自己在设计过程中总结的经验技巧。
IIC协议时序格式以Datasheet中时序图的形式供大家参考。IIC协议有一条时钟线SCL和一条双线数据总线SDA。SDA在SCL高电平时保持稳定,否则视为开始或结束条件。
发送端发送1byte数据后,接收端在下一个SCL高电平期间拉低总线表示应答,即接收数据成功。
以下分别是器件地址为1字节的EEPROM的单字节写和读操作,需要注意的是DEVICE ADDRESS段中前四位固定是4'b1010,后三位根据EEPROM地址信号电平决定(本次实验地址信号引脚均接地,因此后三位为000),最后一位是读写标志位,低电平写。
好了,有了以上五张时序图我们便知道要干什么了,就是实现这些时序嘛!对于这种串行时序,时间有先后且操作差异较大的要用状态机实现。每种类型操作定义在一个状态中,状态内部需要多个操作则配合计数器实现。整体设计思路如下:先构造时钟信号SCL,这里频率定义为200KHz,而系统时钟有频率为200MHz差分晶振提供,显然需要用到分频计数器。由于SCL高电平期间数据要保持稳定,所以我们在分频计数器计数到1/4处拉高SCL,3/4处拉低SCL,这样做的好处是在结束计数时正好处于SCL低电平中间点,此处作为数据变化的时刻再合适不过。
有了时钟信号,下一步就是通过不同的状态实现SDA信号线满足上述时序要求。我们先来划分状态(实际上时序图中都给我们标识好了),很明显综合考虑写和读两种时序,状态应定义为:初始状态、开始、写控制、响应1、写地址、响应2、写数据、响应3、重新开始、读控制、响应4、读数据、不响应、停止。这里写控制和读控制即是DEVICE ADDRESS阶段,唯一的区别在于读写标志位不同。能看出以上状态划分包括写流程分支和读流程分支,可以根据指令用一个标志位加以区分。
定义状态参数并采用独热码进行编码:
IIC协议中每次SCL高电平期间视为一次操作,因此为了让每个状态都有整数个SCL周期(完整分频计数周期),对每个状态进行比特计数,写控制、地址、写数据、读控制、读数据阶段计数周期是8,其他为1。另外为保证代码的“健壮性”,也就是即使发送1byte数据后没有响应也不至于挂死在等待响应阶段,设定在每次等待响应阶段若响应才进入下一操作,否则回到初始状态。由此得到状态转移图(只包括主要流程,转移条件及未响应回到IDLE状态未画出):
至此所有的设计工作都已经完成,接下来就是根据上述分析编写代码。在编写代码之前简要介绍明德扬四段式状态机的设计思想和代码规范:四段式状态机实质上是在三段式状态机基础上单独提出状态转移条件定义的结构。目的是让设计者一个时间段只专注于一件事情,也就是说当设计状态机的时候先把状态转移流程确定,而条件用不同的信号名代替,等状态转移流程确定后再定义转移条件。这样做的另一个好处是作为条件的信号名可以很方便的在后续时序逻辑中使用。其中用于代替条件的信号名要遵循类似如下格式:<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