Chinaunix

标题: SkyEye硬件模拟平台,第三部分: 硬件仿真实现之三 [打印本页]

作者: 北在北方    时间: 2008-03-25 15:29
标题: SkyEye硬件模拟平台,第三部分: 硬件仿真实现之三
本系列文章的第三部分主要介绍了SkyEye硬件模拟平台的实现细节。主要内容包括SkyEye的总体设计、SkyEye的可扩
展框架、SkyEye的关键数据结构、SkyEye对各种CPU的模拟实现、SkyEye对各种外设的模拟实现、如何安装使用SkyEye以及如何扩展
SkyEye的仿真模块等。对SkyEye的深入了解,有助于对嵌入式硬件系统有更深入的认识,特别是对操作系统、驱动程序如何与嵌入式硬件系统进行交互
有更深刻的了解。
                       
      
SkyEye的MMU/CACHE和Memory模拟实现
                       
      
1.        MMU和Memory系统结构
                       
      
                               
        
图 0-1 ARM系统中MMU和Memory的系统结构
                               
        

                       
      
                       
      
ARM
系统中MMU和Memory的系统结构如图 0 1所示。不过具体的CPU在实现MMU时差别较大,可能对其做简化和扩展,
SkyEye的MMU模拟实现基于此,在提供一个标准的接口基础上,分成与具体CPU类型无关的MMU模拟子模块和与具体CPU类型相关的MMU模拟子模
块两个主要部分。
                       
      
2.         ARM 数据访问的基本流程图
                       
      
ARM CPU进行数据访问的基本流程如图 0 2所示。
                       
      
                               
        
图 0-2 ARM CPU进行数据访问的基本流程
                               
        

                       
      
                       
      
3.        MMU的统一接口
                       
      
数据结构
                       
      
                       
      typedef struct mmu_state_t {
        ARMword        control;                //CP15 control register
        ARMword        translation_table_base;        //CP15 translation table base register
        ARMword        domain_access_control;        //CP15 domain access control  register
        ARMword        fault_status;                //CP15 fault status register
        ARMword        fault_address;        //CP15 fault address register
        ARMword        last_domain;          //last access domain
        ARMword        process_id;          //CP15 process id register
        mmu_ops_t        ops;                               
        union{
                sa_mmu_t        sa_mmu;
                arm7100_mmu_t  arm7100_mmu;
        }u;
} mmu_state_t;
typedef struct mmu_ops_s{
        int (*init)(ARMul_State *state);/*initilization*/       
        void (*exit)(ARMul_State *state);/*free on exit*/
        fault_t (*read_byte)(ARMul_State *state, ARMword va, ARMword *data);
        fault_t (*write_byte)(ARMul_State *state, ARMword va, ARMword data);
        fault_t (*read_halfword)(ARMul_State *state, ARMword va, ARMword *data);
        fault_t (*write_halfword)(ARMul_State *state, ARMword va,ARMword data);
        fault_t (*read_word)(ARMul_State *state, ARMword va,ARMword *data);
        fault_t (*write_word)(ARMul_State *state, ARMword va,ARMword data);
        fault_t (*load_instr)(ARMul_State *state, ARMword va, ARMword *instr);
        void (*mcr)(ARMul_State *state, ARMword instr, ARMword val);
        ARMword (*mrc)(ARMul_State *state, ARMword instr);
}mmu_ops_t;
                       
      
数据结构ARMul_State中类型为mmu_state_t的mmu代表模拟中MMU的状态。mmu_state_t中的ops提供具体mmu的接口函数,同时包括一个联合结构u用于具体mmu实现的数据结构。
                       
      
4.        与具体CPU类型无关的MMU模拟子模块
                       
      
与具体CPU类型无关的MMU模拟子模块是实现MMU模拟的基础,它实现了TLB,TTW, 访问控制,CACHE, Write Buffer,Read Buffer等MMU要素的模拟,并且提供了较统一的接口,可以较灵活地组合实现具体MMU模拟。
                       
      
TLB的实现(mmu/tlb.[ch])
        
TLB是一个表,它的表项包含地址映射信息和访问控制信息,地址转换信息只有在该表中查询不到时,才通过TTW在内存中查找。
      
                       
      
数据结构
                       
      
TLB 映射类型
                       
      
                       
      typedef enum tlb_mapping_t {
        TLB_INVALID = 0,
        TLB_SMALLPAGE = 1,
        TLB_LARGEPAGE = 2,
        TLB_SECTION = 3
} tlb_mapping_t;
                       
      
TLB 表项
                       
      
                       
      typedef struct tlb_entry_t {
        ARMword                virt_addr;          //virtual address
        ARMword                phys_addr;         //physical address
        ARMword                perms;             //access permission
        ARMword                domain;           //access domain
        tlb_mapping_t        mapping;        //tlb mapping type
} tlb_entry_t;
                       
      
TLB 表
                       
      
                       
      typedef struct tlb_s{
        int num;                        /*num of tlb entry*/
        int cycle;                        /*current tlb cycle,used for allocate tlb_entry*/
        tlb_entry_t        *entrys;
}tlb_t;
                       
      
接口函数
                       
      
                       
         int mmu_tlb_init(tlb_t *tlb_t, int num);
   初始化TLB
void mmu_tlb_exit(tlb_t *tlb_t);
   释放TLB
void mmu_tlb_invalidate_all(ARMul_State *state, tlb_t *tlb_t);
  无效所有的tlb_entry
void mmu_tlb_invalidate_entry(ARMul_State *state, tlb_t *tlb_t, ARMword virt_addr);
  无效包含指定virt_addr的tlb_entry.
tlb_entry_t * mmu_tlb_search(ARMul_State *state, tlb_t *tlb_t, ARMword virt_addr);
   根据virt_addr查找tlb_entry
   
                       
      
TTW的实现(mmu/tlb.[ch])
        
ARM系统中通过2级映射,实现了页式地址转换。
      
                       
      
接口函数
                       
      
                       
      fault_t translate(ARMul_State *state, ARMword virt_addr, tlb_t *tlb_t, tlb_entry_t **tlb)
{
        /*首先查找TLB表,如果没有找到,执行TTW并更新TLB表*/
        *tlb = mmu_tlb_search(state, tlb_t, virt_addr);/*查找tlb*/
        if (!*tlb) {
                /* 没有找到,进行TTW*/
                ARMword l1addr, l1desc; /*一级描述符地址、一级描述符*/
                tlb_entry_t entry;
                l1addr = state->mmu.translation_table_base & 0xFFFFC000;
                l1addr = (l1addr | (virt_addr >> 18)) & ~3;        /*计算一级描述符地址*/
                l1desc = mem_read_word(state, l1addr);        /*读取一级描述符*/
                switch (l1desc & 3) {
                case 0:
                case 3:
                        return SECTION_TRANSLATION_FAULT;
                case 1:
                        /*页式变换*/
                        {
                                ARMword l2addr, l2desc; /*二级描述符的地址,二级描述符*/
                                l2addr = l1desc & 0xFFFFFC00;
                                /*计算二级描述符的地址*/
                                l2addr = (l2addr | ((virt_addr & 0x000FF000) >> 10)) & ~3;
                                /*读取二级描述符*/
                                l2desc = mem_read_word(state, l2addr);
                                entry.virt_addr = virt_addr;
                                entry.phys_addr = l2desc;
                                entry.perms = l2desc & 0x00000FFC;
                                entry.domain = (l1desc >> 5) & 0x0000000F;
                                switch (l2desc & 3) {
                                case 0:
                                case 3:
                                        state->mmu.last_domain = entry.domain;
                                        return PAGE_TRANSLATION_FAULT;
                                case 1:
                                        entry.mapping = TLB_LARGEPAGE; /*大页*/
                                        break;
                                case 2:
                                        entry.mapping = TLB_SMALLPAGE; /*小页*/
                                        break;
                                }
                        }
                        break;
                case 2:
                        /*段变换*/
                        entry.virt_addr = virt_addr;
                        entry.phys_addr = l1desc;
                        entry.perms = l1desc & 0x00000C0C;
                        entry.domain = (l1desc >> 5) & 0x0000000F;
                        entry.mapping = TLB_SECTION;
                        break;
                }
                entry.virt_addr &= tlb_masks[entry.mapping];
                entry.phys_addr &= tlb_masks[entry.mapping];
                /* 更新tlb*/
                *tlb = &tlb_t->entrys[tlb_t->cycle];
                tlb_t->cycle = (tlb_t->cycle + 1) % tlb_t->num;
                **tlb = entry;
        }
        state->mmu.last_domain = (*tlb)->domain;
        return NO_FAULT;
}
                       
      
访问控制的实现(mmu/tlb.[ch])
                       
      
接口函数
                       
      
                       
      fault_t check_access(ARMul_State *state, ARMword virt_addr,
tlb_entry_t *tlb, int read);
                       
      
CACHE的实现(mmu/cache.[ch])
                       
      
ARM系统中一般只实现组相联CACHE。组相联CACHE分成多组,一个组有包括多个CACHE line,一个32位地址可以由下图表示。
                       
      TagSetWord10
                       
      
数据结构
                       
      
Cache行
                       
      
                       
      typedef struct cache_line_t{
        ARMword tag;        /*         cache line align address |
                                                bit2: last half dirty
                                                bit1: first half dirty
                                                bit0: cache valid flag
                                */
        ARMword pa;                /*physical address*/
        ARMword *data;         /*array of cached data*/
}cache_line_t;
#define TAG_VALID_FLAG 0x00000001
#define TAG_FIRST_HALF_DIRTY 0x00000002
#define TAG_LAST_HALF_DIRTY        0x00000004
                       
      
Cache 组
                       
      
                       
      typedef struct cache_set_s{
        cache_line_t        *lines;
        int cycle;                        /*used for cache line allocation*/
}cache_set_t;
                       
      
Cache 结构
                       
      
                       
      typedef struct cache_s{
        int        width;                /*bytes in a line*/
        int way;                /*way of set asscociate*/
        int        set;                /*num of set*/
        int w_mode;                /*write back or write through*/
        //int a_mode;                /*alloc mode: random or round-bin*/
        cache_set_t *sets;
}cache_t;
                       
      
接口函数
                       
      
                       
           /*Cache的初始化和释放*/
int mmu_cache_init(cache_t *cache_t, int width, int way, int set, int w_mode);
void mmu_cache_exit(cache_t *cache_t);
/*Cache 查找和分配*/
cache_line_t * mmu_cache_search(ARMul_State *state, cache_t *cache_t, ARMword va);
cache_line_t * mmu_cache_alloc(ARMul_State *state, cache_t *cache_t, ARMword va, ARMword pa);
/*Cache 操作*/
void mmu_cache_write_back(ARMul_State *state, cache_t *cache_t, cache_line_t *cache);
void mmu_cache_clean(ARMul_State *state, cache_t *cache_t, ARMword va);
void mmu_cache_invalidate(ARMul_State *state, cache_t *cache_t, ARMword va);
void mmu_cache_invalidate_all(ARMul_State *state, cache_t *cache_t);
void mmu_cache_soft_flush(ARMul_State *state, cache_t *cache_t, ARMword pa);
                       
      
Write Buffer的实现(mmu/wb.[hc])
                       
      
用一个循环队列模拟Write Buffer。
                       
      
数据结构
                       
      
                       
      typedef struct wb_entry_s{
        ARMword pa;        //phy_addr
        ARMbyte *data;//data
        int nb;        //number byte to write
}wb_entry_t;
typedef struct wb_s{
        int num;         //wb_entry_t的总数
        int nb;                 //wb_entry_t中最大字节数
        int first;        //循环队列头
        int last;        //循环队列尾
        int used;        //循环队列中已用wb_entry_t的个数
        wb_entry_t *entrys;
}wb_t;
                       
      
接口函数
                       
      
                       
          /*初始化和释放*/
int        mmu_wb_init(wb_t *wb_t, int num, int nb);
void        mmu_wb_exit(wb_t *wb);
/*数据入Write Buffer*/
void        mmu_wb_write_bytess(ARMul_State *state, wb_t *wb_t, ARMword pa,
                                ARMbyte *data, int n);
/*将Write Buffer中数据全部写入内存*/
void        mmu_wb_drain_all(ARMul_State *state, wb_t *wb_t);
                       
      
Read Buffer的实现(mmu/rb.[hc])
                       
      
数据结构
                       
      
                       
      typedef struct rb_entry_s{
        ARMword        data[RB_WORD_NUM];//array to store data
        ARMword        va;        //first word va
        int        type;                //rb type
        fault_t        fault;        //fault set by rb alloc
}rb_entry_t;
typedef struct rb_s{
        int num;
        rb_entry_t *entrys;
}rb_t;
                       
      
接口函数
                       
      
                       
          /*初始化和释放*/
int mmu_rb_init(rb_t *rb_t, int num);
void mmu_rb_exit(rb_t *rb_t);
rb_entry_t *mmu_rb_search(rb_t *rb_t, ARMword va);/*查找*/
void mmu_rb_invalidate_entry(rb_t *rb_t, int i);/*无效*/
void mmu_rb_invalidate_all(rb_t *rb_t);
void mmu_rb_load(ARMul_State *state, rb_t *rb_t, int i_rb, int type, ARMword va);/*装入*/
                       
      
5.        与具体CPU类型相关的MMU模拟子模块
                       
      
与具体CPU类型相关的MMU模拟子模块建立在与具体CPU无关的MMU模拟实现子模块的基础上,通过实现具体CPU的mmu_ops_t,完成具体CPU的MMU模拟。以下详细介绍StrongARM的MMU实现。
                       
      
6.        StrongARM MMU的组成结构
                       
      
StrongARM
的MMU分成指令和数据两部分。指令部分包括一个指令TLB和一个指令CACHE,数据部分包括一个数据TLB,两个数据CACHE(main
CACHE和 mini CACHE),一个Write Buffer和一个Read Buffer。如图 0 3所示:
                       
      
                               
        
图 0-3 StrongARM数据访问功能部件图
                               
        

                       
      
                       
      
7.        StrongARM mmu_ops_t的实现
                       
      
                       
      mmu_ops_t sa_mmu_ops = {
        sa_mmu_init,
        sa_mmu_exit,
        sa_mmu_read_byte,
        sa_mmu_write_byte,
        sa_mmu_read_halfword,
        sa_mmu_write_halfword,
        sa_mmu_read_word,
        sa_mmu_write_word,
        sa_mmu_load_instr,
        sa_mmu_mcr,
        sa_mmu_mrc,
};
                       
      
SA MMU的初始化
                       
      
                       
      typedef struct sa_mmu_desc_s{
        int        i_tlb;
        cache_desc_t i_cache;
        int d_tlb;
        cache_desc_t main_d_cache;
        cache_desc_t mini_d_cache;
        int        rb;
        wb_desc_t        wb;
}sa_mmu_desc_t;
static sa_mmu_desc_t sa11xx_mmu_desc ={
        32,
        {32, 32, 16, CACHE_WRITE_BACK},
        32,
        {32, 32, 8, CACHE_WRITE_BACK},
        {32, 2, 8, CACHE_WRITE_BACK},
        4,
        {8, 16}
};
typedef        struct sa_mmu_s{
        /*指令部分*/
        tlb_t        i_tlb;                        /*指令tlb*/
        cache_t        i_cache;        /*指令cache*/
        /*数据部分*/       
        tlb_t        d_tlb;                        /*数据tlb*/
        cache_t        main_d_cache;/*main cache*/
        cache_t        mini_d_cache;/*mini cache*/
        rb_t        rb_t;                        /*Read Buffer*/
        wb_t        wb_t;                        /*Write Buffer*/
}sa_mmu_t;
                       
      
int sa_mmu_init(ARMul_State *state);
        
sa_mmu_desc_t 是SA MMU参数描述的数据结构,sa_mmu_t实现SA MMU 的数据表示。sa_mmu_init根据sa11xx_mmu_desc,完成对state->u.sa_mmu的初始化。
      
                       
      
SA MMU的数据读操作
                       
      
                       
      static fault_t
sa_mmu_read(ARMul_State *state, ARMword va, ARMword *data, ARMword datatype)
{
        fault_t fault;
        rb_entry_t *rb;
        tlb_entry_t *tlb;
        cache_line_t *cache;
        ARMword pa, real_va,temp,offset;
        /*根据CP15 process id register转换地址*/
        va = mmu_pid_va_map(va);
        real_va=va;
        if (MMU_Disabled){
                /*如果MMU关闭,直接从内存读取*/
                if (datatype==ARM_BYTE_TYPE)
                        *data = mem_read_byte(state, va);
                else if (datatype==ARM_HALFWORD_TYPE)
                        *data = mem_read_halfword(state, va);
                else if (datatype==ARM_WORD_TYPE)
                        *data = mem_read_word(state, va);
                else {
                        printf("SKYEYE:1 sa_mmu_read error: unknown data type %d\n",datatype);
                        exit(-1);
                }
       
                return 0;
        }
        /*align 异常检查*/
        if (((va & 3 ) && (datatype==ARM_WORD_TYPE) && MMU_Aligned) || \
            ((va & 1 ) && (datatype==ARM_HALFWORD_TYPE) && MMU_Aligned) ){
                d_msg("align\n");
                return ALIGNMENT_FAULT;
        } // else
        va &= ~(WORD_SIZE - 1);
               
        /*TTW得到tlb*/
        fault = translate(state, va, D_TLB(), &tlb);
        if (fault){
                d_msg("translate\n");
                return fault;
        }
        /*访问控制检查*/
        fault = check_access(state, va, tlb, 1);
        if (fault)
                return fault;
        /*首先在Read Buffer中查找数据*/
        rb = mmu_rb_search(RB(), va);
        if (rb){
                if (rb->fault)
                        return rb->fault;
                *data = rb->data[(va & (rb_masks[rb->type]-1))>>WORD_SHT];
                goto datatrans;
                //return 0;
        };
        /*其次查找main cache*/
        cache = mmu_cache_search(state, MAIN_D_CACHE(), va);
        if (cache){
                *data = cache->data[va_cache_index(va, MAIN_D_CACHE())];
                goto datatrans;
                //return 0;
        }
        /*最后查找mini cache*/
        cache = mmu_cache_search(state, MINI_D_CACHE(), va);
        if (cache){
                *data = cache->data[va_cache_index(va, MINI_D_CACHE())];
                goto datatrans;
                //return 0;
        }
        /*计算物理地址*/
        pa = tlb_va_to_pa(tlb, va);
           /*如果在[0xe0000000, 0xe8000000]区间,执行mmu_cache_soft_flush*/
        if ((pa >= 0xe0000000) && (pa data[va_cache_index(va, cache_t)];
        }else{
                /*直接从内存读取*/
                if (datatype==ARM_BYTE_TYPE)
                        *data = mem_read_byte(state, pa|(real_va&3));
                else if (datatype==ARM_HALFWORD_TYPE)
                        *data = mem_read_halfword(state,
pa|(real_va&2));
                else if (datatype==ARM_WORD_TYPE)
                        *data = mem_read_word(state, pa);
                else {
                        printf("SKYEYE:2 sa_mmu_read error: unknown
data type %d\n", datatype);
                        exit(-1);
                }
                return 0;
        }
    /*根据读类型和Endian模式整理结果*/
datatrans:       
        if (datatype==ARM_HALFWORD_TYPE){
             temp = *data;
offset = (((ARMword) state->bigendSig * 2) ^ (real_va & 2)) > offset) & 0xffff;
        } else if (datatype==ARM_BYTE_TYPE) {
             temp = *data;
offset = (((ARMword) state->bigendSig * 3) ^ (real_va & 3)) > offset & 0xffL);
        }
end:
        return 0;
}
                       
      
SA MMU的mcr操作
                       
      
                       
      static void
sa_mmu_mcr(ARMul_State *state, ARMword instr, ARMword value)
{
        mmu_regnum_t creg = BITS(16, 19) & 15;
        if (!strncmp(skyeye_config.cpu->cpu_arch_name, "armv4", 5) )
        {         
        switch (creg) {
                ………………….
        case MMU_CACHE_OPS:
                sa_mmu_cache_ops(state, instr, value);
                break;
        case MMU_TLB_OPS:
                sa_mmu_tlb_ops(state, instr, value);
                break;
        case MMU_SA_RB_OPS:
                sa_mmu_rb_ops(state, instr, value);
                break;
        case MMU_SA_DEBUG:
                break;
        case MMU_SA_CP15_R15:
                break;
        case MMU_PID:
                state->mmu.process_id = value & 0x8e000000;
                break;
               
         default:
                printf("mmu_mcr wrote UNKNOWN - reg %d\n", creg);
                break;
        }
        }
}
                       
      
sa_mmu_mcr实现SA MMU特定的cache, tlb, read buffer, write buffer等操作。
                       
      
8.        Memory系统的实现
                       
      
SkyEye memory系统的模拟实现的基本思想是:
                       
      
1.        地址划分,每个划分区域都提供对应的读写函数
                       
      
2.        通过skyeye.conf配置文件配置memory地址区域
                       
      
数据结构
                       
      
                       
      typedef struct mem_bank_t {
        ARMword        (*read_byte)(ARMul_State *state, ARMword addr);
        void        (*write_byte)(ARMul_State *state, ARMword addr, ARMword data);
        ARMword        (*read_halfword)(ARMul_State *state, ARMword addr);
        void        (*write_halfword)(ARMul_State *state, ARMword addr, ARMword data);
        ARMword        (*read_word)(ARMul_State *state, ARMword addr);
        void        (*write_word)(ARMul_State *state, ARMword addr, ARMword data);
        unsigned long        addr, len;
        char        filename[MAX_STR];
} mem_bank_t;
typedef struct {
        int bank_num;
        int current_num;        /*current num of bank*/
        mem_bank_t mem_banks[MAX_BANK];
} mem_config_t;
                       
      
配置函数
                       
      
int do_mem_bank_option(skyeye_option_t *this_option, int num_params, const char *params[]);        /*完成memory bank的配置*/
                       
      
读写接口函数
                       
      
                       
      ARMword        mem_read_byte(ARMul_State *state, ARMword addr);
ARMword        mem_read_halfword(ARMul_State *state, ARMword addr);
ARMword        mem_read_word(ARMul_State *state, ARMword addr);
void                mem_write_byte(ARMul_State *state, ARMword addr, ARMword data);
void                mem_write_halfword(ARMul_State *state, ARMword addr, ARMword data);
void                mem_write_word(ARMul_State *state, ARMword addr, ARMword data);
                       
      
实现方式都是先根据地址在mem_config_t中找到对应的mem_bank,再调用mem_bank对应的读写函数。
                       
      
内存区域的读写函数
                       
      
                       
      ARMword        real_read_byte(ARMul_State *state, ARMword addr);
void                real_write_byte(ARMul_State *state, ARMword addr, ARMword data);
ARMword        real_read_halfword(ARMul_State *state, ARMword addr);
void                real_write_halfword(ARMul_State *state, ARMword addr, ARMword data);
ARMword        real_read_word(ARMul_State *state, ARMword addr);
void                real_write_word(ARMul_State *state, ARMword addr, ARMword data);
                       
      
IO区域的读写函数
                       
      
                       
      ARMword        io_read_byte(ARMul_State *state, ARMword addr);
void                io_write_byte(ARMul_State *state, ARMword addr, ARMword data);
ARMword        io_read_halfword(ARMul_State *state, ARMword addr);
void                io_write_halfword(ARMul_State *state, ARMword addr, ARMword data);
ARMword        io_read_word(ARMul_State *state, ARMword addr);
void                io_write_word(ARMul_State *state, ARMword addr, ARMword data);
                       
      
这些函数将IO的读写转换成模拟的CPU的IO读写
               
   
参考资料
                       
      
               
   
关于作者


                               
        
陈渝, 清华大学,通过
         
[email=yuchen@tsinghua.edu.cn?cc=d]yuchen@tsinghua.edu.cn[/email]
可以和他联系。
        
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/54500/showart_507676.html




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2