testbench for a microprocessor

M

Mr.X

can anyone help to write da testbench for this 8 bit microprocessor
suggestions r welcome


=====================================================================
Structural VHDL code for the complete EC-2 general-purpose
microprocessor

LIBRARY IEEE;
USE IEEE.std_logic_1164.all;

ENTITY mp IS PORT (
Clock : IN STD_LOGIC;
Reset: IN STD_LOGIC;
Enter: IN STD_LOGIC;
Input: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
Output: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
Halt: OUT STD_LOGIC);
END mp;

ARCHITECTURE Structural OF mp IS

COMPONENT cu PORT (
Clock : IN STD_LOGIC;
Reset : IN STD_LOGIC;
-- control input
Enter: IN STD_LOGIC;
-- control signals
IRload: OUT STD_LOGIC;
JMPmux: OUT STD_LOGIC;
PCload: OUT STD_LOGIC;
MemInst: OUT STD_LOGIC;
MemWr: OUT STD_LOGIC;
Asel: OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: OUT STD_LOGIC;
Sub: OUT STD_LOGIC;
-- status signals
IR: IN STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: IN STD_LOGIC;
Apos: IN STD_LOGIC;
-- control outputs
Halt: OUT STD_LOGIC);
END COMPONENT;

COMPONENT dp PORT (
Clock: IN STD_LOGIC;
Clear: IN STD_LOGIC;
-- datapath input
Input: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- control signals
IRload: IN STD_LOGIC;
JMPmux: IN STD_LOGIC;
PCload: IN STD_LOGIC;
MemInst: IN STD_LOGIC;
MemWr: IN STD_LOGIC;
ASel: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: IN STD_LOGIC;
Sub: IN STD_LOGIC;
-- status signals
IR: OUT STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: OUT STD_LOGIC;
Apos: OUT STD_LOGIC;
-- datapath output
Output: OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END COMPONENT;
-- control signals
SIGNAL mp_IRload: STD_LOGIC;
SIGNAL mp_JMPmux: STD_LOGIC;
SIGNAL mp_PCload: STD_LOGIC;
SIGNAL mp_MemInst: STD_LOGIC;
SIGNAL mp_MemWr: STD_LOGIC;
SIGNAL mp_Asel: STD_LOGIC_VECTOR(1 DOWNTO 0);
SIGNAL mp_Aload: STD_LOGIC;
SIGNAL mp_Sub: STD_LOGIC;
-- status signals
SIGNAL mp_IR: STD_LOGIC_VECTOR(7 DOWNTO 5);
SIGNAL mp_Aeq0: STD_LOGIC;
SIGNAL mp_Apos: STD_LOGIC;
BEGIN
-- doing structural modeling for the microprocessor here
U0: cu PORT MAP (
Clock, Reset,
-- control input
Enter,
-- control signals
mp_IRload, mp_JMPmux, mp_PCload, mp_MemInst, mp_MemWr,
mp_Asel,
mp_Aload, mp_Sub,
-- status signals
mp_IR,
mp_Aeq0, mp_Apos,
-- control outputs
Halt);
U1: dp PORT MAP (
Clock, Reset,
-- datapath input
Input,
-- control signals
mp_IRload, mp_JMPmux, mp_PCload, mp_MemInst, mp_MemWr,
mp_Asel,
mp_Aload, mp_Sub,
-- status signals
mp_IR,
mp_Aeq0, mp_Apos,
-- datapath output
Output);
END Structural;
=====================================================================
VHDL code for the control unit of the EC-2.

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY cu IS PORT (
Clock : IN STD_LOGIC;
Reset : IN STD_LOGIC;
-- control input
Enter: IN STD_LOGIC;
-- control signals
IRload: OUT STD_LOGIC;
JMPmux: OUT STD_LOGIC;
PCload: OUT STD_LOGIC;
MemInst: OUT STD_LOGIC;
MemWr: OUT STD_LOGIC;
Asel: OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: OUT STD_LOGIC;
Sub: OUT STD_LOGIC;
-- status signals
IR: IN STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: IN STD_LOGIC;
Apos: IN STD_LOGIC;
-- control outputs
Halt: OUT STD_LOGIC);
END cu;
ARCHITECTURE FSM OF cu IS
TYPE state_type IS (s_start,s_fetch,s_decode,
s_load,s_store,s_add,s_sub,s_in,s_jz,s_jpos,s_halt);
SIGNAL state: state_type;
BEGIN
next_state_logic: PROCESS(Reset, Clock)
BEGIN
IF(Reset = '1') THEN
state <= s_start;
ELSIF(Clock'EVENT AND Clock = '1') THEN
CASE state IS
WHEN s_start => -- reset
state <= s_fetch;
WHEN s_fetch =>
state <= s_decode;
WHEN s_decode =>
CASE IR IS
WHEN "000" => state <= s_load;
WHEN "001" => state <= s_store;
WHEN "010" => state <= s_add;
WHEN "011" => state <= s_sub;
WHEN "100" => state <= s_in;
WHEN "101" => state <= s_jz;
WHEN "110" => state <= s_jpos;
WHEN "111" => state <= s_halt;
WHEN OTHERS => state <= s_halt;
END CASE;
WHEN s_load =>
state <= s_start;
WHEN s_store =>
state <= s_start;
WHEN s_add =>
state <= s_start;
WHEN s_sub =>
state <= s_start;
WHEN s_in =>
IF (Enter = '0') THEN -- wait for the Enter key for inputs
state <= s_in;
ELSE
state <= s_start;
END IF;
WHEN s_jz =>
state <= s_start;
WHEN s_jpos =>
state <= s_start;
WHEN s_halt =>
state <= s_halt;
WHEN OTHERS =>
state <= s_start;
END CASE;
END IF;
END PROCESS;
output_logic: PROCESS(state)
BEGIN
CASE state IS
WHEN s_fetch =>
IRload <= '1'; -- load IR
JMPmux <= '0';
PCload <= '1'; -- increment PC
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_decode => -- also set up for memory access
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1'; -- pass IR address to memory
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_load =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1';
MemWr <= '0';
Asel <= "10";-- pass memory to A
Aload <= '1'; -- load A
Sub <= '0';
Halt <= '0';
WHEN s_store =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1'; -- pass IR address to memory
MemWr <= '1'; -- store A to memory
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_add =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1';
MemWr <= '0';
Asel <= "00";-- pass add/sub unit to A
Aload <= '1'; -- load A
Sub <= '0'; -- select add
Halt <= '0';
WHEN s_sub =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1';
MemWr <= '0';
Asel <= "00";-- pass add/sub unit to A
Aload <= '1'; -- load A
Sub <= '1'; -- select subtract
Halt <= '0';
WHEN s_in =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '0';
MemWr <= '0';
Asel <= "01";-- pass input to A
Aload <= '1'; -- load A
Sub <= '0';
Halt <= '0';
WHEN s_jz =>
IRload <= '0';
JMPmux <= '1'; -- pass IR address to PC
PCload <= Aeq0; -- load PC if condition is true
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_jpos =>
IRload <= '0';
JMPmux <= '1'; -- pass IR address to PC
PCload <= Apos; -- load PC if condition is true
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_halt =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '1';
WHEN OTHERS =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
END CASE;
END PROCESS;
END FSM;
======================================================================
VHDL code for the datapath of the EC-2.


LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
-- for memory
USE work.lpm_components.ALL;

ENTITY dp IS
PORT (
Clock: IN STD_LOGIC;
Clear: IN STD_LOGIC;
-- datapath input
Input: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- control signals
IRload: IN STD_LOGIC;
JMPmux: IN STD_LOGIC;
PCload: IN STD_LOGIC;
MemInst: IN STD_LOGIC;
MemWr: IN STD_LOGIC;
ASel: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: IN STD_LOGIC;
Sub: IN STD_LOGIC;
-- status signals
IR: OUT STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: OUT STD_LOGIC;
Apos: OUT STD_LOGIC;
-- datapath output
Output: OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END dp;
ARCHITECTURE Structural OF dp IS

COMPONENT reg
GENERIC (size: INTEGER := 4); -- the actual size is defined in the
instantiation GENERIC MAP
PORT (
Clock, Clear, Load: IN STD_LOGIC;
D: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0);
Q: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0));
END COMPONENT;

COMPONENT increment
GENERIC (size: INTEGER := 8); -- default number of bits
PORT (
A: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0);
F: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0));
END COMPONENT;

COMPONENT mux2
GENERIC (size: INTEGER := 8); -- default size
PORT (
S: IN STD_LOGIC; -- select line
D1, D0: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0); -- data bus input
Y: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0)); -- data bus output
END COMPONENT;

COMPONENT mux4
GENERIC (size: INTEGER := 8); -- default size
PORT (
S: IN STD_LOGIC_VECTOR(1 DOWNTO 0); -- select line
D3, D2, D1, D0: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0); -- data bus
input
Y: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0)); -- data bus output
END COMPONENT;

COMPONENT addsub
GENERIC(n: INTEGER :=4); -- default number of bits = 4
PORT(S: IN std_logic; -- select subtract signal
A: IN std_logic_vector(n-1 DOWNTO 0);
B: IN std_logic_vector(n-1 DOWNTO 0);
F: OUT std_logic_vector(n-1 DOWNTO 0);
unsigned_overflow: OUT std_logic;
signed_overflow: OUT std_logic);
END COMPONENT;
SIGNAL dp_IR, dp_RAMQ: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL dp_JMPmux, dp_PC, dp_increment, dp_meminst: STD_LOGIC_VECTOR(4
DOWNTO 0);
SIGNAL dp_Amux, dp_addsub, dp_A: STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
-- doing structural modeling for the datapath here
-- IR
U0: reg GENERIC MAP(8) PORT MAP(Clock, Clear, IRLoad, dp_RAMQ, dp_IR);
IR <= dp_IR(7 DOWNTO 5);
-- JMPmux
U1: mux2 GENERIC MAP(5) PORT MAP(JMPmux,dp_IR(4 DOWNTO 0),
dp_increment,dp_JMPmux);
-- PC
U2: reg GENERIC MAP(5) PORT MAP(Clock, Clear, PCLoad, dp_JMPmux,
dp_PC);
-- Meminst
U3: mux2 GENERIC MAP(5) PORT MAP(Meminst,dp_IR(4 DOWNTO 0),
dp_PC,dp_meminst);
-- increment
U4: increment GENERIC MAP(5) PORT MAP(dp_PC,dp_increment);
-- memory
U5: lpm_ram_dq
GENERIC MAP (
lpm_widthad => 5,
lpm_outdata => "UNREGISTERED",
-- lpm_indata => "UNREGISTERED",
-- lpm_address_control => "UNREGISTERED",
lpm_file => "program.mif",-- fill ram with content of file program.mif
lpm_width => 8)
PORT MAP (
data => dp_A,
address => dp_meminst,
we => MemWr,
inclock => Clock,
q => dp_RAMQ);
-- A input mux
U6: mux4 GENERIC MAP(8) PORT MAP (
Asel,dp_RAMQ,dp_RAMQ,Input,dp_addsub,dp_Amux);
-- Accumulator
U7: reg GENERIC MAP(8) PORT MAP(Clock, Clear, ALoad, dp_Amux, dp_A);
-- Adder-subtractor
U8: addsub GENERIC MAP(8) PORT
MAP(Sub,dp_A,dp_RAMQ,dp_addsub,open,open);
Aeq0 <= '1' WHEN dp_A = "00000000" ELSE '0';
Apos <= NOT dp_A(7);
Output <= dp_A;
END Structural;

======================================================================
 
J

Jonathan Bromley

can anyone help to write da testbench for this 8 bit microprocessor
suggestions r welcome

You are joking, aren't you? If you *are* joking then it's
not a terribly good joke, because (sadly) lots of people
write code that, like the code you showed us, appears to
have been run through an obfuscator program. Or, at the
very least, typed-up from dictation by a disgruntled
secretary who doesn't know where the tab key is.

Horror of horrors, maybe it's *not* a joke. If so,
it's the most audacious bit of homework-dodging I've
seen in a long time. Surely that can't be true?

Here's what I'd do.

I'd start by seeking out the instruction-set documentation
for your CPU, and writing a really classy instruction-
accurate behavioural model of it in VHDL. That will
provide the reference model that you need for any good
testbench.

Next, you'll need a memory model, with the right bus
interface. Not an RTL memory model, because you will
need to be able to preload its contents with interesting
instructions for the CPU to chew, and possibly replace
its contents with randomized values. You'll need a
behavioural model. Not too difficult.

Then I'd build a testbench with the CPU-under-test
connected to the memory model. And I'd put a bus
monitor on the CPU-to-memory connection, to snoop
all its activity. And I'd arrange that every time
the CPU does a fetch, the corresponding instruction
data is fed to my behavioural CPU reference model.
And I'd put a comparator in there, so that I could
confirm that the CPU-under-test is doing the same
things as the behavioural (reference) model.

Now I've got a self-checking testbench, with nice
hooks for other checking and monitoring code. The
last step is to provide interesting streams of
instructions. You can do that in at least two ways:
pre-load your memory model from a file that you've
created from assembly code, or randomly. Luckily you
have built a fully self-checking testbench by now, so
it really doesn't matter what instructions the thing
executes - it just matters that you get a good mix.

It perhaps may not have escaped your notice that
the construction of a good testbench is a much
larger enterprise than the construction of the RTL
design of the CPU. I hope you enjoy the task, because
you are likely to be busy with it for quite some time.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
M

Mike Treseler

Mr.X said:
can anyone help to write da testbench for this 8 bit microprocessor
suggestions r welcome

Depends on the objective:

1. Learn vhdl simulation.

Drop the class, get a simulator, an internet connection
and work on your own. Then, with the money you've saved
on tuition, take a European vacation and an advanced
vhdl class from Jonathan.

2. Find some vhdl synthesis examples to play with

Google a bit. You can certainly do better than
than the one you have. CPU examples seem to be
particularly bad. They seem to attract those
who prefer to build an abstract layer they understand (a cpu)
around something they don't (vhdl synthesis).
Having said that, I guess must refrain from providing
a cpu example ;)

3. Actually write the testbench described.
Don't care about 1. or 2.

Save up about 10,000 euro and hire a consultant.
Synthesis is fun. Verification is work.

-- Mike Treseler
 
G

ghelbig

Depends on the objective:

1. Learn vhdl simulation.

Drop the class, get a simulator, an internet connection
and work on your own. Then, with the money you've saved
on tuition, take a European vacation and an advanced
vhdl class from Jonathan.

2. Find some vhdl synthesis examples to play with

Google a bit. You can certainly do better than
than the one you have. CPU examples seem to be
particularly bad. They seem to attract those
who prefer to build an abstract layer they understand (a cpu)
around something they don't (vhdl synthesis).
Having said that, I guess must refrain from providing
a cpu example ;)

3. Actually write the testbench described.
Don't care about 1. or 2.

Save up about 10,000 euro and hire a consultant.
Synthesis is fun. Verification is work.

-- Mike Treseler

Another idea would be to read the book that the EC-2 was presented in:
Digital Logic and Microprocessor Design with VHDL, Enoch O. Hwang.

G.
 
M

Mike Treseler

Another idea would be to read the book that the EC-2 was presented in:
Digital Logic and Microprocessor Design with VHDL, Enoch O. Hwang.

Thanks for tracking that one down.
You may have saved me $101.56 ;)

-- Mike Treseler
 
R

radarman

can anyone help to write da testbench for this 8 bit microprocessor
suggestions r welcome

=====================================================================
Structural VHDL code for the complete EC-2 general-purpose
microprocessor

LIBRARY IEEE;
USE IEEE.std_logic_1164.all;

ENTITY mp IS PORT (
Clock : IN STD_LOGIC;
Reset: IN STD_LOGIC;
Enter: IN STD_LOGIC;
Input: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
Output: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
Halt: OUT STD_LOGIC);
END mp;

ARCHITECTURE Structural OF mp IS

COMPONENT cu PORT (
Clock : IN STD_LOGIC;
Reset : IN STD_LOGIC;
-- control input
Enter: IN STD_LOGIC;
-- control signals
IRload: OUT STD_LOGIC;
JMPmux: OUT STD_LOGIC;
PCload: OUT STD_LOGIC;
MemInst: OUT STD_LOGIC;
MemWr: OUT STD_LOGIC;
Asel: OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: OUT STD_LOGIC;
Sub: OUT STD_LOGIC;
-- status signals
IR: IN STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: IN STD_LOGIC;
Apos: IN STD_LOGIC;
-- control outputs
Halt: OUT STD_LOGIC);
END COMPONENT;

COMPONENT dp PORT (
Clock: IN STD_LOGIC;
Clear: IN STD_LOGIC;
-- datapath input
Input: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- control signals
IRload: IN STD_LOGIC;
JMPmux: IN STD_LOGIC;
PCload: IN STD_LOGIC;
MemInst: IN STD_LOGIC;
MemWr: IN STD_LOGIC;
ASel: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: IN STD_LOGIC;
Sub: IN STD_LOGIC;
-- status signals
IR: OUT STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: OUT STD_LOGIC;
Apos: OUT STD_LOGIC;
-- datapath output
Output: OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END COMPONENT;
-- control signals
SIGNAL mp_IRload: STD_LOGIC;
SIGNAL mp_JMPmux: STD_LOGIC;
SIGNAL mp_PCload: STD_LOGIC;
SIGNAL mp_MemInst: STD_LOGIC;
SIGNAL mp_MemWr: STD_LOGIC;
SIGNAL mp_Asel: STD_LOGIC_VECTOR(1 DOWNTO 0);
SIGNAL mp_Aload: STD_LOGIC;
SIGNAL mp_Sub: STD_LOGIC;
-- status signals
SIGNAL mp_IR: STD_LOGIC_VECTOR(7 DOWNTO 5);
SIGNAL mp_Aeq0: STD_LOGIC;
SIGNAL mp_Apos: STD_LOGIC;
BEGIN
-- doing structural modeling for the microprocessor here
U0: cu PORT MAP (
Clock, Reset,
-- control input
Enter,
-- control signals
mp_IRload, mp_JMPmux, mp_PCload, mp_MemInst, mp_MemWr,
mp_Asel,
mp_Aload, mp_Sub,
-- status signals
mp_IR,
mp_Aeq0, mp_Apos,
-- control outputs
Halt);
U1: dp PORT MAP (
Clock, Reset,
-- datapath input
Input,
-- control signals
mp_IRload, mp_JMPmux, mp_PCload, mp_MemInst, mp_MemWr,
mp_Asel,
mp_Aload, mp_Sub,
-- status signals
mp_IR,
mp_Aeq0, mp_Apos,
-- datapath output
Output);
END Structural;
=====================================================================
VHDL code for the control unit of the EC-2.

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY cu IS PORT (
Clock : IN STD_LOGIC;
Reset : IN STD_LOGIC;
-- control input
Enter: IN STD_LOGIC;
-- control signals
IRload: OUT STD_LOGIC;
JMPmux: OUT STD_LOGIC;
PCload: OUT STD_LOGIC;
MemInst: OUT STD_LOGIC;
MemWr: OUT STD_LOGIC;
Asel: OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: OUT STD_LOGIC;
Sub: OUT STD_LOGIC;
-- status signals
IR: IN STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: IN STD_LOGIC;
Apos: IN STD_LOGIC;
-- control outputs
Halt: OUT STD_LOGIC);
END cu;
ARCHITECTURE FSM OF cu IS
TYPE state_type IS (s_start,s_fetch,s_decode,
s_load,s_store,s_add,s_sub,s_in,s_jz,s_jpos,s_halt);
SIGNAL state: state_type;
BEGIN
next_state_logic: PROCESS(Reset, Clock)
BEGIN
IF(Reset = '1') THEN
state <= s_start;
ELSIF(Clock'EVENT AND Clock = '1') THEN
CASE state IS
WHEN s_start => -- reset
state <= s_fetch;
WHEN s_fetch =>
state <= s_decode;
WHEN s_decode =>
CASE IR IS
WHEN "000" => state <= s_load;
WHEN "001" => state <= s_store;
WHEN "010" => state <= s_add;
WHEN "011" => state <= s_sub;
WHEN "100" => state <= s_in;
WHEN "101" => state <= s_jz;
WHEN "110" => state <= s_jpos;
WHEN "111" => state <= s_halt;
WHEN OTHERS => state <= s_halt;
END CASE;
WHEN s_load =>
state <= s_start;
WHEN s_store =>
state <= s_start;
WHEN s_add =>
state <= s_start;
WHEN s_sub =>
state <= s_start;
WHEN s_in =>
IF (Enter = '0') THEN -- wait for the Enter key for inputs
state <= s_in;
ELSE
state <= s_start;
END IF;
WHEN s_jz =>
state <= s_start;
WHEN s_jpos =>
state <= s_start;
WHEN s_halt =>
state <= s_halt;
WHEN OTHERS =>
state <= s_start;
END CASE;
END IF;
END PROCESS;
output_logic: PROCESS(state)
BEGIN
CASE state IS
WHEN s_fetch =>
IRload <= '1'; -- load IR
JMPmux <= '0';
PCload <= '1'; -- increment PC
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_decode => -- also set up for memory access
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1'; -- pass IR address to memory
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_load =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1';
MemWr <= '0';
Asel <= "10";-- pass memory to A
Aload <= '1'; -- load A
Sub <= '0';
Halt <= '0';
WHEN s_store =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1'; -- pass IR address to memory
MemWr <= '1'; -- store A to memory
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_add =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1';
MemWr <= '0';
Asel <= "00";-- pass add/sub unit to A
Aload <= '1'; -- load A
Sub <= '0'; -- select add
Halt <= '0';
WHEN s_sub =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '1';
MemWr <= '0';
Asel <= "00";-- pass add/sub unit to A
Aload <= '1'; -- load A
Sub <= '1'; -- select subtract
Halt <= '0';
WHEN s_in =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '0';
MemWr <= '0';
Asel <= "01";-- pass input to A
Aload <= '1'; -- load A
Sub <= '0';
Halt <= '0';
WHEN s_jz =>
IRload <= '0';
JMPmux <= '1'; -- pass IR address to PC
PCload <= Aeq0; -- load PC if condition is true
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_jpos =>
IRload <= '0';
JMPmux <= '1'; -- pass IR address to PC
PCload <= Apos; -- load PC if condition is true
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
WHEN s_halt =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '1';
WHEN OTHERS =>
IRload <= '0';
JMPmux <= '0';
PCload <= '0';
Meminst <= '0';
MemWr <= '0';
Asel <= "00";
Aload <= '0';
Sub <= '0';
Halt <= '0';
END CASE;
END PROCESS;
END FSM;
======================================================================
VHDL code for the datapath of the EC-2.

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
-- for memory
USE work.lpm_components.ALL;

ENTITY dp IS
PORT (
Clock: IN STD_LOGIC;
Clear: IN STD_LOGIC;
-- datapath input
Input: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- control signals
IRload: IN STD_LOGIC;
JMPmux: IN STD_LOGIC;
PCload: IN STD_LOGIC;
MemInst: IN STD_LOGIC;
MemWr: IN STD_LOGIC;
ASel: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
Aload: IN STD_LOGIC;
Sub: IN STD_LOGIC;
-- status signals
IR: OUT STD_LOGIC_VECTOR(7 DOWNTO 5);
Aeq0: OUT STD_LOGIC;
Apos: OUT STD_LOGIC;
-- datapath output
Output: OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END dp;
ARCHITECTURE Structural OF dp IS

COMPONENT reg
GENERIC (size: INTEGER := 4); -- the actual size is defined in the
instantiation GENERIC MAP
PORT (
Clock, Clear, Load: IN STD_LOGIC;
D: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0);
Q: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0));
END COMPONENT;

COMPONENT increment
GENERIC (size: INTEGER := 8); -- default number of bits
PORT (
A: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0);
F: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0));
END COMPONENT;

COMPONENT mux2
GENERIC (size: INTEGER := 8); -- default size
PORT (
S: IN STD_LOGIC; -- select line
D1, D0: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0); -- data bus input
Y: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0)); -- data bus output
END COMPONENT;

COMPONENT mux4
GENERIC (size: INTEGER := 8); -- default size
PORT (
S: IN STD_LOGIC_VECTOR(1 DOWNTO 0); -- select line
D3, D2, D1, D0: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0); -- data bus
input
Y: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0)); -- data bus output
END COMPONENT;

COMPONENT addsub
GENERIC(n: INTEGER :=4); -- default number of bits = 4
PORT(S: IN std_logic; -- select subtract signal
A: IN std_logic_vector(n-1 DOWNTO 0);
B: IN std_logic_vector(n-1 DOWNTO 0);
F: OUT std_logic_vector(n-1 DOWNTO 0);
unsigned_overflow: OUT std_logic;
signed_overflow: OUT std_logic);
END COMPONENT;
SIGNAL dp_IR, dp_RAMQ: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL dp_JMPmux, dp_PC, dp_increment, dp_meminst: STD_LOGIC_VECTOR(4
DOWNTO 0);
SIGNAL dp_Amux, dp_addsub, dp_A: STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
-- doing structural modeling for the datapath here
-- IR
U0: reg GENERIC MAP(8) PORT MAP(Clock, Clear, IRLoad, dp_RAMQ, dp_IR);
IR <= dp_IR(7 DOWNTO 5);
-- JMPmux
U1: mux2 GENERIC MAP(5) PORT MAP(JMPmux,dp_IR(4 DOWNTO 0),
dp_increment,dp_JMPmux);
-- PC
U2: reg GENERIC MAP(5) PORT MAP(Clock, Clear, PCLoad, dp_JMPmux,
dp_PC);
-- Meminst
U3: mux2 GENERIC MAP(5) PORT MAP(Meminst,dp_IR(4 DOWNTO 0),
dp_PC,dp_meminst);
-- increment
U4: increment GENERIC MAP(5) PORT MAP(dp_PC,dp_increment);
-- memory
U5: lpm_ram_dq
GENERIC MAP (
lpm_widthad => 5,
lpm_outdata => "UNREGISTERED",
-- lpm_indata => "UNREGISTERED",
-- lpm_address_control => "UNREGISTERED",
lpm_file => "program.mif",-- fill ram with content of file program.mif
lpm_width => 8)
PORT MAP (
data => dp_A,
address => dp_meminst,
we => MemWr,
inclock => Clock,
q => dp_RAMQ);
-- A input mux
U6: mux4 GENERIC MAP(8) PORT MAP (
Asel,dp_RAMQ,dp_RAMQ,Input,dp_addsub,dp_Amux);
-- Accumulator
U7: reg GENERIC MAP(8) PORT MAP(Clock, Clear, ALoad, dp_Amux, dp_A);
-- Adder-subtractor
U8: addsub GENERIC MAP(8) PORT
MAP(Sub,dp_A,dp_RAMQ,dp_addsub,open,open);
Aeq0 <= '1' WHEN dp_A = "00000000" ELSE '0';
Apos <= NOT dp_A(7);
Output <= dp_A;
END Structural;

======================================================================

Since this is clearly a homework problem, I will give you a few
pointers. I wrote my own 8-bit CPU for fun a couple of years ago, and
went through the same thing.

1) Break it down into maneagable pieces. Test the ALU, Interrupt logic
(if it exists), data path, address generation, instruction decoding
etc. by itself until you are satisfied each piece works. Then start
tying the pieces together and merge the smaller test benches. I spent
a good two months debugging the instruction decode logic of my CPU,
only to have to do it again when I realized I needed to pipeline
everything for speed. I probably spent a few good weeks on the ALU (as
simple as it was) tracking down problem with the status register flags
not working correctly. Attempting to fix some of these issues in a
merged system would have been greatly more difficult.

2) Don't be afraid to break out a few sheets of graph paper and a
pencil. I have several sheets that I used to create timing diagrams of
the microcode. I probably penned a couple dozen diagrams of each major
subsystem to understand what I was doing.

My CPU started out without a real pipeline or instruction/data cache
but the performance stank. The round-trip latency for RAM/ROM reads
was ridiculous, and I was getting, at best, ~40MHz in a Cyclone 2c35.
I needed to add the output registers to the block RAM's to cut the
latency in half. This, unfortunately, meant adding a deeper pipeline
to the CPU.

However, I realized, looking at my timing digrams, that I was wasting
time in the ALU, and with a few other instructions, that could be used
to fetch the next instruction or data, so I added the ability to
"prefetch" data and instructions to internal registers while the ALU
was busy. This helped out a lot, because now the pipeline could be
kept busy. Sure, there is a penalty for branching and initial program
load, but once the pipeline is going, most math operations still
complete in a single clock cycle. Then, after looking at the revised
timing diagrams, I realized that I could now "burst" the CPU context
to the stack while preparing to enter an interrupt service routine
with a few changes to the microcode. I can fold in the read requests
while writing the last pieces of the CPU context because I know the
data won't appear on the bus for a few clock cycles. This works out
because the interrupt control logic takes a clock to generate the
vector offset, and another clock to generate the physical vector
address. In that time, I've written the current program counter out to
the stack, and am preparing to write the status register out. The
final state loads the prefetch register before "jumping" to the new
PC. The end result is that there aren't any wasted clock cycles, even
for interrupts.

Now, I have a CPU that synthesizes (by itself) to > 110MHz in a
Cyclone II and has very low latency; all because I took the time to
draw out what was happening on every clock cycle early on, and was
able to see where I could fold in other operations. Even with a
bitwise-OR bus, I still see SoC's built with the CPU running at >
100MHz.

3) When you (think you) are done, instantiate the processor with a ROM
and a RAM model. There are several ROM generators that will spit out
VHDL code, but I wrote my own using a gigantic case statement. I
pieced the instructions together by concatenating an opcode, optional
subopcode, and register number together such that the ROM looked like
an assembly listing.

Here is a piece from my testbench RAM:

ROM_Ptr := x"0000";
for i in 0 to 65535 loop
case ROM_Ptr is
...
-- Test Branching & multi-cycle math
when x"009C" =>
RAM(i) <= OP_XOR & R0; -- Clear R0
when x"009D" =>
RAM(i) <= OP_T0X & R2; -- Clear R3:R2
when x"009E" =>
RAM(i) <= OP_T0X & R3;
when x"009F" =>
RAM(i) <= OP_CLP & R4; -- CLP PSR_GP4
when x"00A0" =>
RAM(i) <= OP_CLP & R5; -- CLP PSR_GP5
when x"00A1" =>
RAM(i) <= OP_LDI & R1; -- LDI R1,#$02
when x"00A2" =>
RAM(i) <= x"02";
when x"00A3" => -- SET_LOOP:
RAM(i) <= OP_BR0 & R4; -- BNGP4
when x"00A4" =>
RAM(i) <= x"03";
when x"00A5" =>
RAM(i) <= OP_LDI & R1; -- LDI R1,#$04
when x"00A6" =>
RAM(i) <= x"04";
when x"00A7" =>
RAM(i) <= OP_STP & R5; -- STP PSR_GP5
when x"00A8" => -- LOOP:
RAM(i) <= OP_UPP & R2; -- UPP R2
when x"00A9" =>
RAM(i) <= OP_DBNZ & R1; -- DBNZ R1,LOOP
when x"00AA" =>
RAM(i) <= x"FD";
when x"00AB" =>
RAM(i) <= OP_BR1 & R5; -- BRGP5 EXIT
when x"00AC" =>
RAM(i) <= x"04";
when x"00AD" =>
RAM(i) <= OP_STP & R4; -- STP PSR_GP4
...
when others =>
RAM(i) <= OP_TX0 & R0;
end case;
ROM_Ptr := ROM_Ptr + 1;
end loop;

(Note, the "fill" instruction is the equivalent of a NOP)

It's definitely not synthesizeable, but it allowed me to comment each
byte of data in the ROM and make notes. Then, single step your
processor as it accesses the memory and verify each instruction. Then
verify each instruction works correctly when it precedes or follows
every other instruction. Branching is especially fun, but if you have
multicycle instructions, you must verify that you don't screw up if a
branch follows a multi-cycle instruction. If you have interrupts, make
sure you setup a vector table, and verify that you can enter & return
from interrupts properly. (RTI & RTS are similar, and in my case,
share common states, but they use the stack differently)

4) I spent 3x more time debugging than designing. The design actually
went fairly quick, because I chose to implement a RISC architecture
that was already documented, and I had a functional simulator.
Debugging dragged on forever.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top