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

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

组合逻辑存在竞争冒险

第11讲:寄存器

寄存器具有存储功能,一般是由D触发器构成,由时钟脉冲控制,每个D触发器能够存储一位二进制码。

D触发器的工作原理:在一个脉冲信号(一般为晶振产生的时钟脉冲)上升沿或下降沿的作用下,将信号从输入端D送到输出端Q,如果时钟脉冲的边沿信号未出现,即使输入信号改变,输出信号仍然保持原值,且寄存器拥有复位清零功能,其复位又分为同步复位和异步复位。


模块设计

异步复位解决毛刺的影响,提高了可靠性

异步复位有延迟一拍的效果


波形图绘制

flip_flop.v

`timescale  1ns/1ns
module  flip_flop
(
    input   wire    sys_clk     ,   //系统时钟50Mhz,后面我们都是设计的时序电路,所以一定要有时钟,时序电路中几乎所有的信号都是伴随着时钟的沿(上升沿或下降沿,习惯上用上升沿)进行工作的
    input   wire    sys_rst_n   ,   //全局复位,复位信号的主要作用是在系统出现问题是能够回到初始状态,或一些信号的初始化时需要进行复位
    input   wire    key_in      ,   //输入按键
    output  reg     led_out         //输出控制led灯
);
//同步复位
//led_out:led灯输出的结果为key_in按键的输入值
always@(posedge sys_clk)    //当always块中的敏感列表为检测到sys_clk上升沿时执行下面的语句
    if(sys_rst_n == 1'b0)   //sys_rst_n为低电平时复位,但是这个复位有个大前提,那就是当sys_clk的上升沿到来时,如果检测到sys_rst_n为低电平则复位有效。
        led_out <= 1'b0;    //复位的时候一定要给寄存器变量赋一个初值,一般情况下赋值为0(特殊情况除外),在描述时序电路时赋值符号一定要使用“<=”
    else
        led_out <= key_in;
/*
//异步复位
//led_out:led灯输出的结果为key_in按键的输入值
always@(posedge sys_clk or negedge sys_rst_n) //当always块中的敏感列表为检测到sys_clk上升沿或sys_rst_n下降沿时执行下面的语句
    if(sys_rst_n == 1'b0)                     //sys_rst_n为低电平时复位,且是检测到sys_rst_n的下降沿时立刻复位,不需等待sys_clk的上升沿来到后再复位
        led_out <= 1'b0;
    else
        led_out <= key_in;
 */
endmodule

同步复位RTL视图:

异步复位RTL视图

tb_flip_flop.v

`timescale  1ns/1ns
module  tb_flip_flop();
//wire  define
wire            led_out     ;
//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;
reg             key_in      ;
//初始化系统时钟、全局复位和输入信号
initial begin
    sys_clk    = 1'b1;  //时钟信号的初始化为1,且使用“=”赋值,其他信号的赋值都是用“<=”
    sys_rst_n <= 1'b0;  //因为低电平复位,所以复位信号的初始化为0
    key_in    <= 1'b0;  //输入信号按键的初始化,为0和1均可
    #20
    sys_rst_n <= 1'b1;  //初始化20ns后,复位释放,因为是低电平复位,所示释放时,把信号拉高,电路开始工作
    #210
    sys_rst_n <= 1'b0;  //为了观察同步复位和异步复位的区别,在复位释放后电路工作210ns后再让复位有效。之所以选择延时210ns而不是200ns或220ns,是因为能够使复位信号在时钟下降沿时复位,能够清晰的看出同步复位和异步复位的差别
    #40
    sys_rst_n <= 1'b1;  //复位40ns后再次让复位释放掉
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk; //使用always产生时钟信号,让时钟每隔10ns反转一次,即一个时钟周期为20ns,换算为频率为50Mhz
//key_in:产生输入随机数,模拟按键的输入情况
always #20 key_in <= {$random} % 2; //取模求余数,产生非负随机数0、1,每隔20ns产生一次随机数(之所以每20ns产生一次随机数而不是之前的每10ns产生一次随机数,是为了在时序逻辑中能够保证key_in信号的变化的时间小于等于时钟的周期,这样就不会产生类似毛刺的变化信号,虽然产生的毛刺在时序电路中也能被滤除掉,但是不便于我们观察波形)
initial begin
    $timeformat(-9, 0, "ns", 6);
    $monitor("@time %t: key_in=%b led_out=%b", $time, key_in, led_out);
end
//------------- flip_flop_inst -------------
flip_flop   flip_flop_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
    .led_out    (led_out    )   //output    led_out
);
endmodule


第12讲:阻塞赋值与非阻塞赋值

阻塞赋值的赋值号用“=”表示,对应的电路结构往往与触发沿没有关系,只与输入电平的电话有关系。它的操作可以认为是只有一个步骤的操作,即计算赋值号右边的语句并更新赋值号左边的语句,此时不允许有来自任何其他Verilog语句的干扰,直到现行得到赋值完成,才允许下一条的赋值语句的执行。

串行块(begin-end)中,各条阻塞赋值语句将以它们在顺序块中的排列次序依次执行。

非阻塞赋值的赋值号用“<=”表示,对应的电路结构往往与触发沿有关系,只有在触发沿的时刻才能进行非阻塞赋值。

它的操作可以看作为两个步骤的过程:在赋值开始时刻,计算赋值号右边的语句。在赋值结束时刻,更新赋值号左边的语句。

在计算非阻塞语句赋值号右边的语句和更新赋值号左边的语句期间,允许其他的Verilog语句同时进行操作。

非阻塞操作只能用于对寄存器类型变量进行赋值,因此只能用于“initial”和“always”块中,不允许用于连续赋值“assign”


阻塞赋值例子

blocking.v

`timescale  1ns/1ns
module  blocking
(
    input   wire            sys_clk     ,   //系统时钟50Mhz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire    [1:0]   in          ,   //输入信号
    output  reg     [1:0]   out             //输出信号
);
reg     [1:0]   in_reg;
//in_reg:给输入信号打一拍
//out:输出控制一个LED灯
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            in_reg = 2'b0;
            out    = 2'b0;
        end
    else
        begin
            in_reg = in;
            out    = in_reg;
        end
endmodule

tb_blocking.v

`timescale  1ns/1ns
module tb_blocking();
wire    [1:0]   out         ;
reg             sys_clk     ;
reg             sys_rst_n   ;
reg     [1:0]   in          ;
//初始化系统时钟、全局复位和输入信号
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    in        <= 2'b0;
    #20;
    sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
//in:产生输入随机数,模拟按键的输入情况
always #20 in <= {$random} % 4; //取模求余数,产生非负随机数0、1,2,3,每隔20ns产生一次随机数
//------------------------ blocking_inst ------------------------
blocking    blocking_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .in         (in         ),  //input     [1:0]   in
    .out        (out        )   //output    [1:0]   out
);
endmodule


非阻塞赋值例子

non_blocking.v

`timescale  1ns/1ns
module  non_blocking
(
    input   wire            sys_clk     ,   //系统时钟50Mhz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire    [1:0]   in          ,   //输入按键
    output  reg     [1:0]   out             //输出控制led灯
);
reg     [1:0]   in_reg;
//in_reg:给输入信号打一拍
//out:输出控制一个LED灯
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            in_reg <= 2'b0;
            out    <= 2'b0;
        end
    else
        begin
            in_reg <= in;
            out    <= in_reg;
        end
endmodule

非阻塞的仿真代码与阻塞的一样



第13讲:计数器

计数是一种最简单基本的运算,计数器就是实现这种运算的逻辑电路,计数器在数字系统中主要是对脉冲的个数进行计数,以实现测量、计数和控制的功能,同时兼有分频功能。

计数器在数字系统中应用广泛,如电子计算机的控制器中对指令地址进行计数,以便顺序取出下一条指令,在运算器中作乘法、除法运算时记下加法、减法次数,又如在数字仪器中对脉冲的计数等等。


模块设计

波形绘制:每个周期前0.5秒点亮LED,后0.5秒熄灭


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

`timescale  1ns/1ns
//方法1实现:不带标志信号的计数器
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   define
reg     [24:0]  cnt;                //经计算得需要25位宽的寄存器才够500ms
//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;
//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 == CNT_MAX)
        led_out <= ~led_out;
endmodule

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

目录
相关文章
|
4天前
|
异构计算
FPGA入门(6):数码管静态/动态显示(二)
FPGA入门(6):数码管静态/动态显示(二)
18 10
|
4天前
|
缓存 异构计算
FPGA入门(7):IP核调用(二)
FPGA入门(7):IP核调用(二)
6 0
|
4天前
|
异构计算
FPGA入门(7):IP核调用(一)
FPGA入门(7):IP核调用(一)
14 0
|
4天前
|
异构计算
FPGA入门(6):数码管静态/动态显示(一)
FPGA入门(6):数码管静态/动态显示
10 0
|
4天前
|
异构计算
FPGA入门(5):控制LED灯
FPGA入门(5):控制LED灯
8 0
|
4天前
|
异构计算
FPGA入门(4):时序逻辑(二)
FPGA入门(4):时序逻辑(二)
12 0
|
4天前
|
存储 异构计算
FPGA入门(3):组合逻辑
FPGA入门(3):组合逻辑
6 0
|
4天前
|
算法 异构计算
FPGA入门(2):Verilog HDL基础语法
FPGA入门(2):Verilog HDL基础语法
8 0
|
2月前
|
机器学习/深度学习 算法 异构计算
m基于FPGA的多通道FIR滤波器verilog实现,包含testbench测试文件
本文介绍了使用VIVADO 2019.2仿真的多通道FIR滤波器设计。展示了系统RTL结构图,并简述了FIR滤波器的基本理论,包括单通道和多通道的概念、常见结构及设计方法,如窗函数法、频率采样法、优化算法和机器学习方法。此外,还提供了Verilog核心程序代码,用于实现4通道滤波器模块,包含时钟、复位信号及输入输出接口的定义。
81 7
|
2月前
|
算法 异构计算
m基于FPGA的电子钟verilog实现,可设置闹钟,包含testbench测试文件
该文介绍了基于FPGA的电子钟设计,利用Vivado2019.2平台进行开发并展示测试结果。电子钟设计采用Verilog硬件描述语言,核心包括振荡器、分频器和计数器。时间显示为2个十进制格式,闹钟功能通过存储器和比较器实现,当当前时间等于设定时间时触发。文中给出了Verilog核心程序示例,展示了时钟信号、设置信号及输出的交互。
127 2

热门文章

最新文章