16位乘法器学习笔记(Verilog语言源程序+仿真程序)
LCD1602显示源程序如下:
module lcd1602(input clk, //60M
input rst_n,
output lcd_p, //Backlight Source + lcd屏幕背光
output lcd_n, //Backlight Source -
output reg lcd_rs, //0:write order; 1:write data
output lcd_rw, //0:write data; 1:read data
output reg lcd_en, //negedge 在lcd_en下降沿需保证数据有效
output reg [7:0] lcd_data);
mux16
mul(.rst_n(rst_n),.clk(clk),.start(start),.ain(data0),.bin(data1),.yout(data2),.done(done));//端口名称关联
//--------------------lcd1602 order----------------------------
parameter Mode_Set = 8'h31, //功能设置,
Cursor_Set = 8'h0c, //光标设置
Address_Set = 8'h06, //输入模式设置
Clear_Set = 8'h01; //清屏设置
/****************************LCD1602 Display Data****************************/ wire [7:0] data_r0,data_r1,data_r2; //乘数、被乘数
wire [15:0]data0,data1; //结果显示
wire [31:0]data2;
wire [7:0] addr; //write address
wire start,done;
assign data_r0 = 8'h30 + data0[7:0] ; // 8'h30在LCD1602上显示值为0。
assign data_r1 = 8'h30 + data1[7:0] ;
assign data_r2 = 8'h30 + data2[7:0];
//-------------------address------------------
assign addr = 8'h80;
/****************************LCD1602 Driver****************************/
//-----------------------lcd1602 clk_en---------------------
reg [31:0] cnt;
reg lcd_clk_en;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt <= 1'b0;
lcd_clk_en <= 1'b0;
end
else if(cnt == 32'h24999) //600us
begin
lcd_clk_en <= 1'b1;
cnt <= 1'b0;
end
else
begin
cnt <= cnt + 1'b1;
lcd_clk_en <= 1'b0;
end
end
//-----------------------lcd1602 display state-------------------------------------------
reg [6:0] state;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= 1'b0;
lcd_rs <= 1'b0;
lcd_en <= 1'b0;
lcd_data <= 1'b0;
end
else if(lcd_clk_en)
begin
case(state)
//-------------------init_state---------------------
6'd0: begin
lcd_rs <= 1'b0;
lcd_en <= 1'b1;
lcd_data <= Mode_Set; //进入功能设置模式,“31”:数据总线8位,显示一行。6*7点阵/每字符
state <= state + 1'd1;
end
6'd1: begin
lcd_en <= 1'b0; //lcd_en变低电平,使led_en出现下降沿
state <= state + 1'd1;
end
6'd2: begin
lcd_rs <= 1'b0;
lcd_en <= 1'b1;
lcd_data <= Cursor_Set;//光标设置:光标右移一格,且AC值加1.
state <= state + 1'd1;
end
6'd3: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd4: begin
lcd_rs <= 1'b0;
lcd_en <= 1'b1;
lcd_data <= Address_Set;//模式设置:写入新数据后光标右移,AC自增1
state <= state + 1'd1;
end
6'd5: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd6: begin
lcd_rs <= 1'b0;
lcd_en <= 1'b1;
lcd_data <= Clear_Set;//清屏操作:0x01
state <= state + 1'd1;
end
6'd7: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
//--------------------work state--------------------
6'd8: begin
lcd_rs <= 1'b0;
lcd_en <= 1'b1;
lcd_data <= addr; //write addr
state <= state + 1'd1;
end
6'd9: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd10: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= "R"; //write data
state <= state + 1'd1;
6'd11: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd12: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= "E"; //write data
state <= state + 1'd1;
end
6'd13: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd14: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= "S"; //write data
state <= state + 1'd1;
end
6'd15: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd16: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= ":"; //write data
state <= state + 1'd1;
end
6'd17: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd18: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= data_r1; //write data: tens digit
state <= state + 1'd1;
end
6'd19: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
6'd20: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= "*";
state <= state + 1'd1;
end
6'd21: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd22: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= data_r0; //write data: single digit
state <= state + 1'd1;
end
6'd23: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd24: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= "="; //write data: single digit
state <= state + 1'd1;
end
6'd25: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd26: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= data_r2; //write data: single digit
state <= state + 1'd1;
end
6'd27: begin
lcd_en <= 1'b0;
state <= state + 1'd1;
end
6'd28: begin
lcd_rs <= 1'b1;
lcd_en <= 1'b1;
lcd_data <= "."; //write data: single digit
state <= state + 1'd1;
end
6'd29: begin
lcd_en <= 1'b0;
state <= 6'd8;
end
default: state <= 6'bxxxxxx;
endcase
end
end
assign lcd_rw =1'b0; //only write
//------------------backlight driver----------------
assign lcd_n = 1'b0;
assign lcd_p = 1'b1;
Endmodule
16位乘法器源程序如下:
`timescale 1ns / 1ps
module mux16(
clk,rst_n,
start,ain,bin,yout,done
);
input clk; //芯片的时钟信号。
input rst_n; //低电平复位、清零信号。定义为0表示芯片复位;定义为1表示复位信号无效。
input start; //芯片使能信号。定义为0表示信号无效;定义为1表示芯片读入输入管脚得乘数和被乘数,并将
//乘积复位清零。
input[15:0] ain; //输入a(被乘数),其数据位宽为16bit.
input[15:0] bin; //输入b(乘数),其数据位宽为16bit.
output[31:0] yout; //乘积输出,其数据位宽为32bit.
output done; //芯片输出标志信号。定义为1表示乘法运算完成.
reg[15:0] areg; //乘数a寄存器
reg[15:0] breg; //乘数b寄存器
reg[31:0] yout_r; //乘积寄存器
reg done_r;
reg[4:0] i; //移位次数寄存器
//------------------------------------------------
//数据位控制
always @(posedge clk or negedge rst_n)
if(!rst_n) i <= 5'd0;
else if(start && i < 5'd17) i <= i+1'b1;
else if(!start) i <= 5'd0;
//------------------------------------------------
//乘法运算完成标志信号产生
always @(posedge clk or negedge rst_n)
if(!rst_n) done_r <= 1'b0;
else if(i == 5'd16) done_r <= 1'b1; //乘法运算完成标志
else if(i == 5'd17) done_r <= 1'b0; //标志位撤销
assign done = done_r;
//------------------------------------------------
//专用寄存器进行移位累加运算
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
areg <= 16'h0003;
breg <= 16'h0003;
yout_r <= 32'h00000000;
end
else if(start) begin //启动运算
if(i == 5'd0) begin //锁存乘数、被乘数
areg <=ain;
breg <= bin;
end
else if(i > 5'd0 && i < 5'd16) begin
if(areg[i-1]) yout_r = {1'b0,yout[30:15]+breg,yout_r[14:1]}; //累加并移位
else yout_r <= yout_r>>1; //移位不累加
end
else if(i == 5'd16 && areg[15]) yout_r[31:16] <= yout_r[31:16]+breg;
//累加不移位
end
end
assign yout = yout_r;
endmodule
Modelsim仿真激励文件如下:
`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 21:31:52 09/11/2008
// Design Name: test_top
// Module Name: vtf_test1.v
// Project Name: top_dram
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: test_top
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////
module vtf_test;
reg clk; //芯片的时钟信号。
reg rst_n; //低电平复位、清零信号。定义为0表示芯片复位;定义为1表示复位信号无效。reg start; //芯片使能信号。定义为0表示信号无效;定义为1表示芯片读入输入管脚得乘数和被乘数,并将乘积复位清零。
reg[15:0] ain; //输入a(被乘数),其数据位宽为16bit.
reg[15:0] bin; //输入b(乘数),其数据位宽为16bit.
wire[31:0] yout; //乘积输出,其数据位宽为32bit.
wire done; //芯片输出标志信号。定义为1表示乘法运算完成.
mux16 uut(
.clk(clk),
.rst_n(rst_n),
.start(start),
.ain(ain),
.bin(bin),
.yout(yout),
.done(done)
);
initial begin
clk = 0;
forever
#10 clk = ~clk; //??50MHZ???
end
initial begin
rst_n = 1'b0;
start = 1'b0;
ain = 16'd0;
bin = 16'd0;
#1000;
rst_n = 1'b1; //上电后1us复位信号
#1000;
ain = 16'd89;
bin = 16'd33;
#100;
start = 1'b1;
#4500;
start = 1'b0;
#100;
ain = 16'd75;
bin = 16'd62;
#100;
start = 1'b1;
#4500;
start = 1'b0;
#1000_000;
$stop;
end
endmodule
注:上述程序只是提供大概思路,请根据任务具体要求修改程序!
说明如下:
1、熟悉吞吐量概念
2、else if(i > 5'd0 && i < 5'd16) begin
if(areg[i-1]) yout_r = {1'b0,yout[30:15]+breg,yout_r[14:1]}; //
累加并移位
else yout_r <= yout_r>>1; //移位不累加
end
else if(i == 5'd16 && areg[15])
yout_r[31:16] <= yout_r[31:16]+breg;//累加不移位
对于if(areg[i-1]) yout_r = {1'b0,yout[30:15]+breg,yout_r[14:1]};此句说明如下:
(1)、二进制中16位加上16位结果为17位
(2)、areg[0]为1时,第一次只是加并不右移,后面areg[i-1]再次为1时执行累加并右移命令。右移实现通过{}拼接强制最高位(即第32位)为0实现的。
(3)、else if(i == 5'd16 && areg[15])
yout_r[31:16] <= yout_r[31:16]+breg;//累加不移位
对于此语句当areg[15]即最高位为1且i==16时只进行累加,再次应注意最高位(即第32位)的变化。
3、第2条的思想是基于如下二进制算法实现的:
1101
X 1011
1101
1101
0000
1101
100 01 011
4、尝试结果在LCD1602上面显示出来:
(1)assign data_r2 = 8'h30 + data2[7:0];此语句之所以加上8’h30 是因为8’h30在lcd显示时存放的是0的地址。
(2)问题:现在lcd显示屏只能显示八个字符,暂时不知道为什么?另一个问题就是:此lcd显示两行的时候字符会变暗,显示一行就很正常!!!
只能显示八个的原因是:硬件问题,lcd1602出问题了!(坑了)5、16位乘法器仿真图如下:
从上图中能看到start变为1后的第一个周期内yout为0,从下面一个周期开始yout开始输出值,且移位14次即到第15个周期,一直到第16个时钟周期时,yout输出了最终结果值,done也由0变为了1。