UVM & RTL/Verilog HDL

[Verilog HDL] CH7 타이밍 제어

Return 2022. 6. 14. 14:50

Introduce 

Verilog에서는 다양한 행위 수준의 타이밍 제어구조를 사용할 수 있다. 

  • 지연 기반 타이밍 제어(delay-based timing control) 
  • 사건 기반 타이밍 제어(event-based timing control)
  • 준위-구동 타이밍 제어(level-sensitive timing control)

지연 기반 타이밍 제어

수식문 안에서 지연 기반 타이밍 제어는 문장을 만나고 그것이 수행되는 사이의 시간 지연을 지정한다. 지연은 기호 #에 의해서 지정된다. 절차적 할당을 위한 지연에는 3가지 형태가 있다. 

  • 정규 지연 제어 
  • 내부 할당 지연 제어 
  • 제로 지연 제어 

정규지연 제어 

절차적 할당의 왼쪽에 0값이 아닌 지연이 지정될때 사용한다. 사용법은 다음과 같다. 

parameter latency = 20;
parameter delta = 2;

// 레지스터 변수 정의 
reg x, y, z, p, q;

initial begin 
    x = 0; // 지연 제어가 없다. 
    #10 y = 1; // 숫자를 가지고 지연 제어를 한다. 
    #latency z = 0; // 식별자를 가지고 지연 제어를 한다. 
    #(latency + delta) p = 1; // 수식을 가지고 지연 제어.

    #y x = x +1; // 식별자를 가지고 지연 제어를 한다. y의 값을 가진다. 
    #(4:5:6) q = 0; // 최소, 전형, 최대 지연값.
end

내부 할당 지연 제어 

지연 제어를 할당의 왼쪽에 지정하는 대신에, 할당 연산자의 오른쪽에 지연을 할당할 수 있다. 이러한 지연 지정은 수행의 흐름을 다른 방식으로 바꾼다. 다음의 예는 내부 할당 지연과 정규 지연의 대조를 보여준다. 

// 레지스터 변수 정의 
reg x, y, z, p, q;

// 내부 할당 지연 
initial begin 
    x = 0; z = 0;
    y = #5 x + z; // x와 z의 값을 단위시간 0에 취해서, x+z를 계산한뒤 5단위 시간을 기다린 후에 y에 값을 할당
end

// 임시 변수와 정규 지연 제어를 사용한 같은 방법 
initial begin 
    x = 0; z = 0;
    temp_xz = x + z;
    #5 y = temp_xz;
end

정규할당지연에서는 단지 해당 문장을 만난 뒤 특정 단위 시간 후 수행되는 반면에 내부 할당 지연 제어는 현재 시간에 x+z의 값을 취해서 임시 변수에 저장한다. 비록 x와 z가 0과 5사이에 변한다고 해도 단위 시간 5에 할당되는 값은 변하지 않는다. 

제로 지연 제어 

다른 always-initial 블록 안의 절차적 할당은 같은 시뮬레이션 기간에 수행한다. 다른 always-initial블록 안의 이 문장의 수행 순서는 무작위이다. 제로 지연 제어는 다른 모든 문장이 이 시뮬레이션 기간에 실행된 후 이 문장이 마지막으로 수행되게 하는 방법이다. (경쟁 상태를 없애기 위해서 사용된다.) 사용법은 다음과 같다. 

initial begin 
    x = 0;
    y = 0;
end

initial begin 
    #0 x = 1;   // 제로 지연 제어 
    #0 y = 1;
end

두번째 initial 블록이 마지막에 수행된다. 그래서 단위시간 0의 끝에서 x,y는 1의 값을 가진다. 하지만 실제로 #0을 사용하는것은 추천하지 않는다. 

사건 기반 타이밍 제어 

사건(event)는 레지스터 또는 넷의 값 변화를 의미한다. 사건은 문장 또는 문장의 블록 구동을 위해 사용될 수 있다. 사건 기반 타이밍 제어는 4가지 형태가 있다. 

  • 정규 사건 제어 
  • 명명된 사건 제어 
  • 사건 OR 제어 
  • 준위-구동 타이밍 제어 

정규 사건 제어 

@ 기호는 사건 제어를 지정하는데 사용된다. 문장은 신호값 또는 신호값의 양 또는 음으로 변할때 수행된다. 다음 예제는 키워드 posedge는 클럭이 상승시에 사용된다. 

@(clock) q = d; // q = d는 신호 클럭이 별할 때마다 수행.
@(posedge clock) q = d; // q = d는 신호 클럭이 상승 변환을 할 때마다 수행.
q = @(posedge clock) d; // 클럭의 상승 엣지에서 현재 d의 값을 구하고 q로 할당.

명명된 사건 제어 

Verilog는 사건을 선언하는 것을 제공하고 그 사건이 일어나는 경우 구동되고 인정된다. 사건은 어떤 데이터도 가지고 있을 수 없다. 명명된 사건은 키워드 event에 의해 선언되고,  ->에 의해 구동된다. 사건의 구동은 @기호에 의해 인정된다. 

// 데이터의 마지막 패킷이 도착한 후에 데이터 버파가 데이터에 저장되는 얘제 
event received_data ; // received_data라는 사건을 정의.

always @(posedge clock) begin 
    if(last_data_packet)    // 만약 마지막 데이터 패킷이면
        -> received_data;   // 사건 received_data를 구동한다. 
end

always @(received_data) begin   // 사건 received_data가 구동되기를 기다린다.
                                // 사건이 구동되면 데이터 버퍼의 받은 모든 데이터를 버퍼를 연결연산자{}를 통해 저장한다.
    data_buf = {data_pkt[0],data_pkt[1]};
end

사건 OR 제어 

때때로 여러 신호와 사건 중 하나의 신호라도 변하면, 문장이나 문장 블록이 실행될 수 있다. 이것은 사건이나 신호의 OR로 표현한다. OR로 표현된 사건이나 신호의 목록을 감지목록(senseitivity list)라 한다. 다음은 키워드 or가 여러 개의 구동을 지정하는데 사용된다. 

// 비동기 리셋을 가진 준위-구동 래치 
always @(rest or clock or d) begin 
    if(reset) q = 1'b0; 
    else if(clock) q = d;
end

조합 논리 블록의 입력 변수가 매우 많으면 감지 목록들을 쓰기가 매우 복잡해 진다. 게다가, 입력 변수의 값을 잘못 입력하면, 예상치못한 오류를 일으킬 수 있다. 이 문제를 해결하기 위해 Verilog HDL은 두개의 특별한 기호를 도입한다. @*, @(*)

두 기호 모두 동일한 동작을 한다. 이 특별한 기호 뒤에 따라오는 명령문 그룹에서 어떠한 신호의 변화도 감지해낸다. 

// 조합 논리 블록은 복잡한 입력에 or 연산자를 사용한다. 
// 이런 방식은 실수를 하기 쉽다. 
always @(a or b or c or d or e or f) begin
    out1 = a ? b : c;
    out2 = d ? e : f;
end

// 위 방법을 대신해서 @(*)기호를 사용할 수 있다. 
always @(*) begin
    out1 = a ? b : c;
    out2 = d ? e : f;
end

준위 - 구동 타이밍 제어 

사건 제어는 신호값이 바뀌거나 사건이 구동되기를 기다린다는 것을 볼 수 있었다. @기호는 엣지-구동 제어를 제공한다. Verilog는 문장 또는 문장의 블록이 수행괴기 전, 특정 상태가 참이 되기를 기다리는 준위-구동 타이밍 제어를 제공한다. 키우더 wait는 준위-구동 제어를 위해 쓰인다. 

always 
    wait (count_enable) #20 count = count +1 ;

위의 예에서 count_enable의 값은 연속적으로 모니터 된다. 만약 count_enable이 0이면, 문장이 수행되지않고, 1이면 cout = count +1은 20 단위시간 후 수행된다. 즉, count_enable이 1값을 유지하면, 매 20단위시간마다 count값이 증가한다.