(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');
あとは各種ファンクション生成部を作るだけ。
ただ、これが面倒そう。ゴリゴリ書くのは嫌なのでどうにか工夫したい。