这是本人在实际工作中遇到的设计任务,借此研究状态机设计的优化策略
1. 功能描述
串行输入一组无符号数据,要求输出这组数据的最大值及其索引 和 次大值及其索引,并且满足这两个值的索引相差大于某个数字。
例如串行输入 29 31 1 20 4 6 7 2 100 99 38 52 2 91 97 96 21,两个最大值相距≥3,结果应为(100,9)和(97,15)。
注意99和100相邻,不符合题意。
此处直接给出伪代码
if(data > data_max) begin
if(|data - data_max| > eps) begin
data_submax = data_max;
index_submax = index_max;
data_max = data;
index_max = index;
end
else if(|data - data_min| <= eps) begin
data_submax = ∞;
index_submax = ∞;
data_max = data;
index_max = index;
end
else begin
data_submax = data_submax;
index_submax = index_submax;
data_max = data;
index_max = index;
end
end
else if(data > data_submax) begin
if(|data - data_max| > eps) begin
data_submax = data;
index_submax = index;
data_max = data_max;
index_max = index_max;
end
else begin
data_submax = data_submax ;
index_submax = index_submax ;
data_max = data_max;
index_max = index_max;
end
end
2. 参数设计
Signal | Direction | Width(bits) | Description |
---|---|---|---|
rstn | input | 1 | 异步复位 |
clk | input | 1 | 时钟 |
data | input | 32 | 串行输入数组 |
data_val | input | 1 | 串行输入数组有效标志 |
data_max_val | output | 1 | 数组中最大值有效标志,持续一拍 |
data_max | output | 32 | 数组中最大值 |
index_max | output | 32 | 最大值对应的索引 |
data_submax_val | output | 1 | 数组中次大值有效标志,持续一拍 |
data_submax | output | 32 | 数组中次大值 |
index_submax | output | 32 | 次大值对应的索引 |
Parameter | Units | Description |
---|---|---|
EPOSILON | 禁止的索引偏差 |
3. 逻辑设计
有了伪代码,算法部分就不用费神思考了,所以重点在于时序设计。
按照状态机的思路,整个状态是获取data、计算data与data_max、data_submax的差、更新data_max和data_submax,不断循环,如下图
由此可以直接绘制如下时序图,注意下方时序图中data不是连续输入的,可以在该模块输入处连接一个同步FIFO,然从FIFO中读出数据进行计算。
代码忽略!
4. 逻辑优化
从上述波形图中可以看出WAIT_DATA状态下只有data和data_val动作、GET_DIFF下只有diff_data动作、在UPDATE下只有data_max和data_submax动作,所以可以将状态机优化成流水
注意更新data_max和data_submax时不仅需要diff_data还需要用于计算diff_data的data,所以要对data打一拍,时序图如下
按照上图可以正常输出data_max和data_submax,但是别忘了如何输出索引呢?由于不知道data_val拉高的时刻,所以可以为data_d1标记索引。
由于计算diff_data是时序逻辑,所以需要将data与diff_data对齐,所以data_d1还要再打一拍成data_d2与diff_data对齐
4.1. 代码
module find_max_submax#(
parameter eps = 3
)(
input clk,
input rstn,
input [31:0] data,
input data_val,
output [31:0] data_max,
output data_max_val,
output [7:0] index_max,
output [31:0] data_submax,
output data_submax_val,
output [7:0] index_submax
);
reg [31:0] data_d1;
reg data_d1_val;
reg [7:0] index_d1;
reg [31:0] data_d2;
reg data_d2_val;
reg [7:0] index_d2;
reg [7:0] diff_index_max;
reg [7:0] diff_index_submax;
reg [31:0] data_max_r;
reg data_max_submax_val;
reg [7:0] index_max_r;
reg [31:0] data_submax_r;
reg [7:0] index_submax_r;
always@(posedge clk or negedge rstn) begin
if(!rstn) begin
data_d1 <= 32'd0;
data_d1_val <= 1'b0;
data_d2 <= 32'd0;
data_d2_val <= 1'b0;
end
else begin
data_d1 <= data;
data_d1_val <= data_val;
data_d2 <= data_d1;
data_d2_val <= data_d1_val;
end
end
always@(posedge clk or negedge rstn) begin
if(!rstn)
data_max_submax_val <= 1'b0;
else
data_max_submax_val <= data_d2_val;
end
assign data_max_val = data_max_submax_val;
assign data_submax_val = data_max_submax_val;
always@(posedge clk or negedge rstn) begin
if(!rstn)
index_d1 <= 8'd0;
else if(data_val)
index_d1 <= index_d1 + 8'd1;
end
always@(posedge clk or negedge rstn) begin
if(!rstn)
index_d2 <= 8'd0;
else
index_d2 <= index_d1;
end
always@(posedge clk or negedge rstn) begin
if(!rstn) begin
diff_index_max <= 8'd0;
diff_index_submax <= 8'd0;
end
else if(data_d1_val) begin
diff_index_max <= (index_d1 > index_max)?(index_d1 - index_max):(index_max - index_d1);
diff_index_submax <= (index_d1 > index_submax)?(index_d1 - index_submax):(index_submax - index_d1);
end
end
always@(posedge clk or negedge rstn) begin
if(!rstn) begin
data_max_r <= 32'd0;
index_max_r <= 8'd0;
data_submax_r <= 32'd0;
index_submax_r <= 8'd0;
end
else if(data_d2_val) begin
if(data_d2 > data_max) begin
if(diff_index_max > eps) begin
data_submax_r <= data_max;
index_submax_r <= index_max;
data_max_r <= data_d2;
index_max_r <= index_d2;
end
else if(diff_index_submax <= eps) begin
data_submax_r <= 32'd0;
index_submax_r <= 8'd0;
data_max_r <= data_d2;
index_max_r <= index_d2;
end
else begin
data_submax_r <= data_submax;
index_submax_r <= index_submax;
data_max_r <= data_d2;
index_max_r <= index_d2;
end
end
else if(data_d2 > data_submax) begin
if(diff_index_max > eps) begin
data_submax_r <= data_d2;
index_submax_r <= index_d2;
data_max_r <= data_max;
index_max_r <= index_max;
end
end
end
end
assign data_max = data_max_r;
assign index_max = index_max_r;
assign data_submax = data_submax_r;
assign index_submax = index_submax_r;
endmodule
5. 测试
5.1. testbench
所写的简单tb如下
`timescale 1ns/1ps
module find_max_submax_tb();
logic clk;
logic rstn;
logic [31:0] data;
logic data_val;
logic [31:0] data_max;
logic data_max_val;
logic [7:0] index_max;
logic [31:0] data_submax;
logic data_submax_val;
logic [7:0] index_submax;
initial begin
clk = 1'b0;
forever #2.5 clk = !clk;
end
initial begin
rstn = 1'b1;
#50;
rstn = 1'b0;
#50;
rstn = 1'b1;
end
initial begin
data = 'd0;
data_val = 'd0;
#200;
find_max_submax_test();
end
task find_max_submax_test();
automatic bit [31:0] nums[17] = {'d29,'d31,'d1,'d20, 'd4, 'd6, 'd7, 'd2, 'd100, 'd99, 'd38, 'd52, 'd2, 'd91, 'd97, 'd96, 'd21};
foreach(nums[i]) begin
@(posedge clk);
#0.1;
data = nums[i];
data_val = 1;
repeat($urandom_range(2,5)) begin
@(posedge clk);
#0.1;
data = 'd10;
data_val = 0;
end
end
endtask
find_max_submax#(
.eps (3 )
) DUT(
.clk (clk ),
.rstn (rstn ),
.data (data ),
.data_val (data_val ),
.data_max (data_max ),
.data_max_val (data_max_val ),
.index_max (index_max ),
.data_submax (data_submax ),
.data_submax_val (data_submax_val ),
.index_submax (index_submax )
);
endmodule
5.2. 测试结果
预期输出为(100,9)和(97,15),波形符合预期