人气腹语术师天愿在现场披露了被人偶搭档夺取灵魂的腹语术师将妻子杀害的表演节目。天愿真的陷入了多重人格,命令自己杀害妻子和子的人偶的人格出现了。为了不(让自己)杀害和弟子登川有外遇的妻子,天愿提出委托想要监视,然而第二天早上,和子真的被杀害的事件发生了。天愿坦白很可能是在自己的意识失去的时候杀害的……”(----“真相只有一个”《名侦探柯南》一向是老衲喜欢的动画片)这个是第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 |
组合逻辑 | 可 | 可 |
时序逻辑 | 可 | 可 |
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 |
对应电路 | 时序电路 | 时序电路 组合电路 | 时序电路 组合电路 |
//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]; endendendmodule
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 |
可综合性 | 可综合 | 依赖综合软件 | 依赖综合软件 |
//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 endcaseend endmodule 图1例4综合出的电路图(全部为组合逻辑)4. 多路选择,一个例子数据选择器(也称为:多路复用器,英文:multiplexer,简写:MUX),是一种从多路输入信号中选择一个信号作为输出的器件。电器符号如图2所示。
图2数据选择器的电气符号 数据选择器的的逻辑功能是: 注意,其中输入I0、I1和SEL以及输出O都是1比特位宽的信号。对应布尔逻辑表达式是 对应Verilog代码为:1) 利用? :表达式input SEL;input I0;input I1;output Oassign O =(SEL) ? (I0) : (I1);代码中关键的部分是? :表达式,其语法结构为 (逻辑表达式) ? (值0) : (值1);作用是 所以,以上代码满足了数字电路里面数据选择器的功能。 2) 利用if关键词If (SEL == 1’b0)begin O = I0;endelsebegin O = I1;end3) 利用case关键词case (SEL) 1’b0: begin O = I0; end 1’b1: begin O = I1; endendcase 在很多情况下,需要选择的输入位宽大于1,这个时候只要两个待选择的输入与输出的位宽一致,照样可以实现功能(以下按照8比特输入为例)。此时Verilog代码除了模块的接口位宽,其他部分几乎没有变化:
input SEL;
input[7:0] I0;input[7:0] I1;output[7:0] Oassign 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; end2’b01: begin O = I2; end 2’b11: begin O = I3; endendcase
这正是:“组合逻辑大融合,关键语法有心得。不论理论数学河,电路优化靠综合。鄙人说书自有乐,撬行老僧沙弥哥。报告整理嫉妒惹,大乘渡人笑呵呵。”与非网原创内容,谢绝转载!
系列汇总: