Using these particular CPLDs means the design is heavily constrained by availability of I/O pins. Each CPLD has 34 possible I/O pins available. I started the design with the address generation logic, realizing that ABL needs access to three 8-bit wide buses (ABL, DB, and SB). Those already take up 24 out of the 34 I/O pins, plus a required clock input means that there are only 9 pins left for control inputs and carry out to ABH.
At first, I had a wider control word, but when the address generation was running correctly in simulation, I made a list of all possible kinds of address generation/PC update, and settled on this list and encoding (taken from states.i in the sources), using a 5 bit control word.
Code:
AB_RTS1 = 5'b00000, // 0
AB_INDX0 = 5'b00001, // 1
AB_INDX1 = 5'b00011, // 3
AB_RMW = 5'b00101, // 5
AB_ABS0 = 5'b00110, // 6
AB_JMP0 = 5'b00111, // 7
AB_ZPXY = 5'b01000, // 8
AB_BRK2 = 5'b01011, // 11
AB_NMI = 5'b01101, // 13
AB_RST = 5'b01110, // 14
AB_FETCH = 5'b10000, // 16
AB_IRQ0 = 5'b10001, // 17
AB_JSR1 = 5'b10010, // 18
AB_DATA = 5'b10011, // 19
AB_TXS = 5'b10100, // 20
AB_IND0 = 5'b10101, // 21
AB_BRA0 = 5'b10110, // 22
AB_PHA = 5'b11000, // 24
AB_PLA = 5'b11001, // 25
AB_BRK = 5'b11010, // 26
AB_JSR0 = 5'b11011, // 27
AB_RTS0 = 5'b11100, // 28
AB_BRK1 = 5'b11101; // 29
The names are based on most common usage, but may be used for other cases that require same output. For instance, the AB_PHA is used for PHA, but also for PHP and interrupt handling. The list is divided into 4 groups, based on left two bits.
- Group 00 : uses AHL as base address. This is a register used to hold earlier address.
- Group 01 : uses DB as base address, also includes vectors
- Group 10 : uses PCL as base address
- Group 11 : uses SPL (stack pointer) as base address
Depending on the rest of the encoding, the SB may be added to this base address, and optionally an extra '1'. This extra '1' is added in a couple of places, such as when pulling data from the stack (AB <= SPL + 1), or when doing a RTS which needs deal with fact that PC on the stack points to byte before instruction, or when fetching 2nd ZP address in (ZP,X) or (ZP),Y
Also depending on the control bits, the AHL is loaded or not, and PC is incremented or not. Funny detail: the AB is calculated first, and then the PC is updated on negative clock edge by taking the AB value and adding 1. Adders are big on CPLDs, but adding 1 is reasonably cheap because you only need wide input ANDs, not ORs.
The stack pointer is also stored in the ABL module, including a dedicated incrementer/decrementer for the stack operations. Incrementing is also reasonably cheap, because the CPLD registers can be configured as toggle flip flops. Each bit is set to toggle when all the lower bits are '1', which then only needs a wide 'AND', requiring only single product term (plus a second product term to detect all zeroes for a decrementer) In addition, it's easy to skip incrementing by ANDing in an extra enable signal. Of course, the downside of using a toggle flip flop is that it becomes a bit tricky to load a new value. Loading a new value requires 2 product terms, because it needs to XOR in the current state. To minimize resources, it's best to avoid mixing loads/increments.
You can see the AB operation indicated (as decimal) in the square brackets after the AB field of the debug output.