主要內容:
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類型即可