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:
37
sdk/software/bsp/drivers/dma.c
Normal file
37
sdk/software/bsp/drivers/dma.c
Normal 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;
|
||||||
|
}
|
||||||
14
sdk/software/bsp/drivers/fft.c
Normal file
14
sdk/software/bsp/drivers/fft.c
Normal 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);
|
||||||
|
}
|
||||||
30
sdk/software/bsp/include/dma.h
Normal file
30
sdk/software/bsp/include/dma.h
Normal 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);
|
||||||
16
sdk/software/bsp/include/fft.h
Normal file
16
sdk/software/bsp/include/fft.h
Normal 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();
|
||||||
11
sdk/software/examples/dma/Makefile
Normal file
11
sdk/software/examples/dma/Makefile
Normal 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
|
||||||
71
sdk/software/examples/dma/main.c
Normal file
71
sdk/software/examples/dma/main.c
Normal 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;
|
||||||
|
}
|
||||||
75
sdk/software/examples/dma/result.txt
Normal file
75
sdk/software/examples/dma/result.txt
Normal 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.
|
||||||
11
sdk/software/examples/fft/Makefile
Normal file
11
sdk/software/examples/fft/Makefile
Normal 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
|
||||||
156
sdk/software/examples/fft/main.c
Normal file
156
sdk/software/examples/fft/main.c
Normal 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;
|
||||||
|
}
|
||||||
19
sdk/software/examples/fft/result.txt
Normal file
19
sdk/software/examples/fft/result.txt
Normal 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)
|
||||||
11
sdk/software/examples/fft_dma/Makefile
Normal file
11
sdk/software/examples/fft_dma/Makefile
Normal 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
|
||||||
179
sdk/software/examples/fft_dma/main.c
Normal file
179
sdk/software/examples/fft_dma/main.c
Normal 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;
|
||||||
|
}
|
||||||
19
sdk/software/examples/fft_dma/result.txt
Normal file
19
sdk/software/examples/fft_dma/result.txt
Normal 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)
|
||||||
@@ -32,7 +32,11 @@ unsigned long CONFREG_CLOCKS_PER_SEC = 50000000L;
|
|||||||
unsigned long CORE_CLOCKS_PER_SEC = 33000000L;
|
unsigned long CORE_CLOCKS_PER_SEC = 33000000L;
|
||||||
|
|
||||||
/* ─── Memory layout ──────────────────────────────────────────────── */
|
/* ─── 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 SRC_BASE 0 /* word offset — source A/B matrices */
|
||||||
#define N_GROUPS 10 /* must match testbench default */
|
#define N_GROUPS 10 /* must match testbench default */
|
||||||
|
|||||||
Reference in New Issue
Block a user