【原创】“参数化”的编码器
2赞当你看到这个题目的时候,也许你会觉得它非常的简单。好吧,算你猜对了。但是,我想通过该题目向大家介绍可以实现该设计的方法以及几个不常见或不常用的知识点。
首先,在开始我们的设计之前,介绍一下设计中会用到的两个知识点。
<1>由于该设计是可参数化的,所谓“参数化”就是通过设置输入数据的位宽就能实现相应数据位宽的编码器。假设编码器的输入数据位宽为A,输出数据位宽为B,由我们所学的数电知识可知其关系为B = log2(A),其中log2(x)是以2为底的对数函数。而Verilog HDL中有与其对应的系统函数:$clog2(x)。它在参数定义中的一个例子如下所示:
parameter C_DIN_WIDTH = 8; parameter C_DOUT_WIDTH = $clog2(C_DIN_WIDTH);
<2>这里我要介绍的是关键字wor,相信大家都很少用到它。首先,它是可综合的;其次,它拥有线或的功能;最后,由assign对其进行赋值。例如,一个或门的功能模块如下所示:
/*****************************版权申明********************************** ** 电子技术应用网站, CrazyBird ** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird ** **----------------------------文件信息---------------------------------- ** 文件名: or_gate.v ** 创建者: CrazyBird ** 创建日期: 2016-1-24 ** 版本号: v1.0 ** 功能描述: 或门功能 ** ***********************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module or_gate ( dina, dinb, dout ); //****************************************************************** // 端口定义 //****************************************************************** input dina; input dinb; output wor dout; //****************************************************************** // 或门实现 //****************************************************************** assign dout = dina; assign dout = dinb; endmodule
对应的综合结果如图1所示。
图1 综合结果
接下来介绍实现该编码器的方法,我想到的有两种:第一,通过遍历数据位的方法;第二,通过推导公式的方法。
<1>通过遍历数据位的方法有两种,都是借助for循环来实现的。第一种方法是从输入数据的低位向高位遍历,并判断遍历到的数据位是否为1,若为1,则其对应的二进制码就是编码器的输出。对应的Verilog HDL实现如下(下面所有的设计都已验证过,在本博文中就不给出仿真结果了):
/*****************************版权申明********************************** ** 电子技术应用网站, CrazyBird ** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird ** **----------------------------文件信息---------------------------------- ** 文件名: priority_encode.v ** 创建者: CrazyBird ** 创建日期: 2016-1-24 ** 版本号: v1.0 ** 功能描述: 基于遍历法的参数化编码器 ** ***********************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module priority_encode( din, dout ); // ****************************************************************** // 参数定义 // ****************************************************************** parameter C_DIN_WIDTH = 8; parameter C_DOUT_WIDTH = $clog2(C_DIN_WIDTH); // ****************************************************************** // 端口定义 // ****************************************************************** input [C_DIN_WIDTH-1:0] din; output reg [C_DOUT_WIDTH-1:0] dout; // ****************************************************************** // 内部变量定义 // ****************************************************************** reg [C_DOUT_WIDTH:0] bit_cnt; reg [C_DOUT_WIDTH-1:0] dout_r; // ****************************************************************** // 编码器实现 // ****************************************************************** always @(*) begin dout_r = {(C_DOUT_WIDTH){1'b0}}; for(bit_cnt = 0;bit_cnt < C_DIN_WIDTH;bit_cnt = bit_cnt + 1) begin if(din[bit_cnt] == 1'b1) dout_r = bit_cnt[C_DOUT_WIDTH-1:0]; end dout = dout_r; end endmodule
另一种方法是从输入数据的高位向低位遍历,并判断遍历到的数据位是否为1,若为1,则其对应的二进制码就是编码器的输出。
<2>通过推导公式的方法。下面是8-3编码器的输入输出对应关系:
din dout 0000_0000 000 0000_0001 000 0000_0010 001 0000_0100 010 0000_1000 011 0001_0000 100 0010_0000 101 0100_0000 110 1000_0000 111
根据以上关系可推出以下公式:
dout[0] = din[2'b001] ^ din[2'b011] ^ din[2'b101] ^ din[2'b111]; dout[1] = din[2'b010] ^ din[2'b011] ^ din[2'b110] ^ din[2'b111]; dout[2] = din[2'b100] ^ din[2'b101] ^ din[2'b110] ^ din[2'b111];
再总结下上述公式,编码器输出的某个位可由可能导致该位为1的输入位进行或运算的结果决定,如编码器输入din中所有二进制索引号第0位为1的位进行或运算,便得到编码器输出第0位dout[0];编码器输入din中所有二进制索引号第1位为1的位进行或运算,便得到编码器输出第1位dout[1];依次类推。
在设计中,利用两个for循环实现,一个for用于对编码器输入的索引号进行计数,另一个for用于对编码器输出的索引号进行计数。同时还要判断输入二进制索引号对应位以进行相应的或运算。基于公式的参数化编码器,其Verilog HDL实现如下所示:
/*****************************版权申明********************************** ** 电子技术应用网站, CrazyBird ** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird ** **----------------------------文件信息---------------------------------- ** 文件名: priority_encode.v ** 创建者: CrazyBird ** 创建日期: 2016-1-24 ** 版本号: v1.0 ** 功能描述: 基于公式法的参数化编码器 ** ***********************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module priority_encode( din, dout ); //****************************************************************** // 参数定义 //****************************************************************** parameter C_DIN_WIDTH = 8; parameter C_DOUT_WIDTH = $clog2(C_DIN_WIDTH); //****************************************************************** // 端口定义 //****************************************************************** input [C_DIN_WIDTH-1:0] din; output wor [C_DOUT_WIDTH-1:0] dout; //****************************************************************** // 内部变量定义 //****************************************************************** genvar i; genvar j; //****************************************************************** // 编码器实现 //****************************************************************** generate for(i = 0;i < C_DIN_WIDTH;i = i+1) begin : din_traversal for(j = 0;j < C_DOUT_WIDTH;j = j+1) begin : dout_traversal if(i[j] == 1'b1) assign dout[j] = din[i]; end end endgenerate endmodule