只调用加法模块的仿真图:


(注:alu_control 为十六进制
001高位加载 src2的低16位加载到高16位上
002算数右移 src1算数右移 (高位补1)
004逻辑右移 src1逻辑右移 (逻辑右移直接补0)
008逻辑左移 src1逻辑左移
010异或 020或 040或非 080与
100 无符号比较 小于置位
200 有符号比较 小于置位
400 减法
800 加法
alu.v
- `timescale 1ns / 1ps
-
- module alu(
- input [11:0] alu_control,
- input [31:0] alu_src1,
- input [31:0] alu_src2,
- output [31:0] alu_result
- );
- reg [31:0] alu_result;
- wire alu_add; //加法
- wire alu_sub; //减法
- wire alu_slt; //有符号比较,小于置位
- wire alu_sltu; //无符号比较,小于置位
- wire alu_and; //按位与
- wire alu_nor; //按位或非
- wire alu_or; //按位或
- wire alu_xor; //按位异或
- wire alu_sll; //逻辑左移
- wire alu_srl; //逻辑右移
- wire alu_sra; //算数右移
- wire alu_lui; //高位加载
-
- assign alu_mult = alu_control[11];
- assign alu_add = alu_control[11];
- assign alu_sub = alu_control[10];
- assign alu_slt = alu_control[ 9];
- assign alu_sltu = alu_control[ 8];
- assign alu_and = alu_control[ 7];
- assign alu_nor = alu_control[ 6];
- assign alu_or = alu_control[ 5];
- assign alu_xor = alu_control[ 4];
- assign alu_sll = alu_control[ 3];
- assign alu_srl = alu_control[ 2];
- assign alu_sra = alu_control[ 1];
- assign alu_lui = alu_control[ 0];
-
- 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;
-
- wire signed [31:0] temp_src1; //带符号数的临时变量
- assign temp_src1 = alu_src1; //方便后面对alu_src1进行算数右移
- 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'd0}; //高位加载,第二个操作数的低十六位加载到高十六位上
- assign sll_result = alu_src1 << alu_src2; //逻辑左移
- assign srl_result = alu_src1 >> alu_src2; //逻辑右移
- assign slt_result = adder_result[31] ? 1'b1 : 1'b0; // 带符号数小于置位
- assign sltu_result = adder_cout ? 1'b0 : 1'b1; //无符号数小于置位
- assign sra_result = temp_src1 >>> alu_src2; //算数右移
- wire [31:0] adder_operand1;
- wire [31:0] adder_operand2;
- wire adder_cin ;
- wire [31:0] adder_result ;
- wire adder_cout ;
- assign adder_operand1 = alu_src1;
- assign adder_operand2 = alu_add ? alu_src2 : ~alu_src2; //默认进行减法,为slt和sltu服务
- assign adder_cin = ~alu_add; //巧妙到我都以为代码有bug
- adder adder_module( //调用加法模块
- .A(adder_operand1),
- .B(adder_operand2),
- .Cin (adder_cin ),
- .S (adder_result ),
- .Cout (adder_cout )
- );
- assign add_sub_result = adder_result;
- always@(*)
- begin
- if(alu_add | alu_sub)
- alu_result <= add_sub_result;
- else if(alu_slt)
- alu_result <= slt_result;
- else if(alu_sltu)
- alu_result <= sltu_result;
- else if(alu_and)
- alu_result <= and_result;
- else if(alu_nor)
- alu_result <= nor_result;
- else if(alu_or)
- alu_result <= or_result;
- else if(alu_xor)
- alu_result <= xor_result;
- else if(alu_sll)
- alu_result <= sll_result;
- else if(alu_srl)
- alu_result <= srl_result;
- else if(alu_sra)
- alu_result <= sra_result;
- else if(alu_lui)
- alu_result <= lui_result;
-
- end
- endmodule
- `timescale 1ns / 1ps
-
- module tb;
-
- // Inputs
- reg clk;
- reg [11:0] alu_control;
- reg [31:0] alu_src1;
- reg [31:0] alu_src2;
-
- // Outputs
- wire [31:0] alu_result;
-
- // Instantiate the Unit Under Test (UUT)
- //alu al(
- // input [11:0] alu_control,
- // input [31:0] alu_src1,
- // input [31:0] alu_src2,
- // output [31:0] alu_result
- // );
- alu alu_module(
- .alu_control(alu_control),
- .alu_src1 (alu_src1 ),
- .alu_src2 (alu_src2 ),
- .alu_result (alu_result )
- );
- initial begin
- // Initialize Inputs
- clk = 0;
- alu_control = 0;
- alu_src1 = 32'H10001111;
- alu_src2 = 32'H00000004;
-
- // Wait 100 ns for global reset to finish
- #100;
- alu_control = 12'b0000_0000_0001;
- #400;
- alu_control = 12'b0000_0000_0010;
- #500;
- alu_control = 12'b0000_0000_0100;
- #400;
- alu_control = 12'b0000_0000_1000;
- #500;
- alu_control = 12'b0000_0001_0000;
- #400;
- alu_control = 12'b0000_0010_0000;
- #500;
- alu_control = 12'b0000_0100_0000;
- #400;
- alu_control = 12'b0000_1000_0000;
- #400;
- alu_control = 12'b0001_0000_0000;
- #400;
- alu_control = 12'b0010_0000_0000;
- #400;
- alu_control = 12'b0100_0000_0000;
- #400;
- alu_control = 12'b1000_0000_0000;
- // Add stimulus here
- end
- always #5 clk = ~clk;
- endmodule
-
alu_display.v
- module alu_display(
- //时钟与复位信号
- input clk,
- input resetn, //后缀"n"代表低电平有效
-
- //拨码开关,用于选择输入数
- input [1:0] input_sel, //00:输入为控制信号(alu_control)
- //10:输入为源操作数1(alu_src1)
- //11:输入为源操作数2(alu_src2)
-
- //触摸屏相关接口,不需要更改
- output lcd_rst,
- output lcd_cs,
- output lcd_rs,
- output lcd_wr,
- output lcd_rd,
- inout[15:0] lcd_data_io,
- output lcd_bl_ctr,
- inout ct_int,
- inout ct_sda,
- output ct_scl,
- output ct_rstn
- );
- //-----{调用ALU模块}begin
- reg [11:0] alu_control; // ALU控制信号
- reg [31:0] alu_src1; // ALU操作数1
- reg [31:0] alu_src2; // ALU操作数2
- wire [31:0] alu_result; // ALU结果
- alu alu_module(
- .alu_control(alu_control),
- .alu_src1 (alu_src1 ),
- .alu_src2 (alu_src2 ),
- .alu_result (alu_result )
- );
- //-----{调用ALU模块}end
-
- //---------------------{调用触摸屏模块}begin--------------------//
- //-----{实例化触摸屏}begin
- //此小节不需要更改
- reg display_valid;
- reg [39:0] display_name;
- reg [31:0] display_value;
- wire [5 :0] display_number;
- wire input_valid;
- wire [31:0] input_value;
-
- lcd_module lcd_module(
- .clk (clk ), //10Mhz
- .resetn (resetn ),
-
- //调用触摸屏的接口
- .display_valid (display_valid ),
- .display_name (display_name ),
- .display_value (display_value ),
- .display_number (display_number),
- .input_valid (input_valid ),
- .input_value (input_value ),
-
- //lcd触摸屏相关接口,不需要更改
- .lcd_rst (lcd_rst ),
- .lcd_cs (lcd_cs ),
- .lcd_rs (lcd_rs ),
- .lcd_wr (lcd_wr ),
- .lcd_rd (lcd_rd ),
- .lcd_data_io (lcd_data_io ),
- .lcd_bl_ctr (lcd_bl_ctr ),
- .ct_int (ct_int ),
- .ct_sda (ct_sda ),
- .ct_scl (ct_scl ),
- .ct_rstn (ct_rstn )
- );
- //-----{实例化触摸屏}end
-
- //-----{从触摸屏获取输入}begin
- //根据实际需要输入的数修改此小节,
- //建议对每一个数的输入,编写单独一个always块
- //当input_sel为00时,表示输入数控制信号,即alu_control
- always @(posedge clk)
- begin
- if (!resetn)
- begin
- alu_control <= 12'd0;
- end
- else if (input_valid && input_sel==2'b00)
- begin
- alu_control <= input_value[11:0];
- end
- end
-
- //当input_sel为10时,表示输入数为源操作数1,即alu_src1
- always @(posedge clk)
- begin
- if (!resetn)
- begin
- alu_src1 <= 32'd0;
- end
- else if (input_valid && input_sel==2'b10)
- begin
- alu_src1 <= input_value;
- end
- end
-
- //当input_sel为11时,表示输入数为源操作数2,即alu_src2
- always @(posedge clk)
- begin
- if (!resetn)
- begin
- alu_src2 <= 32'd0;
- end
- else if (input_valid && input_sel==2'b11)
- begin
- alu_src2 <= input_value;
- end
- end
- //-----{从触摸屏获取输入}end
-
- //-----{输出到触摸屏显示}begin
- //根据需要显示的数修改此小节,
- //触摸屏上共有44块显示区域,可显示44组32位数据
- //44块显示区域从1开始编号,编号为1~44,
- always @(posedge clk)
- begin
- case(display_number)
- 6'd1 :
- begin
- display_valid <= 1'b1;
- display_name <= "SRC_1";
- display_value <= alu_src1;
- end
- 6'd2 :
- begin
- display_valid <= 1'b1;
- display_name <= "SRC_2";
- display_value <= alu_src2;
- end
- 6'd3 :
- begin
- display_valid <= 1'b1;
- display_name <= "CONTR";
- display_value <={20'd0, alu_control};
- end
- 6'd4 :
- begin
- display_valid <= 1'b1;
- display_name <= "RESUL";
- display_value <= alu_result;
- end
- default :
- begin
- display_valid <= 1'b0;
- display_name <= 40'd0;
- display_value <= 32'd0;
- end
- endcase
- end
- //-----{输出到触摸屏显示}end
- //----------------------{调用触摸屏模块}end---------------------//
- endmodule
这里结果我不知道怎么只显示希望的结果(出现了0,这个是个过程值,当时做乘法器的时候,看仿真的时候是配合着mult_end信号看的,我也不会把这个end信号调出来)

slt sltu没看明白:(手册里有以后看 挖坑)

小于置位指令(SLT,SLTI,SLTU,SLTIU)比较两个操作数然后设置目的寄存器,
如果小于就设置为1,否则就将目的寄存器设置为0。

alu.v
- `timescale 1ns / 1ps
-
- module alu(
- input clk,
- input [12:0] alu_control,
- input [31:0] alu_src1,
- input [31:0] alu_src2,
- output [31:0] alu_result
- );
- reg [31:0] alu_result;
- wire alu_mul;//乘法
- wire alu_add; //加法
- wire alu_sub; //减法
- wire alu_slt; //有符号比较,小于置位
- wire alu_sltu; //无符号比较,小于置位
- wire alu_and; //按位与
- wire alu_nor; //按位或非
- wire alu_or; //按位或
- wire alu_xor; //按位异或
- wire alu_sll; //逻辑左移
- wire alu_srl; //逻辑右移
- wire alu_sra; //算数右移
- wire alu_lui; //高位加载
-
-
- assign alu_mul = alu_control[12];
- assign alu_add = alu_control[11];
- assign alu_sub = alu_control[10];
- assign alu_slt = alu_control[ 9];
- assign alu_sltu = alu_control[ 8];
- assign alu_and = alu_control[ 7];
- assign alu_nor = alu_control[ 6];
- assign alu_or = alu_control[ 5];
- assign alu_xor = alu_control[ 4];
- assign alu_sll = alu_control[ 3];
- assign alu_srl = alu_control[ 2];
- assign alu_sra = alu_control[ 1];
- assign alu_lui = alu_control[ 0];
-
- wire [31:0] mul_result;
- 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;
-
- wire signed [31:0] temp_src1; //带符号数的临时变量
- assign temp_src1 = alu_src1; //方便后面对alu_src1进行算数右移
- 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'd0}; //高位加载,第二个操作数的低十六位加载到高十六位上
- assign sll_result = alu_src1 << alu_src2; //逻辑左移
- assign srl_result = alu_src1 >> alu_src2; //逻辑右移
- assign slt_result = adder_result[31] ? 1'b1 : 1'b0; // 带符号数小于置位
- assign sltu_result = adder_cout ? 1'b0 : 1'b1; //无符号数小于置位
- assign sra_result = temp_src1 >>> alu_src2; //算数右移
- wire [31:0] adder_operand1;
- wire [31:0] adder_operand2;
- wire adder_cin ;
- wire [31:0] adder_result ;
- wire adder_cout ;
- assign adder_operand1 = alu_src1;
- assign adder_operand2 = alu_add ? alu_src2 : ~alu_src2; //默认进行减法,为slt和sltu服务
- assign adder_cin = ~alu_add; //巧妙到我都以为代码有bug
- adder adder_module( //调用加法模块
- .A(adder_operand1),
- .B(adder_operand2),
- .Cin (adder_cin ),
- .S (adder_result ),
- .Cout (adder_cout )
- );
- assign add_sub_result = adder_result;
- wire clk; // 时钟
- // wire mult_begin; // 乘法开始信号
- wire [31:0] mult_op1; // 乘法源操作数1
- wire [31:0] mult_op2; // 乘法源操作数2
- wire [63:0] product; // 乘积
- wire mult_end ; // 乘法结束信号
-
- assign mult_op1 = alu_src1;
- assign mult_op2 =alu_src2;
- assign mult_begin=1;
-
- multiply multiply_module//调用乘法模块
- (
- .clk(clk),
- .mult_begin(mult_begin),
- .mult_op1 (alu_src1 ),
- .mult_op2 (alu_src2 ),
- .product ( product ),
- .mult_end ()
- );
- assign mul_result =product ;
-
- always@(*)
- begin
- if(alu_add | alu_sub)
- alu_result <= add_sub_result;
- else if(alu_mul)
- alu_result =mul_result;
- else if(alu_slt)
- alu_result <= slt_result;
- else if(alu_sltu)
- alu_result <= sltu_result;
- else if(alu_and)
- alu_result <= and_result;
- else if(alu_nor)
- alu_result <= nor_result;
- else if(alu_or)
- alu_result <= or_result;
- else if(alu_xor)
- alu_result <= xor_result;
- else if(alu_sll)
- alu_result <= sll_result;
- else if(alu_srl)
- alu_result <= srl_result;
- else if(alu_sra)
- alu_result <= sra_result;
- else if(alu_lui)
- alu_result <= lui_result;
-
- end
- endmodule
alu.display (就改了个信号数组的范围)
- module alu_display(
- //时钟与复位信号
- input clk,
- input resetn, //后缀"n"代表低电平有效
-
- //拨码开关,用于选择输入数
- input [1:0] input_sel, //00:输入为控制信号(alu_control)
- //10:输入为源操作数1(alu_src1)
- //11:输入为源操作数2(alu_src2)
-
- //触摸屏相关接口,不需要更改
- output lcd_rst,
- output lcd_cs,
- output lcd_rs,
- output lcd_wr,
- output lcd_rd,
- inout[15:0] lcd_data_io,
- output lcd_bl_ctr,
- inout ct_int,
- inout ct_sda,
- output ct_scl,
- output ct_rstn
- );
- //-----{调用ALU模块}begin
- reg [12:0] alu_control; // ALU控制信号
- reg [31:0] alu_src1; // ALU操作数1
- reg [31:0] alu_src2; // ALU操作数2
- wire [31:0] alu_result; // ALU结果
- alu alu_module(
- .alu_control(alu_control),
- .alu_src1 (alu_src1 ),
- .alu_src2 (alu_src2 ),
- .alu_result (alu_result )
- );
- //-----{调用ALU模块}end
-
- //---------------------{调用触摸屏模块}begin--------------------//
- //-----{实例化触摸屏}begin
- //此小节不需要更改
- reg display_valid;
- reg [39:0] display_name;
- reg [31:0] display_value;
- wire [5 :0] display_number;
- wire input_valid;
- wire [31:0] input_value;
-
- lcd_module lcd_module(
- .clk (clk ), //10Mhz
- .resetn (resetn ),
-
- //调用触摸屏的接口
- .display_valid (display_valid ),
- .display_name (display_name ),
- .display_value (display_value ),
- .display_number (display_number),
- .input_valid (input_valid ),
- .input_value (input_value ),
-
- //lcd触摸屏相关接口,不需要更改
- .lcd_rst (lcd_rst ),
- .lcd_cs (lcd_cs ),
- .lcd_rs (lcd_rs ),
- .lcd_wr (lcd_wr ),
- .lcd_rd (lcd_rd ),
- .lcd_data_io (lcd_data_io ),
- .lcd_bl_ctr (lcd_bl_ctr ),
- .ct_int (ct_int ),
- .ct_sda (ct_sda ),
- .ct_scl (ct_scl ),
- .ct_rstn (ct_rstn )
- );
- //-----{实例化触摸屏}end
-
- //-----{从触摸屏获取输入}begin
- //根据实际需要输入的数修改此小节,
- //建议对每一个数的输入,编写单独一个always块
- //当input_sel为00时,表示输入数控制信号,即alu_control
- always @(posedge clk)
- begin
- if (!resetn)
- begin
- alu_control <= 12'd0;
- end
- else if (input_valid && input_sel==2'b00)
- begin
- alu_control <= input_value[11:0];
- end
- end
-
- //当input_sel为10时,表示输入数为源操作数1,即alu_src1
- always @(posedge clk)
- begin
- if (!resetn)
- begin
- alu_src1 <= 32'd0;
- end
- else if (input_valid && input_sel==2'b10)
- begin
- alu_src1 <= input_value;
- end
- end
-
- //当input_sel为11时,表示输入数为源操作数2,即alu_src2
- always @(posedge clk)
- begin
- if (!resetn)
- begin
- alu_src2 <= 32'd0;
- end
- else if (input_valid && input_sel==2'b11)
- begin
- alu_src2 <= input_value;
- end
- end
- //-----{从触摸屏获取输入}end
-
- //-----{输出到触摸屏显示}begin
- //根据需要显示的数修改此小节,
- //触摸屏上共有44块显示区域,可显示44组32位数据
- //44块显示区域从1开始编号,编号为1~44,
- always @(posedge clk)
- begin
- case(display_number)
- 6'd1 :
- begin
- display_valid <= 1'b1;
- display_name <= "SRC_1";
- display_value <= alu_src1;
- end
- 6'd2 :
- begin
- display_valid <= 1'b1;
- display_name <= "SRC_2";
- display_value <= alu_src2;
- end
- 6'd3 :
- begin
- display_valid <= 1'b1;
- display_name <= "CONTR";
- display_value <={20'd0, alu_control};
- end
- 6'd4 :
- begin
- display_valid <= 1'b1;
- display_name <= "RESUL";
- display_value <= alu_result;
- end
- default :
- begin
- display_valid <= 1'b0;
- display_name <= 40'd0;
- display_value <= 32'd0;
- end
- endcase
- end
- //-----{输出到触摸屏显示}end
- //----------------------{调用触摸屏模块}end---------------------//
- endmodule
testbench.v
- `timescale 1ns / 1ps
-
- module tb;
-
- // Inputs
- reg clk;
- reg [12:0] alu_control;
- reg [31:0] alu_src1;
- reg [31:0] alu_src2;
- // Outputs
- wire [31:0] alu_result;
-
- // Instantiate the Unit Under Test (UUT)
- //alu al(
- // input [11:0] alu_control,
- // input [31:0] alu_src1,
- // input [31:0] alu_src2,
- // output [31:0] alu_result
- // );
- alu alu_module(
- .clk(clk),
- .alu_control(alu_control),
- .alu_src1 (alu_src1 ),
- .alu_src2 (alu_src2 ),
- .alu_result (alu_result )
- );
- initial begin
- // Initialize Inputs
- clk = 0;
- alu_control = 0;
- alu_src1 = 32'H10001111;
- alu_src2 = 32'H00000004;
-
- // Wait 100 ns for global reset to finish
- #100;
- alu_control = 13'b00000_0000_0001;
- #400;
- alu_control = 13'b00000_0000_0010;
- #500;
- alu_control = 13'b00000_0000_0100;
- #400;
- alu_control = 13'b00000_0000_1000;
- #500;
- alu_control = 13'b00000_0001_0000;
- #400;
- alu_control = 13'b00000_0010_0000;
- #500;
- alu_control = 13'b00000_0100_0000;
- #400;
- alu_control = 13'b00000_1000_0000;
- #400;
- alu_control = 13'b00001_0000_0000;
- #400;
- alu_control = 13'b00010_0000_0000;
- #400;
- alu_control = 13'b00100_0000_0000;
- #400;
- alu_control = 13'b01000_0000_0000;
- #400;
- alu_control = 13'b10000_0000_0000;
- // Add stimulus here
- end
- always #5 clk = ~clk;
- endmodule
multiply.v 和 add.v 之前的记录里有。
一般认为“>>>”在Verilog里是算术右移指令,但实操中发现它有时会在右移时仍然补零,即使符号位为1。这是因为“>>>”会先判断这个操作数是否有符号数。如果是无符号数,则补零,是有符号数,才会补符号位。而一般使用的reg operand; 这种变量定义法默认所定义的变量为无符号数,因此只补零。
Result = operandB >>> operandA; //错误示范
解决办法是利用Verilog的内置函数$signed(),将被移位的操作数转为有符号数类型。
Result = ($signed(operandB)) >>> operandA; //更正后