这里写目录标题
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触发器)。实例化它们中的三个,然后将它们连接在一起,使移位寄存器的长度为3。clk端口需要连接到所有实例。
提供给你的模块是:
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 );
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