testbench for a microprocessor

Discussion in 'VHDL' started by Mr.X, Mar 26, 2008.

  1. Mr.X

    Mr.X Guest

    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;

    ======================================================================
     
    Mr.X, Mar 26, 2008
    #1
    1. Advertising

  2. On Wed, 26 Mar 2008 12:02:20 -0700 (PDT), "Mr.X" wrote:

    >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

    http://www.MYCOMPANY.com

    The contents of this message may contain personal views which
    are not the views of Doulos Ltd., unless specifically stated.
     
    Jonathan Bromley, Mar 26, 2008
    #2
    1. Advertising

  3. Mr.X wrote:
    > 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
     
    Mike Treseler, Mar 27, 2008
    #3
  4. Mr.X

    Guest

    On Mar 26, 5:33 pm, Mike Treseler <> wrote:
    > Mr.X wrote:
    > > 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


    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.
     
    , Mar 27, 2008
    #4
  5. wrote:

    > 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
     
    Mike Treseler, Mar 27, 2008
    #5
  6. Mr.X

    radarman Guest

    On Mar 26, 2:02 pm, "Mr.X" <> wrote:
    > 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.
     
    radarman, Apr 9, 2008
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Doug Miller
    Replies:
    4
    Views:
    701
    Jonathan Bromley
    Jan 30, 2004
  2. Mike Treseler
    Replies:
    1
    Views:
    703
    Jim Lewis
    Jan 29, 2004
  3. abhishek jain

    VHDL code for a microprocessor

    abhishek jain, Jan 31, 2004, in forum: VHDL
    Replies:
    5
    Views:
    12,166
    Jonathan Bromley
    Feb 4, 2004
  4. Stefan Duenser

    Microprocessor memory

    Stefan Duenser, Jan 6, 2005, in forum: VHDL
    Replies:
    4
    Views:
    1,823
    Stefan Duenser
    Jan 6, 2005
  5. Replies:
    3
    Views:
    1,064
    David Binnie
    Mar 17, 2006
Loading...

Share This Page