Skip to content

Commit

Permalink
Updated based on review feedback to accept comma-separated testcases …
Browse files Browse the repository at this point in the history
…and params. Improved dbt build output to reflect command usage. Outputs coverage database location when enabled.
  • Loading branch information
Niels authored and Niels committed Jan 14, 2022
1 parent 5f4f4e5 commit ad3a076
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 135 deletions.
124 changes: 49 additions & 75 deletions RULES/hdl/questa.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ var VsimFlags = core.StringFlag{
Description: "Extra flags for the vsim command",
}.Register()

// Lint enables additional linting information during compilation.
var Lint = core.BoolFlag{
Name: "questa-lint",
DefaultFn: func() bool {
return false
},
Description: "Enable additional lint information during compilation",
}.Register()

// Access enables the user to control the accessibility in the compiled design for
// debugging purposes.
var Access = core.StringFlag{
Expand All @@ -56,21 +65,12 @@ var Coverage = core.BoolFlag{
Description: "Enable code-coverage database generation",
}.Register()

// CoverageHtml enables the generation of an Html report in the output directory
var CoverageHtml = core.BoolFlag{
Name: "questa-coverage-html",
DefaultFn: func() bool {
return false
},
Description: "Enable code-coverage HTML report generation",
}.Register()

// Target returns the optimization target name defined for this rule.
func (rule Simulation) Target() string {
if Coverage.Value() {
return rule.Name + "_optcov"
return "vopt_cover"
} else {
return rule.Name + "_opt"
return "vopt"
}
}

Expand Down Expand Up @@ -134,10 +134,18 @@ func compileSrcs(ctx core.Context, rule Simulation,
}
} else if IsVhdl(src.String()) {
tool = "vcom"
cmd = cmd + " " + VcomFlags.Value()
}

if Lint.Value() {
cmd = cmd + " -lint"
}

// Create plain compilation command
cmd = tool + " " + cmd + " " + src.String()

// Remove the log file if the command fails to ensure we can recompile it
cmd = tool + " " + cmd + " " + src.String() + " || { rm " + log.String() + " && exit 1; }"
cmd = cmd + " || { rm " + log.String() + " && exit 1; }"

// Add the compilation command as a build step with the log file as the
// generated output
Expand Down Expand Up @@ -205,7 +213,7 @@ func optimize(ctx core.Context, rule Simulation, deps []core.Path) {
if rule.Params != nil {
for key, _ := range rule.Params {
log_files = append(log_files, rule.Path().WithSuffix("/"+key+"_"+log_file_suffix))
targets = append(targets, rule.Target()+key)
targets = append(targets, key + "_" + rule.Target())
params = append(params, key)
}
} else {
Expand Down Expand Up @@ -292,50 +300,6 @@ func verbosityLevelToFlag(level string) (string, bool) {
return verbosity_flag, print_output
}

// preamble creates a preamble for the simulation command for the purpose of generating
// a testcase.
func preamble(rule Simulation, testcase string) (string, string) {
preamble := ""

// Create a testcase generation command if necessary
if rule.TestCaseGenerator != nil {
// No testcase specified, use default
if testcase == "" {
// If directory of testcases available, pick the first one
if rule.TestCasesDir != nil {
if items, err := os.ReadDir(rule.TestCasesDir.String()); err == nil {
if len(items) == 0 {
log.Fatal(fmt.Sprintf("TestCasesDir directory '%s' empty!", rule.TestCasesDir.String()))
}

// Create path to testcase
testcase = rule.TestCasesDir.Absolute() + "/" + items[0].Name()
}
}
} else if testcase != "" && rule.TestCasesDir != nil {
// Create path to testcase
testcase = rule.TestCasesDir.Absolute() + "/" + testcase
}

if testcase == "" {
// Create the preamble for testcase generator without any argument
preamble = fmt.Sprintf("{ %s . ; }", rule.TestCaseGenerator.String())
testcase = "default"
} else {
// Create the preamble for testcase generator with arguments
preamble = fmt.Sprintf("{ %s %s . ; }", rule.TestCaseGenerator.String(), testcase)
}

// Add information to command
preamble = fmt.Sprintf("{ echo Generating %s; } && ", testcase) + preamble

// Trim testcase for use in coverage databaes
testcase = strings.TrimSuffix(path.Base(testcase), path.Ext(testcase))
}

return preamble, testcase
}

// questaCmd will create a command for starting 'vsim' on the compiled and optimized design with flags
// set in accordance with what is specified on the command line.
func questaCmd(rule Simulation, args []string, gui bool, testcase string, params string) string {
Expand All @@ -359,6 +323,14 @@ func questaCmd(rule Simulation, args []string, gui bool, testcase string, params
mode_flag := " -batch -quiet"
plusargs_flag := ""

// Default database name for simulation
var target string
if len(params) > 0 {
target = params + "_" + rule.Target()
} else {
target = rule.Target()
}

// Enable coverage in simulator
coverage_flag := ""
if Coverage.Value() {
Expand Down Expand Up @@ -403,12 +375,12 @@ func questaCmd(rule Simulation, args []string, gui bool, testcase string, params
}

// Create optional command preamble
cmd_preamble, testcase = preamble(rule, testcase)
cmd_preamble, testcase = Preamble(rule, testcase)

cmd_echo := ""
if rule.Params != nil && params != "" {
// Update coverage database name based on parameters. We cannot merge
// different parameter sets, do we have to make a dedicated main database
// Update coverage database name based on parameters. Since we cannot merge
// different parameter sets, we have to make a dedicated main database
// for this parameter set.
main_coverage_db = main_coverage_db + "_" + params
coverage_db = coverage_db + "_" + params
Expand All @@ -435,6 +407,7 @@ func questaCmd(rule Simulation, args []string, gui bool, testcase string, params
}

cmd_postamble := ""
cmd_pass := "PASS"
if gui {
mode_flag = " -gui"
if rule.WaveformInit != nil {
Expand All @@ -453,11 +426,10 @@ func questaCmd(rule Simulation, args []string, gui bool, testcase string, params
do_flags = append(do_flags,
fmt.Sprintf("\"vcover merge -testassociated -out %s.ucdb %s.ucdb %s.ucdb\"",
main_coverage_db, main_coverage_db, coverage_db))
if CoverageHtml.Value() {
do_flags = append(do_flags,
fmt.Sprintf("\"vcover report -html -output %s_covhtml -testdetails -details -assert"+
" -cvg -codeAll %s.ucdb\"", main_coverage_db, main_coverage_db))
}
do_flags = append(do_flags,
fmt.Sprintf("\"vcover report -html -output %s_covhtml -testdetails -details -assert"+
" -cvg -codeAll %s.ucdb\"", main_coverage_db, main_coverage_db))
cmd_pass = cmd_pass + fmt.Sprintf(" Coverage:$$(pwd)/%s.ucdb", main_coverage_db)
}
do_flags = append(do_flags, "\"quit -code [coverage attribute -name TESTSTATUS -concise]\"")
cmd_newline := ":"
Expand All @@ -474,7 +446,7 @@ func questaCmd(rule Simulation, args []string, gui bool, testcase string, params
vsim_flags = vsim_flags + " -do " + do_flag
}

cmd := fmt.Sprintf("{ echo -n %s && vsim %s -work %s %s && echo PASS; }", cmd_echo, vsim_flags, rule.Lib(), rule.Target()+params)
cmd := fmt.Sprintf("{ echo -n %s && vsim %s -work %s %s && echo %s; }", cmd_echo, vsim_flags, rule.Lib(), target, cmd_pass)
if cmd_preamble == "" {
cmd = cmd + " " + cmd_postamble
} else {
Expand All @@ -500,19 +472,21 @@ func simulateQuesta(rule Simulation, args []string, gui bool) string {

// Parse additional arguments
for _, arg := range args {
if strings.HasPrefix(arg, "-testcase=") && rule.TestCaseGenerator != nil {
var testcase string
if _, err := fmt.Sscanf(arg, "-testcase=%s", &testcase); err != nil {
log.Fatal(fmt.Sprintf("-testcase expects a string argument!"))
if strings.HasPrefix(arg, "-testcases=") && rule.TestCaseGenerator != nil {
var testcases_arg string
if _, err := fmt.Sscanf(arg, "-testcases=%s", &testcases_arg); err != nil {
log.Fatal(fmt.Sprintf("-testcases expects a string argument!"))
}
testcases = append(testcases, testcase)
testcases = append(testcases, strings.Split(testcases_arg, ",")...)
} else if strings.HasPrefix(arg, "-params=") && rule.Params != nil {
var param string
if _, err := fmt.Sscanf(arg, "-params=%s", &param); err != nil {
var params_arg string
if _, err := fmt.Sscanf(arg, "-params=%s", &params_arg); err != nil {
log.Fatal(fmt.Sprintf("-params expects a string argument!"))
} else {
if _, ok := rule.Params[param]; ok {
params = append(params, param)
for _, param := range strings.Split(params_arg, ",") {
if _, ok := rule.Params[param]; ok {
params = append(params, param)
}
}
}
}
Expand Down
69 changes: 63 additions & 6 deletions RULES/hdl/simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"
"log"
"os"
"errors"
"strings"
"path"
)

var Simulator = core.StringFlag{
Expand Down Expand Up @@ -91,28 +94,82 @@ func (rule Simulation) Test(args []string) string {

func (rule Simulation) Description() string {
// Print the rule name as its needed for parameter selection
description := " "
description := ""
first := true
for param, _ := range rule.Params {
if first {
description = description + "Params: "
description = description + " -params=" + param
first = false
} else {
description = description + "," + param
}
description = description + param + " "
}

if rule.TestCaseGenerator != nil && rule.TestCasesDir != nil {
description = description + "TestCases: "
description = description + " -testcases="

// Loop through all defined testcases in directory
if items, err := os.ReadDir(rule.TestCasesDir.String()); err == nil {
for _, item := range items {
description = description + item.Name() + " "
for index, item := range items {
if index > 0 {
description = description + ","
}
description = description + item.Name()
}
} else {
log.Fatal(err)
}
}

if len(description) > 0 {
description = description + " "
}

return description
}

// Preamble creates a preamble for the simulation command for the purpose of generating
// a testcase.
func Preamble(rule Simulation, testcase string) (string, string) {
preamble := ""

// Create a testcase generation command if necessary
if rule.TestCaseGenerator != nil {
if testcase == "" && rule.TestCasesDir != nil {
// No testcase specified, pick the first one from the directory
if items, err := os.ReadDir(rule.TestCasesDir.String()); err == nil {
if len(items) == 0 {
log.Fatal(fmt.Sprintf("TestCasesDir directory '%s' empty!", rule.TestCasesDir.String()))
}

// Create path to testcase
testcase = rule.TestCasesDir.Absolute() + "/" + items[0].Name()
}
} else if testcase != "" && rule.TestCasesDir != nil {
// Testcase specified, create path to testcase
testcase = rule.TestCasesDir.Absolute() + "/" + testcase
}

if testcase == "" {
// Create the preamble for testcase generator without any argument
preamble = fmt.Sprintf("{ %s . ; }", rule.TestCaseGenerator.String())
testcase = "default"
} else {
// Check that the testcase exists
if _, err := os.Stat(testcase); errors.Is(err, os.ErrNotExist) {
log.Fatal(fmt.Sprintf("Testcase '%s' does not exist!", testcase))
}

// Create the preamble for testcase generator with arguments
preamble = fmt.Sprintf("{ %s %s . ; }", rule.TestCaseGenerator.String(), testcase)
}

// Add information to command
preamble = fmt.Sprintf("{ echo Generating %s; } && ", testcase) + preamble

// Trim testcase for use in coverage databaes
testcase = strings.TrimSuffix(path.Base(testcase), path.Ext(testcase))
}

return preamble, testcase
}
Loading

0 comments on commit ad3a076

Please sign in to comment.