SkyEye硬件模拟平台,硬件仿真实现之三

时间:2007-10-02 10:16:47   来源:chinaitzhe.com  作者:ChinaITzhe  编辑:gaopoadmin


  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位地址可以由下图表示。
  
  Tag Set Word 1 0
  
  数据结构
  
  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);
  
  Wrie 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 < 0xe8000000)){
  if (tlb_c_flag(tlb)){
  if (tlb_b_flag(tlb)){
  mmu_cache_soft_flush(state, MAIN_D_CACHE(), pa);
  }else{
  mmu_cache_soft_flush(state, MINI_D_CACHE(), pa);
  }
  }
  return 0;
  }
  /*如果B=1,必须先清空Write Buffer*/
  if (tlb_b_flag(tlb))
  mmu_wb_drain_all(state, WB());
  if (tlb_c_flag(tlb) && MMU_DCacheEnabled){
  /*如果C=1,并且Data Cache开启,将数据读入Cache*/
  cache_t *cache_t;
  if (tlb_b_flag(tlb))
  cache_t = MAIN_D_CACHE();
  else
  cache_t = MINI_D_CACHE();
  cache = mmu_cache_alloc(state, cache_t, va, pa);
  *data = cache->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)) <<
  3; /* bit offset into the word */
  *data= (temp>> offset) & 0xffff;
  } else if (datatype==ARM_BYTE_TYPE) {
  temp = *data;
  offset = (((ARMword) state->bigendSig * 3) ^ (real_va & 3)) <<
  3; /* bit offset into the word */
  *data=(temp >> 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读写
  

关键字:SkyEye硬件模拟平台,硬件仿真实

相关文章

    无相关信息

文章评论

共有 0 位网友发表了评论 此处只显示部分留言 点击查看完整评论页面