---
title: Tracing a Program
module: Processor Design
moduleNumber: 4
lessonNumber: 5
order: 405
summary: >
  To close the module, we take a complete Y86-64 program — a loop that sums 1
  through 3 — and run it through SEQ one cycle at a time, recording the PC, the
  fetched instruction, every stage computation, and the registers, condition
  codes, and memory after each cycle. Then we examine single cycles in detail:
  every named signal of an OPq in concrete hex, and a second program whose call
  and ret we trace through the stack. The traces confirm that the assembled
  datapath and control logic behave as a processor.
topics: [Processor Design]
sources:
  - book: Bryant & O'Hallaron
    ref: "CS:APP — §4.3 Sequential Y86-64 Implementations"
  - book: Bistriceanu
    ref: "Computer Architecture Notes — §5 CPU Implementation (Executing an instruction; Hardwired control)"
---

We have a [complete datapath](/computer-architecture/processor-design/assembling-seq)
and we have proven it correct on two instructions in isolation. The last thing to do
is the thing the whole module was for: **run a real program** and watch state evolve.
Correct stages in isolation are not enough: a processor must, started at an address,
drive itself through a program by the loop we built, cycle after cycle,
with no external help. This lesson takes a short Y86-64 program, sets it on SEQ, and
traces every cycle until it halts. If the registers hold the right answer at the end,
the assembled datapath and control logic _are_ a working CPU. They do.

## The program

We compute $1 + 2 + 3 = 6$ with a countdown loop: start a counter at 3, add it to a
running sum, decrement, and repeat until the counter hits zero.

```asm [sum.ys]
        irmovq $3, %rdi      # rdi = n = 3   (loop counter)
        irmovq $0, %rax      # rax = sum = 0
        irmovq $1, %r8       # r8  = 1       (decrement constant)
loop:
        addq   %rdi, %rax    # sum += n
        subq   %r8,  %rdi    # n   -= 1
        jne    loop          # if n != 0, repeat (tests ZF)
        halt
```

The assembler lays this out from address `0x00`. Each `irmovq` is 10 bytes, each
`OPq` is 2, the `jne` is 9, and `halt` is 1, giving the byte addresses below, which
is what the PC steps through.

$$
% caption: The byte layout of sum.ys from address 0. The three irmovq each take 10
% caption: bytes, so the loop body starts at 0x1e; addq and subq are 2 bytes each, jne
% caption: is 9, and halt is 1. The jne destination is the loop label, 0x1e.
\begin{tikzpicture}[font=\footnotesize,
  row/.style={anchor=west}, ad/.style={anchor=east, text=acc, font=\ttfamily}]
  \definecolor{acc}{HTML}{2348F2}
  \foreach \y/\a/\txt in {
    0/{0x00}/{irmovq {\char36}3,\%rdi},
    -0.7/{0x0a}/{irmovq {\char36}0,\%rax},
    -1.4/{0x14}/{irmovq {\char36}1,\%r8},
    -2.1/{0x1e}/{loop: addq \%rdi,\%rax},
    -2.8/{0x20}/{subq \%r8,\%rdi},
    -3.5/{0x22}/{jne 0x1e},
    -4.2/{0x2b}/{halt}} {
    \node[ad] at (0,\y) {\a};
    \node[row, font=\ttfamily] at (0.4,\y) {\txt};
  }
  \draw[acc!50] (-0.95,0.35) -- (-0.95,-4.55);
\end{tikzpicture}
$$

## How to read one cycle

SEQ executes one instruction per clock cycle: in a single tick it fetches, decodes,
executes, touches memory, writes back, and updates the PC, then the new register and
PC values clock in at the rising edge, ready for the next cycle. So one row of the
trace is one cycle is one instruction. For each we record the PC, the instruction
fetched there, the salient stage computations, and the resulting state.

The three `irmovq` cycles are the warm-up. Each fetches at its address, passes the
immediate through the ALU (`valE = 0 + valC`), and writes `valE` to its register; none
touches memory or the condition codes, and `newPC = valP`.

$$
% caption: Cycles 1-3: the three irmovq instructions load the initial registers. Each
% caption: computes valE = 0 + valC and writes it to dstE = rB; PC advances by 10 each
% caption: time (valP). No memory, no condition codes. After cycle 3 the loop begins.
\begin{tikzpicture}[font=\footnotesize,
  h/.style={anchor=west, text=acc, font=\scriptsize\bfseries},
  c/.style={anchor=west}]
  \definecolor{acc}{HTML}{2348F2}
  \draw[acc!40] (-0.3,0.45) -- (13.6,0.45);
  \node[h] at (-0.2,0.85) {cyc}; \node[h] at (0.9,0.85) {PC};
  \node[h] at (2.3,0.85) {instruction}; \node[h] at (6.0,0.85) {Execute / Write-back};
  \node[h] at (10.6,0.85) {state after};
  \foreach \y/\cy/\pc/\ins/\ex/\st in {
    0/1/{0x00}/{irmovq {\char36}3,\%rdi}/{valE$=$3 $\to$ \%rdi}/{\%rdi$=$3},
    -0.75/2/{0x0a}/{irmovq {\char36}0,\%rax}/{valE$=$0 $\to$ \%rax}/{\%rax$=$0},
    -1.5/3/{0x14}/{irmovq {\char36}1,\%r8}/{valE$=$1 $\to$ \%r8}/{\%r8$=$1}} {
    \node[c] at (-0.2,\y) {\cy}; \node[c, font=\ttfamily] at (0.9,\y) {\pc};
    \node[c, font=\ttfamily] at (2.3,\y) {\ins};
    \node[c] at (6.0,\y) {\ex}; \node[c] at (10.6,\y) {\st};
    \draw[acc!25] (-0.3,\y-0.37) -- (13.6,\y-0.37);
  }
\end{tikzpicture}
$$

After cycle 3 the state is $\texttt{\%rdi}=3$, $\texttt{\%rax}=0$,
$\texttt{\%r8}=1$, condition codes unset, and $\texttt{PC}=\texttt{0x1e}$ — the loop
entry. Now the interesting part.

## The loop, cycle by cycle

Each iteration is three cycles: `addq`, `subq`, `jne`. The `addq` updates the sum and
sets the condition codes; the `subq` decrements the counter and sets them again; the
`jne` reads `ZF` to decide whether `newPC` is the loop target `valC = 0x1e` (taken) or
the fall-through `valP = 0x2b` (not taken). Watch `%rax` climb and `%rdi` fall.

$$
% caption: Cycles 4-12: three loop iterations. Each addq adds %rdi into %rax; each
% caption: subq drops %rdi by 1 and sets ZF; each jne takes the branch (newPC=0x1e)
% caption: while ZF=0, and falls through (newPC=0x2b) when the last subq makes %rdi 0,
% caption: setting ZF=1. The sum reaches 6.
\begin{tikzpicture}[font=\footnotesize,
  h/.style={anchor=west, text=acc, font=\scriptsize\bfseries}, c/.style={anchor=west}]
  \definecolor{acc}{HTML}{2348F2}
  \draw[acc!40] (-0.3,0.45) -- (14.6,0.45);
  \node[h] at (-0.2,0.85){cyc}; \node[h] at (0.85,0.85){PC};
  \node[h] at (2.2,0.85){instruction}; \node[h] at (5.4,0.85){computation};
  \node[h] at (9.0,0.85){CC after}; \node[h] at (11.0,0.85){newPC};
  \node[h] at (12.6,0.85){state};
  \foreach \y/\cy/\pc/\ins/\co/\cc/\np/\st in {
    0/4/{0x1e}/{addq \%rdi,\%rax}/{\%rax$=$0$+$3}/{ZF0 SF0}/{0x20}/{\%rax$=$3},
    -0.7/5/{0x20}/{subq \%r8,\%rdi}/{\%rdi$=$3 - 1}/{ZF0 SF0}/{0x22}/{\%rdi$=$2},
    -1.4/6/{0x22}/{jne 0x1e}/{ZF$=$0: taken}/{-}/{0x1e}/{loop again},
    -2.3/7/{0x1e}/{addq \%rdi,\%rax}/{\%rax$=$3$+$2}/{ZF0 SF0}/{0x20}/{\%rax$=$5},
    -3.0/8/{0x20}/{subq \%r8,\%rdi}/{\%rdi$=$2 - 1}/{ZF0 SF0}/{0x22}/{\%rdi$=$1},
    -3.7/9/{0x22}/{jne 0x1e}/{ZF$=$0: taken}/{-}/{0x1e}/{loop again},
    -4.6/10/{0x1e}/{addq \%rdi,\%rax}/{\%rax$=$5$+$1}/{ZF0 SF0}/{0x20}/{\%rax$=$6},
    -5.3/11/{0x20}/{subq \%r8,\%rdi}/{\%rdi$=$1 - 1}/{ZF1 SF0}/{0x22}/{\%rdi$=$0},
    -6.0/12/{0x22}/{jne 0x1e}/{ZF$=$1: fall}/{-}/{0x2b}/{exit loop}} {
    \node[c] at (-0.2,\y){\cy}; \node[c, font=\ttfamily] at (0.85,\y){\pc};
    \node[c, font=\ttfamily\footnotesize] at (2.2,\y){\ins};
    \node[c] at (5.4,\y){\co}; \node[c] at (9.0,\y){\cc};
    \node[c, font=\ttfamily] at (11.0,\y){\np}; \node[c] at (12.6,\y){\st};
    \draw[acc!25] (-0.3,\y-0.35) -- (14.6,\y-0.35);
  }
  % faint band separators between iterations
  \draw[acc!50] (-0.3,-1.78) -- (14.6,-1.78);
  \draw[acc!50] (-0.3,-4.08) -- (14.6,-4.08);
\end{tikzpicture}
$$

Three iterations and the loop is done. The first `addq` adds 3, the second 2, the
third 1, so `%rax` walks $0 \to 3 \to 5 \to 6$. Each `subq` drops `%rdi` by one,
$3 \to 2 \to 1 \to 0$; the moment it reaches 0, that `subq` sets `ZF = 1`, and the
following `jne` reads `ZF = 1`, so its branch condition `Cnd` is false and `newPC`
falls through to `0x2b` instead of looping. Cycle 13 fetches `halt` at `0x2b`, which
sets `Stat = HLT`, and the machine stops with $\texttt{\%rax} = 6$ — the sum
$1 + 2 + 3$, exactly as the program intends.

Laying out the register state across the twelve loop cycles as one table makes the
convergence visible.

$$
% caption: Register and flag state after each loop cycle (cycles 4-12), plus the halt.
% caption: %rax accumulates 3, 5, 6 while %rdi counts down 2, 1, 0; the subq that
% caption: zeroes %rdi sets ZF=1, so the next jne falls through and halt stops with
% caption: %rax = 6. %r8 stays 1 throughout.
\begin{tikzpicture}[font=\footnotesize,
  h/.style={anchor=west, text=acc, font=\scriptsize\bfseries},
  c/.style={anchor=west, font=\ttfamily\scriptsize}]
  \definecolor{acc}{HTML}{2348F2}
  \draw[acc!40] (-0.3,0.45) -- (12.6,0.45);
  \node[h] at (-0.2,0.85){after cyc}; \node[h] at (1.9,0.85){instr};
  \node[h] at (4.3,0.85){\%rax}; \node[h] at (6.0,0.85){\%rdi};
  \node[h] at (7.7,0.85){\%r8}; \node[h] at (9.1,0.85){ZF};
  \node[h] at (10.2,0.85){PC next};
  \foreach \y/\cy/\ins/\ax/\di/\rr/\zf/\pc in {
    0/4/{addq}/{3}/{3}/{1}/{0}/{0x20},
    -0.6/5/{subq}/{3}/{2}/{1}/{0}/{0x22},
    -1.2/6/{jne}/{3}/{2}/{1}/{0}/{0x1e},
    -1.8/7/{addq}/{5}/{2}/{1}/{0}/{0x20},
    -2.4/8/{subq}/{5}/{1}/{1}/{0}/{0x22},
    -3.0/9/{jne}/{5}/{1}/{1}/{0}/{0x1e},
    -3.6/10/{addq}/{6}/{1}/{1}/{0}/{0x20},
    -4.2/11/{subq}/{6}/{0}/{1}/{1}/{0x22},
    -4.8/12/{jne}/{6}/{0}/{1}/{1}/{0x2b},
    -5.4/13/{halt}/{6}/{0}/{1}/{1}/{--}} {
    \node[c] at (-0.2,\y){\cy}; \node[c] at (1.9,\y){\ins};
    \node[c] at (4.3,\y){\ax}; \node[c] at (6.0,\y){\di};
    \node[c] at (7.7,\y){\rr}; \node[c] at (9.1,\y){\zf};
    \node[c] at (10.2,\y){\pc};
    \draw[acc!25] (-0.3,\y-0.3) -- (12.6,\y-0.3);
  }
\end{tikzpicture}
$$

Read down the `%rax` column and the program's whole purpose is visible: 3, 3, 3, 5, 5,
5, 6, 6, 6, 6 — the sum settling as each iteration folds in one more term. Read down
`ZF` and the loop's exit condition is visible: it is 0 until cycle 11's `subq` zeroes
`%rdi`, and the cycle-12 `jne` that reads it is the one that finally falls through.

## Cycle 4 under the microscope

The trace above records outcomes. To connect it back to the
[control logic](/computer-architecture/processor-design/control-logic-and-sequencing),
freeze one cycle and write down **every named signal**: the full contents of the
stage tables and HCL, evaluated with real numbers. Take cycle 4, the first `addq
%rdi, %rax`, fetched at `0x1e` with `%rdi = 3` and `%rax = 0`.

$$
% caption: Every signal of cycle 4, the first addq %rdi,%rax. Fetch splits the two
% caption: bytes 60 70 into icode 6, ifun 0, rA 7, rB 0; the control logic selects
% caption: srcA=rA, srcB=rB, dstE=rB; the ALU adds 0x0 and 0x3; nothing touches
% caption: memory; the E port commits 0x3 to %rax and the PC advances to 0x20.
\begin{tikzpicture}[font=\footnotesize,
  lbl/.style={anchor=east, text=acc, font=\scriptsize}, row/.style={anchor=west}]
  \definecolor{acc}{HTML}{2348F2}
  \draw[acc!40] (-2.6,0.45) -- (11.6,0.45);
  \foreach \y/\stage/\comp in {
    0/{Fetch}/{bytes 60 70: icode$=$6, ifun$=$0, rA$=$7 (\%rdi), rB$=$0 (\%rax); valP$=$0x20},
    -0.85/{Decode}/{srcA$=$7: valA$=$0x3;\ srcB$=$0: valB$=$0x0;\ dstE$=$0;\ dstM$=$F (RNONE)},
    -1.7/{Execute}/{aluA$=$0x3, aluB$=$0x0, alufun$=$ADD: valE$=$0x3;\ set CC: ZF$=$0, SF$=$0, OF$=$0},
    -2.55/{Memory}/{mem read$=$0, mem write$=$0: no access, valM undef\/ined},
    -3.4/{Write-back}/{E port: $R[\mathtt{\%rax}] = \mathtt{0x3}$;\ M port idle},
    -4.25/{PC update}/{not call/jXX/ret: newPC$=$valP$=$0x20}} {
    \node[lbl] at (-0.1,\y) {\stage};
    \node[row] at (0.2,\y) {\comp};
    \draw[acc!40] (-2.6,\y-0.42) -- (11.6,\y-0.42);
  }
\end{tikzpicture}
$$

Every entry is forced. The bytes `60 70` are forced by the assembler; the selects
`srcA = rA`, `srcB = rB`, `dstE = rB` are the `OPq` lines of the HCL; the values
`0x3` and `0x0` are what cycles 1 and 2 left in the registers; `ZF = 0` because
`0x3` is nonzero. Each signal traces back to the matching `case` expression in
lesson 3; this table is that logic, evaluated once.

## A snapshot mid-flight

The same cycle, drawn on the datapath. The figure shows the live values on the key
wires as the instruction climbs the bands.

$$
% caption: SEQ frozen during cycle 4, the addq %rdi,%rax of the first iteration. Fetch
% caption: at PC 0x1e yields icode 6:0; Decode reads valA=3 (%rdi), valB=0 (%rax); the
% caption: ALU adds to valE=3 and sets ZF=0; Write-back stores 3 into %rax; newPC=0x20.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  u/.style={draw, fill=acc!8, align=center, inner sep=3pt, minimum height=9mm},
  w/.style={text=acc, font=\scriptsize}]
  \definecolor{acc}{HTML}{2348F2}
  % vertical stack of the active units
  \node[u, minimum width=22mm] (pc)  at (0,0)     {PC $=$ \texttt{0x1e}};
  \node[u, minimum width=32mm] (im)  at (0,1.6)   {Instruction memory};
  \node[u, minimum width=32mm] (rf)  at (0,3.4)   {Register f\/ile};
  \node[u, minimum width=22mm] (alu) at (0,5.2)   {ALU ($+$)};
  \node[u, minimum width=22mm] (cc)  at (4.4,5.2) {CC};
  \node[u, minimum width=22mm] (wb)  at (0,7.0)   {Write-back};
  \node[u, minimum width=22mm] (npc) at (0,8.6)   {New PC};
  % vertical wires with live values
  \draw[->] (pc.north) -- (im.south) node[w,midway,right]{addr \texttt{0x1e}};
  \draw[->] (im.north) -- (rf.south)
        node[w,midway,right]{icode \texttt{6:0}, rA:rB \texttt{7:0}};
  \draw[->] (rf.north) -- (alu.south)
        node[w,midway,right]{valA$=$3, valB$=$0};
  \draw[->] (alu.east)  -- (cc.west) node[w,midway,above]{result};
  \node[w, anchor=west] at (2.4,5.55) {ZF$=$0};
  \draw[->] (alu.north) -- (wb.south) node[w,midway,right]{valE$=$3 $\to$ \%rax};
  \draw[->] (wb.north) -- (npc.south) node[w,midway,right]{valP};
  \draw[->, acc] (npc.east) -| (6.0,0) -- (pc.east)
        node[w,pos=0.85,below]{newPC $=$ \texttt{0x20}};
\end{tikzpicture}
$$

Every value on that snapshot was derived, not asserted: `valA = 3` because Decode read
`%rdi`, which cycle 1's `irmovq` set; `valE = 3` because the ALU added `valB = 0` and
`valA = 3`; `ZF = 0` because the result is nonzero; `newPC = 0x20 = 0x1e + 2` because
`addq` is two bytes and is not a branch. The control logic of
[lesson 3](/computer-architecture/processor-design/control-logic-and-sequencing) chose
every mux input; the stage tables of
[lesson 2](/computer-architecture/processor-design/the-seq-stages) defined every
computation; the [datapath](/computer-architecture/processor-design/assembling-seq)
carried the bits. Nothing outside the machine intervened.

## A second program: call and ret through the stack

`sum.ys` never touches memory. To watch the stack machinery — the `%rsp` bookkeeping,
the memory write of a return address, the `valM`-into-PC wire — run a second program,
five instructions long.

```asm [callret.ys]
        irmovq $0x100, %rsp  # 0x000: set up the stack
        call sum3            # 0x00a: push 0x013, jump to sum3
        halt                 # 0x013: after the return
sum3:
        irmovq $6, %rax      # 0x014: the function body
        ret                  # 0x01e: pop 0x013 into the PC
```

The layout: `irmovq` (10 bytes) at `0x000`, `call` (9 bytes) at `0x00a`, `halt` at
`0x013`, then `sum3` at `0x014` with its `irmovq` and the `ret` at `0x01e`. Five
cycles run: the two `irmovq`s bracket the `call` (cycles 1–3), then `ret` (cycle 4)
and `halt` (cycle 5). The two cycles worth tracing in full are the `call` and the
`ret`.

$$
% caption: Cycle 2 (call sum3, left) and cycle 4 (ret, right), every stage in hex.
% caption: call computes valE = 0x100 - 8 = 0xf8, stores the return address 0x013
% caption: there, commits %rsp = 0xf8, and jumps to valC = 0x014. ret reads valA =
% caption: 0xf8, loads valM = 0x013 from it, commits %rsp = 0x100, and jumps to valM.
\begin{tikzpicture}[font=\footnotesize,
  lbl/.style={anchor=east, text=acc, font=\scriptsize}, row/.style={anchor=west}]
  \definecolor{acc}{HTML}{2348F2}
  \node[anchor=west, text=acc] at (-2.7,0.95) {\texttt{call sum3}\ \ (PC $=$ \texttt{0x00a})};
  \draw[acc!40] (-2.7,0.45) -- (5.6,0.45);
  \foreach \y/\stage/\comp in {
    0/{Fetch}/{icode$=$8; valC$=$0x014; valP$=$0x013},
    -0.85/{Decode}/{srcB$=$\%rsp: valB$=$0x100},
    -1.7/{Execute}/{valE$=$0x100 - 8$=$0xf8},
    -2.55/{Memory}/{$M_8[\mathtt{0xf8}] = \mathtt{0x013}$ (write valP)},
    -3.4/{Write-back}/{$R[\mathtt{\%rsp}] = \mathtt{0xf8}$},
    -4.25/{PC update}/{newPC$=$valC$=$0x014}} {
    \node[lbl] at (-0.2,\y) {\stage};
    \node[row] at (0.1,\y) {\comp};
    \draw[acc!40] (-2.7,\y-0.42) -- (5.6,\y-0.42);
  }
  \node[anchor=west, text=acc] at (7.1,0.95) {\texttt{ret}\ \ (PC $=$ \texttt{0x01e})};
  \draw[acc!40] (7.1,0.45) -- (15.4,0.45);
  \foreach \y/\stage/\comp in {
    0/{Fetch}/{icode$=$9; valP$=$0x01f (discarded)},
    -0.85/{Decode}/{valA$=$valB$=R[\mathtt{\%rsp}]=$0xf8},
    -1.7/{Execute}/{valE$=$0xf8$+$8$=$0x100},
    -2.55/{Memory}/{valM$=M_8[\mathtt{0xf8}]=$ 0x013 (read at valA)},
    -3.4/{Write-back}/{$R[\mathtt{\%rsp}] = \mathtt{0x100}$},
    -4.25/{PC update}/{newPC$=$valM$=$0x013}} {
    \node[lbl] at (7.2,\y) {\stage};
    \node[row] at (7.5,\y) {\comp};
    \draw[acc!40] (7.1,\y-0.42) -- (15.4,\y-0.42);
  }
\end{tikzpicture}
$$

The two tables are mirror images through the stack. `call` writes `0x013`, an
address _it computed as `valP`_ (the byte after its own 9-byte body), into memory at
the decremented `%rsp`, and that number sits inert at `0xf8` for two cycles while
`sum3` runs. Then `ret`, knowing nothing about who called, reads whatever the top of
the stack holds, and the machine lands back at `halt`. The trace confirms what
the [procedures lesson](/computer-architecture/machine-level-x86-64/procedures)
claimed: a return address is just a word in memory, trusted absolutely.

Check the arithmetic that has to line up for this to work, because a single wrong
constant would break it. The `call` sits at `0x00a` and is 9 bytes long, so its `valP`
is $\texttt{0x00a} + 9 = \texttt{0x013}$ — the address of the `halt`, the point
where control should resume. Execute decrements the stack pointer by 8, so
$\texttt{0x100} - 8 = \texttt{0xf8}$, and Memory stores `0x013` there. When `ret` runs,
its Execute adds 8 back ($\texttt{0xf8} + 8 = \texttt{0x100}$, restoring `%rsp`), but
its _address_ is `valA = 0xf8`, the pre-increment top, so it reads the word `0x013`
that `call` left — not the word above it. The off-by-eight bug flagged in
[lesson 3's memory control](/computer-architecture/processor-design/control-logic-and-sequencing)
amounts to using `valE = 0x100` as the read address instead of
`valA = 0xf8`; the trace shows the correct choice landing back on `0x013` and the
`halt`, and any other choice would return into garbage.

$$
% caption: The stack during callret.ys. call decrements %rsp from 0x100 to 0xf8 and
% caption: stores the return address 0x013 at the new top; ret reads it back and
% caption: restores %rsp to 0x100. The word at 0xf8 still holds 0x013 afterward —
% caption: ret reads, it does not erase.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  cell/.style={draw, minimum width=30mm, minimum height=8mm, inner sep=1pt},
  ad/.style={anchor=east, font=\ttfamily\footnotesize}]
  \definecolor{acc}{HTML}{2348F2}
  \node[cell] (c100) at (0,1.6) {};
  \node[cell, fill=acc!8] (cf8) at (0,0.8) {\texttt{0x013}};
  \node[cell] (cf0) at (0,0) {};
  \node[ad] at (-1.7,1.6) {0x100};
  \node[ad] at (-1.7,0.8) {0x0f8};
  \node[ad] at (-1.7,0)   {0x0f0};
  \node[anchor=south, font=\scriptsize] at (0,2.1) {higher addresses};
  \draw[->, acc] (3.4,1.6) -- (c100.east)
        node[pos=0,anchor=west,font=\scriptsize,text=acc]{\%rsp before call, after ret};
  \draw[->, acc] (3.4,0.8) -- (cf8.east)
        node[pos=0,anchor=west,font=\scriptsize,text=acc]{\%rsp after call (return address here)};
\end{tikzpicture}
$$

## Counting cycles and the CPI that motivates pipelining

The trace also lets us _measure_ SEQ, and the measurement motivates the next module. `sum.ys` executed **13 instructions in 13 cycles**: three `irmovq`, three
loop iterations of three instructions each, and one `halt`. SEQ runs exactly one
instruction per cycle, so its **cycles per instruction (CPI) is exactly 1** — you
cannot do better than one instruction every clock tick if the whole machine is one big
combinational settle per instruction.

CPI is only half of what determines speed. The usual accounting (Bryant & O'Hallaron,
_CS:APP_ §4.3 and §5.7) writes a program's running time as

$$
\text{time} = \text{instructions} \times \text{CPI} \times \text{clock period},
$$

and SEQ wins the middle factor (CPI = 1) only by paying dearly on the third. The clock
period had to cover the _entire_ critical path — instruction fetch, register read, ALU,
data memory, and the New-PC mux in series — so on the illustrative delays of
[the previous lesson](/computer-architecture/processor-design/assembling-seq) that
period was near 390 ps. Every one of `sum.ys`'s 13 cycles was that long, even the
`addq` cycles that needed no memory at all.

Pipelining attacks the third factor without giving up much of the first. If the six
stages each get their own clock band and several instructions occupy different stages
at once, the clock period drops toward the delay of the _single slowest stage_ (roughly
the 120 ps of a memory access) rather than the sum of all of them. The ideal is CPI
still near 1 but a clock several times faster — which, on a straight-line program like
`sum.ys`, is a several-fold speedup for free. The catch is the loop: `sum.ys`'s `jne`
does not know its target until it has been executed, and a pipeline that has already
begun fetching past the branch may have guessed wrong. Resolving those hazards — data
dependencies between overlapping instructions, and control dependencies at branches —
is the entire subject of the [pipelining
module](/computer-architecture/pipelining/from-seq-to-pipe), and this trace is the
baseline it improves on: correct, simple, and one instruction slow at a time.

## What we have built

The traces close the loop the module opened. Twelve cycles of pure combinational
choices, clocked one instruction at a time, took three registers from an initial state
to the correct answer and stopped at the right place; five more cycles pushed a return
address, ran a function, and came back through it, and at no point did anything
direct the machine but its own PC and the bytes it pointed at. That is the entire
claim of the stored-program computer, realized in logic we specified gate by
gate.

> **Takeaway.** Run cycle by cycle, SEQ executes `sum.ys` exactly: three `irmovq`
> initialize the registers, then three loop iterations of `addq`/`subq`/`jne` carry
> `%rax` $0 \to 3 \to 5 \to 6$ and `%rdi` $3 \to 0$, the last `subq` sets `ZF = 1` so
> the final `jne` falls through, and `halt` stops the machine with the sum 6 in
> `%rax`. Frozen mid-cycle, every named signal — `icode 6:0`, `srcA = 7`,
> `valE = 0x3`, `newPC = 0x20` — is the control logic evaluated with real bytes; and
> in `callret.ys`, `call` and `ret` meet through one word of memory at `0xf8`. The
> machine's behavior follows entirely from the wiring — which means the assembled
> datapath and control logic _are_ a functioning processor.

The course has now gone from bits and bytes, through an instruction
set, through gates and memory, to a circuit that runs programs. From here the
questions become _how fast_ — the same six stages, overlapped in a
[pipeline](/computer-architecture/pipelining/from-seq-to-pipe) so several
instructions are in flight at once — and _how to keep the data close_, with the
memory hierarchy and caches.
