디스크립터가 필요한 이유
Linux dmaengine 문서는 연속된 블록 하나만 옮기는 컨트롤러를 먼저 가정하고, 흩어진 버퍼를 연속 버퍼로 옮기는 일을 scatter-gather라고 부릅니다. dmaengine은 적어도 mem2dev 전송에서 scatter-gather 지원을 요구하므로, 하드웨어가 지원하지 않으면 드라이버가 소프트웨어로 구현해야 합니다. 하드웨어 SG는 보통 전송할 조각들의 table이나 linked list를 받아 차례로 처리합니다. dmaengine provider 문서
| 구현 | 디스크립터 위치 | 다음 디스크립터를 찾는 방법 |
|---|---|---|
| Altera 구형 SGDMA | 메모리의 디스크립터 구조체 | 각 디스크립터의 next_desc_ptr |
| Altera mSGDMA(prefetcher 없음) | dispatcher의 descriptor FIFO | 드라이버가 하나씩 FIFO에 써 넣습니다. |
| Lattice VDMA 데모 | 호스트 메모리의 SG-List와 FPGA의 descriptor buffer | SGB 레지스터에 각 SG-List의 첫 항목 주소와 개수가 들어 있습니다. |
- TSE 전용 dmaops
메모리의 next 포인터 체인과 HW_OWNED
- dmaengine provider
준비된 디스크립터 → FIFO에 control을 마지막으로 기록
- Lattice VDMA 데모
SG-List 시작 주소 + 항목 수 → FPGA descriptor buffer
API 이름이 같거나 SG라는 말을 쓴다고 디스크립터 형식까지 호환되지는 않습니다.
구형 SGDMA: 메모리의 linked list
Embedded Peripherals IP User Guide 26.1의 30장 Scatter-Gather DMA Controller Core는 새 설계에 Modular Scatter-Gather DMA Core를 쓰라고 권합니다. 구형 코어의 디스크립터에는 source, destination, next_desc_ptr, bytes_to_transfer, actual_bytes_transferred, desc_status, desc_control이 있습니다. bytes_to_transfer가 0이면 EOP를 만날 때까지 전송합니다. desc_control의 bit 7 OWNED_BY_HW가 1이면 코어가 디스크립터를 갱신할 수 있으므로 소프트웨어는 경쟁 조건을 피하려고 그 디스크립터에 접근하지 않아야 합니다. Scatter-Gather DMA Controller Core DMA Descriptors 표
같은 가이드의 Device Support 표는 구형 SG-DMA 코어의 지원 계열을 MAX 10과 Arria V, Stratix V, Cyclone V로 한정하고, mSGDMA는 표의 모든 계열에서 지원합니다. Linux 6.18.37에서 구형 SGDMA는 dmaengine provider가 아니라 Altera TSE 이더넷 드라이버 안의 dmaops로 남아 있습니다. sgdma_setup_descrip()은 다음 디스크립터의 HW_OWNED를 먼저 지워 체인이 거기서 멈추게 한 뒤 현재 디스크립터를 HW_OWNED로 씁니다. Device Support 표 sgdma_setup_descrip TSE의 SGDMA·MSGDMA dmaops와 compatible
mSGDMA: dispatcher의 descriptor FIFO
mSGDMA의 dispatcher는 디스크립터를 버퍼링하고 해석해 read master와 write master를 제어합니다. prefetcher를 쓰면 디스크립터 포트가 Avalon-ST sink이고, 쓰지 않으면 Avalon-MM agent입니다. 지원 방향은 MM→MM, ST→MM, MM→ST입니다. mSGDMA Dispatcher Core
| Linux 6.18.37 altera-msgdma.c | 내용 |
|---|---|
| struct msgdma_extended_desc (43번 줄) | read/write 주소의 하위·상위 32비트, len, burst·sequence, stride, control로 된 extended descriptor입니다. |
| GO bit 31 (71번 줄) | 1을 쓰면 디스크립터 전체가 descriptor FIFO로 들어갑니다. |
| msgdma_copy_one (496번 줄) | FIFO가 가득 차 있으면 기다리고, control word를 제외한 부분을 먼저 복사한 뒤 wmb() 사이에서 control을 마지막에 씁니다. |
| msgdma_prep_slave_sg (388번 줄) | sg 항목을 MSGDMA_MAX_TRANS_LEN(U32_MAX) 단위로 나눠 디스크립터를 만들고, 장치 쪽 주소는 slave_config에서 가져옵니다. 남은 디스크립터 수는 spin_lock_irqsave로 보호합니다. |
| msgdma_issue_pending (569번 줄) | 장치가 idle이면 pending list를 active list로 옮기고 디스크립터를 FIFO에 씁니다. |
| msgdma_irq_handler (733번 줄) | 완료 처리를 tasklet으로 넘깁니다. |
probe는 DMA_MEMCPY와 DMA_SLAVE, 4바이트 bus width와 세 전송 방향을 등록합니다. residue_granularity가 DESCRIPTOR이고 device_tx_status는 dma_cookie_status이므로 바이트별 잔량을 보고한다고 해석하지 않습니다. DT compatible은 altr,socfpga-msgdma입니다. 소프트웨어에서는 list로 관리하지만, 이 드라이버가 하드웨어에 제출하는 형식은 next 포인터 체인이 아니라 FIFO에 쓰는 디스크립터입니다. 디스크립터 정의와 GO bit msgdma_prep_slave_sg FIFO 쓰기와 issue_pending capability 등록
Linux dmaengine에서 보이는 순서
dmaengine client는 컨트롤러의 디스크립터 형식을 직접 다루지 않습니다. 아래는 순서만 보이는 축약 예시입니다. 실제 구현에는 채널 요청·설정 오류, 매핑 실패(nents == 0), prep 실패(NULL), submit 오류와 완료·정리 경로가 필요합니다.
chan = dma_request_chan(dev, "rx");
dmaengine_slave_config(chan, &cfg); /* device-side address, width, burst */
dma_dev = dmaengine_get_dma_device(chan);
nents = dma_map_sg(dma_dev, sgl, n, DMA_FROM_DEVICE);
desc = dmaengine_prep_slave_sg(chan, sgl, nents, DMA_DEV_TO_MEM, flags);
cookie = dmaengine_submit(desc); /* queued only */
dma_async_issue_pending(chan); /* start */매핑된 주소와 버퍼는 완료되기 전에 재사용하거나 해제하지 않습니다. submit 뒤에는 descriptor 포인터를 다시 접근하지 않고 cookie·콜백으로 상태를 추적합니다. 이 예시는 실행하거나 드라이버로 빌드한 결과가 아닙니다.
| 단계 | 문서가 정한 의미 |
|---|---|
| dmaengine_slave_config | 장치 쪽 주소, bus width, burst 같은 slave 파라미터를 채널에 넘깁니다. |
| dmaengine_get_dma_device, dma_map_sg | prep 전에 DMA 장치의 struct device로 scatterlist를 매핑하고, 전송이 끝날 때까지 매핑을 유지해야 합니다. |
| dmaengine_prep_slave_sg | 전송용 디스크립터를 얻습니다. altera-msgdma에서는 여기서 하드웨어 디스크립터 내용이 만들어집니다. |
| dmaengine_submit | pending queue에 넣을 뿐 시작하지 않습니다. 이후 그 디스크립터 포인터는 dmaengine 소유이므로 client는 무효로 봐야 합니다. |
| dma_async_issue_pending | 채널이 idle이면 queue의 첫 트랜잭션을 시작합니다. 완료되면 tasklet이 client 콜백을 부릅니다. |
Lattice VDMA의 SG-List
Lattice FPGA-UG-02168-1.1의 SG-List는 호스트 프레임 버퍼 하나를 기술하는 SG-Element의 집합입니다. 그림 2.2에서 레지스터의 LEN은 바이트 수가 아니라 SG-Element 개수입니다. FPGA-UG-02168-1.1 2.2절
| 항목 | 가이드의 그림 2.2 |
|---|---|
| SG-Element | 블록 주소 상·하위 32비트와 바이트 길이 |
| SG-List 묶음 | 최대 16개, 즉 호스트 메모리의 frame buffer 최대 16개 |
| SGB ADDR/LEN 레지스터 0~15 | 첫 SG-Element 주소와 항목 수 |
| 버퍼 할당 | 커널 ring buffer, 필요하면 사용자 공간으로 복사 |
가이드 5장의 lscvdma 데모는 Secure Boot 비활성화를 요구합니다. 이는 그 데모의 설치 조건이며 일반 권장사항이 아닙니다. 공개 가이드만으로 dmaengine·V4L2 등록 여부는 확정할 수 없으므로 실제 드라이버를 확인해야 합니다. 이 문서 검토에서 드라이버를 실행하거나 보안 설정을 바꾸지는 않았습니다. FPGA-UG-02168-1.1 5장 데모 실행
그림의 physical address라는 표기를 CPU 물리 주소와 장치 DMA 주소가 항상 같다는 뜻으로 읽지 않습니다. Linux에서는 DMA 매핑이 돌려준 장치용 주소를 사용하고, IOMMU가 있는 경로의 주소 변환을 구분합니다. DMA 버퍼는 누가 읽고 쓰고 있습니까?
공식 근거
- Altera Embedded Peripherals IP UG 26.1: Scatter-Gather DMA Controller Core
- Altera Embedded Peripherals IP UG 26.1: mSGDMA Dispatcher Core
- Linux 6.18.37 drivers/dma/altera-msgdma.c
- Linux 6.18.37 dmaengine client 문서
- Linux 6.18.37 dmaengine provider 문서
- Lattice FPGA-UG-02168-1.1 mVision MIPI Video Sensor to PCIe Bridge Demo