빌딩 블록: D 플립플롭
동기 설계의 모든 것은 한 작은 하드웨어로 귀결됩니다: D 플립플롭. 클럭 입력, 데이터 입력, 데이터 출력을 가집니다. 매 rising 클럭 edge에서 D의 값을 포착해 다음 edge까지 유지합니다. 그게 전부입니다.
Verilog에서:
always @(posedge clk) begin
q <= d;
end
세 줄, 플립플롭 하나. <=는 non-blocking이며 정확히 실제 플립플롭의 동작입니다(Blocking vs Non-blocking에서 다룸). posedge clk sensitivity가 그것을 순차로 만듭니다.
조합 논리를 사이에 두고 많이 쌓으면 어떤 동기 회로든 만들 수 있습니다.
reset이 있는 register
실제 설계는 항상 reset이 필요합니다 - 전원 인가 시나 요청 시 시스템을 알려진 상태로 만드는 방법.
이게 synchronous reset 입니다 - reset 조건이 다른 입력처럼 클럭 edge에서 평가됩니다. synthesizer는 데이터 입력에 2-to-1 mux가 있는 플립플롭을 만듭니다: reset이 high일 때는 mux가 0을 공급, 아니면 d를 공급.
대부분의 설계에서 synchronous reset이 옳은 선택입니다. timing이 단순하고, FPGA와 잘 어울리며, reset 신호가 어디서든 올 수 있습니다 - 클럭에 정렬된 소스가 필요 없습니다.
enable이 있는 register
종종 무언가가 알려 줄 때만 갱신되는 register를 원합니다. clocked 블록 안에 if를 쓰고, 빠진 else의 암시적 "이전 값 유지" 동작에 의존하세요.
정석 "load-enabled register"입니다. 어디서나 등장합니다 - 설정 register, 다운스트림이 준비됐을 때만 진행하는 파이프라인 단계, 일시정지/재개하는 카운터 등. 마지막 else 생략은 clocked 블록 안에서 의도적이고 안전합니다: 플립플롭은 이미 이전 값을 기억하므로 "아무 일 안 함"이 "유지"를 의미합니다.
조합 블록에서 같은 코드는 latch를 inferred합니다. 다른 규칙, 같은 문법.
카운터
카운터는 다음 값이 현재 값 더하기 1인 register입니다.
카운터는 enable이 high인 모든 클럭 사이클에서 증가합니다. 16 사이클 뒤 0으로 wrap됩니다(4비트로 선언했고 15 + 1이 0으로 overflow). 그 wrap은 N비트 산술에 내재적이고, 실제 하드웨어 카운터의 정확한 동작입니다.
shift register
플립플롭을 쌓으면 shift register가 됩니다. 요령은 모든 shift를 하나의 non-blocking 문장으로 하는 것입니다.
본문은 out <= {out[WIDTH-2:0], in} - 현재 out의 하위 비트와 새 in을 concatenate해 전체를 대입합니다. non-blocking이라 RHS가 LHS 갱신 전 옛 out을 읽습니다. 효과는 단일 클럭 사이클의 깔끔한 N비트 shift입니다.
shift-register 패턴의 가장 작은 형태입니다. LFSR, serial transmitter, deserializer - 매 클럭에 플립플롭 체인을 통해 데이터가 이동하는 어떤 설계로도 일반화됩니다.
파이프라인
파이프라인은 조합 논리로 분리된 register 체인입니다. 각 단계는 이전 단계의 데이터를 처리해 다음 단계에 공급합니다.
3단, 3 사이클 latency, 그러나 파이프라인이 가득 차면 매 사이클 새 결과. latency가 3 클럭인 건 데이터가 세 플립플롭을 지나기 때문이고, 처리량이 클럭당 1 연산인 건 세 단계가 다른 입력에 대해 동시에 일하기 때문입니다.
이게 고성능 설계가 처리량 목표를 달성하는 방법입니다: 단계를 짧게 유지하고, 더 깊이 파이프라이닝하고, 병렬성이 일을 하게 합니다.
asynchronous reset (필요할 때)
가끔 reset을 위해 클럭 edge를 기다릴 수 없을 때가 있습니다 - 칩이 전원이 내려가고 있거나, 클럭이 게이트되어 있거나, 외부 워치독이 라인을 끌어내리고 있을 때. 그런 경우엔
always @(posedge clk or negedge reset_n) begin
if (~reset_n) q <= 0;
else q <= d;
end
sensitivity list가 이제 클럭 edge 와 reset edge 둘 다 가집니다. 플립플롭이 둘 중 어디든 즉시 반응합니다. reset_n은 관례상 active-low(low일 때 assert)이라 검사가 ~reset_n입니다.
비동기 reset은 trade-off가 있습니다: timing 분석이 어렵고, de-assertion에서 조심히 다루지 않으면 metastability를 일으킬 수 있고, 모든 FPGA 아키텍처에서 이식 가능하지 않습니다. 기본으로 synchronous reset을, 설계가 요구할 때만 async를 쓰세요.
다음에 볼 내용
이제 어떤 동기 datapath든 만들 수 있습니다. 다음 문서 - Finite State Machines - 는 clocked register와 case 문을 결합해 표준 FSM idiom을 만들어 냅니다: 모든 컨트롤러, 프로토콜 엔진, 결정 블록의 일꾼.
자주 묻는 질문
Verilog의 clocked logic이란?
클럭 신호로 게이트되는 갱신을 가진 로직입니다. 표준 idiom은 always @(posedge clk) target <= next_value; - clk의 매 rising edge마다 target이 next_value를 포착합니다. 그 한 줄이 하드웨어에서 플립플롭을 기술합니다. 그것들을 조합 논리 사이에 쌓아 카운터, shift register, 파이프라인 - 모든 동기 회로를 만듭니다.
Verilog의 synchronous reset과 asynchronous reset의 차이는?
Synchronous reset은 always @(posedge clk) if (reset) ...을 사용합니다 - reset이 다른 입력처럼 클럭 edge에서 sampling됩니다. Asynchronous reset은 always @(posedge clk or negedge reset_n) if (~reset_n) ...을 사용합니다 - 블록이 클럭 edge나 reset assertion에서 트리거되어 reset이 즉시 효력을 발휘합니다. Synchronous가 기본 선택이고, asynchronous는 클럭이 죽어도 reset 효력이 보장되어야 할 때 사용합니다.
Verilog에서 파이프라인은 어떻게 만드나요?
always @(posedge clk) stageN_reg <= stageN_combinational;의 여러 단계를 쌓습니다 - 각 단계의 조합 논리가 다음 단계의 register에 공급되고, 모든 register가 같은 클럭 edge에서 포착합니다. 결과는 매 사이클 새 데이터가 들어가고 N 사이클 뒤에 나오는 파이프라인이며, 클럭당 결과 하나의 처리량을 가집니다.
Verilog의 shift register란?
각 플립플롭의 출력이 다음 입력으로 공급되는 플립플롭의 체인입니다. 매 클럭 edge에서 모든 비트가 한 자리씩 이동합니다. 정석 Verilog 버전은 non-blocking assignment를 사용합니다: out <= {out[N-2:0], in}; - 그 한 줄이 사이클당 in에서 1비트를 받는 N비트 shift register를 만듭니다.