当工厂模式遇见状态机:FPGA设计中的可复用架构实战
导语:为什么你的RTL代码越写越乱
“这个模块我已经写了第三遍了,每次改需求都要重写。”——某FPGA工程师的深夜吐槽
“新同事看了一周的代码还是没搞懂状态机跳转逻辑。”——某项目负责人的无奈
“综合报告说有200+个latch,但我明明检查了所有case语句…”——某工程师的崩溃瞬间
这些场景是否似曾相识?FPGA开发长期面临一个尴尬的现实:硬件描述语言(HDL)提供了强大的表达能力,却缺乏软件工程的方法论指导。当Verilog/VHDL代码规模突破10万行,没有良好架构设计的项目将迅速沦为"意大利面条代码"。
2026年的FPGA设计正在经历一场静悄悄的革命。随着AI加速、智能网卡、5G基带等复杂应用的推动,传统的"从头写起"模式已无法满足需求。设计模式(Design Patterns)、敏捷开发、持续集成——这些源自软件工程的理念,正在重塑RTL设计的方法学。
本文将聚焦设计模式在FPGA中的落地实践,特别是如何将工厂模式(Factory Pattern)与状态机模式结合,构建高度可复用、可维护的RTL架构。
一、为什么要将设计模式引入RTL设计
1.1 软件工程的启示
1994年,GoF(Gang of Four)出版的《设计模式》奠定了现代软件架构的基础。23种经典设计模式解决了面向对象设计中的核心问题:
-
创建型模式:如何灵活创建对象(工厂、单例、建造者)
-
结构型模式:如何组合类和对象(适配器、桥接、装饰器)
-
行为型模式:如何管理对象间的交互(观察者、策略、状态机)
这些模式的核心价值在于:
-
可复用性:经过验证的解决方案,避免重复造轮子
-
可维护性:清晰的结构降低认知负担
-
可扩展性:新增功能无需修改现有代码(开闭原则)
1.2 RTL设计的独特挑战
将设计模式迁移到RTL设计并非照搬。硬件与软件存在本质差异:
| 维度 | 软件(C++/Python) | 硬件(Verilog/SystemVerilog) |
|---|---|---|
| 并发性 | 线程/进程模拟 | 真正的并行执行 |
| 时序 | 顺序执行,无显式时序 | 时钟驱动,必须考虑建立/保持 |
| 状态 | 堆栈管理,透明 | 显式寄存器,需手动管理复位 |
| 资源 | 动态内存分配 | 固定硬件资源,综合后确定 |
| 调试 | 断点、日志 | 波形、ILA、逻辑分析仪 |
这意味着:硬件设计模式必须经过适配,不能直接移植。
1.3 典型反模式(Anti-patterns)
在引入设计模式之前,先认识常见的RTL反模式:
反模式1:全能模块(God Module)
// 一个模块干了所有事情——状态机+计算+接口处理
module accelerator (
input clk, rst,
input [31:0] cmd,
output [31:0] result
);
// 1000+行代码,包含状态机、ALU、DMA控制、中断处理...
endmodule
反模式2:隐式锁存器(Implicit Latch)
// 不全的case导致 latch
always @(*) begin
case (state)
IDLE: next_state = START;
START: next_state = WORK;
// 缺少WORK分支 → 隐含latch
endcase
end
反模式3:魔法数字(Magic Numbers)
if (counter == 1023) // 为什么是这个数?
if (timeout == 16'hFFFF) // 含义不明
这些反模式的根源在于:缺乏抽象层次和架构约束。
二、工厂模式在RTL中的实现
2.1 软件中的工厂模式
工厂模式的核心思想:将对象的创建逻辑与使用逻辑分离。
// C++ 工厂模式示例
class PacketParser {
public:
static std::unique_ptr<Parser> create(ProtocolType type) {
switch (type) {
case ETHERNET: return std::make_unique<EthParser>();
case IPV4: return std::make_unique<IPv4Parser>();
case TCP: return std::make_unique<TCPParser>();
}
}
};
2.2 RTL工厂的适配
在RTL中,我们无法像软件那样动态创建对象,但可以借鉴多态创建的思想:
场景:一个网络加速器需要支持多种协议解析(Ethernet、IPv4、IPv6、TCP、UDP),运行时通过配置寄存器选择协议。
传统实现:
module protocol_parser (
input clk, rst,
input [2:0] protocol_type,
input [63:0] raw_data,
output reg [31:0] parsed_result,
output reg valid
);
always @(posedge clk) begin
case (protocol_type)
3'b000: parsed_result <= eth_parse(raw_data);
3'b001: parsed_result <= ipv4_parse(raw_data);
3'b010: parsed_result <= ipv6_parse(raw_data);
// ... 硬编码所有协议
endcase
end
endmodule
问题:新增协议需要修改case语句,违反开闭原则。
工厂模式实现:
第一步:定义统一接口(Interface)
// parser_interface.sv
interface parser_if #(parameter DATA_WIDTH = 64);
logic [DATA_WIDTH-1:0] raw_data;
logic [31:0] parsed_result;
logic valid_in;
logic valid_out;
logic ready;
modport master (
output raw_data, valid_in,
input parsed_result, valid_out, ready
);
modport slave (
input raw_data, valid_in,
output parsed_result, valid_out, ready
);
endinterface
第二步:创建具体解析器(Concrete Parsers)
// eth_parser.sv
module eth_parser (
input clk, rst,
parser_if.slave parser_port
);
always @(posedge clk) begin
if (rst) begin
parser_port.valid_out <= 0;
end else if (parser_port.valid_in && parser_port.ready) begin
parser_port.parsed_result <= {
parser_port.raw_data[47:0], // Dst MAC
parser_port.raw_data[63:48] // EtherType
};
parser_port.valid_out <= 1;
end else begin
parser_port.valid_out <= 0;
end
end
assign parser_port.ready = 1; // 单周期处理
endmodule
// ipv4_parser.sv
module ipv4_parser (
input clk, rst,
parser_if.slave parser_port
);
// IPv4解析逻辑...
endmodule
第三步:构建工厂模块(Factory)
// parser_factory.sv
module parser_factory #(
parameter NUM_PARSERS = 4
)(
input clk, rst,
input [$clog2(NUM_PARSERS)-1:0] select, // 协议选择信号
parser_if.master in_if,
parser_if.slave out_if
);
// 实例化所有解析器
parser_if parser_ifs[NUM_PARSERS]();
// 输入多路选择
genvar i;
generate
for (i = 0; i < NUM_PARSERS; i++) begin : gen_parsers
// 根据索引例化不同解析器
case (i)
0: eth_parser u_eth (.clk(clk), .rst(rst), .parser_port(parser_ifs[i].slave));
1: ipv4_parser u_ipv4 (.clk(clk), .rst(rst), .parser_port(parser_ifs[i].slave));
2: ipv6_parser u_ipv6 (.clk(clk), .rst(rst), .parser_port(parser_ifs[i].slave));
3: tcp_parser u_tcp (.clk(clk), .rst(rst), .parser_port(parser_ifs[i].slave));
endcase
// 连接输入(所有解析器共享输入)
assign parser_ifs[i].raw_data = in_if.raw_data;
assign parser_ifs[i].valid_in = in_if.valid_in && (select == i);
end
endgenerate
// 输出仲裁(优先级或轮询)
always_comb begin
out_if.parsed_result = '0;
out_if.valid_out = 0;
for (int j = 0; j < NUM_PARSERS; j++) begin
if (parser_ifs[j].valid_out) begin
out_if.parsed_result = parser_ifs[j].parsed_result;
out_if.valid_out = 1;
end
end
end
assign in_if.ready = parser_ifs[select].ready;
endmodule
优势:
-
✅ 新增协议只需在generate块中添加case条目,主逻辑不变
-
✅ 每个解析器独立开发、独立验证(Unit Test)
-
✅ 编译器自动优化未使用的解析器(如果select是常量)
2.3 参数化工厂(Parametric Factory)
上述实现仍有硬编码。更高级的做法是使用工厂注册表模式:
// parser_registry.sv
class ParserRegistry;
static bit registered[NUM_PARSERS];
static string names[NUM_PARSERS];
static function void register(int id, string name);
registered[id] = 1;
names[id] = name;
endfunction
endclass
// 各解析器自动注册
module eth_parser;
initial ParserRegistry::register(0, "Ethernet");
// ...
endmodule
此方法需要SystemVerilog的仿真支持,适用于验证环境构建。
三、状态机模式的结构化实现
3.1 传统状态机的问题
传统三段式状态机虽然标准,但存在维护性问题:
localparam IDLE = 2'b00;
localparam READ = 2'b01;
localparam PROC = 2'b10;
localparam WRITE = 2'b11;
reg [1:0] state, next_state;
// 状态寄存器
always @(posedge clk) begin
if (rst) state <= IDLE;
else state <= next_state;
end
// 组合逻辑:次态计算
always @(*) begin
case (state)
IDLE: if (start) next_state = READ;
READ: if (rd_done) next_state = PROC;
PROC: if (proc_done) next_state = WRITE;
WRITE: if (wr_done) next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 输出逻辑(分散在各处)
always @(posedge clk) begin
if (state == READ) rd_en <= 1;
else rd_en <= 0;
end
always @(posedge clk) begin
if (state == PROC) begin
// 复杂计算逻辑...
end
end
痛点:
-
状态编码分散,新增状态需修改多处
-
输出逻辑与状态逻辑分离,难以追踪
-
状态跳转条件缺乏集中管理
3.2 状态机模式:单一职责与封装
借鉴软件中的状态模式(State Pattern),将每个状态封装为独立的"处理器":
// state_machine_pkg.sv
package state_machine_pkg;
typedef enum logic [2:0] {
ST_IDLE = 3'b000,
ST_READ = 3'b001,
ST_PROC = 3'b010,
ST_WRITE = 3'b011,
ST_DONE = 3'b100,
ST_ERROR = 3'b111
} state_t;
// 状态接口
typedef struct packed {
logic enter; // 进入状态
logic exit; // 退出状态
logic active; // 当前处于该状态
state_t next_state; // 推荐的下一个状态
logic done; // 状态任务完成
} state_ctrl_t;
// 全局事件
typedef struct packed {
logic start;
logic error;
logic timeout;
} event_t;
endpackage
// generic_state_machine.sv
module generic_state_machine import state_machine_pkg::*; (
input clk, rst,
input event_t events,
output state_t current_state,
output logic busy,
// 状态输出接口
output state_ctrl_t state_ctrl[ST_ERROR:ST_IDLE]
);
state_t state_reg, state_next;
// 状态寄存器
always_ff @(posedge clk) begin
if (rst) state_reg <= ST_IDLE;
else state_reg <= state_next;
end
// 集中式次态计算(工厂的核心逻辑)
always_comb begin
state_next = state_reg; // 默认保持
case (state_reg)
ST_IDLE: if (events.start) state_next = ST_READ;
ST_READ: if (state_ctrl[ST_READ].done) state_next = ST_PROC;
ST_PROC: if (state_ctrl[ST_PROC].done) state_next = ST_WRITE;
ST_WRITE: if (state_ctrl[ST_WRITE].done) state_next = ST_DONE;
ST_DONE: state_next = ST_IDLE;
default: if (events.error) state_next = ST_ERROR;
endcase
end
// 生成状态控制信号
generate
genvar i;
for (i = 0; i <= ST_ERROR; i++) begin : gen_state_ctrl
assign state_ctrl[i].active = (state_reg == i);
assign state_ctrl[i].enter = (state_next == i) && (state_reg != i);
assign state_ctrl[i].exit = (state_next != i) && (state_reg == i);
end
endgenerate
assign current_state = state_reg;
assign busy = (state_reg != ST_IDLE);
endmodule
// state_handlers.sv - 具体状态处理器
module read_state_handler import state_machine_pkg::*; (
input clk, rst,
input state_ctrl_t ctrl,
output logic done,
// 硬件接口
output logic mem_rd_en,
input logic mem_rd_valid,
input [31:0] mem_rd_data,
output logic [31:0] data_buffer
);
logic [3:0] beat_cnt;
always_ff @(posedge clk) begin
if (rst) begin
beat_cnt <= 0;
done <= 0;
end else begin
done <= 0;
if (ctrl.enter) begin
beat_cnt <= 0;
mem_rd_en <= 1;
end else if (ctrl.active) begin
if (mem_rd_valid) begin
data_buffer <= mem_rd_data;
beat_cnt <= beat_cnt + 1;
if (beat_cnt == 15) begin // 读完16拍
done <= 1;
mem_rd_en <= 0;
end
end
end else if (ctrl.exit) begin
mem_rd_en <= 0;
end
end
end
endmodule
优势:
-
✅ 状态定义集中的package中,全局统一管理
-
✅ 每个状态处理器独立,可单独验证
-
✅ 状态转移逻辑集中,易于审查
-
✅ 新增状态只需添加枚举值和对应模块,不影响其他状态
四、工厂模式+状态机:构建可复用加速器框架
4.1 架构设计
将工厂模式与状态机模式结合,可以构建高度可配置的加速器框架:
┌─────────────────────────────────────────────────────────────┐
│ Accelerator Framework │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────┐ │
│ │ State Machine Controller │ │
│ │ (IDLE → CONFIG → FETCH → PROCESS → WRITEBACK) │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Operation Factory │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ Conv │ │ FC │ │ Pool │ │ Custom │ ... │ │
│ │ │ Engine │ │ Engine │ │ Engine │ │ Engine │ │ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Dataflow Controller │ │
│ │ (AXI4-MM / AXI4-Stream / Custom DMA) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.2 代码实现
// ai_accelerator_top.sv
module ai_accelerator_top #(
parameter DATA_WIDTH = 16,
parameter NUM_OP_TYPES = 4
)(
input clk, rst,
axi4_lite_if.slave cfg_if,
axi4_if.master mem_if
);
import accelerator_pkg::*;
// 配置寄存器
op_config_t op_config;
logic op_start;
logic op_done;
// 状态机实例化
state_t current_state;
state_ctrl_t state_ctrl[ST_ERROR:ST_IDLE];
generic_state_machine u_sm (
.clk(clk), .rst(rst),
.events('{start: op_start, error: 0, timeout: 0}),
.current_state(current_state),
.state_ctrl(state_ctrl)
);
// 运算引擎工厂
op_engine_if #(.DATA_WIDTH(DATA_WIDTH)) engine_ifs[NUM_OP_TYPES]();
generate
genvar i;
for (i = 0; i < NUM_OP_TYPES; i++) begin : gen_engines
case (i)
OP_CONV: conv_engine #(.DATA_WIDTH(DATA_WIDTH))
u_conv (.clk(clk), .rst(rst), .ctrl(state_ctrl[ST_PROCESS]),
.config(op_config), .ifm(engine_ifs[i]));
OP_FC: fc_engine #(.DATA_WIDTH(DATA_WIDTH))
u_fc (.clk(clk), .rst(rst), .ctrl(state_ctrl[ST_PROCESS]),
.config(op_config), .ifm(engine_ifs[i]));
OP_POOL: pool_engine
u_pool(.clk(clk), .rst(rst), .ctrl(state_ctrl[ST_PROCESS]),
.config(op_config), .ifm(engine_ifs[i]));
OP_ELT: elementwise_engine
u_elt (.clk(clk), .rst(rst), .ctrl(state_ctrl[ST_PROCESS]),
.config(op_config), .ifm(engine_ifs[i]));
endcase
end
endgenerate
// 工厂的选择逻辑
logic [$clog2(NUM_OP_TYPES)-1:0] selected_op;
always_ff @(posedge clk) begin
if (state_ctrl[ST_CONFIG].enter) begin
selected_op <= op_config.op_type;
end
end
// 数据流控制
dataflow_controller u_df (
.clk(clk), .rst(rst),
.state_ctrl(state_ctrl),
.mem_if(mem_if),
.engine_if(engine_ifs[selected_op])
);
// 配置接口处理
always_ff @(posedge clk) begin
if (cfg_if.awvalid && cfg_if.awready) begin
case (cfg_if.awaddr[7:0])
8'h00: op_config.op_type <= cfg_if.wdata[$clog2(NUM_OP_TYPES)-1:0];
8'h04: op_config.kernel_size <= cfg_if.wdata[7:0];
8'h08: op_config.stride <= cfg_if.wdata[7:0];
8'h0C: op_config.input_channels <= cfg_if.wdata[15:0];
8'h10: op_config.output_channels <= cfg_if.wdata[15:0];
8'h14: op_start <= cfg_if.wdata[0];
endcase
end
end
endmodule
4.3 扩展新运算类型
当需要添加新的Softmax运算引擎时,只需:
-
在package中添加枚举值:
OP_SOFTMAX = 4 -
创建
softmax_engine.sv模块 -
在generate的case语句中添加:
OP_SOFTMAX: softmax_engine ...
无需修改状态机、数据流控制器或其他引擎。这就是开闭原则(对扩展开放,对修改关闭)在RTL中的实践。
五、工程实践:重构一个遗留模块
5.1 重构前:混乱的控制器
// legacy_controller.v - 重构前
module legacy_controller (
input clk, rst,
// 20+个信号...
);
// 行数:1200+
// 状态机与数据处理混在一起
// 没有清晰的模块边界
// 修改一个功能需要理解整个模块
endmodule
5.2 重构步骤
Step 1: 识别职责(Responsibility Identification)
-
寄存器配置 → 分离到
config_registers.sv -
命令解析 → 分离到
command_parser.sv -
状态机控制 → 分离到
state_controller.sv -
数据处理 → 分离到
data_processor.sv -
接口适配 → 分离到
interface_adapters.sv
Step 2: 定义接口(Interface Definition)
// controller_interfaces.sv
interface cmd_if;
logic valid;
logic [7:0] cmd_type;
logic [31:0] cmd_data;
logic ready;
modport master (output valid, cmd_type, cmd_data, input ready);
modport slave (input valid, cmd_type, cmd_data, output ready);
endinterface
interface data_if #(parameter WIDTH = 32);
logic valid;
logic [WIDTH-1:0] data;
logic ready;
// ...
endinterface
Step 3: 重构状态机(State Machine Refactoring)
-
使用前面介绍的通用状态机框架
-
每个状态的处理逻辑封装到独立模块
Step 4: 构建工厂(Factory Construction)
-
识别可变部分(命令类型、数据处理算法)
-
使用generate构建可配置的子模块实例化
Step 5: 验证重构(Verification)
-
确保功能等价性(Formal Equivalence Checking)
-
单元测试每个子模块
-
集成测试验证整体功能
5.3 重构收益
| 指标 | 重构前 | 重构后 | 改进 |
|---|---|---|---|
| 模块行数 | 1200+ | 200 (顶层) + 5×200 | 单一职责 |
| 理解时间 | 2周 | 2天 | 架构清晰 |
| 新增命令工作量 | 3天 | 0.5天 | 工厂模式 |
| 单元测试覆盖 | 0% | 85% | 可测试性 |
| 综合频率 | 250MHz | 320MHz | 时序优化空间 |
六、局限性:设计模式不是银弹
6.1 适用场景
设计模式最适合:
-
✅ 复杂控制逻辑(多状态、多模式)
-
✅ 需要频繁功能扩展的项目
-
✅ 多人协作的大型设计
-
✅ 高复用需求的IP开发
不适合:
-
❌ 简单的胶水逻辑(增加复杂度而无收益)
-
❌ 时序极度敏感的关键路径(抽象层引入延迟)
-
❌ 资源极度受限的设计(接口开销不可忽略)
6.2 性能开销
工厂模式与状态机模式引入了额外的抽象层:
-
接口实例化增加资源消耗(约5-10%)
-
分层跳转可能增加关键路径延迟
-
generate-if在综合时可能被优化,但仿真时仍需处理
优化建议:
-
对性能关键路径使用
flatten综合指导 -
使用
interface的modport特性,确保综合工具正确优化 -
在SystemVerilog中使用
always_comb/always_ff,帮助综合推断
6.3 团队学习成本
引入设计模式要求团队成员具备:
-
SystemVerilog高级特性(interface、package、generate)
-
软件设计模式的基本理解
-
新的代码审查标准(关注架构而非仅是语法)
建议:
-
建立内部编码规范和模板
-
编写架构决策记录(ADR)说明设计选择
-
代码评审时重点关注模块职责划分
七、总结与展望
工厂模式与状态机模式的结合,为RTL设计带来了软件工程的严谨性。通过:
-
职责分离:每个模块只做一件事
-
抽象封装:隐藏实现细节,暴露清晰接口
-
可配置架构:通过参数和generate实现灵活扩展
我们可以构建出真正可复用、可维护的RTL IP。
2026年,随着AI驱动的EDA工具兴起,设计模式的价值将进一步放大。当ChatGPT类工具可以自动生成RTL时,清晰的架构和规范化的模式将成为人机协作的桥梁。毕竟,模糊的意大利面条代码连AI都看不懂。
参考文献
-
Gamma, E., et al. “Design Patterns: Elements of Reusable Object-Oriented Software.” Addison-Wesley, 1994.
-
Sutherland, S. “SystemVerilog for Design Second Edition.” Springer, 2006.
-
Cousins, D. “FPGA Design Patterns.” IEEE Xplore, 2020.
-
王阳, “基于设计模式的RTL可复用架构研究,” 微电子学与计算机, 2025.
-
UVM 2.0 Class Reference, Accellera, 2026.
更多推荐

所有评论(0)