* PKE testing was driven by SCEI "test0" bucket; code coverage remains
effectively full. The code is believed to be functionally complete now. Some code cleanup is included at no extra charge in this version. Fri Mar 13 20:21:57 1998 Frank Ch. Eigler <fche@cygnus.com> * sky-vu1.c: (dump_mem): Commented out function to satiate warning-ful compilation. * sky-pke.c: (pke_reset): New function, called explicitly at initialization and at FBRST. (pke_fifo_flush): New function to flush (skip over) existing quadwords in FIFO. (pke_fifo_fit): New function to add space for new quadword in FIFO. (pke_fifo_access): New function to absolute-index into FIFO. (pke_fifo_old): New function to remove old quadwords from FIFO. (pke_begin_interrupt_stall): New function to abstract interrupt-caused stalls. (pke_*): Access PKE FIFO only thorugh pke_fifo functions. (pke_pcrel_*): Renamed pke_pc_* functions. (pke_code_unpack): Numerous logic tweaks for latest UNPACK behavior changes & clarifications from SCEI. * sky-pke.h (struct pke_fifo): New explicit FIFO representation. (struct pke_device): Use struct above. (PKE_DEBUG): Removed macro as misnomer. * sky-hardware.c: Moved *_cmd_install declarations out. * sky-hardware.h: Moved *_cmd_install declarations in.
This commit is contained in:
parent
008a80d207
commit
9614fb3c36
3 changed files with 311 additions and 152 deletions
|
@ -1,10 +1,6 @@
|
|||
/* Copyright (C) 1998, Cygnus Solutions */
|
||||
|
||||
|
||||
/* Debugguing PKE? */
|
||||
#define PKE_DEBUG
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -32,17 +28,23 @@ static int pke_io_read_buffer(device*, void*, int, address_word,
|
|||
unsigned, sim_cpu*, sim_cia);
|
||||
static int pke_io_write_buffer(device*, const void*, int, address_word,
|
||||
unsigned, sim_cpu*, sim_cia);
|
||||
static void pke_reset(struct pke_device*);
|
||||
static void pke_issue(SIM_DESC, struct pke_device*);
|
||||
static unsigned_4 pke_fifo_flush(struct pke_fifo*);
|
||||
static struct fifo_quadword* pke_fifo_fit(struct pke_fifo*);
|
||||
static inline struct fifo_quadword* pke_fifo_access(struct pke_fifo*, unsigned_4 qwnum);
|
||||
static void pke_fifo_old(struct pke_fifo*, unsigned_4 qwnum);
|
||||
static void pke_pc_advance(struct pke_device*, int num_words);
|
||||
static unsigned_4* pke_pc_operand(struct pke_device*, int operand_num);
|
||||
static unsigned_4 pke_pc_operand_bits(struct pke_device*, int bit_offset,
|
||||
int bit_width, unsigned_4* sourceaddr);
|
||||
static struct fifo_quadword* pke_pc_fifo(struct pke_device*, int operand_num,
|
||||
unsigned_4** operand);
|
||||
static struct fifo_quadword* pke_pcrel_fifo(struct pke_device*, int operand_num,
|
||||
unsigned_4** operand);
|
||||
static unsigned_4* pke_pcrel_operand(struct pke_device*, int operand_num);
|
||||
static unsigned_4 pke_pcrel_operand_bits(struct pke_device*, int bit_offset,
|
||||
int bit_width, unsigned_4* sourceaddr);
|
||||
static void pke_attach(SIM_DESC sd, struct pke_device* me);
|
||||
enum pke_check_target { chk_vu, chk_path1, chk_path2, chk_path3 };
|
||||
static int pke_check_stall(struct pke_device* me, enum pke_check_target what);
|
||||
static void pke_flip_dbf(struct pke_device* me);
|
||||
static void pke_begin_interrupt_stall(struct pke_device* me);
|
||||
/* PKEcode handlers */
|
||||
static void pke_code_nop(struct pke_device* me, unsigned_4 pkecode);
|
||||
static void pke_code_stcycl(struct pke_device* me, unsigned_4 pkecode);
|
||||
|
@ -77,7 +79,8 @@ struct pke_device pke0_device =
|
|||
0, 0, /* ID, flags */
|
||||
{}, /* regs */
|
||||
{}, 0, /* FIFO write buffer */
|
||||
NULL, 0, 0, NULL, /* FIFO */
|
||||
{ NULL, 0, 0, 0 }, /* FIFO */
|
||||
NULL, /* FIFO trace file */
|
||||
0, 0 /* pc */
|
||||
};
|
||||
|
||||
|
@ -88,7 +91,8 @@ struct pke_device pke1_device =
|
|||
1, 0, /* ID, flags */
|
||||
{}, /* regs */
|
||||
{}, 0, /* FIFO write buffer */
|
||||
NULL, 0, 0, NULL, /* FIFO */
|
||||
{ NULL, 0, 0, 0 }, /* FIFO */
|
||||
NULL, /* FIFO trace file */
|
||||
0, 0 /* pc */
|
||||
};
|
||||
|
||||
|
@ -103,12 +107,14 @@ void
|
|||
pke0_attach(SIM_DESC sd)
|
||||
{
|
||||
pke_attach(sd, & pke0_device);
|
||||
pke_reset(& pke0_device);
|
||||
}
|
||||
|
||||
void
|
||||
pke1_attach(SIM_DESC sd)
|
||||
{
|
||||
pke_attach(sd, & pke1_device);
|
||||
pke_reset(& pke1_device);
|
||||
}
|
||||
|
||||
|
||||
|
@ -375,15 +381,7 @@ pke_io_write_buffer(device *me_,
|
|||
}
|
||||
if(BIT_MASK_GET(input[0], PKE_REG_FBRST_RST_B, PKE_REG_FBRST_RST_E))
|
||||
{
|
||||
/* clear FIFO by skipping to word after PC: also
|
||||
prevents re-execution attempt of possible stalled
|
||||
instruction */
|
||||
me->fifo_num_elements = me->fifo_pc;
|
||||
/* clear registers, flag, other state */
|
||||
memset(me->regs, 0, sizeof(me->regs));
|
||||
me->fifo_qw_done = 0;
|
||||
me->flags = 0;
|
||||
me->qw_pc = 0;
|
||||
pke_reset(me);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -467,32 +465,19 @@ pke_io_write_buffer(device *me_,
|
|||
/* all done - process quadword after clearing flag */
|
||||
BIT_MASK_SET(me->fifo_qw_done, 0, sizeof(quadword)-1, 0);
|
||||
|
||||
/* ensure FIFO has enough elements */
|
||||
if(me->fifo_num_elements == me->fifo_buffer_size)
|
||||
{
|
||||
/* time to grow */
|
||||
int new_fifo_buffer_size = me->fifo_buffer_size + 20;
|
||||
void* ptr = realloc((void*) me->fifo, new_fifo_buffer_size*sizeof(struct fifo_quadword));
|
||||
|
||||
if(ptr == NULL)
|
||||
{
|
||||
/* oops, cannot enlarge FIFO any more */
|
||||
device_error(me_, "Cannot enlarge FIFO buffer\n");
|
||||
return 0;
|
||||
}
|
||||
/* allocate required address in FIFO */
|
||||
fqw = pke_fifo_fit(& me->fifo);
|
||||
ASSERT(fqw != NULL);
|
||||
|
||||
me->fifo = ptr;
|
||||
me->fifo_buffer_size = new_fifo_buffer_size;
|
||||
}
|
||||
|
||||
/* add new quadword at end of FIFO; store data in host-endian */
|
||||
fqw = & me->fifo[me->fifo_num_elements];
|
||||
/* fill in unclassified FIFO quadword data in host byte order */
|
||||
fqw->word_class[0] = fqw->word_class[1] =
|
||||
fqw->word_class[2] = fqw->word_class[3] = wc_unknown;
|
||||
fqw->data[0] = T2H_4(me->fifo_qw_in_progress[0]);
|
||||
fqw->data[1] = T2H_4(me->fifo_qw_in_progress[1]);
|
||||
fqw->data[2] = T2H_4(me->fifo_qw_in_progress[2]);
|
||||
fqw->data[3] = T2H_4(me->fifo_qw_in_progress[3]);
|
||||
|
||||
/* read DMAC-supplied indicators */
|
||||
ASSERT(sizeof(unsigned_4) == 4);
|
||||
PKE_MEM_READ(me, (me->pke_number == 0 ? DMA_D0_MADR : DMA_D1_MADR),
|
||||
& fqw->source_address, /* converted to host-endian */
|
||||
|
@ -507,8 +492,6 @@ pke_io_write_buffer(device *me_,
|
|||
fqw->word_class[0] = fqw->word_class[1] = wc_dma;
|
||||
}
|
||||
|
||||
me->fifo_num_elements++;
|
||||
|
||||
/* set FQC to "1" as FIFO is now not empty */
|
||||
PKE_REG_MASK_SET(me, STAT, FQC, 1);
|
||||
|
||||
|
@ -522,6 +505,21 @@ pke_io_write_buffer(device *me_,
|
|||
|
||||
|
||||
|
||||
/* Reset the PKE */
|
||||
void
|
||||
pke_reset(struct pke_device* me)
|
||||
{
|
||||
/* advance PC over last quadword in FIFO; keep previous FIFO history */
|
||||
me->fifo_pc = pke_fifo_flush(& me->fifo);
|
||||
me->qw_pc = 0;
|
||||
/* clear registers, flag, other state */
|
||||
memset(me->regs, 0, sizeof(me->regs));
|
||||
me->fifo_qw_done = 0;
|
||||
me->flags = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Issue & swallow next PKE opcode if possible/available */
|
||||
|
||||
void
|
||||
|
@ -534,14 +532,16 @@ pke_issue(SIM_DESC sd, struct pke_device* me)
|
|||
/* 1 -- fetch PKE instruction */
|
||||
|
||||
/* confirm availability of new quadword of PKE instructions */
|
||||
if(me->fifo_num_elements <= me->fifo_pc)
|
||||
fqw = pke_fifo_access(& me->fifo, me->fifo_pc);
|
||||
if(fqw == NULL)
|
||||
return;
|
||||
|
||||
/* skip over DMA tag, if present */
|
||||
pke_pc_advance(me, 0);
|
||||
/* note: this can only change qw_pc from 0 to 2 and will not
|
||||
invalidate fqw */
|
||||
|
||||
/* "fetch" instruction quadword and word */
|
||||
fqw = & me->fifo[me->fifo_pc];
|
||||
fw = fqw->data[me->qw_pc];
|
||||
|
||||
/* store word in PKECODE register */
|
||||
|
@ -561,11 +561,9 @@ pke_issue(SIM_DESC sd, struct pke_device* me)
|
|||
/* check for stall/halt control bits */
|
||||
if(PKE_REG_MASK_GET(me, STAT, PFS) ||
|
||||
PKE_REG_MASK_GET(me, STAT, PSS) || /* note special treatment below */
|
||||
/* PEW bit not a reason to keep stalling - it's re-checked below */
|
||||
/* PGW bit not a reason to keep stalling - it's re-checked below */
|
||||
/* maskable stall controls: ER0, ER1, PIS */
|
||||
PKE_REG_MASK_GET(me, STAT, ER0) ||
|
||||
PKE_REG_MASK_GET(me, STAT, ER1) ||
|
||||
/* PEW bit not a reason to keep stalling - it's just an indication, re-computed below */
|
||||
/* PGW bit not a reason to keep stalling - it's just an indication, re-computed below */
|
||||
/* ER0/ER1 not a reason to keep stalling - it's just an indication */
|
||||
PKE_REG_MASK_GET(me, STAT, PIS))
|
||||
{
|
||||
/* (still) stalled */
|
||||
|
@ -600,8 +598,6 @@ pke_issue(SIM_DESC sd, struct pke_device* me)
|
|||
}
|
||||
else /* new interrupt-flagged instruction */
|
||||
{
|
||||
/* XXX: send interrupt to 5900? */
|
||||
|
||||
/* set INT flag in STAT register */
|
||||
PKE_REG_MASK_SET(me, STAT, INT, 1);
|
||||
/* set loop-prevention flag */
|
||||
|
@ -609,7 +605,7 @@ pke_issue(SIM_DESC sd, struct pke_device* me)
|
|||
|
||||
/* set PIS if stall not masked */
|
||||
if(!PKE_REG_MASK_GET(me, ERR, MII))
|
||||
PKE_REG_MASK_SET(me, STAT, PIS, 1);
|
||||
pke_begin_interrupt_stall(me);
|
||||
|
||||
/* suspend this instruction unless it's PKEMARK */
|
||||
if(!IS_PKE_CMD(cmd, PKEMARK))
|
||||
|
@ -675,6 +671,117 @@ pke_issue(SIM_DESC sd, struct pke_device* me)
|
|||
|
||||
|
||||
|
||||
/* Clear out contents of FIFO; act as if it was empty. Return PC
|
||||
pointing to one-past-last word. */
|
||||
|
||||
unsigned_4
|
||||
pke_fifo_flush(struct pke_fifo* fifo)
|
||||
{
|
||||
/* don't modify any state! */
|
||||
return fifo->origin + fifo->next;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Make space for the next quadword in the FIFO. Allocate/enlarge
|
||||
FIFO pointer block if necessary. Return a pointer to it. */
|
||||
|
||||
struct fifo_quadword*
|
||||
pke_fifo_fit(struct pke_fifo* fifo)
|
||||
{
|
||||
struct fifo_quadword* fqw;
|
||||
|
||||
/* out of space on quadword pointer array? */
|
||||
if(fifo->next == fifo->length) /* also triggered before fifo->quadwords allocated */
|
||||
{
|
||||
struct fifo_quadword** new_qw;
|
||||
unsigned_4 new_length = fifo->length + PKE_FIFO_GROW_SIZE;
|
||||
|
||||
/* allocate new pointer block */
|
||||
new_qw = zalloc(new_length * sizeof(struct fifo_quadword*));
|
||||
ASSERT(new_qw != NULL);
|
||||
|
||||
/* copy over old pointers to beginning of new block */
|
||||
memcpy(new_qw, fifo->quadwords,
|
||||
fifo->length * sizeof(struct fifo_quadword*));
|
||||
|
||||
/* free old block */
|
||||
zfree(fifo->quadwords);
|
||||
|
||||
/* replace pointers & counts */
|
||||
fifo->quadwords = new_qw;
|
||||
fifo->length = new_length;
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
ASSERT(fifo->quadwords != NULL);
|
||||
|
||||
/* allocate new quadword from heap */
|
||||
fqw = zalloc(sizeof(struct fifo_quadword));
|
||||
ASSERT(fqw != NULL);
|
||||
|
||||
/* push quadword onto fifo */
|
||||
fifo->quadwords[fifo->next] = fqw;
|
||||
fifo->next++;
|
||||
return fqw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return a pointer to the FIFO quadword with given absolute index, or
|
||||
NULL if it is out of range */
|
||||
|
||||
struct fifo_quadword*
|
||||
pke_fifo_access(struct pke_fifo* fifo, unsigned_4 qwnum)
|
||||
{
|
||||
struct fifo_quadword* fqw;
|
||||
|
||||
if((qwnum < fifo->origin) || /* before history */
|
||||
(qwnum >= fifo->origin + fifo->next)) /* after last available quadword */
|
||||
fqw = NULL;
|
||||
else
|
||||
{
|
||||
ASSERT(fifo->quadwords != NULL); /* must be allocated already */
|
||||
fqw = fifo->quadwords[qwnum - fifo->origin]; /* pull out pointer from array */
|
||||
ASSERT(fqw != NULL); /* must be allocated already */
|
||||
}
|
||||
|
||||
return fqw;
|
||||
}
|
||||
|
||||
|
||||
/* Authorize release of any FIFO entries older than given absolute quadword. */
|
||||
void
|
||||
pke_fifo_old(struct pke_fifo* fifo, unsigned_4 qwnum)
|
||||
{
|
||||
/* do we have any too-old FIFO elements? */
|
||||
if(fifo->origin + PKE_FIFO_ARCHEOLOGY < qwnum)
|
||||
{
|
||||
/* count quadwords to forget */
|
||||
int horizon = qwnum - (fifo->origin + PKE_FIFO_ARCHEOLOGY);
|
||||
int i;
|
||||
|
||||
/* free quadwords at indices below horizon */
|
||||
for(i=0; i < horizon; i++)
|
||||
zfree(fifo->quadwords[i]);
|
||||
|
||||
/* move surviving quadword pointers down to beginning of array */
|
||||
for(i=horizon; i < fifo->next; i++)
|
||||
fifo->quadwords[i-horizon] = fifo->quadwords[i];
|
||||
|
||||
/* clear duplicate pointers */
|
||||
for(i=fifo->next - horizon; i < fifo->next; i++)
|
||||
fifo->quadwords[i] = NULL;
|
||||
|
||||
/* adjust FIFO pointers */
|
||||
fifo->origin = fifo->origin + horizon;
|
||||
fifo->next = fifo->next - horizon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* advance the PC by given number of data words; update STAT/FQC
|
||||
field; assume FIFO is filled enough; classify passed-over words;
|
||||
write FIFO trace line */
|
||||
|
@ -684,16 +791,19 @@ pke_pc_advance(struct pke_device* me, int num_words)
|
|||
{
|
||||
int num = num_words;
|
||||
struct fifo_quadword* fq = NULL;
|
||||
unsigned_4 old_fifo_pc = me->fifo_pc;
|
||||
|
||||
ASSERT(num_words >= 0);
|
||||
|
||||
/* printf("pke %d pc_advance num_words %d\n", me->pke_number, num_words); */
|
||||
|
||||
while(1)
|
||||
{
|
||||
fq = & me->fifo[me->fifo_pc];
|
||||
/* find next quadword, if any */
|
||||
fq = pke_fifo_access(& me->fifo, me->fifo_pc);
|
||||
|
||||
/* skip over DMA tag words if present in word 0 or 1 */
|
||||
if(fq->word_class[me->qw_pc] == wc_dma)
|
||||
if(fq != NULL && fq->word_class[me->qw_pc] == wc_dma)
|
||||
{
|
||||
/* skip by going around loop an extra time */
|
||||
num ++;
|
||||
|
@ -703,6 +813,9 @@ pke_pc_advance(struct pke_device* me, int num_words)
|
|||
if(num == 0)
|
||||
break;
|
||||
|
||||
/* we are supposed to skip existing words */
|
||||
ASSERT(fq != NULL);
|
||||
|
||||
/* one word skipped */
|
||||
num --;
|
||||
|
||||
|
@ -733,34 +846,41 @@ pke_pc_advance(struct pke_device* me, int num_words)
|
|||
fq->word_class[3], fq->word_class[2],
|
||||
fq->word_class[1], fq->word_class[0]);
|
||||
}
|
||||
|
||||
/* XXX: zap old entries in FIFO */
|
||||
} /* next quadword */
|
||||
}
|
||||
|
||||
/* age old entries before PC */
|
||||
if(me->fifo_pc != old_fifo_pc)
|
||||
{
|
||||
/* we advanced the fifo-pc; authorize disposal of anything
|
||||
before previous PKEcode */
|
||||
pke_fifo_old(& me->fifo, old_fifo_pc);
|
||||
}
|
||||
|
||||
/* clear FQC if FIFO is now empty */
|
||||
if(me->fifo_num_elements == me->fifo_pc)
|
||||
fq = pke_fifo_access(& me->fifo, me->fifo_pc);
|
||||
if(fq == NULL)
|
||||
{
|
||||
PKE_REG_MASK_SET(me, STAT, FQC, 0);
|
||||
}
|
||||
else /* annote the word where the PC lands as an PKEcode */
|
||||
{
|
||||
fq = & me->fifo[me->fifo_pc];
|
||||
ASSERT(fq->word_class[me->qw_pc] == wc_pkecode ||
|
||||
fq->word_class[me->qw_pc] == wc_unknown);
|
||||
ASSERT(fq->word_class[me->qw_pc] == wc_pkecode || fq->word_class[me->qw_pc] == wc_unknown);
|
||||
fq->word_class[me->qw_pc] = wc_pkecode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Return pointer to FIFO quadword containing given operand# in FIFO.
|
||||
`operand_num' starts at 1. Return pointer to operand word in last
|
||||
argument, if non-NULL. If FIFO is not full enough, return 0.
|
||||
Signal an ER0 indication upon skipping a DMA tag. */
|
||||
|
||||
struct fifo_quadword*
|
||||
pke_pc_fifo(struct pke_device* me, int operand_num, unsigned_4** operand)
|
||||
pke_pcrel_fifo(struct pke_device* me, int operand_num, unsigned_4** operand)
|
||||
{
|
||||
int num = operand_num;
|
||||
int new_qw_pc, new_fifo_pc;
|
||||
|
@ -772,7 +892,7 @@ pke_pc_fifo(struct pke_device* me, int operand_num, unsigned_4** operand)
|
|||
new_fifo_pc = me->fifo_pc;
|
||||
new_qw_pc = me->qw_pc;
|
||||
|
||||
/* printf("pke %d pc_fifo operand_num %d\n", me->pke_number, operand_num); */
|
||||
/* printf("pke %d pcrel_fifo operand_num %d\n", me->pke_number, operand_num); */
|
||||
|
||||
do
|
||||
{
|
||||
|
@ -787,21 +907,22 @@ pke_pc_fifo(struct pke_device* me, int operand_num, unsigned_4** operand)
|
|||
new_fifo_pc ++;
|
||||
}
|
||||
|
||||
fq = pke_fifo_access(& me->fifo, new_fifo_pc);
|
||||
|
||||
/* check for FIFO underflow */
|
||||
if(me->fifo_num_elements == new_fifo_pc)
|
||||
{
|
||||
fq = NULL;
|
||||
break;
|
||||
}
|
||||
if(fq == NULL)
|
||||
break;
|
||||
|
||||
/* skip over DMA tag words if present in word 0 or 1 */
|
||||
fq = & me->fifo[new_fifo_pc];
|
||||
if(fq->word_class[new_qw_pc] == wc_dma)
|
||||
{
|
||||
/* set ER0 */
|
||||
PKE_REG_MASK_SET(me, STAT, ER0, 1);
|
||||
|
||||
/* mismatch error! */
|
||||
if(! PKE_REG_MASK_GET(me, ERR, ME0))
|
||||
{
|
||||
PKE_REG_MASK_SET(me, STAT, ER0, 1);
|
||||
pke_begin_interrupt_stall(me);
|
||||
/* don't stall just yet -- finish this instruction */
|
||||
/* the PPS_STALL state will be entered by pke_issue() next time */
|
||||
}
|
||||
|
@ -816,9 +937,8 @@ pke_pc_fifo(struct pke_device* me, int operand_num, unsigned_4** operand)
|
|||
{
|
||||
*operand = & fq->data[new_qw_pc];
|
||||
|
||||
/* annote the word where the pseudo lands as an PKE operand */
|
||||
ASSERT(fq->word_class[new_qw_pc] == wc_pkedata ||
|
||||
fq->word_class[new_qw_pc] == wc_unknown);
|
||||
/* annote the word where the pseudo-PC lands as an PKE operand */
|
||||
ASSERT(fq->word_class[new_qw_pc] == wc_pkedata || fq->word_class[new_qw_pc] == wc_unknown);
|
||||
fq->word_class[new_qw_pc] = wc_pkedata;
|
||||
}
|
||||
|
||||
|
@ -831,15 +951,15 @@ pke_pc_fifo(struct pke_device* me, int operand_num, unsigned_4** operand)
|
|||
them as an error (ER0). */
|
||||
|
||||
unsigned_4*
|
||||
pke_pc_operand(struct pke_device* me, int operand_num)
|
||||
pke_pcrel_operand(struct pke_device* me, int operand_num)
|
||||
{
|
||||
unsigned_4* operand = NULL;
|
||||
struct fifo_quadword* fifo_operand;
|
||||
|
||||
fifo_operand = pke_pc_fifo(me, operand_num, & operand);
|
||||
fifo_operand = pke_pcrel_fifo(me, operand_num, & operand);
|
||||
|
||||
if(fifo_operand == NULL)
|
||||
ASSERT(operand == NULL); /* pke_pc_fifo() ought leave it untouched */
|
||||
ASSERT(operand == NULL); /* pke_pcrel_fifo() ought leave it untouched */
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
@ -851,7 +971,7 @@ pke_pc_operand(struct pke_device* me, int operand_num)
|
|||
enough. Skip over DMA tags, but mark them as an error (ER0). */
|
||||
|
||||
unsigned_4
|
||||
pke_pc_operand_bits(struct pke_device* me, int bit_offset, int bit_width, unsigned_4* source_addr)
|
||||
pke_pcrel_operand_bits(struct pke_device* me, int bit_offset, int bit_width, unsigned_4* source_addr)
|
||||
{
|
||||
unsigned_4* word = NULL;
|
||||
unsigned_4 value;
|
||||
|
@ -862,7 +982,7 @@ pke_pc_operand_bits(struct pke_device* me, int bit_offset, int bit_width, unsign
|
|||
bitnumber = bit_offset%32;
|
||||
|
||||
/* find operand word with bitfield */
|
||||
fifo_operand = pke_pc_fifo(me, wordnumber + 1, &word);
|
||||
fifo_operand = pke_pcrel_fifo(me, wordnumber + 1, &word);
|
||||
ASSERT(word != NULL);
|
||||
|
||||
/* extract bitfield from word */
|
||||
|
@ -951,6 +1071,18 @@ pke_flip_dbf(struct pke_device* me)
|
|||
}
|
||||
|
||||
|
||||
/* set the STAT:PIS bit and send an interrupt to the 5900 */
|
||||
void
|
||||
pke_begin_interrupt_stall(struct pke_device* me)
|
||||
{
|
||||
/* set PIS */
|
||||
PKE_REG_MASK_SET(me, STAT, PIS, 1);
|
||||
|
||||
/* XXX: send interrupt to 5900? */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* PKEcode handler functions -- responsible for checking and
|
||||
confirming old stall conditions, executing pkecode, updating PC and
|
||||
|
@ -1325,7 +1457,7 @@ pke_code_stmask(struct pke_device* me, unsigned_4 pkecode)
|
|||
unsigned_4* mask;
|
||||
|
||||
/* check that FIFO has one more word for STMASK operand */
|
||||
mask = pke_pc_operand(me, 1);
|
||||
mask = pke_pcrel_operand(me, 1);
|
||||
if(mask != NULL)
|
||||
{
|
||||
/* "transferring" operand */
|
||||
|
@ -1359,7 +1491,7 @@ pke_code_strow(struct pke_device* me, unsigned_4 pkecode)
|
|||
/* check that FIFO has four more words for STROW operand */
|
||||
unsigned_4* last_op;
|
||||
|
||||
last_op = pke_pc_operand(me, 4);
|
||||
last_op = pke_pcrel_operand(me, 4);
|
||||
if(last_op != NULL)
|
||||
{
|
||||
/* "transferring" operand */
|
||||
|
@ -1369,10 +1501,10 @@ pke_code_strow(struct pke_device* me, unsigned_4 pkecode)
|
|||
PKE_REG_MASK_SET(me, NUM, NUM, 1);
|
||||
|
||||
/* copy ROW registers: must all exist if 4th operand exists */
|
||||
me->regs[PKE_REG_R0][0] = * pke_pc_operand(me, 1);
|
||||
me->regs[PKE_REG_R1][0] = * pke_pc_operand(me, 2);
|
||||
me->regs[PKE_REG_R2][0] = * pke_pc_operand(me, 3);
|
||||
me->regs[PKE_REG_R3][0] = * pke_pc_operand(me, 4);
|
||||
me->regs[PKE_REG_R0][0] = * pke_pcrel_operand(me, 1);
|
||||
me->regs[PKE_REG_R1][0] = * pke_pcrel_operand(me, 2);
|
||||
me->regs[PKE_REG_R2][0] = * pke_pcrel_operand(me, 3);
|
||||
me->regs[PKE_REG_R3][0] = * pke_pcrel_operand(me, 4);
|
||||
|
||||
/* set NUM */
|
||||
PKE_REG_MASK_SET(me, NUM, NUM, 0);
|
||||
|
@ -1396,7 +1528,7 @@ pke_code_stcol(struct pke_device* me, unsigned_4 pkecode)
|
|||
/* check that FIFO has four more words for STCOL operand */
|
||||
unsigned_4* last_op;
|
||||
|
||||
last_op = pke_pc_operand(me, 4);
|
||||
last_op = pke_pcrel_operand(me, 4);
|
||||
if(last_op != NULL)
|
||||
{
|
||||
/* "transferring" operand */
|
||||
|
@ -1406,10 +1538,10 @@ pke_code_stcol(struct pke_device* me, unsigned_4 pkecode)
|
|||
PKE_REG_MASK_SET(me, NUM, NUM, 1);
|
||||
|
||||
/* copy COL registers: must all exist if 4th operand exists */
|
||||
me->regs[PKE_REG_C0][0] = * pke_pc_operand(me, 1);
|
||||
me->regs[PKE_REG_C1][0] = * pke_pc_operand(me, 2);
|
||||
me->regs[PKE_REG_C2][0] = * pke_pc_operand(me, 3);
|
||||
me->regs[PKE_REG_C3][0] = * pke_pc_operand(me, 4);
|
||||
me->regs[PKE_REG_C0][0] = * pke_pcrel_operand(me, 1);
|
||||
me->regs[PKE_REG_C1][0] = * pke_pcrel_operand(me, 2);
|
||||
me->regs[PKE_REG_C2][0] = * pke_pcrel_operand(me, 3);
|
||||
me->regs[PKE_REG_C3][0] = * pke_pcrel_operand(me, 4);
|
||||
|
||||
/* set NUM */
|
||||
PKE_REG_MASK_SET(me, NUM, NUM, 0);
|
||||
|
@ -1442,7 +1574,7 @@ pke_code_mpg(struct pke_device* me, unsigned_4 pkecode)
|
|||
if(num==0) num=0x100;
|
||||
|
||||
/* check that FIFO has a few more words for MPG operand */
|
||||
last_mpg_word = pke_pc_operand(me, num*2); /* num: number of 64-bit words */
|
||||
last_mpg_word = pke_pcrel_operand(me, num*2); /* num: number of 64-bit words */
|
||||
if(last_mpg_word != NULL)
|
||||
{
|
||||
/* perform implied FLUSHE */
|
||||
|
@ -1497,9 +1629,9 @@ pke_code_mpg(struct pke_device* me, unsigned_4 pkecode)
|
|||
vutrack_addr = vutrack_addr_base + ((signed_8)vu_addr - (signed_8)vu_addr_base) / 2;
|
||||
|
||||
/* Fetch operand words; assume they are already little-endian for VU imem */
|
||||
fq = pke_pc_fifo(me, i*2 + 1, & operand);
|
||||
fq = pke_pcrel_fifo(me, i*2 + 1, & operand);
|
||||
vu_lower_opcode = *operand;
|
||||
vu_upper_opcode = *pke_pc_operand(me, i*2 + 2);
|
||||
vu_upper_opcode = *pke_pcrel_operand(me, i*2 + 2);
|
||||
|
||||
/* write data into VU memory */
|
||||
/* lower (scalar) opcode comes in first word ; macro performs H2T! */
|
||||
|
@ -1550,7 +1682,7 @@ pke_code_direct(struct pke_device* me, unsigned_4 pkecode)
|
|||
/* map zero to max+1 */
|
||||
if(imm==0) imm=0x10000;
|
||||
|
||||
last_direct_word = pke_pc_operand(me, imm*4); /* imm: number of 128-bit words */
|
||||
last_direct_word = pke_pcrel_operand(me, imm*4); /* imm: number of 128-bit words */
|
||||
if(last_direct_word != NULL)
|
||||
{
|
||||
/* VU idle */
|
||||
|
@ -1563,7 +1695,7 @@ pke_code_direct(struct pke_device* me, unsigned_4 pkecode)
|
|||
/* transfer GPUIF quadwords, one word per iteration */
|
||||
for(i=0; i<imm*4; i++)
|
||||
{
|
||||
unsigned_4* operand = pke_pc_operand(me, 1+i);
|
||||
unsigned_4* operand = pke_pcrel_operand(me, 1+i);
|
||||
|
||||
/* collect word into quadword */
|
||||
*A4_16(&fifo_data, 3 - (i % 4)) = *operand;
|
||||
|
@ -1606,27 +1738,36 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
int imm = BIT_MASK_GET(pkecode, PKE_OPCODE_IMM_B, PKE_OPCODE_IMM_E);
|
||||
int cmd = BIT_MASK_GET(pkecode, PKE_OPCODE_CMD_B, PKE_OPCODE_CMD_E);
|
||||
int num = BIT_MASK_GET(pkecode, PKE_OPCODE_NUM_B, PKE_OPCODE_NUM_E);
|
||||
int nummx = (num == 0) ? 0x0100 : num;
|
||||
short vn = BIT_MASK_GET(cmd, 2, 3); /* unpack shape controls */
|
||||
short vl = BIT_MASK_GET(cmd, 0, 1);
|
||||
int m = BIT_MASK_GET(cmd, 4, 4);
|
||||
short cl = PKE_REG_MASK_GET(me, CYCLE, CL); /* cycle controls */
|
||||
short wl = PKE_REG_MASK_GET(me, CYCLE, WL);
|
||||
short addrwl = (wl == 0) ? 0x0100 : wl;
|
||||
int r = BIT_MASK_GET(imm, 15, 15); /* indicator bits in imm value */
|
||||
int usn = BIT_MASK_GET(imm, 14, 14);
|
||||
|
||||
int n, num_operands;
|
||||
unsigned_4* last_operand_word = NULL;
|
||||
|
||||
/* catch all illegal UNPACK variants */
|
||||
if(vl == 3 && vn < 3)
|
||||
{
|
||||
pke_code_error(me, pkecode);
|
||||
return;
|
||||
}
|
||||
|
||||
/* compute PKEcode length, as given in CPU2 spec, v2.1 pg. 11 */
|
||||
if(wl <= cl)
|
||||
if(cl >= addrwl)
|
||||
n = num;
|
||||
else
|
||||
n = cl * (num/wl) + PKE_LIMIT(num % wl, cl);
|
||||
n = cl * (nummx / addrwl) + PKE_LIMIT(nummx % addrwl, cl);
|
||||
num_operands = (31 + (32 >> vl) * (vn+1) * n)/32; /* round up to next word */
|
||||
|
||||
/* confirm that FIFO has enough words in it */
|
||||
if(num_operands > 0)
|
||||
last_operand_word = pke_pc_operand(me, num_operands);
|
||||
last_operand_word = pke_pcrel_operand(me, num_operands);
|
||||
if(last_operand_word != NULL || num_operands == 0)
|
||||
{
|
||||
address_word vu_addr_base, vutrack_addr_base;
|
||||
|
@ -1654,7 +1795,7 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
}
|
||||
|
||||
/* set NUM */
|
||||
PKE_REG_MASK_SET(me, NUM, NUM, num == 0 ? 0x100 : num );
|
||||
PKE_REG_MASK_SET(me, NUM, NUM, nummx);
|
||||
|
||||
/* transfer given number of vectors */
|
||||
vector_num_out = 0; /* output vector number being processed */
|
||||
|
@ -1678,7 +1819,6 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
if(cl >= wl)
|
||||
{
|
||||
/* map zero to max+1 */
|
||||
int addrwl = (wl == 0) ? 0x0100 : wl;
|
||||
vu_addr = vu_addr_base + 16 * (BIT_MASK_GET(imm, 0, 9) +
|
||||
(vector_num_out / addrwl) * cl +
|
||||
(vector_num_out % addrwl));
|
||||
|
@ -1712,8 +1852,8 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
|
||||
/* For cyclic unpack, next operand quadword may come from instruction stream
|
||||
or be zero. */
|
||||
if((num == 0 && cl == 0 && wl == 0) || /* shortcut clear */
|
||||
((cl < wl) && ((vector_num_out % wl) >= cl))) /* && short-circuit asserts wl != 0 */
|
||||
if((cl < addrwl) &&
|
||||
(vector_num_out % addrwl) >= cl)
|
||||
{
|
||||
/* clear operand - used only in a "indeterminate" state */
|
||||
for(i = 0; i < 4; i++)
|
||||
|
@ -1722,7 +1862,7 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
else
|
||||
{
|
||||
/* compute packed vector dimensions */
|
||||
int vectorbits, unitbits;
|
||||
int vectorbits = 0, unitbits = 0;
|
||||
|
||||
if(vl < 3) /* PKE_UNPACK_*_{32,16,8} */
|
||||
{
|
||||
|
@ -1736,9 +1876,8 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
}
|
||||
else /* illegal unpack variant */
|
||||
{
|
||||
/* treat as illegal instruction */
|
||||
pke_code_error(me, pkecode);
|
||||
return;
|
||||
/* should have been caught at top of function */
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
/* loop over columns */
|
||||
|
@ -1754,8 +1893,19 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
if(vl == 3 && vn == 3 && i == 3) /* PKE_UNPACK_V4_5 */
|
||||
unitbits = 1;
|
||||
|
||||
/* confirm we're not reading more than we said we needed */
|
||||
if(vector_num_in * vectorbits >= num_operands * 32)
|
||||
{
|
||||
/* this condition may be triggered by illegal
|
||||
PKEcode / CYCLE combinations. */
|
||||
pke_code_error(me, pkecode);
|
||||
/* XXX: this case needs to be better understood,
|
||||
and detected at a better time. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* fetch bitfield operand */
|
||||
operand = pke_pc_operand_bits(me, bitoffset, unitbits, & source_addr);
|
||||
operand = pke_pcrel_operand_bits(me, bitoffset, unitbits, & source_addr);
|
||||
|
||||
/* selectively sign-extend; not for V4_5 1-bit value */
|
||||
if(usn || unitbits == 1)
|
||||
|
@ -1764,37 +1914,57 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
unpacked_data[i] = SEXT32(operand, unitbits-1);
|
||||
}
|
||||
|
||||
/* clear remaining top words in vector */
|
||||
for(; i<4; i++)
|
||||
unpacked_data[i] = 0;
|
||||
/* set remaining top words in vector */
|
||||
for(i=vn+1; i<4; i++)
|
||||
{
|
||||
if(vn == 0) /* S_{32,16,8}: copy lowest element */
|
||||
unpacked_data[i] = unpacked_data[0];
|
||||
else
|
||||
unpacked_data[i] = 0;
|
||||
}
|
||||
|
||||
/* consumed a vector from the PKE instruction stream */
|
||||
vector_num_in ++;
|
||||
} /* unpack word from instruction operand */
|
||||
|
||||
/* process STMOD register for accumulation operations */
|
||||
switch(PKE_REG_MASK_GET(me, MODE, MDE))
|
||||
{
|
||||
case PKE_MODE_ADDROW: /* add row registers to output data */
|
||||
for(i=0; i<4; i++)
|
||||
/* exploit R0..R3 contiguity */
|
||||
unpacked_data[i] += me->regs[PKE_REG_R0 + i][0];
|
||||
break;
|
||||
|
||||
case PKE_MODE_ACCROW: /* add row registers to output data; accumulate */
|
||||
for(i=0; i<4; i++)
|
||||
{
|
||||
/* exploit R0..R3 contiguity */
|
||||
unpacked_data[i] += me->regs[PKE_REG_R0 + i][0];
|
||||
me->regs[PKE_REG_R0 + i][0] = unpacked_data[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case PKE_MODE_INPUT: /* pass data through */
|
||||
default: /* specified as undefined */
|
||||
;
|
||||
}
|
||||
|
||||
/* compute replacement word */
|
||||
if(m) /* use mask register? */
|
||||
{
|
||||
/* compute index into mask register for this word */
|
||||
int addrwl = (wl == 0) ? 0x0100 : wl;
|
||||
int mask_index = PKE_LIMIT(vector_num_out % addrwl, 3);
|
||||
|
||||
for(i=0; i<4; i++) /* loop over columns */
|
||||
{
|
||||
int mask_op = PKE_MASKREG_GET(me, mask_index, i);
|
||||
unsigned_4* masked_value = NULL;
|
||||
unsigned_4 zero = 0;
|
||||
|
||||
switch(mask_op)
|
||||
{
|
||||
case PKE_MASKREG_INPUT:
|
||||
/* for vn == 0, all columns are copied from column 0 */
|
||||
if(vn == 0)
|
||||
masked_value = & unpacked_data[0];
|
||||
else if(i > vn)
|
||||
masked_value = & zero; /* arbitrary data: undefined in spec */
|
||||
else
|
||||
masked_value = & unpacked_data[i];
|
||||
masked_value = & unpacked_data[i];
|
||||
break;
|
||||
|
||||
case PKE_MASKREG_ROW: /* exploit R0..R3 contiguity */
|
||||
|
@ -1824,29 +1994,6 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
/* no mask - just copy over entire unpacked quadword */
|
||||
memcpy(vu_new_data, unpacked_data, sizeof(unpacked_data));
|
||||
}
|
||||
|
||||
/* process STMOD register for accumulation operations */
|
||||
switch(PKE_REG_MASK_GET(me, MODE, MDE))
|
||||
{
|
||||
case PKE_MODE_ADDROW: /* add row registers to output data */
|
||||
for(i=0; i<4; i++)
|
||||
/* exploit R0..R3 contiguity */
|
||||
vu_new_data[i] += me->regs[PKE_REG_R0 + i][0];
|
||||
break;
|
||||
|
||||
case PKE_MODE_ACCROW: /* add row registers to output data; accumulate */
|
||||
for(i=0; i<4; i++)
|
||||
{
|
||||
/* exploit R0..R3 contiguity */
|
||||
vu_new_data[i] += me->regs[PKE_REG_R0 + i][0];
|
||||
me->regs[PKE_REG_R0 + i][0] = vu_new_data[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case PKE_MODE_INPUT: /* pass data through */
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
/* write new VU data word at address; reverse words if needed */
|
||||
{
|
||||
|
@ -1871,6 +2018,9 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
} /* vector transfer loop */
|
||||
while(PKE_REG_MASK_GET(me, NUM, NUM) > 0);
|
||||
|
||||
/* confirm we've written as many vectors as told */
|
||||
ASSERT(nummx == vector_num_out);
|
||||
|
||||
/* done */
|
||||
PKE_REG_MASK_SET(me, STAT, PPS, PKE_REG_STAT_PPS_IDLE);
|
||||
pke_pc_advance(me, 1 + num_operands);
|
||||
|
@ -1887,10 +2037,12 @@ pke_code_unpack(struct pke_device* me, unsigned_4 pkecode)
|
|||
void
|
||||
pke_code_error(struct pke_device* me, unsigned_4 pkecode)
|
||||
{
|
||||
/* set ER1 flag in STAT register */
|
||||
PKE_REG_MASK_SET(me, STAT, ER1, 1);
|
||||
|
||||
if(! PKE_REG_MASK_GET(me, ERR, ME1))
|
||||
{
|
||||
/* set ER1 flag in STAT register */
|
||||
PKE_REG_MASK_SET(me, STAT, ER1, 1);
|
||||
pke_begin_interrupt_stall(me);
|
||||
PKE_REG_MASK_SET(me, STAT, PPS, PKE_REG_STAT_PPS_STALL);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -34,10 +34,6 @@ typedef unsigned_4 quadword[4];
|
|||
#define PKE1_FIFO_ADDR 0x10005000
|
||||
|
||||
|
||||
/* and now a few definitions that rightfully belong elsewhere */
|
||||
#ifdef PKE_DEBUG
|
||||
|
||||
|
||||
/* VU source-addr tracking tables */ /* changed from 1998-01-22 e-mail plans */
|
||||
#define VU0_MEM0_SRCADDR_START 0x21000000
|
||||
#define VU0_MEM1_SRCADDR_START 0x21004000
|
||||
|
@ -55,8 +51,6 @@ typedef unsigned_4 quadword[4];
|
|||
#define COP2_REG_STAT_VBS0_E 0
|
||||
#define COP2_REG_STAT_VBS0_B 0
|
||||
|
||||
#endif /* PKE_DEBUG */
|
||||
|
||||
|
||||
/* Quadword indices of PKE registers. Actual registers sit at bottom
|
||||
32 bits of each quadword. */
|
||||
|
@ -366,6 +360,19 @@ struct fifo_quadword
|
|||
};
|
||||
|
||||
|
||||
/* quadword FIFO structure for PKE */
|
||||
struct pke_fifo
|
||||
{
|
||||
struct fifo_quadword** quadwords; /* pointer to fifo quadwords */
|
||||
unsigned_4 origin; /* quadword serial number of quadwords[0] */
|
||||
unsigned_4 length; /* length of quadword pointer array: 0..N */
|
||||
unsigned_4 next; /* relative index of first unfilled quadword: 0..length-1 */
|
||||
};
|
||||
|
||||
#define PKE_FIFO_GROW_SIZE 1000 /* number of quadword pointers to allocate */
|
||||
#define PKE_FIFO_ARCHEOLOGY 1000 /* number of old quadwords to keep as history */
|
||||
|
||||
|
||||
/* PKE internal state: FIFOs, registers, handle to VU friend */
|
||||
struct pke_device
|
||||
{
|
||||
|
@ -383,11 +390,9 @@ struct pke_device
|
|||
quadword fifo_qw_in_progress;
|
||||
int fifo_qw_done; /* bitfield */
|
||||
|
||||
/* FIFO */
|
||||
struct fifo_quadword* fifo;
|
||||
int fifo_num_elements; /* no. of quadwords occupied in FIFO */
|
||||
int fifo_buffer_size; /* no. of quadwords of space in FIFO */
|
||||
FILE* fifo_trace_file; /* or 0 for no trace */
|
||||
/* FIFO - private: use only pke_fifo_* routines to access */
|
||||
struct pke_fifo fifo; /* array of FIFO quadword pointers */
|
||||
FILE* fifo_trace_file; /* stdio stream open in append mode, or 0 for no trace */
|
||||
|
||||
/* PC */
|
||||
int fifo_pc; /* 0 .. (fifo_num_elements-1): quadword index of next instruction */
|
||||
|
|
|
@ -23,6 +23,7 @@ static char* vu1_mem_buffer = 0;
|
|||
void init_vu1(void);
|
||||
void init_vu(VectorUnitState *state, char* umem_buffer, char* mem_buffer);
|
||||
|
||||
#if 0
|
||||
static void dump_mem() {
|
||||
int i;
|
||||
typedef int T[2048][4];
|
||||
|
@ -32,6 +33,7 @@ static void dump_mem() {
|
|||
printf("%d: %x %x %x %x\n", i, (*mem)[i][0], (*mem)[i][1], (*mem)[i][2], (*mem)[i][3]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
vu1_issue(void)
|
||||
|
|
Loading…
Reference in a new issue