LJgibbs · 2020年09月17日

HDLBits:在线学习 Verilog (二 · Problem 5-9)

转载自:知乎

本系列文章将和读者一起巡礼数字逻辑在线学习网站 HDLBits 的教程与习题,并附上解答和一些作者个人的理解,相信无论是想 7 分钟精通 Verilog,还是对 Verilog 和数电知识查漏补缺的同学,都能从中有所收获。

Problem 5 : Andgate

本题要求使用 Verilog 语言描述一个模块,实现与门的作用。

从第 4 题开始,是用 Verilog 描述各种 “门”,这也就是 Verilog 硬件描述语言中,描述二次的由来。描述就是我们用 Verilog 的语法,通过写下几句代码来实现一个电路。从最简单的门到 CPU 都可以使用 HDL 描述。

题目给出的模块如下图,有三个 wire : a,b 以及 out。a,b 信号已经由模块的输入端口驱动,但图中黑色的部分中,wire out 还没有被任何信号驱动。本题要写一个 assign 语句,使 a,b 信号经过与门的输出驱动 wire out 信号。

显然,assign 语句的实现和前一题非常接近,只是增加了一个输入信号。和前一题不同的是,我们在这里强调了信号是被驱动(drive)的,被驱动的含义可以理解为,该信号的取值取决于另一个连接到它的信号的值,该信号的值随着另一个信号的值改变而改变。下图中模块的输入端口 input wire 被外部连接到模块的信号所驱动。assign 语句映射到具体的硬件上,就是产生了信号的驱动,由右值驱动左值。

说道 assign,如果你有过一些思考的话(你有思考嚒),一个 wire 信号不能被多个信号同时驱动(当一个信号说往东,另一个信号说往西,两个信号还要同时驱动我时,我到底该往哪?)。另一个方面,一个没有驱动者(driver)的信号的值会处于未定义的状态,可怜的家伙,都没有司机,好在综合器一般会免费给他安排一个,将其信号值驱动为 0.

解答与分析

verilog
module top_module( 
    input a, 
    input b, 
    output out );
    assign    out = a & b;
endmodule

值得注意的是 & 和 && 的区别,& 是逐位与,而 && 是逻辑与。

Problem 6 : Norgate

本题要求使用 Verilog 实现一个 NOR 门,注意这里其实是或非门,而不是更常见的异或门,或非门是或门的输出取反。

assign 语句将某个值赋予 wire 信号,这个 value 可以是常量,也可以是一个复杂的逻辑表达式,综合器会综合出相应的逻辑门实现。assign 语句代表的始终是连续赋值,因为当输入信号改变时,输出信号会重新“计算”。和一个逻辑门的工作方式相同,输入改变,输出对应改变。

解答与分析

verilog
module top_module( 
    input a, 
    input b, 
    output out );
    assign    out =~ (a | b);
endmodule

注意括号的由来,因为 ~ 非逻辑的优先级大于 | 或。

Problem 7 : XNorgate

XNor 的中文是什么,作者也愣了愣神,其实应该是同或门。

我们首先复习下数电,同或门 (XNor Gate) 是异或门 (Nor Gate) 的取反输出。异或门的输入输出可以概括为:(输入)相同(输出)为 0 ,不同为 1 。

解答与分析

verilog
module top_module( 
    input a, 
    input b, 
    output out );
    assign    out =~( a ^ b);
endmodule

这里你就会发现硬件描述语言的好处,其实你把数电的知识还给老师了,不记得相同为 0 ,不同为 1,似乎写出这道题问题也不大……

^ 为逐位异或,Verilog 中不存在逻辑异或符号。

Problem 8 : Declaring wires

到本题为止,我们的电路都十分简单,你是否觉得 Verilog 就这么简单呢,那我要说 是的,就这么简单。(逃<=)

言归正传,之前电路足够简单,我们能直接表示出输入输出信号的逻辑关系,但如果电路变得复杂,那么我们就需要一些中间信号来帮助我们简化描述电路的难度。

定义中间信号的语法格式为

text
wire foo ;

信号定义语句需要放置于模块的 body 中,就好比 C 语言中,你的中间临时变量需要定义在 main 函数函数体中。模块的 body 指的就是 module 和 endmodule 中间的部分。

这里建议先定义信号,再使用信号,就像 C 语言中一样。原则上,你可以在任何位置定义你的信号,使用前使用后都可以,正如之前的课程中说的那样,语句的顺序对于 Verilog 来说没有关系。但有些仿真工具需要你在使用信号之前定义信号,So,你就这么来吧。

举个栗子

上述模块中,存在三个 wire (in, out, and not\_in),其中两个信号已经随着模块的定义而定义了,分别定义为模块的输入输出 wire,这也就是为什么在前面的题目中不需要额外定义 wire 信号的原因。而 not\_in 信号定义于模块中,对于模块外部来说它是不可见的。然后,我们通过 assign 语句定义了两个非门,使用到了中间信号 not\_in。

看到这里,你说我不需要中间信号,我只需要 assign out = ~~in; 就行。没错。

但现在电路仍然比较简单,很容易描述出前一级的输出,但如果前一级的输出很复杂,那么要使用 assign 语句描述出两级电路的输入输出关系集合就比较困难。使用硬件描述语言的好处在于,你可以描述出前一个模块的输出,将其赋给中间信号,并将中间信号作为下一级信号的输入。这样,你永远只需要一次描述一个模块

牛刀小试

实现下图中的模块。首先创建两个中间信号将与门和或门连接起来,信号的名字随你的便,但好的名字往往影响一个信号的一生,若干年后,你还能依稀记起当年定义这个信号的峥嵘岁月。

注意,与门的输出信号也就是或门的输入信号,所以你不需要再定义或门的输入信号。再提醒一下,信号只能被一个信号驱动,但能驱动多个信号。

按照下图中的逻辑关系,你的代码应该有 4 个 assign 语句,对应四个逻辑门,或者说模块。

解答与分析

verilog
`default_nettype none
module top_module(
    input a,
    input b,
    input c,
    input d,
    output out,
    output out_n   ); 
    wire    and_1 = a & b;
    wire    and_2 = c & d;
    wire    or_1  = and_1 | and_2;
    assign    out   = or_1;
    assign    out_n = ~or_1;
endmodule

\`default\_nettype none 是一个宏定义语句,我们将在后续的课程中探讨它。

你问我的答案为什么没有说好的 4 个 assign 语句,因为我在定义 3 个中间信号的同时,还给它们赋了值,这在 Verilog 语法中也是允许的。如果你想看 4 个 assign 语句的答案,可以在完成提交后,通过 Show solution 查看解答。

Problem 9 : 7458

本题要实现个稍稍复杂的电路:数电芯片 7458 。它有 10 个输入信号,2 个输出信号。你可以选择对每个输出信号,使用一个 assign 语句,也可以先产生第一级逻辑门输出的 4 个中间信号。有时间的话,两种方式都可以尝试下。

解答与分析

verilog
module top_module ( 
    input p1a, p1b, p1c, p1d, p1e, p1f,
    output p1y,
    input p2a, p2b, p2c, p2d,
    output p2y );
    assign p1y = (p1a & p1b & p1c) | (p1d & p1e & p1f);
    assign p2y = (p2a & p2b) | (p2d & p2c);
endmodule

作者使用的是第一种方法,你可以在这里尝试一下上一题中讲授的创建中间信号的方法。

显然,完成这题需要你认识逻辑门的符号,一点儿耐心和好一点的视力,后两者对于 IC 从业人员来说很重要。

推荐阅读

关注此系列,请关注专栏FPGA的逻辑
推荐阅读
关注数
10596
内容数
559
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息