题目:
实验一个多功能数字电路模块
具体要求如下:
1) 输入数据为两个一位十进制数A和B,A和B均为8421码表示,其中数据A由拨动开关SW7-SW4输入(SW7为MSB),数据B由SW3-SW0输入(SW3为MSB);
2) 电路的功能包括求和、比较大小、输出最大值和输出最小值四种;分别由如下图所示的按键开关控制,当按键按下时,电路执行相对应的功能并输出结果。
3) 求和功能描述:当按下求和键时,对A和B两个数相加,相加的结果显示在数码管上,注意相加的结果需要进行BCD码调整,显示模块必须调用实验一中设计的显示译码电路,当十位为0时,需要灭零。
4) 比较大小模块描述:当比较大小功能键按下时,比较A和B的大小,当A<B时显示01(不灭0),当A>B时显示10,A=B时显示11。
5) 输出最大(小)值功能描述:当按下输出最大(小)值功能键时,将A和B中的最大(小)值显示在数码管上。
(出题:北京理工大学 张延军老师)
1. 设计思路
在做本题之前,已经编写好了LED显示模块,这个模块比较简单,我就不多说了.
module decoder_7led(
input turn_off,//灭灯信号
input test,//测灯信号
input zero,//灭零信号
input [3:0] number,//输入的四位二进制数
output reg [7:0] display,//数码管片选
output reg led,//控制LD2的0号灯
output reg select=1//段选,恒为1,连接G6管脚。
);
//led变量
always @ *
begin
if (turn_off==1'b0)//仍按照优先级顺序判断
led=1'b0;
else if (test==1'b0)
led=1'b0;
else if ((zero==1'b0)&&(number==4'b0000))//满足灭零条件
led=1'b1;
else
led=1'b0;
end
//display变量
always @ *
begin
if (turn_off==1'b0)//灭灯有效
display=8'b00000000;
else if (test==1'b0)//测试有效
display=8'b11111111;
else if ((zero==1'b0)&&(number==4'b0000))//满足灭零条件
display=8'b00000000;
else
begin
case(number)//情况逐个讨论
4'b0000:display=8'b11111100;
4'b0001:display=8'b01100000;
4'b0010:display=8'b11011010;
4'b0011:display=8'b11110010;
4'b0100:display=8'b01100110;
4'b0101:display=8'b10110110;
4'b0110:display=8'b10111110;
4'b0111:display=8'b11100000;
4'b1000:display=8'b11111110;
4'b1001:display=8'b11100110;
4'b1010:display=8'b00011010;
4'b1011:display=8'b00110010;
4'b1100:display=8'b01000110;
4'b1101:display=8'b10010110;
4'b1110:display=8'b00011110;
4'b1111:display=8'b00000000;
endcase
end
end
endmodule // decoder_7led
首先将整个逻辑模块分为四大功能模块:求和、比较、求最大值和求最小值模块和数码管显示模块,以及起到控制作用的选择模块。本题的关键在于如何将这些模块正确地联系起来。首先分别讨论各自模块的实现:
①求和模块
若要对两个8421数进行求和,且求和结果仍为8421数,则非常关键的一步在于判断是否进位。如果产生了进位,那么要在加法结果之后再加6(0000_0110).
本模块的输入是两个4位8421数,为了方便地进行加法,首先拓展为8位二进制数。产生进位,即直接相加的结果大于9,否则就是没有产生进位,直接将结果输出。为此创建bcd_adder模块,输入为两个8421数和控制加法功能的按钮信号,输出为十位数结果和个位数结果,需要中间变量来储存加法结果,利用8位寄存器变量。还有需要注意的一点是当十位数结果为0时要清零。因为最终计算的结果要输入到数码管控制的电路里,且在实验一中当数码管模块的输入为4’b1111时数码管的8个LED全灭。因此与其控制数码管模块的灭零信号,不如直接让十位数对应的输出为4’1111更为简便。代码如下:
module bcd_adder(
input [3:0] numa1,//输入数字1
input [3:0] numa2,//输入数字2
input button_adder,//按钮
output reg [3:0] resulta1,//结果,十位数
output reg [3:0] resulta2//结果,个位数
);
reg [7:0] temp;
always @ *
begin
temp={4'b0000,numa1}+{4'b0000,numa2};//扩充位数
if(temp>8'b00001001)//产生进位
begin
{resulta1,resulta2}=temp+8'b00000110;//加6
end
else
begin
resulta1=4'b1111;//十位数为0时要清零
resulta2=temp[3:0];
end
end
endmodule // 加法模块
②比较大小模块
比较大小模块的功能比较简单,主体就是if语句,让对应的输入情形对应正确的输出即可。为此创建compare模块,输入为两个8421数和控制比较功能的按钮信号,输出为十位数结果和个位数结果。代码如下:
module compare(
input [3:0] numc1,
input [3:0] numc2,
input button_compare,
output reg [3:0] resultc1,
output reg [3:0] resultc2
);
//resultc1
always @ *
begin
if (numc1<numc2)
resultc1=4'b0000;
else if (numc1>numc2)
resultc1=4'b0001;
else
resultc1=4'b0001;
end
//resultc2
always @ *
begin
if (numc1<numc2)
resultc2=4'b0001;
else if (numc1>numc2)
resultc2=4'b0000;
else
resultc2=4'b0001;
end
endmodule // 比较模块
③输出最大/最小值模块
这两个模块在功能上是相似的。由于输入的是两个8421数,二者中的较大/较小者一定是个位数,因此显示十位数的数码管(或说一个数码管)就不需要显示了,如前所述,可以直接用4’b1111来灭灯。为此创建export_max和export_min模块,代码如下:
module export_max(
input [3:0] num_max1,
input [3:0] num_max2,
input button_max,
output reg [3:0] result_max1=4'b1111, //十位是0
output reg [3:0] result_max2
);
always @ *
begin
if(num_max1>num_max2)
result_max2=num_max1;
else
result_max2=num_max2;
end
endmodule // 输出最大值模块
module export_min(
input [3:0] num_min1,
input [3:0] num_min2,
input button_min,
output reg [3:0] result_min1=4'b1111,//十位是0
output reg [3:0] result_min2
);
always @ *
begin
if(num_min1<num_min2)
result_min2=num_min1;
else
result_min2=num_min2;
end
endmodule // 输出最小值模块
④选择模块
至此实现四个功能的模块已经设计完了,为了避免混乱,四个模块最好独立,不要管脚复用。这时候就需要一个选择器,来选择究竟将哪个模块的输出连接到数码管模块的输入。控制究竟发送哪个数据的信号当然是四个按钮,此外,应将四个模块的输出作为这个模块的输入。为此创建mux模块,输入为四个按钮信号和四个模块的对应输出,输出为控制两个数码管的信号,即显示的第一位数和第二位数对应的信号。代码如下:
module mux(
//四个模块的八个输出
input [3:0] add_output_1,
input [3:0] add_output_2,
input [3:0] cmp_output_1,
input [3:0] cmp_output_2,
input [3:0] max_output_1,
input [3:0] max_output_2,
input [3:0] min_output_1,
input [3:0] min_output_2,
//四个按钮
input button_a,
input button_c,
input button_x,
input button_n,
//输出:第一位数和第二位数
output reg [3:0] result_1,
output reg [3:0] result_2
);
always @ *
begin
if (button_a==1)//如果是加法功能
{result_1,result_2}={add_output_1,add_output_2};
else if (button_c==1)//如果是比较功能
{result_1,result_2}={cmp_output_1,cmp_output_2};
else if (button_x==1)//如果是求最大值功能
{result_1,result_2}={max_output_1,max_output_2};
else if(button_n==1)//如果是求最小值功能
{result_1,result_2}={min_output_1,min_output_2};
else //如果都不是(没有按钮被摁下),就输出两个0.
{result_1,result_2}={4'b0000,4'b0000};
end
endmodule // 选择器 对输出信号进行选择送到LED模块
⑤顶层模块
顶层模块的设计是本题的关键。整个电路的架构应当是:有六个输入,分别是两个8421数和4个按钮。有两个数码管,因此有六个输出,每个数码管对应三个输出:片选、位选和LED灯。这里直接利用实验一的数码管模块。六个输入是并行的,同时输入到四个功能模块中。四个模块和选择器的连接关系前面已述,选择器的两个输出直接送给对应的两个数码管模块。数码管的三个信号:灭灯、灭零和测试信号由于用不到,可以直接赋值为无效。代码如下:
//顶层模块
module multiply_top(
input [3:0] num1,
input [3:0] num2,
input B_add,
input B_cmp,
input B_max,
input B_min,
output [7:0] display1,
output [7:0] display2,
output select1,
output select2,
output led1,
output led2
);
//中间连线
wire [3:0] led_number_a_1;
wire [3:0] led_number_a_2;
wire [3:0] led_number_c_1;
wire [3:0] led_number_c_2;
wire [3:0] led_number_max_1;
wire [3:0] led_number_max_2;
wire [3:0] led_number_min_1;
wire [3:0] led_number_min_2;
wire [3:0] to_led_1;
wire [3:0] to_led_2;
//将四个功能模块实例化
bcd_adder BCD_ADDER
(.numa1(num1),.numa2(num2),.button_adder(B_add),.resulta1(led_number_a_1),.resulta2(led_number_a_2));
compare CMP
(.numc1(num1),.numc2(num2),.button_compare(B_cmp),.resultc1(led_number_c_1),.resultc2(led_number_c_2));
export_max EPMAX
(.num_max1(num1),.num_max2(num2),.button_max(B_max),.result_max1(led_number_max_1),.result_max2(led_number_max_2));
export_min EPMIN
(.num_min1(num1),.num_min2(num2),.button_min(B_min),.result_min1(led_number_min_1),.result_min2(led_number_min_2));
//将选择器实例化
mux MUX1 (
.add_output_1(led_number_a_1),.add_output_2(led_number_a_2),//加法输出
.cmp_output_1(led_number_c_1),.cmp_output_2(led_number_c_2),//比较输出
.max_output_1(led_number_max_1),.max_output_2(led_number_max_2),//最大值输出
.min_output_1(led_number_min_1),.min_output_2(led_number_min_2),//最小值输出
.button_a(B_add),.button_c(B_cmp),.button_x(B_max),.button_n(B_min),//四个按钮
.result_1(to_led_1),.result_2(to_led_2));//输出,传给LED模块
//LED模块实例化
decoder_7led LED1
(.turn_off(1'b1),.test(1'b1),.zero(1'b1),.number(to_led_1),.display(display1),.led(led1),.select(select1));
decoder_7led LED2
(.turn_off(1'b1),.test(1'b1),.zero(1'b1),.number(to_led_2),.display(display2),.led(led2),.select(select2));
endmodule
2. 仿真与测试
编写testbench模块,分别测试四个功能。代码如下:
module testbench;
reg [3:0] NUM1;
reg [3:0] NUM2;
reg B_ADD;
reg B_CMP;
reg B_MAX;
reg B_MIN;
wire [7:0] DISPLAY1;
wire [7:0] DISPLAY2;
wire SELECT1;
wire SELECT2;
wire LED1;
wire LED2;
multiply_top TEST (.num1(NUM1),.num2(NUM2),
.B_add(B_ADD),.B_cmp(B_CMP),
.B_max(B_MAX),.B_min(B_MIN),
.display1(DISPLAY1),.display2(DISPLAY2),
.select1(SELECT1),.select2(SELECT2),
.led1(LED1),.led2(LED2));
initial
begin
NUM1=4'b0000;
NUM2=4'b0000;
B_ADD=0;
B_CMP=0;
B_MAX=0;
B_MIN=0;
end
always
begin
#20 NUM1=4'b0100;NUM2=4'b0001;
#20 B_ADD=1;
#10 B_ADD=0;
#20 B_CMP=1;
#10 B_CMP=0;
#20 B_MAX=1;
#10 B_MAX=0;
#20 B_MIN=1;
#20 B_MIN=0;
#20 NUM1=4'b1000;NUM2=4'b1001;
#20 B_ADD=1;
#10 B_ADD=0;
#20 B_CMP=1;
#10 B_CMP=0;
#20 B_MAX=1;
#10 B_MAX=0;
#20 B_MIN=1;
#20 B_MIN=0;
end
initial
#300 $finish;
endmodule // testbench
仿真结果:
经过验证,输出正确,满足设计要求。