文章目录
1 问题描述
给定位宽为 N
的向量,检测其从 高位到低位(MSB) 第一个 1
出现的位置,向量为 0 时输出 0。示例如下
输入向量 | 输出向量 |
---|---|
8’b0010_1101 | 8’b0010_0000 |
8’b0100_0000 | 8’b0100_0000 |
8’b0000_0000 | 8’b0000_0000 |
下述模块均采用 组合逻辑 实现,例化参数如下
参数 | 描述 |
---|---|
VECTOR_WIDTH | 向量位宽 |
端口定义如下
端口 | 方向 | 类型 | 说明 |
---|---|---|---|
seq | IN | [VECTOR_WIDTH-1:0] | 输入向量 |
pos | OUT | [VECTOR_WIDTH-1:0] | 输出位置 |
1.1 仿真验证
由于不同方法所设计的模块端口完全相同,此处通过修改 VECTOR_DETECT_MODE
参数完成对不同模块的仿真测试,仿真文件如下
`timescale 1ns / 1ns
`define VECTOR_WIDTH 16
`define VECTOR_DETECT_MODE `VECTOR_DETECT_COMPL
`define VECTOR_DETECT_CASEZ 0
`define VECTOR_DETECT_DIVDE 1
`define VECTOR_DETECT_COMPL 2
`define VECTOR_DETECT_SHIFT_XOR 3
module vector_detect_tb;
reg [`VECTOR_WIDTH-1:0] seq;
wire [`VECTOR_WIDTH-1:0] pos;
generate
if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_CASEZ) begin
vector_detect_casez vector_detect_casez_inst (
.seq(seq),
.pos(pos)
);
end else if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_DIVDE) begin
vector_detect_divide #(
.VECTOR_WIDTH(`VECTOR_WIDTH)
) vector_detect_divde_inst (
.seq(seq),
.pos(pos)
);
end else if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_COMPL) begin
vector_detect_compl #(
.VECTOR_WIDTH(`VECTOR_WIDTH)
) vector_detect_compl_inst (
.seq(seq),
.pos(pos)
);
end else if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_SHIFT_XOR) begin
vector_detect_shift_xor #(
.VECTOR_WIDTH(`VECTOR_WIDTH)
) vector_detect_shift_xor_inst (
.seq(seq),
.pos(pos)
);
end
endgenerate
initial begin
for (seq = 0; seq < ~0; seq = seq + 1'b1) begin
#1;
end
#10 $stop;
end
endmodule
注意:暴力破解法(VECTOR_DETECT_CASEZ)不支持参数化,因此修改 VECTOR_WIDTH 参数无效
1.2 性能测试
为对比不同模块的 资源占用 和 时序性能,此处在所有模块上对位宽 16
的向量进行综合测试,所有模块均在 Artix-7 系列 XC7A100TFTG256-1
平台综合实现(采用 默认综合策略),顶层模块和约束如下
`define VECTOR_WIDTH 16
`define VECTOR_DETECT_MODE `VECTOR_DETECT_COMPL
`define VECTOR_DETECT_CASEZ 0
`define VECTOR_DETECT_DIVDE 1
`define VECTOR_DETECT_COMPL 2
`define VECTOR_DETECT_SHIFT_XOR 3
module vector_detect_top (
input clk,
input rst_n,
output reg [`VECTOR_WIDTH-1:0] pos
);
reg [`VECTOR_WIDTH-1:0] seq;
wire [`VECTOR_WIDTH-1:0] pos_w;
generate
if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_CASEZ) begin
vector_detect_casez vector_detect_casez_inst (
.seq(seq),
.pos(pos_w)
);
end else if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_DIVDE) begin
vector_detect_divide #(
.VECTOR_WIDTH(`VECTOR_WIDTH)
) vector_detect_divde_inst (
.seq(seq),
.pos(pos_w)
);
end else if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_COMPL) begin
vector_detect_compl #(
.VECTOR_WIDTH(`VECTOR_WIDTH)
) vector_detect_compl_inst (
.seq(seq),
.pos(pos_w)
);
end else if (`VECTOR_DETECT_MODE == `VECTOR_DETECT_SHIFT_XOR) begin
vector_detect_shift_xor #(
.VECTOR_WIDTH(`VECTOR_WIDTH)
) vector_detect_shift_xor_inst (
.seq(seq),
.pos(pos_w)
);
end
endgenerate
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
seq <= 'b0;
end else begin
seq <= seq + 1'b1;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pos <= 'b0;
end else begin
pos <= pos_w;
end
end
endmodule
create_clock -period 3.000 -name clk [get_ports clk]
不同模块的综合结果如下,综合考虑灵活性、资源占用和时序性能,推荐使用 二分法
FMAX | LUT | WNS | TNS | 参数化 | 资源占用 | 工作频率 | |
---|---|---|---|---|---|---|---|
穷举法 | 435 MHz | 20 | 0.057 ns | 0.156 ns | ❌ | ⭐⭐ | ⭐⭐ |
二分法 | 455 MHz | 19 | 0.006 ns | 0.203 ns | ✅ | ⭐⭐⭐ | ⭐⭐⭐ |
独热加法 | 345 MHz | 33 | 0.007 ns | 0.193 ns | ✅ | ⭐ | ⭐ |
错位相或法 | 435 MHz | 19 | 0.045 ns | 0.216 ns | ✅ | ⭐⭐⭐ | ⭐⭐ |
- FMAX 即最大工作频率,由满足 WNS 和 TNS 均大于 0 条件的最小时钟周期换算而来
- 为了满足时序要求,综合工具往往会通过插入寄存器等方法对模块进行优化,因此同一模块不同工作频率下的资源占用不尽相同。此处资源数据为 FMAX 工作频率下 顶层文件 的资源占用,仅供参考
- 不同模块 FMAX 相同时 不能 通过 WNS 和 TNS 比较性能,因为 Vivado 采用 尽力而为 的综合策略,即以满足时序要求为目标,不追求宽裕的 WNS 和 TNS
2 模块实现
本文提供了常用的 穷举法、二分法、独热加法 和 错位相减法 四种向量 1 检测思路和具体实现,实际上向量 1 检测的思路还有很多,比如遍历向量所有比特位
generate
for (genvar i = 0; i < VECTOR_WIDTH - 1; i = i + 1) begin
assign pos[i] = seq[VECTOR_WIDTH-1:i+1] ? 1'b0 : seq[i];
end
endgenerate
assign pos[VECTOR_WIDTH-1] = seq[VECTOR_WIDTH-1];
或者使用 if-else
if (seq[15]) begin
pos = 16'b1000_0000_0000_0000;
end
else if (seq[14]) begin
pos = 16'b0100_0000_0000_0000;
end
else if ...
上述方法或与本文所提方法 思路相似,或 性能和资源占用 没有优势,因此没有列出
2.1 穷举法
简洁明了,无需多言
module vector_detect_casez (
input [15:0] seq,
output reg [15:0] pos
);
always @(*) begin
casex (seq)
16'b1xxx_xxxx_xxxx_xxxx: pos = 16'b1000_0000_0000_0000;
16'bx1xx_xxxx_xxxx_xxxx: pos = 16'b0100_0000_0000_0000;
16'bxx1x_xxxx_xxxx_xxxx: pos = 16'b0010_0000_0000_0000;
16'bxxx1_xxxx_xxxx_xxxx: pos = 16'b0001_0000_0000_0000;
16'bxxxx_1xxx_xxxx_xxxx: pos = 16'b0000_1000_0000_0000;
16'bxxxx_x1xx_xxxx_xxxx: pos = 16'b0000_0100_0000_0000;
16'bxxxx_xx1x_xxxx_xxxx: pos = 16'b0000_0010_0000_0000;
16'bxxxx_xx