一、设计逻辑
ALU内部包含做加减操作、比较操作、移位操作和逻辑操作的逻辑,ALU的输入同时传输给这些逻辑,各逻辑同时运算,最后通过一个多路选择电路将所需的结果选择出来作为ALU的输出。
本次设计的ALU有如下12种运算:
加法 减法 有符号比较(小于置位)无符号比较(小于置位)按位与 按位或非 按位或 按位异或 逻辑左移 逻辑右移 算术右移 高位加载
二、 代码解构
`timescale 1ns / 1ps
//
// Company:
// Engineer: todaywillbeAC
//
// Create Date: 2021/10/12 11:42:31
// Design Name:
// Module Name: ALU
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ALU(
input [11:0] alu_control,
input [31:0] alu_src1,
input [31:0] alu_src2,
output [31:0] alu_result
);//此部分为声明外部变量,后面的为模块内部线路
//加减操作
wire op_add; // 加法操作
wire op_sub; // 减法操作
//比较运算
wire op_slt; // 有符号比较,小于置位
wire op_sltu; // 无符号比较,小于置位
//逻辑操作
wire op_and; // 按位与
wire op_nor; // 按位或非
wire op_or; // 按位或
wire op_xor; // 按位异或
//移位操作
wire op_sll; // 逻辑左移
wire op_srl; // 逻辑右移
wire op_sra; // 算术右移
wire op_lui; // 高位加载
assign op_add = alu_control[ 0];
assign op_sub = alu_control[ 1];
assign op_slt = alu_control[ 2];
assign op_sltu = alu_control[ 3];
assign op_and = alu_control[ 4];
assign op_nor = alu_control[ 5];
assign op_or = alu_control[ 6];
assign op_xor = alu_control[ 7];
assign op_sll = alu_control[ 8];
assign op_srl = alu_control[ 9];
assign op_sra = alu_control[10];
assign op_lui = alu_control[11];
//这里采用独热码的方式给alu_control的每个位都赋予意义,将内部变量op与外部变量alu_control实打实的联系起来
wire [31:0] add_sub_result;
wire [31:0] slt_result;
wire [31:0] sltu_result;
wire [31:0] and_result;
wire [31:0] nor_result;
wire [31:0] or_result;
wire [31:0] xor_result;
wire [31:0] sll_result;
wire [31:0] srl_result;
wire [31:0] sra_result;
wire [31:0] lui_result;
//这里依然是设立内部变量result,为接下来与外部变量alu_result、alu_src1、alu_src2联系起来做准备
assign and_result = alu_src1 & alu_src2;
assign or_result = alu_src1 | alu_src2;
assign nor_result = ~or_result;
assign xor_result = alu_src1 ^ alu_src2;
assign lui_result = {alu_src2[15:0], 16'b0};//这里是位拼接运算,lui将高位变成alu_src2的值,低位置零
//联系内部变量result与外部变量alu_src1、alu_src2
wire [31:0] adder_a;
wire [31:0] adder_b;
wire adder_cin;
wire [31:0] adder_result;
wire adder_cout;
assign adder_a = alu_src1;
assign adder_b = (op_sub | op_slt | op_sltu) ? ~alu_src2 : alu_src2;
assign adder_cin = (op_sub | op_slt | op_sltu) ? 1'b1 : 1'b0;
assign {adder_cout, adder_result} = adder_a + adder_b + adder_cin;
assign add_sub_result = adder_result;
assign slt_result[31:1] = 31'b0;
assign slt_result[0] = (alu_src1[31] & ~alu_src2[31])
| (~(alu_src1[31]^alu_src2[31]) & adder_result[31]);//有符号比较
assign sltu_result[31:1] = 31'b0;
assign sltu_result[0] = ~adder_cout;//无符号比较
//这部分关于数位的逻辑代码,有多种实现方式,现阶段首先学会如何组织代码,具体逻辑可以过后再做研究
assign sll_result = alu_src2 << alu_src1[4:0];//逻辑左移
assign srl_result = alu_src2 >> alu_src1[4:0];//逻辑右移
assign sra_result = ($signed(alu_src2)) >>> alu_src1[4:0];//算术右移
//******关于“逻辑右移”与“算术右移”的区别需要搞明白
assign alu_result = ({32{op_add|op_sub }} & add_sub_result)
| ({32{op_slt }} & slt_result)
| ({32{op_sltu }} & sltu_result)
| ({32{op_and }} & and_result)
| ({32{op_nor }} & nor_result)
| ({32{op_or }} & or_result)
| ({32{op_xor }} & xor_result)
| ({32{op_sll }} & sll_result)
| ({32{op_srl }} & srl_result)
| ({32{op_sra }} & sra_result)
| ({32{op_lui }} & lui_result);
//利用按位或运算达到数据选择的目的,非常巧妙
endmodule
三、testbeech仿真激励文件
下面附录一个最简单的仿真文件,仿真逻辑如下:
给出alu_src1、alu_src2的初值,然后对于alu_control依次从0-11赋值,观察不同操作结果与预期是否一致。
`timescale 1ns / 1ps
//
// Company:
// Engineer: todaywillbeAC
//
// Create Date: 2021/10/12 11:58:03
// Design Name:
// Module Name: ALU_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ALU_tb();
reg [11:0] alu_control;
reg [31:0] alu_src1;
reg [31:0] alu_src2;
wire [31:0] alu_result;
ALU ALU_inst(
.alu_control(alu_control),
.alu_src1(alu_src1),
.alu_src2(alu_src2),
.alu_result(alu_result)
);
initial
begin
alu_src1 = 8'h0010_0110; alu_src2 = 8'h0000_0110;
alu_control=12'b0000_0000_0000_0001;
#40 alu_control=12'b0000_0000_0000_0010;
#40 alu_control=12'b0000_0000_0000_0100;
#40 alu_control=12'b0000_0000_0000_1000;
#40 alu_control=12'b0000_0000_0001_0000;
#40 alu_control=12'b0000_0000_0010_0000;
#40 alu_control=12'b0000_0000_0100_0000;
#40 alu_control=12'b0000_0000_1000_0000;
#40 alu_control=12'b0000_0001_0000_0000;
#40 alu_control=12'b0000_0010_0000_0000;
#40 alu_control=12'b0000_0100_0000_0000;
#40 alu_control=12'b0000_1000_0000_0000;
#40 alu_control=12'b0001_0000_0000_0000;
#40 alu_control=12'b0010_0000_0000_0000;
#40 alu_control=12'b0100_0000_0000_0000;
#40 alu_control=12'b1000_0000_0000_0000;
end
endmodule