Close

Control Sub-System

A project log for Apollo Guidance Computer

A running hardware implementation of the AGC, block II using TTL chips.

wglasfordwglasford 07/07/2022 at 12:310 Comments

I have drawn initial schematics for this sub-system, but have not started wiring it up.

Here are design notes that help understand the schematics.

Clock: At the heart of the Clock module is a 4 MHz crystal that is divided into four non-overlapping 1 MHz clock signals; CLK1, CLK2, CLK3 and CLK4. This allows the read data to be placed on the bus before the write signals latch the data into the registers. The clock signal can be replaced by a 555 monostable multi-vibrator chip with a variable resistor to provide a slower, variable clock for debugging purposes. The output of these two sources are used to create the two clock pulses.

The two clock signals are generated using two J-K flip-flops. The following table shows the states of the two flip-flops and their meaning. The first flip-flop (FFA) has its J & K inputs tied high, putting that flip-flop into a toggle mode. The Q output of the first flip-flop feeds the J/K input of the second flip-flop (FFB). This puts the second flip-flop into a hold/toggle mode. The Q and !Q outputs of both flip-flops are ANDed together as follows.

FFA QFFA !QFFB QFFB !QSignal
XXCLK1
XXCLK2
XXCLK3
XXCLK4

Scalar: The Scalar module successively divides the first clock pulse in half using six 4-bit counters. Three clock pulses are pulled out of the scalar. The F10 output is a 100 Hz clock pulse used to drive the time counters. The F13 output is a 12.5 Hz clock pulse used in the TPG module. The F17 output is a 0.78125 Hz clock pulse used in the TPG module. These can also be manually generated for debug purposes. Note that a 2.048 mHz crystal is not available, so a 2.0 mHz crystal will be used. This means the real time clock will not be exactly accurate, but close enough.

The first 4-bit counter divides the 1 MHz clock pulse by ten, resulting in a 51.2 kHz clock pulse. The next five 4-bit counters count down and therefore divide the 51.2 pulse by two.

Timing Pulse Generator: This module is a state machine used to sequence through the timing pulses that make up a memory cycle. There are 12 timing pulses plus two additional pulses to perform startup and two pulses to perform debugging. These 16 timing pulses are cycled via a 4-bit counter. Most state changes occur via the clock 1 pulse unless the system is being debugged. In this case there are state change equations that drive the state machine.

The timing pulse generator is driven by CLK1 which is delayed so the write bus can be written to before the TPG changes state.

There are a number of state changes that are numbered and listed below. Note that moving from TP1 through TP12 is performed via the counter which free runs if the CET input is 1.

From StateTo StateNumberEquation
STBYPWRONTPG_0!RESET * (!FCLK + F17)
PWRONTP1TPG_1!FCLK + F13
TP12TP1TPG_2(RUN * !BPHIT) + (!SNI * INST)
TP12STBYTPG_3SNI + SA
TP12SRLSETPG_4BPHIT = (BPEN * ISBP) + (IBPEN * ISRUPT) + (CBPEN * PINC)
SRLSEWAITTPG_5!STEP
WAITTP1TPG_6STEP + RUN

The logic driving this implementation depends heavily on the 74LS161 counter chip. There are two inputs that drive the logic. The first one is the PE or NPE pin. When set low, data is loaded from D0-D3. This data sets the next state to TP1. When NPE is set high, the logic is controlled by the CET pin. The CET pin, when set low operations are put on hold. When high, the chip counts once per clock pulse. The following table shows each state and what these pins need to be to move to the next state.

StateCurrent DCBANext DCBA012345678NPECETComment
STBY0 0 0 00 0 0 00xxxxxxxx10Goto STBY
..0 0 0 11xxxxxxxx11Goto PWON
PWON0 0 0 10 0 0 1x0xxxxxxx10Goto PWON
..0 0 1 0x1xxxxxxx11Goto TP1
TP10 0 1 00 0 1 1xxxxxxxxx11Goto TP2
TP20 0 1 10 1 0 0xxxxxxxxx11Goto TP3
TP30 1 0 00 1 0 1xxxxxxxxx11Goto TP4
TP40 1 0 10 1 1 0xxxxxxxxx11Goto TP5
TP50 1 1 00 1 1 1xxxxxxxxx11Goto TP6
TP60 1 1 11 0 0 0xxxxxxxxx11Goto TP7
TP71 0 0 01 0 0 1xxxxxxxxx11Goto TP8
TP81 0 0 11 0 1 0xxxxxxxxx11Goto TP9
TP91 0 1 01 0 1 1xxxxxxxxx11Goto TP10
TP101 0 1 11 1 0 0xxxxxxxxx11Goto TP11
TP111 1 0 01 1 0 1xxxxxxxxx11Goto TP12
TP121 1 0 11 1 0 1xxx1xxxxx0xGoto STBY
..0 0 1 0xx10xxxxx0xGoto TP1
..1 1 1 0xx00xxxxx11Goto SRLSE
..1 1 1 0xx101xxxx11Goto SRLSE (Address Breakpoint)
..1 1 1 0xx10x1xxx11Goto SRLSE (Counter Breakpoint)
..1 1 1 0xx10xx1xx11Goto SRLSE (Interrupt Breakpoint)
SRLSE1 1 1 01 1 0xxxxxxx0x10Goto SRLSE
..1 1 1 1xxxxxxx1x11Goto WAIT
WAIT1 1 1 10 0 1 0xxxxxxxx10xGoto TP1
..1 1 1 1xxxxxxxx010Goto WAIT

The equations that need to be implemented to drive the NPE and CET pins is as follows. Note that the equations derive the negative because the zeros are the simplest to implement.

NPE = (WAIT * TPG_6) + (TP12 * TPG_3) + (TP12 * TPG_2 * (!TPG_3 + !TPG_4))
CET = (STBY * !TPG_0) + (PWON * !TPG_1) + (SRLSE * !TPG_5) + (WAIT * !TPG_6

Control Pulse Matrix: This module is divided into three sections; CPM-A, CPM-B and CPM-C. CPM-A uses nine EPROMs to house 72 of the original control pulses defined in the original NASA documentation. I created a 16-bit address to define each of the sub-sequence timing pulse control pulses. There can be up to five control pulses defined for each timing pulse. It should be noted that there were minor mistakes in the CPM definition in the original documentation.

The address for the EPROMs are as follows:

BitPurpose
1BR2
2BR1
3-6Timing Pulse
7-9Stage
10Channel Bit
11-12Quarter Code
13-15Op Code
16Extend Bit

Bits 10-16 are identical to what is defined above except for one sub-sequence, PINC. Instead of extending the address, PINC uses an unused code where bits 10-16 are all 1's. The bits are stored and passed around the system as negative logic, i.e. 0 = asserted.

The EPROMs are laid out as a matrix of individual bits, 72 bits per unique address. These bits are inverted, i.e. 1’s complement negative logic. Eight control pulses are defined per EPROM. The first EPROM defines control pulses 1 through 8, the second defines 9-16 and so on until the last EPROM which defines 65-72 control pulses.

BitsEPROM Name
1-8CPM1_8.hex
9-16CPM9_16.hex
17-24CPM17_24.hex
25-32CPM25_32.hex
33-40CPM33_40.hex
41-48CPM41_48.hex
49-56CPM49_56.hex
57-64CPM57_64.hex
65-72CPM65_72.hex

It should be noted that the DINC sub-instruction is not implemented. The MOUT, POUT and ZOUT control pulses are only used by DINC, therefore these have been removed from the list and the control pulses have been re-numbered. This gets the count down to 72, which takes the EPROM chip count down by one.

CPM-B takes a few of the control pulses and asserts other, mostly internal control pulses based on the instruction address. These include RSC, WSC, WG, RCH, WCH, RAD and ZIP. The ZIP processing generates WY, RB, RC, WYD, CI and MCRO as follows.

L15L2L1AddrREADWRITECARRYREMAINDER
0000WY
0011RBWY
0102RBWYD
0113RCWYCIMCRO
1004RBWY
1015RBWYD
1106RCWYCIMCRO
1117WYMCRO

CPM-C asserts control pulses based on the timing pulse of the memory cycle. This drives such things as reading and writing memory values. The state of the TPG cycle drives what control pulses are asserted. The following table is a summary of the control pulses that are asserted for each state and any logic required to assert that pulse. This logic can also be found in the CPM.java code. Some of the more complicated “if” statements are summarized into their own control pulses defined after the table.

StateControl Pulses Asserted and Logic
STBYassert GENRST
PWRONassert NISQ, RSTRT, WZ
TP1if DOTP1 then set CLREXT reg bit, if WS then assert WTS
TP3if STAGE is asserted then assert DCDSUB
TP4if DOTP4 then assert SBWG
TP6if DOTP6 then assert SBWG
TP7if DOTP7 then assert WE
TP10if DOTP10 then assert WE
TP11assert WTS, WPCTR. If sub-sequence = RSM3 then assert CLRP
TP12(see TP12 below)

TP12:

if SNI register bit is 1 then 
	if IRQ asserted then assert INTR, RPT, INHINT

	else assert RB, WSQ

	assert CLISQ
 if DOTP12 then assert RSTSTG, DCDSUB
 if (runPINC & PreStage reg = 0 & EXT reg = 0) then assert PINC
 if CLREXT register = 1 then clear EXT reg bit & clear CLREXT reg bit

DOTP1 is set if (EXT reg bit = 1 & Stage reg != 2 & subseq != (NDX0/NDX1/NDXX0/NDXX1/DCA0/DCS0/DV0/DV1/DV3/DV7/MP0/MP1/RUPT0))

DOTP4 is set if (GTR7 is asserted & GTR1777 is not asserted & subseq != (DV3/DV7/DV6/DV4))

DOTP6 is set if (GTR1777 is asserted & subseq != (DV3/DV7/DV6/DV4))

DOTP7 is set if (WG/GTR7 are asserted & GTR177 is not asserted & WE is not asserted & subseq != TCF0 or subseq == ADS0/TS0/DAS1)

DOTP10 is set if (GTR7 is asserted & GTR1777 is not asserted & Stage reg != 2 & subseq != TCF0/TC0/BZF0/NDX1/MP1/MP3/RSM3/DV0/DV1/DV3/DV4/DV6/DV7/READ0/WRITE0/RAND0/WAND0/ROR0/WOR0/RXOR0/DXCH0/DXCH1/NDXX1)

DOTP12 is set if (subseq != DV0/DV1/DV3/DV7/DV6)

Notice that some of the signals have a suffix of “a” or “b” or “c”. This designates that more than one chip is asserting that signal. These need to to be ORed together so that there is only one source of the signal.

Sequence Generator: This module contains the staging logic, branching logic, SNI register and Sequence register. In Block I the stage register is two bits. In Block II the stage register was extended to three bits due to the divide instruction. I also needed to add a pre-Stage register to facilitate pre-staging of the divide instruction. I refer to the two registers as the Stage and preStage registers. The next sub-sequence stage is set up using the ST1, ST2 and DVST control pulses. These pulses set the preStage register and during TP12 the preStage register is moved the Stage register using the RSTSTG pulse.

The divide is unique in that it uses the STAGE pulse to increment to the next stage after TP3. The sequence of stages for the divide instruction is not sequentially numeric. It uses a simple routine that implements an inverted end-around carry. The best way to show this is visually. To increment the stage number the bits are shifted left one bit with the end around carry being inverted. This is ingenious because it avoids stage #2 which is reserved for the STD2 sub-sequence, it provides the required 6 sub-sequences and it is easy to implement.

Stage #Bit Pattern
0000
1001
3011
7111
6110
4100

The other interesting aspect of this is that it would normally take six memory cycles to implement the six stages. This was modified such that the DV0 sub-sequence only runs for three timing pulses before the DVST control pulse switches to the next sub-sequence. The next sub-sequence then runs from TP4 through TP12 and then TP1 through TP3. At the end DV4 only has TP4 through TP12 defined. This allows the divide instruction to only take five memory cycles.

The branching register consists of two bits that are set by five test control pulses; TSGU, TSGN, TSGN2, TOV and TPZG. These use simple gate logic to decide if the two branch values should be set. The following equations show how BR1 and BR2 are set. The variables U16, L15 and WB15 indicate the values of those register's bits.

BR1 = (TSGU * U16) + (TL15 * L15) + (TSGN * WB15) + (TOV * WB16)
BR2 = (TSGN2 * WB15) + (TOV * WB15) + (TPZG * GMZ) + (TMZ * WBMZ)

The Sequence Generator decodes the sub-sequence using the opcode, quarter code, channel bit and stage values. To simplify the logic, these are stored in a single EPROM. A number of these sub-sequence values are used throughout the computer to drive logic based on which sub-sequence is running. Some of these values are combined to determine when other internal control pulses should be asserted. The 10-bit input “address” to the EPROM is as follows: EOOOQQCSSS where E = the EXTEND bit, OOO = the 3 bit opcode, QQ = the 2 bit quarter code, C = the channel bit and SSS = the 3 bit stage. The output of the EPROM is a 6-bit number representing the subsequence number defined in the design section.

The instruction decoding address depends on the “don't care” bits of the instruction table shown in the design section being zeroed. The table below lists the bits based on the opcode groups. In code this is an if, else if, else if, else construct.

IndexEXTSQ5SQ4SQ3SQ2SQ1SQ0STG3STG2STG1
Stage = 20000000STG3STG2STG1
Order = 010EXTSQ5SQ4SQ3SQ2SQ1SQ0STG3STG2STG1
Order = 1, 2, 5, 011, 012, 016EXTSQ5SQ4SQ3SQ2SQ10STG3STG2STG1
ElseEXTSQ5SQ4SQ3000STG3STG2STG1

The resulting logic is as follows:

The three Stage bits are always the stage value.

The EXT, SQ5, SQ4 & SQ3 bits are their normal values unless the Stage value is 2, then the bits are zeroed.

The SQ0 bit is zero unless the Order code is octal 10.

The SQ1 and SQ2 bits are their true values if the Order code is octal 1, 2, 5, 10, 11, 12 or 16. This last equation is the only one that is not simplistic. If the bits are placed in a truth table (see below) are the values that produce a true, then the following equation is derived. As before, the NOT value is shown as an underline versus an over bar. The NOR is represented as a ||.

ValueA B C D
10 0 0 1
20 0 1 0
50 1 0 1
91 0 0 1
101 0 1 0
141 1 1 0
Result = (!A !C D) + (!B !C D) + (!B C !D) + (A C !D) = (!C !D) || (C D) || (!A B !D) || (A B !C)

Sheet CTL_SEQ_CTL has logic that implements the “if” statements of CPM-C. The naming convention is based on the timing pulse the “if” statement is associated with, such as DOTP1 is the “if” construct for timing pulse 1. The “if” statements are shown below.

TP1 = if (EXTEND == 1 & STG != 2 & !NDX0 & !NDX1 & !NDXX0 & !NDXX1 & !DCA0 & !DCS0 & !DV0 & !DV1 & !DV3 & !DV7 & !MP0 & !MP1 & !RUPT0)
TP4 = if (GTR_7 & !GTR_1777 & !DV3 & !DV7 & !DV6 & !DV4)
TP6 = if (GTR_1777 & !DV3 & !DV7 & !DV6 & !DV4)
TP7 = if (WG & GTR_7 & !GTR_1777 & !TCF0 & !WE)
TP10 = if (GTR_7 & !GTR_1777 & !TCF0 & !TC0 & STG != 2 & !BZF0 & !NDX1 & !MP1 & !MP3 & !RSM3 & !DV0 & !DV1 & !DV3 & !DV4 & !DV6 & !DV7 & !READ0 & !WRITE0 & !RAND0 & !WAND0 & !ROR0 & !WOR0 & !RXOR0 & !DXCH0 & !DXCH1 & !NDXX1)
TP12 = if (!DV0 & !DV1 & !DV3 & !DV7 & !DV6)


Discussions