Verilog语言:还真的是人格分裂的语言

人气腹语术师天愿在现场披露了被人偶搭档夺取灵魂的腹语术师将妻子杀害的表演节目。天愿真的陷入了多重人格,命令自己杀害妻子和子的人偶的人格出现了。为了不(让自己)杀害和弟子登川有外遇的妻子,天愿提出委托想要监视,然而第二天早上,和子真的被杀害的事件发生了。天愿坦白很可能是在自己的意识失去的时候杀害的……”(----“真相只有一个”《名侦探柯南》一向是老衲喜欢的动画片)这个是第806回《腹语師的错觉》的介绍。

人有双重人格,或者叫人格分裂,那么语言呢?Verilog语言还真的是人格分裂的语言。前回书已经说到了,不能简单地把wire类型映射为组合逻辑,同时把reg类型映射为时序逻辑。事实上,这两个概念会交叉的。也就是说,wire类型极可能被综合为组合逻辑也可能综合为时序逻辑,reg类型也是这样。

“‘reg’是什么?”最微软的回答是:注册表文件。这个自然没错,但是违背了“到哪座山,唱哪里歌”的原则。一般的“标准”答案是:寄存器型变量。看看‘reg’,不就是‘register’(寄存器)的缩写吗?大多数中文教材中都是这样说的。

下面为了说明白这桩事情,请允许老僧引用IEEE有关Verilog语言里面的原文:
“Assignments to a reg are made by procedural assignments (see 6.2 and 9.2). Since the reg holds a value between assignments, it can be used to model hardware registers. Edge-sensitive (i.e., flip-flops) and level sensitive (i.e., RS and transparent latches) storage elements can be modeled. A reg need not represent a hardware storage element since it can also be used to represent combinatorial logic.”

为了强调,表1里面给出了wire、reg类型和组合逻辑、时序逻辑之间的映射关系。

表1 wire、reg类型和组合逻辑、时序逻辑之间的映射关系


wire


reg


组合逻辑




时序逻辑



可见reg是“双面间谍”的工作性质,为了能够“左右逢源”,自然用法要比wire来的复杂。类型wire被综合为时序逻辑一般还真是写错了,不必细表。

1. 电平触发,组合实现
和reg“孟不离焦,焦不离孟”的是关键词always,这个要记清楚。人家wire和assign是夫妻,reg和always是一对,千万不要搞混了,这不是能拉郎配的季节。

“always”的语法结构是:
always @(sensitive_tabel)

其中,sensitive_tabel称作敏感列表,其中包含always内部操作的一个或者多个触发条件。字符“@”发音是“艾特”(at),大伙儿发电子邮件(e-mail)的时候常用,不罗嗦。

正如上面提到的、标准立面的说法可以是边沿敏感和电平敏感两种。对于组合逻辑电路,这个敏感列表里面所有条件均为电平敏感。逻辑上,当敏感列表里面的条件符合的时候,always内部的操作可以进行。但是,很多逻辑上可行的代码,由于没有实际电路的支持,是无法实现的。

在Verilog语言95版本里面,电平触发的敏感列表的写法是
triger1 or triger2 or triger3……

其中,triger1等为触发信号。当触发信号电平变化时,说明敏感列表里面条件符合。如果触发信号是向量,则其中一个比特的信号变化,就认为信号电平有变化。到了Verilog 2001版本,这个写法被更加简化了:“,”和“or”都可以用来分割敏感事件;并且,可以用“*”代表所有输入信号,这可以防止遗漏。例1给出了一些符合语法的always的例子。

【例1】always与敏感列表
always @ (triger1 or triger2 or triger3)    //Version 95 and Version 2001
always @ (triger1 , triger2 , triger3)    //Version 2001
always @ (*)    //Version 2001
具有完全电平敏感列表的always模块,总叫人觉得就是组合逻辑了。再次强调数字电路是并行工作的,注意不要用“执行”这个词,不准确。对应的所有assign以及always带领的快都是并行的,其在代码中前后顺序与输出结果无关。也就是说例2里面的两段代码是等效的。代码中,敏感列表sensitive_table1和sensitive_table2对应操作Operation_A和操作Operation_B。
【例2】always所带操作顺序与输出结果无关


always @(sensitive_table1)

Operation_A

always @( sensitive_table2)

Operation_B


always @( sensitive_table2)

Operation_B

always @(sensitive_table1)

Operation_A

理论上,assign后面只有一行,对于这个并行工作的理解不难,不会产生误解。到了always这里,一般其后的代码就有很多行了,一不注意就会出错。

2. 条件判断,分枝多多
“用C语言的标准评价Verilog,如同用水果的标准评价蜜饯。”,但是他们的确很多写法有类似,这是很容易误导学习者的地方。

前文书关于“? :”选择操作哪里介绍过,选择与分枝在一般系统中是不可少的。那里介绍的代码方法,显然会产生阅读困难----尤其是在条件比较多的时候。为了改善这一问题,也同时可以更加符合大家以前的习惯,这一讲书老朽给贵客介绍“if”和“case”这两位大家熟悉的陌生人。

先看眼里的代码,条件语句if的形式有如表2中的三种。其中,condition等表示选择的条件,operation等表示对应的操作。请注意,这里的表达式“选择”,目的是和电路对应,不是故意和别人不一样的哗众取宠。

表2条件语句if的格式


无分枝


单级分枝


多级分枝


形式


if (condition )

begin

operations

end


if (condition )

begin

operations_1

end

else

begin

operations_2

end


if (condition_1 )

begin

operations_1

end

else if (condition_2 )

begin

operations_2

end

else if……

……

begin

operations_m

end


对应电路


时序电路


时序电路

组合电路


时序电路

组合电路

表.2中“对应电路”一行也请施主们注意,if语句中条件的所有路径覆盖不全面,可能会产生时序电路的。对于reg类型的变量,需要满足“条件不满足的时候,保持原值”;同时,组合电路不可“自赋值”(也就是类似“a <= a”的形式)。当需要“保持”的时候,纯组合电路是无法满足的。所以,综合器会引入“锁存器”。不是综合器自作主张,这是代码的要求。“天作孽犹可恕,自作孽不可活”,只能怪你自己了,哭吧!这个对应的器件是锁存器,不是这里的重点,会在以后介绍。这里要说的是:要产生组合逻辑,if的条件路径必须全覆盖。

if语句中条件的所有路径覆盖不全面,可能会产生时序电路的。这个对应的器件是锁存器,不是这里的重点,以后介绍。这里要说的是:要产生组合逻辑,if的条件路径必须全覆盖。

例3是一个单级条件语句if应用的例子,其功能是求有符号数绝对值。其中,输入为8比特有符号数,编码方式为补码;输出是输入数值的绝对值。具体算法是:
 

【例3】绝对值运算模块
module abs
  (
    input[7:0] signed_value,
    output reg[6:0] result
  );

//Definition for Variables in the module

//Load other module(s)

//Logical
always @(signed_value)
begin
    if ( signed_value[7])
  //Negative number input
  begin
      result <= (~signed_value[6:0]) + 7‘h01;
  end
  else
  //Positive number or zero input
  begin
      result <= signed_value[6:0];
  end
end

endmodule

3. 多种情况,并列判决
在条件很多的时候,用if语句来写还是很麻烦的,搞不好就是是一个条件路径覆盖不完全。这个时候,可以选择case套餐。case语句是一种多分支选择语句, Verilog语言提供的case语句直接处理多分支选择。多分支的case有三种形式,如表3所示。

表3条件语句case的形式


case


casex


casez


比较方式


敏感表达式与各项值之间的比较,是一种全等比较


如果分支表达式某些位的值为高阻z,那么对这些位的比较就会忽略,不予考虑,而只关注其他位的比较结果。

casez会把z/?匹配成任意,也会把任意匹配成z/?


在casex语句中,则把这种处理方式进一步扩展到对x的处理,即如果比较双方有一方的某些位的值是z或x,那么这些位的比较就不予考虑。

casex会把z/?x匹配成任意,也会把任意匹配成z/?/x,即直接忽略z/?/x


形式


case (variable)

costant_1:

begin

operations_1

end

costant_2:

begin

operations_2

end

……

default:

begin

operations_m

end

endcase


case (variable)

costant_1:

begin

operations_1

end

costant_2:

begin

operations_2

end

……

default:

begin

operations_m

end

endcase


case (variable)

costant_1:

begin

operations_1

end

costant_2:

begin

operations_2

end

……

default:

begin

operations_m

end

endcase


constant项


各个constant项为确定宽度的常数值,不包含x和z;

可以用“?”表示不关心该位数值


各个constant项为确定宽度的常数值,可包含x但不能包含z


各个constant项为确定宽度的常数值,可包含z但不能包含z


constant例子


3’b000:3比特宽度全0;

3’b0?0:3比特宽度第二比特不关心,其他比特为0


3’b000:3比特宽度全0;

3’b0?0:3比特宽度第二比特不关心,其他比特为0;

3’b00x:3比特宽度最低比特为x,其他比特为0


3’b000:3比特宽度全0;

3’b0?0:3比特宽度第二比特不关心,其他比特为0;

3’b00z:3比特宽度最低比特为不关心,其他比特为0


可综合性


可综合


依赖综合软件


依赖综合软件

case括弧内的变量称为控制表达式,case分支项中的常数称为分支表达式。控制表达式通常表示为控制信号的某些位,分支表达式则用这些控制信号的具体状态值来表示,因此分支表达式又可以称为常量表达式。当控制表达式的值与分支表达式的值相等时,就执行分支表达式后面的语句。如果所有的分支表达式的值都没有与控制表达式的值相匹配的,就执行default后面的语句。

default项可有可无,一个case语句里只能有一个default项。 当分支表达式可以覆盖控制表达式全部分枝路径时,default可以不写。但是,有时候这个全覆盖不是那么容易看出来的,所以建议最好写上default,哪怕有冗余这个default永远不可能被实现。也请大家放心,这种冗余综合软件会大伙儿去掉的,不必担心浪费电路资源。

每一个case分项的分支表达式的值必须互不相同,否则就会出现矛盾现象(对表达式的同一个值,有多种执行方案)。

执行完case分项后的语句,则跳出该case语句结构,终止case语句的执行。(精通C语言的大虾们请特别注意这点,这里case操作执行完之后不必写break了。)

在用case语句表达式进行比较的过程中,只有当信号的对应位的值能明确进行比较时,比较才能成功,因此要详细说明case分项的分支表达式的值。

case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。一个经常犯的错误是用‘bx、‘bz 来替代n‘bx、n‘bz,这样写是不对的,因为信号x、z的缺省宽度是机器的字节宽度,通常是32位(此处 n 是case控制表达式的位宽)。

当分支表达式不完全覆盖控制表达式全部分枝路径时,您老有偷懒没有写default的情况下,可能产生时序逻辑的锁存器的,这点和条件if语句类似。例4是一个例子,说明了default的重要性。但是,图1中的“ld”是锁存器已经是时序电路的元件了,超越了本章的范围。

【例4】case语句条件覆盖不全产生会综合出锁存器
代码1:组合逻辑电路写法
module case_full
  (
    input[7:0] number,
  input[1:0] select,
    output reg[7:0] result
  );
 
//Load other module(s)

//Definition for Variables in the module

//Logical
always @(*)
begin
    case (select)
        2‘b00:
        begin
              result <= number + 8‘b0000_0001;
        end
        2‘b01:
        begin
              result <= number;
        end
        2‘b10:
        begin
              result <= number - 8‘b0000_0001;
        end
        default:
        begin
              result <= 8‘b0000_0000;
        end
    endcase
end 
endmodule


图1例4综合出的电路图(全部为组合逻辑)

4. 多路选择,一个例子
数据选择器(也称为:多路复用器,英文:multiplexer,简写:MUX),是一种从多路输入信号中选择一个信号作为输出的器件。电器符号如图2所示。


图2数据选择器的电气符号

数据选择器的的逻辑功能是:

注意,其中输入I0、I1和SEL以及输出O都是1比特位宽的信号。对应布尔逻辑表达式是

对应Verilog代码为:
1) 利用? :表达式
input SEL;
input I0;
input I1;
output O
assign O =(SEL) ? (I0) : (I1);
代码中关键的部分是?  :表达式,其语法结构为 (逻辑表达式) ?  (值0) : (值1);作用是

所以,以上代码满足了数字电路里面数据选择器的功能。

2) 利用if关键词
If (SEL == 1’b0)
begin
   O = I0;
end
else
begin
   O = I1;
end
3) 利用case关键词
case (SEL)
    1’b0:
      begin
        O = I0;
      end
    1’b1:
      begin
         O = I1;
      end
endcase

在很多情况下,需要选择的输入位宽大于1,这个时候只要两个待选择的输入与输出的位宽一致,照样可以实现功能(以下按照8比特输入为例)。此时Verilog代码除了模块的接口位宽,其他部分几乎没有变化:

input SEL;
input[7:0] I0;
input[7:0]  I1;
output[7:0]  O
assign O =(SEL) ? (I0) : (I1);

当然用if或者case语句也可以实现,相信读者举一反三的能力,就不罗列了。
很多读者或许会感觉到笔者十分啰嗦,实则不然,图3是多位输入的数据选择器的电气原理图。


图3 多位数据选择器的电路原理图

上图看起来是顺理成章的。这里之所以笔者还不厌其烦的画出来,是为了叫读者看到多位与1比特实现上的区别。如果眼睛还没有贵恙的话,可以看出来多位数据选择器就是若干1比特数据选择器的并行排列。考虑到前面内容介绍的时延问题,需要提醒读者注意的是这个位数很高(当然不是例子里面的8比特)的时候,输入和输出信号的skew(线间时延)可能会给设计带来麻烦。

另一种常见的情况是输入不止有两个信号,或者说需要在不仅仅两个信号里面进行选择,这个叫做高阶数据选择器(一般吧SEL的比特数称为数据选择器的阶数)。通常输入个数是2的幂,此时选择信号SEL也就不仅是1比特信号了,这个很容易理解。在理论上,可以通过展开布尔逻辑表达式的方法,完成高速的高阶数据选择器。例如,2阶(也就是有4个输入信号,SEL为2比特变量)的随机选择的布尔逻辑表达式为:
 

其中,I0、I1、I2和I3为器件的输入,S0和S1为SEL信号的低比特和高比特。
这个式子已经不简单了,如果是10阶神马的数据选择器,那样的式子的长度不难想象。所以,在工程上,一般利用低阶数据选择器的串联来实现高阶数据选择器。图4是一个用3个1阶数据选择器实现2阶数据选择器的例子。


图4 利用低阶数据选择器实现高阶数据选择器

对于高阶数据选择器的Verilog代码,一般建议利用case的形式。例如图3里面的2阶数据选择器可以用以下代码实现:
case (SEL)
   2’b00:
       begin
         O = I0;
       end
   2’b01:
       begin
         O = I1;
      end
2’b01:
       begin
         O = I2;
       end
   2’b11:
       begin
         O = I3;
      end

endcase
这正是:

组合逻辑大融合,关键语法有心得。不论理论数学河,电路优化靠综合。
鄙人说书自有乐,撬行老僧沙弥哥。报告整理嫉妒惹,大乘渡人笑呵呵。

与非网原创内容,谢绝转载!

系列汇总:

之一:温故而知新:从电路里来,到Verilog里去!

之二:Verilog编程无法一蹴而就,语言层次讲究“名正则言顺”

之三:数字逻辑不容小窥,电路门一统江湖

关注与非网微信 ( ee-focus )

限量版产业观察、行业动态、技术大餐每日推荐
享受快时代的精品慢阅读

作者简介

加菲

十年一觉,博士毕业后十多年从事无线通讯产品的研发工作。了解W-CDMA、TDS-CDMA和LTE的标准协议、接收机算法以及系统架构和开发。从事过关于W-CDMA的FPGA IP core设计工作,也完成过W-CDMA和TDS-CDMA的接收机理论研究和链路仿真工作。综合上面的工作,最终选择了无线通讯的系统设计和标准设计工作。目前拥有100多个已授权的发明专利,是某通讯行业标准文件的第一作者,亦有专利思想被写入3GPP协议。已出版著作《IP核芯志》。

转载自:http://www.eefocus.com/fpga/365722/r0

时间: 2024-10-10 08:08:17

Verilog语言:还真的是人格分裂的语言的相关文章

乔布斯人格分裂的体现

读了一篇张小龙关于产品方面的分享.他这样说:做好产品,有一个最简单的思路就是把自己当成傻瓜去做产品.这个功力很难,乔布斯可以瞬间把自己变成傻瓜,而他可能需要5-10分钟,马化腾大概1分钟.有些产品经理经常3还不行,因为他们总是太专家了. 由此可以看出,乔布斯是重症人格分裂,马化腾次之,张小龙第三,大部分产品经理则值得恭喜,你们属于正常的人格分裂表现. 为什么说把自己当成傻瓜去做产品很难,需要功力,这可能是因为我们认识和解决问题总是倾向于运用我们已有的知识经验,越是精英,越有良好的阅读.思考.总结

嵌入式linux C++语言(二)——C++对C语言基础语法的扩展

嵌入式linux C++语言(二)--C++对C语言基础语法的扩展 C++是基于C语言扩展发展而来的面向对象的程序设计语言,本文将主要讨论C++语言基于C语言扩展的方面. 一.类型增强 1.类型检查更严格 在C语言中: const int a = 100; int *p = &a; 在C++语言中: const int a = 100;//必须在定义的时候初始化 const int *p = &a; 在C++语言中不能隐式转换数据类型. error: invalid conversion

动态语言和静态语言、编译型语言和解释型语言、强类型语言和弱类型语言的分析

一.动态语言和静态语言1. 我们常说的动.静态语言,通常是指: 动态类型语言 Dynamically Typed Language 静态类型语言 Statically Typed Language 可能还有:动.静态编程语言 Dynamic\Statically Programming Language 2.    动态类型语言:在运行期间检查数据的类型的语言例如:Ruby\Python这类语言编程,不会给变量指定类型,而是在附值时得到数据类型.Python是动态语言,变量只是对象的引用,变量a

D、GO、Rust 谁会在未来取代 C?为什么?——Go语言的定位非常好,Rust语言非常优秀,D语言也不错

不要管我的地位和 D 语言创造者之一的身份.我会坦诚的回答这个问题.我熟悉 Go 和 Rust,并且知道 D 的缺点在哪里.我鼓励人们在 Rust 和 Go 社区相似身份的人,也可以提出他们诚恳的观点.接着,我们开始吧. 首先,C++ 在这个问题中也应该被提及.不管它是否取代 C,或是成为取代 C 的候选人之一,C++ 是这个等式的一个关键部分.它是最接近 C 的,同时也是从 C 中来的.在下面几个问题中我会假设 C++ 是把取代 C 作为目标的. 每一个语言都有一些基础优势(我称之为“十倍优势

「C语言」Windows+EclipseCDT下的C语言开发环境准备

之前写过一篇 「C语言」在Windows平台搭建C语言开发环境的多种方式 ,讨论了如何在Windows下用DEV C++.EclipseCDT.VisualStudio.Sublime Test.Clion等IDE/编辑器搭建C语言开发环境,但也只是点到为止的介绍,对每一个开发环境的选择没有详细的步骤与过程: 这次借助C语言期末课程设计文档上介绍用Eclipse开发C语言的时机,逐步图文论证如何用Eclipse从安装到输出自己的第一个C语言Hello World: 欢迎探讨,欢迎互粉: 目录:

R语言︱H2o深度学习的一些R语言实践——H2o包

R语言H2o包的几个应用案例 笔者寄语:受启发想了解H2o平台的一些R语言实现,网上已有一篇H2o的demo文件.笔者在这多贴一些案例,并且把自己实践的一些小例子贴出来. 关于H2o平台长啥样,可以看H2o的官网,关于深度学习长啥样,可以看一些教程,比如ParallelR博客之中的解析. 下面主要是贴几个案例,让大家看看. ------------------------------------------------------------ Matt︱R语言调用深度学习架构系列引文 R语言︱H

动态语言,静态类型,强类型定义语言,弱类型定义语言,

1 动态语言和静态语言 通常我们所说的动态语言.静态语言是指动态类型语言和静态类型语言.   1 动态类型语言:动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,永远也不用给任何变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来.Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言. 2 静态类型语言:静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其间检查的,也就是说在

Go语言开发(一)、Go语言简介

Go语言开发(一).Go语言简介 一.Go语言简介 1.Go语言简介 Go,全称golang,是Google开发的一种静态强类型.编译型.并发型并具有垃圾回收功能的编程语言. Go从2007年末由Robert Griesemer.Rob Pike.Ken Thompson(C语言发明者)主持开发,于2009年11月正式宣布成为开放源代码项目,并在Linux及Mac OS X平台上进行了实现,后续增加了Windows平台的实现.2012年初,Go语言官方发布了Go 1.0稳定版本,目前Go语言基于

Go语言开发(二)、Go语言基础

Go语言开发(二).Go语言基础 一.Go语言程序结构 Go语言程序基本结构如下:A.包声明B.引入包C.函数D.变量E.语句 & 表达式F.注释 package main //包声明 import "fmt" //引入包 func main(){ //main函数 //打印Hello World fmt.Print("Hello World!") } 二.Go语言基础语法 1.Go语言标记 Go语言程序由多个标记组成,可以是关键字.标识符.常量.字符串.符