diff options
author | Tobias Klauser <tklauser@distanz.ch> | 2010-11-10 09:20:50 +0100 |
---|---|---|
committer | Tobias Klauser <tklauser@distanz.ch> | 2010-11-10 09:20:50 +0100 |
commit | 32f507ce5f66dd9c89a45854688f46bde33c5e3d (patch) | |
tree | 9626fc49bcf9c3f8e7fbf228395efe72688989bf /niosii.c |
Initial import of nios2sim (http://nios2sim.googlecode.com/svn/trunk/ r16)
Diffstat (limited to 'niosii.c')
-rw-r--r-- | niosii.c | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/niosii.c b/niosii.c new file mode 100644 index 0000000..4db4ce2 --- /dev/null +++ b/niosii.c @@ -0,0 +1,603 @@ +/* + Nios-sim - one simple NIOSII simulator only for personal interest and fun. + Copyright (C) 2010 chysun2000@gmail.com + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "public.h" +#include "niosii.h" +#include "io_device.h" +#include "instruction.h" + +static struct NIOS_CPU cpu; +static struct image_info * img_info = NULL; + +void reset_cpu(void) +{ + img_info = get_image_info(); + memset(&cpu, 0x00, sizeof(struct NIOS_CPU)); + /* set the default to control register */ + cpu.ctrl_regs[status] = 0x00800000; + cpu.gp_regs[4] = 0x00; + cpu.gp_regs[7] = 0x00; + cpu.trace_index = 0; +} + +static uint32_t get_opcode(uint32_t intr) +{ + struct i_type_code * code = (struct i_type_code*)&intr; + return code->op; +} + +static uint32_t get_opxcode(uint32_t intr) +{ + struct r_type_code * code = (struct r_type_code *)&intr; + return code->opx; +} + +struct NIOS_CPU * get_nios_cpu(void) +{ + return &cpu; +} + +static uint32_t handle_r_type_code(uint32_t opxcode, uint32_t code) +{ + return (r_type_handlers[opxcode]).handler(&cpu, code); +} + +static uint32_t handle_i_type_code(uint32_t opcode, uint32_t code) +{ + return i_type_handlers[opcode].handler(&cpu, code); +} + +static uint32_t handle_j_type_code(uint32_t opcode, uint32_t code) +{ + return j_type_handlers[opcode].handler(&cpu, code); +} + +uint32_t get_offset_pc(uint32_t pc, uint32_t base_addr) +{ + return (pc - base_addr) / 4; +} + +uint32_t get_instruct(struct NIOS_CPU * cpu, uint32_t * mem_base, + uint32_t base_addr) +{ + uint32_t code = 0; + uint32_t offset = get_offset_pc(cpu->pc, base_addr); + + code = mem_base[offset]; + return code; +} + +static int is_j_type(uint32_t opcode) +{ + if (opcode == CALL || opcode == JMPI){ + return SIM_TRUE; + } + else { + return SIM_FALSE; + } +} + +uint32_t execute(uint32_t code) +{ + uint32_t opcode = 0; + uint32_t opxcode = 0; + uint32_t pc_mod_type = 0; + + opcode = get_opcode(code); + + if (is_j_type(opcode) == SIM_TRUE){ + pc_mod_type = handle_j_type_code(opcode, code); + } + else if (opcode != OP_R_TYPE){ + pc_mod_type = handle_i_type_code(opcode, code); + } + else{ + opxcode = get_opxcode(code); + pc_mod_type = handle_r_type_code(opxcode, code); + } + return pc_mod_type; +} + +int32_t get_addr_type(uint32_t addr) +{ + if ((addr >= img_info->base_addr) + && ((addr - img_info->base_addr) <= img_info->mem_size)){ + return MEM_ADDR; + } + else{ + return IO_ADDR; + } +} + +uint8_t get_byte(uint32_t addr) +{ + if (get_addr_type(addr) == MEM_ADDR){ + uint8_t * buf = (uint8_t *)img_info->mem_base; + return buf[addr - img_info->base_addr]; + } + else{ + struct io_device * dev = get_device(addr); + if (dev != NULL){ + return dev->read_data(dev, addr, 1); + } + else { + printf("%s->error at %x\n",__func__,addr); + return 0; + } + } +} + +void store_byte(uint32_t addr, unsigned char data) +{ + if(get_addr_type(addr) == MEM_ADDR){ + uint8_t * buf = (uint8_t *)img_info->mem_base; + buf[addr - img_info->base_addr] = data; + } + else { + struct io_device * dev = get_device(addr); + if (dev != NULL){ + dev->write_data(dev, addr, data, 1); + } + else { + printf("%s->unhandled data:%x at %x\n",__func__,data,addr); + } + } +} + +uint16_t get_half_word(uint32_t addr) +{ + if (get_addr_type(addr) == MEM_ADDR){ + uint16_t * buf = (uint16_t *)img_info->mem_base; + return buf[(addr - img_info->base_addr)/2]; + } + else { + struct io_device * dev = get_device(addr); + if (dev != NULL){ + return dev->read_data(dev, addr, 2); + } + else { + printf("%s->error at %x\n",__func__,addr); + return 0; + } + } +} + +void store_half_word(uint32_t addr, unsigned short data) +{ + if (get_addr_type(addr) == MEM_ADDR){ + uint16_t * buf = (uint16_t *)img_info->mem_base; + buf[(addr - img_info->base_addr) / 2] = data; + } + else { + struct io_device * dev = get_device(addr); + if (dev != NULL){ + dev->write_data(dev, addr, data, 2); + } + else { + printf("%s->unhandled data:%x at %x\n",__func__,data,addr); + } + } +} + +uint32_t get_word(uint32_t addr) +{ + if (get_addr_type(addr) == MEM_ADDR){ + uint32_t * buf = (uint32_t * )img_info->mem_base; + return buf[(addr - img_info->base_addr) / 4]; + } + else { + struct io_device * dev = get_device(addr); + if (dev != NULL){ + return dev->read_data(dev, addr, 4); + } + else { + printf("%s->error at %x\n",__func__,addr); + return 0; + } + } +} + +void store_word(uint32_t addr, uint32_t data) +{ + if (get_addr_type(addr) == MEM_ADDR){ + uint32_t * buf = (uint32_t *)img_info->mem_base; + buf[(addr - img_info->base_addr) / 4] = data; + } + else { + struct io_device * dev = get_device(addr); + if (dev != NULL){ + dev->write_data(dev, addr, data, 4); + } + else { + printf("%s->unhandled data:%x at %x\n",__func__,data,addr); + } + } +} + +static uint32_t has_irq(struct NIOS_CPU * cpu) +{ + uint32_t ret_val = CPU_HAS_NO_EVENT; + uint32_t irq_mask = 0; + uint32_t temp = 0; + + /* 1. judge the device has irq */ + irq_mask = get_io_irq_status(); + + if (irq_mask != 0){ + if ((cpu->ctrl_regs[status] & REG_STATUS_PIE) == REG_STATUS_PIE){ + temp = cpu->ctrl_regs[ienable] & irq_mask; +#if 1 + if (temp != 0 && (temp != cpu->ctrl_regs[ipending])){ +#else + if (temp != 0){ +#endif + /* 2. modify the control register */ + cpu->ctrl_regs[ipending] |= temp; + ret_val = CPU_HAS_IRQ; + } + } + } + + return ret_val; +} + +static uint32_t has_exception(struct NIOS_CPU * cpu) +{ + uint32_t ret_val = CPU_HAS_NO_EVENT; + + return ret_val; +} + +static uint32_t get_cpu_status(struct NIOS_CPU * cpu) +{ + uint32_t ret_val = CPU_HAS_NO_EVENT; + ret_val |= has_irq(cpu); + ret_val |= has_exception(cpu); + + return ret_val; +} + +static void handle_irq(struct NIOS_CPU * cpu) +{ + uint32_t reg_status_val = 0; + uint32_t temp_pc = 0; + /* according to the kind of interrupt or exception, + * set PC to the address of IRQ or exception vector. + */ + temp_pc = cpu->pc; + cpu->pc = EXCEPTION_HANDLER_ADDR; + reg_status_val = cpu->ctrl_regs[status]; + cpu->ctrl_regs[status] &= 0xFFFFFFFE; /* clear PIE */ + if((reg_status_val & REG_STATUS_EH) == 0){ + cpu->ctrl_regs[estatus] = reg_status_val; + cpu->gp_regs[ea] = temp_pc + 4; + } +} + +static void handle_exception(struct NIOS_CPU * cpu) +{ + +} + +void handle_irq_exception(struct NIOS_CPU * cpu) +{ + uint32_t cpu_status = CPU_HAS_NO_EVENT; + + cpu_status = get_cpu_status(cpu); + if(cpu_status & CPU_HAS_IRQ){ + handle_irq(cpu); + } + else if (cpu_status & CPU_HAS_EXCEPTION){ + handle_exception(cpu); + } +} + +static void dump_j_code(uint32_t code) +{ + def_j_type_code; + + printf("TYPE: J Code [%08X]\n", code); + printf("OP: 0x%02X\n",instr->op); + printf("IMM26: 0x%X\n",instr->imm26); +} + +static void dump_r_code(uint32_t code) +{ + def_r_type_code; + + printf("TYPE: R Code [%08X]\n", code); + printf("OP: 0x%02X\n", instr->op); + printf("A=0x%X B=0x%X C=0x%X OPX=0x%X N=0x%X\n", + instr->a, instr->b, instr->c, instr->opx, instr->n); +} + +static void dump_i_code(uint32_t code) +{ + def_i_type_code; + + printf("TYPE: I Code [%08X]\n", code); + printf("OP: 0x%02X\n", instr->op); + printf("A=0x%X B=0x%X IMM16=0x%04X\n", instr->a, instr->b, instr->imm16); +} + +static void dump_code(uint32_t code) +{ + uint32_t code_type = get_opcode(code); + printf("\n"); + printf("--------------Code Dump--------------\n"); + switch(code_type){ + case OP_J_TYPE: + dump_j_code(code); + break; + case OP_R_TYPE: + dump_r_code(code); + break; + default: + dump_i_code(code); + break; + } + printf("-------------------------------------\n"); +} + +void dump_curr_code(uint32_t code) +{ + dump_code(code); +} + +void dump_next_code(struct NIOS_CPU * cpu) +{ + uint32_t code = get_instruct(cpu,img_info->mem_base, img_info->base_addr); + dump_code(code); +} + +void dump_pc(struct NIOS_CPU * cpu) +{ + int32_t i = 0; + printf("============================================================\n"); + printf("ERROR PC=%08X\n",cpu->pc); + printf("PC Trace Index :%d\n", cpu->trace_index); + printf("============================================================\n"); + for (i=0;i<PC_TRACE_CNT;i++){ + if (i == (cpu->trace_index - 1)){ + printf("(%08X) ",cpu->pc_trace[i]); + } + else { + printf("%08X ",cpu->pc_trace[i]); + } + if (((i+1)% 8) == 0){ + printf("\n"); + } + } + printf("\n============================================================\n"); +} + +static const char * g_regs_name[] = { + " ZERO", + " AT", + "REG02", + "REG03", + "REG04", + "REG05", + "REG06", + "REG07", + "REG08", + "REG09", + "REG10", + "REG11", + "REG12", + "REG13", + "REG14", + "REG15", + "REG16", + "REG17", + "REG18", + "REG19", + "REG20", + "REG21", + "REG22", + "REG23", + " ET", + " BT", + " GP", + " SP", + " FP", + " EA", + " BA", + " RA" +}; + +static const char * c_regs_name[] = { + "SSTAT", + "ESTAT", + "BSTAT", + "IENAB", + "IPEND", + "CPUID", + "RESER", + "EXCEP", + "PTEAD", + "TLBAD", + "TLBMI", + "RESER", + "BADAD", + "CONFI", + "MPUBA", + "MPUAD" +}; + +void dump_register(struct NIOS_CPU * cpu) +{ + int32_t i = 0; + + printf("\n"); + printf("============================================================\n"); + printf("[Genernal Register]\n"); + printf("============================================================\n"); + for (i=0;i<NIOS_REG_CNT;i++){ + printf("%s=%08X ",g_regs_name[i],cpu->gp_regs[i]); + if (((i+1) % 4) == 0) + printf("\n"); + } + printf("============================================================\n"); + printf("[Control Register]\n"); + printf("============================================================\n"); + for (i=0;i<16;i++){ + printf("%s=%08X ",c_regs_name[i],cpu->ctrl_regs[i]); + if (((i+1) % 4) == 0) + printf("\n"); + } + printf("============================================================\n"); +} + +/** + * Parse the options of break command + */ +#define SPACE_CHAR (' ') +#define BREAK_CMD_LEN (16) +extern char break_func_name[256]; +static uint32_t get_break_pc(char * input) +{ + uint32_t ret_pc = 0x0; + uint32_t input_len = 0; + + input_len = strlen(input); + /* + * TODO: the judgement of option's format is not full implemented. + */ + if (input_len == BREAK_CMD_LEN){ + sscanf(input, "break 0x%08x\n", &ret_pc); + } + + if (strstr(input, "func") != NULL){ + memset(break_func_name, 0x00, 256); + sscanf(input, "break func=%s", break_func_name); + return 0xFFFFFFFF; + } + return ret_pc; +} + +void set_break(struct NIOS_CPU * cpu, char * input) +{ + uint32_t break_pc = 0x0; + + break_pc = get_break_pc(input); + if (break_pc != 0x0){ + if (break_pc == 0xFFFFFFFF){ + cpu->mode = BREAK_MODE; + } + else { + cpu->mode = BREAK_MODE; + cpu->break_pc = break_pc; + } + } + else{ + printf("Error Address\n"); + cpu->mode = SINGLE_STEP; + } +} + +static void output_char(char c) +{ + if (c == '\r' || c == '\n'){ + printf("."); + } + else { + printf("%c",c); + } +} +static void output_ascii(uint32_t * buf) +{ + uint32_t val = 0; + char temp = 0; + uint32_t i = 0; + + printf("\t"); + for (i = 0; i < 4; i++){ + val = buf[i]; + temp = val & 0xFF; + output_char(temp); + temp = (val >> 8) & 0xFF; + output_char(temp); + temp = (val >> 16) & 0xFF; + output_char(temp); + temp = (val >> 24) & 0xFF; + output_char(temp); + } +} +/* + * dump the content of memory + */ +void dump_mem(struct NIOS_CPU * cpu, char * input) +{ + uint32_t addr, size; + struct image_info * info = get_image_info(); + int32_t i = 0; + uint32_t * buf = NULL; + uint32_t temp_buf[4] = {0}; + uint32_t index = 0; + sscanf(input, "dump 0x%X %d", &addr, &size); + + addr = addr & (~0x3); + size = size / 4 * 4; + if (addr >= info->base_addr && size < info->mem_size){ + buf = info->mem_base + (addr - info->base_addr) / 4 ; + printf("================================================\n"); + printf("[Memory Dump start:0x%08X size:%d]\n", addr, size); + printf("================================================\n"); + for (i=0;i<size/4;i++){ + temp_buf[index] = buf[i]; + index = index + 1; + if (i % 4 == 0){ + printf("[%08X]",addr + i * 4); + } + printf("%08X ", (uint32_t)buf[i]); + + if (((i+1) % 4) == 0){ + output_ascii(temp_buf); + index = 0; + printf("\n"); + } + else if (i == (size/4 - 1)){ + printf("\n"); + } + } + printf("================================================\n"); + } +} + +void clean_ipending(uint32_t mask) +{ + cpu.ctrl_regs[ipending] &= (~mask); +} + +/* Record the pc trace. It is used when segment fault happening. */ +void trace_pc(struct NIOS_CPU * cpu) +{ + cpu->pc_trace[cpu->trace_index] = cpu->pc; + cpu->trace_index ++; + if (cpu->trace_index >= PC_TRACE_CNT){ + cpu->trace_index = 0; + } +} +/*----------------------------------------------------------------------------*/ + |