FPGA入门(4):时序逻辑(二)

简介: FPGA入门(4):时序逻辑(二)

FPGA入门(4):时序逻辑(一)+https://developer.aliyun.com/article/1556542

tb_counter.v

`timescale  1ns/1ns
module  tb_counter();
//wire  define
wire            led_out     ;
//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;
//初始化系统时钟、全局复位
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    #20
    sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
initial begin
    $timeformat(-9, 0, "ns", 6);
    $monitor("@time %t: led_out=%b", $time, led_out);
end
//------------- counter_inst --------------
counter
#(
    .CNT_MAX    (25'd24     )   //实例化带参数的模块时要注意格式,当我们想要修改常数在当前模块的值时,直接在实例化参数名后面的括号内修改即可
)
counter_inst
(
    .sys_clk    (sys_clk    ),  //input     sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input     sys_rst_n
    .led_out    (led_out    )   //output    led_out
);
endmodule


方法2实现:带标志信号的计数器

//方法2实现:带标志信号的计数器
module  counter
#(
    parameter   CNT_MAX = 25'd24_999_999    //这是我们第一次使用参数的方式定义常量,使用参数的方式定义常量有很多好处,如:我们在RTL代码中实例化该模块时,如果需要两个不同计数值的计数器我们不必设计两个模块,而是直接修改参数的值即可;另一个好处是在编写Testbench进行仿真时我们也需要实例化该模块,但是我们需要仿真至少0.5s的时间才能够看出到led_out效果,这会让仿真时间很长,也会导致产生的仿真文件很大,所以我们可以通过直接修改参数的方式来缩短仿真的时间而看到相同的效果,且不会影响到RTL代码模块中的实际值,因为parameter定义的是局部参数,所以只在本模块中有效。为了更好的区分,参数名我们习惯上都要大写
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    output  reg     led_out         //输出控制led灯
);
reg     [24:0]  cnt;                //经计算得需要25位宽的寄存器才够500ms
reg             cnt_flag;
//cnt:计数器计数,当计数到CNT_MAX的值时清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 25'b0;
    else    if(cnt == CNT_MAX)
        cnt <= 25'b0;
    else
        cnt <= cnt + 1'b1;
//cnt_flag:计数到最大值产生的标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_flag <= 1'b0;
    else    if(cnt == CNT_MAX - 1'b1)
        cnt_flag <= 1'b1;
    else
        cnt_flag <= 1'b0;
//led_out:输出控制一个LED灯,每当计数满标志信号有效时取反
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led_out <= 1'b0;
    else    if(cnt_flag == 1'b1)
        led_out <= ~led_out;
endmodule



第14讲:分频器:偶分频

分频器是数字系统设计中最常见的基本电路之一。所谓“分频”,就是把输入信号的频率变成成倍地低于输入频率的输出信号。

分频器分为偶数分频器和奇数分频器,和计数器非常类似,有时候甚至可以说就是一个东西。

模块设计

方法1实现:仅实现分频功能

波形绘制

divider_six.v

`timescale  1ns/1ns
//方法1实现:仅实现分频功能
module  divider_six
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    output  reg     clk_out         //对系统时钟6分频后的信号
);
//reg   define
reg [1:0] cnt;  //用于计数的寄存器
//cnt:计数器从0到2循环计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 2'b0;
    else    if(cnt == 2'd2)
        cnt <= 2'b0;
    else
        cnt <= cnt + 1'b1;
//clk_out:6分频50%占空比输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        clk_out <= 1'b0;
    else    if(cnt == 2'd2)
        clk_out <= ~clk_out;
    else 
        clk_out <= clk_out;
        
endmodule

`timescale  1ns/1ns
module  tb_divider_six();
//wire  define
wire    clk_out;
//reg   define
reg     sys_clk;
reg     sys_rst_n;
//初始化系统时钟、全局复位
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    #20
    sys_rst_n <= 1'b1;
 end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
//------------- divider_six_inst -------------
divider_six divider_six_inst
(
    .sys_clk    (sys_clk    ),  //input     sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input     sys_rst_n
    .clk_out    (clk_out    )   //output    clk_out
);
endmodule


方法2实现:实用的降频方法

//方法2实现:实用的降频方法
module  divider_six(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    output  reg     clk_flag        //指示系统时钟6分频后的脉冲标志信号
);
reg [2:0] cnt;  //用于计数的寄存器
//cnt:计数器从0到5循环计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 3'b0;
    else    if(cnt == 3'd5)
        cnt <= 3'b0;
    else
        cnt <= cnt + 1'b1;
//clk_flag:脉冲信号指示6分频
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        clk_flag <= 1'b0;
    else    if(cnt == 3'd4)
        clk_flag <= 1'b1;
    else
        clk_flag <= 1'b0;
endmodule



第15讲:分频器:奇分频

模块设计

方法1实现:仅实现分频功能

模型绘制

divider_five.v

`timescale  1ns/1ns
//方法1实现:仅实现分频功能
module  divider_five
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    output  wire    clk_out         //对系统时钟5分频后的信号
);
//reg   define
reg     [2:0]   cnt;
reg             clk1;
reg             clk2;
//cnt:上升沿开始从0到4循环计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 3'b0;
    else    if(cnt == 3'd4)
        cnt <= 3'b0;
    else
        cnt <= cnt + 1'b1;
//clk1:上升沿触发,占空比高电平维持2个系统时钟周期,低电平维持3个系统时钟周期
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        clk1 <= 1'b0;
    else    if(cnt == 3'd2)
        clk1 <= 1'b1;
    else    if(cnt == 3'd4)
        clk1 <= 1'b0;
    else 
      clk1 <= clk1;
//clk2:下降沿触发,占空比高电平维持2个系统时钟周期,低电平维持3个系统时钟周期
always@(negedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        clk2 <= 1'b0;
    else    if(cnt == 3'd2)
        clk2 <= 1'b1;
    else    if(cnt == 3'd4)
        clk2 <= 1'b0;
    else 
      clk2 <= clk2;
//clk_out:5分频50%占空比输出
assign clk_out = clk1 | clk2;
endmodule

`timescale  1ns/1ns
module  tb_divider_five();
//wire  define
wire    clk_out;
//reg   define
reg     sys_clk;
reg     sys_rst_n;
//初始化系统时钟、全局复位
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    #20
    sys_rst_n <= 1'b1;
 end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always  #10 sys_clk = ~sys_clk;
//------------- divider_five_inst --------------
divider_five    divider_five_inst
(
    .sys_clk    (sys_clk    ),  //input     sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input     sys_rst_n
    .clk_out    (clk_out    )   //output    clk_out
);
endmodule


方法2实现:实用的降频方法

//方法2实现:实用的降频方法
module  divider_five
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    output  reg     clk_flag        //指示系统时钟5分频后的脉冲标志信号
);
reg [2:0] cnt;  //用于计数的寄存器
//cnt:计数器从0到4循环计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 3'b0;
    else    if(cnt == 3'd4)
        cnt <= 3'b0;
    else
        cnt <= cnt + 1'b1;
//clk_flag:脉冲信号指示5分频
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        clk_flag <= 1'b0;
    else    if(cnt == 3'd3)
        clk_flag <= 1'b1;
    else
        clk_flag <= 1'b0;
endmodule



第16讲:按键消抖

按键是最为常见的电子元器件之一,在电子设计中应用广泛;在日常生活中,遥控器、玩具、计算器等等电子产品都使用按键。

在FPGA的实验工程中,我们可以使用其作为系统复位信号或者控制信号的外部输入。

按键消抖主要针对的是机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。

因而在闭合及断开的瞬间均伴随着一连串的抖动,为了保证系统能正确识别按键的开关,就必须对按键的抖动进行处理,这就是按键消抖。


模块设计

波形图绘制

key_filter.v

`timescale  1ns/1ns
module  key_filter
#(
    parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    key_in      ,   //按键输入信号
    output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                    //key_flag为0时表示没有检测到按键被按下
);
//reg   define
reg     [19:0]  cnt_20ms    ;   //计数器
//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX)  //为低电平且已经达到最大值,不再进行清零
        cnt_20ms <= CNT_MAX;
    else
        cnt_20ms <= cnt_20ms + 1'b1;
//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;
endmodule

tb_key_filter.v

`timescale  1ns/1ns
module  tb_key_filter();
//parameter define
//为了缩短仿真时间,我们将参数化的时间值改小
//但位宽依然定义和参数名的值保持一致
//也可以将这些参数值改成和参数名的值一致
parameter   CNT_1MS  = 8'd19   ,
            CNT_11MS = 8'd69   ,
            CNT_41MS = 8'd149  ,
            CNT_51MS = 8'd199  ,
            CNT_60MS = 8'd249  ;
//wire  define
wire            key_flag        ;   //消抖后按键信号
//reg   define
reg             sys_clk         ;   //仿真时钟信号
reg             sys_rst_n       ;   //仿真复位信号
reg             key_in          ;   //模拟按键输入
reg     [7:0]  tb_cnt          ;   //模拟按键抖动计数器
//初始化输入信号
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    key_in    <= 1'b0;
    #20         
    sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
//tb_cnt:按键过程计数器,通过该计数器的计数时间来模拟按键的抖动过程
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        tb_cnt <= 8'b0;
    else    if(tb_cnt == 8'd249)
          //计数器计数到CNT_60MS完成一次按键从按下到释放的整个过程
        tb_cnt <= 8'b0;
    else    
        tb_cnt <= tb_cnt + 1'b1;
//key_in:产生输入随机数,模拟按键的输入情况
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_in <= 1'b1;     //按键未按下时的状态为为高电平
    else    if((tb_cnt >= CNT_1MS && tb_cnt <= CNT_11MS) || (tb_cnt >= CNT_41MS && tb_cnt <= CNT_51MS))
    //在该计数区间内产生非负随机数0、1来模拟10ms的前抖动和10ms的后抖动
        key_in <= {$random} % 2;    
    else    if(tb_cnt >= CNT_11MS && tb_cnt <= CNT_41MS)
        key_in <= 1'b1;
    //按键经过10ms的前抖动后稳定在低电平,持续时间需大于CNT_MAX
    else
        key_in <= 1'b0;
//------------------------ key_filter_inst ------------------------
key_filter
#(
    .CNT_MAX    (20'd24     )
            //修改的CNT_MAX值一定要小于(CNT_41MS - CNT_11MS)
            //否则就会表现为按键一直处于“抖动状态”而没有“稳定状态”
            //无法模拟出按键消抖的效果
)
key_filter_inst
(
    .sys_clk    (sys_clk    ),  //input     sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input     sys_rst_n
    .key_in     (key_in     ),  //input     key_in
                        
    .key_flag   (key_flag   )   //output    key_flag
);
endmodule



目录
相关文章
|
5月前
|
异构计算
FPGA入门(6):数码管静态/动态显示(二)
FPGA入门(6):数码管静态/动态显示(二)
68 10
|
5月前
|
缓存 异构计算
FPGA入门(7):IP核调用(二)
FPGA入门(7):IP核调用(二)
52 0
|
5月前
|
异构计算
FPGA入门(7):IP核调用(一)
FPGA入门(7):IP核调用(一)
99 0
|
5月前
|
异构计算
FPGA入门(6):数码管静态/动态显示(一)
FPGA入门(6):数码管静态/动态显示
54 0
|
5月前
|
异构计算
FPGA入门(5):控制LED灯
FPGA入门(5):控制LED灯
58 0
|
5月前
|
存储 异构计算
FPGA入门(4):时序逻辑(一)
FPGA入门(4):时序逻辑
49 0
|
5月前
|
存储 异构计算
FPGA入门(3):组合逻辑
FPGA入门(3):组合逻辑
51 0
|
5月前
|
算法 异构计算
FPGA入门(2):Verilog HDL基础语法
FPGA入门(2):Verilog HDL基础语法
38 0
|
8天前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现了16QAM基带通信系统,包括调制、信道仿真、解调及误码率统计模块。通过Vivado2019.2仿真,设置不同SNR(如8dB、12dB),验证了软解调相较于传统16QAM系统的优越性,误码率显著降低。系统采用Verilog语言编写,详细介绍了16QAM软解调的原理及实现步骤,适用于高性能数据传输场景。
107 69
|
12天前
|
移动开发 算法 数据安全/隐私保护
基于FPGA的QPSK调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的QPSK调制解调系统,通过Vivado 2019.2进行仿真,展示了在不同信噪比(SNR=1dB, 5dB, 10dB)下的仿真效果。与普通QPSK系统相比,该系统的软解调技术显著降低了误码率。文章还详细阐述了QPSK调制的基本原理、信号采样、判决、解调及软解调的实现过程,并提供了Verilog核心程序代码。
49 26

热门文章

最新文章