ARM RVISS(RealView指令集模拟器)是ARM公司提供的一款功能强大的仿真工具,主要用于嵌入式系统开发中的芯片设计和验证。作为一名长期从事ARM架构开发的工程师,我经常使用RVISS进行外设驱动开发和系统级验证。下面我将分享一些关键开发经验。
在RVISS中开发外设模型时,必须正确处理模型的初始化和销毁过程。minperip.h头文件提供了一组关键宏:
c复制BEGIN_INIT(your_model)
{
// 初始化代码
// 通常包括内存分配、寄存器初始化等
printf("Model initializing...\n");
}
END_INIT(your_model)
BEGIN_EXIT(your_model)
{
// 清理代码
// 释放资源,保存状态等
printf("Model exiting...\n");
}
END_EXIT(your_model)
这些宏实际上会展开为更复杂的代码结构,包括错误处理和状态管理。根据我的经验,有几点需要特别注意:
模型状态使用专门的宏来声明:
c复制BEGIN_STATE_DECL(your_model)
uint32_t control_register;
uint32_t status_register;
void* dma_buffer;
END_STATE_DECL(your_model)
这个结构体会被自动命名为your_modelState。在实际项目中,我发现这种声明方式有几个优势:
提示:对于复杂模型,建议将状态分为多个子结构体,比如按功能模块划分,可以提高代码可维护性。
外设模型通常需要挂接到总线解码器上,这通过registerPeripFunc()实现:
c复制// 典型的外设注册代码示例
static ARMul_Error peripheral_access(void* state, ARMul_State* core, ARMul_Address address,
uint32_t* data, uint32_t size, ARMul_AccessType access)
{
// 处理内存访问
if(access == ARMul_READ) {
*data = ((PeripheralState*)state)->register_value;
return ARMul_NoError;
}
// 写入处理...
}
BEGIN_INIT(MyPeripheral)
{
MyPeripheralState* state = (MyPeripheralState*)GET_STATE();
// 注册内存范围0x1000-0x1FFF
ARMul_BusRegisterPeripFunc(core, 0x1000, 0x1FFF, peripheral_access, state);
}
END_INIT(MyPeripheral)
在实际项目中,内存访问处理函数需要注意:
对于内存模型,有两种主要实现方式:
我曾经在一个项目中需要实现带ECC校验的内存模型,采用了第一种方式:
c复制static ARMul_Error ecc_memory_access(ARMul_State* core, ARMul_Address address,
uint8_t* data, uint32_t size, ARMul_AccessType access)
{
// ECC校验和纠正逻辑
if(access == ARMul_READ) {
if(ecc_check(address, data)) {
core->Aborted = ARMul_DataAbortV;
return ARMul_AbortV;
}
}
// 正常内存访问...
}
BEGIN_INIT(ECCMemory)
{
ARMulif_InsertMemInterface(core, ecc_memory_access);
}
END_INIT(ECCMemory)
这种方式的好处是可以复用Flatmem的基础功能,只需添加额外的校验逻辑。
RVISS模型的构建过程因平台而异,但基本流程一致:
设置构建环境
编写Makefile
makefile复制# Windows示例
CC = cl
CFLAGS = /O2 /I$(ARMROOT)\RVARMulator\ExtensionKit\include
LDFLAGS = /DLL
mymodel.dll: mymodel.obj
$(CC) $(LDFLAGS) /Fe$@ $**
执行构建
部署模型
注意:微软编译器必须使用VS6版本,这是RVISS的一个硬性限制。我曾尝试使用VS2005构建,结果导致难以调试的内存错误。
RVISS使用三类配置文件:
一个典型的.dsc文件内容如下:
code复制;; ARMulator configuration file type 3
{ Peripherals
{MyModel
MODEL_DLL_FILENAME=MyModel
}
{ No_MyModel=Nothing
}
}
在项目中,我总结出几个配置技巧:
SimRdi_Manager是RVISS与RealView Debugger之间的桥梁,提供三种核心服务:
初始化监听器的典型代码:
c复制void Model_SimRdi_Listener(SimRdiRegistrationProcVec *registration, void* handle)
{
if (registration->number < 1) return;
MyModelState* state = (MyModelState*)handle;
state->srpv = registration->simrdiprocvec;
// 后续服务注册代码...
}
BEGIN_INIT(MyModel)
{
ARMulif_InstallSimRdiRegistration(core_desc, Model_SimRdi_Listener, GET_STATE());
}
END_INIT(MyModel)
全局断点适用于处理器异常等非地址相关事件:
c复制// 全局断点列表
static const char* my_global_breaks[] = {
"MyModel:WatchdogTimeout",
"MyModel:DMAError"
};
// 断点处理函数
static SIM_ERR handle_global_breaks(void* handle, bool_int enable, uint32_t break_num)
{
MyModelState* state = (MyModelState*)handle;
switch(break_num) {
case 0: state->watchdog_break_enabled = enable; break;
case 1: state->dma_break_enabled = enable; break;
default: return SIM_REGISTER_ACCESS;
}
return SIM_OK;
}
// 注册全局断点
void register_global_breaks(MyModelState* state)
{
SimRdi_Global_Breaks_Advert* advert = state->srpv->global_breaks->c_new(state->srpv->global_breaks);
advert->x.name = "MyModelGlobalBreaks";
advert->len = 2;
advert->global_breaks = my_global_breaks;
advert->handle_for_function = state;
advert->simrdi_global_breaks = handle_global_breaks;
advert->start_global_number = &state->first_global_break_num;
state->srpv->global_breaks->advertise(state->srpv->global_breaks, advert);
}
在实际触发断点时,需要调用停止函数:
c复制void trigger_global_break(MyModelState* state, int break_index)
{
if(!state->srpv) return;
STATUS_INFO status;
status.mode = STATUS_MODE_HALTED;
status.info.halt.reason = GEN_SIGNAL_BREAK;
status.info.halt.break_number = *state->first_global_break_num + break_index;
state->srpv->simrdi->stop(state->srpv->simrdi, &status);
}
寄存器服务允许调试器访问模型内部状态:
c复制static SIM_ERR register_access(void* handle, bool_int reg_read,
uint32_t reg_num, REGVAL* regval, uint16_t size)
{
MyModelState* state = (MyModelState*)handle;
switch(reg_num) {
case 0: // @Control
if(reg_read) memcpy(regval, &state->control_reg, size);
else memcpy(&state->control_reg, regval, size);
break;
case 1: // @Status
if(reg_read) memcpy(regval, &state->status_reg, size);
else return SIM_REGISTER_ACCESS; // 只读寄存器
break;
default:
return SIM_REGISTER_ACCESS;
}
return SIM_OK;
}
void register_model_registers(MyModelState* state)
{
static Register_Definition regs[] = {
{4, EECHGCASE_NOT, TYPE_UNSIGNED_LONG, "@Control", "Control ", 0, 0},
{4, EECHGCASE_NOT, TYPE_UNSIGNED_LONG, "@Status", "Status ", 1, 0}
};
SimRdi_Uniregs_Advert* advert = state->srpv->uniregs->c_new(state->srpv->uniregs);
advert->x.name = "MyModelRegisters";
advert->description = "MyModel Control Registers";
advert->block_num = uniregs_peripherals;
advert->len = 2;
advert->desc = regs;
advert->handle_for_function = state;
advert->_SimReg = register_access;
advert->start_reg_number = 0;
state->srpv->uniregs->advertise(state->srpv->uniregs, advert);
}
在实际项目中,我建议:
对于复杂模型,自动生成的寄存器窗口可能不够直观,可以自定义:
c复制static const char* regwin_lines[] = {
"Control Group",
"=@Control",
"=@Status",
"Data Group",
"$+",
"Data Registers",
"=@Data0",
"=@Data1",
"=@Data2"
};
void create_custom_regwin(MyModelState* state)
{
REG_WIN* regwin = (REG_WIN*)malloc(sizeof(REG_WIN));
regwin->tab_name = "MyModel,My Peripheral Model";
regwin->lines = regwin_lines;
regwin->line_cnt = 9;
regwin->enum_cnt = 0;
regwin->enum_list = NULL;
SimRdi_RegWin_Advert* advert = state->srpv->regwin->c_new(state->srpv->regwin);
advert->x.name = "MyModelRegWin";
advert->regwin = regwin;
state->saved_regwin_advert_id = advert->x.id;
state->srpv->regwin->advertise(state->srpv->regwin, advert);
}
自定义寄存器窗口时需要注意:
在RVISS模型开发过程中,我遇到过许多典型问题:
模型加载失败
内存访问异常
调试器连接问题
基于项目经验,我总结了几点性能优化建议:
减少内存访问回调
优化事件调度
精简调试输出
缓存常用数据
在多版本环境中,兼容性管理至关重要:
c复制void check_version_compatibility(SimRdiProcVec* srpv)
{
// 检查主版本
if((srpv->version->major & 0xFFFF) != EXPECTED_MAJOR_VERSION) {
fprintf(stderr, "不兼容的SimRdiProcVec主版本\n");
return;
}
// 检查服务版本
if(srpv->uniregs->version < MIN_UNIREGS_VERSION) {
fprintf(stderr, "Uniregs服务版本过低\n");
return;
}
// 更多版本检查...
}
在实际项目中,我建议:
以一个实际的Timer模型为例,核心功能包括:
状态结构设计:
c复制BEGIN_STATE_DECL(MyTimer)
uint32_t control_reg;
uint32_t load_value;
uint32_t current_value;
uint32_t interrupt_status;
uint8_t prescaler;
uint8_t prescaler_counter;
bool running;
bool interrupt_enabled;
END_STATE_DECL(MyTimer)
Timer寄存器映射示例:
c复制static ARMul_Error timer_reg_access(void* state, ARMul_State* core,
ARMul_Address address, uint32_t* data,
uint32_t size, ARMul_AccessType access)
{
MyTimerState* timer = (MyTimerState*)state;
uint32_t offset = address - TIMER_BASE;
switch(offset) {
case 0x00: // Control
if(access == ARMul_READ) *data = timer->control_reg;
else {
timer->control_reg = *data;
update_timer_state(timer);
}
break;
case 0x04: // Load
if(access == ARMul_READ) *data = timer->load_value;
else timer->load_value = *data;
break;
// 更多寄存器处理...
default:
return ARMul_Error_Abort;
}
return ARMul_NoError;
}
核心计时逻辑:
c复制void timer_tick(MyTimerState* timer)
{
if(!timer->running) return;
if(++timer->prescaler_counter >= timer->prescaler) {
timer->prescaler_counter = 0;
if(timer->control_reg & COUNT_DOWN_MASK) {
if(--timer->current_value == 0) {
timer->current_value = timer->load_value;
if(timer->interrupt_enabled) {
timer->interrupt_status |= TIMEOUT_FLAG;
trigger_interrupt(timer);
}
}
} else {
if(++timer->current_value >= timer->load_value) {
timer->current_value = 0;
if(timer->interrupt_enabled) {
timer->interrupt_status |= TIMEOUT_FLAG;
trigger_interrupt(timer);
}
}
}
}
}
中断触发与清除:
c复制void trigger_interrupt(MyTimerState* timer)
{
if(!timer->srpv) return;
// 设置中断状态
STATUS_INFO status;
status.mode = STATUS_MODE_HALTED;
status.info.halt.reason = GEN_SIGNAL_INTERRUPT;
status.info.halt.break_number = TIMER_INTERRUPT_NUM;
timer->srpv->simrdi->stop(timer->srpv->simrdi, &status);
}
void clear_interrupt(MyTimerState* timer)
{
timer->interrupt_status &= ~TIMEOUT_FLAG;
if(timer->srpv) {
timer->srpv->simrdi->go(timer->srpv->simrdi);
}
}
最终初始化函数:
c复制BEGIN_INIT(MyTimer)
{
MyTimerState* timer = (MyTimerState*)GET_STATE();
memset(timer, 0, sizeof(MyTimerState));
// 注册内存访问处理
ARMul_BusRegisterPeripFunc(core, TIMER_BASE, TIMER_BASE + 0xFF,
timer_reg_access, timer);
// 注册SimRdi监听器
ARMulif_InstallSimRdiRegistration(core_desc, timer_simrdi_listener, timer);
// 设置初始状态
timer->load_value = 0xFFFFFFFF;
timer->current_value = 0xFFFFFFFF;
timer->prescaler = 1;
}
END_INIT(MyTimer)
这个Timer模型展示了RVISS开发的典型模式,包括状态管理、寄存器访问、中断处理和调试接口集成。在实际项目中,可以根据需求扩展更多功能,如PWM输出、输入捕获等。