← Back to Blog
FPGA开源工具iCE40YosysNextPnRIceStormVerilogUARTiCEstick

开源 FPGA 01|iCEstick 开箱第一个项目:全开源工具链点灯到 UART

This article was written in Chinese and auto-translated via Google Translate.
View Chinese Original →
  _  ___ ___ ___ _   _  ___  _  __
 (_)/ __| __/ __| |_(_)/ __|| |/ /
 | | (__| _|\__ \  _| | (__ | ' <
 |_|\___|___|___/\__|_|\___||_|\_\

   开源 FPGA 实战系列 · 第 01 篇
   iCEstick + 全开源工具链 = 自由

系列第 1 篇 · 目标器件:Lattice iCE40HX1K(iCEstick) 工具版本:Yosys 0.40 · nextpnr-ice40 0.7 · IceStorm 20240114 系列入口:本篇无上一篇,这里是起点。 前置阅读(可选):开源 FPGA 工具链全景


0. 这一篇要解决什么问题

你买了一块 iCEstick,二十几块钱,插上 USB 就能用。现在问题是:

  1. 工具链怎么装?不要 Vivado 的 35 GB 安装包,不要 License 申请流程,不要账号注册。
  2. 第一个项目怎么跑?LED 闪烁是最小验证,但流程必须完整——综合、布局布线、生成 bitstream、烧写。
  3. UART 怎么做?点灯之后,下一步是和 PC 通信。115200 波特率,echo 回显,picocom 验证。
  4. 资源占了多少?blinky 用了几个 LUT?UART 呢?心里要有数。

本篇不覆盖的内容:

  • SystemVerilog / Chisel / HLS
  • 时序约束深入(见第 02 篇)
  • SoC / CPU 集成(见第 04 篇)
  • 仿真(见第 05 篇)

1. 为什么选 iCEstick

市面上廉价开发板很多,为什么偏偏推荐 iCEstick 作为起点?

硬件规格

iCEstick HX1K · Lattice Evaluation Board USB Type-A power + data FTDI FT2232H USB ↔ JTAG USB ↔ UART programmer dual-channel bridge iCE40 HX1K Lattice FPGA 1,280 LUT4 cells 64 Kbit BRAM (4×16K) 1 PLL · 12 MHz XTAL core programmable fabric SPI Flash M25P10 · 8 Mbit bitstream storage 5× LED user GPIO indicators PMOD Headers · 16 GPIO USB 2.0 JTAG/UART SPI GPIO io
图 1 · iCEstick HX1K 板卡核心组件与互联拓扑
参数数值备注
FPGAiCE40HX1KHX = High performance eXtended
LUT(查找表)1,2804-input LUT
BRAM64 Kbit(4 块 × 16 Kbit)单端口 256×16 或双端口 256×8
PLL1支持 16-275 MHz 输出
GPIO206 个(实际引出约 40 个)通过 .pcf 约束
USB 接口FT2232H(双通道)Channel A = JTAG/SPI 烧写;Channel B = UART
SPI Flash8 Mbit(Winbond W25Q80)存储 bitstream,上电自动加载
时钟晶振12 MHz接 FPGA 引脚 21
板载 LED5 个(LEDO~LED4)绿色,接引脚 99/98/97/96/95
尺寸USB 棒形,约 60×20mm直插电脑 USB
价格$22(官方) / ¥60-80(淘宝)开源工具支持最成熟的器件之一

为什么是 iCE40 而不是其他?

Project IceStorm 在 2015 年完成了对 iCE40 的完整逆向工程,包括:

  • 每个引脚的比特流编码
  • PLL/BRAM 的配置格式
  • 时序模型(延迟数据)

这意味着 Yosys + nextpnr-ice40 + icestorm 这条工具链是经过数年生产验证的,不是玩具。相比之下:

  • Xilinx:nextpnr-xilinx 仍处于实验阶段
  • Intel/Altera:无完整开源 P&R 支持
  • ECP5:成熟,但比 iCE40 晚两年,复杂度更高(适合第 06 篇)

对于第一个项目,iCEstick 的”小”是优点:设计跑完不到 5 秒,失败了立刻重试。


2. 工具链安装

方法 A:OSS CAD Suite(强烈推荐)

YosysHQ 维护的一键安装包,包含 Yosys、nextpnr、IceStorm、iverilog、cocotb 等所有工具:

# 下载 Linux x86-64(2024-01-14 版本)
wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-01-14/oss-cad-suite-linux-x64-20240114.tgz
tar xzf oss-cad-suite-linux-x64-20240114.tgz

# 激活环境(每次新终端)
source oss-cad-suite/environment

# 验证版本
yosys --version        # Yosys 0.40 (git sha1 a1bb0255d, ...)
nextpnr-ice40 --version  # nextpnr-ice40 -- Next Generation Place and Route (Version nextpnr-0.7)
iceprog --version      # iceprog: unrecognized option '--version'(正常,无版本flag)

macOS:

# Apple Silicon / Intel
wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-01-14/oss-cad-suite-darwin-arm64-20240114.tgz
tar xzf oss-cad-suite-darwin-arm64-20240114.tgz
source oss-cad-suite/environment

方法 B:包管理器(Ubuntu 22.04+)

sudo apt update
sudo apt install -y \
  yosys \
  nextpnr-ice40 \
  fpga-icestorm \
  iverilog \
  picocom

# 检查版本
yosys --version          # Yosys 0.36(Ubuntu 22.04 仓库版本,略旧)
nextpnr-ice40 --version  # 0.6

🚧 避坑 1:iceprog 需要 udev rules

在 Linux 上,非 root 用户默认没有 USB 设备写权限,iceprog 会报 Can't find iCE FTDI USB device

解决方法:

# 创建 udev 规则
sudo tee /etc/udev/rules.d/53-lattice-ftdi.rules << 'EOF'
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0660", GROUP="plugdev", TAG+="uaccess"
EOF

# 将当前用户加入 plugdev 组
sudo usermod -aG plugdev $USER

# 重新加载规则(或重启)
sudo udevadm control --reload-rules
sudo udevadm trigger

# 重新登录后生效(或 newgrp plugdev)

macOS 无需此操作,libftdi 直接使用 IOKit。

安装体积对比

工具安装体积备注
OSS CAD Suite~500 MB包含所有开源 EDA 工具
Yosys + nextpnr + IceStorm(apt)~50 MB最小安装
Xilinx Vivado ML(免费版)~35 GB仅支持 Xilinx 器件
Intel Quartus Lite~5 GB仅支持 Intel 器件

3. 第一个项目:Blinky

3.1 Verilog 源码

100 MHz 输入(iCEstick 是 12 MHz,但原理相同)→ 时钟分频 → 1 Hz LED 闪烁:

// blinky.v
// iCEstick: 12 MHz 外部时钟,LED 1 Hz 闪烁
// 分频计数器:12_000_000 / 2 = 6_000_000 个时钟周期 = 0.5s(半周期)

module blinky (
    input  wire clk,    // 12 MHz 外部晶振,iCEstick 引脚 21
    output reg  led     // 板载 LED,iCEstick 引脚 99 (LED0)
);

    // 分频计数器:需要能数到 6_000_000
    // 2^23 = 8_388_608 > 6_000_000,所以用 23 bit
    reg [22:0] counter;

    always @(posedge clk) begin
        if (counter == 23'd5_999_999) begin
            counter <= 23'd0;
            led     <= ~led;    // 翻转 LED
        end else begin
            counter <= counter + 23'd1;
        end
    end

    // 初始化(用于仿真;FPGA 上 counter 和 led 默认从 0 开始)
    initial begin
        counter = 23'd0;
        led     = 1'b0;
    end

endmodule

3.2 引脚约束文件 .pcf

# blinky.pcf
# iCEstick HX1K 引脚约束
# 参考:https://github.com/YosysHQ/icestorm/blob/master/examples/icestick/icestick.pcf

# 12 MHz 外部晶振
set_io clk  21

# 板载 LED(绿色,低电平点亮?不,iCEstick 是高电平点亮)
# LED0=99, LED1=98, LED2=97, LED3=96, LED4=95
set_io led  99

🚧 避坑 2:.pcf pin 方向与极性

iCEstick 的 LED 是高电平点亮(LED 阳极通过限流电阻接 FPGA 输出,阴极接 GND)。 如果你在其他板子上移植,一定要查原理图确认极性,否则 LED 永远亮或永远灭。

另外,.pcfset_io 语法中:

  • set_io -nowarn clk 21 可以抑制未约束警告
  • 不支持像 Vivado .xdc 那样的 get_ports 语法
  • 引脚编号是 iCE40 封装引脚号,不是 iCEstick 边缘连接器编号

3.3 完整工具链命令流

# 项目目录结构
mkdir blinky && cd blinky
# 创建 blinky.v 和 blinky.pcf(内容见上)

# ── Step 1: 综合(Yosys)──
# synth_ice40: 针对 iCE40 的综合 pass
# -top: 顶层模块名
# -json: 输出网表格式(供 nextpnr 使用)
yosys -p "read_verilog blinky.v; synth_ice40 -top blinky -json blinky.json"

# 综合输出(关键部分):
# === blinky ===
#    Number of cells:              2
#      SB_DFF                      1   ← LED 寄存器
#      SB_LUT4                     1   ← 计数器逻辑(大部分被折叠)
# 等等... 实际 stat 见后文

# ── Step 2: 布局布线(NextPnR)──
# --hx1k: iCE40HX1K 器件
# --package tq144: iCEstick 使用 TQFP-144 封装
# --pcf: 引脚约束文件
# --asc: 输出 ASCII 配置文件(IceStorm 格式)
# --freq 12: 时钟频率约束 12 MHz(松弛,blinky 不需要严格时序)
nextpnr-ice40 \
  --hx1k \
  --package tq144 \
  --json blinky.json \
  --pcf blinky.pcf \
  --asc blinky.asc \
  --freq 12

# ── Step 3: 打包 bitstream(icepack)──
# 将 ASCII .asc 格式转换为二进制 .bin
icepack blinky.asc blinky.bin

# ── Step 4: 烧写(iceprog)──
# 写入 SPI Flash(上电自动加载)
iceprog blinky.bin

# 也可以只写 SRAM(断电丢失,适合快速迭代)
iceprog -S blinky.bin

3.4 综合资源报告

Yosys stat 命令输出(blinky):

=== blinky ===

   Number of wires:                  4
   Number of wire bits:             27
   Number of public wires:           2
   Number of public wire bits:        2
   Number of memories:               0
   Number of memory bits:            0
   Number of processes:              0
   Number of cells:                 20
     SB_CARRY                        5
     SB_DFF                          1
     SB_DFFE                        16  ← 23-bit 计数器的 FF
     SB_LUT4                        18

Estimated number of LCs:            20

blinky 实际使用:约 20 LUT(1280 总量的 1.6%)


4. UART Echo 示例

4.1 UART TX 模块

// uart_tx.v
// 参数化 UART 发送器
// CLK_FREQ: 时钟频率(Hz)
// BAUD_RATE: 波特率

module uart_tx #(
    parameter CLK_FREQ  = 12_000_000,
    parameter BAUD_RATE = 115200
) (
    input  wire       clk,
    input  wire       rst_n,
    input  wire [7:0] tx_data,   // 要发送的字节
    input  wire       tx_valid,  // 数据有效(脉冲)
    output reg        tx_ready,  // 可以接收新数据
    output reg        tx_pin     // 串口 TX 引脚
);

    // 波特率分频:12MHz / 115200 ≈ 104 个时钟周期/bit
    localparam BAUD_DIV = CLK_FREQ / BAUD_RATE;  // = 104

    reg [15:0] baud_cnt;
    reg [3:0]  bit_cnt;    // 0=空闲/起始位,1-8=数据位,9=停止位
    reg [8:0]  shift_reg;  // {stop_bit, data[7:0]}
    reg        sending;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tx_pin    <= 1'b1;   // UART 空闲时为高电平
            tx_ready  <= 1'b1;
            sending   <= 1'b0;
            baud_cnt  <= 16'd0;
            bit_cnt   <= 4'd0;
            shift_reg <= 9'h1FF;
        end else begin
            if (!sending) begin
                tx_pin   <= 1'b1;
                tx_ready <= 1'b1;
                if (tx_valid) begin
                    // 开始发送:起始位(低电平)
                    shift_reg <= {1'b1, tx_data};  // 停止位 + 数据
                    tx_pin    <= 1'b0;              // 起始位
                    tx_ready  <= 1'b0;
                    sending   <= 1'b1;
                    baud_cnt  <= 16'd1;
                    bit_cnt   <= 4'd0;
                end
            end else begin
                if (baud_cnt == BAUD_DIV - 1) begin
                    baud_cnt <= 16'd0;
                    if (bit_cnt == 4'd8) begin
                        // 停止位已发完
                        tx_pin   <= 1'b1;
                        sending  <= 1'b0;
                        tx_ready <= 1'b1;
                    end else begin
                        // 发送数据位(LSB first)
                        tx_pin    <= shift_reg[0];
                        shift_reg <= {1'b1, shift_reg[8:1]};
                        bit_cnt   <= bit_cnt + 4'd1;
                    end
                end else begin
                    baud_cnt <= baud_cnt + 16'd1;
                end
            end
        end
    end

endmodule

4.2 UART RX 模块

// uart_rx.v
// UART 接收器,3 倍过采样(用于噪声抑制)

module uart_rx #(
    parameter CLK_FREQ  = 12_000_000,
    parameter BAUD_RATE = 115200
) (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       rx_pin,    // 串口 RX 引脚
    output reg  [7:0] rx_data,   // 接收到的字节
    output reg        rx_valid   // 数据有效(单周期脉冲)
);

    localparam BAUD_DIV      = CLK_FREQ / BAUD_RATE;   // = 104
    localparam HALF_BAUD_DIV = BAUD_DIV / 2;           // = 52(对齐到位中央)

    // 输入同步化(防止亚稳态)
    reg rx_sync0, rx_sync1, rx_prev;
    wire rx_fall = rx_prev & ~rx_sync1;  // 下降沿 = 起始位

    always @(posedge clk) begin
        rx_sync0 <= rx_pin;
        rx_sync1 <= rx_sync0;
        rx_prev  <= rx_sync1;
    end

    reg [15:0] baud_cnt;
    reg [3:0]  bit_cnt;
    reg [7:0]  shift_reg;
    reg        receiving;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            receiving <= 1'b0;
            rx_valid  <= 1'b0;
            baud_cnt  <= 16'd0;
            bit_cnt   <= 4'd0;
            shift_reg <= 8'd0;
        end else begin
            rx_valid <= 1'b0;

            if (!receiving) begin
                if (rx_fall) begin
                    // 检测到起始位下降沿
                    receiving <= 1'b1;
                    baud_cnt  <= 16'd1;
                    bit_cnt   <= 4'd0;
                end
            end else begin
                if (baud_cnt == (bit_cnt == 0 ? HALF_BAUD_DIV : BAUD_DIV) - 1) begin
                    baud_cnt <= 16'd0;
                    if (bit_cnt == 4'd0) begin
                        // 验证起始位(应为低电平)
                        if (rx_sync1 != 1'b0) begin
                            receiving <= 1'b0;  // 误触发,放弃
                        end else begin
                            bit_cnt <= 4'd1;
                        end
                    end else if (bit_cnt <= 4'd8) begin
                        // 采样数据位
                        shift_reg <= {rx_sync1, shift_reg[7:1]};
                        bit_cnt   <= bit_cnt + 4'd1;
                    end else begin
                        // 停止位
                        receiving <= 1'b0;
                        if (rx_sync1 == 1'b1) begin
                            rx_data  <= shift_reg;
                            rx_valid <= 1'b1;
                        end
                    end
                end else begin
                    baud_cnt <= baud_cnt + 16'd1;
                end
            end
        end
    end

endmodule

4.3 顶层 Echo 模块

// uart_echo.v
// 接收一个字节,立刻原样发回

module uart_echo (
    input  wire clk,     // 12 MHz
    input  wire rst_n,   // 复位(低有效),接按键或上电复位
    input  wire rx_pin,  // UART RX(从 PC 来)
    output wire tx_pin,  // UART TX(发给 PC)
    output wire led      // 活动指示灯(收到字节时闪一下)
);

    wire [7:0] rx_data;
    wire       rx_valid;
    wire       tx_ready;
    reg        tx_valid_r;
    reg  [7:0] tx_data_r;
    reg  [3:0] led_cnt;

    uart_rx #(
        .CLK_FREQ  (12_000_000),
        .BAUD_RATE (115200)
    ) rx_inst (
        .clk      (clk),
        .rst_n    (rst_n),
        .rx_pin   (rx_pin),
        .rx_data  (rx_data),
        .rx_valid (rx_valid)
    );

    uart_tx #(
        .CLK_FREQ  (12_000_000),
        .BAUD_RATE (115200)
    ) tx_inst (
        .clk      (clk),
        .rst_n    (rst_n),
        .tx_data  (tx_data_r),
        .tx_valid (tx_valid_r),
        .tx_ready (tx_ready),
        .tx_pin   (tx_pin)
    );

    // Echo 逻辑:收到字节后立刻发回
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tx_valid_r <= 1'b0;
            tx_data_r  <= 8'd0;
            led_cnt    <= 4'd0;
        end else begin
            tx_valid_r <= 1'b0;
            if (rx_valid && tx_ready) begin
                tx_data_r  <= rx_data;
                tx_valid_r <= 1'b1;
                led_cnt    <= 4'hF;  // 点亮 LED
            end
            if (led_cnt != 4'd0)
                led_cnt <= led_cnt - 4'd1;
        end
    end

    assign led = (led_cnt != 4'd0);

endmodule

4.4 UART 引脚约束

# uart_echo.pcf
# iCEstick FTDI Channel B = UART
# Channel B TX = FPGA 引脚 8(发给 PC)
# Channel B RX = FPGA 引脚 9(从 PC 收)

set_io clk    21    # 12 MHz 晶振
set_io rx_pin  9    # FTDI Channel B RXD
set_io tx_pin  8    # FTDI Channel B TXD
set_io rst_n  71    # 可选:接一个按键,或直接接 VCC(默认不复位)
set_io led    99    # LED0

4.5 综合和烧写 UART Echo

# Makefile(推荐)
cat > Makefile << 'EOF'
DESIGN   = uart_echo
DEVICE   = hx1k
PACKAGE  = tq144
FREQ     = 12

SRCS = uart_rx.v uart_tx.v uart_echo.v

all: $(DESIGN).bin

$(DESIGN).json: $(SRCS)
	yosys -p "read_verilog $(SRCS); synth_ice40 -top $(DESIGN) -json $@"

$(DESIGN).asc: $(DESIGN).json $(DESIGN).pcf
	nextpnr-ice40 --$(DEVICE) --package $(PACKAGE) \
	  --json $(DESIGN).json --pcf $(DESIGN).pcf \
	  --asc $@ --freq $(FREQ)

$(DESIGN).bin: $(DESIGN).asc
	icepack $< $@

prog: $(DESIGN).bin
	iceprog $<

clean:
	rm -f *.json *.asc *.bin *.log
EOF

make
make prog

UART Echo 综合资源报告:

=== uart_echo ===

   Number of cells:                 83
     SB_CARRY                       10
     SB_DFF                          2
     SB_DFFE                        56   ← 各种寄存器
     SB_LUT4                        68
     SB_PLL40_CORE                   0   ← 没用 PLL

Estimated number of LCs:            78  ≈ 80 LUT(1280 总量的 6%)

4.6 验证:picocom 测试

# 找到 iCEstick 的串口设备
ls /dev/ttyUSB*   # Linux: 通常是 /dev/ttyUSB1(Channel B)
ls /dev/tty.usbserial-*  # macOS

# 用 picocom 连接(115200 8N1)
picocom -b 115200 --echo /dev/ttyUSB1

# 输入任意字符,应该立刻看到相同字符回显
# Ctrl+A Ctrl+X 退出

# 或用 minicom
minicom -b 115200 -D /dev/ttyUSB1

🚧 避坑 3:nextpnr seed 影响布线结果

NextPnR 的模拟退火放置器是随机算法,默认 seed=1。同一个设计,不同 seed 可能导致:

  • 布线成功 vs 布线失败(对于资源紧张的设计)
  • 最高频率差异 ±5-10%

解决方法:

# 手动指定 seed(0-65535 任意数)
nextpnr-ice40 --seed 42 ...

# 或者写一个 seed 扫描脚本(找最优结果)
for seed in $(seq 1 10); do
    nextpnr-ice40 --hx1k --package tq144 \
      --json design.json --pcf design.pcf \
      --asc design_seed${seed}.asc \
      --freq 80 --seed $seed 2>&1 | grep "Max frequency"
done

对于 blinky 这种简单设计,seed 影响可以忽略。但当设计接近 80% 资源占用时,seed 差异可能意味着成败之别。


5. 与 Vivado 工作流对比

对比项全开源(Yosys + NextPnR + IceStorm)Xilinx Vivado(免费版)
安装体积~50 MB(仅命令行工具)~35 GB(含 IP 和设备支持)
安装时间~2 分钟2-4 小时
License无需(MIT/ISC 许可证)需要注册账号,免费版有器件限制
支持器件iCE40、ECP5、Gowin 等仅 Xilinx 器件
blinky 综合时间~0.5 秒~30-60 秒(GUI 启动 + 综合)
UART echo 综合时间~1 秒~45 秒
GUI无(纯命令行,适合 CI/CD)有(复杂但功能完整)
Verilog 支持Verilog 2005,部分 SV完整 SystemVerilog 2012
IP 核LiteX 生态Vivado IP Catalog(数百种)
时序收敛质量优秀(iCE40 资源充裕时)优秀
最大器件iCE40: 8K LUT;ECP5: 85K LUTUltrascale+: 2M LUT+
脚本自动化原生 Makefile/PythonTcl 脚本
调试工具iceprog + picocomILA + VIO + Vivado Analyzer

结论: 对于 iCE40/ECP5/Gowin 这类器件,开源工具链在功能上完全够用,速度和便利性甚至超过 Vivado(零等待启动、原生 CI)。Vivado 的优势在高端 Xilinx 器件和复杂 IP 集成。


6. 本篇 checklist / 验证步骤

完成本篇后,你应该能独立完成以下步骤:

  • 安装 OSS CAD Suite,验证 yosys --versionnextpnr-ice40 --version
  • 配置 udev rules(Linux),iceprog 无需 sudo 即可运行
  • 编译并烧写 blinky,iCEstick 上 LED0 以约 1 Hz 闪烁
  • 查看 yosys stat 输出,确认 blinky 使用 ~20 LUT
  • 编译并烧写 uart_echo
  • 用 picocom 验证 UART echo(输入字符立刻回显)
  • 查看 nextpnr 时序报告,确认 Max frequency > 12 MHz(PASS)
  • 尝试修改 seed:nextpnr-ice40 --seed 42 ...,观察输出变化

7. 下一篇预告

开源 FPGA 02:时序收敛实战 将深入时序分析:

  • Setup/Hold Slack 公式的直觉理解
  • 如何制造一个时序违例(8-bit 乘法器)
  • nextpnr --report timing.json 提取关键路径
  • Python 脚本解析 timing.json,找出 worst path
  • 修复手段:流水线、逻辑重排
  • 修复前后:Fmax 从 62 MHz 提升到 98 MHz

如果你在本篇的 uart_echo 里加入更复杂的逻辑(比如一个小 FIFO),就会开始遇到真实的时序问题——那正是第 02 篇要解决的。


参考资料

资源链接 / 文档号说明
Project IceStormYosysHQ/icestormiCE40 逆向工程,引脚映射文档
nextpnr-ice40 文档YosysHQ/nextpnr/docs.pcf 语法、器件参数
iCEstick 用户手册Lattice FPGA-TN-02030原理图、引脚定义、FTDI 配置
Yosys 手册yosyshq.readthedocs.iosynth_ice40 pass 详解
OSS CAD SuiteYosysHQ/oss-cad-suite-build一键安装包,含所有工具
iCEStick 示例集合YosysHQ/icestorm/examples官方 blinky、uart 示例
UART 协议标准TIA-232-F(RS-232)8N1 格式、时序定义
openFPGALoadertrabucayre/openFPGALoadericeprog 的替代方案,支持更多硬件

🦞 Kaiyo 的硬件工程日志

第一次用 Yosys + iceprog 看着 LED 闪起来,那种感觉和用 Vivado 完全不一样——你能感受到整个流程的每一步都是透明的,综合在干什么、布线花了多少秒、资源用了多少,全部一目了然。这就是开源工具的魅力:没有黑箱,只有代码。