Use uncached ExtRAM access (0xBC400000) to bypass CPU data cache

The open-la500 core has USE_CACHE enabled. Writes to 0x1c400000
go to the write-back data cache and never reach the SRAM model
before the testbench checks ExtRAM. Use the 0xA0000000 uncached
segment (same pattern as fft_dma/main.c).
This commit is contained in:
2026-06-24 02:04:40 +08:00
parent b8a4fcc30b
commit e35fa5884d
14 changed files with 654 additions and 1 deletions

View File

@@ -0,0 +1,37 @@
#include "dma.h"
void dma_start_transfer(int ch_id, uint32_t src, uint32_t dst, uint32_t bytes, uint32_t tag) {
volatile dma_ch_regs_t* ch = DMA_CH(ch_id);
// 检查通道是否空闲 防止覆盖正在运行的任务
if (ch->STATUS & STATUS_BUSY_BIT) {
// printf("channel %d busy\n", ch_id);
return; // 通道正忙,处理报错或重试
}
// 写入基本地址和长度
ch->SRC_ADDR = src;
ch->DST_ADDR = dst;
ch->LENGTH = bytes;
ch->TAG = tag; // 可选,填入任务ID
// 置 Burst 属性并触发传输
// AXI SIZE: 2 (代表 2^2 = 4 Bytes,与 32-bit 数据线匹配)
// AXI LEN: 15 (代表 16 beats 突发传输,最高效)
uint32_t ctrl_val = CTRL_BURST_SIZE(2) | CTRL_BURST_LEN(15);
// 写入 CTRL 并拉高 Bit 0 (Start)
ch->CTRL = ctrl_val | CTRL_START_BIT;
}
void dma_wait_polling(int ch_id) {
volatile dma_ch_regs_t* ch = DMA_CH(ch_id);
// 死等 Done bit 置 1
while (!(ch->STATUS & STATUS_DONE_BIT)) {
}
// 清除 Done 标志位 (Write 1 to Clear Bit 1)
// 注意:写 1 清 0 的设计,所以我们对 bit 1 写入 1
ch->STATUS = STATUS_DONE_BIT;
}

View File

@@ -0,0 +1,14 @@
#include "fft.h"
#include "common_func.h"
void fft_start() {
RegWrite(FFT_CSR_REG, FFT_CTRL_START);
}
void fft_wait() {
while ((RegRead(FFT_CSR_REG) & FFT_STAT_DONE) == 0) {}
}
unsigned int fft_get_csr() {
return RegRead(FFT_CSR_REG);
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
typedef struct {
volatile uint32_t SRC_ADDR; // 0x00: 源地址
volatile uint32_t DST_ADDR; // 0x04: 目的地址
volatile uint32_t LENGTH; // 0x08: 传输总字节数
volatile uint32_t TAG; // 0x0C: 软件 Tag (硬件不处理,留给软件标记任务用)
volatile uint32_t CTRL; // 0x10: 控制寄存器 (配置 Burst 属性 + 启动)
volatile uint32_t STATUS; // 0x14: 状态寄存器 (Busy, Done)
volatile uint32_t RESV[10]; // 0x18 ~ 0x3C: 保留空间,凑齐 0x40 字节
} dma_ch_regs_t;
// DMA 控制器基地址
#define CDMA_MUX_BASE 0xbf300000
#define DMA_CH(i) ((volatile dma_ch_regs_t*)(CDMA_MUX_BASE + (i) * 0x40))
// CTRL 寄存器位定义
#define CTRL_START_BIT (1 << 0)
#define CTRL_BURST_SIZE(x) (((x) & 0x7) << 3) // Bits [5:3]: AXI AxSIZE (0=1B, 1=2B, 2=4B, 3=8B)
#define CTRL_BURST_LEN(x) (((x) & 0xFF) << 6) // Bits [13:6]: AXI AxLEN (0=1 beat, 15=16 beats)
// STATUS 寄存器位定义
#define STATUS_BUSY_BIT (1 << 0)
#define STATUS_DONE_BIT (1 << 1)
void dma_start_transfer(int ch_id, uint32_t src, uint32_t dst, uint32_t bytes, uint32_t tag);
void dma_wait_polling(int ch_id);

View File

@@ -0,0 +1,16 @@
#pragma once
#define FFT_BASE 0xbf400000
#define FFT_IN_RE_BASE (FFT_BASE + 0x1000)
#define FFT_IN_IM_BASE (FFT_BASE + 0x2000)
#define FFT_OUT_RE_BASE (FFT_BASE + 0x3000)
#define FFT_OUT_IM_BASE (FFT_BASE + 0x4000)
#define FFT_CSR_REG (FFT_BASE + 0xF000)
#define FFT_CTRL_START (1 << 4)
#define FFT_STAT_DONE (1 << 1)
#define FFT_STAT_BUSY (1 << 0)
#define FFT_POINT_NUM 1024
void fft_start();
void fft_wait();
unsigned int fft_get_csr();

View File

@@ -0,0 +1,11 @@
TARGET = dma
CFLAGS += -O3 -g
C_SRCS := $(wildcard ./*.c )
OBJDIR = obj
COMMON_DIR = ../../bsp
GCC_DIR=../../../toolchains/loongson-gnu-toolchain-8.3-x86_64-loongarch32r-linux-gnusf-v2.0
PICOLIBC_DIR=../../../toolchains/picolibc
include ../../bsp/common.mk

View File

@@ -0,0 +1,71 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <common_func.h>
#include <confreg_time.h>
#include <dma.h>
// BSP板级支持包所需全局变量
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;
// 加上 aligned(64) 是为了防止 DMA 突发传输时跨越缓存行或 AXI 非对齐边界
uint32_t src_array[64] __attribute__((aligned(64)));
uint32_t dst_array[64] __attribute__((aligned(64)));
int main(int argc, char** argv)
{
uint32_t data_len = 64;
uint32_t byte_len = data_len * 4;
// 虚拟地址转物理地址与无缓存地址
// 屏蔽掉高 3 位(& 0x1FFFFFFF)即可获得真实的物理地址
uint32_t phys_src_addr = ((uint32_t)src_array) & 0x1FFFFFFF;
uint32_t phys_dst_addr = ((uint32_t)dst_array) & 0x1FFFFFFF;
// 或上 0xA0000000 即可获得绕过 Cache 的无缓存(Uncached)指针
volatile uint32_t *uncached_src = (volatile uint32_t *)(phys_src_addr | 0xA0000000);
volatile uint32_t *uncached_dst = (volatile uint32_t *)(phys_dst_addr | 0xA0000000);
// CPU 必须通过无缓存指针写入
for (int i = 0; i < data_len; i++) {
uncached_src[i] = i * i; // 填入 0~63
uncached_dst[i] = 0xDEADBEEF; // 目的数组填入垃圾值,方便验证是否真的被覆盖
}
printf("src_array: %x\n", src_array);
printf("dst_array: %x\n", dst_array);
printf("phys_src_array: %x\n", phys_src_addr);
printf("phys_dst_array: %x\n", phys_dst_addr);
printf("uncached_src: %x\n", uncached_src);
printf("uncached_dst: %x\n", uncached_dst);
// 配置 DMA:DMA 只需要最纯粹的物理地址
dma_start_transfer(0, phys_src_addr, phys_dst_addr, byte_len);
dma_wait_polling(0);
printf("dma passed!\n");
// 校验数据:CPU 必须通过无缓存指针读取
int err = 0;
for (int i = 0; i < data_len; i++) {
uint32_t val = uncached_dst[i];
printf("%d: %d\n", i, val);
if (val != i * i) {
err++;
}
}
if (err == 0) {
printf("\nSuccess! Array to Array DMA transfer verified.\n");
} else {
printf("\nFailed! Found %d errors.\n", err);
}
return 0;
}

View File

@@ -0,0 +1,75 @@
dma_status init = 0
src_array: 1c080d80
dst_array: 1c080c80
phys_src_array: 1c080d80
phys_dst_array: 1c080c80
uncached_src: bc080d80
uncached_dst: bc080c80
polling...
dma passed!
0: 0
1: 1
2: 4
3: 9
4: 16
5: 25
6: 36
7: 49
8: 64
9: 81
10: 100
11: 121
12: 144
13: 169
14: 196
15: 225
16: 256
17: 289
18: 324
19: 361
20: 400
21: 441
22: 484
23: 529
24: 576
25: 625
26: 676
27: 729
28: 784
29: 841
30: 900
31: 961
32: 1024
33: 1089
34: 1156
35: 1225
36: 1296
37: 1369
38: 1444
39: 1521
40: 1600
41: 1681
42: 1764
43: 1849
44: 1936
45: 2025
46: 2116
47: 2209
48: 2304
49: 2401
50: 2500
51: 2601
52: 2704
53: 2809
54: 2916
55: 3025
56: 3136
57: 3249
58: 3364
59: 3481
60: 3600
61: 3721
62: 3844
63: 3969
Success! Array to Array DMA transfer verified.

View File

@@ -0,0 +1,11 @@
TARGET = fft
CFLAGS += -O3 -g
C_SRCS := $(wildcard ./*.c )
OBJDIR = obj
COMMON_DIR = ../../bsp
GCC_DIR=../../../toolchains/loongson-gnu-toolchain-8.3-x86_64-loongarch32r-linux-gnusf-v2.0
PICOLIBC_DIR=../../../toolchains/picolibc
include ../../bsp/common.mk

View File

@@ -0,0 +1,156 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <common_func.h>
#include <confreg_time.h>
#include <fft.h>
// BSP板级支持包所需全局变量
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;
const float PI = 3.14159265358979323846;
// 读取定时器的当前Tick
unsigned int get_timer_ticks() {
return RegRead(CONFREG_TIMER_BASE);
}
// 软件FFT实现 (基2 DIT-FFT 算法)
void sw_fft(float re[], float im[], int n) {
int i, j, k, l;
float tr, ti, ur, ui, wr, wi;
// 比特翻转 (Bit Reversal)
j = 0;
for (i = 0; i < n - 1; i++) {
if (i < j) {
tr = re[i]; ti = im[i];
re[i] = re[j]; im[i] = im[j];
re[j] = tr; im[j] = ti;
}
k = n / 2;
while (k <= j) {
j -= k;
k /= 2;
}
j += k;
}
// 蝶形运算 (Butterfly Computation)
for (l = 1; l < n; l *= 2) {
ur = 1.0;
ui = 0.0;
wr = cos(PI / l);
wi = -sin(PI / l);
for (i = 0; i < n; i += 2 * l) {
ur = 1.0;
ui = 0.0;
for (j = 0; j < l; j++) {
int p = i + j;
int q = p + l;
tr = re[q] * ur - im[q] * ui;
ti = re[q] * ui + im[q] * ur;
re[q] = re[p] - tr;
im[q] = im[p] - ti;
re[p] += tr;
im[p] += ti;
float next_ur = ur * wr - ui * wi;
ui = ur * wi + ui * wr;
ur = next_ur;
}
}
}
}
int main(int argc, char** argv)
{
unsigned int fft_csr = RegRead(FFT_CSR_REG);
printf("fft_csr = %x\n", fft_csr);
// 准备测试数据
float sw_in_re[FFT_POINT_NUM];
float sw_in_im[FFT_POINT_NUM];
for (int i = 0; i < FFT_POINT_NUM; i++) {
float dc_part = 4000.0f;
float f10_part = 8000.0f * cos(2 * PI * 10.0 * i / FFT_POINT_NUM);
float f200_part = 6000.0f * sin(2 * PI * 200.0 * i / FFT_POINT_NUM);
float f400_part = 3000.0f * sin(2 * PI * 400.0 * i / FFT_POINT_NUM);
sw_in_re[i] = dc_part + f10_part + f200_part + f400_part;
sw_in_im[i] = 0.0f;
}
unsigned int tick_start, tick_end;
unsigned int hw_time, sw_time;
// 硬件加速 FFT 测试
printf("\n--- Starting Hardware FFT ---\n");
tick_start = get_ns();
for (int i = 0; i < FFT_POINT_NUM; i++) {
RegWrite(FFT_IN_RE_BASE + (i * 4), (unsigned int)((int)sw_in_re[i]));
RegWrite(FFT_IN_IM_BASE + (i * 4), 0);
}
fft_start();
fft_wait();
tick_end = get_ns();
// 如果定时器是递减的,这里可能是 tick_start - tick_end
// 假设定时器是向上递增的:
hw_time = tick_end - tick_start;
int hw_out_re[FFT_POINT_NUM];
int hw_out_im[FFT_POINT_NUM];
for (int i = 0; i < FFT_POINT_NUM; i++) {
hw_out_re[i] = (int)RegRead(FFT_OUT_RE_BASE + (i * 4));
hw_out_im[i] = (int)RegRead(FFT_OUT_IM_BASE + (i * 4));
}
// ==========================================
// 2. 纯软件 FFT 测试
// ==========================================
printf("--- Starting Software FFT ---\n");
tick_start = get_ns();
sw_fft(sw_in_re, sw_in_im, FFT_POINT_NUM);
tick_end = get_ns();
sw_time = tick_end - tick_start;
// ==========================================
// 3. 打印对比结果
// ==========================================
printf("\n--- Performance Comparison ---\n");
printf("Timer Clock Freq : %lu Hz\n", CONFREG_CLOCKS_PER_SEC);
printf("Hardware FFT Time: %u ns (%.3f ms)\n", hw_time, (float)hw_time / 1000000.0);
printf("Software FFT Time: %u ns (%.3f ms)\n", sw_time, (float)sw_time / 1000000.0);
if (hw_time > 0) {
printf("Speedup Ratio : %.2fx\n", (float)sw_time / hw_time);
}
printf("\n--- Verification (Only showing Bins with energy > 10) ---\n");
for (int i = 0; i < FFT_POINT_NUM; i++) {
if (fabs(hw_out_re[i]) > 10 || fabs(hw_out_im[i]) > 10) {
printf("Bin [%4d] Hz: HW(Re:%6d, Im:%6d) | SW(Re:%6d, Im:%6d)\n",
i,
hw_out_re[i], hw_out_im[i],
(int)sw_in_re[i] / FFT_POINT_NUM, (int)sw_in_im[i] / FFT_POINT_NUM);
}
}
return 0;
}

View File

@@ -0,0 +1,19 @@
fft_csr = 0
--- Starting Hardware FFT ---
--- Starting Software FFT ---
--- Performance Comparison ---
Timer Clock Freq : 50000000 Hz
Hardware FFT Time: 1695360 ns (1.695 ms)
Software FFT Time: 274547190 ns (274.547 ms)
Speedup Ratio : 161.94x
--- Verification (Only showing Bins with energy > 10) ---
Bin [ 0] Hz: HW(Re: 4007, Im: 0) | SW(Re: 4000, Im: 0)
Bin [ 10] Hz: HW(Re: 4006, Im: -1) | SW(Re: 3999, Im: 0)
Bin [ 200] Hz: HW(Re: 2, Im: -2994) | SW(Re: 0, Im: -3000)
Bin [ 400] Hz: HW(Re: -5, Im: -1496) | SW(Re: 0, Im: -1499)
Bin [ 624] Hz: HW(Re: -5, Im: 1497) | SW(Re: 0, Im: 1500)
Bin [ 824] Hz: HW(Re: 3, Im: 2994) | SW(Re: 0, Im: 2999)
Bin [1014] Hz: HW(Re: 4007, Im: 0) | SW(Re: 3999, Im: 0)

View File

@@ -0,0 +1,11 @@
TARGET = fft_dma
CFLAGS += -O3 -g
C_SRCS := $(wildcard ./*.c )
OBJDIR = obj
COMMON_DIR = ../../bsp
GCC_DIR=../../../toolchains/loongson-gnu-toolchain-8.3-x86_64-loongarch32r-linux-gnusf-v2.0
PICOLIBC_DIR=../../../toolchains/picolibc
include ../../bsp/common.mk

View File

@@ -0,0 +1,179 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include <stdint.h>
#include <common_func.h>
#include <confreg_time.h>
#include <dma.h>
#include <fft.h>
// BSP板级支持包所需全局变量
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;
const float PI = 3.14159265358979323846;
// 软件FFT实现 (基2 DIT-FFT 算法)
void sw_fft(float re[], float im[], int n) {
int i, j, k, l;
float tr, ti, ur, ui, wr, wi;
j = 0;
for (i = 0; i < n - 1; i++) {
if (i < j) {
tr = re[i]; ti = im[i];
re[i] = re[j]; im[i] = im[j];
re[j] = tr; im[j] = ti;
}
k = n / 2;
while (k <= j) {
j -= k;
k /= 2;
}
j += k;
}
for (l = 1; l < n; l *= 2) {
ur = 1.0;
ui = 0.0;
wr = cos(PI / l);
wi = -sin(PI / l);
for (i = 0; i < n; i += 2 * l) {
ur = 1.0;
ui = 0.0;
for (j = 0; j < l; j++) {
int p = i + j;
int q = p + l;
tr = re[q] * ur - im[q] * ui;
ti = re[q] * ui + im[q] * ur;
re[q] = re[p] - tr;
im[q] = im[p] - ti;
re[p] += tr;
im[p] += ti;
float next_ur = ur * wr - ui * wi;
ui = ur * wi + ui * wr;
ur = next_ur;
}
}
}
}
// 全局对齐数组:作为 DMA 的源和目的内存
int32_t hw_in_re_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int32_t hw_in_im_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int32_t hw_out_re_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int32_t hw_out_im_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int main(int argc, char** argv)
{
unsigned int fft_csr = RegRead(FFT_CSR_REG);
printf("fft_csr init = %x\n", fft_csr);
// 获取外设的纯物理地址 (屏蔽高3位 0x1FFFFFFF)
uint32_t phys_fft_in_re = FFT_IN_RE_BASE & 0x1FFFFFFF;
uint32_t phys_fft_in_im = FFT_IN_IM_BASE & 0x1FFFFFFF;
uint32_t phys_fft_out_re = FFT_OUT_RE_BASE & 0x1FFFFFFF;
uint32_t phys_fft_out_im = FFT_OUT_IM_BASE & 0x1FFFFFFF;
// 获取内存数组的纯物理地址,并包装为 CPU 使用的无缓存(Uncached)指针
volatile int32_t *uncached_in_re = (volatile int32_t *)(((uint32_t)hw_in_re_arr & 0x1FFFFFFF) | 0xA0000000);
volatile int32_t *uncached_in_im = (volatile int32_t *)(((uint32_t)hw_in_im_arr & 0x1FFFFFFF) | 0xA0000000);
volatile int32_t *uncached_out_re = (volatile int32_t *)(((uint32_t)hw_out_re_arr & 0x1FFFFFFF) | 0xA0000000);
volatile int32_t *uncached_out_im = (volatile int32_t *)(((uint32_t)hw_out_im_arr & 0x1FFFFFFF) | 0xA0000000);
// 准备软件 FFT 测试数据
float sw_in_re[FFT_POINT_NUM];
float sw_in_im[FFT_POINT_NUM];
// 初始化测试波形
for (int i = 0; i < FFT_POINT_NUM; i++) {
float dc_part = 4000.0f;
float f10_part = 8000.0f * cos(2 * PI * 10.0 * i / FFT_POINT_NUM);
float f200_part = 6000.0f * sin(2 * PI * 200.0 * i / FFT_POINT_NUM);
float f400_part = 3000.0f * sin(2 * PI * 400.0 * i / FFT_POINT_NUM);
sw_in_re[i] = dc_part + f10_part + f200_part + f400_part;
sw_in_im[i] = 0.0f;
// 硬件输入需要转为整数存入无缓存内存
uncached_in_re[i] = (int32_t)sw_in_re[i];
uncached_in_im[i] = 0;
// 清理输出内存以防干扰
uncached_out_re[i] = 0xDEADBEEF;
uncached_out_im[i] = 0xDEADBEEF;
}
unsigned int tick_start, tick_end;
unsigned int hw_time, sw_time;
// 硬件加速 FFT 测试 (纯 DMA 搬运)
printf("\n--- Starting Hardware FFT with DMA ---\n");
tick_start = get_ns(); // 开始计时
uint32_t transfer_bytes = FFT_POINT_NUM * 4; // 1024个点 * 4字节
// MA 将数据从内存搬运到 FFT 输入外设
dma_start_transfer(0, ((uint32_t)hw_in_re_arr & 0x1FFFFFFF), phys_fft_in_re, transfer_bytes, 100);
dma_start_transfer(1, ((uint32_t)hw_in_im_arr & 0x1FFFFFFF), phys_fft_in_im, transfer_bytes, 200);
dma_wait_polling(0);
dma_wait_polling(1);
// 启动 FFT 并等待计算完成
fft_start();
fft_wait();
// DMA 将结果从 FFT 输出外设搬回内存
dma_start_transfer(0, phys_fft_out_re, ((uint32_t)hw_out_re_arr & 0x1FFFFFFF), transfer_bytes, 10);
dma_start_transfer(1, phys_fft_out_im, ((uint32_t)hw_out_im_arr & 0x1FFFFFFF), transfer_bytes, 20);
dma_wait_polling(0);
dma_wait_polling(1);
tick_end = get_ns(); // 结束计时
hw_time = tick_end - tick_start;
// 纯软件 FFT 测试
printf("--- Starting Software FFT ---\n");
tick_start = get_ns();
sw_fft(sw_in_re, sw_in_im, FFT_POINT_NUM);
tick_end = get_ns();
sw_time = tick_end - tick_start;
// 打印对比结果
printf("\n--- Performance Comparison ---\n");
printf("Timer Clock Freq : %lu Hz\n", CONFREG_CLOCKS_PER_SEC);
printf("Hardware FFT Time: %u ns (%.3f ms)\n", hw_time, (float)hw_time / 1000000.0);
printf("Software FFT Time: %u ns (%.3f ms)\n", sw_time, (float)sw_time / 1000000.0);
if (hw_time > 0) {
printf("Speedup Ratio : %.2fx\n", (float)sw_time / hw_time);
}
printf("\n--- Verification (Only showing Bins with energy > 10) ---\n");
for (int i = 0; i < FFT_POINT_NUM; i++) {
// CPU 通过无缓存指针读取 DMA 搬运回来的结果
int32_t hw_re = uncached_out_re[i];
int32_t hw_im = uncached_out_im[i];
if (fabs((float)hw_re) > 10 || fabs((float)hw_im) > 10) {
printf("Bin [%4d] Hz: HW(Re:%6d, Im:%6d) | SW(Re:%6d, Im:%6d)\n",
i,
hw_re, hw_im,
(int)sw_in_re[i] / FFT_POINT_NUM, (int)sw_in_im[i] / FFT_POINT_NUM);
}
}
return 0;
}

View File

@@ -0,0 +1,19 @@
fft_csr init = 0
--- Starting Hardware FFT with DMA ---
--- Starting Software FFT ---
--- Performance Comparison ---
Timer Clock Freq : 50000000 Hz
Hardware FFT Time: 407310 ns (0.407 ms)
Software FFT Time: 274786920 ns (274.787 ms)
Speedup Ratio : 674.64x
--- Verification (Only showing Bins with energy > 10) ---
Bin [ 0] Hz: HW(Re: 4007, Im: 0) | SW(Re: 4000, Im: 0)
Bin [ 10] Hz: HW(Re: 4006, Im: -1) | SW(Re: 3999, Im: 0)
Bin [ 200] Hz: HW(Re: 2, Im: -2994) | SW(Re: 0, Im: -3000)
Bin [ 400] Hz: HW(Re: -5, Im: -1496) | SW(Re: 0, Im: -1499)
Bin [ 624] Hz: HW(Re: -5, Im: 1497) | SW(Re: 0, Im: 1500)
Bin [ 824] Hz: HW(Re: 3, Im: 2994) | SW(Re: 0, Im: 2999)
Bin [1014] Hz: HW(Re: 4007, Im: 0) | SW(Re: 3999, Im: 0)

View File

@@ -32,7 +32,11 @@ unsigned long CONFREG_CLOCKS_PER_SEC = 50000000L;
unsigned long CORE_CLOCKS_PER_SEC = 33000000L;
/* ─── Memory layout ──────────────────────────────────────────────── */
#define EXTRAM_BASE 0x1c400000 /* ExtRAM: bit22=1 in RAM controller */
/* ExtRAM physical: 0x1c400000 (bit22=1 in RAM controller).
* Use uncached segment (0xA0000000) to bypass CPU data cache —
* otherwise writes never reach the SRAM model before testbench checks. */
#define EXTRAM_BASE_PHYS 0x1c400000
#define EXTRAM_BASE (0xA0000000 | (EXTRAM_BASE_PHYS & 0x1FFFFFFF))
#define SRC_BASE 0 /* word offset — source A/B matrices */
#define N_GROUPS 10 /* must match testbench default */