Skip to content

Latest commit

 

History

History
339 lines (210 loc) · 10.9 KB

File metadata and controls

339 lines (210 loc) · 10.9 KB

연산자 (operators)

강의일: 4월 10일 FL (4주차)

학습목표: 디지털 회로에서 알고리듬을 표현하려면 알고리듬에서 사용한 연산을 회로로 표현할 수 있어야 한다. 따라서 디지털 하드웨어에서 구현 가능한 연산이 무엇이 있는지 익히는 건 중요하다.

C 랭귀지로 구현한 건 CPU에 의해 동작한다.

하드웨어 프로그래밍으로는 어떻게 각 연산이 구현될까?

  • Verilog의 연산자들

    verilog operator table

    • power 연산, 몫(/)과 나머지(%) 연산 같은 건 이에 상응하는 하드웨어가 존재하지 않는다. 그래서 하드웨어로 이게 어떻게 구현될지 고민해야 한다. 빼기의 반복으로 나누기를 만드는 식으로... 직접 구현해주어야 한다.

    • modulo는 조건부로 합성이 될 때도 있다.

    • 이 표에는 없지만 수업자료에는 ===, !==라는 case 등가, case부등 연산자도 있는데 이것 역시 합성용은 아니다. 테스트용 문법 중 하나임.

  • 실수 형에 쓸 수 있는 연산자

    • 일단 실수는 실제 회로에는 합성하지 못한다는 걸 꼭 기억하자.
    • C언어와 파이썬에서 쓰는 연산자들은 거의 다 됨. 다만 shift 연산자, 비트 연산자는 불가.
  • 연산자 우선순위

    verilog operator priority

    대충 이런 식이다. 참고로 **은 단항 산술연산자 바로 밑 2순위다.

    근데... 굳이 외우지 말고 그냥 괄호 꼬박꼬박 잘 쳐주면 된다.

  • 수식에서의 정수형 수의 저리

    integer IntA, IntB, IntC, IntD; 	// 32-bit signed integer
    IntA = -12/4;						// -3
    IntB = -'d12 / 4;					// 1073741821
    // unsigned에 음수 저장은 불가하나 일단 앞에-를 붙였으니 2의 보수 연산을 해주긴 함.
    // 그러나 저장은 unsigned로서 저장했기 때문에 나누기 연산을 음수로서 해주지 않음
    // signed/unsigned가 저장할 땐 동일하나 연산할 때는 서로 다른 것이 나타나는 예제임
    IntC = -'sd12 / 4;					// -3 
    IntD = -4'sd12 / 4;					// 1
    // IntD 해설: 
    // -4'sd12는 1100의 보수, 즉 0100 = 4 이므로 4로 나눈 결과가 1이 됨.

산술 연산자

흔히 말하는 사칙 연산을 수행하는 연산자들.

  • 산술 연산자 관련 유의할 점 몇가지

    • 결과가 x(unknown)로 나오는 경우 :

      • 피연산자의 비트에 x나 z가 포함됨
      • N / 0 또는 N % 0 연산
    • % 연산의 결과의 부호는 첫번째 피연산자의 부호를 따른다.

    • ** 연산에서 결과가 정의되지 않는 경우 :

      • 0 ** <음수>
      • <음수> ** <실수>
    • 그 외 합성 조건들 (물론 상수 끼리 연산한 결과는 얼마든 합성 가능함) :

      • /(몫)과 %(나머지) 연산의 경우 조건부1 합성가능
      • ** (거듭제곱)은 피연산자 중 하나라도 변수가 있으면 합성 불가능.
  • 산술 연산자에서의 자료형 해석

    • signed : signed net, signed reg, integer, real, realtime
    • unsigned : unsigned net, unsigned reg, time

관계 연산자

"크냐?" "작냐?"를 비교하는 연산자이다. (>, <, >= ,<=)

  • 산술연산자보다 우선순위 떨어진다. 예를 들어 b - (1<a) 와 b-1<a는 다르다.
  • 결과가 x가 나오는 경우 :
    • 피연산자의 비트에 x나 z가 포함됨
  • 두 피연산자의 비트 수가 다른 경우 :
    • 비트수가 큰 쪽에 맞춰 작은 애가 MSB 쪽에 0을 채운다. 그 후 관계 판단.
    • signed인 경우, MSB를 확대해서 부호가 안 바뀌도록 한다. (signed extention)
  • 피연산자 중 하나가 실수형 : 다른 피연산자를 실수형으로 변환한 후 비교

등가 연산자

"같은가?" 를 판단하는 연산자

  • 관계연산자보다 우선순위 떨어진다.

  • 결과가 x가 나오는 경우 :

    • Case Equality 연산자가 아니고, 피연산자의 비트에 x나 z가 포함됨
  • 두 피연산자의 비트 수가 다른 경우:

    • 비트수가 큰 쪽으로 맞춰서 작은 쪽을 MSB 방향으로 0을 채워줌. 그 후 등가 판단.
  • Case Equality 연산자(===, !==)

    • 논리합성불가. test하는 경우에만 써야 한다.
    • x와 z가 포함된 일치 불일치 판단.
      • 테스트 상황에서 unknown이나 high-z가 나오는지, 에러 상황 등을 체크하고 싶을 때 쓰는 편이다.

다음은 등가 연산자 사용지 주의할 만한 상황이다.

// D = 4'b1001, F = 4'b1xxz라고 하자.

/* 일반 등가 연산자 */
if(D!=F)
    #10 A = 0;
else
    #10 A = 1;

// 이 때는 D!=F 연산의 결과가 x이기 때문에, else 실행문으로 빠져서 결과는 A=1


/* Case 등가 연산자 */
if(D!==F)
    #10 A = 0;
else
    #10 A = 1;
    
//  이 경우엔 x와z도 비교 대상으로서 끼워주므로, D!==F가 True라서 결과는 A=0

논리 연산자

"참이냐?" "거짓이냐?"를 판단함.

  • 0이면 거짓, 그 외의 수는 다 참이다.
  • 결과가 x가 되는 경우:
    • 참 / 거짓 판단이 모호함 - 논리 연산자의 피연산자에 x가 섞인 수

대충 이런 식으로 3항을 엮어도 된다는 것 같다.

(a < size-1) && (b != c) && (index != last_one)

비트 연산자

  • 피연산자의 해당 비트들에 대한 연산 수행

    • 피연산자의 비트 수만큼 결과 출력함
  • 두 피연산자의 비트 수가 같지 않으면:

    • 비트 수가 작은 피연산자의 MSB위치에 0 채워준 후 연산함
  • ~ (NOT) ^(XOR) &(AND) |(OR)

  • (a ~^b ) _NOR_는 되는데 ~& (NAND), ~| (NOR)같은 축약은 안 됨. ~(a&b)나 ~(a|b)로 표현해야 한다고 한다.

  • 비트 AND 연산자

    & 0 1 x z
    0 0 0 0 0
    1 0 1 x x
    x 0 x x x
    z 0 x x x

    0의 AND는 무조건 0. 나머지는 1&1 제외 전부 x

  • 비트 OR 연산자

    | 0 1 x z
    0 0 1 x x
    1 1 1 1 1
    x x 1 x x
    z x 1 x x

    1과의 OR은 전부 1. 나머지는 0|0 제외 x

  • 비트 XOR 연산자

    ^ 0 1 x z
    0 0 1 x x
    1 1 0 x x
    x x x x x
    z x x x x

    x와 z가 섞이면 죄다 결과는 x

  • 비트 NOT

    ~0 = 1, ~1 = 0, ~x = x, ~z = x

축약 연산자

  • 단항 연산자이다.

  • 한 피연산자가 가진 비트 끼리끼리 연산을 함.

    reg [7:0] cnt;
    assign parity = ^cnt;
    assign parity = cnt[7]^cnt[7]^cnt[5]^cnt[4]^cnt[3]^cnt[2]^cnt[1]^cnt[0]
        
    // 위 두 구문은 같은 의미다.

    또한 이 경우엔 피연산자가 하나 뿐이라 ~&, ~| 같은 표현도 가능.

  • 어떨 때 이런 연산자를 쓸까?

    • 모든 비트가 0인지(~|) / 모든 비트가 1인지(&) / 1의 개수가 짝수 개인지 (~^) / 1의 개수가 홀수 개인지(^) 확인할 때 유용하다.

시프트 연산자

  • 논리 시프트 연산자

    • <<: 우측 피연산자 값만큼 좌측 피연산자를 왼쪽으로 시프트 후, 비어있는 비트에 0을 채움
    • >>: 위에서 왼쪽 -> 오른쪽
  • 산술 시프트 연산자(arithmetic shift) : 논리 시프트 연산자의 signed 고려 버전. 비트가 아닌 숫자로서 인식한다.

    • <<< : <<와 비슷하나 MSB는 시프트되지 않음.
    • >>> : 위에서 왼쪽 -> 오른쪽
  • 우측 피연산자 (얼만큼 이동시키는지 나타내는 지표)

    • 합성 가능하려면 상수로 써야 함. 변수를 넣어도 되긴 하는데 합성은 안 된다.
    • x 또는 z가 포함된 경우, 시프트 연산의 결과값은 x
    • 항상 unsigned 수가 들어감.
  • 예문

    module shift_op ():
        wire signed [7:0] data1 = 8'b1010_1100;
        wire		[7:0] data2 = 8'b1000_1111; // unsigned
        reg			[7:0] out1, out2;			// unsigned
        
        always @(data1, data2) begin
            out1 = data1 >>> 2; // out1 : 1110_1011
            out2 = data2 >>> 2; // out2 : 0010_0011
            #100 $stop;
        end
    endmodule
    // out1은 us임에도 불구하고 data1>>>2는 s연산을 함. 
    // 피연산자의 자료형이 결정한다는 사실! 

조건 연산자

  • C에서의 삼항 연산자와 동일하다. 신호처리 쪽으로는 multiplexing 개념으로 이해할 수도 있다.
  • 만약 ( )? 안의 값이 참/ 거짓이 아니라 x또는 z라면 ( ) : ( ) 양쪽의 결과를 합쳐서 내놓는다. 즉, 잘못된 결과가 나온다.
    • 합치는 방식: 양쪽 결과항을 서로 비교하며, 같은 자리 수의 비트가 둘다 0이면 0, 둘다 1이면 1, 나머지 모든 상황에선 x로 결과가 나온다.
wire [15:0] buss, data;
// ... 
assign buss = drive_buss ? data : 16'bz
    
// 요게 대강 버스를 위한 삼상회로의 구현방법이다.

결합 및 반복 연산자

결합 연산자

내가 a라는 3비트 데이터와 b라는 4비트 데이터를 7비트 데이터로서 묶어서 쓰고 싶다고 생각해보자. 그럴 때 중괄호 { } 로 묶어주면 된다.

  • 조건

    • 결합되는 피연산자들의 비트가 결정되어있어야 함 (unsized 상수는 결합 연산자 사용 불가)
  • 대입문의 좌측 또는 우측에 사용 가능

  • 비트 폭이 일치하지 않는 변수의 연산 / 대입

    • 우변의 비트 폭이 작다면, 우변의 MSB에 0을 붙여 연산(signed의 경우 MSB 부호 유지)
    • 좌변의 비트 폭이 작다면, 일단은 합쳐 놓고, 좌변에 맞춰 MSB 누락.
  • 사용예

    • 대입문의 우측

      wire [15:0] addr_bus;
      wire [7:0] addr_h, addr_l;
      //...
      assign addr_bus = {addr_h, addr_l}
    • 대입문의 좌측

      wire [3:0] a, b, sum;
      wire carry;
      //...
      assign {carry, sum} = a+b;

반복 연산자

굉장히 유용하고, 많이 애용해도 되는 합성 가능 연산자이다.

  • 형태: {a{b}} b를 a회 반복해준다. (반복 횟수는 0, x, z가 아닌 상수)
{4{w}} // {w, w, w, w}와 동일한 표현이다
{b, {3, {a, b}}} // {b, a, b, a, b, a, b}
a[31:0] = {32{1'b1}}; 
// 이렇게 하면 많은 비트 공간에 전부다 1을 채워넣는 뭐 이런 게 가능함.

Footnotes

  1. 분모가 2의 거듭제곱. 비트(이진수) 단위로 잘라냄으로써 하드웨어에 구현할 수 있기 때문이다. 즉 a/2^n 은 a>>n 과 동일하고, a%2^n은 하위 n비트를 선택하는 것과 같다.