zynq7000- linux平台 PS读写PL测试

ZYNQ PL使用自定义AXI slave 模块 :

BD如下:

FPGA代码简单,可在vivado 自定义 AXI slave ip上的代码更改即可

系统制作完,直接在虚拟机上编译好可执行文件,在板子上运行即可:

./axilite -l 100

运行结果

linux上运行程序。代码可以自行改善。下面代码功能有:

1、遍历 PL寄存器,自定义测试寄存器个数

2、自定义测试轮数,指定对某寄存器读写

等等

注意:

vivado上BD分配的物理地址要和下面代码上能够对上。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <time.h>

#define AXI_BASE_ADDRESS 0x40000000  // 根据实际情况修改
#define MAP_SIZE 0x1000              // 4KB 映射空间
#define REGISTER_COUNT 34             // 测试的寄存器数量

static volatile uint32_t *axi_base = NULL;
static int mem_fd = -1;
static int running = 1;

// 信号处理函数,用于优雅退出
void signal_handler(int sig)
{
    printf("\n收到信号 %d,正在退出...\n", sig);
    running = 0;
}

// 映射物理内存到用户空间
int map_axi_memory(void)
{
    // 打开 /dev/mem 设备
    mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (mem_fd < 0) {
        perror("打开 /dev/mem 失败");
        return -1;
    }

    // 映射物理内存
    axi_base = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, AXI_BASE_ADDRESS);
    if (axi_base == MAP_FAILED) {
        perror("内存映射失败");
        close(mem_fd);
        return -1;
    }

    printf("成功映射物理内存:\n");
    printf("  物理地址: 0x%08X\n", AXI_BASE_ADDRESS);
    printf("  虚拟地址: %p\n", axi_base);
    printf("  映射大小: 0x%X (%d KB)\n", MAP_SIZE, MAP_SIZE / 1024);

    return 0;
}

// 取消内存映射
void unmap_axi_memory(void)
{
    if (axi_base != NULL && axi_base != MAP_FAILED) {
        munmap((void*)axi_base, MAP_SIZE);
        axi_base = NULL;
    }
    
    if (mem_fd >= 0) {
        close(mem_fd);
        mem_fd = -1;
    }
}

// 读取寄存器值
uint32_t read_register(int reg_index)
{
    if (axi_base == NULL || reg_index >= MAP_SIZE / 4) {
        printf("错误: 无效的寄存器索引 %d\n", reg_index);
        return 0;
    }
    
    uint32_t value = axi_base[reg_index];
    printf("读取 寄存器[%d]: 地址=%p, 值=0x%08X\n", 
           reg_index, &axi_base[reg_index], value);
    return value;
}

// 写入寄存器值
void write_register(int reg_index, uint32_t value)
{
    if (axi_base == NULL || reg_index >= MAP_SIZE / 4) {
        printf("错误: 无效的寄存器索引 %d\n", reg_index);
        return;
    }
    
    axi_base[reg_index] = value;
    printf("写入 寄存器[%d]: 地址=%p, 值=0x%08X\n", 
           reg_index, &axi_base[reg_index], value);
}

// 循环读写测试
void read_write_loop_test(int test_count)
{
    printf("\n=== 开始循环读写测试,共 %d 轮 ===\n", test_count);

	int error_count ;
	int error_flag  ;
	error_flag = 0;
    for (int i = 0; i < test_count && running; i++) {
		error_count = 0;
        printf("\n--- 第 %d 轮测试 ---\n", i + 1);
        
        // 写入测试数据
        for (int reg = 1; reg < REGISTER_COUNT; reg++) {
            uint32_t write_value = 0x1000 * (i + 1) + 0x100 * reg + 0xAB;
            write_register(reg, write_value);
            usleep(1000); // 1ms 延迟
        }
        
        // 读取验证
        for (int reg = 1; reg < REGISTER_COUNT; reg++) {
            uint32_t read_value = read_register(reg);
            uint32_t expected = 0x1000 * (i + 1) + 0x100 * reg + 0xAB;
            
            if (read_value == expected) {
                printf("寄存器[%d] ✅ 验证成功\n", reg);
            } else {
				error_count = error_count + 1;
                printf("寄存器[%d] ❌ 验证失败: 读取=0x%08X, 期望=0x%08X\n", 
                       reg, read_value, expected);
            }
            usleep(1000); // 1ms 延迟
        }
        
        // 每10轮显示进度
        if ((i + 1) % 10 == 0) {
            printf("已完成 %d/%d 轮测试\n", i + 1, test_count);
        }
        
        sleep(1); // 每秒一轮
		    printf ("读写错误数量 是: %d\n" ,error_count);
		
		if (error_count > 0) {
			error_flag = 1;
			printf("=== AXI SLave 读写在第 %d轮出现问题 推出测试 ===\n",test_count);
			break;
		}
    }
    printf("=== 数据错误标志 error_flag = %d ===\n",error_flag);
    printf("=== 循环读写测试完成 ===\n");
}

// 递增模式测试
void incremental_pattern_test(void)
{
    printf("\n=== 开始递增模式测试 ===\n");
    
    for (int i = 0; i < 16 && running; i++) {
        uint32_t pattern = 0x11111111 * i;
        
        printf("\n模式 %2d:\n", i);
        
        // 写入不同模式的数据
        write_register(0, pattern);
        write_register(1, ~pattern);
        write_register(2, pattern >> 16);
        write_register(3, pattern << 16);
        
        // 读取显示
        printf("读取结果: ");
        for (int reg = 0; reg < REGISTER_COUNT; reg++) {
            printf("0x%08X ", read_register(reg));
        }
        printf("\n");
        
        sleep(1);
    }
    
    printf("=== 递增模式测试完成 ===\n");
}

// 交互式测试模式
void interactive_test(void)
{
    printf("\n=== 进入交互式测试模式 ===\n");
    printf("命令说明:\n");
    printf("  r <索引>    - 读取寄存器\n");
    printf("  w <索引> <值> - 写入寄存器\n");
    printf("  s          - 显示所有寄存器状态\n");
    printf("  q          - 退出\n");
    
    char command[256];
    while (running) {
        printf("\n> ");
        fflush(stdout);
        
        if (fgets(command, sizeof(command), stdin) == NULL) {
            break;
        }
        
        // 解析命令
        if (strncmp(command, "r", 1) == 0) {
            int reg_index;
            if (sscanf(command + 1, "%d", &reg_index) == 1) {
                read_register(reg_index);
            } else {
                printf("用法: r <寄存器索引>\n");
            }
        }
        else if (strncmp(command, "w", 1) == 0) {
            int reg_index;
            uint32_t value;
            if (sscanf(command + 1, "%d %x", &reg_index, &value) == 2) {
                write_register(reg_index, value);
            } else {
                printf("用法: w <寄存器索引> <十六进制值>\n");
            }
        }
        else if (strncmp(command, "s", 1) == 0) {
            printf("当前寄存器状态:\n");
            for (int i = 0; i < REGISTER_COUNT; i++) {
                uint32_t value = axi_base[i];
                printf("  寄存器[%d]: 0x%08X\n", i, value);
            }
        }
        else if (strncmp(command, "q", 1) == 0) {
            break;
        }
        else {
            printf("未知命令。使用 r, w, s, 或 q\n");
        }
    }
}

// 显示使用说明
void print_usage(const char *program_name)
{
    printf("用法: %s [选项]\n", program_name);
    printf("选项:\n");
    printf("  -l <次数>   循环测试次数 (默认: 50)\n");
    printf("  -i          交互式模式\n");
    printf("  -p          递增模式测试\n");
    printf("  -a <地址>   AXI基地址 (十六进制, 默认: 0x%08X)\n", AXI_BASE_ADDRESS);
    printf("  -s <大小>   映射大小 (十六进制, 默认: 0x%X)\n", MAP_SIZE);
    printf("  -h          显示此帮助信息\n");
    printf("\n示例:\n");
    printf("  %s -l 100          # 循环测试100次\n", program_name);
    printf("  %s -i              # 交互式模式\n", program_name);
    printf("  %s -a 0xA0000000   # 指定基地址\n", program_name);
}

int main(int argc, char *argv[])
{
    int test_count = 10;
    int interactive_mode = 0;
    int pattern_mode = 0;
    uint32_t base_addr = AXI_BASE_ADDRESS;
    size_t map_size = MAP_SIZE;
    
    // 解析命令行参数
    int opt;
    while ((opt = getopt(argc, argv, "l:ipa:s:h")) != -1) {
        switch (opt) {
            case 'l':
                test_count = atoi(optarg);
                break;
            case 'i':
                interactive_mode = 1;
                break;
            case 'p':
                pattern_mode = 1;
                break;
            case 'a':
                base_addr = strtoul(optarg, NULL, 16);
                break;
            case 's':
                map_size = strtoul(optarg, NULL, 16);
                break;
            case 'h':
                print_usage(argv[0]);
                return 0;
            default:
                print_usage(argv[0]);
                return 1;
        }
    }
    
    // 注册信号处理
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    printf("AXI Lite PL寄存器测试应用程序\n");
    printf("基地址: 0x%08X, 映射大小: 0x%zX\n", base_addr, map_size);
    
    // 映射内存
    if (map_axi_memory() < 0) {
        return 1;
    }
    
    // 根据模式执行测试
    if (interactive_mode) {
        interactive_test();
    } else if (pattern_mode) {
        incremental_pattern_test();
    } else {
        read_write_loop_test(test_count);
    }
    
    // 清理资源
    unmap_axi_memory();
    printf("应用程序退出\n");
    
    return 0;
}
相关推荐
律宏阔3 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔3 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴3 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千4 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei374 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei374 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei374 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW4 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW4 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux
yunwei374 小时前
eBPF 开发者教程: 简单的 XDP 负载均衡器
linux·后端·性能优化