Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

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

はじめに

先日、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でも送ってもらえればと思います。
もしくは他に便利な翻訳プログラムがあれば教えていただきたいです。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
2
Help us understand the problem. What are the problem?