一些Verilog的小东西
2019-07-23 本文已影响0人
今日你学左米啊
一些Verilog的小东西
[TOC]
常用小模块
- 奇数次分频
module fdiv5(input clk,output k_or,k1,k2);
reg [2:0] c1,c2;
reg M1,M2;
always @ (posedge clk )
begin
if(c1 == 4) c1<=0 ; else c1<=c1+1;
if(c1 == 1) M1<=~M1; else if(c1 ==3) M1<=~M1;
end
always @ (negedge clk )
begin
if(c2 == 4) c2<=0; else c2 <=c2+1;
if(c2 == 1) M2<=~M2 ; else if (c2 == 3) M2<=~M2;
end
assign k1 = M1; assign k2=M2;
assign k_or = k1|k2;
endmodule
- PWM的FPGA控制
module PWM_Generator_Verilog
(
clk, // 100MHz clock input
increase_duty, // input to increase 10% duty cycle
decrease_duty, // input to decrease 10% duty cycle
PWM_OUT // 10MHz PWM output signal
);
input clk;
input increase_duty;
input decrease_duty;
output PWM_OUT;
wire slow_clk_enable; // slow clock enable signal for debouncing FFs
reg[27:0] counter_debounce=0;// counter for creating slow clock enable signals
wire tmp1,tmp2,duty_inc;// temporary flip-flop signals for debouncing the increasing button
wire tmp3,tmp4,duty_dec;// temporary flip-flop signals for debouncing the decreasing button
reg[3:0] counter_PWM=0;// counter for creating 10Mhz PWM signal
reg[3:0] DUTY_CYCLE=5; // initial duty cycle is 50%
// Debouncing 2 buttons for inc/dec duty cycle
// Firstly generate slow clock enable for debouncing flip-flop (4Hz)
always @(posedge clk)
begin
counter_debounce <= counter_debounce + 1;
//if(counter_debounce>=25000000) then
// for running on FPGA -- comment when running simulation
if(counter_debounce>=1)
// for running simulation -- comment when running on FPGA
counter_debounce <= 0;
end
// assign slow_clk_enable = counter_debounce == 25000000 ?1:0;
// for running on FPGA -- comment when running simulation
assign slow_clk_enable = counter_debounce == 1 ?1:0;
// for running simulation -- comment when running on FPGA
// debouncing FFs for increasing button
DFF_PWM PWM_DFF1(clk,slow_clk_enable,increase_duty,tmp1);
DFF_PWM PWM_DFF2(clk,slow_clk_enable,tmp1, tmp2);
assign duty_inc = tmp1 & (~ tmp2) & slow_clk_enable;
// debouncing FFs for decreasing button
DFF_PWM PWM_DFF3(clk,slow_clk_enable,decrease_duty, tmp3);
DFF_PWM PWM_DFF4(clk,slow_clk_enable,tmp3, tmp4);
assign duty_dec = tmp3 & (~ tmp4) & slow_clk_enable;
// vary the duty cycle using the debounced buttons above
always @(posedge clk)
begin
if(duty_inc==1 && DUTY_CYCLE <= 9)
DUTY_CYCLE <= DUTY_CYCLE + 1;// increase duty cycle by 10%
else if(duty_dec==1 && DUTY_CYCLE>=1)
DUTY_CYCLE <= DUTY_CYCLE - 1;//decrease duty cycle by 10%
end
// Create 10MHz PWM signal with variable duty cycle controlled by 2 buttons
always @(posedge clk)
begin
counter_PWM <= counter_PWM + 1;
if(counter_PWM>=9)
counter_PWM <= 0;
end
assign PWM_OUT = counter_PWM < DUTY_CYCLE ? 1:0;
endmodule
// Debouncing DFFs for push buttons on FPGA
module DFF_PWM(clk,en,D,Q);
input clk,en,D;
output reg Q;
always @(posedge clk)
begin
if(en==1) // slow clock enable signal
Q <= D;
end
endmodule
- 任意时钟分频
// --------------------------------------------------------------------
// Module Function:任意整数时钟分频
module clkdiv ( clk,rst_n,clkout);
input clk,rst_n; //输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
output clkout; //输出信号,可以连接到LED观察分频的时钟
//parameter是verilog里常数语句
parameter WIDTH = 3; //计数器的位数,计数的最大值为 2**WIDTH-1
parameter N = 5; //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出
reg [WIDTH-1:0] cnt_p,cnt_n; //cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
reg clk_p,clk_n; //clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟
//上升沿触发时计数器的控制
always @ (posedge clk or negedge rst_n ) //posedge和negedge是verilog表示信号上升沿和下降沿
begin
if(!rst_n)
cnt_p<=0;
else if (cnt_p==(N-1))
cnt_p<=0;
else cnt_p<=cnt_p+1; //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
end
//上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_p<=0;
else if (cnt_p<(N>>1)) //N>>1表示右移一位,相当于除以2去掉余数
clk_p<=0;
else
clk_p<=1; //得到的分频时钟正周期比负周期多一个clk时钟
end
//下降沿触发时计数器的控制
always @ (negedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_n<=0;
else if (cnt_n==(N-1))
cnt_n<=0;
else cnt_n<=cnt_n+1;
end
//下降沿触发的分频时钟输出,和clk_p相差半个时钟
always @ (negedge clk)
begin
if(!rst_n)
clk_n<=0;
else if (cnt_n<(N>>1))
clk_n<=0;
else
clk_n<=1; //得到的分频时钟正周期比负周期多一个clk时钟
end
assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p; //条件判断表达式
//当N=1时,直接输出clk
//当N为偶数也就是N的最低位为0,N(0)=0,输出clk_p
//当N为奇数也就是N最低位为1,N(0)=1,输出clk_p&clk_n。正周期多所以是相与
endmodule
- 格雷码计数器
module GrayCounter(
input clk,
output [3:0] cnt_gray
);
reg [3:0] cnt = 0;
always @(posedge clk) cnt <= cnt+1; // 4bit binary counter
assign cnt_gray = cnt ^ cnt[3:1]; // then convert to gray
endmodule
module GrayCounter(
input clk,
output reg [3:0] cnt_gray = 0
);
wire [3:0] cnt_cc = {cnt_cc[2:1] & ~cnt_gray[1:0], ^cnt_gray, 1'b1}; // carry-chain type logic
always @(posedge clk) cnt_gray <= cnt_gray ^ cnt_cc ^ cnt_cc[3:1];
endmodule
- 软件按键消抖
module GrayCounter(
input clk,
output [3:0] cnt_gray
);
reg [3:0] cnt = 0;
always @(posedge clk) cnt <= cnt+1; // 4bit binary counter
assign cnt_gray = cnt ^ cnt[3:1]; // then convert to gray
endmodule
module GrayCounter(
input clk,
output reg [3:0] cnt_gray = 0
);
wire [3:0] cnt_cc = {cnt_cc[2:1] & ~cnt_gray[1:0], ^cnt_gray, 1'b1}; // carry-chain type logic
always @(posedge clk) cnt_gray <= cnt_gray ^ cnt_cc ^ cnt_cc[3:1];
endmodule
- 正交信号解码
module quad(clk,
quadA,
quadB,
count);
input clk, quadA, quadB;
output [7:0] count;
reg [2:0] quadA_delayed, quadB_delayed;
always @( posedge clk ) quadA_delayed <= {quadA_delayed[1:0], quadA};
always @( posedge clk ) quadB_delayed <= {quadB_delayed[1:0], quadB};
wire count_enable = quadA_delayed[1] ^ quadA_delayed[2] ^ quadB_delayed[1] ^ quadB_delayed[2];
wire count_direction = quadA_delayed[1] ^ quadB_delayed[2];
reg [7:0] count;
always @( posedge clk )
begin
if ( count_enable )
begin
if ( count_direction ) count <= count+1; else count <= count-1;
end
end
endmodule
状态机
这里给两种比较常见的写法
a.
module fsm(
input x,
input rst,
input clk,
output reg [1:0] y
);
parameter state_A = 3'd0 , state_B = 3'd1 ,
state_C = 3'd2 , state_D = 3'd3 ,
state_E = 3'd4 ;
reg [2:0] state;
always @(posedge clk or posedge rst)
begin
if(rst)
begin
state <= state_A;
y <= 2'b00;
end
else
case(state)
state_A : begin
if(!x) state <= state_B; else state <= state_C;
y <= 2'b00;
end
state_B : begin
if(!x) state <= state_D; else state <= state_C;
y <= 2'b00;
end
state_C : begin
if(!x) state <= state_E; else state <= state_B;
y <= 2'b00;
end
state_D : begin
if(!x) state <= state_D; else state <= state_C;
y <= 2'b01;
end
state_E : begin
if(!x) state <= state_B; else state <= state_E;
y <= 2'b10;
end
default : begin
state <= state_A; y <= 2'b00;
end
endcase
end
endmodule
b.
module fsm2(
input [3:0] row,
input srow,
input reset,
input clk,
output reg [3:0] col
);
parameter S0 = 3'd0 , S1 = 3'd1 ,
S2 = 3'd2 , S3 = 3'd3 ,
S4 = 3'd4 , S5 = 3'd5 ;
reg [3:0] current_state , next_state;
always @(posedge clk or posedge reset)
begin
if(reset)
current_state <= S0;
else
current_state <= next_state;
end
always @(*)
begin
case(current_state)
S0:begin
next_state = (srow==1'b1) ? S1 : S0;
col = 4'd15;
end
S1:begin
next_state = (row==4'd0) ? S2 : S5;
col = 4'd1;
end
S2:begin
next_state =(row==4'd0) ? S3 : S5;
col = 4'd2;
end
S3:begin
next_state = (row==4'd0) ? S4 : S5;
col = 4'd3;
end
S4:begin
next_state = (row==4'd0) ? S0 : S5;
col = 4'd4;
end
S5:begin
next_state = S5;
col = 4'd5;
end
endcase
end
endmodule
懂的人自然都懂.建议用quartus写,然后看看RTL会贴心地给出状态描述图
仿真的 $display $monitor $strobe的区别
-
$display
相当于c语言的printf,一旦程序触发到就立即显示 -
$monitor
一直以一个格式在追踪某几个变量或者表达式的变化
一个程序里面只能有一个monitor在跑
- $strobe
用于观察非阻塞赋值,在所在的always块结束后才会改变
task 和function的区别
这里也是给上一个功能一样,但是分别用task和function实现的代码
- task_test
module task_test(data_in,data_out);
output reg [3:0] data_out;
input [3:0] data_in;
task BCD2Access3;
output [3:0] data_out;
input [3:0] data_in;
data_out = data_in + 4'd3;
endtask
always @(data_in)
begin
if(data_in >= 4'd10) data_out = 4'b0000;
else BCD2Access3(data_out,data_in);
end
endmodule
- function_test
module function_test(data_in,data_out);
output reg [3:0] data_out;
input [3:0] data_in;
function BCD2Access3;
input [3:0] data_in;
BCD2Access3 = data_in + 4'd3;
endfunction
always @(data_in)
begin
if(data_in >= 4'd10) data_out = 4'b0000;
else data_out = BCD2Access3(data_in);
end
endmodule
务必要注意两点:
1. task可以有很多输入输出,但是例化的顺序和task里面写的input,output顺序是一致的,不能搞反
2. function虽说看起来很C语言,但是要注意返回值就是他的名字(,剩下的都是输入