Appendix A

VHDL code for 4 bit processor

 

Introduction

 

This is intended to be an introduction to working with fpga technology that does a little more than flash an led on and off as its goal. The software that was used to do this simulation is free from Xilinx(http://www.xilinx.com/ise/logic_design_prod/webpack.htm). The package will need 6 Gigabytes of permanent hard drive space and another 4 Gigabytes to temporarily hold the install package. This project worked on a 500Mhz Pentium 3 with 500 Meg of ram. The Linux version will run 15 to 25 percent faster than XP on the same pc    hardware. If you cannot use the Linux version try to use XP as there is another 10 percent loss in speed with Vista.

 

The kit that was used to test the design is a low cost board (S3Board for $99)from Digilent[1]. It is called the S3board and has many peripherals on board. The are 8 leds, 4 seven segment displays, a serial port, push buttons, a pc style keyboard input and a primative vga output. There are examples of projects using this board on Utube.

There is another Spartan 3E starter Board for $149 that has additional features.

 

This is a single stack processor design. The single stack is used to hold both data and return addresses for branches and jumps. Inherently, a 4 bit processor has only 16 cells of memory storage, so the program size will be very small. The display system, a single seven segment led device is driven by an instruction, “disp” as a convenience for examining the top of the data stack. To see this in a test bench, a port signal, “top” is added to the entity. “Top” can be removed when the fpga is programmed.

 

The simple program pushes a 9 on the stack, and then displays it. To do this a new location on the stack must be allocated and the top of stack pointer “tos” adjusted to point to it. A 5 is then pushed to the stack and displayed. At this point, another cell is allocated and becomes the new “tos”. The old “tos” is now the next on stack or “nos”. The add instruction adds the two values on the stack, stores the result in “nos” which becomes “tos”. The old “tos” will overwritten when another cell is allocated. After the 5 has been added to the 9 the result, a hexadecimal E will be displayed on the seven segment display. A 1 is now pushed onto the stack, overwriting the 9 that was there. A subtract is performed and the result “d” is displayed. The rest of the program is halt instructions.

 

The test bench figure shows this if we look at the “top” waveform. Note that the display occurs one clock after the data has changed. The “seg” data is inverted because it is used with common anode displays which are used to make the “top” signal easier to verify.

 

When the simulation is working, and it is time to try the hardware out, we have to add a single step clock to be able to step through the program. The safest way to do this is with a state machine using two pushbuttons as inputs. The reset will also cause an initial state

change. The two states could be called “clk_lo” and “clock_hi”. We need to write the hardware description of this state machine.

 

If the code were not modified we would not see the changes of the “top” in the seven segment display as the on-board clock is 50 Mhz and the entire program would have completed an the final value is all that we would be able to see. The single step clock will allow us to see the intermediate steps not just the final result.

 

The Xilinx Integrated Software Environment (ISE)[2] will allow you to import this file to save typing. The waveform is generated automatically when you choose testbench waveform in the source menu.

Since VHDL is a modeling language, simulations can be made of objects (a spring, for example) that cannot be synthesized on silicon. The simulation will not know that the model cannot be synthesized.

 

The quickstart manual will guide you through the essential steps and

allow this project to be simulated and synthesized. Note that the program itself is embedded in the code and does not test all the instructions. It cannot test all the instructions as there are only 16 nibbles available for the code. The test other instructions only these 16 lines need to be modified.

 

 

-- Hsu_stack.vhd Edward Falat

-- This can be pasted into a file

 

library IEEE;

use IEEE.STD_LOGIC_1164.all;

-- use IEEE.std_logic.ARITH.all;

-- use ieee.std_logic.unsigned.all;

use ieee.numeric_std.all;

------------------------------------------------------------------------------------------

entity computer is

  port (

    button                                                               : in   bit;                                                                                                                               -- pushbutton

                 clock , reset         : in bit;                                                                                                                                                 -- system clock

                 top                                                                        : out unsigned(3 downto 0):="1111";              -- tb tool

    seg                                                                   : out unsigned(6 downto 0));                                                           -- seven segment dusplay

end computer; -- entity

------------------------------------------------------------------------------------------

architecture stack of computer is

 

subtype int4bit     is integer  range 0 to 15;   -- decimal equivalent of 4 bit binary nums

subtype unsigned4 is unsigned (3 downto 0);            -- unsigned 4 bit binary

subtype unsigned7 is unsigned (6 downto 0);            -- seven bit binary

type memory  is array (int4bit) of unsigned(3 downto 0);

 

------------------------------------------------------------------------------------------

 

function decode_H4b (i:unsigned4)return unsigned7 is

  variable return_value : unsigned7;

begin  -- decode_H4b

    case (i) is

      when "0000" => return_value := "1000000";

      when "0001" => return_value := "1111001";

      when "0010" => return_value := "0100100";

      when "0011" => return_value := "0110000";

      when "0100" => return_value := "0011001";

      when "0101" => return_value := "0010010";

      when "0110" => return_value := "0000010";

      when "0111" => return_value := "1111000";

      when "1000" => return_value := "0000000";

      when "1001" => return_value := "0010000";

      when "1010" => return_value := "0001000";

      when "1011" => return_value := "0000011";

      when "1100" => return_value := "1000110";

      when "1101" => return_value := "0100001";

      when "1110" => return_value := "0000100";

      when "1111" => return_value := "0001110";                    

      When others => return_value := "1111111";

  end case;

return (return_value);

end decode_H4b;

 

---------------------------architecture-----------------------------------------------------

begin  -- stack

---------------------------architecture-----------------------------------------------------

main : process is

---------------------------the opcodes and mnemonics----------------------------------------

  constant add  : unsigned4 := "0000";                          -- simple add no carry

  constant sub  : unsigned4 := "0001";                          -- simple subtract no borrow

  constant andl : unsigned4 := "0010";                          -- logical bit and

  constant orl  : unsigned4 := "0011";                             -- logical bit or

  constant comp : unsigned4 := "0100";                        -- complement

  constant incr : unsigned4 := "0101";                            -- increment

  constant rsr  : unsigned4 := "0110";                             -- logical shift right

  constant asr  : unsigned4 := "0111";                            -- arithmetic shift right

  constant push : unsigned4 := "1000";                         -- add cell to stack,copied from prog

  constant brz  : unsigned4 := "1001";                            -- if value of tos = 0, branch

  constant brn  : unsigned4 := "1010";        -- if mem(tos)(3)='1'->pc<=a3:a0

  constant jmp  : unsigned4 := "1011";        -- pc<=a3:a0

  constant jsr  : unsigned4 := "1100";        -- tos+=1;mem[tos]:=pc;pc:=a3:a0

  constant rts  : unsigned4 := "1101";        -- pc=mem[tos];tos-=1

  constant disp : unsigned4 := "1110";        -- display mem[tos]

  constant halt : unsigned4 := "1111";        -- stop program execution

 

 --------This is the program---------------------------------------------------------------

  constant program : memory := ( push, "1001",                     -- $0:$1  push 9

                                                                              disp,                                      -- $2                       display "9"

                                                                              push, "0101",      -- $3:$4  push 5

                                                                              disp,                                      -- $5                       display "5"

                                                                              add,                                       -- $6                       add

                                                                              disp,                                      -- $7                       disp "E"

                                                                              push, "0001",                     -- $8$9   push 1

                                                                              disp,                                      -- $A                       disp "1"

                                                                              sub,                                       -- #B                       subtract 1 from E

                                                                              disp,                                      -- $C                       display "d"

                                                                              halt,                                       -- $D

                                                                              halt,                                       -- $E

                                                                              halt);                                      -- $F

--------This is the end of program--------------------------------------------------------

 

  variable stack : memory;                                -- the stack where operands ans results are stored

  variable pc,tos : int4bit := 0;            -- program counter, Top Of Stack : pointers

  variable nos : int4bit;                                                       -- Next On Stack : pointer

  variable insn : unsigned4;                                             -- Instruction

  variable opa : unsigned4;                                              -- Temporary Operand register

  variable memaddr : int4bit;

 

-- This is the “clock” that we will have to single step

  begin

    wait until(clock'event and clock='1' and reset='0');

                if button = '1' then pc := 13 ;

                end if; -- if button pressed jumo to halt

    insn := program(pc) mod 16;       -- load instruction register with opcode

    pc := pc+1 mod 16 ;                       -- advance rhe program counter to the nexr opcode

    case (insn) is

 

                ------------------------two operands in, single result out-------------------------

 

                when add => nos := (tos - 1) mod 16; -- since stack grows upward nos is tos -1

                                stack(nos):=(stack(tos)+stack(nos))mod 16; -- add nos to tos

                               tos:=nos; -- move pointer down to sum

 

      when sub => nos:=(tos-1) mod 16 ;

             stack(nos):=(stack(nos) - stack(tos))mod 16;

                               tos:=nos;  -- move pointer down to sum

 

      when andl =>  nos:=(tos-1) mod 16;

             stack(nos):=(stack(nos)and stack(tos))mod 16;

                               tos:=nos;  -- move pointer down to sum

 

      when orl => nos:=tos-1 ;

             stack(nos):=(stack(nos)or stack(tos))mod 16;

                               tos:=nos;  -- move pointer down to sum

 

                               -------------------------single operand operations------------------------                                                                                         

      when comp => stack(tos):=not stack(tos);

 

      when incr => stack(tos):=(stack(tos)+1)mod 16;

 

      when rsr => opa:=stack(tos) ;opa:=opa(0)&opa(3 downto 1);stack(tos):=opa;

 

      when asr => opa:=stack(tos);opa:=opa(3)&opa(3 downto 1);stack(tos):=opa;

 

      when               push => tos:=tos+1;           -- move pointer to next stack location to be written to

                               stack(tos):=program(pc);  -- copy data from program to top of stack memory

                               pc:=pc+1; -- advance pc past data to next opcode stack grow upward

                               ----------------------branch/jump operations---------------------------------

 

      when brz => if(stack(tos)=0)                                      -- branch on zero

                  then

                                               pc:=to_integer(program(pc));          -- if true branch to new location

                                               stack(tos):=to_unsigned(pc + 1,4);-- store next pc location on stack

                  else

                                               pc:=pc+1;                                                             -- else skip to next opcode

                  end if;

 

      when brn => opa:=stack(tos);    -- assume signed number

                               if(opa(3) ='0')

                               then                       -- branch if negative

                                               pc:=to_integer(program(pc));-- if true branch to new location

                                               stack(tos):=to_unsigned(pc + 1,4);-- store next pc location on stack

                               else

                                               pc:=pc+1;                                                             -- else skip to next opcode                 

                                                                                              end if;

 

      when jmp => pc := to_integer(program(pc));

 

      when jsr => tos := tos+1;                                                           -- allocate new nibble on stack

                  stack(tos):=to_unsigned(pc + 1,4);-- store necr pc location on stack

                  pc:=to_integer(program(pc));        -- jump to subroutine at programmed location

      when rts => pc:=to_integer(stack(tos));                  -- restore program counter

                  tos:=tos-1;                                                         -- used for return from jump and branch

 

                ------------miscellaneous----------------------------------------------------                                                                                          

      when disp => seg<=decode_H4b(stack(tos));     -- display hexadecimal as 7 segment

 

      when halt => pc := pc ;                                                               -- stop

 

      when others => pc := pc;                                                           -- stop

 

    end case;

 

                 top <= stack(tos); -- this is only for test bench, not needed for actual processor

 

  end process;

 

end stack;

 

 

 



[1]    http://www.digilentinc.com/Products/Catalog.cfm?Nav1=Products&Nav2=Prog

[2]    toolbox.xilinx.com/docsan/xilinx7/books/docs/qst/qst.pdf