HDLBits-Verilog学习小结(四)Module

1 Module简述

到目前为止,您已经熟悉模块(Module),该模块是通过输入和输出端口与其外部交互的电路。更大、更复杂的电路是通过将较小的模块和其他连接在一起的模块(例如,assign语句和always块)组成较大的模块而构建的。这形成了一个层次结构,因为模块可以包含其他模块的实例。

下图显示了带有子模块的非常简单的电路。在本练习中,创建模块mod_a的一个实例,然后将模块的三个引脚(in1,in2和out)连接到顶级模块的三个端口(导线a,b和out)。

Module
连接模块时,只有模块上的端口很重要。您不需要知道模块内部的代码。模块mod_a的代码如下所示:

module mod_a ( input in1, input in2, output out );
    // Module body
endmodule

只要使用的所有模块都属于同一个项目,就可以通过实例化另一个模块内部的模块来创建模块的层次结构(这样,编译器就知道在哪里可以找到模块)。一个模块的代码未写在另一模块的主体内(不同模块的代码未嵌套)。 您可以通过端口名称或端口位置将信号连接到模块。

  • By position
    通过位置将电线连接到端口的语法应该很熟悉,因为它使用类似C的语法。实例化模块时,根据模块的声明从左到右连接端口。
    例如:mod_a instance1 (wa,wb,wc);这将实例化类型为mod_a的模块,并为其提供实例名称“ instance1”,然后将信号wa(在新模块之外)连接到新模块的第一个端口(in1),将wb连接到第二个端口(in2),然后wc到第三个端口(out)。
    这种语法的一个缺点是,如果模块的端口列表发生更改,则还需要找到并更改模块的所有实例以匹配新模块。
  • By name
    通过名称将信号连接到模块的端口,即使端口列表发生更改,也可以保持电线正确连接。但是,此语法更为冗长。
    例如:mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );这实例化了一个名为“ instance2”的mod_a类型的模块,然后将信号wa(在模块外部)连接到名为in1的端口,将wb连接到名为in2的端口,并将wc连接到名为out的端口。
    请注意,此处的端口顺序无关紧要,因为将以正确的名称进行连接,而不管其在子模块的端口列表中的位置如何。还要注意此语法中端口名称前的句点

question:
实例化下图所示电路。

Module_practice

solution:

module top_module ( input a, input b, output out );
    mod_a instance1 (.out(out),.in1(a),.in2(b));
endmodule

1.1 Connecting ports by position

question:
此问题与上一个(模块)相似。您将获得一个名为mod_a的模块,该模块具有2个输出和4个输入的顺序。您必须按位置将这6个端口连接到顶级模块的端口out1,out2,a,b,c和d。
获得以下模块:
module mod_a ( output, output, input, input, input, input );

position
solution:

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

endmodule

1.2 Connecting ports by name

question:
获得一个名为mod_a的模块,该模块按顺序具有2个输出和4个输入。必须按名称将6个端口连接到顶级模块的端口。
获得以下模块:
module mod_a ( output out1, output out2, input in1, input in2, input in3, input in4);

name

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);
    mod_a instance3 (.out1(out1),.out2(out2),.in1(a),.in2(b),.in3(c),.in4(d));

endmodule

2 Module shift

2.1 D flip-flops’ Module shift

question:
获得一个模块my_dff,该模块具有两个输入和一个输出(实现了一个D触发器)。实例化它们中的三个,然后将它们链接在一起以构成一个长度为3的移位寄存器。clk端口需要连接到所有实例。现有以下模块D触发器:
module my_dff ( input clk, input d, output q );
注意,要进行内部连接前,需要声明一些导线。在命名您的电线和模块实例时要小心:名称必须唯一
D

D触发器是在CP正跳沿前接受输入信号,正跳沿时触发翻转,正跳沿后输入即被封锁,三步都是在正跳沿后完成,所以有边沿触发器之称。其功能表如下:
D触发器的功能表
solution:

module top_module ( input clk, input d, output q );
    wire q1_wire;
    wire q2_wire;
    my_dff instance4 (.clk(clk),.d(d),.q(q1_wire));
    my_dff instance5 (.clk(clk),.d(q1_wire),.q(q2_wire));
    my_dff instance6 (.clk(clk),.d(q2_wire),.q(q));
       
endmodule

仿真结果如下图:

D)silmulate

2.2 An extension of module_shift

现在,模块不再是仅单个引脚的模块端口,而是具有以向量为端口的模块,您将在其中附加导线矢量而不是普通导线。

像Verilog中的其他任何地方一样,端口的向量长度不必与连接到其的电线相匹配,但是这将导致向量的零填充或局部化。

本练习不使用向量长度不匹配的连接。

question:
我们现在有一个模块my_dff8,该模块具有两个输入和一个输出(实现一个8位的D触发器)。实例化它们中的三个,然后将它们链接在一起以构成一个长度为3的8位宽移位寄存器。
此外,创建一个4比1多路复用器,该多路复用器根据sel [1:0]选择输出的内容:输入d的值,在第一个,第二个之后或在第三个D触发器之后。 (本质上,sel选择从0到3个时钟周期延迟输入的周期。)
提供模块是:
module my_dff8 ( input clk, input [7:0] d, output [7:0] q );
没有提供多路复用器。一种可能的写方法是在always块中包含case语句

extension
solution:

module top_module ( 
    input clk, 
    input [7:0] d, 
    input [1:0] sel, 
    output [7:0] q 
);
    wire [7:0] q1;
    wire [7:0] q2;
    wire [7:0] q3;//wire [7:0] q1,q2,q3;
    my_dff8 instance0 (.clk(clk),.d(d),.q(q1));
    my_dff8 instance1 (.clk(clk),.d(q1),.q(q2));
    my_dff8 instance2 (.clk(clk),.d(q2),.q(q3));
    always @(*)
        begin
            case(sel)
                2'b00:q = d;
                2'b01:q = q1;
                2'b10:q = q2;
                2'b11:q = q3;
            endcase
        end
        
endmodule

2.3 Module add

2.3.1 Create a 32-bit adder

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

question:
如下图所示将模块连接在一起。提供的模块add16具有以下声明:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

solution:

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire cin;
    wire [15:0] sum1,sum2;
    wire cout1,cout2;
    assign cin = 1'b0;
    add16 instance1 (.a(a[15:0]),.b(b[15:0]),.cin(cin),.sum(sum1),.cout(cout1));
    add16 instance2 (.a(a[31:16]),.b(b[31:16]),.cin(cout1),.sum(sum2),.cout(cout2));
    assign sum = {sum2,sum1};
    
endmodule

2.3.2 Create a circuit with two levels of hierarchy

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

与module_add类似,这里将提供执行16位加法的模块add16。必须实例化其中两个以创建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 );

回想一下,一个全加法器计算a + b + cin的总和和进位。 总之,此设计包含三个模块

  • top_module——顶级模块,其中包含两个add16
  • 提供的add16 ——一个16位加法器模块,包含add1
  • add1 —— 1位全加法器模块。

hierarchy

solution:

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

    wire cin;
    wire cout1;
    wire cout2;
    wire [15:0] sum1,sum2;
    assign cin = 1'b0;
    add16 instance0 (a[15:0],b[15:0],cin,sum1,cout1);
    add16 instance1 (a[31:16],b[31:16],cout1,sum2,cout2);
    assign sum = {sum2,sum1};
    
endmodule

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

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

endmodule

2.3.3 a carry-select adder

纹波进位加法器的一个缺点(请参阅前面)是加法器计算进位(在最坏的情况下从进位)的延迟相当慢,并且第二级加法器无法开始计算其进位。进行直到第一阶段加法器完成为止。这使加法器变慢。

ripple-carry adder
进位选择加法器是一种改进,如下所示。第一级加法器与之前相同,但是我们复制了第二级加法器,一个假设进位= 0,一个假设进位= 1,然后使用快速2对1多路复用器选择哪个结果碰巧是正确的。

carry-select adder

question:
在本练习中,将为您提供与上一练习相同的模块add16,该模块将两个带进位的16位数字相加,并产生一个进位与16位和。
我们使用自己的16位2对1多路复用器实例化其中的三个,以构建进位选择加法器。 如下图所示将模块连接在一起。
提供的模块add16具有以下声明:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

solution:

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire cin1,cin2,cin3,cout1,cout2,cout3;
    wire [15:0] sum1,sum2,sum3;
    assign cin1 = 1'b0;
    assign cin2 = 1'b0;
    assign cin3 = 1'b1;
    add16 add1 (a[15:0],b[15:0],cin1,sum1,cout1);
    add16 add2 (a[31:16],b[31:16],cin2,sum2,cout2);
    add16 add3 (a[31:16],b[31:16],cin3,sum3,cout3);
    
    always@(*)
        begin
        	case(cout1)
                1'b0:sum={sum2,sum1};
                1'b1:sum={sum3,sum1};
            endcase
        end
endmodule

2.3.4 An adder-subtractor

可以通过有选择地取反一个输入来从加法器构建一个加法器-减法器,这等效于将输入取反然后加1。净结果是一个可以执行两种操作的电路:(a + b + 0) and (a + ~b + 1)。

设计减法器可以转换为补码来实现,即:
X − Y = [ X 补 ] + [ − Y 补 ] X-Y=[X_补]+[-Y_补] XY=[X]+[Y]Y的补码是其反码加1,Y的反码可以用异或门来实现,得到反码后利用全加器来实现 X + Y 反 X+Y_反 X+Y并使最低位进位位为1。异或门的其中一个输入端控制信号C,当C=1时,实现减法;当C=0时,实现加法。异或的运算规则如下(XOR门也可以看作是可编程反相器,其中一个输入控制是否应将另一个反相):
异或门
以下两个电路都是XOR门:
XOR门

question:
每当sub为1时,使用32位宽的XOR门将b输入反相。(也可以将b [31:0]视为与sub复制32次的XOR运算。请参见复制运算符。)还将子输入连接到加法器的输入端。
这里提供了一个16位加法器模块,需要实例化两次:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

adder-subtractor
solution:

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);
    wire cout1,cout2;
    wire [15:0] sum1,sum2;
    wire [31:0] b_n;
    assign b_n = b ^ {32{sub}};
    
    add16 add1 (a[15:0],b_n[15:0],sub,sum1,cout1);
    add16 add2 (a[31:16],b_n[31:16],cout1,sum2,cout2);
    
    assign sum = {sum2,sum1};

endmodule
  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值