convert scores가 계속 쓰레기로 나오는 문제, 이번에는 input도 아니고 weight도 아닌 descriptor ring / status block의 cache coherency 를 의심해봤다. libedgetpu (정상 동작)와 우리 driver (비정상)의 메모리 할당 방식이 다른 걸 발견했고, 우리쪽을 libedgetpu 쪽에 맞춰 봤다.
먼저 정상 동작하는 libedgetpu가 instruction queue ring 과 status block 을 어떻게 잡는지 확인했다.소스 경로는 driver/kernel/kernel_coherent_allocator.cc 와 windows 전용 driver/kernel/windows/kernel_coherent_allocator_windows.cc 을 참고하면 된다.
// kernel_coherent_allocator.cc:42~
ioctl_buffer.enable = 1;
ioctl_buffer.size = size_bytes; // 16KB (queue 4KB + status 4KB + padding)
ioctl(fd, GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR, &ioctl_buffer);
dma_address_ = ioctl_buffer.dma_address; // ← coral.sys 가 dma_alloc_coherent 로 받은 PA
// windows 전용
apex_memmap_ioctl.dev_dma_addr = dma_address;
ioctl(fd, GASKET_IOCTL_MAP_UMDMA_VIEW, &apex_memmap_ioctl);
return (char*)apex_memmap_ioctl.virtaddr; // user-mode UC view
요약하면 이렇다
chip이 ring descriptor를 fetch 하거나 status block에 DMA write 할 때, CPU cache 와 sync 가 hardware 차원에서 보장 된다.
반면 우리 npu driver의 Device.c를 보면 두 군데가 그냥 cacheable pool 로 잡혀 있었다.
// Device.c:593 (변경 전)
deviceContext->DescRingBase = ExAllocatePoolWithTag(NonPagedPoolNx, PAGE_SIZE, 'DRNG');
// Device.c:620 (변경 전)
deviceContext->StatusBlockBase = ExAllocatePoolWithTag(NonPagedPoolNx, PAGE_SIZE, 'SBLK');
NonPagedPoolNx는 nonpaged (page-out 안됨) 이긴 한데 cache attribute 가 cacheable 이다. PCIe device가 직접 DMA write 한 데이터를 CPU가 cache 에서 stale 값 읽을 가능성이 있다.
가설은 두 가지이다.
증상이랑 부합한다.
1. DescRing 할당
// Device.c:593 (변경 후)
{
PHYSICAL_ADDRESS lo, hi, none;
lo.QuadPart = 0;
hi.QuadPart = 0xFFFFFFFFLL; // < 4GB. chip MMU 32-bit PA 제약
none.QuadPart = 0;
deviceContext->DescRingBase = MmAllocateContiguousMemorySpecifyCache(
PAGE_SIZE, lo, hi, none, MmNonCached);
}
if (deviceContext->DescRingBase == NULL) {
DbgPrint("[%s] Failed to allocate descriptor ring\n", __FUNCTION__);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(deviceContext->DescRingBase, PAGE_SIZE);
hi.QuadPart = 0xFFFFFFFFLL 가 중요하다. chip MMU 가 32-bit PA 만 받기 때문에 4GB 미만 영역에서 잡아야 한다.
2. StatusBlock 할당
// Device.c:620 (변경 후)
{
PHYSICAL_ADDRESS lo, hi, none;
lo.QuadPart = 0;
hi.QuadPart = 0xFFFFFFFFLL;
none.QuadPart = 0;
deviceContext->StatusBlockBase = MmAllocateContiguousMemorySpecifyCache(
PAGE_SIZE, lo, hi, none, MmNonCached);
}
if (deviceContext->StatusBlockBase == NULL) {
DbgPrint("[%s] Failed to allocate status block\n", __FUNCTION__);
MmFreeContiguousMemory(deviceContext->DescRingBase); // ← 이것도 교체
deviceContext->DescRingBase = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
3. Cleanup path
ExFreePoolWithTag 였던 free 도 전부 MmFreeContiguousMemory 로 교체. tag 인자가 없어서 더 깔끔하다.
// Device.c:1149 (변경 후)
if (deviceContext->DescRingBase != NULL) {
MmFreeContiguousMemory(deviceContext->DescRingBase);
deviceContext->DescRingBase = NULL;
}
if (deviceContext->StatusBlockBase != NULL) {
MmFreeContiguousMemory(deviceContext->StatusBlockBase);
deviceContext->StatusBlockBase = NULL;
}

여전히 쓰레기 값이 나온다..
또 다음 가설을 설정 후, 테스트해야겠다..
화이팅