基于LoongArch指令集-五级流水线CPU 乘除法指令的添加

调用Xilinx IP实现乘除法运算部件

  1. 调用Xilinx IP实现乘法运算部件

    wire [31:0] src1,src2;
    wire [63:0] unsigned_prod;
    wire [63:0] signed_prod;

    assign unsigned_prod = src1*src2;
    assign signed_prod = signed(src1)*signed(src2);

  2. 调用 Xilinx IP 实现除法运算部件
    在工程导航栏中点击"IP Catalog",然后在右侧出现的窗口中搜索div。在搜索结果中双击"Divider Generator" 项,打开除法器 IP 设置界面,依次设置参数,如图5-2所示。
    对于除法器的名字,读者可以根据自己的喜好进行调整。在"Channel Settings" 选项卡中,我们首先要对除法器IP的算法进行选择,我们建议采用 Radix2 算法,采用Radix2这种算法实现的除法器的优点是资源消耗少,缺点是完成运算要求的迭代周期数多。
    在"Channel Settings"选项卡中还可以选择除法运算是有符号除法还是无符号除法。与前面介绍的乘法器IP类似,这里也需要生成有符号和无符号两个除法器IP,才能分别用于实现DIV和DIVU指令。将Remainder Type选择为Remainder,以确保余数类型是整数型。在"Options"选项卡中,选择多少拍处理一个除法,这个拍数是连续处理多个除法的间隔拍数,不是一个除法处理完成的拍数。

c 复制代码
u_unsigned_div u_unsigned_divider (
    .aclk                   (clk),
    .s_axis_dividend_tdata  (divider_dividend),
    .s_axis_dividend_tready (unsigned_dividend_tready),
    .s_axis_dividend_tvalid (unsigned_dividend_tvalid),
    .s_axis_divisor_tdata   (divider_divisor),
    .s_axis_divisor_tready  (unsigned_divisor_tready),
    .s_axis_divisor_tvalid  (unsigned_divisor_tvalid),
    .m_axis_dout_tdata      (unsigned_divider_res),
    .m_axis_dout_tvalid     (unsigned_dout_tvalid)
);

u_signed_div u_signed_divider (
    .aclk                   (clk),
    .s_axis_dividend_tdata  (divider_dividend),
    .s_axis_dividend_tready (signed_dividend_tready),
    .s_axis_dividend_tvalid (signed_dividend_tvalid),
    .s_axis_divisor_tdata   (divider_divisor),
    .s_axis_divisor_tready  (signed_divisor_tready),
    .s_axis_divisor_tvalid  (signed_divisor_tvalid),
    .m_axis_dout_tdata      (signed_divider_res),
    .m_axis_dout_tvalid     (signed_dout_tvalid)
);
  • s_axis_dividend_tdata 对应计算时输入的被除数数据
  • s_axis_divisor_tdata 对应计算时输入的除数数据
  • 计算得到的商和余数结果统一从商和余数通道中的64位信号m_axis_dout_tdata中输出。其中前32位是商,后32位是余数
  • tvalid、tready信号是一对"握手"控制信号,其工作原理类似于我们在CPU流水线之间使用的valid、allowin信号。tvalid是请求信号,tready是应答信号。当时钟上升沿到来时,如果采样得到tvalid和tready都等于1,则请求发起方和接收方之间完成一次成功的握手。
  • 我们 假设在执行流水阶段调用所生成的除法器IP。在除法指令处于执行流水级且没有对除法器成功输入数据的时候,同时将s_axis_dividend_tvalid和s_axis_divisor_tvalid置为1.当发现s_axis_dividend_tready和s_axis_divisor_tready反馈为1后(此时在一个时钟上升沿同时看到tvalid和tready为1,表示握手成功),需要将s_axis_dividend_tvalid和s_axis_divisor_tready清0.也就是确保握手成功的那个时钟上升沿之后的s_axis_dividend_tvalid和s_axis_divisor_tvalid为0.只要我们确保握手成功的那个时钟上升沿之后的s_axis_dividend_tvalid和s_axis_divisor_tready为0.只要我们保证s_axis_dividend_tvalid和s_axis_divisor_tready一定同时置为1,那么这里生成的除法器IP反馈的s_axis_dividend_tready和s_axis_divisor_tready一定也同时置为1.握手成功后,tvalid一定要撤销,否则对于除法器IP来说,它会认为又有一个新的除法运算。完成输入数据的握手之后,除法指令就需要在执行流水级等待除法器IP最终输出结果。当m_axis_dout_tvalid置为1时,表示除法计算完成。此时除法指令就可以从m_axis_dout_tdata上取出计算结果,进入流水线的后续阶段。
c 复制代码
assign unsigned_dividend_tvalid = es_valid && (es_inst_div_wu | es_inst_mod_wu) && !unsigned_dividend_sent;
assign unsigned_divisor_tvalid  = es_valid && (es_inst_div_wu | es_inst_mod_wu) && !unsigned_divisor_sent;

always @ (posedge clk) begin
    if (reset) begin
        unsigned_dividend_sent <= 1'b0;
    end else if (unsigned_dividend_tready && unsigned_dividend_tvalid) begin
        unsigned_dividend_sent <= 1'b1;
    end else if (es_ready_go && ms_allowin) begin
        unsigned_dividend_sent <= 1'b0;
    end
    
    if (reset) begin
        unsigned_divisor_sent <= 1'b0;
    end else if (unsigned_divisor_tready && unsigned_divisor_tvalid) begin
        unsigned_divisor_sent <= 1'b1;
    end else if (es_ready_go && ms_allowin) begin
        unsigned_divisor_sent <= 1'b0;
    end
end

reg  signed_dividend_sent;
reg  signed_divisor_sent;

assign signed_dividend_tvalid = es_valid && (es_inst_div_w | es_inst_mod_w) && !signed_dividend_sent;
assign signed_divisor_tvalid  = es_valid && (es_inst_div_w | es_inst_mod_w) && !signed_divisor_sent;

always @ (posedge clk) begin
    if (reset) begin
        signed_dividend_sent <= 1'b0;
    end else if (signed_dividend_tready && signed_dividend_tvalid) begin
        signed_dividend_sent <= 1'b1;
    end else if (es_ready_go && ms_allowin) begin
        signed_dividend_sent <= 1'b0;
    end
    
    if (reset) begin
        signed_divisor_sent <= 1'b0;
    end else if (signed_divisor_tready && signed_divisor_tvalid) begin
        signed_divisor_sent <= 1'b1;
    end else if (es_ready_go && ms_allowin) begin
        signed_divisor_sent <= 1'b0;
    end
end
相关推荐
IOT那些事儿6 个月前
龙芯LS2K0300久久派上手体验
loongarch·龙芯·ls2k0300·久久派
程序媛zcx7 个月前
loongarch64 electron打包deb改成符合统信测试通过的deb
linux·loongarch·统信
码尔泰1 年前
LoongArch 五级流水线实现
fpga开发·loongarch·龙芯杯