1、前言算法
好久沒寫技術博客了,有些懈怠,生活還得繼續折騰。轉眼工做一年多,時間越長愈加以爲本身知之甚少,固然這跟IC行業技術密集有關。用空餘時間在opencores網站上下載些小的IP看看 驗證下,讓本身對EDA tool, design, testbench, bus protocol都能有更好的認識。此次接觸的是WISHBONE I2C Master Core。仿真驗證工具是IES(Irun)+Simvision。網絡
2、IP概述ide
這一IP也是直接從Opencores網站上下載,對於FPGA平臺來講是能夠直接拿來用的,還帶有spec 仿真腳本,真的是貼心。網絡連接見參考節。函數
對着圖簡單介紹下這個IP。內部有預分頻寄存器、控制寄存器、狀態寄存器、發送寄存器、接收寄存器還有命令寄存器。其中控制寄存器只負責使能,而命令寄存器則是I2C 協議中相關的指令操做。IP的核心邏輯在byte command controller和bit command controller兩個模塊中。工具
byte command controller根據命令控制寄存器的指令來將單一的命令轉換爲bit級別的命令,bit command controller接受bit級命令後將每一比特劃分更細的時間片操做SCL和SDA產生特定的時序。好比當讀取一個字節時,bit command controller接收到8個讀指令,而對於每個比特分爲5個時間片IDLE A B C D。這種分層設計方式具備很高的複用性和可讀性。測試
三、IES(IRUN)+Simvision工具網站
IES+Simvision是Cadence公司的仿真調試工具,Simvision的code schematic wave三者創建了映射關係,調試起來效率很是高。irun指令能夠直接一塊兒完成compilation elaboration simulation三個步驟,經過腳本觀察它的使用方式。spa
1 #!/bin/tcsh 2
3 set i2c = ../../.. 4 set bench = $i2c/bench 5 set wave_dir = $i2c/sim/rtl_sim/i2c_verilog/waves 6
7 irun -64bit \ 8 \ 9 +access+rwc \ 10 +define+WAVES \ 11 \ 12 +incdir+$bench/verilog \ 13 +incdir+$i2c/rtl/verilog \ 14 \ 15 $i2c/rtl/verilog/i2c_master_bit_ctrl.v \ 16 $i2c/rtl/verilog/i2c_master_byte_ctrl.v \ 17 $i2c/rtl/verilog/i2c_master_top.v \ 18 \ 19 $bench/verilog/i2c_slave_model.v \ 20 $bench/verilog/wb_master_model.v \ 21 $bench/verilog/tst_bench_top.v
+access+rwc 設置編譯結果的訪問權限爲讀寫執行操作系統
+define+WAVES 在外部添加verilog宏定義 WAVES,至關於`define WAVES.net
+incdir+xxx 添加路徑,把design和testbench代碼路徑添加其中
後邊直接添加須要的.v文件
如今來看看WAVES宏定義的做用:
條件編譯使能dump .sh波形的代碼段。具體使用方式參考文末連接。
./run.csh啓動仿真:
仿真結束後啓動Simvision的GUI。
simvision -64bit WAVES/ &
終於找到在公司debug的感受了。
4、testbench
自帶的testbench中例化了一個wb_master_model,兩個DUT以及一個i2c_slave_model。做者特地例化兩個I2C master意在驗證I2C協議中多總線機制。咱們能夠從Simvision的schematic中直觀地看到tb的總體結構。
testbench中利用wb_master_model內部的task來實現總線讀寫Core寄存器,也就是充當MCU中CPU的角色。原有的testbench code存在些問題,解決後添加了測試中斷信號的部分代碼。源代碼以下:
1 `include "timescale.v" 2 module tst_bench_top(); 3 4 // 5 // wires && regs 6 // 7 reg clk; 8 reg rstn; 9 10 wire [31:0] adr; 11 wire [2:0] adr_i; 12 wire [ 7:0] dat_i, dat_o, dat0_i, dat1_i; 13 wire we; 14 wire stb; 15 wire cyc; 16 wire ack; 17 wire inta0,inta1; 18 19 reg [7:0] q, qq; 20 21 wire scl, scl0_o, scl0_oen, scl1_o, scl1_oen; 22 wire sda, sda0_o, sda0_oen, sda1_o, sda1_oen; 23 24 parameter PRER_LO = 3'b000; 25 parameter PRER_HI = 3'b001; 26 parameter CTR = 3'b010; 27 parameter RXR = 3'b011; 28 parameter TXR = 3'b011; 29 parameter CR = 3'b100; 30 parameter SR = 3'b100; 31 32 parameter TXR_R = 3'b101; // undocumented / reserved output 33 parameter CR_R = 3'b110; // undocumented / reserved output 34 35 parameter RD = 1'b1; 36 parameter WR = 1'b0; 37 parameter SADR = 7'b0010_000; 38 parameter WAIT_TIME=50_000; 39 40 // 41 // Module body 42 // 43 44 // generate clock 45 always #5 clk = ~clk; 46 47 // hookup wishbone master model 48 wb_master_model #(8, 32) u0 ( 49 .clk(clk), 50 .rst(rstn), 51 .adr(adr), 52 .din(dat_i), 53 .dout(dat_o), 54 .cyc(cyc), 55 .stb(stb), 56 .we(we), 57 .sel(), 58 .ack(ack), 59 .err(1'b0), 60 .rty(1'b0) 61 ); 62 63 wire stb0 = stb & ~adr[3]; 64 wire stb1 = stb & adr[3]; 65 assign adr_i = adr[2:0]; 66 67 assign dat_i = ({{8'd8}{stb0}} & dat0_i) | ({{8'd8}{stb1}} & dat1_i); 68 69 // hookup wishbone_i2c_master core 70 i2c_master_top i2c_top ( 71 72 // wishbone interface 73 .wb_clk_i(clk), 74 .wb_rst_i(1'b0), 75 .arst_i(rstn), 76 .wb_adr_i(adr_i), 77 .wb_dat_i(dat_o), 78 .wb_dat_o(dat0_i), 79 .wb_we_i(we), 80 .wb_stb_i(stb0), 81 .wb_cyc_i(cyc), 82 .wb_ack_o(ack), 83 .wb_inta_o(inta0), 84 85 // i2c signals 86 .scl_pad_i(scl), 87 .scl_pad_o(scl0_o), 88 .scl_padoen_o(scl0_oen), 89 .sda_pad_i(sda), 90 .sda_pad_o(sda0_o), 91 .sda_padoen_o(sda0_oen) 92 ), 93 i2c_top2 ( 94 95 // wishbone interface 96 .wb_clk_i(clk), 97 .wb_rst_i(1'b0), 98 .arst_i(rstn), 99 .wb_adr_i(adr_i), 100 .wb_dat_i(dat_o), 101 .wb_dat_o(dat1_i), 102 .wb_we_i(we), 103 .wb_stb_i(stb1), 104 .wb_cyc_i(cyc), 105 .wb_ack_o(ack), 106 .wb_inta_o(inta1), 107 108 // i2c signals 109 .scl_pad_i(scl), 110 .scl_pad_o(scl1_o), 111 .scl_padoen_o(scl1_oen), 112 .sda_pad_i(sda), 113 .sda_pad_o(sda1_o), 114 .sda_padoen_o(sda1_oen) 115 ); 116 117 118 // hookup i2c slave model 119 i2c_slave_model #(SADR) i2c_slave ( 120 .scl(scl), 121 .sda(sda) 122 ); 123 124 // create i2c lines 125 delay m0_scl (scl0_oen ? 1'bz : scl0_o, scl), 126 m1_scl (scl1_oen ? 1'bz : scl1_o, scl), 127 m0_sda (sda0_oen ? 1'bz : sda0_o, sda), 128 m1_sda (sda1_oen ? 1'bz : sda1_o, sda); 129 130 pullup p1(scl); // pullup scl line 131 pullup p2(sda); // pullup sda line 132 133 initial 134 begin 135 `ifdef WAVES 136 $shm_open("waves"); 137 $shm_probe("AS",tst_bench_top,"AS"); 138 $display("INFO: Signal dump enabled ...\n\n"); 139 `endif 140 141 force i2c_slave.debug = 1'b1; // enable i2c_slave debug information 142 //force i2c_slave.debug = 1'b0; // disable i2c_slave debug information 143 144 $display("\nstatus: %t Testbench started\n\n", $time); 145 146 // $dumpfile("bench.vcd"); 147 // $dumpvars(1, tst_bench_top); 148 // $dumpvars(1, tst_bench_top.i2c_slave); 149 150 // initially values 151 clk = 0; 152 153 // reset system 154 rstn = 1'b1; // negate reset 155 #2; 156 rstn = 1'b0; // assert reset 157 repeat(1) @(posedge clk); 158 rstn = 1'b1; // negate reset 159 160 $display("status: %t done reset", $time); 161 162 @(posedge clk); 163 164 // 165 // program core 166 // 167 168 // program internal registers 169 u0.wb_write(1, PRER_LO, 8'hfa); // load prescaler lo-byte 170 u0.wb_write(1, PRER_LO, 8'hc8); // load prescaler lo-byte 171 u0.wb_write(1, PRER_HI, 8'h00); // load prescaler hi-byte 172 $display("status: %t programmed registers", $time); 173 174 u0.wb_cmp(0, PRER_LO, 8'hc8); // verify prescaler lo-byte 175 u0.wb_cmp(0, PRER_HI, 8'h00); // verify prescaler hi-byte 176 $display("status: %t verified registers", $time); 177 178 u0.wb_write(1, CTR, 8'h80); // enable core 179 $display("status: %t core enabled", $time); 180 181 182 183 $display("***************************"); 184 $display("test1: access slave (write)"); 185 $display("***************************"); 186 187 // drive slave address 188 u0.wb_write(1, TXR, {SADR,WR} ); // present slave address, set write-bit 189 u0.wb_write(0, CR, 8'h90 ); // set command (start, write) 190 $display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} ); 191 192 // check tip bit 193 u0.wb_read(1, SR, q); 194 while(q[1]) 195 u0.wb_read(0, SR, q); // poll it until it is zero 196 $display("status: %t tip==0", $time); 197 198 // send memory address 199 u0.wb_write(1, TXR, 8'h01); // present slave's memory address 200 u0.wb_write(0, CR, 8'h10); // set command (write) 201 $display("status: %t write slave memory address 01", $time); 202 203 // check tip bit 204 u0.wb_read(1, SR, q); 205 while(q[1]) 206 u0.wb_read(0, SR, q); // poll it until it is zero 207 $display("status: %t tip==0", $time); 208 209 // send memory contents 210 u0.wb_write(1, TXR, 8'ha5); // present data 211 u0.wb_write(0, CR, 8'h10); // set command (write) 212 $display("status: %t write data a5", $time); 213 214 // check tip bit 215 u0.wb_read(1, SR, q); 216 while(q[1]) 217 u0.wb_read(1, SR, q); // poll it until it is zero 218 $display("status: %t tip==0", $time); 219 220 // send memory contents for next memory address (auto_inc) 221 u0.wb_write(1, TXR, 8'h5a); // present data 222 u0.wb_write(0, CR, 8'h50); // set command (stop, write) 223 $display("status: %t write next data 5a, generate 'stop'", $time); 224 225 // check tip bit 226 u0.wb_read(1, SR, q); 227 while(q[1]) 228 u0.wb_read(1, SR, q); // poll it until it is zero 229 $display("status: %t tip==0", $time); 230 231 #WAIT_TIME; 232 $display("***************************"); 233 $display("test2: access slave (read)"); 234 $display("***************************"); 235 236 // drive slave address 237 u0.wb_write(1, TXR,{SADR,WR} ); // present slave address, set write-bit 238 u0.wb_write(0, CR, 8'h90 ); // set command (start, write) 239 $display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} ); 240 241 // check tip bit 242 u0.wb_read(1, SR, q); 243 while(q[1]) 244 u0.wb_read(1, SR, q); // poll it until it is zero 245 $display("status: %t tip==0", $time); 246 247 // send memory address 248 u0.wb_write(1, TXR, 8'h01); // present slave's memory address 249 u0.wb_write(0, CR, 8'h10); // set command (write) 250 $display("status: %t write slave address 01", $time); 251 252 // check tip bit 253 u0.wb_read(1, SR, q); 254 while(q[1]) 255 u0.wb_read(1, SR, q); // poll it until it is zero 256 $display("status: %t tip==0", $time); 257 258 // drive slave address 259 u0.wb_write(1, TXR, {SADR,RD} ); // present slave's address, set read-bit 260 u0.wb_write(0, CR, 8'h90 ); // set command (start, write) 261 $display("status: %t generate 'repeated start', write cmd %0h (slave address+read)", $time, {SADR,RD} ); 262 263 // check tip bit 264 u0.wb_read(1, SR, q); 265 while(q[1]) 266 u0.wb_read(1, SR, q); // poll it until it is zero 267 $display("status: %t tip==0", $time); 268 269 // read data from slave 270 u0.wb_write(1, CR, 8'h20); // set command (read, ack_read) 271 $display("status: %t read + ack", $time); 272 273 // check tip bit 274 u0.wb_read(1, SR, q); 275 while(q[1]) 276 u0.wb_read(1, SR, q); // poll it until it is zero 277 $display("status: %t tip==0", $time); 278 279 // check data just received 280 u0.wb_read(1, RXR, qq); 281 if(qq !== 8'ha5) 282 $display("\nERROR: Expected a5, received %x at time %t", qq, $time); 283 else 284 $display("status: %t 1th received %x", $time, qq); 285 286 // read data from slave 287 u0.wb_write(1, CR, 8'h68); // set command (read, nack_read,stop) 288 $display("status: %t read + ack", $time); 289 290 // check tip bit 291 u0.wb_read(1, SR, q); 292 while(q[1]) 293 u0.wb_read(1, SR, q); // poll it until it is zero 294 $display("status: %t tip==0", $time); 295 296 // check data just received 297 u0.wb_read(1, RXR, qq); 298 if(qq !== 8'h5a) 299 $display("\nERROR: Expected 5a, received %x at time %t", qq, $time); 300 else 301 $display("status: %t 2th received %x", $time, qq); 302 303 #WAIT_TIME; 304 $display("********************************************************"); 305 $display("test3: access slave (check invalid slave memory address)"); 306 $display("********************************************************"); 307 308 309 // drive slave address 310 u0.wb_write(1, TXR, {SADR,WR} ); // present slave address, set write-bit 311 u0.wb_write(0, CR, 8'h90 ); // set command (start, write) 312 $display("status: %t generate 'start', write cmd %0h (slave address+write). Check invalid address", $time, {SADR,WR} ); 313 314 // check tip bit 315 u0.wb_read(1, SR, q); 316 while(q[1]) 317 u0.wb_read(1, SR, q); // poll it until it is zero 318 $display("status: %t tip==0", $time); 319 320 // send memory address 321 u0.wb_write(1, TXR, 8'h10); // present slave's memory address 322 u0.wb_write(0, CR, 8'h10); // set command (write) 323 $display("status: %t write slave memory address 10", $time); 324 325 // check tip bit 326 u0.wb_read(1, SR, q); 327 while(q[1]) 328 u0.wb_read(1, SR, q); // poll it until it is zero 329 $display("status: %t tip==0", $time); 330 331 // slave should have send NACK 332 $display("status: %t Check for nack", $time); 333 if(!q[7]) 334 $display("\nERROR: Expected NACK, received ACK\n"); 335 336 // stop 337 u0.wb_write(1, CR, 8'h40); // set command (stop) 338 $display("status: %t generate 'stop'", $time); 339 340 // check tip bit 341 u0.wb_read(1, SR, q); 342 while(q[1]) 343 u0.wb_read(1, SR, q); // poll it until it is zero 344 $display("status: %t tip==0", $time); 345 346 #WAIT_TIME; 347 $display("********************************************************"); 348 $display("test4: access slave (write and interrupt acknowledge)"); 349 $display("********************************************************"); 350 351 u0.wb_write(1, CTR, 8'hC0); // enable core and interrupt 352 u0.wb_write(1,CR,8'h01); 353 $display("status: %t core enabled", $time); 354 355 //TODO 356 // drive slave address 357 u0.wb_write(1, TXR, {SADR,WR} ); // present slave address, set write-bit 358 u0.wb_write(0, CR, 8'h90 ); // set command (start, write) 359 $display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} ); 360 361 362 //wait interrupt 363 wait(inta0 == 1'b1); 364 $display("status: %t interrupt assert",$time); 365 u0.wb_read(1,SR,q); 366 if(q[1]) 367 $display("status: %t transfer complete",$time); 368 u0.wb_write(0, CR, 8'h01); // set command (IACK) 369 370 371 // send memory address 372 u0.wb_write(1, TXR, 8'h01); // present slave's memory address 373 u0.wb_write(0, CR, 8'h10); // set command (write) 374 $display("status: %t write slave memory address 01", $time); 375 376 377 //wait interrupt 378 wait(inta0 == 1'b1); 379 $display("status: %t interrupt assert",$time); 380 u0.wb_read(1,SR,q); 381 if(q[1]) 382 $display("status: %t transfer complete",$time); 383 u0.wb_write(0, CR, 8'h01); // set command (IACK) 384 385 // send memory contents 386 u0.wb_write(1, TXR, 8'ha5); // present data 387 u0.wb_write(0, CR, 8'h10); // set command (write) 388 $display("status: %t write data a5", $time); 389 390 //wait interrupt 391 wait(inta0 == 1'b1); 392 $display("status: %t interrupt assert",$time); 393 u0.wb_read(1,SR,q); 394 if(q[1]) 395 $display("status: %t transfer complete",$time); 396 u0.wb_write(0, CR, 8'h01); // set command (IACK) 397 398 399 // send memory contents for next memory address (auto_inc) 400 u0.wb_write(1, TXR, 8'h5a); // present data 401 u0.wb_write(0, CR, 8'h50); // set command (stop, write) 402 $display("status: %t write next data 5a, generate 'stop'", $time); 403 404 //wait interrupt 405 wait(inta0 == 1'b1); 406 $display("status: %t interrupt assert",$time); 407 u0.wb_read(1,SR,q); 408 if(q[1]) 409 $display("status: %t transfer complete",$time); 410 u0.wb_write(0, CR, 8'h01); // set command (IACK) 411 412 #250000; // wait 250us 413 $display("\n\nstatus: %t Testbench done", $time); 414 $finish; 415 end 416 417 endmodule 418 419 module delay (in, out); 420 input in; 421 output out; 422 423 assign out = in; 424 425 specify 426 (in => out) = (600,600); 427 endspecify 428 endmodule
以新添加的中斷測試爲例。這個case是根據test1改動而來的,區別就是將不斷讀取寄存器來判斷上一指令是否響應完成改成等待中斷+讀取狀態寄存器方式。後者不會過多佔用CPU的資源,從軟件從面來說也適用於帶有調度算法的操做系統應用。在case開始前啓動中斷使能並寫IACK比特位清除以前的中斷標誌位。以後在每次寫CR後經過下段代碼完成等待中斷等系列操做。
//wait interrupt
wait(inta0 == 1'b1);
$display("status: %t interrupt assert",$time);
u0.wb_read(1,SR,q);
if(q[1])
$display("status: %t transfer complete",$time);
u0.wb_write(0, CR, 8'h01); // set command (IACK)
這部分對應的波形以下,可見中斷輸出信號inta0被拉高屢次。2字節寫操做完成。
5、總結
折騰折騰仍是有幫助的。以後有打算在此基礎上進一步深刻,好比搭建基於UVM的驗證環境來從新驗證這個IP、添加更多的case覆蓋全部的features、將interface改爲APB bus。
6、參考
1 WISHBONE I2C Master Core下載地址: https://opencores.org/projects/i2c
2 Candence $shm_open $shm_probe 函數_Holden_Liu的博客-CSDN博客
https://blog.csdn.net/holden_liu/article/details/91376709