Verilog案例
1、用verilog HDL设计一个4位加法器树乘法器
`timescale 1ns/10ps module mul_addtree(mul_a,mul_b,mul_out); input [3:0] mul_a,mul_b; //IO端口声明 output [7:0] mul_out; wire [7:0] mul_out; //连线类型声明 wire [7:0] store0,store1,store2,store3; wire [7:0] add01,add23; assign store0 = mul_b[0]?{4'b0000,mul_a}:8'b0000_0000; assign store1 = mul_b[1]?{3'b000,mul_a,1'b0}:8'b0000_0000; assign store2 = mul_b[2]?{2'b00,mul_a,2'b00}:8'b0000_0000; assign store3 = mul_b[3]?{1'b0,mul_a,3'b000}:8'b0000_0000; assign add01 = store0+store1; assign add23 = store2+store3; assign mul_out = add01+add23; endmodule //*****************testbench of mul_addtree****************** module mul_addtree_tb; wire [7:0] mul_out; //输出是wire reg [3:0] mul_a; //输入是reg reg [3:0] mul_b; //模块例化 mul_addtree U(.mul_a(mul_a),.mul_b(mul_b),.mul_out(mul_out)); //测试信号 initial begin mul_a=4'b0;mul_b=4'b0; repeat(9) begin #20 mul_a = mul_a+1'b1;mul_b = mul_b+1'b1; end end endmodule
2、用verilog HDL设计一个两级流水线4位加法器树乘法器
`timescale 1ns/10ps module mul_addtree2(clk,clr,mul_a,mul_b,mul_out); input clk,clr; input [3:0] mul_a,mul_b; //IO端口声明 output [7:0] mul_out; reg [7:0] add_tmp1,add_tmp2,mul_out; //连线类型声明 wire [7:0] store0,store1,store2,store3; assign store0 = mul_b[0]?{4'b0000,mul_a}:8'b0000_0000; //逻辑设计 assign store1 = mul_b[1]?{3'b000,mul_a,1'b0}:8'b0000_0000; assign store2 = mul_b[2]?{2'b00,mul_a,2'b00}:8'b0000_0000; assign store3 = mul_b[3]?{1'b0,mul_a,3'b000}:8'b0000_0000; always@(posedge clk or negedge clr) begin if(!clr) begin //注意使用非阻塞语句 add_tmp1 <= 8'b0; add_tmp2 <= 8'b0; mul_out <= 8'b0; end else begin add_tmp1 <= store2+store3; add_tmp2 <= store1+store0; mul_out <= add_tmp1+add_tmp2; end end endmodule //*****************testbench of mul_addtree****************** module mul_addtree2_tb; wire [7:0] mul_out; //输出是wire reg [3:0] mul_a; //输入是reg reg [3:0] mul_b; reg clk,clr; //模块例化 mul_addtree2 U(.mul_a(mul_a),.mul_b(mul_b),.mul_out(mul_out),.clk(clk),.clr(clr)); //测试信号 initial begin clk=0;clr=0;mul_a=1;mul_b=1; #5 clr=1; end always #10 clk=~clk; initial begin repeat(5) begin #20 mul_a = mul_a+1;mul_b = mul_b+1; end end endmodule
3、用verilog HDL 设计4位wallace树乘法器
1)从数据最多的那一列开始,用半加器将数据最多的那一列中的两个数相加,得到和a0以及向高列的进位b0;
这时得到进位的那一列也有四个数据,我们也用半加器相加得到积a1和进位b0
2)用半加器和全加器反复压缩覆盖所有数据超过三个的列,得到下表所示的数据
3)最后采用简单的2输入加法器,就可以得到相乘结果了
module wallace(x,y,out); input [3:0] x,y; //IO端口声明 output [7:0] out; wire [7:0] out; //连线类型声明 wire [15:0] a; wire [1:0] out1,out2,out3,out4,out5,out6; wire [6:0] add1,add2; assign a = {x[0],x[1],x[2],x[3],x[0],x[1],x[2],x[3],x[0],x[1],x[2],x[3],x[0],x[1],x[2],x[3]} &{y[0],y[0],y[0],y[0],y[1],y[1],y[1],y[1],y[2],y[2],y[2],y[2],y[3],y[3],y[3],y[3]}; //第一级 2个半加器 Hadd U1(.x(a[3]),.y(a[6]),.out(out1)); Hadd U2(.x(a[2]),.y(a[5]),.out(out2)); //第二级 3个全加器+1个半加器 Hadd U3(.x(a[7]),.y(a[10]),.out(out3)); Fadd U4(.x(a[12]),.y(a[9]),.c(out1[0]),.out(out4)); Fadd U5(.x(a[8]),.y(out2[0]),.c(out1[1]),.out(out5)); Fadd U6(.x(a[4]),.y(a[1]),.c(out2[1]),.out(out6)); assign add1 = {a[0],out6[0],out5[0],out4[0],a[13],a[14],a[15]}; assign add2 = {out6[1],out5[1],out4[1],out3[1],out3[0],a[11],1'b0}; assign out = add1+add2; endmodule module Hadd(x,y,out); input x,y; output [1:0] out; assign out = x+y; endmodule module Fadd(x,y,c,out); input x,y,c; output [1:0] out; assign out = x+y+c; endmodule //*************测试代码*************** module wallace_tb; reg [3:0] x,y; wire [7:0] out; wallace U(.x(x),.y(y),.out(out)); initial begin x=3;y=4; #20 x=2;y=3; #20 x=6;y=8; end endmodule
4、用verilog HDL设计一个十六进制键盘电路的键盘扫描和编码器
1)键盘的每一行都会通过一个下拉电阻连接到地,而行和列之间有一个以按键为开关的连接,当按下按键时行和列之间就建立了连续。
2)如果列线的值为1,则由按键连接到的那个列的行值也会被拉升为1,反之行线将会被下拉至0。
3)设计一个时序状态机,通过不同状态之间的转换来实现信号的检测识别以及对应扫描的输出。
每一步对应一个时钟周期
第一步,S_0状态,将Col[3:0] 都置为1,检测row[3:0]的信号值;若row为1,跳到S_1; 若为0,保持在当前状态;
第二步,S_1状态,将Col[0] 置为1,检测row[3:0]的信号值;若row为1,跳到S_5; 若为0,跳到S_2;
第三步,S_2状态,将Col[1] 置为1,检测row[3:0]的信号值;若row为1,跳到S_5; 若为0,跳到S_3;
第四步,S_3状态,将Col[2] 置为1,检测row[3:0]的信号值;若row为1,跳到S_5; 若为0,跳到S_4;
第五步,S_4状态,将Col[3] 置为1,检测row[3:0]的信号值;若row为1,跳到S_5; 若为0,跳到S_0;
第六步,S_5状态,将Col[3:0] 置为1(意味着code的输出值只需要维持一个clk,Valid 的值也一样),检测row[3:0] 的信号值,若row为1,保持为S_5,若为0,跳到S_0
4)十六进制键盘电路的编码器码表
5)keypad.v
//顶层模块 module keypad(clock,reset,row,code,valid,col); input clock,reset; input[3:0] row; output[3:0] code; output valid; output[3:0] col; wire s_row; hex_keypad_grayhill U1(.code(code),.col(col),.valid(valid),.row(row),.s_row(s_row),.clock(clock),.reset(reset)); synchronizer U2(.s_row(s_row),.row(row),.clock(clock),.reset(reset)); endmodule //编码模块 module hex_keypad_grayhill(code,col,valid,row,s_row,clock,reset); output[3:0] code; //对应扫描码 output valid; //当前输出有效 output[3:0] col; //列 input[3:0] row; //行 input s_row; //因为键盘的输入是一个异步信号,因此需要引入一个同步模块,将row 的信号转换成同步的s_row 信号 input clock,reset; reg[3:0] col; reg[3:0] code; reg[5:0] state,next_state; parameter S_0=6'b000_001,S_1=6'b000_010,S_2=6'b000_100,S_3=6'b001_000,S_4=6'b010_000,S_5=6'b100_000; assign valid=(state==S_1 || state==S_2 || state==S_3 || state==S_4) && row; always@(row or col) begin case({row,col}) 8'b0001_0001:code=0; 8'b0001_0010:code=1; 8'b0001_0100:code=2; 8'b0001_1000:code=3; 8'b0010_0001:code=4; 8'b0010_0010:code=5; 8'b0010_0100:code=6; 8'b0010_1000:code=7; 8'b0100_0001:code=8; 8'b0100_0010:code=9; 8'b0100_0100:code=10; 8'b0100_1000:code=11; 8'b1000_0001:code=12; 8'b1000_0010:code=13; 8'b1000_0100:code=14; 8'b1000_1000:code=15; default code=0; endcase end always@(state or row or s_row) begin next_state=state; //要初始化,使得系统复位以后能进入正确的状态 col=0; case(state) S_0:begin col=15; if(s_row) next_state=S_1; end S_1:begin col=1; if(row) next_state=S_5; else next_state=S_2; end S_2:begin col=2; if(row) next_state=S_5; else next_state=S_3; end S_3:begin col=4; if(row) next_state=S_5; else next_state=S_4; end S_4:begin col=8; if(row) next_state=S_5; else next_state=S_0; end S_5:begin col=15; if(row) next_state=S_5; else next_state=S_0; end endcase end always@(posedge clock or posedge reset) begin if(reset) state<=S_0; else state<=next_state; end endmodule module synchronizer(s_row,row,clock,reset); input clock,reset; input[3:0] row; output s_row; reg s_row; reg a_row; always@(negedge clock or posedge reset) //为了消除潜在的冒险,这里的同步clk 选用下边沿驱动 begin if(reset) begin a_row<=0; s_row<=0; end else begin a_row<=row[0]||row[1]||row[2]||row[3]; //通过检测各个行线值的或逻辑来确定是否有按键按下 s_row<=a_row; end end endmodule
6)hex_keypad_grayhill_tb.v
`timescale 1ns/1ns module hex_keypad_grayhill_tb; wire[3:0] code; wire valid; wire[3:0] col; wire[3:0] row; reg clock; reg reset; reg[15:0] key; integer j,k; reg [39:0] pressed;//在ASCII码中一个英文字符占一个字节,这里有5个英文字符故需5*8=40个bit位 keypad U1(.clock(clock),.reset(reset),.row(row),.code(code),.valid(valid),.col(col));//顶层模块 row_signal U2(.row(row),.key(key),.col(col)); //产生测试信号 always@(key)begin case(key) 16'h0000:pressed="None"; 16'h0001:pressed="key_0"; 16'h0002:pressed="key_1"; 16'h0004:pressed="key_2"; 16'h0008:pressed="key_3"; 16'h0010:pressed="key_4"; 16'h0020:pressed="key_5"; 16'h0040:pressed="key_6"; 16'h0080:pressed="key_7"; 16'h0100:pressed="key_8"; 16'h0200:pressed="key_9"; 16'h0400:pressed="key_A"; 16'h0800:pressed="key_B"; 16'h0100:pressed="key_C"; 16'h0200:pressed="key_D"; 16'h0400:pressed="key_E"; 16'h0800:pressed="key_F"; default:pressed="None"; endcase end initial #2000 $stop; initial begin clock=0; forever #5 clock=~clock; end initial begin reset=1; #10 reset=0; end initial begin for(k=0;k<=1;k=k+1) begin key=0; #20 for(j=0;j<=16;j=j+1) begin #20 key[j]=1; #60 key=0; end end end endmodule //用于确定按键所在的行 module row_signal(row,key,col); output[3:0] row; input[15:0] key; input[3:0] col; reg[3:0] row; always@(key or col) begin row[0]=(key[0]&&col[0])||(key[1]&&col[1])||(key[2]&&col[2])||(key[3]&&col[3]); row[1]=(key[4]&&col[0])||(key[5]&&col[1])||(key[6]&&col[2])||(key[7]&&col[3]); row[2]=(key[8]&&col[0])||(key[9]&&col[1])||(key[10]&&col[2])||(key[11]&&col[3]); row[3]=(key[12]&&col[0])||(key[13]&&col[1])||(key[14]&&col[2])||(key[15]&&col[3]); end endmodule
7)波形图