2. 1750 Documentation Interrupt Handling Only the six hardware interrupts are ever enabled. The one-second interrupt is masked off, and one-second ticks are counted instead by rollovers of the 4 ms counter. At 1750 startup, only those interrupts whose detenbuff bit is set are enabled. The detenbuff bits are read from the eeprom at system startup, and after that they may be modified by command. However, the interrupt mask register is only written once, at 1750 startup. Therefore, to disable an interrupt in hardware, you must command the detenbuff bit off, then reset the 1750. Toggling detenbuff without resetting only disables events from that detector in software. The difference is that when the detector is hardware-disabled, its interrupts are ignored and it times out. When it is software-disabled, its interrupts cause the fifo to be read out but the event to be rejected early in the event processing, as though the detector were enabled but its high voltage had not come up yet. Event Timing Local time is kept in a two-word counter, curtime[0..1]. The time is updated in the routine update_curtime(), and the software keeps the time updated by calling this routine frequently. It is called from: det_int() detector interrupt service routine wait_int() background loop ee_to_ilmm() write eeprom to ilmm map_to_ilmm() write map to ilmm second_isr() one-second processing The lower 8 bits are read from the time port, which counts up in 4 ms ticks. (Or is it 256 hz ticks?) The upper 24 bits count rollovers of the 8-bit time register. The software keeps track of the rollovers by checking to see if the lower 8 bits are smaller by more than 1/2 second this time than last time. Because update_curtime() is called much more often than once per second, this method is pretty foolproof. The conversion to spacecraft time is done by recording the local time at the moment the spacecraft time becomes available (once every 4 seconds, on the 8086) and using the difference between them as a correction to apply to local time to get spacecraft time. Event Processing The 1750 software maintains an event queue 31 elements deep. Events are written into the queue by the event interrupt service routine, with the event time latched when the event is placed in the queue. Events are taken out of the queue and processed in the background routine, when nothing else is going on. The longest time delay between an event's arrival and its processing is when the 64 second housekeeping is collected (it could be even longer if there is special 1-second housekeeping going on then too). The event processing cycle is described below in two parts: the event interrupt service routine, and the event processing routine. det_int() This is the actual event interrupt service routine. All six of the hardware detector interrupts are vectored to this one routine. The hardware interrupts operate according to a fixed priority hierarchy defined by the 1750 chip, but a rotating priority scheme is implemented in software. No matter which detector caused the interrupt, the software reads all six detectors in the same order, bit 5 to bit 0 in the order in which the bits appear in the 1750 interrupt registers. If any detector has its bit set in both the pending interrupt register and the mask register, then get4adc() is called to read out the hardware fifo, with the detector number in idet. get4adc() read 4 values from fifo, checking tag bits to make sure the values are in the correct order. If tag bits are incorrect, then increment fifo_ooo[idet] and try to reset the fifo for next time. If the tag bits are good, then place the event in the queue, latch the current local time and place it in the event queue too. end get4adc() Update the event queue pointer. If there's no more room in the event queue, increment queue_overflow[idet] instead. Reset the pending interrupt bit for this detector (reading out the fifo cleared the interrupt, and it's too soon for another event to have been digitized, so there's no race here). end det_int() detector_isr() Despite its name, this is not an interrupt service routine. It gets called from the background loop, wait_int(), when there is something in the event queue for it to process. The first bit of event processing is done within getxyphar(). The original intent was simply to dequeue the event and separate out the various fields, but since then a bunch of other checks have been added. There's no longer any particular reason for the event processing to be broken up this way, except to add confusion. getxyphar() The event is removed from the event queue. Fields are extracted from the raw event and stored as follows: rawbuffer[0..5] raw event from queue eventtime[0..1] time words (rawbuffer[4..5]) adcxy[0..3] Y-, Y+, X-, X+ (these have baselines subtracted in range_check() ) pdet detector number 0 - 5 rise rise time bit anti anti bit uldiscri ul discrim bit pedestal pedestal bit X9 9-bit x (computed below) Y9 9-bit y ( " ) x 7-bit x ( " ) y 7-bit y ( " ) pha energy ( " ) If the pedestal bit is set, then the baseline offsets are updated, and the event is rejected if tmgate bit 14 is zero. If the detector has not reached the proper high voltage yet (i.e. detenable bit is zero) the event is rejected. "Rejected" means that no further processing is done, the event is discarded, and control returns to the background loop. Two counters are incremented at this point: llevent (counts all but pedestal events), and aboveevent (counts events with ul discriminator bit set; note that aboveevent also counts events above pha compress map -- see below). Also, if tmgate bit 6 is zero, events with ul discrim bit set are rejected. Now range_check() gets called to make sure various fields are within normal ranges. range_check() Subtract baselines in adcxybase[pdet][0..3] from adcxy[0..3] and store back in adcxy[]. Negative values are permitted at this point. If any adc value is saturated (4095 before subtraction) then count event into saturated and reject if tmgate bit 8 is zero. end range_check() Next, the x, y, and energy values are computed. A noise check is done (3*max > 7*min), and if exceeded then event is counted into noise and rejected if tmgate bit 10 is zero. X and Y are gamma-corrected, truncated to fit within the range 0 - 511 (9 bits), and saved as X9, Y9. Then the nonlinear correction is applied. The adc values are summed, and if either the x-sum or the y-sum is negative, the event is counted into small, and rejected if tmgate bit 9 is zero. The sum is scaled by e_scale and stored in pha. Finally the parallax correction is applied if parlxenab is 1 and the x-sum is less than the y-sum, and the final x and y values are truncated to fit within the range 0 - 127 (7 bits) and stored in the variables x and y. Gain tracking is done as follows. When the 8086 sends out pulser events, it sets pulsedet equal to the pulsed detector number (if no pulser events, -1). If an event comes through on that detector, whose pha is greater than 200, with gainenab set to 1, then it's a pulser event, and the gain ratio is histogrammed. The event is rejected if tmgate bit 12 is zero. If the high voltage for this detector is off (i.e. the hvisonoff bit for this detector is zero), then the event is rejected if tmgate bit 11 is zero. end getxyphar() Back in detector_isr(), a series of checks is made. Counter tmgate bit Type of event put into telemetry if bit set to 1 rtevent 1 rise time event (rise ==1) antievent 0 anti event (anti == 1) aboveevent 6 above pha compress map (also ul bit) If the event passes these checks, the pha is compressed according to phacompressmap[0..16]. Next, the hot spots are checked, and then the anti-spots. If the event falls into one of the hot spots or out of the anti-spot, the corresponding spotaccum[] counter is incremented, the rtreject counter is incremented, and the event is rejected if tmgate bit 4 is zero. Counter tmgate bit Type of event put into telemetry if bit set to 1 rtreject 4 event within hot spot Next, if the event falls within the range of the fe55 histogram, it is histogrammed in x and y (using 9-bit values). If the event falls within one of the fe55 spots, it is histogrammed in energy using the fe55 compression map, the fe55rej counter is incremented, and the event is rejected if tmgate bit 3 is zero. Counter tmgate bit Type of event put into telemetry if bit set to 1 fe55rej 3 event within fe55 spot Next, the event is histogrammed into the sun histograms. If the event is inside the current sun box, the sunrej counter is incremented and the event is rejected if tmgate bit 5 is zero. Counter tmgate bit Type of event put into telemetry if bit set to 1 sunrej 5 event within sun spot At this point, the event is called "valid" if either the rise or anti bit is set, or it was rejected because of one of the above tests but placed in telemetry because of the tmgate bit. Events that are not valid are rejected here if tmgate bit 2 is zero. Counter tmgate bit Type of event put into telemetry if bit set to 1 [none] 2 invalid event Each of the counters thottletm[0..3] is incremented for this detector. A random number is generated, and if it exceeds the thottle for that detector, thottlerej is incremented and the event is rejected. Otherwise, tmevent is incremented (and so is tmevent8086, the 8086 version of the same counter), totrate is incremented (counts all detectors; tmevent counts each detector separately). The time tag is computed. The event time was latched and written into the event queue when the detector interrupt was serviced. The interarrival time between this event and the last is compressed according to tbinning, and if it is 7 or less, it is the time tag written with the event. Otherwise, the spacecraft time is computed and written into the science buffer and the time tag is set to zero. The event itself is written into the science buffer, and in raw event mode it is followed by the raw event. When the science buffer fills up, it is copied into the next available ILMM buffer. end detector_isr() Baseline Calibration The 8086 generates a sequence of pedestal events at an average rate of 4 Hz in each detector. There are some gaps in this sequence when the time-consuming housekeeping generation task runs. The 8086 counts each pedestal event that it generates, by detector, in peds_sent[]. The 1750 recognizes pedestal events by a tag bit. When it sees one, it calls update_offset() to keep the running average of the baseline, and then checks tmgate bit 14 to see if event processing should continue (zero means reject event). The results of the running averages are kept in the variables cal_adcxybase[0..5][0..3] (first index detector number, second index adc channel). Two EEPROM variables control the operation of the baseline calibration. The first is pedenab, a bit array (one bit per detector, lsb == lowest-numbered detector), which controls whether the pedestal calculation is done at all (zero means skip pedestal calculation and don't even increment counters). The second is pedupdate, also a bit array, which controls whether the calculated baselines in cal_adcxybase are used to update the operational baselines in adcxybase (zero means don't update). update_offset() Check pedenab bit for this detector, and skip processing if it is zero. Count event into peds_got[pdet]. Compute "sigmas" for this detector as running averages (1023 times previous sigma, plus absolute value of difference between adc reading and baseline, divided by 1024) (four sigmas kept, one for each adc channel). The remainder from this integer division is stored and will be summed in to the next numerator, so the running average will actually converge. Check to see if this event is more than 8*sigma from the baseline in any channel. If so, then count the event into peds_rejected[pdet] and don't average this event into baselines. Compute new baselines for this detector as running averages (1023 times previous baseline, plus new baseline, divided by 1024). As before, the remainder is kept and summed in to the numerator the next time the running average is computed. If pedupdate bit for this detector is set to 1, copy the running averages into the baselines that are actually subtracted from the raw event adc readings. Otherwise, continue to use the existing baselines. end update_offset() Gain Tracking The 1750 starts things off. At the appropriate moment (the start of some phase 5 in the 8-minute 64-second housekeeping cycle) it sets pulserflag to 1. When the 8086 sees this, it starts a test pulser run, consisting of 250 pulses (25 groups of 10 pulses each) from each detector whose gain tracking is enabled. These pulses come in to the 1750 during the 64-second interval between phase 5 and phase 6, and they cause x-axis gain tracking computations in the 1750. This information is read out in phase 6. At the end of phase 6 data readout, the 1750 sets pulserflag to 2. This causes the 8086 to start another identical test pulser run, but when the pulses come in to the 1750 during the interval between phase 6 and phase 7 they cause y-axis gain tracking computations. This information is then read out in phase 7. The timing of the gain tracking test pulser runs is controlled by pulsetime, which is an EEPROM parameter representing the number of 8-minute intervals between test pulser runs. If pulsetime is zero, there are no test pulser events. If it is 1, there is a test pulser run in each 8-minute housekeeping cycle. Gain tracking for each detector can be enabled or disabled individually by the gainenab bit array (lsb == lowest number detector, 1 == enable). Moreover, any detector whose detenbuff bit is zero will not be pulsed, no matter what gainenab is set to. Test pulser events are recognized in the 1750 by three criteria: (1) the detector number is the same as the one currently being pulsed; (2) the gainenab bit for the detector is set to 1; (3) the computed pha is greater than 200. When all three are true, the 1750 computes the x-ratio or the y-ratio, depending on whether pulserflag is 1 or 2, according to the formula r = 2**18*(X+) / gamma*(X-). It checks to see whether the ratio is within plus or minus 16 channels of 512. If so, it is added to the 32-channel gamma histogram, and counted in pulses_got[0..5]. If not, it is treated as though it were not a test pulser event (so this is really a fourth criterion for a test pulser event). Because the gamma histograms are telemetered, there is little need for the actual gains to be calculated. Nevertheless, the 1750 does it anyway. It happens in update_gain(), which gets called after the gamma histograms are read out in phases 6 and 7 and before they are cleared for next time. update_gain() Find median channel of histogram new gain =( old gain * median) / 512. Save new gain as cal_gamma[0..5][0..1] (0 == x, 1 == y) end update_gain() Housekeeping and Timed Operations The housekeeping system is driven by what used to be the one-second interrupt service routine, second_isr(). It is no longer interrupt-driven, but instead is called from the background loop wait_int(), after update_curtime() notices that the lower 8 bits of time have rolled over. second_isr() This routine is entered once per second from the background loop. It increments the data cycle counter, datacycle1750, and takes action depending upon the new value of the counter. The routine is divided into one-second operations, four-second operations, 16-second operations, and 64-second operations; which actions are taken depends on whether the data cycle counter is zero mod 2, 4, 16, or 64. For the housekeeping, a RAM buffer is maintained in which the next housekeeping block is built over a 64-second period. One second operations Read rate counters and accumulate into buffers. This is done in ratedump(). It uses a table, cmdreadrate, that is initialized with data in the PROM at 1750 startup time. Each entry in the cmdreadrate table has 4 addresses: counter select, counter xfr, counter clear, and counter read. There are 24 entries. The counts read from each counter are added into accumulation buffers rate8086[0..5][0..3] and rate1750[0..5][0..3]. These are kept in double precision, and cleared asynchronously after they are read out into telemetry. One additional accumulation buffer, regcoinulrate, is also maintained here. Check to see if spacecraft onboard time has been received by 8086, and compute reference time accordingly. Accumulate some additional rate counters: llscale, llratesec, antiscale, ulscale, coinulscale, ratehvoff. Check to see if there was an ILMM error (inbufferr, set when trying to write to ILMM) and call illmcheck() if so (I don't know what this really does, since ilmmcheck() is the routine that sets inbuferr in the first place). Do special one-second housekeeping if necessary. The special one-second housekeeping is controlled by dumpsec (number of seconds to dump). Every second that this is active, the 1750 writes nearly all of its local data areas into two blocks in the ILMM. The first block begins with rate8086 (0x567) and ends with fe55pha. The second block begins with small (0x8eb) and ends somewhere in the middle of eebuffer. Each block contains 900 words. Therefore, to figure out where any variable is, just subtract the address of either rate8086 or small from its address and look that far into block 1 or block 2, respectively. Do regular one-second housekeeping. This involves reading out the high voltage of one detector (specialdet) at one-second intervals and placing the 64 readings thus obtained in the 64-second block. Two-second operations There's a stub of code for two-second operations, which for all I can tell does nothing. Four-second operations Compute deadtime[0..5] from some complicated formula using some other rate counters. do_4sechkblock() Store 12 rates times 6 detectors starting with llevent[0..5] in compressed form. Note that this requires that these rates remain in contiguous storage. Store upper byte of thottle[0..5], deadtime[0..5]. end do_4sechkblock() Clear the rate counters just read out. Clear regcoinulrate, even though it wasn't read out. Compute mmavail, memory available in ILMM. 16-second operations do_16sechkblock() Store three compressed rates (8, 6, 6) and lower byte of suntable[0..3][0..5]. end do_16sechkblock() Clear the rate counters just read out (don't clear suntable). Compute the new position of the sun. As Phong notes, this is simply a line-by-line translation of Ed Fenimore's Fortran simulation code. That's the best reference for this part of the algorithm. Check the high voltage and set the detector enable bit accordingly. When a high voltage setting is commanded, the commanded value goes in hvtarget. The 8086 does the actual stepping up and down of the high voltage, increasing or decreasing hvpresent by the hvstepup or hvstepdown increments. This routine simply checks to see if hvpresent has reached hvtarget, and that the detenbuff bit is set to 1. If so, it sets detenable[0..5] to 1. 64-second operations do_64hkblock() The 64-second housekeeping comes in 8 phases, so the full cycle takes about 8 minutes. The data for each phase, together with the accumulated one-second, four-second, and 16-second housekeeping, are written to the ILMM every 64 seconds. Each phase contains a common data block that appears in every phase (command stack, swhappen, plus some counters). Phases 0-5 contain data for detectors 0-5 -- the same data, in the same order, but for different detectors. Phases 6 and 7 contain other stuff. The code is bulky but straightforward if you have the housekeeping format to refer to. Phases 5 and 6 also start the gain tracking algorithm on the 8086. The only tricky thing about the housekeeping code is that there are some pointers into the housekeeping block that are set up by select_hk_buffer() and maintained by the routines that write housekeeping data into the block in variou