北航计算机组成原理课程设计-2020秋 PreProject-Verilog HDL与ISE-Verilog题目实例与分析

北航计算机学院-计算机组成原理课程设计-2020秋

PreProject-Verilog HDL 与 ISE

Verilog题目实例与分析


本系列所有博客,知识讲解、习题以及答案均由北航计算机学院计算机组成原理课程组创作,解析部分由笔者创作,如有侵权联系删除。


从本节开始,课程组给出的教程中增添了很多视频讲解。为了避免侵权,本系列博客将不会搬运课程组的视频讲解,而对于文字讲解也会相应地加以调整,重点在于根据笔者自己的理解给出习题的解析。因此带来的讲解不到位敬请见谅。


组合电路题目描述

Verilog是一种硬件描述语言,简单来说就是用有语法结构的语句去描述电路并通过综合、仿真,得到我们想要的电路,而不用再聚焦于具体的电器元件。这一章节将主要利用Logisim教程中已经学习过的组合电路时序电路两类电路,结合之前的语法知识,根据题目给出的信息进行分析,并描述目标电路,建立模型。

首先我们先说说简单的组合逻辑电路。

我们举一个简化ALU的例子。

模块规格:

在这里插入图片描述

题目说明:

通过给定的输入inputA,inputB以及op,根据功能输出out。


组合电路例题实现过程

题目分析:

题目中的规格已经确定了我们的输入与输出,并给定了相应的变量名称。我们只需要通过判断相应的op的取值,根据inputA与B,计算出out对应的取值。对于组合逻辑,一般不需要自己额外去定义新的变量,只需要将输出与输入的逻辑表达式给出就可以完成我们的任务了。

对于组合逻辑一般有两种实现方式:

- assign赋值语句。

- always+非阻塞赋值语句。

我们首先选择第一种方式。我们之前学过,assign语句是对wire类型进行赋值的,在题目中我们需要对out进行赋值,所以out的类型应该为wire类型,而对于assign右侧的变量没有要求。其次,由于不同的op会对应不同的out取值,我们可以利用三目运算符来完成操作。建立如下模型。

在这里插入图片描述

对于第二种方法,我们知道always@后面跟着的是敏感变量,如果某些值发生变化则执行always语句块。而对于always@()中的,敏感变量由综合器根据always里面的输入变量自动添加,不用自己考虑。

在always块中,我们可以选择利用case分支语句或者if分支语句对op进行判断,并利用always块保证对每次更新后的变量重新计算输出变量的值。同时由于always块中非阻塞赋值时左侧需要为reg类型,尽管我们知道实际电路out不会是寄存器,但是我们仍需要将其定义为reg类型。而对于右侧对数据类型没有严格的要求,所以用默认的wire即可实现。

在这里插入图片描述

虽然两种方法皆可以实现组合逻辑,但考虑接下来的任务,我们在此建议尽量使用assign赋值实现组合逻辑,而对always+非阻塞的实现方法做了解,只有在必要场景下使用后者:

  1. assign赋值语句的结构清晰:结合三目运算符的多层嵌套调用(如上文代码所示)可以非常简洁地实现对应的组合逻辑。

  2. always+非阻塞的实现方法容易产生混淆:方法容易与实现时序逻辑的非阻塞赋值混淆从而埋下问题,且实现的语句更为复杂。

因而我们建议:在不考虑综合后时序开销的情况下尽量用assign+三目的形式实现组合逻辑,从而保持always代码块中仅有非阻塞赋值语句。在考虑到时序开销的情况下(计组课设不对时序开销有任何严格约束),可以使用形如“assign result = (is_A & A) | (is_B & B);”的形式将连续三目表达式转换为并行的MUX

assign方式实现ALU标程

always(*)方式实现ALU标程


组合电路练习

组合电路练习1

我们在例题的基础上进行一定的更改

模块规格:

名称功能简述位宽方向
inA第一个运算数4I
inB第二个运算数4I
opop为2’b00,输出inA & inB
op为2’b01,输出inA | inB
op为2’b10,输出inA ^ inB
op为2’b11,输出inA + inB
2I
ans输出结果4O

模块名称:ALU

题目说明:

通过给定的输入inA,inB以及op,根据功能输出ans。

提示:可以根据已给出的代码进行更改,依旧推荐大家使用assign的方法。

本题是典型的纯组合逻辑,输出结果只由当前输入决定。由op信号选择运算方式,然后两个运算数进行相应的运算即可。

// ALU.v
module ALU(
    input [3:0] inA,
    input [3:0] inB,
    input [1:0] op,
    output [3:0] ans
    );

assign ans = (op == 2'b00) ? inA & inB : 
		 	(op == 2'b01) ? inA | inB :
			(op == 2'b10) ? inA ^ inB :
			(op == 2'b11) ? inA + inB : 4'b0000;

endmodule
组合电路练习2

我们在例题的基础上进行一定的更改

模块规格:

名称功能简述位宽方向
inA第一个运算数4I
inB第二个运算数4I
inC第三个运算数2I
opop为2’b00,输出inA 算数右移 inC位的结果
op为2’b01,输出inA 逻辑右移 inC位的结果
op为2’b10,输出inA - inB
op为2’b11,输出inA + inB
2I
ans输出结果4O

模块名称:ALU

题目说明:

通过给定的输入inA,inB,inC以及op,根据功能输出ans。

提示:可以根据已给出的代码进行更改。注意对于算数右移,a算数右移b位,可以采用$signed(​$signed(a) >>> b)。

本题和上一题相似,使用assign语句和三目运算符构建组合逻辑即可。需要注意的是题目提示里所述,有符号数的算术右移需要特别添加$signed()来确保Verilog执行有符号数的运算。

// ALU.v
module ALU(
    input [3:0] inA,
    input [3:0] inB,
    input [1:0] inC,
    input [1:0] op,
    output [3:0] ans
    );

assign ans = (op == 2'b00) ? $signed($signed(inA) >>> inC) : 
				 (op == 2'b01) ? inA >> inC :
				 (op == 2'b10) ? inA - inB :
				 (op == 2'b11) ? inA + inB : 4'b0000;

endmodule

时序电路题目描述

时序电路题目分析

我们在理论课以及Logisim教程中都已经学习过了有限状态机,并了解了状态机可以分类为Moore型和Mealy型两种。而这里我们将使用Verilog搭建一个有限状态机,通过这个过程,对时序电路的建模进行分析。

我们举一个简单的例子:

题目说明:

小明正在学习数数,只有当完整的从1数到3才会得到正确的反馈(输出1),其中不能插入任何数字,对数字1之前的数字没有任何要求。若不满足条件,则都会输出0。端口定义如下

在这里插入图片描述

我们约定,在每个时钟上升沿的时候,状态机都会读入小明输入的数字,并对小明输入的数字进行判断,并确定是否输出正确的结果。


时序电路状态机分析

这是一道Moore型状态机的题目。我们首先对状态机进行简单的分析,具体的细节在Logisim章节中有详细的讲解。首先我们可以判断一共存在4个状态S0-S3,分别代表了没有读取到任何有意义的数字,读取了1,读取了12,以及读取了123。当我们判断出读取了123后,将会输出1,否则一直会输出0。我们同时需要考虑处于任意一个状态时,输入了1,2,3时,状态将会如何转移。

在这里插入图片描述

并将我们整理到的图转化为更容易表达的表格。

在这里插入图片描述

当我们用Logisim处理时,需要分别去考虑状态的每一位前后的变化,并写出布尔表达式。但对于Verilog,我们只用判断出状态的转移就可以了,不需要手动的写任何逻辑表达式。


时序电路例题实现过程

我们知道,对于时序逻辑,需要一个寄存器去保存当前状态,用reg类型进行存储。由于我们一共有四种状态,所以状态需要有至少两位进行保存。请注意,这里的reg类型与组合逻辑中利用always方法将out定义为reg类型有所不同,这里的状态在电路中就是一个寄存器。为了满足在“时钟上升沿”进行操作,我们需要在always中将敏感变量设置为posedge clk,来满足在此时完成我们对状态进行更新的操作。更新操作需要结合num和当前状态进行更新,所以可以选择switch分支语句或者if分支语句进行操作。

同时我们需要考虑如何处理我们的ans。在logisim中,我们讨论的“输出”模块就是我们去计算ans的部分,对应于此,我们最好用组合逻辑对其进行赋值。这里我们利用assign语句+三目运算符进行判断,细节和之前讲述的组合逻辑很相似,不过这里的输入就变成了我们的“当前状态”这个变量。至此,我们的模型大体分为了两个部分,第一部分在always语句块中对状态进行更新,第二部分是在输出模块中利用assign对ans进行赋值。

代码具体如下:

首先我们可以使用宏定义对不同状态进行定义,用2‘b00代表S0…2’b11代表S3,方便我们以后对状态进行判断。

img

其次我们需要对状态变量进行初始化,否则其初始值为x,无法进行以后的操作。

img

之后是我们的always块,这里只选取当状态为S0的判断。这里需要注意我们需要同时对状态以及输入的num进行判断,一定要使用非阻塞赋值防止不必要的bug。这里注意我们always的敏感变量的选取。

img

最后我们的输出模块,对ans根据当前的状态进行判断。这里和我们的表格存在一定的出入。表格中的ans为下一状态发生后的ans取值,也就是当当前状态为S2,num为3时,下一状态为S3,同时一下个状态发生时我们的ans取1。而在verilog中由于是对当前的状态与ans进行分析,所以就当状态为S3时,ans取1。

img

最后我们利用仿真查看结果。(具体仿真教程将会在下一节进行讲解)

img

通过上述分析,我们的模块简单分为了3个部分,初始化状态更新输出更新,而分别利用initial块,always块以及assign语句完成。而为了降低我们书写程序的复杂性,我们引用了宏定义的对不同状态进行了化简表达。

对于大部分的状态机,关键点在于寻找到所有的状态,并正确的完整状态迁移,同时要处理好always的敏感变量,剩下的书写部分并不存在很大的难度。

如果同学自己动手尝试,会发现这个模块无法进行综合,也就是不能得到我们目标电路。这是由于在Verilog中一些模块是无法被综合的,而我们使用的initial就是其中之一。如果我们想对其进行综合,可以引入reset输入,当其置1时,需要将状态机进行复位,也就是状态被赋值为S0,而避免了手动初始化的尴尬局面,感兴趣的同学可以自己动手尝试一下。对于综合操作,在前7个project中不会应用,而会在P8中进行应用。所以我们不需要强求一定要写出可以综合的模块出来。

其实我们可以发现,Verilog作为硬件描述语言,我们目前是在行为级进行的描述,也就是描述模块完成了什么任务。通常是将模块分解为对应时序的always模块以及对应组合逻辑的assign赋值,并根据需求加入初始化的initial模块。我们并没有像Logisim中过分纠结使用具体什么电器元件,而是通过对模块进行抽象的行为层次的描述让EDA工具帮助我们综合出我们想要的电路。

最后我们书写完模块后,去验证模块的正确性需要利用ISim的仿真与调试功能,这将会在下一个章节进行讲述。

时序电路例题标程


时序电路练习

时序电路练习1

小明依旧在学习数数,但是这次测评机有了一定的变化,只要小明曾经数出过连续的123,就会输出正确的反馈(即输出将恒为1)。

我们可以根据题目画出相应的状态转移图。

img

端口要求:

名称功能简述位宽方向
num小明输入的数字2I
clk时钟信号1I
ans对小明输入的数字进行反馈,只要序列包含完整的1,2,3后输出1,否则输出01O

模块名称:counting

功能说明:

每个clk上升沿到来的瞬间,从num读入一个数字d。输入的数字对应d的二进制编码(01对应1,10对应2,11对应3。规定不会输入00)。我们对输入的数字串进行判断,如果输入的序列包含完整的123,无论上一个周期是否为3,输出信号都为1,否则输出0。

可以利用上一节样例进行更改。

输入:1 1 2 1 2 1 3 1 2 3 1 2 1 2 3 1

输出:0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1

注意模块名称和端口定义,并且提交相应的.v文件。

本题是上面例程问题的修改,其状态转移图已经给出,参照例程修改状态转移逻辑即可。具体的模块设计和注释如下:

// counting.v
module counting(
    input [1:0] num,
    input clk,
    output ans
    );

reg [1:0] status = 2'b00; // A register to store current status. Initial status should be set 2'b00.

// status: 2'b00 -> counting nothing; 
//			  2'b01 -> counting 1; 
//			  2'b10 -> counting 12; 
//			  2'b11 -> counting 123.
/*        current status  |  input | next status | ans
			2'b00		|	 1   |    2'b01	 |  0
			2'b00		|	 2   |    2'b00	 |  0
			2'b00		|	 3   |    2'b00	 |  0
			2'b01		|	 1   |    2'b01	 |  0
			2'b01		|	 2   |    2'b10	 |  0
			2'b01		|	 3   |    2'b00	 |  0
			2'b10		|	 1   |    2'b01	 |  0
			2'b10		|	 2   |    2'b00	 |  0
			2'b10		|	 3   |    2'b11	 |  1
			2'b11		|	 1   |    2'b11	 |  1
			2'b11		|	 2   |    2'b11	 |  1
			2'b11		|	 3   |    2'b11	 |  1
*/

always @(posedge clk) begin
	if (status == 2'b00) begin
		if (num == 2'b01) status <= 2'b01;
		else status <= 2'b00;
	end else if (status == 2'b01) begin
		if (num == 2'b01) status <= 2'b01;
		else if (num == 2'b10) status <= 2'b10;
		else status <= 2'b00;
	end else if (status == 2'b10) begin
		if (num == 2'b01) status <= 2'b01;
		else if (num == 2'b11) status <= 2'b11;
		else status <= 2'b00;
	end else if (status == 2'b11) begin
		status <= 2'b11;
	end else status <= 2'b00;
end

assign ans = (status == 2'b11) ? 1'b1 : 1'b0;

endmodule


时序电路题目2

小明依旧在学习数数,但是这次测评机有了一定的变化,小明在数到正确的数字x后,可以继续重复数x。当小明成功从1数到3后,就会输出正确的反馈(即输出为1)。

我们可以根据题目画出相应的状态转移图。

img

端口要求:

名称功能简述位宽方向
num小明输入的数字2I
clk时钟信号1I
ans对小明输入的数字进行反馈,若正确数到3则输出1,否则输出01O

模块名称:counting

功能说明:

每个clk上升沿到来的瞬间,从num读入一个数字d。输入的数字对应d的二进制编码(01对应1,10对应2,11对应3。规定不会输入00)。我们对输入的数字串进行判断,若输入序列从某一数字开始(即后缀),存在1…12…23…3,其中对于任意数字都出现大于等于1次(正则表达式1+2+3+),即输出1。否则输出0。

正则表达式: +:匹配前面的表达式一次或多次。

正则表达式仅供参考,感兴趣的同学可以深入了解。可以利用状态迁移图完成题目。

可以利用上一节样例进行更改。

输入:1 1 2 3 3 1 2 1 1 1 2 2 3 3 3 1

输出:0 0 0 1 1 0 0 0 0 0 0 0 1 1 1 0

注意模块名称和端口定义,并且提交相应的.v文件。

本题和上一题情况相同,只是修改了状态转移,无需赘述。代码和注释如下:

// counting.v
module counting(
    input [1:0] num,
    input clk,
    output ans
    );

reg [1:0] status = 2'b00; // A register to store current status. Initial status should be set 2'b00.

// status: 2'b00 -> counting nothing; 
//			  2'b01 -> counting 1; 
//			  2'b10 -> counting 12; 
//			  2'b11 -> counting 123.
/*        current status  | input  | next status | ans
			2'b00		|	 1   |    2'b01	 |  0
			2'b00		|	 2   |    2'b00	 |  0
			2'b00		|	 3   |    2'b00	 |  0
			2'b01		|	 1   |    2'b01	 |  0
			2'b01		|	 2   |    2'b10	 |  0
			2'b01		|	 3   |    2'b00	 |  0
			2'b10		|	 1   |    2'b01	 |  0
			2'b10		|	 2   |    2'b10	 |  0
			2'b10		|	 3   |    2'b11	 |  1
			2'b11		|	 1   |    2'b01	 |  0
			2'b11		|	 2   |    2'b00	 |  0
			2'b11		|	 3   |    2'b11	 |  1
*/

always @(posedge clk) begin
	if (status == 2'b00) begin
		if (num == 2'b01) status <= 2'b01;
		else status <= 2'b00;
	end else if (status == 2'b01) begin
		if (num == 2'b01) status <= 2'b01;
		else if (num == 2'b10) status <= 2'b10;
		else status <= 2'b00;
	end else if (status == 2'b10) begin
		if (num == 2'b01) status <= 2'b01;
		else if (num == 2'b10) status <= 2'b10;
		else if (num == 2'b11) status <= 2'b11;
		else status <= 2'b00;
	end else if (status == 2'b11) begin
		if (num == 2'b01) status <= 2'b01;
		else if (num == 2'b11) status <= 2'b11;
		else status <= 2'b00;
	end else status <= 2'b00;
end

assign ans = (status == 2'b11) ? 1'b1 : 1'b0;

endmodule
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
北航计组Verilog是指北京航空航天大学计算机学院的计算机组成原理课程设计中使用的Verilog HDL语言。Verilog HDL是一种硬件描述语言,用于设计和建模数字电路。在北航计算机学院的计算机组成原理课程设计中,学生需要使用Verilog HDL来实现一些具体的题目实例,如课程设计中的PreProject-Verilog HDLISE Verilog题目实例分析以及PreProject-Verilog HDLISE的获取和使用。通过这些实践,学生可以学习和理解Verilog HDL的基本语法和使用方式,以及时序电路的建模与分析。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [北航计算机组成原理课程设计-2020 PreProject-Verilog HDLISE-Verilog题目实例分析](https://blog.csdn.net/JeremyZhao1998/article/details/113569664)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [北航计算机组成原理课程设计-2020 PreProject-Verilog HDLISE-ISE的获取和使用](https://blog.csdn.net/JeremyZhao1998/article/details/109180170)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值