[Verilog22]HDLBits习题_Latches and Flip-Flops

HDLBits习题讲解_Latches and Flip-Flops

D触发器

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

D flip-flop

  • 题目要求,创建一个D触发器。代码如下:
module top_module (
    input clk,    
    input d,
    output reg q );

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

endmodule

D flip-flops

  • 题目要求,创建8个D触发器,每个都由时钟的上升沿触发。代码如下:
module top_module (
    input clk,
    input [7:0] d,
    output [7:0] q
);

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

同步复位与异步复位

  1. 同步复位:指复位信号只有在时钟上升沿到来时,才能有效。否则,无法完成对系统的复位工作。用Verilog描述如下:
always @ (posedge clk) begin
	if (!Rst_n)
		...
end
  1. 异步复位:指无论时钟沿是否到来,只要复位信号有效,就对系统进行复位。用Verilog描述如下:
always @ (posedge clk or negedge Rst_n) begin
	if (!Rst_n)
		...
end

DFF with reset

  • 题目要求,创建8个d触发器,且支持外部高有效同步复位。所有D触发器由clk的上升沿触发。代码如下:
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

DFF with reset value

  • 题目要求,创建8个d触发器,且支持外部高有效同步复位,复位值为0x34。所有D触发器由clk的下降沿触发。代码如下:
module top_module (
    input clk,
    input reset,            // Synchronous reset
    input [7:0] d,
    output [7:0] q
);

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

DFF with asynchronous reset

  • 题目要求,创建8个d触发器,且支持外部高有效异步复位。所有D触发器由clk的上升沿触发。代码如下:
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),否则会报错;同理,当使用negedge areset时,只能使用if(!areset)进行判断而不能用if(areset),否则会报错。

DFF with byte enable

  • 创建一个16D触发器,有时我们仅需要修改部分触发器中的值。字节使能信号控制当前时钟周期中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 begin
            q <= 16'd0;
        end
    end
    
endmodule

D触发器练习题

D Latch

  • 题目要求,实现上图所示电路(锁存器)。代码如下:
module top_module (
    input d, 
    input ena,
    output q);
    
    always@(*)begin
        if(ena)begin
            q = d;
        end
    end
 
endmodule

DFF(1)

  • 题目要求,实现上图所示电路(异步复位的D触发器)。代码如下:
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

DFF(2)

  • 题目要求,实现上图所示电路(同步复位的D触发器)。代码如下:
module top_module (
    input clk,
    input d, 
    input r,   // synchronous reset
    output q);

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

DFF+gate

  • 题目要求,实现上图所示电路(异或门与D触发器组合)。代码如下:
module top_module (
    input clk,
    input in, 
    output out);

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

Mux and DFF(1)

  • 假设要为这个电路实现分层的Verilog代码,使用一个子模块的三个实例,该子模块中有一个触发器和多路选择器。为这个子模块编写一个名为top_module的Verilog模块(包含一个触发器和多路选择器)。代码如下:
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

Mux and DFF(2)

  • 为上图所示的n-bit移位寄存器的一个阶段编写一个Verilog模块顶层模块,包括触发器和多路选择器。代码如下:
module top_module (
    input clk,
    input w, R, E, L,
    output Q
);

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

endmodule

DFFs and gates

  • 题目要求,编写代码,实现如图所示的有限状态机电路,假设D触发器在机器开始之前被初始重置为零。代码如下:
module top_module (
    input clk,
    input x,
    output z
); 

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

JK触发器

Create circuit from truth table

  • JK触发器有下面的真值表。只使用D触发器和逻辑门实现JK触发器。注:Qold是时钟上升沿前的D触发器的输出。
J K Q
0 0 Qold
0 1 0
1 0 1
1 1 ~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;
        endcase
    end
    
endmodule

脉冲边沿检测

  • 脉冲边沿的特性:两侧电平发生了变化。若检测的是下降沿,那就是高电平变低电平。若检测的是上升沿,那就是低电平变高电平。
  • 若需要检测脉冲边沿,只需将前后进来的信号做异或运算,即两个电平不相同则是发生边沿。具体思路如下:
  1. 设计寄存器{先进reg,后进reg}用来接收被检测的信号;
  2. {先进reg,后进reg}=2'b10,则是下降沿;
  3. {先进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

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

Detect an edge

  • 如上示意图所示,对于8位向量中的每一位,检测输入信号何时从一个时钟周期的0变化到下一个时钟周期的1(正边缘检测)。输出pedge位在发生0到1转换后的下一个时钟周期输出一个单位脉冲。代码如下:
module top_module (
    input clk,
    input [7:0] in,
    output [7:0] pedge
);

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

Detect both edges

  • 如上示意图所示,对于8位向量中的每一位,检测输入信号何时从一个时钟周期变化到下一个时钟周期(正负边缘检测)。输出pedge位在发生转换后的下一个时钟周期输出一个单位脉冲。代码如下:
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

Edge capture register

  • 对于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] in_r;
    always @(posedge clk) begin
        in_r <= in;
    end
    always @(posedge clk) begin
        if(reset)begin
            out<=32'b0;
        end
        else begin
            out<=(in_r & ~in) | out;
        end
    end
     
endmodule

Dual-edge triggered flip-flop

  • 一般而言,我们通常会构件一个上升沿触发或是下降沿触发的触发器。但在fpga中,并不存在双边沿触发的触发器,__ 即不可以使用always @(posedge clk or negedge clk),该代码无法综合 __。题目要求,编写代码,构件一个如上图所示的时钟双边沿触发器。代码如下:
module top_module (
    input clk,
    input d,
    output q
);
    reg q_pos , q_neg;
    always@(posedge clk)begin
        q_pos <= d; 
    end
    always@(negedge clk)begin
        q_neg <= d; 
    end
    assign q = clk?  q_pos : q_neg;
endmodule

参考:
HDLBits
HDLBits答案(10)_D触发器、同步与异步复位、脉冲边沿检测