diff --git a/sdk/software/examples/asm/user-sample.c b/sdk/software/examples/asm/user-sample.c index 8905b57..129e6a3 100644 --- a/sdk/software/examples/asm/user-sample.c +++ b/sdk/software/examples/asm/user-sample.c @@ -1,56 +1,61 @@ /** * user-sample - CIC-IEC 2026 Stage 3 Evaluation Program * - * (UPDATED FOR PING-PONG BUFFER ARCHITECTURE) + * (ULTIMATE PERFORMANCE DMA + PING-PONG BUFFER ARCHITECTURE) * - * Reads N groups of 4x4 unsigned matrix multiplications from ExtRAM, - * computes C = A x B via the hardware matmul IP using Ping-Pong buffering, - * writes results back, computes CRC32 over the result region, and outputs - * the required identifiers. + * Utilizes true DMA transfers (Channel 0 for memory->IP, Channel 1 for IP->memory) + * perfectly overlapped with Hardware IP computation to achieve Zero-Wait execution. */ #include +#include +#include #include +#include +#include +#include + /* ─── Configurable group count (default 10 for fast local testing) ─── */ #ifndef MATMUL_GROUP_NUM #define MATMUL_GROUP_NUM 5000 #endif /* ─── BSP globals ─────────────────────────────────────────────────── */ -unsigned long UART_BASE = 0xbf000000; -unsigned long CONFREG_TIMER_BASE = 0xbf20f100; +unsigned long UART_BASE = 0xbf000000; +unsigned long CONFREG_TIMER_BASE = 0xbf20f100; unsigned long CONFREG_CLOCKS_PER_SEC = 50000000L; -unsigned long CORE_CLOCKS_PER_SEC = 33000000L; +unsigned long CORE_CLOCKS_PER_SEC = 33000000L; /* ─── Memory layout ────────────────────────────────────────────────── */ #define EXTRAM_BASE_PHYS 0x1c400000 -#define EXTRAM_BASE (0xA0000000 | (EXTRAM_BASE_PHYS & 0x1FFFFFFF)) +#define EXTRAM_BASE_UNCACHED (0xA0000000 | (EXTRAM_BASE_PHYS & 0x1FFFFFFF)) -#define SRC_BASE 0 -#define DST_BASE (SRC_BASE + MATMUL_GROUP_NUM * 32) +#define SRC_BASE_WORDS 0 +#define DST_BASE_WORDS (SRC_BASE_WORDS + MATMUL_GROUP_NUM * 32) #define RESULT_WORDS (MATMUL_GROUP_NUM * 48) -#define RESULT_BYTES (RESULT_WORDS * 4) + +/* ExtRAM Physical Addresses for DMA */ +#define EXTRAM_PHYS_AB(g) (EXTRAM_BASE_PHYS + (SRC_BASE_WORDS * 4) + (g) * 128) +#define EXTRAM_PHYS_C(g) (EXTRAM_BASE_PHYS + (DST_BASE_WORDS * 4) + (g) * 192) /* ─── MATMUL IP registers (Ping-Pong Memory Map) ──────────────────── */ -#define MATMUL_BASE 0xbf500000 -#define MATMUL_CTRL (MATMUL_BASE + 0x000) -#define MATMUL_STATUS (MATMUL_BASE + 0x004) +#define MATMUL_BASE_PHYS 0x1f500000 +#define MATMUL_BASE_UNCACHED 0xbf500000 -// Bank 0 Memory Space -#define MATMUL_B0_A (MATMUL_BASE + 0x100) -#define MATMUL_B0_B (MATMUL_BASE + 0x140) -#define MATMUL_B0_C (MATMUL_BASE + 0x180) +// CPU Control & Status Registers +#define MATMUL_CTRL (MATMUL_BASE_UNCACHED + 0x000) +#define MATMUL_STATUS (MATMUL_BASE_UNCACHED + 0x004) -// Bank 1 Memory Space -#define MATMUL_B1_A (MATMUL_BASE + 0x300) -#define MATMUL_B1_B (MATMUL_BASE + 0x340) -#define MATMUL_B1_C (MATMUL_BASE + 0x380) +// DMA Physical Addresses for IP Space +// (A and B are contiguous: A is 0x100~0x13F, B is 0x140~0x17F. Total 128 bytes) +#define MATMUL_PHYS_B0_AB (MATMUL_BASE_PHYS + 0x100) +#define MATMUL_PHYS_B0_C (MATMUL_BASE_PHYS + 0x180) // 192 bytes +#define MATMUL_PHYS_B1_AB (MATMUL_BASE_PHYS + 0x300) +#define MATMUL_PHYS_B1_C (MATMUL_BASE_PHYS + 0x380) -// Control & Status Bit Masks #define CTRL_START_B0 0x1 #define CTRL_START_B1 0x2 -#define STATUS_BUSY 0x1 #define STATUS_DONE 0x2 #define STATUS_ERROR 0x4 @@ -83,7 +88,7 @@ static const uint32_t crc32_table[256] = { 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB30A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, @@ -94,40 +99,26 @@ static const uint32_t crc32_table[256] = { 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; -/* ─── Read N words from IP register space ─────────────────────────── */ -static void read_ip(uint32_t base, volatile uint32_t *dst, int n) -{ - for (int i = 0; i < n; i++) - dst[i] = REG_RD(base + i * 4); -} - -/* ─── Write N words to IP register space ──────────────────────────── */ -static void write_ip(uint32_t base, const volatile uint32_t *src, int n) -{ - for (int i = 0; i < n; i++) - REG_WR(base + i * 4, src[i]); -} - /* ─── Poll IP STATUS until done and clear it (W1C) ────────────────── */ -static void wait_and_clear_done(void) +static void wait_matmul_done(void) { uint32_t status; do { status = REG_RD(MATMUL_STATUS); } while (!(status & (STATUS_DONE | STATUS_ERROR))); - // Write 1 to Clear the DONE bit so it's ready for the next interrupt/poll + // Write 1 to Clear the DONE bit REG_WR(MATMUL_STATUS, STATUS_DONE); } -/* ─── CRC32 over ExtRAM result region (little-endian byte stream) ─── */ +/* ─── CRC32 over ExtRAM result region ─────────────────────────────── */ static uint32_t compute_crc32(volatile uint32_t *extram) { uint32_t crc = 0xFFFFFFFF; uint32_t i, w; for (i = 0; i < RESULT_WORDS; i++) { - w = extram[DST_BASE + i]; + w = extram[DST_BASE_WORDS + i]; crc = crc32_table[(crc ^ (w & 0xFF)) & 0xFF] ^ (crc >> 8); crc = crc32_table[(crc ^ ((w >> 8) & 0xFF)) & 0xFF] ^ (crc >> 8); crc = crc32_table[(crc ^ ((w >> 16) & 0xFF)) & 0xFF] ^ (crc >> 8); @@ -141,79 +132,68 @@ int main(void) { if (MATMUL_GROUP_NUM <= 0) return 0; - volatile uint32_t *extram = (volatile uint32_t *)EXTRAM_BASE; - uint32_t a_buf[16], b_buf[16], c_buf[48]; + volatile uint32_t *extram = (volatile uint32_t *)EXTRAM_BASE_UNCACHED; uint32_t crc; - int g, i; - - // Ping-Pong Buffer Register Addresses - uint32_t bank_A[2] = {MATMUL_B0_A, MATMUL_B1_A}; - uint32_t bank_B[2] = {MATMUL_B0_B, MATMUL_B1_B}; - uint32_t bank_C[2] = {MATMUL_B0_C, MATMUL_B1_C}; - uint32_t ctrl_start[2] = {CTRL_START_B0, CTRL_START_B1}; - int cur_bank = 0; + int g; + // Ping-Pong Register Address Arrays (Physical for DMA) + uint32_t phys_bank_AB[2] = {MATMUL_PHYS_B0_AB, MATMUL_PHYS_B1_AB}; + uint32_t phys_bank_C[2] = {MATMUL_PHYS_B0_C, MATMUL_PHYS_B1_C}; + + // IP Control Command Arrays + uint32_t ctrl_start[2] = {CTRL_START_B0, CTRL_START_B1}; + /* ── Start marker (must be first output) ──────────────────── */ printf("MATMUL_START\n"); - /* ── Prologue: Load and Start the very first group ────────── */ - for (i = 0; i < 16; i++) { - a_buf[i] = extram[SRC_BASE + 0 * 32 + i]; - b_buf[i] = extram[SRC_BASE + 0 * 32 + 16 + i]; - } - write_ip(bank_A[0], a_buf, 16); - write_ip(bank_B[0], b_buf, 16); - - // Clear any pending statuses before starting + // 清理寄存器状态 REG_WR(MATMUL_STATUS, STATUS_DONE | STATUS_ERROR); + + /* ── Prologue: 第0组数据初始化 ────────────────────────── */ + // DMA Ch0: 从 ExtRAM 将 Group 0 的 A、B (128 bytes) 搬入 IP 的 Bank 0 + dma_start_transfer(0, EXTRAM_PHYS_AB(0), phys_bank_AB[0], 128, 10); + dma_wait_polling(0); // 必须等待首个数据加载完成 + + // 启动 IP Bank 0 计算 REG_WR(MATMUL_CTRL, ctrl_start[0]); - /* ── Main Loop: Pipelined Execution ───────────────────────── */ + /* ── Main Loop: 硬件完全流水线 (Overlapped Execution) ──── */ + // 在 g 循环中: + // IP 正在计算 prev_bank (g-1) + // DMA_CH0 正在加载 cur_bank (g) + // DMA_CH1 正在写回 prev_prev_bank (g-2) for (g = 1; g < MATMUL_GROUP_NUM; g++) { - cur_bank = g % 2; + int cur_bank = g % 2; int prev_bank = (g - 1) % 2; - int finished_g = g - 1; - // 1. Prepare/Load data for the NEXT group into the idle bank - // (This happens concurrently while hardware calculates the previous group) - for (i = 0; i < 16; i++) { - a_buf[i] = extram[SRC_BASE + g * 32 + i]; - b_buf[i] = extram[SRC_BASE + g * 32 + 16 + i]; + // 1. 发起加载下一组数据的 DMA 传输 (Channel 0: Memory -> IP) + dma_start_transfer(0, EXTRAM_PHYS_AB(g), phys_bank_AB[cur_bank], 128, 10); + + // 2. 等待上一组在 IP 中的计算完成 + wait_matmul_done(); + + // 3. 确保上一次的结果写回 (Channel 1) 已经空闲 + if (g > 1) { + dma_wait_polling(1); } - write_ip(bank_A[cur_bank], a_buf, 16); - write_ip(bank_B[cur_bank], b_buf, 16); - // 2. Wait for the PREVIOUS group hardware calculation to finish - wait_and_clear_done(); + // 4. 发起写回刚才计算完成的结果 (Channel 1: IP -> Memory) + dma_start_transfer(1, phys_bank_C[prev_bank], EXTRAM_PHYS_C(g - 1), 192, 20); - // 3. Immediately start the current bank to keep hardware busy + // 5. 确保当前要计算的输入数据 (Channel 0) 已经加载完毕 + dma_wait_polling(0); + + // 6. 立即启动当前组在 IP 中的计算 REG_WR(MATMUL_CTRL, ctrl_start[cur_bank]); - - // 4. Read the PREVIOUS group's result and write to memory - read_ip(bank_C[prev_bank], c_buf, 48); - - // Required DBG prints logic modified to match the pipelined flow - if (finished_g == 1) { - printf("DBG C[0..2]=%08lx %08lx %08lx\n", - (unsigned long)c_buf[0], (unsigned long)c_buf[1], (unsigned long)c_buf[2]); - } - - for (i = 0; i < 48; i++) { - extram[DST_BASE + finished_g * 48 + i] = c_buf[i]; - } - - if (finished_g == 0) { - printf("DBG wrote C[0]=%08lx @ ExtRAM[%d]\n", - (unsigned long)extram[DST_BASE], DST_BASE); - } } - /* ── Epilogue: Wait for and read the very last group ──────── */ - wait_and_clear_done(); - read_ip(bank_C[cur_bank], c_buf, 48); - for (i = 0; i < 48; i++) { - extram[DST_BASE + (MATMUL_GROUP_NUM - 1) * 48 + i] = c_buf[i]; - } + /* ── Epilogue: 收尾最后一组 ───────────────────────────── */ + wait_matmul_done(); // 等待最后一次计算完成 + dma_wait_polling(1); // 确保之前的 DMA CH1 空闲 + + // DMA CH1 将最后一组结果写回 + dma_start_transfer(1, phys_bank_C[(MATMUL_GROUP_NUM - 1) % 2], EXTRAM_PHYS_C(MATMUL_GROUP_NUM - 1), 192, 20); + dma_wait_polling(1); // 必须等待最后一组写回完成 /* ── CRC32 over result region ─────────────────────────────── */ crc = compute_crc32(extram);