URL
https://opencores.org/ocsvn/light8080/light8080/trunk
Subversion Repositories light8080
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 7 to Rev 8
- ↔ Reverse comparison
Rev 7 → Rev 8
/trunk/doc/designNotes.tex
0,0 → 1,596
\documentclass[11pt]{article} |
\usepackage{graphicx} % needed for including graphics e.g. EPS, PS |
\usepackage{multirow} |
\usepackage{alltt} |
\topmargin -1.5cm % read Lamport p.163 |
\oddsidemargin -0.04cm % read Lamport p.163 |
\evensidemargin -0.04cm % same as oddsidemargin but for left-hand pages |
\textwidth 16.59cm |
\textheight 21.94cm |
%\pagestyle{empty} % Uncomment if don't want page numbers |
\parskip 7.2pt % sets spacing between paragraphs |
%\renewcommand{\baselinestretch}{1.5} % Uncomment for 1.5 spacing between lines |
\parindent 15pt % sets leading space for paragraphs |
|
\begin{document} |
|
\section{Basic behavior} |
\label{basics} |
|
The microcoded machine ($\mu$M) is built around a register bank and an 8-bit |
ALU with registered operands T1 and T2. It performs all its operations in two |
cycles, so I have divided it in two stages: an operand stage and an ALU stage. |
This is nothing more than a 2-stage pipeline. \\ |
|
In the operand stage, registers T1 and T2 are loaded with either the |
contents of the register bank (RB) or the input signal DI.\\ |
In the ALU stage, the ALU output is written back into the RB or loaded |
into the output register DO. Besides, flags are updated, or not, according to |
the microinstruction ($\mu$I) in execution.\\ |
|
Every microinstruction controls the operation of the operand stage |
and the succeeding ALU stage; that is, the execution of a $\mu$I extends over 2 |
succeeding clock cycles, and microinstructions overlap each other. This means |
that the part of the $\mu$I that controls the 2nd stage has to be pipelined; in |
the VHDL code, I have divided the $\mu$I in a field\_1 and a field\_2, the |
latter of which is registered (pipelined) and controls the 2nd $\mu$M stage |
(ALU). \\ |
Many of the control signals are encoded in the microinstructions in what I |
have improperly called flags. You will see many references to flags in the |
following text (\#end,\#decode, etc.). They are just signals that you can |
activate individually in each $\mu$I, some are active in the 1st stage, some in |
the 2nd. They are all explained in section ~\ref{ucodeFlags}. \\ |
|
Note that microinstructions are atomic: both stages are guaranteed to |
execute in all circumstances. Once the 1st stage of a $\mu$I has executed, |
the only thing that can prevent the execution of the 2nd stage is a reset.\\ |
It might have been easier to design the machine so that microinstructions |
executed in one cycle, thus needing no pipeline for the $\mu$I itself. I |
arbitrarily chose to 'split' the microcode execution, figuring that it would be |
easier for me to understand and program the microcode; in hindsight it may have |
been a mistake but in the end, once the debugging is over, it makes little |
difference.\\ |
|
The core as it is now does not support wait states: it does all its |
external accesses (memory or i/o, read or write) in one clock cycle. It would |
not be difficult to improve this with some little modification to the |
micromachine, without changes to the microcode.\\ |
Since the microcode rom is the same type of memory as will be used for program |
memory, the main advantage of microprogramming is lost. Thus, it would make |
sense to develop the core a bit further with support for wait states, so it |
could take advantage of the speed difference between the FPGA and external slow |
memory.\\ |
The register bank reads asynchronously, while writes are synchronous. This |
is the standard behaviour of a Spartan LUT-based RAM. The register bank holds |
all the 8080 registers, including the accumulator, plus temporary, 'hidden' |
registers (x,y,w,z). Only the PSW register is held out of the register bank, in |
a DFF-8 register. |
|
\section{Micromachine control} |
\label{umachineControl} |
|
\subsection{Microcode operation} |
\label{ucodeOperation} |
|
There is little more to the core that what has already been said; all the |
CPU operations are microcoded, including interrupt response, reset and |
instruction opcode fetch. The microcode source code can be seen in file |
\texttt{ucode/light8080.m80}, in a format I expect will be less obscure than a |
plain vhdl constant table.\\ |
|
The microcode table is a synchronous ROM with 512 32-bit words, designed |
to fit in a Spartan 3 block ram. Each 32-bit word makes up a microinstruction. |
The microcode 'program counter' (uc\_addr in the VHDL code) thus is a 9-bit |
register.\\ |
Out of those 512 words, 256 (the upper half of the table) are used as a |
jump-table for instruction decoding. Each entry at 256+NN contains a 'JSR' |
$\mu$I to the start of the microcode for the instruction whose opcode is NN. |
This seemingly unefficient use of RAM is in fact an optimization for the |
|
are too large for the microcode so I chose to fill them up with the decoding |
table.\\ |
This scheme is less than efficient where smaller RAM blocks are available (e.g. |
Altera Stratix).\\ |
The jump table is built automatically by the microcode |
assembler, as explained in section ~\ref{ucodeAssembler}.\\ |
The upper half of the table can only be used for decoding; JSR |
instructions can only point to the lower half, and execution from address 0x0ff |
rolls over to 0x00 (or would; the actual microcode does not use this |
'feature').\\ |
|
The ucode address counter uc\_addr has a number of possible sources: the |
micromachine supports one level of micro-subroutine calls; it can also |
return from those calls; the uc\_addr gets loaded with some constant values upon |
reset, interrupt or instruction fetch. And finally, there is the decoding jump |
table mentioned above. So, in summary, these are the possible sources of |
uc\_addr each cycle: |
|
\begin{itemize} |
\item Constant value of 0x0001 at reset (see VHDL source for details). |
\item Constant value of 0x0003 at the beginning (fetch cycle) of every |
instruction. |
\item Constant value of 0x0007 at interrupt acknowledge. |
\item uc\_addr + 1 in normal microinstruction execution |
\item Some 8-bit value included in JSR microinstructions (calls). |
\item The return value preserved in the last JSR (used when flag \#ret is |
raised) |
\end{itemize} |
|
All of this is readily apparent, I hope, by inspecting the VHDL source. |
Note that there is only one jump microinstruction (JSR) which doubles as 'call'; |
whenever a jump is taken the the 1-level-deep 'return stack' is loaded with |
the return address (address of the $\mu$I following the jump). You just have to |
ignore the return address when you don't need it (e.g. the jumps in the decoding |
jump table). I admit this scheme is awkward and inflexible; but it was the first |
I devised, it works and fits the area budget: more than enough in this project. |
A list of all predefined, 'special' microcode addresses follows.\\ |
\begin{itemize} |
|
After reset, the $\mu$I program counter (uc\_addr in the VHDL code) is |
initialized to 0x00. The program counter works as a pre-increment counter when |
reading the microcode rom, so the $\mu$I at address 0 never gets executed (unless |
'rolling over' from address 0x0ff, which the actual microcode does not). Reset |
starts at address 1 and takes 2 microinstructions to clear PC to 0x0000. It does |
nothing else. After clearing the PC the microcode runs into the fetch routine. |
|
The fetch routine places the PC in the address output lines while postincrementing |
it, and then enables a memory read cycle. In doing so it relies on |
T2 being 0x00 (necessary for the ADC to behave like an INC in the oversimplified |
ALU), which is always true by design. After the fetch is done, the \#decode flag |
is raised, which instructs the micromachine to take the value in the DI signal |
(data input from external memory) and load it into the IR and the microcode |
address counter, while setting the high address bit to 1. At the resulting |
address there will be a JSR $\mu$I pointing to the microcode for the 8080 opcode in |
question (the microcode assembler will make sure of that). The \#decode flag will |
also clear registers T1 and T2. |
|
Whenever a HALT instruction is executed, the \#halt flag is raised, which |
when used in the same $\mu$I as flag \#end, makes the the micromachine jump to this |
address. The $\mu$I at this address does nothing but raise flags \#halt and \#end. The |
micromachine will keep jumping to this address until the halt state is left, |
something which can only happen by reset or by interrupt. The \#halt flag, when |
raised, sets the halt output signal, which will be cleared when the CPU exits |
the halt state. |
\end{itemize} |
|
\subsection{Conditional jumps} |
\label{conditionalJumps} |
|
There is a conditional branch microinstruction: TJSR. This instruction |
tests certain condition and, if the condition is true, performs exactly as JSR. |
Otherwise, it ends the microcode execution exactly as if the flag \#end had been |
raised. This microinstruction has been made for the conditional branches and |
returns of the 8080 CPU and is not flexible enough for any other use. |
The condition tested is encoded in the register IR, in the field ccc (bits |
|
them up in any 8080 reference. Flags are updated in the 2nd stage, so a TJSR |
cannot test the flags modified by the previous $\mu$I. But it is not necessary; this |
instruction will always be used to test conditions set by previous 8080 |
instructions, separated at least by the opcode fetch $\mu$Is, and probably many |
more. Thus, the condition flags will always be valid upon testing. |
|
\subsection{Implicit operations} |
\label{implicitOperations} |
|
Most micromachine operations happen only when explicitly commanded. But |
some happen automatically and have to be taken into account when coding the |
microprogram: |
|
\begin{enumerate} |
\item Register IR is loaded automatically when the flag \#decode is raised. The |
microcode program counter is loaded automatically with the same value as |
the IR, as has been explained above. From that point on, execution resumes |
normally: the jump table contains normal JSR microinstructions. |
\item T1 is cleared to 0x00 at reset, when the flag \#decode is active or when |
the flag \#clrt1 is used. |
\item T2 is cleared to 0x00 at reset, when the flag \#decode is active or when |
the flag \#end is used. |
\item Microcode flow control: |
\begin{enumerate} |
\item When flag \#end is raised, execution continues at $\mu$code address |
0x0003. |
\item When both flags \#halt and \#end are raised, execution continues at |
$\mu$code address 0x0007, unless there is an interrupt pending. |
\item Otherwise, when flag \#ret is raised, execution continues in the address |
following the last JSR executed. If such a return is tried before a JSR |
|
should never happen with the microcode source as it is now. |
\item If none of the above flags are used, the next $\mu$I is executed. |
\end{enumerate} |
\end{enumerate} |
|
Notice that both T1 and T2 are cleared at the end of the opcode fetch, so |
they are guaranteed to be 0x00 at the beginning of the instruction microcode. |
And T2 is cleared too at the end of the instruction microcode, so it is |
guaranteed clear for its use in the opcode fetch microcode. T1 can be cleared |
if a microinstruction so requires. Refer to the section on microinstruction |
flags. |
|
|
\section{Microinstructions} |
\label{uinstructions} |
|
The microcode for the CPU is a source text file encoded in a format |
described below. This 'microcode source' is assembled by the microcode assembler |
(described later) which then builds a microcode table in VHDL format. There's |
nothing stopping you from assembling the microcode by hand directly on the VHDL |
source, and in a machine this simple it might have been better. |
|
|
\subsection{Microcode source format} |
\label{ucodeFormat} |
|
The microcode source format is more similar to some early assembly language |
that to other microcodes you may have seen. Each non-blank, |
non-comment line of code contains a single microinstruction in the format |
informally described below: |
|
% there must be some cleaner way to do this in TeX... |
|
\begin{alltt} |
\textless microinstruction line \textgreater := |
[\textless label \textgreater]\footnote{Labels appear alone by themselves in a line} \textbar |
\textless operand stage control \textgreater ; \textless ALU stage control \textgreater [; [\textless flag list \textgreater]] \textbar |
JSR \textless destination address \textgreater\textbar TJSR \textless destination address \textgreater |
\\ |
\textless label \textgreater := \{':' immediately followed by a common identifier\} |
\textless destination address \textgreater := \{an identifier defined as a label anywhere in the file\} |
\textless operand stage control \textgreater := \textless op\_reg \textgreater = \textless op\_src \textgreater \textbar NOP |
\textless op\_reg \textgreater := T1 \textbar T2 |
\textless op\_src \textgreater := \textless register \textgreater \textbar DI \textbar \textless IR register \textgreater |
\textless IR register \textgreater := \{s\}\textbar\{d\}\textbar\{p\}0\textbar\{p\}1\footnote{Registers are specified by IR field} |
\textless register \textgreater := \_a\textbar\_b\textbar\_c\textbar\_d\textbar\_e\textbar\_h\textbar\_l\textbar\_f\textbar\_a\textbar\_ph\textbar\_pl\textbar\_x\textbar\_y\textbar\_z\textbar\_w\textbar\_sh\textbar\_sl |
\textless ALU stage control \textgreater := \textless alu\_dst \textgreater = \textless alu\_op \textgreater \textbar NOP |
\textless alu\_dst \textgreater := \textless register \textgreater \textbar DO |
\textless alu\_op \textgreater := add\textbar adc\textbar sub\textbar sbb\textbar and\textbar orl\textbar not\textbar xrl\textbar rla\textbar rra\textbar rlca\textbar rrca\textbar aaa\textbar |
t1\textbar rst\textbar daa\textbar cpc\textbar sec\textbar psw |
\textless flag list \textgreater := \textless flag \textgreater [, \textless flag \textgreater ...] |
\textless flag \textgreater := \#decode\textbar\#di\textbar\#ei\textbar\#io\textbar\#auxcy\textbar\#clrt1\textbar\#halt\textbar\#end\textbar\#ret\textbar\#rd\textbar\#wr\textbar\#setacy |
\#ld\_al\textbar\#ld\_addr\textbar\#fp\_c\textbar\#fp\_r\textbar\#fp\_rc \footnote{There are some restrictions on the flags that can be used together} \\ |
\end{alltt} |
|
|
Please bear in mind that this is just an informal description; I made |
it up from my personal notes and the assembler source. The ultimate reference is |
the microcode source itself and the assembler source.\\ |
Due to the way that flags have been encoded (there's less than one bit per |
flag in the microinstruction), there are restrictions on what flags can be used |
together. See section ~\ref{ucodeFlags}. |
|
The assembler will complain if the source does not comply with the |
expected format; but syntax check is somewhat weak. |
In the microcode source you will see words like \_\_reset, \_\_fetch, etc. |
which don't fit the above syntax. Those were supposed to be assembler pragmas, |
which the assembler would use to enforce the alignment of the microinstructions |
to certain addresses. I finally decided not to use them and align the |
instructions myself. The assembler ignores them but I kept them as a reminder. |
|
The 1st part of the $\mu$I controls the ALU operand stage; we can load either |
T1 or T2 with either the contents of the input signal DI, or the selected |
register from the register bank. Or, we can do nothing (NOP).\\ |
The 2nd part of the $\mu$I controls the ALU stage; we can instruct the ALU to |
perform some operation on the operands T1 and T2 loaded by this same |
instruction, in the previous stage; and we can select where to load the ALU |
result, eiher in the output register DO or in the register bank. Or we can do |
nothing of the above (NOP). |
|
The write address for the register bank used in the 2nd stage has to be the |
same as the read address used in the 1st stage; that is, if both $\mu$I parts use the |
RB, both have to use the same address (the assembler will enforce this |
restriction). This is due to an early, silly mistake that I chose not to fix: |
there is a single $\mu$I field that holds both addresses.\\ |
This is a very annoying limitation that unduly complicates the microcode |
and wastes many microcode slots for no saving in hardware; I just did not want |
to make any major refactors until the project is working. As |
you can see in the VHDL source, the machine is prepared to use 2 independent |
address fields with little modification. I may do this improvement and others |
in a later version, but only when I deem the design 'finished' (since the design |
as it is already exceeds my modest performance target). |
|
|
\subsection{Microcode ALU operations} |
\label{ucodeAluOps} |
|
\begin{tabular}{|l|l|l|l|} |
\hline |
\multicolumn{4}{|c|}{ALU operations} \\ |
\hline |
Operation & encoding & result & notes \\ |
|
\hline ADD & 001100 & T2 + T1 & \\ |
\hline ADC & 001101 & T2 + T1 + CY & \\ |
\hline SUB & 001110 & T2 - T1 & \\ |
|
\hline AND & 000100 & T1 AND T2 & \\ |
\hline ORL & 000101 & T1 OR T2 & \\ |
\hline NOT & 000110 & NOT T1 & \\ |
\hline XRL & 000111 & T1 XOR T2 & \\ |
\hline RLA & 000000 & 8080 RLC & \\ |
\hline RRA & 000001 & 8080 RRC & \\ |
\hline RLCA & 000010 & 8080 RAL & \\ |
\hline RRCA & 000011 & 8080 RAR & \\ |
\hline T1 & 010111 & T1 & \\ |
\hline RST & 011111 & 8*IR(5..3) & as per RST instruction \\ |
\hline DAA & 101000 & DAA T1 & but only after executing 2 in a row \\ |
\hline CPC & 101100 & UNDEFINED & CY complemented \\ |
\hline SEC & 101101 & UNDEFINED & CY set \\ |
\hline PSW & 110000 & PSW & \\ |
\hline |
|
\end{tabular} |
|
|
|
Notice that ALU operation DAA takes two cycles to complete; it uses a |
dedicated circuit with an extra pipeline stage. So it has to be executed twice |
in a row before taking the result -- refer to microcode source for an example.\\ |
The PSW register is updated with the ALU result at every cycle, whatever |
|
different means, as it is apparent in the case of CY. Which flags are updated, |
and which keep their previous values, is defined by a microinstruction field |
named flag\_pattern. See the VHDL code for details. |
|
|
\subsection{Microcode binary format} |
\label{ucodeBinFormat} |
|
\begin{tabular}{|l|l|l|} |
\hline |
\multicolumn{3}{|c|}{Microcode word bitfields} \\ \hline |
POS & VHDL NAME & PURPOSE \\ \hline |
31..29 & uc\_flags1 & Encoded flag of group 1 (see section on flags) \\ \hline |
28..26 & uc\_flags2 & Encoded flag of group 2 (see section on flags) \\ \hline |
25 & load\_addr & Address register load enable (note 1) \\ \hline |
24 & load\_al & AL load enable (note 1) \\ \hline |
23 & load\_t1 & T1 load enable \\ \hline |
22 & load\_t2 & T2 load enable \\ \hline |
21 & mux\_in & T1/T2 source mux control (0 for DI, 1 for reg bank) \\ \hline |
20..19 & rb\_addr\_sel & Register bank address source control (note 2) \\ \hline |
18..15 & ra\_field & Register bank address (used both for write and read) \\ \hline |
14 & (unused) & Reserved \\ \hline |
13..10 & (unused) & Reserved for write register bank address, unused yet \\ \hline |
11..10 & uc\_jmp\_addr(7..6) & JSR/TJSR jump address, higher 2 bits \\ \hline |
9..8 & flag\_pattern & PSW flag update control (note 3) (pipelined signal) \\ \hline |
7 & load\_do & DO load enable (note 4) (pipelined signal) \\ \hline |
6 & we\_rb & Register bank write enable (pipelined signal) \\ \hline |
5..0 & uc\_jmp\_addr(5..0) & JSR/TJSR jump address, lower 6 bits \\ \hline |
5..0 & (several) & Encoded ALU operation \\ \hline |
\end{tabular} |
|
\begin{itemize} |
\item {\bf Note 1: load\_al}\\ |
AL is a temporary register for the lower byte of the external 16 bit |
address. The memory interface (and the IO interface) assumes external |
synchronous memory, so the 16 bit address has to be externally loaded as |
commanded by load\_addr. |
Note that both halves of the address signal load directly from the |
register bank output; you can load AL with PC, for instance, in the same cycle |
|
|
\item {\bf Note 2 : rb\_addr\_sel}\\ |
A microinstruction can access any register as specified by ra\_field, or |
the register fields in the 8080 instruction opcode: S, D and RP (the |
microinstruction can select which register of the pair). In the microcode source |
this is encoded like this: |
\begin{description} |
\item[\{s\}] $\Rightarrow$ 0 \& SSS |
\item[\{d\}] $\Rightarrow$ 0 \& DDD |
\item[\{p\}0] $\Rightarrow$ 1 \& PP \& 0 (HIGH byte of register pair) |
\item[\{p\}1] $\Rightarrow$ 1 \& PP \& 1 (LOW byte of register pair) |
\end{description} |
\small SSS = IR(5 downto 3) (source register)\\ |
\small DDD = IR(2 downto 0) (destination register)\\ |
\small PP = IR(5 downto 4) (register pair)\\ |
|
\item {\bf Note 3 : flag\_pattern}\\ |
Selects which flags of the PSW, if any, will be updated by the |
microinstruction: |
\begin{itemize} |
\item When flag\_pattern(0)='1', CY is updated in the PSW. |
\item When flag\_pattern(1)='1', all flags other than CY are updated in the PSW. |
\end{itemize} |
|
\item {\bf Note 4 : load\_do}\\ |
DO is the data ouput register that is loaded with the ALU output, so the |
load enable signal is pipelined. |
|
\item {\bf Note 5 : JSR-H and JSR-L}\\ |
These fields overlap existing fields which are unused in JSR/TJSR |
instructions (fields which can be used with no secondary effects). |
|
\end{itemize} |
|
\subsection{Microcode flags} |
\label{ucodeFlags} |
|
|
Flags is what I have called those signals of the microinstruction that you |
assert individually in the microcode source. Due to the way they have been |
encoded, I have separated them in two groups. Only one flag in each group can be |
used in any instruction. These are all the flags in the format thay appear in |
the microcode source: |
|
\begin{itemize} |
\item Flags from group 1: use only one of these |
\begin{itemize} |
\item \#decode : Load address counter and IR with contents of data input |
lines, thus starting opcode decoging. |
\item \#ei : Set interrupt enable register. |
\item \#di : Reset interrupt enable register. |
\item \#io : Activate io signal for 1st cycle. |
\item \#auxcy : Use aux carry instead of regular carry for this $\mu$I. |
\item \#clrt1 : Clear T1 at the end of 1st cycle. |
\item \#halt : Jump to microcode address 0x07 without saving return value, |
when used with flag \#end, and only if there is no interrupt |
pending. Ignored otherwise. |
\end{itemize} |
|
\item Flags from group 2: use only one of these |
\begin{itemize} |
\item \#setacy : Set aux carry at the start of 1st cycle (used for ++). |
\item \#end : Jump to microinstruction address 3 after the present m.i. |
\item \#ret : Jump to address saved by the last JST or TJSR m.i. |
\item \#rd : Activate rd signal for the 2nd cycle. |
\item \#wr : Activate wr signal for the 2nd cycle. |
\end{itemize} |
|
\item Independent flags: no restrictions |
\begin{itemize} |
\item \#ld\_al : Load AL register with register bank output as read by opn. 1 |
(used in memory and io access). |
\item \#ld\_addr : Load address register (H byte = register bank output as read |
by operation 1, L byte = AL). |
Activate vma signal for 1st cycle. |
\end{itemize} |
|
\item PSW update flags: use only one of these |
\begin{itemize} |
\item \#fp\_r : This instruction updates all PSW flags except for C. |
\item \#fp\_c : This instruction updates only the C flag in the PSW. |
\item \#fp\_rc : This instruction updates all the flags in the PSW. |
\end{itemize} |
|
\end{itemize} |
|
\section{Notes on the microcode assembler} |
\label{ucodeAssembler} |
|
The microcode assembler is a Perl script (\texttt{util/uasm.pl}). Please refer |
to the comments in the script for a reference on the usage of the assembler.\\ |
I will admit up front that the microcode source format and the assembler |
program itself are a mess. They were hacked quickly and then often retouched |
but never redesigned, in order to avoid the 'never ending project' syndrome.\\ |
Please note that use of the assembler, and the microcode assembly source, |
is optional and perhaps overkill for this simple core. All you need to build the |
core is the vhdl source file.\\ |
|
The perl assembler itself accounted for more than half of all the bugs I caught |
during development. |
Though the assembler certainly saved me a lot of mistakes in the hand-assembly |
of the microcode, a half-cooked assembler like |
this one may do more harm than good. I expect that the program now behaves |
correctly; I have done a lot of modifications to the microcode source for |
testing purposes and I have not found any more bugs in the assembler. But you |
have been warned: don't trust the assembler too much (in case someone actually |
wants to mess with these things at all).\\ |
The assembler is a Perl program (\texttt{util/uasm.pl}) that will read a |
microcode text source file and write to stdout a microcode table in the form of |
a chunk of VHDL code. You are supposed to capture that output and paste it into |
the VHDL source (Actually, I used another perl script to do that, but I don't |
want to overcomplicate an already messy documentation).\\ |
The assembler can do some other simple operations on the source, for debug |
purposes. The invocation options are documented in the program file.\\ |
You don't need any extra Perl modules or libraries, any distribution of Perl 5 |
|
tested. |
|
\section{CPU details} |
\label{cpuDetails} |
|
\subsection{Synchronous memory and i/o interface} |
\label{syncMem} |
|
The core is designed to connect to external synchronous memory similar to |
the internal fpga ram blocks found in the Spartan series. It can be used with |
asynchronous ram provided that you add the necessary registers (I have used it |
with external SRAM included on a development board with no trouble). |
|
Signal 'vma' is the master read/write enable. It is designed to be used as |
a synchronous rd/wr enable. All other memory/io signals are only valid when vma |
is active. Read data is sampled in the positive clock edge following deassertion |
of vma. Than is, the core expects external memory and io to behave as an |
internal fpga block ram would.\\ |
I think the interface is simple enough to be fully described by the |
comments in the header of the VHDL source file. |
|
\subsection{Interrupt response} |
\label{irqResponse} |
|
Interrupt response has been greatly simplified, but it follows the outline |
of the original procedure. The biggest difference is that inta is |
active for the entire duration of the instruction, and not only the opcode fetch |
cycle. |
|
Whenever a high value is sampled in line intr in any positive clock edge, |
an interrupt pending flag is internally raised. After the current instruction |
finishes execution, the interrupt pending flag is sampled. If active, it is |
cleared, interrupts are disabled and the processor enters an inta cycle. If |
inactive, the processor enters a fetch cycle as usual. |
The inta cycle is identical to a fetch cycle, with the exception that inta |
signal is asserted high. |
|
The processor will fetch an opcode during the first inta cycle and will |
execute it normally, except the PC increment will not happen and inta will be |
high for the duration of the instruction. Note that though pc increment is |
inhibited while inta is high, pc can be explicitly changed (rst, jmp, etc.). |
After the special inta instruction execution is done, the processor |
resumes normal execution, with interrupts disabled.\\ |
The above means that any instruction (even XTHL, which the original 8080 |
forbids) can be used as an interrupt vector and will be executed normally. The |
core has been tested with rst, lxi and inr, for example. |
|
Since there's no M1 signal available, feeding multi-byte instructions as |
interrupt vectors can be a little complicated. It is up to you to deal with this |
situation (i.e. use only single-byte vectors or make up some sort of cycle |
counter). |
|
\subsection{Instruction timing} |
\label{timing} |
|
This core is slower than the original in terms of clocks per instruction. |
Since the original 8080 was itself one of the slowest micros ever, this does not |
say much for the core. Yet, one of these clocked at 50MHz would outperform an |
original 8080 at 25 Mhz, which is fast enough for many control applications --- |
except that there are possibly better alternatives.\\ |
A comparative table follows. |
|
|
\begin{tabular}{|l|l|l|l|l|l|l|} |
\hline |
\multicolumn{7}{|c|}{Instruction timing (core vs. original)} \\ \hline |
|
Opcode & Intel 8080 & Light8080 & & Opcode & Intel 8080 & Light8080 \\ \hline |
|
MOV r1, r2 & 5 & 6 & & XRA M & 7 & 9 \\ \hline |
MOV r, M & 7 & 9 & & XRI data & 7 & 9 \\ \hline |
MOV M, r & 7 & 9 & & ORA r & 4 & 6 \\ \hline |
MVI r, data & 7 & 9 & & ORA M & 7 & 9 \\ \hline |
MVI M, data & 10 & 12 & & ORI data & 7 & 9 \\ \hline |
LXI rp, data16 & 10 & 14 & & CMP r & 4 & 6 \\ \hline |
LDA addr & 13 & 16 & & CMP M & 7 & 9 \\ \hline |
STA addr & 13 & 16 & & CPI data & 7 & 9 \\ \hline |
LHLD addr & 16 & 19 & & RLC & 4 & 5 \\ \hline |
SHLD addr & 16 & 19 & & RRC & 4 & 5 \\ \hline |
LDAX rp & 7 & 9 & & RAL & 4 & 5 \\ \hline |
STAX rp & 7 & 9 & & RAR & 4 & 5 \\ \hline |
XCHG & 4 & 16 & & CMA & 4 & 5 \\ \hline |
ADD r & 4 & 6 & & CMC & 4 & 5 \\ \hline |
ADD M & 7 & 9 & & STC & 4 & 5 \\ \hline |
ADI data & 7 & 9 & & JMP & 10 & 15 \\ \hline |
ADC r & 4 & 6 & & Jcc & 10 & 12/16 \\ \hline |
ADC M & 7 & 9 & & CALL & 17 & 29 \\ \hline |
ACI data & 7 & 9 & & Ccc & 11/17 & 12/30 \\ \hline |
SUB r & 4 & 6 & & RET & 10 & 14 \\ \hline |
SUB M & 7 & 9 & & Rcc & 5/11 & 5/15 \\ \hline |
SUI data & 7 & 9 & & RST n & 11 & 20 \\ \hline |
SBB r & 4 & 6 & & PCHL & 5 & 8 \\ \hline |
SBB M & 7 & 9 & & PUSH rp & 11 & 19 \\ \hline |
SBI data & 7 & 9 & & PUSH PSW & 11 & 19 \\ \hline |
INR r & 5 & 6 & & POP rp & 10 & 14 \\ \hline |
INR M & 10 & 13 & & POP PSW & 10 & 14 \\ \hline |
INX rp & 5 & 6 & & XTHL & 18 & 32 \\ \hline |
DCR r & 5 & 6 & & SPHL & 5 & 8 \\ \hline |
DCR M & 10 & 14 & & EI & 4 & 5 \\ \hline |
DCX rp & 5 & 6 & & DI & 4 & 5 \\ \hline |
DAD rp & 10 & 8 & & IN port & 10 & 14 \\ \hline |
DAA & 4 & 6 & & OUT port & 10 & 14 \\ \hline |
ANA r & 4 & 6 & & HLT & 7 & 5 \\ \hline |
ANA M & 7 & 9 & & NOP & 4 & 5 \\ \hline |
ANI data & 7 & 9 & & & & \\ \hline |
XRA r & 4 & 6 & & & & \\ \hline |
|
\end{tabular} |
|
|
\end{document} |
|