Skip to content

Commit b12f09c

Browse files
Stephen TridgellQuantamHD
authored andcommitted
xsim_test is defined in vivado/defs.bzl. This can run and generate a waveform. Manually tested waveform generation, passing and failing conditions.
1 parent d2af0c7 commit b12f09c

File tree

6 files changed

+250
-28
lines changed

6 files changed

+250
-28
lines changed

vivado/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ exports_files([
66
"place_optimize.tcl.template",
77
"route.tcl.template",
88
"write_bitstream.tcl.template",
9+
"xsim_test.tcl.template",
910
])

vivado/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,34 @@ write_bitstream(
158158
```
159159

160160
It has the optional argument `_write_bitstream_tcl_template` which by default loads [write_bitstream.tcl.template](write_bitstream.tcl.template). This can be modified.
161+
162+
## xsim_test
163+
164+
Executes a test bench using the vivado simulator, xsim.
165+
166+
Example from `vivado/tests`:
167+
```
168+
xsim_test(
169+
name = "xsim_smoke_test",
170+
module = ":xsim_smoke_tb",
171+
module_top = "xsim_smoke_tb",
172+
part_number = "xczu28dr-ffvg1517-2-e",
173+
xilinx_env = ":xilinx_env.sh",
174+
with_waveform = True,
175+
tags = ["manual"],
176+
)
177+
```
178+
179+
It has the optional argument `_xsim_test_template` which by default loads [_xsim_test_template.tcl.template](_xsim_test_template.tcl.template). This can be modified.
180+
181+
It is tagged as `manual` so it is not run by CI tools. They do not have vivado installed.
182+
183+
To get a waveform, do not use `bazel test`, use `bazel run` instead. For example
184+
`bazel run //vivado/tests:xsim_smoke_test` will then generate something like `bazel-out/k8-fastbuild/bin/vivado/tests/xsim_smoke_test.wdb`.
185+
186+
To open this waveform in vivado:
187+
188+
```
189+
current_fileset
190+
open_wave_database bazel-out/k8-fastbuild/bin/vivado/tests/xsim_smoke_test.wdb
191+
```

vivado/defs.bzl

Lines changed: 150 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -43,32 +43,26 @@ def run_tcl_template(ctx, template, substitutions, xilinx_env, input_files, outp
4343

4444
return [
4545
DefaultInfo(files = depset(outputs)),
46+
vivado_log,
47+
vivado_journal,
4648
]
4749

48-
def create_and_synth(
49-
ctx,
50-
with_synth,
51-
synth_checkpoint = None,
52-
timing_summary_report = None,
53-
util_report = None,
54-
synth_strategy = None):
55-
"""Create a project and optionally synthesize.
50+
def generate_file_load_tcl(module):
51+
"""Generate the strings needed for tcl.
5652
57-
Due to IP issues, it makes sense to do synthesis in project mode.
58-
This function can also be used to generate a vivado project from the input sources.
53+
Look at all the sources for a verilog module and make the needed commands
54+
to get xilinx tools to load all the files needed.
5955
6056
Args:
61-
ctx: Context from a rule
62-
with_synth: A flag indicating if synthesis should be run too.
63-
synth_checkpoint: Optionally define the output synthesis checkpoint. Not used when creating a project only.
64-
timing_summary_report: Optionally define the timing summary report output. Not used when creating a project only.
65-
util_report: Optionally define the utilization report output. Not used when creating a project only.
66-
synth_strategy: Optionally define the synthesis strategy to use. Not used when creating a project only.
57+
module: The top level verilog module
6758
6859
Returns:
69-
DefaultInfo - Files generated by the project.
60+
all_files: All file objects that the module depends on.
61+
hdl_source_content: A string to load any hdl files in xilinx tools.
62+
constraints_content: A string to load any constraints in xilinx tools.
63+
tcl_content: A string to load any other tcl in xilinx tools.
7064
"""
71-
transitive_srcs = depset([], transitive = [ctx.attr.module[VerilogInfo].dag])
65+
transitive_srcs = depset([], transitive = [module[VerilogInfo].dag])
7266
all_srcs = [verilog_info_struct.srcs for verilog_info_struct in transitive_srcs.to_list()]
7367
all_files = [src for sub_tuple in all_srcs for src in sub_tuple]
7468

@@ -92,6 +86,38 @@ def create_and_synth(
9286
else:
9387
fail("Unknown file type: " + file.path)
9488

89+
return [
90+
all_files,
91+
hdl_source_content,
92+
constraints_content,
93+
tcl_content,
94+
]
95+
96+
def create_and_synth(
97+
ctx,
98+
with_synth,
99+
synth_checkpoint = None,
100+
timing_summary_report = None,
101+
util_report = None,
102+
synth_strategy = None):
103+
"""Create a project and optionally synthesize.
104+
105+
Due to IP issues, it makes sense to do synthesis in project mode.
106+
This function can also be used to generate a vivado project from the input sources.
107+
108+
Args:
109+
ctx: Context from a rule
110+
with_synth: A flag indicating if synthesis should be run too.
111+
synth_checkpoint: Optionally define the output synthesis checkpoint. Not used when creating a project only.
112+
timing_summary_report: Optionally define the timing summary report output. Not used when creating a project only.
113+
util_report: Optionally define the utilization report output. Not used when creating a project only.
114+
synth_strategy: Optionally define the synthesis strategy to use. Not used when creating a project only.
115+
116+
Returns:
117+
DefaultInfo - Files generated by the project.
118+
"""
119+
all_files, hdl_source_content, constraints_content, tcl_content = generate_file_load_tcl(ctx.attr.module)
120+
95121
project_dir = ctx.actions.declare_directory(ctx.label.name)
96122

97123
if with_synth:
@@ -134,7 +160,8 @@ def create_and_synth(
134160
)
135161

136162
def _vivado_create_project_impl(ctx):
137-
return create_and_synth(ctx, 0)
163+
default_info = create_and_synth(ctx, 0)
164+
return [default_info[0]]
138165

139166
vivado_create_project = rule(
140167
implementation = _vivado_create_project_impl,
@@ -145,7 +172,7 @@ vivado_create_project = rule(
145172
mandatory = True,
146173
),
147174
"module_top": attr.string(
148-
doc = "The name of the verilog module to verilate.",
175+
doc = "The name of the top level verilog module.",
149176
mandatory = True,
150177
),
151178
"part_number": attr.string(
@@ -160,7 +187,7 @@ vivado_create_project = rule(
160187
),
161188
"jobs": attr.int(
162189
doc = "Jobs to pass to vivado which defines the amount of parallelism.",
163-
default = 4
190+
default = 4,
164191
),
165192
"_create_project_tcl_template": attr.label(
166193
doc = "The create project tcl template",
@@ -198,7 +225,7 @@ vivado_synthesize = rule(
198225
mandatory = True,
199226
),
200227
"module_top": attr.string(
201-
doc = "The name of the verilog module to verilate.",
228+
doc = "The name of the top level verilog module.",
202229
mandatory = True,
203230
),
204231
"part_number": attr.string(
@@ -217,7 +244,7 @@ vivado_synthesize = rule(
217244
),
218245
"jobs": attr.int(
219246
doc = "Jobs to pass to vivado which defines the amount of parallelism.",
220-
default = 4
247+
default = 4,
221248
),
222249
"_create_project_tcl_template": attr.label(
223250
doc = "The create project tcl template",
@@ -285,7 +312,7 @@ vivado_synthesis_optimize = rule(
285312
),
286313
"threads": attr.int(
287314
doc = "Threads to pass to vivado which defines the amount of parallelism.",
288-
default = 8
315+
default = 8,
289316
),
290317
"_synthesis_optimize_template": attr.label(
291318
doc = "The synthesis optimzation tcl template",
@@ -351,7 +378,7 @@ vivado_placement = rule(
351378
),
352379
"threads": attr.int(
353380
doc = "Threads to pass to vivado which defines the amount of parallelism.",
354-
default = 8
381+
default = 8,
355382
),
356383
"_placement_template": attr.label(
357384
doc = "The placement tcl template",
@@ -417,7 +444,7 @@ vivado_place_optimize = rule(
417444
),
418445
"threads": attr.int(
419446
doc = "Threads to pass to vivado which defines the amount of parallelism.",
420-
default = 8
447+
default = 8,
421448
),
422449
"_place_optimize_template": attr.label(
423450
doc = "The placement tcl template",
@@ -499,7 +526,7 @@ vivado_routing = rule(
499526
),
500527
"threads": attr.int(
501528
doc = "Threads to pass to vivado which defines the amount of parallelism.",
502-
default = 8
529+
default = 8,
503530
),
504531
"_route_template": attr.label(
505532
doc = "The placement tcl template",
@@ -526,14 +553,15 @@ def _vivado_write_bitstream_impl(ctx):
526553

527554
outputs = [bitstream]
528555

529-
return run_tcl_template(
556+
default_info = run_tcl_template(
530557
ctx,
531558
ctx.file._write_bitstream_template,
532559
substitutions,
533560
ctx.file.xilinx_env,
534561
[checkpoint_in],
535562
outputs,
536563
)
564+
return [default_info[0]]
537565

538566
vivado_write_bitstream = rule(
539567
implementation = _vivado_write_bitstream_impl,
@@ -618,3 +646,97 @@ def vivado_flow(name, module, module_top, part_number, xilinx_env, tags = []):
618646
xilinx_env = xilinx_env,
619647
tags = tags,
620648
)
649+
650+
def _xsim_test_impl(ctx):
651+
all_files, hdl_source_content, constraints_content, tcl_content = generate_file_load_tcl(ctx.attr.module)
652+
653+
project_dir = ctx.actions.declare_directory("{}_prj".format(ctx.label.name))
654+
if (ctx.attr.with_waveform):
655+
with_waveform_str = "1"
656+
wave_db = ctx.actions.declare_file("{}.wdb".format(ctx.label.name))
657+
wave_db_path = wave_db.path
658+
outputs = [project_dir, wave_db]
659+
else:
660+
with_waveform_str = "0"
661+
wave_db_path = ""
662+
outputs = [project_dir]
663+
664+
substitutions = {
665+
"{{PART_NUMBER}}": ctx.attr.part_number,
666+
"{{HDL_SOURCE_CONTENT}}": hdl_source_content,
667+
"{{TCL_CONTENT}}": tcl_content,
668+
"{{CONSTRAINTS_CONTENT}}": constraints_content,
669+
"{{MODULE_TOP}}": ctx.attr.module_top,
670+
"{{PROJECT_DIR}}": project_dir.path,
671+
"{{WITH_WAVEFORM}}": with_waveform_str,
672+
"{{WAVE_DB}}": wave_db_path,
673+
}
674+
675+
_, vivado_log, vivado_journal = run_tcl_template(
676+
ctx,
677+
ctx.file._xsim_test_template,
678+
substitutions,
679+
ctx.file.xilinx_env,
680+
all_files,
681+
outputs,
682+
)
683+
684+
outputs.append(vivado_log)
685+
outputs.append(vivado_journal)
686+
687+
log_runfiles = ctx.runfiles(files = [vivado_log])
688+
689+
error_parser = "if grep -q Error {}; then\n".format(vivado_log.short_path)
690+
error_parser += "cat {}\n".format(vivado_log.short_path)
691+
error_parser += "exit 64\n"
692+
error_parser += "fi"
693+
694+
ctx.actions.write(
695+
output = ctx.outputs.executable,
696+
content = error_parser,
697+
is_executable = True,
698+
)
699+
700+
return [
701+
DefaultInfo(
702+
files = depset(outputs),
703+
runfiles = log_runfiles,
704+
executable = ctx.outputs.executable,
705+
),
706+
]
707+
708+
xsim_test = rule(
709+
implementation = _xsim_test_impl,
710+
doc = "Use the vivado tool xsim to test.",
711+
test = True,
712+
attrs = {
713+
"module": attr.label(
714+
doc = "The top level build.",
715+
providers = [VerilogInfo],
716+
mandatory = True,
717+
),
718+
"module_top": attr.string(
719+
doc = "The name of the top level verilog module.",
720+
mandatory = True,
721+
),
722+
"part_number": attr.string(
723+
doc = "The targeted xilinx part.",
724+
mandatory = True,
725+
),
726+
"with_waveform": attr.bool(
727+
doc = "Generate with a waveform",
728+
default = False,
729+
),
730+
"xilinx_env": attr.label(
731+
doc = "A shell script to source the vivado environment and " +
732+
"point to license server",
733+
mandatory = True,
734+
allow_single_file = [".sh"],
735+
),
736+
"_xsim_test_template": attr.label(
737+
doc = "The tcl template to run on vivado.",
738+
default = "@rules_hdl//vivado:xsim_test.tcl.template",
739+
allow_single_file = [".template"],
740+
),
741+
},
742+
)

vivado/tests/BUILD

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ load("//verilator:defs.bzl", "verilator_cc_library")
33
load(
44
"//vivado:defs.bzl",
55
"vivado_flow",
6+
"xsim_test",
67
)
78

89
verilog_library(
@@ -27,6 +28,21 @@ cc_test(
2728
],
2829
)
2930

31+
verilog_library(
32+
name = "xsim_smoke_tb",
33+
srcs = ["xsim_smoke_tb.sv"],
34+
)
35+
36+
xsim_test(
37+
name = "xsim_smoke_test",
38+
module = ":xsim_smoke_tb",
39+
module_top = "xsim_smoke_tb",
40+
part_number = "xczu28dr-ffvg1517-2-e",
41+
tags = ["manual"],
42+
with_waveform = True,
43+
xilinx_env = ":xilinx_env.sh",
44+
)
45+
3046
verilog_library(
3147
name = "johnson_counter_top",
3248
srcs = [

vivado/tests/xsim_smoke_tb.sv

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
module xsim_smoke_tb ();
3+
4+
logic clk = 0;
5+
always #5ns clk = !clk;
6+
7+
initial begin
8+
repeat (100) @(posedge clk);
9+
assert (1)
10+
else $error("A message about failed assertion");
11+
repeat (100) @(posedge clk);
12+
$finish();
13+
end
14+
15+
endmodule

0 commit comments

Comments
 (0)