산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Industrial programming >> verilog

Verilog 지연 제어 이해: 타이밍 기법과 모범 사례

Verilog에는 두 가지 타이밍 제어 유형이 있습니다: 지연(delay)과 이벤트(event) 표현식. 지연 제어는 시뮬레이터가 문을 만나고 실제로 실행하는 사이에 지연을 추가하는 방법일 뿐입니다. 이벤트 표현식은 넷이나 변수의 값 변경(암시적 이벤트)이나 다른 프로시저에서 트리거되는 명시적 명명 이벤트 같은 시뮬레이션 이벤트 발생까지 문을 지연시킵니다.

시뮬레이션 시간은 다음 방법들로 진행될 수 있습니다.

내부 지연을 가지도록 모델링된 게이트와 넷도 시뮬레이션 시간을 진행시킵니다.

지연 제어 (Delay Control)

지연 표현식이 알 수 없는 값이나 하이 임피던스 값으로 평가되면 0 지연으로 해석됩니다. 음수 값으로 평가되면 time 변수와 같은 크기의 2의 보수 부호 없는 정수로 해석됩니다.

`timescale 1ns/1ps
module tb;
  reg [3:0] a, b;
  initial begin
    {a, b} <= 0;
    $display ("T=%0t a=%0d b=%0d", $realtime, a, b);
    #10; a <= $random; $display ("T=%0t a=%0d b=%0d", $realtime, a, b);
    #10 b <= $random; $display ("T=%0t a=%0d b=%0d", $realtime, a, b);
    #(a) $display ("T=%0t After a delay of a=%0d units", $realtime, a);
    #(a+b) $display ("T=%0t After a delay of a=%0d + b=%0d = %0d units", $realtime, a, b, a+b);
    #((a+b)*10ps) $display ("T=%0t After a delay of %0d * 10ps", $realtime, a+b);
    #(b-a) $display ("T=%0t Expr evaluates to a negative delay", $realtime);
    #('h10) $display ("T=%0t Delay in hex", $realtime);
    a = 'hX; #(a) $display ("T=%0t Delay is unknown, taken as zero a=%h", $realtime, a);
    a = 'hZ; #(a) $display ("T=%0t Delay is in high impedance, taken as zero a=%h", $realtime, a);
    #1ps $display ("T=%0t Delay of 10ps", $realtime);
  end
endmodule

timescale 정밀도가 1ps이므로, 지연 표현식 (a+b)*10ps 문장에 대해 정밀도 값을 표시하려면 $realtime이 필요합니다.

시뮬레이션 로그

xcelium> run
T=0 a=x b=x
T=10000 a=0 b=0
T=20000 a=4 b=0
T=24000 After a delay of a=4 units
T=29000 After a delay of a=4 + b=1 = 5 units
T=29050 After a delay of 5 * 10ps
T=42050 Expr evaluates to a negative delay
T=58050 Delay in hex
T=58050 Delay is unknown, taken as zero a=x
T=58050 Delay is in high impedance, taken as zero a=z
T=58051 Delay of 10ps
xmsim: *W,RNQUIE: Simulation is complete.

이벤트 제어 (Event Control)

넷과 변수의 값 변경은 다른 절차적 문장의 실행을 트리거하는 동기화 이벤트로 사용될 수 있으며 이는 암시적 이벤트입니다. 이벤트는 변경 방향에 기반할 수도 있는데, 0 방향 변경은 negedge, 1 방향 변경은 posedge가 됩니다.

같은 상태에서 같은 상태로의 전이는 에지로 간주되지 않습니다. posedge나 negedge 같은 에지 이벤트는 벡터 신호나 변수의 LSB에서만 감지할 수 있습니다. 표현식이 같은 결과로 평가되면 이벤트로 간주될 수 없습니다.

module tb;
  reg a, b;
  initial begin
    a <= 0;
    #10 a <= 1;
    #10 b <= 1;
    #10 a <= 0;
    #15 a <= 1;
  end
  // 위 절차적 블록에서 만든 신호 업데이트를 기다리는 다른 절차적 블록
  initial begin
    @(posedge a); $display ("T=%0t Posedge of a detected for 0->1", $time);
    @(posedge b); $display ("T=%0t Posedge of b detected for X->1", $time);
  end
  initial begin
    @(posedge (a + b)) $display ("T=%0t Posedge of a+b", $time);
    @(a) $display ("T=%0t Change in a found", $time);
  end
endmodule

시뮬레이션 로그

ncsim> run
T=10 Posedge of a detected for 0->1
T=20 Posedge of b detected for X->1
T=30 Posedge of a+b
T=45 Change in a found
ncsim: *W,RNQUIE: Simulation is complete.

명명된 이벤트 (Named Events)

event 키워드로 명명된 이벤트를 선언할 수 있으며, 이는 명시적으로 트리거될 수 있습니다. event는 데이터를 담지 않으며, 시간 지속 기간이 없고, 특정 시간에 발생하도록 만들 수 있습니다. 명명된 이벤트는 -> 연산자로 명명된 이벤트 핸들 앞에 붙여서 트리거합니다. 명명된 이벤트는 위에서 설명한 @ 연산자로 기다릴 수 있습니다.

module tb;
  event a_event;
  event b_event[5];
  initial begin
    #20 -> a_event;
    #30; ->a_event;
    #50 ->a_event;
    #10 ->b_event[3];
  end
  always @ (a_event) $display ("T=%0t [always] a_event is triggered", $time);
  initial begin
    #25; @(a_event) $display ("T=%0t [initial] a_event is triggered", $time);
    #10 @(b_event[3]) $display ("T=%0t [initial] b_event is triggered", $time);
  end
endmodule

명명된 이벤트는 동시에 실행되는 두 개 이상의 프로세스를 동기화하는 데 사용할 수 있습니다. 예를 들어, always 블록과 두 번째 initial 블록이 a_event로 동기화됩니다. 이벤트는 배열로 선언할 수 있으며, b_event가 크기 5의 배열이고 인덱스 3이 트리거와 대기 목적으로 사용됩니다.

시뮬레이션 로그

ncsim> run
T=20 [always] a_event is triggered
T=50 [always] a_event is triggered
T=50 [initial] a_event is triggered
T=100 [always] a_event is triggered
T=110 [initial] b_event is triggered
ncsim: *W,RNQUIE: Simulation is complete.

이벤트 or 연산자

or 연산자는 나열된 이벤트 중 하나가 트리거될 때까지 기다리는 데 사용됩니다. 쉼표 ,도 or 연산자 대신 사용할 수 있습니다.

module tb;
  reg a, b;
  initial begin
    $monitor ("T=%0t a=%0d b=%0d", $time, a, b);
    {a, b} <= 0;
    #10 a <= 1;
    #5 b <= 1;
    #5 b <= 0;
  end
  // 이벤트 사이에 "or" 사용
  always @ (posedge a or posedge b) $display ("T=%0t posedge of a or b found", $time);
  // 쉼표 사용
  always @ (posedge a, negedge b) $display ("T=%0t posedge of a or negedge of b found", $time);
  always @ (a, b) $display ("T=%0t Any change on a or b", $time);
endmodule

시뮬레이션 로그

ncsim> run
T=0 posedge of a or negedge of b found
T=0 Any change on a or b
T=0 a=0 b=0
T=10 posedge of a or b found
T=10 posedge of a or negedge of b found
T=10 Any change on a or b
T=10 a=1 b=0
T=15 posedge of a or b found
T=15 Any change on a or b
T=15 a=1 b=1
T=20 posedge of a or negedge of b found
T=20 Any change on a or b
T=20 a=1 b=0
ncsim: *W,RNQUIE: Simulation is complete.

암시적 이벤트 표현식 리스트

민감도 리스트 또는 이벤트 표현식 리스트는 RTL에서 많은 기능적 오류의 흔한 원인입니다. 사용자가 절차적 블록에 새 신호를 도입한 후 민감도 리스트를 업데이트하는 것을 잊을 수 있기 때문입니다.

module tb;
  reg a, b, c, d;
  reg x, y;
  // 이벤트 표현식/민감도 리스트는 @ 연산자 뒤 () 안의
  // 모든 신호로 형성되며, 이 경우 a, b, c, d
  always @ (a, b, c, d) begin
    x = a | b;
    y = c ^ d;
  end
  initial begin
    $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b x=%0b y=%0b", $time, a, b, c, d, x, y);
    {a, b, c, d} <= 0;
    #10 {a, b, c, d} <= $random;
    #10 {a, b, c, d} <= $random;
    #10 {a, b, c, d} <= $random;
  end
endmodule

시뮬레이션 로그

ncsim> run
T=0 a=0 b=0 c=0 d=0 x=0 y=0
T=10 a=0 b=1 c=0 d=0 x=1 y=0
T=20 a=0 b=0 c=0 d=1 x=0 y=1
T=30 a=1 b=0 c=0 d=1 x=1 y=1
ncsim: *W,RNQUIE: Simulation is complete.

사용자가 새 신호 e를 추가하고 그 역을 z로 캡처하기로 결정하면, e도 민감도 리스트에 추가해야 합니다.

module tb;
  reg a, b, c, d, e;
  reg x, y, z;
  // 민감도 리스트에 "e"도 추가
  always @ (a, b, c, d, e) begin
    x = a | b;
    y = c ^ d;
    z = ~e;
  end
  initial begin
    $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
    {a, b, c, d, e} <= 0;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
  end
endmodule

시뮬레이션 로그

ncsim> run
T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1
T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1
T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0
T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0
ncsim: *W,RNQUIE: Simulation is complete.

이제 Verilog는 민감도 리스트를 *로 대체할 수 있게 하여, 문장이 읽는 모든 넷과 변수를 추가함으로써 이러한 문제를 편리하게 해결합니다.

module tb;
  reg a, b, c, d, e;
  reg x, y, z;
  // @* 또는 @(*) 사용
  always @ * begin
    x = a | b;
    y = c ^ d;
    z = ~e;
  end
  initial begin
    $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
    {a, b, c, d, e} <= 0;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
  end
endmodule

시뮬레이션 로그

ncsim> run
T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1
T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1
T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0
T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0
ncsim: *W,RNQUIE: Simulation is complete.

레벨 민감 이벤트 제어 (Level Sensitive Event Control)

절차적 문장의 실행은 조건이 참이 될 때까지 지연될 수 있으며, 이는 wait 키워드로 달성되며 레벨 민감 제어입니다.

wait 문은 조건을 평가하고, 거짓이면 조건이 참이 될 때까지 이후 절차적 문장들이 차단된 상태로 유지됩니다.

module tb;
  reg [3:0] ctr;
  reg clk;
  initial begin
    {ctr, clk} <= 0;
    wait (ctr);
    $display ("T=%0t Counter reached non-zero value 0x%0h", $time, ctr);
    wait (ctr == 4) $display ("T=%0t Counter reached 0x%0h", $time, ctr);
    $finish;
  end
  always #10 clk = ~clk;
  always @ (posedge clk) ctr <= ctr + 1;
endmodule

시뮬레이션 로그

ncsim> run
T=10 Counter reached non-zero value 0x1
T=70 Counter reached 0x4
T=90 Counter reached 0x5
T=170 Counter reached 0x9
Simulation complete via $finish(1) at time 170 NS + 1

verilog

  1. 버릴로그(Verilog) 입문: 디지털 회로 설계를 위한 하드웨어 기술 언어
  2. Verilog 결합(Concatenation) & 복제(Replication) 연산자 실전 가이드
  3. Verilog 할당문 이해: 유형, 문법, 모범 사례
  4. Verilog 차단 vs 비차단 할당: 핵심 차이점 완벽 이해
  5. Verilog 제어문 마스터하기: IF-ELSE, Case, 반복문
  6. Verilog 함수 마스터하기: 효율적인 RTL 설계를 위한 활용법
  7. Verilog 태스크 이해: 함수와의 차이점, 문법, 활용법
  8. Verilog 블록 간/블록 내 지연 이해: 인터/인트라 할당 지연
  9. Verilog 게이트 지연 이해: 신뢰할 수 있는 디지털 설계를 위한 정확한 타이밍
  10. Verilog 클럭 생성기: 설계 및 구현 가이드