LoginSignup
4
2

More than 3 years have passed since last update.

RISC-V(RV32I)のアセンブリから機械語への翻訳(簡易)

Last updated at Posted at 2020-04-29

はじめに

先日、RISC-Vのシングルサイクルデータパスを実装したのですがその時に予め命令メモリに書くべき機械語を作成するのが結構めんどくさかったので、表題のように簡単なアセンブラを機械語に翻訳してくれるプログラムを作ってみました。
ただ、全然アセンブラとかコンパイラの勉強をしたわけではなく、ISAのマニュアルを見てアセンブリと機械語の関係をそのままプログラムしただけなので、色々至らないところは山ほどあると思います。

自分の作成したプログラムはこちらのgithubに置いてあります。

概要

例えば、下の図のようにアセンブリが書かれたファイルがあるとして、
コメント 2020-04-28 213214.png
それを読み込んで下の図のような機械語を出力するプログラムを作成しました。

コメント 2020-04-28 213214.2.png

特徴

メリット

  • RISC-VのRV32Iの命令に対応している
  • Scalaで書かれているのでChiselで開発している人は違う開発環境を立ち上げなくてよい
  • Assembler.scalaとAssembly.txtの二つのファイルとscalaの実行環境さえあれば特殊な設定とかしなくてもすぐに実行できる
  • アセンブラに書くときはスペースとかtabがいくつとか気にしなくてよい。翻訳時に1spaceに変換しているので。

デメリット

  • アセンブラとか分かんないへっぽこプログラマが書いたので、labelとか飛ぶとかで飛ぶとか無理

実際のコード

Assembler
import scala.io.Source

object Assembler extends App {
  val path1 = System.getProperty("user.dir") //現在のディレクトリパス
  val lines = Source.fromFile("Assembly.txt").getLines()
  var counter = 0
  lines.foreach { c =>

    // 2文字以上続くスペースを削除して、もろもろspaceに置き換えて、それからspace区切りでassに入れる
    val asm = c.replaceAll("[ |(|)|,]+", " ").trim.split("[ ]")
    val inst = asm(0) match{  // asm(0)はopcode
      // R形式
      case "srai" => "0100000"+imm(asm(3)).takeRight(5)+fivebitReg(asm(2))+"101"+fivebitReg(asm(1))+"0010011"
      case "add"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"000"+fivebitReg(asm(1))+"0110011"
      case "sub"  => "0100000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"000"+fivebitReg(asm(1))+"0110011"
      case "sll"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"001"+fivebitReg(asm(1))+"0110011"
      case "slt"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"010"+fivebitReg(asm(1))+"0110011"
      case "sltu" => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"011"+fivebitReg(asm(1))+"0110011"
      case "xor"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"100"+fivebitReg(asm(1))+"0110011"
      case "srl"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"101"+fivebitReg(asm(1))+"0110011"
      case "sra"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"101"+fivebitReg(asm(1))+"0110011"
      case "or"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"110"+fivebitReg(asm(1))+"0110011"
      case "and"  => "0000000"+fivebitReg(asm(3))+fivebitReg(asm(2))+"111"+fivebitReg(asm(1))+"0110011"
      // I形式
      case "jalr" => imm(asm(3)).takeRight(12)+fivebitReg(asm(2))+"000"+fivebitReg(asm(1))+"1100111"
      case "lb"   => imm(asm(2)).takeRight(12)+fivebitReg(asm(3))+"000"+fivebitReg(asm(1))+"0000011"
      case "lh"   => imm(asm(2)).takeRight(12)+fivebitReg(asm(3))+"001"+fivebitReg(asm(1))+"0000011"
      case "lw"   => imm(asm(2)).takeRight(12)+fivebitReg(asm(3))+"010"+fivebitReg(asm(1))+"0000011"
      case "mv"   => "000000000000"+fivebitReg(asm(2))+"000"+fivebitReg(asm(1))+"0010011"  // addi rd rs1
      case "li"   => imm(asm(2)).takeRight(12)+"00000"+"000"+fivebitReg(asm(1))+"0010011" // addi rd, rs, x0と同じ
      case "addi" => imm(asm(3)).takeRight(12)+fivebitReg(asm(2))+"000"+fivebitReg(asm(1))+"0010011"
      case "slti" => imm(asm(3)).takeRight(12)+fivebitReg(asm(2))+"010"+fivebitReg(asm(1))+"0010011"
      case "sltiu"=> imm(asm(3)).takeRight(12)+fivebitReg(asm(2))+"011"+fivebitReg(asm(1))+"0010011"
      case "xori" => imm(asm(3)).takeRight(12)+fivebitReg(asm(2))+"100"+fivebitReg(asm(1))+"0010011"
      case "ori" => imm(asm(3)).takeRight(12)+fivebitReg(asm(2))+"110"+fivebitReg(asm(1))+"0010011"
      case "andi" => imm(asm(3)).takeRight(12)+fivebitReg(asm(2))+"111"+fivebitReg(asm(1))+"0010011"
      // S形式
      case "sb"   => imm(asm(2)).takeRight(12).take(7)+fivebitReg(asm(1))+fivebitReg(asm(3))+"000"+imm(asm(2)).takeRight(5)+"0100011"
      case "sh"   => imm(asm(2)).takeRight(12).take(7)+fivebitReg(asm(1))+fivebitReg(asm(3))+"001"+imm(asm(2)).takeRight(5)+"0100011"
      case "sw"   => imm(asm(2)).takeRight(12).take(7)+fivebitReg(asm(1))+fivebitReg(asm(3))+"010"+imm(asm(2)).takeRight(5)+"0100011"
      // B形式         imm[12|10:5]                                       rs2                   rs1           funct3
      case "beq"  => imm(asm(3)).takeRight(13).head+imm(asm(3)).takeRight(11).take(6)+fivebitReg(asm(2))+fivebitReg(asm(1))+"000"+imm(asm(3)).takeRight(5).take(4) +imm(asm(3))(12)+"1100011"
      case "bne"  => imm(asm(3)).takeRight(13).head+imm(asm(3)).takeRight(11).take(6)+fivebitReg(asm(2))+fivebitReg(asm(1))+"001"+imm(asm(3)).takeRight(5).take(4) +imm(asm(3))(12)+"1100011"
      case "blt"  => imm(asm(3)).takeRight(13).head+imm(asm(3)).takeRight(11).take(6)+fivebitReg(asm(2))+fivebitReg(asm(1))+"100"+imm(asm(3)).takeRight(5).take(4) +imm(asm(3))(12)+"1100011"
      case "bge"  => imm(asm(3)).takeRight(13).head+imm(asm(3)).takeRight(11).take(6)+fivebitReg(asm(2))+fivebitReg(asm(1))+"101"+imm(asm(3)).takeRight(5).take(4) +imm(asm(3))(12)+"1100011"
      case "bltu" => imm(asm(3)).takeRight(13).head+imm(asm(3)).takeRight(11).take(6)+fivebitReg(asm(2))+fivebitReg(asm(1))+"110"+imm(asm(3)).takeRight(5).take(4) +imm(asm(3))(12)+"1100011"
      case "bgeu" => imm(asm(3)).takeRight(13).head+imm(asm(3)).takeRight(11).take(6)+fivebitReg(asm(2))+fivebitReg(asm(1))+"111"+imm(asm(3)).takeRight(5).take(4) +imm(asm(3))(12)+"1100011"
      // U形式
      case "lui"  => imm(asm(2)).drop(12)+fivebitReg(asm(1))+"0110111"
      case "auipc"=> imm(asm(2)).drop(12)+fivebitReg(asm(1))+"0010111"
      // J形式
      case "j"    => imm(asm(1)).takeRight(21).head+imm(asm(1)).takeRight(11).init+imm(asm(1)).takeRight(12).head+imm(asm(1)).takeRight(20).dropRight(12)+"00000"+"1101111"  // jal x0 offset と同じ
      case "jal"  => imm(asm(2)).takeRight(21).head+imm(asm(2)).takeRight(11).init+imm(asm(2)).takeRight(12).head+imm(asm(2)).takeRight(20).dropRight(12)+imm(asm(1))+"1101111"
      //
      case "fence"=> ""
      case "ecall" => "000000000000"+"00000"+"000"+"00000"+"1110011"
      case "ebreak"=> "000000000001"+"00000"+"000"+"00000"+"1110011"
      case _ => ""
    }

    //println(inst)
    println("memory.write("+counter+".U, \"b"+inst+"\".U(32.W))")
    counter += 4
  }
  // spとかa0とかをレジスタ番号にする
  def fivebitReg(name:String): String ={
    val tmp = name match {
      case "zero" => 0
      case "ra" => 1
      case "sp" => 2
      case "gp" => 3
      case "tp" => 4
      case "t0" => 5
      case "t1" => 6
      case "t2" => 7
      case "s0" => 8
      case "fp" => 8
      case "s1" => 9
      case "a0" => 10
      case "a1" => 11
      case "a2" => 12
      case "a3" => 13
      case "a4" => 14
      case "a5" => 15
      case "a6" => 16
      case "a7" => 17
      case "s2" => 18
      case "s3" => 19
      case "s4" => 20
      case "s5" => 21
      case "s6" => 22
      case "s7" => 23
      case "s8" => 24
      case "s9" => 25
      case "s10" => 26
      case "s11" => 27
      case "t4" => 29
      case "t5" => 30
      case "t6" => 31

      case "x0" => 0
      case "x1" => 1
      case "x2" => 2
      case "x3" => 3
      case "x4" => 4
      case "x5" => 5
      case "x6" => 6
      case "x7" => 7
      case "x8" => 8
      case "x9" => 9
      case "x10" => 10
      case "x11" => 11
      case "x12" => 12
      case "x13" => 13
      case "x14" => 14
      case "x15" => 15
      case "x16" => 16
      case "x17" => 17
      case "x18" => 18
      case "x19" => 19
      case "x20" => 20
      case "x21" => 21
      case "x22" => 22
      case "x23" => 23
      case "x24" => 24
      case "x25" => 25
      case "x26" => 26
      case "x27" => 27
      case "x28" => 28
      case "x29" => 29
      case "x30" => 30
      case "x31" => 31
      case "x32" => 32

    }
    // 上のを2進数の文字列にして、5文字に足りないのを0パディング
    "%5s".format(tmp.toBinaryString).replace(" ","0")
  }

  def imm(name:String): String ={
    if(name.toInt<0) name.toInt.toBinaryString.takeRight(32)  // 負のとき
    else "%32s".format(name.toInt.toBinaryString).replace(" ", "0")
  }
}

終わりに

アセンブリから機械語への変換プログラムと言ってもほぼパターンマッチなので大したものではないのですが、Chiselで開発しているときにデバッグで機械語を手軽に生成したいときに自分は使おうかなと思います。
もし間違い、質問、感想等あれば、私のtwitterにDMでも送ってもらえればと思います。
もしくは他に便利な翻訳プログラムがあれば教えていただきたいです。

また、もし私のプログラムを改造してくださいましたら、私も使いたいので教えてください。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2