Phase 1: Creating the FPGA design - 32 bit Full Adder

This design will take two double word (32-bit) values, located in the first two double words in the Register space (byte offset 0x0 and 0x4), and add them together. The sum of the two values will be immediately output to the third double word in the Register space (byte offset 0x8).  The sources for all referenced components are installed with the GXFPGA software package to C:\Program Files\Marvin Test Solutions\GxFpga\Examples\Quartus\Gx3700\ Tutorial_VHDL\source

Components Used

Top-level VHDL file

In order to open the VHDL text editor, click on File menu, and then New the following dialog appears.

Select VHDL File:

New File Dialog Box

Top-level inputs and outputs

The top-level object for this project will be named tutorial_design_top.vhd.  Start by creating module prototype with the proper inputs and outputs.  The inputs and outputs all correspond to pin on the FPGA.

 

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

-- Design Name : GXFPGA VHDL Tutorial

-- Function    : Demonstrates functionality described in the

--          VHDL Tutorial chapter of the GXFPGA User's Guide.

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

 

LIBRARY ieee ;

USE ieee.std_logic_1164.all ;

USE ieee.numeric_std.all ;

 

ENTITY tutorial_design_top IS

    PORT (Addr        : IN    STD_LOGIC_VECTOR(6 downto 2);

       CS             : IN   STD_LOGIC_VECTOR(2 downto 1);

       WrEn, RdEn     : IN   STD_LOGIC;

       PCIClock, PXI10Mhz    : IN      STD_LOGIC;

       FlexIO        : OUT  STD_LOGIC_VECTOR(65 downto 33);

       LREAD_DV       : OUT  STD_LOGIC;

       IRQ            : OUT  STD_LOGIC;

       FDt          : INOUT STD_LOGIC_VECTOR(31 downto 0));

END tutorial_design_top;

 

GXFPGA VHDL Tutorial Prototype

 

The first step is creating the circuitry required to decode the PCI Address when data is to be written from the PC to the FPGA. This circuit will be used in all three functions of this example project. The signals required for PCI Write access will be the PCI Clock, Write Enable, Chip Select 1, and some PCI Address lines. The PCI Address lines 5 to 2 will be fed to a decoder which will generate a 32-bit value, and the result will be ANDed with the Chip Select 1 bit. Each Chip Select bit represents a certain PCI BAR access (GX3700 has two bars, memory and register memories). Bit 1 represents BAR1 of the PCI memory space (bit 2 for BAR2). BAR1 is the general-purpose Control Register BAR for the GX3700. The results of the AND operation will be once again ANDed to the Write Enable PCI signal.

To create the address decoder, we’ll need to model the D Flip-flop (to latch the inputs), the AND gate, and the decoder.  For each module that we add, you should use the New File Dialog to add a Verilog HDL file to create the blank file. When saving, give the file the same name as the module.  The source for the referenced modules follows:

 

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

-- Design Name : and_gate_n

-- Function    : A two input and gate, the first input in n-bit width

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

 

ENTITY and_gate_n IS

GENERIC (n_width : NATURAL := 32);

    PORT (Input1      : IN  STD_LOGIC_VECTOR (n_width-1 downto 0);

          Input2      : IN  STD_LOGIC;

       Output        : OUT STD_LOGIC_VECTOR (n_width-1 downto 0));

END and_gate_n;

 

ARCHITECTURE Behavior OF and_gate_n IS

BEGIN

    PROCESS (Input1, Input2) BEGIN

       IF Input2 = '1' THEN

              Output <= Input1;

       ELSE

              FOR i IN 0 TO n_width-1 LOOP

                      Output(i) <= '0';

              END LOOP;

       END IF;

    END PROCESS;

END Behavior;

and_gate_n.vhd Source

 

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

-- Design Name : d_flipflop_1

-- Function    : A 1-bit D flip-flop

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

 

ENTITY d_flipflop_1 IS

  PORT (D      : IN  STD_LOGIC;

    Clock      : IN  STD_LOGIC;

    Enable     : IN  STD_LOGIC;

    Clearn     : IN  STD_LOGIC;

    Q          : OUT STD_LOGIC);

END d_flipflop_1;

 

ARCHITECTURE Behavior OF d_flipflop_1 IS

BEGIN

    PROCESS(Clock)

    BEGIN 

IF (rising_edge(Clock)) THEN

              IF (Clearn = '1') THEN

                      Q <= '0';

              ELSE

                      IF (Enable = '1') THEN

                             Q <= D;      

                      END IF;

              END IF;

       END IF;

    END PROCESS; 

END Behavior;

d_flipflop_1.vhd Source

 

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

-- Design Name : d_flipflop_n

-- Function    : A n-bit D flip-flop

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

 

ENTITY d_flipflop_n IS

    GENERIC (n_width  : INTEGER RANGE 2 TO 32 := 32 );

    PORT (D           : IN  STD_LOGIC_VECTOR (n_width-1 downto 0);

               Clock : IN  STD_LOGIC;

        Enable        : IN  STD_LOGIC;

       Clearn        : IN  STD_LOGIC;

       Q             : OUT STD_LOGIC_VECTOR (n_width-1 downto 0));

END d_flipflop_n;

 

ARCHITECTURE Behavior OF d_flipflop_n IS

BEGIN

    PROCESS(Clock)

    BEGIN 

       if (rising_edge(Clock)) then

              if (Clearn = '1') then

                             A: FOR i IN 0 TO n_width-1 loop

                             Q(i) <= '0';

                             END LOOP;

              ELSE

                      IF (Enable = '1') THEN

                             Q <= D;      

                      END IF;

              END IF;

       END IF;      

    END PROCESS; 

END Behavior;

d_flipflop_n.vhd Source

 

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

-- Design Name : decoder

-- Function    : An 5 to 32 decoder (non-behavioral)

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

USE ieee.numeric_std.all;

 

entity decoder is

   port(Decoder_In    : IN   STD_LOGIC_VECTOR(4 downto 0);

        Decoder_Out   : OUT STD_LOGIC_VECTOR(31 downto 0));

end decoder;

 

ARCHITECTURE Behavior OF decoder IS

BEGIN

Decoder_Out <="00000000000000000000000000000001" when Decoder_In="00000" else

    "00000000000000000000000000000010" when Decoder_In="00001" else

    "00000000000000000000000000000100" when Decoder_In="00010" else

    "00000000000000000000000000001000" when Decoder_In="00011" else

    "00000000000000000000000000010000" when Decoder_In="00100" else

      

This entity was abbreviated due to its repetitive nature.

 

    "00001000000000000000000000000000" when Decoder_In="11011" else

    "00010000000000000000000000000000" when Decoder_In="11100" else

    "00100000000000000000000000000000" when Decoder_In="11101" else

    "01000000000000000000000000000000" when Decoder_In="11110" else

    "10000000000000000000000000000000" when Decoder_In="11111";

END Behavior; 

decoder.vhd Source

 

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

-- Design Name : and_gate_1

-- Function    : A two input and gate

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

 

ENTITY and_gate_1 IS

    PORT (Input1   : IN  STD_LOGIC;

       Input2     : IN  STD_LOGIC;

       Output     : OUT STD_LOGIC);

END and_gate_1;

 

ARCHITECTURE Behavior OF and_gate_1 IS

BEGIN

    PROCESS (Input1, Input2)

    BEGIN

       IF Input2 = '1' THEN

              Output <= Input1;

       ELSE

              Output <= '0';

       END IF;

    END PROCESS;

END Behavior;

and_gate_1.vhd Source

 

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

-- Design Name : adder

-- Function    : An n-bit full adder

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

 

ENTITY adder IS

    GENERIC(n_width          : NATURAL := 32);

      PORT (DataA, DataB     : IN  STD_LOGIC_VECTOR(n_width-1 downto 0);

       Cin                   : IN  STD_LOGIC;

       Result               : OUT STD_LOGIC_VECTOR(n_width-1 downto 0);

       Cout                  : OUT STD_LOGIC);

END adder;

 

ARCHITECTURE Behavior OF adder IS

BEGIN

    adder: PROCESS (DataA, DataB, Cin)

       variable carry : STD_LOGIC;

       variable isum  : STD_LOGIC_VECTOR(n_width-1 downto 0);

    BEGIN

       carry := Cin;

       for i in 0 to n_width-1 loop

              isum(i) := DataA(i) xor DataB(i) xor carry;

carry   := (DataA(i) and DataB(i)) or (DataA(i) and carry) or (DataB(i) and carry);

      end loop;

      Result <= isum;

      Cout    <= carry;

END PROCESS adder;

END Behavior;

adder.vhd Source

 

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

-- Design Name : or_gate2

-- Function    : A two input or gate

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

 

ENTITY or_gate2 IS

   PORT (Input1  : IN  STD_LOGIC;

       Input2    : IN  STD_LOGIC;

       Output    : OUT STD_LOGIC);

END or_gate2;

 

ARCHITECTURE Behavior OF or_gate2 IS

BEGIN

    Output <= Input1 or Input2;

END Behavior;

or_gate2.vhd Source

 

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

-- Design Name : or_gate4

-- Function    : A four input or gate

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

 

LIBRARY ieee;

USE ieee.std_logic_1164.all;

 

ENTITY or_gate4 IS

    GENERIC (n_width : NATURAL := 32);

       PORT (Input1 : IN  STD_LOGIC_VECTOR (n_width-1 downto 0);

       Input2 : IN  STD_LOGIC_VECTOR (n_width-1 downto 0);

       Input3 : IN  STD_LOGIC_VECTOR (n_width-1 downto 0);

       Input4 : IN  STD_LOGIC_VECTOR (n_width-1 downto 0);

       Output : OUT STD_LOGIC_VECTOR (n_width-1 downto 0));

END or_gate4;

 

ARCHITECTURE Behavior OF or_gate4 IS

BEGIN

    Output <= Input1 or Input2 or Input3 or Input4;

END Behavior;

or_gate4.vhd Source

 

In tutorial_design_top.v, we will now write the code to describe our PCI Address Decoder Circuit.  Latch both the Address and Write Enable lines using the PCI Clock.  Decode the 5-bit Address lines into a 32-bit bus named DecodedAddr.  This decoded bus is ANDed with the FPGA’s CS[1] to define our PCI Address Decoded Select lines.

Additionally, we will define our Write Enable (WE) lines in this code block.  We will use this later, along with Read Enable, to read and write to registers.

 

-- PCI Address Decoder Circuit

inst: decoder         PORT MAP (LatchedAddr, DecodedAddr);

inst2: and_gate_n     GENERIC MAP (32) PORT MAP (DecodedAddr, CS(1), Sel);

inst3: and_gate_n     GENERIC MAP (32) PORT MAP (Sel, LatchedWrEn, WE);

inst23: d_flipflop_1  PORT MAP (WrEn, PCIClock, NC_Ena, NC_Rst, LatchedWrEn);

inst24: d_flipflop_n   GENERIC MAP (5) PORT MAP (Addr, PCIClock, NC_Ena, NC_Rst, LatchedAddr);

PCI Address Decoder Circuit

 

You will notice that we used a few undefined symbols in this last section: nc_ena and nc_rst.  These are placeholders for enable and reset lines that our various components can take advantage of.  For this tutorial, I have chosen not to use enable or reset lines at all so we should add the following code to tutorial_design_top.v to explicit set these wires to always enabled, never reset.

 

SIGNAL NC_Cin, NC_Rst : STD_LOGIC := '0';

SIGNAL Res1, NC_Ena : STD_LOGIC := '1';

 

Now that the PCI address decoder circuit is complete, we can feed the appropriate bits from the WE bus to D Flip Flops that will store data clocked in from the PCI data lines. For example, the first double word in PCI memory (representing the first number to be summed) will be written to a D Flip Flop with it enables line tied to WE[0] (the first bit in the WE bus).  The second double word to be added will be written to another D Flip Flop with it enables line tied to WE[1]. Finally, the PCI Clock signal (33Mhz) will be used as the clock source of the D Flip Flops. Note that each bit of the Sel and WE buses represent a consecutive double word address (bit 0 corresponds with byte 0, bit 1 corresponds with byte 4, bit 2 corresponds with byte 8 etc.)

First we start by creating an extend circuit to deal with any timing issues with the WE signal.  Then we will create some Flip Flops to latch inputs to the adders.  We will use a placeholder named LatchedFDt as the input to the D Flip Flops. Eventually the PCI data lines will drive these inputs.   Wire the outputs of the D Flip Flops to the Adder component.  The output of the adder, Sum, will be used as an output later.

 

-- WE extend circuit - Extend write enable to mitigate timing issues

inst26: d_flipflop_n  GENERIC MAP (32) PORT MAP (WE, PCIClock, NC_Ena, NC_Rst, LatchedWE);

inst27: d_flipflop_n  GENERIC MAP (32) PORT MAP (LatchedWE, PCIClock, NC_Ena, NC_Rst, LatchedWE2);

inst28: d_flipflop_n  GENERIC MAP (32) PORT MAP (LatchedWE2, PCIClock, NC_Ena, NC_Rst, LatchedWE3);

inst30: or_gate4       GENERIC MAP (32) PORT MAP (LatchedWE, LatchedWE2, LatchedWE3, WE, WE_EXT);

 

-- Adder circuit - Latch the addends and include adder

inst4: d_flipflop_n   GENERIC MAP (32) PORT MAP (FDt_LoopBack, PCIClock, WE_EXT(0), NC_Rst, AdderA);

inst5: d_flipflop_n   GENERIC MAP (32) PORT MAP (FDt_LoopBack, PCIClock, WE_EXT(1), NC_Rst, AdderB);

inst7: adder          GENERIC MAP (32) PORT MAP (AdderA, AdderB, NC_Cin, AdderBuff, NC_Cout);

WE Extend Circuit and Adder Circuit

 

Before moving on we must first extend the RdEn signal.  Add the following to the tutorial_design_top.v:

-- RdEn to 2 PCI Circuit

inst1: or_gate2        PORT MAP (RdEn, LatchedRdEn, RdEn_Extend);

inst8: d_flipflop_1   PORT MAP (RdEn, PCIClock, NC_Ena, NC_Rst, LatchedRdEn);

inst12: and_gate_n    GENERIC MAP (32) PORT MAP (Sel, RdEn_Extend, RE);

inst21: d_flipflop_1  PORT MAP (LatchedRdEn, PCIClock, NC_Ena, NC_Rst, LREAD_DV);

      FRdEn to 2 PCI Circuit

 

-- RE extend circuit - Extend read enable to mitigate timing issues

inst18: d_flipflop_n  GENERIC MAP (32) PORT MAP (RE, PCIClock, NC_Ena, NC_Rst, LatchedRE);

inst19: d_flipflop_n  GENERIC MAP (32) PORT MAP (LatchedRE, PCIClock, NC_Ena, NC_Rst, LatchedRE2);

inst20: d_flipflop_n  GENERIC MAP (32) PORT MAP (LatchedRE2, PCIClock, NC_Ena, NC_Rst, LatchedRE3);

inst22: or_gate4       GENERIC MAP (32) PORT MAP (LatchedRE, LatchedRE2, LatchedRE3, RE, RE_EXT);

RE Extend Circuit

 

We also create a Read Data Valid output pin, LREAD_DV. This comes from a D-Flipflop with the PCIClock as an input clock and the RdEn as the input data. The D-Flip Flop also creates our extender for our ReadEnable.

The inputs to the D Flips Flops can now be wired to the PCI data lines (FDt). We need to clean up the FDt signal as is comes back into our circuit by adding the D-FlipFlop.

 

-- Tri-state FDt when not reading registers

FDt <= FDt_out_value when RE_EXT /= X"00000000" else (others => 'Z');

      

process (PCIClock, RE_EXT, AdderA, AdderB, AdderBuff, LPM_CONSTANT, FDt)

begin

    if (RE_EXT(2)='1')

       then FDt_out_value <= AdderBuff;

    elsif (RE_EXT(0)='1')

       then FDt_out_value <= AdderA;

    elsif (RE_EXT(1)='1')

       then FDt_out_value <= AdderB;

    elsif (RE_EXT(31)='1')

       then FDt_out_value <= LPM_CONSTANT;

    end if;

    FDt_in_value <= FDt; --store the input value

end process;

FDt In/Out Signal Assignment

 

Now that the design has been completed, a revision number should be added so that the end user can read it back from the PCI bus at the 32nd register double word location (byte address 0x7C).

Including a revision number constant to the design is a Marvin Test Solutions standard practice that we recommend end users to follow. The revision constant is 32 bits long and is read as a hexadecimal number such as 0x3564A000. The first two digits of the hexadecimal number represent the company, in this case 35 is for Marvin Test Solutions designs. The next two digits are the design specific code, 64 in this case. And the last 4 digits, A000, is the revision of the design.

Add the following to tutorial_design_top.vhd in the section where signals are defined:

 

// Add revision constant

SIGNAL LPM_CONSTANT : STD_LOGIC_VECTOR(31 downto 0) := X"3564A000";

Symbol Properties