FPGA教程系列-Vivado实现低延时除法器与generate用法

FPGA教程系列-Vivado实现低延时除法器与generate用法

其实就是用乘法器代替除法器,在matlab中编写一个1/x的查找表,然后通过值来查找对应的结果,y*1/x就相当于y/x了。

Matlab生成coe

将coe文件存在ROM中,当作查找表。

verilog 复制代码
clc; clear; close all;

% --- 参数设置 ---
DEPTH = 8192;           % 数据深度
WIDTH_VAL = 16383;      % 分子 (2^14 - 1),决定了输出的最大值
FILENAME = 'dat_opt.coe';

% --- 计算逻辑 ---
x = 1:DEPTH;            % 注意:x 不能包含 0,否则会出现 Inf
y = round(WIDTH_VAL ./ x);

% --- 检查数据范围 (防止溢出) ---
% 如果目标是写 FPGA,确保数据不超过位宽限制,这里做个简单截断保护
y(y > WIDTH_VAL) = WIDTH_VAL; 

% --- 写文件 (向量化优化版) ---
fid = fopen(FILENAME, 'w');

% 写头信息
fprintf(fid, 'memory_initialization_radix=10;\n');
fprintf(fid, 'memory_initialization_vector=\n');

% 技巧:先打印前 N-1 个带逗号的数据,再打印最后一个带分号的数据
% fprintf 可以直接接受数组,按列优先顺序打印,非常快
fprintf(fid, '%d,\n', y(1:end-1)); 
fprintf(fid, '%d;', y(end));       

fclose(fid);

% --- 绘图验证 ---
figure('Name', '1/x Lookup Table');
plot(y);
grid on;
title(['1/x Curve (Max: ', num2str(WIDTH_VAL), ')']);
xlabel('Address (Input)');
ylabel('Data (Output)');

结果放大看,是一个1/x的函数。

Vivado中IP核的设置

这些IP核在前面都讲过,就不再赘述了,不懂的可以翻一翻。​

导入coe文件

设置乘法器

Top文件

几个点:

1、第一个乘法器用来y与1/x相乘,也就是用来模拟除法器,输出o_P,可以用来查看延时。

2、generate简单用法,生成三个乘法器,分别将除法器的结果乘以1、2、3然后相加输出结果,其实就相当于将除法器的结果乘以6.还涉及到一些截断等,想了解的可以自行百度一下。

verilog 复制代码
module lowdelay_div(
    input i_clk,
    input i_rst,
    input signed [15:0] i_y,
    input signed [15:0] i_x,
    output signed [15:0] o_P,
    output signed [33:0] o_generate 
);

    //==========================================================
    // 参数设置
    //==========================================================
    parameter BRAM_LATENCY = 2; 

    //==========================================================
    // 信号定义
    //==========================================================
    wire [15:0] rom_dout_tdata; 
    wire [12:0] rom_addr;       
    reg signed [15:0] i_y_dly [0:BRAM_LATENCY-1]; 
    wire signed [31:0] mult_result;
    
    wire signed [15:0] div_result_16bit; 
    
    integer k;

    //==========================================================
    // 1. 数据对齐逻辑
    //==========================================================
    always @(posedge i_clk) begin
        if (i_rst) begin
            for (k=0; k<BRAM_LATENCY; k=k+1) begin
                i_y_dly[k] <= 16'd0;
            end
        end else begin
            i_y_dly[0] <= i_y;
            for (k=1; k<BRAM_LATENCY; k=k+1) begin
                i_y_dly[k] <= i_y_dly[k-1];
            end
        end
    end

    //==========================================================
    // 2. BMG (Block RAM) 实例化
    //==========================================================
    assign rom_addr = (i_x > 16'd0) ? (i_x[12:0] - 1'b1) : 13'd0;

    blk_mem_gen_0 blk_mem_gen_0u (
        .clka(i_clk),            
        .rsta(i_rst),            
        .addra(rom_addr),          
        .douta(rom_dout_tdata),          
        .rsta_busy()  
    );

    //==========================================================
    // 3. 第一级乘法器 (计算 y * 1/x)
    //==========================================================
    mult_gen_0 mult_gen_u(
        .CLK(i_clk),
        .A(i_y_dly[BRAM_LATENCY-1]), 
        .B(rom_dout_tdata),          
        .SCLR(i_rst),   
        .P(mult_result) // 32位结果,格式为 Q17.14 (假设)
    );

    assign div_result_16bit = mult_result[29:14]; 
    assign o_P = div_result_16bit; // 输出第一级结果

    //==========================================================
    // 4. 后级乘法器组 (generate 生成)
    //==========================================================
    wire [31:0] mult_out [1:3];
    
    generate
        genvar m;
        for (m=1; m<=3; m=m+1) begin: mult_loop
            mult_gen_0 mult_gen_gen_u(
                .CLK(i_clk),
                .A(div_result_16bit), //输入必须是截位后的16位数据,不能是32位的 mult_result
                .B(m[15:0]),          // 将 genvar 转换为 16位信号输入
                .SCLR(i_rst),   
                .P(mult_out[m])       // 输出 32位
            );
        end
    endgenerate
        
    //==========================================================
    // 5. 输出求和
    //==========================================================
    // 注意符号位扩展,这里简单相加
    assign o_generate = mult_out[1] + mult_out[2] + mult_out[3];
    
endmodule

可以看到,一共用了4个乘法器。

Testbench

编写Testbench进行仿真:

verilog 复制代码
`timescale 1ns / 1ps

module lowdelay_div_tb;

    reg i_clk;
    reg i_rst;
    reg signed [15:0] i_y;
    reg signed [15:0] i_x;
    wire signed [15:0] o_P;
    wire signed [33:0] o_generate;
    // 实例化 DUT
    lowdelay_div lowdelay_div_u(
        .i_clk  (i_clk),
        .i_rst  (i_rst),
        .i_y    (i_y),
        .i_x    (i_x),
        .o_P    (o_P),
        .o_generate  (o_generate) 
    );

    // 时钟生成 (100MHz)
    initial begin
        i_clk = 0;
        forever #5 i_clk = ~i_clk;
    end

    // 激励生成
    initial begin
        // 初始化
        i_rst = 1;
        i_y = 0;
        i_x = 1; // 避免 x=0
        
        // 复位释放同步
        repeat(10) @(posedge i_clk); 
        i_rst = 0;

        // 测试用例 1: 10000 / 100 = 100
        @(posedge i_clk);
        i_y <= 16'd10000;
        i_x <= 16'd100; 
        
        // 保持一段时间,观察流水线输出
        // 即使输入变了,流水线也会按顺序输出结果
        repeat(10) @(posedge i_clk);

        // 测试用例 2: 10000 / 200 = 50
        @(posedge i_clk);
        i_y <= 16'd10000;
        i_x <= 16'd200;
        
        repeat(10) @(posedge i_clk);

        // 测试用例 3: 10000 / 50 = 200
        @(posedge i_clk);
        i_y <= 16'd10000;
        i_x <= 16'd50;

        repeat(20) @(posedge i_clk);
        $stop;
    end

endmodule

仿真结果:

结果与预期相符,延时相对来说较低。

相关推荐
Wishell20151 天前
FPGA教程系列-番外篇Model Composer之滤波器优化设计仿真
仿真
Wishell20152 天前
FPGA教程系列-番外篇Model Composer之HDL滤波器仿真
仿真
Wishell20154 天前
FPGA教程系列-番外篇Model Composer之滤波器仿真
仿真
Eric.Lee20214 天前
物理引擎MuJoCo 项目介绍
人工智能·机器人·仿真·robot·物理引擎·mujoco
Wishell20155 天前
FPGA教程系列-番外篇Model Composer初探
仿真
Wishell20159 天前
FPGA教程系列-通过FIFO实现延时与跨时钟域
仿真
Wishell201512 天前
FPGA教程系列-Vivado复数乘法的实现(IP核与非IP核)
仿真
Wishell201513 天前
FPGA教程系列-Vivado IP核Clock Wizard核解析及测试
仿真
Wishell201515 天前
FPGA教程系列-Vivado中读取ROM中数据
仿真