‘’16bits位宽寄存器,用五(六也行)级逻辑判断其中有15个0和1个1‘’,这么道题目,面试无数,几乎没有人能答出来,连给我衍生‘’14个0和2个1‘’的机会都没有。
今天的问题来源于知乎,某大牛说的面试题,据说很少有人给出正确答案。正确答案究竟是什么我也不清楚,我的目标是实现一个FPGA的版本,特记录于此。
说明一下问题,一个16bits的数输入,检查其中1的个数,如果为独热码,即16位中只有1位为1,则输出为1。题目乍看起来很简单,使用Verilog,可以很轻松实现,实现方法不在赘述。出此面试题的人应该也不是考察HDL编码能力,而是考察面试者对底层逻辑的理解,出题人的本意应该是希望使用逻辑门来实现此功能,但本人是个FPGA工程师,就应该遵循FPGA的设计理念,所以决定使用LUT实现该功能。LUT为xilinx FPGA的原语,LUT是基于SRAM的查找表,使用查找表可以完成逻辑门的功能。如4输入LUT,则是一个4输入,1输出的查找表,查找表的深度为2^4=16,查找表的内容则是逻辑函数的真值表。作为FPGA工程师时刻都要清楚,FPGA的组合逻辑几乎都是用LUT完成的。
言归正传,Xilinx 7系列的查找表为6输入,少于6输入可以只覆盖查找表的低位地址,多余6输入需要级联组合,这也就是FPGA中的逻辑层数,级联了几级便是几层逻辑层数。分析题目,16个输入,有一个输入为1则结果为True,多余1个或是全为0则False。这里引入另一个FPGA的设计理念,即并行二字,FPGA中的电路可以并行运行,不同功能模块只要没有先后依赖关系就可以并行处理。个人以为这个是FPGA和GPU的最大差别,GPU为单指令多数据SIMD,FPGA则为MIMD。GPU应该还是串行执行指令的,而FPGA的不能功能模块则为不同指令,这些不同指令可以同时执行。关于GPU与FPGA的差别,仅是个人思路整理,没有深刻研究过GPU。
再继续说检测onehot,检测主要分成三部分,第一输入中只有一位为1则输出1,如0x001,0x002,0x004...2^n等,使用2个6输入LUT,1个4输入LUT,2*6+4=16个输入。6输入LUT真值表为64‘h0000_0001_0001_0116,4输入LUT真值表为16'h0116,每个真值表都能够检出仅存一个1的输入,每个LUT此时能得出的结果是,输出true表示仅有一个1存在,输出Flase表示输入可能有1个以上的1或是全为0,此时三个LUT输出求结果无解。所以还需要引入第二功能,检测全为0的输入,仍然是2个6输入LUT,1个4输入LUT,真值表值为64’d1,16'd1,表示只有0x0地址输出true,即全为0输入,同样有3个输出,此结果可以确定输入全为0,补充了第一步的条件。这两个功能为并行执行,为第一级组合逻辑,共6个输出,这6个输出可以作为函数输入求解。第二级功能使用一个6输入LUT,假设为全为0检测的输出是dout0[2:0],仅有独个1的输出是dout1[2:0],求结果result=dout1[0]&dout0[1]&dout0[2] | dout1[1]&dout0[0]&dout0[2] | dout1[2]&dout0[1]&dout0[0],这个函数正好是个6输入LUT,LUT初始值:INIT 64'hEE80AA80CC808080。前文已述,4输入lut实际使用6输入lut,所以该项目一共用了7个lut,共两层逻辑。
1 module Top( 2 input[15:0] din, 3 output dout 4 ); 5 reg [15:0] dinr; 6 always@(*)begin 7 dinr = din; 8 end 9 wire [3:0] dl1; 10 wire [3:0] dl0; 11 wire dl2; 12 13 wire[2:0] xl1; 14 wire[2:0] xl0; 15 genvar x; 16 generate 17 for (x=0;x<12;x=x+6) 18 begin : w1 19 Lut6_ #(.init(64'h0000_0001_0001_0116)) Lut6_inst( 20 .in(din[x+5:x]), 21 .out(xl1[x/6]) 22 ); 23 end 24 endgenerate 25 Lut4_ #(.init(16'h0116)) Lut4_x( 26 .in(din[15:12]), 27 .out(xl1[2]) 28 ); 29 30 genvar y; 31 generate 32 for (y=0;y<12;y=y+6) 33 begin : w0 34 Lut6_ #(.init(64'd1)) Lut6_inst( 35 .in(din[y+5:y]), 36 .out(xl0[y/6]) 37 ); 38 end 39 endgenerate 40 Lut4_ #(.init(16'd1)) Lut4_y( 41 .in(din[15:12]), 42 .out(xl0[2]) 43 ); 44 assign dout = xl1[0]&xl0[1]&xl0[2] | xl1[1]&xl0[0]&xl0[2] | xl1[2]&xl0[0]&xl0[1]; 45 46 endmodule 47 48 module Lut4_ #( 49 parameter init = 16'h0000 50 ) ( 51 input [3:0] in, 52 output out 53 ); 54 LUT4 #( 55 .INIT(init) // Specify LUT Contents 56 ) LUT4_inst ( 57 .O(out), // LUT general output 58 .I0(in[0]), // LUT input 59 .I1(in[1]), // LUT input 60 .I2(in[2]), // LUT input 61 .I3(in[3]) // LUT input 62 ); 63 endmodule 64 65 module Lut6_ #( 66 parameter init = 64'h0000000000000000 67 ) ( 68 input [5:0] in, 69 output out 70 ); 71 LUT6 #( 72 .INIT(init) // Specify LUT Contents 73 ) LUT6_inst ( 74 .O(out), // LUT general output 75 .I0(in[0]), // LUT input 76 .I1(in[1]), // LUT input 77 .I2(in[2]), // LUT input 78 .I3(in[3]), // LUT input 79 .I4(in[4]), // LUT input 80 .I5(in[5]) // LUT input 81 ); 82 endmodule