Papilio One 500Kで遊ぶ その1の続きです。VHDLでTD4もどきを書くところまでやりました。
VHDL開発環境のインストール
http://cellspe.matrix.jp/parallella/inst_ise.htmlに従ってISEをインストールしました。注意としては、ライセンス登録が終わったらrunise.shの内容を
#!/bin/bash . /opt/Xilinx/14.7/ISE_DS/settings64.sh ise
に書き換えないとProject Managerが起動しないというところくらいです。
インストールが終わったらPapilio platform - Getting Started WebPack VHDLを見ながらLED点滅プログラムを動かします。
TD4(もどき)の設計
ここで今回作るCPUの設計を考えます。TD4の基本構成はこんな感じです。
恐ろしく適当な図ですね……。プログラム・カウンタの指定に従ってROMがALUに制御信号を送り、ALUは制御信号に従ってレジスタの値を書き換えるということを表したつもりなのですが伝わらないですね……。詳しく知りたい人はCPUの創りかたを買ってくださいということで。
命令セットは次のようにになっています。
- Mov A, Im (0011XXXX): XXXXをAレジスタに書き込む
- Mov B, Im (0111XXXX): XXXXをBレジスタに書き込む
- Mov A, B (00010000): Aレジスタの値をBレジスタに書き込む
- Mov B, A (01000000): Bレジスタの値をAレジスタに書き込む
- Jmp Im (1111XXXX): プログラム・カウンタの値をXXXXに変更する
- Jnc Im (1110XXXX): キャリーフラグが立っていないときにプログラムカウンタの値をXXXXに変更する
- Add A, Im (0000XXXX): AレジスタにXXXXを足す
- Add B, Im (0101XXXX): BレジスタにXXXXを足す
- Out Im (1011XXXX): XXXXを出力する
- Out B (10010000): Bレジスタの値を出力する
ただでさえ少ないTD4の命令セットから入力命令などを除いたのでさらに少なくなっています。これはキラーアプリであるラーメンタイマーが動けばいいだろうという考えにしたからです。
元ネタではROMをDIPスイッチで実装しているのですが、それも面倒なのでVHDLに直接値を書き込んでいます。とことん怠惰な設計です。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity LEDblink is Port ( A : out STD_LOGIC_VECTOR (15 downto 0); B : out STD_LOGIC_VECTOR (15 downto 0); C : out STD_LOGIC_VECTOR (15 downto 0); clk : in STD_LOGIC); end LEDblink; architecture Behavioral of LEDblink is signal counter : STD_LOGIC_VECTOR(19 downto 0) := (others => '0'); signal PC : STD_LOGIC_VECTOR(3 downto 0) := (others => '0'); signal regA : STD_LOGIC_VECTOR(3 downto 0) := (others => '0'); signal regB : STD_LOGIC_VECTOR(3 downto 0) := (others => '0'); signal carry : STD_LOGIC := '0'; signal temp : STD_LOGIC_VECTOR(4 downto 0); subtype operation is std_logic_vector (7 downto 0); type MEMORY is array (0 to 15) of operation; constant RAM : MEMORY := ( "10110111", "00000001", "11100001", "00000001", "11100011", "10110110", "00000001", "11100110", "00000001", "11101000", "10110000", "10110100", "00000001", "11101010", "10111000", "11110000" ); begin process(clk)begin if rising_edge(clk) then counter <= counter+'1'; if counter = "00000000000000000000" then case RAM(conv_integer(PC))(7 downto 4) is when "0011" => regA <= RAM(conv_integer(PC))(3 downto 0); carry <= '0'; PC <= PC+'1'; when "0111" => regB <= RAM(conv_integer(PC))(3 downto 0); carry <= '0'; PC <= PC+'1'; when "0001" => regB <= regA; carry <= '0'; PC <= PC+'1'; when "0100" => regA <= regB; carry <= '0'; PC <= PC+'1'; when "1111" => PC <= RAM(conv_integer(PC))(3 downto 0); carry <= '0'; when "1110" => if carry = '0' then PC <= RAM(conv_integer(PC))(3 downto 0); else PC <= PC + '1'; end if; carry <= '0'; when "0000" => temp <= ('0'®A) + ('0' & RAM(conv_integer(PC))(3 downto 0)); regA <= temp(3 downto 0); carry <= temp(4); PC <= PC+'1'; when "0101" => temp <= ('0' & regB) + ('0' & RAM(conv_integer(PC))(3 downto 0)); regB <= temp(3 downto 0); carry <= temp(4); PC <= PC+'1'; when "1011" => A <= "000000000000"&RAM(conv_integer(PC))(3 downto 0); carry <= '0'; PC <= PC+'1'; when "1001" => A <= "000000000000"®B; carry <= '0'; PC <= PC+'1'; when others => null; end case; end if; end if; end process; B <= "0000000000000000"; C <= "0000000000000000"; end Behavioral;
VHDLのソースコードはこんな感じです。VHDLは完全に初めてなのでcarryやPCの書き方がものすごく頭が悪いことになっていますね……。しばらく前に書いたので当時何を考えていたのか忘れてしまいました。コメントも書いてないし。
結果
とにかくこれを書き込むとラーメンタイマーが動作しました。
というわけでTD4のキラーアプリラーメンタイマーを動かしてみた pic.twitter.com/YM7qtvUedE
— 川奈 清 (@kivantium) 2015, 3月 30
以上です。次回はもう少しまともなCPUをつくります。