/* generic.c -- Generic external peripheral
|
/* generic.c -- Generic external peripheral
|
|
|
Copyright (C) 2008 Embecosm Limited
|
Copyright (C) 2008 Embecosm Limited
|
|
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
|
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
|
|
This program is free software; you can redistribute it and/or modify it
|
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
|
under the terms of the GNU General Public License as published by the Free
|
Software Foundation; either version 3 of the License, or (at your option)
|
Software Foundation; either version 3 of the License, or (at your option)
|
any later version.
|
any later version.
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
more details.
|
more details.
|
|
|
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU General Public License along
|
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
/* This program is commented throughout in a fashion suitable for processing
|
/* This program is commented throughout in a fashion suitable for processing
|
with Doxygen. */
|
with Doxygen. */
|
|
|
|
|
/* This is functional simulation of any external peripheral. It's job is to
|
/* This is functional simulation of any external peripheral. It's job is to
|
* trap accesses in a specific range, so that the simulator can drive an
|
* trap accesses in a specific range, so that the simulator can drive an
|
* external device.
|
* external device.
|
*
|
*
|
* A note on endianess. All external communication is done using HOST
|
* A note on endianess. All external communication is done using HOST
|
* endianess. A set of functions are provided to convert between host and
|
* endianess. A set of functions are provided to convert between host and
|
* model endianess (htoml, htoms, mtohl, mtohs).
|
* model endianess (htoml, htoms, mtohl, mtohs).
|
*/
|
*/
|
|
|
/* Autoconf and/or portability configuration */
|
/* Autoconf and/or portability configuration */
|
#include "config.h"
|
#include "config.h"
|
|
|
/* System includes */
|
/* System includes */
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <stdio.h>
|
#include <stdio.h>
|
|
|
/* Package includes */
|
/* Package includes */
|
#include "arch.h"
|
#include "arch.h"
|
#include "sim-config.h"
|
#include "sim-config.h"
|
#include "abstract.h"
|
#include "abstract.h"
|
#include "toplevel-support.h"
|
#include "toplevel-support.h"
|
#include "sim-cmd.h"
|
#include "sim-cmd.h"
|
|
|
|
|
/*! State associated with the generic device. */
|
/*! State associated with the generic device. */
|
struct dev_generic
|
struct dev_generic
|
{
|
{
|
|
|
/* Info about a particular transaction */
|
/* Info about a particular transaction */
|
|
|
enum
|
enum
|
{ /* Direction of the access */
|
{ /* Direction of the access */
|
GENERIC_READ,
|
GENERIC_READ,
|
GENERIC_WRITE
|
GENERIC_WRITE
|
} trans_direction;
|
} trans_direction;
|
|
|
enum
|
enum
|
{ /* Size of the access */
|
{ /* Size of the access */
|
GENERIC_BYTE,
|
GENERIC_BYTE,
|
GENERIC_HW,
|
GENERIC_HW,
|
GENERIC_WORD
|
GENERIC_WORD
|
} trans_size;
|
} trans_size;
|
|
|
uint32_t value; /* The value to read/write */
|
uint32_t value; /* The value to read/write */
|
|
|
/* Configuration */
|
/* Configuration */
|
|
|
int enabled; /* Device enabled */
|
int enabled; /* Device enabled */
|
int byte_enabled; /* Byte R/W allowed */
|
int byte_enabled; /* Byte R/W allowed */
|
int hw_enabled; /* Half word R/W allowed */
|
int hw_enabled; /* Half word R/W allowed */
|
int word_enabled; /* Full word R/W allowed */
|
int word_enabled; /* Full word R/W allowed */
|
char *name; /* Name of the device */
|
char *name; /* Name of the device */
|
oraddr_t baseaddr; /* Base address of device */
|
oraddr_t baseaddr; /* Base address of device */
|
uint32_t size; /* Address space size (bytes) */
|
uint32_t size; /* Address space size (bytes) */
|
|
|
};
|
};
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Read a byte from an external device
|
/*!Read a byte from an external device
|
|
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
mask for the bytes we don't want.
|
mask for the bytes we don't want.
|
|
|
Since this is only a byte, the endianess of the result is irrelevant.
|
Since this is only a byte, the endianess of the result is irrelevant.
|
|
|
@note We are passed the device address, but we must convert it to a full
|
@note We are passed the device address, but we must convert it to a full
|
address for external use, to allow the single upcall handler to
|
address for external use, to allow the single upcall handler to
|
decode multiple generic devices.
|
decode multiple generic devices.
|
|
|
@param[in] addr The device address to read from (host endian).
|
@param[in] addr The device address to read from (host endian).
|
@param[in] dat The device data structure
|
@param[in] dat The device data structure
|
|
|
@return The byte read. */
|
@return The byte read. */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static uint8_t
|
static uint8_t
|
generic_read_byte (oraddr_t addr,
|
generic_read_byte (oraddr_t addr,
|
void *dat)
|
void *dat)
|
{
|
{
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
|
|
if (!config.ext.read_up)
|
if (!config.ext.read_up)
|
{
|
{
|
fprintf (stderr, "Byte read from disabled generic device\n");
|
fprintf (stderr, "Byte read from disabled generic device\n");
|
return 0;
|
return 0;
|
}
|
}
|
else if (addr >= dev->size)
|
else if (addr >= dev->size)
|
{
|
{
|
fprintf (stderr, "Byte read out of range for generic device %s "
|
fprintf (stderr, "Byte read out of range for generic device %s "
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
return 0;
|
return 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
int bytenum = fulladdr & 0x00000003;
|
int bytenum = fulladdr & 0x00000003;
|
|
|
unsigned char mask[4];
|
unsigned char mask[4];
|
unsigned char res[4];
|
unsigned char res[4];
|
|
|
/* Set the mask, read and get the result */
|
/* Set the mask, read and get the result */
|
memset (mask, 0, sizeof (mask));
|
memset (mask, 0, sizeof (mask));
|
mask[bytenum] = 0xff;
|
mask[bytenum] = 0xff;
|
|
|
if (0 != config.ext.read_up (config.ext.class_ptr, wordaddr, mask, res,
|
if (0 != config.ext.read_up (config.ext.class_ptr, wordaddr, mask, res,
|
4))
|
4))
|
{
|
{
|
fprintf (stderr, "Warning: external byte read failed.\n");
|
fprintf (stderr, "Warning: external byte read failed.\n");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
return res[bytenum];
|
return res[bytenum];
|
}
|
}
|
} /* generic_read_byte() */
|
} /* generic_read_byte() */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Write a byte to an external device
|
/*!Write a byte to an external device
|
|
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
mask for the bytes we don't want.
|
mask for the bytes we don't want.
|
|
|
Since this is only a byte, the endianess of the value is irrelevant.
|
Since this is only a byte, the endianess of the value is irrelevant.
|
|
|
@note We are passed the device address, but we must convert it to a full
|
@note We are passed the device address, but we must convert it to a full
|
address for external use, to allow the single upcall handler to
|
address for external use, to allow the single upcall handler to
|
decode multiple generic devices.
|
decode multiple generic devices.
|
|
|
@param[in] addr The device address to write to (host endian)
|
@param[in] addr The device address to write to (host endian)
|
@param[in] value The byte value to write
|
@param[in] value The byte value to write
|
@param[in] dat The device data structure */
|
@param[in] dat The device data structure */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static void
|
static void
|
generic_write_byte (oraddr_t addr,
|
generic_write_byte (oraddr_t addr,
|
uint8_t value,
|
uint8_t value,
|
void *dat)
|
void *dat)
|
{
|
{
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
|
|
if (!config.ext.write_up)
|
if (!config.ext.write_up)
|
{
|
{
|
fprintf (stderr, "Byte write to disabled generic device\n");
|
fprintf (stderr, "Byte write to disabled generic device\n");
|
}
|
}
|
else if (addr >= dev->size)
|
else if (addr >= dev->size)
|
{
|
{
|
fprintf (stderr, "Byte written out of range for generic device %s "
|
fprintf (stderr, "Byte written out of range for generic device %s "
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
}
|
}
|
else
|
else
|
{
|
{
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
int bytenum = fulladdr & 0x00000003;
|
int bytenum = fulladdr & 0x00000003;
|
|
|
unsigned char mask[4];
|
unsigned char mask[4];
|
unsigned char val[4];
|
unsigned char val[4];
|
|
|
/* Set the mask and write data do the write. */
|
/* Set the mask and write data do the write. */
|
memset (mask, 0, sizeof (mask));
|
memset (mask, 0, sizeof (mask));
|
mask[bytenum] = 0xff;
|
mask[bytenum] = 0xff;
|
val[bytenum] = value;
|
val[bytenum] = value;
|
|
|
if (0 != config.ext.write_up (config.ext.class_ptr, wordaddr, mask, val,
|
if (0 != config.ext.write_up (config.ext.class_ptr, wordaddr, mask, val,
|
4))
|
4))
|
{
|
{
|
fprintf (stderr, "Warning: external byte write failed.\n");
|
fprintf (stderr, "Warning: external byte write failed.\n");
|
}
|
}
|
}
|
}
|
} /* generic_write_byte() */
|
} /* generic_write_byte() */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Read a half word from an external device
|
/*!Read a half word from an external device
|
|
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
mask for the bytes we don't want.
|
mask for the bytes we don't want.
|
|
|
Since this is a half word, the result must be converted to host endianess.
|
Since this is a half word, the result must be converted to host endianess.
|
|
|
@note We are passed the device address, but we must convert it to a full
|
@note We are passed the device address, but we must convert it to a full
|
address for external use, to allow the single upcall handler to
|
address for external use, to allow the single upcall handler to
|
decode multiple generic devices.
|
decode multiple generic devices.
|
|
|
@param[in] addr The device address to read from (host endian).
|
@param[in] addr The device address to read from (host endian).
|
@param[in] dat The device data structure.
|
@param[in] dat The device data structure.
|
|
|
@return The half word read (host endian). */
|
@return The half word read (host endian). */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static uint16_t
|
static uint16_t
|
generic_read_hw (oraddr_t addr,
|
generic_read_hw (oraddr_t addr,
|
void *dat)
|
void *dat)
|
{
|
{
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
|
|
if (!config.ext.read_up)
|
if (!config.ext.read_up)
|
{
|
{
|
fprintf (stderr, "Half word read from disabled generic device\n");
|
fprintf (stderr, "Half word read from disabled generic device\n");
|
return 0;
|
return 0;
|
}
|
}
|
else if (addr >= dev->size)
|
else if (addr >= dev->size)
|
{
|
{
|
fprintf (stderr, "Half-word read out of range for generic device %s "
|
fprintf (stderr, "Half-word read out of range for generic device %s "
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
return 0;
|
return 0;
|
}
|
}
|
else if (addr & 0x1)
|
else if (addr & 0x1)
|
{
|
{
|
/* This should be trapped elsewhere - here for safety. */
|
/* This should be trapped elsewhere - here for safety. */
|
fprintf (stderr,
|
fprintf (stderr,
|
"Unaligned half word read from 0x%" PRIxADDR " ignored\n",
|
"Unaligned half word read from 0x%" PRIxADDR " ignored\n",
|
addr);
|
addr);
|
return 0;
|
return 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
int hwnum = fulladdr & 0x00000002;
|
int hwnum = fulladdr & 0x00000002;
|
|
|
unsigned char mask[4];
|
unsigned char mask[4];
|
unsigned char res[4];
|
unsigned char res[4];
|
|
|
/* Set the mask, read and get the result */
|
/* Set the mask, read and get the result */
|
memset (mask, 0, sizeof (mask));
|
memset (mask, 0, sizeof (mask));
|
mask[hwnum ] = 0xff;
|
mask[hwnum ] = 0xff;
|
mask[hwnum + 1] = 0xff;
|
mask[hwnum + 1] = 0xff;
|
|
|
if (0 != config.ext.read_up (config.ext.class_ptr, wordaddr, mask, res,
|
if (0 != config.ext.read_up (config.ext.class_ptr, wordaddr, mask, res,
|
4))
|
4))
|
{
|
{
|
fprintf (stderr, "Warning: external half word read failed.\n");
|
fprintf (stderr, "Warning: external half word read failed.\n");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Result converted according to endianess */
|
/* Result converted according to endianess */
|
#ifdef OR32_BIG_ENDIAN
|
#ifdef OR32_BIG_ENDIAN
|
return (unsigned short int) res[hwnum ] << 8 |
|
return (unsigned short int) res[hwnum ] << 8 |
|
(unsigned short int) res[hwnum + 1];
|
(unsigned short int) res[hwnum + 1];
|
#else
|
#else
|
return (unsigned short int) res[hwnum + 1] << 8 |
|
return (unsigned short int) res[hwnum + 1] << 8 |
|
(unsigned short int) res[hwnum ];
|
(unsigned short int) res[hwnum ];
|
#endif
|
#endif
|
}
|
}
|
} /* generic_read_hw() */
|
} /* generic_read_hw() */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Write a half word to an external device
|
/*!Write a half word to an external device
|
|
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
To model Wishbone accurately, we always do this as a 4-byte access, with a
|
mask for the bytes we don't want.
|
mask for the bytes we don't want.
|
|
|
Since this is a half word, the value must be converted from host endianess.
|
Since this is a half word, the value must be converted from host endianess.
|
|
|
@note We are passed the device address, but we must convert it to a full
|
@note We are passed the device address, but we must convert it to a full
|
address for external use, to allow the single upcall handler to
|
address for external use, to allow the single upcall handler to
|
decode multiple generic devices.
|
decode multiple generic devices.
|
|
|
@param[in] addr The device address to write to (host endian).
|
@param[in] addr The device address to write to (host endian).
|
@param[in] value The half word value to write (model endian).
|
@param[in] value The half word value to write (model endian).
|
@param[in] dat The device data structure. */
|
@param[in] dat The device data structure. */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static void
|
static void
|
generic_write_hw (oraddr_t addr,
|
generic_write_hw (oraddr_t addr,
|
uint16_t value,
|
uint16_t value,
|
void *dat)
|
void *dat)
|
{
|
{
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
|
|
if (!config.ext.write_up)
|
if (!config.ext.write_up)
|
{
|
{
|
fprintf (stderr, "Half word write to disabled generic device\n");
|
fprintf (stderr, "Half word write to disabled generic device\n");
|
}
|
}
|
else if (addr >= dev->size)
|
else if (addr >= dev->size)
|
{
|
{
|
fprintf (stderr, "Half-word written out of range for generic device %s "
|
fprintf (stderr, "Half-word written out of range for generic device %s "
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
}
|
}
|
else if (addr & 0x1)
|
else if (addr & 0x1)
|
{
|
{
|
fprintf (stderr,
|
fprintf (stderr,
|
"Unaligned half word write to 0x%" PRIxADDR " ignored\n", addr);
|
"Unaligned half word write to 0x%" PRIxADDR " ignored\n", addr);
|
}
|
}
|
else
|
else
|
{
|
{
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int fulladdr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
unsigned long int wordaddr = fulladdr & 0xfffffffc;
|
int hwnum = fulladdr & 0x00000002;
|
int hwnum = fulladdr & 0x00000002;
|
|
|
unsigned char mask[4];
|
unsigned char mask[4];
|
unsigned char val[4];
|
unsigned char val[4];
|
|
|
/* Set the mask and write data do the write. */
|
/* Set the mask and write data do the write. */
|
memset (mask, 0, sizeof (mask));
|
memset (mask, 0, sizeof (mask));
|
mask[hwnum ] = 0xff;
|
mask[hwnum ] = 0xff;
|
mask[hwnum + 1] = 0xff;
|
mask[hwnum + 1] = 0xff;
|
|
|
/* Value converted according to endianess */
|
/* Value converted according to endianess */
|
#ifdef OR32_BIG_ENDIAN
|
#ifdef OR32_BIG_ENDIAN
|
val[hwnum ] = (unsigned char) (value >> 8);
|
val[hwnum ] = (unsigned char) (value >> 8);
|
val[hwnum + 1] = (unsigned char) (value );
|
val[hwnum + 1] = (unsigned char) (value );
|
#else
|
#else
|
val[hwnum + 1] = (unsigned char) (value >> 8);
|
val[hwnum + 1] = (unsigned char) (value >> 8);
|
val[hwnum ] = (unsigned char) (value );
|
val[hwnum ] = (unsigned char) (value );
|
#endif
|
#endif
|
|
|
if (0 != config.ext.write_up (config.ext.class_ptr, wordaddr, mask, val,
|
if (0 != config.ext.write_up (config.ext.class_ptr, wordaddr, mask, val,
|
4))
|
4))
|
{
|
{
|
fprintf (stderr, "Warning: external half word write failed.\n");
|
fprintf (stderr, "Warning: external half word write failed.\n");
|
}
|
}
|
}
|
}
|
} /* generic_write_hw() */
|
} /* generic_write_hw() */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Read a full word from an external device
|
/*!Read a full word from an external device
|
|
|
Since this is a full word, the result must be converted to host endianess.
|
Since this is a full word, the result must be converted to host endianess.
|
|
|
@note We are passed the device address, but we must convert it to a full
|
@note We are passed the device address, but we must convert it to a full
|
address for external use, to allow the single upcall handler to
|
address for external use, to allow the single upcall handler to
|
decode multiple generic devices.
|
decode multiple generic devices.
|
|
|
@param[in] addr The device address to read from (host endian).
|
@param[in] addr The device address to read from (host endian).
|
@param[in] dat The device data structure.
|
@param[in] dat The device data structure.
|
|
|
@return The full word read (host endian). */
|
@return The full word read (host endian). */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static uint32_t
|
static uint32_t
|
generic_read_word (oraddr_t addr,
|
generic_read_word (oraddr_t addr,
|
void *dat)
|
void *dat)
|
{
|
{
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
|
|
if (!config.ext.read_up)
|
if (!config.ext.read_up)
|
{
|
{
|
fprintf (stderr, "Full word read from disabled generic device\n");
|
fprintf (stderr, "Full word read from disabled generic device\n");
|
return 0;
|
return 0;
|
}
|
}
|
else if (addr >= dev->size)
|
else if (addr >= dev->size)
|
{
|
{
|
fprintf (stderr, "Full word read out of range for generic device %s "
|
fprintf (stderr, "Full word read out of range for generic device %s "
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
return 0;
|
return 0;
|
}
|
}
|
else if (0 != (addr & 0x3))
|
else if (0 != (addr & 0x3))
|
{
|
{
|
fprintf (stderr,
|
fprintf (stderr,
|
"Unaligned full word read from 0x%" PRIxADDR " ignored\n",
|
"Unaligned full word read from 0x%" PRIxADDR " ignored\n",
|
addr);
|
addr);
|
return 0;
|
return 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
unsigned long int wordaddr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int wordaddr = (unsigned long int) (addr + dev->baseaddr);
|
|
|
unsigned char mask[4];
|
unsigned char mask[4];
|
unsigned char res[4];
|
unsigned char res[4];
|
|
|
/* Set the mask, read and get the result */
|
/* Set the mask, read and get the result */
|
memset (mask, 0xff, sizeof (mask));
|
memset (mask, 0xff, sizeof (mask));
|
|
|
if (0 != config.ext.read_up (config.ext.class_ptr, wordaddr, mask, res,
|
if (0 != config.ext.read_up (config.ext.class_ptr, wordaddr, mask, res,
|
4))
|
4))
|
{
|
{
|
fprintf (stderr, "Warning: external full word read failed.\n");
|
fprintf (stderr, "Warning: external full word read failed.\n");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Result converted according to endianess */
|
/* Result converted according to endianess */
|
#ifdef OR32_BIG_ENDIAN
|
#ifdef OR32_BIG_ENDIAN
|
return (unsigned long int) res[0] << 24 |
|
return (unsigned long int) res[0] << 24 |
|
(unsigned long int) res[1] << 16 |
|
(unsigned long int) res[1] << 16 |
|
(unsigned long int) res[2] << 8 |
|
(unsigned long int) res[2] << 8 |
|
(unsigned long int) res[3];
|
(unsigned long int) res[3];
|
#else
|
#else
|
return (unsigned long int) res[3] << 24 |
|
return (unsigned long int) res[3] << 24 |
|
(unsigned long int) res[2] << 16 |
|
(unsigned long int) res[2] << 16 |
|
(unsigned long int) res[1] << 8 |
|
(unsigned long int) res[1] << 8 |
|
(unsigned long int) res[0];
|
(unsigned long int) res[0];
|
#endif
|
#endif
|
}
|
}
|
} /* generic_read_word() */
|
} /* generic_read_word() */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Write a full word to an external device
|
/*!Write a full word to an external device
|
|
|
Since this is a half word, the value must be converted from host endianess.
|
Since this is a half word, the value must be converted from host endianess.
|
|
|
@note We are passed the device address, but we must convert it to a full
|
@note We are passed the device address, but we must convert it to a full
|
address for external use, to allow the single upcall handler to
|
address for external use, to allow the single upcall handler to
|
decode multiple generic devices.
|
decode multiple generic devices.
|
|
|
@param[in] addr The device address to write to (host endian).
|
@param[in] addr The device address to write to (host endian).
|
@param[in] value The full word value to write (host endian).
|
@param[in] value The full word value to write (host endian).
|
@param[in] dat The device data structure. */
|
@param[in] dat The device data structure. */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static void
|
static void
|
generic_write_word (oraddr_t addr,
|
generic_write_word (oraddr_t addr,
|
uint32_t value,
|
uint32_t value,
|
void *dat)
|
void *dat)
|
{
|
{
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
|
|
if (!config.ext.write_up)
|
if (!config.ext.write_up)
|
{
|
{
|
fprintf (stderr, "Full word write to disabled generic device\n");
|
fprintf (stderr, "Full word write to disabled generic device\n");
|
}
|
}
|
else if (addr >= dev->size)
|
else if (addr >= dev->size)
|
{
|
{
|
fprintf (stderr, "Full word written out of range for generic device %s "
|
fprintf (stderr, "Full word written out of range for generic device %s "
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
"(addr %" PRIxADDR ")\n", dev->name, addr);
|
}
|
}
|
else if (0 != (addr & 0x3))
|
else if (0 != (addr & 0x3))
|
{
|
{
|
fprintf (stderr,
|
fprintf (stderr,
|
"Unaligned full word write to 0x%" PRIxADDR " ignored\n", addr);
|
"Unaligned full word write to 0x%" PRIxADDR " ignored\n", addr);
|
}
|
}
|
else
|
else
|
{
|
{
|
unsigned long int wordaddr = (unsigned long int) (addr + dev->baseaddr);
|
unsigned long int wordaddr = (unsigned long int) (addr + dev->baseaddr);
|
|
|
unsigned char mask[4];
|
unsigned char mask[4];
|
unsigned char val[4];
|
unsigned char val[4];
|
|
|
/* Set the mask and write data do the write. */
|
/* Set the mask and write data do the write. */
|
memset (mask, 0xff, sizeof (mask));
|
memset (mask, 0xff, sizeof (mask));
|
|
|
/* Value converted according to endianess */
|
/* Value converted according to endianess */
|
#ifdef OR32_BIG_ENDIAN
|
#ifdef OR32_BIG_ENDIAN
|
val[0] = (unsigned char) (value >> 24);
|
val[0] = (unsigned char) (value >> 24);
|
val[1] = (unsigned char) (value >> 16);
|
val[1] = (unsigned char) (value >> 16);
|
val[2] = (unsigned char) (value >> 8);
|
val[2] = (unsigned char) (value >> 8);
|
val[3] = (unsigned char) (value );
|
val[3] = (unsigned char) (value );
|
#else
|
#else
|
val[3] = (unsigned char) (value >> 24);
|
val[3] = (unsigned char) (value >> 24);
|
val[2] = (unsigned char) (value >> 16);
|
val[2] = (unsigned char) (value >> 16);
|
val[1] = (unsigned char) (value >> 8);
|
val[1] = (unsigned char) (value >> 8);
|
val[0] = (unsigned char) (value );
|
val[0] = (unsigned char) (value );
|
#endif
|
#endif
|
|
|
if (0 != config.ext.write_up (config.ext.class_ptr, wordaddr, mask, val,
|
if (0 != config.ext.write_up (config.ext.class_ptr, wordaddr, mask, val,
|
4))
|
4))
|
{
|
{
|
fprintf (stderr, "Warning: external full word write failed.\n");
|
fprintf (stderr, "Warning: external full word write failed.\n");
|
}
|
}
|
}
|
}
|
} /* generic_write_word() */
|
} /* generic_write_word() */
|
|
|
|
|
/* Reset is a null operation */
|
/* Reset is a null operation */
|
|
|
static void
|
static void
|
generic_reset (void *dat)
|
generic_reset (void *dat)
|
{
|
{
|
return;
|
return;
|
|
|
} /* generic_reset() */
|
} /* generic_reset() */
|
|
|
|
|
/* Status report can only advise of configuration. */
|
/* Status report can only advise of configuration. */
|
|
|
static void
|
static void
|
generic_status (void *dat)
|
generic_status (void *dat)
|
{
|
{
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
struct dev_generic *dev = (struct dev_generic *) dat;
|
|
|
PRINTF ("\nGeneric device \"%s\" at 0x%" PRIxADDR ":\n", dev->name,
|
PRINTF ("\nGeneric device \"%s\" at 0x%" PRIxADDR ":\n", dev->name,
|
dev->baseaddr);
|
dev->baseaddr);
|
PRINTF (" Size 0x%" PRIx32 "\n", dev->size);
|
PRINTF (" Size 0x%" PRIx32 "\n", dev->size);
|
|
|
if (dev->byte_enabled)
|
if (dev->byte_enabled)
|
{
|
{
|
PRINTF (" Byte R/W enabled\n");
|
PRINTF (" Byte R/W enabled\n");
|
}
|
}
|
|
|
if (dev->hw_enabled)
|
if (dev->hw_enabled)
|
{
|
{
|
PRINTF (" Half word R/W enabled\n");
|
PRINTF (" Half word R/W enabled\n");
|
}
|
}
|
|
|
if (dev->word_enabled)
|
if (dev->word_enabled)
|
{
|
{
|
PRINTF (" Full word R/W enabled\n");
|
PRINTF (" Full word R/W enabled\n");
|
}
|
}
|
|
|
PRINTF ("\n");
|
PRINTF ("\n");
|
|
|
} /* generic_status() */
|
} /* generic_status() */
|
|
|
|
|
/* Functions to set configuration */
|
/* Functions to set configuration */
|
|
|
static void
|
static void
|
generic_enabled (union param_val val, void *dat)
|
generic_enabled (union param_val val, void *dat)
|
{
|
{
|
((struct dev_generic *) dat)->enabled = val.int_val;
|
((struct dev_generic *) dat)->enabled = val.int_val;
|
|
|
} /* generic_enabled() */
|
} /* generic_enabled() */
|
|
|
|
|
static void
|
static void
|
generic_byte_enabled (union param_val val, void *dat)
|
generic_byte_enabled (union param_val val, void *dat)
|
{
|
{
|
((struct dev_generic *) dat)->byte_enabled = val.int_val;
|
((struct dev_generic *) dat)->byte_enabled = val.int_val;
|
|
|
} /* generic_byte_enabled() */
|
} /* generic_byte_enabled() */
|
|
|
|
|
static void
|
static void
|
generic_hw_enabled (union param_val val, void *dat)
|
generic_hw_enabled (union param_val val, void *dat)
|
{
|
{
|
((struct dev_generic *) dat)->hw_enabled = val.int_val;
|
((struct dev_generic *) dat)->hw_enabled = val.int_val;
|
|
|
} /* generic_hw_enabled() */
|
} /* generic_hw_enabled() */
|
|
|
|
|
static void
|
static void
|
generic_word_enabled (union param_val val, void *dat)
|
generic_word_enabled (union param_val val, void *dat)
|
{
|
{
|
((struct dev_generic *) dat)->word_enabled = val.int_val;
|
((struct dev_generic *) dat)->word_enabled = val.int_val;
|
|
|
} /* generic_word_enabled() */
|
} /* generic_word_enabled() */
|
|
|
|
|
static void
|
static void
|
generic_name (union param_val val, void *dat)
|
generic_name (union param_val val, void *dat)
|
{
|
{
|
((struct dev_generic *) dat)->name = strdup (val.str_val);
|
((struct dev_generic *) dat)->name = strdup (val.str_val);
|
|
|
if (!((struct dev_generic *) dat)->name)
|
if (!((struct dev_generic *) dat)->name)
|
{
|
{
|
fprintf (stderr, "Peripheral 16450: name \"%s\": Run out of memory\n",
|
fprintf (stderr, "Peripheral 16450: name \"%s\": Run out of memory\n",
|
val.str_val);
|
val.str_val);
|
exit (-1);
|
exit (-1);
|
}
|
}
|
} /* generic_name() */
|
} /* generic_name() */
|
|
|
|
|
static void
|
static void
|
generic_baseaddr (union param_val val, void *dat)
|
generic_baseaddr (union param_val val, void *dat)
|
{
|
{
|
((struct dev_generic *) dat)->baseaddr = val.addr_val;
|
((struct dev_generic *) dat)->baseaddr = val.addr_val;
|
|
|
} /* generic_baseaddr() */
|
} /* generic_baseaddr() */
|
|
|
|
|
static void
|
static void
|
generic_size (union param_val val, void *dat)
|
generic_size (union param_val val, void *dat)
|
{
|
{
|
((struct dev_generic *) dat)->size = val.int_val;
|
((struct dev_generic *) dat)->size = val.int_val;
|
|
|
} /* generic_size() */
|
} /* generic_size() */
|
|
|
|
|
/* Start of new generic section */
|
/* Start of new generic section */
|
|
|
static void *
|
static void *
|
generic_sec_start ()
|
generic_sec_start ()
|
{
|
{
|
struct dev_generic *new =
|
struct dev_generic *new =
|
(struct dev_generic *) malloc (sizeof (struct dev_generic));
|
(struct dev_generic *) malloc (sizeof (struct dev_generic));
|
|
|
if (0 == new)
|
if (0 == new)
|
{
|
{
|
fprintf (stderr, "Generic peripheral: Run out of memory\n");
|
fprintf (stderr, "Generic peripheral: Run out of memory\n");
|
exit (-1);
|
exit (-1);
|
}
|
}
|
|
|
/* Default names */
|
/* Default names */
|
|
|
new->enabled = 1;
|
new->enabled = 1;
|
new->byte_enabled = 1;
|
new->byte_enabled = 1;
|
new->hw_enabled = 1;
|
new->hw_enabled = 1;
|
new->word_enabled = 1;
|
new->word_enabled = 1;
|
new->name = "anonymous external peripheral";
|
new->name = "anonymous external peripheral";
|
new->baseaddr = 0;
|
new->baseaddr = 0;
|
new->size = 0;
|
new->size = 0;
|
|
|
return new;
|
return new;
|
|
|
} /* generic_sec_start() */
|
} /* generic_sec_start() */
|
|
|
|
|
/* End of new generic section */
|
/* End of new generic section */
|
|
|
static void
|
static void
|
generic_sec_end (void *dat)
|
generic_sec_end (void *dat)
|
{
|
{
|
struct dev_generic *generic = (struct dev_generic *) dat;
|
struct dev_generic *generic = (struct dev_generic *) dat;
|
struct mem_ops ops;
|
struct mem_ops ops;
|
|
|
/* Give up if not enabled, or if size is zero, or if no access size is
|
/* Give up if not enabled, or if size is zero, or if no access size is
|
enabled. */
|
enabled. */
|
|
|
if (!generic->enabled)
|
if (!generic->enabled)
|
{
|
{
|
free (dat);
|
free (dat);
|
return;
|
return;
|
}
|
}
|
|
|
if (0 == generic->size)
|
if (0 == generic->size)
|
{
|
{
|
fprintf (stderr, "Generic peripheral \"%s\" has size 0: ignoring",
|
fprintf (stderr, "Generic peripheral \"%s\" has size 0: ignoring",
|
generic->name);
|
generic->name);
|
free (dat);
|
free (dat);
|
return;
|
return;
|
}
|
}
|
|
|
if (!generic->byte_enabled &&
|
if (!generic->byte_enabled &&
|
!generic->hw_enabled && !generic->word_enabled)
|
!generic->hw_enabled && !generic->word_enabled)
|
{
|
{
|
fprintf (stderr, "Generic peripheral \"%s\" has no access: ignoring",
|
fprintf (stderr, "Generic peripheral \"%s\" has no access: ignoring",
|
generic->name);
|
generic->name);
|
free (dat);
|
free (dat);
|
return;
|
return;
|
}
|
}
|
|
|
/* Zero all the ops, then set the ones we care about. Read/write delays will
|
/* Zero all the ops, then set the ones we care about. Read/write delays will
|
* come from the peripheral if desired.
|
* come from the peripheral if desired.
|
*/
|
*/
|
|
|
memset (&ops, 0, sizeof (struct mem_ops));
|
memset (&ops, 0, sizeof (struct mem_ops));
|
|
|
if (generic->byte_enabled)
|
if (generic->byte_enabled)
|
{
|
{
|
ops.readfunc8 = generic_read_byte;
|
ops.readfunc8 = generic_read_byte;
|
ops.writefunc8 = generic_write_byte;
|
ops.writefunc8 = generic_write_byte;
|
ops.read_dat8 = dat;
|
ops.read_dat8 = dat;
|
ops.write_dat8 = dat;
|
ops.write_dat8 = dat;
|
}
|
}
|
|
|
if (generic->hw_enabled)
|
if (generic->hw_enabled)
|
{
|
{
|
ops.readfunc16 = generic_read_hw;
|
ops.readfunc16 = generic_read_hw;
|
ops.writefunc16 = generic_write_hw;
|
ops.writefunc16 = generic_write_hw;
|
ops.read_dat16 = dat;
|
ops.read_dat16 = dat;
|
ops.write_dat16 = dat;
|
ops.write_dat16 = dat;
|
}
|
}
|
|
|
if (generic->word_enabled)
|
if (generic->word_enabled)
|
{
|
{
|
ops.readfunc32 = generic_read_word;
|
ops.readfunc32 = generic_read_word;
|
ops.writefunc32 = generic_write_word;
|
ops.writefunc32 = generic_write_word;
|
ops.read_dat32 = dat;
|
ops.read_dat32 = dat;
|
ops.write_dat32 = dat;
|
ops.write_dat32 = dat;
|
}
|
}
|
|
|
/* Register everything */
|
/* Register everything */
|
|
|
reg_mem_area (generic->baseaddr, generic->size, 0, &ops);
|
reg_mem_area (generic->baseaddr, generic->size, 0, &ops);
|
|
|
reg_sim_reset (generic_reset, dat);
|
reg_sim_reset (generic_reset, dat);
|
reg_sim_stat (generic_status, dat);
|
reg_sim_stat (generic_status, dat);
|
|
|
} /* generic_sec_end() */
|
} /* generic_sec_end() */
|
|
|
|
|
/* Register a generic section. */
|
/* Register a generic section. */
|
|
|
void
|
void
|
reg_generic_sec (void)
|
reg_generic_sec (void)
|
{
|
{
|
struct config_section *sec = reg_config_sec ("generic",
|
struct config_section *sec = reg_config_sec ("generic",
|
generic_sec_start,
|
generic_sec_start,
|
generic_sec_end);
|
generic_sec_end);
|
|
|
reg_config_param (sec, "enabled", paramt_int, generic_enabled);
|
reg_config_param (sec, "enabled", PARAMT_INT, generic_enabled);
|
reg_config_param (sec, "byte_enabled", paramt_int, generic_byte_enabled);
|
reg_config_param (sec, "byte_enabled", PARAMT_INT, generic_byte_enabled);
|
reg_config_param (sec, "hw_enabled", paramt_int, generic_hw_enabled);
|
reg_config_param (sec, "hw_enabled", PARAMT_INT, generic_hw_enabled);
|
reg_config_param (sec, "word_enabled", paramt_int, generic_word_enabled);
|
reg_config_param (sec, "word_enabled", PARAMT_INT, generic_word_enabled);
|
reg_config_param (sec, "name", paramt_str, generic_name);
|
reg_config_param (sec, "name", PARAMT_STR, generic_name);
|
reg_config_param (sec, "baseaddr", paramt_addr, generic_baseaddr);
|
reg_config_param (sec, "baseaddr", PARAMT_ADDR, generic_baseaddr);
|
reg_config_param (sec, "size", paramt_int, generic_size);
|
reg_config_param (sec, "size", PARAMT_INT, generic_size);
|
|
|
} /* reg_generic_sec */
|
} /* reg_generic_sec */
|
|
|