组合逻辑设计:加法器、乘法器、比较器、多路选择器、译码器
各位同学,咱们今天聊聊组合逻辑。说白了,组合逻辑就是那种“输入一变,输出立马跟着变”的电路,没有时钟,没有记忆,纯粹靠逻辑门搭出来的硬功夫。
我在做雷达信号处理的时候,组合逻辑用得最多的就是这些基本单元:加法器、乘法器、比较器、多路选择器、译码器。你别看它们基础,但FPGA里90%的运算都离不开它们。今天我就把这几样东西掰开了揉碎了讲给你听。
1. 加法器:FPGA里的“算盘”
加法器是数字信号处理的基础。雷达里做FFT、做滤波,说到底就是一堆加法。
半加器与全加器
半加器只管两个bit相加,不考虑进位输入。全加器则多了一个进位输入。我刚开始学的时候觉得这有啥区别?后来做多bit加法器才发现,进位链才是关键。
// 全加器代码
module full_adder (
input a, b, cin,
output sum, cout
);
assign sum = a ^ b ^ cin;
assign cout = (a & b) | (a & cin) | (b & cin);
endmodule
行波进位加法器
把多个全加器串起来,就是行波进位加法器。优点是结构简单,缺点是速度慢——进位像波浪一样一级一级传下去。我在项目中用过32位的行波进位加法器,结果时序跑不过,后来换了超前进位加法器才搞定。
注意:行波进位加法器的延迟跟位数成正比。位数多了,速度就上不去。FPGA里做高速加法,建议直接用LUT或者DSP48单元。
超前进位加法器
超前进位加法器提前算好进位,不用等上一级。速度更快,但逻辑资源消耗也大。FPGA综合工具一般会自动优化,但我建议你写代码时还是用“+”运算符,让工具去处理。
// 推荐写法:直接用加法运算符
assign result = a + b;
我的经验:在Xilinx的FPGA里,直接用“+”会映射到CARRY4原语,效率很高。别自己手写加法器,除非你有特殊需求。
2. 乘法器:DSP48的用武之地
乘法器在雷达里太常见了。做混频、做相关运算、做矩阵乘法,哪哪都有它。
组合乘法器
最简单的乘法器就是直接用“*”运算符。综合工具会自动调用DSP48硬核。我个人习惯是:能不用LUT实现乘法就不用,因为DSP48又快又省资源。
// 组合乘法器
module mult_comb (
input [7:0] a, b,
output [15:0] result
);
assign result = a * b;
endmodule
乘法器的位宽问题
两个N位二进制数相乘,结果最多是2N位。这个坑我踩过。有一次做雷达脉冲压缩,乘法结果截位没处理好,导致信号失真。嗯,这里要注意:截位时一定要考虑符号位和溢出。
| 输入位宽 | 输出位宽 | 说明 |
|---|---|---|
| 8位 × 8位 | 16位 | 无符号数 |
| 8位 × 8位 | 15位 | 有符号数(去掉符号扩展位) |
关键点:FPGA里的DSP48支持25×18的乘法,做雷达信号处理时尽量把数据位宽对齐到这个范围,效率最高。
3. 比较器:判断大小的利器
比较器在雷达里用来做门限检测、峰值搜索。说白了就是比大小。
等值比较器
判断两个数是否相等,用“==”运算符。综合工具会生成XNOR门组成的比较电路。
// 等值比较
assign equal = (a == b);
大小比较器
判断大于、小于,用“>”、“<”运算符。有符号数和无符号数的比较结果不一样,这个要小心。
// 有符号数比较
assign greater = ($signed(a) > $signed(b));
我曾经踩过的坑:用无符号比较去比有符号数,结果负数被当成很大的正数。比如8'b10000000(-128)和8'b00000001(1)比较,无符号下10000000更大,但实际-128小于1。所以一定要用$signed()。
4. 多路选择器:数据通道的“开关”
多路选择器(MUX)用来从多个输入中选一个输出。雷达里做通道切换、参数选择时经常用。
2选1多路选择器
// 2选1 MUX
module mux2to1 (
input sel,
input [7:0] a, b,
output [7:0] y
);
assign y = sel ? a : b;
endmodule
多路选择器的实现方式
FPGA里的MUX可以用LUT实现,也可以用三态门实现。我个人建议用case语句写多路选择器,代码可读性好,综合效果也不错。
// 4选1 MUX
always @(*) begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
2'b11: y = d;
default: y = 8'b0;
endcase
end
小技巧:如果选择信号是常数,综合工具会优化掉MUX。比如sel一直为0,那MUX就变成了直连线。所以写代码时别担心多余的选择器,工具会帮你优化。
5. 译码器:地址解析的“翻译官”
译码器把输入的编码转换成对应的输出信号。雷达里做地址译码、状态机状态解析时常用。
3-8译码器
// 3-8译码器
module decoder3to8 (
input [2:0] in,
output reg [7:0] out
);
always @(*) begin
case (in)
3'b000: out = 8'b00000001;
3'b001: out = 8'b00000010;
3'b010: out = 8'b00000100;
3'b011: out = 8'b00001000;
3'b100: out = 8'b00010000;
3'b101: out = 8'b00100000;
3'b110: out = 8'b01000000;
3'b111: out = 8'b10000000;
default: out = 8'b00000000;
endcase
end
endmodule
译码器的应用场景
在雷达信号处理中,译码器常用于寄存器地址映射。比如通过地址总线选择不同的配置寄存器。
| 地址 | 寄存器 | 功能 |
|---|---|---|
| 0x00 | CTRL_REG | 控制寄存器 |
| 0x01 | STATUS_REG | 状态寄存器 |
| 0x02 | THRESHOLD_REG | 门限寄存器 |
实战建议:写译码器时一定要加default分支,否则综合出来会有latch。我见过有人没加default,结果仿真正常,上板子就出问题。嗯,这个坑我帮你们踩过了。
总结
组合逻辑设计是FPGA的基石。加法器、乘法器、比较器、多路选择器、译码器,这五个基本单元你玩熟了,雷达信号处理的硬件实现就入门了。
最后说一句:写代码时多用“assign”和“always @(*)”,少用“always @(posedge clk)”。组合逻辑就是组合逻辑,别混进时序逻辑的写法。你想想看,如果组合逻辑里带了时钟,那还叫组合逻辑吗?
好了,今天就到这里。下一章咱们讲时序逻辑设计,到时候会聊到触发器、计数器、移位寄存器这些。记得预习。