From Concepts to Commands: Practical Synthesis with Yosys

In the previous module, you learned the theory of logic synthesis—how RTL transforms into gates through parsing, optimization, and technology mapping. Now it's time to see synthesis in action. Yosys (Yosys Open SYnthesis Suite) is an open-source synthesis framework that makes these concepts tangible through a powerful command-line interface and scripting language.

Unlike commercial synthesis tools that hide their inner workings behind polished GUIs, Yosys exposes every step of the synthesis process. This transparency makes it an excellent learning tool—you can watch your RTL transform step-by-step, inspect intermediate results, and understand exactly what each optimization pass does. This module guides you through the Yosys synthesis flow, from reading RTL to producing optimized generic logic.

Yosys Command Structure and Philosophy

Interactive vs. Script Mode

Interactive Mode
Launch Yosys and type commands one at a time:

$ yosys
yosys> read_verilog counter.v
yosys> hierarchy -check
yosys> proc
yosys> show

This is perfect for learning and experimentation—you can see immediate results and explore.

Script Mode
Put commands in a file and execute:

$ cat synth.ys
read_verilog counter.v
hierarchy -check -top counter
proc
opt
show

$ yosys -s synth.ys

This is better for reproducible builds and automation.

Inline Script Mode
Execute a script inline:

$ yosys -p "read_verilog counter.v; hierarchy -check; proc; opt; show"

Useful for quick one-off synthesis runs.

Command Structure Basics

General Format

command [options] [arguments]

Getting Help

yosys> help
yosys> help read_verilog
yosys> help proc

Every command has detailed documentation accessible via help.

Design Object Selection Many commands operate on specific design objects:

show                    # Show entire design
show counter            # Show specific module
select -module counter  # Select a module for subsequent commands
select w:clk            # Select all wires named 'clk'

Selection patterns use prefixes:

  • t: - Select by cell type (e.g., t:$add)
  • w: - Select wires/signals
  • c: - Select cells
  • m: - Select modules
  • a: - Select attributes

The Design Database

Yosys maintains an internal design database that evolves as you execute commands:

RTL Text File → Parse → Internal Representation (IR)
                         ↓
                    Transformation Commands
                         ↓
                    Modified IR
                         ↓
                    Output (Verilog/JSON/etc.)

You can inspect and modify this database at any stage, which is Yosys's superpower for learning.

Reading RTL: The read_verilog Command

Basic Usage

Simple Read

yosys> read_verilog counter.v

This parses the Verilog file and adds it to the design database.

Reading Multiple Files

yosys> read_verilog module1.v
yosys> read_verilog module2.v
yosys> read_verilog module3.v

Or in one command:

yosys> read_verilog module1.v module2.v module3.v

Reading with Preprocessing

yosys> read_verilog -D SIMULATION -D WIDTH=16 design.v

This defines preprocessor macros, just like Verilog define directives.

Verilog-2005 vs. SystemVerilog

Default: Verilog-2005

yosys> read_verilog design.v

SystemVerilog Mode

yosys> read_verilog -sv design.sv

Enables SystemVerilog features:

  • always_ff, always_comb, always_latch
  • logic data type
  • Enumerated types
  • Interfaces (limited support)

Common Read Options

Specify Top Module During Read

yosys> read_verilog -top my_module design.v

Defer Hierarchy Checking

yosys> read_verilog -defer design.v

Useful when reading multiple files that reference each other.

Include Paths

yosys> read_verilog -I./include -I../common design.v

Specifies directories for include directives.

What Happens During Read?

  1. Lexical Analysis: Tokenize the input file
  2. Syntax Parsing: Build abstract syntax tree (AST)
  3. Semantic Analysis: Check for basic errors
  4. IR Generation: Convert AST to internal representation

After read_verilog, you have behavioral RTL in the design database, but it's not yet optimized or synthesized.

Elaboration and Hierarchy Handling: hierarchy

What Is Elaboration?

Elaboration resolves the design hierarchy:

  • Identifies the top module
  • Instantiates all submodules
  • Resolves parameters
  • Checks for missing modules
  • Flattens generate blocks

Basic Hierarchy Command

Check Hierarchy

yosys> hierarchy -check

Verifies:

  • All instantiated modules exist
  • No dangling module instances
  • Parameter passing is correct

Specify Top Module

yosys> hierarchy -top counter

Tells Yosys which module is the top-level (especially important with multiple modules).

Auto-detect Top Module

yosys> hierarchy -auto-top

Yosys finds the module that isn't instantiated anywhere else.

Hierarchy Options

Generate Top Module Wrapper

yosys> hierarchy -generate top_wrapper

Creates a wrapper module if you don't have one.

Check Design Hierarchy

yosys> hierarchy -check -top my_design

Combines top specification with checking—most common usage.

Purge Unused Modules

yosys> hierarchy -purge_lib

Removes library cells that aren't actually used.

Example: Multi-Module Design

Design Files

// adder.v
module adder (
    input [7:0] a, b,
    output [7:0] sum
);
    assign sum = a + b;
endmodule

// counter.v
module counter (
    input clk, rst,
    output [7:0] count
);
    reg [7:0] count_reg;
    wire [7:0] next_count;
    
    adder u_adder (
        .a(count_reg),
        .b(8'd1),
        .sum(next_count)
    );
    
    always @(posedge clk or posedge rst) begin
        if (rst)
            count_reg <= 8'd0;
        else
            count_reg <= next_count;
    end
    
    assign count = count_reg;
endmodule

Yosys Script

read_verilog adder.v
read_verilog counter.v
hierarchy -check -top counter
# Now Yosys knows:
# - counter is the top module
# - adder is instantiated within counter
# - All modules are present

Hierarchy Visualization

yosys> show -prefix hier -format dot

Generates a graphical representation showing module hierarchy.

High-Level Synthesis Passes

After reading RTL and establishing hierarchy, Yosys must convert high-level constructs into gate-level logic. Three key commands handle this: proc, fsm, and memory.

The proc Pass: Processing Behavioral Code

What proc Does

The proc pass converts behavioral Verilog (always blocks) into primitive logic:

  • Converts always @(posedge clk) into flip-flops
  • Converts always @(*) into combinational logic
  • Resolves if-else and case statements
  • Handles blocking and non-blocking assignments

Before proc: Behavioral RTL

always @(posedge clk) begin
    if (rst)
        q <= 0;
    else if (enable)
        q <= d;
end

After proc: Structural representation

Multiplexer selecting between:
  - Reset value (0)
  - Enable value (d)
  - Hold value (q)
Connected to D flip-flop

Running proc

Basic Usage

yosys> proc

This executes a sequence of sub-passes:

  1. proc_clean - Remove empty blocks
  2. proc_rmdead - Remove dead code
  3. proc_init - Process initial blocks
  4. proc_arst - Extract asynchronous resets
  5. proc_mux - Convert if/case to multiplexers
  6. proc_dlatch - Convert to D-latches
  7. proc_dff - Convert to D flip-flops

Manual Sub-Pass Execution

yosys> proc_clean
yosys> proc_rmdead
yosys> proc_mux
yosys> proc_dff

You can run sub-passes individually for debugging or fine control.

Example: Counter with Reset

RTL

module counter (
    input clk,
    input rst,
    output [3:0] count
);
    reg [3:0] count_reg;
    
    always @(posedge clk or posedge rst) begin
        if (rst)
            count_reg <= 4'd0;
        else
            count_reg <= count_reg + 1;
    end
    
    assign count = count_reg;
endmodule

Yosys Processing

yosys> read_verilog counter.v
yosys> hierarchy -check -top counter

yosys> show -prefix before_proc
# Shows behavioral representation

yosys> proc
# Converts always block to:
# - Adder for count_reg + 1
# - Mux selecting between reset value and incremented value
# - DFF with async reset

yosys> show -prefix after_proc
# Shows structural representation with cells

Internal Representation After proc

$addfull cells for addition
$mux cells for if-else selection
$dff/$adff cells for registers

Common proc Patterns

Synchronous Reset

always @(posedge clk) begin
    if (rst)
        q <= 0;
    else
        q <= d;
end

Becomes: DFF with synchronous reset (reset is just another input to the data mux).

Asynchronous Reset

always @(posedge clk or posedge rst) begin
    if (rst)
        q <= 0;
    else
        q <= d;
end

Becomes: DFF with asynchronous reset (special $adff cell).

Combinational Logic

always @(*) begin
    case (sel)
        2'b00: out = a;
        2'b01: out = b;
        2'b10: out = c;
        2'b11: out = d;
    endcase
end

Becomes: Tree of $mux cells.

The fsm Pass: Finite State Machine Optimization

What fsm Does

The fsm pass detects, extracts, optimizes, and re-encodes finite state machines.

FSM Detection
Yosys identifies state machine patterns:

  • State variable declarations
  • State transition logic
  • Output generation logic

FSM Optimization

  • State minimization (merge equivalent states)
  • State encoding optimization (binary, one-hot, Gray code)
  • Unreachable state removal

Running fsm

Basic Usage

yosys> fsm

This executes sub-passes:

  1. fsm_detect - Find FSMs in the design
  2. fsm_extract - Extract FSM structure
  3. fsm_opt - Optimize FSMs
  4. fsm_recode - Re-encode states
  5. fsm_map - Map back to logic

FSM Visualization

yosys> fsm_detect
yosys> fsm_extract
yosys> show -prefix fsm_graph

Generates state transition diagrams.

Example: Simple Traffic Light Controller

RTL

module traffic_light (
    input clk,
    input rst,
    output reg [1:0] lights
);
    localparam RED    = 2'b00;
    localparam YELLOW = 2'b01;
    localparam GREEN  = 2'b10;
    
    reg [1:0] state;
    reg [3:0] timer;
    
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            state <= RED;
            timer <= 0;
        end else begin
            if (timer == 4'd15) begin
                timer <= 0;
                case (state)
                    RED:    state <= GREEN;
                    GREEN:  state <= YELLOW;
                    YELLOW: state <= RED;
                    default: state <= RED;
                endcase
            end else begin
                timer <= timer + 1;
            end
        end
    end
    
    always @(*) begin
        case (state)
            RED:    lights = 2'b00;
            YELLOW: lights = 2'b01;
            GREEN:  lights = 2'b10;
            default: lights = 2'b00;
        endcase
    end
endmodule

Yosys FSM Processing

yosys> read_verilog traffic_light.v
yosys> hierarchy -check -top traffic_light
yosys> proc

yosys> fsm_detect
# Detects state machine in 'state' variable

yosys> fsm_extract
# Extracts FSM structure

yosys> fsm_opt
# Optimizes (removes unreachable states, minimizes)

yosys> fsm_recode -encoding one-hot
# Re-encodes to one-hot (could use binary, gray, etc.)

yosys> fsm_map
# Maps back to flip-flops and logic

yosys> show

FSM Encoding Options

  • binary - Minimum flip-flops (log2(N) for N states)
  • one-hot - One flip-flop per state (faster, more area)
  • gray - Gray code encoding (reduced switching)

The memory Pass: Memory Inference and Optimization

What memory Does

The memory pass handles array variables that represent memories:

  • Detects memory patterns in RTL
  • Infers register files vs. RAM blocks
  • Optimizes memory access logic
  • Maps to technology-specific memory primitives (or flip-flops)

Running memory

Basic Usage

yosys> memory

Executes sub-passes:

  1. memory_collect - Identify memory structures
  2. memory_dff - Convert simple memories to flip-flops
  3. memory_map - Map memories to cells

Keep as Abstract Memory

yosys> memory -nomap

Leaves memories as abstract $mem cells for later mapping.

Example: Register File

RTL

module register_file (
    input clk,
    input [3:0] wr_addr,
    input [3:0] rd_addr,
    input [7:0] wr_data,
    input wr_en,
    output [7:0] rd_data
);
    reg [7:0] mem [0:15];  // 16 registers, 8-bit each
    
    always @(posedge clk) begin
        if (wr_en)
            mem[wr_addr] <= wr_data;
    end
    
    assign rd_data = mem[rd_addr];
endmodule

Yosys Memory Processing

yosys> read_verilog register_file.v
yosys> hierarchy -check -top register_file
yosys> proc

yosys> memory_collect
# Detects mem[0:15] as memory

yosys> memory_dff
# Since it's small (16 words), converts to flip-flops

yosys> memory_map
# Maps to actual D flip-flops with mux trees

yosys> show
# Shows implementation: 16 8-bit registers + address decoder

Memory vs. Register Decision

  • Small memories (< 32 words): Typically implemented as registers
  • Large memories (> 32 words): Better as RAM blocks (if available)
  • Depends on technology library capabilities

Memory Inference Patterns

Single-Port RAM

reg [7:0] mem [0:255];

always @(posedge clk) begin
    if (wr_en)
        mem[addr] <= wr_data;
    rd_data <= mem[addr];
end

Dual-Port RAM

reg [7:0] mem [0:255];

always @(posedge clk) begin
    if (wr_en)
        mem[wr_addr] <= wr_data;
    rd_data <= mem[rd_addr];
end

ROM (Read-Only Memory)

reg [7:0] rom [0:255];

initial begin
    $readmemh("rom_contents.hex", rom);
end

assign rd_data = rom[addr];

Generic Optimization Passes

After converting behavioral RTL to structural logic, Yosys applies optimization passes to simplify and improve the design.

The opt Pass

Basic Usage

yosys> opt

Runs a comprehensive optimization suite:

  1. opt_expr - Constant folding and expression simplification
  2. opt_merge - Merge identical cells
  3. opt_muxtree - Optimize multiplexer trees
  4. opt_reduce - Reduce word-level operations
  5. opt_clean - Remove unused wires and cells
  6. opt_dff - Optimize flip-flops

Iterative Optimization

yosys> opt
yosys> opt
yosys> opt

Running opt multiple times can find additional optimizations (each pass enables others).

Specific Optimization Passes

Constant Folding (opt_expr)

// Before
assign y = 4 + 5;
assign z = x & 8'hFF;

// After opt_expr
assign y = 9;
assign z = x;  // Masking with all 1's does nothing

Dead Code Elimination (opt_clean)

// Before
wire unused;
wire [3:0] temp;
assign unused = a & b;
assign temp = c + d;
assign out = e | f;

// After opt_clean (if unused and temp aren't used)
assign out = e | f;

Multiplexer Optimization (opt_muxtree)

// Before: Cascaded muxes
wire w1 = sel[0] ? a : b;
wire w2 = sel[1] ? w1 : c;
wire out = sel[2] ? w2 : d;

// After opt_muxtree: Simplified tree structure
// Depending on conditions, might collapse to smaller mux tree

Flip-Flop Optimization (opt_dff)

// Before: DFF with constant input
always @(posedge clk)
    q <= 1'b0;

// After opt_dff: Replaced with constant
assign q = 1'b0;

The clean Pass

Aggressive Cleanup

yosys> clean

More thorough than opt_clean:

  • Removes all unused wires
  • Removes all unused cells
  • Simplifies connections

Use after major transformations to clean up debris.

Checking Design State

Statistics

yosys> stat

Shows:

  • Number of modules
  • Number of wires
  • Number of cells by type
  • Total area estimate

Listing Cells

yosys> ls

Lists all modules and cells in the design.

Cell Type Summary

yosys> stat -top counter

Number of wires:               24
Number of public wires:        7
Number of cells:              15
  $add                         1
  $adff                        4
  $mux                         8
  $eq                          1
  $not                         1

This tells you what's in your design after synthesis passes.

A Complete Generic Synthesis Script

Putting it all together:

# synthesis.ys
# Complete generic synthesis flow

# 1. Read RTL
read_verilog -sv design.sv

# 2. Elaborate hierarchy
hierarchy -check -top my_design

# 3. High-level synthesis
proc          # Convert behavioral to structural
fsm           # Optimize FSMs
memory        # Handle memories

# 4. Optimize
opt           # First optimization pass
opt           # Second pass (often finds more)

# 5. Generate visual output
show -prefix generic_netlist -format svg

# 6. Statistics
stat

# 7. Write output
write_verilog -noattr generic_netlist.v

Running the Script

$ yosys -s synthesis.ys

This produces:

  • Generic gate-level netlist (not yet technology-mapped)
  • Statistics on cell counts and area
  • Visual representation of the design

Debugging Yosys Synthesis

Common Issues and Solutions

Issue: "Module not found"

ERROR: Module 'adder' not found!

Solution: Make sure you've read all necessary files before hierarchy.

Issue: "Unable to detect top module"

ERROR: Can't auto-detect top module.

Solution: Explicitly specify top with hierarchy -top module_name.

Issue: Undriven wires

Warning: Wire 'signal_x' is used but not driven.

Solution: Check your RTL—likely a missing connection.

Issue: Inferred latches

Warning: Inferred latch for signal 'out'.

Solution: Incomplete always @(*) block. Add default case or else clause.

Incremental Development

Save and Load Design State

yosys> write_ilang checkpoint.il
# Later...
yosys> read_ilang checkpoint.il

ILANG is Yosys's internal format—perfect for checkpointing.

Selective Processing

yosys> select -module counter
yosys> proc
yosys> select -clear

Process only specific modules for debugging.

Summary

The Yosys synthesis flow transforms RTL through well-defined stages:

  • read_verilog parses RTL into Yosys's internal representation
  • hierarchy elaborates the design and establishes module relationships
  • proc converts behavioral always blocks into structural logic
  • fsm detects and optimizes finite state machines
  • memory handles memory inference and mapping
  • Generic optimization passes (opt, clean) simplify and reduce logic

This produces an optimized generic netlist ready for technology mapping. Key skills you've developed:

  • Understanding Yosys command structure and scripting
  • Running synthesis passes in the correct order
  • Interpreting intermediate results
  • Debugging common synthesis issues

In the next module, we'll take this generic netlist and map it to an actual technology library, completing the synthesis flow from RTL to gate-level implementation. The generic synthesis flow you've mastered here provides the foundation for technology-specific optimization.

Did you find what you were looking for?

Quiz

No quiz questions available for this article.