
The processor and memory and linked by several sets of wires, the Address Bus, the Data Bus and two control signals, enable and read/write. The Address Bus and Data Bus are both 16 bits wide, while the control signals are individual wires. To retrieve a stored value from the memory, the processor supplies an address on the Address Bus and raises the enable and read/write signals high. The memory responds to this by placing the value stored in the word identified by the given address on the Data Bus. To write a value to a memory location, the processor provides both an address and a data value, then raises the enable high and drops the read/write signal low. The memory responds to this by storing the value on the data bus into the specified location.
The processor contains several 16 bit internal storage elements called registers. The Instruction Register (IREG) is used to store the instruction that is currently being executed by the processor; the Program Counter (PC) stores the address of the next instruction to be executed by the processor; and the Accumulator (ACC) stores data values being manipulated by the program.
The processor executes programs by repeatedly retrieving instructions from memory and carrying out the operations specified by those instructions. This is referred to as the fetch and execute cycle. The fetch portion of the fetch and execute cycle involves retrieving an instruction from memory. The PC is used to supply the address for the required memory read operation, and the value returned by the memory is stored in the IREG. At the very end of the fetch, the PC is incremented, so that it now points to the next memory location. Once an instruction has been retrieved from memory, it must be executed. We'll discuss the various instructions shortly, but for now just note that some instructions involve retrieving values from memory or storing values to memory, while others involve performing some arithmetic or logical operation on the value stored in the ACC.
There are several other components of the processor. The Arithmetic and Logic Unit contains the circuitry that implements the various arithmetic and logical operations supported by the processor. The Indirect Address Register (IAR) is used to temporarily store addresses required by indirect load and store instructions (details below). The Controller coordinates all the steps steps involved in fetching and executing instructions.
Figure 2 shows a circuit board made by Diligent that can be used to implement our simple processor. The board includes a Field Programmable Gate Array (FPGA), which is an integrated circuit that can be re-configured to implement a large number of different digital systems. In particular, we will use it to implement our simple processor. We'll refer to the board as the S3 board (the S3 is short for Spartan 3, which is the name for the family of FPGA parts that the particular FPGA on the board comes from).

In addition to the FPGA, the S3 board has four push buttons, eight slide switches, eight LEDs and a four digit display. These can be used to interact with the processor (or any other circuit that might be configured on the S3 board). The buttons are referred to as btn(3) .. btn(0), with btn(3) on the left. Similarly, the switches are referred to as swt(7) .. swt(0).
The simple processor uses the buttons, switches, LEDs and the display on the S3 board to provide a rudimentary input/output capability. Before we get into the details of how the buttons and switches are used, we need to describe the IO Unit, which is shown in Figure 3.

The IO Unit contains several registers, the Input Register (InReg), the Input Control Register (InCtl), the LED Register (LEDReg) and the Output Register (OutReg). Programs running on the processor interact with the IO unit using memory-mapped IO. What this means is that the IO unit responds to memory read/write requests for certain special memory addresses. Operations on these addresses actually affect the registers in the IO unit, rather than locations in the memory unit (the memory unit ignores operations using these addresses). So for example, if the processor executes a direct store instruction to location 0FFE, the value is stored in the LEDReg in the IO unit. The low order eight bits of this register directly drive the LEDs on the S3 board, so when this happens, we can see what value has been written by looking at the on-off state of the LEDs. Similarly, when the processor writes to location 0FFF, it actually writes the OutReg in the IO unit, which drives the four digit display. This allows a running program to display any 16 bit value. Users can use one of the push buttons (called the Load Button) and the switches to load values into InReg and set bits in InCntl. Running programs can read from location 0FFC to see that a new value has been loaded into InReg and can then read from location 0FFD to retrieve that value.
With this background, we can now describe how the four buttons are used with the simple processor.
sum = 0;
while (inputValue != 0) {
sum = sum + inputValue;
show value of sum on display
}
Note that the program terminates when the user inputs a value of 0.
Now let's see how to implement this using the simple processor's
instructions. Here's the code for the initialization step.
0000 1000 (ACC = 0) 0001 4020 (M[0020] = ACC)The hex value at the start of each line is the memory address where the given instruction is stored. The next hex value on each line is the actual instruction. The comments in parentheses are just a reminder of what each instruction does. So, the first instruction (an immediate load) loads 0 into the ACC, while the second instruction (a direct store) stores this value into M[0020]. Location 0020 is where we are storing the sum.
The next group of instructions, waits until a new input value is available, by repeatedly checking bit 0 of InCtl (which is associated with location 0FFC).
0002 (loop) 1001 (ACC = 1) 0003 DFFC (ACC = ACC and M[0FFC]) 0004 7002 (if ACC=0 goto loop)The (loop) label is just a comment to highlight the fact that this instruction is the first in a loop. Note that after the and instruction at location 0003 executes, the ACC contains the value of bit 0 of InCtl. The next pair of instructions reads the input value from InReg and checks to see if it's zero.
0005 2FFD (ACC = M[0FFD]) 0006 700D (if ACC=0 goto end)The next three update the sum and display it by storing it in OutReg.
0007 A020 (ACC = ACC + M[0020]) 0008 4020 (M[0020] = ACC) 0009 4FFF (M(0FFF] = ACC)The next three clear InCtl, so we can detect the next input value and then branches to the top of the loop.
000A 1000 (ACC = 0) 000B 4FFC (M[0FFC] = ACC) 000C 6002 (goto loop)Putting it all together gives us
Address Instruction Comment 0000 1000 (ACC = 0) sum = 0 0001 4020 (M[0020] = ACC) 0002 (loop) 1001 (ACC = 1) wait for new input value 0003 DFFC (ACC = ACC and M[0FFC]) 0004 7002 (if ACC=0 goto loop) 0005 2FFD (ACC = M[0FFD]) if inputValue=0, halt 0006 700D (if ACC=0 goto end) 0007 A020 (ACC = ACC + M[0020]) sum = sum + input value 0008 4020 (M[0020] = ACC) 0009 4FFF (M[0FFF] = ACC) show value on display 000a 1000 (ACC = 0) clear input control register 000b 4FFC (M[0FFC] = ACC) 000c 6002 (goto loop) branch to top 000d (end) 0000 (halt) the endHere, the comments at the right are higher level comments, indicating what each group of instructions is doing. Notice that the program never checks to see if the user loads a value into the high order bits of InReg. The user may do this, but the program ignores it. But if the user follows up by loading a value in the low order bits as well, the program will then continue to read InReg, getting both the high order bits and the low order bits.
When we run the program on the S3 board, we can put it into single step mode and observe what happens as the program executes. Using the display mode button, we can see what values are in each of the processor's registers. So for example, when the processor pauses at instruction 5, we will observe the values shown below, as we cycle through the different display modes.

Here we are seeing the value of OutReg at left, followed by the values of IREG, PC, ACC and IAR. Another way we can observe the operation of a running program is using simulation. The next figure shows a screen shot of the ModelSim simulator, as it simulates the processor executing the first two instructions of our simple program.

Notice the line near the center of the simulation output labeled "testit/uut/cpuc/state". This shows the "state" of the processor, as it is executing. We start out in the reset state, with all of the processor registers set to zero. When we come out of the reset state, the processor fetches its first instruction from memory location 0000 (because the value of the PC is zero). Notice that during the fetch, the processor raises the memory enable signal (mem_en) and that the memory responds by returning the value 1000 on the data bus (dbus). We can also see this value get loaded into the IREG at the start of tick 2 of the fetch. Here, "tick" refers to ticks of the "clock", where the clock in this case is just a signal that repeatedly cycles between 0 and 1. The clock provides the basic timing reference that is used to coordinate when various things happen as the processor executes. Note for example, that mem_en goes high at the falling clock transition of tick 0 of the fetch, and it goes low again at the falling transition of tick 2. Also note how the PC is incremented at the very end of tick 2 of the fetch.
Since the retrieved instruction 1000 is an immediate load, it causes the value 0000 to be placed in the ACC. In this particular case, there is no effect, since the ACC was already 0. The next fetch proceeds in the same way as the first, but in this case the returned instruction is the direct store, 4020. During the execution of the direct store, the processor places 0020 on the address bus and 0000 on the data bus. It drops the read/write signal causing the memory to perform the write operation.
Let's start by taking another look at the block diagram for the basic processor and memory.

The processor interacts with the memory using the Address Bus, the Data bus and the two control signals enable and read/write. The processor executes programs by repeatedly retrieving instructions from memory and executing the instruction. We can summarize this activity using the conceptual state diagram shown below.

Each of the states in this diagram actually corresponds to a sequence of more specific steps. Let's start by looking at the instruction fetch. To implement the fetch, the processor must place the value of the PC on the address bus, assert the memory control signals appropriately and then after allowing time for the memory to operate, store the value returned on the address bus into the IREG. After all this is done, it must increment the PC.
The timing diagram below shows one way that the various activities required to implement the fetch can be organized in time.

To enable the processor to perform the various actions at the proper times, we define a auxiliary register called tick which is initialized to 0 at the start of every fetch and is then incremented by one on each subsequent rising clock edge. Tick is also reset to 0 at the end of the fetch and incremented during each clock cycle of the instruction execution. In the middle of the first clock period (tick=1), the memory enable, read/write signals are asserted and the value of the PC is placed on the address bus. After the memory has time to operate, the value of the selected memory word is returned on the data bus. This value is stored in the IREG at the end of the second clock period (tick=1). Finally, the PC is incremented at the end of the third clock period (tick=2). Note that some of the actions occur on falling clock transitions, meaning that these signals must be controlled by negative edge-triggered flip flops.
How do we specify this behavior in VHDL? We will need two processes; the first defines all the signals that change on rising clock transitions, and the second defines the signals that change on falling clock transitions. The relevant code from the first process is shown below, with the code directly related to the fetch highlighted in bold.
process(clk) begin
if rising_edge(clk) then
if reset = '1' then
. . .
else
tick <= tick + 1; -- advance time by default
case state is
. . .
when fetch =>
if tick = x"1" then iReg <= dBus; end if;
if tick = x"2" then
decode; pc <= pc + 1; tick <= x"0";
end if;
. . .
end case;
end if;
end if;
end process;
The decode procedure assigns a new value to the state,
depending, base on the instruction stored in the iReg.
We have omitted the code associated with other processor
states. Some of these will be discussed below.
The relevant code for the second process is shown below.
process(clk) begin
if falling_edge(clk) then
if reset = '1' then
. . .
else
case state is
. . .
when fetch =>
if tick = x"0" then
m_en <= '1'; aBus <= pc;
end if;
if tick = x"2" then
m_en <= '0'; aBus <= (aBus'range => 'Z');
end if;
. . .
end case;
end if;
end if;
end process;
We can use similar timing diagrams to define the timing of the steps required to implement the execution phase of the various instructions. The figure below shows the timing of the steps used in the direct load, add and and instructions.

In all three of these instructions, we need to do a memory read, using the contents of the IREG as the address. We then load the ACC, either from the DBUS or from the ALU (in the case of the add and and instructions. The VHDL code that implements these instructions appears below.
process(clk) begin
if rising_edge(clk) then
if reset = '1' then
. . .
else
tick <= tick + 1; -- advance time by default
. . .
case state is
. . .
when dload =>
if tick = x"1" then acc <= dBus; end if;
if tick = x"2" then wrapup; end if;
when add | andd =>
if tick = x"1" then acc <= alu; end if;
if tick = x"2" then wrapup; end if;
. . .
end case;
end if;
end if;
end process;
. . .
process(clk) begin
if falling_edge(clk) then
if reset = '1' then
. . .
else
case state is
. . .
when dload | add | andd =>
if tick = x"0" then
m_en <= '1'; aBus <= x"0" & iReg(11 downto 0);
end if;
if tick = x"2" then
m_en <= '0'; aBus <= (aBus'range => 'Z');
end if;
. . .
end case;
end if;
end if;
end process;
The wrapup procedure is invoked at the end of each
instruction and simply sets the state back to fetch and
tick back to 0.
The next figure shows the timing for the two store
instructions.

In the direct store, the read/write signal goes low only after the address has been placed on the address bus and goes high again before the value on the address bus is allowed to change. This is necessary to ensure that we don't accidentally change the value in some other memory location than the one that we're interested in. The indirect store starts with a memory read, which follows the same pattern as the direct load instruction. The result of this read is stored in the IAR and used as the address in the second phase of the instruction, where the value in the ACC is stored to memory. The VHDL code that implements these instructions appears below.
process(clk) begin
if rising_edge(clk) then
if reset = '1' then
. . .
else
tick <= tick + 1; -- advance time by default
case state is
. . .
when dstore =>
if tick = x"4" then wrapup; end if;
when istore =>
if tick = x"1" then iar <= dBus; end if;
if tick = x"7" then wrapup; end if;
. . .
end case;
end if;
end if;
end process;
. . .
process(clk) begin
if falling_edge(clk) then
if reset = '1' then
. . .
else
case state is
. . .
when dstore =>
if tick = x"0" then
m_en <= '1'; aBus <= x"0" & iReg(11 downto 0);
end if;
if tick = x"1" then m_rw <= '0'; dBus <= acc; end if;
if tick = x"3" then m_rw <= '1'; end if;
if tick = x"4" then
m_en <= '0'; aBus <= (abus'range => 'Z');
dBus <= (dBus'range => 'Z');
end if;
when istore =>
if tick = x"0" then
m_en <= '1'; aBus <= x"0" & iReg(11 downto 0);
end if;
if tick = x"2" then
m_en <= '0'; aBus <= (aBus'range => '0');
end if;
if tick = x"3" then m_en <= '1'; aBus <= iar; end if;
if tick = x"4" then m_rw <= '0'; dBus <= acc; end if;
if tick = x"6" then m_rw <= '1'; end if;
if tick = x"7" then
m_en <= '0'; aBus <= (abus'range => 'Z');
dBus <= (dBus'range => 'Z');
end if;
. . .
end case;
end if;
end if;
end process;
The figure below shows the same portion of the processor simulation that appeared earlier, but highlights some additional details. You should review how each of the highlighted signal changes are specified in the timing diagrams and the VHDL code that implements those changes, to confirm your understanding of how the processor works.

The complete VHDL for the processor appears here. The cpu refers to some globally defined constants and declarations that you will find here. As you review this, you will notice that there are some additional details that we have glossed over in the intial presentation. Review the code carefully to make sure you understand these details. If there is anything you don't understand, ask for additional information.

The debouncer, at the bottom of the diagram, is a simple component that de-bounces the buttons on the S3 board to produce internal signals that have clean transitions. Mechanical buttons, like those on the S3 board vibrate as they are being pressed, and these vibrations can cause multiple connections and disconnections that can be observed by the circuitry implementing the processor. The debouncer filters out these unwanted signal transitions by propagating changes on the input buttons only after the button signals have been stable for about 20 ms.
The memory module at the right of the figure implements the memory used to store programs and data. Due to limitations of the S3 board, the amount of memory configured is 8K words (rather than the 64K that can be addressed by the processor). The loader module initializes the memory contents with a program that is executed when the processor starts up. Note that since both the loader and the processor can access the memory, we need to combine their memory control signals. In particular, the enable signal to the memory is the logical-or of the enable signals form the loader and the processor, while the read/write signal is the logical-and. This works because the loader and processor never attempt to access the memory at the same time (details on this appear below).
The single step controller allows a user to place the processor in single step mode, making it possible to observe the effects of individual instructions. When in single step mode, the processor pauses at the end of each instruction fetch.
The IO unit provides mechanisms to input data to a running program and for a program to display information on the seven segment display or the LEDs.
entity ram is port (
clk: in std_logic
en, rw: in std_logic;
aBus: in address;
dBus: inout word);
end ram;
architecture ramArch of ram is
component ram16k port (
addr: IN std_logic_VECTOR(12 downto 0);
clk: IN std_logic;
din: IN std_logic_VECTOR(15 downto 0);
dout: OUT std_logic_VECTOR(15 downto 0);
en: IN std_logic;
we: IN std_logic);
end component;
signal addr: std_logic_vector(12 downto 0);
signal ramOut: std_logic_vector(15 downto 0);
signal we: std_logic;
begin
addr <= aBus(12 downto 0); we <= not rw;
ram: ram16k port map (addr, clk, dBus, ramOut, en, we);
dBus <= ramOut when en = '1' and rw = '1'
and (addr(15 downto 4) /= x"0FF")
else (dBus'range => 'Z');
end ramArch;
The interface presented by the RAM module has a memory enable and
read/write signals, together with an address bus and a bidirectional
data bus. The ram16k component provided by Coregen has separate
data buses for inputs and outputs and slightly different control
signals. Note that since the ram16k component only has 8K words,
only 13 of the 16 address signals on the external address bus
are actually used. Also note that the ram module puts the dBus
output in the high impedance state whenever it is not being
read. It also does not respond to reads for addresses that
are in the range 0FF0 to 0FFF, since these addresses are
used by the IO module (more on this later).
The purpose of the loader is to initialize the memory so that when the processor beings execution, there is a program already present in memory for it to execute. This makes it relatively easy for users to simulate different programs or experiment with them on the S3 board. The program to be loaded is specified as a constant array of 16 bit values. The external reset button for the S3 board is connected to the restart input of the loader, which provides an internal reset signal used nby the other components of the system. The loader holds this internal reset signal high while it is loading the program into memory, to prevent the processor from starting execution until the loader is ready for it to do so. The VHDL module implementing the loader appears below.
entity loader is port (
clk, restart: in std_logic;
reset, m_en, m_rw: out std_logic;
aBus: out address;
dBus: inout word);
end loader;
architecture loaderArch of loader is
-- Update progSize and program in order to load a different
-- program into memory on startup.
constant progSize: integer := 14; -- number of words to load into memory
type wordArray is array(0 to progSize-1) of word;
constant program: wordArray := (
-- Read input values and accumulate sum.
-- Display current sum. Stop when zero is input
x"1000", -- sum = 0; (sum stored in location x0020)
x"4020",
x"1001", -- wait for new value in input register
x"dffc",
x"7002",
x"2ffd", -- if input value is zero, terminate
x"700d",
x"a020", -- sum = sum + input value
x"4020",
x"4fff", -- show value on display
x"1000", -- clear input control register for next value
x"4ffc",
x"6002", -- branch to top
x"0000" -- halt
);
signal inst: word;
signal tick: std_logic_vector(2 downto 0);
begin
-- update reset, inst and tick on rising clock edges
process(clk) begin
if rising_edge(clk) then
if restart = '1' then
reset <= '1';
inst <= (inst'range => '0');
tick <= (tick'range => '0');
else
if tick < 5 then tick <= tick + 1; end if;
if tick = 4 then
if inst < progSize - 1 then
inst <= inst + 1;
tick <= (tick'range => '0');
else
reset <= '0';
end if;
end if;
end if;
end if;
end process;
-- memory operations occur on falling clock edges
process(clk) begin
if falling_edge(clk) then
if restart = '1' then
m_en <= '0'; m_rw <= '0';
aBus <= (abus'range => 'Z');
dBus <= (dbus'range => 'Z');
else
case int(tick) is
when 0 => m_en <= '1'; aBus <= inst;
when 1 => m_rw <= '0'; dBus <= program(int(inst));
when 3 => m_rw <= '1';
when 4 =>
m_en <= '0';
aBus <= (abus'range => 'Z');
dBus <= (dBus'range => 'Z');
when others => -- do nothing
end case;
end if;
end if;
end process;
end loaderArch;
entity ssCtrl is port(
clk, reset: in std_logic;
ssBtn: in std_logic;
proceed: out std_logic);
end ssCtrl;
architecture ssCtrlArch of ssCtrl is
constant ssDelay: integer := 8 + operationMode*99999992;
signal ssCntr: bigDelay;
signal ssMode, prevSsBtn: std_logic;
begin
process(clk) begin
if rising_edge(clk) then
prevSsBtn <= ssBtn;
if reset = '1' then
ssMode <= '0'; -- initialize to normal mode
elsif ssMode = '0' then
-- transition to ss mode on release of short button push
if ssBtn < prevSsBtn then
ssMode <= '1';
end if;
else -- ssMode = '1'
if ssBtn > prevSsBtn then
-- clear counter on button press
ssCntr <= (ssCntr'range => '0');
elsif ssBtn = '1' then
if ssCntr /= ssDelay then
ssCntr <= ssCntr + 1;
end if;
elsif ssBtn < prevSsBtn then
-- return to normal mode on release of short button push
if ssCntr = ssDelay then
ssMode <= '0';
end if;
end if;
end if;
end if;
end process;
-- assert proceed in normal mode or on release of short button press
proceed <= '1' when ssMode = '0' or
(ssBtn < prevSsBtn and ssCntr /= ssDelay)
else '0';
end ssCtrlArch;
entity ioUnit is port(
clk, reset: in STD_LOGIC;
en, r_w: in std_logic;
abus: in address;
dbus: inout word;
snoopPort: in regSet;
displayModeBtn, loadBtn: in std_logic;
swt: in std_logic_vector(nSwt-1 downto 0);
led: out std_logic_vector(nLED-1 downto 0);
an : out std_logic_vector(nDig-1 downto 0);
ssg: out std_logic_vector(7 downto 0));
end ioUnit;
The ioUnit is connected to the address and data bus to allow
the processor to access its internal registers using normal
load/store instructions. The snoop port (snoopPort) gives the
IO unit access to the CPU's registers so that it can display
their values on the four digit display.
The display mode button (displayModeBtn) controls which of several
display modes the IO unit is in and the load button (loadBtn)
is used to control the input of data values.
The eight switch inputs (swt) are used to specify input data.
The LED signals are used to display data on the S3 boards eight
individual LEDs. The anode signals (an) and seven segment
display signals (ssg) are used to control the operation of the
S3 board'sfour digit display.
The IO unit contains several internal registers that can be accessed by running programs using ordinary load/store instructions. The input control register is accessed using address 0FFC. The input data register is accessed using address 0FFD. The led register is accessed using address 0FFE. The output register is accessed using address 0FFF. The two input registers can also be changed when a user inputs values using the load button. A short press of the load button (less than 2 seconds) causes an eight bit value to be transferred from the switches to the low order byte of inReg, and sets bit 0 of inCtrlReg. A longer button press loads the high order byte of inReg and sets bit 1 of inCtrlReg. The VHDL process that controls these registers appears below.
process(clk) begin
if rising_edge(clk) then
prevLoadBtn <= loadBtn;
dbus <= (dbus'range => 'Z');
if reset = '1' then
inCtrlReg <= (inCtrlReg'range => '0');
inReg <= (inReg'range => '0');
ledReg <= (ledReg'range => '0');
outReg <= (outReg'range => '0');
else
-- time how long the load button is held down
if loadBtn > prevLoadBtn then
loadCntr <= (loadCntr'range => '0');
elsif loadBtn = '1' then
if loadCntr /= loadDelay then
loadCntr <= loadCntr + 1;
end if;
end if;
-- input data in response when load button is released
-- short press loads low-order byte
-- long press loads high-order byte
if loadBtn < prevLoadBtn then
if loadCntr /= loadDelay then
inReg(7 downto 0) <= swt;
inCtrlReg(0) <= '1';
else
inReg(15 downto 8) <= swt;
inCtrlReg(1) <= '1';
end if;
end if;
-- handle memory-mapped IO by program
if en = '1' and r_w = '0' then
case aBus is
when x"0ffc" => inCtrlReg <= dBus;
when x"0ffd" => inReg <= dBus;
when x"0ffe" => ledReg <= dBus;
when x"0fff" => outReg <= dBus;
when others => -- nada
end case;
elsif en = '1' and r_w = '1' then
case aBus is
when x"0ffc" => dbus <= inCtrlReg;
when x"0ffd" => dbus <= inReg;
when x"0ffe" => dbus <= ledReg;
when x"0fff" => dbus <= outReg;
when others => -- nada
end case;
end if;
end if;
end if;
end process;
The IO module can display any of several pieces of information on the seven segment display: the value of outReg, the Program Counter, the Instruction Register, the Accumulator or the Indirect Address Register. Which of these is displayed is determined by the current display mode. Because the S3 board can only display one digit at a time on the four digit display, the IO unit cycles through the four digits every 20 ms, giving the impression that it is continuously displaying values on all four digits. The VHDL code that handles the display appears below.
displayProcess:
process (clk, selDig, displayValue, displayCnt) begin
if rising_edge(clk) then
displayCnt <= displayCnt + 1;
if reset = '1' then
an <= "1111"; -- off to start
displayCnt <= (displayCnt'range => '0');
end if;
end if;
case displayCnt(displayCntBits downto displayCntBits-1) is
when "00" => selDig <= displayValue( 3 downto 0); an <= "1110";
when "01" => selDig <= displayValue( 7 downto 4); an <= "1101";
when "10" => selDig <= displayValue(11 downto 8); an <= "1011";
when others => selDig <= displayValue(15 downto 12); an <= "0111";
end case;
ssg <= ssDecode(selDig);
end process;
with displayMode select
displayValue <= outReg when outputReg,
snoopPort.pc when progCntr,
snoopPort.iReg when instReg,
snoopPort.acc when accumulator,
snoopPort.iar when others;
The constant displayCntBits is typically set to 19,
causing a new digit to be displayed whenever there is a change
in bits 19 and 18. Since the S3 board clock period is 20 ns,
these bits change roughly once every 5 ms.
The final piece of the IO unit is the part that determines the display mode. A user can select the desired display mode by pressing button 1 on the S3 board. This causes the display mode to change in a cyclic fashion. The VHDL that implements this appears below.
-- Process for switching among display modes.
-- 0-1 transition of displayModeBtn advances
-- mode from program to adrBus to dataBus and
-- back to program.
displayModeProcess:
process (clk)
begin
if rising_edge(clk) then
prevDisplayModeBtn <= displayModeBtn;
if reset = '1' then
displayMode <= outputReg;
elsif displayModeBtn > prevDisplayModeBtn then
-- advance mode on stable 0-1 transition
case displayMode is
when outputReg => displayMode <= progCntr;
when progCntr => displayMode <= instReg;
when instReg => displayMode <= accumulator;
when accumulator => displayMode <= iAdrReg;
when others => displayMode <= outputReg;
end case;
end if;
end if;
end process;