cva6/verif/regress/riscv-dv.patch
2023-09-07 09:50:50 +02:00

395 lines
14 KiB
Diff

diff --git a/run.py b/run.py
index bae34b7..76df3c7 100644
--- a/run.py
+++ b/run.py
@@ -23,6 +23,7 @@ import sys
import logging
from scripts.lib import *
+from scripts.verilator_log_to_trace_csv import *
from scripts.spike_log_to_trace_csv import *
from scripts.ovpsim_log_to_trace_csv import *
from scripts.whisper_log_trace_csv import *
@@ -414,7 +415,7 @@ def run_assembly(asm_test, iss_yaml, isa, mabi, gcc_opts, iss_opts, output_dir,
base_cmd = parse_iss_yaml(iss, iss_yaml, isa, setting_dir, debug_cmd)
logging.info("[%0s] Running ISS simulation: %s" % (iss, elf))
cmd = get_iss_cmd(base_cmd, elf, log)
- run_cmd(cmd, 10, debug_cmd = debug_cmd)
+ run_cmd(cmd, 1500, debug_cmd = debug_cmd)
logging.info("[%0s] Running ISS simulation: %s ...done" % (iss, elf))
if len(iss_list) == 2:
compare_iss_log(iss_list, log_list, report)
@@ -596,7 +597,6 @@ def iss_cmp(test_list, iss, output_dir, stop_on_first_error, exp, debug_cmd):
if len(iss_list) != 2:
return
report = ("%s/iss_regr.log" % output_dir).rstrip()
- run_cmd("rm -rf %s" % report)
for test in test_list:
for i in range(0, test['iterations']):
elf = ("%s/asm_tests/%s_%d.o" % (output_dir, test['test'], i))
@@ -622,6 +622,8 @@ def compare_iss_log(iss_list, log_list, report, stop_on_first_error=0, exp=False
csv_list.append(csv)
if iss == "spike":
process_spike_sim_log(log, csv)
+ elif "verilator" in iss or "vsim" in iss:
+ process_verilator_sim_log(log, csv)
elif iss == "ovpsim":
process_ovpsim_sim_log(log, csv, stop_on_first_error)
elif iss == "sail":
@@ -654,7 +656,7 @@ def setup_parser():
parser.add_argument("--target", type=str, default="rv32imc",
help="Run the generator with pre-defined targets: \
- rv32imc, rv32i, rv64imc, rv64gc")
+ rv32imc, rv32i, rv32ima, rv64imc, rv64gc, rv64imac")
parser.add_argument("-o", "--output", type=str,
help="Output directory name", dest="o")
parser.add_argument("-tl", "--testlist", type=str, default="",
@@ -772,6 +774,15 @@ def load_config(args, cwd):
if args.target == "rv32imc":
args.mabi = "ilp32"
args.isa = "rv32imc"
+ elif args.target == "rv32imac":
+ args.mabi = "ilp32"
+ args.isa = "rv32imac"
+ elif args.target == "rv32ima":
+ args.mabi = "ilp32"
+ args.isa = "rv32ima"
+ elif args.target == "rv32gc":
+ args.mabi = "ilp32"
+ args.isa = "rv32gc"
elif args.target == "multi_harts":
args.mabi = "ilp32"
args.isa = "rv32gc"
@@ -787,6 +798,9 @@ def load_config(args, cwd):
elif args.target == "rv64gc":
args.mabi = "lp64"
args.isa = "rv64gc"
+ elif args.target == "rv64imac":
+ args.mabi = "lp64"
+ args.isa = "rv64imac"
elif args.target == "rv64gcv":
args.mabi = "lp64"
args.isa = "rv64gcv"
@@ -817,7 +831,7 @@ def main():
# Load configuration from the command line and the configuration file.
cfg = load_config(args, cwd)
# Create output directory
- output_dir = create_output(args.o, args.noclean)
+ output_dir = create_output(args.o, args.noclean, cwd+"/out_")
if args.verilog_style_check:
logging.debug("Run style check")
diff --git a/scripts/lib.py b/scripts/lib.py
index b974637..90aef00 100644
--- a/scripts/lib.py
+++ b/scripts/lib.py
@@ -230,6 +230,8 @@ def process_regression_list(testlist, test, iterations, matched_list, riscv_dv_r
if (iterations > 0 and entry['iterations'] > 0):
entry['iterations'] = iterations
if entry['iterations'] > 0:
+ entry['asm_tests'] = re.sub("\<path_var\>", get_env_var(entry['path_var']), entry['asm_tests'])
+ entry['gcc_opts'] = re.sub("\<path_var\>", get_env_var(entry['path_var']), entry['gcc_opts'])
logging.info("Found matched tests: %s, iterations:%0d" %
(entry['test'], entry['iterations']))
matched_list.append(entry)
diff --git a/scripts/link.ld b/scripts/link.ld
index df08889..ad50f56 100644
--- a/scripts/link.ld
+++ b/scripts/link.ld
@@ -19,15 +19,17 @@ ENTRY(_start)
SECTIONS
{
. = 0x80000000;
- .text : { *(.text) }
+ .text.init : { *(.text.init) }
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
+ .text : { *(.text) }
+ . = ALIGN(0x1000);
.page_table : { *(.page_table) }
- .data : { *(.data) }
.user_stack : { *(.user_stack) }
.kernel_data : { *(.kernel_data) }
.kernel_stack : { *(.kernel_stack) }
+ .data : { *(.data) }
.bss : { *(.bss) }
_end = .;
}
diff --git a/scripts/verilator_log_to_trace_csv.py b/scripts/verilator_log_to_trace_csv.py
new file mode 100644
index 0000000..2be83ae
--- /dev/null
+++ b/scripts/verilator_log_to_trace_csv.py
@@ -0,0 +1,249 @@
+"""
+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.
+
+Convert verilator sim log to standard riscv instruction trace format
+"""
+
+import argparse
+import os
+import re
+import sys
+import logging
+
+sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
+
+from riscv_trace_csv import *
+from lib import *
+
+RD_RE = re.compile(r"(?P<pri>\d) 0x(?P<addr>[a-f0-9]+?) " \
+ "\((?P<bin>.*?)\) (?P<reg>[xf]\s*\d*?) 0x(?P<val>[a-f0-9]+)")
+CORE_RE = re.compile(r"core.*0x(?P<addr>[a-f0-9]+?) \(0x(?P<bin>.*?)\) (?P<instr>.*?)$")
+ILLE_RE = re.compile(r"trap_illegal_instruction")
+
+LOGGER = logging.getLogger()
+
+
+def process_instr(trace):
+ if trace.instr == "jal":
+ # Spike jal format jal rd, -0xf -> jal rd, -15
+ idx = trace.operand.rfind(",")
+ imm = trace.operand[idx+1:]
+ if imm[0] == "-":
+ imm = "-" + str(int(imm[1:], 16))
+ else:
+ imm = str(int(imm, 16))
+ trace.operand = trace.operand[0:idx+1] + imm
+ trace.operand = trace.operand.replace("(", ",")
+ trace.operand = trace.operand.replace(")", "")
+
+
+def read_verilator_instr(match, full_trace):
+ '''Unpack a regex match for CORE_RE to a RiscvInstructionTraceEntry
+
+ If full_trace is true, extract operand data from the disassembled
+ instruction.
+
+ '''
+
+ # Extract the disassembled instruction.
+ disasm = match.group('instr')
+
+ # Spike's disassembler shows a relative jump as something like "j pc +
+ # 0x123" or "j pc - 0x123". We just want the relative offset.
+ disasm = disasm.replace('pc + ', '').replace('pc - ', '-')
+
+ instr = RiscvInstructionTraceEntry()
+ instr.pc = match.group('addr')
+ instr.instr_str = disasm
+ instr.binary = match.group('bin')
+
+ if full_trace:
+ opcode = disasm.split(' ')[0]
+ operand = disasm[len(opcode):].replace(' ', '')
+ instr.instr, instr.operand = \
+ convert_pseudo_instr(opcode, operand, instr.binary)
+
+ process_instr(instr)
+
+ return instr
+
+
+def read_verilator_trace(path, full_trace):
+ '''Read a Spike simulation log at <path>, yielding executed instructions.
+
+ This assumes that the log was generated with the -l and --log-commits options
+ to Spike.
+
+ If full_trace is true, extract operands from the disassembled instructions.
+
+ Since Spike has a strange trampoline that always runs at the start, we skip
+ instructions up to and including the one at PC 0x1010 (the end of the
+ trampoline). At the end of a DV program, there's an ECALL instruction, which
+ we take as a signal to stop checking, so we ditch everything that follows
+ that instruction.
+
+ This function yields instructions as it parses them as tuples of the form
+ (entry, illegal). entry is a RiscvInstructionTraceEntry. illegal is a
+ boolean, which is true if the instruction caused an illegal instruction trap.
+
+ '''
+
+ # This loop is a simple FSM with states TRAMPOLINE, INSTR, EFFECT. The idea
+ # is that we're in state TRAMPOLINE until we get to the end of Spike's
+ # trampoline, then we switch between INSTR (where we expect to read an
+ # instruction) and EFFECT (where we expect to read commit information).
+ #
+ # We yield a RiscvInstructionTraceEntry object each time we leave EFFECT
+ # (going back to INSTR), we loop back from INSTR to itself, or we get to the
+ # end of the file and have an instruction in hand.
+ #
+ # On entry to the loop body, we are in state TRAMPOLINE if in_trampoline is
+ # true. Otherwise, we are in state EFFECT if instr is not None, otherwise we
+ # are in state INSTR.
+
+ end_trampoline_re = re.compile(r'core.*: 0x0000000080000000 ')
+ start_debug_it_re = re.compile(r'core.*: 0x0000000000000800 ')
+ stop_debug_it_re = re.compile(r'core.*: 0x0000000000000890 ')
+
+ in_trampoline = True
+ in_debug = False
+ instr = None
+
+ with open(path, 'r') as handle:
+ for line in handle:
+ if in_trampoline:
+ # The TRAMPOLINE state
+ if end_trampoline_re.match(line):
+ in_trampoline = False
+ continue
+
+ if not in_trampoline:
+ if in_debug:
+ if stop_debug_it_re.match(line):
+ in_debug = False
+ continue
+ else:
+ if start_debug_it_re.match(line):
+ in_debug = True
+ continue
+
+ if instr is None:
+ # The INSTR state. We expect to see a line matching CORE_RE. We'll
+ # discard any other lines.
+ instr_match = CORE_RE.match(line)
+ if not instr_match:
+ continue
+
+ instr = read_verilator_instr(instr_match, full_trace)
+
+ # If instr.instr_str is 'ecall', we should stop.
+ if instr.instr_str == 'ecall':
+ break
+
+ continue
+
+ # The EFFECT state. If the line matches CORE_RE, we should have been in
+ # state INSTR, so we yield the instruction we had, read the new
+ # instruction and continue. As above, if the new instruction is 'ecall',
+ # we need to stop immediately.
+ instr_match = CORE_RE.match(line)
+ if instr_match:
+ yield (instr, False)
+ instr = read_verilator_instr(instr_match, full_trace)
+ if instr.instr_str == 'ecall':
+ break
+ continue
+
+ # The line doesn't match CORE_RE, so we are definitely on a follow-on
+ # line in the log. First, check for illegal instructions
+ if 'trap_illegal_instruction' in line:
+ yield (instr, True)
+ instr = None
+ continue
+
+ # The instruction seems to have been fine. Do we have commit data (from
+ # the --log-commits Spike option)?
+ commit_match = RD_RE.match(line)
+ if commit_match:
+ instr.gpr.append(gpr_to_abi(commit_match.group('reg')
+ .replace(' ', '')) +
+ ':' + commit_match.group('val'))
+ instr.mode = commit_match.group('pri')
+
+ # At EOF, we might have an instruction in hand. Yield it if so.
+ if instr is not None:
+ yield (instr, False)
+
+
+def process_verilator_sim_log(verilator_log, csv, full_trace = 0):
+ """Process VERILATOR simulation log.
+
+ Extract instruction and affected register information from verilator simulation
+ log and write the results to a CSV file at csv. Returns the number of
+ instructions written.
+
+ """
+ logging.info("Processing verilator log : %s" % verilator_log)
+
+ instrs_in = 0
+ instrs_out = 0
+
+ with open(csv, "w") as csv_fd:
+ trace_csv = RiscvInstructionTraceCsv(csv_fd)
+ trace_csv.start_new_trace()
+
+ for (entry, illegal) in read_verilator_trace(verilator_log, full_trace):
+ instrs_in += 1
+
+ if illegal and full_trace:
+ logging.debug("Illegal instruction: {}, opcode:{}"
+ .format(entry.instr_str, entry.binary))
+
+ # Instructions that cause no architectural update (which includes illegal
+ # instructions) are ignored if full_trace is false.
+ #
+ # We say that an instruction caused an architectural update if either we
+ # saw a commit line (in which case, entry.gpr will contain a single
+ # entry) or the instruction was 'wfi' or 'ecall'.
+ if not (full_trace or entry.gpr or entry.instr_str in ['wfi', 'ecall']):
+ continue
+
+ trace_csv.write_trace_entry(entry)
+ instrs_out += 1
+
+ logging.info("Processed instruction count : %d" % instrs_in)
+ logging.info("CSV saved to : %s" % csv)
+ return instrs_out
+
+
+def main():
+ # Parse input arguments
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--log", type=str, help="Input verilator simulation log")
+ parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
+ parser.add_argument("-f", "--full_trace", dest="full_trace", action="store_true",
+ help="Generate the full trace")
+ parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
+ help="Verbose logging")
+ parser.set_defaults(full_trace=False)
+ parser.set_defaults(verbose=False)
+ args = parser.parse_args()
+ setup_logging(args.verbose)
+ # Process verilator log
+ process_verilator_sim_log(args.log, args.csv, args.full_trace)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/yaml/iss.yaml b/yaml/iss.yaml
index 344ef79..381cda6 100644
--- a/yaml/iss.yaml
+++ b/yaml/iss.yaml
@@ -17,6 +17,16 @@
cmd: >
<path_var>/spike --log-commits --isa=<variant> -l <elf>
+- iss: verilator
+ path_var: RTL_PATH
+ cmd: >
+ make -C <path_var> generate-trace-verilator elf-bin=<elf>
+
+- iss: vsim
+ path_var: RTL_PATH
+ cmd: >
+ make -C <path_var> generate-trace-vsim preload=<elf> elf-bin=none
+
- iss: ovpsim
path_var: OVPSIM_PATH
cmd: >