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/signalsc:- Select cellsm:- Select modulesa:- 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_latchlogicdata 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?
- Lexical Analysis: Tokenize the input file
- Syntax Parsing: Build abstract syntax tree (AST)
- Semantic Analysis: Check for basic errors
- 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-elseandcasestatements - 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:
proc_clean- Remove empty blocksproc_rmdead- Remove dead codeproc_init- Process initial blocksproc_arst- Extract asynchronous resetsproc_mux- Convert if/case to multiplexersproc_dlatch- Convert to D-latchesproc_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:
fsm_detect- Find FSMs in the designfsm_extract- Extract FSM structurefsm_opt- Optimize FSMsfsm_recode- Re-encode statesfsm_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:
memory_collect- Identify memory structuresmemory_dff- Convert simple memories to flip-flopsmemory_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:
opt_expr- Constant folding and expression simplificationopt_merge- Merge identical cellsopt_muxtree- Optimize multiplexer treesopt_reduce- Reduce word-level operationsopt_clean- Remove unused wires and cellsopt_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_verilogparses RTL into Yosys's internal representationhierarchyelaborates the design and establishes module relationshipsprocconverts behavioral always blocks into structural logicfsmdetects and optimizes finite state machinesmemoryhandles 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.