Verilog实现加减乘除计算器
分类:
IT文章
•
2024-04-11 12:20:53
主要内容:
1. 按键按下后,进行加减乘除操作
2. Verilog往TXT文本文件中写入数据
3. 完成计算模块
4. 最终实现加减乘除计算器
1. 实现按键按下后,选择option,进行加减乘除操作,除法计算结果为商&余数
module jsq(
clk,
rst_n,
key,
option,
x,
y,
result,
quotient,
remainder
);
parameter N = 16; // 输入数的位数
input clk; // 输入时钟
input rst_n; // 低电平有效的复位(清零)
input key;
input [1:0]option;
input [N-1:0] x;
input [N-1:0] y;
output [2*N-1:0] result;
output [N-1:0] quotient; //输出计算的商
output [N-1:0] remainder; //输出计算的余数
reg [2*N-1:0] result_r;
reg [N-1:0] quotient_r,remainder_r;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
result_r <= 1'b0;
quotient_r <= 1'b0;
remainder_r <= 1'b0;
end
else
begin
if (key == 1'b0)
begin //按键按下
case(option)
2'b00: result_r = x + y;
2'b01: result_r <= x + (~y + 1'b1);
2'b10: result_r = x * y;
2'b11: //result_r = x / y;
begin
quotient_r = x / y;
remainder_r = x % y;
end
endcase
end
else
begin // 按键释放
result_r <= 1'b0;
quotient_r <= 1'b0;
remainder_r <= 1'b0;
end
end
end
assign result = result_r ;
assign quotient= quotient_r;
assign remainder = remainder_r;
endmodule
View Code
`timescale 1ns/1ps
`define clock_period 20
module jsq_tb;
reg clk;
reg rst_n;
reg key;
reg [1:0]option;
reg [15:0] x,y;
wire [31:0] result;
wire [15:0] quotient;
wire [15:0] remainder;
initial begin
clk = 1'b1;
rst_n = 1'b0;
key = 1'b1; // 复位时,按键释放
# 20 //复位20ns
rst_n = 1'b1;
# 20
key = 1'b0;
option = 2'b10;
# 100
key = 1'b1;
# 20
key = 1'b0;
option = 2'b11;
# 100
// key = 1'b1;
// # 20
$stop;
end
always #(`clock_period/2) clk = ~clk; //50M
jsq #(.N(16)) jsq_0(
.clk(clk),
.rst_n(rst_n),
.key(key),
.option(option),
.x(x),
.y(y),
.result(result),
.quotient(quotient),
.remainder(remainder)
);
initial begin
x = 0;
repeat(20)
#(`clock_period) x = {$random}%100; //通过位拼接操作{}产生0—59范围的随机数
end
initial begin
y = 0;
repeat(20)
#(`clock_period) y = {$random}%50;
end
/*integer i;
initial begin
x = 0;
y = 0;
for(i = 0; i < 20; i = i + 1)
begin
//利用$random系统函数产生随机数。因为是16位,因此产生的数据最大不能超过65535.所以要对65535取模。
x = {$random}%100;
y = {$random}%50;
end
end*/
endmodule
View Code

2.Verilog往TXT文本文件中写入数据
integer handle;//定义后面要用到的变量
//...
//...
handle = $fopen("data.txt");//打开文件
//...
//...
always #10 clk = ~clk;//定义时钟
always #20
begin
$fdisplay(handle,"%d",rand_num);//写数据
while(!rst_n) $fclose(handle);//关文件
end
View Code
3.实现计算模块(减法运算支持结果显示为负数)
module calc(a, b, clk, rst_n, opcode, result);
parameter N = 16;
input [N-1:0] a,b;
input clk;
input rst_n;
input [3:0] opcode;
output [2*N-1:0] result;
// output reg neg_flag;
reg [2*N-1:0] result_r;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
result_r <= 0;
// neg_flag <= 0;
end
else
begin
case(opcode)
10: begin result_r[2*N-1:0] <= a + b; end
11: begin
/*if(a>=b)
result_r[2*N-1:0] <= a - b;
else begin
result_r[2*N-1:0] <= b - a;
//result_r[2*N-1] <= 1;
end*/
result_r <= a + (~b + 1'b1);
end
12: begin result_r[2*N-1:0] <= a * b; end
13: begin result_r[2*N-1:0] <= a / b; end
default: result_r[2*N-1:0] <= 0;
endcase
end
end
assign result = result_r;
endmodule
View Code
`timescale 1ns/1ps
`define clock_period 20
module calc_tb;
reg [15:0] a,b;
reg clk;
reg rst_n;
reg [3:0] opcode;
wire[31:0] result;
initial begin
clk = 1'b1;
rst_n = 1'b0;
a = 0;
b = 0;
# 20 //复位20ns
rst_n = 1'b1;
# 20
opcode = 4'd10;
a = 50;
b = 44;
# 20
a = 550;
b = 440;
# 20
opcode = 4'd11;
a = 11;
b = 9;
# 20
a = 11;
b = 21;
# 20
opcode = 4'd12;
a = 56;
b = 10;
# 20
a = 555;
b = 10;
# 20
opcode = 4'd13;
a = 70;
b = 7;
# 20
a = 7;
b = 70;
# 20
a = 770;
b = 11;
# 20
$stop;
end
always #(`clock_period/2) clk = ~clk; //50M
calc #(.N(16)) calc_0(
.clk(clk),
.rst_n(rst_n),
.opcode(opcode),
.a(a),
.b(b),
.result(result)
);
/* initial begin
a = 0;
repeat(20)
#(`clock_period) a = {$random}%100; //通过位拼接操作{}产生0—59范围的随机数
end
initial begin
b = 0;
repeat(20)
#(`clock_period) b = {$random}%50;
end*/
endmodule
View Code

4. 实现加减乘除计算器(由输入控制模块和计算模块组成)
具体功能为:实现非负整数的加减乘除运算。计算器顶层模块的输入端口有:输入时钟;10个单bit输入端口,分别代表十进制数0到9;5个单bit输入端口,分别代表符号“+”、“-”、“×”、“÷”、“=”;1个单bit输入端口,代表计算器显示清零符号。代表计算器按键的单bit输入端口,如果出现1个时钟周期的高电平脉冲信号,表示这个按键按下。计算器可处理位宽至少为16位二进制数(即十进制数0到65535)的输入数据,并得到正确的运算结果,运算结果的位宽不限于16位二进制数。其中,减法运算支持运算结果为负数,激励中计算器的输入数据和运算结果存入文本文件中
module top(
clk,
rst_n,
input0,
input1,
input2,
input3,
input4,
input5,
input6,
input7,
input8,
input9,
add,
sub,
mul,
div,
enter,
num,
a,
b,
opcode,
result
);
input clk,rst_n;
input input0,input1,input2,input3,input4,input5,input6,input7,input8,input9;
input add,sub,mul,div,enter;
output [15:0] a;
output [15:0] b;
output [3:0] opcode;
output [31:0]result;
output [15:0] num;
key key_0(
.clk(clk),
.rst_n(rst_n),
.input0(input0),
.input1(input1),
.input2(input2),
.input3(input3),
.input4(input4),
.input5(input5),
.input6(input6),
.input7(input7),
.input8(input8),
.input9(input9),
.add(add),
.sub(sub),
.mul(mul),
.div(div),
.enter(enter),
.num(num),
.opcode(opcode),
.a(a),
.b(b)
);
calc #(.N(16)) calc_0(
.clk(clk),
.rst_n(rst_n),
.opcode(opcode),
.a(a),
.b(b),
.enter(enter),
.result(result)
);
endmodule
top
`timescale 1ns/1ps
`define clock_period 20
module top_tb;
reg clk;
reg rst_n;
reg input0,input1,input2,input3,input4,input5,input6,input7,input8,input9;
reg add,sub,mul,div,enter;
wire [15:0] num; //顺序影响波形信号的顺序
wire [15:0] a, b;
wire [3:0] opcode;
wire [31:0]result;
integer file;
initial begin
clk = 1'b1;
rst_n = 1'b0;
input0 = 1'b0;
input1 = 1'b0;
input2 = 1'b0;
input3 = 1'b0;
input4 = 1'b0;
input5 = 1'b0;
input6 = 1'b0;
input7 = 1'b0;
input8 = 1'b0;
input9 = 1'b0;
add = 1'b0;
sub = 1'b0;
mul = 1'b0;
div = 1'b0;
enter = 1'b0;
# 20 //复位20ns
rst_n = 1'b1;
# 20
input1 <= 1'b1;
# 20
input1 <= 1'b0;
# 20
input2 <= 1'b1;
# 20
input2 <= 1'b0;
# 20
mul <= 1'b1;
# 20
mul <= 1'b0;
# 20
input4 <= 1'b1;
# 20
input4 <= 1'b0;
# 20
input2 <= 1'b1;
# 20
input2 <= 1'b0;
# 20
enter <= 1'b1;
# 20
enter <= 1'b0;
# 20
rst_n = 1'b0;
#20
rst_n = 1'b1;
# 20
input4 <= 1'b1;
# 20
input4 <= 1'b0;
# 20
input3 <= 1'b1;
# 20
input3 <= 1'b0;
# 20
sub <= 1'b1;
# 20
sub <= 1'b0;
# 20
input6 <= 1'b1;
# 20
input6 <= 1'b0;
# 20
input5 <= 1'b1;
# 20
input5 <= 1'b0;
# 20
input5 <= 1'b1;
# 20
input5 <= 1'b0;
# 20
input3 <= 1'b1;
# 20
input3 <= 1'b0;
# 20
enter <= 1'b1;
# 20
enter <= 1'b0;
# 20
file = $fopen("calc.txt"); // 打开文件
begin
$fdisplay(file,"%d",a); // 写数据
$fdisplay(file,"%d",b);
$fdisplay(file,"%d",result);
while(!rst_n) $fclose(file); // 关闭文件
end
$stop;
end
always #(`clock_period/2) clk = ~clk; //50M
top top_0(
.clk(clk),
.rst_n(rst_n),
.input0(input0),
.input1(input1),
.input2(input2),
.input3(input3),
.input4(input4),
.input5(input5),
.input6(input6),
.input7(input7),
.input8(input8),
.input9(input9),
.add(add),
.sub(sub),
.mul(mul),
.div(div),
.enter(enter),
.num(num),
.a(a),
.b(b),
.opcode(opcode),
.result(result)
);
endmodule
top_tb
module calc(clk, rst_n, a, b, opcode, enter, result);
parameter N = 16;
input [N-1:0] a,b;
input clk,rst_n;
input enter;
input [3:0] opcode;
output [2*N-1:0] result;
// output reg neg_flag;
reg [2*N-1:0] result_r;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
result_r <= 0;
// neg_flag <= 0;
end
else
begin
if(enter == 1) begin
case(opcode)
4'b0001: begin result_r[2*N-1:0] <= a + b; end
4'b0010: begin
/*if(a>=b) begin
result_r[2*N-1:0] <= a - b;
end
else begin
result_r[2*N-1:0] <= b - a;
end*/
result_r <= a + (~b + 1'b1); //减法结果支持负数显示
end
4'b0100: begin result_r[2*N-1:0] <= a * b; end
4'b1000: begin result_r[2*N-1:0] <= a / b; end
default: result_r[2*N-1:0] <= 0;
endcase
end
end
end
assign result = result_r;
endmodule
calc
module key(
clk,
rst_n,
input0,
input1,
input2,
input3,
input4,
input5,
input6,
input7,
input8,
input9,
add,
sub,
mul,
div,
enter,
num,
a,
b,
opcode
);
input clk,rst_n;
input input0,input1,input2,input3,input4,input5,input6,input7,input8,input9;
input add,sub,mul,div,enter;
output [15:0] a,b;
output [3:0] opcode;
reg [15:0] a_r,b_r,a_temp;
reg [3:0] opcode_r;
reg [9:0] input_all;
//键值翻译
output reg [15:0] num;
always @ (posedge clk or negedge rst_n)
begin
input_all = {input9,input8,input7,input6,input5,input4,input3,input2,input1,input0};
case (input_all)
10'b0000000001: num = 0;
10'b0000000010: num = 1;
10'b0000000100: num = 2;
10'b0000001000: num = 3;
10'b0000010000: num = 4;
10'b0000100000: num = 5;
10'b0001000000: num = 6;
10'b0010000000: num = 7;
10'b0100000000: num = 8;
10'b1000000000: num = 9;
default:;
endcase
end
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
a_r <= 0;
a_temp <= 0;
b_r <= 0;
opcode_r <= 4'b0000;
end
else
begin
if(add)
begin
opcode_r <= 4'b0001;
end
if(sub)
begin
opcode_r <= 4'b0010;
end
if(mul)
begin
opcode_r <= 4'b0100;
end
if(div)
begin
opcode_r <= 4'b1000;
end
if(opcode_r == 4'b0000 && input_all != (10'b0000000000)) // 按下运算符前,存第一个数
a_r <= a_r*10 + num;
if(opcode_r != 4'b0000 && input_all != (10'b0000000000)) // 按下运算符后,存第二个数
b_r <= b_r*10 + num;
end
end
assign a = a_r;
assign b = b_r;
assign opcode = opcode_r;
endmodule
key
`timescale 1ns/1ps
`define clock_period 20
module calc_tb;
reg [15:0] a,b;
reg clk,rst_n;
reg enter;
reg [3:0] opcode;
wire[31:0] result;
initial begin
clk = 1'b1;
rst_n = 1'b0;
enter = 1'b0;
a = 0;
b = 0;
# 20 //复位20ns
rst_n = 1'b1;
# 20
opcode = 4'd10;
a = 50;
b = 44;
enter = 1'b1;
# 20
a = 550;
b = 440;
# 20
opcode = 4'd11;
a = 11;
b = 9;
# 20
a = 11;
b = 21;
# 20
opcode = 4'd12;
a = 56;
b = 10;
# 20
a = 555;
b = 10;
# 20
opcode = 4'd13;
a = 70;
b = 7;
# 20
a = 7;
b = 70;
# 20
a = 770;
b = 11;
# 20
$stop;
end
always #(`clock_period/2) clk = ~clk; //50M
calc #(.N(16)) calc_0(
.clk(clk),
.rst_n(rst_n),
.opcode(opcode),
.enter(enter),
.a(a),
.b(b),
.result(result)
);
/* initial begin
a = 0;
repeat(20)
#(`clock_period) a = {$random}%100; //通过位拼接操作{}产生0—59范围的随机数
end
initial begin
b = 0;
repeat(20)
#(`clock_period) b = {$random}%50;
end*/
endmodule
calc_tb


小BUG:当减法结果为负数时,输出到txt不能正常显示该负数。
解决:输出端口result设为signed类型即可