FC2カウンター FPGAの部屋 2013年07月12日
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

VHDLのmath_realパッケージを使用した sin() と cos() の計算

sin() と cos() の演算を VHDL の math_real パッケージを使用してやってみたので、忘れないようにブログに書いておく。

sin() と cos() の VHDL での表現方法は、符号1ビット、整数部1ビット、小数部8ビットとする。2π/16 の 0 ~ 7 番目までの値を、math_realパッケージを使って real で求めるが、XSTでは、realを扱えないので、固定小数点に変換する。

下にサンプル回路を示す。これは、cos_sel, sin_sel に番号を入力することで、cos(), sin() の求める値を、それぞれ表示する。

-- COS(), SIN() Test (cos_sin_test.vhd)
-- COS(), SIN() 符号1ビット、整数部1ビット、小数部8ビット
-- output は 256倍にしてある

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;
use ieee.math_real.all;

entity cos_sin_test is
    port(
        cos_sel : in std_logic_vector(2 downto 0);
        sin_sel : in std_logic_vector(2 downto 0);
        cos_output : out std_logic_vector(9 downto 0);
        sin_output : out std_logic_vector(9 downto 0)
    );
end cos_sin_test;

architecture RTL of cos_sin_test is
-- 符号1ビット、整数部1ビット、小数部8ビット
constant cos0_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*0.0/16.0) * 256.0)),10));
constant cos1_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*1.0/16.0) * 256.0)),10));
constant cos2_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*2.0/16.0) * 256.0)),10));
constant cos3_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*3.0/16.0) * 256.0)),10));
constant cos4_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*4.0/16.0) * 256.0)),10));
constant cos5_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*5.0/16.0) * 256.0)),10));
constant cos6_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*6.0/16.0) * 256.0)),10));
constant cos7_16 : std_logic_vector := std_logic_vector(to_signed((integer(cos((2.0*MATH_PI)*7.0/16.0) * 256.0)),10));
constant sin0_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*0.0/16.0) * 256.0)),10));
constant sin1_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*1.0/16.0) * 256.0)),10));
constant sin2_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*2.0/16.0) * 256.0)),10));
constant sin3_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*3.0/16.0) * 256.0)),10));
constant sin4_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*4.0/16.0) * 256.0)),10));
constant sin5_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*5.0/16.0) * 256.0)),10));
constant sin6_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*6.0/16.0) * 256.0)),10));
constant sin7_16 : std_logic_vector := std_logic_vector(to_signed((integer(sin((2.0*MATH_PI)*7.0/16.0) * 256.0)),10));
begin
    process(cos_sel) begin
        case cos_sel is
            when "000" =>    cos_output <= cos0_16;
            when "001" =>    cos_output <= cos1_16;
            when "010" =>    cos_output <= cos2_16;
            when "011" =>    cos_output <= cos3_16;
            when "100" =>    cos_output <= cos4_16;
            when "101" =>    cos_output <= cos5_16;
            when "110" =>    cos_output <= cos6_16;
            when others =>    cos_output <= cos7_16;
        end case;
    end process;
    
    process(sin_sel) begin
        case sin_sel is
            when "000" =>    sin_output <= sin0_16;
            when "001" =>    sin_output <= sin1_16;
            when "010" =>    sin_output <= sin2_16;
            when "011" =>    sin_output <= sin3_16;
            when "100" =>    sin_output <= sin4_16;
            when "101" =>    sin_output <= sin5_16;
            when "110" =>    sin_output <= sin6_16;
            when others =>    sin_output <= sin7_16;
        end case;
    end process;
    
end RTL;


テストベンチを書いてシミュレーションしてみた。テストベンチを下に示す。

`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   11:06:54 07/12/2013
// Design Name:   cos_sin_test
// Module Name:   D:/HDL/FndISEWork/Kintex-7/TED_K7/test/cos_sin_test/cos_sin_test_tb.v
// Project Name:  cos_sin_test
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: cos_sin_test
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module cos_sin_test_tb;

    // Inputs
    reg [2:0] cos_sel;
    reg [2:0] sin_sel;

    // Outputs
    wire [9:0] cos_output;
    wire [9:0] sin_output;

    // Instantiate the Unit Under Test (UUT)
    cos_sin_test uut (
        .cos_sel(cos_sel), 
        .sin_sel(sin_sel), 
        .cos_output(cos_output), 
        .sin_output(sin_output)
    );

    initial begin
        $monitor("%h %b %b", cos_sel, cos_output, sin_output);
        
        // Initialize Inputs
        cos_sel = 0;
        sin_sel = 0;

        // Wait 100 ns for global reset to finish
        #100;
        cos_sel = 1;
        sin_sel = 1;
      
        #100;
        cos_sel = 2;
        sin_sel = 2;
      
        #100;
        cos_sel = 3;
        sin_sel = 3;
      
        #100;
        cos_sel = 4;
        sin_sel = 4;
      
        #100;
        cos_sel = 5;
        sin_sel = 5;
      
        #100;
        cos_sel = 6;
        sin_sel = 6;
      
        #100;
        cos_sel = 7;
        sin_sel = 7;
      
        #100;
        // Add stimulus here
    end
    
    initial    $monitor("%h %b %b", cos_sel, cos_output, sin_output);

endmodule


Project Navigator 14.6でプロジェクトを作成して、ISimでシミュレーションを行った。
VHDL_math_real_1_130712.png

出てきた値をExcelで解析してみた。浮動小数点数で計算した cos(), sin() と固定小数点の cos(), sin() では、どのくらいの差があるかを計算で求めた。
VHDL_math_real_2_130712.png

インプリメントも出来た。ピンを配置していないので、BitGenはエラーだったが、インプリメントは成功した。IOを除けば、LUT8個のみを使用している。
VHDL_math_real_3_130712.png

hiyuh さんに教えて頂いたパラメタライズなVHDLコード例を貼っておく。なお、仕様の違いから私が一部修正させて頂いた。
hiyuh さん、教えて頂いてありがとうございました。今後とも宜しく、お願いします。

-- COS(), SIN() Test (cos_sin_test.vhd)
-- COS(), SIN() 符号1ビット、整数部1ビット、小数部8ビット
-- output は 256倍にしてある
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- use ieee.std_logic_misc.all; -- NOTE: std_logic_misc is a legacy POS from Synopsys.
use ieee.math_real.all;
 
entity cos_sin_test is
        -- FIXME: In VHDL-2002, predefined integer should be in [+(2**31)-1, -(2**31)+1] .
        --        To restrict upper bound of SW and OW is worth to consider.
        generic (
                SW : integer range 1 to integer'high :=  3;
                OW : integer range 2 to integer'high := 10
        );
    port(
        -- NOTE: Do not hard code any bit width for future use.
        cos_sel    : in  std_logic_vector(SW-1 downto 0);
        sin_sel    : in  std_logic_vector(SW-1 downto 0);
        cos_output : out std_logic_vector(OW-1 downto 0);
        sin_output : out std_logic_vector(OW-1 downto 0)
    );
begin -- NOTE: Do not be lazy for typing "begin".
end entity cos_sin_test; -- NOTE: Do not be lazy for typing "entity".
 
architecture RTL of cos_sin_test is
-- NOTE: Calculate them into constant LUTs using SW and OW.
        type tLUT is array (0 to 2**SW-1) of std_logic_vector(OW-1 downto 0);
        pure function fCOS_LUT (
                iDUMMY : boolean -- NOTE: Dummy argument for stupid simulator/synthesizer.
        ) return tLUT is
                variable vCOS     : real;
                variable vCOS_LUT : tLUT;
        begin
                F_COS_LUT : for vSEL in tLUT'range loop
                        -- NOTE: Use round for better accuracy.
                        vCOS := round(cos(2.0 * MATH_PI * real(vSEL) / real(2**(SW+1))) * real(2**(OW-2)));
                        -- NOTE: Consider cos(...) = 1.0.
                        -- FIXME: Enlarge OW scheme if you don't like this clamp which produces a bit of
                        --        distortion at specific arguments.
                        vCOS_LUT(vSEL) := std_logic_vector(to_signed(integer(vCOS), OW));
                end loop F_COS_LUT;
                return vCOS_LUT;
        end function fCOS_LUT;
        pure function fSIN_LUT (
                iDUMMY : boolean -- NOTE: Dummy argument for stupid simulator/synthesizer.
        ) return tLUT is
                variable vSIN     : real;
                variable vSIN_LUT : tLUT;
        begin
                F_SIN_LUT : for vSEL in tLUT'range loop
                        -- NOTE: Use round for better accuracy.
                        vSIN := round(sin(2.0 * MATH_PI * real(vSEL) / real(2**(SW+1))) * real(2**(OW-2)));
                        -- NOTE: Consider sin(...) = 1.0.
                        -- FIXME: Enlarge OW scheme if you don't like this clamp which produces a bit of
                        --        distortion at specific arguments.
                        vSIN_LUT(vSEL) := std_logic_vector(to_signed(integer(vSIN), OW));
                end loop F_SIN_LUT;
                return vSIN_LUT;
        end function fSIN_LUT;
        constant cCOS_LUT : tLUT := fCOS_LUT(true);
        constant cSIN_LUT : tLUT := fSIN_LUT(true);
begin
  cos_output <= cCOS_LUT(to_integer(unsigned(cos_sel)));
  sin_output <= cSIN_LUT(to_integer(unsigned(sin_sel)));
end architecture RTL; -- NOTE: Do not be lazy for typing "architecture".

  1. 2013年07月12日 15:59 |
  2. VHDLの書き方
  3. | トラックバック:0
  4. | コメント:0

インプリメントとシミュレーションでのRAM及びROMの初期値

今回のプロジェクトでは、AXI VDMAのレジスタを設定するコントローラ (reg_set_axi_lite_master.v) で、AXI VDMAのアドレスと設定値を保存しておくテキストファイル vdma_reg_set.txt の値を使用して、AXI4 Lite Masterアクセスを行う予定だ。
vdma_reg_set.txtは、reg_set_axi_lite_master.vと同じフォルダに置いてある。(”AXI VDMAのレジスタ設定用AXI Lite Master IPの作製2(シミュレーション)”参照)
VDMA_test_10_130712.png

この状態でVDMA_testプロジェクト全体をインプリメントすると、BRAMに初期値が入る。
VDMA_test_11_130712.png

このまま、ISimでシミュレーションを行うと、rom の初期値は入っていない。
VDMA_test_12_130712.png

プロジェクトのトップフォルダに vdma_reg_set.txt を置いた。
VDMA_test_13_130712.png

シミュレーションを行ったところ、rom に初期値がロードされた。
VDMA_test_14_130712.png

ISimの場合、BRAMの初期値ファイルはプロジェクトのトップフォルダに置く必要があり、インプリメントの時には、初期値ファイルを呼んでいるVerilog HDLファイル (reg_set_axi_lite_master.v) と同じフォルダに置く必要がある。これは、あくまでデフォルトの状態であり、オプションでフォルダをできるのかもしれないが、気をつけておく必要があると思う。
  1. 2013年07月12日 05:04 |
  2. Xilinx ISEについて
  3. | トラックバック:0
  4. | コメント:0