[Review]
이전 내용 참조
Adder
이전에는 gate들을 사용하여 만들어줬지만, 실제로는 FPGA chip 내부의 가산기를 활용하여 만들어야 한다.
module full_4bit( ///// dataflow modeling
input [3:0] a, b,
input cin,
output [3:0] sum,
output carry
);
wire [4:0] temp; /// carry의 자리는 [5], [3:0]은 sum
assign temp = a + b + cin; /// 비트연산자가 아닌, 사칙연산자를 사용하였다. ---> 회로가 달라짐.
/// FPGA 내에 있는 덧셈기를 이용하여 회로를 구성한다.(좀 더 추상화 레벨이 높다.)
assign sum = temp[3:0];
assign carry = temp[4];
endmodule
FPGA 칩 내부에 가장 빠른 adder가 이미 있으므로, 그걸 활용해서 사용해야 한다.
코딩할 때 더하기를 쓰면 위 adder가 자동적으로 사용됨.
look a head adder은 ripple carry adder보다 빠르다.
따라서 FPGA에서는 구조적 모델링을 하지 않는다
왜? : Gate들을 이용해서 만들 필요가 없기 떄문에
이미 다 만들어져 있어. 그리고 다른 것들은 MUX로 구성이 가능하다.
순차 논리 회로
1. 정의
: 같은 입력에 의해 다른 출력이 나올 수 있는데,
다음 출력이 이전 출력의 영향을 받는 Feedback 구조를 가지는 회로
▶ clock의 edge를 받아서 동작하는 것은 순차 논리 회로
▶ clock의 level 받아서 동작하는 것은 조합 논리 회로
이렇게 구분하는 것이 제일 이해하기 쉽다.(정의는 아님)
이러한 순차논리회로는 초기화가 중요하다.
따라서 반드시 들어가야 하는 것이 Reset
2. 대표적 사용 예시
대표적인 순차논리회로가 바로 counter
Resgister는 이전 출력에 의해 다음 출력이 정해지지는 않지만,
edge에서 동작하므로 순차논리회로
3. LATCH
clock없이 feedback 구조를 가지는 순차논리회로가 바로 Latch
FPGA에서는 Latch를 만들면 안 된다.
- Timing issue(타이밍 이슈) : LATCH는 비동기적 동작
- Synthesis Complexity(합성 복잡성) : LATCH는 시간적 의존성을 가지므로 합성이 복잡
- Power Consumption(전력 소모) : LATCH는 동작 중에도 활성화되어 전력 소모 증가 야기
- Verification Complexity(검증 복잡성) : LATCH가 있는 회로는 검증 과정이 복잡해지는 경향이 있다.
4. Memory 영역
FPGA Board에서 DFF와 LUT은 연결되어 있다.
Register는 되먹임 구조가 없기 때문에 LUT이 필요없다.
만약 Memory를 만들기 위해 DFF를 사용하면, 옆에 붙어 있는 LUT도 같이 할당되어 사용 불가
따라서 RAM 영역이 따로 존재하며, Memory를 만들 때 이 영역을 사용한다.
5. LUT(Look-up Table)
f(S₁, S₂, S₃) = m∑(000, 001, 100, 101)
위 equation을 회로로 표현하려면,
Karnough map(카르노 맵) Boolean Algebra(불 대수식)을
이용하여 간소화하는 과정이 필요하다.
→ 복잡하고 시간도 많이 걸림. 귀찮아.
하지만, LUT이라는 개념의 등장으로 위와 같은 복잡한 방법은
더 이상 사용하지 않아도 된다.
LUT은 주어진 연산에 대해 미리 계산된 결과들의 집합이며,
일종의 메모리로 볼 수 있다.
이와 같은 LUT을 사용하여 회로를 설계하게 되면, 설계 시간이 단축되는 것은 물론이고
게이트 수 때문에 발생하는 pdt로 인해 생성되는 Glitch 생성률을 현저히 낮출 수 있다.
위와 같은 편의성때문에 실제로 FPGA에서는 입력이 6개짜리 LUT을 사용한다.
6. 현명한 왕은 신하가 할 수 있는 명령만 내린다.
FPGA는 AND gate 등을 잘 만들지 못한다.
어떻게 만들어 내기는 하는데, 제대로 동작하지 않는 경우가 많다,
FF, LUT들의 조합으로 여러 기능을 만들어 내기 떄문에 그 것에 맞는 코드들 작성해주어야 한다.
[Memory]
ROM / RAM
ROM과 RAM은 컴퓨터 시스템에서 중요한 역할을 하는 주요 유형의 메모리이다.
ROM과 RAM의 차이점 | ||
특성 | ROM (Read-Only Memory) |
RAM (Random-Access Memory) |
내용 변경 가능 여부 | 변경 불가능 | 읽기 및 쓰기 가능 |
데이터 유지 여부 | 비휘발성 | 휘발성 |
데이터 손실 여부 | 전원이 꺼져도 데이터 유지 | 전원 종료 시, 손실 |
주요 용도 | 시동 프로그램(BIOS) 저장 초기 데이터 저장 |
작업 중인 프로그램 및 데이터 저장 |
실행 속도 | 데이터 읽기 속도가 느림 | 데이터 읽기 및 쓰기 속도가 빠름 |
ROM 유형 비교
ROM은 빈 공책이고, PROM은 볼펜으로 기록, EPROM/EEPROM은 연필로 기록한다 생각하면 쉽다.
EPROM의 등장으로 ROM은 더 이상 Read-Only가 아니게 되었다.
특성 | PROM | EPROM | EEPROM |
데이터 유지 | 영구적으로 저장 | 데이터를 소거하지 않는 한, 영구적으로 저장 |
데이터를 소거하지 않는 한, 영구적으로 저장 |
데이터 소거 방법 | 불가능 | UV(자외선) 노출을 통해 데이터 소거 가능 |
전기적으로 소거 가능 |
프로그래밍 방법 | 특수 프로그래머 필요 | UV 프로미터 및 프로그래머 필요 |
전기 신호를 이용한 프로그래밍 필요 |
삭제 필요 여부 | 삭제 불가능 | 삭제 시 UV 노출 필요 | 삭제 가능하지만 전기적으로 프로그래밍 필요 |
프로그래밍 회로 | 기록 가능한 퓨즈 또는 플로팅 게이트 사용 |
플로팅 게이트 사용 | 플로팅 게이트 사용 |
사용 용도 | 고정된 데이터 저장 | 초기 데이터 저장 및 수정 가능한 용도 |
초기 데이터 저장 및 수정 가능한 용도 |
쓰기 주기 제한 | 없음 (한 번 프로그래밍되면 계속 유지) |
여러 번 프로그래밍하면 물리적 손상 가능 |
일정 횟수 이내의 쓰기 주기 제한이 있을 수 있음. |
[Clock Library 생성]
평소에 우리가 사용하는 시간 단위인 sec, msec. usec 등이 필요하므로
cora board system clock을 활용하여 여러 가지 clock_div를 만들어줄 것이다.
// Cora board sys clock : 125MHz
// 1 clock 당, 8ns
// Micro sec clock
module clock_usec(
input clk, reset_p,
output clk_usec
);
// 125개 카운트하면 1us
reg [6:0] cnt_8nsec;
wire cp_usec; // cp : clock pulse
always @(posedge clk, posedge reset_p) begin
if(reset_p) cnt_8nsec = 0; // reset이 들어오면 cnt = 0
else if (cnt_8nsec >= 124) cnt_8nsec = 0; // 0부터 124번까지 카운트하면 다시 0으로
else cnt_8nsec = cnt_8nsec + 1;
end
assign cp_usec = cnt_8nsec < 63 ? 0 : 1; // 0~62 : 0, 63~124 : 1
// 비정규 clock이므로 동기화가 필요하다.
edge_detector_n edg(.clk(clk), .cp_in(cp_usec), .rst(reset_p), .n_edge(clk_usec)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule
// Mili sec clock
module clock_msec(
input clk, clk_usec, reset_p,
output clk_msec
);
// 1000개 카운트하면 1ms
// reg [9:0] cnt_usec;
// wire cp_msec; // cp : clock pulse
// always @(posedge clk, posedge reset_p) begin
// if(reset_p) cnt_usec = 0; // reset이 들어오면 cnt = 0
// else if (cnt_usec >= 999) cnt_usec = 0; // 0부터 125번까지 카운트하면 다시 0으로
// else cnt_usec = cnt_usec + 1;
// end
// assign cp_msec = cnt_usec < 499 ? 0 : 1; // 0~498 : 0, 499~1000 : 1
// 1000개 카운트하면 1ms
reg [8:0] cnt_usec;
reg cp_msec; // cp : clock pulse
always @(posedge clk, posedge reset_p) begin
if(reset_p) cnt_usec = 0; // reset이 들어오면 cnt = 0
else if(clk_usec) begin
if (cnt_usec >= 499) begin
cnt_usec = 0; // 0부터 499번까지 카운트하면 다시 0으로
cp_msec = ~cp_msec;
end
else cnt_usec = cnt_usec + 1;
end
end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
// 비정규 clock이므로 동기화가 필요하다.
edge_detector_n edg(.clk(clk), .cp_in(cp_msec), .rst(reset_p), .n_edge(clk_msec)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule
// sec clock
module clock_sec(
input clk, clk_msec, reset_p,
output clk_sec
);
// 1000개 카운트하면 1s
reg [8:0] cnt_msec;
reg cp_sec; // cp : clock pulse
always @(posedge clk, posedge reset_p) begin
if(reset_p) cnt_msec = 0; // reset이 들어오면 cnt = 0
else if(clk_msec) begin
if (cnt_msec >= 499) begin
cnt_msec = 0; // 0부터 499번까지 카운트하면 다시 0으로
cp_sec = ~cp_sec; // clock pulse 반전
end
else cnt_msec = cnt_msec + 1;
end
end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
// 비정규 clock이므로 동기화가 필요하다.
edge_detector_n edg(.clk(clk), .cp_in(cp_sec), .rst(reset_p), .n_edge(clk_sec)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule
// min clock
module clock_min(
input clk, clk_sec, reset_p,
output clk_min
);
// 60개 카운트하면 1s
reg [5:0] cnt_sec;
reg cp_min; // cp : clock pulse
always @(posedge clk, posedge reset_p) begin
if(reset_p) cnt_sec = 0; // reset이 들어오면 cnt = 0
else if(clk_sec) begin
if (cnt_sec >= 29) begin
cnt_sec = 0; // 0부터 번까지 카운트하면 다시 0으로
cp_min = ~cp_min;
end
else cnt_sec = cnt_sec + 1;
end
end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
// 비정규 clock이므로 동기화가 필요하다.
edge_detector_n edg(.clk(clk), .cp_in(cp_min), .rst(reset_p), .n_edge(clk_min)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule
<Simulation>
Force clock으로 clk에 8ns 주기의 clock 부여
Force constant로 reset_p 8ns동안 1 주고, 다시 0으로
지금 사용하는 cora board는 125MHz의 시스템 클락을 가지지만,
이후에 사용할 board의 시스템 클락은 100MHz이므로 수정이 필요하다.
[StopWatch 만들기]
Start/Stop 기능 구현
module Stop_Watch_top(
input clk,
input reset_p,
input [1:0] btn, // 버튼 2개 쓸 것이므로
output [3:0] com,
output [7:0] seg_7
);
wire up_down_p, start_stop;
// DFF를 위한 분주기
reg [16:0] clk_div;
always @(posedge clk) clk_div = clk_div + 1;
// 바운싱 제거용 DFF
D_flip_flop_n debnc_60(.d(btn[0]), .clk(clk_div[16]), .rst(reset_p), .q(start_stop)); //// 위의 clk divider의 16번쨰 bit를 써야 된다.
// 분주기 인스턴스
wire clk_usec, clk_msec, clk_sec;
clock_usec usec_clk(.clk(clk), .reset_p(reset_p), .clk_usec(clk_usec));
clock_msec msec_clk(.clk(clk), .clk_usec(clk_usec), .reset_p(reset_p), .clk_msec(clk_msec));
clock_sec sec_clk(.clk(clk), .clk_msec(clk_msec), .reset_p(reset_p), .clk_sec(clk_sec)); // 3가지 다 불러와야 sec 분주기 사용 가능
// 카운터 인스턴스
wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
counter_dec_60 cnt_60(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10));
FND_4digit_cntr fnd_cntr(.clk(clk), .rst(rst), .value({8'b0, sec10, sec1}), .com(com), .seg_7(seg_7)); /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
endmodule
// 60bit_Counter
module counter_dec_60(
input clk, reset_p,
input clk_time,
output reg [3:0] dec1, dec10
);
always @(posedge clk, posedge reset_p) begin
if(reset_p) begin
dec1 = 0;
dec10 = 0;
end
else if(clk_time) begin
if(dec1 >= 9) begin
dec1 <= 0;
if(dec10 >= 5) dec10 = 0;
else dec10 <= dec10 + 1;
end
else dec1 <= dec1 + 1;
end
end
endmodule
<Cora-Z7-07s-Master.xdc 설정>
## Buttons
set_property -dict { PACKAGE_PIN D20 IOSTANDARD LVCMOS33 } [get_ports { reset_p }]; #IO_L4N_T0_35 Sch=btn[0]
#set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports { btn1 }]; #IO_L4P_T0_35 Sch=btn[1]
#set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets up_down_IBUF]
## Pmod Header JA
set_property -dict { PACKAGE_PIN Y18 IOSTANDARD LVCMOS33 } [get_ports { btn[0]] }]; #IO_L17P_T2_34 Sch=ja_p[1]
set_property -dict { PACKAGE_PIN Y19 IOSTANDARD LVCMOS33 } [get_ports { btn[1] }]; #IO_L17N_T2_34 Sch=ja_n[1]
#set_property -dict { PACKAGE_PIN Y16 IOSTANDARD LVCMOS33 } [get_ports { ja[2] }]; #IO_L7P_T1_34 Sch=ja_p[2]
#set_property -dict { PACKAGE_PIN Y17 IOSTANDARD LVCMOS33 } [get_ports { ja[3] }]; #IO_L7N_T1_34 Sch=ja_n[2]
#set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { ja[4] }]; #IO_L12P_T1_MRCC_34 Sch=ja_p[3]
#set_property -dict { PACKAGE_PIN U19 IOSTANDARD LVCMOS33 } [get_ports { ja[5] }]; #IO_L12N_T1_MRCC_34 Sch=ja_n[3]
#set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports { ja[6] }]; #IO_L22P_T3_34 Sch=ja_p[4]
#set_property -dict { PACKAGE_PIN W19 IOSTANDARD LVCMOS33 } [get_ports { ja[7] }]; #IO_L22N_T3_34 Sch=ja_n[4]
## ChipKit Outer Digital Header
set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports { seg_7[1] }]; #IO_L11P_T1_SRCC_34 Sch=ck_io[0]
set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports { seg_7[5] }]; #IO_L3N_T0_DQS_34 Sch=ck_io[1]
set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { seg_7[0] }]; #IO_L5P_T0_34 Sch=ck_io[2]
set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { seg_7[4] }]; #IO_L5N_T0_34 Sch=ck_io[3]
set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { seg_7[3] }]; #IO_L21P_T3_DQS_34 Sch=ck_io[4]
set_property -dict { PACKAGE_PIN V18 IOSTANDARD LVCMOS33 } [get_ports { seg_7[2] }]; #IO_L21N_T3_DQS_34 Sch=ck_io[5]
set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { seg_7[7] }]; #IO_L19N_T3_VREF_34 Sch=ck_io[6]
set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { seg_7[6] }]; #IO_L6N_T0_VREF_34 Sch=ck_io[7]
set_property -dict { PACKAGE_PIN N18 IOSTANDARD LVCMOS33 } [get_ports { com[0] }]; #IO_L13P_T2_MRCC_34 Sch=ck_io[8]
set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { com[1] }]; #IO_L8N_T1_AD10N_35 Sch=ck_io[9]
set_property -dict { PACKAGE_PIN U15 IOSTANDARD LVCMOS33 } [get_ports { com[2] }]; #IO_L11N_T1_SRCC_34 Sch=ck_io[10]
set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports { com[3] }]; #IO_L12N_T1_MRCC_35 Sch=ck_io[11]
#set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { ck_io12 }]; #IO_L14P_T2_AD4P_SRCC_35 Sch=ck_io[12]
#set_property -dict { PACKAGE_PIN G15 IOSTANDARD LVCMOS33 } [get_ports { ck_io13 }]; #IO_L19N_T3_VREF_35 Sch=ck_io[13]
1초에 하나씩 count 되어야 하는데, 0.25초에 한 번씩 됨.
내일 다시 수정해봐야겠다.
'# Semiconductor > [Semicon Academy]' 카테고리의 다른 글
[Harman 세미콘 아카데미] 43일차 - ARM & RTOS 활용(Timer, EXTI, DMA) (0) | 2023.08.25 |
---|---|
[Harman 세미콘 아카데미] 42일차 - ARM & RTOS 활용(Endian, Review, Mission) (0) | 2023.08.25 |
[Harman 세미콘 아카데미] 40일차 - PSpice Mission (0) | 2023.08.22 |
[Harman 세미콘 아카데미] 40일차 - PSpice(Flip-Flop, Oscillator) (0) | 2023.08.22 |
[Harman 세미콘 아카데미] 39일차 - ATmega128(Ultrasonic, ADC) (0) | 2023.08.21 |