

\documentstyle[11pt,psfig]{article}

\setlength{\oddsidemargin}{0in}
\setlength{\evensidemargin}{0in}
\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0in}
\setlength{\headsep}{0in}
\setlength{\textwidth}{6.5in}
\setlength{\textheight}{9.1in}
\setlength{\parindent}{0in}
\setlength{\parskip}{0.12in}

\begin{document}

\newcommand{\hsp}{\hspace{0.035in}}

\begin{center}
{\LARGE\bf RISC Architectures

N.S. Matloff}
\end{center}

\bigskip

\section{Overview}

The term {\bf RISC} is an acronym for \underline{r}educed 
\underline{i}nstruction \underline{s}et \underline{c}omputer, the
antonym being {\bf CISC}, for \underline{c}omplex 
\underline{i}nstruction \underline{s}et \underline{c}omputer. 

During the 1970s, computers became more and more CISC-like, with
richer and richer instruction sets.  The most cited example is that 
of the VAX family, whose architecture includes 304 instructions, 
including such highly specialized instructions as POLY, which 
evaluates polynomials.  By contrast, the UC Berkeley RISC I 
architecture, which later became the ``rough draft'' for what is 
now the commercial SPARC chip, which is used in the ACS SPARCstations, 
had only 32 instructions.

At the same time, the single-chip CPU was starting out on its path
to dominance of the market.  The more components a computer has, the
more expensive it is to manufacture, so single-chip CPUs became very
attractive.\footnote{And there is a performance gain as well.  Multi-chip
CPUs suffer from the problem that off-chip communication is slower than
within-chip.}  But the problem then became that the very limited space 
on a chip---this space is colloquially called ``real estate''---made
it very difficult, if not impossible, to fit a CISC architecture onto a
single chip.\footnote{It would seem that the solution is to simply make
larger chips.  However, this idea has not yet worked.  For example,
larger chips have lower {\bf yield} rates, i.e. lower rates of flaw-free
chips.  Of course, one can try to make chips denser as well, but there
are limits here too.}

The VAX was a clear casualty of this.  When the Digital Equipment 
Corporation (DEC) tried to produce a ``VAX-on-a-chip,'' they found that
they could not fit the entire instruction set on one chip.  They were
then forced to implement the remaining instructions in software.  For 
example, when the CPU encountered a POLY instruction, that would 
cause an ``illegal op code'' condition, which triggered a {\bf trap} 
(i.e. internal interrupt); the trap-service routine would then be a 
procedure consisting of ordinary ADD and MUL instructions which
compute the polynomial.

At this point, some researchers at IBM\footnote{Thoug Professor Patterson
at UC Berkeley is the one who really popularized the RISC idea, it 
was actually invented at IBM first.  Actually, the old CDC machines 
designed by Seymour Cray had many RISC characteristics, i.e. small 
instruction sets and load/store architecture.} decided to take a 
fresh look at the whole question of instruction set formulation.  The 
main point is optimal use of real estate.  In a CISC CPU chip, a 
large portion of the chip is devoted to the microcode---and yet much 
of that microcode is rarely, if ever used.  For example, most users 
of VAX machines do not make use of the POLY instruction, and yet
the microcode for that instruction would occupy valuable-but-wasted 
real estate in the chips comprising a VAX CPU.  Maybe, for example, an
on-chip cache would make better use of that space; at least the cache would
adapt itself to the characteristics of whatever program is currently running,
quite unlike the the case of the POLY microcode.  

True, for those who do use the POLY instruction, execution speed will 
increase somewhat,\footnote{You should think this.  First, POLY, being a
single instruction, would require only one instruction fetch, compared to
many fetches which would be needed if one wrote a function consisting of
many instructions which would collectively perform the work that POLY does. 
Second, on the microcode level, we might be able to perform more than one
register transfer at once.}  but for most programs the POLY instruction is
just getting in the way of fast performance.

Other factors came into play.  For example, the richer the instruction
set, the longer the decode portion of the instruction cycle would take
(whether for a microprogrammed \underline{or} a hardwired machine).  Even
more important is the issue of pipelining.  Unless we are willing to make
pipeline stage times to be of variable length, we then have to make each
stage have time equal to that of the worst possible case (e.g. the longest
possible decode time in the example above), or add stages to the pipe.
The former of course slows things down a lot whenever we don't have the 
worst-case situations, and the latter has real problems when branches 
occur. 

For this reason, the IBM people asked, ``Why do we need the microcode at
all?  Why not just make the microengine itself our machine?''  They 
didn't literally mean a microengine, with different bit fields 
directly controlling various CPU components, but instead meant a CPU
with a very simple, limited instruction set, eliminating the need for
all that real-estate-consuming microcode, and one in which most
instructions would---like microengine instructions---execute in a 
single clock cycle, so that a smooth pipeline could be built.

Unfortunately, IBM scrapped their RISC project, but later David Patterson
of the Division of Computer Science at UC Berkeley became interested 
in the idea, and developed two RISC CPU chips, RISC I and RISC II.  
These two chips will be our focus here.  

Patterson's team did not invent the RISC concept, nor did they invent
some of the features of their design such as delayed branch, register 
windows, and so on.  Nevertheless, these people singlehandedly changed the 
thinking of the  entire computer industry, by showing that RISC could 
really work well.  The team considered the problem as a whole, 
investigating all aspects, ranging from the questions of chip layout 
and VLSI electronics technology to issues of compiler writing, and
through this integrated approach were able to make a good case for
the RISC concept.  Manolis Katevenis, a Ph.D. student who wrote his 
dissertation on RISC under Professor Patterson, won the annual ACM 
for the best dissertation in Computer Science.\footnote{Published as 
{\it Reduced Instruction Set Computer Architectures for VLSI}, M. 
Katevenis, MIT Press, 1984.}

Today virtually every computer manufacturer has placed a major emphasis
on RISC.  Sun Microsystems has the SPARC chip, DEC uses MIPS and also
their own new Alpha chip, IBM and Motorola have the Power PC chip
series, and so on.  (On the other hand, this does not imply that
RISC is inherently ``superior,'' and as electronic technologies
advance in the future, there may well be some resurgence of CISC
ideas, at least to some degree.)

The Patterson effort is often cited today as an example of the 
contributions which can be made by university Computer Science 
Departments to the computer industry.


\section{A Definition of RISC}

There is no universally-agreed-upon definition of RISC, but most people
would agree at least to the following characteristics:

\begin{itemize}
\item Most instructions execute in a single clock cycle.

\item ``Load/store architecture''---the only instructions which access
memory are of the LOAD and STORE type; e.g. memory-to-register adds like

\begin{verbatim}
ADD R2,X
\end{verbatim}

are not possible.

\item Every instruction has the same length, e.g. 4 bytes.  By contrast,
Intel instructions range in length from 1 to 6 bytes.

\item ``Orthogonal architecture''---any operation can use any operand.
This is in contrast, for example, to the Intel 80x86-family architecture,
in which for example, MUL and DIV require that one of their operands be
in a specific register, AX.

\item The instruction set is limited to only those instructions which
are truly justified in terms of performance/space/pipeline tradeoffs.
The SPARC chip, for instance, does not even have a MUL instruction.
And the ``VAX on a chip'' ended up doing this kind of thing anyway, albeit 
not intentionally.

\item Hardwired implementation, i.e. not microcoded.
\end{itemize}

All but the last of the traits listed above deal with pipelining.

Concerning hardwired implementation, this actually follows from the others.
In the old days, the rationale for microcoded implementation of a CPU was
that it made the CPU development faster, and the CPU debugging faster.
It simplified a very complex job.  But if the CPU is very simple anyway,
that rationale disappears.

\section{Effects on Compiler Writers}

Computer architects used to partly justify including instructions like POLY 
in the instruction set by saying that this made the job of compiler writers
easier.  However, it was discovered that this was not the case, for several
reasons.

\begin{itemize}
\item Compiler writers found it difficult to write compilers which
would automatically recognize situations in (say) C source code in which
instructions like POLY could be used.

\item Compiler writers found that some specialized instructions that were
actually motivated by high-level language constructs did not match the
latter well.  For example, the VAX had a CASE instruction, obviously
motivated by the Pascal {\bf case} and C {\bf switch} constructs; yet
it turned out to be too restrictive to use well for compiling those
constructs.

\item CISC generally led to very nonorthogonal architectures, which made
the job of compiler writers quite difficult.  For instance, the requirement
that the AX register be used in MUL and DIV, \underline{plus} the
requirement that it be used in other instructions, implies that compiler
writers ``don't have any spare registers to work with,'' and thus they
must have the compiler generate code to save and restore ``popular''
registers such as AX; this is a headache for the compiler writer.
\end{itemize}

Most RISC machines do ask that the compiler writers do additional work
of other kinds, though, in that they have to try to fill {\bf delay
slots}; more on this below.

\section{Dealing With the Branch Problem}

As you know, a major problem with pipelined CPUs is that the pipeline
must be drained when a branch is taken.  If for example there are n
stages to the pipe, and the instruction is a conditional branch (so
that we won't know until the last stage whether the branch will be
taken), we will have n-1 empty clock cycles in which no instruction is
executed.

\subsection{Branch Delay Slots}

Many RISC processors use something called a {\bf delayed branch} to
deal with the branch problem.  The term means that the execution of 
the {\bf branch target}, i.e. the place to which we jump on the branch
, is delayed by one cycle.  This is accomplished in an odd but 
actually quite clever way:  Regardless of whether a branch is taken 
or not, we still execute the instruction following the branch.

For example, suppose we have a 2-stage pipe, with Stage 0 being the
instruction fetch and Stage 1 being decode and execute; while one
instruction is executing, the next one is being fetched.  Note that
this implicitly means that we are assuming that it takes one pipe-stage
time to read memory.\footnote{Or possibly to read from a cache.}
Now consider the following generic (not RISC I/II) code:

\begin{verbatim}
ADD R5,R3
ADD R2,R4
JNEG T
n.s.i.
\end{verbatim}

Here n.s.i. stands for ``next sequential instruction,'' i.e. the 
instruction following JNEG in memory.  Using the normal scheme,
if the condition in JNEG is satisfied and we jump to T, we will
have one empty clock cycle in which no instruction is executed,
because we will have to fetch the instruction at T (which had
not been prefetched).

Under the delayed-branch scheme, the n.s.i. will be executed 
regardless of whether we jump to T!  By the time we start execution 
of the n.s.i., we will know whether the condition in the JNEG was 
satisfied, and so during this time we either fetch from T (if it was 
satisfied) or from the location following the n.s.i. (if it was not 
satisfied).  In either case, we will have no empty clock cycles.

A potential problem arises in that we may not want to have the n.s.i.
executed.  Thus the compiler (or assembly-language programmer) would 
first tentatively produce code like this

\begin{verbatim}
ADD R5,R3
ADD R2,R4
JNEG T
NOP
\end{verbatim}

so that the delayed-branch mechanism will not harm correct execution
of the program.  (Recall that most machines have an NOP instruction, 
pronounced ``no-opp,'' which does literally that, i.e. no operation at all.)

This code would work properly, but it would not exploit the parallelism
idea of delayed branches.  Instead, what we would like to do is 
change the order of the instructions:

\begin{verbatim}
ADD R2,R4
JNEG T
ADD R5,R3
\end{verbatim}

Here the ADD to R5 has been moved to follow the JNEG, and this ADD will
be done right after the JNEG.  The change in order won't affect the correct
execution of the code,\footnote{If we had moved the other ADD, there would
have been an effect.}, but the key point is that in the case the 
branch is taken, we save one cycle!  The time that would have been 
wasted fetching the instruction at T is now usefully employed by 
execution of the ADD at R5 at the same time.  And of course if the 
branch is \underline{not} taken, no harm is done.

The instruction position following a branch is called a {\bf delay slot}.
Machines such as MIPS and SPARC have one delay slot, but some other machines
have more than one.  Clearly, the more delay slots there are, the harder
it is to ``fill'' them, i.e. to find instructions to move to put in the
slots, and thus avoid simply putting wasteful NOPs in them.

How hard is it to fill even one delay slot?  The Katevenis dissertation
on RISC says:

\begin{quote}
Measurements have shown [citation given] that the [compiler] optimizer is
able to remove about 90\% of the no-ops following unconditional transfers
and 40\% to 60\% of those following conditional branches.  The unconditional
and conditional transfer-instructions each represent approximately 10\%
of all executed instructions (20\% total).  Thus, while a conventional
pipeline would lose $\approx$ 20\% of the cycles, optimized RISC code
only loses about 6\% of them.
\end{quote}


\section{Register Windows}

Though register windows are not considered to be part of the definition
of RISC, the RISC I/II machines did use them, and a number of the earlier
commercial RISC processors---notably SPARC---followed suit.

The motivation underlying the register window concept is to reduce traffic
from the CPU to memory, since such access is much slower than CPU speeds.
Let us first recall the notion of a stack frame.

\subsection{Stack Frames}

In most modern machine/compiler combinations, function calls are 
handled via the stack.  Not only does a machine CALL instruction save
the return program counter value on the stack, but also the compiler
produces code to push function parameters on the stack, and also to
allocate stack space for local variables in the called function.

For example, suppose we have a C function whose first few lines are

\begin{verbatim}
x(a,b)
   int a,b;

{  int c,d,e;
\end{verbatim}

and that there is a call to the function like this:

\begin{verbatim}
x(m,n);
\end{verbatim}

The compiler will translate the call to something like

\begin{verbatim}
PUSH N
PUSH M
CALL X
\end{verbatim}

and will translate the beginning of the function itself to something 
like

\begin{verbatim}
SUB SP,3
\end{verbatim}

(assuming successive words differ in address by 1, and assuming that the
stack grows toward 0), in order to allocate three words on the stack 
for x's local variables c, d and e.

In other words, for a given function call, the portion of the stack will
be devoted to that call will look like this:

\begin{verbatim}
local variables
return address
parameters
\end{verbatim}

This is called the {\bf stack frame} for that call.

\subsection{Stack Frames on RISC I and II}

RISC I and II store the top few frames of the stack in registers within
the CPU.  When the stack grows to the point at which all these registers
are occupied, a stack overflow occurs, and some of the lower part of the
stack is copied to memory, to make room for the newly-pushed part of the
stack.  Later, when the stack is popped enough to reach the part of 
the stack which was shunted out to memory, a stack underflow occurs, and
that data will be copied back to these registers in the CPU.

Remember, due to top-down software design, typically we will have 
function f1 call function f2, f2 then in turn calling f3, and so on.
This leads to the notion of {\bf parent} and {\bf child} in the
calling chain.  For example, in the chain above, f2 would have f1 as
its parent and f3 as its child.

This structure is then reflected in the register windows.  What is 
normally called a stack frame for other machines is called a {\bf 
register window} here.  Each window consists of 32 registers,
called r31 through r0.  Of these, r15-r10 are used by the function
to receive parameters from the parent, r25-r16 are used to store 
the function's local variables, r31-r26 are used to send parameters
to the child, and r9-r0 are used to store global variables (but r0 
has the value 0 hardwired into it).

The RISC II CPU chip contains a total of 138 registers, which I will 
denote here by R137-R0.  These registers then form 8 windows, 
numbered 0 through 7.  Each window register ri ($0 <= i <= 31$) maps 
to some Rj, in the following way.

\begin{itemize}
\item [1.]
R9-R0 are held in common by all windows, so that a reference to ri,
$0 <= i <= 9$, will map to Ri.  Again, except for r0/R0, which stores
the constant 0, these registers store global variables, accessible to
all functions.

\item [2.]
Registers r31-r26 of a window will be physically identical to 
r15-r10 of the child.  It is this overlap which allows parameter 
passing between functions, as well as passing the return program 
counter value if this function in turns calls a child.

\item [3.]
Registers 25-16 are used to store local variables. 
\end{itemize}

Below are the correspondences between the ``virtual'' register
numbers ri and the ``physical'' register numbers Rj.  Note that
1r11, for instance, means r11 in window 1.

\begin{verbatim}
...                                                 ...       ...
R122                                                6r26      7r10
                                          ...
...                           ...
R47                 1r31      2r15
...                 ...       ...
R43                 1r27      2r11
R42                 1r26      2r10
R41                 1r25
...                 ...
R32                 1r16
R31       0r31      1r15
...       ...       ...
R27       0r27      1r11
R26       0r26      1r10
R25       0r25
...       ...
R16       0r16
R15       0r15
...       ...
R11       0r11
R10       0r10     
R9        0r9       1r9       2r9      ...          6r9       7r9
...       ...       ...       ...                   ...       ...
R1        0r1       1r1       2r1      ...          6r1       7r1
R0        0r0       1r0       2r0      ..1          6r0       7r0
\end{verbatim}

A special Current Window Pointer register points to r16 of the 
current window.

As an example, consider the C code presented above.  Assuming that
m and n were global variables, the compiler might have them stored
in r1 and r2, and might translate the call to the following RISC II 
instructions:\footnote{This is somewhat simplified from the
real RISC II.}

\begin{verbatim}
add r0,r1,r26
add r0,r2,r27
call x,r28
\end{verbatim}

Since r0 contains the constant 0, the add instructions here
simply copy r1 and r2 to r26 and r26, respectively.\footnote{By this
device, there is no need to have a ``move'' instruction in the RISC
I/II architecture, and there is none.}  

The code produced by the compiler for the function x itself would
access the formal parameters m and n via registers r10 and r11;
remember, these registers are physically the same as r26 and r27
of the parent.  The local variables c, d and e would go in r16-r18.
Finally, the last instruction in x would be

\begin{verbatim}
ret r12
\end{verbatim}

(Again, you should verify all of these statements.)

\subsection{Performance}

The Katevenis thesis reports the following findings, in a comparison
of words of memory access for data in two sample programs:

\begin{verbatim}
machine          puzzle          quicksort
-------          ------          ---------
VAX-11/780         440K               700K
RISC                 8K                 4K
\end{verbatim}

So, the use of register windows does appear to achieve a dramatic
reduction in memory traffic, extremely valuable in light of the
slowness of memory.  Anti-register-window advocates argue that a
cache might do as well or better (the VAX did have a cache, but
not as big a cache as it would have today).  Katevenis, Patterson
{\it et al} disagree, but the question would be hard to resolve
completely to everyone's satisfaction.

\section{SPARC Example:  Delayed Branch}

\begin{verbatim}
     1	Script started on Sun Mar 10 16:19:06 1996
     2	taco 1: m a.c
     3	
     4	int a,b;
     5	
     6	main(argc,argv)
     7	   int argc; char **argv;
     8	
     9	{  int x,y;
    10	
    11	
    12	   a = atoi(argv[1]);
    13	   b = atoi(argv[2]);
    14	   if (a < b) x = a;
    15	   else y = a+b;
    16	}
    17	
    18	taco 2: 
    19	taco 2: 
    20	taco 2: 
    21	taco 2: cc -S a.c
    22	taco 3: m a.s
    23	LL0:
    24	        .seg    "data"
    25	        .common _a,0x4,"data"
    26	        .common _b,0x4,"data"
    27	        .seg    "text"
    28	        .proc 04
    29	        .global _main
    30	_main:
    31	        !#PROLOGUE# 0
    32	        sethi   %hi(LF14),%g1
    33	        add     %g1,%lo(LF14),%g1
    34	        save    %sp,%g1,%sp
    35	        !#PROLOGUE# 1
    36	        st      %i0,[%fp+0x44]
    37	        st      %i1,[%fp+0x48]
    38	        ld      [%fp+0x48],%o0
    39	        ld      [%o0+0x4],%o0
    40	        call    _atoi,1
    41	        nop
    42	        sethi   %hi(_a),%o1
    43	        st      %o0,[%o1+%lo(_a)]
    44	        ld      [%fp+0x48],%o2
    45	        ld      [%o2+0x8],%o0
    46	        call    _atoi,1
    47	        nop
    48	        sethi   %hi(_b),%o3
    49	        st      %o0,[%o3+%lo(_b)]
    50	        sethi   %hi(_a),%o4
    51	        ld      [%o4+%lo(_a)],%o4
    52	        sethi   %hi(_b),%o5
    53	        ld      [%o5+%lo(_b)],%o5
    54	        cmp     %o4,%o5
    55	        bge     L17
    56	        nop
    57	        sethi   %hi(_a),%o7
    58	        ld      [%o7+%lo(_a)],%o7
    59	        st      %o7,[%fp+-0x4]
    60	        b       L18
    61	        nop
    62	L17:
    63	        sethi   %hi(_a),%l0
    64	        ld      [%l0+%lo(_a)],%l0
    65	        sethi   %hi(_b),%l1
    66	        ld      [%l1+%lo(_b)],%l1
    67	        add     %l0,%l1,%l2
    68	        st      %l2,[%fp+-0x8]
    69	L18:
    70	LE14:
    71	        ret
    72	        restore
    73	       LF14 = -104
    74	        LP14 = 96
    75	        LST14 = 96
    76	        LT14 = 96
    77	        .seg    "data"
    78	taco 4: 
    79	
    80	
    81	taco 4: 
    82	taco 4: 
    83	taco 4: cc -S -O1 a.c
    84	taco 5: m a.s
    85	        .seg    "text"                  ! [internal]
    86	        .proc   4
    87	        .global _main
    88	_main:
    89	!#PROLOGUE# 0
    90	!#PROLOGUE# 1
    91	        save    %sp,-104,%sp
    92	        st      %i0,[%fp+68]
    93	        st      %i1,[%fp+72]
    94	        call    _atoi,1
    95	        ld      [%i1+4],%o0
    96	        sethi   %hi(_a),%l0             ! [internal]
    97	        st      %o0,[%l0+%lo(_a)]
    98	        ld      [%fp+72],%o0
    99	        call    _atoi,1
   100	        ld      [%o0+8],%o0
   101	        sethi   %hi(_b),%o1             ! [internal]
   102	        st      %o0,[%o1+%lo(_b)]
   103	        ld      [%l0+%lo(_a)],%l0
   104	        ld      [%o1+%lo(_b)],%o1
   105	        cmp     %l0,%o1
   106	        bge,a   LY1
   107	        sethi   %hi(_a),%l0
   108	        sethi   %hi(_a),%o7
   109	        ld      [%o7+%lo(_a)],%o7
   110	        b       LE14
   111	        st      %o7,[%fp-4]
   112	LY1:                                    ! [internal]
   113	        ld      [%l0+%lo(_a)],%l0
   114	        sethi   %hi(_b),%l1
   115	        ld      [%l1+%lo(_b)],%l1
   116	        add     %l0,%l1,%l0
   117	        st      %l0,[%fp-8]
   118	LE14:
   119	        ret
   120	        restore
   121	        .seg    "data"                  ! [internal]
   122	        .common _a,4,"data"
   123	        .common _b,4,"data"
   124	taco 6: 
   125	taco 6: 
   126	taco 6: e
   127	exit
   128	
   129	script done on Sun Mar 10 16:20:11 1996
\end{verbatim}

In lines 3-17, we have a simple C source file a.c, which, in line 21 we
compile, using the -S option.  This option produces a file a.s, which
shows us the assembly language produced in the compilation.  We look
at that file starting at line 22 (in my .cshrc file, I have aliased
`m' to `more').

There are a number of assembler directives which have been inserted by
the compiler, such as those seen in lines 24-29, but they do not concern
us here.  

Recall that the SPARC was modeled on RISC I/II.  In particular, it
uses register windows.  Remember that registers r31-r26 in RISC I/II
were used to send parameters to the child function, i.e. as ``output''
to the called function.  So, in SPARC these are denoted \%o0, \%o1,
and so on.  Similarly, r15-r10 in RISC I/II are for receiving parameters
from the function which called the current one, so the analogs in
SPARC are denoted \%i0, \%i1, etc.  The registers named \%gn are
analogous to RISC I/II's global registers r9-r0.  

The \%fp register is the frame pointer, which is a second copy of the 
stack pointer.  So the expression \%fp+0x48 in line 38 refers to the
location 0x48 bytes into the stack frame, for instance.

The assembly-language notation lists the destination operand last.
For example, in line 39, the destination operand is \%o0.

Let's go right to line 36, which (together with the lines following 
it) is the result of compiling line 12, the call to atoi().
The call occurs at line 40, but first we have to get the parameter
ready.  This occurs in lines 36-39:

Keep in mind that main() is {\it itself} a function, whose parameters 
are argc and argv.  So, argc and argv will be in some \%i registers,
apparently \%i0 and \%i1, which are copied to the stack frame in lines
36 and 37.

Recall (line 7) that argv is a pointer-to-a-pointer.  After line 38
is executed, \%o0 now points to argv[0], \%o0+4 points to argv[1],
\%o0+8 points to argv[2], and so on.  This is reflected in lines
39 and 45.

After line 39 sets up the parameter, line 40 then is the actual call
to the function.  Of particular interest to us here is that the 
compiler has placed a {\bf nop} in line 41, because after all, the 
{\bf call} in line 40 is a form of branch.  Since we did not request
any optimization in our command line (line 21), the compiler has made
no attempt to fill this branch-delay slot.

The function's return value will be in \%o0.  We now have to store
it in the variable `a', which is done in lines 42 and 43:

The {\bf sethi} (``set high'') instruction (line 42) puts a value 
into the high 22 bits of the destination register.   Typically the
value consists of the high bits of an address, and {\bf sethi} is
executed in preparation for a load or store to that address,
in this case the {\bf st} in line 43.

Here are the details of line 43.  The high 22 bits of the address of 
`a' already have been copied (by line 42) to the high 22 bits of
the register \%o1.  By adding the low 10 bits of the address of `a',
we then get the full address of `a'.  Line 43 therefore stores to `a'.

Note, by the way, that the reason we must resort to this piecemeal
way to set up the address of `a' is that under the RISC philosophy
all instructions ought to be the same length, in this case 32 bits.
Since the address of `a' is already 32 bits, we could not fit both
it \underline{and} an op code into one 32-bit instruction!

Now, in line 83, we have recompiled the program, using first-level
optimization (-O1), to force the compiler to attempt to fill the
branch-delay slots.  

To see an example of how the code has changed, note that the {\bf ld} 
which used to be just before the call, in line 39, now appears just 
after the call, in line 95.  (It also now has slightly different form,
because the compiler has done some other optimizations in addition to
trying to fill delay slots.)
  
Now look at lines 106-107.  The {\bf bge} instruction in line 55 has been
changed to {\bf bge,a,} which means the following:  If the branch in 
line 55 is taken, do execute line 56; if the branch is not taken, then
do not execute line 56.  Look at the lines following line 63 and
line 113, and you will see why things are set up this way.


\section{Analysis of the UCB RISC I/II Pipelines}

\subsection{RISC I}

RISC I had a two-stage pipeline, and three internal buses, the first two
of which serve to copy register values into the ALU, with the third
copying the output of the ALU back to a register.  Here are diagrams
of the RISC I timing, both in its pipeline and its within-cycle
performance:

\vspace{0.2in}
\par
\psfig{file=IPipe.eps}
\par

The upper half of the picture gives an overview of the pipeline.  The
first stage is the instruction fetch, and the second is the execution
of the instruction.  Here I1, I2 and I3 are three instructions which
are executed consecutively.\footnote{As opposed to being consecutive
in memory.  E.g. I1 might be a conditional branch, in which case I2
would be consecutive to I1 in memory, but I3 would be consecutive in
memory to neither.}  The fetch of I2 is done during execution of I1,
etc.

The second half of the picture gives the details of the breakdown of one 
cycle into three subcycles, for each of the major components, the three 
buses and the ALU (as well as the outputs of the latter).\footnote{The
term {\bf precharge} here refers to a process necessitated by the 
NMOS electronics technology, and the modified six-transistor register
cell design, used in the RISC I and II chips.  The details may be 
found in Katevenis' thesis and the references it cites, but do not 
worry about this for the purposes of our course.}

Consider, for example, the instruction

\begin{verbatim}
add r2,r12,r13
\end{verbatim}

which puts the sum of registers r2 and r12 into r13.  In RISC I, registers
r2 and r12 would be put onto buses 1 and 2 during subcycle 1, the sum
would be formed during subcycle 2, and the sum would be written back to
bus 3 and register r13 during subcycle 3.

\subsection{RISC II}

RISC II's design is more complicated, but more efficient, using only
two internal buses:

\vspace{0.2in}
\par
\psfig{file=IIPipe.eps}
\par

Here the instruction add r2,r12,r13 would execute in the following way:
As before registers r2 and r12 would be read onto buses 1 and 2 during
subcycle 1.  The sum would be computed in subcycles 2 and 3, and then
the sum would be written back to bus 1 and r13 during subcyle 2 of the
\underline{next} cycle.\footnote{And thus subcycle 2 of the \underline{current}
cycle would do the write-back of the result from the \underline{previous}
cycle.}

This design is clearly more efficient.  The register file is kept busy
all the time (as opposed to being idle 1/3 of the time in the earlier
design), and precious chip real estate is saved:  With this two-bus design,
they were able to put 75\% more registers into a 25\% smaller area.

But what if the following instruction were, say,

\begin{verbatim}
add r4,r13,r1
\end{verbatim}

This wouldn't work.  During subcycle 1 for this instruction, r13 won't
be ready for copying onto bus 2, since the previous instruction, which
changes r13, has not done its write-back yet.  To solve this problem,
the designers included an {\bf internal forwarding} feature, which sends
to result of the previous instruction directly to the ALU, even though
the write-back for that instruction has not been done yet.  Indicator
bits in the ALU will be set or cleared, depending on whether the 
instruction that follows needs to use the internally-forwarded value.

Note again that a three-stage pipe is no slower than a two-stage, as long
as we keep the pipe filled.  Of course, whenever we have a taken branch,
that condition fails, but if the compiler optimizer has been successful
in filling most delay slots, the problem will arise only rarely.

\subsection{Taking Memory Access Into Account}

RISC I/II were designed on the assumption that memory (or cache) 
access time would be approximately the same as the time to do one
ALU computation.  The pipelines described above work that way, with
an instruction fetch being done during an ALU operation.

But what happens when a load or store instruction is encountered?  If 
we only have one path to memory---i.e. we have a cacheless system 
with only one system bus, or we have a combined instruction/data 
cache---then a load/store can't be done at the same time as an 
instruction fetch.  When a load/store instruction is encountered, the 
pipeline must be suspended.  This is how RISC I/II are actually 
implemented.

Another alternative, though, would be to have two paths to memory.
For example, there could be separate instruction and data caches, or
two system buses, both connected to a {\bf dual-port} memory.  In 
such case, a four-stage pipe such as the following would be appropriate:

\vspace{0.2in}
\par 
\psfig{file=DualPipe.eps}
\par

The boxes labeled `comp' indicate a computation, either that as in

\begin{verbatim}
add r4,r13,r1
\end{verbatim}

or possibly in a memory load/store instruction such as

\begin{verbatim}
ldxw r12,30000,r13
\end{verbatim}

The latter instruction is of interest to us now.  It uses indexed 
addressing mode; it loads the contents of the memory word which is
c(r12)\footnote{The notation c( ) means ``contents of.''} bytes past 
location 30000 into r13.  Thus the addition 30000+c(r12) must be
performed, so that even in the case of load/store instructions, the
labels `comp' in the boxes above do make sense.

The labels `mem' mean that \underline{if} a data memory access is needed
for this instruction---i.e. if the instruction type is load or
store---then the memory access will be done at this stage.  Otherwise,
this stage of the pipe becomes a dummy stage.

Note that double internal forwarding is used here, since the
register write-back occurs two time slots after the computation,
rather than one.  (In the picture the internal forwarding from
memI1 to compI3 genuinely comes after a memory access if I1 is
a store instruction.  Otherwise, the forwarding is done from this
dummy stage, and the forwarded value came from compI1.)

In addition, no dependencies are allowed between a load and the 
instruction which follows it.  If for example I1 is the ldxw 
instruction shown above, I2 is not allowed to use r13 as a source 
register, since compI2 occurs at the same time as memI1.  The 
compiler must avoid writing such code, resorting to inserting a nop 
instruction if necessary.

As before, the addition of one more pipe stage (from three to four),
even a stage for which non-load/store instructions do nothing, does
not hurt us at all---until we encounter a branch instruction.  So
again, the efficiency of the delayed-branch mechanism is vital.

\subsection{The Role of the RISC Concept in Achieving a Smooth
Pipeline}

It is of absolutely central importance that you note that the
``RISC-ness'' of the RISC I/II instruction set is of tremendous
help in achieving a smooth pipeline.  

First, all RISC I/II instructions are four bytes in length.  This
compares, for instance, to the CISC architecture in the Intel
80x86 family, in which instruction length can range from one to
six bytes.  An 80x86 pipeline would have to either allow for the
worst case in the instruction-fetch stage, thus possibly making
the common stage time for all states longer than necessary, or
would have to undergo a vast increase in pipeline complexity,
having to allow for many different special cases.  

Similarly, the Intel family also has variable-length op codes, so
the decoding portion of the pipe\footnote{Which in the case of
RISC I/II is so simple that we have incorporated it into the
instruction-fetch stages in the pictures above.} becomes more complex 
too, with the same kinds of negative implications for the pipe.

Second, except for load/store instructions, all RISC I/II instructions
execute in a single clock cycle.  By contrast, Intel instruction times
have a wide range.  For instance, an ADD instruction could execute in
as few as 2 clock cycles, while a DIV could take as many as 25 cycles.
Again, there are profound implications of this on the complexity of
the pipes in the Intel chips.


\section{So, How Well Does RISC Work?}

Some statistics from Katevenis:

\begin{itemize}
\item Due to the simple, orthogonal instruction set, the load/store
architecture, etc., the C compiler for the machine was easy to write, 
taking only 6 person-months, including the optimization components.

\item Though RISC's simpler instruction set means we sometimes need
to use more instructions to accomplish a given task, the RISC I/II 
code was typically no more than 50\% larger than that of the VAX.

\item The ratio of execution times of hand-coded assembly language to
C code was about 90\% for RISC I/II, but around 45\% for the VAX.  In
other words, on a VAX you pay a much higher speed penalty for the
convenience of coding in a high-level language.

\item The {\bf control area} of the RISC II chip consists just of the
opcode decoder, and occupied only 0.5\% of the chip.  By contrast, in
the Motorola 68000,\footnote{The chip family formerly used in 
Macintoshes.} the control area, in this case meaning the microcode, 
occupied 68\% of the chip.

\item The chip layout design for RISC II was done by students in 12
person-months.  For the 68000 the process was done by experienced
professionals, and took 70 person-months.

\item RISC II and a PDP-11/70, having the same clock speed and the
same register-to-register addition time, were compared over 11 programs.
RISC II was on the average 2.1 times faster.
\end{itemize}

\section{What to Do With All That Extra Space?}

Now that RISC has liberated all this real estate, what might we do with
it?

\begin{itemize}
\item cache

\item more registers

\item on-chip stack

\item another CPU!
\end{itemize}

\end{document}

