This is qp.c in view mode; [Download] [Up]
/* QP (QuintProcessor) board support stuff * * for system 3.0 NeXTs * on system 2.0, look for "system 2.0" and follow the directions * * Basic library of code supplied by Ariel is in /dist/QPdist/lib/source. * The following is a translation of their code to our way of doing things. * (All we want is low-level access to each host interface, and a way to reset each dsp). * */ #include "stdio.h" #include <sys/file.h> #include <sys/ioctl.h> #include <sys/fcntl.h> #include <sys/stat.h> /* #include <sys/errno.h> */ /* this line not needed in system 1.0? */ /* on system 2.0, un-comment-out the previous line */ extern int errno; /* on system 2.0, comment out the previous line */ #include "/NextDeveloper/Headers/bsd/sys/errno.h" /* on system 2.0, comment out the previous line -- last of 2.0/3.0 changes */ /* #include "/dist/QPdist/lib/source/ndslot.h" */ /* also exists on clm distribution area */ #include "ndslot.h" #include <signal.h> #include <setjmp.h> #include <libc.h> #ifndef AKCL #include "clmdsp.h" /* <dsp/dsp.h> and hostInterface declaration */ #endif AKCL /* 1.0 -- #define QP_ID 0x8001 */ #define QP_ID 0x80018000 /* QP.h */ #define RST_NMI 0x803 /* 1.0 -- 0x200 */ /* RESERVED/SIMULT was 0x100, now 0x403 etc -- data in byte 3 in system 2.0 */ static int got_interrupt = 0; int QP_get_interrupt(void) {return got_interrupt;} QP_clear_interrupt(void) {got_interrupt = 0;} static jmp_buf err_buf; static void sys_err(int sig) { got_interrupt = sig; longjmp(err_buf,sig); /* I believe this is C's non-local GOTO */ } static int dspFd = -1; static int current_slot = -1; static int current_dsp = -1; static char *slotbase; static QPRegs *hi[16]; /* array of host interface structures */ static void QP_close_driver(void) { if (dspFd != -1) { ioctl(dspFd,SLOTIOCDISABLE,(void *) 0); close(dspFd); dspFd = -1; } } static int QP_reset_dsp(int dsp) { void (*old_SIGBUS)(); /* signals in unix are handled with int (*signal (int sig void (*func)(int))) int, if that makes sense */ /* (i.e. signal is a function that returns a pointer to a function that returns an integer) */ /* SIGBUS is bus error. SIGSEGV is segmentation violation */ void (*old_SIGSEGV)(); char val; int i; /* extern void usleep(unsigned int useconds); */ /* It is my belief that usleep is incompatible with Lisp's Trace function */ /* -- with usleep we often hang for no apparent reason during qp-boot */ /* here we are apparently preparing to catch a bus error or a segmentation error. We set the new handler */ /* to be sys_err, which does a jump to the last destination set by setjmp (this is pure conjecture) */ /* signal returns the previous handler so that we can restore it later */ old_SIGBUS = signal(SIGBUS,sys_err); old_SIGSEGV = signal(SIGSEGV,sys_err); if (setjmp(err_buf) != 0) /* this (I believe) sets the destination of a subsequent longjmp */ { /* if passed a signal at the longjmp, this returns non-zero (apparently) */ /* so we get here if we got an interrupt. We then restore the old handlers */ /* and return 1. */ (void) signal(SIGBUS,old_SIGBUS); (void) signal(SIGSEGV,old_SIGSEGV); return(1); } val = ~(1 << (dsp+3)); /* RESET is active low */ /* val = ~(1 << (dsp+5)); */ /* dsp is 0..4 here (Ariel doc p59) */ *((unsigned char volatile *)(slotbase+RST_NMI)) = val; /* usleep(10000); */ /* this is a VERY long wait -- surely it's not necessary ?!? */ /* at power on, the on-chip oscillator can take 3.75 ms to stabilize */ /* but NeXT takes 1000's of times longer than that to get going. */ /* Thereafter, this stabilization happens only when we restart (via reset) after */ /* executing a STOP command, which will never happen in clm -- I guarantee it! */ /* I also never use a WAIT or SWI command, in case it matters */ /* usleep(100); */ for (i=0;i<1000;i++); *((unsigned char volatile *)(slotbase+RST_NMI)) = 0xFF; (void) signal(SIGBUS,old_SIGBUS); (void) signal(SIGSEGV,old_SIGSEGV); return(0); } static int QP_open_driver(char *name) /* name == "/dev/slot2" for example */ { struct stat stbuf; if (stat(name,&stbuf) == -1) return(1); if ((stbuf.st_mode & S_IFCHR) == 0) return(2); /* is it a "character special" device */ if ((dspFd=open(name,O_RDWR)) == -1) return (3); /* no driver? */ /* slotbase = 0; */ /* this line causes the QP to be flakey on 3.2! */ if (ioctl(dspFd,SLOTIOCGADDR_CI,(void *) &slotbase) == -1) { QP_close_driver(); return(4); /* can't map address */ } if (ioctl(dspFd,SLOTIOCNOSTOFWD,(void *) 0) == -1) { QP_close_driver(); if (errno == ENXIO) /* sys/errno.h => no such device */ return(5); /* new driver, but no NBIC */ else return(6); /* old QP driver somehow */ } return(0); } static int QP_open_normal_driver_for_slot(int slot) { int i; switch (slot) { case 2: /* slot2 (normal address space) */ i = QP_open_driver("/dev/slot2"); break; case 4: i = QP_open_driver("/dev/slot4"); break; case 6: i = QP_open_driver("/dev/slot6"); break; } return(i); } static int QP_open_s_driver_for_slot(int slot) { int i; switch (slot) { case 2: i = QP_open_driver("/dev/slots2"); /* try to open slots 2 (i.e. slot space where MFG code is and so on) */ break; /* see Ariel doc p56 or maybe NextBus Specification */ case 4: i = QP_open_driver("/dev/slots4"); break; case 6: i = QP_open_driver("/dev/slots6"); break; } return(i); } static int QP_get_board_id(unsigned *bdId) { void (*old_SIGBUS)(); void (*old_SIGSEGV)(); old_SIGBUS = signal(SIGBUS,sys_err); old_SIGSEGV = signal(SIGSEGV,sys_err); if (setjmp(err_buf) != 0) { (void) signal(SIGBUS,old_SIGBUS); (void) signal(SIGSEGV,old_SIGSEGV); return(1); } *bdId = *((unsigned char volatile *)(slotbase + 0xFFFFF0)); /* msbyte of Mfg Code */ *bdId <<= 8; *bdId |= *((unsigned char volatile *)(slotbase + 0xFFFFF4)); /* lsbyte of Mfg Code */ /* 1.0 -- stopped here. Next 4 lines for 2.0 */ *bdId <<= 8; *bdId |= *((unsigned char volatile *)(slotbase + 0xFFFFF8)); //msbyte of Board ID *bdId <<= 8; *bdId |= *((unsigned char volatile *)(slotbase + 0xFFFFFc)); //lsbyte of Board ID /* 2.0 -- end of addition (board ID) */ (void) signal(SIGBUS,old_SIGBUS); (void) signal(SIGSEGV,old_SIGSEGV); return(0); } static void QP_align_slot_offsets(int slot) { int i; for (i=0;i<5;i++) /* set up dsp host interface pointers */ { /* slotbase set as side effect of QP_open_driver */ /* 1.0 -- hi[i+((slot/2-1)*5)+1]=(DSPRegs *)(slotbase+(8<<i)); */ hi[i+((slot/2-1)*5)+1]=(QPRegs *)(slotbase+(32<<i)); } } static int QP_check_slot(int slot) /* check a slot to see if it has a QP board */ { int i; unsigned boardID; signal(SIGBUS,SIG_DFL); /* turn on bus error signal */ /* Is this really what we want? What is the default (SIG_DFL) handler for a bus error? */ i=QP_open_s_driver_for_slot(slot); if (i != 0) { signal(SIGBUS,SIG_IGN); /* SIG_IGN = ignore (bus) error */ return(i+(slot-1)*100); /* no slots2 driver here (or whatever) */ } i = QP_get_board_id(&boardID); /* is it QP board? */ QP_close_driver(); /* don't need slots2 anymore */ signal(SIGBUS,SIG_IGN); /* turn off bus error */ if (i == 0) /* found board ID */ { /* boardID = (boardID & 0xFFFF); */ /* just MFG ID */ if (boardID == QP_ID) /* hooray -- it's a QP board in this slot */ { i = QP_open_normal_driver_for_slot(slot); if (i != 0) return(i+slot*100); /* no slot2 driver (or whatever) */ QP_close_driver(); /* got what we needed... */ return(0); /* 0 returned only if successfully mapped dsps */ } } return(-1); /* no QP here, or some other problem */ } int QP_check_all_slots(int *slots) /* check all three slots for QP boards */ { /* returns 0=some QP board found; 1=no QP boards */ int i; for (i=1;i<=16;i++) { hi[i]=NULL; /* the usual paranoia ... */ } slots[0] = QP_check_slot(2); slots[1] = QP_check_slot(4); slots[2] = QP_check_slot(6); if ((slots[0] == 0) || (slots[1] == 0) || (slots[2] == 0)) return(0); return(1); } static int QP_prepare_dsp(int slot, int dsp) /* slot: 2, 4, or 6; dsp: 0..4 */ { int i; if (current_slot != slot) /* gotta close old driver (if any), open new */ { if (current_slot != -1) /* old driver still active? */ QP_close_driver(); current_slot = slot; i = QP_open_normal_driver_for_slot(slot); if (i != 0) { current_slot = -1; return(i+slot*100); } QP_align_slot_offsets(slot); /* get memory map to host interface of each dsp */ } current_dsp = dsp; return(dsp+((slot/2-1)*5)+1); /* returns hi relative number for this dsp */ } QP_all_done(void) { if (current_slot != -1) QP_close_driver(); current_slot = -1; current_dsp = -1; } /* Y:FFFF has DRAM size bits (4 and 5) * 0 0 = unused * 0 1 = 1M * 1 0 = 4M * 1 1 = 256K * see qp.lisp qp-dram-size */ /* to tell whether it's 16K or 64K, write above 32K, read back, if same = 64K, if 70000F = 16K */ /* to set auto-refresh for DRAM, set bit 7 of Y:FFFA in master (BSET 7 Y FFFA or whatever) */ int QP_boot_dsp(int slot, int dsp, int end, int *program, int monend, int memorymap) { int hi_dsp; int i; int *j,*pend; hi_dsp = QP_prepare_dsp(slot,dsp); if ((hi_dsp > 16) || (hi_dsp < 0)) return(hi_dsp); qp_HI = hi[hi_dsp]; put_qp_icr(0); i = QP_reset_dsp(dsp); if (i != 0) return(i+1000); /* now edit the monitor for QP use (the incoming monitor sets up the built-in dsp) */ if (dsp == 4) /* 0..3 are the slaves */ { /* set IO wait states to 1 */ /* STORE 1 X-IO #xFFFE */ program[monend] = 0x8F4BE; program[monend+1] = 1; /* enable master's DSPRAM */ /* BSET 1 Y #xFFFA */ program[monend+2] = 0xA7061; program[monend+3] = 0xFFFA; /* BSET 1 Y-IO #xFFFA is #xABA61 and 1 */ } else { /* turn on external RAM */ /* #xFFC2 <- #xe00000 -- i.e. set AMODE bits, set handshake reset flag */ /* LOAD A #xe00000 */ program[monend] = 0x56f400; program[monend+1] = memorymap; /* STORE A Y #xFFC2 */ program[monend+2] = 0x5e7000; program[monend+3] = 0xFFC2; /* memory map (bits 21 and 22 of FFC2) * 0 x = no ext p mem, vector mem * 1 0 = ext p, scalar mem * 1 1 = ext p, vector mem * bit 23 = enable handshake flags */ } pend = program + end; /* load monitor */ for (j=program;j<=pend;j++) put_qp_tx(*j); put_qp_icr(8); for (i=0;i<1000;i++); /* Ariel software waits here for some reason */ for (i=0;i<4;i++) program[monend+i]=0; return(0); } /* we use next56.c to deal with these guys -- just set hostInterface to whichever dsp we want to talk to */ /* hi_dsp[0] = main 56000 */ int QP_set_current_dsp (int slot, int dsp) { if (slot != 0) { int hi_dsp; hi_dsp = QP_prepare_dsp(slot,dsp); /* just in case we are changing slots */ if ((hi_dsp > 16) || (hi_dsp < 0)) return hi_dsp; qp_HI = hi[hi_dsp]; } else return 0; return -1; } int QP_is_open (void) { if (dspFd == -1) return(0); return (-1); } QP_report_hi (int *his) { int i; for (i=1;i<16;i++) { his[i]=(int)hi[i]; } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.