Final Design Project: CPU221
By
Peter Burrows, Joanna Wycech, Hokchhay Tann, Jessica
L’Heureux, Junius Santoso, Bicky Shakya, Vishal Bharam, Marco
Eberth
Engineering 221
12.13.11
Table Of Contents: Page No
Introduction 1
Design Goals & Objectives 1
Methodology: 1
Control Unit 2
Register 2
Instruction 2
ALU 4
Addressing Logic 8
VGA 12
Understanding of the VGA 12
Programming Aspect 12
Creating blocks on screen 14
Final VGA Interface 15
Result and Discussion
Control Unit 16
ALU 17
Addressing Logic 17
VGA 18
Debounce Switch: 19
Conclusion 20
Bibliography
Appendix:
Schematic diagram for CPU 21
Schematic for RAM and ROM combined 22
Programming Model 23
CPU timing diagram 24
Instruction Table 25
Sample Subtraction Program 26
Truth Table for Control Unit 27
VHDL for Control 28
Timing diagram for ALU 33
VHDL for addressing logic 37
Timing Diagram for addressing logic 39
Complete Schematic for CPU including VGA 41
Final timing diagram for complete CPU 42
VHDL for VGA Display 43
Introduction
The CPU221 allows one to apply their knowledge of VHDL code to program a microprocessor
system, which carries out a designated operation. The code is split up into sections, each of which are
compiled and simulated to determine if the CPU does in fact perform the correct instruction based on the
operation codes.
Design Goals & Objectives
The CPU221 problem presents the opportunity to design a complete “tiny” microprocessor
system, including CPU and memory, which will execute programs you write in its native machine
language. The design proceeds in a stepwise, modular fashion that involves designing and verifying each
sub-system with VHDL, integrating the elements into a whole and running program. One may realize the
design in a large CPLD or in the FPGA that is resident on the Altera University Program UP-2
development board or on the Xilinx BASYS board. By doing this project one will gain a clear
understanding of how a simple processor works, how the processor executes instructions, and how the
hardware executes software instructions. This is good preparation for further work in hardware and
software engineering.
In order to complete these goals, the group of eight must be divided into different groups
determined by the various sections of the project; thus, each group is responsible for the design and
VHDL verification of their sub-system. The groups are responsible for the following sections: Control
Unit, ALU, Addressing Logic/FPGA, and VGA. Upon the establishment of each group, it is necessary
that the entire group understand the basic instructions carried out by the CPU – this is done through the
use of an extensive Planning Sheet and Schematic Diagram. With these tools, VHDL programming is
completed to carry out operations based on their respective opcodes. Finally, the ultimate goal is to
engineer a CPU that successfully performs the ADD operation.
The detailed description about the work distribution is given below:
CPU Architecture and programming:
Addressing Logic and FPGA: Joanna and Jessica
Control Unit: Hokchhay and Junius
ALU: Peter and Marco
VGA Interfacing and Debounce: Bicky and Vishal
Methodology
Control Unit
CU, which stands for Control Unit is the circuitry that controls the flow of information through
the processor, and the coordinates the activities of the other units within it. The control unit determines
the sequence in which computer programs and instructions are executed. Things like processing of
programs stored in the main memory, interpretation of the instructions and issuing of signals for other
units of the computer to execute them. It also acts as a switchboard operator when several users access the
computer simultaneously.
The control unit is a finite state machine that takes its inputs as the IR, the status register (which
is partly filled by the status output from the ALU), and the current major state of the cycle. Its rules are
encoded either in random logic, a Programmable Logic Array (PLA), or Read-Only Memory (ROM), and
its outputs are sent across the processor to each point requiring coordination or direction for the control
unit. Following is the more detailed overview of the various registers and states implemented in the status
register.
i) Register:
PC ( 8 bits): The Program Counter register, pointing to the memory location, corresponding to the
instructions given in the program.
MAR (8 bits): The Memory Address register points to the desired memory locations. It could be
loaded from PC, X or TEMP register through MARMUX.
TEMP (8 bits): This register was created in order to hold temporary memory. This was mainly
introduced to avoid the racing errors. For example, when we had to send data from Idbus to
MAR, it was stored in the TEMP, which directly loads it to MAR.
Read/~Write Enable (1 bit): This one bit register controls if we were writing into the memory or
reading from the memory. When R/~W equal to 1, the data was being read from memory and
when R/~W equal 0, the data was written on the memory.
IR (4 bits): This register was responsible for all the instruction given to the control unit. It holds
the opcode that is to be executed. However, IR was not really necessary, as the instruction could
have also read from the memory and could have decoded using a decoder.
A (8 bits): This is main operational register through which most of the arithmetic operands and
results are stored. The data from Idbus is also transferred to the ALU, through this register using
AMUX.
X (8 bits): This is secondary operational register, where the second operands are being stored.
This register basically holds the memory address. The data from Idbus is transferred to X through
XMUX.
SR (4 bits): This is the status register, which is being used to show the status of the output though
ALU. This register is kept as a four bit register, where each bit represent a status.
ii) Instruction:
As mentioned earlier, Control Unit (CU) is used to control all of the instruction.
Control unit is separated into two parts which are state generator and control logic. As we might be
able to tell from its name, state generator will generate states (TR, T0, T1, T2, T3, T4, T5). The states will
move in sequentially from T0 up to T5 every time we pulse the clock with low rise pulse (negative edge
triggered) while the state will stay in TR state (reset state) if we let reset input equal to 1. Each states will
have appropriate value of signal y(state) that will be used to driven the control logic, for example at T0
we let yT0 to be equal 1 while yT1, yT2, yT3, yT4, yT5 equal to 0.
The code writing process for the control logic part will solely base on the CPU 221 planning sheet.
This planning sheet will record all the steps required by each opcodes at each state in order to finish its
operation. Furthermore, it will also include all the required selects (selo, selad0, selad1, selx, sela, selpc)
and enables (enmar, entmp, ena, enir, enx, enpc, ensr) for each state. For example, at state T0 program
counter (PC) will always point to MAR, this step will require r_w, selad0, selad1, and enmar to be 1.
After recording all those steps we can then start to find the logic expressions for all the selects and
enables. In order to do this we implemented an excel file that includes the appropriate conditions (state
and opcode) for each selects and enables signal to be on (logic-high).
enmar T0 T1 T2 T3 T4 T5 T6
LDAI 1 1
LDAD 1 1 1
LDAX 1 1 1
ADDI 1 1
ADDX 1 1
NANDX 1 1
STAD 1 1 1
STAX 1 1
NANDI 1 1
LDXI 1 1
LDXD 1 1 1
STXD 1 1 1
UDXI 1 1
BRA 1 1
BEQ 1 1
FigureXX: Truth table for enmar signal.
See appendix 1 for the complete tables of all selects and enables signals.
For example, if we see from the figure above the enmar signal will always be equal to 1 at T0 and T2
regardless the input of opcodes. The signal will also be equal to 1 at T4 for several opcodes while 0 at
other states. Thus the logic expression for enmar signal is:
enmar =T0 OR T2 OR (T4 AND LDAD) OR (T4 AND LDAX) OR (T4 AND STAD) OR (T4 AND
LDXD) OR (T4 AND STXD).
As mentioned before, the control logic part will be driven by the signal y(state) from the state generator
thus the code will end up looks like:
enmar =yT0 OR yT2 OR (yT4 AND LDAD) OR (yT4 AND LDAX) OR (yT4 AND STAD) OR (yT4
AND LDXD) OR (yT4 AND STXD).
After getting both the state generator and control logic part, the next step will be writing the VHDL code
and simulating it. Details explanation of the timing diagram will be explained in the Result and
Discussion part but basically we just need to test the code for each input of opcodes.
For Timing Diagrams & VHDL code for the CU, refer to Appendix.
ALU:
The arithmetic logic unit (ALU) is a standard component in the CPU. Bitwise logic operations
and operations such as addition, subtraction, multiplication, etc. can be performed by the ALU. The ALU
in CPU221 loads and stores data transmitted by the idbus [8...0] into the registers based on the instruction.
The instruction is generated by the control unit, which manipulates all of the enablers and MUX
controllers depending on the current state. The idbus data can be stored in either the Instruction Register
(IR), the TMP register, or the A register depending on each enabler and clock edge.
The IR translates the idbus data into an operational code. To change the opcode, the idbus must
be changed and the IR must be enabled while the clock pulse is on the positive edge. Essentially, the
idbus is changed through a string of data on the Read Only Memory (ROM) controlled by the PC counter.
The IR becomes enabled when the opcode is transmitted on the idbus from the ROM.
For our program, 4+1 is carried out and stored in the RAM. The A register serves as an
accumulator that stores the two numbers being added. A is loaded with ‘4’ from the idbus through the
instruction LDAI. Then ‘1’ is loaded into the TMP register and added to A through the instruction ADDI.
The ALU in CPU221 performs arithmetic on four different instructions (ADDI, ADDX, NANDI, and
NANDX) when initiated by the ROM. For all other instructions, the idbus is stored in the registers
depending on the enablers, which are triggered by the Control Unit. The instructions with their
corresponding opcodes are seen in the table to the right.
Many instructions aren’t used in our simple program. However, if we were to develop a
subtraction program for example, more instructions would be utilized. NANDI followed by ADDI (ADD
0000 0001 immediately) would be used to develop a two’s complement and so on.
sela
gclk
ena
ensr
enir
entmp
idbus[7..0]
TMP_out[7..0]
A_out[7..0]
opcode_out[7..0]
Z_out[7..0]
SR
CPU_ALU
inst
The diagrams above illustrate the inputs and outputs of the ALU. The symbol to the left displays
detailed approach of the system, and the symbol to the right displays the general block diagram of the
system. Outputs opcode_out [7…0] and Z_out [7…0] in the Quartus ALU symbol were implemented to
make the simulations easier to see. The Quartus ALU symbol was generated from the VHDL code seen
below. In the file, the inputs and outputs are defined in the notation.
ARCHITECTURE Behavior OF CPU_ALU IS
--opcode = instruction coming out of the IR
--A_mux = output data from the A multiplexer
--TMP_sout = same as TMP_out
--sum_ab = the ALU sum operation of the TMP and A registers
--car_ab = the carry from each bit from the TMP and A bytes
--nand_ab = the nand value from each bit from the TMP and A bytes
--z_vnout = the carry output from the ALU; will be stored in the SR when enabled
--LDAIcode...etc = will tell the ALU whether or not it needs to add, nand, etc.
--sela = select A; determines the A MUX output
--ena = enable A; enables the A register
--ensr = enable SR; enables the status register
--enir = enable IR; enables the instruction register
--entmp = enable TMP; enables the Temporary storage register
--gclk = synchronous clock
Diagram of ALU
TMP_out Opcode[7…0]
Block Diagram/Quartus
ALU Symbol
--idbus = constanly transmitting data and data addresses from the ROM
--TMP_out = The data coming out of the TMP register
--A_out = the data coming out of the A register
--opcode_out = the data coming out of the IR; represents the opcode
--Z_out = the Z data coming out the ALU; the end result of the operation
--SR = the data coming out of the status register; represents the zero condition only
For all timing Diagrams, code & Simulations for the ALU , see Appendix.
Addressing Logic
The first step in writing the VHDL code for the addressing logic of the CPU was to understand
what each part of it is responsible for. The addressing logic includes a register X, an adder, a program
counter (PC) and a memory address (MAR), as well as four muxes. Going from the top of the diagram
(For Diagram/Schematic, see Lab Handout) the top register is labeled X, it is an index register that
contains an address. When the gclk is high and x is enabled, the data stored under that address comes
either from the idbus, when selx is high in the xmux, or from the offsum, when selx is low. Following the
X register, we have an 8-bit adder, which adds whatever values come from the TMP register, which is a
part of the CPU logic, to either a value stored in the PC (when selo is low in the OMUX), or value stored
in the X register (when selo is high). The next piece is the program counter, which contains the address of
the next instruction. The PC stores the value coming from the adder when selpc is low, however when
selpc is high, the value that is stored in PC gets incremented as PC+1. The final and most important piece
of the addressing logic is the MAR register that contains the current 8-bit memory address; it stores
locations that start with a 0 in the ROM, and the locations that start with 1 in the RAM. The location in
the MAR is stored from the register X, when selad1 is low, and selad0 is high, from TMP register when
both selad1 and selad0 are low, and from the PC when both selad1 and selad0 are zero.
The first piece of VHDL logic was specifying the inputs and outputs. There are four select inputs:
selad1 and selad0, which are responsible for transferring an address to the MAR, as well as selx which is
responsible for transferring the address into X register, and selo putting an address into the adder. There
are also three enable inputs: enx- enables the x register; enpc- enables to store the address into program
counter and enmar- enabling the storage in MAR. With that we have a gclk input that will call an enable
at the positive edge of the rising clock. For the addressing logic we also needed to have two 8-bit inputs
(tmp_out and idbus). Tmp_out is an 8-bit input of the adder that comes from the ALU part of the CPU,
and idbus is an 8-bit address coming from the bus interface unit of the CPU. With all the inputs we really
needed one output ADDR, which is an 8-bit memory address, however it make it easier for us to see if the
addressing logic circuit works properly we added pc_out, x_out, offsum_out and marmux_out outputs to
display the values stored in different registers.
LIBRARY ieee;
USE ieee.std_logic_1164.all ;
ENTITY addressinglogic IS
PORT (selad1,selad0,selx, enx, selo, selpc, enpc, enmar, reset :IN STD_LOGIC;
gclk :IN STD_LOGIC;
tmp_out, idbus :IN STD_LOGIC_VECTOR (7 DOWNTO 0);
ADDR, pc_out, x_out, offsum_out, marmux_out :OUT STD_LOGIC_VECTOR (7 DOWNTO 0));
END addressinglogic;
The next part of the code was building the architecture for the addressing logic. First, we had to
specify the signals that will connect all the registers in the unit. We used signal X to symbolize the X
register, offsum to show the output of an adder, car_ad to be used as a carry in the adder, pcplusone which
increments the new program counter, mar that contains the current 8-bit memory address, adder adding
values from either program counter or the X register with the value in TMP register, and finally carry_pc
that is a carry for the program counter. We also had four signals for the four different muxes in the circuit.
ARCHITECTURE behavior OF addressinglogic IS
SIGNAL X,offsum, pc, car_ad, pcplusone,mar,adder, carry_pc :STD_LOGIC_VECTOR
(7 DOWNTO 0);
SIGNAL pcmux, omux, marmux, xmux :STD_LOGIC_VECTOR (7 DOWNTO 0);
The next piece was writing the body of the code for the muxes. For the o multiplexer, when selo
is set to low omux gets the value from pc, but when set to high it gets the address from the X register. For
x multiplexer, when selx is set to low, xmux gets the address from offsum (adder), however when it is set
to high its gets the value from the idbus. For pc multiplexer, when selpc is set to low pcmux gets the
address from the adder (offsum) and if it is set to high it gets the incremented value of the program
counter. For the final MAR multiplexer, since there are two controls (selad1 and selad0) we needed to
base the select input on two values. When selad1 is low and selad0 is low the marmux gets an address
from the X register. When selad1 is set to low and selad0 is set to high, marmux gets the value from TMP
register, finally when selad1 is set to high and selad0 is high as well then marmux gets the address from
the pc register.
BEGIN
-- ss <= selad1, selad0;
WITH selo SELECT
omux <= pc WHEN '0',
X WHEN OTHERS;
WITH selx SELECT
xmux <= offsum WHEN '0',
idbus WHEN OTHERS;
WITH selpc SELECT
pcmux <= offsum WHEN '0',
pcplusone WHEN OTHERS;
marmux <= X WHEN (selad1 = '0') AND (selad0 = '0') ELSE
tmp_out WHEN (selad1 = '0') AND (selad0 = '1') ELSE
pcmux WHEN (selad1 = '1') AND (selad0 = '1') ELSE
"00000000";
The next part was writing the code for an adder, which adds the value put through the OMUX
(designated by the selo switch) and the value from the TMP register in the arithmetic logic unit. The code
adds the values bit-by- bit and includes the needed carry (car_ad) when adding the bits.
offsum(0) <= omux(0) XOR tmp_out(0);
car_ad (0) <= omux(0) AND tmp_out(0);
offsum(1) <= omux (1) XOR tmp_out(1) XOR car_ad(0);
car_ad(1) <= (omux(1) AND tmp_out(1)) OR (omux(1) AND car_ad(0)) OR (tmp_out(1)
AND car_ad(0));
offsum(2) <= omux (2) XOR tmp_out(2) XOR car_ad(1);
car_ad(2) <= (omux(2) AND tmp_out(2)) OR (omux(2) AND car_ad(1)) OR (tmp_out(2)
AND car_ad(1));
offsum(3) <= omux (3) XOR tmp_out(3) XOR car_ad(2);
car_ad(3) <= (omux(3) AND tmp_out(3)) OR (omux(3) AND car_ad(2)) OR (tmp_out(3)
AND car_ad(2));
offsum(4) <= omux (4) XOR tmp_out(4) XOR car_ad(3);
car_ad(4) <= (omux(4) AND tmp_out(4)) OR (omux(4) AND car_ad(3)) OR (tmp_out(4)
AND car_ad(3));
offsum(5) <= omux (5) XOR tmp_out(5) XOR car_ad(4);
car_ad(5) <= (omux(5) AND tmp_out(5)) OR (omux(5) AND car_ad(4)) OR (tmp_out(5)
AND car_ad(4));
offsum(6) <= omux (6) XOR tmp_out(6) XOR car_ad(5);
car_ad(6) <= (omux(6) AND tmp_out(6)) OR (omux(6) AND car_ad(5)) OR (tmp_out(6)
AND car_ad(5));
offsum(7) <= omux (7) XOR tmp_out(7) XOR car_ad(6);
car_ad(7) <= (omux(7) AND tmp_out(7)) OR (omux(7) AND car_ad(6)) OR (tmp_out(7)
AND car_ad(6));
The next part of the architecture of the code was writing the implementation of the program
counter that contains the address of the next instruction and is updated by branching. It includes carry_pc,
similarly to our code for the adder, it has to add 1 to each bit to update the current location, that is why the
carry needs to be accounted for.
pcplusone(0) <= NOT pc(0);
carry_pc(0) <= pc(0);
pcplusone(1) <= pc(1) XOR carry_pc(0);
carry_pc(1) <= pc(1) AND carry_pc(0);
pcplusone(2) <= pc(2) XOR carry_pc(1);
carry_pc(2) <= pc(2) AND carry_pc(1);
pcplusone(3) <= pc(3) XOR carry_pc(2);
carry_pc(3) <= pc(3) AND carry_pc(2);
pcplusone(4) <= pc(4) XOR carry_pc(3);
carry_pc(4) <= pc(4) AND carry_pc(3);
pcplusone(5) <= pc(5) XOR carry_pc(4);
carry_pc(5) <= pc(5) AND carry_pc(4);
pcplusone(6) <= pc(6) XOR carry_pc(5);
carry_pc(6) <= pc(6) AND carry_pc(5);
pcplusone(7) <= pc(7) XOR carry_pc(6);
The final piece of the code was storing the address in the registers. We are going to process gclk
and reset. If the reset is set to high, then the value in the program counter will be all zeros, which means
the whole program will start the process from the beginning. When enpc is set to high then pc register will
get the value from pcmux. In the case when reset is no longer one, and gclk is high, as well as register x is
enabled, then X register gets the address from XMUX. With glck set to high and enmar set to high, out
final output memory address gets the address stored in MARMUX. Then the process and behavior are
finished in the addressing logic part of the CPU.
pc_out <= pcmux;
x_out <= X;
offsum_out <= offsum;
marmux_out <= marmux;
load: PROCESS (gclk, reset)
BEGIN
IF reset = '1' THEN pc <= "00000000"; ELSIF gclk 'EVENT AND gclk = '1' THEN
IF enpc = '1' THEN pc <= pcmux; END IF; END IF;
IF gclk 'EVENT AND gclk = '1' THEN
IF enx = '1' THEN X <= xmux; END IF;
IF enmar = '1' THEN ADDR <= marmux; END IF;
END IF;
END PROCESS load;
END behavior;
The VHDL code was then compiled successfully and the simulation was run and an appropriate
timing diagram was created (For Timing Diagram of the Simulation, see Appendix) as explained in the
results and discussion section.
VGA
Making a VGA was not the requirement for the initial project; however, as there were eight people in
the group, we decided to implement a VGA, as it would also help to verify the whole project. Thus, we
decided to go on with the VGA.
i) Understanding of the VGA:
VGA (Video Graphics Array) is a popular mode of computer display. In this system, an electron
beam is sent out to a phosphorescent screen. This screen has color-phosphor dots, which radiate red,
green and blue color when an electron beam falls on them. These dots are what we know as ‘pixels’.
Normal clocked frequency for a VGA display ranges around 25 MHz and the display resolution
corresponds to 640*480 pixels, in accordance with the frequency.
When we started to work on the VGA project, we had no idea about any single component of the
VGA. Thus, in order to understand how it is possible to generate a video image using the Altera board, it
was first necessary to understand the various components of the VGA.
After studying about the various parts of the VGA, using different sources, we realized there are
total of 5 active signals are coming out of the VGA, which are known as Red_out, Green_out, Blue_out,
Horiz_out and Vert_out. The three-color signals are often called as RGB, which stands for the primary
colors. Along with these five active signals, there were two 10-bit outputs known as pixel_row and
pixel_columb. These two extra outputs were used to show the exact position of every bit of inputs
(Furman and Hamblen, 140).
640 Pixels
480 pixels
ii) Programming Aspects:
After understanding the ‘physics’ aspect of the VGA display, we started on implementing the
display on VHDL. Our main focus was on generating a coded mechanism for tracing the position of
pixels on the screen. Pixels could either be along a row or switch from row-to-row. With this in mind,
we started the VHDL coding.
In this part of the program, we are first concerned with the input clock signal of 25 Mhz. When
there is a positive clock edge from the clock signal (a high of ‘1’), the program first checks if our variable
h_count has reached the end of the row in the pixel grid. If it has, the value of h_count is set to
‘0000000000’ (RESET), placing our ‘cursor’ at the beginning of the row. If not, the pixels in a row are
counted with the clock signal (counted at every positive clock edge). Thus, we are advancing with the
pixels in a row (from left to right).
Summary: h_count keeps track of position of pixel in a row.
In the h_count section, we were concerned with counting pixels in the same row. Now, the
v_count will be implemented to count the number of rows. When the ‘cursor’ (our electron beam) has
reached the last row (524) and the last column (699), this will trigger the value of v_count to reset. If this
is not the case, when the cursor reaches the last pixel in every row, it will cause the value of v_count to go
up by one.
Summary: v_count helps us to count the number of rows.
Note: Values implemented in the program are not exactly ‘640’ or ‘480’ because we are taking
into consideration the inherent delays in the synchronization signals. This means that we need to leave
some time before and after the actual counting of pixels and rows.
iii) Creating the Blocks on the screen:
After generating the horizontal and vertical timing signals for the video signals, screen signals for
pixel data was used to create the box for the each and every input bit used. Using specific number of the
v_count, which and vertical pixel and h_count, which stands for the horizontal pixel, the boxes were
created.
As you can see in the given code above, vertical and horizontal data was created in order to show
the blocks for the each bit. If it is observed carefully, we can see that from 10 to 400 vertical pixels were
taken into the account in order to create ten rows, where from 50 to 350 horizontal pixels were taken into
the account in order to generate the eight columns referring to the eight bits. All of the blocks were
created as squares of values (twenty pixels × twenty pixels).
After generating the limits of the horizontal and vertical pixel and creating blocks for each bit, we
were supposed to assign them for the bits of the input. All of the blocks were assigned in the same way
using its horizontal and vertical position. The following part of the code shows the block assigned for the
R/~W signal. For this particular block, vertical pixels are in the range of 10-30, and horizontal pixels are
in the range of 50-70. Furthermore, we had choice to show all of these blocks either in red, green or blue
color, where we chose green. Thus, green_out was enabled by assigned it to ‘1’ and blue_out and red_out
were assigned to the bit value as shown below.
As seen above, if the w_e, for example, is 0 then the color of the block remains green, otherwise
if the R/~W (w_e) is ‘1’, then all red, green and blue output would be activated and the color of the block
would change from the green to white (R+G+B = W). Therefore, we had create separate section of ‘IF…
END IF’ for each bit.
iv) The final VGA interface
The Final VGA Interface with Grids Representing States of Components of CPU
1st row - R/~W
2nd
row - IR
3rd
row - PC
4th row - MAR
5th row - IDBUS
6th row - DBUS
7th row - A
8th row - X
9th row - TEMP
10th row - Z
11th row - CLK_COUNT
Results and Discussions
Control Unit
A) Control unit:
Figure1: Timing diagram for control unit
Figure2: Timing diagram for control unit – instruction LDAI
As mentioned before we need to simulate the VHDL code for all opcode inputs, however in this
section we will discuss only the simulation for opcode “0000” or instruction LDAI just for the simplicity.
If we see at the second low rise pulse, TR state changes to T0 state so according to our planning
sheet PC goes to MAR so we should get signal 1 for r_w, enmar, selad0, and salad1. In fact that is what
shown in the timing diagram.
After the third low rise pulse, again state should change form T0 to T1. According to the planning
sheet idbus goes to IR, so we should get signal 1 for r_w, enpc, enir, and selpc. In fact that what is we
have in the diagram.
After the fourth low rise pulse, again state should change form T1 to T2. According to the
planning sheet PC goes to MAR, so we should get signal 1 for r_w, enmar, selad0, and salad1. In fact that
what is we have in the diagram.
After the fifth low rise pulse, again state should change form T1 to T2. According to the planning
sheet idbus goes to A and PC increments by 1, so we should get signal 1 for r_w, ena, enpc, selpc. In fact
that what is we have in the diagram.
Since we get all the correct signals for each states thus we could conclude that our VHDL code is
correct. One important thing to note is that we need to set reset input to be 1 every time we finished
simulating an opcode/instruction. This is done because we want the state to come back to 0 before starting
with the new opcode.
ALU
Simulation results for the ADDI, ADDX, NANDI and NANDX instructions are observed and
explained on the next four pages. The Z_out value changed when any of these four instructions were
fetched and was loaded to A depending on the A select MUX and the A enabler. When any other
instruction was fetched, the ALU did not undergo an arithmetic operation, the Z-out value was not
changed, and the registers were loaded or not loaded, depending on the control logic.
It’s important to understand that ADDI and ADDX both yield (A PLUS TMP), and NANDI and
NANDX both yield (A NAND TMP). However, the difference between the pairs is what gets loaded in
the registers and when they get loaded. Once again, that depends on the control logic. The simulations
for the ALU just test to make sure that the A NAND TMP and A PLUS TMP operations work correctly.
They were set up such that ADDI and ADDX differ by the loaded opcode. The same applies to NANDI
and NANDX. Thus the results will be the same for each pair.
Addressing Logic
In writing the VHDL code for the addressing logic of the central processing unit understanding of
the information flow and the importance of using the multiplexers to switch between the data was crucial.
We were able to compile our program and simulate it to perform different instructions. We ran over ten
different simulations, in order to make sure the proper address is stored correctly in the desired register,
and that the select and multiplexers work properly with each other.
The first simulation we are presenting (For Timing Diagram 1 Addressing Logic, see Appendix)
performs storing of the memory address coming in from the idbus in the X register and then recognizing it
as a current 8-bit memory address stored in the MAR (output ADDR). In order to do that we loaded an
address 00001101 on the idbus, and on the positive edge of the gclk we set selx to high, to get the address
from the idbus, and enabled x to store it in X register. Now that the X register contained that address we
wanted to make it the current address for the ADDR output, in order to do that on the next clock pulse we
setselad0 to low and selad1 to low, as well as we enabled MAR register, which stored our memory
address in the MAR register and therefore our output was “00001101”. We were successful, because we
were able to simply follow the diagram from the handout, which presented the registers and multiplexers.
The second simulation we are presenting (For Timing Diagram 1 Addressing Logic, see
Appendix) uses the adder for the address on the idbus and the address in TMP register. It then takes the
output from the adder (offsum) and stores it as a current memory address in the MAR register, giving out
the desired output. On the first clock pulse we load idbus with “00001001”, as well as set selx to high and
enx to high so that the address from the idbus is stored in the X register. On the next clock pulse we load
tmp_out, which in the final, interconnected circuit will be an address stored in the TMP register, to
“00000011”, and set selo to high, which bring the two addresses (from X register and TMP register) into
the adder. Note that the output of the adder (offsum) changes then its value to 12, which means the bit-
by- bit addition was performed correctly. On the same clock pulse we then set selpc to low, and enpc to
high to load the offsum into the program counter. Note that it is done correctly, since pc_out changes to
12. We then also set both selad1 and selad0 to high, in order to enable MAR multiplexer. Finally, on the
next clock pulse we set enmar to high, in order to put the address from the pc to the MAR and out to
ADDR input, which you can see displays “00001100” making our simulation successful.
Writing the code for the addressing logic of the CPU was challenging, because it incorporated a
lot of logic about multiplexers and transferring the data between different registers. We found the
incrementing the pc to be a little tricky, because it involved a code, which was fairly similar to the adder,
but different because it just added one to the given address. With that said, it was exciting to see how in
the end our code was able to store the address in different registers, and perform different functions.
VGA
Making the VGA work was one of the very challenging and we had to go through a lot to make it
actually work. First we were given the UP2 board, which required Quartus 5.1 and parallel port, which
were unable to find in most of the computer, except a computer in the Q-lab. Therefore, we decided to
get a new board DE2, which was able to work on Quartus 9.1 and it had USB interfaced wire, which
could just be connected to any computer or laptop with Quartus 9.1. When we started to work with DE2,
it seemed as it was working fine. However, as we kept going, more and more problem started coming. We
were working with it for more than five-six days. We tried to measure the frequency and period of the
waves coming from all of the outputs from the VGA and it working fine. After that we checked the same
for the clocks. However, it wasn’t working for the 27MHz clock, which was a problem.
After getting a disappointing result from the work of a week, we decided to go back to the UP2
board. Then we had to download Quartus 5.1 in one of the old computer, where the parallel port would
work. After downloading and installing the Quartus 5.1, we also had a lot of trouble finding the valid
license for it, which we found after a long time. After writing a sample program, we created one block
and we were able to show is successfully on the monitor screen after a lot of debugging. After completing
the whole code for the VGA, the next challenge was to actually make it work and implement the program.
Debounce Switch
One last minute addition to our program was the debounce switch. We realized that on the
frequency of the clock we were using (25 MHz), we would barely be able to see any of the processing
going on within the CPU (on the VGA display). Thus, we needed a system to be able to ‘freeze’ the clock
cycles so that we could observe the states of the various data buses and registers.
This system was implemented using a push-button on the UP2. The push-button is always in a
state of ‘1’ but when we press it, the state changes to ‘0’. So, we setup a system such that when we
pressed the push-button, our clock cycle would be activated. But when the push-button was released, the
clock would stop and the system would ‘freeze’ in the state that it was last in. Only then would we be able
to see the states of the various indicators on the VGA display.
In this program, if we press the push button (button = ‘0’), the value of count increases. In the top
part, we can see that if the value of count increases up to 2000000 (clock cycles), the debounce now gets a
value of ‘1’. This is what will clock all our units in the CPU. If the button is released, count gets a value
of ‘0’. So, we are no longer which means that the value of debounce will be stuck at zero. This will
‘freeze’ the system at the state it was before it was stopped. Thus, we are effectively slowing down the
system so that we can observe the various states.
For the Complete Schematic of the entire CPU221, see Appendix.
Conclusion
Ultimately, the CPU221 consisted of applying one’s knowledge of VHDL code for the
programming and engineering of a microprocessor system. The microprocessor must then carry out
operations based on the operation codes, which are established through the combining of each section of
VHDL code for the complete CPU. Unfortunately, the CAD tools were obsolete and couldn’t contain all
the VHDL coding for 8-bits. However, when transferred the whole system into 4-bits, we were able to
perform expected operations.
2. VHDL code for ALU
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY CPU_ALU IS
PORT
(sela,gclk,ena,ensr,enir,entmp:IN STD_LOGIC;
idbus: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
TMP_out,A_out,opcode_out,Z_out: OUT STD_LOGIC_VECTOR (7 DOWNTO 0);
SR: OUT STD_LOGIC);
END CPU_ALU;
SIGNAL opcode,A_mux,A,Z,TMP_sout,sum_ab,car_ab,nand_ab: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL z_vnout: STD_LOGIC_VECTOR(3 DOWNTO 0);
SIGNAL LDAIcode,LDADcode,LDAXcode,ADDIcode,ADDXcode,NANDXcode,STADcode,
STAXcode,NANDIcode,LDXIcode,LDXDcode,STXDcode,UDXIcode,BRAcode,BEQcode:
STD_LOGIC;
BEGIN
--INSTANT DECODER
ADDIcode <= (NOT opcode(3)) AND (NOT opcode(2)) AND (opcode(1)) AND (opcode(0));
ADDXcode <= (NOT opcode(3)) AND (opcode(2)) AND (NOT opcode(1)) AND (NOT opcode(0));
NANDXcode <= (NOT opcode(3)) AND (opcode(2)) AND (NOT opcode(1)) AND (opcode(0));
NANDIcode <= (opcode(3)) AND (NOT opcode(2)) AND (NOT opcode(1)) AND (NOT opcode(0));
--Add Immediate (ADDI) and Add indexed (ADDX) operations of two bytes stores in the A
and TMP registers
sum_ab(0) <= a(0) XOR TMP_sout(0);
car_ab(0) <= a(0) AND TMP_sout(0);
sum_ab(1) <= a(1) XOR TMP_sout(1) XOR car_ab(0);
car_ab(1) <= (a(1) AND TMP_sout(1)) OR (a(1) AND car_ab(0)) OR (TMP_sout(1) AND
car_ab(0));
sum_ab(2) <= a(2) XOR TMP_sout(2) XOR car_ab(1);
car_ab(2) <= (a(2) AND TMP_sout(2)) OR (a(2) AND car_ab(1)) OR (TMP_sout(2) AND
car_ab(1));
sum_ab(3) <= a(3) XOR TMP_sout(3) XOR car_ab(2);
car_ab(3) <= (a(3) AND TMP_sout(3)) OR (a(3) AND car_ab(2)) OR (TMP_sout(3) AND
car_ab(2));
sum_ab(4) <= a(4) XOR TMP_sout(4) XOR car_ab(3);
car_ab(4) <= (a(4) AND TMP_sout(4)) OR (a(4) AND car_ab(3)) OR (TMP_sout(4) AND
car_ab(3));
sum_ab(5) <= a(5) XOR TMP_sout(5) XOR car_ab(4);
car_ab(5) <= (a(5) AND TMP_sout(5)) OR (a(5) AND car_ab(4)) OR (TMP_sout(5) AND
car_ab(4));
sum_ab(6) <= a(6) XOR TMP_sout(6) XOR car_ab(5);
car_ab(6) <= (a(6) AND TMP_sout(6)) OR (a(6) AND car_ab(5)) OR (TMP_sout(6) AND
car_ab(5));
sum_ab(7) <= a(7) XOR TMP_sout(7) XOR car_ab(6);
car_ab(7) <= (a(7) AND TMP_sout(7)) OR (a(7) AND car_ab(6)) OR (TMP_sout(7) AND
car_ab(6));
--NAND indexed (NANDX) and NAND immediate (NANDI) operations of two bytes
nand_ab(0) <= a(0) NAND TMP_sout(0);
nand_ab(1) <= a(1) NAND TMP_sout(1);
nand_ab(2) <= a(2) NAND TMP_sout(2);
nand_ab(3) <= a(3) NAND TMP_sout(3);
nand_ab(4) <= a(4) NAND TMP_sout(4);
nand_ab(5) <= a(5) NAND TMP_sout(5);
nand_ab(6) <= a(6) NAND TMP_sout(6);
nand_ab(7) <= a(7) NAND TMP_sout(7);
--The sum
Z <= sum_ab WHEN ADDIcode = '1' ELSE
sum_ab WHEN ADDXcode = '1' ELSE
nand_ab WHEN NANDXcode = '1' ELSE
nand_ab WHEN NANDIcode = '1';
--A multiplexer
A_mux <= Z WHEN sela = '1' ELSE
idbus WHEN sela = '0';
--The zero signal
z_vnout(0) <= (NOT Z(0)) AND (NOT Z(1)) AND (NOT Z(2)) AND (NOT Z(3)) AND (NOT Z(4))
AND (NOT Z(5)) AND (NOT Z(6)) AND (NOT Z(7));
--Outputs
TMP_out <= TMP_sout;
A_out <= A;
--Outputs (For simulation use)
opcode_out <= opcode;
Z_out <= Z;
loadregs: PROCESS(gclk)
BEGIN
IF gclk 'EVENT AND gclk = '1' THEN
IF ena = '1' THEN A <= A_mux; END IF;
IF entmp = '1' THEN TMP_sout <= idbus; END IF;
IF enir = '1' THEN opcode <= idbus; END IF;
IF ensr = '1' THEN SR <= z_vnout(0); END IF;--zero bit
END IF;
END PROCESS loadregs;
END Behavior;
3. VHDL code for control unit
Library ieee;
USE ieee.std_logic_1164.all;
ENTITY control_unit IS
PORT
(gclk, reset, sr: IN STD_LOGIC;
opcode : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
enmar, entmp, ena, enir, enx, enpc, ensr : OUT STD_LOGIC; --
selo, selad1, selad0, selx, sela, selpc, r_w : OUT STD_LOGIC;
T0_out, T1_out, T2_out, T3_out, T4_out, T5_out : OUT STD_LOGIC); --
END control_unit;
ARCHITECTURE Behavior OF control_unit IS
TYPE State_type IS (TR, T0, T1, T2, T3, T4, T5);
SIGNAL y: State_type;
SIGNAL yT0, yT1, yT2, yT3, yT4, yT5: std_logic;
signal LDAI, LDAD, LDAX, ADDI, ADDX, NANDX, STAD, STAX, NANDI, LDXI, LDXD,
STXD, UDXI, BRA, BEQ: STD_LOGIC;
BEGIN
PROCESS(reset,gclk)
BEGIN
IF opcode="00000000" THEN
LDAI <= '1';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00000001" THEN
LDAI <= '0';LDAD <= '1'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00000010" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '1'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00000011" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '1'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00000100" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '1';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00000101" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '1'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00000110" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '1'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00000111" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '1'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00001000" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '1'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00001001" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '1';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00001010" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '1'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00001011" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '1'; UDXI <= '0'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00001100" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '1'; BRA <= '0'; BEQ <= '0';
ELSIF opcode="00001101" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '1'; BEQ <= '0';
ELSIF opcode="00001110" THEN
LDAI <= '0';LDAD <= '0'; LDAX <= '0'; ADDI <= '0'; ADDX <= '0';
NANDX <= '0'; STAD <= '0'; STAX <= '0'; NANDI <= '0'; LDXI <= '0';
LDXD <= '0'; STXD <= '0'; UDXI <= '0'; BRA <= '0'; BEQ <= '1';
END IF;
IF reset='1' THEN
y <= TR;
ELSIF (gclk'EVENT AND gclk='0')THEN
CASE y IS
WHEN TR =>
y <= T0;
WHEN T0 =>
y <= T1;
yT0 <= '1';
yT1 <= '0';
yT2 <= '0';
yT3 <= '0';
yT4 <= '0';
yT5 <= '0';
T0_out <= '1';
WHEN T1 =>
y <= T2;
yT0 <= '0';
yT1 <= '1';
yT2 <= '0';
yT3 <= '0';
yT4 <= '0';
yT5 <= '0';
T1_out <= '1';
WHEN T2 =>
y <= T3;
yT0 <= '0';
yT1 <= '0';
yT2 <= '1';
yT3 <= '0';
yT4 <= '0';
yT5 <= '0';
T2_out <= '1';
WHEN T3 =>
yT0 <= '0';
yT1 <= '0';
yT2 <= '0';
yT3 <= '1';
yT4 <= '0';
yT5 <= '0';
T3_out <='1';
IF opcode="00000000" OR opcode="00001001" THEN
y <= T0;
ELSE
y <= T4;
END IF;
WHEN T4 =>
yT0 <= '0';
yT1 <= '0';
yT2 <= '0';
yT3 <= '0';
yT4 <= '1';
yT5 <= '0';
T4_out <='1';
IF opcode="00000001" OR opcode="00000010" OR
opcode="00001010" THEN
y <= T5;
ELSE
y <= T0;
END IF;
WHEN T5 =>
y <= T0;
yT0 <= '0';
yT1 <= '0';
yT2 <= '0';
yT3 <= '0';
yT4 <= '0';
yT5 <= '1';
T5_out <= '1';
END CASE;
END IF;
END PROCESS;
enmar<= yT0 OR yT2 OR (yT4 AND LDAD) OR (yT4 AND LDAX) OR (yT4 AND STAD)
OR (yT4 AND LDXD) OR (yT4 AND STXD);
entmp<=(yT3 AND LDAD) OR (yT3 AND ADDI)OR(yT3 AND ADDX)OR(yT3 AND
NANDX)OR(yT3 AND STAD)OR(yT3 AND NANDI)OR(yT3 AND LDXD)OR(yT3 AND STXD)OR
(yT3 AND UDXI)OR(yT3 AND BRA)OR(yT3 AND BEQ);
ena<= (yT3 AND LDAI)OR(yT4 AND ADDI)OR(yT4 AND ADDX)OR(yT4 AND
NANDX)OR(yT4 AND NANDI)OR(yT5 AND LDAD)OR(yT5 AND LDAX);
enir<=yT1;
enx<=(yT3 AND LDAX)OR(yT3 AND STAX)OR(yT3 AND LDXI)OR(yT4 AND UDXI)OR(yT5
AND LDXD);
enpc<= yT1 OR(yT3 AND LDAI)OR(yT3 AND LDAD)OR(yT3 AND LDAX)OR(yT3 AND
ADDI)OR(yT3 AND STAD)OR(yT3 AND NANDI)OR(yT3 AND LDXI)OR
(yT3 AND LDXD)OR(yT3 AND STXD)OR(yT3 AND UDXI)OR(yT4 AND ADDX)OR
(yT4 AND NANDX)OR(yT4 AND BRA)OR(yT4 AND BEQ)OR(yT5 AND LDAD)OR (yT5 AND
LDAX)OR(yT3 AND LDXD);
ensr <= yT1;
selo<=(yT3 AND UDXI)OR(yT3 AND BRA);
r_w<= yT0 OR yT1 OR yT2 OR (yT2 AND LDAI) OR (yT2 AND LDAD) OR (yT2 AND
LDAX) OR (yT2 AND ADDI) OR (yT2 AND ADDX) OR (yT2 AND NANDX) OR
(yT2 AND STAD) OR (yT2 AND STAX) OR (yT2 AND NANDI) OR (yT2 AND LDXI) OR
(yT2 AND LDXD) OR (yT2 AND BRA) OR (yT2 AND BEQ)OR (yT3 AND LDAI) OR
(yT3 AND LDAD) OR (yT3 AND LDAX) OR (yT3 AND ADDI) OR (yT3 AND ADDX) OR
(yT3 AND NANDX) OR (yT3 AND STAX) OR (yT3 AND NANDI) OR (yT3 AND LDXI) OR
(yT3 AND LDXD) OR (yT3 AND STXD) OR (yT3 AND UDXI) OR (yT3 AND BRA) OR
(yT3 AND BEQ) OR (yT4 AND lDAD) OR (yT4 AND LDAX) OR (yT4 AND LDXD) OR
(yT5 AND LDAD) OR (yT5 AND LDAX)OR (yT4 AND LDXD);
selad1<= yT0 OR(yT2 AND LDAI) OR (yT2 AND LDAD) OR (yT2 AND LDAX) OR (yT2
AND ADDI) OR (yT2 AND STAD) OR (yT2 AND STAX) OR (yT2 AND NANDI) OR (yT2
AND LDXI) OR (yT2 AND LDXD) OR (yT2 AND STXD) OR (yT2 AND UDXI) OR (yT2
AND BRA) OR (yT2 AND BEQ);
selad0<= yT0 OR (yT2 AND LDAI) OR (yT2 AND LDAD) OR (yT2 AND LDAX) OR
(yT2 AND ADDI) OR (yT2 AND STAD) OR (yT2 AND STAX) OR (yT2 AND NANDI) OR
(yT2 AND LDXI) OR (yT2 AND LDXD) OR (yT2 AND STXD) OR (yT2 AND UDXI)
OR(yT2 AND BRA) OR (yT2 AND BEQ) OR (yT4 AND LDAD) OR (yT4 AND STAD) OR
(yT4 AND LDXD) OR (yT4 AND STXD);
selx<=(yT3 AND LDAX) OR (yT2 AND STAX) OR (yT2 AND LDXI) OR (yT5 AND
LDXD);
sela <=(yT4 AND ADDI) OR (yT4 AND ADDX) OR (yT4 AND NANDX) OR (yT4 AND
NANDI);
selpc<=(yT1 AND LDAI) OR (yT1 AND LDAD) OR (yT1 AND LDAX) OR (yT1 AND
ADDI) OR (yT1 AND STAD) OR (yT1 AND STAX) OR (yT1 AND NANDI) OR (yT1 AND
LDXI) OR (yT1 AND LDXD) OR (yT1 AND STXD) OR (yT1 AND UDXI) OR (yT1 AND
BRA) OR (yT1 AND BEQ) OR (yT3 AND LDAI) OR (yT3 AND LDAD) OR (yT3 AND
LDAX) OR (yT3 AND ADDI) OR (yT3 AND STAD) OR (yT3 AND STAX) OR (yT3 AND
NANDI) OR (yT3 AND LDXI) OR (yT3 AND LDXD) OR (yT3 AND STXD) OR (yT4 AND
ADDX) OR (yT4 AND NANDX) OR
(yT5 AND LDAD) OR (yT3 AND LDAX) OR (yT3 AND LDXD);
END Behavior;
4. VHDL for VGA
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.all;
USE IEEE.STD_LOGIC_ARITH.all;
USE IEEE.STD_LOGIC_UNSIGNED.all;
-- VGA Video Sync generation
ENTITY VGA_SYNC IS
PORT(w_e, clock_25Mhz, cpu_clk : IN STD_LOGIC;
PC, MAR, A, X, TEMP : IN
STD_LOGIC_VECTOR(7 DOWNTO 0);
Idbus, dbus, CLK_CNT : IN
STD_LOGIC_VECTOR(7 DOWNTO 0);
IR
: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
red_out, green_out, blue_out : OUT STD_LOGIC;
horiz_sync_out, vert_sync_out : OUT STD_LOGIC;
pixel_row, pixel_column : OUT
STD_LOGIC_VECTOR(9 DOWNTO 0) );
END VGA_SYNC;
ARCHITECTURE a OF VGA_SYNC IS
SIGNAL horiz_sync, vert_sync : STD_LOGIC;
SIGNAL red_out_s,green_out_s, blue_out_s : STD_LOGIC;
SIGNAL video_on, video_on_v, video_on_h : STD_LOGIC;
SIGNAL h_count, v_count :
STD_LOGIC_VECTOR(9 DOWNTO 0);
BEGIN
green_out_s<= '1';
-- video_on is high only when RGB data is being displayed
video_on <= video_on_H AND video_on_V;
--Generate Horizontal and Vertical Timing Signals for Video Signal
PROCESS
BEGIN
WAIT UNTIL(clock_25Mhz'EVENT) AND (clock_25Mhz='1');
-- H_count counts pixels (640 + extra time for sync signals)
--
-- Horiz_sync ------------------------------------__________--------
-- H_count 0 640 659 755 799
--
IF (h_count = 799) THEN
h_count <= "0000000000";
ELSE
h_count <= h_count + 1;
END IF;
--Generate Horizontal Sync Signal using H_count
IF (h_count <= 755) AND (h_count >= 659) THEN
horiz_sync <= '0';
ELSE
horiz_sync <= '1';
END IF;
--V_count counts rows of pixels (480 + extra time for sync signals)
--
-- Vert_sync ----------------------------------_______------------
-- V_count 0 480 493-494 524
--
IF (v_count >= 524) AND (h_count >= 699) THEN
v_count <= "0000000000";
ELSIF (h_count = 699) THEN
v_count <= v_count + 1;
END IF;
-- Generate Vertical Sync Signal using V_count
IF (v_count <= 494) AND (v_count >= 493) THEN
vert_sync <= '0';
ELSE
vert_sync <= '1';
END IF;
-- Generate Video on Screen Signals for Pixel Data
-- Generating horizatal pixel data
IF ((h_count >=50 AND h_count <= 70) OR (h_count >=90 AND h_count <= 110) OR
(h_count >=130 AND h_count <= 150) OR (h_count >=170 AND h_count <= 190)
OR
(h_count >=210 AND h_count <= 230) OR (h_count >=250 AND h_count <= 270)
OR
(h_count >=290 AND h_count <= 310) OR (h_count >=330 AND h_count <= 350))
THEN
video_on_h <= '1';
pixel_column <= h_count;
ELSE
video_on_h <= '0';
END IF;
-- Generating veritical pixel data
IF (((v_count >=10 AND v_count<=30) AND (h_count >= 50 AND h_count <= 70)) OR
((v_count >=50 AND v_count <= 70) AND (h_count >= 50 AND h_count <= 350))
OR
((v_count >=90 AND v_count<= 110) AND (h_count >= 50 AND h_count <= 190))
OR
((v_count >=130 AND v_count<= 150) AND (h_count >= 50 AND h_count <=
350)) OR
((v_count >=170 AND v_count<= 190) AND (h_count >= 50 AND h_count <= 350)
) OR
((v_count >=210 AND v_count<= 230) AND (h_count >= 50 AND h_count <=
350)) OR
((v_count >=250 AND v_count<= 270) AND (h_count >= 50 AND h_count <=
350)) OR
((v_count >=290 AND v_count<= 310) AND (h_count >= 50 AND h_count <=
350)) OR
((v_count >=330 AND v_count<= 350) AND (h_count >= 50 AND h_count <=
350)) OR
((v_count >=380 AND v_count<= 400) AND (h_count >= 50 AND h_count <=
350))) THEN
video_on_v <= '1';
pixel_row <= v_count;
ELSE
video_on_v <= '0';
END IF;
-- Controlling the first block of R/~W >> w_e
IF ((v_count >=10 AND v_count <= 30) AND (h_count >=50 AND h_count <= 50)) THEN
blue_out_s<= w_e;
green_out_s<= w_e;
END IF;
-- Controlling color data for pc, first row
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=50 AND h_count <= 70)) THEN
blue_out_s<= PC(7);
green_out_s<= PC(7);
END IF;
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= PC(6);
green_out_s<= PC(6);
END IF;
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= PC(5);
green_out_s<= PC(5);
END IF;
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= PC(4);
green_out_s<= PC(4);
END IF;
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=210 AND h_count <= 230))
THEN
blue_out_s<= PC(3);
green_out_s<= PC(3);
END IF;
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=250 AND h_count <= 270))
THEN
blue_out_s<= PC(2);
green_out_s<= PC(2);
END IF;
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=290 AND h_count <= 310))
THEN
blue_out_s<= PC(1);
green_out_s<= PC(1);
END IF;
IF ((v_count >=50 AND v_count <= 70) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= PC(0);
green_out_s<= PC(0);
END IF;
-- Controlling color data for IR, second row
IF ((v_count >=90 AND v_count <= 110) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= IR(3);
green_out_s<= IR(3);
END IF;
IF ((v_count >=90 AND v_count <= 110) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= IR(2);
green_out_s<= IR(2);
END IF;
IF ((v_count >=90 AND v_count <= 110) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= IR(1);
green_out_s<= IR(1);
END IF;
IF ((v_count >=90 AND v_count <= 110) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= IR(0);
green_out_s<= IR(0);
END IF;
-- Controlling color data for MAR, third row
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= MAR(7);
green_out_s<= MAR(7);
END IF;
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= MAR(6);
green_out_s<= MAR(6);
END IF;
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= MAR(5);
green_out_s<= MAR(5);
END IF;
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= MAR(4);
green_out_s<= MAR(4);
END IF;
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=210 AND h_count <= 230))
THEN
blue_out_s<= MAR(3);
green_out_s<= MAR(3);
END IF;
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=250 AND h_count <= 270))
THEN
blue_out_s<= MAR(2);
green_out_s<= MAR(2);
END IF;
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=290 AND h_count <= 310))
THEN
blue_out_s<= MAR(1);
green_out_s<= MAR(1);
END IF;
IF ((v_count >=130 AND v_count <= 150) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= MAR(0);
green_out_s<= MAR(0);
END IF;
-- Controlling color data for Idbus, fourth row
IF ((v_count >=170 AND v_count <= 50) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= Idbus(7);
green_out_s<= Idbus(7);
END IF;
IF ((v_count >=170 AND v_count <= 190) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= Idbus(6);
green_out_s<= Idbus(6);
END IF;
IF ((v_count >=170 AND v_count <= 190) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= Idbus(5);
green_out_s<= Idbus(5);
END IF;
IF ((v_count >=170 AND v_count <= 190) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= Idbus(4);
green_out_s<= Idbus(4);
END IF;
IF ((v_count >=170 AND v_count <= 190) AND (h_count >=190 AND h_count <= 210))
THEN
blue_out_s<= Idbus(3);
green_out_s<= Idbus(3);
END IF;
IF ((v_count >=170 AND v_count <= 190) AND (h_count >=230 AND h_count <= 250))
THEN
blue_out_s<= Idbus(2);
green_out_s<= Idbus(2);
END IF;
IF ((v_count >=170 AND v_count <= 190) AND (h_count >=270 AND h_count <= 310))
THEN
blue_out_s<= Idbus(1);
green_out_s<= Idbus(1);
END IF;
IF ((v_count >=170 AND v_count <= 190) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= Idbus(0);
green_out_s<= Idbus(0);
END IF;
-- Controlling color data for dbus, fifth row
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= dbus(7);
green_out_s<= dbus(7);
END IF;
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= dbus(6);
green_out_s<= dbus(6);
END IF;
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= dbus(5);
green_out_s<= dbus(5);
END IF;
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= dbus(4);
green_out_s<= dbus(4);
END IF;
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=210 AND h_count <= 230))
THEN
blue_out_s<= dbus(3);
green_out_s<= dbus(3);
END IF;
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=250 AND h_count <= 270))
THEN
blue_out_s<= dbus(2);
green_out_s<= dbus(2);
END IF;
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=290 AND h_count <= 310))
THEN
blue_out_s<= dbus(1);
green_out_s<= dbus(1);
END IF;
IF ((v_count >=210 AND v_count <= 230) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= dbus(0);
green_out_s<= dbus(0);
END IF;
--Controlling color data for A, sixth row
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= A(7);
green_out_s<= A(7);
END IF;
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= A(6);
green_out_s<= A(6);
END IF;
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= A(5);
green_out_s<= A(5);
END IF;
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= A(4);
green_out_s<= A(4);
END IF;
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=210 AND h_count <= 230))
THEN
blue_out_s<= A(3);
green_out_s<= A(3);
END IF;
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=250 AND h_count <= 270))
THEN
blue_out_s<= A(2);
green_out_s<= A(2);
END IF;
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=290 AND h_count <= 310))
THEN
blue_out_s<= A(1);
green_out_s<= A(1);
END IF;
IF ((v_count >=250 AND v_count <= 270) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= A(0);
green_out_s<= A(0);
END IF;
--Controlling color data for A, seventh row
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= X(7);
green_out_s<= X(7);
END IF;
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= X(6);
green_out_s<= X(6);
END IF;
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= X(5);
green_out_s<= X(5);
END IF;
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= X(4);
green_out_s<= X(4);
END IF;
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=210 AND h_count <= 230))
THEN
blue_out_s<= X(3);
green_out_s<= X(3);
END IF;
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=250 AND h_count <= 270))
THEN
blue_out_s<= X(2);
green_out_s<= X(2);
END IF;
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=290 AND h_count <= 310))
THEN
blue_out_s<= X(1);
green_out_s<= X(1);
END IF;
IF ((v_count >=290 AND v_count <= 310) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= X(0);
green_out_s<= X(0);
END IF;
--controlling color data for TEMP, eighth row
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= TEMP(7);
green_out_s<= TEMP(7);
END IF;
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= TEMP(6);
green_out_s<= TEMP(6);
END IF;
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= TEMP(5);
green_out_s<= TEMP(5);
END IF;
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= TEMP(4);
green_out_s<= TEMP(4);
END IF;
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=210 AND h_count <= 230))
THEN
blue_out_s<= TEMP(3);
green_out_s<= TEMP(3);
END IF;
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=250 AND h_count <= 270))
THEN
blue_out_s<= TEMP(2);
green_out_s<= TEMP(2);
END IF;
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=290 AND h_count <= 310))
THEN
blue_out_s<= TEMP(1);
green_out_s<= TEMP(1);
END IF;
IF ((v_count >=330 AND v_count <= 350) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= TEMP(0);
green_out_s<= TEMP(0);
END IF;
-- FINAL ROW : CLK COUNT
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=50 AND h_count <= 70))
THEN
blue_out_s<= CLK_CNT(7);
green_out_s<= CLK_CNT(7);
END IF;
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=90 AND h_count <= 110))
THEN
blue_out_s<= CLK_CNT(6);
green_out_s<= CLK_CNT(6);
END IF;
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=130 AND h_count <= 150))
THEN
blue_out_s<= CLK_CNT(5);
green_out_s<= CLK_CNT(5);
END IF;
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=170 AND h_count <= 190))
THEN
blue_out_s<= CLK_CNT(4);
green_out_s<= CLK_CNT(4);
END IF;
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=210 AND h_count <= 230))
THEN
blue_out_s<= CLK_CNT(3);
green_out_s<= CLK_CNT(3);
END IF;
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=250 AND h_count <= 270))
THEN
blue_out_s<= CLK_CNT(2);
green_out_s<= CLK_CNT(2);
END IF;
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=290 AND h_count <= 310))
THEN
blue_out_s<= CLK_CNT(1);
green_out_s<= CLK_CNT(1);
END IF;
IF ((v_count >=380 AND v_count <= 400) AND (h_count >=330 AND h_count <= 350))
THEN
blue_out_s<= CLK_CNT(0);
green_out_s<= CLK_CNT(0);
END IF;
-- Put all video signals through DFFs to elminate
-- any logic delays that can cause a blurry image
red_out <= red_out_s AND video_on;
green_out <= green_out_s AND video_on;
blue_out <= blue_out_s AND video_on;
horiz_sync_out <= horiz_sync;
vert_sync_out <= vert_sync;
END PROCESS;
END a;