Jump to Navigation

How to Create a GReasy Block

This section will step through the process of using scripts to generate the necessary files and source code needed to create a GReasy block from HDL.  By following these steps, a user will be able to create a FPGA-based processing module, register it with GNU Radio, and use it as a component in the GNU Radio block library.  The scripts referenced do much of the work for you and are optional, but are highly recommended.  This tutorial will also attempt to explain what these scripts are actually doing and what important files are being generated.  Some important file locations that may be mentioned or referenced in this section are:

  • {INSTALL_ROOT}/GReasy/trunk - the main trunk of the GReasy repository.
  • {INSTALL_ROOT}/GReasy/trunk/hdl - HDL/netlist source for GReasy hardware modules
  • {INSTALL_ROOT}/GReasy/trunk/gnuradio - top level of GNU Radio source code.
  • {INSTALL_ROOT}/GReasy/trunk/gnuradio/*{BLOCK LIBRARY NAME}*/grc - XML representations of GNU Radio blocks
  • {INSTALL_ROOT}/GReasy/trunk/gnuradio/gr-afpga - GNU Radio GReasy block library
  • {INSTALL_ROOT}/GReasy/trunk/tflow or {INSTALL_ROOT}/GReasy/trunk/zflow or {INSTALL_ROOT}/GReasy/trunk/zedflow - top-level of the TFlow precompiled library and the TFlow run-time assembly scripts.  These three different top levels represent the different supported FPGA families.  The folders zedflow and zflow are for the Zed/ZC702 and ZC706 respectively, while the tflow folder is for the Virtex-5.
  • {INSTALL_ROOT}/GReasy/trunk/tflow/modules/lib - module library for TFlow.  Each supported family/device has its own associated TFlow module library.

Creating a Module From HDL

The following section guides a user through the first phase of module creation.  In this phase, the user starts with an HDL representation of their hardware module, then it is compiled with the vendor tools to the bitstream level, while also generating important meta data used by TFlow.  The process is automated; however, understanding the process step-by-step can better inform modular design decisions.    

1. Designing HDL

  1. Start by designing your HDL block.  VHDL or Verilog can be used, yet Verilog is used in this example.  As in GNU Radio, module reuse is an important design principle that should become part of the design process.  Blocks created should be intended for reuse; hence, should follow good design practices, be well documented, and sufficiently flexible.  The declaration of the module should look like the following example:

Verilog Example:

1| module adder{
2|    // --------------------------------------------------
3|    // External interface (visible to the user)
4|    // --------------------------------------------------
5|    input [32:0] A, //data source A
6|    input [32:0] B, //data source B
7|    output reg [32:0] OUT,//data sink
8|    // ---------------------------------------------------
9|    // Internal interfaces - DO NOT MODIFY THE CODE BELOW 
0|    // ---------------------------------------------------
11|    //
12|    // -- system hooks
13|    input clk,  // ADC by default, for DAC use ‘dac_clk’
14|    input rst,  // Active-high synchronous reset
15|    // -- run-time parameter update
16|    input [1:0] param_in,   // optional parameter IF input
17|    output [1:0] param_out, // optional parameter IF output
18| };


Figure 1. AD-FMCOMM1-EBZ board used as an RF
frontend for the GReasy platform.  

  1. Provided are some guidelines for HDL development that should be followed to ensure the best performance from the tools (tFlow and GReasy).
    1. All GReasy modules are assumed to be synchronous, thus clk and rst lines are required. A GReasy design can have multiple clock domains.  For the ZC706 Zynq design, there are multiple clocking options. By default a clock line named, clk will be connected to the ADC clock. If the clock is instead named dac_clk then the module clock will be connected to the DAC clock. Each of these clocks are driven by the AD FMCOMM1-EBZ board (Figure 1).

    2. No pads or clock division components should be included within a component.  Access to these components should be through the static sandbox ports.
    3. Valid signals are a requirement for interblock data transfer.
    4. Every output and output valid signal must be set to a known state by the active-high reset signal.
    5. Any size bus can be created for any block, but it can then only be connected to other blocks with the same sized bus. GNU Radio automatically assumes bus interfaces are 32 bits of data. If a block has a different sized bus, some further customization may need to be done in GNU Radio, but the functionality of the block in the FPGA will be unaffected.
    6. Following GNU Radio design standards, signals between user created modules are standard data types: 32-bit I/Q data (16 I, 16 Q) and 32 bit integer data.  Not all of these wires need to be used within a design.
    7. Module I/O buses must have a valid bit at bus index 0. Therefore, if the bus provides 32 bits of data and a valid bit, the bus must be 33 bits wide.   This bit is true when the data on the subsequent bits represent valid data.
    8. All module outputs should be registered (see the example declaration above).
Example Registered Output (Verilog):
1|  always @(posedge clk) begin
2|     if(rst) begin     //synchronous reset
3|       OUT[32:1] = 0;  //reset the data
4|       OUT[0] = 0;     //reset the valid signal
5|     end else begin
6|       if(A[0] && B[0]) begin  //A and B are valid
7|         OUT[32:1] = A[32:1] + B[32:1];//no error checking
8|         OUT[0] = 1;  //OUT is valid data after this clock
9|       end else begin
10|        OUT[0] = 0; //OUT is invalid if no addition
11|       end
12|     end
13|    end
  1. If the radio block being added to GReasy is not generated from HDL but instead from IP cores, simply create an HDL wrapper that follows the given guidelines. Also See Making A GReasy Block using System Generator
  2. (OPTIONAL) It may be desirable to have certain portions of the component be run-time configurable.  Parameterized modules require a 2-bit parameter in and 2-bit parameter out buses. A module can have multiple parameters.  For each parameter in a hardware block, a submodule can be placed inside a module to serve as register storage of the parameter value. There is a standard parameter module given below used for this storage given below. These submodules can be chained together within a module. Then, in the TFlow assembly stage during flowgraph execution time, parameter data is piped in that order.
Verilog Example:  
1|  module parameter_module(
2|    input             clk,
3|    input             rst,
4|    output reg  [1:0]  param_out, //1-bit data, valid
5|    input       [1:0]  param_in, //1-bit data, valid
6|    output reg [31:0]  param_data, //param stored
7|    output reg         param_data_received,
8|    output reg  [5:0]  param_counter
9|  );
11|always @(posedge clk) begin
12|  if(rst) begin
13|    param_data <= 32'b0;
14|    param_data_received <= 1'b0;
15|    param_counter <= 6'b0;
16|    param_out <= 2'b0;
17|  end else if(param_in[0]) begin
19|    if (param_counter < 32) begin  //data is valid
20|      param_data <= {param_data[30:0], param_in[1]};
21|      param_counter <= param_counter + 1;
22|    end
23|    if (param_counter > 31) begin// once 32 bits have been
24|       param_data_received <= 1; // received this module
25|       param_out <= param_in;    // has its param data
26|    end
27| end else begin //reset so new param pkts can be received
29|    param_data_received <= 0;
30|    param_out <= 0;
31| end


2. Create the TFlow Module

To use an FPGA block in GReasy, it first must be synthesized and registered.  The HDL needs to be synthesized and compiled, and registered into the TFlow module library.  For each supported FPGA device, TFlow has a different directory for registered modules.  

Terminal Output of Makefile_Module script.

Figure 2. Terminal output of the first stage of the Makefile_Module script

  1. Navigate to the {INSTALL_DIR}/GReasy/trunk/ directory and run:
make -f Makefile_Module TOP=”” 

as seen in Figure 10.

  1. There are some other options in that Makefile as well such as:
    As a reminder, be sure that the Xilinx tools settings are established by the appropriate Xilinx startup script, and TORC/tflow is in the search PATH. If they aren’t, then the script will return a variety of errors and will fail. The script “source path_info” sets up these sources. If the paths change, or a different computer is used, update the path_info file accordingly.
    1. HDL_DIR : The HDL directory by default set for the Zynq, ./hdl/zynq/moduels
    2. TFLOW_DIR : The TFlow library and metadata directory by default is set for Zynq ZC7045, ./zflow/modules
    3. TOLERANCE : Adjusts the size area allocation of a TFlow module. TFlow allocates an area for a module and does resource estimation to determine that area. Occasionally it underestimates or does not account for the routing resources properly. If routing takes an extra long time or mapping actual FPGA resources fails, then this option can be set to some whole value greater than 0 which represents the percentage more area to be allocated. Example: TOLERANCE=50 provides 50% more area for the block.
    4. CLK : clock line in the module, default “clk”. If the clock line for the module has a different name, then mark that here. Other options include: dac_clk for the Zynq.
  2. As a reminder, be sure that the Xilinx tools settings are established by the appropriate Xilinx startup script, and TORC/tflow is in the search PATH. If they aren’t, then the script will return a variety of errors and will fail. The script “source path_info” sets up these sources. If the paths change, or a different computer is used, update the path_info file accordingly.
  3. This process has been automated if the block was built in the repository structure. The block source code should be in {INSTALL_DIR}/Greasy/trunk/hdl/zynq/modules/NAME_OF_BLOCK/src/. The top level block of the module should match the same NAME_OF_BLOCK and be placed in the src directory.
  4. The final output of this script, if run successfully should look like Figure 11. Various files are generated from the script including a module bitstream and an EDIF. The edif is automatically copied to gnuradio/ndfs for the next step of the precompilation process.

Terminal Output of Makefile_Module script.

Figure 3. Terminal output of the last stage of the Makefile_Module script

3. Create an AFPGA GReasy Block

Each hardware module generated in TFlow has a GNU Radio block representation in the GNU Radio gr-afpga out-of-tree (meaning not in the GNU Radio source  module library. 

  1. After “registering” a new block with TFlow, a newly generated EDIF representation of the module is automatically be copied to gnuradio/gr-afpga/ndfs source directory. Check that directory to verify the EDIF is there, with the name [NAME_OF_MODULE].ndf
  2. From the GNURadio GReasy block library folder, run the script ./gr_easy_modtool. This script is an extension of the new modtool utility provided by GNU Radio. It generates C/C++ source, SWIG/Python wrappers, and XML for all the blocks. Running this script entails the registration process for "linking" a TFlow hardware module with a GNU Radio representation of the module. Below are some useful options in this script (these options will also come up, running the program with "-help") :
-f [filename of edif]
-d [device family, examples include v5, zynq, kintex7, etc]
-p [# of parameters]
-c [ clk line name (default clk)]
-r [rst line name (default rst)]
-u [name of device]_[name of module]
-help (brings up this menu)
Some examples of running the script are given below.
./gr_easy_modtool -f passthrough.ndf -d zynq
./gr_easy_modtool -f tuner.ndf -p 2
./gr_easy_modtool -f filter_tap.ndf -p 4
./gr_easy_modtool -u zynq_passthrough
  1. If the EDIF does not exist in the ndfs folder located at {INSTALL_DIR}/GReasy/trunk/gnuradio/gr-afpga/ndfs, an error message will be produced.
  2. If the user intends to input any number of parameters associated with the module, the registration script will query the user asking for variable names for each of those parameters and the default values. All parameters at this time are 32 bit integers. The order of the parameters defined here should be in the same order that the parameters are set within the module, as shown in figure 4.
  3. The script will ask for an instance name. This is only used in GRC to create blocks and needs to exist but does not needany special name.  Generally it is just a capitalized acronym or shortened name of the module.\

Parameter Properties, AFPGA tuner block

Figure 4. Example of how AFPGA processing block parameters are exposed in GRC and the Documentation of the block. This is similar to a typical software based GR block.

  1. The script will ask what the module does, documentation. This information is added to the help section of the block so that users can understand it’s purpose, also shown in Figure 4.  This can later be changed by navigating to the XML representation of the block used by GRC and finding the documentation tag near the bottom of the XML source. These XML files are located in {INSTALL_DIR}/GReasy/trunk/gnuradio/gr-afpga/grc/
  2. The script will also query the user about generating some Quality Assurance code. This is not necessary for the GReasy modules since the data processing does not occur in software.
  3. After the module is registered with the registration script, navigate to the build directory in this same gr-afpga directory you should already be in. Then make your build with make and sudo make install. If registering multiple modules at once, the user can run the registration script for each of those modules and then rebuild once all the modules are registered.

4. Removing an AFPGA GReasy Block

If a module needs to be removed, the same gr_easy_modtool can be used but use the "-u" command option with the device library and module name defined after the flag. An example is provided above with the other examples of running the gr_easy_modtool script. It's really that simple.


Main menu 2

Book | by Dr. Radut