Verilog实现加减乘除计算器

主要内容:

  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类型即可

原文地址:https://www.cnblogs.com/HuangYJ/p/13060790.html