实际上,HDL 语言肯定要比《玩转 IP core》或者《IP 核芯志》(业界著名的和尚书)这样的讲系统设计的书来的基础。按照从简单到复杂的思想,应该先说说 Verilog 方为合适。当年老衲也是低估了搞笑的呐喊的先知(jiaoshou)们的毁人不倦,觉得“语言嘛,应该会的了”。结果坊间出现了不少 C Style 的和尚书的实现,老夫就出离愤怒了:是可忍孰不可忍!实际上私下说,老衲不想涉及教语言也有私心杂念:语言莫过于刻板的规则,讲起来也是单调:讲的不喜欢,听的也不喜欢。看看前面两讲的内容,仅仅就幽默感而言,比起和尚书差远了。但是,没办法啊,与其叫语法党、逻辑派横行,不如老夫的电路门一统江湖。我们接着聊!
数字逻辑的了分为组合电路和时序电路两种。组合电路就是即来即服务,信号来了,理论上结果立即呈现。时序电路呢,则对于员工比较人道:在固定时间工作,其他时间可以歇着。
1. 组合逻辑,线型实现
组合逻辑在 Verilog 语言体系里面分两类,分别采用 wire 类型变量和 reg 类型变量来完成。本讲主要介绍第一类:wire 类型变量型组合逻辑描述方法。在下一讲里面,会介绍 reg 的情况。
提到 wire 类型变量型组合逻辑描述方法,一个不能会的关键词就是“assign”。它用于对于 wire 类型变量的赋值。assign 赋值的一般形式为:
assign [drive_strength] [delay] net_lvalue = expression;
其中:drive_strength 为信号强度,delay 为设定时延,net_lvalue 为 wire 型变量名,expression 为表达式;方括号对“[ ]”表示可省略,而且这两个内容与电路逻辑设计无关,在此不详细论述。
例 1 给出了一个直通模块的完整代码。这段代码没有实际意义,就是输出时时刻刻等于输入的功能。各位听众需要掌握的只是完整模块代码的格式。
【例 1】直通模块的完整代码
/***********************************************
Module Name: Bypass
Feature: Input bypass to output
Coder: Garfield
Organization: XXXX Group, Department of Architecture
------------------------------------------------------
Input ports: Input_Data, 8 bits
Output Ports: Output_Data, 8 bits, equales Input_Data
------------------------------------------------------
History:
11-27-2015: First Version by Garfield
11-27_2015: Verified by Garfield with Bypass_test in ISE/Modelsim
***********************************************/
module Bypass
(
input[7:0] Input_Data,
output wire[7:0] Output_Data
);
//Definition for Variables in the module
//None for this module
//Logical
assign Output_Data = Input_Data;
endmodule
说明一下,因为这个模块实际上不具有逻辑功能,所以一般综合软件都会在给出警告(Warning)后,把这个模块优化掉。
2. 门级电路,语言呈现
电路这个东西也属于妖魔鬼怪的,会七十二变,变来变去变成了 Verilog 语言。学习者只有学会了如何使用照妖镜,认得语言的本质,才算学好了 Verilog。
数字电路里面,所有组合单元都可以用与非门做出来。不是在下的结论,是《布尔代数》里面的结论。所以,在上课的时候老师们总喜欢“圈圈圆圆圈圈”地化简“卡诺图”。数学基础在哪里呢,他们不讲的,不晓得为什么。“黑板上老师的粉笔,还在拼命唧唧喳喳写个不停…”以小人之心度之,也不是好事,显得老衲也不那么光明伟岸了,吾有权保持沉默!
对于标量信号的逻辑运算有:
表 .1 逻辑运算操作符、真值表与对应电路符号
对于向量存在按照比特操作(一般语法书里面叫做“按位操作”)和信号各个比特一起操作(一般语法书里面叫做“缩减操作”)两个类型。
按位操作包含:
表 2 按位运算操作符、真值表与对应电路符号
缩减操作包含:
表 3 缩减运算操作符、真值表与对应电路符号
请注意,这里把电路符号也同时提供给施主们了。电路是基础,毋庸置疑。以后,再没有运算的对应电路符号了,为了节约纸张。
3. 加减乘除,四则运算
算术运算全部打包也没有几个,见表 4 所示。
表 4 Verilog 语言中的算术运算
符号 |
例子 |
含义 |
注释 |
+ |
a + b |
a 加 b |
可综合 |
- |
a - b |
a 减 b |
可综合 |
* |
a * b |
a 乘以 b |
可综合 |
/ |
a / b |
a 除以 b |
不可综合 整数除法,结果取靠近 0 的整数 b 为 0 的时候,输出为 x |
% |
a %b |
a 对 b 取模 |
不可综合 结果与 a 的符号相同 |
** |
a **b |
a 的 b 次幂 |
不可综合 操作数的类型为实数、整数或者带符号数,结果返回实数 操作数的类型为无符号数,结果返回无符号数 如果 a 为 0 同时 b 非正,输出 x 如果 a 为负数同时 b 不失整数,输出 x |
请注意,这些运算的可综合性,很重要。
到了 Verilog 2001,运算结果和 a 和 b 的定义有关。如果它们是无符号数,则按照无符号数输出;如果它们是带符号数,则获得带符号数的输出。
算术运算不是简单的门可以解决的,简单讲讲无符号数加法的样子。这里以 4 比特、无符号数加法器作为例子。最基本的、组合逻辑的加法器如图 1。这个叫逐次进位的加法器,建立时间为各级半 / 全加器的建立时间之和。在位宽很大的时候,这种单元可以允许的最高工作时钟频率是很低的。
图 1 无符号加法器的全加器(4 比特)链结构
下来,和大伙儿唠唠 Verilog 语言中有关移位的操作。按照逻辑层面上说,移位操作和算术运算是两类完全不同的东西,似乎分别来讲更为合适。一则,移位涉及的内容不多,作为一回来过于短了,讲听众值不回票价;二来,数字设计中移位用的最多的场合还是乘以或者除以 2 的幂的运算。所以,老比丘把移位的内容,也放到算术运算里面了。如果哪位施主有意见,请找和尚辩理,老道这里只是照本宣科。
移位操作分为:
• 逻辑右移(>>):1 个操作数向右移位,产生的空位用 0 填充
• 逻辑左移(<<):1 个操作数向左移位,产生的空位用 0 填充
• 算术右移(>>>):1 个操作数向右移位。如果是无符号数,则产生的空位用 0 填充;有符号数则用其符号位填充
• 算术左移(<<<):1 个操作数向左移位,产生的空位用 0 填充
4. 大小比较,关系运算
关系操作都是对于两个数值进行的,所谓的“双目操作”。关系操作符包括表 5 中的几个,其中的操作数可以是无符号数,也允许是有符号数。
表 5 关系操作符
符号 |
名称 |
结果 |
备注 |
> |
大于 |
比较 2 个操作数,如果前者大于后者,结果为真 |
可综合 |
< |
小于 |
比较 2 个操作数,如果前者小于后者,结果为真 |
可综合 |
>= |
大于或等于 |
比较 2 个操作数,如果前者大于或等于后者,结果为真 |
可综合 |
<= |
小于或等于 |
比较 2 个操作数,如果前者小于或等于后者,结果为真 |
可综合 |
== |
逻辑相等 |
2 个操作数比较,如果各位均相等,结果为真 如果其中任何一个操作数中含有 x 或 z,则结果为 x |
可综合 |
!= |
逻辑不等 |
2 个操作数比较,如果各位不完全相等,结果为真 如果其中任何一个操作数中含有 x 或 z,则结果为 x |
可综合 |
=== |
case 相等 |
2 个操作数比较,如果各位(包括 x 和 z 位)均相等,结果为真 |
不可综合 |
!== |
case 不等 |
2 个操作数比较,如果各位(包括 x 和 z 位)不完全相等,结果为真 |
不可综合 |
5. 特殊描述,拼接选取
欢迎大家来到 Verilog 语言的绝对领域;这是本语言的一亩三分地;俗话说:我的地盘我做主!瞧瞧把老衲兴奋地,都不知道说什么好了。嘴皮子这个利落啊,就是涂了三十层润唇膏的感觉。老和尚说了半天,客官问什么意思?就是本回书介绍的内容 ---- 位拼接与选取,是 Verilog 语言特有的内容。穆仙长,您可以瞑目了。您终于不用担心大伙儿不知死活,拿 Verilog 语言和其他语言类比了。理由嘛,八个字:“独此一家,别无分号”;现代汉语就是:独家特许经营权。
拼接操作用来完成前面说到的打包工作,其主要功能是把多个信号,按照一定的顺序,合并成为一个信号。拼接的操作符是大括号对“{ }”,一般格式是:
{signal1, signal2, ……}
其中,信号(signal)可以是常数也可以是变量,但是位宽必须已知且不可变。在例 3.13 里面,给出了一个简单的例子。
在 Verilog 语言中,拼接运算符是很好用很常用的,但是用法比较苛刻,所以常常会遇到编译不过的问题。
上面提到的“位宽必须已知且不可变”,这个要求是非常重要的,实践中无数次拼接运算符的报错就是不满足上面的要求。例 2 里面就给出了一些出错的情况。
【例 2】错误的拼接操作
错误写法 |
原因 |
正确表达 |
{4, a[2:0]} |
1 没有指定位长 |
{32’b1,a[2:0]} |
{8’b0000_0010, b} b 是 integer |
b 没有指定位长 |
{8’b0000_0010, b[2:0]} |
与拼接操作经常一起使用的是重复操作,也用拼接的操作符是大括号对“{ }”,一般格式是:
replication_time{signal}
其中,信号(signal)可以是常数也可以是变量,但是位宽必须已知且不可变;重复次数(replication_time)必须是常数。
对于拼接操作,一个自然而然的运用场景就是把一些逻辑上有联系的信号,结合成“总线”。
例如,可以这样把很多控制信号搜集成控制总线:
Control_Bus = {enable, wr_enable, rd_enable, configure_enable }
这是数据总线,依次包含芯片使能信号 enable,写使能信号 wr_enable,读使能信号 rd_enable,和配置使能信号 configure_enable。这些信号的具体含义是系统设计的时候,接口定义规定的,与目前的内容无关,不详细介绍。实际系统中的控制总线一般都比这个复杂的多,客观莫要抬杠,这里只是例子,能说明概念就好了。
6. 左右逢源,选择操作
到目前为止,大家遇到的还是“自古华山一条路”的运算,直来直去的操作。这种不够灵活局面到此为止,虽然在 Verilog 语言操作的优先级里面“? :”忝居末位,但是它在各种操作里面的作用却决不可小觑。没有选择操作,系统的功能永远是“一根筋”;有了这个玩意这就成了“两头堵”了。系统的智能,那是哗哗哗的上升,都可以去做机器人和工业 4.0 了。牛吧?
选择操作的一般形式是:
condition ? value1 : value2
其中,condition 是判决条件,一般是信号或者如第三讲里面的判决表达式;value1 和 value2 是两个信号,当条件 condition 为真时,表达式结果为 value1,当条件 condition 为假时,表达式结果为 value2。看清楚吧,这就是一个基于条件的选择。
当条件 condition 为不确定“x”或者高阻“z”的时候,表达式的值同时取决于 value1 和 value2 的值,见表 6 所示。
表 6“? :”条件为“x”或者“z”时,输出结果
? : Condition = x/z |
0 |
1 |
x |
z |
0 |
0 |
x |
x |
x |
1 |
x |
1 |
x |
x |
x |
x |
x |
x |
x |
z |
x |
x |
x |
x |
上表只是一个仿真的用途,实际中既然电路里面信号不能能是“x”或者“z”,所以无论如何电路的输出是一个确定的值,大家可以放心。话说到这里,“? :”到底对应什么电路里面的元件呢?很简单,地球人都知道:选择器,而且是最简单的双路选择器。其电气原理图,如图 2 所示。按照一般习惯,把条件为真的取值画在上面,把条件为假的取值画在下面,符号内的“1”和“0”表示条件值的“真”和“假”。有时候,为了简化,符号内的“1”和“0”也可以不画。这时候更需要个遵守值的上下位置的约定。
图 2 “? :”对应的选择器
还有,就是表达式 value1 和 / 或者 value2 也可以是“? :”选择操作,这个时候实现的电路就是判断链。
7. 高阻应用,接口风采
“出来混,迟早是要换的”,这句话太好了。前文书,给大伙说了高阻的事情,老沙门讲到“高阻一般会在输入输出管脚处使用”,贫道也只是说“只有和真实物理芯片管脚链接的信号,才能被定义为 inout 类型”。但是如何在代码中体现,却是没有提到,又是一个烂尾楼。
这是一个和这回书话题相关的题外话,顺便也聊聊。那就是对于 inout 这种类型的变量的处理。这种数据类型主要用于类似上面介绍的数据总线的场合。现在定义以对于芯片的操作定义读写。也就是说,读操作意味着芯片向外输出数据,写操作意味着向芯片内写入数据。依照上面的描述,定义片内的读使能信号 RD_IN 和写使能信号 WR_IN,高电平有效。片外的数据总线设为 PCB_DATA_BUS。输出数据在输出信号 DATA_ENABLE 高电平时刻有效,输入数据在输入信号 WR_IN 为高电平时有效。具体时序如图 3,例程关键部分例 3 所示。
图 3 数据总线接口时序
【例 3】数据总线接口关键代码
module BUS_interface
(
inout[15:0] PCB_DATA_BUS,
input WR_IN, RD_IN,
……
output DATA_ENABLE //Data_enable flag from the state-machine
……
);
//Load other module(s)
//DATA_ENABLE assignment here
//Definition for Variables in the module
wire[15:0] in_data;
//Buffer for input data
wire[15:0] out_data;
//Data for output
//Logical
assign in_data = PCB_DATA_BUS;
//Input direction
assign PCB_DATA_BUS = (DATA_ENABLE) ? out_data : 16’hzzzz;
//Output direction
endmodule
简单一句话:输入时时可读,使能有效采样;输出使能赋值,其他时刻高阻。
这正是:
“
操作符号是笑谈,运用巧妙功力现。门级运算有主见,加减乘除结构看。
拼接关系书写间,条件来把数值选。电路代码两不厌,太白遇到敬亭山。
”
与非网原创内容,谢绝转载!
系列汇总: