4bit ALU verilog 设计

4bit ALU verilog 设计

六月 23, 2025 次阅读

设计概要如下:

CPU中简单的4位ALU(算术逻辑单元)
输入信号:第一4位操作数op0[3:0]、第二4位操作数op1[3:0]、功能选择s[2:0]、输出使能信号oe、时钟信号clk
输出信号:4位运算结果r[3:0]、进位标志cf
设计要求:
1、根据功能选择信号s[2:0]选择算术或者逻辑运算功能,s[2:0]值为3’b000(即二进制000B)时为加法运算,3’b001时为减法运算,3’b100时为与运算,3’b101时为或运算,3’b110时为非运算(注意非运算只使用第一4位操作数),3’b111时为异或(XOR)运算。
2、运算结果必须先存储在4位结果寄存器(使用D触发器实现)中,clk上升沿时运算结果存入4位结果寄存器,此功能必须实现。
3、如果加法发生向更高位进位(运算结果超过4位),则需要将进位存储在一个1位的进位寄存器(使用D触发器实现)中,发生进位存储1,反之存储0;如果减法发生向更高位借位,则需要将借位也存储在与加法相同的进位寄存器中,发生借位存储1,反之存储0;同样是clk上升沿时进位或者借位存入1位进位寄存器。此功能必须实现。
4、oe低电平有效,oe高电平时,r[3:0]和cf均为高阻态输出(相当于输出无效);oe低电平时,r[3:0]等于4位结果寄存器(D触发器)的输出,cf等于1位进位寄存器(D触发器)的输出。

设计一个四位的ALU直接使用软件思维非常简单,直接疯狂的条件判断就完事了,但值得注意的一点是,我们需要将结果利用 D 触发器保存到寄存器中,然后将寄存器结果与
输出结果相连即可:

module alu(
    input wire [3:0] op0, // operator0
    input wire [3:0] op1, // operator1
    input wire [2:0] s,      // functional option
    input clk,              // clock
    input wire oe,          // result enable
    output reg [3:0] r,          // result output
    output reg cf          // carry
);

    // internal register
    reg [3:0] result_reg;    // 4-bit result register
    reg cf_reg;              // carry flag register
    
    wire [4:0] adder_result; // output(include carry)
    wire [3:0] logic_result; // logic calculated result
    wire [3:0] result_raw;   // final result
    wire        cf_raw;         // carry/borrow flag

    // arithmetic operation module
    assign adder_result = (s == 3'b000) ? (op0 + op1) : // add
                        (s == 3'b001) ? (op0 + (~op1 + 1)) : // sub
                        5'b0;
                      
    // carry/borrow flag calculate
    assign cf_raw = s == 3'b000 ? adder_result[4]:  // carry or borrow
                    s == 3'b001 ? (op0 < op1) :
                    0;
                
    // logic operation module
    assign logic_result = (s == 3'b100) ? (op0 & op1) : // and
                        (s == 3'b101) ? (op0 | op1) : // or
                        (s == 3'b110) ? (~op0):        // not
                        (s == 3'b111) ? (op0 ^ op1):  // xor
                        4'b0;

    // final result choose
    assign result_raw = (s[2] == 1'b0) ? adder_result[3:0] : //arithmetic operation
                        logic_result[3:0];                     // logic operation
                    
    // D trigger
    always @(posedge clk) begin
        result_reg <= result_raw;
        cf_reg <= cf_raw;
    end
    
    // output control
    always @(*) begin
        if(!oe) begin
            r <= result_reg;
            cf <= cf_reg;
        end else begin
            r <= 4'bzzzz;
            cf <= 1'bz;
        end
    end

endmodule

是的,C语言咋写你就咋写,当然设计的核心在硬件思维的设计,下面我将基于这个 ALU 的软件思维的初版代码,设计一个纯硬件思维的电路出来

一、实现所有的运算逻辑,包装成底层模块

1、加减法模块

还记得上一个实验吗,自动售货机,没错,直接把那边的四位加减法器ban过来即可:

module FourBitAdder(
    input [3:0] A, B,
    input SubEn,
    output [3:0] S,
    output CO
);
    wire [3:0] last_B = {4{SubEn}} ^ B;
    wire [3:0] P = last_B ^ A;  
    wire [3:0] G = A & last_B;
    wire [3:0] C;
    wire CI = SubEn;
    assign C[0] = CI;
    assign C[1] = G[0] | (P[0] & CI);
    assign C[2] = G[1] | (P[1] & G[0]) | (P[1] & P[0] & CI);
    assign C[3] = G[2] | (P[2] & G[1]) | (P[2] & P[1] & G[0]) | (P[2] & P[1] & P[0] & CI);
    assign CO   = SubEn ^ (G[3] | P[3] & C[3]);
    assign S = P ^ C;
endmodule

对的对的直接 ban 就可以了,要是不懂怎么实现的建议回去看一下上一篇文章自动售货机

2、与/或/非/异或模块

module FourBitAnder(
    input [3:0] A, B,
    output [3:0] S
);
    assign S = A & B;
endmodule

module FourBitOrer(
    input [3:0] A, B,
    output [3:0] S
);
    assign S = A | B;
endmodule

module FourBitNoer(
    input [3:0] A,
    output [3:0] S
);
    assign S = ~A;
endmodule

module FourBitXorer(
    input [3:0] A, B,
    output [3:0] S
);
    assign S = A ^ B;
endmodule

这个太简单了就不解释了,按理来说其实不需要给他们单独设置模块,但这样写更清晰一些

二、顶层模块复用所有子模块

房子造好了自己不会住我就帮不了了哈,首先我得说明一下这个 ALU 的全局逻辑,首先,这个 ALU 涉及6个运算逻辑,即加、减、与、或、非、异或,首先让
这些逻辑并行计算所有的结果,也就是所谓的全路径计算,流程图如下:

graph LR
    A[输入数A] --> B[并发数据传递]
    C[输入数B] --> B
    B --> E[加法器/减法器]
    B --> F[与运算]
    B --> O[或运算]
    B --> P[非运算]
    B --> Q[异或运算]
    E & F & O & P & Q --> H[结果选择器]
    H --> I[输出寄存器]
    I --> X[最终输出]

复用部分实现如下:

FourBitAdder add(
        .A(op0),
        .B(op1),
        .SubEn(s[0]),
        .S(add_res),
        .CO(cout)
    );
    FourBitAnder(
        .A(op0),
        .B(op1),
        .S(and_res)
    );
    FourBitOrer(
        .A(op0),
        .B(op1),
        .S(or_res)
    );
    FourBitNoer(
        .A(op0),
        .S(no_res)
    );
    FourBitXorer(
        .A(op0),
        .B(op1),
        .S(xor_res)
    );

注意:加法和减法使用的是同一个逻辑运算单元

三、结果选择器实现

这样一来,我们就只剩下结果选择没有实现了,这里使用 case 语法即可简单实现:

always @(*) begin
        case(s)
            3'b000, 3'b001: begin
                result_raw = add_res;
                cf_raw = cout;
            end
            3'b100: begin
                result_raw = and_res;
                cf_raw = 1'b0;
            end
            3'b101: begin
                result_raw = or_res;
                cf_raw = 1'b0;
            end
            3'b110: begin
                result_raw = no_res;
                cf_raw = 1'b0;
            end
            3'b111: begin
                result_raw = xor_res;
                cf_raw = 1'b0;
            end
            default: begin
                result_raw = 4'b0000;
                cf_raw = 1'b0;
            end
        endcase
    end

四、D触发器存储结果数据实现

always @(posedge clk) begin
        result_reg <= result_raw;
        cf_reg <= cf_raw;
    end

这个实现很简单,在每次上升沿将结果存储到寄存器中即可,利用D触发器作为数据缓冲,保证数据不会乱(消除毛刺)

五、最终输出

always @(*) begin
        if(~oe) begin
            r = result_reg;
            cf = cf_reg;
        end else begin
            r = 4'bzzzz;
            cf = 1'bz;
        end
    end

将寄存器中的值传到输出端,从而实现最终数据输出

最终代码实现如下:

module alu(
    input wire [3:0] op0, // operator0
    input wire [3:0] op1, // operator1
    input wire [2:0] s,      // functional option
    input clk,              // clock
    input wire oe,          // result enable
    output reg [3:0] r,          // result output
    output reg cf          // carry
);

    // internal register
    reg [3:0] result_reg;    // 4-bit result register
    wire [3:0] add_res, and_res, or_res, no_res, xor_res;
    wire cout;
    reg cf_reg;              // carry flag register
    wire adder_co;
    reg adder_op0, adder_op1;
    reg [3:0] result_raw;   // final result
    reg       cf_raw;         // carry/borrow flag
    FourBitAdder add(
        .A(op0),
        .B(op1),
        .SubEn(s[0]),
        .S(add_res),
        .CO(cout)
    );
    FourBitAnder(
        .A(op0),
        .B(op1),
        .S(and_res)
    );
    FourBitOrer(
        .A(op0),
        .B(op1),
        .S(or_res)
    );
    FourBitNoer(
        .A(op0),
        .S(no_res)
    );
    FourBitXorer(
        .A(op0),
        .B(op1),
        .S(xor_res)
    );
    always @(*) begin
        case(s)
            3'b000, 3'b001: begin
                result_raw = add_res;
                cf_raw = cout;
            end
            3'b100: begin
                result_raw = and_res;
                cf_raw = 1'b0;
            end
            3'b101: begin
                result_raw = or_res;
                cf_raw = 1'b0;
            end
            3'b110: begin
                result_raw = no_res;
                cf_raw = 1'b0;
            end
            3'b111: begin
                result_raw = xor_res;
                cf_raw = 1'b0;
            end
            default: begin
                result_raw = 4'b0000;
                cf_raw = 1'b0;
            end
        endcase
    end
                    
    // D trigger
    always @(posedge clk) begin
        result_reg <= result_raw;
        cf_reg <= cf_raw;
    end
    
    // output control
    always @(*) begin
        if(~oe) begin
            r = result_reg;
            cf = cf_reg;
        end else begin
            r = 4'bzzzz;
            cf = 1'bz;
        end
    end

endmodule

module FourBitAdder(
    input [3:0] A, B,
    input SubEn,
    output [3:0] S,
    output CO
);
    wire [3:0] last_B = {4{SubEn}} ^ B;
    wire [3:0] P = last_B ^ A;  
    wire [3:0] G = A & last_B;
    wire [3:0] C;
    wire CI = SubEn;
    assign C[0] = CI;
    assign C[1] = G[0] | (P[0] & CI);
    assign C[2] = G[1] | (P[1] & G[0]) | (P[1] & P[0] & CI);
    assign C[3] = G[2] | (P[2] & G[1]) | (P[2] & P[1] & G[0]) | (P[2] & P[1] & P[0] & CI);
    assign CO   = SubEn ^ (G[3] | P[3] & C[3]);
    assign S = P ^ C;
endmodule

module FourBitAnder(
    input [3:0] A, B,
    output [3:0] S
);
    assign S = A & B;
endmodule

module FourBitOrer(
    input [3:0] A, B,
    output [3:0] S
);
    assign S = A | B;
endmodule

module FourBitNoer(
    input [3:0] A,
    output [3:0] S
);
    assign S = ~A;
endmodule

module FourBitXorer(
    input [3:0] A, B,
    output [3:0] S
);
    assign S = A ^ B;
endmodule