统计1的个数
module test(
input [7:0]data_in,
input [3:0]out
);
reg [3:0]cnt;
integer i;
always@(data_in)begin
cnt <= 'd0;
for(i = 0;i < 8;i = i + 1)begin
if(data_in[i])
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
end
assign out = cnt;
//写法二
//assign out = data_in[0] + data_in[1] + data_in[2] + data_in[3] + data_in[4] + data_in[5] + data_in[6] + data_in[7];
endmodule
Testbench:
`timescale 1ns/1ns
module test_tb;
reg [7:0]data_in;
wire [3:0]out;
test test_inst(
.data_in (data_in ),
.out (out )
);
initial begin
data_in = 8'b0000_0000;
#200;
data_in = 8'b1111_0010;
#200;
data_in = 8'b1100_0010;
#200;
data_in = 8'b1111_1111;
#200;
$stop;
end
endmodule
仿真结果:
独热码检测
独热码
独热码是一种二进制编码方式,它的特点是,用来编码这个数的N位bit中,有且只有一位是1,其余位全部为0。因为只有1位是1,所以叫做one-hot (对应的,还有一种编码方式是只有1位是0,其余位都是1,叫做one-cold)
状态机中使用独热码的好处
利用one-hot来编码状态机,好处就是一个flop就表示一个状态,用来判断状态机在哪一个状态的时候就只需要看第几个flop为1即可,而不需要像binary编码那样是所有flop在一起参与比较。这样可以省下一点点逻辑的比较电路的延时,代价显而易见,用one-hot编码状态机需要的寄存器比binary编码要多,这就是一个典型的利用面积换性能的trade off。
方法一:公式法/卡诺图
00 | 01 | 11 | 10 | |
---|---|---|---|---|
00 | 0 | 1 | 0 | 1 |
01 | 1 | 0 | 0 | 0 |
11 | 0 | 0 | 0 | 0 |
10 | 1 | 0 | 0 | 0 |
Y = (A[0] & (!A[1]) & (!A[2]) & (!A[3]))
|((!A[0]) & A[1] & (!A[2]) & (!A[3]))
|((!A[0]) & (!A[1]) & A[2] & (!A[3]))
|((!A[0]) & (!A[1]) & (!A[2]) & A[3]);
方法二:for循环
其实有个很简单的思路,就是从独热码的定义出发:只有一位是1,其余位都是0。那么不管我输入信号有多少位,有一个性质是不变的 – 把这些位各自相加,最后的结果肯定得是1。那么我们就可以利用一个for循环,把每一位相加,最后再把最终的结果和1比较一下,如果是1,那就是独热码,如果不是1,那就是其他的数
module test_1(
input [3:0]data_in,
input out
);
integer i;
reg [3:0]sum
always@(*)begin
sum <= 'd0;
for(i = 0;i < 4; i = i + 1)begin
sum <= sum + data_in[i];
end
end
assign out = sum == 1?1'b1:1'b0;
endmodule
system verilog代码:
function automatic logic is_onehot(input [WIDTH-1:0] sig);
logic [WIDTH-1:0] parity;
parity[0] = sig[0];
for(int i = 1; i < WIDTH; i++)
parity[i] = parity[i-1] ^ sig[i];
is_onehot = parity[i-1] & (&(parity | ~sig));
endfunction
SUM_WIDTH要用$clog2之后再加1,为什么呢?因为加入输入为4位信号,如果SUM_WIDTH要用clog2之后不加1的话,SUM_WIDTH的位宽为2bit,最大表达为11(3),而4位信号全为1时,sum应该为4,表达位宽不够。
方法三:奇偶校验
对于一个4位的二进制数,我们对这4位奇偶校验,利用异或门依次进行每一位,最后的结果如果是奇数个1那么4位异或之后结果就是1,如果偶数个1那么结果就是0。看到了吗,我们要找的独热码其实是奇数个1的特殊情况,即只有1位是1。所以更加巧妙的方法就来自于这个按位异或。
把A相应按位XOR的结果记为P,其中P[0] = A[0], P[i] = A[i] ^ P[i-1]
即对于独热码,我们得到的P会是这样一个数:如果独热码A的第i位为1,那么P的第i-1到第0位都是0,而第i位和更高位都是1。注意,这个规律只对独热码适用,大家可以试验一下其他任何数,只要多于1位是1,那么就不会出现高位连续的都是1,高位必然会出现0。
将A按位取反,然后再和P按位OR起来会得到什么?A既然是独热码,那么除了为1的那一位(假设是第i位),取反之后都会变成1,只有第i位会是0。而P的第i位是1,那么最后按位OR的结果是什么?全部每一位都是1。
那么如果A不是独热码,而是有两个1或者更多1,假设第i位和第k位是1(k是i之后第一个1),我们进行上面的操作会得到什么?P[k]会得到0。A反和P按位OR之后第k位也是0.
一个特殊情况,即A为全0。当A为全0的时候,P也是全0,但是A取反之后是全1,所以A反和P按位OR之后也会得到全1。幸好,特殊情况就只有这一种,我们只需要对A进行一下全0判断就可以了。
system verilog代码:
function automatic logic is_onehot(input [WIDTH-1:0] sig);
logic [WIDTH-1:0] parity;
parity[0] = sig[0];
for(int i = 1; i < WIDTH; i++)
parity[i] = parity[i-1] ^ sig[i];
is_onehot = parity[i-1] & (&(parity | ~sig));
endfunction
向量前导1检测器
设计一个组合逻辑电路,检测输入32位0/1向量中从高到低第一个1出现的位置,如果向量为全0则输出32。例如:
输入00011000 10000000 00000000 00000000,输出3;
输入00000000 11111111 00000000 00000000,输出8;
输入00000000 00000000 00000000 00001010,输出28.
即输出从左向右,出现第一个1的位置
casex、cazez
module seq_head_detect(
input [31:0] data_in,
output pos_out
);
reg [31:0] tmp;
reg [5:0] pos_out_tmp;
reg [5:0] pos_out;
always @(*) begin
tmp = data_in;
casex(tmp)
32'b1xxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd0;
32'b01xx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd1;
32'b001x_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd2;
32'b0001_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd3; //第4位为1
32'b0000_1xxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd4;
32'b0000_01xx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd5;
32'b0000_001x_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd6;
32'b0000_0001_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd7; //8
32'b0000_0000_1xxx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd8;
32'b0000_0000_01xx_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd9;
32'b0000_0000_001x_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd10;
32'b0000_0000_0001_xxxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd11; //12
32'b0000_0000_0000_1xxx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd12;
32'b0000_0000_0000_01xx_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd13;
32'b0000_0000_0000_001x_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd14;
32'b0000_0000_0000_0001_xxxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd15; //16
32'b0000_0000_0000_0000_1xxx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd16;
32'b0000_0000_0000_0000_01xx_xxxx_xxxx_xxxx : pos_out_tmp = 6'd17;
32'b0000_0000_0000_0000_001x_xxxx_xxxx_xxxx : pos_out_tmp = 6'd18;
32'b0000_0000_0000_0000_0001_xxxx_xxxx_xxxx : pos_out_tmp = 6'd19; //20
32'b0000_0000_0000_0000_0000_1xxx_xxxx_xxxx : pos_out_tmp = 6'd20;
32'b0000_0000_0000_0000_0000_01xx_xxxx_xxxx : pos_out_tmp = 6'd21;
32'b0000_0000_0000_0000_0000_001x_xxxx_xxxx : pos_out_tmp = 6'd22;
32'b0000_0000_0000_0000_0000_0001_xxxx_xxxx : pos_out_tmp = 6'd23; //24
32'b0000_0000_0000_0000_0000_0000_1xxx_xxxx : pos_out_tmp = 6'd24;
32'b0000_0000_0000_0000_0000_0000_01xx_xxxx : pos_out_tmp = 6'd25;
32'b0000_0000_0000_0000_0000_0000_001x_xxxx : pos_out_tmp = 6'd26;
32'b0000_0000_0000_0000_0000_0000_0001_xxxx : pos_out_tmp = 6'd27; //28
32'b0000_0000_0000_0000_0000_0000_0000_1xxx : pos_out_tmp = 6'd28;
32'b0000_0000_0000_0000_0000_0000_0000_01xx : pos_out_tmp = 6'd29;
32'b0000_0000_0000_0000_0000_0000_0000_001x : pos_out_tmp = 6'd30;
32'b0000_0000_0000_0000_0000_0000_0000_0001 : pos_out_tmp = 6'd31; //32
default : pos_out_tmp = 6'd32; //全0
endcase
pos_out = pos_out_tmp;
end
endmodule
逐项比较if……esle
module seq_head_detect(
input [31:0] data_in,
output pos_out
);
reg [31:0] tmp;
reg [5:0] pos_out_tmp;
reg [5:0] pos_out;
always @(*) begin
tmp = data_in;
if(tmp[31]) pos_out_tmp = 6'd0;
else if(tmp[30]) pos_out_tmp = 6'd1;
else if(tmp[29]) pos_out_tmp = 6'd2;
else if(tmp[28]) pos_out_tmp = 6'd3;
else if(tmp[27]) pos_out_tmp = 6'd4;
else if(tmp[26]) pos_out_tmp = 6'd5;
else if(tmp[25]) pos_out_tmp = 6'd6;
else if(tmp[24]) pos_out_tmp = 6'd7;
else if(tmp[23]) pos_out_tmp = 6'd8;
else if(tmp[22]) pos_out_tmp = 6'd9;
else if(tmp[21]) pos_out_tmp = 6'd10;
else if(tmp[20]) pos_out_tmp = 6'd11;
else if(tmp[19]) pos_out_tmp = 6'd12;
else if(tmp[18]) pos_out_tmp = 6'd13;
else if(tmp[17]) pos_out_tmp = 6'd14;
else if(tmp[16]) pos_out_tmp = 6'd15;
else if(tmp[15]) pos_out_tmp = 6'd16;
else if(tmp[14]) pos_out_tmp = 6'd17;
else if(tmp[13]) pos_out_tmp = 6'd18;
else if(tmp[12]) pos_out_tmp = 6'd19;
else if(tmp[11]) pos_out_tmp = 6'd20;
else if(tmp[10]) pos_out_tmp = 6'd21;
else if(tmp[9]) pos_out_tmp = 6'd22;
else if(tmp[8]) pos_out_tmp = 6'd23;
else if(tmp[7]) pos_out_tmp = 6'd24;
else if(tmp[6]) pos_out_tmp = 6'd25;
else if(tmp[5]) pos_out_tmp = 6'd26;
else if(tmp[4]) pos_out_tmp = 6'd27;
else if(tmp[3]) pos_out_tmp = 6'd28;
else if(tmp[2]) pos_out_tmp = 6'd29;
else if(tmp[1]) pos_out_tmp = 6'd30;
else if(tmp[0]) pos_out_tmp = 6'd31;
else pos_out_tmp = 6'd32;
pos_out = pos_out_tmp;
end
endmodule
二分法
module seq_head_detect(
input [31:0] data_in,
output [ 5:0] pos_out
);
//mark:assign不能给reg赋值,只能赋给wire
wire [4:0] data_chk;
wire [15:0] data_1;
wire [7:0] data_2;
wire [3:0] data_3;
wire [1:0] data_4;
assign data_chk[4] = |data_in[31:16];//高16位相或,依此类推
assign data_chk[3] = |data_1[15:8];
assign data_chk[2] = |data_2[7:4];
assign data_chk[1] = |data_3[3:2];
assign data_chk[0] = |data_4[1];
assign data_1 = (data_chk[4]) ? data_in[31:16] : data_in[15:0]; //若data_in高16位有1,则data1取其高16位,否则取低16位
assign data_2 = (data_chk[3]) ? data_1[15:8] : data_1[7:0]; //若data_1高8位有1,则data2取其高8位,否则取低8位
assign data_3 = (data_chk[2]) ? data_2[7:4] : data_2[3:0]; //若data_2高4位有1,则data3取其高4位,否则取低4位
assign data_4 = (data_chk[1]) ? data_3[3:2] : data_3[1:0]; //若data_3高2位有1,则data4取其高2位,否则取低2位
assign pos_out = (|data_in) ? {1'b0, ~data_chk} : 6'd32; //若data_in为全0,posout = 6'd32
/*data_in中有1时,用低5位表示足够,则最高位为0*/
/*假定data_in首位为1,则data_chk = 11111,应有pos_out = 00000,类推可知data_chk取反*/
endmodule
Testbench:
`timescale 1ns / 1ps
module tb_seq_head_detect();
reg [31:0] data_in;
wire [5:0] pos_out;
initial begin
data_in = 32'b0000_0000_0000_0000_0000_0000_0000_0000;
#100
data_in = 32'b0000_0001_0000_0000_0000_0000_0000_1101;//第8位,pos_out = 7
#100
data_in = 32'b0000_0000_0000_0000_0010_0000_0000_1101;//第19位,pos_out = 18
end
seq_head_detect u_seq_head_detect(
.data_in (data_in),
.pos_out (pos_out)
);
endmodule
仿真结果:
找出高位或低位第一个出现1
module find_first
#(
parameter WIDTH = 6
)(
input [WIDTH-1:0] req,
output [WIDTH-1:0] grant
);
genvar i;
if(lsb_first)
begin:lsb_to_msb
assign grant[0] = req[0];
for(i=1;i<WIDTH;i=i+1)begin
grant[i] = req[i] & ~|grant[i-1:0];
end
end
else if(!lsb_first)
begin:msb_to_lsb
assign grant[WIDTH-1] = req[WIDTH-1];
for(i=WIDTH-2;i>0;i=i-1)begin
grant[i] = req[i] & ~|grant[WIDTH-1:i+1];
end
end
endmodule