WORK ON MULTIPLY-DIVIDE DETERMINISTIC ERROR
IBM's CPU diagnostic program exercises all the instructions of the machine, trying all the variants and testing all the edge cases to validate the correct operation. At the end, the program runs a multiply-divide test which is where we are getting an error stop.
The program starts with the largest negative integer, multiplying that number then dividing it, with four chosen other integers that would stress edge cases. The outcome should be zero for each of those multiply-divide pairs, otherwise we get an error stop. Once the four chosen integers are used, out original integer is bumped up by one and we do it all again. After the original integer is tested with all values from the most negative to the most positive, the test ends.
The 1130 uses 16 bit words with twos-complement format. The most negative number is -32768, or 0b1000000000000000 in binary. The most positive integer is +23767 or 0b0111111111111111 in binary. The four chosen integers that get multiplied and divided by our integer are 0x8000, 0x4000, 0xC000 and 0x2001 which are -32768, +16384, -16384 and +8193 respectively.
BACKGROUND ON MULTIPLY AND DIVIDE INSTRUCTIONS
The chosen integer is loaded into the ACC before the multiply instruction is executed. The machine shifts the value from the ACC into the EXT before it accomplishes the multiplication against the chosen integer multiplicand which is loaded from memory in each cycle as part of the multiply instruction.
A divide instruction works with a 32 bit wide value (a doubleword integer with a sign in the top bit of the first word and what normally would be a sign bit of the second word treated as just another data bit of the larger integer value). This doubleword is loaded into the ACC and EXT as a combined pair of registers. It then divides by the integer brought into the AFR as part of the Divide instruction (divisor). The result of the division is to have the result left in the ACC and the remainder in the EXT, both represented as 16 bit twos complement integers.
The multiply instruction takes a variable number of execution cycles based on an algorithm. This algorithm performs cycles where it shifts the multiplier (value in ACC originally) to the right and conditionally does adds or subtracts of the multiplicand value, continuing until all sixteen bit positions of the multiplier have been shifted but ends the multiply when the value in the ACC becomes zero. These steps are essentially adding or subtracting n to the power 2 times the AFR (because of the shifting of the results in the combined ACC and EXT registers).
For each cycle, it considers the two low order bits in the EXT and the previous type of arithmetic operation (add or subtract), to choose what it does in the cycle. This table shows the choices based on those three inputs:
Bit 14 Bit 15 Previous Next Operation
0 0 Add
0 1 Add Add
1 0 Add
1 1 Add Sub
0 0 Sub Add
0 1 Sub
1 0 Sub Sub
1 1 Sub
The hardware always shifts the ACC and EXT to the right in an execution cycle as long as the EXT itself has not become all zero. Shifting to the right continues until the low bit of the EXT changes (if we had a 1 then we shift until the low bit becomes 0, else we shift until the low bit becomes a 1. The original sign bit when we started the multiplication is saved elsewhere in the hardware and we ignore the sign bit on each cycle because the shifting of ACC+EXT put a 0 in the vacated top bit position.
When we begin the first execution cycle of a multiply the previous operation is considered to have been an Add, the multiplier was put in the EXT register and we shift until we find a 1 bit in the low position of the EXT. We count how many positions are shifted until we reach 16, then the multiply ends.
Lets take a simply case where we are multiplying an integer by the chosen integer value 0x4000 to see how this works. The first cycle of the multiply will start with the ACC and EXT as 0x0000 0x4000 then it shifts until the single one bit in EXT reaches the rightmost bit of EXT. We add the integer from the multiplicand into the ACC then shift the ACC + EXT to the right looking for the next bit to be 0. However we reach a shift count of 16 so the multiply stops. The multiplicand value is now in the ACC, meaning that the integer was multiplied by +16384. It looks like the original integer but shifted one place to the right.
For the case of the chosen integer 0x2001, we start with the ACC + EXT having 0x0000 and 0x2001 in them. We have a 1 in the low bit position so our operation is to add the multiplicand into the ACC. The next cycle shifts until it finds a one bit in the low position of the EXT. We then add the multiplicand again, equivalent to multiplying the multiplicand by +8192 and adding it to the running total. That gives us 8193 times the multiplicand in the combined ACC + EXT registers and we shift until we have done all 16 positions.
The other two chosen values just start us with negative numbers, either -32768 or -16384 instead of positive values. Our ACC and EXT begin with 0x0000 0x8000 or 0x0000 0xC000 and we remembered the negative sign this time. The rules for shifting and arithmetic operations will produce the correct result in ACC + EXT.
The divide instruction takes a fixed number of execution cycles - 18 - unless the value of the result would exceed the largest integer values of -32768 to +32767 in which case the divide stops with an overflow error. It always shifts the ACC + EXT one position to the left in a cycle, but choses whether to add or subtract the divisor value based on whether the top bit of the ACC and of the EXT are the same or not. It checks for overflow, where the result would be larger than the biggest positive or negative integer, and stop if that error occurs.
LOOKING AT THE ERROR
When the diagnostic stopped with wait code 0x316D I looked at the value of the integer and which of the chosen integers was multiplied and divided to do the test. It was the first phase, using 0xC000 as the chosen value, because index register 1 was at its initial value of 4. The stop was with 0xFFFB in the ACC and 0x0000 in the EXT register.
Looking at the fact that both multiply and divide will re-fetch a memory location on each execution cycle for the multiplicand or dividend, if one of the memory fetches returns a different value when we could have the symptoms being reported here.
I set the machine to display the value in 0x0CFB which is where the integer began as the largest negative number and is bumped by one after each multiply-divide pair. The machine gave a parity check while displaying 0xE1F4. I believe the error is with bit 13 being on incorrectly. I stored the value 0xE1F0 in the location and tried to display it, but again got a parity check with 0xE1F4 returned.
STRANGE REPEATIBLE BEHAVOR DISPLAYING THAT ADDRESS
Strangely, I could repeatedly put the machine in Load mode, set that address and write the intended value 0xE1F0 yet when I put it in Display mode at address 0x0CFB the machine returns 0xE1F4 with a parity check each time.
If I use the Storage Load mode to set all of memory to some pattern - 0x0000 or 0xffff or 0xE1F0 - then I put the machine in the Storage Display mode to continuously read and display all memory locations, it does NOT get a parity check.
MUSINGS ABOUT THE RESULTS
My memory board returns the 16 bits of the word by setting the Storage Buffer Register (SBR) bit to 1 if the memory value is 1, otherwise it does nothing so that the SBR bit remains 0. The parity bits are generated on the fly by my board based on the 1 and 0 values it will set in the SBR, thus it should NEVER get a parity check. In the past, I got parity checks when a pulse from my board intended to set the SBR bit to a 1 but the pulse was too marginal to actually set the bit.
The situation here appears to be the opposite. Bit 13 of the SBR is turning on but my board is generating parity as if bit 13 is a zero. The pulse is only generated if the MRAM chip on my board outputs a 1 for that bit position, yet the bit being output also generates the parity. I don't see a mechanism where I could produce a pulse when the memory value of the bit is 0.
Further, this happens when the machine has been successfully returning the value of that location for many many cycles, enough to have advanced from 0x8000 to 0xE1F0 before failing. It reached -15,376 from -32,767 meaning it correctly did the multiply and the divide 17,391 times, involving 20 to 34 fetches of the address for each time we do the multiply-divide. That means somewhere between 350,000 and 600,000 times it did not get a parity check or have a phantom bit 13 set.
It also means that some condition comes up that seems to always results in a phantom bit 13 showing up in the SBR but only when fetching from that address. Vary similar to the issue with the memory diagnostic where we get a parity check at the same address every time with a phantom 1 in bit 13.
I noticed that the addresses have bits 8, 9, 10, 11, 14 and 15 set to 1. Why that is significant, I don't know. I am really struggling to find a failure mode that is so strong and repeatable for addresses like this and only when certain unknown conditions sensitize the system to this. That is because when I use Storage Load and Storage Display modes, we never see bit 13 emit a phantom 1, nor do we see parity errors. On the other hand, when I reach this point in the multiply-divide routine, any attempt to display the address generates the phantom bit 13 issue.
This is going to take a bit of noodling and some good luck spotting whatever is causing the issue. I could solve it by locking the value of the data word and parity bits into a hardware buffer so that what comes out is always self-consistent, however that presupposes that the value I lock into the buffer is always the correct contents. Doing that would require changing the PCB and design, something I would hate to do without firm evidence that I know the true root cause and can definitively correct it.
No comments:
Post a Comment