组合逻辑&时序逻辑
- 波形图中,表达时序逻辑时如果时钟和数据是对齐的,则默认当前时钟沿采集到的数据位在该时钟上升沿前一时刻的值。表达组合逻辑时如果时钟和数据是对齐的,则默认当前时钟沿采集到的数据为该始终上升沿同一时刻的值。
- 组合逻辑和时序逻辑的区别:
主要是看数据工作是不是在时钟沿下进行的。在 FPGA 的设计中,复杂的电路设计都要用到时序逻辑电路,往往都是以时序逻辑电路为主,组合逻辑为辅的混合逻辑电路。
- 组合逻辑会存在险竞争冒险,会引起电路的不稳定性和工作时的不确定性
- 时序逻辑最基本的单元就是寄存器,寄存器一般由D触发器构成
- 时序电路“延一拍”,key_in为高,led_out延迟一拍才变高
寄存器、触发器、锁存器辨析
-
寄存器(register):用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果,它被广泛的用于各类数字系统和计算机中。其实寄存器就是一种常用的时序逻辑电路,但这种时序逻辑电路只包含存储电路。寄存器的存储电路是由锁存器或触发器构成的,因为一个锁存器或触发器能存储1位二进制数,所以由N个锁存器或触发器可以构成N位寄存器。 工程中的寄存器一般按计算机中字节的位数设计,所以一般有8位寄存器、16位寄存器等 ;
-
寄存器的应用
a)可以完成数据的并串、串并转换;
b)可以用做显示数据锁存器:许多设备需要显示计数器的记数值,以8421BCD码记数,以七段显示器显示,如果记数速度较高,人眼则无法辨认迅速变化的显示字符。在计数器和译码器之间加入一个锁存器,控制数据的显示时间是常用的方法。
c)用作缓冲器;
d)组成计数器:移位寄存器可以组成移位型计数器,如环形或扭环形计数器。
https://blog.csdn.net/bleauchat/article/details/85312172
D触发器(D Flip Flop,DFF)
- D触发器的功能:
1、在一个脉冲信号上升沿或下降沿的作用下,将信号从输入端D送到输出端Q,如果时钟脉冲的边沿信号未出现,即使输入信号改变,输出信号仍然保持原值。
2、寄存器拥有复位清零功能,复位分为同步复位和异步复位
3、能够存储一位二进制码
- D触发器分为两种,一种是同步复位D触发器,一种是异步触发D触发器。
- 电路符号
- 真值表
同步复位D触发器
- 同步复位D触发器,复位与时钟同步。即时钟上升沿到来时检测到按键的复位操作才有效
- 代码实现
//同步复位D触发器
module filp_flop (
input wire sys_clk,sys_rst_n,
input wire key_in,
output reg led_out
);
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin //rst低电平时复位,并且大前提是clk为上升沿。
led_out <= 1'b0; //sys_clk上升沿到来时,若检测到rst为低电平时复位有效
end else begin
led_out <= key_in;
end
end
endmodule
异步复位D触发器
- 异步复位触发器,复位与时钟不同步。寄存器不关心时钟上升沿来不来,只要有按键按下,就立即执行复位操作。但是复位释放时仍需要等到时钟上升沿才能检测到key_in的值 并给led_out
- 代码实现
module dff ( //异步复位
input wire sys_clk,sys_rst_n,
input wire key_in,
output reg led_out
);
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0) begin //rst低电平时复位,但是大前提不需要clk为一定上升沿。
led_out <= 1'b0; //检测到 sys_rst_n 的下/降沿时立刻复位,不需等待 sys_clk 的上升沿来到后再复位
end else begin
led_out <= key_in;
end
end
endmodule
标准锁存器
- 电路符号
// ===================================
// Description:
// Verilog module for general latch
//====================================
module sirv_gnrl_ltch # (
parameter DW = 32
) (
//input test_mode,
input lden,
input [DW-1:0] dnxt,
output [DW-1:0] qout
);
reg [DW-1:0] qout_r;
always @ *
begin : LTCH_PROC
if (lden == 1'b1)
qout_r <= dnxt;
end
assign qout = qout_r;
endmodule
标准DFF模块大合集
为什么要使用标准DFF模块
- 寄存器是数字同步电路中最基本的单元。使用 Verilog 进行数字电路设计时,最常见的
方式是使用 always 块语法生成寄存器。蜂鸟 E200 处理器核推荐如下原则,本原则来自于严
谨的工业级开发标准,其要点如下。
对于寄存器避免直接使用 always 块编写,而是应该采用模块化的标准 DFF 模块进
行例化。示例如下所示,一个名为 flg_dfflr 的寄存器,除了时钟(clk)和复位信号(rst_n)
之外,还带有使能信号 flg_ena 和输入(flg_nxt)/输出信号(flg_r)。
wire flg_r;
wire flg_nxt = ~flg_r;
wire flg_ena = (ptr_r == ('E203_OITF_DEPTH-1)) & ptr_ena;
//此处使用例化 sirv_gnrl_dfflr 的方式实现寄存器,而不是使用显示的 always 块
sirv_gnrl_dfflr #(1) flg_dfflrs(flg_ena, flg_nxt, flg_r, clk, rst_n);
使用标准 DFF 模块例化的好处包括以下内容。
• 便于全局替换寄存器类型。
• 便于在寄存器中全局插入延迟。
• 明确的 load-enable 使能信号(如下例的 flg_ena)方便综合工具自动插入寄存器级别
的门控时钟以降低动态功耗(参见第 15.1.5 节了解更多此低功耗设计的信息)。
• 便于规避 Verilog 语法 if-else 不能传播不定态的问题,有关此内容可以参考第 5.3.2 节。
标准DFF代码实现
sirv_gnrl_dfflrs //带有 load-enable 使能,带有异步 reset,复位默认值为 1 的寄存器
sirv_gnrl_dfflr //带有 load-enable 使能,带有异步 reset,复位默认值为 0 的寄存器
sirv_gnrl_dffl //带有 load-enable 使能,不带有 reset 的寄存器
sirv_gnrl_dffrs //不带有 load-enable 使能,带有异步 reset,复位默认值为 1 的寄存器
sirv_gnrl_dffr //不带有 load-enable 使能,带有异步 reset,复位默认值为 0 的寄存器
sirv_gnrl_ltch //Latch 锁存器模块
sirv_gnrl_dfflrs, load-enable使能/ 异步reset/ 复位值为1
// =========================================
// Description:
// Verilog module gnrl DFF with Load-enable and Reset
// Default reset value is 1
// =========================================
module gnrl_dfflrs #(
parameter DW = 8
) (
input lden,
input [DW-1:0] dnxt,
output [DW-1:0] qout,
input clk,
input rst_n
);
reg [DW-1:0] qout_r ;
always @(posedge clk or negedge rst_n)
begin: DFFLRS_PORC
if(rst_n == 1'b0) begin
qout_r <= {DW{1'b1}}; // Default reset value is 1
end
else if (lden == 1'b1) begin
qout_r <= #1 dnxt;
end
end
assign qout = qout_r;
endmodule
sirv_gnrl_dfflr,load-enable使能/ 异步reset/ 复位值为0
// ===========================================================================
// Description:
// Verilog module gnrl DFF with Load-enable and Reset
// Default reset value is 0
// ===========================================================================
module gnrl_dfflr # (
parameter DW = 8
) (
input lden,
input [DW-1:0] dnxt,
output [DW-1:0] qout,
input clk,
input rst_n
);
reg [DW-1:0] qout_r;
always @(posedge clk or negedge rst_n)
begin : DFFLR_PROC
if (rst_n == 1'b0)
qout_r <= {DW{1'b0}}; // Default reset value is 0
else if (lden == 1'b1)
qout_r <= #1 dnxt;
end
assign qout = qout_r;
endmodule
sirv_gnrl_dffl,load-enable使能/ 不带reset
// ===========================================================================
// Description:
// Verilog module gnrl DFF with Load-enable, no reset
// ===========================================================================
module gnrl_dffl # (
parameter DW = 8
) (
input lden,
input [DW-1:0] dnxt,
output [DW-1:0] qout,
input clk
);
reg [DW-1:0] qout_r;
always @(posedge clk)
begin : DFFL_PROC
if (lden == 1'b1) //no reset
qout_r <= #1 dnxt;
end
assign qout = qout_r;
endmodule
sirv_gnrl_dffrs,不带load-enable/ 带异步reset/ 复位值为1
// ===========================================================================
// Description:
// Verilog module gnrl DFF with Reset, no load-enable
// Default reset value is 1
// ===========================================================================
module gnrl_dffrs # (
parameter DW = 8
) (
input [DW-1:0] dnxt,
output [DW-1:0] qout,
input clk,
input rst_n
);
reg [DW-1:0] qout_r;
always @(posedge clk or negedge rst_n)
begin : DFFRS_PROC
if (rst_n == 1'b0)
qout_r <= {DW{1'b1}}; //Default reset value is 1
else //no load-enable
qout_r <= #1 dnxt;
end
assign qout = qout_r;
endmodule
sirv_gnrl_dffr,不带load-enable/ 带异步reset/ 复位值为0
// ===========================================================================
// Description:
// Verilog module gnrl DFF with Reset, no load-enable
// Default reset value is 0
// ===========================================================================
module gnrl_dffr # (
parameter DW = 8
) (
input [DW-1:0] dnxt,
output [DW-1:0] qout,
input clk,
input rst_n
);
reg [DW-1:0] qout_r;
always @(posedge clk or negedge rst_n)
begin : DFFR_PROC
if (rst_n == 1'b0)
qout_r <= {DW{1'b0}}; // Default reset value is 0
else
qout_r <= #1 dnxt;
end
assign qout = qout_r;
endmodule
sirv_gnrl_ltch,Latch锁存器模块
// ===========================================================================
// Description:
// Verilog module for general latch
// ===========================================================================
module sirv_gnrl_ltch # (
parameter DW = 32
) (
//input test_mode,
input lden,
input [DW-1:0] dnxt,
output [DW-1:0] qout
);
reg [DW-1:0] qout_r;
always @ *
begin : LTCH_PROC
if (lden == 1'b1)
qout_r <= dnxt;
end
//assign qout = test_mode ? dnxt : qout_r;
assign qout = qout_r;
`ifndef FPGA_SOURCE//{
`ifndef DISABLE_SV_ASSERTION//{
//synopsys translate_off
always_comb
begin
CHECK_THE_X_VALUE:
assert (lden !== 1'bx)
else $fatal ("\n Error: Oops, detected a X value!!! This should never happen. \n");
end
//synopsys translate_on
`endif//}
`endif//}
endmodule
变体
时钟双沿触发器*
构建一个功能类似于双边沿触发触发器的电路:
知识拓展:
DDR在原有的SDRAM的基础上改进而来,SDRAM在一个CLK周期传输一次数据,而DDR在一个CLK周期传输两次数据,分别在上升沿和下降沿各传输一次数据
- 代码实现:
方法一:
//方法一简单明了,但因为触发器Tc_to_q延时的存在,输出波形会产生glitch(毛刺)
//即p、q的值还没更新,就已经执行了assign q = clk? q1:q2
module top_module (
input clk,
input d,
output q
);
reg pos_q, neg_q;
always@(posedge clk)begin pos_q <= d;end
always@(negedge clk)begin c<= d;end
assign q = clk ? pos_q : neg_q;
endmodule
方法二:
//推荐使用方法二进行双边检测
//方法二相较于方法一少了使用clk信号进行选择,可以避免产生毛刺,但电路也会复杂
module top_module (
input clk,
input d,
output q
);
reg pos_q,neg_q;
always@(posedge clk) begin pos_q <= d ^ neg_q; end
always@(negedge clk) begin neg_q <= d ^ pos_q; end
assign q = pos_q ^ neg_q;
endmodule
验证结果:
带字节使能的D触发器
创建16个D触发器,有时我们仅需要修改部分触发器中的值。字节使能信号控制当前时钟周期中16个寄存器中哪个字节需被修改。byteena[1]控制高字节d[15:8],而byteena[0]控制低字节d[7:0]。
resetn是一个同步,低电平有效的复位信号。
所有的D触发器由时钟的上升沿触发。
代码实现:
module top_module (
input clk,
input resetn,
input [1:0] byteena,
input [15:0] d,
output [15:0] q
);
always@(posedge clk)begin
if(!resetn)begin
q <= 16'd0;
end else begin
if(byteena[1] == 1'b1)begin
q[15:8] <= d[15:8];
end
if(byteena[0] == 1'b1)begin
q[7:0] <= d[7:0];
end
end
end
endmodule
- 验证结果
Synchronous active-low reset
DFF with byte enables
JK触发器
纪念Jack Kilby,德仪工程师,诺贝尔物理学奖获得者,发明第一块IC的人
J-K触发器是时钟边沿敏感的基本存储单元。逻辑电路和逻辑符号如下图所示:文章来源:https://www.toymoban.com/news/detail-774823.html
文章来源地址https://www.toymoban.com/news/detail-774823.html
- 真值表
- 代码
module top_module (
input clk,
input j,
input k,
output Q);
always@(posedge clk) begin
case({j,k})
2'b00: Q <= Q;
2'b01: Q <= 1'b0;
2'b10: Q <= 1'b1;
2'b11: Q <= ~Q;
default: Q <= Q;
endcase
end
endmodule
- 验证结果
参考链接
- 《手把手教你设计CPU.RISC-V处理器》
- 锁存器、触发器和寄存器
- 触发器详解——(二)JK触发器
- 一步一步带你理解DDR基本原理
到了这里,关于【IC设计】时序逻辑的基础—锁存器、触发器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!