文章目录
一、什么是DSP48E1?
DSP48E1是7系列底层资源之一,用来做数字逻辑运算的。代码里的大位宽的加法、乘法都会综合成DSP来计算,我们调用的加法器IP、乘法器IP也大多数是调用这个原语。因此,我们学会底层原语的使用有时候比直接调用IP更灵活。我们打开芯片结构图可以看到:
DSP48E1一列一列的存在slicem周围,这样能尽可能保证我们的运算结果到达FF里的路径最短,降低布线延迟。
二、DSP48E1的内部结构
DSP48E1的结构顶层框图如下所示;对于大部分用户使用情况来看只有五个端口,输入ABCD,输出P:
- 输入A为30位宽,输入B为18位宽,输入C为48位宽,输入D为25位宽,输出P为48位宽。
- ①为预加器,可以将A + D 的结果输出给下一级。
- ②为乘法器,可以将A + D 的结果 和 B相乘,一起实现 (A+D)*B。
- ③为累加器,可以将输入的信号相与、相加、相减,一起实现(A+D)*B ± C 或者 (A+D)*B ± P
- Pattern Detector 为模式匹配器,可以设定对应的匹配模式然后输出对应的信号。
整个DSP48E1的内部详细结构路径如下所示:
- 走线箭头上的 / 表示数据位宽
- 带 * 号的表示与级联相关,走的是专用路径,用户不能直接指定它的路由路径
- 除了上面说的输入 ABCD 和输出 P 五个端口,其它的都是配置信号,用来配置整个DSP工作模式和流水级数的
- Dual B Register 为输入端口B 的流水寄存器,主要是对B进行打拍的
- Dual A,D and Pre-adder 为 输入端口A和D的流水线寄存器和预加器,预加的结果取最低25位给后级的乘法器与B相乘
Dual B Register 和Dual A,D and Pre-adder 的结构如下所示:
- 在Dual B Register中,输入为B端口或者上一级DSP级联过来的B端口,然后可配置流水级数,通过CEB1、CEB2,以及INMODE[4]选择输出。
- 在Dual A,D and Pre-adder中,A的路径和B端口一致,输入端口D可以选择一级流水然后和A相加或者相减,取结果的25位然后输出给后级。
三、DSP48E1原语端口
下图显示了 DSP48E1 原语以及输入和输出端口以及每个端口的位宽:
- 为用户输入数据端口
- 为配置模式端口
- 为数据寄存器使能端口
- 为模式寄存器使能端口
- 为寄存器复位端口
- 级联输入端口
- 为级联输出端口
- 运算结果输出端口
- 级联输出端口
- 模式检测信号输出
- 溢出指示信号
端口名 | 方向 | 位宽 | 描述 |
A | 输入 | 30 | A[24:0] 是乘法器或预加法器的 A 输入,A[29:0] 是第二级加法器/减法器或逻辑函数的 A:B 连接输入的最高有效位 |
ACIN | 输入 | 30 | 上一级DSP48E1的ACOUT 的级联数据输入 |
ACOUT | 输出 | 30 | 级联数据输出至下一个DSP48E1的ACIN |
ALUMODE | 输入 | 4 | 控制第二级逻辑功能的选择,相加、相与、相减等 |
B | 输入 | 18 | 乘法器的 B 输入。B[17:0] 是第二级加法器/减法器或逻辑函数的 A:B 连接输入的最低有效位 |
BCIN | 输入 | 18 | 来自前一个 DSP48E1的BCOUT 的级联数据输入 |
BCOUT | 输出 | 18 | 级联数据输出至下一个DSP48E1片的BCIN |
C | 输入 | 48 | 数据输入至第二级加法器/减法器、模式检测器或逻辑函数 |
CARRYCASCIN | 输入 | 1 | 来自前一个 DSP48E1 切片的 CARRYCASCOUT 的级联进位输入 |
CARRYCASCOUT | 输出 | 1 | 级联进位输出至下一个 DSP48E1 切片的 CARRYCASCIN |
CARRYIN | 输入 | 1 | 来自用户的FPGA逻辑输入 |
CARRYINSEL | 输入 | 3 | 选择进位源 |
CARRYOUT | 输出 | 4 | 累加器/加法器/逻辑单元的每个 12 位字段输出 4 位进位。常规 48 位操作仅使用 CARRYOUT3 |
CEA1 | 输入 | 1 | 第1个 A(输入)寄存器的时钟使能。仅当 AREG = 2 或 INMODE[0]= 1 时才使用 A1 |
CEA2 | 输入 | 1 | 第2个 A(输入)寄存器的时钟使能。仅当 AREG = 1 或 2 且 INMODE[0]=0 时才使用 A2 |
CEAD | 输入 | 1 | 预加法器输出 AD 寄存器的时钟使能 |
CEALUMODE | 输入 | 1 | 为 ALUMODE(控制输入)寄存器提供时钟使能 |
CEB1 | 输入 | 1 | 第1个 B(输入)寄存器的时钟使能。仅当 BREG = 2 或 INMODE[4] = 1 时才使用 B1 |
CEB2 | 输入 | 1 | 第2个 B(输入)寄存器的时钟使能。仅当 BREG = 1 或 2 且 INMODE[4]=0 时才使用 B2 |
CEC | 输入 | 1 | C(输入)寄存器的时钟使能 |
CECARRYIN | 输入 | 1 | CARRYIN(来自 FPGA 逻辑的输入)寄存器的时钟使能 |
CECTRL | 输入 | 1 | OPMODE 和 CARRYINSEL(控制输入)寄存器的时钟使能 |
CED | 输入 | 1 | D(输入)寄存器的时钟使能 |
CEINMODE | 输入 | 1 | INMODE 控制输入寄存器的时钟使能 |
CEM | 输入 | 1 | 乘法 M寄存器的时钟使能 |
CEP | 输入 | 1 | P寄存器的时钟使能 |
CLK | 输入 | 1 | CLK 是 DSP48E1 输入时钟,所有内部寄存器和触发器共用 |
D | 输入 | 25 | 25 位数据输入到预加法器或乘法器的另一个输入 |
INMODE | 输入 | 5 | 这5个控制位选择预加法器、A、B 和 D 输入以及输入寄存器的功能。如果未连接,这些位应默认为 5’b00000 |
MULTSIGNIN | 输入 | 1 | 用于 MACC 扩展的上一个 DSP48E1 切片的乘积结果的符号 |
MULTSIGNOUT | 输出 | 1 | 乘法结果的符号级联到下一个 DSP48E1 切片,用于 MACC 扩展 |
OPMODE | 输入 | 7 | 控制 DSP48E1 切片中的 X、Y 和 Z 多路复用器的输入 |
OVERFLOW | 输出 | 1 | 与模式检测器的适当设置一起使用时的溢出指示器 |
P | 输出 | 48 | 来自第二级加法器/减法器或逻辑函数的数据输出 |
PATTERNBDETECT | 输出 | 1 | P[47:0]与模式匹配指示信号 |
PATTERNDETECT | 输出 | 1 | P[47:0]与模式匹配指示信号 |
PCIN | 输入 | 48 | 从上一个 DSP48E1 片的 PCOUT 级联数据输入到加法器 |
PCOUT | 输出 | 48 | 级联数据输出至下一个DSP48E1片的PCIN |
其他的都是复位各个寄存器的复位信号了,这里就不一一展示了。
四、DSP48E1操作模式
DSP48E1 芯片的数学部分由一个 25 位预加法器、一个 25 位 x 18 位二进制补码乘法器以及三个 48 位数据路径多路复用器(输出 X、Y 和 Z)组成。接下来是一个三输入加法器/减法器或双输入逻辑单元。
下面公式展示了P输出的多种运算组合:
乘法器的两个 43 位部分乘积在发送到加法器/减法器之前会进行符号扩展至 48 位。当不使用第一级乘法器时,48 位双输入按位逻辑函数可实现 AND、OR、NOT、NAND、NOR、XOR 和 XNOR。这些函数的输入是通过 X 和 Z 多路复用器选择的 A:B、C、P 或 PCIN,Y 多路复用器根据逻辑运算选择全 1 或全 0。
4.1 属性端口说明
端口名 | 设置(默认) | 描述 |
ACASCREG | 0, 1, 2 (1) | 与 AREG 一起,选择 A 级联路径 ACOUT 上的 A 输入寄存器数量,此属性必须等于或小于 AREG 值:AREG = 0:ACASCREG 必须为 0 ;AREG = 1:ACASCREG 必须为 1 ;AREG = 2:ACASCREG 可以为 1 或 2 |
ADREG | 0, 1 (1) | 选择 AD寄存器的数量 |
ALUMODEREG | 0, 1 (1) | 选择 ALUMODE 输入寄存器的数量 |
AREG | 0, 1, 2 (1) | 选择 A 输入寄存器的数量。选择 1 时,使用 A2 寄存器 |
BCASCREG | 0, 1, 2 (1) | 与 BREG 一起,选择 B 级联路径 BCOUT 上的 B 输入寄存器数量。此属性必须等于或小于 BREG 值:BREG = 0:BCASCREG 必须为 0 ; BREG = 1:BCASCREG 必须为 1 ;BREG = 2:BCASCREG 可以为 1 或 2 |
BREG | 0, 1, 2 (1) | 选择B输入寄存器的数量。选择1时,使用B2寄存器 |
CARRYINREG | 0, 1(1) | 选择 CARRYIN 输入寄存器的数量 |
CARRYINSELREG | 0, 1(1) | 选择 CARRYINSEL 输入寄存器的数量 |
CREG | 0, 1(1) | 选择 C 输入寄存器的数量 |
DREG | 0, 1(1) | 选择 D 输入寄存器的数量 |
INMODEREG | 0, 1(1) | 选择 INMODE 输入寄存器的数量 |
MREG | 0, 1(1) | 选择 M 流水线寄存器的数量 |
OPMODEREG | 0, 1(1) | 选择 OPMODE 输入寄存器的数量 |
PREG | 0, 1(1) | 选择P输出寄存器的数量 |
A_INPUT | DIRECT, CASCADE (DIRECT) | 选择 A 端口的输入为直接输入 (DIRECT) 或来自前一个切片的级联输入 (CASCADE) |
B_INPUT | DIRECT, CASCADE (DIRECT) | 选择 B 端口的输入为直接输入 (DIRECT) 或来自前一个切片的级联输入 (CASCADE) |
USE_DPORT | TRUE, FALSE (FALSE) | 确定预加器和 D 端口是否使用 |
USE_MULT | NONE, MULTIPLY, DYNAMIC (MULTIPLY) | 选择乘法器的使用。设置为 NONE 可在仅使用加法器/逻辑单元时节省电量。 DYNAMIC 设置表示用户正在动态切换 A*B 和 A:B 操作 |
USE_SIMD | ONE48, TWO24,FOUR12 (ONE48) | 选择加法器/减法器的操作模式。属性设置可以是一个 48 位加法器模式 (ONE48)、两个 24 位加法器模式 (TWO24) 或四个 12 位加法器模式 (FOUR12) |
AUTORESET_PATDET | NO_RESET, RESET_MATCH, RESET_NOT_MATCH (NO_RESET) | 如果在此时钟周期上发生了模式检测事件,则在下一个时钟周期自动重置 P 寄存器(累计值或计数器值) |
MASK | 48 位字段 | 此 48 位值用于在模式检测期间屏蔽某些位。当 MASK 位设置为 1 时,将忽略相应的模式位。当 MASK 位设置为 0 时,将比较模式位 |
PATTERN | 48 位字段 | 该 48 位值用于模式检测器 |
SEL_MASK | 48 位字段 | 选择用于模式检测器的掩码。C 和 MASK 设置用于模式检测器的标准用途(计数器、溢出检测等)。ROUNDING_MODE1(Cbar 左移 1)和 ROUNDING_MODE2(C-bar 左移 2) |
SEL_PATTERN | PATTERN, C (PATTERN) | 选择模式字段的输入源。输入源可以是 48 位动态“C”输入,也可以是 48 位静态属性字段 |
USE_PATTERN_DETECT | NO_PATDET, PATDET (NO_PATDET) | 选择是否使用 (PATDET) 或不使用 (NO_PATDET) 模式检测器和相关功能。此属性仅用于速度规范和仿真模型目的 |
4.2 INMODE[3:0] 功能
INMODE是用来配置Dual B Register 和Dual A,D and Pre-adder流水级数的,具体配置路径如下所示:
整个配置功能表如下所示:
INMODE[4]选择B寄存器输出:
4.3 OPMODE[6:0] 功能
OPMODE的功能主要是控制X、Y、Z多路选择器的输出:
4.4 ALUMODE [3:0] 功能
4 位 ALUMODE 控制第二级加法/减法/逻辑单元的行为。 ALUMODE = 0000 选择 Z + (X + Y + CIN) 形式的加法运算。CIN 是 CARRYIN 多路复用器的输出
CARRYIN 多路复用器输出如下所示:
通过使用第二级三输入加法器,DSP48E1 片能够执行加法、减法和简单的逻辑功能。 下表列出了可以在三输入加法器/减法器/逻辑单元的第二级中实现的逻辑功能:
五、原码、反码、补码的运算
在验证DSP之前,我们来回顾一下在计算机中的运算机制。我们知道计算机只能识别二进制数,而数字分为有符号数,无符号数。那么为了计算机能区分出来有符号数,无符号数,就用最高位作为符号位来区分;也就有了原码、反码、补码的机制。
我们知道,正数的原码、反码、补码相同;负数的反码等于原码(除了符号位)取反,补码等于反码 + 1。而在计算机中,数字通常是由补码的形式存放,计算也是以补码的形式计算。
例如,一个8位正整数 5的原码、反码、补码都 = 0000_0101,一个8位正整数9的原码、反码、补码都=0000_1001;因此 8 + 5 = 0000_0101 + 0000_1001 = 0000_1110 = 14;乘法的话使用的是布斯公式,具体想了解的可以去搜一下,这里不过多讲解。
例如,一个8位的负正数 -6的原码= 1000_0110,反码= 1111_1001,补码=1111_1010;一个8位负整数 -5的原码= 1000_0101,反码=1111_1010,补码=1111_1011;因此 -6 - 5 = 1111_1010 + 1111_1011 = 1_1111_0101,最高位舍弃得到补码1111_0101,符号位不变,取反+ 1 = 1000_1011 = -11。 在DSP48E1里面的运算也是基于二进制补码。
六、仿真验证
前面介绍DSP内部结构以及端口说明,我们来实际仿真一下,根据自己的需求来例化不同的功能。由上面的端口说明可知,大部分使用DSP48E1只需要配置INMODE、OPMODE、ALUMODE就能定制出自己想要的算术功能。DSP48E1原语如下:
DSP48E1 #(
// Feature Control Attributes: Data Path Selection
.A_INPUT("DIRECT"), // Selects A input source, "DIRECT" (A port) or "CASCADE" (ACIN port)
.B_INPUT("DIRECT"), // Selects B input source, "DIRECT" (B port) or "CASCADE" (BCIN port)
.USE_DPORT("FALSE"), // Select D port usage (TRUE or FALSE)
.USE_MULT("MULTIPLY"), // Select multiplier usage ("MULTIPLY", "DYNAMIC", or "NONE")
.USE_SIMD("ONE48"), // SIMD selection ("ONE48", "TWO24", "FOUR12")
// Pattern Detector Attributes: Pattern Detection Configuration
.AUTORESET_PATDET("NO_RESET"), // "NO_RESET", "RESET_MATCH", "RESET_NOT_MATCH"
.MASK(48'h3fffffffffff), // 48-bit mask value for pattern detect (1=ignore)
.PATTERN(48'h000000000000), // 48-bit pattern match for pattern detect
.SEL_MASK("MASK"), // "C", "MASK", "ROUNDING_MODE1", "ROUNDING_MODE2"
.SEL_PATTERN("PATTERN"), // Select pattern value ("PATTERN" or "C")
.USE_PATTERN_DETECT("NO_PATDET"), // Enable pattern detect ("PATDET" or "NO_PATDET")
// Register Control Attributes: Pipeline Register Configuration
.ACASCREG(1), // Number of pipeline stages between A/ACIN and ACOUT (0, 1 or 2)
.ADREG(1), // Number of pipeline stages for pre-adder (0 or 1)
.ALUMODEREG(1), // Number of pipeline stages for ALUMODE (0 or 1)
.AREG(1), // Number of pipeline stages for A (0, 1 or 2)
.BCASCREG(1), // Number of pipeline stages between B/BCIN and BCOUT (0, 1 or 2)
.BREG(1), // Number of pipeline stages for B (0, 1 or 2)
.CARRYINREG(1), // Number of pipeline stages for CARRYIN (0 or 1)
.CARRYINSELREG(1), // Number of pipeline stages for CARRYINSEL (0 or 1)
.CREG(1), // Number of pipeline stages for C (0 or 1)
.DREG(1), // Number of pipeline stages for D (0 or 1)
.INMODEREG(1), // Number of pipeline stages for INMODE (0 or 1)
.MREG(1), // Number of multiplier pipeline stages (0 or 1)
.OPMODEREG(1), // Number of pipeline stages for OPMODE (0 or 1)
.PREG(1) // Number of pipeline stages for P (0 or 1)
)
DSP48E1_inst (
// Cascade: 30-bit (each) output: Cascade Ports
.ACOUT(ACOUT), // 30-bit output: A port cascade output
.BCOUT(BCOUT), // 18-bit output: B port cascade output
.CARRYCASCOUT(CARRYCASCOUT), // 1-bit output: Cascade carry output
.MULTSIGNOUT(MULTSIGNOUT), // 1-bit output: Multiplier sign cascade output
.PCOUT(PCOUT), // 48-bit output: Cascade output
// Control: 1-bit (each) output: Control Inputs/Status Bits
.OVERFLOW(OVERFLOW), // 1-bit output: Overflow in add/acc output
.PATTERNBDETECT(PATTERNBDETECT), // 1-bit output: Pattern bar detect output
.PATTERNDETECT(PATTERNDETECT), // 1-bit output: Pattern detect output
.UNDERFLOW(UNDERFLOW), // 1-bit output: Underflow in add/acc output
// Data: 4-bit (each) output: Data Ports
.CARRYOUT(CARRYOUT), // 4-bit output: Carry output
.P(P), // 48-bit output: Primary data output
// Cascade: 30-bit (each) input: Cascade Ports
.ACIN(ACIN), // 30-bit input: A cascade data input
.BCIN(BCIN), // 18-bit input: B cascade input
.CARRYCASCIN(CARRYCASCIN), // 1-bit input: Cascade carry input
.MULTSIGNIN(MULTSIGNIN), // 1-bit input: Multiplier sign input
.PCIN(PCIN), // 48-bit input: P cascade input
// Control: 4-bit (each) input: Control Inputs/Status Bits
.ALUMODE(ALUMODE), // 4-bit input: ALU control input
.CARRYINSEL(CARRYINSEL), // 3-bit input: Carry select input
.CLK(CLK), // 1-bit input: Clock input
.INMODE(INMODE), // 5-bit input: INMODE control input
.OPMODE(OPMODE), // 7-bit input: Operation mode input
// Data: 30-bit (each) input: Data Ports
.A(A), // 30-bit input: A data input
.B(B), // 18-bit input: B data input
.C(C), // 48-bit input: C data input
.CARRYIN(CARRYIN), // 1-bit input: Carry input signal
.D(D), // 25-bit input: D data input
// Reset/Clock Enable: 1-bit (each) input: Reset/Clock Enable Inputs
.CEA1(CEA1), // 1-bit input: Clock enable input for 1st stage AREG
.CEA2(CEA2), // 1-bit input: Clock enable input for 2nd stage AREG
.CEAD(CEAD), // 1-bit input: Clock enable input for ADREG
.CEALUMODE(CEALUMODE), // 1-bit input: Clock enable input for ALUMODE
.CEB1(CEB1), // 1-bit input: Clock enable input for 1st stage BREG
.CEB2(CEB2), // 1-bit input: Clock enable input for 2nd stage BREG
.CEC(CEC), // 1-bit input: Clock enable input for CREG
.CECARRYIN(CECARRYIN), // 1-bit input: Clock enable input for CARRYINREG
.CECTRL(CECTRL), // 1-bit input: Clock enable input for OPMODEREG and CARRYINSELREG
.CED(CED), // 1-bit input: Clock enable input for DREG
.CEINMODE(CEINMODE), // 1-bit input: Clock enable input for INMODEREG
.CEM(CEM), // 1-bit input: Clock enable input for MREG
.CEP(CEP), // 1-bit input: Clock enable input for PREG
.RSTA(RSTA), // 1-bit input: Reset input for AREG
.RSTALLCARRYIN(RSTALLCARRYIN), // 1-bit input: Reset input for CARRYINREG
.RSTALUMODE(RSTALUMODE), // 1-bit input: Reset input for ALUMODEREG
.RSTB(RSTB), // 1-bit input: Reset input for BREG
.RSTC(RSTC), // 1-bit input: Reset input for CREG
.RSTCTRL(RSTCTRL), // 1-bit input: Reset input for OPMODEREG and CARRYINSELREG
.RSTD(RSTD), // 1-bit input: Reset input for DREG and ADREG
.RSTINMODE(RSTINMODE), // 1-bit input: Reset input for INMODEREG
.RSTM(RSTM), // 1-bit input: Reset input for MREG
.RSTP(RSTP) // 1-bit input: Reset input for PREG
);
6.1 B*D的仿真
我们先只使用乘法器;DSP48E1里面只有一个乘法器资源,两个输入位宽分别是25、18位,因此输出最大位宽为43位。由上面DSP48E1内部结构可知,乘法器的一个输入只能是B端口,另一端口输入可以是A[24:0],D、或者A+D的低25位。
流水分析:
- B端口可配置 0、1、2级流水
- A端口可配置 0、1、2级流水
- D端口只能配置0、1级流水
- AD寄存器可以配置 0、1级流水
- 乘法器输出后的M寄存器可配置0、1级流水
- 输出P寄存器可配置0、1级流水
因此我们在使用乘法器的时候,除了需要注意两个输入端口的流水一样,这样才符合平常的使用;还需要注意整个乘法器的流水级数可配置为0 ~ 4级流水,只有选择合适的流水线级数时,整个乘法器才能高速运行。我们先不设置流水线级数,即输入到输出0拍延迟,仿真代码如下:
`timescale 1ns / 1ps
module tb_mult_B18_D25();
reg i_clk ;
reg rst ;
reg [24:0] i_D ;
reg [17:0] i_B ;
wire [47:0] o_P ;
initial begin
i_clk = 0;
rst = 1 ;
i_D = 25'd0;
i_B = 18'd0;
#1000;
rst = 0 ;
end
always #10 i_clk = ~i_clk;
always @(posedge i_clk) begin
if(rst == 1'b1)begin
i_D <= 'd0;
i_B <= 'd0;
end
else begin
i_D <= i_D + 1'b1 ;
i_B <= i_B + 1'b1 ;
end
end
mult_B18_D25#(
.BREG ( 0 ),
.DREG ( 0 ),
.ALUMODE ( 4'b0000 ),
.INMODE ( 5'b00110 ),
.OPMODE ( 7'b0000101 )
)u_mult_B18_D25(
.i_clk ( i_clk ),
.i_D ( i_D ),
.i_B ( i_B ),
.o_P ( o_P )
);
endmodule
乘法结果正确,输入输出0拍延迟,接下来我们仿真负数的乘法,仿真代码如下:
`timescale 1ns / 1ps
module tb_mult_B18_D25();
reg i_clk ;
reg rst ;
reg [24:0] i_D ;
reg [17:0] i_B ;
wire [47:0] o_P ;
initial begin
i_clk = 0;
rst = 1 ;
i_D = 25'd0;
i_B = 18'd0;
#1000;
rst = 0 ;
end
always #10 i_clk = ~i_clk;
always @(posedge i_clk) begin
if(rst == 1'b1)begin
i_D <= 25'd0;
i_B <= 18'd0;
end
else begin
i_D <= -25'd30 ;
i_B <= i_B - 1 ;
end
end
mult_B18_D25#(
.BREG ( 0 ),
.DREG ( 0 ),
.ALUMODE ( 4'b0000 ),
.INMODE ( 5'b00110 ),
.OPMODE ( 7'b0000101 )
)u_mult_B18_D25(
.i_clk ( i_clk ),
.i_D ( i_D ),
.i_B ( i_B ),
.o_P ( o_P )
);
endmodule
负数乘法结果也是正确,接下来我们将流水设置1拍看一下结果:
可以看到输出相对于输入有1拍的延迟,我们将流水设置2拍看一下结果:
可以看到输出相对于输入有2拍的延迟,我们将流水设置3拍看一下结果:
可以看到输出相对于输入有3拍的延迟,我们将流水设置4拍看一下结果:
结果依然没问题。
6.2 (A+D)*B的仿真
DSP48E1内部有个预加器,可以使得A和D先预加后,取低25位再和B相乘,上面我们验证了乘法器的功能,我们把A和D的预加器打开,因为D端口最多打1拍流水,A可以打2拍,因此想要A和D流水对齐再累加,后面还有一个AD寄存器。所以预加器的流水最多设置2拍,加上后面的M寄存器、P寄存器,整个DSP48E1的流水范围也是0~4拍,我们先来看0拍的情况,仿真代码如下:
`timescale 1ns / 1ps
module tb_add_A25_D25_mult_B18();
reg i_clk ;
reg rst ;
reg [29:0] i_A ;
reg [24:0] i_D ;
reg [17:0] i_B ;
wire [47:0] o_P ;
initial begin
i_clk = 0;
rst = 1 ;
i_D = 25'd0;
i_B = 18'd0;
i_A = 30'd0;
#1000;
rst = 0 ;
end
always #10 i_clk = ~i_clk;
always @(posedge i_clk) begin
if(rst == 1'b1)begin
i_D <= 25'd0;
i_B <= 18'd0;
i_A <= 30'd0;
end
else begin
i_D <= i_D + 1 ;
i_B <= i_B + 1 ;
i_A <= i_A + 1;
end
end
add_A25_D25_mult_B18#(
.BREG ( 0 ),
.DREG ( 0 ),
.AREG ( 0 ),
.ALUMODE ( 4'b0000 ),
.INMODE ( 5'b00100 ),
.OPMODE ( 7'b0000101 )
)u_add_A25_D25_mult_B18(
.i_clk ( i_clk ),
.i_A ( i_A ),
.i_D ( i_D ),
.i_B ( i_B ),
.o_P ( o_P )
);
endmodule
可以看到输出相对于输入有0拍的延迟,(1+1)*1=2;(2+2)*2=8,(3+3)*3=18,结果正确,我们接下来看负数的情况,仿真代码如下:
`timescale 1ns / 1ps
module tb_add_A25_D25_mult_B18();
reg i_clk ;
reg rst ;
reg [29:0] i_A ;
reg [24:0] i_D ;
reg [17:0] i_B ;
wire [47:0] o_P ;
initial begin
i_clk = 0;
rst = 1 ;
i_D = 25'd0;
i_B = 18'd0;
i_A = 30'd0;
#1000;
rst = 0 ;
end
always #10 i_clk = ~i_clk;
always @(posedge i_clk) begin
if(rst == 1'b1)begin
i_D <= 25'd0;
i_B <= 18'd0;
i_A <= 30'd0;
end
else begin
i_D <= i_D - $random % 10 ;
i_B <= i_B + 1 ;
i_A <= i_A + $random % 10 ;
end
end
add_A25_D25_mult_B18#(
.BREG ( 0 ),
.DREG ( 0 ),
.AREG ( 0 ),
.ALUMODE ( 4'b0000 ),
.INMODE ( 5'b00100 ),
.OPMODE ( 7'b0000101 )
)u_add_A25_D25_mult_B18(
.i_clk ( i_clk ),
.i_A ( i_A ),
.i_D ( i_D ),
.i_B ( i_B ),
.o_P ( o_P )
);
endmodule
可以看到输出相对于输入有0拍的延迟,(7-8)*1=-1;(23-27)*4=-16,(32-28)*5=20,负数计算的结果也是正确,1~4级的流水这里就不一一展示了,我们直接配置成4级流水,看看结果:
可以看到输出相对于输入有4拍的延迟,结果也正确。
6.3 (A+D)*B±C的仿真
由内部结构图可以知道C输入最多只有一个寄存器,因此流水最多打1拍。所以我们要把A、D、B到第二级的流水也最多设置1拍,再加上输出的P寄存器,因此整个功能的流水范围为0~2,仿真代码如下:
`timescale 1ns / 1ps
module tb_add_A25_D25_mult_B18_add_C48();
reg i_clk ;
reg rst ;
reg [29:0] i_A ;
reg [24:0] i_D ;
reg [17:0] i_B ;
reg [47:0] i_C ;
wire [47:0] o_P ;
initial begin
i_clk = 0;
rst = 1 ;
i_D = 25'd0;
i_B = 18'd0;
i_A = 30'd0;
i_C = 48'd0;
#1000;
rst = 0 ;
end
always #10 i_clk = ~i_clk;
always @(posedge i_clk) begin
if(rst == 1'b1)begin
i_D <= 25'd0;
i_B <= 18'd0;
i_A <= 30'd0;
i_C <= 48'd0;
end
else begin
i_D <= i_D +1 ;
i_B <= i_B + 1 ;
i_A <= i_A +1 ;
i_C <= i_C + 1;
end
end
add_A25_D25_mult_B18_add_C48#(
.BREG ( 0 ),
.DREG ( 0 ),
.AREG ( 0 ),
.CREG ( 0 ),
.ALUMODE ( 4'b0000 ),
.INMODE ( 5'b00100 ),
.OPMODE ( 7'b0110101 )
)u_add_A25_D25_mult_B18_add_C48(
.i_clk ( i_clk ),
.i_A ( i_A ),
.i_D ( i_D ),
.i_B ( i_B ),
.i_C ( i_C ),
.o_P ( o_P )
);
endmodule
可以看到输出相对于输入有0拍的延迟,(1+1)*1 + 1=3;(2+2)*2 + 2=10,(3+3)*3 + 3=21,结果正确,我们配置成2级流水,看看结果:
可以看到输出相对于输入有2拍的延迟,结果也是正确。
6.4 累加器的仿真
DSP48E1输出有个模式检测器,用于检测 P 总线是否与指定模式匹配或是否与模式的补码完全匹配。如果加法器的输出与设定模式匹配,则 PATTERNDETECT (PD) 输出变为高电平。 如果加法器的输出与设定模式的补码匹配,则 PATTERNBDETECT (PBD) 输出变为高电平,如下所示:
SEL_PATTERN控制匹配值的输入,输入源可以是 48 位动态“C”输入,也可以是 48 位静态属性字段。SEL_MASK选择掩码字段,当 MASK 位设置为 1 时,将忽略相应的模式位。当 MASK 位设置为 0 时,将比较模式位。我们来仿真一下,给定一个匹配值输入,让P进行累加,当P累加到匹配值后,看PATTERNDETECT是否拉高,仿真代码如下:
`timescale 1ns / 1ps
module tb_dsp48e1_acc();
reg i_clk ;
wire [47:0] o_P ;
wire PATTERNDETECT ;
initial begin
i_clk = 0;
end
always #10 i_clk = ~i_clk;
dsp48e1_acc#(
.MASK ( 48'd0 ),
.PATTERN ( 48'd351 ),
.SEL_PATTERN ( "PATTERN" )
)u_dsp48e1_acc(
.i_clk ( i_clk ),
.i_C ( {47'd0,1'b1} ),
.PATTERNDETECT ( PATTERNDETECT ),
.o_P ( o_P )
);
endmodule
可以看到,我们设置匹配值为351,当累加器累加到351时,PATTERNDETECT 拉高一个周期,表示匹配成功,至此,关于DSP48E1所有功能的仿真都已经完成,能满足绝大多数应用场景。