Harman 세미콘 아카데미/Verilog

[Verilog]② - 병렬 가산기, 병렬 가감산기, 비교기

U_Pong 2024. 6. 18. 21:00

 

2024.6.17 수업날

오늘은 저번주에 배운 Verilog 연산자의 응용버전에 대해 알아보고 직접 Basys3 보드와 연결하여 결과를 살펴본다.


< 병렬 가산기 >

아래의 사진은 병렬 가산기를 나타난 회로도이다.

병렬 가산기란 전가산기 여러 개를 병렬로 연결한 회로이다.

이 회로도를 보면 입력값이A(4개), B(4개), Cin이고, 출력값이 S(4개), Carry임을 알 수 있다.

 

1. full_adder_4bits_structural(구조적 모델링)

병렬 가산기를 코드로 작성한 full_adder_4bits_structural의 회로도를 자세하게 살펴보면,

아래와 같이 여러 개의 게이트들이 합쳐져 있는 것을 확인할 수 있다.

 

 

이제 시뮬레이션 full_adder_4bits_structural의 시뮬레이션 결과를 확인해보자.

이번에는 a와 b의 0~3의 값을 각각 모두 지정해줘야한다.

아래의 그림에 적은것처럼 Force Clock의 값을 적어준다. 단위는 ns이다.

시뮬레이션 돌리는 시간은 2560ns이다.

 

아래의 화면은 결과값을 좀 더 확대해본 모습이다.

a, b 값을 합하면 결과값이 sum에 나타나고, 정해진 값의 범위를 넘은 결과값이 나오면 carry가 발생한다.

(정해진 값의 범위는 0~16까지이다)

결과값을 확대해 본 모습
a, b, sum, carry_w2 모두의 결과값을 확인하기 위해 캡쳐하였다.

 

 

참고로, Name에 주황색 바탕의 I가 적혀있으면 이는 input을 뜻한다.

초록색 바탕의 O가 적혀있으면 이는 output을 뜻한다.

맨 아래의 아이콘은 wire를 뜻한다.

 

 

 

2. 동작적 모델링(behavioral)

동작적 모델링 코드를 작성하려면,

입력값이 총 9개 이기 때문에 2^9 만큼의 경우의 수를 적어주어야 한다.

따라서 작성하는 것은 생략한다.

 

 

 

3. fadder_4bit_dataflow(데이터 플로우)

a에는

0: 10ns

1: 20ns

2: 40ns

3: 80ns

b 에는

0: 160ns

1: 320ns

2: 640ns

3: 1280ns

cin에는 2560ns 을 차례대로 입력하고

시뮬 돌리는 시간을 2560ns으로 설정하면 아래와 같은 결과를 확인할 수 있다.

구조적 모델링과 결과가 비슷함을 확인할 수 있다.

 

병렬 가산기 소스코드
`timescale 1ns / 1ps
///////////////////////////////////////////////// 2024.6.17  병렬 가산기
module full_adder_4bits_structural(
      input [3:0] a, b,       // [3:0] 은 변수 선언할때 몇 비트인지 선언하는 배열, 여기서는 4비트
      input cin,
      output [3:0] sum,
      output carry);
      
      wire [2:0] carry_w;     // carry_wire
      
      full_adder_structural fa0( .a(a[0]),  .b(b[0]),  .cin(cin),  .sum(sum[0]),  .carry(carry_w[0]));
      full_adder_structural fa1( .a(a[1]),  .b(b[1]),  .cin(carry_w[0]),  .sum(sum[1]),  .carry(carry_w[1]));
      full_adder_structural fa2( .a(a[2]),  .b(b[2]),  .cin(carry_w[1]),  .sum(sum[2]),  .carry(carry_w[2]));
      full_adder_structural fa3( .a(a[3]),  .b(b[3]),  .cin(carry_w[2]),  .sum(sum[3]),  .carry(carry));      

endmodule


////////////////////////////////////////////// 
// 동작적 모델링(behavioral)을 하려면 입력값이 9개 이기 때문에 2^9승의 경우의 수로 나열해야 하므로 생략


///////////////////////////////////////////   데이터 플로우
module fadder_4bit_dataflow(         //  수식처리는 다른 방식에 비해 간단
      input [3:0] a, b, 
      input cin,
      output [3:0] sum, 
      output carry);             //베릴로그의 변수 타입: reg, wire 가 있음    ,   입력문에서는 따로 선언 안함

     wire [4:0] sum_value;    // 디폴트는 wire
     
     assign sum_value = a + b + cin;
     
     assign sum = sum_value[3:0];       // wire --> 연결하라, 
     assign carry = sum_value[4];
      
endmodule

 

 

< 병렬 가감산기 >

아래의 사진은 병렬 가감산기를 나타난 회로도이다.

병렬 가감산기란 병렬 가산기의 입력값 B를 부호 S(0일때 덧셈, 1일때 뺄셈)와 XOR 하여

덧셈과 뺄셈이 모두 가능하도록 만든 회로이다.

이 회로도를 보면 입력값이A(4개), B(4개), S이고, 출력값이 S(4개), Carry임을 알 수 있다.

 

예를 들어, 2진수 4비트 음수 계산을 한다고 가정해보자.

0001은 10진수로 1인데, 1 - 1을 빼서 0이라는 연산을 하려면 어떻게 해야할까?

2진수에서는 1과 0으로 밖에 표현하지 못한다. 따라서 0이라는 값을 원한다면 0000이 나오도록 해야한다. 0001을 0000으로 만드려면, 1111을 더하도록 하면 된다.

2진수에서 1과 1을 더하면 0이되고 올림수(carry)가 발생한다. 때문에 결과값이 0000이 되고, 처음에 원하는 값인 0이 되도록 만들 수 있다.

이 연산에서는 올림수가 계속 생기기 때문에 4비트를 넘어서도 1이 생기지만, 지금은 4비트 연산중이여서 좌측의 1이라는 올림수는 생략한다.

뺄셈을 하려면 유의할 점이 있는데, 음수를 쓰게되면 아래 사진의 두번째 줄처럼 1111이 15가 되지 않도록, 1111이 -1을 의미하도록 생각해야한다.

 

 

이런식으로 10진수 음수값에 해당하는 2진수 4비트를 생각하면 우측과 같은 형태가 된다.

추가로 10진수 양수값에 해당하는 2진수 4비트를 생각하면 좌측과 같은 형태가 된다.

즉, 최상위 비트가 1일때 마이너스(음수값)의 의미를 가지게 되고
최상위 비트가 0일때 플러스(양수값)의 의미를 가지게 된다. 

둘의 관계는 없어보이지만, 어떠한 조건을 거친다면 서로 의미있는 관계가 된다.

 

바로 양수값을 음수값으로 바꾸고 싶을때, 양수값의 비트를 반전시킨다음 1을 더하면 음수값이 된다

 

2진수 4비트의 뺄셈에서는 아래와 같은 자리를 초과하는 올림수(carry)에 따른 규칙이 나타난다.

뺄셈을 할때는 자리를 초과하는 carry 값이 발생하는데 이 의미는 올림수가 아니라 뺄셈한 결과값이 0이거나 양수라는(0이거나 0보다 크다)라는 의미이다. 만약 뺄셈에서 carry가 나타나지 않는다면 이 의미는 뺄셈한 결과값이 음수(0보다 작다)라는 의미이다.

 

위에서 한 계산이 잘 되는지 시뮬레이션 결과를 보며 살펴보자.

 

 

1. fadd_sub_4bit_structural

여기서도 fadder_4bit_dataflow의 시뮬레이션을 돌렸을때와 같이 조건을 설정해주면 된다.

시뮬레이션에서 a에서 10~15는 알파벳 a~f로 표현된다.a가 1이고, s가 0일때는 덧셈을 하며 b가 0일때 출력값인 sum의 결과가 1인 것을 확인할 수 있다.carry가 발생한 부분은 a=f(15), b=1이 되어 값이 16이 되었는데, 4비트에서는 16이라는 값을 출력하지 못한다.때문에 올림수가 발생하고 결과값이 0으로 나온 것을 확인할 수 있다.

 

다음으로 s가 1일때를 살펴보자. s=1이면 뺄셈 연산이 실행된다.

그 전에, 보기 편하도록 입력값과 출력값이 음수로 표현되도록 설정해준다.

아래와같이 a, b, sum을 모두 Signed Decimal로 선택하면 음수값을 볼 수 있다.

 

뺄셈은 위에서 계산했던것처럼 양수값의 비트를 반전시킨다음 1을 더하면 된다.

음수 값의 범위는 -8 부터 +7까지 이기 때문에 이 값의 범위를 넘어가는 값이 나오면 출력되지 못한다.

때문에 계속해서 carry가 발생하며, carry가 생기지 않는 곳은 올림수가 발생하지 않는다는 것이다.

 

병렬 가감산기 구조적 모델링 소스코드
///////////////////////////////////////////////////////////// 병렬 가감산기 구조적 모델링
module fadd_sub_4bit_structural(
      input [3:0] a, b,       // [3:0] 은 변수 선언할때 몇 비트인지 선언하는 배열, 여기서는 4비트
      input s,                      //s 가 0이면 덧셈, 1이면 뺄셈을 하게됨
      output [3:0] sum,
      output carry);
      
      wire [2:0] carry_w;     //carry_wire
      wire [3:0] b_w;           // b_wire
      
      xor(b_w[0], b[0], s);
      xor(b_w[1], b[1], s);
      xor(b_w[2], b[2], s);
      xor(b_w[3], b[3], s);
      
      full_adder_structural fa0( .a(a[0]),  .b(b_w[0]),  .cin(s),  .sum(sum[0]),  .carry(carry_w[0]));
      full_adder_structural fa1( .a(a[1]),  .b(b_w[1]),  .cin(carry_w[0]),  .sum(sum[1]),  .carry(carry_w[1]));
      full_adder_structural fa2( .a(a[2]),  .b(b_w[2]),  .cin(carry_w[1]),  .sum(sum[2]),  .carry(carry_w[2]));
      full_adder_structural fa3( .a(a[3]),  .b(b_w[3]),  .cin(carry_w[2]),  .sum(sum[3]),  .carry(carry)); 
      // 8비트짜리 연산

endmodule

 

 

 

2. fadd_sub_4bit_dataflow

이번에는 데이터플로우 형식의 코드를 작성했을때의 결과를 확인해보자.

구조적 모델링과 마찬가지로 s가 0일때 덧셈 결과가 같음을 확인할 수 있다.

 

하지만 차이는 s가 1일때 발생한다. 먼저 결과를 살펴보면

구조적 모델링과 달리 carry가 반대로 출력되는 것을 알 수 있다.

 

이는 소스코드에서 32비트의 연산자를 만들었기 때문에 carry가 반대로 나타나는 것이다.

병렬 가감산기 데이터 플로우 32(2^5)비트 소스코드
//////////////////////////////////////////////////// 병렬 가감산기 데이터 플로우(2^5=32비트짜리)
module fadd_sub_32bit_dataflow(         //  수식처리는 다른 방식에 비해 간단
      input [3:0] a, b, 
      input s,
      output [3:0] sum, 
      output carry);             //베릴로그의 변수 타입: reg, wire 가 있음    ,   입력문에서는 따로 선언 안함

     wire [4:0] sum_value;    // 디폴트는 wire
     
     assign sum_value = s ? a - b  : a + b;           //조건 연산자 c언어와 비슷, 베릴로그에는 증감 연산자가 없음
     //  s=0 이면 +연산, s=1이면 -연산으로 처리, 32비트짜리 연산
     // 뺄셈에서 carry는 1은 less의 의미로 생각(올림수 아님)
     
     assign sum = sum_value[3:0];       // wire --> 연결하라,        4비트가 sum
     assign carry = sum_value[4];         // 5비트가 carry
      
endmodule

 

예를 들어 아래와 같이 2진수 5비트(2^5 = 32) 뺄셈 연산을 한다고 가정하자.

결과값이 11...111110 으로 나오고, 사실상 자리를 초과하는 실제 carry 값은 0인데 소스코드를 구성할 때 5번 자리를 carry로 설정했기 때문에 시뮬레이션에서 carry가 발생한다.

 

만약 병렬 가감산기 구조적 모델링과 똑같은 결과를 만들고 싶다면 아래와 같이 코드를 수정하면된다.

병렬 가감산기 데이터플로우(8비트) 소스코드
//////////////////////////////////////////////////// 병렬 가감산기 데이터 플로우(2^3=8비트짜리), 병렬 가감산기 구조적 모델링과 결과값이 같음
module fadd_sub_8bit_dataflow(         //  수식처리는 다른 방식에 비해 간단
      input [3:0] a, b, 
      input s,
      output [3:0] sum, 
      output carry);             //베릴로그의 변수 타입: reg, wire 가 있음    ,   입력문에서는 따로 선언 안함

     wire [4:0] sum_value;    // 디폴트는 wire
     
     assign sum_value = s ? a - b  : a + b;           //조건 연산자 c언어와 비슷, 베릴로그에는 증감 연산자가 없음
     //  s=0 이면 +연산, s=1이면 -연산으로 처리
     
     assign sum = sum_value[3:0];       // wire --> 연결하라,        4비트가 sum
     assign carry = s ?~sum_value[4] : sum_value[4];         //8비트짜리 연산, 이 부분을 수정하면 병렬 가감산기 구조적 모델링과 결과값이 같음
      
endmodule

 

 

< 비교기 >

1. 1비트 비교기

아래의 그림은 1비트 비교기의 진리표와 회로도이다.

입력받은 입력값을 서로 비교하여 입력값이 서로 같거나, 서로 다르거나, 입력 값중 큰 값이 있거나 작은 값이 있을때를 비교하여 1 혹은 0으로 나타내는 연산식이다.

 

comparator_dataflow 비교기에서 a, b의 Force Clock를 각각 200ns, 100ns으로 설정하고

시뮬레이션 돌리는 시간을 200ns로 설정하여 시뮬레이션의 결과를 확인한다.

a, b의 값이 같을때는 equal이라는 결과값 1로 표시되고,

a가 b보다 작을때는 less라는 결과값이 1로 표시되고,

a가 b보다 클때는 greater라는 결과값이 1로 표시되는 것을 확인할 수 있다.

 

1비트 비교기 소스코드
`timescale 1ns / 1ps
/////////////////////////////////////////////////// 1비트 비교기
////////////////////////////////////////////////// 비교기는 비트별로 회로도가 새롭게 바뀜
module comparator_dataflow(
      input a, b,
      output equal, greater, less);
      
      assign equal = (a == b) ? 1'b1 : 1'b0;
      assign greater = (a > b) ? 1'b1 : 1'b0;
      assign less = (a < b) ? 1'b1 : 1'b0;
      
endmodule

여기서 full_adder_behavioral에서도 사용했던 1'b0과 같은 형식을 볼 수 있다.

각 자리는 베릴로그에서 사용하는 수 표현 형식으로 각자 의미가 다르다.

예를들어 1'b0 의 의미는

1: 상수 값이 비트 수를 나타내는 상수이다. 0이 아닌 unsigned 10진수가 사용된다. 생략되면 비트 수가 지정되지 않은 상태로 32비트로 표현된다.

b: 밑수(base)지정 문자로 2진수(b), 8진수(o), 10진수(d), 16진수(h)를 지정하며 대소문자 구별이 없다.

0: 숫자를 사용하여 값을 표현한다. 예시의 형식에서는 1비트 형식이므로 0이라는 한자리의 숫자가 나타난 것이다.

또한 (a == b) ? 1'b1 : 1'b0;

라는 형식이 쓰이는 것도 볼 수 있는데,

만약 지정된 a와 b의 값이 같다면 1'b1을 실행하고 다르다면 1'b0을 실행하라 는 뜻이다.

 

 

2. N비트 비교기

먼저 2비트 비교기를 살펴보자

1비트 비교기와 다르게 진리표와 회로 모두 복잡해졌다. 또한, 비교기는 비트별로 회로도가 새롭게 바뀐다.

때문에 2비트 이상의 비교기부터는 회로도가 복잡해지므로 구조적 모델링(structural)은 소스코드로 작성하지 않았다.

 

그러므로 N비트 비교기에 따른 소스코드를 작성하여 사용자가 원하는 N값을 대입하도록 한다.

우선 8비트 비교기의 시뮬레이션 결과를 보기 위하여 데이터 플로우 방식의 8비트 비교기를 만들었다.

이번에는 1비트 비교기와 달리 아래와 같이 Force Constant의 값을 a, b 자체에 각각 따로 설정해 줘야한다.

비교를 위해 a = 3,4,5를 넣고 b = 4를 대입하였다.

 

시뮬레이션 돌릴 값을 100ns로 설정하고 결과를 확인해보면

a, b의 값이 같을때는 equal이라는 결과값 1로 표시되고,

a가 b보다 작을때는 less라는 결과값이 1로 표시되고,

a가 b보다 클때는 greater라는 결과값이 1로 표시되는 것을 확인할 수 있다.

comparator_Nbit 의 시뮬레이션 결과

 

8비트 비교기 - 데이터 플로우 소스코드
`timescale 1ns / 1ps
//////////////////////////////////////////////////// 8비트 비교기
// 데이터플로우 방식(병렬실행)
module comparator_Nbit #(parameter N = 8)(      //만약 2비트로 만들고 싶다면 comparator_2bit #(parameter N = 8)(
//parameter 문법은 베릴로그 설계 교재의 63p참고
// N비트 짜리 비교기를 만들려면  --> module comparator_Nbit #(parameter N = 8)(
      input [N-1:0] a, b,       //// N비트 짜리 비교기를 만들려면 [N-1:0]으로 배열 설정하기
      output equal, greater, less);
      
      assign equal    = (a == b) ? 1'b1 : 1'b0;       // 1'b1 라는 형식은 베릴로그 설계 교재의 32p를 참고
      assign greater = (a > b) ? 1'b1 : 1'b0;
      assign less       = (a < b) ? 1'b1 : 1'b0;
      
endmodule

 

다음으로 동작적 모델링으로 표현한 8비트 비교기의 시뮬레이션 결과도 살펴보자.

이번에도 1비트 비교기와 달리 아래와 같이 Force Constant의 값을 a, b 자체에 각각 따로 설정해 줘야한다.

보기 편하도록 a의 값에 3,5,4를 순차적으로 입력했다. b의 입력값은 마찬가지로 4를 입력했다.

데이터 플로우 방식의 8비트 비교기와 결과값이 같은 것을 확인할 수 있다.

 

8비트 비교기 - 동작적 모델링 방식 소스코드
`timescale 1ns / 1ps
//////////////////////////////////////////////// 동작적 모델링
////// 항상블록 사용(순차적실행)
module comparator_Nbit_b #(parameter N = 8)(           
// N비트 짜리 비교기를 만들려면  --> module comparator_Nbit #(parameter N = 8)(
      input [N-1:0] a, b,       //// N비트 짜리 비교기를 만들려면 [N-1:0]으로 배열 설정하기
      output reg equal, greater, less);         // reg 변수는 이름을 사용자가 지정할때 쓰임
      
     always @(*) begin       //*는 와일드카드, a,b 둘다, 밑의 블럭에 있는 코드를 실행한다.
     // always(*)는 input의 값을 *에 대입하라는 뜻
            equal = 0;
            greater = 0;
            less = 0;
            if(a == b) begin
                   equal = 1;
            end
            else if(a > b)begin
                  greater = 1;
            end
            else if(a < b)begin
                  less = 1;
            end
     end
endmodule

 

동작적 모델링(behavior)은 소스코드에 always 구문을 사용한다.

always @() 형식은 괄호 안에 있는 문자의 내용이 바뀌면, 아래의 값을 수행하라는 의미이다.

또한 always(*) 형식은 input의 값을 *에 대입하고

밑의 블럭에 있는 코드를 실행시킨다는 의미이다.

 

 

3. Basys3 보드와 연결하여 비교기 결과 확인해보기

이번에는 Basys3 보드를 연결하여 비교기가 작동되는지 결과를 확인해본다.

먼저 Basys3 보드를 오늘 처음 받았는데, 박스에 담겨 있었다.

Basys3 보드가 담겨있던 상자의 앞, 뒷면 모습

 

박스를 열어보면 아래와 같은 모습을 볼 수 있다.

Basys3 보드에는 물이 닿으면 바로 고장이 나버리기 때문에 취급에 주의해야한다.

Basys3 보드와 함께 포장되어있던 스폰지를 보드의 뒷 부분에 깔고 전선으로 연결하여 고장날 수 있는 환경을 최대한 줄이도록 조치했다.

Basys3 보드의 앞, 뒷면 모습

 

먼저 Basys3 보드에서 어떤 역할의 부품이 있는지 메뉴얼을 살펴보자.

이번 시간에 사용할 곳은, 5번, 6번, 13번, 15번이다.

5번은 위, 아래로 조정할 수 있는 16개의 스위치가 있다.

6번은 5번의 16개의 스위치 바로 위에는 LED이다. LED도 마찬가지로 16개이다.

13번은 Basys3의 전원이 들어오게 하고 코드를 연결하여 작동시키는 USB포트와 연결하는 곳이다.

15번은 전원을 켜거나 끌 수 있는 전원 스위치이다.

 

본격적으로 보드에 연결하여 결과를 확인하기 전에 vivado에서 몇가지 절차를 거쳐야하는데 이 절차에 대해 자세히 알아본다. 먼저 소스코드를 살펴보자.

 

basys3 보드에 연결시켜서 비교기를 확인해보는 소스코드
`timescale 1ns / 1ps
//////////////////////////////////////////////////// 보드에 직접 돌려보기
module comparator_test_top(
      input [3:0] a, b,
      output equal, greater, less);
      
      comparator_Nbit  #(.N(4)) c_4(.a(a), .b(b), .equal(equal), .greater(greater), .less(less));
      //N을 4로 지정했기 때문에 4비트 비교기가 되는 것이다.
      
endmodule

 

Basys3에는 16개의 스위치가 있다.

여기서 우리는 가장 오른쪽부터 4개의 스위치, 가장 왼쪽부터 4개 스위치 즉 총 8개의 스위치만 사용한다.

때문에 input의 4비트짜리 a, b를 적은 것이다.

 

소스코드를 작성했다면, 또 다른 소스파일을 다운로드 받아야한다.

아래의 링크는 Basys3 보드의 부품을 하나하나 조작할 수 있는 내용이 담긴 코드이다.

https://github.com/Digilent/digilent-xdc/blob/master/Basys-3-Master.xdc

 

digilent-xdc/Basys-3-Master.xdc at master · Digilent/digilent-xdc

A collection of Master XDC files for Digilent FPGA and Zynq boards. - Digilent/digilent-xdc

github.com

 

Vivado에서 좌측의 Sources를 보면 'Constraints' 부분이 있다.

확장버튼을 누르면 바로 아래에 constrs_1이라는 것이 있는데, 이곳을 선택하고 상단의 + 버튼을 클릭한다.

그러면 Add Sources라는 창이 뜨는데 이때 Add or create constraints를 클릭한다.

 

그리고 Add Files를 클릭하여

방금 다운로드 받은 Basys3 보드의 부품을 하나하나 조작할 수 있는 내용이 담긴 코드의 경로를 찾아 추가해준다.

강의실에서 캡쳐한 사진으로, 실제 다운로드 경로와는 다를 수 있다.

 

다운로드 받은 코드를 추가했다면

하단의 Copy constraints files into project의 체크박스를 체크하고 Finish를 클릭한다.

 

그러면 아래와 같이

Constraints - constrs_1에 'Basys-3-Master.xdc'라는 파일이 들어가 있는 것을 확인할 수 있다.

코드를 살펴보면 모든 코드 앞에 '#'이 붙어있는데, 주석으로 설정되어 있는 것을 알 수 있다.

여기서 작동을 하고자하는 부분의 주석을 풀고, 값을 지정해주면 된다.

예를 들어, 11번 째 줄의 Switches아래를 살펴보면 스위치의 번호가 쭉 나열되어있는것을 확인할 수 있다.

스위치는 위로 올렸을때가 1, 아래로 내렸을때가 0으로 인식한다.

 

다운로드 받은 코드의 양이 방대해서, 파일을 첨부하고 수정한 부분만 발췌하여 살펴보자.

Basys-3-Master.xdc
0.01MB

 

Basys-3-Master.xdc 에서 수정한 부분의 소스코드
## Switches
set_property -dict { PACKAGE_PIN V17   IOSTANDARD LVCMOS33 } [get_ports {a[0]}]
set_property -dict { PACKAGE_PIN V16   IOSTANDARD LVCMOS33 } [get_ports {a[1]}]
set_property -dict { PACKAGE_PIN W16   IOSTANDARD LVCMOS33 } [get_ports {a[2]}]
set_property -dict { PACKAGE_PIN W17   IOSTANDARD LVCMOS33 } [get_ports {a[3]}]
#set_property -dict { PACKAGE_PIN W15   IOSTANDARD LVCMOS33 } [get_ports {sw[4]}]
#set_property -dict { PACKAGE_PIN V15   IOSTANDARD LVCMOS33 } [get_ports {sw[5]}]
#set_property -dict { PACKAGE_PIN W14   IOSTANDARD LVCMOS33 } [get_ports {sw[6]}]
#set_property -dict { PACKAGE_PIN W13   IOSTANDARD LVCMOS33 } [get_ports {sw[7]}]
#set_property -dict { PACKAGE_PIN V2    IOSTANDARD LVCMOS33 } [get_ports {sw[8]}]
#set_property -dict { PACKAGE_PIN T3    IOSTANDARD LVCMOS33 } [get_ports {sw[9]}]
#set_property -dict { PACKAGE_PIN T2    IOSTANDARD LVCMOS33 } [get_ports {sw[10]}]
#set_property -dict { PACKAGE_PIN R3    IOSTANDARD LVCMOS33 } [get_ports {sw[11]}]
set_property -dict { PACKAGE_PIN W2    IOSTANDARD LVCMOS33 } [get_ports {b[0]}]
set_property -dict { PACKAGE_PIN U1    IOSTANDARD LVCMOS33 } [get_ports {b[1]}]
set_property -dict { PACKAGE_PIN T1    IOSTANDARD LVCMOS33 } [get_ports {b[2]}]
set_property -dict { PACKAGE_PIN R2    IOSTANDARD LVCMOS33 } [get_ports {b[3]}]


## LEDs
set_property -dict { PACKAGE_PIN U16   IOSTANDARD LVCMOS33 } [get_ports {equal}]
set_property -dict { PACKAGE_PIN E19   IOSTANDARD LVCMOS33 } [get_ports {greater}]
set_property -dict { PACKAGE_PIN U19   IOSTANDARD LVCMOS33 } [get_ports {less}]
#set_property -dict { PACKAGE_PIN V19   IOSTANDARD LVCMOS33 } [get_ports {led[3]}]
#set_property -dict { PACKAGE_PIN W18   IOSTANDARD LVCMOS33 } [get_ports {led[4]}]
#set_property -dict { PACKAGE_PIN U15   IOSTANDARD LVCMOS33 } [get_ports {led[5]}]
#set_property -dict { PACKAGE_PIN U14   IOSTANDARD LVCMOS33 } [get_ports {led[6]}]
#set_property -dict { PACKAGE_PIN V14   IOSTANDARD LVCMOS33 } [get_ports {led[7]}]
#set_property -dict { PACKAGE_PIN V13   IOSTANDARD LVCMOS33 } [get_ports {led[8]}]
#set_property -dict { PACKAGE_PIN V3    IOSTANDARD LVCMOS33 } [get_ports {led[9]}]
#set_property -dict { PACKAGE_PIN W3    IOSTANDARD LVCMOS33 } [get_ports {led[10]}]
#set_property -dict { PACKAGE_PIN U3    IOSTANDARD LVCMOS33 } [get_ports {led[11]}]
#set_property -dict { PACKAGE_PIN P3    IOSTANDARD LVCMOS33 } [get_ports {led[12]}]
#set_property -dict { PACKAGE_PIN N3    IOSTANDARD LVCMOS33 } [get_ports {led[13]}]
#set_property -dict { PACKAGE_PIN P1    IOSTANDARD LVCMOS33 } [get_ports {led[14]}]
#set_property -dict { PACKAGE_PIN L1    IOSTANDARD LVCMOS33 } [get_ports {led[15]}]

 

비교기의 결과값을 확인하기 위해 Basys3의 가장 오른쪽부터 4개의 스위치, 가장 왼쪽부터 4개 스위치 즉 총 8개의 스위치만 사용한다고 위에서 언급했다.

따라서 Basys-3-Master.xdc 에서도 스위치의 상단 4개와 하단 4개의 주석을 풀어서 a, b로 바꿔주고

(Basys3 보드에서 가장 좌측으로부터 4개의 스위치를 b, 가장 우측으로부터 4개의 스위치를 a라고 정의한다.)

LED에서는 equal, greater, less를 확인하기 위해 상단 3개의 주석을 풀어서 무엇을 의미하는지 수정한다.

이제 Basys3 보드와 작성한 코드를 서로 연결하는 방법을 알아보자.

Vivado의 좌측 Flow Navigator에서 'SYNTHESIS' - Run synthesis를 선택한다.

클릭하면 우측 상단에 로딩되는 표시가 나타난다.

로딩이 다 되면 1번째 사진과 같이 창이 나타나는데,

1번째는 바로 다음 단계로 넘어간다는 뜻이고, 2번째는 회로도 그림을 확인해본다는 뜻이다.

2번째를 클릭하면 2번째 사진과 같이 Device 창이 뜨게된다.

이는 Basys3 보드로 비교기 결과를 확인하기 위해 만든 코드가 빨간색 박스로 표시한 만큼 사용한다는 것을 의미한다.

(이 부분이 무엇을 의미하는지는 좀더 공부해볼 필요가 있음을 느꼈다.)

 

여기서 'SYNTHESIS' 메뉴의 맨 아래에 있는 Schematic를 선택하면 회로도의 모습을 볼 수 있다.

 

다음으로 SYNTHESIS의 바로 아래에 있는 'IMPLEMENTATION' - Run implementation을 선택한다.

 

마지막으로 IMPLEMENTATION의 바로 아래에 있는 'PROGRAM AND DEBUG' - Generate Bitestream을 선택한다.

 

로딩이 다 되면 아래와 같은 창이 뜨는데, 2번째의 Open Hardware Manager를 선택한다.

 

Basys3 보드와 연결한 채로 있다면 상단에 Program device라고 파란색 글씨로 나타난다. 이를 클릭해준다.

 

그러면 아래와 같은 창이 나타나는데, 하단의 Program을 클릭하여 Basys3 보드에 결과가 나타나는지 확인한다.

 

1) Equal 상태일때

16개의 스위치는 모두 아래로 위치한 상태. 따라서 코드에서 설정한 맨 우측의 LED(Equal을 의미)가 켜진다.
b(좌측 4개)를 1000, a(우측 4개)를 1000으로 설정했을때 Equal을 의미하는 LED가 켜진다.

 

2) Less 상태일때

b(좌측 4개)를 1000, a(우측 4개)를 0000으로 설정했을때 Less을 의미하는 LED가 켜진다.

 

3) Greater 상태일때

b(좌측 4개)를 0001, a(우측 4개)를 1000으로 설정했을때 Greater를 의미하는 LED가 켜진다.


병렬 가산기, 병렬 가감산기, 비교기 끝!