基于vivado使用verilog语言设计简单的32位ALU

在vivado上使用verilog语言设计32位ALU,包含16种不同的算数、逻辑、比较、移位运算。

32位ALU

  • 顶层设计
    • 输入:
      • n0 ,第一个数据输入,32比特
      • in1 ,第二个数据输入,32比特
      • op ,ALU计算类型码,11比特
    • 输出
      • out ,输出,32比特
      • overflow ,溢出标志,1比特
      • zero ,零标志,1比特
      • carryout ,进位标志,1比特

算数运算

运算 OP编码 具体含义
add 00000100000 有符号数加法
addu 00000100001 无符号数加法
sub 00000100010 有符号数减法
subu 00000100011 无符号数减法

逻辑运算

运算 OP编码 具体含义
and 00000100100 按位与
or 00000100101 按位或
xor 00000100110 异或
nor 00000100111 或非

比较运算

运算 OP编码 具体含义
slt 00000101010 slt $1,$2,$3 意为If($2<$3) $1=1 else $1=0;有符号数的比较
altu 00000101011 sltu $1,$2,$3 意为If($2<$3) $1=1 else $1=0;无符号数的比较

移位运算

运算 OP编码 具体含义
sll 00000000000 sll $1,$2,10 意为$1=$2<<10;左移10位
srl 00000000010 srl $1,$2,10 意为$1=$2>>10;右移10位
sra 00000000011 sra $1,$2,10 意为$1=$2>>10;注意符号位保留
sllv 00000000100 sllv $1,$2,$3 意为$1=$2<<$3
srlv 00000000110 srlv $1,$2,$3 意为$1=$2>>$3
srav 00000000111 srav $1,$2,$3 意为$1=$2>>$3;注意符号位的保留

设计代码

module alu_32(
 reset, in0, in1, op, out, overflow, zero, carryout
    );
    input reset;               //reset用于初始化置零
    input[31:0] in0,in1;       //操作对象
    input[10:0] op;            //操作码
    output[31:0] out;         
    output overflow,zero,carryout;
    reg[31:0] out;
    reg overflow,zero,carryout;
always@(*)              
begin
     if(reset)           //初始化
         begin
          out=0;
          overflow=0;
          zero=0;
          carryout=0;
         end
     else
         alutask( in0, in1, op, out, overflow, zero, carryout);      //把具体功能模块封装成一个任务
end
task alutask;                  //任务定义
   input[31:0] in0,in1;       
   input[10:0] op;           
   output[31:0] out;         
   output overflow,zero,carryout;
   reg[31:0] out;
   reg overflow,zero,carryout;
begin
            overflow=0;            //每次进行不同的运算的时候,标志位需要置0
            carryout=0;
            case( op )
            11'b00000100000: 
                            begin
                            out=$signed(in0)+$signed(in1);                               //$signed是强制转换,把无符号数转为有符号数
                            overflow=in0[31]&in1[31] ^ in0[30]&in1[30];         //有符号数的溢出是通过overflow,判断条件为--最高位进位状态^次高位进位状态=1,则溢出。具体可百度。
                            end
            11'b00000100001: 
                            begin               //增加了判断语句,如果操作对象不是无符号数,则结果输出为不确定的值,下同
                            if(in0[31]==1||in1[31]==1)
                            out=32'bx;
                            else
                            {carryout,out}=$unsigned(in0)+$unsigned(in1);                             //无符号数的溢出通过进借位判断,这里使用用连接符{},使得将carryout作为最高位与out连接在一起            
                            end
            11'b00000100010:
                             begin
                             out=$signed(in0)-$signed(in1);
                             overflow=in0[31]&in1[31] ^ in0[30]&in1[30];
                             end
            11'b00000100011: 
                             begin
                             if(in0[31]==1||in1[31]==1)
                             out=32'bx;
                             else
                             {carryout,out}=$unsigned(in0)-$unsigned(in1);
                             end
            11'b00000100100: out=in0&in1;
            11'b00000100101: out=in0|in1;
            11'b00000100110: out=in0^in1;
            11'b00000100111: out=~(in0|in1);
            11'b00000101010: out=( $signed(in0)<$signed(in1) )? 1:0;
            11'b00000101011: out=(in0<in1)? 1:0;
            11'b00000000000: out=in0<<10;
            11'b00000000010: out=in0>>10;
            11'b00000000011: out=in0>>>10;
            11'b00000000100: out=in0<<in1;
            11'b00000000110: out=in0>>in1;
            11'b00000000111: out=in0>>>in1;
            endcase
            zero=out==0;          //zero通过直接判断out是否为0
end
endtask
endmodule

仿真文件

module addtest;
reg reset;           
reg [31:0] in0,in1;      
reg[10:0] op;            
wire[31:0] out;         
wire overflow,zero,carryout;

alu_32 unit(            //模块实例化
    .reset(reset),
    .in0(in0),
    .in1(in1),
    .op(op),
    .out(out),
    .overflow(overflow),
    .zero(zero),
    .carryout(carryout)
);
initial
begin
    #10 reset=1;
    #10 reset=0;in0=32'd1; in1=32'd2;        //对操作对象都是正数进行仿真,相当于对无符号数验证,且作为有符号数参与运算是没有问题的
    for(op=11'b00000100000;op<11'b00000100111;op=op+1)
    #20;
    #20 op=11'b00000101010;
    #20 op=11'b00000101011;
    #20 op=11'b00000000000;
    #20 op=11'b00000000010;
    #20 op=11'b00000000011;
    #20 op=11'b00000000100;
    #20 op=11'b00000000110;
    #20 op=11'b00000000111;
    
    #10 reset=1;
    #10 reset=0;in0=-32'd1; in1=32'd2;            //对有负数的情况进行仿真,也就是有符号数的情况
    for(op=11'b00000100000;op<11'b00000100111;op=op+1)
    #20;
    #20 op=11'b00000101010;
    #20 op=11'b00000101011;
    #20 op=11'b00000000000;
    #20 op=11'b00000000010;
    #20 op=11'b00000000011;
    #20 op=11'b00000000100;
    #20 op=11'b00000000110;
    #20 op=11'b00000000111;
    
    #100 $finish;
end
initial
$monitor ($time,,,"reset=%b in0=%b in1=%b op=%b out=%b overflow=%b zero=%b carryout=%b ",
reset,in0,in1,op,out,overflow,zero,carryout);
endmodule

电路图

基于vivado使用verilog语言设计简单的32位ALU_第1张图片

仿真波形图

基于vivado使用verilog语言设计简单的32位ALU_第2张图片

附上monitor监视器的监视结果(monitor语句)


扩展:由于vivado自带的加法器非常简单,如果直接使用该加法器的话,会造成加法结果延迟时间较长,因此可以考虑使用自己写的32位超前进位加法器,但是在组织上会复杂一些。
关于有无符号数溢出问题可以参见:有无符号数溢出判断

另:欢迎大家对博客内容批评指正。

你可能感兴趣的:(verilog)