A couple of months have passed till I could eliminate a long-standing issue. After making minor modifications to the control board, and also improving the RAM/ROM module the whole thing has stopped working. Voltage level dropped to about 2.0 V. I have almost given up, since I did not have a clue what was causing this problem. I checked, doublechecked and triplechecked all boards, all connections in vain.
I have spent hours and days to resolve this issue. Finally, I took everything apart. Extended the tester module to give additional control signals for the clock generator. I connected the RAM/ROM, the clock generator and the test modules. Ok, it worked fine. Then, I started adding more boards...
But no signals could be detected at the control board. This was due to some bad wiring, leaving out ground signals. Ok, added ground signals. Almost everything was connected and it was still working.
But when I have plugged the address/control switch bus (bus 5), the clock faded. I tested it wire by wire to find out that again a power line (bad soldering) and ground signal were responsible for this. Finally, it worked just fine, the clock, the RAM/ROM, the control board together.
Then, I have added the ALU and register modules. So everything is interconnected as depicted in the picture below and the whole thing is just working fine. At least it seems to be working. I still need to test ALU and register modules being conntected. Once it is done, I could continue with the finishing touches. Basically I just need to add some EPROMs (with micro-programs) and some control logic.
The vast majority of FunCPU has been completed. Static modules (ALU and registers) are being verified now. The image below shows three modules interconnected. The upportmost is the test module. In the middle one can see the register file, and on the bottom the ALU can be found.
Once static function has been verified, the micro-sequence can implemented and connected to these modules. Numerous tests are to be performed to check if everything is working as expected. Applying different setup conditions defined by the 6 DIP switches mounted on the test-board (note: an additional bus must have been added to the test module.) I can check each register and ALU function by observing the ALU or register output connected to the LED bars on the test module.
The following basic cases have been identified:
- RI_SET (this is tricky, as first DI must be incremented, then cleared)
- DI_CLR_, DI_SET_, DI_INC - clear, set and increment DI respectively.
- SI_CLR_, SI_INC - clear and increment SI respectively.
- V_CLR_, V_SET - clear and set V respectively.
- S_SET, S_CLR - again for set and clear operation executed on S.
- SCZ_SET, SCZ_CLR - same for the SCZ register.
- AC_INC, AC_SET - increment and set argument counter respectively. Special care must be taken to check the working of argument counter flag.
- FI_SET_, FI_INC - setting and incrementing of function index register.
- V register must be tested against different ALU setup (decrement, identity and increment modes).
- Address multiplexer should generate address signals as designed one of the following sources: SI, DI, FI, SI+V'. The last one is an integration test, involving ALU operations.
The following buses will be used for input for register module:
- B10 to provide power supply, classification information and raw 8 bit data.
- B11 to supply most of the control signals (including the two-phase clock) to be generated by the micro-sequencer.
- B12 to transfer additional control information also determined by the micro-program.
The following buses will supply input for the test module:
- B9 - transferring the 7 bit result (MSB is preserved).
- B13 - displaying the 8 bit address.
- B14 - supplying status, classification and argument counter zero flag to micro-sequencer.
- B16 - containing the full 8 bit result of the ALU operation.
In addition to that, of course register and ALU modules are interconnected as the image above also reveals.
|init||0000||Initial, starting state. FunCPU remains in this state until it determines the lenght of the input expression|
|start||0001||Start phase is entered upon each reduction cycle. FunCPU decides if the expression is reduced, or the reduction process should continue.|
|copy||0010||In this state FunCPU copies one symbol after another from the source expression to the target.|
|inc||0011||Increment function is being executed.|
|dec||0100||Decrement function is being executed.|
|if-in||0101||if-then-else function is found. If the condition is constant, then the appropriate branch state is reached.|
|if-then||0110||Condition was true, the "then" part is selected.|
|if-skip-else||0111||Condition was true, the "else" part must be ignored.|
|if-else||1000||Condition was false, the "else" part must be selected.|
|if-skip-then||1001||Condition was false, the "then" part is ignored.|
|func-in||1010||User defined function entry. In this state the FunCPU checks, if all the arguments are available as constant, thus the function can be executed. Otherwise the function reduction is postponed.|
|func-copy||1011||FunCPU is unfolding the function definition and performing argument binding.|
|err-iin||1100||Some unexpected condition is encountered, most probably due to some hardware error or error in micro-program. Program execution is aborted.|
|err-arg||1101||Function with less argument is found, than expected. Program execution is aborted.|
|err-empty||1110||Empty expression or function definition is found. Program execution is aborted.|
|stop||1111||Input expression has been succesfully calculated. The processor will idle its final state and the result value is observable by looking at the data LEDs.|
The register file module has been completed as it is shown in the picture below. Please refer to architecture concept for an overview of registers and overall architecture.
It comprises the following registers:
- RI (redex index) is implemented as a 74HC273 and used to store temporary the destination index value during function reduction. If a function cannot (yet) be called (because some of its arguments are not yet available. i.e. they are not constants), then reduction reverts to the target position defined by this index.
- DI (destination index) is composed of two four bit registers using two 74HC161 chips. This holds the value of the destination register. This value can be stored temporary to and restored from RI.
- SI (source index) is implemented in similar fashion, it stores the source index, it is selected to read the current symbol of source expression.
- FI (function index) is also implemented similarly with two counters, it stores the function index, which is used to read/map the function definitions.
- AC (argument counter) is implemented with a single 74HC161 chip. This acts as the argument counter to facilitate argument interpretation and handling. Note: this counter is slightly differently used as originally proposed to save parts and reduce complexity. This is also an up-counter, with the twist, that the binary value 1111 represents the "zero" value. Applying this encoding, the zero value can be detected by looking at the TC flag output.
- V (value register) is implemented as a 74HC273. This register is used analogously to the accumulator of a traditional CPU (mainly accumulator machines). Calculated values are fetched to and from here between the memory and ALU.
- S (state register) employs a 74HC175 to store processor state. This is a four bit value, 0000 represents initial state, whereas 1111 is the final state, when computation is finished and result is available. This register is refreshed, whenever a state transition is required, as governed by the micro-code.
- SCZ - serves two purposes. The lower 3 bits are used to store the symbol classification value, the most significant bit is a flag, which tells whether argument count reached "logical" (recall the strange encoding) zero value. This register is usually refreshed upon restarting the micro-code sequence.
Please note that no register is fully or partially exposed to the programmer. The programming model does not make use of any registers directly. In fact, none of the registers can be manipulated or even accessed explicitly by the user program. All of these registers are, however, manipulated by the micro-sequencer, but it is hidden from the programmer.
- No conventional PC (program counter) is employed.
- No stack is present.
- No nonventional processor state register or even flags (to denote zero, carry, overflow and so on) can be found here.
A small portion of the register module is dedicated to a 4 input multiplexer, which is introduced to select among four different address values. These are as follows:
- SI - address determined by the source index. Generally used to fetch the next symbol of the expression being reduced.
- DI - address determined by the destination index. This is selected, if the part of the reduced expression must be stored to the resulting expression.
- FI - address as defined by the function index. This input is chosen when function definition is being unfolded.
- Finally, SI + V', which is used when binding argument values in the course of unfolding function definition. Note: in such case SI register points to the next symbol in source expression. Assuming that the function has three arguments: SI-1, SI-2 and SI-3 represent argument 3, 2 and argument 1 respectively. Recall that argument numbers are encoded in two's complement form, therefore by simply adding SI and V' (modified V value holding the argument number in the binary form 1111 11xy) gives us the parameter value to be bounded to the argument in question.
Register board being one of the main boards implements quite a fiew buses.
Bus 10 is utilized to drive data and tag classification signals from the RAM board.
Bus 12 transfer further signals from the micro-sequencer.
Bus 13 supplies a 8 bit address signal to the RAM module.
Bus 14 provides the current CPU state, symbol class along with argument counter zero flag (as defined by the S and SCZ registers respectively).
Bus 15 supplies two ALU input sources, the SI and the V register.
Bus 16 receives the 8 bit ALU result, which is fed to the address multiplexer.
Finally, bus 17 is used to pass some control signals together with argument counter and power supply lines to ALU.
The ALU module is very simple and straightforward. It is mounted together with the control board encoding logic on a single board. It performs a simple addition operation on the two operands as described in the following table. The two selector bits define the two input sources.
0000 0000 (0)
Identity / copy
Only four bit value is used
1111 1110 (-2)
Used to set AC to 1 argument, provided that V is zero beforehand
0000 0000 (0)
1111 1110 (-2)
*these combinations are actually not used.
where V’ denotes 1111 11 V(1) V(0), where V(1) and V(0) are the two least significant bits of V.
where V’ denotes 1111 11 V(1) V(0), where V(1) and V(0) are the two least significant bits of V.
Notice that the ALU is able to generate V (identity), V-1 (decrement), V+1 (increment) and V-2 based on the V value.
Please also observe that since this is a 7-bit CPU, it is not desirable to go out of the seven bit range. In other words, (without the tag interpretation) when V is holding a constant it cannot go beyond the range of 0..127 no matter if it is incremented or decremented. Therefore when storing the modified V value (here we mean solely V-1, V+1, but not V’) to the expression cell as defined by the destination register, the original most significant bit of the V register is fed back along with the possibly modified 7 bit part of the ALU result. This way the highest bit is always reserved and the actual constant part of the result value must lie in the 7 bit range.
Partially, in line with the aforementioned, the content of V register is not directly connected to the databus, instead, the ALU output (except for the MSB, which is coming from the V register) is released to the databus.
Input sources are mapped by six multiplexers. Four 74HC153s are utilized to select among four sources (SI, AC, 0, -2), and two 74HC157s are used to select between V and V'. The outputs of these multiplexers are fed to two 4008s to perform the 8 bit addition.
Bus 15 connects register and ALU modules transferring the following signals to the ALU:
Bus 16 supplies the full 8 bit ALU result to the register module. This value is used in the course of argument binding.
Bus 9 has similar content, with a different arragnement. It transfers signal from the ALU to the RAM module.
Bus 17 provides the signals to the ALU coming from the register modules as depicted in the table below.
Other buses are dedicated to signals related to keyboard processing. These are as follows:
Bus 8 is feeding RAM module with the encoded 8 bit data as defined by the user interface on the control board.
Bus 2 is a two way bus, accepting values from the control board (D0..D7) and also telling the control board what datalines can be active in a particular setup (i.e. when hotkeys are pressed) (DE0, DE1, DE6, DE7).
Finally, bus 3 accepts hot key signals from control board. Based on this information along with the 8 bit data determined by the 8 toggle switches (coming via the bus 2), the actual 8 bit data is generated, and then transferred in bus 8 to the RAM.