simulavr-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Simulavr-devel] vcd file dumper for simulavr


From: Carsten Beth
Subject: [Simulavr-devel] vcd file dumper for simulavr
Date: Wed Oct 23 15:07:01 2002

Hello everybody!

I have written a display program which writes simulavr output to a value

change dump (vcd) file. vcd files are defined in the language reference
manual of the hardware description language verilog. They are mainly
used to
store signal traces and processing them in additional application, e.g.
waveform viewer.

In the attachment there is the tar file disp-vcd.tgz. For installation
you
have to save it in your simulavr build directory and untar it. Then
change to
the disp-vcd directory and call make. Make sure the executable
simulavr-vcd
is in your PATH.

Unfortunately simulavr-vcd needs time-information from simulavr, which
normally isn't transfer. So you have to patch simulavr and recompile it.
I am
not familiar with the patch mechanism, so I send the files, which has to
be
changed, in the attachment. Sorry for that procedure. They work with
simulavr
release 0.1.1.

Before starting a simulation you have to create a configuration file
vcd.cfg
(or copy/modify the example file). In that file you have to define the
signals which shall be traced. Traceable signals are at the moment
registers,
io-registers, sram content, the program counter and the stack pointer.
See
the example vcd.cfg for syntax.

To start simulavr with the simulavr-vcd as a co-process you have to
execute
e.g.: simulavr -d at90s2313 --gdbserver --disp-prog=simulavr-vcd

While simulation the vcd file is written. You can use (hopefully) any
waveform viewer which can handle vcd files. At the moment I have just
test it
with the free viewer GTKWave.

Please try the tool, I think it's a good aid for debugging your
software.

Carsten

Attachment: disp-vcd.tgz
Description: Binary data

/*
 * $Id: display.c,v 1.11 2002/09/08 20:35:11 troth Exp $
 *
 ****************************************************************************
 *
 * simulavr - A simulator for the Atmel AVR family of microcontrollers.
 * Copyright (C) 2001, 2002  Theodore A. Roth
 *
 * 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
 *
 ****************************************************************************
 */

/**
 * \file display.c
 * \brief Interface for using display coprocesses.
 *
 * Simulavr has the ability to use a coprocess to display register and memory
 * values in near real time.
 */

#include <config.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>

#include "avrerror.h"
#include "avrmalloc.h"
#include "display.h"

enum {
    MAX_BUF = 1024,
};

/* I really don't want to use a global here, but I also don't want to have to
   track the pipe's fd in the core. */

static int global_pipe_fd = -1;

/* Need to store the child's pid so that we can kill and waitpid it when you
   close the display. Otherwise we have problems with zombies. */

static pid_t global_child_pid = -1;

/** \brief Open a display as a coprocess.
    \param prog        The program to use as a display coprocess.
    \param no_xterm    If non-zero, don't run the disply in an xterm.
    \param flash_sz    The size of the flash memory space in bytes.
    \param sram_sz     The size of the sram memory space in bytes.
    \param sram_start  The addr of the first byte of sram (usually 0x60 or 
0x100).
    \param eeprom_sz   The size of the eeprom memory space in bytes.

    Try to start up a helper program as a child process for displaying
    registers and memory. If the prog argument is NULL, don't start up a
    display.

    Returns an open file descriptor of a pipe used to send data to
    the helper program. 
    
    Returns -1 if something failed. */

int display_open( char *prog, int no_xterm, int flash_sz, 
                  int sram_sz, int sram_start, int eeprom_sz )
{
    pid_t pid;
    int   pfd[2];               /* pipe file desc: pfd[0] is read, pfd[1] is 
write */
    int   res;

    if (prog == NULL)
    {
        prog = getenv( "SIM_DISP_PROG" );
        if (prog == NULL)
            return -1;
    }

    /* Open a pipe for writing from the simulator to the display program. 
       We don't want to use popen() since the display program might need to 
       use stdin/stdout for it's own uses. */

    res = pipe( pfd );
    if (res < 0)
    {
        avr_warning( "pipe failed: %s\n", strerror(errno) );
        return -1;
    }

    /* Fork off a new process. */

    pid = fork();
    if (pid < 0)
    {
        avr_warning( "fork failed: %s\n", strerror(errno) );
        return -1;
    }
    else if (pid > 0)           /* parent process */
    {
        /* close the read side of the pipe */
        close( pfd[0] );

        /* remember the child's pid */
        global_child_pid = pid;

        global_pipe_fd = pfd[1];
        return global_pipe_fd;
    }
    else                        /* child process */
    {
        char pfd_env[20];
        char fl_sz[20], sr_sz[20], sr_start[20], eep_sz[20];
        char spfd[10];

        /* close the write side of the pipe */
        close( pfd[1] );

        /* setup the args for display program */
        snprintf( fl_sz,    sizeof(fl_sz)-1,     "%d", flash_sz );
        snprintf( sr_sz,    sizeof(sr_sz)-1,     "%d", sram_sz );
        snprintf( sr_start, sizeof(sr_start)-1,  "%d", sram_start );
        snprintf( eep_sz,   sizeof(eep_sz)-1,    "%d", eeprom_sz );
        snprintf( spfd,     sizeof(spfd)-1,      "%d", pfd[0] );

        /* set the SIM_PIPE_FD env variable */
        snprintf( pfd_env,  sizeof(pfd_env), "SIM_PIPE_FD=%d", pfd[0] );
        putenv( pfd_env );

        /* The user can specify not to use an xterm since some display
           programs might not need (or want) to be run in an xterm. For
           example, a gtk+ program would be able to handle it's own
           windowing. Of course, starting 'prog' up with it's own xterm, will
           not hurt and 'prog' will put stdout/stderr there instead of mixing
           with simulavr's output. The default is to start prog in an
           xterm. */

        if (no_xterm)
        {
            execlp( prog, prog, "--pfd", spfd, fl_sz, sr_sz, sr_start, eep_sz, 
NULL );
        }
        else
        {
            /* try to start up the display program in it's own xterm */
            execlp( "xterm", "xterm", "-geom", "100x50", "-e",
                    prog, "--pfd", spfd, fl_sz, sr_sz, sr_start, eep_sz, NULL );
        }

        /* if the exec returns, an error occurred */
        avr_warning( "exec failed: %s\n", strerror(errno) );
        _exit (1);
    }

    return -1;                  /* should never get here */
}

/** \brief Close a display and send coprocess a quit message. */

void display_close( void )
{
    if (global_pipe_fd < 0)
        return;

    display_send_msg( "q" );
    close( global_pipe_fd );
    global_pipe_fd = -1;

    kill( global_child_pid, SIGINT );
    waitpid( 0, NULL, 0 );
}

static unsigned char checksum( char *s )
{
    unsigned char CC = 0;
    while (*s)
    {
        CC += *s;
        s++;
    }

    return CC;
}

/** \brief Encode the message and send to display.
    \param msg   The message string to be sent to the display process.

    Encoding is the same as that used by the gdb remote protocol: '\$...\#CC'
    where '...' is msg, CC is checksum. There is no newline termination for
    encoded messages.

    FIXME: TRoth: This should be a private function. It is only public so that
    dtest.c can be kept simple. dtest.c should be changed to avoid direct use
    of this function. [dtest.c has served it's purpose and will be retired
    soon.] */

void display_send_msg( char *msg )
{
    int   len = strlen(msg)+4+1;
    int   res;
    char *enc_msg;              /* the encoded msg */

    enc_msg = avr_new0( char, len+1 );

    snprintf(enc_msg, len, "$%s#%02x", msg, checksum(msg));
#if defined(DISP_DEBUG_OUTPUT_ON)
    fprintf(stderr, "DISP: %s\n", enc_msg);
#endif
    
    res = write( global_pipe_fd, enc_msg, len );
    if ( (res < 0) && (errno == EINTR) )
    {
        /* write() was interrupted, try again and if it still fails, let it be
           fatal. */
        avr_warning("Interrupted write()\n");
        res = write( global_pipe_fd, enc_msg, len );
    }
    if (res < 0)
        avr_error( "write failed: %s\n", strerror(errno) );
    if (res < len)
        avr_error( "incomplete write\n" );

    avr_free( enc_msg );
}

static char global_buf[MAX_BUF+1];

/** \brief Update the time in the display.
    \param clock   The new time in number of clocks. */

void display_clock( int clock ) /* NEW */
{
    if (global_pipe_fd < 0)
        return;

    snprintf(global_buf, MAX_BUF, "n%x", clock);
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}

/** \brief Update the Program Counter in the display.
    \param val   The new value of the program counter. */

void display_pc( int val )
{
    if (global_pipe_fd < 0)
        return;

    snprintf(global_buf, MAX_BUF, "p%x", val);
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}

/** \brief Update a register in the display.
    \param reg   The register number.
    \param val   The new value of the register. */

void display_reg( int reg, byte val )
{
    if (global_pipe_fd < 0)
        return;

    snprintf(global_buf, MAX_BUF, "r%x:%02x", reg, val);
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}

/** \brief Update an IO register in the display.
    \param reg   The IO register number.
    \param val   The new value of the register. */

void display_io_reg( int reg, byte val )
{
    if (global_pipe_fd < 0)
        return;

    snprintf(global_buf, MAX_BUF, "i%x:%02x", reg, val);
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}

/** \brief Specify a name for an IO register.
    \param reg    The IO register number.
    \param name   The symbolic name of the register.

    Names of IO registers may be different from device to device. */

void display_io_reg_name( int reg, char *name )
{
    if (global_pipe_fd < 0)
        return;

    snprintf(global_buf, MAX_BUF, "I%x:%s", reg, name);
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}

/** \brief Update a block of flash addresses in the display.
    \param addr  Address of beginning of the block.
    \param len   Length of the block (number of words).
    \param vals  Pointer to an array of \a len words.

    The display will update each addr of the block to the coresponding value
    in the \a vals array.

    Each address in the flash references a single 16-bit wide word (or opcode
    or instruction). Therefore, flash addresses are aligned to 16-bit
    boundaries. It is simplest to consider the flash an array of 16-bit values
    indexed by the address. */

void display_flash( int addr, int len, word *vals )
{
    int bytes;
    int i;

    if (global_pipe_fd < 0)
        return;

    bytes = snprintf( global_buf, MAX_BUF, "f%x,%x:", addr, len );

    for ( i=0; i<len; i++ )
    {
        if (MAX_BUF-bytes < 0)
            avr_error( "buffer overflow" );

        bytes += snprintf( global_buf+bytes, MAX_BUF-bytes, 
                        "%04x", vals[i] );
    }
    
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}

/** \brief Update a block of sram addresses in the display.
    \param addr  Address of beginning of the block.
    \param len   Length of the block (number of bytes).
    \param vals  Pointer to an array of \a len bytes.

    The display will update each addr of the block to the coresponding value
    in the \a vals array. */

void display_sram( int addr, int len, byte *vals )
{
    int bytes;
    int i;

    if (global_pipe_fd < 0)
        return;

    bytes = snprintf( global_buf, MAX_BUF, "s%x,%x:", addr, len );

    for ( i=0; i<len; i++ )
    {
        if (MAX_BUF-bytes < 0)
            avr_error( "buffer overflow" );

        bytes += snprintf( global_buf+bytes, MAX_BUF-bytes, 
                        "%02x", vals[i] );
    }
    
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}

/** \brief Update a block of eeprom addresses in the display.
    \param addr  Address of beginning of the block.
    \param len   Length of the block (number of bytes).
    \param vals  Pointer to an array of \a len bytes.

    The display will update each addr of the block to the coresponding value
    in the \a vals array. */


void display_eeprom( int addr, int len, byte *vals )
{
    int bytes;
    int i;

    if (global_pipe_fd < 0)
        return;

    bytes = snprintf( global_buf, MAX_BUF, "e%x,%x:", addr, len );

    for ( i=0; i<len; i++ )
    {
        if (MAX_BUF-bytes < 0)
            avr_error( "buffer overflow" );

        bytes += snprintf( global_buf+bytes, MAX_BUF-bytes, 
                        "%02x", vals[i] );
    }
    
    global_buf[MAX_BUF] = '\0';
    display_send_msg( global_buf );
}
/*
 * $Id: display.h,v 1.5 2002/09/05 06:41:08 troth Exp $
 *
 ****************************************************************************
 *
 * simulavr - A simulator for the Atmel AVR family of microcontrollers.
 * Copyright (C) 2001, 2002  Theodore A. Roth
 *
 * 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
 *
 ****************************************************************************
 */

#ifndef SIM_DISPLAY_H
#define SIM_DISPLAY_H

extern int  display_open  ( char *prog, int no_xterm, 
                            int flash_sz, int sram_sz, int sram_start, int 
eeprom_sz );
extern void display_close ( void );

/* These functions will tell the display to update the given value */

extern void display_clock      ( int clock );
extern void display_pc         ( int val );
extern void display_reg        ( int reg, byte val );
extern void display_io_reg     ( int reg, byte val );
extern void display_io_reg_name( int reg, char *name );
extern void display_flash      ( int addr, int len, word *vals );
extern void display_sram       ( int addr, int len, byte *vals );
extern void display_eeprom     ( int addr, int len, byte *vals );

/* FIXME: this isn't going to be public for much longer */
extern void display_send_msg( char *msg );

#endif /* SIM_DISPLAY_H */
/*
 * $Id: avrcore.c,v 1.53 2002/09/05 06:41:08 troth Exp $
 *
 ****************************************************************************
 *
 * simulavr - A simulator for the Atmel AVR family of microcontrollers.
 * Copyright (C) 2001, 2002  Theodore A. Roth
 *
 * 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
 *
 ****************************************************************************
 */

/**
 *  \file avrcore.c
 *  \brief Module for the core AvrCore object, which is the AVR CPU to be 
simulated.
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

#include "avrerror.h"
#include "avrmalloc.h"
#include "avrclass.h"
#include "utils.h"
#include "callback.h"
#include "op_names.h"

#include "storage.h"
#include "flash.h"

#include "vdevs.h"
#include "memory.h"
#include "stack.h"
#include "register.h"
#include "sram.h"
#include "eeprom.h"
#include "ports.h"

#include "avrcore.h"

#include "display.h"
#include "decoder.h"
#include "sig.h"

/** \brief Flag for enabling output of instruction debug messages. */
int global_debug_inst_output = 0;

/***************************************************************************
 *
 * BreakPt(AvrClass) Methods
 *
 ***************************************************************************/

#ifndef DOXYGEN /* don't expose to doxygen */

typedef struct _BreakPt BreakPt;
struct _BreakPt {
    AvrClass parent;
    int      pc;
};

#endif

static BreakPt *brk_pt_new( int pc );
static void     brk_pt_construct( BreakPt *bp, int pc );
static void     brk_pt_destroy( void *bp );

static BreakPt *brk_pt_new( int pc )
{
    BreakPt *bp;

    bp = avr_new( BreakPt, 1 );
    brk_pt_construct( bp, pc );
    class_overload_destroy( (AvrClass *)bp, brk_pt_destroy );

    return bp;
}

static void brk_pt_construct( BreakPt *bp, int pc )
{
    if (bp == NULL)
        avr_error( "passed null ptr" );

    class_construct( (AvrClass *)bp );

    bp->pc = pc;
}

static void brk_pt_destroy( void *bp )
{
    BreakPt *_bp = (BreakPt *)bp;

    if (_bp == NULL)
        return;

    class_destroy( bp );
}

static DList   *brk_pt_list_add   ( DList *head, int pc );
static DList   *brk_pt_list_delete( DList *head, int pc );
static BreakPt *brk_pt_list_lookup( DList *head, int pc );
static int      brk_pt_cmp        ( AvrClass *d1, AvrClass *d2 );

/* Compare function for break points. */

static int brk_pt_cmp( AvrClass *d1, AvrClass *d2 )
{
    return ((BreakPt *)d1)->pc - ((BreakPt *)d2)->pc;
}

static DList *brk_pt_list_add( DList *head, int pc )
{
    BreakPt *bp = brk_pt_new( pc );

    return dlist_add( head, (AvrClass *)bp, brk_pt_cmp );
}

static DList *brk_pt_list_delete( DList *head, int pc )
{
    BreakPt *bp = brk_pt_new( pc );

    head = dlist_delete( head, (AvrClass *)bp, brk_pt_cmp );
    class_unref( (AvrClass *)bp );

    return head;
}

static BreakPt *brk_pt_list_lookup( DList *head, int pc )
{
    BreakPt *found;
    BreakPt *bp = brk_pt_new( pc );

    found = (BreakPt *)dlist_lookup( head, (AvrClass *)bp, brk_pt_cmp );
    class_unref( (AvrClass *)bp );

    return found;
}

/***************************************************************************
 *
 * Irq(AvrClass) Methods: For managing the irq_pending list.
 *
 ***************************************************************************/

#ifndef DOXYGEN /* don't expose to doxygen */

typedef struct _Irq Irq;
struct _Irq {
    AvrClass  parent;
    IntVect  *vector;

    /* These are only used for storing lookup information. Copies of
       core->{state,sleep_mode}. */
    int          state;
    unsigned int sleep_mode;
};

#endif

static Irq     *irq_new( IntVect *vector, int state, unsigned int sleep_mode );
static void     irq_construct( Irq *irq, IntVect *vector, int state, unsigned 
int sleep_mode );
static void     irq_destroy( void *irq );

static Irq *irq_new( IntVect *vector, int state, unsigned int sleep_mode )
{
    Irq *irq;

    irq = avr_new( Irq, 1 );
    irq_construct( irq, vector, state, sleep_mode );
    class_overload_destroy( (AvrClass *)irq, irq_destroy );

    return irq;
}

static void irq_construct( Irq *irq, IntVect *vector, int state, unsigned int 
sleep_mode )
{
    if (irq == NULL)
        avr_error( "passed null ptr" );

    class_construct( (AvrClass *)irq );

    irq->vector = vector;
    irq->state = state;
    irq->sleep_mode = sleep_mode;
}

static void irq_destroy( void *irq )
{
    Irq *_irq = (Irq *)irq;

    if (_irq == NULL)
        return;

    class_destroy( irq );
}

static DList   *irq_list_add        ( DList *head, IntVect *vector );
static DList   *irq_list_delete     ( DList *head, IntVect *vector );
static Irq     *irq_list_lookup_addr( DList *head, IntVect *vector );
static int      irq_cmp_addr        ( AvrClass *d1, AvrClass *d2 );
static int      irq_cmp_pending     ( AvrClass *d1, AvrClass *d2 );

/* Compare function for break points. */

static int irq_cmp_addr( AvrClass *d1, AvrClass *d2 )
{
    return ((Irq *)d1)->vector->addr - ((Irq *)d2)->vector->addr;
}

static DList *irq_list_add( DList *head, IntVect *vector )
{
    Irq *irq = irq_new( vector, 0, 0 );

    return dlist_add( head, (AvrClass *)irq, irq_cmp_addr );
}

static DList *irq_list_delete( DList *head, IntVect *vector )
{
    Irq *irq = irq_new( vector, 0, 0 );

    head = dlist_delete( head, (AvrClass *)irq, irq_cmp_addr );
    class_unref( (AvrClass *)irq );

    return head;
}

static Irq *irq_list_lookup_addr( DList *head, IntVect *vector )
{
    Irq *found;
    Irq *irq = irq_new( vector, 0, 0 );

    found = (Irq *)dlist_lookup( head, (AvrClass *)irq, irq_cmp_addr );
    class_unref( (AvrClass *)irq );

    return found;
}

static int irq_cmp_pending( AvrClass *d1, AvrClass *d2 )
{
    Irq *i1 = (Irq *)d1;                      /* This is the irq which might
                                                 be ready to be vectored
                                                 into. */
    int state = ((Irq *)d2)->state;           /* The state the device is
                                                 currently in. */
    unsigned int sleep_mode = ((Irq *)d2)->sleep_mode; /* This is the sleep 
mode the
                                                 device in currently in. Only
                                                 one bit should be set.*/

    if (state == STATE_SLEEP)
    {
        /* If device is in the sleep state, the irq will only pending if it
           can wake up the device. */

        if (sleep_mode & i1->vector->can_wake)
            return 0;           /* vector into the irq */
        else
            return -1;          /* try the next irq */
    }

    /* If the state is not STATE_SLEEP, any irq we see is automatically
       pending, so vector it. */

    return 0;
}

/* Walk the list looking for a pending irq which can be handled. If the device
   is in a sleep state, the can_wake mask could force the head of the list to
   not be the irq which gets vectored. */

static IntVect *irq_get_pending_vector( DList *head, int state, unsigned int 
sleep_mode )
{
    Irq *found;
    Irq *irq = irq_new( NULL, state, sleep_mode );

    found = (Irq *)dlist_lookup( head, (AvrClass *)irq, irq_cmp_pending );
    class_unref( (AvrClass *)irq );

    return found->vector;
}

static IntVect *irq_get_head_vector( DList *head )
{
    return ((Irq *)dlist_get_head_data( head ))->vector;
}

/***************************************************************************
 *
 * AvrCore(AvrClass) Methods
 *
 ***************************************************************************/

/** \brief Allocate a new AvrCore object. */

AvrCore *avr_core_new( int flash_sz, int PC_sz, StackType stack_type, 
                       int stack_sz, int vtab_idx )
{
    AvrCore *core;

    core = avr_new( AvrCore, 1 );
    avr_core_construct( core, flash_sz, PC_sz, stack_type, stack_sz, vtab_idx );
    class_overload_destroy( (AvrClass *)core, avr_core_destroy );

    return core;
}

/** \brief Constructor for the AvrCore class. */
  
void avr_core_construct( AvrCore *core, int flash_sz, int PC_sz,
                         StackType stack_type, int stack_sz, int vtab_idx)
{
    if (core == NULL)
        avr_error( "passed null ptr" );

    class_construct( (AvrClass *)core );

    core->state    = STATE_STOPPED;
    core->sleep_mode = 0;       /* each bit represents a sleep mode */
    core->PC       = 0;
    core->PC_size  = PC_sz;
    core->PC_max   = flash_sz / 2; /* flash_sz is in bytes, need number of 
words here */
    core->sreg     = sreg_new();
    core->flash    = flash_new( flash_sz );
    core->gpwr     = gpwr_new();
    core->mem      = mem_new();

    core->opcode_break_pt = 0xffff;
    core->breakpoint      = NULL;

    core->irq_pending = NULL;
    core->irq_vtable  = global_vtable_list[ vtab_idx ];
    core->irq_offset  = 0;

    core->CK       = 0;
    core->inst_CKS = 0;

    core->clk_cb   = NULL;
    core->async_cb = NULL;

    switch (stack_type) {
        case STACK_HARDWARE:
            core->stack = (Stack *)hwstack_new( stack_sz );
            break;
        case STACK_MEMORY:
            core->stack = (Stack *)memstack_new( core->mem );
            break;
        default:
            avr_error( "Bad stack type.\n" );
    }

    /* Since these are being stored in two places, we need to ref em */

    avr_core_attach_vdev( core, (VDevice *)core->sreg );
    class_ref( (AvrClass *)core->sreg );

    avr_core_attach_vdev( core, (VDevice *)core->gpwr );
    class_ref( (AvrClass *)core->gpwr );

    /* Initialize the decoder lookup table */
    decode_init_lookup_table();

    /* Send initial clock cycles to display */
    display_clock( 0 );
}

/**
 * \brief Destructor for the AvrCore class.
 * 
 * Not to be called directly, except by a derived class.
 * Called via class_unref.
 */
void avr_core_destroy( void *core )
{
    AvrCore *_core = (AvrCore *)core;

    if (_core == NULL)
        return;

    class_unref( (AvrClass *)_core->sreg );
    class_unref( (AvrClass *)_core->flash );
    class_unref( (AvrClass *)_core->gpwr );
    class_unref( (AvrClass *)_core->mem );
    class_unref( (AvrClass *)_core->stack );

    dlist_delete_all( _core->breakpoint );
    dlist_delete_all( _core->clk_cb );
    dlist_delete_all( _core->async_cb );
    dlist_delete_all( _core->irq_pending );

    class_destroy( core );
}

/** \brief Query the sizes of the 3 memory spaces: flash, sram, and eeprom. */
void avr_core_get_sizes( AvrCore *core, int *flash, int *sram, int *sram_start,
                         int *eeprom )
{
    VDevice *dev;
    *flash = flash_get_size( core->flash );

    dev = mem_get_vdevice_by_name( core->mem, "SRAM" );
    if (dev)
    {
        *sram = sram_get_size( (SRAM *)dev );
        *sram_start = sram_get_base( (SRAM *)dev );
    }
    else
    {
        *sram = 0;
        *sram_start = 0;
    }

    dev = mem_get_vdevice_by_name( core->mem, "EEProm" );
    if (dev)
        *eeprom = eeprom_get_size( (EEProm *)dev );
    else
        *eeprom = 0;
}

/** \brief Attach a virtual device into the Memory. */
void avr_core_attach_vdev( AvrCore *core, VDevice *dev )
{
    vdev_set_core( dev, (AvrClass *)core );
    mem_attach( core->mem, dev );
}

/** \brief FIXME: write me. */
VDevice *avr_core_get_vdev_by_name( AvrCore *core, char *name )
{
    return mem_get_vdevice_by_name( core->mem, name );
}

/** \brief FIXME: write me. */
VDevice *avr_core_get_vdev_by_addr( AvrCore *core, int addr )
{
    return mem_get_vdevice_by_addr( core->mem, addr );
}

/** \brief FIXME: write me. */
void avr_core_set_state( AvrCore *core, StateType state )
{
    core->state = state;
}

/** \brief FIXME: write me. */
int avr_core_get_state( AvrCore *core )
{
    return core->state;
}

/** \brief FIXME: write me. */
void avr_core_set_sleep_mode( AvrCore *core, int sleep_mode )
{
    core->state = STATE_SLEEP;
    core->sleep_mode = ((unsigned int)1 << sleep_mode);
}

/** \brief FIXME: write me. */
int avr_core_get_sleep_mode( AvrCore *core )
{
    return core->sleep_mode;
}

/**
 * \brief Program Memory Space Access Methods
 */
word avr_core_flash_read( AvrCore *core, int addr )
{
    return flash_read( core->flash, addr );
}

/** \brief FIXME: write me. */
void avr_core_flash_write( AvrCore *core, int addr, word val )
{
    flash_write( core->flash, addr, val );
}

/** \brief FIXME: write me. */
void avr_core_flash_write_lo8( AvrCore *core, int addr, byte val )
{
    flash_write_lo8( core->flash, addr, val );
}

/** \brief FIXME: write me. */
void avr_core_flash_write_hi8( AvrCore *core, int addr, byte val )
{
    flash_write_hi8( core->flash, addr, val );
}

/**
 * \brief Data Memory Space Access Methods
 */
byte avr_core_mem_read( AvrCore *core, int addr )
{
    return mem_read( core->mem, addr );
}

/** \brief FIXME: write me. */
void avr_core_mem_write( AvrCore *core, int addr, byte val )
{
    mem_write( core->mem, addr, val );
}

/**
 * \brief Status Register Access Methods
 */
byte avr_core_sreg_get( AvrCore *core )
{
    return sreg_get( core->sreg );
}

/** \brief FIXME: write me. */
void avr_core_sreg_set( AvrCore *core, byte v )
{
    sreg_set( core->sreg, v );
}

/** \brief FIXME: write me. */
int  avr_core_sreg_get_bit( AvrCore *core, int b )
{
    return sreg_get_bit( core->sreg, b );
}

/** \brief FIXME: write me. */
void avr_core_sreg_set_bit( AvrCore *core, int b, int v )
{
    sreg_set_bit( core->sreg, b, v );
}


/**
 * \brief General Purpose Working Register Access Methods
 */
byte avr_core_gpwr_get ( AvrCore *core, int reg )
{
    return gpwr_get( core->gpwr, reg );
}

/** \brief FIXME: write me. */
void avr_core_gpwr_set ( AvrCore *core, int reg, byte val )
{
    gpwr_set( core->gpwr, reg, val );
}

/**
 * \brief Direct I/O Register Access Methods
 *
 * IO Registers are mapped in memory directly after the 32 (0x20)
 * general registers.
 */

void avr_core_io_display_names( AvrCore *core )
{
    int i;
    byte val;
    char name[80];

    for ( i=IO_REG_ADDR_BEGIN; i<IO_REG_ADDR_END; i++ )
    {
        mem_io_fetch( core->mem, i, &val, name, sizeof(name)-1 );
        display_io_reg_name( i-IO_REG_ADDR_BEGIN, name );
    }
}

/** \brief FIXME: write me. */
byte avr_core_io_read( AvrCore *core, int reg )
{
    return avr_core_mem_read( core, reg + IO_REG_ADDR_BEGIN );
}

/** \brief FIXME: write me. */
void avr_core_io_write( AvrCore *core, int reg, byte val )
{
    avr_core_mem_write( core, reg + IO_REG_ADDR_BEGIN, val );
}

/** \brief Read an io register into val and put the name of the register into 
buf. */

void avr_core_io_fetch( AvrCore *core, int reg, byte *val, char *buf, int 
bufsiz )
{
    mem_io_fetch( core->mem, reg, val, buf, bufsiz );
}

/** \brief Pop 1-4 bytes off of the stack.
 *
 * See stack_pop() for more details.
 */
dword avr_core_stack_pop( AvrCore *core, int bytes )
{
    return stack_pop( core->stack, bytes );
}

/** \brief Push 1-4 bytes onto the stack.
 *
 * See stack_push() for more details.
 */
void avr_core_stack_push( AvrCore *core, int bytes, dword val )
{
    stack_push( core->stack, bytes, val );
}

/* Private
 
   Deal with PC reach-arounds.

   It's possible to set/incrment the program counter with large negative
   values which go past zero. These should be interpreted as wrapping back
   around the last address in the flash. */

static void adjust_PC_to_max( AvrCore *core )
{
    if (core->PC < 0)
        core->PC = core->PC_max + core->PC;

    if (core->PC >= core->PC_max)
        core->PC -= core->PC_max;
}

/** \brief Returns the size of the Program Counter in bytes.
 *
 * Most devices have a 16-bit PC (2 bytes), but some larger ones
 * (e.g. mega128), have a 22-bit PC (3 bytes).
 */
dword avr_core_PC_size( AvrCore *core )           { return core->PC_size; }

/** \brief Returns the maximum value of the Program Counter.
 *
 * This is flash_size / 2.
 */
dword avr_core_PC_max ( AvrCore *core )           { return core->PC_max; }

/** \brief Return the current of the Program Counter. */
dword  avr_core_PC_get( AvrCore *core )           { return core->PC;      }

/** \brief Set the Program Counter to val.
 *
 * If val is not in the valid range of PC values, it is adjusted to fall in
 * the valid range.
 */
void avr_core_PC_set( AvrCore *core, dword val )
{
    core->PC = val;
    adjust_PC_to_max( core );
    display_pc( core->PC );
}

/** \brief Increment the Program Counter by val.
 *
 * val can be either positive or negative.
 *
 * If the result of the incrememt is outside the valid range for PC, it is
 * adjusted to fall in the valid range. This allows addresses to wrap around
 * the end of the insn space.
 */
void avr_core_PC_incr( AvrCore *core, int val )
{
    core->PC += val;
    adjust_PC_to_max( core );
    display_pc( core->PC );
}

/**
 * \brief Methods for accessing CK and instruction Clocks.
 */
qword avr_core_CK_get ( AvrCore *core )         { return core->CK;       }

/** \brief FIXME: write me. */
void  avr_core_CK_incr( AvrCore *core )
{
  core->CK++;

  /* Send clock cycles to display */
  display_clock( core->CK );
}

/** \brief FIXME: write me. */
int   avr_core_inst_CKS_get( AvrCore *core )          { return core->inst_CKS; }

/** \brief FIXME: write me. */
void  avr_core_inst_CKS_set( AvrCore *core, int val ) { core->inst_CKS = val;  }

/**
 * \brief Interrupt Access Methods.
 */
IntVect *avr_core_irq_get_pending( AvrCore *core )
{
    return irq_get_pending_vector( core->irq_pending, core->state, 
core->sleep_mode );
}

/** \brief FIXME: write me. */
void avr_core_irq_raise( AvrCore *core, int irq )
{
    core->irq_pending = irq_list_add( core->irq_pending, &core->irq_vtable[irq] 
);
}

/** \brief FIXME: write me. */
void avr_core_irq_clear( AvrCore *core, IntVect *irq )
{
    core->irq_pending = irq_list_delete( core->irq_pending, irq );
}


/** \brief FIXME: write me. */
void avr_core_irq_clear_all( AvrCore *core )
{
    dlist_delete_all( core->irq_pending );
    core->irq_pending = NULL;
}

/**
 * \brief Break point access methods.
 */
void avr_core_set_breakpoint_opcode( AvrCore *core, word opcode )
{
    core->opcode_break_pt = opcode;
}

/** \brief FIXME: write me. */
void avr_core_insert_breakpoint( AvrCore *core, int pc )
{
    core->breakpoint = brk_pt_list_add( core->breakpoint, pc );
}

/** \brief FIXME: write me. */
void avr_core_remove_breakpoint( AvrCore *core, int pc )
{
    core->breakpoint = brk_pt_list_delete( core->breakpoint, pc );
}

/** \brief FIXME: write me. */
int avr_core_is_breakpoint( AvrCore *core, int pc )
{
    return (int)brk_pt_list_lookup(core->breakpoint, pc);
}

/* Private
  
   Execute an instruction.
   Presets the number of instruction clocks to zero so that break points and
   invalid opcodes don't add extraneous clock counts.
   
   Also checks for software breakpoints. 
   Any opcode except 0xffff can be a breakpoint.
   
   Returns INVALID_OPCODE, BREAK_POINT, or >= 0. */

static int exec_next_instruction( AvrCore *core )
{
    int       result, pc;
    word      opcode;
    Opcode_FP op_handler;

    pc = avr_core_PC_get( core );
    opcode = flash_read( core->flash, pc );

    /* Preset the number of instruction clocks to zero so that break points and
       invalid opcodes don't add extraneous clock counts. */
    avr_core_inst_CKS_set( core, 0 );

    /* Any opcode except 0xffff can be a break point. */
    if ( (opcode == core->opcode_break_pt) && (opcode != 0xffff) )
        return BREAK_POINT;

    /* Check if a software breakpoint has been hit */
    if ( avr_core_is_breakpoint(core, pc) )
        return BREAK_POINT;

    op_handler = decode_opcode( opcode );

    if (op_handler == NULL)
        return INVALID_OPCODE;

    result = op_handler( core, opcode );

    if (global_debug_inst_output)
        fprintf( stderr, "0x%06x (0x%06x) : 0x%04x : %s\n", 
                 pc, pc*2, opcode, global_opcode_name[result] );

    return result;
}

/*
 * Private
 *
 * Checks to see if an interrupt is pending. If any are pending, and
 * if SREG(I) is set, the following will occur:
 *   - push current PC onto stack
 *   - PC <- interrupt vector
 *   - I flag of SREG is cleared
 *
 * Reset vector is not controlled by the SREG(I) flag, thus if reset
 * interrupt has occurred, the device will be reset irregardless of state
 * of SREG(I).
 */

static void avr_core_check_interrupts( AvrCore *core )
{
    IntVect *irq;

    if (core->irq_pending)
    {
        irq = avr_core_irq_get_pending( core );

        if (irq)
        {
            if (irq->name == NULL)
            {
                avr_error( "Raised and invalid irq for device");
            }

            if (irq->addr == IRQ_RESET)
            {
                /* The global interrupt (SREG.I) never blocks a reset. We
                   don't need to clear the irq since a reset clears all
                   pending irq's. */
                avr_core_reset( core );
            }

            if (avr_core_sreg_get_bit(core, SREG_I))
            {
                int pc       = avr_core_PC_get( core );
                int pc_bytes = avr_core_PC_size( core );

                avr_core_stack_push( core, pc_bytes, pc );
                avr_core_sreg_set_bit( core, SREG_I, 0 );

                avr_core_PC_set( core, irq->addr+core->irq_offset );

                avr_core_irq_clear( core, irq );
            }
        }
    }
}

/**
 * \brief Process a single program instruction, all side effects and
 * peripheral stimulii.
 *
 * Executes instructions, calls callbacks, and checks for interrupts.  */

int avr_core_step( AvrCore *core )
{
    int  res = 0;
    int  state;

    /* The MCU is stopped when in one of the many sleep modes */
    state = avr_core_get_state(core);
    if (state != STATE_SLEEP)
    {
        /* execute an instruction; may change state */
        res = exec_next_instruction( core );
    }

    /* Execute the clock callbacks */
    while ( core->inst_CKS > 0 )
    {
        /* propagate clocks here */
        avr_core_clk_cb_exec( core );

        avr_core_CK_incr( core );

        core->inst_CKS--;
    }

    /*
     * FIXME: async cb's and interrupt checking might need to be put somewhere 
else.
     */

    /* Execute the asynchronous callbacks */
    avr_core_async_cb_exec( core );

    /* check interrupts here */
    avr_core_check_interrupts( core );

    return res;
}

/** \brief Start the processing of instructions by the simulator.
 *
 * The simulated device will run until one of the following occurs:
 *   - The state of the core is no longer STATE_RUNNING.
 *   - The simulator receives a SIGINT signal.
 *   - A breakpoint is reached (currently causes core to stop running).
 *   - An invalid opcode is encountered.
 *   - A fatal internal error occurs.
 */

void avr_core_run( AvrCore *core )
{
    int  cnt = 0;
    int  res;

    avr_core_reset( core );     /* make sure the device is in a sane state. */

    core->state = STATE_RUNNING;

    signal_watch_start(SIGINT);

    /* FIXME: [TRoth 2002/03/19] This loop isn't going to handle sleep or idle
       modes properly. */

    while ( core->state == STATE_RUNNING )
    {
        if ( signal_has_occurred(SIGINT) )
            break;

        res = avr_core_step(core);
        switch (res) {
            case INVALID_OPCODE:
                break;
            case BREAK_POINT:
                /* FIXME: do something here? */
                break;
            default:
                cnt++;          /* might want to store this in core itself */
        }
    }

    signal_watch_stop(SIGINT);

    avr_message( "Executed %d instructions.\n", cnt );
    avr_message( "Executed %d clock cycles.\n", avr_core_CK_get(core) );
}


/** \brief Sets the simulated CPU back to its initial state.
 *
 *  Zeroes out PC, IRQ's, clock, and memory.
 */

void avr_core_reset( AvrCore *core )
{
    avr_core_PC_set( core, 0 );
    avr_core_irq_clear_all( core );

    core->CK = 0;
    avr_core_inst_CKS_set( core, 0 );

    /* Send clock cycles to display */
    /* Normaly the clockcycles must not be reset here! */
    /* This leads to an error in the vcd file */
    display_clock( core->CK );

    mem_reset( core->mem );
}

/**
 * \brief For adding external read and write callback functions.
 *
 * rd and wr should come in pairs, but it is safe to add
 * empty function via passing a NULL pointer for either function.
 *
 * \param core A pointer to an AvrCore object.
 *
 * \param port_id The ID for handling the simulavr inheritance model.
 *
 * \param ext_rd Function for the device core to call when it needs to
 * communicate with the external world via I/O Ports.
 *
 * \param ext_wr Function for the device core to call when it needs to
 * communicate with the external world via I/O Ports.
 *
 */
void avr_core_add_ext_rd_wr( AvrCore *core, int port_id,
                             PortFP_ExtRd ext_rd, PortFP_ExtWr ext_wr )
{
    Port *p = (Port *)mem_get_vdevice_by_name( core->mem, name_PORT[port_id] );

    if (p == NULL)
    {
        avr_warning( "Device does not have %s.\n", name_PORT[port_id] );
        return;
    }

    port_add_ext_rd_wr( p, ext_rd, ext_wr );
}

/**
 * \brief Add a new clock callback to list.
 */
void avr_core_clk_cb_add( AvrCore *core, CallBack *new )
{
    core->clk_cb = callback_list_add( core->clk_cb, new );
}

/**
 * \brief Add a new asynchronous callback to list.
 */
void avr_core_async_cb_add( AvrCore *core, CallBack *new )
{
    core->async_cb = callback_list_add( core->async_cb, new );
}

/** \brief Run all the callbacks in the list.
 
    If a callback returns non-zero (true), then it is done with it's job and
    wishes to be removed from the list.
 
    The time argument has dual meaning. If the callback list is for the clock
    callbacks, time is the value of the CK clock counter. If the callback list
    is for the asynchronous callback, time is the number of milliseconds from
    some unknown, arbitrary time on the host system. */

void avr_core_clk_cb_exec( AvrCore *core )
{
    core->clk_cb = callback_list_execute_all( core->clk_cb, 
avr_core_CK_get(core) );
}

/**
 * \brief Run all the asynchronous callbacks.
 */
void avr_core_async_cb_exec( AvrCore *core )
{
    core->async_cb = callback_list_execute_all( core->async_cb, 
get_program_time() );
}

/**
 * \brief Dump the contents of the entire CPU core.
 *
 * \param core A pointer to an AvrCore object.
 * \param An open file descriptor.
 */
void avr_core_dump_core( AvrCore *core, FILE *f_core )
{
    unsigned int pc = avr_core_PC_get(core);

    fprintf( f_core, "PC = 0x%06x (PC*2 = 0x%06x)\n\n", pc, pc*2 );
    mem_dump_core( core->mem, f_core );
    flash_dump_core( core->flash, f_core );
}


/**
 * \brief Load a program from an input file.
 */
int avr_core_load_program( AvrCore *core, char *file, int format )
{
    return flash_load_from_file( core->flash, file, format );
}

#if 0
/*
 * Load a program from an input file.
 */
int avr_core_load_eeprom( AvrCore *core, char *file, int format )
{
    return eeprom_load_from_file( core, file, format );
}
#endif

reply via email to

[Prev in Thread] Current Thread [Next in Thread]