HDLBits(3) Modules: Hierarchy合集

5 篇文章 3 订阅

0 写在前面:

本文讲得做的一个词:“实例化”,其实类似于“函数调用”,这里可以理解为“不同功能的模块的调用

Modules

现在,你已经熟悉了一个模块,它是一个通过输入和输出端口与外部进行交互的电路。更大、更复杂的电路是由更小的模块和连接在一起的其他部分(如assign语句和always块)组成更大的模块。这就形成了一个层次结构,因为模块可以包含其他模块的实例

连接模块时,只关注模块上的端口。您不需要知道模块中的代码。mod_a模块的代码如下:

module mod_a ( input in1, input in2, output out );
    // Module body模块的主体
endmodule

模块的层次结构是通过在另一个模块中实例化一个模块来创建的,只要所有使用的模块都属于同一个项目(这样编译器就知道在哪里可以找到这个模块)。一个模块的代码不会写在另一个模块的主体中(不同模块的代码不会嵌套)。

比如这个是计数器模块,这个是点灯的模块(呼吸灯的例子)

您可以通过端口名称端口位置将信号连接到模块。为了额外的练习**,两种方法**都试试。
在这里插入图片描述

连接信号到模块端口

有两种常用的方法将导线连接到端口:通过位置或通过名称。
下面分别介绍,各有优缺点,不过推荐后者的写法

通过位置

按位置将导线连接到端口的语法应该很熟悉,因为它使用了类似于c的语法。当实例化一个模块时,端口根据模块的声明****从左到右连接。例如:

mod_a instance1 ( wa, wb, wc ); 

它实例化一个类型为mod_a的模块,并给它一个实例名为“instance1”,然后将信号wa(在新模块外部)连接到新模块的第一个端口(in1), wb连接到第二个端口(in2), wc连接到第三个端口(out)。

相当于是此处 (wa, wb, wc )是新模块的端口名,需要与以前的进行连接
,是默认对应的以前的模块的顺序

这种语法的一个缺点是,如果模块的端口列表发生了变化,还需要找到并更改模块的所有实例化,以匹配新模块。
端口多了,或者需要变化的时候,就不便于处理了。看起来也不直观

通过名称

通过名称将信号连接到模块的端口允许电线保持正确连接,即使端口列表发生了变化。然而,这种语法更冗长
But不容易错,容易更改,对应更好

Mod_a instance2 (.out(wc), .in1(wa), .in2(wb));

啊其实更直观的是这么排版的

Mod_a instance2 (
	.out	(wc), 
	.in1	(wa), 
	.in2	(wb));

上面的行实例化了一个名为“instance2”的mod_a类型的模块,然后将信号wa(模块外部)连接到名为in1的端口,wb连接到名为in2的端口,wc连接到名为out的端口。注意端口的顺序在这里是无关紧要的,因为连接将被建立到正确的名称,而不管它在子模块的端口列表中的位置。还要注意这个语法中端口名前面句点!!!!!

Problem Statement

下图显示了一个带有子模块的非常简单的电路。在本练习中,**创建模块mod_a的一个实例,然后将模块的三个引脚(in1、in2和out)连接到顶级模块的三个端口(导线a、b和out)。**模块mod_a已经提供给你了——你必须实例化它。
在这里插入图片描述

Writing Code

位置
容易出错的是,mod_a inst2 ( a, b, out );括号里的是新的端口!默认对应旧的!

module top_module ( input a, input b, output out );//这是顶层模块
    //将模块的三个引脚(in1、in2和out)连接到 顶级模块 的三个端口(导线a、b和out)。
    //mod_a instance1 (in1,in2,out);错误写法!!!!
	mod_a inst2 ( a, b, out );
endmodule

名称

module top_module ( input a, input b, output out );//这是顶层模块
    //将模块的三个引脚(in1、in2和out)连接到 顶级模块 的三个端口(导线a、b和out)。
	mod_a instance1 (
    .out	(out), 
	.in1	(a), 
	.in2	(b));
endmodule

容易出错的一个点:哪个是顶层模块,在做还是在右?
可以这样记忆,左边是新的,右边是连接的旧的
注意中英文的符号不然你错误都找不到。。。

Module pos:connecting ports by position

Problem Statement

此问题与前一个问题(模块)类似。你有一个名为mod_a的模块,它有2个输出和4个输入。您必须按位置将这6个端口连接到顶级模块的端口out1、out2、a、b、c和d,按此顺序。

你会看到以下模块:

module mod_a ( output, output, input, input, input, input );

在这里插入图片描述

Writing Code

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);
    //by position
    //mod_a instance (a,b,c,d,out1,out2);这是错的!为啥
    //题目说了module mod_a ( output, output, input, input, input, input );对应!!!!
    //要对应例化的模块!!!!
    mod_a instance1 (out1,out2,a,b,c,d);
    //界面上的中文逗号颜色更深,英文逗号颜色很浅,要细一些

endmodule

Module pos:connecting ports by name

这个问题类似于模块。你有一个名为mod_a的模块,它有2个输出和4个输入,按照一定的顺序。你必须按名称将这6个端口连接到你的顶级模块的端口:
在这里插入图片描述

module mod_a ( output out1, output out2, input in1, input in2, input in3, input in4);

在这里插入图片描述

Problem Statement

Writing Code

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);

    mod_a name2 (.out1(out1), .out2(out2), .in1(a), .in2(b), .in3(c), .in4(d));
    //不过建议这样写,更格式化一些
    /*
 mod_a name2 (
 	.out1(out1), 
 	.out2(out2), 
 	.in1(a), 
 	.in2(b), 
 	.in3(c),
 	 .in4(d)
);*/
endmodule

Module shift:three modules

模块的转变

Problem Statement

你有一个模块my_dff有两个输入和一个输出(它实现了一个D触发器)。实例化它们中的三个,然后将它们连接在一起,使移位寄存器的长度为3clk端口需要连接到所有实例

提供给你的模块是:

module my_dff ( input clk, input d, output q );

注意,要进行内部连接,需要声明一些连接。在命名连接和模块实例时要小心:名称必须是唯一的
在这里插入图片描述

Writing Code

module top_module ( input clk, input d, output q );
    
    wire q1,q2;
    
	my_dff dff1( clk, d, q1);
    my_dff dff2( clk, q1, q2);
    my_dff dff3( clk, q2, q);
        
endmodule

Module shift8:modules and vectors

这个练习是module_shift的扩展。而不是模块端口只是单引脚,我们现在有模块与矢量作为端口,你将附加线矢量,而不是普通的电线。与Verilog中的其他地方一样,端口的矢量长度不必与连接到它的线匹配,但这将导致矢量的零填充或构造。本练习不使用向量长度不匹配的连接。

Problem Statement

给定一个模块my_dff8,它有两个输入和一个输出(实现一个8位的D触发器)。实例化my_dff8三次,然后将它们连接在一起,形成一个长度为3的8位宽移位寄存器。另外,创建一个4对1的多路复用器(未提供),根据sel[1:0]选择输出内容:输入d处的值,第一个、第二个或第三个d触发器之后的值。(本质上,sel选择输入延迟多少周期,从0到3个时钟周期。)

此外,创建一个4选1多路复用选择器(没有提供),它根据sel[1:0]选择输出什么:在输入d处的值,在第一个D触发器之后、第二个D触发器之后或第三个D触发器之后。(本质上,sel选择多少周期来延迟输入,从0到3个时钟周期。)

module my_dff8 ( input clk, input [7:0] d, output [7:0] q );

## Hint

Writing Code

module top_module (
	input clk,
	input [7:0] d,
	input [1:0] sel,
	output reg [7:0] q
);

	wire [7:0] o1, o2, o3;		// output of each my_dff8
	
	// Instantiate three my_dff8s
	my_dff8 d1 ( clk, d, o1 );
	my_dff8 d2 ( clk, o1, o2 );
	my_dff8 d3 ( clk, o2, o3 );

	// This is one way to make a 4-to-1 multiplexer
	always @(*)		// Combinational always block
		case(sel)
			2'h0: q = d;
			2'h1: q = o1;
			2'h2: q = o2;
			2'h3: q = o3;
		endcase

endmodule

Module add 1

Problem Statement

给你一个模块add16,它执行16位加法。实例化其中两个以创建一个32位加法器。一个add16模块计算结果的下16位,而第二个add16模块在接收到第一个加法器的进位后计算结果的上16位。设计的32位加法器不需要处理低位的进位(假设为0)向高位的进位(忽略),但是内部模块需要这样做才能正常工作。(换句话说,add16模块执行16位a + b + cin,而您的模块执行32位a + b)。

将模块连接在一起,如下图所示。所提供的模块add16有以下声明:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

在这里插入图片描述

Writing Code

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    //声明的时候主要考虑的是 位宽 
    wire cin,cout1,cout2;
    wire [15:0] sum1,sum2;
    //初始化cin为0
    assign cin = 0;
    //例化两个模块
    //1 by position
    //module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
    add16 add1( a[15:0], b[15:0], cin, sum1, cout1 );
    add16 add2( a[31:16], b[31:16], cout1, sum2, cout2 );
    /*2 by name 
    //add1 自己写吧
   add16 add2( 
        .a(a[31:16]),
        .b(b[31:16]), //左边的新的端口名字,右边是所连接的旧的端口名
        .cin(cout1), 
        .sum(sum2), 
        .cout(cout2));
    */
    //输出结果
    
    assign sum={sum2[15:0],sum1[15:0]};
    //assign sum={sum2,sum1};因为位宽对的上,所以这样写也是没问题的

endmodule
	


Module add 2:Module fadd

在本练习中,您将创建一个具有两层层次结构的电路。您的top_module将实例化两个add16副本(提供的),每个副本将实例化16个add1副本(必须写入)。因此,必须编写两个模块:top_module和add1。

与module_add一样,也有一个模块add16,它执行16位加法。您必须实例化其中两个以创建一个32位的加法器。一个add16模块计算加法结果的下16位,而第二个add16模块计算结果的上16位。您的32位加法器不需要处理进位(假设为0)或进位(忽略)

将add16模块连接在一起,如下图所示。所提供的模块add16有以下声明:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

在每个add16中,实例化16个完整加法器(模块add1,未提供)来实际执行添加。你必须写一个完整的加法器模块,声明如下:

module add1 ( input a, input b, input cin, output sum, output cout ); 

Problem Statement

回想一下,全加法器进位CO与和SUM计算a+b+cin。
综上所述,本次设计有三个模块:

top_module — 顶层模块包括两个add16模块
add16, provided — 一个16位加法器包括16个一位加法器
add1 — 一个一位全加器模块
如果你的提交缺少一个模块add1,你会得到一个错误消息:error (12006): Node instance "user_fadd[0]. "A1 “实例化未定义的实体"add1”。

在这里插入图片描述

Hint

完整的加法器方程:

Sum = a ^ b ^ cin

Cout = a&b | a&cin b&cin

Writing Code

//顶层模块    例化add1模块,成为16位的计算器
module top_module (
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);//
    wire cin1,cout1,cout2;
    wire [15:0] sum1,sum2;
    
    assign cin1=0;
    //module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
    add16 add1 (a[15:0], b[15:0], cin1, sum1[15:0], cout1);
    add16 add2 (a[31:16], b[31:16], cout1, sum2[15:0], cout2);
    assign sum = {sum2, sum1};//输出结果
    //类似上一节
endmodule


//1位的加法器
module add1 ( input a, input b, input cin,   output sum, output cout );

	assign sum = a ^ b ^ cin;
	assign cout = a&b | a&cin | b&cin;
// Full adder module here

endmodule

Module addsub:carry_selector adder

Problem Statement

行波进位加法器的一个缺点(参见前面的练习)是加法器计算执行的相当第二级加法器在第一级加法器完成之前不能开始执行计算,这使得加法器变慢。一种改进是进位选择加法器,如下所示。第一级加法器和前面一样,但是我们复制了第二级加法器一个假设进位为0,另一个假设进位为1,然后使用一个快速的2选1多路复用器来选择执行哪一个。

//相当于提前准备下一步的操作,也就两种情况

在本练习中,向你提供了与前一个练习相同的模块add16,它将两个16位数字与低位进位相加,并生成一个16位的和。必须使用自己的16位2选1多路复用器实例化三个add16模块来构建c进位选择加法器。

给定的add16模块是:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

![在这里插入图片描述](https://img-blog.csdnimg.cn/36f2694aebda48d2a61f2249bef1f46c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA54-t6IqxaQ==,size_20,color_FFFFFF,t_70,g_se,x_16

Writing Code

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);

    wire cin,cin1,cin2,cout;
    wire [15:0] sum0, sum1, sum2,sum_h;//sum_h是sum的高位,sum1,sum2是计算的可能的高位的结果
    
    assign cin  = 0;
    assign cin1 = 0;//提前计算第二步的不进位情况
    assign cin2 = 1;//提前计算第二步的进位情况
    
    //实例化模块  eg:module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
    add16 add0 (a[15:0], b[15:0], cin, sum0[15:0], cout);
    add16 add1 (a[31:16], b[31:16], cin1, sum1[15:0],);//最后一位可以省略的写法(高位再进位无关紧要)
    add16 add2 (a[31:16], b[31:16], cin2, sum2[15:0],);
    
    //多路复用器
    always @(*) 
        begin
            case(cout)//用低位向高位的进位来筛选,赋值给sum_h
                1'b0: sum_h = sum1;
                1'b1: sum_h = sum2;
            endcase//有case 就有endcase
        end
        
    assign sum = {sum_h,sum0};//连接sum
    
endmodule

Module addsub:Adder_subtractor

Problem Statement

加法减法器可以利用加法器构建,方法是选择性地其中一个输入求反,这等价于先对输入求反码,然后再加1。最终的结果是一个可以做两种操作(即加法和减法)的电路:(a + b + 0)和(a + ~b + 1)。

给定的add16模块是:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

当sub为1时,使用32位宽的异或门来对输入b取反(这也可以看作是b[31:0] 与复制32次的sub异或),还要将sub连接到加法器的低位来的进位。
在这里插入图片描述

Hint

异或门也可以看作是一个可编程的逆变器,其中一个输入控制另一个应该被倒转。以下两个电路都是异或门:
在这里插入图片描述

Writing Code

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);

    wire [31:0] b_n;
    wire cout1,cout2;
    
    assign b_n = b ^ {32{sub}};
    add16 add0 (a[15:0], b_n[15:0], sub, sum[15:0], cout1);
    add16 add1 (a[31:16], b_n[31:16], cout1, sum[31:16], cout2);
    
endmodule


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值