K2-16/16 Fully Transistorized Computer • no relais! • no valves! • no lamps! This is a bit slicing CPU built from transistors, diodes and resistors. It executes microcode from an external memory. Address bus and data bus have same width! Suggested width is 16 bit, minimum usable width is 8 bits, a demo 'blinky' may run on 4 bits. An 8 bit CPU with 16 bit addresses may be built using external hi-byte registers on the memory board. Microcode width is (at least) 16 bits. A microcode instruction is always executed in 2 clock cycles: 1. fetch microcode 2. execute microcode (including memory access) The CPU has no flag registers. Instead the microcode is organized in 2 planes and the state after each microcode is immediately used to switch between these two planes. Electrical values: 2A at 5V logic levels: 0 = GND 1 = Hi-Z (high impedance, isolated) Estimated costs: ~2000 transistors ~2000 resistors ~5000 diodes (without rom or ram) 16 blades 1 base board (inter-blade bus) 2 boards for control unit 1 board for memory and i/o ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– Busses: A-bus: connected to memory address output. target for arith./boolean functions D-bus: connected to memory data bidirectional bus. 2nd input for functions ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– Registers: registers are addressed with 3 bits. therefore at most 8 registers are possible, but one index is deliberately left unused to be used as dummy data sink and one index selects the external memory. 000 PC program counter for microcode instructions IP general purpose register, normally used as program counter for assembler opcodes SP general purpose register, normally used as stack pointer X general purpose register Y general purpose register Z general purpose register, sometimes used as scratch for some opcodes NN unused slot 111 MEM external memory ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– Microcode: microcodes are 16 bit wide. if the data width is at least 16 bits, then microcode can be stored in user ram and user rom. else a dedicated 16-bit microcode rom must be used. if a dedicated microcode rom is to be built from diodes, then microcodes with predecoded registers should be stored. in each memory access the addressing register is signalled to the memory board. this can be used to dedicate certain registers to certain devices, e.g. PC accesses microcode rom, Z is used only for i/o to not waste memory space. microcode is organized in 2 planes. each microcode has a 1- or 3-bit field to select a condition on which to swich between these planes. microcodes with branches requires both planes, microcodes without branches does not, and short sequences can be fitted into the gaps of the others. in a 16-bit system the plane-bit should be D14. (assuming 32k of shared assembler code / microcode rom). The CPU has no flag registers. The state after each microcode is used immediately to select the plane for the next microcode. Opcodes written in microcode cannot set and test flags. Instead flag results must be stored in registers, suitable for FORTH or C. There are 2 basic types of microcodes: MOVE and FUNC, selected by bit15. during MOVE, data is copied on the A-bus and on the D-bus simultaneously. normally the data on the A-bus is used to address external memory optionally with increment and decrement. the D-bus is the major bus for data movement, between registers or to and from external memory. The CPU can add, subtract, increment, decrement, shift right, and all boolean functions with 0, 1 or 2 inputs. The CPU cannot natively negate. The CPU cannot natively call a subroutine in microcode. Instead 4 or 5 microcodes must be combined. The CPU is designed to use stack-based assembler opcodes addressed with the IP register. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– A microcode has 16 bits. There are 2 busses: Address and Data. External memory is connected to Data.out and Addr.out. Address is always routed through Function: Address is split into Addr.out and Func.out. Function unit is connected to Addr.out, Data.out and Func.out. There are 2 groups: MOVE and FUNC. These are selected by bit 15: MOVE: 0 AAA c DDD Caaa fddd FUNC: 1 AAA c DDD CCCF ffff AAA = Reg.Addr.out: source register for Addr.out. Address is always output to Addr.out. DDD = Reg.Data.out: source register for Data.out. Data is always output to Data.out. aaa = Reg.Addr.in: MOVE: destination register for Func.out. FUNC: aaa = AAA Address is always loaded from Func.out. ddd = Reg.Data.in: MOVE: destination register for Data.out. FUNC: not loaded. (ddd forced to register NN or by gating all Data.in enable lines) c = CYin: carry input to adder and shift right CCC = COND: condition to select code plane for next microcode. MOVE: C is extended to CCC to select 'stay in plane' or 'Data.Zero'. F = select Arith. / boolean result: FUNC: output select for Func.out: 0 = Adder 1 = Boolean MOVE: always Adder. ffff = boolean result bits: FUNC: boolean function results for all four combinations of A and B. MOVE: f is extended to 0000 or 1111 as required for INC or DEC. CYin must be set or cleared for INC or DEC or a plain MOVE. –––––––––––––––– COND: condition to select code plane for next microcode. CCC is encoded so that Cxx in MOVE can be easily extended to CCC to select 'stay in plane' or 'Data.Zero'. 000 stay in plane or 011 100 Data.Zero or 111 ... Func.Zero all others may be assigned arbitrarily CYout Data.bit0 Data.bit15 Func.bit0 Func.bit15 –––––––––––––––– MOVE: Move data on A and D bus simultaneously. A can be moved unmodified, incremented or decremented. If memory is accessed then data and address are on D-bus and A-bus. D -> D D -> D and A|A++|A-- -> A D -> (A|A++|A--) (A|A++|A--) -> D –––––––––––––––– FUNC: Arithmetic/boolean function A • B -> A Bit F=1 selects boolean and F=0 selects arithmetic result for output. The boolean result is also fed into input B of the adder. The 4 possible results of the desired boolean operation are provided by bits ffff. Bit c is attached to CYin of adder and ShiftRight. A=0011 B=0101 BOOL ADDER |||| ffff: 0000 always 0 --> 0x0000, c=1:INC, (c=0:COPY) 1111 always 1 --> 0xFFFF, c=0:DEC, (c=1:COPY) 0011 A --> COPY, SL 1100 !A --> CPL, (c=0:0xFFFF), (c=1:0x0000) 0101 B --> (COPY), ADD 1010 !B --> (CPL), SUB 0001 AND --> AND 0111 OR --> OR 0110 XOR --> XOR 1110 NAND --> NAND 1000 NOR --> NOR 1001 EQU --> EQU 0010 A > B --> A & !B 0100 A < B --> B & !A 1011 A ≥ B --> A | !B 1101 A ≤ B --> B | !A MOVE: A -> A++: f=0 --> ffff=0000 and c=1 A -> A--: f=1 --> ffff=1111 and c=0 A -> A: f=0 --> ffff=0000 and c=0 (recommended) note: Address is always loaded! A -> A: f=1 --> ffff=1111 and c=1 ShiftRight: All bits are used so a special condition must be decoded to select 'ShiftRight'. F=1 and ffff=1111 for boolean result 0xFFFF is easy to implement: easy to decode (5-NAND) and easy to output to Func.out because the output of Func.out needs not to be disabled because it is always 1: Hi-Z.