Skip to content

Latest commit

 

History

History
189 lines (126 loc) · 9.97 KB

synthesis.md

File metadata and controls

189 lines (126 loc) · 9.97 KB

Writing Synthesizable SystemVerilog

This guide gives a brief overview of how to write synthesizable SystemVerilog. (When this guide fails, please move on to this guide: "Busting the Myth that SystemVerilog is only for Verification".)

SystemVerilog and Verilog are overwhelmingly popular in digital design, but are partnered with extremely underdeveloped and unreliable software. This leads to extremely frequent instances of Verilog software not warning on bad code, and instances of software not understanding good code. For example, here is an example of a Verilog-2005 design that works with some tools, but not with others: https://github.com/ucsb-ece154a/verilog_best_practices_example.

By following this guide, you will learn how to overcome the shortcomings of Verilog/SystemVerilog tools and write synthesizable code that meets your desired specifications.

All code examples follow the lowRISC Verilog Coding Style Specifications.

Table of Contents

DigitalJS Online

DigitalJS Online is an incredible website that uses Verilator, Yosys, and ElkJS to lint, synthesize, and visualize any valid Verilog design. Playing around with this website for more than an hour will teach you more about Verilog than reading 90% of all the available online Verilog resources.

Inference

Verilog and SystemVerilog are behavioral, which means that these languages describe the intended behavior of a circuit, and not the logic required to implement the design using physical hardware.

Inference in Verilog and SystemVerilog refers to the process by which logic cells or hardware components are automatically generated from the behavioral code. Synthesis tools will automatically infer which logic gates and other hardware components are necessary to implement the specified behavior.

Additionally, when mapping a synthesized design to a specified target (FPGA/ASIC), the synthesis tool will need to adjust its netlist to ensure that it only uses what logic cells are available. For example, an FPGA without any dedicated adder cells must use the gates it has available to implement an adder if one was described behaviorally.

The exception to this is that Verilog and SystemVerilog allow explicit instantiation of synthesis primitives to force logic cells to be added to the netlist. For example, $_DFF_N_, SB_MAC16, DSP48E1, etc.

Combinational Logic

Combinational logic can be generated either by using assign or always_comb. The rule of thumb is, if you can do it cleanly in 1 line of code, use assign. Otherwise, use always_comb.

logic [WIDTH-1:0] data_i, data_plus1, data_plus2, data_o;

assign data_plus1   = data_i        + WIDTH'(1);
assign data_plus2   = data_plus1    + WIDTH'(1);
assign data_o       = data_plus2    + WIDTH'(1);

always_comb blocks allow for procedural assignment, which enables greater design flexibility.

always_comb begin : data_set
    data_o = data_i + WIDTH'(1);
    data_o = data_o + WIDTH'(1);
    data_o = data_o + WIDTH'(1);
end

Latches

Latches are generally frowned-upon. Many FPGAs don't even have a latch cell you can use. Only use a latch if you REALLY know what you are doing.

To infer a latch, you should structure your code like this:

always_latch begin : y_latch
    if (en_i)
        y_o <= x_i;
end

An unwanted latch is generated in an always_comb block when a net is not updated for a possible input condition. A common good practice to avoid unwanted latches is to set default values for all combinational nets at the top of the always_comb block.

always_comb begin : y_latch
    y1_o = 0; // default value
    if (en_i) begin
        y0_o = x0_i; // latch (gives error)
        y1_o = x1_i; // no latch
    end
end

Flip-Flops

A common practice is to split your flip-flops into _d and _q nets. This way, your code is organized better because all your combinational logic is clearly done to your _d net in an always_comb block, and your _q nets are assigned in a always_ff block using reset and the _d nets. Plus, always_ff blocks do not allow for procedural assignment, so always_comb blocks are always better for combinational logic.

Another popular naming strategy is to use <NAME>_next and <NAME>_reg instead of <NAME>_d and <NAME>_q. This is a personal preference, but it is crucial to match the coding style already introduced by the developers of the project. If it's your project, pick your favorite, and stick to it!

Note: the following are infamously buggy in synthesis tools:

  • FF initial values (initial data = 0; or logic data = 0;). Instead, use a reset value for all FFs.
  • Non-clock/reset logic in always_ff. If you are following the lowRISC naming style with _d and _q, this should never happen.

To infer a flip-flop, you should structure your code like this:

logic [WIDTH-1:0] data_d, data_q;

always_comb begin : data_set
    data_d = input_i;
end

always_ff @(posedge clk_i) begin : data_ff
    if (rst_i) begin
        data_q <= '0;
    end else begin
        data_q <= data_d;
    end
end

Memory

Memories in designs are common, so FPGAs often have built-in block RAMs (BRAMs) that you can use. Unfortunately, synthesis tools are usually really bad at inferring memories from a design, and will often incorrectly infer an array of DFFs. If you want a BRAM, then you should either use target-specific BRAM primitives (SB_RAM40_4K, RAMB36E1, etc.) or write clear behavioral code that the tool developers allow for memory inference.

For a tool to have the greatest success of inferring a memory, you should structure your code like this:

// instantiation
logic [DATA_WIDTH-1:0] MEM [0:NR_ENTRIES-1];

// read port
assign rdata_o = MEM[raddr_i];

// write port
always_ff @(posedge clk_i) begin : mem_write
    if (we_i)
        MEM[waddr_i] <= wdata_i;
end

Clock and Reset

Clock and reset nets hold highly sensitive global signals, and are better optimized when they are only used in always_ff blocks and not always_comb blocks.

When synthesizing your design, you often want to manually tell your synthesis software which nets are clocks. (Vivado example: ucsbieee/mapache64 "clk_constraints.xdc".)

Simulation Tools

There are several Verilog Simulators to choose from, and each have pros and cons. Here is a quick summary of a few important ones:

Open Source Simulation Tools

  • Icarus Verilog
    • Pros: Easy to use, good Verilog-2005 support
    • Cons: Poor SystemVerilog support, slow for large designs, minimal error messages
  • Verilator

Proprietary Simulation Tools

(All good, but all expensive)

  • Siemens ModelSim
  • Synopsys VCS
  • Cadence NCSim

Synthesis Tools

Open Source Synthesis Tools

The only good open-source synthesis software is Yosys. Yosys is a buggy mess that has laughable SystemVerilog support, and is infamous for absolutely scrambling designs without giving any warnings. Therefore, it is CRUCIAL that you ensure your code is well-linted and follows all best-practices before using Yosys.

Proprietary Synthesis Tools

The proprietary tool will depend on what your FPGA supports. Though Vivado is well-liked and offers free synthesis for most Xilinx FPGAs.

OSS CAD Suite

If you want a fully open-source design flow, you will need to install nearly a dozen tools that each do different things. You will need Icarus, Verilator, GTKWave, Yosys, Surelog, sv2v, nextpnr, IceStorm, openFPGALoader, and more. Most of these tools do not provide updated binaries and expect you to compile them yourself. However, as of 2021 this overwhelming scavenger hunt has been made exponentially easier!

OSS CAD Suite is a project that releases updated binaries of all common open-source digital design tools in one TAR file.

This is how to curl a release from OSS:

Note: check the latest release and edit the filenames in the script accordingly.

cd ~/Downloads
curl -JOL https://github.com/YosysHQ/oss-cad-suite-build/releases/download/YYYY-MM-DD/oss-cad-suite-linux-x64-YYYYMMDD.tgz
tar -xzvf oss-cad-suite-linux-x64-YYYYMMDD.tgz -C ~/Utils/

Be sure to add the OSS CAD Suite "bin" directory to PATH.