| Adding New Fuzzer |
| ================= |
| |
| This chapter describes how to create a new fuzzer using a DSP as an example target primitive. |
| The files that are generated with such fuzzer have been described in more detail in the :doc:`Database<../dev_database/index>` chapter. |
| The process of creating a new fuzzer consists of two elements, namely base address calculation and feature fuzzing. |
| |
| Base Address Calculation |
| ------------------------ |
| |
| The base address calculation is based on segmatching (statistical |
| constraint solver) the base addresses. A similar technique is used in |
| most fuzzers for solving configuration bits. |
| |
| Methodology |
| +++++++++++ |
| |
| In this technique all IP blocks are changed in parallel. This means that |
| log(N, 2) bitstreams are required instead of N to get the same number of |
| base addresses. However, as part of this conversion, address propagation |
| is also generally discouraged. So it is also recommended to toggle bits |
| in all IP blocks in a column, not just one. In the CLB case, this means |
| that every single CLB tile gets one bit set to a random value. If there |
| are 4 CLB CMT columns in the ROI, this means we'd randomly set 4 * 50 |
| bits in every bitstream. With 200 bits, it takes minimum floor(log(200, |
| 2)) => 8 bitstreams (specimens) to solve all of them. |
| |
| Calculating the base address |
| ++++++++++++++++++++++++++++ |
| |
| #. Find a tilegrid fuzzer to copy, e.g. "dsp" |
| |
| #. Enter your copied directory |
| |
| #. Edit `top.py` |
| |
| a. Refer to the `Xilinx 7 Series Library guide <https://www.xilinx.com/support/documentation/sw_manuals/xilinx2012_2/ug953-vivado-7series-libraries.pdf>`_ and/or Vivado layout to understand the primitive you need to instantiate |
| |
| b. Find a single bit parameter that can be easily toggled, such as a clock inverter or a bulk configuration bit |
| |
| c. Find the correct site type in gen_sites() |
| |
| d. Instantiate the correct verilog library macro in top |
| |
| e. LOC it, if necessary. It's necessary to LOC it if there is more than one |
| |
| #. Run make, and look at Vivado's output. Especially if you took shortcuts instantiating your macro (ex: not connecting critical ports) you may need to add DRC waivers to generate.tcl |
| |
| #. Inspect the ``build/segbits_tilegrid.tdb`` to observe bit addresses, for example ``DSP_L_X22Y0.DWORD:0.DFRAME:1b 0040171B_000_01`` |
| |
| #. The ``DFRAME`` etc entries are deltas to convert this feature offset to the base address for the tile |
| |
| #. We will fix them in the subsequent step |
| |
| #. Correct Makefile's ``GENERATE_ARGS`` to make it the section base address instead of a specific bit in that memory region |
| |
| #. Align address to 0x80: 0x0040171B => --dframe 1B to yield a base address of 0x00401700 |
| |
| #. Correct word offset. This is harder since it requires some knowledge of how and where the IP block memory is as a whole |
| |
| i. If there is only one tile of this type in the DSP column: |
| start by assuming it occupies the entire address range. |
| In this step add a delta to make the word offset 0 (--dword 0) and later indicate that it occupies 101 words (all of them) |
| |
| ii. If there are multiple: compare the delta between adjacent tiles to get the pitch. |
| This should give an upper bound on the address size. |
| Make a guess with that in mind and you may have to correct it later when you have better information. |
| |
| #. Align bits to 0: 1 => --dbit 1 |
| |
| #. Run ``make clean && make`` |
| |
| #. Verify ``build/segbits_tilegrid.tdb`` now looks resolved |
| |
| #. Ex: ``DSP_L_X22Y0.DWORD:0.DFRAME:1b 0040171B_000_01`` |
| |
| #. In this case there were several DSP48 sites per DSP column |
| |
| #. Find the number of frames for your tile |
| |
| #. Run ``$XRAY_BLOCKWIDTH build/specimen_001/design.bit`` |
| |
| #. Find the base address you used above i.e. we used ``0x00401700``, so use ``0x00401700: 0x1B`` (0x1C => 28) |
| |
| #. This information is in the part YAML file, but is not as easy to read |
| |
| #. Return to the main tilegrid directory |
| |
| #. Edit ``tilegrid/add_tdb.py`` |
| |
| #. Find ``tdb_fns`` and add an entry for your tile type e.g. ``(dsp/build/segbits_tilegrid.tdb", 28, 10)`` |
| |
| #. This is declared to be 28 frames wide and occupy 10 words per tile in the DSP column |
| |
| #. Run ``make`` in the tilegrid directory |
| |
| #. Look at ``build/tilegrid.json`` |
| |
| #. Observe your base address(es) have been inserted (look for bits ``CLB_IO_CLK`` entry in the ``DSP_L_*`` tiles) |
| |
| Feature Fuzzing |
| --------------- |
| |
| The general idea behind fuzzers is to pick some element in the device (say a block RAM or IOB) to target and write a design that is implemented in a specific element. |
| Next, we need to create variations of the design (called specimens) that vary the design parameters, for example, changing the configuration of a single pin and process them in Vivado in order to obtain the respective bitstreams. |
| Finally, by looking at all the resulting specimens, the information which bits in which frame correspond to a particular choice in the design can be correlated. |
| Looking at the implemented design in Vivado with "Show Routing Resources" turned on is quite helpful in understanding what all choices exist. |
| |
| Fuzzer structure |
| ++++++++++++++++ |
| |
| Typically a fuzzer directory consists of a mixture of makefiles, bash, |
| python and tcl scripts. Many of the scripts are shared among fuzzers and |
| only some of them have to be modified when working on a new fuzzer. |
| |
| - Makefile and a number of sub-makefiles contain various targets that |
| have to be run in order to run the fuzzer and commit the results |
| to the final database. The most important ones are: |
| |
| - *run* - run the fuzzer to generate the netlist, create |
| bitstreams in Vivado, solve the bits and update the final |
| database with the newly calculated results. |
| |
| - *database -* run the fuzzer without updating the final database |
| |
| The changes usually done in the Makefile concern various script |
| parameters, like number of specimen, regular expressions for inclusion |
| or exclusion list of features to be calculated or maximal number of |
| iterations the fuzzer should try to solve the bits for. |
| |
| - *top.py* - Python script used to generate the verilog netlist which |
| will be used by the fuzzer for all Vivado runs. |
| |
| - *generate.tcl -* tcl script used by Vivado to read the base verilog |
| design, if necessary tweak some properties and write out the |
| specimen bitstreams |
| |
| - *generate.py -* Python script that reads the generated bitstream and |
| takes a parameterized description of the design (usually in the |
| form of a csv file) in order to produce a file with information |
| about which features are enabled and which are disabled in a given |
| segment. |
| |
| Creating the fuzzer |
| +++++++++++++++++++ |
| |
| 1. Open the *top.py* script and modify the content of the top module by |
| instantiating a DSP primitive and specifying some parameters. Use |
| LOC and DONT_TOUCH attributes to avoid some design optimization |
| since the netlists are in many cases very artificial. |
| |
| 2. Make sure the *top.py* script generates apart from the top.v |
| netlist, a csv file with the values of parameters used in the |
| generated netlist. |
| |
| 3. Modify the *generate.tcl* script to read the netlist generated in |
| step 1, apply, if necessary, some parameters from the csv file |
| generated in step 2 and write out the bitstream |
| |
| 4. Modify the *generate.py* script to insert the tags, which signify |
| whether a feature is disabled or enabled in a site, based on the |
| csv parameters file generated in step 1 |