산술 연산자
예를 보는 게 더 이해가 쉽다.
// a = 4'b0011; (== 3)
// b = 4'b0100; (== 4)
// d = 6;
// e = 4;
a * b // 4'b1100 (== 12), 10진수 값으로 곱한다
d / e // 1, 소수점 이하는 버림
a + b // 4'b0111, 비트 연산
b - a // 4'b0001
13 % 3 // 1, 나머지 연산 값
16 % 4 // 0, 나머지 연산 값
-7 % 2 // -1, 첫번째 값의 부호와 일치해야 한다
7 % -2 // 1, 첫번째 값의 부호와 일치해야 한다
더하기 ( + )와 빼기 ( - )는 비트로 더하든 10진수로 더하든 똑같다.
나누기 ( / )와 나머지 ( % ) 연산자는 두 번째 피연산자가 0인 경우 결괏값은 x인 unknown값이다.
이유는 분모에 0이 들어가면 무한대가 되기 때문이다.
추가적으로 나머지 ( % ) 연산자의 부호는 첫 번째 연산자의 부호를 따른다.
무슨말이냐 하면 위에서 예를 보면,
-7 % 2는 부호 제외하고 보면, 나머지가 1이지만 첫 번째 연산자의 값이 ' - '라 -1이 된다.
좀 더 보자면, -7 = 2 * -3 - 1으로 표현할 수 있고, 2 * -4 + 1로 표현할 수도 있다.
즉, -1이냐 +1이냐의 차이인데 첫 번째 연산자의 값이 음수라 -1을 결괏값으로 취한다.
논리 연산자
참 아니면 거짓만 나온다. 0이 아니면 다 참이다. 0만 거짓이다. 1248도 참이고 -124도 참이다
&& = and 연산
|| = or 연산
! = not 연산
// a = -3; (참)
// b = 0; (거짓)
a && b // 0, 참 && 거짓 = 거짓
a || b // 1, 참 || 거짓 = 참
!a // 0, !참 = 거짓
비트 연산
위에서 본 논리 연산과 잘 구분해야 한다.
& = 비트의 and 연산
| = 비트의 or 연산
~ = 모든 비트를 반전 시킴
^ = 비트의 XOR
~^ = 비트의 XNOR
// a = 1010 (참)
// b = 1111 (참)
a && b // 1, 참 && 참 = 참
a & b //4'b1010, 각각의 비트끼리 연산을 수행
축약 연산자
위에사 잘 구분했는데 또 헷갈리게 축약 연산자가 있다.
축약 연산자는 단항 연산자로 하나의 피연산자에 사용된다.
하나하나의 비트 연산을 수행한다. 사용의 이유는 아래와 같다 축약 연산자의 이름처럼 축약하여 사용할 수 있기 때문이다.
&
~&
|
~|
^
~^가 있다.
&4'b0110 // 0 & 1 & 1 & 0 == 0
~&4'b0110 // ~(0 & 1 & 1 & 0) == 1
// 아래는 같은 연산이다.
parity = ^cnt[7:0];
parity = ^cnt[7]^cnt[6]^cnt[5]^cnt[4]^cnt[3]^cnt[2]^cnt[1]^cnt[0];
등가 연산자
흔히들 알고 있는 기본적으로 ==,!=가 있으나 추가적으로 ===,!== 가 있다.
차이점은 x, z를 포함하냐 하지 않냐의 차이에 있다.
==와 !=는 논리 값인 x, z를 표함 하지 않는 연산자이다. 그러나 결과는 x가 될 수 있다
===는 x, z를 포함하여 일치를 판단한다. !==도 x, z를 포함하여 불일치를 판단한다.
시프트 연산자
논리 시프트 (<<, >>)는 빈자리에 0을 채운다
산술 시프트 (<<<. >>>)는 왼쪽 시프트 <<<는 0을 채우지만 >>>는 빈자리에 MSB를 채운다.
// a = 4'b1100;
b = a >> 1; // 4'b0110
b = a << 1; // 4'b1000
b = a << 2; // 4'b0000
결합 연산자
중요한 연산자이다.
중괄호 {}를 이용하여 2개 이상의 값들을 결합한다. 하나의 나열이라고 볼 수도 있다.
// a = 1'b'1;
// b = 2'b00;
// c = 2'b10;
// d = 3'b110;
y = {b, c}; // 4'b0010, 2비트 +2비트에다 00과 10을 결합한 값
y = {a, b, c, d}; // 8'b1_00_10_110
y = {a, 3'b001}; // 4'b1_001, 자세히 나열 {a, 1'b0, 1'b0, 1'b1}
y = {b[0], c[1]}; // 2'b01
// 반대 작업도 가능
// in_reg = 10'b1_0111_01_010;
{a[1], b[3:0], c[1:0], d[5:3]} = in_reg[9:0];
// a[1] = 1'b1
// b[3:0] = 4'b0111
// c[1:0] = 2'b01
// d[5:3] = 3'b010
여러 정보들을 하나로 묶는 작업이다. 반대로 여러 정보를 하나하나 분배할 수도 있다. 만약 같은 진수가 아니라면 진수의 통일을 하여 수행된다. ( + index는 왼쪽에서부터 시작된다. 예, c [1] = 1, c [0] = 0)
추가적으로 봐야 할 것이 있다. 아래 예를 보자,
// a = 1101
// b = 1001
{carry, sum[3:0]} = a[3:0] + b[3:0];
위의 연산은 4비트 덧셈이다. 하지만 1101과 1001을 더하면 carry가 발생한다. 그래서 5비트 연산으로 이루어진다.
회로적으로 보자,
아래의 회선을 나눠야 한다면 어떻게 작성할까? 결합 연산자를 이용하여 간편하게 작성할 수 있다.
숫자를 지정하여 나눌 수도 있지만 결합 연산자를 이용하면 [31:16] 까지는 bus_hi에, 나머지는 bus_lo에 들어간다.
assign {bus_hi[15:0], bus_lo[15:0]} = bus[31:0];
// 위와 같은 동작
assign bus_hi[15:0] = bus[31:16];
assign bus_lo[15:0] = bus[15:0];
반복 연산자
중괄호 2개를 이용하여 나타낸다. 즉, {{}} 를 이용하여 {a {b}} 와 같은 형태로 사용한다. b를 a번 반복한다는 의미다.
주의해야 할 점은 {}의 수를 잘 맞춰야 한다.
// a = 1'b1;
// b = 2'b00;
// c = 2'b10;
y = { {4{a}} }; // 4'b1111, 1이 4개
y = { {4{a}}, {2{b}} } //8'b1111_0000, 1이 4개, 00이 2개
// 아래 코드는 같다
x = { w, w, w, w}
x = { {4{w}} } // 끝에 있는{}는 결합연산자
a의 값이 1인데 4번 반복하라니 위와 같은 값이 나오고
두 번째의 경우에는 먼저 1을 4번 반복하고, 00을 2번 반복하고 결합을 한 것이다.
회로적 예를 들어보자, 여러 표현 방법이 있을 것이다. 4가지 정도로 알아보자
// 방법 1
assign y[7] = a;
assign y[6] = a;
assign y[5] = b;
assign y[4] = b;
assign y[3] = b;
assign y[2] = 1'b1;
assign y[1] = 1'b1;
assign y[0] = 1'b1;
// 방법 2
assign y[7:6] = {2{a}};
assign y[5:3] = {3{b}};
assign y[2:0] = {3{1'b1}};
// 방법 3
assign y[7:0] = { a, a, b, b, b, 1'b1, 1'b1, 1'b1 }
// 방법 4
assign y[7:0] = { {2{a}}, {3{b}}, {3{1'b1}} }
방법 1은 가장 기본적인 생각이다.
방법 2는 중복을 이용하여 작성한 것이다.
방법 3은 결합을 이용하여 작성한 것이다.
방법 4는 중복과 결합을 이용하여 작성한 것이다.
제일 좋은 것은 방법 4처럼 짜는 것이다. 즉 반복은 결합과 사용하면 더 좋은 코드를 만들어 낼 수 있다.
조건 연산자
프로그래밍 언어에서의 조건 연산자와 똑같다. 주로 여러 개의 입력 중에 하나만 출력하는 mux로 사용한다.
표현 방법은 아래와 같다.
결과 = 조건 ? (참일 경우) : (거짓일 경우);
댓글