홈으로 돌아가기

RISC-V YRV FPGA용 ROM으로 SD 카드

이 기사는 Tang Nano 9K 보드의 RISC-V YRV 코어 실행 코드 저장소로 SD 카드를 사용하는 방법을 설명합니다. RAM 캐싱으로 512바이트 섹터 읽기 FSM 구현. HELO 출력 테스트로 기능성 확인.

RAM 대신 SD: 메모리 카드로 FPGA 상의 RISC-V YRV
Advertisement 728x90

Tang Nano 9K FPGA에서 RISC-V YRV 코어를 위한 SD 카드 코드 저장 방식

Tang Nano 9K와 같은 저가형 FPGA 보드에서는 합성 가능한 리소스가 제한되어 내장 블록 RAM 용량이 매우 작습니다. 따라서 전체 프로그램 코드를 온칩 RAM에 저장하는 것은 현실적으로 불가능합니다. 스택 및 데이터 영역을 위해 확보할 수 있는 공간은 고작 16–20 KB에 불과해, 간단한 테스트는 가능하지만 완전한 펌웨어 실행에는 부족합니다. 이 글에서는 SD 카드를 외부 읽기 전용 메모리(ROM)로 활용하는 방안을 제시합니다. 512바이트 단위의 섹터 기반 읽기 방식으로 구현되며, 기능상 QSPI 플래시에서의 eXecute-In-Place(XIP)와 유사하지만, SPI 프로토콜의 제약 조건에 맞게 최적화되었습니다.

YRV 코어는 16비트 버스를 통해 메모리와 인터페이스합니다. 읽기/쓰기 로직은 Verilog로 구현됩니다:

always @ (posedge clk) begin
  if (mem_trans[0]) begin
    mem_rdata[31:24] <= mcu_mem_bank3 [mem_addr[13:2]];
    mem_rdata[23:16] <= mcu_mem_bank2 [mem_addr[13:2]];
    mem_rdata[15:8]  <= mcu_mem_bank1 [mem_addr[13:2]];
    mem_rdata[7:0]   <= mcu_mem_bank0 [mem_addr[13:2]];
  end
  if (mem_wr_byte[3]) mcu_mem_bank3 [mem_addr_reg[13:2]] <= mem_wdata[31:24];
  if (mem_wr_byte[2]) mcu_mem_bank2 [mem_addr_reg[13:2]] <= mem_wdata[23:16];
  if (mem_wr_byte[1]) mcu_mem_bank1 [mem_addr_reg[13:2]] <= mem_wdata[15:8];
  if (mem_wr_byte[0]) mcu_mem_bank0 [mem_addr_reg[13:2]] <= mem_wdata[7:0];
end

SD 섹터 캐싱을 위한 상태 머신

RAM 용량은 단 하나의 섹터(512바이트)로 축소됩니다. CPU가 현재 캐시된 섹터 내 주소에 접근하면 mem_ready 신호가 즉시 활성화되어 실행이 중단 없이 계속됩니다(ARM AHB-Lite의 HREADY와 유사). 반면, 캐시 미스—즉, 현재 캐시된 섹터 범위를 벗어난 주소 접근 시—유한 상태 머신(FSM)이 동작하여 SD 카드에서 새 섹터를 읽어옵니다. byte_cnt 카운터가 읽기 진행 상황을 정확히 추적합니다.

Google AdInline article slot

FSM은 다음 세 가지 상태로 구성됩니다:

  • IDLE: 메모리 액세스 요청을 대기합니다.
  • READ: 요청된 512바이트 섹터를 SD 카드에서 읽습니다.
  • START: 읽기 명령(rd)을 전송하여 동작을 시작합니다.

SD 컨트롤러는 오픈소스 프로젝트인 FPGA-SDcard-Reader와 MIT 6.111에서 파생되었으며, Tang Nano 9K의 SPI 인터페이스(데이터 라인 DAT1/DAT2는 미사용)에 맞게 재설계되었습니다. 초기화는 신뢰성을 위해 400 kHz로 수행되며, 전원 재시작 없이도 안정적으로 동작합니다.

YRV Plus 프로젝트 내 통합

labs/99_experimental/99_03_yrv_sd 디렉터리에 구현되었으며, basics-graphics-music 기반 프로젝트 위에 구축되었습니다. 설정 이름 tang_nano_9k_tm1638_sd는 SD 카드 핀 전압을 3.3V로 지정합니다. 출력 장치로 TM1638을 사용하며, port0은 BCD 세그먼트 데이터를, port1은 숫자 위치 선택을 담당합니다.

Google AdInline article slot

"HELO" 문자열을 표시하는 어셈블리 예제(각 문자는 별도 섹터에 저장):

li s0, 0xFFFF0000    # port0 — 세그먼트 데이터
li s1, 0xFFFF0002    # port1 — 숫자 위치

main_loop:
  # 'H' (3번째 자리)
  li t1, 0x8
  sh t1, 0(s1)
  .rept 100
  nop
  .endr
  li t2, 0b00110111
  sh t2, 0(s0)
  .rept 1000
  nop
  .endr
  # E, L, O에 대해서도 동일한 방식 반복

펌웨어는 HxD 도구를 사용해 섹터 단위로 기록됩니다.

테스트 결과:

Google AdInline article slot
  • 문자 사이에 약 1,000개의 NOP 명령어가 삽입되어 실제 워크로드를 반영했습니다.
  • "HELO"가 네 개의 7세그먼트 디지털 표시기에서 선명하게 나타납니다.
  • 리셋 및 초기화가 신뢰성 있게 동작하며, 전원 재시작이 필요 없습니다.
  • SD 카드를 제거하면 실행이 깔끔하게 중단됩니다.

핵심 요약

  • SPI 모드 SD 카드는 512바이트 섹터 캐시와 결합 시 XIP와 유사한 실행 환경을 제공합니다.
  • FSM 설계로 지연 시간을 최소화했으며, mem_ready 신호를 통해 CPU 동기화를 유지합니다.
  • 신호 타이밍이 밀리초 단위인 저대역폭 애플리케이션에 최적입니다.
  • 더 매끄러운 성능을 원한다면 캐시 크기를 1–2 KB로 확장하는 것을 권장합니다.
  • Gowin FPGA 기반 YRV 코어와 완벽 호환되며, 외부 SRAM 없이도 작동합니다.

최적화 가능성

현재 구현은 섹터 읽기당 6클럭 사이클 이상 소요되며(QSPI는 6클럭), 캐시를 2–4 섹터로 확장하면 캐시 미스 발생률을 크게 낮출 수 있습니다. 또한 YRV의 원래 메모리 로직을 반영한 멀티뱅크 버퍼링도 실현 가능합니다. 고급 개발자를 위한 제안: Wishbone 또는 AXI 어댑터와 연동해, 이 솔루션을 보다 규모가 큰 SoC 설계로 확장하세요.

— Editorial Team

Advertisement 728x90

다음 읽기