Verification of WISHBONE I2C Master Core(IRUN+Simvision)
一、前言
很久没写技术博客了,有些懈怠,生活还得继续折腾。转眼工作一年多,时间越长越发觉得自己知之甚少,当然这跟IC行业技术密集有关。用空余时间在opencores网站上下载些小的IP看看 验证下,让自己对EDA tool, design, testbench, bus protocol都能有更好的认识。这次接触的是WISHBONE I2C Master Core。仿真验证工具是IES(Irun)+Simvision。
二、IP概述
这一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三个步骤,通过脚本观察它的使用方式。
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
run.csh
+access+rwc 设置编译结果的访问权限为读写执行
+define+WAVES 在外部添加verilog宏定义 WAVES,相当于`define WAVES
+incdir+xxx 添加路径,把design和testbench代码路径添加其中
后边直接添加需要的.v文件
现在来看看WAVES宏定义的作用:
条件编译使能dump .sh波形的代码段。具体使用方式参考文末链接。
./run.csh启动仿真:
仿真结束后启动Simvision的GUI。
simvision -64bit WAVES/ &
终于找到在公司debug的感觉了。
四、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
tst_bench_top.v
以新添加的中断测试为例。这个case是根据test1改动而来的,区别就是将不断读取寄存器来判断上一指令是否响应完成改为等待中断+读取状态寄存器方式。后者不会过多占用CPU的资源,从软件从面来讲也适用于带有调度算法的操作系统应用。在case开始前启动中断使能并写IACK比特位清除之前的中断标志位。之后在每次写CR后通过下段代码完成等待中断等系列操作。
// drive slave address
u0.wb_write(1, TXR, {SADR,WR} ); // present slave address, set write-bit
u0.wb_write(0, CR, 8'h90 ); // set command (start, write)
$display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} );
这部分对应的波形如下,可见中断输出信号inta0被拉高多次。2字节写操作完成。
五、总结
折腾折腾还是有帮助的。之后有打算在此基础上进一步深入,比如搭建基于UVM的验证环境来重新验证这个IP、添加更多的case覆盖所有的features、将interface改成APB bus。
七、参考
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
Verification of WISHBONE I2C Master Core(IRUN+Simvision)的更多相关文章
- I2C controller core之Bit controller(03)
FPGA proven, AISC proven, I2C controller core from OpenCores http://opencores.org/project,i2c Bit-co ...
- I2C controller core之Bit controller(01)
FPGA proven, AISC proven, I2C controller core from OpenCores http://opencores.org/project,i2c Bit-co ...
- I2C controller core之Bit controller(04)
4) detect start/stop condition START- falling edge on SDA while SCL is high; STOP - rising edge on ...
- I2C controller core之Bit controller(02)
4 generate clock and control signals 1 -- architecture signal iscl_oen, isda_oen : std_logic; -- int ...
- I2C controller core之Bit controller(05)
6 generate statemachine 1 -- port cmd_ack : out std_logic; -- command completed 4 -- architecture ty ...
- 【转】I2C总线相关知识
1. I2C access 1.1. I2C introduction I2C(Inter-Integrated Circuit)总线是由NXP恩智浦半导体公司在80年代开发的两线式串行总线,用来进行 ...
- 最新内核3.4)Linux 设备树加载I2C client adapter 的流程(内核3.4 高通)【转】
转自:https://blog.csdn.net/lsn946803746/article/details/52515225 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转 ...
- C51 I2C接口驱动,IO口模拟I2C(主+从)
Master.asm ;/*------------------------------------------------------------------*/ ;/* --- STC MCU I ...
- Git 解决同步 No value for key branch.master.merge found in
[core] repositoryformatversion = 0 filemode = false logallrefupdates = true [remote "origin&quo ...
随机推荐
- jdk环境配置(Windows)
电脑>属性>高级系统设置>环境变量 1 创建JAVA_HOME,值是你的刚刚jdk的安装目录,比如 C:\Program Files (x86)\Java\jdk1.8.0_101 ...
- vs工程生成dll文件及其调用方法
转载:https://blog.csdn.net/weixin_44536482/article/details/91519413 vs工程生成dll文件及其调用方法 ...
- ReverseFind的用法 ; 查找字符中最后一个字符
转载:https://blog.csdn.net/frivolousinstant/article/details/52796922 ReverseFind CString::ReverseFind ...
- 每日一题 LeetCode 679. 24点游戏 【递归】【全排列】
题目链接 https://leetcode-cn.com/problems/24-game/ 题目说明 题解 主要方法:递归 + 全排列 解释说明: 将 4 个数进行组合形成算式,发现除了 (a❈b) ...
- Ubuntu 20.04上通过Wine 安装微信
没有想过会在一个手机软件上花这么多心思,好在今天总算安装成功,觉得可以记录下这个过程,方便他人方便自己. 首先介绍下我使用过的其他方法,希望可以节省大家一些时间: Rambox Pro:因为原理是网页 ...
- 怎么快速从产品助理/初级 PM 成长为高级 PM?
一般想成为一枚产品经理的同学,如果没有经过系统的学习,都是从产品专员/助理开始做起的~ 那要想快速从产品助理/初级 PM 成长为高级 PM,以下这几点必不可少 直接上干货~ 全文篇幅较长,可以点赞收藏 ...
- OpenSSL编程模型
相关头文件: #include <openssl/ssl.h>#include <openssl/err.h> 客户端程序编写流程: 服务端编写流程: 产生私钥:# opens ...
- crontab极简教程
目录 crontab简介 crontab常用命令 示例 crontab简介 Linux crontab是用来定期执行程序的命令. 当安装完成操作系统之后,默认便会启动此任务调度命令. crontab常 ...
- SessionStorage、LocalStorage详解
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://blog.bitsrc.io/sessionstorage-and-localst ...
- 物联网wifi模块
物联网wifi模块 物联网wifi模块 是上海卓岚推出的MQTT+JSON转Modbus物联网WiFi核心模块.支持以MQTT的方式连接云端服务器,支持可以界面话配置,自主采集Modbus仪表/645 ...