special bus error recovery code for recovery from bus error due to bus extenders
This commit is contained in:
381
src/libvxWorks/bus_error_recovery.c
Normal file
381
src/libvxWorks/bus_error_recovery.c
Normal file
@@ -0,0 +1,381 @@
|
||||
|
||||
/*
|
||||
Special case bus error recovery code.
|
||||
|
||||
The code in this file will attempt to retry instructions which caused
|
||||
bus errors up to 10 times before invoking the standard vxWorks bus
|
||||
error handler. The code also records the last 20 bus errors so they
|
||||
can be reported using a utility run from the vxWorks console. Database
|
||||
records can be used to monitor when bus errors occur - See support
|
||||
below.
|
||||
|
||||
The bus error handler only processes bus errors outside the processor's
|
||||
local memory which are normal user/supervisor data accesses.
|
||||
|
||||
------------------
|
||||
epicsBusErrorPrint - Print the last 20 bus errors that occurred. The
|
||||
report includes:
|
||||
1) The bus error number
|
||||
2) FA - The fault address
|
||||
3) PC - The program counter when the fault occurred
|
||||
4) SR - Status register
|
||||
5) SSW - Special status word
|
||||
|
||||
-------------
|
||||
devAiBusError - Simple EPICS ai record device support. Create an
|
||||
ai record with DTYP="Bus Error" and SCAN="I/O Intr" to get
|
||||
informed when a bus error occurs. You may not get informed if the
|
||||
bus error is handled by vxWorks and an important EPICS task is
|
||||
suspended as a result. You will always be notified of a bus
|
||||
error that has been corrected by retries. The ai record will
|
||||
count up every time if is processed. The bus error handler triggers
|
||||
processing of the record if the SCAN type is "I/O Intr".
|
||||
|
||||
If a bus error is handled by vxWorks and you do not get notified via
|
||||
the ai record, you can sign on the console and run epicsBusErrorPrint
|
||||
and see the last 20 bus errors that occurred.
|
||||
|
||||
Add the following line to the cat_ascii/devSup.ascii file:
|
||||
"ai" VME_IO "devAiBusError" "Bus Error"
|
||||
|
||||
----------------------
|
||||
epicsBusErrorInit68040
|
||||
Initialize the bus error handling system. ONLY needs to be run if
|
||||
a bus error monitoring record is NOT present in the database. The
|
||||
ai record device support above runs with function automatically. You
|
||||
need to run this function before iocInit in your vxWorks startup
|
||||
script if you do not use the ai record support described above and
|
||||
have a record for it in the database.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <types.h>
|
||||
#include <vme.h>
|
||||
#include <vxWorks.h>
|
||||
#include <sysLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <semLib.h>
|
||||
#include <intLib.h>
|
||||
|
||||
#include <dbScan.h>
|
||||
#include <devSup.h>
|
||||
#include <callback.h>
|
||||
#include <aiRecord.h>
|
||||
|
||||
struct accessFault
|
||||
{
|
||||
unsigned short sr; /* status reg */
|
||||
unsigned short* pc; /* program counter */
|
||||
unsigned short vo; /* vector offset */
|
||||
unsigned long ea; /* effective address */
|
||||
unsigned short ssw; /* special status word */
|
||||
unsigned short wb3s; /* write-back 3 status */
|
||||
unsigned short wb2s; /* write-back 2 status */
|
||||
unsigned short wb1s; /* write-back 1 status */
|
||||
unsigned char * fa; /* fault address */
|
||||
union {
|
||||
unsigned long* l; unsigned short* s; unsigned char * c;
|
||||
} wb3a; /* write-back 3 address */
|
||||
union {
|
||||
unsigned long l; unsigned short s; unsigned char c;
|
||||
} wb3d; /* write-back 3 */
|
||||
union {
|
||||
unsigned long* l; unsigned short* s; unsigned char * c;
|
||||
} wb2a; /* write-back 2 address */
|
||||
union {
|
||||
unsigned long l; unsigned short s; unsigned char c;
|
||||
} wb2d; /* write-back 2 */
|
||||
union {
|
||||
unsigned long* l; unsigned short* s; unsigned char * c;
|
||||
} wb1a; /* write-back 1 address */
|
||||
union {
|
||||
unsigned long l; unsigned short s; unsigned char c;
|
||||
} wb1d; /* write-back 1 / push data LW0 */
|
||||
unsigned long pd1; /* push data LW1 */
|
||||
unsigned long pd2; /* push data LW2 */
|
||||
unsigned long pd3; /* push data LW3 */
|
||||
};
|
||||
typedef struct accessFault accessFault;
|
||||
|
||||
struct faultData
|
||||
{
|
||||
unsigned short* pc;
|
||||
unsigned char * fa;
|
||||
unsigned short sr;
|
||||
unsigned short ssw;
|
||||
unsigned char flags; /* 0x01:write back incompleted, >0: valid bus error */
|
||||
long cnt;
|
||||
};
|
||||
typedef struct faultData faultData;
|
||||
|
||||
#define TOTAL_FAULTS 20
|
||||
#define ACCESS_FAULT 2
|
||||
#define BS_PRI 199
|
||||
#define NUM_RETRIES 10
|
||||
|
||||
long epicsBusErrorInit68040(void);
|
||||
long epicsBusErrorHandler(void* v);
|
||||
void epicsBusErrorPrint(void);
|
||||
void epicsBusError(void);
|
||||
|
||||
volatile long epicsBusErrorTotal=0;
|
||||
volatile long epicsBusErrorLastRC=0;
|
||||
static volatile faultData* fault_table=NULL;
|
||||
static volatile int pos_handler=0;
|
||||
static unsigned char * be_mem_top=NULL;
|
||||
static unsigned char * be_mem_bottom=0;
|
||||
static FUNCPTR* vbr;
|
||||
static FUNCPTR vx_access_fault;
|
||||
static IOSCANPVT ioscan;
|
||||
static unsigned short* curr_pc=NULL;
|
||||
static unsigned short* last_pc=NULL;
|
||||
static unsigned short count=0;
|
||||
static unsigned long regs[64];
|
||||
static unsigned long* regs_addr=regs;
|
||||
static int init_run=0;
|
||||
|
||||
FUNCPTR* epicsGetVBR()
|
||||
{
|
||||
unsigned long x;
|
||||
asm ("movec vbr,%0" : "=g" (x) );
|
||||
return (FUNCPTR*)x;
|
||||
}
|
||||
|
||||
/* ------------------- ai record to monitor bus errors -------------------- */
|
||||
struct aStats
|
||||
{
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_write;
|
||||
DEVSUPFUN special_linconv;
|
||||
};
|
||||
typedef struct aStats aStats;
|
||||
|
||||
static long aiInit(int pass)
|
||||
{
|
||||
if(pass) return 0;
|
||||
if(init_run==0)
|
||||
{
|
||||
epicsBusErrorInit68040();
|
||||
init_run=1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long aiInitRecord(aiRecord* pr)
|
||||
{
|
||||
unsigned long* x = (unsigned long*)&pr->dpvt;
|
||||
x=0;
|
||||
pr->linr=0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long aiIointInfo(int cmd,aiRecord* pr,IOSCANPVT* iopvt)
|
||||
{
|
||||
*iopvt=ioscan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long aiRead(aiRecord* pr)
|
||||
{
|
||||
unsigned long* x = (unsigned long*)&pr->dpvt;
|
||||
pr->val=++(*x);
|
||||
pr->udf=0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
aStats devAiBusError=
|
||||
{ 6,NULL,aiInit,aiInitRecord,aiIointInfo,aiRead,NULL };
|
||||
|
||||
/* ------------------- ai record to monitor bus errors -------------------- */
|
||||
|
||||
long epicsBusErrorInit68040(void)
|
||||
{
|
||||
long rc=0;
|
||||
int i;
|
||||
|
||||
if(init_run)
|
||||
return 0;
|
||||
else
|
||||
init_run=1;
|
||||
|
||||
vbr=epicsGetVBR(); /* vbr=intVecBaseGet(); */
|
||||
be_mem_top=(unsigned char *)sysMemTop();
|
||||
vx_access_fault=vbr[ACCESS_FAULT];
|
||||
scanIoInit(&ioscan);
|
||||
|
||||
/*
|
||||
printf("VBR=0x%8.8x\n",vbr);
|
||||
printf("MemTop=0x%8.8x\n",be_mem_top);
|
||||
printf("vxWorks Access Fault Handler=0x%8.8x\n",vx_access_fault);
|
||||
*/
|
||||
|
||||
/* up to TOTAL_FAULTS number of interrupts can occur before printing */
|
||||
fault_table=(volatile faultData*)malloc(sizeof(faultData)*TOTAL_FAULTS);
|
||||
for(i=0;i<TOTAL_FAULTS;i++) fault_table[i].flags=0x00;
|
||||
|
||||
/* replace the bus error handler from vxWorks */
|
||||
vbr[ACCESS_FAULT]=(FUNCPTR)epicsBusError;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void epicsBusErrorPrint(void)
|
||||
{
|
||||
volatile int pos;
|
||||
int i;
|
||||
|
||||
for(i=0;i<TOTAL_FAULTS;i++)
|
||||
{
|
||||
if(fault_table[i].flags)
|
||||
{
|
||||
printf("%ld: FA=0x%8.8x PC=0x%8.8x SR=0x%4.4x, SSW=0x%4.4x ",
|
||||
fault_table[i].cnt,
|
||||
fault_table[i].fa,
|
||||
fault_table[i].pc,
|
||||
fault_table[i].sr,
|
||||
fault_table[i].ssw);
|
||||
switch(fault_table[i].flags)
|
||||
{
|
||||
case 0x01: /* write back failed */
|
||||
printf(" Write back incomplete\n");
|
||||
break;
|
||||
case 0x02: /* normal retry */
|
||||
printf(" Normal Retry\n");
|
||||
break;
|
||||
case 0x04: /* vxWorks process */
|
||||
printf(" vxWorks Handled\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* sp+60+sizeof(access fault stack frame) ---> 60+60 */
|
||||
|
||||
asm(" .text");
|
||||
asm(" .even");
|
||||
asm(" .globl _epicsBusError");
|
||||
asm("_epicsBusError:");
|
||||
asm(" moveml #0xfffe,sp@-"); /* save the registers */
|
||||
asm(" pea sp@(60)"); /* get address of access error stack frame */
|
||||
asm(" jbsr _epicsBusErrorHandler"); /* call my bus error handler */
|
||||
asm(" addql #4,sp");
|
||||
asm(" tstl d0"); /* check return code from bus error handler */
|
||||
asm(" jeq L1"); /* exit OK if zero return code */
|
||||
asm(" moveml sp@+,#0x7fff"); /* restore regs */
|
||||
asm(" movel _vx_access_fault,sp@-"); /* put vxWorks handler addr on stack */
|
||||
asm(" rts "); /* load PC from stack (vxWorks handler) */
|
||||
asm("L1:");
|
||||
asm(" moveml sp@+,#0x7fff"); /* restore regs */
|
||||
asm(" rte");
|
||||
|
||||
/*
|
||||
see section 8.4.6 of M68040 user's manual, page 8-24
|
||||
see section 5.3, page 5-5 for Transfer attribute signals (TT/TM)
|
||||
SIZ: 00=long word, 01=byte, 10=word
|
||||
TT: 00=normal, 01=move16, 10=alternate, 11=ack cycle
|
||||
TM: 001=user data, 010=user code, 101=sup data, 110=sup code
|
||||
R/W: set indicates a read access
|
||||
*/
|
||||
|
||||
long epicsBusErrorHandler(void* v)
|
||||
{
|
||||
long rc;
|
||||
unsigned char f;
|
||||
volatile int pos,posp;
|
||||
volatile accessFault* af = (volatile accessFault*)v;
|
||||
unsigned short size,op_code;
|
||||
unsigned short* ppc;
|
||||
int len;
|
||||
|
||||
curr_pc=af->pc;
|
||||
if(curr_pc==last_pc) ++count;
|
||||
|
||||
if( count<NUM_RETRIES &&
|
||||
((af->ssw & 0x0018) == 0) && ((af->ssw & 0xfc00) == 0) &&
|
||||
((af->ssw & 0x0001) || (af->ssw & 0x0005)) &&
|
||||
(af->fa >= be_mem_top) )
|
||||
{
|
||||
/* TT=normal TM=user/sup data access bus-errors */
|
||||
rc=0;
|
||||
f=0x02;
|
||||
|
||||
/*
|
||||
Don't force PC to next instruction, just retry several times
|
||||
Note: the current instruction may not have caused the fault
|
||||
*/
|
||||
|
||||
/* correct bus error here */
|
||||
if(af->wb1s & 0x0080)
|
||||
{
|
||||
/* this is where the fault occured if write */
|
||||
if(af->wb1a.c < be_mem_top)
|
||||
{
|
||||
size=(af->wb1s & 0060)>>5;
|
||||
if(size==0x0000) *(af->wb1a.l)=af->wb1d.l;
|
||||
else if(size==0x0001) *(af->wb1a.c)=af->wb1d.c;
|
||||
else if(size==0x0010) *(af->wb1a.s)=af->wb1d.s;
|
||||
}
|
||||
}
|
||||
if(af->wb2s & 0x0080)
|
||||
{
|
||||
if(af->wb2a.c < be_mem_top)
|
||||
{
|
||||
size=(af->wb2s & 0060)>>5;
|
||||
if(size==0x0000) *(af->wb2a.l)=af->wb2d.l;
|
||||
else if(size==0x0001) *(af->wb2a.c)=af->wb2d.c;
|
||||
else if(size==0x0010) *(af->wb2a.s)=af->wb2d.s;
|
||||
}
|
||||
else
|
||||
f|=0x01;
|
||||
}
|
||||
if(af->wb3s & 0x0080)
|
||||
{
|
||||
if(af->wb3a.c < be_mem_top)
|
||||
{
|
||||
size=(af->wb3s & 0060)>>5;
|
||||
if(size==0x0000) *(af->wb3a.l)=af->wb3d.l;
|
||||
else if(size==0x0001) *(af->wb3a.c)=af->wb3d.c;
|
||||
else if(size==0x0010) *(af->wb3a.s)=af->wb3d.s;
|
||||
}
|
||||
else
|
||||
f|=0x01;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
f=0x04;
|
||||
rc=-1; /* let vxWorks handle it */
|
||||
}
|
||||
|
||||
if(count==0 || count>=NUM_RETRIES)
|
||||
{
|
||||
if((++pos_handler)>=TOTAL_FAULTS) pos_handler=0;
|
||||
fault_table[pos_handler].pc=af->pc;
|
||||
fault_table[pos_handler].fa=af->fa;
|
||||
fault_table[pos_handler].sr=af->sr;
|
||||
fault_table[pos_handler].ssw=af->ssw;
|
||||
fault_table[pos_handler].cnt=epicsBusErrorTotal;
|
||||
fault_table[pos_handler].flags=f;
|
||||
|
||||
if(count>=NUM_RETRIES)
|
||||
{
|
||||
count=0;
|
||||
curr_pc=NULL;
|
||||
}
|
||||
else
|
||||
scanIoRequest(ioscan);
|
||||
|
||||
++epicsBusErrorTotal;
|
||||
}
|
||||
|
||||
epicsBusErrorLastRC=rc;
|
||||
last_pc=curr_pc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user