Skip to content

DevelopersGuidePerl

Adam edited this page Feb 26, 2013 · 5 revisions

Table of Contents

Developers Guide for Perl Users (deprecated)

Attention: This guide applies to the 2.0.0 and above releases.

This guide explains the earlier way of developing for NetFPGA platform using Perl based testing infrastructure for performing regression and simulation tests. This has now been replaced with a new python based unified infrastructure providing single code set for both simulation and hardware testing.

NetFPGA Directory Structure

The directory structure of the NetFPGA source tree is as follows:

   netfpga                {Base directory}
      bin                 {Scripts for simulation/synthesis/register gen}
      bitfiles            {Compiled hardware bitfiles}
      lib                 {Libraries and software tools}
         C                {C libraries/programs}
         java             {Java libraries}
         Makefiles        {Makefile templates used for sim/synth}
         Perl5            {Perl libraries}
         python           {Python libraries}
         release          {XML files for packaging}
         scripts          {Utility scripts}
         verilog
            contributed   {Contributed Verilog modules}
            core          {Official Verilog modules}
         xml              {XML schemas}
      projects            {project directory}
         <project>        {contributed project}
            doc           {documentation}
            include       {project.xml, project specific module XML}
            lib           {Perl and C headers}
            regress       {Regression tests}
            src           {non-library verilog}
            sw            {Project-specific software}
            synth         {Synthesis directory (contains all .xco files)}
            verif         {Simulation tests}

Creating a new project

A new project is created by following these steps:

  1. Create a new project directory inside netfpga/projects
  2. Update the NF_DESIGN_DIR directory to point to the new project directory. Example (Bourne shell syntax): export NF_DESIGN_DIR=$(HOME)/netfpga/projects/my_first_project (Seethe guide for more information.)
  3. Create the following directories inside the project directory: include, regress, src, synth, and verif. Optionally create doc, lib, and sw. (Note: lib will be automatically created when the register generation tool is run.)
  4. Create a project.xml file inside the include directory. See the Register System section below for more information.
  5. Add any library modules to the project.xml file.
  6. Write any project-specific Verilog and place inside the src directory.
  7. Write module-specific XML files for any new modules you have written and place in the include directory.
  8. Write and run simulations to verify your design. Simulations should be placed in the verif directory, and should be of the form test_<major>_<minor>, where major and minor are used to categorize the test. For example, tests that send data could be called: test_send_minlen and test_send_maxlen. (The verification scripts allow subsets of a project's tests to be run by specifying the major and minor.)
  9. Copy the Makefile from the reference router synth directory into your synth directory.
  10. Synthesize the design by running make inside the synth directory.
  11. Write regression tests and place them in the regress directory. Add the regression tests to the tests.txt file in the regress directory. Run with nf_regress_test.pl (See Regression Tests for more information.)
  12. Write any software and place inside the sw directory.
  13. Add documentation to the doc directory.
  14. Contribute your project if you think your project may be useful to others. See the Develop page for more information.
Note: The act of running simulations or synthesizing the project causes the registers to be regenerated. Use the nf_register_gen.pl tool if you want to regenerate registers without running simulation/synthesis.

Build system

The build system manages the process of compiling projects for simulation and synthesizing projects for download to the FPGA.

Build process

The following steps are undertaken during a build:

  1. Query the register system for a list of all modules used by the project.
  2. Instruct the register system to generate the registers. This involves reading the register descriptions for each module, allocating memory for all registers, and outputting that resultant allocation for use in Verilog, C, and Perl.
  3. Generate cores associated with each module and the project.
  4. Compile/synthesize the code associated with each module and the project.

Projects and reusable modules

As previously mentioned, the NetFPGA platform supports reusable hardware modules that can be incorporated into projects. The build system makes using modules extremely simple: a developer simply specifies which modules that they'd like included in their project and the build system allocates addresses for registers within each module, generates any necessary IP blocks, and includes the necessary source files during compilation.

Many new developers do not realize that modules are included as part of the compilation process; this causes confusion to many new developers when they look at the src directory of a project and see very few files, or even none at all. For example, reference router's src directory is completely empty; all of the functionality of the reference router is provided by the reusable modules.

The set of modules that a project uses is specified in the project's project.xml file (see Register System for more information). The nf:use_modules section of the XML files specifies which modules to include. Part of the use_modules section from the reference router is shown below:

<nf:use_modules>
   core/io_queues/cpu_dma_queue
   core/io_queues/ethernet_mac
   core/input_arbiter/rr_input_arbiter
   core/nf2/generic_top
   core/nf2/reference_core   
   ...
</nf:use_modules>
<pre>

This section of code instructs the build system to use these modules: CPU DMA queues, Ethernet MAC, the generic top-level Verilog file ( generic_top), and the reference core. The reusable modules are located in thenetfpga/lib/verilog directory. For example, the CPU DMA queues (specified as core/io_queues/cpu_dma_queue in the above code snippet), is located innetfpga/lib/verilog/core/io_queues/cpu_dma_queue.


=== Shared Makefiles ===


TBD. This section should discuss the shared makefiles.


== Register system ==


This section provides an overview of the register system. Detailed information about the register system can be found on the [[RegisterSystem|Register System]] page.

The register system provides a mechanism for:
* specifying the registers provided by each module
* specifying the modules used by each project
* generating a register map/memory allocation for each project
Information for each project (eg. name, list of modules, location of modules in memory space) and each module (eg. name, list of registers) is specified in an XML file. The register generation tool (=nf_register_gen.pl) reads the XML file of the project and the XML files of the included modules, performs memory allocation, and then outputs a set of files with the memory allocation/register map to files for use in Verilog, C, and Perl.


=== project.xml ===


The project.xml file provides information about the project and specifies the list of shared modules used by the project. A sample XML file is shown below:

<pre>
< ?xml version="1.0" encoding="UTF-8"? >
<nf:project xmlns:nf="http://www.NetFPGA.org/NF2_register_system" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.NetFPGA.org/NF2_register_system NF2_register_system.xsd ">
  <nf:name>Reference router</nf:name>
  <nf:description>Reference IPv4 router</nf:description>
  <nf:version_major>1</nf:version_major>
  <nf:version_minor>0</nf:version_minor>
  <nf:version_revision>0</nf:version_revision>
  <nf:dev_id>0</nf:dev_id>
  <nf:use_modules>
    core/io_queues/cpu_dma_queue
    core/io_queues/ethernet_queue
    contrib/ucsd/gig_eth_mac
    core/input_arbiter/rr_input_arbiter
    ...
  </nf:use_modules>
  <nf:memalloc layout="reference">
    <nf:group name="core1">
      <nf:instance name="device_id" />
      <nf:instance name="dma" base="0x0500000"/>
      <nf:instance name="mdio" />
      <nf:instance name="nf2_mac_grp" count="4" />
      <nf:instance name="cpu_dma_queue" count="4" />
    </nf:group>
    <nf:group name="udp">
      <nf:instance name="in_arb" />
      <nf:instance name="router_op_lut" />
      <nf:instance name="strip_headers" />
      <nf:instance name="output_queues" />
    </nf:group>
  </nf:memalloc>
</nf:project>

Line 1 specifies that this is an XML file.

Line 2 declares that we're working with a project and specifies the XML schema that this file is based on.

Lines 3 -- 8 provide information about the project. Name is a short textual description of the project. Description is a slightly longer description of what the project does. The version major, minor, and revision are used to tag the resultant bitfile; the information is placed in the Device ID module that is instantiated by default in each design. (This module can be queried to identify the currently active bitfile.) The device ID is also incorporated into the Device ID module; currently it serves no purpose other than acting as an opaque identifier.

Lines 9 -- 14 specify the shared modules to use. The modules are listed one per line between nf:use_module tags. Each module is located in the netfpga/lib/verilog directory.

Lines 15 -- 29 specify the memory layout of the design, maps modules to particular regions of the address space, and specifies the number of instances for each module.

Lines 16 and 23 begin groups for memory regions (core1 and udp respectively). udp is the region allocated to the User Data Path; many of the modules written by developers would be instantiated inside the User Data Path. Core1 is a region located outside of the user data path. More information about memory regions can be found in the Register Layout section of the Register System guide.

Line 17 specifies that the device_id module should be instantiated inside the core1 region. The addresses allocated the module will be left entirely to the register system. Note: The name "=device_id=" is the name specified in the module's XML file.

Line 18 specifies that the dma module should be instantiated inside the core1 region. The base attribute specifies that the module should be allocated addresses starting at 0x0500000. Generally developers should leave the address allocation to the register system (it gives the register system more flexibility in it's allocation).

Line 20 specifies that the nf2_mac_grp module should be instantiated inside the core1 region. The count attribute specifies the number of instances of this module to allocate memory for; in this example there are four instances. Each instance requires it's own memory allocation so that the registers for one instance can be uniquely identified from the registers for another instance.

Not shown in the example above is the ability to specify types and constants within the project.xml. These features and more are documented in more detail on the Register System page.

Module XML files

An XML file is provided for each module and specifies the registers, constants, and types used by each module. The XML file should be located in the module's xml directory for shared modules, or the project's include directory for project-specific modules. A sample module XML file is shown below:

< ?xml version="1.0" encoding="UTF-8"? >
<nf:module xmlns:nf="http://www.NetFPGA.org/NF2_register_system" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.NetFPGA.org/NF2_register_system NF2_register_system.xsd ">
	<nf:name>phy_test</nf:name>
	<nf:prefix>phy_test</nf:prefix>
	<nf:location>core</nf:location>
	<nf:description>Ethernet PHY test</nf:description>
	<nf:blocksize>256k</nf:blocksize>
	<nf:registers>
		<nf:register>
			<nf:name>status</nf:name>
			<nf:description>Status of the test</nf:description>
			<nf:type>phy_test_status</nf:type>
		</nf:register>
		<nf:register_group>
			<nf:name>phy</nf:name>
			<nf:description>Individual PHY registers</nf:description>
			<nf:instances>4</nf:instances>
			<nf:register>
				<nf:name>tx_status</nf:name>
				<nf:description>Transmit status</nf:description>
				<nf:type>phy_test_phy_tx_status</nf:type>
			</nf:register>
		</nf:register_group>
	</nf:registers>
	<nf:constants>
		<nf:constant>
			<nf:name>num_patterns</nf:name>
			<nf:description>Number of different test patterns</nf:description>
			<nf:value>5</nf:value>
		</nf:constant>
	</nf:constants>
	<nf:types>		
		<nf:type xsi:type="nf:SimpleType">
			<nf:name>phy_test_status</nf:name>
			<nf:description>Ethernet PHY test status</nf:description>
			<nf:width>32</nf:width>
			<nf:bitmask>
				<nf:name>busy</nf:name>
				<nf:description>Test in progress</nf:description>
				<nf:pos>0</nf:pos>
			</nf:bitmask>
			<nf:bitmask>
				<nf:name>done</nf:name>
				<nf:description>Test completed</nf:description>
				<nf:pos>4</nf:pos>
			</nf:bitmask>
			<nf:bitmask>
				<nf:name>good</nf:name>
				<nf:description>Test successful</nf:description>
				<nf:pos>8</nf:pos>
			</nf:bitmask>
		</nf:type>
	</nf:types>
</nf:module>

Line 1 specifies that this is an XML file.

Line 2 declares that we're working with a project and specifies the XML schema that this file is based on.

Lines 3 and 6 specify the name of the module and a description. The name is used in the project.xml file when specifying the module instances in each memory region.

Line 4 specifies the prefix to attach to register names in the Verilog, C and Perl files. In this example, the name and prefix are identical but they do not need to be.

Line 5 specifies which memory region the module can be instantiated in. Valid locations are "core", "udp", and "cpci". The location is checked when a module is instantiated in the project.xml file to ensure that the module is being instantiated in the correct memory region.

Line 7 specifies the block size of the module; this is the size of the address space allocated to that module, the maximum number of registers that can be instantiated is the block size divided by four (each register is 32 bits/4 bytes wide). Common sizes for udp blocks are 64, 256, and 1024; the only size supported for core blocks is 256k. See Register System for more informaiton.

Lines 8 -- 24 specify the registers in the module.

Lines 9 -- 13 specify the first register in the module. In this case, the module is named "status", the description is "Status of the test", and it's declared to be of type phy_test_status. The type is defined later in the XML file, although it is also possible to specify a width instead of a type. The register name generated in Verilog, C, and Perl files is the concatenation of the module prefix and the register name; in this instance the name would be PHY_TEST_STATUS.

Lines 14 -- 23 specify a register group containing a single register. Register Groups provide a mechanism to generate a repeated set of registers; in this case the register group is used to generate one set of registers per physical port.

In this example, the register group is named phy, there are four instances, and each instance contains a single register named tx_status. The register names for register groups differ slightly between the Verilog code and the C/Perl code, largely due to the use cases. The register names produced by this register group are: PHY_TEST_PHY_0_TX_STATUS_REG through PHY_TEST_PHY_3_TX_STATUS_REG. The name is a concatenation of the module's prefix, the register group name, the instance number, and the register name.

Lines 25 -- 31 specify constants.

Lines 26 -- 30 specify a single constant named num_patterns that represents the number of different test patterns. The value of this constant is specified as 5. Constants can be used anywhere that numerical values can used.

Lines 32 -- 53 specify types.

Lines 33 -- 52 specify a single type named phy_test_status. This type represents the status for the Ethernet PHY tests (as explained in description). The width attribute specifies the width of the type in bits. The bitmasks (lines 37 -- 51) specify the purpose of individual bits within the type; as an example bit 0 is named busy and represents whether a test is in progress. You can also experiment with multi-bit fields using the nf:pos_lo and nf:pos_hi xml tags.

Specifying types is not necessary but aids developers working with projects. The type information is incorporated in the Verilog, C, and Perl output to simplify the developers job; for example, the following constants are provided in the C header file produced by the register system:

// Type: phy_test_status
// Description: Ethernet PHY test status
// File: projects/selftest/include/phy_test.xml

// Part 1: bit positions
#define PHY_TEST_STATUS_BUSY_POS   0
#define PHY_TEST_STATUS_DONE_POS   4
#define PHY_TEST_STATUS_GOOD_POS   8

// Part 2: masks/values
#define PHY_TEST_STATUS_BUSY       0x00000001
#define PHY_TEST_STATUS_DONE       0x00000010
#define PHY_TEST_STATUS_GOOD       0x00000100

The above example provides an overview module XML files. More detailed information is found on the Register System page.

Performing register memory allocation

The register system tool to perform register memory allocation is invoked automatically as part of every simulation and synthesis run. The tool can be invoked manually at any time with the nf_register_gen.pl command. Common command line options include:

  • --project <project> -- specify a particular project (instead of using the current project specified via the NF_DESIGN_DIR variable).

Simulations

Simulations assist in verifying the correctness of designs before the time-consuming process of synthesis. As the name implies, simulations simulate the resultant hardware; they are unable to identify every error that may occur in hardware but are extremely useful for identifying logic errors throughout the design process. Designers are encouraged to test as much of their design via simulation as possible. Of course designers must keep in mind that although a simulation can be started very rapidly, the rate at which the simulation runs is much slower than hardware, therefore only fairly short tests can be run on a regular basis as part of a simulation suite.

Writing simulation tests

Simulations tests should be placed in a project's verif directory. Tests names should be of the form test_<major>_<minor>, where major and minor are used to categorize the test. For example, two tests that send minimum and maximum length packets may be called: test_send_minlen and test_send_maxlen. The reason for this naming scheme is to allow a designer to run a subset of the tests; for example the designer may want to run only those tests that send packets; in this case they would specify that all "send" tests should be run.

A directory named test_<major>_<minor> should be created inside the verif directory for each test. Two files, config.txt and make_pkts.pl should be created inside the test directory.

The config.txt file is used to specify a description of the test and the duration of the test in nanoseconds. A sample config.txt file is shown below:

test_desc = Packet generator -- send single packet
finish = 18000

The make_pkts.pl file is a Perl script that generates the data to be input the design under test and specifies the expected output. The simulation infrastructure provides mechanisms to simulate the arrival of packets at the four Ethernet interfaces of the NetFPGA and to simulate register and DMA transactions on the PCI bus.

Several Perl libraries are used by simulations to perform tasks such as creating packets, sending and receiving packets, and performing PCI transactions. The most common Perl simulation libraries include: NF::PacketGen (netfpga/lib/Perl5/NF/PacketGen), NF::PacketLib (netfpga/lib/Perl5/NF/Packet), and SimLib (netfpga/lib/Perl5/SimLib). Functions provided by these libraries include:

  • SimLib:
    • cpu_rxfifo_rd_pkt -- Have the CPU read specified packet from the FPU RxFIFO
    • enable_interrupts
    • prepare_DMA
    • resetDevice
    • PCI_send_pkt -- send in a packet via a PCI DMA transaction
    • PCI_create_and_sent_pkt -- create and send a packet via a PCI DMA transaction
    • make_ethernet_pkt
    • make_IP_pkt
  • PacketGen:
    • nf_PCI_read32 -- read a register and compare to an expected value
    • nf_PCI_read32_masked -- read a register and compare to an expected value (providing a mask for comparison)
    • nf_PCI_write32 -- write a register
    • nf_packet_in -- send a packet in one of the physical Ethernet ports
    • nf_expected_packet -- expect a packet out one of the physical Ethernet ports
    • nf_dma_data_in -- send a packet in via a PCI DMA transaction
    • nf_expected_dma_data -- expect a packet via a PCI DMA transaction
    • nf_write_sim_files -- write the simulation files to disk
  • PacketLib:
    • Contains classes for creating different types of packets, encapsulations, and payloads; including Ethernet, VLAN, IP and ARP

Running simulations

Simulations are run via the nf_run_test.pl command. Optional parameters include:

  • --major <string> -- Run only those tests with the given major name.
  • --minor <string> -- Run only those tests with the given minor name.
  • --no_compile -- Don't rebuild the simulation files before running the simulation.
  • --gui -- Invoke the simulator's GUI and allow the user to interactively run the simulation. Default is to run the simulations to completion in non-interactive text mode.

GUI mode

Running simulations in GUI mode allows the user to interact with the simulator while the simulator is running. This allows users to select a set of signals to monitor while the simulation is running, allowing a trace of those signals to be plotted. Consult the documentation for your simulation tool (eg. ModelSim) for more information about monitoring signals and other tasks you can perform.

Developers are likely to fix bugs in their code while running the simulator in GUI mode. Most simulators allow a simulation to be rerun from the beginning to avoid having to quit and restart the simulator. When doing so, the developer needs to recompile the source files for the simulator. To do so, they need to change into the work directory for the project and run make. For example:

   cd $NF_WORK_DIR/&lt;project_name&gt;
   make

Regression Tests

All contributed designs are specified by the set of tests that they pass. In other words, the developer of a reusable design is expected to clearly document the set of tests their design passes (e.g. "IPv4 packets from 64-1500 bytes long"), and supply the set of tests that can be repeated by users of that design. This creates unambiguous specifications of a reusable design, sets clear expectations of how complete (or not) the design is, and instills greater confidence for whoever is reusing the code.

Every function of a module or project must have a regression test associated with it. Each test is defined by the overall goal and the process used to test the function. The NetFPGA package provides Perl modules that allow users to send/receive live packets and read/write registers of the NetFPGA. As an example, the Packet Generator has four regression tests written using the Perl libraries. These tests run live network traffic to verify the ability of the hardware to send PCAP data, utilize the rate limiter, perform iterations, and capture packets. Below is the description of the Packet Generator iteration test.

  • Name
    • test_iterations
  • Description
    • Load and send two PCAP files from nf2c0 and nf2c1 with a specified number of iterations for each. Verify the packet sent counters.
  • Process
    1. Initialize NetFPGA hardware
    2. Load two PCAP files for nf2c0 and nf2c1
    3. Set 10 iterations for nf2c0 and 100 iterations for nf2c1
    4. Run Packet Generator (send traffic)\ 1. Check counters to verify the number of packets sent
Within the Reference Router project there are a total of 16 tests that vary from testing ARP misses to longest prefix match hits. All of the regression tests are documented on the Wiki page associated with each project. Examples of the Reference Router and Packet Generator regression tests can be found here: Reference Router Regression Tests,Packet Generator Regression Tests

Writing regression tests

Regression tests are placed in the project's regress directory. A directory should be created for each test of the form test_<name>. Files are then created within this directory to perform the test; typically the main file is called run orrun.pl. Each test must be added to the project's regress/tests.txt file; this is a text file that lists one tests per line.

Two special directories named global and common are used for setup and teardown scripts. The global directory contains setup and teardown scripts that are executed only once before/after the entire regression suite is run. Thecommon directory contains setup and teardown scripts that are run before/after each regression test is run. The setup and teardown scripts should be named setup and teardown respectively.

Running regression tests

Regression tests are run with the nf_regress_test.pl command. Command line parameters include:

  • --project <project> -- Run the regression tests for a particular project.
  • --map <mapfile> -- Map ports specified in the regression tests to specific physical ports specified in mapfile.

Backend utilities

Common backend utilities that a developer is likely to invoke include:

nf_register_gen.pl : Performs register memory allocation for a particular project. See Performing register memory allocation.
nf_run_tests.pl : Run the simulation tests for the project. See Running simulations.
nf_regress_tests.pl : Run the regression tests for a project. See Running regression tests.
nf_download : Download a bitfile to the NetFPGA
nf_info : Read the device ID info from the active design in the Virtex. Note: This is not installed by default prior to the 2.0.1 release. The source is located in netfpga/lib/C/tools/nf_info
cpci_reprogram.pl : Download the latest CPCI design to the Spartan
Clone this wiki locally