#include "sim-main.h"
#include "hw-main.h"
#include "dv-sockser.h"
#include "sim-assert.h"
struct tx3904sio_fifo;
static void tx3904sio_tickle(struct hw*);
static int tx3904sio_fifo_nonempty(struct hw*, struct tx3904sio_fifo*);
static char tx3904sio_fifo_pop(struct hw*, struct tx3904sio_fifo*);
static void tx3904sio_fifo_push(struct hw*, struct tx3904sio_fifo*, char);
static void tx3904sio_fifo_reset(struct hw*, struct tx3904sio_fifo*);
static void tx3904sio_poll(struct hw*, void* data);
enum
{
SLCR_REG = 0,
SLSR_REG = 1,
SDICR_REG = 2,
SDISR_REG = 3,
SFCR_REG = 4,
SBGR_REG = 5,
TFIFO_REG = 8,
SFIFO_REG = 12,
};
enum
{
RESET_PORT,
INT_PORT,
};
static const struct hw_port_descriptor tx3904sio_ports[] =
{
{ "int", INT_PORT, 0, output_port, },
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
struct tx3904sio_fifo
{
int size, used;
unsigned_1 *buffer;
};
struct tx3904sio
{
address_word base_address;
enum {sio_tcp, sio_stdio} backend;
struct tx3904sio_fifo rx_fifo, tx_fifo;
unsigned_4 slcr;
#define SLCR_WR_MASK 0xe17f0000U
#define SLCR_SET_BYTE(c,o,b) ((c)->slcr = SLCR_WR_MASK & (((c)->slcr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
unsigned_4 slsr;
#define SLSR_WR_MASK 0x00000000
unsigned_4 sdicr;
#define SDICR_WR_MASK 0x000f0000U
#define SDICR_SET_BYTE(c,o,b) ((c)->sdicr = SDICR_WR_MASK & (((c)->sdicr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
#define SDICR_GET_SDMAE(c) ((c)->sdicr & 0x00080000)
#define SDICR_GET_ERIE(c) ((c)->sdicr & 0x00040000)
#define SDICR_GET_TDIE(c) ((c)->sdicr & 0x00020000)
#define SDICR_GET_RDIE(c) ((c)->sdicr & 0x00010000)
unsigned_4 sdisr;
#define SDISR_WR_MASK 0x00070000U
#define SDISR_SET_BYTE(c,o,b) ((c)->sdisr = SDISR_WR_MASK & (((c)->sdisr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
#define SDISR_CLEAR_FLAG_BYTE(c,o,b) ((c)->sdisr = SDISR_WR_MASK & (((c)->sdisr & ~LSMASK32((o)*8+7,(o)*8)) & ((b)<< (o)*8)))
#define SDISR_GET_TDIS(c) ((c)->sdisr & 0x00020000)
#define SDISR_SET_TDIS(c) ((c)->sdisr |= 0x00020000)
#define SDISR_GET_RDIS(c) ((c)->sdisr & 0x00010000)
#define SDISR_SET_RDIS(c) ((c)->sdisr |= 0x00010000)
unsigned_4 sfcr;
#define SFCR_WR_MASK 0x001f0000U
#define SFCR_SET_BYTE(c,o,b) ((c)->sfcr = SFCR_WR_MASK & (((c)->sfcr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
#define SFCR_GET_TFRST(c) ((c)->sfcr & 0x00040000)
#define SFCR_GET_RFRST(c) ((c)->sfcr & 0x00020000)
#define SFCR_GET_FRSTE(c) ((c)->sfcr & 0x00010000)
unsigned_4 sbgr;
#define SBGR_WR_MASK 0x03ff0000U
#define SBGR_SET_BYTE(c,o,b) ((c)->sbgr = SBGR_WR_MASK & (((c)->sbgr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
struct hw_event* poll_event;
};
static hw_io_read_buffer_method tx3904sio_io_read_buffer;
static hw_io_write_buffer_method tx3904sio_io_write_buffer;
static hw_port_event_method tx3904sio_port_event;
static void
attach_tx3904sio_regs (struct hw *me,
struct tx3904sio *controller)
{
unsigned_word attach_address;
int attach_space;
unsigned attach_size;
reg_property_spec reg;
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
if (!hw_find_reg_array_property (me, "reg", 0, ®))
hw_abort (me, "\"reg\" property must contain one addr/size entry");
hw_unit_address_to_attach_address (hw_parent (me),
®.address,
&attach_space,
&attach_address,
me);
hw_unit_size_to_attach_size (hw_parent (me),
®.size,
&attach_size, me);
hw_attach_address (hw_parent (me), 0,
attach_space, attach_address, attach_size,
me);
if(hw_find_property(me, "backend") != NULL)
{
const char* value = hw_find_string_property(me, "backend");
if(! strcmp(value, "tcp"))
controller->backend = sio_tcp;
else if(! strcmp(value, "stdio"))
controller->backend = sio_stdio;
else
hw_abort(me, "illegal value for backend parameter `%s': use tcp or stdio", value);
}
controller->base_address = attach_address;
}
static void
tx3904sio_finish (struct hw *me)
{
struct tx3904sio *controller;
controller = HW_ZALLOC (me, struct tx3904sio);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, tx3904sio_io_read_buffer);
set_hw_io_write_buffer (me, tx3904sio_io_write_buffer);
set_hw_ports (me, tx3904sio_ports);
set_hw_port_event (me, tx3904sio_port_event);
controller->backend = sio_stdio;
attach_tx3904sio_regs (me, controller);
tx3904sio_fifo_reset(me, & controller->rx_fifo);
tx3904sio_fifo_reset(me, & controller->tx_fifo);
controller->slsr = controller->sdicr
= controller->sdisr = controller->sfcr
= controller->sbgr = 0;
controller->slcr = 0x40000000;
controller->sbgr = 0x03ff0000;
controller->poll_event = NULL;
}
static void
tx3904sio_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
struct tx3904sio *controller = hw_data (me);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "reset"));
tx3904sio_fifo_reset(me, & controller->rx_fifo);
tx3904sio_fifo_reset(me, & controller->tx_fifo);
controller->slsr = controller->sdicr
= controller->sdisr = controller->sfcr
= controller->sbgr = 0;
controller->slcr = 0x40000000;
controller->sbgr = 0x03ff0000;
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
static unsigned
tx3904sio_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct tx3904sio *controller = hw_data (me);
unsigned byte;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
tx3904sio_tickle(me);
for (byte = 0; byte < nr_bytes; byte++)
{
address_word address = base + byte;
int reg_number = (address - controller->base_address) / 4;
int reg_offset = (address - controller->base_address) % 4;
unsigned_4 register_value;
switch (reg_number)
{
case SLCR_REG: register_value = controller->slcr; break;
case SLSR_REG: register_value = controller->slsr; break;
case SDICR_REG: register_value = controller->sdicr; break;
case SDISR_REG: register_value = controller->sdisr; break;
case SFCR_REG: register_value = controller->sfcr; break;
case SBGR_REG: register_value = controller->sbgr; break;
case TFIFO_REG: register_value = 0; break;
case SFIFO_REG:
if(reg_offset == 0 && tx3904sio_fifo_nonempty(me, & controller->rx_fifo))
register_value = (tx3904sio_fifo_pop(me, & controller->rx_fifo) << 24);
else
register_value = 0;
break;
default: register_value = 0;
}
register_value = H2T_4(register_value);
memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
}
return nr_bytes;
}
static unsigned
tx3904sio_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct tx3904sio *controller = hw_data (me);
unsigned byte;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
for (byte = 0; byte < nr_bytes; byte++)
{
address_word address = base + byte;
unsigned_1 write_byte = ((const unsigned char*) source)[byte];
int reg_number = (address - controller->base_address) / 4;
int reg_offset = 3 - (address - controller->base_address) % 4;
switch (reg_number)
{
case SLCR_REG:
SLCR_SET_BYTE(controller, reg_offset, write_byte);
break;
case SLSR_REG: break;
case SDICR_REG:
{
unsigned_4 last_int, next_int;
last_int = controller->sdisr & controller->sdicr;
SDICR_SET_BYTE(controller, reg_offset, write_byte);
next_int = controller->sdisr & controller->sdicr;
if(SDICR_GET_SDMAE(controller))
hw_abort(me, "Cannot support DMA-driven sio.");
if(~last_int & next_int)
hw_port_event(me, INT_PORT, 1);
if(last_int & ~next_int)
hw_port_event(me, INT_PORT, 0);
}
break;
case SDISR_REG:
{
unsigned_4 last_int, next_int;
last_int = controller->sdisr & controller->sdicr;
SDISR_CLEAR_FLAG_BYTE(controller, reg_offset, write_byte);
next_int = controller->sdisr & controller->sdicr;
if(~last_int & next_int)
hw_port_event(me, INT_PORT, 1);
if(last_int & ~next_int)
hw_port_event(me, INT_PORT, 0);
}
break;
case SFCR_REG:
SFCR_SET_BYTE(controller, reg_offset, write_byte);
if(SFCR_GET_FRSTE(controller))
{
if(SFCR_GET_TFRST(controller)) tx3904sio_fifo_reset(me, & controller->tx_fifo);
if(SFCR_GET_RFRST(controller)) tx3904sio_fifo_reset(me, & controller->rx_fifo);
}
break;
case SBGR_REG:
SBGR_SET_BYTE(controller, reg_offset, write_byte);
break;
case SFIFO_REG: break;
case TFIFO_REG:
if(reg_offset == 3)
tx3904sio_fifo_push(me, & controller->tx_fifo, write_byte);
break;
default:
HW_TRACE ((me, "write to illegal register %d", reg_number));
}
}
tx3904sio_tickle(me);
return nr_bytes;
}
void
tx3904sio_tickle(struct hw *me)
{
struct tx3904sio* controller = hw_data(me);
int c;
char cc;
unsigned_4 last_int, next_int;
switch(controller->backend)
{
case sio_tcp:
while(tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
{
cc = tx3904sio_fifo_pop(me, & controller->tx_fifo);
dv_sockser_write(hw_system(me), cc);
HW_TRACE ((me, "tcp output: %02x", cc));
}
c = dv_sockser_read(hw_system(me));
while(c != -1)
{
cc = (char) c;
HW_TRACE ((me, "tcp input: %02x", cc));
tx3904sio_fifo_push(me, & controller->rx_fifo, cc);
c = dv_sockser_read(hw_system(me));
}
break;
case sio_stdio:
while(tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
{
cc = tx3904sio_fifo_pop(me, & controller->tx_fifo);
sim_io_write_stdout(hw_system(me), & cc, 1);
sim_io_flush_stdout(hw_system(me));
HW_TRACE ((me, "stdio output: %02x", cc));
}
c = sim_io_poll_read(hw_system(me), 0 , & cc, 1);
while(c == 1)
{
HW_TRACE ((me, "stdio input: %02x", cc));
tx3904sio_fifo_push(me, & controller->rx_fifo, cc);
c = sim_io_poll_read(hw_system(me), 0 , & cc, 1);
}
break;
default:
hw_abort(me, "Illegal backend mode: %d", controller->backend);
}
last_int = controller->sdisr & controller->sdicr;
if(tx3904sio_fifo_nonempty(me, & controller->rx_fifo))
SDISR_SET_RDIS(controller);
if(! tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
SDISR_SET_TDIS(controller);
next_int = controller->sdisr & controller->sdicr;
if(~last_int & next_int)
hw_port_event(me, INT_PORT, 1);
if(last_int & ~next_int)
hw_port_event(me, INT_PORT, 0);
if(controller->poll_event == NULL)
{
controller->poll_event = hw_event_queue_schedule (me, 1000,
tx3904sio_poll, NULL);
}
}
int
tx3904sio_fifo_nonempty(struct hw* me, struct tx3904sio_fifo* fifo)
{
return(fifo->used > 0);
}
char
tx3904sio_fifo_pop(struct hw* me, struct tx3904sio_fifo* fifo)
{
char it;
ASSERT(fifo->used > 0);
ASSERT(fifo->buffer != NULL);
it = fifo->buffer[0];
memcpy(& fifo->buffer[0], & fifo->buffer[1], fifo->used - 1);
fifo->used --;
return it;
}
void
tx3904sio_fifo_push(struct hw* me, struct tx3904sio_fifo* fifo, char it)
{
if(fifo->size == fifo->used)
{
int next_size = fifo->size * 2 + 16;
char* next_buf = zalloc(next_size);
memcpy(next_buf, fifo->buffer, fifo->used);
if(fifo->buffer != NULL) zfree(fifo->buffer);
fifo->buffer = next_buf;
fifo->size = next_size;
}
fifo->buffer[fifo->used] = it;
fifo->used ++;
}
void
tx3904sio_fifo_reset(struct hw* me, struct tx3904sio_fifo* fifo)
{
fifo->used = 0;
fifo->size = 0;
zfree(fifo->buffer);
fifo->buffer = 0;
}
void
tx3904sio_poll(struct hw* me, void* ignored)
{
struct tx3904sio* controller = hw_data (me);
tx3904sio_tickle (me);
hw_event_queue_deschedule (me, controller->poll_event);
controller->poll_event = hw_event_queue_schedule (me, 1000,
tx3904sio_poll, NULL);
}
const struct hw_descriptor dv_tx3904sio_descriptor[] = {
{ "tx3904sio", tx3904sio_finish, },
{ NULL },
};