Files
retrobsd/tools/virtualmips/m4k.c
2015-10-29 17:44:44 -07:00

326 lines
7.6 KiB
C

/*
* Simulation of generic M4K core.
*
* Copyright (C) 2015 Serge Vakulenko <serge@vak.ru>
*
* This file is part of the virtualmips distribution.
* See LICENSE file for terms of the license.
*/
#define _GNU_SOURCE
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "vp_lock.h"
#include "utils.h"
#include "mips.h"
#include "vm.h"
#include "cpu.h"
#include "mips_exec.h"
#include "debug.h"
#include "m4k.h"
#include "device.h"
#include "mips_jit.h"
#define BOOT_RAM_KBYTES 64 /* Size of boot RAM area */
/*
* Store a word to the physical memory.
*/
int store_word (vm_instance_t *vm, m_pa_t paddr, unsigned data)
{
struct vdevice *dev = dev_lookup (vm, paddr);
char *ptr;
if (! dev)
return -1;
/* Only for RAM */
ptr = (char *) dev->host_addr + (paddr - dev->phys_addr);
*(m_uint32_t*) ptr = data;
return 0;
}
/*
* Load MIPS hex file into memory.
*/
static void load_hex_file (struct vm_instance *vm, const char *filename)
{
FILE *fp;
char buf [64], *p, *ep;
unsigned address, end_address, data, big_endian = 0;
fp = fopen (filename, "r");
if (! fp) {
fprintf (stderr, "%s: Cannot open\n", filename);
exit (-1);
}
while (fgets (buf, sizeof(buf), fp) != 0) {
/* Check header for endian */
if (buf[0] == '#') {
if (strncmp (buf, "# Endian Big", 12) == 0) {
big_endian = 1;
}
continue;
}
address = strtoul (buf, &ep, 16);
if (ep == buf || *ep == 0) {
continue;
}
if (*ep == '-') {
p = ep+1;
end_address = strtoul (p, &ep, 16);
if (ep == p || *ep != ' ') {
continue;
}
} else
end_address = address;
p = ep;
data = strtoul (p, &ep, 16);
if (ep == p) {
continue;
}
/* Byte swap. */
if (big_endian) {
data = (data & 0x000000ff) << 24 |
(data & 0x0000ff00) << 8 |
(data & 0x00ff0000) >> 8 |
(data & 0xff000000) >> 24 ;
}
do {
if (store_word (vm, address << 2, data) < 0) {
fprintf (stderr, "%s: No memory at physical address %08x\n",
filename, address << 2);
exit (-1);
}
//printf (" %08x <= %08x\n", address << 2, data);
} while (address++ < end_address);
}
fclose (fp);
}
/*
* Initialize the M4K platform.
*/
static int m4k_init_platform (m4k_t *m4k)
{
struct vm_instance *vm = m4k->vm;
cpu_mips_t *cpu0;
void *(*cpu_run_fn) (void *);
//vm_init_vtty (vm);
/* Create a CPU group */
vm->cpu_group = cpu_group_create ("System CPU");
/* Initialize the virtual MIPS processor */
cpu0 = cpu_create (vm, CPU_TYPE_MIPS32, 0);
if (! cpu0) {
vm_error (vm, "unable to create CPU0!\n");
return (-1);
}
/* Add this CPU to the system CPU group */
cpu_group_add (vm->cpu_group, cpu0);
vm->boot_cpu = cpu0;
/* create the CPU thread execution */
cpu_run_fn = (void *) mips_exec_run_cpu;
if (pthread_create (&cpu0->cpu_thread, NULL, cpu_run_fn, cpu0) != 0) {
fprintf (stderr, "cpu_create: unable to create thread for CPU%u\n",
0);
free (cpu0);
return (-1);
}
/* 32-bit address */
cpu0->addr_bus_mask = 0xffffffff;
/* Initialize main RAM */
vm_ram_init (vm, 0x00000000ULL);
/* Initialize boot RAM area */
dev_ram_init (vm, "boot", 0x1fc00000, BOOT_RAM_KBYTES*1024);
load_hex_file (vm, m4k->boot_file_name);
return (0);
}
/*
* Find pending interrupt with the biggest priority.
* Setup INTSTAT and cause registers.
* Update irq_pending flag for CPU.
*/
void m4k_update_irq_flag (m4k_t *m4k)
{
cpu_mips_t *cpu = m4k->vm->boot_cpu;
/* Assume no interrupts pending. */
cpu->irq_cause = 0;
cpu->irq_pending = 0;
//TODO
cpu->irq_cause = 0;
/*printf ("-- vector = %d, level = %d\n", vector, level);*/
mips_update_irq_flag (cpu);
}
void m4k_clear_irq (vm_instance_t *vm, u_int irq)
{
m4k_t *m4k = (m4k_t*) vm->hw_data;
/* Clear interrupt flag status */
//TODO
m4k_update_irq_flag (m4k);
}
void m4k_set_irq (vm_instance_t *vm, u_int irq)
{
m4k_t *m4k = (m4k_t*) vm->hw_data;
/* Set interrupt flag status */
//TODO
m4k_update_irq_flag (m4k);
}
/*
* Activate core timer interrupt
*/
void set_timer_irq (cpu_mips_t *cpu)
{
//TODO
}
/*
* Clear core timer interrupt
*/
void clear_timer_irq (cpu_mips_t *cpu)
{
//TODO
}
/*
* Increment timers.
*/
void host_alarm (cpu_mips_t *cpu, int nclocks)
{
//m4k_t *m4k = (m4k_t*) cpu->vm->hw_data;
//TODO
}
/*
* Create an instance of virtual machine.
*/
vm_instance_t *create_instance (char *filename)
{
vm_instance_t *vm;
m4k_t *m4k;
const char *name = "m4k";
m4k = malloc (sizeof (*m4k));
if (! m4k) {
fprintf (stderr, "M4K: unable to create new instance!\n");
return NULL;
}
memset (m4k, 0, sizeof (*m4k));
vm = vm_create (name, VM_TYPE_PIC32);
if (! vm) {
fprintf (stderr, "M4K: unable to create VM instance!\n");
free (m4k);
return NULL;
}
vm->hw_data = m4k;
m4k->vm = vm;
/* Initialize default parameters for m4k */
vm->ram_size = 4*1024; /* kilobytes */
vm->debug_level = 3; /* trace all instructions */
m4k->boot_file_name = filename ? filename : "test.hex";
m4k->start_address = 0xbfc00000;
const char *output_file_name = "m4k.trace";
printf ("Redirect output to %s\n", output_file_name);
if (freopen(output_file_name, "w", stdout) != stdout) {
fprintf (stderr, "M4K: Unable to redirect output!\n");
exit(-1);
}
/* Print the configure information */
printf("ram_size: %dk bytes \n", vm->ram_size);
printf("start_address: 0x%x\n", m4k->start_address);
/* init gdb debug */
vm_debug_init (m4k->vm);
return m4k->vm;
}
int init_instance (vm_instance_t * vm)
{
m4k_t *m4k = (m4k_t *) vm->hw_data;
cpu_mips_t *cpu;
if (m4k_init_platform (m4k) == -1) {
vm_error (vm, "unable to initialize the platform hardware.\n");
return (-1);
}
if (! vm->boot_cpu) {
vm_error (vm, "unable to boot cpu.\n");
return (-1);
}
/* IRQ routing */
vm->set_irq = m4k_set_irq;
vm->clear_irq = m4k_clear_irq;
vm_suspend (vm);
/* Check that CPU activity is really suspended */
if (cpu_group_sync_state (vm->cpu_group) == -1) {
vm_error (vm, "unable to sync with system CPUs.\n");
return (-1);
}
/* Reset the boot CPU */
cpu = vm->boot_cpu;
mips_reset (cpu);
/* Set config0-config3 registers. */
cpu->cp0.config_usable = 0x0f;
cpu->cp0.config_reg[0] = 0xa4000582;
cpu->cp0.config_reg[1] = 0x80000006;
cpu->cp0.config_reg[2] = 0x80000000;
cpu->cp0.config_reg[3] = 0x00000020;
/* set PC and PRID */
cpu->pc = m4k->start_address;
cpu->cp0.tlb_entries = 0;
cpu->cp0.reg[MIPS_CP0_PRID] = 0x00018700;
cpu->cp0.reg[MIPS_CP0_DEBUG] = 0x00010000;
/* Enable magic opcodes. */
cpu->magic_opcodes = 1;
/* reset all devices */
dev_reset_all (vm);
#ifdef _USE_JIT_
/* if jit is used. flush all jit buffer */
if (vm->jit_use)
mips_jit_flush (cpu, 0);
#endif
/* Launch the simulation */
printf ("--- Start simulation: PC=0x%" LL "x, JIT %sabled\n",
cpu->pc, vm->jit_use ? "en" : "dis");
vm->status = VM_STATUS_RUNNING;
cpu_start (vm->boot_cpu);
return (0);
}