Part 1: Control Register

A project log for SPI Master

An SPI interface for 6502 microcomputers using discrete logic gates

Nate RivardNate Rivard 06/29/2022 at 15:480 Comments


We will start our discussion of this design with the control register because virtually every other subsystem depends on it in some way. It is the primary interface for controlling the behavior of the device. To recap, the control register is an 8-bit read/write register and this is how its bits are laid out:

Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0

Design Considerations

There are a few things to remember when designing this control register:


In discussing the design, we will add complexity layer-by-layer to address each of the design considerations.

To start, we need a read/write register that fits well in a 6502 memory-mapped environment. For this basic task, I chose a 74HC273 Octal D-type positive-edge triggered flip-flop. This IC satisfies the first 2 considerations pretty well. It features:

Unfortunately, this device by itself does not fit well in a 6502 memory-mapped environment. When SPI Master is not being addressed, it should be completely disconnected from the shared data bus. This particular chip doesn't have that capability.

One option is to use a 74HC373. This device has built-in tri-state buffers that can prevent the IC from driving the data bus, but this would break the requirement that other subsystems be able to read flag values when the device isn't asserted.

The solution I've chosen is to use a 74HC245 Octal bus transciever. The '245 is configured to permanently flow in one direction, A -> B, where A are inputs and B are outputs. Next, the '273 outputs are connected to the '245 inputs, and the '273 outputs are connected to the shared data bus.

Other devices can connect to the lines between the two ICs, thus satisfying that design requirement. 

Only one requirement remains, which is the ability to set/reset the ITC bit independently. This requirement is the most complex because in many cases, to the system, it should just look like a regular 8-bit register. You write values to it, you can read that value back. But in certain scenarios, when a transfer is complete (and potentially when a transfer is started), the ITC bit should be set (or cleared, respectively) by the device itself.

To satisfy this design requirement, I've chosen to break the top bit out into its own device, one half of a 74HC74 Dual D-type positive-edge triggered flip-flop. This flip-flop has separate set and reset lines and is triggered at the same time (when their clocks are connected to the same signal) as the '273.

On startup, the RESET line is driven low and the value is 0. When a transfer is complete, TX_COMPLETE is driven low and the value is set to 1. Otherwise, the user can write a value to it on D7 as part of a memory write or can read the value on D7 as part of a memory read.

This could bring a potential failure point (or quirk at least) because you can write a 1 to this bit and tell the device "transfer is complete". This would kick off an interrupt if enabled, etc. One solution to this is to disconnect D7 from the input side, but you would need a way to acknowledge an interrupt and clear ITC from the programmer's perspective. The venerable 6551 ACIA does this by clearing the interrupt flag when the data register is read. That is something to explore, but for now, the programmer can clear ITC (and thus acknowledge the interrupt was handled) by writing a 0 to this bit in the control register.

Putting it all together, this is the "final" design for the control register.

Q7 from the '273 is disconnected from the '245 and instead Q from the '74 is connected. We will address the 2nd half of the '74 and how the various signals referenced (SR_WR, SR_RD, TX_COMPLETE) are generated in later posts.