在设计中,经常出现除法运算,实现方法 :
1、移位操作
2、取模取余
4、查找表
网上很多IP翻译文档,不详细介绍,记录几个重要的点:
1、三种算法模式(不同模式所消耗的资源类型不同)
2、分清除数和被除数;余数模式的选择
3、延迟输出的周期可配置
除法器IP的关键信号:
可以看到仍然是axis接口,所以在给数据时候,仍遵循tready和tvaild握手则数据有效的原则
给定输入的除数以及被除数有效信号和数据,由于握手,所以只有在输出的对应tready信号准备好,才能给定输入的相关信号
代码如下:
bash
`timescale 1ns / 1ps
module DIVIDE_IP(
input CLK ,
input RST ,
output [15:0] REMINDER ,
output [17:0] QUOEITINR
);
reg r_axis_divisor_tvalid = 'b0 ;
reg [15:0] r_axis_divisor_tdata = 'b0 ;
reg r_axis_dividend_tvalid = 'b0 ;
reg [23:0] r_axis_dividend_tdata = 'b0 ;
wire [15:0] s_axis_divisor_tdata ;
wire [23:0] s_axis_dividend_tdata ;
wire [39:0] m_axis_dout_tdata ;
assign s_axis_divisor_tvalid = r_axis_divisor_tvalid ;
assign s_axis_divisor_tdata = r_axis_divisor_tdata ;
assign s_axis_dividend_tvalid= r_axis_dividend_tvalid;
assign s_axis_dividend_tdata = r_axis_dividend_tdata ;
assign REMINDER = m_axis_dout_tdata[15:0]; //余数
assign QUOEITINR = m_axis_dout_tdata[39:16]; //商
div_gen_0 u_div_gen_0 (
.aclk (CLK ), // input wire aclk
.s_axis_divisor_tvalid (s_axis_divisor_tvalid ), // input wire s_axis_divisor_tvalid
.s_axis_divisor_tready (s_axis_divisor_tready ), // output wire s_axis_divisor_tready
.s_axis_divisor_tdata (s_axis_divisor_tdata ), // input wire [15 : 0] s_axis_divisor_tdata
.s_axis_dividend_tvalid(s_axis_dividend_tvalid), // input wire s_axis_dividend_tvalid
.s_axis_dividend_tready(s_axis_dividend_tready), // output wire s_axis_dividend_tready
.s_axis_dividend_tdata (s_axis_dividend_tdata ), // input wire [23 : 0] s_axis_dividend_tdata
.m_axis_dout_tvalid (m_axis_dout_tvalid ), // output wire m_axis_dout_tvalid
.m_axis_dout_tdata (m_axis_dout_tdata ) // output wire [39 : 0] m_axis_dout_tdata
);
always @(posedge CLK)
begin
if(RST)
begin
r_axis_divisor_tvalid <= 'b0;
r_axis_divisor_tdata <= 'd0;
end
else if(s_axis_divisor_tready)
begin
r_axis_divisor_tvalid <= 'b1;
//r_axis_divisor_tdata <= r_axis_divisor_tdata + 'd1; //除数
r_axis_divisor_tdata <= r_axis_divisor_tdata + 'd4; //除数
end
else
begin
r_axis_divisor_tvalid <= 'b0;
r_axis_divisor_tdata <= 'd0;
end
end
always @(posedge CLK)
begin
if(RST)
begin
r_axis_dividend_tvalid <= 'b0;
r_axis_dividend_tdata <= 'd0; //被除数
end
else if(s_axis_dividend_tready)
begin
r_axis_dividend_tvalid <= 'b1;
r_axis_dividend_tdata <= r_axis_dividend_tdata + 'd5; //被除数
end
else
begin
r_axis_dividend_tvalid <= 'b0;
r_axis_dividend_tdata <= 'd0; //被除数
end
end
endmodule
TB:
bash
`timescale 1ns / 1ns
module DIVIDE_IP_TB;
reg CLK ;
reg RST ;
DIVIDE_IP u_DIVIDE_IP(
.CLK (CLK ),
.RST (RST )
);
initial CLK = 1'b1;
always #10 CLK = ~CLK;
initial begin
RST = 'b1;
#100;
RST = 'b0;
end
endmodule
仿真情况:
无余数的情况,可以看到m_axis_dout_tvalid高时,对应的余数都是0
有余数的情况:
出现的问题:
IP输入信号出现高阻态。
如下图所示,给了正确的r_axis_divisor_tvalid、r_axis_divisor_tdata、r_axis_dividend_tvalid、r_axis_dividend_tdata信号,并将其赋值给除法器IP的输入s_axis_divisor_tvalid、s_axis_divisor_tdata、s_axis_dividend_tvalid、s_axis_dividend_tdata。
可以看到tvalid信号正常赋值,tdata却出现高阻态。
原因:赋值时,tvalid位宽1bit,可不定义直接赋值;但等于多bit位宽的tdata来说,赋值assign之前,需要先定义。
bash
wire [15:0] s_axis_divisor_tdata
wire [23:0] s_axis_dividend_tdata
以上简单记录除法器IP的使用方法,后续可应用。比如A / B的操作,若有余数则在商的基础上加1.若没有余数就是商。
这样我们就可以先调用除法器IP,得到商和余数。
判断,当余数==0时,r_tmp = 商,否则r_tmp = 商 + 'b1