mirror of https://github.com/YosysHQ/yosys.git
334 lines
13 KiB
ReStructuredText
334 lines
13 KiB
ReStructuredText
====================================
|
||
010: Converting Verilog to BLIF page
|
||
====================================
|
||
|
||
Installation
|
||
============
|
||
|
||
Yosys written in C++ (using features from C++11) and is tested on modern
|
||
Linux. It should compile fine on most UNIX systems with a C++11
|
||
compiler. The README file contains useful information on building Yosys
|
||
and its prerequisites.
|
||
|
||
Yosys is a large and feature-rich program with a couple of dependencies.
|
||
It is, however, possible to deactivate some of the dependencies in the
|
||
Makefile, resulting in features in Yosys becoming unavailable. When
|
||
problems with building Yosys are encountered, a user who is only
|
||
interested in the features of Yosys that are discussed in this
|
||
Application Note may deactivate TCL, Qt and MiniSAT support in the
|
||
Makefile and may opt against building yosys-abc.
|
||
|
||
This Application Note is based on `Yosys GIT`_ `Rev. e216e0e`_ from 2013-11-23.
|
||
The Verilog sources used for the examples are taken from `yosys-bigsim`_, a
|
||
collection of real-world designs used for regression testing Yosys.
|
||
|
||
.. _Yosys GIT: https://github.com/YosysHQ/yosys
|
||
|
||
.. _Rev. e216e0e: https://github.com/YosysHQ/yosys/tree/e216e0e
|
||
|
||
.. _yosys-bigsim: https://github.com/YosysHQ/yosys-bigsim
|
||
|
||
Getting started
|
||
===============
|
||
|
||
We start our tour with the Navré processor from yosys-bigsim. The `Navré
|
||
processor`_ is an Open Source AVR clone. It is a single module (softusb_navre)
|
||
in a single design file (softusb_navre.v). It also is using only features that
|
||
map nicely to the BLIF format, for example it only uses synchronous resets.
|
||
|
||
.. _Navré processor: http://opencores.org/projects/navre
|
||
|
||
Converting softusb_navre.v to softusb_navre.blif could not be easier:
|
||
|
||
.. code:: sh
|
||
|
||
yosys -o softusb_navre.blif -S softusb_navre.v
|
||
|
||
Behind the scenes Yosys is controlled by synthesis scripts that execute
|
||
commands that operate on Yosys' internal state. For example, the -o
|
||
softusb_navre.blif option just adds the command write_blif
|
||
softusb_navre.blif to the end of the script. Likewise a file on the
|
||
command line – softusb_navre.v in this case – adds the command
|
||
read_verilog softusb_navre.v to the beginning of the synthesis script.
|
||
In both cases the file type is detected from the file extension.
|
||
|
||
Finally the option -S instantiates a built-in default synthesis script.
|
||
Instead of using -S one could also specify the synthesis commands for
|
||
the script on the command line using the -p option, either using
|
||
individual options for each command or by passing one big command string
|
||
with a semicolon-separated list of commands. But in most cases it is
|
||
more convenient to use an actual script file.
|
||
|
||
Using a synthesis script
|
||
========================
|
||
|
||
With a script file we have better control over Yosys. The following
|
||
script file replicates what the command from the last section did:
|
||
|
||
.. code:: yoscrypt
|
||
|
||
read_verilog softusb_navre.v
|
||
hierarchy
|
||
proc; opt; memory; opt; techmap; opt
|
||
write_blif softusb_navre.blif
|
||
|
||
The first and last line obviously read the Verilog file and write the
|
||
BLIF file.
|
||
|
||
The 2nd line checks the design hierarchy and instantiates parametrized
|
||
versions of the modules in the design, if necessary. In the case of this
|
||
simple design this is a no-op. However, as a general rule a synthesis
|
||
script should always contain this command as first command after reading
|
||
the input files.
|
||
|
||
The 3rd line does most of the actual work:
|
||
|
||
- The command opt is the Yosys' built-in optimizer. It can perform some
|
||
simple optimizations such as const-folding and removing unconnected
|
||
parts of the design. It is common practice to call opt after each
|
||
major step in the synthesis procedure. In cases where too much
|
||
optimization is not appreciated (for example when analyzing a
|
||
design), it is recommended to call clean instead of opt.
|
||
|
||
- The command proc converts processes (Yosys' internal representation
|
||
of Verilog always- and initial-blocks) to circuits of multiplexers
|
||
and storage elements (various types of flip-flops).
|
||
|
||
- The command memory converts Yosys' internal representations of arrays
|
||
and array accesses to multi-port block memories, and then maps this
|
||
block memories to address decoders and flip-flops, unless the option
|
||
-nomap is used, in which case the multi-port block memories stay in
|
||
the design and can then be mapped to architecture-specific memory
|
||
primitives using other commands.
|
||
|
||
- The command techmap turns a high-level circuit with coarse grain
|
||
cells such as wide adders and multipliers to a fine-grain circuit of
|
||
simple logic primitives and single-bit storage elements. The command
|
||
does that by substituting the complex cells by circuits of simpler
|
||
cells. It is possible to provide a custom set of rules for this
|
||
process in the form of a Verilog source file, as we will see in the
|
||
next section.
|
||
|
||
Now Yosys can be run with the filename of the synthesis script as
|
||
argument:
|
||
|
||
.. code:: sh
|
||
|
||
yosys softusb_navre.ys
|
||
|
||
Now that we are using a synthesis script we can easily modify how Yosys
|
||
synthesizes the design. The first thing we should customize is the call
|
||
to the hierarchy command:
|
||
|
||
Whenever it is known that there are no implicit blackboxes in the
|
||
design, i.e. modules that are referenced but are not defined, the
|
||
hierarchy command should be called with the -check option. This will
|
||
then cause synthesis to fail when implicit blackboxes are found in the
|
||
design.
|
||
|
||
The 2nd thing we can improve regarding the hierarchy command is that we
|
||
can tell it the name of the top level module of the design hierarchy. It
|
||
will then automatically remove all modules that are not referenced from
|
||
this top level module.
|
||
|
||
For many designs it is also desired to optimize the encodings for the
|
||
finite state machines (FSMs) in the design. The fsm command finds FSMs,
|
||
extracts them, performs some basic optimizations and then generate a
|
||
circuit from the extracted and optimized description. It would also be
|
||
possible to tell the fsm command to leave the FSMs in their extracted
|
||
form, so they can be further processed using custom commands. But in
|
||
this case we don't want that.
|
||
|
||
So now we have the final synthesis script for generating a BLIF file for
|
||
the Navré CPU:
|
||
|
||
.. code:: yoscrypt
|
||
|
||
read_verilog softusb_navre.v
|
||
hierarchy -check -top softusb_navre
|
||
proc; opt; memory; opt; fsm; opt; techmap; opt
|
||
write_blif softusb_navre.blif
|
||
|
||
Advanced example: The Amber23 ARMv2a CPU
|
||
========================================
|
||
|
||
Our 2nd example is the `Amber23 ARMv2a CPU`_. Once again we base our example on
|
||
the Verilog code that is included in `yosys-bigsim`_.
|
||
|
||
.. _Amber23 ARMv2a CPU: http://opencores.org/projects/amber
|
||
|
||
.. code-block:: yoscrypt
|
||
:caption: `amber23.ys`
|
||
:name: amber23.ys
|
||
|
||
read_verilog a23_alu.v
|
||
read_verilog a23_barrel_shift_fpga.v
|
||
read_verilog a23_barrel_shift.v
|
||
read_verilog a23_cache.v
|
||
read_verilog a23_coprocessor.v
|
||
read_verilog a23_core.v
|
||
read_verilog a23_decode.v
|
||
read_verilog a23_execute.v
|
||
read_verilog a23_fetch.v
|
||
read_verilog a23_multiply.v
|
||
read_verilog a23_ram_register_bank.v
|
||
read_verilog a23_register_bank.v
|
||
read_verilog a23_wishbone.v
|
||
read_verilog generic_sram_byte_en.v
|
||
read_verilog generic_sram_line_en.v
|
||
hierarchy -check -top a23_core
|
||
add -global_input globrst 1
|
||
proc -global_arst globrst
|
||
techmap -map adff2dff.v
|
||
opt; memory; opt; fsm; opt; techmap
|
||
write_blif amber23.blif
|
||
|
||
The problem with this core is that it contains no dedicated reset logic. Instead
|
||
the coding techniques shown in :numref:`glob_arst` are used to define reset
|
||
values for the global asynchronous reset in an FPGA implementation. This design
|
||
can not be expressed in BLIF as it is. Instead we need to use a synthesis script
|
||
that transforms this form to synchronous resets that can be expressed in BLIF.
|
||
|
||
(Note that there is no problem if this coding techniques are used to model ROM,
|
||
where the register is initialized using this syntax but is never updated
|
||
otherwise.)
|
||
|
||
:numref:`amber23.ys` shows the synthesis script for the Amber23 core. In line 17
|
||
the add command is used to add a 1-bit wide global input signal with the name
|
||
``globrst``. That means that an input with that name is added to each module in the
|
||
design hierarchy and then all module instantiations are altered so that this new
|
||
signal is connected throughout the whole design hierarchy.
|
||
|
||
.. code-block:: verilog
|
||
:caption: Implicit coding of global asynchronous resets
|
||
:name: glob_arst
|
||
|
||
reg [7:0] a = 13, b;
|
||
initial b = 37;
|
||
|
||
.. code-block:: verilog
|
||
:caption: `adff2dff.v`
|
||
:name: adff2dff.v
|
||
|
||
(* techmap_celltype = "$adff" *)
|
||
module adff2dff (CLK, ARST, D, Q);
|
||
|
||
parameter WIDTH = 1;
|
||
parameter CLK_POLARITY = 1;
|
||
parameter ARST_POLARITY = 1;
|
||
parameter ARST_VALUE = 0;
|
||
|
||
input CLK, ARST;
|
||
input [WIDTH-1:0] D;
|
||
output reg [WIDTH-1:0] Q;
|
||
|
||
wire [1023:0] _TECHMAP_DO_ = "proc";
|
||
|
||
wire _TECHMAP_FAIL_ =
|
||
!CLK_POLARITY || !ARST_POLARITY;
|
||
|
||
always @(posedge CLK)
|
||
if (ARST)
|
||
Q <= ARST_VALUE;
|
||
else
|
||
Q <= D;
|
||
|
||
endmodule
|
||
|
||
In line 18 the :cmd:ref:`proc` command is called. But in this script the signal
|
||
name globrst is passed to the command as a global reset signal for resetting the
|
||
registers to their assigned initial values.
|
||
|
||
Finally in line 19 the techmap command is used to replace all instances of
|
||
flip-flops with asynchronous resets with flip-flops with synchronous resets. The
|
||
map file used for this is shown in :numref:`adff2dff.v`. Note how the
|
||
``techmap_celltype`` attribute is used in line 1 to tell the techmap command
|
||
which cells to replace in the design, how the ``_TECHMAP_FAIL_`` wire in lines
|
||
15 and 16 (which evaluates to a constant value) determines if the parameter set
|
||
is compatible with this replacement circuit, and how the ``_TECHMAP_DO_`` wire
|
||
in line 13 provides a mini synthesis-script to be used to process this cell.
|
||
|
||
.. code-block:: c
|
||
:caption: Test program for the Amber23 CPU (Sieve of Eratosthenes). Compiled
|
||
using GCC 4.6.3 for ARM with ``-Os -marm -march=armv2a
|
||
-mno-thumb-interwork -ffreestanding``, linked with ``--fix-v4bx``
|
||
set and booted with a custom setup routine written in ARM assembler.
|
||
:name: sieve
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
|
||
#define BITMAP_SIZE 64
|
||
#define OUTPORT 0x10000000
|
||
|
||
static uint32_t bitmap[BITMAP_SIZE/32];
|
||
|
||
static void bitmap_set(uint32_t idx) { bitmap[idx/32] |= 1 << (idx % 32); }
|
||
static bool bitmap_get(uint32_t idx) { return (bitmap[idx/32] & (1 << (idx % 32))) != 0; }
|
||
static void output(uint32_t val) { *((volatile uint32_t*)OUTPORT) = val; }
|
||
|
||
int main() {
|
||
uint32_t i, j, k;
|
||
output(2);
|
||
for (i = 0; i < BITMAP_SIZE; i++) {
|
||
if (bitmap_get(i)) continue;
|
||
output(3+2*i);
|
||
for (j = 2*(3+2*i);; j += 3+2*i) {
|
||
if (j%2 == 0) continue;
|
||
k = (j-3)/2;
|
||
if (k >= BITMAP_SIZE) break;
|
||
bitmap_set(k);
|
||
}
|
||
}
|
||
output(0);
|
||
return 0;
|
||
}
|
||
|
||
Verification of the Amber23 CPU
|
||
===============================
|
||
|
||
The BLIF file for the Amber23 core, generated using :numref:`amber23.ys` and
|
||
:numref:`adff2dff.v` and the version of the Amber23 RTL source that is bundled
|
||
with yosys-bigsim, was verified using the test-bench from yosys-bigsim. It
|
||
successfully executed the program shown in :numref:`sieve` in the test-bench.
|
||
|
||
For simulation the BLIF file was converted back to Verilog using `ABC`_. So this
|
||
test includes the successful transformation of the BLIF file into ABC's internal
|
||
format as well.
|
||
|
||
.. _ABC: https://github.com/berkeley-abc/abc
|
||
|
||
The only thing left to write about the simulation itself is that it probably was
|
||
one of the most energy inefficient and time consuming ways of successfully
|
||
calculating the first 31 primes the author has ever conducted.
|
||
|
||
Limitations
|
||
===========
|
||
|
||
At the time of this writing Yosys does not support multi-dimensional memories,
|
||
does not support writing to individual bits of array elements, does not support
|
||
initialization of arrays with ``$readmemb`` and ``$readmemh``, and has only
|
||
limited support for tristate logic, to name just a few limitations.
|
||
|
||
That being said, Yosys can synthesize an overwhelming majority of real-world
|
||
Verilog RTL code. The remaining cases can usually be modified to be compatible
|
||
with Yosys quite easily.
|
||
|
||
The various designs in yosys-bigsim are a good place to look for examples of
|
||
what is within the capabilities of Yosys.
|
||
|
||
Conclusion
|
||
==========
|
||
|
||
Yosys is a feature-rich Verilog-2005 synthesis tool. It has many uses, but one
|
||
is to provide an easy gateway from high-level Verilog code to low-level logic
|
||
circuits.
|
||
|
||
The command line option ``-S`` can be used to quickly synthesize Verilog code to
|
||
BLIF files without a hassle.
|
||
|
||
With custom synthesis scripts it becomes possible to easily perform high-level
|
||
optimizations, such as re-encoding FSMs. In some extreme cases, such as the
|
||
Amber23 ARMv2 CPU, the more advanced Yosys features can be used to change a
|
||
design to fit a certain need without actually touching the RTL code.
|