D触发器、同步与异步复位、脉冲边沿检测

HDLBits链接


D触发器

定义:

D触发器是一个具有记忆功能的,具有两个稳定状态的信息存储器件,触发器具有两个稳定状态,即”0”和”1”,在一定的外界信号作用下,可以从一个稳定状态翻转到另一个稳定状态。在这里解释边沿触发的D触发器,D触发器在时钟脉冲CP的前沿(正跳变0→1)发生翻转,触发器的次态(下一个状态)取决于CP的脉冲上升沿到来之前D端的状态,即次态Q=D。因此,它具有置0、置1两种功能。由于在CP=1期间电路具有维持阻塞作用(即触发器的输出不变),所以在CP=1期间,D端的数据状态变化,不会影响触发器的输出状态,故边沿D触发器受干扰的可能性就降低了。

功能表:

D CLK Q QN
0 时钟上升沿 0 1
1 时钟上升沿 1 0
× 0 last Q last QN
× 1 last Q last QN

1


同步复位与异步复位

同步复位:

顾名思义,同步复位就是指复位信号只有在时钟上升沿到来时,才能有效。否则,无法完成对系统的复位工作。用Verilog描述如下:

1
2
3
4
always @ (posedge clk) begin
if (!Rst_n)
...
end

异步复位:

指无论时钟沿是否到来,只要复位信号有效,就对系统进行复位。用Verilog描述如下:

1
2
3
4
always @ (posedge clk or negedge Rst_n) begin
if (!Rst_n)
...
end

D触发器巩固练习

题目描述1

创建一个D触发器。

Solution1

1
2
3
4
5
6
7
8
9
10
module top_module (
input clk,
input d,
output reg q );

always @(posedge clk) begin
q <= d;
end

endmodule

tips:时序的always块使用非阻塞赋值。

题目描述2

创建8个D触发器,每个都由时钟的上升沿触发。

Solution2

1
2
3
4
5
6
7
8
9
10
11
module top_module (
input clk,
input [7:0] d,
output [7:0] q
);

always @(posedge clk) begin
q <= d;
end

endmodule

题目描述3

创建8个d触发器与主动高同步复位。所有D触发器由clk的上升沿触发。

Solution3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module top_module (
input clk,
input reset, // Synchronous reset
input [7:0] d,
output [7:0] q
);

always @(posedge clk) begin
if(reset)
q <=8'd0;
else
q <= d;
end

endmodule

题目描述4

创建8个D触发器与主动高同步复位。触发器必须被重置为0x34,而不是0。所有D触发器应由clk的下降沿触发。

Solution4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module top_module (
input clk,
input reset,
input [7:0] d,
output [7:0] q
);

always @(negedge clk) begin
if(reset)
q <= 8'h0x34;
else
q <= d;
end

endmodule

题目描述5

创建8个D触发器与主动高异步复位。所有D触发器应由clk的上升沿触发。

Solution5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module top_module (
input clk,
input areset, // active high asynchronous reset
input [7:0] d,
output [7:0] q
);

always @(posedge clk or posedge areset) begin
if(areset) begin
q <= 8'd0;
end
else begin
q <= d;
end
end

endmodule

tips:使用posedge areset时,只能使用if(areset)首先进行判断而不能用if(!areset),否则会报错。

题目描述6

创建一个16D触发器,有时我们仅需要修改部分触发器中的值。字节使能信号控制当前时钟周期中16个寄存器中哪个字节需被修改。byteena[1]控制高字节d[15:8],而byteena[0]控制低字节d[7:0]。

resetn是一个同步,低复位信号。

所有的D触发器由时钟的上升沿触发。

Solution6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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])
q[15:8] <= d[15:8];
if(byteena[0])
q[7:0] <= d[7:0];
end
end

endmodule

题目描述7:

实现下面的电路(锁存器)

2

Solution7

1
2
3
4
5
6
7
8
9
10
11
12
module top_module (
input d,
input ena,
output q);

always@(*)begin
if(ena)begin
q = d;
end
end

endmodule

题目描述8:

实现下面的电路

3

Solution8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module top_module (
input clk,
input d,
input ar, // asynchronous reset
output q);

always @(posedge clk or posedge ar) begin
if(ar) begin
q<=1'b0;
end
else begin
q<=d;
end
end

endmodule

题目描述9:

实现下面的电路

4

Solution9:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module top_module (
input clk,
input d,
input r, // synchronous reset
output q);

always @(posedge clk) begin
if(r) begin
q<=1'b0;
end
else begin
q<=d;
end
end

endmodule

题目描述10:

实现下面的电路

5

Solution10:

1
2
3
4
5
6
7
8
9
10
module top_module (
input clk,
input in,
output out);

always @(posedge clk) begin
out <= (in ^ out);
end

endmodule

题目描述11:

考虑下图的电路:

6

假设要为这个电路实现分层的Verilog代码,使用一个子模块的三个实例,该子模块中有一个触发器和多路选择器。为这个子模块编写一个名为top_module的Verilog模块(包含一个触发器和多路选择器)

Solution11

1
2
3
4
5
6
7
8
9
10
11
12
module top_module (
input clk,
input L,
input r_in,
input q_in,
output reg Q);

always @(posedge clk) begin
Q <= L ? r_in : q_in;
end

endmodule

题目描述12:

考虑下图的n-bit移位寄存器

7

为该电路的一个阶段编写一个Verilog模块顶层模块,包括触发器和多路选择器。

Solution12:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module top_module (
input clk,
input w, R, E, L,
output Q
);

always @(posedge clk) begin
case({E,L})
2'b00:Q<=Q;
2'b01:Q<=R;
2'b10:Q<=w;
2'b11:Q<=R;
endcase
end

endmodule

题目描述13:

给定如图所示的有限状态机电路,假设D触发器在机器开始之前被初始重置为零

8

Solution13:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module top_module (
input clk,
input x,
output z
);

reg Q1,Q2,Q3;
always @(posedge clk) begin
Q1 <= (x ^ Q1);
Q2 <= (x & ~Q2);
Q3 <= (x | ~Q3);
end

assign z = ~(Q1|Q2|Q3);

endmodule

题目描述14:

JK触发器有下面的真值表。只使用D触发器和逻辑门实现JK触发器。注:Qold是时钟上升沿前的D触发器的输出。

J K Q
0 0 Qold
0 1 0
1 0 1
1 1 ~Qold

Solution14:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
endcase
end

endmodule

脉冲边沿检测

原理:

脉冲边沿的特性:两侧电平发生了变化

9

若检测的是下降沿,那就是高电平变低电平。

若检测的是上升沿,那就是低电平变高电平。

若检测脉冲边沿,只需将前后进来的信号做异或运算,即两个电平不相同则是发生边沿。

思路:

设计寄存器用来接收被检测的信号;若{先进reg,后进reg}=2'b10,则是下降沿;

{先进reg,后进reg}=2'b01,则为上升沿。

注:使用多个寄存器可以更好的检测边沿,防止干扰脉冲。具体看下例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
rs232_rx0 <= 1'b0;
rs232_rx1 <= 1'b0;
rs232_rx2 <= 1'b0;
rs232_rx3 <= 1'b0;
end
else begin
rs232_rx0 <= rs232_rx;
rs232_rx1 <= rs232_rx0;
rs232_rx2 <= rs232_rx1;
rs232_rx3 <= rs232_rx2;
end
end
//这种方法可以滤除毛刺
assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0;

易分析,信号rs232_rx0rs232_rx1,必须都为0,且信号rs232_rx3rs232_rx2都必须为1,neg_rs232_rx 才会为1。此时判断为下降沿。


边沿检测巩固练习

题目描述1:

对于8位向量中的每一位,检测输入信号何时从一个时钟周期的0变化到下一个时钟周期的1(正边缘检测)。输出位应该在发生0到1转换后的周期,如下示意图所示:

10

Solution1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module top_module (
input clk,
input [7:0] in,
output [7:0] pedge
);

reg [7:0] temp_in;

always @(posedge clk) begin
temp_in <= in;
pedge <= ~temp_in & in;
end

endmodule

题目描述2:

对于8位向量中的每一位,检测输入信号何时从一个时钟周期变化到下一个时钟周期(检测脉冲边沿)。输出位应该在发生转换后的周期。

11

Solution2:

1
2
3
4
5
6
7
8
9
10
11
12
13
module top_module (
input clk,
input [7:0] in,
output [7:0] anyedge
);

reg [7:0] temp_in;
always @(posedge clk) begin
temp_in <= in;
anyedge <= temp_in ^ in;
end

endmodule

题目描述3:

对于32位向量中的每一位,当输入信号从一个时钟周期的1变化到下一个时钟周期的0时捕获(捕捉下降沿),“捕获”意味着输出将保持1直到被reset(同步重置)。

每个输出位的行为就像一个SR触发器:输出位应该在发生1到0转换后的周期被设置(为1)。当复位为高时,输出位应该在正时钟边缘复位(为0)。如果上述两个事件同时发生,则reset具有优先级。

在下面示例波形的最后4个周期中,“reset”事件比“set”事件早一个周期发生,因此这里不存在冲突。

12

Solution3(1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module top_module (
input clk,
input reset,
input [31:0] in,
output [31:0] out
);

reg [31:0] temp_in;
reg [31:0] state;
integer i;

always @(posedge clk) begin
temp_in <= in;
for(i=0;i<32;i++) begin
case({temp_in[i] & ~in[i],reset})
2'b10:out[i]<=1'b1;
2'b11:out[i]<=1'b0;
2'b01:out[i]<=1'b0;
default:out[i]<=out[i];
endcase
end
end

endmodule

Solution3(2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module top_module (
input clk,
input reset,
input [31:0] in,
output [31:0] out
);

reg [31:0] temp_in;

always @(posedge clk) begin
temp_in <= in;
end

always @(posedge clk) begin
if(reset)begin
out<=32'b0;
end
else begin
out<=temp_in & ~in | out;
end
end

endmodule

题目描述4:

时钟双沿触发器

always @(posedge clk or negedge clk)

Solution4(1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module top_module (
input clk,
input d,
output q
);

reg q1,q2;

always @(posedge clk) begin
q1<=d;
end

always @(negedge clk) begin
q2<=d;
end

assign q = clk?q1:q2;

endmodule

Solution4(2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module top_module (
input clk,
input d,
output q
);

reg q1,q2;

always @(posedge clk) begin
q1<= d ^ q2;
end

always @(negedge clk) begin
q2<= d ^ q1;
end

assign q = q1 ^ q2;

endmodule

我们知道任何一个数异或一个数再异或同一个数,将得到本身。这就是最简单的加密与解密原理。

Solution2相较于Solution1少了使用clk信号进行选择,可以避免产生毛刺。推荐使用Solution2进行双边检测。


总结

  • 学习了D触发器与同步异步复位的概念
  • 学会了实际工程中常用的边沿检测电路