HDLBits Verilog基础编程练习(1)

一. 在线Verilog编程网站学习

设计一个数字电路需要以下几步:编写 HDL 硬件描述语言,比如使用 Verilog;编译(综合)代码为一个数字电路;仿真分析电路的功能和时序;最后,Kill those bugs。

编写代码最简单的方式是直接在 HDLBits 网页下方的代码框内编写你的代码,因为网页已经帮你生成了部分代码,比如模块的输入输出端口。
你也可以在其他的编辑器上完成代码的编写,然后使用上传功能,上传你的 v 文件,这样会有些繁琐,但你可以利用到编辑器的语法补全,代码片段等特性。这里推荐开源免费,全功能编辑器,微软推出的 VSCode。
之后点击页面上的 Simulate 按钮,对代码进行编译(综合),仿真。编译(逻辑综合)你的代码会通过 Altera Quartus 的综合器综合为硬件电路。Quartus 会生成一组综合信息,通过 Show Quartus messages 可以显示/隐藏他们。
通过这些信息可以进行相应的修改,减少代码中的警告,但有些警告就随它去吧。仿真你的综合电路会通过功能仿真来检查其功能是否正确。HDLBits 使用 ModelSim 同时仿真你的代码和参考解决方案,然后比较两者的输出。

仿真报告关注两个方面:一是,会报告你的电路的输出与参考电路的输出是否完全一致,(不存在 mismatch)。mismatch 代表某一时刻两者的输出并不一致,正确的电路中不存在 mismatch。
此外,仿真报告会产生你的电路运行测试向量时的输出时序,时序分为三组:输入,你的电路的输出,参考电路的输出。
值得注意的是,不要修改题目中给定的模块以及端口的名称,否则会造成仿真错误。
结果状态如果你的电路完全正确,那么你会看到 Status: Success!
当然情况也有不妙的时候:

  • Compile Error — 电路综合失败
  • Simulation Error — 电路综合成功,但是仿真存在错误
  • Incorrect — 电路综合,仿真成功,但输出结果和参考结果不同

问题0
我们将从一点点 HDL 开始,以熟悉 HDLBits 使用的接口。以下是您需要为此练习构建的电路的描述:
构建一个没有输入和一个输出的电路。该输出应始终驱动 1(或逻辑高电平)。模块的端口已经给出。
在这里插入图片描述
这个题目很简单,assign one = 1; 即可。
1 在数字逻辑中代表 logic high,而 0 代表 logic low。
在这里插入图片描述
问题1
在前一题的基础上,要求构建一个电路,没有输入端口,只有一个输出端口,但这次输出端口时钟驱动逻辑 0 。输入assign zero = 0;即可
在这里插入图片描述
在这里插入图片描述
问题2
创建一个具有一个输入和一个输出的模块,其行为类似于电线。

与物理导线不同,Verilog中的导线(和其他信号)是定向的。这意味着信息只沿一个方向流动,从(通常是一个)源到接收器(源通常也称为将值驱动到电线上的驱动器)。
在Verilog的“连续赋值”( assign left_side = right_side; ),右侧的信号值被驱动到左侧的导线上。赋值是“连续的”,因为即使右侧的值发生变化,赋值也会一直持续。连续分配不是一次性事件。
模块上的端口也有一个方向(通常是输入或输出)。输入端口由模块外部的某个东西驱动,而输出端口则由外部的某个东西驱动。从模块内部查看时,输入端口是驱动器或源,而输出端口是接收器。
下图说明了电路的每个部分如何对应于Verilog代码的每个位。模块和端口声明创建电路的黑色部分。您的任务是通过添加要 assign 连接到 in 的语句来创建连线(绿色 out )。包装盒外的部件不是您关心的问题,但您应该知道,您的电路是通过将信号从我们的测试线束连接到 . top_module
除了连续赋值之外,Verilog 还有另外三种赋值类型用于程序块,其中两种是可综合的。在我们开始使用程序块之前,我们不会使用它们。

在这里插入图片描述

这里要澄清一个容易混淆的概念,图中的绿线代表的是 wire 之间的连接,而不是 wire 本身。即 wire 是连线两端的信号,而不是连线本身。wire 的中文可以翻译为导线,但 Verilog 中的 wire 和现实中的导线不同,wire 应该理解为一个信号,信号是有方向性的。
模块中的端口也带有方向性,主要分为输入 input 和输出 output 端口。输入端口是由模块外部的信号驱动的,而输出端口则又会驱动另一个外部信号。如果我们通过一个模块来模拟 wire,那么从模块内部来看,输入端口就直接驱动输出端口。

这个概念其实很好理解,将输入值赋值给输出值即可。
在这里插入图片描述
在这里插入图片描述
问题3
创建一个具有 3 个输入和 4 个输出的模块,其行为类似于进行以下连接的电线:
a -> w
b -> x
b -> y
c -> z
下图说明了电路的每个部分如何对应于Verilog代码的每个位。从模块外部看,有三个输入端口和四个输出端口。
在这里插入图片描述

绿色箭头代表电线之间的连接,但本身不是电线。模块本身已经声明了 7 根电线(命名为 a、b、c、w、x、y 和 z)。这是因为 input 除非另有说明,否则 and output 声明实际上声明了一条电线。
写作 input wire a 与 input a 相同。因此,这些 assign 语句不是在创建电线,而是在已经存在的 7 根电线之间创建连接。

这个概念也很好理解,一个值可以赋值给多个值并输出。
在这里插入图片描述
在这里插入图片描述

门电路

问题0 非门
创建一个实现 NOT 门的模块。
该电路类似于电线,但略有不同。在进行从电线到电线的连接时,我们将实现逆变器(或“非栅极”)而不是普通电线 in out 。
使用 assign 语句。该 assign 语句将不断驱动 on wire out 的 in 逆向。
在这里插入图片描述
与 wire 模块相同,非门模块中 in 被连接到 out,相比 wire 模块,唯一的区别在于:输出信号 out 是将输入信号 in 取反得到。

输出等于输入取反。
在这里插入图片描述
在这里插入图片描述问题1 与门
该电路现在有三根线( a 、 b 和 out )。连线 a , b 并且已经有由输入端口驱动的值。但电线 out 目前不是由任何东西驱动的。编写一个 assign 语句,用信号 a 的 AND 和 b 驱动 out 。
在这里插入图片描述
输出等于a和b的逻辑与值。
在这里插入图片描述
在这里插入图片描述
问题2 或非门
创建一个实现 NOR 门的模块。NOR 门是输出反相的 OR 门。在 Verilog 中编写时,NOR 函数需要两个运算符。
assign 语句用值驱动电线(或“net”,因为它更正式地称为)。此值可以是任意复杂的函数,只要它是组合函数(即无内存,无隐藏状态)函数即可。 assign 语句是一个连续赋值,因为每当它的任何输入发生变化时,输出就会被“重新计算”,就像一个简单的逻辑门一样。
在这里插入图片描述
~(a|b) |是逻辑或的意思在这里插入图片描述
在这里插入图片描述
问题3 同或门
创建一个实现 XNOR 门的模块。
同或门 (XNor Gate) 是异或门 (Nor Gate) 的取反输出。异或门的输入输出可以概括为:(输入)相同(输出)为 0 ,不同为 1 。
在这里插入图片描述
~(a^b) ^是按位异或的意思
在这里插入图片描述

组合电路

1 声明电线
实现以下电路。创建两条中间线(命名为您想要的任何名称)将 AND 和 OR 门连接在一起。请注意,馈入 NOT 门的导线实际上是导线,因此您不一定需要在此处声明第三根导线 out 。请注意,导线如何仅由一个源(栅极的输出)驱动,但可以馈送多个输入。
如果您遵循图中的电路结构,则最终应该有四个赋值语句,因为有四个信号需要赋值。
在这里插入图片描述
根据前面的练习,可以看出首先是a和b逻辑与,c和d逻辑与,然后这两个的结果进行逻辑或之后直接输出和一个取反输出。所以代码这样写:
在这里插入图片描述
在这里插入图片描述2 在芯片中的电路 7458芯片
7458 是一款具有四个 AND 门和两个 OR 门的芯片。此问题比 7420 稍微复杂一些。
创建一个与 7458 芯片具有相同功能的模块。它有 10 个输入和 2 个输出。您可以选择使用语句 assign 来驱动每根输出线,也可以选择声明(四)根线用作中间信号,其中每根内部线由其中一个 AND 门的输出驱动。如需额外的练习,请尝试两种方式。
在这里插入图片描述
这张图对比前面的略显复杂,但是不难看出输出是只有两个,所以挨着分析各个输入做的运算就可以了。
在这里插入图片描述

二、采用门电路组合电路方式完成一个1位全加器的设计

并在Logisim中进行测试。然后封装这个1位全加器为自定义的一个子电路模块(比如名称为OneAdder),然后新建一个项目,用1位全加器串行级联方式完成一个4位全加器的设计,并进行功能测试。

Logisim 电路图

在这里插入图片描述
在这里插入图片描述
封装后串联得到四位全加器:
在这里插入图片描述

Quartus 软件实现

分别采用原理图输入以及 Verilog编程
在这里插入图片描述

Verilog代码:

module demo1(
	//输入信号,ain表示被加数,bin表示加数,cin表示低位向高位的进位
	input ain,bin,cin,
	//输出信号,cout表示向高位的进位,sum表示本位的相加和
	output reg cout,sum

);
reg s1,s2,s3;
always @(ain or bin or cin) begin
	sum=(ain^bin)^cin;//本位和输出表达式
	s1=ain&cin;
	s2=bin&cin;
	s3=ain&bin;
	cout=(s1|s2)|s3;//高位进位输出表达式
end
endmodule

这里我第一次编译出错犯的错误是,module定义的名称要和工程名一样,不然会显示没有被定义。

通过tool->Netlist Viewers->RTL Viewer,查看电路图

Verilog电路:
在这里插入图片描述
然后通过4个1位全加器的串行级联,完成一个4位全加器的 原理图设计;再改用 Verilog编程(3种模式:门电路、数据流和行为级描述),完成这个4位全加器设计,并观察Verilog代码编译综合后生成的 RTL电路,与之前电路图设计的4位全加器电路进行对比 。

新建一个文件,将1位全加器放在这个文件里

module Verilog3(
	//输入信号,ain表示被加数,bin表示加数,cin表示低位向高位的进位
	input ain,bin,cin,
	//输出信号,cout表示向高位的进位,sum表示本位的相加和
	output reg cout,sum

);
reg s1,s2,s3;
always @(ain or bin or cin) begin
	sum=(ain^bin)^cin;//本位和输出表达式
	s1=ain&cin;
	s2=bin&cin;
	s3=ain&bin;
	cout=(s1|s2)|s3;//高位进位输出表达式
end
endmodule

然后在主文件里面调用这个实例

module demo1(
    input [3:0] A, B, // 4位输入A和B
    input Cin, // 初始进位输入
    output [3:0] Sum, // 4位和输出
    output Cout // 最终进位输出
);
    wire c1, c2, c3; // 定义内部进位信号

    // 实例化四个全加器模块
    Verilog3 FA0 (.ain(A[0]), .bin(B[0]), .cin(Cin), .sum(Sum[0]), .cout(c1));
    Verilog3 FA1 (.ain(A[1]), .bin(B[1]), .cin(c1), .sum(Sum[1]), .cout(c2));
    Verilog3 FA2 (.ain(A[2]), .bin(B[2]), .cin(c2), .sum(Sum[2]), .cout(c3));
    Verilog3 FA3 (.ain(A[3]), .bin(B[3]), .cin(c3), .sum(Sum[3]), .cout(Cout));
endmodule

4位全加器电路图

在这里插入图片描述

  • 28
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值