【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

想学好soc,再怎么看芯片手册和linux kernel都不为过。但是要学习好kernel,那再怎么看skyeye都不为过。学习虚拟机,不光可以自己添加soc,也可以自己添加外设硬件(比如网卡、norflash等等),实在不错。

我们自己可以找一段skyeye的soc代码,看看skyeye是怎么仿真外设的,比如1.2.5版本下对ep9312的仿真,

/*
	skyeye_mach_ep9312.c - define machine ep9312 for skyeye
	Copyright (C) 2003 Skyeye Develop Group
	for help please send mail to <[email protected]>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
/*
 * 11/06/2004	clean some codes
 *		wlm <[email protected]>
 * 10/8/2004 	init this file.
 * 		add machine ep9312's function.
 *		Cai Qiang <[email protected]>
 *
 * */

#include "armdefs.h"
#include "clps9312.h"
#include "ep9312.h"
#include "serial_amba.h"
//zzc:2005-1-1
#ifdef __CYGWIN__
//chy 2005-07-28
#include <time.h>
//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
/*struct timeval
{
	int tv_sec;
	int tv_usec;
};*/
//AJ2D--------------------------------------------------------------------------
#endif

/* 2007-01-18 added by Anthony Lee : for new uart device frame */
#include "skyeye_uart.h"

void ep9312_io_write_word (ARMul_State * state, ARMword addr, ARMword data);
ARMword ep9312_io_read_word (ARMul_State * state, ARMword addr);

#define NR_UART			3

#define UART_FR_TXFE	(1<<7)
#define UART_FR_RXFE	(1<<4)

#define UART_IIR_RIS	(1<<1)
#define UART_IIR_TIS	(1<<2)

const int TCOI[2] = { 1 << 4, 1 << 5 };
const int UART_RXINTR[3] = { 1 << 23, 1 << 25, 1 << 27 };
const int UART_TXINTR[3] = { 1 << 24, 1 << 26, 1 << 28 };
const int INT_UART[3] = { 1 << (52 - 32), 1 << (54 - 32), 1 << (55 - 32) };
const int iConsole = 0;		//index of uart of serial console

/*Internal IO Register*/
typedef struct ep9312_io
{
	ARMword syscon_devcfg;	/* System control */

	ARMword intsr[2];	/* Interrupt status reg */
	ARMword intmr[2];	/* Interrupt mask reg */

	struct ep9312_tc_io tc[4];
	struct ep9312_uart_io uart[NR_UART];

} ep9312_io_t;

static ep9312_io_t ep9312_io;
#define io ep9312_io

static void
ep9312_update_int (ARMul_State * state)
{
	ARMword requests = io.intsr[0] & io.intmr[0];
	requests |= io.intsr[1] & io.intmr[1];

	state->NfiqSig = (requests & 0x0001) ? LOW : HIGH;
	state->NirqSig = (requests & 0xfffe) ? LOW : HIGH;
}

static void
ep9312_io_reset (ARMul_State * state)
{
	int i;
	io.syscon_devcfg = 0;
	io.intmr[0] = 0;
	io.intmr[1] = 0;

	/* reset TC register */
	io.tc[0].value = 0;
	io.tc[1].value = 0;
	io.tc[2].value = 0;
	io.tc[0].mod_value = 0xffff;
	io.tc[1].mod_value = 0xffff;
	io.tc[2].mod_value = 0xffffffff;

	for (i = 0; i < NR_UART; i++) {
		io.uart[i].dr = 0;
		io.uart[i].fr = UART_FR_TXFE;
	}
}

void
ep9312_io_do_cycle (ARMul_State * state)
{
	int i;

	/* We must implement TC1, TC2 and TC4 */
	for (i = 0; i < 2; i++) {
		if (io.tc[i].value == 0) {
			if (io.tc[i].ctl & TC_CTL_MODE)
				io.tc[i].value = io.tc[i].load;
			else
				io.tc[i].value = io.tc[i].mod_value;
			io.intsr[0] |= TCOI[i];
			ep9312_update_int (state);
		}
		else {
			io.tc[i].value--;
		}
	}
	io.tc[3].load++;

	if (!(io.intsr[0] & (UART_RXINTR[iConsole]))
	    && io.uart[iConsole].dr == 0) {
		/* 2007-01-18 modified by Anthony Lee : for new uart device frame */
		struct timeval tv;
		unsigned char buf;

		tv.tv_sec = 0;
		tv.tv_usec = 0;

		if(skyeye_uart_read(-1, &buf, 1, &tv, NULL) > 0)
		{
			io.uart[iConsole].dr = (int) buf;
			io.intsr[0] |= UART_RXINTR[iConsole];
			io.intmr[0] |= UART_RXINTR[iConsole];
			io.intsr[1] |= INT_UART[iConsole];
			io.intmr[1] |= INT_UART[iConsole];
			io.uart[iConsole].iir |= UART_IIR_RIS;
			io.uart[iConsole].fr &= ~UART_FR_RXFE;
			ep9312_update_int (state);
		}
	}			//if (!(io.intsr & URXINT))
}

static void
ep9312_uart_read (ARMul_State * state, u32 offset, u32 * data, int index)
{
	switch (offset) {
	case UART_DR:
		*data = io.uart[index].dr;
		io.uart[index].dr = 0;
		io.intsr[0] &= ~(UART_RXINTR[index]);
		io.intsr[1] &= ~(INT_UART[index]);
		io.uart[index].iir &= ~UART_IIR_RIS;
		io.uart[index].fr |= UART_FR_RXFE;
		ep9312_update_int (state);
		break;
	case UART_RSR:
		*data = io.uart[index].rsr;
		break;
		//case UART_ECR:
	case UART_CR_H:
	case UART_CR_M:
	case UART_CR_L:
		break;
	case UART_CR:
		*data = io.uart[index].cr;
		break;
	case UART_FR:
		*data = io.uart[index].fr;
		break;
	case UART_IIR:
		*data = io.uart[index].iir;
		break;
		//case UART_ICR:
	case UART_ILPR:
	case UART_DMACR:
	case UART_TCR:
	case UART_TISR:
	case UART_TOCR:
	case UART_TMR:
	case UART_MCR:
	case UART_MSR:
		break;
	default:
		SKYEYE_DBG ("%s(0x%x, 0x%x)\n", __func__, offset, data);
		break;
	}
}
static void
ep9312_uart_write (ARMul_State * state, u32 offset, u32 data, int index)
{
	switch (offset) {
	case UART_DR:
		{
			char c = data;

			/* 2007-01-18 modified by Anthony Lee : for new uart device frame */
			skyeye_uart_write(-1, &c, 1, NULL);
		}
	case UART_RSR:
		//case UART_ECR:
	case UART_CR_H:
	case UART_CR_M:
	case UART_CR_L:
		break;
	case UART_CR:
		{
			io.uart[index].cr = data;
			if ((data & AMBA_UARTCR_TIE) == 0) {
				io.intmr[0] &= ~(UART_TXINTR[index]);
				io.intsr[0] &= ~(UART_TXINTR[index]);
				io.intsr[1] &= ~(INT_UART[index]);
				io.intmr[1] &= ~(INT_UART[index]);

				io.uart[index].iir &= ~(UART_IIR_TIS);	//Interrupt Identification and Clear
			}
			else {

				io.intmr[0] |= (UART_TXINTR[index]);
				io.intsr[0] |= (UART_TXINTR[index]);
				io.intsr[1] = (INT_UART[index]);
				io.intmr[1] = (INT_UART[index]);
				io.uart[index].iir |= (UART_IIR_TIS);
			}
			ep9312_update_int (state);
		}
		break;
	case UART_FR:
	case UART_IIR:
		io.uart[index].iir = data;
		break;
		//case UART_ICR:
	case UART_ILPR:
	case UART_DMACR:
	case UART_TCR:
	case UART_TISR:
	case UART_TOCR:
	case UART_TMR:
	case UART_MCR:
	case UART_MSR:
		break;
	default:
		SKYEYE_DBG ("%s(0x%x, 0x%x)\n", __func__, offset, data);
	}
}

/* Timer read/write register
 */
static void
ep9312_tc_read (u32 offset, u32 * data, int index)
{
	if (index == 4) {
		if (offset == TC_VALUELOW)
			*data = io.tc[index].load;
		else if (offset == TC_VALUEHIGH)
			*data = io.tc[index].value;
	}
	switch (offset) {
	case TC_LOAD:
		*data = io.tc[index].load;
		break;
	case TC_VALUE:
		*data = io.tc[index].value;
		break;
	case TC_CTL:
		*data = io.tc[index].ctl;
		break;
	case TC_CLEAR:
		SKYEYE_DBG ("%s(0x%x, 0x%x): read WO register\n", __func__,
			    offset, data);
		break;
	default:
		SKYEYE_DBG ("%s(0x%x, 0x%x)\n", __func__, offset, data);
		break;
	}
}
static void
ep9312_tc_write (ARMul_State * state, u32 offset, u32 data, int index)
{
	switch (offset) {
	case TC_LOAD:
		io.tc[index].load = data;
		break;
	case TC_VALUE:
		SKYEYE_DBG ("%s(0x%x, 0x%x): write RO register\n", __func__,
			    offset, data);
		break;
	case TC_CTL:
		io.tc[index].ctl = data;
		break;
	case TC_CLEAR:
		io.intsr[0] &= ~TCOI[index];
		ep9312_update_int (state);
		break;
	default:
		SKYEYE_DBG ("%s(0x%x, 0x%x)\n", __func__, offset, data);
		break;
	}
}

ARMword
ep9312_io_read_byte (ARMul_State * state, ARMword addr)
{
	return ep9312_io_read_word (state, addr);
}

ARMword
ep9312_io_read_halfword (ARMul_State * state, ARMword addr)
{

	SKYEYE_DBG ("SKYEYE: %s error\n", __func__);
}

ARMword
ep9312_io_read_word (ARMul_State * state, ARMword addr)
{
	ARMword data = 0;

	/* TC1 */
	if ((addr >= EP9312_TC_BASE1) &&
	    (addr < (EP9312_TC_BASE1 + EP9312_TC_SIZE))) {
		ep9312_tc_read ((u32) (addr - EP9312_TC_BASE1),
				(u32 *) & data, 0);
	}
	/* TC2 */
	if ((addr >= EP9312_TC_BASE4) &&
	    (addr < (EP9312_TC_BASE4 + EP9312_TC_SIZE))) {
		ep9312_tc_read ((u32) (addr - EP9312_TC_BASE4),
				(u32 *) & data, 3);
	}
	/* UART1 */
	if ((addr >= EP9312_UART_BASE1) &&
	    (addr < (EP9312_UART_BASE1 + EP9312_UART_SIZE))) {
		ep9312_uart_read (state, (u32) (addr - EP9312_UART_BASE1),
				  (u32 *) & data, 0);
		return data;
	}
	/* UART3 */
	if ((addr >= EP9312_UART_BASE3) &&
	    (addr < (EP9312_UART_BASE3 + EP9312_UART_SIZE))) {
		ep9312_uart_read (state, (u32) (addr - EP9312_UART_BASE3),
				  (u32 *) & data, 2);
		return data;
	}
	switch (addr) {
	case SYSCON_PWRCNT:
		break;
	case VIC0INTENABLE:
		data = io.intmr[0];
//              printf("%s(0x%08x) = 0x%08x\n", __func__, addr, data);
		break;
	case VIC0IRQSTATUS:
		data = io.intsr[0];
		io.intsr[0] = 0;	//!!!
		break;
	case VIC1IRQSTATUS:
		data = io.intsr[1];
		io.intsr[1] = 0;
		break;
	case RTCDR:
	case AACGCR:
	case AACRGIS:
//              printf("%s(0x%08x) = 0x%08x\n", __func__, addr, data);
		break;
	case SYSCON_DEVCFG:
		data = io.syscon_devcfg;
		break;
	default:
		SKYEYE_DBG ("SKYEYE:unknown io addr, %s(0x%08x) = 0x%08x\n",
			    __func__, addr, data);
		break;
	}
	return data;
}

void
ep9312_io_write_byte (ARMul_State * state, ARMword addr, ARMword data)
{
	ep9312_io_write_word (state, addr, data);
}

void
ep9312_io_write_halfword (ARMul_State * state, ARMword addr, ARMword data)
{
	SKYEYE_DBG ("SKYEYE: %s error\n", __func__);
}

void
ep9312_io_write_word (ARMul_State * state, ARMword addr, ARMword data)
{
	ARMword tmp;
	if ((addr >= EP9312_TC_BASE1) &&
	    (addr < (EP9312_TC_BASE1 + EP9312_TC_SIZE))) {
		ep9312_tc_write (state, (u32) (addr - EP9312_TC_BASE1), data,
				 0);
	}
	if ((addr >= EP9312_UART_BASE1) &&
	    (addr < (EP9312_UART_BASE1 + EP9312_UART_SIZE))) {
		ep9312_uart_write (state, (u32) (addr - EP9312_UART_BASE1),
				   data, 0);
	}
	if ((addr >= EP9312_UART_BASE3) &&
	    (addr < (EP9312_UART_BASE3 + EP9312_UART_SIZE))) {
		ep9312_uart_write (state, (u32) (addr - EP9312_UART_BASE3),
				   data, 2);
	}

	switch (addr) {
	case SYSCON_CLKSET1:
		break;
	case SYSCON_CLKSET2:
	case SYSCON_PWRCNT:
		break;
	case VIC0INTENABLE:
		io.intmr[0] = data;
		if (data != 0x10 && data != 0x20)
			printf ("SKYEYE: write VIC0INTENABLE=0x%x\n", data);
		ep9312_update_int (state);
		break;
	case VIC1INTENABLE:
		io.intmr[1] = data;
//              printf("SKYEYE: write VIC1INTENABLE=0x%x\n", data);

		ep9312_update_int (state);
		break;
	case VIC0INTENCLEAR:
		io.intmr[0] ^= data;
		ep9312_update_int (state);
		break;
	case VIC1INTENCLEAR:
		io.intmr[1] ^= data;
		ep9312_update_int (state);
		break;
	case SYSCON_DEVCFG:
		io.syscon_devcfg = data;
		break;
	default:
		SKYEYE_DBG
			("SKYEYE:unknown io addr, %s(0x%08x, 0x%08x), pc %x \n",
			 __func__, addr, data, state->Reg[15]);
		break;
	}
}

void
ep9312_mach_init (ARMul_State * state, machine_config_t * this_mach)
{
	ARMul_SelectProcessor (state, ARM_v4_Prop);
	/* ARM920T uses LOW */
	state->lateabtSig = LOW;

//      state->Reg[1] = 282;    //for EP9312 2.4.x arch id
	state->Reg[1] = 451;	//for EP9312 2.6.x arch id
	//state->Reg[1] = 386;  //for EP9315 2.4.x arch id
	this_mach->mach_io_do_cycle = ep9312_io_do_cycle;
	this_mach->mach_io_reset = ep9312_io_reset;
	this_mach->mach_io_read_byte = ep9312_io_read_byte;
	this_mach->mach_io_write_byte = ep9312_io_write_byte;
	this_mach->mach_io_read_halfword = ep9312_io_read_halfword;
	this_mach->mach_io_write_halfword = ep9312_io_write_halfword;
	this_mach->mach_io_read_word = ep9312_io_read_word;
	this_mach->mach_io_write_word = ep9312_io_write_word;

	this_mach->mach_update_int = ep9312_update_int;

}

我觉得soc仿真的意义在于让外设的地址读写变得有意义,比如如何让串口地址进行输出、让时钟进行计数、让中断可以真正地发挥作用。上面这段代码看上去行数很多,但是最重要的还是最后一个函数。当然,除此之外,系统注册也是必不可少的,

machine_config_t arm_machines[] = {
	/* machine define for cpu without mmu */
	{"at91", at91_mach_init, NULL, NULL, NULL},		/* ATMEL AT91X40 */
	{"lpc", lpc_mach_init, NULL, NULL, NULL},		/* PHILIPS LPC2xxxx */
	{"s3c4510b", s3c4510b_mach_init, NULL, NULL, NULL},	/* Samsung s3c4510b */
	{"s3c44b0x", s3c44b0x_mach_init, NULL, NULL, NULL},	/* Samsung s3c44b0x */
	{"s3c44b0", s3c44b0x_mach_init, NULL, NULL, NULL},	/* Samsung s3c44b0x */
	{"s3c3410x", s3c3410x_mach_init, NULL, NULL, NULL},	/* Samsung s3c3410x */

	/* machine define for cpu with mmu */
	{"ep7312", ep7312_mach_init, NULL, NULL, NULL},		/* Cirrus Logic EP7312 */
	{"lh79520", lh79520_mach_init, NULL, NULL, NULL},	/* sharp LH79520 */
	{"ep9312", ep9312_mach_init, NULL, NULL, NULL},		/* Cirrus Logic EP9312 */
	{"cs89712", cs89712_mach_init, NULL, NULL, NULL},	/* cs89712 */
	{"sa1100", sa1100_mach_init, NULL, NULL, NULL},		/* sa1100 */
	{"pxa_lubbock", pxa250_mach_init, NULL, NULL, NULL},	/* xscale pxa250 lubbock developboard */
	{"pxa_mainstone", pxa270_mach_init, NULL, NULL, NULL},	/* xscale pxa270 mainstone developboard */
	{"at91rm92", at91rm92_mach_init, NULL, NULL, NULL},	/* at91RM9200 */
	{"s3c2410x", s3c2410x_mach_init, NULL, NULL, NULL},	/* s3c2410x */
	{"s3c2440", s3c2440_mach_init, NULL, NULL, NULL},	/* s3c2440 */
	{"sharp_lh7a400", shp_mach_init, NULL, NULL, NULL},	/* sharp lh7a400 developboard */
	{"ns9750", ns9750_mach_init, NULL, NULL, NULL},		/* NetSilicon ns9750 */
	{"lpc2210", lpc2210_mach_init, NULL, NULL, NULL},	/* Philips LPC2210 */
	{"ps7500", ps7500_mach_init, NULL, NULL, NULL},		/* Cirrus Logic PS7500FE */
};

有了这两个文件,基本上soc的逻辑算是了解了。


05-17 00:36