이번 편에서도 마이크로컨트롤러(uC)와 필드 프로그래머블 게이트 어레이(FPGA) 간 인터페이스를 계속 알아볼 것입니다.
- 1부에서는 대규모 시스템 개발의 방향을 제시하는 베릴로그 설계 철학에 대해 소개하고 있습니다. 이 글은 클록 경계, 스트로브 사용, 그리고 이중 버퍼의 필요성과 같은 레지스터 전송 수준(RTL)의 설계 지침을 다루는 매우 중요한 부분입니다.
- 2부에서는 SPI 프로토콜에 대해 소개하고 있습니다. 이 프로토콜은 데이터 무결성을 보장하기 위해 가변 페이로드 길이와 순환 중복 검사(CRC)와 같은 컨셉을 사용하는 802.3 이더넷 프레임에서 변형된 것입니다.
이전 편의 핵심인 명령 및 응답 프레임을 아래 그림 1에 다시 제시하였습니다. 명령 프레임은 uC에서 FPGA로 흐르는 정보의 프로토콜을, 응답 프레임은 FPGA에서 uC로 흐르는 정보의 프로토콜을 설명합니다. 이 프로토콜은 SPI를 이용하는 전이중 통신을 위해 설계되었으며, 이로 인해 FPGA 하드웨어와 관련된 타이밍에 세심한 주의가 요구됩니다. 명령 프레임을 수신하는 동시에 응답 프레임을 실시간으로 생성해야 합니다. 예를 들어, uC가 FPGA로 길이 바이트를 전송하는 동안, FPGA는 동시에 사용자 정의 상태 플래그를 전송합니다. 마찬가지로, uC가 명령 프레임의 CRC를 전송하는 동안, FPGA는 응답 프레임의 CRC를 전송합니다.
이러한 실시간 스트리밍 요구사항은 설계를 복잡하게 만듭니다. 따라서 이번 편에서는 최상위 계층 FPGA 구현을 블록 다이어그램 형식으로 설명하는 데 집중하였습니다. 이후 편에서는 이중 버퍼 모듈에 대한 심층 분석을 시작으로, 개별 FPGA 블록들을 자세히 다룰 예정입니다.
그림 1: uC와 FPGA 간 SPI 프로토콜의 기반이 되는 명령 및 응답 프레임.
최상위 계층 관점에서 바라본 uC에서 FPGA로의 데이터 전송
최상위 계층에서 바라본 FPGA 하드웨어가 그림 2에 나와 있습니다. 하드웨어의 동작을 더 잘 이해하기 위해 오른쪽에서 왼쪽으로 이동하며 개략적으로 살펴보겠습니다. 아직 1부를 보지 않으셨다면, 시스템 수준 설계와 관련된 몇 가지 도전 과제, 특히 이중 버퍼 부분을 다시 읽어보시는 것도 좋습니다.
오른쪽 상단에는 펄스 폭 변조기(PWM) 모듈이 있습니다. 이 모듈은 16비트 인터페이스를 갖추고 있으며, 동시에 데이터는 그림 1과 같이 SPI를 통해 8비트 형식으로 전송된다는 암묵적인 전제가 있습니다. 그러나 PWM이 한 번에 한 바이트씩 업데이트되면, 예상치 못한 동작이 발생할 수 있기 때문에 문제가 됩니다. 대신, 모든 16비트를 동기화하여 레지스터에 저장하면 하나의 클럭 사이클 내에 모든 16비트가 PWM에 전달될 수 있습니다.
이러한 동시 업데이트는 이중 버퍼에 의해 수행됩니다. 이 모듈은 블록 다이어그램의 중앙에 있는 SPI 하드웨어와 다양한 버퍼 및 제어 모듈로부터 데이터를 수신합니다. 더블 버퍼는 두 바이트가 모두 수신될 때까지 대기한 후, PWM을 동시에 업데이트합니다. 그런 다음, PWM 모듈은 예를 들어 다음 PWM 듀티 사이클의 시작 시점과 같은 정해진 시점에 변경 사항을 적용합니다.
각 이중 버퍼는 그림 3과 같이 바이트 폭과 주소라는 두 매개변수로 인스턴스화됩니다. 바이트 폭은 16비트 PWM 또는 32비트 직접 디지털 합성기(DDS)와 같이 해당 하드웨어에 맞게 선택됩니다. 주소는 그림 1에 제시된 명령 프레임과 연관됩니다. PWM에 듀티 사이클을 설정하기 위한 최소 명령 프레임 0x07, 0x00, 0x00, 0x02, 0x00, 0xAA, 0x55, 0xFD, 0x07을 예로 들어 보겠습니다. 이 예제에서 우리는 0xAA55라는 듀티 사이클 값을 0x0200에 위치한 16비트 PWM에 쓸 것입니다. 0x0000으로 설정된 읽기 주소는 이 예제에서 중요하지 않습니다. CRC 모듈은 이후 편에서 자세히 다룰 예정입니다.
그림 2: FPGA의 데이터 흐름을 보여주는 최상위 계층 블록 다이어그램.
왼쪽으로 계속 이동해 보면, 메시지 라이터가 있으며, 이 모듈은 프레임 버퍼와 CRC 검증기로부터 데이터를 입력 받으며, 이들은 다시 SPI 인터페이스로부터 데이터를 입력 받습니다. 메시지 라이터는 CRC 유효 스트로브(CRC Valid Strobe)에 의해 트리거됩니다. 이 스트로브는 그림 1에 제시된 명령 프레임이 수신되었고 수신된 CRC와 CRC 검증기에서 계산한 CRC가 일치함을 나타냅니다. 메시지 라이터가 활성화되면, 이중 버퍼 라이터는 프레임 버퍼에 있는 내용을 PWM, DAC, 그리고 DDS와 같은 다양한 FPGA 하드웨어 모듈과 연결된 이중 버퍼로 전송합니다.
프레임 버퍼는 데이터 무결성 검증 절차의 핵심적인 모듈입니다. 우선, 프레임 버퍼가 SPI 인터페이스를 통해 스트리밍되는 데이터를 수집하면, 동시에 CRC 검증기는 스트리밍된 데이터에 대한 CRC를 계산합니다. 그림 1에서 볼 수 있듯이, CRC는 프레임 끝에 붙어 있습니다. 따라서, CRC와 프레임 무결성 검증은 전체 프레임이 수신되기 전까지는 알 수 없습니다. 프레임 버퍼는 CRC가 완료될 때까지 시간을 확보해주기 때문에 중요하며, FPGA 하드웨어가 해당 데이터를 처리하기 전에 데이터를 검증할 수 있습니다. 따라서 프레임 버퍼는 손상된 프레임으로 인한 변경을 복구할 필요가 없습니다.
그림 1과 그림 2의 프레임 프로토콜에서 알 수 있듯이, 길이, 읽기 및 쓰기 주소 필드, 그리고 페이로드 모두 이중 버퍼 라이터에 의해 처리됩니다. CRC가 검증되었다면, 이중 버퍼 라이터는 기준 주소와 첫 번째 페이로드 바이트를 설정합니다. 그런 다음, 이중 버퍼 내 첫 번째 1바이트 크기의 버퍼를 활성화하는 쓰기 스트로브를 전송합니다. 이후 이중 버퍼 라이터는 다음 데이터로 이동하여 이 과정을 N번 반복합니다. 여기서 N은 프레임 길이 필드에서 5바이트의 헤더 필드를 뺀 값입니다. 그림 3의 이중 버퍼 구조를 살펴보면 이 과정을 더 잘 이해할 수 있습니다.
그림 3: 이중 버퍼의 블록 다이어그램 표현.
각 이중 버퍼는 기준 주소와 길이로 인스턴스화되어 있으며, 동작 순서가 제어됩니다. 이중 버퍼 내 첫 번째 1바이트 크기의 버퍼가 모두 채워지면, 이중 버퍼는 자동으로 해당 정보를 두 번째 버퍼로 전송합니다. 이러한 자동 전송은 이중 버퍼의 동작을 제어할 필요가 없기 때문에 메시지 라이터의 설계를 단순화시켜 줍니다. 대신, 프레임 페이로드를 한 바이트씩 순차적으로 전송하기만 하면 됩니다.
uC에서 FPGA로의 데이터 스트림으로 넘어가기 전에, 이중 버퍼 라이터의 속도에 대해 언급하고자 합니다. 그림 2에 나오는 모든 모듈은 동일한 클럭 도메인으로 동기화되어 있습니다. CRC 검증기의 스트로브를 수신하면, 이중 버퍼 라이터는 클럭에 의해서만 제한되는 속도로 이중 버퍼에 데이터를 쓸 것입니다. 그 결과, 100% 가득 찬 프레임 버퍼를 약 5us 이내에 읽을 수 있습니다.
버퍼의 읽는 위치는 8’b05에서 8’bFF입니다. 동시에, uC는 프레임 버퍼가 비워지는 동안 새로운 프레임을 시작하여 프레임 버퍼를 병행해 채울 수 있습니다. uC가 버퍼를 채우는 속도보다 더 빠르게 FPGA 메시지 라이터가 버퍼를 비우기 때문에 간섭은 문제가 되지 않습니다. 이는 SPI 모듈이 쿼드 또는 옥탈 SPI로 교체되더라도 여전히 유효해야 합니다.
기술 팁: 아이러니하게도, 이 글에서 설명한 uC에서 FPGA로의 인터페이스는 FPGA 기반 소프트 코어 프로세서의 도움을 받을 수 있습니다. 즉, uC에서 FPGA로의 인터페이스를 위해 FPGA 내부에 전용 uC를 인스턴스화하는 것입니다. Xilinx FPGA의 경우, PicoBlaze와 같은 컨트롤러가 적합한 선택이 될 수 있습니다. 이 소형 상태 머신은 SPI 모듈, 여러 이중 버퍼와 그에 관련된 주변 장치를 제외한 모든 것을 대체할 수 있습니다. 어셈블러로 코딩할 수 있는 능력에 따라, 이 방법이 명쾌한 솔루션이 될 수도 있습니다. 전통적인 C 언어로 프로그래밍 할 수 있는 MicroBlaze와 같은 강력한 대안도 있으며, Xilinx Zynq를 사용하면 아예 uC를 FPGA에 옮길 수도 있습니다. 대부분의 FPGA 플랫폼에서 이와 유사한 옵션을 사용할 수 있습니다.
최상위 계층 관점에서 바라본 FPGA에서 uC로의 데이터 전송
이 연재 글에서 설명하는 SPI 인터페이스는 그림 1에서 볼 수 있듯이 읽기 및 쓰기 주소를 각각 전송할 수 있는 전이중 방식입니다. 이는 스트리밍 프로세스로 uC가 보낸 데이터가 MOSI 라인을 통해 FPGA의 클록에 동기화되어 입력되는 동시에 MISO 라인을 통해 uC로 데이터를 전송합니다. 이제 그림 2에 설명된 RTL 기반의 FPGA 전송 프로세스를 오른쪽에서 왼쪽으로 신호의 흐름을 따라 살펴보겠습니다.
이 연재글의 1부에서 설명하였듯이, 이중 버퍼 프로세스가 기본적으로 필요합니다. 이는 그림 2의 오른쪽 하단에 나와있는 엔티티들에서 특히 잘 드러납니다. FPGA의 선택 스위치, 16비트 DAC, 사용자 정의 상태 플래그 및 에러 메시지 카운트가 모두 레지스터화되어 있는 것을 확인할 수 있습니다. 각 SPI 프레임이 시작될 때 데이터는 캡처됩니다. 이렇게 레지스터에 저장하면 데이터가 uC로 다시 스트리밍 될 때 값이 변경되지 않도록 합니다. 이는 다중 바이트 데이터 중 한 바이트만 업데이트할 때 발생할 수 있는 위험을 제거합니다.
FPGA에서 uC로 스트리밍되는 모든 데이터는 그림 2의 오른쪽 하단에 있는 멀티플렉서(mux)로 전달됩니다. 멀티플렉서의 선택 과정은 그림 1에 나와있는 명령 프레임의 읽기 주소에 따라 결정됩니다. SPI 인터페이스가 클럭에 동기화된 한 바이트를 입력 받으면, 제어 로직은 멀티플렉서를 다음 순서의 읽기 주소로 이동시킵니다. 이 동작을 통해 읽기와 쓰기 주소를 독립적으로 처리하는 전이중 통신이 가능해집니다. 예를 들어, PWM에 데이터를 쓰는 동시에 FPGA의 슬라이드 스위치를 읽을 수 있습니다. 단순화를 위해, 그림 2의 멀티플렉서는 입력은 N 바이트 그리고 출력은 1 바이트로 간략하게 표현되어 있습니다.
그림 2에 CRC 검증기가 두 개 인스턴스화되어 있는 것을 확인할 수 있습니다. 앞서 설명한 것처럼, 위쪽 모듈은 uC에서 FPGA로의 데이터 스트림에, 아래쪽 모듈은 FPGA에서 uC로의 데이터 스트림에 사용됩니다.
FPGA에서 uC로의 데이터 흐름을 오른쪽에서 왼쪽으로 계속 따라가 보면, 큰 멀티플렉서의 출력이 CRC 검증기에 전달되는 것을 확인할 수 있습니다. 이 출력은 작은 멀티플렉서를 통과해 SPI 인터페이스로 전달되고 최종적으로 MISO 라인으로 전송됩니다. CRC 검증기 모듈은 스트리밍 되는 데이터에 대해 CRC 값을 생성합니다. 작은 멀티플렉서를 통해 이 CRC는 FPGA에서 uC로 전송되는 메시지의 끝에 덧붙여집니다. 이 스위칭 동작은 프레임 버퍼에 저장된 Num_bytes에 대한 MSG 리더 컨트롤의 판단에 따릅니다.
여러분의 의견과 제안을 환영합니다. 특히 고급 RTL 시스템 설계 방법론에 대한 추가 논의를 환영합니다.
감사합니다,
APDahlen
영문 원본: Implementing a Robust Microcontroller to FPGA SPI Interface: Part 3 - FPGA Top level Modules