1
说两句大背景
众所周知,目前IC验证行业使用最主流的语言是SystemVerilog,这个语言有一个重要特点就是它是面向对象的语言。对于面向对象的语言,想要把代码写得更“牛逼”,其实就绕不开一个概念叫“设计模式”。
设计模式是个啥呢?
简单地说,就是在人类软件开发历史长河之中,经过一代代的程序员踩坑和迭代,总结提炼出的各种具体问题的代码解决方案。
本文由“壹伴编辑器”提供技术支持
那一年,写代码的种种经验还一直是口口相传散落在江湖中;
突然一个名字叫“GOF”的4个人正式成团出道;
合著了一本名为《设计模式》的书;
在其中总结出了23种经典的软件设计模式;
至此开启了人类对软件设计模式探索的浪潮!!!
本文由“壹伴编辑器”提供技术支持
这23种经典的设计模式,可分为3大类,在此先列出他们的分类和名字,先转转混个眼熟:
1.创建型模式(5种,创建对象的不同玩法)
单例模式、建造者模式、工厂方法模式、抽象工厂模式、原型模式。
2.行为型模式(11种,类和对象交互和职责分配)
策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
3.结构型模式(7种,类和对象的组合的处理)
适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
以SystemVerilog或其他面向对象的语言为主要武器的IC验证工程师们,对设计模式的学习理解,虽然不需要像真正的软件开发工程师理解的那么多,但是稍微的简单了解一些,也许就会对你的编码思想,代码风格产生不错的收益。
今天《IC验证er一起学点设计模式》这个系列,就一起来聊聊关于设计模式在验证中应用。当然对于初学者来说,本系列话题的文章只做了解即可,可以不用关注这么多。刚开始还是要先以把需求和功能顺利实现出来为第一目标,后面有精力再慢慢地提高代码的优雅性。
2
什么是单例模式
有了大背景,今天我们就直奔主题,先说说单例模式。
见名知义,“单例”的“单”,同“单身狗”的“单”,代表独一。
“单例”的“例”,代表实例,也就是类(非抽象类)创建的对象。即创建独一无二的实例的模式。
这个模式说白了,就是在一个软件系统中,针对某个类,保证系统中有且仅有一个实例,然后再提供你一个全局访问点,谁想用就通过访问点访问即可。
直接上代码,先认识一下:
单例模式实现方法其实有很多种,有细节区别,核心思想一样,我们以如上的一种实现聊聊足矣。
我们定义的jerry_singleton这个类,就是我们想做成单例模式的类。在这个类中,除了Jerry定义出的这些方法以外,可以自定义实现具体功能的核心资源代码,这里我们就直接省略了,我们主要看单例模式实现核心。
这段代码是有“套路”的,寥寥几行,内涵丰富。
关键点如下:
1、第3行,jerry_singleton这个类,声明了自己的句柄,叫m_inst。而且修饰为static和local(只能当前类访问,儿子都不让见)。
2、第4行,定义了get() 静态函数,这个函数是单例模式的核心,里面会做一个判断:如果m_inst(我们第1步中声明的jerry_singleton类的句柄)没有指向任何实例,那么我们就通过new创建一个给m_inst,把m_inst作为函数返回值。如果判断已经指向某个实例了(第1步中m_inst声明local和仅仅在此get函数中有new的动作,就保证了这里“某个”实例只可能是jerry_singleton这个类的实例),那就不需要再new了,直接返回即可。这个函数就保证了实例不会二次创建。
3、第6行,new函数,protected类型(只自己和儿子可见,外人不可见)。堵死了外界不用get()函数,而直接使用new函数创造实例的可能性。
4、本来,到上一步可以完成了,使用时候,只需要通过“jerry_singleton::get()”即可获得单实例,进而对其中定义的资源进行调用。
第18行是一种经常锦上添花的写法:
A、首先,一定要想清楚,J_SIG是什么?
J_SIG不是一个字符串,它是一个jerry_singleton类型的句柄!
这句话让这个句柄直接指向了那个通过get函数返回的实例。
B、然后,大家注意,这句话,写在class外边!也即是我们之前讲的$unit空间,可看作J_SIG全局可见,可依照名字直接调用!(前文回顾:人人都会用到,但是大部分人不清楚是什么的“神秘空间”)
C、最后,神来之笔,顺手写上了一个const,把句柄J_SIG进行了常量限制,也就是只读变量。这样这个J_SIG句柄就不能再指向其他的对象了!
这样简单的一句话下来,你便可以在验证平台任何地方(相同编译单元域),直接使用J_SIG这个句柄, 就可以方便访问到jerry_singleton类的唯一实例!
说到这里忍不住顺便来一嘴子,UVM源代码中有这么一句:
const uvm_root uvm_top = uvm_root::get();
是不是很熟悉呢!我们平时使用的“uvm_top”和今天的“J_SIG”一样的玩法。
看完例子3句话再总结下实现单例的核心:
类内部自new对象
new的时候保证唯一性
提供全局访问点
3
单例模式优劣
有人问了,这个玩意有什么用?
优秀的推销人员,一般先不会直接给你说产品有什么用,而是先甩出已经拿下的一堆大客户再说。
所以,你问我单例模式有什么用?我希望你先别问我,请你在UVM源代码目录下grep一下例如“::get()”或者 "function .* get();"等类似的关键词,只是随便追一追,看看是不是大把的单例模式?
看看他们怎么用,再来问我有没有用?
如同下面录屏视频中Jerry在uvm1.2源代码里grep的简单操作,可以大体看出例如uvm各种phase、uvm_reg_cbs和uvm_callback相关的一些类、uvm_resource_pool、uvm_root,等等都用了单例模式。
值得一提的是,在UVM1.2中更是单独设计了一个单例类uvm_coreserice_t,把uvm_factory、report_server等重要的共享的信息都带上。
哈哈,当然,我们也简单地聊聊单例有什么好处。
1.唯一性
验证平台中,有的信息我们希望是唯一存在的,比如想收集到不同组件的关键信息进行统一存放或管理,如果存在多个对象,就不能得到一个完整唯一的信息,存在风险。此时可以考虑用单例方式优雅地解决。比如uvm_root、各种phase,这些实例如果不全局唯一,就会存在风险和麻烦。
2.省内存
假如你的验证平台,有一个类里面的内容很多很庞大,偏偏许多组件都想使用它里面的东西,用的时候每个组件都需要把这个组件声明和new一下,创建了这么多对象,显然是非常浪费内存空间,用单例就可以很好地节省。
3.全局访问更方便
从上一小节的代码中可以看出,我们要访问单例中的内容变得非常简单,不需要在任何组件类中声明例化,随用即可。
4.资源和数据共享
因为全局的访问方便和唯一性,通过单例,不同的组件可以在不关联的情况下,直接实现资源的共享或通信。这在搭建更高层次的验证平台和统一的时候可以提高很多效率。例如你的验证平台中有一些通用功能的公共函数,这些函数甚至可以在不同级别的验证平台中公用,那完全可以考虑为单例模式。
本文由“壹伴编辑器”提供技术支持
话说回来,盐好吃也不能多吃,单例也是有缺点的,不是啥情况都能用的,比如说虽然你想资源共享你的某个类,但是需要根据不同的参数例化不同的对象,在这种情况下,单例模式就不是很友好了,即便是节省了内存或方便了访问,无法满足实际应用需求也是徒劳。
大家可以结合前面提到的UVM源代码中单例的应用进行其优劣思考,但是也不要局限在其中,多想想自己设计的验证平台中哪些更适合用单例模式,然后应用之。
好了,时光如水,又到了说再见的时候了,祝大家天天开心,吃好喝好,越来越牛逼~
——The End——