Skip to content
This repository has been archived by the owner on May 28, 2024. It is now read-only.

Commit

Permalink
add automated ModelSim tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SinaKarvandi committed Feb 23, 2024
1 parent 52439d8 commit 3fd777f
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 0 deletions.
32 changes: 32 additions & 0 deletions sim/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Automated ModelSim Viewer

First of all, make sure to edit the address of the "ModelSim" directory in **modelsim.py**.

After that, only modify the "modelsim.config" file.

In the "modelsim.config" file, the first line that starts with "module:" is the name of the target module's **Tester** class. The rest of the lines are signals to be shown.

For example:
```
module:MinMaxParallelOnlineComparatorTest
clock
maxOutput_3
```

If you don't specify the signals to be filtered, then **ALL** signals will be shown.

For example:
```
module:MinMaxParallelOnlineComparatorTest
```

At last, run it with the following command:
```
python3 modelsim.py
```

or,
```
python3 sim/modelsim.py
```

3 changes: 3 additions & 0 deletions sim/modelsim.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module:OnlineMultipleComparatorTest
io_result
io_resultValid
157 changes: 157 additions & 0 deletions sim/modelsim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import os
import subprocess
import glob

MODELSIM = "/home/sina/intelFPGA/20.1/modelsim_ase/bin"

#
# Check modelsim directory
#
if not os.path.exists(MODELSIM):
print("[x] Error: The path mdoes not exist")
exit()
else:
print("[*] Oh, the modelsim path found :)")

MODELSIM_VCD2WLF = MODELSIM + "/vcd2wlf"
MODELSIM_VSIM = MODELSIM + "/vsim"

#
# Config file variables
#
CONFIG_TEST_MODULE_CLASS = ""
CONFIG_SHOW_ALL_WAVES = True
CONFIG_WAVES_LIST = []

#
# Check if user is root or not
#
if os.geteuid() != 0:
print("[x] you should run this script with root (sudo) user permission")
exit()
else:
print("[*] user is root")

#
# Get the current script's directory
#
current_script_path = os.path.dirname(os.path.abspath(__file__))

WAVE_OUTPUT_FILES_PATH = current_script_path + \
"/../test_run_dir/DUT_should_pass/"
CONFIG_FILE_PATH = current_script_path + "/modelsim.config"
print("[*] current script path:", WAVE_OUTPUT_FILES_PATH)

#
# Check config file
#
if os.path.exists(CONFIG_FILE_PATH) == False:
print("[x] config file not found")
exit()

#
# Interpreting config file
#
with open(CONFIG_FILE_PATH, 'r') as file:
for line in file:

if line.lower().startswith("module:") or line.lower().startswith("module :"):
# it's the test module name
CONFIG_TEST_MODULE_CLASS = line.split(":")[1]
print("[*] found module name:", CONFIG_TEST_MODULE_CLASS)
else:
# it's a wave, so no longer need to show all waves
if line.isspace() == False:
CONFIG_SHOW_ALL_WAVES = False
CONFIG_WAVES_LIST.append(line)
print("[*] signal filter for:", line)

#
# Show message if all signals need to be shown
#
if CONFIG_SHOW_ALL_WAVES == True:
print("[*] no signal filter found, assuming all signals to be shown!")

#
# Check if test module is empty or not
#
if CONFIG_TEST_MODULE_CLASS.isspace() == True:
print("[x] main test module not found, please add 'module:' to the config file")
exit()

#
# Set the current working directory
#
os.chdir(current_script_path + "/..")
print("[*] current working directory: " + format(os.getcwd()))

#
# Create TCL config file
#
print("[*] writing to TCL config file: " +
current_script_path + '/modelsim.tcl')
if CONFIG_SHOW_ALL_WAVES:
with open(current_script_path + '/modelsim.tcl', 'w') as f:
# add the clock at top of the signals by default
f.write("add wave -position insertpoint clock\n")
f.write("add wave -position insertpoint *\n")
else:
with open(current_script_path + '/modelsim.tcl', 'w') as f:
for item in CONFIG_WAVES_LIST:
f.write("add wave -position insertpoint {*" + item.replace('\n','').replace('\r', '') + '*}\n')

#
# Remove all the previous *.wlf, *.vcd, *.fir files
#
print("[*] removing previously generated files")
for file_name in os.listdir(WAVE_OUTPUT_FILES_PATH):
if file_name.endswith('.wlf') or file_name.endswith('.vcd') or file_name.endswith('.fir'):
os.remove(os.path.join(WAVE_OUTPUT_FILES_PATH, file_name))

#
# Run the VCD wave generator
#
print("[*] running chisel VCD file generator for module: " +
CONFIG_TEST_MODULE_CLASS)
print("running command: '" + "sbt testOnly " + CONFIG_TEST_MODULE_CLASS + " -- -DwriteVcd=1" + "'")
result = subprocess.run(
["sbt", "testOnly " + CONFIG_TEST_MODULE_CLASS + " -- -DwriteVcd=1"], stdout=subprocess.PIPE)
print(result.stdout.decode())

#
# Get all files in directory
#
files = glob.glob(WAVE_OUTPUT_FILES_PATH + "/*")

#
# Sort files by last modified time
#
files.sort(key=lambda x: os.path.getmtime(x))

#
# Check if the list is empty or not
#
if not files or not files[-1].endswith('.vcd') :
print("[x] there was an error in generating VCD files")
exit()

#
# Get the latest modified file
#
latest_vcd_file = files[-1]
print("[*] latest generated VCD file: " + latest_vcd_file)

#
# Converting VCD to WLF
#
print("[*] converting VCD file to WLF file")
result = subprocess.run(
[MODELSIM_VCD2WLF, latest_vcd_file, latest_vcd_file + ".wlf"], stdout=subprocess.PIPE)
print(result.stdout.decode())

#
# Run the generated WLF file
#
print("[*] openning file in vsim: " + latest_vcd_file + ".wlf")
subprocess.run([MODELSIM_VSIM, latest_vcd_file + ".wlf",
"-do", current_script_path + '/modelsim.tcl'])
2 changes: 2 additions & 0 deletions sim/modelsim.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add wave -position insertpoint {*io_result*}
add wave -position insertpoint {*io_resultValid*}
23 changes: 23 additions & 0 deletions src/main/scala/gcd/GCD.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,26 @@ class GCD extends Module {
io.outputGCD := x
io.outputValid := y === 0.U
}

object Main extends App {

//
// These lines generate the Verilog output
//
println(
(new chisel3.stage.ChiselStage).emitVerilog(
new GCD(
// parameters (if any)
),
Array(
"--emission-options=disableMemRandomization,disableRegisterRandomization",
"-e", // The intention for this argument (and next argument) is to separate generated files.
"verilog", // We could also use "sverilog" to generate SystemVerilog files.
"--target-dir",
"generated/",
"--target:fpga"
)
)
)

}

0 comments on commit 3fd777f

Please sign in to comment.