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 Q | FFA !Q | FFB Q | FFB !Q | Signal |
---|---|---|---|---|
X | X | CLK1 | ||
X | X | CLK2 | ||
X | X | CLK3 | ||
X | X | CLK4 |
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 State | To State | Number | Equation |
---|---|---|---|
STBY | PWRON | TPG_0 | !RESET * (!FCLK + F17) |
PWRON | TP1 | TPG_1 | !FCLK + F13 |
TP12 | TP1 | TPG_2 | (RUN * !BPHIT) + (!SNI * INST) |
TP12 | STBY | TPG_3 | SNI + SA |
TP12 | SRLSE | TPG_4 | BPHIT = (BPEN * ISBP) + (IBPEN * ISRUPT) + (CBPEN * PINC) |
SRLSE | WAIT | TPG_5 | !STEP |
WAIT | TP1 | TPG_6 | STEP + 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.
State | Current DCBA | Next DCBA | 012345678 | NPE | CET | Comment |
---|---|---|---|---|---|---|
STBY | 0 0 0 0 | 0 0 0 0 | 0xxxxxxxx | 1 | 0 | Goto STBY |
. | . | 0 0 0 1 | 1xxxxxxxx | 1 | 1 | Goto PWON |
PWON | 0 0 0 1 | 0 0 0 1 | x0xxxxxxx | 1 | 0 | Goto PWON |
. | . | 0 0 1 0 | x1xxxxxxx | 1 | 1 | Goto TP1 |
TP1 | 0 0 1 0 | 0 0 1 1 | xxxxxxxxx | 1 | 1 | Goto TP2 |
TP2 | 0 0 1 1 | 0 1 0 0 | xxxxxxxxx | 1 | 1 | Goto TP3 |
TP3 | 0 1 0 0 | 0 1 0 1 | xxxxxxxxx | 1 | 1 | Goto TP4 |
TP4 | 0 1 0 1 | 0 1 1 0 | xxxxxxxxx | 1 | 1 | Goto TP5 |
TP5 | 0 1 1 0 | 0 1 1 1 | xxxxxxxxx | 1 | 1 | Goto TP6 |
TP6 | 0 1 1 1 | 1 0 0 0 | xxxxxxxxx | 1 | 1 | Goto TP7 |
TP7 | 1 0 0 0 | 1 0 0 1 | xxxxxxxxx | 1 | 1 | Goto TP8 |
TP8 | 1 0 0 1 | 1 0 1 0 | xxxxxxxxx | 1 | 1 | Goto TP9 |
TP9 | 1 0 1 0 | 1 0 1 1 | xxxxxxxxx | 1 | 1 | Goto TP10 |
TP10 | 1 0 1 1 | 1 1 0 0 | xxxxxxxxx | 1 | 1 | Goto TP11 |
TP11 | 1 1 0 0 | 1 1 0 1 | xxxxxxxxx | 1 | 1 | Goto TP12 |
TP12 | 1 1 0 1 | 1 1 0 1 | xxx1xxxxx | 0 | x | Goto STBY |
. | . | 0 0 1 0 | xx10xxxxx | 0 | x | Goto TP1 |
. | . | 1 1 1 0 | xx00xxxxx | 1 | 1 | Goto SRLSE |
. | . | 1 1 1 0 | xx101xxxx | 1 | 1 | Goto SRLSE (Address Breakpoint) |
. | . | 1 1 1 0 | xx10x1xxx | 1 | 1 | Goto SRLSE (Counter Breakpoint) |
. | . | 1 1 1 0 | xx10xx1xx | 1 | 1 | Goto SRLSE (Interrupt Breakpoint) |
SRLSE | 1 1 1 0 | 1 1 0 | xxxxxxx0x | 1 | 0 | Goto SRLSE |
. | . | 1 1 1 1 | xxxxxxx1x | 1 | 1 | Goto WAIT |
WAIT | 1 1 1 1 | 0 0 1 0 | xxxxxxxx1 | 0 | x | Goto TP1 |
. | . | 1 1 1 1 | xxxxxxxx0 | 1 | 0 | Goto 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:
Bit | Purpose |
---|---|
1 | BR2 |
2 | BR1 |
3-6 | Timing Pulse |
7-9 | Stage |
10 | Channel Bit |
11-12 | Quarter Code |
13-15 | Op Code |
16 | Extend 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.
Bits | EPROM Name |
---|---|
1-8 | CPM1_8.hex |
9-16 | CPM9_16.hex |
17-24 | CPM17_24.hex |
25-32 | CPM25_32.hex |
33-40 | CPM33_40.hex |
41-48 | CPM41_48.hex |
49-56 | CPM49_56.hex |
57-64 | CPM57_64.hex |
65-72 | CPM65_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.
L15 | L2 | L1 | Addr | READ | WRITE | CARRY | REMAINDER |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | – | WY | – | – |
0 | 0 | 1 | 1 | RB | WY | – | – |
0 | 1 | 0 | 2 | RB | WYD | – | – |
0 | 1 | 1 | 3 | RC | WY | CI | MCRO |
1 | 0 | 0 | 4 | RB | WY | – | – |
1 | 0 | 1 | 5 | RB | WYD | – | – |
1 | 1 | 0 | 6 | RC | WY | CI | MCRO |
1 | 1 | 1 | 7 | – | WY | – | MCRO |
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.
State | Control Pulses Asserted and Logic |
---|---|
STBY | assert GENRST |
PWRON | assert NISQ, RSTRT, WZ |
TP1 | if DOTP1 then set CLREXT reg bit, if WS then assert WTS |
TP3 | if STAGE is asserted then assert DCDSUB |
TP4 | if DOTP4 then assert SBWG |
TP6 | if DOTP6 then assert SBWG |
TP7 | if DOTP7 then assert WE |
TP10 | if DOTP10 then assert WE |
TP11 | assert 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 |
---|---|
0 | 000 |
1 | 001 |
3 | 011 |
7 | 111 |
6 | 110 |
4 | 100 |
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.
Index | EXT | SQ5 | SQ4 | SQ3 | SQ2 | SQ1 | SQ0 | STG3 | STG2 | STG1 |
---|---|---|---|---|---|---|---|---|---|---|
Stage = 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | STG3 | STG2 | STG1 |
Order = 010 | EXT | SQ5 | SQ4 | SQ3 | SQ2 | SQ1 | SQ0 | STG3 | STG2 | STG1 |
Order = 1, 2, 5, 011, 012, 016 | EXT | SQ5 | SQ4 | SQ3 | SQ2 | SQ1 | 0 | STG3 | STG2 | STG1 |
Else | EXT | SQ5 | SQ4 | SQ3 | 0 | 0 | 0 | STG3 | STG2 | STG1 |
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 ||.
Value | A B C D |
---|---|
1 | 0 0 0 1 |
2 | 0 0 1 0 |
5 | 0 1 0 1 |
9 | 1 0 0 1 |
10 | 1 0 1 0 |
14 | 1 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
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.