Module
到目前為止,您已經熟悉了模塊,它是一種通過輸入和輸出端口與外部交互的電路。更大、更復雜的電路是由更小的模塊組成更大的模塊,并將其他部分(如賦值語句和always塊)連接在一起來構建的。這形成了一個層次結構,因為模塊可以包含其他模塊的實例。
下圖顯示了一個非常簡單的帶有子模塊的電路。在本練習中,創建模塊mod_a的一個實例,然后將模塊的三個引腳(in1、in2和out)連接到頂層模塊的三個端口(連接a、b和out)。模塊mod_a是為你提供的-你必須實例化它。
連接模塊時,只有模塊上的端口是重要的。您不需要知道模塊內部的代碼。模塊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)。這種語法的一個缺點是,如果模塊的端口列表改變了,模塊的所有實例化也需要被找到并改變以匹配新的模塊。
按名字連接
通過名稱將信號連接到模塊的端口,這樣即使端口列表發生變化,線路也能保持正確連接。但是,這種語法更加冗長。
mod_a instance2 (.out (wc) .in1 (wa) .in2 (wb));
上面這行代碼實例化了一個名為“instance2”的mod_a類型的模塊,然后將信號wa(模塊外部)連接到名為in1的端口,wb連接到名為in2的端口,wc連接到名為out的端口。請注意,端口的順序在這里是不相關的,因為無論它在子模塊的端口列表中的位置如何,都將連接到正確的名稱。還要注意此語法中緊鄰端口名的句點。
module top_module ( input a, input b, output out );
通過端口名稱或端口位置將信號連接到模塊。答案:通過位置連接
通過名字連接
module top_module ( input a, input b, output out );
mod_a instance2 (
.out(out),
.in1(a),
.in2(b)
);
endmodule
按位置進行模塊連接
這個問題類似于上一個。給定一個名為mod_a的模塊,該模塊按此順序有2個輸出和4個輸入。您必須按位置將6個端口連接到頂級模塊的端口out1、out2、a、b、c和d,按此順序。 你會得到以下模塊:
module mod_a ( output, output, input, input, input, input );
Module Declaration
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
答案:
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a inst1_mod_a( out1, out2, a, b, c, d );
endmodule
按名字進行模塊連接
這個問題類似前兩個。給定一個名為mod_a的模塊,該模塊按一定順序有2個輸出和4個輸入。你必須將這6個端口按名稱連接到你的頂級模塊的端口:
你會得到以下模塊:
module mod_a ( output out1, output out2, input in1, input in2, input in3, input in4);
Module Declaration
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
答案:
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a u_mod_a(
.out1(out1),
.out2(out2),
.in1(a),
.in2(b),
.in3(c),
.in4(d)
);
endmodule
Module shift(移位寄存器)
你會得到一個模塊my_dff,它有兩個輸入和一個輸出(它實現了D觸發器)。實例化它們中的三個,然后將它們鏈在一起,生成一個長度為3的移位寄存器。clk端口需要連接到所有實例。
module my_dff ( input clk, input d, output q );
注意,要建立內部連接,需要聲明一些連接。在命名連線和模塊實例時要小心:名稱必須唯一。
Module Declaration
module top_module ( input clk, input d, output q );
答案:按位置:
module top_module ( input clk, input d, output q );
wire q1,q2;
my_dff u1_my_dff( clk, d, q1 );
my_dff u2_my_dff( clk, q1, q2 );
my_dff u3_my_dff( clk, q2, q );
endmodule
按名字:
module top_module ( input clk, input d, output q );
wire q1,q2;
my_dff u1_my_dff( .clk(clk), .d(d), .q(q1) );
my_dff u2_my_dff( .clk(clk), .d(q1), .q(q2) );
my_dff u3_my_dff( .clk(clk), .d(q2), .q(q ));
endmodule
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語句。
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
end
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 );
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位全加法器模塊。
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 );
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
end
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次。看到復制操作符)。也連接子輸入到加法器的進位。
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