分割線 ...
1 ////////////////////////////////////////////////// 2 //clk = 20 MHz ,一個周期50ns 3 //sck = 100 kHz (scl) ,一個周期 1000ns 4 //I2C在sck下降沿更新數據,上升沿讀取(採樣)數據 5 /////////////////////////////////////////////////// 6 module demo_I2C #(parameter F100K = 9'd200)(clk,rstn,start_sig,word_addr,wr_data,rd_data,done_sig,scl,sda,sq_i); 7 8 input clk ; 9 input rstn ; 10 11 input [1:0] start_sig ; // 12 input [7:0] word_addr ; //word address 13 input [7:0] wr_data ; //Data 14 output [7:0] rd_data ; //Data from EEPROM 15 output done_sig ; 16 17 output scl ; //sda和scl其實是用來作為模擬信號添加在這裡的,寄存器信號都用rscl和rsda表示了,最後用assign將rscl和rsda賦值給sda和scl,連到模塊外部模擬用 18 inout sda ; //sda表示當前sda的in或out的值 19 20 output [4:0] sq_i ; 21 /************************************ 22 在這裡,iic_func_module.v 的步驟i已經被引出來了。讀者要知道步驟i在無論是在設計上還是模擬上都有許多的好處。 23 步驟i在模擬中可以充當“調試跟蹤”的作用,因為只要模塊的那個部分出問題,步驟i就會指向它。 24 此外,步驟i在驅動IO口的時候,我們還可以知道模擬對象的內部到底發生什麼事情了。 25 *************************************/ 26 27 reg [4:0] i ; 28 reg [9:0] cnt ; 29 reg [4:0] go ; 30 reg isout ; 31 reg isack ; //臨時存放ack信號用於判斷 32 reg [7:0] rdata ; //存放任意8位數據的寄存器。在讀的最後一步,還會將讀到的8位sda存起來賦值給rd_data 33 reg rsda ; //用來寄存任意一位sda 34 reg rscl ; 35 reg rdone_sig ; 36 37 always@(posedge clk or negedge rstn) 38 begin 39 if(!rstn) 40 begin 41 // start_sig <= 2'b00 ; /*輸入信號不是寄存器類型,不需要Reset*/ 42 // word_addr <= 8'd0 ; /*在處理輸入輸出信號時,輸入信號因為不是reg而是wire,不需要Reset*/ 43 // wr_data <= 8'd0 ; /*輸出信號一般也不直接Reset,而是定義一個他們對應的reg,在Reset或者其他操作時對這些reg進行操作,最後用assign將輸出信號和各自的reg相連*/ 44 rdata <= 8'd0 ; 45 rdone_sig <= 1'b0 ; 46 47 rscl <= 1'b1 ; 48 rsda <= 1'b1 ; 49 i <= 5'd0 ; 50 isout <= 1'b1 ; 51 isack <= 1'b0 ; 52 rdata <= 8'd0 ; 53 go <= 3'd0 ; 54 end 55 56 else if(start_sig[0]) //write option 57 case(i) 58 0: //start 59 begin 60 if(cnt == 10'd0) 61 begin 62 rscl <= 1'b1 ; 63 rsda <= 1'b1 ; 64 end 65 else cnt <= cnt + 1'b1; 66 67 if(cnt == 10'd100) 68 begin 69 rsda <= 1'b0 ; 70 71 end 72 else cnt <= cnt + 1'b1; 73 74 if(cnt == F100K - 1'b1) 75 begin 76 i <= i + 5'd1 ; 77 cnt <= 0 ; 78 end 79 else cnt <= cnt + 1'b1; 80 end 81 82 1: //write device address 83 begin 84 isout = 1'b1; 85 rdata <= {4'b1010,3'b000,1'b0}; //1010是EEPROM型號,000是這顆EEPROM地址(三個引腳全部接地),0表示/W(寫) 86 i <= 5'd7; 87 go <= i + 1'b1; 88 end 89 90 2: //write word address 91 begin 92 isout = 1'b1; 93 i <= 5'd7; 94 rdata <= word_addr; 95 go <= i + 1'b1; 96 end 97 98 3: //write data 99 begin 100 isout = 1'b1; 101 i <= 5'd7; 102 rdata <= wr_data; 103 go <= i + 1'b1; 104 end 105 106 4: //stop 107 begin 108 if(cnt == 10'd0) 109 rscl <= 1'b0 ; 110 else if(cnt == 10'd50) 111 rscl <= 1'b1 ; 112 else 113 cnt <= cnt + 1'b1 ; 114 115 if(cnt == 10'd0) 116 rsda <= 1'b0 ; 117 else if(cnt == 10'd150) 118 rsda <= 1'b1 ; 119 else 120 cnt <= cnt + 1'b1 ; 121 122 if(cnt == 10'd50 + F100K - 1'b1) 123 begin 124 i <= i + 1'b1 ; 125 cnt <= 10'd0 ; 126 end 127 else 128 cnt <= cnt + 1'b1 ; 129 end 130 131 5: //return done_sig 132 begin 133 rdone_sig <= 1'b1; 134 i <= i + 1'b1; 135 end 136 6: //return IDLE 137 begin 138 rdone_sig <= 1'b0; 139 i <= 5'd0; 140 end 141 7,8,9,10,11,12,13,14: 142 begin 143 isout = 1'b1; 144 rsda <= rdata[14 - i]; 145 if(cnt == 10'd0) 146 rscl <= 1'b0 ; 147 else if(cnt == 10'd100) 148 rscl <= 1'b1 ; 149 else 150 cnt <= cnt + 1'b1 ; 151 152 if(cnt == F100K - 1) 153 begin 154 i <= i + 1'b1 ; 155 cnt <= 10'b0 ; 156 end 157 else 158 cnt <= cnt + 1'b1 ; 159 160 end 161 15: //waiting for acknowledge 162 begin 163 isout = 1'b0; //等待應答時是Read,因此是輸入模式,=表示即時響應 164 if(cnt == 10'b0) 165 rscl <= 1'b0; 166 else if(cnt == 10'b100) 167 rscl <= 1'b1; 168 else 169 cnt <= cnt + 1'b1; 170 171 if(cnt == F100K - 1) 172 begin 173 i <= i + 1'b1; 174 cnt <= 0; 175 end 176 else 177 cnt <= cnt + 1'b1; 178 179 if(cnt == 10'd150) 180 isack <= sda; //保險起見,在150個clk後才進行ack讀取 181 else 182 cnt <= cnt + 1'b1; 183 end 184 16: //判斷是否應答,返回go 185 begin 186 if(!isack) 187 i <= go; 188 else 189 i <= 0; 190 end 191 192 default: i <= 0; 193 endcase 194 /***************************************************************************************************************************************/ 195 else if(start_sig[1]) //read option 196 case(i) //讀寫操作不衝突,i 不衝突 197 0: //start 198 begin 199 if(cnt == 0) 200 begin 201 rscl <= 1'b1 ; 202 rsda <= 1'b1 ; 203 end 204 else cnt <= cnt + 1'b1; 205 206 if(cnt == 100) 207 begin 208 rsda <= 1'b0 ; 209 210 end 211 else cnt <= cnt + 1'b1; 212 213 if(cnt == F100K - 1) 214 begin 215 i <= i + 5'd1 ; 216 cnt <= 0 ; 217 end 218 else cnt <= cnt + 1'b1; 219 end 220 1: //write device address (read前先要write獲得從機應答) 221 begin 222 isout = 1'b1; 223 rdata <= {4'b1010,3'b000,1'b0}; //1010是EEPROM型號,000是這顆EEPROM地址(三個引腳全部接地),0表示/W(寫) 224 i <= 5'd10; 225 go <= i + 1'b1; 226 end 227 2: //write word address 228 begin 229 isout = 1'b1; 230 rdata <= word_addr; 231 i <= 5'd10; 232 go <= i + 1'b1; 233 end 234 3: //start again ,需要再控制sda和scl共同作用產生start信號 235 begin 236 isout = 1'b1; 237 if(cnt == 0) 238 begin 239 rscl <= 1'b0 ; 240 rsda <= 1'b0 ; 241 end 242 else cnt <= cnt + 1'b1; 243 244 if(cnt == 50) 245 begin 246 rsda <= 1'b1 ; 247 rscl <= 1'b1 ; 248 end 249 else cnt <= cnt + 1'b1; 250 251 if(cnt == 150) 252 begin 253 rsda <= 1'b0 ; 254 255 end 256 else cnt <= cnt + 1'b1; 257 if(cnt == 250) 258 begin 259 rscl <= 1'b0 ; //這時EEPROM已經start了 260 261 end 262 else cnt <= cnt + 1'b1; 263 if(cnt == 300 - 1) //保險起見,等到start穩定再進入下一狀態 264 begin 265 i <= i + 5'd1 ; 266 cnt <= 0 ; 267 end 268 else cnt <= cnt + 1'b1; 269 end 270 271 272 4: // 再寫一次device address,告訴從設備變成read了 273 begin 274 isout = 1'b1; //切換到輸入(讀取)模式 275 rdata <= {4'b1010,3'b000,1'b1}; //1010是EEPROM型號,000是這顆EEPROM地址(三個引腳全部接地),1表示R(讀) 276 i <= 5'd10; 277 go <= i + 1'b1; 278 end 279 5: //read data 280 begin 281 isout = 1'b0; 282 rdata <= 8'd0; ///* 註意這裡,在讀取8位sda寄存在rdata之前,要先將rdata清零*/// 283 i <= 5'd20; 284 go <= i + 1'b1; 285 end 286 287 6: //stop 288 begin 289 isout = 1'b1; 290 291 if(cnt == 0) 292 rscl <= 1'b0 ; 293 else if(cnt == 50) 294 rscl <= 1'b1 ; 295 else 296 cnt <= cnt + 1'b1 ; 297 298 if(cnt == 0) 299 rsda <= 1'b0 ; 300 else if(cnt == 150) 301 rsda <= 1'b1 ; 302 else 303 cnt <= cnt + 1'b1 ; 304 305 if(cnt == 50 + F100K - 1) 306 begin 307 i <= i + 1'b1 ; 308 cnt <= 0 ; 309 end 310 else 311 cnt <= cnt + 1'b1 ; 312 end 313 314 7: //return isdone 315 begin 316 rdone_sig <= 1'b1; 317 i <= i + 1'b1; 318 end 319 8: //return IDLE 320 begin 321 rdone_sig <= 1'b0; 322 i <= 0; 323 end 324 10,11,12,13,14,15,16,17: 325 begin 326 isout = 1'b1; 327 rsda <= rdata[17 - i]; 328 if(cnt == 0) 329 rscl <= 1'b0 ; 330 else if(cnt == 100) 331 rscl <= 1'b1 ; 332 else 333 cnt <= cnt + 1'b1 ; 334 335 if(cnt == F100K - 1) 336 begin 337 i <= i + 1'b1; 338 cnt <= 0 ; 339 end 340 else 341 cnt <= cnt + 1'b1 ; 342 343 end 344 18: //waiting for acknowledge 345 begin 346 isout = 1'b0; //等待應答時是Read,因此是輸入模式,=表示即時響應 347 if(cnt == 0) 348 rscl <= 1'b0; 349 else if(cnt == 100) 350 rscl <= 1'b1; 351 else 352 cnt <= cnt + 1'b1; 353 354 if(cnt == F100K - 1) 355 begin 356 i <= i + 1'b1; 357 cnt <= 0; 358 end 359 else 360 cnt <= cnt + 1'b1; 361 362 if(cnt == 150) 363 isack <= sda; //保險起見,在150個clk後才進行sda讀取 364 else 365 cnt <= cnt + 1'b1; 366 end 367 19: //判斷是否應答,返回go 368 begin 369 if(!isack) 370 i <= go; 371 else 372 i <= 0; 373 end 374 375 20,21,22,23,24,25,26,27: 376 begin 377 isout = 1'b0; 378 379 if(cnt == 0) 380 rscl <= 1'b0 ; 381 else if(cnt == 100) 382 rscl <= 1'b1 ; 383 else 384 cnt <= cnt + 1'b1 ; 385 386 if(cnt == F100K - 1) 387 begin 388 i <= i + 1'b1 ; 389 cnt <= 0 ; 390 end 391 else 392 cnt <= cnt + 1'b1 ; 393 394 if(cnt == 150) //保險起見,在150個clk後才進行sda讀取 395 rdata[27- i] <= sda; //讀寫都是先對MSB操作 396 else 397 cnt <= cnt + 1