Update google_riscv-dv to google/riscv-dv@7cce16c (#246)

Update code from upstream repository https://github.com/google/riscv-
dv to revision 7cce16c0a212c8713a82516fbf8f2570d3dc4505

* Update spike log processing script to include full trace information
  (Tao Liu)
* Add new tests (Tao Liu)
* Add basic debug test functionality (Udi)
* fix spelling error, fix output directory arg (Udi)
* Add shorten option (dang hai)
* Support SAIL-RISCV ISSi, update README (Tao Liu)
* Fix CSR map copy issue (Tao Liu)
This commit is contained in:
udinator 2019-08-16 09:43:27 -07:00 committed by taoliug
parent e1a7dcf37f
commit 6ccd2b698d
15 changed files with 434 additions and 170 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/google/riscv-dv
rev: 63fa0ca922ecf10f3cd733d15a0a79a7937a591e
rev: 7cce16c0a212c8713a82516fbf8f2570d3dc4505
}
}

View file

@ -100,89 +100,23 @@ python3 run.py --test riscv_page_table_exception_test --co
### Privileged CSR Test Generation
The CSR generation script is located at [scripts/gen_csr_test.py](https://github.com/google/riscv-dv/blob/master/scripts/gen_csr_test.py). The CSR test code that this script generates will execute every CSR instruction on every processor implemented CSR, writing values to the CSR and then using a prediction function to calculate a reference value that will be written into another GPR. The reference value will then be compared to the value actually stored in the CSR to determine whether to jump to the failure condition or continue executing, allowing it to be completely self checking.
To quickly generate a basic CSR test, run the below command:
The CSR generation script is located at
[scripts/gen_csr_test.py](https://github.com/google/riscv-dv/blob/master/scripts/gen_csr_test.py).
The CSR test code that this script generates will execute every CSR instruction
on every processor implemented CSR, writing values to the CSR and then using a
prediction function to calculate a reference value that will be written into
another GPR. The reference value will then be compared to the value actually
stored in the CSR to determine whether to jump to the failure condition or
continue executing, allowing it to be completely self checking. This script has
been integrated with run.py. If you want to run it separately, you can get the
command reference with --help:
```
python3 scripts/gen_csr_test.py
python3 scripts/gen_csr_test.py --help
```
To create a new processor-specific CSR description YAML file, format it as
detailed below, and then run:
```
python3 scripts/gen_csr_test.py --csr_file PATH_TO_NEW_CSR_DESCRIPTION_FILE
```
If the CSRs are a different XLEN, such as 64-bit or 128-bit, use the `--xlen`
option to change the ISA length used to generate the test.
```
python3 scripts/gen_csr_test.py --xlen 64
```
To input the number of CSR test files that should be generated, use the
`--iterations` option. Say 10 tests should be generated:
```
python3 scripts/gen_csr_test.py --iterations 10
```
To change the output directory that the tests are generated into, use the
`--out` option. Say the tests should be generated into the home directory:
```
python3 scripts/gen_csr_test.py --out ~
```
Any number of the above options can be combined together, and all of them have
default values should they not be specified at runtime.
## Configuration
### Setup CSR description list
This YAML description file of all CSRs is only required for the privileged CSR
test. All other standard tests do not use this description.
[CSR descriptions in YAML
format](https://github.com/google/riscv-dv/blob/master/yaml/csr_template.yaml)
```
- csr: CSR_NAME
description: >
BRIEF_DESCRIPTION
address: 0x###
privilege_mode: MODE (D/M/S/H/U)
rv32:
- MSB_FIELD_NAME:
- description: >
BRIEF_DESCRIPTION
- type: TYPE (WPRI/WLRL/WARL/R)
- reset_val: RESET_VAL
- msb: MSB_POS
- lsb: LSB_POS
- ...
- ...
- LSB_FIELD_NAME:
- description: ...
- type: ...
- ...
rv64:
- MSB_FIELD_NAME:
- description: >
BRIEF_DESCRIPTION
- type: TYPE (WPRI/WLRL/WARL/R)
- reset_val: RESET_VAL
- msb: MSB_POS
- lsb: LSB_POS
- ...
- ...
- LSB_FIELD_NAME:
- description: ...
- type: ...
- ...
```
To specify what ISA width should be generated in the test, simply include the
matching rv32/rv64/rv128 entry and fill in the appropriate CSR field entries.
### Setup regression test list
[Test list in YAML format](https://github.com/google/riscv-dv/blob/master/yaml/testlist.yaml)
@ -269,6 +203,56 @@ riscv_instr_group_t supported_isa[] = {RV32I, RV32M, RV64I, RV64M};
| boot_mode | m:Machine mode, s:Supervisor mode, u:User mode | m |
| no_directed_instr | Disable directed instruction stream | 0 |
| enable_interrupt | Enable MStatus.MIE, used in interrupt test | 0 |
| empty_debug_section | Disables randomized debug_rom section | 0 |
### Setup Privileged CSR description
This YAML description file of all CSRs is only required for the privileged CSR
test. All other standard tests do not use this description.
[CSR descriptions in YAML
format](https://github.com/google/riscv-dv/blob/master/yaml/csr_template.yaml)
```
- csr: CSR_NAME
description: >
BRIEF_DESCRIPTION
address: 0x###
privilege_mode: MODE (D/M/S/H/U)
rv32:
- MSB_FIELD_NAME:
- description: >
BRIEF_DESCRIPTION
- type: TYPE (WPRI/WLRL/WARL/R)
- reset_val: RESET_VAL
- msb: MSB_POS
- lsb: LSB_POS
- ...
- ...
- LSB_FIELD_NAME:
- description: ...
- type: ...
- ...
rv64:
- MSB_FIELD_NAME:
- description: >
BRIEF_DESCRIPTION
- type: TYPE (WPRI/WLRL/WARL/R)
- reset_val: RESET_VAL
- msb: MSB_POS
- lsb: LSB_POS
- ...
- ...
- LSB_FIELD_NAME:
- description: ...
- type: ...
- ...
```
To specify what ISA width should be generated in the test, simply include the
matching rv32/rv64/rv128 entry and fill in the appropriate CSR field entries.
### Adding new instruction stream and test
@ -290,17 +274,19 @@ it with random instructions
## Run ISS (Instruction Set Simulator) simulation
The default ISS is spike. Thanks for the great support from Imperas Software Ltd.,
we have added the support for [riscv-ovpsim](https://github.com/riscv/riscv-ovpsim).
Currently three ISS are supported, the default ISS is spike. You can install any
one of below to run ISS simulation.
- spike setup
- [spike](https://github.com/riscv/riscv-isa-sim#) setup
- Follow the [steps](https://github.com/riscv/riscv-isa-sim#build-steps) to build spike
- Make sure RISCV_ENABLE_COMMITLOG is defined in [config.h.in](https://github.com/riscv/riscv-isa-sim/blob/master/config.h.in)
- Set environment variable SPIKE_PATH to the directory of the spike binary
- [riscv-ovpsim](https://github.com/riscv/riscv-ovpsim) setup
- Download the riscv-ovpsim binary
- Set environment variable OVPSIM_PATH to the directory of the ovpsim binary
- [sail-riscv](https://github.com/rems-project/sail-riscv) setup
- Follow the [steps](https://github.com/rems-project/sail-riscv/blob/master/README.md) to install sail-riscv
- Set environment variable SAIL_RISCV to the sail-riscv binary
You can use -iss to run with different ISS.
@ -310,6 +296,9 @@ python3 run.py --test riscv_page_table_exception_test --iss spike
// Run ISS with riscv-ovpsim
python3 run.py --test riscv_rand_instr_test --iss ovpsim
// Run ISS with sail-riscv
python3 run.py --test riscv_rand_instr_test --iss sail
```
To run with ISS simulation for RV32IMC, you can specify ISA and ABI from command
@ -327,6 +316,7 @@ real RISC-V processor.
```
python3 run.py --test=riscv_rand_instr_test --iss=spike,ovpsim
python3 run.py --test=riscv_rand_instr_test --iss=spike,sail
```
### Integrate a new ISS

View file

@ -27,6 +27,7 @@ from datetime import date
from scripts.lib import *
from scripts.spike_log_to_trace_csv import *
from scripts.ovpsim_log_to_trace_csv import *
from scripts.sail_log_to_trace_csv import *
from scripts.instr_trace_compare import *
LOGGER = logging.getLogger()
@ -170,6 +171,8 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
cmd = re.sub("<seed>", str(rand_seed), cmd)
if "gen_opts" in test:
cmd += test['gen_opts']
if not re.search("c", isa):
cmd += "+disable_comparessed_instr=1";
logging.info("Generating %d %s" % (iterations, test['test']))
if lsf_cmd:
cmd_list.append(cmd)
@ -195,11 +198,19 @@ def gcc_compile(test_list, output_dir, isa, mabi, opts):
elf = prefix + ".o"
binary = prefix + ".bin"
# gcc comilation
cmd = ("%s -march=%s -mabi=%s -static -mcmodel=medany \
cmd = ("%s -static -mcmodel=medany \
-fvisibility=hidden -nostdlib \
-nostartfiles %s \
-Tscripts/link.ld %s -o %s" % \
(get_env_var("RISCV_GCC") ,isa, mabi, asm, opts, elf))
-Tscripts/link.ld %s -o %s " % \
(get_env_var("RISCV_GCC"), asm, opts, elf))
if 'gcc_opts' in test:
cmd += test['gcc_opts']
# If march/mabi is not defined in the test gcc_opts, use the default
# setting from the command line.
if not re.search('march', cmd):
cmd += (" -march=%s" % isa)
if not re.search('mabi', cmd):
cmd += (" -mabi=%s" % mabi)
logging.info("Compiling %s" % asm)
logging.debug(cmd)
output = subprocess.check_output(cmd.split())
@ -270,6 +281,8 @@ def iss_cmp(test_list, iss, output_dir, isa):
process_spike_sim_log(log, csv)
elif iss == "ovpsim":
process_ovpsim_sim_log(log, csv)
elif iss == "sail":
process_sail_sim_log(log, csv)
else:
logging.error("Unsupported ISS" % iss)
sys.exit(1)
@ -290,32 +303,20 @@ def setup_parser():
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--o", type=str,
help="Output directory name")
parser.add_argument("--testlist", type=str, default="",
help="Regression testlist")
parser.add_argument("--csr_yaml", type=str, default="",
help="CSR description file")
parser.add_argument("--end_signature_addr", type=str, default="0",
help="Address that privileged CSR test writes to at EOT")
parser.add_argument("--isa", type=str, default="rv64imc",
help="RISC-V ISA subset")
parser.add_argument("--mabi", type=str, default="lp64",
help="mabi used for compilation, lp32 or lp64")
parser.add_argument("--test", type=str, default="all",
help="Test name, 'all' means all tests in the list")
parser.add_argument("-o", "--output", type=str,
help="Output directory name", dest="o")
parser.add_argument("-tl", "--testlist", type=str, default="",
help="Regression testlist", dest="testlist")
parser.add_argument("-tn", "--test", type=str, default="all",
help="Test name, 'all' means all tests in the list", dest="test")
parser.add_argument("--seed", type=int, default=-1,
help="Randomization seed, default -1 means random seed")
parser.add_argument("--iterations", type=int, default=0,
help="Override the iteration count in the test list")
parser.add_argument("--simulator", type=str, default="vcs",
help="Simulator used to run the generator, default VCS")
parser.add_argument("--simulator_yaml", type=str, default="",
help="RTL simulator setting YAML")
parser.add_argument("-i", "--iterations", type=int, default=0,
help="Override the iteration count in the test list", dest="iterations")
parser.add_argument("-si", "--simulator", type=str, default="vcs",
help="Simulator used to run the generator, default VCS", dest="simulator")
parser.add_argument("--iss", type=str, default="spike",
help="RISC-V instruction set simulator: spike, ovpsim")
parser.add_argument("--iss_yaml", type=str, default="",
help="ISS setting YAML")
help="RISC-V instruction set simulator: spike,ovpsim,sail")
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
help="Verbose logging")
parser.add_argument("--co", dest="co", action="store_true",
@ -328,15 +329,27 @@ def setup_parser():
help="Simulation options for the generator")
parser.add_argument("--gcc_opts", type=str, default="",
help="GCC compile options")
parser.add_argument("--steps", type=str, default="all",
help="Run steps: gen,gcc_compile,iss_sim,iss_cmp")
parser.add_argument("-s", "--steps", type=str, default="all",
help="Run steps: gen,gcc_compile,iss_sim,iss_cmp", dest="steps")
parser.add_argument("--lsf_cmd", type=str, default="",
help="LSF command. Run in local sequentially if lsf \
command is not specified")
parser.add_argument("--isa", type=str, default="rv64imc",
help="RISC-V ISA subset")
parser.add_argument("-m", "--mabi", type=str, default="lp64",
help="mabi used for compilation, lp32 or lp64", dest="mabi")
parser.add_argument("--gen_timeout", type=int, default=360,
help="Generator timeout limit in seconds")
parser.add_argument("--end_signature_addr", type=str, default="0",
help="Address that privileged CSR test writes to at EOT")
parser.add_argument("--iss_timeout", type=int, default=50,
help="ISS sim timeout limit in seconds")
parser.add_argument("--iss_yaml", type=str, default="",
help="ISS setting YAML")
parser.add_argument("--simulator_yaml", type=str, default="",
help="RTL simulator setting YAML")
parser.add_argument("--csr_yaml", type=str, default="",
help="CSR description file")
parser.set_defaults(co=False)
parser.set_defaults(so=False)
@ -369,6 +382,9 @@ def main():
cwd = os.path.dirname(os.path.realpath(__file__))
setup_logging(args.verbose)
if not args.csr_yaml:
args.csr_yaml = cwd + "/yaml/csr_template.yaml"
if not args.iss_yaml:
args.iss_yaml = cwd + "/yaml/iss.yaml"

View file

@ -31,6 +31,7 @@ import sys
import yaml
import argparse
import random
import copy
try:
from bitstring import BitArray as bitarray
@ -249,14 +250,14 @@ def gen_csr_test_pass(test_file, end_addr):
test_file.write(f"\tj csr_pass\n")
def gen_csr_instr(csr_map, csr_instructions, xlen,
def gen_csr_instr(original_csr_map, csr_instructions, xlen,
iterations, out, end_signature_addr):
"""
Uses the information in the map produced by get_csr_map() to generate
test CSR instructions operating on the generated random values.
Args:
csr_map: The dictionary containing CSR mappings generated by get_csr_map()
original_csr_map: The dictionary containing CSR mappings generated by get_csr_map()
csr_instructions: A list of all supported CSR instructions in string form.
xlen: The RISC-V ISA bit length.
iterations: Indicates how many randomized test files will be generated.
@ -270,6 +271,7 @@ def gen_csr_instr(csr_map, csr_instructions, xlen,
for i in range(iterations):
# pick two GPRs at random to act as source and destination registers
# for CSR operations
csr_map = copy.deepcopy(original_csr_map)
source_reg, dest_reg = [f"x{i}" for i in random.sample(range(1, 16), 2)]
csr_list = list(csr_map.keys())
with open(f"{out}/riscv_csr_test.{i}.S", "w") as csr_test_file:

View file

@ -32,6 +32,10 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
matched_cnt = 0
mismatch_cnt = 0
# ensure that in order mode is disabled if necessary
if compare_final_value_only:
in_order_mode = 0
if log:
fd = open(log, 'a+')
else:
@ -56,6 +60,8 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
gpr_val_2 = {}
for trace in instr_trace_1:
trace_1_index += 1
if trace.rd == "":
continue
# Check if there's a GPR change caused by this instruction
gpr_state_change_1 = check_update_gpr(trace.rd, trace.rd_val, gpr_val_1)
if gpr_state_change_1 == 0:
@ -63,10 +69,11 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
# Move forward the other trace until a GPR update happens
gpr_state_change_2 = 0
while (gpr_state_change_2 == 0 and trace_2_index < len(instr_trace_2)):
gpr_state_change_2 = check_update_gpr(
instr_trace_2[trace_2_index].rd,
instr_trace_2[trace_2_index].rd_val,
gpr_val_2)
if instr_trace_2[trace_2_index].rd != "":
gpr_state_change_2 = check_update_gpr(
instr_trace_2[trace_2_index].rd,
instr_trace_2[trace_2_index].rd_val,
gpr_val_2)
trace_2_index += 1
# Check if the GPR update is the same between trace 1 and 2
if gpr_state_change_2 == 0:
@ -131,8 +138,8 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
for trace_1_index in range(0, len(gpr_trace_1[gpr])-1):
if (trace_2_index == len(gpr_trace_2[gpr])):
break
if long(gpr_trace_1[gpr][trace_1_index].rd_val, 16) != \
long(gpr_trace_2[gpr][trace_2_index].rd_val, 16):
if int(gpr_trace_1[gpr][trace_1_index].rd_val, 16) != \
int(gpr_trace_2[gpr][trace_2_index].rd_val, 16):
if coalesced_updates >= coalescing_limit:
coalesced_updates = 0
mismatch_cnt += 1
@ -163,8 +170,8 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
mismatch_cnt += 1
fd.write("Zero GPR[%s] updates observed: %s:%d, %s:%d\n" % (gpr,
name1, len(gpr_trace_1[gpr]), name2, len(gpr_trace_2[gpr])))
elif long(gpr_trace_1[gpr][-1].rd_val, 16) != \
long(gpr_trace_2[gpr][-1].rd_val, 16):
elif int(gpr_trace_1[gpr][-1].rd_val, 16) != \
int(gpr_trace_2[gpr][-1].rd_val, 16):
mismatch_cnt += 1
if mismatch_cnt <= mismatch_print_limit:
fd.write("Mismatch final value:\n")
@ -183,12 +190,13 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
def parse_gpr_update_from_trace(trace_csv, gpr_trace):
prev_val = {}
for trace in trace_csv:
if not (trace.rd in prev_val):
gpr_trace[trace.rd] = []
gpr_trace[trace.rd].append(trace)
elif prev_val[trace.rd] != trace.rd_val:
gpr_trace[trace.rd].append(trace)
prev_val[trace.rd] = trace.rd_val
if trace.rd != "":
if not (trace.rd in prev_val):
gpr_trace[trace.rd] = []
gpr_trace[trace.rd].append(trace)
elif prev_val[trace.rd] != trace.rd_val:
gpr_trace[trace.rd].append(trace)
prev_val[trace.rd] = trace.rd_val
def check_update_gpr(rd, rd_val, gpr):
@ -206,10 +214,10 @@ def check_update_gpr(rd, rd_val, gpr):
def main():
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("csv_file_1", type=str, help="Instruction trace 1 CSV")
parser.add_argument("csv_file_2", type=str, help="Instruction trace 2 CSV")
parser.add_argument("csv_name_1", type=str, help="Instruction trace 1 name")
parser.add_argument("csv_name_2", type=str, help="Instruction trace 2 name")
parser.add_argument("--csv_file_1", type=str, help="Instruction trace 1 CSV")
parser.add_argument("--csv_file_2", type=str, help="Instruction trace 2 CSV")
parser.add_argument("--csv_name_1", type=str, help="Instruction trace 1 name")
parser.add_argument("--csv_name_2", type=str, help="Instruction trace 2 name")
# optional arguments
parser.add_argument("--log", type=str, default="",
help="Log file")
@ -228,9 +236,6 @@ def main():
args = parser.parse_args()
if args.compare_final_value_only:
args.in_order_mode = 0
# Compare trace CSV
compare_trace_csv(args.csv_file_1, args.csv_file_2,
args.csv_name_1, args.csv_name_2, args.log,

View file

@ -32,6 +32,7 @@ class RiscvInstructiontTraceEntry(object):
self.addr = ""
self.binary = ""
self.instr_str = ""
self.instr = ""
self.privileged_mode = ""
def get_trace_string(self):
@ -47,6 +48,7 @@ class RiscvInstructiontTraceCsv(object):
def __init__(self, csv_fd):
self.csv_fd = csv_fd
self.gpr = {}
def start_new_trace(self):
@ -72,11 +74,19 @@ class RiscvInstructiontTraceCsv(object):
def write_trace_entry(self, entry):
"""Write a new trace entry to CSV"""
self.csv_writer.writerow({'str' : entry.instr_str,
'rd' : entry.rd,
'rd_val' : entry.rd_val,
'binary' : entry.binary,
'addr' : entry.addr})
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,
'binary' : entry.binary,
'addr' : entry.addr})
def gpr_to_abi(gpr):

View file

@ -0,0 +1,97 @@
"""
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 sail 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 *
START_RE = re.compile(r"\[4\] \[M\]: 0x.*00001010")
END_RE = re.compile(r"ecall")
INSTR_RE = re.compile(r"\[[0-9].*\] \[(?P<pri>.)\]: 0x(?P<addr>[A-F0-9]+?)"
" \(0x(?P<bin>[A-F0-9]+?)\) (?P<instr>.+?$)")
RD_RE = re.compile(r"x(?P<reg>[0-9]+?) <- 0x(?P<val>[A-F0-9]*)")
def process_sail_sim_log(sail_log, csv):
"""Process SAIL RISCV simulation log.
Extract instruction and affected register information from sail simulation
log and save to a list.
"""
logging.info("Processing sail log : %s" % sail_log)
instr_cnt = 0
sail_instr = ""
with open(sail_log, "r") as f, open(csv, "w") as csv_fd:
search_start = 0
instr_start = 0
trace_csv = RiscvInstructiontTraceCsv(csv_fd)
trace_csv.start_new_trace()
instr = None
for line in f:
# Extract instruction infromation
m = START_RE.search(line)
if m:
search_start = 1
continue
m = END_RE.search(line)
if m:
break
if search_start:
instr = INSTR_RE.search(line)
if instr:
instr_start = 1
pri = instr.group("pri")
addr = instr.group("addr").lower()
binary = instr.group("bin").lower()
instr_str = instr.group("instr")
continue
if instr_start:
m = RD_RE.search(line)
if m:
# Write the extracted instruction to a csvcol buffer file
instr_cnt += 1
rv_instr_trace = RiscvInstructiontTraceEntry()
rv_instr_trace.rd = gpr_to_abi("x%0s" % m.group("reg"))
rv_instr_trace.rd_val = m.group("val").lower()
rv_instr_trace.privileged_mode = pri
rv_instr_trace.addr = addr
rv_instr_trace.binary = binary
rv_instr_trace.instr_str = instr_str
trace_csv.write_trace_entry(rv_instr_trace)
instr_start = 0
logging.info("Processed instruction count : %d" % instr_cnt)
def main():
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input sail simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
args = parser.parse_args()
# Process sail log
process_sail_sim_log(args.log, args.csv)
if __name__ == "__main__":
main()

View file

@ -36,6 +36,13 @@ def process_spike_sim_log(spike_log, csv):
instr_cnt = 0
spike_instr = ""
RD_RE = re.compile(r"(?P<pri>\d) 0x(?P<addr>[a-f0-9]+?) " \
"\((?P<bin>.*?)\) x\s*(?P<reg>\d*?) 0x(?P<val>.*)")
CORE_RE = re.compile(r"core.*0x(?P<addr>[a-f0-9]+?) \(0x(?P<bin>.*?)\) (?P<instr>.*?)$")
INSTR_RE = re.compile(r"(?P<instr>[a-z\.]+?)(\s+?)(?P<operand>.*)")
GPR_RE = re.compile(r"^[a-z][0-9a-z]$")
CSR_RE = re.compile(r"csr")
# Remove all the init spike boot instructions
cmd = ("sed -i '/core.*0x0000000000001010/,$!d' %s" % spike_log)
os.system(cmd)
@ -43,28 +50,58 @@ def process_spike_sim_log(spike_log, csv):
cmd = ("sed -i '/ecall/q' %s" % spike_log)
os.system(cmd)
gpr = {}
gpr["zero"] = 0
with open(spike_log, "r") as f, open(csv, "w") as csv_fd:
trace_csv = RiscvInstructiontTraceCsv(csv_fd)
trace_csv.start_new_trace()
for line in f:
# Extract instruction infromation
m = re.search(r"core(.*)\) (.*)", line)
m = CORE_RE.search(line)
if m:
spike_instr = m.group(2)
else:
# Extract register value information
m = re.search(r"(?P<pri>\d) 0x(?P<addr>[a-f0-9]+?) " \
"\((?P<bin>.*?)\) x\s*(?P<reg>\d*?) 0x(?P<val>.*)", line)
if m:
# Write the extracted instruction to a csvcol buffer file
instr_cnt += 1
rv_instr_trace = RiscvInstructiontTraceEntry()
rv_instr_trace.rd = gpr_to_abi("x%0s" % m.group("reg"))
rv_instr_trace.rd_val = m.group("val")
rv_instr_trace.privileged_mode = m.group("pri")
rv_instr_trace.addr = m.group("addr")
rv_instr_trace.binary = m.group("bin")
rv_instr_trace.instr_str = spike_instr
spike_instr = m.group("instr")
rv_instr_trace = RiscvInstructiontTraceEntry()
rv_instr_trace.instr_str = spike_instr
rv_instr_trace.addr = m.group("addr")
rv_instr_trace.binary = m.group("bin")
nextline = f.readline()
if nextline != "":
m = RD_RE.search(nextline)
if m:
# Extract RD information
instr_cnt += 1
rv_instr_trace.rd = gpr_to_abi("x%0s" % m.group("reg"))
rv_instr_trace.rd_val = m.group("val")
rv_instr_trace.privileged_mode = m.group("pri")
gpr[rv_instr_trace.rd] = rv_instr_trace.rd_val
s = INSTR_RE.search(spike_instr)
if s:
rv_instr_trace.instr = s.group("instr")
operand_str = s.group("operand").replace(" ", "")
if operand_str != "" :
operands = operand_str.split(",")
if CSR_RE.search(s.group("instr")):
# CSR instruction
operand = operands[-1]
if GPR_RE.search(operand) or operand == "zero":
rv_instr_trace.rs1 = operand
rv_instr_trace.rs1_val = gpr[operand]
else:
rv_instr_trace.imm = operand
else:
# Non CSR instruction
for i in range(1, len(operands)):
operand = operands[i]
if GPR_RE.search(operand) or operand == "zero":
if i == 1:
rv_instr_trace.rs1 = operand
rv_instr_trace.rs1_val = gpr[operand]
else:
rv_instr_trace.rs2 = operands[i]
rv_instr_trace.rs2_val = gpr[operand]
else:
rv_instr_trace.imm = operand
trace_csv.write_trace_entry(rv_instr_trace)
logging.info("Processed instruction count : %d" % instr_cnt)
@ -79,5 +116,6 @@ def main():
# Process spike log
process_spike_sim_log(args.log, args.csv)
if __name__ == "__main__":
main()

View file

@ -31,6 +31,7 @@ class riscv_asm_program_gen extends uvm_object;
riscv_instr_sequence sub_program[];
// Program in binary format, stored in the data section, used to inject illegal/HINT instruction
riscv_instr_sequence bin_program;
riscv_instr_sequence debug_program;
string instr_binary[$];
// Kernel programs
// These programs are called in the interrupt/exception handling routine based on the privileged
@ -78,6 +79,7 @@ class riscv_asm_program_gen extends uvm_object;
if(cfg.enable_illegal_instruction || cfg.enable_hint_instruction) begin
bin_program = riscv_instr_sequence::type_id::create("bin_program");
bin_program.instr_cnt = cfg.bin_program_instr_cnt;
bin_program.is_debug_program = 0;
bin_program.label_name = bin_program.get_name();
bin_program.cfg = cfg;
if (cfg.enable_illegal_instruction) begin
@ -99,6 +101,7 @@ class riscv_asm_program_gen extends uvm_object;
foreach(sub_program[i]) begin
sub_program[i] = riscv_instr_sequence::type_id::create($sformatf("sub_%0d",i+1));
sub_program[i].instr_cnt = cfg.sub_program_instr_cnt[i];
sub_program[i].is_debug_program = 0;
generate_directed_instr_stream(.label(sub_program[i].get_name()),
.original_instr_cnt(sub_program[i].instr_cnt),
.min_insert_cnt(0),
@ -113,6 +116,7 @@ class riscv_asm_program_gen extends uvm_object;
// Generate main program
main_program = riscv_instr_sequence::type_id::create("main_program");
main_program.instr_cnt = cfg.main_program_instr_cnt;
main_program.is_debug_program = 0;
main_program.label_name = "_main";
generate_directed_instr_stream(.label("main"),
.original_instr_cnt(main_program.instr_cnt),
@ -174,8 +178,10 @@ class riscv_asm_program_gen extends uvm_object;
gen_privileged_mode_switch_routine();
// Program end
gen_program_end();
// Generate debug mode section
// Generate debug rom section
gen_debug_mode_section();
// Generate debug mode exception handler
gen_debug_exception_section();
// Starting point of data section
gen_data_page_begin();
// Generate the sub program in binary format
@ -238,6 +244,7 @@ class riscv_asm_program_gen extends uvm_object;
.instr_stream(seq.directed_instr),
.access_u_mode_mem(1'b0));
seq.label_name = seq.get_name();
seq.is_debug_program = 0;
seq.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(seq)
seq.gen_instr(0);
@ -796,6 +803,14 @@ class riscv_asm_program_gen extends uvm_object;
// Helper functions
//---------------------------------------------------------------------------------------
// Format a code section, without generating it
virtual function void format_section(ref string instr[$]);
string prefix = format_string(" ", LABEL_STR_LEN);
foreach(instr[i]) begin
instr[i] = {prefix, instr[i]};
end
endfunction
// Generate a code section
virtual function void gen_section(string label, string instr[$]);
string str;
@ -918,10 +933,39 @@ class riscv_asm_program_gen extends uvm_object;
// Generate the program in the debug ROM
// Processor will fetch instruction from here upon receiving debug request from debug module
virtual function void gen_debug_mode_section();
string instr[];
string push_gpr[$];
string pop_gpr[$];
string instr[$];
string dret;
if (riscv_instr_pkg::support_debug_mode) begin
instr = {"dret"};
// The main debug rom
if (!cfg.empty_debug_section) begin
debug_program = riscv_instr_sequence::type_id::create("debug_program");
debug_program.instr_cnt = cfg.debug_program_instr_cnt;
debug_program.is_debug_program = 1;
debug_program.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(debug_program)
debug_program.gen_instr(.is_main_program(1'b1), .enable_hint_instr(1'b0),
.no_branch(1'b1));
debug_program.post_process_instr();
debug_program.generate_instr_stream(.no_label(1'b1));
// Need to save off GPRs to avoid modifying program flow
push_gpr_to_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, push_gpr);
format_section(push_gpr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, pop_gpr);
format_section(pop_gpr);
instr = {push_gpr, debug_program.instr_string_list, pop_gpr};
end
dret = {format_string(" ", LABEL_STR_LEN), "dret"};
instr = {instr, dret};
gen_section("debug_rom", instr);
end
endfunction
// Generate exception handling routine for debug ROM
virtual function void gen_debug_exception_section();
if (riscv_instr_pkg::support_debug_mode) begin
string instr[];
instr = {"dret"};
gen_section("debug_exception", instr);
end

View file

@ -93,7 +93,7 @@ class riscv_jump_instr extends riscv_rand_instr_stream;
rand riscv_instr_base jump;
rand riscv_instr_base addi;
rand riscv_pseudo_instr la;
rand riscv_instr_base branch;
rand riscv_rand_instr branch;
rand bit enable_branch;
rand int mixed_instr_cnt;
riscv_instr_base stack_exit_instr[];
@ -125,10 +125,14 @@ class riscv_jump_instr extends riscv_rand_instr_stream;
jump = riscv_instr_base::type_id::create("jump");
la = riscv_pseudo_instr::type_id::create("la");
addi = riscv_instr_base::type_id::create("addi");
branch = riscv_instr_base::type_id::create("branch");
branch = riscv_rand_instr::type_id::create("branch");
instr_list.rand_mode(0);
endfunction
function void pre_randomize();
branch.cfg = cfg;
endfunction
function void post_randomize();
riscv_instr_base instr[];
// Generate some random instructions to mix with jump instructions
@ -171,7 +175,7 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
int num_of_redudant_instr;
riscv_instr_base push_stack_instr[];
riscv_reg_t saved_regs[];
rand riscv_instr_base branch_instr;
rand riscv_rand_instr branch_instr;
rand bit enable_branch;
string push_start_label;
@ -179,7 +183,6 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
function new(string name = "");
super.new(name);
branch_instr = riscv_instr_base::type_id::create("branch_instr");
endfunction
function void init();
@ -225,6 +228,8 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
end
if(enable_branch) begin
// Cover jal -> branch scenario, the branch is added before push stack operation
branch_instr = riscv_rand_instr::type_id::create("branch_instr");
branch_instr.cfg = cfg;
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr,
category == BRANCH;)
branch_instr.imm_str = push_start_label;

View file

@ -30,6 +30,9 @@ class riscv_instr_gen_config extends uvm_object;
// Instruction count of each sub-program
rand int sub_program_instr_cnt[];
// Instruction count of the debug rom
rand int debug_program_instr_cnt;
// Pattern of data section: RAND_DATA, ALL_ZERO, INCR_VAL
rand data_pattern_t data_page_pattern;
@ -62,6 +65,12 @@ class riscv_instr_gen_config extends uvm_object;
bit check_misa_init_val = 1'b0;
bit check_xstatus = 1'b1;
// Enable a full or empty debug_rom section.
// Full debug_rom will contain random instruction streams.
// Empty debug_rom will contain just dret instruction and will return immediately.
// Will be empty by default.
bit empty_debug_section = 1'b0;
//-----------------------------------------------------------------------------
// Command line options or control knobs
//-----------------------------------------------------------------------------
@ -123,8 +132,13 @@ class riscv_instr_gen_config extends uvm_object;
constraint default_c {
sub_program_instr_cnt.size() == num_of_sub_program;
main_program_instr_cnt + sub_program_instr_cnt.sum() == instr_cnt;
if (riscv_instr_pkg::support_debug_mode) {
main_program_instr_cnt + sub_program_instr_cnt.sum() + debug_program_instr_cnt == instr_cnt;
} else {
main_program_instr_cnt + sub_program_instr_cnt.sum() == instr_cnt;
}
main_program_instr_cnt inside {[1 : instr_cnt]};
debug_program_instr_cnt inside {[1 : instr_cnt]};
foreach(sub_program_instr_cnt[i]) {
sub_program_instr_cnt[i] inside {[1 : instr_cnt]};
}
@ -255,6 +269,7 @@ class riscv_instr_gen_config extends uvm_object;
get_bool_arg_value("+enable_hint_instruction=", enable_hint_instruction);
get_bool_arg_value("+force_m_delegation=", force_m_delegation);
get_bool_arg_value("+force_s_delegation=", force_s_delegation);
get_bool_arg_value("+empty_debug_section=", empty_debug_section);
if(inst.get_arg_value("+boot_mode=", boot_mode_opts)) begin
`uvm_info(get_full_name(), $sformatf(
"Got boot mode option - %0s", boot_mode_opts), UVM_LOW)
@ -278,7 +293,8 @@ class riscv_instr_gen_config extends uvm_object;
uvm_split_string(s, ",", cmdline_march_list);
riscv_instr_pkg::supported_isa.delete();
foreach(cmdline_march_list[i]) begin
if(uvm_enum_wrapper#(riscv_instr_group_t)::from_name(cmdline_march_list[i], march)) begin
if(uvm_enum_wrapper#(riscv_instr_group_t)::from_name(
cmdline_march_list[i].toupper(), march)) begin
riscv_instr_pkg::supported_isa.push_back(march);
end else begin
`uvm_fatal(get_full_name(), $sformatf(

View file

@ -40,6 +40,7 @@ class riscv_instr_sequence extends uvm_sequence;
riscv_pop_stack_instr instr_stack_exit; // Stack pop instructions for sub-programs
riscv_rand_instr_stream instr_stream; // Main instruction streams
bit is_main_program; // Type of this sequence (main or sub program)
bit is_debug_program; // Indicates whether sequence is debug program
string label_name; // Label of the sequence (program name)
riscv_instr_gen_config cfg; // Configuration class handle
string instr_string_list[$]; // Save the instruction list in string format
@ -70,7 +71,8 @@ class riscv_instr_sequence extends uvm_sequence;
// considerably as the instruction stream becomes longer. The downside is we cannot specify
// constraints between instructions. The way to solve it is to have a dedicated directed
// instruction stream for such scenarios, like hazard sequence.
virtual function void gen_instr(bit is_main_program, bit enable_hint_instr = 1'b0);
virtual function void gen_instr(bit is_main_program, bit enable_hint_instr = 1'b0,
bit no_branch = 1'b0);
this.is_main_program = is_main_program;
instr_stream.cfg = cfg;
instr_stream.initialize_instr_list(instr_cnt);
@ -78,7 +80,8 @@ class riscv_instr_sequence extends uvm_sequence;
instr_stream.instr_list.size()), UVM_LOW)
// Do not generate load/store instruction here
// The load/store instruction will be inserted as directed instruction stream
instr_stream.gen_instr(.no_load_store(1'b1), .enable_hint_instr(enable_hint_instr));
instr_stream.gen_instr(.no_branch(no_branch), .no_load_store(1'b1),
.enable_hint_instr(enable_hint_instr));
if(!is_main_program) begin
gen_stack_enter_instr();
gen_stack_exit_instr();
@ -243,13 +246,17 @@ class riscv_instr_sequence extends uvm_sequence;
// Convert the instruction stream to the string format.
// Label is attached to the instruction if available, otherwise attach proper space to make
// the code indent consistent.
function void generate_instr_stream();
function void generate_instr_stream(bit no_label = 1'b0);
string prefix, str;
int i;
instr_string_list = {};
for(i = 0; i < instr_stream.instr_list.size(); i++) begin
if(i == 0) begin
prefix = format_string($sformatf("%0s:", label_name), LABEL_STR_LEN);
if (no_label) begin
prefix = format_string(" ", LABEL_STR_LEN);
end else begin
prefix = format_string($sformatf("%0s:", label_name), LABEL_STR_LEN);
end
instr_stream.instr_list[i].has_label = 1'b1;
end else begin
if(instr_stream.instr_list[i].has_label) begin

View file

@ -85,7 +85,6 @@ class riscv_instr_base_test extends uvm_test;
endfunction
virtual function void apply_directed_instr();
endfunction
task run_phase(uvm_phase phase);

View file

@ -26,3 +26,8 @@
--override riscvOVPsim/cpu/simulateexceptions=T
--trace --tracechange --traceshowicount --program <elf>
--finishafter 500000
- iss: sail
path_var: SAIL_RISCV
cmd: >
<path_var> <elf>

View file

@ -26,6 +26,7 @@
# sim_opts : Simulation options passed to the instruction generator
# no_post_compare : Enable/disable comparison of trace log and ISS log (Optional)
# compare_opts : Options for the RTL & ISS trace comparison
# gcc_opts : gcc compile options
# --------------------------------------------------------------------------------
- test: riscv_arithmetic_basic_test
@ -79,6 +80,17 @@
+directed_instr_5=riscv_multi_page_load_store_instr_stream,4
rtl_test: core_base_test
- test: riscv_non_compressed_instr_test
description: >
Random instruction test without compressed instructions
iterations: 2
gen_test: riscv_rand_instr_test
gen_opts: >
+march=RV32I,RV32M,RV64I,RV64M
gcc_opts: >
-march=rv64im
rtl_test: core_base_test
- test: riscv_rand_jump_test
description: >
Jump among large number of sub-programs, stress testing iTLB operations.
@ -97,7 +109,7 @@
iterations: 2
gen_test: riscv_instr_base_test
gen_opts: >
+instr_cnt=10000
+instr_cnt=5000
+num_of_sub_program=5
+directed_instr_0=riscv_load_store_rand_instr_stream,40
+directed_instr_1=riscv_load_store_hazard_instr_stream,40
@ -208,10 +220,28 @@
compare_opts: >
+compare_final_value_only=1
# Please enable this test for your RTL simulation
- test: riscv_csr_test
description: >
Test all CSR instructions on all implemented CSR registers
iterations: 1
iterations: 0
no_iss: 1
rtl_test: core_ibex_csr_test
no_post_compare: 1
- test: riscv_unaligned_load_store_test
description: >
Unaligned load/store test, ibex should handle it correctly without raising any exception
iterations: 1
gen_test: riscv_instr_base_test
gcc_opts: >
-mno-strict-align
gen_opts: >
+instr_cnt=6000
+num_of_sub_program=5
+directed_instr_0=riscv_load_store_rand_instr_stream,20
+directed_instr_1=riscv_load_store_hazard_instr_stream,20
+directed_instr_2=riscv_cache_line_stress_instr_stream,20
+directed_instr_3=riscv_multi_page_load_store_instr_stream,20
+enable_unaligned_load_store=1
rtl_test: core_ibex_base_test