3. 组合逻辑设计:多路选择器、加法器、译码器、比较器、乘法器的FPGA实现
各位同学,今天咱们聊聊组合逻辑。说白了,组合逻辑就是那种「输入一变,输出立马跟着变」的电路,没有时钟,没有状态,纯粹靠逻辑门搭出来的硬功夫。
我在金融科技领域做低延迟交易系统时,组合逻辑用得特别多。比如行情解析中的协议字段提取,说白了就是一堆多路选择器和译码器在干活。你想想看,交易所的UDP包每秒几百万个,每个包里的字段位置还不一样,不用组合逻辑硬解,靠CPU软解?延迟直接爆炸。
好,咱们一个一个来。
3.1 多路选择器(MUX)—— 数据通道的「开关」
多路选择器,我习惯叫它MUX。功能很简单:从多个输入中选一个送到输出。
FPGA里实现MUX有两种方式:
- case语句:最直观,适合选择路数不多的情况
- 三元运算符:适合2选1,代码简洁
我个人习惯用case语句,因为可读性强,后期维护也方便。不过要注意,case语句要写完整,不然综合出来一堆锁存器,那可就麻烦了。
// 4选1多路选择器
module mux_4to1 (
input [1:0] sel,
input [7:0] a, b, c, d,
output reg [7:0] out
);
always @(*) begin
case (sel)
2'b00: out = a;
2'b01: out = b;
2'b10: out = c;
2'b11: out = d;
default: out = 8'h00; // 一定要写!
endcase
end
endmodule
在FPGA里,MUX其实是用LUT(查找表)实现的。一个6输入LUT可以实现4选1的MUX,还富余两个输入。所以别担心资源,FPGA天生就是干这个的。
3.2 加法器 —— 从半加器到超前进位
加法器是算术逻辑的基础。FPGA里实现加法,我建议直接用 + 运算符,综合器会帮你优化成最优结构。
但咱们得理解底层原理,不然出了问题都不知道怎么调。
| 类型 | 特点 | 适用场景 |
|---|---|---|
| 半加器 | 两个1位输入,产生和与进位 | 基础教学 |
| 全加器 | 三个1位输入(含进位),产生和与进位 | 级联构成多位加法器 |
| 行波进位加法器 | 全加器级联,延迟随位数线性增长 | 位数少(<8位) |
| 超前进位加法器 | 并行计算进位,延迟固定 | 位数多(>16位),高频设计 |
嗯,这里要注意。在FPGA里,行波进位加法器其实没那么差。因为FPGA的进位链是专用的硬件资源,延迟很小。我测过,32位行波进位加法器在Xilinx的器件上能跑到400MHz以上。
assign sum = a + b; 就行。综合器会自动选择最优结构。别自己手搓加法器,除非你在做教学或者有特殊要求。
3.3 译码器 —— 地址解析的利器
译码器,说白了就是把一个小的编码,翻译成大的控制信号。在FPGA里最常见的应用就是地址译码。
比如在交易所协议解析中,我们需要根据报文类型字段(比如4位)来生成不同的处理使能信号。这时候译码器就派上用场了。
// 3-8译码器
module decoder_3to8 (
input [2:0] in,
output reg [7:0] out
);
always @(*) begin
out = 8'b0;
out[in] = 1'b1; // 一行搞定,简洁明了
end
endmodule
你看,用 out[in] = 1'b1 这种方式,比写case语句简洁多了。而且综合出来的资源是一样的。
我曾经在项目中用译码器做寄存器组的地址映射。32个寄存器,用5位地址译码,每个寄存器对应一个写使能。这样CPU通过地址总线写数据时,只有目标寄存器被更新,其他寄存器纹丝不动。这个设计在金融交易系统的配置管理模块里用了好几年,稳得很。
3.4 比较器 —— 不只是大于小于
比较器,大家第一反应就是 >、<、==。但在FPGA里,比较器的实现方式会影响资源消耗和时序。
我总结了几种常见场景:
- 相等比较:用XOR + NOR实现,资源最少
- 大小比较:用减法器实现,看符号位
- 范围比较:比如判断一个数是否在[10, 100]之间,可以用两个比较器+与门
price >= lower_limit && price <= upper_limit。这个逻辑如果用两个比较器,会消耗两个LUT。但如果你把范围比较写成 assign in_range = (price - lower_limit) <= (upper_limit - lower_limit);,只需要一个比较器,资源减半。
// 范围比较优化
module range_check (
input [15:0] price,
input [15:0] lower,
input [15:0] upper,
output in_range
);
wire [15:0] range = upper - lower;
wire [15:0] offset = price - lower;
assign in_range = (offset <= range); // 一个比较器搞定
endmodule
3.5 乘法器 —— 资源与速度的权衡
乘法器是组合逻辑里的「大块头」。FPGA里实现乘法,有几种选择:
- 直接用
*运算符:综合器会调用DSP单元,速度快,资源可控 - 用LUT实现:适合小位宽(4位以下),不占DSP
- 用移位相加:适合常数乘法,比如乘以10可以写成
(a << 3) + (a << 1)
我个人建议:能用DSP就别用LUT。DSP是FPGA里专门为乘法优化的硬核,速度快、功耗低。你非要用LUT搭乘法器,不仅占资源,时序还容易崩。
// 使用DSP的乘法器
module multiplier_dsp (
input [15:0] a, b,
output [31:0] prod
);
assign prod = a * b; // 综合器自动映射到DSP
endmodule
// 常数乘法优化
module mul_by_10 (
input [15:0] a,
output [19:0] result
);
assign result = (a << 3) + (a << 1); // a*8 + a*2 = a*10
endmodule
3.6 知识体系总览
说了这么多,咱们用一张图来总结一下这五种组合逻辑的关系和应用场景。
这张图把咱们今天讲的内容串起来了。你看,五种组合逻辑在FPGA加速交易所协议解析中都有用武之地。MUX和译码器负责数据通路的选择和地址解析,加法器、比较器、乘法器负责数值计算和条件判断。
好了,这一章的内容就到这儿。记住一句话:组合逻辑是FPGA设计的基石,把这些基础打牢了,后面做复杂设计才能游刃有余。
公众号:蓝海资料掘金营,微信deep3321