4

本質的に、私の質問は次のとおりです。「これ」とは何か、以下に続きます(コードも):

VHDL で実装された一種の「補数」カウンター関数が必要でした。これは基本的に、各ステップでカウンター値を反転/補数/非反転し、テスト用に少しリッチなビット パターンを提供します。もちろん、私はこれを合成可能 (カウンター値をピンに割り当てることができるようにするため) および移植可能なコード (つまり、IEEE ライブラリのみを実装し、いいえSTD_LOGIC_ARITH) にしたかったのです。また、デフォルトですべてを署名なしとして扱いたくありません(したがって、避けたいと思いSTD_LOGIC_UNSIGNEDます)。

簡単に言うと、このカウンターは次のように説明できます。初期値 C[0] が与えられた場合、各クロック ティックでの値は次のようになります。

C[i+1] = not(C[i]) + ( ( C[i]<(Cmax/2) ) ? 0 : 1 )

... または、C が 16 ビット幅の場合 (符号なし Cmax = 65535 および Cmax/2 = 32768 になります)、次のように書くこともできます。

C[i+1] = 65535 - C[i] + ( ( C[i]<32768 ) ? 0 : 1 )

ここでの秘訣は、カウンターが 1 回だけインクリメントする必要があることです。カウンターが補完的な範囲と「通常の」範囲の両方で増加する場合、変更は発生しません (式は 2 つの値の間で「振動」します)。

 

したがって、チェック C[i]<(Cmax/2) は C の最上位 (15 番目) ビットをチェックすることと基本的に同じであるため、次のようなものを使用して VHDL でこのようなものを簡単に実装できると考えました。

Y <= not(Y) + Y(15);

 

少年、私は「簡単に」について間違っていました:)

最初の問題は、上記の式が 65535+1 になる可能性があることです。この場合、結果には 17 ビットが必要になります (つまり、オーバーフロー)。私の場合、「キャリービット」を切り捨て/無視したいだけです。

これは、何を使用するかという問題につながります。

  • std_logic_vector補数がnot()定義されています。+しかし、 (追加)が定義されていません
  • natural/integer内部で 32 ビットを使用する場合があるため、それらのビット幅は必ずしも指定されていません。算術はサポートしています+が、補数はサポートしていませんnot()
  • 私も試してみunsignedましたが、いくつかの問題もありました(どれを思い出せません)

15 番目 (MSB) のビットは、Y が の場合にのみ抽出できますstd_logic_vector。この場合、Y(15) は単一ですが、それ以外の場合は加算が定義されていないため、型にstd_logic変換する必要があります。integer+

したがって、私の現在のソリューション (以下) には、最初にカウンター レジスタの 2 つのコピーがあります。一つはSIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0); もう一つはSIGNAL tmp_na : natural。それで:

  • 2 つのクロックがあります。1 つは「マスター」@ 50 MHz、もう 1 つは「カウンター」クロックです。マスターは 16 倍に分周された周波数 (3.125 MHz) です。
  • 「カウンター」クロックは、立ち下がりエッジでカウンター値の計算をアクティブにする必要があります
  • 計算はnatural変数を介して実行されます(変数からコピーされますSTD_LOGIC_VECTOR
  • どうやら、最初に変換された場合にstd_logicのみ変換できるようです(ネットで関数を見つけることができて幸運でした)。integerstd_logic_vectorvectorize

ここで最も厄介な部分は、natural変数値をフィードバックする方法でしたSTD_LOGIC_VECTOR。私が作成できる唯一の機能するコマンドは次のとおりです。

wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));

...; ただし、このコマンドは基本的に値を「設定」することに注意してください。この値は、次に同じコマンドを実行したときに「有効」になります。したがって、「カウンター」クロック プロセスでは実行できません。以下のコードでは、より高速な「マスター」クロック プロセスで実行しています。

最後に、以下のコードは機能します (ISE WebPack でビヘイビア シミュレーションを実行します) - しかし、これを解決するためのより簡単な方法があるかどうかを知りたいです。

ご回答ありがとうございます。乾杯!

 

コード:

----------------------------------------------------------------------------------

library IEEE;
  use IEEE.STD_LOGIC_1164.ALL;
  -- use IEEE.STD_LOGIC_ARITH.ALL;
  -- use IEEE.STD_LOGIC_UNSIGNED.ALL;
  use IEEE.NUMERIC_STD.ALL;


ENTITY complement_count_test_tbw IS
END complement_count_test_tbw;

ARCHITECTURE testbench_arch OF complement_count_test_tbw IS

  -- http://www.ingenieurbuero-eschemann.de/downloads/ipicregs/example/vhdl/test/timer_regs_tb.vhd
  -- convert std_logic to std_logic_vector(0 downto 0)
  function vectorize(s: std_logic) return std_logic_vector is
  variable v: std_logic_vector(0 downto 0);
  begin
      v(0) := s;
      return v;
  end;


  -- DECLARE REGISTERS ==========================

  -- 'wires'
  SIGNAL wtCLK : std_logic := '0';

  -- counter register: 16 bit
  SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => 'Z');

  -- temporary 'natural' copy of counter register
  -- http://www.velocityreviews.com/forums/t21700-std_logic_vector-to-unsigned-type-casting.html
  SIGNAL tmp_na : natural;


  -- clock parameters
  constant PERIODN : natural := 20; -- can be real := 20.0;
  constant PERIOD : time := PERIODN * 1 ns;
  constant DUTY_CYCLE : real := 0.5;
  constant OFFSET : time := 100 ns;

  -- freq divisor; with initial values
  constant fdiv : natural := 16;
  SIGNAL fdiv_cnt : natural := 1;
  SIGNAL wfdiv_CLK : std_logic := '0';

BEGIN

  -- initializations of connections:

  -- instances of components, and their wiring (port maps)...
  -- END instances of components, and their wiring (port maps)...


  -- PROCESSES (STATE MACHINES) CODE =========

  -- clock process for generating CLK
  PROCESS
  BEGIN

    WAIT for OFFSET;

    CLOCK_LOOP : LOOP
      wtCLK <= '0';
      -- MUST refresh counter reg here with value of tmp_na
      wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));
      WAIT FOR (PERIOD - (PERIOD * DUTY_CYCLE));
      wtCLK <= '1';
      WAIT FOR (PERIOD * DUTY_CYCLE);
    END LOOP CLOCK_LOOP;
  END PROCESS;

  -- freq divided clock
  freq_divisor: PROCESS(wtCLK)
  BEGIN
    IF rising_edge(wtCLK) THEN -- posedge
      IF fdiv_cnt = fdiv THEN
        -- reset
        fdiv_cnt <= 1 ;
        wfdiv_CLK <= not(wfdiv_CLK);
      ELSE
        fdiv_cnt <= fdiv_cnt + 1;
      END IF;
    END IF;
  END PROCESS freq_divisor;



  -- sim: count
  PROCESS
  BEGIN

    WAIT for 10 ns;

    tmp_na <= 125;

    WAIT for 10 ns;


    TESTCOUNT_LOOP: LOOP

      -- change counter on negedge of freq. divided clock
      WAIT until falling_edge(wfdiv_CLK);

      tmp_na <= to_integer(unsigned(not(wCntReg))) + to_integer(unsigned(vectorize(wCntReg(15))));

      WAIT for 10 ns;

    END LOOP TESTCOUNT_LOOP;

  END PROCESS;

  -- END PROCESSES (STATE MACHINES) CODE =====

-- END IMPLEMENT ENGINE of 'CORE' ===============
END testbench_arch;
-- END ARCHITECTURE -----------------------------
4

1 に答える 1

6

まず、std_logic_arith を使用しないための満点!

ベクトルをunsignedthen (プロセス外) として定義する場合、必要なのは次のとおりです。

  cn_plus_1 <= not cn when cn < halfc else (not cn) + 1;

したがって、次のように割り当てることができますcn_plus_1std_logic_vector

wCntReg <= std_logic_vector(cn_plus_1);

VHDL の慣用的な方法の完全な例をいくつか示します。

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity funny_counter1 is
    port (
        cn: IN unsigned(15 downto 0);
        cn_plus_1 : out unsigned(15 downto 0));
end entity funny_counter1;

architecture a1 of funny_counter1 is
    constant halfc : unsigned(cn'range) := (cn'high => '1', others => '0');
begin  -- architecture a1
    cn_plus_1 <= not cn when cn < halfc else (not cn) + 1;
end architecture a1;

または同期プロセスで:

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

entity funny_counter is
    port (
        clk   : in  std_logic;
        reset : in  std_logic;
        cout  : out unsigned(15 downto 0));
end entity funny_counter;

architecture a1 of funny_counter is
    constant halfc : unsigned(cout'range) := (cout'high => '1', others => '0');
begin
    process (clk) is
        variable c   : unsigned(15 downto 0);
        variable add : integer range 0 to 1;
    begin  -- process
        if rising_edge(clk) then  -- rising clock edge
            if reset = '1' then
                c := (others => '0');
            else
                add := 0;
                if c < halfc then
                    add := 1;
                end if;
                c := (not c) + add;
            end if;
            cout <= c;
        end if;
    end process;
end architecture a1;

std_logic_vectors とs (または同様のもの) の間で多くの変換を行うinteger場合は、通常、実際の数値を使用しています。unsigned/signedベクトルまたは整数のいずれかで作業します。すべての最後にベクトルが必要な場合は、最後に 1 つだけ変換します。必ずしも見ていて楽しいものではありません。ただし、最初に、値を送信する対象がそのインターフェイスで何らかの数値型を使用する必要があるかどうかを確認してください。それが本当に 'bag-of-bits' である場合にのみstd_logic_vector、最高のタイプです。名前wCntRegは私にはカウント値のように聞こえるので、数値型である必要があります。

あなたが持っているこのコード:

wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));

必要よりもわずかに悪い:

wCntReg <= std_logic_vector(to_unsigned(tmp_na, wCntReg'length));

うまくいくはずです。

最後に、これが次のクロック ティックでのみ有効になることについてコメントすると、VHDL がどのように機能するかということになります。それまでに新しい値を使用したい場合は、変数を使用してください。変数はすぐに更新されます。

したがってtempna、変数の場合は、直後に wCntReg 割り当てを行うことができます。

完全を期すために: これ (通常は kludge IME) を回避する別の方法はwait for 0 ns;、シグナル割り当ての後で行うことです。これにより、更新の観点から時間が経過するため、他のすべてのデルタ サイクルが現在の時間 (伝播する信号の割り当てを含む) で実行され、時間が (0ns!) 進み、新しいデルタ サイクルが実行されます。始める。

于 2011-07-15T12:21:09.597 に答える