溢出处理的整数乘累加器 Verilog 实现

大致题意

设计一个乘累加器(MAC: Multiply Accumulator)

s u m = ∑ i a i b i sum=\sum_{i}a_{i} b_{i} sum=iaibi

模块要去实现上述公式的乘累加操作;数据输入接口中 din_a, din_b 为位宽为 5 的有符号整数;数据输出接口中 dout 为位宽为 12 的有符号整数;当累加器内部检测到有溢出时,overflow信号赋 1 以向外报告。

模块接口定义

首先声明模块接口信号的输入、输出类型以及位宽。数据输入接口 din_a, din_b ,以及数据输出接口 dout 设置为 signed 类型。这里在模块接口定义处,声明输出信号 dout_valid 为寄存器类型,也可以在模块接口定义外声明。

module mac ( 
    input           reset       ,
    input           clk         ,
    
    input           enable      ,
    
    input  signed [4:0]   din_a         ,
    input  signed [4:0]   din_b         ,
    input           din_valid   ,
    
    output signed [11:0]   dout       ,
    output reg      dout_valid ,
    
    output          overflow
    );
        
//模块处理代码

endmodule
实现乘累加功能

为了处理 溢出 问题,sum 扩展了 1 bit 位宽。仅当输入信号有效且出现使能信号时,乘累加器正常工作,即累加 din_a, din_b 的乘积,并将输出有效信号赋 1。

Verilog 支持有符号整数的乘法(此时要保证足够的位宽,保证符号位不受影响,且乘号两侧都是有符号数),乘累加的实现直接使用乘法运算符即可。

要避免同一个变量在不同的 always 块里赋值,不同逻辑的代码要分代码块写。虽然按 C 风格写仿真容易上手,但在板子上很有可能会综合出谜之逻辑。

    reg signed [12:0]   sum;

    always @(posedge clk)
        if (reset)
            begin
                sum         <= 0;
                dout_valid  <= 1'b0;
            end
        else
            if (din_valid & enable)
                begin
                    sum  <= sum + din_a * din_b;
                    dout_valid <= 1'b1;
                end
            else
                dout_valid <= 1'b0;
结果输出及溢出信号的处理

对于乘累加结果,按要求输出 12 位的有符号数,即 sum 的低 12 位。

对于溢出信号 overflow 的处理,可以简单地通过将存放乘累加值的寄存器宽度拓展一个 bit 来实现。因为有符号数是以 补码 形式存储,判断加法溢出可以简单地通过符号位进位 异或 数值最高位进位来判断,若结果为 1,出现溢出;反之,没有溢出。

分正数、负数情况考虑,很容易发现其数值最高位进位即是符号位的值,符号位进位则是符号位左一位的值,于是将存放乘累加值的寄存器宽度拓展一个 bit ,以简单实现加法溢出的判断。

    assign dout     = sum[11:0];
    assign overflow = sum[11] ^ sum[12]; 

需要先判断溢出再进行赋值操作的话,可以用中间结果的位运算实现。使用阻塞赋值可以写得更简洁一些。

	if ((1 & ((din_a * din_b + sum) >> 11)) ^ (1 & ((din_a * din_b + sum) >> 12)))
		begin
			//数据溢出需要进行的操作
		end

你可能感兴趣的:(FPGA)