mirror of https://github.com/openXC7/prjxray.git
Add dsp fuzzer creation description
Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
This commit is contained in:
parent
39813be4cb
commit
028aa6173c
|
|
@ -1,10 +1,11 @@
|
|||
Fuzzers
|
||||
=======
|
||||
Fuzzers are things that generate a design, feed it to Vivado, and look at the resulting bitstream to make some conclusion.
|
||||
|
||||
Fuzzers are a set of tests which generate a design, feed it to Vivado, and look at the resulting bitstream to make some conclusion.
|
||||
This is how the contents of the database are generated.
|
||||
|
||||
The general idea behind fuzzers is to pick some element in the device (say a block RAM or IOB) to target.
|
||||
If you picked the IOB (no one is working on that yet), you'd write a design that is implemented in a specific IOB.
|
||||
If you picked the IOB, you'd write a design that is implemented in a specific IOB.
|
||||
Then you'd create a program that creates variations of the design (called specimens) that vary the design parameters, for example, changing the configuration of a single pin.
|
||||
|
||||
A lot of this program is TCL that runs inside Vivado to change the design parameters, because it is a bit faster to load in one Verilog model and use TCL to replicate it with varying inputs instead of having different models and loading them individually.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Database Development Process
|
|||
|
||||
readme
|
||||
contributing
|
||||
new_fuzzer
|
||||
fuzzers/index
|
||||
minitests/index
|
||||
parts
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
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`` dsd a
|
||||
|
||||
#. 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.
|
||||
A lot of this program is TCL that runs inside Vivado to change the design parameters, because it is a bit faster to load in one Verilog model and use TCL to replicate it with varying inputs instead of having different models and loading them individually.
|
||||
|
||||
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
|
||||
Loading…
Reference in New Issue