SymbiFlow
Table Of Contents
SymbiFlow
Table Of Contents

Yosys

Yosys is a Free and Open Source Verilog HDL synthesis tool. It was designed to be highly extensible and multiplatform. In SymbiFlow toolchain, it is responsible for the whole synthesis process described in FPGA Design Flow

It is not necessary to call Yosys directly using the SymbiFlow toolchain. Nevertheless, the following description, should provide sufficient introduction to Yosys usage inside the project. It is also a good starting point for a deeper understanding of the whole toolchain.

Short description

Yosys consists of several subsystems. Most distinguishable are the first and last ones used in the synthesis process, called frontend and backend respectively. Intermediate subsystems are called passes.

The frontend is responsible for changing the Verilog input file into an internal Yosys, representation which is common for all passes used by the program. The passes are responsible for a variety of optimizations (opt_) and simplifications (proc_).

Two passes, that are worth to mention separately are ABC and techmap. The first one optimizes logic functions from the design and assigns obtained results into Look Up Tables (LUTs) of chosen width. The second mentioned pass - techmap is responsible for mapping the synthesized design from Yosys internal blocks to the primitives used by the implementation tool. Recommended synthesis flows for different FPGAs are combined into macros i.e. synth_ice40 (for Lattice iCE40 FPGA) or synth_xilinx (for Xilinx 7-series FPGAs).

The backend on the other hand, is responsible for converting internal Yosys representation into one of the standardized formats. Symbiflow uses .eblif as its output file format.

Usage in Toolchain

All operations performed by Yosys are written in .tcl script. Commands used in the scripts are responsible for preparing output file to match with the expectations of other toolchain tools. There is no need to change it even for big designs. An example configuration script can be found below:

yosys -import

synth_ice40 -nocarry

opt_expr -undriven
opt_clean

setundef -zero -params
write_blif -attr -cname -param $::env(OUT_EBLIF)
write_verilog $::env(OUT_SYNTH_V)

It can be seen that this script performs a platform-specific process of synthesis, some optimization steps (opt_ commands), and writes the final file in .eblif and Verilog formats. Yosys synthesis configuration scripts are platform-specific and can by found in <platform-dir>/yosys/synth.tcl in the Symbiflow Architecture Definitions repository.

To understand performed operations, view the log file. It is usually generated in the project build directory. It should be named top.eblif.log.

Output analysis

Input file:

module top (
    input  clk,
    output LD7,
);
    localparam BITS = 1;
    localparam LOG2DELAY = 25;

    reg [BITS+LOG2DELAY-1:0] counter = 0;
    always @(posedge clk) begin
            counter <= counter + 1;
    end

    assign {LD7} = counter >> LOG2DELAY;
endmodule

after synthesis is described only with use of primitives appropriate for chosen platform:

module top(clk, LD7);
  wire [25:0] _000_;
  wire _001_;

...

  FDRE_ZINI #(
    .IS_C_INVERTED(1'h0),
    .ZINI(1'h1)
  ) _073_ (
    .C(clk),
    .CE(_012_),
    .D(_000_[0]),
    .Q(counter[0]),
    .R(_013_)
  );

...

  SR_GND _150_ (
    .GND(_062_)
  );
  assign _003_[25:0] = _000_;
  assign counter[25] = LD7;
endmodule

The same structure is described by the .eblif file.

Technology mapping in SymbiFlow toolchain

It is important to understand the connection between the synthesis and implementation tools used in the SymbiFlow toolchain. As mentioned before, synthesis tools like Yosys take the design description from the source files and convert them into a netlist that consists of the primitives used by the implementation tool. Usually, to support multiple implementation tools, an additional intermediate representation of FPGA primitives is provided. The process of translating the primitives from the synthesis tool’s internal representation to the specific primitives used in the implementation tools is called technology mapping (or techmapping).

Technology mapping for VPR

As mentioned before, VPR is one of the implementation tools (often referred to as Place & Route or P&R tools) used in SymbiFlow. By default, the SymbiFlow toolchain uses it during bitstream generation for, i.e., Xilinx 7-Series devices. Since the architecture models for this FPGA family were created from scratch, appropriate techmaps were needed to instruct Yosys on translating the primitives to the versions compatible with VPR.

The clock buffers used in the 7-Series devices are a good example for explaining the techmapping process. Generally, as stated in the Xilinx 7 Series FPGAs Clocking Resources User Guide, a designer has various buffer types that they can use in designs:

  • BUFGCTRL

  • BUFG

  • BUFGCE

  • BUFGCE_1

  • BUFGMUX

  • BUFGMUX_1

  • BUFGMUX_CTRL

Nevertheless, the actual chips consist only of the BUFGCTRL primitives, which are the most universal and can function as other clock buffer primitives from the Xilinx manual. Because of that, only one architecture model is required for VPR. The rest of the primitives is mapped to this general buffer during the techmapping process. The model of BUFGCTRL primitive used by VPR is called BUFGCTR_VPR (More information about the architecture modeling in VPR can be found in the VTR FPGA Architecture Description).

Support for particular primitive in VTR consist of two files:

  • Model XML (xxx.model.xml) - Contains general information about the module’s input and output ports and their relations.

  • Physical Block XML (xxx.pb_type.xml) - Describes the actual layout of the primitive, with information about the timings, internal connections, etc.

Below you can see the pb_type XML for BUFGCTRL_VPR primitive:

<!-- Model of BUFG group in BUFG_CLK_TOP/BOT -->
<pb_type name="BLK-TL-BUFGCTRL" xmlns:xi="http://www.w3.org/2001/XInclude">
  <output name="O" num_pins="1"/>
  <input name="CE0" num_pins="1"/>
  <input name="CE1" num_pins="1"/>
  <clock name="I0" num_pins="1"/>
  <clock name="I1" num_pins="1"/>
  <input name="IGNORE0" num_pins="1"/>
  <input name="IGNORE1" num_pins="1"/>
  <input name="S0" num_pins="1"/>
  <input name="S1" num_pins="1"/>
  <mode name="EMPTY">
    <pb_type name="empty" blif_model=".latch" num_pb="1" />
    <interconnect />
  </mode>
  <mode name="BUFGCTRL">
    <pb_type name="BUFGCTRL_VPR" blif_model=".subckt BUFGCTRL_VPR" num_pb="1">
      <output name="O" num_pins="1"/>
      <input name="CE0" num_pins="1"/>
      <input name="CE1" num_pins="1"/>
      <clock name="I0" num_pins="1"/>
      <clock name="I1" num_pins="1"/>
      <input name="IGNORE0" num_pins="1"/>
      <input name="IGNORE1" num_pins="1"/>
      <input name="S0" num_pins="1"/>
      <input name="S1" num_pins="1"/>
      <metadata>
        <meta name="fasm_params">
          ZPRESELECT_I0 = ZPRESELECT_I0
          ZPRESELECT_I1 = ZPRESELECT_I1
          IS_IGNORE0_INVERTED = IS_IGNORE0_INVERTED
          IS_IGNORE1_INVERTED = IS_IGNORE1_INVERTED
          ZINV_CE0 = ZINV_CE0
          ZINV_CE1 = ZINV_CE1
          ZINV_S0 = ZINV_S0
          ZINV_S1 = ZINV_S1
        </meta>
      </metadata>
    </pb_type>
    <interconnect>
      <direct name="O" input="BUFGCTRL_VPR.O" output="BLK-TL-BUFGCTRL.O"/>
      <direct name="CE0" input="BLK-TL-BUFGCTRL.CE0" output="BUFGCTRL_VPR.CE0"/>
      <direct name="CE1" input="BLK-TL-BUFGCTRL.CE1" output="BUFGCTRL_VPR.CE1"/>
      <direct name="I0" input="BLK-TL-BUFGCTRL.I0" output="BUFGCTRL_VPR.I0"/>
      <direct name="I1" input="BLK-TL-BUFGCTRL.I1" output="BUFGCTRL_VPR.I1"/>
      <direct name="IGNORE0" input="BLK-TL-BUFGCTRL.IGNORE0" output="BUFGCTRL_VPR.IGNORE0"/>
      <direct name="IGNORE1" input="BLK-TL-BUFGCTRL.IGNORE1" output="BUFGCTRL_VPR.IGNORE1"/>
      <direct name="S0" input="BLK-TL-BUFGCTRL.S0" output="BUFGCTRL_VPR.S0"/>
      <direct name="S1" input="BLK-TL-BUFGCTRL.S1" output="BUFGCTRL_VPR.S1"/>

    </interconnect>
    <metadata>
      <meta name="fasm_features">
        IN_USE
      </meta>
    </metadata>
  </mode>
</pb_type>

A correctly prepared techmap for any VPR model contains a declaration of the module that should be substituted. Inside the module declaration, one should provide a necessary logic and instantiate another module that will substitute its original version. Additionally, all equations within a techmap that are not used directly in a module instantiation should evaluate to a constant value. Therefore most of the techmaps use additional constant parameters to modify the signals attached to the instantiated module.

Here is a piece of a techmap, which instructs Yosys to convert a BUFG primitive to the BUFGCTRL_VPR. In this case, the techmaping process consists of two steps. Firstly, the techmap shows how to translate the BUFG primitive to the BUFGCTRL. Then how to translate the BUFGCTRL to the BUFGCTRL_VPR:

module BUFG (
  input I,
  output O
  );

  BUFGCTRL _TECHMAP_REPLACE_ (
    .O(O),
    .CE0(1'b1),
    .CE1(1'b0),
    .I0(I),
    .I1(1'b1),
    .IGNORE0(1'b0),
    .IGNORE1(1'b1),
    .S0(1'b1),
    .S1(1'b0)
  );
endmodule

module BUFGCTRL (
output O,
input I0, input I1,
input S0, input S1,
input CE0, input CE1,
input IGNORE0, input IGNORE1
);

  parameter [0:0] INIT_OUT = 1'b0;
  parameter [0:0] PRESELECT_I0 = 1'b0;
  parameter [0:0] PRESELECT_I1 = 1'b0;
  parameter [0:0] IS_IGNORE0_INVERTED = 1'b0;
  parameter [0:0] IS_IGNORE1_INVERTED = 1'b0;
  parameter [0:0] IS_CE0_INVERTED = 1'b0;
  parameter [0:0] IS_CE1_INVERTED = 1'b0;
  parameter [0:0] IS_S0_INVERTED = 1'b0;
  parameter [0:0] IS_S1_INVERTED = 1'b0;

  parameter _TECHMAP_CONSTMSK_IGNORE0_ = 0;
  parameter _TECHMAP_CONSTVAL_IGNORE0_ = 0;
  parameter _TECHMAP_CONSTMSK_IGNORE1_ = 0;
  parameter _TECHMAP_CONSTVAL_IGNORE1_ = 0;
  parameter _TECHMAP_CONSTMSK_CE0_ = 0;
  parameter _TECHMAP_CONSTVAL_CE0_ = 0;
  parameter _TECHMAP_CONSTMSK_CE1_ = 0;
  parameter _TECHMAP_CONSTVAL_CE1_ = 0;
  parameter _TECHMAP_CONSTMSK_S0_ = 0;
  parameter _TECHMAP_CONSTVAL_S0_ = 0;
  parameter _TECHMAP_CONSTMSK_S1_ = 0;
  parameter _TECHMAP_CONSTVAL_S1_ = 0;

  localparam [0:0] INV_IGNORE0 = (
      _TECHMAP_CONSTMSK_IGNORE0_ == 1 &&
      _TECHMAP_CONSTVAL_IGNORE0_ == 0 &&
      IS_IGNORE0_INVERTED == 0);
  localparam [0:0] INV_IGNORE1 = (
      _TECHMAP_CONSTMSK_IGNORE1_ == 1 &&
      _TECHMAP_CONSTVAL_IGNORE1_ == 0 &&
      IS_IGNORE1_INVERTED == 0);
  localparam [0:0] INV_CE0 = (
      _TECHMAP_CONSTMSK_CE0_ == 1 &&
      _TECHMAP_CONSTVAL_CE0_ == 0 &&
      IS_CE0_INVERTED == 0);
  localparam [0:0] INV_CE1 = (
      _TECHMAP_CONSTMSK_CE1_ == 1 &&
      _TECHMAP_CONSTVAL_CE1_ == 0 &&
      IS_CE1_INVERTED == 0);
  localparam [0:0] INV_S0 = (
      _TECHMAP_CONSTMSK_S0_ == 1 &&
      _TECHMAP_CONSTVAL_S0_ == 0 &&
      IS_S0_INVERTED == 0);
  localparam [0:0] INV_S1 = (
      _TECHMAP_CONSTMSK_S1_ == 1 &&
      _TECHMAP_CONSTVAL_S1_ == 0 &&
      IS_S1_INVERTED == 0);

  BUFGCTRL_VPR #(
      .INIT_OUT(INIT_OUT),
      .ZPRESELECT_I0(PRESELECT_I0),
      .ZPRESELECT_I1(PRESELECT_I1),
      .IS_IGNORE0_INVERTED(!IS_IGNORE0_INVERTED ^ INV_IGNORE0),
      .IS_IGNORE1_INVERTED(!IS_IGNORE1_INVERTED ^ INV_IGNORE1),
      .ZINV_CE0(!IS_CE0_INVERTED ^ INV_CE0),
      .ZINV_CE1(!IS_CE1_INVERTED ^ INV_CE1),
      .ZINV_S0(!IS_S0_INVERTED ^ INV_S0),
      .ZINV_S1(!IS_S1_INVERTED ^ INV_S1)
  ) _TECHMAP_REPLACE_ (
    .O(O),
    .CE0(CE0 ^ INV_CE0),
    .CE1(CE1 ^ INV_CE1),
    .I0(I0),
    .I1(I1),
    .IGNORE0(IGNORE0 ^ INV_IGNORE0),
    .IGNORE1(IGNORE1 ^ INV_IGNORE1),
    .S0(S0 ^ INV_S0),
    .S1(S1 ^ INV_S1)
  );

 endmodule

Note

All SymbiFlow techmaps for Xilinx 7-Series devices use special inverter logic that converts constant 0 signals at the BEL to constant-1 signals at the site. This behavior is desired since VCC is the default signal in 7-Series and US/US+ devices. The presented solution matches the conventions used by the vendor tools and gives the opportunity to validate generated bitstreams with fasm2bels and Vivado.

Yosys provides special techmapping naming conventions for wires, parameters, and modules. The special names that start with _TECHMAP_ can be used to force certain behavior during the techmapping process. Currently, the following special names are used in SymbiFlow techmaps:

  • _TECHMAP_REPLACE_ is used as a name for an instantiated module, which will replace the one used in the original design. This special name causes the instantiated module to inherit the name and all attributes from the module that is being replaced.

  • _TECHMAP_CONSTMSK_<port_name>_ and _TECHMAP_CONSTVAL_<port_name>_ are used together as names of parameters. The _TECHMAP_CONSTMASK_<port_name>_ has a length of the input signal. Its bits take the value 1 if the corresponding signal bit has a constant value, or 0 otherwise. The _TECHMAP_CONSTVAL_<port_name>_ bits store the actual constant signal values when the _TECHMAP_CONSTMASK_<port_name>_ is equal to 1.

More information about special wire, parameter, and module names can be found in techmap section in the Yosys Manual.

Note

Techmapping can be used not only to change the names of the primitives but primarily to match the port declarations and express the logic behind the primitive substitution:

module BUFG(output O, input I)
module BUFGCTRL(output O, input CE0, input CE1, input I0, input I1, input IGNORE0, input IGNORE1, input S0, input S1)

More information

Additional information about Yosys can be found on the Yosys Project Website , or in Yosys Manual. You can also compile one of the tests described in Getting Started section and watch the log file to understand which operations are performed by Yosys.