30那个地方改仿真的时长,默认是10us(但实际上好像是1us) 这里改成30us
//加载被乘数,运算时每次左移一位 (这里把被乘数位拓展了)
reg [63:0] multiplicand
//加载乘数,运算时每次右移一位,相当于y
reg [31:0] multiplier;
// 部分积:乘数末位为1,由被乘数左移得到;乘数末位为0,部分积为0
wire [63:0] partial_product;
//累加器
reg [63:0] product_temp; //临时结果 product_temp <= product_temp + partial_product;
这个部分积直接由被乘数得到,然后每一个时钟上跳沿直接加到temp上。
代码中always是并行的。这是理解乘法器的关键!然后每个always里仅对一个信号赋值!
mult_end 为1的时候 表示乘法运算结束 (故看仿真结果的时候得看end为1时候对应的结果)
mult_valid 为1 表示进行有效的运算
代码见Verilog编程之乘法器的实现_王森ouc的博客-CSDN博客_verilog乘法
人家总结:reg型变量必须通过过程赋值语句赋值!
不能通过assign语句赋值!
而wire型数据不能放在过程块内赋值。
multiply.v 与 testbench.v分别如下
`timescale 1ns / 1ps
module multiply( // 乘法器
input clk, // 时钟
input mult_begin, // 乘法开始信号
input [31:0] mult_op1, // 乘法源操作数1
input [31:0] mult_op2, // 乘法源操作数2
output [63:0] product, // 乘积
output mult_end // 乘法结束信号
);
//乘法正在运算信号和结束信号
reg mult_valid;
assign mult_end = mult_valid & ~(|multiplier); //乘法结束信号:乘数全0
always @(posedge clk) //①
begin
if (!mult_begin || mult_end) //如果没有开始或者已经结束了
begin
mult_valid <= 1'b0; //mult_valid 赋值成0,说明现在没有进行有效的乘法运算
end
else
begin
mult_valid <= 1'b1;
end
end
//两个源操作取绝对值,正数的绝对值为其本身,负数的绝对值为取反加1
wire op1_sign; //操作数1的符号位
wire op2_sign; //操作数2的符号位
wire [31:0] op1_absolute; //操作数1的绝对值
wire [31:0] op2_absolute; //操作数2的绝对值
assign op1_sign = mult_op1[31];
assign op2_sign = mult_op2[31];
assign op1_absolute = op1_sign ? (~mult_op1+1) : mult_op1;
assign op2_absolute = op2_sign ? (~mult_op2+1) : mult_op2;
//加载被乘数,运算时每次左移一位
reg [63:0] multiplicand;
always @ (posedge clk) //②
begin
if (mult_valid)
begin // 如果正在进行乘法,则被乘数每时钟左移一位
multiplicand <= {multiplicand[62:0],1'b0}; //被乘数x每次左移一位。
end
else if (mult_begin)
begin // 乘法开始,加载被乘数,为乘数1的绝对值
multiplicand <= {32'd0,op1_absolute};
end
end
//加载乘数,运算时每次右移一位,相当于y
reg [31:0] multiplier;
always @ (posedge clk) //③
begin
if(mult_valid)
begin //如果正在进行乘法,则乘数每时钟右移一位
multiplier <= {1'b0,multiplier[31:1]}; //相当于乘数y右移一位
end
else if(mult_begin)
begin //乘法开始,加载乘数,为乘数2的绝对值
multiplier <= op2_absolute;
end
end
// 部分积:乘数末位为1,由被乘数左移得到;乘数末位为0,部分积为0
wire [63:0] partial_product;
assign partial_product = multiplier[0] ? multiplicand:64'd0; //若此时y的最低位为1,则把x赋值给部分积partial_product,否则把0赋值给partial_product
//累加器
reg [63:0] product_temp; //临时结果
always @ (posedge clk) //④//clk信号从0变为1时,激发此段语句的执行,但语句的执行需要时间
begin
if (mult_valid)
begin
product_temp <= product_temp + partial_product;
end
else if (mult_begin)
begin
product_temp <= 64'd0;
end
end
//乘法结果的符号位和乘法结果
reg product_sign; //乘积结果的符号
always @ (posedge clk) // 乘积⑤
begin
if (mult_valid)
begin
product_sign <= op1_sign ^ op2_sign;
end
end
//若乘法结果为负数,则需要对结果取反+1
assign product = product_sign ? (~product_temp+1) : product_temp;
endmodule
`timescale 1ns / 1ps
module tb;
// Inputs
reg clk;
reg mult_begin;
reg [31:0] mult_op1;
reg [31:0] mult_op2;
// Outputs
wire [63:0] product;
wire mult_end;
// Instantiate the Unit Under Test (UUT)
multiply uut (
.clk(clk),
.mult_begin(mult_begin),
.mult_op1(mult_op1),
.mult_op2(mult_op2),
.product(product),
.mult_end(mult_end)
);
initial begin
// Initialize Inputs
clk = 0;
mult_begin = 0;
mult_op1 = 0;
mult_op2 = 0;
// Wait 100 ns for global reset to finish
#100;
mult_begin = 1;
mult_op1 = 32'H00001111;
mult_op2 = 32'H00001111;
#400;
mult_begin = 0;
#500;
mult_begin = 1;
mult_op1 = 32'H00001111;
mult_op2 = 32'H00002222;
#400;
mult_begin = 0;
#500;
mult_begin = 1;
mult_op1 = 32'H00000002;
mult_op2 = 32'HFFFFFFFF;
#400;
mult_begin = 0;
#500;
mult_begin = 1;
mult_op1 = 32'H00000002;
mult_op2 = 32'H80000000;
#400;
mult_begin = 0;
// Add stimulus here
end
always #5 clk = ~clk;
endmodule
尽量不要在一个always块里对多个信号赋值,不然可能造成多驱动的问题(在不同的always里若有对相同的赋值,那出大问题(听谁的呢))
wire只是一个线路,并不能把数存住,存住得放到寄存器里(reg)
multiplier multiplicand 为内部信号 可从仿真的左侧栏里Scope-uut模块里-objects-右键某个信号就可以加进去(新加进去的是空的,得刷新下Relaunch)
仿真技巧: shift +滚轮 在变量可以新建文件夹:把要看的变量扔进去
赋初值的时候,常用非阻塞赋值。阻塞赋值即串行,非阻塞是执行完了一起赋值。(p58)
`timescale 1ns/1ns
写在所有仿真文件(.v)的代码首行,时间尺度、精度单位定义,时间尺度预编译指令,用来定义模块仿真时的时间单位和时间精度,不可被综合,但在可综合代码中也可以写,只是会在仿真时表达效果,而综合时会自动被综合器优化掉。格式如下:
`timescale 时间尺度/时间精度 或 `timescale timeunit / timeprecision
注意:仿真时间单位和时间精度的数字只能是1、10、100,不能为其它的数字。而且,时间精度不能比时间单位还要大。最多两者一样大。比如:下面定义都是对的:
`timescale 1ns/1ns
`timescale 100ns/100ns
下面的定义是错的
`timescale 1ps/1ns
时间的进位关系如下: s ms us ns ps 毫秒 微秒 纳秒 皮秒 10的三次方
时间精度就是模块仿真时间和延时的精确程序,比如:定义时间精度为10ns,那么时序中所有的延时至多能精确到10ns,而8ns或者18ns是不可能做到的。
上板验证:
约束文件 与 display文件 变量名字对应上哦,可根据之前加法器的display和约束文件对着看。
set_property PACKAGE_PIN AC19 [get_ports clk]
set_property PACKAGE_PIN U26 [get_ports led_end]
set_property PACKAGE_PIN AB21 [get_ports resetn]
set_property PACKAGE_PIN AE17 [get_ports input_sel]
set_property PACKAGE_PIN AF17 [get_ports sw_begin]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports led_end]
set_property IOSTANDARD LVCMOS33 [get_ports resetn]
set_property IOSTANDARD LVCMOS33 [get_ports input_sel]
set_property IOSTANDARD LVCMOS33 [get_ports sw_begin]
#lcd
set_property PACKAGE_PIN E5 [get_ports lcd_rst]
set_property PACKAGE_PIN G7 [get_ports lcd_cs]
set_property PACKAGE_PIN H7 [get_ports lcd_rs]
set_property PACKAGE_PIN E6 [get_ports lcd_wr]
set_property PACKAGE_PIN D5 [get_ports lcd_rd]
set_property PACKAGE_PIN J5 [get_ports lcd_bl_ctr]
set_property PACKAGE_PIN C4 [get_ports {lcd_data_io[0]}]
set_property PACKAGE_PIN C3 [get_ports {lcd_data_io[1]}]
set_property PACKAGE_PIN D4 [get_ports {lcd_data_io[2]}]
set_property PACKAGE_PIN D3 [get_ports {lcd_data_io[3]}]
set_property PACKAGE_PIN F5 [get_ports {lcd_data_io[4]}]
set_property PACKAGE_PIN G6 [get_ports {lcd_data_io[5]}]
set_property PACKAGE_PIN F4 [get_ports {lcd_data_io[6]}]
set_property PACKAGE_PIN E3 [get_ports {lcd_data_io[7]}]
set_property PACKAGE_PIN G5 [get_ports {lcd_data_io[8]}]
set_property PACKAGE_PIN H6 [get_ports {lcd_data_io[9]}]
set_property PACKAGE_PIN F2 [get_ports {lcd_data_io[10]}]
set_property PACKAGE_PIN F3 [get_ports {lcd_data_io[11]}]
set_property PACKAGE_PIN G4 [get_ports {lcd_data_io[12]}]
set_property PACKAGE_PIN G2 [get_ports {lcd_data_io[13]}]
set_property PACKAGE_PIN H4 [get_ports {lcd_data_io[14]}]
set_property PACKAGE_PIN H3 [get_ports {lcd_data_io[15]}]
set_property PACKAGE_PIN K6 [get_ports ct_int]
set_property PACKAGE_PIN J6 [get_ports ct_sda]
set_property PACKAGE_PIN L8 [get_ports ct_scl]
set_property PACKAGE_PIN K7 [get_ports ct_rstn]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rst]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_cs]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rs]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_wr]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rd]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_bl_ctr]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[12]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[13]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[14]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[15]}]
set_property IOSTANDARD LVCMOS33 [get_ports ct_int]
set_property IOSTANDARD LVCMOS33 [get_ports ct_sda]
set_property IOSTANDARD LVCMOS33 [get_ports ct_scl]
set_property IOSTANDARD LVCMOS33 [get_ports ct_rstn]
module multiply_display(
//时钟与复位信号
input clk,
input resetn, //后缀"n"代表低电平有效
//拨码开关,用于选择输入数
input input_sel, //0:输入为乘数1;1:输入为乘数2
input sw_begin,
//乘法结束信号
output led_end,
//触摸屏相关接口,不需要更改
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
);
//-----{调用乘法器模块}begin
wire mult_begin;
reg [31:0] mult_op1;
reg [31:0] mult_op2;
wire [63:0] product;
wire mult_end;
assign mult_begin = sw_begin;
assign led_end = mult_end;
multiply multiply_module (
.clk (clk ),
.mult_begin(mult_begin),
.mult_op1 (mult_op1 ),
.mult_op2 (mult_op2 ),
.product (product ),
.mult_end (mult_end )
);
reg [63:0] product_r;
always @(posedge clk)
begin
if (!resetn)
begin
product_r <= 64'd0;
end
else if (mult_end)
begin
product_r <= product;
end
end
//-----{调用乘法器模块}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为0时,表示输入数为乘数1
always @(posedge clk)
begin
if (!resetn)
begin
mult_op1 <= 32'd0;
end
else if (input_valid && !input_sel)
begin
mult_op1 <= input_value;
end
end
//当input_sel为1时,表示输入数为乘数2
always @(posedge clk)
begin
if (!resetn)
begin
mult_op2 <= 32'd0;
end
else if (input_valid && input_sel)
begin
mult_op2 <= 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 <= "M_OP1";
display_value <= mult_op1;
end
6'd2 :
begin
display_valid <= 1'b1;
display_name <= "M_OP2";
display_value <= mult_op2;
end
6'd3 :
begin
display_valid <= 1'b1;
display_name <= "PRO_H";
display_value <= product_r[63:32];
end
6'd4 :
begin
display_valid <= 1'b1;
display_name <= "PRO_L";
display_value <= product_r[31: 0];
end
default :
begin
display_valid <= 1'b0;
display_name <= 48'd0;
display_value <= 32'd0;
end
endcase
end
//-----{输出到触摸屏显示}end
//----------------------{调用触摸屏模块}end---------------------//
endmodule