blob: b3a06d7db32b0a7189944fb976e4cbff9c4f8343 [file] [log] [blame]
"""
Copyright 2019 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Class for RISC-V instruction trace CSV
"""
import csv
import re
import logging
class RiscvInstructionTraceEntry(object):
"""RISC-V instruction trace entry"""
def __init__(self):
self.rd = ""
self.rd_val = ""
self.rs1 = ""
self.rs1_val = ""
self.rs2 = ""
self.rs2_val = ""
self.imm = ""
self.instr_name = ""
self.addr = ""
self.binary = ""
self.instr_str = ""
self.instr = ""
self.privileged_mode = ""
self.csr = ""
def get_trace_string(self):
"""Return a short string of the trace entry"""
return ("%s -> %s(0x%s) addr:0x%s" %
(self.instr_str, self.rd, self.rd_val, self.addr))
class RiscvInstructionTraceCsv(object):
"""RISC-V instruction trace CSV class
This class provides functions to read/write trace CSV
"""
def __init__(self, csv_fd):
self.csv_fd = csv_fd
self.gpr = {}
def start_new_trace(self):
"""Create a CSV file handle for a new trace"""
fields = ["instr", "rd", "rd_val", "rs1", "rs1_val", "rs2", "rs2_val",
"imm", "str", "addr", "binary", "csr", "mode"]
self.csv_writer = csv.DictWriter(self.csv_fd, fieldnames=fields)
self.csv_writer.writeheader()
def read_trace(self, trace):
"""Read instruction trace from CSV file"""
csv_reader = csv.DictReader(self.csv_fd)
for row in csv_reader:
new_trace = RiscvInstructionTraceEntry()
new_trace.rd = row['rd']
new_trace.rd_val = row['rd_val']
new_trace.addr = row['addr']
new_trace.binary = row['binary']
new_trace.instr_str = row['str']
trace.append(new_trace)
def write_trace_entry(self, entry):
"""Write a new trace entry to CSV"""
self.gpr[entry.rd] = entry.rd_val
self.csv_writer.writerow({'str' : entry.instr_str,
'rd' : entry.rd,
'rd_val' : entry.rd_val,
'rs1' : entry.rs1,
'rs1_val' : entry.rs1_val,
'rs2' : entry.rs2,
'rs2_val' : entry.rs2_val,
'addr' : entry.addr,
'instr' : entry.instr,
'imm' : entry.imm,
'csr' : entry.csr,
'binary' : entry.binary})
def gpr_to_abi(gpr):
"""Convert a general purpose register to its corresponding abi name"""
switcher = {
"x0" : "zero",
"x1" : "ra",
"x2" : "sp",
"x3" : "gp",
"x4" : "tp",
"x5" : "t0",
"x6" : "t1",
"x7" : "t2",
"x8" : "s0",
"x9" : "s1",
"x10" : "a0",
"x11" : "a1",
"x12" : "a2",
"x13" : "a3",
"x14" : "a4",
"x15" : "a5",
"x16" : "a6",
"x17" : "a7",
"x18" : "s2",
"x19" : "s3",
"x20" : "s4",
"x21" : "s5",
"x22" : "s6",
"x23" : "s7",
"x24" : "s8",
"x25" : "s9",
"x26" : "s10",
"x27" : "s11",
"x28" : "t3",
"x29" : "t4",
"x30" : "t5",
"x31" : "t6",
"f0" : "ft0",
"f1" : "ft1",
"f2" : "ft2",
"f3" : "ft3",
"f4" : "ft4",
"f5" : "ft5",
"f6" : "ft6",
"f7" : "ft7",
"f8" : "fs0",
"f9" : "fs1",
"f10" : "fa0",
"f11" : "fa1",
"f12" : "fa2",
"f13" : "fa3",
"f14" : "fa4",
"f15" : "fa5",
"f16" : "fa6",
"f17" : "fa7",
"f18" : "fs2",
"f19" : "fs3",
"f20" : "fs4",
"f21" : "fs5",
"f22" : "fs6",
"f23" : "fs7",
"f24" : "fs8",
"f25" : "fs9",
"f26" : "fs10",
"f27" : "fs11",
"f28" : "ft8",
"f29" : "ft9",
"f30" : "ft10",
"f31" : "ft11",
}
return switcher.get(gpr, "na")
def sint_to_hex(val):
"""Signed integer to hex conversion"""
return str(hex((val + (1 << 32)) % (1 << 32)))
def get_imm_hex_val(imm):
"""Get the hex representation of the imm value"""
if imm[0] == '-':
is_negative = 1
imm = imm[1:]
else:
is_negative = 0
if len(imm) > 1 and imm[1] != 'x':
imm = "0x"+imm
imm_val = int(imm, 0)
if is_negative:
imm_val = -imm_val
hexstr = sint_to_hex(imm_val)
return hexstr[2:]
ADDR_RE = re.compile(r"(?P<imm>[\-0-9]+?)\((?P<rs1>.*)\)")
def assign_operand(trace, operands, gpr):
"""Assign the operand value of the instruction trace"""
if trace.instr in ['lb', 'lh', 'lw', 'lbu', 'lhu', 'ld', 'lq', 'lwu', 'ldu',
'c.lw', 'c.ld', 'c.lq', 'c.lwsp', 'c.ldsp', 'c.lqsp']:
# TODO: Support regular load/store format
m = ADDR_RE.search(operands[1])
# Load instruction
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
if m:
trace.imm = get_imm_hex_val(m.group('imm'))
trace.rs1 = m.group('rs1')
trace.rs1_val = gpr[trace.rs1]
else:
logging.info("Unexpected load address %0s", operands[1])
elif trace.instr in ['sb', 'sh', 'sw', 'sd', 'sq', 'c.sw', 'c.sd', 'c.sq',
'c.swsp', 'c.sdsp', 'c.sqsp']:
# Store instruction
m = ADDR_RE.search(operands[1])
# Load instruction
trace.rs2 = operands[0]
trace.rs2_val = gpr[trace.rs2]
if m:
trace.imm = get_imm_hex_val(m.group('imm'))
trace.rs1 = m.group('rs1')
trace.rs1_val = gpr[trace.rs1]
else:
logging.info("Unexpected store address %0s", operands[1])
elif trace.instr in ['mul', 'mulh', 'mulhsu', 'mulhu', 'div', 'divu', 'rem', 'remu',
'mulw', 'muld', 'divw', 'divuw', 'divd', 'remw', 'remd', 'remuw',
'remud', 'sll', 'srl', 'sra', 'add', 'sub', 'xor', 'or', 'and',
'slt', 'sltu', 'sllw', 'slld', 'srlw', 'srld', 'sraw', 'srad',
'addw', 'addd', 'subw', 'subd']:
# R type instruction
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = operands[2]
trace.rs2_val = gpr[trace.rs2]
elif trace.instr in ['c.add', 'c.addw', 'c.mv', 'c.sub', 'c.and', 'c.or', 'c.xor', 'c.subw']:
# CR type
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = operands[1]
trace.rs2_val = gpr[trace.rs2]
elif trace.instr in ['c.jr']:
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = 'zero'
trace.rs2_val = '0'
trace.rd = 'zero'
trace.rd_val = '0'
elif trace.instr in ['c.jr', 'c.jalr']:
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = 'zero'
trace.rs2_val = '0'
elif trace.instr in ['slli', 'srli', 'srai', 'addi', 'xori', 'ori', 'andi', 'slti',
'sltiu', 'slliw', 'sllid', 'srliw', 'srlid', 'sraiw', 'sraid',
'addiw', 'addid']:
# I type instruction
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val(operands[2])
elif trace.instr in ['c.addi16sp', 'c.addi4spn']:
trace.rs1 = 'sp'
trace.rs1_val = gpr[trace.rs1]
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.imm = get_imm_hex_val(operands[-1])
elif trace.instr in ['c.addi', 'c.addiw', 'c.li', 'c.lui',
'c.slli', 'c.srai', 'c.srli', 'c.andi']:
# CI/CIW type
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val(operands[-1])
elif trace.instr in ['beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu']:
# SB type instruction
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = operands[1]
trace.rs2_val = gpr[trace.rs2]
trace.imm = get_imm_hex_val(operands[2])
elif trace.instr in ['c.beqz', 'c.bnez']:
# CB type instruction
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val(operands[1])
elif trace.instr in ['csrrw', 'csrrs', 'csrrc']:
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.csr = operands[1]
trace.rs1 = operands[2]
trace.rs1_val = gpr[trace.rs1]
elif trace.instr in ['csrrwi', 'csrrsi', 'csrrci']:
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.csr = operands[1]
trace.imm = get_imm_hex_val(operands[2])
elif trace.instr in ['scall', 'sbreak', 'fence', 'fence.i', 'ecall', 'ebreak', 'wfi',
'sfence.vma', 'c.ebreak', 'nop', 'c.nop']:
trace.rd = 'zero'
trace.rs1 = 'zero'
trace.rs2 = 'zero'
trace.rd_val = '0'
trace.rs1_val = '0'
trace.rs2_val = '0'
trace.imm = get_imm_hex_val('0')
elif trace.instr in ['lui', 'auipc']:
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.imm = get_imm_hex_val(operands[1])
elif trace.instr in ['jal']:
if len(operands) == 1:
trace.imm = get_imm_hex_val(operands[0])
else:
trace.imm = get_imm_hex_val(operands[1])
elif trace.instr in ['jalr']:
if len(operands) == 1:
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val('0')
else:
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val(operands[2])
elif trace.instr in ['c.j', 'c.jal']:
trace.imm = get_imm_hex_val(operands[0])
# Pseudo instruction convertion below
elif trace.instr in ['mv']:
trace.instr = 'addi'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val('0')
elif trace.instr in ['not']:
trace.instr = 'xori'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val('-1')
elif trace.instr in ['neg']:
trace.instr = 'sub'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = 'zero'
trace.rs1_val = '0'
trace.rs2 = operands[1]
trace.rs2_val = gpr[trace.rs2]
elif trace.instr in ['negw']:
trace.instr = 'subw'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = 'zero'
trace.rs1_val = '0'
trace.rs2 = operands[1]
trace.rs2_val = gpr[trace.rs2]
elif trace.instr in ['sext.w']:
trace.instr = 'addiw'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val('0')
elif trace.instr in ['seqz']:
trace.instr = 'sltiu'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val('1')
elif trace.instr in ['snez']:
trace.instr = 'sltu'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = 'zero'
trace.rs1_val = '0'
trace.rs2 = operands[1]
trace.rs2_val = gpr[trace.rs2]
elif trace.instr in ['sltz']:
trace.instr = 'slt'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = 'zero'
trace.rs2_val = '0'
elif trace.instr in ['sgtz']:
trace.instr = 'slt'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.rs1 = 'zero'
trace.rs1_val = '0'
trace.rs2 = operands[1]
trace.rs2_val = gpr[trace.rs2]
elif trace.instr in ['beqz', 'bnez', 'bgez', 'bltz']:
trace.instr = trace.instr[0:3]
trace.rs1 = operands[0]
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = 'zero'
trace.rs2_val = '0'
trace.imm = get_imm_hex_val(operands[1])
elif trace.instr in ['blez']:
trace.instr = 'bge'
trace.rs1 = 'zero'
trace.rs1_val = '0'
trace.rs2 = operands[0]
trace.rs2_val = gpr[trace.rs2]
trace.imm = get_imm_hex_val(operands[1])
elif trace.instr in ['bgtz']:
trace.instr = 'blt'
trace.rs1 = 'zero'
trace.rs1_val = '0'
trace.rs2 = operands[0]
trace.rs2_val = gpr[trace.rs2]
trace.imm = get_imm_hex_val(operands[1])
elif trace.instr in ['csrr']:
trace.instr = 'csrrw'
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.csr = operands[1]
trace.rs1 = 'zero'
trace.rs1_val = '0'
elif trace.instr in ['csrw', 'csrs', 'csrc']:
trace.instr = 'csrr' + trace.instr[-1]
trace.csr = operands[0]
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.rd = 'zero'
trace.rd_val = '0'
elif trace.instr in ['csrwi', 'csrsi', 'csrci']:
trace.instr = 'csrr' + trace.instr[-2:]
trace.rd = 'zero'
trace.rd_val = '0'
trace.csr = operands[0]
trace.imm = get_imm_hex_val(operands[1])
elif trace.instr in ['j']:
trace.instr = 'jal'
trace.rd = 'zero'
trace.rd_val = '0'
trace.imm = get_imm_hex_val(operands[0])
elif trace.instr in ['jr']:
trace.instr = 'jal'
trace.rd = 'zero'
trace.rd_val = '0'
trace.rs1 = operands[0]
if trace.rs1 in gpr:
trace.rs1_val = gpr[trace.rs1]
elif trace.instr in ['li']:
trace.instr = 'li'
elif trace.instr[0:2] in ['lr', 'am', 'sc']:
# TODO: Support A-extension
pass
else:
# TODO: Support other instructions
logging.info("Unsupported instr : %s" % trace.instr)