#9. Verilog 문법-if 조건문, case문

Verilog/Verilog HDL강의정리(KOCW) 2023. 7. 24. 11:33
반응형

행위수준모델링에서 if문과 case문에 대해서 알아보도록 하겠다.


1. if 조건문

C언어에서의 if문과 거의 비슷한다. 조건을 만족할 경우 문장을 수행한다. 사용예시는

if(expression)
    statement_true;
else
    statement_flase;

형태로 사용하게 된다. 

expression 조건이 참이면 statement_true문을 수행하고, 거짓이면 statement_false문을 사용하게 된다.

statement에는 blocking, non-blocking 모두 사용 가능하다.

추가적으로, if 와 else가 짝이 맞아야 할 것을 권장한다.(else문이 생략되어도 회로가 합성될 수 있지만, 동작 오류가 발생할 수 있다.정확하게는 latch가 발생한다) 또한 if문이 중첩되어 있을 경우 begin~end문을 사용하여 else가 어디에 붙은 else인지 확인이 가능하도록 작성한다. 이 예시는 다음과 같다.

if(index>0)begin  //if문 시작, 안쪽 if 문 구성을 위해 begin
    if(rega>regb)  //if문 중첩
        result = rega; //blocking문으로 할당
end                    // 안쪽 if문은 else가 생략된 상태로 end 종료

else result = regb; //첫 if문의 else에 해당하는 부분

if문을 통해 2-1mux를 만드는 예시를 하나 보도록 하겠다.

module mux21(a, b, sel, out);
    input [1:0] a, b;   //input 선언(2bit)
    input       sel;    //a, b중 선택하도록 sel 선언
    output [1:0] out;  // output 2bit 선언
    reg    [1:0] out;  // out이 always 안으로 들어가므로 reg type 선언
    
    always@(a or b or sel)  //always문 선언
        if(sel==1'b0)       // sel이 0일 때, 출력이 a
            out = a;
        else
            out = b;        // sel이 1일 때, 출력이 b
endmodule

위 코드는 2-1mux를 만드는 코드이다. sel의 조건에 따라 출력이 결정되며 이 과정에서 if문을 활용한 것이다.

한가지 예시를 더 확인해보도록 하겠다. 다음은 if문을 활용한 D플립플롭을 나타낸 것이다. 조건으로는 비동기식,active-low  set/reset이 존재하는 D 플립플롭이다.

module dff_sr_async(clk, d, rb, sb, q, qb);
    input clk, d, rb, sb; //input 선언, 클럭, D, 리셋, 셋
    output q, qb;         //output 선언, Q, Qb
    reg q;                // always 안에 들어가므로 reg 선언
    
    always@(posedge clk or negedge rb or negedge sb) //3가지 조건, posedge, negedge에 유의
    begin
        if(rb == 0)   // 리셋신호가 들어올때(negedge이므로 0이 reset신호, active-low)
            q<=0;    //q값 0으로 초기화
        else if(sb ==0) //셋 신호가 들어올때
            q<=1;    //q값 1로 set
        else 
            q<=d;   //rb, sb가 모두 0이 아닐때, q에 d를 할당
    end
    
    assign qb = ~q; // always 구문을 통해 만들어진 q를 반전시켜 qb에 할당
                    //이는 d 플립플롭의 출력단에 q와 반전된 qb를 만들어 낸 것임
    
endmodule

참고로 여기서 always 구문 안에 clk이외에 나머지 sb, rb도 구문 수행 조건에 영향을 미치게 되는데 이를 비동기식(asyncronous)라고 한다. clk의 변화에 상관없이 작동하기 때문이다. 만약, 동기식(syncronous)를 원한다면, negedge rb와 negedge sb를 제거하면 된다.)


2. case문

if문과 비슷하지만, 참과 거짓이 아닌, 결과값이 여러개가 나올 수 있을 때, 각 결과값 마다 문장이 실행되도록 할 수 있다.

case(expression)
    case_item {, case_item} : statement_or_null;
    | default [:] statement_or_null;
endcase

case_item에 해당하는 항일 경우 statement_or_null이 실행되며, case의 어떤 값에도 해당되지 않을 경우, default문을 실행하게 된다. if문의 else문과 마찬가지로 default 문도 생략이 가능하지만, 회로합성시 latch가 발생할 수 있으므로, 가급적 생략하지 않도록 유의한다.

위 예시가 조금 어렵기 때문에 2-1mux를 만드는 예제를 하나 더 살펴보도록 하겠다.

module mux21_case(a, b, sel, out);
    input  [1:0] a, b;
    input        sel;
    output [1:0] out;
    reg    [1:0] out;  //always문 안에 쓰이므로 reg type 선언
    
    always@(a or b or sel)
    begin
        case(sel) //case문 선언
            0 : out = a; //sel 값이 0 이면 out 에 a할당
            1 : out = b; //sel 값이 1 이면 out 에 b할당
            default : out = x;
        endcase // case문 종료
    end
endmodule

그렇다면 case구문이 if문 보다 더 잘 쓰일 수 있는 부분은 어디일까? 

case문의 경우 결과값이 여러개가 나올 수 있는 상황이면 if문보다 더 유용하게 쓰일 수 있기 때문에, 디지털회로에서는 디코더에서 사용이 가능하다. 디코더에 대한 내용은 다음을 참조하면 되겠다. 논리회로에서 참조하길 바란다.

reg [15:0] rega;
reg [9:0]  result;

always@(rega) 
begin
    case(rega)
        16'd0 : result = 10'b0111111111;
        16'd1 : result = 10'b1011111111;
        16'd2 : result = 10'b1101111111;
        16'd3 : result = 10'b1110111111;
        16'd4 : result = 10'b1111011111;
        16'd5 : result = 10'b1111101111;
        16'd6 : result = 10'b1111110111;
        16'd7 : result = 10'b1111111011;
        16'd8 : result = 10'b1111111101;
        16'd9 : result = 10'b1111111110;
        default : result = 10'bx;
    endcase
end

rega의 값에 따라 result에 값이 할당되는 디코더이다. rega의 값에 따라 10비트의 어떤 값이 result로 저장된다.


2-1. don't care를 포함하는 case문

don't care(X, Z)를 포함하도록 case문을 구성할 수도 있다.

casez문 : z를 don't care로 취급하여 해당비트를 비교에서 제외하여 구성(?기호 사용가능)

casex문 : x, z를 don't care로 취급하여 해당비트를 비교에서 제외하여 구성

다음은 casex문을 활용한 4비트 우선순위 인코더이다.

module pri_enc_casex(encode, enc);
    input [3:0] encode;
    output [1:0] enc;
    reg    [1:0] enc;
    
    always@(encode)
    begin
      casex(encode) //casex문 선언
          4'b1xxx : enc = 2'b11;  // 맨앞 1비트만 맞으면 동작 수행
          4'b01xx : enc = 2'b10;  // 맨앞 2비트만 맞으면 동작 수행
          4'b001x : enc = 2'b01;
          4'b0001 : enc = 2'b00;
          default : $display("Error") //default문은 error 출력
      endcase
    end
endmodule

여기서 한가지 더 살펴보자면, case문의 조건 안에 상수가 들어갈 수도 있는데, 예시를 확인해보면

module pri_enc_casex(encode, enc);
    input  [3:0] encode;
    output [1:0] enc;
    reg    [1:0] enc;
    
    always@(encode)
    begin
      casex(1) //casex문 선언
          encode[3] : enc = 2'b11;  // encode의 맨 앞 비트가 1이면 동작수행
          encode[2] : enc = 2'b10;  // encode의 두번째 비트가 1이면 동작수행
          encode[1] : enc = 2'b01;
          encode[0] : enc = 2'b00;
          default : $display("Error") //default문은 error 출력
      endcase
    end
endmodule

이렇게 작성하여도 동작은 동일하다. case문에 상수를 넣음으로써, 값이 1인 비트의 위치를 확인하여 인코더를 만든 것이다. 코딩 스타일의 한 종류이니 확인만 하고 넘어가도록 한다.

이렇게 해서 행위수준모델링에 대한 always, if, case문에 대해서 알아보았다. 다음 글에서는 반복문을 알아보도록 한다.

반응형