FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RustでRISC-V命令セットシミュレータを作ろう (9. 命令のディスアセンブル機能を実装する)

f:id:msyksphinz:20190224185310p:plain:w400

Rustを使ったRISC-Vシミュレータの調整を行っている。デバッグ・トレース機能の調整を行った。

もうひとつの機能は、命令の機械語からニーモニックを出力する機能だ。 実はこの機能は必須の機能ではない。 spike-dasmというriscv-toolsを使えば、逆アセンブルの情報は命令の機械語から出力することができる。

$ echo "DASM(6ae6ba23)" | spike-dasm
sd      a4, 1716(a3)

これでも良いのだが、せっかくなのでシミュレータ自体にも逆アセンブルの機能を持たせておきたい。 これを実現するために、各命令のニーモニックの情報と、オペランドに関する情報をインプットした。

まずはC++で実装したRISC-Vシミュレータの情報をベースにして、命令のニーモニックオペランドの数、それぞれのオペランドの種類と機械語のどのフィールドから抽出すべきかの情報を格納する。

  • src/riscv_inst_mnemonic.rs
pub fn get_riscv_inst_mnemonic(dec_inst: RiscvInstId) -> String {
    match dec_inst {
...
        RiscvInstId::ADDI        => format!("addi       @,@,@"),
...

ニーモニックに関する情報だ。 命令IDに対して命令オペコードの情報、そしていくつのオペランドを使用するかについて記述している。 @の部分がオペランドになり、これは命令トレースを出力する段階で置き換える。

次に、オペランドの情報だが以下のような構造体を作っている。

  • src/riscv_inst_operand.rs
pub struct OperandInfo {
    pub m_size: u32,                       // オペランド情報のサイズ
    pub m_type_lst: [OperandType; 256],    // オペランドの種類
    pub m_msb_lst: [u32; 256],             // オペランド情報を切り出す命令のMSB
    pub m_lsb_lst: [u32; 256],             // オペランド情報を切り出す命令のLSB
    pub m_connect: [bool; 256],            // 次の情報と結合するか?
}
impl Tracer {
    pub fn format_operand(&mut self)
    {
...
        {
            let mut inst_operand = OperandInfo::new();

            // InstId_t::INST_ID_ADDI
            inst_operand.m_size = 3;
            inst_operand.m_type_lst[0] = OperandType::TypeXReg;
            inst_operand.m_msb_lst[0] = 11;
            inst_operand.m_lsb_lst[0] = 7;
            inst_operand.m_connect[0] = false;
            // ["r[11:7]", "r[19:15]", "h[31:20]"]
            inst_operand.m_type_lst[1] = OperandType::TypeXReg;
            inst_operand.m_msb_lst[1] = 19;
            inst_operand.m_lsb_lst[1] = 15;
            inst_operand.m_connect[1] = false;
            // ["r[11:7]", "r[19:15]", "h[31:20]"]
            inst_operand.m_type_lst[2] = OperandType::TypeHex;
            inst_operand.m_msb_lst[2] = 31;
            inst_operand.m_lsb_lst[2] = 20;
            inst_operand.m_connect[2] = false;
            // ["r[11:7]", "r[19:15]", "h[31:20]"]

            self.m_inst_operand_map.insert(RiscvInstId::ADDI, inst_operand);
        }

これらの情報は命令のディスアセンブル時に使用する。 上記のaddi @,@,@から、@に到達するとオペランドデータベースから当該命令のオペランド情報を取得し、文字列に変換して@の代わりに出力する。

        match self.m_dec_inst {
            Some(id) => {
                let inst_str = get_riscv_inst_mnemonic(id);
                let operand_info = self.m_inst_operand_map.get(&id);
                match operand_info {
                    Some(operand_info) => {
                        let mut at_index = 0;
                        let mut consume_idx = 0;
                        for c in inst_str.chars() {
                            if c == '@' {
                                let msb = operand_info.m_msb_lst[at_index];
                                let lsb = operand_info.m_lsb_lst[at_index];

                                let mask = (1 << (msb - lsb + 1)) - 1;
                                let opr_val = (self.m_inst_hex >> lsb) & mask;

                                match operand_info.m_type_lst[at_index] {
                                    OperandType::TypeXReg    => { print!("x{:02}", opr_val); consume_idx = consume_idx + 3; },
...
                                    _ => panic!("Unknown operand type {:?}", operand_info.m_type_lst[at_index] as u32),
                                }
                                at_index = at_index + 1;
                            } else {

これにより、命令トレース中にシミュレータ自身が逆アセンブルした情報を出力することができる。

ffc28293:addi       x05,x05,0xffc

最終的に、以下のようなトレースデータを出力することができるようになった。

        17:M:Bare:800028dc:00001697:auipc      x13,0x00001        :x13<=00000000800038dc
        18:M:Bare:800028e0:72468693:addi       x13,x13,0x724      :x13=>00000000800038dc x13<=0000000080004000
        19:M:Bare:800028e4:00002717:auipc      x14,0x00002        :x14<=00000000800048e4
        20:M:Bare:800028e8:71c70713:addi       x14,x14,0x71c      :x14=>00000000800048e4 x14<=0000000080005000
        21:M:Bare:800028ec:00c6d693:srli       x13,x13,0x0c       :x13=>0000000080004000 x13<=0000000000080004
        22:M:Bare:800028f0:00c75713:srli       x14,x14,0x0c       :x14=>0000000080005000 x14<=0000000000080005
        23:M:Bare:800028f4:00003797:auipc      x15,0x00003        :x15<=00000000800058f4
        24:M:Bare:800028f8:70c78793:addi       x15,x15,0x70c      :x15=>00000000800058f4 x15<=0000000080006000
        25:M:Bare:800028fc:00a69693:slli       x13,x13,0x0a       :x13=>0000000000080004 x13<=0000000020001000