IBM 1410 FPGA Testing, and Memory

“There you go again”

What with the Thanksgiving holiday and all (even though it was spent at home, ’cause 2020), it took several days to ferret out the issue where simulation of a power on reset followed by START worked, but on the FPGA that failed. Of course it was bound to be an issue with triggers. However, I used the Integrated Logic Analyzer (ILA) to confirm that issue before I changed anything. Sure enough, card type DEZ needed the same adjustments as DEY: I changed it so that the transition of the AC SET input was required to occur after the transition of GATE ON / GATE OFF. At the same time, I moved all of that logic inside the FPGA clock transition section, to make it synchronous with the FPGA clock. That fixed the issue, which occurred on page

So now, after I load the FPGA, all I need do is press START and I see the correct waveform. (Also note that the transition of the output to ON (NEXT TO LAST LOGIC GATE) immediate turns off GATE ON – because that is exactly how it is wired. So first we see -S (active low) STOP AT F . LOGIC GATE D at about 320 into the trace. Note that GATE ON is already present, however, the trigger is held off by the “DCRFORCE” input coming from block 3B until STOP AT F . LOGIC GATE D goes true. Next, we see CLOCK PULSE 2 appear (but it gets inverted before it us used for ACSET, so it essentially matches CLOCK PULSE 1) at 389 on the diagram. Then at 392, the trigger sets, NEXT TO LAST LOGIC GATE goes true, and GATE ON is removed.

Corrected timing for power on reset followed by START

Decoding Memory Addresses

The IBM 1410 storage address register (STAR) is a five position two-out-of-five code register. However, it is not used altogether to select an address. On a read, all of the 10,000 position core memory segments are read, in parallel, each to its own B data register position. So, for a 40K machine, it has 4 B data registers, for 0-9999, 10000-19999, 20000-29999 and 30000-39999. On a read, the ten thousands position of the memory address is used to select one of the B data registers to gate onto the B Channel. During the write portion of a memory cycle (to change memory, or to regenerate memory if only a read is required), the ten thousands position is used to decide which B data register to update, if any, before the write portion of the memory cycle. The other B data registers retain their original contents from the read cycle.

At present, rather than simulating the full core plane of the IBM 1410, I chose to implement memory, for now, using a binary address and (probably) an array of 10K FPGA Block RAM segments. This means that the binary addressed RAM for the FPGA IBM 1410 uses a 14 bit address (to cover 0-9999), with memory arranged in the same 10K sections.

The inputs into this decoder are the -Y MEM AR UP/TP/HP/THP signals, which are in two-out-of-five code. The output from the decoder is a 14 bit binary address, 0 – 9999. This was implemented “brute force” by decoding each of the positions to the appropriate binary number, so the UP decodes to 0 – 9 in a 14 bit binary number, the TP decode to 00 – 90 (by tens) in a 14 bit binary number, and so on. Then the four binary numbers are simply added together in the VHDL – leaving the implementation of the adder for the tool chain to figure out.

Memory – and a Startup Issue

Next I created a simple set of Block RAM modules (BRAM) along with a VHDL wrapper for the 40K main core unit, accepting the aforementioned addresses in 2 out of 5 code, the Ten Thousands position (so that eventually it can tell if it is the 40K main core or the 60K Z Frame core), inhibit signals, and output sense signals. In order to more easily generate the necessary BRAM enable and write enable signals, I also brought out the MY_X_RD_1 and MY_X_WR_1 signals.

After some fussing I got my BRAM module working under simulation, but ran into an odd problem where during startup the system would overwrite location 00001 with garbage (and presumably 10001, 20001 and 30001 as well) because the system was left in Logic Gate B (LGB) state during the power on reset that then proceeding through a memory cycle once the power on reset was complete triggering the computer reset- thereby writing those locations with uninitialized garbage. (Computer reset will finish a cycle if it is encountered mid cycle to avoid corrupting core.)

I temporarily disabled memory writes, and then found I could successfully execute my halt instruction (WM/. WM/. in locations 1 and 2) under simulation AND on the FPGA.

The startup issue was interesting. I had supposed that the power on reset signal issued on ALD page would start OFF, come ON during the power on reset, and then turn OFF again. However, that was having the undesirable effect of letting the system progress from LGA to LGB during the power on reset as mentioned above. Some study of the aforementioned ALD, along with the IBM 1415 console manual (the power on switch is located there) made me realize I had misunderstood.

That power on reset signal is asserted during power on to keep the system in a reset state (and thus in LGA) for an RC constant of about 500ms. Then, after that, the signal goes to logic 0, triggering the one-shot at block 2D to do a computer reset. At that point, the machine is in LGA, and the computer reset will leave it there.

Since I already have a system initialization VHDL process in the FPGA VHDL logic, it was easy to manage the power on reset signal there – indeed, the two may eventually become one, as they both have the same purpose of getting some stuff set up before letting the IBM 1410 CPU start.

This tested OK under both simulation and on the FPGA, though I have not yet verified that the FPGA is actually writing the contents back into the BRAM.

Next up: testing write by setting a wordmark somewhere (to verify read/write), and then halting, and a simple loop – both of which should be possible with my current VHDL by just changing the RAM initial values.

Volume IX is Now a Memory

Testing of the ALDs in volume IX is now complete. With that all ALD pages for the main CPU and the channels (including the I/O channels E and F) have been tested, some more thoroughly than others. Memory spans volumes IX and X, so memory isn’t done yet.

After that comes the last volume I plan on working on for now, volume X. The main piece is the I/O Selectric console support.

Adventures in Error Land

One of the things that is interesting about the IBM is the extent of error checking. The Core has parity (check bit), but all of the major data paths (called “channels” on the IBM 1410) as well (A, B and Assembly channels, for example). Unfortunately, some of this is not covered in the ILD diagrams. While it is covered in the System Fundamentals manual, the logic actually used on some of the ALD’s is a bit different from channel to channel, even though the end result is essentially the same. In particular, while all of the pages use card type DHL (And-Or-Invert, or And/Nor, if you like), some of the pages use plain old NOR gates in places in combination with NAND gates – resulting in differences in positive +S inputs vs. negative -S inputs, so that I was not able to just copy the test bench code from one channel to the next.

Also, page had an error – combining two outputs with and AND on the ILD when the circuit is actually OR. When the test failed, the reason was apparent, comparing it with others on the same ILD, figure 59.

Page had a different kind of error. The polarity of the signal shown as “+S LOG GT E.2ND+3RD CHK TEST” is incorrect. It is correctly depicted as -S on the destination sheet, The ILD also shows this as Logic Gate F rather than E, even though the ILD for the destination page (on the same ILD sheet) correctly shows Logic Gate E. So, yet another vote of confidence for the testing process. 😉

Set… No, Reset…, No Set…. I’m all Confused

Page presented an interesting challenge. This is the Address Channel Validity check trigger. It is reset via collector pullover of the OFF output when a valid character is present and sets when an invalid character show sup (except during Logic Gate A of a 1401 cycle). It then also has a latch, along with a feedback path besides, to keep it set. That feedback path prevents a valid character from resetting the trigger once set.

But it also has a DC Set via the check test switch. The two are connected to their respective inputs by 4.7K ohm resistors. The interesting situation happens when you think about there being a valid character present (thus the off input is pulled high) when one presses the check test switch (which wants to pull the on input high). If this were ordinary logic levels fed from different gates, this would leave one input being logic 1 (0v) and the other being logic 0 (-12V) and would leave the base on the “on” side in no-man’s land. HOWEVER, the check test switch supplies -V voltage – more than enough to force the “on” side on and the on output to ground (logic 0).

In VHDL, one or the other necessarily takes precedence. When I developed the circuit for the DEZ trigger card type, I set it up with the Off output pullover taking precedence. That has worked fine – until this page.

But that does not mean one ought to reverse it. That might break some other page. For my test bench, I worked around the issue by putting an invalid character up long enough for the trigger to set. It would be interesting to scope this situation on a real machine, though that will never ever happen.

(Oh, and did I forget to mention that Triggers are a Pain? 8D).

Oh, and have I mentioned…

Page presented a challenge. It has a trigger (naturally) with the ubiquitous pull on and pull off connections. Unlike the previous ones, however, this pull off DOT function is driven by two gates instead of just one. The program logic recognizes the case where a single gate pulls on or pulls of a trigger, but not one where more than one gate is involved.

Rather than futzing with the program logic for what appears to be a single case (we’ll see about that), instead I inserted a gate “FXOR” (for faux OR) befoer the DOT function to fix it.

(Oh, and did I forget to mention … never mind. 😉 ).

We Interrupt this program…

Near the end of Volume IX were the ILD pages for interrupt processing. Most of that was straightforward: if interrupts are enabled, and an I/O operation completes (presumably in overlap mode), an interrupt is generated.\

But while there is a section on interrupts in the CPU instructional materials, including some ILD-type diagrams, their coverage is not as thorough as a regular ILD.

There is also a special case for interrupts for unit record equipment, perhaps intended for SPOOL (simultaneous peripheral operation online) support. There is a toggle switch for turning on interrupts for overlapped I/O operations (including 1301 seek), console inquiry, console output, non-overlapped seek and disk device “attention” signals.

Unit record interrupts are special, however: the operator must select one (card reader, punch, printer or paper tape) using a rotary switch, and then push a special priority interrupt button. This generates an initial interrupt – completed I/O turns on subsequent interrupts.

This last one was tricky to test, because it is implemented using a set of three latches – the first one being a “one and only one” latch to handle the button press. (We didn’t ever actually use that on the School of Business IBM 1410).

Chains of latches, especially where the signals are not brought out to the sheet edge are almost as much “fun” to work with as triggers. 😉

Mutually exclusive logic blocks

Memory testing also brought up the first case where I could not test an ALD without disabling one or more logic blocks. In particular a couple of sheets had gates for both feature S10 (10K of memory) and S2 (20K or more of memory), and the logic design was such that they cannot coexist, so I used the application to disable generation of the logic blocks related to feature S10 on those pages. (Other pages may still have both – but in those cases coexistence seemed possible.)

Load Sharing Matrix Switches

While I am testing or at least sort of testing most of the memory related circuits, including the sense amplifiers from a digital point of view, and the drivers for the LSMS units, I am not testing/generating/simulating the LSMS units themselves. This is mostly because each LSMS takes up four logic blocks on an ALD, and I don’t currently have a way to merge more than two logic blocks or merge two that are not vertically adjacent to each other. Finally it would just add complexity to the memory unit when I generate an FPGA to actually simulate the core. Instead I will just simulate Read/Write/Inhibit on 8 bits at a time directly from the storage / memory address register (variously referred to as STAR and MAR – the former in the documentation, the latter in the ALD signal names.

A Sense of Forcing my h-AND

Finally, the ALDs related to sense amplifiers required special treatment. For each bit of each of the possible four memory readout characters on a system with 40K, there are two sense amplifiers, one supporting 5K bits of a core plane – either the 0-4999 or 5000-5999 portions. These gates’ outputs are then connected together, but not using just load resistors. Instead, each sense amplifier card has a diode on the output such that when they are connected together driving a -Y signal level, they comprise an AND gate.

The HDL generation currently supposes that B level DOT functions are AND and that the others are OR (with a per signal level setting). Most cases for Y signal level DOT functions are indeed OR, but not in this case, because of the diodes that are present (anode facing out from the gate). Fortunately, a while back I added the ability to force a given DOT function’s generated logic, so this was easy to deal with.