Verilog99题——22-30题(时序逻辑基础)

前言

不忘出芯veriloig99题的22-30题是时序逻辑基础相关。

22. 画一个D触发器的电路图。

23. 说明D触发器与Latch的区别。

都是时序逻辑,输出不但同当前的输入相关还同上一状态的输出相关。

1、latch由电平触发,非同步控制。在使能信号有效时latch相当于通路,在使能信号无效时latch保持输出状态。DFF由时钟沿触发,同步控制。

2、latch容易产生毛刺(glitch),DFF则不易产生毛刺。

3、如果使用门电路来搭建latch和DFF,则latch消耗的门资源比DFF要少,这是latch比DFF优越的地方。所以,在ASIC中使用 latch的集成度比DFF高,但在FPGA中正好相反,因为FPGA中没有标准的latch单元,但有DFF单元,一个LATCH需要多个LE才能实现。

4、latch将静态时序分析变得极为复杂。

参考链接

D锁存器和D触发器的输出波形图

上图所示为D锁存器和D触发器输出端随输入信号变化的波形图( Waveform diagram)。由图中可见,输入信号D在tat_a时刻以前,D锁存器和D触发器输出端随输入信号变化的波形相同;在t时刻,CP=1期间D发生了变化,D锁存器的输出随着输入信号的变化而变化,D触发器则是在下一个时钟到来后输出状态才发生改变。这是由于触发器不具有传输透明性(Transparency),即触发器输入端发生变化并不会同步引起其输出端发生变化。触发器输出端的变化仅受控制输入(时钟)信号或异步置位复位信号的控制。在通常情况下,除了输入信号在CP=1期间发生变化以外,锁存器和触发器的输出响应是相同的。

24. 解释一下D触发器的建立时间与保持时间。

建立时间(setup time) 是指在触发器的时钟信号上升沿到来以前,数据稳定不变的最小时间,如果建立时间不够,数据将不能在这个时钟上升沿被打入触发器;

保持时间(hold time) 是指在触发器的时钟信号上升沿到来以后,数据稳定不变的最小时间,如果保持时间不够,数据同样不能被打入触发器。

25. 解释一下Latch的建立时间与保持时间。

建立时间(Setup time)tsut_{su}:锁存器输入信号D到来时刻到输入信号达到稳定所持续的时间。

保持时间(Hold time)tht_{h}:输入信号D达到稳定时刻至输入信号被锁存器接受所持续的时间。

控制脉冲宽度(Control pulse width)twt_{w}:控制时钟(使能)脉冲C1,处于激活状态时的持续时间。

其中,建立时间必须确保数据输入端的任何变化在控制输入信号到来以前能够准确地传送到输入电路:保持时间和控制脉冲宽度必须保证输入信号在产生新状态以前读入当前数据。

传输延迟时间(Propagation delay):是输入信号的时序变化对输出信号的时序影响,定义为从输入信号(D或C)变化时开始,到输出端(Q或QN)出现相应的输出响应所持续的时间。传输延迟时间通常描述为输入端信号发生变化,引起输出端电平从高到低变化时的传输延迟时间tpHLt_{pHL}和输出端电平从低到高变化时的传输延迟时间tpLHt_{pLH}

锁存器时序参数图

26. 用verilog实现1bit信号边沿检测功能,输出一个周期宽度的脉冲信号。

  1. 上升沿
  2. 下降沿
  3. 上升沿或者下降沿

input clk, rst_n, data;

output data_edge;

Verilog描述

module test26(
    input   clk, rst_n, data,
    output  rise_edge,  //上升沿
    output  fall_edge,  //下降沿
    output  data_edge   //边沿
    );

reg data_r,data_rr;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_r  <= 1'b0;
        data_rr <= 1'b0;     
    end else begin
      data_r    <= data;
      data_rr   <= data_r;
    end
end

assign rise_edge = ( ~data_rr && data_r ) ? 1'b1 : 1'b0 ;
assign fall_edge = ( data_rr && ~data_r ) ? 1'b1 : 1'b0 ;
assign data_edge = ( data_rr^data_r ) ? 1'b1 : 1'b0 ;
endmodule

仿真图

边沿检测仿真图

27. 用verilog实现一个4bit二进制计数器。

  1. 异步复位
  2. 同步复位
input clk, rst_n; 
output [3:0] o_cnt;
module test27(
    input clk, rst_n,
    output [3:0] o_cnt
    );
    reg [3:0] cnt;
//异步复位
/* always @ (posedge clk or negedge rst_n) begin
    if ( !rst_n )
        cnt <= 4'b0000;
    else if ( cnt == 4'b1111 )
        cnt <= 4'b0000;
    else
        cnt <= cnt + 1'b1;
end */
//同步复位
always @ (posedge clk) begin
    if ( !rst_n )
        cnt <= 4'b0000;
    else if ( cnt == 4'b1111 )
        cnt <= 4'b0000;
    else
        cnt <= cnt + 1'b1;
end

assign o_cnt = cnt;

endmodule

28. 用verilog实现串并转换。

  1. lsb优先
  2. msb优先

input clk, rst_n, data_i;

output [7:0] data_o;

Verilog描述

module test28(
    input clk, rst_n, data_i,
    output [7:0] data_o
    );
    reg [7:0] data_r;
    reg [2:0] cnt;
//lsb
always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    data_r <= 8'd0;
    cnt <= 3'b0;
  end else begin
    data_r <= {data_r[6:0],data_i};
    cnt <= cnt + 1'b1;
  end
end
//msb
/* always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    data_r <= 8'd0;
    cnt <= 3'b0;
  end else begin
    data_r <= {data_i,data_r[7:1]};
    cnt <= cnt + 1'b1;
  end
end */

assign data_o = (cnt == 3'd0) ? data_r : data_o;
endmodule

testbench

module tb28(    );
reg clk, rst_n, data_i;
wire [7:0] data_o;
  
  initial begin
    clk = 1;
    forever #10 clk = ~clk;
  end
  
  initial begin
    rst_n = 1'b0;
    #22 rst_n = 1'b1;
  end

  initial begin
    repeat(100)begin
      @(negedge clk)
	data_i = {$random};
    end
    $finish;
  end
  
  initial begin
    $dumpfile("tb28.vcd");
    $dumpvars();
  end
  
test28 test28(
    .clk     ( clk    ), 
    .rst_n   ( rst_n  ), 
    .data_i  ( data_i ),
    .data_o  ( data_o )
    );
endmodule

仿真图

lsb优先:

lsb优先

msb优先:

msb优先

上下两幅图结果正好相反,进一步验证了正确性。

29. 序列检测器:有“101”序列输入时输出为1,其他输入情况下,输出为0。画出状态转移图,并用verilog描述。

input clk, rst_n, data;

output flag_101;

状态转移图

状态转移

verilog描述

module test29(
input clk, rst_n, data,
output reg flag_101
    );
parameter   ST0 = 4'b0001,
            ST1 = 4'b0010,
            ST2 = 4'b0100,
            ST3 = 4'b1000;

reg [3:0] c_st;
reg [3:0] n_st;
//FSM-1
always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    c_st <= ST0;
  end else begin
    c_st <= n_st;
  end
end
//FSM-2
always @(*) begin
  case (c_st)
    ST0: n_st = data ? ST1 : ST0;
    ST1: n_st = data ? ST1 : ST2;
    ST2: n_st = data ? ST3 : ST0;
    ST3: n_st = data ? ST1 : ST2;
    default: n_st = ST0;
  endcase
end
//FSM-3
always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    flag_101 <= 1'b0;
  end else begin
    case (n_st)
      ST3: flag_101 <= 1'b1;
      ST0,ST1,ST2: flag_101 <= 1'b0;
      default: flag_101 <= 1'b0;
    endcase
  end
end
endmodule

testbench

module tb29(    );
reg     clk, rst_n, data;
wire    flag_101;

  initial begin
    clk = 0;
    forever #10 clk = ~clk;
  end
  
  initial begin
    rst_n = 1'b0;
    #22 rst_n = 1'b1;
  end

  initial begin
    repeat(100)begin
      @(negedge clk)
	      data = {$random};
    end
    $finish;
  end
  
/*   initial begin
    $dumpfile("seq101_tb.vcd");
    $dumpvars();
  end */

test29 test29(
    .clk        (clk     ), 
    .rst_n      (rst_n   ), 
    .data       (data    ),
    .flag_101   (flag_101)
    );
endmodule

仿真结果

全加器计数

30. 用verilog实现两路数据的乘法运算,要求只使用1个乘法器。

input clk, rst_n;

input sel_x;

input [7:0] da_x_y;

input [7:0] db_x_y;

output reg [15:0] dout_x_y;

verilog描述

module test30(
    input              clk             ,
    input              rst_n           ,
    input              sel_x           ,
    input       [ 7:0] da_x            , 
    input       [ 7:0] da_y            , 
    input       [ 7:0] db_x            , 
    input       [ 7:0] db_y            , 
    output  reg [15:0] dout_x_y
    );
    wire    [ 7:0] da;
    wire    [ 7:0] db;
    wire    [15:0] dout;
assign da = sel_x ? da_x : da_y;
assign db = sel_x ? db_y : db_x;
assign dout = da * db ;

always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    dout_x_y <= 16'd0;
  end else begin
    dout_x_y <= dout;
  end
end
endmodule

即将入行的数字IC设计工程师