Blog

IBM 1410 FPGA: 1401 Mode SAR Instruction

Having fixed the 1401 Store B Register instruction and gotten further in the 1410 1401 mode diagnostic program M011, it was time to look at the Store A Address Register (SAR) instruction again.

The CE Instruction materials say:

“Therefore the A address left from the previous operation must move to the BAR before the new A address reads in. This is done during a B cycle that occurs just after the Q-op is detected at I-op time.”

The first timing diagram, below shows that the +S RO A AR signal that presumably ought to be making the AAR contents available becomes inactive before the BAR is reset, so the BAR never receives valid contents – it ends up with invalid binary zeros. (In the 1410, the address registers contain 2 out of 5 code – and having no bits set at all is invalid.) The result was a number of errors, halting the CPU, as would be expected – B Channel, Address Channel, and B Character Select (because the resulting memory address was invalid, none of the 10K “core” modules was selected.

Timing diagram showing lack of A Address Register Readout while +S SET B AR is active, resulting in invalid B Register contents.
Timing diagram showing issue reading out A Address when setting B Address

There is a signal which looks like maybe it was intended to force the readout of the A Address Register – -S 1401 Q OP TRANS, however once the B cycle starts, the B Character register was cleared and so the signals that relied on – the “Q” character with at wordmark were no there once the B data register was reset early in the B Cycle.

This does not seem to be a race condition. Rather, I suspect it is resulting from the fact that some of the pages for the signals involved are for a 1410 with the accelerator feature, and some without. My hunch is that on a 1410 with the accelerator feature, all of the needed activities to copy the AAR to the BAR occur during the I OP cycle, driven by the -S 1401 Q OP TRANS signal. However, when I tried to force it that way, I was not successful.

To work around the problem, I added logic to ALD page 14.71.30.1 (A Address Register Readout) to read out the AAR when signals +S 1401 MODE 1 AND +S STORE A AR OP CODE AND +S B CYCLE CONTROL are all active. The result was the timing diagram below (sorry that it is only half of the signals, but the important ones are there.) With that change, the +S RO AAR is active through the necessary part of the B Cycle so that the contents of the AAR are properly transferred to the BAR.

Timing diagram showing fix applied to ALD page 14.71.30.1 to read out a register during the (first and only) B Cycle of the 1401 Store A Address Register (SAR) Instruction.
Timing diagram showing fix to page 14.71.30.1 to read out AAR when setting BAR during B Cycle.

With that fix in place, the diagnostics mostly run but fail with console error message “ERR 601J”. So, on to the next fix…

IBM 1410 FPGA: (Address) Exit Stage Right

With the 1410 mode CPU running correctly, it was time to test out the 1401 side of things. The 1410 has a toggle switch, allowing the CPU to run as an IBM 1401 compatible with most 1401 programs. The 1410 and 1401 are similar, architecturally. The biggest difference is that the 1401 uses 3 character addresses, with the zone bits over the units and hundreds positions in order to address up to 16,000 characters, whereas the 1410 uses 4 character addresses, capable of addressing memory up to 100,000 characters.

The primary 1401 diagnostic is M011. Once I figured out a problem involving the 1401 addressing scheme, I turned my attention to the next failure: The Store A Address Register (SAR) instruction died a horrible death with B Channel, Address Channel and B Character Select Errors. That problem manifested as a failure to copy the A Address Register to the B Address Register for storing later. I tried for a bit to fix this first, but was not immediately successful, so I decided to try and NOP out the uses of SAR, that mostly had to do with storing the address of each test so it could print out a failure message.

Having done so, the next problem quickly cropped up: the partner instruction for the B address register, Store B Address Register (SBR) also failed – it is essentially the same as SAR, but without that first step of transferring the AAR to the BAR. And the failure error was different as well: an Address Chanel Exit error. The ALD for this circuit is 18.14.02.1 . The ILD is on figure 59 at coordinate 2B – however there is also an error in that ILD. It shows the GateOff input to trigger DEZ as ground (the usual ground symbol). However, on the ALD, the F input it connect to M, which is -12 Volts, and in my scheme, that is logic 0. (Logic 1 being 0V).

The timing diagram below shows the timing issue. The B Address Register read out and validity check look like they are OK with respect to the Address Exit flip flop ACSET signal. However, the problem was that the VHDL for implementing the SMS card type DEZ has a 3 clock cycle “silo” for ACSET, so that the flip flop is not really set until 3 clock cycles after its ACSET input. In addition, there was no such silo for the other inputs. This silo was put in place to give the ACSET signal some noise immunity – so that a 1 cycle glitch would not set the trigger. But the fact that the other inputs did not have a silo meant that they were sampled 3 clock cycles (FPGA_CLK) after ACSET, and by that time the B Address Register Readout signal (PS_RO_B_AR, i.e. +S RO B AR) had become inactive.

Timing diagram for 1401 mode Store B Register instruction showing the delay in setting the Address Exit Check with respect to the readout of the B Address Register.
Delay in the Address Exit Check inputs with respect to the readout of the B address Register, resulting in an Address Exit Check.

The relevant VHDL looked like this:

   SMS_DEZ_PROCESS: process(FPGA_CLK, ACSET, DCSET, DCRESET, DCRFORCE, DCSFORCE)
        begin
        if(rising_edge(FPGA_CLK)) then
           if(DCRESET = '0' OR DCRFORCE = '1') then
              OUTOFF <= '1';
              OUTON <= '0';
              SSTAGE1 <= ACSET;
              SSTAGE2 <= ACSET;
              SSTAGE3 <= ACSET;
           elsif(DCSET = '0' OR DCSFORCE = '1') then
              OUTON <= '1';
              OUTOFF <= '0';
              SSTAGE1 <= ACSET;
              SSTAGE2 <= ACSET;
              SSTAGE3 <= ACSET;
           else
              SSTAGE1 <= ACSET;
              SSTAGE2 <= SSTAGE1;
              SSTAGE3 <= SSTAGE2;
              if(GATEON = '1' AND SSTAGE2 = '1' AND 
                SSTAGE1 = '1' AND SSTAGE3 = '0') then
                  OUTON <= '1';
                  OUTOFF <= '0';
                elsif(GATEOFF = '1' AND SSTAGE2 = '1' AND
                  SSTAGE1 = '1' AND SSTAGE3 = '0') then
                   OUTOFF <= '1';
                   OUTON <= '0';               
               end if;
            end if;
        end if;
        end process;

Once I realized what was going on, the fix was pretty easy. Just add a 3 clock silo for the inputs as well as ACSET. I took the conservative approach, however, and created a separate special instance of SMS_DEZ.vhdl, called SMS_DEZ_SYNC.vhdl to use in the Address Exit validity check logic. This new implementation of DEZ would probably work fine across the board, but I am just using it in the one place, for now.

The timing diagram below shows that now the OUTON signal does not activate – no Address Exit Check, because now the inputs are sampled at the same time as ACSET, and those inputs are still active from the B Address Register readout.

Timing diagram for 1401 Mode Store B Register showing response after adding silos for all the inputs, and not just the clocks.
Timing diagram for 1401 mode SBR after fixes.

The relevant VHDL looks like this, for gate type SMS_DEZ_SYNC:

    SMS_DEZ_PROCESS: process(FPGA_CLK, ACSET, DCSET, DCRESET, DCRFORCE, DCSFORCE)
        begin
        if(rising_edge(FPGA_CLK)) then
           if(DCRESET = '0' OR DCRFORCE = '1') then
              OUTOFF <= '1';
              OUTON <= '0';
              SSTAGE1 <= ACSET;
              SSTAGE2 <= ACSET;
              SSTAGE3 <= ACSET;
              GON1 <= GATEON;
              GON2 <= GATEON;
              GON3 <= GATEON;
              GOFF1 <= GATEOFF;
              GOFF2 <= GATEOFF;
              GOFF3 <= GATEOFF;
           elsif(DCSET = '0' OR DCSFORCE = '1') then
              OUTON <= '1';
              OUTOFF <= '0';
              SSTAGE1 <= ACSET;
              SSTAGE2 <= ACSET;
              SSTAGE3 <= ACSET;
              GON1 <= GATEON;
              GON2 <= GATEON;
              GON3 <= GATEON;
              GOFF1 <= GATEOFF;
              GOFF2 <= GATEOFF;
              GOFF3 <= GATEOFF;
           else
              SSTAGE1 <= ACSET;
              SSTAGE2 <= SSTAGE1;
              SSTAGE3 <= SSTAGE2;
              GON1 <= GATEON;
              GON2 <= GON1;
              GON3 <= GON2;
              GOFF1 <= GATEOFF;
              GOFF2 <= GOFF1;
              GOFF3 <= GOFF2;
              if(GON3 = '1' AND GON2 = '1' AND GON1 = '1' AND GATEON = '1' AND
                SSTAGE2 = '1' AND  SSTAGE1 = '1' AND SSTAGE3 = '0') then
                  OUTON <= '1';
                  OUTOFF <= '0';
                elsif(GOFF3 = '1' AND GOFF2 = '1' AND GOFF1 = '1' and GATEOFF = '1' AND 
                  SSTAGE2 = '1' AND SSTAGE1 = '1' AND SSTAGE3 = '0') then
                   OUTOFF <= '1';
                   OUTON <= '0';               
               end if;
            end if;
        end if;
        end process;

With that out of the way, I could run M011 diagnostics quite far into the tests, until it needed to use the SAR instruction for more than just tracking the test address.

IBM 1410 FPGA: 1401 Mode “Zoned Out”

Having fixed the problem with the space, I continued to test out the IBM 1410 in 1401 mode. At first, I had thought that it was getting quite far in the diagnostic M011 – it would error out with the I register (instruction counter) at 6029. I spent the better part of a day chasing down an apparent problem with the Store A Address Register (SAR – opcode Q) instruction at the indicate location. However, when I placed a halt at the test before that, it still failed at the same place. How could that be?

So, I decided it would be worth the trouble to run the 1401 diagnostic in Instruction Fetch/Execute mode (I/E). The problem became apparent much more quickly than I had anticipated. After halting at the halt and branch instruction at location 2018, it ended up trying to fetch an instruction at a location near 06020. (My memory is just a little fuzzy on when that flying leap actually occurred, and I don’t have the output anymore.)

As you may know, the 1410 has 5 digit addresses, whereas the 1401 used 3 digit addresses plus zone bits to address up to 16K of memory. It was clear that somehow the translation from the 1401 style address to the 1410 address register had gone awry.

A little digging reminded me that there was one page of the automated logic diagrams (ALDs) associated with this translation, part of what is called the “Zone Adder” that was missing – page 14.18.04.1. After some time working with the xsim simulator under Vivado, I began to suspect a problem with my re-creation of the logic from that page.

  • Input “A”: -S ZONE ADDER A A DOT B A
  • Input “B”: +S ZONE ADDER B BITS EVEN
  • Output “O”: +S A NOT A DOT B A DOT B BITS EVEN

The output name is sufficiently complicated that the equation for this is not self-evident. Looking at the ILD figure 55, it looks like the following should be the equation:

O <= NOT A and NOT B — The NOT A is because A, above is -S not +S

Interestingly, page 14.18.04.1 is not referenced at all in the 1410 1401 Compatibility manual on page 7. On both the ILD and that manual, this signal is routed to the middle of a logical OR gate labeled “Zone Adder Carry”, which is laid out on page 14.18.06.1.

Regardless this VHDL caused the problem noted above. However, I had also penciled in on the ILD an additional inverter, which would be the signal without the added inverter (double negative and all that…). For that, the equation would be:

O <= NOT(NOT A and NOT B)

And it actually takes one less “fudged” gate, because the SDRTL provides this result outright.

It turns out that removing that inverter from the logic for that ALD (and thus implementing this second equation) cured that particular problem, both under xsim and running on the FPGA. But, at least superficially, this makes it look like it does not match the ILD — at least at first.

However, and finally, examining the page to which this signal is routed, 14.18.06.1, one sees that it is routed to a logical OR (physical NAND) gate where -S / -B active inputs generated the +S active output. So if one takes the two together, the end result is such that it does match the ILD.

The diagnostic didn’t get very much further, however, and now fails at location 2032 on a SAR instruction, similar to the failure after the “flying leap” problem, just at a different location. SAR is interesting in that it first copies the A Address Register to the B Address Register, and that is apparently failing.

IBM 1410 FPGA: “Space Cadet”

No, not that kind of cadet. Unlike the IBM 1620, the IBM 1410 does have adder circuits for doing addition (and multiplication and division as well). Instead, this issue had to do with the IBM 1415 space function.

I had thought that the principle instruction diagnostic, CU01, was working perfectly, but it was not quite so. I had noticed that even after finishing the diagnostic, I kept getting space characters sent to the PC support program, but figured that was just a software bug somewhere. However, when I started to run IBM 1410 1401 compatibility diagnostic M011, it stopped typing its instructional message to switch to 1401 mode on the console when it hit the first space character.

After some sleuthing, I discovered that the console typewriter logic never asked the channel for the character after the space – it just kept sending that space to the console typewriter again and again and again.

As one might expect, the issue turned out to be in the console typewriter Selectric emulation VHDL code. I had miss-interpreted the signal -V CON PRINTER SPACE NO to just involve console input via the space bar. a closer look at the related automated logic diagrams (ALDs) made me realize that in fact this was also the Selectric’s way of telling the 1411 that the space process had been completed, and that it really had nothing to do with input from the space bar.

At first I tried to just use the relevant states of the finite state machine (FSM) that controls spacing. That did not work right, however, because that same FSM also controls back-spacing. Qualifying the logic signal to only be active during a space, but not backspace, operation filled the bill.

IBM 1410 FPGA: Off to the Races??

Once the 1415 console emulation was up and running, I was able to run diagnostics. The first set of diagnostic failures arose in the Assembly Channel because the Automated Logic Diagrams I had for parts of the Assembly Channel were not for the 1410 Accelerator feature, unlike the vast majority of ALD pages I had.

Once I fixed that, diagnostic CU01 ran OK in non-overlapped non-priority (interrupt) mode. However, once I enabled overlapped I/O with the priority feature in the diagnostic settings, the diagnostic errored out with an Instruction Check. The overlapped I/O is that of the 1415 console. The diagnostic then monitors that (along with a priority interrupt) to make sure that the channel status information and the interrupt operate as expected.

The Assembly Channel issue had been reproducible using a single instruction, and I could set that instruction in the initialization of the first 10K memory module, so I was able to troubleshoot it using simulation. But not this one – it happens after 10s of thousands of instructions. I had been less than confident about using the built-in logic analyzer capability that Vivado affords for Xilinx chips, but this problem left me no choice. Fortunately, after just one false start, I was able to figure out how to make a change in the signals the logic analyzer had available, set triggers, and so on – so, not so bad.

Here is what the problem looked like that caused the Instruction Check. Note that the signal +S E CYCLE REQUIRED is going active (high) just after signal -S LOGIC GATE A. Now, that should not be a problem, except that +S ERROR SAMPLE is also high at this point, and since Logic Gate A is active, as well as E Cycle Required, the logic in the CPU sees that as a possible problem – knowing that if E Cycle Required is active, it ought to be activating Logic Gate R rather than Logic Gate A. (Note: At this point I had not included +S E CYCLE REQUIRED A (one of four different ways that +S E CYCLE REQUIRED can be asserted – and which turned out to be the “villan” in this case. Anyway, here is what the output of the logic analyzer looked like:

E Cycle Required Request with a Race Condition
E Cycle Required Request with a Race Condition

Now, this was not occurring on all or even anywhere near a majority of overlapped I/O operations. Below is an example (using the very same FPGA configuration) of a successful overlap. Note that in this example, +S E CYCLE REQUIRED is asserted much earlier – along with -S LOGIC GATE E so there is no race – E Cycle Required is ready and present long before the time of+S LOGIC GATE Z when the CPU makes the decision between Logic Gate A and Logic Gate R is made.

IBM 1410 FPGA Overlapped I/O Cycle With No Race Condition
IBM 1410 FPGA Overlapped I/O Cycle With No Race Condition

So, I went looking for possibilities:

  • Had I made a mistake when I entered the data for the associated ALDs?
  • Was this a race condition caused by the fact that I insert “D” flip flops to disconnect any timing loop identified by my generation code (within a single ALD) or by Vivado during synthesis (involving multiple ALDs)?
  • Was this a race condition caused because the FPGA gates are much faster than the original RTL logic SMS cards, and/or the fact that LUTs are used to collapse combinatorial logic into a decision table that may eliminate multiple levels of original gates?
  • Was this a very tight timing window in the original CPU?

During my investigation I came upon this tidbit in manual 226-2692, IBM Customer Engineering Instruction-Reference 1411 Input-Output Operations on page 46:

Service Note
Because close timing conditions occur in the areas listed below, excessive delay, or accumulated delays in the logic circuits may cause machine failures:
CHANNEL REGISTERS AND CONTROLS
E-cycle required
F-cycle required
E-cycle control
F-cycle control
Address channel

Well, do any of those look familiar? Like maybe the italicized one? While I have resolved the issue, I do not know, for sure, the exact nature of why I ran into it. The original CPU had the +S E CYCLE REQUIRED signal originating in (physical) Frame “D”, whereas the logic gate signals are created in Frame “C”, a couple of feet apart. As a guess I think it is most likely the the FPGA logic is faster than the original hardware in this area, such that +S E CYCLE REQUIRED could be being asserted earlier than the original engineers thought probable.

Regardless, I faced the issue of what to do about it. I had several choices:

  • I could try and tweak my generated logic in terms of speed, by adding delays, to see if I could resolve it that way.
  • I could inhibit +S E CYCLE REQUIRED A (the one that seemed problematic) in the presence of -S LOGIC GATE A so that in such a case, the E Cycle Required signal would not be asserted until near the end of the memory cycle of this enar miss.
  • I could inhibit +S E CYCLE REQUIRED (so, all four possibilities) in the presence of -S LOGIC GATE A

The first two choices might work, but would leave me in a situation where this problem could recur later on, in some other setting, whereas the third option would prevent it from happening a priori. The only downside that I could see would be that it might prevent a device from transferring data to memory quite as fast as the original.

So I investigated transfer speeds of various devices to see what they might be like:

  • Unit record devices have a core buffer in the IBM 1414 I/O Synchronizer, and are slow enough anyway, that it would not likely make any observable difference.
  • IBM 729 tape drives. The 729 IV transfers at 112.5 IPS at 556 CPI, the 729 VI at 112.6 IPS at 800 CPI. So, roughly, 112.6*800 = 90,080 cps, or a bit over 11 micro-seconds per character. So, every other cycle would be fine on a an 1410 with the Accelerator feature with 4.0 microsecond cycles – allowing it to “steal” every other core cycle.
  • IBM 7340 Hypertape Drives. Some models are 112.5 IPS / 170,000 cps tape drives (so, roughly 1500 CPI), or 5.9 microseconds/character. That would require making transfers in non-overlapped mode (see the 1302 disk drive, below). However, ones attached to a 1410 more likely operated at 34,000 cps – plenty slow enough.
  • A 1405 Disk Drive spins at 1200 RPM (so only 20 rps) and has a 1000 character track size (so, say 1200 to be conservative). That gives us about (1/20)/1200, or 50ms/1200, or 41 microseconds per character – lots of time.
  • A 1301 transfers characters at 90,100 cps, or 11 microseconds per character – so plenty of time using every other core storage cycle.
  • A 1302 transfers characters at 184,000 cps, or 5.4 microseconds per character. But on a 1410, these devices transfer data only in non-overlapped mode (even if the I/O instruction specifies overlapped mode) – because they must use consecutive storage cycles for their data.
  • A 1311 (the 2311 is not supported) has 2980 characters/track, and rotates at 40ms/revolution. this gives us roughly 13 microseconds per character.

These point to things being OK so long as the peripheral can “steal” every other core cycle. On top of that, the 1410 Channels are double buffered, so even if sometimes it takes 5 cycles to get two characters in or out, operation should not be affected. It would not seem the peripheral speed would prevent using the last option listed.

So, I made the changes, labeling them with a fictitious ECO “JRJ001” in the database and tested – diagnostic CU01 now passes without problems. Below is what the signals look like, timing wise. I believe (but cannot prove) that what happened is that +S E CYCLE REQUIRED ended up delaying until the next possible “last logic gate” (in a given memory cycle), which is typically logic gate E, as is the case in this capture.

E Cycle Required after Installing "ECO" JRJ001
E Cycle Required after Installing “ECO” JRJ001

So, what is next on the block?

  • Several enhancements to the console program, including merging what is now the main root window with the 1415 console form – no need to have them separate.
  • Run more diagnostics, by saving core images under my software simulator and transferring them to the FPGA memory to read them, as I did with CU01.
  • Experiments with speeds: how fast can I run the 1411 CPU before it fails its diagnostics?
  • Research into channel signals. I don’t have ALDs for the relevant IBM 1414 I/O Synchronizers, though I do have ILDs, which pretty well define the logic. But rather than parroting exactly what the 1414s would have done, I will likely just use VHDL using some of that ILD logic as a wrapper around communication to and from the PC support program, at least at first.

IBM 1410 FPGA: Diagnostics!!!

With the console typewriter working, and the ability to load core images, I was now ready to try and run some diagnostics. The first issue I ran into was that the main diagnostics, CU01, was initialized for 80K of core. That the core image file was larger wasn’t an issue: the Windows based support program knows to only send the first 40K. However, there was a flag denoting machine size that needed to be “patched” — which I did using my emulator, and then saved as a new core image.

Loading the diagnostic, it typed out its identifier, but then quickly errored out with an Assembly Channel error on a Subtract instruction where the destination B-field was initially positive, but turned negative during the subtraction. This was not the first place I expected to see an issue – figured it would more likely relate to some trigger or other. But nope, this was a simple issue in the Assembly Channel.

The problem arose because the ALD diagrams I have are mostly for an IBM 1410 with the Accelerator special feature, however, most of the ALD diagrams for the Assembly channel proper (ALD 15.50.xx.1) are for the non-accelerated version whereas the Assembly channel controls (ALD 15.49.xx.1) are for the accelerated version. There were several signals that ended up either not being used, or having no source as a result:

  • -B B CH MIN+INV PLUS SIGN GATED
  • -B B CH PLU+IN MIN SIGN GATED
  • -B B MN +IN PL+PL+INV MN GATED

These all relate to cases where the B field ends up with a sign other than its original sign. I suspect that the idea was that these signals provided earlier gating to the assembly channel rather than just deriving them from the adder with additional gate delays.

After fixing the Subtract issues which involved just the “B” bit on page 15.50.06.1, (going from positive to negative) I immediately ran into the same issue on the first pass of the diagnostic during the same test, changing from negative to a positive result. Fixing this involved pages 15.50.05.1 (mostly). As a result the pages for the Assembly channel have some gates who’s note at the top is “FUDGED”. 😉

With that, so long as I didn’t enable I/O overlap and Priority Alert (interrupts) during the test, CU01 completed successfully! Frankly this was something of a surprise: It tests most of the instructions and ways of moving data and they all just worked.

Also, the run time of about 3.75 minutes matches very well with the information in the diagnostic listing for a 1410 with the Accelerated logic feature.

So then, I ran it again, but with overlapped I/O and priority features turned on in the diagnostic. It ran fine for about a minute, but then failed during overlapped execution of a multiply instruction. More on this during the next installment.

IBM1410 FPGA: Core Memory Upload – Dual Ported CORE??

After a year messing with other stuff, I finally got back to working on the FPGA implementation of the IBM 1410. With the CPU work essentially done, yet no peripherals, the issue to tackle was how to test the CPU – with no way to load diagnostics.

The solution I came up with was to take memory images I had generated (and could continue to generate) of diagnostics loaded into core (which are generally restartable at location 1) and load them into the FPGA.

Since I didn’t want to integrate this additional “feature” into the existing 1410 memory logic, I chose instead to implement dual port VHDL RAM memory that is underlying what the CPU things of as core, so I could load stuff into it. I then wrote a simple state machine using the existing multi-facet UART interface hardware (which also handles things like console I/O, lights and switches between the FPGA and the PC running the console program) so that it could load memory images from my existing 1410 Simulator. In so doing, I had to swap the top two bits – the simulator has the WM has the high order bit and the C bit as the next lower bit, whereas the FPGA has the C bit as the high order bit.

This turned to be a bit easier than I expected to pull of, and after a week or so of effort, it is now in place.

Loading up the main CPU diagnostic, CU01, it did not get all that far. The Subtract instruction has an issue where it isn’t placing the sign bit over the B field (result) units position – at least it is not doing so when the result sign is different from the original sign. Presumably an error in entering the information from an ALD or a signal misconnection. I suppose it could be the kind of thing that might have happened in the factory during the construction of a real IBM1410 where something was amiss.

Fortunately I can easily reproduce this problem under the Vivado simulator, which provides what amounts to a mult-hundred probe logic analyzer, and am using that to track down the problem.

Adventures With an HP 2748B Paper Tape Reader

An denizen of the Classic Computer mailing list approached me recently about getting some paper tapes read in. These were BASIC programs from his High School days. At first I tried reading them in my PC05 paper tape read on my PDP-11/34, however that reader has a sprocket feed, and no good place to hold a rolled up tape. I tried 3D printing a spool reel holder and output guide, that that was not successful. So I decided I would try instead to use one of my two HP 2748 paper tape readers – an HP 2748B in particular.

Note that this reader has a capstan (small cylinder to the right) and a pinch roller (the larger black roller to the right) to firmly grip the tape and pull it through the read station. It works pretty well, but I do have to clean it frequently when reading oiled rolled tapes or they start to slip. Fortunately, both the capstan and the pinch roller are metal – no chance for rubber rot turning to “goo” here!

The Arduino “Sketch” described in this post, the PC side perl script, and some perl script tools for working with paper tapes for 8080 machines, PDP-11 and PDP-8 are available for download.

The jig I used to install new grain-of-wheat lamps into the read head is available on Thingiverse .

Documentation is available for this reader at https://bitsavers.org/pdf/hp/paperTape/

HP 2748B Front Panel
Front Panel of an HP 2748B Paper Tape Reader

A gorgeous device inside

Like many/most HP devices, this device is absolutely gorgeous inside. Gold plated printed circuit boards (the entire board, not just the contact fingers), lots of space to work inside, easy disassembly and reassembly.

HP2748B Paper Tape Reader Chassis
Top view of the HP 2748B Paper Tape Reader Chassis

A Continental Connector for the Interface

The first challenge was the connector. It is a 50 pin connector originally made by Continental. First I bought a Winchester connector off of eBay, but its pins were much too narrow. Then I purchased an AMP connector of eBay, and its pins were just a little bit too narrow. For now, I added solder to the relevant pins to ensure they make contact. Some are still a bit too wide and need some filing down so the connector mates fully, but it works well enough, for now. I will not be leaving this connector attached permanently because of the solder. But I thought it was a better than the alternative of wiring up a second interface connector.

The default logic voltages for the interface are +/- 12V. However, HP also planned for a TTL logic level interface (0 – 5V) by adding a clamping circuit on each interface connection. This clamping is activated by connecting interface pins C and H. Unfortunately, my AMP connector had a coax connector on pin C, so for now I made this connection internal to the reader. When I get some time, I’ll se if I can swap pins so I can make this connection in the way the designers intended.

My plan was to use an Arduino to drive the paper tape reader, and connect to that via TCP/IP, similar to how I did my paper tape simulator.

It’s ALWAYS a Light Bulb

The first thing I did was re-form the capacitors. This turned out to not be necessary. For one thing, most of the power supply capacitors are rated at 5 TIMES to 10 TIMES the expected voltage across them. There was almost no current flow after a minute or two of “reforming”.

The next thing I thought of (thankfully) was to check to see if the illumination in the read head was OK. Well, of course it wasn’t (though the lamps in my other HP paper tape reader, an HP 2748A, turned out to be OK).

Unlike the DEC paper tape readers, these HP readers use a grain-of-wheat lamp for each channel: 10 lamps in all, 8 for the data channels, one for the feed hole, and one additional lamp used for temperature compensation. Clearly at least one was bad, but it turned out to be much more confusing than that.

I had some lamps on hand that are ostensibly for an RK05 positions sensor, but when I tried to replace the lamp I thought was bad (and at that point, I thought it was just one), another one seemed to fail. I took out the lamp I thought was bad, and tested it – and it seemed OK. Put it back in, and it seemed bad. Eventually I figured out that what was probably going on was that the wires on the lamps were so corroded that solder wasn’t taking properly to the leads. Eventually I decided to order some likely replacements off of eBay and replace all of them.

The process of lamp replacement is sufficiently tricky and time consuming that HP service people generally were not expected to do it in the field. The bulbs aren’t listed as replaceable components. Instead, service people / customers were expected to acquire a replacement for the entire read head.

Metal alignment pins at least make the removal and re-installation process relatively straight forward – takes about a minute.

HP 2747B Paper Tape Reader Read Head, Front View
HP 2747B Paper Tape Reader Read Head, Front View
HP 2748B Paper Tape Reader Read Head, Top View
HP 2748B Paper Tape Reader Read Head, Top View

To do the replacement, I removed the little PCB (left side of the photo), unsoldered everything, including the wiring to the rest of the read head, and install new lamps. I had already learned from experience that getting a single lamp into position could be tricky, so I didn’t relish trying to do what with eight of the little wee beasties. In order to assist the installation, I made a 3D printed jig that fit the boards and had a place for each bulb at an appropriate distance from the PCB based on the lead lengths of the original lamps that I had removed.

I made this jig available on Thingiverse .

3D Printed HP 2748B Lamp Installation Jig
3D Printed HP 2748B Lamp Installation Jig

It still wasn’t easy – at 70 years of age I am pretty shaky at times (and have been more shaky than average my entire life), but it worked well enough. However, since these bulbs are slightly different than the originals, I ended up adding a fixed 1/2 Watt resistor to add some additional resistance. That resistor is inside the black heat shrink tubing that goes from the adjustable resistor in the top view over to the PCB.

Then I used silicone sealant to hold the board in place, much as had been used originally – messy, but effective.

Adjustments, Adjustments

Next, I adapted the “solder enhanced” AMP connector to the tape readers interface connector. That took some trial and error in terms of how much solder to add so that I had good reliable contact. It still isn’t perfect: some are a bit too large right now.

Then I was ready to adjust the read head. Fortunately, there are good instructions in the manual (see the link at the top of this post) for how to do that. I ended up going thru that process several times, but in the end it ended up about where it was after the first time.

During this process I was also able to verify the interface signals were behaving as expected. It took a minute or ten to figure out that the read hole signal would not be present unless the READ button on the reader was engaged. Also, at first, I misunderstood the lamp/bit order – on this reader the feed holes go nearest to the front panel when inserting from left to right – the opposite of a DEC PC-05 reader.

The Arduino Interface

For this application, an Arduino Uno had sufficient interface pins, and unlike the Raspberry Pi, is supports 5V interfacing. The code was relatively straight forward, and uses the same kind of program I used for the paper tape emulator I called out earlier – the PC just makes a TCP connection and sucks up data.

The interface pins for the HP Connector, the Arduino and the Arduino digital signals are all documented at the top of the code, and symbolic constants and macros are in place for handling the Arduino port setup and usage.

There were some things I learned/ tweaked along the way:

  • At first I tried to stop the reader every time I sent a buffer of data to the PC. However, the clutch and brake on the reader don’t seem to be working quite well enough for that to succeed, so in the end I just streamed the data an “hoped” the Arduino and the TCP connection would keep up – and it seems that they do.
  • Originally I though I might have some kind of header on each buffer of data I sent, but it occurred to me that TCP is essentially doing that already. The code for that header is in SendBuffer(), but is commented out.
  • I was having problems with some extra characters, so I added a little time wasting loop to more or less ignore rapid transitions on the h0le signal. That code could probably be improved upon.

It is Working – Trust but VERIFY

After a couple of passes of tweaking the lamp positions and going through the adjustments, the reader reads a DEC test tape pretty reliably – maybe 1 error every 50,000 characters. It also handles both fan folded and wound “round” tape spools well. However, I always try and verify that tapes read correctly, so I wrote some perl scripts to do things like:

  • Verify 8080 binary tapes (the format is documented in the script)
  • Verify 8 Bit Intel HEX tapes
  • Verify Even parity text tapes
  • Verify PDP-8 binary format tapes

You can download a Zip archive with these tools, the Arduino Sketch and the PC side Perl script here.

One More Problem – There’s Always at Least One

As I started reading in tapes, starting with the DEC test tapes (MAINDEC-00-D2G2-PT and MAINDEC-00-D2G4-PT) things were fine. As I proceeded to read in some 8080 binary tapes, I started having some issues with the high order bit. That was easy to fix – the lamp was slightly miss-aligned.

I also found finding the balance between not having data errors (dropped bits) and having duplicated characters a little tricky – it took me a few passes doing the adjustment procedure described in the HP manual to get it working well.

However, I then discovered one more problem. Sometimes it would drop some of the null characters appearing in a blank area of tape after the initial leader. The HP 2748B has a special circuit that causes it to intentionally drop leader nulls – once you hit the READ button, it drops nulls until the first character. After trying this and that (including running through the adjustment procedure yet again), it occurred to me that maybe that circuit was firing, causing the reader to drop nulls.

I connected several signal lines to a connector that I thought might be useful in order to scope them (and not have to keep moving the scope probe around the board, as I have no extenders for these connectors), and quickly confirmed my suspicion. I tried a lot of things – including swapping some of the four identical transistors (two for a flip flop, and two more acting as signal inverters) so that the flip flop would have matched transistors, to no avail. I also tried replacing the capacitor that connects to +12V that resets the flip flop on power up (in case the READ button is already pressed). Nothing seemed to help.

I noticed on the oscilloscope that the length of time before the circuit fired when it shouldn’t was kind of random. Could this be noise? Connecting a .1uf bypass capacitor on the +12V line feeding the reset circuit was the first thought, but that was going to be harder to do on the board than I preferred. In the end, I hooked up the capacitor from the base of the reset signal inverter Q13, and that cured the problem.

Now the reader is generally reliable so long as I keep the read head, capstan and pinch roller clean.

IBM1410 FPGA: More Inputs

The IBM 1410 FPGA project has reached a milestone. Console input of characters, word mark, space bar, index (force premature end of line) and the special inquiry keys: request, cancel and release is now complete.

With that it is possible to display memory, update memory, start execution at a specific address, start the machine, stop and machine, etc.

I have tried a few instructions in as simple way: Set Word Mark, Halt, Add, Subtract, Jump on Inquiry Request and Jump unconditionally, and they work OK for at least a very simple example.

With this, the implementation is very close to being in same state as a real IBM 1410 donated by Oscar Mayer that we played with in the basement of the University of Wisconsin Computer Science lab: CPU, console but no peripherals or I/O synchronizers.

There are some issues, though, both with the console implementation and with the FPGA implementation. For the FPGA

  • The Console Check Test 3 fails to set the ADDRESS CHANNEL error. This is a “fight” between the DC Set which the switch activates, and another signal holding the trigger reset. (I think an earlier blog post discusses this in more detail).
  • Attempts to display memory at 09998 show only two characters (this may or may not be correct), and attempts to display memory above 10000 fail miserably with a B Character Select error. This could be an issue of a gate being present that should not be, because I generally entered all gates using the IBM 1410 SMS application, even ones that I knew I would not want eventually, and/or a missing tie-down or tie-up of a signal. This is a critical problem to ferret out and fix. [Resolved 6/11/2023 – simply a matter of marking most of the gates for features “S10” (or $10 depending on where you look) and “S20” for “no generation”.]
  • As mentioned in an earlier post, when starting a display memory sequence, during the first step – address entry – a “D” should be displayed. Instead an underlined (invalid parity) “F” is displayed because of a bit 2 “pick”. This looks to be an error in the actual ALD, will be easy to fix, but doesn’t really affect much of anything.
  • An attempt to do output I/O to the console, using instruction M%T0aaaaaW does not operate properly — it repeats the first character in the I/O buffer at address “aaaaa” without end. This is also a critical problem, as diagnostics need to do this. [This has also been fixed as of 6/20/2022. It might have involved the change that *removed* a “D” flip flop after gate 3F.] [Update: This problem has also since been resolved.]

With the addition of the console, there are now three github projects involved with this effort:

The immediate activity, which may take a few weeks due to other commitments, will be to figure out what is going on with the console output issue.

IBM1410 FPGA: Inputs

Since the last post, I have begun working on inputs – switches, in particular. The input subsystem is much simpler than output, at least for things like switches and console input- there is no need for any kind of common FIFO or arbitration. Instead the module IBM1410_UART_INPUT_SUBSYSTEM receives data from the FPGA development board UART, and then places that character onto one of N (currently 0..7) input FIFOs (one each for receiving data for switches, console input, card reader, and so on), and N write data flags.

Then, each input module (for example, module IBM1410_CONSOLE_SWITCHES_RECEIVER) reads data off of its FIFO, and does the appropriate thing with that data. Specifically for switches, this means receiving the entire vector of switches into a temporary vector, and once fully received, replacing the one that the 1410 itself is using.

Testing the various switches has led to some interesting discoveries:

  • Adding switches made a number of multi-page combinatorial loops pop up, that were previously hidden as a result of optimization by the Xlinx Vivado toolset. The 1401 compatibility switch was the second (and I hope last) of the loops involving large numbers (more than 300) gates. I could have change the design rule check to ignore this issue, but I decided instead to modify the SMS generation application to look in the logic block Notes field for “DFLIPFLOP” causing it to add a D Flip Flop after the identified logic block.
  • I like to be able to run the FPGA CPU on its own. This means that it’s initial switch vector (the one that gets copied into when an entire switch vector is received from the host support program) needs to be reasonably initialized. To do this there are a few SWITCH_VECTOR(….._INDEX) => ‘1’ entries along with an OTHERS => ‘0’ at the end to set things up right. This fact is what actually led to the replace with a buffered copy concept described above. (The first time ANY switch is activated on the PC host program, the entire switch vector gets replaced by the transmitted one; indexing in hardware is hard. 😉
  • While testing the “DISPLAY” function of the console’s MODE switch, when one presses START, a “D” should appear. However, currently, instead an “F” with invalid parity currently appears. This is occurring because the printing of “B” is also enabled, causing a “pick” of the “2” bit. It looks like my ALD is out of date – the “B” should really be being suppressed under these conditions – in other words, a hardware “bug”. Sure hope there are not too many of those! A similar thing happens with the MODE switch set to C.E. A “#” should be printed, but instead I get a “.” – same bit pick, for the same reason.
  • Pressing CHECK TEST SWITCH #3 (and holding – which the C# user interface host program makes easy by holding on a click, and then releasing on the next click), and then pressing START should light up all the PROCESS CHECK lamps. This works fine for check test #1 and #2, but for #3 it does not light up the ADDRESS CHANNEL error as expected. This scenario uses the DC SET input to set the flip flop, but it appears that there is a signal using collector pull down causing the flip flop to reset (DCRFORCE in the SMS gate’s module) at the same time – and that reset it taking priority in the VHDL. There are not too many of that particular gate type in the system, so I may try reversing the priority of those things, so that a DC SET takes priority and seeing if that breaks anything else.
  • The SCAN GATE switch on page 14.17.18.1 (part of the ADDRESS STOP circuitry) has a note that says it is a “CIRCUIT OPENINING” switch – in other words, all outputs are -36V except the selected position. In most cases in the IBM 1410, a connected switch means ‘0’, that is, -36V, so this behavior is (to be verified) a natural outcome of how I implemented switches – I “NOT” switches in the VHDL all – because an “on” switch electrically connects to -36V. For this switch, a given unselected position has to be a ‘1’, which is then “NOT-ed” in the VHDL to a ‘0’ – which then forces the output of the gate it is connected to to a ‘1’ – taking it out of the filtering criteria for address stop – it’s scan position can be either on or off. This, along with a failure to initialize the SCAN GATE switch and the address top address switches had me tied up in “NOTs” for a little while (pun intended.)
  • On this same page there are a couple of optional SYNC COND inputs – at least one of which need to be tied to logic ‘0’ in order for address stop to work. They were intended to give CE’s a way to do an address stop on arbitrary conditions inside the machine.