Saturday, August 27, 2022

Adding diagnostic output from FPGA via Neopixel arrays


Neopixels are tricolor LEDs with an integrated controller, wired in a long string. The single data line that connects all of the LEDs allows a string of pulses that address every LED so that one can control any or all of them by sending the proper pulse stream. 

Each of the three colored LEDs inside a single Neopixel takes an eight bit brightness value, thus we are shifting a 24 bit quantity into the Neopixel to set the brightness of the three colors. When we have a long string of Neopixels, we begin shifting out the 24 bit value for the last Neopixel, followed by the next to last and so forth until the last value we shift is for the first Neopixel in the string. 

The bits flow through each Neopixel and out to the next, thus the first Neopixel has seen the bits for all its successors flow through it. The final Neopixel only sees the 24 bits intended for it, while the next to last sees 48 bits. 

I bought arrays of Neopixels in a grid of 16 by 16 positions, these are addressed as 256 linear units. They can be chained, thus I will start with two arrays that consist of 512 addressable Neopixels. 


The wire that runs through our 512 Neopixels is clocked at 800KHz sending out 24 bit values at the rate of about 33,333 positions per second. The string of pulses begins with a minimum low interval of 50 microseconds to reset the devices then the pulses go out. The proportion of on versus off time for each of the bit positions encodes whether that bit is a 1 or a 0. 

Each bit position is 1.25 microseconds long. The full 24 bits takes 30 microseconds and my string of 512 Neopixels will therefore consume 15.41 milliseconds to set their values including the initial quiet period. If the chain is updated as fast as possible we will have a refresh rate of almost 65 per second. 

The single data line that drives the chain of Neopixels requires 5V logic levels, thus we will be passing it through a level shifter from the 3.3V of the FPGA. The Neopixels are also fed +5V and ground to feed the LEDs themselves.


Some debugging requires me to capture fleeting signals or to see the time relationship. These would be quite difficult to capture via the light arrays, but any static, long lasting or latched signals can be routed to the arrays for display. As an example, I can use one position for each state of a finite state machine I want to monitor. If the FSM sticks in some position I will immediately see which one by the light that is illuminated. 

I can also track interesting values such as the cylinder and track active for the drive. I may latch the last address on the SPI link as another idea for helpful data to show on the array. I only give up one output pin to drive all 512 lights, a good tradeoff even with the limitation to slow changing or static information. 

Friday, August 26, 2022

Aha - spotted a defect in my logic that was stalling the SPI link state machine


I should have realized that since I saw the two RAM access controlling commands - RAM to drive and RAM to SPI - working successfully but the load and unload commands stalling, the error had in parts of the SPI link state machine that weren't involved in the two good transaction types.

It was then that I spotted my error. I was triggering requests to read or write RAM and then waiting for a signal that was generated by the RAM state machine, but the done signal is in the faster clock domain unique to the RAM and not valid in the general logic clock domain. 

My simulation worked okay but there must have been just enough phase difference in the real world that it never caught the go-ahead signal. That pulse was one cycle long in the 100MHz clock domain but if it wasn't high at the beginning of a 50MHz clock cycle we could miss it. Apparently we did miss it consistently. 

Imagine that our clock rises a few nanoseconds before the RAM clock rises. When we look at the rising edge of our, the done signal from RAM has not yet been emitted. It goes high while we are in our 50 ns clock period, but we only look at our rising edge. The RAM clock advances twice as fast thus it has already dropped the done signal before we get to our next clock edge in our clock domain. Signal missed entirely. 


The solution was to emit a signal in our clock domain - from the FIFO that fetches the responses from RAM. That FIFO is loaded under the RAMs clock and thus is synchronized with the done signal I originally used - it works fine. The output of the FIFO is running under our slower general logic clock and the state machine for that side of the FIFO can emit a new type of done signal in the proper clock domain. 

The SPI link state machine now watches for the new done signal and will see it because it has a common clock domain. This should avoid the deadlocked condition we experienced earlier. 

While I was looking over the logic, I came up with an improved method of pulling the two bytes off the SPI link for each word, recoded it into two state machines, one for each direction on the link, and was happy with the simulation results.

I set up to test in the real world once again and this time I found it stalling only during the unload transactions. Will be digging into this.

First word x0009 - unload transaction

Green is RAM fetch completion before transmitting first word


Level shifter board

Ribbon cable to wire wrap adapter

Wednesday, August 24, 2022

Watching state machines and hunting for defects


I set up a set of indications to display on the four LEDs for each of the state machines, illuminated when it is not at its idle or starting position. That way I can quickly find any machine that is stalled or active when it shouldn't be. 

I also set up the red color of one of the multicolor LEDs to light when ANY of the state machines are not at idle, a very immediate visual cue that something odd is happening. While it is processing transactions it should turn red, reflecting the activity, but then turn off in quiet times. 


I could see that the main SPI transaction state machine was not returning to idle. This made no sense as every state where it can wait includes a top priority test that if the SPItransaction signal is off, it returns to idle. I will be instrumenting this more thoroughly as this is consistent with the flawed behavior I was seeing - one attempt at accessing RAM but no more than the first was attempted. 

Obviously I will be studying this one closely for any situation that might cause it to lock up, but also planning on the best instrumentation I can install to locate the problem. I have some ideas on how I can accomplish this without having to make multiple 25 minute runs reprogramming the FPGA board in between investigations. 


My PCBs arrived and look great. There is an extraneous REF comment on the silkscreen next to the four mounting holes, but that is irrelevant to its functionality. When I began looking closely at my connectors and cables for the links to the 1130 drive electronics I realized that I had bought IDE connectors not generic 40 pin connectors.

This is bad because there is a blank pin position on an IDE cable, right where I intended to run a signal. Both the IDE cable and the connector I bought to anchor the wire wrap lines has this missing pin. Fortunately the connectors for the PCB itself are full 40 pin units.

I have new cables coming in two days which resolve the blank position issue, but I do have to work around the missing pin on the anchor board for the wire wrap leads. It appears that I can press in a pin and solder that onto the board, restoring the connector to full functionality, but it will depend on what pins I can scrounge up. 

Finished PCB

Tuesday, August 23, 2022

Digging into stall in state machines driving RAM access; switch cover mounted; header for backplane wiring prepped


The connection for the Virtual 2315 Cartridge Facility to the disk drive electronics inside the 1130 is by wire wrap to the backplane on the drive, with those signals routed over an IDE ribbon cable to the level shifter board I designed. 

Wire wrapping uses individual 30 gauge wires which I will solder to a header with an IDE connector. I found a gender changer for IDE that uses a small PCB between the two connectors. By unsoldering and removing one of the connectors, I would have solder pads to attach the wire wrap lines. 

I completed the removal of one connector and cleaned the pads, ready to attach wires. I do need to design and install a mount to hold this firmly near the backplane, otherwise someone yanking on the IDE cable might rip the wires off the backplane pins or bend the pins. 


I installed the red safety switch over the DPDT switch that converts the disk drive between real and virtual mode. To protect the disk cartridges and heads, we want to stay in Virtual mode unless we are exceedingly certain that we want to risk the actual heads flying on a platter. 

In real mode, the pick signal from the electronics activates the solenoid, lowering the heads onto the spinning disk surface and via a microswitch indicating to the electronics that the load is complete. In virtual mode, the pick signal is instead sent to the Arduino in my facility and the Arduino tells the electronics when the heads are 'loaded', even though they don't actually move down in virtual mode since the solenoid is not connected.


The FPGA board has only a few exposed input-output pins that are not used by my logic, to which I can route various state information to be displayed on a scope. Currently I have access to only five of them, thus every time I have a new area I need to examine, I have to select five signals or signal states, then run the Vivado toolchain to reprogram the FPGA board. 

The longest step by far is the routing process. The tool synthesizes my VHDL into logic elements, it places these on locations across the board, then generates the signal routing between all the elements to complete the circuits I have designed. Routing hundreds of thousands of gates is a slow process, well over twenty minutes on a pretty fast 'gaming' laptop. 

This means that each time I watch the current set of diagnostic signals and form a hypothesis, another group of signals must be monitored to progress onward with debugging. That means another 25 minutes staring at the wall, waiting on the toolchain. 


The memory controller for the DDR3 RAM on this board operates with multiple clocks at 100 and 200 MHz, actually slow compared to the speed this memory can attain. It produces a 50MHz clock that drives all the rest of the logic on the FPGA.

However, this means that signals are changing in different clock domains, an issue as they may be switching right at some boundary where a logic gate is being clocked. To deal with this, clock domain crossing techniques are necessary. In this case, I am using FIFO logic built into the FPGA chip which operates with different clocks on each side of the queue. 

Thus I need state machines to push a request into the request FIFO, another to see that a request entered and pull it out under the memory controller clock. A state machine sees that request and drives the signals to the memory controller to accomplish the read or write. 

The results of the memory access appear in the controller clock domain, so they must be pushed into a response FIFO by yet another state machine. The output side of the response FIFO, running in the FPGA logic clock domain, sees a response, pulls it and puts the data (for a read) on the appropriate register for further processing. 

These must all run appropriately and not deadlock, smoothly handing requests through to the memory controller and pulling out the responses. This all seems to work in simulation, but I wasn't able to accurately model the different clock domains and transitions for my simulation, as it works in sim but not in real life. 


What I have observed so far is that we get one request for RAM access, which passes in as a request, the memory controller signals completion by the ram_done signal, and the request FIFO is reset to its idle position. 

However, there is only one request for RAM. We never move on to send the next read or write and are stalled somewhere. I am going to have to figure out a way to instrument the various state machines to my diagnostic outputs to see which state machine(s) are stalled. 

Sunday, August 21, 2022

SPI link debugged, transactions working; next up is testing whether RAM is properly written and read back


As I though the issue was resynchronizing after the first transaction had ended. I spotted the issue which wasn't caught by the particular signal timing I had set up in the simulation. The fix was obvious and quickly made.


My test setup sent a repeating sequence of transactions that flipped the FPGA between the SCspi and SCdrive states, that is with the RAM access controlled by the SPI link or by the drive emulation side of the logic. It was observable by the color of one of the color LEDs, where the blue color would be on when the RAM was set to SCspi. 

Indeed, it nicely switched between the two states, every time I started it up. I am happy that the link is working well and moved on to check out the access to RAM from the SPI link side. 


I set up a routine that, during startup of the Arduino, will send a defined pattern to the RAM, a different pattern to a different location, then read back the original sector and verify it. If that works we know that we are accessing RAM, uniquely addressing it and the Load and Unload transactions are working at least to some level. 

The Arduino code that is testing the data coming back from the unload is indicating a flaw. The FPGA side is happy with the format of the transaction, thus not indicating any error. However, the Arduino side is giving the indication that the data was not what was expected. 

I looked superficially at the MISO and MOSI lines during the transactions - the pattern I saw coming back from the Unload transaction appeared to match what I had sent during the first Load transaction and not what was sent in the second Load. I was only looking at one word near the end of the transaction and therefore may have missed an error with the first or 321st words, or it may not really have matched.

I am back at my home working out better diagnostic information. I suspect that I will have to use the USB serial link to my laptop to view better information about what is coming back on the link. I will also switch the five diagnostic outputs of the FPGA board to signals that will show me key memory controller signals. 

Virtual/Real switch being wired into drive

Saturday, August 20, 2022

More battles with SPI link debugging; built bracket to hold the Virtual/Real mode switch for the disk drive


Because of obligations I had elsewhere I only got to the shop late in the day to run the tests with my new and improved diagnostic outputs. When last I tested, the first transaction received was successfully executed, switching the FPGA RAM access to the drive from the SPI link. However, it seemed to hang up at that time and didn't indicate the subsequent transactions, nor execute them.

I thought I had made changes that would ensure resynchronization to avoid the observed behavior. When I hooked up and tested, however, I no longer saw the transaction command code latching at all. My diagnostics showed that the SPI transaction state machine was not reaching various states I had instrumented, nor was the state machine that assembled the two transmitted bytes into a 16 bit word. 


Because I mistyped the state of the SPI machine that would trigger the byte assembly machine to start, they were deadlocked. This is why neither reached the states I was monitoring. When I looked at the warning messages from Vivado when it was synthesizing my logic, I found messages that it was trimming SPI link FSM One-Hot register bits 18 to 2 from the design, as well as most of the FSM One Hot Register bits from the byte assembly state machine.

FSM is Finite State Machine, and it is implemented by setting bits in a register. The One-Hot method assigns one bit to each possible state of the machine, which speeds up testing to see if the machine is in any given state as only one bit is interrogated. 

This message was subtly telling me that my state machine only had a few states it could ever reach, therefore there was no need to implement hardware to hold the other one-hot bits. It doesn't say "Your FSM can never reach all the states", it refers to FSM One-Hot Register bits.

I did agree that it was going to deadlock and verified that in a simulation. I then corrected the typo, simulated to successful outcome, then synthesized the logic once again. When it was complete, I looked VERY carefully over the hundreds of somewhat obscure warning messages to be sure that nothing like this was occurring elsewhere in the logic. 

Next test session I hope to have more success. I have prepared a test routine so that if I can alternate flipping the FPGA between drive and SPI access to RAM, I can move on to write data into RAM in a few locations and verify that one of them is read back as written. 


Most of the connections I am making to the internal disk drive of the IBM 1130 are passive, they simply detect what various signals are doing. There are two that are passive as long as I don't assert them by pulling them low - the raw read pulses I produce and the write error halt I inject if errors occur during capture of written data from the CPU to the drive.

However, there are some active changes that must be made for my Virtual 2315 Cartridge Facility to work. I must block the signal from the drive electronics that activates the head loading solenoid. I must instead route that signal (pick) to the Arduino. The microswitch that detects when the heads have physically been lowered onto the platter must be unwired from the drive electronics and instead the Arduino must cause the drive electronics to believe the microswitch has activated. 

I want the drive to be capable of working in either mode - Virtual or Real - thus there is a toggle switch with those two settings. In the Real mode, it connects the pick signal to the head loading solenoid and leaves the microswitch hooked to the drive electronics. The heads will really load down onto the platter in Real mode. In Virtual mode, the active changes in the prior paragraph are implemented so that the Arduino sees the pick signal and it simulates the microswitch closing. 

I am using an aircraft style toggle switch, one with a red plastic guard that protects against inadvertently switching the switch to Real mode. The operator must lift the red guard to throw the switch, otherwise it stays in Virtual mode. I chose this scheme because of the risk of head crash if the heads are actually down on the platter surface. 

I formed a bracket to hold the switch in a convenient location just behind the drive, then drilled holes in the machine frame for the mounting screws. I have begun wiring the switch to the drive side - the solenoid, pick signal line, microswitch and microswitch signal line to the electronics. I have not yet connected the wires that will go to the Arduino as they have to be connected to the level shifter board which is still in fabrication in China. 

Friday, August 19, 2022

SPI link testing continues with some success; Wiring the Virtual/Real switch into the IBM 1130


The SPI state machine moves through a number of states from the first to the 325th word being exchanged. To verify that it is reaching the end properly I emitted some signals that can be displayed on the scope alongside the end of transaction (SPItransaction going low). 

The state machine reaches a state SBcheckA when it has finished with the 321st data word and is preparing to send and receive the checksum of those data words. It then passes through SBtest where it verifies if an error free transaction has been received. Near the very end it reaches SBflagB state. 

I am also monitoring the command it believes it has received. One line goes high when it sees the bit pattern SCdrive that sets the RAM access to the drive side. Another line is the default state SCspi for when the RAM access should be connected to the SPI link. If an error condition is detected during the transaction, I would expect to see the command switch from SCspi to SCdrive for a while then revert before the end of the transaction. 


I worked on various issues including not setting the command state to SCspi at power on reset, something that would have no impact on the system in real operation but needed repair to have a clean design. With the new instrumentation I discovered a small flaw, corrected it, and was pleased to see that the FPGA board switched the RAM over to drive mode from SPI mode - a successful transaction completion.

It appears however that something gets wedged after that one transaction, as I never latch in the subsequent command to switch back to SPI mode, while I can see the properly formatted transaction being received. I changed the diagnostic outputs and watched the state machine working well for the first transaction and it should be resetting to the idle state when the SPItransaction signal is dropped. 

Next up is a careful review of all the state machines and how they reset. It may be that I need to control how they move out of reset to avoid whatever is taking place to lock it up. I will also produce some new diagnostic output signals to help me find and resolve the issue causing this behavior. 


My modification to support the Virtual 2315 Cartridge Facility is a switch that reroutes signals that ran to the Head Load solenoid and the Heads Loaded microswitch. In the Real mode, signals are wired as usual, but in Virtual mode, the solenoid is out of the loop. 

For Virtual mode, the signal that would activate the solenoid is instead routed up to the Arduino inside my facility. The Heads Loaded signal line to the drive electronics is disconnected from the microswitch and instead routed to the Arduino. This lets the Arduino see that the drive electronics is requesting the heads to be down on the platter surface and the Arduino can lie to the electronics, telling it that the heads have indeed come down. 

I bought some final connectors and a mounting plate to install the switch in position on the frame of the IBM 1130 behind the disk drive. The remainder of the wiring and the physical installation will take place over the next few days. 

Thursday, August 18, 2022

Head loading modification installed and tested


I bought a small bracket to install on the drive. It is needed to add a small fixed pre-movement of the load mechanism at all times, which will permit the arm to be moved in and out even when the heads are not loaded down on the disk. 

The solenoid that loads the heads down on the disk surface pulls a rod that pivots a structure and removes a wedge that holds the heads up high of the surface. The spring loaded structure lowers them down and keeps a fixed downward pressure as they fly. A bent lever on the structure contacts a microswitch when it has pivoted all the way to the point where the heads are down on the disk surface.

That bent lever moves to its full rest position when the solenoid is not activated, then is pulled to the other extreme as the pivoting structure loads heads. When in full rest position, the structure has a ridge that blocks the disk arm from moving out from its retracted "home" position. 

Since my scheme blocks the solenoid from activating, thus blocking the heads from lowering on to the disk surface, I want the arm to move freely without head contact. I discovered that a vary small amount of rotation of the pivoting structure clears the block on arm movement but does not begin to lower the heads at all. 

The bracket I bought was fastened on the mechanism to hold the bent lever slightly in from its rest position, enough so that the arm can move. It fits to the rear of the bracket holding the solenoid and microswitch, bent over to fit behind the bent tab which operates the microswitch. 

Top view

Side view


With the bracket in place, I tested the free movement of the arm assembly to position the heads to any of the 203 cylinder positions radially towards the center of the spindle. Everything checked out well. A future test will verify that with the switch installed and set to the Virtual 2315 position, we can get the drive to go Ready without the heads down on the disc surface. 

Arm fully forward past cylinder 203 position

Arm in its retracted Home position - cylinder 0

New SPI logic installed, testing the link again


I had previously used a very well written set of SPI modules and decided to put that into my project. This was part of the SPI MASTER/SLAVE INTERFACE project,spi_master_slave written by Jonny Doin (, as described in the code. 

This operates differently from the code I first wrote, thus it forced some changes to the state machine that was driving the emission and reception of words coming over the SPI link. I successfully tested those on the simulator. 


As a way of further testing what is being received, I added in a register that is latched from the input at the time we receive the first word of the transaction. That was now visible on the four LEDs (in groups of four bits) as I set the slide switches to 0010, 0011, 0100 and 0101. I should see the left five bits showing the command code, either 10101 or 01010 depending on which of the two transactions has last been processed. 


The FPGA was latching the proper first words but the transactions were not completing. Keep in mind that I am using a scope to look at the transactions, triggering on either the start of the transaction or the end of it, using the SPItransaction signal. I was not looking at the entire 325 word transaction.

MOSI yellow, SS green and SPItransaction blue

That was the key to my first discovery. The code on the Arduino was only sending one word of data, instead the transaction was too short. I wouldn't have spotted this from the FPGA simulation since my testbench was what generated the traffic, not the Arduino. 

With that corrected, I was still not getting through to the end of the transaction successfully. I will need to instrument carefully to watch this both with a scope on diagnostic output pins and via the LEDs on the board. 

Wednesday, August 17, 2022

SPI link testing, designed interface PCB and sent to fab


The various parts of the Virtual 2315 Cartridge Facility operate at different voltage levels. IBM's SLT logic in the 1130 and its disk drive have a high of 3V. The FPGA operates on 3.3V signals and the Arduino Mega 2560 is a 5V device. Thus there are level shifters needed for signals moving between these devices. 

In addition, there needs to be a secure way of disconnecting and reconnecting the system to the IBM 1130 since ultimately the signals from the drive are accessed by wirewrap onto the backplane pins. I developed a board that will mount on the back of the disk drive, inside the covers of the 1130, which has two 40 pin IDE connectors on it. It is 5 3/8" wide by 2 3/4" high.


One IDE cable will be run to an IDC connector bolted to the side of the disk drive SLT card cage, with wires running to the points on the backplane where we access or drive the signals needed for the facility. The other cable will run to a connector on the box that contains the FPGA board, the Arduino board, an LCD screen, Pushbuttons, and the SD card socket. 

The only other part of the facility is a DPDT switch installed on the back of the drive that reroutes two signals from the drive. One is the signal from the electronics to load the heads, the other is a line that senses the state of the microswitch closed when the heads are down. This flips the drive between its normal (physical) mode and the virtual cartridge mode.

I have all the MOSFET transistors and resistors on order. The boards should arrive from the fab in about a week when I can solder it all up and test it out. 


I used the scope to verify a number of the key link characteristics are correct:

  • Clock period is 250ns for a 4MHz SPI link speed
  • The clock is normally high when idle
  • The bits are sampled on the rising edge of the clock
  • Slave select bounds the exchange of an 8 bit byte on the link
  • The bits are coming out on the SPI link in the order I expected
I set up a better testbench with more solid wire connections and good grounds between the FPGA, the Arduino and the scope. The FPGA is set up to show me the value received from the SPI link. All was ready to conduct the testing. 

I never got an error latch on the FPGA, thus it always passed the muster of having the second word be an exact inverse of the first word. Whether these are seen in the correct or the reverse order (bit 0 to 15 instead of bit 15 to 0) is not yet clear. 

The scope showed me the transaction ending with the good flag x5A5A but that will happen as long as we don't have the mismatch of the first two words. Again this suggests some degree of health in the link.

However, the two commands did not take effect. They should have toggled the FPGA between Ram to the drive and RAM to the SPI link, flipping one of the tricolor LEDs between green and blue. It stayed in whatever state I forced it to with the pushbuttons. 

Beginning of a transaction

End of transaction

For tomorrow I will be emitting some diagnostic data to help me debug the SPI code inside the FPGA. In parallel with that, I have begun to swap in a more powerful and well designed slave module I have used successfully in past projects. It requires a bit of redesign of the SPI handling logic but I believe this is worth substituting for the existing logic. 

Monday, August 15, 2022

Capacitor removed from Arduino and secondary SPI clock is clearly working properly


I found the capacitor which had one end connected to the AREF external connector of the board. Given its relatively large capacitance, its impedance at the clock frequency of my SPI link was nearly zero. 


It was quickly desoldered. The repaired board was taken to the bench and the scope connected to the AREF external connector pin which was now wired to the SPI clock for USART 2 on the microprocessor. If the clock was now working, with the scheme I put in place there should be bursts of 16 clock cycles every second or two while my Arduino looped around sending alternating commands to the FPGA.


I also connected this through the level shifter boards I will be using, as I needed to be convinced that the shifters could operate with a 4MHz signal. The results were great! The clock from the SPI link clearly showed up on the scope in bursts of 8 cycles with the rest state high.

The voltage of the Arduino produced clock swung between +5 and 0V, while the output of the shifter moved in concert between +3.3 and 0V. The waveform was not distorted too much, a possible outcome if the shifter couldn't handle the 250ns cycle time and 125ns duration of the 'on' phase of the clock. All was good with the signal quality and levels. 


What was evident immediately, however, was that I was NOT getting the 16 bits I expected during the SPI transaction. The ATMEL documentation made it sound like the buffered USART would accept two bytes and send out both in one transaction, but that is not what I am seeing on the scope in real life. This could be a C coding error or a misunderstanding of the documentation. 

In addition to that, there are several other things that must be verified to ensure that both ends are able to communicate. These include clock polarity, clock phase, and endian-ness of the transmission. I can change these at the Arduino to ensure a match with the FPGA but they must be verified and adjusted in necessary to have a good link. 

Polarity for an SPI clock is whether the idle state of the clock is high or low. I want high and that is what I see on the scope, so clock polarity passes muster. 

Clock Phase is a design choice for when signals are sampled - rising or falling edge of the clock - and as a consequence when they can be changed from one bit's value to the next. I can only verify this by watching the MOSI line, where the master (Arduino) sets up the 1 and 0 values of the bits in relation to the clock. 

The Arduino takes a parallel word input to the SPI channel and shifts out bits one by one. What we need to see is whether it should put the most significant or the least significant bit out first. The goal is to have the assembled word in the FPGA match what we expect, not be swapped back to front. Again I can observe this by watching what the SPI channel outputs on MOSI and SCLK from the pattern I am writing 

Sunday, August 14, 2022

Of all the pins I could have chosen to repurpose for the secondary SPI channel clock . . .


I discovered alternative schematic files for the Arduino Mega 2560 which conflict with the ones provided by the official site. In particular, the signal AREF which is show on the official diagram as connected solely to the connector block pin labeled AREF, in the alternatives I find a capacitor on that signal with the other end tied to ground. 

Not a good thing to have on a multi megahertz clock line. At 4 MHz this capacitor has an impedance of 0.4 ohms, essentially a dead short to the oscillator signal. Even if my setup of the secondary SPI channel on USART 2 is correct, I wouldn't have much of a clock signal available. 


Reworking to pull that capacitor off the board was quick and easy once I knew it had to be done. If by chance the USART clock was damaged by the short, I have three other USARTs I can switch over to on this board. 

First testing of SPI link between the Arduino and the FPGA - SPI is not trying


The FPGA input output pins are set to LVCMOS 3.3V logic levels, but the Atmel Mega 2560 processor operates with TTL 5V signal levels, thus I have to insert a level shifting circuit in the SPI link lines MOSI, MISO, SCK and Slave Select, as well as with the SPI transaction flag line. 

When I build the final production version of the Virtual 2315 Cartridge box I will have manufactured a printed circuit board to hold the level shifters, which also are needed for the thirteen lines between the IBM 1130 disk drive and my FPGA board. 


I tied the grounds of the two boards together and then snaked the signal lines through the level shifter breadboard. The Arduino code was modified to loop sending two initial commands over the SPI link, turning the RAM access over to drive mode and then switching it back to SPI mode. Success will be immediately visible on color LED 0 which will shift to blue when the board is in drive mode. 


The clock of the SPI secondary channel never oscillated, nor did I see any action on the MOSI line. This tells me that the channel was not initialized properly or there is some other defect. I verified this on the TTL (+5V) side as well, it is not a level shifter failure.


I need to walk through all the documentation from Atmel on setting up the USART for SPI Master mode as something is definitely not right with my Arduino sketch. 

Friday, August 12, 2022

Modified Arduino Mega 2560 to enable second SPI channel


The chip on the Arduino has power serial channels called USARTs which do more than the usual asynchronous serial protocols. Atmel provides support in their serial ports for pseudo SPI mode - master mode only, can't work as a slave - and the chip has four USART ports. 

One is connected to the USB link for serial communications as well as programming the board. The other three are accessible for normal serial communications, bringing their Tx and Rx lines to external pins on the board, but the clock pin of those serial ports is not brought out for connection. This limits the use of certain clocked serial modes and more importantly it does not allow the use of the port as an SPI link. 

Those signals are on pads of the Atmel ATMega2560 chip, sitting right there on the board in its TQFP 100 pin format that has the leads around the rim of the chip. If it had been a BGA or similar chip that has the connections underneath it would not have been as easy to gain access to the signal. 

Time to whip out the stereo microscope, rework tools and modify this board to allow the clock signal of one of the USARTs to be connected via one of the external connectors on the board. All the external connector pins are wired to other signal pins on the chip or to other circuits, but not every pin is important for my planned use. 


For my sacrificial external pin I chose the AREF signal, used with analog inputs. I don't need it for this project. It is at pin 98 of the chip and runs only to the AREF connector which is on the other side of the ground in next to Arduino pin 13. That will now be my Sclk pin for USART 2.  The lead of the processor was lifted off the pad to disconnect it and I verified lack of connectivity with a VOM.

Pin 98 lifted off pad


USART for serial port 2 has the clock pin at position 14 on the chip, which is currently unconnected. A wire tacked onto that lead of the chip is the next step of this modification. 

Pin 14 is XCLK2 - clock for USART 2

I used some 30 gauge wire as it is nice and thin, appropriate to the width of the processor lead to which it is soldered.

Wire tacked onto pin 14


The wire was routed through a mounting hole and down to the underside where I connected it to the bottom of the connector block for the AREF pin. This is now a SCLK pin for USART channel 2. 

Routing wire to underside

Soldered to AREF pin of connector


I discovered that Seeed Studio produces a variant of the Arduino Mega 2560 which they call the Seeeduino Mega 2560 which among other differences brings out more signals to external pins. Very significantly, it brings the serial port two clock to a connector, the same signal that I had to hack to access with a standard Arduino. Seeeduino Mega description

It is on order and should arrive next week, allowing me to skip the hacks when building more of these. For now I can begin to debug the Arduino to FPGA communications with the hacked board while I wait for the shipment that is already delayed by UPS for 'operational reasons' which I guess is code for 'my bad'. 

Feedback and diagnostic information brought to LEDs on the Virtual 2315 Cartridge FPGA board


The FPGA board I use for this project has a number of user inputs and outputs that I decided to put to use. There are four slide switches, four pushbuttons, four white LEDs and two tri-color LEDs on the board. 

The FPGA board


As I was limited on output indicators, I chose to use the four slide switches to route signals to the four LEDs. For each of the sixteen binary values set on the switches, I route a different four signals to the LEDs. 


The board has two tricolor LEDs, with individually selected red, green and blue LEDs inside the package. While these can be set to a wide range of colors by modulating the brightness of the three colors, interpreting subtle shade differences didn't seem useful.

As a result I set these up so that in most cases they would glow in one distinct color to highlight various conditions. One of them displays normal state information, the other displays if there are high level error conditions. 

The status LED is blue when the board is set to communicate with the Arduino, off when set to operate with the drive or green during reading or red during writing. 

The error LED is green when no error exists. If we caught an error during write and locked up the drive, it is red, while if there have been errors on the SPI link to the Arduino we light it blue. 


There are four pushbuttons plus the reset button on the FPGA board. One button will reassert the power-on reset condition to put all the logic back in its initial state. Another will reset the latch set whenever there has been an SPI link error. By pushing that button and observing how long the error lamp stays green instead of blue, the user can get an idea of the rate of errors on the link.

The other two buttons have no real function. For convenience I wired one to pulse the Access Go signal, thus changing the cylinder number in accordance with the step size and movement direction signals. The other button triggers a push of a read from RAM with whatever address happens to be set up, primarily to allow observation of a RAM access cycle.  I will change these to something more useful when come up with a use. 

Wednesday, August 10, 2022

Finished the debugging of the Arduino link on the FPGA side using Vivado simulation


Just as I had yesterday with the unload transaction, I closely examined the behavior of my logic when a simulated SPI link stream gave it a properly formatted transaction with a stream of data to be loaded into RAM. After chasing some issues, I was satisfied that it was performing as designed. 


Sector load
The trace above shows the start of a load transaction, which addresses a given sector on the virtual disk cartridge and then passes in the new content, 321 words of 16 bits, to be loaded into RAM at the addresses assigned to those words in that sector. It accumulates a checkpoint value which is tested against the checkpoint transmitted in the transaction, then returns a flag indicating all error checking was passed. 
One word of the transaction
Above is a smaller section of the transaction, with one word shown being shifted in from the SPI link and stored away in RAM. SPI is always bidirectional, we are shifting out bits of an outgoing word while simultaneously shifting in bits from the remote end to form an inbound word. The load transaction sends words of x0000 to the other end but receives back the contents to be written into RAM, except that during the checksum word, the penultimate word in the transaction, we ship our calculated checksum at the same time that we receive the checksum which will come from the Arduino.

RAM activity during load
Further zooming shows us the action after we have acquired the inbound word, where we trigger this being pushed into the FIFO to the RAM engine, see the signal to the memory controller which write the data into RAM, then see what is pushed into the response FIFO to indicate the memory activity is complete.

I moved on to the unload transaction. This one will read all the words from RAM for a given cylinder, head and sector location, passing them back to the Arduino one by one. My fake memory module which was substituted for the actual memory controller in order to simulate, provided a known sequence of 8 words that repeated to permit me to validate that the logic sent those up to the Arduino correctly.

A sector unload transaction begins
The unload transaction above involves fetching words from memory before each word is exchanged on the SPI link, whereas the load transaction above first grabbed a word and then sent it to memory. The relative timing is visible in the above and the following traces.

A word being unloaded
Narrowing the focus lets us see one word which is fetched from RAM on the left then toggled out on the SPI link. The checksum at the bottom is updated based on this word, giving us a running checksum that covers the entire 321 words of the sector. 

RAM during unload
Our command sequence for the memory access is appropriate for a read, where we pass along the addresses of the 8 byte grouping that are read as a burst by the DDR3 memory controller, then wait for the data valid signal from the controller which indicates we can now grab the contents off the app_rd_data bus. For simplicity of my design, I put the same data in all four words (8 bytes) and pick off just one of the duplicated words on a read. 

I then simulated a sequence of two transactions, one each unload and load, to verify that the state machines restore properly to handle multiple transactions from the Arduino. 

Two transactions serially

At this granularity, you can't see the individual words but the start and end of the successive transactions is evident. This ends my simulation testing of the FPGA unless I discover issues once I am connecting it to the Arduino over SPI and to the disk drive electronics in the IBM 1130. 


The Arduino Mega 2560 has a chip with a primary SPI channel which can be either a master or a slave. However, the chip itself has four serial port channels which are capable of being operated as SPI masters, but not slaves. It is a simple matter to set up the port for this mode and to exchange words using it, except for one minor issue.

The designers of the Arduino board did NOT bring the clock pin of any of those serial channels to any pins on the board. Thus, while the port can be made into an SPI channel it can't be used as the wire can't be connected to the Sclk pin. 

I worked out a way to tack a wire onto the pin on the chip that carries Sclk for one of the serial ports, the hook the other end to a connector pin on the board. In order to do this, I need to disconnect the original signal line for the pin I choose, then tack on the wire from the Sclk signal. I have worked out a candidate but haven't yet implemented this.

When I am next in the shop I will use the microscope to break the sacrificial connection, then tack on the jumper wire to bring Sclk to that external connector pin. This will allow me to use one of the other serial port channels for an SPI link to the FPGA, reserving the primary SPI link for its connection to the SD card shield where I host the virtual cartridge images.

The software to access the SD card shield assumes it is the only device on the SPI link and does not play well if a second device is sharing the channel. This in spite of the use of individual Slave Select lines for each of the multiple devices sharing an SPI channel, since it is the software that gets confused not the hardware. 

Tuesday, August 9, 2022

Arduino link state machine written and in debug


The connection between the FPGA and the Arduino is a four wire SPI (Serial Peripheral Interface) link with the Arduino as the controller. Every transaction is initiated by the Arduino, first turning on a fifth wire that marks a transaction, then sending 325 words. Each word is 16 bits and the Slave Select line of the SPI link is switched on and off for each word. This makes it easy to detect the incoming words.

The link sends a five bit command code plus the cylinder, track and sector address as the first word - the command word. The second word sent is the same word with every bit inverted. This serves as a kind of error check on the transaction. 

Next we send 321 words, one for each word of the sector from 0 to the end. As the words are processed, a running checksum is accumulated. It is simply the exclusive-OR of each of the 16 bits of each word with the corresponding bits of the checksum. The initial value or seed is x1234 to ensure that a completely open link doesn't produce a seemingly valid checksum at the end.

After the last sector word is sent, the checksum is transmitted as the penultimate word of the transaction. If the checksum calculated matches the checksum received over the link, and if the inverted command word was correct, then the pattern xA5A5 is transmitted as the final word, a flag. If any error occurred the flag is set to x0000 which is also the value that would be received on an open SPI link.

All error recovery is the responsibility of the Arduino, generally by resending the same sector down until it is successful. The FPGA won't respond to the drive electronics until a transaction is sent with a command word whose command bits request a switchover to drive mode. Drive mode also allows the drive modeling logic in the FPGA to control RAM access.

This can be rescinded such as when the heads are unloaded at spin down, by sending a different command bit pattern to return control of RAM to the SPI link machine. Initially the system is in SPI link mode as that is how we load a virtual cartridge image into RAM. 


When a transaction word begins, the data to be sent out to the Arduino must already be set up in the output word. The Slave Select is activated then the SPI clock toggles 16 times to shift 16 bits across the link, taking one bit off the output on each clock pulse and sending it out over the MISO line. At the same time, it is grabbing bits from the Arduino off the MOSI line and shifting them into a 16 bit input word. Thus at the end of the word, as Slave Select is turned off, we have exchanged two words.

If one wishes to respond to the last exchange of words based on the contents sent down from the Arduino, the output word must be set up before the Slave Select activates for the next exchange. If it is set up too late we have the wrong bits going up to the Arduino. 

The SPI link operates at 4MHz, therefore it is clocking at 250 ns per clock. The FPGA main logic is switching with a 50ns clock, which does give us a bare minimum of five clock cycles even if the Arduino were blazingly fast reasserting the Slave Select. 


I put together a procedure to switch the SPI MISO, clock and Slave Select lines to shift out one output word. Stringing these together, 325 of them, bounded by the SPI transaction signal I defined, will drive the SPI slave module and the state machine to handle the transaction. 


I started with the Unload command, which sends a cylinder, head and sector address to the FPGA along with 321 words of all zero. The state machine, after validating the command words, will read RAM for each of the word addresses and send out the contents of RAM on the output word for the same exchange - zeros in, RAM content out. 

I could indeed see the logic managing the stream, verifying the command and its inverse, reading and sending out all the words, then transmitting the checksum and a flag word. The flag word will be a given pattern if nothing went wrong, otherwise it is x0000 to indicate an error.

I found a few small issues and fixed them up, moving on to fully validating all the error checking behavior of the link for this command type. I did test the two trivial commands - switch the RAM control to the drive circuits and switch it to the SPI link engine, which worked exactly as designed.


The major work for tomorrow will be debugging the Load command. This is a transfer from the Arduino to the FPGA of 321 words of content, each of which must be written to RAM at the proper address, added to the running checksum, and then at the end we must have a good compare of our calculated checksum with the value sent down by the Arduino in order to give a good flag value indicating success. 

Successfully pushing words back into RAM from the write stream coming from 1130 CPU - in simulation


Using the edges of the 720 KHz write oscillator phases emitted by the drive, my state machine properly scans for pulses during the phase B intervals and uses that to differentiate a 1 data bit from a 0. It shifts those into an accumulated word as it records the number of 1 bits seen in this word. 

The final four bits, the error checking ones, are also recorded into the count of 1 bits so that at the end of the word we can do an error check. The final counter must be b00, thus the count of 1 bits must be evenly divisible by four. If this fails we lock up with a Write Select error setting the drive Not Ready and informing the software on the 1130. 

After the word is accumulated, the data and address must be combined along with a command indication of 'write' to push into the RAM request FIFO. A trigger pulse is emitted which causes the request to push into the FIFO then we wait for the indication from the memory controller that our RAM write is complete. Once that indication arrives, we bump the word address and go back unless we have captured the 321st word. If at any time WriteGate is de-asserted we go back to idle immediately. 


I observed that the signals are properly set up with enough hold time before clock edges, that the control signals are asserted at the correct time and for the appropriate number of cycles and that we wait for the memory controller to inform us when the write has completed to the memory chip. 


My logic for controlling the RAM interlocks on the app_rdy signal from the memory controller. This goes not ready after we request the write, while the controller is putting away the data into the DDR3 chips. Once the physical writing is complete the memory controller will reassert app_rdy. My fake memory controller module is emitting the app_rdy signal to complete the interlocked transaction with the fpga logic. I could see this working properly in the simulation traces.


Once I had the logic working as it should to capture words when the CPU is writing to the disk drive, given perfect data, I set up one of the words with an incorrect set of check bits to verify that the error is detected and the drive is stopped from further writes. It stopped at the end of the word as I expected.


The data patterns I created allow me to see the edge cases of the first and the last data bits of the incoming word. Having patterns where those bits are 1 and others where they are 0 gave me confidence that I the logic captures the word correctly.  The simulation showed this working as intended.

As an additional test, I had the testbench produce more than 321 words of data, allowing me to validate that I ignore the extraneous data. As an acid test on this, I wrote bad error checking bits for the 322nd word, which were ignored - I didn't want to trigger a write select error spuriously that turned off the drive ready status. 


It is legitimate to write less than the full 321 words of a sector and this does occur in software that runs on IBM 1130 systems. When the word count of the XIO Initiate Write command in the CPU is exhausted, the device controller drops WriteGate which should turn off the erase and write head operation. 

My logic has to handle this gracefully, in other words it has to stop looking for 720KHz clock pulses and the write data bits. I set up my simulation testbench with a short count so that I knew this works properly.  

shutdown after word 321


Onward to finishing up the SPI link protocol logic and debugging it from this side. 

Sunday, August 7, 2022

Debugging my way through the capture of writes to the disk drive and proper storage onto the virtual cartridge image


The pattern for a new sector is to have zeroes written for 250 us after the sector mark pulse, one sync word to establish the timing of clock versus data pulses and the start of each word,  then  up to 321 words of 20 bits each. The timing of the clock versus data pulses is handled for me by the drive emitting the two phases of the 720 KHz write oscillator and sending the data bits only during phase B.

However, it is important to spot the sync word as that tells us how to split a long stream of 6, 440 data bits into words. My logic begins watching during the early part of the sector when only clock pulses are written, in other words when the bit cells are all data value 0. As soon as I see a 1 bit written from the CPU, I assume it is the sync word. 

Each word from the IBM 1130 is 16 bits long, written back to front on the disk, with four check bits appended. Thus the sync word appears to be a string of fifteen 0 bits, a 1 bit, then the appropriate four check bit pattern of 1110. When I see the 1 bit, I test that the next three bits are 1 and the last one is 0. If this is true then we have correctly synced up and know that the immediate next bit is word 1, bit 15.

If this is not true, I turn on a signal that is generated by the drive to signal some kind of writing error in the circuity that makes it unsafe to write on the disk. This signal will make the drive go "Not Ready" and signal the error status to the CPU. It can only be reset by spinning the drive down to zero and turning it back on. 

I tested this with a variety of error conditions as well as the proper bit pattern for the sync word and am satisfied that this works properly.

Test catching and validating the sync word

In the simulation run above you can see the two clock phases A and B, the pulses on the Write Data Bit line from the CPU that would be detected and the state machines for disk rotational modeling at the top and the write capture machine below the two clocks. In between the clocks and data bits you can see the bit counter that tracks the number of 1 bits in a word in order to validate the check bit pattern.


Up next is the testing of the logic that accepts 16 bits, 15 down to 0, turning them into a parallel word with the bits 0 to 15 from left to right, then verifies the four check bits of the 20 bit recorded word. Any error in the check bits will be cause for me to turn on the same write unsafe error condition to make the drive Not Ready, as this blocks any further words from being written onto the virtual cartridge. 

I have already set up the testbench with four words of data following the sync word, all properly formatted with the appropriate check bit patterns. It will allow me to verify the operation of the logic to extract words and ask the memory controller to write them to RAM. 

Adding logic to snag words being written to the drive from the IBM 1130


Other uses of the similar mechanism, e.g. Diablo on Xerox Alto or DEC RK-05, have the processor's device controller generate the clock pulses along with the data bit pulses, thus snagging any data written by the CPU requires capturing async incoming pulses and separating data from clock.

Fortunately, since the IBM 1800 and IBM 1130 were the first uses of this mechanism before it was widely licenses to other vendors, they did things a bit differently. Sometimes the newer ways were an improvement, but I am glad for one of the original design choices.

In later versions of the drive, the CPU sent the target cylinder number to the drive which moved as many cylinders as needed in a single operation, while the 1130 implementation of the drive only moves 1 or 2 cylinders per operation. This puts more burden on the device driver software or user application but otherwise has no impact on me.

In later versions, the data to be written was delivered in a single line called Write Data and Clock, which was the raw stream that would be amplified and turned into flux reversals by the write head. If I intercepted such a line I would need to shadow the clock to know which pulses are clock, to be stripped, and which are a 1 data bit and which interval with no pulse is a 0 data bit. 

In the original 1800/1130 version, the drive produces a 720 KHz clock whose normal and inverted state are output as 700KCphaseA and 700KCphase B. The CPU device interface logic uses this to time the emission of data bits in phase B, leaving the drive to generate the clock pulses during phase A. The only bits I see on the Write Data line are data bits that are a 1. If there is no bit during a phase B, I infer a data bit value of 0. 


The sector begins after the sector mark ends with a stream of zero bit cells for 250 us. Since every data bit value is 0, there will be no pulses coming out of the Write Data line during this time, although I will see the 700KCphaseA and 700KCphaseB signals alternating. 

The next word is a sync word, a word with value x8000 whose pattern on disk including check bits is b00000000000000011110 thus when I see the first 1 bit coming out of Write Data I know I am near the end of the sync word. 

I will check to be sure that the next four bits following the one I just detected are 1, 1, 1 and 0 because that proves I am seeing a sync word being written. If not, I will force the Write Error state to turn on which will stop the CPU from writing and block any further operations to the disk. 

Once I consumed that stream of 1, 1, 1, 1, 0 the very next bit cell will be for bit 15 of the first of 321 words in the sector. It is possible that the CPU may write fewer than 321 words, thus I am always prepared to safely cease the capture of written data. I then use the 700KCphaseB as the indicator of each bit cell, counting out 20 per word and then 321 groups of 20 to complete a sector. 


Each occurrence of phase B opens a window where I look for any 1 value coming from Write Data. I latch that in as a data bit value 1, but if the phase ends without such an incoming value, I have detected a data bit value of 0. At the phase A time following, I shift the bit into a shift register to assemble a word. 

The bits on the disk surface start with the low order bit of the data word, bit 15, and continue leftward to bit 0, after which there are four check bits. I have to inject the bits into the bit 0 position and shift right on each cycle to end up with a 16 bit value in the proper orientation for the 1130.

I begin the write request to put that newly captured word into RAM at the address associated with the cylinder, head, sector and word count. I bump the word count and move on to error checking the final four bits.

During the capture of the data I increment a two bit binary counter whenever I have captured a data bit value of 1. As each of the four check bits is captured, it also increments the bit counter. A proper check value causes the counter to end up with the value b00 indicating that our written word has a number of 1 bits that is evenly divisible by four, the error detection algorithm.

If the bit counter is not b00 then I force the Write Error state. This again stops the write from the CPU and blocks the software from further disk access.

When the WriteGate control signal is switched off, we are done writing words. My logic should go back to the idle state where it waits for the next write from the CPU. 


For generating the bit stream during reading, I have to model the actions during the disk rotation very faithfully, deciding where each clock pulse and data plus is placed. I have to generate 250 us of zero data bits, the sync word, then spit out all with words along with clock pulses and the correct four check bits. 

When writing, I don't have the same control over timing and won't be able to predict where the clock pulse is or where the sync word begins with its first 0 data bit. Fortunately I don't have to. I only need to know in sequence that

  • we have Write Gate on and are seeing 700KCphaseA and 700KCphaseB
  • a bit on Write Data while in 700KCphaseB means we are inside the sync word
  • proper verification that the bits I see next are the right check bits 1,1,1,0
  • every 700KCphaseB is a new bit cell of the 20 that make up a word
  • every 20 bit cells I begin a new word
  • when Write Gate is dropped or I finished capturing 321 words we are at the end
  • bad check bits or a sector mark with Write Gate active is an error I inject back
Thus my rotational modeling isn't needed at all except as a simple indication that we moved beyond the fall of the sector mark, easily gleaned from the modeling circuit's output. 

Saturday, August 6, 2022

Locking down the read bit stream performance for the Virtual 2315 Cartridge system


There are a number of state machines involved in generating the read bit streams. One covers the overall sector timing from the sector marker pulse, initial zero stream, sync word and then 321 20 bit data words. Another walks through the timing of the bit cells for each word, spitting out clocks and data pulses at the proper time, counting data bits that are 1 and producing the appropriate final four check bit cells of the word 

In addition there are machines that pull the next ram word and ready it for the machine timing each word. If the word isn't ready in time or arrives too early thus stepping on the value still being output, we would not have proper output. if the word machine doesn't begin at the proper point when each of the 321 words begins, we have distorted initial and final bit cell patterns. 

I worked out means to get this operating together rather than attempting to ensure that the individual word machine cycles perfectly to align with the sector machine defining the start of each word. The individual word machine waits to be triggered by the sector level machine, thus I could adjust the timing to get distortion free transitions. 


From some maintenance documents I uncovered, I found that the pulses produced by the read amplifier for each flux transition was shorter than the pulses I initially designed into my device. I adjusted the VHDL to make my pulses 200 nanoseconds long, which is in line with the scope images shown in the maintenance document. 

They must be long enough to trigger the drive circuits to emit a properly timed pulse out of the data separator. They also must be short enough to avoid false detection of 1 bits. With a bit cell of 1.4 us and a clock separator circuit that generates a 600 ns window to detect the data bit pulse, my original pulse width of 400ns might allow a clock pulse to slop over into the data bit window time. 


The final simulation run showed me ideal results - the bit cells were all exactly 70 cycles long, or 1.4 microseconds, with the clock pulses all in the proper place and pulses for data bits of 1 exactly where they should be. 

I checked that the RAM addresses being requested were consistent with my scheme for storing the virtual cartridge images. I verified that the logic switched properly from sector to sector and wrapped around to zero after a rotation. 

Start of a sector - zeros, sync, words

zoom in on a few words

multiple sectors, sector marks at top