-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathatom.xml
542 lines (257 loc) · 422 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Stephen's Blog</title>
<subtitle>welcome to Stephen's Blog</subtitle>
<link href="https://www.daipeihong.top/atom.xml" rel="self"/>
<link href="https://www.daipeihong.top/"/>
<updated>2022-12-28T02:10:34.477Z</updated>
<id>https://www.daipeihong.top/</id>
<author>
<name>DaiPeihong</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>lightroom后期(七)渐变滤镜的使用</title>
<link href="https://www.daipeihong.top/2022/12/28/lightroom-hou-qi-qi-jian-bian-lu-jing-de-shi-yong/"/>
<id>https://www.daipeihong.top/2022/12/28/lightroom-hou-qi-qi-jian-bian-lu-jing-de-shi-yong/</id>
<published>2022-12-28T02:10:00.000Z</published>
<updated>2022-12-28T02:10:34.477Z</updated>
<content type="html"><![CDATA[<p>渐变滤镜和径向滤镜的使用方法见[[lightroom后期(四)局部曝光的调整]]</p><h1 id="1-渐变滤镜"><a href="#1-渐变滤镜" class="headerlink" title="1. 渐变滤镜"></a>1. 渐变滤镜</h1><p>在拍摄的时候,经常会遇见画面当中的光线不平衡的问题,天空的部分过亮,而地面相对来讲比较暗,就算是光线比较柔和的日落也是一样,甚至遇到画面当中左右的光线都不平衡的问题。在遇到这类拍摄问题时,可以使用渐变滤镜来平衡光线,但是如果没有滤镜,在后期软件当中也可以有同样的工具来实现同样的效果。</p><p>在lightroom的软件右侧,选择工具栏当中添加渐变滤镜,由于是电子滤镜,它可以选择任何的方向、任何的位置,在添加完滤镜之后,在右侧的蒙板界面当中可以自定义滤镜的效果,可以设置白平衡、曝光度等等。在设置渐变滤镜的时候,有一个小技巧,拉动滤镜范围越大,渐变的过度就会越软化,如果把它的范围变得很小,它的过度就会变得更生硬。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212280932915.png" width="90%" height="90%"></div><p>渐变滤镜工具还可以对所覆盖的范围进行非常个性化的选择,这也是lightroom渐变滤镜当中非常强大的一个功能。</p><h1 id="2-范围蒙版"><a href="#2-范围蒙版" class="headerlink" title="2. 范围蒙版"></a>2. 范围蒙版</h1><p>滤镜蒙板的范围主要是分为两种:<span style="background:rgba(5, 117, 197, 0.2)">颜色蒙板</span>和<span style="background:#d3f8b6">明亮度蒙板</span>。<br><span style="background:#d3f8b6">明亮度范围蒙版</span>就比较适合于较大光比的拍摄场景,例如照片当中天空特别的亮,而建筑都处于阴影当中,使用明亮度范围可以很容易的选择天空部分或者是建筑部分。勾选显示明亮度蒙板,可以看到现在的渐变蒙板影响了哪个范围,红色的部分是受影响的,而白色的部分是不受影响的。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212280937587.png" width="80%" height="80%"></div><p>在这张照片当中呢,我想要让我所有的建筑把这部分这这一侧的云受到影响,是因为它是选择了明暗度嘛,这片云也是在暗的部分,所以它也受到了一些影响,那这个我觉得也是OK的,因为这个部分也确实有一点欠爆,我们可以把它的曝光度滤提升。水面的部分和有滤镜的这张照片相比呢,还是有点太暗了,所以我们可以选择渐变滤镜只影响水面的部分提高。</p><p>颜色范围蒙板适用于光线比较一致,有明显的颜色区分的场景,可以通过一个渐变滤镜选择颜色,点击滴管选择被选物体颜色。 这样这个渐变滤镜呢,就只影响某个颜色的部分。如此处选择橙色范围:</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212280954403.png" width="80%" height="80%"></div><p> light的渐变滤镜功能是非常强大的平衡光线的工具,通过不同的蒙版,可以帮我们快速的选择想要调整的区域,但这也不代表滤镜不值得购买,因为在前期的拍摄当中,我们会想尽一切办法让照片接近于完美,这样在后期修图的时候才能让照片有更多的可能性。</p><h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p><a href="https://www.bilibili.com/video/BV1p7411r7nT/?vd_source=83d80c57e4377b69f911cc68016fb835">【Lightroom教程】使用渐变滤镜工具轻松搞定光线不平衡的照片_哔哩哔哩_bilibili</a></p>]]></content>
<summary type="html"><p>渐变滤镜和径向滤镜的使用方法见[[lightroom后期(四)局部曝光的调整]]</p>
<h1 id="1-渐变滤镜"><a href="#1-渐变滤镜" class="headerlink" title="1. 渐变滤镜"></a>1. 渐变滤镜</h1><p>在拍摄的</summary>
<category term="摄影后期" scheme="https://www.daipeihong.top/categories/%E6%91%84%E5%BD%B1%E5%90%8E%E6%9C%9F/"/>
<category term="lightroom" scheme="https://www.daipeihong.top/tags/lightroom/"/>
</entry>
<entry>
<title>lightroom后期(六)lr基础面板的使用</title>
<link href="https://www.daipeihong.top/2022/12/27/lightroom-hou-qi-liu-lr-ji-chu-mian-ban-de-shi-yong/"/>
<id>https://www.daipeihong.top/2022/12/27/lightroom-hou-qi-liu-lr-ji-chu-mian-ban-de-shi-yong/</id>
<published>2022-12-27T04:47:00.000Z</published>
<updated>2022-12-27T04:54:21.566Z</updated>
<content type="html"><![CDATA[<h1 id="1-基础面板布局"><a href="#1-基础面板布局" class="headerlink" title="1. 基础面板布局"></a>1. 基础面板布局</h1><p>在基本面板当中,有很多重要的参数需要设置,后期十将近80%的调整,其实都集中在基本面板当中。在基本面板当中,可以做三件事儿,<span style="background:rgba(240, 200, 0, 0.2)">第一调整白平衡</span>,<span style="background:#d3f8b6">第二修正照片的曝光</span>,也就是色调部分,<span style="background:rgba(5, 117, 197, 0.2)">第三是对照片进行个性化处理的偏好设置</span>。在后期时,按照从上到下的顺序进行调整。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212271008235.png" width="30%" height="30%"></div><h1 id="2-调整白平衡"><a href="#2-调整白平衡" class="headerlink" title="2. 调整白平衡"></a>2. 调整白平衡</h1><p><a href="http://localhost:4000/2022/12/27/lightroom-hou-qi-wu-bai-ping-heng-de-xiao-zhun/">白平衡的校准</a></p><h1 id="3-修正曝光"><a href="#3-修正曝光" class="headerlink" title="3. 修正曝光"></a>3. 修正曝光</h1><h2 id="3-1-曝光度"><a href="#3-1-曝光度" class="headerlink" title="3.1 曝光度"></a>3.1 曝光度</h2><p>在色调设置中的六个数值都是用来修正照片的曝光度的,照片中任何过曝嵌爆的现象,都可以在这里得到解决。在设置每一项数值的时候,到底该设置多少才是正确的曝光呢?因为每张照片的情况呢都不一样,这样没有一个完全正确的参数设置,所以在调整有关曝光度的时候啊,通过界面上方的<span style="background:rgba(240, 200, 0, 0.2)">直方图曲线</span>来观察设置是否合理。也可以打开高光和阴影剪切功能,在调整的时候直接看照片的高光和阴影变化。红色部分过多,说明这个区域的曝光过度,蓝色太多,说明这个区域太暗。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212271023437.png" width="80%" height="80%"></div><p>当照片的曝光不正确的时候呢,可以通过调整曝光度数值来拉回到正确的曝光。曝光度数值,对照片的中间调影响最大,也就是直方图的中间部分,当然它也会轻度影响到高光和阴影,所以说在前期拍摄的时候,达到正确的曝光是十分重要的。通常一张正确的曝光下的照片,不需要过多的调整曝光度,因为在后期过度的增加曝光度会带来更多的噪点。</p><h2 id="3-2-对比度"><a href="#3-2-对比度" class="headerlink" title="3.2 对比度"></a>3.2 对比度</h2><p>对比度设置用来<span style="background:rgba(136, 49, 204, 0.2)">增加照片当中亮部和暗部之间的差距</span>,就是<span style="background:rgba(136, 49, 204, 0.2)">让暗的部分更暗,亮的部分更亮</span>。减少对比度可以让暗的部分亮一些,亮的部分暗下来缩小亮部与暗部之间的差距。<br>增加对比度,还会影响另外一个效果,由于亮部和暗部之间多了更多的细节,所以呢,也会让照片看上去更生动,就好像增加了饱和度似的。</p><h2 id="3-3-高光"><a href="#3-3-高光" class="headerlink" title="3.3 高光"></a>3.3 高光</h2><p>通过这个设置,可以增加或者减少照片当中高光部分的曝光度,恢复一部分细节。</p><h2 id="3-4-阴影"><a href="#3-4-阴影" class="headerlink" title="3.4 阴影"></a>3.4 阴影</h2><p>阴影部分的调整,提高阴影部分的曝光,可以让那些没有被光照到的地方可以被看得更清楚,在拍摄夜景的时候,也可以让照片里有更多的内容显现出来。</p><h2 id="3-5-白色-amp-黑色色阶"><a href="#3-5-白色-amp-黑色色阶" class="headerlink" title="3.5 白色&黑色色阶"></a>3.5 白色&黑色色阶</h2><p>白色色阶和黑色色阶的原理,和高光阴影的设置非常的类似,<span style="background:rgba(240, 200, 0, 0.2)">区别在于白色和黑色色阶对亮部和暗部的调整范围相比来讲更大。</span>从直方图当中可以看到,<span style="background:rgba(240, 200, 0, 0.2)">当调整白色和黑色色阶的时候,直方图中的曲线的波动相对高光和阴影调整时要更大。</span>(除了配合直方图之外,在调整白色和黑色色阶的时候啊,有一个小技巧可以来帮我们判断是否调整过度。<u>在拖动滑轨的同时呢,按下键盘上的alt键,屏幕会变成黑色或者是白色,而过量和过暗的部分呢,就会随着拖动数值显现出来。</u>)</p><h1 id="4-偏好设置"><a href="#4-偏好设置" class="headerlink" title="4. 偏好设置"></a>4. 偏好设置</h1><p>经过白平衡和色调调整之后呢,一张照片的基本色彩就还原出来了,正确的曝光可以让照片恢复本该有的细节和立体感。在这个基础上啊,可以进行偏好设置。<br>纹理设置可以增加照片当中的细节,建筑上面的细节适合使用纹理来体现细节,但是在增加建筑纹理的同时呢,天空的纹理也在增加,特别是建筑与天空的衔接之处呢,会出现断层的现象。所以在提升纹理的同同时,可以通过边缘来查看是否在合理的范围之内。<br>清晰度的调整和对比度十分的相似,可以把这个设置看作是<span style="background:rgba(5, 117, 197, 0.2)">对比度的微调版本</span>,通过改变亮部和暗部之间的过渡,让照片当中的细节轮廓更加清晰。通过向右拖动清晰度来增加建筑纹理和形状,但是人像照片可就要注意,这是放大皮肤瑕疵的罪魁祸首,要向左移动减少清晰度来实现柔胶的效果。<br>去朦胧功能,可以非常有效的去除因为逆光拍摄时所产生的晕光,或者是啊阴天时的雾,让照片多一些层次感,也可以很有效的去除雾霾。反向调整可以增加迷雾的效果。<br>鲜艳度和饱和度这两个设置,看上去几乎一模一样。<span style="background:rgba(240, 107, 5, 0.2)">鲜艳度和饱和度的区别在于:鲜艳度控制的是照片当中有颜色的部分,而饱和度控制的是全部颜色。</span>把鲜艳度调整到最左的时候,照片仍然有一小部分的颜色存在,而饱和度设置如果拖到最左的话,照片就会完全变成一张黑白照片。在某种程度上,鲜艳度要比饱和度更实用一些,看上去也不会特别的不自然。</p><p>通过这些设置,一张照片基本上就修出来了,就这样直接导出的话,也是完全可以的,只要在前期拍摄的时候,该对焦的都拍清楚了,曝光没有相差特别的远,在后期的时候都可以通过基本设置拯救回来。</p>]]></content>
<summary type="html"><h1 id="1-基础面板布局"><a href="#1-基础面板布局" class="headerlink" title="1. 基础面板布局"></a>1. 基础面板布局</h1><p>在基本面板当中,有很多重要的参数需要设置,后期十将近80%的调整,其实都集中在基本面板当</summary>
<category term="摄影后期" scheme="https://www.daipeihong.top/categories/%E6%91%84%E5%BD%B1%E5%90%8E%E6%9C%9F/"/>
<category term="lightroom" scheme="https://www.daipeihong.top/tags/lightroom/"/>
</entry>
<entry>
<title>lightroom后期(五)白平衡的校准</title>
<link href="https://www.daipeihong.top/2022/12/27/lightroom-hou-qi-wu-bai-ping-heng-de-xiao-zhun/"/>
<id>https://www.daipeihong.top/2022/12/27/lightroom-hou-qi-wu-bai-ping-heng-de-xiao-zhun/</id>
<published>2022-12-27T01:34:00.000Z</published>
<updated>2022-12-27T04:47:30.191Z</updated>
<content type="html"><![CDATA[<h1 id="1-白平衡"><a href="#1-白平衡" class="headerlink" title="1. 白平衡"></a>1. 白平衡</h1><p>对于城市和风光摄影来说,无论是在拍摄前期设置相机内的参数,还是后期利用软件进行修图,最大的目的,就是让照片当中所有的颜色能够还原最真实的色彩。这其中白平衡的设置呢是最不可忽视的,在后期修图的时候,可以利用白平衡来校正拍摄时所产生的偏色。</p><p>白平衡的色温和色调数值,和一张照片最终呈现的颜色效果有非常直接的关系,所以在后期一张照片的时候,第一步我们应该做的就是校正白平衡,因为数码相机在拍摄过程中所做出的判断是非常有可能有偏差的,例如在拍摄雪景或者画面中阴影过多的照片,颜色多多少少都会偏蓝;而使用ND减光滤镜的时候呢,照片也是会有出现偏色的情况。</p><h1 id="2-三种校正白平衡的方法"><a href="#2-三种校正白平衡的方法" class="headerlink" title="2. 三种校正白平衡的方法"></a>2. 三种校正白平衡的方法</h1><h2 id="2-1-直接拖动白平衡数值"><a href="#2-1-直接拖动白平衡数值" class="headerlink" title="2.1 直接拖动白平衡数值"></a>2.1 直接拖动白平衡数值</h2><p>在lightroom基本面板当中,比较直观的调整方法就是用鼠标直接拖动色温和色调的数值,但这如果对色温工作原理不是特别熟悉的话,这种方法就很让人头疼,因为不知道要选多少的K值才合适,也会比较浪费时间。</p><h2 id="2-2-选择白平衡预设"><a href="#2-2-选择白平衡预设" class="headerlink" title="2.2 选择白平衡预设"></a>2.2 选择白平衡预设</h2><p>另外一个设置白平衡的方法,是在白平衡的设置的右侧有一个下拉菜单,这里会显示几种不同的预设,和相机内设置的白平衡操作非常的类似。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212271154327.png" width="40%" height="40%"></div><p>原照设置就是拍摄时我们在相机里所使用的白平衡数值,这里可以做一个参考。自动以及其他几个预设呢,可以让我们很方便的预览照片在不同的白平衡下会是什么样的效果。这里边需要注意的是,色温的数值以及预设菜单只会出现在编辑RAW格式文档的时候,如果拍摄的是jpeg模式,是没有这个设置的,只有“原照”、“自动”、“自定义”。<br>一张照片的白平衡会有四种偏色的可能性,偏蓝,太黄、绿调过多,还有太粉。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212271201381.png" width="80%" height="80%"></div><p>白平衡中的色温和色调这两个设置,就是为了调整这四种偏色的,当颜色偏蓝的时候呢,我们可以把色温右移,色调太粉的时候呢,我们可以把色调的数值左移。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212271200771.png" width="50%" height="50%"></div><h2 id="2-3-使用滴管进行中性颜色取样"><a href="#2-3-使用滴管进行中性颜色取样" class="headerlink" title="2.3 使用滴管进行中性颜色取样"></a>2.3 使用滴管进行中性颜色取样</h2><p>但是这些都要靠我们用眼睛来判断,为了提高准确度,我们可以使用局部采样的方法来矫正白平衡。第三种矫正白平衡的方式:在白平衡的设置左侧,有这样一个滴管的图标,叫做白平衡选择器,点击之后呢,它就变成了一个滴管的形式,然后呢,我们要在照片当中选择一个中性颜色的部分(也就是本应该是白色或或者是灰色的部分)来进行取样。</p><p>一定记得勾选显示放大视图这个工具,一定要把它选择上。在这个放大视图下侧,我会看到有三个数值是RGB,也就是red green blue,红、绿、蓝,它们是光的三原色,当这三个数字非常接近的时候,说明这个部分的颜色是中性的。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212271239540.png" width="60%" height="60%"></div><p>调整过程中可以使用键盘的上下左右键来手动调整白平衡,调整色温色调。照片当中的三个颜色数值非常接近时,说明所取样的部分,已经把它校正到了中性色彩,这就说明现在整张照片的白平衡是正确的。</p>]]></content>
<summary type="html"><h1 id="1-白平衡"><a href="#1-白平衡" class="headerlink" title="1. 白平衡"></a>1. 白平衡</h1><p>对于城市和风光摄影来说,无论是在拍摄前期设置相机内的参数,还是后期利用软件进行修图,最大的目的,就是让照片当中所</summary>
<category term="摄影后期" scheme="https://www.daipeihong.top/categories/%E6%91%84%E5%BD%B1%E5%90%8E%E6%9C%9F/"/>
<category term="lightroom" scheme="https://www.daipeihong.top/tags/lightroom/"/>
</entry>
<entry>
<title>SystemVerilog课程进度总览(完结)</title>
<link href="https://www.daipeihong.top/2022/12/10/systemverilog-ke-cheng-jin-du-zong-lan-chi-xu-geng-xin/"/>
<id>https://www.daipeihong.top/2022/12/10/systemverilog-ke-cheng-jin-du-zong-lan-chi-xu-geng-xin/</id>
<published>2022-12-10T12:48:00.000Z</published>
<updated>2022-12-10T08:51:37.200Z</updated>
<content type="html"><![CDATA[<p><a href="http://daipeihong.top/2022/10/04/systemverilog-ke-cheng-bi-ji-san/">一、初步初步使用module和initial实现初步激励</a><br>初步TestBench平台概览</p> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211052041415.png" width="500" height="300" title=" "> <p><a href="http://daipeihong.top/2022/10/04/systemverilog-ke-cheng-bi-ji-si/">二、继续改进</a><br><span style="background:rgba(5, 117, 197, 0.2)">改进1</span>:将initial过程块改造为task、将前一次的两个initial begin end 语句封装进task任务中<br><span style="background:rgba(5, 117, 197, 0.2)">改进2</span>:任意source_channl向任意destination_channl发送数据</p><p>初步TestBench平台概览</p> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211052041415.png" width="500" height="300" title=" "> <p><a href="http://daipeihong.top/2022/10/17/systemverilog-ke-cheng-bi-ji-wu/">三、构建generator</a><br><span style="background:rgba(5, 117, 197, 0.2)">改进1</span>:stimulator只发送数据,构建generator产生数据发送给stimulator<br><span style="background:rgba(5, 117, 197, 0.2)">改进2</span>:来自于不同的source chnl可以并行发送<br><span style="background:rgba(5, 117, 197, 0.2)">改进3</span>:增加接口(interface)<br><span style="background:rgba(5, 117, 197, 0.2)">改进4</span>:增加组件monitor,监测输入数据和输出数据 </p><p>初步TestBench平台概览</p> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211171916069.png" width="500" height="300" title=" "> <p><a href="http://daipeihong.top/2022/10/24/systemverilog-ke-cheng-bi-ji-liu/">四、stimulator、generator、monitor改为类、增加checker组件</a><br><span style="background:rgba(5, 117, 197, 0.2)">改进1</span>:将数据传输从结构体改造为类<br><span style="background:rgba(5, 117, 197, 0.2)">改进2</span>:将drive_chnl任务的输入改为类<br><span style="background:rgba(5, 117, 197, 0.2)">改进3</span>:将stimulator、generator、monitor改为类<br><span style="background:rgba(5, 117, 197, 0.2)">改进4</span>:增加checker组件</p><p>TestBench平台概览</p> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211171924107.png" width="500" height="300" title=" "> <p><a href="http://daipeihong.top/2022/10/29/systemverilog-ke-cheng-bi-ji-qi/">五、利用类的继承,将多种test包含进base test中</a><br><span style="background:rgba(5, 117, 197, 0.2)">改进1</span>:利用类的继承,将多种test包含进base test中<br><span style="background:rgba(5, 117, 197, 0.2)">改进2</span>:增加数据传输完成信号、checker的判断、test name<br><span style="background:rgba(5, 117, 197, 0.2)">改进3</span>:package封装与makefile的使用<br>TestBench平台概览</p> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211171924107.png" width="500" height="300" title=" "> <p><a href="http://daipeihong.top/2022/11/01/systemverilog-ke-cheng-bi-ji-ba/">六、利用随机产生数据</a><br><span style="background:rgba(5, 117, 197, 0.2)">改进1</span>:改用samephore判断chnl是否被占用<br><span style="background:rgba(5, 117, 197, 0.2)">改进2</span>:对single chnl添加随机<br>TestBench平台概览同上</p><p><a href="http://daipeihong.top/2022/11/22/systemverilog-ke-cheng-bi-ji-jiu/">七、mailbox的使用</a><br><span style="background:rgba(5, 117, 197, 0.2)">改进1</span>:将generator、monitor中的队列改为mailbox<br><span style="background:rgba(5, 117, 197, 0.2)">改进2</span>:将generator、monitor中的mailbox设置上限为1<br><span style="background:rgba(5, 117, 197, 0.2)">改进3</span>:将数据从genarator到stimulator的传递改为mailbox句柄的传递<br><span style="background:rgba(5, 117, 197, 0.2)">改进4</span>:使用关联数组进行test用例的选择<br>TestBench平台概览同上</p><p><a href="http://daipeihong.top/2022/11/23/systemverilog-ke-cheng-bi-ji-shi/">八、改进测试用例</a><br><span style="background:rgba(5, 117, 197, 0.2)">改进1</span>:从generator收到的数据按照chnl编号分发到16个mailbox里<br><span style="background:rgba(5, 117, 197, 0.2)">改进2</span>:完善two_ch_same_chout_test、full_ch_test<br><span style="background:rgba(5, 117, 197, 0.2)">改进3</span>:完善各test父类与子类的关系<br><span style="background:rgba(5, 117, 197, 0.2)">改进4</span>:完善rt_full_ch_test<br>TestBench平台概览同上</p><p><a href="http://daipeihong.top/2022/11/24/systemverilog-ke-cheng-bi-ji-shi-yi/">九、查看功能覆盖率</a><br><span style="background:rgba(5, 117, 197, 0.2)">覆盖率1</span>:查看代码覆盖率<br><span style="background:rgba(5, 117, 197, 0.2)">覆盖率2</span>:查看功能覆盖率<br>TestBench平台概览同上</p>]]></content>
<summary type="html"><p><a href="http://daipeihong.top/2022/10/04/systemverilog-ke-cheng-bi-ji-san/">一、初步初步使用module和initial实现初步激励</a><br>初步TestBench平台概览</p>
</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
<category term="总结" scheme="https://www.daipeihong.top/tags/%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>SystemVerilog课程笔记(十一)</title>
<link href="https://www.daipeihong.top/2022/11/24/systemverilog-ke-cheng-bi-ji-shi-yi/"/>
<id>https://www.daipeihong.top/2022/11/24/systemverilog-ke-cheng-bi-ji-shi-yi/</id>
<published>2022-11-24T12:07:00.000Z</published>
<updated>2022-12-10T08:06:12.585Z</updated>
<content type="html"><![CDATA[<h1 id="1-查看代码覆盖率"><a href="#1-查看代码覆盖率" class="headerlink" title="1. 查看代码覆盖率"></a>1. 查看代码覆盖率</h1><h2 id="1-1-修改Makefile"><a href="#1-1-修改Makefile" class="headerlink" title="1.1 修改Makefile"></a>1.1 修改Makefile</h2><pre class=" language-c"><code class="language-c">###########<span class="token macro property"># User variables</span>###########TB <span class="token operator">=</span> tbSEED <span class="token operator">=</span> <span class="token number">1</span>TESTNAME <span class="token operator">?</span><span class="token operator">=</span> rt_single_ch_testFILES <span class="token operator">=</span> router<span class="token punctuation">.</span>v rt_test_pkg<span class="token punctuation">.</span>sv lab11tb<span class="token punctuation">.</span>sv#############<span class="token macro property"># Environment variables</span>############COMP <span class="token operator">=</span> vcs <span class="token operator">-</span>full64 <span class="token operator">-</span>sverilog <span class="token operator">-</span>debug_acc<span class="token operator">+</span>all <span class="token operator">-</span>timescale<span class="token operator">=</span>1ps<span class="token operator">/</span>1ps <span class="token operator">-</span>l elab<span class="token punctuation">.</span>log <span class="token operator">-</span>cm line<span class="token operator">+</span>tgl<span class="token operator">+</span>branch<span class="token operator">+</span>fsm $<span class="token punctuation">(</span>FILES<span class="token punctuation">)</span>RUN <span class="token operator">=</span> <span class="token punctuation">.</span><span class="token operator">/</span>$<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simv <span class="token operator">-</span>l run<span class="token punctuation">.</span>log <span class="token operator">-</span>sml <span class="token operator">+</span>ntb_random_seed<span class="token operator">=</span>$<span class="token punctuation">(</span>SEED<span class="token punctuation">)</span> <span class="token operator">+</span>TESTNAME<span class="token operator">=</span>$<span class="token punctuation">(</span>TESTNAME<span class="token punctuation">)</span> <span class="token operator">-</span>cm line<span class="token operator">+</span>tgl<span class="token operator">+</span>branch <span class="token operator">-</span>cm_name $<span class="token punctuation">(</span>TESTNAME<span class="token punctuation">)</span>_$<span class="token punctuation">(</span>SEED<span class="token punctuation">)</span>comp<span class="token punctuation">:</span> $<span class="token punctuation">(</span>COMP<span class="token punctuation">)</span> <span class="token operator">-</span>top $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span> <span class="token operator">-</span>o $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simvrun<span class="token punctuation">:</span> $<span class="token punctuation">(</span>RUN<span class="token punctuation">)</span> rung<span class="token punctuation">:</span> $<span class="token punctuation">(</span>RUN<span class="token punctuation">)</span> <span class="token operator">-</span>guieditcov<span class="token punctuation">:</span> urg <span class="token operator">-</span>full64 <span class="token operator">-</span>format both <span class="token operator">-</span>dir $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simv<span class="token punctuation">.</span>vdb dve <span class="token operator">-</span>cov <span class="token operator">-</span>full64 <span class="token operator">-</span>dir $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simv<span class="token punctuation">.</span>vdbviewcov<span class="token punctuation">:</span> urg <span class="token operator">-</span>full64 <span class="token operator">-</span>format both <span class="token operator">-</span>dir $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simv<span class="token punctuation">.</span>vdb firefox urgReport<span class="token operator">/</span>dashboard<span class="token punctuation">.</span>htmlclean<span class="token punctuation">:</span> rm <span class="token operator">-</span>rf AN<span class="token punctuation">.</span>DB DVEfiles csrc simv<span class="token operator">*</span> <span class="token operator">*</span><span class="token punctuation">.</span>cst <span class="token operator">*</span><span class="token punctuation">.</span>simv <span class="token operator">*</span><span class="token punctuation">.</span>simv<span class="token punctuation">.</span>daidir <span class="token operator">*</span><span class="token punctuation">.</span>simv<span class="token punctuation">.</span>vdb ucli<span class="token punctuation">.</span>key rm <span class="token operator">-</span>rf <span class="token operator">*</span><span class="token punctuation">.</span>log<span class="token operator">*</span> <span class="token operator">*</span><span class="token punctuation">.</span>vpd <span class="token operator">*</span><span class="token punctuation">.</span>h urgReport</code></pre><h2 id="1-2-查看代码覆盖率"><a href="#1-2-查看代码覆盖率" class="headerlink" title="1.2 查看代码覆盖率"></a>1.2 查看代码覆盖率</h2><p>编译后,进行test用例的测试,</p><blockquote><p>make run TESTNAME=rt_single_ch_test SEED=1000//改变随机种子进行测试<br>make run TESTNAME=rt_single_ch_test SEED=2000</p></blockquote><p>会生成覆盖率的database文件</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091513983.png" width="80%" height="80%"></div><p>执行VCS命令,打开coverage面板,添加上面生成覆盖率的database文件</p><blockquote><p>dve -cov -full64 &</p></blockquote><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091520934.png" width="80%" height="80%"></div><p>添加后,查看覆盖率</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211242030613.png" width="100%" height="100%"></div><h1 id="2-增加covergroup,查看功能覆盖率"><a href="#2-增加covergroup,查看功能覆盖率" class="headerlink" title="2. 增加covergroup,查看功能覆盖率"></a>2. 增加covergroup,查看功能覆盖率</h1><h2 id="2-1-增加covergroup代码"><a href="#2-1-增加covergroup代码" class="headerlink" title="2.1 增加covergroup代码"></a>2.1 增加covergroup代码</h2><h3 id="2-1-1-改进后的rt-test-pkg-sv"><a href="#2-1-1-改进后的rt-test-pkg-sv" class="headerlink" title="2.1.1 改进后的rt_test_pkg.sv"></a>2.1.1 改进后的rt_test_pkg.sv</h3><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) gen_pkts; mailbox #(rt_packet) ch_pkts[16];//从generator收到的数据按照chnl编号分发到16个mailbox里 function new(); //pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); foreach (ch_pkts[i]) ch_pkts[i] = new(1); endfunction//generator传送p给stimulatortask run(); fork drive_reset(); //reset动作 distribute_packets();//分发数据 get_packet_and_drive(); //drive_chnl动作,发送数据 join_noneendtask//分发数据task distribute_packets();//从generator收到的数据按照chnl编号分发到16个mailbox里 rt_packet p; forever begin gen_pkts.get(p);//从stimulator中的mailbox中取出数据 ch_pkts[p.src].put(p);//将数据写入分发的对应chnl的mailbox中 endendtasktask drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive(); @(negedge intf.reset_n); repeat (10)@(posedge intf.clock); foreach(ch_pkts[i])begin automatic int id = i; automatic rt_packet p; fork forever begin ch_pkts[id].get(p);//从对应chnl的mailbox中取出数据 drive_chnl(p);//drive数据 end join_none endendtasktask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtaskendclass //covergroupcovergroup rt_cg_src_chnl_to_dest_chnl (int id)with function sample(rt_packet p);//手动调用采样函数sampleoption.name = $sformatf("source channel to dest channel coverage [%0d]",id);option.per_instance = 1;//展现覆盖率的时候不合并DEST: coverpoint p.dst;endgroup//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; rt_cg_src_chnl_to_dest_chnl cg_src_chnl_to_dest_chnl[16];//例化covergroup function new( ); endfunctiontask run(); foreach(cg_src_chnl_to_dest_chnl[i]) begin cg_src_chnl_to_dest_chnl[i]= new(i); end fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts //coverage sampling采样 cg_src_chnl_to_dest_chnl[id].sample(pkt); $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 stim.gen_pkts = gen.pkts;//将generaor的句柄赋值给stimlutor,数据从gen传到stim endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 10;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction virtual task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[20:30]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 rand int ch_num; rand bit [3:0]src[]; rand bit [3:0]dst[]; rand int unsigned pkt_count = 10; constraint multi_ch_cstr { soft pkt_count inside {[5:10]}; ch_num inside {[1:16]}; src.size == ch_num; dst.size == ch_num; foreach(src[i]) src[i] inside {[0:15]}; foreach(dst[i]) dst[i] inside {[0:15]}; } constraint unique_cstr{ unique {src}; unique {dst}; }//单独声明约束,方便rt_two_ch_same_chout_test里关掉 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 foreach (src[i]) begin p = new( ); p.randomize() with {src == local::src[i]; dst == local::dst[i]; }; env.gen.put_pkt(p); end end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_multi_ch_test; constraint two_ch_cstr { ch_num == 2;//2通道测试 } function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test;//子类可以继承父类的run,此处不需要再添加,只需添加约束即可 rand bit [3:0]same_dst; constraint two_ch_same_chout_cstr{ foreach(dst[i]) dst[i] == same_dst;//保证dst_chnl相同 same_dst inside {[0:15]}; unique {src};//随机过程过程中每个src不一样 } function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); unique_cstr.constraint_mode (0);//关掉父类约束 endfunction endclass class rt_full_ch_test extends rt_multi_ch_test; constraint full_ch_cstr { ch_num == 16;//通道数量为16 } function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h3 id="2-1-2-lab11tb-sv"><a href="#2-1-2-lab11tb-sv" class="headerlink" title="2.1.2 lab11tb.sv"></a>2.1.2 lab11tb.sv</h3><pre class=" language-systemverilog"><code class="language-systemverilog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_two_ch_test two_ch_test; rt_multi_ch_test multi_ch_test; rt_two_ch_same_chout_test two_ch_same_chout_test; rt_full_ch_test full_ch_test; rt_base_test tests[string];//父类记得添加virtual,否则存放的是父类句柄,执行父类的run initial begin : Select_the_test string name; single_ch_test = new(intf); two_ch_test = new(intf); multi_ch_test = new(intf); two_ch_same_chout_test = new(intf); full_ch_test = new(intf); tests["rt_single_ch_test"] = single_ch_test; tests["rt_two_ch_test" ] = two_ch_test; tests["rt_multi_ch_test" ] = multi_ch_test ; tests["rt_two_ch_same_chout_test"] = two_ch_same_chout_test; tests["rt_full_ch_test"] = full_ch_test; if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 if(tests.exists(name)) tests[name].run();//调用对应test进行run else $fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); end end endmodule </code></pre><h2 id="2-2-查看功能覆盖率"><a href="#2-2-查看功能覆盖率" class="headerlink" title="2.2 查看功能覆盖率"></a>2.2 查看功能覆盖率</h2><p>重新编译make comp后不会删除之前代码覆盖率的database,之前代码覆盖率的database与新产生的database不匹配,因此编译前需要清理之前的database,可通过执行make clean执行(清理内容见Makefile)</p><p>进行test用例的测试</p><blockquote><p>make run TESTNAME=rt_single_ch_test SEED=3000//改变随机种子进行测试<br>make run TESTNAME=rt_single_ch_test SEED=4000<br>make run TESTNAME=rt_single_ch_test SEED=5000</p></blockquote><p>生成功能覆盖率的database文件(SEED=3000、4000、5000),执行VCS命令,打开coverage面板,查看功能覆盖率</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211242106244.png" width="100%" height="100%"></div><p>改进方法:将covergroup的例化放在run里面</p><pre class=" language-systemverilog"><code class="language-systemverilog">task run(); foreach(cg_src_chnl_to_dest_chnl[i]) begin cg_src_chnl_to_dest_chnl[i]= new(); end fork mon_chnls(); join_noneendtask</code></pre><p>之后就能看见单个test对应的16个covergroup,下图为添加SEED=3000的功能覆盖率</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211242143595.png" width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091551618.png" width="80%" height="80%"></div><p>将SEED=3000、4000、5000生成的database文件均添加进来,得到功能覆盖率如下</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091555016.png" width="80%" height="80%"></div><h1 id="3-增加test测试"><a href="#3-增加test测试" class="headerlink" title="3. 增加test测试"></a>3. 增加test测试</h1><p>之后不断增加test测试,提高覆盖率(更多的test会对覆盖率产生作用)</p><blockquote><p>make run TESTNAME=rt_single_ch_test SEED=3000<br>make run TESTNAME=rt_single_ch_test SEED=4000<br>make run TESTNAME=rt_single_ch_test SEED=5000<br>make run TESTNAME=rt_two_ch_test SEED=1000<br>make run TESTNAME=rt_two_ch_test SEED=2000<br>make run TESTNAME=rt_two_ch_test SEED=3000<br>make run TESTNAME=rt_multi_ch_test SEED=1000<br>make run TESTNAME=rt_multi_ch_test SEED=2000<br>make run TESTNAME=rt_multi_ch_test SEED=3000<br>make run TESTNAME=rt_full_ch_test SEED=1000<br>make run TESTNAME=rt_full_ch_test SEED=2000<br>make run TESTNAME=rt_full_ch_test SEED=3000</p></blockquote><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091615104.png" width="80%" height="80%"></div><h1 id="4-makefile里的命令"><a href="#4-makefile里的命令" class="headerlink" title="4. makefile里的命令"></a>4. makefile里的命令</h1><p>VCS命令:</p><blockquote><p>make editcov &</p></blockquote><p>把所有的testdata合并到一起打开</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211242157701.png" width="100%" height="100%"></div><p>VCS命令:</p><blockquote><p>make viewcov &</p></blockquote><p>把所有的testdata合并并产生一个html报告</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211242159721.png" width="100%" height="100%"></div><h1 id="5-在interface中添加covergroup"><a href="#5-在interface中添加covergroup" class="headerlink" title="5. 在interface中添加covergroup"></a>5. 在interface中添加covergroup</h1><p>在特定的时间点采样特定的信号<br>如何捕捉当前时间是否有两个chnl同时发送/接收数据(monitor无法及时检测,两个chnl的frame信号或长或短),可通过interface解决,在interface中例化covergroup</p><h2 id="5-1-改进后的lab11tb-sv"><a href="#5-1-改进后的lab11tb-sv" class="headerlink" title="5.1 改进后的lab11tb.sv"></a>5.1 改进后的lab11tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;covergroup rt_src_two_chnl_parallel_proc @(posedge clock iff $countones(valid_n) == (16 - 2));//至少有两个的chnl同时工作进行采样 PARA: coverpoint valid_n{ wildcard bins ch0 = {16'bxxxx_xxxx_xxxx_xxx0}; wildcard bins ch1 = {16'bxxxx_xxxx_xxxx_xx0x}; wildcard bins ch2 = {16'bxxxx_xxxx_xxxx_x0xx}; wildcard bins ch3 = {16'bxxxx_xxxx_xxxx_0xxx}; wildcard bins ch4 = {16'bxxxx_xxxx_xxx0_xxxx}; wildcard bins ch5 = {16'bxxxx_xxxx_xx0x_xxxx}; wildcard bins ch6 = {16'bxxxx_xxxx_x0xx_xxxx}; wildcard bins ch7 = {16'bxxxx_xxxx_0xxx_xxxx}; wildcard bins ch8 = {16'bxxxx_xxx0_xxxx_xxxx}; wildcard bins ch9 = {16'bxxxx_xx0x_xxxx_xxxx}; wildcard bins ch10 = {16'bxxxx_x0xx_xxxx_xxxx}; wildcard bins ch11 = {16'bxxxx_0xxx_xxxx_xxxx}; wildcard bins ch12 = {16'bxxx0_xxxx_xxxx_xxxx}; wildcard bins ch13 = {16'bxx0x_xxxx_xxxx_xxxx}; wildcard bins ch14 = {16'bx0xx_xxxx_xxxx_xxxx}; wildcard bins ch15 = {16'b0xxx_xxxx_xxxx_xxxx}; }endgroupinitial begin rt_src_two_chnl_parallel_proc src_two_chnl_parallel_proc = new();endcovergroup rt_dst_two_chnl_parallel_proc @(posedge clock iff $countones(valido_n) == (16 - 2));//至少有两个以上的chnl同时工作进行采样 PARA: coverpoint valido_n{ wildcard bins ch0 = {16'bxxxx_xxxx_xxxx_xxx0}; wildcard bins ch1 = {16'bxxxx_xxxx_xxxx_xx0x}; wildcard bins ch2 = {16'bxxxx_xxxx_xxxx_x0xx}; wildcard bins ch3 = {16'bxxxx_xxxx_xxxx_0xxx}; wildcard bins ch4 = {16'bxxxx_xxxx_xxx0_xxxx}; wildcard bins ch5 = {16'bxxxx_xxxx_xx0x_xxxx}; wildcard bins ch6 = {16'bxxxx_xxxx_x0xx_xxxx}; wildcard bins ch7 = {16'bxxxx_xxxx_0xxx_xxxx}; wildcard bins ch8 = {16'bxxxx_xxx0_xxxx_xxxx}; wildcard bins ch9 = {16'bxxxx_xx0x_xxxx_xxxx}; wildcard bins ch10 = {16'bxxxx_x0xx_xxxx_xxxx}; wildcard bins ch11 = {16'bxxxx_0xxx_xxxx_xxxx}; wildcard bins ch12 = {16'bxxx0_xxxx_xxxx_xxxx}; wildcard bins ch13 = {16'bxx0x_xxxx_xxxx_xxxx}; wildcard bins ch14 = {16'bx0xx_xxxx_xxxx_xxxx}; wildcard bins ch15 = {16'b0xxx_xxxx_xxxx_xxxx}; }endgroupinitial begin rt_src_two_chnl_parallel_proc src_two_chnl_parallel_proc = new(); rt_dst_two_chnl_parallel_proc dst_two_chnl_parallel_proc = new();endendinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_two_ch_test two_ch_test; rt_multi_ch_test multi_ch_test; rt_two_ch_same_chout_test two_ch_same_chout_test; rt_full_ch_test full_ch_test; rt_base_test tests[string];//父类记得添加virtual,否则存放的是父类句柄,执行父类的run initial begin : Select_the_test string name; single_ch_test = new(intf); two_ch_test = new(intf); multi_ch_test = new(intf); two_ch_same_chout_test = new(intf); full_ch_test = new(intf); tests["rt_single_ch_test"] = single_ch_test; tests["rt_two_ch_test" ] = two_ch_test; tests["rt_multi_ch_test" ] = multi_ch_test ; tests["rt_two_ch_same_chout_test"] = two_ch_same_chout_test; tests["rt_full_ch_test"] = full_ch_test; if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 if(tests.exists(name)) tests[name].run();//调用对应test进行run else $fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); end end endmodule </code></pre><h2 id="5-2-查看覆盖率"><a href="#5-2-查看覆盖率" class="headerlink" title="5.2 查看覆盖率"></a>5.2 查看覆盖率</h2><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091720500.png" width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091718619.png" width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212091721999.png" width="80%" height="80%"></div>]]></content>
<summary type="html"><h1 id="1-查看代码覆盖率"><a href="#1-查看代码覆盖率" class="headerlink" title="1. 查看代码覆盖率"></a>1. 查看代码覆盖率</h1><h2 id="1-1-修改Makefile"><a href="#1-1-修改Mak</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>SystemVerilog课程笔记(十)</title>
<link href="https://www.daipeihong.top/2022/11/23/systemverilog-ke-cheng-bi-ji-shi/"/>
<id>https://www.daipeihong.top/2022/11/23/systemverilog-ke-cheng-bi-ji-shi/</id>
<published>2022-11-23T03:39:00.000Z</published>
<updated>2022-11-23T14:22:32.366Z</updated>
<content type="html"><![CDATA[<h1 id="1-从generator收到的数据按照chnl编号分发到16个mailbox里"><a href="#1-从generator收到的数据按照chnl编号分发到16个mailbox里" class="headerlink" title="1. 从generator收到的数据按照chnl编号分发到16个mailbox里"></a>1. 从generator收到的数据按照chnl编号分发到16个mailbox里</h1><h2 id="1-1-改进后的rt-tst-pkg-sv"><a href="#1-1-改进后的rt-tst-pkg-sv" class="headerlink" title="1.1 改进后的rt_tst_pkg.sv"></a>1.1 改进后的rt_tst_pkg.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) gen_pkts; mailbox #(rt_packet) ch_pkts[16];//从generator收到的数据按照chnl编号分发到16个mailbox里 function new(); //pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); foreach (ch_pkts[i]) ch_pkts[i] = new(1); endfunction//generator传送p给stimulatortask run(); fork drive_reset(); //reset动作 distribute_packets();//分发数据 get_packet_and_drive(); //drive_chnl动作,发送数据 join_noneendtask//分发数据task distribute_packets();//从generator收到的数据按照chnl编号分发到16个mailbox里 rt_packet p; forever begin gen_pkts.get(p);//从stimulator中的mailbox中取出数据 ch_pkts[p.src].put(p);//将数据写入分发的对应chnl的mailbox中 endendtasktask drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive(); @(negedge intf.reset_n); repeat (10)@(posedge intf.clock); foreach(ch_pkts[i])begin automatic int id = i; automatic rt_packet p; fork forever begin ch_pkts[id].get(p);//从对应chnl的mailbox中取出数据 drive_chnl(p);//drive数据 end join_none endendtasktask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 stim.gen_pkts = gen.pkts;//将generaor的句柄赋值给stimlutor,数据从gen传到stim endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 200;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction virtual task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[20:30]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; rand bit signed [4:0] src[2]; rand bit signed [4:0] dst[2]; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[5:10]}; foreach (src[i]) src[i] inside {[0 : 15]}; foreach (src[i]) dst[i] inside {[0 : 15]}; unique {src};//随机过程过程中每个src、dst不一样 unique {dst}; } function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 foreach (src[i]) begin p = new( ); p.randomize() with {src == local::src[i]; dst == local::dst[i]; }; env.gen.put_pkt(p); end end set_trans_done(); endtask endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="1-2-改进后的lab10tb-sv"><a href="#1-2-改进后的lab10tb-sv" class="headerlink" title="1.2 改进后的lab10tb.sv"></a>1.2 改进后的lab10tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_two_ch_test two_ch_test; rt_multi_ch_test multi_ch_test; rt_base_test tests[string];//父类记得添加virtual,否则存放的是父类句柄,执行父类的run initial begin : Select_the_test string name; single_ch_test = new(intf); two_ch_test = new(intf); multi_ch_test = new(intf); tests["rt_single_ch_test"] = single_ch_test; tests["rt_two_ch_test" ] = two_ch_test; tests["rt_multi_ch_test" ] = multi_ch_test ; if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 if(tests.exists(name)) tests[name].run();//调用对应test进行run else $fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); end end endmodule </code></pre><h2 id="1-3-仿真结果"><a href="#1-3-仿真结果" class="headerlink" title="1.3 仿真结果"></a>1.3 仿真结果</h2><p>执行two_ch_test命令:</p><blockquote><p>make rung TESTNAME=rt_two_ch_test</p></blockquote><p><span style="background:rgba(240, 200, 0, 0.2)">结果没有交替发送的情况</span></p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211231510365.png" width="100%" height="100%"></div><h1 id="2-完善two-ch-same-chout-test、full-ch-test"><a href="#2-完善two-ch-same-chout-test、full-ch-test" class="headerlink" title="2. 完善two_ch_same_chout_test、full_ch_test"></a>2. 完善two_ch_same_chout_test、full_ch_test</h1><h2 id="2-1-改进后的rt-pkg-sv"><a href="#2-1-改进后的rt-pkg-sv" class="headerlink" title="2.1 改进后的rt_pkg.sv"></a>2.1 改进后的rt_pkg.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) gen_pkts; mailbox #(rt_packet) ch_pkts[16];//从generator收到的数据按照chnl编号分发到16个mailbox里 function new(); //pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); foreach (ch_pkts[i]) ch_pkts[i] = new(1); endfunction//generator传送p给stimulatortask run(); fork drive_reset(); //reset动作 distribute_packets();//分发数据 get_packet_and_drive(); //drive_chnl动作,发送数据 join_noneendtask//分发数据task distribute_packets();//从generator收到的数据按照chnl编号分发到16个mailbox里 rt_packet p; forever begin gen_pkts.get(p);//从stimulator中的mailbox中取出数据 ch_pkts[p.src].put(p);//将数据写入分发的对应chnl的mailbox中 endendtasktask drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive(); @(negedge intf.reset_n); repeat (10)@(posedge intf.clock); foreach(ch_pkts[i])begin automatic int id = i; automatic rt_packet p; fork forever begin ch_pkts[id].get(p);//从对应chnl的mailbox中取出数据 drive_chnl(p);//drive数据 end join_none endendtasktask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 stim.gen_pkts = gen.pkts;//将generaor的句柄赋值给stimlutor,数据从gen传到stim endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 200;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction virtual task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[20:30]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; rand bit signed [3:0] src[2]; rand bit signed [3:0] dst[2]; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[5:10]}; foreach (src[i]) src[i] inside {[0 : 15]}; foreach (src[i]) dst[i] inside {[0 : 15]}; unique {src};//随机过程过程中每个src、dst不一样 unique {dst}; } function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 foreach (src[i]) begin p = new( ); p.randomize() with {src == local::src[i]; dst == local::dst[i]; }; env.gen.put_pkt(p); end end set_trans_done(); endtask endclass class rt_two_ch_same_chout_test extends rt_two_ch_test;//子类可以继承父类的run,此处不需要再添加,只需添加约束即可 rand bit [3:0]same_dst; constraint two_ch_same_chout_cstr{ foreach(dst[i]) dst[i] == same_dst;//保证dst_chnl相同 same_dst inside {[0:15]}; } function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="2-2-改进后的lab10tb-sv"><a href="#2-2-改进后的lab10tb-sv" class="headerlink" title="2.2 改进后的lab10tb.sv"></a>2.2 改进后的lab10tb.sv</h2><pre class=" language-systemveirlog"><code class="language-systemveirlog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_two_ch_test two_ch_test; rt_multi_ch_test multi_ch_test; rt_two_ch_same_chout_test two_ch_same_chout_test; rt_full_ch_test full_ch_test; rt_base_test tests[string];//父类记得添加virtual,否则存放的是父类句柄,执行父类的run initial begin : Select_the_test string name; single_ch_test = new(intf); two_ch_test = new(intf); multi_ch_test = new(intf); two_ch_same_chout_test = new(intf); full_ch_test = new(intf); tests["rt_single_ch_test"] = single_ch_test; tests["rt_two_ch_test" ] = two_ch_test; tests["rt_multi_ch_test" ] = multi_ch_test ; tests["rt_two_ch_same_chout_test"] = two_ch_same_chout_test; tests["rt_full_ch_test "] = full_ch_test; if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 if(tests.exists(name)) tests[name].run();//调用对应test进行run else $fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); end end endmodule </code></pre><h2 id="2-3-仿真结果"><a href="#2-3-仿真结果" class="headerlink" title="2.3 仿真结果"></a>2.3 仿真结果</h2><p>进行相同dst_chnl通道测试,执行命令:</p><blockquote><p>make rung TESTNAME=rt_two_ch_same_chout_test &</p></blockquote><p>randmize失败(所有src、dst均为0),父类与子类的约束冲突</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211231541927.png" width="80%" height="80%"></div><h2 id="2-4-解决方法"><a href="#2-4-解决方法" class="headerlink" title="2.4 解决方法"></a>2.4 解决方法</h2><p>关掉父类约束,部分代码如下:</p><pre class=" language-systemverilog"><code class="language-systemverilog"> ...... class rt_two_ch_test extends rt_base_test; rand bit signed [3:0] src[2]; rand bit signed [3:0] dst[2]; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[5:10]}; foreach (src[i]) src[i] inside {[0 : 15]}; foreach (src[i]) dst[i] inside {[0 : 15]}; unique {src};//随机过程过程中每个src、dst不一样 unique {dst}; } function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 foreach (src[i]) begin p = new( ); p.randomize() with {src == local::src[i]; dst == local::dst[i]; }; env.gen.put_pkt(p); end end set_trans_done(); endtask endclass class rt_two_ch_same_chout_test extends rt_two_ch_test;//子类可以继承父类的run,此处不需要再添加,只需添加约束即可 rand bit [3:0]same_dst; constraint two_ch_same_chout_cstr{ foreach(dst[i]) dst[i] == same_dst;//保证dst_chnl相同 same_dst inside {[0:15]}; soft pkt_count inside {[5:10]}; unique {src};//随机过程过程中每个src不一样 } function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); super.test_cstr.constraint_mode (0);//关掉父类约束 endfunction endclas ......</code></pre><h2 id="2-5-仿真结果"><a href="#2-5-仿真结果" class="headerlink" title="2.5 仿真结果"></a>2.5 仿真结果</h2><p>进行相同dst_chnl通道测试,执行命令:</p><blockquote><p>make rung TESTNAME=rt_two_ch_same_chout_test &</p></blockquote><blockquote><p>TOTAL COMPARING 5 times<br>TEST [rt_two_ch_same_chout_test]FAILED!<br>TOTAL ERROR 5 times<br>$finish called from file “rt_test_pkg.sv”, line 370.<br>$finish at simulation time 210765000<br>Simulation complete, time is 210765000 ps.</p></blockquote><p>可以实现约束(12和3通道同时发往0通道),但是checker会比较错误,因为DUT不支持同时像dst chnl发送数据</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211231617389.png" width="100%" height="100%"></div><h1 id="3-完善各test父类与子类的关系"><a href="#3-完善各test父类与子类的关系" class="headerlink" title="3. 完善各test父类与子类的关系"></a>3. 完善各test父类与子类的关系</h1><h2 id="3-1-各test父类子类结构图"><a href="#3-1-各test父类子类结构图" class="headerlink" title="3.1 各test父类子类结构图"></a>3.1 各test父类子类结构图</h2><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211232222901.png " width="60%" height="60%"></div><h2 id="3-2-改进后的rt-test-pkg-sv"><a href="#3-2-改进后的rt-test-pkg-sv" class="headerlink" title="3.2 改进后的rt_test_pkg.sv"></a>3.2 改进后的rt_test_pkg.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) gen_pkts; mailbox #(rt_packet) ch_pkts[16];//从generator收到的数据按照chnl编号分发到16个mailbox里 function new(); //pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); foreach (ch_pkts[i]) ch_pkts[i] = new(1); endfunction//generator传送p给stimulatortask run(); fork drive_reset(); //reset动作 distribute_packets();//分发数据 get_packet_and_drive(); //drive_chnl动作,发送数据 join_noneendtask//分发数据task distribute_packets();//从generator收到的数据按照chnl编号分发到16个mailbox里 rt_packet p; forever begin gen_pkts.get(p);//从stimulator中的mailbox中取出数据 ch_pkts[p.src].put(p);//将数据写入分发的对应chnl的mailbox中 endendtasktask drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive(); @(negedge intf.reset_n); repeat (10)@(posedge intf.clock); foreach(ch_pkts[i])begin automatic int id = i; automatic rt_packet p; fork forever begin ch_pkts[id].get(p);//从对应chnl的mailbox中取出数据 drive_chnl(p);//drive数据 end join_none endendtasktask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 stim.gen_pkts = gen.pkts;//将generaor的句柄赋值给stimlutor,数据从gen传到stim endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 200;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction virtual task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[20:30]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 rand bit [3:0]ch_num; rand bit [3:0]src[]; rand bit [3:0]dst[]; rand int unsigned pkt_count = 10; constraint multi_ch_cstr { soft pkt_count inside {[5:10]}; src.size == ch_num; dst.size == ch_num; foreach(src[i]) src[i] inside {[0:15]}; foreach(dst[i]) dst[i] inside {[0:15]}; } constraint unique_cstr{ unique {src}; unique {dst}; }//单独声明约束,方便rt_two_ch_same_chout_test里关掉 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 foreach (src[i]) begin p = new( ); p.randomize() with {src == local::src[i]; dst == local::dst[i]; }; env.gen.put_pkt(p); end end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_multi_ch_test; constraint two_ch_cstr { ch_num == 2;//2通道测试 } function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test;//子类可以继承父类的run,此处不需要再添加,只需添加约束即可 rand bit [3:0]same_dst; constraint two_ch_same_chout_cstr{ foreach(dst[i]) dst[i] == same_dst;//保证dst_chnl相同 same_dst inside {[0:15]}; unique {src};//随机过程过程中每个src不一样 } function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); unique_cstr.constraint_mode (0);//关掉父类约束 endfunction endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="3-3-仿真结果"><a href="#3-3-仿真结果" class="headerlink" title="3.3 仿真结果"></a>3.3 仿真结果</h2><p>进行相同dst_chnl通道测试,执行命令:</p><blockquote><p>make rung TESTNAME=rt_two_ch_same_chout_test &</p></blockquote><p>结果同上</p><h1 id="4-完善rt-full-ch-test"><a href="#4-完善rt-full-ch-test" class="headerlink" title="4. 完善rt_full_ch_test"></a>4. 完善rt_full_ch_test</h1><h2 id="4-1-Makefile"><a href="#4-1-Makefile" class="headerlink" title="4.1 Makefile"></a>4.1 Makefile</h2><pre class=" language-c"><code class="language-c">###########################<span class="token macro property"># User variables</span>###########################TB <span class="token operator">=</span> tbSEED <span class="token operator">=</span> <span class="token number">1</span>TESTNAME <span class="token operator">?</span><span class="token operator">=</span> rt_single_ch_testFILES <span class="token operator">=</span> router<span class="token punctuation">.</span>v rt_test_pkg<span class="token punctuation">.</span>sv lab8tb<span class="token punctuation">.</span>sv #User Defination###########################<span class="token macro property"># Environment variables</span>###########################COMP <span class="token operator">=</span> vcs <span class="token operator">-</span>full64 <span class="token operator">-</span>sverilog <span class="token operator">-</span>debug_access<span class="token operator">+</span>all <span class="token operator">-</span>timescale<span class="token operator">=</span>1ns<span class="token operator">/</span>1ps <span class="token operator">-</span>l comp<span class="token punctuation">.</span>log $<span class="token punctuation">(</span>FILES<span class="token punctuation">)</span>RUN <span class="token operator">=</span> <span class="token punctuation">.</span><span class="token operator">/</span>$<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simv <span class="token operator">-</span>l run<span class="token punctuation">.</span>log <span class="token operator">-</span>sml <span class="token operator">+</span>ntb_random_seed<span class="token operator">=</span>$<span class="token punctuation">(</span>SEED<span class="token punctuation">)</span> <span class="token operator">+</span>TESTNAME<span class="token operator">=</span>$<span class="token punctuation">(</span>TESTNAME<span class="token punctuation">)</span>comp<span class="token punctuation">:</span> $<span class="token punctuation">(</span>COMP<span class="token punctuation">)</span> <span class="token operator">-</span>top $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span> <span class="token operator">-</span>o $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simvrun<span class="token punctuation">:</span> $<span class="token punctuation">(</span>RUN<span class="token punctuation">)</span>rung<span class="token punctuation">:</span> $<span class="token punctuation">(</span>RUN<span class="token punctuation">)</span> <span class="token operator">-</span>gui</code></pre><h2 id="4-2-lab10tb-sv"><a href="#4-2-lab10tb-sv" class="headerlink" title="4.2 lab10tb.sv"></a>4.2 lab10tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_two_ch_test two_ch_test; rt_multi_ch_test multi_ch_test; rt_two_ch_same_chout_test two_ch_same_chout_test; rt_full_ch_test full_ch_test; rt_base_test tests[string];//父类记得添加virtual,否则存放的是父类句柄,执行父类的run initial begin : Select_the_test string name; single_ch_test = new(intf); two_ch_test = new(intf); multi_ch_test = new(intf); two_ch_same_chout_test = new(intf); full_ch_test = new(intf); tests["rt_single_ch_test"] = single_ch_test; tests["rt_two_ch_test" ] = two_ch_test; tests["rt_multi_ch_test" ] = multi_ch_test ; tests["rt_two_ch_same_chout_test"] = two_ch_same_chout_test; tests["rt_full_ch_test"] = full_ch_test; if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 if(tests.exists(name)) tests[name].run();//调用对应test进行run else $fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); end end endmodule </code></pre><h2 id="4-3-rt-test-pkg-sv"><a href="#4-3-rt-test-pkg-sv" class="headerlink" title="4.3 rt_test_pkg.sv"></a>4.3 rt_test_pkg.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) gen_pkts; mailbox #(rt_packet) ch_pkts[16];//从generator收到的数据按照chnl编号分发到16个mailbox里 function new(); //pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); foreach (ch_pkts[i]) ch_pkts[i] = new(1); endfunction//generator传送p给stimulatortask run(); fork drive_reset(); //reset动作 distribute_packets();//分发数据 get_packet_and_drive(); //drive_chnl动作,发送数据 join_noneendtask//分发数据task distribute_packets();//从generator收到的数据按照chnl编号分发到16个mailbox里 rt_packet p; forever begin gen_pkts.get(p);//从stimulator中的mailbox中取出数据 ch_pkts[p.src].put(p);//将数据写入分发的对应chnl的mailbox中 endendtasktask drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive(); @(negedge intf.reset_n); repeat (10)@(posedge intf.clock); foreach(ch_pkts[i])begin automatic int id = i; automatic rt_packet p; fork forever begin ch_pkts[id].get(p);//从对应chnl的mailbox中取出数据 drive_chnl(p);//drive数据 end join_none endendtasktask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 stim.gen_pkts = gen.pkts;//将generaor的句柄赋值给stimlutor,数据从gen传到stim endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 10;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction virtual task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[20:30]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 rand int ch_num; rand bit [3:0]src[]; rand bit [3:0]dst[]; rand int unsigned pkt_count = 10; constraint multi_ch_cstr { soft pkt_count inside {[5:10]}; ch_num inside {[1:16]}; src.size == ch_num; dst.size == ch_num; foreach(src[i]) src[i] inside {[0:15]}; foreach(dst[i]) dst[i] inside {[0:15]}; } constraint unique_cstr{ unique {src}; unique {dst}; }//单独声明约束,方便rt_two_ch_same_chout_test里关掉 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 foreach (src[i]) begin p = new( ); p.randomize() with {src == local::src[i]; dst == local::dst[i]; }; env.gen.put_pkt(p); end end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_multi_ch_test; constraint two_ch_cstr { ch_num == 2;//2通道测试 } function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test;//子类可以继承父类的run,此处不需要再添加,只需添加约束即可 rand bit [3:0]same_dst; constraint two_ch_same_chout_cstr{ foreach(dst[i]) dst[i] == same_dst;//保证dst_chnl相同 same_dst inside {[0:15]}; unique {src};//随机过程过程中每个src不一样 } function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); unique_cstr.constraint_mode (0);//关掉父类约束 endfunction endclass class rt_full_ch_test extends rt_multi_ch_test; constraint full_ch_cstr { ch_num == 16;//通道数量为16 } function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="4-4-仿真结果"><a href="#4-4-仿真结果" class="headerlink" title="4.4 仿真结果"></a>4.4 仿真结果</h2><p>执行命令</p><blockquote><p>make rung TESTNAME=rt_full_ch_test &</p></blockquote><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211231658181.png " width="100%" height="100%"></div>]]></content>
<summary type="html"><h1 id="1-从generator收到的数据按照chnl编号分发到16个mailbox里"><a href="#1-从generator收到的数据按照chnl编号分发到16个mailbox里" class="headerlink" title="1. 从generator收</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>SystemVerilog课程笔记(九)</title>
<link href="https://www.daipeihong.top/2022/11/22/systemverilog-ke-cheng-bi-ji-jiu/"/>
<id>https://www.daipeihong.top/2022/11/22/systemverilog-ke-cheng-bi-ji-jiu/</id>
<published>2022-11-22T12:56:00.000Z</published>
<updated>2022-11-23T14:25:10.603Z</updated>
<content type="html"><![CDATA[<h1 id="1-将generator、monitor中的队列改为mailbox"><a href="#1-将generator、monitor中的队列改为mailbox" class="headerlink" title="1. 将generator、monitor中的队列改为mailbox"></a>1. 将generator、monitor中的队列改为mailbox</h1><blockquote><p>mailbox #(rt_packet) pkts;</p></blockquote><h1 id="2-将generator、monitor中的mailbox设置上限为1"><a href="#2-将generator、monitor中的mailbox设置上限为1" class="headerlink" title="2. 将generator、monitor中的mailbox设置上限为1"></a>2. 将generator、monitor中的mailbox设置上限为1</h1><h2 id="2-1-改造后的rt-test-pkg"><a href="#2-1-改造后的rt-test-pkg" class="headerlink" title="2.1 改造后的rt_test_pkg"></a>2.1 改造后的rt_test_pkg</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) pkts; function new(); pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); endfunction//generator传送p给stimulator task put_pkt(input rt_packet p); pkts.put(p); endtasktask run(); fork drive_reset(); //reset动作 get_packet_and_drive(); //drive_chnl动作 join_noneendtask task drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive();//drive_chnl @(negedge intf.reset_n); repeat(10) @(posedge intf.clock);//延迟10个时钟周期 forever begin automatic rt_packet p;//声明一个动态的 pkts.get(p); fork//后台触发线程,触发线程在后台运行,继续执行剩下内容 begin wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待 drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据 set_src_chnl_avail(p); end join //join_none endendtasktask wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待 src_chnl_status[p.src].get(1);//拿到钥匙endtaskfunction set_src_chnl_avail(rt_packet p); src_chnl_status[p.src].put(1);//还钥匙endfunctiontask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator forever begin//此处不需要延迟0,执行task run的时候默认function new已执行 gen.get_pkt(p);//get句柄 stim.put_pkt(p);//put句柄 end end join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 200;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[1:20]}; pkt_count == 1000; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="2-2-仿真结果"><a href="#2-2-仿真结果" class="headerlink" title="2.2 仿真结果"></a>2.2 仿真结果</h2><blockquote><p>TOTAL COMPARING1000 timesTEST [rt_single_ch_test] PASSED!<br>$finish called from file “rt_test_pkg.sv”, line 377.<br>$finish at simulation time<br>1647385000<br>Simulation complete, time is 1647385000 ps.</p></blockquote><h1 id="3-将数据从genarator到stimulator的传递改为mailbox句柄的传递"><a href="#3-将数据从genarator到stimulator的传递改为mailbox句柄的传递" class="headerlink" title="3. 将数据从genarator到stimulator的传递改为mailbox句柄的传递"></a>3. 将数据从genarator到stimulator的传递改为mailbox句柄的传递</h1><h2 id="3-1-改造"><a href="#3-1-改造" class="headerlink" title="3.1 改造"></a>3.1 改造</h2><pre class=" language-systemverilog"><code class="language-systemverilog">begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator forever begin//此处不需要延迟0,执行task run的时候默认function new已执行 gen.get_pkt(p);//get句柄 stim.put_pkt(p);//put句柄 end</code></pre><p>将数据从genarator到stimulator的传递改为mailbox句柄的传递</p><pre class=" language-systemverilog"><code class="language-systemverilog">stim.pkts = gen.pkts;//将generaor的句柄赋值给stimlutor</code></pre><h2 id="3-2-改进后的lab9tb-sv"><a href="#3-2-改进后的lab9tb-sv" class="headerlink" title="3.2 改进后的lab9tb.sv"></a>3.2 改进后的lab9tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_multi_ch_test multi_ch_test; initial begin : Select_the_test string name; single_ch_test = new(intf); multi_ch_test = new(intf); //single_ch_test.run(); //multi_ch_test.run(); if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 case(name) "rt_single_ch_test" : single_ch_test.run();//输入rt_single_ch_test,调用对应run,执行输入rt_single_ch_test "rt_multi_ch_test" :multi_ch_test.run(); default:$fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); endcase end end endmodule </code></pre><h2 id="3-3-改进后的rt-test-pkg"><a href="#3-3-改进后的rt-test-pkg" class="headerlink" title="3.3 改进后的rt_test_pkg"></a>3.3 改进后的rt_test_pkg</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) pkts; function new(); //pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); endfunction//generator传送p给stimulator task put_pkt(input rt_packet p); pkts.put(p); endtasktask run(); fork drive_reset(); //reset动作 get_packet_and_drive(); //drive_chnl动作 join_noneendtask task drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive();//drive_chnl @(negedge intf.reset_n); repeat(10) @(posedge intf.clock);//延迟10个时钟周期 forever begin automatic rt_packet p;//声明一个动态的 pkts.get(p); fork//后台触发线程,触发线程在后台运行,继续执行剩下内容 begin wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待 drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据 set_src_chnl_avail(p); end join //join_none endendtasktask wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待 src_chnl_status[p.src].get(1);//拿到钥匙endtaskfunction set_src_chnl_avail(rt_packet p); src_chnl_status[p.src].put(1);//还钥匙endfunctiontask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 stim.pkts = gen.pkts; endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 200;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[20:30]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="3-4-仿真结果"><a href="#3-4-仿真结果" class="headerlink" title="3.4 仿真结果"></a>3.4 仿真结果</h2><p>同上</p><h1 id="4-使用关联数组进行test用例的选择"><a href="#4-使用关联数组进行test用例的选择" class="headerlink" title="4. 使用关联数组进行test用例的选择"></a>4. 使用关联数组进行test用例的选择</h1><h2 id="4-1改造"><a href="#4-1改造" class="headerlink" title="4.1改造"></a>4.1改造</h2><pre class=" language-systemverilog"><code class="language-systemverilog">rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_two_ch_test two_ch_test; rt_multi_ch_test multi_ch_test; rt_base_test tests[string];//父类记得添加virtual,否则存放的是父类句柄,执行父类的run initial begin : Select_the_test string name; single_ch_test = new(intf); two_ch_test = new(intf); multi_ch_test = new(intf); tests["rt_single_ch_test"] = single_ch_test; tests["rt_two_ch_test" ] = two_ch_test; tests["rt_multi_ch_test" ] = multi_ch_test ; if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 if(tests.exists(name)) tests[name].run();//调用对应test进行run else $fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); end end</code></pre><h2 id="4-2-虚方法的使用"><a href="#4-2-虚方法的使用" class="headerlink" title="4.2 虚方法的使用"></a>4.2 虚方法的使用</h2><p>注意,要在父类的run任务里面<span style="background:rgba(240, 200, 0, 0.2)">添加virtual,优先调用子类里面的run</span><br>虚方法的使用详见<a href="http://daipeihong.top/2022/11/06/systemverilog-xue-xi-bi-ji-jiu/">SystemVerilog课程笔记(九)#4.2 虚方法的使用</a></p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211230951057.png" width="100%" height="100%"></div><h2 id="4-3-改造后的lab9tb-sv"><a href="#4-3-改造后的lab9tb-sv" class="headerlink" title="4.3 改造后的lab9tb.sv"></a>4.3 改造后的lab9tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_two_ch_test two_ch_test; rt_multi_ch_test multi_ch_test; rt_base_test tests[string];//父类记得添加virtual,否则存放的是父类句柄,执行父类的run initial begin : Select_the_test string name; single_ch_test = new(intf); two_ch_test = new(intf); multi_ch_test = new(intf); tests["rt_single_ch_test"] = single_ch_test; tests["rt_two_ch_test" ] = two_ch_test; tests["rt_multi_ch_test" ] = multi_ch_test ; if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 if(tests.exists(name)) tests[name].run();//调用对应test进行run else $fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); end end endmodule </code></pre><h2 id="4-4-改造后的rt-test-pkg"><a href="#4-4-改造后的rt-test-pkg" class="headerlink" title="4.4 改造后的rt_test_pkg"></a>4.4 改造后的rt_test_pkg</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; semaphore src_chnl_status[16];//16个chnl的钥匙 mailbox #(rt_packet) pkts; function new(); //pkts = new(1);//设定mailbox上限为1 foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); endfunction//generator传送p给stimulator task put_pkt(input rt_packet p); pkts.put(p); endtasktask run(); fork drive_reset(); //reset动作 get_packet_and_drive(); //drive_chnl动作 join_noneendtask task drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive();//drive_chnl @(negedge intf.reset_n); repeat(10) @(posedge intf.clock);//延迟10个时钟周期 forever begin automatic rt_packet p;//声明一个动态的 pkts.get(p); fork//后台触发线程,触发线程在后台运行,继续执行剩下内容 begin wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待 drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据 set_src_chnl_avail(p); end join //join_none endendtasktask wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待 src_chnl_status[p.src].get(1);//拿到钥匙endtaskfunction set_src_chnl_avail(rt_packet p); src_chnl_status[p.src].put(1);//还钥匙endfunctiontask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator mailbox #(rt_packet) pkts;//定义队列 function new(); pkts = new(1);//设定mailbox上限为1 endfunction task put_pkt(input rt_packet p); pkts.put(p); endtask task get_pkt(output rt_packet p); pkts.get(p); endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 stim.pkts = gen.pkts; endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 200;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction virtual task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[20:30]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="4-5-仿真结果"><a href="#4-5-仿真结果" class="headerlink" title="4.5 仿真结果"></a>4.5 仿真结果</h2><p>执行two_ch_test命令:</p><blockquote><p>make rung TESTNAME=rt_two_ch_test</p></blockquote><p>同上,但存在交替发送的问题</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211231139467.png" width="100%" height="100%"></div>]]></content>
<summary type="html"><h1 id="1-将generator、monitor中的队列改为mailbox"><a href="#1-将generator、monitor中的队列改为mailbox" class="headerlink" title="1. 将generator、monitor中的队列改</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>使用PLI接口实现故障注入</title>
<link href="https://www.daipeihong.top/2022/11/21/shi-yong-pli-jie-kou-shi-xian-gu-zhang-zhu-ru/"/>
<id>https://www.daipeihong.top/2022/11/21/shi-yong-pli-jie-kou-shi-xian-gu-zhang-zhu-ru/</id>
<published>2022-11-21T08:13:00.000Z</published>
<updated>2022-12-10T08:13:31.597Z</updated>
<content type="html"><然后在Verilog HDL 源文件中调用系统任务<div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212051944032.png" width="50%" height="50%"></div>最后使用VCS 仿真器编译Verilog PLI 源代码,生成一个名为simv的可执行程序,运行simv 程序进行对硬件设计的仿真。<div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212051945131.png" width="50%" height="50%"></div><h1 id="2-如何利用Verilog-PLI实现故障注入"><a href="#2-如何利用Verilog-PLI实现故障注入" class="headerlink" title="2. 如何利用Verilog PLI实现故障注入"></a>2. 如何利用Verilog PLI实现故障注入</h1><h2 id="2-1-基于Verilog-PLI-技术的故障注入工具VPFIT"><a href="#2-1-基于Verilog-PLI-技术的故障注入工具VPFIT" class="headerlink" title="2.1 基于Verilog PLI 技术的故障注入工具VPFIT"></a>2.1 基于Verilog PLI 技术的故障注入工具VPFIT</h2><p><strong>VPFIT</strong> :一个系统的故障注入工具,包含配置、控制、监视和数据分析等不同的功能模块<br><strong>关键</strong>:<span style="background:rgba(240, 200, 0, 0.2)">基于PLI 技术的对Verilog HDL 描述的系统模型进行故障注入,故障注入过程主要由PLI 系 </span><br><span style="background:rgba(240, 200, 0, 0.2)">统任务完成</span>。VPFIT 工具的实现主要包括四个PLI 系统任务:Test_Exectime 任务、Inject_Transfault 任务、Inject_Intermfault 任务和Inject_Permfault 任务。主要使用了misctf 例程的回调机制来实现四个基本的系统任务。</p><p>基于Verilog PLI 技术的故障注入工具VPFIT 针对的对象为使用Verilog 硬件描述语言构造的目标系统模拟模型,采用的是PLI 技术,VPFIT 的总体框架主要包括用户配置模块、RTL 源码及Testbench 模块、Verilog PLI 库、Verilog仿真器以及故障注入结果分析与数据统计模块。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212052113715.png" width="90%" height="90%"></div><p><strong>用户配置文件</strong>:主要用于配置故障注入实验的参数,包括故障注入目标模块的选择、故障注入次数、故障持续时间区间选择、工作负载选择和无故障工作负载运行时间等。<br><strong>Verilog RTL源码</strong>:故障注入目标系统HDL源代码。<br><strong>Testbench</strong>:是针对目标系统编写的顶层模块,主要完成的任务是对故障注入系统产生负载激励。<br><strong>Verilog PLI 库</strong>:包括<u>系统PLI库</u>和<u>用户自定义PLI库</u>。系统PLI库提供了一组标准的系统任务和函数,在硬件描述的模块中可以直接使用,除此还包括TF实用库函数和ACC 访问库函数,供自定义的PLI应用的相关子例程调用实现用户自定义系统任务。用户自定义PLI 库是包含PLI应用例程的C语言源代码经过编译后生成的库文件,以动态链接库的形式存放在系统库目录下。</p><h2 id="2-2-故障注入的实现"><a href="#2-2-故障注入的实现" class="headerlink" title="2.2 故障注入的实现"></a>2.2 故障注入的实现</h2><p>VPFIT 工具的实现主要包括四个PLI 系统任务:</p><ol><li>Test_Exectime 任务:测试未注入故障时工作负载的执行时间</li><li>Inject_Transfault 任务:完成瞬时故障的注入</li><li>Inject_Intermfault 任务:完成间歇故障的注入</li><li>Inject_Permfault 任务:完成永久故障的注入<br>主要使用了misctf 例程的回调机制来实现四个基本的系统任务。</li></ol><h3 id="2-2-1-misctf-例程回调"><a href="#2-2-1-misctf-例程回调" class="headerlink" title="2.2.1 misctf 例程回调"></a>2.2.1 misctf 例程回调</h3><p>四个任务都使用了Misctf 例程的回调机制,Misctf 例程由各种仿真事件或 PLI 应用例程调度事件驱动,完成仿真事件的自动回调或 PLI 应用调度事件的回调。当 misctf 任务被调用时,仿真器传递参数reason 常量,<u>misctf 例程被调用时由reason 常量得到回调原因,判断事件类型,然后执行相应的操作。</u></p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212061440720.png" width="60%" height="60%"></div>Misctf 例程的回调可以分为两类:* 基于仿真事件的自动回调Misctf 例程由于仿真过程中各种仿真事件驱动而自动调用,通过这种对misctf例程的回调过程来处理各种特殊的操作。Misctf 例程可以由各种原因自动调用,misctf 例程通过检查常量输入参数reason,只需处理PLI 应用相关事件的回调即可。* 基于PLI 应用例程调度事件的回调TF 库提供了一些库函数,使PLI 应用可以调度它的misctf 例程在未来的仿真时间被调用,当由这三个函数调度的misctf 例程被激活时,仿真器将向misctf 例程传递一个reason参数常量值REASON_REACTIVATE。<div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212061500681.png" width="80%" height="80%"></div><h3 id="2-2-2-TestExectime-任务"><a href="#2-2-2-TestExectime-任务" class="headerlink" title="2.2.2 TestExectime 任务"></a>2.2.2 TestExectime 任务</h3><p>定义 PLI 任务 Test_Exectime,主要目的是测试未注入故障时工作负载的执行时间Tworkload</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212061056873.png" width="60%" height="60%"></div>当 REASON_ENDOFCOMPILE 事件发生时,在 State_flag=0状态,调用库函数 tf_gettime()获得当前仿真时间;当 REASON_FINISH 事件发生时,在END 状态,同样调用库函数tf_gettime()获得当前仿真时间,通过两个不同状态获得的时间差就可得到未注入故障时工作负载的执行时间Tworkload。### 2.2.3 InjectTransfault 任务定义 PLI 任务 Inject_Transfault,主要功能是完成<span style="background:rgba(240, 200, 0, 0.2)">瞬时故障</span>的注入<div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212061250259.png" width="80%" height="80%"></div><h3 id="2-2-4-InjectIntermfault-任务"><a href="#2-2-4-InjectIntermfault-任务" class="headerlink" title="2.2.4 InjectIntermfault 任务"></a>2.2.4 InjectIntermfault 任务</h3><p>定义PLI 任务Inject_Intermfault,主要功能是完成<span style="background:rgba(240, 200, 0, 0.2)">间歇故障</span>的注入</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212061254169.png" width="80%" height="80%"></div><h3 id="2-2-5-InjectPermfault-任务"><a href="#2-2-5-InjectPermfault-任务" class="headerlink" title="2.2.5 InjectPermfault 任务"></a>2.2.5 InjectPermfault 任务</h3><p>定义 PLI 任务 Inject_Permfault,主要功能是完成<span style="background:rgba(240, 200, 0, 0.2)">永久故障</span>的注入</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202212061258003.png" width="80%" height="80%"></div>]]></content>
<summary type="html"><h1 id="1-PLI相关"><a href="#1-PLI相关" class="headerlink" title="1. PLI相关"></a>1. PLI相关</h1><h2 id="1-1-PLI概念"><a href="#1-1-PLI概念" class="head</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="车规芯片" scheme="https://www.daipeihong.top/tags/%E8%BD%A6%E8%A7%84%E8%8A%AF%E7%89%87/"/>
<category term="PLI接口" scheme="https://www.daipeihong.top/tags/PLI%E6%8E%A5%E5%8F%A3/"/>
</entry>
<entry>
<title>Verilog数字系统设计第(三)章总结</title>
<link href="https://www.daipeihong.top/2022/11/17/verilog-shu-zi-xi-tong-she-ji-di-san-zhang-zong-jie/"/>
<id>https://www.daipeihong.top/2022/11/17/verilog-shu-zi-xi-tong-she-ji-di-san-zhang-zong-jie/</id>
<published>2022-11-17T07:26:00.000Z</published>
<updated>2022-11-17T07:51:17.629Z</updated>
<content type="html"><![CDATA[<h1 id="3-1-模块的结构"><a href="#3-1-模块的结构" class="headerlink" title="3.1 模块的结构"></a>3.1 模块的结构</h1><p>Verilog 的基本设计单元是“模块”( block)。一个模块是由两部分组成的:<u>一部分描述接口</u>,<u>另一部分描述逻辑功能,即定义输入是如何影响输出的。</u><br>Verilog 模块结构位于在 module和 endmodule声明语句之间。每个Verilog程序包括4个主要部分:<u>端口定义、I/O说明、内部信号声明、功能定义</u>。</p><h2 id="3-1-1-模块的端口定义"><a href="#3-1-1-模块的端口定义" class="headerlink" title="3.1.1 模块的端口定义"></a>3.1.1 模块的端口定义</h2><p>模块的端口声明了模块的输入输出口。其格式如下:</p><pre class=" language-verilog"><code class="language-verilog"><span class="token keyword">module</span>模块名<span class="token punctuation">(</span>口<span class="token number">1</span><span class="token punctuation">,</span>口<span class="token number">2</span><span class="token punctuation">,</span>口<span class="token number">3</span><span class="token punctuation">,</span>口<span class="token number">4</span><span class="token punctuation">,</span>…<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span></code></pre><p>在引用模块时其端口有两种方式连接:<br>一种是<strong>位置关联</strong></p><pre class=" language-verilog"><code class="language-verilog">模块名<span class="token punctuation">(</span>连接端口<span class="token number">1</span>信号名<span class="token punctuation">,</span>连接端口⒉信号名<span class="token punctuation">,</span>连接端口<span class="token number">3</span>信号名<span class="token punctuation">,</span>……<span class="token punctuation">)</span> <span class="token punctuation">;</span></code></pre><p>一种是<strong>名称关联</strong></p><pre class=" language-verilog"><code class="language-verilog">模块名<span class="token punctuation">(</span><span class="token punctuation">.</span>端口<span class="token number">1</span>名<span class="token punctuation">(</span>连接信号<span class="token number">1</span>名<span class="token punctuation">)</span><span class="token punctuation">,</span>端口<span class="token number">2</span>名<span class="token punctuation">(</span>连接信号<span class="token number">2</span>名<span class="token punctuation">)</span><span class="token punctuation">,</span>……<span class="token punctuation">)</span> <span class="token punctuation">;</span></code></pre><h2 id="3-1-2-模块内容"><a href="#3-1-2-模块内容" class="headerlink" title="3.1.2 模块内容"></a>3.1.2 模块内容</h2><p>模块的内容包括I/O说明、内部信号声明、功能定义。</p><ol><li><strong>I/O说明的格式</strong></li></ol><pre class=" language-verilog"><code class="language-verilog">输入口<span class="token punctuation">:</span><span class="token keyword">input</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名<span class="token number">1</span><span class="token punctuation">;</span><span class="token keyword">input</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名<span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token keyword">input</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名i<span class="token punctuation">;</span><span class="token comment" spellcheck="true">//(共有i个输人口)</span>输出口<span class="token punctuation">:</span><span class="token keyword">output</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名<span class="token number">1</span><span class="token punctuation">;</span><span class="token keyword">output</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名<span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token keyword">output</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名j<span class="token punctuation">;</span><span class="token comment" spellcheck="true">//(共有j个输出口)</span>输人<span class="token operator">/</span>输出口<span class="token punctuation">:</span><span class="token keyword">inout</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名<span class="token number">1</span><span class="token punctuation">;</span><span class="token keyword">inout</span><span class="token punctuation">[</span>信号位<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名<span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token keyword">inout</span><span class="token punctuation">[</span>信号位宽<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">]</span>端口名k<span class="token punctuation">;</span><span class="token comment" spellcheck="true">//(共有k个双向总线端口)</span></code></pre><ol start="2"><li><strong>内部信号声明</strong><br>wire和 reg类型变量的声明:</li></ol><pre class=" language-verilog"><code class="language-verilog"><span class="token keyword">reg</span> <span class="token punctuation">[</span>width<span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">]</span>R变量<span class="token number">1</span><span class="token punctuation">,</span>R变量<span class="token number">2</span>……<span class="token punctuation">;</span><span class="token keyword">wire</span> <span class="token punctuation">[</span>width<span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">]</span>W变量l<span class="token punctuation">,</span>W变量<span class="token number">2</span>……<span class="token punctuation">;</span></code></pre><ol start="3"><li><strong>功能定义</strong><br>模块中最重要的部分是逻辑功能定义部分,有以下3种方法可在模块中产生逻辑。<br>(1)assign声明语句<br>如:</li></ol><pre class=" language-verilog"><code class="language-verilog"><span class="token keyword">assign</span> a<span class="token operator">=</span>b <span class="token operator">&</span>c<span class="token punctuation">;</span><span class="token comment" spellcheck="true">//两输入与门</span></code></pre><p>(2)实例元件<br>如:</p><pre class=" language-verilog"><code class="language-verilog"><span class="token keyword">and</span> <span class="token number">#2</span> <span class="token function">ul</span><span class="token punctuation">(</span> q,a,b <span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">//两输入与门</span></code></pre><p>(3))always块<br>如:</p><pre class=" language-verilog"><code class="language-verilog"><span class="token important">always @</span> <span class="token punctuation">(</span><span class="token keyword">posedge</span> clk <span class="token keyword">or</span> <span class="token keyword">posedge</span> clr<span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token keyword">begin</span> <span class="token function">if</span><span class="token punctuation">(</span>clr<span class="token punctuation">)</span>q<span class="token operator"><=</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token function">if</span><span class="token punctuation">(</span>en<span class="token punctuation">)</span>q<span class="token operator"><=</span> d<span class="token punctuation">;</span><span class="token keyword">end</span></code></pre><p>采用assign语句是描述组合逻辑最常用的方法之一,而 always块既可用于描述组合逻辑,也可描述时序逻辑。</p><p><strong>always语句的执行顺序:</strong></p><ul><li>在<u>“always”模块内,逻辑是按照指定的顺序执行的</u>。“always”块中的语句称为“顺序语句”,因为它们是顺序执行的。所以, “ always”块也称为“<strong>过程块</strong>”。</li><li>两个或更多的“always”模块都是同时执行的,而模块内部的语句是顺序执行的。</li></ul><p><strong>与C语言的不同:</strong></p><ul><li><u>在Verilog模块中所有过程块(如:initial 块,always块),连续赋值语句,实例引用都是并行的</u>。</li><li>它们表示的是一种通过变量名互相连接的关系。</li><li>在同一模块中,assign语句、实例元件、always块语句这三者出现的先后顺序没有关系。</li><li><u>只有连续赋值语句assign和实例引用语句可以独立于过程块而存在于模块的功能定义部分</u>。</li></ul><h1 id="3-2-数据类型及其常量和变量"><a href="#3-2-数据类型及其常量和变量" class="headerlink" title="3.2 数据类型及其常量和变量"></a>3.2 数据类型及其常量和变量</h1>]]></content>
<summary type="html"><h1 id="3-1-模块的结构"><a href="#3-1-模块的结构" class="headerlink" title="3.1 模块的结构"></a>3.1 模块的结构</h1><p>Verilog 的基本设计单元是“模块”( block)。一个模块是由两部分组成的:</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="Verilog" scheme="https://www.daipeihong.top/tags/Verilog/"/>
</entry>
<entry>
<title>SystemVerilog学习笔记(十)</title>
<link href="https://www.daipeihong.top/2022/11/15/systemverilog-xue-xi-bi-ji-shi/"/>
<id>https://www.daipeihong.top/2022/11/15/systemverilog-xue-xi-bi-ji-shi/</id>
<published>2022-11-15T05:01:00.000Z</published>
<updated>2022-11-21T09:57:46.459Z</updated>
<content type="html"><![CDATA[<div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211152049091.png " width="90%" height="90%"></div># 1. 覆盖率## 1.1 概述覆盖率是衡量验证完备性的一个通用词语,用来衡量设计中已经被测部分和未测部分的比例,通常被定义为已达到所需验证部分的百分比。## 1.2 分类### 按照覆盖率生成方法* 隐性生成* 显性生成### 按照覆盖率溯源* 从功能描述而来* 从设计实现而来功能覆盖率是显性的需要人为定义的覆盖率,而代码覆盖率则是隐性覆盖率这是因为仿真工具可以自动从RTL代码来生成。将上述两个分类的方式进行组合,那么可以将代码覆盖率、断言覆盖率以及功能覆盖率分别置入到不同的象限。<div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211151928901.png " width="40%" height="40%"></div>但是需要注意,目前有一个象限仍然处于研究阶段,没有隐性的可以从功能描述生成某种覆盖率的方法,这也是为什么功能覆盖率依然需要人为定义的原因。# 2. 两种主要覆盖率## 2.1 代码覆盖率* 跳转覆盖率:用来衡量寄存器跳转的次数(0→1,1→0)* 行覆盖率:用来衡量源代码哪些代码行被执行过,以此来指出哪些代码行没有被执行过* 分支覆盖率:分支覆盖率是用来对条件语句( if/else,case,?:),指出其执行的分支轨迹。* 条件覆盖率:条件覆盖率是用来衡量一些布尔表达式中各个条件真伪判断的执行轨迹。* 状态机覆盖率:每个状态的进入次数,状态之间的跳转次数,以及多个状态的跳转顺序都可以由仿真工具记录下来。<h2 id="2-2-功能覆盖率"><a href="#2-2-功能覆盖率" class="headerlink" title="2.2 功能覆盖率"></a>2.2 功能覆盖率</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>我们无法通过代码覆盖率得知要求的功能是否被实现,需要显性地通过功能覆盖率与设计功能描述做映射,继而量化功能验证的进程。<br>创建功能覆盖率模型需要完成以下两个步骤:</p><ul><li>从功能描述文档提取拆分需要测试的功能点</li><li>将功能点量化为与设计实现对应的SV功能覆盖代码</li></ul><h3 id="覆盖组-covergroup"><a href="#覆盖组-covergroup" class="headerlink" title="覆盖组(covergroup)"></a>覆盖组(covergroup)</h3><ul><li>覆盖组与类相似,在一次定义以后便可以多次进行例化。</li><li>覆盖组含有覆盖点(coverpoint)、选项(option)、形式参数(argument)和可选触发(trigger event) </li><li>一个覆盖组包含了一个或者多个数据点,全都在同一时间采集。</li><li>覆盖组可以定义在类里,也可以定义在模块或者程序(program)中。</li></ul><pre class=" language-systemverilog"><code class="language-systemverilog">class xyz; bit [3:0] m_;int m y; bit m_z; covergroup cov1 @m_z; ll embedded covergroup coverpoint m_x; coverpoint m_y; endgroup function new() ; cov1 = new ; endfunctionendclass</code></pre><ul><li>覆盖组可以采集任何可见的变量,比如程序或模块变量、接口信号或者设计中的任何信号</li><li>在类中的覆盖组也可以采集类的成员变量·覆盖组应该定义在适当的抽象层次上</li><li>对任何事务的采样都必须等到数据被待测设计接收到以后。</li><li>一个类也可以包含多个覆盖组,每个覆盖组可以根据需要将它们使能或者禁止</li></ul><pre class=" language-systemverilog"><code class="language-systemverilog">class MC; logic [3:0] m_x;local logic m_z;bit m_e; covergroup cv1 @(posedge clk); coverpoint m_x; endgroup covergroup cv2 @m_e coverpoint m_z; endgroupendclass</code></pre><h3 id="覆盖点-coverpoint"><a href="#覆盖点-coverpoint" class="headerlink" title="覆盖点(coverpoint)"></a>覆盖点(coverpoint)</h3><h3 id="仓-bin"><a href="#仓-bin" class="headerlink" title="仓(bin)"></a>仓(bin)</h3><p>使用coverpoint 指定采样一个变量和表达式时,sv会创建很多的<strong>bin</strong>(仓)来记录每个数值被捕捉的次数。这些<strong>bin是衡量功能覆盖率的基本单位。</strong></p><h4 id="数值覆盖"><a href="#数值覆盖" class="headerlink" title="数值覆盖"></a>数值覆盖</h4><p>Covergroup的参数也可以被传递到bin的定义中</p><pre class=" language-systemverilog"><code class="language-systemverilog">covergroup cg (ref int ra,input int low, int high )@ (posedge clk) ; coverpoint r { bins good = { [low : high] }; bins bad[] = default; }endgroup...int va, vb;cg c1 = new ( va, 0,50 ) ;cg c2 = new( vb,120,600 ) ;</code></pre><h4 id="值变化覆盖"><a href="#值变化覆盖" class="headerlink" title="值变化覆盖"></a>值变化覆盖</h4><p>除了可以覆盖数值,还可以覆盖数值的变化</p><pre class=" language-systemverilog"><code class="language-systemverilog">value1 =>value2value1 =>value3 =>value4 =>value5range_list1 =>range_list21,5 =>6,7trans__item [*repeat_range]</code></pre><p>除了使用=>来表示相邻采样点的变化,也可以使用->来表示非相邻采样点的数值变化,在=>序列后的下一个时序必须紧跟=>序列的最后一个事件<br>3 [->3]<br>表示…=>3…=>3…=>3<br>1=>3 [->3]=>5<br>表示1…=>3…=>3…=>3 =>5</p><p>与[-> repeat range]类似的有[= repeatrange]也表示非连续的循环,只是与->有区别的在于,跟随->序列的下一次值变化可以发生在->结束后的任何时刻。<br>3 [= 2]<br>表示…=>3…=>3<br>1=>3 [=2]=>6<br>表示1…=>3…=>3…=>6</p><h4 id="自动生成"><a href="#自动生成" class="headerlink" title="自动生成"></a>自动生成</h4><p>如果coverpoint没有指定任何bin,那么SV将会为其自动生成bin,遵循的原则是:</p><ul><li>如果变量是枚举类型,那么bin的数量是枚举类型的基数(所有枚举数值的合)</li><li>如果变量是整形(M位宽),那么bin的类型将是2^M和auto_bin_max选项的较小值</li></ul><h4 id="通配应用"><a href="#通配应用" class="headerlink" title="通配应用"></a>通配应用</h4><ul><li>默认情况下,数值的变化可以针对四值类型变量进行覆盖,例如bin如果包含x或者z,则表示只有该变量对应位也为x或者z的时候,bin也才可以被采样到。</li><li>wildcard修饰符可以使得bin中包含x,z和?的数值都将用来表示0或者1,也就是通配符的意思</li></ul><pre class=" language-systemverilog"><code class="language-systemverilog">wildcard bins g12_15 = { 4 'b11??} ;g12_15可以用来表示12到15之间的值,即1100,1101,1110,1111</code></pre><h4 id="忽略类型和非法类型"><a href="#忽略类型和非法类型" class="headerlink" title="忽略类型和非法类型"></a>忽略类型和非法类型</h4><p>ignore_bins用来将其排除在有效统计的bin集合之外</p><pre class=" language-systemverilog"><code class="language-systemverilog">covergroup cg23; coverpoint a { ignore_bins ignore_vals = { 7,8} ; ignore_bins ignore_trans = (1=>3=>5); }endgroup</code></pre><p>illegal_bins用来指出采样到的数值为非法制,如果illegal_bins被采样到,那么仿真将报错</p><pre class=" language-systemverilog"><code class="language-systemverilog">covergroup cg3 ; coverpoint b { i1legal_bins bad_vals = { 1,2,3}; illegal_bins bad_trans = (4=>5=>6); }endgroup</code></pre><h3 id="交叉覆盖率-cross"><a href="#交叉覆盖率-cross" class="headerlink" title="交叉覆盖率(cross)"></a>交叉覆盖率(cross)</h3><ul><li>covergroup可以在两个或者更多的coverpoint或者变量之间定义交叉覆盖率(cross coverage)</li><li>在对a和b产生交叉覆盖率之前,系统会先为它们隐性产生对应的coverpoint和bin,每个coverpoint都有16个自动产生的bin</li><li>两个coverpoint交叉以后将生成256个交叉的bin</li></ul><pre class=" language-systemverilog"><code class="language-systemverilog">bit [3:0] a, b;covergroup cov @(posedge clk) ; axb : cross a, b;endgroup</code></pre><ul><li>除了系统会自动为交叉覆盖率生成bin以外,用户还可以自己定义交叉覆盖率的bin</li><li>binsof()的参数可以是coverpoint或者变量,表示对应的bin总和,可以利用binsof()对其结果做进—步的过滤</li></ul><pre class=" language-systemverilog"><code class="language-systemverilog">int i,j; covergroup ct; coverpoint i { bins i[]= { [O:1] } ; } coverpoint j { bins j[] = { [0:1] } ; } x1 : cross i,j; x2 : cross i,j{ bins i_zero = binsof(i)intersect { o }; }endgroup</code></pre><h1 id="3-问答题"><a href="#3-问答题" class="headerlink" title="3. 问答题"></a>3. 问答题</h1><p><strong>请考虑对于目前的DUT (router),要完成对它的功能测试,从接口时序、路由功能来看,需要考虑哪些功能和对应的测试场景?考虑路桑给出的接口插件文件(包含功能覆盖率定义和采样),是否没有全部映射你给出的待测功能点?如果需要补全,你是否可以在给出的接口插件文件中,补全这部分功能覆盖率定义代码呢?请将补全的部分代码,粘贴到这里,并且在你的实验四的代码中,试运行去收集你关心的这部分功能覆盖率,在验收实验四的时候,给予必要的说明。</strong></p><p>答:需要考虑是否各个输入端口都能输入数据,各个输出端口是否都能接收到数据;以及不同输入端口并行发送数据、同一端口发送多个数据到不同输出端,同一输出端口接收来自不同输入端口发送的数据……</p><h1 id="可参考"><a href="#可参考" class="headerlink" title="可参考"></a>可参考</h1><p><a href="https://blog.csdn.net/m0_59670134/article/details/119606764?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-119606764-blog-123967971.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-119606764-blog-123967971.pc_relevant_recovery_v2&utm_relevant_index=6">SV基础知识6—覆盖率收集_持续学习_ing的博客-CSDN博客_覆盖率收集</a></p>]]></content>
<summary type="html"><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211152049091.png " width="90%" h</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>10.31-11.13总结</title>
<link href="https://www.daipeihong.top/2022/11/13/10.31-11.13-zong-jie/"/>
<id>https://www.daipeihong.top/2022/11/13/10.31-11.13-zong-jie/</id>
<published>2022-11-13T11:50:00.000Z</published>
<updated>2022-11-17T11:51:02.019Z</updated>
<content type="html"><![CDATA[<h3 id="一、任务"><a href="#一、任务" class="headerlink" title="一、任务"></a>一、任务</h3><p>用ECC对 leon 系统里的SRAM进行加固</p><hr><h3 id="二、思路"><a href="#二、思路" class="headerlink" title="二、思路"></a>二、思路</h3><p>leon系统主要模块有<br>(1)Leon2 处理器核;(2)指令 SRAM 模块;(3)数据 SRAM 模块;(4)初始化指令 SRAM 模块</p><p>选取星载计算机系统中的数据 SRAM,将ECC电路加到数据SRAM上</p> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211131327595.png" width="500" height="300" title=" "> <hr><h3 id="三、模块示意图"><a href="#三、模块示意图" class="headerlink" title="三、模块示意图"></a>三、模块示意图</h3> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211131414230.png" width="500" height="350" title=" "> <hr><h3 id="四、仿真结果"><a href="#四、仿真结果" class="headerlink" title="四、仿真结果"></a>四、仿真结果</h3> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211131534173.png" width="1000" height="200" title=" "> <meta charset="utf-8"> <title>HTML</title> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211131544733.png" width="1200" height="100" title=" "> ]]></content>
<summary type="html"><h3 id="一、任务"><a href="#一、任务" class="headerlink" title="一、任务"></a>一、任务</h3><p>用ECC对 leon 系统里的SRAM进行加固</p>
<hr>
<h3 id="二、思路"><a href="#二、思路"</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="leon处理器" scheme="https://www.daipeihong.top/tags/leon%E5%A4%84%E7%90%86%E5%99%A8/"/>
<category term="ECC" scheme="https://www.daipeihong.top/tags/ECC/"/>
</entry>
<entry>
<title>SystemVerilog学习笔记(九)</title>
<link href="https://www.daipeihong.top/2022/11/06/systemverilog-xue-xi-bi-ji-jiu/"/>
<id>https://www.daipeihong.top/2022/11/06/systemverilog-xue-xi-bi-ji-jiu/</id>
<published>2022-11-06T06:45:00.000Z</published>
<updated>2022-11-21T09:56:59.373Z</updated>
<content type="html"><![CDATA[<p>本文思维导图如下:</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211071641178.png" width="90%" height="90%"></div><h1 id="1-线程控制"><a href="#1-线程控制" class="headerlink" title="1. 线程控制"></a>1. 线程控制</h1><h2 id="1-1-并行线程"><a href="#1-1-并行线程" class="headerlink" title="1.1 并行线程"></a>1.1 并行线程</h2><p>Verilog中与顺序线程begin…end相对的是并行线程fork…join。</p><h3 id="1-1-1创建线程"><a href="#1-1-1创建线程" class="headerlink" title="1.1.1创建线程"></a>1.1.1创建线程</h3><p>SV引入了两种新新的创建线程的方法,<u>fork…join_none</u>和<u>fork…join_any</u>。</p><ul><li><span style="background:rgba(136, 49, 204, 0.2)">fork…join</span><br>fork…join需要所有并行的线程都结束以后才会继续执行。</li><li><span style="background:rgba(136, 49, 204, 0.2)">fork…join_any</span><br>fork…join_any则会等到任何一个线程结束以后就继续执行。</li><li><span style="background:rgba(136, 49, 204, 0.2)">fork…join_none</span><br>fork…join_none则不会等待其子线程而继续执行。<div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061457338.png " width="60%" height="60%"></div>实例<div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061537443.png" width="60%" height="60%"></div></li></ul><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061537770.png" width="60%" height="60%"></div><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061539318.png " width="60%" height="60%"></div><h3 id="1-1-2子程序等待-x2F-停止"><a href="#1-1-2子程序等待-x2F-停止" class="headerlink" title="1.1.2子程序等待/停止"></a>1.1.2子程序等待/停止</h3><p>需要注意的是,fork…join_any和fork. . .join_none继续执行后,其一些未完成的子程序仍将在后台运行。如果要等待这些子程序全部完成,或者停止这些子程序,可以使用<span style="background:rgba(3, 135, 102, 0.2)">wait fork</span>或者<span style="background:rgba(3, 135, 102, 0.2)">disable fork</span>。<br><strong>(1)wait fork</strong></p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061547271.png" width="60%" height="60%"></div>**(2)disable fork**<div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061549290.png " width="60%" height="60%"></div>## 1.2 时序控制SV可以通过延迟控制或者事件等待来对过程块完成时序控制。### 1.2.1 延迟控制延迟控制即通过#来完成。>#10 rega = regb;<h3 id="1-2-2-事件控制"><a href="#1-2-2-事件控制" class="headerlink" title="1.2.2 事件控制"></a>1.2.2 事件控制</h3><p>事件(event)控制即通过@来完成。</p><blockquote><p>@r rega = regb ;<br>@(posedge clock) rega = regb;</p></blockquote><h3 id="1-2-3-事件等待"><a href="#1-2-3-事件等待" class="headerlink" title="1.2.3 事件等待"></a>1.2.3 事件等待</h3><p>wait语句也可以与事件或者表达式结合来完成。</p><blockquote><p>real AOR[] ;<br>initial wait(AOR.size() > 0) …. ;</p></blockquote><h1 id="2-进程间同步和通信"><a href="#2-进程间同步和通信" class="headerlink" title="2. 进程间同步和通信"></a>2. 进程间同步和通信</h1><p>测试平台中的所有线程都需要同步并交换数据。<br>一个线程等待另外一个,例如验证环境需要等待所有激励结束、比较结束才可以结束仿真。比如监测器需要将监测到的数据发送至比较器,比较器又需要从不同的缓存获取数据进行比较。</p><h2 id="2-1-事件"><a href="#2-1-事件" class="headerlink" title="2.1 事件"></a>2.1 事件</h2><ul><li>声明<br>可以通过event来声明一个命名event变量,并且去触发它。这个命名event可以用来控制进程的执行。</li><li>触发<br>可以通过->来触发事件。<br>其它等待该事件的进程可以通过@操作符或者wait()来检查event触发状态来完成。</li><li>等待<br>wait__order可以使得进程保持等待,直到在参数列表中的事件event按照顺序从左到右依次完成。如果参数列表中的事件被触发但是没有按照要求的顺序,那么会使得等待操作失败。<blockquote><p>wait_order ( a, b, c);</p><p>wait order( a, b, c ) else $display ( “Error: eventsout of order” );</p><p>bit success ;<br>wait order( a, b, c ) success = 1; else success = 0 ;</p></blockquote></li></ul><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061714487.png" width="60%" height="60%"></div><h2 id="2-2-旗语"><a href="#2-2-旗语" class="headerlink" title="2.2 旗语"></a>2.2 旗语</h2><p>旗语从概念上讲,是一个容器。在创建旗语的时候,会为其分配固定的钥匙数量。使用旗语的进程必须先获得其钥匙,才可以继续执行。旗语的钥匙数量可以有多个,等待旗语钥匙的进程也可同时有多个。旗语通常用于互斥,对共享资源的访问控制,以及基本的同步</p><div align="center"><img src=" https://img-blog.csdnimg.cn/8bbdaa8efccc4e36b6ece797b80c605a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASSBDIGUgcg==,size_20,color_FFFFFF,t_70,g_se,x_16" width="60%" height="60%"></div>### 2.2.1用法* 创建旗语,并为其分配钥匙的方式如下:>semaphore sm;>sm = new ();<ul><li><p>创建一个具有固定钥匙数量的旗语:</p><blockquote><p>new (N = 0)</p></blockquote></li><li><p>从旗语那里获取一个或多个钥匙(阻塞型) : </p><blockquote><p>get (N = 1)</p></blockquote></li><li><p>将一个或多个钥匙返回到旗语中:</p><blockquote><p>put (N = 1)</p></blockquote></li><li><p>尝试获取一个或多个钥匙而不会阻塞(非阻塞型) : </p><blockquote><p>try_get (N = 1)</p></blockquote></li></ul><h3 id="2-2-2基本操作"><a href="#2-2-2基本操作" class="headerlink" title="2.2.2基本操作"></a>2.2.2基本操作</h3><h4 id="senmaphore-new"><a href="#senmaphore-new" class="headerlink" title="senmaphore: :new()"></a>senmaphore: :new()</h4><p>new ())的原型如下:</p><blockquote><p>function new (int keyCount = 0) ;</p></blockquote><p>keyCount指定最初分配给旗语的钥匙数目。keyCount的默认值为0。当更多钥匙放入旗语时,钥匙数目可以超出初始时的keyCount数量,而不是删除。</p><h4 id="semaphore-put"><a href="#semaphore-put" class="headerlink" title="semaphore: :put()"></a>semaphore: :put()</h4><p>put ()方法用于将钥匙数量返回给旗语。<br>put ()的原型如下:</p><blockquote><p>function void put (int keyCount = 1) ;</p></blockquote><p>keycount指定返回到旗语的钥匙数量。默认值为1。调用semaphore.put ()函数时,指定数量的钥匙将返回到旗语。如果其它进程已经在等待旗语,则该进程应在有足够数量钥匙的情况下返回。</p><h4 id="semaphore-get"><a href="#semaphore-get" class="headerlink" title="semaphore: :get()"></a>semaphore: :get()</h4><p>get ()方法用于从旗语中获取指定数量的钥匙。get ()的原型如下:</p><blockquote><p>task get (int keycount = 1) ;</p></blockquote><p>keyCount指定从旗语获取所需的钥匙数,默认值为1。<u>如果指定数量的钥匙可用,则该方法返回并继续执行。如果指定数量的钥匙不足,进程将阻塞,直到钥匙数目充足。</u>旗语的等待队列是<span style="background:rgba(240, 200, 0, 0.2)">先进先出(FIFO)</span>,即先排队等待旗语的将优先得到钥匙。</p><h4 id="semaphore-try-get"><a href="#semaphore-try-get" class="headerlink" title="semaphore : :try_get()"></a>semaphore : :try_get()</h4><p>try get ()方法用于从信号量中获取指定数量的钥匙,但不会被阻塞。try_get ()的原型如下:</p><blockquote><p>function int try_get (int keyCount = 1) ;</p></blockquote><p>keyCount指定从旗语处获取所需的钥匙数目,默认值为1。<u>如果指定数量的钥匙可用,则该方法返回正数并继续执行。如果指定数量的钥匙不足,则该方法返回0。</u></p><h3 id="2-2-3实例"><a href="#2-2-3实例" class="headerlink" title="2.2.3实例"></a>2.2.3实例</h3><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061715075.png" width="100%" height="100%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061715493.png" width="100%" height="100%"></div>## 2.3 信箱信箱mailbox可以使得进程之间的信息得以交换,数据可以由一个进程写入信箱,再由另外一个进程获得。<div align="center"><img src=" https://img-blog.csdnimg.cn/c6469e7241f04c10affb418009c08e36.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASSBDIGUgcg==,size_20,color_FFFFFF,t_70,g_se,x_16" width="60%" height="60%"></div>信箱在创建时可以限制其容量,或者不限制。<u>当信箱容量写满时,后续再写入的动作会被挂起,直到信箱的数据从中读取,使得信箱有空间以后才可以继续写入。</u>不限制容量的信箱则不会挂起写入信箱的动作。### 2.3.1 内建方法#### new()new()用于创建信箱。<u>可以在创建信箱的时候限定或者不限定其大小</u>。<u>默认情况下,如果不传入参数,bound默认值为0,表示不限定信箱大小,如果传入的数值大于0,那么表示信箱的最大容量</u>。bound应为正数,如果未负数的话,系统会提示警告和出现无法预期的行为。>function new (int bound = 0) ;<h4 id="put"><a href="#put" class="headerlink" title="put()"></a>put()</h4><p>put()会将信息按照FIFO的顺序写入到信箱中,如果信箱此时已满,则put()任务会挂起,直到信箱有新的空间可以容纳消息。</p><h4 id="try-put"><a href="#try-put" class="headerlink" title="try_put()"></a>try_put()</h4><p>try put()试着写入信箱但不会阻塞。try put()也会按照FIFO顺序写入信箱,不会发生阻塞。如果信箱已满,则写入失败,返回0;如果信箱未满,则写入成功,返回1。</p><h4 id="get-x2F-peek"><a href="#get-x2F-peek" class="headerlink" title="get()/peek()"></a>get()/peek()</h4><p>获取信息: get()同时会取出数据,peek()不会取出数据。get()会将信息从信箱中取出,如果信箱此时为空,则get()任务会挂起,直到信箱中有消息可以读取,任务才会返回。该方法会将读取到的消息从信箱中移除。</p><h4 id="try-get-x2F-try-peek"><a href="#try-get-x2F-try-peek" class="headerlink" title="try_get()/try_peek()"></a>try_get()/try_peek()</h4><p>试着从信箱取出数据但不会阻塞。try get()也会将信息从信箱中取出,只是该函数不会发生阻塞。如果信箱为空,则读取失败,返回0;如果信箱不为空,则读取成功,返回1。该方法也会将读取到的消息从信箱中移除。</p><h4 id="num"><a href="#num" class="headerlink" title="num()"></a>num()</h4><p>num()会返回信箱目前的消息数目。可以结合num()与get()或者put(),防止get()/put()方法在信箱为空或者为满的时候被阻塞。</p><h3 id="2-3-2-实例"><a href="#2-3-2-实例" class="headerlink" title="2.3.2 实例"></a>2.3.2 实例</h3><p>mailbox <span style="background:rgba(240, 200, 0, 0.2)">#(int)</span> mb;//声明mailbox句柄,并且指明该mailbox中<span style="background:rgba(240, 200, 0, 0.2)">只能存放int类型</span>的数据</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211070937007.png" width="90%" height="90%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211070938538.png" width="90%" height="90%"></div><h2 id="2-4虚方法"><a href="#2-4虚方法" class="headerlink" title="2.4虚方法"></a>2.4虚方法</h2><p>给pringtB添加关键词virtual,P1扩大查找范围,扩大到子类对象里面。如果子类里面有同名的方法,若有,调用子类里面同名的方法(以子类的实现优先)。<br>子类不添加virtual无影响,父类添加即可。</p><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211071005100.png " width="60%" height="60%"></div># 3. 类型转换## 3.1 静态转换静态转换操作符不对转换值进行检查。转换时指定目标类型,并在要转换的表达式前加上单引号即可。Verilog对整数和实数类型,或者不同位宽的向量之间进行隐式转换。>int i;>real r;>i = int ' (10.0 - 0.1); //cast is optional>r= real'(42);// cast is optional## 3.2 动态转换我们总是可以将子类的句柄赋值给父类的句柄。但是在我们将父类的句柄赋值给子类的句柄时,编译将会报错。$cast()系统函数<u>可以将父类句柄转换为子类句柄</u>,只要该父类句柄指向的是一个子类的对象。>function int $cast( singular dest_var, singularsource_exp ) ;<p>或者</p><blockquote><p>task $cast( singular dest_var, singular source_exp );</p></blockquote><h1 id="4-问答题"><a href="#4-问答题" class="headerlink" title="4. 问答题"></a>4. 问答题</h1><p><strong>1. 用bit key =0来实现的类似旗语(semaphore)的功能,来保护对共享资源的访问,是否有不安全的地方?示例代码如下(包含自然语言表征)。如果有不安全的地方,请用文字表述,并且对以下代码进行修改,并且进行测试、编译和仿真打印消息。</strong></p><blockquote><p>bit key = 1;//创建钥匙<br>task get_key();<br>wait(key > 0);<br>key = 0;//获得钥匙<br>…//省略对于共享资源的处理<br>key = 1; /还回钥匙<br>endtask</p></blockquote><p><span style="background:rgba(240, 200, 0, 0.2)">答</span>:当多个task并行执行时,只要key=1,满足wait(key>0)即可执行,无法实现资源同时只能被指定个数的进程访问的目的。</p><p><strong>2. SV的原生旗语semaphore在例化以后,并不能从其获取哪一个对象(字符串stringID)获取了钥匙,那么请你将semaphore封装到一个类中,重新定义get()/put(方法,并且通过最后的一个打印函数来得知当前获取钥匙的string ID是谁?示例代码大致如下,请完成提示需要完成的地方,对你的代码进行编译和仿真,展示你的代码和仿真的打印消息。</strong><br><span style="background:rgba(240, 200, 0, 0.2)">答</span>:sv代码如下</p><pre class=" language-systemverilog"><code class="language-systemverilog">module tb;class smart_key; semaphore s;//创建旗语 string holder; function new(N= 1); s = new(N);//分配1把钥匙 endfunction task get(int N= 1,string str);//请添加需要的参数,并且实现该方法 s.get();//拿到钥匙 holder = str; #1ns; //$display("%0t: %s get the key",$time,str); endtask task put(int N= 1,string str);//请添加需要的参数,并且实现该方法 s.put();//放回钥匙 #1ns; //$display("%0t: %s put the key",$time,str); endtask function void print_key_holders();//请事先该方法,使其能够打印出当前获得钥匙的ID $display("%0t: now %s hold the key",$time,holder); endfunction endclass: smart_key initial begin smart_key skey = new(); string string1 = "A"; string string2 = "B"; string string3 = "C"; fork begin skey.get(1,string1); skey.print_key_holders(); skey.put(1,string1); end begin skey.get(1,string2); skey.print_key_holders(); skey.put(1,string2); end begin skey.get(1,string3); skey.print_key_holders(); skey.put(1,string3); end joinend endmodule: tb</code></pre><p><strong>仿真结果:</strong></p><blockquote><p>1000: now A hold the key<br>2000: now B hold the key<br>3000: now C hold the key</p></blockquote><p><strong>3. 我们在使用信箱mailbox时,如何可以限定其容量为8,并且只能存放string类型呢?那么,是否类似的方法也可以限定一个存放字符串的队列其最大容量为8呢?</strong><br><span style="background:rgba(240, 200, 0, 0.2)">答</span>:声明方法如下</p><blockquote><p>mailbox #(int) mb;<br>mb = new(8);</p></blockquote><p>队列空间是动态的,不能限定其最大容量。</p><p><strong>写的不错,可以参考</strong>:<a href="https://blog.csdn.net/qq_41337361/article/details/122723681?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-1-122723681-blog-78677789.pc_relevant_aa2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-1-122723681-blog-78677789.pc_relevant_aa2&utm_relevant_index=2">SV学习笔记—线程之间的通信(事件event、信箱mailbox、旗语semaphore)_Verification_White的博客-CSDN博客_sv event</a></p>]]></content>
<summary type="html"><p>本文思维导图如下:</p>
<div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211071641178.p</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>SystemVerilog实验(一)</title>
<link href="https://www.daipeihong.top/2022/11/05/systemverilog-shi-yan-yi/"/>
<id>https://www.daipeihong.top/2022/11/05/systemverilog-shi-yan-yi/</id>
<published>2022-11-05T14:08:00.000Z</published>
<updated>2022-11-06T02:20:18.526Z</updated>
<content type="html"><![CDATA[<h1 id="一、实验要求"><a href="#一、实验要求" class="headerlink" title="一、实验要求"></a>一、实验要求</h1><p><a href="https://shimo.im/docs/axk6MwD8D8IpFeqr/read">路科验证V0课程实验指导 (shimo.im)</a></p><h1 id="二、实验代码"><a href="#二、实验代码" class="headerlink" title="二、实验代码"></a>二、实验代码</h1><h2 id="router-io-sv"><a href="#router-io-sv" class="headerlink" title="router_io.sv"></a>router_io.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//定义接口,将复位信号(test.sv产生)送入DUT(router.v)interface router_io(input bit clock); logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface</code></pre><h2 id="test-sv"><a href="#test-sv" class="headerlink" title="test.sv"></a>test.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//产生复位信号,复位时,reset_n为低电平,frame_n和valid_n为高电平module test(router_io intf); initial begin #2 intf.reset_n = 1'b1; #2 intf.reset_n = 1'b0; #2 intf.reset_n <= 1'b1;//复位信号持续2 end initial begin reset();//调用reset产生复位信号 end task reset(); //@(posedge intf.clk or negedge intf.reset_n); @(negedge intf.reset_n); intf.din <= 0; intf.frame_n <= '1; intf.valid_n <= '1; repeat(15) @(intf.clock);//复位后至少等待15个时钟周期后发送数据 endtaskendmodule</code></pre><h2 id="router-test-top-sv"><a href="#router-test-top-sv" class="headerlink" title="router_test_top.sv"></a>router_test_top.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//产生时钟//例化另外三个文件`timescale 1ns/1psmodule router_test_top; bit clk; parameter cicle = 10; initial begin forever #(cicle/2) clk = !clk;//产生周期为10的时钟 end router_io intf( .clock(clk) );//例化接口 test tb(intf);//例化test,产生复位信号 router dut( .clock (intf.clock ), .reset_n (intf.reset_n ), .din (intf.din ), .frame_n (intf.frame_n ), .valid_n (intf.valid_n ), .dout (intf.dout ), .valido_n(intf.valido_n), .busy_n (intf.busy_n ), .frameo_n(intf.frameo_n) );//例化router为DUTendmodule</code></pre><h1 id="三、硬件层次"><a href="#三、硬件层次" class="headerlink" title="三、硬件层次"></a>三、硬件层次</h1><p><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061008531.png"></p><h1 id="四、仿真结果"><a href="#四、仿真结果" class="headerlink" title="四、仿真结果"></a>四、仿真结果</h1><p><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211061003883.png"></p>]]></content>
<summary type="html"><h1 id="一、实验要求"><a href="#一、实验要求" class="headerlink" title="一、实验要求"></a>一、实验要求</h1><p><a href="https://shimo.im/docs/axk6MwD8D8IpFeqr/read"></summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>SystemVerilog课程笔记(八)</title>
<link href="https://www.daipeihong.top/2022/11/01/systemverilog-ke-cheng-bi-ji-ba/"/>
<id>https://www.daipeihong.top/2022/11/01/systemverilog-ke-cheng-bi-ji-ba/</id>
<published>2022-11-01T12:50:00.000Z</published>
<updated>2022-11-22T12:55:36.521Z</updated>
<content type="html"><![CDATA[<h1 id="1-改用samephore判断chnl是否被占用"><a href="#1-改用samephore判断chnl是否被占用" class="headerlink" title="1. 改用samephore判断chnl是否被占用"></a>1. 改用samephore判断chnl是否被占用</h1><h1 id="2-对single-chnl添加随机"><a href="#2-对single-chnl添加随机" class="headerlink" title="2. 对single chnl添加随机"></a>2. 对single chnl添加随机</h1><h2 id="2-1-改造后的rt-test-pkg-sv"><a href="#2-1-改造后的rt-test-pkg-sv" class="headerlink" title="2.1 改造后的rt_test_pkg.sv"></a>2.1 改造后的rt_test_pkg.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; rand bit [3:0] src; rand bit [3:0] dst; rand bit [7:0] data []; constraint pkg_cstr{ soft data.size inside{[1:32]}; foreach(data[i]) soft data[i] == (src << 4) + i; } function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; //int src_chnl_status[int]; //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ?? semaphore src_chnl_status[16];//16个chnl的钥匙 function new(); foreach(src_chnl_status[i]) src_chnl_status[i] = new(1); endfunction//generator传送p给stimulator rt_packet pkts[$];//定义stimulator中的的队列pkts function void put_pkt(input rt_packet p); pkts.push_back(p);//将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p) endfunctiontask run(); fork drive_reset(); //reset动作 get_packet_and_drive(); //drive_chnl动作 join_noneendtask task drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive();//drive_chnl //rt_packet_t p; @(negedge intf.reset_n); repeat(10) @(posedge intf.clock);//延迟10个时钟周期 forever begin automatic rt_packet p;//声明一个动态的 wait(pkts.size()>0); p = pkts.pop_front();//将p从队列pkts里面取出 fork//后台触发线程,触发线程在后台运行,继续执行剩下内容 begin wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待 drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据 set_src_chnl_avail(p); end join_none endendtasktask wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待 src_chnl_status[p.src].get(1);//拿到钥匙 //if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足) //src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据) //else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待 //wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endtaskfunction set_src_chnl_avail(rt_packet p); src_chnl_status[p.src].put(1);//还钥匙 //src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endfunctiontask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator rt_packet pkts[$];//定义队列 task put_pkt(input rt_packet p); //wait(pkts.size()=0) pkts.push_back(p);//将p放入队列pkts里面(在pkts队列尾插入p) endtask task get_pkt(output rt_packet p); wait(pkts.size() >0 )//队列不为空 p = pkts.pop_front();//将p从队列pkts里面取出,提取队列首元素 endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); // NOTE : src is from data format (user defined) pkt.src = pkt.data[0][7:4];//高4位 out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",exp_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator forever begin//此处不需要延迟0,执行task run的时候默认function new已执行 gen.get_pkt(p);//get句柄 stim.put_pkt(p);//put句柄 end end join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 200;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 rand bit signed [4:0] src; rand bit signed [4:0] dst; rand int unsigned pkt_count =10; constraint test_cstr { soft pkt_count inside {[1:20]}; src inside {[0 : 15]}; dst inside {[0 : 15]}; } function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 this.randomize(); //randmoize self to get constrained data for ( int cnt=0; cnt < pkt_count; cnt++) begin//随机10次 p = new( ); p.randomize() with {src == local::src; dst == local::dst; }; env.gen.put_pkt(p); end set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage</code></pre><h2 id="2-2-改造后的lab8tb-sv"><a href="#2-2-改造后的lab8tb-sv" class="headerlink" title="2.2 改造后的lab8tb.sv"></a>2.2 改造后的lab8tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">//********************************** rt_interface **********************************//interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb; import rt_test_pkg ::*; bit clk,rstn; logic [15:0] din, frame_n, valid_n; logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_multi_ch_test multi_ch_test; initial begin : Select_the_test string name; single_ch_test = new(intf); multi_ch_test = new(intf); //single_ch_test.run(); //multi_ch_test.run(); if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 case(name) "rt_single_ch_test" : single_ch_test.run();//输入rt_single_ch_test,调用对应run,执行输入rt_single_ch_test "rt_multi_ch_test" :multi_ch_test.run(); default:$fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); endcase end end endmodule </code></pre><h2 id="2-3-makefile"><a href="#2-3-makefile" class="headerlink" title="2.3 makefile"></a>2.3 makefile</h2><pre class=" language-c"><code class="language-c">###########################<span class="token macro property"># User variables</span>###########################TB <span class="token operator">=</span> tbSEED <span class="token operator">=</span> <span class="token number">1</span>TESTNAME <span class="token operator">?</span><span class="token operator">=</span> rt_single_ch_testFILES <span class="token operator">=</span> router<span class="token punctuation">.</span>v rt_test_pkg<span class="token punctuation">.</span>sv lab8tb<span class="token punctuation">.</span>sv #User Defination###########################<span class="token macro property"># Environment variables</span>###########################COMP <span class="token operator">=</span> vcs <span class="token operator">-</span>full64 <span class="token operator">-</span>sverilog <span class="token operator">-</span>debug_access<span class="token operator">+</span>all <span class="token operator">-</span>timescale<span class="token operator">=</span>1ns<span class="token operator">/</span>1ps <span class="token operator">-</span>l comp<span class="token punctuation">.</span>log $<span class="token punctuation">(</span>FILES<span class="token punctuation">)</span>RUN <span class="token operator">=</span> <span class="token punctuation">.</span><span class="token operator">/</span>$<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simv <span class="token operator">-</span>l run<span class="token punctuation">.</span>log <span class="token operator">-</span>sml <span class="token operator">+</span>ntb_random_seed<span class="token operator">=</span>$<span class="token punctuation">(</span>SEED<span class="token punctuation">)</span> <span class="token operator">+</span>TESTNAME<span class="token operator">=</span>$<span class="token punctuation">(</span>TESTNAME<span class="token punctuation">)</span>comp<span class="token punctuation">:</span> $<span class="token punctuation">(</span>COMP<span class="token punctuation">)</span> <span class="token operator">-</span>top $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span> <span class="token operator">-</span>o $<span class="token punctuation">(</span>TB<span class="token punctuation">)</span><span class="token punctuation">.</span>simvrun<span class="token punctuation">:</span> $<span class="token punctuation">(</span>RUN<span class="token punctuation">)</span>rung<span class="token punctuation">:</span> $<span class="token punctuation">(</span>RUN<span class="token punctuation">)</span> <span class="token operator">-</span>gui</code></pre><h1 id="3-仿真结果"><a href="#3-仿真结果" class="headerlink" title="3. 仿真结果"></a>3. 仿真结果</h1><p>DVE报告</p><blockquote><p>TEST rt_single_ch_test started<br>@105000:[DRV] src_chnl[10] & dest_chnl[8] data trans started with packet:<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>@150000: [MON] src_chn1[10] & dest_chn1[8] data trans started<br>@205000: [MON] CH_OUT dest_chn1[8] data trans started<br>@1400000:[MON] CH_IN src_chnl[10] &dest_chnl[8 ] finished with packet:<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>@1405000: [DRV]src_chnl[10],dest_chnl[8] data trans finished<br>@1405000:[DRV] src_chnl[10] & dest_chnl[8] data trans started with packet:<br>src = 10<br>dst = 8<br>data_length = 14<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had</p><p>@1420000: [MON] CH_OUT dest_chn1[8] data finished woth packet :<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>[CHK] data compare success with packet :<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>@1450000: [MON] src_chn1[10] & dest_chn1[8] data trans started<br>@1505000: [MON] CH_OUT dest_chn1[8] data trans started<br>@2620000:[MON] CH_IN src_chnl[10] &dest_chnl[8 ] finished with packet:<br>src = 10<br>dst = 8<br>data_length = 14<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had</p><p>@2625000: [DRV]src_chnl[10],dest_chnl[8] data trans finished<br>@2625000:[DRV] src_chnl[10] & dest_chnl[8] data trans started with packet:<br>src = 10<br>dst = 8<br>data_length = 30<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba<br>data[27] = ‘hbb<br>data[28] = ‘hbc<br>data[29] = ‘hbd</p><p>@2640000: [MON] CH_OUT dest_chn1[8] data finished woth packet :<br>src = 10<br>dst = 8<br>data_length = 14<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had</p><p>[CHK] data compare success with packet :<br>src = 10<br>dst = 8<br>data_length = 14<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had</p><p>@2670000: [MON] src_chn1[10] & dest_chn1[8] data trans started<br>@2725000: [MON] CH_OUT dest_chn1[8] data trans started<br>@5120000:[MON] CH_IN src_chnl[10] &dest_chnl[8 ] finished with packet:<br>src = 10<br>dst = 8<br>data_length = 30<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba<br>data[27] = ‘hbb<br>data[28] = ‘hbc<br>data[29] = ‘hbd</p><p>@5125000: [DRV]src_chnl[10],dest_chnl[8] data trans finished<br>@5125000:[DRV] src_chnl[10] & dest_chnl[8] data trans started with packet:<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>@5140000: [MON] CH_OUT dest_chn1[8] data finished woth packet :<br>src = 10<br>dst = 8<br>data_length = 30<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba<br>data[27] = ‘hbb<br>data[28] = ‘hbc<br>data[29] = ‘hbd</p><p>[CHK] data compare success with packet :<br>src = 10<br>dst = 8<br>data_length = 30<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba<br>data[27] = ‘hbb<br>data[28] = ‘hbc<br>data[29] = ‘hbd</p><p>@5170000: [MON] src_chn1[10] & dest_chn1[8] data trans started<br>@5225000: [MON] CH_OUT dest_chn1[8] data trans started<br>@6420000:[MON] CH_IN src_chnl[10] &dest_chnl[8 ] finished with packet:<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>@6425000: [DRV]src_chnl[10],dest_chnl[8] data trans finished<br>@6425000:[DRV] src_chnl[10] & dest_chnl[8] data trans started with packet:<br>src = 10<br>dst = 8<br>data_length = 27<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba</p><p>@6440000: [MON] CH_OUT dest_chn1[8] data finished woth packet :<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>[CHK] data compare success with packet :<br>src = 10<br>dst = 8<br>data_length = 15<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae</p><p>@6470000: [MON] src_chn1[10] & dest_chn1[8] data trans started<br>@6525000: [MON] CH_OUT dest_chn1[8] data trans started<br>@8680000:[MON] CH_IN src_chnl[10] &dest_chnl[8 ] finished with packet:<br>src = 10<br>dst = 8<br>data_length = 27<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba</p><p>@8685000: [DRV]src_chnl[10],dest_chnl[8] data trans finished<br>@8700000: [MON] CH_OUT dest_chn1[8] data finished woth packet :<br>src = 10<br>dst = 8<br>data_length = 27<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba</p><p>[CHK] data compare success with packet :<br>src = 10<br>dst = 8<br>data_length = 27<br>data[0] = ‘ha0<br>data[1] = ‘ha1<br>data[2] = ‘ha2<br>data[3] = ‘ha3<br>data[4] = ‘ha4<br>data[5] = ‘ha5<br>data[6] = ‘ha6<br>data[7] = ‘ha7<br>data[8] = ‘ha8<br>data[9] = ‘ha9<br>data[10] = ‘haa<br>data[11] = ‘hab<br>data[12] = ‘hac<br>data[13] = ‘had<br>data[14] = ‘hae<br>data[15] = ‘haf<br>data[16] = ‘hb0<br>data[17] = ‘hb1<br>data[18] = ‘hb2<br>data[19] = ‘hb3<br>data[20] = ‘hb4<br>data[21] = ‘hb5<br>data[22] = ‘hb6<br>data[23] = ‘hb7<br>data[24] = ‘hb8<br>data[25] = ‘hb9<br>data[26] = ‘hba</p><p>TOTAL COMPARING 5 times<br>TEST [rt_single_ch_test] PASSED!</p></blockquote>]]></content>
<summary type="html"><h1 id="1-改用samephore判断chnl是否被占用"><a href="#1-改用samephore判断chnl是否被占用" class="headerlink" title="1. 改用samephore判断chnl是否被占用"></a>1. 改用samephor</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>SystemVerilog学习笔记(八)</title>
<link href="https://www.daipeihong.top/2022/11/01/systemverilog-xue-xi-bi-ji-ba/"/>
<id>https://www.daipeihong.top/2022/11/01/systemverilog-xue-xi-bi-ji-ba/</id>
<published>2022-11-01T03:53:00.000Z</published>
<updated>2022-11-21T10:08:12.603Z</updated>
<content type="html"><![CDATA[<div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211022245932.png" width="60%" height="60%"></div><h2 id="1-如何简单产生一个随机常数"><a href="#1-如何简单产生一个随机常数" class="headerlink" title="1. 如何简单产生一个随机常数"></a>1. 如何简单产生一个随机常数</h2><h3 id="1-1-randomize"><a href="#1-1-randomize" class="headerlink" title="1.1 $randomize"></a>1.1 $randomize</h3><h3 id="1-2-urandom"><a href="#1-2-urandom" class="headerlink" title="1.2 $urandom"></a>1.2 $urandom</h3><p>$urandom(),可以生成一个32位的无符号随机数。<br>$urandom_range(maxval, minval=0),可以生成间与maxval与minval之间的数。</p><blockquote><p>val = $urandom_range(7,0); //结果是[0,7]<br>val = $urandom_range(0,7); //如果mixval比minval小,参数列表会自动反向<br>val = $urandom_range(7); //如果minval没有指定,默认为0</p></blockquote><h2 id="2-系统而有机地组织随机变量"><a href="#2-系统而有机地组织随机变量" class="headerlink" title="2. 系统而有机地组织随机变量"></a>2. 系统而有机地组织随机变量</h2><h3 id="2-1-约束"><a href="#2-1-约束" class="headerlink" title="2.1 约束"></a>2.1 约束</h3><p>以上独立地生成一些随机数,在面向DUT的随机激励发生过程中,为了符合协议、满足测试需求,还需要添加一些”约束”。这些”约束”会使得变量朝着希望他们变化的方向去随机。不但如此,这些约束也会对变量与变量之间的关系生效。</p><h3 id="2-2-约束的载体"><a href="#2-2-约束的载体" class="headerlink" title="2.2 约束的载体"></a>2.2 约束的载体</h3><p>因此,我们需要一个”载体”去容纳这些变量以及它们之间的约束。这个”载体”即是类,而类的成员变量均可声明为”随机”属性,用rand或者randc来表示。</p><h3 id="2-3-随机变量"><a href="#2-3-随机变量" class="headerlink" title="2.3 随机变量"></a>2.3 随机变量</h3><p>任何类中的<span style="background:#d3f8b6">整形(bit/byte/int)变量</span>都可以声明为rand/randc。<br><span style="background:#d3f8b6">定长数组、动态数组、关联数组和队列</span>都可以声明为rand/randc,可以对动态数组和队列的长度加以约束。<br><span style="background:#d3f8b6">指向对象的句柄成员</span>,也可以声明为rand(不能被声明为randc) ,随机时该句柄指向对象中的随机变量也会一并被随机。</p><h3 id="2-4-实例"><a href="#2-4-实例" class="headerlink" title="2.4 实例"></a>2.4 实例</h3><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011751369.png " width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211022220922.png" width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211022221417.png" width="80%" height="80%"></div><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211022222796.png " width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211022222514.png" width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011754978.png" width="80%" height="80%"></div><h2 id="2-5-约束块"><a href="#2-5-约束块" class="headerlink" title="2.5 约束块"></a>2.5 约束块</h2><p>有用的激励不仅仅是随机值,<span style="background:rgba(240, 200, 0, 0.2)">变量之间也有着相互关系</span>。没有约束的随机变量会包含许多无效的和非法的值,这会使得有效激励的产生</p><h3 id="2-5-1-设置取值范围"><a href="#2-5-1-设置取值范围" class="headerlink" title="2.5.1 设置取值范围"></a>2.5.1 设置取值范围</h3><p>约束块支持整形通过set操作符来设置它们的可取值范围。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011759261.png" width="60%" height="60%"></div><h3 id="2-5-2-权重"><a href="#2-5-2-权重" class="headerlink" title="2.5.2 权重"></a>2.5.2 权重</h3><p>除了成员集合设置,约束块也支持设置可取值的同时也为其设置随机时的权重。<br> <span style="background:rgba(240, 200, 0, 0.2)">:=</span> 操作符:表示每一个值的权重是相同的。<br> <span style="background:rgba(240, 200, 0, 0.2)"> :/ </span> 操作符:表示权重会平均分配到每一个值。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011802842.png" width="60%" height="60%"></div><h3 id="2-5-3-唯一标识"><a href="#2-5-3-唯一标识" class="headerlink" title="2.5.3 唯一标识"></a>2.5.3 唯一标识</h3><p>unique可以用来约束一组变量,<span style="background:rgba(240, 200, 0, 0.2)">使得其在随机后变量之间不会有相同的数值。</span></p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011807987.png" width="60%" height="60%"></div><h3 id="2-5-4-条件约束"><a href="#2-5-4-条件约束" class="headerlink" title="2.5.4 条件约束"></a>2.5.4 条件约束</h3><p>使用if-else或者->操作符来表示条件约束。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011808349.png" width="50%" height="50%"></div><h3 id="2-5-5-迭代约束"><a href="#2-5-5-迭代约束" class="headerlink" title="2.5.5 迭代约束"></a>2.5.5 迭代约束</h3><p>foreach可以用来迭代约束数组中的元素,这些数组可以是定长数组、动态数组、关联数组或者队列。<br>也可以使用数组的缩减方法做迭代约束。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011824469.png" width="50%" height="50%"></div><hr><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211011825775.png" width="50%" height="50%"></div><h3 id="2-5-6-函数调用"><a href="#2-5-6-函数调用" class="headerlink" title="2.5.6 函数调用"></a>2.5.6 函数调用</h3><h3 id="2-5-7-软约束"><a href="#2-5-7-软约束" class="headerlink" title="2.5.7 软约束"></a>2.5.7 软约束</h3><p>在没有soft描述时的约束,我们称之为硬约束,而带有soft描述的则是软约束。软约束用来指定变量的默认值和权重。如果用户在使用时,指定了外部约束对同一个变量做二次约束,或者用户定义了子类,也对同一个变量做二次约束时,那么<span style="background:rgba(240, 200, 0, 0.2)">硬约束可以”覆盖”软约束</span>,并且不会导致随机数产生的失败。</p><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012017431.png " width="90%" height="90%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012017414.png" width="80%" height="80%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012017120.png" width="90%" height="90%"></div><h3 id="2-5-8-实例"><a href="#2-5-8-实例" class="headerlink" title="2.5.8 实例"></a>2.5.8 实例</h3><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012042971.png" width="80%" height="80%"></div><h3 id="2-5-9-内嵌约束(指向模糊)"><a href="#2-5-9-内嵌约束(指向模糊)" class="headerlink" title="2.5.9 内嵌约束(指向模糊)"></a>2.5.9 内嵌约束(指向模糊)</h3><p>此处x是加给f的约束,就近句柄f指向类c1,优先找这个类里面有无x<br>若要指向task参数中的x,使用 <span style="background:#fff88f">local::x</span></p><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012052456.png " width="60%" height="60%"></div><h3 id="2-5-10-内嵌约束(指向明确)"><a href="#2-5-10-内嵌约束(指向明确)" class="headerlink" title="2.5.10 内嵌约束(指向明确)"></a>2.5.10 内嵌约束(指向明确)</h3><h3 id="2-5-11-local域指向"><a href="#2-5-11-local域指向" class="headerlink" title="2.5.11 local域指向"></a>2.5.11 local域指向</h3><p>之前在使用内嵌约束随机时,对于同名的变量处在不同的域中,可能会出现指向模糊的情况,之前我们已经给出了一种方法。接下来我们可以通过<span style="background:#fff88f">local::</span> 的域索引方式来明确随机变量的指向,即<u> <span style="background:#fff88f">local::</span> 指向的变量会在包含randomize()方法的对象中</u>。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211021543318.png" width="60%" height="60%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012109025.png" width="60%" height="60%"></div><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012106238.png " width="60%" height="60%"></div><h3 id="2-5-12-随机控制"><a href="#2-5-12-随机控制" class="headerlink" title="2.5.12 随机控制"></a>2.5.12 随机控制</h3><p>rand_mode可以用来使能或者禁止随机变量。<br>当随机数被禁止时,它会同为声明为随机变量的普通变量一样,不会参与到随机化过程当中。<br>从以下两个函数声明可以看到,可以就单个随机变量调用其rand_mode,或者对整个对象调用rand_mode来控制其中所有的随机变量。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012124495.png" width="60%" height="60%"></div><h3 id="2-5-13-约束控制"><a href="#2-5-13-约束控制" class="headerlink" title="2.5.13 约束控制"></a>2.5.13 约束控制</h3><p>类似于随机控制,一些约束块或者某个类的约束块集合都可以单个控制或者集体控制。</p><blockquote><p>task object[.constraint_identifier] : :constraint_mode (bit on_off );funetion int object.constraint_identifier : : constraint_mode() ;</p></blockquote><p>可以通过约束控制函数来使能或者关闭某些约束块。</p><div align="center"><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012126652.png " width="50%" height="50%"></div><h3 id="2-5-14-内嵌变量控制"><a href="#2-5-14-内嵌变量控制" class="headerlink" title="2.5.14 内嵌变量控制"></a>2.5.14 内嵌变量控制</h3><p>在使用类的随机化函数randomize()时,如果伴有参数,那么只会随机化这些变量,而其余变量无论是否之前被声明为rand/randc,都将不会参与到随机化当中。</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012130326.png" width="50%" height="50%"></div><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211012226039.png" width="60%" height="60%"></div><h1 id="3-问答题"><a href="#3-问答题" class="headerlink" title="3. 问答题"></a>3. 问答题</h1><p><strong>1.随机测试相比于定向测试,就你目前的认识,你认为有哪些优缺点?</strong><br>答:当一个项目的功能成倍增加时,编写足够多的定向测试几乎不可能。而且,定向测试很难满足检查功能完整性的要求,随机测试能够产生更加完整复杂的激励场景,去对设计进行测试。缺点:随机测试会使验证环境的复杂度提高,从环境组件上考虑不再只需要发送激励的组件,而且还包括监测器、比较<br>器等。</p><p><strong>2.产生一个随机数有哪些方法呢?请提交代码文本和仿真打印结果。</strong><br><strong>使用randomize</strong></p><pre class=" language-systemverilog"><code class="language-systemverilog">module randnum; bit [3:0] a; bit [3:0] b; initial begin $display("before randomized a = %0d b = %0d",a,b); std::randomize (a,b); $display("after randomized a = %0d b = %0d",a,b); endendmodule</code></pre><p>仿真结果:</p><blockquote><p>before randomized a =0 b=0<br>after randomized a= 4 b = 14</p></blockquote><p><strong>使用系统函数$urandom</strong></p><pre class=" language-systemverilog"><code class="language-systemverilog">module randnum; bit [3:0] a; bit [3:0] b; initial begin $display("before randomized a = %0d b = %0d",a,b); b = $urandom(); a = $urandom_range(0,10); $display("after randomized a = %0d b = %0d",a,b); endendmodule</code></pre><p>仿真结果</p><blockquote><p>before randomized a = 0 b = 0<br>after randomized a = 6 b = 6</p></blockquote><p><strong>3.如何用rand bit [15:0]向量产生一个随机实数?请提交代码文本和仿真打印结果。</strong></p><p><strong>4.如何用rand bit[7:0] arr[8]通过约束块(不用unique来约束)产生一个元素各不相同的数组?请提交代码文本和仿真打印结果。</strong></p><p><strong>5.请仿真以下代码并且解释仿真结果。</strong></p><pre class=" language-systemverilog"><code class="language-systemverilog">module tb; class randnum; rand bit[3:0] x, y; endclassinitial begin randnum rn = new(); int x = 5, y=7; for(int loop = 0; loop < 10; loop++) begin rn.randomize() with {x < y;}; $display("LOOP-%0d:: CONSTRAINT{x<y;}\n rn.x = %0d, rn.y = %0d", loop, rn.x, rn.y); rn.randomize() with {x < this.y;}; $display("LOOP-%0d: CONSTRAINT {x<this.y;}\n rn.x = %0d, rn.y = %0d", loop, rn.x, rn.y); rn.randomize() with {x < local::y;}; $display("LOOP-%0d:: CONSTRAINT{x<local::y;}\n rn.x = %0d, rn.y = %0d", loop, rn.x,rn.y); endendendmodule</code></pre><p><strong>分析:</strong><br>y——指向rand bit[3:0] y中的y(就近原则,rn指向randnum这个类,此处y指向randnum这个要被随机化的类里面的y),所以每次随机后的x小于每次随机后的y即可。<br>this.y——同上<br>local::y——指向y=7(local::指向的变量y会在包含randomize()方法的对象rn中),所以每次随机后x小于7即可。</p><blockquote><p>仿真结果<br>LOOP-0:: CONSTRAINT{x<y;}<br>rn.x = 4, rn.y = 6<br>LOOP-0: CONSTRAINT {x<this.y;}<br>rn.x = 5, rn.y = 15<br>LOOP-0:: CONSTRAINT{x<local::y;}<br>rn.x = 5, rn.y = 1</p><p>LOOP-1:: CONSTRAINT{x<y;}<br>rn.x = 2, rn.y = 11<br>LOOP-1: CONSTRAINT {x<this.y;}<br>rn.x = 0, rn.y = 4<br>LOOP-1:: CONSTRAINT{x<local::y;}<br>rn.x = 4, rn.y = 7</p><p>LOOP-2:: CONSTRAINT{x<y;}<br>rn.x = 8, rn.y = 13<br>LOOP-2: CONSTRAINT {x<this.y;}<br>rn.x = 7, rn.y = 8<br>LOOP-2:: CONSTRAINT{x<local::y;}<br>rn.x = 6, rn.y = 13</p><p>LOOP-3:: CONSTRAINT{x<y;}<br>rn.x = 2, rn.y = 15<br>LOOP-3: CONSTRAINT {x<this.y;}<br>rn.x = 4, rn.y = 6<br>LOOP-3:: CONSTRAINT{x<local::y;}<br>rn.x = 0, rn.y = 15</p><p>LOOP-4:: CONSTRAINT{x<y;}<br>rn.x = 11, rn.y = 15<br>LOOP-4: CONSTRAINT {x<this.y;}<br>rn.x = 2, rn.y = 5<br>LOOP-4:: CONSTRAINT{x<local::y;}<br>rn.x = 4, rn.y = 10</p><p>LOOP-5:: CONSTRAINT{x<y;}<br>rn.x = 6, rn.y = 10<br>LOOP-5: CONSTRAINT {x<this.y;}<br>rn.x = 7, rn.y = 10<br>LOOP-5:: CONSTRAINT{x<local::y;}<br>rn.x = 3, rn.y = 3</p><p>LOOP-6:: CONSTRAINT{x<y;}<br>rn.x = 0, rn.y = 12<br>LOOP-6: CONSTRAINT {x<this.y;}<br>rn.x = 9, rn.y = 13<br>LOOP-6:: CONSTRAINT{x<local::y;}<br>rn.x = 3, rn.y = 0</p><p>LOOP-7:: CONSTRAINT{x<y;}<br>rn.x = 0, rn.y = 1<br>LOOP-7: CONSTRAINT {x<this.y;}<br>rn.x = 4, rn.y = 6<br>LOOP-7:: CONSTRAINT{x<local::y;}<br>rn.x = 5, rn.y = 13</p><p>LOOP-8:: CONSTRAINT{x<y;}<br>rn.x = 0, rn.y = 12<br>LOOP-8: CONSTRAINT {x<this.y;}<br>rn.x = 11, rn.y = 14<br>LOOP-8:: CONSTRAINT{x<local::y;}<br>rn.x = 0, rn.y = 15</p><p>LOOP-9:: CONSTRAINT{x<y;}<br>rn.x = 3, rn.y = 15<br>LOOP-9: CONSTRAINT {x<this.y;}<br>rn.x = 1, rn.y = 14<br>LOOP-9:: CONSTRAINT{x<local::y;}<br>rn.x = 0, rn.y = 8</p></blockquote>]]></content>
<summary type="html"><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211022245932.png" width="60%" h</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
<category term="随机约束" scheme="https://www.daipeihong.top/tags/%E9%9A%8F%E6%9C%BA%E7%BA%A6%E6%9D%9F/"/>
</entry>
<entry>
<title>文献调研——星载计算机单粒子效应故障注入仿真</title>
<link href="https://www.daipeihong.top/2022/10/30/wen-xian-diao-yan-xing-zai-ji-suan-ji-dan-li-zi-xiao-ying-gu-zhang-zhu-ru-fang-zhen/"/>
<id>https://www.daipeihong.top/2022/10/30/wen-xian-diao-yan-xing-zai-ji-suan-ji-dan-li-zi-xiao-ying-gu-zhang-zhu-ru-fang-zhen/</id>
<published>2022-10-30T14:57:00.000Z</published>
<updated>2022-11-02T15:01:29.388Z</updated>
<content type="html"><![CDATA[<h1 id="名词解释"><a href="#名词解释" class="headerlink" title="名词解释"></a>名词解释</h1><p>OBC——星载计算机(On-Board Computer,OBC)<br>SIP——系统级封装</p><h1 id="空间环境中的辐射效应"><a href="#空间环境中的辐射效应" class="headerlink" title="空间环境中的辐射效应"></a>空间环境中的辐射效应</h1><h2 id="总剂量效应"><a href="#总剂量效应" class="headerlink" title="总剂量效应"></a>总剂量效应</h2><h2 id="单粒子效应"><a href="#单粒子效应" class="headerlink" title="单粒子效应"></a>单粒子效应</h2><h1 id="抗辐照性能的评估方法"><a href="#抗辐照性能的评估方法" class="headerlink" title="抗辐照性能的评估方法"></a>抗辐照性能的评估方法</h1><h2 id="空间环境实验"><a href="#空间环境实验" class="headerlink" title="空间环境实验"></a>空间环境实验</h2><p>空间环境实验以及地面模拟等效实验都有几个很明显的缺点:首先是需要搭建实验评估环境,致使试验成本升高;其次是需要对待评估的模块进行流片试产,如果辐照评估出现问题就会重新调试生产,导致设计周期变长;最后是在实验中观察到单粒子效应后,对系统的错误定位也是目前的技术难点。</p><h2 id="地面模拟等效实验"><a href="#地面模拟等效实验" class="headerlink" title="地面模拟等效实验"></a>地面模拟等效实验</h2><p>随着SiP 封装技术在航天星载计算机设计领域的普及,需要对SiP 封装的芯片进行 辐射效应评估,由于地面实验的加速器粒子能量不够,导致在进行地面辐照试验时难以穿透SiP 封装腔体,所以不能有效的对系统进行评估。</p><h2 id="计算机数值模拟仿真"><a href="#计算机数值模拟仿真" class="headerlink" title="计算机数值模拟仿真"></a>计算机数值模拟仿真</h2><p>而使用计算机仿真技术对星载计算机系统进行抗辐照性能评估,使得设计人员能在设计初期对系统可靠性有准确把握。使用计算机仿真软件可以对空间粒子在器件上的作用机理进行模拟仿真,具有效率高、成本低和效果好等诸多优点。</p><h1 id="单粒子效应仿真方法"><a href="#单粒子效应仿真方法" class="headerlink" title="单粒子效应仿真方法"></a>单粒子效应仿真方法</h1><h2 id="器件级仿真"><a href="#器件级仿真" class="headerlink" title="器件级仿真"></a>器件级仿真</h2><p>器件级单粒子效应仿真是通过 TCAD 仿真工具对器件或者小型电路进行建模,通过求解三大方程得到精确的器件模型。最后来模拟空间环境对器件模型产生的辐照效应,以实现对单粒子效应进行分析,是目前业内单粒子效应机理研究方向的主要仿真方法。</p><h2 id="电路级仿真"><a href="#电路级仿真" class="headerlink" title="电路级仿真"></a>电路级仿真</h2><p>电路级单粒子效应仿真忽略了器件底层的物理细节,更加偏重晶体管层面的仿真,通过用SPICE 工具支持的语法描述单粒子脉冲,实现对电路的单粒子效应故障注入。是研究模拟电路以及小规模数字电路单粒子效应的主流仿真手段。</p><h2 id="逻辑门级仿真"><a href="#逻辑门级仿真" class="headerlink" title="逻辑门级仿真"></a>逻辑门级仿真</h2><p>逻辑门级仿真主要是通过预设网表中每个逻辑门的单位时间故障(Failure in Time,FIT),然后分析Verilog 门级网表结构,从而得到整个门级网表的FIT 和软错误率,这种仿真方法有着计算速度快,支持电路规模大的特点。</p><h2 id="RTL-级仿真"><a href="#RTL-级仿真" class="headerlink" title="RTL 级仿真"></a>RTL 级仿真</h2><p>RTL 级故障注入仿真对电路系统的 RTL 级模型进行仿真,由于仿真方法的模型层次较高因此仿真速度也最快。</p><h1 id="星载计算机系统"><a href="#星载计算机系统" class="headerlink" title="星载计算机系统"></a>星载计算机系统</h1><p><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210272021408.png" alt="|500"><br>通过SiP 封装技术,将系统各模块封装在两个腔体中,其中<font color="#ffc000">腔体1</font> <u>中包括了3 片SRAM 芯片以及两片Flash 芯片</u>,而<font color="#ffc000">腔体2</font> <u>中包括了2 片SRAM 芯片、一片Flash 芯片以及处理器芯片</u>。系统使用倒装技术,片间用金键合丝以及TSV 等技术实现系统互连。</p><ul><li>处理器模块是基于SPARC V8 指令集架构的开源LEON处理器进行开发设计,该模块<u>在系统中负责对外设的控制与通信,程序的读取与执行</u>,是系统的核心。</li><li><u>Flash 与部分SRAM 模块负责程序指令的存储</u>,</li><li><u>其余SRAM 模块负责保存系统的运行数据</u>。</li></ul><p>当系统上电后,处理器先读取保存在Flash 中的boot 初始化程序,对系统寄存器等模块初始化之后,将后续执行的程序,从Flash 转存至指令SRAM中,随后地址跳转到指令SRAM 进行程序的取指、译码、执行、访存、回写等操作。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210272105446.png" alt="|500"></p><p>SRAM的加固 ^a9ac5d</p><p>对存有初始化程序的 Flash,以及存有指令的 SRAM 进行了模块级的三模冗余(Triple Modular Redundancy,TMR)加固设计。<br>加固设计后的目标系统结构就变为下图<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210272109479.png" alt="|500"><br>在为星载计算机系统存入程序时,对<u>三块 Flash 和指令 SRAM 都存储相同的程序</u>,这样在收到外部读取请求信号时,存储器的输出端口中所有数据位都先会通过三输入表决器后再输出到数据总线,从而提高了系统在辐照环境下的工作稳定性。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210291457431.png"><br>采用ECC算法进行SRAM加固模块如下<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210300843699.png"></p><p><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210300845185.png" alt="|600"><br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210300847213.png"> ^df961a</p><h1 id="星载计算机系统模型的搭建"><a href="#星载计算机系统模型的搭建" class="headerlink" title="星载计算机系统模型的搭建"></a>星载计算机系统模型的搭建</h1><h2 id="leon2处理器建模(管级网表的提取)"><a href="#leon2处理器建模(管级网表的提取)" class="headerlink" title="leon2处理器建模(管级网表的提取)"></a>leon2处理器建模(管级网表的提取)</h2><p>想要对OBC-SiP 系统进行单粒子效应仿真,首先就必须得到OBC-SiP 的系统模型。</p><p>Leon2 开源代码由可综合的超高速集成电路硬件描述语言(VHDL)代码构成,为了建立 Leon2 处理器的 SPICE 电路模型并进行单粒子效应仿真,需要将 Leon2 处理器寄存器传输 级(RTL)的 VHDL 代码转换为 SPICE 描述的晶体管级网表,<br>需要如下两个步骤:<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210271629166.png"></p><h3 id="DC综合"><a href="#DC综合" class="headerlink" title="DC综合"></a>DC综合</h3><ul><li><strong>转换</strong><br>综合工具首先会将 Leon2 处理器的 VHDL 的描述转换成一个与工艺独立的 RTL 级网表(网表中 RTL 模块通过连线互联)。</li><li><strong>映射</strong><br>然后根据具体指定的工艺库,将 RTL 级网表映射到工艺库上,成为一个门级网表。</li><li><strong>优化</strong><br>最后再根据设计者施加的诸如延时、面积方面的约束条件,对门级网表进行优化。<blockquote><p>[!info]+ DC综合<br>Synopsys 公司的DC(Design Compiler)是目前业内使用最广泛的电路综合工具,可以很方便地将硬件描述语言的可综合代码设计转换到基于标准单元库的门级网表。<br>DC 主要包括<span style="background:rgba(240, 200, 0, 0.2)">转换(translation)</span>、<span style="background:rgba(240, 200, 0, 0.2)">映射(mapping)</span>及<span style="background:rgba(240, 200, 0, 0.2)">优化(optimization)</span>三个步骤。<br>综合工具首先将电路设计代码设计读入内存,用“gtech.db”库中的基本单元将设计转换为基本电路,然后根据综合人员给定的约束设置,对电路性能、面积等参数进行计算并优化,然后将优化之后的电路映射到用户设定的工艺库上<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210261019502.png" alt="|500"></p></blockquote></li></ul><h3 id="管级网表转换"><a href="#管级网表转换" class="headerlink" title="管级网表转换"></a>管级网表转换</h3><p>Mentor 公司提供的<span style="background:rgba(240, 200, 0, 0.2)"> Calibre工具</span>在数字 IC 后端设计中具有广泛的应用。其中 <span style="background:rgba(240, 200, 0, 0.2)">v2lvs命令</span>可以将 DC 综合生成的门级网表转换为 SPICE 管级网表,为后续的处理器故障注入仿真提供基础文件。</p><blockquote><p>[!info]+ 管级网表转换<br>Mentor Graphics 公司的Calibre 工具在IC 后端设计中必不可少,其中Calibre 中的v2lvs 命令能够很方便的将Verilog 的门级网表转换成我们所需要的SPICE 管级网表<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210261023891.png" alt="|500"></p></blockquote><h2 id="存储器模块建模"><a href="#存储器模块建模" class="headerlink" title="存储器模块建模"></a>存储器模块建模</h2><h3 id="Flash模块建模"><a href="#Flash模块建模" class="headerlink" title="Flash模块建模"></a>Flash模块建模</h3><p>Flash 主要器件结构是浮栅晶体管等特殊器件,由于其工艺的特殊性,无法建立该模块的SPICE 晶体管级模型,因此只对Flash 模块进行行为级建模。</p><h3 id="SRAM-模块建模"><a href="#SRAM-模块建模" class="headerlink" title="SRAM 模块建模"></a>SRAM 模块建模</h3><p>SRAM 模块内部由多个子模块组成,可以大致分为:存储单元电路模块、逻辑接口电路模块、行译码电路模块、列译码器和灵敏放大器电路模块几个部分。存储器设计流程与上文中LEON2 处理器设计流程不同,并且灵敏放大器电路等模块和存储单元等模块使用工艺不同,无法按照“DC 综合-管级网表转换”这样的流程来进行建模。<u>采用 Memory Compiler 工具,直接生成其行为级模型和 SPICE 管级模型</u></p><h1 id="SiP-互连建模"><a href="#SiP-互连建模" class="headerlink" title="SiP 互连建模"></a>SiP 互连建模</h1><p>单根金丝可以用图示的简化模型进行电路等效<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210272114894.png" alt="|500"><br>在对系统各模块进行连接时,依据项目组提供资料,为键合金丝的等效模型选择合适的寄生参数,在SPICE 网表中将系统各模块的端口进行连接。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210272116911.png" alt="|500"></p><h2 id="星载计算机系统模型构建(-Leon2-处理器-外部电路)"><a href="#星载计算机系统模型构建(-Leon2-处理器-外部电路)" class="headerlink" title="星载计算机系统模型构建( Leon2 处理器+外部电路)"></a>星载计算机系统模型构建( Leon2 处理器+外部电路)</h2><p>完成了 OBC-SiP 系统中各模块电路的建模工作之后,就可以对整个目标系统进行模型搭建。依据LEON2 处理器中的可编程存储器控制器设计要求,以及各存储模块的读写时序,通过连线将各模块进行连接,最终得到整个OBC-SiP 目标系统模型。<br>根据获得的 Leon2 处理器 SPICE 管级网表模型,对其进行外部电路扩展,构成一个小型的星载计算机系统模型,在该模型中存在以下几个模块:①Leon2 处理器核;②指令 SRAM 模块;③数据 SRAM 模块;④初始化指令 SRAM 模块。</p><h1 id="单粒子效应故障注入技术"><a href="#单粒子效应故障注入技术" class="headerlink" title="单粒子效应故障注入技术"></a>单粒子效应故障注入技术</h1><p>对于故障注入技术,按照注入的电路层次不同,可以分为硬件故障注入,软件故障注入,RTL 级故障注入与 SPICE 网表故障注入等。</p><h2 id="软件层次故障注入技术"><a href="#软件层次故障注入技术" class="headerlink" title="软件层次故障注入技术"></a>软件层次故障注入技术</h2><p>软件故障注入技术是在软件层次来实现处理器的故障注入方法,在处理器程序编译或者运行时动态的修改执行程序,或者人为设置寄存器值来改变处理器的运行状态。</p><h3 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h3><ul><li>成本低,故障注入位置灵活可控,具有相当高的自由性。</li><li>可通过脚本或者程序可以实现批量的故障注入操作,完成故障注入的全自动化设置。</li></ul><h3 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h3><ul><li>只能对计算机体系结构中可配置的寄存器进行故障注入设置,无法对不可配置部分进行故障注入,具有局限性,无法实现对整体芯片的抗辐射可靠性评估。</li><li>软件层次注入一般只对寄存器进行注入,无法精确模拟单粒子效应的实际注入情况,如注入组合逻辑模块等。</li><li>故障注入技术一般需要添加一段额外的处理器程序如故障注入控制程序,结果收集及分析模块等,因此需要确保额外的程序不会影响到原本处理器的正常工作且不会对故障注入结果造成干扰,这通常是难以精确控制的。</li></ul><h2 id="硬件层次故障注入技术"><a href="#硬件层次故障注入技术" class="headerlink" title="硬件层次故障注入技术"></a>硬件层次故障注入技术</h2><p>硬件层次的故障注入是在芯片流片后后进行是对实际的处理器芯片进行故障注入,通用的方法主要有引脚级注入法、重离子加速注入法、激光注入法以及电源电压注入法等,这是最接近于实际的故障环境。</p><h3 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h3><p>采用对实际芯片进行故障注入的方式来分析芯片的抗单粒子效应能力,可以在实际的处理器芯片中触发瞬态故障,翻转故障等,这是最贴近于实际情况的故障注入方式。</p><h3 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h3><ul><li>需要昂贵的硬件设备,如离子加速器造价昂贵,且资源紧张*</li><li>硬件层次的故障注入极有可能会对芯片造成损伤,代价高昂,增加了设计成本。</li><li>该层次的故障注入对象为实体芯片,必须等芯片流片后才能进行故障注入,增加了芯片的设计与制造周期。</li><li>难以将故障注入技术实现全自动化,因为芯片级的故障注入需要相当数量的故障次数才能使故障注入分析结果具有统计意义,这需要耗费大量的时间。</li><li>难以精确定位引起芯片运行错误的故障注入点。</li></ul><h2 id="RTL-层次故障注入技术"><a href="#RTL-层次故障注入技术" class="headerlink" title="RTL 层次故障注入技术"></a>RTL 层次故障注入技术</h2><p>RTL 级故障注入技术是针对系统电路的行为级模型进行故障注入,其通常用硬件描述语言如 VHDL,verilog HDL 来实现。</p><h3 id="优点-2"><a href="#优点-2" class="headerlink" title="优点"></a>优点</h3><ul><li>硬件描述语言具有对电路良好的建模与抽象功能,可以从不同的抽象级别来对处理器内部各个模块进行单粒子效应故障注入,如行为级故障注入仿真,逻辑门级故障注入仿真。</li><li>在 RTL级可以精确地对每个逻辑门或者接口信号进行数值仿真与分析,所以可以精确地控制故障注入的注入时间,注入位置等,更加有利于监控单粒子注入后处理器系统的故障行为。</li><li>在电路设计阶段就能进行故障注入与单粒子效应敏感性分析。根据分析结果就可以在设计阶段采取相应的加固措施来提高处理器的抗辐射能力,因此降低了抗辐射电路的设计成本与与设计周期,有效地加快了抗辐射芯片的研发速度。</li></ul><h3 id="缺点-2"><a href="#缺点-2" class="headerlink" title="缺点"></a>缺点</h3><p>verilog HDL 等硬件描述语言进行建模的电路属于数字电路,只有 0 与 1 两种工作状态,对应于模拟电路中的高低电平两种状态。使用 RTL 级故障注入技术忽略了单粒子效应的底层物理细节,只是单纯的改变电路的工作状态。而高能粒子入射期间后引发的瞬态脉冲电流有可能位于高低电平之间的中间区域,RTL 级故障注入技术无法精确地对这种类型的粒子入射进行模拟入射。</p><h2 id="SPICE-层次网表故障注入技术"><a href="#SPICE-层次网表故障注入技术" class="headerlink" title="SPICE 层次网表故障注入技术"></a>SPICE 层次网表故障注入技术</h2><p>SPICE 级网表故障注入技术是针对电路的管级网表模型进行仿真的。从管级网表的特征来分析,因为管级网表体现了电路晶体管的物理特性和电学特性,涉及到具体电压值的改变,不是由简单的 0 或者 1 来表示,表示方式更加准确,因此在最底层晶体管级进行故障注入是最贴近于实际情况且最精确的故障注入技术。</p><h3 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h3><ul><li>首先需要对目标处理器系统进行高精度的 SPICE 建模,进而建立正确的管级模型。*</li><li>需要建立准确的故障注入源,一般情况下,管级网表的故障注入源由器件级单粒子效应仿真结果得到的电流响应数据为基础,通过数学模型的拟合,可以得到不同 LET 值的瞬态脉冲电流源模型作为故障注入源。</li></ul><h3 id="优点-3"><a href="#优点-3" class="headerlink" title="优点"></a>优点</h3><p>贴近底层晶体管,能够更加准确实际的描述单粒子效应故障入射的情况。</p><h3 id="缺点-3"><a href="#缺点-3" class="headerlink" title="缺点"></a>缺点</h3><p>在仿真过程了添加了许多物理和电学特性,这导致了 SPICE 管级网表进行仿真的速度过慢,一般只支持小型集成电路。</p><h1 id="SPICE-混合仿真"><a href="#SPICE-混合仿真" class="headerlink" title="SPICE 混合仿真"></a>SPICE 混合仿真</h1><p>既考虑了器件级的仿真精度,又兼顾了电路级、RTL 级的仿真规模与速度,解决了现有方法的部分不足。</p><h2 id="单粒子脉冲注入模型建模"><a href="#单粒子脉冲注入模型建模" class="headerlink" title="单粒子脉冲注入模型建模"></a>单粒子脉冲注入模型建模</h2><p>需要通过器件级单粒子效应仿真来分析单粒子瞬态电流数据,得到单粒子效应在不同LET 条件下,产生的瞬态电流脉冲注入模型。<br>首先通过器件级离子注入仿真粒子入射MOSFET 器件电流数据,对数据进行处理,转换为符合SPICE 语法的PWL 电流源模型,最终得到不同能量粒子轰击半导体器件产生的单粒子瞬态电流脉冲注入模型</p><h2 id="提取电路敏感节点"><a href="#提取电路敏感节点" class="headerlink" title="提取电路敏感节点"></a>提取电路敏感节点</h2><p>对待注入模块的 SPICE 网表进行处理,提取出该模块的敏感节点,得到敏感节点列表文件。</p><blockquote><p>[!info]+ 电路敏感节点<br>NMOS 收集迁移率更高的电子,因此在相同入射条件,相同时间内,NMOS 总能收集更多的载流子从而产生更大的单粒子瞬态电流,体现出更强的单粒子瞬态效应。本文定义电路敏感节点为SPICE 网表中每个NMOS 晶体管的漏极。<u>本文定义电路敏感节点为SPICE 网表中每个NMOS 晶体管的漏极。</u></p></blockquote><h2 id="敏感节点故障注入"><a href="#敏感节点故障注入" class="headerlink" title="敏感节点故障注入"></a>敏感节点故障注入</h2><p>得到待注入模块网表的敏感节点列表之后,就可以对该模块的敏感节点进行单粒子随机或定点故障注入。<br>该步骤用 Perl 脚本实现,自动生成故障注入文件。通过用户设定的<span style="background:rgba(240, 200, 0, 0.2)">单粒子 LET值</span>,<span style="background:rgba(240, 200, 0, 0.2)">注入时间范围</span>,<span style="background:rgba(240, 200, 0, 0.2)">注入时间步长(纳秒)</span>,<span style="background:rgba(240, 200, 0, 0.2)">注入模块名称</span>等参数,脚本会结合待注入模块的敏感节点列表和单粒子脉冲注入模型,对脉冲电流源进行参数处理,得到包含单粒子注入信息的故障注入文件。<br><u></u></p><h2 id="SPICE-混合仿真-1"><a href="#SPICE-混合仿真-1" class="headerlink" title="SPICE 混合仿真"></a>SPICE 混合仿真</h2><p>获得单粒子瞬态脉冲电流源模型以及对待注入模块的敏感节点提取并生成故障注入文件之后,就可以使用 SPICE 仿真工具对目标系统进行单粒子效应仿真。<br>然而对于规模较大的星载计算机系统,直接使用 SPICE 仿真器进行电路级单粒子仿真,需要的仿真时间以及运算资源都会非常大。<br>因此本文(柳炜鑫论文)在电路级基础上进行改进,提出基于SPICE 混合仿真的单粒子效应仿真方法。</p><p><span style="background:rgba(136, 49, 204, 0.2)">第一步</span>,讨论单粒子注入方案,对目标系统进行模块划分。明确单粒子注入位置(<u>如整数单元、总线控制器或者SRAM 等等</u>),根据注入需求,将系统划分成各种功能模块。 </p><p><span style="background:rgba(136, 49, 204, 0.2)">第二步</span>,分别搭建系统模块不同层级的模型。完成目标系统的模块划分后,为了减小仿真中SPICE 网表的规模,本文采用的方案是<u>对待注入模块需要得到SPICE管级模型,而对于不需要进行单粒子故障注入的模块则可以建立其行为级模型或 RTL模型</u>。 </p><p><span style="background:rgba(136, 49, 204, 0.2)">第三步</span>,<u>完成接口网络(Mixed Nets)的搭建。这个模块是用于在混合信号仿真中实现模拟和数字模块之间连接的信号网络,位于模拟模块和数字模块的边界。</u>在混合仿真中,根据需要仿真器需要根据信号的传输方向,将混合信号进行 D2A 转换,以将数字逻辑值转换为模拟电压量,反之亦然。该接口网络为理想网络,D2A 转换中的默认高低电压值分别是SPICE 网表中的VDD 和0V。A2D 转换中默认的电压阈值为VDD 的50%。可以根据需要使用d2a 和a2d 命令对这些值进行修改。</p><p><span style="background:rgba(136, 49, 204, 0.2)">第四步</span>,完成系统的连接。将各模块所搭建的不同层级模型,通过接口网络实现整个目标系统的连接。</p><p><span style="background:rgba(136, 49, 204, 0.2)">第五步</span>,进行混合仿真。得到了目标系统的混合模型,就可以使用 VCS-MX 和Finesim Pro 进行仿真工作。 </p><p><span style="background:rgba(136, 49, 204, 0.2)">第六步</span>,输出仿真结果。等待仿真结束后,依据仿真网表中节点电压测量语句,会将需要观测的节点电压值通过文本的形式进行输出,最终得到单粒子效应仿真的结果。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210272139793.png" alt="|500"></p><p><strong>以OBC-SiP 目标系统模型进行单粒子效应仿真为例</strong><br>首先完成模块划分,<u>本例将处理器和 SRAM 作为待注入模块,因此系统中处理器和 SRAM 使用 SPICE 管级模型,而 Flash 模块使用行为级模型。 </u><br>然后是完成接口网络搭建,对于使用VCS-MX和Finesim Pro进行VHDL / Verilog-SPICE 的流程中,该工具会自动插入A2D 和D2A 模块。<br>接下来使用接口网络完成系统的连接;完成系统混合模型之后,输入命令提交仿真。<br>等待仿真结束,就完成了一次对 OBC-SiP 目标系统的单粒子故障注入仿真,接下来就可以对仿真器输出的结果进行仿真比对以及系统错误统计。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210262147339.png" alt="|500"></p><h1 id="柳师兄论文调研"><a href="#柳师兄论文调研" class="headerlink" title="柳师兄论文调研"></a>柳师兄论文调研</h1><h1 id="荀师兄论文调研"><a href="#荀师兄论文调研" class="headerlink" title="荀师兄论文调研"></a>荀师兄论文调研</h1><p>通过 Perl 与 Shell 脚本实现了仿真中的数据测量、结果比对等工<br>作,并针对混合仿真<u>提出了一种混合电路模块快速划分方法</u>,有效地提升了仿真效率。<br><u>提出了 SiP 系统模块级单粒子效应敏感性的仿真方案</u>;<u>采用三阶矩阵乘法的程序指令,分别针对目标 SiP 系统中的整数处理单元、寄存器堆、缓存、AMBA 总线控制器、存储控制器、指令 SRAM 和数据 SRAM 进行仿真分析</u>,发现数据 SRAM 具有 5.57%的系统软错误率,远高于系统其余模块,需要在系统抗单粒子效应加固设计时重点考虑。<br>基于 SiP 系统模块级单粒子效应敏感性的仿真方案,提出了面积等效的分析方法,进一步实现了全电路的混合仿真。</p><h1 id="吕师兄论文调研"><a href="#吕师兄论文调研" class="headerlink" title="吕师兄论文调研"></a>吕师兄论文调研</h1><p>实现了一种<u>基于断点分析的多点故障注入技术</u>,该技术在传统的单点故障注入方法基础上进行改进而来,通过在单粒子效应系统级仿真过程中加入断点的方式,在上一级仿真结果的基础上加入下一级故障注入点,这样可以有效地多次注入故障注入点,同时保证了相邻故障注入点之间的结果非相干性。<br>通过在星载计算机系统中加载矩阵乘法程序并进行 SPICE 仿真,得到在运行该程序的基础上可以实现的最小故障注入间隔时间。</p>]]></content>
<summary type="html"><h1 id="名词解释"><a href="#名词解释" class="headerlink" title="名词解释"></a>名词解释</h1><p>OBC——星载计算机(On-Board Computer,OBC)<br>SIP——系统级封装</p>
<h1 id="空间</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="星载计算机" scheme="https://www.daipeihong.top/tags/%E6%98%9F%E8%BD%BD%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
<category term="单粒子效应" scheme="https://www.daipeihong.top/tags/%E5%8D%95%E7%B2%92%E5%AD%90%E6%95%88%E5%BA%94/"/>
<category term="leon2处理器" scheme="https://www.daipeihong.top/tags/leon2%E5%A4%84%E7%90%86%E5%99%A8/"/>
</entry>
<entry>
<title>SystemVerilog课程笔记(七)</title>
<link href="https://www.daipeihong.top/2022/10/29/systemverilog-ke-cheng-bi-ji-qi/"/>
<id>https://www.daipeihong.top/2022/10/29/systemverilog-ke-cheng-bi-ji-qi/</id>
<published>2022-10-29T00:55:00.000Z</published>
<updated>2022-11-23T02:01:26.292Z</updated>
<content type="html"><![CDATA[<h1 id="1-利用类的继承,将多种test包含进base-test中"><a href="#1-利用类的继承,将多种test包含进base-test中" class="headerlink" title="1. 利用类的继承,将多种test包含进base test中"></a>1. 利用类的继承,将多种test包含进base test中</h1><h2 id="1-1-改进"><a href="#1-1-改进" class="headerlink" title="1.1 改进"></a>1.1 改进</h2><p>(1)将stimulator、monitor、generator、checker的例化、连接、run放入rt_env类中;<br>(2)genarator_to_stimulator的数据传输语句块放入rt_env类中的task run;<br>(3)将generate_data_proc产生数据的语句块放入对应的单通道/双通道/多通道测试的类中;</p><pre class=" language-systemverilog"><code class="language-systemverilog">class rt_env; //rt_env包含各个组件的例化、连接、run) ...endclass ...endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; ...endclassclass rt_single_ch_test extends rt_base_test;//单个通道测试 ...endclassclass rt_two_ch_test extends rt_base_test;//两个通道测试 ...endclassclass rt_two_ch_same_chout_test extends rt_two_ch_test; ...endclassclass rt_multi_ch_test extends rt_base_test;//多个通道测试 ...endclassclass rt_full_ch_test extends rt_multi_ch_test; ...endclass</code></pre><p>各个class层次结构如下:</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211181107662.png" width="60%" height="60%"></div><h2 id="1-2-改进后的tb-sv"><a href="#1-2-改进后的tb-sv" class="headerlink" title="1.2 改进后的tb.sv"></a>1.2 改进后的tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">class rt_packet; bit [3:0] src; bit [3:0] dst; bit [7:0] data []; function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclassinterface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; int src_chnl_status[int]; //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ?? //generator传送p给stimulator rt_packet pkts[$];//定义stimulator中的的队列pkts function void put_pkt(input rt_packet p); pkts.push_back(p);//将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p) endfunctiontask run(); fork drive_reset(); //reset动作 get_packet_and_drive(); //drive_chnl动作 join_noneendtask task drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive();//drive_chnl //rt_packet_t p; @(negedge intf.reset_n); repeat(10) @(posedge intf.clock);//延迟10个时钟周期 forever begin automatic rt_packet p;//声明一个动态的 wait(pkts.size()>0); p = pkts.pop_front();//将p从队列pkts里面取出 fork//后台触发线程,触发线程在后台运行,继续执行剩下内容 begin wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待 drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据 set_src_chnl_avail(p); end join_none endendtasktask wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待 if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足) src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据) else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待 wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endtaskfunction set_src_chnl_avail(rt_packet p); src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endfunctiontask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator rt_packet pkts[$];//定义队列 function void put_pkt(input rt_packet p); pkts.push_back(p);//将p放入队列pkts里面(在pkts队列尾插入p) endfunction task get_pkt(output rt_packet p); wait(pkts.size() >0 )//队列不为空 p = pkts.pop_front();//将p从队列pkts里面取出,提取队列首元素 endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.src = 0; pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",act_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void do_report(); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count) $display("TEST PASSED!"); else begin $display("TEST FAILED!"); $display("TOTAL ERROR %0d times", error_count); end endfunctionendclass//********************************** **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect sage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator forever begin//此处不需要延迟0,执行task run的时候默认function new已执行 gen.get_pkt(p);//get句柄 stim.put_pkt(p);//put句柄 end end join_none endtask endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; function new(virtual rt_interface intf); env = new(intf); endfunction task run(); fork env.run(); join_none endtaskendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 function new(virtual rt_interface intf); env = new(intf); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 p = new();//obj1(每次put_pkt都需要new一下) p.set_members(0,3,'{8'h33,8'h77});//注意层次变化,在env层次下面 env.gen.put_pkt(p); p = new();//obj2 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); endtask endclass class rt_two_ch_test extends rt_base_test; function new(virtual rt_interface intf); env = new(intf); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf); env = new(intf); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf); env = new(intf); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf); env = new(intf); endfunction endclass//********************************** **********************************////********************************** tb **********************************//module tb;bit clk,rstn;logic [15:0] din, frame_n, valid_n;logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_multi_ch_test multi_ch_test; initial begin : inst_init_proc single_ch_test = new(intf); //multi_ch_test = new(intf); single_ch_test.run(); //multi_ch_test.run(); end endmodule </code></pre><h2 id="1-3-仿真结果"><a href="#1-3-仿真结果" class="headerlink" title="1.3 仿真结果"></a>1.3 仿真结果</h2><h3 id="1-3-1-单通道测试-0→3,0→5"><a href="#1-3-1-单通道测试-0→3,0→5" class="headerlink" title="1.3.1 单通道测试(0→3,0→5)"></a>1.3.1 单通道测试(0→3,0→5)</h3><p>单通道测试:</p><pre class=" language-systemverilog"><code class="language-systemverilog">single_ch_test = new(intf);//multi_ch_test = new(intf);single_ch_test.run();//multi_ch_test.run();</code></pre><p>仿真结果:</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211172147174.png" width="60%" height="60%"></div><h3 id="1-3-2-多通道测试-0→3,0→5,3→6,4→7"><a href="#1-3-2-多通道测试-0→3,0→5,3→6,4→7" class="headerlink" title="1.3.2 多通道测试(0→3,0→5,3→6,4→7)"></a>1.3.2 多通道测试(0→3,0→5,3→6,4→7)</h3><p>多通道测试:</p><pre class=" language-systemverilog"><code class="language-systemverilog">//single_ch_test = new(intf);multi_ch_test = new(intf);//single_ch_test.run();multi_ch_test.run();</code></pre><p>仿真结果:</p><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211172152813.png" width="60%" height="50%"></div><h1 id="2-增加数据传输完成信号、checker的判断、test-name"><a href="#2-增加数据传输完成信号、checker的判断、test-name" class="headerlink" title="2. 增加数据传输完成信号、checker的判断、test name"></a>2. 增加数据传输完成信号、checker的判断、test name</h1><h2 id="2-1-改进"><a href="#2-1-改进" class="headerlink" title="2.1 改进"></a>2.1 改进</h2><p><strong>(1)增加数据传输完成信号,数据传输完成后调用checker里的report进行报告</strong></p><pre class=" language-systemverilog"><code class="language-systemverilog">task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current testendtask</code></pre><p><strong>(2)增加checker比较的判断,需判断exp_out_pkts与mon.out_pkts必须有数据</strong></p><pre class=" language-systemverilog"><code class="language-systemverilog">function void report(); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST PASSED!"); else begin $display("TEST FAILED!"); $display("TOTAL ERROR %0d times", error_count); endendfunctionfunction bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end endendfunction</code></pre><p><strong>(3)增加test的name,并由checker里的report函数打印</strong></p><h2 id="2-2-改进后的tb-sv"><a href="#2-2-改进后的tb-sv" class="headerlink" title="2.2 改进后的tb.sv"></a>2.2 改进后的tb.sv</h2><pre class=" language-systemverilog"><code class="language-systemverilog">class rt_packet; bit [3:0] src; bit [3:0] dst; bit [7:0] data []; function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclassinterface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; int src_chnl_status[int]; //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ?? //generator传送p给stimulator rt_packet pkts[$];//定义stimulator中的的队列pkts function void put_pkt(input rt_packet p); pkts.push_back(p);//将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p) endfunctiontask run(); fork drive_reset(); //reset动作 get_packet_and_drive(); //drive_chnl动作 join_noneendtask task drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive();//drive_chnl //rt_packet_t p; @(negedge intf.reset_n); repeat(10) @(posedge intf.clock);//延迟10个时钟周期 forever begin automatic rt_packet p;//声明一个动态的 wait(pkts.size()>0); p = pkts.pop_front();//将p从队列pkts里面取出 fork//后台触发线程,触发线程在后台运行,继续执行剩下内容 begin wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待 drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据 set_src_chnl_avail(p); end join_none endendtasktask wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待 if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足) src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据) else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待 wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endtaskfunction set_src_chnl_avail(rt_packet p); src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endfunctiontask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator rt_packet pkts[$];//定义队列 function void put_pkt(input rt_packet p); pkts.push_back(p);//将p放入队列pkts里面(在pkts队列尾插入p) endfunction task get_pkt(output rt_packet p); wait(pkts.size() >0 )//队列不为空 p = pkts.pop_front();//将p从队列pkts里面取出,提取队列首元素 endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.src = 0; pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",act_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator forever begin//此处不需要延迟0,执行task run的时候默认function new已执行 gen.get_pkt(p);//get句柄 stim.put_pkt(p);//put句柄 end end join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 1;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 p = new();//obj1(每次put_pkt都需要new一下) p.set_members(0,3,'{8'h33,8'h77});//注意层次变化,在env层次下面 env.gen.put_pkt(p); p = new();//obj2 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclass//********************************** Optional tests **********************************////********************************** tb **********************************//module tb;bit clk,rstn;logic [15:0] din, frame_n, valid_n;logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_multi_ch_test multi_ch_test; initial begin : inst_init_proc single_ch_test = new(intf); //multi_ch_test = new(intf); single_ch_test.run(); //multi_ch_test.run(); end endmodule </code></pre><h2 id="2-3-仿真结果"><a href="#2-3-仿真结果" class="headerlink" title="2.3 仿真结果"></a>2.3 仿真结果</h2><p>同上</p><h1 id="3-package封装与makefile的使用"><a href="#3-package封装与makefile的使用" class="headerlink" title="3. package封装与makefile的使用"></a>3. package封装与makefile的使用</h1><h2 id="3-1-利用package将文件打包,并将package里的内容放入一个单独的文件里面"><a href="#3-1-利用package将文件打包,并将package里的内容放入一个单独的文件里面" class="headerlink" title="3.1 利用package将文件打包,并将package里的内容放入一个单独的文件里面"></a>3.1 利用package将文件打包,并将package里的内容放入一个单独的文件里面</h2><h3 id="rt-test-pkg-sv"><a href="#rt-test-pkg-sv" class="headerlink" title="rt_test_pkg.sv"></a>rt_test_pkg.sv</h3><pre class=" language-systemverilog"><code class="language-systemverilog">package rt_test_pkg;class rt_packet; bit [3:0] src; bit [3:0] dst; bit [7:0] data []; function new(); endfunction function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []); this.src = src; this.dst = dst; this.data = data; endfunction function string sprint();//打印packet信息的函数 sprint = {sprint , $sformatf("src = %0d\n",src)}; sprint = {sprint , $sformatf("dst = %0d\n",dst)}; sprint = {sprint , $sformatf("data_length = %0d\n",data.size())}; foreach(data[i]) sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])}; endfunction function bit compare(rt_packet p);//输入exp_pkt if(dst == p.dst && data == p.data) compare = 1; else compare = 0; endfunctionendclass//********************************** stimulator **********************************//class rt_stimulator; virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针//for debug purpose from waveform//定义检测状态的变量 typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t; drv_state_t dbg_state; byte unsigned dbg_din_chnl0_data; int src_chnl_status[int]; //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ?? //generator传送p给stimulator rt_packet pkts[$];//定义stimulator中的的队列pkts function void put_pkt(input rt_packet p); pkts.push_back(p);//将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p) endfunctiontask run(); fork drive_reset(); //reset动作 get_packet_and_drive(); //drive_chnl动作 join_noneendtask task drive_reset();//reset forever begin @(negedge intf.reset_n); dbg_state <= DRV_RESET; intf.din <= 0; intf.frame_n <= '1;//等效16'hFFFF intf.valid_n <= '1; endendtask// 发送数据task get_packet_and_drive();//drive_chnl //rt_packet_t p; @(negedge intf.reset_n); repeat(10) @(posedge intf.clock);//延迟10个时钟周期 forever begin automatic rt_packet p;//声明一个动态的 wait(pkts.size()>0); p = pkts.pop_front();//将p从队列pkts里面取出 fork//后台触发线程,触发线程在后台运行,继续执行剩下内容 begin wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待 drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据 set_src_chnl_avail(p); end join_none endendtasktask wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待 if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足) src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据) else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待 wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endtaskfunction set_src_chnl_avail(rt_packet p); src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)endfunctiontask drive_chnl(rt_packet p); $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint()); // drive address phase 输入地址位阶段for(int i=0;i<4;i++)begin //4 clock @(posedge intf.clock); dbg_state <=DRV_ADDR; intf.din[p.src] <= p.dst[i]; intf.valid_n[p.src] <= $urandom_range(0,1);//valid_n在din的地址输入时间段可为任意值x intf.frame_n[p.src] <= 1'b0;//frame_n需要为低end // drive pad phase //隔离阶段 for (int i=0;i<5;i++)begin //5 clock @(posedge intf.clock); dbg_state <=DRV_PAD; intf.din[p.src] <= 1'b1; intf.valid_n[p.src] <= 1'b1;//valid_n需为高电平 intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平 end // drive data phase 传输数据阶段 foreach(p.data[id])begin for(int i=0;i<8;i++)begin @(posedge intf.clock); dbg_state <=DRV_DATA; dbg_din_chnl0_data <= p.data[id]; intf.din[p.src] <= p.data[id][i]; intf.valid_n[p.src] <=1'b0; intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高 end end// drive idle phase 闲置(没有数据传输)阶段 @(posedge intf.clock); dbg_state <=DRV_IDLE; dbg_din_chnl0_data <= 0; intf.din[p.src] <= 1'b0; intf.valid_n[p.src] <= 1'b1; intf.frame_n[p.src] <= 1'b1; $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);endtaskendclass//********************************** generator **********************************//class rt_generator;//generator产生数据交给stimulator rt_packet pkts[$];//定义队列 function void put_pkt(input rt_packet p); pkts.push_back(p);//将p放入队列pkts里面(在pkts队列尾插入p) endfunction task get_pkt(output rt_packet p); wait(pkts.size() >0 )//队列不为空 p = pkts.pop_front();//将p从队列pkts里面取出,提取队列首元素 endtask //generate a random packet function void gen_pkt(int src = -1, int dst = -1); endfunction task run(); //TODO endtask endclass//********************************** monitor **********************************//class rt_monitor;virtual rt_interface intf; rt_packet in_pkts[16][$]; rt_packet out_pkts[16][$]; task run(); fork mon_chnls(); join_noneendtask task mon_chnls; foreach(in_pkts[i]) begin automatic int chid = i; fork mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入 mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入 join_none endendtask task mon_chnl_in (bit[3:0]id);//监测数据输入的任务 rt_packet pkt;//定义结构体变量 forever begin //clear content for the same struct variable。清除pkt pkt = new(); pkt.src = id;//第id个输入端口 //monitor specific channel-in data and put it into the queue // monitor address phase @(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动) for(int i=0; i<4; i++)begin @(negedge intf.clock);//frame_n下降沿后监测4个clk negedge pkt.dst[i]= intf.din[id]; end $display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst); //pass pad phase 不考虑pad阶段是否满足协议要求 repeat(5) @(negedge intf.clock); do begin pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data for ( int i=0; i<8; i++) begin @(negedge intf.clock); //在8个clk negedge监测8bit数据 pkt.data[pkt.data.size-1][i] = intf.din[id]; end end while(!intf.frame_n[id]); in_pkts[id].push_back(pkt);//将monitor拿到的数据放入in_pkts $display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint()); endendtasktask mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务 rt_packet pkt; forever begin //clear content for the same struct variable pkt = new(); pkt.src = 0; pkt.dst = id; @(negedge intf.frameo_n[id]); $display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst); do begin pkt.data = new [ pkt.data.size + 1](pkt.data); for(int i=0;i<8; i++) begin @(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低 pkt.data[pkt.data.size-1][i]= intf.dout [id]; end end while(!intf.frameo_n[id]); out_pkts[id].push_back(pkt); $display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint()); //monitor specific channel-out data and put it into the queue endendtaskendclass//********************************** checker **********************************//class rt_checker; int unsigned compare_count; int unsigned error_count; function new();//赋初值,可有可无,整形变量默认初始值为0 compare_count = 0; error_count = 0; endfunction rt_packet exp_out_pkts[16][$]; rt_monitor mon; task run(); foreach(exp_out_pkts[i])begin automatic int chid = i; fork do_routing(chid); do_compare(chid); join_none end endtask task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中 rt_packet pkt; forever begin wait(mon.in_pkts[id].size > 0); pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl end endtask task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出 rt_packet exp_pkt, act_pkt; forever begin wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值 act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据 exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据 if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1 $display("[CHK] data compare success with packet : \n%s",act_pkt.sprint()); end else begin $display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint()); error_count++; end compare_count++; end endtask function void report(string name); $display("TOTAL COMPARING %0d times",compare_count); if(!error_count && check_data_buffer())//判断无误且二者有数据 $display("TEST [%s] PASSED!",name); else begin $display("TEST [%s]FAILED!",name); $display("TOTAL ERROR %0d times", error_count); end endfunction function bit check_data_buffer(); check_data_buffer = 1; foreach(exp_out_pkts[id])begin if(exp_out_pkts[id].size != 0)begin//exp_out_pkts必须有数据 check_data_buffer = 0; $display("exp_out_pkts[%0d] buffer size is not 0(still with %0d data)",id,exp_out_pkts[id].size); end if(mon.out_pkts[id].size != 0)begin//mon.out_pkts必须有数据 check_data_buffer = 0; $display("mon.out_pkts[%0d] buffer size is not 0(still with %0d data)",id,mon.out_pkts[id].size); end end endfunction endclass//********************************** Optional tests **********************************//class rt_env;//rt_env包含各个组件 rt_stimulator stim; rt_monitor mon; rt_generator gen; rt_checker chk; function new(virtual rt_interface intf); //build stage,例化 stim = new(); gen = new(); mon = new(); chk = new(); //connect stage,连接 stim.intf = intf; mon.intf = intf; chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列 endfunction task run(); rt_packet p; //run stage,run fork stim.run();//class里面的函数不会自动调用,需要手动调用 gen.run(); mon.run(); chk.run(); begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator forever begin//此处不需要延迟0,执行task run的时候默认function new已执行 gen.get_pkt(p);//get句柄 stim.put_pkt(p);//put句柄 end end join_none endtask function void report(string name); chk.report(name); endfunction endclassclass rt_base_test;//rt_base_test包含rt_env,rt_env包含各个组件 rt_env env; bit gen_trans_done = 0;//表示数据传输未开始 int unsigned test_drain_time_us = 1;//数据传输完后等待报告的时间 string name; function new(virtual rt_interface intf,string name = "rt_base_test"); env = new(intf); this.name = name; endfunction task run(); $display("TEST %s started",name); fork env.run(); report();//调用report join_none endtask task report(); wait(gen_trans_done == 1); #(test_drain_time_us * 1us); env.report(name);//调用env里的chk.report()进行report $finish();//terminates the current test endtask function void set_trans_done(bit done = 1);//将set_trans_done信号置为1,表示数据传输已完成 gen_trans_done = done; endfunctionendclass class rt_single_ch_test extends rt_base_test;//单个通道测试 function new(virtual rt_interface intf,string name = "rt_single_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run();//调用父类的run,即执行env.run(),进行组件的例化等操作 p = new();//obj1(每次put_pkt都需要new一下) p.set_members(0,3,'{8'h33,8'h77});//注意层次变化,在env层次下面 env.gen.put_pkt(p); p = new();//obj2 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_two_ch_test extends rt_base_test; function new(virtual rt_interface intf,string name = "rt_two_ch_test"); super.new(intf,name); endfunction endclass class rt_two_ch_same_chout_test extends rt_two_ch_test; function new(virtual rt_interface intf,string name = "rt_two_ch_same_chout_test"); super.new(intf,name); endfunction endclass class rt_multi_ch_test extends rt_base_test;//多通道测试 function new(virtual rt_interface intf,string name = "rt_multi_ch_test"); super.new(intf,name); endfunction task run(); rt_packet p; super.run(); p = new(); p.set_members(0,3,'{8'h33,8'h77}); env.gen.put_pkt(p); p = new();//每次put_pkt都需要new一下 p.set_members(0,5,'{8'h55,8'h66}); env.gen.put_pkt(p); p = new(); p.set_members(3,6,'{8'h77,8'h88,8'h22}); env.gen.put_pkt(p); p = new(); p.set_members(4,7,'{8'haa,8'hcc,8'h33}); env.gen.put_pkt(p); set_trans_done(); endtask endclass class rt_full_ch_test extends rt_multi_ch_test; function new(virtual rt_interface intf,string name = "rt_full_ch_test"); super.new(intf,name); endfunction endclassendpackage//********************************** Optional tests **********************************//</code></pre><h3 id="lab7tb-sv"><a href="#lab7tb-sv" class="headerlink" title="lab7tb.sv"></a>lab7tb.sv</h3><pre class=" language-systemverilog"><code class="language-systemverilog">interface rt_interface(); logic clock; logic reset_n; logic [15:0] din; logic [15:0] frame_n; logic [15:0] valid_n; logic [15:0] dout; logic [15:0] valido_n; logic [15:0] busy_n; logic [15:0] frameo_n;endinterface//********************************** tb **********************************//module tb;import rt_test_pkg:: * ; bit clk,rstn;logic [15:0] din, frame_n, valid_n;logic [15:0] dout, valido_n, busy_n, frameo_n;// 产生时钟,周期为10nsinitial forever #5ns clk <= !clk;// 产生复位信号 initial begin #2ns rstn <= 1; #10ns rstn <= 0; #10ns rstn <= 1; end//例化router为DUTrouter dut( .reset_n(rstn), .clock(clk), .frame_n(intf.frame_n), .valid_n(intf.valid_n), .din(intf.din), .dout(intf.dout), .busy_n(intf.busy_n), .valido_n(intf.valido_n), .frameo_n(intf.frameo_n));rt_interface intf();//例化接口 assign intf.reset_n = rstn; assign intf.clock = clk; rt_single_ch_test single_ch_test; rt_multi_ch_test multi_ch_test; initial begin : inst_init_proc single_ch_test = new(intf); multi_ch_test = new(intf); //single_ch_test.run(); multi_ch_test.run(); end endmodule</code></pre><h3 id="VCS命令"><a href="#VCS命令" class="headerlink" title="VCS命令"></a>VCS命令</h3><blockquote><p>vcs -full64 -debug_access+all -sverilog -timescale=1ns/1ps <span style="background:rgba(5, 117, 197, 0.2)">router.v</span> <span style="background:rgba(136, 49, 204, 0.2)">rt_test_pkg.sv</span> <span style="background:#affad1">lab7tb.sv</span> -top tb<br>//注意文件顺序,lab7tb.sv中的类是在rt_test_pkg.sv中创建的,rt_test_pkg.sv应在前面</p></blockquote><div align="center"><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211180958453.png" width="45%" height="45%"></div><h2 id="3-2-使用makefile"><a href="#3-2-使用makefile" class="headerlink" title="3.2 使用makefile"></a>3.2 使用makefile</h2><p>使用脚本包含命令及其组合,makefile代码如下:</p><pre class=" language-systemverilog"><code class="language-systemverilog">############################ User variables###########################TB = tbSEED = 1TESTNAME ?= rt_single_ch_testFILES = router.v rt_test_pkg.sv lab7tb.sv #User Defination############################ Environment variables###########################COMP = vcs -full64 -sverilog -debug_access+all -timescale=1ns/1ps -l comp.log $(FILES)RUN = ./$(TB).simv -l run.log -sml +ntb_random_seed=$(SEED) +TESTNAME=$(TESTNAME)comp: $(COMP) -top $(TB) -o $(TB).simvrun: $(RUN)rung: $(RUN) -gui</code></pre><p>相关命令:</p><blockquote><p>make comp //编译当前路径下的makefile文件,生成命令<br>make run/make rung //执行run或rung的内容,生成log文件,make rung会调用vcs的可视化窗口</p></blockquote><h2 id="3-3对makefile增加输入参数"><a href="#3-3对makefile增加输入参数" class="headerlink" title="3.3对makefile增加输入参数"></a>3.3对makefile增加输入参数</h2><p>将外部输入参数添加到tb文件中:</p><pre class=" language-systemverilog"><code class="language-systemverilog"> initial begin : Select_the_test string name; single_ch_test = new(intf); multi_ch_test = new(intf); //single_ch_test.run(); //multi_ch_test.run(); if($value$plusargs("TESTNAME=%s",name))begin//$value$plusargs作用:运行仿真时输入参数 case(name) "rt_single_ch_test" : single_ch_test.run();//输入rt_single_ch_test,调用对应run,执行输入rt_single_ch_test "rt_multi_ch_test" :multi_ch_test.run(); default:$fatal("[ERRTEST],test name %s is invalid,please specity a valid name!0",name); endcase end end</code></pre><p>相关命令:</p><blockquote><p>make rung TESTNAME=rt_multi_ch_test & //传入参数rt_multi_ch_test,进行多通道测试<br>make rung TESTNAME=rt_single_ch_test & //传入参数rt_single_ch_test,进行多通道测试</p></blockquote><h2 id="3-4-仿真结果"><a href="#3-4-仿真结果" class="headerlink" title="3.4 仿真结果"></a>3.4 仿真结果</h2><p>同上</p>]]></content>
<summary type="html"><h1 id="1-利用类的继承,将多种test包含进base-test中"><a href="#1-利用类的继承,将多种test包含进base-test中" class="headerlink" title="1. 利用类的继承,将多种test包含进base test中"></</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="数字前端" scheme="https://www.daipeihong.top/tags/%E6%95%B0%E5%AD%97%E5%89%8D%E7%AB%AF/"/>
<category term="SystemVerilog验证" scheme="https://www.daipeihong.top/tags/SystemVerilog%E9%AA%8C%E8%AF%81/"/>
</entry>
<entry>
<title>lightroom后期(四)局部曝光的调整</title>
<link href="https://www.daipeihong.top/2022/10/28/lightroom-hou-qi-si-ju-bu-pu-guang-de-diao-zheng/"/>
<id>https://www.daipeihong.top/2022/10/28/lightroom-hou-qi-si-ju-bu-pu-guang-de-diao-zheng/</id>
<published>2022-10-28T10:48:00.000Z</published>
<updated>2022-10-28T12:39:25.511Z</updated>
<content type="html"><![CDATA[<h1 id="一、渐变滤镜"><a href="#一、渐变滤镜" class="headerlink" title="一、渐变滤镜"></a>一、渐变滤镜</h1><p>渐变滤镜的作用呢,是创造有过度的曝光调整,可以是生硬的过度,也可以是柔和的过度。软过度的调整呢,让照片中的光线看起来要更自然。<br>拍日落的时候,当太阳逐渐降低到地平线后,天空的曝光很亮,但是地面的建筑阴影这些背光的位置很暗。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210281956398.png"><br>这个时候就可以使用渐变滤镜来调整。在照片中曝光有差别的位置,拉一个渐变滤镜,通常是在地平线的位置。过度要稍微的软一些,也就是这三条线的距离要大一点。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282000844.png"><br>在参数的设置里,可以按照上一节([[lightroom后期(三)提升照片质感]])对比度和立体感的调整方法来设置参数<br>可以添加一个明亮度范围蒙板<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282002665.png"><br>在范围这里边,左侧是暗部,右侧是亮部,点击显示明亮度蒙板就可以看到调整的过程当中蒙板的变化。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282005945.png"><br>同样也可以增加阴影来调高所有的阴影位置,在这个案例当中,可以搭配阴影和明亮度蒙板来调整建筑与天空之间的过渡。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282008223.png"><br>对比一下原片,可以看到高光的位置,也就是天空被压暗,而建筑呢被提亮。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282009769.png"><br>无论是1/2还是1/3的构图方法,甚至是左右分割以及对角线曝光不平衡的画面,只要是大面积的需要调整曝光的时候呢,都可以使用渐变滤镜来解决。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282011038.png"></p><h1 id="二、径向滤镜"><a href="#二、径向滤镜" class="headerlink" title="二、径向滤镜"></a>二、径向滤镜</h1><p>由于径向滤镜的覆盖面积,是圆形或者是椭圆形的,在调整高光的时候要更方便,在城市建筑以及街景的案例当中更为常用。例如这张照片左侧的树和右侧的建筑,刚好形成了一个框架,照片中间的建筑和天空被阳光直射,使用径向滤镜刚好达到完美的覆盖这个区域。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282013415.png"><br>在径向滤镜当中,比渐变滤镜啊要多一个选项,叫做羽化,指的是滤镜周围的过渡,大多数的情况下呢,都需要使用比较大的羽化,让光线过度更自然,勾选左下角的显示选定的蒙板叠加,就可以看到现在径向滤镜所影响的范围。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282015936.png"><br>降低羽化会让滤镜的边缘过渡非常的生硬,所以经常我们要加大羽化的数值,营造比较自然柔和的光线。</p><h1 id="三、调整画笔"><a href="#三、调整画笔" class="headerlink" title="三、调整画笔"></a>三、调整画笔</h1><p>调整画笔是非常重要的细节修补工具,特别是城市照片这种细节特别多的题材,例如这张照片当中,酒店建筑外观的立体感稍微有点差,渐变滤镜和径向滤镜的覆盖范围又没有办法在这个位置发挥很好的作用,那就刚好用到了调整画笔。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282018972.png"></p><p>后期思路:降低屋顶曝光,提高建筑白色部分曝光度,增大与屋顶对比。添加白色色阶,降低黑色色阶,让窗户的这个窗户框能够变得更明显,增加一点点的纹理。通过这样的分区调整屋顶以及楼体的曝光,就可以实现将建筑细节重塑曝光的效果。<br>对比原片,就可以看到现在建筑拥有更立体的效果。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210282022793.png"></p><p>以上这三个工具的区别,可以理解为覆盖面积的不一样。<br>渐变滤镜的面积最大,径向滤镜适合圆形的范围调整;画笔适应能力最强,它的覆盖面积最小,可以自由的选择想要调整的位置以及形状,是精修细节的最好选择。组合搭配这三个工具,就可以实现特别的光影效果,特别是在调整黑白照片当中,利用局部曝光的明暗对比来提亮主体,压暗阴影,获得更具有戏剧性的视觉效果。</p><h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p><a href="https://www.bilibili.com/video/BV15b4y1Q7xH/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=83d80c57e4377b69f911cc68016fb835">【Edi x TourBox后期训练营】光影的后期再造秘籍 - 局部曝光的调整对照片的微妙作用_哔哩哔哩_bilibili</a></p>]]></content>
<summary type="html"><h1 id="一、渐变滤镜"><a href="#一、渐变滤镜" class="headerlink" title="一、渐变滤镜"></a>一、渐变滤镜</h1><p>渐变滤镜的作用呢,是创造有过度的曝光调整,可以是生硬的过度,也可以是柔和的过度。软过度的调整呢,让照片中的光</summary>
<category term="摄影后期" scheme="https://www.daipeihong.top/categories/%E6%91%84%E5%BD%B1%E5%90%8E%E6%9C%9F/"/>
<category term="lightroom" scheme="https://www.daipeihong.top/tags/lightroom/"/>
</entry>
<entry>
<title>lightroom后期(三) 提升照片质感</title>
<link href="https://www.daipeihong.top/2022/10/27/lightroom-hou-qi-san-ti-sheng-zhao-pian-zhi-gan/"/>
<id>https://www.daipeihong.top/2022/10/27/lightroom-hou-qi-san-ti-sheng-zhao-pian-zhi-gan/</id>
<published>2022-10-27T06:32:00.000Z</published>
<updated>2022-10-28T12:33:03.615Z</updated>
<content type="html"><![CDATA[<h1 id="一、对比度"><a href="#一、对比度" class="headerlink" title="一、对比度"></a>一、对比度</h1><h2 id="亮度对比"><a href="#亮度对比" class="headerlink" title="亮度对比"></a>亮度对比</h2><p>改变光线的明暗差别,可以提高物体的立体感。当有光照到的亮部和阴影位置的暗部差别很大的时候,物体看起来更加立体。所以天气不好或者阴天的时候建筑立体感看起来要差一些。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210271427490.png"><br>大范围设置:<font color="#0070c0">对比度</font><br>更精细设置:<font color="#0070c0">高光</font>和<font color="#0070c0">阴影</font>以及<font color="#0070c0">黑白色阶</font>设置,它基本上只影响了直方图左右两端的曲线,也就是非常亮和非常暗的区域(直方图从左到右分别是黑色色阶、阴影、曝光度、高光和白色色阶)。 </p><h2 id="颜色的明亮度对比"><a href="#颜色的明亮度对比" class="headerlink" title="颜色的明亮度对比"></a>颜色的明亮度对比</h2><p><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210271428890.png"><br>在<font color="#0070c0">HSL颜色工具中的</font><font color="#0070c0">明亮度</font>设置,可以将主体和背景的颜色拉开对比, 同样可以获得很好的立体感提升。</p><h1 id="二、清晰度"><a href="#二、清晰度" class="headerlink" title="二、清晰度"></a>二、清晰度</h1><p><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210271429623.png"><br><font color="#0070c0">纹理</font>是用来增加细节的很好方法,特别是建筑纹理或者不同材质材料的纹路。<br><font color="#0070c0">清晰度</font>的调整范围要比纹理更大,不仅影响纹理,还影响整张照片所有物体边缘的明暗对比。增加清晰度可以看到阴影部分变得更暗,<u>它和对比度参数最大的不同是:对比度设置会降低暗部提高亮度,提高亮部的亮度。而清晰度设置对物体边缘暗部的影响要更大,而高光只有一点点变化。</u></p><h1 id="三、锐化"><a href="#三、锐化" class="headerlink" title="三、锐化"></a>三、锐化</h1><p>在Lightroom的细节工具当中,<font color="#0070c0">锐化</font>是后期时常用的细节增强工具。锐化会给照片中所有的物体边缘带来非常强烈的对比。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210271424301.png"><br><font color="#00b050">数量参数</font>通常控制在50以下,避免过度锐化带来更多噪点。<br><font color="#00b050">半径参数</font>指锐化边缘的范围,半径数值从0到3.0像素,避免过度使用的话控制在1-1.5之间。<br><font color="#00b050">蒙版设置</font>是用来去掉一些不该被锐化的区域,例如只想要锐化建筑,但是并不想影响云和天空,添加蒙版就可以去掉这些区域的锐化效果</p><h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p><a href="https://www.bilibili.com/video/BV1nz4y117Ub/">【Edi x TourBox后期训练营】提高照片质感的方法 - 对比度/清晰度/锐化可以为照片带来什么样改变_哔哩哔哩_bilibili</a></p>]]></content>
<summary type="html"><h1 id="一、对比度"><a href="#一、对比度" class="headerlink" title="一、对比度"></a>一、对比度</h1><h2 id="亮度对比"><a href="#亮度对比" class="headerlink" title="亮度对比"</summary>
<category term="摄影后期" scheme="https://www.daipeihong.top/categories/%E6%91%84%E5%BD%B1%E5%90%8E%E6%9C%9F/"/>
<category term="lightroom" scheme="https://www.daipeihong.top/tags/lightroom/"/>
<category term="提升质感" scheme="https://www.daipeihong.top/tags/%E6%8F%90%E5%8D%87%E8%B4%A8%E6%84%9F/"/>
</entry>
<entry>
<title>车规芯片(四) leon2处理器</title>
<link href="https://www.daipeihong.top/2022/10/24/che-gui-xin-pian-si-leon2-chu-li-qi/"/>
<id>https://www.daipeihong.top/2022/10/24/che-gui-xin-pian-si-leon2-chu-li-qi/</id>
<published>2022-10-24T12:21:00.000Z</published>
<updated>2022-10-30T01:00:17.777Z</updated>
<content type="html"><![CDATA[<h1 id="一、简介"><a href="#一、简介" class="headerlink" title="一、简介"></a>一、简介</h1><p><span style="background:rgba(240, 200, 0, 0.2)">leon2处理器是一款开源的、符合SPARC(可扩展处理器体系架构) V8规范的、采用RISC 结构的32位处理器I P 核。</span>它可以从互联网上免费下载使用。Leon2是以VHDL 形式存在的软核、完全可综合、内部硬件资源可裁剪、主要面向嵌入式应用系统、可以用FPGA/CPLD 和ASIC等技术实现。</p><h1 id="二、leno2处理器结构"><a href="#二、leno2处理器结构" class="headerlink" title="二、leno2处理器结构"></a>二、leno2处理器结构</h1><h3 id="处理器单元"><a href="#处理器单元" class="headerlink" title="处理器单元"></a>处理器单元</h3><p>处理器单元由<font color="#ff0000">整数单元IU</font>、<font color="#ff0000">浮点单元FPU</font>、<font color="#ff0000">协处理器单元CP</font>构成。</p><ul><li>整数单元的特点有:5级指令流水、分离的数据和指令Cache、支持2~32个寄存器窗口、可选的4个观察口寄存器、可配置乘法器、可选的16×16位MAC(40位累加器)、基2除法器。</li><li>可支持的浮点处理器有GaislerResearch的GRFPU,Sun Microsystems的Meiko FPU或其他通用浮点处理单元。</li><li>Leon2提供了一个通用的用户可定义的协处理器,同IU并行运行增强了系统功能。</li></ul><h3 id="Cache子系统"><a href="#Cache子系统" class="headerlink" title="Cache子系统"></a>Cache子系统</h3><p>可配置的模式有直接映射模式和2~4组相联的多组相联模式;可选的三种替换算法是LRU、LRR和伪随机。</p><h3 id="片上外设"><a href="#片上外设" class="headerlink" title="片上外设"></a>片上外设</h3><p>片上外设包括:2个中断控制器、2个UART、2个Timer和1个Watchdog、16位的I/O口、存储器控制器(PROM、SRAM、S13RAM)、PCI桥接器、Ethernet接口、高级片上调试支持单元(DSU)和跟踪缓冲器等.中断控制器可以最大处理46个内部和外部中断。2个串行通信口 (UART),支持8位数据帧、1位校验位、1位停止位,支持硬件流控功能。调试支持单元(DSU)能够把处理器设置到调试模式,通过它可以读写处理器的所有寄存器和Cache。DSU还包括一个跟踪缓存,可以保存已执行了的指令和AHB上传输的数据。</p><p>开源的 LEON2 处理器版本中包括:<u>整数处理单元(Integer Unit,IU),独立的指令缓存(I-Cache)模块和数据缓存(D-Cache)模块,16 位I/O 端口,存储器控制器模块(Memory Controller)等等。</u>根据需求,设计者可以通过片上AMBA AHB/APB 总线控制器实现外设的添加。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210260836471.png" alt="|500"></p><h1 id="三、Leon2的技术特点"><a href="#三、Leon2的技术特点" class="headerlink" title="三、Leon2的技术特点"></a>三、Leon2的技术特点</h1><p>Leon2的技术特点主要有:<span style="background:rgba(240, 107, 5, 0.2)">采用SPARC V8结构</span>、<span style="background:rgba(240, 107, 5, 0.2)">采用内部AMBA总线结构</span>、<span style="background:rgba(240, 107, 5, 0.2)">容错设计</span>和<span style="background:rgba(240, 107, 5, 0.2)">VHDL编程风格</span>。</p><h2 id="SPARC-V8"><a href="#SPARC-V8" class="headerlink" title="SPARC V8"></a>SPARC V8</h2><p>SPARC是可扩展处理器体系架构的首字母缩略词,是一个从RISC派生出的CPU指令集结构(ISA)。指令集结构是指:定义了指令、寄存器、指令和数据存储器、指令执行对寄存器和存储器的影响、控制指令执行的算法等内容,但不定义时钟周期、每条指令的执行时钟周期数(CPI)、数据通路等内容。作为一个结构,SPARC允许在具有不同性能价格比的广泛应用中,实现不同系列的芯片和系统,包括科学、工程、编程、实时和商业应用等。<span style="background:rgba(240, 200, 0, 0.2)">SPARC的设计目标是优化编译器和易于硬件流水线实现。</span></p><h3 id="指令集架构"><a href="#指令集架构" class="headerlink" title="指令集架构"></a>指令集架构</h3><p> 所有指令都编码成32位格式,可以分成六个基本的类型,一共有72条指令。六种基本的指令分别是:①Load/store;②Arthmetic/logical/shift;③Control transfer;④Read/write control register;⑤Floating-point operate;⑥Coprocessor operate。<br> ### 寄存器窗口<br>它是SPARC的最大技术特点。SPARC处理器包括两种寄存器:一种是<span style="background:rgba(240, 200, 0, 0.2)">通用寄存器</span>,另一种是<span style="background:rgba(240, 200, 0, 0.2)">控制/状态寄存器</span>。其中整数单元中的控制/状态寄存器主要负责记录<br>处理器状态和计数等任务,包括PSR,WIM,TBR,PC,ASRs 等;而整数单元的通用寄存器,则<u>采用独特寄存器窗口的实现方式来提高处理器运行效率</u>,这也是SPARC 架构区别于架构设计之处。<br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210260939296.png"><br><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210260941294.png"><br>IU的通用寄存器叫r寄存器。一个IU(整数元)的实现可能包括40<del>520个32位r寄存器。这些寄存器又被分成8个全局寄存器,再加上2</del>32个与实现有关的16位寄存器组,每一个寄存器组又进一步分为8个输入寄存器和8个输出寄存器。<br> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210260926694.png" alt="|500"><br> <img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202210260932765.png" alt="|500"><br>上图是一个8窗口寄存器结构示意图。在任何一个时刻,一条指令只能访问8个全局寄存器和由当前窗口指针(CWP)指定的当前窗口。这个窗口是由8个输入寄存器、8个局部寄存器和8个输入寄存器构成。从图中可以看出,两个相邻窗口的入和出寄存器是共享的。<u>当前是按窗口号顺序(或前或后)转换的,正好上一次的输出寄存器成为当前窗口的输入寄存器,这样可以减少存储器读写和运行时的现场保护。</u></p><h2 id="AMBA"><a href="#AMBA" class="headerlink" title="AMBA"></a>AMBA</h2><p> AMBA(Advanced Microntroller Bus Architecture)规范,是一种已制定的、开放的规范,充当着SoC设计的架构,正迅速成为SoC和IP库开发事实上的标准,为高性能嵌入式微控制器设计定义了一种片上通信标准。<br> AMBA规范中定义了三种不同的总线,即 AHB 、ASB 和 APB 。<br> * AHB 是为高性能、高时钟频率的系统模块提供的,担任着高性能系统的背板总线、支持多处理器、片上各种存储器和片外外部存储器接口连接到低功耗辅助宏单元。<br> * ASB也是为高性能系统模块提供,当AHB 的高性能特点无需要时,就可以用ASB 来代替;它也支持多处理器、片上各种存储器和片外外部存储器接口连接到低功耗辅助宏单元。<br> * APB 是为低功耗的外围设备提供的,它优化到为最小功耗和减小接口的复杂性来支持辅助功能。</p><h2 id="容错问题"><a href="#容错问题" class="headerlink" title="容错问题"></a>容错问题</h2><p>为了适用于航空航天的高可靠性应用,Leon2采用多层次的容错策略;奇偶校验、TMR(三模冗余)寄存器、片上EDAC(检错和纠错)、流水线重启、强迫Cache不命中等.尽管现在几乎所有CPU都有一些常规的容错措施,如奇偶校验、流水线重启等,像IBM S/390 G5还采用了写阶段以前的全部流水线复制技术。IntelItanium采用的混合ECC和校验编码等技术;但远没有Leon2那样,采用如此全面的容错措施。</p><p>Leon2将时序(存储)单元的状态翻转作为数字容错的主要内容,根据时序逻辑的不同特点和性质,采用了不同的容错技术和手段.</p><h3 id="Cache的容错"><a href="#Cache的容错" class="headerlink" title="Cache的容错"></a>Cache的容错</h3><p>大的Cache对高性能CPU来说是至关重要的,而且位于处理器的关键(时间)通路上。为了减少复杂性和时间开销,错误检测的方法<span style="background:rgba(240, 200, 0, 0.2)">采用2位的奇偶校验位,l位用作奇校验,l位偶校验</span>,因此可以检查所有的错误情况,在读Cache的同时进行校验。当校验出错误,强制Cache丢失,并从外部存储去获取数据。</p><h3 id="处理器寄存器文件的错误保护"><a href="#处理器寄存器文件的错误保护" class="headerlink" title="处理器寄存器文件的错误保护"></a>处理器寄存器文件的错误保护</h3><p>寄存器文件是处理器内部的寄存器堆,内部的寄存器对于指令的运行速度和用户程序设计的灵活程度都是很重要的。内部寄存器的使用频率很大,其状态的正确性是也很关键。Leon2<span style="background:rgba(240, 200, 0, 0.2)">采用1、2奇偶校验位和(32.7)BCH校验和进行容错</span>。</p><h3 id="触发器的错误保护"><a href="#触发器的错误保护" class="headerlink" title="触发器的错误保护"></a>触发器的错误保护</h3><p>处理器的2500个触发器均<span style="background:rgba(240, 200, 0, 0.2)">采用三模冗余的方式进行容错</span>,通过表决器来决出正确的输出。</p><h3 id="外部存储器的错误保护"><a href="#外部存储器的错误保护" class="headerlink" title="外部存储器的错误保护"></a>外部存储器的错误保护</h3><p>采用挂上的EDAC单元实现。EDAC:<span style="background:rgba(240, 200, 0, 0.2)">采用标准的(32.7)BCH码,每32位字可纠正1位错误和检测2位错误。</span></p><h3 id="主检测模式"><a href="#主检测模式" class="headerlink" title="主检测模式"></a>主检测模式</h3><p>是指<span style="background:rgba(240, 200, 0, 0.2)">两个相同的处理器同时并行执行相同的指令,只让其中的主模式处理器输出结果,不让检测模式的处理器输出结果</span>。在内部,将检测模式处理器的输出同主模式处理器输出进行比较,以检查错误是否存在。这种工作模式,可以应用于要求更高可靠性的情况。</p><h3 id="Cache的清洗"><a href="#Cache的清洗" class="headerlink" title="Cache的清洗"></a>Cache的清洗</h3><p>因为上面介绍的五种方法,只有在对相应的单元进行访问时才进行错误检查。如果存储单元的数据不常使用,这些单元的错误会逐渐增加,因此必须<span style="background:rgba(240, 200, 0, 0.2)">使用一些软件的方法来实现</span>。</p><h2 id="编码风格"><a href="#编码风格" class="headerlink" title="编码风格"></a>编码风格</h2>]]></content>
<summary type="html"><h1 id="一、简介"><a href="#一、简介" class="headerlink" title="一、简介"></a>一、简介</h1><p><span style="background:rgba(240, 200, 0, 0.2)">leon2处理器是一款开源的</summary>
<category term="数字IC" scheme="https://www.daipeihong.top/categories/%E6%95%B0%E5%AD%97IC/"/>
<category term="车规芯片" scheme="https://www.daipeihong.top/tags/%E8%BD%A6%E8%A7%84%E8%8A%AF%E7%89%87/"/>
<category term="leon2处理器" scheme="https://www.daipeihong.top/tags/leon2%E5%A4%84%E7%90%86%E5%99%A8/"/>
</entry>
</feed>