Skip to content

Multiple AGWB trees

Wojciech M. Zabolotny edited this page Aug 26, 2021 · 16 revisions

Implementation of multiple slightly different AGWB trees in a single design

The problem described in that page occurs when we need to implement a few control buses with slightly different configuration in the same design.

We have faced that issue during development of the firmware for a huge Xilinx FPGA consisting of two SLRs (Super Logic Regions). The design had to be implemented in the common VHDL project, but used two separate Wishbone buses driven by two separate PCIe cores. Certain blocks had to be implemented only in one SLR. Also the amount of various resources should depend on the SLR.

Similar situation may occur also in other scenarios where multiple interfaces may be used to control similar subsystems implemented in the same FPGA. Therefore it was necessary to develop a suitable version of AGWB.

The proposed solution is based on the following assumptions:

  • Mutiple AGWB designs are similar in the sense, that the AGWB-related differences are only of the following kind:
    • certain blocks are implemented only in one design,
    • the length of certain vectors of blocks or registers may depend on the number of SLR,
  • The AGWB-generated VHDL code must be placed in the user-generated logic. We assume that the user should be able to reuse the same code for both SLRs. Therefore, the possible customizations should be handled with generics and "if generate".

The simplest and viable solution is to allocate the address space based on maximum requirements (all sub-blocks included, the maximum length of all vectors). Then the customization can be performed only in the user logic. The user may simply not connect certain WB-buses, and not connect certain control and status registers.

However, for reliable, testable, and maintainable design we need a few more requirements:

  • The generated design should not waste resources
    • The unconnected sub-block WB-buses won't increase resource consumption.
    • The unused status registers will be optimized out by the synthesis tools.
    • The unused control registers will consume resources. They are implemented R/W, so they are a form of storage. Even if they are not connected to the user logic, they will be still implemented.
  • The attempt to access a non-implemented resource should generate an error.
    • In case of sub-block buses it is sufficient to set the default value in the slave-to-master part of WB bus with asserted "ERR" signal. If the user does not connect this sub-bus, any access to it will generate the bus error.
    • In case of vectors of blocks and registers, AGWB should provide the generics describing their length. The default value of the generic will be the maximum length of the vector. When instantiating the generated block the user may set this generic to the lower (checked by an assertion) value describing the reduced length.

The above ensures correct generation of the parametrisable VHDL code. Another question is how to generate the address map that contains only the existing resources (not done yet).

The simplest solution is that we define "VARIANTS" and all variant-dependent resources have the number of repetitions defined for each variant separately (e.g.: reps="10;5"). Because the force_vec attribute was removed, the availability of a single block or register in different variants must be specified with another attribute: (e.g.: used="0;1" defines that the object is used only in the second variant).

The generated VHDL code uses the following data describing possible variants, for object XOBJ:

  • The g_XOBJ_size - the integer generic describing the size of the object in the current instance.
  • The c_XOBJ_size - the integer constant, describing the maximum size of the object, used for allocation of the address space.
  • The v_XOBJ_size - the constant array of integers, describing the size of the object of possible variants.

Please note, that the generated VHDL code may be customized without using the variants. For example, one may implement a vector of 5 blocks, where in the first 4 instances vector of registers "REGS" has a size of 5, and in the last one has the size of 4 registers. Of course that requires setting the generics accordingly, using the for-generate and if-generate statements. Also the software must know the size of objects (or it may probe it, as an attempt to access an non-existing object returns the WB error.

The first working version that supports variants for the new target (address map XML - AMAP XML) is implemented. This format is similar to the IPbus XML, but it does not generate separate nodes for all elements of vectors. Instead it adds two attributes to the vectors:

  • nelems - number of elements
  • elemoffs - offset of the next element (it may be bigger than the size of the element, due to the address alignment)

The essential facts:

  • The variants are defined by defining different numbers of repetitions of the object, separated with semicolon. E.g., <creg name="ENABLEs" desc="Link enable registers" reps="10;5" default="0x0"/>
  • If certain register or block should be used only in certain variant, then it should be described with the attribute used, that behaves similarly to reps but may have only 0 or 1 values. E.g., <creg name="CTRL" desc="Control register" stb="1" used="1;0">
  • The attribute reps enforces implementation of an object as a part of the vector (hence obsoleting the old force_vec attribute). That's why reps and used are available.
  • If any object uses variants, the number of variants must be the same! Otherwise the error is detected.
  • The AMAP XML target is activated with --amapxml=/output/path option.
    • The generated files have names like: agwb_BLOCK_amap_v0.xml (the number after v is the number of the variant, starting from 0). If there are no variants, only one agwb_BLOCK_amap.xml will be generated for the BLOCK.

The implementation generates an additional agwb_pkg.vhd package that is automatically included in the list of generated files for fusesoc.

Please note, that currently only the AMAP XML and Python targets support the variants.

Please note, that the AMAP XML maps assume that blackboxes may also have different variants. If that's not the case, you may simply create symlinks pointing to the same file.

Important remark

If you use variants in your code, you should pass the variant number down the hierarchy, and you should set the version id (VER) to the right value when instantiating the generated AGWB nodes. Otherwise, if your software check the version of the AGWB firmware blocks, you may get strange errors (in fact, if one of the nested blocks is variant-dependent, it MUST receive the right version number, ot you'll get incorrect implementation).

In the example below the entity myblock in the work library is the user logic implementing your block. It receives the variant number as one of generics - nvar. Then it passes it to the associated AGWB node - agwb.myblock. It also passes it to the nested block nestblock.

entity myblock is
  generic (
     nvar : integer
     );
   port (
     rst_n_i   : in  std_logic;
     clk_sys_i : in  std_logic;
     wb_s_in   : in  t_wishbone_slave_in;
     w_s_out  : out t_wishbone_slave_out;
     [...]
     other_port : in std_logic
     );
end entity myblock;

architecture synth of myblock is
   [...]
begin

  myblock_1 : entity agwb.myblock
    generic map (
      g_ver_id => v_myblock_ver_id(nvar)
    )
  port map (
    slave_i => wb_s_in,
    slave_o => wb_s_out,
    [...]
    rst_n_i      => rst_n_i,
    clk_sys_i    => clk_sys_i
    );

  nestblock_1 : entity work.nestblock
    generic map (
      nvar => nvar
      )
    port map (      
      [...]
      rst_n_i      => rst_n_i,
      clk_sys_i    => clk_sys_i
    );
  [...]
end architecture synth;