当前位置: 首页 > ops >正文

记录几个SystemVerilog的语法——时钟块和进程通信

1. 时钟块(clocking block)

时钟块的声明和例化是一体化,也就是在声明的时候,其实就实例化了,不需要再例化了。时钟块不能嵌套,且只能声明再module、interface、checker或program里。时钟块有三大用法:

  • Synchronous events:同步事件
  • Input sampling:输入采样
  • Synchronous drives:同步驱动

时钟块中指定为input方向的信号只能被read,不能被write;指定为output方向的信号只能被write,不能被read;指定为inout方向的信号既能被read,也能被write,因为inout拥有input和output两种属性,它在本质上会同时定义两个相同名字的input和output信号。

2. 时钟偏离(Clocking skew)

Clocking skew决定了一根信号在距离时钟事件多远时被采样或驱动的。Input skew隐含为负数的,也就是总是在时钟事件之前发生的,output skew总是在时钟事件之后发生的。同一个时钟块中不同信号可以采用不同的input/output skew,也可以采用default将一个clocking skew应用到同一个时钟块中。除非有特定指定,不然默认的input skew 是1step和默认的output skew是0。对于不带input skew或者input skew为1step的,输入采样值会在时钟事件之前的postponed region或当前时钟事件的preponed region采样的。

如果input指定#0 skew,按道理应该在对应的时钟事件发生时马上采样,但是SystemVerilog为了避免冲突,把它们放在Observed region采样。类似的,如果output带有#0 skew或没有skew,那re-NBA region驱动。

有个注意点是:如果时钟事件是在program里的执行程序触发的,那么时钟事件和采样值之间存在竞争关系,只有时钟事件是在module里更新的才不会出现竞争关系的。

3. 时钟块同步驱动(synchronous drive)

时钟块中信号的写(赋值)只能使用synchronous drive语法(<=),用其它方式赋值会报错。对于时钟块中信号的赋值可能在非时钟事件时被执行到,这样的驱动语句暂时不会阻塞执行,但驱动的值应该是在下一个时钟事件到来时才生效的。也就是说右边的值在执行到时马上计算评估出来,但是驱动的处理是被延迟了,直到下一个时钟事件到来时。例如:

default clocking cb @(posedge clk);   // Assume clk has a period of #10 unitsoutput v;
endclockinginitial begin#3 cb.v <= expr1;   // Matures in cycle 1; equivalent to ##1 cb.v <= expr1
end

时钟块synchronous drive给inout信号时不改变时钟块中该信号的input值,这是因为input值是刚被采样更新的,而不是在驱动时重新更新的。例子如下:

clocking cb @(posedge clk);inout a;output b;
endclockinginitial begincb.a <= c;  // The value of a will change in the Re-NBA regioncb.b <= cb.a;  // b is assigned the value of a before the change
end

上述时钟块a信号在re-NBA时才会被驱动,b表达式右边的a在上一个time step时已经采样了,在当前time step还没有被更新,因此b仍然用的是旧值(看input skew,#0则在observed时,非#0则在更之前采样的(如#1step则是在上一个time step的postponed采样))。

4. 进程间同步和通信

SystemVerilog给进程间同步和通信提供了三种方式:

  • named event type(->, @):命名事件类型;
  • semaphore:旗语;
  • mailbox:邮箱;

其中旗语和邮箱是SV内建的class类型,而且可以被作为基类拓展为更高层级的子类。这些内建类都是放在std package里,可以在任何范围内使用。

旗语就像是一把锁,只有获取到这把锁的钥匙(key)的进程才能继续执行,因此它常用语互斥锁、对共享资源的访问控制或基本的同步。

邮箱是一种通讯机制,可以用于进程间进行消息的交换,数据可以通过邮箱从一个进程送到另一个进程。邮箱有有界和无界两种。对于有界的邮箱,如果邮箱满了,那么put()方法会被阻塞住。无界邮箱不会满,因此put()方法不可能会被阻塞住,任何时候数据都可以放进邮箱中。

邮箱有一个要提的是,它分为通用邮箱和参数化邮箱,这两种有什么区别呢?通用邮箱就是它里面可以放各种类型的数据,因此在运行时需要做类型检查,如果put()和get()两测的类型不匹配,那么会报错。参数化邮箱只能放特定类型的数据,仿真器可以在编译时就进行检查。

命名事件提供了同步对象的句柄。当一个进程在等待事件触发时,该进程会被放在事件同步对象维护的队列中。进程可以通过@操作符或检查触发状态的wait()方法来等待命名事件。另外,wait_order还可以用于检测几个event发生的顺序。事件可以通过赋值为null来释放掉的,如:

event E1 = null;
@ E1;  // undefined: might block forever or not at all
wait( E1.triggered );  // undefined
-> E1;  // no effect

还有就是事件变量的句柄可以用logically equality(==, !=)和case equality(===, !==)来进行比较的,如果比较相同,返回1,反之返回0。例子如下:

event E1, E2;
if ( E1 )E1 = E2;    // same as if ( E1 != null )
if ( E1 == E2 )$display( "E1 and E2 are the same event" );

http://www.xdnf.cn/news/16429.html

相关文章:

  • 盛最多水的容器-leetcode
  • 洛谷 P10446 64位整数乘法-普及-
  • 详解力扣高频SQL50题之1164. 指定日期的产品价格【中等】
  • 3,Windows11安装docker保姆级教程
  • LeetCode 76:最小覆盖子串
  • mybatis的insert(pojo),会返回pojo吗
  • Petalinux生成文件的关系
  • 力扣面试150题--二进制求和
  • mmap机制
  • 2.qt调试日志输出
  • 《C++》STL--string详解(上)
  • vue3报错:this.$refs.** undefined
  • 在Podman/Docker容器中为Luckfox Lyra Zero W编译SDK:终极排错指南
  • Linux实战:从零搭建基于LNMP+NFS+DNS的WordPress博客系统
  • yolo11分类一键训练工具免安装环境windows版使用教程
  • 小白成长之路-Ansible自动化(一)
  • 20250707-2-Kubernetes 网络-Ingress暴露应用(http与https)_笔记
  • LeetCode 60:排列序列
  • 10.模块与包:站在巨人的肩膀上
  • MySQL ROUTER安装部署
  • 网络配置实验报告:主机间通信配置
  • python---eval函数
  • Day44 Java数组08 冒泡排序
  • 51核和ARM核单片机OTA实战解析(二)
  • day062-监控告警方式与Grafana优雅展示
  • 【通识】设计模式
  • Ashampoo Background Remover(照片去背景工具) v2.0.2 免费版
  • MyBatis-Plus IService 接口全量方法实现与测试(续)
  • 【Python系列】从内存分析到性能剖析
  • 【c++】从 “勉强能用” 到 “真正好用”:中文问答系统的 200 行关键优化——关于我用AI编写了一个聊天机器人……(16)