赞
踩
除法在 FPGA 开发与应用中是一种重要的运算,按照被除数和除数是否为定值,分三种情况:(1)被除数固定,除数变化;(2)除数固定,被除数变化;(3)被除数和除数都不固定。本文讨论如何用 FPGA 实现除数固定,被除数变化的整数除法。
目录
由于除数是固定的,先根据除数本身的性质对问题进行分类。
对于 2 的指数次幂的除数,例如 2,4,8...,用逻辑右移就可以很方便地实现除法运算,右移 1 位等价于除以 2,右移 2 位等价于除以 4,...,右移 N 位等价于除以 2 的 N 次幂。
对于非 2 的指数次幂的除数,上述方法就不适用了,下面讨论两种实现方法。
这种方法的原理是,根据被除数与除数的关系,划分若干区间,并对这些区间进行编码。把除法运算转换为,判断被除数落在哪个区间,然后输出区间的编码。
例如,在处理 1920x1080p 视频画面时,假如需要计算 video_col ÷ 480,可以把 video_col 的取值范围划分为 [0,479], [480,959], [960,1439] 和 [1440,1919] 四个区间,并将这四个区间依次编码为 00, 01, 10, 11。
以下是用 VHDL 语言实现 video_col ÷ 480 的代码:
- process(video_col)
- begin
- if video_col < 480 then
- q <= "00";
- elsif video_col < 960 then
- q <= "01";
- elsif video_col < 1440 then
- q <= "10";
- else
- q <= "11";
- end if;
- end process;
这种方法的优点是易于理解,容易修改;缺点是对于数值小的除数,需要编码的区间数量较多,这会消耗大量 LUT 资源用于数值比较。
对于除数较小的情况,第一种方法可能出现很多选择分支。为了节约 LUT 资源,可以采用第二种方法,即被除数先乘以一个固定的数,然后将结果进行逻辑右移,截掉低位部分。
如果用 x, y, z, a 分别表示被除数,除数,商和余数,它们满足
改写成
令 ,有
容易知道,n 的取值范围相对 m 较小,因此在寻找满足要求的 m 和 n 时,可以先给定 n,再寻找 m 的最优值。
令
其中 ,于是,m 的最优值由以下整数规划问题给出:
m 有两个可能的最优解,分别是
有了 m 和 n 的关系之后,只要在一定范围内遍历 n 的取值,再代入并验证 是否成立即可。
用乘积右移截位法实现求 x 整除 5 的运算电路,x 的位宽取 8 bit。在 [3,12] 的范围内遍历 n,计算发现当 n = 10, m = 205 时,误差已经为 0。
取 m = 205, n = 10,VHDL 代码如下:
- library ieee;
- use ieee.std_logic_1164.all;
- use ieee.std_logic_arith.all;
- use ieee.std_logic_unsigned.all;
-
- entity top is
- generic(
- M : integer := 205;
- N : integer := 10;
- W : integer := 6
- );
- port(
- ina : in std_logic_vector;
- y : out std_logic_vector
- );
- end entity;
- architecture behav of top is
- -- 常量与信号定义
- constant Y_HIGH: integer := W + N - 1;
- constant D_WIDTH: integer := ina'LENGTH + 32;
- signal Prod: std_logic_vector(D_WIDTH-1 downto 0);
- signal A_buf: bit_vector(D_WIDTH-1 downto 0) := (others => '0');
- signal M_buf: std_logic_vector(31 downto 0);
- begin
- -- 信号初始化
- A_buf(ina'HIGH downto 0) <= to_bitvector(ina);
- M_buf <= conv_std_logic_vector(M,32);
-
- -- 循环移位乘法器
- process(A_buf,M_buf)
- variable tmp: std_logic_vector(D_WIDTH-1 downto 0);
- begin
- tmp := (others => '0');
- for i in M_buf'REVERSE_RANGE loop
- if M_buf(i) = '1' then
- tmp := tmp + to_stdlogicvector(A_buf SLL i);
- end if;
- end loop;
- Prod <= tmp;
- end process;
- -- 截掉结果的低位部分
- y <= Prod(Y_HIGH downto N);
- end architecture;
仿真激励代码如下:
- library ieee;
- use ieee.std_logic_1164.all;
- use ieee.std_logic_arith.all;
- use ieee.std_logic_unsigned.all;
-
- entity top_tb is
- end entity;
- architecture behav of top_tb is
- signal sys_rst: std_logic := '1';
- signal sys_clk: std_logic := '1';
- signal A: std_logic_vector(7 downto 0);
- signal A_div5: std_logic_vector(5 downto 0);
- begin
- -- Todo
- sys_rst <= '1', '0' after 100 ns;
- sys_clk <= not sys_clk after 2500 ps;
-
- process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- A <= (others => '0');
- elsif rising_edge(sys_clk) then
- if A = 255 then
- A <= A;
- else
- A <= A + 1;
- end if;
- end if;
- end process;
-
- top_inst: entity work.top
- port map(
- ina => A,
- y => A_div5
- );
- end architecture;
仿真波形如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。