ASIC到FPGA移植:10步流程实战

全流程总览

Step 0      Step 1      Step 2      Step 3      Step 4
设计盘点 → 存储器替换 → 时钟替换 → 移除 DFT → 模拟 IP Tie-off
  │           │           │           │           │
  ▼           ▼           ▼           ▼           ▼
grep 盘点   BRAM 封装   PLL 配置   全部 Tie-off  osc/adc 固定

Step 5      Step 6      Step 7      Step 8      Step 9       Step 10
复位同步 → IO Pad → CDC 分析 → 时序约束 → 综合实现 → 板级调试
  │           │           │           │           │            │
  ▼           ▼           ▼           ▼           ▼            ▼
每域同步    Pad 宏移除   异步 FIFO   create_clock  Vivado      ILA 上板
                                          set_false_path  bitstream  14/14 PASS

Step 0:设计盘点

在改任何代码之前,先把 ASIC 设计的家底摸清。

盘点六项清单:

类别 检查什么 怎么查
IP 清单 第三方 IP / 自研 IP / 模拟 IP RTL 文件列表 + 例化层次
存储器 SRAM 类型、深度、位宽、端口 grep -r "BIS_|SRAM" *.v
时钟域 时钟源、频率、门控结构 grep -r "posedge|clock|clk" *.v
复位域 复位信号、同步/异步 grep -r "negedge|rst|reset" *.v
I/O 顶层端口方向、位宽、电压域 顶层模块端口列表
DFT 扫描链 / BIST / 测试模式 grep -r "scan|bist|test_en" *.v

盘点的产出是一张对照表——ASIC 侧有什么 vs FPGA 侧用什么替代。以 Flash 子系统为例:

ASIC 侧:
  存储器: BIS_064K29DB ×2 (64K×29bit 双通道)
  时钟域: sysclk (100MHz)
  模拟口: vpp (inout, 高压编程)
  DFT:    TCK/TDI/TDO/TESTEN/STROBE

FPGA 侧 (NE-7K 载板 + FMCH-FLASH 子卡):
  目标器件: XC7K325T-2FFG900
  载板时钟: 200MHz DDR3 sys + 74.25MHz + 135MHz LVDS
  子卡:     NOR Flash 1Gbit + SPI Flash 512Mbit + eMMC 8GB
  接口:     FMC HPC1, Bank 15/16/17/18, VADJ=3.3V

Step 1:存储器替换

将所有 ASIC 编译 SRAM 实例替换为 FPGA BRAM 封装层。

位宽向上取整:ASIC SRAM 位宽非 8/16/32 倍数时向上取整,多出的 bit 写 0、读忽略。

`ifdef fpga_vision
    // BIS_064K29DB: 64K×29bit → BRAM 64K×32bit
    sram1rw_64k32 #(.AWIDTH(16), .DWIDTH(32)) u_bram (
        .clk  (sysclk),
        .din  ({3'b0, din[28:0]}),  // 29→32bit, 高位补 0
        .dout (dout)                 // 读回 32bit, 忽略高 3bit
    );
`else
    BIS_064K29DB u_bist (...);   // ASIC 原始
`endif

Flash 语义适配层的六层架构详见上一篇文章。


Step 2:时钟生成器替换

将 ASIC PLL/OSC 时钟树替换为 FPGA PLL + 载板时钟源。

NE-7K 载板实际时钟方案——在顶层新增 XCLKIN_P/N 差分时钟口:

XCLKIN_P/N (差分, FPGA 新增顶层端口)
  ↓
顶层 PLL → 30MHz 参考时钟
  ↓
内部 OSC 模块被 FPGA PLL 替代
  → 10MHz / 15MHz / 20MHz / 30MHz / 40MHz (五路)
  → osc_*_trim = 4'h8 (Tie-off 中点)

创建 PLL IP 核:Vivado IP Catalog → Clocking Wizard → PLLE2_ADV,配置输入频率和五路输出分频比。

PLL 选型:没有动态移相需求(无 DDR3)→ 选 PLL(低功耗、配置简单);有 DDR3/源同步接口 → 选 MMCM(动态相位校准)。


Step 3:移除 DFT / BIST / SCAN

红线——全部 Tie-off 或 `ifdef 排除。

`ifdef fpga_vision
    assign scan_enable = 1'b0;
    assign scan_mode   = 1'b0;
    assign test_mode   = 1'b0;
    // MBIST 控制器整块不例化
    // 扫描 MUX 直接选功能路径
    // assign sig = scan_mode ? scan_in : func_sig;  →  assign sig = func_sig;
`endif

综合后检查 synth.log,确认无 DFT 相关 warning。Vivado 中通过 synth_design -verilog_define fpga_vision 启用宏。


Step 4:处理模拟 IP

模拟 IP 处理方式 示例
Flash VPP 拉低 assign vpp = 1'b0
OSC trim Tie-off 中点 4'h8
ADC trim Tie-off 中点 16'h8000
CSM 安全模块 旁路 assign secure = 1'b0

Step 5:复位同步适配

每个时钟域一个独立的异步复位同步释放电路:

module reset_sync (
    input  wire clk,
    input  wire rst_n_async,
    output wire rst_n_sync
);
    reg [1:0] rst_meta;
    always @(posedge clk, negedge rst_n_async) begin
        if (!rst_n_async)
            rst_meta <= 2'b0;
        else
            rst_meta <= {rst_meta[0], 1'b1};
    end
    assign rst_n_sync = rst_meta[1];
endmodule

注意事项:多个复位源(POR、WDT、SW reset)在 FPGA 中合并为一个主复位;禁止在 RTL 中使用 #1 延迟复位(ASIC 仿真用的物理延迟模型,FPGA 不综合)。


Step 6:I/O Pad 替换与引脚分配

移除 ASIC IO pad 宏单元,FPGA IOB 直连:

`ifdef fpga_vision
    assign chip_io_pad   = internal_output;   // 输出直连
    assign internal_input = chip_io_pad;       // 输入直连
`else
    PAD_IN  u_pad_in  (.PAD(pad), .Y(internal));   // ASIC pad
    PAD_OUT u_pad_out (.PAD(pad), .A(internal));
`endif

引脚分配三条原则

  • 时钟 → 专用时钟引脚 (MRCC/SRCC)
  • Bank 电压必须匹配 IO 标准(FMC VADJ=3.3V → LVCMOS33)
  • JTAG → FPGA 专用 JTAG 引脚,不与 ASIC TAP 共用

Step 7:CDC 跨时钟域分析

所有跨时钟域路径必须识别并同步。五种场景的处理方式:

跨域类型 同步策略 验证方式
单 bit 控制信号 2 级同步器 仿真
多 bit 数据总线 异步 FIFO(格雷码指针) 仿真 + 时序分析
慢 → 快时钟域 2 级同步器即可 确认无漏采样
快 → 慢时钟域 展宽 + 同步器 仿真检查展宽宽度
复位信号 异步复位同步释放 Step 5 电路

异步 FIFO 直接用 Vivado FIFO Generator IP——自己写格雷码指针容易出 bug,IP 核经过充分验证。


Step 8:时序约束 (.xdc) 创建

一份完整约束至少覆盖这五类:

# 1. 时钟定义
create_clock -period 33.333 -name xclkin [get_ports {xclkin_p xclkin_n}]
create_generated_clock -name clk_30m -source [get_ports xclkin_p] \
    -divide_by 1 [get_pins clk_pll/clk_out1]

# 2. 异步接口豁免 (Flash 为异步接口)
set_false_path -from [get_ports {nor_*}] -to [get_ports {nor_*}]
set_max_delay -from [get_ports {nor_d[*]}] -to [get_cells *] 50.000

# 3. FMC 子卡异步信号
set_false_path -from [get_ports {spi_*}] -to [get_ports {spi_*}]

# 4. 复位路径例外 (复位同步器第一级为亚稳态容忍)
set_false_path -through [get_pins reset_sync/*/rst_meta[*]]

# 5. 时钟组 (告诉工具哪些时钟是异步的)
set_clock_groups -asynchronous \
    -group [get_clocks xclkin] \
    -group [get_clocks clk_30m] \
    -group [get_clocks {clk_10m clk_15m clk_20m clk_40m}]

Step 9:综合与实现

标准 Tcl 流程:

# 创建项目
create_project -part xc7k325tffg900-2
add_files -scan [glob rtl/*.v]
add_files -fileset constrs_1 constrs/qmx201e_top.xdc
add_files -fileset constrs_1 constrs/FLASH.xdc

# 综合
synth_design -top qmx201e_top -flatten_hierarchy rebuilt
write_checkpoint -force synth.dcp

# 实现
opt_design
place_design
phys_opt_design           # 物理优化
route_design
write_checkpoint -force impl.dcp

# Bitstream
write_bitstream -force qmx201e.bit

综合后常见问题速查:

问题 原因 解法
面积过大 门控时钟未转换 检查是否还有 @(posedge gated_clk)
大量 Latch case/default 不完整 补全 case default 和 else 分支
BRAM 推断失败 地址译码不满足模板 改用 BMG IP 显式例化
三态冲突 assign ... = 'bz FPGA 内部不支持三态,改用 MUX
DSP 推断失败 写法不符合 DSP 模板 检查 * / + 运算符写法

Step 10:板级调试

bitstream 下载到 NE-7K 载板后:

  1. 上电确认:电源轨正常(载板 TI UCD9090 管理)、时钟有输出(示波器测 BUFG 输出)
  2. ILA 探针:例化 Vivado ILA IP 核,抓关键状态机、Flash 读写握手、BRAM 接口
  3. 运行回归测试:14 个 test case 全部 PASS → 移植完成

总结

10 步流程按依赖关系排列,前面是后面的前提:

前 5 步是"修 RTL":盘点 → 存储器 → 时钟 → DFT → 模拟 IP
         ↓
后 5 步是"收口":复位 → IO → CDC → 约束 → 综合上板

三条经验:

  1. 先通后优——先跑通全量综合通过、仿真 PASS,再管时序收敛
  2. 只增不改——ASIC 原始代码一字不动,FPGA 逻辑全部加在 `ifdef 分支里
  3. 保留仿真对照——ASIC 路径和 FPGA 路径在同一个 testbench 中对比运行,行为差异一目了然

本系列三篇覆盖了 ASIC→FPGA 移植从认知、存储到实操的完整链路。第一篇讲 Why(为什么要改),第二篇讲 What(具体怎么适配 Flash),本篇讲 How(操作的顺序和命令)。

posted @ 2026-07-31 16:20  小民的硬件笔记  阅读(0)  评论(0)    收藏  举报