読者です 読者をやめる 読者になる 読者になる

非公開日記

日記です。自分が興味あることを挫折しながら書いていきます。

CPU開発日記[その2]

今回の目標

前回までで必要なツールなどのインストールが終わったので、CPUの開発を進めていきます。
と言ってもいきなりCPUを作るのは僕には無理です。
そもそもインストールしたツールの使い方すらよくわかっていません。

そこで今回はまずツールやFPGA開発を体験するという意味で、独自CPU開発で学ぶコンピュータのしくみ』(以下参考書)に記載されている 4bit の加算回路をFPGAに書き込んでみて、その動作を確認するということを目標とします。

なお、今回は体験ということで、ほぼ参考書の流れに沿う事になり、オリジナリティとか全然ないです。

書き込み対象の確認

4 bit の加算回路といいましたが、具体的には参考書 P.75-76の図56に記載されているコードが対象となります(実際には図56 を評価ボードに適応させるための"ゲタ"の役目を果たすコードも一緒ですが、それについては後述)。
このコードはgithub でも公開されています。

/mist32e-cpubook/synth_adder_4bit/src/ にある "adder_4bit.sv"と"top_adder_4bit.sv"が今回 FPGAに書き込む対象となります(後者が先ほど申し上げたゲタのほうです)。

書き込み手順

プロジェクトの作成

  1. ディレクトリのダウンロード
    まず書き込みに使うファイルやらをダウンロードします。
    これは上記のgithub からダウンロードできます。今回はZip ファイルをダウンロードしました。Gitを使われている方はclone をしてご自分のPC上にディレクトリを保存してもよいかと思います。
    保存したZip ファイルは展開しておきます。

    f:id:QZ2501:20170110204619p:plain

  2.  次に "Quartus Prime"(その1でAltera のホームページからインストールしたツール)を開きます。

    すると、下記のような画面が表示されるかと思いますので、[New Project Wizard]をクリックします。

    f:id:QZ2501:20170110205124p:plain

  3.  "Introduction"というウィンドウが出ますので、[Next]をクリックします。

    f:id:QZ2501:20170110205910p:plain

  4. 次のウィンドウでは 作業ディレクトリ、プロジェクト名、トップレベルモジュールの設定を行います。
    ●まず作業ディレクトリですが参考書に合わせる意味で手順その1でGithub からダウンロードしたzipファイルを展開してできたディレクトリの /mist32e-cpubook/synth_adder_4bit/synth/を指定します。
    特に指定した作業ディレクトリがあるならそこを指定していいと思います。
    ●次にプロジェクト名ですがここでは"top_adder_4bit"とします。後日ここで違う名前にするとどうなるかについても書くつもりですが、今回は"top_adder_4bit"固定で行きます。
    すると自動で、トップレベルモジュールのほうにも "top_adder_4bit"と入力されると思います。
    ●そしたら、[Next]をクリックします。

    f:id:QZ2501:20170110210903p:plain

  5. 次のページでは"Empty project"が選択されていることを確認し、[Next] をクリックします。

    f:id:QZ2501:20170110211438p:plain

  6.  [Add]の左側にある[...]をクリックし、Githubからダウンロードしたディレクトリの/mist32e-cpubook/synth_adder_4bit/src/ にある"adder_4bit.sv"と"top_adder_4bit.sv"を選択し[開く]をクリックし、追加します。

    f:id:QZ2501:20170110211902p:plain

    ファイルが2つ追加されたことを確認したら[Next]をクリックします。

    f:id:QZ2501:20170110212113p:plain

  7.  次の画面は"Family, Device & Board Settings"ということで使用する機器のファミリー(系統)、具体的な機器がどれか等を設定します。
    FPGAボードとしてTerasic 社 DE0-CVを使用しているので"Device family"の"Familiy"には Cyclone V(E/GX/GT/SX/SE/ST) を選択します。
    その後、"Name filter"に"5CEBA4F23C7"と打ち込むと該当の機器が"Available devices"に表示されますので、そのデバイスを選択して、[Next]をクリックします。

    f:id:QZ2501:20170113200210p:plain

  8. さて、ここまでは参考書通りに問題なく進めましたが、ここから参考書の記載と自分の環境で差異が出ました。
    まず、自分の環境の画面を見てみましょう。

    f:id:QZ2501:20170113200444p:plain
    上記画像の"Simulation" のTool nameと Format(s) として<None> が選択されています。しかし参考書の図ではここには"ModelSim-Altera"と "VHDL"が選択されています。
    参考書ではこの画面では何を変更せずに次へ進むのですが、上記のようにデフォルトの状態に差異があることですし、ここは少し弄り
    "Tool Name"には "ModelSim-Altera" を選択し、"Format(s)"には"VHDL"を選択し、[Next]をクリックします。これで参考書の記載に沿えます。

    f:id:QZ2501:20170114000810p:plain

  9.  Summayが出ますので[Finish]をクリックします。
    以上でプロジェクトの作成は終了です。f:id:QZ2501:20170113201103p:plain

  10. プロジェクトの作成が終わったのですが最後に設定の変更を一つだけ。
    ツールバーの[Assignments]->[Settings]をクリックします。
    "Settings"のウィンドウが表示されたら、Complier Settingsの[Verilog HDL input] をクリックし、"Verilog version" に"System Verilog"を選択し[OK]をクリックします。

    f:id:QZ2501:20170114001314p:plain

    f:id:QZ2501:20170114001325p:plain




    *なお、参考書ではこの後".pof"ファイルの設定を行うなのですが、このエントリでは省略します。
    pof ファイルの設定はAS方式のFPGAの書き込みで使用される設定なのですが、今回はJTAGでの書き込みを行うため不要だからです。
    勿論、省略せず行っても問題ありません。

FPGAのピンとverilog のポートの対応付け

この時点でverilog で書かれたファイルと使用しているFPGAの情報がツールに伝えられました。しかしそれだけではまだ不十分らしく、verilog で記述された各種ポートとFPGAが物理的に持っているピンとの対応付けを行う必要があるそうです。
お芝居の脚本にかかれたキャラクターに実際の人間を配役する、みたいなイメージでしょうか。
具体的な手順をみていきましょう。

  1. まずツールバーにある[Processing]->[Start]->[Start Analysis & Synthesis]を選択し実行します。
    すると、画面左にある"Tasks"という区画にある "Analysis & Synthesis"パーセントの表示がでてきて問題なく進めばチェックマークになります。

    f:id:QZ2501:20170113204310p:plain

    f:id:QZ2501:20170113204316p:plain

    参考書によるとこの手順はポートとピンを対応付けるために必要な『トップモジュールのポートを認識させること』を目的に行われているそうです。
    トップモジュールのポートというのは一番外側、つまり実際にピンとつながるポートだと認識しておいて良さそうです。

  2. 次にピン設定のインポートを行います。
    今回は参考書と同じく事前に用意されたものをインポートする形で行います。
    具体的にはツールバーの[Assignments]->[Import assignment]をクリックします。

    f:id:QZ2501:20170113204939p:plain

  3.  "Import Assignments"というウィンドウがでますので、[...]をクリックします。

    f:id:QZ2501:20170113205046p:plain

  4.  Github からディレクト/mist32e-cpubook/synth_adder_4bit/board_config/にある "pin_default_de0_cv.qsf"を選択し、[開く]をクリックします。

    f:id:QZ2501:20170113205849p:plain

  5. また Import Assignments のウィンドウに帰ってくるので、[OK]をクリックします。

    f:id:QZ2501:20170113205951p:plain

  6.  

    正しくピンがインポートされた見るために [Assignments]->[Pin Planner]をクリックします。すると下記画像のような画面が表示されます。
    僕はこれを見ても正しくインポートされたのかいまいち判断がつきませんが、何のエラーも出てないことをもって良しとしましょう。Pin planner画面は右上の[x]をクリックして消しておきます。

    f:id:QZ2501:20170113210316p:plain

    f:id:QZ2501:20170113210340p:plain

  7.  次に[Processing]->[Start compilation]をクリックします。

    するとTasks 区画にある各task たちの進捗状況が表示されるようになるので全てが完了するまで待ちます。この手順が具体的に何をさせているのか、まだしっかりと把握できていませんがトップモジュールのポートを認識させ、ピン配置を読み込ませた後なのでいよいよポートとピンを対応付けしたということなのでしょうか。
    とりあえず、ポートとピンの対応付けはここまでで完了です。

    f:id:QZ2501:20170113210825p:plain

    f:id:QZ2501:20170113210924p:plain

タイミング制約の追加

さて、まだやることがあるようです。次はクロックの設定などを行うようです。

  1.  ツールバーの[Tool]->[TimeQuest Timinig Analyzer]をクリックします。
    すると、別ウィンドウで"TimeQuest Timing Analyzer" が開かれます。

    f:id:QZ2501:20170113211440p:plain

    f:id:QZ2501:20170113211444p:plain

  2.  

    画面左側のTasks 区画にある"Creat Timing Netlist" をダブルクリックします。

    f:id:QZ2501:20170113211649p:plain

  3.  

    まずはクロックの設定です。
    ツールバーの[Constraints]->[Create Clock]をクリックします。

    f:id:QZ2501:20170113211827p:plain

  4.  "Create Clock"というウィンドウが表示されます。
    "Clock name"は何でもよいらしいので適当に"clock" としておきます。
    "Period"については今回使用しているFPGAボードには50MHzのクロックが搭載されているため 20nsとします。

    f:id:QZ2501:20170113212645p:plain

  5. 次に"Target"の設定を行います。
    [...]をクリックし、"Name Finder" とウィンドウを出たら[List]をクリックします。すると、"xx matches found"という文字とともに候補がリストアップされます。

    f:id:QZ2501:20170113212934p:plain

    f:id:QZ2501:20170113213143p:plain

  6.  

    "CLOCK_50"を選択し、[>]をクリックします。すると、"CLOCK_50" が右側のスペースにも表示されます。その後[OK]をクリックします。

    f:id:QZ2501:20170113213348p:plain

  7.  

    "Create Clock"のウィンドウに戻ってくるので、[Run] をクリックします。

    f:id:QZ2501:20170113214252p:plain

  8.  

    クロックの設定が終わったら次は入力ポートの制約の追加です。
    "TimeQuest Timinig Analyzer"ウィンドウのツールバー上にある[Constraints]->[Set Input Delay]をクリックします。

    f:id:QZ2501:20170113214449p:plain

  9.  

    "Set Input Delay"というウィンドウが開かれます。
    "Clock name"には上記手順4で入力したクロックの名前を入力します。今回の場合には"clock"となります。
    "Input deley options"には "Maximum" と "Rise"を選択します。
    "Delay value"には "1"を入力します。
    また"Add delay" にチェックをいれます。
    なぜこれらの入力を行うのか、よく理解できていませんのが今回は体験することが目的なので何も考えず入力していきましょう。

    f:id:QZ2501:20170113220000p:plain

  10. 次に"Target" を設定するために[...]をタップします。
    すると"Name Finder" が開かれるので[List]をクリックします。
    表示されたリストのなかからKEY[0]~KEY[3]とSW[0]~SW[17]を選択し右側のスペースに移した後、[OK]を押します。

    f:id:QZ2501:20170113215402p:plain

  11.  "Set Input Delay"のウィンドウに戻ってきますので、[Run]をクリックします。
    以上で入力ポートへの制約の追加は終了です。

    f:id:QZ2501:20170113215506p:plain

  12. 最後に出力ポートの制約を行います。
    入力ポートと同じように"TimeQuest Timinig Analyzer"ウィンドウのツールバー上にある[Constraints]->[Set Output Delay]をクリックします。

    f:id:QZ2501:20170113215654p:plain

  13.  "Set Output delay"のウィンドウが開かれたら、
    "Clock name"に"clock"を入力。
    "Maximum"と "Rise"を選択。
    "Delay value"に"1"を入力して、"Add delay" にチェックを入れます。

    f:id:QZ2501:20170113220250p:plain

  14.  "Target" を設定するために[...]をタップします。

    すると"Name Finder" が開かれるので[List]をクリックします。
    表示されたリストのなかからLEDR[0]~LEDR[7]を選択し右側のスペースに移した後、[OK]を押します。
    なお、参考書には "LEDG"となっており自分の環境とは差異があります。
    この差異は何が原因なのかはわかりませんが、特に問題ないようなので気にしないでおきます。
    ちなみにLEDRはLEDR[9]までありますが、LEDR[7]までを選択します。

    f:id:QZ2501:20170113221251p:plain

    f:id:QZ2501:20170113221256p:plain

  15.  "Setup Output Delay"のウィンドウに戻るので[Run]をクリックします。

    f:id:QZ2501:20170113221423p:plain

  16.  各種タイミング制約の追加がこれで完了しましたので、追加した設定をプロジェクトに反映させます。

    Time Quest Timining Analyzer の Tasks 区画の一番下に"Write SD File..."という項目があるのでこれをダブルクリックします。

    f:id:QZ2501:20170113221738p:plain

  17.  "Write SDC File"というウィンドウが出ますが、変更を加えず[OK]をクリックします。

    f:id:QZ2501:20170113221859p:plain

  18.  "TimeQuest Timing Analyzer"を閉じ、"Quartus Prime"のツールバー[Assignments]->"Settings"をクリックします。

    f:id:QZ2501:20170113222206p:plain

  19. ウィンドウが開くので [TimeQuest Timinig Analyzer]を選択し、[...]をクリックします。

    f:id:QZ2501:20170113222521p:plain

  20.  

    上記手順17で書きだしたファイルを選択し、[開く]を押します。

    f:id:QZ2501:20170113222711p:plain

  21.  

    ファイルが白紙だったスペースに追加されたことを確認したら、[OK]をタップします。

    f:id:QZ2501:20170113222841p:plain

  22. 以上までで設定した制約をツールに認識させることができました。ただそれだけでは足りないようです。認識させた後は更新が必要らしいので、[Processing]->[Start compilation]をクリックし、処理が終わるのを待ちます。

    f:id:QZ2501:20170113223133p:plain

書き込み

ここまでとても長かったですがようやく書き込みの段階まで来ました。
今回は手っ取り早く体験をするためにJTAG方式での書き込みを行います。

  1. 上記のようにJTAG方式で書き込みを行うため、ボード上にあるスイッチをいれます。下記の画像の中のスイッチを"RUN"の方向に傾けます。

    f:id:QZ2501:20170113231819j:plain

  2.  

    ボードをACアダプターと接続し、電源ボタンを押します。
    その後、PCとUSBで接続します。

    f:id:QZ2501:20170113232051j:plain

  3.  

    Quartus Prime のツールバーの [Tools]->[Programmer]をクリックします。

    f:id:QZ2501:20170113232252p:plain

  4.  

    "Programmer"ウィンドウが開いたら[Hardware Setup...]の右側に "USB-Blaster"が表示されていることを確認します(僕の環境では"USB-Blaster[USB-0]"と表示されました)。

    f:id:QZ2501:20170113232747p:plain


    表示されない場合には[Hardware Setup]をクリックし、"Available hardware items"にある"USB-Blaster"をダブルクリックして、"Currenly selected hardware"に選択した後、[Close]すればいいそうです。

    f:id:QZ2501:20170113232823p:plain

  5.  さてこの次に参考書では[Auto Detect]をクリックしデバイスを選択した後、[Add file]をするのですが、参考書の記載通りやっても結果が同じになりません。

    実際にやってみましょう[Auto Detect]をクリックしたときに表示されるデバイスが、"5CEBA4"と"5CEFA4"と出て 参考書でDE0-CVのボードを使っている場合に選ぶとされている "5CEBA4F23C7"が出てこないのです。

    f:id:QZ2501:20170113233502p:plain

    とりあえず、"5CEBA4"を選択するととりあえずツール上に"5CEBA4"が表示されます。

    f:id:QZ2501:20170113233625p:plain
    その後参考書の記載に従って[Add file]で.sof ファイルを追加してみると、先に登録された 5CEBA4 に続いて "5CEBA4F23C7"が接続される形になります。

    f:id:QZ2501:20170113233921p:plain
    参考書の図とは全然違う様子になりました。
    ちなみにこのまま書き込みを行っても正常に動作しないので、このエントリでは参考書の記載を一部異なる手順をとります。

    具体的には[Auto Detect] を行わず [Add file]をクリックし、sof ファイルを追加します。この手順ですと "5CEBA4F23C7"のみがツールに認識され、参考書の記載の結果と同じ形になります。

    f:id:QZ2501:20170113234400p:plain


    また、要らないデバイスはクリックして選択した後に[Delete]をクリックすることで消すことができます。

    f:id:QZ2501:20170113234451p:plain

  6. "Program/Configure"にチェックが入っていることを確認したら、[Start]をクリックします。
    "Progress"が100% になったら書き込み完了です。

    f:id:QZ2501:20170113234720p:plain

結果確認

長い作業お疲れ様でした。
以上までで書き込みが完了です。
では結果のほうを見てみましょう。
今回の回路ではスイッチが入力、LEDが出力となっています。
またスイッチは一番右側から4つ(SW[0]~SW[3])とそのまた次の4つ(SW[4]~SW[7])は別々の入力として扱われています。

f:id:QZ2501:20170114001742j:plain


SW[0]~SW[3]で表現される4bit と SW[4]~SW[7] の4bit の加算結果が LED に表示されるという仕組みです。

実際にスイッチを上げたり下げたりしてLEDの点灯の変化を試してみてください。
下記図は 0001 + 0001 = 0010 という計算結果が表示されており、ちゃんと4bit の加算回路が動いていることがわかります。

f:id:QZ2501:20170114000221j:plain

最後に

というわけで初めてのFPGA開発でした。
今後少しずつ慣れていきたいと思います。
ではまた。