HDLBits(3)——Modules
----- 20. Module -----
Problem Statement
The figure below shows a very simple circuit with a sub-module. In this exercise, create one instance of module mod_a
, then connect the module’s three pins (in1, in2, and out) to your top-level module’s three ports (wires a, b, and out). The module mod_a
is provided for you — you must instantiate(实例化) it.
Expected solution length: Around 1 line.
Answer
module mod_a ( input in1, input in2, output out );
// Module body
endmodule
//批注:上面部分不属于答案内容,提交时应删去。这里加入本部分是为了让代码完整,下同
module top_module ( input a, input b, output out );
mod_a instance1 ( a, b, out );
// other way: mod_a instance2 ( .out(wc), .in1(wa), .in2(wb));
endmodule
Note: Module
- The hierarchy of modules is created by instantiating one module inside another, as long as all of the modules used belong to the same project (so the compiler knows where to find the module). The code for one module is not written inside another module’s body (Code for different modules are not nested).(批注:模块内部不能再定义/嵌套模块!!!)
Note: Connecting Signals to Module Ports(模块实例化)
module mod_a ( input in1, input in2, output out );
// Module body
endmodule
There are two commonly-used methods to connect a wire to a port: by position or by name.
- By position:
mod_a instance1 ( wa, wb, wc );
(批注:wa、wb、wc与模块声明中的参数一一对应) - By name:
mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );
(批注:不按顺序指定wa、wb、wc的连接。注意前面的句点)
----- 21. Module Pos -----
Problem Statement
You are given a module named mod_a that has 2 outputs and 4 inputs, in that order. You must connect the 6 ports by position to your top-level module’s ports out1, out2, a, b, c, and d, in that order.
You are given the following module:
module mod_a ( output, output, input, input, input, input );
Expected solution length: Around 1 line.
Answer
module mod_a ( output _out1, output _out2, input _a, input _b, input _c, input _d);
//...
endmodule
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a instance1(out1, out2, a, b, c, d);
endmodule
----- 22. Module Name -----
Problem Statement
You are given a module named mod_a that has 2 outputs and 4 inputs, in some order. You must connect the 6 ports by name to your top-level module’s ports:
You are given the following module:
module mod_a ( output out1, output out2, input in1, input in2, input in3, input in4);
Expected solution length: Around 1 line.
Answer
module mod_a ( output out1, output out2, input in1, input in2, input in3, input in4);
//...
endmodule
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a instance1(.out1(out1), .out2(out2), .in1(a), .in2(b), .in3(c), .in4(d));
endmodule
----- 23. Module Shift -----
Problem Statement
You are given a module my_dff
with two inputs and one output (that implements a D flip-flop). Instantiate three of them, then chain them together to make a shift register of length 3. The clk port needs to be connected to all instances.
The module provided to you is: module my_dff ( input clk, input d, output q );
Note that to make the internal connections, you will need to declare some wires. Be careful about naming your wires and module instances: the names must be unique.
Answer
module my_dff ( input clk, input d, output q );
//...
endmodule
module top_module ( input clk, input d, output q );
wire qd1, qd2;
my_dff instance1(.clk(clk), .d(d), .q(qd1));
my_dff instance2(.clk(clk), .d(qd1), .q(qd2));
my_dff instance3(.clk(clk), .d(qd2), .q(q));
endmodule
----- 24. Module Shift8 -----
Problem Statement
You are given a module my_dff8
with two inputs and one output (that implements a set of 8 D flip-flops). Instantiate three of them, then chain them together to make a 8-bit wide shift register of length 3. In addition, create a 4-to-1 multiplexer (not provided) that chooses what to output depending on sel[1:0]
: The value at the input d, after the first, after the second, or after the third D flip-flop. (Essentially, sel selects how many cycles to delay the input, from zero to three clock cycles.)
The module provided to you is: module my_dff8 ( input clk, input [7:0] d, output [7:0] q );
The multiplexer is not provided. One possible way to write one is inside an always block with a case statement inside. (See also: mux9to1v)
Answer
module my_dff8 ( input clk, input[7:0] d, output[7:0] q );
//...
endmodule
module mux41 (
input[7:0] a, b, c, d,
input[1:0] sel,
output reg[7:0] q
);
always @ (*)
begin
case(sel)
2'b00: q = a;
2'b01: q = b;
2'b10: q = c;
2'b11: q = d;
endcase
end
endmodule
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 instance1(.clk(clk), .d(d), .q(q1));
my_dff8 instance2(.clk(clk), .d(q1), .q(q2));
my_dff8 instance3(.clk(clk), .d(q2), .q(q3));
mux41 instance4(.a(d), .b(q1), .c(q2), .d(q3), .sel(sel), .q(q));
endmodule
----- 25. Module add -----
Problem Statement
You are given a module add16 that performs a 16-bit addition. Instantiate two of them to create a 32-bit adder. One add16 module computes the lower 16 bits of the addition result, while the second add16 module computes the upper 16 bits of the result, after receiving the carry-out from the first adder. Your 32-bit adder does not need to handle carry-in (assume 0) or carry-out (ignored), but the internal modules need to in order to function correctly. (In other words, the add16 module performs 16-bit a + b + cin, while your module performs 32-bit a + b).
Connect the modules together as shown in the diagram below. The provided module add16 has the following declaration:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
Answer
module add16(
input cin,
input [15:0] a,
input [15:0] b,
output [15:0] sum,
output cout
);
//...
endmodule
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire io;
add16 adder1(.cin(0), .a(a[15:0]), .b(b[15:0]), .sum(sum[15:0]), .cout(io));
add16 adder2(.cin(io), .a(a[31:16]), .b(b[31:16]), .sum(sum[31:16]), .cout());
endmodule
----- 26. Module fadd -----
Problem Statement
In this exercise, you will create a circuit with two levels of hierarchy. Your top_module
will instantiate two copies of add16 (provided), each of which will instantiate 16 copies of add1 (which you must write). Thus, you must write two modules: top_module
and add1.
Like module_add, you are given a module add16 that performs a 16-bit addition. You must instantiate two of them to create a 32-bit adder. One add16 module computes the lower 16 bits of the addition result, while the second add16 module computes the upper 16 bits of the result. Your 32-bit adder does not need to handle carry-in (assume 0) or carry-out (ignored).
Connect the add16 modules together as shown in the diagram below. The provided module add16 has the following declaration:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
Within each add16, 16 full adders (module add1, not provided) are instantiated to actually perform the addition. You must write the full adder module that has the following declaration:
module add1 ( input a, input b, input cin, output sum, output cout );
Recall that a full adder computes the sum and carry-out of a+b+cin.
Answer
module top_module (
input [31:0] a,
input [31:0] b,
output [31:0] sum
);//
wire io;
add16 adder16_1(.cin(0), .a(a[15:0]), .b(b[15:0]), .sum(sum[15:0]), .cout(io));
add16 adder16_2(.cin(io), .a(a[31:16]), .b(b[31:16]), .sum(sum[31:16]), .cout());
endmodule
module add16(
input cin,
input [15:0] a,
input [15:0] b,
output [15:0] sum,
output cout
);
//...
endmodule
module add1 ( input a, input b, input cin, output sum, output cout );
// Full adder module here
wire s, co1, co2;
assign s = a ^ b;
assign co1 = a & b;
assign sum = cin ^ s;
assign co2 = cin & s;
assign cout = co1 | co2;
endmodule
----- 27. Module cseladd -----
Problem Statement
One drawback of the ripple carry adder (行波进位加法器,RCA) is that the delay for an adder to compute the carry out (from the carry-in, in the worst case) is fairly slow, and the second-stage adder cannot begin computing its carry-out until the first-stage adder has finished. This makes the adder slow. (批注:行波进位的方式太慢,因为第二个加法器要等第一个加法器算出进位才能进行)
One improvement is a carry-select adder (进位选择加法器,CSA,可参考文献:Bedrij: Carry-Select Adder), shown below. The first-stage adder is the same as before, but we duplicate(补充) the second-stage adder, one assuming carry-in=0 and one assuming carry-in=1, then using a fast 2-to-1 multiplexer to select which result happened to be correct.
In this exercise, you are provided with the same module add16 as the previous exercise, which adds two 16-bit numbers with carry-in and produces a carry-out and 16-bit sum. You must instantiate three of these to build the carry-select adder, using your own 16-bit 2-to-1 multiplexer.
Connect the modules together as shown in the diagram below. The provided module add16 has the following declaration:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
Answer
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
//...
endmodule
module mux21 (
input [15:0] a, b,
input sel,
output reg[15:0] q
);
always @ (*)
begin
case(sel)
1'b0: q = a;
1'b1: q = b;
endcase
end
endmodule
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire [15:0] sum1, sum2, cout;
add16 add16_1(.a(a[15:0]), .b(b[15:0]), .cin(0), .cout(cout), .sum(sum[15:0]));
add16 add16_2(.a(a[31:16]), .b(b[31:16]), .cin(0), .cout(), .sum(sum1));
add16 add16_3(.a(a[31:16]), .b(b[31:16]), .cin(1), .cout(), .sum(sum2));
mux21 mux21_1(.a(sum1), .b(sum2), .sel(cout), .q(sum[31:16]));
endmodule
----- 28. Module addsub -----
Problem Statement
An adder-subtractor can be built from an adder by optionally negating one of the inputs, which is equivalent to inverting the input then adding 1. The net result is a circuit that can do two operations: (a + b + 0) and (a + ~b + 1)
. See Wikipedia if you want a more detailed explanation of how this circuit works. (批注:学过数电的同学不用查wiki了,减法运算的实现过程就是把减数b转化为补码,然后当加法进行运算)
Build the adder-subtractor below.
You are provided with a 16-bit adder module, which you need to instantiate twice:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
Use a 32-bit wide XOR gate to invert the b input whenever sub is 1. (This can also be viewed as b[31:0] XORed with sub replicated 32 times. See replication operator. (批注:因为sub信号只有1位,所以要使用并置操作将其扩展为32位)). Also connect the sub input to the carry-in of the adder.
Answer
module add16(
input cin,
input [15:0] a,
input [15:0] b,
output [15:0] sum,
output cout
);
//...
endmodule
module top_module(
input [31:0] a,
input [31:0] b,
input sub,
output [31:0] sum
);
wire [31:0] bio, subio;
wire cio;
assign subio = { {32{sub}} };
assign bio[15:0] = b[15:0] ^ subio[15:0];
assign bio[31:16] = b[31:16] ^ subio[31:16];
add16 add16_1(.cin(sub), .a(a[15:0]), .b(bio[15:0]), .sum(sum[15:0]), .cout(cio));
add16 add16_2(.cin(cio), .a(a[31:16]), .b(bio[31:16]), .sum(sum[31:16]), .cout());
endmodule