소개
이 글은 Xilinx Vivado의 부분 재구성(Partial Reconfiguration), 즉 DFX(Dynamic Function eXchange)에 관한 네 편짜리 시리즈의 두 번째 글입니다. 이 글의 목적은 FPGA 설계에서 부분 재구성(Partial Reconfiguration)을 활성화하는 단계를 안내하고 설명하는 것입니다. 아직 읽지 않았다면 첫 번째 글을 먼저 읽어 보시길 권합니다. 이 단계들의 배경 개념을 설명해 두었기 때문입니다.
Xilinx는 2020년에 부분 재구성(Partial Reconfiguration)을 "DFX(Dynamic Function eXchange)"로 브랜드를 변경했습니다. DFX는 Vivado의 메뉴와 안내 문구에 사용되는 표현입니다. 하지만 이 글에서는 기술 용어인 "부분 재구성(Partial Reconfiguration)"을 계속 사용합니다.
이 글에서는 설명을 단순하게 하기 위해 프로젝트에 재구성 파티션(reconfigurable partition)이 하나만 있다고 가정합니다. 여러 개의 파티션으로 확장하는 것은 비교적 간단합니다.
이 글에서 설명하는 절차는 프로젝트가 부분 재구성(Partial Reconfiguration)을 사용하도록 설정되기 이전부터 시작합니다. 대략적으로 단계를 나누면 다음과 같습니다.
- 플로어플래닝(floorplanning)을 위한 프로젝트 준비
- 부분 재구성(Partial Reconfiguration)을 위한 초기 프로젝트 설정
- 플로어플래닝(floorplanning)
- 설계 구현을 통해 플로어플래닝 확인 및 수정
- 두 번째 재구성 모듈(또는 여러 모듈) 추가
- 비트스트림(bitstream) 파일을 얻기 위한 구현 수행
- 프로젝트 검토
플로어플래닝 준비
플로어플래닝(floorplanning)은 다른 어떤 작업보다 머리를 써야 하는 작업입니다. FPGA 로직 영역을 낭비하지 않으면서도, 정적 파티션(static partition)과 재구성 파티션 모두 배치 및 배선(place and route) 과정에서 큰 장애물을 만나지 않도록 보장해야 하는 미묘한 균형이 필요합니다.
따라서 이 글의 상당 부분이 이 주제를 다룹니다.
오래전에는 플로어플래닝(floorplanning)이 타이밍 클로저(timing closure)를 위해 사용되던 기법이었습니다. 플로어플래닝은 도구가 로직을 합리적인 방식으로 배치하도록 도와주었습니다. FPGA 설계 도구가 시간이 지나면서 개선됨에 따라, 플로어플래닝이 타이밍 제약(timing constraints) 달성에 실제로 도움이 되는 것을 마지막으로 본 지도 수년이 지났습니다. 오늘날에는 거의 언제나 도구가 스스로 판단하도록 두는 것이 타이밍 제약을 달성하는 최선의 전략입니다.
부분 재구성(Partial Reconfiguration)에서는 플로어플래닝(floorplanning)이 필수이므로, 목표는 상황을 더 나쁘게 만들지 않는 것입니다. 이를 위한 작업은 대개 시행착오의 연속입니다. 좋은 결과를 얻는 가장 간단한 방법은 플로어플래닝 없이 설계를 한번 구현해 보고, 로직이 자연스럽게 배치된 모습을 출발점으로 삼는 것입니다. 그다음 단계로, 초기 배치를 가이드라인으로 삼아 부분 재구성에 맞는 영역 구성을 시도하는 것입니다.
플러그인 사용(plugin usage) 시나리오에서는 프로젝트가 발전함에 따라 플로어플래닝(floorplanning)을 갱신할 수 있습니다. 그러나 Remote Update(원격 업데이트) 시나리오, 즉 출시된 설계를 부분 재구성(Partial Reconfiguration)으로 버전 업데이트하는 경우에는 그렇지 않습니다. Remote Update에서는 모든 부분 비트스트림이 초기 비트스트림과 일치해야 합니다. 따라서 초기 비트스트림이 출시되는 순간 설계의 정적 로직(static logic) 부분은 동결됩니다. 그 외에도 플로어플래닝이 동일하게 유지되어야 한다는 뜻입니다.
그러므로 부분 재구성을 시작하기 전에, 가장 먼저 할 일은 FPGA 안에서 정적 로직(static logic)이 들어갈 적절한 영역을 찾는 것입니다. 여기에 너무 많은 시간을 쓸 필요는 없습니다. 프로젝트가 두 부분으로 나뉜 뒤에 진행할 수 있는 출발점만 잡으면 됩니다.
혼동하지 마세요. 이 첫 단계의 목적은 플로어플래닝(floorplanning) 자체가 아닙니다. 제약 없이 Vivado가 로직을 어떻게 배치하는지 확인하고, 그로부터 정적 로직(static logic)에 어떤 영역을 할당할지 결정하는 것입니다. 절차는 다음과 같습니다.
- 평소처럼 설계 구현을 수행합니다. 구현된 설계(Implemented Design)를 열고 디바이스 뷰(Device view)를 살펴봅니다. 정적 설계가 로직 리소스를 얼마나 사용하는지, Vivado가 그 로직을 어떻게 배치하는 경향이 있는지 감을 잡아 보세요.
- 재구성 로직으로 사용할 부분을 프로젝트에서 임시로 제거하면 이 작업이 더 쉬울 수 있습니다. 단, 로직 최적화 때문에 정적 로직(static logic)까지 함께 제거되지 않는 방식이어야 합니다.
- 디바이스 뷰(Device view)에서 아무 로직 요소도 선택하지 않은 상태인지 확인하고, 뷰 위에 마우스 오른쪽 버튼을 클릭한 다음 "Draw Pblock"을 선택합니다. 정적 로직(static logic)을 수용하기에 적합해 보이는 영역을 그립니다. Vivado가 만든 배치를 그대로 흉내 낼 필요는 없습니다. 로직 배치와 타이밍 제약(timing constraints) 달성에 장애물을 만들지 않으면서도 최소한의 영역을 할당하는 모양을 찾도록 하세요.
- Vivado는 "Create a new Pblock"이라는 대화상자를 엽니다. Pblock을 클록 영역(clock region) 기준으로 정의하라는 제안이 나올 수 있는데, 그러지 마세요. 슬라이스(slice), DSP, 그리고 가능하면 다른 로직 요소를 기준으로 Pblock을 지정해야 합니다.
- Ultrascale FPGA의 경우 Pblock 대화상자에서 IOB를 포함하라는 제안도 나올 수 있습니다. 그렇다면 그 옵션을 해제하세요. 나중에 Pblock을 저장하거나 크기를 조정할 때 Vivado가 멈출 수 있습니다(Vivado의 버그 때문에).
- Pblock의 모양, 특히 슬라이스(slice)의 범위에 주의하세요. 이 정보는 Vivado GUI의 Pblock 속성 창("General" 탭 아래)이나 Tcl 콘솔에서 확인할 수 있습니다. Tcl 콘솔에는 다음과 같은 내용이 출력됩니다.
startgroup create_pblock pblock_1 resize_pblock pblock_1 -add {SLICE_X108Y148:SLICE_X149Y249 DSP48_X4Y60:DSP48_X5Y99 RAMB18_X4Y60:RAMB18_X6Y99 RAMB36_X4Y30:RAMB36_X6Y49} endgroup - Tcl 콘솔에 경고가 표시되어도 무시하세요.
- 구현된 설계(Implemented Design)를 닫을 때 Vivado가 저장할 것인지 묻습니다. 방금 만든 Pblock은 쓸모가 없으므로 "No"를 선택하세요.
부분 재구성(Partial Reconfiguration) 프로젝트 설정
Xilinx의 UG909는 부분 재구성(Partial Reconfiguration)을 위한 두 가지 작업 절차를 제시합니다.
- 비프로젝트 방식(non-project flow, UG909 3장)입니다. Tcl 스크립트(script)를 직접 작성하고 실행하여 구현을 수행하는 방식입니다.
- 프로젝트 방식(project flow, 4장)입니다. Vivado의 GUI와 Vivado가 자동으로 생성하는 스크립트(script)를 사용하는 방식입니다.
이 글에서는 프로젝트 방식을 사용하겠습니다. 여기에는 몇 가지 제약이 있고, 그중 일부는 재구성 모듈에 포함되는 소스의 유형과 관련됩니다(특히 블록 디자인을 사용하는 경우). 어쨌든 프로젝트 방식으로 시작하는 편이 좋습니다. 프로젝트 방식이 생성하는 구현용 스크립트(script)는 필요할 때 비프로젝트 방식의 좋은 기반이 되기 때문입니다.
기존 프로젝트에서 부분 재구성(Partial Reconfiguration) 지원을 활성화하는 단계는 다음과 같습니다.
- Tools > Enable Dynamic Function eXchange…를 선택하고 "Convert"를 클릭합니다. GUI가 프로젝트를 부분 재구성(Partial Reconfiguration) 흐름으로 전환하는 것은 되돌릴 수 없다는 점을 알려줄 것이므로, 동의하면 됩니다. 실제로 실행되는 Tcl 명령은 다음과 같습니다.
set_property PR_FLOW 1 [current_project]
- 재구성 파티션의 최상위 모듈(top-level module)을 정하고, Vivado의 Project Manager에 있는 Sources 창에서 해당 소스 파일을 마우스 오른쪽 버튼으로 클릭합니다. "Create Partition Definition…"을 선택합니다. 이 옵션은 DFX를 활성화한 후에만 사용할 수 있는데, 방금 활성화했으니 괜찮습니다.
- "Create Partition Definition" 대화상자가 나타나며 두 가지를 입력하라고 요청합니다. 첫 번째는 Partition Definition의 이름입니다. 이 이름은 로직 계층 구조에서 다른 재구성 모듈을 삽입할 수 있는 위치를 가리키는 데 사용됩니다. 적당한 이름으로 "pr" 같은 것을 쓸 수 있습니다. 두 번째는 Reconfigurable Module Name입니다. 이 이름은 어떤 로직이 파티션에 들어가는지를 나타냅니다. 예를 들어 부분 재구성(Partial Reconfiguration)으로 오디오 필터를 교체한다고 하면, Reconfigurable Module Name으로 "lpf", "bpf", "hpf" 같은 것이 적절합니다. 각 이름이 어떤 필터인지 말해 주니까요. 그러한 이름이 최상위 모듈의 이름과 같아도 무방하며, 오히려 기능을 이해하는 데 도움이 될 수 있습니다.
- 이제 Sources 목록에서 선택한 모듈의 행에 노란색 다이아몬드 모양이 표시됩니다. 모듈 이름과 인스턴스 이름(예: "pr_block"과 "pr_block_ins")이 Verilog/VHDL 파일에 정의된 대로 함께 표시됩니다. 이 이름들은 파티션에 어떤 로직이 삽입되는지를 말해 주지 않으며, HDL에서의 이름을 반영할 뿐입니다. 파티션과 재구성 모듈은 같은 Sources 창의 "Partition Definitions" 탭에서 찾을 수 있습니다.
- 재구성 모듈 안에 IP 코어(IP core)의 인스턴스화(instantiation), 예를 들어 FIFO가 있다면, 해당 IP를 메인 프로젝트 소스("Hierarchy" 창)에서 마우스 오른쪽 버튼으로 클릭하고 "Move to configurable module…"을 선택하여 이동할 수 있습니다. 이에 해당하는 Tcl 명령은 다음과 같습니다.
move_files -of_objects [get_reconfig_modules lpf] [get_files /path/to/blkmem.xci]이렇게 하면 IP 코어(IP core)가 "Partition Definitions" 탭으로 이동합니다. - 그뿐 아니라 "Partition Definitions" 탭은 각 재구성 모듈에 대한 소스 계층 구조의 모음처럼 동작합니다. 예를 들어 재구성 모듈에 필요한 HDL 파일을 추가하려면 이 탭 아래의 "+"를 클릭하면 됩니다.
- 재구성 모듈이 준비되면 부모 구현(Parent Implementation)을 정의합니다. 부모 구현, 자식 구현, 마법사에 대한 자세한 내용은 이전 글을 참조하세요.
- Tools > Dynamic Function eXchange Wizard를 선택합니다.
- 시작 페이지에서 Next를 클릭하고, 재구성 모듈 편집 페이지에서도 Next를 클릭합니다.
- "Edit Configurations" 페이지에서 "+"를 클릭하여 구성(configuration)을 추가합니다. 기본 이름인 config_1을 그대로 사용해도 됩니다. 별로 중요하지 않습니다. 기본적으로 Vivado는 config_1에 해당하는 재구성 모듈을 올바르게 선택하는데, 지금으로서는 유일한 모듈이므로 당연한 일입니다.
- 다음 화면은 구성 런(configuration run)을 추가하는 화면입니다. 지금은 추가하지 마세요.
- 마법사를 마칩니다(Finish).
이 시점에서 프로젝트 구현을 시도하면 대부분 다음과 같은 오류로 실패합니다. "[DRC HDPR-30] Missing PBLOCK On Reconfigurable Cell: HD.RECONFIGURABLE cell 'pr_block_ins' must have PBLOCK assigned to itself or its descendant cells". 쉽게 말하면 플로어플래닝(floorplanning)이 필요하다는 뜻입니다.
플로어플래닝(floorplanning)
이 시점에서 프로젝트는 플로어플래닝(floorplanning) 작업을 진행할 수 있을 만큼만 설정되어 있습니다.
이 작업을 작은 단계로 나누기 전에 몇 가지 유의할 점을 언급해 두겠습니다.
- 정적 로직(static logic)을 위한 FPGA 영역은 가능한 한 작게 만들어야 하지만, 배치 및 배선(place and route)을 어렵게 만들어서는 안 됩니다. 앞서 "플로어플래닝 준비" 절에서 그 모양에 대한 대략적인 감을 잡아 두었을 것입니다.
- 정적 로직과 재구성 로직의 모양은 모두 가능한 한 단순해야 합니다. 될 수 있으면 단순한 사각형이 좋고, 라우팅에 어려움을 주지 않는 모양이어야 합니다.
- 정적 로직의 라우팅은 재구성 로직 영역을 가로지를 수 있습니다. 그러나 대부분의 경우 그 반대는 성립하지 않습니다.
- 이번 플로어플래닝 작업에서는 재구성 로직의 모양을 그립니다. 방금 언급한 두 가지 이유 때문에, 이 모양을 단순하게 유지하는 것이 중요합니다.
- 사용하는 FPGA의 플로어플래닝(floorplanning) 가능성과 제약을 잘 알고 있어야 합니다. 자세한 내용은 UG909의 6~8장에 나와 있습니다. 예를 들어 series-7 FPGA를 사용한다면, 영역의 경계를 클록 영역(clock region) 경계에 맞추는 것이 아마 가장 좋습니다.
이제 단계별로 나누어 보겠습니다.
- 프로젝트의 합성(synthesis)을 시작합니다(즉, synth_1 런을 시작). 재구성 모듈의 합성은 자동으로 OOC(Out-of-Context) 런으로 수행됩니다. 예를 들어 lpf_synth_1과 같습니다. OOC에 대한 자세한 내용은 마지막 글에서 다룹니다.
- 런이 완료되면 합성된 설계(Synthesized Design)를 엽니다. 이 시점에서는 재구성 모듈과 연결된 Pblock이 없으므로 구현(implementation)은 불가능합니다.
- 재구성 로직용 Pblock을 그립니다. 준비 단계와 달리, 이 Pblock은 재구성 로직과 연결되어야 합니다. 왼쪽 위 창이 Netlist 탭으로 열려 있는지 확인하고, 재구성 파티션에 들어가는 최상위 셀(예: "pr_block_ins")을 마우스 오른쪽 버튼으로 클릭합니다. Floorplanning > Draw Pblock을 선택하고 FPGA 위에 영역을 그립니다. 수행할 GUI 조작은 위의 "플로어플래닝 준비" 절에서 설명한 것과 같습니다. 즉, 슬라이스(slice)와 다른 로직 요소를 기준으로 선택하는 것입니다.
- 다시 말하지만, Pblock에 IOB를 포함하라는 제안이 나오면 수락하지 마세요. 나중에 처리할 때 Vivado가 멈출 수 있습니다.
- 이 작업에 너무 공을 들이지 마세요. Vivado가 문제를 지적해서 수정해야 할 가능성이 높기 때문입니다. 다시 한번 강조하지만, Pblock은 재구성 로직을 위해 그리는 것이며, 정적 로직(static logic)은 나머지 영역을 차지합니다.
- 이제 Pblock 속성(Pblock Properties) 창을 다룰 차례입니다. 이 창이 나타나지 않으면 디바이스 뷰에서 Pblock을 마우스 오른쪽 버튼으로 클릭하고 Pblock Properties…를 선택해야 할 수 있습니다.
- Pblock 속성 창에서 Properties 탭을 선택합니다.
- series-7 FPGA(Ultrascale 이상이 아닌 경우)에서는, 부분 비트스트림(partial bitstream)을 로드한 뒤 로직이 FPGA의 내부 리셋을 받도록 하려면 Pblock 속성 창에서 RESET_AFTER_RECONFIG를 설정하는 것이 좋습니다. 재구성 모듈 리셋에 대한 자세한 내용은 다음 글을 참조하세요. 이렇게 하면 다음과 같은 XDC 제약 조건이 생성됩니다.
set_property RESET_AFTER_RECONFIG true [get_pblocks pblock_pr_block_ins]
이 제약 조건은 무엇보다 플립플롭을 기본값으로 되돌립니다. 그러나 이는 HDL이나 다른 방식으로 로직에 정의된 리셋과는 아무 관련이 없다는 점에 유의하세요. 또한 series-7 FPGA에서는 이 기능을 사용하려면 Pblock의 세로 경계가 클록 영역(clock region)과 정렬되어 있어야 합니다.
Ultrascale FPGA 이상에서는 이 리셋이 항상 활성화되어 있습니다. - 또한 SNAPPING_MODE 속성이 있습니다. series-7 FPGA에서는 기본값이 정의되어 있지 않습니다(즉, OFF와 동일). 일부 FPGA에서는 이 값을 ROUTING이나 ON으로 설정해야 할 수도 있습니다. Ultrascale의 기본값은 ON입니다. 이에 대해서는 나중에 다루겠습니다.
- 다음으로 CTRL-S를 눌러 제약 조건을 저장합니다(또는 상단 바의 디스켓 아이콘을 클릭). 그러면 XDC 파일에 다음과 같은 몇 줄이 추가됩니다.
create_pblock pblock_pr_block_ins add_cells_to_pblock [get_pblocks pblock_pr_block_ins] [get_cells -quiet [list pr_block_ins]] resize_pblock [get_pblocks pblock_pr_block_ins] -add {SLICE_X40Y100:SLICE_X79Y149} resize_pblock [get_pblocks pblock_pr_block_ins] -add {DSP48_X2Y40:DSP48_X2Y59} resize_pblock [get_pblocks pblock_pr_block_ins] -add {RAMB18_X2Y40:RAMB18_X2Y59} resize_pblock [get_pblocks pblock_pr_block_ins] -add {RAMB36_X2Y20:RAMB36_X2Y29} - 합성된 설계(Synthesized Design)를 닫습니다.
- synth_1 런을 리셋합니다.
- 비트스트림(bitstream) 생성을 시도합니다("Generate Bitstream" 클릭). 이 구현의 목적은 플로어플래닝(floorplanning)에 결함이 있는지 확인하는 것입니다. 즉, Vivado가 그러한 결함에 대해 Critical Warning(치명적 경고)을 내는지 보는 것입니다. 설계를 검증하는 방법치고는 비전문적으로 들릴 수 있지만, 쉽고 확실한 방법입니다.
플로어플래닝 수정
부분 재구성(Partial Reconfiguration)에서 가장 달갑지 않은 부분이 아마 바로 이것일 것입니다. 플로어플래닝(floorplanning)을 정확하게 맞추는 일 말입니다. Remote Update(원격 업데이트) 용도로 작업하고 있다면 이 단계는 특히 중요합니다. 이 플로어플래닝이 프로젝트 수명 내내 그대로 유지되기 때문입니다.
플로어플래닝 수정이 필요한 이유는 크게 두 가지입니다. Critical Warning(치명적 경고)에 대응하기 위한 경우와, 나중 단계에서 FPGA 사용을 최적화하기 위한 경우입니다. 목표는 리소스 낭비를 줄이면서 동시에 배치 및 배선(place and route)에 장애물을 만들지 않는 것입니다.
수정 자체는 어렵지 않습니다. Pblock의 경계를 끌어서 조정하면 되니까요. 추가 사각형으로 Pblock을 확장하는 것도 쉽습니다. Pblock을 마우스 오른쪽 버튼으로 클릭하고 "Add Pblock Rectangle"을 선택하면 됩니다.
Critical Warning은 대개 어떤 수정이 필요한지 알려 줍니다. 그렇더라도 사용 중인 FPGA의 플로어플래닝 제약에 관한 Xilinx 사용자 가이드 UG909의 해당 장(6, 7 또는 8장)을 반드시 읽어 두어야 합니다.
이 절의 나머지 부분에서는 series-7 FPGA에서 발생할 수 있는 문제를 다룹니다. Ultrascale FPGA는 작업하기가 훨씬 쉽습니다.
series-7 FPGA에서 흔한 오류 중 하나는 인터커넥트 타일 컬럼(interconnect tile column)이 분할되는 것입니다. 예를 들면 다음과 같습니다.
[Constraints 18-993] The Pblock pblock_pr_block_ins has defined an area that causes the splitting of interconnect tile columns. Dynamic Function eXchange requires that the left and right paired interconnect tile columns cannot be split by a reconfigurable boundary. This is caused by either the left or right edge of a Pblock boundary, or by the Pblock spanning over logic types not included in the Pblock ranges. To avoid an unroutable situation, placement will be prohibited from both of these columns. To avoid placement restrictions, modify the Pblock to avoid splitting the two columns. The column of the split contains interconnect tile INT_L_X48Y299 (SLICE_X79Y299 SLICE_X78Y299). Please refer to the Xilinx document on Dynamic Function eXchange. Resolution: Set the Pblock property SNAPPING_MODE to value of ON, or modify the column/X specification of the pblock to avoid this edge.
그리고
[Constraints 18-996] The split between the left and right columns occurs between a reconfigurable Pblock and Static logic. The static sites are not reconfigurable. The Pblock should be adjusted to remove the column from the Pblock, unless the excluded reconfigurable and static sites are not needed for the design. Note that adjusting the Pblock will prevent prohibits and improve placement of the design, but may reduce the routability if the removed sites were needed to span across the static logic. Failure to modify the Pblock may lead to an unplaceable design if these prohibited sites are required by the design. Resolution: Set the Pblock property SNAPPING_MODE to value of ON, or modify the column/X specification of the pblock to avoid this edge. and
이 문제를 해결하려면 첫 번째 경고에서 제안한 대로 Pblock의 SNAPPING_MODE 속성을 ROUTING 또는 ON으로 설정하세요. 아마 ROUTING만으로는 충분하지 않을 가능성이 높으므로 ON을 선택하세요. 이렇게 하면 XDC 파일에 다음과 같은 종류의 제약 조건이 많이 추가될 것입니다.
set_property PROHIBIT true [get_sites SLICE_X79Y349] set_property PROHIBIT true [get_sites SLICE_X78Y349] [ ... ] set_property PROHIBIT true [get_sites SLICE_X79Y191] set_property PROHIBIT true [get_sites SLICE_X78Y191] set_property PROHIBIT true [get_sites PMV_X0Y2] set_property PROHIBIT true [get_sites SLICE_X36Y190] set_property PROHIBIT true [get_sites SLICE_X37Y190] [ ... ] set_property PROHIBIT true [get_sites SLICE_X79Y176] set_property PROHIBIT true [get_sites SLICE_X78Y176] set_property PROHIBIT true [get_sites T14] set_property PROHIBIT true [get_sites R15] set_property PROHIBIT true [get_sites XADC_X0Y0] set_property PROHIBIT true [get_sites SLICE_X36Y175] set_property PROHIBIT true [get_sites SLICE_X37Y175] [ ... ]
그리고 이런 줄이 계속 이어집니다.
슬라이스(slice) 사이트에 대한 PROHIBIT 설정은 앞서 언급한 Critical Warning을 잠재우는 역할을 합니다. 나머지 PROHIBIT 지정은 기하학적 영역에는 포함되지만, 사용 중인 FPGA에서 부분 재구성(Partial Reconfiguration)이 허용되지 않는 로직 사이트에 대해 추가됩니다. Ultrascale FPGA 이상에서는 PROHIBIT 줄이 아예 없거나 훨씬 적게 생성됩니다.
단순히 Critical Warning을 잠재우는 것이 목적이라면, PROHIBIT가 있는 줄을 모두 제거하고 슬라이스(slice)에 대한 줄 하나만 남겨도 괜찮습니다. Vivado가 SNAPPING_MODE 변경에 대응하여 추가한 슬라이스 범위를 하나로 합치면 됩니다. 그 범위를 다음과 같은 형태로 바꾸는 것입니다.
set_property PROHIBIT true [get_sites -range {SLICE_X79Y0 SLICE_X79Y349}]
이런 형태의 줄은 XDC 파일을 방대하게 만들지 않으면서 인터커넥트 분할 문제를 해결할 수 있는 방법입니다.
어쨌든 다음과 같은 줄도 XDC 파일에 나타날 수 있습니다. 이 줄을 제거해도 문제없는 것으로 보입니다.
set_property HD.PLATFORM_WRAPPER true [get_cells pr_block_ins]
XDC 파일을 Critical Warning이 발생하지 않는 최소한의 내용으로 줄이는 것이 다소 피상적으로 보일 수 있습니다. 그러나 그 대안은 나중에 혼란을 부르는 거대한 제약 파일입니다. 제 경험상 이러한 종류의 경고가 없다는 것은 설계의 플로어플랜이 괜찮다는 승인으로 받아들여도 됩니다.
SNAPPING_MODE 속성을 다시 OFF로 되돌리면 XDC를 어떻게 바꿨든 문제가 다시 생길 가능성이 높습니다.
재구성 모듈 추가
지금까지의 구현은 사실상 몇 가지 제약이 추가된 계층적 설계(Hierarchical Design)와 같은 결과를 만듭니다. 부분 비트스트림(partial bitstream)이 생성되기는 하지만, 그것은 거의 쓸모가 없습니다. 로드해도 같은 설계가 유지될 뿐이기 때문입니다.
따라서 목표는 다른 재구성 모듈을 기반으로 하는 또 다른 부분 비트스트림을 만드는 것입니다. 그러려면 자식 구현(Child Implementation)을 추가해야 합니다.
이 글을 계속 읽기 전에 이전 글을 잘 기억해 두세요. 특히 부모 구현(Parent Implementation)과 자식 구현(Child Implementation), 그리고 Dynamic Function eXchange Wizard에 관한 부분이 중요합니다. 또한 이 글의 앞부분에서 설명했듯이, "Partition Definitions" 탭에는 현재 정의된 재구성 모듈과 해당 소스가 들어 있다는 점도 기억하세요.
Tools 메뉴에서 Dynamic Function eXchange Wizard를 열고 시작 창에서 Next를 클릭합니다.
"Edit Reconfigurable Modules" 창에서 "+"를 클릭합니다. 그러면 재구성 모듈을 추가하는 대화상자가 열립니다. 이 대화상자에서 신경 쓸 것은 Reconfigurable Module Name뿐입니다. 앞서 설명한 대로, 이 이름은 재구성 로직을 식별하는 데 사용됩니다.
대화상자에서는 이 모듈을 파티션 정의(partition definition) 이름과 연결하도록 요구합니다. 하지만 어차피 파티션 정의는 하나뿐입니다. 이 글은 파티션이 하나만 정의되어 있다고 가정하기 때문입니다.
계속하려면 Verilog/VHDL 소스 파일을 최소한 하나는 추가해야 합니다. 더 많은 파일은 나중에 "Partition Definitions" 탭에서 추가할 수 있습니다. 이 재구성 모듈의 최상위 모듈 이름을 명시해 두는 것도 나쁘지 않습니다. 특히 소스 파일만으로는 어떤 로직인지 분명하지 않은 경우에 유용합니다.
마법사로 돌아와 다시 Next를 클릭하여 "Edit Configurations" 창으로 갑니다. "+"를 클릭하고 구성(configuration) 이름을 입력합니다. 이 이름이 중요한 이유는 Design Runs 창에 표시되기 때문뿐입니다. config_2 같은 이름이면 충분합니다.
구성 목록에 새 행이 나타납니다. 파티션에 해당하는 열의 재구성 모듈을 변경하여, 각 구성이 서로 다른 재구성 모듈을 갖도록 하세요.
마지막 창은 Edit Configuration Runs로, 런(run)을 구성(configuration)에 할당하는 곳입니다. 쉬운 방법은 이 창에 나열된 모든 런(있다면)을 삭제하고 "automatically create configuration runs"를 클릭하는 것입니다. 그러면 수동으로 했을 작업을 자동으로 해 줍니다. 부모 런을 만들고 "impl_1"이라고 이름을 붙인 다음, 자식 런들을 만들고 원하는 이름을 붙이고, 그 자식 런들이 "impl_1"의 자식이 되도록 하는 것입니다.
마법사는 각 런에 대해 구성을 자동으로 선택하지만, 그것은 쉽게 바꿀 수 있습니다. 중요한 것은 부모 런에 어떤 구성이 연결되는지뿐입니다.
그리고 참고로, 마법사에서 모든 런을 삭제하면 모든 자식 런이 사라지지만 impl_1은 남아 있습니다.
마지막으로: 설계 구현
비트스트림을 생성하려면 평소처럼 Vivado에서 "Generate Bitstreams"를 클릭하면 됩니다. 이전 글에서 이미 언급했듯이, 부분 재구성(Partial Reconfiguration) 프로젝트에서는 구성(configuration)마다 비트스트림이 두 개 또는 세 개 생성됩니다.
예를 들어 Ultrascale FPGA에서는 비트 파일(bit file)이 다음과 같이 생성될 수 있습니다.
- theproject.bit: 현재 구성과 관련된 정적 로직(static logic)과 재구성 로직이 포함된 초기 비트스트림(bitstream) 파일입니다.
- pr_block_ins_lpf_partial.bit: 현재 구성과 관련된 재구성 로직을 로드하는 부분 비트스트림(partial bitstream)입니다.
- Ultrascale에서는 pr_block_ins_lpf_partial_clear.bit도 생성됩니다. 현재 구성이 FPGA에 이미 들어 있는 경우, 다른 부분 비트스트림을 로드하기 전에 먼저 로드해야 하는 비트스트림입니다.
모든 구현에 대해 동일한 수의 비트스트림 파일이 생성된다는 점에 유의하세요. 다시 말해 자식 구현(Child Implementation)에 대해서도 초기 비트스트림 파일이 생성됩니다. 따라서 자식 구현 중 하나의 초기 비트스트림으로 FPGA를 로드한 다음, 그 상태에서 계속 진행하는 것도 충분히 가능합니다.
PCIe 또는 USB 3.x를 통해 부분 비트스트림을 간단히 로드하는 방법은 이 페이지를 참조하세요.
어떤 구현에서도 Pblock이나 플로어플래닝(floorplanning)과 관련된 Critical Warning이 발생하거나 실패해서는 안 됩니다. 그러한 문제는 이미 해결되었기 때문입니다. 만약 이런 문제가 여전히 발생한다면, 앞서 설명한 대로 플로어플래닝을 수정해야 합니다.
때때로 "Generate Bitstream"을 클릭했는데 변경 사항이 자식 구현(child implementation)에만 있을 경우, Vivado가 "Bitstream generation has already completed and is up-to-date. Re-run anyway?"라고 응답할 수 있습니다. 다소 혼동스럽지만 "Yes"를 클릭하면 자식 구현이 제대로 실행됩니다. 자식 구현(Child Implementation)과 관련된 이 모든 것은 Vivado에 다소 부가적으로 붙은 기능에 가깝습니다. 그래서 구현 중 상태 표시줄에 "write_bitstream complete. Child running" 같은 문구가 표시되는 것입니다.
결과 검토
부분 재구성(Partial Reconfiguration)은 배치와 관련이 깊으므로, 구현된 설계를 검토해 보는 것이 좋습니다. 특정 구현을 열려면 "Open Implemented Design"을 마우스 오른쪽 버튼으로 클릭하고, 다시 표시되는 "Open Implemented Design" 메뉴 위에 커서를 올린 다음, 목록에서 열 구현을 선택하면 됩니다. 목록에 어떤 구현이 없다면 아마 이미 열려 있는 것입니다.
구현된 설계 뷰의 Netlist 창에서 재구성 로직의 최상위 행을 마우스 오른쪽 버튼으로 클릭하고 "Highlight Leaf cells"를 선택해 보세요. 그런 다음 정적 로직(static logic)에 대해서도 다른 색으로 같은 작업을 해 보세요.
같은 오른쪽 클릭 메뉴에는 "Show Connectivity"도 있습니다. 이 기능은 서로 연결된 로직 요소 사이에 직선의 흰색 선을 그립니다. FPGA의 실제 라우팅 경로는 당연히 다르므로, 이 선들이 플로어플래닝(floorplanning)의 어떤 영역을 가로지르는지는 의미가 없습니다. 그럼에도 연결 상태를 살펴보면 플로어플랜의 전체적인 구성이 도구를 얼마나 어렵게 만드는지 알아차리는 데 도움이 될 수 있습니다.
분명히 정적 로직(static logic)에 속하는 것처럼 보이는 일부 셀이 재구성 영역 안에 배치되거나, 그 반대의 경우도 상당히 정상적이고 문제없습니다. 주의해야 할 점은 어딘가에 혼잡이 있어 보이는 경우입니다. 로직이 전체적으로 또는 특정 영역에서 너무 빽빽하게 들어차 보인다면, 가능하다면 플로어플래닝(floorplanning)을 변경하여 완화할 수 있습니다.
또 하나 살펴볼 것은 파티션 핀(partition pin)의 위치입니다. 파티션 핀은 디바이스 뷰(Device view)에서 흰색 가로 막대로 표시됩니다. 다음과 같이 나타납니다(이미지를 클릭하면 확대됩니다).
이전 글에서 언급했듯이 파티션 핀(partition pin)은 재구성 파티션 안 어디에나 있을 수 있습니다. 그런데 파티션 핀이 파티션의 가장자리에서 멀리 떨어져 있다면, 부모 구현(Parent Implementation) 중에 라우터가 타이밍 문제로 어려움을 겪었다는 신호일 수 있습니다.
다음 Tcl 명령을 사용하면 파티션 핀의 좌표를 텍스트 목록으로 얻을 수도 있습니다. pr_block_ins를 실제 재구성 로직 셀의 이름으로 바꾸세요.
foreach s [get_pins -of [get_cells pr_block_ins]] { set partpin [get_pplocs -quiet -pins [get_pins $s]] ; puts "$s => $partpin"; }
파티션 핀(partition pin)의 좌표는 슬라이스(slice)가 아니라 CLB 그리드에 해당합니다. 표시된 그림에서 이 핀들은 "Cell pins"라고 불립니다.
셀의 핀 중 일부에 파티션 핀(partition pin)이 할당되지 않을 수도 있습니다. 이는 재구성 모듈의 포트 목록(및/또는 벡터의 폭)과 정적 로직 모듈이 그 모듈을 인스턴스화한 방식 사이에 불일치가 있을 때 발생합니다. 이러한 불일치는 Verilog에서 완전히 허용되지만, 결과가 바람직하지 않을 수 있습니다. 앞의 Tcl 명령을 실행하면 연결되지 않는 포트를 발견할 수 있습니다. 특히 이런 불일치가 의도된 것이 아닐 때 유용합니다.
이것으로 Vivado 프로젝트 설정에 대한 기술적 설명을 마칩니다. 다만 다음 글에서는 FPGA 설계의 중요한 한 측면을 다룹니다: 로직 교체가 안정적이고 원활하게 이루어지도록 보장하는 방법입니다.
