source:http://hi.baidu.com/encore7787564/blog/item/a899302f38a4d4564fc22688.html
目前SkyEye还在不断的改进和增加新的功能, SkyEye采用了一种可扩展的架构,允许开发者SkyEye的现有代码框架模拟自己的应用处理器,模拟不同的外设,模拟不同的体系结构。
下面我们以ARM平台为例来描述如何基于已有的SkyEye框架来模拟一个新的应用处理器:
在模拟一个新的应用处理器之前,我们首先需要知道在SkyEye中是否支持这款应用处理器的ARM核,假设我们模拟的应用处理器是LPC2210,属于 ARM7TDMI的系列,目前SkyEye已经支持ARM7TDMI的处理器核,这样我们的工作就会轻松了很多,我们只需要考虑lpc2210中的一些外 设和中断的模拟。。
对于ARM平台,应用处理器模拟的相关代码位于arch/arm/mach目录下。我们需要在这个目录下添加两个文 件,skyeye_mach_lpc.h和skyeye_mach_lpc2210.c。其中skyeye_mach_lpc2210.h里面包含一些我 们在模拟应用处理器时需要的一些结构体和宏。在skyeye_mach_lpc2210.c中,包含了对lpc2210处理器的模拟器的实现代码。
我们在skyeye_mach_lpc.c中需要实现的接口数据结构为machine_config_t的结构体,位于
typedef struct machine_config
{
const char *machine_name;
void (*mach_init) (ARMul_State * state, struct machine_config * this_mach);
void (*mach_io_do_cycle) (ARMul_State * state);
void (*mach_io_reset) (ARMul_State * state);
ARMword (*mach_io_read_byte) (ARMul_State * state, ARMword addr);
void (*mach_io_write_byte) (ARMul_State * state, ARMword addr,
ARMword data);
ARMword (*mach_io_read_halfword) (ARMul_State * state,
ARMword addr);
void (*mach_io_write_halfword) (ARMul_State * state, ARMword addr,
ARMword data);
ARMword (*mach_io_read_word) (ARMul_State * state, ARMword addr);
void (*mach_io_write_word) (ARMul_State * state, ARMword addr,
ARMword data);
ARMword (*mach_flash_read_byte) (ARMul_State * state, ARMword addr);
void (*mach_flash_write_byte) (ARMul_State * state, ARMword addr,
ARMword data);
ARMword (*mach_flash_read_halfword) (ARMul_State * state,
ARMword addr);
void (*mach_flash_write_halfword) (ARMul_State * state, ARMword addr,
ARMword data);
ARMword (*mach_flash_read_word) (ARMul_State * state, ARMword addr);
void (*mach_flash_write_word) (ARMul_State * state, ARMword addr,
ARMword data);
/* for I/O device
* */
void (*mach_set_intr) (u32 interrupt); /*set interrupt pending bit */
int (*mach_pending_intr) (u32 interrupt); /*test if interrupt is pending. 1: pending */
void (*mach_update_intr) (void *mach); /*update interrupt pending bit */
int (*mach_mem_read_byte) (void *mach, u32 addr, u32 * data);
int (*mach_mem_write_byte) (void *mach, u32 addr, u32 data);
void *state;
struct device_desc **devices;
int dev_count;
}machine_config_t;
Machine_name成员用来记录模拟的应用处理器的名称
Mach_init成员函数,为整个machine_config_t的初始化函数,在SkyEye启动时被调用,来初始化machine_config_t的其他成员函数指针和成员变量。
Mach_io_do_cycle成员函数,用来监测当前是否有中断产生。它在每条指令执行时都会被调用。
Mach_io_reset成员函数,用来对一些模拟外设的寄存器的值进行初始化。
mach_update_int成员函数,被一些外设模拟模块调用,用来更新当前的中断状态。
Mach_io_read_byte成员函数,是对模拟外设的字节读操作,也就是说对所有外设寄存器的字节长度的读访问,在这个函数中实现。
mach_io_write_byte成员函数,和mach_io_read_byte相对应,是对模拟外设的自己写操作。
mach_io_read_halfword成员函数,是对模拟外设的半字的读操作。
Mach_io_write_halftword成员函数, 是对模拟外设的半字的写操作
Mach_io_read_word成员函数和mach_io_write_word两个成员函数分别是对模拟外设的字长度的读写操作。
其中mach_flash_read_word,mach_flash_write_word等函数和flash模拟有关,是已经过时的接口,将来很可能会被去掉,可不实现。
mach_set_intr成员函数,用来实现对一个中断状态寄存器的赋值,通常在外设模拟模块中,这个成员函数被调用。
mach_pending_intr成员函数,用来实现对某一中断位的测试,是否有中断发生。
mach_update_intr成员函数,用来对中断状态的寄存器的更新。
mach_mem_read_byte和mach_mem_write_byte,用来对内存的读写,主要是让模拟的片外外设调用来读取一些数据。这两个接口在未来的SkyEye版本中很可能被取代。
state成员变量,用来体系结构相关的表示CPU状态的寄存器,这样我们可以通过这样一个成员变量来读取当前处理器的寄存器状态。
devices是一个指向模拟的外设的结构体的链表,也就是说可以通过遍历这样一个链表来获得当前系统中所有模拟的外设的信息。
Dev_count是当前系统中模拟的外设的数目。
总结一下,我们可以把mach_config_t中的成员变量分为两部分:
一部分是和SOC模拟有关的,我们也可以认为这部分是和片内外设有关。另外一部分是和扩展片外外设有关的,这里面片外外设是独立于应用处理器的,如8019网卡,它是一种片外的外设,可以在不同的应用处理器中应用。
下面我们以一个SkyEye中已有的应用处理器模拟模块arch/arm/mach/skyeye_mach_lpc2200.c为例,来讲解各个接口必要的实现:
第一步
首先我们需要在arch/arm/common/arm_arch_interface.c的arm_machines数组中添加一个成员如下:
{"lpc2200", lpc2200_mach_init, NULL, NULL, NULL}
通过这样一行,我们把lpc2200的初始化函数和lpc2200的字符串联系起来,当SkyEye解析skyeye.conf配置文件时,发现我们要摹拟的是lpc2200处理器,lpc2200_mach_init就会被调用。
同时我们为了避免编译错误,还需要加一个外部声明:
extern void lpc2200_mach_init ();
第二步
添加skyeye_mach_lpc2200.c和lpc2200.h文件到arch/arm/mach/目录中,其中在lpc2200文件中,主要定义一些模拟lpc2200处理器时用到的一些宏或者结构体等。
下面我们就在skyeye_mach_lpc2200.c中定义lpc2200_mach_init函数和实现mach_config_t中的必要的成员函数和成员变量。
pc2200_mach_init实现如下:
void lpc2210_mach_init(ARMul_State *state, machine_config_t *this_mach)
{
//chy 2003-08-19, setprocessor
ARMul_SelectProcessor(state, ARM_v4_Prop);
//chy 2004-05-09, set lateabtSig
state->lateabtSig = HIGH;
this_mach->mach_io_do_cycle = lpc2210_io_do_cycle;
this_mach->mach_io_reset = lpc2210_io_reset;
this_mach->mach_io_read_byte = lpc2210_io_read_byte;
this_mach->mach_io_write_byte = lpc2210_io_write_byte;
this_mach->mach_io_read_halfword = lpc2210_io_read_halfword;
this_mach->mach_io_write_halfword = lpc2210_io_write_halfword;
this_mach->mach_io_read_word = lpc2210_io_read_word;
this_mach->mach_io_write_word = lpc2210_io_write_word;
}
这个函数挂载了为this_mach这个变量挂载了主要的函数指针,我们需要在本文件中实现如下的函数:
lpc2210_io_do_cycle;
lpc2210_io_reset
Lpc2210_io_read_byte
Lpc2210_io_write_byte
Lpc2210_io_read_halfword
Lpc2210_io_write_halfword
Lpc2210_io_read_word
Lpc2210_io_write_word
最后的六个读写相关的函数,主要是对模拟外设的寄存器进行相应的读写。
Lpc2210_io_reset主要是为一些外设的寄存器进行初始化的赋值,我们需要根据数据手册来确定。
Lpc_io_do_cycle用来监测当前我们的模拟外设的发生中断条件是否具备,如果具备,则模拟中断的产生。 其实际实现代码如下:
1 void lpc2210_io_do_cycle(ARMul_State *state)
2 {
3 int t;
4 io.timer[0].pc++;
5 io.timer[1].pc++;
6
7 if (!(io.vic.RawIntr & IRQ_TC0)) { //no timer0 int yet
8 if (io.timer[0].pc >= io.timer[0].pr+1) {
9 io.timer[0].tc++;
10 io.timer[0].pc = 0;
11 if(io.timer[0].tc >= io.timer[0].mr0/1000) {
12
13 io.vic.RawIntr |= IRQ_TC0;
14 io.timer[0].tc = 0;
15 }
16 lpc2210_update_int(state);
17 }
18 }
19 if(io.timer[0].pc == 0){
20 if (!(io.vic.RawIntr & IRQ_UART0)) {
21 /* 2007-01-18 modified by Anthony Lee : for new uart device frame */
22 struct timeval tv;
23 unsigned char buf;
24
25 tv.tv_sec = 0;
26 tv.tv_usec = 0;
27
28 if(skyeye_uart_read(-1, &buf, 1, &tv, NULL) > 0)
29 {
30 io.uart[0].rbr = buf;
31 io.uart[0].lsr |= 0x1;
32 io.vic.RawIntr |= IRQ_UART0;
33 lpc2210_update_int(state);
34 }
35 }/* if (rcr > 0 && ...*/
36 }
37 }
其中7到18行,检测时钟中断是否发生,如果时钟中断发生条件满足,则在13行为中断状态寄存器置位,并在16行更新当前的我们模拟的ARM处理器的状 态。其中 19到36行,为检测是否uart有数据达到,如果有数据到达,我们需要产生相应的uart中断。和产生时钟中断类似,我们也是需要给中断状态寄存器置 位,并且调用lpc2210_update_int来更新当前我们模拟的ARM处理器的状态。
具体的一些时钟外设和uart外设的逻辑和寄存器结构,我们需要在实现自己的模拟器时,进行具体的应用。
第三步 我们需要修改Makefile来添加我们新增加的skyeye_mach_lpc2210.c和skyeye_mach_lpc.h文件。
修改顶层Makefile,在SIM_MACH_OBJS宏追加:
SIM_MACH_OBJS = binary/skyeye_mach_at91.o binary/ skyeye_mach_s3c4510b.o binary/skyeye_mach_lpc2200.o
在Makefile的末尾添加:
binary/skyeye_mach_lpc2200.o: $(ARM_MACH_PATH)/skyeye_mach_lpc2200.c $(ARM_MACH_PATH)/lpc2200.h
$(CC) $(ALL_CFLAGS) -c $< -o $@
这样我们的模拟lpc2210应用处理器的全部工作就完成了