Module 2: Logic Synthesis ยท 2.2 Yosys in OpenLane

The Synthesis Script OpenLane Runs

Section 2.1 was about what synthesis does. Section 2.2 is about running it.

It is tempting to treat run_synthesis as a button that produces a netlist by some proprietary magic. It is not. Under the OpenLane wrapper there is a plain Yosys Tcl script, one you can open, read and hand-modify, running the same commands you now understand from Section 2.1. This article opens that file.

The reference design

From here through the rest of the module, every hands-on example uses the same small design, alu8, an 8-bit ALU with a registered output stage:

module alu8 (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [7:0]  a,
    input  wire [7:0]  b,
    input  wire [2:0]  op,
    output reg  [7:0]  result,
    output reg         zero,
    output reg         carry_out
);
    // op: 0=ADD 1=SUB 2=AND 3=OR 4=XOR 5=SLT 6=SLL 7=SRL
    reg [8:0] alu_ext;
    always @(*) begin
        case (op)
            3'd0: alu_ext = a + b;
            3'd1: alu_ext = a - b;
            3'd2: alu_ext = {1'b0, a & b};
            3'd3: alu_ext = {1'b0, a | b};
            3'd4: alu_ext = {1'b0, a ^ b};
            3'd5: alu_ext = {8'b0, (a < b)};
            3'd6: alu_ext = {1'b0, a} << 1;
            default: alu_ext = {1'b0, a} >> 1;
        endcase
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            result    <= 8'b0;
            zero      <= 1'b0;
            carry_out <= 1'b0;
        end else begin
            result    <= alu_ext[7:0];
            zero      <= (alu_ext[7:0] == 8'b0);
            carry_out <= alu_ext[8];
        end
    end
endmodule

Nothing exotic: eight operations selected by a 3-bit opcode, with the result, a zero flag and a carry-out captured in flip-flops every clock. Small enough to reason about by hand, big enough to produce a non-trivial gate count.

Where the script lives

When you run:

openlane --flow Classic --run-tag alu8_run designs/alu8/config.json

the synthesis stage templates out and runs a Yosys Tcl script into that run’s runs/alu8_run/tmp/synthesis/ directory. Trimmed to the essential commands:

# 1. Read the standard cell library so Yosys knows what is available downstream
read_liberty -lib -ignore_miss_dir -setattr blackbox \
    $::env(LIB_SYNTH)

# 2. Read the RTL
read_verilog -sv $::env(VERILOG_FILES)

# 3. Elaborate and check the top module
hierarchy -check -top alu8

# 4. Generic, technology-independent synthesis
synth -top alu8

# 5. Map generic flip-flops to the library's sequential cells
dfflibmap -liberty $::env(LIB_SYNTH)

# 6. Technology mapping: generic combinational cells -> real standard cells
abc -liberty $::env(LIB_SYNTH) -script $::env(ABC_SCRIPT)

# 7. Clean up and write the final structural netlist
splitnets -ports
opt_clean -purge
write_verilog -noattr -noexpr -nohex -nodec $::env(SAVE_NETLIST)

Every command maps onto a concept from Section 2.1:

Command What it does Concept from Section 2.1
read_liberty Loads the SKY130 .lib so later steps know what cells exist The menu technology mapping selects from
read_verilog Parses the RTL into Yosys’s internal representation
hierarchy -check -top Confirms the top module and checks module references
synth -top Runs generic, technology-independent optimisation The generic-cells stage
dfflibmap Maps generic $_DFF_P_-style flip-flops to real sequential cells Mapped flip-flops
abc -liberty ... -script ... Maps the remaining combinational logic to real cells Technology mapping, guided by a strategy script
write_verilog Emits the final, fully mapped netlist The gate-level netlist

dfflibmap and abc are separate on purpose. Sequential elements and combinational logic are mapped by two different pieces of machinery, because a flip-flop’s Liberty timing arcs (setup and hold relative to a clock) are a different shape of data from a combinational gate’s input-to-output delay.

The -script detail

Look at the abc line: -script $::env(ABC_SCRIPT). That flag points at a small file of ABC commands controlling how mapping searches for a covering, whether it biases toward fewer, larger cells (area) or more, faster cells (delay). It is the whole subject of the next article, SYNTH_STRATEGY.

Every OpenLane stage, synthesis included, leaves its generated scripts, logs and intermediate netlists in the run’s tmp/ directory so you can read exactly what ran. When a synthesis result looks wrong, the generated script and its log are the first place to look.

Practice Exercise

In a run’s runs/<tag>/tmp/synthesis/ directory you find a log line reading:

ABC: WARNING: (unmapped) ... hardcode

Given the seven-command script, which command produced it, and what does “unmapped” most likely mean about that logic?

Solution

The line comes from command 6, abc -liberty ... -script ..., the mapping step. An “(unmapped)” warning means ABC found generic logic it could not cover with any legal combination of cells from the Liberty file, and left it in its generic or blackboxed form rather than inventing a cell. This is the situation the previous article flagged with a stray $_DFF_P_ in a final netlist: a signal to check the RTL or the library for something unaccounted for (an unusual width, a construct the library has no cell for, a library file that did not load), not something to hope disappears later.

In the next article we zoom in on the -script argument: SYNTH_STRATEGY, the config knob that decides whether ABC favours area or delay, and why neither choice is simply correct.

โ† Generic Cells Versus Standard Cells SYNTH_STRATEGY: Area Versus Delay โ†’