主要内容:
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
`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
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
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
`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
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
`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
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
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
`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
小BUG:当减法结果为负数时,输出到txt不能正常显示该负数。
解决:输出端口result设为signed类型即可