一些Verilog的小东西

2019-07-23  本文已影响0人  今日你学左米啊

一些Verilog的小东西

[TOC]

常用小模块

  1. 奇数次分频

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

  1. 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
  1. 任意时钟分频
// --------------------------------------------------------------------
// 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     
  1. 格雷码计数器
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
  1. 软件按键消抖
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
  1. 正交信号解码
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的区别

  1. $display
    相当于c语言的printf,一旦程序触发到就立即显示

  2. $monitor
    一直以一个格式在追踪某几个变量或者表达式的变化

一个程序里面只能有一个monitor在跑

  1. $strobe
    用于观察非阻塞赋值,在所在的always块结束后才会改变

task 和function的区别

这里也是给上一个功能一样,但是分别用task和function实现的代码

  1. 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
  1. 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语言,但是要注意返回值就是他的名字(,剩下的都是输入

上一篇下一篇

猜你喜欢

热点阅读