[轉]SkyEye硬件模擬平臺,第三部分: 硬件仿真實現之三

SkyEye硬件模擬平臺,第三部分: 硬件仿真實現之三

MMU/CACHE仿真

developerWorks
文檔選項
將打印機的版面設置成橫向打印模式

打印本頁

將此頁作爲電子郵件發送

將此頁作爲電子郵件發送

未顯示需要 JavaScript 的文檔選項


級別: 初級

陳渝 ([email protected])清華大學

2004 年 10 月 01 日

本系列文章的第三部分主要介紹了SkyEye硬件模擬平臺的實現細節。主要內容包括SkyEye的總體設計、SkyEye的可擴展框架、SkyEye的關鍵數據結構、SkyEye對各種CPU的模擬實現、SkyEye對各種外設的模擬實現、如何安裝使用SkyEye以及如何擴展SkyEye的仿真模塊等。對SkyEye的深入瞭解,有助於對嵌入式硬件系統有更深入的認識,特別是對操作系統、驅動程序如何與嵌入式硬件系統進行交互有更深刻的瞭解。

SkyEye的MMU/CACHE和Memory模擬實現

1. MMU和Memory系統結構


圖 0-1 ARM系統中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進行數據訪問的基本流程
圖 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);

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數據訪問功能部件圖
圖 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讀寫



參考資料



關於作者

陳渝, 清華大學,通過 [email protected] 可以和他聯繫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章