zoukankan      html  css  js  c++  java
  • 【黑金原创教程】【Modelsim】【第一章】Modelsim仿真的扫盲文

    声明:本文为黑金动力社区(http://www.heijin.org)原创教程,如需转载请注明出处,谢谢!


    黑金动力社区2013年原创教程连载计划:

    http://www.cnblogs.com/alinx/p/3362790.html


    《FPGA那些事儿—Modelsim仿真技巧》REV1.0 PDF下载地址:

    http://www.heijin.org/forum.php?mod=viewthread&tid=22419&extra=page%3D1


    前 言

    第一章 仿真的扫盲文   

    1.1 Modelsim 是电视机

    1.2 仿真和调试

    1.3 理想与物理

    1.4 综合与验证

    1.5 激励文本(Testbench)   

    1.6 仿真流程

    1.7 建模的切糕

    1.8 仿真的切糕

    1.9 自动思想的简介

    1.10 不可仿真对象的简介

    总结


    笔者一直以来都在纠结,自己是否要为仿真编辑相关的教程呢?一般而言,Modelsim等价仿真已经成为大众的常识,但是学习仿真是否学习Modelsim,笔者则是一直保持保留的态度。笔者认为,仿真是Modelsim,但是Modelsim不是仿真,严格来讲Modelsim只是仿真所需的工具而已,又或者说Modelsim只是学习仿真的一部小插曲而已。除此之外,笔者也认为仿真可以是验证语言,但是验证语言却不是仿真,因为验证语言只是仿真的一小部分而已,事实上仿真也不一定需要验证语言。

    常规告诉笔者,仿真一定要学习Modelsim还有验证语言,亦即Modelsim除了学习操作软件以外,我们还要熟悉 TCL命令(Tool Command Language)。此外,学习验证语言除了掌握部分关键字以外,还要记忆熟悉大量的系统函数,还有预处理。年轻的笔者,因为年少无知就这样上当了,最后笔者因为承受不了那巨大的学习负担,结果自爆了。

    经过惨痛的经历以后,笔者重新思考“仿真是什么?”,仿真难道是常规口中说过的东西吗?还是其它呢?苦思冥想后,笔者终于悟道“仿真既是虚拟建模”这一概念。虚拟建模还有实际建模除了概念(环境)的差别以外,两者其实是同样的东西。换句话说,一套用在实际建模的习惯,也能应用在仿真的身上。

    按照这条线索继续思考,笔者发现仿真其实是复合体,其中包括建模,时序等各种基础知识。换言之,仿真不仅需要一定程度的基础,仿真不能按照常规去理解,不然脑袋会短路。期间,笔者发现愈多细节,那压抑不了的求知欲也就愈烧愈旺盛,就这样日夜颠倒研究一段时间以后,笔者终于遇见仿真的关键,亦即个体仿真与整体仿真之间的差异。

    常规的参考书一般都是讨论个体仿真而已,然而它们不曾涉及整体仿真。一个过多模块其中的仿真对象好比一块大切糕,压倒性的仿真信息会让我们喘不过起来,为此笔者开始找寻解决方法。后来笔者又发现到,早期建模会严重影响仿真的表现,如果笔者不规则分化整体模块,仿真很容易会变得一团糟,而且模块也会失去连接性。

    笔者愈是深入研究仿真,愈是发现以往不曾遇见的细节问题,然而这些细节问题也未曾出现在任何一本参考书的身上。渐渐地,笔者开始认识,那些所谓的权威还有常规,从根本上只是外表好看的纸老虎而已,细节的涉及程度完全不行。笔者非常后悔,为什么自己会浪费那么多时间在它们的身上。可恶的常规!快把笔者的青春还回来! 所以说,常规什么的最讨厌了,最好统统都给我爆炸去吧!呜咕,过多怨气实在一言难尽,欲知详情,读者自己看书去吧 ...

    第一章 仿真的扫盲文

    1.1 Modelsim 是电视机

    如果笔者提问Modelsim 是为何物?想必同学们都会认为“Modelsim就是仿真”这种等价的关系。草草而言,该想法只是美丽的误会而已,笔者眼中Modelsim 只是类似电视机的工具。我们知道电视机除了播放功能以外,它甚么也不是。换之,身为用户的我们,使用电视机就要学会开关和调节节目,而不是去研究构造和功能原理。

    Modelsim成功播放波形图以后自然可以功成身退。笔者一直以来都无法理解,为甚么会有那么多同学特别纠结 Modelsim 的五脏六腑,笔者不知要佩服他们的挑战精神才好,还是要讽刺他们皮痒才好呢?因为信心满满到的他们,最终都会被Modelsim搞到变成破烂回来,结果实在惨不忍睹。笔者也不是有意中伤他们,因为笔者也是经验者。

    Modelsim 有各种各样的版本,除了官方的默认版本以外,还有第二方的自定义版本,如Altera Modelsim SE或者Altera Modelsim PE。以上是 Altera 公司自定义的两个版本, SE 是 Start Edition 亦即入门版本,AE是 Altera Edition,亦即是付费版本。

    许多同学都认为 AE一定比 SE 更强更加好用。逻辑上的确如此,不过 AE 与 SE之间宛如 24寸 与 19寸液晶显示器的大小差别而已。基本上SE已经足够应付一切仿真应用了,此外SE还有许多功能根本排不上用场。婆婆曾说过做人要节俭,东西够用就好,过多就是浪费,所以笔者告诫读者不要过度纠结 AE,如果读者硬要自寻烦恼的话,那么后果请自负。

    笔者知道自己很烦,最后还是要再强调一下,目前同学们只要把 Modelsim当成电视机就好,然而 Modelsim除了学习开关之余,还有就是调节节目而已,至于详细的用法,往后我们会慢慢接触到。

    1.2 仿真和调试

    曾笔者还没讲述“仿真”之前,让我们在根本上先理解仿真和调试的区别。仿真这词的同义虽然接近调试可是仿真却不等于调试。调试用来观察结果,仿真则是观察过程。仿真有“功能仿真”和“行为仿真”两个专业分类,对此调试也有“下线调试”和“上线调试”两个专业分类,让笔者用表格来说明:

    表格1.2.1 仿真与调试的对应关系。

    并行语言

    顺序语言

    仿真

    调试

    功能仿真

    下线调试

    行为仿真

    上线调试

    那么,什么是上线调试与下线调试呢?上线调试最为常见的方法就是将程序下载到开发板,然后再观察开发板的结果是否与预期一样,如发出“哔哔哔”声的程序,下去开发板后蜂鸣器发出“哔哔哔”的话,那么程序就合格。此外,上线调试还有较为细腻的方法,就是利用专用的上位程序或者集成环境序同步测试开发板,详细情形笔者就不谈了,有玩过单片机的朋友一定略知一二。

    下线调试就是隔着开发板在电脑上模拟程序的测试结果,让笔者用例子详细说明吧。

    1.    main()
    2.    {
    3.        int varA = 1;
    4.        printf( "%d", varA );
    5.    }

    代码1.2.1

    代码1.2.1大意是指,第3行声明整型变量varA然后赋予初值16,然后再第4行使用打印函数——printf 输出 varA的储存结果。假设varA的储存结果1对应LED点亮,那0对应LED消灭。如代码1.2.1所示,它会告诉我们LED最终会点亮,因为varA被赋予初值1。

    所谓下线调试就是在没有开发板的情况下,利用模拟环境取得假想结果,假想结果再经由大脑进一步脑补(反映)实际的情况。如代码1.2.1所示,当我们在集成环境(IDE)按下 <F5> 或者 <F6> 的测试热键,varA的输出结果“1”就会在集成环境的信息窗口显示出来。再来经由我们自己脑补道:“varA的输出结果是1,那么开发板的LED是点亮的”。

    到目前为止,笔者也只是大概讲述一下“调试”的内容而已,事实上“仿真”的内容比起“调试”还要麻烦许多,如果读者要确确实实理解调试与仿真之前的实际区别,读者就必须从语言的本质开始理解。

    在笔者的眼中,一些高级语言如 C, C++, Java 等都视为顺序语言,此外还有古老的Basic 和汇编也是一样。为什么笔者之将它们称为顺序语言呢?原因很单纯,因为这些语言是按着顺序的步骤在执行操作,举个例子:

    1.    main() 
    2.    {
    3.        varA = 1;
    4.        varB = 2;
    5.        varC = 3;
    6.    }

    代码1.2.2

    代码1.2.2的3~5行是varA,varB与varC的赋值操作,先是varA赋予1值,然后varB再赋予2值,最后varC赋予3值。在此之间,varB的赋值操作需要等待 varA的赋值操作完毕之后才能执行,同样varC 的赋值操作需要等待 varB赋值完毕之后才被执行。varA,varB与varC之间的赋值操作延迟也称为”步骤差“。

    故名思议,顺序语言会让我们普遍将代码以步骤的单位去认为,如果步骤1不完成执行,步骤2就无法继续,其它以此类推。笔者再将代码1.2.2换成另一个形式看看:

    1.    main() 
    2.    {
    3.        varA = 1; varB = 2; varC = 3;
    4.    }

    代码1.2.3

    代码1.2.3是将代码1.2.2的3行集为1行,然后我们会这样解读代码: ”第3行,varA赋予1值,varB赋予2值,varC赋予3值。“,笔者再顽皮一点,继续胡搞代码1.2.3:

    1.    main() 
    2.    {
    3.        varC = 3; varB = 2; varA = 1; 
    4.    }

    代码1.2.4

    代码1.2.4是将代码1.2.3的赋值操来回颠倒,然后我们会这样解读代码: ”第3行,varC赋予3值,varB赋予2值,varA赋予1值。“,无论笔者怎样胡搞varA,varB与varC的位置,我们都无法割舍步骤这个单位去解读代码中 varA 与 varB 还有 varC的赋值过程。因为只要失去“步骤”我们就会迷失解读的方向,这是顺序语言的特征,我们也可以说“步骤就是支撑整体顺序语言最基本的结构”。

    在此读者必须理解,“平常我们就是太习以为常使用步骤这个单位去解读代码,不知不觉习惯就成为理所当然”,但是事实却好相反,这种想法还有这种思路也仅限于“调试”这个框架而已。此外,读者还必须理解“步骤”只是可视的宏观单位而已,然而不可视的微观单位却是“指令”。假设varA = 1的赋值操作,可视步骤也只有1个,但是在隐藏中,varA = 1这样简单的赋值操作到底需N个指令完成,完全取决与编译器的编译质量。

    对于我们这些只会认为肉眼看见才是事实的小白而言,指令就像不存在与人界的幽灵般,时而增多时而减少,尽是虚幻也难以捉摸。除此之外,处理指令所需的时钟,也会根据指令的版本,处理器的工艺等因素产生改变。讲白点,调试只会输出步骤的结果却无视指令的内容,它也会无视时钟的消耗数。

    因此读者必须理解,调试一般离不开顺序语言,宏观上顺序语言需由由步骤这个可视单位支撑。微观上,步骤则是由无数的指令在后面支撑着,然而指令的内容还有时钟的消耗数都是隐藏内容。总结说,调试只在乎步骤在表面上产生的结果而已,又或者说是追求单向结果。

    笔者一直以来都在不停思考,为什么Verilog HDL 不使用“调试”而是“仿真”这词呢?有些朋友可能会认为笔者有点过于钻牛角尖了,对此笔者不敢否认,但是那种违和感时时刻刻都在折磨笔者,笔者也十分焦急想将它揪出来。我们知道“调试”与顺序语言有切不断的关系,然而“仿真”却与并行语言有着强烈的羁绊。

    所谓的并行语言有如VHDL或者Verilog HDL 等各种描述语言,描述语言不像顺序语言有步骤支撑整体的顺序结构,结果描述语言显得结构自由,甚至称为没有结构的地步。顺序语言每一个关键字都在暗示处理器的处理行为,然而描述语言每一个关键字只是描述手段,我们用它在白纸上绘出我们想要的“形状”。

    顺序语言与并行语言之间最大的差异就是,顺序语言每一个时钟只能执行一个步骤(如果这个步骤在一个时钟内完成的话),然而并行语言可以在一个时钟内执行千千万万个步骤,上限是没有尽头的。顺序语言之所以不关心时钟,那是因为时钟不仅隐藏而且还无法控制。反之,并行语言的时钟不仅开放也能控制。

    仿真有“功能仿真”还有“行为仿真”两大分类,如表格1.2.1所示。功能仿真与行为仿真的差别,即前者是下线,后者则是上线。一般所谓的仿真就是“功能仿真”,而不是行为仿真,为了避免浑浊,往下内容笔者皆用“仿真”来表示“功能仿真”。至于行为仿真已经超出本书的范围,怒不解释。

    仿真是一件很麻烦的事情,而且“仿真”也不像“调试”那样,只要轻松按下 <F5> 或者 <F6> 等调试热键,信息就会像水一样,花啦啦地打印出来。换之,执行仿真之前必须经历许多准备工作,如创建仿真对象,编辑仿真环境,仿真之间必我们必须一边追踪过程,一边解析信息。

    笔者曾说过调试是追求单向结果,如果用仿真来比拟调试,调试是用来断定某个信号在某个时钟的某个结果而已,如:信号B在时钟T10的结果是逻辑0,又或者数据C在时钟T12的结果是8’hAA。反之,仿真是追求多向过程,亦即N个信号在所有个时钟发生里什么结果,如:模块A,有信号A与数据B,而且模块A的一次性操作需要耗时10个时钟,那么仿真会用来观察信号A与数据B在10个时钟内的结果变化。

    笔者曾被仿真杀死过许多次,如果不是它在笔者最绝望的时候拉笔者一把,如今笔者就是一只怨气十足的冤魂了。学习仿真就像处于金庸所描述的江湖般,那里存在许多各种帮门流派,其中一种称为传统流派,也是网络流传已久的右翼硬派,门徒最多,死人也是最多。初落平阳的笔者为了寻求照应,就这样糊里糊涂加入其中。

    期间笔者至少死过十余来次,最后终于支持不住,一心来到崖边寻求解脱。那是笔者的人生当中最黑暗的一刻,就在跳下的瞬间,一直温柔却有力的右手揪主笔者,然后让笔者感动不已的声音传遍全身:“孩子,千万别做傻事,明天总有希望!”,此刻是笔者最痛哭的一次。

    为了寻找仿真之道,为了知晓那份违和感 ... 经过七七四十九天的闭关以后,歪道终于开窍,所有问题自然迎刃而解。歪道是什么?那是完全背离传统的知识,它像毒瘾一般让人深入无法自拔,甚至落沦魔鬼。歪道宛如不懂善恶的恶魔一般,只会给予方法却不会承担后果,这是沾染歪道的唯一风险。

    1.3 理想与物理

    理想与物理就像梦想与现实之间的关系 ... 我们知道现实世界(物理)是充满病痛,鄙视,欺骗还有杀戮等各种悲剧的复合空间,然而传统流派就是基于它们。笔者第一次踏入传统流派的大门,一股恶寒经由脚根直达脊椎,全身也不禁颤栗起来。反之,理想想世界却是现实的相反,那里不存在任何悲剧。

    闭关期间,笔者曾经穿梭诸神逗留的乌托邦,眼前出现的一切不经让笔者目睁口呆。在那里,生命都有黄金比例的结构,大伙都是协调相处,啊!多么理想的世界 ... 对!这就是笔者向往的世界,也是仿真应该演变的方向,而不是那坑坑爸爸的物理世界。我们知道时序就是寄存器还有组合逻辑产生的活动(信号),传统流派强调时序应该接近物理,亦即物理时序,反之笔者强调时序是理想完美,亦即理想时序。

    clip_image002

    图1.3.1 理想世界与物理世界的寄存器。

    图1.3.1显示有两个世界的居民,左图是理想世界,右图是物理世界。理想世界是非常整洁的世界,不像物理世界存在许多物理元素如: Tco/Tsu/Th 等寄存器特性以外,还有 tPath等物理延迟。初学的朋友可能会问:什么是寄存器特性?什么又是物理延迟?,朋友可以将它们想象为阻碍寄存器沟通的障碍。

    clip_image004

    图1.3.2 理想时序与物理时序。

    图1.3.3是寄存器之间的活动记录(沟通记录),亦即时序,也是该世界唯一可视的信息。左图是理想世界产生的理想时序,右图是物理世界产生的物理时序。根据左图所示,寄存器1在T1的时候向寄存器2发送(启动)数据,接着寄存器2便在T2接收(锁存)并且输出。根据右图的表现,寄存器1在T1向寄存器2发送数据,可是数据遭受Tco的妨碍之余,还被Tpath拖后腿,最终数据在T4被寄存器2读取,不过寄存器2在读取之间还要考虑 Tsu与Th是否满足。

    左图是非常整洁又高效的沟通过程,然而右图是沟通过程非常烦乱。试问读者,哪一个时序图更加顺眼更加容易解读?答案理想是理想时序,不是吗?在此,心机重的读者可能会反驳道:“叫兽说过时序不能能缺少物理元素,如果物理元素被吃掉,时序还是时序吗?”可怜的读者,叫兽就是风水师,骗人骗到祖宗十八代也不奇怪。

    首先我们必须理解,所谓仿真就是在虚拟的环境下运行模块,测试功能是否按照预期执行。为此,我们为何不取最理想的结果呢? 举例而言,假设模块A,要求T1拉高输出,然后T2拉低输出。为此,仿真仅是单纯地观察它是在T1拉高输出,然后在T2拉低输出——这是理想状态。而不是观察模块A在1ps拉高的电平是多少V,2ps拉低电平多少V——这是物理状态。

    再者,笔者也强调过,Modelsim只是一台电视机而已,功能就是播放波形(时序),这种情形宛如读者看电视,要享受当然选择高清节目,而不是走色的崩坏节目。理想时序就是高清节目,物理时序就是崩坏节目,仿真就是享受高清节目这么一回事,读者能理解吗?

    在此,有些同学可能会不安道:“物理时序该怎么办?物理元素该怎么办?”。为此,先让笔者帮忙消除不安:

    1) Tco/Tsu/Th 寄存器特性,或者 Tpath 等物理延迟,理想时序都会无视。

    2) Modelsim只是一台电视机而已,它可以播放理想时序也可以播放物理时序,不过没有傻子会喜欢崩坏的节目。

    3) 物理时序Modelsim 虽然可以播放但是无法解决。此外,各大FPGA厂商早已经为物理时序准备好各种仿真和纠正的工具,如TimeQuest。

    4) 笔者也准备好物理时序的教程。

    读者用不着担心理想时序多难学习,事实上理想时序相较物理时序更加容易掌握。再者,笔者爱用的建模技巧,仿真技巧,整合技巧,甚至静态时序分析,都有应用理想时序。

    理想时序作为设计是非常重要的概念,尤其是仿真的环节上,它不仅可以减少仿真的难度,也可以减轻激励文本的编辑工作,还有内容的解读。

    它曾说过:“理想的开始就是成功的一切”,这句话暗喻心情的重要性,理想或者美丽的东西会舒缓心情,结果高产。反之,瑕疵还有丑陋的东西会搞坏心情,结果难产。这种感觉好比新年图新,什么都是新,华人相信新东西除了示意好开始之外还有圆满和理想的含义,因为新东西没有肮脏和瑕疵。

    1.4 综合与验证

    描述语言专业分类有综合语言和验证语言,一般认为综合语言用来设计,验证语言用来仿真,说实说那是放屁!笔者还记得第一天学艺的时候,师兄当下给笔者递过一本厚厚的书籍,封面写着“验证语言”,笔者随意一翻,蛋蛋立即落在地上。因为内容仅是意义不明的关键字还有语法。

    师兄最后还说道:“今天给老子啃完,不然明天把你干掉 .... ”

    许多新手曾是那样,综合语言还没有掌握又要立即学习验证语言,不然仿真就无法开工。老实说,别开本大爷的玩笑了!拜托了,笔者是人不是吸尘机,不可能在短时间内吸收那么多东西。学习和恋爱一样,不能同时一脚踏两船,不然进度会进入两头不到岸的窘境 .... 直到最后,不管综合语言还是验证语言,半桶水的程度也没有达到。

    此外,验证语言也会随着年份拉长,内容也会不断增加,如Verilog 1993 进化到 Verilog 2001(据说未来还会继续增长)。只要稍微打开手册一看,我们立即就会发现验证语言占满全量的4/5,这点无疑是一起厚重的压力。闭关期间,笔者一直苦思冥想:“仿真的定义是什么?为什么仿真离不开验证语言呢?”不知不觉,笔者的意识再度穿梭诸神逗留的乌托邦。

    它告诉笔者:“物理世界有资源却有法则束缚,理想世界没有资源也没有法则束缚 ,真是一言惊醒梦中人。仿真是利用虚拟的环境取得假想的结果。然而,这个虚拟环境,假想空间,没有所谓的物理限制,如:理想的FPGA有数不尽的逻辑资源,开发板要什么硬件就有什么硬件。但是这个虚拟环境却没有实际的资源,如时钟信号什么的。为此,我们需要利用验证语言产生虚拟的时钟信号。

    上面的内容告诉我们一个非常重要的信息,亦即仿真也是建模,不过是虚拟建模,它虽然不会局限于硬件,但是需要模拟实际资源,为此需要用到验证语言。为此,笔者得到这样一个问题,如果验证语言可以用来描述虚拟的资源,为什么综合语言不能用来描述虚拟的资源呢?。

    实际上,仿真只要最小利用验证语言而已,例如产生时钟信号什么的,之余其余的描述工作,我们都可以交由综合语言去搞定。这是笔者最活跃的仿真思想,把仿真当成建模来玩。反之,传统流派的仿真概念好像被堵塞的臭水沟一样非常死非常臭,仿真和建模有绝对的分割线划开,建模就是综合,仿真就是验证,两者没有深切的关系。反之笔者却认为,仿真与建模不仅关系深切,而且两者之间只有概念的差别而已,即一个是虚拟建模,一个则是实际建模。

    1.5 激励文本(Testbench)

    激励文本或称激励文件,英文名为 Testbench,常见的后缀名有 .vt 与 .tb。笔者曾问过师兄, 激励文件是什么,师兄却怒吼道: ”激励文件就是激励文件啦,怎么!想死吗!?“,这是传统流派给予的回答。根据笔者的妄想,激励文本宛如绘出虚拟世界的一张白纸,亦即仿真环境。然而,我们就是创建这个环境的神明.

    身为神明,我们有5项重任:

    1) 产生环境输入。

    2) 建立仿真对象。

    3) 产生虚拟输入。

    4) 产生虚拟输出。

    5) 观察世界,更正世界。

    clip_image006

    图1.5.1 神明的任务之一。

    如图1.5.1所示,那是仿真环境最基本的的概念。首先是环境输入,环境输入一般则是模块所要的时钟信号还有复位信号,它们都是最基本的需要,这种感觉好比水源,空气等 ... 任一缺少仿真环境也无法成立。仿真对象好比居住在仿真环境的生物,它一般先在集成环境建模,然后实例化在激励文本当中,我们当然也可以直接在激励文本中描述仿真对象。

    虚拟输入又指刺激,这种感觉好比生物的生存危机,如外敌如入侵,资源干枯等。仿真对象除了需要环境输入,仿真对象也要虚拟输入刺激才行。虚拟输入又分为基本输入与反馈输入,基本输入可以视为第一刺激,反馈输入则是第二刺激,形象点说:假设外星人入侵地球,此为人类的第一刺激。事情发生以后,人类不仅没有团结,而且还出现叛徒,这是人类的第二刺激。

    除了,环境输入还有虚拟输入以外,激励文本还有虚拟输出。虚拟输出又指反应,这种感觉好比生物对应危机的反应。虚拟输出也有基本输出与反馈输出之分,它们也称为第一反应与第二反应。这种感觉好比外星人入侵地球以后,有些人会绝望,有些人会反抗,有些人宁愿成为走狗,此为人类的第一反应。为了解决那些叛徒,联合国实现全名监控,此为人类的第二反应。

    clip_image008

    图1.5.2 神的任务之二。

    仿真环境对神来说不过是心血来潮的实验场所而已,神闲来无事创建了仿真环境A,不久之后生物B便诞生。神为了刺激生物B进化,神召唤外星人攻击生物B,此刻生物B会出现各种抉择。如图1.5.2所示,那是仿真环境的演化过程,也是仿真环境的运动,亦即时序。作为神,我们的眼睛“全能之眼”时常处在高处窥视一切,如果觉得那里不顺眼就插手哪里。

    例如神觉得外星人入侵太无趣了,于是顺便召唤陨石下来(更动虚拟输入) ... 又或者神觉得生物B太弱了,然后强化它们(更动仿真对象)。再假设神觉得演化步伐太慢了,结果神加快时间的流失(更动环境输入)。在此,读者可能会觉得这个神太可恶了,把生物当成玩具来玩 ... 嘛,别激动朋友,仿真本来就是那么一回事。

    笔者说过,仿真既是虚拟建模,为此笔者开始自问:“激励文本是不是也要结构?”。答案是肯定的,激励文本有两种结构性,其一是布局的结构性,还有激励内容的结构性。

    clip_image009

    图1.5.3 布局的结构性。

    如图1.5.3所示,那是笔者根据习惯,然后为激励文本所建立的布局结构。环境输入一般都是置于激励文本的最顶端,余下是仿真对象的实例化,接下则是虚拟输入还有虚拟输出,最后就是其它。好奇的同学可能会怀疑布局结构的重要性,这位同学试想一下,如果世界万物失去结构会是怎样的场景呢?是不是无法想象呢?根据笔者的认识,激励文本之所以需要布局结构,其一是为了维护激励文本内容,其二是为了节能。

    除了布局结构以外,激励文本还有激励内容的结构性。笔者一般都将虚拟输入还有虚拟输出称为激励内容又或者激励过程。那是因为虚拟输入还有虚拟输出原本就是一组操作,而且操作都是经由无数步骤组成。笔者曾经说过,描述语言是自由结构的语言,步骤又是顺序操作的单位,默认下它是没有结构它的,为此笔者应用了低级建模的用法模板。

    1.    reg [3:0]i;
    2.    
    3.        always @ ( posedge CLOCK or negedge RESET )
    4.            if( !RESET )
    5.                begin
    6.                    i <= 4'd0;
    7.                    Start_Sig <= 1'b0;
    8.                    WrData <= 8'd0;
    9.                end                
    10.              else 
    11.                  case( i )
    12.                    
    13.                         0: 
    14.                         if( Done_Sig ) begin Start_Sig <= 1'b0; i <= i + 1'b1; end
    15.                         else begin WrData <= 8'd8; Start_Sig <= 1'b1; end
    16.                         
    17.                         1:
    18.                         if( Done_Sig ) begin Start_Sig <= 1'b0; i <= i + 1'b1; end
    19.                         else begin WrData <= 8'd9; Start_Sig <= 1'b1; end
    20.                         
    21.                         2:
    22.                         if( Done_Sig ) begin Start_Sig <= 1'b0; i <= i + 1'b1; end
    23.                         else begin WrData <= 8'd10; Start_Sig <= 1'b1; end
    24.                         
    25.                         3:
    26.                         begin i <= i; end
    27.                    
    28.                    endcase
    29.                    
    30.        /***********************************/

    代码1.5.1

    如代码1.5.1所示,那是虚拟输入应用用法模板以后的例子。其中我们可以看见步骤i指向步骤,指向操作。用法模板除了为激励内容提供最基本的顺序结构以外,用法模板还会帮助我们节能。因为,如果仿真对象还有激励内容都有相同的用法模板,那么两者之间都能应用相同的思路,还有相同的习惯。

    1.6 仿真流程

    clip_image011

    图1.6.1 仿真流程图。

    为了帮助小白扫盲,笔者绘出简单明了的仿真流程图。如图1.6.1所示,里边拥有许多流程与分支,然而一切流程与分支都起源于“集成环境”。接下来,让我们从“集成环境”开始,然后来了解各个流程与分支。

    流程1

    1) 集成环境生成软模块,也是俗称的建模。

    2) 软模块经过综合工具成为硬模块,也是俗称的综合或者编译。

    3) 硬模块生成以后便下载到开发板观察输出,如果输出结果不理想,就返回步骤1。

    软模块和硬模型都是笔者的专用语,软模型是意思是指综合之前或者下载到开发板之前的理想模块,没有实际的逻辑资源。反之,硬模型意思是指物理模块,拥有实际的逻辑资源。流程1也称为上线调试,亦即典型的调试方法,只要结果不符合预期,步骤就会返回开始,然后重复流程。该调试方式虽为最笨但也是最管用,不管调试对象是什么都适合。

    流程2

    1) 集成环境生成软模型。

    2) 软模型经过仿真工具编译成为仿真对象。

    3) 创建激励文本。

    4) 激励文本作用仿真对象经由仿真工具输出波形(理想时序图)。

    流程2是仿真最基本的仿真步骤,流程2相较常规的仿真流程,只有细节上的大同小异而已。在此有些读者可能会觉得疑惑,软模块与仿真对象都是理想模块,两者之间的差异究竟在哪里?理论上来说,软模块是未经加工的生肉,而且本质理想。换之,仿真对象是经过仿真工具加工过的正肉,本质也是理想。

    流程2之后会产生3条通往不同分支。

    流程2,分支1(仿真结果符合预期):

    1) 仿真结果符合预期以后,流程会返回集成环境。

    2) 软模型经过综合成为硬模型。

    3) 硬模型下载到开发板观察输出,如果输出结果不理想返回步骤1。

    流程2,分支1基本上是流程1的翻版,亦即仿真结果符合预期,但不等于实际效果是否理想,所以需要进一步将软模块综合成为硬模块,再下载到开发板观察输出是否达到预想效果?如果是,流程结束;如果不是,重复流程1。

    流程2,分支2(仿真结果不符合预期,仿真对象有问题,返回集成环境):

    1) 仿真结果不符合预期以后,流程返回集成环境更正软模型。

    2) 更正以后的软模型,再经过仿真工具编译成为仿真对象。

    3) 仿真对象经由仿真工具输出波形(理想时序图)。

    流程2,分支3(仿真结果不符合预期,仿真对象有问题,返回仿真工具):

    1) 仿真结果不符合预期以后,流程返回仿真工具更正软模型。

    2) 更正以后的软模型,再经过仿真工具编译成为仿真对象。

    3) 仿真对象经由仿真工具输出波形(理想时序图)。

    流程2,分支2与3直接的差距就是第二次更正的软模型是经过集成环境还是仿真工具。在此,可能会有同学觉得疑惑它们之间的差异何在?集成环境拥有较强的更正能力,但也更加耗时耗力;相反,仿真工具的更正能力虽然不及集成环境,但是耗时耗力相对较小。不管选择哪一种分支,都是见仁见智的事情。

    此外,还有一个关键点,一些仿真对象可能会携带官方插件模块,许多时候仿真工具都对官方插件模块的支持不怎么友善,主要问题是编译手段还有仿真库的问题。安全起见,那些携带官方插件模块的仿真对象返回集成环境会比较妥当。

    流程2,分支4(仿真结果不符合预期,激励文本有问题):

    1) 仿真结果不符合预期以后,流程重返更正激励文本。

    2) 更正以后的激励文本再作用仿真对象输出波形图(理想时序图)。

    流程2,分支4是激励文本有问题,除了最基本的语法错误以外,读者还要注意一下。仿真工具的编译器比较别扭,必须遵守先声明后调用这个规则。相比之下,集成环境的编译器比较醒目,声明还有调用的次序上下颠倒也没有问题。所以说,集成环境编译成功并不代表仿真工具一定编译成功,其中一定出现声明调用的次序问题。

    总结来说,图1.6.1只是自定义的仿真流程而已,实际流程会因人而异。图1.6.1是笔者的经验总结,也是笔者的想象力爆发。它曾说过:“流程会根据平衡发生变化 ... ”,这句话足让笔者深思许久,流程充其量只是便利的指南而已,活物不是跟死流程的机械人,所以笔者非常建议,流程看看以笑笑就好,不要过度纠结。

    1.7 建模的切糕

    切糕是什么?切糕是梦幻般的硬通货,传说投资切糕比起金银还有房地产更实在。市价好的时候可以换钱,灾难来的时候可以充饥,此外也有切糕达人一夜巨富的故事 ... 啊哈哈,以上纯属恶搞而已。切糕是新疆的传统食物,既是玛仁糖,也是体积庞大的饼干,模块好比切糕,其实这种比喻一点也不夸张,还不如说再适合不过。一个系统模块的份量,差不多是一座200公斤重的切糕.。

    clip_image013

    图1.7.1 传统建模,单文件(单模块),多文件(多模块)。

    传统建模有单模块与多模块之分。如图1.7.1所示,假设有四个模块A,B,C与D,左图是多模块建模,模块A与模块B各有独自的 .v文件,模块C与模块D则共用一个 .v文件。右图则是单模块建模,也是笔者常常讽刺的单文件主义,这里所有模块共处于一室。传统建模都有一贯的致命缺点,就是没有结构性可言。接下来,让笔者逐个分析它们的缺点吧。

    首先是单模块建模,也是初学者最常犯的问题,所有模块都强挤在一起,虽然在编辑方面有过人之处,作为代价,单模块建模却为后期工作带来许多麻烦。单模块建模宛如所有代码集于一身的Main函数一样,不过别忘了,并行语言不是顺序语言,它没有“步骤”这个最小的单位去支撑。此外,单模块建模不仅拖累模块的表达能力,单模块建模也不适合仿真。这种建模过度集中的情况,最终会演变成一块大切糕。

    仿真还没有开始之前,我们的心情就被切糕一样大的模块搞砸了,200公斤绝对不是普通人可以应付的重量,到头来,我们也只能傻乎乎看着它而已。所以说,要一口气搞定切糕一样大的模块绝非易事,于是人们开始动脑思考对策 ... 如果整座切糕无法处理,我们是否可以分块处理恶?就这样“多模块”的思想开始流行起来。

    传统的多模块建模如图1.7.1左图所示,模块A,B,C,D分别分散在各别的.v文件里,模块虽然有分化,但是模块却没有规则分化。这种感觉好比没有规则切整座切糕,结果每块小切糕都高矮不一。人类是一种自欺欺人的生物,人们以为只要分散整座切糕,压力就会不复存在,可是事实确是如此嘛?在此之前,先听笔者讲个故事:

    某天,小明不幸在森林里迷路,衰到极点的小明被一只饿狼追杀,小明只有不停向前逃命而已。忽然间,眼前的状况把小明愣住了,因为眼前出现2条以上的分叉路,就在小明犹豫的一瞬间,饿狼就把小明推到了,可怜的小明死因却是一瞬的犹豫。可能读者会吐糟小明笨,干嘛停下犹豫,总之先跑再说 ... 旁人总是在旁冷言冷语,因为它们不是当事者也无法知晓小明,同时它们也忽略一个重要的关键,那就是“小明当下的状态”。

    小明之所以失去冷静,是因为焦急,为什么焦急,因为被饿狼追赶,然后最该死的关键是突如其来的分叉路,它害死小明出现片刻的犹豫。当我们在执行仿真的时候,我们的处境就好比小明,不 ... 应该说是比小明更糟,因为小明起码被一只饿狼追杀而已。一只饿狼的好比一个过程在运行。

    并行语言发生问题绝不仅一处,换句话说Verilog绝非一个时间执行一个过程,而是同一个时间有无数过程在同步执行。这种情况好比N只饿狼同时在追赶读者,普通人类根本无法同时应付N只饿狼。再者,无规则分散模块会将仿真搞成像迷宫一样,如果读者同时被N只饿狼在迷宫里追杀,想必读者的结局比小明更加惨不忍睹。为什么呢?原因很简单,无规则分散模块会将无数过程搞成错综复杂,在此犹豫的时间一定会多过小明,人一旦犹豫就会忘记如何前进。

    曾经何时,笔者也像小明一样,同时被N只饿狼追赶,然而无规则分散模块的后果,仿真相似迷宫一样让足笔者琢磨不定,失去方寸,找不到仿真的切入口。仿真从模块A开始?还是从模块B?除此之外,激励文本的编辑工作也非常混乱。模块A与模块B应该共享一个激励文本?还是模块A与模块B拥有各自的激励文本呢?想着想着,就觉得头好疼,蛋蛋好不舒服。

    圣者说过:“胡乱分散就是混乱”,不规则分散模块会导致模块变成迷宫,然后丢失仿真的方向。既然如此,我们还不如不分散为好,可是整座模块的压力实在太大了,不分散又不行。结果分也不是!不分也不是!左右为难,还不如死去算了!冷静点我的朋友,切糕一定要分,不过要有规则还有标准。

    clip_image015

    图1.8.1 低级建模的基本模块。

    这种标准就是“功能”。模块按功能分化,这种感觉好比切糕按公斤划分。模块除了按“功能”量化以外,模块也根据按照“功能”类化。如图1.8.1所示,那是低级建模的基本模块,分别有控制模块,和功能模块。控制模块有正方形的外观,属于“控制类”;功能模块有长方形的外观,属于“功能类”。无论是哪一种模块,它们都遵守低级建模的准则,亦即“一个模块,只有一个功能”而已。

    假设一套进食动作有以下几个步骤:

    1) 举起食物

    2) 张开嘴巴

    3) 送入嘴中

    4) 闭上嘴巴

    5) 咬碎食物

    6) 吞入肚子

    一套完成的进食动作,基本上由6个步骤组成,每个步骤可以视为“功能类”。然而,左边的数字1~6却可是视为“控制类”。

    clip_image017

    图1.8.2 用餐模块化。

    一套进食动作按照“功能”分化以后,结果如图1.8.2所示。每一个模块仅有一个功能类,如:举起食物,张开嘴巴,送入肚子,闭上嘴巴,压碎食物,送入肚子都是拥有一个模块”。至于用餐步骤,它属于“控制类”,它也有一个模块。

    如果一座大切糕可以品均又有规划,分散成为每块小切糕的话,那么每块小切糕好比独立的个体一样 ... 这种感觉好比整座切糕被分散成为无数可以承受的最小份量,最后再由耐心将全部慢慢吃掉。模块有规则分化以后,功能的复杂度也会跟着简化,形象点说就是一座复杂的大迷宫,无数分散成为简单的小迷宫,如此一来仿真就不容易迷路了。此外,控制模块也好比迷宫的入口一样,一切从它开始。为此,我们可以这样说:有规则分化模块除了可以简化迷宫以外,它也为仿真建立入口。

    除此之外,该准则还有一个好处就是分散功能,分散过程,举例而言:一个大功能好比10只饿狼的狼群,面对它们我们不仅没有胜算,而且压力也很大了。不过,大功能经过分散以后,10只饿狼的狼群也会因此分散成,我们虽然不能一次单挑10只饿狼,但是我们可以10次单挑1只饿狼。

    总结来说,有规划分散模块除了上述的好处以外,有规划分散模块也会为人带来好心情。这种感觉好比切糕太大我们会觉得反胃,切糕切成稀巴烂我们也会觉得恶心,不管哪一种情况都会影响我们进食的心情,仿真也是一样的道理。还有一点读者必须好好记住,建模还有仿真有千丝万缕切不断的关系,前期有好的建模,后期就有轻松的仿真。

    1.8 仿真的切糕

    笔者曾在1.8小节当中说过,单模块或者没有规则的多模块建模会将仿真搞成迷宫,让仿真失去方向,最糟还会搞坏心情。反之,有规则的多模块建模,不仅会简化迷宫也会给仿真前带来好心情。切糕除了出现在建模以外,切糕也会出现在仿真之前还有仿真之间。不过切糕又会以什么形式出现在仿真当中呢?这个问题就让笔者来慢慢长谈吧,好让读者有个深刻的认识。

    clip_image009[1]

    图1.8.1 激励文本。

    笔者曾在小节1.5介绍过激励文本的作用。根据笔者的习惯,激励文本可以按着图1.8.1所示的次序插入相关激励内容。处于激励文本的顶端是环境输入,其次就是仿真对象。仿真对象一般都是先在集合环境里完成建模,然后再经由激励文本将其实例化成为仿真对象,如:

    function_module.v
    module function1( input CLK, input RESET, input SigD, output SigQ );
    ...
    endmodule
    
    function_module.vt
    module function1_simulation();
    function1 U1( .CLK(CLK), .RESET(RESET), .Sig(SigD), .SigQ(SigQ) );  // 仿真对象实例化,出入端声明
    ...
    endmodule

    代码1.8.1

    笔者先在集成环境里创建名为function1的模块,为了仿真function1模块,笔者必须事先建立仿真环境。为此,笔者建立一个名为function1_simulation的仿真环境名,然后将 function1实例化为U1,过程如代码1.8.1所示。

    根据代码1.8.1所示,function1有4个出入端,亦即 CLK信号,RESET信号,SigD信号,还有SigQ信号。在此,读者尝试想象一下,像整座像切糕一样大的模块到底有多少出入端呢?答案是肯定的,亦即非常多,而且还多到不可想象。过多的出入端无疑会拉长激励文本,结果导致激励文本成为臃肿的大肥,这是切糕出现在仿真中的第一种形式。

    根据笔者的经历,20几个出入端还算是小切糕的程度而已,随着建模层次不断提升,出入端的数量也会不断增加,一个系统模块有50来个出入端也是非常普遍的事情。分散模块就是了预防这个问题,逻辑而言,模块愈是分散,出入端的数量理应也会分散,而且实例化也会变的更加轻松,激励文本因此也会变得更加苗条。浏览苗条的激励文本,好比我们观察苗条的美女一般,骨干的美丽还有完美的曲线(简洁直观的内容),不仅令我们流失压力,也让我们的心情变得愈加愉快,情绪越来越有干劲。

    知晓切糕的第一种形式以后,接下来让我们寻找切糕的第二种形式。

    ==================================================================

    出入端过多除了拉长激励文本以外,它也会影响激励过程的编辑工作。激励文本有两种激励内容,亦即虚拟输入还有虚拟输出,尤其是虚拟输入,出入端的影响会是更重。

    clip_image019

    图1.8.2 虚拟输入。

      reg [3:0]i;
    2.        
    3.        always @( posedge CLK or negedge RSTn )
    4.            if( !RSTn )
    5.                      begin
    6.                      Pin_In = 1'b1;
    7.                      i <= 4'd0;
    8.                  end
    9.             else 
    10.                 case( i )
    11.                  
    12.                        0,1:
    13.                        begin Pin_In <= 1'b1; i <= i + 1'b1; end
    14.                         
    15.                        2:
    16.                        begin Pin_In <= 1'b0; i <= i + 1'b1; end
    17.                        .....
    18.                  
    19.                  endcase

    代码1.8.2 虚拟输入。

    虚拟输入有基本输入与反馈输入,图1.8.2还有代码1.8.2则是基本输入最简单的例子。

    如图1.8.2所示,激励内容经由 Pin_In产生基本输入刺激仿真对象。

    clip_image021

    图1.8.3 反馈输入的假想图。

    1.        reg [3:0]i;
    
    2.        always @ ( posedge CLK or negedge RSTn )
    3.            if( !RSTn )
    4.                  begin
    5.                          i <= 4'd0;
    6.                         A <= 8'd0;
    7.                         B <= 8'd0;
    8.                         Start_Sig <= 1'b0;
    9.                end    
    10.              else 
    11.                  case( i )
    12.                
    13.                          0:
    14.                         if( Done_Sig ) begin Start_Sig <= 1'b0; i <= i + 1'b1; end
    15.                         else begin A <= 8'd2; B <= 8'd4; Start_Sig <= 1'b1; end
    16.                                 
    17.                 endcase

    代码1.8.3 虚拟输入。

    图1.8.3还有代码1.8.4则是反馈输入简单的例子。首先,仿真对象会根据基本输入的刺激产生相关的反应,即基本输出。如代码1.8.3所示,在步骤0当中,A赋值 8'd2, B赋值8'd4,Start_Sig 则拉高,这是基本输入。仿真对象完成操作以后,它会经由 Done_Sig 反馈完成信号,这是基本输出。虚拟输入接收Done_Sig以后,便会拉低 Start_Sig,这是反馈输入。

    眼睛犀利的同学想必已经猜想到切糕处的第二种形式了吧!没错,仿真对象的功能越是复杂,出入端的数量就越多,期间激励内容的编辑工作不仅会变得繁重,而且激励文本也会越来越臃肿,内容变得非常杂乱。当简单的内容剧增到某种程度以后也会变成枯燥乏味,这种感觉好比读者被老师罚写名字,假设读者有“王一二”般的简单名字,假设老师罚写500次,就算名字再怎么简单,读者也会哭死。激励内容的编辑工作也是同样的道理。

    笔者相信解读时序信息是最头疼问题 ... 但是,让真正笔者担心是没完没了的时序。我们知晓顺序语言如果当前的步骤无法奏效,那么问题一定是上一个步骤有地方弄错了,顺序语言好比单行通道一样,就算通道再长问题再多,只要努力的走下去,黎明一定会出现。

    红苹果敲响牛顿的头,让他发现万有引力,不过野心勃勃的牛顿预想挑战万有定律,牛顿最后发现万物只能按照时间演变下去,这种感觉好比我们走在又长又暗的单行通道里,单调即无趣。牛顿虽然克服万有定律,但是他的人生观宛如牛顿力学一般非常消极,因为人生只能按照时间一直走下去而已 ... 这种情况,我们称为单向。

    牛顿死后几个世纪,量子邪说也随之兴起,量子力学之所以称为邪说是因为主意完全违背牛顿力学,举例来说:一只猫将其关在盒子里始终不打开,根据邪说,世界很可能会分支成为两条不同演化的世界线,亦即并行世界。世界有可能走向猫已死亡的世界线A,世界也有可能走向猫还活着的世界线B,其中猫死亡与否是造就世界线A与B的变数。

    这种情况,我们称为多向。

    仿真可是单向,也是多向。多向的仿真好比量子邪说般,信号亦即变数,仿真对象的功能越多,信号的数量理应也是一样多,结果变数也会变多。变数会衍生分支,无数的变数最终会衍生数之不尽的分支,从而海量化仿真信息。

    clip_image023

    图1.8.4 ps2接口模块的建模图。

    为了给予读者一个感知的认识笔者就用一张建模图来解释。如图1.8.4所示,那是一幅ps2接口的建模图,ps2接口差不多有一座小切糕的级别,它有3个功能模块,1个控制模块,信号的数量总和有10,亦即:PS2_CLK_Pin_In,PS2_Data_Pin_In,PS2_Data,PS2_Done_Sig,FIFO_Write_Data,Write_Req_Sig,Full_Sig,FIFO_Read_Data,Read_Req_Sig,还有 Empty_Sig。

    ps2接口模块的激励过程大致如下:

    我们必须先产生2个主要的虚拟输入,亦即 PS2_CLK_Pin 和 PS2_Data_Pin_In 。电平检测模块接收输入就会产生输出 H2L_Sig 刺激 ps2解码模块,ps2解码在PS2_Data_Pin_In 还有 H2L_Sig 双双的刺激下就会产生PS2_Data 还有 PS2_Done_Sig 的输出进一步刺激 PS2控制模块。

    PS2控制模块接收刺激之后,会经过 FIFO_Write_Data 和 Write_Req_Sig 等输出刺激 FIFO模块,FIFO模块也会使用 Full_Sig 反馈 PS2控制模块。最后 FIFO模块会经由顶层信号 Read_Req_Sig 刺激接而输出 FIFO_Read_Data 还有 Empty_Sig 等顶层信号。

    读者千万别忘了,上述流程只是简单的文字描述而已,此外上述流程仅讲述一条成功的流向而已。单文字流程,笔者的头脑已经快要发疼了,假设仿真流程既不是文字描述,也不仅一条的话 ... 没错,在实际的仿真当中,流程不仅没有成功的保证,流程也不可能只有一条而已,此外信息也不是文字描述,而是错综复杂的信号。多向仿真说过,信号就是变数,信号越多变数越多,变数所衍生的流程也会越多,许多流程的信息相乘以后会产生令人窒息的信息量。

    没错,这就是糕的第三形式,为了让读者深刻体验切糕的威力,笔者继续用图1.8.5来举。假设有两条信号H2L_Sig与PS2_Data_In刺激ps2解码模块,此刻会产生以下几个可能性:

    可能性一:H2L_Sig 先刺激 ps2解码模块,然后PS2_Data_In 再刺ps2激解码模块;

    可能性二:PS2_Data_In 先刺激 ps2 解码模块,然后 H2L_Sig 再刺激 ps2 解码模块;

    可能性三:H2L_Sig 与 PS2_Data_In 同时刺激ps2解码模块;

    假设1个可能性仅为1条流程,然后一条流程需要仿真1次。上述有3个可能性表示有3条流程,因此需要仿真3次。读者请仔细一想,上述3条流程仅是ps2解码模块的局部情况而已,试问ps2接口有几个模块?结果又会产生多少可能性呢?为了取得正确的结果,我们又要仿真多少次呢?没错,这就是仿真最可怕的地方,亦即无限可能性,海量仿真信息!

    在此,读者的蛋蛋是不是开始在发颤呢?人不仅时间有限,而且精力也不多,庄子一直劝告我们,“别用有限的身体去追求无限的目标”。当模块过度集中以后,出入端增加仅是小事一桩,真正让人觉得害怕的是,无数信号相乘以后做照成的仿真信息。为了避免仿真变成悲剧,我们必须想尽办法分散模块,分散功能,分散信号,控制变数,减少流程 ...

    最后,让我们来总结一下,这个小节究竟有几种形式的切糕?

    l 仿真对象的功能越复杂,出入端越多,激励文本越臃肿。

    l 仿真对象的功能越复杂,出入端越多,激励内容越混乱。

    l 仿真对象的功能越复杂,信号的数量越多,变数也会越多。

    切糕除了小节1.8的介绍以外,还有本节的3种形式。切糕之所以给人绝望是因为天生俱来的压倒性份量 ... 描述语言不同与高级语言,那些许多烦人又猥琐的底层工作高级语言都交由后台处理,我们只要关心代码的正确性即可。反之,描述语言的后台能力很弱,结果许多底层工作必须交由我们自己承担,但是人的精力是有限的 ...

    最后笔者再次强调,仿真真正最可怕的地方就是数之不尽的流程,还有解析不玩的海量信息。世界线还有平行世界虽然听起来感觉非常浪漫,可是浪漫的背后往往都是无限残酷,这种感觉好比 Stein:Gate 的男主,为了拯救青梅竹马,却无法避免世界线的收束,结果无数次目睹她的死亡。为了避免仿真发生悲剧,我们应该仅可能减少功能数量还有变数,以最小的力气引导仿真流程走向预想的途径。

    有些同学可能还在幻想自己成为勇者挑战恶龙然后征服所有世界线,但是笔者还是告诫读者千万不要过分高估自己的承受能力,就算读者有再好的吞吐量,吃了第一块大切糕,绝对没有能耐吃第二块大切糕,这点笔者可以拍胸保证,因为笔者就是过来人。如今,笔者已经放弃当初作为勇者的身份,因为笔者发觉自己的身心已经被切糕搞到破烂不堪。

    1.9 自动思想的简介

    传统流派虽然手段强硬而且暴力,不过它们相信气势可以闯出一片天,是典型自信的主攻派,然而,笔者本质柔弱还有节能,宛如不停往下游走的水资源,是典型怕事的撤退派。面对宛如切糕一样大的模块,传统流派有可能会选择消耗战,誓死吃掉整座切糕。换之,笔者没有那种自信,也没有那种胆量,所以笔者选择分化切糕到最小数量,然后再逐个吃掉。

    笔者当然晓得过度分散模块的缺点,亦即重复性的工作量,举例而言:假设一块切糕有10个功能,切糕经过分散以后成为10块小切糕,因此笔者必须重复十次仿真的工作。很不巧这是马死落地走的最佳方法,鬼叫笔者的承受能力差,无法一口吃掉切糕。此外,模块过度分散也会导致仿真失去整体性与连接性,亦即我们只能看见个体的局部仿真状况,而不是整体的仿真状况。这个问题让足笔者纠结过一阵子 ...

    师兄曾说过:“仿真是允许强者淘汰弱者的残酷世界! ”,不管笔者怎样否定传统流派,这是唯一一句受到笔者赞同的话,仿真本来是挑战一块又一块的切糕,那些承受能力差的弱者,根本没有仿真的资格。这句话听起来,虽然让人觉得不舒服,然而这就是现实。不是仿真拒绝我们,而是弱小让我们失格。

    笔者是弱者,所以笔者没有能耐承受大切糕,为此必须将大切糕分化为无数小切糕。然而大切糕经过分化以后就会失去连接性与集中性,原先的完整性就会受到破坏。笔者只要一天找不到连接小切糕的关键,回复切糕的完整性,笔者不仅没有资格仿真,笔者也没有资格去挑战切糕 ...

    为了寻找那份关键的东西,笔者苦思冥想了好久,想着想着 ... 意识不知不觉中又来到诸神逗留的乌托邦,然而它却在那里等待笔者。

    “在这里,生命不受负责也不受管理,每个生命既是个体也是整体”,它说道。

    “既是个体也是整体的生命,这不矛盾吗?”,笔者反驳道。

    它摇摇头,然后指向前方继续说道。

    “那里有一处花田,花草虫蚁都在生活着,生存压力让它们无瑕顾及其它,这是个体表现。从旁观望,无数个体存造就眼前的花田,这是整体表现 ... 理解了吗,孩子? ”

    clip_image025

    图1.9.1 主动设计概念。

    神的话永远都是充满寓意,为了了解它的话语,我们必须换个角度去思考问题。如图1.9.1所示,假设有甲乙丙丁四只家伙在交谈,小丁因为不擅长交流所以被排斥在外。如果站在局外者的角度观察这起交谈,小丁则会认为这起谈话必须甲乙丙三人无间断合作才能完成。反观,如果站在当局者的角度去观察这起交谈的话,事实上小甲只说想说,小乙只听想听,然而小丙边听边说,每个人只作自己想作的而已。

    假设小丁不小心错过交谈内容却又想了解详情 ... 此刻,如果小丁是传统流派的门徒,它认为最佳方法莫过再现现场还有执行实时监控。换之,如果小丁换作笔者,它会认为实时监控和再现现场太耗神耗力了,此外小丁还要同时接受三人份的谈话信息。所以小丁选择逐个拜访小甲,小丙,还有小乙 ... 小丁了解片断的详情后,然后将无数片断信息都交由想象力去结合。

    并行语言是一门偏硬的语言,由于本质限制的关系,它无法实现顺序语言所拥有的函数调用还有传递参数等能力,不过并行语言可以建立仿顺序结构的效仿顺序操作。但是,没有步骤的弱点,导致并行语言不适合过多内容集于一身,因为会降低解读能力,所以多模块建模就流行起来。低级建模也是多模块建模之一,不过是有准则的多模块建模。多模块建模却有一个缺点,亦即模块分化以后,模块会失去集中性与连接性。

    为此,建模图的作用就非常重要了,建模图不仅可视化模块之间的关系,也能加强个体整体的存在感,过后经由想象力再现整体效果。建模阶段,个体模块只做想做,只听想听,不用顾忌其他。完后,再运用想象力联系所有个体情况,整体情况就会出现在脑海当中。这种思想笔者称为主动思想,应用这种思想的技巧也称为主动设计。

    人类有两种记忆能力,左脑拥有清晰记忆也擅长处理具体的信息;右脑拥有模糊激励也擅长处理抽象的信息(想象)。主动思想的作用就是将左脑的记忆压力,还有处理压力分担给右脑,好让用脑保持平衡而不是一面倒。人类的头脑是一件不可思议的工具,右脑虽然没有左脑的强劲分析能力,但是右脑的想象能力比起左脑可优秀许多。例如记忆陌生人的面貌,如果用左脑记忆的话,左脑竟可能会多收集更多的面部数据,但是面部信息远远超越左脑的承受能力,所以用不了多久,左脑就会当机。

    反之右脑只会收集少数面部特征,其余细节任由想象力填充,亦即模糊能力。假设将两张相似的明星脸交由电脑对照,不管两张脸给人的感觉再怎么相识,电脑始终会给予否定的答案。如果将明星脸交托左脑分析,左脑也会给予相同的否定,因为左脑和电脑不会给予“好像,似乎,相识”等暧昧的回应,所以说明星脸是右脑引起的浪漫现象。

    根据图1.9.1的谈话情况,小丁在不知不觉之间发挥了主动思想的能力。首先它无意识记忆甲乙丙最有特征性的谈话内容,假设甲在这次谈话中说了,“海边”,乙说了“美女”,丙则说了“BBQ”。小丁经由脑补以后,小丁会认为甲乙丙在谈论“美女在海边BBQ”,又或者“美女在海边被BBQ”。主动思想除了应用在日常生活种以外,主动思想也能应用在仿真当中。

    当我们将整体模块分化为无数个体模块以后,为了应用主动思想充当个体模块之间的连线,为此个体模块必须露出特征,给人留下强烈的局部印象。这种感觉好比笔者记忆美女A的脸庞,不过笔者很懒,所以笔者不会记忆所有脸部细节,为此笔者会记忆她怜人的泪痣,性感的粉唇,还有可爱的娃娃头。最后在笔者的记忆中,美女A的脸庞除了泪痣,嘴唇,还有娃娃头特别清晰以外,其它细节怎样也无所谓。

    仿真也有同样的道理,如果个体模块有强烈的特征,经过仿真以后,它就会成为清晰的局部信息,然后深深烙印在我们的记忆中。无数清晰的局部信息刻入记忆以后,再由想象力将所有细节结合起来,一幅完整的整体信息就会出现在我们的脑海当中。换句话说,我们利用想象力充当个体模块之间的黏糊剂,但是作为前提,个体模块必须清晰,而且印象要非常强烈,不然的话我们作不到脑补时序。

    1.10 不可仿真对象的简介

    这个世界上存在一些危及生命的仿真对象,例如切糕级别的模块(功能过多的模块),但是还有一些仿真对象比切糕更危险,笔者称为不可仿真对象 ... 不可仿真对象实际上不是不能仿真,而是没有仿真意义,或者阻碍仿真。根据笔者的认识,不可仿真对象有三种,亦即“超乱模块”,“超烦模块”还有“超傻模块”。

    超乱模块顾名思义就是模块内容过度杂乱以致无法解读,说白点就是解读能力极差的模块。这类模块好比,官方插件模块,还有就是失去结构的模块。官方为了保护商业秘密,结果会故意搅乱模块的内容,这是一种非常恶心的行为,不过它们起码也做好善尾的工作,确保插件模块即时健全,还有附加说明书,所以它们情有可原。

    仿真本来就是处在虚拟的环境下模拟模块的功能状态,如果模块内容本身也无法确切表达,仿真就会失去原来意义。此外,仿真也是用来测试不健全的模块块,然而官方插件模块不仅健全,而且还有许多资料支持。仔细一想,我们为何还要浪费无谓的精力在它们的身上呢?为了节能,除非是特殊的情况,否则笔者是不会仿真官方插件模块。

    ”超烦模块“是指用时过于沉长的模块,让人等到烦心。这个问题一般主要是由计数器或者定时器灌水过多引起的悲剧。仿真的最小单位不是现实中1秒或者1ms等物理时间,而是N个时钟。例如典型的流水灯实验,流水间隔至少都是100~1000ms之间,结果灌水会是非常严重。

    根据笔者的惨痛经历,20Mhz为例的仿真时钟,仿真时间一般不推荐操作超过1秒,1ms~10ms之间最为理想,过长的仿真时间会拉长wave界面。曾经何时笔者也是傻子一名,为了验证正确的计时结果,笔者常常在仿真界面上拖来拖去,实在累人 ... 天生节能的笔者绝对不能容许这种滑稽的事情发生在自己身上,于是笔者左思右想最终想到整合技巧。整合技巧有各种作用,其中一项功能就是精密控时,任何长时间计时都能在代码上搞定,而不是经由仿真。

    最后还有一个不可仿真对象就是“超傻模块”,故名思议超傻模块是指功能过度简单明了,这种功能简单到一眼就能辨出是园是扁,难道我们还会大费周章,敲锣打鼓去仿真它吗?当然不会啦。超傻模块典型例子有:多路选择器,加码,解码等小组合逻辑还有小功能模块。

    最后让笔者来个简单的总结吧:

    超乱模块:模块内容极乱,解读不能,例子:官方插件模块,无结构的模块。

    超烦模块:模块内容水分,极度费神,例子:定时器,计数器。

    超傻模块:模块内容单纯,浪费气力,例子:多路选择器,输出器等小组合逻辑。

    总结:

    第一章虽然尽是毫无里头的扫盲文,不过扫盲文的作用究竟有多大读者应该心中有数。

    “仿真有建模三倍的负担”这句话一点也夸张 ... 建模亦即实际建模,除了建模还是建模;然而,仿真除了建模以外,还有创建激励还有解读时序信息等猥琐的工作要我们去干。说实话,同时兼学建模和仿真很容易两头不到岸,这也是学习最忌遇见的窘境。笔者希望读者可以透过扫盲文做好最起码的思想准备。

    仿真是什么?虽然在字面上“调试”还有“仿真”相较有同义的嫌疑,不过“调试”不等价“仿真”,“调试”为求单向结果,换之“仿真”为求多向过程。简单说,调试是顺序语言的测试手段,然而仿真是并行语言的测试手段,后者相较前者不仅不单纯,而且让人匪夷所思甚至难以捉摸。

    调试与仿真之间也是顺序语言与并行语言之间的思路问题,错用思路宛如遇上一堵杀人不偿命的隐形巨墙。笔者已经无数目睹同学FQ不成反被摔死,实在可怜 ... 为什么他们要模仿飞蛾寻求绝命之火呢?事实上这些悲剧,传统流派实在功不可没,它们手段强硬而且暴力,强迫学习从来不说明,无辜之人才会接续遭殃。

    “仿真结果一定是物理时序!“

    “仿真需要验证语言!“

    “仿真和建模是两回事!“

    “我们才是正道!“

    正是这些花言巧语骗尽傻子卖猪仔,曾经何时笔者也是一只傻傻上当的猪仔 ... 解脱不遂之后,笔者才渐渐领悟,传统流派只会传授蜻蜓点水的表面功夫,许多重要细节都会潇洒带过。仿真不当容易受伤,不过仿真真正让人恐惧的是——切糕。切糕是隐藏在仿真背后的绝望,切糕会改变形态躲在黑暗处,然后无时无刻等待时机扑食希望。切糕一般是指压倒性的工作量还有信息量,主要是仿真手段不当所产生的副产物。传统流派不知真傻还是装傻,那么大的切糕它们既然可以视而不见?

    笔者被切糕施虐的惨痛的经历最终浓缩成为这本书。这个世界(仿真)太大了,传统手段只会沦落切糕的猎物,为求生存异常手段是必须的。这本书是这个世界(仿真)的地图,也是这个世界(仿真)的生存手册 ... 此外,这本书也能给予出发前的心里准备。读者好好切记,这个世界(仿真)是由并行法则支配的空间,以往的顺序手段已经一去不返,心存半点也会引来悲剧。

    “仿真是会死人的学习 ... ”,笔者道。

  • 相关阅读:
    递归函数及Java范例
    笔记本的硬盘坏了
    “References to generic type List should be parameterized”
    配置管理软件(configuration management software)介绍
    WinCE文件目录定制及内存调整
    使用Silverlight for Embedded开发绚丽的界面(3)
    wince国际化语言支持
    Eclipse IDE for Java EE Developers 与Eclipse Classic 区别
    WinCE Heartbeat Message的实现
    使用Silverlight for Embedded开发绚丽的界面(2)
  • 原文地址:https://www.cnblogs.com/alinx/p/3367573.html
Copyright © 2011-2022 走看看