46 July 2017
it accordingly. While this would work for columns 0x2 to
0x9, we would need another approach for the first two
That is why I ended up writing two units: one ALU
which concentrates on most arithmetic and logical
instructions; and a second unit (Logical Unit 2, or LU2)
which performs other operations shown in columns 0x0
and 0x1 (not all operations seen on those columns are
performed by LU2). The operation codes for both ALU and
LU2 were chosen to match opcode rows shown in Figure
Another important detail is that all instructions within
the same column and group are the same size in bytes,
thus can be decoded in the same decoder section.
The decoder design makes use of a large finite state
machine (FSM) which advances on each clock tick. Every
instruction starts in the CPU_DECOD stat. This is where
the decoder actually decodes the opcodes, prepares
buses and internal supporting signals, and steps to other
execution states. Among all those states, two are widely
used by a lot of instructions: CPU_OMA and CPU_OMA2.
Can you guess why? If you said because they are related to
ALU and LU2, you are absolutely right!
OMA is short for One Memory Access and it is the last
state for all ALU related instructions (ADD, ADC, ADDX,
ADCX, SUB, SBC, SUBX, SBCX, OR, ORX, AND, ANDX,
XOR, XORX, CP, CPC, TCM, TCMX, TM, TMX, and some
variants of LD and LDX). On the other hand, CPU_OMA2
DEC, DA, COM, RL, CLR, RRC, SRA,
SRL, RR, and SWAP).
Now, let’s take a look inside
the CPU_DECOD state. Refer to
Within the CPU_DECOD state,
we can see that a lot of action
takes place. In the beginning, some
temporary variables are initialized
to a default condition. Note that
NUM_BYTES is very important
as it controls how many bytes
were consumed by the instruction
decoder. Its value is used in the last
part of this stage to increment the
PC (program counter), advance the
queue read pointer, and decrement
the number of available bytes in the
Following the initialization
section, we can see the interrupt
processing section. It is responsible
for detecting any pending interrupts
and prepares the CPU accordingly.
I’ll cover this in the next section.
The actual instruction decoding block checks whether
a low power mode is not active and also if the debugger
mode is off (OCDCR.DBGMODE=0). Or, while in debug
mode, a single step debug command was issued (OCDCR.
DBGMODE=1 and OCD.SINGLE_STEP=1). It then checks
the available bytes in the queue and proceeds with
Some instructions (mostly the singlebyte ones) are
completed within the CPU_DECOD state, while others
need multiple states until they are fully completed.
Note that some instruction decoding can make use of
several functions and procedures written specially for the
y DATAWRITE — This procedure prepares buses for a
writing operation. It selects whether the destination is an
internal SFR, an external SFR, or a user RAM location.
y DATAREAD — This is a reciprocal function for
DATAWRITE. It is used to read a source address and
automatically chooses whether it is an internal SFR, an
external SFR, or a user RAM location.
y CONDITIONCODE — Used for conditional
instructions (such as JR and JP). It takes a four-bit condition
code, tests it, and returns the result.
y ADDRESSER4, ADDRESSER8, and ADDRESSER12 —
These functions return a 12-bit address from a four-, eight-,
or 12-bit source. They use the content of the RP register
to generate the final 12-bit address. ADDRESSER8 and
ADDRESSER12 also check for any escaped addressing
FIGURE 5. FPz8 interrupt system.