It’s time to translate my intent for the MicroCode Sequencer into design decisions.
Narratively, when the CPU executes an instruction:
- The Instruction Controller presents the MC Program Counter where execution should start (MC_PC_START) and sets the MC_START bit
- The MC Sequencer outputs the control signals from the MC ROM at the address specified by the MC_PC to the ALU and other parts of the CPU
- At every clock cycle when an instruction is executing*, the MC Sequencer increments the MC_PC or loads a new value entirely in the event of a Jump or Branch, loop back to 3
- At the end of an instruction, the MC ROM sets a signal (MC_DONE) that prevents the MC_PC from loading a new value and instead stops execution and signals the Instruction Controller that the MicroCode Sequencer is done.
*Note (from above): whenever the MicroCode Sequencer requests a memory read or write from the CPU bus controller, it should wait any number of clock cycles until the memory operation completes. So, in the case of memory-wait condition, the MC PC won’t actually advance on every clock cycle.
The inputs to the MicroCode Sequencer will be:
- MC_START - Instruction Controller calls the MicroSequencer to start
- MC_PC_START - Starting MicroSequencer Program Counter, supplied by Instruction Controller
- BUS_CONTROLLER_DONE - Memory has been read and data is available, or data has been written
- COND - Branch condition bits extracted from the Instruction Word by the Instruction Controller
- BRA_OFF - Branch condition relative offset extracted from the Instruction Word by the Instruction Controller
The outputs of the MicroCode Sequencer are:
- To the ALU
- A bunch of signals that I now realize I can’t define until I design the ALU
- Other
- BUS_CONTROLLER_READ_REQUEST - to read instructions and data
- BUS_CONTROLLER_WRITE_REQUEST - to write data to RAM
- MC_DONE - Signal to Instruction Controller that the MicroSequencer is done executing the current instruction
- MC_PC_SEL - Select which data to load into the MC_ProgramCounter on the next clock cycle:
- 00 = 0, used for RESET
- 01 = BRANCH_REGISTER
- 10 = INSTRUCTION CONTROLLER START ADDR
- 11 = NEXT = MC_PC+1
Let’s address the implementation itself.
We’ll use a simple S-R flip-flop to track whether the MC_Seq is busy or idle.
When the MC_Seq is done with an instruction, we’ll use an S-R flip flop to generate a one-clock-cycle DONE pulse to the Instruction controller.
We’ll use a register to capture the MC_START_ADDRESS on every clock cycle when the MC_Seq is idle, so that when we receive MC_START, the start address is already captured.
We’ll need some logic to examine MC_Seq control signals like ready/busy, branch, jump, done, and RESET to determine which value to load into the MC_PC. Conceptually:
- If RESET, load the value 0x0000
- If the MCSeq is not busy, load from MC_PC_START
- If the MCSeq is busy and done, don’t care
- If the MCSeq is busy, not doing a branch or jump, and not done, load from MC_PC_INC
- If the MCSeq is busy and doing a jump, load from MC_ROM_LOW_BITS
- If the MCSeq is busy and doing a branch, and the CCR_Condition matches, load from MC_ROM_LOW_BITS
- If the MCSeq is busy, and doing a branch, and the CCR_Condition does not match, load from MC_PC_INC
I’ll use a 4-1 multiplexer for the routing of the MC_PC address.
Select mapping for the multiplexer is:
Select | Input |
00 | 0x00 |
01 | MC_ROM output low-bits |
10 | MC_START_ADDR |
11 | MC_PC + 1 |
To implement the logic of selecting which source to use for the next MC_PC, I’ll use this Karnaugh map:
Jump, Branch, CCR Match | |||||||||
000 | 001 | 011 | 010 | 110 | 111 | 101 | 100 | ||
Reset, Busy, Done | 000 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 |
001 | d/c | d/c | d/c | d/c | d/c | d/c | d/c | d/c | |
011 | d/c | d/c | d/c | d/c | d/c | d/c | d/c | d/c | |
010 | 11 | 11 | 01 | 11 | n/a | n/a | 01 | 01 | |
110 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | |
111 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | |
101 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | |
100 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
n/a - not applicable, condition should not arise
d/c - don’t care
Select_Bit_1 = AND(Not_RESET, Not_Busy) OR AND(Not RESET, Not_Jump, Not_Branch) OR AND(Not_RESET, Branch, Not_Match)
Select_Bit_0 = AND(Not_RESET, Busy, Not_Done)
I’ll route the output of the MC_PC latch to an adder to prepare MC_PC + 1.
I’ll route the output of the MC_PC to the MicroCode ROM, which will be quite wide (I’m expecting 20-40 bits wide).
Except during MicroCode branch operations, I’ll want to route the MC ROM output to the ALU (and other CPU sections), but will want to suppress that output for some of the bits during Jump and Branch instructions, so I’ll use some tri-state output buffers to control when the whole output of the MC ROM is passed to the ALU, etc.
During MicroCode Branch operations, I’ll want to route the condition bits to the ALU CCR section to perform the comparison, so I’ll use some tri-state output buffers to control when the condition select bits are routed to the CCR condition test logic.
Here’s the resulting circuit diagram. If you look in detail, you’ll see that it is currently set up for a 4-bit MC_ROM address space (OK for simulation, not realistic) and a 16-bit MC_ROM output (also OK for simulation, not realistic).
I did a simulation of the circuit and it seems to work OK. Again, famous last words…
Next, I’ll take on the design of the ALU. That will help me define the ALU control signals I’ll need.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.