CPUの創りかたのTD4を少し変更した


CPUの創りかたのTD4をDE0-CVに実装した

TD4は命令フェッチ~実行までのすべてのステップを1クロックでやっている。
これは結構無茶な作りで、1ステップ1クロックでやるのが普通(のはず)。

TD4は周波数も遅いしIC10個でやるという制約もあるしで1クロックでぜんぶ処理する
作りになっていると思われるが、TD4をベースに色々と手を加えることを考えると
現実的なCPUの作りに近づけたほうが良いと思った。

あと、前の記事で作ったやつは74シリーズのロジックICをHDLにして回路図に近い形で
結線していたけれど、後の拡張とメンテナンスを考えると得策でないのでこれも
変更したかった。

ということでTD4の機能はそのままで色々と手を加えた。

まず処理はこんな感じ。パイプライン化は考えない。
f:id:m_keishi2006:20180103005754p:plain

アーキテクチャはこんな感じ。
TD4の基本構造はそのままで、ロジックICで作っていたものを一つのモジュールにした。
f:id:m_keishi2006:20180103005744p:plain

で、実装したものがこちら。
ほぼ一発でラーメンタイマーが動き大成功。
DE0_CV_TD4_mkII.qar - Google ドライブ

次にやりたいのはデータのバス幅を4bitから8bitにしたいこと。 これは今回作ったやつでconstantで仕込んだので、うまくすればそんなに面倒でない。
→8bit化できた。TD4_ROM_PKG.vhdのcData_widthを書き換え、
 rom_dataの値を変えれば4bitと8bitの切替可能。
 ちょっと修正したものをgoogle driveに再upload

あとはALUの機能UP。 最低でも74181相当の機能は欲しい。

命令もなにかに合わせて作ってみたいけど、まずはTD4ベースで。。
ExcelででもVisualStudioででもコンパイラ作ろうかな。(命令決まらないとできないけど)

CPUの創りかたのTD4をDE0-CVに実装した

RISC-VをDE0-CVに実装しようとしたけど、そもそもCPUの知識がなくて挫折した。

ので、CPUの創りかたのTD4を実装することにした。

実装したものがこちら。QuartusII 17.0.0 でアーカイブ
DE0_CV_TD4.qar - Google ドライブ

Simしてないのでバグあるかも。。。
追記ここから
 SimしたらLOAD3のデコードがバグってたので修正
 Sim環境もqarに含んだ。
 qarを展開したらsimフォルダが入っているので、ModelSimでsimフォルダに移動
 do tb_DE0_CV_TD4.do と叩くと勝手にコンパイルしてSimが走る。
 Simのときはif-generateで50MHzで動くようにしている。
追記ここまで

周波数が50MHzだと早すぎるので、PLLで5MHzに落としたあと
カウンタで524288分周して約9.5Hzにしている。
PLL使っているのでバージョンが違うとめんどくさいかも。

基本的には74シリーズをVHDL化して、CPUの創りかた275ページ「CPUの全回路図」通りに結線した。
ただ、74HC10や74HC32で作っているデコーダ部分は面倒なのでそのままTD4_Decoder.vhdで直書きした。

プログラムはTD4_ROM.vhdに直書きする。 いまはラーメンタイマーのプログラムを書いている。

    constant cdata : rom_vector :=
    ( X"B3", X"01", X"E1", X"01", X"E3", X"B6", X"01", X"E6",
      X"01", X"E8", X"B0", X"B4", X"01", X"EA", X"B8", X"FF"
    );

変換は、CPUの創りかたを見つつ、ハンドアセンブル

9.5Hzだからなのか、なんかすごく処理が早い気がする。
フラグのところ間違ってないよな……?
上で追記したバグのせいだった。

(DE0-CV) FPGAマガジンのRISC-VをDE0-CVに実装する

FPGAマガジン No.18でRISC-Vを実装していたので自分もやってみる。

FPGAマガジンだとVerilogで記述しているけど、自分はVHDLで記述する。
なおかつ実装するのはDE0-CV。

ソースコードは紙面に載っているだけでサポートとかでの提供はないっぽい?
紙面を読みながらまずはfmrv32im_decode.vを実装してみる。

まずはentity

entity fmrv32im_decode is
    port (
        RST_N           : in    std_logic;
        CLK             : in    std_logic;

        -- インストラクション・コード
        INST_CODE       : in    std_logic_vector(31 downto 0);

        -- レジスタ番号
        RD_NUM          : out   std_logic_vector( 4 downto 0);
        RS1_NUM         : out   std_logic_vector( 4 downto 0);
        RS2_NUM         : out   std_logic_vector( 4 downto 0);

        -- イミディエイト
        IMM             : out   std_logic_vector(31 downto 0);

        -- 命令
        INST            : in    t_INST
    );
end entity;

INSTはズラッと並べるのもしんどいのでpackageファイルでrecodeタイプ宣言している。 こんな感じ。

    type t_INST is record
        INST_LUI            : std_logic;
        INST_AUIPC          : std_logic;
        INST_JAL            : std_logic;
        INST_JALR           : std_logic;
--------------------------------------------
--      略
--------------------------------------------
        INST_DIV            : std_logic;
        INST_DIVU           : std_logic;
        INST_REM            : std_logic;
        INST_REMU           : std_logic;
    end record;

次は命令タイプの判別とイミディエイトの生成部分

    -- イミディエイト判別
    sIMM.i_type <= JUDGE_I(INST_CODE);
    sIMM.r_type <= JUDGE_R(INST_CODE);
    sIMM.s_type <= JUDGE_S(INST_CODE);
    sIMM.b_type <= JUDGE_B(INST_CODE);
    sIMM.u_type <= JUDGE_U(INST_CODE);
    sIMM.j_type <= JUDGE_J(INST_CODE);
    c0_type     <= '0';
    
    -- イミディエイト生成
    process (CLK, RST_N) is
    begin
        if RST_N = '0' then
            IMM <= (others => '0');
        elsif rising_edge(CLK) then
            -- 各イミディエイトとtype判定結果をandすることで、
            -- 判定したtypeのイミディエイトのみ残して他をマスクしている。
            -- 多bit and 1bit というのはVHDL-2008の構文
            -- (多bitすべてに1bitをandする回路になる)
            IMM <= ( (IMM_I(INST_CODE) and sIMM.i_type)
                  or (IMM_S(INST_CODE) and sIMM.s_type)
                  or (IMM_B(INST_CODE) and sIMM.b_type)
                  or (IMM_U(INST_CODE) and sIMM.u_type)
                  or (IMM_J(INST_CODE) and sIMM.j_type) );
        end if;
    end process;

sIMMもpackageで定義したrecordタイプの信号。

    type t_IMM is record
        r_type : std_logic;
        i_type : std_logic;
        s_type : std_logic;
        b_type : std_logic;
        u_type : std_logic;
        j_type : std_logic;
    end record;

JUDGE_*()はfunction。 FPGAマガジンの実装と違い、こんな感じでそのタイプに属するOPCODEを判定してORしている。
OPCODEもpackageで定義したrecordタイプ。

    function JUDGE_R(
        INST_CODE : std_logic_vector(31 downto 0)
    ) return STD_LOGIC is
        variable O : std_logic_vector(4 downto 0);
    begin
        O := INST_CODE(6 downto 2);  -- OPCODE
        return ( O ?= OPCODE.OP_ADD or O ?= OPCODE.OP_SUB or O ?= OPCODE.OP_SLT or O ?= OPCODE.OP_SLTU
              or O ?= OPCODE.OP_AND or O ?= OPCODE.OP_OR  or O ?= OPCODE.OP_XOR
              or O ?= OPCODE.OP_SLL or O ?= OPCODE.OP_SRL or O ?= OPCODE.OP_SRA
             );
    end function;

ちなみにR-TYPEだとOPCODEぜんぶ同じ。論理合成で消える。
命令ぜんぶ書いたほうがわかりやすいかな。。と思ってこんな実装にしたけど、記述ミスでバグが入りそう……。
あとで作り直すかも。

?=ってのはVHDL-2008の構文。

イミディエイト生成箇所は、IMM_*()というfunctionで各イミディエイトを抽出して、イミディエイト判定結果で
andしてマスクするという作りにした。

イミディエイト抽出しているfunctionはこんな感じ。
RISC-V仕様書のChapter2, 2.2 Base Instruction Formatに書いてあるとおりに作った。

    -- Create Immediate for I_TYPE
    --  31          11 10        5 4         1        0
    --  -- inst[31] -- inst[30:25] inst[24:21] inst[20]
    function IMM_I(
        INST_CODE : std_logic_vector(31 downto 0)
    ) return std_logic_vector(31 downto 0) is
        variable tmp : std_logic_vector(31 downto 0);
    begin
        for i in 31 downto 11 loop
            tmp(i) := INST_CODE(31);
        end loop;
        tmp(10 downto  5) := INST_CODE(30 downto 25);
        tmp( 4 downto  1) := INST_CODE(24 downto 21);
        tmp(           0) := INST_CODE(          20);
        return tmp;
    end function;

レジスタ番号生成部はFPGAマガジンそのまま。
上の処理と整合性を取るならこれもfunction化すべきかも。

    -- レジスタ番号生成
    RD_NUM  <= INST_CODE(11 downto  7) when (sIMM.r_type or sIMM.i_type or sIMM.u_type or sIMM.j_type or c0_type)
        else (others => '0');
    RS1_NUM <= INST_CODE(19 downto 15) when (sIMM.r_type or sIMM.i_type or sIMM.s_type or sIMM.b_type)
        else (others => '0');
    RS2_NUM <= INST_CODE(24 downto 20) when (sIMM.r_type or sIMM.s_type or sIMM.b_type)
        else (others => '0');

あとは各種ファンクション生成部を作るだけ。
ただ、これが面倒そう。ゴリゴリ書くのは嫌なのでどうにか工夫したい。

FPGAで、VHDLで書いたinteger信号の範囲は守られるのだろうか

例えばVHDLで下記のようなintegerの信号を定義したとする。

signal sCountVal : integer range 0 to 9;

この信号は0~9の値を取る、と宣言している。
これはFPGAでどのように論理合成されるのだろうか。

FPGA上では信号線で値を表現するので、例えば信号線3本、3bitの信号なら0~7の値を取り、
信号線4本、4bitの信号なら0~15の値を取る。
値の取りうる範囲は2のべき乗にしかなり得ない。

0~9を表現できるのは4bitだが、では10~15はどうなるのだろうか。
10~15の値にならないように論理合成されるのだろうか。
DE0-CVでちょっと試してみよう。

DE0-CVの4つのプッシュスイッチのうち2つ(KEY0とKEY1)と7セグメントLEDを使って動作を確認する。
KEY0を押したらintegerのカウンターをカウントアップ
KEY1を押したらintegerのカウンターをカウントダウン
カウンターの値を7セグメントLEDに出力
という回路を作った。

コンパイルはQuartus Prime 17.0で行った。

    --------------------
    -- 略
    --------------------

    -- DE0-CVのPushスイッチ4つの立ち上がりエッジ信号
    signal sKEY_hiedge          : std_logic_vector( 3 downto 0);

    -- integerのカウンター
    signal sCountVal            : integer range 0 to 9;

begin
    --------------------
    -- チャタリング除去とか立ち上がりエッジ検出とかの記述
    -- 略
    --------------------

    -- up/down count
    process (CLOCK_50, RESET_N) is
    begin
        if (RESET_N = '0') then
            sCountVal <= 0;
        elsif rising_edge(CLOCK_50) then
            if (sKEY_hiedge(0) = '1') then
                sCountVal <= sCountVal + 1;  -- KEY0を押したらカウントアップ
            elsif (sKEY_hiedge(1) = '1') then
                sCountVal <= sCountVal - 1;  -- KEY1を押したらカウントダウン
            end if;
        end if;
    end process;

    --------------------
    -- 値を7セグメントLEDに出力
    -- 略
    --------------------

動かしてみた。

FPGAにsofファイルを書き込むと7セグメントLEDに0が表示される。
KEY0を押すとカウントアップする。
9までカウントアップした。
f:id:m_keishi2006:20170729230721j:plain

もしVHDLの記述通り、範囲が0-9であるのであれば次のカウントアップでオーバーフローして値が0になるはずである。
KEY0を押してみた。
f:id:m_keishi2006:20170729230729j:plain

7セグメントの値がA(16進表記)に変化した。
VHDLで記述したrangeの範囲を超えてしまった。
そのままカウントアップすると、F(16進表記)まで変化して、
f:id:m_keishi2006:20170729230735j:plain

もう一度押すとオーバーフローして0に戻った。
f:id:m_keishi2006:20170729230743j:plain

ちなみに0からカウントダウンするとFになった。

つまり、FPGAの実装としては4bit信号でカウンターが構成されているということ。
integerのrangeはあくまで何bitの信号とするのか決めるだけであり、値の範囲の制御はちゃんと記述する必要がある。
これを認識しないと思い違いによるバグを生みかねないので注意しよう。

ちなみにSTARCではintegerのrangeの範囲は2のべき乗にしなければならないというルールがあったはず。
(うろ覚えだけど)

追記:
ModelSimでカウントアップしてみた。
integerの範囲を超えるたときにFatal ErrorになってSimが止まった。
SimのFatal Errorが誤動作の原因になるような記述を見つける指標になると思う。

VisualStudioでVHDL編集する (2)


VisualStudioでVHDL編集する (1)

V3SのTrialライセンスは1ヶ月間のみ有効 1ヶ月後は使えないので、それ以上使いたいならライセンスを買う必要がある。
ここではPrivateライセンスの購入方法を解説する。

Privateライセンスを買う

V3Sのライセンス体系はこのようになっている。
f:id:m_keishi2006:20170717121726p:plain

Privateライセンスは$42で、だいたい\5,000以下で買うことができる。
有効期限は1年間。
これを高いと思うか安いと思うかは人それぞれ・・・。
お金を出すか出さないか判断するためにも、Trialライセンスで十分に試そう。

Privateライセンスのところの「Purchase」をクリックすると下記の画面になる。
f:id:m_keishi2006:20170717164756p:plain

名前とメールアドレス、そして使用するPCのMACアドレスを入力する。 ライセンスはMACアドレスに紐付いているので、ここで登録したMACアドレスのPC以外からの利用はできない。

必要情報を入力して次に進むとこの画面になる。
f:id:m_keishi2006:20170717165106p:plain

入力は日本語で大丈夫だった。
クレジット、PayPalAmazonのいずれかで支払うことができる。
自分はPayPalで支払った。

お金を支払うと、ViDE-Softwareから「v3s-private-license.lic」というファイルがメールで送られてくる。
これをPCのどこかに格納する。

ライセンスをV3Sで読み込む

VisualStudioを起動し、V3S - Preference - Licensing を開く。
Path to license file をチェックして、「v3s-private-license.lic」の場所を読み込ませる。
License Summary がチェックマークになったら読み込み成功。
これで1年間、V3Sを利用できるようになった。
f:id:m_keishi2006:20170717165937p:plain
※画像は色々と情報を消してます。

MACアドレスを変更する。

MACアドレス変更にはSerial Numberが必要です。

MACアドレスを変更にはライセンスのシリアルナンバーが必要である。
シリアルナンバーは Preference - Licensing の Serial Number から確認可能。
v3s-private-license.licをテキストエディタで開いてもSerial Numberが書いてある。

MACアドレスを変更するということはPCを変更するということ。
gmail等でライセンスファイルを受け取った人は良いが、PCにしか保存していない人は、
シリアルナンバーがわからずにMACアドレス変更に苦労するかもしれない。
ライセンスファイルが送られてきたメールはできればクラウド上に保管しよう。

MACアドレス変更はとってもかんたん
ここのMACアドレス変更URLから変更しよう。

注意!
かんたんにMACアドレスを変更できるのは1回のみ。 2回目からはViDE-Softwareに連絡しなきゃならない。

VisualStudioでVHDL編集する (1)

V3SというVisualStudioのアドインを使う。

開発元はViDE-Software

ライセンス形態はこんな感じ。
自分は1ヶ月のTrialライセンスを使ってみて、良さそうだと感じたのでPrivateライセンスを購入した。
f:id:m_keishi2006:20170717121726p:plain

Trialライセンスを使う

  1. VisualStudioをインストールする

V3SはVisualStudioのアドインなので、当然ながらVisualStudioをインストールしないと使えない。
VisualStudioをインストールしよう。
V3SはVisualStudioのバージョンごとに提供されており、2017-07-17では

VS2010, VS2012, VS2013, VS2015, VS2017

に対応したバージョンが提供されている。

なお、アドインはVisualStudio Expressでは利用できないので注意。
Community版を使用しよう。(Pro版が使える環境ならそちらで)

※4月にインストールしたときはVS2017だとプロジェクトを開いたときにVisualStudioが落っこちて使えなかった。
 Updateされたのかわからないが、ひとまずVS2015バージョンで説明する。

VisualStudioのインストールは解説記事がたくさんあるので説明するつもりはない。
つもりだったが、VisualStudio2017がリリースされてから以前のバージョンをインストールするのが面倒になっていたので
(自分の覚え書きも兼ねて)簡単に説明する。

VisualStudioのダウンロードページを開く
ダウンロード | IDE、Code、Team Foundation Server | Visual Studio

最新版のVisualStudioであれば VisualStudio Communityの「無償ダウンロード」をクリックすればインストーラが手に入るので
そのままインストールすればOK。

それより以前のバージョンをお探しの場合はここでは見つからないので、下の方にグリグリスクロールして「以前のバージョン」をクリックする。
f:id:m_keishi2006:20170717123504p:plain

表示されたページの「Visual Studio (MSDN) サブスクリプションにサインインする」をクリックする。
f:id:m_keishi2006:20170717124033p:plain

Microsoftアカウントでサインインするように促されるのでサインインする。
もしMicrosoftアカウントを持っていない人は作成する必要がある。

・・・すごく厭らしい。まぁ、VisualStudio利用者はMicrosoftアカウントを持っている人が多いと思うが。。

ちなみに、「MSDN サブスクリプションをお持ちでない方へ」というところの「今すぐ無料で参加する」ではインストーラは手にはいらないので注意。
そもそもそれもMicrosoftアカウントを要求してくる。

サインインしたら、検索窓で「Community 2015」とでも検索すればCommunity 2015のインストーラが手に入る。
インストールするのは Visual Studio Community 2015 with Update 3 とかで良いんじゃないかな。
その他のバージョンが欲しい場合は2015のところを必要なバージョンに変更すれば良い。

  1. V3Sをインストールする。

V3SはVisualStudioのMarketplaceからダウンロードできる。 下記からインストールしているVSのバージョンを選んでダウンロードし、インストールする。

marketplace.visualstudio.com

インストールしているVSバージョンと違うものはインストールできないようになっている。

インストール後、VisualStudioを起動してメニューバーに「V3S」が見えたらインストールできている。
f:id:m_keishi2006:20170717125434p:plain

  1. V3Sを使ってみる。

「新しいプロジェクト」のウィザードに、「V3S-Extension」が追加されているので選ぶ。
適当にプロジェクトを作成する場所を選んでOKを押す。 f:id:m_keishi2006:20170717125844p:plain

こんなウィザードが出てくる。
f:id:m_keishi2006:20170717130212p:plain

AlteraのProjectファイルやXilinxのProjectファイルから、登録されているソースを自動で読み込んでくれる機能を持っている。 ただ、この機能を利用するとVisualStudioプロジェクトを作成する場所を間違うとVisualStudioが落っこちるので注意。 (そのうち解説できればいいな~)

ここではSample Projectを選ぶ。
SampleProjectが作成されると、ソリューションエクスプローラーにソースファイルが表示される。
f:id:m_keishi2006:20170717130721p:plain

試しにspi.vhdを開いてみるとこんな感じで表示される。
f:id:m_keishi2006:20170717130931p:plain

シンタックスハイライトだけでなく、マウスオーバーすると定義が表示される。
これで「あれ、この信号は何bitだったっけ?」とか「このrecordタイプの信号って何があったっけ?」とか悩まずに済む。
f:id:m_keishi2006:20170717131220p:plain
f:id:m_keishi2006:20170717131543p:plain

その他にもVisualStudioのショートカット機能やスニペット機能を使える。

設定を変えたい場合はメニューバーで V3S - Preference から。 Code Coloring で色を変更できる。

なお、Verilogシンタックスハイライトしか対応していない。
VHDL相当の機能は開発中らしい。

とりあえずTrial版の説明はここまで。
次回はPrivate版の購入方法を解説する。

VisualStudioでVHDL編集する (2)

PC購入

使用していたPC(HP Z400)がOSの修復中から起動しなくなったので新しいPCを購入。
(セーフモードですら起動しなくなった)

CADとかするだろうということでWorkstationなんぞを使っていたけれど、ぶっちゃけCADなんかやらなかった。
今回は、CPUはCore i7とし、GPUモロモロを旧PCから流用するつもりでドスパラで組んだ。
流用するのはGPU(GTX970とQuadro2000)、HDD(1TBx2でRAID構成だった。今回はRAIDにせず計2TBで使用)
メモリは残念ながら再利用不可だった。再利用しようと思って刺せなくて気が付いた。
旧PCはPC3-10600で、新PCはPC4-19200。会社のPCがPC3-10600だったと思うので会社に持っていこう。

覚書として新PCのスペックを記載。

項目 内容
OS Windows10 Pro 64bit
メモリ 16GB DDR4 SDRAM(PC4-19200/8GBx2/デュアルチャネル)
電源 玄人志向 700W 静音電源 (80PLUS TITANIUM / KRPW-TI700W/94+)
CPU インテル Core i7-7700K (クアッドコア/HT対応/定格4.20GHz/TB時最大4.50GHz/L3キャッシュ8MB)
SSD 500GB SSD
HDD1 2TB HDD
HDD2(*) 1TB HDD
HDD3(*) 1TB HDD
光学ドライブ ブルーレイドライブ (読み書き対応)
GPU1(*) GeForce GTX970
GPU2(*) Quadro 2000
Officeソフト Microsoft® Office Home and Business 2016 (Word/Excel/PowerPoint/Outlook/OneNote)

約21万円だった。
※HDD2,HDD3,GPU1,GPU2は旧PCから流用

HDD2とHDD3が認識しない……。
ディスクの管理でも出てこないので、たぶん電源をつながなきゃならないのだと思う。
SATAケーブルしかつないでない。旧PCがそうだったから……)

Quadro2000からちゃんと映像出力されない。マウスカーソルしか見えない。
なんか直近のWindows10のアップデートからこんな感じ。 旧PCで何とかしようと電源のON/OFFくりかえしてたらOSがぶっ壊れた。。のでもう諦めることにする。

せっかくCore i7-7700Kを積んでいるので、GTX1080Tiとか載せてみたい。
そんなお金ないけど。