支持RV32E的单周期NPC

📚 使用须知

  • 本博客内容仅供学习参考
  • 建议理解思路后独立实现
  • 欢迎交流讨论

task : 支持RV32E的单周期NPC

搭建面向riscv32e-npc的运行时环境

为NPC搭建基础设施

为NPC搭建sdb

为NPC添加trace支持

为NPC添加DiffTest支持

task : 为NPC添加DiffTest支持

DiffTest是处理器调试的一大杀手锏, 在为NPC实现更多指令之前, 为其搭建DiffTest是一个明智的选择. 在这里, DUTNPC, 而REF则选择NEMU. 为此, 你需要

  • nemu/src/cpu/difftest/ref.c中实现DiffTestAPI, 包括difftest_memcpy(), difftest_regcpy()difftest_exec(). 此外difftest_raise_intr()是为中断准备的, 目前暂不使用
  • NEMUmenuconfig中选择共享库作为编译的目标
Build target (Executable on Linux Native)  --->
  (X) Shared object (used as REF for differential testing)
  • 重新编译NEMU, 成功后将会生成动态库文件nemu/build/riscv32-nemu-interpreter-so
  • 在NPC的仿真环境中通过动态链接方式链接上述的动态库文件, 通过其中的API来实现DiffTest的功能, 具体可以参考NEMU的相关代码

尝试在打开DiffTest机制的情况下在NPC中正确运行dummy程序. 为了检查DiffTest机制是否生效, 你可以为NPC中addi指令的实现注入一个错误, 观察DiffTest是否能够按照预期地报告这一错误.

注意, 为了再次将NEMU编译成ELF, 你还需要修改NEMU中menuconfig的编译目标.

include/sdb.h
// include/sdb.h
#ifndef __SDB_H__
#define __SDB_H__

#include <stdbool.h>
#include <verilated_vcd_c.h>

enum SimState {
    SIM_RUNNING,
    SIM_PAUSED,
    SIM_FINISHED
};

// SDB 初始化
void init_sdb(VerilatedVcdC* tfp);
void sdb_set_batch_mode();

// SDB 主循环
void sdb_mainloop();

// 单步执行控制
bool sdb_should_pause();
void sdb_step_tick();
bool sdb_is_enabled();

// 波形控制
void sdb_flush_waveform();

extern int check_watchpoints();

#endif
csrc/dpi_interface.c
// csrc/dpi_interface.c
#include <iostream>
#include <cstdlib>
#include <cstdint>
#include <fstream>
#include <vector>
#include "verilated.h"
#include "verilated_dpi.h"
#include "../obj_dir/Vtop.h"
#include "../include/trace.h"

#ifdef __cplusplus
extern "C" {
#endif

// ==================== 全局调试变量 ====================
static uint32_t *cpu_gpr = NULL;  // 寄存器文件指针
static uint32_t *cpu_csr = NULL;  // CSR寄存器指针
static Vtop *top_ptr = NULL;      // Verilog顶层模块指针
static bool sdb_enabled = false;  // SDB使能标志
static bool ebreak_triggered = false;
static int ebreak_exit_code = 0;
static uint32_t last_pc = 0;
static uint32_t last_inst = 0;

// 修改这里:去掉 static,使变量全局可见
bool single_step_mode = false;

// ==================== 寄存器访问 ====================
void set_gpr_ptr(const svOpenArrayHandle r) {
    cpu_gpr = (uint32_t *)(((VerilatedDpiOpenVar*)r)->datap());
    printf("[DPI] GPR pointer set: %p\n", cpu_gpr);
}

void set_top_ptr(void* ptr) {
    top_ptr = (Vtop*)ptr;
    printf("[DPI] Top pointer set: %p\n", top_ptr);
}

uint32_t debug_read_gpr(int index) {
    if (cpu_gpr && index >= 0 && index < 32) {
        return cpu_gpr[index];
    }
    return 0xDEADBEEF;
}

uint32_t debug_read_pc() {
    if (top_ptr) {
        return top_ptr->pc;
    }
    return 0;
}

// ==================== 内存访问 ====================
static std::vector<uint8_t> pmem(64 * 1024 * 1024, 0); // 64MB内存
static uint32_t mem_base = 0x80000000;

void load_binary(const char* filename, uint32_t base_addr) {
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) {
        std::cerr << "Error: Cannot open file " << filename << std::endl;
        return;
    }
    
    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);
    
    if (base_addr < mem_base) {
        std::cerr << "Error: Base address below memory base" << std::endl;
        return;
    }
    
    uint32_t offset = base_addr - mem_base;
    if (offset + size > pmem.size()) {
        std::cerr << "Error: Binary too large for memory" << std::endl;
        return;
    }
    
    if (!file.read((char*)(pmem.data() + offset), size)) {
        std::cerr << "Error: Failed to read file" << std::endl;
        return;
    }
    
    printf("[DPI] Loaded %ld bytes to 0x%08x\n", size, base_addr);
    file.close();
}

int pmem_read(int raddr) {
    uint32_t addr = raddr;
    
    if (addr < mem_base) {
        return 0;
    }
    
    uint32_t offset = addr - mem_base;
    if (offset + 4 > pmem.size()) {
        return 0;
    }
    
    // 读取32位字(小端序)
    uint32_t value = 0;
    value |= pmem[offset + 0] << 0;
    value |= pmem[offset + 1] << 8;
    value |= pmem[offset + 2] << 16;
    value |= pmem[offset + 3] << 24;
    
    #ifdef CONFIG_MTRACE
    mtrace(1, addr, value, 4);  // type=1: Read, size=4 bytes
    #endif
    
    return value;
}

// 添加指令fetch trace
void trace_ifetch(uint32_t pc, uint32_t inst) {
    last_pc = pc;
    last_inst = inst;
    
    #ifdef CONFIG_ITRACE
    itrace(pc, inst);
    #endif
    
    #ifdef CONFIG_MTRACE
    mtrace(0, pc, inst, 4);  // type=0: Instruction Fetch
    #endif
}

void trace_func_call(uint32_t pc, uint32_t target, uint32_t ret_addr) {
    #ifdef CONFIG_FTRACE
    //printf("[FTRACE DEBUG] Function call: pc=0x%08x -> target=0x%08x, return=0x%08x\n",
    //       pc, target, ret_addr);
    ftrace(pc, target, ret_addr, 0);
    #endif
}

void trace_func_ret(uint32_t pc, uint32_t target) {
    #ifdef CONFIG_FTRACE
    //printf("[FTRACE DEBUG] Function return: pc=0x%08x -> target=0x%08x\n",
    //      pc, target);
    ftrace(pc, target, 0, 1);
    #endif
}

// 添加指令执行trace(供Verilog调用)
void trace_exec(uint32_t pc, uint32_t inst) {
    // 这里可以添加更多执行时的trace信息
}

void pmem_write(int waddr, int wdata, char wmask) {
    uint32_t addr = waddr;
    
    if (addr < mem_base) {
        return;
    }
    
    uint32_t offset = addr - mem_base;
    if (offset + 4 > pmem.size()) {
        return;
    }
    
    // 根据写掩码写入字节
    if (wmask & 0x1) pmem[offset + 0] = (wdata >> 0) & 0xFF;
    if (wmask & 0x2) pmem[offset + 1] = (wdata >> 8) & 0xFF;
    if (wmask & 0x4) pmem[offset + 2] = (wdata >> 16) & 0xFF;
    if (wmask & 0x8) pmem[offset + 3] = (wdata >> 24) & 0xFF;
}

// ==================== 调试控制 ====================
void debug_set_single_step(int enable) {
    single_step_mode = (enable != 0);
    printf("[DPI] Single step mode: %s\n", single_step_mode ? "ON" : "OFF");
}

int debug_should_pause() {
    return single_step_mode ? 1 : 0;
}

// ==================== ebreak处理 ====================
void ebreak_handler(int code) {
    printf("\n" "========================================" "\n");
    
    if (code == 0) {
        printf("HIT GOOD TRAP\n");
        printf("Program completed successfully!\n");
    } else {
        printf("HIT BAD TRAP\n");
        printf("Exit code: %d\n", code);
        printf("Program failed!\n");
    }
    
    printf("========================================" "\n");
    
    
    // 如果SDB启用,不立即退出,进入调试器
    if (sdb_enabled) {
        printf("Press Enter to continue debugging...\n");
        // 设置暂停标志,由SDB处理
    } else {
        Verilated::gotFinish(true);
    }
}

// ==================== 波形控制 ====================
void flush_waveform() {
    // 此函数将在SDB中调用以刷新波形
    printf("[DPI] Waveform flushed\n");
}

#ifdef __cplusplus
}
#endif
include/difftest.h
/***************************************************************************************
* NPC DiffTest 接口定义 - 与 NEMU 兼容
***************************************************************************************/

#ifndef __DIFFTEST_H__
#define __DIFFTEST_H__

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

// NEMU 兼容类型定义
typedef uint32_t word_t;
typedef uint32_t paddr_t;
typedef uint32_t vaddr_t;

// DiffTest 方向定义
#define DIFFTEST_TO_DUT 0
#define DIFFTEST_TO_REF 1

// CPU 状态结构体 (必须与 NEMU 完全一致)
typedef struct {
    // 32 个通用寄存器
    word_t gpr[32];
    // 程序计数器
    vaddr_t pc;
} CPU_state;

// NEMU 兼容函数声明
void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction);
void difftest_regcpy(void *dut, bool direction);
void difftest_exec(uint64_t n);
void difftest_raise_intr(word_t NO);
void difftest_init(int port);

// NPC DiffTest 接口
void npc_init_difftest(const char* ref_so_file, long img_size);
void npc_difftest_step(uint32_t pc, uint32_t inst);
void npc_difftest_enable();
void npc_difftest_disable();
void npc_difftest_close();
void difftest_skip_ref();
void difftest_skip_dut(int nr_ref, int nr_dut);

// DPI-C 接口
void difftest_init_dpi(const char* ref_so_file, long img_size);
void difftest_check_dpi(uint32_t pc, uint32_t inst);
void difftest_enable_dpi();
void difftest_disable_dpi();

#ifdef __cplusplus
}
#endif

#endif // __DIFFTEST_H__
csrc/difftest.c
/***************************************************************************************
* NPC DiffTest 实现 - 与 NEMU 兼容
***************************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include "../include/difftest.h"
#include "../include/npc.h"

// 全局 DiffTest 状态
static bool diff_enabled = false;
static bool skip_ref = false;
static int skip_dut_nr_inst = 0;
static void *ref_handle = NULL;

// NEMU 函数指针
static void (*ref_difftest_memcpy)(paddr_t addr, void *buf, size_t n, bool direction) = NULL;
static void (*ref_difftest_regcpy)(void *dut, bool direction) = NULL;
static void (*ref_difftest_exec)(uint64_t n) = NULL;
static void (*ref_difftest_raise_intr)(word_t NO) = NULL;
static void (*ref_difftest_init)(int port) = NULL;

// NPC 状态
static CPU_state npc_cpu;
static uint64_t inst_count = 0;

// 外部函数声明
extern "C" {
    // 寄存器访问
    uint32_t debug_read_gpr(int index);
    uint32_t debug_read_pc();
    
    // 内存访问
    int pmem_read(int raddr);
    
    // 调试控制
    void ebreak_handler(int code);
}

// ==================== 辅助函数 ====================

// 从 NPC 读取当前 CPU 状态
static void read_npc_state() {
    // 读取通用寄存器
    for (int i = 0; i < 32; i++) {
        npc_cpu.gpr[i] = debug_read_gpr(i);
    }
    
    // 读取 PC
    npc_cpu.pc = debug_read_pc();
}

// 检查寄存器是否匹配
static bool checkregs(CPU_state *ref, vaddr_t pc) {
    bool match = true;
    
    // 检查 PC
    if (npc_cpu.pc != ref->pc) {
        printf("DIFFTEST ERROR: PC mismatch at NPC inst #%lu\n", inst_count);
        printf("  NPC: 0x%08x\n", npc_cpu.pc);
        printf("  REF: 0x%08x\n", ref->pc);
        match = false;
    }
    
    // 检查通用寄存器
    for (int i = 0; i < 32; i++) {
        if (npc_cpu.gpr[i] != ref->gpr[i]) {
            // 跳过 x0 寄存器(总是为 0)
            if (i == 0) continue;
            
            printf("DIFFTEST ERROR: GPR x%02d mismatch at NPC inst #%lu\n", i, inst_count);
            printf("  NPC: 0x%08x (%d)\n", npc_cpu.gpr[i], npc_cpu.gpr[i]);
            printf("  REF: 0x%08x (%d)\n", ref->gpr[i], ref->gpr[i]);
            match = false;
        }
    }
    
    if (!match) {
        printf("\nFull register state at error:\n");
        printf("NPC PC: 0x%08x, REF PC: 0x%08x\n", npc_cpu.pc, ref->pc);
        
        for (int i = 0; i < 32; i++) {
            if (npc_cpu.gpr[i] != ref->gpr[i] && i != 0) {
                printf("x%02d: NPC=0x%08x REF=0x%08x %s\n", 
                       i, npc_cpu.gpr[i], ref->gpr[i],
                       (npc_cpu.gpr[i] == ref->gpr[i]) ? "" : " <-- MISMATCH");
            }
        }
        
        // 触发错误处理
        ebreak_handler(1);
    }
    
    return match;
}

// ==================== NEMU 风格的 DiffTest 接口 ====================

// 初始化 DiffTest(NEMU 风格)
void npc_init_difftest(const char* ref_so_file, long img_size) {
    if (ref_so_file == NULL) {
        printf("DIFFTEST INFO: No reference simulator specified, DiffTest disabled\n");
        return;
    }
    
    // 尝试加载参考模拟器动态库
    ref_handle = dlopen(ref_so_file, RTLD_LAZY);
    if (ref_handle == NULL) {
        printf("DIFFTEST WARNING: Failed to load reference simulator: %s\n", dlerror());
        printf("DIFFTEST INFO: Continuing without DiffTest\n");
        return;
    }
    
    // 获取函数指针
    ref_difftest_memcpy = (void (*)(paddr_t, void*, size_t, bool))dlsym(ref_handle, "difftest_memcpy");
    ref_difftest_regcpy = (void (*)(void*, bool))dlsym(ref_handle, "difftest_regcpy");
    ref_difftest_exec = (void (*)(uint64_t))dlsym(ref_handle, "difftest_exec");
    ref_difftest_raise_intr = (void (*)(word_t))dlsym(ref_handle, "difftest_raise_intr");
    ref_difftest_init = (void (*)(int))dlsym(ref_handle, "difftest_init");
    
    // 检查必需函数
    if (ref_difftest_memcpy == NULL || ref_difftest_regcpy == NULL || 
        ref_difftest_exec == NULL || ref_difftest_init == NULL) {
        printf("DIFFTEST ERROR: Reference simulator missing required functions\n");
        dlclose(ref_handle);
        ref_handle = NULL;
        return;
    }
    
    printf("DIFFTEST INFO: Reference simulator loaded: %s\n", ref_so_file);
    
    // 初始化参考模拟器
    ref_difftest_init(1234);  // 使用默认端口
    
    // 同步内存(从 NPC 复制到 REF)
    // 假设程序从 0x80000000 开始,大小为 img_size
    uint8_t *buf = (uint8_t*)malloc(img_size);
    if (buf == NULL) {
        printf("DIFFTEST ERROR: Failed to allocate memory buffer\n");
        return;
    }
    
    // 从 NPC 内存中读取数据
    for (long i = 0; i < img_size; i += 4) {
        uint32_t addr = 0x80000000 + i;
        uint32_t data = pmem_read(addr);
        
        buf[i + 0] = (data >> 0) & 0xFF;
        buf[i + 1] = (data >> 8) & 0xFF;
        buf[i + 2] = (data >> 16) & 0xFF;
        buf[i + 3] = (data >> 24) & 0xFF;
    }
    
    // 复制到参考模拟器
    ref_difftest_memcpy(0x80000000, buf, img_size, DIFFTEST_TO_REF);
    free(buf);
    
    // 读取 NPC 初始状态并复制到参考模拟器
    read_npc_state();
    ref_difftest_regcpy(&npc_cpu, DIFFTEST_TO_REF);
    
    // 启用 DiffTest
    diff_enabled = true;
    skip_ref = false;
    skip_dut_nr_inst = 0;
    inst_count = 0;
    
    printf("DIFFTEST INFO: DiffTest initialized and enabled (img_size=%ld)\n", img_size);
}

// 跳过参考模拟器的检查
void difftest_skip_ref() {
    skip_ref = true;
    skip_dut_nr_inst = 0;
}

// 跳过 NPC 指令
void difftest_skip_dut(int nr_ref, int nr_dut) {
    skip_dut_nr_inst += nr_dut;
    
    while (nr_ref-- > 0) {
        ref_difftest_exec(1);
    }
}

// 主要的 DiffTest 步骤函数
void npc_difftest_step(uint32_t pc, uint32_t inst) {
    if (!diff_enabled || ref_handle == NULL) {
        return;
    }
    
    // 读取 NPC 当前状态
    read_npc_state();
    
    // 处理跳过指令的情况
    if (skip_dut_nr_inst > 0) {
        CPU_state ref_r;
        ref_difftest_regcpy(&ref_r, DIFFTEST_TO_DUT);
        
        // 计算下一条指令的 PC(简化处理)
        uint32_t npc = pc + 4;
        
        if (ref_r.pc == npc) {
            skip_dut_nr_inst = 0;
            // 检查寄存器匹配
            checkregs(&ref_r, npc);
            return;
        }
        
        skip_dut_nr_inst--;
        if (skip_dut_nr_inst == 0) {
            printf("DIFFTEST ERROR: Cannot catch up with ref.pc = 0x%08x at pc = 0x%08x\n", 
                   ref_r.pc, pc);
            ebreak_handler(1);
        }
        return;
    }
    
    // 处理跳过参考模拟器的情况
    if (skip_ref) {
        ref_difftest_regcpy(&npc_cpu, DIFFTEST_TO_REF);
        skip_ref = false;
        return;
    }
    
    // 正常执行流程
    inst_count++;
    
    // 让参考模拟器执行一条指令
    ref_difftest_exec(1);
    
    // 获取参考模拟器的状态
    CPU_state ref_state;
    memset(&ref_state, 0, sizeof(ref_state));
    ref_difftest_regcpy(&ref_state, DIFFTEST_TO_DUT);
    
    // 检查状态匹配
    checkregs(&ref_state, pc);
    
    // 定期输出进度
    if (inst_count % 1000 == 0) {
        printf("DIFFTEST INFO: %lu instructions compared, PC=0x%08x\n", 
               inst_count, pc);
    }
}

// 启用/禁用 DiffTest
void npc_difftest_enable() {
    if (ref_handle != NULL) {
        diff_enabled = true;
        printf("DIFFTEST INFO: Enabled\n");
    }
}

void npc_difftest_disable() {
    diff_enabled = false;
    printf("DIFFTEST INFO: Disabled\n");
}

// 清理资源
void npc_difftest_close() {
    if (ref_handle != NULL) {
        dlclose(ref_handle);
        ref_handle = NULL;
        printf("DIFFTEST INFO: Reference simulator unloaded\n");
    }
    
    printf("DIFFTEST INFO: Total instructions compared: %lu\n", inst_count);
}

// ==================== DPI-C 接口 ====================

// DPI-C 初始化包装函数
void difftest_init_dpi(const char* ref_so_file, long img_size) {
    npc_init_difftest(ref_so_file, img_size);
}

// DPI-C 检查包装函数
void difftest_check_dpi(uint32_t pc, uint32_t inst) {
    npc_difftest_step(pc, inst);
}

// DPI-C 启用/禁用包装函数
void difftest_enable_dpi() {
    npc_difftest_enable();
}

void difftest_disable_dpi() {
    npc_difftest_disable();
}
vsrc/top.v
// vsrc/top.v
// ==================== 顶层模块定义 ====================
module top(
    input clk,
    input rst,
    output reg [31:0] outdata,
    output reg [31:0] pc
);

// 内部信号声明
reg [31:0] next_pc;
wire [31:0] instruction;

// 寄存器文件信号
/* verilator lint_off UNOPTFLAT */
wire [4:0] rs1_addr, rs2_addr, rd_addr;
/* verilator lint_on UNOPTFLAT */
wire [31:0] rs1_data, rs2_data;
wire [31:0] rf_wdata;
wire rf_wen;

// 立即数信号
wire [31:0] imm_i, imm_u, imm_j;

// ALU信号
wire [31:0] alu_a, alu_b, alu_result;
wire [3:0] alu_op;

// 控制信号
wire is_auipc, is_lui, is_jal, is_jalr;
wire is_addi, is_sw, is_ebreak;
wire pc_src;
wire [31:0] jump_target;

// 写回数据选择
wire [1:0] wb_sel;

// 退出码信号
reg [31:0] exit_code;

// IDU输出信号
wire trace_call_en;
wire trace_ret_en;
wire [31:0] trace_ret_addr;
    
// 避免重复记录的寄存器
reg [31:0] last_trace_pc;
reg last_trace_valid;

reg idu_ftrace_call;
reg idu_ftrace_ret;
reg [31:0] idu_ftrace_target;
reg [31:0] idu_ftrace_ret_addr;

// ==================== DPI-C函数导入 ====================
import "DPI-C" function int pmem_read(input int raddr);
import "DPI-C" function void ebreak_handler(input int code);
import "DPI-C" function void set_top_ptr(input chandle ptr);
import "DPI-C" function void trace_func_call(input int pc, input int target, input int ret_addr);
import "DPI-C" function void trace_func_ret(input int pc, input int target);

// DiffTest DPI-C函数
import "DPI-C" function void difftest_init_dpi(input string ref_so_file, input longint img_size);
import "DPI-C" function void difftest_check_dpi(input int pc, input int inst);
import "DPI-C" function void difftest_enable_dpi();
import "DPI-C" function void difftest_disable_dpi();

// ==================== DiffTest配置 ====================
parameter DIFFTEST_ENABLED = 1'b1;
parameter DIFFTEST_REF_SO = "build/riscv32-nemu-interpreter-so";
parameter IMAGE_SIZE = 64 * 1024;  // 64KB

// ==================== DiffTest状态 ====================
reg diff_enabled;
reg [63:0] diff_inst_count;

// ==================== PC更新逻辑 ====================
always @(posedge clk or posedge rst) begin
    if (rst) begin
        pc <= 32'h8000_0000;
        exit_code <= 32'b0;
        diff_enabled <= DIFFTEST_ENABLED;
        diff_inst_count <= 0;
    end else begin
        pc <= next_pc;
        
        // 如果当前指令是ebreak,保存a0寄存器值作为退出码
        if (is_ebreak) begin
            exit_code <= rs1_data;
        end
    end
end

// PC+4计算
wire [31:0] pc_plus_4 = pc + 4;

// 下一PC值选择
always @(*) begin
    if (pc_src) begin
        next_pc = jump_target;
    end else begin
        next_pc = pc_plus_4;
    end
end

always @(posedge clk) begin
    if (!rst) begin
        // 处理函数调用跟踪
        if (idu_ftrace_call) begin
            trace_func_call(pc, idu_ftrace_target, idu_ftrace_ret_addr);
        end
        // 处理函数返回跟踪
        if (idu_ftrace_ret) begin
            trace_func_ret(pc, idu_ftrace_target);
        end
    end
end

// ==================== 模块实例化 ====================
ysyx_25110281_ifu ifu(
    .pc(pc),
    .clk(clk),
    .ins(instruction)
);

ysyx_25110281_idu idu(
    .ins(instruction),
    .pc(pc),
    .rs1_data(rs1_data),
    
    // 寄存器地址输出
    .rs1_addr(rs1_addr),
    .rs2_addr(rs2_addr),
    .rd_addr(rd_addr),
    
    // 立即数输出
    .imm_i(imm_i),
    .imm_u(imm_u),
    .imm_j(imm_j),
    
    // 控制信号输出
    .is_auipc(is_auipc),
    .is_lui(is_lui),
    .is_jal(is_jal),
    .is_jalr(is_jalr),
    .is_addi(is_addi),
    .is_sw(is_sw),
    .is_ebreak(is_ebreak),
    
    // ALU控制
    .alu_op(alu_op),
    
    // PC控制
    .pc_src(pc_src),
    .jump_target(jump_target),
    
    // 写回控制
    .wb_sel(wb_sel),
    .rf_wen(rf_wen),
    
    .ftrace_call(idu_ftrace_call),
    .ftrace_ret(idu_ftrace_ret),
    .ftrace_target(idu_ftrace_target),
    .ftrace_ret_addr(idu_ftrace_ret_addr)
    
);

RegisterFile regfile(
    .clk(clk),
    .rst(rst),
    .raddr1(rs1_addr),
    .raddr2(rs2_addr),
    .rdata1(rs1_data),
    .rdata2(rs2_data),
    .waddr(rd_addr),
    .wdata(rf_wdata),
    .wen(rf_wen)
);

ysyx_25110281_alu alu(
    .a(alu_a),
    .b(alu_b),
    .op(alu_op),
    .result(alu_result)
);

// ==================== 数据通路连接 ====================

// ALU输入A选择:auipc时选PC,其他选rs1
assign alu_a = is_auipc ? pc : rs1_data;

// ALU输入B选择:立即数
assign alu_b = imm_i;

// 写回数据选择
assign rf_wdata = (wb_sel == 2'b00) ? alu_result :
                  (wb_sel == 2'b01) ? pc_plus_4 :
                  (wb_sel == 2'b10) ? imm_u :
                  32'b0;

// ==================== DiffTest逻辑 ====================
always @(posedge clk) begin
    if (rst) begin
        diff_inst_count <= 0;
    end else if (diff_enabled) begin
        // 跳过无效指令
        if (instruction === 32'bx) begin
            return;
        end
        
        // 跳过ebreak指令
        if (is_ebreak) begin
            return;
        end
        
        // 执行DiffTest检查
        difftest_check_dpi(pc, instruction);
        diff_inst_count <= diff_inst_count + 1;
        
        // 定期输出状态
        if (diff_inst_count % 1000 == 0 && diff_inst_count != 0) begin
            $display("[DIFFTEST] Processed %0d instructions", diff_inst_count);
        end
    end
end

// ==================== DiffTest初始化 ====================
initial begin
    if (DIFFTEST_ENABLED) begin
        // 等待复位完成
        #1000;
        
        $display("[DIFFTEST] Initializing with reference: %s", DIFFTEST_REF_SO);
        difftest_init_dpi(DIFFTEST_REF_SO, IMAGE_SIZE);
        
        // 跳过前几条指令的检查
        #100;
        
        $display("[DIFFTEST] Initialization complete");
    end
end

// ==================== ebreak处理 ====================
always @(posedge clk) begin
	if (is_ebreak && !rst) begin
        $display("Time=%0t: Detected ebreak, exit code = %0d", $time, exit_code);
        ebreak_handler(exit_code);
    end
end

// ==================== 最终化 ====================
final begin
    if (DIFFTEST_ENABLED) begin
        $display("[DIFFTEST] Final statistics:");
        $display("[DIFFTEST] Total instructions compared: %0d", diff_inst_count);
    end
end

endmodule
csrc/main.c
/***************************************************************************************
* NPC 主程序 - 集成完整 SDB 和 DiffTest
***************************************************************************************/

#include "verilated.h"
#include "verilated_vcd_c.h"
#include "../obj_dir/Vtop.h"
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <getopt.h>
#include "../include/sdb.h"
#include "../include/npc.h"
#include "../include/trace.h"
#include "../include/difftest.h"

// 外部 C 函数
extern "C" {
    // 寄存器访问
    uint32_t debug_read_gpr(int index);
    uint32_t debug_read_pc();
    void set_gpr_ptr(uint32_t* ptr);
    void set_top_ptr(void* ptr);
    
    // 内存访问
    int pmem_read(int raddr);
    void pmem_write(int waddr, int wdata, char wmask);
    void load_binary(const char* filename, uint32_t base_addr);
    
    // 调试控制
    void debug_set_single_step(int enable);
    int debug_should_pause();
    void ebreak_handler(int code);
    
    // SDB 函数
    void cpu_exec(uint64_t n);
    void isa_reg_display();
    
    // DiffTest 函数
    void npc_init_difftest(const char* ref_so_file, long img_size);
    void npc_difftest_enable();
    void npc_difftest_disable();
    void npc_difftest_close();
}

// 全局变量定义
VerilatedContext* contextp = NULL;
VerilatedVcdC* tfp = NULL;
static Vtop* top;

SimState sim_state = SIM_RUNNING;
static uint64_t step_count = 0;

// 在这里定义 npc_exit_flag,使其全局可见
int npc_exit_flag = 0;

// 声明外部变量和函数
extern "C" {
    extern bool single_step_mode;
}

// 步进和波形记录
void step_and_dump_wave() {
    top->clk = !top->clk;
    top->eval();
    contextp->timeInc(1);
    if (tfp) {
        tfp->dump(contextp->time());
    }
}

// cpu_exec 实现(SDB 调用)
extern "C" void cpu_exec(uint64_t n) {
    if (n == (uint64_t)-1) {
        // 连续执行:设置 step_count = 0 表示无限执行
        step_count = 0;
        sim_state = SIM_RUNNING;
        printf("Starting continuous execution...\n");
    } else {
        // 单步执行 N 条指令
        step_count = n*2;
        sim_state = SIM_RUNNING;
        printf("Stepping %lu instructions...\n", n);
    }
}

// 参数解析
struct SimConfig {
    const char* binary_file = nullptr;
    const char* diff_so_file = nullptr;
    const char* log_file = nullptr;
    uint32_t entry_addr = 0x80000000;
    bool dump_wave = true;
    bool batch_mode = false;
    bool difftest_enabled = true;
    int max_cycles = 1000000;
} config;

// 获取文件大小
long get_file_size(const char* filename) {
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) {
        return 0;
    }
    return file.tellg();
}

// NEMU风格的参数解析
static int parse_args(int argc, char *argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " [OPTION...] IMAGE [args]\n\n";
        std::cerr << "Options:\n";
        std::cerr << "  -b,--batch              run with batch mode\n";
        std::cerr << "  -l,--log=FILE           output log to FILE\n";
        std::cerr << "  -d,--diff=REF_SO        run DiffTest with reference REF_SO\n";
        std::cerr << "  -nowave                 disable waveform dumping\n";
        std::cerr << "  -maxcycles=N            maximum simulation cycles\n";
        std::cerr << "  -nodifftest             disable DiffTest\n";
        std::cerr << "  -help                   show this help message\n";
        return -1;
    }
    
    // NEMU 风格的选项表
    const struct option table[] = {
        {"batch"    , no_argument      , NULL, 'b'},
        {"log"      , required_argument, NULL, 'l'},
        {"diff"     , required_argument, NULL, 'd'},
        {"help"     , no_argument      , NULL, 'h'},
        {0          , 0                , NULL,  0 },
    };
    
    int o;
    while ((o = getopt_long(argc, argv, "-bhl:d:", table, NULL)) != -1) {
        switch (o) {
            case 'b':
                config.batch_mode = true;
                sdb_set_batch_mode();
                break;
            case 'l':
                config.log_file = optarg;
                break;
            case 'd':
                config.diff_so_file = optarg;
                break;
            case 1:  // 非选项参数(通常是镜像文件名)
                config.binary_file = optarg;
                break;
            default:
                return -1;
        }
    }
    
    // 检查其他参数
    for (int i = optind; i < argc; i++) {
        if (strcmp(argv[i], "-nowave") == 0) {
            config.dump_wave = false;
        } else if (strcmp(argv[i], "-maxcycles") == 0 && i + 1 < argc) {
            config.max_cycles = atoi(argv[++i]);
        } else if (strcmp(argv[i], "-nodifftest") == 0) {
            config.difftest_enabled = false;
        } else if (strcmp(argv[i], "-entry") == 0 && i + 1 < argc) {
            config.entry_addr = strtoul(argv[++i], NULL, 0);
        }
    }
    
    // 如果没有指定DiffTest参考模拟器,使用默认值
    if (config.difftest_enabled && !config.diff_so_file) {
        config.diff_so_file = "build/riscv32-nemu-interpreter-so";
    }
    
    return 0;
}

void print_welcome() {
    printf("\n");
    printf("    _   __  ____   ___ \n");
    printf("   / | / / / __ \\ / _ \\\n");
    printf("  /  |/ / / /_/ //  __/\n");
    printf(" / /|  / / ____/ \\___/ \n");
    printf("/_/ |_/ /_/            \n");
    printf("\n");
    printf("Welcome to riscv32-NPC!\n");
    printf("For help, type \"help\"\n");
    printf("\n");
}

int main(int argc, char** argv) {
    print_welcome();
    
    // 解析参数(NEMU风格)
    if (parse_args(argc, argv) < 0) {
        return 1;
    }
    
    // 检查文件
    if (!config.binary_file) {
        std::cerr << "Error: No binary file specified\n";
        return 1;
    }
    
    std::ifstream file(config.binary_file);
    if (!file) {
        std::cerr << "Error: Cannot open binary file: " << config.binary_file << std::endl;
        return 1;
    }
    file.close();
    
    // 获取二进制文件大小
    long img_size = get_file_size(config.binary_file);
    if (img_size <= 0) {
        std::cerr << "Error: Invalid binary file size\n";
        return 1;
    }
    
    // Trace配置
    const char* itrace_file = config.log_file ? "itrace.log" : NULL;
    const char* mtrace_file = config.log_file ? "mtrace.log" : NULL;
    const char* ftrace_file = config.log_file ? "ftrace.log" : NULL;
    
    // 初始化 Verilator
    contextp = new VerilatedContext;
    top = new Vtop{contextp};
    
    set_top_ptr(top);
    
    // 设置波形
    if (config.dump_wave) {
        contextp->traceEverOn(true);
        tfp = new VerilatedVcdC;
        top->trace(tfp, 99);
        tfp->open("waveform.vcd");
    }
    
    // 初始化 SDB
    init_sdb(tfp);
    
    // 初始化Trace系统
    init_trace(itrace_file, mtrace_file, ftrace_file);
    
    // 加载二进制
    printf("[NPC] Loading binary: %s (size: %ld bytes) to 0x%08x\n", 
           config.binary_file, img_size, config.entry_addr);
    load_binary(config.binary_file, config.entry_addr);
    
    // 初始化 DiffTest
    if (config.difftest_enabled && config.diff_so_file) {
        printf("[DIFFTEST] Initializing with reference: %s\n", config.diff_so_file);
        npc_init_difftest(config.diff_so_file, img_size);
    } else if (config.difftest_enabled) {
        printf("[DIFFTEST] No reference simulator specified\n");
        printf("[DIFFTEST] Continuing without DiffTest\n");
        config.difftest_enabled = false;
    } else {
        printf("[DIFFTEST] Disabled\n");
    }
    
    // 复位序列
    printf("[NPC] Starting reset sequence...\n");
    top->rst = 1;
    for (int i = 0; i < 5; i++) {
        step_and_dump_wave();
    }
    top->rst = 0;
    printf("[NPC] Reset completed\n");
    
    std::cout << "========================================" << std::endl;
    std::cout << "NPC Simulator with SDB and DiffTest Started" << std::endl;
    std::cout << "Binary: " << config.binary_file << std::endl;
    std::cout << "DiffTest: " << (config.difftest_enabled ? "ENABLED" : "DISABLED") << std::endl;
    std::cout << "Type 'help' for SDB commands" << std::endl;
    std::cout << "========================================" << std::endl;
    
    // 初始化仿真状态 - 确保停在 SDB
    sim_state = SIM_PAUSED;  // 初始状态为暂停
    step_count = 0;
    
    // 修改仿真循环部分
    int cycle_count = 0;

    while (cycle_count < config.max_cycles && !Verilated::gotFinish() && !npc_exit_flag) {
        if (sim_state == SIM_PAUSED) {
            // 进入 SDB
            if (tfp) tfp->flush();
            sdb_mainloop();
            
            // 如果 SDB 设置了退出标志,退出循环
            if (npc_exit_flag) {
                break;
            }
        }
        else if (sim_state == SIM_RUNNING) {
            if (step_count > 0) {
                // 单步执行模式:执行指定数量的周期
                step_and_dump_wave();
                cycle_count++;
                step_count--;
                
                if (step_count == 0) {
                    sim_state = SIM_PAUSED;  // 单步执行完成,暂停
                    printf("Step execution completed. Total cycles: %d\n", cycle_count);
                }
            }
            else if (step_count == 0) {
                // 连续执行模式
                step_and_dump_wave();
                cycle_count++;
                
                // 检查是否应该暂停(监视点触发或 ebreak)
                if (check_watchpoints()) {
                    sim_state = SIM_PAUSED;
                    printf("Watchpoint triggered. Pausing at cycle %d\n", cycle_count);
                }
                
                // 定期输出进度
                if (cycle_count % 10000 == 0) {
                    printf("[NPC] Cycle: %d, PC: 0x%08x\n", cycle_count, debug_read_pc());
                }
            }
        }
        else {
            // 其他状态,等待
            usleep(10000);
        }
    }
    
    // 关闭DiffTest
    if (config.difftest_enabled) {
        npc_difftest_close();
    }
    
    // 关闭trace系统
    close_trace();
    
    // 清理
    if (tfp) {
        tfp->close();
        delete tfp;
    }
    delete top;
    delete contextp;
    
    std::cout << "========================================" << std::endl;
    std::cout << "NPC simulation finished. Cycles: " << cycle_count << std::endl;
    std::cout << "========================================" << std::endl;
    
    return 0;
}
Makefile
# NPC Makefile - 完整修复版本

CFLAGS += -I./include -g -O2 -Wall
LDFLAGS += -lreadline -ldl

# 配置选项
CONFIG_ITRACE ?= y
CONFIG_MTRACE ?= y
CONFIG_FTRACE ?= y
CONFIG_DIFFTEST ?= y  # 添加DiffTest配置

# 添加Capstone相关配置
ifneq ($(CONFIG_ITRACE),)
# Capstone配置
CAPSTONE_DIR = tools/capstone/repo
CAPSTONE_LIB = $(CAPSTONE_DIR)/libcapstone.so
CAPSTONE_INC = $(CAPSTONE_DIR)/include

# 确保Capstone库已构建
$(CAPSTONE_LIB):
	@echo "Building Capstone library..."
	$(MAKE) -C tools/capstone/repo
	@echo "Capstone library built successfully"

CFLAGS += -I$(CAPSTONE_INC) -DCONFIG_ITRACE
# 使用绝对路径链接库
CAPSTONE_ABS_DIR = $(realpath $(CAPSTONE_DIR))
LDFLAGS += -L$(CAPSTONE_ABS_DIR) -Wl,-rpath,$(CAPSTONE_ABS_DIR) -lcapstone
endif

ifneq ($(CONFIG_MTRACE),)
CFLAGS += -DCONFIG_MTRACE
endif

ifneq ($(CONFIG_FTRACE),)
CFLAGS += -DCONFIG_FTRACE
endif

# 添加DiffTest配置
ifneq ($(CONFIG_DIFFTEST),)
CFLAGS += -DCONFIG_DIFFTEST
# 添加dlopen支持
LDFLAGS += -ldl
endif

# 源文件
VSRC = $(wildcard ./vsrc/*.v)
CSRC = $(wildcard ./csrc/*.c)

# 根据配置排除或包含trace源文件
ifeq ($(CONFIG_ITRACE)$(CONFIG_MTRACE)$(CONFIG_FTRACE),)
CSRC := $(filter-out ./csrc/trace.c, $(CSRC))
endif

# 添加difftest.c到源文件列表
ifneq ($(CONFIG_DIFFTEST),)
CSRC += ./csrc/difftest.c
endif

# 默认目标
all: build

# 检查NEMU是否存在
check-nemu:
	@if [ ! -f "../nemu/build/riscv32-nemu-interpreter-so" ]; then \
		echo "WARNING: NEMU reference simulator not found at ../nemu/build/riscv32-nemu-interpreter-so"; \
		echo "DiffTest will be disabled at runtime"; \
		echo ""; \
		echo "To build NEMU:"; \
		echo "  cd ../nemu && make isa=riscv32 nemu-interpreter-so"; \
	else \
		echo "NEMU reference found: ../nemu/build/riscv32-nemu-interpreter-so"; \
	fi

# 构建 NPC 仿真器
build: $(CAPSTONE_LIB) check-nemu
	@echo "Building NPC simulator with trace and DiffTest support..."
	@echo "Config: ITRACE=$(CONFIG_ITRACE) MTRACE=$(CONFIG_MTRACE) FTRACE=$(CONFIG_FTRACE) DIFFTEST=$(CONFIG_DIFFTEST)"
	@verilator -Wno-fatal -Wno-WIDTH \
		$(VSRC) $(CSRC) \
		--top-module top \
		--cc \
		--trace \
		--exe \
		--build \
		-CFLAGS "$(CFLAGS)" \
		-LDFLAGS "$(LDFLAGS)" \
		--Mdir ./obj_dir
	@echo "Build completed: ./obj_dir/Vtop"

# 运行
run: build
	@if [ -z "$(ARGS)" ]; then \
		echo "========================================"; \
		echo "Error: No binary file specified"; \
		echo ""; \
		echo "Usage: make run ARGS=\"<path/to/binary.bin> [options]\""; \
		echo ""; \
		echo "Example:"; \
		echo "  make run ARGS=\"test.bin\""; \
		echo "  make run ARGS=\"test.bin -batch\""; \
		echo "  make run ARGS=\"test.bin -d ../nemu/build/riscv32-nemu-interpreter-so\""; \
		echo "========================================"; \
		exit 1; \
	fi
	
	@echo "========================================"
	@echo "Running NPC with: $(ARGS)"
	@echo "========================================"
	
	# 检查二进制文件
	@BINARY=$$(echo $(ARGS) | cut -d' ' -f1); \
	if [ ! -f "$$BINARY" ]; then \
		echo "Error: File not found: $$BINARY"; \
		exit 1; \
	fi
	
	# 运行仿真器
	@./obj_dir/Vtop $(ARGS) || true
	
	@echo "========================================"
	@echo "NPC simulation finished"
	@echo "========================================"

# 批处理模式
batch: build
	@if [ -z "$(ARGS)" ]; then \
		echo "Usage: make batch ARGS=<binary.bin>"; \
		exit 1; \
	fi
	@./obj_dir/Vtop $(ARGS) -batch

# 查看波形
wave:
	@if [ -f "waveform.vcd" ]; then \
		gtkwave waveform.vcd & \
	else \
		echo "No waveform found. Run 'make run' first."; \
	fi

# 清理
clean:
	rm -rf obj_dir *.vcd *.log test.bin

# 创建简单测试程序
test.bin:
	@echo "Creating test program using Python..."
	@python3 -c "with open('test.bin', 'wb') as f: f.write(b'\x13\x01\x10\x00\x93\x02\xa0\x00\x73\x00\x10\x00')"
	@echo "Created test.bin"
	@echo "Content verification:"
	@hexdump -C test.bin

# 创建dummy测试程序
test/dummy.bin:
	@mkdir -p test
	@echo "Creating RISC-V dummy test program..."
	@cat > test/dummy.s << 'EOF'
.text
.global _start
_start:
    # Simple test program
    addi x1, x0, 1      # x1 = 1
    addi x2, x1, 2      # x2 = 3
    addi x3, x2, 3      # x3 = 6
    
    # Test lui
    lui x4, 0x12345     # x4 = 0x12345000
    
    # Test auipc
    auipc x5, 0x10000   # x5 = PC + 0x10000000
    
    # Test jal (skip next instruction)
    jal x6, target
    addi x7, x0, 1      # This should be skipped
target:
    addi x8, x0, 2      # x8 = 2
    
    # Success exit
    addi a0, x0, 0      # Exit code 0 (success)
    ebreak
EOF
	@riscv64-unknown-elf-as -march=rv32i -mabi=ilp32 -o test/dummy.o test/dummy.s
	@riscv64-unknown-elf-ld -Ttext=0x80000000 -o test/dummy.elf test/dummy.o
	@riscv64-unknown-elf-objcopy -O binary test/dummy.elf test/dummy.bin
	@echo "Dummy program created: test/dummy.bin"

# 测试 SDB
test: test.bin build
	@echo "========================================"
	@echo "Testing SDB..."
	@echo "Instructions:"
	@echo "  1. addi x2, x0, 1   # x2 = 1"
	@echo "  2. addi x5, x0, 10  # x5 = 10"
	@echo "  3. ebreak           # breakpoint"
	@echo "========================================"
	@./obj_dir/Vtop test.bin

# 测试DiffTest
test-diff: test/dummy.bin build
	@echo "========================================"
	@echo "Testing DiffTest with dummy program"
	@echo "========================================"
	@if [ -f "../nemu/build/riscv32-nemu-interpreter-so" ]; then \
		./obj_dir/Vtop test/dummy.bin -d ../nemu/build/riscv32-nemu-interpreter-so || true; \
	else \
		echo "NEMU reference not found, running without DiffTest"; \
		./obj_dir/Vtop test/dummy.bin -nodifftest || true; \
	fi

.PHONY: all build run batch wave clean test test-diff check-nemu

新增文件:

include/difftest.h - DiffTest头文件,定义与NEMU兼容的接口
csrc/difftest.c - DiffTest核心实现,与NEMU完全兼容
setup_difftest_env.sh - 环境设置脚本

修改文件:

vsrc/top.v - 集成DiffTest DPI-C接口,添加DiffTest逻辑
csrc/main.c - 添加DiffTest支持,NEMU风格参数解析
Makefile - 添加DiffTest配置选项和构建规则

Makefile

Makefile:158: *** missing separator. Stop.

直接使用这个简单的测试程序。

Makefile
# NPC Makefile - 完整修复版本

CFLAGS += -I./include -g -O2 -Wall
LDFLAGS += -lreadline -ldl

# 配置选项
CONFIG_ITRACE ?= y
CONFIG_MTRACE ?= y
CONFIG_FTRACE ?= y
CONFIG_DIFFTEST ?= y  # 添加DiffTest配置

# 添加Capstone相关配置
ifneq ($(CONFIG_ITRACE),)
# Capstone配置
CAPSTONE_DIR = tools/capstone/repo
CAPSTONE_LIB = $(CAPSTONE_DIR)/libcapstone.so
CAPSTONE_INC = $(CAPSTONE_DIR)/include

# 确保Capstone库已构建
$(CAPSTONE_LIB):
	@echo "Building Capstone library..."
	$(MAKE) -C tools/capstone/repo
	@echo "Capstone library built successfully"

CFLAGS += -I$(CAPSTONE_INC) -DCONFIG_ITRACE
# 使用绝对路径链接库
CAPSTONE_ABS_DIR = $(realpath $(CAPSTONE_DIR))
LDFLAGS += -L$(CAPSTONE_ABS_DIR) -Wl,-rpath,$(CAPSTONE_ABS_DIR) -lcapstone
endif

ifneq ($(CONFIG_MTRACE),)
CFLAGS += -DCONFIG_MTRACE
endif

ifneq ($(CONFIG_FTRACE),)
CFLAGS += -DCONFIG_FTRACE
endif

# 添加DiffTest配置
ifneq ($(CONFIG_DIFFTEST),)
CFLAGS += -DCONFIG_DIFFTEST
# 添加dlopen支持
LDFLAGS += -ldl
endif

# 源文件
VSRC = $(wildcard ./vsrc/*.v)
CSRC = $(wildcard ./csrc/*.c)

# 根据配置排除或包含trace源文件
ifeq ($(CONFIG_ITRACE)$(CONFIG_MTRACE)$(CONFIG_FTRACE),)
CSRC := $(filter-out ./csrc/trace.c, $(CSRC))
endif

# 添加difftest.c到源文件列表
ifneq ($(CONFIG_DIFFTEST),)
CSRC += ./csrc/difftest.c
endif

# 默认目标
all: build

# 检查NEMU是否存在
check-nemu:
	@if [ ! -f "../nemu/build/riscv32-nemu-interpreter-so" ]; then \
		echo "WARNING: NEMU reference simulator not found at ../nemu/build/riscv32-nemu-interpreter-so"; \
		echo "DiffTest will be disabled at runtime"; \
		echo ""; \
		echo "To build NEMU:"; \
		echo "  cd ../nemu && make isa=riscv32 nemu-interpreter-so"; \
	else \
		echo "NEMU reference found: ../nemu/build/riscv32-nemu-interpreter-so"; \
	fi

# 构建 NPC 仿真器
build: $(CAPSTONE_LIB) check-nemu
	@echo "Building NPC simulator with trace and DiffTest support..."
	@echo "Config: ITRACE=$(CONFIG_ITRACE) MTRACE=$(CONFIG_MTRACE) FTRACE=$(CONFIG_FTRACE) DIFFTEST=$(CONFIG_DIFFTEST)"
	@verilator -Wno-fatal -Wno-WIDTH \
		--timing \
		$(VSRC) $(CSRC) \
		--top-module top \
		--cc \
		--trace \
		--exe \
		--build \
		-CFLAGS "$(CFLAGS)" \
		-LDFLAGS "$(LDFLAGS)" \
		--Mdir ./obj_dir
	@echo "Build completed: ./obj_dir/Vtop"

# 运行
run: build
	@if [ -z "$(ARGS)" ]; then \
		echo "========================================"; \
		echo "Error: No binary file specified"; \
		echo ""; \
		echo "Usage: make run ARGS=\"<path/to/binary.bin> [options]\""; \
		echo ""; \
		echo "Example:"; \
		echo "  make run ARGS=\"test.bin\""; \
		echo "  make run ARGS=\"test.bin -batch\""; \
		echo "  make run ARGS=\"test.bin -d ../nemu/build/riscv32-nemu-interpreter-so\""; \
		echo "========================================"; \
		exit 1; \
	fi
	
	@echo "========================================"
	@echo "Running NPC with: $(ARGS)"
	@echo "========================================"
	
	# 检查二进制文件
	@BINARY=$$(echo $(ARGS) | cut -d' ' -f1); \
	if [ ! -f "$$BINARY" ]; then \
		echo "Error: File not found: $$BINARY"; \
		exit 1; \
	fi
	
	# 运行仿真器
	@./obj_dir/Vtop $(ARGS) || true
	
	@echo "========================================"
	@echo "NPC simulation finished"
	@echo "========================================"

# 批处理模式
batch: build
	@if [ -z "$(ARGS)" ]; then \
		echo "Usage: make batch ARGS=<binary.bin>"; \
		exit 1; \
	fi
	@./obj_dir/Vtop $(ARGS) -batch

# 查看波形
wave:
	@if [ -f "waveform.vcd" ]; then \
		gtkwave waveform.vcd & \
	else \
		echo "No waveform found. Run 'make run' first."; \
	fi

# 清理
clean:
	rm -rf obj_dir *.vcd *.log test.bin

# 创建简单测试程序
test.bin:
	@echo "Creating test program using Python..."
	@python3 -c "with open('test.bin', 'wb') as f: f.write(b'\x13\x01\x10\x00\x93\x02\xa0\x00\x73\x00\x10\x00')"
	@echo "Created test.bin"
	@echo "Content verification:"
	@hexdump -C test.bin

# 测试 SDB
test: test.bin build
	@echo "========================================"
	@echo "Testing SDB..."
	@echo "Instructions:"
	@echo "  1. addi x1, x0, 1   # x1 = 1"
	@echo "  2. addi x2, x0, 10  # x2 = 10"
	@echo "  3. ebreak           # breakpoint"
	@echo "========================================"
	@./obj_dir/Vtop test.bin

# 测试DiffTest - 使用简单的test.bin
test-diff: test.bin build
	@echo "========================================"
	@echo "Testing DiffTest with simple test program"
	@echo "========================================"
	@if [ -f "../nemu/build/riscv32-nemu-interpreter-so" ]; then \
		./obj_dir/Vtop test.bin -d ../nemu/build/riscv32-nemu-interpreter-so || true; \
	else \
		echo "NEMU reference not found, running without DiffTest"; \
		./obj_dir/Vtop test.bin -nodifftest || true; \
	fi

.PHONY: all build run batch wave clean test test-diff check-nemu

路径加载

NEMU实际在../nemu/build/riscv32-nemu-interpreter-so但DiffTest尝试从 build/riscv32-nemu-interpreter-so 加载(这是NPC目录下的build目录)

DIFFTEST INFO: Reference simulator loaded: ../nemu/build/riscv32-nemu-interpreter-so
[src/memory/paddr.c:50 init_mem] physical memory area [0x80000000, 0x87ffffff]
Loading image to address 0x80000000
Image size: 20 bytes
DIFFTEST INFO: DiffTest initialized and enabled (img_size=12)
[NPC] Starting reset sequence...
[DIFFTEST] Initializing with reference: build/riscv32-nemu-interpreter-so
DIFFTEST WARNING: Failed to load reference simulator: build/riscv32-nemu-interpreter-so: cannot open shared object file: No such file or directory
DIFFTEST INFO: Continuing without DiffTest
[DIFFTEST] Initialization complete

主要修改内容:

在Makefile中添加了NEMU库的复制规则:确保NEMU库被复制到NPC的build目录

更新了路径变量:使用正确的相对路径

简化了DiffTest逻辑:移除了Verilog中的初始化,依赖C代码的初始化

添加了等待计数器:确保C代码初始化完成后才开始检查

问题分析

时机问题:DiffTest初始化是在复位前完成的,但Verilog侧的检查可能没有及时启动

指令执行太快:程序只有3条指令,可能在DiffTest准备好之前就执行完了

检查条件不满足:可能检查条件有问题

从输出可以看到:

在Time=6执行第一条指令

在Time=8执行第二条指令

在Time=10执行第三条指令(ebreak)

而DiffTest在Time=8才显示"Verilog side ready for DiffTest",此时已经错过了两条指令。

1. 修复 difftest.c - 改进NPC状态读取

2. 修复 difftest.c - 改进内存同步

3. 修复 top.v - 调整DiffTest检查时机

4. 修复 difftest.c - 改进错误处理

继续修改,关键改进点

时序同步:在Verilog中使用状态机控制DiffTest的激活时机

延迟检查:将指令检查延迟一个周期,确保指令已执行完成

详细调试:在每个关键步骤添加调试输出

更长的测试程序:创建包含多条指令的测试程序,确保有足够的时间进行DiffTest比较

排除无效指令:过滤掉ebreak和空指令

当前项目状态分析

✅ 基本功能正常:

CPU能够正确执行RISC-V指令

寄存器写入正常(x2=1, x5=10)

ebreak处理正常

Verilator仿真流程完整

⚠️ DiffTest问题:

Total instructions compared: 0 表示DiffTest没有进行任何指令比对

这可能是因为DiffTest初始化或同步时机有问题

DiffTest初始化成功 - 参考模拟器已加载

内存同步完成 - 12字节复制到参考模拟器

NPC执行了3条指令 - addi x2, x0, 1; addi x5, x0, 10; ebreak

但没有进行任何比对 - 指令比对计数为0

执行时机:确保NPC和参考模拟器从相同的状态开始执行相同的指令

状态同步:在NPC执行指令前,将状态同步到参考模拟器

内存同步:确保参考模拟器的内存与NPC一致

调试输出:添加详细的调试信息以便定位问题

修改总结:

  1. top.v 主要修改:

    移除了延迟寄存器逻辑(diff_delayed_pc, diff_delayed_inst, diff_delayed_valid)

    简化了DiffTest检查逻辑,直接在指令执行后的下一个周期检查

    添加了更多调试信息输出

    修复了复位计数器逻辑

  2. difftest.c 主要修改:

    实现了正确的DiffTest执行流程:

    读取NPC执行前的状态

    同步状态到参考模拟器

    参考模拟器执行一条指令

    获取参考模拟器执行后的状态

    NPC已经执行指令,读取执行后的状态

    比较两者状态

    添加了详细的调试信息

    修复了内存复制逻辑

  3. dpi_interface.c 主要修改:

    添加了调试信息(可选启用)

    完善了内存访问的错误处理

  4. main.c 主要修改:

    添加了DiffTest初始化的调试信息

    确保在复位后等待足够时间让DiffTest初始化完成

从日志看,主要问题是Difftest初始化时机太早,在寄存器指针(cpu_gpr)还未设置时就开始读取NPC状态,导致读取失败。需要调整初始化顺序,并确保寄存器指针已正确设置。

主要问题解决:

Difftest初始化时机问题:将Difftest初始化移到复位完成之后,确保寄存器指针已设置

寄存器读取失败:在read_npc_state中添加调试信息,并在读取到无效值时进行修复

复位逻辑简化:移除复杂的复位计数器,在第一个非复位周期标记Difftest就绪

执行流程优化:添加更多调试信息,便于定位问题

从日志分析,主要问题是时序错位:

Difftest初始化时机太晚:NPC已经执行了第一条指令(addi x2, x0, 1)才初始化Difftest

状态读取时机错误:在Difftest检查时读取的NPC状态是执行前的状态,而不是执行后的状态

PC不匹配:NPC的PC是0x80000004(执行前),而参考模拟器的PC是0x80000008(执行后)

修改方案:

  1. 修改 main.c - 调整初始化顺序

  2. 修改 difftest.c - 修复时序和状态读取

  3. 修改 top.v - 调整Difftest检查时机

主要修改:

  1. main.c:将Difftest初始化移到复位之前,确保在第一条指令执行前就已初始化

  2. difftest.c:

  • 简化Difftest步骤:让参考模拟器先执行,然后读取NPC状态比较

  • 设置正确的初始状态(PC=0x80000000)

  • 改进调试信息输出

  1. top.v:将Difftest检查移到时钟下降沿,确保NPC状态已稳定
从日志可以看到,Difftest已经能正确工作了! 比较了2条指令,并且都通过了。现在的问题是:

检查时机有点晚:第一条指令在PC=0x80000004时才检查PC=0x80000000的指令

PC地址显示有点混乱:检查时显示的PC是下一条指令的地址

这是正常的,因为:

NPC在执行指令的同一周期就更新了PC

我们是在时钟下降沿检查,此时PC已经更新到下一条指令

为了让日志更清晰,我们可以进一步优化。

主要优化:

top.v:

添加了last_check_pc和last_check_inst寄存器,记录要检查的指令

修改日志输出,清晰显示执行PC和指令,以及下一条PC

避免了检查无效指令(指令为0的情况)

difftest.c:

简化了调试输出,只显示关键信息

改进了匹配成功的输出信息,显示PC跳转情况
从日志可以看出,NPC的auipc指令执行有问题。NPC计算的结果是0x80000004,而参考模拟器的正确结果是0x80009004。这说明NPC的auipc指令没有正确处理立即数的左移12位操作。

根据之前分析的代码结构,问题很可能出在立即数选择上。auipc指令应该使用imm_u(已经左移12位的立即数),而不是imm_i。

// 原代码:
assign alu_b = imm_i;

// 新代码:
assign alu_b = (is_auipc | is_lui) ? imm_u : imm_i;

完整代码

scrc/difftest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include "../include/difftest.h"
#include "../include/npc.h"

// 全局 DiffTest 状态
static bool diff_enabled = false;
static bool skip_ref = false;
static int skip_dut_nr_inst = 0;
static void *ref_handle = NULL;

// NEMU 函数指针
static void (*ref_difftest_memcpy)(paddr_t addr, void *buf, size_t n, bool direction) = NULL;
static void (*ref_difftest_regcpy)(void *dut, bool direction) = NULL;
static void (*ref_difftest_exec)(uint64_t n) = NULL;
static void (*ref_difftest_raise_intr)(word_t NO) = NULL;
static void (*ref_difftest_init)(int port) = NULL;

// NPC 状态
static CPU_state npc_cpu;
static uint64_t inst_count = 0;
static uint32_t last_pc = 0;

// 外部函数声明
extern "C" {
    // 寄存器访问
    uint32_t debug_read_gpr(int index);
    uint32_t debug_read_pc();
    
    // 内存访问
    int pmem_read(int raddr);
    
    // 调试控制
    void ebreak_handler(int code);
}

// ==================== 辅助函数 ====================

// 从 NPC 读取当前 CPU 状态
static void read_npc_state() {
    // 重置NPC状态结构
    memset(&npc_cpu, 0, sizeof(npc_cpu));
    
    // 读取通用寄存器
    for (int i = 0; i < 32; i++) {
        uint32_t val = debug_read_gpr(i);
        npc_cpu.gpr[i] = val;
    }
    
    // 读取 PC - 确保读取的是正确的PC
    uint32_t current_pc = debug_read_pc();
    npc_cpu.pc = current_pc;
    last_pc = current_pc;
}

// ==================== NEMU 风格的 DiffTest 接口 ====================

// 初始化 DiffTest(NEMU 风格)
void npc_init_difftest(const char* ref_so_file, long img_size) {
    if (ref_so_file == NULL) {
        printf("[DIFFTEST INFO] No reference simulator specified, DiffTest disabled\n");
        return;
    }
    
    // 尝试加载参考模拟器动态库
    printf("[DIFFTEST] Loading reference simulator: %s\n", ref_so_file);
    ref_handle = dlopen(ref_so_file, RTLD_LAZY);
    if (ref_handle == NULL) {
        printf("[DIFFTEST WARNING] Failed to load reference simulator: %s\n", dlerror());
        printf("[DIFFTEST INFO] Continuing without DiffTest\n");
        return;
    }
    
    // 获取函数指针
    ref_difftest_memcpy = (void (*)(paddr_t, void*, size_t, bool))dlsym(ref_handle, "difftest_memcpy");
    ref_difftest_regcpy = (void (*)(void*, bool))dlsym(ref_handle, "difftest_regcpy");
    ref_difftest_exec = (void (*)(uint64_t))dlsym(ref_handle, "difftest_exec");
    ref_difftest_raise_intr = (void (*)(word_t))dlsym(ref_handle, "difftest_raise_intr");
    ref_difftest_init = (void (*)(int))dlsym(ref_handle, "difftest_init");
    
    // 检查必需函数
    if (ref_difftest_memcpy == NULL || ref_difftest_regcpy == NULL || 
        ref_difftest_exec == NULL || ref_difftest_init == NULL) {
        printf("[DIFFTEST ERROR] Reference simulator missing required functions\n");
        dlclose(ref_handle);
        ref_handle = NULL;
        return;
    }
    
    printf("[DIFFTEST INFO] Reference simulator loaded: %s\n", ref_so_file);
    
    // 初始化参考模拟器
    printf("[DIFFTEST] Initializing reference simulator...\n");
    ref_difftest_init(1234);  // 使用默认端口
    
    // 同步内存(从 NPC 复制到 REF)
    printf("[DIFFTEST] Copying %ld bytes of memory to reference...\n", img_size);
    
    // 为内存复制分配缓冲区
    uint8_t *buf = (uint8_t*)malloc(img_size);
    if (buf == NULL) {
        printf("[DIFFTEST ERROR] Failed to allocate memory buffer\n");
        return;
    }
    
    // 从 NPC 内存中读取数据
    printf("[DIFFTEST] Reading NPC memory...\n");
    for (long i = 0; i < img_size; i += 4) {
        uint32_t addr = 0x80000000 + i;
        int ret = pmem_read(addr);
        uint32_t data = (uint32_t)ret;
        
        buf[i + 0] = (data >> 0) & 0xFF;
        buf[i + 1] = (data >> 8) & 0xFF;
        buf[i + 2] = (data >> 16) & 0xFF;
        buf[i + 3] = (data >> 24) & 0xFF;
    }
    
    // 复制到参考模拟器
    printf("[DIFFTEST] Copying memory to reference simulator...\n");
    ref_difftest_memcpy(0x80000000, buf, img_size, DIFFTEST_TO_REF);
    free(buf);
    
    // 设置NPC初始状态(复位状态)
    memset(&npc_cpu, 0, sizeof(npc_cpu));
    npc_cpu.pc = 0x80000000;  // 复位后的PC
    
    // 复制NPC初始状态到参考模拟器
    printf("[DIFFTEST] Copying initial CPU state to reference...\n");
    ref_difftest_regcpy(&npc_cpu, DIFFTEST_TO_REF);
    
    // 启用 DiffTest
    diff_enabled = true;
    skip_ref = false;
    skip_dut_nr_inst = 0;
    inst_count = 0;
    
    printf("[DIFFTEST INFO] DiffTest initialized and enabled (img_size=%ld)\n", img_size);
}

// 主要的 DiffTest 步骤函数
void npc_difftest_step(uint32_t pc, uint32_t inst) {
    if (!diff_enabled || ref_handle == NULL) {
        return;
    }
    
    printf("[DIFFTEST] Executing reference for PC=0x%08x, INST=0x%08x\n", pc, inst);
    
    // 第一步:让参考模拟器执行一条指令
    ref_difftest_exec(1);
    
    // 第二步:获取参考模拟器执行后的状态
    CPU_state ref_state_after;
    ref_difftest_regcpy(&ref_state_after, DIFFTEST_TO_DUT);
    
    // 第三步:读取NPC执行后的状态
    read_npc_state();
    
    // 第四步:比较状态
    bool match = true;
    
    // 检查 PC
    if (npc_cpu.pc != ref_state_after.pc) {
        printf("[DIFFTEST ERROR] PC mismatch\n");
        printf("  NPC PC after execution: 0x%08x\n", npc_cpu.pc);
        printf("  REF PC after execution: 0x%08x\n", ref_state_after.pc);
        match = false;
    }
    
    // 检查通用寄存器
    for (int i = 1; i < 32; i++) {  // 跳过x0
        if (npc_cpu.gpr[i] != ref_state_after.gpr[i]) {
            printf("[DIFFTEST ERROR] GPR x%02d mismatch\n", i);
            printf("  NPC: 0x%08x (%d)\n", npc_cpu.gpr[i], npc_cpu.gpr[i]);
            printf("  REF: 0x%08x (%d)\n", ref_state_after.gpr[i], ref_state_after.gpr[i]);
            match = false;
            break;
        }
    }
    
    if (match) {
        inst_count++;
        printf("[DIFFTEST] Instruction %lu compared OK (PC=0x%08x -> 0x%08x)\n", 
               inst_count, pc, npc_cpu.pc);
    } else {
        printf("[DIFFTEST ERROR] at instruction %lu\n", inst_count + 1);
        ebreak_handler(1);
    }
}

// 启用/禁用 DiffTest
void npc_difftest_enable() {
    if (ref_handle != NULL) {
        diff_enabled = true;
        printf("[DIFFTEST INFO] Enabled\n");
    }
}

void npc_difftest_disable() {
    diff_enabled = false;
    printf("[DIFFTEST INFO] Disabled\n");
}

// 清理资源
void npc_difftest_close() {
    if (ref_handle != NULL) {
        dlclose(ref_handle);
        ref_handle = NULL;
        printf("[DIFFTEST INFO] Reference simulator unloaded\n");
    }
    
    printf("[DIFFTEST INFO] Total instructions compared: %lu\n", inst_count);
}

// ==================== DPI-C 接口 ====================

// DPI-C 初始化包装函数
void difftest_init_dpi(const char* ref_so_file, long img_size) {
    npc_init_difftest(ref_so_file, img_size);
}

// DPI-C 检查包装函数
void difftest_check_dpi(uint32_t pc, uint32_t inst) {
    npc_difftest_step(pc, inst);
}

// DPI-C 启用/禁用包装函数
void difftest_enable_dpi() {
    npc_difftest_enable();
}

void difftest_disable_dpi() {
    npc_difftest_disable();
}
csrc/main.c
/***************************************************************************************
* NPC 主程序 - 集成完整 SDB 和 DiffTest
***************************************************************************************/

#include "verilated.h"
#include "verilated_vcd_c.h"
#include "../obj_dir/Vtop.h"
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <getopt.h>
#include "../include/sdb.h"
#include "../include/npc.h"
#include "../include/trace.h"
#include "../include/difftest.h"

// 外部 C 函数
extern "C" {
    // 寄存器访问
    uint32_t debug_read_gpr(int index);
    uint32_t debug_read_pc();
    void set_gpr_ptr(uint32_t* ptr);
    void set_top_ptr(void* ptr);
    
    // 内存访问
    int pmem_read(int raddr);
    void pmem_write(int waddr, int wdata, char wmask);
    void load_binary(const char* filename, uint32_t base_addr);
    
    // 调试控制
    void debug_set_single_step(int enable);
    int debug_should_pause();
    void ebreak_handler(int code);
    
    // SDB 函数
    void cpu_exec(uint64_t n);
    void isa_reg_display();
    
    // DiffTest 函数
    void npc_init_difftest(const char* ref_so_file, long img_size);
    void npc_difftest_enable();
    void npc_difftest_disable();
    void npc_difftest_close();
}

// 全局变量定义
VerilatedContext* contextp = NULL;
VerilatedVcdC* tfp = NULL;
static Vtop* top;

SimState sim_state = SIM_RUNNING;
static uint64_t step_count = 0;

// 在这里定义 npc_exit_flag,使其全局可见
int npc_exit_flag = 0;

// 声明外部变量和函数
extern "C" {
    extern bool single_step_mode;
}

// 步进和波形记录
void step_and_dump_wave() {
    top->clk = !top->clk;
    top->eval();
    contextp->timeInc(1);
    if (tfp) {
        tfp->dump(contextp->time());
    }
}

// cpu_exec 实现(SDB 调用)
extern "C" void cpu_exec(uint64_t n) {
    if (n == (uint64_t)-1) {
        // 连续执行:设置 step_count = 0 表示无限执行
        step_count = 0;
        sim_state = SIM_RUNNING;
        printf("Starting continuous execution...\n");
    } else {
        // 单步执行 N 条指令
        step_count = n*2;
        sim_state = SIM_RUNNING;
        printf("Stepping %lu instructions...\n", n);
    }
}

// 获取文件大小
long get_file_size(const char* filename) {
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) {
        return 0;
    }
    return file.tellg();
}

// 打印欢迎信息
void print_welcome() {
    printf("\n");
    printf("    _   __  ____   ___ \n");
    printf("   / | / / / __ \\ / _ \\\n");
    printf("  /  |/ / / /_/ //  __/\n");
    printf(" / /|  / / ____/ \\___/ \n");
    printf("/_/ |_/ /_/            \n");
    printf("\n");
    printf("Welcome to riscv32-NPC!\n");
    printf("For help, type \"help\"\n");
    printf("\n");
}

// 参数解析
struct SimConfig {
    const char* binary_file = nullptr;
    const char* diff_so_file = nullptr;
    const char* log_file = nullptr;
    uint32_t entry_addr = 0x80000000;
    bool dump_wave = true;
    bool batch_mode = false;
    bool difftest_enabled = true;
    int max_cycles = 1000000;
} config;

// NEMU风格的参数解析
static int parse_args(int argc, char *argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " [OPTION...] IMAGE [args]\n\n";
        std::cerr << "Options:\n";
        std::cerr << "  -b,--batch              run with batch mode\n";
        std::cerr << "  -l,--log=FILE           output log to FILE\n";
        std::cerr << "  -d,--diff=REF_SO        run DiffTest with reference REF_SO\n";
        std::cerr << "  -nowave                 disable waveform dumping\n";
        std::cerr << "  -maxcycles=N            maximum simulation cycles\n";
        std::cerr << "  -nodifftest             disable DiffTest\n";
        std::cerr << "  -entry <addr>           entry address (default: 0x80000000)\n";
        std::cerr << "  -help                   show this help message\n";
        return -1;
    }
    
    // NEMU 风格的选项表
    const struct option table[] = {
        {"batch"    , no_argument      , NULL, 'b'},
        {"log"      , required_argument, NULL, 'l'},
        {"diff"     , required_argument, NULL, 'd'},
        {"help"     , no_argument      , NULL, 'h'},
        {0          , 0                , NULL,  0 },
    };
    
    int o;
    while ((o = getopt_long(argc, argv, "-bhl:d:", table, NULL)) != -1) {
        switch (o) {
            case 'b':
                config.batch_mode = true;
                sdb_set_batch_mode();
                break;
            case 'l':
                config.log_file = optarg;
                break;
            case 'd':
                config.diff_so_file = optarg;
                break;
            case 1:  // 非选项参数(通常是镜像文件名)
                config.binary_file = optarg;
                break;
            default:
                return -1;
        }
    }
    
    // 检查其他参数
    for (int i = optind; i < argc; i++) {
        if (strcmp(argv[i], "-nowave") == 0) {
            config.dump_wave = false;
        } else if (strcmp(argv[i], "-maxcycles") == 0 && i + 1 < argc) {
            config.max_cycles = atoi(argv[++i]);
        } else if (strcmp(argv[i], "-nodifftest") == 0) {
            config.difftest_enabled = false;
        } else if (strcmp(argv[i], "-entry") == 0 && i + 1 < argc) {
            config.entry_addr = strtoul(argv[++i], NULL, 0);
        }
    }
    
    // 如果没有指定DiffTest参考模拟器,使用默认值
    if (config.difftest_enabled && !config.diff_so_file) {
        config.diff_so_file = "build/riscv32-nemu-interpreter-so";
    }
    
    return 0;
}

int main(int argc, char** argv) {
    print_welcome();
    
    // 解析参数(NEMU风格)
    if (parse_args(argc, argv) < 0) {
        return 1;
    }
    
    // 检查文件
    if (!config.binary_file) {
        std::cerr << "Error: No binary file specified\n";
        return 1;
    }
    
    std::ifstream file(config.binary_file);
    if (!file) {
        std::cerr << "Error: Cannot open binary file: " << config.binary_file << std::endl;
        return 1;
    }
    file.close();
    
    // 获取二进制文件大小
    long img_size = get_file_size(config.binary_file);
    if (img_size <= 0) {
        std::cerr << "Error: Invalid binary file size\n";
        return 1;
    }
    
    // Trace配置
    const char* itrace_file = config.log_file ? "itrace.log" : NULL;
    const char* mtrace_file = config.log_file ? "mtrace.log" : NULL;
    const char* ftrace_file = config.log_file ? "ftrace.log" : NULL;
    
    // 初始化 Verilator
    contextp = new VerilatedContext;
    top = new Vtop{contextp};
    
    set_top_ptr(top);
    
    // 设置波形
    if (config.dump_wave) {
        contextp->traceEverOn(true);
        tfp = new VerilatedVcdC;
        top->trace(tfp, 99);
        tfp->open("waveform.vcd");
    }
    
    // 初始化 SDB
    init_sdb(tfp);
    
    // 初始化 Trace 系统
    init_trace(itrace_file, mtrace_file, ftrace_file);
    
    // 加载二进制
    printf("[NPC] Loading binary: %s (size: %ld bytes) to 0x%08x\n", 
           config.binary_file, img_size, config.entry_addr);
    load_binary(config.binary_file, config.entry_addr);
    
    // 先初始化 DiffTest(在复位前)
    if (config.difftest_enabled && config.diff_so_file) {
        printf("[DIFFTEST] Initializing with reference: %s\n", config.diff_so_file);
        printf("[DIFFTEST] Image size: %ld bytes\n", img_size);
        npc_init_difftest(config.diff_so_file, img_size);
    } else if (config.difftest_enabled) {
        printf("[DIFFTEST] No reference simulator specified\n");
        printf("[DIFFTEST] Continuing without DiffTest\n");
        config.difftest_enabled = false;
    } else {
        printf("[DIFFTEST] Disabled\n");
    }
    
    // 复位序列
    printf("[NPC] Starting reset sequence...\n");
    top->rst = 1;
    for (int i = 0; i < 3; i++) {
        step_and_dump_wave();
    }
    top->rst = 0;
    
    // 额外运行几个周期以确保Verilog内部状态稳定
    for (int i = 0; i < 2; i++) {
        step_and_dump_wave();
    }
    
    printf("[NPC] Reset completed, Verilog state stabilized\n");
    
    std::cout << "========================================" << std::endl;
    std::cout << "NPC Simulator with SDB and DiffTest Started" << std::endl;
    std::cout << "Binary: " << config.binary_file << std::endl;
    std::cout << "DiffTest: " << (config.difftest_enabled ? "ENABLED" : "DISABLED") << std::endl;
    std::cout << "Type 'help' for SDB commands" << std::endl;
    std::cout << "========================================" << std::endl;
    
    // 对于简单的测试程序,我们直接开始执行(跳过SDB交互)
    if (!config.batch_mode) {
        printf("[NPC] Auto-starting execution for test program...\n");
        config.batch_mode = true;
        sdb_set_batch_mode();
    }
    
    // 设置仿真状态为运行
    sim_state = SIM_RUNNING;
    step_count = 0;
    
    // 主仿真循环
    int cycle_count = 0;

    while (cycle_count < config.max_cycles && !Verilated::gotFinish() && !npc_exit_flag) {
        if (sim_state == SIM_PAUSED) {
            // 进入 SDB
            if (tfp) tfp->flush();
            sdb_mainloop();
            
            // 如果 SDB 设置了退出标志,退出循环
            if (npc_exit_flag) {
                break;
            }
        }
        else if (sim_state == SIM_RUNNING) {
            if (step_count > 0) {
                // 单步执行模式:执行指定数量的周期
                step_and_dump_wave();
                cycle_count++;
                step_count--;
                
                if (step_count == 0) {
                    sim_state = SIM_PAUSED;
                    printf("Step execution completed. Total cycles: %d\n", cycle_count);
                }
            }
            else if (step_count == 0) {
                // 连续执行模式
                step_and_dump_wave();
                cycle_count++;
                
                // 检查是否应该暂停(监视点触发或 ebreak)
                if (check_watchpoints()) {
                    sim_state = SIM_PAUSED;
                    printf("Watchpoint triggered. Pausing at cycle %d\n", cycle_count);
                }
                
                // 定期输出进度
                if (cycle_count % 1000 == 0) {
                    printf("[NPC] Cycle: %d, PC: 0x%08x\n", cycle_count, debug_read_pc());
                }
            }
        }
        else {
            // 其他状态,等待
            usleep(10000);
        }
    }
    
    // 关闭DiffTest
    if (config.difftest_enabled) {
        npc_difftest_close();
    }
    
    // 关闭trace系统
    close_trace();
    
    // 关闭波形文件
    if (tfp) {
        tfp->close();
        delete tfp;
    }
    
    // 清理 Verilator
    delete top;
    delete contextp;
    
    // 输出总结
    printf("========================================\n");
    printf("NPC Simulation Finished\n");
    printf("Total cycles: %d\n", cycle_count);
    
    if (cycle_count >= config.max_cycles) {
        printf("Reached maximum cycle limit\n");
    }
    
    printf("========================================\n");
    
    return 0;
}
csrc/dpi_interface.c
// csrc/dpi_interface.c
#include <iostream>
#include <cstdlib>
#include <cstdint>
#include <fstream>
#include <vector>
#include "verilated.h"
#include "verilated_dpi.h"
#include "../obj_dir/Vtop.h"
#include "../include/trace.h"

#ifdef __cplusplus
extern "C" {
#endif

// ==================== 全局调试变量 ====================
static uint32_t *cpu_gpr = NULL;  // 寄存器文件指针
static uint32_t *cpu_csr = NULL;  // CSR寄存器指针
static Vtop *top_ptr = NULL;      // Verilog顶层模块指针
static bool sdb_enabled = false;  // SDB使能标志
static bool ebreak_triggered = false;
static int ebreak_exit_code = 0;
static uint32_t last_pc = 0;
static uint32_t last_inst = 0;

// 修改这里:去掉 static,使变量全局可见
bool single_step_mode = false;

// ==================== 寄存器访问 ====================
void set_gpr_ptr(const svOpenArrayHandle r) {
    cpu_gpr = (uint32_t *)(((VerilatedDpiOpenVar*)r)->datap());
    printf("[DPI] GPR pointer set: %p\n", cpu_gpr);
}

void set_top_ptr(void* ptr) {
    top_ptr = (Vtop*)ptr;
    printf("[DPI] Top pointer set: %p\n", top_ptr);
}

uint32_t debug_read_gpr(int index) {
    if (cpu_gpr && index >= 0 && index < 32) {
        // 调试:打印寄存器读取
        // printf("[DPI DEBUG] Read GPR x%d: 0x%08x\n", index, cpu_gpr[index]);
        return cpu_gpr[index];
    }
    printf("[DPI DEBUG] ERROR: cpu_gpr is NULL or index out of range\n");
    return 0xDEADBEEF;
}

uint32_t debug_read_pc() {
    if (top_ptr) {
        uint32_t pc_value = top_ptr->pc;
        // 调试:打印PC读取
        // printf("[DPI DEBUG] Read PC: 0x%08x\n", pc_value);
        return pc_value;
    }
    printf("[DPI DEBUG] ERROR: top_ptr is NULL\n");
    return 0;
}

// ==================== 内存访问 ====================
static std::vector<uint8_t> pmem(64 * 1024 * 1024, 0); // 64MB内存
static uint32_t mem_base = 0x80000000;

void load_binary(const char* filename, uint32_t base_addr) {
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) {
        std::cerr << "Error: Cannot open file " << filename << std::endl;
        return;
    }
    
    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);
    
    if (base_addr < mem_base) {
        std::cerr << "Error: Base address below memory base" << std::endl;
        return;
    }
    
    uint32_t offset = base_addr - mem_base;
    if (offset + size > pmem.size()) {
        std::cerr << "Error: Binary too large for memory" << std::endl;
        return;
    }
    
    if (!file.read((char*)(pmem.data() + offset), size)) {
        std::cerr << "Error: Failed to read file" << std::endl;
        return;
    }
    
    printf("[DPI] Loaded %ld bytes to 0x%08x\n", size, base_addr);
    file.close();
}

int pmem_read(int raddr) {
    uint32_t addr = raddr;
    
    if (addr < mem_base) {
        printf("[DPI DEBUG] Warning: Reading from address below memory base: 0x%08x\n", addr);
        return 0;
    }
    
    uint32_t offset = addr - mem_base;
    if (offset + 4 > pmem.size()) {
        printf("[DPI DEBUG] Warning: Address out of bounds: 0x%08x\n", addr);
        return 0;
    }
    
    // 读取32位字(小端序)
    uint32_t value = 0;
    value |= pmem[offset + 0] << 0;
    value |= pmem[offset + 1] << 8;
    value |= pmem[offset + 2] << 16;
    value |= pmem[offset + 3] << 24;
    
    #ifdef CONFIG_MTRACE
    mtrace(1, addr, value, 4);  // type=1: Read, size=4 bytes
    #endif
    
    return value;
}

// 添加指令fetch trace
void trace_ifetch(uint32_t pc, uint32_t inst) {
    last_pc = pc;
    last_inst = inst;
    
    #ifdef CONFIG_ITRACE
    itrace(pc, inst);
    #endif
    
    #ifdef CONFIG_MTRACE
    mtrace(0, pc, inst, 4);  // type=0: Instruction Fetch
    #endif
}

void trace_func_call(uint32_t pc, uint32_t target, uint32_t ret_addr) {
    #ifdef CONFIG_FTRACE
    //printf("[FTRACE DEBUG] Function call: pc=0x%08x -> target=0x%08x, return=0x%08x\n",
    //       pc, target, ret_addr);
    ftrace(pc, target, ret_addr, 0);
    #endif
}

void trace_func_ret(uint32_t pc, uint32_t target) {
    #ifdef CONFIG_FTRACE
    //printf("[FTRACE DEBUG] Function return: pc=0x%08x -> target=0x%08x\n",
    //      pc, target);
    ftrace(pc, target, 0, 1);
    #endif
}

// 添加指令执行trace(供Verilog调用)
void trace_exec(uint32_t pc, uint32_t inst) {
    // 这里可以添加更多执行时的trace信息
}

void pmem_write(int waddr, int wdata, char wmask) {
    uint32_t addr = waddr;
    
    if (addr < mem_base) {
        printf("[DPI DEBUG] Warning: Writing to address below memory base: 0x%08x\n", addr);
        return;
    }
    
    uint32_t offset = addr - mem_base;
    if (offset + 4 > pmem.size()) {
        printf("[DPI DEBUG] Warning: Address out of bounds: 0x%08x\n", addr);
        return;
    }
    
    // 根据写掩码写入字节
    if (wmask & 0x1) pmem[offset + 0] = (wdata >> 0) & 0xFF;
    if (wmask & 0x2) pmem[offset + 1] = (wdata >> 8) & 0xFF;
    if (wmask & 0x4) pmem[offset + 2] = (wdata >> 16) & 0xFF;
    if (wmask & 0x8) pmem[offset + 3] = (wdata >> 24) & 0xFF;
}

// ==================== 调试控制 ====================
void debug_set_single_step(int enable) {
    single_step_mode = (enable != 0);
    printf("[DPI] Single step mode: %s\n", single_step_mode ? "ON" : "OFF");
}

int debug_should_pause() {
    return single_step_mode ? 1 : 0;
}

// ==================== ebreak处理 ====================
void ebreak_handler(int code) {
    printf("\n" "========================================" "\n");
    
    if (code == 0) {
        printf("HIT GOOD TRAP\n");
        printf("Program completed successfully!\n");
    } else {
        printf("HIT BAD TRAP\n");
        printf("Exit code: %d\n", code);
        printf("Program failed!\n");
    }
    
    printf("========================================" "\n");
    
    
    // 如果SDB启用,不立即退出,进入调试器
    if (sdb_enabled) {
        printf("Press Enter to continue debugging...\n");
        // 设置暂停标志,由SDB处理
    } else {
        Verilated::gotFinish(true);
    }
}

// ==================== 波形控制 ====================
void flush_waveform() {
    // 此函数将在SDB中调用以刷新波形
    printf("[DPI] Waveform flushed\n");
}

#ifdef __cplusplus
}
#endif
vsrc/top.v
// vsrc/top.v
// ==================== 顶层模块定义 ====================
module top(
    input clk,
    input rst,
    output reg [31:0] outdata,
    output reg [31:0] pc
);

// 内部信号声明
wire [31:0] next_pc_reg;
wire [31:0] instruction;

// 寄存器文件信号
/* verilator lint_off UNOPTFLAT */
wire [4:0] rs1_addr, rs2_addr, rd_addr;
/* verilator lint_on UNOPTFLAT */
wire [31:0] rs1_data, rs2_data;
wire [31:0] rf_wdata;
wire rf_wen;

// 立即数信号
wire [31:0] imm_i, imm_u, imm_j;

// ALU信号
wire [31:0] alu_a, alu_b, alu_result;
wire [3:0] alu_op;

// 控制信号
wire is_auipc, is_lui, is_jal, is_jalr;
wire is_addi, is_sw, is_ebreak;
wire pc_src;
wire [31:0] jump_target;

// 写回数据选择
wire [1:0] wb_sel;

// 退出码信号
reg [31:0] exit_code;

// IDU输出信号
wire idu_ftrace_call;
wire idu_ftrace_ret;
wire [31:0] idu_ftrace_target;
wire [31:0] idu_ftrace_ret_addr;

// 避免重复记录的寄存器
reg [31:0] last_trace_pc;
reg last_trace_valid;

// ==================== DPI-C函数导入 ====================
import "DPI-C" function int pmem_read(input int raddr);
import "DPI-C" function void ebreak_handler(input int code);
import "DPI-C" function void set_top_ptr(input chandle ptr);
import "DPI-C" function void trace_ifetch(input int pc, input int inst);
import "DPI-C" function void trace_func_call(input int pc, input int target, input int ret_addr);
import "DPI-C" function void trace_func_ret(input int pc, input int target);

// DiffTest DPI-C函数
import "DPI-C" function void difftest_init_dpi(input string ref_so_file, input longint img_size);
import "DPI-C" function void difftest_check_dpi(input int pc, input int inst);
import "DPI-C" function void difftest_enable_dpi();
import "DPI-C" function void difftest_disable_dpi();

// ==================== 参数定义 ====================
parameter DIFFTEST_ENABLED = 1'b1;

// ==================== DiffTest相关寄存器 ====================
reg diff_enabled;
reg [63:0] diff_inst_count;
reg diff_initialized;
reg [31:0] last_check_pc;
reg [31:0] last_check_inst;

// ==================== PC相关信号 ====================
wire [31:0] pc_plus_4 = pc + 4;

// ==================== 初始化DiffTest ====================
initial begin
    if (DIFFTEST_ENABLED) begin
        diff_enabled = 1'b1;  // 修复:改为阻塞赋值
        $display("[DIFFTEST] Verilog side initialization complete");
    end else begin
        diff_enabled = 1'b0;  // 修复:改为阻塞赋值
        $display("[DIFFTEST] Disabled at compile time");
    end
    
    diff_inst_count = 0;      // 修复:改为阻塞赋值
    diff_initialized = 1'b0;  // 修复:改为阻塞赋值
    last_check_pc = 0;        // 修复:改为阻塞赋值
    last_check_inst = 0;      // 修复:改为阻塞赋值
    
    last_trace_pc = 0;        // 修复:改为阻塞赋值
    last_trace_valid = 0;     // 修复:改为阻塞赋值
end

// ==================== PC更新逻辑 ====================
always @(posedge clk or posedge rst) begin
    if (rst) begin
        pc <= 32'h8000_0000;
        exit_code <= 32'b0;
        diff_initialized <= 1'b0;  // 复位时清除初始化标志
        $display("[DIFFTEST] Reset: PC set to 0x80000000");
    end else begin
        pc <= next_pc_reg;
        
        // 如果当前指令是ebreak,保存a0寄存器值作为退出码
        if (is_ebreak) begin
            exit_code <= rs1_data;
            $display("[DIFFTEST] Ebreak detected, exit code = %d", exit_code);
        end
        
        // 复位后第一个非复位周期标记为初始化完成
        if (!diff_initialized) begin
            diff_initialized <= 1'b1;
            $display("[DIFFTEST] Now ready for instruction checking at time %0t", $time);
        end
    end
end

// 下一PC值选择
assign next_pc_reg = pc_src ? jump_target : pc_plus_4;

// ==================== 函数调用跟踪逻辑 ====================
always @(posedge clk) begin
    if (!rst && diff_initialized) begin
        // 避免重复记录相同的PC
        if (last_trace_pc != pc || !last_trace_valid) begin
            // 处理函数调用跟踪
            if (idu_ftrace_call) begin
                $display("[FTRACE DEBUG] Call: pc=0x%08x -> target=0x%08x, ret=0x%08x",
                        pc, idu_ftrace_target, idu_ftrace_ret_addr);
                trace_func_call(pc, idu_ftrace_target, idu_ftrace_ret_addr);
            end
            
            // 处理函数返回跟踪
            if (idu_ftrace_ret) begin
                $display("[FTRACE DEBUG] Return: pc=0x%08x -> target=0x%08x",
                        pc, idu_ftrace_target);
                trace_func_ret(pc, idu_ftrace_target);
            end
            
            last_trace_pc <= pc;
            last_trace_valid <= 1'b1;
        end
    end else if (rst) begin
        last_trace_pc <= 0;
        last_trace_valid <= 0;
    end
end

// ==================== 记录当前PC和指令 ====================
always @(posedge clk) begin
    if (!rst) begin
        last_check_pc <= pc;
        last_check_inst <= instruction;
    end
end

// ==================== DiffTest检查逻辑 ====================
// 在时钟下降沿进行检查,确保NPC状态已稳定
always @(negedge clk) begin
    if (!rst && diff_enabled && diff_initialized) begin
        // 检查当前指令是否有效(排除ebreak和无效指令)
        if (!is_ebreak && last_check_inst !== 32'bx && last_check_inst != 0) begin
            $display("[DIFFTEST] Check #%0d: Execute PC=0x%08x, INST=0x%08x, Next PC=0x%08x", 
                    diff_inst_count + 1, last_check_pc, last_check_inst, pc);
            
            // 调用DiffTest检查
            difftest_check_dpi(last_check_pc, last_check_inst);
            
            // 增加计数器
            diff_inst_count <= diff_inst_count + 1;
        end
    end
end

// ==================== 模块实例化 ====================
ysyx_25110281_ifu ifu(
    .pc(pc),
    .clk(clk),
    .ins(instruction)
);

ysyx_25110281_idu idu(
    .ins(instruction),
    .pc(pc),
    .rs1_data(rs1_data),
    
    // 寄存器地址输出
    .rs1_addr(rs1_addr),
    .rs2_addr(rs2_addr),
    .rd_addr(rd_addr),
    
    // 立即数输出
    .imm_i(imm_i),
    .imm_u(imm_u),
    .imm_j(imm_j),
    
    // 控制信号输出
    .is_auipc(is_auipc),
    .is_lui(is_lui),
    .is_jal(is_jal),
    .is_jalr(is_jalr),
    .is_addi(is_addi),
    .is_sw(is_sw),
    .is_ebreak(is_ebreak),
    
    // ALU控制
    .alu_op(alu_op),
    
    // PC控制
    .pc_src(pc_src),
    .jump_target(jump_target),
    
    // 写回控制
    .wb_sel(wb_sel),
    .rf_wen(rf_wen),
    
    // 函数调用跟踪信号
    .ftrace_call(idu_ftrace_call),
    .ftrace_ret(idu_ftrace_ret),
    .ftrace_target(idu_ftrace_target),
    .ftrace_ret_addr(idu_ftrace_ret_addr)
);

RegisterFile regfile(
    .clk(clk),
    .rst(rst),
    .raddr1(rs1_addr),
    .raddr2(rs2_addr),
    .rdata1(rs1_data),
    .rdata2(rs2_data),
    .waddr(rd_addr),
    .wdata(rf_wdata),
    .wen(rf_wen)
);

ysyx_25110281_alu alu(
    .a(alu_a),
    .b(alu_b),
    .op(alu_op),
    .result(alu_result)
);

// ==================== 数据通路连接 ====================

// ALU输入A选择:auipc时选PC,其他选rs1
assign alu_a = is_auipc ? pc : rs1_data;

// ALU输入B选择:立即数 - 修复:auipc和lui使用imm_u,其他使用imm_i
assign alu_b = (is_auipc | is_lui) ? imm_u : imm_i;

// 写回数据选择
assign rf_wdata = (wb_sel == 2'b00) ? alu_result :
                  (wb_sel == 2'b01) ? pc_plus_4 :
                  (wb_sel == 2'b10) ? imm_u :
                  32'b0;

// ==================== ebreak处理 ====================
always @(posedge clk) begin
    if (is_ebreak && !rst) begin
        $display("Time=%0t: Detected ebreak, exit code = %0d", $time, exit_code);
        ebreak_handler(exit_code);
    end
end

// ==================== 最终化 ====================
final begin
    if (DIFFTEST_ENABLED) begin
        $display("[DIFFTEST] Final statistics:");
        $display("[DIFFTEST] Total instructions compared: %0d", diff_inst_count);
        if (diff_inst_count == 0) begin
            $display("[DIFFTEST] WARNING: No instructions were compared!");
            $display("[DIFFTEST] Possible issues:");
            $display("[DIFFTEST]   - diff_enabled: %b", diff_enabled);
            $display("[DIFFTEST]   - diff_initialized: %b", diff_initialized);
            $display("[DIFFTEST]   - Total simulation time: %0t", $time);
        end else if (diff_inst_count < 3) begin
            $display("[DIFFTEST] NOTE: Only %0d instructions were compared", diff_inst_count);
            $display("[DIFFTEST] This may be normal for short test programs");
        end else begin
            $display("[DIFFTEST] SUCCESS: Compared %0d instructions", diff_inst_count);
        end
    end
end

endmodule
Makefile
# NPC Makefile - 完整修复版本

CFLAGS += -I./include -g -O2 -Wall
LDFLAGS += -lreadline -ldl

# 配置选项
CONFIG_ITRACE ?= y
CONFIG_MTRACE ?= y
CONFIG_FTRACE ?= y
CONFIG_DIFFTEST ?= y  # 添加DiffTest配置

# NEMU参考模拟器路径
NEMU_REF_SO = ../nemu/build/riscv32-nemu-interpreter-so
BUILD_REF_SO = build/riscv32-nemu-interpreter-so

# 添加Capstone相关配置
ifneq ($(CONFIG_ITRACE),)
# Capstone配置
CAPSTONE_DIR = tools/capstone/repo
CAPSTONE_LIB = $(CAPSTONE_DIR)/libcapstone.so
CAPSTONE_INC = $(CAPSTONE_DIR)/include

# 确保Capstone库已构建
$(CAPSTONE_LIB):
	@echo "Building Capstone library..."
	$(MAKE) -C tools/capstone/repo
	@echo "Capstone library built successfully"

CFLAGS += -I$(CAPSTONE_INC) -DCONFIG_ITRACE
# 使用绝对路径链接库
CAPSTONE_ABS_DIR = $(realpath $(CAPSTONE_DIR))
LDFLAGS += -L$(CAPSTONE_ABS_DIR) -Wl,-rpath,$(CAPSTONE_ABS_DIR) -lcapstone
endif

ifneq ($(CONFIG_MTRACE),)
CFLAGS += -DCONFIG_MTRACE
endif

ifneq ($(CONFIG_FTRACE),)
CFLAGS += -DCONFIG_FTRACE
endif

# 添加DiffTest配置
ifneq ($(CONFIG_DIFFTEST),)
CFLAGS += -DCONFIG_DIFFTEST
# 添加dlopen支持
LDFLAGS += -ldl
endif

# 源文件
VSRC = $(wildcard ./vsrc/*.v)
CSRC = $(wildcard ./csrc/*.c)

# 根据配置排除或包含trace源文件
ifeq ($(CONFIG_ITRACE)$(CONFIG_MTRACE)$(CONFIG_FTRACE),)
CSRC := $(filter-out ./csrc/trace.c, $(CSRC))
endif

# 添加difftest.c到源文件列表
ifneq ($(CONFIG_DIFFTEST),)
CSRC += ./csrc/difftest.c
endif

# 默认目标
all: build

# 检查并复制NEMU库
$(BUILD_REF_SO):
	@if [ -f "$(NEMU_REF_SO)" ]; then \
		echo "Copying NEMU reference simulator..."; \
		mkdir -p build; \
		cp $(NEMU_REF_SO) $(BUILD_REF_SO); \
		echo "Copied NEMU reference to $(BUILD_REF_SO)"; \
	else \
		echo "WARNING: NEMU reference simulator not found at $(NEMU_REF_SO)"; \
		echo "DiffTest will be disabled at runtime"; \
		echo ""; \
		echo "To build NEMU:"; \
		echo "  cd ../nemu && make isa=riscv32 nemu-interpreter-so"; \
	fi

# 构建 NPC 仿真器
build: $(CAPSTONE_LIB) $(BUILD_REF_SO)
	@echo "Building NPC simulator with trace and DiffTest support..."
	@echo "Config: ITRACE=$(CONFIG_ITRACE) MTRACE=$(CONFIG_MTRACE) FTRACE=$(CONFIG_FTRACE) DIFFTEST=$(CONFIG_DIFFTEST)"
	@verilator -Wno-fatal -Wno-WIDTH \
		--timing \
		$(VSRC) $(CSRC) \
		--top-module top \
		--cc \
		--trace \
		--exe \
		--build \
		-CFLAGS "$(CFLAGS)" \
		-LDFLAGS "$(LDFLAGS)" \
		--Mdir ./obj_dir
	@echo "Build completed: ./obj_dir/Vtop"

# 运行
run: build
	@if [ -z "$(ARGS)" ]; then \
		echo "========================================"; \
		echo "Error: No binary file specified"; \
		echo ""; \
		echo "Usage: make run ARGS=\"<path/to/binary.bin> [options]\""; \
		echo ""; \
		echo "Example:"; \
		echo "  make run ARGS=\"test.bin\""; \
		echo "  make run ARGS=\"test.bin -batch\""; \
		echo "  make run ARGS=\"test.bin -d build/riscv32-nemu-interpreter-so\""; \
		echo "========================================"; \
		exit 1; \
	fi
	
	@echo "========================================"
	@echo "Running NPC with: $(ARGS)"
	@echo "========================================"
	
	# 检查二进制文件
	@BINARY=$$(echo $(ARGS) | cut -d' ' -f1); \
	if [ ! -f "$$BINARY" ]; then \
		echo "Error: File not found: $$BINARY"; \
		exit 1; \
	fi
	
	# 运行仿真器
	@./obj_dir/Vtop $(ARGS) || true
	
	@echo "========================================"
	@echo "NPC simulation finished"
	@echo "========================================"

# 批处理模式
batch: build
	@if [ -z "$(ARGS)" ]; then \
		echo "Usage: make batch ARGS=<binary.bin>"; \
		exit 1; \
	fi
	@./obj_dir/Vtop $(ARGS) -batch

# 查看波形
wave:
	@if [ -f "waveform.vcd" ]; then \
		gtkwave waveform.vcd & \
	else \
		echo "No waveform found. Run 'make run' first."; \
	fi

# 清理
clean:
	rm -rf obj_dir *.vcd *.log test.bin build/riscv32-nemu-interpreter-so

# 创建简单测试程序
test.bin:
	@echo "Creating test program using Python..."
	@python3 -c "with open('test.bin', 'wb') as f: f.write(b'\x13\x01\x10\x00\x93\x02\xa0\x00\x73\x00\x10\x00')"
	@echo "Created test.bin"
	@echo "Content verification:"
	@hexdump -C test.bin

# 测试 SDB
test: test.bin build
	@echo "========================================"
	@echo "Testing SDB..."
	@echo "Instructions:"
	@echo "  1. addi x1, x0, 1   # x1 = 1"
	@echo "  2. addi x2, x0, 10  # x2 = 10"
	@echo "  3. ebreak           # breakpoint"
	@echo "========================================"
	@./obj_dir/Vtop test.bin

# 测试DiffTest - 使用简单的test.bin
test-diff: test.bin build
	@echo "========================================"
	@echo "Testing DiffTest with simple test program"
	@echo "========================================"
	@if [ -f "$(BUILD_REF_SO)" ]; then \
		./obj_dir/Vtop test.bin -d $(BUILD_REF_SO) || true; \
	else \
		echo "NEMU reference not found, running without DiffTest"; \
		./obj_dir/Vtop test.bin -nodifftest || true; \
	fi

.PHONY: all build run batch wave clean test test-diff

实现RV32E指令集

posted @ 2025-12-25 21:27  mo686  阅读(9)  评论(0)    收藏  举报