2-2. Verilog 연산자

1. Verilog 연산자 개요

Verilog는 디지털 회로를 모델링하고 시뮬레이션하기 위한 하드웨어 기술 언어(HDL)입니다. Verilog에서 연산자는 회로의 동작을 정의하는 데 핵심적인 역할을 합니다. 이러한 연산자들은 다양한 데이터 타입에 적용되어 덧셈, 뺄셈, 논리 연산, 비트 단위 연산 등 다양한 기능을 수행합니다. 본 문서에서는 Verilog에서 제공하는 연산자들을 종류별로 자세히 살펴보고, 예제를 통해 실제 사용 방법을 익혀보겠습니다.

2. 연산자 종류

Verilog는 다양한 연산을 수행하기 위한 여러 종류의 연산자를 제공합니다. 각 연산자는 우선순위와 결합 법칙을 가지며, 이를 통해 연산의 순서가 결정됩니다. 주요 연산자 종류는 다음과 같습니다.

  1. 산술 연산자
  2. 논리 연산자
  3. 비트 단위 연산자
  4. 관계 연산자
  5. 축소 연산자
  6. 시프트 연산자
  7. 조건 연산자
  8. 연접 연산자

각 연산자에 대해 자세히 알아보겠습니다.

1) 산술 연산자

산술 연산자는 덧셈, 뺄셈, 곱셈, 나눗셈, 나머지 연산을 수행합니다.

연산자 설명 예시
+ 덧셈 a + b
- 뺄셈 a - b
* 곱셈 a * b
/ 나눗셈 a / b
% 나머지 a % b
  • +, -, * 연산은 부호 있는(signed) 또는 부호 없는(unsigned) 피연산자에 대해 모두 지원됩니다.
  • / 연산에서 피연산자가 모두 정수형이고 나누어떨어지지 않으면 소수점 이하는 버려집니다.
  • % 연산자는 나머지 연산을 수행하며, 나눗셈과 동일한 부호 규칙을 따릅니다.

2) 논리 연산자

논리 연산자는 true 또는 false 값을 반환하며, 조건식을 평가하는 데 사용됩니다.

연산자 설명 예시
&& 논리 AND a && b
|| 논리 OR a || b
! 논리 NOT !a
  • && 연산자는 두 피연산자가 모두 true일 때 true를 반환합니다.
  • || 연산자는 두 피연산자 중 하나라도 true이면 true를 반환합니다.
  • ! 연산자는 피연산자가 true이면 false를, false이면 true를 반환합니다.

3) 비트 단위 연산자

비트 단위 연산자는 각 비트에 대해 개별적으로 연산을 수행합니다.

연산자 설명 예시
~ 비트 NOT ~a
& 비트 AND a & b
| 비트 OR a | b
^ 비트 XOR a ^ b
^~, ~^ 비트 XNOR a ^~ b, ~^ b
  • ~ 연산자는 각 비트를 반전시킵니다.
  • & 연산자는 두 피연산자의 각 비트에 대해 AND 연산을 수행합니다.
  • | 연산자는 두 피연산자의 각 비트에 대해 OR 연산을 수행합니다.
  • ^ 연산자는 두 피연산자의 각 비트에 대해 XOR 연산을 수행합니다.
  • ^~, ~^ 연산자는 두 피연산자의 각 비트에 대해 XNOR 연산을 수행합니다.

4) 관계 연산자

관계 연산자는 두 피연산자의 크기를 비교합니다.

연산자 설명 예시
> a > b
< 작음 a < b
>= 큼 또는 같음 a >= b
<= 작음 또는 같음 a <= b
<mark class="highlight"> 같음 a </mark> b
!<mark class="highlight"><strong><u> 같지 않음 a !</u></strong></mark> b
  • 관계 연산자는 true 또는 false 값을 반환합니다.
  • ==!= 연산자는 두 피연산자의 모든 비트가 같은지 또는 다른지를 비교합니다.

5) 축소 연산자

축소 연산자는 단항 연산자로서, 피연산자의 모든 비트에 대해 연산을 수행하여 하나의 비트 결과를 생성합니다.

연산자 설명 예시
& 축소 AND &a
| 축소 OR |a
^ 축소 XOR ^a
~& 축소 NAND ~&a
~| 축소 NOR ~|a
~^, ^~ 축소 XNOR ~^a, ^~a
  • 예를 들어, &aa의 모든 비트에 대해 AND 연산을 수행합니다.
  • a가 4비트인 경우, &aa[3] & a[2] & a[1] & a[0]과 같습니다.

6) 시프트 연산자

시프트 연산자는 비트 열을 왼쪽 또는 오른쪽으로 이동시킵니다.

연산자 설명 예시
<< 왼쪽 시프트 a << b
>> 오른쪽 시프트 a >> b
<<< 왼쪽 산술 시프트 a <<< b
>>> 오른쪽 산술 시프트 a >>> b
  • << 연산자는 ab 비트만큼 왼쪽으로 이동시킵니다. 빈 비트는 0으로 채워집니다.
  • >> 연산자는 ab 비트만큼 오른쪽으로 이동시킵니다. 빈 비트는 0으로 채워집니다 (unsigned인 경우). signed인 경우 최상위 비트(MSB)로 채워집니다.
  • <<<는 왼쪽 산술 시프트, >>>는 오른쪽 산술 시프트입니다. signed, unsigned에 따라 채워지는 비트가 다릅니다.

7) 조건 연산자

조건 연산자는 삼항 연산자이며, 조건에 따라 두 개의 값 중 하나를 선택합니다.

연산자 설명 예시
?: 조건 연산자 (ternary operator) condition ? value1 : value2
  • conditiontrue이면 value1을 반환하고, false이면 value2를 반환합니다.
  • 조건 연산자는 if-else 문과 유사한 기능을 제공하지만, 더 간결하게 표현할 수 있습니다.

8) 연접 연산자

연접 연산자는 여러 개의 신호를 묶어 하나의 신호로 만듭니다.

연산자 설명 예시
{} 연접 (concatenation) {a, b, c}
  • 예를 들어, {a, b, c}는 신호 a, b, c를 순서대로 연결하여 하나의 신호를 생성합니다.

3. 연산자 우선순위

Verilog 연산자는 각기 다른 우선순위를 가집니다. 연산자 우선순위는 연산의 순서를 결정하며, 괄호를 사용하여 명시적으로 우선순위를 지정할 수 있습니다. 일반적으로 괄호는 연산의 의도를 명확하게 하고, 예상치 못한 결과를 방지하는 데 도움이 됩니다.

다음은 Verilog 연산자의 우선순위를 나타낸 표입니다 (높은 우선순위에서 낮은 우선순위 순).

연산자 설명 우선순위
() 괄호 (가장 높은 우선순위) 1
. 멤버 접근 1
[] 배열/비트 선택 1
+ - ! ~ & ~ & | ~| ^ ~^ 단항 연산자 (부호, NOT, 비트 NOT, 축소 연산자) 2
* / % 산술 연산 (곱셈, 나눗셈, 나머지) 3
+ - 산술 연산 (덧셈, 뺄셈) 4
<< >> <<< >>> 시프트 연산 5
< <= > >= 관계 연산 6
== !<mark class="highlight"><strong><u> </u></strong></mark><mark class="highlight"> !</mark> 동일성 연산 7
& 비트 AND 8
^ ~^ ~^ 비트 XOR, XNOR 9
| 비트 OR 10
&& 논리 AND 11
|| 논리 OR 12
?: 조건 연산 13
{} {} 연접, 반복 연접 14

4. 연산자 사용 예제

다음은 다양한 연산자를 사용하는 간단한 Verilog 예제입니다.

module operator_example;

  // 데이터 타입 선언
  reg [3:0] a, b, result;
  reg [7:0] c;
  reg flag;

  // 초기값 설정
  initial begin
    a = 4'b1010;  // 10
    b = 4'b0101;  // 5
    c = 8'b11001100; // 204
  end

  // 산술 연산자 예제
  always @(*) begin
    result <mark class="highlight"><strong><u> a + b; // 10 + 5 </u></strong></mark> 15
    $display("산술 연산: a + b = %d", result);
    result <mark class="highlight"><strong><u> a - b; // 10 - 5 </u></strong></mark> 5
    $display("산술 연산: a - b = %d", result);
    result <mark class="highlight"><strong><u> a * b; // 10 * 5 </u></strong></mark> 50
    $display("산술 연산: a * b = %d", result);
    result <mark class="highlight"><strong><u> a / b; // 10 / 5 </u></strong></mark> 2
    $display("산술 연산: a / b = %d", result);
    result <mark class="highlight"><strong><u> a % b; // 10 % 5 </u></strong></mark> 0
    $display("산술 연산: a %% b = %d", result);
  end

  // 논리 연산자 예제
  always @(*) begin
    flag = (a > 0) && (b < 10);
    $display("논리 연산: (a > 0) && (b < 10) = %b", flag);
    flag = (a > 10) || (b < 10);
    $display("논리 연산: (a > 10) || (b < 10) = %b", flag);
    flag = !(a > b);
    $display("논리 연산: !(a > b) = %b", flag);
  end

  // 비트 단위 연산자 예제
  always @(*) begin
    result = ~a;
    $display("비트 연산: ~a = %b", result);
    result = a & b;
    $display("비트 연산: a & b = %b", result);
    result = a | b;
    $display("비트 연산: a | b = %b", result);
    result = a ^ b;
    $display("비트 연산: a ^ b = %b", result);
  end

  // 관계 연산자 예제
  always @(*) begin
    flag = (a > b);
    $display("관계 연산: a > b = %b", flag);
    flag = (a <mark class="highlight"> b);
    $display("관계 연산: a </mark> b = %b", flag);
    flag <mark class="highlight"><strong><u> (a !</u></strong></mark> b);
    $display("관계 연산: a !<mark class="highlight"><strong><u> b </u></strong></mark> %b", flag);
  end

  // 축소 연산자 예제
  always @(*) begin
    result = &a; // a의 모든 비트가 1이면 1, 아니면 0
    $display("축소 연산: &a = %b", result);
    result = |a; // a의 비트 중 하나라도 1이면 1, 아니면 0
    $display("축소 연산: |a = %b", result);
    result = ^a; // a의 비트가 짝수 개의 1이면 0, 홀수 개의 1이면 1
    $display("축소 연산: ^a = %b", result);
  end

  // 시프트 연산자 예제
  always @(*) begin
    result = a << 1;
    $display("시프트 연산: a << 1 = %b", result);
    result = c >> 2;
    $display("시프트 연산: c >> 2 = %b", result);
  end

  // 조건 연산자 예제
  always @(*) begin
    result = (a > b) ? a : b;
    $display("조건 연산: (a > b) ? a : b = %d", result);
  end

  // 연접 연산자 예제
  always @(*) begin
    result = {a[3:2], b[1:0]};
    $display("연접 연산: {a[3:2], b[1:0]} = %b", result);
  end

endmodule

이 예제에서는 각 연산자를 사용하여 다양한 연산을 수행하고, $display 문을 통해 결과를 출력합니다. 이를 통해 각 연산자의 동작을 확인하고 이해할 수 있습니다.

5. 중요 팁과 주의사항

1) 데이터 타입의 중요성

Verilog 연산에서 데이터 타입은 매우 중요합니다. 부호 있는(signed) 데이터 타입과 부호 없는(unsigned) 데이터 타입은 연산 결과에 큰 영향을 미칠 수 있습니다. 예를 들어, 부호 없는 숫자를 음수와 연산하면 예기치 않은 결과를 얻을 수 있습니다.

  reg signed [3:0] signed_a = -5; // 부호 있는 정수
  reg [3:0] unsigned_b = 3;  // 부호 없는 정수

  always @(*) begin
    $display("signed_a + unsigned_b = %d", signed_a + unsigned_b); // -2
  end

2) 우선순위와 괄호 사용

연산자 우선순위를 정확히 이해하고 괄호를 사용하여 연산의 순서를 명확하게 지정하는 것이 중요합니다. 괄호는 코드의 가독성을 높이고, 예상치 못한 결과를 방지하는 데 도움이 됩니다. 특히 복잡한 표현식에서는 괄호를 적극적으로 사용하여 의도를 명확히 하는 것이 좋습니다.

3) 비트 폭 (Bit Width)

연산 결과의 비트 폭은 피연산자의 비트 폭에 따라 결정됩니다. 연산 결과를 저장할 변수의 비트 폭이 충분하지 않으면, 데이터 손실이 발생할 수 있습니다.

  reg [3:0] a = 8; // 1000
  reg [3:0] b = 7; // 0111
  reg [3:0] result;

  always @(*) begin
    result = a + b; // 15 (1111) -> 결과는 1111 이지만, result는 4비트이므로 오버플로우가 발생하지 않음
    $display("a + b = %d", result); // result = 15
    result = a * b; // 56 (111000) -> 6비트 필요. 하지만 result는 4비트이므로 하위 4비트만 저장됨
    $display("a * b = %d", result); // result = 8
  end

4) 시프트 연산의 주의

시프트 연산은 비트 열을 이동시키므로, 데이터 손실이 발생할 수 있습니다. 또한, 부호 있는 숫자를 오른쪽으로 시프트할 때, 산술 시프트와 논리 시프트의 동작이 다르므로 주의해야 합니다.

5) X, Z 값 처리

Verilog 시뮬레이션에서 XZ 값은 초기화되지 않은 상태를 나타냅니다. 연산에 이러한 값이 포함되면, 결과도 X 또는 Z가 될 수 있습니다. 따라서, 연산 전에 변수를 적절하게 초기화하는 것이 중요합니다.

6. 결론

Verilog 연산자는 디지털 회로를 모델링하고 설계하는 데 필수적인 요소입니다. 다양한 연산자를 이해하고 적절하게 활용하면, 복잡한 기능을 구현하는 회로를 효과적으로 설계할 수 있습니다. 데이터 타입, 연산자 우선순위, 비트 폭, 그리고 시프트 연산과 관련된 주의사항을 숙지하여, 더욱 정확하고 효율적인 Verilog 코드를 작성할 수 있습니다. 본 문서에서 제공된 예제를 통해 각 연산자의 동작을 이해하고, 실제 설계에 적용해 보시기 바랍니다.

비슷한 글 추천

Comments (0)

No comments yet. Be the first to comment!