前言:
本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。
目录如下:
10.数字IC手撕代码-数据位宽转换器(宽-窄,窄-宽转换)
13.数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)
18.数字IC手撕代码-双端口RAM(dual-port-RAM)
...持续更新
更多手撕代码题可以前往 数字IC手撕代码--题库
题目
使用 v/sv 编写如下功能模块,求输入信号序列 din 在 din_vld 为高电平的时间段内的次小值与次小值出现的次数
接口信号如下
module sec_min(
input clk,//时钟
input rst_n,//复位
input [9:0] din, //10bit 无符号数
input din_vld, //输入数据有效信号
output [9:0] dout, //次小值
output [8:0] cnt //次小值出现的次数。溢出时重新计数
);
原理分析
题目要找次小值和次小值出现的次数,我们就可以做两个变量,一个变量存储最小值min,一个变量存储次小值sec_min。这样就有几种可能:
① 输入din < min:
那我们就让min = din, sec_min = min;此时要注意的是,这时候随着次小值和最小值交换顺序,次小值出现的次数也会变成最小值出现的次数,因此不仅要将最小值赋值给次小值,还要将最小值出现次数赋值给次小值出现次数。
② 输入din = min:
需要统计最小值出现的次数,在din<min时,令cnt_sec = cnt_min。
③ 输入din < min_sec:
次小值刷新,最小值不变,令次小值min_sec = din;同时令次小值计数器cnt_sec = 1。
④ 输入din = min_sec:
最小值和次小值均不变,次小值计数器加一。
⑤ 输入din > min_sec:
啥也不干。
代码
module sec_min(
input clk , //时钟
input rst_n , //复位
input [9:0] din , //10bit 无符号数
input din_vld , //输入数据有效信号
output [9:0] dout , //次小值
output [8:0] cnt //次小值出现的次数。溢出时重新计数
);
reg [10:0] min,sec_min; //minimum and second minimum
reg [8:0] cnt_sec,cnt_min;
reg [9:0] dout_reg;
always @(posedge clk)begin
if(!rst_n)begin
cnt_sec <= 9'd0 ;
cnt_min <= 9'd0 ;
dout_reg<= 10'd0 ;
min <= 11'b111_1111_1111;
sec_min <= 11'b111_1111_1111;
end
else if(din_vld)begin
if(din < min)begin
min <= din ;
sec_min <= min ;
cnt_sec <= cnt_min ;
cnt_min <= 9'd1 ;
end
else if(din == min)begin
cnt_min <= cnt_min + 1'b1;
end
else if(din < sec_min)begin
sec_min <= din ;
cnt_sec <= 9'd1 ;
end
else if(din == sec_min)begin
cnt_sec <= cnt_sec + 1'b1;
end
end
end
assign dout = sec_min;
assign cnt = cnt_sec;
endmodule
testbench
module tb_sec_min();
reg rst_n,clk;
reg [9:0] din ;
reg din_vld ;
wire [9:0] dout ;
wire [8:0] cnt ;
task input_num;
input [9:0] number;
begin
@(posedge clk)begin #1
din <= number;
end
end
endtask
initial begin
rst_n <= 1;
clk <= 0;
din_vld <= 0;
#5
rst_n <= 0;
#20
rst_n <= 1;
din_vld <= 1;
repeat(2) begin
input_num(15);
end
repeat(3)begin
input_num(19);
end
input_num(18);
input_num(17);
input_num(16);
input_num(14);
#30
;
end
always #5 clk = ~clk;
sec_min u_sec_min(
.clk (clk) ,
.rst_n (rst_n) ,
.din (din) ,
.din_vld (din_vld) ,
.dout (dout) ,
.cnt (cnt)
);
endmodule
波形图
输入序列为:15,15,19,19,19,18,17,16,16,14
随着序列的输入,最小值会从一开始的不可能的取值(1023),计数器0;依次变为19计数1~3次,18计数1次,17计数1次,16计数1次,15计数2次。
结果验证正确。