mirror of https://github.com/openXC7/prjxray.git
163 lines
7.3 KiB
ReStructuredText
163 lines
7.3 KiB
ReStructuredText
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
|