只调用加法模块的仿真图:
(注:alu_control 为十六进制
001高位加载 src2的低16位加载到高16位上
002算数右移 src1算数右移 (高位补1)
004逻辑右移 src1逻辑右移 (逻辑右移直接补0)
008逻辑左移 src1逻辑左移
010异或 020或 040或非 080与
100 无符号比较 小于置位
200 有符号比较 小于置位
400 减法
800 加法
没有调用乘法器的ALU (下面那个调用了)
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
testbench.v
`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
调用乘法器的ALU(代码扔到末尾)
这里结果我不知道怎么只显示希望的结果(出现了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; //更正后