一生一芯 算术逻辑部件 ALU verilog浅析与实现

本文记录了一生一芯项目的数电实验,重点在于使用Verilog实现ALU,涉及补码运算、溢出判断等内容。作者详细介绍了补码的定义、加减法规则,并提供了Verilog代码实现。在调试过程中遇到加法结果不一致的问题,最终通过改进代码解决了溢出标志的获取。
摘要由CSDN通过智能技术生成

仙歌音,玉笛灵,酒盏玉露清
剑舞轻,潇洒过白袍影。----《谪仙》


一生一芯项目,预学习部分,数电实验学习记录,笔者电子寄术挂科,基础知识甚是不熟,故文中可能多有错漏,恳请指出!

0. 前言

笔者是新手,verilog很不熟练,写这个只是记录自己的学习过程避免遗忘,同时希望能够帮助后来者省去一些查资料的时间,所以代码肯定写的很烂,也多有错漏,恳请读者评论指出!

1. 项目要求

项目要求
功能选择只需要一个case语句就可以完成,后面的运算也都是基本运算,直接用内置的运算符就可以。但是尤其注意的是,这里需要设计的是补码运算功能。作为挂科学生,必须复习一下补码的运算法则了(感觉这玩意就算你没挂估计你也记不得了吧,毕竟平时用的不多)

2. 补码知识复习

这一部分多为网上资料,摘自这里

正数的补码就是其本身

负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

例如

[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补

3.补码的加减法

3.1 补码的加法

想要运用补码进行加运算:
设两数原码分别为A、B,则应:
1.将负数除了符号位之外的位取反后加1得到补码
2.直接按位相加,符号位直接溢出不管(超出字长部分直接舍弃)

例如

1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原

如果A、B已经是补码,直接相加就可以

3.2 补码的减法

设A、B为两数的补码,则:

A - B = A + ((~B) + 1)

即减去一个数等于加上这个数的(取反+1)。

4. 关于溢出判断的补充

我在查资料的时候也发现有人说,双符号位可以判断溢出,很有道理,摘在此处以为启发,原文点此

双符号位可以判断溢出情况
运算结束,符号位情况对应下列结果
00:结果为正数,无溢出
01:结果正溢出
10:结果负溢出
11:结果为负数,无溢出

5. 补码比较大小

定理:若符号位相同,则除去符号位之后的补码越大,原码也越大

所以直接先判断是否异号,异号就正大负小,同号直接比大小即可。

6. verilog程序实现

上面虽然复习了补码的内容,但是实际上这个ALU的输入就是补码,所以不需要你去写转换。

module top(
    input [2:0] command_input,
    input [3:0] a_input,
    input [3:0] b_input,
    input [0:0] clk,
    output [3:0] ans,
    output [0:0] overflow_flag
);

reg [3:0] temp_ans;
reg [0:0] temp_overflow_flag;
reg [2:0] command = command_input;
reg [3:0] a = a_input;   //如果你需要测试,command、a、b直接赋值就可以,例如
reg [3:0] b = b_input;	 //改成a = 4‘b0111;即可

    always @(posedge clk) begin
        case (command)
            3'b000:begin
            	 //这样写能够取出溢出的那一位,用以判断是否溢出,如果需要,可以输出
                 {temp_overflow_flag, temp_ans} = a + b;
            end
            3'b001:begin
                 b = ~b + 1;
                 {temp_overflow_flag, temp_ans} = a + b;
            end
            3'b010:begin
                 temp_ans = ~a;
            end
            3'b011:begin
                 temp_ans = a & b;
            end
            3'b100:begin
                 temp_ans = a | b;
            end
            3'b101:begin
                 temp_ans = a ^ b;
            end
            3'b110:begin
                if (a[3] == b[3]) begin		//判断符号位是否相等
                    if (a < b)  temp_ans = 1;	//符号位相等直接比大小
                        else  temp_ans = 0;
                end
                else begin					//符号位不相等直接比符号位
                    if ((a[3] == 0) && (b[3] == 1)) temp_ans = 0;
                    if ((a[3] == 1) && (b[3] == 0)) temp_ans = 1;
                end
            end
            3'b111:begin
                if (a == b)  temp_ans = 1;
                    else  temp_ans = 0;
            end
        endcase
    end

    assign ans = temp_ans;
    assign overflow_flag = temp_overflow_flag;		//溢出标志

endmodule

7. 调试中发现的问题

同样的程序,两次编译运行结果却不一样,实在是不知道为什么。不知道verilog的加法是如何自动舍弃的?有知道的请评论留言!

这是第一次运行,加数是0001和1111,答案显而易见应该是10000,因为溢出舍弃最高位,所以ans应该是0000,但是这里输出是0100,不是很懂。看这个样子是verilog舍弃了最低位?并且以为第5位是符号位?你看加完之后不是10000吗,他应该是自动补了符号位然后取了前四位?
1-1
第二次编译运行莫名奇妙的好了,没有改动任何东西。
1-2
截图证明两次都编译运行了,不是没有编译。
2-1
2-2
verilog加法自动舍弃的问题我还不是很懂,有知道的请留言一下
,谢谢。

和同学讨论之后,他建议我改成
3-1
来获得溢出的符号位。前者一位,后者四位,这样正好符合本位之前说的双符号位。所以程序中改成了这样,确实好用。

8. 后记

这个东西统共调了我一天,真的是憨,很不熟练。尤其是遇到了一个重大的问题,我会另发一篇博客来说,不过确实回顾、学到了一些知识。希望之后能加快进度。

命中注定不能靠近,爱你的事当作秘密。----《云与海》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值