| 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 |