Module shift8(八位移位寄存器)


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

您会得到一个模块my_dff8,它有两个输入和一个输出(它实现了一组8d触发器)。实例化其中三个,然后将它们链在一起,生成一个长度为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语句。

image.png

Module Declaration

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

答案:

module top_module ( 
    input clk, 
    input [7:0] d, 
    input [1:0] sel, 
    output [7:0] q 
    wire [7:0] q1,q2,q3;
    my_dff8 (clk,d, q1 );
    my_dff8 (clk,q1, q2 );
    my_dff8 (clk,q2, q3 );
    always@(*)begin
        case(sel)
            0: q = d;
            1: q = q1;
            2: q = q2;
            3: q = q3;
        endcase
endmodule

加法器模块1


您将得到一个模块add16,它执行一个16位的添加操作。实例化其中两个以创建一个32位加法器。一个add16模块计算相加结果的低16位,而第二个add16模块在收到第一个加法器的进位后计算结果的高16位。您的32位加法器不需要处理进位(假设0)或进位(忽略),但内部模块需要正确工作。(换句话说,add16模块执行16位a + b + cin,而您的模块执行32位a + b)。

如图所示将这些模块连接在一起: module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

image.png

Module Declaration

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

答案(按名字连接比较常用,所以这里只给出按名字的结果):

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
    wire cout_to_cin;
    add16 u_add16_l( .a(a[15:0]), .b(b[15:0]), .cin(0), .sum(sum[15:0]), .cout(cout_to_cin));
    add16 u_add16_h( .a(a[31:16]), .b(b[31:16]), .cin(cout_to_cin), .sum(sum[31:16]), .cout());
endmodule

加法器模块2


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

综上所述,本设计主要分为三个模块:

  • top_module -你的顶级模块。
  • add16 -16位加法器模块。
  • add1 - 1位全加法器模块。

image.png

Module Declaration

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

答案:

module top_module (
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
    wire cout_to_cin;
    add16 u_add16_l( .a(a[15:0]), .b(b[15:0]), .cin(0), .sum(sum[15:0]), .cout(cout_to_cin));
    add16 u_add16_h( .a(a[31:16]), .b(b[31:16]), .cin(cout_to_cin), .sum(sum[31:16]), .cout());
endmodule
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

进位选择加法器


纹波进位加法器的一个缺点(前面的练习)是,加法器计算进位的延迟(在最坏的情况下,从进位算起)相当缓慢,第二级加法器直到第一级加法器完成后才能开始计算其进位。这使得加法器变慢。一个改进是进位选择加法器,如下所示。第一级加法器和之前一样,但是我们复制了第二级加法器,一个假设进位为0,一个假设进位为1,然后使用快速的2比1多路复用器来选择哪个结果恰好是正确的。

在本练习中,提供了与前一个练习相同的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 );

image.png

Module Declaration

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

答案:

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
    wire cout_to_cin;
    wire [15:0]sum_0;
    wire [15:0]sum_1;
    add16 u_add16_1( .a(a[15:0]), .b(b[15:0]), .cin(0), .sum(sum[15:0]), .cout(cout_to_cin));
    add16 u_add16_2( .a(a[31:16]), .b(b[31:16]), .cin(0), .sum(sum_0), .cout());
    add16 u_add16_3( .a(a[31:16]), .b(b[31:16]), .cin(1), .sum(sum_1), .cout());
    always@(*)begin
        case(cout_to_cin)
            0: sum[31:16]= sum_0;
            1: sum[31:16]= sum_1;
        endcase
endmodule

加减法器


加-减法器可以由加法器建立,可以选择对一个输入取反,这相当于将输入取反,然后加1。最终得到的电路可以进行两种运算:(a + b + 0)和(a + ~b + 1)。

构建下面的加减器。

你提供了一个16位加法器模块,你需要实例化两次:

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] xor与子复制32次。看到复制操作符)。也连接子输入到加法器的进位。

image.png

Module Declaration

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

答案:

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
    wire cout_to_cin;
    wire [31:0] b1 ={32{sub}}^b;
    add16 u_add16_1( .a(a[15:0]), .b(b1[15:0]), .cin(sub), .sum(sum[15:0]), .cout(cout_to_cin));
    add16 u_add16_2( .a(a[31:16]), .b(b1[31:16]), .cin(cout_to_cin), .sum(sum[31:16]), .cout());
endmodule
Verilog模块例化
这种方法将需要例化的模块端口与外部信号按照其名字进行连接,端口顺序随意,可以与引用 module 的声明端口顺序不一致,只要保证端口名字与外部信号匹配即可。
Verilog 设计方法
Verilog 的设计多采用自上而下的设计方法(top-down)。即先定义顶层模块功能,进而分析要构成顶层模块的必要子模块;然后进一步对各个模块进行分解、设计,直到到达无法进一步分解的底层功能块。这样,可以把一个较大的系统,细化成多个小系统,从时间、工作量上分配给更多的人员去设计,从而提高了设计速度,缩短了开发周期。
注释是对代码的“提示”,而不是文档。程序中的注释不可喧宾夺主,注释太多会让人眼花缭乱。 边写代码边注释,修改代码的同时要修改相应的注释,以保证注释与代码的一致性,不再有用的注释要删除。 如果代码本来就是清楚的,则不必加注释。