HDLBits第十章练习及答案

本文深入讲解了时序逻辑电路的基本组成单元——锁存器和触发器,并详细介绍了D触发器的不同类型及其应用,包括同步与异步复位、边沿检测等关键技术。此外,还探讨了如何使用Verilog语言来实现这些电路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、D触发器

定义:

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

DCLKQQN
0时钟上升沿01
1时钟下降沿10
x0last Qlast QN
x1last Qlast QN

在这里插入图片描述
同步复位与异步复位

(1) 同步复位:

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

always @ (posedge clk) begin
	if (!Rst_n)
		...
end

(2) 异步复位:

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

always @ (posedge clk or negedge Rst_n) begin
	if (!Rst_n)
		...
end

练习:

创建单个 D 触发器。
注:时序的always块常使用非阻塞赋值。
在这里插入图片描述
代码实现:

module top_module (
    input clk,    
    input d,
    output reg q );

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

验证结果:
在这里插入图片描述

2、8个D触发器

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

代码实现:

module top_module (
    input clk,
    input [7:0] d,
    output [7:0] q
);
    always@(posedge clk) begin
        q <= d;
    end

endmodule

验证结果:
在这里插入图片描述

3、带同步复位的D触发器(上升沿触发)

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

代码实现:

module top_module (
    input clk,
    input reset,            
    input [7:0] d,
    output [7:0] q
);
    
    always@(posedge clk) begin
        if(!reset)
            q <= d;
    	else
            q <= 8'b0;
    end

endmodule

验证结果:
在这里插入图片描述

4、带同步复位的D触发器(下降沿触发)

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

代码实现:

module top_module (
    input clk,
    input reset,
    input [7:0] d,
    output [7:0] q
);
    always@(negedge clk) begin
        if(!reset)
            q <= d;
        else
            q <= 8'h0x34;
    end
    
endmodule

验证结果:
在这里插入图片描述

5、带异步复位的D触发器

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

代码实现:

module top_module (
    input clk,
    input areset,   
    input [7:0] d,
    output [7:0] q
);
    
    always@(posedge clk or posedge areset) begin
        if(areset) 
            q <= 8'b0;
        else 
            q <= d;
    end
        
endmodule

验证结果:
在这里插入图片描述

6、带字节使能的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
            if(byteena[1])
                q[15:8] <= d[15:8];
            else
                q[15:8] <= q[15:8];
 
            if(byteena[0])
                q[7:0] <= d[7:0];
            else
                q[7:0] <= q[7:0];  
        end
        
        else 
            q <= 16'b0;
    end

endmodule

验证结果:
在这里插入图片描述
在这里插入图片描述

7、D锁存器

实现下面的电路(锁存器)。
在这里插入图片描述
代码实现:

module top_module (
    input d, 
    input ena,
    output q);
    
    always@(*) begin
    if(ena)
      q = d;
    else
      q = q;
    end

endmodule

验证结果:
在这里插入图片描述

8、D触发器1

实现下面的电路。
在这里插入图片描述
代码实现:

module top_module (
    input clk,
    input d, 
    input ar,   // asynchronous reset
    output q);
    
    always@(posedge clk or posedge ar) begin
        if(ar) 
            q <= 1'b0;
        else 
            q <= d;   
    end        
    
endmodule

验证结果:
在这里插入图片描述

9、D触发器2

实现下面的电路:
在这里插入图片描述
代码实现:

module top_module (
    input clk,
    input d, 
    input r,   // synchronous reset
    output q);
    
    always@(posedge clk) begin
        if(!r)
            q <= d;
        else
            q <= 1'b0;
    end

endmodule

验证结果:
在这里插入图片描述

10、D触发器+门电路

实现下面的电路:
在这里插入图片描述
代码实现:

module top_module (
    input clk,
    input in, 
    output out);
    
    always@(posedge clk) begin
        out <= in ^ out ;
    end
        
endmodule

验证结果:
在这里插入图片描述

11、多路选择器+D触发器1

考虑下面的时序电路:
在这里插入图片描述
假设要为这个电路实现分层的Verilog代码,使用一个子模块的三个实例,该子模块中有一个触发器和多路选择器。为这个子模块编写一个名为top_module的Verilog模块(包含一个触发器和多路选择器)。

代码实现:

方法一:if-else 语句实现

module top_module (
	input clk,
	input L,
	input r_in,
	input q_in,
	output reg Q);
    
    always@(posedge clk) begin
        if(L)
            Q <= r_in;
        else
            Q <= q_in;     
    end

endmodule

方法二:a?b:c 语句实现

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、多路选择器+D触发器2

考虑下图的n-bit移位寄存器:
在这里插入图片描述
为该电路的一个阶段编写一个名为 top_module 的 Verilog 模块,包括触发器和多路复用器。

代码实现:

方法一:

module top_module (
    input clk,
    input w, R, E, L,
    output Q
);

    always@(posedge clk) begin
        Q <= L ? R: ( E ? w : Q);
    end
        
endmodule

方法二:

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;
         default: Q <= Q;
        endcase
    end
        
endmodule

验证结果:
在这里插入图片描述

13、多个D触发器+门电路

给定如图所示的有限状态机电路,假设D触发器在机器开始之前被初始重置为零。
在这里插入图片描述
代码实现:

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触发器的输出。

JKQ
00Qold
010
101
11~Qold

代码实现:

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

验证结果:
在这里插入图片描述

15、脉冲边沿检测1

脉冲边沿检测原理

脉冲边沿的特性:两侧电平发生了变化。
在这里插入图片描述
若检测的是下降沿,那就是高电平变低电平。

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

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

思路:

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

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

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

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
//这种方法可以滤除20-40ns的毛刺
assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0;

易分析,后进信号rs232_rx0,rs232_rx1,必须都为0,且先进信号rs232_rx3 ,rs232_rx2都必须为1,neg_rs232_rx 才会为1。则此时判断为下降沿。

练习:

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

module top_module (
    input clk,
    input [7:0] in,
    output [7:0] pedge
);

    reg [7:0] temp;
    always@(posedge clk) begin
        temp <= in;
        pedge <= in & ~temp;
    end
    
endmodule

验证结果:
在这里插入图片描述

16、脉冲边沿检测2

对于8位向量中的每一位,检测输入信号何时从一个时钟周期变化到下一个时钟周期(检测脉冲边沿)。输出位应该在发生转换后的周期。
在这里插入图片描述
代码实现:

module top_module (
    input clk,
    input [7:0] in,
    output [7:0] anyedge
);
    reg [7:0] temp;
    always@(posedge clk) begin
        temp <= in;
        anyedge <= in ^ temp;
    end

endmodule

验证结果:
在这里插入图片描述

17、边缘捕捉寄存器

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

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

在下面示例波形的最后4个周期中,“reset”事件比“set”事件早一个周期发生,因此这里不存在冲突。
在这里插入图片描述
代码实现:

方法一:

module top_module (
    input clk,
    input reset,
    input [31:0] in,
    output [31:0] out
);
    
    reg [31:0] temp;
    always@(posedge clk) begin
        temp <= in;
        if(reset)
            out <= 32'b0;
        else
            out <= ~in & temp | out;
    end

endmodule

方法二:

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

验证结果:
在这里插入图片描述在这里插入图片描述

18、时钟双沿触发器

构建一个功能类似于双边沿触发触发器的电路:
在这里插入图片描述
代码实现:

方法一:

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

方法二:

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

验证结果:
在这里插入图片描述
方法二相较于方法一少了使用clk信号进行选择,可以避免产生毛刺。推荐使用方法二进行双边检测。

参考资料: D触发器、同步与异步复位、脉冲边沿检测.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值