消除未使用的位:创建具有不同尺寸的可合成多维数组

问题描述 投票:2回答:1

这是How can I iteratively create buses of parameterized size to connect modules also iteratively created?的后续问题。答案太复杂,无法在评论中回答,解决方案可能对其他SO有帮助。这个问题遵循self-answer格式。鼓励加入答案。


以下代码适用于并使用双向数组。

module Multiplier #(parameter M = 4, parameter N = 4)(
  input [M-1:0] A, //Input A, size M
  input [N-1:0] B, //Input B, size N
  output [M+N-1:0] P );  //Output P (product), size M+N

wire [M+N-1:0] PP [N-1:0]; // Partial Product array
assign PP[0] = { {N{1'b0}} , { A & {M{B[0]}} } }; // Pad upper bits with 0s
assign P = PP[N-1]; // Product

genvar i;
generate
for (i=1; i < N; i=i+1)
begin: addPartialProduct
    wire [M+i-1:0] gA,gB,gS;  wire Cout;
    assign gA = { A & {M{B[i]}} , {i{1'b0}} };
    assign gB = PP[i-1][M+i-1:0];
    assign PP[i] = { {(N-i){1'b0}}, Cout, gS}; // Pad upper bits with 0s
    RippleCarryAdder#(M+i) adder( .A(gA), .B(gB), .S(gS), .Cin(1'b0), .* );
end
endgenerate

endmodule

从未使用过某些位,例如PP[0][M+N-1:M+1]。合成器通常会在优化过程中删除这些位,并可能发出警告。有些合成器不够先进,无法正确执行此操作。要解决此问题,设计人员必须实现额外的逻辑。在此示例中,所有RippleCarryAdder的参数都将设置为M+N。额外的逻辑浪费了区域并且有效地降低了性能。

如何安全地消除未使用的位?可以使用具有不同尺寸的多维数组吗?最终代码是否可读且可调试?

verilog system-verilog
1个回答
2
投票

可以使用具有不同尺寸的多维数组吗?

简短的回答,没有。

Verilog不支持唯一大小的多维数组。 SystemVerilog支持动态数组,但这些数组无法连接到模块端口,无法合成。

嵌入式代码(例如Perl的EP3,Ruby的eRuby / ruby_it,Python的prepro等)可以生成自定义的命名数组和代码迭代,但参数必须在编译之前进行硬编码。在编译期间,在嵌入式脚本运行之后,给定实例的任何参数的最终值都是发现者。该参数必须被视为全局常量,因此Multiplier#(4,4)Multiplier#(8,8)不能存在于同一个项目中,除非教会脚本如何提取项目的完整层次结构和参数。 (祝你好运编码和维护)。

如何安全地消除未使用的位?

如果合成器的进展不足以排除未使用的位,则可以通过将多维数组展平为具有智能部分选择的一维数组来优化这些位。诀窍是找到可以通过以下步骤实现的等式:

  1. 找到每个零件选择的lsb指数的模式: 假设M是4,每个部分选择的lsb0, 5, 11, 18, 26, 35, ...。将此模式插入WolframAlpha以查找等式a(n) = (n-1)*(n+8)/2。 重复M等于3为模式0, 4, 9, 15, ...得到方程a(n)=(n-1)*(n+6)/2 再次使用M等于5为模式0, 6, 13, 21, 30, ...得到方程a(n)=(n-1)*(n+10)/2。 由于MN的关系是线性的(即多个;没有指数,对数等),因此只需要两个方程来创建可变参数M方程。对于非线性方程,建议使用更多的数据点方程。在这种情况下请注意,对于M=3,4,5模式(n+6),(n+8),(n+10),因此通用方程可以推导为:lsb(n)=(n-1)*(n+2*M)/2
  2. 精细化每个部分的msb指数模式选择: 使用与查找lsb相同的过程(最终是msb(n)=(n**2+(M*2+1)*n-2)/2)。或者用msblsb来定义msb(n)=lsb(n+1)-1

IEEE std 1364-2001(Verilog 2001)引入了带参数的宏和索引部分选择;见§19.3.1'`define'和§4.2.1'矢量位选择和部分选择寻址'。或者分别参见IEEE std 1800-2012§22.5.1'`define'和§11.5.1'向量位选择和部分选择寻址'。这个答案假设SO的模拟器和合成器支持这些功能,因为generate关键字也在IEEE std 1364-2001中引入,参见§12.1.3“生成的实例化”(和IEEE std 1800-2012§27“生成结构”)。对于不完全支持IEEE std 1364-2001的工具,请参阅`ifdef提供的here示例。

由于经常使用计算部分选择范围的函数,因此请使用带参数的`define宏。这有助于防止复制/粘贴错误。宏观定义中额外的()集合是为了确保正确的order of operations`undef模块定义末尾的宏也是一个好主意,可以防止全局空间受到污染。使用扁平阵列,调试可能会变得很困难。通过在生成块的for循环中定义直通连接,信号可以变得可读并且可以以波形进行探测。

module Multiplier #(parameter M = 4, parameter N = 4)(
  input [M-1:0] A, //Input A, size M
  input [N-1:0] B, //Input B, size N
  output [M+N-1:0] P );  //Output P (product), size M+N

// global space macros
`define calc_pp_lsb(n) (((n)-1)*((n)+2*M)/2)
`define calc_pp_msb(n) (`calc_pp_lsb(n+1)-1)
`define calc_pp_range(n) `calc_pp_lsb(n) +: (M+n)

wire [`calc_pp_msb(N):0] PP; // Partial Product
assign PP[`calc_pp_range(1)] = { 1'b0 , { A & {M{B[0]}} } };
assign P = PP[`calc_pp_range(N)]; // Product

genvar i;
generate
for (i=1; i < N; i=i+1)
begin: addPartialProduct
    wire [M+i-1:0] gA,gB,gS;  wire Cout;
    assign gA = PP[`calc_pp_range(i)];
    assign gB = { A & {M{B[i]}} , {i{1'b0}} };
    assign PP[`calc_pp_range(i+1)] = {Cout,gS};
    RippleCarryAdder#(M+i) adder( .A(gA), .B(gB), .S(gS), .Cin (1'b0), .* );
end
endgenerate

// Cleanup global space
`undef calc_pp_range
`undef calc_pp_msb
`undef calc_pp_lsb

endmodule

并排和测试平台的工作示例:http://www.edaplayground.com/s/6/591

最终代码是否可读且可调试?

是的,对于已经学会如何正确使用generate构造的人。生成块的for循环定义局部连线,这些连线局限于循环索引的范围。来自loop-1的gA形式loop-0和gA是唯一的信号,不能相互交互。本地信号可以波形探测,非常适合调试。

© www.soinside.com 2019 - 2024. All rights reserved.