mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			271 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			271 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
Identifying the root cause of bugs
 | 
						|
==================================
 | 
						|
 | 
						|
This document references Yosys internals and is intended for people interested
 | 
						|
in solving or investigating Yosys bugs.  This also applies if you are using a
 | 
						|
fuzzing tool; fuzzers have a tendency to find many variations of the same bug,
 | 
						|
so identifying the root cause is important for avoiding issue spam.
 | 
						|
 | 
						|
If you're familiar with C/C++, you might try to have a look at the source code
 | 
						|
of the command that's failing.  Even if you can't fix the problem yourself, it
 | 
						|
can be very helpful for anyone else investigating if you're able to identify
 | 
						|
where the issue is arising.
 | 
						|
 | 
						|
 | 
						|
Finding the failing command
 | 
						|
---------------------------
 | 
						|
 | 
						|
Using the ``-L`` flag can help here, allowing you to specify a file to log to,
 | 
						|
such as ``yosys -L out.log -s script.ys``.  Most commands will print a header
 | 
						|
message when they begin; something like ``2.48. Executing HIERARCHY pass
 | 
						|
(managing design hierarchy).``  The last header message will usually be the
 | 
						|
failing command.  There are some commands which don't print a header message, so
 | 
						|
you may want to add ``echo on`` to the start of your script.  The `echo` command
 | 
						|
echoes each command executed, along with any arguments given to it.  For the
 | 
						|
`hierarchy` example above this might be ``yosys> hierarchy -check``.
 | 
						|
 | 
						|
.. note::
 | 
						|
 | 
						|
   It may also be helpful to use the `log` command to add messages which you can
 | 
						|
   then search for either in the terminal or the logfile.  This can be quite
 | 
						|
   useful if your script contains script-passes, like the
 | 
						|
   :doc:`/using_yosys/synthesis/synth`, which call many sub-commands and you're
 | 
						|
   not sure exactly which script-pass is calling the failing command.
 | 
						|
 | 
						|
 | 
						|
Minimizing scripts
 | 
						|
------------------
 | 
						|
 | 
						|
.. warning::
 | 
						|
 | 
						|
   This section is intended as **advanced usage**, and generally not necessary
 | 
						|
   for normal bug reports.
 | 
						|
 | 
						|
If you're using a command line prompt, such as ``yosys -p 'synth_xilinx' -o
 | 
						|
design.json design.v``, consider converting it to a script.  It's generally much
 | 
						|
easier to iterate over changes to a script in a file rather than one on the
 | 
						|
command line, as well as being better for sharing with others.
 | 
						|
 | 
						|
.. code-block:: yoscrypt
 | 
						|
   :caption: example script, ``script.ys``, for prompt ``yosys -p 'synth_xilinx' -o design.json design.v``
 | 
						|
 | 
						|
   read_verilog design.v
 | 
						|
   synth_xilinx
 | 
						|
   write_json design.json
 | 
						|
 | 
						|
Next up you want to remove everything *after* the error occurs.  If your final
 | 
						|
command calls sub-commands, replace it with its contents first.  In the case of
 | 
						|
the :doc:`/using_yosys/synthesis/synth`, as well as certain other script-passes,
 | 
						|
you can use the ``-run`` option to simplify this. For example we can replace
 | 
						|
``synth -top <top> -lut`` with the :ref:`replace_synth`.  The options ``-top
 | 
						|
<top> -lut`` can be provided to each `synth` step, or to just the step(s) where
 | 
						|
it is relevant, as done here.
 | 
						|
 | 
						|
.. code-block:: yoscrypt
 | 
						|
   :caption: example replacement script for `synth` command
 | 
						|
   :name: replace_synth
 | 
						|
 | 
						|
   synth -top <top> -run :coarse
 | 
						|
   synth -lut -run coarse:fine
 | 
						|
   synth -lut -run fine:check
 | 
						|
   synth -run check:
 | 
						|
 | 
						|
Say we ran :ref:`replace_synth` and were able to remove the ``synth -run
 | 
						|
check:`` and still got our error, then we check the log and we see the last
 | 
						|
thing before the error was ``7.2. Executing MEMORY_MAP pass (converting memories
 | 
						|
to logic and flip-flops)``. By checking the output of ``yosys -h synth`` (or the
 | 
						|
`synth` help page) we can see that the `memory_map` pass is called in the
 | 
						|
``fine`` step.  We can then update our script to the following:
 | 
						|
 | 
						|
.. code-block:: yoscrypt
 | 
						|
   :caption: example replacement script for `synth` when `memory_map` is failing
 | 
						|
 | 
						|
   synth -top <top> -run :fine
 | 
						|
   opt -fast -full
 | 
						|
   memory_map
 | 
						|
 | 
						|
By giving `synth` the option ``-run :fine``, we are telling it to run from the
 | 
						|
beginning of the script until the ``fine`` step, where we then give it the exact
 | 
						|
commands to run.  There are some cases where the commands given in the help
 | 
						|
output are not an exact match for what is being run, but are instead a
 | 
						|
simplification.  If you find that replacing the script-pass with its contents
 | 
						|
causes the error to disappear, or change, try calling the script-pass with
 | 
						|
``echo on`` to see exactly what commands are being called and what options are
 | 
						|
used.
 | 
						|
 | 
						|
.. warning::
 | 
						|
 | 
						|
   Before continuing further, *back up your code*.  The following steps can
 | 
						|
   remove context and lead to over-minimizing scripts, hiding underlying issues.
 | 
						|
   Check out :ref:`yosys_internals/extending_yosys/advanced_bugpoint:Why
 | 
						|
   context matters` to learn more.
 | 
						|
 | 
						|
When a problem is occurring many steps into a script, minimizing the design at
 | 
						|
the start of the script isn't always enough to identify the cause of the issue.
 | 
						|
Each extra step of the script can lead to larger sections of the input design
 | 
						|
being needed for the specific problem to be preserved until it causes a crash.
 | 
						|
So to find the smallest possible reproducer it can sometimes be helpful to
 | 
						|
remove commands prior to the failure point.
 | 
						|
 | 
						|
The simplest way to do this is by writing out the design, resetting the current
 | 
						|
state, and reading back the design:
 | 
						|
 | 
						|
.. code-block:: yoscrypt
 | 
						|
 | 
						|
   write_rtlil <design.il>; design -reset; read_rtlil <design.il>;
 | 
						|
 | 
						|
In most cases, this can be inserted immediately before the failing command while
 | 
						|
still producing the error, allowing you to :ref:`minimize your
 | 
						|
RTLIL<using_yosys/bugpoint:minimizing rtlil designs with bugpoint>` with the
 | 
						|
``<design.il>`` output.  For our previous example with `memory_map`, if
 | 
						|
:ref:`map_reset` still gives the same error, then we should now be able to call
 | 
						|
``yosys design.il -p 'memory_map'`` to reproduce it.
 | 
						|
 | 
						|
.. code-block:: yoscrypt
 | 
						|
   :caption: resetting the design immediately before failure
 | 
						|
   :name: map_reset
 | 
						|
 | 
						|
   synth -top <top> -run :fine
 | 
						|
   opt -fast -full
 | 
						|
   write_rtlil design.il; design -reset; read_rtlil design.il;
 | 
						|
   memory_map
 | 
						|
 | 
						|
If that doesn't give the error (or doesn't give the same error), then you should
 | 
						|
try to move the write/reset/read earlier in the script until it does.  If you
 | 
						|
have no idea where exactly you should put the reset, the best way is to use a
 | 
						|
"binary search" type approach, reducing the possible options by half after each
 | 
						|
attempt.
 | 
						|
 | 
						|
.. note::
 | 
						|
 | 
						|
   By default, `write_rtlil` doesn't include platform specific IP blocks and
 | 
						|
   other primitive cell models which are typically loaded with a ``read_verilog
 | 
						|
   -lib`` command at the start of the synthesis script.  You may have to
 | 
						|
   duplicate these commands *after* the call to ``design -reset``.  It is also
 | 
						|
   possible to write out *everything* with ``select =*; write_rtlil -selected
 | 
						|
   <design.il>``.
 | 
						|
 | 
						|
As an example, your script has 16 commands in it before failing on the 17th. If
 | 
						|
resetting immediately before the 17th doesn't reproduce the error, try between
 | 
						|
the 8th and 9th (8 is half of the total 16).  If that produces the error then
 | 
						|
you can remove everything before the `read_rtlil` and try reset again in the
 | 
						|
middle of what's left, making sure to use a different name for the output file
 | 
						|
so that you don't overwrite what you've already got.  If the error isn't
 | 
						|
produced then you need to go earlier still, so in this case you would do between
 | 
						|
the 4th and 5th (4 is half of the previous 8).  Repeat this until you can't
 | 
						|
reduce the remaining commands any further.
 | 
						|
 | 
						|
A more conservative, but more involved, method is to remove or comment out
 | 
						|
commands prior to the failing command.  Each command, or group of commands, can
 | 
						|
be disabled one at a time while checking if the error still occurs, eventually
 | 
						|
giving the smallest subset of commands needed to take the original input through
 | 
						|
to the error.  The difficulty with this method is that depending on your design,
 | 
						|
some commands may be necessary even if they aren't needed to reproduce the
 | 
						|
error.  For example, if your design includes ``process`` blocks, many commands
 | 
						|
will fail unless you run the `proc` command.  While this approach can do a
 | 
						|
better job of maintaining context, it is often easier to *recover* the context
 | 
						|
after the design has been minimized for producing the error.  For more on
 | 
						|
recovering context, checkout
 | 
						|
:ref:`yosys_internals/extending_yosys/advanced_bugpoint:Why context matters`.
 | 
						|
 | 
						|
 | 
						|
Why context matters
 | 
						|
-------------------
 | 
						|
 | 
						|
Sometimes when a command is raising an error, you're seeing a symptom rather
 | 
						|
than the underlying issue.  It's possible that an earlier command may be putting
 | 
						|
the design in an invalid state, which isn't picked up until the error is raised.
 | 
						|
This is particularly true for the pre-packaged
 | 
						|
:doc:`/using_yosys/synthesis/synth`, which rely on a combination of generic and
 | 
						|
architecture specific passes.  As new features are added to Yosys and more
 | 
						|
designs are supported, the types of cells output by a pass can grow and change;
 | 
						|
and sometimes this leads to a mismatch in what a pass is intended to handle.
 | 
						|
 | 
						|
If you minimized your script, and removed commands prior to the failure to get a
 | 
						|
smaller reproducer, try to work backwards and find which commands may have
 | 
						|
contributed to the design failing.  From the minimized design you should have
 | 
						|
some understanding of the cell or cells which are producing the error; but where
 | 
						|
did those cells come from?  The name and/or type of the cell can often point you
 | 
						|
in the right direction:
 | 
						|
 | 
						|
.. code-block::
 | 
						|
 | 
						|
   # internal cell types start with a $
 | 
						|
   # lowercase for word-level, uppercase for bit-level
 | 
						|
   $and
 | 
						|
   $_AND_
 | 
						|
 | 
						|
   # cell types with $__ are typically intermediate cells used in techmapping
 | 
						|
   $__MUL16X16
 | 
						|
 | 
						|
   # cell types without a $ are either user-defined or architecture specific
 | 
						|
   my_module
 | 
						|
   SB_MAC16
 | 
						|
 | 
						|
   # object names might give you the name of the pass that created them
 | 
						|
   $procdff$1204
 | 
						|
   $memory\rom$rdmux[0][0][0]$a$1550
 | 
						|
 | 
						|
   # or even the line number in the Yosys source
 | 
						|
   $auto$muxcover.cc:557:implement_best_cover$2152
 | 
						|
   $auto$alumacc.cc:495:replace_alu$1209
 | 
						|
 | 
						|
Try running the unminimized script and search the log for the names of the
 | 
						|
objects in your minimized design.  In the case of cells you can also search for
 | 
						|
the type of the cell.  Remember that calling `stat` will list all the types of
 | 
						|
cells currently used in the design, and ``select -list =*`` will list the names
 | 
						|
of of all the current objects.  You can add these commands to your script, or
 | 
						|
use an interactive terminal to run each command individually.  Adding them to
 | 
						|
the script can be more repeatable, but if it takes a long time to run to the
 | 
						|
point you're interested in then an interactive shell session can give you more
 | 
						|
flexibility once you reach that point.  You can also add a call to the `shell`
 | 
						|
command at any point in a script to start an interactive session at a given
 | 
						|
point; allowing you to script any preparation steps, then come back once it's
 | 
						|
done.
 | 
						|
 | 
						|
The ``--dump-design`` option
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
Yosys provides the ``--dump-design`` option (or ``-P`` for short) for dumping
 | 
						|
the design at specific steps of the script based on the log header.  If the last
 | 
						|
step before an error is ``7.2. Executing MEMORY_MAP pass (converting memories to
 | 
						|
logic and flip-flops)``, then calling Yosys with ``--dump-design 7.2:bad.il``
 | 
						|
will save the design *before* this command runs, in the file ``bad.il``.
 | 
						|
 | 
						|
It is also possible to use this option multiple times, e.g. ``-P2:hierarchy.il
 | 
						|
-P7 -P7.2:bad.il``, to get multiple dumps in the same run.  This can make it
 | 
						|
easier to follow the design through each step to find where certain cells or
 | 
						|
connections are coming from.  ``--dump-design ALL`` is also allowed, writing out
 | 
						|
the design at each log header.
 | 
						|
 | 
						|
A worked example
 | 
						|
~~~~~~~~~~~~~~~~
 | 
						|
  
 | 
						|
Say you did all the minimization and found that an error in `synth_xilinx`
 | 
						|
occurs when a call to ``techmap -map +/xilinx/cells_map.v`` with
 | 
						|
``MIN_MUX_INPUTS`` defined parses a `$_MUX16_` with all inputs set to ``1'x``.
 | 
						|
You could fix the bug in ``+/xilinx/cells_map.v``, but that might only solve
 | 
						|
this one case while leaving other problems that haven't been found yet.  So you
 | 
						|
step through the original script, calling `stat` after each step to find when
 | 
						|
the `$_MUX16_` is added.
 | 
						|
 | 
						|
You find that the `$_MUX16_` is introduced by a call to `muxcover`, but all the
 | 
						|
inputs are defined, so calling `techmap` now works as expected.  From running
 | 
						|
`bugpoint` with the failing techmap you know that the cell with index ``2297``
 | 
						|
will fail, so you call ``select top/*$2297`` to limit to just that cell.  This
 | 
						|
can then be saved with ``design -save pre_bug`` or ``write_rtlil -selected
 | 
						|
pre_bug.il``, so that you don't have to re-run all the earlier steps to get back
 | 
						|
here.
 | 
						|
 | 
						|
Next you step through the remaining commands and call `dump` after each to find
 | 
						|
when the inputs are disconnected.  You find that ``opt -full`` has optimized
 | 
						|
away portions of the circuit, leading to `opt_expr` setting the undriven mux
 | 
						|
inputs to ``x``, but failing to remove the now unnecessary `$_MUX16_`.  Now
 | 
						|
you've identified a problem in `opt_expr` that affects all of the wide muxes,
 | 
						|
and could happen in any synthesis flow, not just `synth_xilinx`.
 | 
						|
 | 
						|
.. seealso::
 | 
						|
 | 
						|
   This example is taken from `YosysHQ/yosys#4590
 | 
						|
   <https://github.com/YosysHQ/yosys/issues/4590>`_ and can be reproduced with a
 | 
						|
   version of Yosys between 0.45 and 0.51.
 |