0

私が持っているデュアルチャネルADCから値を読み取るには、継続的なSPI通信を行う必要があり、そうするための一種のステートマシンを作成しました。しかし、2番目のチャンネルを読み取る状態にはなっていないようで、その理由がわかりません。これがVHDLです...

SPI_read: process (mclk)
                                                        --command bits: Start.Single.Ch.MSBF....
    constant query_x: unsigned(ADC_datawidth-1 downto 0) := "11010000000000000";    -- Query ADC Ch0 ( inclinometer x-axis)
    constant query_y: unsigned(ADC_datawidth-1 downto 0) := "11110000000000000";    -- Query ADC Ch1 ( inclinometer y-axis)

begin

    if rising_edge(mclk) then

        -- when SPI is not busy, change state and latch Rx data from last communication
        if (SPI_busy = '0') then

            case SPI_action is
                when SETUP => 
                    SPI_pol <= '0'; -- Clk low when not active
                    SPI_pha <= 1;       -- First edge is half an SCLK period after CS activated
                    SPI_action <= READ_X;
                when READ_X =>
                    SPI_Tx_buf <= query_x; -- Load in command
                    y_data <= "00000" & SPI_Rx_buf(11 downto 1);
                    SPI_send <= '1';
                    SPI_action <= READ_Y;
                when READ_Y =>
                    SPI_Tx_buf <= query_y; -- Load in command
                    x_data <= "00000" & SPI_Rx_buf(11 downto 1);
                    SPI_send <= '1';
                    SPI_action <= READ_X;
            end case;

        else
            SPI_send <= '0'; -- Deassert send pin
        end if;

    end if;

end process SPI_read;

コマンドはTxバッファに送信され、最後に受信したデータの値が信号に書き込まれ、7セグメントディスプレイに出力されます。転送を開始するにはSPI_sendからのパルスが必要であり、開始すると、転送が完了するまでSPI_busyがハイに設定されます。

現時点では、SPIを介してquery_xのみを送信します。スコープで確認できるため、これを知ることができます。ただし、興味深いことに、両方のディスプレイに同じ値が出力されているため、出力されているTxデータは変更されていませんが、まだREAD_Y状態になっていると思います。

私はこのコードを何時間も見つめていますが、理解できません。新鮮な目で生活が楽になることもあるので、何か見つけたら教えてください。また、私はこれに対処するためのより良い方法の提案を非常に受け入れています。私はVHDLを学んでいるだけなので、ほとんど正しい方法で物事を行っているかどうかさえわかりません。

4

3 に答える 3

1

これまでの私のコメントを答えに要約します。

このプロセス/モジュールをSPIマスターコンポーネントでシミュレートします。

あなたのアプローチは一般的に正しいですが、ステートマシンを少し再構築して、各spiトランザクション(wait_x、wait_y)の間に明示的な待機状態を置き、モジュール間でより堅牢なハンドシェイクを行うことをお勧めします。ハイにした後、ビジーがローになるまでwait_xに留まります。

sendが2サイクルの間アサートされているようで、各サイクルでread_xとread_yの両方を遷移しています。 タイミング図

このプログラム http://wavedrom.googlecode.com/svn/trunk/editor.htmlから このソースを使用して引用:

{ "signal" : [
  { "name": "clk",           "wave": "P........" },
  { "name": "busy",          "wave": "0..1.|0..1"},
  { "name": "SPI_Action",    "wave": "====.|.==.",   "data": ["SETUP", "READ_X", "READ_Y", "READ_X", "READ_Y", "READ_X", "READ_Y", ] },
  { "name": "SPI_send",      "wave": "0.1.0|.1.0",   "data": ["0", "1", "Load", "Start","WaitA"] },
  { "name": "SPI_Tx_buf",    "wave": "x.===|..==", "data": ["query_x","query_y","query_x","query_y","query_x"],},
]}
于 2013-01-04T01:19:46.203 に答える
0

あなたは正しい基本的な考え方に沿って考えていますが、他の答えが言うように、ステートマシンにはまったく正しくないさまざまなことがあり、これらはシミュレーションで簡単に見つけることができます。

例えば

        when READ_X =>
            SPI_Tx_buf <= query_x; -- Load in command
            y_data <= "00000" & SPI_Rx_buf(11 downto 1);
            SPI_send <= '1';
            SPI_action <= READ_Y;

ここで、SPI_Busyが2サイクル目ローのままである場合、これは明らかにREAD_X状態にとどまらず、直接READ_Yに移行しますが、これはおそらくあなたが望むものではありません。

より通常のステートマシンは、状態を最も外側として扱い、入力信号を状態ごとに異なる方法で解釈します。

   case SPI_Action is             
      when READ_X => 
                     if SPI_Busy = '0' then    -- Wait here if busy
                        SPI_Tx_buf <= query_x; -- Load in command
                        y_data <= "00000" & SPI_Rx_buf(11 downto 1);
                        SPI_send <= '1';
                        SPI_action <= READING_X;
                     end if;
      when READING_X =>
                     if SPI_Busy = '1' then   -- command accepted
                        SPI_action <= READ_Y; -- ready to send next one
                     end if;
      when READ_Y => 
                     if SPI_Busy = '0' then    -- Wait here if busy

このバージョンはビジー信号をハンドシェイクとして扱い、ビジー状態が変化したときにのみ進行することがわかります。必要に応じて、アプローチ(最も外側の場合)を機能させることができると確信していますが、ハンドシェイクの原則を自分で適用する方法を理解する必要があります。

XもYも読み取られていない「アイドル」状態もありません。このSMは、XとYをできるだけ速く交互に読み取ります。通常、両方を読み取り、他の開始信号がアイドルを終了して新しい読み取りセットを実行するように指示するまで、アイドルに戻ります。

「whenothers」句を使用して、ステートマシンをより堅牢にすることもできます。定義されたすべての状態をカバーすることを保証する場合、これは必須ではありませんが、メンテナンスが容易になります。一方、そのような句がないと、コンパイラはカバーされていない状態を通知し、間違いを防ぎます。

「whenothers」句が不可欠であり、合成ツールが「whenothers」句からより安全であるが最適ではないステートマシンを生成するという神話があります。ただし、合成ツールがステートマシンを生成する方法を制御するための合成属性またはコマンドラインオプションがあります。

于 2013-01-04T11:58:58.223 に答える
0

Davidが提起した問題に同意し、いくつかのメモを追加する必要があります。

コードにあまり良くないことがいくつかあります。まず、州のリストに「whenothers=>」が含まれている必要があります。SPI_sendを除いて、どの信号にもデフォルト値がないようです。また、ステートマシンをifステートメント内に配置することもお勧めしません。そして、それをしなければならない場合は、両方の場合にすべての信号を設定する必要があります。そうしないと、フリップフロップではなくラッチになってしまいます。これを行う簡単な方法の1つは、コードの開始時にすべてのシグナルをデフォルト値に設定し、必要に応じて変更することです。

このようにして、合成ツールはそれらを処理する方法を認識し、あなたは正しくなります。

これは、Siemenceの合成用VHDLドキュメントからのものです。

シグナルまたは変数にifステートメントのすべての可能な *ブランチで値が割り当てられていない場合、ラッチが推測されます*。ラッチを推測しない場合は、ステートメントのすべてのブランチでシグナルまたは変数に値を明示的に割り当てる必要があります。

ガイドはPDF形式で次の場所にあります:http ://web.ewu.edu/groups/technology/Claudio/ee360/Lectures/vhdl-for-synthesis.pdf

于 2013-01-04T09:23:48.857 に答える